/* -*- 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>
#if !
defined (_WIN32) &&
defined (__LP64__)
#include <cstring>
#include <sys/mman.h>
#endif
#include "gtest/gtest.h"
#include "der_encode.h"
#include "scoped_ptrs_smime.h"
#include "secoid.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[] = {
0 x30,
0 x82,
0 x30,
0 x30,
0 x06,
0 x09,
0 x2A,
0 x86,
0 x48,
0 x86,
0 xF7,
0 x0D,
0 x01,
0 x07,
0 x02,
0 xA0,
0 x82,
0 x02,
0 x30,
0 x30,
0 x30,
0 x02,
0 x01,
0 x30,
0 x31,
0 x0F,
0 x30,
0 x0D,
0 x06,
0 x09,
0 x30,
0 x30,
0 x30,
0 x30,
0 x30,
0 x30,
0 x30,
0 x30,
0 x30,
0 x30,
0 x00,
0 x30,
0 x0B,
0 x06,
0 x09,
0 x2A,
0 x86,
0 x48,
0 x86,
0 xF7,
0 x0D,
0 x01,
0 x07,
0 x05};
// secp256r1 signature with no certs and no attrs
static unsigned char kValidSignature[] = {
0 x30,
0 x81,
0 xFE,
0 x06,
0 x09,
0 x2A,
0 x86,
0 x48,
0 x86,
0 xF7,
0 x0D,
0 x01,
0 x07,
0 x02,
0 xA0,
0 x81,
0 xF0,
0 x30,
0 x81,
0 xED,
0 x02,
0 x01,
0 x01,
0 x31,
0 x0F,
0 x30,
0 x0D,
0 x06,
0 x09,
0 x60,
0 x86,
0 x48,
0 x01,
0 x65,
0 x03,
0 x04,
0 x02,
0 x01,
0 x05,
0 x00,
0 x30,
0 x0B,
0 x06,
0 x09,
0 x2A,
0 x86,
0 x48,
0 x86,
0 xF7,
0 x0D,
0 x01,
0 x07,
0 x01,
0 x31,
0 x81,
0 xC9,
0 x30,
0 x81,
0 xC6,
0 x02,
0 x01,
0 x01,
0 x30,
0 x5D,
0 x30,
0 x45,
0 x31,
0 x0B,
0 x30,
0 x09,
0 x06,
0 x03,
0 x55,
0 x04,
0 x06,
0 x13,
0 x02,
0 x41,
0 x55,
0 x31,
0 x13,
0 x30,
0 x11,
0 x06,
0 x03,
0 x55,
0 x04,
0 x08,
0 x0C,
0 x0A,
0 x53,
0 x6F,
0 x6D,
0 x65,
0 x2D,
0 x53,
0 x74,
0 x61,
0 x74,
0 x65,
0 x31,
0 x21,
0 x30,
0 x1F,
0 x06,
0 x03,
0 x55,
0 x04,
0 x0A,
0 x0C,
0 x18,
0 x49,
0 x6E,
0 x74,
0 x65,
0 x72,
0 x6E,
0 x65,
0 x74,
0 x20,
0 x57,
0 x69,
0 x64,
0 x67,
0 x69,
0 x74,
0 x73,
0 x20,
0 x50,
0 x74,
0 x79,
0 x20,
0 x4C,
0 x74,
0 x64,
0 x02,
0 x14,
0 x6B,
0 x22,
0 xCA,
0 x91,
0 xE0,
0 x71,
0 x97,
0 xEB,
0 x45,
0 x0D,
0 x68,
0 xC0,
0 xD4,
0 xB6,
0 xE9,
0 x45,
0 x38,
0 x4C,
0 xDD,
0 xA3,
0 x30,
0 x0D,
0 x06,
0 x09,
0 x60,
0 x86,
0 x48,
0 x01,
0 x65,
0 x03,
0 x04,
0 x02,
0 x01,
0 x05,
0 x00,
0 x30,
0 x0A,
0 x06,
0 x08,
0 x2A,
0 x86,
0 x48,
0 xCE,
0 x3D,
0 x04,
0 x03,
0 x02,
0 x04,
0 x47,
0 x30,
0 x45,
0 x02,
0 x20,
0 x48,
0 xEB,
0 xE6,
0 xBA,
0 xFC,
0 xFD,
0 x83,
0 xB3,
0 xA2,
0 xB5,
0 x59,
0 x35,
0 x0C,
0 xA1,
0 x31,
0 x0E,
0 x2F,
0 xE3,
0 x8D,
0 x81,
0 xD8,
0 xF5,
0 x33,
0 xE4,
0 x83,
0 x87,
0 xB1,
0 xFD,
0 x43,
0 x9D,
0 x95,
0 x7D,
0 x02,
0 x21,
0 x00,
0 xD0,
0 x05,
0 x0E,
0 x05,
0 xA6,
0 x80,
0 x3C,
0 x1A,
0 xFE,
0 x51,
0 xFC,
0 x4D,
0 x1A,
0 x25,
0 x05,
0 x78,
0 xB5,
0 x42,
0 xF5,
0 xDE,
0 x4E,
0 x8A,
0 xF8,
0 xE3,
0 xD8,
0 x52,
0 xDC,
0 x2B,
0 x73,
0 x80,
0 x4A,
0 x1A};
// See bug 1507135; this is a CMS signature that contains only the OID
static unsigned char kTruncatedSignature[] = {
0 x30,
0 x0B,
0 x06,
0 x09,
0 x2A,
0 x86,
0 x48,
0 x86,
0 xF7,
0 x0D,
0 x01,
0 x07,
0 x02};
// secp256r1 signature that's truncated by one byte.
static unsigned char kSlightlyTruncatedSignature[] = {
0 x30,
0 x81,
0 xFE,
0 x06,
0 x09,
0 x2A,
0 x86,
0 x48,
0 x86,
0 xF7,
0 x0D,
0 x01,
0 x07,
0 x02,
0 xA0,
0 x81,
0 xF0,
0 x30,
0 x81,
0 xED,
0 x02,
0 x01,
0 x01,
0 x31,
0 x0F,
0 x30,
0 x0D,
0 x06,
0 x09,
0 x60,
0 x86,
0 x48,
0 x01,
0 x65,
0 x03,
0 x04,
0 x02,
0 x01,
0 x05,
0 x00,
0 x30,
0 x0B,
0 x06,
0 x09,
0 x2A,
0 x86,
0 x48,
0 x86,
0 xF7,
0 x0D,
0 x01,
0 x07,
0 x01,
0 x31,
0 x81,
0 xC9,
0 x30,
0 x81,
0 xC6,
0 x02,
0 x01,
0 x01,
0 x30,
0 x5D,
0 x30,
0 x45,
0 x31,
0 x0B,
0 x30,
0 x09,
0 x06,
0 x03,
0 x55,
0 x04,
0 x06,
0 x13,
0 x02,
0 x41,
0 x55,
0 x31,
0 x13,
0 x30,
0 x11,
0 x06,
0 x03,
0 x55,
0 x04,
0 x08,
0 x0C,
0 x0A,
0 x53,
0 x6F,
0 x6D,
0 x65,
0 x2D,
0 x53,
0 x74,
0 x61,
0 x74,
0 x65,
0 x31,
0 x21,
0 x30,
0 x1F,
0 x06,
0 x03,
0 x55,
0 x04,
0 x0A,
0 x0C,
0 x18,
0 x49,
0 x6E,
0 x74,
0 x65,
0 x72,
0 x6E,
0 x65,
0 x74,
0 x20,
0 x57,
0 x69,
0 x64,
0 x67,
0 x69,
0 x74,
0 x73,
0 x20,
0 x50,
0 x74,
0 x79,
0 x20,
0 x4C,
0 x74,
0 x64,
0 x02,
0 x14,
0 x6B,
0 x22,
0 xCA,
0 x91,
0 xE0,
0 x71,
0 x97,
0 xEB,
0 x45,
0 x0D,
0 x68,
0 xC0,
0 xD4,
0 xB6,
0 xE9,
0 x45,
0 x38,
0 x4C,
0 xDD,
0 xA3,
0 x30,
0 x0D,
0 x06,
0 x09,
0 x60,
0 x86,
0 x48,
0 x01,
0 x65,
0 x03,
0 x04,
0 x02,
0 x01,
0 x05,
0 x00,
0 x30,
0 x0A,
0 x06,
0 x08,
0 x2A,
0 x86,
0 x48,
0 xCE,
0 x3D,
0 x04,
0 x03,
0 x02,
0 x04,
0 x47,
0 x30,
0 x45,
0 x02,
0 x20,
0 x48,
0 xEB,
0 xE6,
0 xBA,
0 xFC,
0 xFD,
0 x83,
0 xB3,
0 xA2,
0 xB5,
0 x59,
0 x35,
0 x0C,
0 xA1,
0 x31,
0 x0E,
0 x2F,
0 xE3,
0 x8D,
0 x81,
0 xD8,
0 xF5,
0 x33,
0 xE4,
0 x83,
0 x87,
0 xB1,
0 xFD,
0 x43,
0 x9D,
0 x95,
0 x7D,
0 x02,
0 x21,
0 x00,
0 xD0,
0 x05,
0 x0E,
0 x05,
0 xA6,
0 x80,
0 x3C,
0 x1A,
0 xFE,
0 x51,
0 xFC,
0 x4D,
0 x1A,
0 x25,
0 x05,
0 x78,
0 xB5,
0 x42,
0 xF5,
0 xDE,
0 x4E,
0 x8A,
0 xF8,
0 xE3,
0 xD8,
0 x52,
0 xDC,
0 x2B,
0 x73,
0 x80,
0 x4A};
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));
}
// Verify that the CMS streaming decoder rejects content large enough to
// trigger integer truncation in the buffer-growth path of work_data.
// This bug only manifests on LP64 where unsigned long is 64-bit and the
// 64-bit product is truncated when assigned to int.
#if !
defined (_WIN32) &&
defined (__LP64__)
TEST_F(SMimeTest, CmsDecoderRejectsOversizeContent) {
const size_t kLenA =
0 x3F800000;
const size_t kLenB =
0 x42000000;
// CMS SignedData with a constructed indefinite-length OCTET STRING
// containing two large primitive substrings.
static const uint8_t kHeader[] = {
0 x30,
0 x80,
0 x06,
0 x09,
0 x2A,
0 x86,
0 x48,
0 x86,
0 xF7,
0 x0D,
0 x01,
0 x07,
0 x02,
0 xA0,
0 x80,
0 x30,
0 x80,
0 x02,
0 x01,
0 x01,
0 x31,
0 x00,
0 x30,
0 x80,
0 x06,
0 x09,
0 x2A,
0 x86,
0 x48,
0 x86,
0 xF7,
0 x0D,
0 x01,
0 x07,
0 x01,
0 xA0,
0 x80,
0 x24,
0 x80,
0 x04,
0 x84,
(uint8_t)(kLenA >>
24 ),
(uint8_t)(kLenA >>
16 ),
(uint8_t)(kLenA >>
8 ),
(uint8_t)(kLenA),
};
static const uint8_t kMid[] = {
0 x04,
0 x84,
(uint8_t)(kLenB >>
24 ),
(uint8_t)(kLenB >>
16 ),
(uint8_t)(kLenB >>
8 ),
(uint8_t)(kLenB),
};
static const uint8_t kFooter[] = {
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x31,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
0 x00,
};
const size_t total =
sizeof (kHeader) + kLenA +
sizeof (kMid) + kLenB +
sizeof (kFooter);
void * m = mmap(nullptr, total, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -
1 ,
0 );
ASSERT_NE(MAP_FAILED, m) <<
"mmap of " << total <<
" bytes failed" ;
uint8_t* p = static_cast<uint8_t*>(m);
memcpy(p, kHeader,
sizeof (kHeader));
memcpy(p +
sizeof (kHeader) + kLenA, kMid,
sizeof (kMid));
memcpy(p +
sizeof (kHeader) + kLenA +
sizeof (kMid) + kLenB, kFooter,
sizeof (kFooter));
NSSCMSDecoderContext* dcx = NSS_CMSDecoder_Start(
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
ASSERT_NE(nullptr, dcx);
SECStatus rv =
NSS_CMSDecoder_Update(dcx, reinterpret_cast<
const char *>(p), total);
NSSCMSMessage* msg = NSS_CMSDecoder_Finish(dcx);
munmap(m, total);
EXPECT_NE(SECSuccess, rv);
if (msg) {
NSS_CMSMessage_Destroy(msg);
}
}
#endif // !defined(_WIN32) && defined(__LP64__)
// Helper functions for DeepNestingRejected and ShallowNestingNotRejected
static Bytes NssOid(SECOidTag tag) {
const SECOidData* od = SECOID_FindOIDByTag(tag);
return OidVal(od->oid.data, od->oid.len);
}
static Bytes MakeNestedDigestedData(
int levels) {
Bytes alg = Seq(NssOid(SEC_OID_SHA1));
// Innermost DigestedData: version 0 (NSS_CMS_DIGESTED_DATA_VERSION_DATA),
// eContentType=id-data so nss_cms_before_data() returns early without
// creating a child decoder.
Bytes encap = Seq(Cat({NssOid(SEC_OID_PKCS7_DATA), Ctx0(OctetStr({
0 x00}))}));
Bytes dd = Seq(
Cat({Bytes{
0 x02,
0 x01,
0 x00}, alg, encap, OctetStr(Bytes(
20 ,
0 x00))}));
// Each wrapper level uses version 2 (NSS_CMS_DIGESTED_DATA_VERSION_ENCAP)
// because its eContentType is id-digestedData, not id-data.
for (
int i =
1 ; i < levels; i++) {
encap = Seq(Cat({NssOid(SEC_OID_PKCS7_DIGESTED_DATA), Ctx0(OctetStr(dd))}));
dd = Seq(
Cat({Bytes{
0 x02,
0 x01,
0 x02}, alg, encap, OctetStr(Bytes(
20 ,
0 x00))}));
}
// Root ContentInfo wrapper (not itself a DigestedData level).
return Seq(Cat({NssOid(SEC_OID_PKCS7_DIGESTED_DATA), Ctx0(dd)}));
}
// Bug 2023208: a CMS message whose nesting depth exceeds
// NSS_CMS_MAX_NESTING_DEPTH must be rejected cleanly by the setup-time check in
// nss_cms_before_data().
TEST_F(SMimeTest, DeepNestingRejected) {
Bytes der = MakeNestedDigestedData(
40 );
SECItem item = {siBuffer, der.data(), static_cast<
unsigned int >(der.size())};
ScopedNSSCMSMessage msg(NSS_CMSMessage_CreateFromDER(
&item, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr));
EXPECT_EQ(nullptr, msg.get());
}
TEST_F(SMimeTest, ShallowNestingNotRejected) {
Bytes der = MakeNestedDigestedData(
3 );
SECItem item = {siBuffer, der.data(), static_cast<
unsigned int >(der.size())};
ScopedNSSCMSMessage msg(NSS_CMSMessage_CreateFromDER(
&item, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr));
EXPECT_NE(nullptr, msg.get());
}
}
// namespace nss_test
Messung V0.5 in Prozent C=94 H=94 G=93
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-08-26)
¤
*© Formatika GbR, Deutschland