diff options
author | Kevin Jacobs <kjacobs@mozilla.com> | 2020-06-10 16:18:39 +0000 |
---|---|---|
committer | Kevin Jacobs <kjacobs@mozilla.com> | 2020-06-10 16:18:39 +0000 |
commit | 6bd9c7b86e88e13cac6086c62e93ffaade158751 (patch) | |
tree | b8eda0b60ec010e2d293ca0e3a25e033bbc910ea | |
parent | 486400ca1c16833569ae2aa7f41d03b5471c947d (diff) | |
download | nss-hg-6bd9c7b86e88e13cac6086c62e93ffaade158751.tar.gz |
Bug 1603042 - Support external PSKs in tstclnt/selfserv. r=jcj
This patch adds support for TLS 1.3 external PSKs in tstclnt and selfserv with the `-z` option.
Command examples:
- `selfserv -D -p 4443 -d . -n localhost.localdomain -w nss -V tls1.3: -H 1 -z 0xAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD[:label] -m`
- `tstclnt -h 127.0.0.1 -p 4443 -z 0xAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD[:label] -d . -w nss`
For OpenSSL interop:
- `openssl s_server -nocert -port 4433 -psk AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD [-psk_identity label]`
Note: If the optional label is omitted, both NSS tools and OpenSSL default to "Client_identity".
Differential Revision: https://phabricator.services.mozilla.com/D75836
-rw-r--r-- | cmd/lib/basicutil.c | 5 | ||||
-rw-r--r-- | cmd/lib/secutil.c | 54 | ||||
-rw-r--r-- | cmd/lib/secutil.h | 2 | ||||
-rw-r--r-- | cmd/selfserv/selfserv.c | 60 | ||||
-rw-r--r-- | cmd/tstclnt/tstclnt.c | 60 | ||||
-rw-r--r-- | lib/ssl/tls13psk.c | 4 |
6 files changed, 171 insertions, 14 deletions
diff --git a/cmd/lib/basicutil.c b/cmd/lib/basicutil.c index 345e970ad..476475d90 100644 --- a/cmd/lib/basicutil.c +++ b/cmd/lib/basicutil.c @@ -741,7 +741,6 @@ SECU_HexString2SECItem(PLArenaPool *arena, SECItem *item, const char *str) int byteval = 0; int tmp = PORT_Strlen(str); - PORT_Assert(arena); PORT_Assert(item); if ((tmp % 2) != 0) { @@ -762,7 +761,9 @@ SECU_HexString2SECItem(PLArenaPool *arena, SECItem *item, const char *str) } else if ((str[i] >= 'A') && (str[i] <= 'F')) { tmp = str[i] - 'A' + 10; } else { - /* item is in arena and gets freed by the caller */ + if (!arena) { + SECITEM_FreeItem(item, PR_FALSE); + } return NULL; } diff --git a/cmd/lib/secutil.c b/cmd/lib/secutil.c index b05dc7938..605139925 100644 --- a/cmd/lib/secutil.c +++ b/cmd/lib/secutil.c @@ -4159,3 +4159,57 @@ exportKeyingMaterials(PRFileDesc *fd, return SECSuccess; } + +SECStatus +readPSK(const char *arg, SECItem *psk, SECItem *label) +{ + SECStatus rv = SECFailure; + char *str = PORT_Strdup(arg); + if (!str) { + goto cleanup; + } + + char *pskBytes = strtok(str, ":"); + if (!pskBytes) { + goto cleanup; + } + if (PORT_Strncasecmp(pskBytes, "0x", 2) != 0) { + goto cleanup; + } + + psk = SECU_HexString2SECItem(NULL, psk, &pskBytes[2]); + if (!psk || !psk->data || psk->len != strlen(&str[2]) / 2) { + goto cleanup; + } + + SECItem labelItem = { siBuffer, NULL, 0 }; + char *inLabel = strtok(NULL, ":"); + if (inLabel) { + labelItem.data = (unsigned char *)PORT_Strdup(inLabel); + if (!labelItem.data) { + goto cleanup; + } + labelItem.len = strlen(inLabel); + + if (PORT_Strncasecmp(inLabel, "0x", 2) == 0) { + rv = SECU_SECItemHexStringToBinary(&labelItem); + if (rv != SECSuccess) { + SECITEM_FreeItem(&labelItem, PR_FALSE); + goto cleanup; + } + } + rv = SECSuccess; + } else { + const PRUint8 defaultLabel[] = { 'C', 'l', 'i', 'e', 'n', 't', '_', + 'i', 'd', 'e', 'n', 't', 'i', 't', 'y' }; + rv = SECITEM_MakeItem(NULL, &labelItem, defaultLabel, + sizeof(defaultLabel)); + } + if (rv == SECSuccess) { + *label = labelItem; + } + +cleanup: + PORT_Free(str); + return rv; +} diff --git a/cmd/lib/secutil.h b/cmd/lib/secutil.h index c6da961e7..0bdfa9508 100644 --- a/cmd/lib/secutil.h +++ b/cmd/lib/secutil.h @@ -424,6 +424,8 @@ SECStatus exportKeyingMaterials(PRFileDesc *fd, const secuExporter *exporters, unsigned int exporterCount); +SECStatus readPSK(const char *arg, SECItem *psk, SECItem *label); + /* * * Error messaging diff --git a/cmd/selfserv/selfserv.c b/cmd/selfserv/selfserv.c index 0f500d65c..1584d7ee0 100644 --- a/cmd/selfserv/selfserv.c +++ b/cmd/selfserv/selfserv.c @@ -138,6 +138,8 @@ static SECItem bigBuf; static int configureDHE = -1; /* -1: don't configure, 0 disable, >=1 enable*/ static int configureReuseECDHE = -1; /* -1: don't configure, 0 refresh, >=1 reuse*/ static int configureWeakDHE = -1; /* -1: don't configure, 0 disable, >=1 enable*/ +SECItem psk = { siBuffer, NULL, 0 }; +SECItem pskLabel = { siBuffer, NULL, 0 }; static PRThread *acceptorThread; @@ -167,7 +169,7 @@ PrintUsageHeader(const char *progName) " [ T <good|revoked|unknown|badsig|corrupted|none|ocsp>] [-A ca]\n" " [-C SSLCacheEntries] [-S dsa_nickname] [-Q]\n" " [-I groups] [-J signatureschemes] [-e ec_nickname]\n" - " -U [0|1] -H [0|1|2] -W [0|1]\n" + " -U [0|1] -H [0|1|2] -W [0|1] [-z externalPsk]\n" "\n", progName); } @@ -241,7 +243,11 @@ PrintParameterUsage() " 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", + " is a decimal integer.\n" + "-z Configure a TLS 1.3 External PSK with the given hex string for a key.\n" + " To specify a label, use ':' as a delimiter. For example:\n" + " 0xAAAABBBBCCCCDDDD:mylabel. Otherwise, the default label of\n" + " 'Client_identity' will be used.\n", stderr); } @@ -1841,6 +1847,32 @@ handshakeCallback(PRFileDesc *fd, void *client_data) } } +static SECStatus +importPsk(PRFileDesc *model_sock) +{ + SECU_PrintAsHex(stdout, &psk, "Using External PSK", 0); + PK11SlotInfo *slot = NULL; + PK11SymKey *symKey = NULL; + slot = PK11_GetInternalSlot(); + if (!slot) { + errWarn("PK11_GetInternalSlot failed"); + return SECFailure; + } + symKey = PK11_ImportSymKey(slot, CKM_HKDF_KEY_GEN, PK11_OriginUnwrap, + CKA_DERIVE, &psk, NULL); + PK11_FreeSlot(slot); + if (!symKey) { + errWarn("PK11_ImportSymKey failed\n"); + return SECFailure; + } + + SECStatus rv = SSL_AddExternalPsk(model_sock, symKey, + (const PRUint8 *)pskLabel.data, + pskLabel.len, ssl_hash_sha256); + PK11_FreeSymKey(symKey); + return rv; +} + void server_main( PRFileDesc *listen_sock, @@ -2050,6 +2082,13 @@ server_main( } } + if (psk.data) { + rv = importPsk(model_sock); + if (rv != SECSuccess) { + errExit("importPsk failed"); + } + } + if (MakeCertOK) SSL_BadCertHook(model_sock, myBadCertHandler, NULL); @@ -2291,10 +2330,9 @@ 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', 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. */ + ** in 3.28, please leave some time before resuing those. */ 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:x: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:yz:"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { ++optionsFound; switch (optstate->option) { @@ -2516,6 +2554,16 @@ main(int argc, char **argv) zeroRTT = PR_TRUE; break; + case 'z': + rv = readPSK(optstate->value, &psk, &pskLabel); + if (rv != SECSuccess) { + PL_DestroyOptState(optstate); + fprintf(stderr, "Bad PSK specified.\n"); + Usage(progName); + exit(1); + } + break; + case 'Q': enableALPN = PR_TRUE; break; @@ -2871,6 +2919,8 @@ cleanup: if (antiReplay) { SSL_ReleaseAntiReplayContext(antiReplay); } + SECITEM_ZfreeItem(&psk, PR_FALSE); + SECITEM_ZfreeItem(&pskLabel, PR_FALSE); if (NSS_Shutdown() != SECSuccess) { SECU_PrintError(progName, "NSS_Shutdown"); if (loggerThread) { diff --git a/cmd/tstclnt/tstclnt.c b/cmd/tstclnt/tstclnt.c index 6fa154106..c37df118e 100644 --- a/cmd/tstclnt/tstclnt.c +++ b/cmd/tstclnt/tstclnt.c @@ -109,6 +109,8 @@ SSLNamedGroup *enabledGroups = NULL; unsigned int enabledGroupsCount = 0; const SSLSignatureScheme *enabledSigSchemes = NULL; unsigned int enabledSigSchemeCount = 0; +SECItem psk = { siBuffer, NULL, 0 }; +SECItem pskLabel = { siBuffer, NULL, 0 }; const char * signatureSchemeName(SSLSignatureScheme scheme) @@ -229,7 +231,7 @@ PrintUsageHeader() " [-r N] [-w passwd] [-W pwfile] [-q [-t seconds]]\n" " [-I groups] [-J signatureschemes]\n" " [-A requestfile] [-L totalconnections] [-P {client,server}]\n" - " [-N encryptedSniKeys] [-Q]\n" + " [-N encryptedSniKeys] [-Q] [-z externalPsk]\n" "\n", progName); } @@ -325,6 +327,12 @@ PrintParameterUsage() "%-20s a hex string if it is preceded by \"0x\"; OUTPUT-LENGTH\n" "%-20s is a decimal integer.\n", "-x", "", "", "", "", ""); + fprintf(stderr, + "%-20s Configure a TLS 1.3 External PSK with the given hex string for a key\n" + "%-20s To specify a label, use ':' as a delimiter. For example\n" + "%-20s 0xAAAABBBBCCCCDDDD:mylabel. Otherwise, the default label of\n" + "%-20s 'Client_identity' will be used.\n", + "-z externalPsk", "", "", ""); } static void @@ -1230,6 +1238,31 @@ connectToServer(PRFileDesc *s, PRPollDesc *pollset) return SECSuccess; } +static SECStatus +importPsk(PRFileDesc *s) +{ + SECU_PrintAsHex(stdout, &psk, "Using External PSK", 0); + PK11SlotInfo *slot = NULL; + PK11SymKey *symKey = NULL; + slot = PK11_GetInternalSlot(); + if (!slot) { + SECU_PrintError(progName, "PK11_GetInternalSlot failed"); + return SECFailure; + } + symKey = PK11_ImportSymKey(slot, CKM_HKDF_KEY_GEN, PK11_OriginUnwrap, + CKA_DERIVE, &psk, NULL); + PK11_FreeSlot(slot); + if (!symKey) { + SECU_PrintError(progName, "PK11_ImportSymKey failed"); + return SECFailure; + } + + SECStatus rv = SSL_AddExternalPsk(s, symKey, (const PRUint8 *)pskLabel.data, + pskLabel.len, ssl_hash_sha256); + PK11_FreeSymKey(symKey); + return rv; +} + static int run() { @@ -1498,6 +1531,15 @@ run() } } + if (psk.data) { + rv = importPsk(s); + if (rv != SECSuccess) { + SECU_PrintError(progName, "importPsk failed"); + error = 1; + goto done; + } + } + serverCertAuth.dbHandle = CERT_GetDefaultCertDB(); SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth); @@ -1752,11 +1794,8 @@ main(int argc, char **argv) } } - /* Note: 'z' was removed in 3.39 - * 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:x:"); + "46A:BCDEFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:x:z:"); while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case '?': @@ -2015,6 +2054,15 @@ main(int argc, char **argv) Usage(); } break; + + case 'z': + rv = readPSK(optstate->value, &psk, &pskLabel); + if (rv != SECSuccess) { + PL_DestroyOptState(optstate); + fprintf(stderr, "Bad PSK specified.\n"); + Usage(); + } + break; } } PL_DestroyOptState(optstate); @@ -2210,6 +2258,8 @@ done: PORT_Free(host); PORT_Free(zeroRttData); PORT_Free(encryptedSNIKeys); + SECITEM_ZfreeItem(&psk, PR_FALSE); + SECITEM_ZfreeItem(&pskLabel, PR_FALSE); if (enabledGroups) { PORT_Free(enabledGroups); diff --git a/lib/ssl/tls13psk.c b/lib/ssl/tls13psk.c index cc1d14106..7343c5a6f 100644 --- a/lib/ssl/tls13psk.c +++ b/lib/ssl/tls13psk.c @@ -130,7 +130,7 @@ tls13_CopyPsk(sslPsk *opsk) * are derived during the handshake. */ PORT_Assert(opsk->type == ssl_psk_external); PORT_Assert(opsk->key); - PORT_Assert(opsk->binderKey); + PORT_Assert(!opsk->binderKey); psk->hash = opsk->hash; psk->type = opsk->type; psk->key = opsk->key ? PK11_ReferenceSymKey(opsk->key) : NULL; @@ -216,4 +216,4 @@ tls13_ResetHandshakePsks(sslSocket *ss, PRCList *list) PR_APPEND_LINK(&epsk->link, list); } return SECSuccess; -}
\ No newline at end of file +} |