mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 1020993 - properly handle unknown critical extensions in BackCert::Init r=briansmith
This commit is contained in:
parent
a61943f22f
commit
f6a2f616b1
@ -75,10 +75,19 @@ BackCert::Init(const SECItem& certDER)
|
||||
for (const CERTCertExtension* ext = *exts; ext; ext = *++exts) {
|
||||
const SECItem** out = nullptr;
|
||||
|
||||
if (ext->id.len == 3 &&
|
||||
ext->id.data[0] == 0x55 && ext->id.data[1] == 0x1d) {
|
||||
// { id-ce x }
|
||||
switch (ext->id.data[2]) {
|
||||
// python DottedOIDToCode.py id-ce 2.5.29
|
||||
static const uint8_t id_ce[] = {
|
||||
0x55, 0x1d
|
||||
};
|
||||
|
||||
// python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1
|
||||
static const uint8_t id_pe_authorityInfoAccess[] = {
|
||||
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
|
||||
};
|
||||
|
||||
if (ext->id.len == PR_ARRAY_SIZE(id_ce) + 1 &&
|
||||
!memcmp(ext->id.data, id_ce, PR_ARRAY_SIZE(id_ce))) {
|
||||
switch (ext->id.data[ext->id.len - 1]) {
|
||||
case 14: out = &dummyEncodedSubjectKeyIdentifier; break; // bug 965136
|
||||
case 15: out = &encodedKeyUsage; break;
|
||||
case 17: out = &dummyEncodedSubjectAltName; break; // bug 970542
|
||||
@ -89,21 +98,20 @@ BackCert::Init(const SECItem& certDER)
|
||||
case 37: out = &encodedExtendedKeyUsage; break;
|
||||
case 54: out = &encodedInhibitAnyPolicy; break; // Bug 989051
|
||||
}
|
||||
} else if (ext->id.len == 9 &&
|
||||
ext->id.data[0] == 0x2b && ext->id.data[1] == 0x06 &&
|
||||
ext->id.data[2] == 0x06 && ext->id.data[3] == 0x01 &&
|
||||
ext->id.data[4] == 0x05 && ext->id.data[5] == 0x05 &&
|
||||
ext->id.data[6] == 0x07 && ext->id.data[7] == 0x01) {
|
||||
// { id-pe x }
|
||||
switch (ext->id.data[8]) {
|
||||
// We should remember the value of the encoded AIA extension here, but
|
||||
// since our TrustDomain implementations get the OCSP URI using
|
||||
// CERT_GetOCSPAuthorityInfoAccessLocation, we currently don't need to.
|
||||
case 1: out = &dummyEncodedAuthorityInfoAccess; break;
|
||||
}
|
||||
} else if (ext->critical.data && ext->critical.len > 0) {
|
||||
// The only valid explicit value of the critical flag is TRUE because
|
||||
// it is defined as BOOLEAN DEFAULT FALSE, so we just assume it is true.
|
||||
} else if (ext->id.len == PR_ARRAY_SIZE(id_pe_authorityInfoAccess) &&
|
||||
!memcmp(ext->id.data, id_pe_authorityInfoAccess,
|
||||
PR_ARRAY_SIZE(id_pe_authorityInfoAccess))) {
|
||||
// We should remember the value of the encoded AIA extension here, but
|
||||
// since our TrustDomain implementations get the OCSP URI using
|
||||
// CERT_GetOCSPAuthorityInfoAccessLocation, we currently don't need to.
|
||||
out = &dummyEncodedAuthorityInfoAccess;
|
||||
}
|
||||
|
||||
// If this is an extension we don't understand and it's marked critical,
|
||||
// we must reject this certificate.
|
||||
// (The only valid explicit value of the critical flag is TRUE because
|
||||
// it is defined as BOOLEAN DEFAULT FALSE, so we just assume it is true.)
|
||||
if (!out && ext->critical.data && ext->critical.len > 0) {
|
||||
return Fail(RecoverableError, SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ LIBRARY_NAME = 'mozillapkix_gtest'
|
||||
SOURCES += [
|
||||
'nssgtest.cpp',
|
||||
'pkix_cert_chain_length_tests.cpp',
|
||||
'pkix_cert_extension_tests.cpp',
|
||||
'pkix_ocsp_request_tests.cpp',
|
||||
'pkixder_input_tests.cpp',
|
||||
'pkixder_pki_types_tests.cpp',
|
||||
|
319
security/pkix/test/gtest/pkix_cert_extension_tests.cpp
Normal file
319
security/pkix/test/gtest/pkix_cert_extension_tests.cpp
Normal file
@ -0,0 +1,319 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This code is made available to you under your choice of the following sets
|
||||
* of licensing terms:
|
||||
*/
|
||||
/* 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/.
|
||||
*/
|
||||
/* Copyright 2013 Mozilla Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "nssgtest.h"
|
||||
#include "pkix/pkix.h"
|
||||
#include "secerr.h"
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::pkix::test;
|
||||
|
||||
// Creates a self-signed certificate with the given extension.
|
||||
static bool
|
||||
CreateCert(PLArenaPool* arena, const char* subjectStr,
|
||||
const SECItem* extension,
|
||||
/*out*/ ScopedSECKEYPrivateKey& subjectKey,
|
||||
/*out*/ ScopedCERTCertificate& subjectCert)
|
||||
{
|
||||
static long serialNumberValue = 0;
|
||||
++serialNumberValue;
|
||||
const SECItem* serialNumber(CreateEncodedSerialNumber(arena,
|
||||
serialNumberValue));
|
||||
if (!serialNumber) {
|
||||
return false;
|
||||
}
|
||||
const SECItem* issuerDER(ASCIIToDERName(arena, subjectStr));
|
||||
if (!issuerDER) {
|
||||
return false;
|
||||
}
|
||||
const SECItem* subjectDER(ASCIIToDERName(arena, subjectStr));
|
||||
if (!subjectDER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SECItem* extensions[2] = { extension, nullptr };
|
||||
|
||||
SECItem* certDER(CreateEncodedCertificate(arena, v3, SEC_OID_SHA256,
|
||||
serialNumber, issuerDER,
|
||||
PR_Now() - ONE_DAY,
|
||||
PR_Now() + ONE_DAY,
|
||||
subjectDER, extensions,
|
||||
nullptr, SEC_OID_SHA256,
|
||||
subjectKey));
|
||||
if (!certDER) {
|
||||
return false;
|
||||
}
|
||||
subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDER,
|
||||
nullptr, false, true);
|
||||
return subjectCert.get() != nullptr;
|
||||
}
|
||||
|
||||
class TrustEverythingTrustDomain : public TrustDomain
|
||||
{
|
||||
private:
|
||||
SECStatus GetCertTrust(EndEntityOrCA,
|
||||
const CertPolicyId&,
|
||||
const SECItem& candidateCert,
|
||||
/*out*/ TrustLevel* trustLevel)
|
||||
{
|
||||
*trustLevel = TrustLevel::TrustAnchor;
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
|
||||
PRTime time,
|
||||
/*out*/ ScopedCERTCertList& results)
|
||||
{
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
SECStatus VerifySignedData(const CERTSignedData* signedData,
|
||||
const SECItem& subjectPublicKeyInfo)
|
||||
{
|
||||
return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
SECStatus CheckRevocation(EndEntityOrCA, const CERTCertificate*,
|
||||
/*const*/ CERTCertificate*, PRTime,
|
||||
/*optional*/ const SECItem*)
|
||||
{
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
virtual SECStatus IsChainValid(const CERTCertList*)
|
||||
{
|
||||
return SECSuccess;
|
||||
}
|
||||
};
|
||||
|
||||
class pkix_cert_extensions: public NSSTest
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
NSSTest::SetUpTestCase();
|
||||
}
|
||||
|
||||
protected:
|
||||
static TrustEverythingTrustDomain trustDomain;
|
||||
};
|
||||
|
||||
/*static*/ TrustEverythingTrustDomain pkix_cert_extensions::trustDomain;
|
||||
|
||||
// Tests that a critical extension not in the id-ce or id-pe arcs (which is
|
||||
// thus unknown to us) is detected and that verification fails with the
|
||||
// appropriate error.
|
||||
TEST_F(pkix_cert_extensions, UnknownCriticalExtension)
|
||||
{
|
||||
static const uint8_t unknownCriticalExtensionBytes[] = {
|
||||
0x30, 0x19, // SEQUENCE (length = 25)
|
||||
0x06, 0x12, // OID (length = 18)
|
||||
// 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
|
||||
0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a,
|
||||
0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03,
|
||||
0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
|
||||
0x04, 0x00 // OCTET STRING (length = 0)
|
||||
};
|
||||
static const SECItem unknownCriticalExtension = {
|
||||
siBuffer,
|
||||
const_cast<unsigned char*>(unknownCriticalExtensionBytes),
|
||||
sizeof(unknownCriticalExtensionBytes)
|
||||
};
|
||||
const char* certCN = "CN=Cert With Unknown Critical Extension";
|
||||
ScopedSECKEYPrivateKey key;
|
||||
ScopedCERTCertificate cert;
|
||||
ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalExtension, key,
|
||||
cert));
|
||||
ScopedCERTCertList results;
|
||||
ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
|
||||
BuildCertChain(trustDomain, cert.get(),
|
||||
now, EndEntityOrCA::MustBeEndEntity,
|
||||
0, // key usage
|
||||
KeyPurposeId::anyExtendedKeyUsage,
|
||||
CertPolicyId::anyPolicy,
|
||||
nullptr, // stapled OCSP response
|
||||
results));
|
||||
}
|
||||
|
||||
// Tests that a non-critical extension not in the id-ce or id-pe arcs (which is
|
||||
// thus unknown to us) verifies successfully.
|
||||
TEST_F(pkix_cert_extensions, UnknownNonCriticalExtension)
|
||||
{
|
||||
static const uint8_t unknownNonCriticalExtensionBytes[] = {
|
||||
0x30, 0x16, // SEQUENCE (length = 22)
|
||||
0x06, 0x12, // OID (length = 18)
|
||||
// 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
|
||||
0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a,
|
||||
0x85, 0x1a, 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03,
|
||||
0x04, 0x00 // OCTET STRING (length = 0)
|
||||
};
|
||||
static const SECItem unknownNonCriticalExtension = {
|
||||
siBuffer,
|
||||
const_cast<unsigned char*>(unknownNonCriticalExtensionBytes),
|
||||
sizeof(unknownNonCriticalExtensionBytes)
|
||||
};
|
||||
const char* certCN = "CN=Cert With Unknown NonCritical Extension";
|
||||
ScopedSECKEYPrivateKey key;
|
||||
ScopedCERTCertificate cert;
|
||||
ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownNonCriticalExtension, key,
|
||||
cert));
|
||||
ScopedCERTCertList results;
|
||||
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
|
||||
now, EndEntityOrCA::MustBeEndEntity,
|
||||
0, // key usage
|
||||
KeyPurposeId::anyExtendedKeyUsage,
|
||||
CertPolicyId::anyPolicy,
|
||||
nullptr, // stapled OCSP response
|
||||
results));
|
||||
}
|
||||
|
||||
// Tests that an incorrect OID for id-pe-authorityInformationAccess
|
||||
// (when marked critical) is detected and that verification fails.
|
||||
// (Until bug 1020993 was fixed, the code checked for this OID.)
|
||||
TEST_F(pkix_cert_extensions, WrongOIDCriticalExtension)
|
||||
{
|
||||
static const uint8_t wrongOIDCriticalExtensionBytes[] = {
|
||||
0x30, 0x10, // SEQUENCE (length = 16)
|
||||
0x06, 0x09, // OID (length = 9)
|
||||
// 1.3.6.6.1.5.5.7.1.1 (there is an extra "6" that shouldn't be there)
|
||||
0x2b, 0x06, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
|
||||
0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
|
||||
0x04, 0x00 // OCTET STRING (length = 0)
|
||||
};
|
||||
static const SECItem wrongOIDCriticalExtension = {
|
||||
siBuffer,
|
||||
const_cast<unsigned char*>(wrongOIDCriticalExtensionBytes),
|
||||
sizeof(wrongOIDCriticalExtensionBytes)
|
||||
};
|
||||
const char* certCN = "CN=Cert With Critical Wrong OID Extension";
|
||||
ScopedSECKEYPrivateKey key;
|
||||
ScopedCERTCertificate cert;
|
||||
ASSERT_TRUE(CreateCert(arena.get(), certCN, &wrongOIDCriticalExtension, key,
|
||||
cert));
|
||||
ScopedCERTCertList results;
|
||||
ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
|
||||
BuildCertChain(trustDomain, cert.get(),
|
||||
now, EndEntityOrCA::MustBeEndEntity,
|
||||
0, // key usage
|
||||
KeyPurposeId::anyExtendedKeyUsage,
|
||||
CertPolicyId::anyPolicy,
|
||||
nullptr, // stapled OCSP response
|
||||
results));
|
||||
}
|
||||
|
||||
// Tests that a id-pe-authorityInformationAccess critical extension
|
||||
// is detected and that verification succeeds.
|
||||
TEST_F(pkix_cert_extensions, CriticalAIAExtension)
|
||||
{
|
||||
static const uint8_t criticalAIAExtensionBytes[] = {
|
||||
0x30, 0x0f, // SEQUENCE (length = 15)
|
||||
0x06, 0x08, // OID (length = 8)
|
||||
// 1.3.6.1.5.5.7.1.1
|
||||
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
|
||||
0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
|
||||
0x04, 0x00 // OCTET STRING (length = 0)
|
||||
};
|
||||
static const SECItem criticalAIAExtension = {
|
||||
siBuffer,
|
||||
const_cast<unsigned char*>(criticalAIAExtensionBytes),
|
||||
sizeof(criticalAIAExtensionBytes)
|
||||
};
|
||||
const char* certCN = "CN=Cert With Critical AIA Extension";
|
||||
ScopedSECKEYPrivateKey key;
|
||||
ScopedCERTCertificate cert;
|
||||
ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalAIAExtension, key,
|
||||
cert));
|
||||
ScopedCERTCertList results;
|
||||
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
|
||||
now, EndEntityOrCA::MustBeEndEntity,
|
||||
0, // key usage
|
||||
KeyPurposeId::anyExtendedKeyUsage,
|
||||
CertPolicyId::anyPolicy,
|
||||
nullptr, // stapled OCSP response
|
||||
results));
|
||||
}
|
||||
|
||||
// We know about some id-ce extensions (OID arc 2.5.29), but not all of them.
|
||||
// Tests that an unknown id-ce extension is detected and that verification
|
||||
// fails.
|
||||
TEST_F(pkix_cert_extensions, UnknownCriticalCEExtension)
|
||||
{
|
||||
static const uint8_t unknownCriticalCEExtensionBytes[] = {
|
||||
0x30, 0x0a, // SEQUENCE (length = 10)
|
||||
0x06, 0x03, // OID (length = 3)
|
||||
0x55, 0x1d, 0x37, // 2.5.29.55
|
||||
0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
|
||||
0x04, 0x00 // OCTET STRING (length = 0)
|
||||
};
|
||||
static const SECItem unknownCriticalCEExtension = {
|
||||
siBuffer,
|
||||
const_cast<unsigned char*>(unknownCriticalCEExtensionBytes),
|
||||
sizeof(unknownCriticalCEExtensionBytes)
|
||||
};
|
||||
const char* certCN = "CN=Cert With Unknown Critical id-ce Extension";
|
||||
ScopedSECKEYPrivateKey key;
|
||||
ScopedCERTCertificate cert;
|
||||
ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalCEExtension, key,
|
||||
cert));
|
||||
ScopedCERTCertList results;
|
||||
ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
|
||||
BuildCertChain(trustDomain, cert.get(),
|
||||
now, EndEntityOrCA::MustBeEndEntity,
|
||||
0, // key usage
|
||||
KeyPurposeId::anyExtendedKeyUsage,
|
||||
CertPolicyId::anyPolicy,
|
||||
nullptr, // stapled OCSP response
|
||||
results));
|
||||
}
|
||||
|
||||
// Tests that a certificate with a known critical id-ce extension (in this case,
|
||||
// OID 2.5.29.54, which is id-ce-inhibitAnyPolicy), verifies successfully.
|
||||
TEST_F(pkix_cert_extensions, KnownCriticalCEExtension)
|
||||
{
|
||||
static const uint8_t criticalCEExtensionBytes[] = {
|
||||
0x30, 0x0a, // SEQUENCE (length = 10)
|
||||
0x06, 0x03, // OID (length = 3)
|
||||
0x55, 0x1d, 0x36, // 2.5.29.54 (id-ce-inhibitAnyPolicy)
|
||||
0x01, 0x01, 0xff, // BOOLEAN (length = 1) TRUE
|
||||
0x04, 0x00 // OCTET STRING (length = 0)
|
||||
};
|
||||
static const SECItem criticalCEExtension = {
|
||||
siBuffer,
|
||||
const_cast<unsigned char*>(criticalCEExtensionBytes),
|
||||
sizeof(criticalCEExtensionBytes)
|
||||
};
|
||||
const char* certCN = "CN=Cert With Known Critical id-ce Extension";
|
||||
ScopedSECKEYPrivateKey key;
|
||||
ScopedCERTCertificate cert;
|
||||
ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalCEExtension, key, cert));
|
||||
ScopedCERTCertList results;
|
||||
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
|
||||
now, EndEntityOrCA::MustBeEndEntity,
|
||||
0, // key usage
|
||||
KeyPurposeId::anyExtendedKeyUsage,
|
||||
CertPolicyId::anyPolicy,
|
||||
nullptr, // stapled OCSP response
|
||||
results));
|
||||
}
|
Loading…
Reference in New Issue
Block a user