diff options
Diffstat (limited to 'src/providers/dime/common/crypto.c')
-rw-r--r-- | src/providers/dime/common/crypto.c | 1085 |
1 files changed, 1085 insertions, 0 deletions
diff --git a/src/providers/dime/common/crypto.c b/src/providers/dime/common/crypto.c new file mode 100644 index 00000000..85615f9a --- /dev/null +++ b/src/providers/dime/common/crypto.c @@ -0,0 +1,1085 @@ +#include <stdio.h> +#include <string.h> + +#include <openssl/bio.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include "dime/common/dcrypto.h" +#include "dime/common/misc.h" +#include "dime/common/error.h" + +static EC_GROUP *_encryption_group = NULL; +static EVP_MD const *_ecies_envelope_evp = NULL; + +/** + * @brief + * Initialize the cryptographic subsystem. + * @return + * -1 if any part of the initialization process failed, or 0 on success. + */ +int +_crypto_init(void) +{ + SSL_load_error_strings(); + SSL_library_init(); + OPENSSL_add_all_algorithms_noconf(); + + if (!(_encryption_group = EC_GROUP_new_by_curve_name(EC_ENCRYPT_CURVE))) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT(ERR_UNSPEC, "could not initialize encryption curve"); + } + + if (!(_ecies_envelope_evp = EVP_get_digestbyname(OBJ_nid2sn(NID_sha512)))) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT(ERR_UNSPEC, "unable to get SHA-512 digest by NID"); + } + + //EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_COMPRESSED); + + return 0; +} + +/** + * @brief + * Shutdown the cryptographic subsystem. + */ +void +_crypto_shutdown(void) +{ + if (_encryption_group) { + EC_GROUP_clear_free(_encryption_group); + _encryption_group = NULL; + } + + EVP_cleanup(); + ERR_free_strings(); +} + +/** + * @brief + * Verify that an elliptic curve signature for a given hashed data buffer is + * valid. + * @param hash + * a pointer to the hashed data buffer used to generate the signature. + * @param hlen + * the length, in bytes, of the hashed data buffer. + * @param sig + * a pointer to the signature buffer to be verified against the input data. + * @param slen + * the length, in bytes, of the signature buffer. + * @param key + * the EC key which will have its public portion used to verify the signature + * of the supplied hashed data. + * @return + * -1 on general failure, 0 if the signature did not match the hashed data, or + * 1 if it did. + */ +int +_verify_ec_signature( + unsigned char const *hash, + size_t hlen, + unsigned char const *sig, + size_t slen, + EC_KEY *key) +{ + ECDSA_SIG *ec_sig; + int result; + + if (!hash || !hlen || !sig || !slen || !key) { + RET_ERROR_INT(ERR_BAD_PARAM, NULL); + } + + if (!(ec_sig = d2i_ECDSA_SIG(NULL, &sig, slen))) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT(ERR_UNSPEC, "unable to read EC signature from buffer"); + } + + if ((result = ECDSA_do_verify(hash, hlen, ec_sig, key)) < 0) { + PUSH_ERROR_OPENSSL(); + ECDSA_SIG_free(ec_sig); + RET_ERROR_INT(ERR_UNSPEC, "unable to complete ECDSA signature verification"); + } + + ECDSA_SIG_free(ec_sig); + + return result; +} + +/** + * @brief + * Verify that a signature for a given data buffer is valid. + * @param data + * a pointer to the data buffer used to generate the signature. + * @param dlen + * the length, in bytes, of the data buffer. + * @param + * shabits the number of bits for the desired SHA hash (160, 256, or 512). + * @param sig + * a pointer to the signature buffer to be verified against the input data. + * @param slen + * the length, in bytes, of the signature buffer. + * @param key + * the EC key which will have its public portion used to verify the signature + * of the supplied data. + * @return + * -1 on general failure, 0 if the signature did not match the data, or 1 if + * it did. + */ +int +_verify_ec_sha_signature( + unsigned char const *data, + size_t dlen, + unsigned int shabits, + unsigned char const *sig, + size_t slen, + EC_KEY *key) +{ + unsigned char hashbuf[SHA_512_SIZE]; + + if (!data || !dlen || !sig || !slen || !key) { + RET_ERROR_INT(ERR_BAD_PARAM, NULL); + } else if ((shabits != 160) && (shabits != 256) && (shabits != 512)) { + RET_ERROR_INT( + ERR_BAD_PARAM, + "ECDSA signature only accepts SHA hash sizes of 160, 256, or 512 bits"); + } + + if (_compute_sha_hash(shabits, data, dlen, hashbuf) < 0) { + RET_ERROR_INT( + ERR_UNSPEC, + "unable to compute SHA hash for ECDSA signature verification operation"); + } + + return (_verify_ec_signature(hashbuf, shabits / 8, sig, slen, key)); +} + +/** + * @brief + * Sign a body of data using the ECDSA algorithm. + * @param hash + * a pointer to the hashed data buffer to be signed. + * @param hlen + * the length, in bytes, of the hashed data to be signed. + * @param key + * the EC key which will have its private portion used to sign the supplied data. + * @param siglen + * a pointer to a variable that will receive the length of the data signature + * buffer on success. + * @return + * NULL on failure, or a pointer to the newly allocated signature data buffer + * on success. + */ +unsigned char * +_ec_sign_data( + unsigned char const *hash, + size_t hlen, + EC_KEY *key, + size_t *siglen) +{ + ECDSA_SIG *signature; + unsigned char *buf = NULL; + int bsize; + + if (!hash || !hlen || !key || !siglen) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } + + if (!(signature = ECDSA_do_sign(hash, hlen, key))) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_PTR( + ERR_UNSPEC, + "unable to take ECDSA signature of hash buffer"); + } + + if ((bsize = i2d_ECDSA_SIG(signature, &buf)) < 0) { + PUSH_ERROR_OPENSSL(); + ECDSA_SIG_free(signature); + RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize ECDSA signature"); + } + + ECDSA_SIG_free(signature); + + *siglen = bsize; + + return buf; +} + +/** + * @brief + * Sign a SHA-hashed body of data using the ECDSA algorithm. + * @param data + * a pointer to the data buffer to be signed. + * @param dlen + * the length, in bytes, of the data buffer to be signed. + * @param + * shabits the number of bits for the desired SHA hash (160, 256, or 512). + * @param key + * the EC key which will have its private portion used to sign the supplied + * data. + * @param siglen + * a pointer to a variable that will receive the length of the data signature + * buffer on success. + * @return + * NULL on failure, or a pointer to the newly allocated signature data buffer + * on success. + */ +unsigned char * +_ec_sign_sha_data( + unsigned char const *data, + size_t dlen, + unsigned int shabits, + EC_KEY *key, + size_t *siglen) +{ + unsigned char hashbuf[SHA_512_SIZE]; + + if (!data || !dlen || !key || !siglen) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } else if ((shabits != 160) && (shabits != 256) && (shabits != 512)) { + RET_ERROR_PTR( + ERR_BAD_PARAM, + "ECDSA signature only accepts SHA hash sizes of 160, 256, or 512 bits"); + } + + if (_compute_sha_hash(shabits, data, dlen, hashbuf) < 0) { + RET_ERROR_PTR( + ERR_UNSPEC, + "unable to compute SHA hash for ECDSA signature operation"); + } + + return (_ec_sign_data(hashbuf, shabits / 8, key, siglen)); +} + +/** + * @brief + * Serialize an EC public key to be shared. + * @param + * key a pointer to the EC key pair to have its public key serialized. + * @param + * outsize a pointer to a variable that will receive the length of the + * serialized key on success. + * @return + * a pointer to the serialized EC public key on success, or NULL on failure. + */ +unsigned char * +_serialize_ec_pubkey(EC_KEY *key, size_t *outsize) +{ + unsigned char *buf = NULL; + int bsize; + + if (!key || !outsize) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } + + if ((bsize = i2o_ECPublicKey(key, &buf)) < 0) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC public key"); + } + + *outsize = bsize; + + return buf; +} + +/** + * @brief + * Deserialize an EC public key stored in binary format. + * @param buf + * a pointer to the buffer holding the EC public key in binary format. + * @param blen + * the length, in bytes, of the buffer holding the EC public key. + * @param signing + * if set, generate a key from the pre-defined EC signing curve; if zero, the + * default encryption curve will be used instead. + * @return + * a pointer to the deserialized EC public key on success, or NULL on failure. + */ +EC_KEY * +_deserialize_ec_pubkey( + unsigned char const *buf, + size_t blen, + int signing) +{ + EC_KEY *result; + int nid; + + if (!buf || !blen) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } else if (signing) { + RET_ERROR_PTR( + ERR_BAD_PARAM, + "deserialization of signing keys is not supported"); + } + + nid = signing ? EC_SIGNING_CURVE : EC_ENCRYPT_CURVE; + + if (!(result = EC_KEY_new())) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_PTR( + ERR_UNSPEC, + "could not generate new EC key for deserialization"); + } + + if (EC_KEY_set_group(result, EC_GROUP_new_by_curve_name(nid)) != 1) { + PUSH_ERROR_OPENSSL(); + EC_KEY_free(result); + RET_ERROR_PTR( + ERR_UNSPEC, + "could not get curve group for deserialization"); + } + + if (!(result = + o2i_ECPublicKey( + &result, + (const unsigned char **)&buf, + blen))) + { + PUSH_ERROR_OPENSSL(); + EC_KEY_free(result); + RET_ERROR_PTR( + ERR_UNSPEC, + "deserialization of EC public key portion failed"); + } + + return result; +} + +/** + * @brief + * Serialize an EC private key into a data buffer. + * @param key + * a pointer to the EC key pair to have its private key serialized. + * @param outsize + * a pointer to a variable that will receive the length of the serialized key + * on success. + * @return + * a pointer to the serialized EC private key on success, or NULL on failure. + */ +unsigned char * +_serialize_ec_privkey(EC_KEY *key, size_t *outsize) +{ + unsigned char *buf = NULL; + int bsize; + + if (!key || !outsize) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } + + if ((bsize = i2d_ECPrivateKey(key, &buf)) < 0) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC private key"); + } + + *outsize = bsize; + + return buf; +} + +/** + * @brief + * Deserialize an EC private key stored in binary format. + * @param buf + * a pointer to the buffer holding the EC private key in binary format. + * @param blen + * the length, in bytes, of the buffer holding the EC private key. + * @param signing + * if set, generate a key from the pre-defined EC signing curve; if zero, the + * default encryption curve will be used instead. + * @return + * a pointer to the deserialized EC private key on success, or NULL on + * failure. + */ +EC_KEY * +_deserialize_ec_privkey( + unsigned char const *buf, + size_t blen, + int signing) +{ + EC_KEY *result; + int nid; + const unsigned char *bufptr = buf; + + if (!buf || !blen) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } else if (signing) { + RET_ERROR_PTR( + ERR_BAD_PARAM, + "deserialization of signing keys is not supported"); + } + + nid = signing ? EC_SIGNING_CURVE : EC_ENCRYPT_CURVE; + + if (!(result = EC_KEY_new())) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_PTR( + ERR_UNSPEC, + "could not generate new EC key for deserialization"); + } + + if (EC_KEY_set_group( + result, + EC_GROUP_new_by_curve_name(nid)) + != 1) + { + PUSH_ERROR_OPENSSL(); + EC_KEY_free(result); + RET_ERROR_PTR( + ERR_UNSPEC, + "could not get curve group for deserialization"); + } + + if (!(result = d2i_ECPrivateKey(&result, &bufptr, blen))) { + PUSH_ERROR_OPENSSL(); + EC_KEY_free(result); + RET_ERROR_PTR( + ERR_UNSPEC, + "deserialization of EC public key portion failed"); + } + + /* + * At this point, in most cases bufptr == buf + blen. There may be cases + * though where the private key is shorter than the provided buffer. This + * is because DER is a variable-length encoding. Parsing any field behind + * the privkey must take this into account. + */ + + return result; +} + +/** + * @brief + * Load an EC private key from a file. + * @param filename + * the name of the filename from which the key should be loaded + * @return + * a pointer to the deserialized private key from the the file. + */ +EC_KEY * +_load_ec_privkey(char const *filename) +{ + char *filedata; + unsigned char *bin; + size_t binsize; + EC_KEY *result; + + if(!filename) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } + + if(!(filedata = _read_pem_data(filename, "EC PRIVATE KEY", 1))) { + RET_ERROR_PTR(ERR_UNSPEC, "could not read ec pubkey pem file"); + } + + bin = _b64decode(filedata, strlen(filedata), &binsize); + _secure_wipe(filedata, strlen(filedata)); + free(filedata); + if(!bin) { + RET_ERROR_PTR(ERR_UNSPEC, "could not decode b64 data"); + } + + result = _deserialize_ec_privkey(bin, binsize, 0); + _secure_wipe(bin, binsize); + free(bin); + if(!result) { + RET_ERROR_PTR(ERR_UNSPEC, "could not deserialize binary ec pubkey"); + } + + return result; +} + +/** + * @brief + * Load an EC public key from a file. + * @param filename + * the name of the file from which the key should be loaded + * @return + * a pointer to the deserialized public key from the the file. + */ +EC_KEY * +_load_ec_pubkey(char const *filename) +{ + char *filedata; + unsigned char *bin; + size_t binsize; + EC_KEY *result; + + if(!filename) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } + + if(!(filedata = _read_pem_data(filename, "PUBLIC KEY", 1))) { + RET_ERROR_PTR( + ERR_UNSPEC, + "could not read ec pubkey pem file"); + } + + bin = _b64decode(filedata, strlen(filedata), &binsize); + _secure_wipe(filedata, strlen(filedata)); + free(filedata); + if(!bin) { + RET_ERROR_PTR(ERR_UNSPEC, "could not decode b64 data"); + } + + result = _deserialize_ec_pubkey(bin, binsize, 0); + _secure_wipe(bin, binsize); + free(bin); + if(!result) { + RET_ERROR_PTR( + ERR_UNSPEC, + "could not deserialize binary ec pubkey"); + } + + return result; +} + +/** + * @brief + * Generate an EC key pair. + * @return + * a newly allocated and generated EC key pair on success, or NULL on failure. + */ +EC_KEY * +_generate_ec_keypair(void) +{ + EC_GROUP *group; + EC_KEY *result; + + group = _encryption_group; + + if (!group) { + RET_ERROR_PTR(ERR_UNSPEC, "could not determine curve group for operation"); + } + + if (!(result = EC_KEY_new())) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_PTR(ERR_UNSPEC, "unable to allocate new EC key pair for generation"); + } + + if (EC_KEY_set_group(result, group) != 1) { + PUSH_ERROR_OPENSSL(); + EC_KEY_free(result); + RET_ERROR_PTR(ERR_UNSPEC, "unable to associate curve group with new EC key pair"); + } + + if (EC_KEY_generate_key(result) != 1) { + PUSH_ERROR_OPENSSL(); + EC_KEY_free(result); + RET_ERROR_PTR(ERR_UNSPEC, "unable to generate new EC key pair"); + } + + return result; +} + +/** + * @brief + * Free an EC keypair. + * @param + * key a pointer to the EC keypair to be freed. + */ +void +_free_ec_key(EC_KEY *key) +{ + if (!key) { + //fprintf(stderr, "Error: Attempted to free NULL EC key.\n"); + return; + } + + EC_KEY_free(key); +} + +/** + * @brief + * Generate an ed25519 key pair. + * @return + * a newly allocated and generated ed25519 key pair on success, or NULL on + * failure. + */ +ED25519_KEY * +_generate_ed25519_keypair(void) +{ + ED25519_KEY *result; + + if (!(result = malloc(sizeof(ED25519_KEY)))) { + PUSH_ERROR_SYSCALL("malloc"); + RET_ERROR_PTR( + ERR_NOMEM, + "could not generate ed25519 key because of " + "memory allocation error"); + } + + memset(result, 0, sizeof(ED25519_KEY)); + + if (RAND_bytes( + result->private_key, + sizeof(result->private_key)) + != 1) + { + PUSH_ERROR_OPENSSL(); + _secure_wipe(result, sizeof(ED25519_KEY)); + free(result); + RET_ERROR_PTR(ERR_UNSPEC, "could not generate ed25519 secret key"); + } + + ed25519_publickey(result->private_key, result->public_key); + + return result; +} + +/** + * @brief + * Take an ed25519 signature of a data buffer. + * @return + * 0 on success or -1 on failure. + */ +int _ed25519_sign_data( + unsigned char const *data, + size_t dlen, + ED25519_KEY *key, + ed25519_signature sigbuf) +{ + if (!data || !dlen || !key || !sigbuf) { + RET_ERROR_INT(ERR_BAD_PARAM, NULL); + } + + ed25519_sign(data, dlen, key->private_key, key->public_key, sigbuf); + + return 0; +} + +/** + * @brief + * Verify an ed25519 signature taken over a data buffer. + * @return + * 1 if the signature matched the buffer, 0 if it did not, or -1 on failure. + */ +int +_ed25519_verify_sig( + unsigned char const *data, + size_t dlen, + ED25519_KEY *key, + ed25519_signature sigbuf) +{ + int result; + + if (!data || !dlen || !key || !sigbuf) { + RET_ERROR_INT(ERR_BAD_PARAM, NULL); + } + + result = ed25519_sign_open(data, dlen, key->public_key, sigbuf); + + if (!result) { + return 1; + } + + return 0; +} + +/** + * @brief + * Free an ed25519 keypair. + * @param key + * a pointer to the ed25519 keypair to be freed. + */ +void +_free_ed25519_key(ED25519_KEY *key) +{ + if (!key) { + return; + } + + _secure_wipe(key, sizeof(ED25519_KEY)); + free(key); +} + +/** + * @brief + * Free a list of ed25519 keypairs. + * @param keys + * a pointer to a NULL terminated list of ed25519 keypair to be freed. + */ +void +_free_ed25519_key_chain(ED25519_KEY **keys) +{ + if (!keys) { + return; + } + + for(size_t i = 0; keys[i]; ++i) { + _free_ed25519_key(keys[i]); + } + + free(keys); +} + +/** + * @brief + * Load an ed25519 private key from a file. + * @param filename + * the path of the armored file from which the ed25519 private key will be + * loaded. + * @return + * a pointer to a newly allocated ed25519 keypair on success, or NULL on + * failure. + */ +ED25519_KEY * +_load_ed25519_privkey(char const *filename) +{ + ED25519_KEY *result; + unsigned char *keydata; + char *pemdata; + size_t klen; + + if (!filename) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } + + if (!(pemdata = + _read_pem_data(filename, "ED25519 PRIVATE KEY", 1))) { + RET_ERROR_PTR( + ERR_UNSPEC, + "unable to read ed25519 private key data from PEM file"); + } + + keydata = _b64decode(pemdata, strlen(pemdata), &klen); + _secure_wipe(pemdata, strlen(pemdata)); + free(pemdata); + + if (!keydata || (klen != ED25519_KEY_SIZE)) { + + if (keydata) { + _secure_wipe(keydata, klen); + free(keydata); + } + + RET_ERROR_PTR(ERR_UNSPEC, "bad ED25519 key data was read from file"); + } + + if (!(result = malloc(sizeof(ED25519_KEY)))) { + PUSH_ERROR_SYSCALL("malloc"); + _secure_wipe(keydata, klen); + free(keydata); + RET_ERROR_PTR(ERR_NOMEM, "unable to allocate space for ED25519 key"); + } + + memset(result, 0, sizeof(ED25519_KEY)); + memcpy(result->private_key, keydata, sizeof(result->private_key)); + _secure_wipe(keydata, klen); + free(keydata); + ed25519_publickey(result->private_key, result->public_key); + + return result; +} + +/** + * @brief + * Deserializes an ed25519 public key into a public-only ED25519_KEY structure + * that can only be used for signature verification, not signing. + * @param serial_pubkey + * Serialized ed25519 public key. + * @return + * Pointer to ED25519_KEY structure. +*/ +ED25519_KEY * +_deserialize_ed25519_pubkey( + unsigned char const *serial_pubkey) +{ + ED25519_KEY *key; + + if(!serial_pubkey) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } + + if(!(key = malloc(sizeof(ED25519_KEY)))) { + PUSH_ERROR_SYSCALL("malloc"); + RET_ERROR_PTR(ERR_NOMEM, NULL); + } + + memset(key, 0, sizeof(ED25519_KEY)); + memcpy(key->public_key, serial_pubkey, ED25519_KEY_SIZE); + + return key; +} + +/** + * @brief + * Deserializes an ed25519 private key into a ED25519_KEY structure. + * @param serial_privkey + * Serialized ed25519 private key. + * @return + * Pointer to the ED25119_KEY structure. +*/ +ED25519_KEY * +_deserialize_ed25519_privkey( + unsigned char const *serial_privkey) +{ + ED25519_KEY *key; + + if(!serial_privkey) { + RET_ERROR_PTR(ERR_BAD_PARAM, NULL); + } + + if(!(key = malloc(sizeof(ED25519_KEY)))) { + PUSH_ERROR_SYSCALL("malloc"); + RET_ERROR_PTR(ERR_NOMEM, NULL); + } + + memset(key, 0, sizeof(ED25519_KEY)); + memcpy(key->private_key, serial_privkey, ED25519_KEY_SIZE); + ed25519_publickey(key->private_key, key->public_key); + + return key; +} + +/** + * @note + * This function was taken from providers/cryptography/openssl.c + */ +void * +_ecies_env_derivation( + void const *input, + size_t ilen, + void *output, + size_t *olen) +{ + if (EVP_Digest( + input, + ilen, + output, + (unsigned int *)olen, + _ecies_envelope_evp, + NULL) + != 1) + { + return NULL; + } + + return output; +} + + +/** + * @brief + * Compute a derived AES-256 key from the intersection of a public EC key and + * a private EC key. + */ +int +_compute_aes256_kek( + EC_KEY *public_key, + EC_KEY *private_key, + unsigned char *keybuf) +{ + unsigned char aeskey[SHA_512_SIZE]; + + if (!public_key || !private_key || !keybuf) { + RET_ERROR_INT(ERR_BAD_PARAM, NULL); + } + + if (ECDH_compute_key( + aeskey, + sizeof(aeskey), + EC_KEY_get0_public_key(public_key), + private_key, + _ecies_env_derivation) + != SHA_512_SIZE) + { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT( + ERR_UNSPEC, + "could not derive AES key from EC keypair"); + } + + for(size_t i = 0; i < 16; ++i) { + keybuf[i] = aeskey[i] ^ aeskey[i + 16]; + } + + memcpy(keybuf + 16, aeskey + 32, 32); + _secure_wipe(aeskey, sizeof(aeskey)); + + return 0; +} + + +/** + * @brief + * Fill a buffer with a sequence of (securely) random bytes. + * @param buf + * a pointer to the buffer to be filled with random bytes. + * @param len + * the length, in bytes, of the buffer to be filled. + * @return + * 0 on success or -1 on failure. + */ +int +_get_random_bytes( + void *buf, + size_t len) +{ + if (!buf) { + RET_ERROR_INT(ERR_BAD_PARAM, NULL); + } + + if (!RAND_bytes(buf, len)) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT(ERR_UNSPEC, "unable to generate random bytes"); + } + + return 0; +} + +/** + * @brief + * Encrypt a data buffer using an AES-256 key (in CBC mode). + * @param outbuf + * a pointer to the output buffer that will receive the encrypted data. NOTE: + * the size of this buffer must be successfully negotiated by the caller. + * @param data + * a pointer to the data buffer to be encrypted. + * @param dlen + * the size, in bytes, of the data buffer to be encrypted. + * @param key + * a pointer to the 32-byte buffer holding the AES-256 encryption key for the + * operation. + * @param iv + * a pointer to the 32-byte initialization vector to be used for the + * encryption process. + * @return + * the number of bytes successfully encrypted on success, or -1 on failure. + */ +int +_encrypt_aes_256( + unsigned char *outbuf, + unsigned char const *data, + size_t dlen, + unsigned char const *key, + unsigned char const *iv) +{ + EVP_CIPHER_CTX *ctx; + int len, result; + + if (!outbuf || !data || !dlen || !key || !iv) { + RET_ERROR_INT(ERR_BAD_PARAM, NULL); + } + + if (dlen % AES_256_PADDING_SIZE) { + RET_ERROR_INT( + ERR_BAD_PARAM, + "input data was not aligned to required padding size"); + } + + if (!(ctx = EVP_CIPHER_CTX_new())) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT( + ERR_UNSPEC, + "unable to create new context for AES-256 encryption"); + } + + if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT( + ERR_UNSPEC, + "unable to initialize context for AES-256 encryption"); + } + + if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT( + ERR_UNSPEC, + "unable to set no padding for AES-256 encryption"); + } + + if (EVP_EncryptUpdate(ctx, outbuf, &len, data, dlen) != 1) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT( + ERR_UNSPEC, + "AES-256 encryption update failed"); + } + + result = len; + + if (EVP_EncryptFinal_ex(ctx, (unsigned char *)outbuf + len, &len) != 1) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT( + ERR_UNSPEC, + "AES-256 encryption finalization failed"); + } + + result += len; + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + return result; +} + +/** + * @brief + * Decrypt a data buffer using an AES-256 key (in CBC mode). + * @param outbuf + * a pointer to the output buffer that will receive the decrypted data. NOTE: + * the size of this buffer must be successfully negotiated by the caller. + * @param data + * a pointer to the data buffer to be decrypted. + * @param dlen + * the size, in bytes, of the data buffer to be decrypted. + * @param key + * a pointer to the 32-byte buffer holding the AES-256 decryption key for the + * operation. + * @param iv + * a pointer to the 32-byte initialization vector to be used for the + * decryption process. + * @return + * the number of bytes successfully decrypted on success, or -1 on failure. + */ +int +_decrypt_aes_256( + unsigned char *outbuf, + unsigned char const *data, + size_t dlen, + unsigned char const *key, + unsigned char const *iv) +{ + EVP_CIPHER_CTX *ctx = NULL; + int len, result; + + if (!outbuf || !data || !dlen || !key || !iv) { + RET_ERROR_INT(ERR_BAD_PARAM, NULL); + } + + if (dlen % AES_256_PADDING_SIZE) { + RET_ERROR_INT(ERR_BAD_PARAM, "input data was not aligned to required padding size"); + } + + if (!(ctx = EVP_CIPHER_CTX_new())) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT(ERR_UNSPEC, "unable to create new context for AES-256 decryption"); + } + + if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT(ERR_UNSPEC, "unable to initialize context for AES-256 decryption"); + } + + if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT(ERR_UNSPEC, "unable to set no padding for AES-256 decryption"); + } + + if (EVP_DecryptUpdate(ctx, outbuf, &len, data, dlen) != 1) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT(ERR_UNSPEC, "AES-256 decryption update failed"); + } + + result = len; + + if (EVP_DecryptFinal_ex(ctx, (unsigned char *)outbuf + len, &len) != 1) { + PUSH_ERROR_OPENSSL(); + RET_ERROR_INT(ERR_UNSPEC, "AES-256 decryption finalization failed"); + } + + result += len; + + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + + return result; +} |