nss: update nss to hg rev e5e10a46b9ad with vc2013 hackfix

This commit is contained in:
Roy Tam 2019-04-06 10:14:46 +08:00
commit dcdc5d70e0
133 changed files with 8084 additions and 2038 deletions

View file

@ -0,0 +1,47 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 "gtest/gtest.h"
#include "nss.h"
#include "secerr.h"
#include "pk11pub.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {
class CertTest : public ::testing::Test {};
// Tests CERT_GetCertificateDer for the certs we have.
TEST_F(CertTest, GetCertDer) {
// Listing all the certs should get us the default trust anchors.
ScopedCERTCertList certs(PK11_ListCerts(PK11CertListAll, nullptr));
ASSERT_FALSE(PR_CLIST_IS_EMPTY(&certs->list));
for (PRCList* cursor = PR_NEXT_LINK(&certs->list); cursor != &certs->list;
cursor = PR_NEXT_LINK(cursor)) {
CERTCertListNode* node = (CERTCertListNode*)cursor;
SECItem der;
ASSERT_EQ(SECSuccess, CERT_GetCertificateDer(node->cert, &der));
ASSERT_EQ(0, SECITEM_CompareItem(&der, &node->cert->derCert));
}
}
TEST_F(CertTest, GetCertDerBad) {
EXPECT_EQ(SECFailure, CERT_GetCertificateDer(nullptr, nullptr));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
ScopedCERTCertList certs(PK11_ListCerts(PK11CertListAll, nullptr));
ASSERT_FALSE(PR_CLIST_IS_EMPTY(&certs->list));
CERTCertListNode* node = (CERTCertListNode*)PR_NEXT_LINK(&certs->list);
EXPECT_EQ(SECFailure, CERT_GetCertificateDer(node->cert, nullptr));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
SECItem der;
EXPECT_EQ(SECFailure, CERT_GetCertificateDer(nullptr, &der));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
}

View file

@ -12,6 +12,8 @@
'type': 'executable',
'sources': [
'alg1485_unittest.cc',
'cert_unittest.cc',
'decode_certs_unittest.cc',
'<(DEPTH)/gtests/common/gtests.cc'
],
'dependencies': [
@ -20,6 +22,7 @@
'<(DEPTH)/lib/util/util.gyp:nssutil3',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl3',
'<(DEPTH)/lib/nss/nss.gyp:nss3',
'<(DEPTH)/lib/smime/smime.gyp:smime3',
]
}
],

View file

@ -0,0 +1,28 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 "gtest/gtest.h"
#include "cert.h"
#include "prerror.h"
#include "secerr.h"
class DecodeCertsTest : public ::testing::Test {};
TEST_F(DecodeCertsTest, EmptyCertPackage) {
// This represents a PKCS#7 ContentInfo with a contentType of
// '2.16.840.1.113730.2.5' (Netscape data-type cert-sequence) and a content
// consisting of an empty SEQUENCE. This is valid ASN.1, but it contains no
// certificates, so CERT_DecodeCertFromPackage should just return a null
// pointer.
unsigned char emptyCertPackage[] = {0x30, 0x0f, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x86, 0xf8, 0x42, 0x02,
0x05, 0xa0, 0x02, 0x30, 0x00};
EXPECT_EQ(nullptr, CERT_DecodeCertFromPackage(
reinterpret_cast<char*>(emptyCertPackage),
sizeof(emptyCertPackage)));
EXPECT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
}

View file

@ -8,6 +8,8 @@ MODULE = nss
CPPSRCS = \
alg1485_unittest.cc \
cert_unittest.cc \
decode_certs_unittest.cc \
$(NULL)
INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \

View file

@ -10,7 +10,23 @@
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
const char *workdir = "";
uint32_t flags = NSS_INIT_READONLY;
for (int i = 0; i < argc; i++) {
if (!strcmp(argv[i], "-d")) {
if (i + 1 >= argc) {
PR_fprintf(PR_STDERR, "Usage: %s [-d <dir> [-w]]\n", argv[0]);
exit(2);
}
workdir = argv[i + 1];
i++;
} else if (!strcmp(argv[i], "-w")) {
flags &= ~NSS_INIT_READONLY;
}
}
if (NSS_Initialize(workdir, "", "", SECMOD_DB, flags) != SECSuccess) {
return 1;
}
if (NSS_SetDomesticPolicy() != SECSuccess) {

View file

@ -24,6 +24,7 @@ NSS_SRCDIRS = \
cryptohi_gtest \
der_gtest \
pk11_gtest \
smime_gtest \
softoken_gtest \
ssl_gtest \
$(SYSINIT_GTEST) \

View file

@ -13,6 +13,7 @@ CPPSRCS = \
pk11_ecdsa_unittest.cc \
pk11_encrypt_derive_unittest.cc \
pk11_export_unittest.cc \
pk11_import_unittest.cc \
pk11_pbkdf2_unittest.cc \
pk11_prf_unittest.cc \
pk11_prng_unittest.cc \
@ -33,4 +34,3 @@ EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) \
$(DIST)/lib/$(LIB_PREFIX)cpputil.$(LIB_SUFFIX) \
$(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX) \
$(NULL)

View file

@ -18,6 +18,7 @@
'pk11_curve25519_unittest.cc',
'pk11_ecdsa_unittest.cc',
'pk11_encrypt_derive_unittest.cc',
'pk11_import_unittest.cc',
'pk11_pbkdf2_unittest.cc',
'pk11_prf_unittest.cc',
'pk11_prng_unittest.cc',

View file

@ -0,0 +1,43 @@
#! gmake
#
# 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/.
#######################################################################
# (1) Include initial platform-independent assignments (MANDATORY). #
#######################################################################
include manifest.mn
#######################################################################
# (2) Include "global" configuration information. (OPTIONAL) #
#######################################################################
include $(CORE_DEPTH)/coreconf/config.mk
#######################################################################
# (3) Include "component" configuration information. (OPTIONAL) #
#######################################################################
#######################################################################
# (4) Include "local" platform-dependent assignments (OPTIONAL). #
#######################################################################
include ../common/gtest.mk
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
#######################################################################
include $(CORE_DEPTH)/coreconf/rules.mk
#######################################################################
# (6) Execute "component" rules. (OPTIONAL) #
#######################################################################
#######################################################################
# (7) Execute "local" rules. (OPTIONAL). #
#######################################################################

View file

@ -0,0 +1,22 @@
#
# 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/.
CORE_DEPTH = ../..
DEPTH = ../..
MODULE = nss
CPPSRCS = \
smime_unittest.cc \
$(NULL)
INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
-I$(CORE_DEPTH)/gtests/common \
-I$(CORE_DEPTH)/cpputil
REQUIRES = nspr gtest
PROGRAM = smime_gtest
EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) $(EXTRA_OBJS) \
$(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX)

View file

@ -0,0 +1,30 @@
# 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/.
{
'includes': [
'../../coreconf/config.gypi',
'../common/gtest.gypi',
],
'targets': [
{
'target_name': 'smime_gtest',
'type': 'executable',
'sources': [
'smime_unittest.cc',
'<(DEPTH)/gtests/common/gtests.cc'
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
'<(DEPTH)/lib/util/util.gyp:nssutil3',
'<(DEPTH)/lib/nss/nss.gyp:nss3',
'<(DEPTH)/lib/smime/smime.gyp:smime',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl3',
]
}
],
'variables': {
'module': 'nss'
}
}

View file

@ -0,0 +1,137 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 <string>
#include "gtest/gtest.h"
#include "scoped_ptrs_smime.h"
#include "smime.h"
namespace nss_test {
// See bug 1507174; this is a CMS serialization (RFC 5652) that claims to be
// 12336 bytes long, which ensures CMS validates the streaming decoder's
// incorrect length.
static const unsigned char kHugeLenAsn1[] = {
0x30, 0x82, 0x30, 0x30, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
0x0D, 0x01, 0x07, 0x02, 0xA0, 0x82, 0x02, 0x30, 0x30, 0x30, 0x02,
0x01, 0x30, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x09, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x0B, 0x06,
0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x05};
// secp256r1 signature with no certs and no attrs
static unsigned char kValidSignature[] = {
0x30, 0x81, 0xFE, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01,
0x07, 0x02, 0xA0, 0x81, 0xF0, 0x30, 0x81, 0xED, 0x02, 0x01, 0x01, 0x31,
0x0F, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
0x02, 0x01, 0x05, 0x00, 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
0xF7, 0x0D, 0x01, 0x07, 0x01, 0x31, 0x81, 0xC9, 0x30, 0x81, 0xC6, 0x02,
0x01, 0x01, 0x30, 0x5D, 0x30, 0x45, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x53, 0x6F, 0x6D, 0x65, 0x2D, 0x53,
0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x04,
0x0A, 0x0C, 0x18, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20,
0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
0x4C, 0x74, 0x64, 0x02, 0x14, 0x6B, 0x22, 0xCA, 0x91, 0xE0, 0x71, 0x97,
0xEB, 0x45, 0x0D, 0x68, 0xC0, 0xD4, 0xB6, 0xE9, 0x45, 0x38, 0x4C, 0xDD,
0xA3, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
0x02, 0x01, 0x05, 0x00, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE,
0x3D, 0x04, 0x03, 0x02, 0x04, 0x47, 0x30, 0x45, 0x02, 0x20, 0x48, 0xEB,
0xE6, 0xBA, 0xFC, 0xFD, 0x83, 0xB3, 0xA2, 0xB5, 0x59, 0x35, 0x0C, 0xA1,
0x31, 0x0E, 0x2F, 0xE3, 0x8D, 0x81, 0xD8, 0xF5, 0x33, 0xE4, 0x83, 0x87,
0xB1, 0xFD, 0x43, 0x9D, 0x95, 0x7D, 0x02, 0x21, 0x00, 0xD0, 0x05, 0x0E,
0x05, 0xA6, 0x80, 0x3C, 0x1A, 0xFE, 0x51, 0xFC, 0x4D, 0x1A, 0x25, 0x05,
0x78, 0xB5, 0x42, 0xF5, 0xDE, 0x4E, 0x8A, 0xF8, 0xE3, 0xD8, 0x52, 0xDC,
0x2B, 0x73, 0x80, 0x4A, 0x1A};
// See bug 1507135; this is a CMS signature that contains only the OID
static unsigned char kTruncatedSignature[] = {0x30, 0x0B, 0x06, 0x09, 0x2A,
0x86, 0x48, 0x86, 0xF7, 0x0D,
0x01, 0x07, 0x02};
// secp256r1 signature that's truncated by one byte.
static unsigned char kSlightlyTruncatedSignature[] = {
0x30, 0x81, 0xFE, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01,
0x07, 0x02, 0xA0, 0x81, 0xF0, 0x30, 0x81, 0xED, 0x02, 0x01, 0x01, 0x31,
0x0F, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
0x02, 0x01, 0x05, 0x00, 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
0xF7, 0x0D, 0x01, 0x07, 0x01, 0x31, 0x81, 0xC9, 0x30, 0x81, 0xC6, 0x02,
0x01, 0x01, 0x30, 0x5D, 0x30, 0x45, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03,
0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x53, 0x6F, 0x6D, 0x65, 0x2D, 0x53,
0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x04,
0x0A, 0x0C, 0x18, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20,
0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
0x4C, 0x74, 0x64, 0x02, 0x14, 0x6B, 0x22, 0xCA, 0x91, 0xE0, 0x71, 0x97,
0xEB, 0x45, 0x0D, 0x68, 0xC0, 0xD4, 0xB6, 0xE9, 0x45, 0x38, 0x4C, 0xDD,
0xA3, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
0x02, 0x01, 0x05, 0x00, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE,
0x3D, 0x04, 0x03, 0x02, 0x04, 0x47, 0x30, 0x45, 0x02, 0x20, 0x48, 0xEB,
0xE6, 0xBA, 0xFC, 0xFD, 0x83, 0xB3, 0xA2, 0xB5, 0x59, 0x35, 0x0C, 0xA1,
0x31, 0x0E, 0x2F, 0xE3, 0x8D, 0x81, 0xD8, 0xF5, 0x33, 0xE4, 0x83, 0x87,
0xB1, 0xFD, 0x43, 0x9D, 0x95, 0x7D, 0x02, 0x21, 0x00, 0xD0, 0x05, 0x0E,
0x05, 0xA6, 0x80, 0x3C, 0x1A, 0xFE, 0x51, 0xFC, 0x4D, 0x1A, 0x25, 0x05,
0x78, 0xB5, 0x42, 0xF5, 0xDE, 0x4E, 0x8A, 0xF8, 0xE3, 0xD8, 0x52, 0xDC,
0x2B, 0x73, 0x80, 0x4A};
class SMimeTest : public ::testing::Test {};
TEST_F(SMimeTest, InvalidDER) {
PK11SymKey* bulk_key = nullptr;
NSSCMSDecoderContext* dcx =
NSS_CMSDecoder_Start(nullptr, nullptr, nullptr, /* content callback */
nullptr, nullptr, /* password callback */
nullptr, /* key callback */
bulk_key);
ASSERT_NE(nullptr, dcx);
EXPECT_EQ(SECSuccess, NSS_CMSDecoder_Update(
dcx, reinterpret_cast<const char*>(kHugeLenAsn1),
sizeof(kHugeLenAsn1)));
EXPECT_EQ(nullptr, bulk_key);
ASSERT_FALSE(NSS_CMSDecoder_Finish(dcx));
}
TEST_F(SMimeTest, IsSignedValid) {
SECItem sig_der_item = {siBuffer, kValidSignature, sizeof(kValidSignature)};
ScopedNSSCMSMessage cms_msg(NSS_CMSMessage_CreateFromDER(
&sig_der_item, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr));
ASSERT_TRUE(cms_msg);
ASSERT_TRUE(NSS_CMSMessage_IsSigned(cms_msg.get()));
}
TEST_F(SMimeTest, TruncatedCmsSignature) {
SECItem sig_der_item = {siBuffer, kTruncatedSignature,
sizeof(kTruncatedSignature)};
ScopedNSSCMSMessage cms_msg(NSS_CMSMessage_CreateFromDER(
&sig_der_item, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr));
ASSERT_TRUE(cms_msg);
ASSERT_FALSE(NSS_CMSMessage_IsSigned(cms_msg.get()));
}
TEST_F(SMimeTest, SlightlyTruncatedCmsSignature) {
SECItem sig_der_item = {siBuffer, kSlightlyTruncatedSignature,
sizeof(kSlightlyTruncatedSignature)};
ScopedNSSCMSMessage cms_msg(NSS_CMSMessage_CreateFromDER(
&sig_der_item, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr));
ASSERT_FALSE(cms_msg);
ASSERT_FALSE(NSS_CMSMessage_IsSigned(cms_msg.get()));
}
TEST_F(SMimeTest, IsSignedNull) {
ASSERT_FALSE(NSS_CMSMessage_IsSigned(nullptr));
}
} // namespace nss_test

View file

@ -297,38 +297,6 @@ SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group) {
return groupDef->keaType;
}
SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
sslCipherSpecChangedFunc func,
void *arg) {
sslSocket *ss;
ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}
ss->ssl3.changedCipherSpecFunc = func;
ss->ssl3.changedCipherSpecArg = arg;
return SECSuccess;
}
PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec) {
return spec->keyMaterial.key;
}
SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec) {
return spec->cipherDef->calg;
}
const PRUint8 *SSLInt_CipherSpecToIv(const ssl3CipherSpec *spec) {
return spec->keyMaterial.iv;
}
PRUint16 SSLInt_CipherSpecToEpoch(const ssl3CipherSpec *spec) {
return spec->epoch;
}
void SSLInt_SetTicketLifetime(uint32_t lifetime) {
ssl_ticket_lifetime = lifetime;
}
@ -360,16 +328,14 @@ void SSLInt_RolloverAntiReplay(void) {
tls13_AntiReplayRollover(ssl_TimeUsec());
}
SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch,
PRUint16 *writeEpoch) {
SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending) {
sslSocket *ss = ssl_FindSocket(fd);
if (!ss || !readEpoch || !writeEpoch) {
if (!ss) {
return SECFailure;
}
ssl_GetSpecReadLock(ss);
*readEpoch = ss->ssl3.crSpec->epoch;
*writeEpoch = ss->ssl3.cwSpec->epoch;
ssl_ReleaseSpecReadLock(ss);
ssl_GetSSL3HandshakeLock(ss);
*pending = ss->ssl3.hs.msg_body.len > 0;
ssl_ReleaseSSL3HandshakeLock(ss);
return SECSuccess;
}

View file

@ -39,16 +39,7 @@ SECStatus SSLInt_AdvanceWriteSeqNum(PRFileDesc *fd, PRUint64 to);
SECStatus SSLInt_AdvanceReadSeqNum(PRFileDesc *fd, PRUint64 to);
SECStatus SSLInt_AdvanceWriteSeqByAWindow(PRFileDesc *fd, PRInt32 extra);
SSLKEAType SSLInt_GetKEAType(SSLNamedGroup group);
SECStatus SSLInt_GetEpochs(PRFileDesc *fd, PRUint16 *readEpoch,
PRUint16 *writeEpoch);
SECStatus SSLInt_SetCipherSpecChangeFunc(PRFileDesc *fd,
sslCipherSpecChangedFunc func,
void *arg);
PRUint16 SSLInt_CipherSpecToEpoch(const ssl3CipherSpec *spec);
PK11SymKey *SSLInt_CipherSpecToKey(const ssl3CipherSpec *spec);
SSLCipherAlgorithm SSLInt_CipherSpecToAlgorithm(const ssl3CipherSpec *spec);
const PRUint8 *SSLInt_CipherSpecToIv(const ssl3CipherSpec *spec);
SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending);
void SSLInt_SetTicketLifetime(uint32_t lifetime);
SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);
void SSLInt_RolloverAntiReplay(void);

View file

@ -35,7 +35,9 @@ CPPSRCS = \
ssl_keyupdate_unittest.cc \
ssl_loopback_unittest.cc \
ssl_misc_unittest.cc \
ssl_primitive_unittest.cc \
ssl_record_unittest.cc \
ssl_recordsep_unittest.cc \
ssl_recordsize_unittest.cc \
ssl_resumption_unittest.cc \
ssl_renegotiation_unittest.cc \

View file

@ -176,6 +176,321 @@ TEST_P(TlsConnectGeneric, ClientAuth) {
CheckKeys();
}
class TlsCertificateRequestContextRecorder : public TlsHandshakeFilter {
public:
TlsCertificateRequestContextRecorder(const std::shared_ptr<TlsAgent>& a,
uint8_t handshake_type)
: TlsHandshakeFilter(a, {handshake_type}), buffer_(), filtered_(false) {
EnableDecryption();
}
bool filtered() const { return filtered_; }
const DataBuffer& buffer() const { return buffer_; }
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
assert(1 < input.len());
size_t len = input.data()[0];
assert(len + 1 < input.len());
buffer_.Assign(input.data() + 1, len);
filtered_ = true;
return KEEP;
}
private:
DataBuffer buffer_;
bool filtered_;
};
// All stream only tests; DTLS isn't supported yet.
TEST_F(TlsConnectStreamTls13, PostHandshakeAuth) {
EnsureTlsSetup();
auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>(
server_, kTlsHandshakeCertificateRequest);
auto capture_certificate =
MakeTlsFilter<TlsCertificateRequestContextRecorder>(
client_, kTlsHandshakeCertificate);
client_->SetupClientAuth();
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
size_t called = 0;
server_->SetAuthCertificateCallback(
[&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
called++;
return SECSuccess;
});
Connect();
EXPECT_EQ(0U, called);
EXPECT_FALSE(capture_cert_req->filtered());
EXPECT_FALSE(capture_certificate->filtered());
// Send CertificateRequest.
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
// Need to do a round-trip so that the post-handshake message is
// handled on both client and server.
server_->SendData(50);
client_->ReadBytes(50);
client_->SendData(50);
server_->ReadBytes(50);
EXPECT_EQ(1U, called);
EXPECT_TRUE(capture_cert_req->filtered());
EXPECT_TRUE(capture_certificate->filtered());
// Check if a non-empty request context is generated and it is
// properly sent back.
EXPECT_LT(0U, capture_cert_req->buffer().len());
EXPECT_EQ(capture_cert_req->buffer().len(),
capture_certificate->buffer().len());
EXPECT_EQ(0, memcmp(capture_cert_req->buffer().data(),
capture_certificate->buffer().data(),
capture_cert_req->buffer().len()));
ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
}
static SECStatus GetClientAuthDataHook(void* self, PRFileDesc* fd,
CERTDistNames* caNames,
CERTCertificate** clientCert,
SECKEYPrivateKey** clientKey) {
ScopedCERTCertificate cert;
ScopedSECKEYPrivateKey priv;
// use a different certificate than TlsAgent::kClient
if (!TlsAgent::LoadCertificate(TlsAgent::kRsa2048, &cert, &priv)) {
return SECFailure;
}
*clientCert = cert.release();
*clientKey = priv.release();
return SECSuccess;
}
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMultiple) {
client_->SetupClientAuth();
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
size_t called = 0;
server_->SetAuthCertificateCallback(
[&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
called++;
return SECSuccess;
});
Connect();
EXPECT_EQ(0U, called);
EXPECT_EQ(nullptr, SSL_PeerCertificate(server_->ssl_fd()));
// Send 1st CertificateRequest.
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
server_->SendData(50);
client_->ReadBytes(50);
client_->SendData(50);
server_->ReadBytes(50);
EXPECT_EQ(1U, called);
ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
// Send 2nd CertificateRequest.
EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook(
client_->ssl_fd(), GetClientAuthDataHook, nullptr));
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
server_->SendData(50);
client_->ReadBytes(50);
client_->SendData(50);
server_->ReadBytes(50);
EXPECT_EQ(2U, called);
ScopedCERTCertificate cert3(SSL_PeerCertificate(server_->ssl_fd()));
ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd()));
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert4->derCert));
EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert1->derCert));
}
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthConcurrent) {
client_->SetupClientAuth();
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
Connect();
// Send 1st CertificateRequest.
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
// Send 2nd CertificateRequest.
EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd()));
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
}
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthMissingExtension) {
client_->SetupClientAuth();
Connect();
// Send CertificateRequest, should fail due to missing
// post_handshake_auth extension.
EXPECT_EQ(SECFailure, SSL_SendCertificateRequest(server_->ssl_fd()));
EXPECT_EQ(SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION, PORT_GetError());
}
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthAfterClientAuth) {
client_->SetupClientAuth();
server_->RequestClientAuth(true);
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
size_t called = 0;
server_->SetAuthCertificateCallback(
[&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
called++;
return SECSuccess;
});
Connect();
EXPECT_EQ(1U, called);
ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
// Send CertificateRequest.
EXPECT_EQ(SECSuccess, SSL_GetClientAuthDataHook(
client_->ssl_fd(), GetClientAuthDataHook, nullptr));
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
server_->SendData(50);
client_->ReadBytes(50);
client_->SendData(50);
server_->ReadBytes(50);
EXPECT_EQ(2U, called);
ScopedCERTCertificate cert3(SSL_PeerCertificate(server_->ssl_fd()));
ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd()));
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert4->derCert));
EXPECT_FALSE(SECITEM_ItemsAreEqual(&cert3->derCert, &cert1->derCert));
}
// Damages the request context in a CertificateRequest message.
// We don't modify a Certificate message instead, so that the client
// can compute CertificateVerify correctly.
class TlsDamageCertificateRequestContextFilter : public TlsHandshakeFilter {
public:
TlsDamageCertificateRequestContextFilter(const std::shared_ptr<TlsAgent>& a)
: TlsHandshakeFilter(a, {kTlsHandshakeCertificateRequest}) {
EnableDecryption();
}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
*output = input;
assert(1 < output->len());
// The request context has a 1 octet length.
output->data()[1] ^= 73;
return CHANGE;
}
};
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthContextMismatch) {
EnsureTlsSetup();
MakeTlsFilter<TlsDamageCertificateRequestContextFilter>(server_);
client_->SetupClientAuth();
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
Connect();
// Send CertificateRequest.
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
server_->SendData(50);
client_->ReadBytes(50);
client_->SendData(50);
server_->ExpectSendAlert(kTlsAlertIllegalParameter);
server_->ReadBytes(50);
EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CERTIFICATE, PORT_GetError());
server_->ExpectReadWriteError();
server_->SendData(50);
client_->ExpectReceiveAlert(kTlsAlertIllegalParameter);
client_->ReadBytes(50);
EXPECT_EQ(SSL_ERROR_ILLEGAL_PARAMETER_ALERT, PORT_GetError());
}
// Replaces signature in a CertificateVerify message.
class TlsDamageSignatureFilter : public TlsHandshakeFilter {
public:
TlsDamageSignatureFilter(const std::shared_ptr<TlsAgent>& a)
: TlsHandshakeFilter(a, {kTlsHandshakeCertificateVerify}) {
EnableDecryption();
}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
const DataBuffer& input,
DataBuffer* output) {
*output = input;
assert(2 < output->len());
// The signature follows a 2-octet signature scheme.
output->data()[2] ^= 73;
return CHANGE;
}
};
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthBadSignature) {
EnsureTlsSetup();
MakeTlsFilter<TlsDamageSignatureFilter>(client_);
client_->SetupClientAuth();
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
Connect();
// Send CertificateRequest.
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
server_->SendData(50);
client_->ReadBytes(50);
client_->SendData(50);
server_->ExpectSendAlert(kTlsAlertDecodeError);
server_->ReadBytes(50);
EXPECT_EQ(SSL_ERROR_RX_MALFORMED_CERT_VERIFY, PORT_GetError());
}
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDecline) {
EnsureTlsSetup();
auto capture_cert_req = MakeTlsFilter<TlsCertificateRequestContextRecorder>(
server_, kTlsHandshakeCertificateRequest);
auto capture_certificate =
MakeTlsFilter<TlsCertificateRequestContextRecorder>(
client_, kTlsHandshakeCertificate);
client_->SetupClientAuth();
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
// Client to decline the certificate request.
EXPECT_EQ(SECSuccess,
SSL_GetClientAuthDataHook(
client_->ssl_fd(),
[](void*, PRFileDesc*, CERTDistNames*, CERTCertificate**,
SECKEYPrivateKey**) -> SECStatus { return SECFailure; },
nullptr));
size_t called = 0;
server_->SetAuthCertificateCallback(
[&called](TlsAgent*, PRBool, PRBool) -> SECStatus {
called++;
return SECSuccess;
});
Connect();
EXPECT_EQ(0U, called);
// Send CertificateRequest.
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
server_->SendData(50);
client_->ReadBytes(50);
client_->SendData(50);
server_->ReadBytes(50);
// AuthCertificateCallback is not called, because the client sends
// an empty certificate_list.
EXPECT_EQ(0U, called);
EXPECT_TRUE(capture_cert_req->filtered());
EXPECT_TRUE(capture_certificate->filtered());
// Check if a non-empty request context is generated and it is
// properly sent back.
EXPECT_LT(0U, capture_cert_req->buffer().len());
EXPECT_EQ(capture_cert_req->buffer().len(),
capture_certificate->buffer().len());
EXPECT_EQ(0, memcmp(capture_cert_req->buffer().data(),
capture_certificate->buffer().data(),
capture_cert_req->buffer().len()));
}
// In TLS 1.3, the client sends its cert rejection on the
// second flight, and since it has already received the
// server's Finished, it transitions to complete and
@ -273,9 +588,7 @@ class TlsReplaceSignatureSchemeFilter : public TlsHandshakeFilter {
TlsReplaceSignatureSchemeFilter(const std::shared_ptr<TlsAgent>& a,
SSLSignatureScheme scheme)
: TlsHandshakeFilter(a, {kTlsHandshakeCertificateVerify}),
scheme_(scheme) {
EnableDecryption();
}
scheme_(scheme) {}
protected:
virtual PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
@ -552,7 +865,9 @@ TEST_P(TlsConnectTls12, SignatureAlgorithmDrop) {
TEST_P(TlsConnectTls13, UnsupportedSignatureSchemeAlert) {
EnsureTlsSetup();
MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(server_, ssl_sig_none);
auto filter =
MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(server_, ssl_sig_none);
filter->EnableDecryption();
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
@ -563,8 +878,9 @@ TEST_P(TlsConnectTls13, InconsistentSignatureSchemeAlert) {
EnsureTlsSetup();
// This won't work because we use an RSA cert by default.
MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(
auto filter = MakeTlsFilter<TlsReplaceSignatureSchemeFilter>(
server_, ssl_sig_ecdsa_secp256r1_sha256);
filter->EnableDecryption();
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
server_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);

View file

@ -62,7 +62,6 @@ TEST_P(TlsConnectGenericPre13, DamageServerSignature) {
EnsureTlsSetup();
auto filter = MakeTlsFilter<TlsLastByteDamager>(
server_, kTlsHandshakeServerKeyExchange);
filter->EnableDecryption();
ExpectAlert(client_, kTlsAlertDecryptError);
ConnectExpectFail();
client_->CheckErrorCode(SEC_ERROR_BAD_SIGNATURE);
@ -84,7 +83,9 @@ TEST_P(TlsConnectGeneric, DamageClientSignature) {
server_->RequestClientAuth(true);
auto filter = MakeTlsFilter<TlsLastByteDamager>(
client_, kTlsHandshakeCertificateVerify);
filter->EnableDecryption();
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
filter->EnableDecryption();
}
server_->ExpectSendAlert(kTlsAlertDecryptError);
// Do these handshakes by hand to avoid race condition on
// the client processing the server's alert.

View file

@ -66,6 +66,38 @@ TEST_P(TlsConnectDatagramPre13, DropServerSecondFlightThrice) {
Connect();
}
static void CheckAcks(const std::shared_ptr<TlsRecordRecorder>& acks,
size_t index, std::vector<uint64_t> expected) {
ASSERT_LT(index, acks->count());
const DataBuffer& buf = acks->record(index).buffer;
size_t offset = 2;
uint64_t len;
EXPECT_EQ(2 + expected.size() * 8, buf.len());
ASSERT_TRUE(buf.Read(0, 2, &len));
ASSERT_EQ(static_cast<size_t>(len + 2), buf.len());
if ((2 + expected.size() * 8) != buf.len()) {
while (offset < buf.len()) {
uint64_t ack;
ASSERT_TRUE(buf.Read(offset, 8, &ack));
offset += 8;
std::cerr << "Ack=0x" << std::hex << ack << std::dec << std::endl;
}
return;
}
for (size_t i = 0; i < expected.size(); ++i) {
uint64_t a = expected[i];
uint64_t ack;
ASSERT_TRUE(buf.Read(offset, 8, &ack));
offset += 8;
if (a != ack) {
ADD_FAILURE() << "Wrong ack " << i << " expected=0x" << std::hex << a
<< " got=0x" << ack << std::dec;
}
}
}
class TlsDropDatagram13 : public TlsConnectDatagram13,
public ::testing::WithParamInterface<bool> {
public:
@ -139,37 +171,6 @@ class TlsDropDatagram13 : public TlsConnectDatagram13,
std::shared_ptr<PacketFilter> chain_;
};
void CheckAcks(const DropAckChain& chain, size_t index,
std::vector<uint64_t> acks) {
const DataBuffer& buf = chain.ack_->record(index).buffer;
size_t offset = 2;
uint64_t len;
EXPECT_EQ(2 + acks.size() * 8, buf.len());
ASSERT_TRUE(buf.Read(0, 2, &len));
ASSERT_EQ(static_cast<size_t>(len + 2), buf.len());
if ((2 + acks.size() * 8) != buf.len()) {
while (offset < buf.len()) {
uint64_t ack;
ASSERT_TRUE(buf.Read(offset, 8, &ack));
offset += 8;
std::cerr << "Ack=0x" << std::hex << ack << std::dec << std::endl;
}
return;
}
for (size_t i = 0; i < acks.size(); ++i) {
uint64_t a = acks[i];
uint64_t ack;
ASSERT_TRUE(buf.Read(offset, 8, &ack));
offset += 8;
if (a != ack) {
ADD_FAILURE() << "Wrong ack " << i << " expected=0x" << std::hex << a
<< " got=0x" << ack << std::dec;
}
}
}
void CheckedHandshakeSendReceive() {
Handshake();
CheckPostHandshake();
@ -199,7 +200,7 @@ TEST_P(TlsDropDatagram13, DropClientFirstFlightOnce) {
client_->Handshake();
server_->Handshake();
CheckedHandshakeSendReceive();
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
TEST_P(TlsDropDatagram13, DropServerFirstFlightOnce) {
@ -210,7 +211,7 @@ TEST_P(TlsDropDatagram13, DropServerFirstFlightOnce) {
server_->Handshake();
server_filters_.drop_->Disable();
CheckedHandshakeSendReceive();
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// Dropping the server's first record also does not produce
@ -223,7 +224,7 @@ TEST_P(TlsDropDatagram13, DropServerFirstRecordOnce) {
server_->Handshake();
Handshake();
CheckedHandshakeSendReceive();
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// Dropping the second packet of the server's flight should
@ -236,8 +237,8 @@ TEST_P(TlsDropDatagram13, DropServerSecondRecordOnce) {
HandshakeAndAck(client_);
expected_client_acks_ = 1;
CheckedHandshakeSendReceive();
CheckAcks(client_filters_, 0, {0}); // ServerHello
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(client_filters_.ack_, 0, {0}); // ServerHello
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// Drop the server ACK and verify that the client retransmits
@ -265,8 +266,8 @@ TEST_P(TlsDropDatagram13, DropServerAckOnce) {
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
CheckPostHandshake();
// There should be two copies of the finished ACK
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_, 1, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 1, {0x0002000000000000ULL});
}
// Drop the client certificate verify.
@ -281,10 +282,10 @@ TEST_P(TlsDropDatagram13, DropClientCertVerify) {
expected_server_acks_ = 2;
CheckedHandshakeSendReceive();
// Ack of the Cert.
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
// Ack of the whole client handshake.
CheckAcks(
server_filters_, 1,
server_filters_.ack_, 1,
{0x0002000000000000ULL, // CH (we drop everything after this on client)
0x0002000000000003ULL, // CT (2)
0x0002000000000004ULL}); // FIN (2)
@ -310,11 +311,11 @@ TEST_P(TlsDropDatagram13, DropFirstHalfOfServerCertificate) {
// as the previous CT1).
EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
CheckedHandshakeSendReceive();
CheckAcks(client_filters_, 0,
CheckAcks(client_filters_.ack_, 0,
{0, // SH
0x0002000000000000ULL, // EE
0x0002000000000002ULL}); // CT2
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// Shrink the MTU down so that certs get split and drop the second piece.
@ -336,13 +337,13 @@ TEST_P(TlsDropDatagram13, DropSecondHalfOfServerCertificate) {
// Check that the first record is CT1
EXPECT_EQ(ct1_size, server_filters_.record(0).buffer.len());
CheckedHandshakeSendReceive();
CheckAcks(client_filters_, 0,
CheckAcks(client_filters_.ack_, 0,
{
0, // SH
0x0002000000000000ULL, // EE
0x0002000000000001ULL, // CT1
});
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// In this test, the Certificate message is sent four times, we drop all or part
@ -392,18 +393,18 @@ class TlsFragmentationAndRecoveryTest : public TlsDropDatagram13 {
0, // SH
0x0002000000000000ULL // EE
};
CheckAcks(client_filters_, 0, client_acks);
CheckAcks(client_filters_.ack_, 0, client_acks);
// And from the second attempt for the half was kept (we delayed this ACK).
client_acks.push_back(0x0002000000000000ULL + second_flight_count +
~dropped_half % 2);
CheckAcks(client_filters_, 1, client_acks);
CheckAcks(client_filters_.ack_, 1, client_acks);
// And the third attempt where the first and last thirds got through.
client_acks.push_back(0x0002000000000000ULL + second_flight_count +
third_flight_count - 1);
client_acks.push_back(0x0002000000000000ULL + second_flight_count +
third_flight_count + 1);
CheckAcks(client_filters_, 2, client_acks);
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(client_filters_.ack_, 2, client_acks);
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
private:
@ -548,7 +549,7 @@ TEST_P(TlsDropDatagram13, NoDropsDuringZeroRtt) {
CheckConnected();
SendReceive();
EXPECT_EQ(0U, client_filters_.ack_->count());
CheckAcks(server_filters_, 0,
CheckAcks(server_filters_.ack_, 0,
{0x0001000000000001ULL, // EOED
0x0002000000000000ULL}); // Finished
}
@ -567,8 +568,8 @@ TEST_P(TlsDropDatagram13, DropEEDuringZeroRtt) {
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
CheckAcks(client_filters_, 0, {0});
CheckAcks(server_filters_, 0,
CheckAcks(client_filters_.ack_, 0, {0});
CheckAcks(server_filters_.ack_, 0,
{0x0001000000000002ULL, // EOED
0x0002000000000000ULL}); // Finished
}
@ -608,22 +609,22 @@ TEST_P(TlsDropDatagram13, ReorderServerEE) {
expected_client_acks_ = 1;
HandshakeAndAck(client_);
CheckedHandshakeSendReceive();
CheckAcks(client_filters_, 0,
CheckAcks(client_filters_.ack_, 0,
{
0, // SH
0x0002000000000000, // EE
});
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
// The client sends an out of order non-handshake message
// but with the handshake key.
class TlsSendCipherSpecCapturer {
public:
TlsSendCipherSpecCapturer(std::shared_ptr<TlsAgent>& agent)
: send_cipher_specs_() {
SSLInt_SetCipherSpecChangeFunc(agent->ssl_fd(), CipherSpecChanged,
(void*)this);
TlsSendCipherSpecCapturer(const std::shared_ptr<TlsAgent>& agent)
: agent_(agent), send_cipher_specs_() {
EXPECT_EQ(SECSuccess,
SSL_SecretCallback(agent_->ssl_fd(), SecretCallback, this));
}
std::shared_ptr<TlsCipherSpec> spec(size_t i) {
@ -634,28 +635,42 @@ class TlsSendCipherSpecCapturer {
}
private:
static void CipherSpecChanged(void* arg, PRBool sending,
ssl3CipherSpec* newSpec) {
if (!sending) {
static void SecretCallback(PRFileDesc* fd, PRUint16 epoch,
SSLSecretDirection dir, PK11SymKey* secret,
void* arg) {
auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
std::cerr << self->agent_->role_str() << ": capture " << dir
<< " secret for epoch " << epoch << std::endl;
if (dir == ssl_secret_read) {
return;
}
auto self = static_cast<TlsSendCipherSpecCapturer*>(arg);
SSLPreliminaryChannelInfo preinfo;
EXPECT_EQ(SECSuccess,
SSL_GetPreliminaryChannelInfo(self->agent_->ssl_fd(), &preinfo,
sizeof(preinfo)));
EXPECT_EQ(sizeof(preinfo), preinfo.length);
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite);
auto spec = std::make_shared<TlsCipherSpec>();
bool ret = spec->Init(SSLInt_CipherSpecToEpoch(newSpec),
SSLInt_CipherSpecToAlgorithm(newSpec),
SSLInt_CipherSpecToKey(newSpec),
SSLInt_CipherSpecToIv(newSpec));
EXPECT_EQ(true, ret);
SSLCipherSuiteInfo cipherinfo;
EXPECT_EQ(SECSuccess,
SSL_GetCipherSuiteInfo(preinfo.cipherSuite, &cipherinfo,
sizeof(cipherinfo)));
EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length);
auto spec = std::make_shared<TlsCipherSpec>(true, epoch);
EXPECT_TRUE(spec->SetKeys(&cipherinfo, secret));
self->send_cipher_specs_.push_back(spec);
}
std::shared_ptr<TlsAgent> agent_;
std::vector<std::shared_ptr<TlsCipherSpec>> send_cipher_specs_;
};
TEST_P(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
TEST_F(TlsConnectDatagram13, SendOutOfOrderAppWithHandshakeKey) {
StartConnect();
// Capturing secrets means that we can't use decrypting filters on the client.
TlsSendCipherSpecCapturer capturer(client_);
client_->Handshake();
server_->Handshake();
@ -680,9 +695,12 @@ TEST_P(TlsDropDatagram13, SendOutOfOrderAppWithHandshakeKey) {
EXPECT_EQ(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE, PORT_GetError());
}
TEST_P(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
TEST_F(TlsConnectDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
StartConnect();
TlsSendCipherSpecCapturer capturer(client_);
auto acks = MakeTlsFilter<TlsRecordRecorder>(server_, ssl_ct_ack);
acks->EnableDecryption();
client_->Handshake();
server_->Handshake();
client_->Handshake();
@ -699,10 +717,10 @@ TEST_P(TlsDropDatagram13, SendOutOfOrderHsNonsenseWithHandshakeKey) {
ssl_ct_handshake,
DataBuffer(buf, sizeof(buf))));
server_->Handshake();
EXPECT_EQ(2UL, server_filters_.ack_->count());
EXPECT_EQ(2UL, acks->count());
// The server acknowledges client Finished twice.
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_, 1, {0x0002000000000000ULL});
CheckAcks(acks, 0, {0x0002000000000000ULL});
CheckAcks(acks, 1, {0x0002000000000000ULL});
}
// Shrink the MTU down so that certs get split and then swap the first and
@ -726,7 +744,7 @@ TEST_P(TlsReorderDatagram13, ReorderServerCertificate) {
ShiftDtlsTimers();
CheckedHandshakeSendReceive();
EXPECT_EQ(2UL, server_filters_.records_->count()); // ACK + Data
CheckAcks(server_filters_, 0, {0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0, {0x0002000000000000ULL});
}
TEST_P(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
@ -761,7 +779,8 @@ TEST_P(TlsReorderDatagram13, DataAfterEOEDDuringZeroRtt) {
CheckConnected();
EXPECT_EQ(0U, client_filters_.ack_->count());
// Acknowledgements for EOED and Finished.
CheckAcks(server_filters_, 0, {0x0001000000000002ULL, 0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0,
{0x0001000000000002ULL, 0x0002000000000000ULL});
uint8_t buf[8];
rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
EXPECT_EQ(-1, rv);
@ -800,7 +819,8 @@ TEST_P(TlsReorderDatagram13, DataAfterFinDuringZeroRtt) {
CheckConnected();
EXPECT_EQ(0U, client_filters_.ack_->count());
// Acknowledgements for EOED and Finished.
CheckAcks(server_filters_, 0, {0x0001000000000002ULL, 0x0002000000000000ULL});
CheckAcks(server_filters_.ack_, 0,
{0x0001000000000002ULL, 0x0002000000000000ULL});
uint8_t buf[8];
rv = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
EXPECT_EQ(-1, rv);

View file

@ -482,6 +482,73 @@ TEST_P(TlsExtensionTestGeneric, SupportedCurvesTrailingData) {
client_, ssl_elliptic_curves_xtn, extension));
}
TEST_P(TlsExtensionTest12, SupportedCurvesDisableX25519) {
// Disable session resumption.
ConfigureSessionCache(RESUME_NONE, RESUME_NONE);
// Ensure that we can enable its use in the key exchange.
SECStatus rv =
NSS_SetAlgorithmPolicy(SEC_OID_CURVE25519, NSS_USE_ALG_IN_SSL_KX, 0);
ASSERT_EQ(SECSuccess, rv);
rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL,
0);
ASSERT_EQ(SECSuccess, rv);
auto capture1 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_elliptic_curves_xtn);
Connect();
EXPECT_TRUE(capture1->captured());
const DataBuffer& ext1 = capture1->extension();
uint32_t count;
ASSERT_TRUE(ext1.Read(0, 2, &count));
// Whether or not we've seen x25519 offered in this handshake.
bool seen1_x25519 = false;
for (size_t offset = 2; offset <= count; offset++) {
uint32_t val;
ASSERT_TRUE(ext1.Read(offset, 2, &val));
if (val == ssl_grp_ec_curve25519) {
seen1_x25519 = true;
break;
}
}
ASSERT_TRUE(seen1_x25519);
// Ensure that we can disable its use in the key exchange.
rv = NSS_SetAlgorithmPolicy(SEC_OID_CURVE25519, 0, NSS_USE_ALG_IN_SSL_KX);
ASSERT_EQ(SECSuccess, rv);
rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL,
0);
ASSERT_EQ(SECSuccess, rv);
// Clean up after the last run.
Reset();
auto capture2 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_elliptic_curves_xtn);
Connect();
EXPECT_TRUE(capture2->captured());
const DataBuffer& ext2 = capture2->extension();
ASSERT_TRUE(ext2.Read(0, 2, &count));
// Whether or not we've seen x25519 offered in this handshake.
bool seen2_x25519 = false;
for (size_t offset = 2; offset <= count; offset++) {
uint32_t val;
ASSERT_TRUE(ext2.Read(offset, 2, &val));
if (val == ssl_grp_ec_curve25519) {
seen2_x25519 = true;
break;
}
}
ASSERT_FALSE(seen2_x25519);
}
TEST_P(TlsExtensionTestPre13, SupportedPointsEmpty) {
const uint8_t val[] = {0x00};
DataBuffer extension(val, sizeof(val));
@ -547,6 +614,56 @@ TEST_P(TlsExtensionTest12, SignatureAlgorithmConfiguration) {
}
}
// This only works on TLS 1.2, since it relies on DSA.
TEST_P(TlsExtensionTest12, SignatureAlgorithmDisableDSA) {
const std::vector<SSLSignatureScheme> schemes = {
ssl_sig_dsa_sha1, ssl_sig_dsa_sha256, ssl_sig_dsa_sha384,
ssl_sig_dsa_sha512, ssl_sig_rsa_pss_rsae_sha256};
// Connect with DSA enabled by policy.
SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_ANSIX9_DSA_SIGNATURE,
NSS_USE_ALG_IN_SSL_KX, 0);
ASSERT_EQ(SECSuccess, rv);
rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL,
0);
ASSERT_EQ(SECSuccess, rv);
Reset(TlsAgent::kServerDsa);
auto capture1 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn);
client_->SetSignatureSchemes(schemes.data(), schemes.size());
Connect();
// Check if all the signature algorithms are advertised.
EXPECT_TRUE(capture1->captured());
const DataBuffer& ext1 = capture1->extension();
EXPECT_EQ(2U + 2U * schemes.size(), ext1.len());
// Connect with DSA disabled by policy.
rv = NSS_SetAlgorithmPolicy(SEC_OID_ANSIX9_DSA_SIGNATURE, 0,
NSS_USE_ALG_IN_SSL_KX);
ASSERT_EQ(SECSuccess, rv);
rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, NSS_USE_POLICY_IN_SSL,
0);
ASSERT_EQ(SECSuccess, rv);
Reset(TlsAgent::kServerDsa);
auto capture2 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn);
client_->SetSignatureSchemes(schemes.data(), schemes.size());
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
// Check if no DSA algorithms are advertised.
EXPECT_TRUE(capture2->captured());
const DataBuffer& ext2 = capture2->extension();
EXPECT_EQ(2U + 2U, ext2.len());
uint32_t v = 0;
EXPECT_TRUE(ext2.Read(2, 2, &v));
EXPECT_EQ(ssl_sig_rsa_pss_rsae_sha256, v);
}
// Temporary test to verify that we choke on an empty ClientKeyShare.
// This test will fail when we implement HelloRetryRequest.
TEST_P(TlsExtensionTest13, EmptyClientKeyShare) {
@ -1121,6 +1238,10 @@ INSTANTIATE_TEST_CASE_P(
INSTANTIATE_TEST_CASE_P(ExtensionDatagramOnly, TlsExtensionTestDtls,
TlsConnectTestBase::kTlsV11Plus);
INSTANTIATE_TEST_CASE_P(ExtensionTls12, TlsExtensionTest12,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV12));
INSTANTIATE_TEST_CASE_P(ExtensionTls12Plus, TlsExtensionTest12Plus,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV12Plus));

View file

@ -36,7 +36,9 @@
'ssl_keyupdate_unittest.cc',
'ssl_loopback_unittest.cc',
'ssl_misc_unittest.cc',
'ssl_primitive_unittest.cc',
'ssl_record_unittest.cc',
'ssl_recordsep_unittest.cc',
'ssl_recordsize_unittest.cc',
'ssl_resumption_unittest.cc',
'ssl_renegotiation_unittest.cc',

View file

@ -0,0 +1,217 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 <memory>
#include "keyhi.h"
#include "pk11pub.h"
#include "secerr.h"
#include "ssl.h"
#include "sslerr.h"
#include "sslexp.h"
#include "sslproto.h"
#include "gtest_utils.h"
#include "nss_scoped_ptrs.h"
#include "scoped_ptrs_ssl.h"
#include "tls_connect.h"
namespace nss_test {
// From tls_hkdf_unittest.cc:
extern size_t GetHashLength(SSLHashType ht);
class AeadTest : public ::testing::Test {
public:
AeadTest() : slot_(PK11_GetInternalSlot()) {}
void InitSecret(SSLHashType hash_type) {
static const uint8_t kData[64] = {'s', 'e', 'c', 'r', 'e', 't'};
SECItem key_item = {siBuffer, const_cast<uint8_t *>(kData),
static_cast<unsigned int>(GetHashLength(hash_type))};
PK11SymKey *s =
PK11_ImportSymKey(slot_.get(), CKM_SSL3_MASTER_KEY_DERIVE,
PK11_OriginUnwrap, CKA_DERIVE, &key_item, NULL);
ASSERT_NE(nullptr, s);
secret_.reset(s);
}
void SetUp() override {
InitSecret(ssl_hash_sha256);
PORT_SetError(0);
}
protected:
static void EncryptDecrypt(const ScopedSSLAeadContext &ctx,
const uint8_t *ciphertext, size_t ciphertext_len) {
static const uint8_t kAad[] = {'a', 'a', 'd'};
static const uint8_t kPlaintext[] = {'t', 'e', 'x', 't'};
static const size_t kMaxSize = 32;
ASSERT_GE(kMaxSize, ciphertext_len);
uint8_t output[kMaxSize];
unsigned int output_len = 0;
EXPECT_EQ(SECSuccess, SSL_AeadEncrypt(ctx.get(), 0, kAad, sizeof(kAad),
kPlaintext, sizeof(kPlaintext),
output, &output_len, sizeof(output)));
ASSERT_EQ(ciphertext_len, static_cast<size_t>(output_len));
EXPECT_EQ(0, memcmp(ciphertext, output, ciphertext_len));
memset(output, 0, sizeof(output));
EXPECT_EQ(SECSuccess, SSL_AeadDecrypt(ctx.get(), 0, kAad, sizeof(kAad),
ciphertext, ciphertext_len, output,
&output_len, sizeof(output)));
ASSERT_EQ(sizeof(kPlaintext), static_cast<size_t>(output_len));
EXPECT_EQ(0, memcmp(kPlaintext, output, sizeof(kPlaintext)));
// Now for some tests of decryption failure.
// Truncate the input.
EXPECT_EQ(SECFailure, SSL_AeadDecrypt(ctx.get(), 0, kAad, sizeof(kAad),
ciphertext, ciphertext_len - 1,
output, &output_len, sizeof(output)));
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
// Skip the first byte of the AAD.
EXPECT_EQ(
SECFailure,
SSL_AeadDecrypt(ctx.get(), 0, kAad + 1, sizeof(kAad) - 1, ciphertext,
ciphertext_len, output, &output_len, sizeof(output)));
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
uint8_t input[kMaxSize] = {0};
// Toggle a byte of the input.
memcpy(input, ciphertext, ciphertext_len);
input[0] ^= 9;
EXPECT_EQ(SECFailure, SSL_AeadDecrypt(ctx.get(), 0, kAad, sizeof(kAad),
input, ciphertext_len, output,
&output_len, sizeof(output)));
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
// Toggle the last byte (the auth tag).
memcpy(input, ciphertext, ciphertext_len);
input[ciphertext_len - 1] ^= 77;
EXPECT_EQ(SECFailure, SSL_AeadDecrypt(ctx.get(), 0, kAad, sizeof(kAad),
input, ciphertext_len, output,
&output_len, sizeof(output)));
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
// Toggle some of the AAD.
memcpy(input, kAad, sizeof(kAad));
input[1] ^= 23;
EXPECT_EQ(SECFailure, SSL_AeadDecrypt(ctx.get(), 0, input, sizeof(kAad),
ciphertext, ciphertext_len, output,
&output_len, sizeof(output)));
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
}
protected:
ScopedPK11SymKey secret_;
private:
ScopedPK11SlotInfo slot_;
};
// These tests all use fixed inputs: a fixed secret, a fixed label, and fixed
// inputs. So they have fixed outputs.
static const char *kLabel = "test ";
static const uint8_t kCiphertextAes128Gcm[] = {
0x11, 0x14, 0xfc, 0x58, 0x4f, 0x44, 0xff, 0x8c, 0xb6, 0xd8,
0x20, 0xb3, 0xfb, 0x50, 0xd9, 0x3b, 0xd4, 0xc6, 0xe1, 0x14};
static const uint8_t kCiphertextAes256Gcm[] = {
0xf7, 0x27, 0x35, 0x80, 0x88, 0xaf, 0x99, 0x85, 0xf2, 0x83,
0xca, 0xbb, 0x95, 0x42, 0x09, 0x3f, 0x9c, 0xf3, 0x29, 0xf0};
static const uint8_t kCiphertextChaCha20Poly1305[] = {
0x4e, 0x89, 0x2c, 0xfa, 0xfc, 0x8c, 0x40, 0x55, 0x6d, 0x7e,
0x99, 0xac, 0x8e, 0x54, 0x58, 0xb1, 0x18, 0xd2, 0x66, 0x22};
TEST_F(AeadTest, AeadBadVersion) {
SSLAeadContext *ctx = nullptr;
ASSERT_EQ(SECFailure,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_2, TLS_AES_128_GCM_SHA256,
secret_.get(), kLabel, strlen(kLabel), &ctx));
EXPECT_EQ(nullptr, ctx);
}
TEST_F(AeadTest, AeadUnsupportedCipher) {
SSLAeadContext *ctx = nullptr;
ASSERT_EQ(SECFailure,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_RSA_WITH_NULL_MD5,
secret_.get(), kLabel, strlen(kLabel), &ctx));
EXPECT_EQ(nullptr, ctx);
}
TEST_F(AeadTest, AeadOlderCipher) {
SSLAeadContext *ctx = nullptr;
ASSERT_EQ(
SECFailure,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_RSA_WITH_AES_128_CBC_SHA,
secret_.get(), kLabel, strlen(kLabel), &ctx));
EXPECT_EQ(nullptr, ctx);
}
TEST_F(AeadTest, AeadNoLabel) {
SSLAeadContext *ctx = nullptr;
ASSERT_EQ(SECFailure,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_AES_128_GCM_SHA256,
secret_.get(), nullptr, 12, &ctx));
EXPECT_EQ(nullptr, ctx);
}
TEST_F(AeadTest, AeadLongLabel) {
SSLAeadContext *ctx = nullptr;
ASSERT_EQ(SECFailure,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_AES_128_GCM_SHA256,
secret_.get(), "", 254, &ctx));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(nullptr, ctx);
}
TEST_F(AeadTest, AeadNoPointer) {
SSLAeadContext *ctx = nullptr;
ASSERT_EQ(SECFailure,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_AES_128_GCM_SHA256,
secret_.get(), kLabel, strlen(kLabel), nullptr));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(nullptr, ctx);
}
TEST_F(AeadTest, AeadAes128Gcm) {
SSLAeadContext *ctxInit;
ASSERT_EQ(SECSuccess,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_AES_128_GCM_SHA256,
secret_.get(), kLabel, strlen(kLabel), &ctxInit));
ScopedSSLAeadContext ctx(ctxInit);
EXPECT_NE(nullptr, ctx);
EncryptDecrypt(ctx, kCiphertextAes128Gcm, sizeof(kCiphertextAes128Gcm));
}
TEST_F(AeadTest, AeadAes256Gcm) {
SSLAeadContext *ctxInit;
ASSERT_EQ(SECSuccess,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_AES_256_GCM_SHA384,
secret_.get(), kLabel, strlen(kLabel), &ctxInit));
ScopedSSLAeadContext ctx(ctxInit);
EXPECT_NE(nullptr, ctx);
EncryptDecrypt(ctx, kCiphertextAes256Gcm, sizeof(kCiphertextAes256Gcm));
}
TEST_F(AeadTest, AeadChaCha20Poly1305) {
SSLAeadContext *ctxInit;
ASSERT_EQ(
SECSuccess,
SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3, TLS_CHACHA20_POLY1305_SHA256,
secret_.get(), kLabel, strlen(kLabel), &ctxInit));
ScopedSSLAeadContext ctx(ctxInit);
EXPECT_NE(nullptr, ctx);
EncryptDecrypt(ctx, kCiphertextChaCha20Poly1305,
sizeof(kCiphertextChaCha20Poly1305));
}
} // namespace nss_test

View file

@ -0,0 +1,576 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 "secerr.h"
#include "ssl.h"
#include "sslerr.h"
#include "sslproto.h"
extern "C" {
// This is not something that should make you happy.
#include "libssl_internals.h"
}
#include <queue>
#include "gtest_utils.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"
namespace nss_test {
class HandshakeSecretTracker {
public:
HandshakeSecretTracker(const std::shared_ptr<TlsAgent>& agent,
uint16_t first_read_epoch, uint16_t first_write_epoch)
: agent_(agent),
next_read_epoch_(first_read_epoch),
next_write_epoch_(first_write_epoch) {
EXPECT_EQ(SECSuccess,
SSL_SecretCallback(agent_->ssl_fd(),
HandshakeSecretTracker::SecretCb, this));
}
void CheckComplete() const {
EXPECT_EQ(0, next_read_epoch_);
EXPECT_EQ(0, next_write_epoch_);
}
private:
static void SecretCb(PRFileDesc* fd, PRUint16 epoch, SSLSecretDirection dir,
PK11SymKey* secret, void* arg) {
HandshakeSecretTracker* t = reinterpret_cast<HandshakeSecretTracker*>(arg);
t->SecretUpdated(epoch, dir, secret);
}
void SecretUpdated(PRUint16 epoch, SSLSecretDirection dir,
PK11SymKey* secret) {
if (g_ssl_gtest_verbose) {
std::cerr << agent_->role_str() << ": secret callback for " << dir
<< " epoch " << epoch << std::endl;
}
EXPECT_TRUE(secret);
uint16_t* p;
if (dir == ssl_secret_read) {
p = &next_read_epoch_;
} else {
ASSERT_EQ(ssl_secret_write, dir);
p = &next_write_epoch_;
}
EXPECT_EQ(*p, epoch);
switch (*p) {
case 1: // 1 == 0-RTT, next should be handshake.
case 2: // 2 == handshake, next should be application data.
(*p)++;
break;
case 3: // 3 == application data, there should be no more.
// Use 0 as a sentinel value.
*p = 0;
break;
default:
ADD_FAILURE() << "Unexpected next epoch: " << *p;
}
}
std::shared_ptr<TlsAgent> agent_;
uint16_t next_read_epoch_;
uint16_t next_write_epoch_;
};
TEST_F(TlsConnectTest, HandshakeSecrets) {
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
EnsureTlsSetup();
HandshakeSecretTracker c(client_, 2, 2);
HandshakeSecretTracker s(server_, 2, 2);
Connect();
SendReceive();
c.CheckComplete();
s.CheckComplete();
}
TEST_F(TlsConnectTest, ZeroRttSecrets) {
SetupForZeroRtt();
HandshakeSecretTracker c(client_, 2, 1);
HandshakeSecretTracker s(server_, 1, 2);
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, true);
Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
c.CheckComplete();
s.CheckComplete();
}
class KeyUpdateTracker {
public:
KeyUpdateTracker(const std::shared_ptr<TlsAgent>& agent,
bool expect_read_secret)
: agent_(agent), expect_read_secret_(expect_read_secret), called_(false) {
EXPECT_EQ(SECSuccess, SSL_SecretCallback(agent_->ssl_fd(),
KeyUpdateTracker::SecretCb, this));
}
void CheckCalled() const { EXPECT_TRUE(called_); }
private:
static void SecretCb(PRFileDesc* fd, PRUint16 epoch, SSLSecretDirection dir,
PK11SymKey* secret, void* arg) {
KeyUpdateTracker* t = reinterpret_cast<KeyUpdateTracker*>(arg);
t->SecretUpdated(epoch, dir, secret);
}
void SecretUpdated(PRUint16 epoch, SSLSecretDirection dir,
PK11SymKey* secret) {
EXPECT_EQ(4U, epoch);
EXPECT_EQ(expect_read_secret_, dir == ssl_secret_read);
EXPECT_TRUE(secret);
called_ = true;
}
std::shared_ptr<TlsAgent> agent_;
bool expect_read_secret_;
bool called_;
};
TEST_F(TlsConnectTest, KeyUpdateSecrets) {
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
Connect();
// The update is to the client write secret; the server read secret.
KeyUpdateTracker c(client_, false);
KeyUpdateTracker s(server_, true);
EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_FALSE));
SendReceive(50);
SendReceive(60);
CheckEpochs(4, 3);
c.CheckCalled();
s.CheckCalled();
}
// BadPrSocket is an instance of a PR IO layer that crashes the test if it is
// ever used for reading or writing. It does that by failing to overwrite any
// of the DummyIOLayerMethods, which all crash when invoked.
class BadPrSocket : public DummyIOLayerMethods {
public:
BadPrSocket(std::shared_ptr<TlsAgent>& agent) : DummyIOLayerMethods() {
static PRDescIdentity bad_identity = PR_GetUniqueIdentity("bad NSPR id");
fd_ = DummyIOLayerMethods::CreateFD(bad_identity, this);
// This is terrible, but NSPR doesn't provide an easy way to replace the
// bottom layer of an IO stack. Take the DummyPrSocket and replace its
// NSPR method vtable with the ones from this object.
dummy_layer_ =
PR_GetIdentitiesLayer(agent->ssl_fd(), DummyPrSocket::LayerId());
original_methods_ = dummy_layer_->methods;
original_secret_ = dummy_layer_->secret;
dummy_layer_->methods = fd_->methods;
dummy_layer_->secret = reinterpret_cast<PRFilePrivate*>(this);
}
// This will be destroyed before the agent, so we need to restore the state
// before we tampered with it.
virtual ~BadPrSocket() {
dummy_layer_->methods = original_methods_;
dummy_layer_->secret = original_secret_;
}
private:
ScopedPRFileDesc fd_;
PRFileDesc* dummy_layer_;
const PRIOMethods* original_methods_;
PRFilePrivate* original_secret_;
};
class StagedRecords {
public:
StagedRecords(std::shared_ptr<TlsAgent>& agent) : agent_(agent), records_() {
EXPECT_EQ(SECSuccess,
SSL_RecordLayerWriteCallback(
agent_->ssl_fd(), StagedRecords::StageRecordData, this));
}
virtual ~StagedRecords() {
// Uninstall so that the callback doesn't fire during cleanup.
EXPECT_EQ(SECSuccess,
SSL_RecordLayerWriteCallback(agent_->ssl_fd(), nullptr, nullptr));
}
bool empty() const { return records_.empty(); }
void ForwardAll(std::shared_ptr<TlsAgent>& peer) {
EXPECT_NE(agent_, peer) << "can't forward to self";
for (auto r : records_) {
r.Forward(peer);
}
records_.clear();
}
// This forwards all saved data and checks the resulting state.
void ForwardAll(std::shared_ptr<TlsAgent>& peer,
TlsAgent::State expected_state) {
ForwardAll(peer);
switch (expected_state) {
case TlsAgent::STATE_CONNECTED:
// The handshake callback should have been called, so check that before
// checking that SSL_ForceHandshake succeeds.
EXPECT_EQ(expected_state, peer->state());
EXPECT_EQ(SECSuccess, SSL_ForceHandshake(peer->ssl_fd()));
break;
case TlsAgent::STATE_CONNECTING:
// Check that SSL_ForceHandshake() blocks.
EXPECT_EQ(SECFailure, SSL_ForceHandshake(peer->ssl_fd()));
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
// Update and check the state.
peer->Handshake();
EXPECT_EQ(TlsAgent::STATE_CONNECTING, peer->state());
break;
default:
ADD_FAILURE() << "No idea how to handle this state";
}
}
void ForwardPartial(std::shared_ptr<TlsAgent>& peer) {
if (records_.empty()) {
ADD_FAILURE() << "No records to slice";
return;
}
auto& last = records_.back();
auto tail = last.SliceTail();
ForwardAll(peer, TlsAgent::STATE_CONNECTING);
records_.push_back(tail);
EXPECT_EQ(TlsAgent::STATE_CONNECTING, peer->state());
}
private:
// A single record.
class StagedRecord {
public:
StagedRecord(const std::string role, uint16_t epoch, SSLContentType ct,
const uint8_t* data, size_t len)
: role_(role), epoch_(epoch), content_type_(ct), data_(data, len) {
if (g_ssl_gtest_verbose) {
std::cerr << role_ << ": staged epoch " << epoch_ << " "
<< content_type_ << ": " << data_ << std::endl;
}
}
// This forwards staged data to the identified agent.
void Forward(std::shared_ptr<TlsAgent>& peer) {
// Now there should be staged data.
EXPECT_FALSE(data_.empty());
if (g_ssl_gtest_verbose) {
std::cerr << role_ << ": forward " << data_ << std::endl;
}
EXPECT_EQ(SECSuccess,
SSL_RecordLayerData(peer->ssl_fd(), epoch_, content_type_,
data_.data(),
static_cast<unsigned int>(data_.len())));
}
// Slices the tail off this record and returns it.
StagedRecord SliceTail() {
size_t slice = 1;
if (data_.len() <= slice) {
ADD_FAILURE() << "record too small to slice in two";
slice = 0;
}
size_t keep = data_.len() - slice;
StagedRecord tail(role_, epoch_, content_type_, data_.data() + keep,
slice);
data_.Truncate(keep);
return tail;
}
private:
std::string role_;
uint16_t epoch_;
SSLContentType content_type_;
DataBuffer data_;
};
// This is an SSLRecordWriteCallback that stages data.
static SECStatus StageRecordData(PRFileDesc* fd, PRUint16 epoch,
SSLContentType content_type,
const PRUint8* data, unsigned int len,
void* arg) {
auto stage = reinterpret_cast<StagedRecords*>(arg);
stage->records_.push_back(StagedRecord(stage->agent_->role_str(), epoch,
content_type, data,
static_cast<size_t>(len)));
return SECSuccess;
}
std::shared_ptr<TlsAgent>& agent_;
std::deque<StagedRecord> records_;
};
// Attempting to feed application data in before the handshake is complete
// should be caught.
static void RefuseApplicationData(std::shared_ptr<TlsAgent>& peer,
uint16_t epoch) {
static const uint8_t d[] = {1, 2, 3};
EXPECT_EQ(SECFailure,
SSL_RecordLayerData(peer->ssl_fd(), epoch, ssl_ct_application_data,
d, static_cast<unsigned int>(sizeof(d))));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
static void SendForwardReceive(std::shared_ptr<TlsAgent>& sender,
StagedRecords& sender_stage,
std::shared_ptr<TlsAgent>& receiver) {
const size_t count = 10;
sender->SendData(count, count);
sender_stage.ForwardAll(receiver);
receiver->ReadBytes(count);
}
TEST_P(TlsConnectStream, ReplaceRecordLayer) {
StartConnect();
client_->SetServerKeyBits(server_->server_key_bits());
// BadPrSocket installs an IO layer that crashes when the SSL layer attempts
// to read or write.
BadPrSocket bad_layer_client(client_);
BadPrSocket bad_layer_server(server_);
// StagedRecords installs a handler for unprotected data from the socket, and
// captures that data.
StagedRecords client_stage(client_);
StagedRecords server_stage(server_);
// Both peers should refuse application data from epoch 0.
RefuseApplicationData(client_, 0);
RefuseApplicationData(server_, 0);
// This first call forwards nothing, but it causes the client to handshake,
// which starts things off. This stages the ClientHello as a result.
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
// This processes the ClientHello and stages the first server flight.
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
RefuseApplicationData(server_, 1);
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
// Process the server flight and the client is done.
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
} else {
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
RefuseApplicationData(client_, 1);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
}
CheckKeys();
// Reading and writing application data should work.
SendForwardReceive(client_, client_stage, server_);
SendForwardReceive(server_, server_stage, client_);
}
static SECStatus AuthCompleteBlock(TlsAgent*, PRBool, PRBool) {
return SECWouldBlock;
}
TEST_P(TlsConnectStream, ReplaceRecordLayerAsyncLateAuth) {
StartConnect();
client_->SetServerKeyBits(server_->server_key_bits());
BadPrSocket bad_layer_client(client_);
BadPrSocket bad_layer_server(server_);
StagedRecords client_stage(client_);
StagedRecords server_stage(server_);
client_->SetAuthCertificateCallback(AuthCompleteBlock);
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
// Prior to TLS 1.3, the client sends its second flight immediately. But in
// TLS 1.3, a client won't send a Finished until it is happy with the server
// certificate. So blocking certificate validation causes the client to send
// nothing.
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
ASSERT_TRUE(client_stage.empty());
// Client should have stopped reading when it saw the Certificate message,
// so it will be reading handshake epoch, and writing cleartext.
client_->CheckEpochs(2, 0);
// Server should be reading handshake, and writing application data.
server_->CheckEpochs(2, 3);
// Handshake again and the client will read the remainder of the server's
// flight, but it will remain blocked.
client_->Handshake();
ASSERT_TRUE(client_stage.empty());
EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
} else {
// In prior versions, the client's second flight is always sent.
ASSERT_FALSE(client_stage.empty());
}
// Now declare the certificate good.
EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0));
client_->Handshake();
ASSERT_FALSE(client_stage.empty());
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
} else {
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
}
CheckKeys();
// Reading and writing application data should work.
SendForwardReceive(client_, client_stage, server_);
}
TEST_F(TlsConnectStreamTls13, ReplaceRecordLayerAsyncPostHandshake) {
StartConnect();
client_->SetServerKeyBits(server_->server_key_bits());
BadPrSocket bad_layer_client(client_);
BadPrSocket bad_layer_server(server_);
StagedRecords client_stage(client_);
StagedRecords server_stage(server_);
client_->SetAuthCertificateCallback(AuthCompleteBlock);
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
ASSERT_TRUE(client_stage.empty());
client_->Handshake();
ASSERT_TRUE(client_stage.empty());
EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
// Now declare the certificate good.
EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0));
client_->Handshake();
ASSERT_FALSE(client_stage.empty());
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
} else {
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
}
CheckKeys();
// Reading and writing application data should work.
SendForwardReceive(client_, client_stage, server_);
// Post-handshake messages should work here.
EXPECT_EQ(SECSuccess, SSL_SendSessionTicket(server_->ssl_fd(), nullptr, 0));
SendForwardReceive(server_, server_stage, client_);
}
// This test ensures that data is correctly forwarded when the handshake is
// resumed after asynchronous server certificate authentication, when
// SSL_AuthCertificateComplete() is called. The logic for resuming the
// handshake involves a different code path than the usual one, so this test
// exercises that code fully.
TEST_F(TlsConnectStreamTls13, ReplaceRecordLayerAsyncEarlyAuth) {
StartConnect();
client_->SetServerKeyBits(server_->server_key_bits());
BadPrSocket bad_layer_client(client_);
BadPrSocket bad_layer_server(server_);
StagedRecords client_stage(client_);
StagedRecords server_stage(server_);
client_->SetAuthCertificateCallback(AuthCompleteBlock);
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTING);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTING);
// Send a partial flight on to the client.
// This includes enough to trigger the certificate callback.
server_stage.ForwardPartial(client_);
EXPECT_TRUE(client_stage.empty());
// Declare the certificate good.
EXPECT_EQ(SECSuccess, SSL_AuthCertificateComplete(client_->ssl_fd(), 0));
client_->Handshake();
EXPECT_TRUE(client_stage.empty());
// Send the remainder of the server flight.
PRBool pending = PR_FALSE;
EXPECT_EQ(SECSuccess,
SSLInt_HasPendingHandshakeData(client_->ssl_fd(), &pending));
EXPECT_EQ(PR_TRUE, pending);
EXPECT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
server_stage.ForwardAll(client_, TlsAgent::STATE_CONNECTED);
client_stage.ForwardAll(server_, TlsAgent::STATE_CONNECTED);
CheckKeys();
SendForwardReceive(server_, server_stage, client_);
}
TEST_P(TlsConnectStream, ForwardDataFromWrongEpoch) {
const uint8_t data[] = {1};
Connect();
uint16_t next_epoch;
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
EXPECT_EQ(SECFailure,
SSL_RecordLayerData(client_->ssl_fd(), 2, ssl_ct_application_data,
data, sizeof(data)));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError())
<< "Passing data from an old epoch is rejected";
next_epoch = 4;
} else {
// Prior to TLS 1.3, the epoch is only updated once during the handshake.
next_epoch = 2;
}
EXPECT_EQ(SECFailure,
SSL_RecordLayerData(client_->ssl_fd(), next_epoch,
ssl_ct_application_data, data, sizeof(data)));
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError())
<< "Passing data from a future epoch blocks";
}
TEST_F(TlsConnectStreamTls13, ForwardInvalidData) {
const uint8_t data[1] = {0};
EnsureTlsSetup();
// Zero-length data.
EXPECT_EQ(SECFailure, SSL_RecordLayerData(client_->ssl_fd(), 0,
ssl_ct_application_data, data, 0));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// NULL data.
EXPECT_EQ(SECFailure,
SSL_RecordLayerData(client_->ssl_fd(), 0, ssl_ct_application_data,
nullptr, 1));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
TEST_F(TlsConnectDatagram13, ForwardDataDtls) {
EnsureTlsSetup();
const uint8_t data[1] = {0};
EXPECT_EQ(SECFailure,
SSL_RecordLayerData(client_->ssl_fd(), 0, ssl_ct_application_data,
data, sizeof(data)));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
} // namespace nss_test

View file

@ -123,9 +123,11 @@ TEST_P(TlsConnectGeneric, RecordSizeMaximum) {
EnsureTlsSetup();
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
client_max->EnableDecryption();
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
server_max->EnableDecryption();
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
client_max->EnableDecryption();
server_max->EnableDecryption();
}
Connect();
client_->SendData(send_size, send_size);
@ -140,7 +142,9 @@ TEST_P(TlsConnectGeneric, RecordSizeMaximum) {
TEST_P(TlsConnectGeneric, RecordSizeMinimumClient) {
EnsureTlsSetup();
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
server_max->EnableDecryption();
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
server_max->EnableDecryption();
}
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
@ -152,7 +156,9 @@ TEST_P(TlsConnectGeneric, RecordSizeMinimumClient) {
TEST_P(TlsConnectGeneric, RecordSizeMinimumServer) {
EnsureTlsSetup();
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
client_max->EnableDecryption();
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
client_max->EnableDecryption();
}
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
@ -164,9 +170,11 @@ TEST_P(TlsConnectGeneric, RecordSizeMinimumServer) {
TEST_P(TlsConnectGeneric, RecordSizeAsymmetric) {
EnsureTlsSetup();
auto client_max = MakeTlsFilter<TlsRecordMaximum>(client_);
client_max->EnableDecryption();
auto server_max = MakeTlsFilter<TlsRecordMaximum>(server_);
server_max->EnableDecryption();
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
client_max->EnableDecryption();
server_max->EnableDecryption();
}
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 100);
@ -256,9 +264,11 @@ class TlsRecordPadder : public TlsRecordFilter {
return KEEP;
}
uint16_t protection_epoch;
uint8_t inner_content_type;
DataBuffer plaintext;
if (!Unprotect(header, record, &inner_content_type, &plaintext)) {
if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
&plaintext)) {
return KEEP;
}
@ -267,8 +277,8 @@ class TlsRecordPadder : public TlsRecordFilter {
}
DataBuffer ciphertext;
bool ok =
Protect(header, inner_content_type, plaintext, &ciphertext, padding_);
bool ok = Protect(spec(protection_epoch), header, inner_content_type,
plaintext, &ciphertext, padding_);
EXPECT_TRUE(ok);
if (!ok) {
return KEEP;
@ -334,7 +344,9 @@ TEST_P(TlsConnectGeneric, RecordSizeCapExtensionClient) {
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 16385);
auto capture =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_record_size_limit_xtn);
capture->EnableDecryption();
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
capture->EnableDecryption();
}
Connect();
uint64_t val = 0;
@ -352,7 +364,9 @@ TEST_P(TlsConnectGeneric, RecordSizeCapExtensionServer) {
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 16385);
auto capture =
MakeTlsFilter<TlsExtensionCapture>(server_, ssl_record_size_limit_xtn);
capture->EnableDecryption();
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
capture->EnableDecryption();
}
Connect();
uint64_t val = 0;
@ -393,10 +407,24 @@ TEST_P(TlsConnectGeneric, RecordSizeServerExtensionInvalid) {
static const uint8_t v[] = {0xf4, 0x1f};
auto replace = MakeTlsFilter<TlsExtensionReplacer>(
server_, ssl_record_size_limit_xtn, DataBuffer(v, sizeof(v)));
replace->EnableDecryption();
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
replace->EnableDecryption();
}
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
}
TEST_P(TlsConnectGeneric, RecordSizeServerExtensionExtra) {
EnsureTlsSetup();
server_->SetOption(SSL_RECORD_SIZE_LIMIT, 1000);
static const uint8_t v[] = {0x01, 0x00, 0x00};
auto replace = MakeTlsFilter<TlsExtensionReplacer>(
server_, ssl_record_size_limit_xtn, DataBuffer(v, sizeof(v)));
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
replace->EnableDecryption();
}
ConnectExpectAlert(client_, kTlsAlertDecodeError);
}
class RecordSizeDefaultsTest : public ::testing::Test {
public:
void SetUp() {

View file

@ -214,12 +214,6 @@ class TestPolicyVersionRange
ASSERT_EQ(SECSuccess, rv);
rv = NSS_OptionSet(NSS_DTLS_VERSION_MAX_POLICY, saved_max_dtls_);
ASSERT_EQ(SECSuccess, rv);
// If it wasn't set initially, clear the bit that we set.
if (!(saved_algorithm_policy_ & NSS_USE_POLICY_IN_SSL)) {
rv = NSS_SetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY, 0,
NSS_USE_POLICY_IN_SSL);
ASSERT_EQ(SECSuccess, rv);
}
}
private:
@ -233,16 +227,12 @@ class TestPolicyVersionRange
ASSERT_EQ(SECSuccess, rv);
rv = NSS_OptionGet(NSS_DTLS_VERSION_MAX_POLICY, &saved_max_dtls_);
ASSERT_EQ(SECSuccess, rv);
rv = NSS_GetAlgorithmPolicy(SEC_OID_APPLY_SSL_POLICY,
&saved_algorithm_policy_);
ASSERT_EQ(SECSuccess, rv);
}
int32_t saved_min_tls_;
int32_t saved_max_tls_;
int32_t saved_min_dtls_;
int32_t saved_max_dtls_;
uint32_t saved_algorithm_policy_;
};
VersionPolicy saved_version_policy_;

View file

@ -25,10 +25,13 @@ namespace nss_test {
if (g_ssl_gtest_verbose) LOG(a); \
} while (false)
PRDescIdentity DummyPrSocket::LayerId() {
static PRDescIdentity id = PR_GetUniqueIdentity("dummysocket");
return id;
}
ScopedPRFileDesc DummyPrSocket::CreateFD() {
static PRDescIdentity test_fd_identity =
PR_GetUniqueIdentity("testtransportadapter");
return DummyIOLayerMethods::CreateFD(test_fd_identity, this);
return DummyIOLayerMethods::CreateFD(DummyPrSocket::LayerId(), this);
}
void DummyPrSocket::Reset() {
@ -136,19 +139,18 @@ int32_t DummyPrSocket::Write(PRFileDesc *f, const void *buf, int32_t length) {
DataBuffer filtered;
PacketFilter::Action action = PacketFilter::KEEP;
if (filter_) {
LOGV("Original packet: " << packet);
action = filter_->Process(packet, &filtered);
}
switch (action) {
case PacketFilter::CHANGE:
LOG("Original packet: " << packet);
LOG("Filtered packet: " << filtered);
dst->PacketReceived(filtered);
break;
case PacketFilter::DROP:
LOG("Droppped packet: " << packet);
LOG("Drop packet");
break;
case PacketFilter::KEEP:
LOGV("Packet: " << packet);
dst->PacketReceived(packet);
break;
}

View file

@ -68,6 +68,8 @@ class DummyPrSocket : public DummyIOLayerMethods {
write_error_(0) {}
virtual ~DummyPrSocket() {}
static PRDescIdentity LayerId();
// Create a file descriptor that will reference this object. The fd must not
// live longer than this adapter; call PR_Close() before.
ScopedPRFileDesc CreateFD();

View file

@ -640,6 +640,16 @@ void TlsAgent::CheckAlpn(SSLNextProtoState expected_state,
}
}
void TlsAgent::CheckEpochs(uint16_t expected_read,
uint16_t expected_write) const {
uint16_t read_epoch = 0;
uint16_t write_epoch = 0;
EXPECT_EQ(SECSuccess,
SSL_GetCurrentEpoch(ssl_fd(), &read_epoch, &write_epoch));
EXPECT_EQ(expected_read, read_epoch) << role_str() << " read epoch";
EXPECT_EQ(expected_write, write_epoch) << role_str() << " write epoch";
}
void TlsAgent::EnableSrtp() {
EXPECT_TRUE(EnsureTlsSetup());
const uint16_t ciphers[] = {SRTP_AES128_CM_HMAC_SHA1_80,

View file

@ -139,6 +139,7 @@ class TlsAgent : public PollTarget {
const std::string& expected = "") const;
void EnableSrtp();
void CheckSrtp() const;
void CheckEpochs(uint16_t expected_read, uint16_t expected_write) const;
void CheckErrorCode(int32_t expected) const;
void WaitForErrorCode(int32_t expected, uint32_t delay) const;
// Send data on the socket, encrypting it.

View file

@ -167,18 +167,8 @@ void TlsConnectTestBase::CheckShares(
void TlsConnectTestBase::CheckEpochs(uint16_t client_epoch,
uint16_t server_epoch) const {
uint16_t read_epoch = 0;
uint16_t write_epoch = 0;
EXPECT_EQ(SECSuccess,
SSLInt_GetEpochs(client_->ssl_fd(), &read_epoch, &write_epoch));
EXPECT_EQ(server_epoch, read_epoch) << "client read epoch";
EXPECT_EQ(client_epoch, write_epoch) << "client write epoch";
EXPECT_EQ(SECSuccess,
SSLInt_GetEpochs(server_->ssl_fd(), &read_epoch, &write_epoch));
EXPECT_EQ(client_epoch, read_epoch) << "server read epoch";
EXPECT_EQ(server_epoch, write_epoch) << "server write epoch";
client_->CheckEpochs(server_epoch, client_epoch);
server_->CheckEpochs(client_epoch, server_epoch);
}
void TlsConnectTestBase::ClearStats() {
@ -193,12 +183,33 @@ void TlsConnectTestBase::ClearServerCache() {
SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
}
void TlsConnectTestBase::SaveAlgorithmPolicy() {
saved_policies_.clear();
for (auto it = algorithms_.begin(); it != algorithms_.end(); ++it) {
uint32_t policy;
SECStatus rv = NSS_GetAlgorithmPolicy(*it, &policy);
ASSERT_EQ(SECSuccess, rv);
saved_policies_.push_back(std::make_tuple(*it, policy));
}
}
void TlsConnectTestBase::RestoreAlgorithmPolicy() {
for (auto it = saved_policies_.begin(); it != saved_policies_.end(); ++it) {
auto algorithm = std::get<0>(*it);
auto policy = std::get<1>(*it);
SECStatus rv = NSS_SetAlgorithmPolicy(
algorithm, policy, NSS_USE_POLICY_IN_SSL | NSS_USE_ALG_IN_SSL_KX);
ASSERT_EQ(SECSuccess, rv);
}
}
void TlsConnectTestBase::SetUp() {
SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
SSLInt_ClearSelfEncryptKey();
SSLInt_SetTicketLifetime(30);
SSL_SetupAntiReplay(1 * PR_USEC_PER_SEC, 1, 3);
ClearStats();
SaveAlgorithmPolicy();
Init();
}
@ -209,6 +220,7 @@ void TlsConnectTestBase::TearDown() {
SSL_ClearSessionCache();
SSLInt_ClearSelfEncryptKey();
SSL_ShutdownServerSessionIDCache();
RestoreAlgorithmPolicy();
}
void TlsConnectTestBase::Init() {

View file

@ -132,6 +132,9 @@ class TlsConnectTestBase : public ::testing::Test {
// Move the DTLS timers for both endpoints to pop the next timer.
void ShiftDtlsTimers();
void SaveAlgorithmPolicy();
void RestoreAlgorithmPolicy();
protected:
SSLProtocolVariant variant_;
std::shared_ptr<TlsAgent> client_;
@ -149,6 +152,14 @@ class TlsConnectTestBase : public ::testing::Test {
// NSS will move this final entry to the front when used with ALPN.
const uint8_t alpn_dummy_val_[4] = {0x01, 0x62, 0x01, 0x61};
// A list of algorithm IDs whose policies need to be preserved
// around test cases. In particular, DSA is checked in
// ssl_extension_unittest.cc.
const std::vector<SECOidTag> algorithms_ = {SEC_OID_APPLY_SSL_POLICY,
SEC_OID_ANSIX9_DSA_SIGNATURE,
SEC_OID_CURVE25519};
std::vector<std::tuple<SECOidTag, uint32_t>> saved_policies_;
private:
void CheckResumption(SessionResumptionMode expected);
void CheckExtendedMasterSecret();

View file

@ -305,11 +305,14 @@ TEST_P(TlsConnectTls13, ConnectEsniHrr) {
server_, kTlsHandshakeHelloRetryRequest);
auto filter =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
auto cfilter =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
auto filter2 =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn, true);
client_->SetFilter(std::make_shared<ChainedPacketFilter>(
ChainedPacketFilterInit({filter, filter2})));
server_->SetSniCallback(SniCallback);
Connect();
CheckSniExtension(cfilter->extension());
CheckSniExtension(filter->extension());
CheckSniExtension(filter2->extension());
EXPECT_NE(0UL, hrr_capture->buffer().len());
}

View file

@ -45,40 +45,65 @@ void TlsVersioned::WriteStream(std::ostream& stream) const {
}
}
void TlsRecordFilter::EnableDecryption() {
SSLInt_SetCipherSpecChangeFunc(agent()->ssl_fd(), CipherSpecChanged,
(void*)this);
TlsRecordFilter::TlsRecordFilter(const std::shared_ptr<TlsAgent>& a)
: agent_(a) {
cipher_specs_.emplace_back(a->variant() == ssl_variant_datagram, 0);
}
void TlsRecordFilter::CipherSpecChanged(void* arg, PRBool sending,
ssl3CipherSpec* newSpec) {
TlsRecordFilter* self = static_cast<TlsRecordFilter*>(arg);
PRBool isServer = self->agent()->role() == TlsAgent::SERVER;
void TlsRecordFilter::EnableDecryption() {
EXPECT_EQ(SECSuccess,
SSL_SecretCallback(agent()->ssl_fd(), SecretCallback, this));
decrypting_ = true;
}
void TlsRecordFilter::SecretCallback(PRFileDesc* fd, PRUint16 epoch,
SSLSecretDirection dir, PK11SymKey* secret,
void* arg) {
TlsRecordFilter* self = static_cast<TlsRecordFilter*>(arg);
if (g_ssl_gtest_verbose) {
std::cerr << (isServer ? "server" : "client") << ": "
<< (sending ? "send" : "receive")
<< " cipher spec changed: " << newSpec->epoch << " ("
<< newSpec->phase << ")" << std::endl;
std::cerr << self->agent()->role_str() << ": " << dir
<< " secret changed for epoch " << epoch << std::endl;
}
if (!sending) {
if (dir == ssl_secret_read) {
return;
}
uint64_t seq_no;
if (self->agent()->variant() == ssl_variant_datagram) {
seq_no = static_cast<uint64_t>(SSLInt_CipherSpecToEpoch(newSpec)) << 48;
} else {
seq_no = 0;
for (auto& spec : self->cipher_specs_) {
ASSERT_NE(spec.epoch(), epoch) << "duplicate spec for epoch " << epoch;
}
self->in_sequence_number_ = seq_no;
self->out_sequence_number_ = seq_no;
self->dropped_record_ = false;
self->cipher_spec_.reset(new TlsCipherSpec());
bool ret = self->cipher_spec_->Init(
SSLInt_CipherSpecToEpoch(newSpec), SSLInt_CipherSpecToAlgorithm(newSpec),
SSLInt_CipherSpecToKey(newSpec), SSLInt_CipherSpecToIv(newSpec));
EXPECT_EQ(true, ret);
SSLPreliminaryChannelInfo preinfo;
EXPECT_EQ(SECSuccess,
SSL_GetPreliminaryChannelInfo(self->agent()->ssl_fd(), &preinfo,
sizeof(preinfo)));
EXPECT_EQ(sizeof(preinfo), preinfo.length);
// Check the version.
if (preinfo.valuesSet & ssl_preinfo_version) {
EXPECT_EQ(SSL_LIBRARY_VERSION_TLS_1_3, preinfo.protocolVersion);
} else {
EXPECT_EQ(1U, epoch);
}
uint16_t suite;
if (epoch == 1) {
// 0-RTT
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_0rtt_cipher_suite);
suite = preinfo.zeroRttCipherSuite;
} else {
EXPECT_TRUE(preinfo.valuesSet & ssl_preinfo_cipher_suite);
suite = preinfo.cipherSuite;
}
SSLCipherSuiteInfo cipherinfo;
EXPECT_EQ(SECSuccess,
SSL_GetCipherSuiteInfo(suite, &cipherinfo, sizeof(cipherinfo)));
EXPECT_EQ(sizeof(cipherinfo), cipherinfo.length);
bool is_dtls = self->agent()->variant() == ssl_variant_datagram;
self->cipher_specs_.emplace_back(is_dtls, epoch);
EXPECT_TRUE(self->cipher_specs_.back().SetKeys(&cipherinfo, secret));
}
bool TlsRecordFilter::is_dtls13() const {
@ -95,6 +120,23 @@ bool TlsRecordFilter::is_dtls13() const {
info.canSendEarlyData;
}
// Gets the cipher spec that matches the specified epoch.
TlsCipherSpec& TlsRecordFilter::spec(uint16_t write_epoch) {
for (auto& sp : cipher_specs_) {
if (sp.epoch() == write_epoch) {
return sp;
}
}
// If we aren't decrypting, provide a cipher spec that does nothing other than
// count sequence numbers.
EXPECT_FALSE(decrypting_) << "No spec available for epoch " << write_epoch;
;
bool is_dtls = agent()->variant() == ssl_variant_datagram;
cipher_specs_.emplace_back(is_dtls, write_epoch);
return cipher_specs_.back();
}
PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
DataBuffer* output) {
// Disable during shutdown.
@ -108,34 +150,28 @@ PacketFilter::Action TlsRecordFilter::Filter(const DataBuffer& input,
output->Allocate(input.len());
TlsParser parser(input);
// This uses the current write spec for the purposes of parsing the epoch and
// sequence number from the header. This might be wrong because we can
// receive records from older specs, but guessing is good enough:
// - In DTLS, parsing the sequence number corrects any errors.
// - In TLS, we don't use the sequence number unless decrypting, where we use
// trial decryption to get the right epoch.
uint16_t write_epoch = 0;
SECStatus rv = SSL_GetCurrentEpoch(agent()->ssl_fd(), nullptr, &write_epoch);
if (rv != SECSuccess) {
ADD_FAILURE() << "unable to read epoch";
return KEEP;
}
uint64_t guess_seqno = static_cast<uint64_t>(write_epoch) << 48;
while (parser.remaining()) {
TlsRecordHeader header;
DataBuffer record;
if (!header.Parse(is_dtls13(), in_sequence_number_, &parser, &record)) {
if (!header.Parse(is_dtls13(), guess_seqno, &parser, &record)) {
ADD_FAILURE() << "not a valid record";
return KEEP;
}
// Track the sequence number, which is necessary for stream mode when
// decrypting and for TLS 1.3 datagram to recover the sequence number.
//
// We reset the counter when the cipher spec changes, but that notification
// appears before a record is sent. If multiple records are sent with
// different cipher specs, this would fail. This filters out cleartext
// records, so we don't get confused by handshake messages that are sent at
// the same time as encrypted records. Sequence numbers are therefore
// likely to be incorrect for cleartext records.
//
// This isn't perfectly robust: if there is a change from an active cipher
// spec to another active cipher spec (KeyUpdate for instance) AND writes
// are consolidated across that change, this code could use the wrong
// sequence numbers when re-encrypting records with the old keys.
if (header.content_type() == ssl_ct_application_data) {
in_sequence_number_ =
(std::max)(in_sequence_number_, header.sequence_number() + 1);
}
if (FilterRecord(header, record, &offset, output) != KEEP) {
changed = true;
} else {
@ -159,14 +195,16 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
DataBuffer filtered;
uint8_t inner_content_type;
DataBuffer plaintext;
uint16_t protection_epoch = 0;
if (!Unprotect(header, record, &inner_content_type, &plaintext)) {
if (g_ssl_gtest_verbose) {
std::cerr << "unprotect failed: " << header << ":" << record << std::endl;
}
if (!Unprotect(header, record, &protection_epoch, &inner_content_type,
&plaintext)) {
std::cerr << agent()->role_str() << ": unprotect failed: " << header << ":"
<< record << std::endl;
return KEEP;
}
auto& protection_spec = spec(protection_epoch);
TlsRecordHeader real_header(header.variant(), header.version(),
inner_content_type, header.sequence_number());
@ -174,7 +212,9 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
// In stream mode, even if something doesn't change we need to re-encrypt if
// previous packets were dropped.
if (action == KEEP) {
if (header.is_dtls() || !dropped_record_) {
if (header.is_dtls() || !protection_spec.record_dropped()) {
// Count every outgoing packet.
protection_spec.RecordProtected();
return KEEP;
}
filtered = plaintext;
@ -182,7 +222,7 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
if (action == DROP) {
std::cerr << "record drop: " << header << ":" << record << std::endl;
dropped_record_ = true;
protection_spec.RecordDropped();
return DROP;
}
@ -192,19 +232,18 @@ PacketFilter::Action TlsRecordFilter::FilterRecord(
std::cerr << "record new: " << filtered << std::endl;
}
uint64_t seq_num;
if (header.is_dtls() || !cipher_spec_ ||
header.content_type() != ssl_ct_application_data) {
seq_num = header.sequence_number();
} else {
seq_num = out_sequence_number_++;
uint64_t seq_num = protection_spec.next_out_seqno();
if (!decrypting_ && header.is_dtls()) {
// Copy over the epoch, which isn't tracked when not decrypting.
seq_num |= header.sequence_number() & (0xffffULL << 48);
}
TlsRecordHeader out_header(header.variant(), header.version(),
header.content_type(), seq_num);
DataBuffer ciphertext;
bool rv = Protect(out_header, inner_content_type, filtered, &ciphertext);
EXPECT_TRUE(rv);
bool rv = Protect(protection_spec, out_header, inner_content_type, filtered,
&ciphertext);
if (!rv) {
return KEEP;
}
@ -227,15 +266,20 @@ uint64_t TlsRecordHeader::RecoverSequenceNumber(uint64_t expected,
uint32_t partial,
size_t partial_bits) {
EXPECT_GE(32U, partial_bits);
uint64_t mask = (1 << partial_bits) - 1;
uint64_t mask = (1ULL << partial_bits) - 1;
// First we determine the highest possible value. This is half the
// expressible range above the expected value.
uint64_t cap = expected + (1ULL << (partial_bits - 1));
// expressible range above the expected value, less 1.
//
// We subtract the extra 1 from the cap so that when given a choice between
// the equidistant expected+N and expected-N we want to chose the lower. With
// 0-RTT, we sometimes have to recover an epoch of 1 when we expect an epoch
// of 3 and with 2 partial bits, the alternative result of 5 is wrong.
uint64_t cap = expected + (1ULL << (partial_bits - 1)) - 1;
// Add the partial piece in. e.g., xxxx789a and 1234 becomes xxxx1234.
uint64_t seq_no = (cap & ~mask) | partial;
// If the partial value is higher than the same partial piece from the cap,
// then the real value has to be lower. e.g., xxxx1234 can't become xxxx5678.
if (partial > (cap & mask)) {
if (partial > (cap & mask) && (seq_no >= (1ULL << partial_bits))) {
seq_no -= 1ULL << partial_bits;
}
return seq_no;
@ -375,16 +419,41 @@ size_t TlsRecordHeader::Write(DataBuffer* buffer, size_t offset,
bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
const DataBuffer& ciphertext,
uint16_t* protection_epoch,
uint8_t* inner_content_type,
DataBuffer* plaintext) {
if (!cipher_spec_ || header.content_type() != ssl_ct_application_data) {
if (!decrypting_ || header.content_type() != ssl_ct_application_data) {
// Maintain the epoch and sequence number for plaintext records.
uint16_t ep = 0;
if (agent()->variant() == ssl_variant_datagram) {
ep = static_cast<uint16_t>(header.sequence_number() >> 48);
}
spec(ep).RecordUnprotected(header.sequence_number());
*protection_epoch = ep;
*inner_content_type = header.content_type();
*plaintext = ciphertext;
return true;
}
if (!cipher_spec_->Unprotect(header, ciphertext, plaintext)) {
return false;
uint16_t ep = 0;
if (agent()->variant() == ssl_variant_datagram) {
ep = static_cast<uint16_t>(header.sequence_number() >> 48);
if (!spec(ep).Unprotect(header, ciphertext, plaintext)) {
return false;
}
} else {
// In TLS, records aren't clearly labelled with their epoch, and we
// can't just use the newest keys because the same flight of messages can
// contain multiple epochs. So... trial decrypt!
for (size_t i = cipher_specs_.size() - 1; i > 0; --i) {
if (cipher_specs_[i].Unprotect(header, ciphertext, plaintext)) {
ep = cipher_specs_[i].epoch();
break;
}
}
if (!ep) {
return false;
}
}
size_t len = plaintext->len();
@ -396,33 +465,45 @@ bool TlsRecordFilter::Unprotect(const TlsRecordHeader& header,
return false;
}
*protection_epoch = ep;
*inner_content_type = plaintext->data()[len - 1];
plaintext->Truncate(len - 1);
if (g_ssl_gtest_verbose) {
std::cerr << "unprotect: " << std::hex << header.sequence_number()
<< std::dec << " type=" << static_cast<int>(*inner_content_type)
std::cerr << agent()->role_str() << ": unprotect: epoch=" << ep
<< " seq=" << std::hex << header.sequence_number() << std::dec
<< " " << *plaintext << std::endl;
}
return true;
}
bool TlsRecordFilter::Protect(const TlsRecordHeader& header,
bool TlsRecordFilter::Protect(TlsCipherSpec& protection_spec,
const TlsRecordHeader& header,
uint8_t inner_content_type,
const DataBuffer& plaintext,
DataBuffer* ciphertext, size_t padding) {
if (!cipher_spec_ || header.content_type() != ssl_ct_application_data) {
if (!protection_spec.is_protected()) {
// Not protected, just keep the sequence numbers updated.
protection_spec.RecordProtected();
*ciphertext = plaintext;
return true;
}
if (g_ssl_gtest_verbose) {
std::cerr << "protect: " << header.sequence_number() << std::endl;
}
DataBuffer padded;
padded.Allocate(plaintext.len() + 1 + padding);
size_t offset = padded.Write(0, plaintext.data(), plaintext.len());
padded.Write(offset, inner_content_type, 1);
return cipher_spec_->Protect(header, padded, ciphertext);
bool ok = protection_spec.Protect(header, padded, ciphertext);
if (!ok) {
ADD_FAILURE() << "protect fail";
} else if (g_ssl_gtest_verbose) {
std::cerr << agent()->role_str()
<< ": protect: epoch=" << protection_spec.epoch()
<< " seq=" << std::hex << header.sequence_number() << std::dec
<< " " << *ciphertext << std::endl;
}
return ok;
}
bool IsHelloRetry(const DataBuffer& body) {

View file

@ -97,13 +97,7 @@ inline std::shared_ptr<T> MakeTlsFilter(const std::shared_ptr<TlsAgent>& agent,
// Abstract filter that operates on entire (D)TLS records.
class TlsRecordFilter : public PacketFilter {
public:
TlsRecordFilter(const std::shared_ptr<TlsAgent>& a)
: agent_(a),
count_(0),
cipher_spec_(),
dropped_record_(false),
in_sequence_number_(0),
out_sequence_number_(0) {}
TlsRecordFilter(const std::shared_ptr<TlsAgent>& a);
std::shared_ptr<TlsAgent> agent() const { return agent_.lock(); }
@ -118,10 +112,11 @@ class TlsRecordFilter : public PacketFilter {
// behavior.
void EnableDecryption();
bool Unprotect(const TlsRecordHeader& header, const DataBuffer& cipherText,
uint8_t* inner_content_type, DataBuffer* plaintext);
bool Protect(const TlsRecordHeader& header, uint8_t inner_content_type,
const DataBuffer& plaintext, DataBuffer* ciphertext,
size_t padding = 0);
uint16_t* protection_epoch, uint8_t* inner_content_type,
DataBuffer* plaintext);
bool Protect(TlsCipherSpec& protection_spec, const TlsRecordHeader& header,
uint8_t inner_content_type, const DataBuffer& plaintext,
DataBuffer* ciphertext, size_t padding = 0);
protected:
// There are two filter functions which can be overriden. Both are
@ -146,20 +141,17 @@ class TlsRecordFilter : public PacketFilter {
}
bool is_dtls13() const;
TlsCipherSpec& spec(uint16_t epoch);
private:
static void CipherSpecChanged(void* arg, PRBool sending,
ssl3CipherSpec* newSpec);
static void SecretCallback(PRFileDesc* fd, PRUint16 epoch,
SSLSecretDirection dir, PK11SymKey* secret,
void* arg);
std::weak_ptr<TlsAgent> agent_;
size_t count_;
std::unique_ptr<TlsCipherSpec> cipher_spec_;
// Whether we dropped a record since the cipher spec changed.
bool dropped_record_;
// The sequence number we use for reading records as they are written.
uint64_t in_sequence_number_;
// The sequence number we use for writing modified records.
uint64_t out_sequence_number_;
size_t count_ = 0;
std::vector<TlsCipherSpec> cipher_specs_;
bool decrypting_ = false;
};
inline std::ostream& operator<<(std::ostream& stream, const TlsVersioned& v) {

View file

@ -7,6 +7,9 @@
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sslproto.h"
#include "sslexp.h"
#include "tls13hkdf.h"
#include "databuffer.h"
@ -56,6 +59,39 @@ const size_t kHashLength[] = {
64, /* ssl_hash_sha512 */
};
size_t GetHashLength(SSLHashType hash) {
size_t i = static_cast<size_t>(hash);
if (i < PR_ARRAY_SIZE(kHashLength)) {
return kHashLength[i];
}
ADD_FAILURE() << "Unknown hash: " << hash;
return 0;
}
CK_MECHANISM_TYPE GetHkdfMech(SSLHashType hash) {
switch (hash) {
case ssl_hash_sha256:
return CKM_NSS_HKDF_SHA256;
case ssl_hash_sha384:
return CKM_NSS_HKDF_SHA384;
default:
ADD_FAILURE() << "Unknown hash: " << hash;
}
return CKM_INVALID_MECHANISM;
}
PRUint16 GetSomeCipherSuiteForHash(SSLHashType hash) {
switch (hash) {
case ssl_hash_sha256:
return TLS_AES_128_GCM_SHA256;
case ssl_hash_sha384:
return TLS_AES_256_GCM_SHA384;
default:
ADD_FAILURE() << "Unknown hash: " << hash;
}
return 0;
}
const std::string kHashName[] = {"None", "MD5", "SHA-1", "SHA-224",
"SHA-256", "SHA-384", "SHA-512"};
@ -64,7 +100,7 @@ static void ImportKey(ScopedPK11SymKey* to, const DataBuffer& key,
ASSERT_LT(hash_type, sizeof(kHashLength));
ASSERT_LE(kHashLength[hash_type], key.len());
SECItem key_item = {siBuffer, const_cast<uint8_t*>(key.data()),
static_cast<unsigned int>(kHashLength[hash_type])};
static_cast<unsigned int>(GetHashLength(hash_type))};
PK11SymKey* inner =
PK11_ImportSymKey(slot, CKM_SSL3_MASTER_KEY_DERIVE, PK11_OriginUnwrap,
@ -112,15 +148,19 @@ class TlsHkdfTest : public ::testing::Test,
ImportKey(&k2_, kKey2, hash_type_, slot_.get());
}
void VerifyKey(const ScopedPK11SymKey& key, const DataBuffer& expected) {
void VerifyKey(const ScopedPK11SymKey& key, CK_MECHANISM_TYPE expected_mech,
const DataBuffer& expected_value) {
EXPECT_EQ(expected_mech, PK11_GetMechanism(key.get()));
SECStatus rv = PK11_ExtractKeyValue(key.get());
ASSERT_EQ(SECSuccess, rv);
SECItem* key_data = PK11_GetKeyData(key.get());
ASSERT_NE(nullptr, key_data);
EXPECT_EQ(expected.len(), key_data->len);
EXPECT_EQ(0, memcmp(expected.data(), key_data->data, expected.len()));
EXPECT_EQ(expected_value.len(), key_data->len);
EXPECT_EQ(
0, memcmp(expected_value.data(), key_data->data, expected_value.len()));
}
void HkdfExtract(const ScopedPK11SymKey& ikmk1, const ScopedPK11SymKey& ikmk2,
@ -133,7 +173,15 @@ class TlsHkdfTest : public ::testing::Test,
ScopedPK11SymKey prkk(prk);
DumpKey("Output", prkk);
VerifyKey(prkk, expected);
VerifyKey(prkk, GetHkdfMech(base_hash), expected);
// Now test the public wrapper.
PRUint16 cs = GetSomeCipherSuiteForHash(base_hash);
rv = SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_3, cs, ikmk1.get(),
ikmk2.get(), &prk);
ASSERT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, prk);
VerifyKey(ScopedPK11SymKey(prk), GetHkdfMech(base_hash), expected);
}
void HkdfExpandLabel(ScopedPK11SymKey* prk, SSLHashType base_hash,
@ -150,6 +198,32 @@ class TlsHkdfTest : public ::testing::Test,
ASSERT_EQ(SECSuccess, rv);
DumpData("Output", &output[0], output.size());
EXPECT_EQ(0, memcmp(expected.data(), &output[0], expected.len()));
// Verify that the public API produces the same result.
PRUint16 cs = GetSomeCipherSuiteForHash(base_hash);
PK11SymKey* secret;
rv = SSL_HkdfExpandLabel(SSL_LIBRARY_VERSION_TLS_1_3, cs, prk->get(),
session_hash, session_hash_len, label, label_len,
&secret);
EXPECT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, prk);
VerifyKey(ScopedPK11SymKey(secret), GetHkdfMech(base_hash), expected);
// Verify that a key can be created with a different key type and size.
rv = SSL_HkdfExpandLabelWithMech(
SSL_LIBRARY_VERSION_TLS_1_3, cs, prk->get(), session_hash,
session_hash_len, label, label_len, CKM_DES3_CBC_PAD, 24, &secret);
EXPECT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, prk);
ScopedPK11SymKey with_mech(secret);
EXPECT_EQ(static_cast<CK_MECHANISM_TYPE>(CKM_DES3_CBC_PAD),
PK11_GetMechanism(with_mech.get()));
// Just verify that the key is the right size.
rv = PK11_ExtractKeyValue(with_mech.get());
ASSERT_EQ(SECSuccess, rv);
SECItem* key_data = PK11_GetKeyData(with_mech.get());
ASSERT_NE(nullptr, key_data);
EXPECT_EQ(24U, key_data->len);
}
protected:
@ -175,7 +249,7 @@ TEST_P(TlsHkdfTest, HkdfNullNull) {
0x10, 0xba, 0x18, 0xe2, 0x35, 0x7e, 0x71, 0x69, 0x71, 0xf9, 0x36, 0x2f,
0x2c, 0x2f, 0xe2, 0xa7, 0x6b, 0xfd, 0x78, 0xdf, 0xec, 0x4e, 0xa9, 0xb5}};
const DataBuffer expected_data(tv[hash_type_], kHashLength[hash_type_]);
const DataBuffer expected_data(tv[hash_type_], GetHashLength(hash_type_));
HkdfExtract(nullptr, nullptr, hash_type_, expected_data);
}
@ -193,7 +267,7 @@ TEST_P(TlsHkdfTest, HkdfKey1Only) {
0x57, 0xc2, 0x76, 0x9f, 0x3f, 0x83, 0x45, 0x2f, 0xf6, 0xf3, 0x56, 0x1f,
0x58, 0x63, 0xdb, 0x88, 0xda, 0x40, 0xce, 0x63, 0x7d, 0x24, 0x37, 0xf3}};
const DataBuffer expected_data(tv[hash_type_], kHashLength[hash_type_]);
const DataBuffer expected_data(tv[hash_type_], GetHashLength(hash_type_));
HkdfExtract(k1_, nullptr, hash_type_, expected_data);
}
@ -211,7 +285,7 @@ TEST_P(TlsHkdfTest, HkdfKey2Only) {
0xd4, 0x6a, 0xf6, 0xe5, 0xec, 0xea, 0xf8, 0x7d, 0x91, 0x71, 0x81, 0xf1,
0xdb, 0x3b, 0xaf, 0xbf, 0xde, 0x71, 0x61, 0x15, 0xeb, 0xb5, 0x5f, 0x68}};
const DataBuffer expected_data(tv[hash_type_], kHashLength[hash_type_]);
const DataBuffer expected_data(tv[hash_type_], GetHashLength(hash_type_));
HkdfExtract(nullptr, k2_, hash_type_, expected_data);
}
@ -229,7 +303,7 @@ TEST_P(TlsHkdfTest, HkdfKey1Key2) {
0x1c, 0x5b, 0x98, 0x0b, 0x02, 0x92, 0x3f, 0xfd, 0x73, 0x5a, 0x6f, 0x2a,
0x95, 0xa3, 0xee, 0xf6, 0xd6, 0x8e, 0x6f, 0x86, 0xea, 0x63, 0xf8, 0x33}};
const DataBuffer expected_data(tv[hash_type_], kHashLength[hash_type_]);
const DataBuffer expected_data(tv[hash_type_], GetHashLength(hash_type_));
HkdfExtract(k1_, k2_, hash_type_, expected_data);
}
@ -247,12 +321,122 @@ TEST_P(TlsHkdfTest, HkdfExpandLabel) {
0x74, 0xf7, 0x8b, 0x06, 0x38, 0x28, 0x06, 0x37, 0x75, 0x23, 0xa2, 0xb7,
0x34, 0xb1, 0x72, 0x2e, 0x59, 0x6d, 0x5a, 0x31, 0xf5, 0x53, 0xab, 0x99}};
const DataBuffer expected_data(tv[hash_type_], kHashLength[hash_type_]);
HkdfExpandLabel(&k1_, hash_type_, kSessionHash, kHashLength[hash_type_],
const DataBuffer expected_data(tv[hash_type_], GetHashLength(hash_type_));
HkdfExpandLabel(&k1_, hash_type_, kSessionHash, GetHashLength(hash_type_),
kLabelMasterSecret, strlen(kLabelMasterSecret),
expected_data);
}
TEST_P(TlsHkdfTest, HkdfExpandLabelNoHash) {
const uint8_t tv[][48] = {
{/* ssl_hash_none */},
{/* ssl_hash_md5 */},
{/* ssl_hash_sha1 */},
{/* ssl_hash_sha224 */},
{0xb7, 0x08, 0x00, 0xe3, 0x8e, 0x48, 0x68, 0x91, 0xb1, 0x0f, 0x5e,
0x6f, 0x22, 0x53, 0x6b, 0x84, 0x69, 0x75, 0xaa, 0xa3, 0x2a, 0xe7,
0xde, 0xaa, 0xc3, 0xd1, 0xb4, 0x05, 0x22, 0x5c, 0x68, 0xf5},
{0x13, 0xd3, 0x36, 0x9f, 0x3c, 0x78, 0xa0, 0x32, 0x40, 0xee, 0x16, 0xe9,
0x11, 0x12, 0x66, 0xc7, 0x51, 0xad, 0xd8, 0x3c, 0xa1, 0xa3, 0x97, 0x74,
0xd7, 0x45, 0xff, 0xa7, 0x88, 0x9e, 0x52, 0x17, 0x2e, 0xaa, 0x3a, 0xd2,
0x35, 0xd8, 0xd5, 0x35, 0xfd, 0x65, 0x70, 0x9f, 0xa9, 0xf9, 0xfa, 0x23}};
const DataBuffer expected_data(tv[hash_type_], GetHashLength(hash_type_));
HkdfExpandLabel(&k1_, hash_type_, nullptr, 0, kLabelMasterSecret,
strlen(kLabelMasterSecret), expected_data);
}
TEST_P(TlsHkdfTest, BadExtractWrapperInput) {
PK11SymKey* key = nullptr;
// Bad version.
EXPECT_EQ(SECFailure,
SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_2, TLS_AES_128_GCM_SHA256,
k1_.get(), k2_.get(), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Bad ciphersuite.
EXPECT_EQ(SECFailure,
SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_3, TLS_RSA_WITH_NULL_SHA,
k1_.get(), k2_.get(), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Old ciphersuite.
EXPECT_EQ(SECFailure, SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_RSA_WITH_AES_128_CBC_SHA, k1_.get(),
k2_.get(), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// NULL outparam..
EXPECT_EQ(SECFailure, SSL_HkdfExtract(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_RSA_WITH_AES_128_CBC_SHA, k1_.get(),
k2_.get(), nullptr));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(nullptr, key);
}
TEST_P(TlsHkdfTest, BadExpandLabelWrapperInput) {
PK11SymKey* key = nullptr;
static const char* kLabel = "label";
// Bad version.
EXPECT_EQ(
SECFailure,
SSL_HkdfExpandLabel(SSL_LIBRARY_VERSION_TLS_1_2, TLS_AES_128_GCM_SHA256,
k1_.get(), nullptr, 0, kLabel, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Bad ciphersuite.
EXPECT_EQ(
SECFailure,
SSL_HkdfExpandLabel(SSL_LIBRARY_VERSION_TLS_1_3, TLS_RSA_WITH_NULL_MD5,
k1_.get(), nullptr, 0, kLabel, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Old ciphersuite.
EXPECT_EQ(SECFailure,
SSL_HkdfExpandLabel(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_RSA_WITH_AES_128_CBC_SHA, k1_.get(),
nullptr, 0, kLabel, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Null PRK.
EXPECT_EQ(SECFailure, SSL_HkdfExpandLabel(
SSL_LIBRARY_VERSION_TLS_1_2, TLS_AES_128_GCM_SHA256,
nullptr, nullptr, 0, kLabel, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Null, non-zero-length handshake hash.
EXPECT_EQ(
SECFailure,
SSL_HkdfExpandLabel(SSL_LIBRARY_VERSION_TLS_1_2, TLS_AES_128_GCM_SHA256,
k1_.get(), nullptr, 2, kLabel, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Null, non-zero-length label.
EXPECT_EQ(SECFailure,
SSL_HkdfExpandLabel(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_AES_128_GCM_SHA256, k1_.get(), nullptr, 0,
nullptr, strlen(kLabel), &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Null, empty label.
EXPECT_EQ(SECFailure, SSL_HkdfExpandLabel(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_AES_128_GCM_SHA256, k1_.get(),
nullptr, 0, nullptr, 0, &key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Null key pointer..
EXPECT_EQ(SECFailure,
SSL_HkdfExpandLabel(SSL_LIBRARY_VERSION_TLS_1_3,
TLS_AES_128_GCM_SHA256, k1_.get(), nullptr, 0,
kLabel, strlen(kLabel), nullptr));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(nullptr, key);
}
static const SSLHashType kHashTypes[] = {ssl_hash_sha256, ssl_hash_sha384};
INSTANTIATE_TEST_CASE_P(AllHashFuncs, TlsHkdfTest,
::testing::ValuesIn(kHashTypes));

View file

@ -5,145 +5,98 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "tls_protect.h"
#include "sslproto.h"
#include "tls_filter.h"
namespace nss_test {
AeadCipher::~AeadCipher() {
if (key_) {
PK11_FreeSymKey(key_);
static uint64_t FirstSeqno(bool dtls, uint16_t epoc) {
if (dtls) {
return static_cast<uint64_t>(epoc) << 48;
}
return 0;
}
bool AeadCipher::Init(PK11SymKey *key, const uint8_t *iv) {
key_ = PK11_ReferenceSymKey(key);
if (!key_) return false;
TlsCipherSpec::TlsCipherSpec(bool dtls, uint16_t epoc)
: dtls_(dtls),
epoch_(epoc),
in_seqno_(FirstSeqno(dtls, epoc)),
out_seqno_(FirstSeqno(dtls, epoc)) {}
memcpy(iv_, iv, sizeof(iv_));
bool TlsCipherSpec::SetKeys(SSLCipherSuiteInfo* cipherinfo,
PK11SymKey* secret) {
SSLAeadContext* ctx;
SECStatus rv = SSL_MakeAead(SSL_LIBRARY_VERSION_TLS_1_3,
cipherinfo->cipherSuite, secret, "",
0, // Use the default labels.
&ctx);
if (rv != SECSuccess) {
return false;
}
aead_.reset(ctx);
return true;
}
void AeadCipher::FormatNonce(uint64_t seq, uint8_t *nonce) {
memcpy(nonce, iv_, 12);
for (size_t i = 0; i < 8; ++i) {
nonce[12 - (i + 1)] ^= seq & 0xff;
seq >>= 8;
bool TlsCipherSpec::Unprotect(const TlsRecordHeader& header,
const DataBuffer& ciphertext,
DataBuffer* plaintext) {
if (aead_ == nullptr) {
return false;
}
DataBuffer d(nonce, 12);
}
bool AeadCipher::AeadInner(bool decrypt, void *params, size_t param_length,
const uint8_t *in, size_t inlen, uint8_t *out,
size_t *outlen, size_t maxlen) {
SECStatus rv;
unsigned int uoutlen = 0;
SECItem param = {
siBuffer, static_cast<unsigned char *>(params),
static_cast<unsigned int>(param_length),
};
if (decrypt) {
rv = PK11_Decrypt(key_, mech_, &param, out, &uoutlen, maxlen, in, inlen);
} else {
rv = PK11_Encrypt(key_, mech_, &param, out, &uoutlen, maxlen, in, inlen);
}
*outlen = (int)uoutlen;
return rv == SECSuccess;
}
bool AeadCipherAesGcm::Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len,
uint64_t seq, const uint8_t *in, size_t inlen,
uint8_t *out, size_t *outlen, size_t maxlen) {
CK_GCM_PARAMS aeadParams;
unsigned char nonce[12];
memset(&aeadParams, 0, sizeof(aeadParams));
aeadParams.pIv = nonce;
aeadParams.ulIvLen = sizeof(nonce);
aeadParams.pAAD = const_cast<uint8_t *>(hdr);
aeadParams.ulAADLen = hdr_len;
aeadParams.ulTagBits = 128;
FormatNonce(seq, nonce);
return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
in, inlen, out, outlen, maxlen);
}
bool AeadCipherChacha20Poly1305::Aead(bool decrypt, const uint8_t *hdr,
size_t hdr_len, uint64_t seq,
const uint8_t *in, size_t inlen,
uint8_t *out, size_t *outlen,
size_t maxlen) {
CK_NSS_AEAD_PARAMS aeadParams;
unsigned char nonce[12];
memset(&aeadParams, 0, sizeof(aeadParams));
aeadParams.pNonce = nonce;
aeadParams.ulNonceLen = sizeof(nonce);
aeadParams.pAAD = const_cast<uint8_t *>(hdr);
aeadParams.ulAADLen = hdr_len;
aeadParams.ulTagLen = 16;
FormatNonce(seq, nonce);
return AeadInner(decrypt, (unsigned char *)&aeadParams, sizeof(aeadParams),
in, inlen, out, outlen, maxlen);
}
bool TlsCipherSpec::Init(uint16_t epoc, SSLCipherAlgorithm cipher,
PK11SymKey *key, const uint8_t *iv) {
epoch_ = epoc;
switch (cipher) {
case ssl_calg_aes_gcm:
aead_.reset(new AeadCipherAesGcm());
break;
case ssl_calg_chacha20:
aead_.reset(new AeadCipherChacha20Poly1305());
break;
default:
return false;
}
return aead_->Init(key, iv);
}
bool TlsCipherSpec::Unprotect(const TlsRecordHeader &header,
const DataBuffer &ciphertext,
DataBuffer *plaintext) {
// Make space.
plaintext->Allocate(ciphertext.len());
auto header_bytes = header.header();
size_t len;
bool ret =
aead_->Aead(true, header_bytes.data(), header_bytes.len(),
header.sequence_number(), ciphertext.data(), ciphertext.len(),
plaintext->data(), &len, plaintext->len());
if (!ret) return false;
unsigned int len;
uint64_t seqno;
if (dtls_) {
seqno = header.sequence_number();
} else {
seqno = in_seqno_;
}
SECStatus rv =
SSL_AeadDecrypt(aead_.get(), seqno, header_bytes.data(),
header_bytes.len(), ciphertext.data(), ciphertext.len(),
plaintext->data(), &len, plaintext->len());
if (rv != SECSuccess) {
return false;
}
plaintext->Truncate(len);
RecordUnprotected(seqno);
plaintext->Truncate(static_cast<size_t>(len));
return true;
}
bool TlsCipherSpec::Protect(const TlsRecordHeader &header,
const DataBuffer &plaintext,
DataBuffer *ciphertext) {
bool TlsCipherSpec::Protect(const TlsRecordHeader& header,
const DataBuffer& plaintext,
DataBuffer* ciphertext) {
if (aead_ == nullptr) {
return false;
}
// Make a padded buffer.
ciphertext->Allocate(plaintext.len() +
32); // Room for any plausible auth tag
size_t len;
unsigned int len;
DataBuffer header_bytes;
(void)header.WriteHeader(&header_bytes, 0, plaintext.len() + 16);
bool ret =
aead_->Aead(false, header_bytes.data(), header_bytes.len(),
header.sequence_number(), plaintext.data(), plaintext.len(),
ciphertext->data(), &len, ciphertext->len());
if (!ret) return false;
uint64_t seqno;
if (dtls_) {
seqno = header.sequence_number();
} else {
seqno = out_seqno_;
}
SECStatus rv =
SSL_AeadEncrypt(aead_.get(), seqno, header_bytes.data(),
header_bytes.len(), plaintext.data(), plaintext.len(),
ciphertext->data(), &len, ciphertext->len());
if (rv != SECSuccess) {
return false;
}
RecordProtected();
ciphertext->Truncate(len);
return true;

View file

@ -10,71 +10,48 @@
#include <cstdint>
#include <memory>
#include "databuffer.h"
#include "pk11pub.h"
#include "sslt.h"
#include "sslexp.h"
#include "databuffer.h"
#include "scoped_ptrs_ssl.h"
namespace nss_test {
class TlsRecordHeader;
class AeadCipher {
public:
AeadCipher(CK_MECHANISM_TYPE mech) : mech_(mech), key_(nullptr) {}
virtual ~AeadCipher();
bool Init(PK11SymKey *key, const uint8_t *iv);
virtual bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len,
uint64_t seq, const uint8_t *in, size_t inlen, uint8_t *out,
size_t *outlen, size_t maxlen) = 0;
protected:
void FormatNonce(uint64_t seq, uint8_t *nonce);
bool AeadInner(bool decrypt, void *params, size_t param_length,
const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
size_t maxlen);
CK_MECHANISM_TYPE mech_;
PK11SymKey *key_;
uint8_t iv_[12];
};
class AeadCipherChacha20Poly1305 : public AeadCipher {
public:
AeadCipherChacha20Poly1305() : AeadCipher(CKM_NSS_CHACHA20_POLY1305) {}
protected:
bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len, uint64_t seq,
const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
size_t maxlen);
};
class AeadCipherAesGcm : public AeadCipher {
public:
AeadCipherAesGcm() : AeadCipher(CKM_AES_GCM) {}
protected:
bool Aead(bool decrypt, const uint8_t *hdr, size_t hdr_len, uint64_t seq,
const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen,
size_t maxlen);
};
// Our analog of ssl3CipherSpec
class TlsCipherSpec {
public:
TlsCipherSpec() : epoch_(0), aead_() {}
TlsCipherSpec(bool dtls, uint16_t epoc);
bool SetKeys(SSLCipherSuiteInfo* cipherinfo, PK11SymKey* secret);
bool Init(uint16_t epoch, SSLCipherAlgorithm cipher, PK11SymKey *key,
const uint8_t *iv);
bool Protect(const TlsRecordHeader& header, const DataBuffer& plaintext,
DataBuffer* ciphertext);
bool Unprotect(const TlsRecordHeader& header, const DataBuffer& ciphertext,
DataBuffer* plaintext);
bool Protect(const TlsRecordHeader &header, const DataBuffer &plaintext,
DataBuffer *ciphertext);
bool Unprotect(const TlsRecordHeader &header, const DataBuffer &ciphertext,
DataBuffer *plaintext);
uint16_t epoch() const { return epoch_; }
uint64_t next_in_seqno() const { return in_seqno_; }
void RecordUnprotected(uint64_t seqno) {
// Reordering happens, so don't let this go backwards.
in_seqno_ = (std::max)(in_seqno_, seqno + 1);
}
uint64_t next_out_seqno() { return out_seqno_; }
void RecordProtected() { out_seqno_++; }
void RecordDropped() { record_dropped_ = true; }
bool record_dropped() const { return record_dropped_; }
bool is_protected() const { return aead_ != nullptr; }
private:
bool dtls_;
uint16_t epoch_;
std::unique_ptr<AeadCipher> aead_;
uint64_t in_seqno_;
uint64_t out_seqno_;
bool record_dropped_ = false;
ScopedSSLAeadContext aead_;
};
} // namespace nss_test