/** * @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. * @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) { EC_KEY *result; if (!buf || !blen) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } if (!(result = EC_KEY_new_d())) { PUSH_ERROR_OPENSSL(); RET_ERROR_PTR(ERR_UNSPEC, "could not generate new EC key for deserialization"); } if (EC_KEY_set_group_d(result, EC_GROUP_new_by_curve_name_d(NID_secp256k1)) != 1) { PUSH_ERROR_OPENSSL(); EC_KEY_free_d(result); RET_ERROR_PTR(ERR_UNSPEC, "could not get curve group for deserialization"); } if (!(result = o2i_ECPublicKey_d(&result, (const unsigned char **)&buf, blen))) { PUSH_ERROR_OPENSSL(); EC_KEY_free_d(result); RET_ERROR_PTR(ERR_UNSPEC, "deserialization of EC public key portion failed"); } return result; }
/** * @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_d(hash, hlen, key))) { PUSH_ERROR_OPENSSL(); RET_ERROR_PTR(ERR_UNSPEC, "unable to take ECDSA signature of hash buffer"); } if ((bsize = i2d_ECDSA_SIG_d(signature, &buf)) < 0) { PUSH_ERROR_OPENSSL(); ECDSA_SIG_free_d(signature); RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize ECDSA signature"); } ECDSA_SIG_free_d(signature); *siglen = bsize; return buf; }
/** * @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; if (!key || !outsize) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } stringer_t *pub; if (!(pub = secp256k1_public_get(key, MANAGEDBUF(33)))) { PUSH_ERROR_OPENSSL(); RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC public key"); } buf = mm_alloc(33); memmove(buf, st_data_get(pub), st_length_get(pub)); *outsize = st_length_get(pub); // EC_KEY_set_conv_form_d(key, POINT_CONVERSION_COMPRESSED); // if ((bsize = i2o_ECPublicKey_d(key, &buf)) < 0) { // PUSH_ERROR_OPENSSL(); // RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC public key"); // } // *outsize = bsize; return buf; }
/** * @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_d())) { PUSH_ERROR_OPENSSL(); RET_ERROR_INT(ERR_UNSPEC, "unable to create new context for AES-256 decryption"); } if (EVP_DecryptInit_ex_d(ctx, EVP_aes_256_cbc_d(), 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_d(ctx, 0) != 1) { PUSH_ERROR_OPENSSL(); RET_ERROR_INT(ERR_UNSPEC, "unable to set no padding for AES-256 decryption"); } if (EVP_DecryptUpdate_d(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_d(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_d(ctx); ctx = NULL; return result; }
/** * @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_d(); SSL_library_init_d(); OPENSSL_add_all_algorithms_noconf_d(); if (!(_encryption_group = EC_GROUP_new_by_curve_name_d(EC_ENCRYPT_CURVE))) { PUSH_ERROR_OPENSSL(); RET_ERROR_INT(ERR_UNSPEC, "could not initialize encryption curve"); } if (!(_ecies_envelope_evp = EVP_get_digestbyname_d(OBJ_nid2sn_d(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 * 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_d(buf, len)) { PUSH_ERROR_OPENSSL(); RET_ERROR_INT(ERR_UNSPEC, "unable to generate random bytes"); } return 0; }
/** * @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_d(NULL, &sig, slen))) { PUSH_ERROR_OPENSSL(); RET_ERROR_INT(ERR_UNSPEC, "unable to read EC signature from buffer"); } if ((result = ECDSA_do_verify_d(hash, hlen, ec_sig, key)) < 0) { PUSH_ERROR_OPENSSL(); ECDSA_SIG_free_d(ec_sig); RET_ERROR_INT(ERR_UNSPEC, "unable to complete ECDSA signature verification"); } ECDSA_SIG_free_d(ec_sig); return result; }
END_TEST START_TEST(test_openssl_error_100) { ERR_clear_error(); for (int i = 0; i < 100; i++) { ERR_put_error(ERR_LIB_SYS, SYS_F_FOPEN, ERR_R_SYS_LIB, "filename", 100 + i); } PUSH_ERROR_OPENSSL(); const errinfo_t *last_error = get_last_error(); ck_assert_ptr_ne(NULL, last_error); ck_assert_int_eq(ERR_OPENSSL, last_error->errcode); ck_assert_uint_eq(sizeof(last_error->auxmsg) - 1, strlen(last_error->auxmsg)); }
END_TEST START_TEST(test_openssl_error_5) { ERR_clear_error(); for (int i = 0; i < 5; i++) { ERR_put_error(ERR_LIB_SYS, SYS_F_FOPEN, ERR_R_SYS_LIB, "filename", 100); } PUSH_ERROR_OPENSSL(); const errinfo_t *last_error = get_last_error(); ck_assert_ptr_ne(NULL, last_error); ck_assert_int_eq(ERR_OPENSSL, last_error->errcode); ck_assert_str_eq(ERRMSG SEP ERRMSG SEP ERRMSG SEP ERRMSG SEP ERRMSG, last_error->auxmsg); }
/** * @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; if (!key || !outsize) { RET_ERROR_PTR(ERR_BAD_PARAM, NULL); } stringer_t *priv; buf = mm_alloc(32); if (!(priv = secp256k1_private_get(key, PLACER(buf, 32)))) { // if ((bsize = i2d_ECPrivateKey_d(key, &buf)) < 0) { PUSH_ERROR_OPENSSL(); RET_ERROR_PTR(ERR_UNSPEC, "unable to serialize EC private key"); } *outsize = 32; return buf; }
/** * @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_d(aeskey, sizeof(aeskey), EC_KEY_get0_public_key_d(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; }
END_TEST /** * In an old version of the code that used strncat, this caused a SIGSEGV. */ START_TEST(test_openssl_error_longfilename) { ERR_clear_error(); #define fn10 "1234567890" #define fn50 fn10 fn10 fn10 fn10 fn10 #define fn250 fn50 fn50 fn50 fn50 fn50 #define fn1000 fn250 fn250 fn250 fn250 ERR_put_error(ERR_LIB_SYS, SYS_F_FOPEN, ERR_R_SYS_LIB, fn1000, 1); ERR_put_error(ERR_LIB_SYS, SYS_F_FOPEN, ERR_R_SYS_LIB, fn1000, 1); PUSH_ERROR_OPENSSL(); const errinfo_t *last_error = get_last_error(); ck_assert_ptr_ne(NULL, last_error); ck_assert_int_eq(ERR_OPENSSL, last_error->errcode); ck_assert_uint_eq(sizeof(last_error->auxmsg) - 1, strlen(last_error->auxmsg)); }
/** * @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_d(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_donna(result->private_key, result->public_key); return result; }