in source/cipher_openssl.c [337:445]
int aws_cryptosdk_sig_get_privkey(
const struct aws_cryptosdk_sig_ctx *ctx, struct aws_allocator *alloc, struct aws_string **priv_key) {
AWS_PRECONDITION(aws_cryptosdk_sig_ctx_is_valid(ctx));
AWS_PRECONDITION(ctx->is_sign);
AWS_PRECONDITION(AWS_OBJECT_PTR_IS_READABLE(priv_key));
/*
* When serializing private keys we use this ad-hoc format:
*
* 1. CryptoSDK algorithm ID (16-bit, big endian)
* 2. Public key length (8-bit)
* 3. Private key length (8-bit)
* 4. Public key (DER, compressed point format)
* 5. Private key (as an ASN.1 integer)
*
* We avoid the use of the d2i_ECPrivateKey format because it serializes the group,
* and we have no reliable way of checking whether the deserialized group was correct.
* In particular, EC_GROUP_cmp returns a non-equal result if the "method" of the group
* differs (i.e. if one uses a generic EC backend, and the other uses an optimized backend
* for the specific group). This can happen if i2d_ECPrivateKey chose an explicit curve
* representation, which got loaded as a generic curve, while internally we choose a named
* curve for the curve to compare against.
*/
unsigned char *privkey_buf = NULL, *pubkey_buf = NULL;
ASN1_INTEGER *privkey_int = NULL;
/* Should be long enough to encode the private + compressed public key + alg id */
unsigned char tmparr[MAX_PUBKEY_SIZE * 2 + 2] = { 0 };
int privkey_len = 0, pubkey_len = 0;
int rv = AWS_OP_ERR;
struct aws_byte_buf tmpbuf = aws_byte_buf_from_array(tmparr, sizeof(tmparr));
struct aws_byte_cursor input = { 0 };
const uint16_t alg_id = aws_hton16(ctx->props->alg_id);
*priv_key = NULL;
privkey_int = BN_to_ASN1_INTEGER(EC_KEY_get0_private_key(ctx->keypair), NULL);
if (!privkey_int) {
aws_raise_error(AWS_ERROR_OOM);
goto err;
}
privkey_len = i2d_ASN1_INTEGER(privkey_int, &privkey_buf);
/*
* Clear and free the private key ASN1 string immediately, regardless of the success
* or failure of serialization, to simplify error handling.
*/
ASN1_STRING_clear_free(privkey_int);
privkey_int = NULL;
EC_KEY_set_conv_form(ctx->keypair, POINT_CONVERSION_COMPRESSED);
pubkey_len = i2o_ECPublicKey(ctx->keypair, &pubkey_buf);
if (!privkey_buf || !pubkey_buf) {
aws_raise_error(AWS_ERROR_OOM);
goto err;
}
if (privkey_len > 0xFF || pubkey_len > 0xFF) {
aws_raise_error(AWS_CRYPTOSDK_ERR_CRYPTO_UNKNOWN);
goto err;
}
tmpbuf.len = 0;
/* TODO: Refactor once writing routines are moved to aws_byte_bufs */
input = aws_byte_cursor_from_array((const uint8_t *)&alg_id, sizeof(alg_id));
if (aws_byte_buf_append(&tmpbuf, &input)) {
goto err;
}
tmpbuf.buffer[tmpbuf.len++] = pubkey_len;
tmpbuf.buffer[tmpbuf.len++] = privkey_len;
input = aws_byte_cursor_from_array(pubkey_buf, pubkey_len);
if (aws_byte_buf_append(&tmpbuf, &input)) {
goto err;
}
input = aws_byte_cursor_from_array(privkey_buf, privkey_len);
if (aws_byte_buf_append(&tmpbuf, &input)) {
goto err;
}
// Since this is an internal-only value, we don't bother with base64-encoding.
*priv_key = aws_string_new_from_array(alloc, tmpbuf.buffer, tmpbuf.len);
if (!*priv_key) {
// OOM
goto err;
}
rv = AWS_OP_SUCCESS;
err:
aws_secure_zero(tmparr, sizeof(tmparr));
if (privkey_buf) {
aws_secure_zero(privkey_buf, privkey_len);
aws_cryptosdk_free(privkey_buf);
}
if (pubkey_buf) {
aws_secure_zero(pubkey_buf, pubkey_len);
aws_cryptosdk_free(pubkey_buf);
}
// There is no error path that results in a non-NULL priv_key, so we don't need to
// clean that up.
AWS_POSTCONDITION(AWS_OBJECT_PTR_IS_READABLE(priv_key));
AWS_POSTCONDITION(rv == AWS_OP_SUCCESS ? aws_string_is_valid(*priv_key) : !*priv_key);
return rv;
}