diff --git a/configure.ac b/configure.ac index 79607d2bf..7f93045bf 100644 --- a/configure.ac +++ b/configure.ac @@ -289,6 +289,19 @@ then fi +# Public Key Callbacks +AC_ARG_ENABLE([pkcallbacks], + [ --enable-pkcallbacks Enable Public Key Callbacks (default: disabled)], + [ ENABLED_PKCALLBACKS=$enableval ], + [ ENABLED_PKCALLBACKS=no ] + ) + +if test "$ENABLED_PKCALLBACKS" = "yes" +then + AM_CFLAGS="$AM_CFLAGS -DHAVE_PK_CALLBACKS" +fi + + # SNIFFER AC_ARG_ENABLE([sniffer], [AS_HELP_STRING([--enable-sniffer],[ Enable CyaSSL sniffer support (default: disabled) ])],[ @@ -1498,6 +1511,7 @@ echo " * CRL-MONITOR: $ENABLED_CRL_MONITOR" echo " * Persistent session cache: $ENABLED_SAVESESSION" echo " * Persistent cert cache: $ENABLED_SAVECERT" echo " * Atomic User Record Layer: $ENABLED_ATOMICUSER" +echo " * Public Key Callbacks: $ENABLED_PKCALLBACKS" echo " * NTRU: $ENABLED_NTRU" echo " * SNI: $ENABLED_SNI" echo " * Maximum Fragment Length: $ENABLED_MAX_FRAGMENT" diff --git a/ctaocrypt/src/ecc.c b/ctaocrypt/src/ecc.c index 36225eb92..2698b0555 100644 --- a/ctaocrypt/src/ecc.c +++ b/ctaocrypt/src/ecc.c @@ -1245,8 +1245,8 @@ void ecc_free(ecc_key* key) key The corresponding public ECC key return MP_OKAY if successful (even if the signature is not valid) */ -int ecc_verify_hash(const byte* sig, word32 siglen, byte* hash, word32 hashlen, - int* stat, ecc_key* key) +int ecc_verify_hash(const byte* sig, word32 siglen, const byte* hash, + word32 hashlen, int* stat, ecc_key* key) { ecc_point *mG, *mQ; mp_int r; @@ -1324,7 +1324,7 @@ int ecc_verify_hash(const byte* sig, word32 siglen, byte* hash, word32 hashlen, /* truncate down to byte size, may be all that's needed */ if ( (CYASSL_BIT_SIZE * hashlen) > orderBits) hashlen = (orderBits + CYASSL_BIT_SIZE - 1)/CYASSL_BIT_SIZE; - err = mp_read_unsigned_bin(&e, (byte*)hash, hashlen); + err = mp_read_unsigned_bin(&e, hash, hashlen); /* may still need bit truncation too */ if (err == MP_OKAY && (CYASSL_BIT_SIZE * hashlen) > orderBits) diff --git a/cyassl/ctaocrypt/asn.h b/cyassl/ctaocrypt/asn.h index a1ec134b4..eadf8c1f8 100644 --- a/cyassl/ctaocrypt/asn.h +++ b/cyassl/ctaocrypt/asn.h @@ -356,7 +356,7 @@ CYASSL_LOCAL int ValidateDate(const byte* date, byte format, int dateType); CYASSL_LOCAL int DecodeECC_DSA_Sig(const byte* sig, word32 sigLen, mp_int* r, mp_int* s); /* private key helpers */ - CYASSL_LOCAL int EccPrivateKeyDecode(const byte* input,word32* inOutIdx, + CYASSL_API int EccPrivateKeyDecode(const byte* input,word32* inOutIdx, ecc_key*,word32); #endif diff --git a/cyassl/ctaocrypt/ecc.h b/cyassl/ctaocrypt/ecc.h index 7b5e8d5ed..2eccef6fb 100644 --- a/cyassl/ctaocrypt/ecc.h +++ b/cyassl/ctaocrypt/ecc.h @@ -91,8 +91,8 @@ CYASSL_API int ecc_sign_hash(const byte* in, word32 inlen, byte* out, word32 *outlen, RNG* rng, ecc_key* key); CYASSL_API -int ecc_verify_hash(const byte* sig, word32 siglen, byte* hash, word32 hashlen, - int* stat, ecc_key* key); +int ecc_verify_hash(const byte* sig, word32 siglen, const byte* hash, + word32 hashlen, int* stat, ecc_key* key); CYASSL_API void ecc_init(ecc_key* key); CYASSL_API diff --git a/cyassl/internal.h b/cyassl/internal.h index f93d0bfab..62516a1d6 100644 --- a/cyassl/internal.h +++ b/cyassl/internal.h @@ -1264,6 +1264,12 @@ struct CYASSL_CTX { CallbackMacEncrypt MacEncryptCb; /* Atomic User Mac/Encrypt Cb */ CallbackDecryptVerify DecryptVerifyCb; /* Atomic User Decrypt/Verify Cb */ #endif +#ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + CallbackEccSign EccSignCb; /* User EccSign Callback handler */ + CallbackEccVerify EccVerifyCb; /* User EccVerify Callback handler */ + #endif /* HAVE_ECC */ +#endif /* HAVE_PK_CALLBACKS */ }; @@ -1556,6 +1562,11 @@ typedef struct Buffers { #ifdef CYASSL_DTLS CYASSL_DTLS_CTX dtlsCtx; /* DTLS connection context */ #endif +#ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + buffer peerEccDsaKey; /* we own for Ecc Verify Callbacks */ + #endif /* HAVE_ECC */ +#endif /* HAVE_PK_CALLBACKS */ } Buffers; typedef struct Options { @@ -1831,6 +1842,12 @@ struct CYASSL { void* MacEncryptCtx; /* Atomic User Mac/Encrypt Callback Context */ void* DecryptVerifyCtx; /* Atomic User Decrypt/Verify Callback Context */ #endif +#ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + void* EccSignCtx; /* Ecc Sign Callback Context */ + void* EccVerifyCtx; /* Ecc Verify Callback Context */ + #endif /* HAVE_ECC */ +#endif /* HAVE_PK_CALLBACKS */ }; diff --git a/cyassl/ssl.h b/cyassl/ssl.h index 11ba84c5b..a5315e34c 100644 --- a/cyassl/ssl.h +++ b/cyassl/ssl.h @@ -1004,6 +1004,27 @@ enum BulkCipherAlgorithm { cyassl_rabbit }; + +/* Public Key Callback support */ +typedef int (*CallbackEccSign)(CYASSL* ssl, + const unsigned char* in, unsigned int inSz, + unsigned char* out, unsigned int* outSz, + const unsigned char* keyDer, unsigned int keySz, + void* ctx); +CYASSL_API void CyaSSL_CTX_SetEccSignCb(CYASSL_CTX*, CallbackEccSign); +CYASSL_API void CyaSSL_SetEccSignCtx(CYASSL* ssl, void *ctx); +CYASSL_API void* CyaSSL_GetEccSignCtx(CYASSL* ssl); + +typedef int (*CallbackEccVerify)(CYASSL* ssl, + const unsigned char* sig, unsigned int sigSz, + const unsigned char* hash, unsigned int hashSz, + const unsigned char* keyDer, unsigned int keySz, + int* result, void* ctx); +CYASSL_API void CyaSSL_CTX_SetEccVerifyCb(CYASSL_CTX*, CallbackEccVerify); +CYASSL_API void CyaSSL_SetEccVerifyCtx(CYASSL* ssl, void *ctx); +CYASSL_API void* CyaSSL_GetEccVerifyCtx(CYASSL* ssl); + + #ifndef NO_CERTS CYASSL_API void CyaSSL_CTX_SetCACb(CYASSL_CTX*, CallbackCACache); diff --git a/cyassl/test.h b/cyassl/test.h index a37f05d8b..bfd667951 100644 --- a/cyassl/test.h +++ b/cyassl/test.h @@ -15,6 +15,13 @@ #include #include #endif +#ifdef HAVE_PK_CALLBACKS + #include + #include + #ifdef HAVE_ECC + #include + #endif /* HAVE_ECC */ +#endif /*HAVE_PK_CALLBACKS */ #ifdef USE_WINDOWS_API #include @@ -1493,6 +1500,66 @@ static INLINE void FreeAtomicUser(CYASSL* ssl) #endif /* ATOMIC_USER */ +#ifdef HAVE_PK_CALLBACKS + +static INLINE int myEccSign(CYASSL* ssl, const byte* in, word32 inSz, + byte* out, word32* outSz, const byte* key, word32 keySz, void* ctx) +{ + RNG rng; + int ret; + word32 idx = 0; + ecc_key myKey; + + (void)ssl; + (void)ctx; + + InitRng(&rng); + ecc_init(&myKey); + + ret = EccPrivateKeyDecode(key, &idx, &myKey, keySz); + if (ret == 0) + ret = ecc_sign_hash(in, inSz, out, outSz, &rng, &myKey); + ecc_free(&myKey); + + return ret; +} + + +static INLINE int myEccVerify(CYASSL* ssl, const byte* sig, word32 sigSz, + const byte* hash, word32 hashSz, const byte* key, word32 keySz, + int* result, void* ctx) +{ + int ret; + ecc_key myKey; + + (void)ssl; + (void)ctx; + + ecc_init(&myKey); + + ret = ecc_import_x963(key, keySz, &myKey); + if (ret == 0) + ret = ecc_verify_hash(sig, sigSz, hash, hashSz, result, &myKey); + ecc_free(&myKey); + + return ret; +} + + +static INLINE void SetupPkCallbacks(CYASSL_CTX* ctx, CYASSL* ssl) +{ + (void)ctx; + (void)ssl; + + #ifdef HAVE_ECC + CyaSSL_CTX_SetEccSignCb(ctx, myEccSign); + CyaSSL_CTX_SetEccVerifyCb(ctx, myEccVerify); + #endif /* HAVE_ECC */ +} + +#endif /* HAVE_PK_CALLBACKS */ + + #if defined(__hpux__) || defined(__MINGW32__) /* HP/UX doesn't have strsep, needed by test/suites.c */ diff --git a/examples/client/client.c b/examples/client/client.c index 3d0065926..6c82d627c 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -146,6 +146,9 @@ static void Usage(void) #ifdef ATOMIC_USER printf("-U Atomic User Record Layer Callbacks\n"); #endif +#ifdef HAVE_PK_CALLBACKS + printf("-P Public Key Callbacks\n"); +#endif } @@ -190,6 +193,7 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) int useClientCert = 1; int fewerPackets = 0; int atomicUser = 0; + int pkCallbacks = 0; char* cipherList = NULL; char* verifyCert = (char*)caCert; char* ourCert = (char*)cliCert; @@ -226,11 +230,12 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) (void)sslResume; (void)trackMemory; (void)atomicUser; + (void)pkCallbacks; StackTrap(); while ((ch = mygetopt(argc, argv, - "?gdusmNrtfxUh:p:v:l:A:c:k:b:zS:L:ToO:")) != -1) { + "?gdusmNrtfxUPh:p:v:l:A:c:k:b:zS:L:ToO:")) != -1) { switch (ch) { case '?' : Usage(); @@ -276,6 +281,12 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) #endif break; + case 'P' : + #ifdef HAVE_PK_CALLBACKS + pkCallbacks = 1; + #endif + break; + case 'h' : host = myoptarg; domain = myoptarg; @@ -608,6 +619,10 @@ THREAD_RETURN CYASSL_THREAD client_test(void* args) #ifdef ATOMIC_USER if (atomicUser) SetupAtomicUser(ctx, ssl); +#endif +#ifdef HAVE_PK_CALLBACKS + if (pkCallbacks) + SetupPkCallbacks(ctx, ssl); #endif if (matchName && doPeerCheck) CyaSSL_check_domain_name(ssl, domain); diff --git a/src/internal.c b/src/internal.c index 85e7abec4..dec68e7c9 100644 --- a/src/internal.c +++ b/src/internal.c @@ -432,6 +432,12 @@ int InitSSL_Ctx(CYASSL_CTX* ctx, CYASSL_METHOD* method) ctx->MacEncryptCb = NULL; ctx->DecryptVerifyCb = NULL; #endif +#ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + ctx->EccSignCb = NULL; + ctx->EccVerifyCb = NULL; + #endif /* HAVE_ECC */ +#endif /* HAVE_PK_CALLBACKS */ if (InitMutex(&ctx->countMutex) < 0) { CYASSL_MSG("Mutex error on CTX init"); @@ -1271,6 +1277,12 @@ int InitSSL(CYASSL* ssl, CYASSL_CTX* ctx) ssl->buffers.clearOutputBuffer.length = 0; ssl->buffers.prevSent = 0; ssl->buffers.plainSz = 0; +#ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + ssl->buffers.peerEccDsaKey.buffer = 0; + ssl->buffers.peerEccDsaKey.length = 0; + #endif /* HAVE_ECC */ +#endif /* HAVE_PK_CALLBACKS */ #ifdef KEEP_PEER_CERT InitX509(&ssl->peerCert, 0); @@ -1484,6 +1496,13 @@ int InitSSL(CYASSL* ssl, CYASSL_CTX* ctx) ssl->MacEncryptCtx = NULL; ssl->DecryptVerifyCtx = NULL; #endif +#ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + ssl->EccSignCtx = NULL; + ssl->EccVerifyCtx = NULL; + #endif /* HAVE_ECC */ +#endif /* HAVE_PK_CALLBACKS */ + /* all done with init, now can return errors, call other stuff */ /* increment CTX reference count */ @@ -1692,6 +1711,11 @@ void SSL_ResourceFree(CYASSL* ssl) XFREE(ssl->eccDsaKey, ssl->heap, DYNAMIC_TYPE_ECC); } #endif +#ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + XFREE(ssl->buffers.peerEccDsaKey.buffer, ssl->heap, DYNAMIC_TYPE_ECC); + #endif /* HAVE_ECC */ +#endif /* HAVE_PK_CALLBACKS */ #ifdef HAVE_TLS_EXTENSIONS TLSX_FreeAll(ssl->extensions); #endif @@ -1779,6 +1803,12 @@ void FreeHandshakeResources(CYASSL* ssl) ssl->eccDsaKey = NULL; } #endif +#ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + XFREE(ssl->buffers.peerEccDsaKey.buffer, ssl->heap, DYNAMIC_TYPE_ECC); + ssl->buffers.peerEccDsaKey.buffer = NULL; + #endif /* HAVE_ECC */ +#endif /* HAVE_PK_CALLBACKS */ } @@ -3234,8 +3264,24 @@ static int DoCertificate(CYASSL* ssl, byte* input, word32* inOutIdx) ssl->peerEccDsaKey) != 0) { ret = PEER_KEY_ERROR; } - else + else { ssl->peerEccDsaKeyPresent = 1; + #ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + ssl->buffers.peerEccDsaKey.buffer = + XMALLOC(dCert.pubKeySize, + ssl->heap, DYNAMIC_TYPE_ECC); + if (ssl->buffers.peerEccDsaKey.buffer == NULL) + ret = MEMORY_ERROR; + else { + XMEMCPY(ssl->buffers.peerEccDsaKey.buffer, + dCert.publicKey, dCert.pubKeySize); + ssl->buffers.peerEccDsaKey.length = + dCert.pubKeySize; + } + #endif /* HAVE_ECC */ + #endif /*HAVE_PK_CALLBACKS */ + } } break; #endif /* HAVE_ECC */ @@ -7471,6 +7517,15 @@ static void PickHashSigAlgo(CYASSL* ssl, byte* digest = hash256; word32 digestSz = SHA256_DIGEST_SIZE; #endif + byte doUserEcc = 0; + + #ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + if (ssl->ctx->EccVerifyCb) + doUserEcc = 1; + #endif /* HAVE_ECC */ + #endif /*HAVE_PK_CALLBACKS */ + if (!ssl->peerEccDsaKeyPresent) return NO_PEER_KEY; @@ -7494,9 +7549,21 @@ static void PickHashSigAlgo(CYASSL* ssl, #endif } } - - ret = ecc_verify_hash(signature, sigLen, digest, digestSz, - &verify, ssl->peerEccDsaKey); + if (doUserEcc) { + #ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + ret = ssl->ctx->EccVerifyCb(ssl, signature, sigLen, + digest, digestSz, + ssl->buffers.peerEccDsaKey.buffer, + ssl->buffers.peerEccDsaKey.length, + &verify, ssl->EccVerifyCtx); + #endif /* HAVE_ECC */ + #endif /*HAVE_PK_CALLBACKS */ + } + else { + ret = ecc_verify_hash(signature, sigLen, digest, digestSz, + &verify, ssl->peerEccDsaKey); + } if (ret != 0 || verify == 0) return VERIFY_SIGN_ERROR; } @@ -7861,6 +7928,14 @@ static void PickHashSigAlgo(CYASSL* ssl, digestSz = SHA256_DIGEST_SIZE; digest = ssl->certHashes.sha256; #endif + byte doUserEcc = 0; + + #ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + if (ssl->ctx->EccSignCb) + doUserEcc = 1; + #endif /* HAVE_ECC */ + #endif /*HAVE_PK_CALLBACKS */ if (IsAtLeastTLSv1_2(ssl)) { if (ssl->suites->hashAlgo == sha_mac) { @@ -7883,8 +7958,21 @@ static void PickHashSigAlgo(CYASSL* ssl, } } - ret = ecc_sign_hash(digest, digestSz, encodedSig, - &localSz, ssl->rng, &eccKey); + if (doUserEcc) { + #ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + ret = ssl->ctx->EccSignCb(ssl, digest, digestSz, + encodedSig, &localSz, + ssl->buffers.key.buffer, + ssl->buffers.key.length, + ssl->EccSignCtx); + #endif /* HAVE_ECC */ + #endif /*HAVE_PK_CALLBACKS */ + } + else { + ret = ecc_sign_hash(digest, digestSz, encodedSig, + &localSz, ssl->rng, &eccKey); + } if (ret == 0) { length = localSz; c16toa((word16)length, verify + extraSz); /* prepend hdr */ @@ -8395,6 +8483,14 @@ static void PickHashSigAlgo(CYASSL* ssl, word32 digestSz = SHA256_DIGEST_SIZE; #endif word32 sz = sigSz; + byte doUserEcc = 0; + + #ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + if (ssl->ctx->EccSignCb) + doUserEcc = 1; + #endif /* HAVE_ECC */ + #endif /*HAVE_PK_CALLBACKS */ if (IsAtLeastTLSv1_2(ssl)) { if (ssl->suites->hashAlgo == sha_mac) { @@ -8417,8 +8513,21 @@ static void PickHashSigAlgo(CYASSL* ssl, } } - ret = ecc_sign_hash(digest, digestSz, + if (doUserEcc) { + #ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + ret = ssl->ctx->EccSignCb(ssl, digest, digestSz, + output + LENGTH_SZ + idx, &sz, + ssl->buffers.key.buffer, + ssl->buffers.key.length, + ssl->EccSignCtx); + #endif /* HAVE_ECC */ + #endif /*HAVE_PK_CALLBACKS */ + } + else { + ret = ecc_sign_hash(digest, digestSz, output + LENGTH_SZ + idx, &sz, ssl->rng, &dsaKey); + } #ifndef NO_RSA FreeRsaKey(&rsaKey); #endif @@ -9709,6 +9818,14 @@ static void PickHashSigAlgo(CYASSL* ssl, int err = -1; byte* digest = ssl->certHashes.sha; word32 digestSz = SHA_DIGEST_SIZE; + byte doUserEcc = 0; + + #ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + if (ssl->ctx->EccVerifyCb) + doUserEcc = 1; + #endif /* HAVE_ECC */ + #endif /*HAVE_PK_CALLBACKS */ CYASSL_MSG("Doing ECC peer cert verify"); @@ -9729,9 +9846,20 @@ static void PickHashSigAlgo(CYASSL* ssl, #endif } } - err = ecc_verify_hash(sig, sz, digest, digestSz, - &verify, ssl->peerEccDsaKey); - + if (doUserEcc) { + #ifdef HAVE_PK_CALLBACKS + #ifdef HAVE_ECC + ret = ssl->ctx->EccVerifyCb(ssl, sig, sz, digest, digestSz, + ssl->buffers.peerEccDsaKey.buffer, + ssl->buffers.peerEccDsaKey.length, + &verify, ssl->EccVerifyCtx); + #endif /* HAVE_ECC */ + #endif /*HAVE_PK_CALLBACKS */ + } + else { + err = ecc_verify_hash(sig, sz, digest, digestSz, + &verify, ssl->peerEccDsaKey); + } if (err == 0 && verify == 1) ret = 0; /* verified */ } diff --git a/src/ssl.c b/src/ssl.c index f3165da1d..2560df74c 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -10385,3 +10385,58 @@ int CyaSSL_CTX_OCSP_set_override_url(CYASSL_CTX* ctx, const char* url) } +#ifndef NO_CERTS +#ifdef HAVE_PK_CALLBACKS + +#ifdef HAVE_ECC + +void CyaSSL_CTX_SetEccSignCb(CYASSL_CTX* ctx, CallbackEccSign cb) +{ + if (ctx) + ctx->EccSignCb = cb; +} + + +void CyaSSL_SetEccSignCtx(CYASSL* ssl, void *ctx) +{ + if (ssl) + ssl->EccSignCtx = ctx; +} + + +void* CyaSSL_GetEccSignCtx(CYASSL* ssl) +{ + if (ssl) + return ssl->EccSignCtx; + + return NULL; +} + + +void CyaSSL_CTX_SetEccVerifyCb(CYASSL_CTX* ctx, CallbackEccVerify cb) +{ + if (ctx) + ctx->EccVerifyCb = cb; +} + + +void CyaSSL_SetEccVerifyCtx(CYASSL* ssl, void *ctx) +{ + if (ssl) + ssl->EccVerifyCtx = ctx; +} + + +void* CyaSSL_GetEccVerifyCtx(CYASSL* ssl) +{ + if (ssl) + return ssl->EccVerifyCtx; + + return NULL; +} + +#endif /* HAVE_ECC */ + +#endif /* HAVE_PK_CALLBACKS */ +#endif /* NO_CERTS */ +