I am trying to create a derived key from my shared secret and shared key. I have done this before, in Java and Python, where it is a fairly straight forward process, however, it seems that in C this requires a few more steps.
Here is my working Java code:
HKDFBytesGenerator hkdf2 = new HKDFBytesGenerator(digest);
hkdf2.init(new HKDFParameters(sharedSecret, mobileDeviceEphemeralPublicKey, info));
byte[] sharedKey = new byte[48];
hkdf2.generateBytes(sharedKey, 0, sharedKey.length);
Where info is:
[-63, 20, 124, -15, -40, -14, -53, 23, 15, 24, -38, 39, 34, 28, -43, 47, 46, 37, 107, -15, 4, -92, -117, -45, 75, -7, -35, -80, -82, 78, 109, 95, -38, 40, -5, -123, 85, -79, 33, -96, 17, -86, 106, -121, -17, -52, -7, -111, -77, 11, 105, -117, 110, 12, -87, 19, 44, -74, 58, 95, 87, -98, -90, 19, 2, 13, 76, 41, 3, 44, 54, 4, 76, -11, 27, -82, 70, -107, -1, 36, -114, 41, -46, 122, -60, -111, -111, -14, -105, -85, -113, -74, 118, 7, -127, -100, 115, 37, -13, 71, -113, 48, 68, 2, 32, 83, -33, 54, -35, -77, 4, -95, -55, -128, -66, 126, -46, -23, -43, -63, -41, 66, 92, 104, 85, -19, 113, 85, -9, 95, -97, -4, -58, 89, 88, -76, -36, 2, 32, 119, -73, 86, -9, -45, 21, -66, -56, -101, -107, 118, -78, 51, -48, -118, 91, -14, 51, -40, -115, -7, -5, 58, -60, -75, 22, 29, 40, 13, -110, 94, 36]
mobileDeviceEphemeralPublicKey is:
[3, -13, 114, -79, -98, 38, 121, -52, -109, -35, 111, 100, 43, 78, -96, 42, -28, -32, -75, -87, 83, 22, -22, -105, 69, -109, 47, -23, 127, -62, -128, -109, -99]
And sharedSecret:
[-27, -24, -42, 64, 23, 20, 36, 24, 46, 45, 57, -60, -53, 5, 106, 82, -128, -126, 66, 62, 42, -60, -72, -75, -90, -38, 54, -97, 98, -67, 29, 59]
And as a result my sharedKey is
-85, 26, -57, -120, -55, -12, 58, 22, -36, -86, 40, -21, -69, -109, -86, 2, 2, 26, 87, 97, 51, -56, -16, -71, 95, -115, 9, -45, -98, 125, 35, -12, 100, 41, -68, -68, 9, -40, 43, 74, 108, 27, -101, -98, -67, 85, 119, -108
My broken C code is:
#include <stdio.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/sha.h>
#include <openssl/kdf.h>
#include <openssl/hmac.h>
void set_error(char **error, const char *message) {
if (error != NULL) {
if (*error == NULL) {
*error = strdup(message);
}
}
}
void concatenateByteArrays(unsigned char *result, int *result_len, unsigned char *arrays[], const int lengths[], int num_arrays) {
int pos = 0;
for (int i = 0; i < num_arrays; i++) {
memcpy(result + pos, arrays[i], lengths[i]);
pos += lengths[i];
}
*result_len = pos;
}
unsigned char *extractSharedKey(
unsigned char *mobileDeviceEphemeralPublicKey, int mobileDeviceEphemeralPublicKey_len,
unsigned char *terminalNonce, int terminalNonce_len,
unsigned char *mobileDeviceNonce, int mobileDeviceNonce_len,
unsigned char *collectorId, int collectorId_len,
unsigned char *terminalEphemeralPublicKeyCompressed, int terminalEphemeralPublicKeyCompressed_len,
unsigned char *signedData, int signedData_len,
unsigned char *sharedSecret, int sharedSecret_len,
int sharedKey_len, // Added parameter to specify the desired key length
char **error)
{
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
if (!pctx) {
set_error(error, "Failed to create HKDF context");
return NULL;
}
unsigned char info[1024]; // Ensure this buffer is large enough for your data.
int info_len;
unsigned char *arrays[] = {
terminalNonce, mobileDeviceNonce, collectorId,
terminalEphemeralPublicKeyCompressed, signedData
};
int lengths[] = {
terminalNonce_len, mobileDeviceNonce_len, collectorId_len,
terminalEphemeralPublicKeyCompressed_len, signedData_len
};
// Concatenate the arrays into 'info'
concatenateByteArrays(info, &info_len, arrays, lengths, 5);
printf("Info: ");
for (int i = 0; i < info_len; i++) {
printf("%02x ", info[i]);
}
printf("\n");
// Allocate memory for the shared key
unsigned char *sharedKey = (unsigned char *)malloc(sharedKey_len);
if (sharedKey == NULL) {
set_error(error, "Failed to allocate memory for shared key");
return NULL;
}
printf("mobile key: ");
for (int i = 0; i < mobileDeviceEphemeralPublicKey_len; i++) {
printf("%02x ", mobileDeviceEphemeralPublicKey[i]);
}
printf("\n");
printf("shared secret: ");
for (int i = 0; i < sharedSecret_len; i++) {
printf("%02x ", sharedSecret[i]);
}
if (EVP_PKEY_derive_init(pctx) <= 0) {
ERR_print_errors_fp(stderr);
set_error(error, "Failed to initialize HKDF");
EVP_PKEY_CTX_free(pctx);
free(sharedKey);
return NULL;
}
if (EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()) <= 0) {
ERR_print_errors_fp(stderr);
set_error(error, "Failed to set HKDF hash function");
EVP_PKEY_CTX_free(pctx);
free(sharedKey);
return NULL;
}
if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, mobileDeviceEphemeralPublicKey, mobileDeviceEphemeralPublicKey_len) <= 0) {
ERR_print_errors_fp(stderr);
set_error(error, "Failed to set HKDF salt");
EVP_PKEY_CTX_free(pctx);
free(sharedKey);
return NULL;
}
if (EVP_PKEY_CTX_set1_hkdf_key(pctx, sharedSecret, sharedSecret_len) <= 0) {
ERR_print_errors_fp(stderr);
set_error(error, "Failed to set HKDF key");
EVP_PKEY_CTX_free(pctx);
free(sharedKey);
return NULL;
}
if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info, info_len) <= 0) {
ERR_print_errors_fp(stderr);
set_error(error, "Failed to add HKDF info");
EVP_PKEY_CTX_free(pctx);
free(sharedKey);
return NULL;
}
printf("Derived shared key: ");
for (int i = 0; i < sharedKey_len; i++) {
printf("%02x ", sharedKey[i]);
}
printf("\n");
printf("Shared key length: %zu\n", sharedKey_len);
printf("pctx: %p\n", pctx);
if (EVP_PKEY_derive(pctx, sharedKey, &sharedKey_len) <= 0) {
ERR_print_errors_fp(stderr);
set_error(error, "HKDF operation failed");
EVP_PKEY_CTX_free(pctx);
free(sharedKey);
return NULL;
}
EVP_PKEY_CTX_free(pctx);
return sharedKey; // Return the derived shared key
}
EC_KEY* getPublicKeyFromBytes(const unsigned char* pubKeyBytes, size_t length, char** error) {
EC_KEY* key = NULL;
EC_POINT* point = NULL;
const EC_GROUP* group = NULL;
// Initialize OpenSSL
OpenSSL_add_all_algorithms();
// Create a new EC_KEY structure for the specified curve
key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (!key) {
set_error(error, "Error creating EC_KEY structure.");
return NULL;
}
// Get the group used by the curve
group = EC_KEY_get0_group(key);
// Create an EC_POINT object from the bytes
point = EC_POINT_new(group);
if (!point) {
set_error(error, "Error creating EC_POINT.");
EC_KEY_free(key);
return NULL;
}
if (EC_POINT_oct2point(group, point, pubKeyBytes, length, NULL) != 1) {
set_error(error, "Error converting bytes to EC_POINT.");
EC_POINT_free(point);
EC_KEY_free(key);
return NULL;
}
// Set the public key point in the EC_KEY structure
if (EC_KEY_set_public_key(key, point) != 1) {
set_error(error, "Error setting public key.");
EC_POINT_free(point);
EC_KEY_free(key);
return NULL;
}
// Free the point object
EC_POINT_free(point);
// Return the created key
return key;
}
int main() {
unsigned char mobileDeviceEphemeralPublicKey[] = {3, 243, 114, 177, 158, 38, 121, 204, 147, 221, 111, 100, 43, 78, 160, 42, 228, 224, 181, 169, 83, 22, 230, 151, 69, 147, 47, 233, 127, 194, 128, 147, 157};
unsigned char terminalNonce[] = {193, 20, 124, 241, 216, 242, 203, 23, 15, 24, 218, 39, 34, 28, 213, 47, 46, 37, 107, 241, 4, 164, 139, 211, 75, 249, 221, 176, 174, 78, 109, 95};
unsigned char mobileDeviceNonce[] = {218, 40, 251, 133, 85, 177, 33, 160, 17, 170, 106, 135, 239, 204, 249, 145, 179, 11, 105, 139, 110, 12, 169, 19, 44, 182, 58, 95, 87, 158, 166, 19};
unsigned char collectorId[] = {2, 13, 76, 41};
unsigned char terminalEphemeralPublicKeyCompressed[] = {3, 44, 54, 4, 76, 245, 27, 174, 70, 149, 255, 36, 142, 41, 210, 122, 196, 145, 145, 242, 151, 171, 143, 182, 118, 7, 129, 156, 115, 37, 243, 71, 143};
unsigned char signedData[] = {48, 68, 2, 32, 83, 223, 54, 221, 179, 4, 161, 201, 128, 190, 126, 210, 233, 213, 193, 215, 66, 92, 104, 85, 237, 113, 85, 247, 95, 159, 252, 198, 89, 88, 180, 220, 2, 32, 119, 183, 86, 247, 211, 21, 190, 200, 155, 149, 118, 178, 51, 208, 138, 91, 242, 51, 216, 141, 249, 251, 58, 196, 181, 22, 29, 40, 13, 146, 94, 36};
unsigned char sharedSecret[] = {229, 232, 214, 64, 23, 20, 36, 24, 46, 45, 57, 196, 203, 5, 106, 82, 128, 130, 66, 62, 42, 196, 184, 181, 166, 218, 54, 159, 98, 189, 29, 59};
char *error = NULL;
int sharedKeyLen = 48; // Desired length of the shared key
unsigned char *sharedKey = extractSharedKey(
mobileDeviceEphemeralPublicKey, sizeof(mobileDeviceEphemeralPublicKey),
terminalNonce, sizeof(terminalNonce),
mobileDeviceNonce, sizeof(mobileDeviceNonce),
collectorId, sizeof(collectorId),
terminalEphemeralPublicKeyCompressed, sizeof(terminalEphemeralPublicKeyCompressed),
signedData, sizeof(signedData),
sharedSecret, sizeof(sharedSecret),
sharedKeyLen, &error);
if (sharedKey) {
printf("Shared Key: ");
for (int i = 0; i < sharedKeyLen; i++) {
printf("%02X", sharedKey[i]);
}
printf("\n");
free(sharedKey); // Free the allocated memory for the shared key
} else {
printf("Error extracting shared key: %s\n", error);
free(error); // Free the allocated memory for the error message
}
return 0;
}
However, I get the error "HKDF operation failed". Any ideas would be greatly appreciated, I've spent a while trying to crack this 😅
As we were not able to reproduce it appears to be a portability issue or undefined behavior:
On linux you need to #define POSIX_C_SOURCE 200809L
and #include <string.h>
for strdup()
and memcpy()
.
Don't cast the void *
from malloc()
. It's unnecessary and may hide a type problem.
EVP_PKEY_derive()
expects the 3rd parameter keylen
to be a size_t *
but you have int sharedKey_len
.
Prefer the types in inttypes.h
for portable code. Native types may vary in size across platforms.
In extractSharedKey()
you haven't initialized sharedKey
before you print it (presumably leave this out; and when you do you will see that need to print a newline after the previous field):
printf("Derived shared key: ");
for (int i = 0; i < sharedKey_len; i++) {
printf("%02x ", sharedKey[i]);
}
printf("\n");