|
| 1 | +/* Example showing ECC shared secret and HKDF to generate a symmetric key */ |
| 2 | +/* |
| 3 | +gcc -Wall -okeywrap -lwolfssl keywrap.c |
| 4 | +*/ |
| 5 | +#include <stdio.h> |
| 6 | +#include <stdint.h> |
| 7 | + |
| 8 | +#include <wolfssl/options.h> |
| 9 | +#include <wolfssl/wolfcrypt/settings.h> |
| 10 | +#include <wolfssl/wolfcrypt/random.h> |
| 11 | +#include <wolfssl/wolfcrypt/ecc.h> |
| 12 | +#include <wolfssl/wolfcrypt/error-crypt.h> |
| 13 | +#include <wolfssl/wolfcrypt/asn_public.h> |
| 14 | +#include <wolfssl/wolfcrypt/hmac.h> |
| 15 | +#include <wolfssl/wolfcrypt/sha512.h> |
| 16 | +#include <wolfssl/wolfcrypt/aes.h> |
| 17 | + |
| 18 | +#if defined(HAVE_ECC) && !defined(NO_HMAC) && defined(HAVE_HKDF) && defined(HAVE_AESGCM) |
| 19 | + |
| 20 | +#define ECC_CURVE_ID ECC_SECP384R1 |
| 21 | +#define USE_PEM /* USE_PEM, USE_DER else KeyGen */ |
| 22 | + |
| 23 | +/* from server-ecc384-key.pem */ |
| 24 | +#ifdef USE_PEM |
| 25 | +static const char* privKeyPem = |
| 26 | + "-----BEGIN PRIVATE KEY-----\n" |
| 27 | + "MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCk5QboBhY+q4n4YEPA\n" |
| 28 | + "YCXbunv+GTUIVWV24tzgAYtraN/Pb4ASznk36yuce8RoHHShZANiAATqz5NPLAm7\n" |
| 29 | + "ORQPVmTDQLTfDmOu5XFLAMwEl//h6TiWu1+RsmrMtTlfj3BZ8QH2WisBbGgLz1Ul\n" |
| 30 | + "r22YSAqodMmpF6AMw/vTI2j+BDxjUIg7uU98ZzT3O6lz5xvDUV4iGOw=\n" |
| 31 | + "-----END PRIVATE KEY-----"; |
| 32 | +#endif |
| 33 | +#ifdef USE_DER |
| 34 | +static const uint8_t privKeyDer[] = { |
| 35 | + 0x30, 0x81, 0xA4, 0x02, 0x01, 0x01, 0x04, 0x30, 0xA4, 0xE5, 0x06, 0xE8, 0x06, |
| 36 | + 0x16, 0x3E, 0xAB, 0x89, 0xF8, 0x60, 0x43, 0xC0, 0x60, 0x25, 0xDB, 0xBA, 0x7B, |
| 37 | + 0xFE, 0x19, 0x35, 0x08, 0x55, 0x65, 0x76, 0xE2, 0xDC, 0xE0, 0x01, 0x8B, 0x6B, |
| 38 | + 0x68, 0xDF, 0xCF, 0x6F, 0x80, 0x12, 0xCE, 0x79, 0x37, 0xEB, 0x2B, 0x9C, 0x7B, |
| 39 | + 0xC4, 0x68, 0x1C, 0x74, 0xA0, 0x07, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, |
| 40 | + 0xA1, 0x64, 0x03, 0x62, 0x00, 0x04, 0xEA, 0xCF, 0x93, 0x4F, 0x2C, 0x09, 0xBB, |
| 41 | + 0x39, 0x14, 0x0F, 0x56, 0x64, 0xC3, 0x40, 0xB4, 0xDF, 0x0E, 0x63, 0xAE, 0xE5, |
| 42 | + 0x71, 0x4B, 0x00, 0xCC, 0x04, 0x97, 0xFF, 0xE1, 0xE9, 0x38, 0x96, 0xBB, 0x5F, |
| 43 | + 0x91, 0xB2, 0x6A, 0xCC, 0xB5, 0x39, 0x5F, 0x8F, 0x70, 0x59, 0xF1, 0x01, 0xF6, |
| 44 | + 0x5A, 0x2B, 0x01, 0x6C, 0x68, 0x0B, 0xCF, 0x55, 0x25, 0xAF, 0x6D, 0x98, 0x48, |
| 45 | + 0x0A, 0xA8, 0x74, 0xC9, 0xA9, 0x17, 0xA0, 0x0C, 0xC3, 0xFB, 0xD3, 0x23, 0x68, |
| 46 | + 0xFE, 0x04, 0x3C, 0x63, 0x50, 0x88, 0x3B, 0xB9, 0x4F, 0x7C, 0x67, 0x34, 0xF7, |
| 47 | + 0x3B, 0xA9, 0x73, 0xE7, 0x1B, 0xC3, 0x51, 0x5E, 0x22, 0x18, 0xEC |
| 48 | +}; |
| 49 | +#endif |
| 50 | + |
| 51 | +/* from client-ecc384-key.pem */ |
| 52 | +#ifdef USE_PEM |
| 53 | +/* openssl ec -in ./certs/client-ecc384-key.pem -pubout -text */ |
| 54 | +static const char* pubKeyPem = |
| 55 | + "-----BEGIN PUBLIC KEY-----\n" |
| 56 | + "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEZsQIPWanoRXUUwojs60Lzo/I9Jgdptiy\n" |
| 57 | + "biIR+rnvmcD6KT5IAPn+wqZKG6cSqGuQTBy7rF1uDmLOcCD3Q3fYl8d002j+iex3\n" |
| 58 | + "yxkviUodd/mXS2YCaKVir5WBy+MkNuuF\n" |
| 59 | + "-----END PUBLIC KEY-----"; |
| 60 | +#endif |
| 61 | +#ifdef USE_DER |
| 62 | +static uint8_t pubKeyDer[] = { |
| 63 | + 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, |
| 64 | + 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04, 0x66, 0xC4, |
| 65 | + 0x08, 0x3D, 0x66, 0xA7, 0xA1, 0x15, 0xD4, 0x53, 0x0A, 0x23, 0xB3, 0xAD, 0x0B, |
| 66 | + 0xCE, 0x8F, 0xC8, 0xF4, 0x98, 0x1D, 0xA6, 0xD8, 0xB2, 0x6E, 0x22, 0x11, 0xFA, |
| 67 | + 0xB9, 0xEF, 0x99, 0xC0, 0xFA, 0x29, 0x3E, 0x48, 0x00, 0xF9, 0xFE, 0xC2, 0xA6, |
| 68 | + 0x4A, 0x1B, 0xA7, 0x12, 0xA8, 0x6B, 0x90, 0x4C, 0x1C, 0xBB, 0xAC, 0x5D, 0x6E, |
| 69 | + 0x0E, 0x62, 0xCE, 0x70, 0x20, 0xF7, 0x43, 0x77, 0xD8, 0x97, 0xC7, 0x74, 0xD3, |
| 70 | + 0x68, 0xFE, 0x89, 0xEC, 0x77, 0xCB, 0x19, 0x2F, 0x89, 0x4A, 0x1D, 0x77, 0xF9, |
| 71 | + 0x97, 0x4B, 0x66, 0x02, 0x68, 0xA5, 0x62, 0xAF, 0x95, 0x81, 0xCB, 0xE3, 0x24, |
| 72 | + 0x36, 0xEB, 0x85 |
| 73 | +}; |
| 74 | +#endif |
| 75 | + |
| 76 | +static void print_bin(char* desc, uint8_t* s, int sLen) |
| 77 | +{ |
| 78 | + int i; |
| 79 | + printf("%s: ", desc); |
| 80 | + for (i = 0; i < sLen; i++) |
| 81 | + printf("%02x", s[i]); |
| 82 | + printf("\n"); |
| 83 | +} |
| 84 | + |
| 85 | + |
| 86 | +int do_ecdh(WC_RNG* rng, int devId, uint8_t* secret, uint32_t* secretLen) |
| 87 | +{ |
| 88 | + int ret; |
| 89 | + ecc_key myKey, peerKey; |
| 90 | +#if defined(USE_PEM) || defined(USE_DER) |
| 91 | + uint32_t idx; |
| 92 | + #ifdef USE_PEM |
| 93 | + uint8_t der[ECC_BUFSIZE]; |
| 94 | + uint32_t derSz = (uint32_t)sizeof(der); |
| 95 | + #else |
| 96 | + uint8_t* der; |
| 97 | + uint32_t derSz; |
| 98 | + #endif |
| 99 | +#endif |
| 100 | + |
| 101 | + memset(&myKey, 0, sizeof(myKey)); |
| 102 | + memset(&peerKey, 0, sizeof(peerKey)); |
| 103 | + |
| 104 | + ret = wc_ecc_init_ex(&myKey, NULL, devId); |
| 105 | + if (ret == 0) |
| 106 | + ret = wc_ecc_init_ex(&peerKey, NULL, devId); |
| 107 | + |
| 108 | + /* load (or generate) private key */ |
| 109 | + if (ret == 0) { |
| 110 | + #ifdef USE_PEM |
| 111 | + ret = wc_KeyPemToDer((const uint8_t*)privKeyPem, strlen(privKeyPem), |
| 112 | + der, derSz, NULL); |
| 113 | + if (ret > 0) { |
| 114 | + derSz = ret; |
| 115 | + ret = 0; |
| 116 | + } |
| 117 | + #elif defined(USE_DER) |
| 118 | + der = (uint8_t*)privKeyDer; |
| 119 | + derSz = sizeof(privKeyDer); |
| 120 | + #endif |
| 121 | + } |
| 122 | + if (ret == 0) { |
| 123 | + #if defined(USE_DER) || defined(USE_PEM) |
| 124 | + idx = 0; |
| 125 | + ret = wc_EccPrivateKeyDecode(der, &idx, &myKey, derSz); |
| 126 | + #else |
| 127 | + /* don't use fixed key, just generate a key to throw away (ephemeral) */ |
| 128 | + ret = wc_ecc_make_key_ex(rng, 0, &myKey, ECC_CURVE_ID); |
| 129 | + #endif |
| 130 | + } |
| 131 | + |
| 132 | + /* load (or generate) public key */ |
| 133 | + if (ret == 0) { |
| 134 | + #ifdef USE_PEM |
| 135 | + ret = wc_PubKeyPemToDer((const uint8_t*)pubKeyPem, strlen(pubKeyPem), |
| 136 | + der, derSz); |
| 137 | + if (ret > 0) { |
| 138 | + derSz = ret; |
| 139 | + ret = 0; |
| 140 | + } |
| 141 | + #elif defined(USE_DER) |
| 142 | + der = (uint8_t*)pubKeyDer; |
| 143 | + derSz = sizeof(pubKeyDer); |
| 144 | + #endif |
| 145 | + } |
| 146 | + if (ret == 0) { |
| 147 | + #if defined(USE_DER) || defined(USE_PEM) |
| 148 | + idx = 0; |
| 149 | + ret = wc_EccPublicKeyDecode(der, &idx, &peerKey, derSz); |
| 150 | + #else |
| 151 | + /* don't use fixed key, just generate a key to throw away (ephemeral) */ |
| 152 | + ret = wc_ecc_make_key_ex(rng, 0, &peerKey, ECC_CURVE_ID); |
| 153 | + #endif |
| 154 | + } |
| 155 | + |
| 156 | + /* compute shared secret */ |
| 157 | + if (ret == 0) { |
| 158 | + *secretLen = wc_ecc_size(&myKey); |
| 159 | + wc_ecc_set_rng(&myKey, rng); |
| 160 | + ret = wc_ecc_shared_secret(&myKey, &peerKey, secret, secretLen); |
| 161 | + } |
| 162 | + |
| 163 | + wc_ecc_free(&peerKey); |
| 164 | + wc_ecc_free(&myKey); |
| 165 | + |
| 166 | + return ret; |
| 167 | +} |
| 168 | + |
| 169 | +#if 0 |
| 170 | +int wc_HKDF(int type, const uint8_t* inKey, uint32_t inKeySz, |
| 171 | + const uint8_t* salt, uint32_t saltSz, |
| 172 | + const uint8_t* info, uint32_t infoSz, |
| 173 | + uint8_t* out, uint32_t outSz) |
| 174 | +#endif |
| 175 | + |
| 176 | +int do_example(void) |
| 177 | +{ |
| 178 | + int ret; |
| 179 | + uint8_t secret[MAX_ECC_BYTES]; |
| 180 | + uint32_t secretLen = (uint32_t)sizeof(secret); |
| 181 | + const uint8_t* kdfSalt = NULL; /* optional salt for kdf */ |
| 182 | + const uint8_t* kdfInfo = NULL; /* optional info for kdf */ |
| 183 | + uint32_t kdfSaltSz = 0; /* size of kdfSalt */ |
| 184 | + uint32_t kdfInfoSz = 0; /* size of kdfInfo */ |
| 185 | + WC_RNG rng; |
| 186 | + uint8_t key[AES_256_KEY_SIZE]; |
| 187 | + int keyLen = (int)sizeof(key); |
| 188 | + uint8_t wrapKey[AES_256_KEY_SIZE]; |
| 189 | + uint8_t wrapKeyEnc[AES_256_KEY_SIZE]; |
| 190 | + int wrapKeyLen = (int)sizeof(wrapKey); |
| 191 | + Aes aes; |
| 192 | + uint8_t tag[AES_BLOCK_SIZE]; |
| 193 | + uint8_t iv[GCM_NONCE_MID_SZ]; |
| 194 | + uint8_t* aad = NULL; /* optional additional auth used in tag generation */ |
| 195 | + uint32_t aadLen = 0; |
| 196 | + int devId = INVALID_DEVID; |
| 197 | + |
| 198 | + |
| 199 | + ret = wc_InitRng_ex(&rng, NULL, devId); |
| 200 | + if (ret != 0) { |
| 201 | + printf("RNG Init failed! %d (%s)\n", ret, wc_GetErrorString(ret)); |
| 202 | + return ret; |
| 203 | + } |
| 204 | + |
| 205 | + |
| 206 | + /* create a shared secret between a private and public key */ |
| 207 | + ret = do_ecdh(&rng, devId, secret, &secretLen); |
| 208 | + if (ret == 0) { |
| 209 | + print_bin("ECDH Secret", secret, (int)secretLen); |
| 210 | + } |
| 211 | + else { |
| 212 | + printf("ECDH failed! %d (%s)\n", ret, wc_GetErrorString(ret)); |
| 213 | + goto exit; |
| 214 | + } |
| 215 | + |
| 216 | + /* derive a key that can be used for symmetric */ |
| 217 | + ret = wc_HKDF(WC_SHA384, |
| 218 | + secret, secretLen, |
| 219 | + kdfSalt, kdfSaltSz, |
| 220 | + kdfInfo, kdfInfoSz, |
| 221 | + key, (uint32_t)keyLen |
| 222 | + ); |
| 223 | + if (ret == 0) { |
| 224 | + print_bin("HKDF Derived Key", key, keyLen); |
| 225 | + } |
| 226 | + else { |
| 227 | + printf("HKDF failed! %d (%s)\n", ret, wc_GetErrorString(ret)); |
| 228 | + goto exit; |
| 229 | + } |
| 230 | + |
| 231 | + /* generate random value */ |
| 232 | + ret = wc_RNG_GenerateBlock(&rng, wrapKey, wrapKeyLen); |
| 233 | + if (ret == 0) { |
| 234 | + print_bin("Random Key", wrapKey, wrapKeyLen); |
| 235 | + } |
| 236 | + else { |
| 237 | + printf("Random Key failed! %d (%s)\n", ret, wc_GetErrorString(ret)); |
| 238 | + goto exit; |
| 239 | + } |
| 240 | + |
| 241 | + /* IV */ |
| 242 | + memset(iv, 0, sizeof(iv)); |
| 243 | + |
| 244 | + /* encrypt random value */ |
| 245 | + ret = wc_AesInit(&aes, NULL, devId); |
| 246 | + if (ret == 0) { |
| 247 | + ret = wc_AesGcmSetKey(&aes, key, keyLen); |
| 248 | + if (ret == 0) { |
| 249 | + ret = wc_AesGcmEncrypt(&aes, |
| 250 | + wrapKeyEnc, wrapKey, wrapKeyLen, /* out, in, len */ |
| 251 | + iv, sizeof(iv), /* IV = should be unique for each key, this will use zero's */ |
| 252 | + tag, sizeof(tag), /* output: tag used to validate integrity of data */ |
| 253 | + aad, aadLen /* additional authentication data (optional) and mixed with tag */ |
| 254 | + ); |
| 255 | + } |
| 256 | + wc_AesFree(&aes); |
| 257 | + } |
| 258 | + if (ret == 0) { |
| 259 | + print_bin("AES GCM Key Wrap", wrapKeyEnc, wrapKeyLen); |
| 260 | + } |
| 261 | + else { |
| 262 | + printf("AES GCM Key Wrap failed! %d (%s)\n", ret, wc_GetErrorString(ret)); |
| 263 | + goto exit; |
| 264 | + } |
| 265 | + |
| 266 | + /* test decrypt */ |
| 267 | + ret = wc_AesInit(&aes, NULL, devId); |
| 268 | + if (ret == 0) { |
| 269 | + ret = wc_AesGcmSetKey(&aes, key, keyLen); |
| 270 | + if (ret == 0) { |
| 271 | + ret = wc_AesGcmDecrypt(&aes, |
| 272 | + wrapKey, wrapKeyEnc, wrapKeyLen, /* out, in, len */ |
| 273 | + iv, sizeof(iv), /* IV = should be unique for each key, this will use zero's */ |
| 274 | + tag, sizeof(tag), /* output: tag used to validate integrity of data */ |
| 275 | + aad, aadLen /* additional authentication data (optional) and mixed with tag */ |
| 276 | + ); |
| 277 | + } |
| 278 | + wc_AesFree(&aes); |
| 279 | + } |
| 280 | + if (ret == 0) { |
| 281 | + print_bin("Decrypted Random Key", wrapKey, wrapKeyLen); |
| 282 | + } |
| 283 | + else { |
| 284 | + printf("AES GCM Key Unwrap failed! %d (%s)\n", ret, wc_GetErrorString(ret)); |
| 285 | + goto exit; |
| 286 | + } |
| 287 | + |
| 288 | + /* PKCS7 create bundle using my key and cert */ |
| 289 | + /* see: pkcs7/authEnvelopedData-ktri.c */ |
| 290 | + |
| 291 | + |
| 292 | +exit: |
| 293 | + wc_FreeRng(&rng); |
| 294 | + |
| 295 | + return ret; |
| 296 | +} |
| 297 | +#endif |
| 298 | + |
| 299 | +int main(int argc, char** argv) |
| 300 | +{ |
| 301 | + int ret; |
| 302 | +#if defined(HAVE_ECC) && !defined(NO_HMAC) && defined(HAVE_HKDF) && defined(HAVE_AESGCM) |
| 303 | + ret = do_example(); |
| 304 | +#else |
| 305 | + printf("Example requires ECC, HMAC, HKDF and AES GCM\n"); |
| 306 | + ret = -1; |
| 307 | +#endif |
| 308 | + return ret; |
| 309 | +} |
0 commit comments