From 8e203bf1f0e93e0e55d61489cf88eadac99aa2ee Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 1 Nov 2023 16:31:39 -0700 Subject: [PATCH] Example for key wrap using ECDH, HKDF and AES GCM. --- crypto/keywrap/keywrap.c | 309 ++++++++++++++++++++++ pk/ecdh_generate_secret/ecdh_gen_secret.c | 2 + 2 files changed, 311 insertions(+) create mode 100644 crypto/keywrap/keywrap.c diff --git a/crypto/keywrap/keywrap.c b/crypto/keywrap/keywrap.c new file mode 100644 index 00000000..3f63ab47 --- /dev/null +++ b/crypto/keywrap/keywrap.c @@ -0,0 +1,309 @@ +/* Example showing ECC shared secret and HKDF to generate a symmetric key */ +/* +gcc -Wall -okeywrap -lwolfssl keywrap.c +*/ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_ECC) && !defined(NO_HMAC) && defined(HAVE_HKDF) && defined(HAVE_AESGCM) + +#define ECC_CURVE_ID ECC_SECP384R1 +#define USE_PEM /* USE_PEM, USE_DER else KeyGen */ + +/* from server-ecc384-key.pem */ +#ifdef USE_PEM +static const char* privKeyPem = + "-----BEGIN PRIVATE KEY-----\n" + "MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCk5QboBhY+q4n4YEPA\n" + "YCXbunv+GTUIVWV24tzgAYtraN/Pb4ASznk36yuce8RoHHShZANiAATqz5NPLAm7\n" + "ORQPVmTDQLTfDmOu5XFLAMwEl//h6TiWu1+RsmrMtTlfj3BZ8QH2WisBbGgLz1Ul\n" + "r22YSAqodMmpF6AMw/vTI2j+BDxjUIg7uU98ZzT3O6lz5xvDUV4iGOw=\n" + "-----END PRIVATE KEY-----"; +#endif +#ifdef USE_DER +static const uint8_t privKeyDer[] = { + 0x30, 0x81, 0xA4, 0x02, 0x01, 0x01, 0x04, 0x30, 0xA4, 0xE5, 0x06, 0xE8, 0x06, + 0x16, 0x3E, 0xAB, 0x89, 0xF8, 0x60, 0x43, 0xC0, 0x60, 0x25, 0xDB, 0xBA, 0x7B, + 0xFE, 0x19, 0x35, 0x08, 0x55, 0x65, 0x76, 0xE2, 0xDC, 0xE0, 0x01, 0x8B, 0x6B, + 0x68, 0xDF, 0xCF, 0x6F, 0x80, 0x12, 0xCE, 0x79, 0x37, 0xEB, 0x2B, 0x9C, 0x7B, + 0xC4, 0x68, 0x1C, 0x74, 0xA0, 0x07, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, + 0xA1, 0x64, 0x03, 0x62, 0x00, 0x04, 0xEA, 0xCF, 0x93, 0x4F, 0x2C, 0x09, 0xBB, + 0x39, 0x14, 0x0F, 0x56, 0x64, 0xC3, 0x40, 0xB4, 0xDF, 0x0E, 0x63, 0xAE, 0xE5, + 0x71, 0x4B, 0x00, 0xCC, 0x04, 0x97, 0xFF, 0xE1, 0xE9, 0x38, 0x96, 0xBB, 0x5F, + 0x91, 0xB2, 0x6A, 0xCC, 0xB5, 0x39, 0x5F, 0x8F, 0x70, 0x59, 0xF1, 0x01, 0xF6, + 0x5A, 0x2B, 0x01, 0x6C, 0x68, 0x0B, 0xCF, 0x55, 0x25, 0xAF, 0x6D, 0x98, 0x48, + 0x0A, 0xA8, 0x74, 0xC9, 0xA9, 0x17, 0xA0, 0x0C, 0xC3, 0xFB, 0xD3, 0x23, 0x68, + 0xFE, 0x04, 0x3C, 0x63, 0x50, 0x88, 0x3B, 0xB9, 0x4F, 0x7C, 0x67, 0x34, 0xF7, + 0x3B, 0xA9, 0x73, 0xE7, 0x1B, 0xC3, 0x51, 0x5E, 0x22, 0x18, 0xEC +}; +#endif + +/* from client-ecc384-key.pem */ +#ifdef USE_PEM +/* openssl ec -in ./certs/client-ecc384-key.pem -pubout -text */ +static const char* pubKeyPem = + "-----BEGIN PUBLIC KEY-----\n" + "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEZsQIPWanoRXUUwojs60Lzo/I9Jgdptiy\n" + "biIR+rnvmcD6KT5IAPn+wqZKG6cSqGuQTBy7rF1uDmLOcCD3Q3fYl8d002j+iex3\n" + "yxkviUodd/mXS2YCaKVir5WBy+MkNuuF\n" + "-----END PUBLIC KEY-----"; +#endif +#ifdef USE_DER +static uint8_t pubKeyDer[] = { + 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, + 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0x66, 0xC4, + 0x08, 0x3D, 0x66, 0xA7, 0xA1, 0x15, 0xD4, 0x53, 0x0A, 0x23, 0xB3, 0xAD, 0x0B, + 0xCE, 0x8F, 0xC8, 0xF4, 0x98, 0x1D, 0xA6, 0xD8, 0xB2, 0x6E, 0x22, 0x11, 0xFA, + 0xB9, 0xEF, 0x99, 0xC0, 0xFA, 0x29, 0x3E, 0x48, 0x00, 0xF9, 0xFE, 0xC2, 0xA6, + 0x4A, 0x1B, 0xA7, 0x12, 0xA8, 0x6B, 0x90, 0x4C, 0x1C, 0xBB, 0xAC, 0x5D, 0x6E, + 0x0E, 0x62, 0xCE, 0x70, 0x20, 0xF7, 0x43, 0x77, 0xD8, 0x97, 0xC7, 0x74, 0xD3, + 0x68, 0xFE, 0x89, 0xEC, 0x77, 0xCB, 0x19, 0x2F, 0x89, 0x4A, 0x1D, 0x77, 0xF9, + 0x97, 0x4B, 0x66, 0x02, 0x68, 0xA5, 0x62, 0xAF, 0x95, 0x81, 0xCB, 0xE3, 0x24, + 0x36, 0xEB, 0x85 +}; +#endif + +static void print_bin(char* desc, uint8_t* s, int sLen) +{ + int i; + printf("%s: ", desc); + for (i = 0; i < sLen; i++) + printf("%02x", s[i]); + printf("\n"); +} + + +int do_ecdh(WC_RNG* rng, int devId, uint8_t* secret, uint32_t* secretLen) +{ + int ret; + ecc_key myKey, peerKey; +#if defined(USE_PEM) || defined(USE_DER) + uint32_t idx; + #ifdef USE_PEM + uint8_t der[ECC_BUFSIZE]; + uint32_t derSz = (uint32_t)sizeof(der); + #else + uint8_t* der; + uint32_t derSz; + #endif +#endif + + memset(&myKey, 0, sizeof(myKey)); + memset(&peerKey, 0, sizeof(peerKey)); + + ret = wc_ecc_init_ex(&myKey, NULL, devId); + if (ret == 0) + ret = wc_ecc_init_ex(&peerKey, NULL, devId); + + /* load (or generate) private key */ + if (ret == 0) { + #ifdef USE_PEM + ret = wc_KeyPemToDer((const uint8_t*)privKeyPem, strlen(privKeyPem), + der, derSz, NULL); + if (ret > 0) { + derSz = ret; + ret = 0; + } + #elif defined(USE_DER) + der = (uint8_t*)privKeyDer; + derSz = sizeof(privKeyDer); + #endif + } + if (ret == 0) { + #if defined(USE_DER) || defined(USE_PEM) + idx = 0; + ret = wc_EccPrivateKeyDecode(der, &idx, &myKey, derSz); + #else + /* don't use fixed key, just generate a key to throw away (ephemeral) */ + ret = wc_ecc_make_key_ex(rng, 0, &myKey, ECC_CURVE_ID); + #endif + } + + /* load (or generate) public key */ + if (ret == 0) { + #ifdef USE_PEM + ret = wc_PubKeyPemToDer((const uint8_t*)pubKeyPem, strlen(pubKeyPem), + der, derSz); + if (ret > 0) { + derSz = ret; + ret = 0; + } + #elif defined(USE_DER) + der = (uint8_t*)pubKeyDer; + derSz = sizeof(pubKeyDer); + #endif + } + if (ret == 0) { + #if defined(USE_DER) || defined(USE_PEM) + idx = 0; + ret = wc_EccPublicKeyDecode(der, &idx, &peerKey, derSz); + #else + /* don't use fixed key, just generate a key to throw away (ephemeral) */ + ret = wc_ecc_make_key_ex(rng, 0, &peerKey, ECC_CURVE_ID); + #endif + } + + /* compute shared secret */ + if (ret == 0) { + *secretLen = wc_ecc_size(&myKey); + wc_ecc_set_rng(&myKey, rng); + ret = wc_ecc_shared_secret(&myKey, &peerKey, secret, secretLen); + } + + wc_ecc_free(&peerKey); + wc_ecc_free(&myKey); + + return ret; +} + +#if 0 +int wc_HKDF(int type, const uint8_t* inKey, uint32_t inKeySz, + const uint8_t* salt, uint32_t saltSz, + const uint8_t* info, uint32_t infoSz, + uint8_t* out, uint32_t outSz) +#endif + +int do_example(void) +{ + int ret; + uint8_t secret[MAX_ECC_BYTES]; + uint32_t secretLen = (uint32_t)sizeof(secret); + const uint8_t* kdfSalt = NULL; /* optional salt for kdf */ + const uint8_t* kdfInfo = NULL; /* optional info for kdf */ + uint32_t kdfSaltSz = 0; /* size of kdfSalt */ + uint32_t kdfInfoSz = 0; /* size of kdfInfo */ + WC_RNG rng; + uint8_t key[AES_256_KEY_SIZE]; + int keyLen = (int)sizeof(key); + uint8_t wrapKey[AES_256_KEY_SIZE]; + uint8_t wrapKeyEnc[AES_256_KEY_SIZE]; + int wrapKeyLen = (int)sizeof(wrapKey); + Aes aes; + uint8_t tag[AES_BLOCK_SIZE]; + uint8_t iv[GCM_NONCE_MID_SZ]; + uint8_t* aad = NULL; /* optional additional auth used in tag generation */ + uint32_t aadLen = 0; + int devId = INVALID_DEVID; + + + ret = wc_InitRng_ex(&rng, NULL, devId); + if (ret != 0) { + printf("RNG Init failed! %d (%s)\n", ret, wc_GetErrorString(ret)); + return ret; + } + + + /* create a shared secret between a private and public key */ + ret = do_ecdh(&rng, devId, secret, &secretLen); + if (ret == 0) { + print_bin("ECDH Secret", secret, (int)secretLen); + } + else { + printf("ECDH failed! %d (%s)\n", ret, wc_GetErrorString(ret)); + goto exit; + } + + /* derive a key that can be used for symmetric */ + ret = wc_HKDF(WC_SHA384, + secret, secretLen, + kdfSalt, kdfSaltSz, + kdfInfo, kdfInfoSz, + key, (uint32_t)keyLen + ); + if (ret == 0) { + print_bin("HKDF Derived Key", key, keyLen); + } + else { + printf("HKDF failed! %d (%s)\n", ret, wc_GetErrorString(ret)); + goto exit; + } + + /* generate random value */ + ret = wc_RNG_GenerateBlock(&rng, wrapKey, wrapKeyLen); + if (ret == 0) { + print_bin("Random Key", wrapKey, wrapKeyLen); + } + else { + printf("Random Key failed! %d (%s)\n", ret, wc_GetErrorString(ret)); + goto exit; + } + + /* IV */ + memset(iv, 0, sizeof(iv)); + + /* encrypt random value */ + ret = wc_AesInit(&aes, NULL, devId); + if (ret == 0) { + ret = wc_AesGcmSetKey(&aes, key, keyLen); + if (ret == 0) { + ret = wc_AesGcmEncrypt(&aes, + wrapKeyEnc, wrapKey, wrapKeyLen, /* out, in, len */ + iv, sizeof(iv), /* IV = should be unique for each key, this will use zero's */ + tag, sizeof(tag), /* output: tag used to validate integrity of data */ + aad, aadLen /* additional authentication data (optional) and mixed with tag */ + ); + } + wc_AesFree(&aes); + } + if (ret == 0) { + print_bin("AES GCM Key Wrap", wrapKeyEnc, wrapKeyLen); + } + else { + printf("AES GCM Key Wrap failed! %d (%s)\n", ret, wc_GetErrorString(ret)); + goto exit; + } + + /* test decrypt */ + ret = wc_AesInit(&aes, NULL, devId); + if (ret == 0) { + ret = wc_AesGcmSetKey(&aes, key, keyLen); + if (ret == 0) { + ret = wc_AesGcmDecrypt(&aes, + wrapKey, wrapKeyEnc, wrapKeyLen, /* out, in, len */ + iv, sizeof(iv), /* IV = should be unique for each key, this will use zero's */ + tag, sizeof(tag), /* output: tag used to validate integrity of data */ + aad, aadLen /* additional authentication data (optional) and mixed with tag */ + ); + } + wc_AesFree(&aes); + } + if (ret == 0) { + print_bin("Decrypted Random Key", wrapKey, wrapKeyLen); + } + else { + printf("AES GCM Key Unwrap failed! %d (%s)\n", ret, wc_GetErrorString(ret)); + goto exit; + } + + /* PKCS7 create bundle using my key and cert */ + /* see: pkcs7/authEnvelopedData-ktri.c */ + + +exit: + wc_FreeRng(&rng); + + return ret; +} +#endif + +int main(int argc, char** argv) +{ + int ret; +#if defined(HAVE_ECC) && !defined(NO_HMAC) && defined(HAVE_HKDF) && defined(HAVE_AESGCM) + ret = do_example(); +#else + printf("Example requires ECC, HMAC, HKDF and AES GCM\n"); + ret = -1; +#endif + return ret; +} diff --git a/pk/ecdh_generate_secret/ecdh_gen_secret.c b/pk/ecdh_generate_secret/ecdh_gen_secret.c index 00786792..16d9d6e1 100644 --- a/pk/ecdh_generate_secret/ecdh_gen_secret.c +++ b/pk/ecdh_generate_secret/ecdh_gen_secret.c @@ -91,11 +91,13 @@ int do_ecc(void) goto all_three; secretLen = ECC_256_BIT_FIELD; /* explicit set */ + wc_ecc_set_rng(&AliceKey, &rng); ret = wc_ecc_shared_secret(&AliceKey, &BobKey, AliceSecret, &secretLen); if (ret != 0) goto all_three; secretLen = ECC_256_BIT_FIELD; /* explicit reset for best practice */ + wc_ecc_set_rng(&BobKey, &rng); ret = wc_ecc_shared_secret(&BobKey, &AliceKey, BobSecret, &secretLen); if (ret == 0) { if (XMEMCMP(AliceSecret, BobSecret, secretLen))