int aws_cryptosdk_sig_get_privkey()

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;
}