0
votes

I have been struggling to get ecdh working with openssl by passing in byte key buffers for the private and peer keys. Looking online, I can see how to do ecdh if the keys are generated in the ecdh function. What I haven't figured out is related to the comment in the sample code that says, "assumes pkey, peerkey have been already set up." (I don't think that I am setting these up properly.--I am new to openssl and there is probably something fundamental that I am missing.) I have generated two pairs of public/private keys using the included generateAsymmetricKeyPair() function. I then hard code those values so I could have the same values for each test. (Note that there is a prepended 0x04 value on the public keys for compression). What I have found is that I don't get any errors but the ecdh values that I get are different each time I call the function. (I would have expected to get the same result, even if it was wrong.) My code is included and if anyone could alert me to what I am doing wrong, it would be much appreciated.

    int32_t generateAsymmetricKeyPair(
        int32_t keySizeBits,
        unsigned char* publicKeyData,
        unsigned char* privateKeyData,
        unsigned char* keyIdData)
{
    EC_KEY                  *ecKey          = EC_KEY_new();
    EC_POINT                *ecPoint        = NULL;
    const EC_POINT          *ecPoint2       = NULL;
    EC_GROUP                *ecGroup        = NULL;
    point_conversion_form_t form            = POINT_CONVERSION_UNCOMPRESSED;
    int32_t                 asn1_flag       = OPENSSL_EC_NAMED_CURVE;
    int32_t                 sizePrvKeyBytes = 0;
    int32_t                 sizePubKeyBytes = 0;

    switch(keySizeBits)
    {
        case 256:
            ecGroup = EC_GROUP_new_by_curve_name(OBJ_sn2nid("prime256v1"));
            break;
        case 384:
            ecGroup = EC_GROUP_new_by_curve_name(OBJ_sn2nid("secp384r1"));
            break;
        case 521:
            ecGroup = EC_GROUP_new_by_curve_name(OBJ_sn2nid("secp521r1"));
            break;
        default:
            return 0;
    }

    sizePrvKeyBytes = keySizeBits / 8 + (((keySizeBits % 8) > 0 ) ? 1 : 0);
    sizePubKeyBytes = sizePrvKeyBytes * 2 + 1;

    EC_GROUP_set_asn1_flag(ecGroup, asn1_flag);
    EC_GROUP_set_point_conversion_form(ecGroup, form);
    EC_KEY_set_group(ecKey, ecGroup);

    ecPoint = EC_POINT_new(ecGroup);

    if(EC_KEY_generate_key(ecKey))
    {
        const BIGNUM *bn;

        /* Get Private Key */
        bn = EC_KEY_get0_private_key(ecKey);
        BN_bn2bin(bn, privateKeyData);

        /* Get Public Key */
        ecPoint2 = EC_KEY_get0_public_key(ecKey);
        EC_POINT_point2oct(EC_KEY_get0_group(ecKey),
                ecPoint2,
                POINT_CONVERSION_UNCOMPRESSED,
                publicKeyData,
                sizePubKeyBytes,
                NULL);

        /* Calculate keyId, Ignore format byte[0] */
        SHA1(publicKeyData + 1, sizePubKeyBytes - 1, keyIdData);
    }
    else
    {
        return 0;
    }
    return 1;
}

void deriveSharedSecret(
        unsigned char* pKey,
        unsigned char* peerKey,
        unsigned char* &secretKey,
        size_t* secretLen,
        int32_t privKeySize)
{
    bool                    grpForEc        = true;
    EVP_PKEY_CTX            *sharedCtx;
    EC_KEY                  *ecPrivKey      = NULL;
    EC_KEY                  *ecPeerKey      = NULL;
    EVP_PKEY                *evpPrivKey     = EVP_PKEY_new();
    EVP_PKEY                *evpPeerKey     = EVP_PKEY_new();
    BIGNUM                  *privBN         = BN_new();
    EC_POINT                *ecPubKey       = NULL;
    EC_POINT                *point          = NULL;
    BN_CTX                  *ctx            = BN_CTX_new();
    int32_t                 curve           = -1;
    int32_t                 peerKeySize     = -1;

    // One way of setting up EC key
    EC_GROUP                *ecGroup        = NULL;
    point_conversion_form_t form            = POINT_CONVERSION_UNCOMPRESSED;
    int32_t                 asn1_flag       = OPENSSL_EC_NAMED_CURVE;

    // Initialize output so that a return will result to NULL key and zero
    // length if error condition.
    *secretLen = 0;
    secretKey = NULL;

    // Create EC priv Key
    switch(privKeySize)
    {
        case 32:
            curve = NID_X9_62_prime256v1;
            break;
        case 48:
            curve = NID_secp384r1;
            break;
        case 66:
            curve = NID_secp521r1;
            break;
        default:
            return;
    }
    peerKeySize = (2 * privKeySize + 1); // Add one for compression byte

    try {
        if(NULL == (ecGroup = EC_GROUP_new_by_curve_name(curve))) {throw ossl_error();}
        EC_GROUP_set_asn1_flag(ecGroup, asn1_flag);
        EC_GROUP_set_point_conversion_form(ecGroup, form);
        if(NULL == (ecPrivKey = EC_KEY_new())) {throw ossl_error();}
        if(NULL == (ecPeerKey = EC_KEY_new())) {throw ossl_error();}
        if(1 != (EC_KEY_set_group(ecPrivKey, ecGroup))) {throw ossl_error();}
        if(1 != (EC_KEY_set_group(ecPeerKey, ecGroup))) {throw ossl_error();}

        /* PRIVATE KEY SETUP */
        // Convert to BIGNUM for private key conversion
        if(NULL == (BN_bin2bn(pKey, (privKeySize * 8), privBN))) {throw ossl_error();}

        // Convert private key to EC_KEY
        if(1 != (EC_KEY_set_private_key(ecPrivKey, privBN))) {throw ossl_error();}

        // Set the public key from the private key
        if(NULL == (ecPubKey = EC_POINT_new(EC_KEY_get0_group(ecPrivKey)))) {throw ossl_error();}
        if(1 != (EC_POINT_mul(EC_KEY_get0_group(ecPrivKey), ecPubKey,
                EC_KEY_get0_private_key(ecPrivKey), NULL, NULL, NULL))) {throw ossl_error();}
        if(1 != (EC_KEY_set_public_key(ecPrivKey, ecPubKey))) {throw ossl_error();}

        // Set the key in EVP_PKEY to ecPrivKey
        evpPrivKey = EVP_PKEY_new();
        if(1 != (EVP_PKEY_set1_EC_KEY(evpPrivKey, ecPrivKey))) {throw ossl_error();}

        /* PUBLIC PEER KEY SETUP */
        if(NULL == (point = EC_POINT_new(EC_KEY_get0_group(ecPeerKey)))) {throw ossl_error();}
        if(1 != (EC_POINT_oct2point(EC_KEY_get0_group(ecPeerKey), point, peerKey,
                peerKeySize, ctx))) {throw ossl_error();}

        if(1 != (EC_KEY_set_public_key(ecPeerKey, point))) {throw ossl_error();}

        // Set the key in EVP_PKEY to ecPeerKey
        if(1 != (EVP_PKEY_set1_EC_KEY(evpPeerKey, ecPeerKey))) {throw ossl_error();}

        /* SHARED SECRET SETUP */
        if(NULL == (sharedCtx = EVP_PKEY_CTX_new(evpPrivKey, NULL))) {throw ossl_error();}

        // Initialize
        if(1 != (EVP_PKEY_derive_init(sharedCtx))) {throw ossl_error();}

        // Provide the peer (public) key
        if(1 != (EVP_PKEY_derive_set_peer(sharedCtx, evpPeerKey))){throw ossl_error();}

        // Determine the buffer length for shared secret
        if((1 != EVP_PKEY_derive(sharedCtx, NULL, secretLen))) {throw ossl_error();}

        // Generate the shared secret buffer
        if(NULL == (secretKey = reinterpret_cast<unsigned char *>(OPENSSL_malloc(*secretLen))))
                {throw ossl_error();}

        // Derive the shared secret
        if(1 != (EVP_PKEY_derive(sharedCtx, secretKey, secretLen))) {throw ossl_error();}
    } catch(ossl_error& ex) {
        *secretLen = 0;
        secretKey = NULL;

        if(NULL != ecPrivKey) { EC_KEY_free(ecPrivKey); }
        if(NULL != ecPeerKey) { EC_KEY_free(ecPeerKey); }
        if(NULL != ecPubKey) { EC_POINT_free(ecPubKey); }
        if(NULL != point) { EC_POINT_free(point); }
        if(NULL != sharedCtx) { EVP_PKEY_CTX_free(sharedCtx); }
        if(NULL != evpPrivKey) { EVP_PKEY_free(evpPrivKey); }
        if(NULL != evpPeerKey) { EVP_PKEY_free(evpPeerKey); }
        if(NULL != ctx) { BN_CTX_free(ctx); }
        if(NULL != privBN) { BN_free(privBN); }

        return;
    }
    EC_KEY_free(ecPrivKey);
    EC_KEY_free(ecPeerKey);
    EC_POINT_free(ecPubKey);
    EC_POINT_free(point);
    EVP_PKEY_CTX_free(sharedCtx);
    EVP_PKEY_free(evpPrivKey);
    EVP_PKEY_free(evpPeerKey);
    BN_CTX_free(ctx);
    BN_free(privBN);

}

int main ()
{
    int32_t         keySizeBits     = 384;
    int32_t         keySize         = keySizeBits / 8;
    unsigned char   privateKeyData[keySize];
    unsigned char   publicKeyData[keySize * 2 + 1];
    unsigned char   keyIdData[keySize];
    unsigned char   privateKeyData2[keySize];
    unsigned char   publicKeyData2[keySize * 2 + 1];
    unsigned char   keyIdData2[keySize];
    unsigned char* sharedSecret = new unsigned char[keySize];
    unsigned char* sharedSecret2 = new unsigned char[keySize];
    size_t          secretLen       = 0;
    size_t          secretLen2      = 0;

    unsigned char _privateKeyData[] = {
            0x5d, 0xf0, 0x0d, 0x0f, 0x47, 0x1b, 0x3c, 0x11, 0xb7, 0x5d,
            0x12, 0x90, 0xff, 0x91, 0x93, 0xe6, 0xba, 0xd0, 0x75, 0xf9,
            0xe3, 0x48, 0xa2, 0x09, 0x85, 0x05, 0xe1, 0x0a, 0xf2, 0xfd,
            0xad, 0x9a, 0xf7, 0x98, 0xa3, 0xb3, 0x5f, 0x23, 0x80, 0xf2,
            0x05, 0x73, 0xeb, 0x73, 0x85, 0x0c, 0x1b, 0x3e
    };

    unsigned char _publicKeyData[] = {
            0x04, 0x9b, 0x6e, 0x53, 0x33, 0x4f, 0x87, 0x18, 0x45, 0xee,
            0x17, 0x9a, 0xbe, 0xa6, 0x41, 0xe1, 0xf0, 0xb2, 0x52, 0x9f,
            0xe5, 0x22, 0x67, 0x80, 0x64, 0x3b, 0x1b, 0x34, 0x8e, 0xa5,
            0x01, 0x2a, 0x06, 0xe4, 0xaa, 0xd2, 0xea, 0x29, 0x0a, 0xf5,
            0xa0, 0x80, 0x49, 0x1f, 0xa9, 0x4a, 0x30, 0x91, 0xd7, 0x59,
            0x27, 0x9c, 0xa8, 0x9b, 0x18, 0x76, 0xe4, 0xd6, 0x27, 0x93,
            0x0a, 0x6f, 0x01, 0x4d, 0x7d, 0x72, 0xb5, 0x78, 0x91, 0x8f,
            0x30, 0x9b, 0xe2, 0x55, 0x3f, 0xfa, 0x13, 0x3c, 0x52, 0x1b,
            0x5f, 0x56, 0xf7, 0x24, 0x3b, 0x54, 0x19, 0x0b, 0x61, 0x28,
            0x54, 0x72, 0x04, 0xd5, 0xb8, 0x0a, 0x76
    };


    unsigned char _privateKeyData2[] = {
            0x50, 0x96, 0xce, 0x89, 0xb3, 0x17, 0xbf, 0x63, 0x3d, 0x05,
            0x95, 0x82, 0x99, 0xc5, 0xd1, 0x75, 0x0d, 0x21, 0x23, 0x38,
            0xd6, 0x5e, 0x2a, 0xc6, 0x11, 0x09, 0xb4, 0xb0, 0x07, 0x18,
            0x0d, 0xa4, 0xb5, 0x2c, 0xf3, 0x96, 0x0d, 0x9c, 0x6e, 0xad,
            0x7c, 0x14, 0x7f, 0x5b, 0xcd, 0xfd, 0x33, 0xce
    };


    unsigned char _publicKeyData2[] = {
            0x04, 0x29, 0x00, 0x8a, 0xe7, 0xe2, 0x81, 0xab, 0xf2, 0xf4,
            0x2e, 0x1b, 0xb3, 0x73, 0xb4, 0xfd, 0xc6, 0x7e, 0x5e, 0x02,
            0x61, 0x25, 0x65, 0x6d, 0x5e, 0x44, 0xc9, 0x66, 0x21, 0x83,
            0x10, 0xea, 0xdc, 0x36, 0xa9, 0x7d, 0x1a, 0x55, 0xf0, 0x27,
            0xa5, 0x6f, 0x71, 0x36, 0x70, 0x57, 0xf7, 0x9f, 0x3b, 0x58,
            0x78, 0x3a, 0x14, 0x42, 0x58, 0x13, 0xf6, 0xef, 0xcb, 0x55,
            0xe2, 0xaa, 0x50, 0x95, 0x20, 0xa7, 0x29, 0x9c, 0x78, 0x94,
            0x9e, 0xe8, 0x81, 0x5a, 0x8e, 0x6e, 0x45, 0xd8, 0xd1, 0xae,
            0x1b, 0x51, 0x80, 0xcf, 0xce, 0x0c, 0x06, 0xab, 0x7d, 0xca,
            0xa3, 0xff, 0x4e, 0x65, 0x97, 0xb8, 0xd6
    };

    memset(privateKeyData, 0, keySize);
    memset(publicKeyData, 0, keySize * 2 + 1);
    memset(privateKeyData2, 0, keySize);
    memset(publicKeyData2, 0, keySize * 2 + 1);
    memset(sharedSecret, 0, keySize);
    memset(sharedSecret2, 0, keySize);
    memcpy(privateKeyData, _privateKeyData, keySize);
    memcpy(publicKeyData, _publicKeyData, keySize * 2 + 1);
    memcpy(privateKeyData2, _privateKeyData2, keySize);
    memcpy(publicKeyData2, _publicKeyData2, keySize * 2 + 1);

    deriveSharedSecret(privateKeyData, publicKeyData2,
            reinterpret_cast<unsigned char * &>(sharedSecret), &secretLen, keySizeBits / 8);
    deriveSharedSecret(privateKeyData2, publicKeyData,
            reinterpret_cast<unsigned char * &>(sharedSecret2), &secretLen2, keySizeBits / 8);

    OPENSSL_free(sharedSecret);
    OPENSSL_free(sharedSecret2);

    cout << "Out of Main" << endl;

    return 0;
}
1
Have you taken a look at Elliptic Curve Diffie Hellman on the OpenSSL wiki?jww
Chris Murphy recognized that I was multiplying my byte buffer by eight in: if(NULL == (BN_bin2bn(pKey, (privKeySize * 8), privBN))) {throw ossl_error();} This is what was causing my problem. Thanks for taking a look at the code.steamedboat

1 Answers

0
votes

The problem was that I was providing an incorrect size to BN_bin2bn. I was inadvertently converting the bytes to bits (multiplying by 8) and by changing the parameter to just the private key size caused the shared secret to be correct. Also, I found out that it was unnecessary to set the public key from the private key as the set_private_key function will do this already, although doing this did not cause my problem. Also, if anyone wants to use this code for reference, it is probably better not to have the public key contain the encoded character when passed in; the encoding should probably be done on the fly.