diff --git a/configure.ac b/configure.ac index 57f16b732..55169de1b 100644 --- a/configure.ac +++ b/configure.ac @@ -595,10 +595,35 @@ AC_ARG_ENABLE([fpecc], if test "$ENABLED_FPECC" = "yes" then + if test "$ENABLED_ECC" = "no" + then + AC_MSG_ERROR([cannot enable fpecc without enabling ecc.]) + fi AM_CFLAGS="$AM_CFLAGS -DFP_ECC" fi +# ECC encrypt +AC_ARG_ENABLE([eccencrypt], + [ --enable-eccencrypt Enable ECC encrypt (default: disabled)], + [ ENABLED_ECC_ENCRYPT=$enableval ], + [ ENABLED_ECC_ENCRYPT=no ] + ) + +if test "$ENABLED_ECC_ENCRYPT" = "yes" +then + if test "$ENABLED_ECC" = "no" + then + AC_MSG_ERROR([cannot enable eccencrypt without enabling ecc.]) + fi + if test "$ENABLED_HKDF" = "no" + then + AC_MSG_ERROR([cannot enable eccencrypt without enabling hkdf.]) + fi + AM_CFLAGS="$AM_CFLAGS -DHAVE_ECC_ENCRYPT" +fi + + # PSK AC_ARG_ENABLE([psk], [ --enable-psk Enable PSK (default: disabled)], @@ -1522,6 +1547,7 @@ echo " * certgen: $ENABLED_CERTGEN" echo " * HC-128: $ENABLED_HC128" echo " * RABBIT: $ENABLED_RABBIT" echo " * PWDBASED: $ENABLED_PWDBASED" +echo " * HKDF: $ENABLED_HKDF" echo " * MD4: $ENABLED_MD4" echo " * PSK: $ENABLED_PSK" echo " * LEANPSK: $ENABLED_LEANPSK" @@ -1530,6 +1556,7 @@ echo " * DSA: $ENABLED_DSA" echo " * DH: $ENABLED_DH" echo " * ECC: $ENABLED_ECC" echo " * FPECC: $ENABLED_FPECC" +echo " * ECC_ENCRYPT: $ENABLED_ECC_ENCRYPT" echo " * ASN: $ENABLED_ASN" echo " * CODING: $ENABLED_CODING" echo " * MEMORY: $ENABLED_MEMORY" diff --git a/ctaocrypt/src/ecc.c b/ctaocrypt/src/ecc.c index e67679aef..0a87e00cb 100644 --- a/ctaocrypt/src/ecc.c +++ b/ctaocrypt/src/ecc.c @@ -33,6 +33,11 @@ #include #include +#ifdef HAVE_ECC_ENCRYPT + #include + #include +#endif + /* map @@ -3484,6 +3489,269 @@ void ecc_fp_free(void) } - #endif /* FP_ECC */ + +#ifdef HAVE_ECC_ENCRYPT + +/* init and set defaults, just holders */ +void ecc_encrypt_init_options(ecEncOptions* options) +{ + if (options) { + XMEMSET(options, 0, sizeof(ecEncOptions)); + + options->encAlgo = ecAES_128_CBC; + options->kdfAlgo = ecHKDF_SHA256; + options->macAlgo = ecHMAC_SHA256; + } +} + + +/* free any resources, clear any keys */ +void ecc_encrypt_free_options(ecEncOptions* options) +{ + if (options) { + XMEMSET(options, 0, sizeof(ecEncOptions)); + } +} + + +static int ecc_get_key_sizes(ecEncOptions* options, int* encKeySz, int* ivSz, + int* keysLen, word32* digestSz, word32* blockSz) +{ + if (options) { + switch (options->encAlgo) { + case ecAES_128_CBC: + *encKeySz = KEY_SIZE_128; + *ivSz = IV_SIZE_64; + *blockSz = AES_BLOCK_SIZE; + break; + default: + return BAD_FUNC_ARG; + } + + switch (options->macAlgo) { + case ecHMAC_SHA256: + *digestSz = SHA256_DIGEST_SIZE; + break; + default: + return BAD_FUNC_ARG; + } + } else + return BAD_FUNC_ARG; + + *keysLen = *encKeySz + *ivSz + *digestSz; + + return 0; +} + + +/* ecc encrypt with shared secret run through kdf + options holds non default algos and inputs + msgSz should be the right size for encAlgo, i.e., already padded + return 0 on success */ +int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, + word32 msgSz, byte* out, word32* outSz, ecEncOptions* opts) +{ + int ret; + word32 blockSz; + word32 digestSz; + ecEncOptions options; + byte sharedSecret[ECC_MAXSIZE]; /* 521 max size */ + byte keys[ECC_BUFSIZE]; /* max size */ + word32 sharedSz = sizeof(sharedSecret); + int keysLen; + int encKeySz; + int ivSz; + byte* encKey; + byte* encIv; + byte* macKey; + + if (privKey == NULL || pubKey == NULL || msg == NULL || out == NULL || + outSz == NULL) + return BAD_FUNC_ARG; + + if (opts) + options = *opts; + else { + ecc_encrypt_init_options(&options); /* defaults */ + } + + ret = ecc_get_key_sizes(&options, &encKeySz, &ivSz, &keysLen, &digestSz, + &blockSz); + if (ret != 0) + return ret; + + if ( (msgSz%blockSz) != 0) + return BAD_FUNC_ARG; + + if (*outSz < (msgSz + digestSz)) + return BUFFER_E; + + ret = ecc_shared_secret(privKey, pubKey, sharedSecret, &sharedSz); + if (ret != 0) + return ret; + + switch (options.kdfAlgo) { + case ecHKDF_SHA256 : + ret = HKDF(SHA256, sharedSecret, sharedSz, options.kdfSalt, + options.kdfSaltSz, options.kdfInfo, + options.kdfInfoSz, keys, keysLen); + if (ret != 0) + return ret; + break; + + default: + return BAD_FUNC_ARG; + } + + encKey = keys; + encIv = encKey + encKeySz; + macKey = encKey + encKeySz + ivSz; + + switch (options.encAlgo) { + case ecAES_128_CBC: + { + Aes aes; + ret = AesSetKey(&aes, encKey,KEY_SIZE_128,encIv,AES_ENCRYPTION); + if (ret != 0) + return ret; + ret = AesCbcEncrypt(&aes, out, msg, msgSz); + if (ret != 0) + return ret; + } + break; + + default: + return BAD_FUNC_ARG; + } + + switch (options.macAlgo) { + case ecHMAC_SHA256: + { + Hmac hmac; + ret = HmacSetKey(&hmac, SHA256, macKey, SHA256_DIGEST_SIZE); + if (ret != 0) + return ret; + HmacUpdate(&hmac, out, msgSz); + HmacUpdate(&hmac, options.macSalt, options.macSaltSz); + HmacFinal(&hmac, out+msgSz); + } + break; + + default: + return BAD_FUNC_ARG; + } + + *outSz = msgSz + digestSz; + + return 0; +} + + +int ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, + word32 msgSz, byte* out, word32* outSz, ecEncOptions* opts) +{ + int ret; + word32 blockSz; + word32 digestSz; + ecEncOptions options; + byte sharedSecret[ECC_MAXSIZE]; /* 521 max size */ + byte keys[ECC_BUFSIZE]; /* max size */ + word32 sharedSz = sizeof(sharedSecret); + int keysLen; + int encKeySz; + int ivSz; + byte* encKey; + byte* encIv; + byte* macKey; + + if (privKey == NULL || pubKey == NULL || msg == NULL || out == NULL || + outSz == NULL) + return BAD_FUNC_ARG; + + if (opts) + options = *opts; + else { + ecc_encrypt_init_options(&options); /* defaults */ + } + + ret = ecc_get_key_sizes(&options, &encKeySz, &ivSz, &keysLen, &digestSz, + &blockSz); + if (ret != 0) + return ret; + + if ( ((msgSz-digestSz) % blockSz) != 0) + return BAD_FUNC_ARG; + + if (*outSz < (msgSz - digestSz)) + return BUFFER_E; + + ret = ecc_shared_secret(privKey, pubKey, sharedSecret, &sharedSz); + if (ret != 0) + return ret; + + switch (options.kdfAlgo) { + case ecHKDF_SHA256 : + ret = HKDF(SHA256, sharedSecret, sharedSz, options.kdfSalt, + options.kdfSaltSz, options.kdfInfo, + options.kdfInfoSz, keys, keysLen); + if (ret != 0) + return ret; + break; + + default: + return BAD_FUNC_ARG; + } + + encKey = keys; + encIv = encKey + encKeySz; + macKey = encKey + encKeySz + ivSz; + + switch (options.macAlgo) { + case ecHMAC_SHA256: + { + byte verify[SHA256_DIGEST_SIZE]; + Hmac hmac; + ret = HmacSetKey(&hmac, SHA256, macKey, SHA256_DIGEST_SIZE); + if (ret != 0) + return ret; + HmacUpdate(&hmac, msg, msgSz-digestSz); + HmacUpdate(&hmac, options.macSalt, options.macSaltSz); + HmacFinal(&hmac, verify); + + if (memcmp(verify, msg + msgSz - digestSz, digestSz) != 0) { + return -1; + } + } + break; + + default: + return BAD_FUNC_ARG; + } + + switch (options.encAlgo) { + case ecAES_128_CBC: + { + Aes aes; + ret = AesSetKey(&aes, encKey,KEY_SIZE_128,encIv,AES_DECRYPTION); + if (ret != 0) + return ret; + ret = AesCbcDecrypt(&aes, out, msg, msgSz-digestSz); + if (ret != 0) + return ret; + } + break; + + default: + return BAD_FUNC_ARG; + } + + *outSz = msgSz - digestSz; + + return 0; +} + + +#endif /* HAVE_ECC_ENCRYPT */ + #endif /* HAVE_ECC */ diff --git a/ctaocrypt/test/test.c b/ctaocrypt/test/test.c index b9002bd82..02d887da3 100644 --- a/ctaocrypt/test/test.c +++ b/ctaocrypt/test/test.c @@ -158,6 +158,9 @@ int pkcs12_test(void); int pbkdf2_test(void); #ifdef HAVE_ECC int ecc_test(void); + #ifdef HAVE_ECC_ENCRYPT + int ecc_encrypt_test(void); + #endif #endif #ifdef HAVE_BLAKE2 int blake2b_test(void); @@ -434,6 +437,12 @@ void ctaocrypt_test(void* args) err_sys("ECC test failed!\n", ret); else printf( "ECC test passed!\n"); + #ifdef HAVE_ECC_ENCRYPT + if ( (ret = ecc_encrypt_test()) != 0) + err_sys("ECC Enc test failed!\n", ret); + else + printf( "ECC Enc test passed!\n"); + #endif #endif #ifdef HAVE_LIBZ @@ -3583,6 +3592,56 @@ int ecc_test(void) return 0; } +#ifdef HAVE_ECC_ENCRYPT + +int ecc_encrypt_test(void) +{ + RNG rng; + int ret; + ecc_key userA, userB; + byte msg[48]; + byte plain[48]; + byte out[80]; + word32 outSz = sizeof(out); + word32 plainSz = sizeof(plain); + int i; + + ret = InitRng(&rng); + if (ret != 0) + return -3001; + + ecc_init(&userA); + ecc_init(&userB); + + ret = ecc_make_key(&rng, 32, &userA); + ret += ecc_make_key(&rng, 32, &userB); + + if (ret != 0) + return -3002; + + for (i = 0; i < 48; i++) + msg[i] = i; + + /* send encrypted msg to B */ + ret = ecc_encrypt(&userA, &userB, msg, sizeof(msg), out, &outSz, NULL); + if (ret != 0) + return -3003; + + /* decrypted msg to B */ + ret = ecc_decrypt(&userB, &userA, out, outSz, plain, &plainSz, NULL); + if (ret != 0) + return -3004; + + if (memcmp(plain, msg, sizeof(msg)) != 0) + return -3005; + + ecc_free(&userB); + ecc_free(&userA); + + return 0; +} + +#endif /* HAVE_ECC_ENCRYPT */ #endif /* HAVE_ECC */ #ifdef HAVE_LIBZ diff --git a/cyassl/ctaocrypt/ecc.h b/cyassl/ctaocrypt/ecc.h index e88c10ab4..683429fec 100644 --- a/cyassl/ctaocrypt/ecc.h +++ b/cyassl/ctaocrypt/ecc.h @@ -119,6 +119,53 @@ CYASSL_API int ecc_sig_size(ecc_key* key); +/* ecc encrypt */ + +enum ecEncAlgo { + ecAES_128_CBC = 1, /* default */ + ecAES_256_CBC = 2 +}; + +enum ecKdfAlgo { + ecHKDF_SHA256 = 1, /* default */ + ecHKDF_SHA1 = 2 +}; + +enum ecMacAlgo { + ecHMAC_SHA256 = 1, /* default */ + ecHMAC_SHA1 = 2 +}; + +enum { + KEY_SIZE_128 = 16, + KEY_SIZE_256 = 32, + IV_SIZE_64 = 8 +}; + +typedef struct ecEncOptions { + byte encAlgo; /* which encryption type */ + byte kdfAlgo; /* which key derivation function type */ + byte macAlgo; /* which mac function type */ + byte* kdfSalt; /* optional salt for kdf */ + byte* kdfInfo; /* optional info for kdf */ + byte* macSalt; /* optional salt for mac */ + word32 kdfSaltSz; /* size of kdfSalt */ + word32 kdfInfoSz; /* size of kdfInfo */ + word32 macSaltSz; /* size of macSalt */ +} ecEncOptions; + +CYASSL_API +void ecc_encrypt_init_options(ecEncOptions*); /* init and set to defaults */ +CYASSL_API +void ecc_encrypt_free_options(ecEncOptions*); /* release/clear options */ + +CYASSL_API +int ecc_encrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, + word32 msgSz, byte* out, word32* outSz, ecEncOptions* options); +CYASSL_API +int ecc_decrypt(ecc_key* privKey, ecc_key* pubKey, const byte* msg, + word32 msgSz, byte* out, word32* outSz, ecEncOptions* options); + #ifdef __cplusplus } /* extern "C" */ #endif