Skip to content

Commit

Permalink
Bug 1603042 - Support external PSKs in tstclnt/selfserv. r=jcj
Browse files Browse the repository at this point in the history
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

--HG--
extra : moz-landing-system : lando
  • Loading branch information
Kevin Jacobs committed Jun 10, 2020
1 parent 389f377 commit 5b67270
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 14 deletions.
5 changes: 3 additions & 2 deletions cmd/lib/basicutil.c
Expand Up @@ -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) {
Expand All @@ -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;
}

Expand Down
54 changes: 54 additions & 0 deletions cmd/lib/secutil.c
Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions cmd/lib/secutil.h
Expand Up @@ -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
Expand Down
60 changes: 55 additions & 5 deletions cmd/selfserv/selfserv.c
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2871,6 +2919,8 @@ main(int argc, char **argv)
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) {
Expand Down
60 changes: 55 additions & 5 deletions cmd/tstclnt/tstclnt.c
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 '?':
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -2210,6 +2258,8 @@ main(int argc, char **argv)
PORT_Free(host);
PORT_Free(zeroRttData);
PORT_Free(encryptedSNIKeys);
SECITEM_ZfreeItem(&psk, PR_FALSE);
SECITEM_ZfreeItem(&pskLabel, PR_FALSE);

if (enabledGroups) {
PORT_Free(enabledGroups);
Expand Down
4 changes: 2 additions & 2 deletions lib/ssl/tls13psk.c
Expand Up @@ -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;
Expand Down Expand Up @@ -216,4 +216,4 @@ tls13_ResetHandshakePsks(sslSocket *ss, PRCList *list)
PR_APPEND_LINK(&epsk->link, list);
}
return SECSuccess;
}
}

0 comments on commit 5b67270

Please sign in to comment.