mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-07-30 14:29:19 +00:00
780 lines
24 KiB
C
780 lines
24 KiB
C
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||
|
/*
|
||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||
|
|
||
|
#include "nss.h"
|
||
|
#include "pk11func.h"
|
||
|
#include "secder.h"
|
||
|
#include "sechash.h"
|
||
|
#include "ssl.h"
|
||
|
#include "sslproto.h"
|
||
|
#include "sslimpl.h"
|
||
|
#include "ssl3exthandle.h"
|
||
|
#include "tls13exthandle.h"
|
||
|
#include "tls13hkdf.h"
|
||
|
#include "tls13subcerts.h"
|
||
|
|
||
|
/* Parses the delegated credential (DC) from the raw extension |b| of length
|
||
|
* |length|. Memory for the DC is allocated and set to |*dcp|.
|
||
|
*
|
||
|
* It's the caller's responsibility to invoke |tls13_DestroyDelegatedCredential|
|
||
|
* when this data is no longer needed.
|
||
|
*/
|
||
|
SECStatus
|
||
|
tls13_ReadDelegatedCredential(PRUint8 *b, PRUint32 length,
|
||
|
sslDelegatedCredential **dcp)
|
||
|
{
|
||
|
sslDelegatedCredential *dc = NULL;
|
||
|
SECStatus rv;
|
||
|
PRUint64 n;
|
||
|
sslReadBuffer tmp;
|
||
|
sslReader rdr = SSL_READER(b, length);
|
||
|
|
||
|
PORT_Assert(!*dcp);
|
||
|
|
||
|
dc = PORT_ZNew(sslDelegatedCredential);
|
||
|
if (!dc) {
|
||
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* Read the valid_time field of DelegatedCredential.cred. */
|
||
|
rv = sslRead_ReadNumber(&rdr, 4, &n);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
dc->validTime = n;
|
||
|
|
||
|
/* Read the expected_cert_verify_algorithm field of
|
||
|
* DelegatedCredential.cred. */
|
||
|
rv = sslRead_ReadNumber(&rdr, 2, &n);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
dc->expectedCertVerifyAlg = n;
|
||
|
|
||
|
/* Read the ASN1_subjectPublicKeyInfo field of DelegatedCredential.cred. */
|
||
|
rv = sslRead_ReadVariable(&rdr, 3, &tmp);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
rv = SECITEM_MakeItem(NULL, &dc->derSpki, tmp.buf, tmp.len);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* Parse the DER-encoded SubjectPublicKeyInfo. */
|
||
|
dc->spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&dc->derSpki);
|
||
|
if (!dc->spki) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* Read the algorithm field of the DelegatedCredential. */
|
||
|
rv = sslRead_ReadNumber(&rdr, 2, &n);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
dc->alg = n;
|
||
|
|
||
|
/* Read the signature field of the DelegatedCredential. */
|
||
|
rv = sslRead_ReadVariable(&rdr, 2, &tmp);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
rv = SECITEM_MakeItem(NULL, &dc->signature, tmp.buf, tmp.len);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* There should be nothing left to read. */
|
||
|
if (SSL_READER_REMAINING(&rdr) > 0) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
*dcp = dc;
|
||
|
return SECSuccess;
|
||
|
|
||
|
loser:
|
||
|
tls13_DestroyDelegatedCredential(dc);
|
||
|
*dcp = NULL;
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
/* Frees |dc| from the heap. */
|
||
|
void
|
||
|
tls13_DestroyDelegatedCredential(sslDelegatedCredential *dc)
|
||
|
{
|
||
|
if (!dc) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
SECKEY_DestroySubjectPublicKeyInfo(dc->spki);
|
||
|
SECITEM_FreeItem(&dc->derSpki, PR_FALSE);
|
||
|
SECITEM_FreeItem(&dc->signature, PR_FALSE);
|
||
|
PORT_ZFree(dc, sizeof(sslDelegatedCredential));
|
||
|
}
|
||
|
|
||
|
/* Sets |*certVerifyAlg| to the expected_cert_verify_algorithm field from the
|
||
|
* serialized DC |in|. Returns SECSuccess upon success; SECFailure indicates a
|
||
|
* decoding failure or the input wasn't long enough.
|
||
|
*/
|
||
|
static SECStatus
|
||
|
tls13_GetExpectedCertVerifyAlg(SECItem in, SSLSignatureScheme *certVerifyAlg)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
PRUint64 n;
|
||
|
sslReader rdr = SSL_READER(in.data, in.len);
|
||
|
|
||
|
if (in.len < 6) { /* Buffer too short to contain the first two params. */
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
rv = sslRead_ReadNumber(&rdr, 4, &n);
|
||
|
if (rv != SECSuccess) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
rv = sslRead_ReadNumber(&rdr, 2, &n);
|
||
|
if (rv != SECSuccess) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
*certVerifyAlg = n;
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Returns PR_TRUE if the host is verifying the handshake with a DC. */
|
||
|
PRBool
|
||
|
tls13_IsVerifyingWithDelegatedCredential(const sslSocket *ss)
|
||
|
{
|
||
|
/* As of draft-ietf-subcerts-03, only the server may authenticate itself
|
||
|
* with a DC.
|
||
|
*/
|
||
|
if (ss->sec.isServer ||
|
||
|
!ss->opt.enableDelegatedCredentials ||
|
||
|
!ss->xtnData.peerDelegCred) {
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
/* Returns PR_TRUE if the host is signing the handshake with a DC. */
|
||
|
PRBool
|
||
|
tls13_IsSigningWithDelegatedCredential(const sslSocket *ss)
|
||
|
{
|
||
|
if (!ss->sec.isServer ||
|
||
|
!ss->xtnData.sendingDelegCredToPeer ||
|
||
|
!ss->xtnData.peerRequestedDelegCred) {
|
||
|
return PR_FALSE;
|
||
|
}
|
||
|
|
||
|
return PR_TRUE;
|
||
|
}
|
||
|
|
||
|
/* Commits to authenticating with a DC if all of the following conditions hold:
|
||
|
* - the negotiated protocol is TLS 1.3 or newer;
|
||
|
* - the selected certificate has a DC configured;
|
||
|
* - the peer has indicated support for this extension;
|
||
|
* - the peer has indicated support for the DC signature scheme; and
|
||
|
* - the host supports the DC signature scheme.
|
||
|
*
|
||
|
* It's the caller's responsibility to ensure that the version has been
|
||
|
* negotiated and the certificate has been selected.
|
||
|
*/
|
||
|
SECStatus
|
||
|
tls13_MaybeSetDelegatedCredential(sslSocket *ss)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
PRBool doesRsaPss;
|
||
|
SECKEYPrivateKey *priv;
|
||
|
SSLSignatureScheme scheme;
|
||
|
|
||
|
/* Assert that the host is the server (as of draft-ietf-subcerts-03, only
|
||
|
* the server may authenticate itself with a DC), the certificate has been
|
||
|
* chosen, TLS 1.3 or higher has been negotiated, and that the set of
|
||
|
* signature schemes supported by the client is known.
|
||
|
*/
|
||
|
PORT_Assert(ss->sec.isServer);
|
||
|
PORT_Assert(ss->sec.serverCert);
|
||
|
PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
|
||
|
PORT_Assert(ss->xtnData.sigSchemes);
|
||
|
|
||
|
/* Check that the peer has indicated support and that a DC has been
|
||
|
* configured for the selected certificate.
|
||
|
*/
|
||
|
if (!ss->xtnData.peerRequestedDelegCred ||
|
||
|
!ss->sec.serverCert->delegCred.len ||
|
||
|
!ss->sec.serverCert->delegCredKeyPair) {
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Check that the host and peer both support the signing algorithm used with
|
||
|
* the DC.
|
||
|
*/
|
||
|
rv = tls13_GetExpectedCertVerifyAlg(ss->sec.serverCert->delegCred,
|
||
|
&scheme);
|
||
|
if (rv != SECSuccess) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
priv = ss->sec.serverCert->delegCredKeyPair->privKey;
|
||
|
rv = ssl_PrivateKeySupportsRsaPss(priv, &doesRsaPss);
|
||
|
if (rv != SECSuccess) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
if (!ssl_SignatureSchemeEnabled(ss, scheme) ||
|
||
|
!ssl_CanUseSignatureScheme(scheme,
|
||
|
ss->xtnData.sigSchemes,
|
||
|
ss->xtnData.numSigSchemes,
|
||
|
PR_FALSE /* requireSha1 */,
|
||
|
doesRsaPss)) {
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Commit to sending a DC and set the handshake signature scheme to the
|
||
|
* indicated algorithm.
|
||
|
*/
|
||
|
ss->xtnData.sendingDelegCredToPeer = PR_TRUE;
|
||
|
ss->ssl3.hs.signatureScheme = scheme;
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Serializes the DC up to the signature. */
|
||
|
static SECStatus
|
||
|
tls13_AppendCredentialParams(sslBuffer *buf, sslDelegatedCredential *dc)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
rv = sslBuffer_AppendNumber(buf, dc->validTime, 4);
|
||
|
if (rv != SECSuccess) {
|
||
|
return SECFailure; /* Error set by caller. */
|
||
|
}
|
||
|
|
||
|
rv = sslBuffer_AppendNumber(buf, dc->expectedCertVerifyAlg, 2);
|
||
|
if (rv != SECSuccess) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
rv = sslBuffer_AppendVariable(buf, dc->derSpki.data, dc->derSpki.len, 3);
|
||
|
if (rv != SECSuccess) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
rv = sslBuffer_AppendNumber(buf, dc->alg, 2);
|
||
|
if (rv != SECSuccess) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Serializes the DC signature. */
|
||
|
static SECStatus
|
||
|
tls13_AppendCredentialSignature(sslBuffer *buf, sslDelegatedCredential *dc)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
rv = sslBuffer_AppendVariable(buf, dc->signature.data,
|
||
|
dc->signature.len, 2);
|
||
|
if (rv != SECSuccess) {
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Hashes the message used to sign/verify the DC. */
|
||
|
static SECStatus
|
||
|
tls13_HashCredentialSignatureMessage(SSL3Hashes *hash,
|
||
|
SSLSignatureScheme scheme,
|
||
|
const CERTCertificate *cert,
|
||
|
const sslBuffer *dcBuf)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
PK11Context *ctx = NULL;
|
||
|
unsigned int hashLen;
|
||
|
|
||
|
/* Set up hash context. */
|
||
|
hash->hashAlg = ssl_SignatureSchemeToHashType(scheme);
|
||
|
ctx = PK11_CreateDigestContext(ssl3_HashTypeToOID(hash->hashAlg));
|
||
|
if (!ctx) {
|
||
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
static const PRUint8 kCtxStrPadding[64] = {
|
||
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||
|
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||
|
};
|
||
|
|
||
|
static const PRUint8 kCtxStr[] = "TLS, server delegated credentials";
|
||
|
|
||
|
/* Hash the message signed by the peer. */
|
||
|
rv = SECSuccess;
|
||
|
rv |= PK11_DigestBegin(ctx);
|
||
|
rv |= PK11_DigestOp(ctx, kCtxStrPadding, sizeof kCtxStrPadding);
|
||
|
rv |= PK11_DigestOp(ctx, kCtxStr, 1 /* 0-byte */ + strlen((const char *)kCtxStr));
|
||
|
rv |= PK11_DigestOp(ctx, cert->derCert.data, cert->derCert.len);
|
||
|
rv |= PK11_DigestOp(ctx, dcBuf->buf, dcBuf->len);
|
||
|
rv |= PK11_DigestFinal(ctx, hash->u.raw, &hashLen, sizeof hash->u.raw);
|
||
|
if (rv != SECSuccess) {
|
||
|
PORT_SetError(SSL_ERROR_SHA_DIGEST_FAILURE);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
hash->len = hashLen;
|
||
|
if (ctx) {
|
||
|
PK11_DestroyContext(ctx, PR_TRUE);
|
||
|
}
|
||
|
return SECSuccess;
|
||
|
|
||
|
loser:
|
||
|
if (ctx) {
|
||
|
PK11_DestroyContext(ctx, PR_TRUE);
|
||
|
}
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
/* Verifies the DC signature. */
|
||
|
static SECStatus
|
||
|
tls13_VerifyCredentialSignature(sslSocket *ss, sslDelegatedCredential *dc)
|
||
|
{
|
||
|
SECStatus rv = SECSuccess;
|
||
|
SSL3Hashes hash;
|
||
|
sslBuffer dcBuf = SSL_BUFFER_EMPTY;
|
||
|
CERTCertificate *cert = ss->sec.peerCert;
|
||
|
SECKEYPublicKey *pubKey = NULL;
|
||
|
|
||
|
/* Serialize the DC parameters. */
|
||
|
rv = tls13_AppendCredentialParams(&dcBuf, dc);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser; /* Error set by caller. */
|
||
|
}
|
||
|
|
||
|
/* Hash the message that was signed by the delegator. */
|
||
|
rv = tls13_HashCredentialSignatureMessage(&hash, dc->alg, cert, &dcBuf);
|
||
|
if (rv != SECSuccess) {
|
||
|
FATAL_ERROR(ss, PORT_GetError(), internal_error);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
pubKey = SECKEY_ExtractPublicKey(&cert->subjectPublicKeyInfo);
|
||
|
if (pubKey == NULL) {
|
||
|
FATAL_ERROR(ss, SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE, internal_error);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* Verify the signature of the message. */
|
||
|
rv = ssl_VerifySignedHashesWithPubKey(ss, pubKey, dc->alg,
|
||
|
&hash, &dc->signature);
|
||
|
if (rv != SECSuccess) {
|
||
|
FATAL_ERROR(ss, SSL_ERROR_DC_BAD_SIGNATURE, illegal_parameter);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
SECKEY_DestroyPublicKey(pubKey);
|
||
|
sslBuffer_Clear(&dcBuf);
|
||
|
return SECSuccess;
|
||
|
|
||
|
loser:
|
||
|
SECKEY_DestroyPublicKey(pubKey);
|
||
|
sslBuffer_Clear(&dcBuf);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
/* Checks that the peer's end-entity certificate has the correct key usage. */
|
||
|
static SECStatus
|
||
|
tls13_CheckCertDelegationUsage(sslSocket *ss)
|
||
|
{
|
||
|
int i;
|
||
|
PRBool found;
|
||
|
CERTCertExtension *ext;
|
||
|
SECItem delegUsageOid = { siBuffer, NULL, 0 };
|
||
|
const CERTCertificate *cert = ss->sec.peerCert;
|
||
|
|
||
|
/* 1.3.6.1.4.1.44363.44, as defined in draft-ietf-tls-subcerts. */
|
||
|
static unsigned char kDelegationUsageOid[] = {
|
||
|
0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xda, 0x4b, 0x2c,
|
||
|
};
|
||
|
|
||
|
delegUsageOid.data = kDelegationUsageOid;
|
||
|
delegUsageOid.len = sizeof kDelegationUsageOid;
|
||
|
|
||
|
/* The certificate must have the delegationUsage extension that authorizes
|
||
|
* it to negotiate delegated credentials.
|
||
|
*/
|
||
|
found = PR_FALSE;
|
||
|
for (i = 0; cert->extensions[i] != NULL; i++) {
|
||
|
ext = cert->extensions[i];
|
||
|
if (SECITEM_CompareItem(&ext->id, &delegUsageOid) == SECEqual) {
|
||
|
found = PR_TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* The certificate must also have the digitalSignature keyUsage set. */
|
||
|
if (!found ||
|
||
|
!cert->keyUsagePresent ||
|
||
|
!(cert->keyUsage & KU_DIGITAL_SIGNATURE)) {
|
||
|
FATAL_ERROR(ss, SSL_ERROR_DC_INVALID_KEY_USAGE, illegal_parameter);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
static SECStatus
|
||
|
tls13_CheckCredentialExpiration(sslSocket *ss, sslDelegatedCredential *dc)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
PRTime start, end /* microseconds */;
|
||
|
CERTCertificate *cert = ss->sec.peerCert;
|
||
|
|
||
|
rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore);
|
||
|
if (rv != SECSuccess) {
|
||
|
FATAL_ERROR(ss, PORT_GetError(), internal_error);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
end = start + ((PRTime)dc->validTime * PR_USEC_PER_SEC);
|
||
|
if (ssl_Time(ss) > end) {
|
||
|
FATAL_ERROR(ss, SSL_ERROR_DC_EXPIRED, illegal_parameter);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
return SECSuccess;
|
||
|
}
|
||
|
|
||
|
/* Returns SECSucces if |dc| is a DC for the current handshake; otherwise it
|
||
|
* returns SECFailure. A valid DC meets three requirements: (1) the signature
|
||
|
* was produced by the peer's end-entity certificate, (2) the end-entity
|
||
|
* certificate must have the correct key usage, and (3) the DC must not be
|
||
|
* expired.
|
||
|
*
|
||
|
* This function calls FATAL_ERROR() when an error occurs.
|
||
|
*/
|
||
|
SECStatus
|
||
|
tls13_VerifyDelegatedCredential(sslSocket *ss,
|
||
|
sslDelegatedCredential *dc)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
PRTime start;
|
||
|
PRExplodedTime end;
|
||
|
CERTCertificate *cert = ss->sec.peerCert;
|
||
|
char endStr[256];
|
||
|
|
||
|
rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore);
|
||
|
if (rv != SECSuccess) {
|
||
|
FATAL_ERROR(ss, PORT_GetError(), internal_error);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
PR_ExplodeTime(start + (dc->validTime * PR_USEC_PER_SEC),
|
||
|
PR_GMTParameters, &end);
|
||
|
if (PR_FormatTime(endStr, sizeof(endStr), "%a %b %d %H:%M:%S %Y", &end)) {
|
||
|
SSL_TRC(20, ("%d: TLS13[%d]: Received delegated credential (expires %s)",
|
||
|
SSL_GETPID(), ss->fd, endStr));
|
||
|
} else {
|
||
|
SSL_TRC(20, ("%d: TLS13[%d]: Received delegated credential",
|
||
|
SSL_GETPID(), ss->fd));
|
||
|
}
|
||
|
|
||
|
rv = SECSuccess;
|
||
|
rv |= tls13_VerifyCredentialSignature(ss, dc);
|
||
|
rv |= tls13_CheckCertDelegationUsage(ss);
|
||
|
rv |= tls13_CheckCredentialExpiration(ss, dc);
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
static CERTSubjectPublicKeyInfo *
|
||
|
tls13_MakePssSpki(const SECKEYPublicKey *pub, SECOidTag hashOid)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
||
|
if (!arena) {
|
||
|
goto loser; /* Code already set. */
|
||
|
}
|
||
|
CERTSubjectPublicKeyInfo *spki = PORT_ArenaZNew(arena, CERTSubjectPublicKeyInfo);
|
||
|
if (!spki) {
|
||
|
goto loser; /* Code already set. */
|
||
|
}
|
||
|
spki->arena = arena;
|
||
|
|
||
|
SECKEYRSAPSSParams params = { 0 };
|
||
|
params.hashAlg = PORT_ArenaZNew(arena, SECAlgorithmID);
|
||
|
rv = SECOID_SetAlgorithmID(arena, params.hashAlg, hashOid, NULL);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser; /* Code already set. */
|
||
|
}
|
||
|
|
||
|
/* Set the mask hash algorithm too, which is an argument to
|
||
|
* a SEC_OID_PKCS1_MGF1 value. */
|
||
|
SECAlgorithmID maskHashAlg;
|
||
|
memset(&maskHashAlg, 0, sizeof(maskHashAlg));
|
||
|
rv = SECOID_SetAlgorithmID(arena, &maskHashAlg, hashOid, NULL);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser; /* Code already set. */
|
||
|
}
|
||
|
SECItem *maskHashAlgItem =
|
||
|
SEC_ASN1EncodeItem(arena, NULL, &maskHashAlg,
|
||
|
SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
|
||
|
if (!maskHashAlgItem) {
|
||
|
/* Probably OOM, but not certain. */
|
||
|
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
params.maskAlg = PORT_ArenaZNew(arena, SECAlgorithmID);
|
||
|
rv = SECOID_SetAlgorithmID(arena, params.maskAlg, SEC_OID_PKCS1_MGF1,
|
||
|
maskHashAlgItem);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser; /* Code already set. */
|
||
|
}
|
||
|
|
||
|
/* Always include saltLength: all hashes are larger than 20. */
|
||
|
unsigned int saltLength = HASH_ResultLenByOidTag(hashOid);
|
||
|
PORT_Assert(saltLength > 20);
|
||
|
if (!SEC_ASN1EncodeInteger(arena, ¶ms.saltLength, saltLength)) {
|
||
|
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||
|
goto loser;
|
||
|
}
|
||
|
/* Omit the trailerField always. */
|
||
|
|
||
|
SECItem *algorithmItem =
|
||
|
SEC_ASN1EncodeItem(arena, NULL, ¶ms,
|
||
|
SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate));
|
||
|
if (!algorithmItem) {
|
||
|
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||
|
goto loser; /* Code already set. */
|
||
|
}
|
||
|
rv = SECOID_SetAlgorithmID(arena, &spki->algorithm,
|
||
|
SEC_OID_PKCS1_RSA_PSS_SIGNATURE, algorithmItem);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser; /* Code already set. */
|
||
|
}
|
||
|
|
||
|
PORT_Assert(pub->u.rsa.modulus.type == siUnsignedInteger);
|
||
|
PORT_Assert(pub->u.rsa.publicExponent.type == siUnsignedInteger);
|
||
|
SECItem *pubItem = SEC_ASN1EncodeItem(arena, &spki->subjectPublicKey, pub,
|
||
|
SEC_ASN1_GET(SECKEY_RSAPublicKeyTemplate));
|
||
|
if (!pubItem) {
|
||
|
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
|
||
|
goto loser;
|
||
|
}
|
||
|
spki->subjectPublicKey.len *= 8; /* Key length is in bits. */
|
||
|
return spki;
|
||
|
|
||
|
loser:
|
||
|
PORT_FreeArena(arena, PR_FALSE);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static CERTSubjectPublicKeyInfo *
|
||
|
tls13_MakeDcSpki(const SECKEYPublicKey *dcPub, SSLSignatureScheme dcCertVerifyAlg)
|
||
|
{
|
||
|
switch (SECKEY_GetPublicKeyType(dcPub)) {
|
||
|
case rsaKey: {
|
||
|
SECOidTag hashOid;
|
||
|
switch (dcCertVerifyAlg) {
|
||
|
/* Though we might prefer to use a pure PSS SPKI here, we can't
|
||
|
* because we have to choose based on client preferences. And
|
||
|
* not all clients advertise the pss_pss schemes. So use the
|
||
|
* default SPKI construction for an RSAE SPKI. */
|
||
|
case ssl_sig_rsa_pss_rsae_sha256:
|
||
|
case ssl_sig_rsa_pss_rsae_sha384:
|
||
|
case ssl_sig_rsa_pss_rsae_sha512:
|
||
|
return SECKEY_CreateSubjectPublicKeyInfo(dcPub);
|
||
|
|
||
|
case ssl_sig_rsa_pss_pss_sha256:
|
||
|
hashOid = SEC_OID_SHA256;
|
||
|
break;
|
||
|
case ssl_sig_rsa_pss_pss_sha384:
|
||
|
hashOid = SEC_OID_SHA384;
|
||
|
break;
|
||
|
case ssl_sig_rsa_pss_pss_sha512:
|
||
|
hashOid = SEC_OID_SHA512;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
|
||
|
return NULL;
|
||
|
}
|
||
|
return tls13_MakePssSpki(dcPub, hashOid);
|
||
|
}
|
||
|
|
||
|
case ecKey: {
|
||
|
const sslNamedGroupDef *group = ssl_ECPubKey2NamedGroup(dcPub);
|
||
|
if (!group) {
|
||
|
PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
|
||
|
return NULL;
|
||
|
}
|
||
|
SSLSignatureScheme keyScheme;
|
||
|
switch (group->name) {
|
||
|
case ssl_grp_ec_secp256r1:
|
||
|
keyScheme = ssl_sig_ecdsa_secp256r1_sha256;
|
||
|
break;
|
||
|
case ssl_grp_ec_secp384r1:
|
||
|
keyScheme = ssl_sig_ecdsa_secp384r1_sha384;
|
||
|
break;
|
||
|
case ssl_grp_ec_secp521r1:
|
||
|
keyScheme = ssl_sig_ecdsa_secp521r1_sha512;
|
||
|
break;
|
||
|
default:
|
||
|
PORT_SetError(SEC_ERROR_INVALID_KEY);
|
||
|
return NULL;
|
||
|
}
|
||
|
if (keyScheme != dcCertVerifyAlg) {
|
||
|
PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
|
||
|
return NULL;
|
||
|
}
|
||
|
return SECKEY_CreateSubjectPublicKeyInfo(dcPub);
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
PORT_SetError(SEC_ERROR_INVALID_KEY);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Returns a serialized DC with the given parameters.
|
||
|
*
|
||
|
* Note that this function is meant primarily for testing. In particular, it
|
||
|
* DOES NOT verify any of the following:
|
||
|
* - |certPriv| is the private key corresponding to |cert|;
|
||
|
* - that |checkCertKeyUsage(cert) == SECSuccess|;
|
||
|
* - |dcValidFor| is less than 7 days (the maximum permitted by the spec); or
|
||
|
* - validTime doesn't overflow a PRUint32.
|
||
|
*
|
||
|
* These conditions are things we want to test for, which is why we allow them
|
||
|
* here. A real API for creating DCs would want to explicitly check ALL of these
|
||
|
* conditions are met.
|
||
|
*/
|
||
|
SECStatus
|
||
|
SSLExp_DelegateCredential(const CERTCertificate *cert,
|
||
|
const SECKEYPrivateKey *certPriv,
|
||
|
const SECKEYPublicKey *dcPub,
|
||
|
SSLSignatureScheme dcCertVerifyAlg,
|
||
|
PRUint32 dcValidFor,
|
||
|
PRTime now,
|
||
|
SECItem *out)
|
||
|
{
|
||
|
SECStatus rv;
|
||
|
SSL3Hashes hash;
|
||
|
CERTSubjectPublicKeyInfo *spki = NULL;
|
||
|
SECKEYPrivateKey *tmpPriv = NULL;
|
||
|
sslDelegatedCredential *dc = NULL;
|
||
|
sslBuffer dcBuf = SSL_BUFFER_EMPTY;
|
||
|
|
||
|
if (!cert || !certPriv || !dcPub || !out) {
|
||
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||
|
return SECFailure;
|
||
|
}
|
||
|
|
||
|
dc = PORT_ZNew(sslDelegatedCredential);
|
||
|
if (!dc) {
|
||
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* Serialize the DC parameters. */
|
||
|
PRTime start;
|
||
|
rv = DER_DecodeTimeChoice(&start, &cert->validity.notBefore);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
dc->validTime = ((now - start) / PR_USEC_PER_SEC) + dcValidFor;
|
||
|
|
||
|
/* Building the SPKI also validates |dcCertVerifyAlg|. */
|
||
|
spki = tls13_MakeDcSpki(dcPub, dcCertVerifyAlg);
|
||
|
if (!spki) {
|
||
|
goto loser;
|
||
|
}
|
||
|
dc->expectedCertVerifyAlg = dcCertVerifyAlg;
|
||
|
|
||
|
SECItem *spkiDer =
|
||
|
SEC_ASN1EncodeItem(NULL /*arena*/, &dc->derSpki, spki,
|
||
|
SEC_ASN1_GET(CERT_SubjectPublicKeyInfoTemplate));
|
||
|
if (!spkiDer) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo,
|
||
|
PR_TRUE /* isTls13 */, &dc->alg);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
if (dc->alg == ssl_sig_none) {
|
||
|
SECOidTag spkiOid = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm);
|
||
|
/* If the Cert SPKI contained an AlgorithmIdentifier of "rsaEncryption", set a
|
||
|
* default rsa_pss_rsae_sha256 scheme. */
|
||
|
if (spkiOid == SEC_OID_PKCS1_RSA_ENCRYPTION) {
|
||
|
SSLSignatureScheme scheme = ssl_sig_rsa_pss_rsae_sha256;
|
||
|
if (ssl_SignatureSchemeValid(scheme, spkiOid, PR_TRUE /* isTls13 */)) {
|
||
|
dc->alg = scheme;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
PORT_Assert(dc->alg != ssl_sig_none);
|
||
|
|
||
|
rv = tls13_AppendCredentialParams(&dcBuf, dc);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* Hash signature message. */
|
||
|
rv = tls13_HashCredentialSignatureMessage(&hash, dc->alg, cert, &dcBuf);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* Sign the hash with the delegation key.
|
||
|
*
|
||
|
* The PK11 API discards const qualifiers, so we have to make a copy of
|
||
|
* |certPriv| and pass the copy to |ssl3_SignHashesWithPrivKey|.
|
||
|
*/
|
||
|
tmpPriv = SECKEY_CopyPrivateKey(certPriv);
|
||
|
rv = ssl3_SignHashesWithPrivKey(&hash, tmpPriv, dc->alg,
|
||
|
PR_TRUE /* isTls */, &dc->signature);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* Serialize the DC signature. */
|
||
|
rv = tls13_AppendCredentialSignature(&dcBuf, dc);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
/* Copy the serialized DC to |out|. */
|
||
|
rv = SECITEM_MakeItem(NULL, out, dcBuf.buf, dcBuf.len);
|
||
|
if (rv != SECSuccess) {
|
||
|
goto loser;
|
||
|
}
|
||
|
|
||
|
PRINT_BUF(20, (NULL, "delegated credential", dcBuf.buf, dcBuf.len));
|
||
|
|
||
|
SECKEY_DestroySubjectPublicKeyInfo(spki);
|
||
|
SECKEY_DestroyPrivateKey(tmpPriv);
|
||
|
tls13_DestroyDelegatedCredential(dc);
|
||
|
sslBuffer_Clear(&dcBuf);
|
||
|
return SECSuccess;
|
||
|
|
||
|
loser:
|
||
|
SECKEY_DestroySubjectPublicKeyInfo(spki);
|
||
|
SECKEY_DestroyPrivateKey(tmpPriv);
|
||
|
tls13_DestroyDelegatedCredential(dc);
|
||
|
sslBuffer_Clear(&dcBuf);
|
||
|
return SECFailure;
|
||
|
}
|