Bug 1488622 - land NSS 94bcc2706b98 UPGRADE_NSS_RELEASE, r=me

--HG--
extra : rebase_source : 761520ca901dabbf0a908a886732155d0d40d468
This commit is contained in:
J.C. Jones 2018-10-01 07:44:32 -07:00
Родитель 319c43d823
Коммит b5cc135a82
104 изменённых файлов: 21263 добавлений и 225 удалений

Просмотреть файл

@ -1 +1 @@
a706ba3c4fa9
94bcc2706b98

Просмотреть файл

@ -99,6 +99,7 @@ while [ $# -gt 0 ]; do
--system-nspr) set_nspr_path "/usr/include/nspr/:"; no_local_nspr=1 ;;
--enable-libpkix) gyp_params+=(-Ddisable_libpkix=0) ;;
--enable-fips) gyp_params+=(-Ddisable_fips=0) ;;
--mozpkix-only) gyp_params+=(-Dmozpkix_only=1 -Ddisable_tests=1 -Dsign_libs=0) ;;
*) show_help; exit 2 ;;
esac
shift

Просмотреть файл

@ -108,8 +108,12 @@
'emit_llvm%': 0,
'nss_public_dist_dir%': '<(nss_dist_dir)/public',
'nss_private_dist_dir%': '<(nss_dist_dir)/private',
# This is only needed when building with --mozpkix-only and might not work
# on all machines.
'nss_include_dir%': '/usr/include/nss',
'only_dev_random%': 1,
'disable_fips%': 1,
'mozpkix_only%': 0,
},
'target_defaults': {
# Settings specific to targets should go here.
@ -126,6 +130,11 @@
'<(nss_dist_dir)/private/<(module)',
],
'conditions': [
[ 'mozpkix_only==1 and OS=="linux"', {
'include_dirs': [
'<(nss_include_dir)',
],
}],
[ 'disable_fips==1', {
'defines': [
'NSS_FIPS_DISABLED',

Просмотреть файл

@ -10,3 +10,4 @@
*/
#error "Do not include this header file."

Просмотреть файл

@ -8,7 +8,7 @@
#include "prerror.h"
#include "prio.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
class DummyIOLayerMethods {
public:

Просмотреть файл

@ -4,8 +4,8 @@
* 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 scoped_ptrs_h__
#define scoped_ptrs_h__
#ifndef nss_scoped_ptrs_h__
#define nss_scoped_ptrs_h__
#include <memory>
#include "cert.h"
@ -13,7 +13,6 @@
#include "p12.h"
#include "pk11pub.h"
#include "pkcs11uri.h"
#include "sslexp.h"
struct ScopedDelete {
void operator()(CERTCertificate* cert) { CERT_DestroyCertificate(cert); }
@ -29,6 +28,9 @@ struct ScopedDelete {
void operator()(PK11SymKey* key) { PK11_FreeSymKey(key); }
void operator()(PRFileDesc* fd) { PR_Close(fd); }
void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); }
void operator()(SECKEYEncryptedPrivateKeyInfo* e) {
SECKEY_DestroyEncryptedPrivateKeyInfo(e, true);
}
void operator()(SECItem* item) { SECITEM_FreeItem(item, true); }
void operator()(SECKEYPublicKey* key) { SECKEY_DestroyPublicKey(key); }
void operator()(SECKEYPrivateKey* key) { SECKEY_DestroyPrivateKey(key); }
@ -39,9 +41,6 @@ struct ScopedDelete {
void operator()(PLArenaPool* arena) { PORT_FreeArena(arena, PR_FALSE); }
void operator()(PK11Context* context) { PK11_DestroyContext(context, true); }
void operator()(PK11GenericObject* obj) { PK11_DestroyGenericObject(obj); }
void operator()(SSLResumptionTokenInfo* token) {
SSL_DestroyResumptionTokenInfo(token);
}
void operator()(SEC_PKCS12DecoderContext* dcx) {
SEC_PKCS12DecoderFinish(dcx);
}
@ -69,6 +68,7 @@ SCOPED(PK11SlotInfo);
SCOPED(PK11SymKey);
SCOPED(PRFileDesc);
SCOPED(SECAlgorithmID);
SCOPED(SECKEYEncryptedPrivateKeyInfo);
SCOPED(SECItem);
SCOPED(SECKEYPublicKey);
SCOPED(SECKEYPrivateKey);
@ -77,10 +77,9 @@ SCOPED(PK11URI);
SCOPED(PLArenaPool);
SCOPED(PK11Context);
SCOPED(PK11GenericObject);
SCOPED(SSLResumptionTokenInfo);
SCOPED(SEC_PKCS12DecoderContext);
SCOPED(CERTDistNames);
#undef SCOPED
#endif // scoped_ptrs_h__
#endif // nss_scoped_ptrs_h__

Просмотреть файл

@ -0,0 +1,35 @@
/* -*- 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/. */
#ifndef scoped_ptrs_ssl_h__
#define scoped_ptrs_ssl_h__
#include <memory>
#include "sslexp.h"
struct ScopedDeleteSSL {
void operator()(SSLResumptionTokenInfo* token) {
SSL_DestroyResumptionTokenInfo(token);
}
};
template <class T>
struct ScopedMaybeDeleteSSL {
void operator()(T* ptr) {
if (ptr) {
ScopedDeleteSSL del;
del(ptr);
}
}
};
#define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDeleteSSL<x> > Scoped##x
SCOPED(SSLResumptionTokenInfo);
#undef SCOPED
#endif // scoped_ptrs_ssl_h__

Просмотреть файл

@ -5,9 +5,82 @@
'includes': [
'coreconf/config.gypi'
],
'conditions': [
[ 'mozpkix_only==0', {
'targets': [
{
'target_name': 'nss_exports',
'type': 'none',
'direct_dependent_settings': {
'include_dirs': [
'<(nss_public_dist_dir)/nss',
]
},
'dependencies': [
'cmd/lib/exports.gyp:cmd_lib_exports',
'lib/base/exports.gyp:lib_base_exports',
'lib/certdb/exports.gyp:lib_certdb_exports',
'lib/certhigh/exports.gyp:lib_certhigh_exports',
'lib/ckfw/builtins/exports.gyp:lib_ckfw_builtins_exports',
'lib/ckfw/exports.gyp:lib_ckfw_exports',
'lib/crmf/exports.gyp:lib_crmf_exports',
'lib/cryptohi/exports.gyp:lib_cryptohi_exports',
'lib/dev/exports.gyp:lib_dev_exports',
'lib/freebl/exports.gyp:lib_freebl_exports',
'lib/jar/exports.gyp:lib_jar_exports',
'lib/nss/exports.gyp:lib_nss_exports',
'lib/pk11wrap/exports.gyp:lib_pk11wrap_exports',
'lib/pkcs12/exports.gyp:lib_pkcs12_exports',
'lib/pkcs7/exports.gyp:lib_pkcs7_exports',
'lib/pki/exports.gyp:lib_pki_exports',
'lib/smime/exports.gyp:lib_smime_exports',
'lib/softoken/exports.gyp:lib_softoken_exports',
'lib/sqlite/exports.gyp:lib_sqlite_exports',
'lib/ssl/exports.gyp:lib_ssl_exports',
'lib/util/exports.gyp:lib_util_exports',
'lib/zlib/exports.gyp:lib_zlib_exports',
],
'conditions': [
[ 'disable_libpkix==0', {
'dependencies': [
'lib/libpkix/include/exports.gyp:lib_libpkix_include_exports',
'lib/libpkix/pkix/certsel/exports.gyp:lib_libpkix_pkix_certsel_exports',
'lib/libpkix/pkix/checker/exports.gyp:lib_libpkix_pkix_checker_exports',
'lib/libpkix/pkix/crlsel/exports.gyp:lib_libpkix_pkix_crlsel_exports',
'lib/libpkix/pkix/params/exports.gyp:lib_libpkix_pkix_params_exports',
'lib/libpkix/pkix/results/exports.gyp:lib_libpkix_pkix_results_exports',
'lib/libpkix/pkix/store/exports.gyp:lib_libpkix_pkix_store_exports',
'lib/libpkix/pkix/top/exports.gyp:lib_libpkix_pkix_top_exports',
'lib/libpkix/pkix/util/exports.gyp:lib_libpkix_pkix_util_exports',
'lib/libpkix/pkix_pl_nss/module/exports.gyp:lib_libpkix_pkix_pl_nss_module_exports',
'lib/libpkix/pkix_pl_nss/pki/exports.gyp:lib_libpkix_pkix_pl_nss_pki_exports',
'lib/libpkix/pkix_pl_nss/system/exports.gyp:lib_libpkix_pkix_pl_nss_system_exports',
],
}],
],
},
{
'target_name': 'dbm_exports',
'type': 'none',
'conditions': [
['disable_dbm==0', {
'direct_dependent_settings': {
'include_dirs': [
'<(nss_public_dist_dir)/dbm'
]
},
'dependencies': [
'lib/dbm/include/exports.gyp:lib_dbm_include_exports'
],
}],
],
}
],
}],
],
'targets': [
{
'target_name': 'nss_exports',
'target_name': 'nss_mozpkix_exports',
'type': 'none',
'direct_dependent_settings': {
'include_dirs': [
@ -15,63 +88,9 @@
]
},
'dependencies': [
'cmd/lib/exports.gyp:cmd_lib_exports',
'lib/base/exports.gyp:lib_base_exports',
'lib/certdb/exports.gyp:lib_certdb_exports',
'lib/certhigh/exports.gyp:lib_certhigh_exports',
'lib/ckfw/builtins/exports.gyp:lib_ckfw_builtins_exports',
'lib/ckfw/exports.gyp:lib_ckfw_exports',
'lib/crmf/exports.gyp:lib_crmf_exports',
'lib/cryptohi/exports.gyp:lib_cryptohi_exports',
'lib/dev/exports.gyp:lib_dev_exports',
'lib/freebl/exports.gyp:lib_freebl_exports',
'lib/jar/exports.gyp:lib_jar_exports',
'lib/nss/exports.gyp:lib_nss_exports',
'lib/pk11wrap/exports.gyp:lib_pk11wrap_exports',
'lib/pkcs12/exports.gyp:lib_pkcs12_exports',
'lib/pkcs7/exports.gyp:lib_pkcs7_exports',
'lib/pki/exports.gyp:lib_pki_exports',
'lib/smime/exports.gyp:lib_smime_exports',
'lib/softoken/exports.gyp:lib_softoken_exports',
'lib/sqlite/exports.gyp:lib_sqlite_exports',
'lib/ssl/exports.gyp:lib_ssl_exports',
'lib/util/exports.gyp:lib_util_exports',
'lib/zlib/exports.gyp:lib_zlib_exports'
],
'conditions': [
[ 'disable_libpkix==0', {
'dependencies': [
'lib/libpkix/include/exports.gyp:lib_libpkix_include_exports',
'lib/libpkix/pkix/certsel/exports.gyp:lib_libpkix_pkix_certsel_exports',
'lib/libpkix/pkix/checker/exports.gyp:lib_libpkix_pkix_checker_exports',
'lib/libpkix/pkix/crlsel/exports.gyp:lib_libpkix_pkix_crlsel_exports',
'lib/libpkix/pkix/params/exports.gyp:lib_libpkix_pkix_params_exports',
'lib/libpkix/pkix/results/exports.gyp:lib_libpkix_pkix_results_exports',
'lib/libpkix/pkix/store/exports.gyp:lib_libpkix_pkix_store_exports',
'lib/libpkix/pkix/top/exports.gyp:lib_libpkix_pkix_top_exports',
'lib/libpkix/pkix/util/exports.gyp:lib_libpkix_pkix_util_exports',
'lib/libpkix/pkix_pl_nss/module/exports.gyp:lib_libpkix_pkix_pl_nss_module_exports',
'lib/libpkix/pkix_pl_nss/pki/exports.gyp:lib_libpkix_pkix_pl_nss_pki_exports',
'lib/libpkix/pkix_pl_nss/system/exports.gyp:lib_libpkix_pkix_pl_nss_system_exports',
],
}],
'lib/mozpkix/exports.gyp:lib_mozpkix_exports',
'lib/mozpkix/exports.gyp:lib_mozpkix_test_exports',
],
},
{
'target_name': 'dbm_exports',
'type': 'none',
'conditions': [
['disable_dbm==0', {
'direct_dependent_settings': {
'include_dirs': [
'<(nss_public_dist_dir)/dbm'
]
},
'dependencies': [
'lib/dbm/include/exports.gyp:lib_dbm_include_exports'
],
}],
],
}
]
],
}

Просмотреть файл

@ -8,7 +8,7 @@
#include "ssl.h"
#include "cpputil.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_server_certs.h"
const uint8_t kP256ServerCert[] = {

Просмотреть файл

@ -9,7 +9,7 @@
#include "gtest/gtest.h"
#include "nss.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "prprf.h"
namespace nss_test {

Просмотреть файл

@ -8,7 +8,7 @@
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "cryptohi.h"
#include "secitem.h"
#include "secerr.h"

Просмотреть файл

@ -11,7 +11,7 @@
#include "secutil.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {

Просмотреть файл

@ -8,7 +8,7 @@
#include "p12.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {

Просмотреть файл

@ -7,7 +7,7 @@
#include <stdint.h>
#include "blapi.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "secerr.h"
namespace nss_test {

Просмотреть файл

@ -0,0 +1,61 @@
-------------
Running Tests
-------------
Because of the rules below, you can run all the unit tests in this directory,
and only these tests, with:
mach gtest "pkix*"
You can run just the tests for functions defined in filename pkixfoo.cpp with:
mach gtest "pkixfoo*"
If you run "mach gtest" then you'll end up running every gtest in Gecko.
------------
Naming Files
------------
Name files containing tests according to one of the following patterns:
* <filename>_tests.cpp
* <filename>_<Function>_tests.cpp
* <filename>_<category>_tests.cpp
<filename> is the name of the file containing the definitions of the
function(s) being tested by every test.
<Function> is the name of the function that is being tested by every
test.
<category> describes the group of related functions that are being
tested by every test.
------------------------------------------------
Always Use a Fixture Class: TEST_F(), not TEST()
------------------------------------------------
Many tests don't technically need a fixture, and so TEST() could technically
be used to define the test. However, when you use TEST_F() instead of TEST(),
the compiler will not allow you to make any typos in the test case name, but
if you use TEST() then the name of the test case is not checked.
See https://code.google.com/p/googletest/wiki/Primer#Test_Fixtures:_Using_the_Same_Data_Configuration_for_Multiple_Te
to learn more about test fixtures.
---------------
Naming Fixtures
---------------
When all tests in a file use the same fixture, use the base name of the file
without the "_tests" suffix as the name of the fixture class; e.g. tests in
"pkixocsp.cpp" should use a fixture "class pkixocsp" by default.
Sometimes tests in a file need separate fixtures. In this case, name the
fixture class according to the pattern <fixture_base>_<fixture_suffix>, where
<fixture_base> is the base name of the file without the "_tests" suffix, and
<fixture_suffix> is a descriptive name for the fixture class, e.g.
"class pkixocsp_DelegatedResponder".

Просмотреть файл

@ -0,0 +1,71 @@
# 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': 'mozpkix_gtest',
'type': 'executable',
'sources': [
'<(DEPTH)/gtests/common/gtests.cc',
'pkixbuild_tests.cpp',
'pkixcert_extension_tests.cpp',
'pkixcert_signature_algorithm_tests.cpp',
'pkixcheck_CheckExtendedKeyUsage_tests.cpp',
'pkixcheck_CheckIssuer_tests.cpp',
'pkixcheck_CheckKeyUsage_tests.cpp',
'pkixcheck_CheckSignatureAlgorithm_tests.cpp',
'pkixcheck_CheckValidity_tests.cpp',
'pkixcheck_ParseValidity_tests.cpp',
'pkixcheck_TLSFeaturesSatisfiedInternal_tests.cpp',
'pkixder_input_tests.cpp',
'pkixder_pki_types_tests.cpp',
'pkixder_universal_types_tests.cpp',
'pkixgtest.cpp',
'pkixnames_tests.cpp',
'pkixocsp_CreateEncodedOCSPRequest_tests.cpp',
'pkixocsp_VerifyEncodedOCSPResponse.cpp',
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
'<(DEPTH)/lib/util/util.gyp:nssutil',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl',
'<(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/mozpkix/mozpkix.gyp:mozpkix',
'<(DEPTH)/lib/mozpkix/mozpkix.gyp:mozpkix-testlib',
],
'include_dirs': [
'<(DEPTH)/lib/mozpkix/',
'<(DEPTH)/lib/mozpkix/lib',
'<(DEPTH)/lib/mozpkix/include/',
'<(DEPTH)/lib/mozpkix/include/pkix-test/',
],
'conditions': [
[ 'OS=="win"', {
'libraries': [
'advapi32.lib',
],
}],
],
'defines': [
'NSS_USE_STATIC_LIBS'
],
}
],
'variables': {
'module': 'nss',
'use_static_libs': 1,
}
}

Просмотреть файл

@ -0,0 +1,894 @@
/* -*- 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.
*/
#if defined(_MSC_VER) && _MSC_VER < 1900
// When building with -D_HAS_EXCEPTIONS=0, MSVC's <xtree> header triggers
// warning C4702: unreachable code.
// https://connect.microsoft.com/VisualStudio/feedback/details/809962
#pragma warning(push)
#pragma warning(disable: 4702)
#endif
#include <map>
#include <vector>
#if defined(_MSC_VER) && _MSC_VER < 1900
#pragma warning(pop)
#endif
#include "pkixgtest.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
static ByteString
CreateCert(const char* issuerCN, // null means "empty name"
const char* subjectCN, // null means "empty name"
EndEntityOrCA endEntityOrCA,
/*optional modified*/ std::map<ByteString, ByteString>*
subjectDERToCertDER = nullptr,
/*optional*/ const ByteString* extension = nullptr,
/*optional*/ const TestKeyPair* issuerKeyPair = nullptr,
/*optional*/ const TestKeyPair* subjectKeyPair = nullptr)
{
static long serialNumberValue = 0;
++serialNumberValue;
ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(issuerCN ? CNToDERName(issuerCN) : Name(ByteString()));
ByteString subjectDER(subjectCN ? CNToDERName(subjectCN) : Name(ByteString()));
std::vector<ByteString> extensions;
if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
ByteString basicConstraints =
CreateEncodedBasicConstraints(true, nullptr, Critical::Yes);
EXPECT_FALSE(ENCODING_FAILED(basicConstraints));
extensions.push_back(basicConstraints);
}
if (extension) {
extensions.push_back(*extension);
}
extensions.push_back(ByteString()); // marks the end of the list
ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
ByteString certDER(CreateEncodedCertificate(
v3, sha256WithRSAEncryption(), serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow, subjectDER,
subjectKeyPair ? *subjectKeyPair : *reusedKey,
extensions.data(),
issuerKeyPair ? *issuerKeyPair : *reusedKey,
sha256WithRSAEncryption()));
EXPECT_FALSE(ENCODING_FAILED(certDER));
if (subjectDERToCertDER) {
(*subjectDERToCertDER)[subjectDER] = certDER;
}
return certDER;
}
class TestTrustDomain final : public DefaultCryptoTrustDomain
{
public:
// The "cert chain tail" is a longish chain of certificates that is used by
// all of the tests here. We share this chain across all the tests in order
// to speed up the tests (generating keypairs for the certs is very slow).
bool SetUpCertChainTail()
{
static char const* const names[] = {
"CA1 (Root)", "CA2", "CA3", "CA4", "CA5", "CA6", "CA7"
};
for (size_t i = 0; i < MOZILLA_PKIX_ARRAY_LENGTH(names); ++i) {
const char* issuerName = i == 0 ? names[0] : names[i-1];
CreateCACert(issuerName, names[i]);
if (i == 0) {
rootCACertDER = leafCACertDER;
}
}
return true;
}
void CreateCACert(const char* issuerName, const char* subjectName)
{
leafCACertDER = CreateCert(issuerName, subjectName,
EndEntityOrCA::MustBeCA, &subjectDERToCertDER);
assert(!ENCODING_FAILED(leafCACertDER));
}
ByteString GetLeafCACertDER() const { return leafCACertDER; }
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time)
override
{
ByteString subjectDER(InputToByteString(encodedIssuerName));
ByteString certDER(subjectDERToCertDER[subjectDER]);
Input derCert;
Result rv = derCert.Init(certDER.data(), certDER.length());
if (rv != Success) {
return rv;
}
bool keepGoing;
rv = checker.Check(derCert, nullptr/*additionalNameConstraints*/,
keepGoing);
if (rv != Success) {
return rv;
}
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
std::map<ByteString, ByteString> subjectDERToCertDER;
ByteString leafCACertDER;
ByteString rootCACertDER;
};
class pkixbuild : public ::testing::Test
{
public:
static void SetUpTestCase()
{
if (!trustDomain.SetUpCertChainTail()) {
abort();
}
}
protected:
static TestTrustDomain trustDomain;
};
/*static*/ TestTrustDomain pkixbuild::trustDomain;
TEST_F(pkixbuild, MaxAcceptableCertChainLength)
{
{
ByteString leafCACert(trustDomain.GetLeafCACertDER());
Input certDER;
ASSERT_EQ(Success, certDER.Init(leafCACert.data(), leafCACert.length()));
ASSERT_EQ(Success,
BuildCertChain(trustDomain, certDER, Now(),
EndEntityOrCA::MustBeCA,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
{
ByteString certDER(CreateCert("CA7", "Direct End-Entity",
EndEntityOrCA::MustBeEndEntity));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Success,
BuildCertChain(trustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
}
TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
{
static char const* const caCertName = "CA Too Far";
trustDomain.CreateCACert("CA7", caCertName);
{
ByteString certDER(trustDomain.GetLeafCACertDER());
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
BuildCertChain(trustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeCA,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
{
ByteString certDER(CreateCert(caCertName, "End-Entity Too Far",
EndEntityOrCA::MustBeEndEntity));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
BuildCertChain(trustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
}
// A TrustDomain that checks certificates against a given root certificate.
// It is initialized with the DER encoding of a root certificate that
// is treated as a trust anchor and is assumed to have issued all certificates
// (i.e. FindIssuer always attempts to build the next step in the chain with
// it).
class SingleRootTrustDomain : public DefaultCryptoTrustDomain
{
public:
explicit SingleRootTrustDomain(ByteString aRootDER)
: rootDER(aRootDER)
{
}
// The CertPolicyId argument is unused because we don't care about EV.
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
Input rootCert;
Result rv = rootCert.Init(rootDER.data(), rootDER.length());
if (rv != Success) {
return rv;
}
if (InputsAreEqual(candidateCert, rootCert)) {
trustLevel = TrustLevel::TrustAnchor;
} else {
trustLevel = TrustLevel::InheritsTrust;
}
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
// keepGoing is an out parameter from IssuerChecker.Check. It would tell us
// whether or not to continue attempting other potential issuers. We only
// know of one potential issuer, however, so we ignore it.
bool keepGoing;
Input rootCert;
Result rv = rootCert.Init(rootDER.data(), rootDER.length());
if (rv != Success) {
return rv;
}
return checker.Check(rootCert, nullptr, keepGoing);
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
private:
ByteString rootDER;
};
// A TrustDomain that explicitly fails if CheckRevocation is called.
class ExpiredCertTrustDomain final : public SingleRootTrustDomain
{
public:
explicit ExpiredCertTrustDomain(ByteString aRootDER)
: SingleRootTrustDomain(aRootDER)
{
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
ADD_FAILURE();
return NotReached("CheckRevocation should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
};
TEST_F(pkixbuild, NoRevocationCheckingForExpiredCert)
{
const char* rootCN = "Root CA";
ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
nullptr));
EXPECT_FALSE(ENCODING_FAILED(rootDER));
ExpiredCertTrustDomain expiredCertTrustDomain(rootDER);
ByteString serialNumber(CreateEncodedSerialNumber(100));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(CNToDERName(rootCN));
ByteString subjectDER(CNToDERName("Expired End-Entity Cert"));
ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
ByteString certDER(CreateEncodedCertificate(
v3, sha256WithRSAEncryption(),
serialNumber, issuerDER,
twoDaysBeforeNow,
oneDayBeforeNow,
subjectDER, *reusedKey, nullptr, *reusedKey,
sha256WithRSAEncryption()));
EXPECT_FALSE(ENCODING_FAILED(certDER));
Input cert;
ASSERT_EQ(Success, cert.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE,
BuildCertChain(expiredCertTrustDomain, cert, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr));
}
class DSSTrustDomain final : public EverythingFailsByDefaultTrustDomain
{
public:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
Input, /*out*/ TrustLevel& trustLevel) override
{
trustLevel = TrustLevel::TrustAnchor;
return Success;
}
};
class pkixbuild_DSS : public ::testing::Test { };
TEST_F(pkixbuild_DSS, DSSEndEntityKeyNotAccepted)
{
DSSTrustDomain trustDomain;
ByteString serialNumber(CreateEncodedSerialNumber(1));
ASSERT_FALSE(ENCODING_FAILED(serialNumber));
ByteString subjectDER(CNToDERName("DSS"));
ASSERT_FALSE(ENCODING_FAILED(subjectDER));
ScopedTestKeyPair subjectKey(GenerateDSSKeyPair());
ASSERT_TRUE(subjectKey.get());
ByteString issuerDER(CNToDERName("RSA"));
ASSERT_FALSE(ENCODING_FAILED(issuerDER));
ScopedTestKeyPair issuerKey(CloneReusedKeyPair());
ASSERT_TRUE(issuerKey.get());
ByteString cert(CreateEncodedCertificate(v3, sha256WithRSAEncryption(),
serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow,
subjectDER, *subjectKey, nullptr,
*issuerKey, sha256WithRSAEncryption()));
ASSERT_FALSE(ENCODING_FAILED(cert));
Input certDER;
ASSERT_EQ(Success, certDER.Init(cert.data(), cert.length()));
ASSERT_EQ(Result::ERROR_UNSUPPORTED_KEYALG,
BuildCertChain(trustDomain, certDER, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
class IssuerNameCheckTrustDomain final : public DefaultCryptoTrustDomain
{
public:
IssuerNameCheckTrustDomain(const ByteString& aIssuer, bool aExpectedKeepGoing)
: issuer(aIssuer)
, expectedKeepGoing(aExpectedKeepGoing)
{
}
Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&, Input,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = endEntityOrCA == EndEntityOrCA::MustBeCA
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
Input issuerInput;
EXPECT_EQ(Success, issuerInput.Init(issuer.data(), issuer.length()));
bool keepGoing;
EXPECT_EQ(Success,
checker.Check(issuerInput, nullptr /*additionalNameConstraints*/,
keepGoing));
EXPECT_EQ(expectedKeepGoing, keepGoing);
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
private:
const ByteString issuer;
const bool expectedKeepGoing;
};
struct IssuerNameCheckParams
{
const char* subjectIssuerCN; // null means "empty name"
const char* issuerSubjectCN; // null means "empty name"
bool matches;
Result expectedError;
};
static const IssuerNameCheckParams ISSUER_NAME_CHECK_PARAMS[] =
{
{ "foo", "foo", true, Success },
{ "foo", "bar", false, Result::ERROR_UNKNOWN_ISSUER },
{ "f", "foo", false, Result::ERROR_UNKNOWN_ISSUER }, // prefix
{ "foo", "f", false, Result::ERROR_UNKNOWN_ISSUER }, // prefix
{ "foo", "Foo", false, Result::ERROR_UNKNOWN_ISSUER }, // case sensitive
{ "", "", true, Success },
{ nullptr, nullptr, false, Result::ERROR_EMPTY_ISSUER_NAME }, // empty issuer
// check that certificate-related errors are deferred and superseded by
// ERROR_UNKNOWN_ISSUER when a chain can't be built due to name mismatches
{ "foo", nullptr, false, Result::ERROR_UNKNOWN_ISSUER },
{ nullptr, "foo", false, Result::ERROR_UNKNOWN_ISSUER }
};
class pkixbuild_IssuerNameCheck
: public ::testing::Test
, public ::testing::WithParamInterface<IssuerNameCheckParams>
{
};
TEST_P(pkixbuild_IssuerNameCheck, MatchingName)
{
const IssuerNameCheckParams& params(GetParam());
ByteString issuerCertDER(CreateCert(params.issuerSubjectCN,
params.issuerSubjectCN,
EndEntityOrCA::MustBeCA, nullptr));
ASSERT_FALSE(ENCODING_FAILED(issuerCertDER));
ByteString subjectCertDER(CreateCert(params.subjectIssuerCN, "end-entity",
EndEntityOrCA::MustBeEndEntity,
nullptr));
ASSERT_FALSE(ENCODING_FAILED(subjectCertDER));
Input subjectCertDERInput;
ASSERT_EQ(Success, subjectCertDERInput.Init(subjectCertDER.data(),
subjectCertDER.length()));
IssuerNameCheckTrustDomain trustDomain(issuerCertDER, !params.matches);
ASSERT_EQ(params.expectedError,
BuildCertChain(trustDomain, subjectCertDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
INSTANTIATE_TEST_CASE_P(pkixbuild_IssuerNameCheck, pkixbuild_IssuerNameCheck,
testing::ValuesIn(ISSUER_NAME_CHECK_PARAMS));
// Records the embedded SCT list extension for later examination.
class EmbeddedSCTListTestTrustDomain final : public SingleRootTrustDomain
{
public:
explicit EmbeddedSCTListTestTrustDomain(ByteString aRootDER)
: SingleRootTrustDomain(aRootDER)
{
}
virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
Input extensionData) override
{
if (extension == AuxiliaryExtension::EmbeddedSCTList) {
signedCertificateTimestamps = InputToByteString(extensionData);
} else {
ADD_FAILURE();
}
}
ByteString signedCertificateTimestamps;
};
TEST_F(pkixbuild, CertificateTransparencyExtension)
{
// python security/pkix/tools/DottedOIDToCode.py --tlv
// id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
static const uint8_t tlv_id_embeddedSctList[] = {
0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
};
static const uint8_t dummySctList[] = {
0x01, 0x02, 0x03, 0x04, 0x05
};
ByteString ctExtension = TLV(der::SEQUENCE,
BytesToByteString(tlv_id_embeddedSctList) +
Boolean(false) +
TLV(der::OCTET_STRING,
// SignedCertificateTimestampList structure is encoded as an OCTET STRING
// within the X.509v3 extension (see RFC 6962 section 3.3).
// pkix decodes it internally and returns the actual structure.
TLV(der::OCTET_STRING, BytesToByteString(dummySctList))));
const char* rootCN = "Root CA";
ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA));
ASSERT_FALSE(ENCODING_FAILED(rootDER));
ByteString certDER(CreateCert(rootCN, "Cert with SCT list",
EndEntityOrCA::MustBeEndEntity,
nullptr, /*subjectDERToCertDER*/
&ctExtension));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certInput;
ASSERT_EQ(Success, certInput.Init(certDER.data(), certDER.length()));
EmbeddedSCTListTestTrustDomain extTrustDomain(rootDER);
ASSERT_EQ(Success,
BuildCertChain(extTrustDomain, certInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr /*stapledOCSPResponse*/));
ASSERT_EQ(BytesToByteString(dummySctList),
extTrustDomain.signedCertificateTimestamps);
}
// This TrustDomain implements a hierarchy like so:
//
// A B
// | |
// C D
// \ /
// E
//
// where A is a trust anchor, B is not a trust anchor and has no known issuer, C
// and D are intermediates with the same subject and subject public key, and E
// is an end-entity (in practice, the end-entity will be generated by the test
// functions using this trust domain).
class MultiplePathTrustDomain: public DefaultCryptoTrustDomain
{
public:
void SetUpCerts()
{
ASSERT_FALSE(ENCODING_FAILED(CreateCert("UntrustedRoot", "UntrustedRoot",
EndEntityOrCA::MustBeCA,
&subjectDERToCertDER)));
// The subject DER -> cert DER mapping would be overwritten for subject
// "Intermediate" when we create the second "Intermediate" certificate, so
// we keep a copy of this "Intermediate".
intermediateSignedByUntrustedRootCertDER =
CreateCert("UntrustedRoot", "Intermediate", EndEntityOrCA::MustBeCA);
ASSERT_FALSE(ENCODING_FAILED(intermediateSignedByUntrustedRootCertDER));
rootCACertDER = CreateCert("TrustedRoot", "TrustedRoot",
EndEntityOrCA::MustBeCA, &subjectDERToCertDER);
ASSERT_FALSE(ENCODING_FAILED(rootCACertDER));
ASSERT_FALSE(ENCODING_FAILED(CreateCert("TrustedRoot", "Intermediate",
EndEntityOrCA::MustBeCA,
&subjectDERToCertDER)));
}
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result CheckCert(ByteString& certDER, IssuerChecker& checker, bool& keepGoing)
{
Input derCert;
Result rv = derCert.Init(certDER.data(), certDER.length());
if (rv != Success) {
return rv;
}
return checker.Check(derCert, nullptr/*additionalNameConstraints*/,
keepGoing);
}
Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time)
override
{
ByteString subjectDER(InputToByteString(encodedIssuerName));
ByteString certDER(subjectDERToCertDER[subjectDER]);
assert(!ENCODING_FAILED(certDER));
bool keepGoing;
Result rv = CheckCert(certDER, checker, keepGoing);
if (rv != Success) {
return rv;
}
// Also try the other intermediate.
if (keepGoing) {
rv = CheckCert(intermediateSignedByUntrustedRootCertDER, checker,
keepGoing);
if (rv != Success) {
return rv;
}
}
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*,
/*optional*/ const Input*) override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
std::map<ByteString, ByteString> subjectDERToCertDER;
ByteString rootCACertDER;
ByteString intermediateSignedByUntrustedRootCertDER;
};
TEST_F(pkixbuild, BadEmbeddedSCTWithMultiplePaths)
{
MultiplePathTrustDomain localTrustDomain;
localTrustDomain.SetUpCerts();
// python security/pkix/tools/DottedOIDToCode.py --tlv
// id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
static const uint8_t tlv_id_embeddedSctList[] = {
0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
};
static const uint8_t dummySctList[] = {
0x01, 0x02, 0x03, 0x04, 0x05
};
ByteString ctExtension = TLV(der::SEQUENCE,
BytesToByteString(tlv_id_embeddedSctList) +
Boolean(false) +
// The contents of the OCTET STRING are supposed to consist of an OCTET
// STRING of useful data. We're testing what happens if it isn't, so shove
// some bogus (non-OCTET STRING) data in there.
TLV(der::OCTET_STRING, BytesToByteString(dummySctList)));
ByteString certDER(CreateCert("Intermediate", "Cert with bogus SCT list",
EndEntityOrCA::MustBeEndEntity,
nullptr, /*subjectDERToCertDER*/
&ctExtension));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_BAD_DER,
BuildCertChain(localTrustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
// Same as a MultiplePathTrustDomain, but the end-entity is revoked.
class RevokedEndEntityTrustDomain final : public MultiplePathTrustDomain
{
public:
Result CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&, Time,
Duration, /*optional*/ const Input*,
/*optional*/ const Input*) override
{
if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
return Result::ERROR_REVOKED_CERTIFICATE;
}
return Success;
}
};
TEST_F(pkixbuild, RevokedEndEntityWithMultiplePaths)
{
RevokedEndEntityTrustDomain localTrustDomain;
localTrustDomain.SetUpCerts();
ByteString certDER(CreateCert("Intermediate", "RevokedEndEntity",
EndEntityOrCA::MustBeEndEntity));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE,
BuildCertChain(localTrustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
// This represents a collection of different certificates that all have the same
// subject and issuer distinguished name.
class SelfIssuedCertificatesTrustDomain final : public DefaultCryptoTrustDomain
{
public:
void SetUpCerts(size_t totalCerts)
{
ASSERT_TRUE(totalCerts > 0);
// First we generate a trust anchor.
ScopedTestKeyPair rootKeyPair(GenerateKeyPair());
rootCACertDER = CreateCert("DN", "DN", EndEntityOrCA::MustBeCA, nullptr,
nullptr, rootKeyPair.get(), rootKeyPair.get());
ASSERT_FALSE(ENCODING_FAILED(rootCACertDER));
certs.push_back(rootCACertDER);
ScopedTestKeyPair issuerKeyPair(rootKeyPair.release());
size_t subCAsGenerated;
// Then we generate 6 sub-CAs (given that we were requested to generate at
// least that many).
for (subCAsGenerated = 0;
subCAsGenerated < totalCerts - 1 && subCAsGenerated < 6;
subCAsGenerated++) {
// Each certificate has to have a unique SPKI (mozilla::pkix does loop
// detection and stops searching if it encounters two certificates in a
// path with the same subject and SPKI).
ScopedTestKeyPair keyPair(GenerateKeyPair());
ByteString cert(CreateCert("DN", "DN", EndEntityOrCA::MustBeCA, nullptr,
nullptr, issuerKeyPair.get(), keyPair.get()));
ASSERT_FALSE(ENCODING_FAILED(cert));
certs.push_back(cert);
issuerKeyPair.reset(keyPair.release());
}
// We set firstIssuerKey here because we can't end up with a path that has
// more than 7 CAs in it (because mozilla::pkix limits the path length).
firstIssuerKey.reset(issuerKeyPair.release());
// For any more sub CAs we generate, it doesn't matter what their keys are
// as long as they're different.
for (; subCAsGenerated < totalCerts - 1; subCAsGenerated++) {
ScopedTestKeyPair keyPair(GenerateKeyPair());
ByteString cert(CreateCert("DN", "DN", EndEntityOrCA::MustBeCA, nullptr,
nullptr, keyPair.get(), keyPair.get()));
ASSERT_FALSE(ENCODING_FAILED(cert));
certs.insert(certs.begin(), cert);
}
}
const TestKeyPair* GetFirstIssuerKey()
{
return firstIssuerKey.get();
}
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
bool keepGoing;
for (auto& cert: certs) {
Input certInput;
Result rv = certInput.Init(cert.data(), cert.length());
if (rv != Success) {
return rv;
}
rv = checker.Check(certInput, nullptr, keepGoing);
if (rv != Success || !keepGoing) {
return rv;
}
}
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
std::vector<ByteString> certs;
ByteString rootCACertDER;
ScopedTestKeyPair firstIssuerKey;
};
TEST_F(pkixbuild, AvoidUnboundedPathSearchingFailure)
{
SelfIssuedCertificatesTrustDomain localTrustDomain;
// This creates a few hundred million potential paths of length 8 (end entity
// + 6 sub-CAs + root). It would be prohibitively expensive to enumerate all
// of these, so we give mozilla::pkix a budget that is spent when searching
// paths. If the budget is exhausted, it simply returns an unknown issuer
// error. In the future it might be nice to return a specific error that would
// give the front-end a hint that maybe it shouldn't have so many certificates
// that all have the same subject and issuer DN but different SPKIs.
localTrustDomain.SetUpCerts(18);
ByteString certDER(CreateCert("DN", "DN", EndEntityOrCA::MustBeEndEntity,
nullptr, nullptr,
localTrustDomain.GetFirstIssuerKey()));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
BuildCertChain(localTrustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
TEST_F(pkixbuild, AvoidUnboundedPathSearchingSuccess)
{
SelfIssuedCertificatesTrustDomain localTrustDomain;
// This creates a few hundred thousand possible potential paths of length 8
// (end entity + 6 sub-CAs + root). This will nearly exhaust mozilla::pkix's
// search budget, so this should succeed.
localTrustDomain.SetUpCerts(10);
ByteString certDER(CreateCert("DN", "DN", EndEntityOrCA::MustBeEndEntity,
nullptr, nullptr,
localTrustDomain.GetFirstIssuerKey()));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Success,
BuildCertChain(localTrustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}

Просмотреть файл

@ -0,0 +1,276 @@
/* -*- 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 "pkixgtest.h"
#include "mozpkix/pkixder.h"
#include "mozpkix/test/pkixtestutil.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
// Creates a self-signed certificate with the given extension.
static ByteString
CreateCertWithExtensions(const char* subjectCN,
const ByteString* extensions)
{
static long serialNumberValue = 0;
++serialNumberValue;
ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(CNToDERName(subjectCN));
EXPECT_FALSE(ENCODING_FAILED(issuerDER));
ByteString subjectDER(CNToDERName(subjectCN));
EXPECT_FALSE(ENCODING_FAILED(subjectDER));
ScopedTestKeyPair subjectKey(CloneReusedKeyPair());
return CreateEncodedCertificate(v3, sha256WithRSAEncryption(),
serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow,
subjectDER, *subjectKey, extensions,
*subjectKey,
sha256WithRSAEncryption());
}
// Creates a self-signed certificate with the given extension.
static ByteString
CreateCertWithOneExtension(const char* subjectStr, const ByteString& extension)
{
const ByteString extensions[] = { extension, ByteString() };
return CreateCertWithExtensions(subjectStr, extensions);
}
class TrustEverythingTrustDomain final : public DefaultCryptoTrustDomain
{
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = TrustLevel::TrustAnchor;
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
};
// python DottedOIDToCode.py --tlv unknownExtensionOID 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
static const uint8_t tlv_unknownExtensionOID[] = {
0x06, 0x12, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a, 0x85, 0x1a,
0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03
};
// python DottedOIDToCode.py --tlv id-pe-authorityInformationAccess 1.3.6.1.5.5.7.1.1
static const uint8_t tlv_id_pe_authorityInformationAccess[] = {
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
};
// python DottedOIDToCode.py --tlv wrongExtensionOID 1.3.6.6.1.5.5.7.1.1
// (there is an extra "6" that shouldn't be in this OID)
static const uint8_t tlv_wrongExtensionOID[] = {
0x06, 0x09, 0x2b, 0x06, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
};
// python DottedOIDToCode.py --tlv id-ce-unknown 2.5.29.55
// (this is a made-up OID for testing "id-ce"-prefixed OIDs that mozilla::pkix
// doesn't handle)
static const uint8_t tlv_id_ce_unknown[] = {
0x06, 0x03, 0x55, 0x1d, 0x37
};
// python DottedOIDToCode.py --tlv id-ce-inhibitAnyPolicy 2.5.29.54
static const uint8_t tlv_id_ce_inhibitAnyPolicy[] = {
0x06, 0x03, 0x55, 0x1d, 0x36
};
// python DottedOIDToCode.py --tlv id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5
static const uint8_t tlv_id_pkix_ocsp_nocheck[] = {
0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05
};
struct ExtensionTestcase
{
ByteString extension;
Result expectedResult;
};
::std::ostream& operator<<(::std::ostream& os, const ExtensionTestcase&)
{
return os << "TODO (bug 1318770)";
}
static const ExtensionTestcase EXTENSION_TESTCASES[] =
{
// Tests that a non-critical extension not in the id-ce or id-pe arcs (which
// is thus unknown to us) verifies successfully even if empty (extensions we
// know about aren't normally allowed to be empty).
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_unknownExtensionOID) +
TLV(der::OCTET_STRING, ByteString())),
Success
},
// 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.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_unknownExtensionOID) +
Boolean(true) +
TLV(der::OCTET_STRING, ByteString())),
Result::ERROR_UNKNOWN_CRITICAL_EXTENSION
},
// Tests that a id-pe-authorityInformationAccess critical extension
// is detected and that verification succeeds.
// XXX: According to RFC 5280 an AIA that consists of an empty sequence is
// not legal, but we accept it and that is not what we're testing here.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_pe_authorityInformationAccess) +
Boolean(true) +
TLV(der::OCTET_STRING, TLV(der::SEQUENCE, ByteString()))),
Success
},
// Tests that an incorrect OID for id-pe-authorityInformationAccess
// (when marked critical) is detected and that verification fails.
// (Until bug 1020993 was fixed, this wrong value was used for
// id-pe-authorityInformationAccess.)
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_wrongExtensionOID) +
Boolean(true) +
TLV(der::OCTET_STRING, ByteString())),
Result::ERROR_UNKNOWN_CRITICAL_EXTENSION
},
// 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.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_ce_unknown) +
Boolean(true) +
TLV(der::OCTET_STRING, ByteString())),
Result::ERROR_UNKNOWN_CRITICAL_EXTENSION
},
// 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.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_ce_inhibitAnyPolicy) +
Boolean(true) +
TLV(der::OCTET_STRING, Integer(0))),
Success
},
// Tests that a certificate with the id-pkix-ocsp-nocheck extension (marked
// critical) verifies successfully.
// RFC 6960:
// ext-ocsp-nocheck EXTENSION ::= { SYNTAX NULL IDENTIFIED
// BY id-pkix-ocsp-nocheck }
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_pkix_ocsp_nocheck) +
Boolean(true) +
TLV(der::OCTET_STRING, TLV(der::NULLTag, ByteString()))),
Success
},
// Tests that a certificate with another representation of the
// id-pkix-ocsp-nocheck extension (marked critical) verifies successfully.
// According to http://comments.gmane.org/gmane.ietf.x509/30947,
// some code creates certificates where value of the extension is
// an empty OCTET STRING.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_pkix_ocsp_nocheck) +
Boolean(true) +
TLV(der::OCTET_STRING, ByteString())),
Success
},
};
class pkixcert_extension
: public ::testing::Test
, public ::testing::WithParamInterface<ExtensionTestcase>
{
protected:
static TrustEverythingTrustDomain trustDomain;
};
/*static*/ TrustEverythingTrustDomain pkixcert_extension::trustDomain;
TEST_P(pkixcert_extension, ExtensionHandledProperly)
{
const ExtensionTestcase& testcase(GetParam());
const char* cn = "Cert Extension Test";
ByteString cert(CreateCertWithOneExtension(cn, testcase.extension));
ASSERT_FALSE(ENCODING_FAILED(cert));
Input certInput;
ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
ASSERT_EQ(testcase.expectedResult,
BuildCertChain(trustDomain, certInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
INSTANTIATE_TEST_CASE_P(pkixcert_extension,
pkixcert_extension,
testing::ValuesIn(EXTENSION_TESTCASES));
// Two subjectAltNames must result in an error.
TEST_F(pkixcert_extension, DuplicateSubjectAltName)
{
// python DottedOIDToCode.py --tlv id-ce-subjectAltName 2.5.29.17
static const uint8_t tlv_id_ce_subjectAltName[] = {
0x06, 0x03, 0x55, 0x1d, 0x11
};
ByteString subjectAltName(
TLV(der::SEQUENCE,
BytesToByteString(tlv_id_ce_subjectAltName) +
TLV(der::OCTET_STRING, TLV(der::SEQUENCE, DNSName("example.com")))));
static const ByteString extensions[] = { subjectAltName, subjectAltName,
ByteString() };
static const char* certCN = "Cert With Duplicate subjectAltName";
ByteString cert(CreateCertWithExtensions(certCN, extensions));
ASSERT_FALSE(ENCODING_FAILED(cert));
Input certInput;
ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
ASSERT_EQ(Result::ERROR_EXTENSION_VALUE_INVALID,
BuildCertChain(trustDomain, certInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}

Просмотреть файл

@ -0,0 +1,259 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
#include "pkixgtest.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
static ByteString
CreateCert(const char* issuerCN,
const char* subjectCN,
EndEntityOrCA endEntityOrCA,
const TestSignatureAlgorithm& signatureAlgorithm,
/*out*/ ByteString& subjectDER)
{
static long serialNumberValue = 0;
++serialNumberValue;
ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(CNToDERName(issuerCN));
EXPECT_FALSE(ENCODING_FAILED(issuerDER));
subjectDER = CNToDERName(subjectCN);
EXPECT_FALSE(ENCODING_FAILED(subjectDER));
ByteString extensions[2];
if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
extensions[0] =
CreateEncodedBasicConstraints(true, nullptr, Critical::Yes);
EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
}
ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
ByteString certDER(CreateEncodedCertificate(v3, signatureAlgorithm,
serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow,
subjectDER, *reusedKey,
extensions, *reusedKey,
signatureAlgorithm));
EXPECT_FALSE(ENCODING_FAILED(certDER));
return certDER;
}
class AlgorithmTestsTrustDomain final : public DefaultCryptoTrustDomain
{
public:
AlgorithmTestsTrustDomain(const ByteString& aRootDER,
const ByteString& aRootSubjectDER,
/*optional*/ const ByteString& aIntDER,
/*optional*/ const ByteString& aIntSubjectDER)
: rootDER(aRootDER)
, rootSubjectDER(aRootSubjectDER)
, intDER(aIntDER)
, intSubjectDER(aIntSubjectDER)
{
}
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
if (InputEqualsByteString(candidateCert, rootDER)) {
trustLevel = TrustLevel::TrustAnchor;
} else {
trustLevel = TrustLevel::InheritsTrust;
}
return Success;
}
Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time)
override
{
ByteString* issuerDER = nullptr;
if (InputEqualsByteString(encodedIssuerName, rootSubjectDER)) {
issuerDER = &rootDER;
} else if (InputEqualsByteString(encodedIssuerName, intSubjectDER)) {
issuerDER = &intDER;
} else {
// FindIssuer just returns success if it can't find a potential issuer.
return Success;
}
Input issuerCert;
Result rv = issuerCert.Init(issuerDER->data(), issuerDER->length());
if (rv != Success) {
return rv;
}
bool keepGoing;
return checker.Check(issuerCert, nullptr, keepGoing);
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
const Input*, const Input*) override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
ByteString rootDER;
ByteString rootSubjectDER;
ByteString intDER;
ByteString intSubjectDER;
};
static const TestSignatureAlgorithm NO_INTERMEDIATE
{
TestPublicKeyAlgorithm(ByteString()),
TestDigestAlgorithmID::MD2,
ByteString(),
false
};
struct ChainValidity final
{
ChainValidity(const TestSignatureAlgorithm& aEndEntitySignatureAlgorithm,
const TestSignatureAlgorithm& aOptionalIntSignatureAlgorithm,
const TestSignatureAlgorithm& aRootSignatureAlgorithm,
bool aIsValid)
: endEntitySignatureAlgorithm(aEndEntitySignatureAlgorithm)
, optionalIntermediateSignatureAlgorithm(aOptionalIntSignatureAlgorithm)
, rootSignatureAlgorithm(aRootSignatureAlgorithm)
, isValid(aIsValid)
{ }
// In general, a certificate is generated for each of these. However, if
// optionalIntermediateSignatureAlgorithm is NO_INTERMEDIATE, then only 2
// certificates are generated.
// The certificate generated for the given rootSignatureAlgorithm is the
// trust anchor.
TestSignatureAlgorithm endEntitySignatureAlgorithm;
TestSignatureAlgorithm optionalIntermediateSignatureAlgorithm;
TestSignatureAlgorithm rootSignatureAlgorithm;
bool isValid;
};
static const ChainValidity CHAIN_VALIDITY[] =
{
// The trust anchor may have a signature with an unsupported signature
// algorithm.
ChainValidity(sha256WithRSAEncryption(),
NO_INTERMEDIATE,
md5WithRSAEncryption(),
true),
ChainValidity(sha256WithRSAEncryption(),
NO_INTERMEDIATE,
md2WithRSAEncryption(),
true),
// Certificates that are not trust anchors must not have a signature with an
// unsupported signature algorithm.
ChainValidity(md5WithRSAEncryption(),
NO_INTERMEDIATE,
sha256WithRSAEncryption(),
false),
ChainValidity(md2WithRSAEncryption(),
NO_INTERMEDIATE,
sha256WithRSAEncryption(),
false),
ChainValidity(md2WithRSAEncryption(),
NO_INTERMEDIATE,
md5WithRSAEncryption(),
false),
ChainValidity(sha256WithRSAEncryption(),
md5WithRSAEncryption(),
sha256WithRSAEncryption(),
false),
ChainValidity(sha256WithRSAEncryption(),
md2WithRSAEncryption(),
sha256WithRSAEncryption(),
false),
ChainValidity(sha256WithRSAEncryption(),
md2WithRSAEncryption(),
md5WithRSAEncryption(),
false),
};
class pkixcert_IsValidChainForAlgorithm
: public ::testing::Test
, public ::testing::WithParamInterface<ChainValidity>
{
};
::std::ostream& operator<<(::std::ostream& os,
const pkixcert_IsValidChainForAlgorithm&)
{
return os << "TODO (bug 1318770)";
}
::std::ostream& operator<<(::std::ostream& os, const ChainValidity&)
{
return os << "TODO (bug 1318770)";
}
TEST_P(pkixcert_IsValidChainForAlgorithm, IsValidChainForAlgorithm)
{
const ChainValidity& chainValidity(GetParam());
const char* rootCN = "CN=Root";
ByteString rootSubjectDER;
ByteString rootEncoded(
CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
chainValidity.rootSignatureAlgorithm, rootSubjectDER));
EXPECT_FALSE(ENCODING_FAILED(rootEncoded));
EXPECT_FALSE(ENCODING_FAILED(rootSubjectDER));
const char* issuerCN = rootCN;
const char* intermediateCN = "CN=Intermediate";
ByteString intermediateSubjectDER;
ByteString intermediateEncoded;
// If the the algorithmIdentifier is empty, then it's NO_INTERMEDIATE.
if (!chainValidity.optionalIntermediateSignatureAlgorithm
.algorithmIdentifier.empty()) {
intermediateEncoded =
CreateCert(rootCN, intermediateCN, EndEntityOrCA::MustBeCA,
chainValidity.optionalIntermediateSignatureAlgorithm,
intermediateSubjectDER);
EXPECT_FALSE(ENCODING_FAILED(intermediateEncoded));
EXPECT_FALSE(ENCODING_FAILED(intermediateSubjectDER));
issuerCN = intermediateCN;
}
AlgorithmTestsTrustDomain trustDomain(rootEncoded, rootSubjectDER,
intermediateEncoded,
intermediateSubjectDER);
const char* endEntityCN = "CN=End Entity";
ByteString endEntitySubjectDER;
ByteString endEntityEncoded(
CreateCert(issuerCN, endEntityCN, EndEntityOrCA::MustBeEndEntity,
chainValidity.endEntitySignatureAlgorithm,
endEntitySubjectDER));
EXPECT_FALSE(ENCODING_FAILED(endEntityEncoded));
EXPECT_FALSE(ENCODING_FAILED(endEntitySubjectDER));
Input endEntity;
ASSERT_EQ(Success, endEntity.Init(endEntityEncoded.data(),
endEntityEncoded.length()));
Result expectedResult = chainValidity.isValid
? Success
: Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
ASSERT_EQ(expectedResult,
BuildCertChain(trustDomain, endEntity, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy, nullptr));
}
INSTANTIATE_TEST_CASE_P(pkixcert_IsValidChainForAlgorithm,
pkixcert_IsValidChainForAlgorithm,
testing::ValuesIn(CHAIN_VALIDITY));

Просмотреть файл

@ -0,0 +1,722 @@
/* -*- 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 2016 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 "pkixgtest.h"
#include "mozpkix/pkixder.h"
#include "mozpkix/pkixutil.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
namespace mozilla { namespace pkix {
extern Result CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA,
const Input* encodedExtendedKeyUsage,
KeyPurposeId requiredEKU,
TrustDomain& trustDomain, Time notBefore);
} } // namespace mozilla::pkix
class pkixcheck_CheckExtendedKeyUsage : public ::testing::Test
{
protected:
DefaultCryptoTrustDomain mTrustDomain;
};
#define ASSERT_BAD(x) ASSERT_EQ(Result::ERROR_INADEQUATE_CERT_TYPE, x)
// tlv_id_kp_OCSPSigning and tlv_id_kp_serverAuth are defined in pkixtestutil.h
// python DottedOIDToCode.py --tlv id-kp-clientAuth 1.3.6.1.5.5.7.3.2
static const uint8_t tlv_id_kp_clientAuth[] = {
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02
};
// python DottedOIDToCode.py --tlv id-kp-codeSigning 1.3.6.1.5.5.7.3.3
static const uint8_t tlv_id_kp_codeSigning[] = {
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03
};
// python DottedOIDToCode.py --tlv id_kp_emailProtection 1.3.6.1.5.5.7.3.4
static const uint8_t tlv_id_kp_emailProtection[] = {
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04
};
// python DottedOIDToCode.py --tlv id-Netscape-stepUp 2.16.840.1.113730.4.1
static const uint8_t tlv_id_Netscape_stepUp[] = {
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01
};
// python DottedOIDToCode.py --tlv unknownOID 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
static const uint8_t tlv_unknownOID[] = {
0x06, 0x12, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a, 0x85, 0x1a,
0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03
};
// python DottedOIDToCode.py --tlv anyExtendedKeyUsage 2.5.29.37.0
static const uint8_t tlv_anyExtendedKeyUsage[] = {
0x06, 0x04, 0x55, 0x1d, 0x25, 0x00
};
TEST_F(pkixcheck_CheckExtendedKeyUsage, none)
{
// The input Input is nullptr. This means the cert had no extended key usage
// extension. This is always valid except for when the certificate is an
// end-entity and the required usage is id-kp-OCSPSigning.
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::anyExtendedKeyUsage,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::anyExtendedKeyUsage,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::id_kp_clientAuth,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_clientAuth,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::id_kp_codeSigning,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_codeSigning,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::id_kp_emailProtection,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_emailProtection,
mTrustDomain, Now()));
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyPurposeId::id_kp_OCSPSigning,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_OCSPSigning,
mTrustDomain, Now()));
}
static const Input empty_null;
TEST_F(pkixcheck_CheckExtendedKeyUsage, empty)
{
// The input Input is empty. The cert has an empty extended key usage
// extension, which is syntactically invalid.
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, &empty_null,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
static const uint8_t dummy = 0x00;
Input empty_nonnull;
ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0));
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
}
struct EKUTestcase
{
ByteString ekuSEQUENCE;
KeyPurposeId keyPurposeId;
Result expectedResultEndEntity;
Result expectedResultCA;
};
::std::ostream& operator<<(::std::ostream& os, const EKUTestcase&)
{
return os << "TODO (bug 1318770)";
}
class CheckExtendedKeyUsageTest
: public ::testing::Test
, public ::testing::WithParamInterface<EKUTestcase>
{
protected:
DefaultCryptoTrustDomain mTrustDomain;
};
TEST_P(CheckExtendedKeyUsageTest, EKUTestcase)
{
const EKUTestcase& param(GetParam());
Input encodedEKU;
ASSERT_EQ(Success, encodedEKU.Init(param.ekuSEQUENCE.data(),
param.ekuSEQUENCE.length()));
ASSERT_EQ(param.expectedResultEndEntity,
CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, &encodedEKU,
param.keyPurposeId,
mTrustDomain, Now()));
ASSERT_EQ(param.expectedResultCA,
CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, &encodedEKU,
param.keyPurposeId,
mTrustDomain, Now()));
}
#define SINGLE_EKU_SUCCESS(oidBytes, keyPurposeId) \
{ TLV(der::SEQUENCE, BytesToByteString(oidBytes)), keyPurposeId, \
Success, Success }
#define SINGLE_EKU_SUCCESS_CA(oidBytes, keyPurposeId) \
{ TLV(der::SEQUENCE, BytesToByteString(oidBytes)), keyPurposeId, \
Result::ERROR_INADEQUATE_CERT_TYPE, Success }
#define SINGLE_EKU_FAILURE(oidBytes, keyPurposeId) \
{ TLV(der::SEQUENCE, BytesToByteString(oidBytes)), keyPurposeId, \
Result::ERROR_INADEQUATE_CERT_TYPE, Result::ERROR_INADEQUATE_CERT_TYPE }
#define DOUBLE_EKU_SUCCESS(oidBytes1, oidBytes2, keyPurposeId) \
{ TLV(der::SEQUENCE, \
BytesToByteString(oidBytes1) + BytesToByteString(oidBytes2)), \
keyPurposeId, \
Success, Success }
#define DOUBLE_EKU_SUCCESS_CA(oidBytes1, oidBytes2, keyPurposeId) \
{ TLV(der::SEQUENCE, \
BytesToByteString(oidBytes1) + BytesToByteString(oidBytes2)), \
keyPurposeId, \
Result::ERROR_INADEQUATE_CERT_TYPE, Success }
#define DOUBLE_EKU_FAILURE(oidBytes1, oidBytes2, keyPurposeId) \
{ TLV(der::SEQUENCE, \
BytesToByteString(oidBytes1) + BytesToByteString(oidBytes2)), \
keyPurposeId, \
Result::ERROR_INADEQUATE_CERT_TYPE, Result::ERROR_INADEQUATE_CERT_TYPE }
static const EKUTestcase EKU_TESTCASES[] =
{
SINGLE_EKU_SUCCESS(tlv_id_kp_serverAuth, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_SUCCESS(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_id_kp_clientAuth, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_SUCCESS(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_id_kp_codeSigning, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_SUCCESS(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_id_kp_emailProtection, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_SUCCESS(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_OCSPSigning),
// For end-entities, if id-kp-OCSPSigning is present, no usage is allowed
// except OCSPSigning.
SINGLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_SUCCESS(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
// For compatibility, id-Netscape-stepUp is treated as equivalent to
// id-kp-serverAuth for CAs.
SINGLE_EKU_SUCCESS_CA(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
};
INSTANTIATE_TEST_CASE_P(pkixcheck_CheckExtendedKeyUsage,
CheckExtendedKeyUsageTest,
::testing::ValuesIn(EKU_TESTCASES));
struct EKUChainTestcase
{
ByteString ekuExtensionEE;
ByteString ekuExtensionCA;
KeyPurposeId keyPurposeId;
Result expectedResult;
};
::std::ostream& operator<<(::std::ostream& os, const EKUChainTestcase&)
{
return os << "TODO (bug 1318770)";
}
class CheckExtendedKeyUsageChainTest
: public ::testing::Test
, public ::testing::WithParamInterface<EKUChainTestcase>
{
};
static ByteString
CreateCert(const char* issuerCN, const char* subjectCN,
EndEntityOrCA endEntityOrCA, ByteString encodedEKU)
{
static long serialNumberValue = 0;
++serialNumberValue;
ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(CNToDERName(issuerCN));
ByteString subjectDER(CNToDERName(subjectCN));
ByteString extensions[3];
extensions[0] =
CreateEncodedBasicConstraints(endEntityOrCA == EndEntityOrCA::MustBeCA,
nullptr, Critical::Yes);
EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
if (encodedEKU.length() > 0) {
extensions[1] = encodedEKU;
}
ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
ByteString certDER(CreateEncodedCertificate(
v3, sha256WithRSAEncryption(), serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow, subjectDER,
*reusedKey, extensions, *reusedKey,
sha256WithRSAEncryption()));
EXPECT_FALSE(ENCODING_FAILED(certDER));
return certDER;
}
class EKUTrustDomain final : public DefaultCryptoTrustDomain
{
public:
explicit EKUTrustDomain(ByteString issuerCertDER)
: mIssuerCertDER(issuerCertDER)
{
}
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(candidateCert, mIssuerCertDER)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
Input derCert;
Result rv = derCert.Init(mIssuerCertDER.data(), mIssuerCertDER.length());
if (rv != Success) {
return rv;
}
bool keepGoing;
return checker.Check(derCert, nullptr, keepGoing);
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
const Input*, const Input*) override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
ByteString mIssuerCertDER;
};
TEST_P(CheckExtendedKeyUsageChainTest, EKUChainTestcase)
{
const EKUChainTestcase& param(GetParam());
ByteString issuerCertDER(CreateCert("CA", "CA", EndEntityOrCA::MustBeCA,
param.ekuExtensionCA));
ByteString subjectCertDER(CreateCert("CA", "EE",
EndEntityOrCA::MustBeEndEntity,
param.ekuExtensionEE));
EKUTrustDomain trustDomain(issuerCertDER);
Input subjectCertDERInput;
ASSERT_EQ(Success, subjectCertDERInput.Init(subjectCertDER.data(),
subjectCertDER.length()));
ASSERT_EQ(param.expectedResult,
BuildCertChain(trustDomain, subjectCertDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
param.keyPurposeId,
CertPolicyId::anyPolicy,
nullptr));
}
// python DottedOIDToCode.py --tlv id-ce-extKeyUsage 2.5.29.37
static const uint8_t tlv_id_ce_extKeyUsage[] = {
0x06, 0x03, 0x55, 0x1d, 0x25
};
static inline ByteString
CreateEKUExtension(ByteString ekuOIDs)
{
return TLV(der::SEQUENCE,
BytesToByteString(tlv_id_ce_extKeyUsage) +
TLV(der::OCTET_STRING, TLV(der::SEQUENCE, ekuOIDs)));
}
static const EKUChainTestcase EKU_CHAIN_TESTCASES[] =
{
{
// Both end-entity and CA have id-kp-serverAuth => should succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// CA has no EKU extension => should succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
ByteString(),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// End-entity has no EKU extension => should succeed
ByteString(),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// No EKU extensions at all => should succeed
ByteString(),
ByteString(),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// CA has EKU without id-kp-serverAuth => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// End-entity has EKU without id-kp-serverAuth => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// Both end-entity and CA have EKU without id-kp-serverAuth => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// End-entity has no EKU, CA doesn't have id-kp-serverAuth => should fail
ByteString(),
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// End-entity doesn't have id-kp-serverAuth, CA has no EKU => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
ByteString(),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// CA has id-Netscape-stepUp => should succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_Netscape_stepUp)),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// End-entity has id-Netscape-stepUp => should fail
CreateEKUExtension(BytesToByteString(tlv_id_Netscape_stepUp)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// End-entity and CA have id-kp-serverAuth and id-kp-clientAuth => should
// succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_clientAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// End-entity has id-kp-serverAuth and id-kp-OCSPSigning => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_OCSPSigning)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// CA has id-kp-serverAuth and id-kp-OCSPSigning => should succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_clientAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_OCSPSigning)),
KeyPurposeId::id_kp_serverAuth,
Success
},
};
INSTANTIATE_TEST_CASE_P(pkixcheck_CheckExtendedKeyUsage,
CheckExtendedKeyUsageChainTest,
::testing::ValuesIn(EKU_CHAIN_TESTCASES));

Просмотреть файл

@ -0,0 +1,63 @@
/* -*- 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 2016 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 "pkixgtest.h"
#include "mozpkix/pkixcheck.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
class pkixcheck_CheckIssuer : public ::testing::Test { };
static const uint8_t EMPTY_NAME_DATA[] = {
0x30, 0x00 /* tag, length */
};
static const Input EMPTY_NAME(EMPTY_NAME_DATA);
static const uint8_t VALID_NAME_DATA[] = {
/* From https://www.example.com/: C=US, O=DigiCert Inc, OU=www.digicert.com,
* CN=DigiCert SHA2 High Assurance Server CA */
0x30, 0x70, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0A,
0x13, 0x0C, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49,
0x6E, 0x63, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13,
0x10, 0x77, 0x77, 0x77, 0x2E, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72,
0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x2F, 0x30, 0x2D, 0x06, 0x03, 0x55,
0x04, 0x03, 0x13, 0x26, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74,
0x20, 0x53, 0x48, 0x41, 0x32, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41,
0x73, 0x73, 0x75, 0x72, 0x61, 0x6E, 0x63, 0x65, 0x20, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x20, 0x43, 0x41
};
static const Input VALID_NAME(VALID_NAME_DATA);
TEST_F(pkixcheck_CheckIssuer, ValidIssuer)
{
ASSERT_EQ(Success, CheckIssuer(VALID_NAME));
}
TEST_F(pkixcheck_CheckIssuer, EmptyIssuer)
{
ASSERT_EQ(Result::ERROR_EMPTY_ISSUER_NAME, CheckIssuer(EMPTY_NAME));
}

Просмотреть файл

@ -0,0 +1,284 @@
/* -*- 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 "pkixgtest.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
namespace mozilla { namespace pkix {
extern Result CheckKeyUsage(EndEntityOrCA endEntityOrCA,
const Input* encodedKeyUsage,
KeyUsage requiredKeyUsageIfPresent);
} } // namespace mozilla::pkix
class pkixcheck_CheckKeyUsage : public ::testing::Test { };
#define ASSERT_BAD(x) ASSERT_EQ(Result::ERROR_INADEQUATE_KEY_USAGE, x)
// Make it easy to define test data for the common, simplest cases.
#define NAMED_SIMPLE_KU(name, unusedBits, bits) \
const uint8_t name##_bytes[4] = { \
0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, unusedBits, bits \
}; \
const Input name(name##_bytes);
static const Input empty_null;
// Note that keyCertSign is really the only interesting case for CA
// certificates since we don't support cRLSign.
TEST_F(pkixcheck_CheckKeyUsage, EE_none)
{
// The input Input is nullptr. This means the cert had no keyUsage
// extension. This is always valid because no key usage in an end-entity
// means that there are no key usage restrictions.
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::noParticularKeyUsageRequired));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::digitalSignature));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::nonRepudiation));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::keyEncipherment));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::dataEncipherment));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::keyAgreement));
}
TEST_F(pkixcheck_CheckKeyUsage, EE_empty)
{
// The input Input is empty. The cert had an empty keyUsage extension,
// which is syntactically invalid.
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null,
KeyUsage::digitalSignature));
static const uint8_t dummy = 0x00;
Input empty_nonnull;
ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull,
KeyUsage::digitalSignature));
}
TEST_F(pkixcheck_CheckKeyUsage, CA_none)
{
// A CA certificate does not have a KU extension.
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyUsage::keyCertSign));
}
TEST_F(pkixcheck_CheckKeyUsage, CA_empty)
{
// A CA certificate has an empty KU extension.
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_null,
KeyUsage::keyCertSign));
static const uint8_t dummy = 0x00;
Input empty_nonnull;
ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull,
KeyUsage::keyCertSign));
}
TEST_F(pkixcheck_CheckKeyUsage, maxUnusedBits)
{
NAMED_SIMPLE_KU(encoded, 7, 0x80);
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded,
KeyUsage::digitalSignature));
}
TEST_F(pkixcheck_CheckKeyUsage, tooManyUnusedBits)
{
static uint8_t oneValueByteData[] = {
0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 8/*unused bits*/, 0x80
};
static const Input oneValueByte(oneValueByteData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte,
KeyUsage::digitalSignature));
static uint8_t twoValueBytesData[] = {
0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 8/*unused bits*/, 0x01, 0x00
};
static const Input twoValueBytes(twoValueBytesData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes,
KeyUsage::digitalSignature));
}
TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_NoPaddingBits)
{
static const uint8_t DER_BYTES[] = {
0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 0/*unused bits*/
};
static const Input DER(DER_BYTES);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER,
KeyUsage::digitalSignature));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER,
KeyUsage::keyCertSign));
}
TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_7PaddingBits)
{
static const uint8_t DER_BYTES[] = {
0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 7/*unused bits*/
};
static const Input DER(DER_BYTES);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER,
KeyUsage::digitalSignature));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER,
KeyUsage::keyCertSign));
}
void ASSERT_SimpleCase(uint8_t unusedBits, uint8_t bits, KeyUsage usage)
{
// Test that only the right bit is accepted for the usage for both EE and CA
// certs.
NAMED_SIMPLE_KU(good, unusedBits, bits);
ASSERT_EQ(Success,
CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, usage));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, usage));
// We use (~bits >> unusedBits) << unusedBits) instead of using the same
// calculation that is in CheckKeyUsage to validate that the calculation in
// CheckKeyUsage is correct.
// 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));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &notGood, usage));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood, usage));
// Test that none of the other non-padding bits are mistaken for the given
// key usage in the two-byte value case.
const uint8_t twoByteNotGoodData[] = {
0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, unusedBits,
static_cast<uint8_t>(~bits),
static_cast<uint8_t>((0xFFu >> unusedBits) << unusedBits)
};
Input twoByteNotGood(twoByteNotGoodData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
usage));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood, usage));
}
TEST_F(pkixcheck_CheckKeyUsage, simpleCases)
{
ASSERT_SimpleCase(7, 0x80, KeyUsage::digitalSignature);
ASSERT_SimpleCase(6, 0x40, KeyUsage::nonRepudiation);
ASSERT_SimpleCase(5, 0x20, KeyUsage::keyEncipherment);
ASSERT_SimpleCase(4, 0x10, KeyUsage::dataEncipherment);
ASSERT_SimpleCase(3, 0x08, KeyUsage::keyAgreement);
}
// Only CAs are allowed to assert keyCertSign.
// End-entity certs may assert it along with other key usages if keyCertSign
// isn't the required key usage. This is for compatibility.
TEST_F(pkixcheck_CheckKeyUsage, keyCertSign)
{
NAMED_SIMPLE_KU(good, 2, 0x04);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good,
KeyUsage::keyCertSign));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good,
KeyUsage::keyCertSign));
// Test that none of the other non-padding bits are mistaken for the given
// key usage in the one-byte value case.
NAMED_SIMPLE_KU(notGood, 2, 0xFB);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &notGood,
KeyUsage::keyCertSign));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood,
KeyUsage::keyCertSign));
// Test that none of the other non-padding bits are mistaken for the given
// key usage in the two-byte value case.
static uint8_t twoByteNotGoodData[] = {
0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 2/*unused bits*/, 0xFBu, 0xFCu
};
static const Input twoByteNotGood(twoByteNotGoodData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
KeyUsage::keyCertSign));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood,
KeyUsage::keyCertSign));
// If an end-entity certificate does assert keyCertSign, this is allowed
// as long as that isn't the required key usage.
NAMED_SIMPLE_KU(digitalSignatureAndKeyCertSign, 2, 0x84);
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
&digitalSignatureAndKeyCertSign,
KeyUsage::digitalSignature));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
&digitalSignatureAndKeyCertSign,
KeyUsage::keyCertSign));
}
TEST_F(pkixcheck_CheckKeyUsage, unusedBitNotZero)
{
// single byte control case
static uint8_t controlOneValueByteData[] = {
0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80
};
static const Input controlOneValueByte(controlOneValueByteData);
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
&controlOneValueByte,
KeyUsage::digitalSignature));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA,
&controlOneValueByte,
KeyUsage::digitalSignature));
// single-byte test case
static uint8_t oneValueByteData[] = {
0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 | 0x01
};
static const Input oneValueByte(oneValueByteData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte,
KeyUsage::digitalSignature));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &oneValueByte,
KeyUsage::digitalSignature));
// two-byte control case
static uint8_t controlTwoValueBytesData[] = {
0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
0x80 | 0x01, 0x80
};
static const Input controlTwoValueBytes(controlTwoValueBytesData);
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
&controlTwoValueBytes,
KeyUsage::digitalSignature));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA,
&controlTwoValueBytes,
KeyUsage::digitalSignature));
// two-byte test case
static uint8_t twoValueBytesData[] = {
0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
0x80 | 0x01, 0x80 | 0x01
};
static const Input twoValueBytes(twoValueBytesData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes,
KeyUsage::digitalSignature));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoValueBytes,
KeyUsage::digitalSignature));
}

Просмотреть файл

@ -0,0 +1,367 @@
/* -*- 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 2015 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 "pkixgtest.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
namespace mozilla { namespace pkix {
extern Result CheckSignatureAlgorithm(
TrustDomain& trustDomain, EndEntityOrCA endEntityOrCA,
Time notBefore,
const der::SignedDataWithSignature& signedData,
Input signatureValue);
} } // namespace mozilla::pkix
struct CheckSignatureAlgorithmTestParams
{
ByteString signatureAlgorithmValue;
ByteString signatureValue;
unsigned int signatureLengthInBytes;
Result expectedResult;
};
::std::ostream& operator<<(::std::ostream& os,
const CheckSignatureAlgorithmTestParams&)
{
return os << "TODO (bug 1318770)";
}
#define BS(s) ByteString(s, MOZILLA_PKIX_ARRAY_LENGTH(s))
// python DottedOIDToCode.py --tlv sha256WithRSAEncryption 1.2.840.113549.1.1.11
static const uint8_t tlv_sha256WithRSAEncryption[] = {
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
};
// Same as tlv_sha256WithRSAEncryption, except one without the "0x0b" and with
// the DER length decreased accordingly.
static const uint8_t tlv_sha256WithRSAEncryption_truncated[] = {
0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01
};
// python DottedOIDToCode.py --tlv sha-1WithRSAEncryption 1.2.840.113549.1.1.5
static const uint8_t tlv_sha_1WithRSAEncryption[] = {
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05
};
// python DottedOIDToCode.py --tlv sha1WithRSASignature 1.3.14.3.2.29
static const uint8_t tlv_sha1WithRSASignature[] = {
0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d
};
// python DottedOIDToCode.py --tlv md5WithRSAEncryption 1.2.840.113549.1.1.4
static const uint8_t tlv_md5WithRSAEncryption[] = {
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04
};
static const CheckSignatureAlgorithmTestParams
CHECKSIGNATUREALGORITHM_TEST_PARAMS[] =
{
{ // Both algorithm IDs are empty
ByteString(),
ByteString(),
2048 / 8,
Result::ERROR_BAD_DER,
},
{ // signatureAlgorithm is empty, signature is supported.
ByteString(),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Result::ERROR_BAD_DER,
},
{ // signatureAlgorithm is supported, signature is empty.
BS(tlv_sha256WithRSAEncryption),
ByteString(),
2048 / 8,
Result::ERROR_BAD_DER,
},
{ // Algorithms match, both are supported.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Success
},
{ // Algorithms do not match because signatureAlgorithm is truncated.
BS(tlv_sha256WithRSAEncryption_truncated),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // Algorithms do not match because signature is truncated.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption_truncated),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // Algorithms do not match, both are supported.
BS(tlv_sha_1WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH,
},
{ // Algorithms do not match, both are supported.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha_1WithRSAEncryption),
2048 / 8,
Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH,
},
{ // Algorithms match, both are unsupported.
BS(tlv_md5WithRSAEncryption),
BS(tlv_md5WithRSAEncryption),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // signatureAlgorithm is unsupported, signature is supported.
BS(tlv_md5WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // signatureAlgorithm is supported, signature is unsupported.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_md5WithRSAEncryption),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // Both have the optional NULL parameter.
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
2048 / 8,
Success
},
{ // signatureAlgorithm has the optional NULL parameter, signature doesn't.
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Success
},
{ // signatureAlgorithm does not have the optional NULL parameter, signature
// does.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
2048 / 8,
Success
},
{ // The different OIDs for RSA-with-SHA1 we support are semantically
// equivalent.
BS(tlv_sha1WithRSASignature),
BS(tlv_sha_1WithRSAEncryption),
2048 / 8,
Success,
},
{ // The different OIDs for RSA-with-SHA1 we support are semantically
// equivalent (opposite order).
BS(tlv_sha_1WithRSAEncryption),
BS(tlv_sha1WithRSASignature),
2048 / 8,
Success,
},
{ // Algorithms match, both are supported, key size is not a multile of 128
// bits. This test verifies that we're not wrongly rounding up the
// signature size like we did in the original patch for bug 1131767.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption),
(2048 / 8) - 1,
Success
},
};
class pkixcheck_CheckSignatureAlgorithm
: public ::testing::Test
, public ::testing::WithParamInterface<CheckSignatureAlgorithmTestParams>
{
};
class pkixcheck_CheckSignatureAlgorithm_TrustDomain final
: public EverythingFailsByDefaultTrustDomain
{
public:
explicit pkixcheck_CheckSignatureAlgorithm_TrustDomain(
unsigned int aPublicKeySizeInBits)
: publicKeySizeInBits(aPublicKeySizeInBits)
, checkedDigestAlgorithm(false)
, checkedModulusSizeInBits(false)
{
}
Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time)
override
{
checkedDigestAlgorithm = true;
return Success;
}
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA endEntityOrCA,
unsigned int modulusSizeInBits)
override
{
EXPECT_EQ(EndEntityOrCA::MustBeEndEntity, endEntityOrCA);
EXPECT_EQ(publicKeySizeInBits, modulusSizeInBits);
checkedModulusSizeInBits = true;
return Success;
}
const unsigned int publicKeySizeInBits;
bool checkedDigestAlgorithm;
bool checkedModulusSizeInBits;
};
TEST_P(pkixcheck_CheckSignatureAlgorithm, CheckSignatureAlgorithm)
{
const Time now(Now());
const CheckSignatureAlgorithmTestParams& params(GetParam());
Input signatureValueInput;
ASSERT_EQ(Success,
signatureValueInput.Init(params.signatureValue.data(),
params.signatureValue.length()));
pkixcheck_CheckSignatureAlgorithm_TrustDomain
trustDomain(params.signatureLengthInBytes * 8);
der::SignedDataWithSignature signedData;
ASSERT_EQ(Success,
signedData.algorithm.Init(params.signatureAlgorithmValue.data(),
params.signatureAlgorithmValue.length()));
ByteString dummySignature(params.signatureLengthInBytes, 0xDE);
ASSERT_EQ(Success,
signedData.signature.Init(dummySignature.data(),
dummySignature.length()));
ASSERT_EQ(params.expectedResult,
CheckSignatureAlgorithm(trustDomain, EndEntityOrCA::MustBeEndEntity,
now, signedData, signatureValueInput));
ASSERT_EQ(params.expectedResult == Success,
trustDomain.checkedDigestAlgorithm);
ASSERT_EQ(params.expectedResult == Success,
trustDomain.checkedModulusSizeInBits);
}
INSTANTIATE_TEST_CASE_P(
pkixcheck_CheckSignatureAlgorithm, pkixcheck_CheckSignatureAlgorithm,
testing::ValuesIn(CHECKSIGNATUREALGORITHM_TEST_PARAMS));
class pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain
: public DefaultCryptoTrustDomain
{
public:
explicit pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain(
const ByteString& aIssuer)
: issuer(aIssuer)
{
}
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
Input cert, /*out*/ TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(cert, issuer)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
EXPECT_FALSE(ENCODING_FAILED(issuer));
Input issuerInput;
EXPECT_EQ(Success, issuerInput.Init(issuer.data(), issuer.length()));
bool keepGoing;
EXPECT_EQ(Success, checker.Check(issuerInput, nullptr, keepGoing));
EXPECT_FALSE(keepGoing);
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*,
/*optional*/ const Input*) override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
ByteString issuer;
};
// Test that CheckSignatureAlgorithm actually gets called at some point when
// BuildCertChain is called.
TEST_F(pkixcheck_CheckSignatureAlgorithm, BuildCertChain)
{
ScopedTestKeyPair keyPair(CloneReusedKeyPair());
ASSERT_TRUE(keyPair.get());
ByteString issuerExtensions[2];
issuerExtensions[0] = CreateEncodedBasicConstraints(true, nullptr,
Critical::No);
ASSERT_FALSE(ENCODING_FAILED(issuerExtensions[0]));
ByteString issuer(CreateEncodedCertificate(3,
sha256WithRSAEncryption(),
CreateEncodedSerialNumber(1),
CNToDERName("issuer"),
oneDayBeforeNow, oneDayAfterNow,
CNToDERName("issuer"),
*keyPair,
issuerExtensions,
*keyPair,
sha256WithRSAEncryption()));
ASSERT_FALSE(ENCODING_FAILED(issuer));
ByteString subject(CreateEncodedCertificate(3,
sha1WithRSAEncryption(),
CreateEncodedSerialNumber(2),
CNToDERName("issuer"),
oneDayBeforeNow, oneDayAfterNow,
CNToDERName("subject"),
*keyPair,
nullptr,
*keyPair,
sha256WithRSAEncryption()));
ASSERT_FALSE(ENCODING_FAILED(subject));
Input subjectInput;
ASSERT_EQ(Success, subjectInput.Init(subject.data(), subject.length()));
pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain
trustDomain(issuer);
Result rv = BuildCertChain(trustDomain, subjectInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr);
ASSERT_EQ(Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH, rv);
}

Просмотреть файл

@ -0,0 +1,128 @@
/* -*- 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 2014 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 "pkixgtest.h"
#include "mozpkix/pkixcheck.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
static const Time PAST_TIME(YMDHMS(1998, 12, 31, 12, 23, 56));
#define OLDER_GENERALIZEDTIME \
0x18, 15, /* tag, length */ \
'1', '9', '9', '9', '0', '1', '0', '1', /* 1999-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
#define OLDER_UTCTIME \
0x17, 13, /* tag, length */ \
'9', '9', '0', '1', '0', '1', /* (19)99-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
static const Time NOW(YMDHMS(2016, 12, 31, 12, 23, 56));
#define NEWER_GENERALIZEDTIME \
0x18, 15, /* tag, length */ \
'2', '0', '2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
#define NEWER_UTCTIME \
0x17, 13, /* tag, length */ \
'2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
static const Time FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
class pkixcheck_CheckValidity : public ::testing::Test { };
static const uint8_t OLDER_UTCTIME_NEWER_UTCTIME_DATA[] = {
OLDER_UTCTIME,
NEWER_UTCTIME,
};
static const Input
OLDER_UTCTIME_NEWER_UTCTIME(OLDER_UTCTIME_NEWER_UTCTIME_DATA);
TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_UTCTIME)
{
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(OLDER_UTCTIME_NEWER_UTCTIME, &notBefore, &notAfter));
ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_GENERALIZEDTIME)
{
static const uint8_t DER[] = {
OLDER_GENERALIZEDTIME,
NEWER_GENERALIZEDTIME,
};
static const Input validity(DER);
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(validity, &notBefore, &notAfter));
ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_UTCTIME)
{
static const uint8_t DER[] = {
OLDER_GENERALIZEDTIME,
NEWER_UTCTIME,
};
static const Input validity(DER);
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(validity, &notBefore, &notAfter));
ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_GENERALIZEDTIME)
{
static const uint8_t DER[] = {
OLDER_UTCTIME,
NEWER_GENERALIZEDTIME,
};
static const Input validity(DER);
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(validity, &notBefore, &notAfter));
ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, InvalidBeforeNotBefore)
{
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(OLDER_UTCTIME_NEWER_UTCTIME, &notBefore, &notAfter));
ASSERT_EQ(Result::ERROR_NOT_YET_VALID_CERTIFICATE, CheckValidity(PAST_TIME, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, InvalidAfterNotAfter)
{
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(OLDER_UTCTIME_NEWER_UTCTIME, &notBefore, &notAfter));
ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(FUTURE_TIME, notBefore, notAfter));
}

Просмотреть файл

@ -0,0 +1,84 @@
/* -*- 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 2014 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 "pkixgtest.h"
#include "mozpkix/pkixcheck.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
#define OLDER_UTCTIME \
0x17, 13, /* tag, length */ \
'9', '9', '0', '1', '0', '1', /* (19)99-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
#define NEWER_UTCTIME \
0x17, 13, /* tag, length */ \
'2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
static const Time FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
class pkixcheck_ParseValidity : public ::testing::Test { };
TEST_F(pkixcheck_ParseValidity, BothEmptyNull)
{
static const uint8_t DER[] = {
0x17/*UTCTime*/, 0/*length*/,
0x17/*UTCTime*/, 0/*length*/,
};
static const Input validity(DER);
ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
}
TEST_F(pkixcheck_ParseValidity, NotBeforeEmptyNull)
{
static const uint8_t DER[] = {
0x17/*UTCTime*/, 0x00/*length*/,
NEWER_UTCTIME
};
static const Input validity(DER);
ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
}
TEST_F(pkixcheck_ParseValidity, NotAfterEmptyNull)
{
static const uint8_t DER[] = {
NEWER_UTCTIME,
0x17/*UTCTime*/, 0x00/*length*/,
};
static const Input validity(DER);
ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
}
TEST_F(pkixcheck_ParseValidity, InvalidNotAfterBeforeNotBefore)
{
static const uint8_t DER[] = {
NEWER_UTCTIME,
OLDER_UTCTIME,
};
static const Input validity(DER);
ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
}

Просмотреть файл

@ -0,0 +1,120 @@
/* -*- 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 2015 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 "pkixgtest.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
namespace mozilla { namespace pkix {
extern Result TLSFeaturesSatisfiedInternal(const Input* requiredTLSFeatures,
const Input* stapledOCSPResponse);
} } // namespace mozilla::pkix
struct TLSFeaturesTestParams
{
ByteString requiredTLSFeatures;
Result expectedResultWithResponse;
Result expectedResultWithoutResponse;
};
::std::ostream& operator<<(::std::ostream& os, const TLSFeaturesTestParams&)
{
return os << "TODO (bug 1318770)";
}
#define BS(s) ByteString(s, MOZILLA_PKIX_ARRAY_LENGTH(s))
static const uint8_t statusRequest[] = {
0x30, 0x03, 0x02, 0x01, 0x05
};
static const uint8_t unknown[] = {
0x30, 0x03, 0x02, 0x01, 0x06
};
static const uint8_t statusRequestAndUnknown[] = {
0x30, 0x06, 0x02, 0x01, 0x05, 0x02, 0x01, 0x06
};
static const uint8_t duplicateStatusRequest[] = {
0x30, 0x06, 0x02, 0x01, 0x05, 0x02, 0x01, 0x05
};
static const uint8_t twoByteUnknown[] = {
0x30, 0x04, 0x02, 0x02, 0x05, 0x05
};
static const uint8_t zeroByteInteger[] = {
0x30, 0x02, 0x02, 0x00
};
static const TLSFeaturesTestParams
TLSFEATURESSATISFIED_TEST_PARAMS[] =
{
// some tests with checks enforced
{ ByteString(), Result::ERROR_BAD_DER, Result::ERROR_BAD_DER },
{ BS(statusRequest), Success, Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(unknown), Result::ERROR_REQUIRED_TLS_FEATURE_MISSING,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(statusRequestAndUnknown), Result::ERROR_REQUIRED_TLS_FEATURE_MISSING,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(duplicateStatusRequest), Success,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(twoByteUnknown), Result::ERROR_REQUIRED_TLS_FEATURE_MISSING,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(zeroByteInteger), Result::ERROR_REQUIRED_TLS_FEATURE_MISSING,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
};
class pkixcheck_TLSFeaturesSatisfiedInternal
: public ::testing::Test
, public ::testing::WithParamInterface<TLSFeaturesTestParams>
{
};
TEST_P(pkixcheck_TLSFeaturesSatisfiedInternal, TLSFeaturesSatisfiedInternal) {
const TLSFeaturesTestParams& params(GetParam());
Input featuresInput;
ASSERT_EQ(Success, featuresInput.Init(params.requiredTLSFeatures.data(),
params.requiredTLSFeatures.length()));
Input responseInput;
// just create an input with any data in it
ByteString stapledOCSPResponse = BS(statusRequest);
ASSERT_EQ(Success, responseInput.Init(stapledOCSPResponse.data(),
stapledOCSPResponse.length()));
// first we omit the response
ASSERT_EQ(params.expectedResultWithoutResponse,
TLSFeaturesSatisfiedInternal(&featuresInput, nullptr));
// then we try again with the response
ASSERT_EQ(params.expectedResultWithResponse,
TLSFeaturesSatisfiedInternal(&featuresInput, &responseInput));
}
INSTANTIATE_TEST_CASE_P(
pkixcheck_TLSFeaturesSatisfiedInternal,
pkixcheck_TLSFeaturesSatisfiedInternal,
testing::ValuesIn(TLSFEATURESSATISFIED_TEST_PARAMS));

Просмотреть файл

@ -0,0 +1,920 @@
/* -*- 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 <functional>
#include <vector>
#include "pkixgtest.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::der;
namespace {
class pkixder_input_tests : public ::testing::Test { };
static const uint8_t DER_SEQUENCE_EMPTY[] = {
0x30, // SEQUENCE
0x00, // length
};
static const uint8_t DER_SEQUENCE_NOT_EMPTY[] = {
0x30, // SEQUENCE
0x01, // length
'X', // value
};
static const uint8_t DER_SEQUENCE_NOT_EMPTY_VALUE[] = {
'X', // value
};
static const uint8_t DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED[] = {
0x30, // SEQUENCE
0x01, // length
};
const uint8_t DER_SEQUENCE_OF_INT8[] = {
0x30, // SEQUENCE
0x09, // length
0x02, 0x01, 0x01, // INTEGER length 1 value 0x01
0x02, 0x01, 0x02, // INTEGER length 1 value 0x02
0x02, 0x01, 0x03 // INTEGER length 1 value 0x03
};
const uint8_t DER_TRUNCATED_SEQUENCE_OF_INT8[] = {
0x30, // SEQUENCE
0x09, // length
0x02, 0x01, 0x01, // INTEGER length 1 value 0x01
0x02, 0x01, 0x02 // INTEGER length 1 value 0x02
// MISSING DATA HERE ON PURPOSE
};
const uint8_t DER_OVERRUN_SEQUENCE_OF_INT8[] = {
0x30, // SEQUENCE
0x09, // length
0x02, 0x01, 0x01, // INTEGER length 1 value 0x01
0x02, 0x01, 0x02, // INTEGER length 1 value 0x02
0x02, 0x02, 0xFF, 0x03 // INTEGER length 2 value 0xFF03
};
const uint8_t DER_INT16[] = {
0x02, // INTEGER
0x02, // length
0x12, 0x34 // 0x1234
};
static const Input EMPTY_INPUT;
TEST_F(pkixder_input_tests, InputInit)
{
Input buf;
ASSERT_EQ(Success,
buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
}
TEST_F(pkixder_input_tests, InputInitWithNullPointerOrZeroLength)
{
Input buf;
ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init(nullptr, 0));
ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init(nullptr, 100));
// Though it seems odd to initialize with zero-length and non-null ptr, this
// is working as intended. The Reader class was intended to protect against
// buffer overflows, and there's no risk with the current behavior. See bug
// 1000354.
ASSERT_EQ(Success, buf.Init((const uint8_t*) "hello", 0));
ASSERT_TRUE(buf.GetLength() == 0);
}
TEST_F(pkixder_input_tests, InputInitWithLargeData)
{
Input buf;
// Data argument length does not matter, it is not touched, just
// needs to be non-null
ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init((const uint8_t*) "", 0xffff+1));
ASSERT_EQ(Success, buf.Init((const uint8_t*) "", 0xffff));
}
TEST_F(pkixder_input_tests, InputInitMultipleTimes)
{
Input buf;
ASSERT_EQ(Success,
buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS,
buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
}
TEST_F(pkixder_input_tests, PeekWithinBounds)
{
const uint8_t der[] = { 0x11, 0x11 };
Input buf(der);
Reader input(buf);
ASSERT_TRUE(input.Peek(0x11));
ASSERT_FALSE(input.Peek(0x22));
}
TEST_F(pkixder_input_tests, PeekPastBounds)
{
const uint8_t der[] = { 0x11, 0x22 };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 1));
Reader input(buf);
uint8_t readByte;
ASSERT_EQ(Success, input.Read(readByte));
ASSERT_EQ(0x11, readByte);
ASSERT_FALSE(input.Peek(0x22));
}
TEST_F(pkixder_input_tests, ReadByte)
{
const uint8_t der[] = { 0x11, 0x22 };
Input buf(der);
Reader input(buf);
uint8_t readByte1;
ASSERT_EQ(Success, input.Read(readByte1));
ASSERT_EQ(0x11, readByte1);
uint8_t readByte2;
ASSERT_EQ(Success, input.Read(readByte2));
ASSERT_EQ(0x22, readByte2);
}
TEST_F(pkixder_input_tests, ReadBytePastEnd)
{
const uint8_t der[] = { 0x11, 0x22 };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 1));
Reader input(buf);
uint8_t readByte1 = 0;
ASSERT_EQ(Success, input.Read(readByte1));
ASSERT_EQ(0x11, readByte1);
uint8_t readByte2 = 0;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readByte2));
ASSERT_NE(0x22, readByte2);
}
TEST_F(pkixder_input_tests, ReadByteWrapAroundPointer)
{
// The original implementation of our buffer read overflow checks was
// susceptible to integer overflows which could make the checks ineffective.
// This attempts to verify that we've fixed that. Unfortunately, decrementing
// 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;
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));
Reader input(buf);
uint8_t b;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
}
TEST_F(pkixder_input_tests, ReadWord)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
uint16_t readWord1 = 0;
ASSERT_EQ(Success, input.Read(readWord1));
ASSERT_EQ(0x1122, readWord1);
uint16_t readWord2 = 0;
ASSERT_EQ(Success, input.Read(readWord2));
ASSERT_EQ(0x3344, readWord2);
}
TEST_F(pkixder_input_tests, ReadWordPastEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 2)); // Initialize with too-short length
Reader input(buf);
uint16_t readWord1 = 0;
ASSERT_EQ(Success, input.Read(readWord1));
ASSERT_EQ(0x1122, readWord1);
uint16_t readWord2 = 0;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord2));
ASSERT_NE(0x3344, readWord2);
}
TEST_F(pkixder_input_tests, ReadWordWithInsufficentData)
{
const uint8_t der[] = { 0x11, 0x22 };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 1));
Reader input(buf);
uint16_t readWord1 = 0;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord1));
ASSERT_NE(0x1122, readWord1);
}
TEST_F(pkixder_input_tests, ReadWordWrapAroundPointer)
{
// The original implementation of our buffer read overflow checks was
// susceptible to integer overflows which could make the checks ineffective.
// This attempts to verify that we've fixed that. Unfortunately, decrementing
// 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;
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));
Reader input(buf);
uint16_t b;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
}
TEST_F(pkixder_input_tests, Skip)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(1));
uint8_t readByte1 = 0;
ASSERT_EQ(Success, input.Read(readByte1));
ASSERT_EQ(0x22, readByte1);
ASSERT_EQ(Success, input.Skip(1));
uint8_t readByte2 = 0;
ASSERT_EQ(Success, input.Read(readByte2));
ASSERT_EQ(0x44, readByte2);
}
TEST_F(pkixder_input_tests, Skip_ToEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(sizeof der));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, Skip_PastEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1));
}
TEST_F(pkixder_input_tests, Skip_ToNewInput)
{
const uint8_t der[] = { 0x01, 0x02, 0x03, 0x04 };
Input buf(der);
Reader input(buf);
Reader skippedInput;
ASSERT_EQ(Success, input.Skip(3, skippedInput));
uint8_t readByte1 = 0;
ASSERT_EQ(Success, input.Read(readByte1));
ASSERT_EQ(0x04, readByte1);
ASSERT_TRUE(input.AtEnd());
// Reader has no Remaining() or Length() so we simply read the bytes
// and then expect to be at the end.
for (uint8_t i = 1; i <= 3; ++i) {
uint8_t readByte = 0;
ASSERT_EQ(Success, skippedInput.Read(readByte));
ASSERT_EQ(i, readByte);
}
ASSERT_TRUE(skippedInput.AtEnd());
}
TEST_F(pkixder_input_tests, Skip_ToNewInputPastEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Reader skippedInput;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der * 2, skippedInput));
}
TEST_F(pkixder_input_tests, Skip_ToInput)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33 };
Input item;
ASSERT_EQ(Success, input.Skip(sizeof expectedItemData, item));
Input expected(expectedItemData);
ASSERT_TRUE(InputsAreEqual(expected, item));
}
TEST_F(pkixder_input_tests, Skip_WrapAroundPointer)
{
// The original implementation of our buffer read overflow checks was
// susceptible to integer overflows which could make the checks ineffective.
// This attempts to verify that we've fixed that. Unfortunately, decrementing
// 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;
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(1));
}
TEST_F(pkixder_input_tests, Skip_ToInputPastEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Input skipped;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1, skipped));
}
TEST_F(pkixder_input_tests, SkipToEnd_ToInput)
{
static const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Input skipped;
ASSERT_EQ(Success, input.SkipToEnd(skipped));
}
TEST_F(pkixder_input_tests, SkipToEnd_ToInput_InputAlreadyInited)
{
static const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
static const uint8_t initialValue[] = { 0x01, 0x02, 0x03 };
Input x(initialValue);
// Fails because skipped was already initialized once, and Inputs are not
// allowed to be Init()d multiple times.
ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, input.SkipToEnd(x));
ASSERT_TRUE(InputsAreEqual(x, Input(initialValue)));
}
TEST_F(pkixder_input_tests, ExpectTagAndSkipValue)
{
Input buf(DER_SEQUENCE_OF_INT8);
Reader input(buf);
ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
ASSERT_EQ(Success, End(input));
}
TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithTruncatedData)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndSkipValue(input, SEQUENCE));
}
TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithOverrunData)
{
Input buf(DER_OVERRUN_SEQUENCE_OF_INT8);
Reader input(buf);
ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
}
TEST_F(pkixder_input_tests, AtEndOnUnInitializedInput)
{
Reader input;
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, AtEndAtBeginning)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_FALSE(input.AtEnd());
}
TEST_F(pkixder_input_tests, AtEndAtEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(sizeof der));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, MarkAndGetInput)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Reader::Mark mark = input.GetMark();
const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33 };
ASSERT_EQ(Success, input.Skip(sizeof expectedItemData));
Input item;
ASSERT_EQ(Success, input.GetInput(mark, item));
Input expected(expectedItemData);
ASSERT_TRUE(InputsAreEqual(expected, item));
}
// Cannot run this test on debug builds because of the NotReached
#ifdef NDEBUG
TEST_F(pkixder_input_tests, MarkAndGetInputDifferentInput)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Reader another;
Reader::Mark mark = another.GetMark();
ASSERT_EQ(Success, input.Skip(3));
Input item;
ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, input.GetInput(mark, item));
}
#endif
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_AtEnd)
{
Reader input(EMPTY_INPUT);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER, ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_TruncatedAfterTag)
{
static const uint8_t DER[] = { SEQUENCE };
Input buf(DER);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER, ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
uint8_t tag = 0;
Input value;
ASSERT_EQ(Success, ReadTagAndGetValue(input, tag, value));
ASSERT_EQ(SEQUENCE, tag);
ASSERT_EQ(0u, value.GetLength());
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_ValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
uint8_t tag = 0;
Input value;
ASSERT_EQ(Success, ReadTagAndGetValue(input, tag, value));
ASSERT_EQ(SEQUENCE, tag);
Input expected(DER_SEQUENCE_NOT_EMPTY_VALUE);
ASSERT_TRUE(InputsAreEqual(expected, value));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests,
ReadTagAndGetValue_Input_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER, ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidHighTagNumberForm1)
{
// High tag number form is not allowed (illegal 1 byte tag)
//
// If the decoder treats 0x1F as a valid low tag number tag, then it will
// treat the actual tag (1) as a length, and then it will return Success
// with value == { 0x00 } and tag == 0x1f.
//
// It is illegal to encode tag 1 in the high tag number form because it isn't
// the shortest encoding (the low tag number form is).
static const uint8_t DER[] = {
0x1F, // high tag number form indicator
1, // tag 1 (not legal!)
0 // length zero
};
Input buf(DER);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidHighTagNumberForm2)
{
// High tag number form is not allowed (legal 1 byte tag).
//
// ReadTagAndGetValue's check to prohibit the high tag number form has no
// effect on whether this test passes or fails, because ReadTagAndGetValue
// will interpret the second byte (31) as a length, and the input doesn't
// have 31 bytes following it. This test is here to guard against the case
// where somebody actually implements high tag number form parsing, to remind
// that person that they need to add tests here, including in particular
// tests for overly-long encodings.
static const uint8_t DER[] = {
0x1F, // high tag number form indicator
31, // tag 31
0 // length zero
};
Input buf(DER);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidHighTagNumberForm3)
{
// High tag number form is not allowed (2 byte legal tag)
//
// ReadTagAndGetValue's check to prohibit the high tag number form has no
// effect on whether this test passes or fails, because ReadTagAndGetValue
// will interpret the second byte as a length, and the input doesn't have
// that many bytes following it. This test is here to guard against the case
// where somebody actually implements high tag number form parsing, to remind
// that person that they need to add tests here, including in particular
// tests for overly-long encodings.
static const uint8_t DER[] = {
0x1F, // high tag number form indicator
0x80 | 0x01, 0x00, // tag 0x100 (256)
0 // length zero
};
Input buf(DER);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
Reader value;
ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
ASSERT_TRUE(value.AtEnd());
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_ValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Reader value;
ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
ASSERT_TRUE(value.MatchRest(DER_SEQUENCE_NOT_EMPTY_VALUE));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests,
ExpectTagAndGetValue_Reader_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
Reader value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, SEQUENCE, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
Reader value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, SEQUENCE, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_InvalidWrongTag)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Reader value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, INTEGER, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
Input value;
ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
ASSERT_EQ(0u, value.GetLength());
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Input value;
ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
Input expected(DER_SEQUENCE_NOT_EMPTY_VALUE);
ASSERT_TRUE(InputsAreEqual(expected, value));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests,
ExpectTagAndGetValue_Input_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, SEQUENCE, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, SEQUENCE, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidWrongTag)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, INTEGER, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
ASSERT_EQ(Success, ExpectTagAndEmptyValue(input, SEQUENCE));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_InValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, SEQUENCE));
}
TEST_F(pkixder_input_tests,
ExpectTagAndEmptyValue_Input_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, SEQUENCE));
}
TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, SEQUENCE));
}
TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_InvalidWrongTag)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, INTEGER));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
Input tlv;
ASSERT_EQ(Success, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
Input expected(DER_SEQUENCE_EMPTY);
ASSERT_TRUE(InputsAreEqual(expected, tlv));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_ValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Input tlv;
ASSERT_EQ(Success, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
Input expected(DER_SEQUENCE_NOT_EMPTY);
ASSERT_TRUE(InputsAreEqual(expected, tlv));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests,
ExpectTagAndGetTLV_Input_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
Input tlv;
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
Input tlv;
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_InvalidWrongTag)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Input tlv;
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, INTEGER, tlv));
}
TEST_F(pkixder_input_tests, EndAtEnd)
{
Input buf(DER_INT16);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(4));
ASSERT_EQ(Success, End(input));
}
TEST_F(pkixder_input_tests, EndBeforeEnd)
{
Input buf(DER_INT16);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(2));
ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
}
TEST_F(pkixder_input_tests, EndAtBeginning)
{
Input buf(DER_INT16);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
}
// TODO: Need tests for Nested too?
Result NestedOfHelper(Reader& input, std::vector<uint8_t>& readValues)
{
uint8_t value = 0;
Result rv = input.Read(value);
EXPECT_EQ(Success, rv);
if (rv != Success) {
return rv;
}
readValues.push_back(value);
return Success;
}
TEST_F(pkixder_input_tests, NestedOf)
{
Input buf(DER_SEQUENCE_OF_INT8);
Reader input(buf);
std::vector<uint8_t> readValues;
ASSERT_EQ(Success,
NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
[&readValues](Reader& r) {
return NestedOfHelper(r, readValues);
}));
ASSERT_EQ(3u, readValues.size());
ASSERT_EQ(0x01, readValues[0]);
ASSERT_EQ(0x02, readValues[1]);
ASSERT_EQ(0x03, readValues[2]);
ASSERT_EQ(Success, End(input));
}
TEST_F(pkixder_input_tests, NestedOfWithTruncatedData)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
std::vector<uint8_t> readValues;
ASSERT_EQ(Result::ERROR_BAD_DER,
NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
[&readValues](Reader& r) {
return NestedOfHelper(r, readValues);
}));
ASSERT_EQ(0u, readValues.size());
}
TEST_F(pkixder_input_tests, MatchRestAtEnd)
{
static const uint8_t der[1] = { };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));
Reader input(buf);
ASSERT_TRUE(input.AtEnd());
static const uint8_t toMatch[] = { 1 };
ASSERT_FALSE(input.MatchRest(toMatch));
}
TEST_F(pkixder_input_tests, MatchRest1Match)
{
static const uint8_t der[] = { 1 };
Input buf(der);
Reader input(buf);
ASSERT_FALSE(input.AtEnd());
ASSERT_TRUE(input.MatchRest(der));
}
TEST_F(pkixder_input_tests, MatchRest1Mismatch)
{
static const uint8_t der[] = { 1 };
Input buf(der);
Reader input(buf);
static const uint8_t toMatch[] = { 2 };
ASSERT_FALSE(input.MatchRest(toMatch));
ASSERT_FALSE(input.AtEnd());
}
TEST_F(pkixder_input_tests, MatchRest2WithTrailingByte)
{
static const uint8_t der[] = { 1, 2, 3 };
Input buf(der);
Reader input(buf);
static const uint8_t toMatch[] = { 1, 2 };
ASSERT_FALSE(input.MatchRest(toMatch));
}
TEST_F(pkixder_input_tests, MatchRest2Mismatch)
{
static const uint8_t der[] = { 1, 2, 3 };
Input buf(der);
Reader input(buf);
static const uint8_t toMatchMismatch[] = { 1, 3 };
ASSERT_FALSE(input.MatchRest(toMatchMismatch));
ASSERT_TRUE(input.MatchRest(der));
}
} // namespace

Просмотреть файл

@ -0,0 +1,480 @@
/* -*- 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 <functional>
#include <vector>
#include "pkixgtest.h"
#include "mozpkix/pkixtypes.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::der;
class pkixder_pki_types_tests : public ::testing::Test { };
TEST_F(pkixder_pki_types_tests, CertificateSerialNumber)
{
const uint8_t DER_CERT_SERIAL[] = {
0x02, // INTEGER
8, // length
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
};
Input input(DER_CERT_SERIAL);
Reader reader(input);
Input item;
ASSERT_EQ(Success, CertificateSerialNumber(reader, item));
Input expected;
ASSERT_EQ(Success,
expected.Init(DER_CERT_SERIAL + 2, sizeof DER_CERT_SERIAL - 2));
ASSERT_TRUE(InputsAreEqual(expected, item));
}
TEST_F(pkixder_pki_types_tests, CertificateSerialNumberLongest)
{
const uint8_t DER_CERT_SERIAL_LONGEST[] = {
0x02, // INTEGER
20, // length
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
};
Input input(DER_CERT_SERIAL_LONGEST);
Reader reader(input);
Input item;
ASSERT_EQ(Success, CertificateSerialNumber(reader, item));
Input expected;
ASSERT_EQ(Success,
expected.Init(DER_CERT_SERIAL_LONGEST + 2,
sizeof DER_CERT_SERIAL_LONGEST - 2));
ASSERT_TRUE(InputsAreEqual(expected, item));
}
TEST_F(pkixder_pki_types_tests, CertificateSerialNumberCrazyLong)
{
const uint8_t DER_CERT_SERIAL_CRAZY_LONG[] = {
0x02, // INTEGER
32, // length
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
};
Input input(DER_CERT_SERIAL_CRAZY_LONG);
Reader reader(input);
Input item;
ASSERT_EQ(Success, CertificateSerialNumber(reader, item));
}
TEST_F(pkixder_pki_types_tests, CertificateSerialNumberZeroLength)
{
const uint8_t DER_CERT_SERIAL_ZERO_LENGTH[] = {
0x02, // INTEGER
0x00 // length
};
Input input(DER_CERT_SERIAL_ZERO_LENGTH);
Reader reader(input);
Input item;
ASSERT_EQ(Result::ERROR_INVALID_INTEGER_ENCODING,
CertificateSerialNumber(reader, item));
}
TEST_F(pkixder_pki_types_tests, OptionalVersionV1ExplicitEncodingAllowed)
{
const uint8_t DER_OPTIONAL_VERSION_V1[] = {
0xa0, 0x03, // context specific 0
0x02, 0x01, 0x00 // INTEGER(0)
};
Input input(DER_OPTIONAL_VERSION_V1);
Reader reader(input);
// XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we
// do here for compatibility reasons.
// Version version;
// ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(reader, version));
der::Version version = der::Version::v3;
ASSERT_EQ(Success, OptionalVersion(reader, version));
ASSERT_EQ(der::Version::v1, version);
}
TEST_F(pkixder_pki_types_tests, OptionalVersionV2)
{
const uint8_t DER_OPTIONAL_VERSION_V2[] = {
0xa0, 0x03, // context specific 0
0x02, 0x01, 0x01 // INTEGER(1)
};
Input input(DER_OPTIONAL_VERSION_V2);
Reader reader(input);
der::Version version = der::Version::v1;
ASSERT_EQ(Success, OptionalVersion(reader, version));
ASSERT_EQ(der::Version::v2, version);
}
TEST_F(pkixder_pki_types_tests, OptionalVersionV3)
{
const uint8_t DER_OPTIONAL_VERSION_V3[] = {
0xa0, 0x03, // context specific 0
0x02, 0x01, 0x02 // INTEGER(2)
};
Input input(DER_OPTIONAL_VERSION_V3);
Reader reader(input);
der::Version version = der::Version::v1;
ASSERT_EQ(Success, OptionalVersion(reader, version));
ASSERT_EQ(der::Version::v3, version);
}
TEST_F(pkixder_pki_types_tests, OptionalVersionUnknown)
{
const uint8_t DER_OPTIONAL_VERSION_INVALID[] = {
0xa0, 0x03, // context specific 0
0x02, 0x01, 0x42 // INTEGER(0x42)
};
Input input(DER_OPTIONAL_VERSION_INVALID);
Reader reader(input);
der::Version version = der::Version::v1;
ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(reader, version));
}
TEST_F(pkixder_pki_types_tests, OptionalVersionInvalidTooLong)
{
const uint8_t DER_OPTIONAL_VERSION_INVALID_TOO_LONG[] = {
0xa0, 0x03, // context specific 0
0x02, 0x02, 0x12, 0x34 // INTEGER(0x1234)
};
Input input(DER_OPTIONAL_VERSION_INVALID_TOO_LONG);
Reader reader(input);
der::Version version;
ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(reader, version));
}
TEST_F(pkixder_pki_types_tests, OptionalVersionMissing)
{
const uint8_t DER_OPTIONAL_VERSION_MISSING[] = {
0x02, 0x11, 0x22 // INTEGER
};
Input input(DER_OPTIONAL_VERSION_MISSING);
Reader reader(input);
der::Version version = der::Version::v3;
ASSERT_EQ(Success, OptionalVersion(reader, version));
ASSERT_EQ(der::Version::v1, version);
}
static const size_t MAX_ALGORITHM_OID_DER_LENGTH = 13;
struct InvalidAlgorithmIdentifierTestInfo
{
uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH];
size_t derLength;
};
struct ValidDigestAlgorithmIdentifierTestInfo
{
DigestAlgorithm algorithm;
uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH];
size_t derLength;
};
class pkixder_DigestAlgorithmIdentifier_Valid
: public ::testing::Test
, public ::testing::WithParamInterface<ValidDigestAlgorithmIdentifierTestInfo>
{
};
static const ValidDigestAlgorithmIdentifierTestInfo
VALID_DIGEST_ALGORITHM_TEST_INFO[] =
{
{ DigestAlgorithm::sha512,
{ 0x30, 0x0b, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 },
13
},
{ DigestAlgorithm::sha384,
{ 0x30, 0x0b, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 },
13
},
{ DigestAlgorithm::sha256,
{ 0x30, 0x0b, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 },
13
},
{ DigestAlgorithm::sha1,
{ 0x30, 0x07, 0x06, 0x05,
0x2b, 0x0e, 0x03, 0x02, 0x1a },
9
},
};
TEST_P(pkixder_DigestAlgorithmIdentifier_Valid, Valid)
{
const ValidDigestAlgorithmIdentifierTestInfo& param(GetParam());
{
Input input;
ASSERT_EQ(Success, input.Init(param.der, param.derLength));
Reader reader(input);
DigestAlgorithm alg;
ASSERT_EQ(Success, DigestAlgorithmIdentifier(reader, alg));
ASSERT_EQ(param.algorithm, alg);
ASSERT_EQ(Success, End(reader));
}
{
uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2];
memcpy(derWithNullParam, param.der, param.derLength);
derWithNullParam[1] += 2; // we're going to expand the value by 2 bytes
derWithNullParam[param.derLength] = 0x05; // NULL tag
derWithNullParam[param.derLength + 1] = 0x00; // length zero
Input input;
ASSERT_EQ(Success, input.Init(derWithNullParam, param.derLength + 2));
Reader reader(input);
DigestAlgorithm alg;
ASSERT_EQ(Success, DigestAlgorithmIdentifier(reader, alg));
ASSERT_EQ(param.algorithm, alg);
ASSERT_EQ(Success, End(reader));
}
}
INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier_Valid,
pkixder_DigestAlgorithmIdentifier_Valid,
testing::ValuesIn(VALID_DIGEST_ALGORITHM_TEST_INFO));
class pkixder_DigestAlgorithmIdentifier_Invalid
: public ::testing::Test
, public ::testing::WithParamInterface<InvalidAlgorithmIdentifierTestInfo>
{
};
static const InvalidAlgorithmIdentifierTestInfo
INVALID_DIGEST_ALGORITHM_TEST_INFO[] =
{
{ // MD5
{ 0x30, 0x0a, 0x06, 0x08,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05 },
12,
},
{ // ecdsa-with-SHA256 (1.2.840.10045.4.3.2) (not a hash algorithm)
{ 0x30, 0x0a, 0x06, 0x08,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 },
12,
},
};
TEST_P(pkixder_DigestAlgorithmIdentifier_Invalid, Invalid)
{
const InvalidAlgorithmIdentifierTestInfo& param(GetParam());
Input input;
ASSERT_EQ(Success, input.Init(param.der, param.derLength));
Reader reader(input);
DigestAlgorithm alg;
ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM,
DigestAlgorithmIdentifier(reader, alg));
}
INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier_Invalid,
pkixder_DigestAlgorithmIdentifier_Invalid,
testing::ValuesIn(INVALID_DIGEST_ALGORITHM_TEST_INFO));
struct ValidSignatureAlgorithmIdentifierValueTestInfo
{
PublicKeyAlgorithm publicKeyAlg;
DigestAlgorithm digestAlg;
uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH];
size_t derLength;
};
static const ValidSignatureAlgorithmIdentifierValueTestInfo
VALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO[] =
{
// ECDSA
{ PublicKeyAlgorithm::ECDSA,
DigestAlgorithm::sha512,
{ 0x06, 0x08,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 },
10,
},
{ PublicKeyAlgorithm::ECDSA,
DigestAlgorithm::sha384,
{ 0x06, 0x08,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 },
10,
},
{ PublicKeyAlgorithm::ECDSA,
DigestAlgorithm::sha256,
{ 0x06, 0x08,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 },
10,
},
{ PublicKeyAlgorithm::ECDSA,
DigestAlgorithm::sha1,
{ 0x06, 0x07,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01 },
9,
},
// RSA
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha512,
{ 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d },
11,
},
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha384,
{ 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c },
11,
},
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha256,
{ 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b },
11,
},
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha1,
// IETF Standard OID
{ 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 },
11,
},
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha1,
// Legacy OIW OID (bug 1042479)
{ 0x06, 0x05,
0x2b, 0x0e, 0x03, 0x02, 0x1d },
7,
},
};
class pkixder_SignatureAlgorithmIdentifierValue_Valid
: public ::testing::Test
, public ::testing::WithParamInterface<
ValidSignatureAlgorithmIdentifierValueTestInfo>
{
};
TEST_P(pkixder_SignatureAlgorithmIdentifierValue_Valid, Valid)
{
const ValidSignatureAlgorithmIdentifierValueTestInfo& param(GetParam());
{
Input input;
ASSERT_EQ(Success, input.Init(param.der, param.derLength));
Reader reader(input);
PublicKeyAlgorithm publicKeyAlg;
DigestAlgorithm digestAlg;
ASSERT_EQ(Success,
SignatureAlgorithmIdentifierValue(reader, publicKeyAlg,
digestAlg));
ASSERT_EQ(param.publicKeyAlg, publicKeyAlg);
ASSERT_EQ(param.digestAlg, digestAlg);
ASSERT_EQ(Success, End(reader));
}
{
uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2];
memcpy(derWithNullParam, param.der, param.derLength);
derWithNullParam[param.derLength] = 0x05; // NULL tag
derWithNullParam[param.derLength + 1] = 0x00; // length zero
Input input;
ASSERT_EQ(Success, input.Init(derWithNullParam, param.derLength + 2));
Reader reader(input);
PublicKeyAlgorithm publicKeyAlg;
DigestAlgorithm digestAlg;
ASSERT_EQ(Success,
SignatureAlgorithmIdentifierValue(reader, publicKeyAlg,
digestAlg));
ASSERT_EQ(param.publicKeyAlg, publicKeyAlg);
ASSERT_EQ(param.digestAlg, digestAlg);
ASSERT_EQ(Success, End(reader));
}
}
INSTANTIATE_TEST_CASE_P(
pkixder_SignatureAlgorithmIdentifierValue_Valid,
pkixder_SignatureAlgorithmIdentifierValue_Valid,
testing::ValuesIn(VALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO));
static const InvalidAlgorithmIdentifierTestInfo
INVALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO[] =
{
// id-dsa-with-sha256 (2.16.840.1.101.3.4.3.2)
{ { 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02 },
11,
},
// id-dsa-with-sha1 (1.2.840.10040.4.3)
{ { 0x06, 0x07,
0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03 },
9,
},
// RSA-with-MD5 (1.2.840.113549.1.1.4)
{ { 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 },
11,
},
// id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid because SHA-256 is not
// a signature algorithm.
{ { 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 },
11,
},
};
class pkixder_SignatureAlgorithmIdentifier_Invalid
: public ::testing::Test
, public ::testing::WithParamInterface<InvalidAlgorithmIdentifierTestInfo>
{
};
TEST_P(pkixder_SignatureAlgorithmIdentifier_Invalid, Invalid)
{
const InvalidAlgorithmIdentifierTestInfo& param(GetParam());
Input input;
ASSERT_EQ(Success, input.Init(param.der, param.derLength));
Reader reader(input);
der::PublicKeyAlgorithm publicKeyAlg;
DigestAlgorithm digestAlg;
ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
SignatureAlgorithmIdentifierValue(reader, publicKeyAlg, digestAlg));
}
INSTANTIATE_TEST_CASE_P(
pkixder_SignatureAlgorithmIdentifier_Invalid,
pkixder_SignatureAlgorithmIdentifier_Invalid,
testing::ValuesIn(INVALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO));

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,46 @@
/* -*- 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 "pkixgtest.h"
#include <ctime>
#include "mozpkix/Time.h"
namespace mozilla { namespace pkix { namespace test {
static const std::time_t ONE_DAY_IN_SECONDS_AS_TIME_T =
static_cast<std::time_t>(Time::ONE_DAY_IN_SECONDS);
// This assumes that time/time_t are POSIX-compliant in that time() returns
// the number of seconds since the Unix epoch.
static const std::time_t now(time(nullptr));
const std::time_t oneDayBeforeNow(now - ONE_DAY_IN_SECONDS_AS_TIME_T);
const std::time_t oneDayAfterNow(now + ONE_DAY_IN_SECONDS_AS_TIME_T);
const std::time_t twoDaysBeforeNow(now - (2 * ONE_DAY_IN_SECONDS_AS_TIME_T));
const std::time_t twoDaysAfterNow(now + (2 * ONE_DAY_IN_SECONDS_AS_TIME_T));
const std::time_t tenDaysBeforeNow(now - (10 * ONE_DAY_IN_SECONDS_AS_TIME_T));
const std::time_t tenDaysAfterNow(now + (10 * ONE_DAY_IN_SECONDS_AS_TIME_T));
} } } // namespace mozilla::pkix::test

Просмотреть файл

@ -0,0 +1,229 @@
/* -*- 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 2014 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.
*/
#ifndef mozilla_pkix_pkixgtest_h
#define mozilla_pkix_pkixgtest_h
#include <ostream>
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
#pragma clang diagnostic ignored "-Wmissing-noreturn"
#pragma clang diagnostic ignored "-Wshift-sign-overflow"
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wundef"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wextra"
#elif defined(_MSC_VER)
#pragma warning(push, 3)
// C4224: Nonstandard extension used: formal parameter 'X' was previously
// defined as a type.
#pragma warning(disable : 4224)
// C4826: Conversion from 'type1 ' to 'type_2' is sign - extended. This may
// cause unexpected runtime behavior.
#pragma warning(disable : 4826)
#endif
#include "gtest/gtest.h"
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
#include "mozpkix/pkix.h"
#include "mozpkix/test/pkixtestutil.h"
// PrintTo must be in the same namespace as the type we're overloading it for.
namespace mozilla {
namespace pkix {
inline void PrintTo(const Result& result, ::std::ostream* os) {
const char* stringified = MapResultToName(result);
if (stringified) {
*os << stringified;
} else {
*os << "mozilla::pkix::Result(" << static_cast<unsigned int>(result) << ")";
}
}
}
} // namespace mozilla::pkix
namespace mozilla {
namespace pkix {
namespace test {
extern const std::time_t oneDayBeforeNow;
extern const std::time_t oneDayAfterNow;
extern const std::time_t twoDaysBeforeNow;
extern const std::time_t twoDaysAfterNow;
extern const std::time_t tenDaysBeforeNow;
extern const std::time_t tenDaysAfterNow;
class EverythingFailsByDefaultTrustDomain : public TrustDomain {
public:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
/*out*/ TrustLevel&) override {
ADD_FAILURE();
return NotReached("GetCertTrust should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result FindIssuer(Input, IssuerChecker&, Time) override {
ADD_FAILURE();
return NotReached("FindIssuer should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*,
/*optional*/ const Input*) override {
ADD_FAILURE();
return NotReached("CheckRevocation should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override {
ADD_FAILURE();
return NotReached("IsChainValid should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result DigestBuf(Input, DigestAlgorithm, /*out*/ uint8_t*, size_t) override {
ADD_FAILURE();
return NotReached("DigestBuf should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
Time) override {
ADD_FAILURE();
return NotReached("CheckSignatureDigestAlgorithm should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override {
ADD_FAILURE();
return NotReached("CheckECDSACurveIsAcceptable should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result VerifyECDSASignedDigest(const SignedDigest&, Input) override {
ADD_FAILURE();
return NotReached("VerifyECDSASignedDigest should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA,
unsigned int) override {
ADD_FAILURE();
return NotReached("CheckRSAPublicKeyModulusSizeInBits should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override {
ADD_FAILURE();
return NotReached("VerifyRSAPKCS1SignedDigest should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
KeyPurposeId) override {
ADD_FAILURE();
return NotReached("CheckValidityIsAcceptable should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result NetscapeStepUpMatchesServerAuth(Time, bool&) override {
ADD_FAILURE();
return NotReached("NetscapeStepUpMatchesServerAuth should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
virtual void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {
ADD_FAILURE();
}
};
class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain {
Result DigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf, size_t digestBufLen) override {
return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
}
Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
Time) override {
return Success;
}
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override {
return Success;
}
Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo) override {
return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
}
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA,
unsigned int) override {
return Success;
}
Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo) override {
return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
}
Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
KeyPurposeId) override {
return Success;
}
Result NetscapeStepUpMatchesServerAuth(Time, /*out*/ bool& matches) override {
matches = true;
return Success;
}
void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {}
};
class DefaultNameMatchingPolicy : public NameMatchingPolicy {
public:
virtual Result FallBackToCommonName(
Time,
/*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override {
fallBackToCommonName = FallBackToSearchWithinSubject::Yes;
return Success;
}
};
}
}
} // namespace mozilla::pkix::test
#endif // mozilla_pkix_pkixgtest_h

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,146 @@
/* -*- 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 "pkixgtest.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
class CreateEncodedOCSPRequestTrustDomain final
: public EverythingFailsByDefaultTrustDomain
{
private:
Result DigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t *digestBuf, size_t digestBufLen)
override
{
return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
}
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
override
{
return Success;
}
};
class pkixocsp_CreateEncodedOCSPRequest : public ::testing::Test
{
protected:
void MakeIssuerCertIDComponents(const char* issuerASCII,
/*out*/ ByteString& issuerDER,
/*out*/ ByteString& issuerSPKI)
{
issuerDER = CNToDERName(issuerASCII);
ASSERT_FALSE(ENCODING_FAILED(issuerDER));
ScopedTestKeyPair keyPair(GenerateKeyPair());
ASSERT_TRUE(keyPair.get());
issuerSPKI = keyPair->subjectPublicKeyInfo;
}
CreateEncodedOCSPRequestTrustDomain trustDomain;
};
// Test that the large length of the child serial number causes
// CreateEncodedOCSPRequest to fail.
TEST_F(pkixocsp_CreateEncodedOCSPRequest, ChildCertLongSerialNumberTest)
{
static const uint8_t UNSUPPORTED_LEN = 128; // must be larger than 127
ByteString serialNumberString;
// tag + length + value is 1 + 2 + UNSUPPORTED_LEN
// Encoding the length takes two bytes: one byte to indicate that a
// second byte follows, and the second byte to indicate the length.
serialNumberString.push_back(0x80 + 1);
serialNumberString.push_back(UNSUPPORTED_LEN);
// value is 0x010000...00
serialNumberString.push_back(0x01);
for (size_t i = 1; i < UNSUPPORTED_LEN; ++i) {
serialNumberString.push_back(0x00);
}
ByteString issuerDER;
ByteString issuerSPKI;
ASSERT_NO_FATAL_FAILURE(MakeIssuerCertIDComponents("CA", issuerDER,
issuerSPKI));
Input issuer;
ASSERT_EQ(Success, issuer.Init(issuerDER.data(), issuerDER.length()));
Input spki;
ASSERT_EQ(Success, spki.Init(issuerSPKI.data(), issuerSPKI.length()));
Input serialNumber;
ASSERT_EQ(Success, serialNumber.Init(serialNumberString.data(),
serialNumberString.length()));
uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
size_t ocspRequestLength;
ASSERT_EQ(Result::ERROR_BAD_DER,
CreateEncodedOCSPRequest(trustDomain,
CertID(issuer, spki, serialNumber),
ocspRequest, ocspRequestLength));
}
// Test that CreateEncodedOCSPRequest handles the longest serial number that
// it's required to support (i.e. 20 octets).
TEST_F(pkixocsp_CreateEncodedOCSPRequest, LongestSupportedSerialNumberTest)
{
static const uint8_t LONGEST_REQUIRED_LEN = 20;
ByteString serialNumberString;
// tag + length + value is 1 + 1 + LONGEST_REQUIRED_LEN
serialNumberString.push_back(der::INTEGER);
serialNumberString.push_back(LONGEST_REQUIRED_LEN);
serialNumberString.push_back(0x01);
// value is 0x010000...00
for (size_t i = 1; i < LONGEST_REQUIRED_LEN; ++i) {
serialNumberString.push_back(0x00);
}
ByteString issuerDER;
ByteString issuerSPKI;
ASSERT_NO_FATAL_FAILURE(MakeIssuerCertIDComponents("CA", issuerDER,
issuerSPKI));
Input issuer;
ASSERT_EQ(Success, issuer.Init(issuerDER.data(), issuerDER.length()));
Input spki;
ASSERT_EQ(Success, spki.Init(issuerSPKI.data(), issuerSPKI.length()));
Input serialNumber;
ASSERT_EQ(Success, serialNumber.Init(serialNumberString.data(),
serialNumberString.length()));
uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
size_t ocspRequestLength;
ASSERT_EQ(Success,
CreateEncodedOCSPRequest(trustDomain,
CertID(issuer, spki, serialNumber),
ocspRequest, ocspRequestLength));
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -18,7 +18,7 @@
#include "ssl3prot.h"
#include "sslerr.h"
#include "sslproto.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "nsskeys.h"

Просмотреть файл

@ -10,7 +10,7 @@
#include "secerr.h"
#include "sechash.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "gcm-vectors.h"
#include "gtest/gtest.h"

Просмотреть файл

@ -9,7 +9,7 @@
#include "pk11pub.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {
@ -129,4 +129,4 @@ TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest6) {
WrapUnwrap(kKEK3, sizeof(kKEK3), kKD6, sizeof(kKD6), kC6);
}
} /* nss_test */
} /* nss_test */

Просмотреть файл

@ -10,7 +10,7 @@
#include "sechash.h"
#include "cpputil.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "gtest/gtest.h"

Просмотреть файл

@ -7,7 +7,7 @@
#include "pk11pub.h"
#include "cpputil.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "gtest/gtest.h"

Просмотреть файл

@ -11,7 +11,7 @@
#include "secutil.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {

Просмотреть файл

@ -8,7 +8,7 @@
#include "sechash.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "pk11_ecdsa_vectors.h"
#include "pk11_signature_test.h"

Просмотреть файл

@ -8,7 +8,7 @@
#include "prerror.h"
#include "nss.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "cpputil.h"
#include "databuffer.h"
#include "util.h"

Просмотреть файл

@ -9,7 +9,7 @@
#include "pk11pub.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {

Просмотреть файл

@ -9,7 +9,7 @@
#include "pk11pub.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {

Просмотреть файл

@ -10,7 +10,7 @@
#include "pk11pub.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "cpputil.h"
namespace nss_test {

Просмотреть файл

@ -10,7 +10,7 @@
#include "sechash.h"
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "pk11_signature_test.h"
#include "pk11_rsapss_vectors.h"

Просмотреть файл

@ -8,7 +8,7 @@
#include "sechash.h"
#include "cpputil.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "databuffer.h"
#include "gtest/gtest.h"

Просмотреть файл

@ -11,7 +11,7 @@
#include "pk11pub.h"
#include "secerr.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"

Просмотреть файл

@ -19,7 +19,7 @@ extern "C" {
#include "databuffer.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {

Просмотреть файл

@ -16,7 +16,7 @@ extern "C" {
}
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -15,7 +15,7 @@ extern "C" {
}
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -17,7 +17,7 @@ extern "C" {
}
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -13,7 +13,7 @@
#include "sslproto.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -14,7 +14,7 @@ extern "C" {
}
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -17,7 +17,7 @@ extern "C" {
}
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -10,7 +10,7 @@
#include "sslproto.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -10,7 +10,7 @@
#include "sslproto.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -13,7 +13,7 @@
#include "ssl3prot.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -15,7 +15,7 @@ extern "C" {
}
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -18,7 +18,7 @@ extern "C" {
}
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -10,7 +10,7 @@
#include "sslproto.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -18,7 +18,8 @@ extern "C" {
}
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "scoped_ptrs_ssl.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -17,7 +17,7 @@ extern "C" {
}
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -11,7 +11,7 @@
#include "sslproto.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -12,7 +12,7 @@
#include "sslproto.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "tls_connect.h"
#include "tls_filter.h"
#include "tls_parser.h"

Просмотреть файл

@ -17,7 +17,7 @@
#include "databuffer.h"
#include "dummy_io.h"
#include "prio.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "sslt.h"
namespace nss_test {

Просмотреть файл

@ -26,7 +26,7 @@ extern "C" {
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
extern std::string g_working_dir_path;

Просмотреть файл

@ -17,7 +17,8 @@
#define GTEST_HAS_RTTI 0
#include "gtest/gtest.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "scoped_ptrs_ssl.h"
extern bool g_ssl_gtest_verbose;

Просмотреть файл

@ -14,7 +14,7 @@ extern "C" {
#include "databuffer.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include "sslproto.h"
extern std::string g_working_dir_path;

Просмотреть файл

@ -11,7 +11,7 @@
#include "databuffer.h"
#include "gtest_utils.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {

Просмотреть файл

@ -3,7 +3,7 @@ Usage: build.sh [-hcv] [-cc] [-j <n>] [--nspr] [--gyp|-g] [--opt|-o] [-m32]
[--asan] [--ubsan] [--msan] [--sancov[=edge|bb|func|...]]
[--disable-tests] [--fuzz[=tls|oss]] [--system-sqlite]
[--no-zdefs] [--with-nspr] [--system-nspr] [--enable-libpkix]
[--enable-fips]
[--enable-fips] [--mozpkix-only]
This script builds NSS with gyp and ninja.
@ -48,3 +48,5 @@ NSS build tool options:
might not work on all systems.
--enable-libpkix make libpkix part of the build.
--enable-fips don't disable FIPS checks.
--mozpkix-only build only static mozpkix and mozpkix-test libraries.
Note that support for this build option is limited.

Просмотреть файл

@ -0,0 +1,4 @@
---
Language: Cpp
BasedOnStyle: Google
...

Просмотреть файл

@ -0,0 +1,47 @@
# 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'
],
'targets': [
{
'target_name': 'lib_mozpkix_exports',
'type': 'none',
'copies': [
{
'files': [
'<(DEPTH)/cpputil/nss_scoped_ptrs.h',
'include/pkix/Input.h',
'include/pkix/Time.h',
'include/pkix/Result.h',
'include/pkix/pkix.h',
'include/pkix/pkixnss.h',
'include/pkix/pkixtypes.h',
'include/pkix/pkixutil.h',
'include/pkix/pkixcheck.h',
'include/pkix/pkixder.h',
],
'destination': '<(nss_public_dist_dir)/<(module)/mozpkix'
},
],
},
{
'target_name': 'lib_mozpkix_test_exports',
'type': 'none',
'copies': [
{
'files': [
'include/pkix-test/pkixtestutil.h',
'include/pkix-test/pkixtestnss.h',
],
'destination': '<(nss_public_dist_dir)/<(module)/mozpkix/test'
},
],
}
],
'variables': {
'module': 'nss'
}
}

Просмотреть файл

@ -0,0 +1,48 @@
/* -*- 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 2018 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.
*/
// This file provides some implementation-specific test utilities. This is only
// necessary because some PSM xpcshell test utilities overlap in functionality
// with these test utilities, so the underlying implementation is shared.
#ifndef mozilla_pkix_test_pkixtestnss_h
#define mozilla_pkix_test_pkixtestnss_h
#include <keyhi.h>
#include <keythi.h>
#include "mozpkix/nss_scoped_ptrs.h"
#include "mozpkix/test/pkixtestutil.h"
namespace mozilla {
namespace pkix {
namespace test {
TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
const ScopedSECKEYPublicKey& publicKey,
const ScopedSECKEYPrivateKey& privateKey);
}
}
} // namespace mozilla::pkix::test
#endif // mozilla_pkix_test_pkixtestnss_h

Просмотреть файл

@ -0,0 +1,406 @@
/* -*- 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.
*/
#ifndef mozilla_pkix_test_pkixtestutil_h
#define mozilla_pkix_test_pkixtestutil_h
#include <cstdint>
#include <cstring>
#include <ctime>
#include <string>
#include "mozpkix/pkixtypes.h"
namespace mozilla {
namespace pkix {
namespace test {
typedef std::basic_string<uint8_t> ByteString;
inline bool ENCODING_FAILED(const ByteString& bs) { return bs.empty(); }
template <size_t L>
inline ByteString BytesToByteString(const uint8_t (&bytes)[L]) {
return ByteString(bytes, L);
}
// XXX: Ideally, we should define this instead:
//
// template <typename T, std::size_t N>
// constexpr inline std::size_t
// ArrayLength(T (&)[N])
// {
// return N;
// }
//
// However, we don't because not all supported compilers support constexpr,
// and we need to calculate array lengths in static_assert sometimes.
//
// XXX: Evaluates its argument twice
#define MOZILLA_PKIX_ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))
bool InputEqualsByteString(Input input, const ByteString& bs);
ByteString InputToByteString(Input input);
// python DottedOIDToCode.py --tlv id-kp-OCSPSigning 1.3.6.1.5.5.7.3.9
static const uint8_t tlv_id_kp_OCSPSigning[] = {0x06, 0x08, 0x2b, 0x06, 0x01,
0x05, 0x05, 0x07, 0x03, 0x09};
// python DottedOIDToCode.py --tlv id-kp-serverAuth 1.3.6.1.5.5.7.3.1
static const uint8_t tlv_id_kp_serverAuth[] = {0x06, 0x08, 0x2b, 0x06, 0x01,
0x05, 0x05, 0x07, 0x03, 0x01};
enum class TestDigestAlgorithmID {
MD2,
MD5,
SHA1,
SHA224,
SHA256,
SHA384,
SHA512,
};
struct TestPublicKeyAlgorithm {
explicit TestPublicKeyAlgorithm(const ByteString& aAlgorithmIdentifier)
: algorithmIdentifier(aAlgorithmIdentifier) {}
bool operator==(const TestPublicKeyAlgorithm& other) const {
return algorithmIdentifier == other.algorithmIdentifier;
}
ByteString algorithmIdentifier;
};
ByteString DSS_P();
ByteString DSS_Q();
ByteString DSS_G();
TestPublicKeyAlgorithm DSS();
TestPublicKeyAlgorithm RSA_PKCS1();
struct TestSignatureAlgorithm {
TestSignatureAlgorithm(const TestPublicKeyAlgorithm& publicKeyAlg,
TestDigestAlgorithmID digestAlg,
const ByteString& algorithmIdentifier, bool accepted);
TestPublicKeyAlgorithm publicKeyAlg;
TestDigestAlgorithmID digestAlg;
ByteString algorithmIdentifier;
bool accepted;
};
TestSignatureAlgorithm md2WithRSAEncryption();
TestSignatureAlgorithm md5WithRSAEncryption();
TestSignatureAlgorithm sha1WithRSAEncryption();
TestSignatureAlgorithm sha256WithRSAEncryption();
// e.g. YMDHMS(2016, 12, 31, 1, 23, 45) => 2016-12-31:01:23:45 (GMT)
mozilla::pkix::Time YMDHMS(uint16_t year, uint16_t month, uint16_t day,
uint16_t hour, uint16_t minutes, uint16_t seconds);
ByteString TLV(uint8_t tag, size_t length, const ByteString& value);
inline ByteString TLV(uint8_t tag, const ByteString& value) {
return TLV(tag, value.length(), value);
}
// Although we can't enforce it without relying on Cuser-defined literals,
// which aren't supported by all of our compilers yet, you should only pass
// string literals as the last parameter to the following two functions.
template <size_t N>
inline ByteString TLV(uint8_t tag, const char (&value)[N]) {
static_assert(N > 0, "cannot have string literal of size 0");
assert(value[N - 1] == 0);
return TLV(tag, ByteString(reinterpret_cast<const uint8_t*>(&value), N - 1));
}
template <size_t N>
inline ByteString TLV(uint8_t tag, size_t length, const char (&value)[N]) {
static_assert(N > 0, "cannot have string literal of size 0");
assert(value[N - 1] == 0);
return TLV(tag, length,
ByteString(reinterpret_cast<const uint8_t*>(&value), N - 1));
}
ByteString Boolean(bool value);
ByteString Integer(long value);
ByteString CN(const ByteString&, uint8_t encodingTag = 0x0c /*UTF8String*/);
inline ByteString CN(const char* value,
uint8_t encodingTag = 0x0c /*UTF8String*/) {
return CN(
ByteString(reinterpret_cast<const uint8_t*>(value), std::strlen(value)),
encodingTag);
}
ByteString OU(const ByteString&, uint8_t encodingTag = 0x0c /*UTF8String*/);
inline ByteString OU(const char* value,
uint8_t encodingTag = 0x0c /*UTF8String*/) {
return OU(
ByteString(reinterpret_cast<const uint8_t*>(value), std::strlen(value)),
encodingTag);
}
ByteString emailAddress(const ByteString&);
inline ByteString emailAddress(const char* value) {
return emailAddress(
ByteString(reinterpret_cast<const uint8_t*>(value), std::strlen(value)));
}
// RelativeDistinguishedName ::=
// SET SIZE (1..MAX) OF AttributeTypeAndValue
//
ByteString RDN(const ByteString& avas);
// Name ::= CHOICE { -- only one possibility for now --
// rdnSequence RDNSequence }
//
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
//
ByteString Name(const ByteString& rdns);
inline ByteString CNToDERName(const ByteString& cn) {
return Name(RDN(CN(cn)));
}
inline ByteString CNToDERName(const char* cn) { return Name(RDN(CN(cn))); }
// GeneralName ::= CHOICE {
// otherName [0] OtherName,
// rfc822Name [1] IA5String,
// dNSName [2] IA5String,
// x400Address [3] ORAddress,
// directoryName [4] Name,
// ediPartyName [5] EDIPartyName,
// uniformResourceIdentifier [6] IA5String,
// iPAddress [7] OCTET STRING,
// registeredID [8] OBJECT IDENTIFIER }
inline ByteString RFC822Name(const ByteString& name) {
// (2 << 6) means "context-specific", 1 is the GeneralName tag.
return TLV((2 << 6) | 1, name);
}
template <size_t L>
inline ByteString RFC822Name(const char (&bytes)[L]) {
return RFC822Name(
ByteString(reinterpret_cast<const uint8_t*>(&bytes), L - 1));
}
inline ByteString DNSName(const ByteString& name) {
// (2 << 6) means "context-specific", 2 is the GeneralName tag.
return TLV((2 << 6) | 2, name);
}
template <size_t L>
inline ByteString DNSName(const char (&bytes)[L]) {
return DNSName(ByteString(reinterpret_cast<const uint8_t*>(&bytes), L - 1));
}
inline ByteString DirectoryName(const ByteString& name) {
// (2 << 6) means "context-specific", (1 << 5) means "constructed", and 4 is
// the DirectoryName tag.
return TLV((2 << 6) | (1 << 5) | 4, name);
}
inline ByteString IPAddress() {
// (2 << 6) means "context-specific", 7 is the GeneralName tag.
return TLV((2 << 6) | 7, ByteString());
}
template <size_t L>
inline ByteString IPAddress(const uint8_t (&bytes)[L]) {
// (2 << 6) means "context-specific", 7 is the GeneralName tag.
return TLV((2 << 6) | 7, ByteString(bytes, L));
}
// Names should be zero or more GeneralNames, like DNSName and IPAddress return,
// concatenated together.
//
// CreatedEncodedSubjectAltName(ByteString()) results in a SAN with an empty
// sequence. CreateEmptyEncodedSubjectName() results in a SAN without any
// sequence.
ByteString CreateEncodedSubjectAltName(const ByteString& names);
ByteString CreateEncodedEmptySubjectAltName();
class TestKeyPair {
public:
virtual ~TestKeyPair() {}
const TestPublicKeyAlgorithm publicKeyAlg;
// The DER encoding of the entire SubjectPublicKeyInfo structure. This is
// what is encoded in certificates.
const ByteString subjectPublicKeyInfo;
// The DER encoding of subjectPublicKeyInfo.subjectPublicKey. This is what is
// hashed to create CertIDs for OCSP.
const ByteString subjectPublicKey;
virtual Result SignData(const ByteString& tbs,
const TestSignatureAlgorithm& signatureAlgorithm,
/*out*/ ByteString& signature) const = 0;
virtual TestKeyPair* Clone() const = 0;
protected:
TestKeyPair(const TestPublicKeyAlgorithm& publicKeyAlg,
const ByteString& spk);
TestKeyPair(const TestKeyPair&) = delete;
void operator=(const TestKeyPair&) = delete;
};
TestKeyPair* CloneReusedKeyPair();
TestKeyPair* GenerateKeyPair();
TestKeyPair* GenerateDSSKeyPair();
inline void DeleteTestKeyPair(TestKeyPair* keyPair) { delete keyPair; }
typedef std::unique_ptr<TestKeyPair> ScopedTestKeyPair;
Result TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo);
Result TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo);
Result TestDigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf, size_t digestBufLen);
// Replace one substring in item with another of the same length, but only if
// the substring was found exactly once. The "same length" restriction is
// useful for avoiding invalidating lengths encoded within the item. The
// "only once" restriction is helpful for avoiding making accidental changes.
//
// The string to search for must be 8 or more bytes long so that it is
// extremely unlikely that there will ever be any false positive matches
// in digital signatures, keys, hashes, etc.
Result TamperOnce(/*in/out*/ ByteString& item, const ByteString& from,
const ByteString& to);
///////////////////////////////////////////////////////////////////////////////
// Encode Certificates
enum Version { v1 = 0, v2 = 1, v3 = 2 };
// signature is assumed to be the DER encoding of an AlgorithmIdentifer. It is
// put into the signature field of the TBSCertificate. In most cases, it will
// be the same as signatureAlgorithm, which is the algorithm actually used
// to sign the certificate.
// serialNumber is assumed to be the DER encoding of an INTEGER.
//
// If extensions is null, then no extensions will be encoded. Otherwise,
// extensions must point to an array of ByteStrings, terminated with an empty
// ByteString. (If the first item of the array is empty then an empty
// Extensions sequence will be encoded.)
ByteString CreateEncodedCertificate(
long version, const TestSignatureAlgorithm& signature,
const ByteString& serialNumber, const ByteString& issuerNameDER,
time_t notBefore, time_t notAfter, const ByteString& subjectNameDER,
const TestKeyPair& subjectKeyPair,
/*optional*/ const ByteString* extensions, const TestKeyPair& issuerKeyPair,
const TestSignatureAlgorithm& signatureAlgorithm);
ByteString CreateEncodedSerialNumber(long value);
enum class Critical { No = 0, Yes = 1 };
ByteString CreateEncodedBasicConstraints(
bool isCA,
/*optional in*/ const long* pathLenConstraint, Critical critical);
// Creates a DER-encoded extKeyUsage extension with one EKU OID.
ByteString CreateEncodedEKUExtension(Input eku, Critical critical);
///////////////////////////////////////////////////////////////////////////////
// Encode OCSP responses
class OCSPResponseExtension final {
public:
OCSPResponseExtension();
ByteString id;
bool critical;
ByteString value;
OCSPResponseExtension* next;
};
class OCSPResponseContext final {
public:
OCSPResponseContext(const CertID& certID, std::time_t time);
const CertID& certID;
// TODO(bug 980538): add a way to specify what certificates are included.
// The fields below are in the order that they appear in an OCSP response.
enum OCSPResponseStatus {
successful = 0,
malformedRequest = 1,
internalError = 2,
tryLater = 3,
// 4 is not used
sigRequired = 5,
unauthorized = 6,
};
uint8_t responseStatus; // an OCSPResponseStatus or an invalid value
bool skipResponseBytes; // If true, don't include responseBytes
// responderID
ByteString signerNameDER; // If set, responderID will use the byName
// form; otherwise responderID will use the
// byKeyHash form.
std::time_t producedAt;
// SingleResponse extensions (for the certID given in the constructor).
OCSPResponseExtension* singleExtensions;
// ResponseData extensions.
OCSPResponseExtension* responseExtensions;
bool includeEmptyExtensions; // If true, include the extension wrapper
// regardless of if there are any actual
// extensions.
ScopedTestKeyPair signerKeyPair;
TestSignatureAlgorithm signatureAlgorithm;
bool badSignature; // If true, alter the signature to fail verification
const ByteString* certs; // optional; array terminated by an empty string
// The following fields are on a per-SingleResponse basis. In the future we
// may support including multiple SingleResponses per response.
enum CertStatus {
good = 0,
revoked = 1,
unknown = 2,
};
uint8_t certStatus; // CertStatus or an invalid value
std::time_t revocationTime; // For certStatus == revoked
std::time_t thisUpdate;
std::time_t nextUpdate;
bool includeNextUpdate;
};
ByteString CreateEncodedOCSPResponse(OCSPResponseContext& context);
}
}
} // namespace mozilla::pkix::test
#endif // mozilla_pkix_test_pkixtestutil_h

Просмотреть файл

@ -0,0 +1,310 @@
/* -*- 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.
*/
#ifndef mozilla_pkix_Input_h
#define mozilla_pkix_Input_h
#include <algorithm>
#include "mozpkix/Result.h"
#include "stdint.h"
namespace mozilla {
namespace pkix {
class Reader;
// An Input is a safety-oriented immutable weak reference to a array of bytes
// of a known size. The data can only be legally accessed by constructing a
// Reader object, which guarantees all accesses to the data are memory safe.
// Neither Input not Reader provide any facilities for modifying the data
// they reference.
//
// Inputs are small and should usually be passed by value, not by reference,
// though for inline functions the distinction doesn't matter:
//
// Result GoodExample(Input input);
// Result BadExample(const Input& input);
// Result WorseExample(const uint8_t* input, size_t len);
//
// Note that in the example, GoodExample has the same performance
// characteristics as WorseExample, but with much better safety guarantees.
class Input final {
public:
typedef uint16_t size_type;
// This constructor is useful for inputs that are statically known to be of a
// fixed size, e.g.:
//
// static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 };
// const Input expected(EXPECTED_BYTES);
//
// This is equivalent to (and preferred over):
//
// static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 };
// Input expected;
// Result rv = expected.Init(EXPECTED_BYTES, sizeof EXPECTED_BYTES);
template <size_type N>
explicit Input(const uint8_t (&aData)[N]) : data(aData), len(N) {}
// Construct a valid, empty, Init-able Input.
Input() : data(nullptr), len(0u) {}
// This is intentionally not explicit in order to allow value semantics.
Input(const Input&) = default;
// Initialize the input. data must be non-null and len must be less than
// 65536. Init may not be called more than once.
Result Init(const uint8_t* aData, size_t aLen) {
if (this->data) {
// already initialized
return Result::FATAL_ERROR_INVALID_ARGS;
}
if (!aData || aLen > 0xffffu) {
// input too large
return Result::ERROR_BAD_DER;
}
this->data = aData;
this->len = aLen;
return Success;
}
// Initialize the input to be equivalent to the given input. Init may not be
// called more than once.
//
// This is basically operator=, but it wasn't given that name because
// normally callers do not check the result of operator=, and normally
// operator= can be used multiple times.
Result Init(Input other) { return Init(other.data, other.len); }
// Returns the length of the input.
//
// Having the return type be size_type instead of size_t avoids the need for
// callers to ensure that the result is small enough.
size_type GetLength() const { return static_cast<size_type>(len); }
// Don't use this. It is here because we have some "friend" functions that we
// don't want to declare in this header file.
const uint8_t* UnsafeGetData() const { return data; }
private:
const uint8_t* data;
size_t len;
void operator=(const Input&) = delete; // Use Init instead.
};
inline bool InputsAreEqual(const Input& a, const Input& b) {
return a.GetLength() == b.GetLength() &&
std::equal(a.UnsafeGetData(), a.UnsafeGetData() + a.GetLength(),
b.UnsafeGetData());
}
// An Reader is a cursor/iterator through the contents of an Input, designed to
// maximize safety during parsing while minimizing the performance cost of that
// safety. In particular, all methods do strict bounds checking to ensure
// buffer overflows are impossible, and they are all inline so that the
// compiler can coalesce as many of those checks together as possible.
//
// In general, Reader allows for one byte of lookahead and no backtracking.
// However, the Match* functions internally may have more lookahead.
class Reader final {
public:
Reader() : input(nullptr), end(nullptr) {}
explicit Reader(Input aInput)
: input(aInput.UnsafeGetData()),
end(aInput.UnsafeGetData() + aInput.GetLength()) {}
Result Init(Input aInput) {
if (this->input) {
return Result::FATAL_ERROR_INVALID_ARGS;
}
this->input = aInput.UnsafeGetData();
this->end = aInput.UnsafeGetData() + aInput.GetLength();
return Success;
}
bool Peek(uint8_t expectedByte) const {
return input < end && *input == expectedByte;
}
Result Read(uint8_t& out) {
Result rv = EnsureLength(1);
if (rv != Success) {
return rv;
}
out = *input++;
return Success;
}
Result Read(uint16_t& out) {
Result rv = EnsureLength(2);
if (rv != Success) {
return rv;
}
out = *input++;
out <<= 8u;
out |= *input++;
return Success;
}
template <Input::size_type N>
bool MatchRest(const uint8_t (&toMatch)[N]) {
// Normally we use EnsureLength which compares (input + len < end), but
// here we want to be sure that there is nothing following the matched
// bytes
if (static_cast<size_t>(end - input) != N) {
return false;
}
if (!std::equal(input, end, toMatch)) {
return false;
}
input = end;
return true;
}
bool MatchRest(Input toMatch) {
// Normally we use EnsureLength which compares (input + len < end), but
// here we want to be sure that there is nothing following the matched
// bytes
size_t remaining = static_cast<size_t>(end - input);
if (toMatch.GetLength() != remaining) {
return false;
}
if (!std::equal(input, end, toMatch.UnsafeGetData())) {
return false;
}
input = end;
return true;
}
Result Skip(Input::size_type len) {
Result rv = EnsureLength(len);
if (rv != Success) {
return rv;
}
input += len;
return Success;
}
Result Skip(Input::size_type len, Reader& skipped) {
Result rv = EnsureLength(len);
if (rv != Success) {
return rv;
}
rv = skipped.Init(input, len);
if (rv != Success) {
return rv;
}
input += len;
return Success;
}
Result Skip(Input::size_type len, /*out*/ Input& skipped) {
Result rv = EnsureLength(len);
if (rv != Success) {
return rv;
}
rv = skipped.Init(input, len);
if (rv != Success) {
return rv;
}
input += len;
return Success;
}
void SkipToEnd() { input = end; }
Result SkipToEnd(/*out*/ Input& skipped) {
return Skip(static_cast<Input::size_type>(end - input), skipped);
}
Result EnsureLength(Input::size_type len) {
if (static_cast<size_t>(end - input) < len) {
return Result::ERROR_BAD_DER;
}
return Success;
}
bool AtEnd() const { return input == end; }
class Mark final {
public:
Mark(const Mark&) = default; // Intentionally not explicit.
private:
friend class Reader;
Mark(const Reader& aInput, const uint8_t* aMark)
: input(aInput), mark(aMark) {}
const Reader& input;
const uint8_t* const mark;
void operator=(const Mark&) = delete;
};
Mark GetMark() const { return Mark(*this, input); }
Result GetInput(const Mark& mark, /*out*/ Input& item) {
if (&mark.input != this || mark.mark > input) {
return NotReached("invalid mark", Result::FATAL_ERROR_INVALID_ARGS);
}
return item.Init(mark.mark,
static_cast<Input::size_type>(input - mark.mark));
}
private:
Result Init(const uint8_t* data, Input::size_type len) {
if (input) {
// already initialized
return Result::FATAL_ERROR_INVALID_ARGS;
}
input = data;
end = data + len;
return Success;
}
const uint8_t* input;
const uint8_t* end;
Reader(const Reader&) = delete;
void operator=(const Reader&) = delete;
};
inline bool InputContains(const Input& input, uint8_t toFind) {
Reader reader(input);
for (;;) {
uint8_t b;
if (reader.Read(b) != Success) {
return false;
}
if (b == toFind) {
return true;
}
}
}
}
} // namespace mozilla::pkix
#endif // mozilla_pkix_Input_h

Просмотреть файл

@ -0,0 +1,219 @@
/* -*- 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.
*/
#ifndef mozilla_pkix_Result_h
#define mozilla_pkix_Result_h
#include <cassert>
namespace mozilla {
namespace pkix {
static const unsigned int FATAL_ERROR_FLAG = 0x800;
// ----------------------------------------------------------------------------
// SELECTED ERROR CODE EXPLANATIONS
//
// Result::ERROR_UNTRUSTED_CERT
// means that the end-entity certificate was actively distrusted.
// Result::ERROR_UNTRUSTED_ISSUER
// means that path building failed because of active distrust.
// Result::ERROR_INVALID_DER_TIME
// means the DER-encoded time was unexpected, such as being before the
// UNIX epoch (allowed by X500, but not valid here).
// Result::ERROR_EXPIRED_CERTIFICATE
// means the end entity certificate expired.
// Result::ERROR_EXPIRED_ISSUER_CERTIFICATE
// means the CA certificate expired.
// Result::ERROR_UNKNOWN_ISSUER
// means that the CA could not be found in the root store.
// Result::ERROR_POLICY_VALIDATION_FAILED
// means that an encoded policy could not be applied or wasn't present
// when expected. Usually this is in the context of Extended Validation.
// Result::ERROR_BAD_CERT_DOMAIN
// means that the certificate's name couldn't be matched to the
// reference identifier.
// Result::ERROR_CERT_NOT_IN_NAME_SPACE
// typically means the certificate violates name constraints applied
// by the issuer.
// Result::ERROR_BAD_DER
// means the input was improperly encoded.
// Result::ERROR_UNKNOWN_ERROR
// means that an external library (NSS) provided an error we didn't
// anticipate. See the map below in Result.h to add new ones.
// Result::FATAL_ERROR_LIBRARY_FAILURE
// is an unexpected fatal error indicating a library had an unexpected
// failure, and we can't proceed.
// Result::FATAL_ERROR_INVALID_ARGS
// means that we violated our own expectations on inputs and there's a
// bug somewhere.
// Result::FATAL_ERROR_INVALID_STATE
// means that we violated our own expectations on state and there's a
// bug somewhere.
// Result::FATAL_ERROR_NO_MEMORY
// means a memory allocation failed, prohibiting validation.
// ----------------------------------------------------------------------------
// The first argument to MOZILLA_PKIX_MAP() is used for building the mapping
// from error code to error name in MapResultToName.
//
// The second argument is for defining the value for the enum literal in the
// Result enum class.
//
// The third argument to MOZILLA_PKIX_MAP() is used, along with the first
// argument, for maintaining the mapping of mozilla::pkix error codes to
// NSS/NSPR error codes in pkixnss.cpp.
#define MOZILLA_PKIX_MAP_LIST \
MOZILLA_PKIX_MAP(Success, 0, 0) \
MOZILLA_PKIX_MAP(ERROR_BAD_DER, 1, SEC_ERROR_BAD_DER) \
MOZILLA_PKIX_MAP(ERROR_CA_CERT_INVALID, 2, SEC_ERROR_CA_CERT_INVALID) \
MOZILLA_PKIX_MAP(ERROR_BAD_SIGNATURE, 3, SEC_ERROR_BAD_SIGNATURE) \
MOZILLA_PKIX_MAP(ERROR_CERT_BAD_ACCESS_LOCATION, 4, \
SEC_ERROR_CERT_BAD_ACCESS_LOCATION) \
MOZILLA_PKIX_MAP(ERROR_CERT_NOT_IN_NAME_SPACE, 5, \
SEC_ERROR_CERT_NOT_IN_NAME_SPACE) \
MOZILLA_PKIX_MAP(ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 6, \
SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) \
MOZILLA_PKIX_MAP(ERROR_CONNECT_REFUSED, 7, PR_CONNECT_REFUSED_ERROR) \
MOZILLA_PKIX_MAP(ERROR_EXPIRED_CERTIFICATE, 8, \
SEC_ERROR_EXPIRED_CERTIFICATE) \
MOZILLA_PKIX_MAP(ERROR_EXTENSION_VALUE_INVALID, 9, \
SEC_ERROR_EXTENSION_VALUE_INVALID) \
MOZILLA_PKIX_MAP(ERROR_INADEQUATE_CERT_TYPE, 10, \
SEC_ERROR_INADEQUATE_CERT_TYPE) \
MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_USAGE, 11, \
SEC_ERROR_INADEQUATE_KEY_USAGE) \
MOZILLA_PKIX_MAP(ERROR_INVALID_ALGORITHM, 12, SEC_ERROR_INVALID_ALGORITHM) \
MOZILLA_PKIX_MAP(ERROR_INVALID_DER_TIME, 13, SEC_ERROR_INVALID_TIME) \
MOZILLA_PKIX_MAP(ERROR_KEY_PINNING_FAILURE, 14, \
MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) \
MOZILLA_PKIX_MAP(ERROR_PATH_LEN_CONSTRAINT_INVALID, 15, \
SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID) \
MOZILLA_PKIX_MAP(ERROR_POLICY_VALIDATION_FAILED, 16, \
SEC_ERROR_POLICY_VALIDATION_FAILED) \
MOZILLA_PKIX_MAP(ERROR_REVOKED_CERTIFICATE, 17, \
SEC_ERROR_REVOKED_CERTIFICATE) \
MOZILLA_PKIX_MAP(ERROR_UNKNOWN_CRITICAL_EXTENSION, 18, \
SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION) \
MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ERROR, 19, PR_UNKNOWN_ERROR) \
MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ISSUER, 20, SEC_ERROR_UNKNOWN_ISSUER) \
MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_CERT, 21, SEC_ERROR_UNTRUSTED_CERT) \
MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_ISSUER, 22, SEC_ERROR_UNTRUSTED_ISSUER) \
MOZILLA_PKIX_MAP(ERROR_OCSP_BAD_SIGNATURE, 23, SEC_ERROR_OCSP_BAD_SIGNATURE) \
MOZILLA_PKIX_MAP(ERROR_OCSP_INVALID_SIGNING_CERT, 24, \
SEC_ERROR_OCSP_INVALID_SIGNING_CERT) \
MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_REQUEST, 25, \
SEC_ERROR_OCSP_MALFORMED_REQUEST) \
MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_RESPONSE, 26, \
SEC_ERROR_OCSP_MALFORMED_RESPONSE) \
MOZILLA_PKIX_MAP(ERROR_OCSP_OLD_RESPONSE, 27, SEC_ERROR_OCSP_OLD_RESPONSE) \
MOZILLA_PKIX_MAP(ERROR_OCSP_REQUEST_NEEDS_SIG, 28, \
SEC_ERROR_OCSP_REQUEST_NEEDS_SIG) \
MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONDER_CERT_INVALID, 29, \
SEC_ERROR_OCSP_RESPONDER_CERT_INVALID) \
MOZILLA_PKIX_MAP(ERROR_OCSP_SERVER_ERROR, 30, SEC_ERROR_OCSP_SERVER_ERROR) \
MOZILLA_PKIX_MAP(ERROR_OCSP_TRY_SERVER_LATER, 31, \
SEC_ERROR_OCSP_TRY_SERVER_LATER) \
MOZILLA_PKIX_MAP(ERROR_OCSP_UNAUTHORIZED_REQUEST, 32, \
SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST) \
MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, 33, \
SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS) \
MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_CERT, 34, SEC_ERROR_OCSP_UNKNOWN_CERT) \
MOZILLA_PKIX_MAP(ERROR_OCSP_FUTURE_RESPONSE, 35, \
SEC_ERROR_OCSP_FUTURE_RESPONSE) \
MOZILLA_PKIX_MAP(ERROR_INVALID_KEY, 36, SEC_ERROR_INVALID_KEY) \
MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_KEYALG, 37, SEC_ERROR_UNSUPPORTED_KEYALG) \
MOZILLA_PKIX_MAP(ERROR_EXPIRED_ISSUER_CERTIFICATE, 38, \
SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE) \
MOZILLA_PKIX_MAP(ERROR_CA_CERT_USED_AS_END_ENTITY, 39, \
MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY) \
MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_SIZE, 40, \
MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE) \
MOZILLA_PKIX_MAP(ERROR_V1_CERT_USED_AS_CA, 41, \
MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA) \
MOZILLA_PKIX_MAP(ERROR_BAD_CERT_DOMAIN, 42, SSL_ERROR_BAD_CERT_DOMAIN) \
MOZILLA_PKIX_MAP(ERROR_NO_RFC822NAME_MATCH, 43, \
MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH) \
MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_ELLIPTIC_CURVE, 44, \
SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE) \
MOZILLA_PKIX_MAP(ERROR_NOT_YET_VALID_CERTIFICATE, 45, \
MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE) \
MOZILLA_PKIX_MAP(ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE, 46, \
MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE) \
MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_EC_POINT_FORM, 47, \
SEC_ERROR_UNSUPPORTED_EC_POINT_FORM) \
MOZILLA_PKIX_MAP(ERROR_SIGNATURE_ALGORITHM_MISMATCH, 48, \
MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH) \
MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONSE_FOR_CERT_MISSING, 49, \
MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING) \
MOZILLA_PKIX_MAP(ERROR_VALIDITY_TOO_LONG, 50, \
MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG) \
MOZILLA_PKIX_MAP(ERROR_REQUIRED_TLS_FEATURE_MISSING, 51, \
MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING) \
MOZILLA_PKIX_MAP(ERROR_INVALID_INTEGER_ENCODING, 52, \
MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING) \
MOZILLA_PKIX_MAP(ERROR_EMPTY_ISSUER_NAME, 53, \
MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME) \
MOZILLA_PKIX_MAP(ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED, 54, \
MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED) \
MOZILLA_PKIX_MAP(ERROR_SELF_SIGNED_CERT, 55, \
MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT) \
MOZILLA_PKIX_MAP(ERROR_MITM_DETECTED, 56, MOZILLA_PKIX_ERROR_MITM_DETECTED) \
MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_ARGS, FATAL_ERROR_FLAG | 1, \
SEC_ERROR_INVALID_ARGS) \
MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_STATE, FATAL_ERROR_FLAG | 2, \
PR_INVALID_STATE_ERROR) \
MOZILLA_PKIX_MAP(FATAL_ERROR_LIBRARY_FAILURE, FATAL_ERROR_FLAG | 3, \
SEC_ERROR_LIBRARY_FAILURE) \
MOZILLA_PKIX_MAP(FATAL_ERROR_NO_MEMORY, FATAL_ERROR_FLAG | 4, \
SEC_ERROR_NO_MEMORY) \
/* nothing here */
enum class Result {
#define MOZILLA_PKIX_MAP(name, value, nss_name) name = value,
MOZILLA_PKIX_MAP_LIST
#undef MOZILLA_PKIX_MAP
};
// Returns the stringified name of the given result, e.g. "Result::Success",
// or nullptr if result is unknown (invalid).
const char* MapResultToName(Result result);
// We write many comparisons as (x != Success), and this shortened name makes
// those comparisons clearer, especially because the shortened name often
// results in less line wrapping.
static const Result Success = Result::Success;
inline bool IsFatalError(Result rv) {
return (static_cast<unsigned int>(rv) & FATAL_ERROR_FLAG) != 0;
}
inline Result NotReached(const char* /*explanation*/, Result result) {
assert(false);
return result;
}
}
} // namespace mozilla::pkix
#endif // mozilla_pkix_Result_h

Просмотреть файл

@ -0,0 +1,137 @@
/* -*- 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 2014 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.
*/
#ifndef mozilla_pkix_Time_h
#define mozilla_pkix_Time_h
#include <stdint.h>
#include <ctime>
#include <limits>
#include "mozpkix/Result.h"
namespace mozilla {
namespace pkix {
// Time with a range from the first second of year 0 (AD) through at least the
// last second of year 9999, which is the range of legal times in X.509 and
// OCSP. This type has second-level precision. The time zone is always UTC.
//
// Pass by value, not by reference.
class Time final {
public:
// Construct an uninitialized instance.
//
// This will fail to compile because there is no default constructor:
// Time x;
//
// This will succeed, leaving the time uninitialized:
// Time x(Time::uninitialized);
enum Uninitialized { uninitialized };
explicit Time(Uninitialized) {}
bool operator==(const Time& other) const {
return elapsedSecondsAD == other.elapsedSecondsAD;
}
bool operator>(const Time& other) const {
return elapsedSecondsAD > other.elapsedSecondsAD;
}
bool operator>=(const Time& other) const {
return elapsedSecondsAD >= other.elapsedSecondsAD;
}
bool operator<(const Time& other) const {
return elapsedSecondsAD < other.elapsedSecondsAD;
}
bool operator<=(const Time& other) const {
return elapsedSecondsAD <= other.elapsedSecondsAD;
}
Result AddSeconds(uint64_t seconds) {
if (std::numeric_limits<uint64_t>::max() - elapsedSecondsAD < seconds) {
return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
}
elapsedSecondsAD += seconds;
return Success;
}
Result SubtractSeconds(uint64_t seconds) {
if (seconds > elapsedSecondsAD) {
return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
}
elapsedSecondsAD -= seconds;
return Success;
}
static const uint64_t ONE_DAY_IN_SECONDS =
UINT64_C(24) * UINT64_C(60) * UINT64_C(60);
private:
// This constructor is hidden to prevent accidents like this:
//
// Time foo(time_t t)
// {
// // WRONG! 1970-01-01-00:00:00 == time_t(0), but not Time(0)!
// return Time(t);
// }
explicit Time(uint64_t aElapsedSecondsAD)
: elapsedSecondsAD(aElapsedSecondsAD) {}
friend Time TimeFromElapsedSecondsAD(uint64_t);
friend class Duration;
uint64_t elapsedSecondsAD;
};
inline Time TimeFromElapsedSecondsAD(uint64_t aElapsedSecondsAD) {
return Time(aElapsedSecondsAD);
}
Time Now();
// Note the epoch is the unix epoch (ie 00:00:00 UTC, 1 January 1970)
Time TimeFromEpochInSeconds(uint64_t secondsSinceEpoch);
class Duration final {
public:
Duration(Time timeA, Time timeB)
: durationInSeconds(
timeA < timeB ? timeB.elapsedSecondsAD - timeA.elapsedSecondsAD
: timeA.elapsedSecondsAD - timeB.elapsedSecondsAD) {}
explicit Duration(uint64_t aDurationInSeconds)
: durationInSeconds(aDurationInSeconds) {}
bool operator>(const Duration& other) const {
return durationInSeconds > other.durationInSeconds;
}
bool operator<(const Duration& other) const {
return durationInSeconds < other.durationInSeconds;
}
private:
uint64_t durationInSeconds;
};
}
} // namespace mozilla::pkix
#endif // mozilla_pkix_Time_h

Просмотреть файл

@ -0,0 +1,160 @@
/* -*- 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.
*/
#ifndef mozilla_pkix_pkix_h
#define mozilla_pkix_pkix_h
#include "mozpkix/pkixtypes.h"
namespace mozilla {
namespace pkix {
// ----------------------------------------------------------------------------
// LIMITED SUPPORT FOR CERTIFICATE POLICIES
//
// If SEC_OID_X509_ANY_POLICY is passed as the value of the requiredPolicy
// parameter then all policy validation will be skipped. Otherwise, path
// building and validation will be done for the given policy.
//
// In RFC 5280 terms:
//
// * user-initial-policy-set = { requiredPolicy }.
// * initial-explicit-policy = true
// * initial-any-policy-inhibit = false
//
// We allow intermediate cerificates to use this extension but since
// we do not process the inhibit anyPolicy extesion we will fail if this
// extension is present. TODO(bug 989051)
// Because we force explicit policy and because we prohibit policy mapping, we
// do not bother processing the policy mapping, or policy constraint.
//
// ----------------------------------------------------------------------------
// ERROR RANKING
//
// BuildCertChain prioritizes certain checks ahead of others so that when a
// certificate chain has multiple errors, the "most serious" error is
// returned. In practice, this ranking of seriousness is tied directly to how
// Firefox's certificate error override mechanism.
//
// The ranking is:
//
// 1. Active distrust (Result::ERROR_UNTRUSTED_CERT).
// 2. Problems with issuer-independent properties for CA certificates.
// 3. Unknown issuer (Result::ERROR_UNKNOWN_ISSUER).
// 4. Problems with issuer-independent properties for EE certificates.
// 5. Revocation.
//
// In particular, if BuildCertChain returns Result::ERROR_UNKNOWN_ISSUER then
// the caller can call CERT_CheckCertValidTimes to determine if the certificate
// is ALSO expired.
//
// It would be better if revocation were prioritized above expiration and
// unknown issuer. However, it is impossible to do revocation checking without
// knowing the issuer, since the issuer information is needed to validate the
// revocation information. Also, generally revocation checking only works
// during the validity period of the certificate.
//
// In general, when path building fails, BuildCertChain will return
// Result::ERROR_UNKNOWN_ISSUER. However, if all attempted paths resulted in
// the same error (which is trivially true when there is only one potential
// path), more specific errors will be returned.
//
// ----------------------------------------------------------------------------
// Meanings of specific error codes can be found in Result.h
// This function attempts to find a trustworthy path from the supplied
// certificate to a trust anchor. In the event that no trusted path is found,
// the method returns an error result; the error ranking is described above.
//
// Parameters:
// time:
// Timestamp for which the chain should be valid; this is useful to
// analyze whether a record was trustworthy when it was made.
// requiredKeyUsageIfPresent:
// What key usage bits must be set, if the extension is present at all,
// to be considered a valid chain. Multiple values should be OR'd
// together. If you don't want to specify anything, use
// KeyUsage::noParticularKeyUsageRequired.
// requiredEKUIfPresent:
// What extended key usage bits must be set, if the EKU extension
// exists, to be considered a valid chain. Multiple values should be
// OR'd together. If you don't want to specify anything, use
// KeyPurposeId::anyExtendedKeyUsage.
// requiredPolicy:
// This is the policy to apply; typically included in EV certificates.
// If there is no policy, pass in CertPolicyId::anyPolicy.
Result BuildCertChain(TrustDomain& trustDomain, Input cert, Time time,
EndEntityOrCA endEntityOrCA,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
/*optional*/ const Input* stapledOCSPResponse);
// Verify that the given end-entity cert, which is assumed to have been already
// validated with BuildCertChain, is valid for the given hostname. The matching
// function attempts to implement RFC 6125 with a couple of differences:
// - IP addresses are out of scope of RFC 6125, but this method accepts them for
// backward compatibility (see SearchNames in pkixnames.cpp)
// - A wildcard in a DNS-ID may only appear as the entirety of the first label.
Result CheckCertHostname(Input cert, Input hostname,
NameMatchingPolicy& nameMatchingPolicy);
// Construct an RFC-6960-encoded OCSP request, ready for submission to a
// responder, for the provided CertID. The request has no extensions.
static const size_t OCSP_REQUEST_MAX_LENGTH = 127;
Result CreateEncodedOCSPRequest(TrustDomain& trustDomain, const CertID& certID,
/*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH],
/*out*/ size_t& outLen);
// The out parameter expired will be true if the response has expired. If the
// response also indicates a revoked or unknown certificate, that error
// will be returned. Otherwise, Result::ERROR_OCSP_OLD_RESPONSE will be
// returned for an expired response.
//
// The optional parameter thisUpdate will be the thisUpdate value of
// the encoded response if it is considered trustworthy. Only
// good, unknown, or revoked responses that verify correctly are considered
// trustworthy. If the response is not trustworthy, thisUpdate will be 0.
// Similarly, the optional parameter validThrough will be the time through
// which the encoded response is considered trustworthy (that is, as long as
// the given time at which to validate is less than or equal to validThrough,
// the response will be considered trustworthy).
Result VerifyEncodedOCSPResponse(
TrustDomain& trustDomain, const CertID& certID, Time time,
uint16_t maxLifetimeInDays, Input encodedResponse,
/* out */ bool& expired,
/* optional out */ Time* thisUpdate = nullptr,
/* optional out */ Time* validThrough = nullptr);
// Check that the TLSFeature extensions in a given end-entity cert (which is
// assumed to have been already validated with BuildCertChain) are satisfied.
// The only feature which we cancurrently process a requirement for is
// status_request (OCSP stapling) so we reject any extension that specifies a
// requirement for another value. Empty extensions are also rejected.
Result CheckTLSFeaturesAreSatisfied(Input& cert,
const Input* stapledOCSPResponse);
}
} // namespace mozilla::pkix
#endif // mozilla_pkix_pkix_h

Просмотреть файл

@ -0,0 +1,65 @@
/* -*- 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.
*/
#ifndef mozilla_pkix_pkixcheck_h
#define mozilla_pkix_pkixcheck_h
#include "mozpkix/pkixtypes.h"
namespace mozilla {
namespace pkix {
class BackCert;
Result CheckIssuerIndependentProperties(TrustDomain& trustDomain,
const BackCert& cert, Time time,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
unsigned int subCACount,
/*out*/ TrustLevel& trustLevel);
Result CheckNameConstraints(Input encodedNameConstraints,
const BackCert& firstChild,
KeyPurposeId requiredEKUIfPresent);
Result CheckIssuer(Input encodedIssuer);
// ParseValidity and CheckValidity are usually used together. First you parse
// the dates from the DER Validity sequence, then you compare them to the time
// at which you are validating. They are separate so that the notBefore and
// notAfter times can be used for other things before they are checked against
// the time of validation.
Result ParseValidity(Input encodedValidity,
/*optional out*/ Time* notBeforeOut = nullptr,
/*optional out*/ Time* notAfterOut = nullptr);
Result CheckValidity(Time time, Time notBefore, Time notAfter);
// Check that a subject has TLS Feature (rfc7633) requirements that match its
// potential issuer
Result CheckTLSFeatures(const BackCert& subject, BackCert& potentialIssuer);
}
} // namespace mozilla::pkix
#endif // mozilla_pkix_pkixcheck_h

Просмотреть файл

@ -0,0 +1,520 @@
/* -*- 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.
*/
#ifndef mozilla_pkix_pkixder_h
#define mozilla_pkix_pkixder_h
// Expect* functions advance the input mark and return Success if the input
// matches the given criteria; they fail with the input mark in an undefined
// state if the input does not match the criteria.
//
// Match* functions advance the input mark and return true if the input matches
// the given criteria; they return false without changing the input mark if the
// input does not match the criteria.
//
// Skip* functions unconditionally advance the input mark and return Success if
// they are able to do so; otherwise they fail with the input mark in an
// undefined state.
#include "mozpkix/Input.h"
#include "mozpkix/pkixtypes.h"
namespace mozilla {
namespace pkix {
namespace der {
enum Class : uint8_t {
UNIVERSAL = 0 << 6,
// APPLICATION = 1 << 6, // unused
CONTEXT_SPECIFIC = 2 << 6,
// PRIVATE = 3 << 6 // unused
};
enum Constructed { CONSTRUCTED = 1 << 5 };
enum Tag : uint8_t {
BOOLEAN = UNIVERSAL | 0x01,
INTEGER = UNIVERSAL | 0x02,
BIT_STRING = UNIVERSAL | 0x03,
OCTET_STRING = UNIVERSAL | 0x04,
NULLTag = UNIVERSAL | 0x05,
OIDTag = UNIVERSAL | 0x06,
ENUMERATED = UNIVERSAL | 0x0a,
UTF8String = UNIVERSAL | 0x0c,
SEQUENCE = UNIVERSAL | CONSTRUCTED | 0x10, // 0x30
SET = UNIVERSAL | CONSTRUCTED | 0x11, // 0x31
PrintableString = UNIVERSAL | 0x13,
TeletexString = UNIVERSAL | 0x14,
IA5String = UNIVERSAL | 0x16,
UTCTime = UNIVERSAL | 0x17,
GENERALIZED_TIME = UNIVERSAL | 0x18,
};
enum class EmptyAllowed { No = 0, Yes = 1 };
Result ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag,
/*out*/ Input& value);
Result End(Reader& input);
inline Result ExpectTagAndGetValue(Reader& input, uint8_t tag,
/*out*/ Input& value) {
uint8_t actualTag;
Result rv = ReadTagAndGetValue(input, actualTag, value);
if (rv != Success) {
return rv;
}
if (tag != actualTag) {
return Result::ERROR_BAD_DER;
}
return Success;
}
inline Result ExpectTagAndGetValue(Reader& input, uint8_t tag,
/*out*/ Reader& value) {
Input valueInput;
Result rv = ExpectTagAndGetValue(input, tag, valueInput);
if (rv != Success) {
return rv;
}
return value.Init(valueInput);
}
inline Result ExpectTagAndEmptyValue(Reader& input, uint8_t tag) {
Reader value;
Result rv = ExpectTagAndGetValue(input, tag, value);
if (rv != Success) {
return rv;
}
return End(value);
}
inline Result ExpectTagAndSkipValue(Reader& input, uint8_t tag) {
Input ignoredValue;
return ExpectTagAndGetValue(input, tag, ignoredValue);
}
// Like ExpectTagAndGetValue, except the output Input will contain the
// encoded tag and length along with the value.
inline Result ExpectTagAndGetTLV(Reader& input, uint8_t tag,
/*out*/ Input& tlv) {
Reader::Mark mark(input.GetMark());
Result rv = ExpectTagAndSkipValue(input, tag);
if (rv != Success) {
return rv;
}
return input.GetInput(mark, tlv);
}
inline Result End(Reader& input) {
if (!input.AtEnd()) {
return Result::ERROR_BAD_DER;
}
return Success;
}
template <typename Decoder>
inline Result Nested(Reader& input, uint8_t tag, Decoder decoder) {
Reader nested;
Result rv = ExpectTagAndGetValue(input, tag, nested);
if (rv != Success) {
return rv;
}
rv = decoder(nested);
if (rv != Success) {
return rv;
}
return End(nested);
}
template <typename Decoder>
inline Result Nested(Reader& input, uint8_t outerTag, uint8_t innerTag,
Decoder decoder) {
Reader nestedInput;
Result rv = ExpectTagAndGetValue(input, outerTag, nestedInput);
if (rv != Success) {
return rv;
}
rv = Nested(nestedInput, innerTag, decoder);
if (rv != Success) {
return rv;
}
return End(nestedInput);
}
// This can be used to decode constructs like this:
//
// ...
// foos SEQUENCE OF Foo,
// ...
// Foo ::= SEQUENCE {
// }
//
// using code like this:
//
// Result Foo(Reader& r) { /*...*/ }
//
// rv = der::NestedOf(input, der::SEQEUENCE, der::SEQUENCE, Foo);
//
// or:
//
// Result Bar(Reader& r, int value) { /*...*/ }
//
// int value = /*...*/;
//
// rv = der::NestedOf(input, der::SEQUENCE, [value](Reader& r) {
// return Bar(r, value);
// });
//
// In these examples the function will get called once for each element of
// foos.
//
template <typename Decoder>
inline Result NestedOf(Reader& input, uint8_t outerTag, uint8_t innerTag,
EmptyAllowed mayBeEmpty, Decoder decoder) {
Reader inner;
Result rv = ExpectTagAndGetValue(input, outerTag, inner);
if (rv != Success) {
return rv;
}
if (inner.AtEnd()) {
if (mayBeEmpty != EmptyAllowed::Yes) {
return Result::ERROR_BAD_DER;
}
return Success;
}
do {
rv = Nested(inner, innerTag, decoder);
if (rv != Success) {
return rv;
}
} while (!inner.AtEnd());
return Success;
}
// Often, a function will need to decode an Input or Reader that contains
// DER-encoded data wrapped in a SEQUENCE (or similar) with nothing after it.
// This function reduces the boilerplate necessary for stripping the outermost
// SEQUENCE (or similar) and ensuring that nothing follows it.
inline Result ExpectTagAndGetValueAtEnd(Reader& outer, uint8_t expectedTag,
/*out*/ Reader& inner) {
Result rv = der::ExpectTagAndGetValue(outer, expectedTag, inner);
if (rv != Success) {
return rv;
}
return der::End(outer);
}
// Similar to the above, but takes an Input instead of a Reader&.
inline Result ExpectTagAndGetValueAtEnd(Input outer, uint8_t expectedTag,
/*out*/ Reader& inner) {
Reader outerReader(outer);
return ExpectTagAndGetValueAtEnd(outerReader, expectedTag, inner);
}
// Universal types
namespace internal {
enum class IntegralValueRestriction {
NoRestriction,
MustBePositive,
MustBe0To127,
};
Result IntegralBytes(
Reader& input, uint8_t tag, IntegralValueRestriction valueRestriction,
/*out*/ Input& value,
/*optional out*/ Input::size_type* significantBytes = nullptr);
// This parser will only parse values between 0..127. If this range is
// increased then callers will need to be changed.
Result IntegralValue(Reader& input, uint8_t tag, /*out*/ uint8_t& value);
} // namespace internal
Result BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value);
inline Result Boolean(Reader& input, /*out*/ bool& value) {
Reader valueReader;
Result rv = ExpectTagAndGetValue(input, BOOLEAN, valueReader);
if (rv != Success) {
return rv;
}
uint8_t intValue;
rv = valueReader.Read(intValue);
if (rv != Success) {
return rv;
}
rv = End(valueReader);
if (rv != Success) {
return rv;
}
switch (intValue) {
case 0:
value = false;
return Success;
case 0xFF:
value = true;
return Success;
default:
return Result::ERROR_BAD_DER;
}
}
// This is for BOOLEAN DEFAULT FALSE.
// The standard stipulates that "The encoding of a set value or sequence value
// shall not include an encoding for any component value which is equal to its
// default value." However, it appears to be common that other libraries
// incorrectly include the value of a BOOLEAN even when it's equal to the
// default value, so we allow invalid explicit encodings here.
inline Result OptionalBoolean(Reader& input, /*out*/ bool& value) {
value = false;
if (input.Peek(BOOLEAN)) {
Result rv = Boolean(input, value);
if (rv != Success) {
return rv;
}
}
return Success;
}
// This parser will only parse values between 0..127. If this range is
// increased then callers will need to be changed.
inline Result Enumerated(Reader& input, uint8_t& value) {
return internal::IntegralValue(input, ENUMERATED | 0, value);
}
namespace internal {
// internal::TimeChoice implements the shared functionality of GeneralizedTime
// and TimeChoice. tag must be either UTCTime or GENERALIZED_TIME.
//
// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
// eliminate the chance for complications in converting times to traditional
// time formats that start at 1970.
Result TimeChoice(Reader& input, uint8_t tag, /*out*/ Time& time);
} // namespace internal
// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
// eliminate the chance for complications in converting times to traditional
// time formats that start at 1970.
inline Result GeneralizedTime(Reader& input, /*out*/ Time& time) {
return internal::TimeChoice(input, GENERALIZED_TIME, time);
}
// Only times from 1970-01-01-00:00:00 onward are accepted, in order to
// eliminate the chance for complications in converting times to traditional
// time formats that start at 1970.
inline Result TimeChoice(Reader& input, /*out*/ Time& time) {
uint8_t expectedTag = input.Peek(UTCTime) ? UTCTime : GENERALIZED_TIME;
return internal::TimeChoice(input, expectedTag, time);
}
// Parse a DER integer value into value. Empty values, negative values, and
// zero are rejected. If significantBytes is not null, then it will be set to
// the number of significant bytes in the value (the length of the value, less
// the length of any leading padding), which is useful for key size checks.
inline Result PositiveInteger(
Reader& input, /*out*/ Input& value,
/*optional out*/ Input::size_type* significantBytes = nullptr) {
return internal::IntegralBytes(
input, INTEGER, internal::IntegralValueRestriction::MustBePositive, value,
significantBytes);
}
// This parser will only parse values between 0..127. If this range is
// increased then callers will need to be changed.
inline Result Integer(Reader& input, /*out*/ uint8_t& value) {
return internal::IntegralValue(input, INTEGER, value);
}
// This parser will only parse values between 0..127. If this range is
// increased then callers will need to be changed. The default value must be
// -1; defaultValue is only a parameter to make it clear in the calling code
// what the default value is.
inline Result OptionalInteger(Reader& input, long defaultValue,
/*out*/ long& value) {
// If we need to support a different default value in the future, we need to
// test that parsedValue != defaultValue.
if (defaultValue != -1) {
return Result::FATAL_ERROR_INVALID_ARGS;
}
if (!input.Peek(INTEGER)) {
value = defaultValue;
return Success;
}
uint8_t parsedValue;
Result rv = Integer(input, parsedValue);
if (rv != Success) {
return rv;
}
value = parsedValue;
return Success;
}
inline Result Null(Reader& input) {
return ExpectTagAndEmptyValue(input, NULLTag);
}
template <uint8_t Len>
Result OID(Reader& input, const uint8_t (&expectedOid)[Len]) {
Reader value;
Result rv = ExpectTagAndGetValue(input, OIDTag, value);
if (rv != Success) {
return rv;
}
if (!value.MatchRest(expectedOid)) {
return Result::ERROR_BAD_DER;
}
return Success;
}
// PKI-specific types
inline Result CertificateSerialNumber(Reader& input, /*out*/ Input& value) {
// http://tools.ietf.org/html/rfc5280#section-4.1.2.2:
//
// * "The serial number MUST be a positive integer assigned by the CA to
// each certificate."
// * "Certificate users MUST be able to handle serialNumber values up to 20
// octets. Conforming CAs MUST NOT use serialNumber values longer than 20
// octets."
// * "Note: Non-conforming CAs may issue certificates with serial numbers
// that are negative or zero. Certificate users SHOULD be prepared to
// gracefully handle such certificates."
return internal::IntegralBytes(
input, INTEGER, internal::IntegralValueRestriction::NoRestriction, value);
}
// x.509 and OCSP both use this same version numbering scheme, though OCSP
// only supports v1.
enum class Version { v1 = 0, v2 = 1, v3 = 2, v4 = 3, Uninitialized = 255 };
// X.509 Certificate and OCSP ResponseData both use
// "[0] EXPLICIT Version DEFAULT v1". Although an explicit encoding of v1 is
// illegal, we support it because some real-world OCSP responses explicitly
// encode it.
Result OptionalVersion(Reader& input, /*out*/ Version& version);
template <typename ExtensionHandler>
inline Result OptionalExtensions(Reader& input, uint8_t tag,
ExtensionHandler extensionHandler) {
if (!input.Peek(tag)) {
return Success;
}
return Nested(input, tag, [extensionHandler](Reader& tagged) {
// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
//
// TODO(bug 997994): According to the specification, there should never be
// an empty sequence of extensions but we've found OCSP responses that have
// that (see bug 991898).
return NestedOf(
tagged, SEQUENCE, SEQUENCE, EmptyAllowed::Yes,
[extensionHandler](Reader& extension) -> Result {
// Extension ::= SEQUENCE {
// extnID OBJECT IDENTIFIER,
// critical BOOLEAN DEFAULT FALSE,
// extnValue OCTET STRING
// }
Reader extnID;
Result rv = ExpectTagAndGetValue(extension, OIDTag, extnID);
if (rv != Success) {
return rv;
}
bool critical;
rv = OptionalBoolean(extension, critical);
if (rv != Success) {
return rv;
}
Input extnValue;
rv = ExpectTagAndGetValue(extension, OCTET_STRING, extnValue);
if (rv != Success) {
return rv;
}
bool understood = false;
rv = extensionHandler(extnID, extnValue, critical, understood);
if (rv != Success) {
return rv;
}
if (critical && !understood) {
return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
}
return Success;
});
});
}
Result DigestAlgorithmIdentifier(Reader& input,
/*out*/ DigestAlgorithm& algorithm);
enum class PublicKeyAlgorithm { RSA_PKCS1, ECDSA, Uninitialized };
Result SignatureAlgorithmIdentifierValue(
Reader& input,
/*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
/*out*/ DigestAlgorithm& digestAlgorithm);
struct SignedDataWithSignature final {
public:
Input data;
Input algorithm;
Input signature;
void operator=(const SignedDataWithSignature&) = delete;
};
// Parses a SEQUENCE into tbs and then parses an AlgorithmIdentifier followed
// by a BIT STRING into signedData. This handles the commonality between
// parsing the signed/signature fields of certificates and OCSP responses. In
// the case of an OCSP response, the caller needs to parse the certs
// separately.
//
// Note that signatureAlgorithm is NOT parsed or validated.
//
// Certificate ::= SEQUENCE {
// tbsCertificate TBSCertificate,
// signatureAlgorithm AlgorithmIdentifier,
// signatureValue BIT STRING }
//
// BasicOCSPResponse ::= SEQUENCE {
// tbsResponseData ResponseData,
// signatureAlgorithm AlgorithmIdentifier,
// signature BIT STRING,
// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
Result SignedData(Reader& input, /*out*/ Reader& tbs,
/*out*/ SignedDataWithSignature& signedDataWithSignature);
}
}
} // namespace mozilla::pkix::der
#endif // mozilla_pkix_pkixder_h

Просмотреть файл

@ -0,0 +1,106 @@
/* -*- 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.
*/
#ifndef mozilla_pkix_pkixnss_h
#define mozilla_pkix_pkixnss_h
#include <seccomon.h>
#include "mozpkix/pkixtypes.h"
#include "prerror.h"
namespace mozilla {
namespace pkix {
// Verifies the PKCS#1.5 signature on the given data using the given RSA public
// key.
Result VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd,
Input subjectPublicKeyInfo,
void* pkcs11PinArg);
// Verifies the ECDSA signature on the given data using the given ECC public
// key.
Result VerifyECDSASignedDigestNSS(const SignedDigest& sd,
Input subjectPublicKeyInfo,
void* pkcs11PinArg);
// Computes the digest of the given data using the given digest algorithm.
//
// item contains the data to hash.
// digestBuf must point to a buffer to where the digest will be written.
// digestBufLen must be the size of the buffer, which must be exactly equal
// to the size of the digest output (20 for SHA-1, 32 for SHA-256,
// etc.)
//
// TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
// other, extensive, memory safety efforts in mozilla::pkix, and we should find
// a way to provide a more-obviously-safe interface.
Result DigestBufNSS(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf, size_t digestBufLen);
Result MapPRErrorCodeToResult(PRErrorCode errorCode);
PRErrorCode MapResultToPRErrorCode(Result result);
// The error codes within each module must fit in 16 bits. We want these
// errors to fit in the same module as the NSS errors but not overlap with
// any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error
// involves negating the value of the error and then synthesizing an error
// in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at
// a negative value that both doesn't overlap with the current value
// ranges for NSS errors and that will fit in 16 bits when negated.
static const PRErrorCode ERROR_BASE = -0x4000;
static const PRErrorCode ERROR_LIMIT = ERROR_BASE + 1000;
enum ErrorCode {
MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = ERROR_BASE + 0,
MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY = ERROR_BASE + 1,
MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE = ERROR_BASE + 2,
MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA = ERROR_BASE + 3,
MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH = ERROR_BASE + 4,
MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = ERROR_BASE + 5,
MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = ERROR_BASE + 6,
MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH = ERROR_BASE + 7,
MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING = ERROR_BASE + 8,
MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG = ERROR_BASE + 9,
MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING = ERROR_BASE + 10,
MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING = ERROR_BASE + 11,
MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME = ERROR_BASE + 12,
MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED = ERROR_BASE + 13,
MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT = ERROR_BASE + 14,
MOZILLA_PKIX_ERROR_MITM_DETECTED = ERROR_BASE + 15,
END_OF_LIST
};
void RegisterErrorTable();
inline SECItem UnsafeMapInputToSECItem(Input input) {
SECItem result = {siBuffer, const_cast<uint8_t*>(input.UnsafeGetData()),
input.GetLength()};
static_assert(sizeof(decltype(input.GetLength())) <= sizeof(result.len),
"input.GetLength() must fit in a SECItem");
return result;
}
}
} // namespace mozilla::pkix
#endif // mozilla_pkix_pkixnss_h

Просмотреть файл

@ -0,0 +1,400 @@
/* -*- 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.
*/
#ifndef mozilla_pkix_pkixtypes_h
#define mozilla_pkix_pkixtypes_h
#include <memory>
#include "mozpkix/Input.h"
#include "mozpkix/Time.h"
#include "stdint.h"
namespace mozilla {
namespace pkix {
enum class DigestAlgorithm {
sha512 = 1,
sha384 = 2,
sha256 = 3,
sha1 = 4,
};
enum class NamedCurve {
// secp521r1 (OID 1.3.132.0.35, RFC 5480)
secp521r1 = 1,
// secp384r1 (OID 1.3.132.0.34, RFC 5480)
secp384r1 = 2,
// secp256r1 (OID 1.2.840.10045.3.1.7, RFC 5480)
secp256r1 = 3,
};
struct SignedDigest final {
Input digest;
DigestAlgorithm digestAlgorithm;
Input signature;
void operator=(const SignedDigest&) = delete;
};
enum class EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 };
enum class KeyUsage : uint8_t {
digitalSignature = 0,
nonRepudiation = 1,
keyEncipherment = 2,
dataEncipherment = 3,
keyAgreement = 4,
keyCertSign = 5,
// cRLSign = 6,
// encipherOnly = 7,
// decipherOnly = 8,
noParticularKeyUsageRequired = 0xff,
};
enum class KeyPurposeId {
anyExtendedKeyUsage = 0,
id_kp_serverAuth = 1, // id-kp-serverAuth
id_kp_clientAuth = 2, // id-kp-clientAuth
id_kp_codeSigning = 3, // id-kp-codeSigning
id_kp_emailProtection = 4, // id-kp-emailProtection
id_kp_OCSPSigning = 9, // id-kp-OCSPSigning
};
struct CertPolicyId final {
uint16_t numBytes;
static const uint16_t MAX_BYTES = 24;
uint8_t bytes[MAX_BYTES];
bool IsAnyPolicy() const;
bool operator==(const CertPolicyId& other) const;
static const CertPolicyId anyPolicy;
};
enum class TrustLevel {
TrustAnchor = 1, // certificate is a trusted root CA certificate or
// equivalent *for the given policy*.
ActivelyDistrusted = 2, // certificate is known to be bad
InheritsTrust = 3 // certificate must chain to a trust anchor
};
// Extensions extracted during the verification flow.
// See TrustDomain::NoteAuxiliaryExtension.
enum class AuxiliaryExtension {
// Certificate Transparency data, specifically Signed Certificate
// Timestamps (SCTs). See RFC 6962.
// SCT list embedded in the end entity certificate. Called by BuildCertChain
// after the certificate containing the SCTs has passed the revocation checks.
EmbeddedSCTList = 1,
// SCT list from OCSP response. Called by VerifyEncodedOCSPResponse
// when its result is a success and the SCT list is present.
SCTListFromOCSPResponse = 2
};
// CertID references the information needed to do revocation checking for the
// certificate issued by the given issuer with the given serial number.
//
// issuer must be the DER-encoded issuer field from the certificate for which
// revocation checking is being done, **NOT** the subject field of the issuer
// certificate. (Those two fields must be equal to each other, but they may not
// be encoded exactly the same, and the encoding matters for OCSP.)
// issuerSubjectPublicKeyInfo is the entire DER-encoded subjectPublicKeyInfo
// field from the issuer's certificate. serialNumber is the entire DER-encoded
// serial number from the subject certificate (the certificate for which we are
// checking the revocation status).
struct CertID final {
public:
CertID(Input aIssuer, Input aIssuerSubjectPublicKeyInfo, Input aSerialNumber)
: issuer(aIssuer),
issuerSubjectPublicKeyInfo(aIssuerSubjectPublicKeyInfo),
serialNumber(aSerialNumber) {}
const Input issuer;
const Input issuerSubjectPublicKeyInfo;
const Input serialNumber;
void operator=(const CertID&) = delete;
};
typedef std::unique_ptr<CertID> ScopedCertID;
class DERArray {
public:
// Returns the number of DER-encoded items in the array.
virtual size_t GetLength() const = 0;
// Returns a weak (non-owning) pointer the ith DER-encoded item in the array
// (0-indexed). The result is guaranteed to be non-null if i < GetLength(),
// and the result is guaranteed to be nullptr if i >= GetLength().
virtual const Input* GetDER(size_t i) const = 0;
protected:
DERArray() {}
virtual ~DERArray() {}
};
// Applications control the behavior of path building and verification by
// implementing the TrustDomain interface. The TrustDomain is used for all
// cryptography and for determining which certificates are trusted or
// distrusted.
class TrustDomain {
public:
virtual ~TrustDomain() {}
// Determine the level of trust in the given certificate for the given role.
// This will be called for every certificate encountered during path
// building.
//
// When policy.IsAnyPolicy(), then no policy-related checking should be done.
// When !policy.IsAnyPolicy(), then GetCertTrust MUST NOT return with
// trustLevel == TrustAnchor unless the given cert is considered a trust
// anchor *for that policy*. In particular, if the user has marked an
// intermediate certificate as trusted, but that intermediate isn't in the
// list of EV roots, then GetCertTrust must result in
// trustLevel == InheritsTrust instead of trustLevel == TrustAnchor
// (assuming the candidate cert is not actively distrusted).
virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA,
const CertPolicyId& policy,
Input candidateCertDER,
/*out*/ TrustLevel& trustLevel) = 0;
class IssuerChecker {
public:
// potentialIssuerDER is the complete DER encoding of the certificate to
// be checked as a potential issuer.
//
// If additionalNameConstraints is not nullptr then it must point to an
// encoded NameConstraints extension value; in that case, those name
// constraints will be checked in addition to any any name constraints
// contained in potentialIssuerDER.
virtual Result Check(Input potentialIssuerDER,
/*optional*/ const Input* additionalNameConstraints,
/*out*/ bool& keepGoing) = 0;
protected:
IssuerChecker();
virtual ~IssuerChecker();
IssuerChecker(const IssuerChecker&) = delete;
void operator=(const IssuerChecker&) = delete;
};
// Search for a CA certificate with the given name. The implementation must
// call checker.Check with the DER encoding of the potential issuer
// certificate. The implementation must follow these rules:
//
// * The implementation must be reentrant and must limit the amount of stack
// space it uses; see the note on reentrancy and stack usage below.
// * When checker.Check does not return Success then immediately return its
// return value.
// * When checker.Check returns Success and sets keepGoing = false, then
// immediately return Success.
// * When checker.Check returns Success and sets keepGoing = true, then
// call checker.Check again with a different potential issuer certificate,
// if any more are available.
// * When no more potential issuer certificates are available, return
// Success.
// * Don't call checker.Check with the same potential issuer certificate more
// than once in a given call of FindIssuer.
// * The given time parameter may be used to filter out certificates that are
// not valid at the given time, or it may be ignored.
//
// Note on reentrancy and stack usage: checker.Check will attempt to
// recursively build a certificate path from the potential issuer it is given
// to a trusted root, as determined by this TrustDomain. That means that
// checker.Check may call any/all of the methods on this TrustDomain. In
// particular, there will be call stacks that look like this:
//
// BuildCertChain
// [...]
// TrustDomain::FindIssuer
// [...]
// IssuerChecker::Check
// [...]
// TrustDomain::FindIssuer
// [...]
// IssuerChecker::Check
// [...]
//
// checker.Check is responsible for limiting the recursion to a reasonable
// limit.
//
// checker.Check will verify that the subject's issuer field matches the
// potential issuer's subject field. It will also check that the potential
// issuer is valid at the given time. However, if the FindIssuer
// implementation has an efficient way of filtering potential issuers by name
// and/or validity period itself, then it is probably better for performance
// for it to do so.
virtual Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker,
Time time) = 0;
// Called as soon as we think we have a valid chain but before revocation
// checks are done. This function can be used to compute additional checks,
// especially checks that require the entire certificate chain. This callback
// can also be used to save a copy of the built certificate chain for later
// use.
//
// This function may be called multiple times, regardless of whether it
// returns success or failure. It is guaranteed that BuildCertChain will not
// return Success unless the last call to IsChainValid returns Success.
// Further,
// it is guaranteed that when BuildCertChain returns Success the last chain
// passed to IsChainValid is the valid chain that should be used for further
// operations that require the whole chain.
//
// Keep in mind, in particular, that if the application saves a copy of the
// certificate chain the last invocation of IsChainValid during a validation,
// it is still possible for BuildCertChain to fail, in which case the
// application must not assume anything about the validity of the last
// certificate chain passed to IsChainValid; especially, it would be very
// wrong to assume that the certificate chain is valid.
//
// certChain.GetDER(0) is the trust anchor.
virtual Result IsChainValid(const DERArray& certChain, Time time,
const CertPolicyId& requiredPolicy) = 0;
virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA,
const CertID& certID, Time time,
Duration validityDuration,
/*optional*/ const Input* stapledOCSPresponse,
/*optional*/ const Input* aiaExtension) = 0;
// Check that the given digest algorithm is acceptable for use in signatures.
//
// Return Success if the algorithm is acceptable,
// Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED if the algorithm is not
// acceptable, or another error code if another error occurred.
virtual Result CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg,
EndEntityOrCA endEntityOrCA,
Time notBefore) = 0;
// Check that the RSA public key size is acceptable.
//
// Return Success if the key size is acceptable,
// Result::ERROR_INADEQUATE_KEY_SIZE if the key size is not acceptable,
// or another error code if another error occurred.
virtual Result CheckRSAPublicKeyModulusSizeInBits(
EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) = 0;
// Verify the given RSA PKCS#1.5 signature on the given digest using the
// given RSA public key.
//
// CheckRSAPublicKeyModulusSizeInBits will be called before calling this
// function, so it is not necessary to repeat those checks here. However,
// VerifyRSAPKCS1SignedDigest *is* responsible for doing the mathematical
// verification of the public key validity as specified in NIST SP 800-56A.
virtual Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo) = 0;
// Check that the given named ECC curve is acceptable for ECDSA signatures.
//
// Return Success if the curve is acceptable,
// Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE if the curve is not acceptable,
// or another error code if another error occurred.
virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA,
NamedCurve curve) = 0;
// Verify the given ECDSA signature on the given digest using the given ECC
// public key.
//
// CheckECDSACurveIsAcceptable will be called before calling this function,
// so it is not necessary to repeat that check here. However,
// VerifyECDSASignedDigest *is* responsible for doing the mathematical
// verification of the public key validity as specified in NIST SP 800-56A.
virtual Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo) = 0;
// Check that the validity duration is acceptable.
//
// Return Success if the validity duration is acceptable,
// Result::ERROR_VALIDITY_TOO_LONG if the validity duration is not acceptable,
// or another error code if another error occurred.
virtual Result CheckValidityIsAcceptable(Time notBefore, Time notAfter,
EndEntityOrCA endEntityOrCA,
KeyPurposeId keyPurpose) = 0;
// For compatibility, a CA certificate with an extended key usage that
// contains the id-Netscape-stepUp OID but does not contain the
// id-kp-serverAuth OID may be considered valid for issuing server auth
// certificates. This function allows TrustDomain implementations to control
// this setting based on the start of the validity period of the certificate
// in question.
virtual Result NetscapeStepUpMatchesServerAuth(Time notBefore,
/*out*/ bool& matches) = 0;
// Some certificate or OCSP response extensions do not directly participate
// in the verification flow, but might still be of interest to the clients
// (notably Certificate Transparency data, RFC 6962). Such extensions are
// extracted and passed to this function for further processing.
virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
Input extensionData) = 0;
// Compute a digest of the data in item using the given digest algorithm.
//
// item contains the data to hash.
// digestBuf points to a buffer to where the digest will be written.
// digestBufLen will be the size of the digest output (20 for SHA-1,
// 32 for SHA-256, etc.).
//
// TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
// other, extensive, memory safety efforts in mozilla::pkix, and we should
// find a way to provide a more-obviously-safe interface.
virtual Result DigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf, size_t digestBufLen) = 0;
protected:
TrustDomain() {}
TrustDomain(const TrustDomain&) = delete;
void operator=(const TrustDomain&) = delete;
};
enum class FallBackToSearchWithinSubject { No = 0, Yes = 1 };
// Applications control the behavior of matching presented name information from
// a certificate against a reference hostname by implementing the
// NameMatchingPolicy interface. Used in concert with CheckCertHostname.
class NameMatchingPolicy {
public:
virtual ~NameMatchingPolicy() {}
// Given that the certificate in question has a notBefore field with the given
// value, should name matching fall back to searching within the subject
// common name field?
virtual Result FallBackToCommonName(
Time notBefore,
/*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) = 0;
protected:
NameMatchingPolicy() {}
NameMatchingPolicy(const NameMatchingPolicy&) = delete;
void operator=(const NameMatchingPolicy&) = delete;
};
}
} // namespace mozilla::pkix
#endif // mozilla_pkix_pkixtypes_h

Просмотреть файл

@ -0,0 +1,265 @@
/* -*- 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.
*/
#ifndef mozilla_pkix_pkixutil_h
#define mozilla_pkix_pkixutil_h
#include "mozpkix/pkixder.h"
namespace mozilla {
namespace pkix {
// During path building and verification, we build a linked list of BackCerts
// from the current cert toward the end-entity certificate. The linked list
// is used to verify properties that aren't local to the current certificate
// and/or the direct link between the current certificate and its issuer,
// such as name constraints.
//
// Each BackCert contains pointers to all the given certificate's extensions
// so that we can parse the extension block once and then process the
// extensions in an order that may be different than they appear in the cert.
class BackCert final {
public:
// certDER and childCert must be valid for the lifetime of BackCert.
BackCert(Input aCertDER, EndEntityOrCA aEndEntityOrCA,
const BackCert* aChildCert)
: der(aCertDER),
endEntityOrCA(aEndEntityOrCA),
childCert(aChildCert),
version(der::Version::Uninitialized) {}
Result Init();
const Input GetDER() const { return der; }
const der::SignedDataWithSignature& GetSignedData() const {
return signedData;
}
der::Version GetVersion() const { return version; }
const Input GetSerialNumber() const { return serialNumber; }
const Input GetSignature() const { return signature; }
const Input GetIssuer() const { return issuer; }
// XXX: "validity" is a horrible name for the structure that holds
// notBefore & notAfter, but that is the name used in RFC 5280 and we use the
// RFC 5280 names for everything.
const Input GetValidity() const { return validity; }
const Input GetSubject() const { return subject; }
const Input GetSubjectPublicKeyInfo() const { return subjectPublicKeyInfo; }
const Input* GetAuthorityInfoAccess() const {
return MaybeInput(authorityInfoAccess);
}
const Input* GetBasicConstraints() const {
return MaybeInput(basicConstraints);
}
const Input* GetCertificatePolicies() const {
return MaybeInput(certificatePolicies);
}
const Input* GetExtKeyUsage() const { return MaybeInput(extKeyUsage); }
const Input* GetKeyUsage() const { return MaybeInput(keyUsage); }
const Input* GetInhibitAnyPolicy() const {
return MaybeInput(inhibitAnyPolicy);
}
const Input* GetNameConstraints() const {
return MaybeInput(nameConstraints);
}
const Input* GetSubjectAltName() const { return MaybeInput(subjectAltName); }
const Input* GetRequiredTLSFeatures() const {
return MaybeInput(requiredTLSFeatures);
}
const Input* GetSignedCertificateTimestamps() const {
return MaybeInput(signedCertificateTimestamps);
}
private:
const Input der;
public:
const EndEntityOrCA endEntityOrCA;
BackCert const* const childCert;
private:
// When parsing certificates in BackCert::Init, we don't accept empty
// extensions. Consequently, we don't have to store a distinction between
// empty extensions and extensions that weren't included. However, when
// *processing* extensions, we distinguish between whether an extension was
// included or not based on whetehr the GetXXX function for the extension
// returns nullptr.
static inline const Input* MaybeInput(const Input& item) {
return item.GetLength() > 0 ? &item : nullptr;
}
der::SignedDataWithSignature signedData;
der::Version version;
Input serialNumber;
Input signature;
Input issuer;
// XXX: "validity" is a horrible name for the structure that holds
// notBefore & notAfter, but that is the name used in RFC 5280 and we use the
// RFC 5280 names for everything.
Input validity;
Input subject;
Input subjectPublicKeyInfo;
Input authorityInfoAccess;
Input basicConstraints;
Input certificatePolicies;
Input extKeyUsage;
Input inhibitAnyPolicy;
Input keyUsage;
Input nameConstraints;
Input subjectAltName;
Input criticalNetscapeCertificateType;
Input requiredTLSFeatures;
Input signedCertificateTimestamps; // RFC 6962 (Certificate Transparency)
Result RememberExtension(Reader& extnID, Input extnValue, bool critical,
/*out*/ bool& understood);
BackCert(const BackCert&) = delete;
void operator=(const BackCert&) = delete;
};
class NonOwningDERArray final : public DERArray {
public:
NonOwningDERArray() : numItems(0) {
// we don't need to initialize the items array because we always check
// numItems before accessing i.
}
size_t GetLength() const override { return numItems; }
const Input* GetDER(size_t i) const override {
return i < numItems ? &items[i] : nullptr;
}
Result Append(Input der) {
if (numItems >= MAX_LENGTH) {
return Result::FATAL_ERROR_INVALID_ARGS;
}
Result rv = items[numItems].Init(der); // structure assignment
if (rv != Success) {
return rv;
}
++numItems;
return Success;
}
// Public so we can static_assert on this. Keep in sync with MAX_SUBCA_COUNT.
static const size_t MAX_LENGTH = 8;
private:
Input items[MAX_LENGTH]; // avoids any heap allocations
size_t numItems;
NonOwningDERArray(const NonOwningDERArray&) = delete;
void operator=(const NonOwningDERArray&) = delete;
};
// Extracts the SignedCertificateTimestampList structure which is encoded as an
// OCTET STRING within the X.509v3 / OCSP extensions (see RFC 6962 section 3.3).
Result ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
Input& sctList);
inline unsigned int DaysBeforeYear(unsigned int year) {
assert(year <= 9999);
return ((year - 1u) * 365u) +
((year - 1u) / 4u) // leap years are every 4 years,
- ((year - 1u) / 100u) // except years divisible by 100,
+ ((year - 1u) / 400u); // except years divisible by 400.
}
static const size_t MAX_DIGEST_SIZE_IN_BYTES = 512 / 8; // sha-512
Result DigestSignedData(TrustDomain& trustDomain,
const der::SignedDataWithSignature& signedData,
/*out*/ uint8_t (&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES],
/*out*/ der::PublicKeyAlgorithm& publicKeyAlg,
/*out*/ SignedDigest& signedDigest);
Result VerifySignedDigest(TrustDomain& trustDomain,
der::PublicKeyAlgorithm publicKeyAlg,
const SignedDigest& signedDigest,
Input signerSubjectPublicKeyInfo);
// Combines DigestSignedData and VerifySignedDigest
Result VerifySignedData(TrustDomain& trustDomain,
const der::SignedDataWithSignature& signedData,
Input signerSubjectPublicKeyInfo);
// Extracts the key parameters from |subjectPublicKeyInfo|, invoking
// the relevant methods of |trustDomain|.
Result CheckSubjectPublicKeyInfo(Input subjectPublicKeyInfo,
TrustDomain& trustDomain,
EndEntityOrCA endEntityOrCA);
// In a switch over an enum, sometimes some compilers are not satisfied that
// all control flow paths have been considered unless there is a default case.
// However, in our code, such a default case is almost always unreachable dead
// code. That can be particularly problematic when the compiler wants the code
// to choose a value, such as a return value, for the default case, but there's
// no appropriate "impossible case" value to choose.
//
// MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM accounts for this. Example:
//
// // In xy.cpp
// #include "xt.h"
//
// enum class XY { X, Y };
//
// int func(XY xy) {
// switch (xy) {
// case XY::X: return 1;
// case XY::Y; return 2;
// MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
// }
// }
#if defined(__clang__)
// Clang will warn if not all cases are covered (-Wswitch-enum) AND it will
// warn if a switch statement that covers every enum label has a default case
// (-W-covered-switch-default). Versions prior to 3.5 warned about unreachable
// code in such default cases (-Wunreachable-code) even when
// -W-covered-switch-default was disabled, but that changed in Clang 3.5.
#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM // empty
#elif defined(__GNUC__)
// GCC will warn if not all cases are covered (-Wswitch-enum). It does not
// assume that the default case is unreachable.
#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \
default: \
assert(false); \
__builtin_unreachable();
#elif defined(_MSC_VER)
// MSVC will warn if not all cases are covered (C4061, level 4). It does not
// assume that the default case is unreachable.
#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \
default: \
assert(false); \
__assume(0);
#else
#error Unsupported compiler for MOZILLA_PKIX_UNREACHABLE_DEFAULT.
#endif
}
} // namespace mozilla::pkix
#endif // mozilla_pkix_pkixutil_h

Просмотреть файл

@ -0,0 +1,418 @@
/* -*- 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 "mozpkix/pkix.h"
#include "mozpkix/pkixcheck.h"
#include "mozpkix/pkixutil.h"
namespace mozilla { namespace pkix {
static Result BuildForward(TrustDomain& trustDomain,
const BackCert& subject,
Time time,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
/*optional*/ const Input* stapledOCSPResponse,
unsigned int subCACount,
unsigned int& buildForwardCallBudget);
TrustDomain::IssuerChecker::IssuerChecker() { }
TrustDomain::IssuerChecker::~IssuerChecker() { }
// The implementation of TrustDomain::IssuerTracker is in a subclass only to
// hide the implementation from external users.
class PathBuildingStep final : public TrustDomain::IssuerChecker
{
public:
PathBuildingStep(TrustDomain& aTrustDomain, const BackCert& aSubject,
Time aTime, KeyPurposeId aRequiredEKUIfPresent,
const CertPolicyId& aRequiredPolicy,
/*optional*/ const Input* aStapledOCSPResponse,
unsigned int aSubCACount, Result aDeferredSubjectError,
unsigned int& aBuildForwardCallBudget)
: trustDomain(aTrustDomain)
, subject(aSubject)
, time(aTime)
, requiredEKUIfPresent(aRequiredEKUIfPresent)
, requiredPolicy(aRequiredPolicy)
, stapledOCSPResponse(aStapledOCSPResponse)
, subCACount(aSubCACount)
, deferredSubjectError(aDeferredSubjectError)
, subjectSignaturePublicKeyAlg(der::PublicKeyAlgorithm::Uninitialized)
, result(Result::FATAL_ERROR_LIBRARY_FAILURE)
, resultWasSet(false)
, buildForwardCallBudget(aBuildForwardCallBudget)
{
}
Result Check(Input potentialIssuerDER,
/*optional*/ const Input* additionalNameConstraints,
/*out*/ bool& keepGoing) override;
Result CheckResult() const;
private:
TrustDomain& trustDomain;
const BackCert& subject;
const Time time;
const KeyPurposeId requiredEKUIfPresent;
const CertPolicyId& requiredPolicy;
/*optional*/ Input const* const stapledOCSPResponse;
const unsigned int subCACount;
const Result deferredSubjectError;
// Initialized lazily.
uint8_t subjectSignatureDigestBuf[MAX_DIGEST_SIZE_IN_BYTES];
der::PublicKeyAlgorithm subjectSignaturePublicKeyAlg;
SignedDigest subjectSignature;
Result RecordResult(Result currentResult, /*out*/ bool& keepGoing);
Result result;
bool resultWasSet;
unsigned int& buildForwardCallBudget;
PathBuildingStep(const PathBuildingStep&) = delete;
void operator=(const PathBuildingStep&) = delete;
};
Result
PathBuildingStep::RecordResult(Result newResult, /*out*/ bool& keepGoing)
{
if (newResult == Result::ERROR_UNTRUSTED_CERT) {
newResult = Result::ERROR_UNTRUSTED_ISSUER;
} else if (newResult == Result::ERROR_EXPIRED_CERTIFICATE) {
newResult = Result::ERROR_EXPIRED_ISSUER_CERTIFICATE;
} else if (newResult == Result::ERROR_NOT_YET_VALID_CERTIFICATE) {
newResult = Result::ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE;
}
if (resultWasSet) {
if (result == Success) {
return NotReached("RecordResult called after finding a chain",
Result::FATAL_ERROR_INVALID_STATE);
}
// If every potential issuer has the same problem (e.g. expired) and/or if
// there is only one bad potential issuer, then return a more specific
// error. Otherwise, punt on trying to decide which error should be
// returned by returning the generic Result::ERROR_UNKNOWN_ISSUER error.
if (newResult != Success && newResult != result) {
newResult = Result::ERROR_UNKNOWN_ISSUER;
}
}
result = newResult;
resultWasSet = true;
keepGoing = result != Success;
return Success;
}
Result
PathBuildingStep::CheckResult() const
{
if (!resultWasSet) {
return Result::ERROR_UNKNOWN_ISSUER;
}
return result;
}
// The code that executes in the inner loop of BuildForward
Result
PathBuildingStep::Check(Input potentialIssuerDER,
/*optional*/ const Input* additionalNameConstraints,
/*out*/ bool& keepGoing)
{
BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA,
&subject);
Result rv = potentialIssuer.Init();
if (rv != Success) {
return RecordResult(rv, keepGoing);
}
// Simple TrustDomain::FindIssuers implementations may pass in all possible
// CA certificates without any filtering. Because of this, we don't consider
// a mismatched name to be an error. Instead, we just pretend that any
// certificate without a matching name was never passed to us. In particular,
// we treat the case where the TrustDomain only asks us to check CA
// certificates with mismatched names as equivalent to the case where the
// TrustDomain never called Check() at all.
if (!InputsAreEqual(potentialIssuer.GetSubject(), subject.GetIssuer())) {
keepGoing = true;
return Success;
}
// Loop prevention, done as recommended by RFC4158 Section 5.2
// TODO: this doesn't account for subjectAltNames!
// TODO(perf): This probably can and should be optimized in some way.
for (const BackCert* prev = potentialIssuer.childCert; prev;
prev = prev->childCert) {
if (InputsAreEqual(potentialIssuer.GetSubjectPublicKeyInfo(),
prev->GetSubjectPublicKeyInfo()) &&
InputsAreEqual(potentialIssuer.GetSubject(), prev->GetSubject())) {
// XXX: error code
return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing);
}
}
if (potentialIssuer.GetNameConstraints()) {
rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(),
subject, requiredEKUIfPresent);
if (rv != Success) {
return RecordResult(rv, keepGoing);
}
}
if (additionalNameConstraints) {
rv = CheckNameConstraints(*additionalNameConstraints, subject,
requiredEKUIfPresent);
if (rv != Success) {
return RecordResult(rv, keepGoing);
}
}
rv = CheckTLSFeatures(subject, potentialIssuer);
if (rv != Success) {
return RecordResult(rv, keepGoing);
}
// If we've ran out of budget, stop searching.
if (buildForwardCallBudget == 0) {
Result savedRv = RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing);
keepGoing = false;
return savedRv;
}
buildForwardCallBudget--;
// RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the
// subject public key MUST NOT be used to verify signatures on certificates
// or CRLs unless the corresponding keyCertSign or cRLSign bit is set."
rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign,
requiredEKUIfPresent, requiredPolicy, nullptr, subCACount,
buildForwardCallBudget);
if (rv != Success) {
return RecordResult(rv, keepGoing);
}
// Calculate the digest of the subject's signed data if we haven't already
// done so. We do this lazily to avoid doing it at all if we backtrack before
// getting to this point. We cache the result to avoid recalculating it if we
// backtrack after getting to this point.
if (subjectSignature.digest.GetLength() == 0) {
rv = DigestSignedData(trustDomain, subject.GetSignedData(),
subjectSignatureDigestBuf,
subjectSignaturePublicKeyAlg, subjectSignature);
if (rv != Success) {
return rv;
}
}
rv = VerifySignedDigest(trustDomain, subjectSignaturePublicKeyAlg,
subjectSignature,
potentialIssuer.GetSubjectPublicKeyInfo());
if (rv != Success) {
return RecordResult(rv, keepGoing);
}
// We avoid doing revocation checking for expired certificates because OCSP
// responders are allowed to forget about expired certificates, and many OCSP
// responders return an error when asked for the status of an expired
// certificate.
if (deferredSubjectError != Result::ERROR_EXPIRED_CERTIFICATE) {
CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(),
subject.GetSerialNumber());
Time notBefore(Time::uninitialized);
Time notAfter(Time::uninitialized);
// This should never fail. If we're here, we've already parsed the validity
// and checked that the given time is in the certificate's validity period.
rv = ParseValidity(subject.GetValidity(), &notBefore, &notAfter);
if (rv != Success) {
return rv;
}
Duration validityDuration(notAfter, notBefore);
rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time,
validityDuration, stapledOCSPResponse,
subject.GetAuthorityInfoAccess());
if (rv != Success) {
// Since this is actually a problem with the current subject certificate
// (rather than the issuer), it doesn't make sense to keep going; all
// paths through this certificate will fail.
Result savedRv = RecordResult(rv, keepGoing);
keepGoing = false;
return savedRv;
}
if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
const Input* sctExtension = subject.GetSignedCertificateTimestamps();
if (sctExtension) {
Input sctList;
rv = ExtractSignedCertificateTimestampListFromExtension(*sctExtension,
sctList);
if (rv != Success) {
// Again, the problem is with this certificate, and all paths through
// it will fail.
Result savedRv = RecordResult(rv, keepGoing);
keepGoing = false;
return savedRv;
}
trustDomain.NoteAuxiliaryExtension(AuxiliaryExtension::EmbeddedSCTList,
sctList);
}
}
}
return RecordResult(Success, keepGoing);
}
// Recursively build the path from the given subject certificate to the root.
//
// Be very careful about changing the order of checks. The order is significant
// because it affects which error we return when a certificate or certificate
// chain has multiple problems. See the error ranking documentation in
// pkix/pkix.h.
static Result
BuildForward(TrustDomain& trustDomain,
const BackCert& subject,
Time time,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
/*optional*/ const Input* stapledOCSPResponse,
unsigned int subCACount,
unsigned int& buildForwardCallBudget)
{
Result rv;
TrustLevel trustLevel;
// If this is an end-entity and not a trust anchor, we defer reporting
// any error found here until after attempting to find a valid chain.
// See the explanation of error prioritization in pkix.h.
rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
requiredKeyUsageIfPresent,
requiredEKUIfPresent, requiredPolicy,
subCACount, trustLevel);
Result deferredEndEntityError = Success;
if (rv != Success) {
if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
trustLevel != TrustLevel::TrustAnchor) {
deferredEndEntityError = rv;
} else {
return rv;
}
}
if (trustLevel == TrustLevel::TrustAnchor) {
// End of the recursion.
NonOwningDERArray chain;
for (const BackCert* cert = &subject; cert; cert = cert->childCert) {
rv = chain.Append(cert->GetDER());
if (rv != Success) {
return NotReached("NonOwningDERArray::SetItem failed.", rv);
}
}
// This must be done here, after the chain is built but before any
// revocation checks have been done.
return trustDomain.IsChainValid(chain, time, requiredPolicy);
}
if (subject.endEntityOrCA == EndEntityOrCA::MustBeCA) {
// Avoid stack overflows and poor performance by limiting cert chain
// length.
static const unsigned int MAX_SUBCA_COUNT = 6;
static_assert(1/*end-entity*/ + MAX_SUBCA_COUNT + 1/*root*/ ==
NonOwningDERArray::MAX_LENGTH,
"MAX_SUBCA_COUNT and NonOwningDERArray::MAX_LENGTH mismatch.");
if (subCACount >= MAX_SUBCA_COUNT) {
return Result::ERROR_UNKNOWN_ISSUER;
}
++subCACount;
} else {
assert(subCACount == 0);
}
// Find a trusted issuer.
PathBuildingStep pathBuilder(trustDomain, subject, time,
requiredEKUIfPresent, requiredPolicy,
stapledOCSPResponse, subCACount,
deferredEndEntityError, buildForwardCallBudget);
// TODO(bug 965136): Add SKI/AKI matching optimizations
rv = trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time);
if (rv != Success) {
return rv;
}
rv = pathBuilder.CheckResult();
if (rv != Success) {
return rv;
}
// If we found a valid chain but deferred reporting an error with the
// end-entity certificate, report it now.
if (deferredEndEntityError != Success) {
return deferredEndEntityError;
}
// We've built a valid chain from the subject cert up to a trusted root.
return Success;
}
Result
BuildCertChain(TrustDomain& trustDomain, Input certDER,
Time time, EndEntityOrCA endEntityOrCA,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
/*optional*/ const Input* stapledOCSPResponse)
{
// XXX: Support the legacy use of the subject CN field for indicating the
// domain name the certificate is valid for.
BackCert cert(certDER, endEntityOrCA, nullptr);
Result rv = cert.Init();
if (rv != Success) {
return rv;
}
// See bug 1056341 for context. If mozilla::pkix is being used in an
// environment where there are many certificates that all have the same
// distinguished name as their subject and issuer (but different SPKIs - see
// the loop prevention as per RFC4158 Section 5.2 in PathBuildingStep::Check),
// the space to search becomes exponential. Because it would be prohibitively
// expensive to explore the entire space, we introduce a budget here that,
// when exhausted, terminates the search with the result
// Result::ERROR_UNKNOWN_ISSUER. Essentially, we limit the total number of
// times `BuildForward` can be called. The current value appears to be a good
// balance between finding a path when one exists (when the space isn't too
// large) and timing out quickly enough when the space is too large or there
// is no valid path to a trust anchor.
unsigned int buildForwardCallBudget = 200000;
return BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent,
requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse,
0/*subCACount*/, buildForwardCallBudget);
}
} } // namespace mozilla::pkix

Просмотреть файл

@ -0,0 +1,323 @@
/* -*- 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 2014 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 "mozpkix/pkixutil.h"
namespace mozilla { namespace pkix {
Result
BackCert::Init()
{
Result rv;
// Certificate ::= SEQUENCE {
// tbsCertificate TBSCertificate,
// signatureAlgorithm AlgorithmIdentifier,
// signatureValue BIT STRING }
Reader tbsCertificate;
// The scope of |input| and |certificate| are limited to this block so we
// don't accidentally confuse them for tbsCertificate later.
{
Reader certificate;
rv = der::ExpectTagAndGetValueAtEnd(der, der::SEQUENCE, certificate);
if (rv != Success) {
return rv;
}
rv = der::SignedData(certificate, tbsCertificate, signedData);
if (rv != Success) {
return rv;
}
rv = der::End(certificate);
if (rv != Success) {
return rv;
}
}
// TBSCertificate ::= SEQUENCE {
// version [0] EXPLICIT Version DEFAULT v1,
// serialNumber CertificateSerialNumber,
// signature AlgorithmIdentifier,
// issuer Name,
// validity Validity,
// subject Name,
// subjectPublicKeyInfo SubjectPublicKeyInfo,
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
// -- If present, version MUST be v2 or v3
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
// -- If present, version MUST be v2 or v3
// extensions [3] EXPLICIT Extensions OPTIONAL
// -- If present, version MUST be v3
// }
rv = der::OptionalVersion(tbsCertificate, version);
if (rv != Success) {
return rv;
}
rv = der::CertificateSerialNumber(tbsCertificate, serialNumber);
if (rv != Success) {
return rv;
}
rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature);
if (rv != Success) {
return rv;
}
rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer);
if (rv != Success) {
return rv;
}
rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity);
if (rv != Success) {
return rv;
}
// TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate
// that the name is syntactically valid, if they care. In Gecko we do this
// implicitly by parsing the certificate into a CERTCertificate object.
// Instead of relying on the caller to do this, we should do it ourselves.
rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject);
if (rv != Success) {
return rv;
}
rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE,
subjectPublicKeyInfo);
if (rv != Success) {
return rv;
}
static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED;
// According to RFC 5280, all fields below this line are forbidden for
// certificate versions less than v3. However, for compatibility reasons,
// we parse v1/v2 certificates in the same way as v3 certificates. So if
// these fields appear in a v1 certificate, they will be used.
// Ignore issuerUniqueID if present.
if (tbsCertificate.Peek(CSC | 1)) {
rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1);
if (rv != Success) {
return rv;
}
}
// Ignore subjectUniqueID if present.
if (tbsCertificate.Peek(CSC | 2)) {
rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2);
if (rv != Success) {
return rv;
}
}
rv = der::OptionalExtensions(
tbsCertificate, CSC | 3,
[this](Reader& extnID, const Input& extnValue, bool critical,
/*out*/ bool& understood) {
return RememberExtension(extnID, extnValue, critical, understood);
});
if (rv != Success) {
return rv;
}
// The Netscape Certificate Type extension is an obsolete
// Netscape-proprietary mechanism that we ignore in favor of the standard
// extensions. However, some CAs have issued certificates with the Netscape
// Cert Type extension marked critical. Thus, for compatibility reasons, we
// "understand" this extension by ignoring it when it is not critical, and
// by ensuring that the equivalent standardized extensions are present when
// it is marked critical, based on the assumption that the information in
// the Netscape Cert Type extension is consistent with the information in
// the standard extensions.
//
// Here is a mapping between the Netscape Cert Type extension and the
// standard extensions:
//
// Netscape Cert Type | BasicConstraints.cA | Extended Key Usage
// --------------------+-----------------------+----------------------
// SSL Server | false | id_kp_serverAuth
// SSL Client | false | id_kp_clientAuth
// S/MIME Client | false | id_kp_emailProtection
// Object Signing | false | id_kp_codeSigning
// SSL Server CA | true | id_kp_serverAuth
// SSL Client CA | true | id_kp_clientAuth
// S/MIME CA | true | id_kp_emailProtection
// Object Signing CA | true | id_kp_codeSigning
if (criticalNetscapeCertificateType.GetLength() > 0 &&
(basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) {
return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION;
}
return der::End(tbsCertificate);
}
Result
BackCert::RememberExtension(Reader& extnID, Input extnValue,
bool critical, /*out*/ bool& understood)
{
understood = false;
// python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15
static const uint8_t id_ce_keyUsage[] = {
0x55, 0x1d, 0x0f
};
// python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17
static const uint8_t id_ce_subjectAltName[] = {
0x55, 0x1d, 0x11
};
// python DottedOIDToCode.py id-ce-basicConstraints 2.5.29.19
static const uint8_t id_ce_basicConstraints[] = {
0x55, 0x1d, 0x13
};
// python DottedOIDToCode.py id-ce-nameConstraints 2.5.29.30
static const uint8_t id_ce_nameConstraints[] = {
0x55, 0x1d, 0x1e
};
// python DottedOIDToCode.py id-ce-certificatePolicies 2.5.29.32
static const uint8_t id_ce_certificatePolicies[] = {
0x55, 0x1d, 0x20
};
// python DottedOIDToCode.py id-ce-policyConstraints 2.5.29.36
static const uint8_t id_ce_policyConstraints[] = {
0x55, 0x1d, 0x24
};
// python DottedOIDToCode.py id-ce-extKeyUsage 2.5.29.37
static const uint8_t id_ce_extKeyUsage[] = {
0x55, 0x1d, 0x25
};
// python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54
static const uint8_t id_ce_inhibitAnyPolicy[] = {
0x55, 0x1d, 0x36
};
// 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
};
// python DottedOIDToCode.py id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5
static const uint8_t id_pkix_ocsp_nocheck[] = {
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05
};
// python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1
static const uint8_t Netscape_certificate_type[] = {
0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01
};
// python DottedOIDToCode.py id-pe-tlsfeature 1.3.6.1.5.5.7.1.24
static const uint8_t id_pe_tlsfeature[] = {
0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x18
};
// python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
// See Section 3.3 of RFC 6962.
static const uint8_t id_embeddedSctList[] = {
0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
};
Input* out = nullptr;
// We already enforce the maximum possible constraints for policies so we
// can safely ignore even critical policy constraint extensions.
//
// XXX: Doing it this way won't allow us to detect duplicate
// policyConstraints extensions, but that's OK because (and only because) we
// ignore the extension.
Input dummyPolicyConstraints;
// We don't need to save the contents of this extension if it is present. We
// just need to handle its presence (it is essentially ignored right now).
Input dummyOCSPNocheck;
// For compatibility reasons, for some extensions we have to allow empty
// extension values. This would normally interfere with our duplicate
// extension checking code. However, as long as the extensions we allow to
// have empty values are also the ones we implicitly allow duplicates of,
// this will work fine.
bool emptyValueAllowed = false;
// RFC says "Conforming CAs MUST mark this extension as non-critical" for
// both authorityKeyIdentifier and subjectKeyIdentifier, and we do not use
// them for anything, so we totally ignore them here.
if (extnID.MatchRest(id_ce_keyUsage)) {
out = &keyUsage;
} else if (extnID.MatchRest(id_ce_subjectAltName)) {
out = &subjectAltName;
} else if (extnID.MatchRest(id_ce_basicConstraints)) {
out = &basicConstraints;
} else if (extnID.MatchRest(id_ce_nameConstraints)) {
out = &nameConstraints;
} else if (extnID.MatchRest(id_ce_certificatePolicies)) {
out = &certificatePolicies;
} else if (extnID.MatchRest(id_ce_policyConstraints)) {
out = &dummyPolicyConstraints;
} else if (extnID.MatchRest(id_ce_extKeyUsage)) {
out = &extKeyUsage;
} else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) {
out = &inhibitAnyPolicy;
} else if (extnID.MatchRest(id_pe_authorityInfoAccess)) {
out = &authorityInfoAccess;
} else if (extnID.MatchRest(id_pe_tlsfeature)) {
out = &requiredTLSFeatures;
} else if (extnID.MatchRest(id_embeddedSctList)) {
out = &signedCertificateTimestamps;
} else if (extnID.MatchRest(id_pkix_ocsp_nocheck) && critical) {
// We need to make sure we don't reject delegated OCSP response signing
// certificates that contain the id-pkix-ocsp-nocheck extension marked as
// critical when validating OCSP responses. Without this, an application
// that implements soft-fail OCSP might ignore a valid Revoked or Unknown
// response, and an application that implements hard-fail OCSP might fail
// to connect to a server given a valid Good response.
out = &dummyOCSPNocheck;
// We allow this extension to have an empty value.
// See http://comments.gmane.org/gmane.ietf.x509/30947
emptyValueAllowed = true;
} else if (extnID.MatchRest(Netscape_certificate_type) && critical) {
out = &criticalNetscapeCertificateType;
}
if (out) {
// Don't allow an empty value for any extension we understand. This way, we
// can test out->GetLength() != 0 or out->Init() to check for duplicates.
if (extnValue.GetLength() == 0 && !emptyValueAllowed) {
return Result::ERROR_EXTENSION_VALUE_INVALID;
}
if (out->Init(extnValue) != Success) {
// Duplicate extension
return Result::ERROR_EXTENSION_VALUE_INVALID;
}
understood = true;
}
return Success;
}
Result
ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
Input& sctList)
{
Reader decodedValue;
Result rv = der::ExpectTagAndGetValueAtEnd(extnValue, der::OCTET_STRING,
decodedValue);
if (rv != Success) {
return rv;
}
return decodedValue.SkipToEnd(sctList);
}
} } // namespace mozilla::pkix

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,611 @@
/* -*- 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 "mozpkix/pkixder.h"
#include "mozpkix/pkixutil.h"
namespace mozilla { namespace pkix { namespace der {
// Too complicated to be inline
Result
ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag, /*out*/ Input& value)
{
Result rv;
rv = input.Read(tag);
if (rv != Success) {
return rv;
}
if ((tag & 0x1F) == 0x1F) {
return Result::ERROR_BAD_DER; // high tag number form not allowed
}
uint16_t length;
// The short form of length is a single byte with the high order bit set
// to zero. The long form of length is one byte with the high order bit
// set, followed by N bytes, where N is encoded in the lowest 7 bits of
// the first byte.
uint8_t length1;
rv = input.Read(length1);
if (rv != Success) {
return rv;
}
if (!(length1 & 0x80)) {
length = length1;
} else if (length1 == 0x81) {
uint8_t length2;
rv = input.Read(length2);
if (rv != Success) {
return rv;
}
if (length2 < 128) {
// Not shortest possible encoding
return Result::ERROR_BAD_DER;
}
length = length2;
} else if (length1 == 0x82) {
rv = input.Read(length);
if (rv != Success) {
return rv;
}
if (length < 256) {
// Not shortest possible encoding
return Result::ERROR_BAD_DER;
}
} else {
// We don't support lengths larger than 2^16 - 1.
return Result::ERROR_BAD_DER;
}
return input.Skip(length, value);
}
static Result
OptionalNull(Reader& input)
{
if (input.Peek(NULLTag)) {
return Null(input);
}
return Success;
}
namespace {
Result
AlgorithmIdentifierValue(Reader& input, /*out*/ Reader& algorithmOIDValue)
{
Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmOIDValue);
if (rv != Success) {
return rv;
}
return OptionalNull(input);
}
} // namespace
Result
SignatureAlgorithmIdentifierValue(Reader& input,
/*out*/ PublicKeyAlgorithm& publicKeyAlgorithm,
/*out*/ DigestAlgorithm& digestAlgorithm)
{
// RFC 5758 Section 3.2 (ECDSA with SHA-2), and RFC 3279 Section 2.2.3
// (ECDSA with SHA-1) say that parameters must be omitted.
//
// RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for
// RSA must be encoded as NULL; we relax that requirement by allowing the
// NULL to be omitted, to match all the other signature algorithms we support
// and for compatibility.
Reader algorithmID;
Result rv = AlgorithmIdentifierValue(input, algorithmID);
if (rv != Success) {
return rv;
}
// RFC 5758 Section 3.2 (ecdsa-with-SHA224 is intentionally excluded)
// python DottedOIDToCode.py ecdsa-with-SHA256 1.2.840.10045.4.3.2
static const uint8_t ecdsa_with_SHA256[] = {
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02
};
// python DottedOIDToCode.py ecdsa-with-SHA384 1.2.840.10045.4.3.3
static const uint8_t ecdsa_with_SHA384[] = {
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03
};
// python DottedOIDToCode.py ecdsa-with-SHA512 1.2.840.10045.4.3.4
static const uint8_t ecdsa_with_SHA512[] = {
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04
};
// RFC 4055 Section 5 (sha224WithRSAEncryption is intentionally excluded)
// python DottedOIDToCode.py sha256WithRSAEncryption 1.2.840.113549.1.1.11
static const uint8_t sha256WithRSAEncryption[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
};
// python DottedOIDToCode.py sha384WithRSAEncryption 1.2.840.113549.1.1.12
static const uint8_t sha384WithRSAEncryption[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c
};
// python DottedOIDToCode.py sha512WithRSAEncryption 1.2.840.113549.1.1.13
static const uint8_t sha512WithRSAEncryption[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d
};
// RFC 3279 Section 2.2.1
// python DottedOIDToCode.py sha-1WithRSAEncryption 1.2.840.113549.1.1.5
static const uint8_t sha_1WithRSAEncryption[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05
};
// NIST Open Systems Environment (OSE) Implementor's Workshop (OIW)
// http://www.oiw.org/agreements/stable/12s-9412.txt (no longer works).
// http://www.imc.org/ietf-pkix/old-archive-97/msg01166.html
// We need to support this this non-PKIX OID for compatibility.
// python DottedOIDToCode.py sha1WithRSASignature 1.3.14.3.2.29
static const uint8_t sha1WithRSASignature[] = {
0x2b, 0x0e, 0x03, 0x02, 0x1d
};
// RFC 3279 Section 2.2.3
// python DottedOIDToCode.py ecdsa-with-SHA1 1.2.840.10045.4.1
static const uint8_t ecdsa_with_SHA1[] = {
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01
};
// Matching is attempted based on a rough estimate of the commonality of the
// algorithm, to minimize the number of MatchRest calls.
if (algorithmID.MatchRest(sha256WithRSAEncryption)) {
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
digestAlgorithm = DigestAlgorithm::sha256;
} else if (algorithmID.MatchRest(ecdsa_with_SHA256)) {
publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
digestAlgorithm = DigestAlgorithm::sha256;
} else if (algorithmID.MatchRest(sha_1WithRSAEncryption)) {
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
digestAlgorithm = DigestAlgorithm::sha1;
} else if (algorithmID.MatchRest(ecdsa_with_SHA1)) {
publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
digestAlgorithm = DigestAlgorithm::sha1;
} else if (algorithmID.MatchRest(ecdsa_with_SHA384)) {
publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
digestAlgorithm = DigestAlgorithm::sha384;
} else if (algorithmID.MatchRest(ecdsa_with_SHA512)) {
publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA;
digestAlgorithm = DigestAlgorithm::sha512;
} else if (algorithmID.MatchRest(sha384WithRSAEncryption)) {
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
digestAlgorithm = DigestAlgorithm::sha384;
} else if (algorithmID.MatchRest(sha512WithRSAEncryption)) {
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
digestAlgorithm = DigestAlgorithm::sha512;
} else if (algorithmID.MatchRest(sha1WithRSASignature)) {
// XXX(bug 1042479): recognize this old OID for compatibility.
publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1;
digestAlgorithm = DigestAlgorithm::sha1;
} else {
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
}
return Success;
}
Result
DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm)
{
return der::Nested(input, SEQUENCE, [&algorithm](Reader& r) -> Result {
Reader algorithmID;
Result rv = AlgorithmIdentifierValue(r, algorithmID);
if (rv != Success) {
return rv;
}
// RFC 4055 Section 2.1
// python DottedOIDToCode.py id-sha1 1.3.14.3.2.26
static const uint8_t id_sha1[] = {
0x2b, 0x0e, 0x03, 0x02, 0x1a
};
// python DottedOIDToCode.py id-sha256 2.16.840.1.101.3.4.2.1
static const uint8_t id_sha256[] = {
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
};
// python DottedOIDToCode.py id-sha384 2.16.840.1.101.3.4.2.2
static const uint8_t id_sha384[] = {
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
};
// python DottedOIDToCode.py id-sha512 2.16.840.1.101.3.4.2.3
static const uint8_t id_sha512[] = {
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
};
// Matching is attempted based on a rough estimate of the commonality of the
// algorithm, to minimize the number of MatchRest calls.
if (algorithmID.MatchRest(id_sha1)) {
algorithm = DigestAlgorithm::sha1;
} else if (algorithmID.MatchRest(id_sha256)) {
algorithm = DigestAlgorithm::sha256;
} else if (algorithmID.MatchRest(id_sha384)) {
algorithm = DigestAlgorithm::sha384;
} else if (algorithmID.MatchRest(id_sha512)) {
algorithm = DigestAlgorithm::sha512;
} else {
return Result::ERROR_INVALID_ALGORITHM;
}
return Success;
});
}
Result
SignedData(Reader& input, /*out*/ Reader& tbs,
/*out*/ SignedDataWithSignature& signedData)
{
Reader::Mark mark(input.GetMark());
Result rv;
rv = ExpectTagAndGetValue(input, SEQUENCE, tbs);
if (rv != Success) {
return rv;
}
rv = input.GetInput(mark, signedData.data);
if (rv != Success) {
return rv;
}
rv = ExpectTagAndGetValue(input, der::SEQUENCE, signedData.algorithm);
if (rv != Success) {
return rv;
}
rv = BitStringWithNoUnusedBits(input, signedData.signature);
if (rv == Result::ERROR_BAD_DER) {
rv = Result::ERROR_BAD_SIGNATURE;
}
return rv;
}
Result
BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value)
{
Reader valueWithUnusedBits;
Result rv = ExpectTagAndGetValue(input, BIT_STRING, valueWithUnusedBits);
if (rv != Success) {
return rv;
}
uint8_t unusedBitsAtEnd;
if (valueWithUnusedBits.Read(unusedBitsAtEnd) != Success) {
return Result::ERROR_BAD_DER;
}
// XXX: Really the constraint should be that unusedBitsAtEnd must be less
// than 7. But, we suspect there are no real-world values in OCSP responses
// or certificates with non-zero unused bits. It seems like NSS assumes this
// in various places, so we enforce it too in order to simplify this code. If
// we find compatibility issues, we'll know we're wrong and we'll have to
// figure out how to shift the bits around.
if (unusedBitsAtEnd != 0) {
return Result::ERROR_BAD_DER;
}
return valueWithUnusedBits.SkipToEnd(value);
}
static inline Result
ReadDigit(Reader& input, /*out*/ unsigned int& value)
{
uint8_t b;
if (input.Read(b) != Success) {
return Result::ERROR_INVALID_DER_TIME;
}
if (b < '0' || b > '9') {
return Result::ERROR_INVALID_DER_TIME;
}
value = static_cast<unsigned int>(b - static_cast<uint8_t>('0'));
return Success;
}
static inline Result
ReadTwoDigits(Reader& input, unsigned int minValue, unsigned int maxValue,
/*out*/ unsigned int& value)
{
unsigned int hi;
Result rv = ReadDigit(input, hi);
if (rv != Success) {
return rv;
}
unsigned int lo;
rv = ReadDigit(input, lo);
if (rv != Success) {
return rv;
}
value = (hi * 10) + lo;
if (value < minValue || value > maxValue) {
return Result::ERROR_INVALID_DER_TIME;
}
return Success;
}
namespace internal {
// We parse GeneralizedTime and UTCTime according to RFC 5280 and we do not
// accept all time formats allowed in the ASN.1 spec. That is,
// GeneralizedTime must always be in the format YYYYMMDDHHMMSSZ and UTCTime
// must always be in the format YYMMDDHHMMSSZ. Timezone formats of the form
// +HH:MM or -HH:MM or NOT accepted.
Result
TimeChoice(Reader& tagged, uint8_t expectedTag, /*out*/ Time& time)
{
unsigned int days;
Reader input;
Result rv = ExpectTagAndGetValue(tagged, expectedTag, input);
if (rv != Success) {
return rv;
}
unsigned int yearHi;
unsigned int yearLo;
if (expectedTag == GENERALIZED_TIME) {
rv = ReadTwoDigits(input, 0, 99, yearHi);
if (rv != Success) {
return rv;
}
rv = ReadTwoDigits(input, 0, 99, yearLo);
if (rv != Success) {
return rv;
}
} else if (expectedTag == UTCTime) {
rv = ReadTwoDigits(input, 0, 99, yearLo);
if (rv != Success) {
return rv;
}
yearHi = yearLo >= 50u ? 19u : 20u;
} else {
return NotReached("invalid tag given to TimeChoice",
Result::ERROR_INVALID_DER_TIME);
}
unsigned int year = (yearHi * 100u) + yearLo;
if (year < 1970u) {
// We don't support dates before January 1, 1970 because that is the epoch.
return Result::ERROR_INVALID_DER_TIME;
}
days = DaysBeforeYear(year);
unsigned int month;
rv = ReadTwoDigits(input, 1u, 12u, month);
if (rv != Success) {
return rv;
}
unsigned int daysInMonth;
static const unsigned int jan = 31u;
const unsigned int feb = ((year % 4u == 0u) &&
((year % 100u != 0u) || (year % 400u == 0u)))
? 29u
: 28u;
static const unsigned int mar = 31u;
static const unsigned int apr = 30u;
static const unsigned int may = 31u;
static const unsigned int jun = 30u;
static const unsigned int jul = 31u;
static const unsigned int aug = 31u;
static const unsigned int sep = 30u;
static const unsigned int oct = 31u;
static const unsigned int nov = 30u;
static const unsigned int dec = 31u;
switch (month) {
case 1: daysInMonth = jan; break;
case 2: daysInMonth = feb; days += jan; break;
case 3: daysInMonth = mar; days += jan + feb; break;
case 4: daysInMonth = apr; days += jan + feb + mar; break;
case 5: daysInMonth = may; days += jan + feb + mar + apr; break;
case 6: daysInMonth = jun; days += jan + feb + mar + apr + may; break;
case 7: daysInMonth = jul; days += jan + feb + mar + apr + may + jun;
break;
case 8: daysInMonth = aug; days += jan + feb + mar + apr + may + jun +
jul;
break;
case 9: daysInMonth = sep; days += jan + feb + mar + apr + may + jun +
jul + aug;
break;
case 10: daysInMonth = oct; days += jan + feb + mar + apr + may + jun +
jul + aug + sep;
break;
case 11: daysInMonth = nov; days += jan + feb + mar + apr + may + jun +
jul + aug + sep + oct;
break;
case 12: daysInMonth = dec; days += jan + feb + mar + apr + may + jun +
jul + aug + sep + oct + nov;
break;
default:
return NotReached("month already bounds-checked by ReadTwoDigits",
Result::FATAL_ERROR_INVALID_STATE);
}
unsigned int dayOfMonth;
rv = ReadTwoDigits(input, 1u, daysInMonth, dayOfMonth);
if (rv != Success) {
return rv;
}
days += dayOfMonth - 1;
unsigned int hours;
rv = ReadTwoDigits(input, 0u, 23u, hours);
if (rv != Success) {
return rv;
}
unsigned int minutes;
rv = ReadTwoDigits(input, 0u, 59u, minutes);
if (rv != Success) {
return rv;
}
unsigned int seconds;
rv = ReadTwoDigits(input, 0u, 59u, seconds);
if (rv != Success) {
return rv;
}
uint8_t b;
if (input.Read(b) != Success) {
return Result::ERROR_INVALID_DER_TIME;
}
if (b != 'Z') {
return Result::ERROR_INVALID_DER_TIME;
}
if (End(input) != Success) {
return Result::ERROR_INVALID_DER_TIME;
}
uint64_t totalSeconds = (static_cast<uint64_t>(days) * 24u * 60u * 60u) +
(static_cast<uint64_t>(hours) * 60u * 60u) +
(static_cast<uint64_t>(minutes) * 60u) +
seconds;
time = TimeFromElapsedSecondsAD(totalSeconds);
return Success;
}
Result
IntegralBytes(Reader& input, uint8_t tag,
IntegralValueRestriction valueRestriction,
/*out*/ Input& value,
/*optional out*/ Input::size_type* significantBytes)
{
Result rv = ExpectTagAndGetValue(input, tag, value);
if (rv != Success) {
return rv;
}
Reader reader(value);
// There must be at least one byte in the value. (Zero is encoded with a
// single 0x00 value byte.)
uint8_t firstByte;
rv = reader.Read(firstByte);
if (rv != Success) {
if (rv == Result::ERROR_BAD_DER) {
return Result::ERROR_INVALID_INTEGER_ENCODING;
}
return rv;
}
// If there is a byte after an initial 0x00/0xFF, then the initial byte
// indicates a positive/negative integer value with its high bit set/unset.
bool prefixed = !reader.AtEnd() && (firstByte == 0 || firstByte == 0xff);
if (prefixed) {
uint8_t nextByte;
if (reader.Read(nextByte) != Success) {
return NotReached("Read of one byte failed but not at end.",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
if ((firstByte & 0x80) == (nextByte & 0x80)) {
return Result::ERROR_INVALID_INTEGER_ENCODING;
}
}
switch (valueRestriction) {
case IntegralValueRestriction::MustBe0To127:
if (value.GetLength() != 1 || (firstByte & 0x80) != 0) {
return Result::ERROR_INVALID_INTEGER_ENCODING;
}
break;
case IntegralValueRestriction::MustBePositive:
if ((value.GetLength() == 1 && firstByte == 0) ||
(firstByte & 0x80) != 0) {
return Result::ERROR_INVALID_INTEGER_ENCODING;
}
break;
case IntegralValueRestriction::NoRestriction:
break;
}
if (significantBytes) {
*significantBytes = value.GetLength();
if (prefixed) {
assert(*significantBytes > 1);
--*significantBytes;
}
assert(*significantBytes > 0);
}
return Success;
}
// This parser will only parse values between 0..127. If this range is
// increased then callers will need to be changed.
Result
IntegralValue(Reader& input, uint8_t tag, /*out*/ uint8_t& value)
{
// Conveniently, all the Integers that we actually have to be able to parse
// are positive and very small. Consequently, this parser is *much* simpler
// than a general Integer parser would need to be.
Input valueBytes;
Result rv = IntegralBytes(input, tag, IntegralValueRestriction::MustBe0To127,
valueBytes, nullptr);
if (rv != Success) {
return rv;
}
Reader valueReader(valueBytes);
rv = valueReader.Read(value);
if (rv != Success) {
return NotReached("IntegralBytes already validated the value.", rv);
}
rv = End(valueReader);
assert(rv == Success); // guaranteed by IntegralBytes's range checks.
return rv;
}
} // namespace internal
Result
OptionalVersion(Reader& input, /*out*/ Version& version)
{
static const uint8_t TAG = CONTEXT_SPECIFIC | CONSTRUCTED | 0;
if (!input.Peek(TAG)) {
version = Version::v1;
return Success;
}
return Nested(input, TAG, [&version](Reader& value) -> Result {
uint8_t integerValue;
Result rv = Integer(value, integerValue);
if (rv != Success) {
return rv;
}
// XXX(bug 1031093): We shouldn't accept an explicit encoding of v1,
// but we do here for compatibility reasons.
switch (integerValue) {
case static_cast<uint8_t>(Version::v3): version = Version::v3; break;
case static_cast<uint8_t>(Version::v2): version = Version::v2; break;
case static_cast<uint8_t>(Version::v1): version = Version::v1; break;
case static_cast<uint8_t>(Version::v4): version = Version::v4; break;
default:
return Result::ERROR_BAD_DER;
}
return Success;
});
}
} } } // namespace mozilla::pkix::der

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,236 @@
/*- *- 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 "mozpkix/pkixnss.h"
#include <limits>
#include "cryptohi.h"
#include "keyhi.h"
#include "pk11pub.h"
#include "mozpkix/nss_scoped_ptrs.h"
#include "mozpkix/pkix.h"
#include "mozpkix/pkixutil.h"
#include "secerr.h"
#include "sslerr.h"
namespace mozilla { namespace pkix {
namespace {
Result
VerifySignedDigest(const SignedDigest& sd,
Input subjectPublicKeyInfo,
SECOidTag pubKeyAlg,
void* pkcs11PinArg)
{
SECOidTag digestAlg;
switch (sd.digestAlgorithm) {
case DigestAlgorithm::sha512: digestAlg = SEC_OID_SHA512; break;
case DigestAlgorithm::sha384: digestAlg = SEC_OID_SHA384; break;
case DigestAlgorithm::sha256: digestAlg = SEC_OID_SHA256; break;
case DigestAlgorithm::sha1: digestAlg = SEC_OID_SHA1; break;
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
SECItem subjectPublicKeyInfoSECItem =
UnsafeMapInputToSECItem(subjectPublicKeyInfo);
ScopedCERTSubjectPublicKeyInfo
spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem));
if (!spki) {
return MapPRErrorCodeToResult(PR_GetError());
}
ScopedSECKEYPublicKey
pubKey(SECKEY_ExtractPublicKey(spki.get()));
if (!pubKey) {
return MapPRErrorCodeToResult(PR_GetError());
}
SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest));
SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature));
SECStatus srv = VFY_VerifyDigestDirect(&digestSECItem, pubKey.get(),
&signatureSECItem, pubKeyAlg,
digestAlg, pkcs11PinArg);
if (srv != SECSuccess) {
return MapPRErrorCodeToResult(PR_GetError());
}
return Success;
}
} // namespace
Result
VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd,
Input subjectPublicKeyInfo,
void* pkcs11PinArg)
{
return VerifySignedDigest(sd, subjectPublicKeyInfo,
SEC_OID_PKCS1_RSA_ENCRYPTION, pkcs11PinArg);
}
Result
VerifyECDSASignedDigestNSS(const SignedDigest& sd,
Input subjectPublicKeyInfo,
void* pkcs11PinArg)
{
return VerifySignedDigest(sd, subjectPublicKeyInfo,
SEC_OID_ANSIX962_EC_PUBLIC_KEY, pkcs11PinArg);
}
Result
DigestBufNSS(Input item,
DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf,
size_t digestBufLen)
{
SECOidTag oid;
size_t bits;
switch (digestAlg) {
case DigestAlgorithm::sha512: oid = SEC_OID_SHA512; bits = 512; break;
case DigestAlgorithm::sha384: oid = SEC_OID_SHA384; bits = 384; break;
case DigestAlgorithm::sha256: oid = SEC_OID_SHA256; bits = 256; break;
case DigestAlgorithm::sha1: oid = SEC_OID_SHA1; bits = 160; break;
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
if (digestBufLen != bits / 8) {
return Result::FATAL_ERROR_INVALID_ARGS;
}
SECItem itemSECItem = UnsafeMapInputToSECItem(item);
if (itemSECItem.len >
static_cast<decltype(itemSECItem.len)>(
std::numeric_limits<int32_t>::max())) {
PR_NOT_REACHED("large items should not be possible here");
return Result::FATAL_ERROR_INVALID_ARGS;
}
SECStatus srv = PK11_HashBuf(oid, digestBuf, itemSECItem.data,
static_cast<int32_t>(itemSECItem.len));
if (srv != SECSuccess) {
return MapPRErrorCodeToResult(PR_GetError());
}
return Success;
}
Result
MapPRErrorCodeToResult(PRErrorCode error)
{
switch (error)
{
#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \
case nss_result: return Result::mozilla_pkix_result;
MOZILLA_PKIX_MAP_LIST
#undef MOZILLA_PKIX_MAP
default:
return Result::ERROR_UNKNOWN_ERROR;
}
}
PRErrorCode
MapResultToPRErrorCode(Result result)
{
switch (result)
{
#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \
case Result::mozilla_pkix_result: return nss_result;
MOZILLA_PKIX_MAP_LIST
#undef MOZILLA_PKIX_MAP
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
}
void
RegisterErrorTable()
{
// Note that these error strings are not localizable.
// When these strings change, update the localization information too.
static const PRErrorMessage ErrorTableText[] = {
{ "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE",
"The server uses key pinning (HPKP) but no trusted certificate chain "
"could be constructed that matches the pinset. Key pinning violations "
"cannot be overridden." },
{ "MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY",
"The server uses a certificate with a basic constraints extension "
"identifying it as a certificate authority. For a properly-issued "
"certificate, this should not be the case." },
{ "MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE",
"The server presented a certificate with a key size that is too small "
"to establish a secure connection." },
{ "MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA",
"An X.509 version 1 certificate that is not a trust anchor was used to "
"issue the server's certificate. X.509 version 1 certificates are "
"deprecated and should not be used to sign other certificates." },
{ "MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH",
"The certificate is not valid for the given email address." },
{ "MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE",
"The server presented a certificate that is not yet valid." },
{ "MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE",
"A certificate that is not yet valid was used to issue the server's "
"certificate." },
{ "MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH",
"The signature algorithm in the signature field of the certificate does "
"not match the algorithm in its signatureAlgorithm field." },
{ "MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING",
"The OCSP response does not include a status for the certificate being "
"verified." },
{ "MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG",
"The server presented a certificate that is valid for too long." },
{ "MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING",
"A required TLS feature is missing." },
{ "MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING",
"The server presented a certificate that contains an invalid encoding of "
"an integer. Common causes include negative serial numbers, negative RSA "
"moduli, and encodings that are longer than necessary." },
{ "MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME",
"The server presented a certificate with an empty issuer distinguished "
"name." },
{ "MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED",
"An additional policy constraint failed when validating this "
"certificate." },
{ "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT",
"The certificate is not trusted because it is self-signed." },
{ "MOZILLA_PKIX_ERROR_MITM_DETECTED",
"Your connection is being intercepted by a TLS proxy. Uninstall it if "
"possible or configure your device to trust its root certificate." },
};
// Note that these error strings are not localizable.
// When these strings change, update the localization information too.
static const PRErrorTable ErrorTable = {
ErrorTableText,
"pkixerrors",
ERROR_BASE,
PR_ARRAY_SIZE(ErrorTableText)
};
(void) PR_ErrorInstallTable(&ErrorTable);
}
} } // namespace mozilla::pkix

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,46 @@
/*- *- 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 "mozpkix/Result.h"
#include "mozpkix/pkixutil.h"
namespace mozilla { namespace pkix {
const char*
MapResultToName(Result result)
{
switch (result)
{
#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \
case Result::mozilla_pkix_result: return "Result::" #mozilla_pkix_result;
MOZILLA_PKIX_MAP_LIST
#undef MOZILLA_PKIX_MAP
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
}
} } // namespace mozilla::pkix

Просмотреть файл

@ -0,0 +1,78 @@
/* -*- 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 2014 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 "mozpkix/Time.h"
#include "mozpkix/pkixutil.h"
#ifdef _WINDOWS
#ifdef _MSC_VER
#pragma warning(push, 3)
#endif
#include "windows.h"
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#else
#include "sys/time.h"
#endif
namespace mozilla { namespace pkix {
Time
Now()
{
uint64_t seconds;
#ifdef _WINDOWS
// "Contains a 64-bit value representing the number of 100-nanosecond
// intervals since January 1, 1601 (UTC)."
// - http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
uint64_t ft64 = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) |
ft.dwLowDateTime;
seconds = (DaysBeforeYear(1601) * Time::ONE_DAY_IN_SECONDS) +
ft64 / (1000u * 1000u * 1000u / 100u);
#else
// "The gettimeofday() function shall obtain the current time, expressed as
// seconds and microseconds since the Epoch."
// - http://pubs.opengroup.org/onlinepubs/009695399/functions/gettimeofday.html
timeval tv;
(void) gettimeofday(&tv, nullptr);
seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) +
static_cast<uint64_t>(tv.tv_sec);
#endif
return TimeFromElapsedSecondsAD(seconds);
}
Time
TimeFromEpochInSeconds(uint64_t secondsSinceEpoch)
{
uint64_t seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) +
secondsSinceEpoch;
return TimeFromElapsedSecondsAD(seconds);
}
} } // namespace mozilla::pkix

Просмотреть файл

@ -0,0 +1,106 @@
/* -*- 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 2015 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 "mozpkix/pkixutil.h"
namespace mozilla { namespace pkix {
Result
DigestSignedData(TrustDomain& trustDomain,
const der::SignedDataWithSignature& signedData,
/*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES],
/*out*/ der::PublicKeyAlgorithm& publicKeyAlg,
/*out*/ SignedDigest& signedDigest)
{
Reader signatureAlg(signedData.algorithm);
Result rv = der::SignatureAlgorithmIdentifierValue(
signatureAlg, publicKeyAlg, signedDigest.digestAlgorithm);
if (rv != Success) {
return rv;
}
if (!signatureAlg.AtEnd()) {
return Result::ERROR_BAD_DER;
}
size_t digestLen;
switch (signedDigest.digestAlgorithm) {
case DigestAlgorithm::sha512: digestLen = 512 / 8; break;
case DigestAlgorithm::sha384: digestLen = 384 / 8; break;
case DigestAlgorithm::sha256: digestLen = 256 / 8; break;
case DigestAlgorithm::sha1: digestLen = 160 / 8; break;
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
assert(digestLen <= sizeof(digestBuf));
rv = trustDomain.DigestBuf(signedData.data, signedDigest.digestAlgorithm,
digestBuf, digestLen);
if (rv != Success) {
return rv;
}
rv = signedDigest.digest.Init(digestBuf, digestLen);
if (rv != Success) {
return rv;
}
return signedDigest.signature.Init(signedData.signature);
}
Result
VerifySignedDigest(TrustDomain& trustDomain,
der::PublicKeyAlgorithm publicKeyAlg,
const SignedDigest& signedDigest,
Input signerSubjectPublicKeyInfo)
{
switch (publicKeyAlg) {
case der::PublicKeyAlgorithm::ECDSA:
return trustDomain.VerifyECDSASignedDigest(signedDigest,
signerSubjectPublicKeyInfo);
case der::PublicKeyAlgorithm::RSA_PKCS1:
return trustDomain.VerifyRSAPKCS1SignedDigest(signedDigest,
signerSubjectPublicKeyInfo);
case der::PublicKeyAlgorithm::Uninitialized:
assert(false);
return Result::FATAL_ERROR_LIBRARY_FAILURE;
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
}
Result
VerifySignedData(TrustDomain& trustDomain,
const der::SignedDataWithSignature& signedData,
Input signerSubjectPublicKeyInfo)
{
uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES];
der::PublicKeyAlgorithm publicKeyAlg;
SignedDigest signedDigest;
Result rv = DigestSignedData(trustDomain, signedData, digestBuf,
publicKeyAlg, signedDigest);
if (rv != Success) {
return rv;
}
return VerifySignedDigest(trustDomain, publicKeyAlg, signedDigest,
signerSubjectPublicKeyInfo);
}
} } // namespace mozilla::pkix

Просмотреть файл

@ -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/.
{
'includes': [
'../../coreconf/config.gypi'
],
'targets': [
{
'target_name': 'mozpkix',
'type': 'static_library',
'standalone_static_library': 1,
'sources': [
'lib/pkixbuild.cpp',
'lib/pkixcert.cpp',
'lib/pkixcheck.cpp',
'lib/pkixder.cpp',
'lib/pkixnames.cpp',
'lib/pkixnss.cpp',
'lib/pkixocsp.cpp',
'lib/pkixresult.cpp',
'lib/pkixtime.cpp',
'lib/pkixverify.cpp',
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_mozpkix_exports',
],
'conditions': [
[ 'mozpkix_only==0', {
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports'
],
}],
],
},
{
'target_name': 'mozpkix-testlib',
'type': 'static_library',
'standalone_static_library': 1,
'sources': [
'test-lib/pkixtestalg.cpp',
'test-lib/pkixtestnss.cpp',
'test-lib/pkixtestutil.cpp',
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_mozpkix_exports',
],
'conditions': [
[ 'mozpkix_only==0', {
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports'
],
}],
],
},
],
'variables': {
'module': 'nss',
}
}

Просмотреть файл

@ -0,0 +1,211 @@
/* -*- 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 2015 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 "mozpkix/test/pkixtestutil.h"
#include "mozpkix/pkixder.h"
#include "mozpkix/nss_scoped_ptrs.h"
// python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10040 1.2.840.10040
#define PREFIX_1_2_840_10040 0x2a, 0x86, 0x48, 0xce, 0x38
// python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10045 1.2.840.10045
#define PREFIX_1_2_840_10045 0x2a, 0x86, 0x48, 0xce, 0x3d
// python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_113549 1.2.840.113549
#define PREFIX_1_2_840_113549 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d
namespace mozilla { namespace pkix { namespace test {
namespace {
enum class NULLParam { NO, YES };
template <size_t SIZE>
ByteString
OID(const uint8_t (&rawValue)[SIZE])
{
return TLV(der::OIDTag, ByteString(rawValue, SIZE));
}
template <size_t SIZE>
ByteString
SimpleAlgID(const uint8_t (&rawValue)[SIZE],
NULLParam nullParam = NULLParam::NO)
{
ByteString sequenceValue(OID(rawValue));
if (nullParam == NULLParam::YES) {
sequenceValue.append(TLV(der::NULLTag, ByteString()));
}
return TLV(der::SEQUENCE, sequenceValue);
}
template <size_t SIZE>
ByteString
DERInteger(const uint8_t (&rawValue)[SIZE])
{
ByteString value(rawValue, SIZE);
if (value[0] & 0x80u) {
// Prefix with a leading zero to disambiguate this from a negative value.
value.insert(value.begin(), 0x00);
}
return TLV(der::INTEGER, value);
}
// Generated with "openssl dsaparam -C -noout 2048" and reformatted.
// openssl 1.0 or later must be used so that a 256-bit Q value is
// generated.
static const uint8_t DSS_P_RAW[] =
{
0xB3,0xCD,0x29,0x44,0xF0,0x25,0xA7,0x73,0xFC,0x86,0x70,0xA2,
0x69,0x5A,0x97,0x3F,0xBD,0x1C,0x6F,0xAA,0x4A,0x40,0x42,0x8E,
0xCF,0xAE,0x62,0x12,0xED,0xB4,0xFD,0x05,0xC2,0xAE,0xB1,0x8C,
0xFC,0xBE,0x38,0x90,0xBB,0x7C,0xFF,0x16,0xF4,0xED,0xCE,0x72,
0x12,0x93,0x83,0xF0,0xA4,0xA1,0x71,0xDC,0x4B,0xF0,0x4E,0x3A,
0x2B,0xFA,0x17,0xB7,0xB3,0x2A,0xCC,0x2C,0xD3,0xC8,0x21,0x49,
0x7A,0x83,0x71,0x8B,0x3D,0x62,0x96,0xDC,0xAD,0xA8,0x03,0xBE,
0x1D,0x33,0x11,0xF3,0xEB,0xD8,0x1B,0x8D,0xDB,0x62,0x79,0x83,
0xF8,0x67,0x4E,0x62,0x21,0x2C,0x81,0x59,0xE8,0x73,0xD7,0xAF,
0xB9,0x63,0x60,0xEA,0xAE,0xEC,0x68,0x6A,0xB4,0xB0,0x65,0xBA,
0xA3,0x4C,0x09,0x99,0x29,0x6A,0x2E,0x2B,0xFC,0x6D,0x51,0xCA,
0x30,0xA2,0x2F,0x7A,0x65,0x76,0xA7,0x55,0x13,0x11,0xA0,0x02,
0xA2,0x59,0x4B,0xCE,0xA7,0x05,0xF6,0x07,0x35,0x9B,0x41,0xD7,
0x11,0x5A,0x18,0x57,0xA7,0x78,0x88,0xC3,0xA8,0xE3,0x39,0xF5,
0x47,0x3D,0x2E,0x18,0x54,0xB0,0xF0,0xBF,0x65,0x3F,0x77,0xC7,
0x11,0xB8,0x0D,0x52,0xAD,0xC8,0xE8,0x6D,0xF6,0x7E,0x88,0x65,
0x84,0x2B,0xF7,0xEF,0x8E,0xB5,0x7C,0xBD,0x2E,0x0D,0xF3,0xC6,
0xDD,0x0B,0xB4,0xF2,0x23,0x1F,0xDA,0x55,0x05,0xF5,0xDC,0x53,
0xA6,0x83,0xDA,0x5C,0xEF,0x29,0x02,0x78,0x68,0xD0,0xA4,0x39,
0x09,0x7F,0xFA,0x49,0x18,0xD0,0xB5,0x19,0x35,0x31,0x8E,0xDE,
0x43,0x35,0xA3,0xB9,0x6D,0xC1,0x70,0xC6,0x0D,0x18,0x24,0xEB,
0x1E,0x4D,0x52,0xB7,
};
static const uint8_t DSS_Q_RAW[] =
{
0x8D,0x6B,0x86,0x89,0x9C,0x8D,0x30,0x91,0xCC,0x6E,0x34,0xF1,
0xE8,0x9C,0x8A,0x5C,0xD6,0xAB,0x01,0x1E,0xC4,0xDB,0xFD,0x07,
0xEB,0x5F,0x4E,0xE8,0xFA,0xFC,0x98,0x2D,
};
static const uint8_t DSS_G_RAW[] =
{
0x0E,0x2C,0x34,0xB2,0xE1,0x66,0x49,0xB6,0x9A,0x7D,0x67,0x3E,
0xEE,0x98,0x35,0x18,0x28,0x35,0xFC,0x05,0x36,0x3B,0x94,0xE6,
0x1E,0x1C,0x5B,0x05,0x3E,0x86,0x1B,0xE3,0xED,0xD2,0xE1,0xF3,
0xF7,0xF7,0x60,0x6D,0x7D,0xA1,0xAF,0x9A,0xD1,0xDF,0xA2,0x9C,
0xFC,0xA2,0xEB,0x90,0x8B,0x1C,0x82,0x92,0x45,0x7B,0x30,0x2A,
0xFD,0x7A,0xE6,0x68,0x8F,0xEC,0x89,0x3A,0x9A,0xAD,0xFE,0x25,
0x5E,0x51,0xC5,0x29,0x45,0x7F,0xAC,0xDE,0xFC,0xB4,0x1B,0x3A,
0xDA,0xC7,0x21,0x68,0x87,0x27,0x8D,0x7B,0xB2,0xBB,0x41,0x60,
0x46,0x42,0x5B,0x6B,0xE8,0x80,0xD2,0xE4,0xA3,0x30,0x8F,0xD5,
0x71,0x07,0x8A,0x7B,0x32,0x56,0x84,0x41,0x1C,0xDF,0x69,0xE9,
0xFD,0xBA,0x48,0xE0,0x43,0xA0,0x38,0x92,0x12,0xF3,0x52,0xA5,
0x40,0x87,0xCB,0x34,0xBB,0x3E,0x25,0x29,0x3C,0xC6,0xA5,0x17,
0xFD,0x58,0x47,0x89,0xDB,0x9B,0xB9,0xCF,0xE9,0xA8,0xF2,0xEC,
0x55,0x76,0xF5,0xF1,0x9C,0x6E,0x0A,0x3F,0x16,0x5F,0x49,0x31,
0x31,0x1C,0x43,0xA2,0x83,0xDA,0xDD,0x7F,0x1C,0xEA,0x05,0x36,
0x7B,0xED,0x09,0xFB,0x6F,0x8A,0x2B,0x55,0xB9,0xBC,0x4A,0x8C,
0x28,0xC1,0x4D,0x13,0x6E,0x47,0xF4,0xAD,0x79,0x00,0xE9,0x5A,
0xB6,0xC7,0x73,0x28,0xA9,0x89,0xAD,0xE8,0x6E,0xC6,0x54,0xA5,
0x56,0x2D,0xAA,0x81,0x83,0x9E,0xC1,0x13,0x79,0xA4,0x12,0xE0,
0x76,0x1F,0x25,0x43,0xB6,0xDE,0x56,0xF7,0x52,0xCC,0x07,0xB8,
0x37,0xE2,0x8C,0xC5,0x56,0x8C,0xDD,0x63,0xF5,0xB6,0xA3,0x46,
0x62,0xF6,0x35,0x76,
};
} // namespace
TestSignatureAlgorithm::TestSignatureAlgorithm(
const TestPublicKeyAlgorithm& aPublicKeyAlg,
TestDigestAlgorithmID aDigestAlg,
const ByteString& aAlgorithmIdentifier,
bool aAccepted)
: publicKeyAlg(aPublicKeyAlg)
, digestAlg(aDigestAlg)
, algorithmIdentifier(aAlgorithmIdentifier)
, accepted(aAccepted)
{
}
ByteString DSS_P() { return ByteString(DSS_P_RAW, sizeof(DSS_P_RAW)); }
ByteString DSS_Q() { return ByteString(DSS_Q_RAW, sizeof(DSS_Q_RAW)); }
ByteString DSS_G() { return ByteString(DSS_G_RAW, sizeof(DSS_G_RAW)); }
TestPublicKeyAlgorithm
DSS()
{
static const uint8_t oidValue[] = { PREFIX_1_2_840_10040, 4, 1 };
// RFC 3279 Section-2.3.2
return TestPublicKeyAlgorithm(
TLV(der::SEQUENCE,
OID(oidValue) +
TLV(der::SEQUENCE,
DERInteger(DSS_P_RAW) +
DERInteger(DSS_Q_RAW) +
DERInteger(DSS_G_RAW))));
}
// RFC 3279 Section 2.3.1
TestPublicKeyAlgorithm
RSA_PKCS1()
{
static const uint8_t rsaEncryption[] = { PREFIX_1_2_840_113549, 1, 1, 1 };
return TestPublicKeyAlgorithm(SimpleAlgID(rsaEncryption, NULLParam::YES));
}
// RFC 3279 Section 2.2.1
TestSignatureAlgorithm md2WithRSAEncryption()
{
static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 2 };
return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::MD2,
SimpleAlgID(oidValue), false);
}
// RFC 3279 Section 2.2.1
TestSignatureAlgorithm md5WithRSAEncryption()
{
static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 4 };
return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::MD5,
SimpleAlgID(oidValue), false);
}
// RFC 3279 Section 2.2.1
TestSignatureAlgorithm sha1WithRSAEncryption()
{
static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 5 };
return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::SHA1,
SimpleAlgID(oidValue), true);
}
// RFC 4055 Section 5
TestSignatureAlgorithm sha256WithRSAEncryption()
{
static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 11 };
return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::SHA256,
SimpleAlgID(oidValue), true);
}
} } } // namespace mozilla::pkix

Просмотреть файл

@ -0,0 +1,364 @@
/* -*- 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 "mozpkix/test/pkixtestutil.h"
#include "mozpkix/test/pkixtestnss.h"
#include <limits>
#include "cryptohi.h"
#include "keyhi.h"
#include "nss.h"
#include "pk11pqg.h"
#include "pk11pub.h"
#include "mozpkix/nss_scoped_ptrs.h"
#include "mozpkix/pkixnss.h"
#include "mozpkix/pkixder.h"
#include "mozpkix/pkixutil.h"
#include "prinit.h"
#include "secerr.h"
#include "secitem.h"
namespace mozilla { namespace pkix { namespace test {
namespace {
TestKeyPair* GenerateKeyPairInner();
void
InitNSSIfNeeded()
{
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
abort();
}
}
static ScopedTestKeyPair reusedKeyPair;
PRStatus
InitReusedKeyPair()
{
InitNSSIfNeeded();
reusedKeyPair.reset(GenerateKeyPairInner());
return reusedKeyPair ? PR_SUCCESS : PR_FAILURE;
}
class NSSTestKeyPair final : public TestKeyPair
{
public:
NSSTestKeyPair(const TestPublicKeyAlgorithm& aPublicKeyAlg,
const ByteString& spk,
const ByteString& aEncryptedPrivateKey,
const ByteString& aEncryptionAlgorithm,
const ByteString& aEncryptionParams)
: TestKeyPair(aPublicKeyAlg, spk)
, encryptedPrivateKey(aEncryptedPrivateKey)
, encryptionAlgorithm(aEncryptionAlgorithm)
, encryptionParams(aEncryptionParams)
{
}
Result SignData(const ByteString& tbs,
const TestSignatureAlgorithm& signatureAlgorithm,
/*out*/ ByteString& signature) const override
{
SECOidTag oidTag;
if (signatureAlgorithm.publicKeyAlg == RSA_PKCS1()) {
switch (signatureAlgorithm.digestAlg) {
case TestDigestAlgorithmID::MD2:
oidTag = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;
break;
case TestDigestAlgorithmID::MD5:
oidTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
break;
case TestDigestAlgorithmID::SHA1:
oidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
break;
case TestDigestAlgorithmID::SHA224:
oidTag = SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION;
break;
case TestDigestAlgorithmID::SHA256:
oidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
break;
case TestDigestAlgorithmID::SHA384:
oidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION;
break;
case TestDigestAlgorithmID::SHA512:
oidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
break;
MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
}
} else {
abort();
}
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
return MapPRErrorCodeToResult(PR_GetError());
}
SECItem encryptedPrivateKeyInfoItem = {
siBuffer,
const_cast<uint8_t*>(encryptedPrivateKey.data()),
static_cast<unsigned int>(encryptedPrivateKey.length())
};
SECItem encryptionAlgorithmItem = {
siBuffer,
const_cast<uint8_t*>(encryptionAlgorithm.data()),
static_cast<unsigned int>(encryptionAlgorithm.length())
};
SECItem encryptionParamsItem = {
siBuffer,
const_cast<uint8_t*>(encryptionParams.data()),
static_cast<unsigned int>(encryptionParams.length())
};
SECKEYEncryptedPrivateKeyInfo encryptedPrivateKeyInfo = {
nullptr,
{ encryptionAlgorithmItem, encryptionParamsItem },
encryptedPrivateKeyInfoItem
};
SECItem passwordItem = { siBuffer, nullptr, 0 };
SECItem publicValueItem = {
siBuffer,
const_cast<uint8_t*>(subjectPublicKey.data()),
static_cast<unsigned int>(subjectPublicKey.length())
};
SECKEYPrivateKey* privateKey;
// This should always be an RSA key (we'll have aborted above if we're not
// doing an RSA signature).
if (PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
slot.get(), &encryptedPrivateKeyInfo, &passwordItem, nullptr,
&publicValueItem, false, false, rsaKey, KU_ALL, &privateKey,
nullptr) != SECSuccess) {
return MapPRErrorCodeToResult(PR_GetError());
}
ScopedSECKEYPrivateKey scopedPrivateKey(privateKey);
SECItem signatureItem;
if (SEC_SignData(&signatureItem, tbs.data(),
static_cast<int>(tbs.length()),
scopedPrivateKey.get(), oidTag) != SECSuccess) {
return MapPRErrorCodeToResult(PR_GetError());
}
signature.assign(signatureItem.data, signatureItem.len);
SECITEM_FreeItem(&signatureItem, false);
return Success;
}
TestKeyPair* Clone() const override
{
return new (std::nothrow) NSSTestKeyPair(publicKeyAlg,
subjectPublicKey,
encryptedPrivateKey,
encryptionAlgorithm,
encryptionParams);
}
private:
const ByteString encryptedPrivateKey;
const ByteString encryptionAlgorithm;
const ByteString encryptionParams;
};
} // namespace
// This private function is also used by Gecko's PSM test framework
// (OCSPCommon.cpp).
TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
const ScopedSECKEYPublicKey& publicKey,
const ScopedSECKEYPrivateKey& privateKey)
{
ScopedCERTSubjectPublicKeyInfo
spki(SECKEY_CreateSubjectPublicKeyInfo(publicKey.get()));
if (!spki) {
return nullptr;
}
SECItem spkDER = spki->subjectPublicKey;
DER_ConvertBitString(&spkDER); // bits to bytes
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
return nullptr;
}
// Because NSSTestKeyPair isn't tracked by XPCOM and won't otherwise be aware
// of shutdown, we don't have a way to release NSS resources at the
// appropriate time. To work around this, NSSTestKeyPair doesn't hold on to
// NSS resources. Instead, we export the generated private key part as an
// encrypted blob (with an empty password and fairly lame encryption). When we
// need to use it (e.g. to sign something), we decrypt it and create a
// temporary key object.
SECItem passwordItem = { siBuffer, nullptr, 0 };
ScopedSECKEYEncryptedPrivateKeyInfo encryptedPrivateKey(
PK11_ExportEncryptedPrivKeyInfo(
slot.get(), SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC,
&passwordItem, privateKey.get(), 1, nullptr));
if (!encryptedPrivateKey) {
return nullptr;
}
return new (std::nothrow) NSSTestKeyPair(
publicKeyAlg,
ByteString(spkDER.data, spkDER.len),
ByteString(encryptedPrivateKey->encryptedData.data,
encryptedPrivateKey->encryptedData.len),
ByteString(encryptedPrivateKey->algorithm.algorithm.data,
encryptedPrivateKey->algorithm.algorithm.len),
ByteString(encryptedPrivateKey->algorithm.parameters.data,
encryptedPrivateKey->algorithm.parameters.len));
}
namespace {
TestKeyPair*
GenerateKeyPairInner()
{
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
abort();
}
// Bug 1012786: PK11_GenerateKeyPair can fail if there is insufficient
// entropy to generate a random key. Attempting to add some entropy and
// retrying appears to solve this issue.
for (uint32_t retries = 0; retries < 10; retries++) {
PK11RSAGenParams params;
params.keySizeInBits = 2048;
params.pe = 3;
SECKEYPublicKey* publicKeyTemp = nullptr;
ScopedSECKEYPrivateKey
privateKey(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
&params, &publicKeyTemp, false, true,
nullptr));
ScopedSECKEYPublicKey publicKey(publicKeyTemp);
if (privateKey) {
return CreateTestKeyPair(RSA_PKCS1(), publicKey, privateKey);
}
assert(!publicKeyTemp);
if (PR_GetError() != SEC_ERROR_PKCS11_FUNCTION_FAILED) {
break;
}
// Since these keys are only for testing, we don't need them to be good,
// random keys.
// https://xkcd.com/221/
static const uint8_t RANDOM_NUMBER[] = { 4, 4, 4, 4, 4, 4, 4, 4 };
if (PK11_RandomUpdate((void*) &RANDOM_NUMBER,
sizeof(RANDOM_NUMBER)) != SECSuccess) {
break;
}
}
abort();
}
} // namespace
TestKeyPair*
GenerateKeyPair()
{
InitNSSIfNeeded();
return GenerateKeyPairInner();
}
TestKeyPair*
CloneReusedKeyPair()
{
static PRCallOnceType initCallOnce;
if (PR_CallOnce(&initCallOnce, InitReusedKeyPair) != PR_SUCCESS) {
abort();
}
assert(reusedKeyPair);
return reusedKeyPair->Clone();
}
TestKeyPair*
GenerateDSSKeyPair()
{
InitNSSIfNeeded();
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
return nullptr;
}
ByteString p(DSS_P());
ByteString q(DSS_Q());
ByteString g(DSS_G());
static const PQGParams PARAMS = {
nullptr,
{ siBuffer,
const_cast<uint8_t*>(p.data()),
static_cast<unsigned int>(p.length())
},
{ siBuffer,
const_cast<uint8_t*>(q.data()),
static_cast<unsigned int>(q.length())
},
{ siBuffer,
const_cast<uint8_t*>(g.data()),
static_cast<unsigned int>(g.length())
}
};
SECKEYPublicKey* publicKeyTemp = nullptr;
ScopedSECKEYPrivateKey
privateKey(PK11_GenerateKeyPair(slot.get(), CKM_DSA_KEY_PAIR_GEN,
const_cast<PQGParams*>(&PARAMS),
&publicKeyTemp, false, true, nullptr));
if (!privateKey) {
return nullptr;
}
ScopedSECKEYPublicKey publicKey(publicKeyTemp);
return CreateTestKeyPair(DSS(), publicKey, privateKey);
}
Result
TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo)
{
InitNSSIfNeeded();
return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
nullptr);
}
Result
TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo)
{
InitNSSIfNeeded();
return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
nullptr);
}
Result
TestDigestBuf(Input item,
DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf,
size_t digestBufLen)
{
InitNSSIfNeeded();
return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
}
} } } // namespace mozilla::pkix::test

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,216 @@
# 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.
from __future__ import print_function
import argparse
import itertools
import sys
def base128(value):
"""
Given an integral value, returns an array of the base-128 representation
of that value, with all but the last byte having the high bit set as
required by the DER rules for the nodes of an OID after the first two
bytes.
>>> base128(1)
[1]
>>> base128(10045)
[206, 61]
"""
if value < 0:
raise ValueError("An OID must have only positive-value nodes.")
# least significant byte has highest bit unset
result = [value % 0x80]
value /= 0x80
while value != 0:
result = [0x80 | (value % 0x80)] + result
value /= 0x80
return result
def dottedOIDToEncodedArray(dottedOID):
"""
Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and
returns an array that contains the DER encoding of its value, without
the tag and length (e.g. [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04]).
"""
nodes = [int(x) for x in dottedOID.strip().split(".")]
if len(nodes) < 2:
raise ValueError("An OID must have at least two nodes.")
if not (0 <= nodes[0] <= 2):
raise ValueError("The first node of an OID must be 0, 1, or 2.")
if not (0 <= nodes[1] <= 39):
# XXX: Does this restriction apply when the first part is 2?
raise ValueError("The second node of an OID must be 0-39.")
firstByte = (40 * nodes[0]) + nodes[1]
restBase128 = [base128(x) for x in nodes[2:]]
return [firstByte] + list(itertools.chain.from_iterable(restBase128))
def dottedOIDToCArray(dottedOID, mode):
"""
Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and
returns a string that contains the hex encoding of the OID in C++ literal
notation, e.g. '0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04'.
"""
bytes = dottedOIDToEncodedArray(dottedOID)
if mode != "value" and mode != "prefixdefine":
bytes = [0x06, len(bytes)] + bytes
if mode == "alg":
# Wrap the DER-encoded OID in a SEQUENCE to create an
# AlgorithmIdentifier with no parameters.
bytes = [0x30, len(bytes)] + bytes
return ", ".join(["0x%.2x" % b for b in bytes])
def specNameToCName(specName):
"""
Given an string containing an ASN.1 name, returns a string that is a valid
C++ identifier that is as similar to that name as possible. Since most
ASN.1 identifiers used in PKIX specifications are legal C++ names except
for containing hyphens, this function just converts the hyphens to
underscores. This may need to be improved in the future if we encounter
names with other funny characters.
"""
return specName.replace("-", "_")
def toCode(programName, specName, dottedOID, mode):
"""
Given an ASN.1 name and a string containing the dotted representation of an
OID, returns a string that contains a C++ declaration for a named constant
that contains that OID value. If mode is "value" then only the value of
the OID (without the tag or length) will be included in the output. If mode
is "tlv" then the value will be prefixed with the tag and length. If mode
is "alg" then the value will be a complete der-encoded AlgorithmIdentifier
with no parameters.
This:
toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4",
"value")
would result in a string like:
// python DottedOIDToCode.py ecdsa-with-SHA512 1.2.840.10045.4.3.4
static const uint8_t ecdsa_with_SHA512[] = {
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04
};
This:
toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4",
"tlv")
would result in a string like:
// python DottedOIDToCode.py --tlv ecdsa-with-SHA512 1.2.840.10045.4.3.4
static const uint8_t tlv_ecdsa_with_SHA512[] = {
0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04
};
This:
toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4",
"alg")
would result in a string like:
// python DottedOIDToCode.py --alg ecdsa-with-SHA512 1.2.840.10045.4.3.4
static const uint8_t alg_ecdsa_with_SHA512[] = {
0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04
};
This:
toCode("DottedOIDToCode.py", "PREFIX_1_2_840_10045", "1.2.840.10045",
"prefixdefine")
would result in a string like this (note the lack of indention):
// python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10045 1.2.840.10045
#define PREFIX_1_2_840_10045 0x2a, 0x86, 0x48, 0xce, 0x3d
"""
programNameWithOptions = programName
if mode == "prefixdefine":
programNameWithOptions += " --prefixdefine"
varName = specName
return ("// python %s %s %s\n" +
"#define %s %s\n") % (programNameWithOptions, specName,
dottedOID, varName,
dottedOIDToCArray(dottedOID, mode))
varName = specNameToCName(specName)
if mode == "tlv":
programNameWithOptions += " --tlv"
varName = "tlv_" + varName
elif mode == "alg":
programNameWithOptions += " --alg"
varName = "alg_" + varName
elif mode == "prefixdefine":
programNameWithOptions += " --alg"
varName = varName
return (" // python %s %s %s\n" +
" static const uint8_t %s[] = {\n" +
" %s\n" +
" };\n") % (programNameWithOptions, specName, dottedOID, varName,
dottedOIDToCArray(dottedOID, mode))
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generate code snippets to handle OIDs in C++",
epilog="example: python %s ecdsa-with-SHA1 1.2.840.10045.4.1"
% sys.argv[0])
group = parser.add_mutually_exclusive_group()
group.add_argument("--tlv", action='store_true',
help="Wrap the encoded OID value with the tag and length")
group.add_argument("--alg", action='store_true',
help="Wrap the encoded OID value in an encoded SignatureAlgorithm")
group.add_argument("--prefixdefine", action='store_true',
help="generate a OID prefix #define")
parser.add_argument("name",
help="The name given to the OID in the specification")
parser.add_argument("dottedOID", metavar="dotted-oid",
help="The OID value, in dotted notation")
args = parser.parse_args()
if args.alg:
mode = 'alg'
elif args.tlv:
mode = 'tlv'
elif args.prefixdefine:
mode = 'prefixdefine'
else:
mode = 'value'
print(toCode(sys.argv[0], args.name, args.dottedOID, mode))

Просмотреть файл

@ -6,7 +6,7 @@
#define util_h__
#include "nspr.h"
#include "scoped_ptrs.h"
#include "nss_scoped_ptrs.h"
#include <secmodt.h>
#include <string>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше