Commit 9192d330 authored by Daiki Ueno's avatar Daiki Ueno

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
parent 2323fb4d
......@@ -27,6 +27,7 @@ include $(CORE_DEPTH)/coreconf/config.mk
#######################################################################
include config.mk
include ../platlibs.mk
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
......
......@@ -27,7 +27,8 @@
],
'target_defaults': {
'defines': [
'NSPR20'
'NSPR20',
'NSS_USE_STATIC_LIBS'
]
},
'variables': {
......
......@@ -37,3 +37,5 @@ CSRCS = basicutil.c \
endif
NO_MD_RELEASE = 1
USE_STATIC_LIBS = 1
......@@ -22,6 +22,7 @@
#include <stdarg.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#ifdef XP_UNIX
#include <unistd.h>
......@@ -3979,3 +3980,164 @@ done:
*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;
}
......@@ -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);
/*
*
......
......@@ -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) \
......
......@@ -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");
......
......@@ -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);
......
......@@ -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 "</TABLE><BR>"
}
############################ 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 "</TABLE><BR>"
}
############################## 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
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment