diff --git a/configure.ac b/configure.ac index 1bf5b8eb2..c7e14cc9d 100644 --- a/configure.ac +++ b/configure.ac @@ -2508,6 +2508,28 @@ AM_CONDITIONAL([BUILD_MCAPI], [test "x$ENABLED_MCAPI" = "xyes"]) # check if PSK was enabled for conditionally running psk.test script AM_CONDITIONAL([BUILD_PSK], [test "x$ENABLED_PSK" = "xyes"]) + +# check if should run the trusted peer certs test +# (for now checking both C_FLAGS and C_EXTRA_FLAGS) +case $C_EXTRA_FLAGS in + *WOLFSSL_TRUST_PEER_CERT*) + have_tp=yes + break;; + *) + have_tp=no ;; +esac +if test "$have_tp" = "no"; then + case $C_FLAGS in + *WOLFSSL_TRUST_PEER_CERT*) + have_tp=yes + break;; + *) + have_tp=no ;; + esac +fi +AM_CONDITIONAL([BUILD_TRUST_PEER_CERT], [test "x$have_tp" = "xyes"]) + + ################################################################################ # Check for build-type conflicts # ################################################################################ diff --git a/examples/client/client.c b/examples/client/client.c index 57f0a212d..08fdf9ad2 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -391,6 +391,9 @@ static void Usage(void) #ifdef HAVE_CRL printf("-C Disable CRL\n"); #endif +#ifdef WOLFSSL_TRUST_PEER_CERT + printf("-E Path to load trusted peer cert\n"); +#endif } THREAD_RETURN WOLFSSL_THREAD client_test(void* args) @@ -449,6 +452,10 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) const char* ourCert = cliCert; const char* ourKey = cliKey; +#ifdef WOLFSSL_TRUST_PEER_CERT + const char* trustCert = NULL; +#endif + #ifdef HAVE_SNI char* sniHostName = NULL; #endif @@ -501,7 +508,7 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) #ifndef WOLFSSL_VXWORKS while ((ch = mygetopt(argc, argv, - "?gdeDusmNrwRitfxXUPCVh:p:v:l:A:c:k:Z:b:zS:F:L:ToO:aB:W:")) != -1) { + "?gdeDusmNrwRitfxXUPCVh:p:v:l:A:c:k:Z:b:zS:F:L:ToO:aB:W:E:")) != -1) { switch (ch) { case '?' : Usage(); @@ -543,6 +550,12 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) #endif break; + #ifdef WOLFSSL_TRUST_PEER_CERT + case 'E' : + trustCert = myoptarg; + break; + #endif + case 'm' : matchName = 1; break; @@ -988,6 +1001,14 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) if (wolfSSL_CTX_load_verify_locations(ctx, eccCert, 0) != SSL_SUCCESS) err_sys("can't load ecc ca file, Please run from wolfSSL home dir"); #endif /* HAVE_ECC */ +#ifdef WOLFSSL_TRUST_PEER_CERT + if (trustCert) { + if ((ret = wolfSSL_CTX_trust_peer_cert(ctx, trustCert, + SSL_FILETYPE_PEM)) != SSL_SUCCESS) { + err_sys("can't load trusted peer cert file"); + } + } +#endif /* WOLFSSL_TRUST_PEER_CERT */ } #endif /* !NO_FILESYSTEM && !NO_CERTS */ #if !defined(NO_CERTS) diff --git a/examples/server/server.c b/examples/server/server.c index 0bea91863..d4937d280 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -238,6 +238,9 @@ static void Usage(void) printf("-n Use NTRU key (needed for NTRU suites)\n"); #endif printf("-B Benchmark throughput using bytes and print stats\n"); +#ifdef WOLFSSL_TRUST_PEER_CERT + printf("-E Path to load trusted peer cert\n"); +#endif } THREAD_RETURN CYASSL_THREAD server_test(void* args) @@ -288,6 +291,10 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) int argc = ((func_args*)args)->argc; char** argv = ((func_args*)args)->argv; +#ifdef WOLFSSL_TRUST_PEER_CERT + const char* trustCert = NULL; +#endif + #ifndef NO_PSK int sendPskIdentityHint = 1; #endif @@ -330,7 +337,7 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) #ifdef WOLFSSL_VXWORKS useAnyAddr = 1; #else - while ((ch = mygetopt(argc, argv, "?dbstnNufrawPIR:p:v:l:A:c:k:Z:S:oO:D:L:ieB:j")) + while ((ch = mygetopt(argc, argv, "?jdbstnNufrawPIR:p:v:l:A:c:k:Z:S:oO:D:L:ieB:E:")) != -1) { switch (ch) { case '?' : @@ -507,6 +514,12 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) } break; + #ifdef WOLFSSL_TRUST_PEER_CERT + case 'E' : + trustCert = myoptarg; + break; + #endif + default: Usage(); exit(MY_EX_USAGE); @@ -685,6 +698,14 @@ THREAD_RETURN CYASSL_THREAD server_test(void* args) SSL_VERIFY_FAIL_IF_NO_PEER_CERT),0); if (SSL_CTX_load_verify_locations(ctx, verifyCert, 0) != SSL_SUCCESS) err_sys("can't load ca file, Please run from wolfSSL home dir"); + #ifdef WOLFSSL_TRUST_PEER_CERT + if (trustCert) { + if ((ret = wolfSSL_CTX_trust_peer_cert(ctx, trustCert, + SSL_FILETYPE_PEM)) != SSL_SUCCESS) { + err_sys("can't load trusted peer cert file"); + } + } + #endif /* WOLFSSL_TRUST_PEER_CERT */ } #endif diff --git a/scripts/include.am b/scripts/include.am index 19e05feaf..53087fdc5 100644 --- a/scripts/include.am +++ b/scripts/include.am @@ -39,8 +39,16 @@ endif endif +if BUILD_PSK +dist_noinst_SCRIPTS+= scripts/psk.test endif +if BUILD_TRUST_PEER_CERT +dist_noinst_SCRIPTS+= scripts/trusted_peer.test +endif + +endif # end of BUILD_EXAMPLE_SERVERS + if BUILD_EXAMPLE_CLIENTS if !BUILD_IPV6 dist_noinst_SCRIPTS+= scripts/external.test @@ -49,10 +57,6 @@ dist_noinst_SCRIPTS+= scripts/google.test endif endif -if BUILD_PSK -dist_noinst_SCRIPTS+= scripts/psk.test -endif - EXTRA_DIST += scripts/testsuite.pcap # leave openssl.test as extra until non bash works EXTRA_DIST += scripts/openssl.test diff --git a/scripts/trusted_peer.test b/scripts/trusted_peer.test new file mode 100755 index 000000000..a4faf1c18 --- /dev/null +++ b/scripts/trusted_peer.test @@ -0,0 +1,286 @@ +#!/bin/sh + +# trusted_peer.test +# copyright wolfSSL 2016 + +# getting unique port is modeled after resume.test script +# need a unique port since may run the same time as testsuite +# use server port zero hack to get one +port=0 +no_pid=-1 +server_pid=$no_pid +counter=0 +# let's use absolute path to a local dir (make distcheck may be in sub dir) +# also let's add some randomness by adding pid in case multiple 'make check's +# per source tree +ready_file=`pwd`/wolfssl_tp_ready$$ + +# variables for certs so can use RSA or ECC +client_cert=`pwd`/certs/client-cert.pem +client_ca=`pwd`/certs/ca-cert.pem +client_key=`pwd`/certs/client-key.pem +ca_key=`pwd`/certs/ca-key.pem +server_cert=`pwd`/certs/server-cert.pem +server_key=`pwd`/certs/server-key.pem +combined_cert=`pwd`/certs/client_combined.pem +wrong_ca=`pwd`/certs/wolfssl-website-ca.pem +wrong_cert=`pwd`/certs/server-revoked-cert.pem + +echo "ready file $ready_file" + +create_port() { + while [ ! -s $ready_file -a "$counter" -lt 20 ]; do + echo -e "waiting for ready file..." + sleep 0.1 + counter=$((counter+ 1)) + done + + if test -e $ready_file; then + echo -e "found ready file, starting client..." + + # get created port 0 ephemeral port + port=`cat $ready_file` + else + echo -e "NO ready file ending test..." + do_cleanup + fi +} + +remove_ready_file() { + if test -e $ready_file; then + echo -e "removing existing ready file" + rm $ready_file + fi +} + +do_cleanup() { + echo "in cleanup" + + if [ $server_pid != $no_pid ] + then + echo "killing server" + kill -9 $server_pid + fi + remove_ready_file +} + +do_trap() { + echo "got trap" + do_cleanup + exit -1 +} + +trap do_trap INT TERM + +[ ! -x ./examples/client/client ] && echo -e "\n\nClient doesn't exist" && exit 1 + +# Look for if RSA and/or ECC is enabled and adjust certs/keys +ciphers=`./examples/client/client -e` +if [[ $ciphers != *"RSA"* ]]; then + if [[ $ciphers == *"ECDSA"* ]]; then + client_cert=`pwd`/certs/client-ecc-cert.pem + client_ca=`pwd`/certs/server-ecc.pem + client_key=`pwd`/certs/ecc-client-key.pem + ca_key=`pwd`/certs/ecc-key.pem + server_cert=`pwd`/certs/server-ecc.pem + server_key=`pwd`/certs/ecc-key.pem + wrong_ca=`pwd`/certs/server-ecc-comp.pem + wrong_cert=`pwd`/certs/server-ecc-comp.pem + else + echo "configure options not set up for test. No RSA or ECC" + exit 0 + fi +fi + +# CRL list not set up for tests +crl_test=`./examples/client/client -h` +if [[ $crl_test == *"-C "* ]]; then + echo "test not set up to run with CRL" + exit 0 +fi + +# Test for trusted peer certs build +echo "" +echo "Checking built with trusted peer certs " +echo "-----------------------------------------------------" +port=0 +remove_ready_file +./examples/server/server -E $client_cert -c $server_cert -k $server_key -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -A $client_ca -p $port +RESULT=$? +remove_ready_file +# if fail here then is a settings issue so return 0 +if [ $RESULT -ne 0 ]; then + echo -e "\n\nTrusted peer certs not enabled \"WOLFSSL_TRUST_PEER_CERT\"" + do_cleanup + exit 0 +fi +echo "" + +# Test that using no CA's and only trusted peer certs works +echo "Server and Client relying on trusted peer cert loaded" +echo "-----------------------------------------------------" +port=0 +./examples/server/server -A $wrong_ca -E $client_cert -c $server_cert -k $server_key -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -A $wrong_ca -E $server_cert -c $client_cert -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -ne 0 ]; then + echo -e "\nServer and Client trusted peer cert failed!" + do_cleanup + exit 1 +fi +echo "" + +# Test that using server trusted peer certs works +echo "Server relying on trusted peer cert loaded" +echo "-----------------------------------------------------" +port=0 +./examples/server/server -A $wrong_ca -E $client_cert -c $server_cert -k $server_key -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -A $client_ca -c $client_cert -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -ne 0 ]; then + echo -e "\nServer trusted peer cert test failed!" + do_cleanup + exit 1 +fi +echo "" + +# Test that using client trusted peer certs works +echo "Client relying on trusted peer cert loaded" +echo "-----------------------------------------------------" +port=0 +./examples/server/server -c $server_cert -k $server_key -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -A $wrong_ca -E $server_cert -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -ne 0 ]; then + echo -e "\nClient trusted peer cert test failed!" + do_cleanup + exit 1 +fi +echo "" + +# Test that client fall through to CA works +echo "Client fall through to loaded CAs" +echo "-----------------------------------------------------" +port=0 +./examples/server/server -c $server_cert -k $server_key -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -A $client_ca -E $wrong_cert -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -ne 0 ]; then + echo -e "\nClient trusted peer cert fall through to CA test failed!" + do_cleanup + exit 1 +fi +echo "" + +# Test that client can fail +# check if using ECC client example is hard coded to load correct ECC ca so skip +if [[ $wrong_ca != *"ecc"* ]]; then +echo "Client wrong CA and wrong trusted peer cert loaded" +echo "-----------------------------------------------------" +port=0 +./examples/server/server -c $server_cert -k $server_key -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -A $wrong_ca -E $wrong_cert -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -eq 0 ]; then + echo -e "\nClient trusted peer cert test failed!" + do_cleanup + exit 1 +fi +echo "" +fi + +# Test that server can fail +echo "Server wrong CA and wrong trusted peer cert loaded" +echo "-----------------------------------------------------" +port=0 +./examples/server/server -A $wrong_ca -E $wrong_cert -c $server_cert -k $server_key -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -A $client_ca -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -eq 0 ]; then + echo -e "\nServer trusted peer cert test failed!" + do_cleanup + exit 1 +fi +echo "" + +# Test that server fall through to CA works +echo "Server fall through to loaded CAs" +echo "-----------------------------------------------------" +port=0 +./examples/server/server -E $wrong_cert -c $server_cert -k $server_key -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -A $client_ca -p $port +RESULT=$? +remove_ready_file +if [ $RESULT -ne 0 ]; then + echo -e "\nServer trusted peer cert fall through to CA test failed!" + do_cleanup + exit 1 +fi +echo "" + +# test loading multiple certs +echo "Server loading multiple trusted peer certs" +echo "Test two success cases and one fail case" +echo "-----------------------------------------------------" +port=0 +cat $client_cert $client_ca > $combined_cert +./examples/server/server -i -A $wrong_ca -E $combined_cert -c $server_cert -k $server_key -R $ready_file -p $port & +server_pid=$! +create_port +./examples/client/client -A $client_ca -c $client_cert -k $client_key -p $port +RESULT=$? +if [ $RESULT -ne 0 ]; then + echo -e "\nServer load multiple trusted peer certs failed!" + do_cleanup + exit 1 +fi +./examples/client/client -A $client_ca -c $client_ca -k $ca_key -p $port +RESULT=$? +if [ $RESULT -ne 0 ]; then + echo -e "\nServer load multiple trusted peer certs failed!" + do_cleanup + exit 1 +fi +./examples/client/client -A $client_ca -c $wrong_cert -k $client_key -p $port +RESULT=$? +if [ $RESULT -eq 0 ]; then + echo -e "\nServer load multiple trusted peer certs failed!" + do_cleanup + exit 1 +fi + +do_cleanup # kill PID of server running in infinit loop +rm $combined_cert +remove_ready_file +echo "" + +echo "-----------------------------------------------------" +echo "ALL TESTS PASSED" +echo "-----------------------------------------------------" + +exit 0 + + diff --git a/src/internal.c b/src/internal.c index 8222a2d25..c1fa85438 100644 --- a/src/internal.c +++ b/src/internal.c @@ -4552,6 +4552,10 @@ static int DoCertificate(WOLFSSL* ssl, byte* input, word32* inOutIdx, WOLFSSL_X509_STORE_CTX store[1]; #endif +#ifdef WOLFSSL_TRUST_PEER_CERT + byte haveTrustPeer = 0; /* was cert verified by loaded trusted peer cert */ +#endif + #ifdef WOLFSSL_CALLBACKS if (ssl->hsInfoOn) AddPacketName("Certificate", &ssl->handShakeInfo); if (ssl->toInfoOn) AddLateName("Certificate", &ssl->timeoutInfo); @@ -4618,6 +4622,43 @@ static int DoCertificate(WOLFSSL* ssl, byte* input, word32* inOutIdx, return MEMORY_E; #endif +#ifdef WOLFSSL_TRUST_PEER_CERT + /* if using trusted peer certs check before verify chain and CA test */ + if (count > 0) { + TrustedPeerCert* tp = NULL; + + InitDecodedCert(dCert, certs[0].buffer, certs[0].length, ssl->heap); + ret = ParseCertRelative(dCert, CERT_TYPE, 0, ssl->ctx->cm); + #ifndef NO_SKID + if (dCert->extAuthKeyIdSet) { + tp = GetTrustedPeer(ssl->ctx->cm, dCert->extSubjKeyId, + WC_MATCH_SKID); + } + else { /* if the cert has no SKID try to match by name */ + tp = GetTrustedPeer(ssl->ctx->cm, dCert->subjectHash, + WC_MATCH_NAME); + } + #else /* NO_SKID */ + tp = GetTrustedPeer(ssl->ctx->cm, dCert->subjectHash, + WC_MATCH_NAME); + #endif /* NO SKID */ + WOLFSSL_MSG("Checking for trusted peer cert"); + + if (tp == NULL) { + /* no trusted peer cert */ + WOLFSSL_MSG("No matching trusted peer cert. Checking CAs"); + FreeDecodedCert(dCert); + } else if (MatchTrustedPeer(tp, dCert)){ + WOLFSSL_MSG("Found matching trusted peer cert"); + haveTrustPeer = 1; + } else { + WOLFSSL_MSG("Trusted peer cert did not match!"); + FreeDecodedCert(dCert); + } + } + if (!haveTrustPeer) { /* do not verify chain if trusted peer cert found */ +#endif /* WOLFSSL_TRUST_PEER_CERT */ + /* verify up to peer's first */ while (count > 1) { buffer myCert = certs[count - 1]; @@ -4707,6 +4748,10 @@ static int DoCertificate(WOLFSSL* ssl, byte* input, word32* inOutIdx, count--; } +#ifdef WOLFSSL_TRUST_PEER_CERT + } /* end of if (haveTrustPeer) -- a check for if already verified */ +#endif + /* peer's, may not have one if blank client cert sent by TLSv1.2 */ if (count) { buffer myCert = certs[0]; @@ -4714,9 +4759,16 @@ static int DoCertificate(WOLFSSL* ssl, byte* input, word32* inOutIdx, WOLFSSL_MSG("Verifying Peer's cert"); +#ifdef WOLFSSL_TRUST_PEER_CERT + if (!haveTrustPeer) { /* do not parse again if previously verified */ +#endif InitDecodedCert(dCert, myCert.buffer, myCert.length, ssl->heap); ret = ParseCertRelative(dCert, CERT_TYPE, !ssl->options.verifyNone, ssl->ctx->cm); +#ifdef WOLFSSL_TRUST_PEER_CERT + } +#endif + if (ret == 0) { WOLFSSL_MSG("Verified Peer's cert"); fatal = 0; diff --git a/src/ssl.c b/src/ssl.c index 70573a268..dd2d392f0 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -1704,6 +1704,14 @@ WOLFSSL_CERT_MANAGER* wolfSSL_CertManagerNew(void) wolfSSL_CertManagerFree(cm); return NULL; } + + #ifdef WOLFSSL_TRUST_PEER_CERT + if (InitMutex(&cm->tpLock) != 0) { + WOLFSSL_MSG("Bad mutex init"); + wolfSSL_CertManagerFree(cm); + return NULL; + } + #endif } return cm; @@ -1730,6 +1738,12 @@ void wolfSSL_CertManagerFree(WOLFSSL_CERT_MANAGER* cm) #endif FreeSignerTable(cm->caTable, CA_TABLE_SIZE, NULL); FreeMutex(&cm->caLock); + + #ifdef WOLFSSL_TRUST_PEER_CERT + FreeTrustedPeerTable(cm->tpTable, TP_TABLE_SIZE, NULL); + FreeMutex(&cm->tpLock); + #endif + XFREE(cm, NULL, DYNAMIC_TYPE_CERT_MANAGER); } @@ -1756,6 +1770,27 @@ int wolfSSL_CertManagerUnloadCAs(WOLFSSL_CERT_MANAGER* cm) } +#ifdef WOLFSSL_TRUST_PEER_CERT +int wolfSSL_CertManagerUnload_trust_peers(WOLFSSL_CERT_MANAGER* cm) +{ + WOLFSSL_ENTER("wolfSSL_CertManagerUnload_trust_peers"); + + if (cm == NULL) + return BAD_FUNC_ARG; + + if (LockMutex(&cm->tpLock) != 0) + return BAD_MUTEX_E; + + FreeTrustedPeerTable(cm->tpTable, TP_TABLE_SIZE, NULL); + + UnLockMutex(&cm->tpLock); + + + return SSL_SUCCESS; +} +#endif /* WOLFSSL_TRUST_PEER_CERT */ + + /* Return bytes written to buff or < 0 for error */ int wolfSSL_CertPemToDer(const unsigned char* pem, int pemSz, unsigned char* buff, int buffSz, int type) @@ -2168,6 +2203,112 @@ int AlreadySigner(WOLFSSL_CERT_MANAGER* cm, byte* hash) } +#ifdef WOLFSSL_TRUST_PEER_CERT +/* hash is the SHA digest of name, just use first 32 bits as hash */ +static INLINE word32 TrustedPeerHashSigner(const byte* hash) +{ + return MakeWordFromHash(hash) % TP_TABLE_SIZE; +} + +/* does trusted peer already exist on signer list */ +int AlreadyTrustedPeer(WOLFSSL_CERT_MANAGER* cm, byte* hash) +{ + TrustedPeerCert* tp; + int ret = 0; + word32 row = TrustedPeerHashSigner(hash); + + if (LockMutex(&cm->tpLock) != 0) + return ret; + tp = cm->tpTable[row]; + while (tp) { + byte* subjectHash; + #ifndef NO_SKID + subjectHash = tp->subjectKeyIdHash; + #else + subjectHash = tp->subjectNameHash; + #endif + if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) { + ret = 1; + break; + } + tp = tp->next; + } + UnLockMutex(&cm->tpLock); + + return ret; +} + + +/* return Trusted Peer if found, otherwise NULL + type is what to match on + */ +TrustedPeerCert* GetTrustedPeer(void* vp, byte* hash, int type) +{ + WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)vp; + TrustedPeerCert* ret = NULL; + TrustedPeerCert* tp = NULL; + word32 row; + + if (cm == NULL || hash == NULL) + return NULL; + + row = TrustedPeerHashSigner(hash); + + if (LockMutex(&cm->tpLock) != 0) + return ret; + + tp = cm->tpTable[row]; + while (tp) { + byte* subjectHash; + switch (type) { + #ifndef NO_SKID + case WC_MATCH_SKID: + subjectHash = tp->subjectKeyIdHash; + break; + #endif + case WC_MATCH_NAME: + subjectHash = tp->subjectNameHash; + break; + default: + WOLFSSL_MSG("Unknown search type"); + UnLockMutex(&cm->tpLock); + return NULL; + } + if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) { + ret = tp; + break; + } + tp = tp->next; + } + UnLockMutex(&cm->tpLock); + + return ret; +} + + +int MatchTrustedPeer(TrustedPeerCert* tp, DecodedCert* cert) +{ + if (tp == NULL || cert == NULL) + return BAD_FUNC_ARG; + + /* subject key id or subject hash has been compared when searching + tpTable for the cert from function GetTrustedPeer */ + + /* compare signatures */ + if (tp->sigLen == cert->sigLength) { + if (XMEMCMP(tp->sig, cert->signature, cert->sigLength)) { + return SSL_FAILURE; + } + } + else { + return SSL_FAILURE; + } + + return SSL_SUCCESS; +} +#endif /* WOLFSSL_TRUST_PEER_CERT */ + + /* return CA if found, otherwise NULL */ Signer* GetCA(void* vp, byte* hash) { @@ -2234,6 +2375,134 @@ Signer* GetCAByName(void* vp, byte* hash) #endif +#ifdef WOLFSSL_TRUST_PEER_CERT +/* add a trusted peer cert to linked list */ +int AddTrustedPeer(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int verify) +{ + int ret, row; + TrustedPeerCert* peerCert; + DecodedCert* cert = NULL; + DerBuffer* der = *pDer; + byte* subjectHash = NULL; + + WOLFSSL_MSG("Adding a Trusted Peer Cert"); + + cert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (cert == NULL) + return MEMORY_E; + + InitDecodedCert(cert, der->buffer, der->length, cm->heap); + if ((ret = ParseCert(cert, TRUSTED_PEER_TYPE, verify, cm)) != 0) { + XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + WOLFSSL_MSG(" Parsed new trusted peer cert"); + + peerCert = (TrustedPeerCert*)XMALLOC(sizeof(TrustedPeerCert), NULL, + DYNAMIC_TYPE_CERT); + if (peerCert == NULL) { + FreeDecodedCert(cert); + XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } + XMEMSET(peerCert, 0, sizeof(TrustedPeerCert)); + +#ifndef NO_SKID + if (cert->extAuthKeyIdSet) { + subjectHash = cert->extSubjKeyId; + } + else { + subjectHash = cert->subjectHash; + } +#else + subjectHash = cert->subjectHash; +#endif + + #ifndef IGNORE_NAME_CONSTRAINTS + if (peerCert->permittedNames) + FreeNameSubtrees(peerCert->permittedNames, cm->heap); + if (peerCert->excludedNames) + FreeNameSubtrees(peerCert->excludedNames, cm->heap); + #endif + + if (AlreadyTrustedPeer(cm, subjectHash)) { + WOLFSSL_MSG(" Already have this CA, not adding again"); + (void)ret; + } + else { + /* add trusted peer signature */ + peerCert->sigLen = cert->sigLength; + peerCert->sig = XMALLOC(cert->sigLength, cm->heap, + DYNAMIC_TYPE_SIGNATURE); + if (peerCert->sig == NULL) { + FreeDecodedCert(cert); + XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER); + FreeTrustedPeer(peerCert, cm->heap); + return MEMORY_E; + } + XMEMCPY(peerCert->sig, cert->signature, cert->sigLength); + + /* add trusted peer name */ + peerCert->nameLen = cert->subjectCNLen; + peerCert->name = cert->subjectCN; + #ifndef IGNORE_NAME_CONSTRAINTS + peerCert->permittedNames = cert->permittedNames; + peerCert->excludedNames = cert->excludedNames; + #endif + + /* add SKID when available and hash of name */ + #ifndef NO_SKID + XMEMCPY(peerCert->subjectKeyIdHash, cert->extSubjKeyId, + SIGNER_DIGEST_SIZE); + #endif + XMEMCPY(peerCert->subjectNameHash, cert->subjectHash, + SIGNER_DIGEST_SIZE); + peerCert->next = NULL; /* If Key Usage not set, all uses valid. */ + cert->subjectCN = 0; + #ifndef IGNORE_NAME_CONSTRAINTS + cert->permittedNames = NULL; + cert->excludedNames = NULL; + #endif + + #ifndef NO_SKID + if (cert->extAuthKeyIdSet) { + row = TrustedPeerHashSigner(peerCert->subjectKeyIdHash); + } + else { + row = TrustedPeerHashSigner(peerCert->subjectNameHash); + } + #else + row = TrustedPeerHashSigner(peerCert->subjectNameHash); + #endif + + if (LockMutex(&cm->tpLock) == 0) { + peerCert->next = cm->tpTable[row]; + cm->tpTable[row] = peerCert; /* takes ownership */ + UnLockMutex(&cm->tpLock); + } + else { + WOLFSSL_MSG(" Trusted Peer Cert Mutex Lock failed"); + FreeDecodedCert(cert); + XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER); + FreeTrustedPeer(peerCert, cm->heap); + return BAD_MUTEX_E; + } + } + + WOLFSSL_MSG(" Freeing parsed trusted peer cert"); + FreeDecodedCert(cert); + XFREE(cert, NULL, DYNAMIC_TYPE_TMP_BUFFER); + WOLFSSL_MSG(" Freeing der trusted peer cert"); + FreeDer(&der); + WOLFSSL_MSG(" OK Freeing der trusted peer cert"); + WOLFSSL_LEAVE("AddTrustedPeer", ret); + + return SSL_SUCCESS; +} +#endif /* WOLFSSL_TRUST_PEER_CERT */ + + /* owns der, internal now uses too */ /* type flag ids from user or from chain received during verify don't allow chain ones to be added w/o isCA extension */ @@ -2638,6 +2907,7 @@ int PemToDer(const unsigned char* buff, long longSz, int type, switch (type) { case CA_TYPE: /* same as below */ + case TRUSTED_PEER_TYPE: case CERT_TYPE: header=BEGIN_CERT; footer=END_CERT; break; case CRL_TYPE: header=BEGIN_X509_CRL; footer=END_X509_CRL; break; case DH_PARAM_TYPE: header=BEGIN_DH_PARAM; footer=END_DH_PARAM; break; @@ -3068,6 +3338,17 @@ static int ProcessBuffer(WOLFSSL_CTX* ctx, const unsigned char* buff, /* verify CA unless user set to no verify */ return AddCA(ctx->cm, &der, WOLFSSL_USER_CA, !ctx->verifyNone); } +#ifdef WOLFSSL_TRUST_PEER_CERT + else if (type == TRUSTED_PEER_TYPE) { + if (ctx == NULL) { + WOLFSSL_MSG("Need context for trusted peer cert load"); + FreeDer(&der); + return BAD_FUNC_ARG; + } + /* add trusted peer cert */ + return AddTrustedPeer(ctx->cm, &der, !ctx->verifyNone); + } +#endif /* WOLFSSL_TRUST_PEER_CERT */ else if (type == CERT_TYPE) { if (ssl) { /* Make sure previous is free'd */ @@ -3796,7 +4077,8 @@ int ProcessFile(WOLFSSL_CTX* ctx, const char* fname, int format, int type, if ( (ret = (int)XFREAD(myBuffer, sz, 1, file)) < 0) ret = SSL_BAD_FILE; else { - if (type == CA_TYPE && format == SSL_FILETYPE_PEM) + if ((type == CA_TYPE || type == TRUSTED_PEER_TYPE) + && format == SSL_FILETYPE_PEM) ret = ProcessChainBuffer(ctx, myBuffer, sz, format, type, ssl); #ifdef HAVE_CRL else if (type == CRL_TYPE) @@ -3926,6 +4208,25 @@ int wolfSSL_CTX_load_verify_locations(WOLFSSL_CTX* ctx, const char* file, } +#ifdef WOLFSSL_TRUST_PEER_CERT +/* Used to specify a peer cert to match when connecting + ctx : the ctx structure to load in peer cert + file: the string name of cert file + type: type of format such as PEM/DER + */ +int wolfSSL_CTX_trust_peer_cert(WOLFSSL_CTX* ctx, const char* file, int type) +{ + WOLFSSL_ENTER("wolfSSL_CTX_trust_peer_cert"); + + if (ctx == NULL || file == NULL) { + return SSL_FAILURE; + } + + return ProcessFile(ctx, file, type, TRUSTED_PEER_TYPE, NULL, 0, NULL); +} +#endif /* WOLFSSL_TRUST_PEER_CERT */ + + /* Verify the certificate, SSL_SUCCESS for ok, < 0 for error */ int wolfSSL_CertManagerVerify(WOLFSSL_CERT_MANAGER* cm, const char* fname, int format) @@ -7352,6 +7653,28 @@ int wolfSSL_set_compression(WOLFSSL* ssl) } +#ifdef WOLFSSL_TRUST_PEER_CERT + int wolfSSL_CTX_trust_peer_buffer(WOLFSSL_CTX* ctx, + const unsigned char* in, + long sz, int format) + { + WOLFSSL_ENTER("wolfSSL_CTX_trust_peer_buffer"); + + /* sanity check on arguments */ + if (sz < 0 || in == NULL || ctx == NULL) { + return BAD_FUNC_ARG; + } + + if (format == SSL_FILETYPE_PEM) + return ProcessChainBuffer(ctx, in, sz, format, + TRUSTED_PEER_TYPE, NULL); + else + return ProcessBuffer(ctx, in, sz, format, TRUSTED_PEER_TYPE, + NULL,NULL,0); + } +#endif /* WOLFSSL_TRUST_PEER_CERT */ + + int wolfSSL_CTX_use_certificate_buffer(WOLFSSL_CTX* ctx, const unsigned char* in, long sz, int format) { @@ -7535,6 +7858,18 @@ int wolfSSL_set_compression(WOLFSSL* ssl) return wolfSSL_CertManagerUnloadCAs(ctx->cm); } + +#ifdef WOLFSSL_TRUST_PEER_CERT + int wolfSSL_CTX_Unload_trust_peers(WOLFSSL_CTX* ctx) + { + WOLFSSL_ENTER("wolfSSL_CTX_Unload_trust_peers"); + + if (ctx == NULL) + return BAD_FUNC_ARG; + + return wolfSSL_CertManagerUnload_trust_peers(ctx->cm); + } +#endif /* WOLFSSL_TRUST_PEER_CERT */ /* old NO_FILESYSTEM end */ #endif /* !NO_CERTS */ diff --git a/tests/api.c b/tests/api.c index f2340c224..59fcb4d51 100644 --- a/tests/api.c +++ b/tests/api.c @@ -211,6 +211,57 @@ static void test_wolfSSL_CTX_use_PrivateKey_file(void) } +/* test both file and buffer versions along with unloading trusted peer certs */ +static void test_wolfSSL_CTX_trust_peer_cert(void) +{ +#if !defined(NO_CERTS) && defined(WOLFSSL_TRUST_PEER_CERT) + WOLFSSL_CTX *ctx; + + AssertNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + +#if !defined(NO_FILESYSTEM) + /* invalid file */ + assert(wolfSSL_CTX_trust_peer_cert(ctx, NULL, + SSL_FILETYPE_PEM) != SSL_SUCCESS); + assert(wolfSSL_CTX_trust_peer_cert(ctx, bogusFile, + SSL_FILETYPE_PEM) != SSL_SUCCESS); + assert(wolfSSL_CTX_trust_peer_cert(ctx, cliCert, + SSL_FILETYPE_ASN1) != SSL_SUCCESS); + + /* success */ + assert(wolfSSL_CTX_trust_peer_cert(ctx, cliCert, SSL_FILETYPE_PEM) + == SSL_SUCCESS); + + /* unload cert */ + assert(wolfSSL_CTX_Unload_trust_peers(NULL) != SSL_SUCCESS); + assert(wolfSSL_CTX_Unload_trust_peers(ctx) == SSL_SUCCESS); +#endif + + /* Test of loading certs from buffers */ + + /* invalid buffer */ + assert(wolfSSL_CTX_trust_peer_buffer(ctx, NULL, -1, + SSL_FILETYPE_ASN1) != SSL_SUCCESS); + + /* success */ +#ifdef USE_CERT_BUFFERS_1024 + assert(wolfSSL_CTX_trust_peer_buffer(ctx, client_cert_der_1024, + sizeof_client_cert_der_1024, SSL_FILETYPE_ASN1) == SSL_SUCCESS); +#endif +#ifdef USE_CERT_BUFFERS_2048 + assert(wolfSSL_CTX_trust_peer_buffer(ctx, client_cert_der_2048, + sizeof_client_cert_der_2048, SSL_FILETYPE_ASN1) == SSL_SUCCESS); +#endif + + /* unload cert */ + assert(wolfSSL_CTX_Unload_trust_peers(NULL) != SSL_SUCCESS); + assert(wolfSSL_CTX_Unload_trust_peers(ctx) == SSL_SUCCESS); + + wolfSSL_CTX_free(ctx); +#endif +} + + static void test_wolfSSL_CTX_load_verify_locations(void) { #if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) @@ -1599,6 +1650,7 @@ void ApiTest(void) test_wolfSSL_CTX_use_certificate_file(); test_wolfSSL_CTX_use_PrivateKey_file(); test_wolfSSL_CTX_load_verify_locations(); + test_wolfSSL_CTX_trust_peer_cert(); test_wolfSSL_CTX_SetTmpDH_file(); test_wolfSSL_CTX_SetTmpDH_buffer(); test_server_wolfSSL_new(); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index c65e3c713..79ed25d03 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -4994,7 +4994,7 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm) } #endif - if (verify && type != CA_TYPE) { + if (verify && type != CA_TYPE && type != TRUSTED_PEER_TYPE) { Signer* ca = NULL; #ifndef NO_SKID if (cert->extAuthKeyIdSet) @@ -5110,6 +5110,48 @@ void FreeSignerTable(Signer** table, int rows, void* heap) } } +#ifdef WOLFSSL_TRUST_PEER_CERT +/* Free an individual trusted peer cert */ +void FreeTrustedPeer(TrustedPeerCert* tp, void* heap) +{ + if (tp == NULL) { + return; + } + + if (tp->name) { + XFREE(tp->name, heap, DYNAMIC_TYPE_SUBJECT_CN); + } + + if (tp->sig) { + XFREE(tp->sig, heap, DYNAMIC_TYPE_SIGNATURE); + } + #ifndef IGNORE_NAME_CONSTRAINTS + if (tp->permittedNames) + FreeNameSubtrees(tp->permittedNames, heap); + if (tp->excludedNames) + FreeNameSubtrees(tp->excludedNames, heap); + #endif + XFREE(tp, heap, DYNAMIC_TYPE_CERT); + + (void)heap; +} + +/* Free the whole Trusted Peer linked list */ +void FreeTrustedPeerTable(TrustedPeerCert** table, int rows, void* heap) +{ + int i; + + for (i = 0; i < rows; i++) { + TrustedPeerCert* tp = table[i]; + while (tp) { + TrustedPeerCert* next = tp->next; + FreeTrustedPeer(tp, heap); + tp = next; + } + table[i] = NULL; + } +} +#endif /* WOLFSSL_TRUST_PEER_CERT */ WOLFSSL_LOCAL int SetMyVersion(word32 version, byte* output, int header) { diff --git a/wolfssl/internal.h b/wolfssl/internal.h index afc5dc80d..1bdad174d 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1435,17 +1435,27 @@ struct WOLFSSL_CRL { #ifdef NO_ASN typedef struct Signer Signer; +#ifdef WOLFSSL_TRUST_PEER_CERT + typedef struct TrustedPeerCert TrustedPeerCert; +#endif #endif #ifndef CA_TABLE_SIZE #define CA_TABLE_SIZE 11 #endif +#ifdef WOLFSSL_TRUST_PEER_CERT + #define TP_TABLE_SIZE 11 +#endif /* wolfSSL Certificate Manager */ struct WOLFSSL_CERT_MANAGER { Signer* caTable[CA_TABLE_SIZE]; /* the CA signer table */ void* heap; /* heap helper */ +#ifdef WOLFSSL_TRUST_PEER_CERT + TrustedPeerCert* tpTable[TP_TABLE_SIZE]; /* table of trusted peer certs */ + wolfSSL_Mutex tpLock; /* trusted peer list lock */ +#endif WOLFSSL_CRL* crl; /* CRL checker */ WOLFSSL_OCSP* ocsp; /* OCSP checker */ #if !defined(NO_WOLFSSL_SEVER) && (defined(HAVE_CERTIFICATE_STATUS_REQUEST) \ @@ -1934,6 +1944,12 @@ int ProcessOldClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, int AddCA(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int type, int verify); WOLFSSL_LOCAL int AlreadySigner(WOLFSSL_CERT_MANAGER* cm, byte* hash); +#ifdef WOLFSSL_TRUST_PEER_CERT + WOLFSSL_LOCAL + int AddTrustedPeer(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int verify); + WOLFSSL_LOCAL + int AlreadyTrustedPeer(WOLFSSL_CERT_MANAGER* cm, byte* hash); +#endif #endif /* All cipher suite related info */ @@ -2822,6 +2838,17 @@ WOLFSSL_LOCAL int VerifyClientSuite(WOLFSSL* ssl); const byte* plain, word32 plainSz, RsaKey* key); #endif + #ifdef WOLFSSL_TRUST_PEER_CERT + + /* options for searching hash table for a matching trusted peer cert */ + #define WC_MATCH_SKID 0 + #define WC_MATCH_NAME 1 + + WOLFSSL_LOCAL TrustedPeerCert* GetTrustedPeer(void* vp, byte* hash, + int type); + WOLFSSL_LOCAL int MatchTrustedPeer(TrustedPeerCert* tp, + DecodedCert* cert); + #endif WOLFSSL_LOCAL Signer* GetCA(void* cm, byte* hash); #ifndef NO_SKID WOLFSSL_LOCAL Signer* GetCAByName(void* cm, byte* hash); diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 6e965561e..ea9ba0801 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -229,6 +229,9 @@ WOLFSSL_API int wolfSSL_CTX_use_certificate_file(WOLFSSL_CTX*, const char*, int) WOLFSSL_API int wolfSSL_CTX_use_PrivateKey_file(WOLFSSL_CTX*, const char*, int); WOLFSSL_API int wolfSSL_CTX_load_verify_locations(WOLFSSL_CTX*, const char*, const char*); +#ifdef WOLFSSL_TRUST_PEER_CERT +WOLFSSL_API int wolfSSL_CTX_trust_peer_cert(WOLFSSL_CTX*, const char*, int); +#endif WOLFSSL_API int wolfSSL_CTX_use_certificate_chain_file(WOLFSSL_CTX *, const char *file); WOLFSSL_API int wolfSSL_CTX_use_RSAPrivateKey_file(WOLFSSL_CTX*, const char*, int); @@ -968,6 +971,11 @@ WOLFSSL_API int wolfSSL_make_eap_keys(WOLFSSL*, void* key, unsigned int len, #ifndef NO_CERTS /* SSL_CTX versions */ WOLFSSL_API int wolfSSL_CTX_UnloadCAs(WOLFSSL_CTX*); +#ifdef WOLFSSL_TRUST_PEER_CERT + WOLFSSL_API int wolfSSL_CTX_Unload_trust_peers(WOLFSSL_CTX*); + WOLFSSL_API int wolfSSL_CTX_trust_peer_buffer(WOLFSSL_CTX*, + const unsigned char*, long, int); +#endif WOLFSSL_API int wolfSSL_CTX_load_verify_buffer(WOLFSSL_CTX*, const unsigned char*, long, int); WOLFSSL_API int wolfSSL_CTX_use_certificate_buffer(WOLFSSL_CTX*, @@ -1247,6 +1255,9 @@ WOLFSSL_API void* wolfSSL_GetRsaDecCtx(WOLFSSL* ssl); WOLFSSL_API int wolfSSL_CertManagerLoadCABuffer(WOLFSSL_CERT_MANAGER*, const unsigned char* in, long sz, int format); WOLFSSL_API int wolfSSL_CertManagerUnloadCAs(WOLFSSL_CERT_MANAGER* cm); +#ifdef WOLFSSL_TRUST_PEER_CERT + WOLFSSL_API int wolfSSL_CertManagerUnload_trust_peers(WOLFSSL_CERT_MANAGER* cm); +#endif WOLFSSL_API int wolfSSL_CertManagerVerify(WOLFSSL_CERT_MANAGER*, const char* f, int format); WOLFSSL_API int wolfSSL_CertManagerVerifyBuffer(WOLFSSL_CERT_MANAGER* cm, diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index fbf4db2e4..c7a4e340b 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -365,6 +365,9 @@ struct DecodedName { typedef struct DecodedCert DecodedCert; typedef struct DecodedName DecodedName; typedef struct Signer Signer; +#ifdef WOLFSSL_TRUST_PEER_CERT +typedef struct TrustedPeerCert TrustedPeerCert; +#endif /* WOLFSSL_TRUST_PEER_CERT */ struct DecodedCert { @@ -554,6 +557,28 @@ struct Signer { }; +#ifdef WOLFSSL_TRUST_PEER_CERT +/* used for having trusted peer certs rather then CA */ +struct TrustedPeerCert { + int nameLen; + char* name; /* common name */ + #ifndef IGNORE_NAME_CONSTRAINTS + Base_entry* permittedNames; + Base_entry* excludedNames; + #endif /* IGNORE_NAME_CONSTRAINTS */ + byte subjectNameHash[SIGNER_DIGEST_SIZE]; + /* sha hash of names in certificate */ + #ifndef NO_SKID + byte subjectKeyIdHash[SIGNER_DIGEST_SIZE]; + /* sha hash of names in certificate */ + #endif + word32 sigLen; + byte* sig; + struct TrustedPeerCert* next; +}; +#endif /* WOLFSSL_TRUST_PEER_CERT */ + + /* not for public consumption but may use for testing sometimes */ #ifdef WOLFSSL_TEST_CERT #define WOLFSSL_TEST_API WOLFSSL_API @@ -575,7 +600,10 @@ WOLFSSL_LOCAL int DecodeToKey(DecodedCert*, int verify); WOLFSSL_LOCAL Signer* MakeSigner(void*); WOLFSSL_LOCAL void FreeSigner(Signer*, void*); WOLFSSL_LOCAL void FreeSignerTable(Signer**, int, void*); - +#ifdef WOLFSSL_TRUST_PEER_CERT +WOLFSSL_LOCAL void FreeTrustedPeer(TrustedPeerCert*, void*); +WOLFSSL_LOCAL void FreeTrustedPeerTable(TrustedPeerCert**, int, void*); +#endif /* WOLFSSL_TRUST_PEER_CERT */ WOLFSSL_LOCAL int ToTraditional(byte* buffer, word32 length); WOLFSSL_LOCAL int ToTraditionalEnc(byte* buffer, word32 length,const char*,int); diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index fee49760f..f450e4784 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -50,7 +50,8 @@ enum CertType { RSA_TYPE, PUBLICKEY_TYPE, RSA_PUBLICKEY_TYPE, - ECC_PUBLICKEY_TYPE + ECC_PUBLICKEY_TYPE, + TRUSTED_PEER_TYPE };