Issue #1338 - Part 2: Update NSS to 3.48-RTM

This commit is contained in:
wolfbeast 2020-01-02 21:06:40 +01:00 committed by Roy Tam
commit c57cac24e8
885 changed files with 1650639 additions and 59530 deletions

View file

@ -26,6 +26,16 @@ include $(CORE_DEPTH)/coreconf/config.mk
# (4) Include "local" platform-dependent assignments (OPTIONAL). #
#######################################################################
# Don't build sysinit gtests unless we are also building libnsssysinit.
# See lib/Makefile for the corresponding rules.
ifndef MOZILLA_CLIENT
ifeq ($(OS_ARCH),Linux)
ifneq ($(NSS_BUILD_UTIL_ONLY),1)
SYSINIT_GTEST=sysinit_gtest
endif
endif
endif
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #

View file

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

View file

@ -21,6 +21,13 @@
'libraries': [
'-lws2_32',
],
'conditions': [
['static_libs==1', {
'libraries': [
'-ladvapi32',
],
}],
],
}],
['OS=="android"', {
'libraries': [

View file

@ -1,6 +1,5 @@
#include "nspr.h"
#include "nss.h"
#include "ssl.h"
#include <cstdlib>
@ -10,10 +9,23 @@
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
return 1;
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_SetDomesticPolicy() != SECSuccess) {
if (NSS_Initialize(workdir, "", "", SECMOD_DB, flags) != SECSuccess) {
return 1;
}
int rv = RUN_ALL_TESTS();

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,117 @@
/* 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/. */
/* This file is generated from sources in nss/gtests/common/wycheproof
* automatically and should not be touched manually.
* Generation is trigged by calling ./mach wycheproof */
#ifndef chachapoly_vectors_h__
#define chachapoly_vectors_h__
#include <string>
#include <vector>
typedef struct chaChaTestVectorStr {
uint32_t id;
std::vector<uint8_t> Data;
std::vector<uint8_t> AAD;
std::vector<uint8_t> Key;
std::vector<uint8_t> IV;
std::vector<uint8_t> CT;
bool invalidTag;
bool invalidIV;
} chaChaTestVector;
// ChaCha20/Poly1305 Test Vector 1, RFC 7539
// <http://tools.ietf.org/html/rfc7539#section-2.8.2>
// ChaCha20/Poly1305 Test Vector 2, RFC 7539
// <http://tools.ietf.org/html/rfc7539#appendix-A.5>
const chaChaTestVector kChaCha20Vectors[] = {
{0,
{0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47,
0x65, 0x6e, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x6f, 0x66,
0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79,
0x6f, 0x75, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
0x62, 0x65, 0x20, 0x69, 0x74, 0x2e},
{0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7},
{0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f},
{0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47},
{0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc,
0x53, 0xef, 0x7e, 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e,
0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6,
0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4,
0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65,
0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16, 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09,
0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91},
false,
false},
{1,
{0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44, 0x72, 0x61,
0x66, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x72, 0x61, 0x66,
0x74, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20,
0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20,
0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x6f, 0x66, 0x20, 0x73,
0x69, 0x78, 0x20, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x20, 0x61, 0x6e,
0x64, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63,
0x65, 0x64, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x73, 0x6f, 0x6c,
0x65, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6f, 0x74, 0x68, 0x65,
0x72, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20,
0x61, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2e,
0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x61, 0x70, 0x70,
0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20,
0x75, 0x73, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x73, 0x20, 0x61, 0x73, 0x20, 0x72,
0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x6d, 0x61, 0x74,
0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20,
0x63, 0x69, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x20, 0x6f, 0x74,
0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x20,
0x2f, 0xe2, 0x80, 0x9c, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x20,
0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x2f, 0xe2, 0x80,
0x9d},
{0xf3, 0x33, 0x88, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x91},
{0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88,
0x86, 0x04, 0xf6, 0xb5, 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b,
0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0},
{0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
{0x64, 0xa0, 0x86, 0x15, 0x75, 0x86, 0x1a, 0xf4, 0x60, 0xf0, 0x62, 0xc7,
0x9b, 0xe6, 0x43, 0xbd, 0x5e, 0x80, 0x5c, 0xfd, 0x34, 0x5c, 0xf3, 0x89,
0xf1, 0x08, 0x67, 0x0a, 0xc7, 0x6c, 0x8c, 0xb2, 0x4c, 0x6c, 0xfc, 0x18,
0x75, 0x5d, 0x43, 0xee, 0xa0, 0x9e, 0xe9, 0x4e, 0x38, 0x2d, 0x26, 0xb0,
0xbd, 0xb7, 0xb7, 0x3c, 0x32, 0x1b, 0x01, 0x00, 0xd4, 0xf0, 0x3b, 0x7f,
0x35, 0x58, 0x94, 0xcf, 0x33, 0x2f, 0x83, 0x0e, 0x71, 0x0b, 0x97, 0xce,
0x98, 0xc8, 0xa8, 0x4a, 0xbd, 0x0b, 0x94, 0x81, 0x14, 0xad, 0x17, 0x6e,
0x00, 0x8d, 0x33, 0xbd, 0x60, 0xf9, 0x82, 0xb1, 0xff, 0x37, 0xc8, 0x55,
0x97, 0x97, 0xa0, 0x6e, 0xf4, 0xf0, 0xef, 0x61, 0xc1, 0x86, 0x32, 0x4e,
0x2b, 0x35, 0x06, 0x38, 0x36, 0x06, 0x90, 0x7b, 0x6a, 0x7c, 0x02, 0xb0,
0xf9, 0xf6, 0x15, 0x7b, 0x53, 0xc8, 0x67, 0xe4, 0xb9, 0x16, 0x6c, 0x76,
0x7b, 0x80, 0x4d, 0x46, 0xa5, 0x9b, 0x52, 0x16, 0xcd, 0xe7, 0xa4, 0xe9,
0x90, 0x40, 0xc5, 0xa4, 0x04, 0x33, 0x22, 0x5e, 0xe2, 0x82, 0xa1, 0xb0,
0xa0, 0x6c, 0x52, 0x3e, 0xaf, 0x45, 0x34, 0xd7, 0xf8, 0x3f, 0xa1, 0x15,
0x5b, 0x00, 0x47, 0x71, 0x8c, 0xbc, 0x54, 0x6a, 0x0d, 0x07, 0x2b, 0x04,
0xb3, 0x56, 0x4e, 0xea, 0x1b, 0x42, 0x22, 0x73, 0xf5, 0x48, 0x27, 0x1a,
0x0b, 0xb2, 0x31, 0x60, 0x53, 0xfa, 0x76, 0x99, 0x19, 0x55, 0xeb, 0xd6,
0x31, 0x59, 0x43, 0x4e, 0xce, 0xbb, 0x4e, 0x46, 0x6d, 0xae, 0x5a, 0x10,
0x73, 0xa6, 0x72, 0x76, 0x27, 0x09, 0x7a, 0x10, 0x49, 0xe6, 0x17, 0xd9,
0x1d, 0x36, 0x10, 0x94, 0xfa, 0x68, 0xf0, 0xff, 0x77, 0x98, 0x71, 0x30,
0x30, 0x5b, 0xea, 0xba, 0x2e, 0xda, 0x04, 0xdf, 0x99, 0x7b, 0x71, 0x4d,
0x6c, 0x6f, 0x2c, 0x29, 0xa6, 0xad, 0x5c, 0xb4, 0x02, 0x2b, 0x02, 0x70,
0x9b, 0xee, 0xad, 0x9d, 0x67, 0x89, 0x0c, 0xbb, 0x22, 0x39, 0x23, 0x36,
0xfe, 0xa1, 0x85, 0x1f, 0x38},
false,
false}};
#endif // chachapoly_vectors_h__

View file

@ -0,0 +1,75 @@
/* 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/. */
#ifndef curve25519_vectors_h__
#define curve25519_vectors_h__
#include <string>
#include <vector>
typedef struct curve25519_testvector_str {
std::vector<uint8_t> private_key;
std::vector<uint8_t> public_key;
std::vector<uint8_t> secret;
bool valid;
} curve25519_testvector;
const curve25519_testvector kCurve25519Vectors[] = {
{{0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda,
0x47, 0x0f, 0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20,
0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72,
0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a,
0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a, 0xa1, 0x23, 0x03, 0x21,
0x00, 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d,
0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a,
0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a},
{0x30, 0x39, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01,
0x03, 0x21, 0x00, 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3,
0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b,
0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f},
{0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, 0x72, 0x8e, 0x3b,
0xf4, 0x80, 0x35, 0x0f, 0x25, 0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1,
0x9e, 0x33, 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42},
true},
// A public key that's too short (31 bytes).
{{0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda,
0x47, 0x0f, 0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20,
0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72,
0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a,
0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a, 0xa1, 0x23, 0x03, 0x21,
0x00, 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d,
0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a,
0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a},
{0x30, 0x38, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01,
0x03, 0x20, 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b,
0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78,
0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f},
{},
false},
// A public key that's too long (33 bytes).
{{0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda,
0x47, 0x0f, 0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20,
0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72,
0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a,
0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a, 0xa1, 0x23, 0x03, 0x21,
0x00, 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d,
0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a,
0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a},
{0x30, 0x3a, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01,
0x03, 0x22, 0x00, 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3,
0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b,
0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f, 0x34},
{},
false}};
#endif // curve25519_vectors_h__

View file

@ -3,12 +3,17 @@
* 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/. */
/* This file is generated from sources in nss/gtests/common/wycheproof
* automatically and should not be touched manually.
* Generation is trigged by calling ./mach wycheproof */
#ifndef gcm_vectors_h__
#define gcm_vectors_h__
#include <string>
typedef struct gcm_kat_str {
uint32_t test_id;
std::string key;
std::string plaintext;
std::string additional_data;
@ -16,49 +21,55 @@ typedef struct gcm_kat_str {
std::string hash_key;
std::string ghash;
std::string result;
bool invalid_ct;
bool invalid_iv;
} gcm_kat_value;
/*
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
*/
const gcm_kat_value kGcmKatValues[] = {
{"00000000000000000000000000000000", "", "", "000000000000000000000000",
{1, "00000000000000000000000000000000", "", "", "000000000000000000000000",
"66e94bd4ef8a2c3b884cfa59ca342b2e", "00000000000000000000000000000000",
"58e2fccefa7e3061367f1d57a4e7455a"},
"58e2fccefa7e3061367f1d57a4e7455a", false, false},
{"00000000000000000000000000000000", "00000000000000000000000000000000", "",
"000000000000000000000000", "66e94bd4ef8a2c3b884cfa59ca342b2e",
{2, "00000000000000000000000000000000", "00000000000000000000000000000000",
"", "000000000000000000000000", "66e94bd4ef8a2c3b884cfa59ca342b2e",
"f38cbb1ad69223dcc3457ae5b6b0f885",
"0388dace60b6a392f328c2b971b2fe78ab6e47d42cec13bdf53a67b21257bddf"},
"0388dace60b6a392f328c2b971b2fe78ab6e47d42cec13bdf53a67b21257bddf", false,
false},
{"feffe9928665731c6d6a8f9467308308",
{3, "feffe9928665731c6d6a8f9467308308",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
"", "cafebabefacedbaddecaf888", "b83b533708bf535d0aa6e52980d53b78",
"7f1b32b81b820d02614f8895ac1d4eac",
"42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25"
"466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f59854d5c2af327cd64a62c"
"f35abd2ba6fab4"},
"f35abd2ba6fab4",
false, false},
{"feffe9928665731c6d6a8f9467308308",
{4, "feffe9928665731c6d6a8f9467308308",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
"feedfacedeadbeeffeedfacedeadbeefabaddad2", "cafebabefacedbaddecaf888",
"b83b533708bf535d0aa6e52980d53b78", "698e57f70e6ecc7fd9463b7260a9ae5f",
"42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25"
"466931c7d8f6a5aac84aa051ba30b396a0aac973d58e0915bc94fbc3221a5db94fae95ae7"
"121a47"},
"121a47",
false, false},
{"feffe9928665731c6d6a8f9467308308",
{5, "feffe9928665731c6d6a8f9467308308",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
"feedfacedeadbeeffeedfacedeadbeefabaddad2", "cafebabefacedbad",
"b83b533708bf535d0aa6e52980d53b78", "df586bb4c249b92cb6922877e444d37b",
"61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e"
"49f24b22b097544d4896b424989b5e1ebac0f07c23f45983612d2e79e3b0785561be14aac"
"a2fccb"},
"a2fccb",
false, false},
{"feffe9928665731c6d6a8f9467308308",
{6, "feffe9928665731c6d6a8f9467308308",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
"feedfacedeadbeeffeedfacedeadbeefabaddad2",
@ -67,45 +78,51 @@ const gcm_kat_value kGcmKatValues[] = {
"b83b533708bf535d0aa6e52980d53b78", "1c5afe9760d3932f3c9a878aac3dc3de",
"8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4f"
"ba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5619cc5aefffe0bfa462af43c16"
"99d050"},
"99d050",
false, false},
{"000000000000000000000000000000000000000000000000", "", "",
{7, "000000000000000000000000000000000000000000000000", "", "",
"000000000000000000000000", "aae06992acbf52a3e8f4a96ec9300bd7",
"00000000000000000000000000000000", "cd33b28ac773f74ba00ed1f312572435"},
"00000000000000000000000000000000", "cd33b28ac773f74ba00ed1f312572435",
false, false},
{"000000000000000000000000000000000000000000000000",
{8, "000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000", "", "000000000000000000000000",
"aae06992acbf52a3e8f4a96ec9300bd7", "e2c63f0ac44ad0e02efa05ab6743d4ce",
"98e7247c07f0fe411c267e4384b0f6002ff58d80033927ab8ef4d4587514f0fb"},
"98e7247c07f0fe411c267e4384b0f6002ff58d80033927ab8ef4d4587514f0fb", false,
false},
{"feffe9928665731c6d6a8f9467308308feffe9928665731c",
{9, "feffe9928665731c6d6a8f9467308308feffe9928665731c",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
"", "cafebabefacedbaddecaf888", "466923ec9ae682214f2c082badb39249",
"51110d40f6c8fff0eb1ae33445a889f0",
"3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c"
"144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade2569924a7c8587336bfb1"
"18024db8674a14"},
"18024db8674a14",
false, false},
{"feffe9928665731c6d6a8f9467308308feffe9928665731c",
{10, "feffe9928665731c6d6a8f9467308308feffe9928665731c",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
"feedfacedeadbeeffeedfacedeadbeefabaddad2", "cafebabefacedbaddecaf888",
"466923ec9ae682214f2c082badb39249", "ed2ce3062e4a8ec06db8b4c490e8a268",
"3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c"
"144c525ac619d18c84a3f4718e2448b2fe324d9ccda27102519498e80f1478f37ba55bd6d"
"27618c"},
"27618c",
false, false},
{"feffe9928665731c6d6a8f9467308308feffe9928665731c",
{11, "feffe9928665731c6d6a8f9467308308feffe9928665731c",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
"feedfacedeadbeeffeedfacedeadbeefabaddad2", "cafebabefacedbad",
"466923ec9ae682214f2c082badb39249", "1e6a133806607858ee80eaf237064089",
"0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9"
"a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f765dcc57fcf623a24094fcca40d"
"3533f8"},
"3533f8",
false, false},
{"feffe9928665731c6d6a8f9467308308feffe9928665731c",
{12, "feffe9928665731c6d6a8f9467308308feffe9928665731c",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
"feedfacedeadbeeffeedfacedeadbeefabaddad2",
@ -114,45 +131,51 @@ const gcm_kat_value kGcmKatValues[] = {
"466923ec9ae682214f2c082badb39249", "82567fb0b4cc371801eadec005968e94",
"d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012a"
"f34ddd9e2f037589b292db3e67c036745fa22e7e9b7373bdcf566ff291c25bbb8568fc3d3"
"76a6d9"},
"76a6d9",
false, false},
{"0000000000000000000000000000000000000000000000000000000000000000", "", "",
"000000000000000000000000", "dc95c078a2408989ad48a21492842087",
"00000000000000000000000000000000", "530f8afbc74536b9a963b4f1c4cb738b"},
{13, "0000000000000000000000000000000000000000000000000000000000000000", "",
"", "000000000000000000000000", "dc95c078a2408989ad48a21492842087",
"00000000000000000000000000000000", "530f8afbc74536b9a963b4f1c4cb738b",
false, false},
{"0000000000000000000000000000000000000000000000000000000000000000",
{14, "0000000000000000000000000000000000000000000000000000000000000000",
"00000000000000000000000000000000", "", "000000000000000000000000",
"dc95c078a2408989ad48a21492842087", "83de425c5edc5d498f382c441041ca92",
"cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919"},
"cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919", false,
false},
{"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
{15, "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
"", "cafebabefacedbaddecaf888", "acbef20579b4b8ebce889bac8732dad7",
"4db870d37cb75fcb46097c36230d1612",
"522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e485"
"90dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015adb094dac5d93471bdec"
"1a502270e3cc6c"},
"1a502270e3cc6c",
false, false},
{"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
{16, "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
"feedfacedeadbeeffeedfacedeadbeefabaddad2", "cafebabefacedbaddecaf888",
"acbef20579b4b8ebce889bac8732dad7", "8bd0c4d8aacd391e67cca447e8c38f65",
"522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e485"
"90dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f66276fc6ece0f4e1768cddf8853bb"
"2d551b"},
"2d551b",
false, false},
{"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
{17, "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
"feedfacedeadbeeffeedfacedeadbeefabaddad2", "cafebabefacedbad",
"acbef20579b4b8ebce889bac8732dad7", "75a34288b8c68f811c52b2e9a2f97f63",
"c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33"
"934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f3a337dbf46a792c45e454913fe"
"2ea8f2"},
"2ea8f2",
false, false},
{"feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
{18, "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c959"
"56809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
"feedfacedeadbeeffeedfacedeadbeefabaddad2",
@ -161,12 +184,14 @@ const gcm_kat_value kGcmKatValues[] = {
"acbef20579b4b8ebce889bac8732dad7", "d5ffcf6fc5ac4d69722187421a7f170b",
"5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b78"
"0f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3fa44a8266ee1c8eb0c8b5d4cf5a"
"e9f19a"},
"e9f19a",
false, false},
/* Extra, non-NIST, test case to test 64-bit binary multiplication carry
* correctness. This is a GHASH-only test. */
{"", "", "", "", "0000000000000000fcefef64ffc4766c",
{19, "", "", "", "", "0000000000000000fcefef64ffc4766c",
"3561e34e52d8b598f9937982512fff27",
"0000000000000000ffcef9ebbffdbd8b00000000000000000000000000000000"}};
"0000000000000000ffcef9ebbffdbd8b00000000000000000000000000000000", false,
false}};
#endif // gcm_vectors_h__

View file

@ -8,7 +8,21 @@
#define util_h__
#include <cassert>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <sys/stat.h>
#include <vector>
#if defined(_WIN32)
#include <windows.h>
#include <codecvt>
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "nspr.h"
static inline std::vector<uint8_t> hex_string_to_bytes(std::string s) {
std::vector<uint8_t> bytes;
@ -18,4 +32,81 @@ static inline std::vector<uint8_t> hex_string_to_bytes(std::string s) {
return bytes;
}
// Given a prefix, attempts to create a unique directory that the user can do
// work in without impacting other tests. For example, if given the prefix
// "scratch", a directory like "scratch05c17b25" will be created in the current
// working directory (or the location specified by NSS_GTEST_WORKDIR, if
// defined).
// Upon destruction, the implementation will attempt to delete the directory.
// However, no attempt is made to first remove files in the directory - the
// user is responsible for this. If the directory is not empty, deleting it will
// fail.
// Statistically, it is technically possible to fail to create a unique
// directory name, but this is extremely unlikely given the expected workload of
// this implementation.
class ScopedUniqueDirectory {
public:
explicit ScopedUniqueDirectory(const std::string &prefix) {
std::string path;
const char *workingDirectory = PR_GetEnvSecure("NSS_GTEST_WORKDIR");
if (workingDirectory) {
path.assign(workingDirectory);
}
path.append(prefix);
for (int i = 0; i < RETRY_LIMIT; i++) {
std::string pathCopy(path);
// TryMakingDirectory will modify its input. If it fails, we want to throw
// away the modified result.
if (TryMakingDirectory(pathCopy)) {
mPath.assign(pathCopy);
break;
}
}
assert(mPath.length() > 0);
#if defined(_WIN32)
// sqldb always uses UTF-8 regardless of the current system locale.
DWORD len =
MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), nullptr, 0);
std::vector<wchar_t> buf(len, L'\0');
MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), buf.data(),
buf.size());
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
mUTF8Path = converter.to_bytes(std::wstring(buf.begin(), buf.end()));
#else
mUTF8Path = mPath;
#endif
}
// NB: the directory must be empty upon destruction
~ScopedUniqueDirectory() { assert(rmdir(mPath.c_str()) == 0); }
const std::string &GetPath() { return mPath; }
const std::string &GetUTF8Path() { return mUTF8Path; }
private:
static const int RETRY_LIMIT = 5;
static void GenerateRandomName(/*in/out*/ std::string &prefix) {
std::stringstream ss;
ss << prefix;
// RAND_MAX is at least 32767.
ss << std::setfill('0') << std::setw(4) << std::hex << rand() << rand();
// This will overwrite the value of prefix. This is a little inefficient,
// but at least it makes the code simple.
ss >> prefix;
}
static bool TryMakingDirectory(/*in/out*/ std::string &prefix) {
GenerateRandomName(prefix);
#if defined(_WIN32)
return _mkdir(prefix.c_str()) == 0;
#else
return mkdir(prefix.c_str(), 0777) == 0;
#endif
}
std::string mPath;
std::string mUTF8Path;
};
#endif // util_h__

View file

@ -0,0 +1,191 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 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/.
import json
import os
import subprocess
script_dir = os.path.dirname(os.path.abspath(__file__))
# Imports a JSON testvector file.
def import_testvector(file):
"""Import a JSON testvector file and return an array of the contained objects."""
with open(file) as f:
vectors = json.loads(f.read())
return vectors
# Convert a test data string to a hex array.
def string_to_hex_array(string):
"""Convert a string of hex chars to a string representing a C-format array of hex bytes."""
b = bytearray.fromhex(string)
result = '{' + ', '.join("{:#04x}".format(x) for x in b) + '}'
return result
# Writes one AES-GCM testvector into C-header format. (Not clang-format conform)
class AESGCM():
"""Class that provides the generator function for a single AES-GCM test case."""
def format_testcase(self, vector):
"""Format an AES-GCM testcase object. Return a string in C-header format."""
result = '{{ {},\n'.format(vector['tcId'])
for key in ['key', 'msg', 'aad', 'iv']:
result += ' \"{}\",\n'.format(vector[key])
result += ' \"\",\n'
result += ' \"{}\",\n'.format(vector['tag'])
result += ' \"{}\",\n'.format(vector['ct'] + vector['tag'])
result += ' {},\n'.format(str(vector['result'] == 'invalid').lower())
result += ' {}}},\n\n'.format(str('ZeroLengthIv' in vector['flags']).lower())
return result
# Writes one ChaChaPoly testvector into C-header format. (Not clang-format conform)
class ChaChaPoly():
"""Class that provides the generator function for a single ChaCha test case."""
def format_testcase(self, testcase):
"""Format an ChaCha testcase object. Return a string in C-header format."""
result = '\n// Comment: {}'.format(testcase['comment'])
result += '\n{{{},\n'.format(testcase['tcId']-1)
for key in ['msg', 'aad', 'key', 'iv']:
result += '{},\n'.format(string_to_hex_array(testcase[key]))
ct = testcase['ct'] + testcase['tag']
result += '{},\n'.format(string_to_hex_array(ct))
result += '{},\n'.format(str(testcase['result'] == 'invalid').lower())
result += '{}}},\n'.format(str(testcase['comment'] == 'invalid nonce size').lower())
return result
# Writes one Curve25519 testvector into C-header format. (Not clang-format conform)
class Curve25519():
"""Class that provides the generator function for a single curve25519 test case."""
# Static pkcs8 and skpi wrappers for the raw keys from Wycheproof.
# The public key section of the pkcs8 wrapper is filled up with 0's, which is
# not correct, but acceptable for the tests at this moment because
# validity of the public key is not checked.
# It's still necessary because of
# https://searchfox.org/nss/rev/7bc70a3317b800aac07bad83e74b6c79a9ec5bff/lib/pk11wrap/pk11pk12.c#171
pkcs8WrapperStart = "3067020100301406072a8648ce3d020106092b06010401da470f01044c304a0201010420"
pkcs8WrapperEnd = "a1230321000000000000000000000000000000000000000000000000000000000000000000"
spkiWrapper = "3039301406072a8648ce3d020106092b06010401da470f01032100"
def format_testcase(self, testcase):
result = '\n// Comment: {}'.format(testcase['comment'])
result += '\n{{{},\n'.format(string_to_hex_array(self.pkcs8WrapperStart + testcase['private'] + self.pkcs8WrapperEnd))
result += '{},\n'.format(string_to_hex_array(self.spkiWrapper + testcase['public']))
result += '{},\n'.format(string_to_hex_array(testcase['shared']))
# Flag 'acceptable' cases with secret == 0 as invalid for NSS.
# Flag 'acceptable' cases with forbidden public key values as invalid for NSS.
# Flag 'acceptable' cases with small public key (0 or 1) as invalid for NSS.
valid = testcase['result'] in ['valid', 'acceptable'] \
and not testcase['shared'] == "0000000000000000000000000000000000000000000000000000000000000000" \
and not testcase["public"] == "daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" \
and not testcase["public"] == "dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" \
and not 'Small public key' in testcase['flags']
result += '{}}},\n'.format(str(valid).lower())
return result
def generate_vectors_file(params):
"""
Generate and store a .h-file with test vectors for one test.
params -- Dictionary with parameters for test vector generation for the desired test.
"""
cases = import_testvector(os.path.join(script_dir, params['source_dir'] + params['source_file']))
with open(os.path.join(script_dir, params['base'])) as base:
header = base.read()
header = header[:params['crop_size_start']]
header += '\n\n// Testvectors from project wycheproof\n'
header += '// <https://github.com/google/wycheproof>\n'
vectors_file = header + params['array_init']
for group in cases['testGroups']:
for test in group['tests']:
vectors_file += params['formatter'].format_testcase(test)
vectors_file = vectors_file[:params['crop_size_end']] + '};\n\n'
vectors_file += params['finish']
with open(os.path.join(script_dir, params['target']), 'w') as target:
target.write(vectors_file)
# Parameters that describe the generation of a testvector file for each supoorted testself.
# source -- relaive path the wycheproof JSON source file with testvectorsself.
# base -- relative path to the pre-fabricated .h-file with general defintions and non-wycheproof vectors.
# target -- relative path to where the finished .h-file is written.
# crop_size_start -- number of characters removed from the end of the base file at start.
# array_init -- string to initialize the c-header style array of testvectors.
# formatter -- the test case formatter class to be used for this test.
# crop_size_end -- number of characters removed from the end of the last generated test vector to close the array definiton.
# finish -- string to re-insert at the end and finish the file. (identical to chars cropped at the start)
# comment -- additional comments to add to the file just before defintion of the test vector array.
aes_gcm_params = {
'source_dir': 'source_vectors/',
'source_file': 'aes_gcm_test.json',
'base': '../testvectors_base/gcm-vectors_base.h',
'target': '../testvectors/gcm-vectors.h',
'crop_size_start': -27,
'array_init': 'const gcm_kat_value kGcmWycheproofVectors[] = {\n',
'formatter' : AESGCM(),
'crop_size_end': -3,
'finish': '#endif // gcm_vectors_h__\n',
'comment' : ''
}
chacha_poly_params = {
'source_dir': 'source_vectors/',
'source_file': 'chacha20_poly1305_test.json',
'base': '../testvectors_base/chachapoly-vectors_base.h',
'target': '../testvectors/chachapoly-vectors.h',
'crop_size_start': -35,
'array_init': 'const chacha_testvector kChaCha20WycheproofVectors[] = {\n',
'formatter' : ChaChaPoly(),
'crop_size_end': -2,
'finish': '#endif // chachapoly_vectors_h__\n',
'comment' : ''
}
curve25519_params = {
'source_dir': 'source_vectors/',
'source_file': 'x25519_test.json',
'base': '../testvectors_base/curve25519-vectors_base.h',
'target': '../testvectors/curve25519-vectors.h',
'crop_size_start': -34,
'array_init': 'const curve25519_testvector kCurve25519WycheproofVectors[] = {\n',
'formatter' : Curve25519(),
'crop_size_end': -2,
'finish': '#endif // curve25519_vectors_h__\n',
'comment' : '// The public key section of the pkcs8 wrapped private key is\n\
// filled up with 0\'s, which is not correct, but acceptable for the\n\
// tests at this moment because validity of the public key is not checked.\n'
}
def update_tests(tests):
remote = "https://raw.githubusercontent.com/google/wycheproof/master/testvectors/"
for test in tests:
subprocess.check_call(['wget', remote+test['source_file'], '-O',
'gtests/common/wycheproof/source_vectors/' +test['source_file'],
'--no-check-certificate'])
def generate_test_vectors():
"""Generate C-header files for all supported tests."""
all_tests = [aes_gcm_params, chacha_poly_params, curve25519_params]
update_tests(all_tests)
for test in all_tests:
generate_vectors_file(test)
def main():
generate_test_vectors()
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,956 @@
{
"algorithm" : "X25519",
"generatorVersion" : "0.4.12",
"notes" : {
"LowOrderPublic" : "Curve25519 or its twist contains some points of low order. This test vector contains a public key with such a point. While many libraries reject such public keys, doing so is not a strict requirement according to RFC 7748.",
"Small public key" : "The public key is insecure and does not belong to a valid private key. Some libraries reject such keys.",
"Twist" : "Public keys are either points on curve25519 or points on its twist. Implementations may either reject such keys or compute X25519 using the twist. If a point multiplication is performed then it is important that the result is correct, since otherwise attacks with invalid keys are possible."
},
"numberOfTests" : 87,
"header" : [],
"testGroups" : [
{
"curve" : "curve25519",
"tests" : [
{
"tcId" : 1,
"comment" : "normal case",
"curve" : "curve25519",
"public" : "9c647d9ae589b9f58fdc3ca4947efbc915c4b2e08e744a0edf469dac59c8f85a",
"private" : "4852834d9d6b77dadeabaaf2e11dca66d19fe74993a7bec36c6e16a0983feaba",
"shared" : "87b7f212b627f7a54ca5e0bcdaddd5389d9de6156cdbcf8ebe14ffbcfb436551",
"result" : "valid",
"flags" : []
},
{
"tcId" : 2,
"comment" : "normal case",
"curve" : "curve25519",
"public" : "9c647d9ae589b9f58fdc3ca4947efbc915c4b2e08e744a0edf469dac59c8f85a",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "4b82bd8650ea9b81a42181840926a4ffa16434d1bf298de1db87efb5b0a9e34e",
"result" : "valid",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 3,
"comment" : "public key on twist",
"curve" : "curve25519",
"public" : "63aa40c6e38346c5caf23a6df0a5e6c80889a08647e551b3563449befcfc9733",
"private" : "588c061a50804ac488ad774ac716c3f5ba714b2712e048491379a500211998a8",
"shared" : "b1a707519495ffffb298ff941716b06dfab87cf8d91123fe2be9a233dda22212",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 4,
"comment" : "public key on twist",
"curve" : "curve25519",
"public" : "0f83c36fded9d32fadf4efa3ae93a90bb5cfa66893bc412c43fa7287dbb99779",
"private" : "b05bfd32e55325d9fd648cb302848039000b390e44d521e58aab3b29a6960ba8",
"shared" : "67dd4a6e165533534c0e3f172e4ab8576bca923a5f07b2c069b4c310ff2e935b",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 5,
"comment" : "public key on twist",
"curve" : "curve25519",
"public" : "0b8211a2b6049097f6871c6c052d3c5fc1ba17da9e32ae458403b05bb283092a",
"private" : "70e34bcbe1f47fbc0fddfd7c1e1aa53d57bfe0f66d243067b424bb6210bed19c",
"shared" : "4a0638cfaa9ef1933b47f8939296a6b25be541ef7f70e844c0bcc00b134de64a",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 6,
"comment" : "public key on twist",
"curve" : "curve25519",
"public" : "343ac20a3b9c6a27b1008176509ad30735856ec1c8d8fcae13912d08d152f46c",
"private" : "68c1f3a653a4cdb1d37bba94738f8b957a57beb24d646e994dc29a276aad458d",
"shared" : "399491fce8dfab73b4f9f611de8ea0b27b28f85994250b0f475d585d042ac207",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 7,
"comment" : "public key on twist",
"curve" : "curve25519",
"public" : "fa695fc7be8d1be5bf704898f388c452bafdd3b8eae805f8681a8d15c2d4e142",
"private" : "d877b26d06dff9d9f7fd4c5b3769f8cdd5b30516a5ab806be324ff3eb69ea0b2",
"shared" : "2c4fe11d490a53861776b13b4354abd4cf5a97699db6e6c68c1626d07662f758",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 8,
"comment" : "public key = 0",
"curve" : "curve25519",
"public" : "0000000000000000000000000000000000000000000000000000000000000000",
"private" : "207494038f2bb811d47805bcdf04a2ac585ada7f2f23389bfd4658f9ddd4debc",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"Small public key"
]
},
{
"tcId" : 9,
"comment" : "public key = 1",
"curve" : "curve25519",
"public" : "0100000000000000000000000000000000000000000000000000000000000000",
"private" : "202e8972b61c7e61930eb9450b5070eae1c670475685541f0476217e4818cfab",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"Small public key"
]
},
{
"tcId" : 10,
"comment" : "edge case on twist",
"curve" : "curve25519",
"public" : "0200000000000000000000000000000000000000000000000000000000000000",
"private" : "38dde9f3e7b799045f9ac3793d4a9277dadeadc41bec0290f81f744f73775f84",
"shared" : "9a2cfe84ff9c4a9739625cae4a3b82a906877a441946f8d7b3d795fe8f5d1639",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 11,
"comment" : "edge case on twist",
"curve" : "curve25519",
"public" : "0300000000000000000000000000000000000000000000000000000000000000",
"private" : "9857a914e3c29036fd9a442ba526b5cdcdf28216153e636c10677acab6bd6aa5",
"shared" : "4da4e0aa072c232ee2f0fa4e519ae50b52c1edd08a534d4ef346c2e106d21d60",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 12,
"comment" : "edge case on twist",
"curve" : "curve25519",
"public" : "ffffff030000f8ffff1f0000c0ffffff000000feffff070000f0ffff3f000000",
"private" : "48e2130d723305ed05e6e5894d398a5e33367a8c6aac8fcdf0a88e4b42820db7",
"shared" : "9ed10c53747f647f82f45125d3de15a1e6b824496ab40410ffcc3cfe95760f3b",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 13,
"comment" : "edge case on twist",
"curve" : "curve25519",
"public" : "000000fcffff070000e0ffff3f000000ffffff010000f8ffff0f0000c0ffff7f",
"private" : "28f41011691851b3a62b641553b30d0dfddcb8fffcf53700a7be2f6a872e9fb0",
"shared" : "cf72b4aa6aa1c9f894f4165b86109aa468517648e1f0cc70e1ab08460176506b",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 14,
"comment" : "edge case on twist",
"curve" : "curve25519",
"public" : "00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffff7f",
"private" : "18a93b6499b9f6b3225ca02fef410e0adec23532321d2d8ef1a6d602a8c65b83",
"shared" : "5d50b62836bb69579410386cf7bb811c14bf85b1c7b17e5924c7ffea91ef9e12",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 15,
"comment" : "edge case on twist",
"curve" : "curve25519",
"public" : "eaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "c01d1305a1338a1fcac2ba7e2e032b427e0b04903165aca957d8d0553d8717b0",
"shared" : "19230eb148d5d67c3c22ab1daeff80a57eae4265ce2872657b2c8099fc698e50",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 16,
"comment" : "edge case for public key",
"curve" : "curve25519",
"public" : "0400000000000000000000000000000000000000000000000000000000000000",
"private" : "386f7f16c50731d64f82e6a170b142a4e34f31fd7768fcb8902925e7d1e21abe",
"shared" : "0fcab5d842a078d7a71fc59b57bfb4ca0be6873b49dcdb9f44e14ae8fbdfa542",
"result" : "valid",
"flags" : []
},
{
"tcId" : 17,
"comment" : "edge case for public key",
"curve" : "curve25519",
"public" : "ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000",
"private" : "e023a289bd5e90fa2804ddc019a05ef3e79d434bb6ea2f522ecb643a75296e95",
"shared" : "54ce8f2275c077e3b1306a3939c5e03eef6bbb88060544758d9fef59b0bc3e4f",
"result" : "valid",
"flags" : []
},
{
"tcId" : 18,
"comment" : "edge case for public key",
"curve" : "curve25519",
"public" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03",
"private" : "68f010d62ee8d926053a361c3a75c6ea4ebdc8606ab285003a6f8f4076b01e83",
"shared" : "f136775c5beb0af8110af10b20372332043cab752419678775a223df57c9d30d",
"result" : "valid",
"flags" : []
},
{
"tcId" : 19,
"comment" : "edge case for public key",
"curve" : "curve25519",
"public" : "fffffffbfffffbffffdfffffdffffffffefffffefffff7fffff7ffffbfffff3f",
"private" : "58ebcb35b0f8845caf1ec630f96576b62c4b7b6c36b29deb2cb0084651755c96",
"shared" : "bf9affd06b844085586460962ef2146ff3d4533d9444aab006eb88cc3054407d",
"result" : "valid",
"flags" : []
},
{
"tcId" : 20,
"comment" : "edge case for public key",
"curve" : "curve25519",
"public" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"private" : "188c4bc5b9c44b38bb658b9b2ae82d5b01015e093184b17cb7863503a783e1bb",
"shared" : "d480de04f699cb3be0684a9cc2e31281ea0bc5a9dcc157d3d20158d46ca5246d",
"result" : "valid",
"flags" : []
},
{
"tcId" : 21,
"comment" : "edge case for public key",
"curve" : "curve25519",
"public" : "fffffffffeffff7ffffffffffeffff7ffffffffffeffff7ffffffffffeffff7f",
"private" : "e06c11bb2e13ce3dc7673f67f5482242909423a9ae95ee986a988d98faee23a2",
"shared" : "4c4401cce6b51e4cb18f2790246c9bf914db667750a1cb89069092af07292276",
"result" : "valid",
"flags" : []
},
{
"tcId" : 22,
"comment" : "edge case for public key",
"curve" : "curve25519",
"public" : "ebffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "c0658c46dde18129293877535b1162b6f9f5414a23cf4d2cbc140a4d99da2b8f",
"shared" : "578ba8cc2dbdc575afcf9df2b3ee6189f5337d6854c79b4ce165ea12293b3a0f",
"result" : "valid",
"flags" : []
},
{
"tcId" : 23,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800",
"private" : "10255c9230a97a30a458ca284a629669293a31890cda9d147febc7d1e22d6bb1",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 24,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157",
"private" : "78f1e8edf14481b389448dac8f59c70b038e7cf92ef2c7eff57a72466e115296",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 25,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "a0a05a3e8f9f44204d5f8059a94ac7dfc39a49ac016dd743dbfa43c5d671fd88",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 26,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "d0dbb3ed1906663f15420af31f4eaf6509d9a9949723500605ad7c1c6e7450a9",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 27,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "c0b1d0eb22b244fe3291140072cdd9d989b5f0ecd96c100feb5bca241c1d9f8f",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 28,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "0000000000000000000000000000000000000000000000000000000000000080",
"private" : "480bf45f594942a8bc0f3353c6e8b8853d77f351f1c2ca6c2d1abf8a00b4229c",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 29,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "0100000000000000000000000000000000000000000000000000000000000080",
"private" : "30f993fcf8514fc89bd8db14cd43ba0d4b2530e73c4276a05e1b145d420cedb4",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 30,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880",
"private" : "c04974b758380e2a5b5df6eb09bb2f6b3434f982722a8e676d3da251d1b3de83",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 31,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7",
"private" : "502a31373db32446842fe5add3e024022ea54f274182afc3d9f1bb3d39534eb5",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 32,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "90fa6417b0e37030fd6e43eff2abaef14c6793117a039cf621318ba90f4e98be",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 33,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "78ad3f26027f1c9fdd975a1613b947779bad2cf2b741ade01840885a30bb979c",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 34,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "98e23de7b1e0926ed9c87e7b14baf55f497a1d7096f93977680e44dc1c7b7b8b",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"LowOrderPublic"
]
},
{
"tcId" : 35,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "0000000000000000000000000000000000000000000000000000000000000000",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 36,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "0100000000000000000000000000000000000000000000000000000000000000",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 37,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 38,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 39,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 40,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 41,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 42,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "0000000000000000000000000000000000000000000000000000000000000080",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 43,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "0100000000000000000000000000000000000000000000000000000000000080",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 44,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 45,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 46,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 47,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 48,
"comment" : "public key with low order",
"curve" : "curve25519",
"public" : "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "1064a67da639a8f6df4fbea2d63358b65bca80a770712e14ea8a72df5a3313ae",
"shared" : "0000000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 49,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "f01e48dafac9d7bcf589cbc382c878d18bda3550589ffb5d50b523bebe329dae",
"shared" : "bd36a0790eb883098c988b21786773de0b3a4df162282cf110de18dd484ce74b",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 50,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "288796bc5aff4b81a37501757bc0753a3c21964790d38699308debc17a6eaf8d",
"shared" : "b4e0dd76da7b071728b61f856771aa356e57eda78a5b1655cc3820fb5f854c5c",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 51,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "98df845f6651bf1138221f119041f72b6dbc3c4ace7143d99fd55ad867480da8",
"shared" : "6fdf6c37611dbd5304dc0f2eb7c9517eb3c50e12fd050ac6dec27071d4bfc034",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 52,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"private" : "f09498e46f02f878829e78b803d316a2ed695d0498a08abdf8276930e24edcb0",
"shared" : "4c8fc4b1c6ab88fb21f18f6d4c810240d4e94651ba44f7a2c863cec7dc56602d",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 53,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "0200000000000000000000000000000000000000000000000000000000000080",
"private" : "1813c10a5c7f21f96e17f288c0cc37607c04c5f5aea2db134f9e2ffc66bd9db8",
"shared" : "1cd0b28267dc541c642d6d7dca44a8b38a63736eef5c4e6501ffbbb1780c033c",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 54,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "0300000000000000000000000000000000000000000000000000000000000080",
"private" : "7857fb808653645a0beb138a64f5f4d733a45ea84c3cda11a9c06f7e7139149e",
"shared" : "8755be01c60a7e825cff3e0e78cb3aa4333861516aa59b1c51a8b2a543dfa822",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 55,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "0400000000000000000000000000000000000000000000000000000000000080",
"private" : "e03aa842e2abc56e81e87b8b9f417b2a1e5913c723eed28d752f8d47a59f498f",
"shared" : "54c9a1ed95e546d27822a360931dda60a1df049da6f904253c0612bbdc087476",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 56,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "f8f707b7999b18cb0d6b96124f2045972ca274bfc154ad0c87038c24c6d0d4b2",
"shared" : "cc1f40d743cdc2230e1043daba8b75e810f1fbab7f255269bd9ebb29e6bf494f",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 57,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "a034f684fa631e1a348118c1ce4c98231f2d9eec9ba5365b4a05d69a785b0796",
"shared" : "54998ee43a5b007bf499f078e736524400a8b5c7e9b9b43771748c7cdf880412",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 58,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "dcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "30b6c6a0f2ffa680768f992ba89e152d5bc9893d38c9119be4f767bfab6e0ca5",
"shared" : "ead9b38efdd723637934e55ab717a7ae09eb86a21dc36a3feeb88b759e391e09",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 59,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "eaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "901b9dcf881e01e027575035d40b43bdc1c5242e030847495b0c7286469b6591",
"shared" : "602ff40789b54b41805915fe2a6221f07a50ffc2c3fc94cf61f13d7904e88e0e",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 60,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "ebffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "8046677c28fd82c9a1bdb71a1a1a34faba1225e2507fe3f54d10bd5b0d865f8e",
"shared" : "e00ae8b143471247ba24f12c885536c3cb981b58e1e56b2baf35c12ae1f79c26",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 61,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "602f7e2f68a846b82cc269b1d48e939886ae54fd636c1fe074d710127d472491",
"shared" : "98cb9b50dd3fc2b0d4f2d2bf7c5cfdd10c8fcd31fc40af1ad44f47c131376362",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 62,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "60887b3dc72443026ebedbbbb70665f42b87add1440e7768fbd7e8e2ce5f639d",
"shared" : "38d6304c4a7e6d9f7959334fb5245bd2c754525d4c91db950206926234c1f633",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 63,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "78d31dfa854497d72d8def8a1b7fb006cec2d8c4924647c93814ae56faeda495",
"shared" : "786cd54996f014a5a031ec14db812ed08355061fdb5de680a800ac521f318e23",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 64,
"comment" : "public key >= p",
"curve" : "curve25519",
"public" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"private" : "c04c5baefa8302ddded6a4bb957761b4eb97aefa4fc3b8043085f96a5659b3a5",
"shared" : "29ae8bc73e9b10a08b4f681c43c3e0ac1a171d31b38f1a48efba29ae639ea134",
"result" : "acceptable",
"flags" : []
},
{
"tcId" : 65,
"comment" : "RFC 7748",
"curve" : "curve25519",
"public" : "e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
"private" : "a046e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449a44",
"shared" : "c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
"result" : "valid",
"flags" : []
},
{
"tcId" : 66,
"comment" : "RFC 7748",
"curve" : "curve25519",
"public" : "e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a413",
"private" : "4866e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba4d",
"shared" : "95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957",
"result" : "valid",
"flags" : []
},
{
"tcId" : 67,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "0ab4e76380d84dde4f6833c58f2a9fb8f83bb0169b172be4b6e0592887741a36",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "0200000000000000000000000000000000000000000000000000000000000000",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 68,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "89e10d5701b4337d2d032181538b1064bd4084401ceca1fd12663a1959388000",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "0900000000000000000000000000000000000000000000000000000000000000",
"result" : "valid",
"flags" : []
},
{
"tcId" : 69,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "2b55d3aa4a8f80c8c0b2ae5f933e85af49beac36c2fa7394bab76c8933f8f81d",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "1000000000000000000000000000000000000000000000000000000000000000",
"result" : "valid",
"flags" : []
},
{
"tcId" : 70,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "63e5b1fe9601fe84385d8866b0421262f78fbfa5aff9585e626679b18547d959",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 71,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "e428f3dac17809f827a522ce32355058d07369364aa78902ee10139b9f9dd653",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "valid",
"flags" : []
},
{
"tcId" : 72,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "b3b50e3ed3a407b95de942ef74575b5ab8a10c09ee103544d60bdfed8138ab2b",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 73,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "213fffe93d5ea8cd242e462844029922c43c77c9e3e42f562f485d24c501a20b",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
"result" : "valid",
"flags" : []
},
{
"tcId" : 74,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "91b232a178b3cd530932441e6139418f72172292f1da4c1834fc5ebfefb51e3f",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03",
"result" : "valid",
"flags" : []
},
{
"tcId" : 75,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "045c6e11c5d332556c7822fe94ebf89b56a3878dc27ca079103058849fabcb4f",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "e5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 76,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "1ca2190b71163539063c35773bda0c9c928e9136f0620aeb093f099197b7f74e",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "e3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 77,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "f76e9010ac33c5043b2d3b76a842171000c4916222e9e85897a0aec7f6350b3c",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "ddffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"result" : "valid",
"flags" : []
},
{
"tcId" : 78,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "bb72688d8f8aa7a39cd6060cd5c8093cdec6fe341937c3886a99346cd07faa55",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 79,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "88fddea193391c6a5933ef9b71901549447205aae9da928a6b91a352ba10f41f",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "0000000000000000000000000000000000000000000000000000000000000002",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 80,
"comment" : "edge case for shared secret",
"curve" : "curve25519",
"public" : "303b392f153116cad9cc682a00ccc44c95ff0d3bbe568beb6c4e739bafdc2c68",
"private" : "a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63",
"shared" : "0000000000000000000000000000000000000000000000000000000000008000",
"result" : "acceptable",
"flags" : [
"Twist"
]
},
{
"tcId" : 81,
"comment" : "checking for overflow",
"curve" : "curve25519",
"public" : "fd300aeb40e1fa582518412b49b208a7842b1e1f056a040178ea4141534f652d",
"private" : "c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2",
"shared" : "b734105dc257585d73b566ccb76f062795ccbec89128e52b02f3e59639f13c46",
"result" : "valid",
"flags" : []
},
{
"tcId" : 82,
"comment" : "checking for overflow",
"curve" : "curve25519",
"public" : "c8ef79b514d7682677bc7931e06ee5c27c9b392b4ae9484473f554e6678ecc2e",
"private" : "c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2",
"shared" : "647a46b6fc3f40d62141ee3cee706b4d7a9271593a7b143e8e2e2279883e4550",
"result" : "valid",
"flags" : []
},
{
"tcId" : 83,
"comment" : "checking for overflow",
"curve" : "curve25519",
"public" : "64aeac2504144861532b7bbcb6c87d67dd4c1f07ebc2e06effb95aecc6170b2c",
"private" : "c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2",
"shared" : "4ff03d5fb43cd8657a3cf37c138cadcecce509e4eba089d0ef40b4e4fb946155",
"result" : "valid",
"flags" : []
},
{
"tcId" : 84,
"comment" : "checking for overflow",
"curve" : "curve25519",
"public" : "bf68e35e9bdb7eee1b50570221860f5dcdad8acbab031b14974cc49013c49831",
"private" : "c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2",
"shared" : "21cee52efdbc812e1d021a4af1e1d8bc4db3c400e4d2a2c56a3926db4d99c65b",
"result" : "valid",
"flags" : []
},
{
"tcId" : 85,
"comment" : "checking for overflow",
"curve" : "curve25519",
"public" : "5347c491331a64b43ddc683034e677f53dc32b52a52a577c15a83bf298e99f19",
"private" : "c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2",
"shared" : "18cb89e4e20c0c2bd324305245266c9327690bbe79acb88f5b8fb3f74eca3e52",
"result" : "valid",
"flags" : []
},
{
"tcId" : 86,
"comment" : "private key == -1 (mod order)",
"curve" : "curve25519",
"public" : "258e04523b8d253ee65719fc6906c657192d80717edc828fa0af21686e2faa75",
"private" : "a023cdd083ef5bb82f10d62e59e15a6800000000000000000000000000000050",
"shared" : "258e04523b8d253ee65719fc6906c657192d80717edc828fa0af21686e2faa75",
"result" : "valid",
"flags" : []
},
{
"tcId" : 87,
"comment" : "private key == 1 (mod order) on twist",
"curve" : "curve25519",
"public" : "2eae5ec3dd494e9f2d37d258f873a8e6e9d0dbd1e383ef64d98bb91b3e0be035",
"private" : "58083dd261ad91eff952322ec824c682ffffffffffffffffffffffffffffff5f",
"shared" : "2eae5ec3dd494e9f2d37d258f873a8e6e9d0dbd1e383ef64d98bb91b3e0be035",
"result" : "acceptable",
"flags" : []
}
]
}
]
}

View file

@ -16,17 +16,35 @@
#include "secerr.h"
#include "secitem.h"
const SEC_ASN1Template mySEC_NullTemplate[] = {
{SEC_ASN1_NULL, 0, NULL, sizeof(SECItem)}};
namespace nss_test {
class QuickDERTest : public ::testing::Test,
public ::testing::WithParamInterface<SECItem> {};
struct TemplateAndInput {
const SEC_ASN1Template* t;
SECItem input;
};
class QuickDERTest : public ::testing::Test,
public ::testing::WithParamInterface<TemplateAndInput> {};
static const uint8_t kBitstringTag = 0x03;
static const uint8_t kNullTag = 0x05;
static const uint8_t kLongLength = 0x80;
const SEC_ASN1Template kBitstringTemplate[] = {
{SEC_ASN1_BIT_STRING, 0, NULL, sizeof(SECItem)}, {0}};
// Empty bitstring with unused bits.
static uint8_t kEmptyBitstringUnused[] = {kBitstringTag, 1, 1};
// Bitstring with 8 unused bits.
static uint8_t kBitstring8Unused[] = {kBitstringTag, 3, 8, 0xff, 0x00};
// Bitstring with >8 unused bits.
static uint8_t kBitstring9Unused[] = {kBitstringTag, 3, 9, 0xff, 0x80};
const SEC_ASN1Template kNullTemplate[] = {
{SEC_ASN1_NULL, 0, NULL, sizeof(SECItem)}, {0}};
// Length of zero wrongly encoded as 0x80 instead of 0x00.
static uint8_t kOverlongLength_0_0[] = {kNullTag, kLongLength | 0};
@ -53,14 +71,22 @@ static uint8_t kOverlongLength_16_0[] = {kNullTag, kLongLength | 0x10,
0x00, 0x00,
0x00, 0x00};
static const SECItem kInvalidDER[] = {
{siBuffer, kOverlongLength_0_0, sizeof(kOverlongLength_0_0)},
{siBuffer, kOverlongLength_1_0, sizeof(kOverlongLength_1_0)},
{siBuffer, kOverlongLength_16_0, sizeof(kOverlongLength_16_0)},
#define TI(t, x) \
{ \
t, { siBuffer, x, sizeof(x) } \
}
static const TemplateAndInput kInvalidDER[] = {
TI(kBitstringTemplate, kEmptyBitstringUnused),
TI(kBitstringTemplate, kBitstring8Unused),
TI(kBitstringTemplate, kBitstring9Unused),
TI(kNullTemplate, kOverlongLength_0_0),
TI(kNullTemplate, kOverlongLength_1_0),
TI(kNullTemplate, kOverlongLength_16_0),
};
#undef TI
TEST_P(QuickDERTest, InvalidLengths) {
const SECItem& original_input(GetParam());
const SECItem& original_input(GetParam().input);
ScopedSECItem copy_of_input(SECITEM_AllocItem(nullptr, nullptr, 0U));
ASSERT_TRUE(copy_of_input);
@ -69,11 +95,10 @@ TEST_P(QuickDERTest, InvalidLengths) {
PORTCheapArenaPool pool;
PORT_InitCheapArena(&pool, DER_DEFAULT_CHUNKSIZE);
ScopedSECItem parsed_value(SECITEM_AllocItem(nullptr, nullptr, 0U));
ASSERT_TRUE(parsed_value);
StackSECItem parsed_value;
ASSERT_EQ(SECFailure,
SEC_QuickDERDecodeItem(&pool.arena, parsed_value.get(),
mySEC_NullTemplate, copy_of_input.get()));
SEC_QuickDERDecodeItem(&pool.arena, &parsed_value, GetParam().t,
copy_of_input.get()));
ASSERT_EQ(SEC_ERROR_BAD_DER, PR_GetError());
PORT_DestroyCheapArena(&pool);
}

View file

@ -0,0 +1,187 @@
// 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 <stdint.h>
#include <memory>
#include "blapi.h"
#include "secitem.h"
#include "freebl_scoped_ptrs.h"
class CmacAesTest : public ::testing::Test {
protected:
bool Compare(const uint8_t *actual, const uint8_t *expected,
unsigned int length) {
return strncmp((const char *)actual, (const char *)expected, length) == 0;
}
};
TEST_F(CmacAesTest, CreateInvalidSize) {
uint8_t key[1] = {0x00};
ScopedCMACContext ctx(CMAC_Create(CMAC_AES, key, sizeof(key)));
ASSERT_EQ(ctx, nullptr);
}
TEST_F(CmacAesTest, CreateRightSize) {
uint8_t *key = PORT_NewArray(uint8_t, AES_128_KEY_LENGTH);
ScopedCMACContext ctx(CMAC_Create(CMAC_AES, key, AES_128_KEY_LENGTH));
ASSERT_NE(ctx, nullptr);
PORT_Free(key);
}
// The following tests were taken from NIST's Cryptographic Standards and
// Guidelines page for AES-CMAC Examples with Intermediate Values. These same
// test vectors for AES-128 can be found in RFC 4493, Section 4.
static const uint8_t kNistKeys[][AES_256_KEY_LENGTH] = {
{0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, 0xD2, 0xA6, 0xAB, 0xF7, 0x15,
0x88, 0x09, 0xCF, 0x4F, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x8E, 0x73, 0xB0, 0xF7, 0xDA, 0x0E, 0x64, 0x52, 0xC8, 0x10, 0xF3,
0x2B, 0x80, 0x90, 0x79, 0xE5, 0x62, 0xF8, 0xEA, 0xD2, 0x52, 0x2C,
0x6B, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
{0x60, 0x3D, 0xEB, 0x10, 0x15, 0xCA, 0x71, 0xBE, 0x2B, 0x73, 0xAE,
0xF0, 0x85, 0x7D, 0x77, 0x81, 0x1F, 0x35, 0x2C, 0x07, 0x3B, 0x61,
0x08, 0xD7, 0x2D, 0x98, 0x10, 0xA3, 0x09, 0x14, 0xDF, 0xF4}};
static const size_t kNistKeyLengthsCount = PR_ARRAY_SIZE(kNistKeys);
static const unsigned int kNistKeyLengths[kNistKeyLengthsCount] = {
AES_128_KEY_LENGTH, AES_192_KEY_LENGTH, AES_256_KEY_LENGTH};
static const uint8_t kNistPlaintext[64] = {
0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E,
0x11, 0x73, 0x93, 0x17, 0x2A, 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03,
0xAC, 0x9C, 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51, 0x30,
0xC8, 0x1C, 0x46, 0xA3, 0x5C, 0xE4, 0x11, 0xE5, 0xFB, 0xC1, 0x19,
0x1A, 0x0A, 0x52, 0xEF, 0xF6, 0x9F, 0x24, 0x45, 0xDF, 0x4F, 0x9B,
0x17, 0xAD, 0x2B, 0x41, 0x7B, 0xE6, 0x6C, 0x37, 0x10};
static const unsigned int kNistPlaintextLengths[] = {0, 16, 20, 64};
static const size_t kNistPlaintextLengthsCount =
PR_ARRAY_SIZE(kNistPlaintextLengths);
// This table contains the result of a CMAC over kNistPlaintext using keys from
// kNistKeys. For each key, there are kNistPlaintextLengthsCount answers, all
// listed one after the other as the input is truncated to the different sizes
// in kNistPlaintextLengths.
static const uint8_t kNistKnown[][AES_BLOCK_SIZE] = {
{0xBB, 0x1D, 0x69, 0x29, 0xE9, 0x59, 0x37, 0x28, 0x7F, 0xA3, 0x7D, 0x12,
0x9B, 0x75, 0x67, 0x46},
{0x07, 0x0A, 0x16, 0xB4, 0x6B, 0x4D, 0x41, 0x44, 0xF7, 0x9B, 0xDD, 0x9D,
0xD0, 0x4A, 0x28, 0x7C},
{0x7D, 0x85, 0x44, 0x9E, 0xA6, 0xEA, 0x19, 0xC8, 0x23, 0xA7, 0xBF, 0x78,
0x83, 0x7D, 0xFA, 0xDE},
{0x51, 0xF0, 0xBE, 0xBF, 0x7E, 0x3B, 0x9D, 0x92, 0xFC, 0x49, 0x74, 0x17,
0x79, 0x36, 0x3C, 0xFE},
{0xD1, 0x7D, 0xDF, 0x46, 0xAD, 0xAA, 0xCD, 0xE5, 0x31, 0xCA, 0xC4, 0x83,
0xDE, 0x7A, 0x93, 0x67},
{0x9E, 0x99, 0xA7, 0xBF, 0x31, 0xE7, 0x10, 0x90, 0x06, 0x62, 0xF6, 0x5E,
0x61, 0x7C, 0x51, 0x84},
{0x3D, 0x75, 0xC1, 0x94, 0xED, 0x96, 0x07, 0x04, 0x44, 0xA9, 0xFA, 0x7E,
0xC7, 0x40, 0xEC, 0xF8},
{0xA1, 0xD5, 0xDF, 0x0E, 0xED, 0x79, 0x0F, 0x79, 0x4D, 0x77, 0x58, 0x96,
0x59, 0xF3, 0x9A, 0x11},
{0x02, 0x89, 0x62, 0xF6, 0x1B, 0x7B, 0xF8, 0x9E, 0xFC, 0x6B, 0x55, 0x1F,
0x46, 0x67, 0xD9, 0x83},
{0x28, 0xA7, 0x02, 0x3F, 0x45, 0x2E, 0x8F, 0x82, 0xBD, 0x4B, 0xF2, 0x8D,
0x8C, 0x37, 0xC3, 0x5C},
{0x15, 0x67, 0x27, 0xDC, 0x08, 0x78, 0x94, 0x4A, 0x02, 0x3C, 0x1F, 0xE0,
0x3B, 0xAD, 0x6D, 0x93},
{0xE1, 0x99, 0x21, 0x90, 0x54, 0x9F, 0x6E, 0xD5, 0x69, 0x6A, 0x2C, 0x05,
0x6C, 0x31, 0x54, 0x10}};
PR_STATIC_ASSERT(PR_ARRAY_SIZE(kNistKnown) ==
kNistKeyLengthsCount * kNistPlaintextLengthsCount);
TEST_F(CmacAesTest, AesNistAligned) {
for (unsigned int key_index = 0; key_index < kNistKeyLengthsCount;
key_index++) {
ScopedCMACContext ctx(CMAC_Create(CMAC_AES, kNistKeys[key_index],
kNistKeyLengths[key_index]));
ASSERT_NE(ctx, nullptr);
for (unsigned int plaintext_index = 0;
plaintext_index < kNistPlaintextLengthsCount; plaintext_index++) {
CMAC_Begin(ctx.get());
unsigned int known_index =
(key_index * kNistPlaintextLengthsCount) + plaintext_index;
CMAC_Update(ctx.get(), kNistPlaintext,
kNistPlaintextLengths[plaintext_index]);
uint8_t output[AES_BLOCK_SIZE];
CMAC_Finish(ctx.get(), output, NULL, AES_BLOCK_SIZE);
ASSERT_TRUE(Compare(output, kNistKnown[known_index], AES_BLOCK_SIZE));
}
}
}
TEST_F(CmacAesTest, AesNistUnaligned) {
for (unsigned int key_index = 0; key_index < kNistKeyLengthsCount;
key_index++) {
unsigned int key_length = kNistKeyLengths[key_index];
ScopedCMACContext ctx(
CMAC_Create(CMAC_AES, kNistKeys[key_index], key_length));
ASSERT_NE(ctx, nullptr);
// Skip the zero-length test.
for (unsigned int plaintext_index = 1;
plaintext_index < kNistPlaintextLengthsCount; plaintext_index++) {
unsigned int known_index =
(key_index * kNistPlaintextLengthsCount) + plaintext_index;
unsigned int plaintext_length = kNistPlaintextLengths[plaintext_index];
// Test all possible offsets and make sure that misaligned updates
// produce the desired result. That is, do two updates:
// 0 ... offset
// offset ... len - offset
// and ensure the result is the same as doing one update.
for (unsigned int offset = 1; offset < plaintext_length; offset++) {
CMAC_Begin(ctx.get());
CMAC_Update(ctx.get(), kNistPlaintext, offset);
CMAC_Update(ctx.get(), kNistPlaintext + offset,
plaintext_length - offset);
uint8_t output[AES_BLOCK_SIZE];
CMAC_Finish(ctx.get(), output, NULL, AES_BLOCK_SIZE);
ASSERT_TRUE(Compare(output, kNistKnown[known_index], AES_BLOCK_SIZE));
}
}
}
}
TEST_F(CmacAesTest, AesNistTruncated) {
for (unsigned int key_index = 0; key_index < kNistKeyLengthsCount;
key_index++) {
unsigned int key_length = kNistKeyLengths[key_index];
ScopedCMACContext ctx(
CMAC_Create(CMAC_AES, kNistKeys[key_index], key_length));
ASSERT_TRUE(ctx != nullptr);
// Skip the zero-length test.
for (unsigned int plaintext_index = 1;
plaintext_index < kNistPlaintextLengthsCount; plaintext_index++) {
unsigned int known_index =
(key_index * kNistPlaintextLengthsCount) + plaintext_index;
unsigned int plaintext_length = kNistPlaintextLengths[plaintext_index];
// Test truncated outputs to ensure that we always get the desired values.
for (unsigned int out_len = 1; out_len < AES_BLOCK_SIZE; out_len++) {
CMAC_Begin(ctx.get());
CMAC_Update(ctx.get(), kNistPlaintext, plaintext_length);
unsigned int actual_out_len = 0;
uint8_t output[AES_BLOCK_SIZE];
CMAC_Finish(ctx.get(), output, &actual_out_len, out_len);
ASSERT_TRUE(actual_out_len == out_len);
ASSERT_TRUE(Compare(output, kNistKnown[known_index], out_len));
}
}
}
}

View file

@ -23,6 +23,7 @@
'<(DEPTH)/lib/dev/dev.gyp:nssdev',
'<(DEPTH)/lib/pki/pki.gyp:nsspki',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl',
'<(DEPTH)/lib/libpkix/libpkix.gyp:libpkix',
],
},
{
@ -34,12 +35,20 @@
'ecl_unittest.cc',
'ghash_unittest.cc',
'rsa_unittest.cc',
'cmac_unittests.cc',
'<(DEPTH)/gtests/common/gtests.cc'
],
'dependencies': [
'freebl_gtest_deps',
'<(DEPTH)/exports.gyp:nss_exports',
],
'conditions': [
[ 'cc_is_gcc==1 and (target_arch=="ia32" or target_arch=="x64")', {
'cflags_cc': [
'-msse2',
],
}],
],
},
{
'target_name': 'prng_gtest',
@ -78,7 +87,7 @@
'defines': [
'NSS_USE_STATIC_LIBS',
],
# For test builds we have to set MPI defines.
# For static builds we have to set MPI defines.
'conditions': [
[ 'ct_verif==1', {
'defines': [

View file

@ -2,7 +2,7 @@
// 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 "gcm-vectors.h"
#include "testvectors/gcm-vectors.h"
#include "gtest/gtest.h"
#include "util.h"

View file

@ -6,6 +6,7 @@
#include <stdint.h>
#include <string.h>
#include <memory>
#ifdef __MACH__
#include <mach/clock.h>
@ -27,7 +28,7 @@ void gettime(struct timespec* tp) {
tp->tv_sec = mts.tv_sec;
tp->tv_nsec = mts.tv_nsec;
#else
clock_gettime(CLOCK_MONOTONIC, tp);
ASSERT_NE(0, timespec_get(tp, TIME_UTC));
#endif
}
@ -84,8 +85,9 @@ class MPITest : public ::testing::Test {
mp_int a;
ASSERT_EQ(MP_OKAY, mp_init(&a));
ASSERT_EQ(MP_OKAY, mp_read_unsigned_octets(&a, ref.data(), ref.size()));
uint8_t buf[len];
ASSERT_EQ(MP_OKAY, mp_to_fixlen_octets(&a, buf, len));
std::unique_ptr<uint8_t[]> buf(new uint8_t[len]);
ASSERT_NE(buf, nullptr);
ASSERT_EQ(MP_OKAY, mp_to_fixlen_octets(&a, buf.get(), len));
size_t compare;
if (len > ref.size()) {
for (size_t i = 0; i < len - ref.size(); ++i) {
@ -96,9 +98,9 @@ class MPITest : public ::testing::Test {
compare = len;
}
dump("value", ref.data(), ref.size());
dump("output", buf, len);
ASSERT_EQ(0, memcmp(buf + len - compare, ref.data() + ref.size() - compare,
compare))
dump("output", buf.get(), len);
ASSERT_EQ(0, memcmp(buf.get() + len - compare,
ref.data() + ref.size() - compare, compare))
<< "comparing " << compare << " octets";
mp_clear(&a);
}
@ -146,6 +148,41 @@ TEST_F(MPITest, MpiCmpUnalignedTest) {
}
#endif
// The two follow tests ensure very similar mp_set_* functions are ok.
TEST_F(MPITest, MpiSetUlong) {
mp_int a, b, c;
MP_DIGITS(&a) = 0;
MP_DIGITS(&b) = 0;
MP_DIGITS(&c) = 0;
ASSERT_EQ(MP_OKAY, mp_init(&a));
ASSERT_EQ(MP_OKAY, mp_init(&b));
ASSERT_EQ(MP_OKAY, mp_init(&c));
EXPECT_EQ(MP_OKAY, mp_set_ulong(&a, 1));
EXPECT_EQ(MP_OKAY, mp_set_ulong(&b, 0));
EXPECT_EQ(MP_OKAY, mp_set_ulong(&c, -1));
mp_clear(&a);
mp_clear(&b);
mp_clear(&c);
}
TEST_F(MPITest, MpiSetInt) {
mp_int a, b, c;
MP_DIGITS(&a) = 0;
MP_DIGITS(&b) = 0;
MP_DIGITS(&c) = 0;
ASSERT_EQ(MP_OKAY, mp_init(&a));
ASSERT_EQ(MP_OKAY, mp_init(&b));
ASSERT_EQ(MP_OKAY, mp_init(&c));
EXPECT_EQ(MP_OKAY, mp_set_int(&a, 1));
EXPECT_EQ(MP_OKAY, mp_set_int(&b, 0));
EXPECT_EQ(MP_OKAY, mp_set_int(&c, -1));
mp_clear(&a);
mp_clear(&b);
mp_clear(&c);
}
TEST_F(MPITest, MpiFixlenOctetsZero) {
std::vector<uint8_t> zero = {0};
TestToFixedOctets(zero, 1);
@ -253,4 +290,4 @@ TEST_F(DISABLED_MPITest, MpiCmpConstTest) {
mp_clear(&c);
}
} // nss_test
} // namespace nss_test

View file

@ -5,6 +5,7 @@
#include "gtest/gtest.h"
#include <stdint.h>
#include <memory>
#include "blapi.h"
#include "secitem.h"

View file

@ -24,9 +24,12 @@ NSS_SRCDIRS = \
cryptohi_gtest \
der_gtest \
pk11_gtest \
smime_gtest \
softoken_gtest \
ssl_gtest \
$(SYSINIT_GTEST) \
nss_bogo_shim \
pkcs11testmodule \
$(NULL)
endif
endif

View file

@ -43,6 +43,7 @@
'<(DEPTH)/lib/base/base.gyp:nssb',
'<(DEPTH)/lib/dev/dev.gyp:nssdev',
'<(DEPTH)/lib/pki/pki.gyp:nsspki',
'<(DEPTH)/lib/libpkix/libpkix.gyp:libpkix',
'<(DEPTH)/lib/mozpkix/mozpkix.gyp:mozpkix',
'<(DEPTH)/lib/mozpkix/mozpkix.gyp:mozpkix-testlib',
],

View file

@ -152,10 +152,14 @@ private:
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
Time validityBeginning, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
// All of the certificates in this test for which this is called have a
// validity period that begins "one day before now".
EXPECT_EQ(TimeFromEpochInSeconds(oneDayBeforeNow), validityBeginning);
return Success;
}
@ -301,10 +305,14 @@ public:
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
Time validityBeginning, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
// All of the certificates in this test for which this is called have a
// validity period that begins "one day before now".
EXPECT_EQ(TimeFromEpochInSeconds(oneDayBeforeNow), validityBeginning);
return Success;
}
@ -321,7 +329,7 @@ public:
{
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
@ -442,10 +450,14 @@ public:
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
Time validityBeginning, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
// All of the certificates in this test for which this is called have a
// validity period that begins "one day before now".
EXPECT_EQ(TimeFromEpochInSeconds(oneDayBeforeNow), validityBeginning);
return Success;
}
@ -665,10 +677,14 @@ private:
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
Time validityBeginning, Duration,
/*optional*/ const Input*,
/*optional*/ const Input*) override
{
// All of the certificates in this test for which this is called have a
// validity period that begins "one day before now".
EXPECT_EQ(TimeFromEpochInSeconds(oneDayBeforeNow), validityBeginning);
return Success;
}
@ -723,7 +739,7 @@ class RevokedEndEntityTrustDomain final : public MultiplePathTrustDomain
{
public:
Result CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&, Time,
Duration, /*optional*/ const Input*,
Time, Duration, /*optional*/ const Input*,
/*optional*/ const Input*) override
{
if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
@ -828,10 +844,14 @@ private:
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
Time validityBeginning, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
// All of the certificates in this test for which this is called have a
// validity period that begins "one day before now".
EXPECT_EQ(TimeFromEpochInSeconds(oneDayBeforeNow), validityBeginning);
return Success;
}

View file

@ -70,7 +70,7 @@ private:
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{

View file

@ -92,7 +92,7 @@ private:
return checker.Check(issuerCert, nullptr, keepGoing);
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Time, Duration,
const Input*, const Input*) override
{
return Success;

View file

@ -558,7 +558,7 @@ private:
return checker.Check(derCert, nullptr, keepGoing);
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Time, Duration,
const Input*, const Input*) override
{
return Success;

View file

@ -166,8 +166,8 @@ void ASSERT_SimpleCase(uint8_t unusedBits, uint8_t bits, KeyUsage usage)
// Test that none of the other non-padding bits are mistaken for the given
// key usage in the single-byte value case.
NAMED_SIMPLE_KU(notGood, unusedBits,
static_cast<uint8_t>((~bits >> unusedBits) << unusedBits));
uint8_t paddingBits = (static_cast<uint8_t>(~bits) >> unusedBits) << unusedBits;
NAMED_SIMPLE_KU(notGood, unusedBits, paddingBits);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &notGood, usage));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood, usage));

View file

@ -302,7 +302,7 @@ public:
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Time, Duration,
/*optional*/ const Input*,
/*optional*/ const Input*) override
{

View file

@ -191,8 +191,10 @@ TEST_F(pkixder_input_tests, ReadByteWrapAroundPointer)
// a null pointer is undefined behavior according to the C++ language spec.,
// but this should catch the problem on at least some compilers, if not all of
// them.
const uint8_t* der = nullptr;
--der;
uintptr_t derint = -1;
auto der = reinterpret_cast<const uint8_t*>(derint);
ASSERT_EQ(sizeof(der), sizeof(derint))
<< "underflow of pointer might not work";
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));
Reader input(buf);
@ -359,6 +361,7 @@ TEST_F(pkixder_input_tests, Skip_WrapAroundPointer)
// but this should catch the problem on at least some compilers, if not all of
// them.
const uint8_t* der = nullptr;
// coverity[FORWARD_NULL]
--der;
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));

View file

@ -1224,3 +1224,53 @@ TEST_F(pkixder_universal_types_tests, OID)
ASSERT_EQ(Success, OID(reader, expectedOID));
}
TEST_F(pkixder_universal_types_tests, SkipOptionalImplicitPrimitiveTag)
{
const uint8_t DER_IMPLICIT_BIT_STRING_WITH_CLASS_NUMBER_1[] = {
0x81,
0x04,
0x00,
0x0A,
0x0B,
0x0C,
};
Input input(DER_IMPLICIT_BIT_STRING_WITH_CLASS_NUMBER_1);
Reader reader(input);
ASSERT_EQ(Success, SkipOptionalImplicitPrimitiveTag(reader, 1));
ASSERT_TRUE(reader.AtEnd());
}
TEST_F(pkixder_universal_types_tests, SkipOptionalImplicitPrimitiveTagMismatch)
{
const uint8_t DER_IMPLICIT_BIT_STRING_WITH_CLASS_NUMBER_1[] = {
0x81,
0x04,
0x00,
0x0A,
0x0B,
0x0C,
};
Input input(DER_IMPLICIT_BIT_STRING_WITH_CLASS_NUMBER_1);
Reader reader(input);
ASSERT_EQ(Success, SkipOptionalImplicitPrimitiveTag(reader, 2));
ASSERT_FALSE(reader.AtEnd());
}
TEST_F(pkixder_universal_types_tests, NoSkipOptionalImplicitConstructedTag)
{
const uint8_t DER_IMPLICIT_SEQUENCE_WITH_CLASS_NUMBER_1[] = {
0xA1,
0x03,
0x05,
0x01,
0x00,
};
Input input(DER_IMPLICIT_SEQUENCE_WITH_CLASS_NUMBER_1);
Reader reader(input);
ASSERT_EQ(Success, SkipOptionalImplicitPrimitiveTag(reader, 1));
ASSERT_FALSE(reader.AtEnd());
}

View file

@ -100,7 +100,7 @@ class EverythingFailsByDefaultTrustDomain : public TrustDomain {
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Time, Duration,
/*optional*/ const Input*,
/*optional*/ const Input*) override {
ADD_FAILURE();

View file

@ -7,20 +7,32 @@ DEPTH = ../..
MODULE = nss
CPPSRCS = \
pk11_aes_gcm_unittest.cc \
pk11_aeskeywrap_unittest.cc \
pk11_aeskeywrappad_unittest.cc \
pk11_cbc_unittest.cc \
pk11_chacha20poly1305_unittest.cc \
pk11_curve25519_unittest.cc \
pk11_der_private_key_import_unittest.cc \
pk11_des_unittest.cc \
pk11_ecdsa_unittest.cc \
pk11_encrypt_derive_unittest.cc \
pk11_export_unittest.cc \
pk11_find_certs_unittest.cc \
pk11_import_unittest.cc \
pk11_keygen.cc \
pk11_key_unittest.cc \
pk11_module_unittest.cc \
pk11_pbkdf2_unittest.cc \
pk11_prf_unittest.cc \
pk11_prng_unittest.cc \
pk11_rsapkcs1_unittest.cc \
pk11_rsapss_unittest.cc \
pk11_der_private_key_import_unittest.cc \
pk11_seed_cbc_unittest.cc \
$(NULL)
DEFINES += -DDLL_PREFIX=\"$(DLL_PREFIX)\" -DDLL_SUFFIX=\"$(DLL_SUFFIX)\"
INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
-I$(CORE_DEPTH)/gtests/common \
-I$(CORE_DEPTH)/cpputil
@ -33,4 +45,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

@ -0,0 +1,91 @@
/* -*- 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 "nss.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sechash.h"
#include "blapi.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "util.h"
namespace nss_test {
class Pkcs11AesCmacTest : public ::testing::Test {
protected:
ScopedPK11SymKey ImportKey(CK_MECHANISM_TYPE mech, SECItem *key_item) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE() << "Can't get slot";
return nullptr;
}
ScopedPK11SymKey result(PK11_ImportSymKey(
slot.get(), mech, PK11_OriginUnwrap, CKA_SIGN, key_item, nullptr));
return result;
}
void RunTest(uint8_t *key, unsigned int key_len, uint8_t *data,
unsigned int data_len, uint8_t *expected,
unsigned int expected_len, CK_ULONG mechanism) {
// Create SECItems for everything...
std::vector<uint8_t> output(expected_len);
SECItem key_item = {siBuffer, key, key_len};
SECItem output_item = {siBuffer, output.data(), expected_len};
SECItem data_item = {siBuffer, data, data_len};
SECItem expected_item = {siBuffer, expected, expected_len};
// Do the PKCS #11 stuff...
ScopedPK11SymKey p11_key = ImportKey(mechanism, &key_item);
ASSERT_NE(nullptr, p11_key.get());
SECStatus ret = PK11_SignWithSymKey(p11_key.get(), CKM_AES_CMAC, NULL,
&output_item, &data_item);
// Verify the result...
ASSERT_EQ(SECSuccess, ret);
ASSERT_EQ(0, SECITEM_CompareItem(&output_item, &expected_item));
}
};
// Sanity check of the PKCS #11 API only. Extensive tests for correctness of
// underling CMAC implementation conducted in the following file:
// gtests/freebl_gtest/cmac_unittests.cc
TEST_F(Pkcs11AesCmacTest, Aes128NistExample1) {
uint8_t key[AES_128_KEY_LENGTH] = {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE,
0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88,
0x09, 0xCF, 0x4F, 0x3C};
uint8_t known[AES_BLOCK_SIZE] = {0xBB, 0x1D, 0x69, 0x29, 0xE9, 0x59,
0x37, 0x28, 0x7F, 0xA3, 0x7D, 0x12,
0x9B, 0x75, 0x67, 0x46};
RunTest(key, AES_128_KEY_LENGTH, NULL, 0, known, AES_BLOCK_SIZE,
CKM_AES_CMAC);
}
TEST_F(Pkcs11AesCmacTest, General) {
uint8_t key[AES_128_KEY_LENGTH] = {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE,
0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88,
0x09, 0xCF, 0x4F, 0x3C};
uint8_t known[4] = {0xBB, 0x1D, 0x69, 0x29};
RunTest(key, AES_128_KEY_LENGTH, NULL, 0, known, 4, CKM_AES_CMAC_GENERAL);
}
TEST_F(Pkcs11AesCmacTest, InvalidKeySize) {
uint8_t key[4] = {0x00, 0x00, 0x00, 0x00};
SECItem key_item = {siBuffer, key, 4};
ScopedPK11SymKey result = ImportKey(CKM_AES_CMAC, &key_item);
ASSERT_EQ(nullptr, result.get());
}
}

View file

@ -12,7 +12,7 @@
#include "nss_scoped_ptrs.h"
#include "gcm-vectors.h"
#include "testvectors/gcm-vectors.h"
#include "gtest/gtest.h"
#include "util.h"
@ -26,87 +26,120 @@ class Pkcs11AesGcmTest : public ::testing::TestWithParam<gcm_kat_value> {
std::vector<uint8_t> plaintext = hex_string_to_bytes(val.plaintext);
std::vector<uint8_t> aad = hex_string_to_bytes(val.additional_data);
std::vector<uint8_t> result = hex_string_to_bytes(val.result);
bool invalid_ct = val.invalid_ct;
bool invalid_iv = val.invalid_iv;
std::stringstream s;
s << "Test #" << val.test_id << " failed.";
std::string msg = s.str();
// Ignore GHASH-only vectors.
if (key.empty()) {
return;
}
// Prepare AEAD params.
CK_GCM_PARAMS gcmParams;
gcmParams.pIv = iv.data();
gcmParams.ulIvLen = iv.size();
gcmParams.pAAD = aad.data();
gcmParams.ulAADLen = aad.size();
gcmParams.ulTagBits = 128;
CK_GCM_PARAMS gcm_params;
gcm_params.pIv = iv.data();
gcm_params.ulIvLen = iv.size();
gcm_params.pAAD = aad.data();
gcm_params.ulAADLen = aad.size();
gcm_params.ulTagBits = 128;
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcmParams),
sizeof(gcmParams)};
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcm_params),
sizeof(gcm_params)};
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
SECItem keyItem = {siBuffer, key.data(),
static_cast<unsigned int>(key.size())};
SECItem key_item = {siBuffer, key.data(),
static_cast<unsigned int>(key.size())};
// Import key.
ScopedPK11SymKey symKey(PK11_ImportSymKey(
slot.get(), mech, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr));
EXPECT_TRUE(!!symKey);
ScopedPK11SymKey sym_key(PK11_ImportSymKey(
slot.get(), mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, nullptr));
ASSERT_TRUE(!!sym_key) << msg;
// Encrypt with bogus parameters.
unsigned int output_len = 0;
std::vector<uint8_t> output(plaintext.size() + gcm_params.ulTagBits / 8);
// "maxout" must be at least "inlen + tagBytes", or, in this case:
// "output.size()" must be at least "plaintext.size() + tagBytes"
gcm_params.ulTagBits = 128;
SECStatus rv =
PK11_Encrypt(sym_key.get(), mech, &params, output.data(), &output_len,
output.size() - 10, plaintext.data(), plaintext.size());
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, output_len);
// The valid values for tag size in AES_GCM are:
// 32, 64, 96, 104, 112, 120 and 128.
gcm_params.ulTagBits = 110;
rv = PK11_Encrypt(sym_key.get(), mech, &params, output.data(), &output_len,
output.size(), plaintext.data(), plaintext.size());
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, output_len);
// Encrypt.
unsigned int outputLen = 0;
std::vector<uint8_t> output(plaintext.size() + gcmParams.ulTagBits / 8);
SECStatus rv =
PK11_Encrypt(symKey.get(), mech, &params, output.data(), &outputLen,
output.size(), plaintext.data(), plaintext.size());
EXPECT_EQ(rv, SECSuccess);
ASSERT_EQ(outputLen, output.size());
gcm_params.ulTagBits = 128;
rv = PK11_Encrypt(sym_key.get(), mech, &params, output.data(), &output_len,
output.size(), plaintext.data(), plaintext.size());
if (invalid_iv) {
EXPECT_EQ(SECFailure, rv) << msg;
EXPECT_EQ(0U, output_len);
return;
}
EXPECT_EQ(SECSuccess, rv) << msg;
ASSERT_EQ(output_len, output.size()) << msg;
// Check ciphertext and tag.
EXPECT_EQ(result, output);
if (invalid_ct) {
EXPECT_NE(result, output) << msg;
} else {
EXPECT_EQ(result, output) << msg;
}
// Decrypt.
unsigned int decryptedLen = 0;
unsigned int decrypted_len = 0;
// The PK11 AES API is stupid, it expects an explicit IV and thus wants
// a block more of available output memory.
std::vector<uint8_t> decrypted(output.size());
rv =
PK11_Decrypt(symKey.get(), mech, &params, decrypted.data(),
&decryptedLen, decrypted.size(), output.data(), outputLen);
EXPECT_EQ(rv, SECSuccess);
ASSERT_EQ(decryptedLen, plaintext.size());
rv = PK11_Decrypt(sym_key.get(), mech, &params, decrypted.data(),
&decrypted_len, decrypted.size(), output.data(),
output_len);
EXPECT_EQ(SECSuccess, rv) << msg;
ASSERT_EQ(decrypted_len, plaintext.size()) << msg;
// Check the plaintext.
EXPECT_EQ(plaintext,
std::vector<uint8_t>(decrypted.begin(),
decrypted.begin() + decryptedLen));
decrypted.begin() + decrypted_len))
<< msg;
}
SECStatus EncryptWithIV(std::vector<uint8_t>& iv) {
// Generate a random key.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey symKey(
ScopedPK11SymKey sym_key(
PK11_KeyGen(slot.get(), mech, nullptr, 16, nullptr));
EXPECT_TRUE(!!symKey);
EXPECT_TRUE(!!sym_key);
std::vector<uint8_t> data(17);
std::vector<uint8_t> output(33);
std::vector<uint8_t> aad(0);
// Prepare AEAD params.
CK_GCM_PARAMS gcmParams;
gcmParams.pIv = iv.data();
gcmParams.ulIvLen = iv.size();
gcmParams.pAAD = aad.data();
gcmParams.ulAADLen = aad.size();
gcmParams.ulTagBits = 128;
CK_GCM_PARAMS gcm_params;
gcm_params.pIv = iv.data();
gcm_params.ulIvLen = iv.size();
gcm_params.pAAD = aad.data();
gcm_params.ulAADLen = aad.size();
gcm_params.ulTagBits = 128;
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcmParams),
sizeof(gcmParams)};
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcm_params),
sizeof(gcm_params)};
// Try to encrypt.
unsigned int outputLen = 0;
return PK11_Encrypt(symKey.get(), mech, &params, output.data(), &outputLen,
output.size(), data.data(), data.size());
unsigned int output_len = 0;
return PK11_Encrypt(sym_key.get(), mech, &params, output.data(),
&output_len, output.size(), data.data(), data.size());
}
const CK_MECHANISM_TYPE mech = CKM_AES_GCM;
@ -117,19 +150,22 @@ TEST_P(Pkcs11AesGcmTest, TestVectors) { RunTest(GetParam()); }
INSTANTIATE_TEST_CASE_P(NISTTestVector, Pkcs11AesGcmTest,
::testing::ValuesIn(kGcmKatValues));
INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11AesGcmTest,
::testing::ValuesIn(kGcmWycheproofVectors));
TEST_F(Pkcs11AesGcmTest, ZeroLengthIV) {
std::vector<uint8_t> iv(0);
EXPECT_EQ(EncryptWithIV(iv), SECFailure);
EXPECT_EQ(SECFailure, EncryptWithIV(iv));
}
TEST_F(Pkcs11AesGcmTest, AllZeroIV) {
std::vector<uint8_t> iv(16, 0);
EXPECT_EQ(EncryptWithIV(iv), SECSuccess);
EXPECT_EQ(SECSuccess, EncryptWithIV(iv));
}
TEST_F(Pkcs11AesGcmTest, TwelveByteZeroIV) {
std::vector<uint8_t> iv(12, 0);
EXPECT_EQ(EncryptWithIV(iv), SECSuccess);
EXPECT_EQ(SECSuccess, EncryptWithIV(iv));
}
} // namespace nss_test

View file

@ -8,125 +8,115 @@
#include "nss.h"
#include "pk11pub.h"
#include "testvectors/kw-vectors.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {
// Test vectors from https://tools.ietf.org/html/rfc3394#section-4.1 to 4.6
unsigned char kKEK1[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
unsigned char kKD1[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
unsigned char kC1[] = {0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47,
0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5};
unsigned char kKEK2[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
unsigned char kC2[] = {0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35,
0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2,
0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D};
unsigned char kKEK3[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
unsigned char kC3[] = {0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2,
0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A,
0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7};
unsigned char kKD4[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
unsigned char kC4[] = {0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32,
0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC,
0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93,
0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2};
unsigned char kC5[] = {0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F,
0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4,
0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95,
0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1};
unsigned char kKD6[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
unsigned char kC6[] = {0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4,
0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26,
0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26,
0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B,
0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21};
class Pkcs11AESKeyWrapTest : public ::testing::Test {
class Pkcs11AESKeyWrapTest : public ::testing::TestWithParam<keywrap_vector> {
protected:
CK_MECHANISM_TYPE mechanism = CKM_NSS_AES_KEY_WRAP;
void WrapUnwrap(unsigned char* kek, unsigned int kekLen,
unsigned char* keyData, unsigned int keyDataLen,
unsigned char* expectedCiphertext) {
unsigned char wrappedKey[40];
unsigned int wrappedKeyLen;
unsigned char unwrappedKey[40];
unsigned int unwrappedKeyLen = 0;
void WrapUnwrap(unsigned char* kek_data, unsigned int kek_len,
unsigned char* key_data, unsigned int key_data_len,
unsigned char* expected_ciphertext,
unsigned int expected_ciphertext_len,
std::map<Action, Result> tests, uint32_t test_id) {
std::vector<unsigned char> wrapped_key(PR_MAX(1U, expected_ciphertext_len));
std::vector<unsigned char> unwrapped_key(PR_MAX(1U, key_data_len));
std::vector<unsigned char> zeros(PR_MAX(1U, expected_ciphertext_len));
std::fill(zeros.begin(), zeros.end(), 0);
unsigned int wrapped_key_len = 0;
unsigned int unwrapped_key_len = 0;
SECStatus rv;
std::stringstream s;
s << "Test with original ID #" << test_id << " failed." << std::endl;
std::string msg = s.str();
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ASSERT_NE(nullptr, slot) << msg;
// Import encryption key.
SECItem keyItem = {siBuffer, kek, kekLen};
ScopedPK11SymKey encryptionKey(
PK11_ImportSymKey(slot.get(), CKM_NSS_AES_KEY_WRAP, PK11_OriginUnwrap,
CKA_ENCRYPT, &keyItem, nullptr));
EXPECT_TRUE(!!encryptionKey);
SECItem kek_item = {siBuffer, kek_data, kek_len};
ScopedPK11SymKey kek(PK11_ImportSymKey(slot.get(), CKM_NSS_AES_KEY_WRAP,
PK11_OriginUnwrap, CKA_ENCRYPT,
&kek_item, nullptr));
EXPECT_TRUE(!!kek) << msg;
// Wrap key
rv = PK11_Encrypt(encryptionKey.get(), mechanism, nullptr /* param */,
wrappedKey, &wrappedKeyLen, sizeof(wrappedKey), keyData,
keyDataLen);
EXPECT_EQ(rv, SECSuccess) << "CKM_NSS_AES_KEY_WRAP encrypt failed";
EXPECT_TRUE(!memcmp(expectedCiphertext, wrappedKey, wrappedKeyLen));
Action test = WRAP;
if (tests.count(test)) {
rv = PK11_Encrypt(kek.get(), mechanism, nullptr /* param */,
wrapped_key.data(), &wrapped_key_len,
wrapped_key.size(), key_data, key_data_len);
ASSERT_EQ(rv, tests[test].expect_rv) << msg;
// If we failed, check that output was not produced.
if (rv == SECFailure) {
EXPECT_TRUE(wrapped_key_len == 0);
EXPECT_TRUE(!memcmp(wrapped_key.data(), zeros.data(), wrapped_key_len));
}
if (tests[test].output_match) {
EXPECT_EQ(expected_ciphertext_len, wrapped_key_len) << msg;
EXPECT_TRUE(!memcmp(expected_ciphertext, wrapped_key.data(),
expected_ciphertext_len))
<< msg;
} else {
// If we produced output, verify that it doesn't match the vector
if (wrapped_key_len) {
EXPECT_FALSE(wrapped_key_len == expected_ciphertext_len &&
!memcmp(wrapped_key.data(), expected_ciphertext,
expected_ciphertext_len))
<< msg;
}
}
}
// Unwrap key
rv = PK11_Decrypt(encryptionKey.get(), mechanism, nullptr /* param */,
unwrappedKey, &unwrappedKeyLen, sizeof(unwrappedKey),
wrappedKey, wrappedKeyLen);
EXPECT_EQ(rv, SECSuccess) << " CKM_NSS_AES_KEY_WRAP decrypt failed\n";
EXPECT_TRUE(!memcmp(keyData, unwrappedKey, unwrappedKeyLen));
test = UNWRAP;
if (tests.count(test)) {
rv = PK11_Decrypt(kek.get(), mechanism, nullptr /* param */,
unwrapped_key.data(), &unwrapped_key_len,
unwrapped_key.size(), expected_ciphertext,
expected_ciphertext_len);
ASSERT_EQ(rv, tests[test].expect_rv) << msg;
// If we failed, check that output was not produced.
if (rv == SECFailure) {
EXPECT_TRUE(unwrapped_key_len == 0);
EXPECT_TRUE(
!memcmp(unwrapped_key.data(), zeros.data(), unwrapped_key_len));
}
if (tests[test].output_match) {
EXPECT_EQ(unwrapped_key_len, key_data_len) << msg;
EXPECT_TRUE(!memcmp(key_data, unwrapped_key.data(), key_data_len))
<< msg;
} else {
// If we produced output, verify that it doesn't match the vector
if (unwrapped_key_len) {
EXPECT_FALSE(
unwrapped_key_len == expected_ciphertext_len &&
!memcmp(unwrapped_key.data(), key_data, unwrapped_key_len))
<< msg;
}
}
}
}
void WrapUnwrap(keywrap_vector testvector) {
WrapUnwrap(testvector.key.data(), testvector.key.size(),
testvector.msg.data(), testvector.msg.size(),
testvector.ct.data(), testvector.ct.size(), testvector.tests,
testvector.test_id);
}
};
TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest1) {
WrapUnwrap(kKEK1, sizeof(kKEK1), kKD1, sizeof(kKD1), kC1);
}
TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest2) {
WrapUnwrap(kKEK2, sizeof(kKEK2), kKD1, sizeof(kKD1), kC2);
}
TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest3) {
WrapUnwrap(kKEK3, sizeof(kKEK3), kKD1, sizeof(kKD1), kC3);
}
TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest4) {
WrapUnwrap(kKEK2, sizeof(kKEK2), kKD4, sizeof(kKD4), kC4);
}
TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest5) {
WrapUnwrap(kKEK3, sizeof(kKEK3), kKD4, sizeof(kKD4), kC5);
}
TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest6) {
WrapUnwrap(kKEK3, sizeof(kKEK3), kKD6, sizeof(kKD6), kC6);
}
TEST_P(Pkcs11AESKeyWrapTest, TestVectors) { WrapUnwrap(GetParam()); }
INSTANTIATE_TEST_CASE_P(Pkcs11WycheproofAESKWTest, Pkcs11AESKeyWrapTest,
::testing::ValuesIn(kWycheproofAesKWVectors));
} /* nss_test */

View file

@ -0,0 +1,415 @@
/* -*- 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 "gtest/gtest.h"
#include "nss.h"
#include "nss_scoped_ptrs.h"
#include "pk11pub.h"
namespace nss_test {
class Pkcs11AESKeyWrapPadTest : public ::testing::Test {};
// Encrypt an ephemeral EC key (U2F use case)
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapECKey) {
const uint32_t kwrappedBufLen = 256;
const uint32_t kPublicKeyLen = 65;
const uint32_t kOidLen = 65;
unsigned char param_buf[kOidLen];
unsigned char unwrap_buf[kPublicKeyLen];
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
SECItem ecdsa_params = {siBuffer, param_buf, sizeof(param_buf)};
SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
ASSERT_NE(oid_data, nullptr);
ecdsa_params.data[0] = SEC_ASN1_OBJECT_ID;
ecdsa_params.data[1] = oid_data->oid.len;
memcpy(ecdsa_params.data + 2, oid_data->oid.data, oid_data->oid.len);
ecdsa_params.len = oid_data->oid.len + 2;
SECKEYPublicKey* pub_tmp;
ScopedSECKEYPublicKey pub_key;
ScopedSECKEYPrivateKey priv_key(
PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsa_params,
&pub_tmp, PR_FALSE, PR_TRUE, nullptr));
ASSERT_NE(nullptr, priv_key);
ASSERT_NE(nullptr, pub_tmp);
pub_key.reset(pub_tmp);
// Generate a KEK.
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
ScopedSECItem wrapped(::SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen));
ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, nullptr));
SECStatus rv = PK11_WrapPrivKey(slot.get(), kek.get(), priv_key.get(),
CKM_NSS_AES_KEY_WRAP_PAD, param.get(),
wrapped.get(), nullptr);
ASSERT_EQ(rv, SECSuccess);
SECItem pubKey = {siBuffer, unwrap_buf, kPublicKeyLen};
CK_ATTRIBUTE_TYPE usages[] = {CKA_SIGN};
int usageCount = 1;
ScopedSECKEYPrivateKey unwrapped(
PK11_UnwrapPrivKey(slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD,
param.get(), wrapped.get(), nullptr, &pubKey, false,
true, CKK_EC, usages, usageCount, nullptr));
ASSERT_EQ(0, PORT_GetError());
ASSERT_TRUE(!!unwrapped);
}
// Encrypt an ephemeral RSA key
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRsaKey) {
const uint32_t kwrappedBufLen = 648;
unsigned char unwrap_buf[kwrappedBufLen];
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
PK11RSAGenParams rsa_param;
rsa_param.keySizeInBits = 1024;
rsa_param.pe = 65537L;
SECKEYPublicKey* pub_tmp;
ScopedSECKEYPublicKey pub_key;
ScopedSECKEYPrivateKey priv_key(
PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsa_param,
&pub_tmp, PR_FALSE, PR_FALSE, nullptr));
ASSERT_NE(nullptr, priv_key);
ASSERT_NE(nullptr, pub_tmp);
pub_key.reset(pub_tmp);
// Generate a KEK.
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
ScopedSECItem wrapped(::SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen));
ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, nullptr));
SECStatus rv = PK11_WrapPrivKey(slot.get(), kek.get(), priv_key.get(),
CKM_NSS_AES_KEY_WRAP_PAD, param.get(),
wrapped.get(), nullptr);
ASSERT_EQ(rv, SECSuccess);
SECItem pubKey = {siBuffer, unwrap_buf, kwrappedBufLen};
CK_ATTRIBUTE_TYPE usages[] = {CKA_SIGN};
int usageCount = 1;
ScopedSECKEYPrivateKey unwrapped(
PK11_UnwrapPrivKey(slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD,
param.get(), wrapped.get(), nullptr, &pubKey, false,
false, CKK_EC, usages, usageCount, nullptr));
ASSERT_EQ(0, PORT_GetError());
ASSERT_TRUE(!!unwrapped);
ScopedSECItem priv_key_data(
PK11_ExportDERPrivateKeyInfo(priv_key.get(), nullptr));
ScopedSECItem unwrapped_data(
PK11_ExportDERPrivateKeyInfo(unwrapped.get(), nullptr));
EXPECT_TRUE(!!priv_key_data);
EXPECT_TRUE(!!unwrapped_data);
ASSERT_EQ(priv_key_data->len, unwrapped_data->len);
ASSERT_EQ(
0, memcmp(priv_key_data->data, unwrapped_data->data, priv_key_data->len));
}
// Wrap a random that's a multiple of the block size, and compare the unwrap
// result.
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_EvenBlock) {
const uint32_t kInputKeyLen = 128;
uint32_t out_len = 0;
std::vector<unsigned char> input_key(kInputKeyLen);
std::vector<unsigned char> wrapped_key(
kInputKeyLen + AES_BLOCK_SIZE); // One block of padding
std::vector<unsigned char> unwrapped_key(
kInputKeyLen + AES_BLOCK_SIZE); // One block of padding
// Generate input key material
SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size());
EXPECT_EQ(SECSuccess, rv);
// Generate a KEK.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
wrapped_key.data(), &out_len,
static_cast<unsigned int>(wrapped_key.size()),
input_key.data(),
static_cast<unsigned int>(input_key.size()));
ASSERT_EQ(SECSuccess, rv);
rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
unwrapped_key.data(), &out_len,
static_cast<unsigned int>(unwrapped_key.size()),
wrapped_key.data(), out_len);
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(input_key.size(), out_len);
ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len));
}
// Wrap a random that's NOT a multiple of the block size, and compare the unwrap
// result.
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_OddBlock1) {
const uint32_t kInputKeyLen = 65;
uint32_t out_len = 0;
std::vector<unsigned char> input_key(kInputKeyLen);
std::vector<unsigned char> wrapped_key(
kInputKeyLen + AES_BLOCK_SIZE); // One block of padding
std::vector<unsigned char> unwrapped_key(
kInputKeyLen + AES_BLOCK_SIZE); // One block of padding
// Generate input key material
SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size());
EXPECT_EQ(SECSuccess, rv);
// Generate a KEK.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
wrapped_key.data(), &out_len,
static_cast<unsigned int>(wrapped_key.size()),
input_key.data(),
static_cast<unsigned int>(input_key.size()));
ASSERT_EQ(SECSuccess, rv);
rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
unwrapped_key.data(), &out_len,
static_cast<unsigned int>(unwrapped_key.size()),
wrapped_key.data(), out_len);
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(input_key.size(), out_len);
ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len));
}
// Wrap a random that's NOT a multiple of the block size, and compare the unwrap
// result.
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_OddBlock2) {
const uint32_t kInputKeyLen = 63;
uint32_t out_len = 0;
std::vector<unsigned char> input_key(kInputKeyLen);
std::vector<unsigned char> wrapped_key(
kInputKeyLen + AES_BLOCK_SIZE); // One block of padding
std::vector<unsigned char> unwrapped_key(
kInputKeyLen + AES_BLOCK_SIZE); // One block of padding
// Generate input key material
SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size());
EXPECT_EQ(SECSuccess, rv);
// Generate a KEK.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
wrapped_key.data(), &out_len, wrapped_key.size(),
input_key.data(), input_key.size());
ASSERT_EQ(SECSuccess, rv);
rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
unwrapped_key.data(), &out_len,
static_cast<unsigned int>(unwrapped_key.size()),
wrapped_key.data(), out_len);
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(input_key.size(), out_len);
ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len));
}
// Invalid long padding (over the block size, but otherwise valid)
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_PaddingTooLong) {
const uint32_t kInputKeyLen = 32;
uint32_t out_len = 0;
// Apply our own padding
const unsigned char buf[32] = {
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
// Generate a KEK.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
SECStatus rv =
PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding
/* param */ nullptr, wrapped_key.data(), &out_len,
wrapped_key.size(), buf, sizeof(buf));
ASSERT_EQ(SECSuccess, rv);
rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
unwrapped_key.data(), &out_len,
static_cast<unsigned int>(unwrapped_key.size()),
wrapped_key.data(), out_len);
ASSERT_EQ(SECFailure, rv);
}
// Invalid 0-length padding (there should be a full block if the message doesn't
// need to be padded)
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_NoPadding) {
const uint32_t kInputKeyLen = 32;
uint32_t out_len = 0;
// Apply our own padding
const unsigned char buf[32] = {0};
std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
// Generate a KEK.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
SECStatus rv =
PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding
/* param */ nullptr, wrapped_key.data(), &out_len,
wrapped_key.size(), buf, sizeof(buf));
ASSERT_EQ(SECSuccess, rv);
rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
unwrapped_key.data(), &out_len,
static_cast<unsigned int>(unwrapped_key.size()),
wrapped_key.data(), out_len);
ASSERT_EQ(SECFailure, rv);
}
// Invalid padding
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_BadPadding1) {
const uint32_t kInputKeyLen = 32;
uint32_t out_len = 0;
// Apply our own padding
const unsigned char buf[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08}; // Check all 8 bytes
std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
// Generate a KEK.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
SECStatus rv =
PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding
/* param */ nullptr, wrapped_key.data(), &out_len,
wrapped_key.size(), buf, sizeof(buf));
ASSERT_EQ(SECSuccess, rv);
rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
unwrapped_key.data(), &out_len,
static_cast<unsigned int>(unwrapped_key.size()),
wrapped_key.data(), out_len);
ASSERT_EQ(SECFailure, rv);
}
// Invalid padding
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_BadPadding2) {
const uint32_t kInputKeyLen = 32;
uint32_t out_len = 0;
// Apply our own padding
const unsigned char
buf[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x02}; // Check first loop repeat
std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
// Generate a KEK.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
SECStatus rv =
PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding
/* param */ nullptr, wrapped_key.data(), &out_len,
wrapped_key.size(), buf, sizeof(buf));
ASSERT_EQ(SECSuccess, rv);
rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
unwrapped_key.data(), &out_len,
static_cast<unsigned int>(unwrapped_key.size()),
wrapped_key.data(), out_len);
ASSERT_EQ(SECFailure, rv);
}
// Minimum valid padding
TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_ShortValidPadding) {
const uint32_t kInputKeyLen = 32;
uint32_t out_len = 0;
// Apply our own padding
const unsigned char buf[kInputKeyLen] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; // Minimum
std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE);
// Generate a KEK.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
// Wrap the key
SECStatus rv =
PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding
/* param */ nullptr, wrapped_key.data(), &out_len,
wrapped_key.size(), buf, sizeof(buf));
ASSERT_EQ(SECSuccess, rv);
rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr,
unwrapped_key.data(), &out_len,
static_cast<unsigned int>(unwrapped_key.size()),
wrapped_key.data(), out_len);
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(kInputKeyLen - 1, out_len);
ASSERT_EQ(0, memcmp(buf, unwrapped_key.data(), out_len));
}
} /* nss_test */

View file

@ -0,0 +1,558 @@
/* -*- 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 "nss.h"
#include "pk11pub.h"
#include "secerr.h"
#include "nss_scoped_ptrs.h"
#include "gtest/gtest.h"
namespace nss_test {
static const uint8_t kInput[99] = {1, 2, 3};
static const uint8_t kKeyData[24] = {'K', 'E', 'Y'};
static SECItem* GetIv() {
static const uint8_t kIvData[16] = {'I', 'V'};
static const SECItem kIv = {siBuffer, const_cast<uint8_t*>(kIvData),
static_cast<unsigned int>(sizeof(kIvData))};
return const_cast<SECItem*>(&kIv);
}
class Pkcs11CbcPadTest : public ::testing::TestWithParam<CK_MECHANISM_TYPE> {
protected:
bool is_padded() const {
switch (GetParam()) {
case CKM_AES_CBC_PAD:
case CKM_DES3_CBC_PAD:
return true;
case CKM_AES_CBC:
case CKM_DES3_CBC:
return false;
default:
ADD_FAILURE() << "Unknown mechanism " << GetParam();
}
return false;
}
uint32_t GetUnpaddedMechanism() const {
switch (GetParam()) {
case CKM_AES_CBC_PAD:
return CKM_AES_CBC;
case CKM_DES3_CBC_PAD:
return CKM_DES3_CBC;
default:
ADD_FAILURE() << "Unknown padded mechanism " << GetParam();
}
return 0;
}
size_t block_size() const {
return static_cast<size_t>(PK11_GetBlockSize(GetParam(), nullptr));
}
size_t GetInputLen(CK_ATTRIBUTE_TYPE op) const {
if (is_padded() && op == CKA_ENCRYPT) {
// Anything goes for encryption when padded.
return sizeof(kInput);
}
// Otherwise, use a strict multiple of the block size.
size_t block_count = sizeof(kInput) / block_size();
EXPECT_LT(1U, block_count) << "need 2 blocks for tests";
return block_count * block_size();
}
ScopedPK11SymKey MakeKey(CK_ATTRIBUTE_TYPE op) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
EXPECT_NE(nullptr, slot);
if (!slot) {
return nullptr;
}
unsigned int key_len = 0;
switch (GetParam()) {
case CKM_AES_CBC_PAD:
case CKM_AES_CBC:
key_len = 16; // This doesn't do AES-256 to keep it simple.
break;
case CKM_DES3_CBC_PAD:
case CKM_DES3_CBC:
key_len = 24;
break;
default:
ADD_FAILURE() << "Unknown mechanism " << GetParam();
return nullptr;
}
SECItem key_item = {siBuffer, const_cast<uint8_t*>(kKeyData), key_len};
PK11SymKey* p = PK11_ImportSymKey(slot.get(), GetParam(), PK11_OriginUnwrap,
op, &key_item, nullptr);
EXPECT_NE(nullptr, p);
return ScopedPK11SymKey(p);
}
ScopedPK11Context MakeContext(CK_ATTRIBUTE_TYPE op) {
ScopedPK11SymKey k = MakeKey(op);
PK11Context* ctx =
PK11_CreateContextBySymKey(GetParam(), op, k.get(), GetIv());
EXPECT_NE(nullptr, ctx);
return ScopedPK11Context(ctx);
}
};
TEST_P(Pkcs11CbcPadTest, EncryptDecrypt) {
uint8_t encrypted[sizeof(kInput) + 64]; // Allow for padding and expansion.
size_t input_len = GetInputLen(CKA_ENCRYPT);
ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT);
unsigned int encrypted_len = 0;
SECStatus rv =
PK11_Encrypt(ek.get(), GetParam(), GetIv(), encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
ASSERT_EQ(SECSuccess, rv);
EXPECT_LE(input_len, static_cast<size_t>(encrypted_len));
// Though the decrypted result can't be larger than the input we provided,
// NSS needs extra space to put the padding in.
uint8_t decrypted[sizeof(kInput) + 64];
unsigned int decrypted_len = 0;
ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT);
rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted, &decrypted_len,
sizeof(decrypted), encrypted, encrypted_len);
ASSERT_EQ(SECSuccess, rv);
EXPECT_EQ(input_len, static_cast<size_t>(decrypted_len));
EXPECT_EQ(0, memcmp(kInput, decrypted, input_len));
}
TEST_P(Pkcs11CbcPadTest, ContextEncryptDecrypt) {
uint8_t encrypted[sizeof(kInput) + 64]; // Allow for padding and expansion.
size_t input_len = GetInputLen(CKA_ENCRYPT);
ScopedPK11Context ectx = MakeContext(CKA_ENCRYPT);
int encrypted_len = 0;
SECStatus rv = PK11_CipherOp(ectx.get(), encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
ASSERT_EQ(SECSuccess, rv);
EXPECT_LE(0, encrypted_len); // Stupid signed parameters.
unsigned int final_len = 0;
rv = PK11_CipherFinal(ectx.get(), encrypted + encrypted_len, &final_len,
sizeof(encrypted) - encrypted_len);
ASSERT_EQ(SECSuccess, rv);
encrypted_len += final_len;
EXPECT_LE(input_len, static_cast<size_t>(encrypted_len));
uint8_t decrypted[sizeof(kInput) + 64];
int decrypted_len = 0;
ScopedPK11Context dctx = MakeContext(CKA_DECRYPT);
rv = PK11_CipherOp(dctx.get(), decrypted, &decrypted_len, sizeof(decrypted),
encrypted, encrypted_len);
ASSERT_EQ(SECSuccess, rv);
EXPECT_LE(0, decrypted_len);
rv = PK11_CipherFinal(dctx.get(), decrypted + decrypted_len, &final_len,
sizeof(decrypted) - decrypted_len);
ASSERT_EQ(SECSuccess, rv);
decrypted_len += final_len;
EXPECT_EQ(input_len, static_cast<size_t>(decrypted_len));
EXPECT_EQ(0, memcmp(kInput, decrypted, input_len));
}
TEST_P(Pkcs11CbcPadTest, ContextEncryptDecryptTwoParts) {
uint8_t encrypted[sizeof(kInput) + 64];
size_t input_len = GetInputLen(CKA_ENCRYPT);
ScopedPK11Context ectx = MakeContext(CKA_ENCRYPT);
int first_len = 0;
SECStatus rv = PK11_CipherOp(ectx.get(), encrypted, &first_len,
sizeof(encrypted), kInput, block_size());
ASSERT_EQ(SECSuccess, rv);
ASSERT_LE(0, first_len);
int second_len = 0;
rv = PK11_CipherOp(ectx.get(), encrypted + first_len, &second_len,
sizeof(encrypted) - first_len, kInput + block_size(),
input_len - block_size());
ASSERT_EQ(SECSuccess, rv);
ASSERT_LE(0, second_len);
unsigned int final_len = 0;
rv = PK11_CipherFinal(ectx.get(), encrypted + first_len + second_len,
&final_len, sizeof(encrypted) - first_len - second_len);
ASSERT_EQ(SECSuccess, rv);
unsigned int encrypted_len = first_len + second_len + final_len;
ASSERT_LE(input_len, static_cast<size_t>(encrypted_len));
// Now decrypt this in a similar fashion.
uint8_t decrypted[sizeof(kInput) + 64];
ScopedPK11Context dctx = MakeContext(CKA_DECRYPT);
rv = PK11_CipherOp(dctx.get(), decrypted, &first_len, sizeof(decrypted),
encrypted, block_size());
ASSERT_EQ(SECSuccess, rv);
EXPECT_LE(0, first_len);
rv = PK11_CipherOp(dctx.get(), decrypted + first_len, &second_len,
sizeof(decrypted) - first_len, encrypted + block_size(),
encrypted_len - block_size());
ASSERT_EQ(SECSuccess, rv);
EXPECT_LE(0, second_len);
unsigned int decrypted_len = 0;
rv = PK11_CipherFinal(dctx.get(), decrypted + first_len + second_len,
&decrypted_len,
sizeof(decrypted) - first_len - second_len);
ASSERT_EQ(SECSuccess, rv);
decrypted_len += first_len + second_len;
EXPECT_EQ(input_len, static_cast<size_t>(decrypted_len));
EXPECT_EQ(0, memcmp(kInput, decrypted, input_len));
}
TEST_P(Pkcs11CbcPadTest, FailDecryptSimple) {
ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT);
uint8_t output[sizeof(kInput) + 64];
unsigned int output_len = 999;
SECStatus rv =
PK11_Decrypt(dk.get(), GetParam(), GetIv(), output, &output_len,
sizeof(output), kInput, GetInputLen(CKA_DECRYPT));
if (is_padded()) {
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(999U, output_len);
} else {
// Unpadded decryption can't really fail.
EXPECT_EQ(SECSuccess, rv);
}
}
TEST_P(Pkcs11CbcPadTest, FailEncryptSimple) {
ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT);
uint8_t output[3]; // Too small for anything.
unsigned int output_len = 333;
SECStatus rv =
PK11_Encrypt(ek.get(), GetParam(), GetIv(), output, &output_len,
sizeof(output), kInput, GetInputLen(CKA_ENCRYPT));
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(333U, output_len);
}
// It's a bit of a lie to put this in pk11_cbc_unittest, since we
// also test bounds checking in other modes. There doesn't seem
// to be an appropriately-generic place elsewhere.
TEST_F(Pkcs11CbcPadTest, FailEncryptShortParam) {
SECStatus rv = SECFailure;
uint8_t encrypted[sizeof(kInput)];
unsigned int encrypted_len = 0;
size_t input_len = AES_BLOCK_SIZE;
// CK_GCM_PARAMS is the largest param struct used across AES modes
uint8_t param_buf[sizeof(CK_GCM_PARAMS)];
SECItem param = {siBuffer, param_buf, sizeof(param_buf)};
SECItem key_item = {siBuffer, const_cast<uint8_t*>(kKeyData), 16};
// Setup (we use the ECB key for other modes)
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), CKM_AES_ECB,
PK11_OriginUnwrap, CKA_ENCRYPT,
&key_item, nullptr));
ASSERT_TRUE(key.get());
// CTR should have a CK_AES_CTR_PARAMS
param.len = sizeof(CK_AES_CTR_PARAMS) - 1;
rv = PK11_Encrypt(key.get(), CKM_AES_CTR, &param, encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
EXPECT_EQ(SECFailure, rv);
param.len++;
reinterpret_cast<CK_AES_CTR_PARAMS*>(param.data)->ulCounterBits = 32;
rv = PK11_Encrypt(key.get(), CKM_AES_CTR, &param, encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
EXPECT_EQ(SECSuccess, rv);
// GCM should have a CK_GCM_PARAMS
param.len = sizeof(CK_GCM_PARAMS) - 1;
rv = PK11_Encrypt(key.get(), CKM_AES_GCM, &param, encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
EXPECT_EQ(SECFailure, rv);
param.len++;
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->pIv = param_buf;
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->ulIvLen = 12;
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->pAAD = nullptr;
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->ulAADLen = 0;
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->ulTagBits = 128;
rv = PK11_Encrypt(key.get(), CKM_AES_GCM, &param, encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
EXPECT_EQ(SECSuccess, rv);
// CBC should have a 16B IV
param.len = AES_BLOCK_SIZE - 1;
rv = PK11_Encrypt(key.get(), CKM_AES_CBC, &param, encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
EXPECT_EQ(SECFailure, rv);
param.len++;
rv = PK11_Encrypt(key.get(), CKM_AES_CBC, &param, encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
EXPECT_EQ(SECSuccess, rv);
// CTS
param.len = AES_BLOCK_SIZE - 1;
rv = PK11_Encrypt(key.get(), CKM_AES_CTS, &param, encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
EXPECT_EQ(SECFailure, rv);
param.len++;
rv = PK11_Encrypt(key.get(), CKM_AES_CTS, &param, encrypted, &encrypted_len,
sizeof(encrypted), kInput, input_len);
EXPECT_EQ(SECSuccess, rv);
}
TEST_P(Pkcs11CbcPadTest, ContextFailDecryptSimple) {
ScopedPK11Context dctx = MakeContext(CKA_DECRYPT);
uint8_t output[sizeof(kInput) + 64];
int output_len = 77;
SECStatus rv = PK11_CipherOp(dctx.get(), output, &output_len, sizeof(output),
kInput, GetInputLen(CKA_DECRYPT));
EXPECT_EQ(SECSuccess, rv);
EXPECT_LE(0, output_len) << "this is not an AEAD, so content leaks";
unsigned int final_len = 88;
rv = PK11_CipherFinal(dctx.get(), output, &final_len, sizeof(output));
if (is_padded()) {
EXPECT_EQ(SECFailure, rv);
ASSERT_EQ(88U, final_len) << "final_len should be untouched";
} else {
// Unpadded decryption can't really fail.
EXPECT_EQ(SECSuccess, rv);
}
}
TEST_P(Pkcs11CbcPadTest, ContextFailDecryptInvalidBlockSize) {
ScopedPK11Context dctx = MakeContext(CKA_DECRYPT);
uint8_t output[sizeof(kInput) + 64];
int output_len = 888;
SECStatus rv = PK11_CipherOp(dctx.get(), output, &output_len, sizeof(output),
kInput, GetInputLen(CKA_DECRYPT) - 1);
EXPECT_EQ(SECFailure, rv);
// Because PK11_CipherOp is partial, it can return data on failure.
// This means that it needs to reset its output length to 0 when it starts.
EXPECT_EQ(0, output_len) << "output_len is reset";
}
TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_PaddingTooLong) {
if (!is_padded()) {
return;
}
// Padding that's over the block size
const std::vector<uint8_t> input = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
std::vector<uint8_t> encrypted(input.size());
uint32_t encrypted_len = 0;
ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT);
SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(),
encrypted.data(), &encrypted_len,
encrypted.size(), input.data(), input.size());
ASSERT_EQ(SECSuccess, rv);
EXPECT_EQ(input.size(), encrypted_len);
std::vector<uint8_t> decrypted(input.size());
uint32_t decrypted_len = 0;
ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT);
rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(),
&decrypted_len, decrypted.size(), encrypted.data(),
encrypted_len);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, decrypted_len);
}
TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ShortPadding1) {
if (!is_padded()) {
return;
}
// Padding that's one byte short
const std::vector<uint8_t> input = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
std::vector<uint8_t> encrypted(input.size());
uint32_t encrypted_len = 0;
ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT);
SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(),
encrypted.data(), &encrypted_len,
encrypted.size(), input.data(), input.size());
ASSERT_EQ(SECSuccess, rv);
EXPECT_EQ(input.size(), encrypted_len);
std::vector<uint8_t> decrypted(input.size());
uint32_t decrypted_len = 0;
ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT);
rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(),
&decrypted_len, decrypted.size(), encrypted.data(),
encrypted_len);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, decrypted_len);
}
TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ShortPadding2) {
if (!is_padded()) {
return;
}
// Padding that's one byte short
const std::vector<uint8_t> input = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02};
std::vector<uint8_t> encrypted(input.size());
uint32_t encrypted_len = 0;
ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT);
SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(),
encrypted.data(), &encrypted_len,
encrypted.size(), input.data(), input.size());
ASSERT_EQ(SECSuccess, rv);
EXPECT_EQ(input.size(), encrypted_len);
std::vector<uint8_t> decrypted(input.size());
uint32_t decrypted_len = 0;
ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT);
rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(),
&decrypted_len, decrypted.size(), encrypted.data(),
encrypted_len);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, decrypted_len);
}
TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ZeroLengthPadding) {
if (!is_padded()) {
return;
}
// Padding of length zero
const std::vector<uint8_t> input = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
std::vector<uint8_t> encrypted(input.size());
uint32_t encrypted_len = 0;
ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT);
SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(),
encrypted.data(), &encrypted_len,
encrypted.size(), input.data(), input.size());
ASSERT_EQ(SECSuccess, rv);
EXPECT_EQ(input.size(), encrypted_len);
std::vector<uint8_t> decrypted(input.size());
uint32_t decrypted_len = 0;
ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT);
rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(),
&decrypted_len, decrypted.size(), encrypted.data(),
encrypted_len);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, decrypted_len);
}
TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_OverflowPadding) {
if (!is_padded()) {
return;
}
// Padding that's much longer than block size
const std::vector<uint8_t> input = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
std::vector<uint8_t> encrypted(input.size());
uint32_t encrypted_len = 0;
ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT);
SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(),
encrypted.data(), &encrypted_len,
encrypted.size(), input.data(), input.size());
ASSERT_EQ(SECSuccess, rv);
EXPECT_EQ(input.size(), encrypted_len);
std::vector<uint8_t> decrypted(input.size());
uint32_t decrypted_len = 0;
ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT);
rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(),
&decrypted_len, decrypted.size(), encrypted.data(),
encrypted_len);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, decrypted_len);
}
TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ShortValidPadding) {
if (!is_padded()) {
return;
}
// Minimal valid padding
const std::vector<uint8_t> input = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
std::vector<uint8_t> encrypted(input.size());
uint32_t encrypted_len = 0;
ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT);
SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(),
encrypted.data(), &encrypted_len,
encrypted.size(), input.data(), input.size());
ASSERT_EQ(SECSuccess, rv);
EXPECT_EQ(input.size(), encrypted_len);
std::vector<uint8_t> decrypted(input.size());
uint32_t decrypted_len = 0;
ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT);
rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(),
&decrypted_len, decrypted.size(), encrypted.data(),
encrypted_len);
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(input.size() - 1, decrypted_len);
EXPECT_EQ(0, memcmp(decrypted.data(), input.data(), decrypted_len));
}
INSTANTIATE_TEST_CASE_P(EncryptDecrypt, Pkcs11CbcPadTest,
::testing::Values(CKM_AES_CBC_PAD, CKM_AES_CBC,
CKM_DES3_CBC_PAD, CKM_DES3_CBC));
} // namespace nss_test

View file

@ -8,114 +8,31 @@
#include "nss.h"
#include "pk11pub.h"
#include "sechash.h"
#include "secerr.h"
#include "cpputil.h"
#include "nss_scoped_ptrs.h"
#include "testvectors/chachapoly-vectors.h"
#include "gtest/gtest.h"
namespace nss_test {
// ChaCha20/Poly1305 Test Vector 1, RFC 7539
// <http://tools.ietf.org/html/rfc7539#section-2.8.2>
const uint8_t kTestVector1Data[] = {
0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47,
0x65, 0x6e, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x6f, 0x66,
0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79,
0x6f, 0x75, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20,
0x62, 0x65, 0x20, 0x69, 0x74, 0x2e};
const uint8_t kTestVector1AAD[] = {0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1,
0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7};
const uint8_t kTestVector1Key[] = {
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f};
const uint8_t kTestVector1IV[] = {0x07, 0x00, 0x00, 0x00, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47};
const uint8_t kTestVector1CT[] = {
0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc,
0x53, 0xef, 0x7e, 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e,
0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6,
0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4,
0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65,
0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16, 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09,
0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91};
static const CK_MECHANISM_TYPE kMech = CKM_NSS_CHACHA20_POLY1305;
static const CK_MECHANISM_TYPE kMechXor = CKM_NSS_CHACHA20_CTR;
// Some test data for simple tests.
static const uint8_t kKeyData[32] = {'k'};
static const uint8_t kCtrNonce[16] = {'c', 0, 0, 0, 'n'};
static const uint8_t kData[16] = {'d'};
// ChaCha20/Poly1305 Test Vector 2, RFC 7539
// <http://tools.ietf.org/html/rfc7539#appendix-A.5>
const uint8_t kTestVector2Data[] = {
0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44, 0x72, 0x61,
0x66, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x72, 0x61, 0x66,
0x74, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20,
0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20,
0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x6f, 0x66, 0x20, 0x73,
0x69, 0x78, 0x20, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x20, 0x61, 0x6e,
0x64, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63,
0x65, 0x64, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x73, 0x6f, 0x6c,
0x65, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6f, 0x74, 0x68, 0x65,
0x72, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20,
0x61, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2e,
0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x61, 0x70, 0x70,
0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20,
0x75, 0x73, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x73, 0x20, 0x61, 0x73, 0x20, 0x72,
0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x6d, 0x61, 0x74,
0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20,
0x63, 0x69, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x20, 0x6f, 0x74,
0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x20,
0x2f, 0xe2, 0x80, 0x9c, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x20,
0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x2f, 0xe2, 0x80,
0x9d};
const uint8_t kTestVector2AAD[] = {0xf3, 0x33, 0x88, 0x86, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4e, 0x91};
const uint8_t kTestVector2Key[] = {
0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88,
0x86, 0x04, 0xf6, 0xb5, 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b,
0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0};
const uint8_t kTestVector2IV[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
const uint8_t kTestVector2CT[] = {
0x64, 0xa0, 0x86, 0x15, 0x75, 0x86, 0x1a, 0xf4, 0x60, 0xf0, 0x62, 0xc7,
0x9b, 0xe6, 0x43, 0xbd, 0x5e, 0x80, 0x5c, 0xfd, 0x34, 0x5c, 0xf3, 0x89,
0xf1, 0x08, 0x67, 0x0a, 0xc7, 0x6c, 0x8c, 0xb2, 0x4c, 0x6c, 0xfc, 0x18,
0x75, 0x5d, 0x43, 0xee, 0xa0, 0x9e, 0xe9, 0x4e, 0x38, 0x2d, 0x26, 0xb0,
0xbd, 0xb7, 0xb7, 0x3c, 0x32, 0x1b, 0x01, 0x00, 0xd4, 0xf0, 0x3b, 0x7f,
0x35, 0x58, 0x94, 0xcf, 0x33, 0x2f, 0x83, 0x0e, 0x71, 0x0b, 0x97, 0xce,
0x98, 0xc8, 0xa8, 0x4a, 0xbd, 0x0b, 0x94, 0x81, 0x14, 0xad, 0x17, 0x6e,
0x00, 0x8d, 0x33, 0xbd, 0x60, 0xf9, 0x82, 0xb1, 0xff, 0x37, 0xc8, 0x55,
0x97, 0x97, 0xa0, 0x6e, 0xf4, 0xf0, 0xef, 0x61, 0xc1, 0x86, 0x32, 0x4e,
0x2b, 0x35, 0x06, 0x38, 0x36, 0x06, 0x90, 0x7b, 0x6a, 0x7c, 0x02, 0xb0,
0xf9, 0xf6, 0x15, 0x7b, 0x53, 0xc8, 0x67, 0xe4, 0xb9, 0x16, 0x6c, 0x76,
0x7b, 0x80, 0x4d, 0x46, 0xa5, 0x9b, 0x52, 0x16, 0xcd, 0xe7, 0xa4, 0xe9,
0x90, 0x40, 0xc5, 0xa4, 0x04, 0x33, 0x22, 0x5e, 0xe2, 0x82, 0xa1, 0xb0,
0xa0, 0x6c, 0x52, 0x3e, 0xaf, 0x45, 0x34, 0xd7, 0xf8, 0x3f, 0xa1, 0x15,
0x5b, 0x00, 0x47, 0x71, 0x8c, 0xbc, 0x54, 0x6a, 0x0d, 0x07, 0x2b, 0x04,
0xb3, 0x56, 0x4e, 0xea, 0x1b, 0x42, 0x22, 0x73, 0xf5, 0x48, 0x27, 0x1a,
0x0b, 0xb2, 0x31, 0x60, 0x53, 0xfa, 0x76, 0x99, 0x19, 0x55, 0xeb, 0xd6,
0x31, 0x59, 0x43, 0x4e, 0xce, 0xbb, 0x4e, 0x46, 0x6d, 0xae, 0x5a, 0x10,
0x73, 0xa6, 0x72, 0x76, 0x27, 0x09, 0x7a, 0x10, 0x49, 0xe6, 0x17, 0xd9,
0x1d, 0x36, 0x10, 0x94, 0xfa, 0x68, 0xf0, 0xff, 0x77, 0x98, 0x71, 0x30,
0x30, 0x5b, 0xea, 0xba, 0x2e, 0xda, 0x04, 0xdf, 0x99, 0x7b, 0x71, 0x4d,
0x6c, 0x6f, 0x2c, 0x29, 0xa6, 0xad, 0x5c, 0xb4, 0x02, 0x2b, 0x02, 0x70,
0x9b, 0xee, 0xad, 0x9d, 0x67, 0x89, 0x0c, 0xbb, 0x22, 0x39, 0x23, 0x36,
0xfe, 0xa1, 0x85, 0x1f, 0x38};
class Pkcs11ChaCha20Poly1305Test : public ::testing::Test {
class Pkcs11ChaCha20Poly1305Test
: public ::testing::TestWithParam<chaChaTestVector> {
public:
void EncryptDecrypt(PK11SymKey* symKey, const uint8_t* data, size_t data_len,
const uint8_t* aad, size_t aad_len, const uint8_t* iv,
size_t iv_len, const uint8_t* ct = nullptr,
size_t ct_len = 0) {
void EncryptDecrypt(const ScopedPK11SymKey& key, const bool invalid_iv,
const bool invalid_tag, const uint8_t* data,
size_t data_len, const uint8_t* aad, size_t aad_len,
const uint8_t* iv, size_t iv_len,
const uint8_t* ct = nullptr, size_t ct_len = 0) {
// Prepare AEAD params.
CK_NSS_AEAD_PARAMS aead_params;
aead_params.pNonce = toUcharPtr(iv);
@ -127,135 +44,261 @@ class Pkcs11ChaCha20Poly1305Test : public ::testing::Test {
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params),
sizeof(aead_params)};
// Encrypt with bad parameters.
unsigned int encrypted_len = 0;
std::vector<uint8_t> encrypted(data_len + aead_params.ulTagLen);
aead_params.ulTagLen = 158072;
SECStatus rv =
PK11_Encrypt(key.get(), kMech, &params, encrypted.data(),
&encrypted_len, encrypted.size(), data, data_len);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, encrypted_len);
aead_params.ulTagLen = 16;
// Encrypt.
unsigned int outputLen = 0;
std::vector<uint8_t> output(data_len + aead_params.ulTagLen);
SECStatus rv = PK11_Encrypt(symKey, mech, &params, &output[0], &outputLen,
output.size(), data, data_len);
rv = PK11_Encrypt(key.get(), kMech, &params, encrypted.data(),
&encrypted_len, encrypted.size(), data, data_len);
// Return if encryption failure was expected due to invalid IV.
// Without valid ciphertext, all further tests can be skipped.
if (invalid_iv) {
EXPECT_EQ(rv, SECFailure);
EXPECT_EQ(0U, encrypted_len)
<< "encrypted_len is unmodified after failure";
return;
}
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(encrypted.size(), static_cast<size_t>(encrypted_len));
// Check ciphertext and tag.
if (ct) {
EXPECT_TRUE(!memcmp(ct, &output[0], outputLen));
ASSERT_EQ(ct_len, encrypted_len);
EXPECT_TRUE(!memcmp(ct, encrypted.data(), encrypted.size()) !=
invalid_tag);
}
// Decrypt.
unsigned int decryptedLen = 0;
std::vector<uint8_t> decrypted(data_len);
rv = PK11_Decrypt(symKey, mech, &params, &decrypted[0], &decryptedLen,
decrypted.size(), &output[0], outputLen);
// Get the *estimated* plaintext length. This value should
// never be zero as it could lead to a NULL outPtr being
// passed to a subsequent decryption call (for AEAD we
// must authenticate even when the pt is zero-length).
unsigned int decrypt_bytes_needed = 0;
rv = PK11_Decrypt(key.get(), kMech, &params, nullptr, &decrypt_bytes_needed,
0, encrypted.data(), encrypted_len);
EXPECT_EQ(rv, SECSuccess);
EXPECT_GT(decrypt_bytes_needed, data_len);
// Now decrypt it
std::vector<uint8_t> decrypted(decrypt_bytes_needed);
unsigned int decrypted_len = 0;
rv = PK11_Decrypt(key.get(), kMech, &params, decrypted.data(),
&decrypted_len, decrypted.size(), encrypted.data(),
encrypted.size());
EXPECT_EQ(rv, SECSuccess);
// Check the plaintext.
EXPECT_TRUE(!memcmp(data, &decrypted[0], decryptedLen));
ASSERT_EQ(data_len, decrypted_len);
EXPECT_TRUE(!memcmp(data, decrypted.data(), decrypted_len));
// Decrypt with bogus data.
{
std::vector<uint8_t> bogusCiphertext(output);
bogusCiphertext[0] ^= 0xff;
rv = PK11_Decrypt(symKey, mech, &params, &decrypted[0], &decryptedLen,
decrypted.size(), &bogusCiphertext[0], outputLen);
EXPECT_NE(rv, SECSuccess);
// Skip if there's no data to modify.
if (encrypted_len > 0) {
decrypted_len = 0;
std::vector<uint8_t> bogus_ciphertext(encrypted);
bogus_ciphertext[0] ^= 0xff;
rv = PK11_Decrypt(key.get(), kMech, &params, decrypted.data(),
&decrypted_len, decrypted.size(),
bogus_ciphertext.data(), encrypted_len);
EXPECT_EQ(rv, SECFailure);
EXPECT_EQ(0U, decrypted_len);
}
// Decrypt with bogus tag.
{
std::vector<uint8_t> bogusTag(output);
bogusTag[outputLen - 1] ^= 0xff;
rv = PK11_Decrypt(symKey, mech, &params, &decrypted[0], &decryptedLen,
decrypted.size(), &bogusTag[0], outputLen);
EXPECT_NE(rv, SECSuccess);
// Skip if there's no tag to modify.
if (encrypted_len > 0) {
decrypted_len = 0;
std::vector<uint8_t> bogus_tag(encrypted);
bogus_tag[encrypted_len - 1] ^= 0xff;
rv = PK11_Decrypt(key.get(), kMech, &params, decrypted.data(),
&decrypted_len, decrypted.size(), bogus_tag.data(),
encrypted_len);
EXPECT_EQ(rv, SECFailure);
EXPECT_EQ(0U, decrypted_len);
}
// Decrypt with bogus IV.
{
SECItem bogusParams(params);
// iv_len == 0 is invalid and should be caught earlier.
// Still skip, if there's no IV to modify.
if (iv_len != 0) {
decrypted_len = 0;
SECItem bogus_params(params);
CK_NSS_AEAD_PARAMS bogusAeadParams(aead_params);
bogusParams.data = reinterpret_cast<unsigned char*>(&bogusAeadParams);
bogus_params.data = reinterpret_cast<unsigned char*>(&bogusAeadParams);
std::vector<uint8_t> bogusIV(iv, iv + iv_len);
bogusAeadParams.pNonce = toUcharPtr(&bogusIV[0]);
bogusAeadParams.pNonce = toUcharPtr(bogusIV.data());
bogusIV[0] ^= 0xff;
rv = PK11_Decrypt(symKey, mech, &bogusParams, &decrypted[0],
&decryptedLen, data_len, &output[0], outputLen);
EXPECT_NE(rv, SECSuccess);
rv = PK11_Decrypt(key.get(), kMech, &bogus_params, decrypted.data(),
&decrypted_len, data_len, encrypted.data(),
encrypted.size());
EXPECT_EQ(rv, SECFailure);
EXPECT_EQ(0U, decrypted_len);
}
// Decrypt with bogus additional data.
{
SECItem bogusParams(params);
CK_NSS_AEAD_PARAMS bogusAeadParams(aead_params);
bogusParams.data = reinterpret_cast<unsigned char*>(&bogusAeadParams);
// Skip when AAD was empty and can't be modified.
// Alternatively we could generate random aad.
if (aad_len != 0) {
decrypted_len = 0;
SECItem bogus_params(params);
CK_NSS_AEAD_PARAMS bogus_aead_params(aead_params);
bogus_params.data = reinterpret_cast<unsigned char*>(&bogus_aead_params);
std::vector<uint8_t> bogusAAD(aad, aad + aad_len);
bogusAeadParams.pAAD = toUcharPtr(&bogusAAD[0]);
bogusAAD[0] ^= 0xff;
std::vector<uint8_t> bogus_aad(aad, aad + aad_len);
bogus_aead_params.pAAD = toUcharPtr(bogus_aad.data());
bogus_aad[0] ^= 0xff;
rv = PK11_Decrypt(symKey, mech, &bogusParams, &decrypted[0],
&decryptedLen, data_len, &output[0], outputLen);
EXPECT_NE(rv, SECSuccess);
rv = PK11_Decrypt(key.get(), kMech, &bogus_params, decrypted.data(),
&decrypted_len, data_len, encrypted.data(),
encrypted.size());
EXPECT_EQ(rv, SECFailure);
EXPECT_EQ(0U, decrypted_len);
}
}
void EncryptDecrypt(const uint8_t* key, size_t key_len, const uint8_t* data,
size_t data_len, const uint8_t* aad, size_t aad_len,
const uint8_t* iv, size_t iv_len, const uint8_t* ct,
size_t ct_len) {
void EncryptDecrypt(const chaChaTestVector testvector) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
SECItem keyItem = {siBuffer, toUcharPtr(key),
static_cast<unsigned int>(key_len)};
SECItem keyItem = {siBuffer, toUcharPtr(testvector.Key.data()),
static_cast<unsigned int>(testvector.Key.size())};
// Import key.
ScopedPK11SymKey symKey(PK11_ImportSymKey(
slot.get(), mech, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr));
EXPECT_TRUE(!!symKey);
ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), kMech, PK11_OriginUnwrap,
CKA_ENCRYPT, &keyItem, nullptr));
EXPECT_TRUE(!!key);
// Check.
EncryptDecrypt(symKey.get(), data, data_len, aad, aad_len, iv, iv_len, ct,
ct_len);
EncryptDecrypt(key, testvector.invalidIV, testvector.invalidTag,
testvector.Data.data(), testvector.Data.size(),
testvector.AAD.data(), testvector.AAD.size(),
testvector.IV.data(), testvector.IV.size(),
testvector.CT.data(), testvector.CT.size());
}
protected:
CK_MECHANISM_TYPE mech = CKM_NSS_CHACHA20_POLY1305;
};
#define ENCRYPT_DECRYPT(v) \
EncryptDecrypt(v##Key, sizeof(v##Key), v##Data, sizeof(v##Data), v##AAD, \
sizeof(v##AAD), v##IV, sizeof(v##IV), v##CT, sizeof(v##CT));
TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateEncryptDecrypt) {
// Generate a random key.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey symKey(PK11_KeyGen(slot.get(), mech, nullptr, 32, nullptr));
EXPECT_TRUE(!!symKey);
ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr));
EXPECT_TRUE(!!key);
// Generate random data.
std::vector<uint8_t> data(512);
SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), &data[0], data.size());
std::vector<uint8_t> input(512);
SECStatus rv =
PK11_GenerateRandomOnSlot(slot.get(), input.data(), input.size());
EXPECT_EQ(rv, SECSuccess);
// Generate random AAD.
std::vector<uint8_t> aad(16);
rv = PK11_GenerateRandomOnSlot(slot.get(), &aad[0], aad.size());
rv = PK11_GenerateRandomOnSlot(slot.get(), aad.data(), aad.size());
EXPECT_EQ(rv, SECSuccess);
// Generate random IV.
std::vector<uint8_t> iv(12);
rv = PK11_GenerateRandomOnSlot(slot.get(), &iv[0], iv.size());
rv = PK11_GenerateRandomOnSlot(slot.get(), iv.data(), iv.size());
EXPECT_EQ(rv, SECSuccess);
// Check.
EncryptDecrypt(symKey.get(), &data[0], data.size(), &aad[0], aad.size(),
&iv[0], iv.size());
EncryptDecrypt(key, false, false, input.data(), input.size(), aad.data(),
aad.size(), iv.data(), iv.size());
}
TEST_F(Pkcs11ChaCha20Poly1305Test, CheckTestVector1) {
ENCRYPT_DECRYPT(kTestVector1);
TEST_F(Pkcs11ChaCha20Poly1305Test, Xor) {
static const uint8_t kExpected[sizeof(kData)] = {
0xd8, 0x15, 0xd3, 0xb3, 0xe9, 0x34, 0x3b, 0x7a,
0x24, 0xf6, 0x5f, 0xd7, 0x95, 0x3d, 0xd3, 0x51};
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
SECItem keyItem = {siBuffer, toUcharPtr(kKeyData),
static_cast<unsigned int>(sizeof(kKeyData))};
ScopedPK11SymKey key(PK11_ImportSymKey(
slot.get(), kMechXor, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr));
EXPECT_TRUE(!!key);
SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce),
static_cast<unsigned int>(sizeof(kCtrNonce))};
uint8_t encrypted[sizeof(kData)];
unsigned int encrypted_len = 88; // This should be overwritten.
SECStatus rv =
PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
&encrypted_len, sizeof(encrypted), kData, sizeof(kData));
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(sizeof(kExpected), static_cast<size_t>(encrypted_len));
EXPECT_EQ(0, memcmp(kExpected, encrypted, sizeof(kExpected)));
// Decrypting has the same effect.
rv = PK11_Decrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
&encrypted_len, sizeof(encrypted), kData, sizeof(kData));
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len));
EXPECT_EQ(0, memcmp(kExpected, encrypted, sizeof(kExpected)));
// Operating in reverse too.
rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
&encrypted_len, sizeof(encrypted), kExpected,
sizeof(kExpected));
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(sizeof(kExpected), static_cast<size_t>(encrypted_len));
EXPECT_EQ(0, memcmp(kData, encrypted, sizeof(kData)));
}
TEST_F(Pkcs11ChaCha20Poly1305Test, CheckTestVector2) {
ENCRYPT_DECRYPT(kTestVector2);
// This test just ensures that a key can be generated for use with the XOR
// function. The result is random and therefore cannot be checked.
TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateXor) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr));
EXPECT_TRUE(!!key);
SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce),
static_cast<unsigned int>(sizeof(kCtrNonce))};
uint8_t encrypted[sizeof(kData)];
unsigned int encrypted_len = 88; // This should be overwritten.
SECStatus rv =
PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
&encrypted_len, sizeof(encrypted), kData, sizeof(kData));
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len));
}
TEST_F(Pkcs11ChaCha20Poly1305Test, XorInvalidParams) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr));
EXPECT_TRUE(!!key);
SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce),
static_cast<unsigned int>(sizeof(kCtrNonce)) - 1};
uint8_t encrypted[sizeof(kData)];
unsigned int encrypted_len = 88;
SECStatus rv =
PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
&encrypted_len, sizeof(encrypted), kData, sizeof(kData));
EXPECT_EQ(SECFailure, rv);
ctrNonceItem.data = nullptr;
rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
&encrypted_len, sizeof(encrypted), kData, sizeof(kData));
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
}
TEST_P(Pkcs11ChaCha20Poly1305Test, TestVectors) { EncryptDecrypt(GetParam()); }
INSTANTIATE_TEST_CASE_P(NSSTestVector, Pkcs11ChaCha20Poly1305Test,
::testing::ValuesIn(kChaCha20Vectors));
INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11ChaCha20Poly1305Test,
::testing::ValuesIn(kChaCha20WycheproofVectors));
} // namespace nss_test

View file

@ -5,111 +5,122 @@
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "prerror.h"
#include "cpputil.h"
#include "nss_scoped_ptrs.h"
#include "testvectors/curve25519-vectors.h"
#include "gtest/gtest.h"
namespace nss_test {
// <https://tools.ietf.org/html/rfc7748#section-6.1>
const uint8_t kPkcs8[] = {
0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda,
0x47, 0x0f, 0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20,
0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72,
0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a,
0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a, 0xa1, 0x23, 0x03, 0x21,
0x00, 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d,
0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a,
0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a};
const uint8_t kSpki[] = {
0x30, 0x39, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01,
0x03, 0x21, 0x00, 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3,
0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b,
0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f};
const uint8_t kSecret[] = {0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1,
0x72, 0x8e, 0x3b, 0xf4, 0x80, 0x35, 0x0f, 0x25,
0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, 0x9e, 0x33,
0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42};
// A public key that's too short (31 bytes).
const uint8_t kSpkiShort[] = {
0x30, 0x38, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01,
0x03, 0x20, 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b,
0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78,
0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f};
// A public key that's too long (33 bytes).
const uint8_t kSpkiLong[] = {
0x30, 0x3a, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01,
0x03, 0x22, 0x00, 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3,
0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b,
0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f, 0x34};
class Pkcs11Curve25519Test : public ::testing::Test {
class Pkcs11Curve25519Test
: public ::testing::TestWithParam<curve25519_testvector> {
protected:
void Derive(const uint8_t* pkcs8, size_t pkcs8_len, const uint8_t* spki,
size_t spki_len, const uint8_t* secret, size_t secret_len,
bool expect_success) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8),
static_cast<unsigned int>(pkcs8_len)};
SECItem pkcs8_item = {siBuffer, toUcharPtr(pkcs8),
static_cast<unsigned int>(pkcs8_len)};
SECKEYPrivateKey* key = nullptr;
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL, &key,
slot.get(), &pkcs8_item, nullptr, nullptr, false, false, KU_ALL, &key,
nullptr);
EXPECT_EQ(SECSuccess, rv);
ScopedSECKEYPrivateKey privKey(key);
ASSERT_TRUE(privKey);
ScopedSECKEYPrivateKey priv_key_sess(key);
ASSERT_TRUE(priv_key_sess);
SECItem spkiItem = {siBuffer, toUcharPtr(spki),
static_cast<unsigned int>(spki_len)};
SECItem spki_item = {siBuffer, toUcharPtr(spki),
static_cast<unsigned int>(spki_len)};
ScopedCERTSubjectPublicKeyInfo certSpki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
ASSERT_TRUE(certSpki);
ScopedCERTSubjectPublicKeyInfo cert_spki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
if (!expect_success && !cert_spki) {
return;
}
ASSERT_TRUE(cert_spki);
ScopedSECKEYPublicKey pubKey(SECKEY_ExtractPublicKey(certSpki.get()));
ASSERT_TRUE(pubKey);
ScopedSECKEYPublicKey pub_key_remote(
SECKEY_ExtractPublicKey(cert_spki.get()));
ASSERT_TRUE(pub_key_remote);
ScopedPK11SymKey symKey(PK11_PubDeriveWithKDF(
privKey.get(), pubKey.get(), false, nullptr, nullptr, CKM_ECDH1_DERIVE,
CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr));
EXPECT_EQ(expect_success, !!symKey);
// sym_key_sess = ECDH(session_import(private_test), public_test)
ScopedPK11SymKey sym_key_sess(PK11_PubDeriveWithKDF(
priv_key_sess.get(), pub_key_remote.get(), false, nullptr, nullptr,
CKM_ECDH1_DERIVE, CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr,
nullptr));
ASSERT_EQ(expect_success, !!sym_key_sess);
if (expect_success) {
rv = PK11_ExtractKeyValue(symKey.get());
rv = PK11_ExtractKeyValue(sym_key_sess.get());
EXPECT_EQ(SECSuccess, rv);
SECItem* keyData = PK11_GetKeyData(symKey.get());
EXPECT_EQ(secret_len, keyData->len);
EXPECT_EQ(memcmp(keyData->data, secret, secret_len), 0);
SECItem* key_data = PK11_GetKeyData(sym_key_sess.get());
EXPECT_EQ(secret_len, key_data->len);
EXPECT_EQ(memcmp(key_data->data, secret, secret_len), 0);
// Perform wrapped export on the imported private, import it as
// permanent, and verify we derive the same shared secret
static const uint8_t pw[] = "pw";
SECItem pwItem = {siBuffer, toUcharPtr(pw), sizeof(pw)};
ScopedSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo(
slot.get(), SEC_OID_AES_256_CBC, &pwItem, priv_key_sess.get(), 1,
nullptr));
ASSERT_NE(nullptr, epki) << "PK11_ExportEncryptedPrivKeyInfo failed: "
<< PORT_ErrorToName(PORT_GetError());
ScopedSECKEYPublicKey pub_key_local(
SECKEY_ConvertToPublicKey(priv_key_sess.get()));
SECKEYPrivateKey* priv_key_tok = nullptr;
rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
slot.get(), epki.get(), &pwItem, nullptr,
&pub_key_local->u.ec.publicValue, PR_TRUE, PR_TRUE, ecKey, 0,
&priv_key_tok, nullptr);
ASSERT_EQ(SECSuccess, rv) << "PK11_ImportEncryptedPrivateKeyInfo failed "
<< PORT_ErrorToName(PORT_GetError());
ASSERT_TRUE(priv_key_tok);
// sym_key_tok = ECDH(token_import(export(private_test)),
// public_test)
ScopedPK11SymKey sym_key_tok(PK11_PubDeriveWithKDF(
priv_key_tok, pub_key_remote.get(), false, nullptr, nullptr,
CKM_ECDH1_DERIVE, CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr,
nullptr));
EXPECT_TRUE(sym_key_tok);
if (sym_key_tok) {
rv = PK11_ExtractKeyValue(sym_key_tok.get());
EXPECT_EQ(SECSuccess, rv);
key_data = PK11_GetKeyData(sym_key_tok.get());
EXPECT_EQ(secret_len, key_data->len);
EXPECT_EQ(memcmp(key_data->data, secret, secret_len), 0);
}
rv = PK11_DeleteTokenPrivateKey(priv_key_tok, true);
EXPECT_EQ(SECSuccess, rv);
}
}
};
void Derive(const curve25519_testvector testvector) {
Derive(testvector.private_key.data(), testvector.private_key.size(),
testvector.public_key.data(), testvector.public_key.size(),
testvector.secret.data(), testvector.secret.size(),
testvector.valid);
};
};
TEST_F(Pkcs11Curve25519Test, DeriveSharedSecret) {
Derive(kPkcs8, sizeof(kPkcs8), kSpki, sizeof(kSpki), kSecret, sizeof(kSecret),
true);
}
TEST_P(Pkcs11Curve25519Test, TestVectors) { Derive(GetParam()); }
TEST_F(Pkcs11Curve25519Test, DeriveSharedSecretShort) {
Derive(kPkcs8, sizeof(kPkcs8), kSpkiShort, sizeof(kSpkiShort), nullptr, 0,
false);
}
INSTANTIATE_TEST_CASE_P(NSSTestVector, Pkcs11Curve25519Test,
::testing::ValuesIn(kCurve25519Vectors));
TEST_F(Pkcs11Curve25519Test, DeriveSharedSecretLong) {
Derive(kPkcs8, sizeof(kPkcs8), kSpkiLong, sizeof(kSpkiLong), nullptr, 0,
false);
}
INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11Curve25519Test,
::testing::ValuesIn(kCurve25519WycheproofVectors));
} // namespace nss_test

View file

@ -15,6 +15,20 @@
namespace nss_test {
const std::vector<uint8_t> kValidP256Key = {
0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57,
0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12,
0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21, 0xa1, 0x44, 0x03, 0x42,
0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61,
0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61,
0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79, 0x03,
0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28,
0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3,
0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99};
const std::vector<uint8_t> kValidRSAKey = {
// 512-bit RSA private key (PKCS#8)
0x30, 0x82, 0x01, 0x54, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,
@ -73,38 +87,76 @@ const std::vector<uint8_t> kInvalidZeroLengthKey = {
class DERPrivateKeyImportTest : public ::testing::Test {
public:
bool ParsePrivateKey(const std::vector<uint8_t>& data) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
EXPECT_TRUE(slot);
bool ParsePrivateKey(const std::vector<uint8_t>& data, bool expect_success) {
SECKEYPrivateKey* key = nullptr;
SECStatus rv = SECFailure;
std::string nick_str =
::testing::UnitTest::GetInstance()->current_test_info()->name() +
std::to_string(rand());
SECItem item = {siBuffer, const_cast<unsigned char*>(data.data()),
(unsigned int)data.size()};
static_cast<unsigned int>(data.size())};
SECItem nick = {siBuffer, reinterpret_cast<unsigned char*>(
const_cast<char*>(nick_str.data())),
static_cast<unsigned int>(nick_str.length())};
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &item, nullptr, nullptr, false, false, KU_ALL, &key,
nullptr);
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
EXPECT_TRUE(slot);
if (!slot) {
return false;
}
if (PK11_NeedUserInit(slot.get())) {
if (PK11_InitPin(slot.get(), nullptr, nullptr) != SECSuccess) {
EXPECT_EQ(rv, SECSuccess) << "PK11_InitPin failed";
}
}
rv = PK11_Authenticate(slot.get(), PR_TRUE, nullptr);
EXPECT_EQ(rv, SECSuccess);
rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &item, &nick, nullptr, true, false, KU_ALL, &key, nullptr);
EXPECT_EQ(rv == SECSuccess, key != nullptr);
SECKEY_DestroyPrivateKey(key);
if (expect_success) {
// Try to find the key via its label
ScopedSECKEYPrivateKeyList list(PK11_ListPrivKeysInSlot(
slot.get(), const_cast<char*>(nick_str.c_str()), nullptr));
EXPECT_FALSE(!list);
}
if (key) {
rv = PK11_DeleteTokenPrivateKey(key, true);
EXPECT_EQ(SECSuccess, rv);
// PK11_DeleteTokenPrivateKey leaves an errorCode set when there's
// no cert. This is expected, so clear it.
if (PORT_GetError() == SSL_ERROR_NO_CERTIFICATE) {
PORT_SetError(0);
}
}
return rv == SECSuccess;
}
};
TEST_F(DERPrivateKeyImportTest, ImportPrivateRSAKey) {
EXPECT_TRUE(ParsePrivateKey(kValidRSAKey));
EXPECT_FALSE(PORT_GetError());
EXPECT_TRUE(ParsePrivateKey(kValidRSAKey, true));
EXPECT_FALSE(PORT_GetError()) << PORT_GetError();
}
TEST_F(DERPrivateKeyImportTest, ImportEcdsaKey) {
EXPECT_TRUE(ParsePrivateKey(kValidP256Key, true));
EXPECT_FALSE(PORT_GetError()) << PORT_GetError();
}
TEST_F(DERPrivateKeyImportTest, ImportInvalidPrivateKey) {
EXPECT_FALSE(ParsePrivateKey(kInvalidLengthKey));
EXPECT_EQ(PORT_GetError(), SEC_ERROR_BAD_DER);
EXPECT_FALSE(ParsePrivateKey(kInvalidLengthKey, false));
EXPECT_EQ(PORT_GetError(), SEC_ERROR_BAD_DER) << PORT_GetError();
}
TEST_F(DERPrivateKeyImportTest, ImportZeroLengthPrivateKey) {
EXPECT_FALSE(ParsePrivateKey(kInvalidZeroLengthKey));
EXPECT_EQ(PORT_GetError(), SEC_ERROR_BAD_KEY);
EXPECT_FALSE(ParsePrivateKey(kInvalidZeroLengthKey, false));
EXPECT_EQ(PORT_GetError(), SEC_ERROR_BAD_KEY) << PORT_GetError();
}
} // namespace nss_test

View file

@ -0,0 +1,65 @@
/* -*- 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 "nss.h"
#include "pk11pub.h"
#include "nss_scoped_ptrs.h"
#include "gtest/gtest.h"
namespace nss_test {
class Pkcs11DesTest : public ::testing::Test {
protected:
SECStatus EncryptWithIV(std::vector<uint8_t>& iv,
const CK_MECHANISM_TYPE mech) {
// Generate a random key.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey sym_key(
PK11_KeyGen(slot.get(), mech, nullptr, 8, nullptr));
EXPECT_TRUE(!!sym_key);
std::vector<uint8_t> data(16);
std::vector<uint8_t> output(16);
SECItem params = {siBuffer, iv.data(),
static_cast<unsigned int>(iv.size())};
// Try to encrypt.
unsigned int output_len = 0;
return PK11_Encrypt(sym_key.get(), mech, &params, output.data(),
&output_len, output.size(), data.data(), data.size());
}
};
TEST_F(Pkcs11DesTest, ZeroLengthIV) {
std::vector<uint8_t> iv(0);
EXPECT_EQ(SECFailure, EncryptWithIV(iv, CKM_DES_CBC));
EXPECT_EQ(SECFailure, EncryptWithIV(iv, CKM_DES3_CBC));
}
TEST_F(Pkcs11DesTest, IVTooShort) {
std::vector<uint8_t> iv(7);
EXPECT_EQ(SECFailure, EncryptWithIV(iv, CKM_DES_CBC));
EXPECT_EQ(SECFailure, EncryptWithIV(iv, CKM_DES3_CBC));
}
TEST_F(Pkcs11DesTest, WrongLengthIV) {
// We tolerate IVs > 8
std::vector<uint8_t> iv(15, 0);
EXPECT_EQ(SECSuccess, EncryptWithIV(iv, CKM_DES_CBC));
EXPECT_EQ(SECSuccess, EncryptWithIV(iv, CKM_DES3_CBC));
}
TEST_F(Pkcs11DesTest, AllGood) {
std::vector<uint8_t> iv(8, 0);
EXPECT_EQ(SECSuccess, EncryptWithIV(iv, CKM_DES_CBC));
EXPECT_EQ(SECSuccess, EncryptWithIV(iv, CKM_DES3_CBC));
}
} // namespace nss_test

View file

@ -45,6 +45,11 @@ static const Pkcs11EcdsaTestParams kEcdsaVectors[] = {
DataBuffer(kP256Spki, sizeof(kP256Spki)),
DataBuffer(kP256Data, sizeof(kP256Data)),
DataBuffer(kP256Signature, sizeof(kP256Signature))}},
{SEC_OID_SHA256,
{DataBuffer(kP256Pkcs8ZeroPad, sizeof(kP256Pkcs8ZeroPad)),
DataBuffer(kP256SpkiZeroPad, sizeof(kP256SpkiZeroPad)),
DataBuffer(kP256DataZeroPad, sizeof(kP256DataZeroPad)),
DataBuffer(kP256SignatureZeroPad, sizeof(kP256SignatureZeroPad))}},
{SEC_OID_SHA384,
{DataBuffer(kP384Pkcs8, sizeof(kP384Pkcs8)),
DataBuffer(kP384Spki, sizeof(kP384Spki)),

View file

@ -130,6 +130,38 @@ const uint8_t kP521Signature[] = {
0xd8, 0xb8, 0xc3, 0x7f, 0xf0, 0x77, 0x7b, 0x1a, 0x20, 0xf8, 0xcc, 0xb1,
0xdc, 0xcc, 0x43, 0x99, 0x7f, 0x1e, 0xe0, 0xe4, 0x4d, 0xa4, 0xa6, 0x7a};
// ECDSA P256 test case with a leading zero in the private key
const uint8_t kP256Pkcs8ZeroPad[] = {
0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
0x00, 0x16, 0x40, 0x71, 0x99, 0xe3, 0x07, 0xaa, 0xdc, 0x98, 0x0b, 0x21,
0x62, 0xce, 0x66, 0x1f, 0xe4, 0x1a, 0x86, 0x9a, 0x23, 0x33, 0xf6, 0x72,
0xb4, 0xa3, 0xdc, 0x3b, 0x50, 0xba, 0x20, 0xce, 0xa1, 0x44, 0x03, 0x42,
0x00, 0x04, 0x53, 0x11, 0x9a, 0x86, 0xa0, 0xc2, 0x99, 0x4f, 0xa6, 0xf8,
0x08, 0xf8, 0x61, 0x01, 0x0e, 0x6b, 0x04, 0x9c, 0xd8, 0x15, 0x63, 0x2e,
0xd1, 0x38, 0x00, 0x10, 0xee, 0xe4, 0xc9, 0x11, 0xff, 0x05, 0xba, 0xd6,
0xcd, 0x94, 0xea, 0x00, 0xec, 0x85, 0x26, 0x2c, 0xbd, 0x4d, 0x85, 0xbd,
0x20, 0xce, 0xa5, 0xb1, 0x3f, 0x4d, 0x82, 0x9b, 0x9f, 0x28, 0x2e, 0xd3,
0x8a, 0x87, 0x1f, 0x89, 0xf8, 0x02};
const uint8_t kP256SpkiZeroPad[] = {
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
0x42, 0x00, 0x04, 0x53, 0x11, 0x9a, 0x86, 0xa0, 0xc2, 0x99, 0x4f, 0xa6,
0xf8, 0x08, 0xf8, 0x61, 0x01, 0x0e, 0x6b, 0x04, 0x9c, 0xd8, 0x15, 0x63,
0x2e, 0xd1, 0x38, 0x00, 0x10, 0xee, 0xe4, 0xc9, 0x11, 0xff, 0x05, 0xba,
0xd6, 0xcd, 0x94, 0xea, 0x00, 0xec, 0x85, 0x26, 0x2c, 0xbd, 0x4d, 0x85,
0xbd, 0x20, 0xce, 0xa5, 0xb1, 0x3f, 0x4d, 0x82, 0x9b, 0x9f, 0x28, 0x2e,
0xd3, 0x8a, 0x87, 0x1f, 0x89, 0xf8, 0x02};
const uint8_t kP256DataZeroPad[] = {'s', 'a', 'm', 'p', 'l', 'e'};
const uint8_t kP256SignatureZeroPad[] = {
0xa6, 0xf4, 0xe4, 0xa8, 0x3f, 0x03, 0x59, 0x89, 0x60, 0x53, 0xe7,
0xdc, 0xb5, 0xbe, 0x78, 0xaf, 0xc1, 0xca, 0xc0, 0x65, 0xba, 0xa4,
0x3c, 0xf1, 0xe4, 0xae, 0xe3, 0xba, 0x22, 0x3d, 0xac, 0x9d, 0x6d,
0x1b, 0x26, 0x00, 0xcf, 0x47, 0xa1, 0xe1, 0x04, 0x21, 0x8d, 0x0b,
0xbb, 0x16, 0xfa, 0x3e, 0x59, 0x32, 0x01, 0xb0, 0x45, 0x3e, 0x27,
0xa4, 0xc4, 0xfd, 0x31, 0xc9, 0x1a, 0x8e, 0x74, 0xd8};
// ECDSA test vectors, SPKI and PKCS#8 edge cases.
const uint8_t kP256Pkcs8NoCurveOIDOrAlgorithmParams[] = {
0x30, 0x7d, 0x02, 0x01, 0x00, 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48,

View file

@ -0,0 +1,547 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=4 et sw=4 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.h>
#include "nss.h"
#include "pk11pub.h"
#include "prenv.h"
#include "prerror.h"
#include "secmod.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "util.h"
namespace nss_test {
// These test certificates were generated using pycert/pykey from
// mozilla-central (https://hg.mozilla.org/mozilla-central/file/ ...
// 9968319230a74eb8c1953444a0e6973c7500a9f8/security/manager/ssl/ ...
// tests/unit/pycert.py).
// issuer:test cert
// subject:test cert
// issuerKey:secp256r1
// subjectKey:secp256r1
// serialNumber:1
const std::vector<uint8_t> kTestCert1DER = {
0x30, 0x82, 0x01, 0x1D, 0x30, 0x81, 0xC2, 0xA0, 0x03, 0x02, 0x01, 0x02,
0x02, 0x01, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10,
0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20,
0x63, 0x65, 0x72, 0x74, 0x30, 0x22, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x37,
0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18,
0x0F, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x5A, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03,
0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, 0x63, 0x65,
0x72, 0x74, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE,
0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01,
0x07, 0x03, 0x42, 0x00, 0x04, 0x4F, 0xBF, 0xBB, 0xBB, 0x61, 0xE0, 0xF8,
0xF9, 0xB1, 0xA6, 0x0A, 0x59, 0xAC, 0x87, 0x04, 0xE2, 0xEC, 0x05, 0x0B,
0x42, 0x3E, 0x3C, 0xF7, 0x2E, 0x92, 0x3F, 0x2C, 0x4F, 0x79, 0x4B, 0x45,
0x5C, 0x2A, 0x69, 0xD2, 0x33, 0x45, 0x6C, 0x36, 0xC4, 0x11, 0x9D, 0x07,
0x06, 0xE0, 0x0E, 0xED, 0xC8, 0xD1, 0x93, 0x90, 0xD7, 0x99, 0x1B, 0x7B,
0x2D, 0x07, 0xA3, 0x04, 0xEA, 0xA0, 0x4A, 0xA6, 0xC0, 0x30, 0x0D, 0x06,
0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x5C, 0x75, 0x51, 0x9F, 0x13,
0x11, 0x50, 0xCD, 0x5D, 0x8A, 0xDE, 0x20, 0xA3, 0xBC, 0x06, 0x30, 0x91,
0xFF, 0xB2, 0x73, 0x75, 0x5F, 0x31, 0x64, 0xEC, 0xFD, 0xCB, 0x42, 0x80,
0x0A, 0x70, 0xE6, 0x02, 0x20, 0x11, 0xFA, 0xA2, 0xCA, 0x06, 0xF3, 0xBC,
0x5F, 0x8A, 0xCA, 0x17, 0x63, 0x36, 0x87, 0xCF, 0x8D, 0x5C, 0xA0, 0x56,
0x84, 0x44, 0x61, 0xB2, 0x33, 0x42, 0x07, 0x58, 0x9F, 0x0C, 0x9E, 0x49,
0x83,
};
// issuer:test cert
// subject:test cert
// issuerKey:secp256r1
// subjectKey:secp256r1
// serialNumber:2
const std::vector<uint8_t> kTestCert2DER = {
0x30, 0x82, 0x01, 0x1E, 0x30, 0x81, 0xC2, 0xA0, 0x03, 0x02, 0x01, 0x02,
0x02, 0x01, 0x02, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10,
0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20,
0x63, 0x65, 0x72, 0x74, 0x30, 0x22, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x37,
0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18,
0x0F, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x5A, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03,
0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, 0x63, 0x65,
0x72, 0x74, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE,
0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01,
0x07, 0x03, 0x42, 0x00, 0x04, 0x4F, 0xBF, 0xBB, 0xBB, 0x61, 0xE0, 0xF8,
0xF9, 0xB1, 0xA6, 0x0A, 0x59, 0xAC, 0x87, 0x04, 0xE2, 0xEC, 0x05, 0x0B,
0x42, 0x3E, 0x3C, 0xF7, 0x2E, 0x92, 0x3F, 0x2C, 0x4F, 0x79, 0x4B, 0x45,
0x5C, 0x2A, 0x69, 0xD2, 0x33, 0x45, 0x6C, 0x36, 0xC4, 0x11, 0x9D, 0x07,
0x06, 0xE0, 0x0E, 0xED, 0xC8, 0xD1, 0x93, 0x90, 0xD7, 0x99, 0x1B, 0x7B,
0x2D, 0x07, 0xA3, 0x04, 0xEA, 0xA0, 0x4A, 0xA6, 0xC0, 0x30, 0x0D, 0x06,
0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x5C, 0x75, 0x51, 0x9F, 0x13,
0x11, 0x50, 0xCD, 0x5D, 0x8A, 0xDE, 0x20, 0xA3, 0xBC, 0x06, 0x30, 0x91,
0xFF, 0xB2, 0x73, 0x75, 0x5F, 0x31, 0x64, 0xEC, 0xFD, 0xCB, 0x42, 0x80,
0x0A, 0x70, 0xE6, 0x02, 0x21, 0x00, 0xF6, 0x5E, 0x42, 0xC7, 0x54, 0x40,
0x81, 0xE9, 0x4C, 0x16, 0x48, 0xB1, 0x39, 0x0A, 0xA0, 0xE2, 0x8C, 0x23,
0xAA, 0xC5, 0xBB, 0xAC, 0xEB, 0x9B, 0x15, 0x0B, 0x2F, 0xB7, 0xF5, 0x85,
0xB2, 0x54,
};
const std::vector<uint8_t> kTestCertSubjectDER = {
0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03,
0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, 0x63, 0x65, 0x72, 0x74,
};
// issuer:test cert
// subject:unrelated subject DN
// issuerKey:secp256r1
// subjectKey:secp256r1
// serialNumber:3
const std::vector<uint8_t> kUnrelatedTestCertDER = {
0x30, 0x82, 0x01, 0x28, 0x30, 0x81, 0xCD, 0xA0, 0x03, 0x02, 0x01, 0x02,
0x02, 0x01, 0x03, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10,
0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20,
0x63, 0x65, 0x72, 0x74, 0x30, 0x22, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x37,
0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18,
0x0F, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x5A, 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03,
0x55, 0x04, 0x03, 0x0C, 0x14, 0x75, 0x6E, 0x72, 0x65, 0x6C, 0x61, 0x74,
0x65, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x44,
0x4E, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D,
0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
0x03, 0x42, 0x00, 0x04, 0x4F, 0xBF, 0xBB, 0xBB, 0x61, 0xE0, 0xF8, 0xF9,
0xB1, 0xA6, 0x0A, 0x59, 0xAC, 0x87, 0x04, 0xE2, 0xEC, 0x05, 0x0B, 0x42,
0x3E, 0x3C, 0xF7, 0x2E, 0x92, 0x3F, 0x2C, 0x4F, 0x79, 0x4B, 0x45, 0x5C,
0x2A, 0x69, 0xD2, 0x33, 0x45, 0x6C, 0x36, 0xC4, 0x11, 0x9D, 0x07, 0x06,
0xE0, 0x0E, 0xED, 0xC8, 0xD1, 0x93, 0x90, 0xD7, 0x99, 0x1B, 0x7B, 0x2D,
0x07, 0xA3, 0x04, 0xEA, 0xA0, 0x4A, 0xA6, 0xC0, 0x30, 0x0D, 0x06, 0x09,
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03,
0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x5C, 0x75, 0x51, 0x9F, 0x13, 0x11,
0x50, 0xCD, 0x5D, 0x8A, 0xDE, 0x20, 0xA3, 0xBC, 0x06, 0x30, 0x91, 0xFF,
0xB2, 0x73, 0x75, 0x5F, 0x31, 0x64, 0xEC, 0xFD, 0xCB, 0x42, 0x80, 0x0A,
0x70, 0xE6, 0x02, 0x20, 0x0F, 0x1A, 0x04, 0xC2, 0xF8, 0xBA, 0xC2, 0x94,
0x26, 0x6E, 0xBC, 0x91, 0x7D, 0xDB, 0x75, 0x7B, 0xE8, 0xA3, 0x4F, 0x69,
0x1B, 0xF3, 0x1F, 0x2C, 0xCE, 0x82, 0x67, 0xC9, 0x5B, 0xBB, 0xBA, 0x0A,
};
class PK11FindCertsTestBase : public ::testing::Test {
protected:
PK11FindCertsTestBase()
: m_slot(nullptr), test_cert_db_dir_("PK11FindCertsTestBase-") {}
virtual void SetUp() {
std::string test_cert_db_path(test_cert_db_dir_.GetPath());
const char* test_name =
::testing::UnitTest::GetInstance()->current_test_info()->name();
std::string mod_spec = "configDir='sql:";
mod_spec.append(test_cert_db_path);
mod_spec.append("' tokenDescription='");
mod_spec.append(test_name);
mod_spec.append("'");
m_slot = SECMOD_OpenUserDB(mod_spec.c_str());
ASSERT_NE(m_slot, nullptr);
}
virtual void TearDown() {
ASSERT_EQ(SECMOD_CloseUserDB(m_slot), SECSuccess);
PK11_FreeSlot(m_slot);
std::string test_cert_db_path(test_cert_db_dir_.GetPath());
ASSERT_EQ(0, unlink((test_cert_db_path + "/cert9.db").c_str()));
ASSERT_EQ(0, unlink((test_cert_db_path + "/key4.db").c_str()));
}
PK11SlotInfo* m_slot;
ScopedUniqueDirectory test_cert_db_dir_;
};
class PK11FindRawCertsBySubjectTest : public PK11FindCertsTestBase {};
// If we don't have any certificates, we shouldn't get any when we search for
// them.
TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsImportedNoCertsFound) {
SECItem subject_item = {
siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()),
(unsigned int)kTestCertSubjectDER.size()};
CERTCertificateList* certificates = nullptr;
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(certificates, nullptr);
}
// If we have one certificate but it has an unrelated subject DN, we shouldn't
// get it when we search.
TEST_F(PK11FindRawCertsBySubjectTest, TestOneCertImportedNoCertsFound) {
char cert_nickname[] = "Unrelated Cert";
SECItem cert_item = {siBuffer,
const_cast<unsigned char*>(kUnrelatedTestCertDER.data()),
(unsigned int)kUnrelatedTestCertDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false),
SECSuccess);
SECItem subject_item = {
siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()),
(unsigned int)kTestCertSubjectDER.size()};
CERTCertificateList* certificates = nullptr;
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(certificates, nullptr);
}
TEST_F(PK11FindRawCertsBySubjectTest, TestMultipleMatchingCertsFound) {
char cert1_nickname[] = "Test Cert 1";
SECItem cert1_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false),
SECSuccess);
char cert2_nickname[] = "Test Cert 2";
SECItem cert2_item = {siBuffer,
const_cast<unsigned char*>(kTestCert2DER.data()),
(unsigned int)kTestCert2DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE,
cert2_nickname, false),
SECSuccess);
char unrelated_cert_nickname[] = "Unrelated Test Cert";
SECItem unrelated_cert_item = {
siBuffer, const_cast<unsigned char*>(kUnrelatedTestCertDER.data()),
(unsigned int)kUnrelatedTestCertDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &unrelated_cert_item, CK_INVALID_HANDLE,
unrelated_cert_nickname, false),
SECSuccess);
CERTCertificateList* certificates = nullptr;
SECItem subject_item = {
siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()),
(unsigned int)kTestCertSubjectDER.size()};
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
ASSERT_NE(certificates, nullptr);
ScopedCERTCertificateList scoped_certificates(certificates);
ASSERT_EQ(scoped_certificates->len, 2);
std::vector<uint8_t> found_cert1(
scoped_certificates->certs[0].data,
scoped_certificates->certs[0].data + scoped_certificates->certs[0].len);
std::vector<uint8_t> found_cert2(
scoped_certificates->certs[1].data,
scoped_certificates->certs[1].data + scoped_certificates->certs[1].len);
EXPECT_TRUE(found_cert1 == kTestCert1DER || found_cert1 == kTestCert2DER);
EXPECT_TRUE(found_cert2 == kTestCert1DER || found_cert2 == kTestCert2DER);
EXPECT_TRUE(found_cert1 != found_cert2);
}
// If we try to search the internal slots, we won't find the certificate we just
// imported (because it's on a different slot).
TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsOnInternalSlots) {
char cert1_nickname[] = "Test Cert 1";
SECItem cert1_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false),
SECSuccess);
SECItem subject_item = {
siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()),
(unsigned int)kTestCertSubjectDER.size()};
CERTCertificateList* internal_key_slot_certificates = nullptr;
ScopedPK11SlotInfo internal_key_slot(PK11_GetInternalKeySlot());
SECStatus rv = PK11_FindRawCertsWithSubject(
internal_key_slot.get(), &subject_item, &internal_key_slot_certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(internal_key_slot_certificates, nullptr);
CERTCertificateList* internal_slot_certificates = nullptr;
ScopedPK11SlotInfo internal_slot(PK11_GetInternalSlot());
rv = PK11_FindRawCertsWithSubject(internal_slot.get(), &subject_item,
&internal_slot_certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(internal_slot_certificates, nullptr);
}
// issuer:test cert
// subject:(empty - this had to be done by hand as pycert doesn't support this)
// issuerKey:secp256r1
// subjectKey:secp256r1
// serialNumber:4
const std::vector<uint8_t> kEmptySubjectCertDER = {
0x30, 0x82, 0x01, 0x09, 0x30, 0x81, 0xAE, 0xA0, 0x03, 0x02, 0x01, 0x02,
0x02, 0x01, 0x04, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10,
0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20,
0x63, 0x65, 0x72, 0x74, 0x30, 0x22, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x37,
0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18,
0x0F, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x5A, 0x30, 0x00, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48,
0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x4F, 0xBF, 0xBB,
0xBB, 0x61, 0xE0, 0xF8, 0xF9, 0xB1, 0xA6, 0x0A, 0x59, 0xAC, 0x87, 0x04,
0xE2, 0xEC, 0x05, 0x0B, 0x42, 0x3E, 0x3C, 0xF7, 0x2E, 0x92, 0x3F, 0x2C,
0x4F, 0x79, 0x4B, 0x45, 0x5C, 0x2A, 0x69, 0xD2, 0x33, 0x45, 0x6C, 0x36,
0xC4, 0x11, 0x9D, 0x07, 0x06, 0xE0, 0x0E, 0xED, 0xC8, 0xD1, 0x93, 0x90,
0xD7, 0x99, 0x1B, 0x7B, 0x2D, 0x07, 0xA3, 0x04, 0xEA, 0xA0, 0x4A, 0xA6,
0xC0, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01,
0x01, 0x0B, 0x05, 0x00, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x5C,
0x75, 0x51, 0x9F, 0x13, 0x11, 0x50, 0xCD, 0x5D, 0x8A, 0xDE, 0x20, 0xA3,
0xBC, 0x06, 0x30, 0x91, 0xFF, 0xB2, 0x73, 0x75, 0x5F, 0x31, 0x64, 0xEC,
0xFD, 0xCB, 0x42, 0x80, 0x0A, 0x70, 0xE6, 0x02, 0x20, 0x31, 0x1B, 0x92,
0xAA, 0xA8, 0xB7, 0x51, 0x52, 0x7B, 0x64, 0xD6, 0xF7, 0x2F, 0x0C, 0xFB,
0xBB, 0xD5, 0xDF, 0x86, 0xA3, 0x97, 0x96, 0x60, 0x42, 0xDA, 0xD4, 0xA8,
0x5F, 0x2F, 0xA4, 0xDE, 0x7C};
std::vector<uint8_t> kEmptySubjectDER = {0x30, 0x00};
// This certificate has the smallest possible subject. Finding it should work.
TEST_F(PK11FindRawCertsBySubjectTest, TestFindEmptySubject) {
char empty_subject_cert_nickname[] = "Empty Subject Cert";
SECItem empty_subject_cert_item = {
siBuffer, const_cast<unsigned char*>(kEmptySubjectCertDER.data()),
(unsigned int)kEmptySubjectCertDER.size()};
ASSERT_EQ(
PK11_ImportDERCert(m_slot, &empty_subject_cert_item, CK_INVALID_HANDLE,
empty_subject_cert_nickname, false),
SECSuccess);
SECItem subject_item = {siBuffer,
const_cast<unsigned char*>(kEmptySubjectDER.data()),
(unsigned int)kEmptySubjectDER.size()};
CERTCertificateList* certificates = nullptr;
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
ASSERT_NE(certificates, nullptr);
ScopedCERTCertificateList scoped_certificates(certificates);
ASSERT_EQ(scoped_certificates->len, 1);
std::vector<uint8_t> found_cert(
scoped_certificates->certs[0].data,
scoped_certificates->certs[0].data + scoped_certificates->certs[0].len);
EXPECT_EQ(found_cert, kEmptySubjectCertDER);
}
// Searching for a zero-length subject doesn't make sense (the minimum subject
// is the SEQUENCE tag followed by a length byte of 0), but it shouldn't cause
// problems.
TEST_F(PK11FindRawCertsBySubjectTest, TestSearchForNullSubject) {
char cert1_nickname[] = "Test Cert 1";
SECItem cert1_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false),
SECSuccess);
SECItem subject_item = {siBuffer, nullptr, 0};
CERTCertificateList* certificates = nullptr;
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(certificates, nullptr);
}
class PK11GetCertsMatchingPrivateKeyTest : public PK11FindCertsTestBase {};
// This is the private secp256r1 key corresponding to the above test
// certificates.
const std::vector<uint8_t> kTestPrivateKeyInfoDER = {
0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
0x21, 0x91, 0x40, 0x3d, 0x57, 0x10, 0xbf, 0x15, 0xa2, 0x65, 0x81, 0x8c,
0xd4, 0x2e, 0xd6, 0xfe, 0xdf, 0x09, 0xad, 0xd9, 0x2d, 0x78, 0xb1, 0x8e,
0x7a, 0x1e, 0x9f, 0xeb, 0x95, 0x52, 0x47, 0x02, 0xa1, 0x44, 0x03, 0x42,
0x00, 0x04, 0x4f, 0xbf, 0xbb, 0xbb, 0x61, 0xe0, 0xf8, 0xf9, 0xb1, 0xa6,
0x0a, 0x59, 0xac, 0x87, 0x04, 0xe2, 0xec, 0x05, 0x0b, 0x42, 0x3e, 0x3c,
0xf7, 0x2e, 0x92, 0x3f, 0x2c, 0x4f, 0x79, 0x4b, 0x45, 0x5c, 0x2a, 0x69,
0xd2, 0x33, 0x45, 0x6c, 0x36, 0xc4, 0x11, 0x9d, 0x07, 0x06, 0xe0, 0x0e,
0xed, 0xc8, 0xd1, 0x93, 0x90, 0xd7, 0x99, 0x1b, 0x7b, 0x2d, 0x07, 0xa3,
0x04, 0xea, 0xa0, 0x4a, 0xa6, 0xc0,
};
// issuer:test cert (different key)
// subject:test cert (different key)
// issuerKey:secp256k1
// subjectKey:secp256k1
// serialNumber:1
const std::vector<uint8_t> kTestCertWithOtherKeyDER = {
0x30, 0x82, 0x01, 0x3a, 0x30, 0x81, 0xdf, 0xa0, 0x03, 0x02, 0x01, 0x02,
0x02, 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x24, 0x31, 0x22, 0x30, 0x20,
0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x74, 0x65, 0x73, 0x74, 0x20,
0x63, 0x65, 0x72, 0x74, 0x20, 0x28, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72,
0x65, 0x6e, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x30, 0x22, 0x18, 0x0f,
0x32, 0x30, 0x31, 0x37, 0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30,
0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x24, 0x31, 0x22,
0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x74, 0x65, 0x73,
0x74, 0x20, 0x63, 0x65, 0x72, 0x74, 0x20, 0x28, 0x64, 0x69, 0x66, 0x66,
0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x30, 0x56,
0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a, 0x03, 0x42, 0x00, 0x04, 0x35, 0xee,
0x7c, 0x72, 0x89, 0xd8, 0xfe, 0xf7, 0xa8, 0x6a, 0xfe, 0x5d, 0xa6, 0x6d,
0x8b, 0xc2, 0xeb, 0xb6, 0xa8, 0x54, 0x3f, 0xd2, 0xfe, 0xad, 0x08, 0x9f,
0x45, 0xce, 0x7a, 0xcd, 0x0f, 0xa6, 0x43, 0x82, 0xa9, 0x50, 0x0c, 0x41,
0xda, 0xd7, 0x70, 0xff, 0xd4, 0xb5, 0x11, 0xbf, 0x4b, 0x49, 0x2e, 0xb1,
0x23, 0x88, 0x00, 0xc3, 0x2c, 0x4f, 0x76, 0xc7, 0x3a, 0x3f, 0x32, 0x94,
0xe7, 0xc5, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20,
0x63, 0x59, 0x02, 0x01, 0x89, 0xd7, 0x3e, 0x5b, 0xff, 0xd1, 0x16, 0x4e,
0xe3, 0xe2, 0x0a, 0xe0, 0x4a, 0xd8, 0x75, 0xaf, 0x77, 0x5c, 0x93, 0x60,
0xba, 0x10, 0x1f, 0x97, 0xdd, 0x27, 0x2d, 0x24, 0x02, 0x20, 0x1e, 0xa0,
0x7b, 0xee, 0x90, 0x9b, 0x5f, 0x2c, 0x49, 0xd6, 0x61, 0xda, 0x31, 0x14,
0xb1, 0xa4, 0x0d, 0x2d, 0x90, 0x2b, 0x70, 0xd8, 0x6b, 0x07, 0x64, 0x27,
0xa5, 0x2e, 0xfe, 0xca, 0x6e, 0xe6,
};
// If there are no certs at all, we'll get back a null list.
TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestNoCertsAtAll) {
SECItem private_key_info = {
siBuffer, const_cast<unsigned char*>(kTestPrivateKeyInfoDER.data()),
(unsigned int)kTestPrivateKeyInfoDER.size(),
};
SECKEYPrivateKey* priv_key = nullptr;
ASSERT_EQ(PK11_ImportDERPrivateKeyInfoAndReturnKey(
m_slot, &private_key_info, nullptr, nullptr, false, false,
KU_ALL, &priv_key, nullptr),
SECSuccess);
ASSERT_NE(priv_key, nullptr);
ScopedSECKEYPrivateKey scoped_priv_key(priv_key);
ScopedCERTCertList certs(
PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get()));
ASSERT_TRUE(CERT_LIST_EMPTY(certs));
}
// If there are no certs for the private key, we'll get back a null list.
TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestNoCertsForKey) {
SECItem private_key_info = {
siBuffer, const_cast<unsigned char*>(kTestPrivateKeyInfoDER.data()),
(unsigned int)kTestPrivateKeyInfoDER.size(),
};
SECKEYPrivateKey* priv_key = nullptr;
ASSERT_EQ(PK11_ImportDERPrivateKeyInfoAndReturnKey(
m_slot, &private_key_info, nullptr, nullptr, false, false,
KU_ALL, &priv_key, nullptr),
SECSuccess);
ASSERT_NE(priv_key, nullptr);
ScopedSECKEYPrivateKey scoped_priv_key(priv_key);
char cert_nickname[] = "Test Cert With Other Key";
SECItem cert_item = {
siBuffer, const_cast<unsigned char*>(kTestCertWithOtherKeyDER.data()),
(unsigned int)kTestCertWithOtherKeyDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false),
SECSuccess);
ScopedCERTCertList certs(
PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get()));
ASSERT_TRUE(CERT_LIST_EMPTY(certs));
}
void CheckCertListForSubjects(
ScopedCERTCertList& list,
const std::vector<const char*>& expected_subjects) {
ASSERT_NE(list.get(), nullptr);
ASSERT_NE(expected_subjects.size(), 0ul);
for (const auto& expected_subject : expected_subjects) {
size_t list_length = 0;
bool found = false;
for (CERTCertListNode* n = CERT_LIST_HEAD(list); !CERT_LIST_END(n, list);
n = CERT_LIST_NEXT(n)) {
list_length++;
if (strcmp(n->cert->subjectName, expected_subject) == 0) {
ASSERT_FALSE(found);
found = true;
}
}
ASSERT_TRUE(found);
ASSERT_EQ(list_length, expected_subjects.size());
}
}
// We should only get back certs that actually match the private key.
TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestOneCertForKey) {
SECItem private_key_info = {
siBuffer, const_cast<unsigned char*>(kTestPrivateKeyInfoDER.data()),
(unsigned int)kTestPrivateKeyInfoDER.size(),
};
SECKEYPrivateKey* priv_key = nullptr;
ASSERT_EQ(PK11_ImportDERPrivateKeyInfoAndReturnKey(
m_slot, &private_key_info, nullptr, nullptr, false, false,
KU_ALL, &priv_key, nullptr),
SECSuccess);
ASSERT_NE(priv_key, nullptr);
ScopedSECKEYPrivateKey scoped_priv_key(priv_key);
char cert1_nickname[] = "Test Cert 1";
SECItem cert1_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false),
SECSuccess);
char cert_nickname[] = "Test Cert With Other Key";
SECItem cert_item = {
siBuffer, const_cast<unsigned char*>(kTestCertWithOtherKeyDER.data()),
(unsigned int)kTestCertWithOtherKeyDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false),
SECSuccess);
ScopedCERTCertList certs(
PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get()));
CheckCertListForSubjects(certs, {"CN=test cert"});
}
// We should be able to get back all certs that match the private key.
TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestTwoCertsForKey) {
SECItem private_key_info = {
siBuffer, const_cast<unsigned char*>(kTestPrivateKeyInfoDER.data()),
(unsigned int)kTestPrivateKeyInfoDER.size(),
};
SECKEYPrivateKey* priv_key = nullptr;
ASSERT_EQ(PK11_ImportDERPrivateKeyInfoAndReturnKey(
m_slot, &private_key_info, nullptr, nullptr, false, false,
KU_ALL, &priv_key, nullptr),
SECSuccess);
ASSERT_NE(priv_key, nullptr);
ScopedSECKEYPrivateKey scoped_priv_key(priv_key);
char cert1_nickname[] = "Test Cert 1";
SECItem cert1_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false),
SECSuccess);
char cert2_nickname[] = "Test Cert 2 (same key, different subject)";
SECItem cert2_item = {
siBuffer, const_cast<unsigned char*>(kUnrelatedTestCertDER.data()),
(unsigned int)kUnrelatedTestCertDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE,
cert2_nickname, false),
SECSuccess);
char cert_nickname[] = "Test Cert With Other Key";
SECItem cert_item = {
siBuffer, const_cast<unsigned char*>(kTestCertWithOtherKeyDER.data()),
(unsigned int)kTestCertWithOtherKeyDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false),
SECSuccess);
ScopedCERTCertList certs(
PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get()));
CheckCertListForSubjects(certs, {"CN=test cert", "CN=unrelated subject DN"});
}
} // namespace nss_test

View file

@ -11,39 +11,50 @@
'target_name': 'pk11_gtest',
'type': 'executable',
'sources': [
'pk11_aeskeywrap_unittest.cc',
'pk11_aes_cmac_unittest.cc',
'pk11_aes_gcm_unittest.cc',
'pk11_aeskeywrap_unittest.cc',
'pk11_aeskeywrappad_unittest.cc',
'pk11_cbc_unittest.cc',
'pk11_chacha20poly1305_unittest.cc',
'pk11_cipherop_unittest.cc',
'pk11_curve25519_unittest.cc',
'pk11_der_private_key_import_unittest.cc',
'pk11_des_unittest.cc',
'pk11_ecdsa_unittest.cc',
'pk11_encrypt_derive_unittest.cc',
'pk11_find_certs_unittest.cc',
'pk11_import_unittest.cc',
'pk11_keygen.cc',
'pk11_key_unittest.cc',
'pk11_module_unittest.cc',
'pk11_pbkdf2_unittest.cc',
'pk11_prf_unittest.cc',
'pk11_prng_unittest.cc',
'pk11_rsapkcs1_unittest.cc',
'pk11_rsapss_unittest.cc',
'pk11_der_private_key_import_unittest.cc',
'pk11_seed_cbc_unittest.cc',
'<(DEPTH)/gtests/common/gtests.cc'
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/lib/util/util.gyp:nssutil3',
'<(DEPTH)/cpputil/cpputil.gyp:cpputil',
'<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
'<(DEPTH)/lib/util/util.gyp:nssutil3',
],
'conditions': [
[ 'test_build==1', {
[ 'static_libs==1', {
'dependencies': [
'<(DEPTH)/lib/base/base.gyp:nssb',
'<(DEPTH)/lib/certdb/certdb.gyp:certdb',
'<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
'<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
'<(DEPTH)/lib/dev/dev.gyp:nssdev',
'<(DEPTH)/lib/nss/nss.gyp:nss_static',
'<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
'<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
'<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
'<(DEPTH)/lib/certdb/certdb.gyp:certdb',
'<(DEPTH)/lib/base/base.gyp:nssb',
'<(DEPTH)/lib/dev/dev.gyp:nssdev',
'<(DEPTH)/lib/pki/pki.gyp:nsspki',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl',
'<(DEPTH)/lib/libpkix/libpkix.gyp:libpkix',
],
}, {
'dependencies': [
@ -54,6 +65,12 @@
],
}
],
'target_defaults': {
'defines': [
'DLL_PREFIX=\"<(dll_prefix)\"',
'DLL_SUFFIX=\"<(dll_suffix)\"'
]
},
'variables': {
'module': 'nss'
}

View file

@ -0,0 +1,281 @@
/* -*- 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 "nss.h"
#include "pk11pub.h"
#include "pk11pqg.h"
#include "prerror.h"
#include "secoid.h"
#include "cpputil.h"
#include "nss_scoped_ptrs.h"
#include "gtest/gtest.h"
#include "databuffer.h"
#include "pk11_keygen.h"
namespace nss_test {
// This deleter deletes a set of objects, unlike the deleter on
// ScopedPK11GenericObject, which only deletes one.
struct PK11GenericObjectsDeleter {
void operator()(PK11GenericObject* objs) {
if (objs) {
PK11_DestroyGenericObjects(objs);
}
}
};
class Pk11KeyImportTestBase : public ::testing::Test {
public:
Pk11KeyImportTestBase() = default;
virtual ~Pk11KeyImportTestBase() = default;
void SetUp() override {
slot_.reset(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot_);
static const uint8_t pw[] = "pw";
SECItem pwItem = {siBuffer, toUcharPtr(pw), sizeof(pw)};
password_.reset(SECITEM_DupItem(&pwItem));
}
void Test(const Pkcs11KeyPairGenerator& generator) {
// Generate a key and export it.
KeyType key_type = nullKey;
ScopedSECKEYEncryptedPrivateKeyInfo key_info;
ScopedSECItem public_value;
GenerateAndExport(generator, &key_type, &key_info, &public_value);
// Note: NSS is currently unable export wrapped DH keys, so this doesn't
// test those beyond generate and verify.
if (key_type == dhKey) {
return;
}
ASSERT_NE(nullptr, public_value);
ASSERT_NE(nullptr, key_info);
// Now import the encrypted key.
static const uint8_t nick[] = "nick";
SECItem nickname = {siBuffer, toUcharPtr(nick), sizeof(nick)};
SECKEYPrivateKey* priv_tmp;
SECStatus rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
slot_.get(), key_info.get(), password_.get(), &nickname,
public_value.get(), PR_TRUE, PR_TRUE, key_type, 0, &priv_tmp, NULL);
ASSERT_EQ(SECSuccess, rv) << "PK11_ImportEncryptedPrivateKeyInfo failed "
<< PORT_ErrorToName(PORT_GetError());
ScopedSECKEYPrivateKey priv_key(priv_tmp);
ASSERT_NE(nullptr, priv_key);
CheckForPublicKey(priv_key, public_value.get());
}
private:
SECItem GetPublicComponent(ScopedSECKEYPublicKey& pub_key) {
SECItem null = {siBuffer, NULL, 0};
switch (SECKEY_GetPublicKeyType(pub_key.get())) {
case rsaKey:
case rsaPssKey:
case rsaOaepKey:
return pub_key->u.rsa.modulus;
case keaKey:
return pub_key->u.kea.publicValue;
case dsaKey:
return pub_key->u.dsa.publicValue;
case dhKey:
return pub_key->u.dh.publicValue;
case ecKey:
return pub_key->u.ec.publicValue;
case fortezzaKey: /* depricated */
case nullKey:
/* didn't use default here so we can catch new key types at compile time
*/
break;
}
return null;
}
void CheckForPublicKey(const ScopedSECKEYPrivateKey& priv_key,
const SECItem* expected_public) {
// Verify the public key exists.
StackSECItem priv_id;
KeyType type = SECKEY_GetPrivateKeyType(priv_key.get());
SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, priv_key.get(),
CKA_ID, &priv_id);
ASSERT_EQ(SECSuccess, rv) << "Couldn't read CKA_ID from private key: "
<< PORT_ErrorToName(PORT_GetError());
CK_ATTRIBUTE_TYPE value_type = CKA_VALUE;
switch (type) {
case rsaKey:
value_type = CKA_MODULUS;
break;
case dhKey:
case dsaKey:
value_type = CKA_VALUE;
break;
case ecKey:
value_type = CKA_EC_POINT;
break;
default:
FAIL() << "unknown key type";
}
// Scan public key objects until we find one with the same CKA_ID as
// priv_key
std::unique_ptr<PK11GenericObject, PK11GenericObjectsDeleter> objs(
PK11_FindGenericObjects(slot_.get(), CKO_PUBLIC_KEY));
ASSERT_NE(nullptr, objs);
for (PK11GenericObject* obj = objs.get(); obj != nullptr;
obj = PK11_GetNextGenericObject(obj)) {
StackSECItem pub_id;
rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_ID, &pub_id);
if (rv != SECSuccess) {
// Can't read CKA_ID from object.
continue;
}
if (!SECITEM_ItemsAreEqual(&priv_id, &pub_id)) {
// This isn't the object we're looking for.
continue;
}
StackSECItem token;
rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_TOKEN, &token);
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(1U, token.len);
ASSERT_NE(0, token.data[0]);
StackSECItem raw_value;
SECItem decoded_value;
rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, value_type, &raw_value);
ASSERT_EQ(SECSuccess, rv);
SECItem value = raw_value;
// Decode the EC_POINT and check the output against expected.
// CKA_EC_POINT isn't stable, see Bug 1520649.
ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
ASSERT_TRUE(arena);
if (value_type == CKA_EC_POINT) {
// If this fails due to the noted inconsistency, we may need to
// check the whole raw_value, or remove a leading UNCOMPRESSED_POINT tag
rv = SEC_QuickDERDecodeItem(arena.get(), &decoded_value,
SEC_ASN1_GET(SEC_OctetStringTemplate),
&raw_value);
ASSERT_EQ(SECSuccess, rv);
value = decoded_value;
}
ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &value))
<< "expected: "
<< DataBuffer(expected_public->data, expected_public->len)
<< std::endl
<< "actual: " << DataBuffer(value.data, value.len) << std::endl;
// Finally, convert the private to public and ensure it matches.
ScopedSECKEYPublicKey pub_key(SECKEY_ConvertToPublicKey(priv_key.get()));
ASSERT_TRUE(pub_key);
SECItem converted_public = GetPublicComponent(pub_key);
ASSERT_TRUE(converted_public.len != 0);
ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &converted_public))
<< "expected: "
<< DataBuffer(expected_public->data, expected_public->len)
<< std::endl
<< "actual: "
<< DataBuffer(converted_public.data, converted_public.len)
<< std::endl;
}
}
void GenerateAndExport(const Pkcs11KeyPairGenerator& generator,
KeyType* key_type,
ScopedSECKEYEncryptedPrivateKeyInfo* key_info,
ScopedSECItem* public_value) {
ScopedSECKEYPrivateKey priv_key;
ScopedSECKEYPublicKey pub_key;
generator.GenerateKey(&priv_key, &pub_key);
ASSERT_TRUE(priv_key);
// Save the public value, which we will need on import */
SECItem* pub_val;
KeyType t = SECKEY_GetPublicKeyType(pub_key.get());
switch (t) {
case rsaKey:
pub_val = &pub_key->u.rsa.modulus;
break;
case dhKey:
pub_val = &pub_key->u.dh.publicValue;
break;
case dsaKey:
pub_val = &pub_key->u.dsa.publicValue;
break;
case ecKey:
pub_val = &pub_key->u.ec.publicValue;
break;
default:
FAIL() << "Unknown key type";
}
CheckForPublicKey(priv_key, pub_val);
*key_type = t;
// Note: NSS is currently unable export wrapped DH keys, so this doesn't
// test those beyond generate and verify.
if (t == dhKey) {
return;
}
public_value->reset(SECITEM_DupItem(pub_val));
// Wrap and export the key.
ScopedSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo(
slot_.get(), SEC_OID_AES_256_CBC, password_.get(), priv_key.get(), 1,
nullptr));
ASSERT_NE(nullptr, epki) << "PK11_ExportEncryptedPrivKeyInfo failed: "
<< PORT_ErrorToName(PORT_GetError());
key_info->swap(epki);
}
ScopedPK11SlotInfo slot_;
ScopedSECItem password_;
};
class Pk11KeyImportTest
: public Pk11KeyImportTestBase,
public ::testing::WithParamInterface<CK_MECHANISM_TYPE> {
public:
Pk11KeyImportTest() = default;
virtual ~Pk11KeyImportTest() = default;
};
TEST_P(Pk11KeyImportTest, GenerateExportImport) {
Test(Pkcs11KeyPairGenerator(GetParam()));
}
INSTANTIATE_TEST_CASE_P(Pk11KeyImportTest, Pk11KeyImportTest,
::testing::Values(CKM_RSA_PKCS_KEY_PAIR_GEN,
CKM_DSA_KEY_PAIR_GEN,
CKM_DH_PKCS_KEY_PAIR_GEN));
class Pk11KeyImportTestEC : public Pk11KeyImportTestBase,
public ::testing::WithParamInterface<SECOidTag> {
public:
Pk11KeyImportTestEC() = default;
virtual ~Pk11KeyImportTestEC() = default;
};
TEST_P(Pk11KeyImportTestEC, GenerateExportImport) {
Test(Pkcs11KeyPairGenerator(CKM_EC_KEY_PAIR_GEN, GetParam()));
}
INSTANTIATE_TEST_CASE_P(Pk11KeyImportTestEC, Pk11KeyImportTestEC,
::testing::Values(SEC_OID_SECG_EC_SECP256R1,
SEC_OID_SECG_EC_SECP384R1,
SEC_OID_SECG_EC_SECP521R1,
SEC_OID_CURVE25519));
} // namespace nss_test

View file

@ -0,0 +1,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 "nss.h"
#include "pk11pub.h"
#include "pk11pqg.h"
#include "prerror.h"
#include "secoid.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "pk11_keygen.h"
namespace nss_test {
class Pkcs11NullKeyTestBase : public ::testing::Test {
protected:
// This constructs a key pair, then erases the public value from the public
// key. NSS should reject this.
void Test(const Pkcs11KeyPairGenerator& generator,
CK_MECHANISM_TYPE dh_mech) {
ScopedSECKEYPrivateKey priv;
ScopedSECKEYPublicKey pub;
generator.GenerateKey(&priv, &pub);
ASSERT_TRUE(priv);
// These don't leak because they are allocated to the arena associated with
// the public key.
SECItem* pub_val = nullptr;
switch (SECKEY_GetPublicKeyType(pub.get())) {
case rsaKey:
pub_val = &pub->u.rsa.modulus;
break;
case dsaKey:
pub_val = &pub->u.dsa.publicValue;
break;
case dhKey:
pub_val = &pub->u.dh.publicValue;
break;
case ecKey:
pub_val = &pub->u.ec.publicValue;
break;
default:
FAIL() << "Unknown key type " << SECKEY_GetPublicKeyType(pub.get());
}
pub_val->data = nullptr;
pub_val->len = 0;
ScopedPK11SymKey symKey(PK11_PubDeriveWithKDF(
priv.get(), pub.get(), false, nullptr, nullptr, dh_mech,
CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr));
ASSERT_FALSE(symKey);
}
};
class Pkcs11DhNullKeyTest : public Pkcs11NullKeyTestBase {};
TEST_F(Pkcs11DhNullKeyTest, UseNullPublicValue) {
Test(Pkcs11KeyPairGenerator(CKM_DH_PKCS_KEY_PAIR_GEN), CKM_DH_PKCS_DERIVE);
}
class Pkcs11EcdhNullKeyTest : public Pkcs11NullKeyTestBase,
public ::testing::WithParamInterface<SECOidTag> {
};
TEST_P(Pkcs11EcdhNullKeyTest, UseNullPublicValue) {
Test(Pkcs11KeyPairGenerator(CKM_EC_KEY_PAIR_GEN, GetParam()),
CKM_ECDH1_DERIVE);
}
INSTANTIATE_TEST_CASE_P(Pkcs11EcdhNullKeyTest, Pkcs11EcdhNullKeyTest,
::testing::Values(SEC_OID_SECG_EC_SECP256R1,
SEC_OID_SECG_EC_SECP384R1,
SEC_OID_SECG_EC_SECP521R1,
SEC_OID_CURVE25519));
} // namespace nss_test

View file

@ -0,0 +1,143 @@
/* 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 "pk11_keygen.h"
#include "pk11pub.h"
#include "pk11pqg.h"
#include "prerror.h"
#include "gtest/gtest.h"
namespace nss_test {
class ParamHolder {
public:
virtual void* get() = 0;
virtual ~ParamHolder() = default;
protected:
ParamHolder() = default;
};
void Pkcs11KeyPairGenerator::GenerateKey(ScopedSECKEYPrivateKey* priv_key,
ScopedSECKEYPublicKey* pub_key) const {
// This function returns if an assertion fails, so don't leak anything.
priv_key->reset(nullptr);
pub_key->reset(nullptr);
auto params = MakeParams();
ASSERT_NE(nullptr, params);
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
SECKEYPublicKey* pub_tmp;
ScopedSECKEYPrivateKey priv_tmp(PK11_GenerateKeyPair(
slot.get(), mech_, params->get(), &pub_tmp, PR_FALSE, PR_TRUE, nullptr));
ASSERT_NE(nullptr, priv_tmp) << "PK11_GenerateKeyPair failed: "
<< PORT_ErrorToName(PORT_GetError());
ASSERT_NE(nullptr, pub_tmp);
priv_key->swap(priv_tmp);
pub_key->reset(pub_tmp);
}
class RsaParamHolder : public ParamHolder {
public:
RsaParamHolder() : params_({1024, 0x010001}) {}
~RsaParamHolder() = default;
void* get() override { return &params_; }
private:
PK11RSAGenParams params_;
};
class PqgParamHolder : public ParamHolder {
public:
PqgParamHolder(PQGParams* params) : params_(params) {}
~PqgParamHolder() = default;
void* get() override { return params_.get(); }
private:
ScopedPQGParams params_;
};
class DhParamHolder : public PqgParamHolder {
public:
DhParamHolder(PQGParams* params)
: PqgParamHolder(params),
params_({nullptr, params->prime, params->base}) {}
~DhParamHolder() = default;
void* get() override { return &params_; }
private:
SECKEYDHParams params_;
};
class EcParamHolder : public ParamHolder {
public:
EcParamHolder(SECOidTag curve_oid) {
SECOidData* curve = SECOID_FindOIDByTag(curve_oid);
EXPECT_NE(nullptr, curve);
size_t plen = curve->oid.len + 2;
extra_.reset(new uint8_t[plen]);
extra_[0] = SEC_ASN1_OBJECT_ID;
extra_[1] = static_cast<uint8_t>(curve->oid.len);
memcpy(&extra_[2], curve->oid.data, curve->oid.len);
ec_params_ = {siBuffer, extra_.get(), static_cast<unsigned int>(plen)};
}
~EcParamHolder() = default;
void* get() override { return &ec_params_; }
private:
SECKEYECParams ec_params_;
std::unique_ptr<uint8_t[]> extra_;
};
std::unique_ptr<ParamHolder> Pkcs11KeyPairGenerator::MakeParams() const {
switch (mech_) {
case CKM_RSA_PKCS_KEY_PAIR_GEN:
std::cerr << "Generate RSA pair" << std::endl;
return std::unique_ptr<ParamHolder>(new RsaParamHolder());
case CKM_DSA_KEY_PAIR_GEN:
case CKM_DH_PKCS_KEY_PAIR_GEN: {
PQGParams* pqg_params = nullptr;
PQGVerify* pqg_verify = nullptr;
const unsigned int key_size = 1024;
SECStatus rv = PK11_PQG_ParamGenV2(key_size, 0, key_size / 16,
&pqg_params, &pqg_verify);
if (rv != SECSuccess) {
ADD_FAILURE() << "PK11_PQG_ParamGenV2 failed";
return nullptr;
}
EXPECT_NE(nullptr, pqg_verify);
EXPECT_NE(nullptr, pqg_params);
PK11_PQG_DestroyVerify(pqg_verify);
if (mech_ == CKM_DSA_KEY_PAIR_GEN) {
std::cerr << "Generate DSA pair" << std::endl;
return std::unique_ptr<ParamHolder>(new PqgParamHolder(pqg_params));
}
std::cerr << "Generate DH pair" << std::endl;
return std::unique_ptr<ParamHolder>(new DhParamHolder(pqg_params));
}
case CKM_EC_KEY_PAIR_GEN:
std::cerr << "Generate EC pair on " << curve_ << std::endl;
return std::unique_ptr<ParamHolder>(new EcParamHolder(curve_));
default:
ADD_FAILURE() << "unknown OID " << mech_;
}
return nullptr;
}
} // namespace nss_test

View file

@ -0,0 +1,34 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nss.h"
#include "secoid.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {
class ParamHolder;
class Pkcs11KeyPairGenerator {
public:
Pkcs11KeyPairGenerator(CK_MECHANISM_TYPE mech, SECOidTag curve_oid)
: mech_(mech), curve_(curve_oid) {}
Pkcs11KeyPairGenerator(CK_MECHANISM_TYPE mech)
: Pkcs11KeyPairGenerator(mech, SEC_OID_UNKNOWN) {}
CK_MECHANISM_TYPE mechanism() const { return mech_; }
SECOidTag curve() const { return curve_; }
void GenerateKey(ScopedSECKEYPrivateKey* priv_key,
ScopedSECKEYPublicKey* pub_key) const;
private:
std::unique_ptr<ParamHolder> MakeParams() const;
CK_MECHANISM_TYPE mech_;
SECOidTag curve_;
};
} // namespace nss_test

View file

@ -0,0 +1,84 @@
/* -*- 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 "nss.h"
#include "pk11pub.h"
#include "prerror.h"
#include "prsystem.h"
#include "secoid.h"
#include "nss_scoped_ptrs.h"
#include "gtest/gtest.h"
#include "databuffer.h"
namespace nss_test {
class Pkcs11ModuleTest : public ::testing::Test {
public:
Pkcs11ModuleTest() {}
void SetUp() override {
ASSERT_EQ(SECSuccess, SECMOD_AddNewModule("Pkcs11ModuleTest", DLL_PREFIX
"pkcs11testmodule." DLL_SUFFIX,
0, 0))
<< PORT_ErrorToName(PORT_GetError());
}
void TearDown() override {
int type;
ASSERT_EQ(SECSuccess, SECMOD_DeleteModule("Pkcs11ModuleTest", &type));
ASSERT_EQ(SECMOD_EXTERNAL, type);
}
};
TEST_F(Pkcs11ModuleTest, LoadUnload) {
ScopedSECMODModule module(SECMOD_FindModule("Pkcs11ModuleTest"));
EXPECT_NE(nullptr, module);
}
TEST_F(Pkcs11ModuleTest, ListSlots) {
ScopedPK11SlotList slots(
PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, nullptr));
EXPECT_NE(nullptr, slots);
PK11SlotListElement* element = PK11_GetFirstSafe(slots.get());
EXPECT_NE(nullptr, element);
// These tokens are always present.
const std::vector<std::string> kSlotsWithToken = {
"NSS Internal Cryptographic Services",
"NSS User Private Key and Certificate Services",
"Test PKCS11 Public Certs Slot", "Test PKCS11 Slot 二"};
std::vector<std::string> foundSlots;
do {
std::string name = PK11_GetSlotName(element->slot);
foundSlots.push_back(name);
std::cerr << "loaded slot: " << name << std::endl;
} while ((element = PK11_GetNextSafe(slots.get(), element, PR_FALSE)) !=
nullptr);
std::sort(foundSlots.begin(), foundSlots.end());
EXPECT_TRUE(std::equal(kSlotsWithToken.begin(), kSlotsWithToken.end(),
foundSlots.begin()));
}
TEST_F(Pkcs11ModuleTest, PublicCertificatesToken) {
const std::string kRegularToken = "Test PKCS11 Tokeñ 2 Label";
const std::string kPublicCertificatesToken = "Test PKCS11 Public Certs Token";
ScopedPK11SlotInfo slot1(PK11_FindSlotByName(kRegularToken.c_str()));
EXPECT_NE(nullptr, slot1);
EXPECT_FALSE(PK11_IsFriendly(slot1.get()));
ScopedPK11SlotInfo slot2(
PK11_FindSlotByName(kPublicCertificatesToken.c_str()));
EXPECT_NE(nullptr, slot2);
EXPECT_TRUE(PK11_IsFriendly(slot2.get()));
}
} // namespace nss_test

View file

@ -22,53 +22,102 @@ class Pkcs11Pbkdf2Test : public ::testing::Test {
public:
void Derive(std::vector<uint8_t>& derived, SECOidTag hash_alg) {
// Shared between test vectors.
const unsigned int iterations = 4096;
const unsigned int kIterations = 4096;
std::string pass("passwordPASSWORDpassword");
std::string salt("saltSALTsaltSALTsaltSALTsaltSALTsalt");
// Derivation must succeed with the right values.
EXPECT_TRUE(DeriveBytes(pass, salt, derived, hash_alg, iterations));
EXPECT_TRUE(DeriveBytes(pass, salt, derived, hash_alg, kIterations));
// Derivation must fail when the password is bogus.
std::string bogusPass("PasswordPASSWORDpassword");
EXPECT_FALSE(DeriveBytes(bogusPass, salt, derived, hash_alg, iterations));
std::string bogus_pass("PasswordPASSWORDpassword");
EXPECT_FALSE(DeriveBytes(bogus_pass, salt, derived, hash_alg, kIterations));
// Derivation must fail when the salt is bogus.
std::string bogusSalt("SaltSALTsaltSALTsaltSALTsaltSALTsalt");
EXPECT_FALSE(DeriveBytes(pass, bogusSalt, derived, hash_alg, iterations));
std::string bogus_salt("SaltSALTsaltSALTsaltSALTsaltSALTsalt");
EXPECT_FALSE(DeriveBytes(pass, bogus_salt, derived, hash_alg, kIterations));
// Derivation must fail when using the wrong hash function.
SECOidTag next_hash_alg = static_cast<SECOidTag>(hash_alg + 1);
EXPECT_FALSE(DeriveBytes(pass, salt, derived, next_hash_alg, iterations));
EXPECT_FALSE(DeriveBytes(pass, salt, derived, next_hash_alg, kIterations));
// Derivation must fail when using the wrong number of iterations.
EXPECT_FALSE(DeriveBytes(pass, salt, derived, hash_alg, iterations + 1));
// Derivation must fail when using the wrong number of kIterations.
EXPECT_FALSE(DeriveBytes(pass, salt, derived, hash_alg, kIterations + 1));
}
void KeySizes(SECOidTag hash_alg) {
// These tests will only validate the controls around the key sizes.
// The resulting key is tested above, with valid key sizes.
const unsigned int kIterations = 10;
std::string pass("passwordPASSWORDpassword");
std::string salt("saltSALTsaltSALTsaltSALTsaltSALTsalt");
// Derivation must fail when using key sizes bigger than MAX_KEY_LEN.
const int big_key_size = 768;
EXPECT_FALSE(KeySizeParam(pass, salt, big_key_size, hash_alg, kIterations));
// Zero is acceptable as key size and will be managed internally.
const int zero_key_size = 0;
EXPECT_TRUE(KeySizeParam(pass, salt, zero_key_size, hash_alg, kIterations));
// -1 will be set to 0 internally and this means that the key size will be
// obtained from the template. If the template doesn't have this defined,
// it must fail.
const int minus_key_size = -1;
EXPECT_FALSE(
KeySizeParam(pass, salt, minus_key_size, hash_alg, kIterations));
// Lower than -1 is not allowed, as -1 means no keyLen defined.
const int negative_key_size = -10;
EXPECT_FALSE(
KeySizeParam(pass, salt, negative_key_size, hash_alg, kIterations));
}
private:
bool DeriveBytes(std::string& pass, std::string& salt,
std::vector<uint8_t>& derived, SECOidTag hash_alg,
unsigned int iterations) {
SECItem passItem = {siBuffer, ToUcharPtr(pass),
static_cast<unsigned int>(pass.length())};
SECItem saltItem = {siBuffer, ToUcharPtr(salt),
static_cast<unsigned int>(salt.length())};
unsigned int kIterations) {
SECItem pass_item = {siBuffer, ToUcharPtr(pass),
static_cast<unsigned int>(pass.length())};
SECItem salt_item = {siBuffer, ToUcharPtr(salt),
static_cast<unsigned int>(salt.length())};
// Set up PBKDF2 params.
ScopedSECAlgorithmID alg_id(
PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, hash_alg, hash_alg,
derived.size(), iterations, &saltItem));
derived.size(), kIterations, &salt_item));
// Derive.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey symKey(
PK11_PBEKeyGen(slot.get(), alg_id.get(), &passItem, false, nullptr));
ScopedPK11SymKey sym_key(
PK11_PBEKeyGen(slot.get(), alg_id.get(), &pass_item, false, nullptr));
SECStatus rv = PK11_ExtractKeyValue(symKey.get());
SECStatus rv = PK11_ExtractKeyValue(sym_key.get());
EXPECT_EQ(rv, SECSuccess);
SECItem* keyData = PK11_GetKeyData(symKey.get());
return !memcmp(&derived[0], keyData->data, keyData->len);
SECItem* key_data = PK11_GetKeyData(sym_key.get());
return !memcmp(&derived[0], key_data->data, key_data->len);
}
bool KeySizeParam(std::string& pass, std::string& salt, const int key_size,
SECOidTag hash_alg, unsigned int kIterations) {
SECItem pass_item = {siBuffer, ToUcharPtr(pass),
static_cast<unsigned int>(pass.length())};
SECItem salt_item = {siBuffer, ToUcharPtr(salt),
static_cast<unsigned int>(salt.length())};
// Set up PBKDF2 params.
ScopedSECAlgorithmID alg_id(
PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, hash_alg, hash_alg,
key_size, kIterations, &salt_item));
// Try to generate a key with the defined params.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey sym_key(
PK11_PBEKeyGen(slot.get(), alg_id.get(), &pass_item, false, nullptr));
// Should be nullptr if fail.
return sym_key.get();
}
};
@ -93,4 +142,9 @@ TEST_F(Pkcs11Pbkdf2Test, DeriveKnown2) {
Derive(derived, SEC_OID_HMAC_SHA256);
}
TEST_F(Pkcs11Pbkdf2Test, KeyLenSizes) {
// The size controls are regardless of the algorithms.
KeySizes(SEC_OID_HMAC_SHA256);
}
} // namespace nss_test

View file

@ -93,6 +93,20 @@ TEST_F(Pkcs11RsaPssTest, GenerateAndSignAndVerify) {
EXPECT_EQ(rv, SECFailure);
}
TEST_F(Pkcs11RsaPssTest, NoLeakWithInvalidExponent) {
// Attempt to generate an RSA key with a public exponent of 1. This should
// fail, but it shouldn't leak memory.
PK11RSAGenParams rsaGenParams = {1024, 0x01};
// Generate RSA key pair.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
SECKEYPublicKey* pubKey = nullptr;
SECKEYPrivateKey* privKey =
PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaGenParams,
&pubKey, false, false, nullptr);
EXPECT_FALSE(privKey);
EXPECT_FALSE(pubKey);
}
class Pkcs11RsaPssVectorTest
: public Pkcs11RsaPssTest,
public ::testing::WithParamInterface<Pkcs11SignatureTestParams> {};

View file

@ -0,0 +1,71 @@
/* -*- 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 "nss.h"
#include "pk11pub.h"
#include "secerr.h"
#include "nss_scoped_ptrs.h"
#include "gtest/gtest.h"
#include "util.h"
namespace nss_test {
class Pkcs11SeedCbcTest : public ::testing::Test {
protected:
enum class Action { Encrypt, Decrypt };
SECStatus EncryptDecryptSeed(Action action, unsigned int input_size,
unsigned int output_size) {
// Generate a random key.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey sym_key(
PK11_KeyGen(slot.get(), kMech, nullptr, 16, nullptr));
EXPECT_TRUE(!!sym_key);
std::vector<uint8_t> data(input_size);
std::vector<uint8_t> init_vector(16);
std::vector<uint8_t> output(output_size);
SECItem params = {siBuffer, init_vector.data(),
(unsigned int)init_vector.size()};
// Try to encrypt/decrypt.
unsigned int output_len = 0;
if (action == Action::Encrypt) {
return PK11_Encrypt(sym_key.get(), kMech, &params, output.data(),
&output_len, output_size, data.data(), data.size());
} else {
return PK11_Decrypt(sym_key.get(), kMech, &params, output.data(),
&output_len, output_size, data.data(), data.size());
}
}
const CK_MECHANISM_TYPE kMech = CKM_SEED_CBC;
};
// The intention here is to test the arguments of these functions
// The resulted content is already tested in EncryptDeriveTests.
// SEED_CBC needs an IV of 16 bytes.
// The input data size must be multiple of 16.
// If not, some padding should be added.
// The output size must be at least the size of input data.
TEST_F(Pkcs11SeedCbcTest, SeedCBC_ValidArgs) {
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Encrypt, 16, 16));
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Decrypt, 16, 16));
// No problem if maxLen is bigger than input data.
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Encrypt, 16, 32));
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Decrypt, 16, 32));
}
TEST_F(Pkcs11SeedCbcTest, SeedCBC_InvalidArgs) {
// maxLen lower than input data.
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Encrypt, 16, 10));
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Decrypt, 16, 10));
// input data not multiple of SEED_BLOCK_SIZE (16)
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Encrypt, 17, 32));
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Decrypt, 17, 32));
}
} // namespace nss_test

View file

@ -59,6 +59,9 @@ class Pk11SignatureTest : public ::testing::Test {
ScopedCERTSubjectPublicKeyInfo certSpki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
if (!certSpki) {
return nullptr;
}
return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get()));
}

View file

@ -0,0 +1,45 @@
#! 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 config.mk
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
#######################################################################
include $(CORE_DEPTH)/coreconf/rules.mk
#######################################################################
# (6) Execute "component" rules. (OPTIONAL) #
#######################################################################
#######################################################################
# (7) Execute "local" rules. (OPTIONAL). #
#######################################################################
export:: private_export

View file

@ -0,0 +1,16 @@
#
# 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/.
# can't do this in manifest.mn because OS_TARGET isn't defined there.
ifeq (,$(filter-out WIN%,$(OS_TARGET)))
# don't want the 32 in the shared library name
SHARED_LIBRARY = $(OBJDIR)/$(DLL_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX)
IMPORT_LIBRARY = $(OBJDIR)/$(IMPORT_LIB_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION)$(IMPORT_LIB_SUFFIX)
RES = $(OBJDIR)/$(LIBRARY_NAME).res
RESNAME = $(LIBRARY_NAME).rc
endif

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 = \
pkcs11testmodule.cpp \
$(NULL)
INCLUDES += -I$(CORE_DEPTH)/cpputil
REQUIRES = cpputil
MAPFILE = $(OBJDIR)/pkcs11testmodule.def
LIBRARY_NAME = pkcs11testmodule
EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)cpputil.$(LIB_SUFFIX) \
$(NULL)

View file

@ -0,0 +1,658 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
// This is a testing PKCS #11 module that simulates a token being inserted and
// removed from a slot every 50ms. This is achieved mainly in
// Test_C_WaitForSlotEvent. If the application that loaded this module calls
// C_WaitForSlotEvent, this module waits for 50ms and returns, having changed
// its internal state to report that the token has either been inserted or
// removed, as appropriate.
// This module also provides an alternate token that is always present for tests
// that don't want the cyclic behavior described above.
#include <assert.h>
#include <string.h>
#ifdef _WIN32
# include <windows.h> // for Sleep
#else
# include <unistd.h> // for usleep
#endif
#include "pkcs11t.h"
#undef CK_DECLARE_FUNCTION
#ifdef _WIN32
#define CK_DECLARE_FUNCTION(rtype, func) extern rtype __declspec(dllexport) func
#else
#define CK_DECLARE_FUNCTION(rtype, func) extern rtype func
#endif
#include "pkcs11.h"
#if __cplusplus < 201103L
# include <prtypes.h>
# define static_assert(condition, message) PR_STATIC_ASSERT(condition)
#endif
CK_RV Test_C_Initialize(CK_VOID_PTR) { return CKR_OK; }
CK_RV Test_C_Finalize(CK_VOID_PTR) { return CKR_OK; }
static const CK_VERSION CryptokiVersion = {2, 2};
static const CK_VERSION TestLibraryVersion = {0, 0};
static const char TestLibraryDescription[] = "Test PKCS11 Library";
static const char TestManufacturerID[] = "Test PKCS11 Manufacturer ID";
/* The dest buffer is one in the CK_INFO or CK_TOKEN_INFO structs.
* Those buffers are padded with spaces. DestSize corresponds to the declared
* size for those buffers (e.g. 32 for `char foo[32]`).
* The src buffer is a string literal. SrcSize includes the string
* termination character (e.g. 4 for `const char foo[] = "foo"` */
template <size_t DestSize, size_t SrcSize>
void CopyString(unsigned char (&dest)[DestSize], const char (&src)[SrcSize]) {
static_assert(DestSize >= SrcSize - 1, "DestSize >= SrcSize - 1");
memcpy(dest, src, SrcSize - 1);
memset(dest + SrcSize - 1, ' ', DestSize - SrcSize + 1);
}
CK_RV Test_C_GetInfo(CK_INFO_PTR pInfo) {
if (!pInfo) {
return CKR_ARGUMENTS_BAD;
}
pInfo->cryptokiVersion = CryptokiVersion;
CopyString(pInfo->manufacturerID, TestManufacturerID);
pInfo->flags = 0; // must be 0
CopyString(pInfo->libraryDescription, TestLibraryDescription);
pInfo->libraryVersion = TestLibraryVersion;
return CKR_OK;
}
CK_RV Test_C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR) { return CKR_OK; }
static int tokenPresent = 0;
// The token in slot 4 has 2 objects. Both of them are profile object
// and identified by object ID 1 or 2.
static bool readingProfile = false;
static const CK_PROFILE_ID profiles[] = {CKP_PUBLIC_CERTIFICATES_TOKEN,
CKP_BASELINE_PROVIDER};
static int profileIndex = 0;
CK_RV Test_C_GetSlotList(CK_BBOOL limitToTokensPresent,
CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) {
if (!pulCount) {
return CKR_ARGUMENTS_BAD;
}
CK_SLOT_ID slots[4];
CK_ULONG slotCount = 0;
// We always return slot 2 and 4.
slots[slotCount++] = 2;
slots[slotCount++] = 4;
// Slot 1 is a removable slot where a token is present if
// tokenPresent = CK_TRUE.
if (tokenPresent || !limitToTokensPresent) {
slots[slotCount++] = 1;
}
// Slot 3 is a removable slot which never has a token.
if (!limitToTokensPresent) {
slots[slotCount++] = 3;
}
if (pSlotList) {
if (*pulCount < slotCount) {
return CKR_BUFFER_TOO_SMALL;
}
memcpy(pSlotList, slots, sizeof(CK_SLOT_ID) * slotCount);
}
*pulCount = slotCount;
return CKR_OK;
}
static const char TestSlotDescription[] = "Test PKCS11 Slot";
static const char TestSlot2Description[] = "Test PKCS11 Slot 二";
static const char TestSlot3Description[] = "Empty PKCS11 Slot";
static const char TestSlot4Description[] = "Test PKCS11 Public Certs Slot";
CK_RV Test_C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) {
if (!pInfo) {
return CKR_ARGUMENTS_BAD;
}
switch (slotID) {
case 1:
CopyString(pInfo->slotDescription, TestSlotDescription);
pInfo->flags =
(tokenPresent ? CKF_TOKEN_PRESENT : 0) | CKF_REMOVABLE_DEVICE;
break;
case 2:
CopyString(pInfo->slotDescription, TestSlot2Description);
pInfo->flags = CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE;
break;
case 3:
CopyString(pInfo->slotDescription, TestSlot3Description);
pInfo->flags = CKF_REMOVABLE_DEVICE;
break;
case 4:
CopyString(pInfo->slotDescription, TestSlot4Description);
pInfo->flags = CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE;
break;
default:
return CKR_ARGUMENTS_BAD;
}
CopyString(pInfo->manufacturerID, TestManufacturerID);
pInfo->hardwareVersion = TestLibraryVersion;
pInfo->firmwareVersion = TestLibraryVersion;
return CKR_OK;
}
// Deliberately include énye to ensure we're handling encoding correctly.
// The PKCS #11 base specification v2.20 specifies that strings be encoded
// as UTF-8.
static const char TestTokenLabel[] = "Test PKCS11 Tokeñ Label";
static const char TestToken2Label[] = "Test PKCS11 Tokeñ 2 Label";
static const char TestToken4Label[] = "Test PKCS11 Public Certs Token";
static const char TestTokenModel[] = "Test Model";
CK_RV Test_C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) {
if (!pInfo) {
return CKR_ARGUMENTS_BAD;
}
switch (slotID) {
case 1:
CopyString(pInfo->label, TestTokenLabel);
break;
case 2:
CopyString(pInfo->label, TestToken2Label);
break;
case 4:
CopyString(pInfo->label, TestToken4Label);
break;
default:
return CKR_ARGUMENTS_BAD;
}
CopyString(pInfo->manufacturerID, TestManufacturerID);
CopyString(pInfo->model, TestTokenModel);
memset(pInfo->serialNumber, 0, sizeof(pInfo->serialNumber));
pInfo->flags = CKF_TOKEN_INITIALIZED;
pInfo->ulMaxSessionCount = 1;
pInfo->ulSessionCount = 0;
pInfo->ulMaxRwSessionCount = 1;
pInfo->ulRwSessionCount = 0;
pInfo->ulMaxPinLen = 4;
pInfo->ulMinPinLen = 4;
pInfo->ulTotalPublicMemory = 1024;
pInfo->ulFreePublicMemory = 1024;
pInfo->ulTotalPrivateMemory = 1024;
pInfo->ulFreePrivateMemory = 1024;
pInfo->hardwareVersion = TestLibraryVersion;
pInfo->firmwareVersion = TestLibraryVersion;
memset(pInfo->utcTime, 0, sizeof(pInfo->utcTime));
return CKR_OK;
}
CK_RV Test_C_GetMechanismList(CK_SLOT_ID, CK_MECHANISM_TYPE_PTR,
CK_ULONG_PTR pulCount) {
if (!pulCount) {
return CKR_ARGUMENTS_BAD;
}
*pulCount = 0;
return CKR_OK;
}
CK_RV Test_C_GetMechanismInfo(CK_SLOT_ID, CK_MECHANISM_TYPE,
CK_MECHANISM_INFO_PTR) {
return CKR_OK;
}
CK_RV Test_C_InitToken(CK_SLOT_ID, CK_UTF8CHAR_PTR, CK_ULONG, CK_UTF8CHAR_PTR) {
return CKR_OK;
}
CK_RV Test_C_InitPIN(CK_SESSION_HANDLE, CK_UTF8CHAR_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SetPIN(CK_SESSION_HANDLE, CK_UTF8CHAR_PTR, CK_ULONG,
CK_UTF8CHAR_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS, CK_VOID_PTR, CK_NOTIFY,
CK_SESSION_HANDLE_PTR phSession) {
switch (slotID) {
case 1:
*phSession = 1;
break;
case 2:
*phSession = 2;
break;
case 4:
*phSession = 4;
break;
default:
return CKR_ARGUMENTS_BAD;
}
return CKR_OK;
}
CK_RV Test_C_CloseSession(CK_SESSION_HANDLE) { return CKR_OK; }
CK_RV Test_C_CloseAllSessions(CK_SLOT_ID) { return CKR_OK; }
CK_RV Test_C_GetSessionInfo(CK_SESSION_HANDLE hSession,
CK_SESSION_INFO_PTR pInfo) {
if (!pInfo) {
return CKR_ARGUMENTS_BAD;
}
switch (hSession) {
case 1:
pInfo->slotID = 1;
break;
case 2:
pInfo->slotID = 2;
break;
case 4:
pInfo->slotID = 4;
break;
default:
return CKR_ARGUMENTS_BAD;
}
pInfo->state = CKS_RO_PUBLIC_SESSION;
pInfo->flags = CKF_SERIAL_SESSION;
return CKR_OK;
}
CK_RV Test_C_GetOperationState(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SetOperationState(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
CK_OBJECT_HANDLE, CK_OBJECT_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_Login(CK_SESSION_HANDLE, CK_USER_TYPE, CK_UTF8CHAR_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_Logout(CK_SESSION_HANDLE) { return CKR_FUNCTION_NOT_SUPPORTED; }
CK_RV Test_C_CreateObject(CK_SESSION_HANDLE, CK_ATTRIBUTE_PTR, CK_ULONG,
CK_OBJECT_HANDLE_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_CopyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ATTRIBUTE_PTR,
CK_ULONG, CK_OBJECT_HANDLE_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DestroyObject(CK_SESSION_HANDLE, CK_OBJECT_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_GetObjectSize(CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_GetAttributeValue(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE hObject,
CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
if (hSession == 4) {
assert(hObject >= 1 &&
hObject - 1 < sizeof(profiles) / sizeof(profiles[0]));
for (CK_ULONG count = 0; count < ulCount; count++) {
if (pTemplate[count].type == CKA_PROFILE_ID) {
if (pTemplate[count].pValue) {
assert(pTemplate[count].ulValueLen == sizeof(CK_ULONG));
CK_ULONG value = profiles[hObject - 1];
memcpy(pTemplate[count].pValue, &value, sizeof(value));
} else {
pTemplate[count].ulValueLen = sizeof(CK_ULONG);
}
} else {
pTemplate[count].ulValueLen = (CK_ULONG)-1;
}
}
return CKR_OK;
}
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SetAttributeValue(CK_SESSION_HANDLE, CK_OBJECT_HANDLE,
CK_ATTRIBUTE_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_FindObjectsInit(CK_SESSION_HANDLE hSession,
CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) {
// Slot 4
if (hSession == 4) {
for (CK_ULONG count = 0; count < ulCount; count++) {
CK_ATTRIBUTE attribute = pTemplate[count];
if (attribute.type == CKA_CLASS) {
assert(attribute.ulValueLen == sizeof(CK_ULONG));
CK_ULONG value;
memcpy(&value, attribute.pValue, attribute.ulValueLen);
if (value == CKO_PROFILE) {
readingProfile = true;
profileIndex = 0;
break;
}
}
}
}
return CKR_OK;
}
CK_RV Test_C_FindObjects(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE_PTR phObject,
CK_ULONG ulMaxObjectCount,
CK_ULONG_PTR pulObjectCount) {
if (readingProfile) {
assert(hSession == 4);
CK_ULONG count = ulMaxObjectCount;
size_t remaining = sizeof(profiles) / sizeof(profiles[0]) - profileIndex;
if (count > remaining) {
count = remaining;
}
for (CK_ULONG i = 0; i < count; i++) {
phObject[i] = i + 1;
}
profileIndex += count;
*pulObjectCount = count;
} else {
*pulObjectCount = 0;
}
return CKR_OK;
}
CK_RV Test_C_FindObjectsFinal(CK_SESSION_HANDLE hSession) {
readingProfile = false;
return CKR_OK;
}
CK_RV Test_C_EncryptInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
CK_OBJECT_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_Encrypt(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_EncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_EncryptFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DecryptInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
CK_OBJECT_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_Decrypt(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DecryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DecryptFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DigestInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_Digest(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DigestUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DigestKey(CK_SESSION_HANDLE, CK_OBJECT_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DigestFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SignInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_Sign(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SignUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SignFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SignRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
CK_OBJECT_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SignRecover(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_VerifyInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_Verify(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_VerifyUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_VerifyFinal(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_VerifyRecoverInit(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
CK_OBJECT_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_VerifyRecover(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DigestEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DecryptDigestUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SignEncryptUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DecryptVerifyUpdate(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG,
CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_GenerateKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR,
CK_ULONG, CK_OBJECT_HANDLE_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_GenerateKeyPair(CK_SESSION_HANDLE, CK_MECHANISM_PTR,
CK_ATTRIBUTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR,
CK_ULONG, CK_OBJECT_HANDLE_PTR,
CK_OBJECT_HANDLE_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_WrapKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE,
CK_OBJECT_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_UnwrapKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE,
CK_BYTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG,
CK_OBJECT_HANDLE_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_DeriveKey(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE,
CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_SeedRandom(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_GenerateRandom(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_GetFunctionStatus(CK_SESSION_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_CancelFunction(CK_SESSION_HANDLE) {
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV Test_C_WaitForSlotEvent(CK_FLAGS, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR) {
#ifdef _WIN32
Sleep(50); // Sleep takes the duration argument as milliseconds
#else
usleep(50000); // usleep takes the duration argument as microseconds
#endif
*pSlot = 1;
tokenPresent = !tokenPresent;
return CKR_OK;
}
static CK_FUNCTION_LIST FunctionList = {{2, 2},
Test_C_Initialize,
Test_C_Finalize,
Test_C_GetInfo,
Test_C_GetFunctionList,
Test_C_GetSlotList,
Test_C_GetSlotInfo,
Test_C_GetTokenInfo,
Test_C_GetMechanismList,
Test_C_GetMechanismInfo,
Test_C_InitToken,
Test_C_InitPIN,
Test_C_SetPIN,
Test_C_OpenSession,
Test_C_CloseSession,
Test_C_CloseAllSessions,
Test_C_GetSessionInfo,
Test_C_GetOperationState,
Test_C_SetOperationState,
Test_C_Login,
Test_C_Logout,
Test_C_CreateObject,
Test_C_CopyObject,
Test_C_DestroyObject,
Test_C_GetObjectSize,
Test_C_GetAttributeValue,
Test_C_SetAttributeValue,
Test_C_FindObjectsInit,
Test_C_FindObjects,
Test_C_FindObjectsFinal,
Test_C_EncryptInit,
Test_C_Encrypt,
Test_C_EncryptUpdate,
Test_C_EncryptFinal,
Test_C_DecryptInit,
Test_C_Decrypt,
Test_C_DecryptUpdate,
Test_C_DecryptFinal,
Test_C_DigestInit,
Test_C_Digest,
Test_C_DigestUpdate,
Test_C_DigestKey,
Test_C_DigestFinal,
Test_C_SignInit,
Test_C_Sign,
Test_C_SignUpdate,
Test_C_SignFinal,
Test_C_SignRecoverInit,
Test_C_SignRecover,
Test_C_VerifyInit,
Test_C_Verify,
Test_C_VerifyUpdate,
Test_C_VerifyFinal,
Test_C_VerifyRecoverInit,
Test_C_VerifyRecover,
Test_C_DigestEncryptUpdate,
Test_C_DecryptDigestUpdate,
Test_C_SignEncryptUpdate,
Test_C_DecryptVerifyUpdate,
Test_C_GenerateKey,
Test_C_GenerateKeyPair,
Test_C_WrapKey,
Test_C_UnwrapKey,
Test_C_DeriveKey,
Test_C_SeedRandom,
Test_C_GenerateRandom,
Test_C_GetFunctionStatus,
Test_C_CancelFunction,
Test_C_WaitForSlotEvent};
#ifdef _WIN32
__declspec(dllexport)
#endif
CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) {
*ppFunctionList = &FunctionList;
return CKR_OK;
}

View file

@ -0,0 +1,8 @@
;+NSS_3.48 { # NSS 3.48 release
;+ global:
LIBRARY pkcs11testmodule ;-
EXPORTS ;-
C_GetFunctionList;
;+ local:
;+ *;
;+};

View file

@ -0,0 +1,25 @@
# 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': 'pkcs11testmodule',
'type': 'shared_library',
'sources': [
'pkcs11testmodule.cpp',
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/cpputil/cpputil.gyp:cpputil',
],
}
],
'variables': {
'module': 'nss'
}
}

View file

@ -0,0 +1,60 @@
/* 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 <winver.h>
#define MY_LIBNAME "pkcs11testmodule"
#define MY_FILEDESCRIPTION "NSS PKCS #11 Test Module"
#ifdef _DEBUG
#define MY_DEBUG_STR " (debug)"
#define MY_FILEFLAGS_1 VS_FF_DEBUG
#else
#define MY_DEBUG_STR ""
#define MY_FILEFLAGS_1 0x0L
#endif
#define MY_FILEFLAGS_2 MY_FILEFLAGS_1
#ifdef WINNT
#define MY_FILEOS VOS_NT_WINDOWS32
#else
#define MY_FILEOS VOS__WINDOWS32
#endif
#define MY_INTERNAL_NAME MY_LIBNAME
#define MY_VERSION "0"
/////////////////////////////////////////////////////////////////////////////
//
// Version-information resource
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,0,0,0
PRODUCTVERSION 0,0,0,0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS MY_FILEFLAGS_2
FILEOS MY_FILEOS
FILETYPE VFT_DLL
FILESUBTYPE 0x0L // not used
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0" // Lang=US English, CharSet=Unicode
BEGIN
VALUE "CompanyName", "Mozilla Foundation\0"
VALUE "FileDescription", MY_FILEDESCRIPTION MY_DEBUG_STR "\0"
VALUE "FileVersion", MY_VERSION "\0"
VALUE "InternalName", MY_INTERNAL_NAME "\0"
VALUE "OriginalFilename", MY_INTERNAL_NAME ".dll\0"
VALUE "ProductName", "Network Security Services\0"
VALUE "ProductVersion", MY_VERSION "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

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

@ -6,12 +6,22 @@ CORE_DEPTH = ../..
DEPTH = ../..
MODULE = nss
DEFINES += -DDLL_SUFFIX=\"$(DLL_SUFFIX)\" -DDLL_PREFIX=\"$(DLL_PREFIX)\"
include $(CORE_DEPTH)/coreconf/arch.mk
ifneq ($(OS_ARCH),WINNT)
DB_TESTS = \
softoken_nssckbi_testlib_gtest.cc
endif
CPPSRCS = \
softoken_gtest.cc \
$(DB_TESTS) \
$(NULL)
INCLUDES += \
-I$(CORE_DEPTH)/gtests/google_test/gtest/include \
-I$(CORE_DEPTH)/gtests/common \
-I$(CORE_DEPTH)/cpputil \
$(NULL)

View file

@ -1,104 +1,20 @@
#include <cstdlib>
#if defined(_WIN32)
#include <windows.h>
#include <codecvt>
#endif
#include "cert.h"
#include "certdb.h"
#include "nspr.h"
#include "nss.h"
#include "pk11pub.h"
#include "secmod.h"
#include "secerr.h"
#include "nss_scoped_ptrs.h"
#include "util.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
#include <fstream>
namespace nss_test {
// Given a prefix, attempts to create a unique directory that the user can do
// work in without impacting other tests. For example, if given the prefix
// "scratch", a directory like "scratch05c17b25" will be created in the current
// working directory (or the location specified by NSS_GTEST_WORKDIR, if
// defined).
// Upon destruction, the implementation will attempt to delete the directory.
// However, no attempt is made to first remove files in the directory - the
// user is responsible for this. If the directory is not empty, deleting it will
// fail.
// Statistically, it is technically possible to fail to create a unique
// directory name, but this is extremely unlikely given the expected workload of
// this implementation.
class ScopedUniqueDirectory {
public:
explicit ScopedUniqueDirectory(const std::string &prefix);
// NB: the directory must be empty upon destruction
~ScopedUniqueDirectory() { assert(rmdir(mPath.c_str()) == 0); }
const std::string &GetPath() { return mPath; }
const std::string &GetUTF8Path() { return mUTF8Path; }
private:
static const int RETRY_LIMIT = 5;
static void GenerateRandomName(/*in/out*/ std::string &prefix);
static bool TryMakingDirectory(/*in/out*/ std::string &prefix);
std::string mPath;
std::string mUTF8Path;
};
ScopedUniqueDirectory::ScopedUniqueDirectory(const std::string &prefix) {
std::string path;
const char *workingDirectory = PR_GetEnvSecure("NSS_GTEST_WORKDIR");
if (workingDirectory) {
path.assign(workingDirectory);
}
path.append(prefix);
for (int i = 0; i < RETRY_LIMIT; i++) {
std::string pathCopy(path);
// TryMakingDirectory will modify its input. If it fails, we want to throw
// away the modified result.
if (TryMakingDirectory(pathCopy)) {
mPath.assign(pathCopy);
break;
}
}
assert(mPath.length() > 0);
#if defined(_WIN32)
// sqldb always uses UTF-8 regardless of the current system locale.
DWORD len =
MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), nullptr, 0);
std::vector<wchar_t> buf(len, L'\0');
MultiByteToWideChar(CP_ACP, 0, mPath.data(), mPath.size(), buf.data(),
buf.size());
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
mUTF8Path = converter.to_bytes(std::wstring(buf.begin(), buf.end()));
#else
mUTF8Path = mPath;
#endif
}
void ScopedUniqueDirectory::GenerateRandomName(std::string &prefix) {
std::stringstream ss;
ss << prefix;
// RAND_MAX is at least 32767.
ss << std::setfill('0') << std::setw(4) << std::hex << rand() << rand();
// This will overwrite the value of prefix. This is a little inefficient, but
// at least it makes the code simple.
ss >> prefix;
}
bool ScopedUniqueDirectory::TryMakingDirectory(std::string &prefix) {
GenerateRandomName(prefix);
#if defined(_WIN32)
return _mkdir(prefix.c_str()) == 0;
#else
return mkdir(prefix.c_str(), 0777) == 0;
#endif
}
class SoftokenTest : public ::testing::Test {
protected:
SoftokenTest() : mNSSDBDir("SoftokenTest.d-") {}
@ -205,6 +121,27 @@ TEST_F(SoftokenTest, CreateObjectChangePassword) {
EXPECT_EQ(nullptr, obj);
}
// The size limit for a password is 500 characters as defined in pkcs11i.h
TEST_F(SoftokenTest, CreateObjectChangeToBigPassword) {
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
EXPECT_EQ(
SECSuccess,
PK11_ChangePW(slot.get(), "",
"rUIFIFr2bxKnbJbitsfkyqttpk6vCJzlYMNxcxXcaN37gSZKbLk763X7iR"
"yeVNWZHQ02lSF69HYjzTyPW3318ZD0DBFMMbALZ8ZPZP73CIo5uIQlaowV"
"IbP8eOhRYtGUqoLGlcIFNEYogV8Q3GN58VeBMs0KxrIOvPQ9s8SnYYkqvt"
"zzgntmAvCgvk64x6eQf0okHwegd5wi6m0WVJytEepWXkP9J629FSa5kNT8"
"FvL3jvslkiImzTNuTvl32fQDXXMSc8vVk5Q3mH7trMZM0VDdwHWYERjHbz"
"kGxFgp0VhediHx7p9kkz6H6ac4et9sW4UkTnN7xhYc1Zr17wRSk2heQtcX"
"oZJGwuzhiKm8A8wkuVxms6zO56P4JORIk8oaUW6lyNTLo2kWWnTA"));
EXPECT_EQ(SECSuccess, PK11_Logout(slot.get()));
ScopedPK11GenericObject obj(PK11_CreateGenericObject(
slot.get(), attributes, PR_ARRAY_SIZE(attributes), true));
EXPECT_EQ(nullptr, obj);
}
TEST_F(SoftokenTest, CreateObjectChangeToEmptyPassword) {
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
@ -221,6 +158,76 @@ TEST_F(SoftokenTest, CreateObjectChangeToEmptyPassword) {
EXPECT_NE(nullptr, obj);
}
// We should be able to read CRLF, LF and CR.
// During the Initialization of the NSS Database, is called a function to load
// PKCS11 modules defined in pkcs11.txt. This file is read to get the
// specifications, parse them and load the modules. Here we are ensuring that
// the parsing will work correctly, independent of the breaking line format of
// pkcs11.txt file, which could vary depending where it was created.
// If the parsing is not well interpreted, the database cannot initialize.
TEST_F(SoftokenTest, CreateObjectReadBreakLine) {
const std::string path = mNSSDBDir.GetPath();
const std::string dbname_in = path + "/pkcs11.txt";
const std::string dbname_out_cr = path + "/pkcs11_cr.txt";
const std::string dbname_out_crlf = path + "/pkcs11_crlf.txt";
const std::string dbname_out_lf = path + "/pkcs11_lf.txt";
std::ifstream in(dbname_in);
ASSERT_TRUE(in);
std::ofstream out_cr(dbname_out_cr);
ASSERT_TRUE(out_cr);
std::ofstream out_crlf(dbname_out_crlf);
ASSERT_TRUE(out_crlf);
std::ofstream out_lf(dbname_out_lf);
ASSERT_TRUE(out_lf);
// Database should be correctly initialized by Setup()
ASSERT_TRUE(NSS_IsInitialized());
ASSERT_EQ(SECSuccess, NSS_Shutdown());
// Prepare the file formats with CR, CRLF and LF
for (std::string line; getline(in, line);) {
out_cr << line << "\r";
out_crlf << line << "\r\n";
out_lf << line << "\n";
}
in.close();
out_cr.close();
out_crlf.close();
out_lf.close();
// Change the pkcs11.txt to CR format.
ASSERT_TRUE(!remove(dbname_in.c_str()));
ASSERT_TRUE(!rename(dbname_out_cr.c_str(), dbname_in.c_str()));
// Try to initialize with CR format.
std::string nssInitArg("sql:");
nssInitArg.append(mNSSDBDir.GetUTF8Path());
ASSERT_EQ(SECSuccess, NSS_Initialize(nssInitArg.c_str(), "", "", SECMOD_DB,
NSS_INIT_NOROOTINIT));
ASSERT_TRUE(NSS_IsInitialized());
ASSERT_EQ(SECSuccess, NSS_Shutdown());
// Change the pkcs11.txt to CRLF format.
ASSERT_TRUE(!remove(dbname_in.c_str()));
ASSERT_TRUE(!rename(dbname_out_crlf.c_str(), dbname_in.c_str()));
// Try to initialize with CRLF format.
ASSERT_EQ(SECSuccess, NSS_Initialize(nssInitArg.c_str(), "", "", SECMOD_DB,
NSS_INIT_NOROOTINIT));
ASSERT_TRUE(NSS_IsInitialized());
ASSERT_EQ(SECSuccess, NSS_Shutdown());
// Change the pkcs11.txt to LF format.
ASSERT_TRUE(!remove(dbname_in.c_str()));
ASSERT_TRUE(!rename(dbname_out_lf.c_str(), dbname_in.c_str()));
// Try to initialize with LF format.
ASSERT_EQ(SECSuccess, NSS_Initialize(nssInitArg.c_str(), "", "", SECMOD_DB,
NSS_INIT_NOROOTINIT));
ASSERT_TRUE(NSS_IsInitialized());
}
class SoftokenNonAsciiTest : public SoftokenTest {
protected:
SoftokenNonAsciiTest() : SoftokenTest("SoftokenTest.\xF7-") {}
@ -351,6 +358,100 @@ TEST_F(SoftokenNoDBTest, NeedUserInitNoDB) {
ASSERT_EQ(SECSuccess, NSS_Shutdown());
}
#ifndef NSS_FIPS_DISABLED
class SoftokenFipsTest : public SoftokenTest {
protected:
SoftokenFipsTest() : SoftokenTest("SoftokenFipsTest.d-") {}
virtual void SetUp() {
SoftokenTest::SetUp();
// Turn on FIPS mode (code borrowed from FipsMode in modutil/pk11.c)
char *internal_name;
ASSERT_FALSE(PK11_IsFIPS());
internal_name = PR_smprintf("%s", SECMOD_GetInternalModule()->commonName);
ASSERT_EQ(SECSuccess, SECMOD_DeleteInternalModule(internal_name));
PR_smprintf_free(internal_name);
ASSERT_TRUE(PK11_IsFIPS());
}
};
const std::vector<std::string> kFipsPasswordCases[] = {
// FIPS level1 -> level1 -> level1
{"", "", ""},
// FIPS level1 -> level1 -> level2
{"", "", "strong-_123"},
// FIXME: this should work: FIPS level1 -> level2 -> level2
// {"", "strong-_123", "strong-_456"},
// FIPS level2 -> level2 -> level2
{"strong-_123", "strong-_456", "strong-_123"}};
const std::vector<std::string> kFipsPasswordBadCases[] = {
// FIPS level1 -> level2 -> level1
{"", "strong-_123", ""},
// FIPS level2 -> level1 -> level1
{"strong-_123", ""},
// FIPS level2 -> level2 -> level1
{"strong-_123", "strong-_456", ""},
// initialize with a weak password
{"weak"},
// FIPS level1 -> weak password
{"", "weak"},
// FIPS level2 -> weak password
{"strong-_123", "weak"}};
class SoftokenFipsPasswordTest
: public SoftokenFipsTest,
public ::testing::WithParamInterface<std::vector<std::string>> {};
class SoftokenFipsBadPasswordTest
: public SoftokenFipsTest,
public ::testing::WithParamInterface<std::vector<std::string>> {};
TEST_P(SoftokenFipsPasswordTest, SetPassword) {
const std::vector<std::string> &passwords = GetParam();
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
auto it = passwords.begin();
auto prev_it = it;
EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, (*it).c_str()));
for (it++; it != passwords.end(); it++, prev_it++) {
EXPECT_EQ(SECSuccess,
PK11_ChangePW(slot.get(), (*prev_it).c_str(), (*it).c_str()));
}
}
TEST_P(SoftokenFipsBadPasswordTest, SetBadPassword) {
const std::vector<std::string> &passwords = GetParam();
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
auto it = passwords.begin();
auto prev_it = it;
SECStatus rv = PK11_InitPin(slot.get(), nullptr, (*it).c_str());
if (it + 1 == passwords.end())
EXPECT_EQ(SECFailure, rv);
else
EXPECT_EQ(SECSuccess, rv);
for (it++; it != passwords.end(); it++, prev_it++) {
rv = PK11_ChangePW(slot.get(), (*prev_it).c_str(), (*it).c_str());
if (it + 1 == passwords.end())
EXPECT_EQ(SECFailure, rv);
else
EXPECT_EQ(SECSuccess, rv);
}
}
INSTANTIATE_TEST_CASE_P(FipsPasswordCases, SoftokenFipsPasswordTest,
::testing::ValuesIn(kFipsPasswordCases));
INSTANTIATE_TEST_CASE_P(BadFipsPasswordCases, SoftokenFipsBadPasswordTest,
::testing::ValuesIn(kFipsPasswordBadCases));
#endif
} // namespace nss_test
int main(int argc, char **argv) {

View file

@ -12,6 +12,7 @@
'type': 'executable',
'sources': [
'softoken_gtest.cc',
'softoken_nssckbi_testlib_gtest.cc',
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
@ -19,7 +20,7 @@
'<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
],
'conditions': [
[ 'test_build==1', {
[ 'static_libs==1', {
'dependencies': [
'<(DEPTH)/lib/nss/nss.gyp:nss_static',
'<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
@ -30,6 +31,7 @@
'<(DEPTH)/lib/dev/dev.gyp:nssdev',
'<(DEPTH)/lib/pki/pki.gyp:nsspki',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl',
'<(DEPTH)/lib/libpkix/libpkix.gyp:libpkix',
],
}, {
'dependencies': [
@ -43,6 +45,10 @@
'target_defaults': {
'include_dirs': [
'../../lib/util'
],
'defines': [
'DLL_PREFIX=\"<(dll_prefix)\"',
'DLL_SUFFIX=\"<(dll_suffix)\"'
]
},
'variables': {

View file

@ -0,0 +1,124 @@
#include "cert.h"
#include "certdb.h"
#include "nspr.h"
#include "nss.h"
#include "pk11pub.h"
#include "secerr.h"
#include "nss_scoped_ptrs.h"
#include "util.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
namespace nss_test {
class SoftokenBuiltinsTest : public ::testing::Test {
protected:
SoftokenBuiltinsTest() : nss_db_dir_("SoftokenBuiltinsTest.d-") {}
SoftokenBuiltinsTest(const std::string &prefix) : nss_db_dir_(prefix) {}
virtual void SetUp() {
std::string nss_init_arg("sql:");
nss_init_arg.append(nss_db_dir_.GetUTF8Path());
ASSERT_EQ(SECSuccess, NSS_Initialize(nss_init_arg.c_str(), "", "",
SECMOD_DB, NSS_INIT_NOROOTINIT));
}
virtual void TearDown() {
ASSERT_EQ(SECSuccess, NSS_Shutdown());
const std::string &nss_db_dir_path = nss_db_dir_.GetPath();
ASSERT_EQ(0, unlink((nss_db_dir_path + "/cert9.db").c_str()));
ASSERT_EQ(0, unlink((nss_db_dir_path + "/key4.db").c_str()));
ASSERT_EQ(0, unlink((nss_db_dir_path + "/pkcs11.txt").c_str()));
}
virtual void LoadModule() {
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
EXPECT_EQ(SECSuccess, PK11_InitPin(slot.get(), nullptr, nullptr));
SECStatus result = SECMOD_AddNewModule(
"Builtins-testlib", DLL_PREFIX "nssckbi-testlib." DLL_SUFFIX, 0, 0);
ASSERT_EQ(result, SECSuccess);
}
ScopedUniqueDirectory nss_db_dir_;
};
// The next tests in this class are used to test the Distrust Fields.
// More details about these fields in lib/ckfw/builtins/README.
TEST_F(SoftokenBuiltinsTest, CheckNoDistrustFields) {
const char *kCertNickname =
"Builtin Object Token:Distrust Fields Test - no_distrust";
LoadModule();
CERTCertDBHandle *cert_handle = CERT_GetDefaultCertDB();
ASSERT_TRUE(cert_handle);
ScopedCERTCertificate cert(
CERT_FindCertByNickname(cert_handle, kCertNickname));
ASSERT_TRUE(cert);
EXPECT_EQ(PR_FALSE,
PK11_HasAttributeSet(cert->slot, cert->pkcs11ID,
CKA_NSS_SERVER_DISTRUST_AFTER, PR_FALSE));
EXPECT_EQ(PR_FALSE,
PK11_HasAttributeSet(cert->slot, cert->pkcs11ID,
CKA_NSS_EMAIL_DISTRUST_AFTER, PR_FALSE));
ASSERT_FALSE(cert->distrust);
}
TEST_F(SoftokenBuiltinsTest, CheckOkDistrustFields) {
const char *kCertNickname =
"Builtin Object Token:Distrust Fields Test - ok_distrust";
LoadModule();
CERTCertDBHandle *cert_handle = CERT_GetDefaultCertDB();
ASSERT_TRUE(cert_handle);
ScopedCERTCertificate cert(
CERT_FindCertByNickname(cert_handle, kCertNickname));
ASSERT_TRUE(cert);
const char *kExpectedDERValueServer = "200617000000Z";
const char *kExpectedDERValueEmail = "071014085320Z";
// When a valid timestamp is encoded, the result length is exactly 13.
const unsigned int kDistrustFieldSize = 13;
ASSERT_TRUE(cert->distrust);
ASSERT_EQ(kDistrustFieldSize, cert->distrust->serverDistrustAfter.len);
ASSERT_NE(nullptr, cert->distrust->serverDistrustAfter.data);
EXPECT_TRUE(!memcmp(kExpectedDERValueServer,
cert->distrust->serverDistrustAfter.data,
kDistrustFieldSize));
ASSERT_EQ(kDistrustFieldSize, cert->distrust->emailDistrustAfter.len);
ASSERT_NE(nullptr, cert->distrust->emailDistrustAfter.data);
EXPECT_TRUE(!memcmp(kExpectedDERValueEmail,
cert->distrust->emailDistrustAfter.data,
kDistrustFieldSize));
}
TEST_F(SoftokenBuiltinsTest, CheckInvalidDistrustFields) {
const char *kCertNickname =
"Builtin Object Token:Distrust Fields Test - err_distrust";
LoadModule();
CERTCertDBHandle *cert_handle = CERT_GetDefaultCertDB();
ASSERT_TRUE(cert_handle);
ScopedCERTCertificate cert(
CERT_FindCertByNickname(cert_handle, kCertNickname));
ASSERT_TRUE(cert);
// The field should never be set to TRUE in production, we are just
// testing if this field is readable, even if set to TRUE.
EXPECT_EQ(PR_TRUE,
PK11_HasAttributeSet(cert->slot, cert->pkcs11ID,
CKA_NSS_SERVER_DISTRUST_AFTER, PR_FALSE));
// If something other than CK_BBOOL CK_TRUE, it will be considered FALSE
// Here, there is an OCTAL value, but with unexpected content (1 digit less).
EXPECT_EQ(PR_FALSE,
PK11_HasAttributeSet(cert->slot, cert->pkcs11ID,
CKA_NSS_EMAIL_DISTRUST_AFTER, PR_FALSE));
ASSERT_FALSE(cert->distrust);
}
} // namespace nss_test

View file

@ -36,6 +36,12 @@ CPPSRCS := $(filter-out $(shell grep -l '^TEST_F' $(CPPSRCS)), $(CPPSRCS))
CFLAGS += -DNSS_DISABLE_TLS_1_3
endif
ifdef NSS_ALLOW_SSLKEYLOGFILE
SSLKEYLOGFILE_FILES = ssl_keylog_unittest.cc
else
SSLKEYLOGFILE_FILES = $(NULL)
endif
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
#######################################################################

View file

@ -12,6 +12,48 @@
#include "seccomon.h"
#include "selfencrypt.h"
SECStatus SSLInt_TweakChannelInfoForDC(PRFileDesc *fd, PRBool changeAuthKeyBits,
PRBool changeScheme) {
if (!fd) {
return SECFailure;
}
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}
// Just toggle so we'll always have a valid value.
if (changeScheme) {
ss->sec.signatureScheme = (ss->sec.signatureScheme == ssl_sig_ed25519)
? ssl_sig_ecdsa_secp256r1_sha256
: ssl_sig_ed25519;
}
if (changeAuthKeyBits) {
ss->sec.authKeyBits = ss->sec.authKeyBits ? ss->sec.authKeyBits * 2 : 384;
}
return SECSuccess;
}
SECStatus SSLInt_GetHandshakeRandoms(PRFileDesc *fd, SSL3Random client_random,
SSL3Random server_random) {
if (!fd) {
return SECFailure;
}
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
return SECFailure;
}
if (client_random) {
memcpy(client_random, ss->ssl3.hs.client_random, sizeof(SSL3Random));
}
if (server_random) {
memcpy(server_random, ss->ssl3.hs.server_random, sizeof(SSL3Random));
}
return SECSuccess;
}
SECStatus SSLInt_IncrementClientHandshakeVersion(PRFileDesc *fd) {
sslSocket *ss = ssl_FindSocket(fd);
if (!ss) {
@ -109,9 +151,10 @@ void SSLInt_PrintCipherSpecs(const char *label, PRFileDesc *fd) {
}
}
/* Force a timer expiry by backdating when all active timers were started. We
* could set the remaining time to 0 but then backoff would not work properly if
* we decide to test it. */
/* DTLS timers are separate from the time that the rest of the stack uses.
* Force a timer expiry by backdating when all active timers were started.
* We could set the remaining time to 0 but then backoff would not work properly
* if we decide to test it. */
SECStatus SSLInt_ShiftDtlsTimers(PRFileDesc *fd, PRIntervalTime shift) {
size_t i;
sslSocket *ss = ssl_FindSocket(fd);
@ -297,42 +340,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;
}
SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size) {
sslSocket *ss;
@ -356,20 +363,14 @@ SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size) {
return SECSuccess;
}
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

@ -20,7 +20,8 @@ SECStatus SSLInt_IncrementClientHandshakeVersion(PRFileDesc *fd);
SECStatus SSLInt_UpdateSSLv2ClientRandom(PRFileDesc *fd, uint8_t *rnd,
size_t rnd_len, uint8_t *msg,
size_t msg_len);
SECStatus SSLInt_GetHandshakeRandoms(PRFileDesc *fd, SSL3Random client_random,
SSL3Random server_random);
PRBool SSLInt_ExtensionNegotiated(PRFileDesc *fd, PRUint16 ext);
void SSLInt_ClearSelfEncryptKey();
void SSLInt_SetSelfEncryptMacKey(PK11SymKey *key);
@ -39,18 +40,9 @@ 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);
void SSLInt_SetTicketLifetime(uint32_t lifetime);
SECStatus SSLInt_HasPendingHandshakeData(PRFileDesc *fd, PRBool *pending);
SECStatus SSLInt_SetSocketMaxEarlyDataSize(PRFileDesc *fd, uint32_t size);
void SSLInt_RolloverAntiReplay(void);
SECStatus SSLInt_TweakChannelInfoForDC(PRFileDesc *fd, PRBool changeAuthKeyBits,
PRBool changeScheme);
#endif // ndef libssl_internals_h_

View file

@ -17,9 +17,11 @@ CPPSRCS = \
ssl_agent_unittest.cc \
ssl_auth_unittest.cc \
ssl_cert_ext_unittest.cc \
ssl_cipherorder_unittest.cc \
ssl_ciphersuite_unittest.cc \
ssl_custext_unittest.cc \
ssl_damage_unittest.cc \
ssl_debug_env_unittest.cc \
ssl_dhe_unittest.cc \
ssl_drop_unittest.cc \
ssl_ecdh_unittest.cc \
@ -31,11 +33,12 @@ CPPSRCS = \
ssl_gather_unittest.cc \
ssl_gtest.cc \
ssl_hrr_unittest.cc \
ssl_keylog_unittest.cc \
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 \
@ -52,7 +55,9 @@ CPPSRCS = \
tls_hkdf_unittest.cc \
tls_filter.cc \
tls_protect.cc \
tls_subcerts_unittest.cc \
tls_esni_unittest.cc \
$(SSLKEYLOGFILE_FILES) \
$(NULL)
INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \

View file

@ -45,11 +45,40 @@ TEST_P(TlsConnectTls13, ZeroRttServerRejectByOption) {
SendReceive();
}
TEST_P(TlsConnectTls13, ZeroRttApplicationReject) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
auto reject_0rtt = [](PRBool firstHello, const PRUint8* clientToken,
unsigned int clientTokenLen, PRUint8* appToken,
unsigned int* appTokenLen, unsigned int appTokenMax,
void* arg) {
auto* called = reinterpret_cast<bool*>(arg);
*called = true;
EXPECT_TRUE(firstHello);
EXPECT_EQ(0U, clientTokenLen);
return ssl_hello_retry_reject_0rtt;
};
bool cb_run = false;
EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(server_->ssl_fd(),
reject_0rtt, &cb_run));
ZeroRttSendReceive(true, false);
Handshake();
EXPECT_TRUE(cb_run);
CheckConnected();
SendReceive();
}
TEST_P(TlsConnectTls13, ZeroRttApparentReplayAfterRestart) {
// The test fixtures call SSL_SetupAntiReplay() in SetUp(). This results in
// 0-RTT being rejected until at least one window passes. SetupFor0Rtt()
// forces a rollover of the anti-replay filters, which clears this state.
// Here, we do the setup manually here without that forced rollover.
// The test fixtures enable anti-replay in SetUp(). This results in 0-RTT
// being rejected until at least one window passes. SetupFor0Rtt() forces a
// rollover of the anti-replay filters, which clears that state and allows
// 0-RTT to work. Make the first connection manually to avoid that rollover
// and cause 0-RTT to be rejected.
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
@ -106,7 +135,7 @@ class TlsZeroRttReplayTest : public TlsConnectTls13 {
SendReceive();
if (rollover) {
SSLInt_RolloverAntiReplay();
RolloverAntiReplay();
}
// Now replay that packet against the server.
@ -184,20 +213,21 @@ TEST_P(TlsConnectTls13, ZeroRttServerOnly) {
CheckKeys();
}
// A small sleep after sending the ClientHello means that the ticket age that
// arrives at the server is too low. With a small tolerance for variation in
// ticket age (which is determined by the |window| parameter that is passed to
// SSL_SetupAntiReplay()), the server then rejects early data.
// Advancing time after sending the ClientHello means that the ticket age that
// arrives at the server is too low. The server then rejects early data if this
// delay exceeds half the anti-replay window.
TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
static const PRTime kWindow = 10 * PR_USEC_PER_SEC;
ResetAntiReplay(kWindow);
SetupForZeroRtt();
Reset();
StartConnect();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
EXPECT_EQ(SECSuccess, SSL_SetupAntiReplay(1, 1, 3));
SSLInt_RolloverAntiReplay(); // Make sure to flush replay state.
SSLInt_RolloverAntiReplay();
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, false, []() {
PR_Sleep(PR_MillisecondsToInterval(10));
ZeroRttSendReceive(true, false, [this]() {
AdvanceTime(1 + kWindow / 2);
return true;
});
Handshake();
@ -212,13 +242,15 @@ TEST_P(TlsConnectTls13, ZeroRttRejectOldTicket) {
// small tolerance for variation in ticket age and the ticket will appear to
// arrive prematurely, causing the server to reject early data.
TEST_P(TlsConnectTls13, ZeroRttRejectPrematureTicket) {
static const PRTime kWindow = 10 * PR_USEC_PER_SEC;
ResetAntiReplay(kWindow);
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
server_->Set0RttEnabled(true);
StartConnect();
client_->Handshake(); // ClientHello
server_->Handshake(); // ServerHello
PR_Sleep(PR_MillisecondsToInterval(10));
AdvanceTime(1 + kWindow / 2);
Handshake(); // Remainder of handshake
CheckConnected();
SendReceive();
@ -227,9 +259,6 @@ TEST_P(TlsConnectTls13, ZeroRttRejectPrematureTicket) {
Reset();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
EXPECT_EQ(SECSuccess, SSL_SetupAntiReplay(1, 1, 3));
SSLInt_RolloverAntiReplay(); // Make sure to flush replay state.
SSLInt_RolloverAntiReplay();
ExpectResumption(RESUME_TICKET);
ExpectEarlyDataAccepted(false);
StartConnect();
@ -649,6 +678,351 @@ TEST_P(TlsConnectTls13, ZeroRttOrdering) {
EXPECT_EQ(2U, step);
}
// Early data remains available after the handshake completes for TLS.
TEST_F(TlsConnectStreamTls13, ZeroRttLateReadTls) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
client_->Handshake(); // ClientHello
// Write some early data.
const uint8_t data[] = {1, 2, 3, 4, 5, 6, 7, 8};
PRInt32 rv = PR_Write(client_->ssl_fd(), data, sizeof(data));
EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), rv);
// Consume the ClientHello and generate ServerHello..Finished.
server_->Handshake();
// Read some of the data.
std::vector<uint8_t> small_buffer(1 + sizeof(data) / 2);
rv = PR_Read(server_->ssl_fd(), small_buffer.data(), small_buffer.size());
EXPECT_EQ(static_cast<PRInt32>(small_buffer.size()), rv);
EXPECT_EQ(0, memcmp(data, small_buffer.data(), small_buffer.size()));
Handshake(); // Complete the handshake.
ExpectEarlyDataAccepted(true);
CheckConnected();
// After the handshake, it should be possible to read the remainder.
uint8_t big_buf[100];
rv = PR_Read(server_->ssl_fd(), big_buf, sizeof(big_buf));
EXPECT_EQ(static_cast<PRInt32>(sizeof(data) - small_buffer.size()), rv);
EXPECT_EQ(0, memcmp(&data[small_buffer.size()], big_buf,
sizeof(data) - small_buffer.size()));
// And that's all there is to read.
rv = PR_Read(server_->ssl_fd(), big_buf, sizeof(big_buf));
EXPECT_GT(0, rv);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
}
// Early data that arrives before the handshake can be read after the handshake
// is complete.
TEST_F(TlsConnectDatagram13, ZeroRttLateReadDtls) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
client_->Handshake(); // ClientHello
// Write some early data.
const uint8_t data[] = {1, 2, 3};
PRInt32 written = PR_Write(client_->ssl_fd(), data, sizeof(data));
EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), written);
Handshake(); // Complete the handshake.
ExpectEarlyDataAccepted(true);
CheckConnected();
// Reading at the server should return the early data, which was buffered.
uint8_t buf[sizeof(data) + 1] = {0};
PRInt32 read = PR_Read(server_->ssl_fd(), buf, sizeof(buf));
EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), read);
EXPECT_EQ(0, memcmp(data, buf, sizeof(data)));
}
class PacketHolder : public PacketFilter {
public:
PacketHolder() = default;
virtual Action Filter(const DataBuffer& input, DataBuffer* output) {
packet_ = input;
Disable();
return DROP;
}
const DataBuffer& packet() const { return packet_; }
private:
DataBuffer packet_;
};
// Early data that arrives late is discarded for DTLS.
TEST_F(TlsConnectDatagram13, ZeroRttLateArrivalDtls) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
client_->Handshake(); // ClientHello
// Write some early data. Twice, so that we can read bits of it.
const uint8_t data[] = {1, 2, 3};
PRInt32 written = PR_Write(client_->ssl_fd(), data, sizeof(data));
EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), written);
// Block and capture the next packet.
auto holder = std::make_shared<PacketHolder>();
client_->SetFilter(holder);
written = PR_Write(client_->ssl_fd(), data, sizeof(data));
EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), written);
EXPECT_FALSE(holder->enabled()) << "the filter should disable itself";
// Consume the ClientHello and generate ServerHello..Finished.
server_->Handshake();
// Read some of the data.
std::vector<uint8_t> small_buffer(sizeof(data));
PRInt32 read =
PR_Read(server_->ssl_fd(), small_buffer.data(), small_buffer.size());
EXPECT_EQ(static_cast<PRInt32>(small_buffer.size()), read);
EXPECT_EQ(0, memcmp(data, small_buffer.data(), small_buffer.size()));
Handshake(); // Complete the handshake.
ExpectEarlyDataAccepted(true);
CheckConnected();
server_->SendDirect(holder->packet());
// Reading now should return nothing, even though a valid packet was
// delivered.
read = PR_Read(server_->ssl_fd(), small_buffer.data(), small_buffer.size());
EXPECT_GT(0, read);
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
}
// Early data reads in TLS should be coalesced.
TEST_F(TlsConnectStreamTls13, ZeroRttCoalesceReadTls) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
client_->Handshake(); // ClientHello
// Write some early data. In two writes.
const uint8_t data[] = {1, 2, 3, 4, 5, 6};
PRInt32 written = PR_Write(client_->ssl_fd(), data, 1);
EXPECT_EQ(1, written);
written = PR_Write(client_->ssl_fd(), data + 1, sizeof(data) - 1);
EXPECT_EQ(static_cast<PRInt32>(sizeof(data) - 1), written);
// Consume the ClientHello and generate ServerHello..Finished.
server_->Handshake();
// Read all of the data.
std::vector<uint8_t> buffer(sizeof(data));
PRInt32 read = PR_Read(server_->ssl_fd(), buffer.data(), buffer.size());
EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), read);
EXPECT_EQ(0, memcmp(data, buffer.data(), sizeof(data)));
Handshake(); // Complete the handshake.
ExpectEarlyDataAccepted(true);
CheckConnected();
}
// Early data reads in DTLS should not be coalesced.
TEST_F(TlsConnectDatagram13, ZeroRttNoCoalesceReadDtls) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
client_->Handshake(); // ClientHello
// Write some early data. In two writes.
const uint8_t data[] = {1, 2, 3, 4, 5, 6};
PRInt32 written = PR_Write(client_->ssl_fd(), data, 1);
EXPECT_EQ(1, written);
written = PR_Write(client_->ssl_fd(), data + 1, sizeof(data) - 1);
EXPECT_EQ(static_cast<PRInt32>(sizeof(data) - 1), written);
// Consume the ClientHello and generate ServerHello..Finished.
server_->Handshake();
// Try to read all of the data.
std::vector<uint8_t> buffer(sizeof(data));
PRInt32 read = PR_Read(server_->ssl_fd(), buffer.data(), buffer.size());
EXPECT_EQ(1, read);
EXPECT_EQ(0, memcmp(data, buffer.data(), 1));
// Read the remainder.
read = PR_Read(server_->ssl_fd(), buffer.data(), buffer.size());
EXPECT_EQ(static_cast<PRInt32>(sizeof(data) - 1), read);
EXPECT_EQ(0, memcmp(data + 1, buffer.data(), sizeof(data) - 1));
Handshake(); // Complete the handshake.
ExpectEarlyDataAccepted(true);
CheckConnected();
}
// Early data reads in DTLS should fail if the buffer is too small.
TEST_F(TlsConnectDatagram13, ZeroRttShortReadDtls) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
client_->Handshake(); // ClientHello
// Write some early data. In two writes.
const uint8_t data[] = {1, 2, 3, 4, 5, 6};
PRInt32 written = PR_Write(client_->ssl_fd(), data, sizeof(data));
EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), written);
// Consume the ClientHello and generate ServerHello..Finished.
server_->Handshake();
// Try to read all of the data into a small buffer.
std::vector<uint8_t> buffer(sizeof(data));
PRInt32 read = PR_Read(server_->ssl_fd(), buffer.data(), 1);
EXPECT_GT(0, read);
EXPECT_EQ(SSL_ERROR_RX_SHORT_DTLS_READ, PORT_GetError());
// Read again with more space.
read = PR_Read(server_->ssl_fd(), buffer.data(), buffer.size());
EXPECT_EQ(static_cast<PRInt32>(sizeof(data)), read);
EXPECT_EQ(0, memcmp(data, buffer.data(), sizeof(data)));
Handshake(); // Complete the handshake.
ExpectEarlyDataAccepted(true);
CheckConnected();
}
// There are few ways in which TLS uses the clock and most of those operate on
// timescales that would be ridiculous to wait for in a test. This is the one
// test we have that uses the real clock. It tests that time passes by checking
// that a small sleep results in rejection of early data. 0-RTT has a
// configurable timer, which makes it ideal for this.
TEST_F(TlsConnectStreamTls13, TimePassesByDefault) {
// Calling EnsureTlsSetup() replaces the time function on client and server,
// and sets up anti-replay, which we don't want, so initialize each directly.
client_->EnsureTlsSetup();
server_->EnsureTlsSetup();
// StartConnect() calls EnsureTlsSetup(), so avoid that too.
client_->StartConnect();
server_->StartConnect();
// Set a tiny anti-replay window. This has to be at least 2 milliseconds to
// have any chance of being relevant as that is the smallest window that we
// can detect. Anything smaller rounds to zero.
static const unsigned int kTinyWindowMs = 5;
ResetAntiReplay(static_cast<PRTime>(kTinyWindowMs * PR_USEC_PER_MSEC));
server_->SetAntiReplayContext(anti_replay_);
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
server_->Set0RttEnabled(true);
Handshake();
CheckConnected();
SendReceive(); // Absorb a session ticket.
CheckKeys();
// Clear the first window.
PR_Sleep(PR_MillisecondsToInterval(kTinyWindowMs));
Reset();
client_->EnsureTlsSetup();
server_->EnsureTlsSetup();
client_->StartConnect();
server_->StartConnect();
// Early data is rejected by the server only if time passes for it as well.
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, false, []() {
// Sleep long enough that we minimize the risk of our RTT estimation being
// duped by stutters in test execution. This is very long to allow for
// flaky and low-end hardware, especially what our CI runs on.
PR_Sleep(PR_MillisecondsToInterval(1000));
return true;
});
Handshake();
ExpectEarlyDataAccepted(false);
CheckConnected();
}
// Test that SSL_CreateAntiReplayContext doesn't pass bad inputs.
TEST_F(TlsConnectStreamTls13, BadAntiReplayArgs) {
SSLAntiReplayContext* p;
// Zero or negative window.
EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, -1, 1, 1, &p));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 0, 1, 1, &p));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Zero k.
EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 1, 0, 1, &p));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Zero bits.
EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 1, 1, 0, &p));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(SECFailure, SSL_CreateAntiReplayContext(0, 1, 1, 1, nullptr));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Prove that these parameters do work, even if they are useless..
EXPECT_EQ(SECSuccess, SSL_CreateAntiReplayContext(0, 1, 1, 1, &p));
ASSERT_NE(nullptr, p);
ScopedSSLAntiReplayContext ctx(p);
// The socket isn't a client or server until later, so configuring a client
// should work OK.
client_->EnsureTlsSetup();
EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(client_->ssl_fd(), ctx.get()));
EXPECT_EQ(SECSuccess, SSL_SetAntiReplayContext(client_->ssl_fd(), nullptr));
}
// See also TlsConnectGenericResumption.ResumeServerIncompatibleCipher
TEST_P(TlsConnectTls13, ZeroRttDifferentCompatibleCipher) {
EnsureTlsSetup();
server_->EnableSingleCipher(TLS_AES_128_GCM_SHA256);
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
// Change the ciphersuite. Resumption is OK because the hash is the same, but
// early data will be rejected.
server_->EnableSingleCipher(TLS_CHACHA20_POLY1305_SHA256);
ExpectResumption(RESUME_TICKET);
StartConnect();
ZeroRttSendReceive(true, false);
Handshake();
ExpectEarlyDataAccepted(false);
CheckConnected();
SendReceive();
}
// See also TlsConnectGenericResumption.ResumeServerIncompatibleCipher
TEST_P(TlsConnectTls13, ZeroRttDifferentIncompatibleCipher) {
EnsureTlsSetup();
server_->EnableSingleCipher(TLS_AES_256_GCM_SHA384);
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
// Resumption is rejected because the hash is different.
server_->EnableSingleCipher(TLS_CHACHA20_POLY1305_SHA256);
ExpectResumption(RESUME_NONE);
StartConnect();
ZeroRttSendReceive(true, false);
Handshake();
ExpectEarlyDataAccepted(false);
CheckConnected();
SendReceive();
}
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_TEST_CASE_P(Tls13ZeroRttReplayTest, TlsZeroRttReplayTest,
TlsConnectTestBase::kTlsVariantsAll);

View file

@ -176,14 +176,434 @@ TEST_P(TlsConnectGeneric, ClientAuth) {
CheckKeys();
}
// 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
// then gets an alert from the server. The test harness
// doesn't handle this right yet.
TEST_P(TlsConnectStream, DISABLED_ClientAuthRequiredRejected) {
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()));
ASSERT_NE(nullptr, cert1.get());
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
ASSERT_NE(nullptr, cert2.get());
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()));
ASSERT_NE(nullptr, cert1.get());
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
ASSERT_NE(nullptr, cert2.get());
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()));
ASSERT_NE(nullptr, cert3.get());
ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd()));
ASSERT_NE(nullptr, cert4.get());
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, PostHandshakeAuthBeforeKeyUpdate) {
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());
// Send KeyUpdate.
EXPECT_EQ(SECFailure, SSL_KeyUpdate(server_->ssl_fd(), PR_TRUE));
EXPECT_EQ(PR_WOULD_BLOCK_ERROR, PORT_GetError());
}
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthDuringClientKeyUpdate) {
client_->SetupClientAuth();
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
Connect();
CheckEpochs(3, 3);
// Send CertificateRequest from server.
EXPECT_EQ(SECSuccess, SSL_SendCertificateRequest(server_->ssl_fd()))
<< "Unexpected error: " << PORT_ErrorToName(PORT_GetError());
// Send KeyUpdate from client.
EXPECT_EQ(SECSuccess, SSL_KeyUpdate(client_->ssl_fd(), PR_TRUE));
server_->SendData(50); // server sends CertificateRequest
client_->SendData(50); // client sends KeyUpdate
server_->ReadBytes(50); // server receives KeyUpdate and defers response
CheckEpochs(4, 3);
client_->ReadBytes(50); // client receives CertificateRequest
client_->SendData(
50); // client sends Certificate, CertificateVerify, Finished
server_->ReadBytes(
50); // server receives Certificate, CertificateVerify, Finished
client_->CheckEpochs(3, 4);
server_->CheckEpochs(4, 4);
server_->SendData(50); // server sends KeyUpdate
client_->ReadBytes(50); // client receives KeyUpdate
client_->CheckEpochs(4, 4);
}
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);
ConnectExpectFail();
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()));
ASSERT_NE(nullptr, cert1.get());
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
ASSERT_NE(nullptr, cert2.get());
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()));
ASSERT_NE(nullptr, cert3.get());
ScopedCERTCertificate cert4(SSL_LocalCertificate(client_->ssl_fd()));
ASSERT_NE(nullptr, cert4.get());
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));
EXPECT_EQ(SECSuccess,
SSL_OptionSet(server_->ssl_fd(), SSL_REQUIRE_CERTIFICATE,
SSL_REQUIRE_ALWAYS));
// 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); // send Certificate Request
client_->ReadBytes(50); // read Certificate Request
client_->SendData(50); // send empty Certificate+Finished
server_->ExpectSendAlert(kTlsAlertCertificateRequired);
server_->ReadBytes(50); // read empty Certificate+Finished
server_->ExpectReadWriteError();
server_->SendData(50); // send alert
// 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()));
}
// Check if post-handshake auth still works when session tickets are enabled:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1553443
TEST_F(TlsConnectStreamTls13, PostHandshakeAuthWithSessionTicketsEnabled) {
EnsureTlsSetup();
client_->SetupClientAuth();
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_POST_HANDSHAKE_AUTH, PR_TRUE));
EXPECT_EQ(SECSuccess, SSL_OptionSet(client_->ssl_fd(),
SSL_ENABLE_SESSION_TICKETS, PR_TRUE));
EXPECT_EQ(SECSuccess, SSL_OptionSet(server_->ssl_fd(),
SSL_ENABLE_SESSION_TICKETS, PR_TRUE));
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_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(1U, called);
ScopedCERTCertificate cert1(SSL_PeerCertificate(server_->ssl_fd()));
ASSERT_NE(nullptr, cert1.get());
ScopedCERTCertificate cert2(SSL_LocalCertificate(client_->ssl_fd()));
ASSERT_NE(nullptr, cert2.get());
EXPECT_TRUE(SECITEM_ItemsAreEqual(&cert1->derCert, &cert2->derCert));
}
TEST_P(TlsConnectGenericPre13, ClientAuthRequiredRejected) {
server_->RequestClientAuth(true);
ConnectExpectAlert(server_, kTlsAlertBadCertificate);
client_->CheckErrorCode(SSL_ERROR_BAD_CERT_ALERT);
server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE);
}
// In TLS 1.3, the client will claim that the connection is done and then
// receive the alert afterwards. So drive the handshake manually.
TEST_P(TlsConnectTls13, ClientAuthRequiredRejected) {
server_->RequestClientAuth(true);
StartConnect();
client_->Handshake(); // CH
server_->Handshake(); // SH.. (no resumption)
client_->Handshake(); // Next message
ASSERT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
ExpectAlert(server_, kTlsAlertCertificateRequired);
server_->Handshake(); // Alert
server_->CheckErrorCode(SSL_ERROR_NO_CERTIFICATE);
client_->Handshake(); // Receive Alert
client_->CheckErrorCode(SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT);
}
TEST_P(TlsConnectGeneric, ClientAuthRequestedRejected) {
@ -219,7 +639,9 @@ static void CheckSigScheme(std::shared_ptr<TlsHandshakeRecorder>& capture,
EXPECT_EQ(expected_scheme, static_cast<uint16_t>(scheme));
ScopedCERTCertificate remote_cert(SSL_PeerCertificate(peer->ssl_fd()));
ASSERT_NE(nullptr, remote_cert.get());
ScopedSECKEYPublicKey remote_key(CERT_ExtractPublicKey(remote_cert.get()));
ASSERT_NE(nullptr, remote_key.get());
EXPECT_EQ(expected_size, SECKEY_PublicKeyStrengthInBits(remote_key.get()));
}
@ -273,9 +695,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,
@ -342,6 +762,59 @@ TEST_P(TlsConnectTls12, ClientAuthInconsistentPssSignatureScheme) {
ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
}
TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureScheme) {
static const SSLSignatureScheme kSignatureScheme[] = {
ssl_sig_rsa_pkcs1_sha256, ssl_sig_rsa_pss_rsae_sha256};
Reset(TlsAgent::kServerRsa, "rsa");
client_->SetSignatureSchemes(kSignatureScheme,
PR_ARRAY_SIZE(kSignatureScheme));
server_->SetSignatureSchemes(kSignatureScheme,
PR_ARRAY_SIZE(kSignatureScheme));
client_->SetupClientAuth();
server_->RequestClientAuth(true);
auto capture_cert_verify = MakeTlsFilter<TlsHandshakeRecorder>(
client_, kTlsHandshakeCertificateVerify);
capture_cert_verify->EnableDecryption();
Connect();
CheckSigScheme(capture_cert_verify, 0, server_, ssl_sig_rsa_pss_rsae_sha256,
1024);
}
// Client should refuse to connect without a usable signature scheme.
TEST_P(TlsConnectTls13, ClientAuthPkcs1SignatureSchemeOnly) {
static const SSLSignatureScheme kSignatureScheme[] = {
ssl_sig_rsa_pkcs1_sha256};
Reset(TlsAgent::kServerRsa, "rsa");
client_->SetSignatureSchemes(kSignatureScheme,
PR_ARRAY_SIZE(kSignatureScheme));
client_->SetupClientAuth();
client_->StartConnect();
client_->Handshake();
EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
client_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
}
// Though the client has a usable signature scheme, when a certificate is
// requested, it can't produce one.
TEST_P(TlsConnectTls13, ClientAuthPkcs1AndEcdsaScheme) {
static const SSLSignatureScheme kSignatureScheme[] = {
ssl_sig_rsa_pkcs1_sha256, ssl_sig_ecdsa_secp256r1_sha256};
Reset(TlsAgent::kServerRsa, "rsa");
client_->SetSignatureSchemes(kSignatureScheme,
PR_ARRAY_SIZE(kSignatureScheme));
client_->SetupClientAuth();
server_->RequestClientAuth(true);
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
}
class TlsZeroCertificateRequestSigAlgsFilter : public TlsHandshakeFilter {
public:
TlsZeroCertificateRequestSigAlgsFilter(const std::shared_ptr<TlsAgent>& a)
@ -552,7 +1025,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,15 +1038,16 @@ 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);
client_->CheckErrorCode(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
}
TEST_P(TlsConnectTls12Plus, RequestClientAuthWithSha384) {
TEST_P(TlsConnectTls12, RequestClientAuthWithSha384) {
server_->SetSignatureSchemes(kSignatureSchemeRsaSha384,
PR_ARRAY_SIZE(kSignatureSchemeRsaSha384));
server_->RequestClientAuth(false);
@ -888,11 +1364,11 @@ TEST_P(TlsConnectGeneric, AuthFailImmediate) {
}
static const SSLExtraServerCertData ServerCertDataRsaPkcs1Decrypt = {
ssl_auth_rsa_decrypt, nullptr, nullptr, nullptr};
ssl_auth_rsa_decrypt, nullptr, nullptr, nullptr, nullptr, nullptr};
static const SSLExtraServerCertData ServerCertDataRsaPkcs1Sign = {
ssl_auth_rsa_sign, nullptr, nullptr, nullptr};
ssl_auth_rsa_sign, nullptr, nullptr, nullptr, nullptr, nullptr};
static const SSLExtraServerCertData ServerCertDataRsaPss = {
ssl_auth_rsa_pss, nullptr, nullptr, nullptr};
ssl_auth_rsa_pss, nullptr, nullptr, nullptr, nullptr, nullptr};
// Test RSA cert with usage=[signature, encipherment].
TEST_F(TlsAgentStreamTestServer, ConfigureCertRsaPkcs1SignAndKEX) {
@ -972,6 +1448,109 @@ TEST_F(TlsAgentStreamTestServer, ConfigureCertRsaPss) {
&ServerCertDataRsaPss));
}
// A server should refuse to even start a handshake with
// misconfigured certificate and signature scheme.
TEST_P(TlsConnectTls12Plus, MisconfiguredCertScheme) {
Reset(TlsAgent::kServerDsa);
static const SSLSignatureScheme kScheme[] = {ssl_sig_ecdsa_secp256r1_sha256};
server_->SetSignatureSchemes(kScheme, PR_ARRAY_SIZE(kScheme));
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
if (version_ < SSL_LIBRARY_VERSION_TLS_1_3) {
// TLS 1.2 disables cipher suites, which leads to a different error.
server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
} else {
server_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
}
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
}
// In TLS 1.2, disabling an EC group causes ECDSA to be invalid.
TEST_P(TlsConnectTls12, Tls12CertDisabledGroup) {
Reset(TlsAgent::kServerEcdsa256);
static const std::vector<SSLNamedGroup> k25519 = {ssl_grp_ec_curve25519};
server_->ConfigNamedGroups(k25519);
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
server_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
}
// In TLS 1.3, ECDSA configuration only depends on the signature scheme.
TEST_P(TlsConnectTls13, Tls13CertDisabledGroup) {
Reset(TlsAgent::kServerEcdsa256);
static const std::vector<SSLNamedGroup> k25519 = {ssl_grp_ec_curve25519};
server_->ConfigNamedGroups(k25519);
Connect();
}
// A client should refuse to even start a handshake with only DSA.
TEST_P(TlsConnectTls13, Tls13DsaOnlyClient) {
static const SSLSignatureScheme kDsa[] = {ssl_sig_dsa_sha256};
client_->SetSignatureSchemes(kDsa, PR_ARRAY_SIZE(kDsa));
client_->StartConnect();
client_->Handshake();
EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
client_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
}
TEST_P(TlsConnectTls13, Tls13DsaOnlyServer) {
Reset(TlsAgent::kServerDsa);
static const SSLSignatureScheme kDsa[] = {ssl_sig_dsa_sha256};
server_->SetSignatureSchemes(kDsa, PR_ARRAY_SIZE(kDsa));
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
server_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
}
TEST_P(TlsConnectTls13, Tls13Pkcs1OnlyClient) {
static const SSLSignatureScheme kPkcs1[] = {ssl_sig_rsa_pkcs1_sha256};
client_->SetSignatureSchemes(kPkcs1, PR_ARRAY_SIZE(kPkcs1));
client_->StartConnect();
client_->Handshake();
EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
client_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
}
TEST_P(TlsConnectTls13, Tls13Pkcs1OnlyServer) {
static const SSLSignatureScheme kPkcs1[] = {ssl_sig_rsa_pkcs1_sha256};
server_->SetSignatureSchemes(kPkcs1, PR_ARRAY_SIZE(kPkcs1));
ConnectExpectAlert(server_, kTlsAlertHandshakeFailure);
server_->CheckErrorCode(SSL_ERROR_NO_SUPPORTED_SIGNATURE_ALGORITHM);
client_->CheckErrorCode(SSL_ERROR_NO_CYPHER_OVERLAP);
}
TEST_P(TlsConnectTls13, Tls13DsaIsNotAdvertisedClient) {
EnsureTlsSetup();
static const SSLSignatureScheme kSchemes[] = {ssl_sig_dsa_sha256,
ssl_sig_rsa_pss_rsae_sha256};
client_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
auto capture =
MakeTlsFilter<TlsExtensionCapture>(client_, ssl_signature_algorithms_xtn);
Connect();
// We should only have the one signature algorithm advertised.
static const uint8_t kExpectedExt[] = {0, 2, ssl_sig_rsa_pss_rsae_sha256 >> 8,
ssl_sig_rsa_pss_rsae_sha256 & 0xff};
ASSERT_EQ(DataBuffer(kExpectedExt, sizeof(kExpectedExt)),
capture->extension());
}
TEST_P(TlsConnectTls13, Tls13DsaIsNotAdvertisedServer) {
EnsureTlsSetup();
static const SSLSignatureScheme kSchemes[] = {ssl_sig_dsa_sha256,
ssl_sig_rsa_pss_rsae_sha256};
server_->SetSignatureSchemes(kSchemes, PR_ARRAY_SIZE(kSchemes));
auto capture = MakeTlsFilter<TlsExtensionCapture>(
server_, ssl_signature_algorithms_xtn, true);
capture->SetHandshakeTypes({kTlsHandshakeCertificateRequest});
capture->EnableDecryption();
server_->RequestClientAuth(false); // So we get a CertificateRequest.
Connect();
// We should only have the one signature algorithm advertised.
static const uint8_t kExpectedExt[] = {0, 2, ssl_sig_rsa_pss_rsae_sha256 >> 8,
ssl_sig_rsa_pss_rsae_sha256 & 0xff};
ASSERT_EQ(DataBuffer(kExpectedExt, sizeof(kExpectedExt)),
capture->extension());
}
// variant, version, certificate, auth type, signature scheme
typedef std::tuple<SSLProtocolVariant, uint16_t, std::string, SSLAuthType,
SSLSignatureScheme>
@ -1033,12 +1612,21 @@ TEST_P(TlsSignatureSchemeConfiguration, SignatureSchemeConfigBoth) {
INSTANTIATE_TEST_CASE_P(
SignatureSchemeRsa, TlsSignatureSchemeConfiguration,
::testing::Combine(
TlsConnectTestBase::kTlsVariantsAll, TlsConnectTestBase::kTlsV12Plus,
TlsConnectTestBase::kTlsVariantsAll, TlsConnectTestBase::kTlsV12,
::testing::Values(TlsAgent::kServerRsaSign),
::testing::Values(ssl_auth_rsa_sign),
::testing::Values(ssl_sig_rsa_pkcs1_sha256, ssl_sig_rsa_pkcs1_sha384,
ssl_sig_rsa_pkcs1_sha512, ssl_sig_rsa_pss_rsae_sha256,
ssl_sig_rsa_pss_rsae_sha384)));
// RSASSA-PKCS1-v1_5 is not allowed to be used in TLS 1.3
INSTANTIATE_TEST_CASE_P(
SignatureSchemeRsaTls13, TlsSignatureSchemeConfiguration,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV13,
::testing::Values(TlsAgent::kServerRsaSign),
::testing::Values(ssl_auth_rsa_sign),
::testing::Values(ssl_sig_rsa_pss_rsae_sha256,
ssl_sig_rsa_pss_rsae_sha384)));
// PSS with SHA-512 needs a bigger key to work.
INSTANTIATE_TEST_CASE_P(
SignatureSchemeBigRsa, TlsSignatureSchemeConfiguration,

View file

@ -43,10 +43,10 @@ class SignedCertificateTimestampsExtractor {
}
void assertTimestamps(const DataBuffer& timestamps) {
EXPECT_TRUE(auth_timestamps_);
ASSERT_NE(nullptr, auth_timestamps_);
EXPECT_EQ(timestamps, *auth_timestamps_);
EXPECT_TRUE(handshake_timestamps_);
ASSERT_NE(nullptr, handshake_timestamps_);
EXPECT_EQ(timestamps, *handshake_timestamps_);
const SECItem* current =
@ -64,8 +64,8 @@ static const uint8_t kSctValue[] = {0x01, 0x23, 0x45, 0x67, 0x89};
static const SECItem kSctItem = {siBuffer, const_cast<uint8_t*>(kSctValue),
sizeof(kSctValue)};
static const DataBuffer kSctBuffer(kSctValue, sizeof(kSctValue));
static const SSLExtraServerCertData kExtraSctData = {ssl_auth_null, nullptr,
nullptr, &kSctItem};
static const SSLExtraServerCertData kExtraSctData = {
ssl_auth_null, nullptr, nullptr, &kSctItem, nullptr, nullptr};
// Test timestamps extraction during a successful handshake.
TEST_P(TlsConnectGenericPre13, SignedCertificateTimestampsLegacy) {
@ -147,8 +147,8 @@ static const SECItem kOcspItems[] = {
{siBuffer, const_cast<uint8_t*>(kOcspValue2), sizeof(kOcspValue2)}};
static const SECItemArray kOcspResponses = {const_cast<SECItem*>(kOcspItems),
PR_ARRAY_SIZE(kOcspItems)};
const static SSLExtraServerCertData kOcspExtraData = {ssl_auth_null, nullptr,
&kOcspResponses, nullptr};
const static SSLExtraServerCertData kOcspExtraData = {
ssl_auth_null, nullptr, &kOcspResponses, nullptr, nullptr, nullptr};
TEST_P(TlsConnectGeneric, NoOcsp) {
EnsureTlsSetup();
@ -224,7 +224,7 @@ TEST_P(TlsConnectGeneric, OcspHugeSuccess) {
const SECItemArray hugeOcspResponses = {const_cast<SECItem*>(hugeOcspItems),
PR_ARRAY_SIZE(hugeOcspItems)};
const SSLExtraServerCertData hugeOcspExtraData = {
ssl_auth_null, nullptr, &hugeOcspResponses, nullptr};
ssl_auth_null, nullptr, &hugeOcspResponses, nullptr, nullptr, nullptr};
// The value should be available during the AuthCertificateCallback
client_->SetAuthCertificateCallback([&](TlsAgent* agent, bool checksig,

View file

@ -0,0 +1,241 @@
/* -*- 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 "ssl.h"
#include "sslerr.h"
#include "sslproto.h"
#include <memory>
#include "tls_connect.h"
#include "tls_filter.h"
namespace nss_test {
class TlsCipherOrderTest : public TlsConnectTestBase {
protected:
virtual void ConfigureTLS() {
EnsureTlsSetup();
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
}
virtual SECStatus BuildTestLists(std::vector<uint16_t> &cs_initial_list,
std::vector<uint16_t> &cs_new_list) {
// This is the current CipherSuites order of enabled CipherSuites as defined
// in ssl3con.c
const PRUint16 *kCipherSuites = SSL_GetImplementedCiphers();
for (unsigned int i = 0; i < kNumImplementedCiphers; i++) {
PRBool pref = PR_FALSE, policy = PR_FALSE;
SECStatus rv;
rv = SSL_CipherPolicyGet(kCipherSuites[i], &policy);
if (rv != SECSuccess) {
return SECFailure;
}
rv = SSL_CipherPrefGetDefault(kCipherSuites[i], &pref);
if (rv != SECSuccess) {
return SECFailure;
}
if (pref && policy) {
cs_initial_list.push_back(kCipherSuites[i]);
}
}
// We will test set function with the first 15 enabled ciphers.
const PRUint16 kNumCiphersToSet = 15;
for (unsigned int i = 0; i < kNumCiphersToSet; i++) {
cs_new_list.push_back(cs_initial_list[i]);
}
cs_new_list[0] = cs_initial_list[1];
cs_new_list[1] = cs_initial_list[0];
return SECSuccess;
}
public:
TlsCipherOrderTest() : TlsConnectTestBase(ssl_variant_stream, 0) {}
const unsigned int kNumImplementedCiphers = SSL_GetNumImplementedCiphers();
};
const PRUint16 kCSUnsupported[] = {20196, 10101};
const PRUint16 kNumCSUnsupported = PR_ARRAY_SIZE(kCSUnsupported);
const PRUint16 kCSEmpty[] = {0};
// Get the active CipherSuites odered as they were compiled
TEST_F(TlsCipherOrderTest, CipherOrderGet) {
std::vector<uint16_t> initial_cs_order;
std::vector<uint16_t> new_cs_order;
SECStatus result = BuildTestLists(initial_cs_order, new_cs_order);
ASSERT_EQ(result, SECSuccess);
ConfigureTLS();
std::vector<uint16_t> current_cs_order(SSL_GetNumImplementedCiphers() + 1);
unsigned int current_num_active_cs = 0;
result = SSL_CipherSuiteOrderGet(client_->ssl_fd(), current_cs_order.data(),
&current_num_active_cs);
ASSERT_EQ(result, SECSuccess);
ASSERT_EQ(current_num_active_cs, initial_cs_order.size());
for (unsigned int i = 0; i < initial_cs_order.size(); i++) {
EXPECT_EQ(initial_cs_order[i], current_cs_order[i]);
}
// Get the chosen CipherSuite during the Handshake without any modification.
Connect();
SSLChannelInfo channel;
result = SSL_GetChannelInfo(client_->ssl_fd(), &channel, sizeof channel);
ASSERT_EQ(result, SECSuccess);
EXPECT_EQ(channel.cipherSuite, initial_cs_order[0]);
}
// The "server" used for gtests honor only its ciphersuites order.
// So, we apply the new set for the server instead of client.
// This is enough to test the effect of SSL_CipherSuiteOrderSet function.
TEST_F(TlsCipherOrderTest, CipherOrderSet) {
std::vector<uint16_t> initial_cs_order;
std::vector<uint16_t> new_cs_order;
SECStatus result = BuildTestLists(initial_cs_order, new_cs_order);
ASSERT_EQ(result, SECSuccess);
ConfigureTLS();
// change the server_ ciphersuites order.
result = SSL_CipherSuiteOrderSet(server_->ssl_fd(), new_cs_order.data(),
new_cs_order.size());
ASSERT_EQ(result, SECSuccess);
// The function expect an array. We are using vector for VStudio
// compatibility.
std::vector<uint16_t> current_cs_order(SSL_GetNumImplementedCiphers() + 1);
unsigned int current_num_active_cs = 0;
result = SSL_CipherSuiteOrderGet(server_->ssl_fd(), current_cs_order.data(),
&current_num_active_cs);
ASSERT_EQ(result, SECSuccess);
ASSERT_EQ(current_num_active_cs, new_cs_order.size());
for (unsigned int i = 0; i < new_cs_order.size(); i++) {
ASSERT_EQ(new_cs_order[i], current_cs_order[i]);
}
Connect();
SSLChannelInfo channel;
// changes in server_ order reflect in client chosen ciphersuite.
result = SSL_GetChannelInfo(client_->ssl_fd(), &channel, sizeof channel);
ASSERT_EQ(result, SECSuccess);
EXPECT_EQ(channel.cipherSuite, new_cs_order[0]);
}
// Duplicate socket configuration from a model.
TEST_F(TlsCipherOrderTest, CipherOrderCopySocket) {
std::vector<uint16_t> initial_cs_order;
std::vector<uint16_t> new_cs_order;
SECStatus result = BuildTestLists(initial_cs_order, new_cs_order);
ASSERT_EQ(result, SECSuccess);
ConfigureTLS();
// Use the existing sockets for this test.
result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), new_cs_order.data(),
new_cs_order.size());
ASSERT_EQ(result, SECSuccess);
std::vector<uint16_t> current_cs_order(SSL_GetNumImplementedCiphers() + 1);
unsigned int current_num_active_cs = 0;
result = SSL_CipherSuiteOrderGet(server_->ssl_fd(), current_cs_order.data(),
&current_num_active_cs);
ASSERT_EQ(result, SECSuccess);
ASSERT_EQ(current_num_active_cs, initial_cs_order.size());
for (unsigned int i = 0; i < current_num_active_cs; i++) {
ASSERT_EQ(initial_cs_order[i], current_cs_order[i]);
}
// Import/Duplicate configurations from client_ to server_
PRFileDesc *rv = SSL_ImportFD(client_->ssl_fd(), server_->ssl_fd());
EXPECT_NE(nullptr, rv);
result = SSL_CipherSuiteOrderGet(server_->ssl_fd(), current_cs_order.data(),
&current_num_active_cs);
ASSERT_EQ(result, SECSuccess);
ASSERT_EQ(current_num_active_cs, new_cs_order.size());
for (unsigned int i = 0; i < new_cs_order.size(); i++) {
EXPECT_EQ(new_cs_order.data()[i], current_cs_order[i]);
}
}
// If the infomed num of elements is lower than the actual list size, only the
// first "informed num" elements will be considered. The rest is ignored.
TEST_F(TlsCipherOrderTest, CipherOrderSetLower) {
std::vector<uint16_t> initial_cs_order;
std::vector<uint16_t> new_cs_order;
SECStatus result = BuildTestLists(initial_cs_order, new_cs_order);
ASSERT_EQ(result, SECSuccess);
ConfigureTLS();
result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), new_cs_order.data(),
new_cs_order.size() - 1);
ASSERT_EQ(result, SECSuccess);
std::vector<uint16_t> current_cs_order(SSL_GetNumImplementedCiphers() + 1);
unsigned int current_num_active_cs = 0;
result = SSL_CipherSuiteOrderGet(client_->ssl_fd(), current_cs_order.data(),
&current_num_active_cs);
ASSERT_EQ(result, SECSuccess);
ASSERT_EQ(current_num_active_cs, new_cs_order.size() - 1);
for (unsigned int i = 0; i < new_cs_order.size() - 1; i++) {
ASSERT_EQ(new_cs_order.data()[i], current_cs_order[i]);
}
}
// Testing Errors Controls
TEST_F(TlsCipherOrderTest, CipherOrderSetControls) {
std::vector<uint16_t> initial_cs_order;
std::vector<uint16_t> new_cs_order;
SECStatus result = BuildTestLists(initial_cs_order, new_cs_order);
ASSERT_EQ(result, SECSuccess);
ConfigureTLS();
// Create a new vector with diplicated entries
std::vector<uint16_t> repeated_cs_order(SSL_GetNumImplementedCiphers() + 1);
std::copy(initial_cs_order.begin(), initial_cs_order.end(),
repeated_cs_order.begin());
repeated_cs_order[0] = repeated_cs_order[1];
// Repeated ciphersuites in the list
result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), repeated_cs_order.data(),
initial_cs_order.size());
EXPECT_EQ(result, SECFailure);
// Zero size for the sent list
result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), new_cs_order.data(), 0);
EXPECT_EQ(result, SECFailure);
// Wrong size, greater than actual
result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), new_cs_order.data(),
SSL_GetNumImplementedCiphers() + 1);
EXPECT_EQ(result, SECFailure);
// Wrong ciphersuites, not implemented
result = SSL_CipherSuiteOrderSet(client_->ssl_fd(), kCSUnsupported,
kNumCSUnsupported);
EXPECT_EQ(result, SECFailure);
// Null list
result =
SSL_CipherSuiteOrderSet(client_->ssl_fd(), nullptr, new_cs_order.size());
EXPECT_EQ(result, SECFailure);
// Empty list
result =
SSL_CipherSuiteOrderSet(client_->ssl_fd(), kCSEmpty, new_cs_order.size());
EXPECT_EQ(result, SECFailure);
// Confirm that the controls are working, as the current ciphersuites
// remained untouched
std::vector<uint16_t> current_cs_order(SSL_GetNumImplementedCiphers() + 1);
unsigned int current_num_active_cs = 0;
result = SSL_CipherSuiteOrderGet(client_->ssl_fd(), current_cs_order.data(),
&current_num_active_cs);
ASSERT_EQ(result, SECSuccess);
ASSERT_EQ(current_num_active_cs, initial_cs_order.size());
for (unsigned int i = 0; i < initial_cs_order.size(); i++) {
ASSERT_EQ(initial_cs_order[i], current_cs_order[i]);
}
}
} // namespace nss_test

View file

@ -56,6 +56,9 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
std::vector<SSLNamedGroup> groups = {group_};
if (cert_group_ != ssl_grp_none) {
groups.push_back(cert_group_);
}
client_->ConfigNamedGroups(groups);
server_->ConfigNamedGroups(groups);
kea_type_ = SSLInt_GetKEAType(group_);
@ -68,41 +71,48 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
virtual void SetupCertificate() {
if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) {
switch (sig_scheme_) {
case ssl_sig_rsa_pkcs1_sha256:
case ssl_sig_rsa_pkcs1_sha384:
case ssl_sig_rsa_pkcs1_sha512:
case ssl_sig_rsa_pss_rsae_sha256:
std::cerr << "Signature scheme: rsa_pss_rsae_sha256" << std::endl;
Reset(TlsAgent::kServerRsaSign);
auth_type_ = ssl_auth_rsa_sign;
break;
case ssl_sig_rsa_pss_rsae_sha256:
case ssl_sig_rsa_pss_rsae_sha384:
std::cerr << "Signature scheme: rsa_pss_rsae_sha384" << std::endl;
Reset(TlsAgent::kServerRsaSign);
auth_type_ = ssl_auth_rsa_sign;
break;
case ssl_sig_rsa_pss_rsae_sha512:
// You can't fit SHA-512 PSS in a 1024-bit key.
std::cerr << "Signature scheme: rsa_pss_rsae_sha512" << std::endl;
Reset(TlsAgent::kRsa2048);
auth_type_ = ssl_auth_rsa_sign;
break;
case ssl_sig_rsa_pss_pss_sha256:
std::cerr << "Signature scheme: rsa_pss_pss_sha256" << std::endl;
Reset(TlsAgent::kServerRsaPss);
auth_type_ = ssl_auth_rsa_pss;
break;
case ssl_sig_rsa_pss_pss_sha384:
std::cerr << "Signature scheme: rsa_pss_pss_sha384" << std::endl;
Reset("rsa_pss384");
auth_type_ = ssl_auth_rsa_pss;
break;
case ssl_sig_rsa_pss_pss_sha512:
std::cerr << "Signature scheme: rsa_pss_pss_sha512" << std::endl;
Reset("rsa_pss512");
auth_type_ = ssl_auth_rsa_pss;
break;
case ssl_sig_ecdsa_secp256r1_sha256:
std::cerr << "Signature scheme: ecdsa_secp256r1_sha256" << std::endl;
Reset(TlsAgent::kServerEcdsa256);
auth_type_ = ssl_auth_ecdsa;
cert_group_ = ssl_grp_ec_secp256r1;
break;
case ssl_sig_ecdsa_secp384r1_sha384:
std::cerr << "Signature scheme: ecdsa_secp384r1_sha384" << std::endl;
Reset(TlsAgent::kServerEcdsa384);
auth_type_ = ssl_auth_ecdsa;
cert_group_ = ssl_grp_ec_secp384r1;
break;
default:
ADD_FAILURE() << "Unsupported signature scheme: " << sig_scheme_;
@ -118,9 +128,11 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
break;
case ssl_auth_ecdsa:
Reset(TlsAgent::kServerEcdsa256);
cert_group_ = ssl_grp_ec_secp256r1;
break;
case ssl_auth_ecdh_ecdsa:
Reset(TlsAgent::kServerEcdhEcdsa);
cert_group_ = ssl_grp_ec_secp256r1;
break;
case ssl_auth_ecdh_rsa:
Reset(TlsAgent::kServerEcdhRsa);
@ -198,6 +210,7 @@ class TlsCipherSuiteTestBase : public TlsConnectTestBase {
SSLAuthType auth_type_;
SSLKEAType kea_type_;
SSLNamedGroup group_;
SSLNamedGroup cert_group_ = ssl_grp_none;
SSLSignatureScheme sig_scheme_;
SSLCipherSuiteInfo csinfo_;
};
@ -330,6 +343,12 @@ static SSLSignatureScheme kSignatureSchemesParamsArr[] = {
ssl_sig_rsa_pss_pss_sha256, ssl_sig_rsa_pss_pss_sha384,
ssl_sig_rsa_pss_pss_sha512};
static SSLSignatureScheme kSignatureSchemesParamsArrTls13[] = {
ssl_sig_ecdsa_secp256r1_sha256, ssl_sig_ecdsa_secp384r1_sha384,
ssl_sig_rsa_pss_rsae_sha256, ssl_sig_rsa_pss_rsae_sha384,
ssl_sig_rsa_pss_rsae_sha512, ssl_sig_rsa_pss_pss_sha256,
ssl_sig_rsa_pss_pss_sha384, ssl_sig_rsa_pss_pss_sha512};
INSTANTIATE_CIPHER_TEST_P(RC4, Stream, V10ToV12, kDummyNamedGroupParams,
kDummySignatureSchemesParams,
TLS_RSA_WITH_RC4_128_SHA,
@ -394,7 +413,7 @@ INSTANTIATE_CIPHER_TEST_P(
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_CIPHER_TEST_P(TLS13, All, V13,
::testing::ValuesIn(kFasterDHEGroups),
::testing::ValuesIn(kSignatureSchemesParamsArr),
::testing::ValuesIn(kSignatureSchemesParamsArrTls13),
TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256,
TLS_AES_256_GCM_SHA384);
INSTANTIATE_CIPHER_TEST_P(TLS13AllGroups, All, V13,

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

@ -0,0 +1,53 @@
/* -*- 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 <cstdlib>
#include <fstream>
#include <sstream>
#include "gtest_utils.h"
#include "tls_connect.h"
namespace nss_test {
extern "C" {
extern FILE* ssl_trace_iob;
#ifdef NSS_ALLOW_SSLKEYLOGFILE
extern FILE* ssl_keylog_iob;
#endif
}
// These tests ensure that when the associated environment variables are unset
// that the lazily-initialized defaults are what they are supposed to be.
#ifdef DEBUG
TEST_P(TlsConnectGeneric, DebugEnvTraceFileNotSet) {
char* ev = PR_GetEnvSecure("SSLDEBUGFILE");
if (ev && ev[0]) {
// note: should use GTEST_SKIP when GTest gets updated to support it
return;
}
Connect();
EXPECT_EQ(stderr, ssl_trace_iob);
}
#endif
#ifdef NSS_ALLOW_SSLKEYLOGFILE
TEST_P(TlsConnectGeneric, DebugEnvKeylogFileNotSet) {
char* ev = PR_GetEnvSecure("SSLKEYLOGFILE");
if (ev && ev[0]) {
// note: should use GTEST_SKIP when GTest gets updated to support it
return;
}
Connect();
EXPECT_EQ(nullptr, ssl_keylog_iob);
}
#endif
} // namespace nss_test

View file

@ -682,4 +682,100 @@ TEST_P(TlsConnectTls12, ConnectInconsistentSigAlgDHE) {
ConnectExpectAlert(client_, kTlsAlertIllegalParameter);
}
static void CheckSkeSigScheme(
std::shared_ptr<TlsHandshakeRecorder>& capture_ske,
uint16_t expected_scheme) {
TlsParser parser(capture_ske->buffer());
EXPECT_TRUE(parser.SkipVariable(2)) << " read dh_p";
EXPECT_TRUE(parser.SkipVariable(2)) << " read dh_q";
EXPECT_TRUE(parser.SkipVariable(2)) << " read dh_Ys";
uint32_t tmp;
EXPECT_TRUE(parser.Read(&tmp, 2)) << " read sig_scheme";
EXPECT_EQ(expected_scheme, static_cast<uint16_t>(tmp));
}
TEST_P(TlsConnectTls12, ConnectSigAlgEnabledByPolicyDhe) {
EnableOnlyDheCiphers();
const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1,
ssl_sig_rsa_pkcs1_sha384};
EnsureTlsSetup();
client_->SetSignatureSchemes(schemes.data(), schemes.size());
server_->SetSignatureSchemes(schemes.data(), schemes.size());
auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeServerKeyExchange);
StartConnect();
client_->Handshake(); // Send ClientHello
// Enable SHA-1 by policy.
SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 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);
Handshake(); // Remainder of handshake
// The server should now report that it is connected
EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha1);
}
TEST_P(TlsConnectTls12, ConnectSigAlgDisabledByPolicyDhe) {
EnableOnlyDheCiphers();
const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1,
ssl_sig_rsa_pkcs1_sha384};
EnsureTlsSetup();
client_->SetSignatureSchemes(schemes.data(), schemes.size());
server_->SetSignatureSchemes(schemes.data(), schemes.size());
auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeServerKeyExchange);
StartConnect();
client_->Handshake(); // Send ClientHello
// Disable SHA-1 by policy after sending ClientHello so that CH
// includes SHA-1 signature scheme.
SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 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);
Handshake(); // Remainder of handshake
// The server should now report that it is connected
EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha384);
}
TEST_P(TlsConnectPre12, ConnectSigAlgDisabledByPolicyDhePre12) {
EnableOnlyDheCiphers();
EnsureTlsSetup();
StartConnect();
client_->Handshake(); // Send ClientHello
// Disable SHA-1 by policy. This will cause the connection fail as
// TLS 1.1 or earlier uses combined SHA-1 + MD5 signature.
SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 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);
server_->ExpectSendAlert(kTlsAlertHandshakeFailure);
client_->ExpectReceiveAlert(kTlsAlertHandshakeFailure);
// Remainder of handshake
Handshake();
server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
}
} // namespace nss_test

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

@ -666,6 +666,80 @@ TEST_P(TlsConnectTls12, ConnectIncorrectSigAlg) {
client_->CheckErrorCode(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM);
}
static void CheckSkeSigScheme(
std::shared_ptr<TlsHandshakeRecorder> &capture_ske,
uint16_t expected_scheme) {
TlsParser parser(capture_ske->buffer());
uint32_t tmp = 0;
EXPECT_TRUE(parser.Read(&tmp, 1)) << " read curve_type";
EXPECT_EQ(3U, tmp) << "curve type has to be 3";
EXPECT_TRUE(parser.Skip(2)) << " read namedcurve";
EXPECT_TRUE(parser.SkipVariable(1)) << " read public";
EXPECT_TRUE(parser.Read(&tmp, 2)) << " read sig_scheme";
EXPECT_EQ(expected_scheme, static_cast<uint16_t>(tmp));
}
TEST_P(TlsConnectTls12, ConnectSigAlgEnabledByPolicy) {
EnsureTlsSetup();
client_->DisableAllCiphers();
client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1,
ssl_sig_rsa_pkcs1_sha384};
client_->SetSignatureSchemes(schemes.data(), schemes.size());
server_->SetSignatureSchemes(schemes.data(), schemes.size());
auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeServerKeyExchange);
StartConnect();
client_->Handshake(); // Send ClientHello
// Enable SHA-1 by policy.
SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 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);
Handshake(); // Remainder of handshake
// The server should now report that it is connected
EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha1);
}
TEST_P(TlsConnectTls12, ConnectSigAlgDisabledByPolicy) {
EnsureTlsSetup();
client_->DisableAllCiphers();
client_->EnableCiphersByKeyExchange(ssl_kea_ecdh);
const std::vector<SSLSignatureScheme> schemes = {ssl_sig_rsa_pkcs1_sha1,
ssl_sig_rsa_pkcs1_sha384};
client_->SetSignatureSchemes(schemes.data(), schemes.size());
server_->SetSignatureSchemes(schemes.data(), schemes.size());
auto capture_ske = MakeTlsFilter<TlsHandshakeRecorder>(
server_, kTlsHandshakeServerKeyExchange);
StartConnect();
client_->Handshake(); // Send ClientHello
// Disable SHA-1 by policy.
SECStatus rv = NSS_SetAlgorithmPolicy(SEC_OID_SHA1, 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);
Handshake(); // Remainder of handshake
// The server should now report that it is connected
EXPECT_EQ(TlsAgent::STATE_CONNECTED, server_->state());
CheckSkeSigScheme(capture_ske, ssl_sig_rsa_pkcs1_sha384);
}
INSTANTIATE_TEST_CASE_P(KeyExchangeTest, TlsKeyExchangeTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsAll,
TlsConnectTestBase::kTlsV11Plus));

View file

@ -436,14 +436,14 @@ TEST_P(TlsExtensionTest12Plus, SignatureAlgorithmsOddLength) {
}
TEST_F(TlsExtensionTest13Stream, SignatureAlgorithmsPrecedingGarbage) {
// 31 unknown signature algorithms followed by sha-256, rsa
// 31 unknown signature algorithms followed by sha-256, rsa-pss
const uint8_t val[] = {
0x00, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x01};
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x04};
DataBuffer extension(val, sizeof(val));
MakeTlsFilter<TlsExtensionReplacer>(client_, ssl_signature_algorithms_xtn,
extension);
@ -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_NO_CYPHER_OVERLAP);
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

@ -22,7 +22,7 @@ namespace nss_test {
const uint8_t kShortEmptyFinished[8] = {0};
const uint8_t kLongEmptyFinished[128] = {0};
class TlsFuzzTest : public ::testing::Test {};
class TlsFuzzTest : public TlsConnectGeneric {};
// Record the application data stream.
class TlsApplicationDataRecorder : public TlsRecordFilter {
@ -46,16 +46,9 @@ class TlsApplicationDataRecorder : public TlsRecordFilter {
DataBuffer buffer_;
};
// Ensure that ssl_Time() returns a constant value.
FUZZ_F(TlsFuzzTest, SSL_Time_Constant) {
PRUint32 now = ssl_TimeSec();
PR_Sleep(PR_SecondsToInterval(2));
EXPECT_EQ(ssl_TimeSec(), now);
}
// Check that due to the deterministic PRNG we derive
// the same master secret in two consecutive TLS sessions.
FUZZ_P(TlsConnectGeneric, DeterministicExporter) {
FUZZ_P(TlsFuzzTest, DeterministicExporter) {
const char kLabel[] = "label";
std::vector<unsigned char> out1(32), out2(32);
@ -95,7 +88,7 @@ FUZZ_P(TlsConnectGeneric, DeterministicExporter) {
// Check that due to the deterministic RNG two consecutive
// TLS sessions will have the exact same transcript.
FUZZ_P(TlsConnectGeneric, DeterministicTranscript) {
FUZZ_P(TlsFuzzTest, DeterministicTranscript) {
// Make sure we have RSA blinding params.
Connect();
@ -130,9 +123,7 @@ FUZZ_P(TlsConnectGeneric, DeterministicTranscript) {
// with all supported TLS versions, STREAM and DGRAM.
// Check that records are NOT encrypted.
// Check that records don't have a MAC.
FUZZ_P(TlsConnectGeneric, ConnectSendReceive_NullCipher) {
EnsureTlsSetup();
FUZZ_P(TlsFuzzTest, ConnectSendReceive_NullCipher) {
// Set up app data filters.
auto client_recorder = MakeTlsFilter<TlsApplicationDataRecorder>(client_);
auto server_recorder = MakeTlsFilter<TlsApplicationDataRecorder>(server_);
@ -157,7 +148,7 @@ FUZZ_P(TlsConnectGeneric, ConnectSendReceive_NullCipher) {
}
// Check that an invalid Finished message doesn't abort the connection.
FUZZ_P(TlsConnectGeneric, BogusClientFinished) {
FUZZ_P(TlsFuzzTest, BogusClientFinished) {
EnsureTlsSetup();
MakeTlsFilter<TlsInspectorReplaceHandshakeMessage>(
@ -168,7 +159,7 @@ FUZZ_P(TlsConnectGeneric, BogusClientFinished) {
}
// Check that an invalid Finished message doesn't abort the connection.
FUZZ_P(TlsConnectGeneric, BogusServerFinished) {
FUZZ_P(TlsFuzzTest, BogusServerFinished) {
EnsureTlsSetup();
MakeTlsFilter<TlsInspectorReplaceHandshakeMessage>(
@ -179,7 +170,7 @@ FUZZ_P(TlsConnectGeneric, BogusServerFinished) {
}
// Check that an invalid server auth signature doesn't abort the connection.
FUZZ_P(TlsConnectGeneric, BogusServerAuthSignature) {
FUZZ_P(TlsFuzzTest, BogusServerAuthSignature) {
EnsureTlsSetup();
uint8_t msg_type = version_ == SSL_LIBRARY_VERSION_TLS_1_3
? kTlsHandshakeCertificateVerify
@ -190,7 +181,7 @@ FUZZ_P(TlsConnectGeneric, BogusServerAuthSignature) {
}
// Check that an invalid client auth signature doesn't abort the connection.
FUZZ_P(TlsConnectGeneric, BogusClientAuthSignature) {
FUZZ_P(TlsFuzzTest, BogusClientAuthSignature) {
EnsureTlsSetup();
client_->SetupClientAuth();
server_->RequestClientAuth(true);
@ -199,7 +190,7 @@ FUZZ_P(TlsConnectGeneric, BogusClientAuthSignature) {
}
// Check that session ticket resumption works.
FUZZ_P(TlsConnectGeneric, SessionTicketResumption) {
FUZZ_P(TlsFuzzTest, SessionTicketResumption) {
ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
Connect();
SendReceive();
@ -212,7 +203,7 @@ FUZZ_P(TlsConnectGeneric, SessionTicketResumption) {
}
// Check that session tickets are not encrypted.
FUZZ_P(TlsConnectGeneric, UnencryptedSessionTickets) {
FUZZ_P(TlsFuzzTest, UnencryptedSessionTickets) {
ConfigureSessionCache(RESUME_TICKET, RESUME_TICKET);
auto filter = MakeTlsFilter<TlsHandshakeRecorder>(
@ -220,23 +211,45 @@ FUZZ_P(TlsConnectGeneric, UnencryptedSessionTickets) {
Connect();
std::cerr << "ticket" << filter->buffer() << std::endl;
size_t offset = 4; /* lifetime */
size_t offset = 4; // Skip lifetime.
if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
offset += 4; /* ticket_age_add */
offset += 4; // Skip ticket_age_add.
uint32_t nonce_len = 0;
EXPECT_TRUE(filter->buffer().Read(offset, 1, &nonce_len));
offset += 1 + nonce_len;
}
offset += 2 + /* ticket length */
2; /* TLS_EX_SESS_TICKET_VERSION */
offset += 2; // Skip the ticket length.
// This bit parses the contents of the ticket, which would ordinarily be
// encrypted. Start by checking that we have the right version. This needs
// to be updated every time that TLS_EX_SESS_TICKET_VERSION is changed. But
// we don't use the #define. That way, any time that code is updated, this
// test will fail unless it is manually checked.
uint32_t ticket_version;
EXPECT_TRUE(filter->buffer().Read(offset, 2, &ticket_version));
EXPECT_EQ(0x010aU, ticket_version);
offset += 2;
// Check the protocol version number.
uint32_t tls_version = 0;
EXPECT_TRUE(filter->buffer().Read(offset, sizeof(version_), &tls_version));
EXPECT_EQ(version_, static_cast<decltype(version_)>(tls_version));
offset += sizeof(version_);
// Check the cipher suite.
uint32_t suite = 0;
EXPECT_TRUE(filter->buffer().Read(offset + sizeof(version_), 2, &suite));
EXPECT_TRUE(filter->buffer().Read(offset, 2, &suite));
client_->CheckCipherSuite(static_cast<uint16_t>(suite));
}
}
INSTANTIATE_TEST_CASE_P(
FuzzStream, TlsFuzzTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsVAll));
INSTANTIATE_TEST_CASE_P(
FuzzDatagram, TlsFuzzTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11Plus));
} // namespace nss_test

View file

@ -18,9 +18,11 @@
'ssl_agent_unittest.cc',
'ssl_auth_unittest.cc',
'ssl_cert_ext_unittest.cc',
'ssl_cipherorder_unittest.cc',
'ssl_ciphersuite_unittest.cc',
'ssl_custext_unittest.cc',
'ssl_damage_unittest.cc',
'ssl_debug_env_unittest.cc',
'ssl_dhe_unittest.cc',
'ssl_drop_unittest.cc',
'ssl_ecdh_unittest.cc',
@ -32,11 +34,12 @@
'ssl_gather_unittest.cc',
'ssl_gtest.cc',
'ssl_hrr_unittest.cc',
'ssl_keylog_unittest.cc',
'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',
@ -52,7 +55,8 @@
'tls_filter.cc',
'tls_hkdf_unittest.cc',
'tls_esni_unittest.cc',
'tls_protect.cc'
'tls_protect.cc',
'tls_subcerts_unittest.cc'
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
@ -74,7 +78,7 @@
'<(DEPTH)/lib/libpkix/libpkix.gyp:libpkix',
],
'conditions': [
[ 'test_build==1', {
[ 'static_libs==1', {
'dependencies': [
'<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
],
@ -91,6 +95,14 @@
'<(DEPTH)/lib/dbm/src/src.gyp:dbm',
],
}],
[ 'enable_sslkeylogfile==1 and sanitizer_flags==0', {
'sources': [
'ssl_keylog_unittest.cc',
],
'defines': [
'NSS_ALLOW_SSLKEYLOGFILE',
],
}],
],
}
],

View file

@ -4,8 +4,6 @@
* 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/. */
#ifdef NSS_ALLOW_SSLKEYLOGFILE
#include <cstdlib>
#include <fstream>
#include <sstream>
@ -15,20 +13,59 @@
namespace nss_test {
static const std::string keylog_file_path = "keylog.txt";
static const std::string keylog_env = "SSLKEYLOGFILE=" + keylog_file_path;
static const std::string kKeylogFilePath = "keylog.txt";
static const std::string kKeylogBlankEnv = "SSLKEYLOGFILE=";
static const std::string kKeylogSetEnv = kKeylogBlankEnv + kKeylogFilePath;
extern "C" {
extern FILE* ssl_keylog_iob;
}
class KeyLogFileTestBase : public TlsConnectGeneric {
private:
std::string env_to_set_;
class KeyLogFileTest : public TlsConnectGeneric {
public:
virtual void CheckKeyLog() = 0;
KeyLogFileTestBase(std::string env) : env_to_set_(env) {}
void SetUp() override {
TlsConnectGeneric::SetUp();
// Remove previous results (if any).
(void)remove(keylog_file_path.c_str());
PR_SetEnv(keylog_env.c_str());
(void)remove(kKeylogFilePath.c_str());
PR_SetEnv(env_to_set_.c_str());
}
void CheckKeyLog() {
std::ifstream f(keylog_file_path);
void ConnectAndCheck() {
// This is a child process, ensure that error messages immediately
// propagate or else it will not be visible.
::testing::GTEST_FLAG(throw_on_failure) = true;
if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, true);
Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
} else {
Connect();
}
CheckKeyLog();
_exit(0);
}
};
class KeyLogFileTest : public KeyLogFileTestBase {
public:
KeyLogFileTest() : KeyLogFileTestBase(kKeylogSetEnv) {}
void CheckKeyLog() override {
std::ifstream f(kKeylogFilePath);
std::map<std::string, size_t> labels;
std::set<std::string> client_randoms;
for (std::string line; std::getline(f, line);) {
@ -65,28 +102,6 @@ class KeyLogFileTest : public TlsConnectGeneric {
ASSERT_EQ(4U, labels["EXPORTER_SECRET"]);
}
}
void ConnectAndCheck() {
// This is a child process, ensure that error messages immediately
// propagate or else it will not be visible.
::testing::GTEST_FLAG(throw_on_failure) = true;
if (version_ == SSL_LIBRARY_VERSION_TLS_1_3) {
SetupForZeroRtt();
client_->Set0RttEnabled(true);
server_->Set0RttEnabled(true);
ExpectResumption(RESUME_TICKET);
ZeroRttSendReceive(true, true);
Handshake();
ExpectEarlyDataAccepted(true);
CheckConnected();
SendReceive();
} else {
Connect();
}
CheckKeyLog();
_exit(0);
}
};
// Tests are run in a separate process to ensure that NSS is not initialized yet
@ -113,6 +128,37 @@ INSTANTIATE_TEST_CASE_P(
TlsConnectTestBase::kTlsV13));
#endif
} // namespace nss_test
class KeyLogFileUnsetTest : public KeyLogFileTestBase {
public:
KeyLogFileUnsetTest() : KeyLogFileTestBase(kKeylogBlankEnv) {}
#endif // NSS_ALLOW_SSLKEYLOGFILE
void CheckKeyLog() override {
std::ifstream f(kKeylogFilePath);
EXPECT_FALSE(f.good());
EXPECT_EQ(nullptr, ssl_keylog_iob);
}
};
TEST_P(KeyLogFileUnsetTest, KeyLogFile) {
testing::GTEST_FLAG(death_test_style) = "threadsafe";
ASSERT_EXIT(ConnectAndCheck(), ::testing::ExitedWithCode(0), "");
}
INSTANTIATE_TEST_CASE_P(
KeyLogFileDTLS12, KeyLogFileUnsetTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsDatagram,
TlsConnectTestBase::kTlsV11V12));
INSTANTIATE_TEST_CASE_P(
KeyLogFileTLS12, KeyLogFileUnsetTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV10ToV12));
#ifndef NSS_DISABLE_TLS_1_3
INSTANTIATE_TEST_CASE_P(
KeyLogFileTLS13, KeyLogFileUnsetTest,
::testing::Combine(TlsConnectTestBase::kTlsVariantsStream,
TlsConnectTestBase::kTlsV13));
#endif
} // namespace nss_test

View file

@ -33,6 +33,37 @@ TEST_F(TlsConnectTest, KeyUpdateClient) {
CheckEpochs(4, 3);
}
TEST_F(TlsConnectStreamTls13, KeyUpdateTooEarly_Client) {
StartConnect();
auto filter = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>(
server_, kTlsHandshakeFinished, kTlsHandshakeKeyUpdate);
filter->EnableDecryption();
client_->Handshake();
server_->Handshake();
ExpectAlert(client_, kTlsAlertUnexpectedMessage);
client_->Handshake();
client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE);
server_->Handshake();
server_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
}
TEST_F(TlsConnectStreamTls13, KeyUpdateTooEarly_Server) {
StartConnect();
auto filter = MakeTlsFilter<TlsEncryptedHandshakeMessageReplacer>(
client_, kTlsHandshakeFinished, kTlsHandshakeKeyUpdate);
filter->EnableDecryption();
client_->Handshake();
server_->Handshake();
client_->Handshake();
ExpectAlert(server_, kTlsAlertUnexpectedMessage);
server_->Handshake();
server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE);
client_->Handshake();
client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
}
TEST_F(TlsConnectTest, KeyUpdateClientRequestUpdate) {
ConfigureVersion(SSL_LIBRARY_VERSION_TLS_1_3);
Connect();

View file

@ -0,0 +1,218 @@
/* -*- 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);
ASSERT_LT(0U, 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 = nullptr;
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

@ -205,6 +205,42 @@ TEST_F(TlsConnectDatagram13, ShortHeadersServer) {
SendReceive();
}
TEST_F(TlsConnectStreamTls13, UnencryptedFinishedMessage) {
StartConnect();
client_->Handshake(); // Send ClientHello
server_->Handshake(); // Send first server flight
// Record and drop the first record, which is the Finished.
auto recorder = std::make_shared<TlsRecordRecorder>(client_);
recorder->EnableDecryption();
auto dropper = std::make_shared<SelectiveDropFilter>(1);
client_->SetFilter(std::make_shared<ChainedPacketFilter>(
ChainedPacketFilterInit({recorder, dropper})));
client_->Handshake(); // Save and drop CFIN.
EXPECT_EQ(TlsAgent::STATE_CONNECTED, client_->state());
ASSERT_EQ(1U, recorder->count());
auto& finished = recorder->record(0);
DataBuffer d;
size_t offset = d.Write(0, ssl_ct_handshake, 1);
offset = d.Write(offset, SSL_LIBRARY_VERSION_TLS_1_2, 2);
offset = d.Write(offset, finished.buffer.len(), 2);
d.Append(finished.buffer);
client_->SendDirect(d);
// Now process the message.
ExpectAlert(server_, kTlsAlertUnexpectedMessage);
// The server should generate an alert.
server_->Handshake();
EXPECT_EQ(TlsAgent::STATE_ERROR, server_->state());
server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE);
// Have the client consume the alert.
client_->Handshake();
EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
client_->CheckErrorCode(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
}
const static size_t kContentSizesArr[] = {
1, kMacSize - 1, kMacSize, 30, 31, 32, 36, 256, 257, 287, 288};

View file

@ -0,0 +1,577 @@
/* -*- 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());
EXPECT_TRUE(dummy_layer_);
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);
@ -222,14 +230,15 @@ TEST_P(TlsConnectTls13, RecordSizePlaintextExceed) {
// Tweak the ciphertext of server records so that they greatly exceed the limit.
// This requires a much larger expansion than for plaintext to trigger the
// guard, which runs before decryption (current allowance is 304 octets).
// guard, which runs before decryption (current allowance is 320 octets,
// see MAX_EXPANSION in ssl3con.c).
TEST_P(TlsConnectTls13, RecordSizeCiphertextExceed) {
EnsureTlsSetup();
client_->SetOption(SSL_RECORD_SIZE_LIMIT, 64);
Connect();
auto server_expand = MakeTlsFilter<TlsRecordExpander>(server_, 320);
auto server_expand = MakeTlsFilter<TlsRecordExpander>(server_, 336);
server_->SendData(100);
client_->ExpectReadWriteError();
@ -256,9 +265,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 +278,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 +345,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 +365,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 +408,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() {

Some files were not shown because too many files have changed in this diff Show more