From 9192d33083688a81237e963db93d1a7486338def Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Fri, 27 Sep 2019 17:36:35 +0200 Subject: [PATCH] Bug 1494063, add -x option to tstclnt/selfserv to export keying material, r=mt Reviewers: rrelyea, mt Reviewed By: mt Subscribers: HubertKario Bug #: 1494063 Differential Revision: https://phabricator.services.mozilla.com/D29166 --HG-- extra : amend_source : 6dfd5e36e755ace210e517388c00f5eac5c71316 --- cmd/lib/Makefile | 1 + cmd/lib/lib.gyp | 3 +- cmd/lib/manifest.mn | 2 + cmd/lib/secutil.c | 162 ++++++++++++++++++++++++++++++++++++++++ cmd/lib/secutil.h | 14 ++++ cmd/platlibs.mk | 4 +- cmd/selfserv/selfserv.c | 36 ++++++++- cmd/tstclnt/tstclnt.c | 34 ++++++++- tests/ssl/ssl.sh | 37 ++++++++- 9 files changed, 284 insertions(+), 9 deletions(-) diff --git a/cmd/lib/Makefile b/cmd/lib/Makefile index 0fb6c90585..6b53451ef0 100644 --- a/cmd/lib/Makefile +++ b/cmd/lib/Makefile @@ -27,6 +27,7 @@ include $(CORE_DEPTH)/coreconf/config.mk ####################################################################### include config.mk +include ../platlibs.mk ####################################################################### # (5) Execute "global" rules. (OPTIONAL) # diff --git a/cmd/lib/lib.gyp b/cmd/lib/lib.gyp index 6b7da58108..c5835a8696 100644 --- a/cmd/lib/lib.gyp +++ b/cmd/lib/lib.gyp @@ -27,7 +27,8 @@ ], 'target_defaults': { 'defines': [ - 'NSPR20' + 'NSPR20', + 'NSS_USE_STATIC_LIBS' ] }, 'variables': { diff --git a/cmd/lib/manifest.mn b/cmd/lib/manifest.mn index 87440c0526..b3c36b3a98 100644 --- a/cmd/lib/manifest.mn +++ b/cmd/lib/manifest.mn @@ -37,3 +37,5 @@ CSRCS = basicutil.c \ endif NO_MD_RELEASE = 1 + +USE_STATIC_LIBS = 1 diff --git a/cmd/lib/secutil.c b/cmd/lib/secutil.c index 97c7f750a6..aafde9b5fc 100644 --- a/cmd/lib/secutil.c +++ b/cmd/lib/secutil.c @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef XP_UNIX #include @@ -3979,3 +3980,164 @@ parseSigSchemeList(const char *arg, const SSLSignatureScheme **enabledSigSchemes *enabledSigSchemes = schemes; return SECSuccess; } + +/* Parse the exporter spec in the form: LABEL[:OUTPUT-LENGTH[:CONTEXT]] */ +static SECStatus +parseExporter(const char *arg, + secuExporter *exporter) +{ + SECStatus rv = SECSuccess; + + char *str = PORT_Strdup(arg); + if (!str) { + rv = SECFailure; + goto done; + } + + char *labelEnd = strchr(str, ':'); + if (labelEnd) { + *labelEnd = '\0'; + labelEnd++; + + /* To extract CONTEXT, first skip OUTPUT-LENGTH */ + char *outputEnd = strchr(labelEnd, ':'); + if (outputEnd) { + *outputEnd = '\0'; + outputEnd++; + + exporter->hasContext = PR_TRUE; + exporter->context.data = (unsigned char *)PORT_Strdup(outputEnd); + exporter->context.len = strlen(outputEnd); + if (PORT_Strncasecmp((char *)exporter->context.data, "0x", 2) == 0) { + rv = SECU_SECItemHexStringToBinary(&exporter->context); + if (rv != SECSuccess) { + goto done; + } + } + } + } + + if (labelEnd && *labelEnd != '\0') { + long int outputLength = strtol(labelEnd, NULL, 10); + if (!(outputLength > 0 && outputLength <= UINT_MAX)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + rv = SECFailure; + goto done; + } + exporter->outputLength = outputLength; + } else { + exporter->outputLength = 20; + } + + char *label = PORT_Strdup(str); + exporter->label.data = (unsigned char *)label; + exporter->label.len = strlen(label); + if (PORT_Strncasecmp((char *)exporter->label.data, "0x", 2) == 0) { + rv = SECU_SECItemHexStringToBinary(&exporter->label); + if (rv != SECSuccess) { + goto done; + } + } + +done: + PORT_Free(str); + + return rv; +} + +SECStatus +parseExporters(const char *arg, + const secuExporter **enabledExporters, + unsigned int *enabledExporterCount) +{ + secuExporter *exporters; + unsigned int numValues = 0; + unsigned int count = 0; + + if (countItems(arg, &numValues) != SECSuccess) { + return SECFailure; + } + exporters = PORT_ZNewArray(secuExporter, numValues); + if (!exporters) { + return SECFailure; + } + + /* Get exporter definitions. */ + char *str = PORT_Strdup(arg); + if (!str) { + goto done; + } + char *p = strtok(str, ","); + while (p) { + SECStatus rv = parseExporter(p, &exporters[count++]); + if (rv != SECSuccess) { + count = 0; + goto done; + } + p = strtok(NULL, ","); + } + +done: + PORT_Free(str); + if (!count) { + PORT_Free(exporters); + return SECFailure; + } + + *enabledExporterCount = count; + *enabledExporters = exporters; + return SECSuccess; +} + +static SECStatus +exportKeyingMaterial(PRFileDesc *fd, const secuExporter *exporter) +{ + SECStatus rv = SECSuccess; + unsigned char *out = PORT_Alloc(exporter->outputLength); + + if (!out) { + fprintf(stderr, "Unable to allocate buffer for keying material\n"); + return SECFailure; + } + rv = SSL_ExportKeyingMaterial(fd, + (char *)exporter->label.data, + exporter->label.len, + exporter->hasContext, + exporter->context.data, + exporter->context.len, + out, + exporter->outputLength); + if (rv != SECSuccess) { + goto done; + } + fprintf(stdout, "Exported Keying Material:\n"); + secu_PrintRawString(stdout, (SECItem *)&exporter->label, "Label", 1); + if (exporter->hasContext) { + SECU_PrintAsHex(stdout, &exporter->context, "Context", 1); + } + SECU_Indent(stdout, 1); + fprintf(stdout, "Length: %u\n", exporter->outputLength); + SECItem temp = { siBuffer, out, exporter->outputLength }; + SECU_PrintAsHex(stdout, &temp, "Keying Material", 1); + +done: + PORT_Free(out); + return rv; +} + +SECStatus +exportKeyingMaterials(PRFileDesc *fd, + const secuExporter *exporters, + unsigned int exporterCount) +{ + unsigned int i; + + for (i = 0; i < exporterCount; i++) { + SECStatus rv = exportKeyingMaterial(fd, &exporters[i]); + if (rv != SECSuccess) { + return rv; + } + } + + return SECSuccess; +} diff --git a/cmd/lib/secutil.h b/cmd/lib/secutil.h index 90d7639097..c6da961e74 100644 --- a/cmd/lib/secutil.h +++ b/cmd/lib/secutil.h @@ -409,6 +409,20 @@ SECStatus parseGroupList(const char *arg, SSLNamedGroup **enabledGroups, SECStatus parseSigSchemeList(const char *arg, const SSLSignatureScheme **enabledSigSchemes, unsigned int *enabledSigSchemeCount); +typedef struct { + SECItem label; + PRBool hasContext; + SECItem context; + unsigned int outputLength; +} secuExporter; + +SECStatus parseExporters(const char *arg, + const secuExporter **enabledExporters, + unsigned int *enabledExporterCount); + +SECStatus exportKeyingMaterials(PRFileDesc *fd, + const secuExporter *exporters, + unsigned int exporterCount); /* * diff --git a/cmd/platlibs.mk b/cmd/platlibs.mk index a59d03d06b..6401778826 100644 --- a/cmd/platlibs.mk +++ b/cmd/platlibs.mk @@ -144,8 +144,8 @@ endif ifeq ($(OS_ARCH), WINNT) EXTRA_LIBS += \ - $(NSS_LIBS_1) \ $(SECTOOL_LIB) \ + $(NSS_LIBS_1) \ $(NSS_LIBS_2) \ $(SOFTOKENLIB) \ $(CRYPTOLIB) \ @@ -161,8 +161,8 @@ EXTRA_LIBS += \ else EXTRA_LIBS += \ - $(NSS_LIBS_1) \ $(SECTOOL_LIB) \ + $(NSS_LIBS_1) \ $(NSS_LIBS_2) \ $(SOFTOKENLIB) \ $(NSS_LIBS_3) \ diff --git a/cmd/selfserv/selfserv.c b/cmd/selfserv/selfserv.c index fb2d8d53cf..03e39d67bf 100644 --- a/cmd/selfserv/selfserv.c +++ b/cmd/selfserv/selfserv.c @@ -235,7 +235,13 @@ PrintParameterUsage() " rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512,\n" "-Z enable 0-RTT (for TLS 1.3; also use -u)\n" "-E enable post-handshake authentication\n" - " (for TLS 1.3; only has an effect with 3 or more -r options)\n", + " (for TLS 1.3; only has an effect with 3 or more -r options)\n" + "-x Export and print keying material after successful handshake\n" + " The argument is a comma separated list of exporters in the form:\n" + " LABEL[:OUTPUT-LENGTH[:CONTEXT]]\n" + " where LABEL and CONTEXT can be either a free-form string or\n" + " a hex string if it is preceded by \"0x\"; OUTPUT-LENGTH\n" + " is a decimal integer.\n", stderr); } @@ -812,6 +818,8 @@ SSLNamedGroup *enabledGroups = NULL; unsigned int enabledGroupsCount = 0; const SSLSignatureScheme *enabledSigSchemes = NULL; unsigned int enabledSigSchemeCount = 0; +const secuExporter *enabledExporters = NULL; +unsigned int enabledExporterCount = 0; static char *virtServerNameArray[MAX_VIRT_SERVER_NAME_ARRAY_INDEX]; static int virtServerNameIndex = 1; @@ -1822,6 +1830,15 @@ handshakeCallback(PRFileDesc *fd, void *client_data) SECITEM_FreeItem(hostInfo, PR_TRUE); } } + if (enabledExporters) { + SECStatus rv = exportKeyingMaterials(fd, enabledExporters, enabledExporterCount); + if (rv != SECSuccess) { + PRErrorCode err = PR_GetError(); + fprintf(stderr, + "couldn't export keying material: %s\n", + SECU_Strerror(err)); + } + } } void @@ -2012,7 +2029,7 @@ server_main( errExit("SSL_CipherPrefSetDefault:TLS_RSA_WITH_NULL_MD5"); } - if (expectedHostNameVal) { + if (expectedHostNameVal || enabledExporters) { SSL_HandshakeCallback(model_sock, handshakeCallback, (void *)expectedHostNameVal); } @@ -2246,11 +2263,11 @@ main(int argc, char **argv) /* please keep this list of options in ASCII collating sequence. ** numbers, then capital letters, then lower case, alphabetical. - ** XXX: 'B', 'E', 'q', and 'x' were used in the past but removed + ** XXX: 'B', and 'q' were used in the past but removed ** in 3.28, please leave some time before resuing those. ** 'z' was removed in 3.39. */ optstate = PL_CreateOptState(argc, argv, - "2:A:C:DEGH:I:J:L:M:NP:QRS:T:U:V:W:YZa:bc:d:e:f:g:hi:jk:lmn:op:rst:uvw:y"); + "2:A:C:DEGH:I:J:L:M:NP:QRS:T:U:V:W:YZa:bc:d:e:f:g:hi:jk:lmn:op:rst:uvw:x:y"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { ++optionsFound; switch (optstate->option) { @@ -2496,6 +2513,17 @@ main(int argc, char **argv) } break; + case 'x': + rv = parseExporters(optstate->value, + &enabledExporters, &enabledExporterCount); + if (rv != SECSuccess) { + PL_DestroyOptState(optstate); + fprintf(stderr, "Bad exporter specified.\n"); + fprintf(stderr, "Run '%s -h' for usage information.\n", progName); + exit(5); + } + break; + default: case '?': fprintf(stderr, "Unrecognized or bad option specified.\n"); diff --git a/cmd/tstclnt/tstclnt.c b/cmd/tstclnt/tstclnt.c index 7f560111ed..6fa154106b 100644 --- a/cmd/tstclnt/tstclnt.c +++ b/cmd/tstclnt/tstclnt.c @@ -318,6 +318,13 @@ PrintParameterUsage() fprintf(stderr, "%-20s Enable post-handshake authentication\n" "%-20s for TLS 1.3; need to specify -n\n", "-E", ""); + fprintf(stderr, "%-20s Export and print keying material after successful handshake.\n" + "%-20s The argument is a comma separated list of exporters in the form:\n" + "%-20s LABEL[:OUTPUT-LENGTH[:CONTEXT]]\n" + "%-20s where LABEL and CONTEXT can be either a free-form string or\n" + "%-20s a hex string if it is preceded by \"0x\"; OUTPUT-LENGTH\n" + "%-20s is a decimal integer.\n", + "-x", "", "", "", "", ""); } static void @@ -998,6 +1005,8 @@ PRBool handshakeComplete = PR_FALSE; char *encryptedSNIKeys = NULL; PRBool enablePostHandshakeAuth = PR_FALSE; PRBool enableDelegatedCredentials = PR_FALSE; +const secuExporter *enabledExporters = NULL; +unsigned int enabledExporterCount = 0; static int writeBytesToServer(PRFileDesc *s, const PRUint8 *buf, int nb) @@ -1093,6 +1102,18 @@ handshakeCallback(PRFileDesc *fd, void *client_data) requestToExit = PR_TRUE; } handshakeComplete = PR_TRUE; + + if (enabledExporters) { + SECStatus rv; + + rv = exportKeyingMaterials(fd, enabledExporters, enabledExporterCount); + if (rv != SECSuccess) { + PRErrorCode err = PR_GetError(); + FPRINTF(stderr, + "couldn't export keying material: %s\n", + SECU_Strerror(err)); + } + } } static SECStatus @@ -1735,7 +1756,7 @@ main(int argc, char **argv) * Please leave some time before reusing these. */ optstate = PL_CreateOptState(argc, argv, - "46A:BCDEFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:"); + "46A:BCDEFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:x:"); while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case '?': @@ -1983,6 +2004,17 @@ main(int argc, char **argv) Usage(); } break; + + case 'x': + rv = parseExporters(optstate->value, + &enabledExporters, + &enabledExporterCount); + if (rv != SECSuccess) { + PL_DestroyOptState(optstate); + fprintf(stderr, "Bad exporter specified.\n"); + Usage(); + } + break; } } PL_DestroyOptState(optstate); diff --git a/tests/ssl/ssl.sh b/tests/ssl/ssl.sh index a050906947..d8892ed87c 100755 --- a/tests/ssl/ssl.sh +++ b/tests/ssl/ssl.sh @@ -84,7 +84,7 @@ ssl_init() PORT=$(($PORT + $padd)) fi NSS_SSL_TESTS=${NSS_SSL_TESTS:-normal_normal} - nss_ssl_run="stapling signed_cert_timestamps cov auth dtls scheme" + nss_ssl_run="stapling signed_cert_timestamps cov auth dtls scheme exporter" NSS_SSL_RUN=${NSS_SSL_RUN:-$nss_ssl_run} # Test case files @@ -1319,6 +1319,38 @@ ssl_scheme_stress() html "
" } +############################ ssl_exporter ################################### +# local shell function to test tstclnt and selfserv handling of TLS exporter +######################################################################### +ssl_exporter() +{ + html_head "SSL EXPORTER $NORM_EXT - server $SERVER_MODE/client $CLIENT_MODE" + + save_fileout=${fileout} + fileout=1 + SAVE_SERVEROUTFILE=${SERVEROUTFILE} + SERVEROUTFILE=server.out + exporters=("label" "label:10" "label:10:0xdeadbeef" "0x666f6f2c:10:0xdeadbeef" "label1:10:0xdeadbeef,label2:10") + for exporter in "${exporters[@]}"; do + start_selfserv -V tls1.2:tls1.2 -x "$exporter" + + echo "tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f -d ${P_R_CLIENTDIR} $verbose ${CLIENT_OPTIONS} \\" + echo " -V tls1.2:tls1.2 -x $exporter < ${REQUEST_FILE}" + ${PROFTOOL} ${BINDIR}/tstclnt -4 -p ${PORT} -h ${HOSTADDR} -f ${CLIENT_OPTIONS} \ + -d ${P_R_CLIENTDIR} $verbose -V tls1.2:tls1.2 -x "$exporter" < ${REQUEST_FILE} 2>&1 > client.out + kill_selfserv + diff <(LC_ALL=C grep -A1 "^ *Keying Material:" server.out) \ + <(LC_ALL=C grep -A1 "^ *Keying Material:" client.out) + ret=$? + html_msg $ret 0 "${testname}" \ + "produced a returncode of $ret, expected is 0" + done + SERVEROUTFILE=${SAVE_SERVEROUTFILE} + fileout=${save_fileout} + + html "
" +} + ############################## ssl_cleanup ############################# # local shell function to finish this script (no exit since it might be # sourced) @@ -1363,6 +1395,9 @@ ssl_run() ssl_scheme ssl_scheme_stress ;; + "exporter") + ssl_exporter + ;; esac done }