Packages

Signal Protocol cryptographic primitives NIF implementation

Current section

Files

Jump to
libsignal_protocol_nif c_src crypto crypto.c
Raw

c_src/crypto/crypto.c

#include "crypto.h"
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/ec.h>
#include <string.h>
#include <openssl/ecdsa.h>
#include <openssl/err.h>
#include <openssl/objects.h>
#include <openssl/param_build.h>
#include <openssl/core_names.h>
// AES-256-GCM implementation
crypto_error_t aes_gcm_encrypt(const aes_key_t *key,
const uint8_t *iv, size_t iv_len,
const uint8_t *plaintext, size_t plaintext_len,
const uint8_t *aad, size_t aad_len,
uint8_t *ciphertext, size_t *ciphertext_len,
uint8_t *tag, size_t tag_len)
{
if (!key || !iv || !plaintext || !ciphertext || !tag)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx)
{
return CRYPTO_ERROR_MEMORY;
}
if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key->key, iv))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
if (aad && aad_len > 0)
{
int len;
if (!EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
}
int len;
if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
*ciphertext_len = len;
if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
*ciphertext_len += len;
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_len, tag))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_OK;
}
crypto_error_t aes_gcm_decrypt(const aes_key_t *key,
const uint8_t *iv, size_t iv_len,
const uint8_t *ciphertext, size_t ciphertext_len,
const uint8_t *aad, size_t aad_len,
const uint8_t *tag, size_t tag_len,
uint8_t *plaintext, size_t *plaintext_len)
{
if (!key || !iv || !ciphertext || !tag || !plaintext)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx)
{
return CRYPTO_ERROR_MEMORY;
}
if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key->key, iv))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
if (aad && aad_len > 0)
{
int len;
if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
}
int len;
if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
*plaintext_len = len;
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len, (void *)tag))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INVALID_SIGNATURE;
}
if (!EVP_DecryptFinal_ex(ctx, plaintext + len, &len))
{
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_ERROR_INVALID_SIGNATURE;
}
*plaintext_len += len;
EVP_CIPHER_CTX_free(ctx);
return CRYPTO_OK;
}
// HMAC-SHA256 implementation
crypto_error_t hmac_sha256(const hmac_key_t *key,
const uint8_t *data, size_t data_len,
uint8_t *mac, size_t *mac_len)
{
if (!key || !data || !mac)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
unsigned int len;
if (!HMAC(EVP_sha256(), key->key, HMAC_KEY_SIZE,
data, data_len, mac, &len))
{
return CRYPTO_ERROR_INTERNAL;
}
*mac_len = len;
return CRYPTO_OK;
}
// Curve25519 implementation (X25519 via OpenSSL EVP_PKEY API)
crypto_error_t curve25519_generate_keypair(curve25519_key_t *public_key,
curve25519_key_t *private_key)
{
if (!public_key || !private_key)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL);
if (!pctx)
return CRYPTO_ERROR_INTERNAL;
if (EVP_PKEY_keygen_init(pctx) <= 0)
{
EVP_PKEY_CTX_free(pctx);
return CRYPTO_ERROR_INTERNAL;
}
EVP_PKEY *pkey = NULL;
if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
{
EVP_PKEY_CTX_free(pctx);
return CRYPTO_ERROR_INTERNAL;
}
// Extract private key
size_t priv_len = CURVE25519_KEY_SIZE;
if (EVP_PKEY_get_raw_private_key(pkey, private_key->key, &priv_len) <= 0 || priv_len != CURVE25519_KEY_SIZE)
{
EVP_PKEY_free(pkey);
EVP_PKEY_CTX_free(pctx);
return CRYPTO_ERROR_INTERNAL;
}
// Extract public key
size_t pub_len = CURVE25519_KEY_SIZE;
if (EVP_PKEY_get_raw_public_key(pkey, public_key->key, &pub_len) <= 0 || pub_len != CURVE25519_KEY_SIZE)
{
EVP_PKEY_free(pkey);
EVP_PKEY_CTX_free(pctx);
return CRYPTO_ERROR_INTERNAL;
}
EVP_PKEY_free(pkey);
EVP_PKEY_CTX_free(pctx);
return CRYPTO_OK;
}
crypto_error_t curve25519_shared_secret(const curve25519_key_t *private_key,
const curve25519_key_t *public_key,
uint8_t *shared_secret,
size_t *shared_secret_len)
{
if (!private_key || !public_key || !shared_secret || !shared_secret_len)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
EVP_PKEY *priv = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, private_key->key, CURVE25519_KEY_SIZE);
EVP_PKEY *pub = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, public_key->key, CURVE25519_KEY_SIZE);
if (!priv || !pub)
{
if (priv)
EVP_PKEY_free(priv);
if (pub)
EVP_PKEY_free(pub);
return CRYPTO_ERROR_INTERNAL;
}
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(priv, NULL);
if (!ctx)
{
EVP_PKEY_free(priv);
EVP_PKEY_free(pub);
return CRYPTO_ERROR_INTERNAL;
}
if (EVP_PKEY_derive_init(ctx) <= 0)
{
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(priv);
EVP_PKEY_free(pub);
return CRYPTO_ERROR_INTERNAL;
}
if (EVP_PKEY_derive_set_peer(ctx, pub) <= 0)
{
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(priv);
EVP_PKEY_free(pub);
return CRYPTO_ERROR_INTERNAL;
}
size_t secret_len = CURVE25519_KEY_SIZE;
if (EVP_PKEY_derive(ctx, shared_secret, &secret_len) <= 0 || secret_len != CURVE25519_KEY_SIZE)
{
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(priv);
EVP_PKEY_free(pub);
return CRYPTO_ERROR_INTERNAL;
}
*shared_secret_len = secret_len;
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(priv);
EVP_PKEY_free(pub);
return CRYPTO_OK;
}
// SHA-256 implementation
crypto_error_t sha256(const uint8_t *data, size_t data_len,
uint8_t *digest, size_t *digest_len)
{
if (!data || !digest)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
if (!SHA256(data, data_len, digest))
{
return CRYPTO_ERROR_INTERNAL;
}
*digest_len = SHA256_DIGEST_SIZE;
return CRYPTO_OK;
}
// SHA-512 implementation
crypto_error_t sha512(const uint8_t *data, size_t data_len,
uint8_t *digest, size_t *digest_len)
{
if (!data || !digest)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
if (!SHA512(data, data_len, digest))
{
return CRYPTO_ERROR_INTERNAL;
}
*digest_len = SHA512_DIGEST_SIZE;
return CRYPTO_OK;
}
// Random number generation
crypto_error_t crypto_random_bytes(uint8_t *buffer, size_t len)
{
if (!buffer)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
if (!RAND_bytes(buffer, len))
{
return CRYPTO_ERROR_INTERNAL;
}
return CRYPTO_OK;
}
// Memory security
void crypto_secure_zero(void *buffer, size_t len)
{
if (buffer)
{
volatile uint8_t *p = buffer;
while (len--)
{
*p++ = 0;
}
}
}
// New EVP_PKEY-based functions (replacing deprecated EC_KEY API)
crypto_error_t evp_generate_ec_keypair(EVP_PKEY **public_key, EVP_PKEY **private_key)
{
if (!public_key || !private_key)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
// Create EVP_PKEY context for EC key generation using modern API
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (!pctx)
{
return CRYPTO_ERROR_INTERNAL;
}
if (EVP_PKEY_keygen_init(pctx) <= 0)
{
EVP_PKEY_CTX_free(pctx);
return CRYPTO_ERROR_INTERNAL;
}
// Set curve to P-256 using modern API
if (EVP_PKEY_CTX_set_group_name(pctx, "P-256") <= 0)
{
EVP_PKEY_CTX_free(pctx);
return CRYPTO_ERROR_INTERNAL;
}
EVP_PKEY *pkey = NULL;
if (EVP_PKEY_generate(pctx, &pkey) <= 0)
{
EVP_PKEY_CTX_free(pctx);
return CRYPTO_ERROR_INTERNAL;
}
EVP_PKEY_CTX_free(pctx);
// For EC keys, the same EVP_PKEY contains both public and private key
EVP_PKEY_up_ref(pkey);
*public_key = pkey;
*private_key = pkey;
return CRYPTO_OK;
}
crypto_error_t evp_sign_data(EVP_PKEY *private_key, const uint8_t *data, size_t data_len,
uint8_t *signature, size_t *signature_len)
{
if (!private_key || !data || !signature || !signature_len)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
int key_type = EVP_PKEY_id(private_key);
if (key_type == EVP_PKEY_ED25519)
{
// Use Ed25519 signing
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
if (!md_ctx)
{
return CRYPTO_ERROR_MEMORY;
}
if (EVP_DigestSignInit(md_ctx, NULL, NULL, NULL, private_key) <= 0)
{
EVP_MD_CTX_free(md_ctx);
return CRYPTO_ERROR_INTERNAL;
}
// Get signature length
size_t sig_len = 0;
if (EVP_DigestSign(md_ctx, NULL, &sig_len, data, data_len) <= 0)
{
EVP_MD_CTX_free(md_ctx);
return CRYPTO_ERROR_INTERNAL;
}
if (sig_len > *signature_len)
{
EVP_MD_CTX_free(md_ctx);
return CRYPTO_ERROR_INVALID_PARAMETER;
}
// Generate signature
if (EVP_DigestSign(md_ctx, signature, &sig_len, data, data_len) <= 0)
{
EVP_MD_CTX_free(md_ctx);
return CRYPTO_ERROR_INTERNAL;
}
*signature_len = sig_len;
EVP_MD_CTX_free(md_ctx);
return CRYPTO_OK;
}
else
{
// Use ECDSA with SHA-256 for other key types
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
if (!md_ctx)
{
return CRYPTO_ERROR_MEMORY;
}
if (EVP_DigestSignInit(md_ctx, NULL, EVP_sha256(), NULL, private_key) <= 0)
{
EVP_MD_CTX_free(md_ctx);
return CRYPTO_ERROR_INTERNAL;
}
// Get signature length
size_t sig_len = 0;
if (EVP_DigestSign(md_ctx, NULL, &sig_len, data, data_len) <= 0)
{
EVP_MD_CTX_free(md_ctx);
return CRYPTO_ERROR_INTERNAL;
}
if (sig_len > *signature_len)
{
EVP_MD_CTX_free(md_ctx);
return CRYPTO_ERROR_INVALID_PARAMETER;
}
// Generate signature
if (EVP_DigestSign(md_ctx, signature, &sig_len, data, data_len) <= 0)
{
EVP_MD_CTX_free(md_ctx);
return CRYPTO_ERROR_INTERNAL;
}
*signature_len = sig_len;
EVP_MD_CTX_free(md_ctx);
return CRYPTO_OK;
}
}
crypto_error_t evp_verify_signature(EVP_PKEY *public_key, const uint8_t *data, size_t data_len,
const uint8_t *signature, size_t signature_len)
{
if (!public_key || !data || !signature)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
if (!md_ctx)
{
return CRYPTO_ERROR_MEMORY;
}
if (EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, public_key) <= 0)
{
EVP_MD_CTX_free(md_ctx);
return CRYPTO_ERROR_INTERNAL;
}
int result = EVP_DigestVerify(md_ctx, signature, signature_len, data, data_len);
EVP_MD_CTX_free(md_ctx);
return (result == 1) ? CRYPTO_OK : CRYPTO_ERROR_INVALID_SIGNATURE;
}
crypto_error_t evp_serialize_public_key(EVP_PKEY *key, uint8_t *buffer, size_t *buffer_len)
{
if (!key || !buffer || !buffer_len)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
// Use EVP_PKEY_get_octet_string_param to get the public key in uncompressed format
size_t len = 0;
if (EVP_PKEY_get_octet_string_param(key, OSSL_PKEY_PARAM_PUB_KEY, NULL, 0, &len) != 1)
{
return CRYPTO_ERROR_INTERNAL;
}
if (len > *buffer_len)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
if (EVP_PKEY_get_octet_string_param(key, OSSL_PKEY_PARAM_PUB_KEY, buffer, len, &len) != 1)
{
return CRYPTO_ERROR_INTERNAL;
}
*buffer_len = len;
return CRYPTO_OK;
}
crypto_error_t evp_deserialize_public_key(const uint8_t *buffer, size_t buffer_len, EVP_PKEY **key)
{
if (!buffer || !key)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
// Create EVP_PKEY context for EC key generation
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (!ctx)
{
return CRYPTO_ERROR_INTERNAL;
}
// Initialize key generation
if (EVP_PKEY_keygen_init(ctx) != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
// Set the curve to P-256
if (EVP_PKEY_CTX_set_group_name(ctx, "P-256") != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
// Create a temporary key to get the group
EVP_PKEY *temp_key = NULL;
if (EVP_PKEY_generate(ctx, &temp_key) != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
EVP_PKEY_CTX_free(ctx);
// Create new context for setting the public key
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, temp_key, NULL);
EVP_PKEY_free(temp_key);
if (!ctx)
{
return CRYPTO_ERROR_INTERNAL;
}
// Set the public key from the buffer
if (EVP_PKEY_fromdata_init(ctx) != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
OSSL_PARAM params[] = {
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, "P-256", 0),
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, (void *)buffer, buffer_len),
OSSL_PARAM_END};
if (EVP_PKEY_fromdata(ctx, key, EVP_PKEY_PUBLIC_KEY, params) != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
EVP_PKEY_CTX_free(ctx);
return CRYPTO_OK;
}
crypto_error_t evp_serialize_private_key(EVP_PKEY *key, uint8_t *buffer, size_t *buffer_len)
{
if (!key || !buffer || !buffer_len)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
// Use EVP_PKEY_get_octet_string_param to get the private key
size_t len = 0;
if (EVP_PKEY_get_octet_string_param(key, OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0, &len) != 1)
{
return CRYPTO_ERROR_INTERNAL;
}
if (len > *buffer_len)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
if (EVP_PKEY_get_octet_string_param(key, OSSL_PKEY_PARAM_PRIV_KEY, buffer, len, &len) != 1)
{
return CRYPTO_ERROR_INTERNAL;
}
*buffer_len = len;
return CRYPTO_OK;
}
crypto_error_t evp_deserialize_private_key(const uint8_t *buffer, size_t buffer_len, EVP_PKEY **key)
{
if (!buffer || !key)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
// Create EVP_PKEY context for EC key generation
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (!ctx)
{
return CRYPTO_ERROR_INTERNAL;
}
// Initialize key generation
if (EVP_PKEY_keygen_init(ctx) != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
// Set the curve to P-256
if (EVP_PKEY_CTX_set_group_name(ctx, "P-256") != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
// Create a temporary key to get the group
EVP_PKEY *temp_key = NULL;
if (EVP_PKEY_generate(ctx, &temp_key) != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
EVP_PKEY_CTX_free(ctx);
// Create new context for setting the private key
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, temp_key, NULL);
EVP_PKEY_free(temp_key);
if (!ctx)
{
return CRYPTO_ERROR_INTERNAL;
}
// Set the private key from the buffer
if (EVP_PKEY_fromdata_init(ctx) != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
OSSL_PARAM params[] = {
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, "P-256", 0),
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, (void *)buffer, buffer_len),
OSSL_PARAM_END};
if (EVP_PKEY_fromdata(ctx, key, EVP_PKEY_KEYPAIR, params) != 1)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
EVP_PKEY_CTX_free(ctx);
return CRYPTO_OK;
}
crypto_error_t evp_validate_key(EVP_PKEY *key)
{
if (!key)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key, NULL);
if (!ctx)
{
return CRYPTO_ERROR_INTERNAL;
}
int ret = EVP_PKEY_check(ctx);
EVP_PKEY_CTX_free(ctx);
if (ret <= 0)
{
return CRYPTO_ERROR_INVALID_KEY;
}
return CRYPTO_OK;
}
crypto_error_t evp_compute_shared_secret(EVP_PKEY *private_key, EVP_PKEY *public_key,
unsigned char *shared_secret, size_t *shared_secret_len)
{
if (!private_key || !public_key || !shared_secret || !shared_secret_len)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(private_key, NULL);
if (!ctx)
{
return CRYPTO_ERROR_INTERNAL;
}
if (EVP_PKEY_derive_init(ctx) <= 0)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
if (EVP_PKEY_derive_set_peer(ctx, public_key) <= 0)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
size_t len = 0;
if (EVP_PKEY_derive(ctx, NULL, &len) <= 0)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
if (len > *shared_secret_len)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INVALID_PARAMETER;
}
if (EVP_PKEY_derive(ctx, shared_secret, &len) <= 0)
{
EVP_PKEY_CTX_free(ctx);
return CRYPTO_ERROR_INTERNAL;
}
*shared_secret_len = len;
EVP_PKEY_CTX_free(ctx);
return CRYPTO_OK;
}
crypto_error_t evp_derive_key(const unsigned char *input, size_t input_len,
const unsigned char *salt, size_t salt_len,
unsigned char *output, size_t output_len)
{
if (!input || !output)
{
return CRYPTO_ERROR_INVALID_PARAMETER;
}
// Use PBKDF2 with SHA-256 for key derivation
if (!PKCS5_PBKDF2_HMAC((const char *)input, input_len,
salt, salt_len,
10000, // iterations
EVP_sha256(),
output_len, output))
{
return CRYPTO_ERROR_INTERNAL;
}
return CRYPTO_OK;
}