├── tools ├── hg-to-git.sh ├── hg-to-git-README.txt ├── hg-to-git-am.py └── DottedOIDToCode.py ├── moz.build ├── test ├── lib │ ├── moz.build │ ├── pkixtestalg.cpp │ └── pkixtestnss.cpp └── gtest │ ├── pkixgtest.cpp │ ├── moz.build │ ├── README.txt │ ├── pkixcheck_CheckValidity_tests.cpp │ ├── pkixocsp_CreateEncodedOCSPRequest_tests.cpp │ ├── pkixgtest.h │ ├── pkixcert_signature_algorithm_tests.cpp │ ├── pkixcert_extension_tests.cpp │ ├── pkixcheck_CheckKeyUsage_tests.cpp │ └── pkixcheck_CheckSignatureAlgorithm_tests.cpp ├── lib ├── pkixresult.cpp ├── pkixcheck.h ├── ScopedPtr.h ├── pkixtime.cpp ├── pkixverify.cpp ├── pkixnss.cpp ├── pkixutil.h ├── pkixcert.cpp └── pkixbuild.cpp ├── warnings.mozbuild └── include └── pkix ├── pkixnss.h ├── Time.h ├── pkix.h ├── Input.h ├── Result.h └── pkixtypes.h /tools/hg-to-git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # The documentation for this is in hg-to-git-README.txt. 3 | set -euo pipefail 4 | IFS=$'\n\t' 5 | 6 | while read p; do 7 | echo $p 8 | hg --repository $1 export --git -r $p \ 9 | | python tools/hg-to-git-am.py $1 \ 10 | | tee am-log \ 11 | | git am --whitespace=nowarn --committer-date-is-author-date 12 | done 13 | -------------------------------------------------------------------------------- /moz.build: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 | # vim: set filetype=python: 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | SOURCES += [ 8 | 'lib/pkixbuild.cpp', 9 | 'lib/pkixcert.cpp', 10 | 'lib/pkixcheck.cpp', 11 | 'lib/pkixder.cpp', 12 | 'lib/pkixnames.cpp', 13 | 'lib/pkixnss.cpp', 14 | 'lib/pkixocsp.cpp', 15 | 'lib/pkixresult.cpp', 16 | 'lib/pkixtime.cpp', 17 | 'lib/pkixverify.cpp', 18 | ] 19 | 20 | LOCAL_INCLUDES += [ 21 | 'include', 22 | ] 23 | 24 | TEST_DIRS += [ 25 | 'test/gtest', 26 | 'test/lib', 27 | ] 28 | 29 | include('warnings.mozbuild') 30 | 31 | Library('mozillapkix') 32 | 33 | FINAL_LIBRARY = 'xul' 34 | 35 | -------------------------------------------------------------------------------- /test/lib/moz.build: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 | # This code is made available to you under your choice of the following sets 3 | # of licensing terms: 4 | # 5 | # This Source Code Form is subject to the terms of the Mozilla Public 6 | # License, v. 2.0. If a copy of the MPL was not distributed with this 7 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | # 9 | # Copyright 2013 Mozilla Contributors 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | 23 | SOURCES += [ 24 | 'pkixtestalg.cpp', 25 | 'pkixtestnss.cpp', 26 | 'pkixtestutil.cpp', 27 | ] 28 | 29 | Library('pkixtestutil') 30 | 31 | LOCAL_INCLUDES += [ 32 | '../../include', 33 | '../../lib', 34 | ] 35 | 36 | FINAL_LIBRARY = 'xul-gtest' 37 | 38 | FAIL_ON_WARNINGS = True 39 | -------------------------------------------------------------------------------- /lib/pkixresult.cpp: -------------------------------------------------------------------------------- 1 | /*- *- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkix/Result.h" 26 | #include "pkixutil.h" 27 | 28 | namespace mozilla { namespace pkix { 29 | 30 | const char* 31 | MapResultToName(Result result) 32 | { 33 | switch (result) 34 | { 35 | #define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \ 36 | case Result::mozilla_pkix_result: return "Result::" #mozilla_pkix_result; 37 | 38 | MOZILLA_PKIX_MAP_LIST 39 | 40 | #undef MOZILLA_PKIX_MAP 41 | 42 | MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM 43 | } 44 | } 45 | 46 | } } // namespace mozilla::pkix 47 | -------------------------------------------------------------------------------- /warnings.mozbuild: -------------------------------------------------------------------------------- 1 | FAIL_ON_WARNINGS = True 2 | 3 | if CONFIG['CLANG_CXX']: 4 | CXXFLAGS += [ 5 | '-Weverything', 6 | 7 | '-Wno-c++98-compat', 8 | '-Wno-c++98-compat-pedantic', 9 | '-Wno-missing-prototypes', 10 | '-Wno-missing-variable-declarations', 11 | '-Wno-padded', 12 | '-Wno-reserved-id-macro', # NSPR and NSS use reserved IDs in their include guards. 13 | '-Wno-shadow', # XXX: Clang's rules are too strict for constructors. 14 | '-Wno-weak-vtables', # We rely on the linker to merge the duplicate vtables. 15 | ] 16 | elif CONFIG['_MSC_VER']: 17 | CXXFLAGS += [ 18 | '-sdl', # Enable additional security checks based on Microsoft's SDL. 19 | 20 | '-Wall', 21 | 22 | '-wd4514', # 'function': unreferenced inline function has been removed 23 | '-wd4668', # warning C4668: 'X' is not defined as a preprocessor macro, 24 | # replacing with '0' for '#if/#elif'. 25 | '-wd4710', # 'function': function not inlined 26 | '-wd4711', # function 'function' selected for inline expansion 27 | '-wd4800', # forcing value to bool 'true' or 'false' 28 | '-wd4820', # 'bytes' bytes padding added after construct 'member_name' 29 | 30 | # XXX: We cannot use /Za (Disable Microsoft Extensions) because windows.h 31 | # won't copmile with it. 32 | '-Zc:forScope', # Standard C++ rules for variable scope in for loops. 33 | '-Zc:inline', # Standard C++ rules requiring definition inline functions. 34 | '-Zc:rvalueCast', # Standard C++ rules for result of cast being an rvalue. 35 | '-Zc:strictStrings', # Standard C++ rule that string literals are const. 36 | ] 37 | else: 38 | CXXFLAGS += [ 39 | '-Wall', 40 | '-Wextra', 41 | '-pedantic-errors', 42 | ] 43 | -------------------------------------------------------------------------------- /test/gtest/pkixgtest.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixgtest.h" 26 | 27 | #include 28 | 29 | #include "pkix/Time.h" 30 | 31 | namespace mozilla { namespace pkix { namespace test { 32 | 33 | const std::time_t ONE_DAY_IN_SECONDS_AS_TIME_T = 34 | static_cast(Time::ONE_DAY_IN_SECONDS); 35 | 36 | // This assumes that time/time_t are POSIX-compliant in that time() returns 37 | // the number of seconds since the Unix epoch. 38 | const std::time_t now(time(nullptr)); 39 | const std::time_t oneDayBeforeNow(time(nullptr) - 40 | ONE_DAY_IN_SECONDS_AS_TIME_T); 41 | const std::time_t oneDayAfterNow(time(nullptr) + 42 | ONE_DAY_IN_SECONDS_AS_TIME_T); 43 | 44 | } } } // namespace mozilla::pkix::test 45 | -------------------------------------------------------------------------------- /test/gtest/moz.build: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 | # vim: set filetype=python: 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | SOURCES += [ 8 | 'pkixbuild_tests.cpp', 9 | 'pkixcert_extension_tests.cpp', 10 | 'pkixcert_signature_algorithm_tests.cpp', 11 | 'pkixcheck_CheckKeyUsage_tests.cpp', 12 | 'pkixcheck_CheckSignatureAlgorithm_tests.cpp', 13 | 'pkixcheck_CheckValidity_tests.cpp', 14 | 15 | # The naming conventions are described in ./README.txt. 16 | 17 | 'pkixder_input_tests.cpp', 18 | 'pkixder_pki_types_tests.cpp', 19 | 'pkixder_universal_types_tests.cpp', 20 | 'pkixgtest.cpp', 21 | 'pkixnames_tests.cpp', 22 | 'pkixocsp_CreateEncodedOCSPRequest_tests.cpp', 23 | 'pkixocsp_VerifyEncodedOCSPResponse.cpp', 24 | ] 25 | 26 | LOCAL_INCLUDES += [ 27 | '../../include', 28 | '../../lib', 29 | '../lib', 30 | ] 31 | 32 | FINAL_LIBRARY = 'xul-gtest' 33 | 34 | include('../../warnings.mozbuild') 35 | 36 | # These warnings are disabled in order to minimize the amount of boilerplate 37 | # required to implement tests, and/or because they originate in the GTest 38 | # framework in a way we cannot otherwise work around. 39 | if CONFIG['CLANG_CXX']: 40 | CXXFLAGS += [ 41 | '-Wno-exit-time-destructors', 42 | '-Wno-global-constructors', 43 | '-Wno-old-style-cast', 44 | '-Wno-used-but-marked-unused', 45 | ] 46 | elif CONFIG['_MSC_VER']: 47 | CXXFLAGS += [ 48 | '-wd4350', # behavior change: 'std::_Wrap_alloc>::... 49 | '-wd4275', # non dll-interface class used as base for dll-interface class 50 | '-wd4548', # Expression before comma has no effect 51 | '-wd4625', # copy constructor could not be generated. 52 | '-wd4626', # assugment operator could not be generated. 53 | '-wd4640', # construction of local static object is not thread safe. 54 | ] 55 | -------------------------------------------------------------------------------- /lib/pkixcheck.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef mozilla_pkix_pkixcheck_h 26 | #define mozilla_pkix_pkixcheck_h 27 | 28 | #include "pkix/pkixtypes.h" 29 | 30 | namespace mozilla { namespace pkix { 31 | 32 | class BackCert; 33 | 34 | Result CheckIssuerIndependentProperties( 35 | TrustDomain& trustDomain, 36 | const BackCert& cert, 37 | Time time, 38 | KeyUsage requiredKeyUsageIfPresent, 39 | KeyPurposeId requiredEKUIfPresent, 40 | const CertPolicyId& requiredPolicy, 41 | unsigned int subCACount, 42 | /*out*/ TrustLevel& trustLevel); 43 | 44 | Result CheckNameConstraints(Input encodedNameConstraints, 45 | const BackCert& firstChild, 46 | KeyPurposeId requiredEKUIfPresent); 47 | 48 | Result CheckValidity(Input encodedValidity, Time time, 49 | /*optional out*/ Time* notBeforeOut = nullptr, 50 | /*optional out*/ Time* notAfterOut = nullptr); 51 | 52 | } } // namespace mozilla::pkix 53 | 54 | #endif // mozilla_pkix_pkixcheck_h 55 | -------------------------------------------------------------------------------- /tools/hg-to-git-README.txt: -------------------------------------------------------------------------------- 1 | The hg-to-git tool converts commits to Mozilla's mozilla-central Mercurial 2 | repository to commits to a standalone mozilla::pkix git repository. This tool 3 | tries to ensure that the git commits are as similar to the original commits to 4 | mozilla-central as possible, except: 5 | 6 | * In mozill-central, mozilla::pkix is in the subdirectory security/pkix 7 | (was security/insanity); in the git repository, we strip off that 8 | path prefix. 9 | * Any changes to files outside of security/pkix (and security/insanity) 10 | are discarded, because they are not part of mozilla::pkix. 11 | * A link to the mozilla-central changeset on https://hg.mozilla.org 12 | and links to the bugs on https://bugzilla.mozilla.org are appended to 13 | commit message during the conversion. (Such links are not needed in 14 | mozilla-central commits because Mozilla's web tools synthesize these 15 | links automatically similar to the way that hg-to-git.py does.) 16 | * The committer and commiter date represent who imported the Mercurial 17 | commits into the git repository and when, not who committed the changes 18 | to mozilla-central and when. 19 | 20 | usage: 21 | 22 | tools/hg-to-git.sh < > 23 | 24 | e.g.: 25 | 26 | # track the changes to mozilla-central on a branch other than master 27 | git checkout -b mozilla-central 28 | 29 | # import the revisions 30 | tools/hg-to-git.sh ../firefox/mozilla-central < ../firefox/revisions.txt > log 31 | 32 | Where revisions.txt contains a list of revision numbers containing changes 33 | to security/pkix (and/or security/insanity, for old revisions): 34 | 35 | (cd ../firefox/mozilla-central ; \ 36 | hg log -M -removed -r "ancestors(.)" --template "{node}\n" \ 37 | security/pkix security/insanity \ 38 | | grep -v a555f10c40e553030345ced1bab3088533c5119b \ 39 | > ../revisions.txt) 40 | 41 | The -M argument filters out merge commits. Commit 42 | a555f10c40e553030345ced1bab3088533c5119b is skipped because its metadata 43 | indicates that it updated some files in mozilla::pkix, but it actually didn't; 44 | see https://bugzilla.mozilla.org/show_bug.cgi?id=1037220#c13. 45 | 46 | You need to omit any revisions that have already been imported. 47 | 48 | -------------------------------------------------------------------------------- /test/gtest/README.txt: -------------------------------------------------------------------------------- 1 | ------------- 2 | Running Tests 3 | ------------- 4 | 5 | Because of the rules below, you can run all the unit tests in this directory, 6 | and only these tests, with: 7 | 8 | mach gtest "pkix*" 9 | 10 | You can run just the tests for functions defined in filename pkixfoo.cpp with: 11 | 12 | mach gtest "pkixfoo*" 13 | 14 | If you run "mach gtest" then you'll end up running every gtest in Gecko. 15 | 16 | 17 | 18 | ------------ 19 | Naming Files 20 | ------------ 21 | 22 | Name files containing tests according to one of the following patterns: 23 | 24 | * _tests.cpp 25 | * __tests.cpp 26 | * __tests.cpp 27 | 28 | is the name of the file containing the definitions of the 29 | function(s) being tested by every test. 30 | is the name of the function that is being tested by every 31 | test. 32 | describes the group of related functions that are being 33 | tested by every test. 34 | 35 | 36 | 37 | ------------------------------------------------ 38 | Always Use a Fixture Class: TEST_F(), not TEST() 39 | ------------------------------------------------ 40 | 41 | Many tests don't technically need a fixture, and so TEST() could technically 42 | be used to define the test. However, when you use TEST_F() instead of TEST(), 43 | the compiler will not allow you to make any typos in the test case name, but 44 | if you use TEST() then the name of the test case is not checked. 45 | 46 | See https://code.google.com/p/googletest/wiki/Primer#Test_Fixtures:_Using_the_Same_Data_Configuration_for_Multiple_Te 47 | to learn more about test fixtures. 48 | 49 | --------------- 50 | Naming Fixtures 51 | --------------- 52 | 53 | When all tests in a file use the same fixture, use the base name of the file 54 | without the "_tests" suffix as the name of the fixture class; e.g. tests in 55 | "pkixocsp.cpp" should use a fixture "class pkixocsp" by default. 56 | 57 | Sometimes tests in a file need separate fixtures. In this case, name the 58 | fixture class according to the pattern _, where 59 | is the base name of the file without the "_tests" suffix, and 60 | is a descriptive name for the fixture class, e.g. 61 | "class pkixocsp_DelegatedResponder". 62 | -------------------------------------------------------------------------------- /lib/ScopedPtr.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef mozilla_pkix_ScopedPtr_h 26 | #define mozilla_pkix_ScopedPtr_h 27 | 28 | namespace mozilla { namespace pkix { 29 | 30 | // A subset polyfill of std::unique_ptr that does not support move construction 31 | // or move assignment. This is used instead of std::unique_ptr because some 32 | // important toolchains still don't provide std::unique_ptr, including in 33 | // particular Android NDK projects with APP_STL=stlport_static or 34 | // ALL_STL=stlport_shared. 35 | template 36 | class ScopedPtr final 37 | { 38 | public: 39 | explicit ScopedPtr(T* value = nullptr) : mValue(value) { } 40 | 41 | ScopedPtr(const ScopedPtr&) = delete; 42 | 43 | ~ScopedPtr() 44 | { 45 | if (mValue) { 46 | Destroyer(mValue); 47 | } 48 | } 49 | 50 | void operator=(const ScopedPtr&) = delete; 51 | 52 | T& operator*() const { return *mValue; } 53 | T* operator->() const { return mValue; } 54 | 55 | explicit operator bool() const { return mValue; } 56 | 57 | T* get() const { return mValue; } 58 | 59 | T* release() 60 | { 61 | T* result = mValue; 62 | mValue = nullptr; 63 | return result; 64 | } 65 | 66 | void reset(T* newValue = nullptr) 67 | { 68 | // The C++ standard requires std::unique_ptr to destroy the old value 69 | // pointed to by mValue, if any, *after* assigning the new value to mValue. 70 | T* oldValue = mValue; 71 | mValue = newValue; 72 | if (oldValue) { 73 | Destroyer(oldValue); 74 | } 75 | } 76 | 77 | private: 78 | T* mValue; 79 | }; 80 | 81 | } } // namespace mozilla::pkix 82 | 83 | #endif // mozilla_pkix_ScopedPtr_h 84 | -------------------------------------------------------------------------------- /lib/pkixtime.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2014 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkix/Time.h" 26 | #include "pkixutil.h" 27 | 28 | #ifdef WIN32 29 | #ifdef _MSC_VER 30 | #pragma warning(push, 3) 31 | #endif 32 | #include "windows.h" 33 | #ifdef _MSC_VER 34 | #pragma warning(pop) 35 | #endif 36 | #else 37 | #include "sys/time.h" 38 | #endif 39 | 40 | namespace mozilla { namespace pkix { 41 | 42 | Time 43 | Now() 44 | { 45 | uint64_t seconds; 46 | 47 | #ifdef WIN32 48 | // "Contains a 64-bit value representing the number of 100-nanosecond 49 | // intervals since January 1, 1601 (UTC)." 50 | // - http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx 51 | FILETIME ft; 52 | GetSystemTimeAsFileTime(&ft); 53 | uint64_t ft64 = (static_cast(ft.dwHighDateTime) << 32) | 54 | ft.dwLowDateTime; 55 | seconds = (DaysBeforeYear(1601) * Time::ONE_DAY_IN_SECONDS) + 56 | ft64 / (1000u * 1000u * 1000u / 100u); 57 | #else 58 | // "The gettimeofday() function shall obtain the current time, expressed as 59 | // seconds and microseconds since the Epoch." 60 | // - http://pubs.opengroup.org/onlinepubs/009695399/functions/gettimeofday.html 61 | timeval tv; 62 | (void) gettimeofday(&tv, nullptr); 63 | seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) + 64 | static_cast(tv.tv_sec); 65 | #endif 66 | 67 | return TimeFromElapsedSecondsAD(seconds); 68 | } 69 | 70 | Time 71 | TimeFromEpochInSeconds(uint64_t secondsSinceEpoch) 72 | { 73 | uint64_t seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) + 74 | secondsSinceEpoch; 75 | return TimeFromElapsedSecondsAD(seconds); 76 | } 77 | 78 | } } // namespace mozilla::pkix 79 | -------------------------------------------------------------------------------- /lib/pkixverify.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2015 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixutil.h" 26 | 27 | namespace mozilla { namespace pkix { 28 | 29 | Result 30 | DigestSignedData(TrustDomain& trustDomain, 31 | const der::SignedDataWithSignature& signedData, 32 | /*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES], 33 | /*out*/ der::PublicKeyAlgorithm& publicKeyAlg, 34 | /*out*/ SignedDigest& signedDigest) 35 | { 36 | Reader signatureAlg(signedData.algorithm); 37 | Result rv = der::SignatureAlgorithmIdentifierValue( 38 | signatureAlg, publicKeyAlg, signedDigest.digestAlgorithm); 39 | if (rv != Success) { 40 | return rv; 41 | } 42 | if (!signatureAlg.AtEnd()) { 43 | return Result::ERROR_BAD_DER; 44 | } 45 | 46 | size_t digestLen; 47 | switch (signedDigest.digestAlgorithm) { 48 | case DigestAlgorithm::sha512: digestLen = 512 / 8; break; 49 | case DigestAlgorithm::sha384: digestLen = 384 / 8; break; 50 | case DigestAlgorithm::sha256: digestLen = 256 / 8; break; 51 | case DigestAlgorithm::sha1: digestLen = 160 / 8; break; 52 | MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM 53 | } 54 | assert(digestLen <= sizeof(digestBuf)); 55 | 56 | rv = trustDomain.DigestBuf(signedData.data, signedDigest.digestAlgorithm, 57 | digestBuf, digestLen); 58 | if (rv != Success) { 59 | return rv; 60 | } 61 | rv = signedDigest.digest.Init(digestBuf, digestLen); 62 | if (rv != Success) { 63 | return rv; 64 | } 65 | 66 | return signedDigest.signature.Init(signedData.signature); 67 | } 68 | 69 | Result 70 | VerifySignedDigest(TrustDomain& trustDomain, 71 | der::PublicKeyAlgorithm publicKeyAlg, 72 | const SignedDigest& signedDigest, 73 | Input signerSubjectPublicKeyInfo) 74 | { 75 | switch (publicKeyAlg) { 76 | case der::PublicKeyAlgorithm::ECDSA: 77 | return trustDomain.VerifyECDSASignedDigest(signedDigest, 78 | signerSubjectPublicKeyInfo); 79 | case der::PublicKeyAlgorithm::RSA_PKCS1: 80 | return trustDomain.VerifyRSAPKCS1SignedDigest(signedDigest, 81 | signerSubjectPublicKeyInfo); 82 | MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM 83 | } 84 | } 85 | 86 | Result 87 | VerifySignedData(TrustDomain& trustDomain, 88 | const der::SignedDataWithSignature& signedData, 89 | Input signerSubjectPublicKeyInfo) 90 | { 91 | uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES]; 92 | der::PublicKeyAlgorithm publicKeyAlg; 93 | SignedDigest signedDigest; 94 | Result rv = DigestSignedData(trustDomain, signedData, digestBuf, 95 | publicKeyAlg, signedDigest); 96 | if (rv != Success) { 97 | return rv; 98 | } 99 | return VerifySignedDigest(trustDomain, publicKeyAlg, signedDigest, 100 | signerSubjectPublicKeyInfo); 101 | } 102 | 103 | } } // namespace mozilla::pkix 104 | -------------------------------------------------------------------------------- /include/pkix/pkixnss.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef mozilla_pkix_pkixnss_h 26 | #define mozilla_pkix_pkixnss_h 27 | 28 | #include "pkixtypes.h" 29 | #include "prerror.h" 30 | #include "seccomon.h" 31 | 32 | namespace mozilla { namespace pkix { 33 | 34 | // Verifies the PKCS#1.5 signature on the given data using the given RSA public 35 | // key. 36 | Result VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd, 37 | Input subjectPublicKeyInfo, 38 | void* pkcs11PinArg); 39 | 40 | // Verifies the ECDSA signature on the given data using the given ECC public 41 | // key. 42 | Result VerifyECDSASignedDigestNSS(const SignedDigest& sd, 43 | Input subjectPublicKeyInfo, 44 | void* pkcs11PinArg); 45 | 46 | // Computes the digest of the given data using the given digest algorithm. 47 | // 48 | // item contains the data to hash. 49 | // digestBuf must point to a buffer to where the digest will be written. 50 | // digestBufLen must be the size of the buffer, which must be exactly equal 51 | // to the size of the digest output (20 for SHA-1, 32 for SHA-256, 52 | // etc.) 53 | // 54 | // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our 55 | // other, extensive, memory safety efforts in mozilla::pkix, and we should find 56 | // a way to provide a more-obviously-safe interface. 57 | Result DigestBufNSS(Input item, 58 | DigestAlgorithm digestAlg, 59 | /*out*/ uint8_t* digestBuf, 60 | size_t digestBufLen); 61 | 62 | Result MapPRErrorCodeToResult(PRErrorCode errorCode); 63 | PRErrorCode MapResultToPRErrorCode(Result result); 64 | 65 | // The error codes within each module must fit in 16 bits. We want these 66 | // errors to fit in the same module as the NSS errors but not overlap with 67 | // any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error 68 | // involves negating the value of the error and then synthesizing an error 69 | // in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at 70 | // a negative value that both doesn't overlap with the current value 71 | // ranges for NSS errors and that will fit in 16 bits when negated. 72 | static const PRErrorCode ERROR_BASE = -0x4000; 73 | static const PRErrorCode ERROR_LIMIT = ERROR_BASE + 1000; 74 | 75 | enum ErrorCode 76 | { 77 | MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = ERROR_BASE + 0, 78 | MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY = ERROR_BASE + 1, 79 | MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE = ERROR_BASE + 2, 80 | MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA = ERROR_BASE + 3, 81 | MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH = ERROR_BASE + 4, 82 | MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = ERROR_BASE + 5, 83 | MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = ERROR_BASE + 6, 84 | MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH = ERROR_BASE + 7, 85 | MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING = ERROR_BASE + 8, 86 | MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG = ERROR_BASE + 9, 87 | }; 88 | 89 | void RegisterErrorTable(); 90 | 91 | inline SECItem UnsafeMapInputToSECItem(Input input) 92 | { 93 | SECItem result = { 94 | siBuffer, 95 | const_cast(input.UnsafeGetData()), 96 | input.GetLength() 97 | }; 98 | static_assert(sizeof(decltype(input.GetLength())) <= sizeof(result.len), 99 | "input.GetLength() must fit in a SECItem"); 100 | return result; 101 | } 102 | 103 | } } // namespace mozilla::pkix 104 | 105 | #endif // mozilla_pkix_pkixnss_h 106 | -------------------------------------------------------------------------------- /include/pkix/Time.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2014 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef mozilla_pkix_Time_h 26 | #define mozilla_pkix_Time_h 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "pkix/Result.h" 33 | 34 | namespace mozilla { namespace pkix { 35 | 36 | // Time with a range from the first second of year 0 (AD) through at least the 37 | // last second of year 9999, which is the range of legal times in X.509 and 38 | // OCSP. This type has second-level precision. The time zone is always UTC. 39 | // 40 | // Pass by value, not by reference. 41 | class Time final 42 | { 43 | public: 44 | // Construct an uninitialized instance. 45 | // 46 | // This will fail to compile because there is no default constructor: 47 | // Time x; 48 | // 49 | // This will succeed, leaving the time uninitialized: 50 | // Time x(Time::uninitialized); 51 | enum Uninitialized { uninitialized }; 52 | explicit Time(Uninitialized) { } 53 | 54 | bool operator==(const Time& other) const 55 | { 56 | return elapsedSecondsAD == other.elapsedSecondsAD; 57 | } 58 | bool operator>(const Time& other) const 59 | { 60 | return elapsedSecondsAD > other.elapsedSecondsAD; 61 | } 62 | bool operator>=(const Time& other) const 63 | { 64 | return elapsedSecondsAD >= other.elapsedSecondsAD; 65 | } 66 | bool operator<(const Time& other) const 67 | { 68 | return elapsedSecondsAD < other.elapsedSecondsAD; 69 | } 70 | bool operator<=(const Time& other) const 71 | { 72 | return elapsedSecondsAD <= other.elapsedSecondsAD; 73 | } 74 | 75 | Result AddSeconds(uint64_t seconds) 76 | { 77 | if (std::numeric_limits::max() - elapsedSecondsAD 78 | < seconds) { 79 | return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow 80 | } 81 | elapsedSecondsAD += seconds; 82 | return Success; 83 | } 84 | 85 | Result SubtractSeconds(uint64_t seconds) 86 | { 87 | if (seconds > elapsedSecondsAD) { 88 | return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow 89 | } 90 | elapsedSecondsAD -= seconds; 91 | return Success; 92 | } 93 | 94 | static const uint64_t ONE_DAY_IN_SECONDS 95 | = UINT64_C(24) * UINT64_C(60) * UINT64_C(60); 96 | 97 | private: 98 | // This constructor is hidden to prevent accidents like this: 99 | // 100 | // Time foo(time_t t) 101 | // { 102 | // // WRONG! 1970-01-01-00:00:00 == time_t(0), but not Time(0)! 103 | // return Time(t); 104 | // } 105 | explicit Time(uint64_t elapsedSecondsAD) 106 | : elapsedSecondsAD(elapsedSecondsAD) 107 | { 108 | } 109 | friend Time TimeFromElapsedSecondsAD(uint64_t); 110 | friend class Duration; 111 | 112 | uint64_t elapsedSecondsAD; 113 | }; 114 | 115 | inline Time TimeFromElapsedSecondsAD(uint64_t elapsedSecondsAD) 116 | { 117 | return Time(elapsedSecondsAD); 118 | } 119 | 120 | Time Now(); 121 | 122 | // Note the epoch is the unix epoch (ie 00:00:00 UTC, 1 January 1970) 123 | Time TimeFromEpochInSeconds(uint64_t secondsSinceEpoch); 124 | 125 | class Duration final 126 | { 127 | public: 128 | Duration(Time timeA, Time timeB) 129 | : durationInSeconds(timeA < timeB 130 | ? timeB.elapsedSecondsAD - timeA.elapsedSecondsAD 131 | : timeA.elapsedSecondsAD - timeB.elapsedSecondsAD) 132 | { 133 | } 134 | 135 | explicit Duration(uint64_t durationInSeconds) 136 | : durationInSeconds(durationInSeconds) 137 | { 138 | } 139 | 140 | bool operator>(const Duration& other) const 141 | { 142 | return durationInSeconds > other.durationInSeconds; 143 | } 144 | bool operator<(const Duration& other) const 145 | { 146 | return durationInSeconds < other.durationInSeconds; 147 | } 148 | 149 | private: 150 | uint64_t durationInSeconds; 151 | }; 152 | 153 | } } // namespace mozilla::pkix 154 | 155 | #endif // mozilla_pkix_Time_h 156 | -------------------------------------------------------------------------------- /test/gtest/pkixcheck_CheckValidity_tests.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2014 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixcheck.h" 26 | #include "pkixgtest.h" 27 | 28 | using namespace mozilla::pkix; 29 | using namespace mozilla::pkix::test; 30 | 31 | static const Time PAST_TIME(YMDHMS(1998, 12, 31, 12, 23, 56)); 32 | 33 | #define OLDER_GENERALIZEDTIME \ 34 | 0x18, 15, /* tag, length */ \ 35 | '1', '9', '9', '9', '0', '1', '0', '1', /* 1999-01-01 */ \ 36 | '0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */ 37 | 38 | #define OLDER_UTCTIME \ 39 | 0x17, 13, /* tag, length */ \ 40 | '9', '9', '0', '1', '0', '1', /* (19)99-01-01 */ \ 41 | '0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */ 42 | 43 | static const Time NOW(YMDHMS(2016, 12, 31, 12, 23, 56)); 44 | 45 | #define NEWER_GENERALIZEDTIME \ 46 | 0x18, 15, /* tag, length */ \ 47 | '2', '0', '2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \ 48 | '0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */ 49 | 50 | #define NEWER_UTCTIME \ 51 | 0x17, 13, /* tag, length */ \ 52 | '2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \ 53 | '0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */ 54 | 55 | static const Time FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56)); 56 | 57 | class pkixcheck_CheckValidity : public ::testing::Test { }; 58 | 59 | TEST_F(pkixcheck_CheckValidity, BothEmptyNull) 60 | { 61 | static const uint8_t DER[] = { 62 | 0x17/*UTCTime*/, 0/*length*/, 63 | 0x17/*UTCTime*/, 0/*length*/, 64 | }; 65 | static const Input validity(DER); 66 | ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, CheckValidity(validity, NOW)); 67 | } 68 | 69 | TEST_F(pkixcheck_CheckValidity, NotBeforeEmptyNull) 70 | { 71 | static const uint8_t DER[] = { 72 | 0x17/*UTCTime*/, 0x00/*length*/, 73 | NEWER_UTCTIME 74 | }; 75 | static const Input validity(DER); 76 | ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, CheckValidity(validity, NOW)); 77 | } 78 | 79 | TEST_F(pkixcheck_CheckValidity, NotAfterEmptyNull) 80 | { 81 | static const uint8_t DER[] = { 82 | NEWER_UTCTIME, 83 | 0x17/*UTCTime*/, 0x00/*length*/, 84 | }; 85 | static const Input validity(DER); 86 | ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, CheckValidity(validity, NOW)); 87 | } 88 | 89 | static const uint8_t OLDER_UTCTIME_NEWER_UTCTIME_DATA[] = { 90 | OLDER_UTCTIME, 91 | NEWER_UTCTIME, 92 | }; 93 | static const Input 94 | OLDER_UTCTIME_NEWER_UTCTIME(OLDER_UTCTIME_NEWER_UTCTIME_DATA); 95 | 96 | TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_UTCTIME) 97 | { 98 | ASSERT_EQ(Success, CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, NOW)); 99 | } 100 | 101 | TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_GENERALIZEDTIME) 102 | { 103 | static const uint8_t DER[] = { 104 | OLDER_GENERALIZEDTIME, 105 | NEWER_GENERALIZEDTIME, 106 | }; 107 | static const Input validity(DER); 108 | ASSERT_EQ(Success, CheckValidity(validity, NOW)); 109 | } 110 | 111 | TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_UTCTIME) 112 | { 113 | static const uint8_t DER[] = { 114 | OLDER_GENERALIZEDTIME, 115 | NEWER_UTCTIME, 116 | }; 117 | static const Input validity(DER); 118 | ASSERT_EQ(Success, CheckValidity(validity, NOW)); 119 | } 120 | 121 | TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_GENERALIZEDTIME) 122 | { 123 | static const uint8_t DER[] = { 124 | OLDER_UTCTIME, 125 | NEWER_GENERALIZEDTIME, 126 | }; 127 | static const Input validity(DER); 128 | ASSERT_EQ(Success, CheckValidity(validity, NOW)); 129 | } 130 | 131 | TEST_F(pkixcheck_CheckValidity, InvalidBeforeNotBefore) 132 | { 133 | ASSERT_EQ(Result::ERROR_NOT_YET_VALID_CERTIFICATE, 134 | CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, PAST_TIME)); 135 | } 136 | 137 | TEST_F(pkixcheck_CheckValidity, InvalidAfterNotAfter) 138 | { 139 | ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, 140 | CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, FUTURE_TIME)); 141 | } 142 | 143 | TEST_F(pkixcheck_CheckValidity, InvalidNotAfterBeforeNotBefore) 144 | { 145 | static const uint8_t DER[] = { 146 | NEWER_UTCTIME, 147 | OLDER_UTCTIME, 148 | }; 149 | static const Input validity(DER); 150 | ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, CheckValidity(validity, NOW)); 151 | } 152 | -------------------------------------------------------------------------------- /test/gtest/pkixocsp_CreateEncodedOCSPRequest_tests.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixgtest.h" 26 | #include "pkixder.h" 27 | 28 | using namespace mozilla::pkix; 29 | using namespace mozilla::pkix::test; 30 | 31 | class CreateEncodedOCSPRequestTrustDomain final 32 | : public EverythingFailsByDefaultTrustDomain 33 | { 34 | private: 35 | Result DigestBuf(Input item, DigestAlgorithm digestAlg, 36 | /*out*/ uint8_t *digestBuf, size_t digestBufLen) 37 | override 38 | { 39 | return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen); 40 | } 41 | 42 | Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) 43 | override 44 | { 45 | return Success; 46 | } 47 | }; 48 | 49 | class pkixocsp_CreateEncodedOCSPRequest : public ::testing::Test 50 | { 51 | protected: 52 | void MakeIssuerCertIDComponents(const char* issuerASCII, 53 | /*out*/ ByteString& issuerDER, 54 | /*out*/ ByteString& issuerSPKI) 55 | { 56 | issuerDER = CNToDERName(issuerASCII); 57 | ASSERT_FALSE(ENCODING_FAILED(issuerDER)); 58 | 59 | ScopedTestKeyPair keyPair(GenerateKeyPair()); 60 | ASSERT_TRUE(keyPair.get()); 61 | issuerSPKI = keyPair->subjectPublicKeyInfo; 62 | } 63 | 64 | CreateEncodedOCSPRequestTrustDomain trustDomain; 65 | }; 66 | 67 | // Test that the large length of the child serial number causes 68 | // CreateEncodedOCSPRequest to fail. 69 | TEST_F(pkixocsp_CreateEncodedOCSPRequest, ChildCertLongSerialNumberTest) 70 | { 71 | static const uint8_t UNSUPPORTED_LEN = 128; // must be larger than 127 72 | 73 | ByteString serialNumberString; 74 | // tag + length + value is 1 + 2 + UNSUPPORTED_LEN 75 | // Encoding the length takes two bytes: one byte to indicate that a 76 | // second byte follows, and the second byte to indicate the length. 77 | serialNumberString.push_back(0x80 + 1); 78 | serialNumberString.push_back(UNSUPPORTED_LEN); 79 | // value is 0x010000...00 80 | serialNumberString.push_back(0x01); 81 | for (size_t i = 1; i < UNSUPPORTED_LEN; ++i) { 82 | serialNumberString.push_back(0x00); 83 | } 84 | 85 | ByteString issuerDER; 86 | ByteString issuerSPKI; 87 | ASSERT_NO_FATAL_FAILURE(MakeIssuerCertIDComponents("CA", issuerDER, 88 | issuerSPKI)); 89 | 90 | Input issuer; 91 | ASSERT_EQ(Success, issuer.Init(issuerDER.data(), issuerDER.length())); 92 | 93 | Input spki; 94 | ASSERT_EQ(Success, spki.Init(issuerSPKI.data(), issuerSPKI.length())); 95 | 96 | Input serialNumber; 97 | ASSERT_EQ(Success, serialNumber.Init(serialNumberString.data(), 98 | serialNumberString.length())); 99 | 100 | uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH]; 101 | size_t ocspRequestLength; 102 | ASSERT_EQ(Result::ERROR_BAD_DER, 103 | CreateEncodedOCSPRequest(trustDomain, 104 | CertID(issuer, spki, serialNumber), 105 | ocspRequest, ocspRequestLength)); 106 | } 107 | 108 | // Test that CreateEncodedOCSPRequest handles the longest serial number that 109 | // it's required to support (i.e. 20 octets). 110 | TEST_F(pkixocsp_CreateEncodedOCSPRequest, LongestSupportedSerialNumberTest) 111 | { 112 | static const uint8_t LONGEST_REQUIRED_LEN = 20; 113 | 114 | ByteString serialNumberString; 115 | // tag + length + value is 1 + 1 + LONGEST_REQUIRED_LEN 116 | serialNumberString.push_back(der::INTEGER); 117 | serialNumberString.push_back(LONGEST_REQUIRED_LEN); 118 | serialNumberString.push_back(0x01); 119 | // value is 0x010000...00 120 | for (size_t i = 1; i < LONGEST_REQUIRED_LEN; ++i) { 121 | serialNumberString.push_back(0x00); 122 | } 123 | 124 | ByteString issuerDER; 125 | ByteString issuerSPKI; 126 | ASSERT_NO_FATAL_FAILURE(MakeIssuerCertIDComponents("CA", issuerDER, 127 | issuerSPKI)); 128 | 129 | Input issuer; 130 | ASSERT_EQ(Success, issuer.Init(issuerDER.data(), issuerDER.length())); 131 | 132 | Input spki; 133 | ASSERT_EQ(Success, spki.Init(issuerSPKI.data(), issuerSPKI.length())); 134 | 135 | Input serialNumber; 136 | ASSERT_EQ(Success, serialNumber.Init(serialNumberString.data(), 137 | serialNumberString.length())); 138 | 139 | uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH]; 140 | size_t ocspRequestLength; 141 | ASSERT_EQ(Success, 142 | CreateEncodedOCSPRequest(trustDomain, 143 | CertID(issuer, spki, serialNumber), 144 | ocspRequest, ocspRequestLength)); 145 | } 146 | -------------------------------------------------------------------------------- /test/gtest/pkixgtest.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2014 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | #ifndef mozilla_pkix_pkixgtest_h 25 | #define mozilla_pkix_pkixgtest_h 26 | 27 | #include 28 | 29 | #if defined(__clang__) 30 | #pragma clang diagnostic push 31 | #pragma clang diagnostic ignored "-Wdeprecated" 32 | #pragma clang diagnostic ignored "-Wmissing-noreturn" 33 | #pragma clang diagnostic ignored "-Wshift-sign-overflow" 34 | #pragma clang diagnostic ignored "-Wsign-conversion" 35 | #pragma clang diagnostic ignored "-Wundef" 36 | #elif defined(__GNUC__) 37 | #pragma GCC diagnostic push 38 | #pragma GCC diagnostic ignored "-Wextra" 39 | #elif defined(_MSC_VER) 40 | #pragma warning(push, 3) 41 | // C4224: Nonstandard extension used: formal parameter 'X' was previously 42 | // defined as a type. 43 | #pragma warning(disable: 4224) 44 | // C4826: Conversion from 'type1 ' to 'type_2' is sign - extended. This may 45 | // cause unexpected runtime behavior. 46 | #pragma warning(disable: 4826) 47 | #endif 48 | 49 | #include "gtest/gtest.h" 50 | 51 | #if defined(__clang__) 52 | #pragma clang diagnostic pop 53 | #elif defined(__GNUC__) 54 | #pragma GCC diagnostic pop 55 | #elif defined(_MSC_VER) 56 | #pragma warning(pop) 57 | #endif 58 | 59 | #include "pkix/pkix.h" 60 | #include "pkixtestutil.h" 61 | 62 | // PrintTo must be in the same namespace as the type we're overloading it for. 63 | namespace mozilla { namespace pkix { 64 | 65 | inline void 66 | PrintTo(const Result& result, ::std::ostream* os) 67 | { 68 | const char* stringified = MapResultToName(result); 69 | if (stringified) { 70 | *os << stringified; 71 | } else { 72 | *os << "mozilla::pkix::Result(" << static_cast(result) << ")"; 73 | } 74 | } 75 | 76 | } } // namespace mozilla::pkix 77 | 78 | namespace mozilla { namespace pkix { namespace test { 79 | 80 | extern const std::time_t ONE_DAY_IN_SECONDS_AS_TIME_T; 81 | 82 | extern const std::time_t now; 83 | extern const std::time_t oneDayBeforeNow; 84 | extern const std::time_t oneDayAfterNow; 85 | 86 | 87 | class EverythingFailsByDefaultTrustDomain : public TrustDomain 88 | { 89 | public: 90 | Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, 91 | Input, /*out*/ TrustLevel&) override 92 | { 93 | ADD_FAILURE(); 94 | return NotReached("GetCertTrust should not be called", 95 | Result::FATAL_ERROR_LIBRARY_FAILURE); 96 | } 97 | 98 | Result FindIssuer(Input, IssuerChecker&, Time) override 99 | { 100 | ADD_FAILURE(); 101 | return NotReached("FindIssuer should not be called", 102 | Result::FATAL_ERROR_LIBRARY_FAILURE); 103 | } 104 | 105 | Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, 106 | /*optional*/ const Input*, 107 | /*optional*/ const Input*) override 108 | { 109 | ADD_FAILURE(); 110 | return NotReached("CheckRevocation should not be called", 111 | Result::FATAL_ERROR_LIBRARY_FAILURE); 112 | } 113 | 114 | Result IsChainValid(const DERArray&, Time) override 115 | { 116 | ADD_FAILURE(); 117 | return NotReached("IsChainValid should not be called", 118 | Result::FATAL_ERROR_LIBRARY_FAILURE); 119 | } 120 | 121 | Result DigestBuf(Input, DigestAlgorithm, /*out*/ uint8_t*, size_t) override 122 | { 123 | ADD_FAILURE(); 124 | return NotReached("DigestBuf should not be called", 125 | Result::FATAL_ERROR_LIBRARY_FAILURE); 126 | } 127 | 128 | Result CheckSignatureDigestAlgorithm(DigestAlgorithm, 129 | EndEntityOrCA) override 130 | { 131 | ADD_FAILURE(); 132 | return NotReached("CheckSignatureDigestAlgorithm should not be called", 133 | Result::FATAL_ERROR_LIBRARY_FAILURE); 134 | } 135 | 136 | Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override 137 | { 138 | ADD_FAILURE(); 139 | return NotReached("CheckECDSACurveIsAcceptable should not be called", 140 | Result::FATAL_ERROR_LIBRARY_FAILURE); 141 | } 142 | 143 | Result VerifyECDSASignedDigest(const SignedDigest&, Input) override 144 | { 145 | ADD_FAILURE(); 146 | return NotReached("VerifyECDSASignedDigest should not be called", 147 | Result::FATAL_ERROR_LIBRARY_FAILURE); 148 | } 149 | 150 | Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) 151 | override 152 | { 153 | ADD_FAILURE(); 154 | return NotReached("CheckRSAPublicKeyModulusSizeInBits should not be called", 155 | Result::FATAL_ERROR_LIBRARY_FAILURE); 156 | } 157 | 158 | Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override 159 | { 160 | ADD_FAILURE(); 161 | return NotReached("VerifyRSAPKCS1SignedDigest should not be called", 162 | Result::FATAL_ERROR_LIBRARY_FAILURE); 163 | } 164 | 165 | Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) 166 | override 167 | { 168 | ADD_FAILURE(); 169 | return NotReached("CheckValidityIsAcceptable should not be called", 170 | Result::FATAL_ERROR_LIBRARY_FAILURE); 171 | } 172 | }; 173 | 174 | class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain 175 | { 176 | Result DigestBuf(Input item, DigestAlgorithm digestAlg, 177 | /*out*/ uint8_t* digestBuf, size_t digestBufLen) override 178 | { 179 | return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen); 180 | } 181 | 182 | Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA) override 183 | { 184 | return Success; 185 | } 186 | 187 | Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override 188 | { 189 | return Success; 190 | } 191 | 192 | Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, 193 | Input subjectPublicKeyInfo) override 194 | { 195 | return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo); 196 | } 197 | 198 | Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) 199 | override 200 | { 201 | return Success; 202 | } 203 | 204 | Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, 205 | Input subjectPublicKeyInfo) override 206 | { 207 | return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo); 208 | } 209 | 210 | Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) 211 | override 212 | { 213 | return Success; 214 | } 215 | }; 216 | 217 | } } } // namespace mozilla::pkix::test 218 | 219 | #endif // mozilla_pkix_pkixgtest_h 220 | -------------------------------------------------------------------------------- /include/pkix/pkix.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef mozilla_pkix_pkix_h 26 | #define mozilla_pkix_pkix_h 27 | 28 | #include "pkixtypes.h" 29 | 30 | namespace mozilla { namespace pkix { 31 | 32 | // ---------------------------------------------------------------------------- 33 | // LIMITED SUPPORT FOR CERTIFICATE POLICIES 34 | // 35 | // If SEC_OID_X509_ANY_POLICY is passed as the value of the requiredPolicy 36 | // parameter then all policy validation will be skipped. Otherwise, path 37 | // building and validation will be done for the given policy. 38 | // 39 | // In RFC 5280 terms: 40 | // 41 | // * user-initial-policy-set = { requiredPolicy }. 42 | // * initial-explicit-policy = true 43 | // * initial-any-policy-inhibit = false 44 | // 45 | // We allow intermediate cerificates to use this extension but since 46 | // we do not process the inhibit anyPolicy extesion we will fail if this 47 | // extension is present. TODO(bug 989051) 48 | // Because we force explicit policy and because we prohibit policy mapping, we 49 | // do not bother processing the policy mapping, or policy constraint. 50 | // 51 | // ---------------------------------------------------------------------------- 52 | // ERROR RANKING 53 | // 54 | // BuildCertChain prioritizes certain checks ahead of others so that when a 55 | // certificate chain has multiple errors, the "most serious" error is 56 | // returned. In practice, this ranking of seriousness is tied directly to how 57 | // Firefox's certificate error override mechanism. 58 | // 59 | // The ranking is: 60 | // 61 | // 1. Active distrust (Result::ERROR_UNTRUSTED_CERT). 62 | // 2. Problems with issuer-independent properties for CA certificates. 63 | // 3. Unknown issuer (Result::ERROR_UNKNOWN_ISSUER). 64 | // 4. Problems with issuer-independent properties for EE certificates. 65 | // 5. Revocation. 66 | // 67 | // In particular, if BuildCertChain returns Result::ERROR_UNKNOWN_ISSUER then 68 | // the caller can call CERT_CheckCertValidTimes to determine if the certificate 69 | // is ALSO expired. 70 | // 71 | // It would be better if revocation were prioritized above expiration and 72 | // unknown issuer. However, it is impossible to do revocation checking without 73 | // knowing the issuer, since the issuer information is needed to validate the 74 | // revocation information. Also, generally revocation checking only works 75 | // during the validity period of the certificate. 76 | // 77 | // In general, when path building fails, BuildCertChain will return 78 | // Result::ERROR_UNKNOWN_ISSUER. However, if all attempted paths resulted in 79 | // the same error (which is trivially true when there is only one potential 80 | // path), more specific errors will be returned. 81 | // 82 | // ---------------------------------------------------------------------------- 83 | // Meanings of specific error codes can be found in Result.h 84 | 85 | // This function attempts to find a trustworthy path from the supplied 86 | // certificate to a trust anchor. In the event that no trusted path is found, 87 | // the method returns an error result; the error ranking is described above. 88 | // 89 | // Parameters: 90 | // time: 91 | // Timestamp for which the chain should be valid; this is useful to 92 | // analyze whether a record was trustworthy when it was made. 93 | // requiredKeyUsageIfPresent: 94 | // What key usage bits must be set, if the extension is present at all, 95 | // to be considered a valid chain. Multiple values should be OR'd 96 | // together. If you don't want to specify anything, use 97 | // KeyUsage::noParticularKeyUsageRequired. 98 | // requiredEKUIfPresent: 99 | // What extended key usage bits must be set, if the EKU extension 100 | // exists, to be considered a valid chain. Multiple values should be 101 | // OR'd together. If you don't want to specify anything, use 102 | // KeyPurposeId::anyExtendedKeyUsage. 103 | // requiredPolicy: 104 | // This is the policy to apply; typically included in EV certificates. 105 | // If there is no policy, pass in CertPolicyId::anyPolicy. 106 | Result BuildCertChain(TrustDomain& trustDomain, Input cert, 107 | Time time, EndEntityOrCA endEntityOrCA, 108 | KeyUsage requiredKeyUsageIfPresent, 109 | KeyPurposeId requiredEKUIfPresent, 110 | const CertPolicyId& requiredPolicy, 111 | /*optional*/ const Input* stapledOCSPResponse); 112 | 113 | // Verify that the given end-entity cert, which is assumed to have been already 114 | // validated with BuildCertChain, is valid for the given hostname. The matching 115 | // function attempts to implement RFC 6125 with a couple of differences: 116 | // - IP addresses are out of scope of RFC 6125, but this method accepts them for 117 | // backward compatibility (see SearchNames in pkixnames.cpp) 118 | // - A wildcard in a DNS-ID may only appear as the entirety of the first label. 119 | Result CheckCertHostname(Input cert, Input hostname); 120 | 121 | // Construct an RFC-6960-encoded OCSP request, ready for submission to a 122 | // responder, for the provided CertID. The request has no extensions. 123 | static const size_t OCSP_REQUEST_MAX_LENGTH = 127; 124 | Result CreateEncodedOCSPRequest(TrustDomain& trustDomain, 125 | const CertID& certID, 126 | /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH], 127 | /*out*/ size_t& outLen); 128 | 129 | // The out parameter expired will be true if the response has expired. If the 130 | // response also indicates a revoked or unknown certificate, that error 131 | // will be returned. Otherwise, Result::ERROR_OCSP_OLD_RESPONSE will be 132 | // returned for an expired response. 133 | // 134 | // The optional parameter thisUpdate will be the thisUpdate value of 135 | // the encoded response if it is considered trustworthy. Only 136 | // good, unknown, or revoked responses that verify correctly are considered 137 | // trustworthy. If the response is not trustworthy, thisUpdate will be 0. 138 | // Similarly, the optional parameter validThrough will be the time through 139 | // which the encoded response is considered trustworthy (that is, as long as 140 | // the given time at which to validate is less than or equal to validThrough, 141 | // the response will be considered trustworthy). 142 | Result VerifyEncodedOCSPResponse(TrustDomain& trustDomain, 143 | const CertID& certID, Time time, 144 | uint16_t maxLifetimeInDays, 145 | Input encodedResponse, 146 | /* out */ bool& expired, 147 | /* optional out */ Time* thisUpdate = nullptr, 148 | /* optional out */ Time* validThrough = nullptr); 149 | 150 | } } // namespace mozilla::pkix 151 | 152 | #endif // mozilla_pkix_pkix_h 153 | -------------------------------------------------------------------------------- /tools/hg-to-git-am.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # The documentation for this is in hg-to-git-README.txt. 3 | from __future__ import print_function 4 | import os 5 | import re 6 | import subprocess 7 | import sys 8 | from cStringIO import StringIO 9 | 10 | def stop(msg): 11 | print("Error: %s" % msg, file=sys.stderr) 12 | sys.exit(1) 13 | 14 | def parse_line(line, regex): 15 | match = re.match(regex, line) 16 | if not match: 17 | stop('Error: expected "%s", got "%s"' % (regex, line)); 18 | return match.groups() 19 | 20 | def hg_patch_to_git_patch(hg_repo, hg_patch): 21 | # XXX: hg 3.0.1's "hg cat" doesn't understand the --repository argument, 22 | # so we have to change to the HG repo directory instead. 23 | os.chdir(hg_repo) 24 | 25 | lines = hg_patch.split('\n') 26 | 27 | # HG metadata at the top of the patch 28 | parse_line(lines[0], '# HG changeset patch$') 29 | author = parse_line(lines[1], '# User (.+ <.+>)$')[0] 30 | parse_line(lines[2], '# Date .+$') 31 | date = parse_line(lines[3], '# (.+)$')[0] 32 | hg_hash = parse_line(lines[4], '# Node ID (.+)$')[0] 33 | # skip remaining metadata 34 | i = 5 35 | while i < len(lines) and lines[i].startswith('#'): 36 | i += 1 37 | 38 | # Commit message 39 | commit_msg = [] 40 | while i < len(lines): 41 | if lines[i].startswith('diff --git'): 42 | break 43 | commit_msg.append(lines[i]) 44 | i += 1 45 | 46 | # Remove blank lines at the end of the commit message. 47 | while commit_msg and not commit_msg[-1].strip(): 48 | del commit_msg[-1] 49 | 50 | if not commit_msg: 51 | stop('no commit essage'); 52 | 53 | # Separate amendments to the commit message from the hand-written part 54 | commit_msg.append("") 55 | 56 | # Append links to all the bugs mentioned in the first line of the commit 57 | # message to the commit message. 58 | for bug in re.findall('bug ([1-9](?:[0-9]*))', commit_msg[0], re.I): 59 | commit_msg.append( 60 | 'BUG=https://bugzilla.mozilla.org/show_bug.cgi?id=%s' % bug) 61 | 62 | commit_msg.append("") 63 | 64 | # Append a link to the mozilla-central commit to the commit message. 65 | # The short node ID is the first 12 characters of the node ID. 66 | commit_msg.append( 67 | 'Imported-from: https://hg.mozilla.org/mozilla-central/rev/%s' 68 | % (hg_hash[0:12])) 69 | 70 | diff_lines = filter_diffs(lines[i:], hg_hash) 71 | 72 | if not diff_lines: 73 | stop('No relevant changes in patch') 74 | 75 | # XXX Do I have to worry about text encoding here? 76 | print('From: %s' % author) 77 | print('Date: %s' % date) 78 | print('Subject: %s' % commit_msg[0]) 79 | 80 | if (len(commit_msg) > 1): 81 | print() 82 | print('\n'.join(commit_msg[1:])) 83 | 84 | # Add a git note like hg-fast-export does 85 | print() 86 | print('---') 87 | print() 88 | print('Notes (hg):') 89 | print(' %s' % hg_hash) 90 | print() 91 | 92 | print('\n'.join(diff_lines)) 93 | 94 | def substitute_path(old_path): 95 | '''If old_path identifies a file under security/insanity/ or security/pkix 96 | then returns the path without the security/insanity or security/pkix prefix. 97 | 98 | If old_path identifies /dev/null then returns '/dev/null'. ('/dev/null' is 99 | used as the original file path when a file is new; it is used as the new 100 | file path when the file is removed.) 101 | 102 | Otherwise, the path identifies a file that is not within mozilla::pkix, 103 | in which case this function returns None. 104 | ''' 105 | match = re.match('([ab])/security/(?:insanity|pkix)/(.+)$', old_path) 106 | if not match: 107 | if old_path == '/dev/null': 108 | return old_path 109 | return None 110 | 111 | return '%s/%s' % (match.group(1), match.group(2)) 112 | return result 113 | 114 | def unprefixed_path(path): 115 | if path == '/dev/null': 116 | return path 117 | 118 | return path[2:] 119 | 120 | def filter_diffs(diff, hg_hash): 121 | result = [] 122 | i = 0 123 | while i < len(diff): 124 | try: 125 | old_a, old_b = parse_line(diff[i], 'diff --git ([^ ]+) (.+)$') 126 | except ValueError: 127 | stop(diff[i] + (", line %d" % i)) 128 | 129 | i += 1 130 | 131 | new_a = substitute_path(old_a) 132 | new_b = substitute_path(old_b) 133 | 134 | if (not new_a) and (not new_b): 135 | # The diff does not affect mozilla::pkix at all so ignore it. 136 | while i < len(diff) and not(diff[i].startswith('diff --git')): 137 | i += 1 138 | continue 139 | 140 | # Moves from a directory outside of mozilla::pkix and moves to a 141 | # directory outside of mozilla::pkix are treated like file additions 142 | # and file deletions, respectively. Additions and deletions use the 143 | # same file name in the "diff --git" command for both the source and 144 | # destination. 145 | diff_src = new_a if new_a else new_b 146 | diff_dest = new_b if new_b else new_a 147 | 148 | # Copy the metadata for the diff, substituting names. 149 | 150 | synthesize_patch = (not new_a) or (not new_b) 151 | if not synthesize_patch: 152 | result.append('diff --git a/%s b/%s' % (unprefixed_path(new_a), unprefixed_path(new_b))) 153 | while i < len(diff) and not(diff[i].startswith('diff --git')): 154 | if not synthesize_patch: 155 | result.append(diff[i].replace(unprefixed_path(old_a), 156 | unprefixed_path(new_a)) 157 | .replace(unprefixed_path(old_b), 158 | unprefixed_path(new_b))) 159 | was_plus_plus_plus = diff[i].startswith('+++') 160 | i += 1 161 | if was_plus_plus_plus: 162 | break 163 | elif not new_a: 164 | # Synthesized file addition from a move from outside mozilla::pkix. 165 | print("hg cat -r " + hg_hash + " " + unprefixed_path(old_b), file=sys.stderr) 166 | file_contents = subprocess.Popen( 167 | ["hg", "cat", "-r", hg_hash, unprefixed_path(old_b)], 168 | stdout=subprocess.PIPE, 169 | stderr=subprocess.PIPE, 170 | stdin=subprocess.PIPE, 171 | env={'PATH': os.environ['PATH']}).communicate()[0] 172 | result.append('diff --git a/%s b/%s' % (unprefixed_path(new_b), unprefixed_path(new_b))) 173 | result.append("new file mode 100644") 174 | result.append("--- /dev/null") 175 | result.append("+++ " + new_b) 176 | lines = [] 177 | for line in file_contents.split('\n'): 178 | lines.append("+" + line) 179 | result.append("@@ -0,0 +1,%d @@" % (len(lines))) 180 | result = result + lines 181 | for line in result: 182 | print(line, file=sys.stderr) 183 | else: 184 | # Synthesized file removal from a move to outside mozilla::pkix. 185 | result.append('diff --git a/%s b/%s' % (unprefixed_path(new_a), unprefixed_path(new_a))) 186 | result.append("deleted file mode 100644") 187 | result.append("--- " + new_a) 188 | result.append("+++ /dev/null") 189 | # TODO: result.append("@@ -0,0 +1,%d" % (len(lines))) 190 | 191 | while i < len(diff) and not(diff[i].startswith('diff --git')): 192 | if not synthesize_patch: 193 | # Copy the actual diff, verbatim. 194 | result.append(diff[i]) 195 | i += 1 196 | 197 | return result 198 | 199 | if __name__ == '__main__': 200 | hg_patch_to_git_patch(sys.argv[1], sys.stdin.read()) 201 | -------------------------------------------------------------------------------- /lib/pkixnss.cpp: -------------------------------------------------------------------------------- 1 | /*- *- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkix/pkixnss.h" 26 | 27 | #include 28 | 29 | #include "cryptohi.h" 30 | #include "keyhi.h" 31 | #include "pk11pub.h" 32 | #include "pkix/pkix.h" 33 | #include "pkixutil.h" 34 | #include "ScopedPtr.h" 35 | #include "secerr.h" 36 | #include "sslerr.h" 37 | 38 | namespace mozilla { namespace pkix { 39 | 40 | namespace { 41 | 42 | Result 43 | VerifySignedDigest(const SignedDigest& sd, 44 | Input subjectPublicKeyInfo, 45 | SECOidTag pubKeyAlg, 46 | void* pkcs11PinArg) 47 | { 48 | SECOidTag digestAlg; 49 | switch (sd.digestAlgorithm) { 50 | case DigestAlgorithm::sha512: digestAlg = SEC_OID_SHA512; break; 51 | case DigestAlgorithm::sha384: digestAlg = SEC_OID_SHA384; break; 52 | case DigestAlgorithm::sha256: digestAlg = SEC_OID_SHA256; break; 53 | case DigestAlgorithm::sha1: digestAlg = SEC_OID_SHA1; break; 54 | MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM 55 | } 56 | 57 | SECItem subjectPublicKeyInfoSECItem = 58 | UnsafeMapInputToSECItem(subjectPublicKeyInfo); 59 | ScopedPtr 60 | spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem)); 61 | if (!spki) { 62 | return MapPRErrorCodeToResult(PR_GetError()); 63 | } 64 | ScopedPtr 65 | pubKey(SECKEY_ExtractPublicKey(spki.get())); 66 | if (!pubKey) { 67 | return MapPRErrorCodeToResult(PR_GetError()); 68 | } 69 | 70 | SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest)); 71 | SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature)); 72 | SECStatus srv = VFY_VerifyDigestDirect(&digestSECItem, pubKey.get(), 73 | &signatureSECItem, pubKeyAlg, 74 | digestAlg, pkcs11PinArg); 75 | if (srv != SECSuccess) { 76 | return MapPRErrorCodeToResult(PR_GetError()); 77 | } 78 | 79 | return Success; 80 | } 81 | 82 | } // unnamed namespace 83 | 84 | Result 85 | VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd, 86 | Input subjectPublicKeyInfo, 87 | void* pkcs11PinArg) 88 | { 89 | return VerifySignedDigest(sd, subjectPublicKeyInfo, 90 | SEC_OID_PKCS1_RSA_ENCRYPTION, pkcs11PinArg); 91 | } 92 | 93 | Result 94 | VerifyECDSASignedDigestNSS(const SignedDigest& sd, 95 | Input subjectPublicKeyInfo, 96 | void* pkcs11PinArg) 97 | { 98 | return VerifySignedDigest(sd, subjectPublicKeyInfo, 99 | SEC_OID_ANSIX962_EC_PUBLIC_KEY, pkcs11PinArg); 100 | } 101 | 102 | Result 103 | DigestBufNSS(Input item, 104 | DigestAlgorithm digestAlg, 105 | /*out*/ uint8_t* digestBuf, 106 | size_t digestBufLen) 107 | { 108 | SECOidTag oid; 109 | size_t bits; 110 | switch (digestAlg) { 111 | case DigestAlgorithm::sha512: oid = SEC_OID_SHA512; bits = 512; break; 112 | case DigestAlgorithm::sha384: oid = SEC_OID_SHA384; bits = 384; break; 113 | case DigestAlgorithm::sha256: oid = SEC_OID_SHA256; bits = 256; break; 114 | case DigestAlgorithm::sha1: oid = SEC_OID_SHA1; bits = 160; break; 115 | MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM 116 | } 117 | if (digestBufLen != bits / 8) { 118 | return Result::FATAL_ERROR_INVALID_ARGS; 119 | } 120 | 121 | SECItem itemSECItem = UnsafeMapInputToSECItem(item); 122 | if (itemSECItem.len > 123 | static_cast( 124 | std::numeric_limits::max())) { 125 | PR_NOT_REACHED("large items should not be possible here"); 126 | return Result::FATAL_ERROR_INVALID_ARGS; 127 | } 128 | SECStatus srv = PK11_HashBuf(oid, digestBuf, itemSECItem.data, 129 | static_cast(itemSECItem.len)); 130 | if (srv != SECSuccess) { 131 | return MapPRErrorCodeToResult(PR_GetError()); 132 | } 133 | return Success; 134 | } 135 | 136 | Result 137 | MapPRErrorCodeToResult(PRErrorCode error) 138 | { 139 | switch (error) 140 | { 141 | #define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \ 142 | case nss_result: return Result::mozilla_pkix_result; 143 | 144 | MOZILLA_PKIX_MAP_LIST 145 | 146 | #undef MOZILLA_PKIX_MAP 147 | 148 | default: 149 | return Result::ERROR_UNKNOWN_ERROR; 150 | } 151 | } 152 | 153 | PRErrorCode 154 | MapResultToPRErrorCode(Result result) 155 | { 156 | switch (result) 157 | { 158 | #define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \ 159 | case Result::mozilla_pkix_result: return nss_result; 160 | 161 | MOZILLA_PKIX_MAP_LIST 162 | 163 | #undef MOZILLA_PKIX_MAP 164 | 165 | MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM 166 | } 167 | } 168 | 169 | void 170 | RegisterErrorTable() 171 | { 172 | // Note that these error strings are not localizable. 173 | // When these strings change, update the localization information too. 174 | static const PRErrorMessage ErrorTableText[] = { 175 | { "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE", 176 | "The server uses key pinning (HPKP) but no trusted certificate chain " 177 | "could be constructed that matches the pinset. Key pinning violations " 178 | "cannot be overridden." }, 179 | { "MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY", 180 | "The server uses a certificate with a basic constraints extension " 181 | "identifying it as a certificate authority. For a properly-issued " 182 | "certificate, this should not be the case." }, 183 | { "MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE", 184 | "The server presented a certificate with a key size that is too small " 185 | "to establish a secure connection." }, 186 | { "MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA", 187 | "An X.509 version 1 certificate that is not a trust anchor was used to " 188 | "issue the server's certificate. X.509 version 1 certificates are " 189 | "deprecated and should not be used to sign other certificates." }, 190 | { "MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH", 191 | "The certificate is not valid for the given email address." }, 192 | { "MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE", 193 | "The server presented a certificate that is not yet valid." }, 194 | { "MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE", 195 | "A certificate that is not yet valid was used to issue the server's " 196 | "certificate." }, 197 | { "MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH", 198 | "The signature algorithm in the signature field of the certificate does " 199 | "not match the algorithm in its signatureAlgorithm field." }, 200 | { "MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING", 201 | "The OCSP response does not include a status for the certificate being " 202 | "verified." }, 203 | { "MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG", 204 | "The server presented a certificate that is valid for too long." }, 205 | }; 206 | // Note that these error strings are not localizable. 207 | // When these strings change, update the localization information too. 208 | 209 | static const PRErrorTable ErrorTable = { 210 | ErrorTableText, 211 | "pkixerrors", 212 | ERROR_BASE, 213 | PR_ARRAY_SIZE(ErrorTableText) 214 | }; 215 | 216 | (void) PR_ErrorInstallTable(&ErrorTable); 217 | } 218 | 219 | } } // namespace mozilla::pkix 220 | -------------------------------------------------------------------------------- /test/lib/pkixtestalg.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2015 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixtestutil.h" 26 | 27 | #include "pkixder.h" 28 | 29 | // python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10040 1.2.840.10040 30 | #define PREFIX_1_2_840_10040 0x2a, 0x86, 0x48, 0xce, 0x38 31 | 32 | // python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10045 1.2.840.10045 33 | #define PREFIX_1_2_840_10045 0x2a, 0x86, 0x48, 0xce, 0x3d 34 | 35 | // python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_113549 1.2.840.113549 36 | #define PREFIX_1_2_840_113549 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d 37 | 38 | namespace mozilla { namespace pkix { namespace test { 39 | 40 | namespace { 41 | 42 | enum class NULLParam { NO, YES }; 43 | 44 | template 45 | ByteString 46 | OID(const uint8_t (&rawValue)[SIZE]) 47 | { 48 | return TLV(der::OIDTag, ByteString(rawValue, SIZE)); 49 | } 50 | 51 | template 52 | ByteString 53 | SimpleAlgID(const uint8_t (&rawValue)[SIZE], 54 | NULLParam nullParam = NULLParam::NO) 55 | { 56 | ByteString sequenceValue(OID(rawValue)); 57 | if (nullParam == NULLParam::YES) { 58 | sequenceValue.append(TLV(der::NULLTag, ByteString())); 59 | } 60 | return TLV(der::SEQUENCE, sequenceValue); 61 | } 62 | 63 | template 64 | ByteString 65 | DERInteger(const uint8_t (&rawValue)[SIZE]) 66 | { 67 | ByteString value(rawValue, SIZE); 68 | if (value[0] & 0x80u) { 69 | // Prefix with a leading zero to disambiguate this from a negative value. 70 | value.insert(value.begin(), 0x00); 71 | } 72 | return TLV(der::INTEGER, value); 73 | } 74 | 75 | // Generated with "openssl dsaparam -C -noout 2048" and reformatted. 76 | // openssl 1.0 or later must be used so that a 256-bit Q value is 77 | // generated. 78 | static const uint8_t DSS_P_RAW[] = 79 | { 80 | 0xB3,0xCD,0x29,0x44,0xF0,0x25,0xA7,0x73,0xFC,0x86,0x70,0xA2, 81 | 0x69,0x5A,0x97,0x3F,0xBD,0x1C,0x6F,0xAA,0x4A,0x40,0x42,0x8E, 82 | 0xCF,0xAE,0x62,0x12,0xED,0xB4,0xFD,0x05,0xC2,0xAE,0xB1,0x8C, 83 | 0xFC,0xBE,0x38,0x90,0xBB,0x7C,0xFF,0x16,0xF4,0xED,0xCE,0x72, 84 | 0x12,0x93,0x83,0xF0,0xA4,0xA1,0x71,0xDC,0x4B,0xF0,0x4E,0x3A, 85 | 0x2B,0xFA,0x17,0xB7,0xB3,0x2A,0xCC,0x2C,0xD3,0xC8,0x21,0x49, 86 | 0x7A,0x83,0x71,0x8B,0x3D,0x62,0x96,0xDC,0xAD,0xA8,0x03,0xBE, 87 | 0x1D,0x33,0x11,0xF3,0xEB,0xD8,0x1B,0x8D,0xDB,0x62,0x79,0x83, 88 | 0xF8,0x67,0x4E,0x62,0x21,0x2C,0x81,0x59,0xE8,0x73,0xD7,0xAF, 89 | 0xB9,0x63,0x60,0xEA,0xAE,0xEC,0x68,0x6A,0xB4,0xB0,0x65,0xBA, 90 | 0xA3,0x4C,0x09,0x99,0x29,0x6A,0x2E,0x2B,0xFC,0x6D,0x51,0xCA, 91 | 0x30,0xA2,0x2F,0x7A,0x65,0x76,0xA7,0x55,0x13,0x11,0xA0,0x02, 92 | 0xA2,0x59,0x4B,0xCE,0xA7,0x05,0xF6,0x07,0x35,0x9B,0x41,0xD7, 93 | 0x11,0x5A,0x18,0x57,0xA7,0x78,0x88,0xC3,0xA8,0xE3,0x39,0xF5, 94 | 0x47,0x3D,0x2E,0x18,0x54,0xB0,0xF0,0xBF,0x65,0x3F,0x77,0xC7, 95 | 0x11,0xB8,0x0D,0x52,0xAD,0xC8,0xE8,0x6D,0xF6,0x7E,0x88,0x65, 96 | 0x84,0x2B,0xF7,0xEF,0x8E,0xB5,0x7C,0xBD,0x2E,0x0D,0xF3,0xC6, 97 | 0xDD,0x0B,0xB4,0xF2,0x23,0x1F,0xDA,0x55,0x05,0xF5,0xDC,0x53, 98 | 0xA6,0x83,0xDA,0x5C,0xEF,0x29,0x02,0x78,0x68,0xD0,0xA4,0x39, 99 | 0x09,0x7F,0xFA,0x49,0x18,0xD0,0xB5,0x19,0x35,0x31,0x8E,0xDE, 100 | 0x43,0x35,0xA3,0xB9,0x6D,0xC1,0x70,0xC6,0x0D,0x18,0x24,0xEB, 101 | 0x1E,0x4D,0x52,0xB7, 102 | }; 103 | 104 | static const uint8_t DSS_Q_RAW[] = 105 | { 106 | 0x8D,0x6B,0x86,0x89,0x9C,0x8D,0x30,0x91,0xCC,0x6E,0x34,0xF1, 107 | 0xE8,0x9C,0x8A,0x5C,0xD6,0xAB,0x01,0x1E,0xC4,0xDB,0xFD,0x07, 108 | 0xEB,0x5F,0x4E,0xE8,0xFA,0xFC,0x98,0x2D, 109 | }; 110 | 111 | static const uint8_t DSS_G_RAW[] = 112 | { 113 | 0x0E,0x2C,0x34,0xB2,0xE1,0x66,0x49,0xB6,0x9A,0x7D,0x67,0x3E, 114 | 0xEE,0x98,0x35,0x18,0x28,0x35,0xFC,0x05,0x36,0x3B,0x94,0xE6, 115 | 0x1E,0x1C,0x5B,0x05,0x3E,0x86,0x1B,0xE3,0xED,0xD2,0xE1,0xF3, 116 | 0xF7,0xF7,0x60,0x6D,0x7D,0xA1,0xAF,0x9A,0xD1,0xDF,0xA2,0x9C, 117 | 0xFC,0xA2,0xEB,0x90,0x8B,0x1C,0x82,0x92,0x45,0x7B,0x30,0x2A, 118 | 0xFD,0x7A,0xE6,0x68,0x8F,0xEC,0x89,0x3A,0x9A,0xAD,0xFE,0x25, 119 | 0x5E,0x51,0xC5,0x29,0x45,0x7F,0xAC,0xDE,0xFC,0xB4,0x1B,0x3A, 120 | 0xDA,0xC7,0x21,0x68,0x87,0x27,0x8D,0x7B,0xB2,0xBB,0x41,0x60, 121 | 0x46,0x42,0x5B,0x6B,0xE8,0x80,0xD2,0xE4,0xA3,0x30,0x8F,0xD5, 122 | 0x71,0x07,0x8A,0x7B,0x32,0x56,0x84,0x41,0x1C,0xDF,0x69,0xE9, 123 | 0xFD,0xBA,0x48,0xE0,0x43,0xA0,0x38,0x92,0x12,0xF3,0x52,0xA5, 124 | 0x40,0x87,0xCB,0x34,0xBB,0x3E,0x25,0x29,0x3C,0xC6,0xA5,0x17, 125 | 0xFD,0x58,0x47,0x89,0xDB,0x9B,0xB9,0xCF,0xE9,0xA8,0xF2,0xEC, 126 | 0x55,0x76,0xF5,0xF1,0x9C,0x6E,0x0A,0x3F,0x16,0x5F,0x49,0x31, 127 | 0x31,0x1C,0x43,0xA2,0x83,0xDA,0xDD,0x7F,0x1C,0xEA,0x05,0x36, 128 | 0x7B,0xED,0x09,0xFB,0x6F,0x8A,0x2B,0x55,0xB9,0xBC,0x4A,0x8C, 129 | 0x28,0xC1,0x4D,0x13,0x6E,0x47,0xF4,0xAD,0x79,0x00,0xE9,0x5A, 130 | 0xB6,0xC7,0x73,0x28,0xA9,0x89,0xAD,0xE8,0x6E,0xC6,0x54,0xA5, 131 | 0x56,0x2D,0xAA,0x81,0x83,0x9E,0xC1,0x13,0x79,0xA4,0x12,0xE0, 132 | 0x76,0x1F,0x25,0x43,0xB6,0xDE,0x56,0xF7,0x52,0xCC,0x07,0xB8, 133 | 0x37,0xE2,0x8C,0xC5,0x56,0x8C,0xDD,0x63,0xF5,0xB6,0xA3,0x46, 134 | 0x62,0xF6,0x35,0x76, 135 | }; 136 | 137 | } // unnamed namespace 138 | 139 | TestSignatureAlgorithm::TestSignatureAlgorithm( 140 | const TestPublicKeyAlgorithm& publicKeyAlg, 141 | TestDigestAlgorithmID digestAlg, 142 | const ByteString& algorithmIdentifier, 143 | bool accepted) 144 | : publicKeyAlg(publicKeyAlg) 145 | , digestAlg(digestAlg) 146 | , algorithmIdentifier(algorithmIdentifier) 147 | , accepted(accepted) 148 | { 149 | } 150 | 151 | ByteString DSS_P() { return ByteString(DSS_P_RAW, sizeof(DSS_P_RAW)); } 152 | ByteString DSS_Q() { return ByteString(DSS_Q_RAW, sizeof(DSS_Q_RAW)); } 153 | ByteString DSS_G() { return ByteString(DSS_G_RAW, sizeof(DSS_G_RAW)); } 154 | 155 | TestPublicKeyAlgorithm 156 | DSS() 157 | { 158 | static const uint8_t oidValue[] = { PREFIX_1_2_840_10040, 4, 1 }; 159 | 160 | // RFC 3279 Section-2.3.2 161 | return TestPublicKeyAlgorithm( 162 | TLV(der::SEQUENCE, 163 | OID(oidValue) + 164 | TLV(der::SEQUENCE, 165 | DERInteger(DSS_P_RAW) + 166 | DERInteger(DSS_Q_RAW) + 167 | DERInteger(DSS_G_RAW)))); 168 | } 169 | 170 | // RFC 3279 Section 2.3.1 171 | TestPublicKeyAlgorithm 172 | RSA_PKCS1() 173 | { 174 | static const uint8_t rsaEncryption[] = { PREFIX_1_2_840_113549, 1, 1, 1 }; 175 | return TestPublicKeyAlgorithm(SimpleAlgID(rsaEncryption, NULLParam::YES)); 176 | } 177 | 178 | // RFC 3279 Section 2.2.1 179 | TestSignatureAlgorithm md2WithRSAEncryption() 180 | { 181 | static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 2 }; 182 | return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::MD2, 183 | SimpleAlgID(oidValue), false); 184 | } 185 | 186 | // RFC 3279 Section 2.2.1 187 | TestSignatureAlgorithm md5WithRSAEncryption() 188 | { 189 | static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 4 }; 190 | return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::MD5, 191 | SimpleAlgID(oidValue), false); 192 | } 193 | 194 | // RFC 3279 Section 2.2.1 195 | TestSignatureAlgorithm sha1WithRSAEncryption() 196 | { 197 | static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 5 }; 198 | return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::SHA1, 199 | SimpleAlgID(oidValue), true); 200 | } 201 | 202 | // RFC 4055 Section 5 203 | TestSignatureAlgorithm sha256WithRSAEncryption() 204 | { 205 | static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 11 }; 206 | return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::SHA256, 207 | SimpleAlgID(oidValue), true); 208 | } 209 | 210 | } } } // namespace mozilla::pkix 211 | -------------------------------------------------------------------------------- /tools/DottedOIDToCode.py: -------------------------------------------------------------------------------- 1 | # This code is made available to you under your choice of the following sets 2 | # of licensing terms: 3 | ############################################################################### 4 | # This Source Code Form is subject to the terms of the Mozilla Public 5 | # License, v. 2.0. If a copy of the MPL was not distributed with this 6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 | ############################################################################### 8 | # Copyright 2013 Mozilla Contributors 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | 22 | from __future__ import print_function 23 | import argparse 24 | import itertools 25 | import sys 26 | 27 | def base128(value): 28 | """ 29 | Given an integral value, returns an array of the base-128 representation 30 | of that value, with all but the last byte having the high bit set as 31 | required by the DER rules for the nodes of an OID after the first two 32 | bytes. 33 | 34 | >>> base128(1) 35 | [1] 36 | >>> base128(10045) 37 | [206, 61] 38 | """ 39 | 40 | if value < 0: 41 | raise ValueError("An OID must have only positive-value nodes.") 42 | 43 | # least significant byte has highest bit unset 44 | result = [value % 0x80] 45 | value /= 0x80 46 | 47 | while value != 0: 48 | result = [0x80 | (value % 0x80)] + result 49 | value /= 0x80 50 | 51 | return result 52 | 53 | def dottedOIDToEncodedArray(dottedOID): 54 | """ 55 | Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and 56 | returns an array that contains the DER encoding of its value, without 57 | the tag and length (e.g. [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04]). 58 | """ 59 | nodes = [int(x) for x in dottedOID.strip().split(".")] 60 | if len(nodes) < 2: 61 | raise ValueError("An OID must have at least two nodes.") 62 | if not (0 <= nodes[0] <= 2): 63 | raise ValueError("The first node of an OID must be 0, 1, or 2.") 64 | if not (0 <= nodes[1] <= 39): 65 | # XXX: Does this restriction apply when the first part is 2? 66 | raise ValueError("The second node of an OID must be 0-39.") 67 | firstByte = (40 * nodes[0]) + nodes[1] 68 | restBase128 = [base128(x) for x in nodes[2:]] 69 | return [firstByte] + list(itertools.chain.from_iterable(restBase128)) 70 | 71 | def dottedOIDToCArray(dottedOID, mode): 72 | """ 73 | Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and 74 | returns a string that contains the hex encoding of the OID in C++ literal 75 | notation, e.g. '0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04'. 76 | """ 77 | bytes = dottedOIDToEncodedArray(dottedOID) 78 | 79 | if mode != "value" and mode != "prefixdefine": 80 | bytes = [0x06, len(bytes)] + bytes 81 | 82 | if mode == "alg": 83 | # Wrap the DER-encoded OID in a SEQUENCE to create an 84 | # AlgorithmIdentifier with no parameters. 85 | bytes = [0x30, len(bytes)] + bytes 86 | 87 | return ", ".join(["0x%.2x" % b for b in bytes]) 88 | 89 | def specNameToCName(specName): 90 | """ 91 | Given an string containing an ASN.1 name, returns a string that is a valid 92 | C++ identifier that is as similar to that name as possible. Since most 93 | ASN.1 identifiers used in PKIX specifications are legal C++ names except 94 | for containing hyphens, this function just converts the hyphens to 95 | underscores. This may need to be improved in the future if we encounter 96 | names with other funny characters. 97 | """ 98 | return specName.replace("-", "_") 99 | 100 | def toCode(programName, specName, dottedOID, mode): 101 | """ 102 | Given an ASN.1 name and a string containing the dotted representation of an 103 | OID, returns a string that contains a C++ declaration for a named constant 104 | that contains that OID value. If mode is "value" then only the value of 105 | the OID (without the tag or length) will be included in the output. If mode 106 | is "tlv" then the value will be prefixed with the tag and length. If mode 107 | is "alg" then the value will be a complete der-encoded AlgorithmIdentifier 108 | with no parameters. 109 | 110 | This: 111 | 112 | toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4", 113 | "value") 114 | 115 | would result in a string like: 116 | 117 | // python DottedOIDToCode.py ecdsa-with-SHA512 1.2.840.10045.4.3.4 118 | static const uint8_t ecdsa_with_SHA512[] = { 119 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 120 | }; 121 | 122 | This: 123 | 124 | toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4", 125 | "tlv") 126 | 127 | would result in a string like: 128 | 129 | // python DottedOIDToCode.py --tlv ecdsa-with-SHA512 1.2.840.10045.4.3.4 130 | static const uint8_t tlv_ecdsa_with_SHA512[] = { 131 | 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 132 | }; 133 | 134 | This: 135 | 136 | toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4", 137 | "alg") 138 | 139 | would result in a string like: 140 | 141 | // python DottedOIDToCode.py --alg ecdsa-with-SHA512 1.2.840.10045.4.3.4 142 | static const uint8_t alg_ecdsa_with_SHA512[] = { 143 | 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 144 | }; 145 | 146 | This: 147 | 148 | toCode("DottedOIDToCode.py", "PREFIX_1_2_840_10045", "1.2.840.10045", 149 | "prefixdefine") 150 | 151 | would result in a string like this (note the lack of indention): 152 | 153 | // python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10045 1.2.840.10045 154 | #define PREFIX_1_2_840_10045 0x2a, 0x86, 0x48, 0xce, 0x3d 155 | """ 156 | programNameWithOptions = programName 157 | 158 | if mode == "prefixdefine": 159 | programNameWithOptions += " --prefixdefine" 160 | varName = specName 161 | return ("// python %s %s %s\n" + 162 | "#define %s %s\n") % (programNameWithOptions, specName, 163 | dottedOID, varName, 164 | dottedOIDToCArray(dottedOID, mode)) 165 | 166 | varName = specNameToCName(specName) 167 | if mode == "tlv": 168 | programNameWithOptions += " --tlv" 169 | varName = "tlv_" + varName 170 | elif mode == "alg": 171 | programNameWithOptions += " --alg" 172 | varName = "alg_" + varName 173 | elif mode == "prefixdefine": 174 | programNameWithOptions += " --alg" 175 | varName = varName 176 | 177 | return (" // python %s %s %s\n" + 178 | " static const uint8_t %s[] = {\n" + 179 | " %s\n" + 180 | " };\n") % (programNameWithOptions, specName, dottedOID, varName, 181 | dottedOIDToCArray(dottedOID, mode)) 182 | 183 | if __name__ == "__main__": 184 | parser = argparse.ArgumentParser( 185 | description="Generate code snippets to handle OIDs in C++", 186 | epilog="example: python %s ecdsa-with-SHA1 1.2.840.10045.4.1" 187 | % sys.argv[0]) 188 | group = parser.add_mutually_exclusive_group() 189 | group.add_argument("--tlv", action='store_true', 190 | help="Wrap the encoded OID value with the tag and length") 191 | group.add_argument("--alg", action='store_true', 192 | help="Wrap the encoded OID value in an encoded SignatureAlgorithm") 193 | group.add_argument("--prefixdefine", action='store_true', 194 | help="generate a OID prefix #define") 195 | parser.add_argument("name", 196 | help="The name given to the OID in the specification") 197 | parser.add_argument("dottedOID", metavar="dotted-oid", 198 | help="The OID value, in dotted notation") 199 | 200 | args = parser.parse_args() 201 | if args.alg: 202 | mode = 'alg' 203 | elif args.tlv: 204 | mode = 'tlv' 205 | elif args.prefixdefine: 206 | mode = 'prefixdefine' 207 | else: 208 | mode = 'value' 209 | 210 | print(toCode(sys.argv[0], args.name, args.dottedOID, mode)) 211 | -------------------------------------------------------------------------------- /test/gtest/pkixcert_signature_algorithm_tests.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* Any copyright is dedicated to the Public Domain. 4 | * http://creativecommons.org/publicdomain/zero/1.0/ */ 5 | 6 | #include "pkixder.h" 7 | #include "pkixgtest.h" 8 | 9 | using namespace mozilla::pkix; 10 | using namespace mozilla::pkix::test; 11 | 12 | static ByteString 13 | CreateCert(const char* issuerCN, 14 | const char* subjectCN, 15 | EndEntityOrCA endEntityOrCA, 16 | const TestSignatureAlgorithm& signatureAlgorithm, 17 | /*out*/ ByteString& subjectDER) 18 | { 19 | static long serialNumberValue = 0; 20 | ++serialNumberValue; 21 | ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue)); 22 | EXPECT_FALSE(ENCODING_FAILED(serialNumber)); 23 | 24 | ByteString issuerDER(CNToDERName(issuerCN)); 25 | EXPECT_FALSE(ENCODING_FAILED(issuerDER)); 26 | subjectDER = CNToDERName(subjectCN); 27 | EXPECT_FALSE(ENCODING_FAILED(subjectDER)); 28 | 29 | ByteString extensions[2]; 30 | if (endEntityOrCA == EndEntityOrCA::MustBeCA) { 31 | extensions[0] = 32 | CreateEncodedBasicConstraints(true, nullptr, Critical::Yes); 33 | EXPECT_FALSE(ENCODING_FAILED(extensions[0])); 34 | } 35 | 36 | ScopedTestKeyPair reusedKey(CloneReusedKeyPair()); 37 | ByteString certDER(CreateEncodedCertificate(v3, signatureAlgorithm, 38 | serialNumber, issuerDER, 39 | oneDayBeforeNow, oneDayAfterNow, 40 | subjectDER, *reusedKey, 41 | extensions, *reusedKey, 42 | signatureAlgorithm)); 43 | EXPECT_FALSE(ENCODING_FAILED(certDER)); 44 | return certDER; 45 | } 46 | 47 | class AlgorithmTestsTrustDomain final : public DefaultCryptoTrustDomain 48 | { 49 | public: 50 | AlgorithmTestsTrustDomain(const ByteString& rootDER, 51 | const ByteString& rootSubjectDER, 52 | /*optional*/ const ByteString& intDER, 53 | /*optional*/ const ByteString& intSubjectDER) 54 | : rootDER(rootDER) 55 | , rootSubjectDER(rootSubjectDER) 56 | , intDER(intDER) 57 | , intSubjectDER(intSubjectDER) 58 | { 59 | } 60 | 61 | private: 62 | Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert, 63 | /*out*/ TrustLevel& trustLevel) override 64 | { 65 | if (InputEqualsByteString(candidateCert, rootDER)) { 66 | trustLevel = TrustLevel::TrustAnchor; 67 | } else { 68 | trustLevel = TrustLevel::InheritsTrust; 69 | } 70 | return Success; 71 | } 72 | 73 | Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time) 74 | override 75 | { 76 | ByteString* issuerDER = nullptr; 77 | if (InputEqualsByteString(encodedIssuerName, rootSubjectDER)) { 78 | issuerDER = &rootDER; 79 | } else if (InputEqualsByteString(encodedIssuerName, intSubjectDER)) { 80 | issuerDER = &intDER; 81 | } else { 82 | // FindIssuer just returns success if it can't find a potential issuer. 83 | return Success; 84 | } 85 | Input issuerCert; 86 | Result rv = issuerCert.Init(issuerDER->data(), issuerDER->length()); 87 | if (rv != Success) { 88 | return rv; 89 | } 90 | bool keepGoing; 91 | return checker.Check(issuerCert, nullptr, keepGoing); 92 | } 93 | 94 | Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, 95 | const Input*, const Input*) override 96 | { 97 | return Success; 98 | } 99 | 100 | Result IsChainValid(const DERArray&, Time) override 101 | { 102 | return Success; 103 | } 104 | 105 | ByteString rootDER; 106 | ByteString rootSubjectDER; 107 | ByteString intDER; 108 | ByteString intSubjectDER; 109 | }; 110 | 111 | static const TestSignatureAlgorithm NO_INTERMEDIATE 112 | { 113 | TestPublicKeyAlgorithm(ByteString()), 114 | TestDigestAlgorithmID::MD2, 115 | ByteString(), 116 | false 117 | }; 118 | 119 | struct ChainValidity final 120 | { 121 | ChainValidity(const TestSignatureAlgorithm& endEntitySignatureAlgorithm, 122 | const TestSignatureAlgorithm& optionalIntSignatureAlgorithm, 123 | const TestSignatureAlgorithm& rootSignatureAlgorithm, 124 | bool isValid) 125 | : endEntitySignatureAlgorithm(endEntitySignatureAlgorithm) 126 | , optionalIntermediateSignatureAlgorithm(optionalIntSignatureAlgorithm) 127 | , rootSignatureAlgorithm(rootSignatureAlgorithm) 128 | , isValid(isValid) 129 | { } 130 | 131 | // In general, a certificate is generated for each of these. However, if 132 | // optionalIntermediateSignatureAlgorithm is NO_INTERMEDIATE, then only 2 133 | // certificates are generated. 134 | // The certificate generated for the given rootSignatureAlgorithm is the 135 | // trust anchor. 136 | TestSignatureAlgorithm endEntitySignatureAlgorithm; 137 | TestSignatureAlgorithm optionalIntermediateSignatureAlgorithm; 138 | TestSignatureAlgorithm rootSignatureAlgorithm; 139 | bool isValid; 140 | }; 141 | 142 | static const ChainValidity CHAIN_VALIDITY[] = 143 | { 144 | // The trust anchor may have a signature with an unsupported signature 145 | // algorithm. 146 | ChainValidity(sha256WithRSAEncryption(), 147 | NO_INTERMEDIATE, 148 | md5WithRSAEncryption(), 149 | true), 150 | ChainValidity(sha256WithRSAEncryption(), 151 | NO_INTERMEDIATE, 152 | md2WithRSAEncryption(), 153 | true), 154 | 155 | // Certificates that are not trust anchors must not have a signature with an 156 | // unsupported signature algorithm. 157 | ChainValidity(md5WithRSAEncryption(), 158 | NO_INTERMEDIATE, 159 | sha256WithRSAEncryption(), 160 | false), 161 | ChainValidity(md2WithRSAEncryption(), 162 | NO_INTERMEDIATE, 163 | sha256WithRSAEncryption(), 164 | false), 165 | ChainValidity(md2WithRSAEncryption(), 166 | NO_INTERMEDIATE, 167 | md5WithRSAEncryption(), 168 | false), 169 | ChainValidity(sha256WithRSAEncryption(), 170 | md5WithRSAEncryption(), 171 | sha256WithRSAEncryption(), 172 | false), 173 | ChainValidity(sha256WithRSAEncryption(), 174 | md2WithRSAEncryption(), 175 | sha256WithRSAEncryption(), 176 | false), 177 | ChainValidity(sha256WithRSAEncryption(), 178 | md2WithRSAEncryption(), 179 | md5WithRSAEncryption(), 180 | false), 181 | }; 182 | 183 | class pkixcert_IsValidChainForAlgorithm 184 | : public ::testing::Test 185 | , public ::testing::WithParamInterface 186 | { 187 | }; 188 | 189 | TEST_P(pkixcert_IsValidChainForAlgorithm, IsValidChainForAlgorithm) 190 | { 191 | const ChainValidity& chainValidity(GetParam()); 192 | const char* rootCN = "CN=Root"; 193 | ByteString rootSubjectDER; 194 | ByteString rootEncoded( 195 | CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA, 196 | chainValidity.rootSignatureAlgorithm, rootSubjectDER)); 197 | EXPECT_FALSE(ENCODING_FAILED(rootEncoded)); 198 | EXPECT_FALSE(ENCODING_FAILED(rootSubjectDER)); 199 | 200 | const char* issuerCN = rootCN; 201 | 202 | const char* intermediateCN = "CN=Intermediate"; 203 | ByteString intermediateSubjectDER; 204 | ByteString intermediateEncoded; 205 | 206 | // If the the algorithmIdentifier is empty, then it's NO_INTERMEDIATE. 207 | if (!chainValidity.optionalIntermediateSignatureAlgorithm 208 | .algorithmIdentifier.empty()) { 209 | intermediateEncoded = 210 | CreateCert(rootCN, intermediateCN, EndEntityOrCA::MustBeCA, 211 | chainValidity.optionalIntermediateSignatureAlgorithm, 212 | intermediateSubjectDER); 213 | EXPECT_FALSE(ENCODING_FAILED(intermediateEncoded)); 214 | EXPECT_FALSE(ENCODING_FAILED(intermediateSubjectDER)); 215 | issuerCN = intermediateCN; 216 | } 217 | 218 | AlgorithmTestsTrustDomain trustDomain(rootEncoded, rootSubjectDER, 219 | intermediateEncoded, 220 | intermediateSubjectDER); 221 | 222 | const char* endEntityCN = "CN=End Entity"; 223 | ByteString endEntitySubjectDER; 224 | ByteString endEntityEncoded( 225 | CreateCert(issuerCN, endEntityCN, EndEntityOrCA::MustBeEndEntity, 226 | chainValidity.endEntitySignatureAlgorithm, 227 | endEntitySubjectDER)); 228 | EXPECT_FALSE(ENCODING_FAILED(endEntityEncoded)); 229 | EXPECT_FALSE(ENCODING_FAILED(endEntitySubjectDER)); 230 | 231 | Input endEntity; 232 | ASSERT_EQ(Success, endEntity.Init(endEntityEncoded.data(), 233 | endEntityEncoded.length())); 234 | Result expectedResult = chainValidity.isValid 235 | ? Success 236 | : Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; 237 | ASSERT_EQ(expectedResult, 238 | BuildCertChain(trustDomain, endEntity, Now(), 239 | EndEntityOrCA::MustBeEndEntity, 240 | KeyUsage::noParticularKeyUsageRequired, 241 | KeyPurposeId::id_kp_serverAuth, 242 | CertPolicyId::anyPolicy, nullptr)); 243 | } 244 | 245 | INSTANTIATE_TEST_CASE_P(pkixcert_IsValidChainForAlgorithm, 246 | pkixcert_IsValidChainForAlgorithm, 247 | testing::ValuesIn(CHAIN_VALIDITY)); 248 | -------------------------------------------------------------------------------- /lib/pkixutil.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef mozilla_pkix_pkixutil_h 26 | #define mozilla_pkix_pkixutil_h 27 | 28 | #include "pkixder.h" 29 | 30 | namespace mozilla { namespace pkix { 31 | 32 | // During path building and verification, we build a linked list of BackCerts 33 | // from the current cert toward the end-entity certificate. The linked list 34 | // is used to verify properties that aren't local to the current certificate 35 | // and/or the direct link between the current certificate and its issuer, 36 | // such as name constraints. 37 | // 38 | // Each BackCert contains pointers to all the given certificate's extensions 39 | // so that we can parse the extension block once and then process the 40 | // extensions in an order that may be different than they appear in the cert. 41 | class BackCert final 42 | { 43 | public: 44 | // certDER and childCert must be valid for the lifetime of BackCert. 45 | BackCert(Input certDER, EndEntityOrCA endEntityOrCA, 46 | const BackCert* childCert) 47 | : der(certDER) 48 | , endEntityOrCA(endEntityOrCA) 49 | , childCert(childCert) 50 | { 51 | } 52 | 53 | Result Init(); 54 | 55 | const Input GetDER() const { return der; } 56 | const der::SignedDataWithSignature& GetSignedData() const { 57 | return signedData; 58 | } 59 | 60 | der::Version GetVersion() const { return version; } 61 | const Input GetSerialNumber() const { return serialNumber; } 62 | const Input GetSignature() const { return signature; } 63 | const Input GetIssuer() const { return issuer; } 64 | // XXX: "validity" is a horrible name for the structure that holds 65 | // notBefore & notAfter, but that is the name used in RFC 5280 and we use the 66 | // RFC 5280 names for everything. 67 | const Input GetValidity() const { return validity; } 68 | const Input GetSubject() const { return subject; } 69 | const Input GetSubjectPublicKeyInfo() const 70 | { 71 | return subjectPublicKeyInfo; 72 | } 73 | const Input* GetAuthorityInfoAccess() const 74 | { 75 | return MaybeInput(authorityInfoAccess); 76 | } 77 | const Input* GetBasicConstraints() const 78 | { 79 | return MaybeInput(basicConstraints); 80 | } 81 | const Input* GetCertificatePolicies() const 82 | { 83 | return MaybeInput(certificatePolicies); 84 | } 85 | const Input* GetExtKeyUsage() const 86 | { 87 | return MaybeInput(extKeyUsage); 88 | } 89 | const Input* GetKeyUsage() const 90 | { 91 | return MaybeInput(keyUsage); 92 | } 93 | const Input* GetInhibitAnyPolicy() const 94 | { 95 | return MaybeInput(inhibitAnyPolicy); 96 | } 97 | const Input* GetNameConstraints() const 98 | { 99 | return MaybeInput(nameConstraints); 100 | } 101 | const Input* GetSubjectAltName() const 102 | { 103 | return MaybeInput(subjectAltName); 104 | } 105 | 106 | private: 107 | const Input der; 108 | 109 | public: 110 | const EndEntityOrCA endEntityOrCA; 111 | BackCert const* const childCert; 112 | 113 | private: 114 | // When parsing certificates in BackCert::Init, we don't accept empty 115 | // extensions. Consequently, we don't have to store a distinction between 116 | // empty extensions and extensions that weren't included. However, when 117 | // *processing* extensions, we distinguish between whether an extension was 118 | // included or not based on whetehr the GetXXX function for the extension 119 | // returns nullptr. 120 | static inline const Input* MaybeInput(const Input& item) 121 | { 122 | return item.GetLength() > 0 ? &item : nullptr; 123 | } 124 | 125 | der::SignedDataWithSignature signedData; 126 | 127 | der::Version version; 128 | Input serialNumber; 129 | Input signature; 130 | Input issuer; 131 | // XXX: "validity" is a horrible name for the structure that holds 132 | // notBefore & notAfter, but that is the name used in RFC 5280 and we use the 133 | // RFC 5280 names for everything. 134 | Input validity; 135 | Input subject; 136 | Input subjectPublicKeyInfo; 137 | 138 | Input authorityInfoAccess; 139 | Input basicConstraints; 140 | Input certificatePolicies; 141 | Input extKeyUsage; 142 | Input inhibitAnyPolicy; 143 | Input keyUsage; 144 | Input nameConstraints; 145 | Input subjectAltName; 146 | Input criticalNetscapeCertificateType; 147 | 148 | Result RememberExtension(Reader& extnID, Input extnValue, bool critical, 149 | /*out*/ bool& understood); 150 | 151 | BackCert(const BackCert&) = delete; 152 | void operator=(const BackCert&) = delete; 153 | }; 154 | 155 | class NonOwningDERArray final : public DERArray 156 | { 157 | public: 158 | NonOwningDERArray() 159 | : numItems(0) 160 | { 161 | // we don't need to initialize the items array because we always check 162 | // numItems before accessing i. 163 | } 164 | 165 | size_t GetLength() const override { return numItems; } 166 | 167 | const Input* GetDER(size_t i) const override 168 | { 169 | return i < numItems ? &items[i] : nullptr; 170 | } 171 | 172 | Result Append(Input der) 173 | { 174 | if (numItems >= MAX_LENGTH) { 175 | return Result::FATAL_ERROR_INVALID_ARGS; 176 | } 177 | Result rv = items[numItems].Init(der); // structure assignment 178 | if (rv != Success) { 179 | return rv; 180 | } 181 | ++numItems; 182 | return Success; 183 | } 184 | 185 | // Public so we can static_assert on this. Keep in sync with MAX_SUBCA_COUNT. 186 | static const size_t MAX_LENGTH = 8; 187 | private: 188 | Input items[MAX_LENGTH]; // avoids any heap allocations 189 | size_t numItems; 190 | 191 | NonOwningDERArray(const NonOwningDERArray&) = delete; 192 | void operator=(const NonOwningDERArray&) = delete; 193 | }; 194 | 195 | inline unsigned int 196 | DaysBeforeYear(unsigned int year) 197 | { 198 | assert(year <= 9999); 199 | return ((year - 1u) * 365u) 200 | + ((year - 1u) / 4u) // leap years are every 4 years, 201 | - ((year - 1u) / 100u) // except years divisible by 100, 202 | + ((year - 1u) / 400u); // except years divisible by 400. 203 | } 204 | 205 | static const size_t MAX_DIGEST_SIZE_IN_BYTES = 512 / 8; // sha-512 206 | 207 | Result DigestSignedData(TrustDomain& trustDomain, 208 | const der::SignedDataWithSignature& signedData, 209 | /*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES], 210 | /*out*/ der::PublicKeyAlgorithm& publicKeyAlg, 211 | /*out*/ SignedDigest& signedDigest); 212 | 213 | Result VerifySignedDigest(TrustDomain& trustDomain, 214 | der::PublicKeyAlgorithm publicKeyAlg, 215 | const SignedDigest& signedDigest, 216 | Input signerSubjectPublicKeyInfo); 217 | 218 | // Combines DigestSignedData and VerifySignedDigest 219 | Result VerifySignedData(TrustDomain& trustDomain, 220 | const der::SignedDataWithSignature& signedData, 221 | Input signerSubjectPublicKeyInfo); 222 | 223 | // In a switch over an enum, sometimes some compilers are not satisfied that 224 | // all control flow paths have been considered unless there is a default case. 225 | // However, in our code, such a default case is almost always unreachable dead 226 | // code. That can be particularly problematic when the compiler wants the code 227 | // to choose a value, such as a return value, for the default case, but there's 228 | // no appropriate "impossible case" value to choose. 229 | // 230 | // MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM accounts for this. Example: 231 | // 232 | // // In xy.cpp 233 | // #include "xt.h" 234 | // 235 | // enum class XY { X, Y }; 236 | // 237 | // int func(XY xy) { 238 | // switch (xy) { 239 | // case XY::X: return 1; 240 | // case XY::Y; return 2; 241 | // MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM 242 | // } 243 | // } 244 | #if defined(__clang__) 245 | // Clang will warn if not all cases are covered (-Wswitch-enum) AND it will 246 | // warn if a switch statement that covers every enum label has a default case 247 | // (-W-covered-switch-default). Versions prior to 3.5 warned about unreachable 248 | // code in such default cases (-Wunreachable-code) even when 249 | // -W-covered-switch-default was disabled, but that changed in Clang 3.5. 250 | #define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM // empty 251 | #elif defined(__GNUC__) 252 | // GCC will warn if not all cases are covered (-Wswitch-enum). It does not 253 | // assume that the default case is unreachable. 254 | #define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \ 255 | default: assert(false); __builtin_unreachable(); 256 | #elif defined(_MSC_VER) 257 | // MSVC will warn if not all cases are covered (C4061, level 4). It does not 258 | // assume that the default case is unreachable. 259 | #define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \ 260 | default: assert(false); __assume(0); 261 | #else 262 | #error Unsupported compiler for MOZILLA_PKIX_UNREACHABLE_DEFAULT. 263 | #endif 264 | 265 | } } // namespace mozilla::pkix 266 | 267 | #endif // mozilla_pkix_pkixutil_h 268 | -------------------------------------------------------------------------------- /test/lib/pkixtestnss.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixtestutil.h" 26 | 27 | #include 28 | 29 | #include "cryptohi.h" 30 | #include "keyhi.h" 31 | #include "nss.h" 32 | #include "pk11pqg.h" 33 | #include "pk11pub.h" 34 | #include "pkix/pkixnss.h" 35 | #include "pkixder.h" 36 | #include "pkixutil.h" 37 | #include "prinit.h" 38 | #include "secerr.h" 39 | #include "secitem.h" 40 | 41 | namespace mozilla { namespace pkix { namespace test { 42 | 43 | namespace { 44 | 45 | typedef ScopedPtr 46 | ScopedSECKEYPublicKey; 47 | typedef ScopedPtr 48 | ScopedSECKEYPrivateKey; 49 | 50 | inline void 51 | SECITEM_FreeItem_true(SECItem* item) 52 | { 53 | SECITEM_FreeItem(item, true); 54 | } 55 | 56 | typedef mozilla::pkix::ScopedPtr ScopedSECItem; 57 | 58 | TestKeyPair* GenerateKeyPairInner(); 59 | 60 | void 61 | InitNSSIfNeeded() 62 | { 63 | if (NSS_NoDB_Init(nullptr) != SECSuccess) { 64 | abort(); 65 | } 66 | } 67 | 68 | static ScopedTestKeyPair reusedKeyPair; 69 | 70 | PRStatus 71 | InitReusedKeyPair() 72 | { 73 | InitNSSIfNeeded(); 74 | reusedKeyPair.reset(GenerateKeyPairInner()); 75 | return reusedKeyPair ? PR_SUCCESS : PR_FAILURE; 76 | } 77 | 78 | class NSSTestKeyPair final : public TestKeyPair 79 | { 80 | public: 81 | // NSSTestKeyPair takes ownership of privateKey. 82 | NSSTestKeyPair(const TestPublicKeyAlgorithm& publicKeyAlg, 83 | const ByteString& spk, 84 | SECKEYPrivateKey* privateKey) 85 | : TestKeyPair(publicKeyAlg, spk) 86 | , privateKey(privateKey) 87 | { 88 | } 89 | 90 | Result SignData(const ByteString& tbs, 91 | const TestSignatureAlgorithm& signatureAlgorithm, 92 | /*out*/ ByteString& signature) const override 93 | { 94 | SECOidTag oidTag; 95 | if (signatureAlgorithm.publicKeyAlg == RSA_PKCS1()) { 96 | switch (signatureAlgorithm.digestAlg) { 97 | case TestDigestAlgorithmID::MD2: 98 | oidTag = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION; 99 | break; 100 | case TestDigestAlgorithmID::MD5: 101 | oidTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; 102 | break; 103 | case TestDigestAlgorithmID::SHA1: 104 | oidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; 105 | break; 106 | case TestDigestAlgorithmID::SHA224: 107 | oidTag = SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION; 108 | break; 109 | case TestDigestAlgorithmID::SHA256: 110 | oidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; 111 | break; 112 | case TestDigestAlgorithmID::SHA384: 113 | oidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; 114 | break; 115 | case TestDigestAlgorithmID::SHA512: 116 | oidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; 117 | break; 118 | MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM 119 | } 120 | } else { 121 | abort(); 122 | } 123 | 124 | SECItem signatureItem; 125 | if (SEC_SignData(&signatureItem, tbs.data(), 126 | static_cast(tbs.length()), 127 | privateKey.get(), oidTag) != SECSuccess) { 128 | return MapPRErrorCodeToResult(PR_GetError()); 129 | } 130 | signature.assign(signatureItem.data, signatureItem.len); 131 | SECITEM_FreeItem(&signatureItem, false); 132 | return Success; 133 | } 134 | 135 | TestKeyPair* Clone() const override 136 | { 137 | ScopedSECKEYPrivateKey 138 | privateKeyCopy(SECKEY_CopyPrivateKey(privateKey.get())); 139 | if (!privateKeyCopy) { 140 | return nullptr; 141 | } 142 | return new (std::nothrow) NSSTestKeyPair(publicKeyAlg, 143 | subjectPublicKey, 144 | privateKeyCopy.release()); 145 | } 146 | 147 | private: 148 | ScopedSECKEYPrivateKey privateKey; 149 | }; 150 | 151 | } // unnamed namespace 152 | 153 | // This private function is also used by Gecko's PSM test framework 154 | // (OCSPCommon.cpp). 155 | // 156 | // Ownership of privateKey is transfered. 157 | TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg, 158 | const SECKEYPublicKey& publicKey, 159 | SECKEYPrivateKey* privateKey) 160 | { 161 | ScopedPtr 162 | spki(SECKEY_CreateSubjectPublicKeyInfo(&publicKey)); 163 | if (!spki) { 164 | return nullptr; 165 | } 166 | SECItem spkDER = spki->subjectPublicKey; 167 | DER_ConvertBitString(&spkDER); // bits to bytes 168 | return new (std::nothrow) NSSTestKeyPair(publicKeyAlg, 169 | ByteString(spkDER.data, spkDER.len), 170 | privateKey); 171 | } 172 | 173 | namespace { 174 | 175 | TestKeyPair* 176 | GenerateKeyPairInner() 177 | { 178 | ScopedPtr slot(PK11_GetInternalSlot()); 179 | if (!slot) { 180 | abort(); 181 | } 182 | 183 | // Bug 1012786: PK11_GenerateKeyPair can fail if there is insufficient 184 | // entropy to generate a random key. Attempting to add some entropy and 185 | // retrying appears to solve this issue. 186 | for (uint32_t retries = 0; retries < 10; retries++) { 187 | PK11RSAGenParams params; 188 | params.keySizeInBits = 2048; 189 | params.pe = 3; 190 | SECKEYPublicKey* publicKeyTemp = nullptr; 191 | ScopedSECKEYPrivateKey 192 | privateKey(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, 193 | ¶ms, &publicKeyTemp, false, true, 194 | nullptr)); 195 | ScopedSECKEYPublicKey publicKey(publicKeyTemp); 196 | if (privateKey) { 197 | return CreateTestKeyPair(RSA_PKCS1(), *publicKey, privateKey.release()); 198 | } 199 | 200 | assert(!publicKeyTemp); 201 | 202 | if (PR_GetError() != SEC_ERROR_PKCS11_FUNCTION_FAILED) { 203 | break; 204 | } 205 | 206 | // Since these keys are only for testing, we don't need them to be good, 207 | // random keys. 208 | // https://xkcd.com/221/ 209 | static const uint8_t RANDOM_NUMBER[] = { 4, 4, 4, 4, 4, 4, 4, 4 }; 210 | if (PK11_RandomUpdate((void*) &RANDOM_NUMBER, 211 | sizeof(RANDOM_NUMBER)) != SECSuccess) { 212 | break; 213 | } 214 | } 215 | 216 | abort(); 217 | } 218 | 219 | } // unnamed namespace 220 | 221 | TestKeyPair* 222 | GenerateKeyPair() 223 | { 224 | InitNSSIfNeeded(); 225 | return GenerateKeyPairInner(); 226 | } 227 | 228 | TestKeyPair* 229 | CloneReusedKeyPair() 230 | { 231 | static PRCallOnceType initCallOnce; 232 | if (PR_CallOnce(&initCallOnce, InitReusedKeyPair) != PR_SUCCESS) { 233 | abort(); 234 | } 235 | assert(reusedKeyPair); 236 | return reusedKeyPair->Clone(); 237 | } 238 | 239 | TestKeyPair* 240 | GenerateDSSKeyPair() 241 | { 242 | InitNSSIfNeeded(); 243 | 244 | ScopedPtr slot(PK11_GetInternalSlot()); 245 | if (!slot) { 246 | return nullptr; 247 | } 248 | 249 | ByteString p(DSS_P()); 250 | ByteString q(DSS_Q()); 251 | ByteString g(DSS_G()); 252 | 253 | static const PQGParams PARAMS = { 254 | nullptr, 255 | { siBuffer, 256 | const_cast(p.data()), 257 | static_cast(p.length()) 258 | }, 259 | { siBuffer, 260 | const_cast(q.data()), 261 | static_cast(q.length()) 262 | }, 263 | { siBuffer, 264 | const_cast(g.data()), 265 | static_cast(g.length()) 266 | } 267 | }; 268 | 269 | SECKEYPublicKey* publicKeyTemp = nullptr; 270 | ScopedSECKEYPrivateKey 271 | privateKey(PK11_GenerateKeyPair(slot.get(), CKM_DSA_KEY_PAIR_GEN, 272 | const_cast(&PARAMS), 273 | &publicKeyTemp, false, true, nullptr)); 274 | if (!privateKey) { 275 | return nullptr; 276 | } 277 | ScopedSECKEYPublicKey publicKey(publicKeyTemp); 278 | return CreateTestKeyPair(DSS(), *publicKey, privateKey.release()); 279 | } 280 | 281 | Result 282 | TestVerifyECDSASignedDigest(const SignedDigest& signedDigest, 283 | Input subjectPublicKeyInfo) 284 | { 285 | InitNSSIfNeeded(); 286 | return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, 287 | nullptr); 288 | } 289 | 290 | Result 291 | TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, 292 | Input subjectPublicKeyInfo) 293 | { 294 | InitNSSIfNeeded(); 295 | return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, 296 | nullptr); 297 | } 298 | 299 | Result 300 | TestDigestBuf(Input item, 301 | DigestAlgorithm digestAlg, 302 | /*out*/ uint8_t* digestBuf, 303 | size_t digestBufLen) 304 | { 305 | InitNSSIfNeeded(); 306 | return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); 307 | } 308 | 309 | } } } // namespace mozilla::pkix::test 310 | -------------------------------------------------------------------------------- /include/pkix/Input.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef mozilla_pkix_Input_h 26 | #define mozilla_pkix_Input_h 27 | 28 | #include 29 | 30 | #include "pkix/Result.h" 31 | #include "stdint.h" 32 | 33 | namespace mozilla { namespace pkix { 34 | 35 | class Reader; 36 | 37 | // An Input is a safety-oriented immutable weak reference to a array of bytes 38 | // of a known size. The data can only be legally accessed by constructing a 39 | // Reader object, which guarantees all accesses to the data are memory safe. 40 | // Neither Input not Reader provide any facilities for modifying the data 41 | // they reference. 42 | // 43 | // Inputs are small and should usually be passed by value, not by reference, 44 | // though for inline functions the distinction doesn't matter: 45 | // 46 | // Result GoodExample(Input input); 47 | // Result BadExample(const Input& input); 48 | // Result WorseExample(const uint8_t* input, size_t len); 49 | // 50 | // Note that in the example, GoodExample has the same performance 51 | // characteristics as WorseExample, but with much better safety guarantees. 52 | class Input final 53 | { 54 | public: 55 | typedef uint16_t size_type; 56 | 57 | // This constructor is useful for inputs that are statically known to be of a 58 | // fixed size, e.g.: 59 | // 60 | // static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 }; 61 | // const Input expected(EXPECTED_BYTES); 62 | // 63 | // This is equivalent to (and preferred over): 64 | // 65 | // static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 }; 66 | // Input expected; 67 | // Result rv = expected.Init(EXPECTED_BYTES, sizeof EXPECTED_BYTES); 68 | template 69 | explicit Input(const uint8_t (&data)[N]) 70 | : data(data) 71 | , len(N) 72 | { 73 | } 74 | 75 | // Construct a valid, empty, Init-able Input. 76 | Input() 77 | : data(nullptr) 78 | , len(0u) 79 | { 80 | } 81 | 82 | // This is intentionally not explicit in order to allow value semantics. 83 | Input(const Input&) = default; 84 | 85 | // Initialize the input. data must be non-null and len must be less than 86 | // 65536. Init may not be called more than once. 87 | Result Init(const uint8_t* data, size_t len) 88 | { 89 | if (this->data) { 90 | // already initialized 91 | return Result::FATAL_ERROR_INVALID_ARGS; 92 | } 93 | if (!data || len > 0xffffu) { 94 | // input too large 95 | return Result::ERROR_BAD_DER; 96 | } 97 | 98 | this->data = data; 99 | this->len = len; 100 | 101 | return Success; 102 | } 103 | 104 | // Initialize the input to be equivalent to the given input. Init may not be 105 | // called more than once. 106 | // 107 | // This is basically operator=, but it wasn't given that name because 108 | // normally callers do not check the result of operator=, and normally 109 | // operator= can be used multiple times. 110 | Result Init(Input other) 111 | { 112 | return Init(other.data, other.len); 113 | } 114 | 115 | // Returns the length of the input. 116 | // 117 | // Having the return type be size_type instead of size_t avoids the need for 118 | // callers to ensure that the result is small enough. 119 | size_type GetLength() const { return static_cast(len); } 120 | 121 | // Don't use this. It is here because we have some "friend" functions that we 122 | // don't want to declare in this header file. 123 | const uint8_t* UnsafeGetData() const { return data; } 124 | 125 | private: 126 | const uint8_t* data; 127 | size_t len; 128 | 129 | void operator=(const Input&) = delete; // Use Init instead. 130 | }; 131 | 132 | inline bool 133 | InputsAreEqual(const Input& a, const Input& b) 134 | { 135 | return a.GetLength() == b.GetLength() && 136 | !std::memcmp(a.UnsafeGetData(), b.UnsafeGetData(), a.GetLength()); 137 | } 138 | 139 | // An Reader is a cursor/iterator through the contents of an Input, designed to 140 | // maximize safety during parsing while minimizing the performance cost of that 141 | // safety. In particular, all methods do strict bounds checking to ensure 142 | // buffer overflows are impossible, and they are all inline so that the 143 | // compiler can coalesce as many of those checks together as possible. 144 | // 145 | // In general, Reader allows for one byte of lookahead and no backtracking. 146 | // However, the Match* functions internally may have more lookahead. 147 | class Reader final 148 | { 149 | public: 150 | Reader() 151 | : input(nullptr) 152 | , end(nullptr) 153 | { 154 | } 155 | 156 | explicit Reader(Input input) 157 | : input(input.UnsafeGetData()) 158 | , end(input.UnsafeGetData() + input.GetLength()) 159 | { 160 | } 161 | 162 | Result Init(Input input) 163 | { 164 | if (this->input) { 165 | return Result::FATAL_ERROR_INVALID_ARGS; 166 | } 167 | this->input = input.UnsafeGetData(); 168 | this->end = input.UnsafeGetData() + input.GetLength(); 169 | return Success; 170 | } 171 | 172 | bool Peek(uint8_t expectedByte) const 173 | { 174 | return input < end && *input == expectedByte; 175 | } 176 | 177 | Result Read(uint8_t& out) 178 | { 179 | Result rv = EnsureLength(1); 180 | if (rv != Success) { 181 | return rv; 182 | } 183 | out = *input++; 184 | return Success; 185 | } 186 | 187 | Result Read(uint16_t& out) 188 | { 189 | Result rv = EnsureLength(2); 190 | if (rv != Success) { 191 | return rv; 192 | } 193 | out = *input++; 194 | out <<= 8u; 195 | out |= *input++; 196 | return Success; 197 | } 198 | 199 | template 200 | bool MatchRest(const uint8_t (&toMatch)[N]) 201 | { 202 | // Normally we use EnsureLength which compares (input + len < end), but 203 | // here we want to be sure that there is nothing following the matched 204 | // bytes 205 | if (static_cast(end - input) != N) { 206 | return false; 207 | } 208 | if (memcmp(input, toMatch, N)) { 209 | return false; 210 | } 211 | input = end; 212 | return true; 213 | } 214 | 215 | bool MatchRest(Input toMatch) 216 | { 217 | // Normally we use EnsureLength which compares (input + len < end), but 218 | // here we want to be sure that there is nothing following the matched 219 | // bytes 220 | size_t remaining = static_cast(end - input); 221 | if (toMatch.GetLength() != remaining) { 222 | return false; 223 | } 224 | if (std::memcmp(input, toMatch.UnsafeGetData(), remaining)) { 225 | return false; 226 | } 227 | input = end; 228 | return true; 229 | } 230 | 231 | Result Skip(Input::size_type len) 232 | { 233 | Result rv = EnsureLength(len); 234 | if (rv != Success) { 235 | return rv; 236 | } 237 | input += len; 238 | return Success; 239 | } 240 | 241 | Result Skip(Input::size_type len, Reader& skipped) 242 | { 243 | Result rv = EnsureLength(len); 244 | if (rv != Success) { 245 | return rv; 246 | } 247 | rv = skipped.Init(input, len); 248 | if (rv != Success) { 249 | return rv; 250 | } 251 | input += len; 252 | return Success; 253 | } 254 | 255 | Result Skip(Input::size_type len, /*out*/ Input& skipped) 256 | { 257 | Result rv = EnsureLength(len); 258 | if (rv != Success) { 259 | return rv; 260 | } 261 | rv = skipped.Init(input, len); 262 | if (rv != Success) { 263 | return rv; 264 | } 265 | input += len; 266 | return Success; 267 | } 268 | 269 | void SkipToEnd() 270 | { 271 | input = end; 272 | } 273 | 274 | Result SkipToEnd(/*out*/ Input& skipped) 275 | { 276 | return Skip(static_cast(end - input), skipped); 277 | } 278 | 279 | Result EnsureLength(Input::size_type len) 280 | { 281 | if (static_cast(end - input) < len) { 282 | return Result::ERROR_BAD_DER; 283 | } 284 | return Success; 285 | } 286 | 287 | bool AtEnd() const { return input == end; } 288 | 289 | class Mark final 290 | { 291 | public: 292 | Mark(const Mark&) = default; // Intentionally not explicit. 293 | private: 294 | friend class Reader; 295 | Mark(const Reader& input, const uint8_t* mark) : input(input), mark(mark) { } 296 | const Reader& input; 297 | const uint8_t* const mark; 298 | void operator=(const Mark&) = delete; 299 | }; 300 | 301 | Mark GetMark() const { return Mark(*this, input); } 302 | 303 | Result GetInput(const Mark& mark, /*out*/ Input& item) 304 | { 305 | if (&mark.input != this || mark.mark > input) { 306 | return NotReached("invalid mark", Result::FATAL_ERROR_INVALID_ARGS); 307 | } 308 | return item.Init(mark.mark, 309 | static_cast(input - mark.mark)); 310 | } 311 | 312 | private: 313 | Result Init(const uint8_t* data, Input::size_type len) 314 | { 315 | if (input) { 316 | // already initialized 317 | return Result::FATAL_ERROR_INVALID_ARGS; 318 | } 319 | input = data; 320 | end = data + len; 321 | return Success; 322 | } 323 | 324 | const uint8_t* input; 325 | const uint8_t* end; 326 | 327 | Reader(const Reader&) = delete; 328 | void operator=(const Reader&) = delete; 329 | }; 330 | 331 | inline bool 332 | InputContains(const Input& input, uint8_t toFind) 333 | { 334 | Reader reader(input); 335 | for (;;) { 336 | uint8_t b; 337 | if (reader.Read(b) != Success) { 338 | return false; 339 | } 340 | if (b == toFind) { 341 | return true; 342 | } 343 | } 344 | } 345 | 346 | } } // namespace mozilla::pkix 347 | 348 | #endif // mozilla_pkix_Input_h 349 | -------------------------------------------------------------------------------- /test/gtest/pkixcert_extension_tests.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixder.h" 26 | #include "pkixgtest.h" 27 | 28 | using namespace mozilla::pkix; 29 | using namespace mozilla::pkix::test; 30 | 31 | // Creates a self-signed certificate with the given extension. 32 | static ByteString 33 | CreateCertWithExtensions(const char* subjectCN, 34 | const ByteString* extensions) 35 | { 36 | static long serialNumberValue = 0; 37 | ++serialNumberValue; 38 | ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue)); 39 | EXPECT_FALSE(ENCODING_FAILED(serialNumber)); 40 | ByteString issuerDER(CNToDERName(subjectCN)); 41 | EXPECT_FALSE(ENCODING_FAILED(issuerDER)); 42 | ByteString subjectDER(CNToDERName(subjectCN)); 43 | EXPECT_FALSE(ENCODING_FAILED(subjectDER)); 44 | ScopedTestKeyPair subjectKey(CloneReusedKeyPair()); 45 | return CreateEncodedCertificate(v3, sha256WithRSAEncryption(), 46 | serialNumber, issuerDER, 47 | oneDayBeforeNow, oneDayAfterNow, 48 | subjectDER, *subjectKey, extensions, 49 | *subjectKey, 50 | sha256WithRSAEncryption()); 51 | } 52 | 53 | // Creates a self-signed certificate with the given extension. 54 | static ByteString 55 | CreateCertWithOneExtension(const char* subjectStr, const ByteString& extension) 56 | { 57 | const ByteString extensions[] = { extension, ByteString() }; 58 | return CreateCertWithExtensions(subjectStr, extensions); 59 | } 60 | 61 | class TrustEverythingTrustDomain final : public DefaultCryptoTrustDomain 62 | { 63 | private: 64 | Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input, 65 | /*out*/ TrustLevel& trustLevel) override 66 | { 67 | trustLevel = TrustLevel::TrustAnchor; 68 | return Success; 69 | } 70 | 71 | Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, 72 | /*optional*/ const Input*, /*optional*/ const Input*) 73 | override 74 | { 75 | return Success; 76 | } 77 | 78 | Result IsChainValid(const DERArray&, Time) override 79 | { 80 | return Success; 81 | } 82 | }; 83 | 84 | // python DottedOIDToCode.py --tlv unknownExtensionOID 1.3.6.1.4.1.13769.666.666.666.1.500.9.3 85 | static const uint8_t tlv_unknownExtensionOID[] = { 86 | 0x06, 0x12, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a, 0x85, 0x1a, 87 | 0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03 88 | }; 89 | 90 | // python DottedOIDToCode.py --tlv id-pe-authorityInformationAccess 1.3.6.1.5.5.7.1.1 91 | static const uint8_t tlv_id_pe_authorityInformationAccess[] = { 92 | 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 93 | }; 94 | 95 | // python DottedOIDToCode.py --tlv wrongExtensionOID 1.3.6.6.1.5.5.7.1.1 96 | // (there is an extra "6" that shouldn't be in this OID) 97 | static const uint8_t tlv_wrongExtensionOID[] = { 98 | 0x06, 0x09, 0x2b, 0x06, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 99 | }; 100 | 101 | // python DottedOIDToCode.py --tlv id-ce-unknown 2.5.29.55 102 | // (this is a made-up OID for testing "id-ce"-prefixed OIDs that mozilla::pkix 103 | // doesn't handle) 104 | static const uint8_t tlv_id_ce_unknown[] = { 105 | 0x06, 0x03, 0x55, 0x1d, 0x37 106 | }; 107 | 108 | // python DottedOIDToCode.py --tlv id-ce-inhibitAnyPolicy 2.5.29.54 109 | static const uint8_t tlv_id_ce_inhibitAnyPolicy[] = { 110 | 0x06, 0x03, 0x55, 0x1d, 0x36 111 | }; 112 | 113 | // python DottedOIDToCode.py --tlv id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5 114 | static const uint8_t tlv_id_pkix_ocsp_nocheck[] = { 115 | 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05 116 | }; 117 | 118 | template 119 | inline ByteString 120 | BytesToByteString(const uint8_t (&bytes)[L]) 121 | { 122 | return ByteString(bytes, L); 123 | } 124 | 125 | struct ExtensionTestcase 126 | { 127 | ByteString extension; 128 | Result expectedResult; 129 | }; 130 | 131 | static const ExtensionTestcase EXTENSION_TESTCASES[] = 132 | { 133 | // Tests that a non-critical extension not in the id-ce or id-pe arcs (which 134 | // is thus unknown to us) verifies successfully even if empty (extensions we 135 | // know about aren't normally allowed to be empty). 136 | { TLV(der::SEQUENCE, 137 | BytesToByteString(tlv_unknownExtensionOID) + 138 | TLV(der::OCTET_STRING, ByteString())), 139 | Success 140 | }, 141 | 142 | // Tests that a critical extension not in the id-ce or id-pe arcs (which is 143 | // thus unknown to us) is detected and that verification fails with the 144 | // appropriate error. 145 | { TLV(der::SEQUENCE, 146 | BytesToByteString(tlv_unknownExtensionOID) + 147 | Boolean(true) + 148 | TLV(der::OCTET_STRING, ByteString())), 149 | Result::ERROR_UNKNOWN_CRITICAL_EXTENSION 150 | }, 151 | 152 | // Tests that a id-pe-authorityInformationAccess critical extension 153 | // is detected and that verification succeeds. 154 | // XXX: According to RFC 5280 an AIA that consists of an empty sequence is 155 | // not legal, but we accept it and that is not what we're testing here. 156 | { TLV(der::SEQUENCE, 157 | BytesToByteString(tlv_id_pe_authorityInformationAccess) + 158 | Boolean(true) + 159 | TLV(der::OCTET_STRING, TLV(der::SEQUENCE, ByteString()))), 160 | Success 161 | }, 162 | 163 | // Tests that an incorrect OID for id-pe-authorityInformationAccess 164 | // (when marked critical) is detected and that verification fails. 165 | // (Until bug 1020993 was fixed, this wrong value was used for 166 | // id-pe-authorityInformationAccess.) 167 | { TLV(der::SEQUENCE, 168 | BytesToByteString(tlv_wrongExtensionOID) + 169 | Boolean(true) + 170 | TLV(der::OCTET_STRING, ByteString())), 171 | Result::ERROR_UNKNOWN_CRITICAL_EXTENSION 172 | }, 173 | 174 | // We know about some id-ce extensions (OID arc 2.5.29), but not all of them. 175 | // Tests that an unknown id-ce extension is detected and that verification 176 | // fails. 177 | { TLV(der::SEQUENCE, 178 | BytesToByteString(tlv_id_ce_unknown) + 179 | Boolean(true) + 180 | TLV(der::OCTET_STRING, ByteString())), 181 | Result::ERROR_UNKNOWN_CRITICAL_EXTENSION 182 | }, 183 | 184 | // Tests that a certificate with a known critical id-ce extension (in this 185 | // case, OID 2.5.29.54, which is id-ce-inhibitAnyPolicy), verifies 186 | // successfully. 187 | { TLV(der::SEQUENCE, 188 | BytesToByteString(tlv_id_ce_inhibitAnyPolicy) + 189 | Boolean(true) + 190 | TLV(der::OCTET_STRING, Integer(0))), 191 | Success 192 | }, 193 | 194 | // Tests that a certificate with the id-pkix-ocsp-nocheck extension (marked 195 | // critical) verifies successfully. 196 | // RFC 6960: 197 | // ext-ocsp-nocheck EXTENSION ::= { SYNTAX NULL IDENTIFIED 198 | // BY id-pkix-ocsp-nocheck } 199 | { TLV(der::SEQUENCE, 200 | BytesToByteString(tlv_id_pkix_ocsp_nocheck) + 201 | Boolean(true) + 202 | TLV(der::OCTET_STRING, TLV(der::NULLTag, ByteString()))), 203 | Success 204 | }, 205 | 206 | // Tests that a certificate with another representation of the 207 | // id-pkix-ocsp-nocheck extension (marked critical) verifies successfully. 208 | // According to http://comments.gmane.org/gmane.ietf.x509/30947, 209 | // some code creates certificates where value of the extension is 210 | // an empty OCTET STRING. 211 | { TLV(der::SEQUENCE, 212 | BytesToByteString(tlv_id_pkix_ocsp_nocheck) + 213 | Boolean(true) + 214 | TLV(der::OCTET_STRING, ByteString())), 215 | Success 216 | }, 217 | }; 218 | 219 | class pkixcert_extension 220 | : public ::testing::Test 221 | , public ::testing::WithParamInterface 222 | { 223 | protected: 224 | static TrustEverythingTrustDomain trustDomain; 225 | }; 226 | 227 | /*static*/ TrustEverythingTrustDomain pkixcert_extension::trustDomain; 228 | 229 | TEST_P(pkixcert_extension, ExtensionHandledProperly) 230 | { 231 | const ExtensionTestcase& testcase(GetParam()); 232 | const char* cn = "Cert Extension Test"; 233 | ByteString cert(CreateCertWithOneExtension(cn, testcase.extension)); 234 | ASSERT_FALSE(ENCODING_FAILED(cert)); 235 | Input certInput; 236 | ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length())); 237 | ASSERT_EQ(testcase.expectedResult, 238 | BuildCertChain(trustDomain, certInput, Now(), 239 | EndEntityOrCA::MustBeEndEntity, 240 | KeyUsage::noParticularKeyUsageRequired, 241 | KeyPurposeId::anyExtendedKeyUsage, 242 | CertPolicyId::anyPolicy, 243 | nullptr/*stapledOCSPResponse*/)); 244 | } 245 | 246 | INSTANTIATE_TEST_CASE_P(pkixcert_extension, 247 | pkixcert_extension, 248 | testing::ValuesIn(EXTENSION_TESTCASES)); 249 | 250 | // Two subjectAltNames must result in an error. 251 | TEST_F(pkixcert_extension, DuplicateSubjectAltName) 252 | { 253 | // python DottedOIDToCode.py --tlv id-ce-subjectAltName 2.5.29.17 254 | static const uint8_t tlv_id_ce_subjectAltName[] = { 255 | 0x06, 0x03, 0x55, 0x1d, 0x11 256 | }; 257 | 258 | ByteString subjectAltName( 259 | TLV(der::SEQUENCE, 260 | BytesToByteString(tlv_id_ce_subjectAltName) + 261 | TLV(der::OCTET_STRING, TLV(der::SEQUENCE, DNSName("example.com"))))); 262 | static const ByteString extensions[] = { subjectAltName, subjectAltName, 263 | ByteString() }; 264 | static const char* certCN = "Cert With Duplicate subjectAltName"; 265 | ByteString cert(CreateCertWithExtensions(certCN, extensions)); 266 | ASSERT_FALSE(ENCODING_FAILED(cert)); 267 | Input certInput; 268 | ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length())); 269 | ASSERT_EQ(Result::ERROR_EXTENSION_VALUE_INVALID, 270 | BuildCertChain(trustDomain, certInput, Now(), 271 | EndEntityOrCA::MustBeEndEntity, 272 | KeyUsage::noParticularKeyUsageRequired, 273 | KeyPurposeId::anyExtendedKeyUsage, 274 | CertPolicyId::anyPolicy, 275 | nullptr/*stapledOCSPResponse*/)); 276 | } 277 | -------------------------------------------------------------------------------- /include/pkix/Result.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef mozilla_pkix_Result_h 26 | #define mozilla_pkix_Result_h 27 | 28 | #include 29 | 30 | namespace mozilla { namespace pkix { 31 | 32 | static const unsigned int FATAL_ERROR_FLAG = 0x800; 33 | 34 | // ---------------------------------------------------------------------------- 35 | // SELECTED ERROR CODE EXPLANATIONS 36 | // 37 | // Result::ERROR_UNTRUSTED_CERT 38 | // means that the end-entity certificate was actively distrusted. 39 | // Result::ERROR_UNTRUSTED_ISSUER 40 | // means that path building failed because of active distrust. 41 | // Result::ERROR_INVALID_DER_TIME 42 | // means the DER-encoded time was unexpected, such as being before the 43 | // UNIX epoch (allowed by X500, but not valid here). 44 | // Result::ERROR_EXPIRED_CERTIFICATE 45 | // means the end entity certificate expired. 46 | // Result::ERROR_EXPIRED_ISSUER_CERTIFICATE 47 | // means the CA certificate expired. 48 | // Result::ERROR_UNKNOWN_ISSUER 49 | // means that the CA could not be found in the root store. 50 | // Result::ERROR_POLICY_VALIDATION_FAILED 51 | // means that an encoded policy could not be applied or wasn't present 52 | // when expected. Usually this is in the context of Extended Validation. 53 | // Result::ERROR_BAD_CERT_DOMAIN 54 | // means that the certificate's name couldn't be matched to the 55 | // reference identifier. 56 | // Result::ERROR_CERT_NOT_IN_NAME_SPACE 57 | // typically means the certificate violates name constraints applied 58 | // by the issuer. 59 | // Result::ERROR_BAD_DER 60 | // means the input was improperly encoded. 61 | // Result::ERROR_UNKNOWN_ERROR 62 | // means that an external library (NSS) provided an error we didn't 63 | // anticipate. See the map below in Result.h to add new ones. 64 | // Result::FATAL_ERROR_LIBRARY_FAILURE 65 | // is an unexpected fatal error indicating a library had an unexpected 66 | // failure, and we can't proceed. 67 | // Result::FATAL_ERROR_INVALID_ARGS 68 | // means that we violated our own expectations on inputs and there's a 69 | // bug somewhere. 70 | // Result::FATAL_ERROR_INVALID_STATE 71 | // means that we violated our own expectations on state and there's a 72 | // bug somewhere. 73 | // Result::FATAL_ERROR_NO_MEMORY 74 | // means a memory allocation failed, prohibiting validation. 75 | // ---------------------------------------------------------------------------- 76 | 77 | // The first argument to MOZILLA_PKIX_MAP() is used for building the mapping 78 | // from error code to error name in MapResultToName. 79 | // 80 | // The second argument is for defining the value for the enum literal in the 81 | // Result enum class. 82 | // 83 | // The third argument to MOZILLA_PKIX_MAP() is used, along with the first 84 | // argument, for maintaining the mapping of mozilla::pkix error codes to 85 | // NSS/NSPR error codes in pkixnss.cpp. 86 | #define MOZILLA_PKIX_MAP_LIST \ 87 | MOZILLA_PKIX_MAP(Success, 0, 0) \ 88 | MOZILLA_PKIX_MAP(ERROR_BAD_DER, 1, \ 89 | SEC_ERROR_BAD_DER) \ 90 | MOZILLA_PKIX_MAP(ERROR_CA_CERT_INVALID, 2, \ 91 | SEC_ERROR_CA_CERT_INVALID) \ 92 | MOZILLA_PKIX_MAP(ERROR_BAD_SIGNATURE, 3, \ 93 | SEC_ERROR_BAD_SIGNATURE) \ 94 | MOZILLA_PKIX_MAP(ERROR_CERT_BAD_ACCESS_LOCATION, 4, \ 95 | SEC_ERROR_CERT_BAD_ACCESS_LOCATION) \ 96 | MOZILLA_PKIX_MAP(ERROR_CERT_NOT_IN_NAME_SPACE, 5, \ 97 | SEC_ERROR_CERT_NOT_IN_NAME_SPACE) \ 98 | MOZILLA_PKIX_MAP(ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 6, \ 99 | SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) \ 100 | MOZILLA_PKIX_MAP(ERROR_CONNECT_REFUSED, 7, \ 101 | PR_CONNECT_REFUSED_ERROR) \ 102 | MOZILLA_PKIX_MAP(ERROR_EXPIRED_CERTIFICATE, 8, \ 103 | SEC_ERROR_EXPIRED_CERTIFICATE) \ 104 | MOZILLA_PKIX_MAP(ERROR_EXTENSION_VALUE_INVALID, 9, \ 105 | SEC_ERROR_EXTENSION_VALUE_INVALID) \ 106 | MOZILLA_PKIX_MAP(ERROR_INADEQUATE_CERT_TYPE, 10, \ 107 | SEC_ERROR_INADEQUATE_CERT_TYPE) \ 108 | MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_USAGE, 11, \ 109 | SEC_ERROR_INADEQUATE_KEY_USAGE) \ 110 | MOZILLA_PKIX_MAP(ERROR_INVALID_ALGORITHM, 12, \ 111 | SEC_ERROR_INVALID_ALGORITHM) \ 112 | MOZILLA_PKIX_MAP(ERROR_INVALID_DER_TIME, 13, \ 113 | SEC_ERROR_INVALID_TIME) \ 114 | MOZILLA_PKIX_MAP(ERROR_KEY_PINNING_FAILURE, 14, \ 115 | MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) \ 116 | MOZILLA_PKIX_MAP(ERROR_PATH_LEN_CONSTRAINT_INVALID, 15, \ 117 | SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID) \ 118 | MOZILLA_PKIX_MAP(ERROR_POLICY_VALIDATION_FAILED, 16, \ 119 | SEC_ERROR_POLICY_VALIDATION_FAILED) \ 120 | MOZILLA_PKIX_MAP(ERROR_REVOKED_CERTIFICATE, 17, \ 121 | SEC_ERROR_REVOKED_CERTIFICATE) \ 122 | MOZILLA_PKIX_MAP(ERROR_UNKNOWN_CRITICAL_EXTENSION, 18, \ 123 | SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION) \ 124 | MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ERROR, 19, \ 125 | PR_UNKNOWN_ERROR) \ 126 | MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ISSUER, 20, \ 127 | SEC_ERROR_UNKNOWN_ISSUER) \ 128 | MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_CERT, 21, \ 129 | SEC_ERROR_UNTRUSTED_CERT) \ 130 | MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_ISSUER, 22, \ 131 | SEC_ERROR_UNTRUSTED_ISSUER) \ 132 | MOZILLA_PKIX_MAP(ERROR_OCSP_BAD_SIGNATURE, 23, \ 133 | SEC_ERROR_OCSP_BAD_SIGNATURE) \ 134 | MOZILLA_PKIX_MAP(ERROR_OCSP_INVALID_SIGNING_CERT, 24, \ 135 | SEC_ERROR_OCSP_INVALID_SIGNING_CERT) \ 136 | MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_REQUEST, 25, \ 137 | SEC_ERROR_OCSP_MALFORMED_REQUEST) \ 138 | MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_RESPONSE, 26, \ 139 | SEC_ERROR_OCSP_MALFORMED_RESPONSE) \ 140 | MOZILLA_PKIX_MAP(ERROR_OCSP_OLD_RESPONSE, 27, \ 141 | SEC_ERROR_OCSP_OLD_RESPONSE) \ 142 | MOZILLA_PKIX_MAP(ERROR_OCSP_REQUEST_NEEDS_SIG, 28, \ 143 | SEC_ERROR_OCSP_REQUEST_NEEDS_SIG) \ 144 | MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONDER_CERT_INVALID, 29, \ 145 | SEC_ERROR_OCSP_RESPONDER_CERT_INVALID) \ 146 | MOZILLA_PKIX_MAP(ERROR_OCSP_SERVER_ERROR, 30, \ 147 | SEC_ERROR_OCSP_SERVER_ERROR) \ 148 | MOZILLA_PKIX_MAP(ERROR_OCSP_TRY_SERVER_LATER, 31, \ 149 | SEC_ERROR_OCSP_TRY_SERVER_LATER) \ 150 | MOZILLA_PKIX_MAP(ERROR_OCSP_UNAUTHORIZED_REQUEST, 32, \ 151 | SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST) \ 152 | MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, 33, \ 153 | SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS) \ 154 | MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_CERT, 34, \ 155 | SEC_ERROR_OCSP_UNKNOWN_CERT) \ 156 | MOZILLA_PKIX_MAP(ERROR_OCSP_FUTURE_RESPONSE, 35, \ 157 | SEC_ERROR_OCSP_FUTURE_RESPONSE) \ 158 | MOZILLA_PKIX_MAP(ERROR_INVALID_KEY, 36, \ 159 | SEC_ERROR_INVALID_KEY) \ 160 | MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_KEYALG, 37, \ 161 | SEC_ERROR_UNSUPPORTED_KEYALG) \ 162 | MOZILLA_PKIX_MAP(ERROR_EXPIRED_ISSUER_CERTIFICATE, 38, \ 163 | SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE) \ 164 | MOZILLA_PKIX_MAP(ERROR_CA_CERT_USED_AS_END_ENTITY, 39, \ 165 | MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY) \ 166 | MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_SIZE, 40, \ 167 | MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE) \ 168 | MOZILLA_PKIX_MAP(ERROR_V1_CERT_USED_AS_CA, 41, \ 169 | MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA) \ 170 | MOZILLA_PKIX_MAP(ERROR_BAD_CERT_DOMAIN, 42, \ 171 | SSL_ERROR_BAD_CERT_DOMAIN) \ 172 | MOZILLA_PKIX_MAP(ERROR_NO_RFC822NAME_MATCH, 43, \ 173 | MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH) \ 174 | MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_ELLIPTIC_CURVE, 44, \ 175 | SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE) \ 176 | MOZILLA_PKIX_MAP(ERROR_NOT_YET_VALID_CERTIFICATE, 45, \ 177 | MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE) \ 178 | MOZILLA_PKIX_MAP(ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE, 46, \ 179 | MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE) \ 180 | MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_EC_POINT_FORM, 47, \ 181 | SEC_ERROR_UNSUPPORTED_EC_POINT_FORM) \ 182 | MOZILLA_PKIX_MAP(ERROR_SIGNATURE_ALGORITHM_MISMATCH, 48, \ 183 | MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH) \ 184 | MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONSE_FOR_CERT_MISSING, 49, \ 185 | MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING) \ 186 | MOZILLA_PKIX_MAP(ERROR_VALIDITY_TOO_LONG, 50, \ 187 | MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG) \ 188 | MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_ARGS, FATAL_ERROR_FLAG | 1, \ 189 | SEC_ERROR_INVALID_ARGS) \ 190 | MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_STATE, FATAL_ERROR_FLAG | 2, \ 191 | PR_INVALID_STATE_ERROR) \ 192 | MOZILLA_PKIX_MAP(FATAL_ERROR_LIBRARY_FAILURE, FATAL_ERROR_FLAG | 3, \ 193 | SEC_ERROR_LIBRARY_FAILURE) \ 194 | MOZILLA_PKIX_MAP(FATAL_ERROR_NO_MEMORY, FATAL_ERROR_FLAG | 4, \ 195 | SEC_ERROR_NO_MEMORY) \ 196 | /* nothing here */ 197 | 198 | enum class Result 199 | { 200 | #define MOZILLA_PKIX_MAP(name, value, nss_name) name = value, 201 | MOZILLA_PKIX_MAP_LIST 202 | #undef MOZILLA_PKIX_MAP 203 | }; 204 | 205 | // Returns the stringified name of the given result, e.g. "Result::Success", 206 | // or nullptr if result is unknown (invalid). 207 | const char* MapResultToName(Result result); 208 | 209 | // We write many comparisons as (x != Success), and this shortened name makes 210 | // those comparisons clearer, especially because the shortened name often 211 | // results in less line wrapping. 212 | static const Result Success = Result::Success; 213 | 214 | inline bool 215 | IsFatalError(Result rv) 216 | { 217 | return (static_cast(rv) & FATAL_ERROR_FLAG) != 0; 218 | } 219 | 220 | inline Result 221 | NotReached(const char* /*explanation*/, Result result) 222 | { 223 | assert(false); 224 | return result; 225 | } 226 | 227 | } } // namespace mozilla::pkix 228 | 229 | #endif // mozilla_pkix_Result_h 230 | -------------------------------------------------------------------------------- /lib/pkixcert.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2014 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixutil.h" 26 | 27 | namespace mozilla { namespace pkix { 28 | 29 | Result 30 | BackCert::Init() 31 | { 32 | Result rv; 33 | 34 | // Certificate ::= SEQUENCE { 35 | // tbsCertificate TBSCertificate, 36 | // signatureAlgorithm AlgorithmIdentifier, 37 | // signatureValue BIT STRING } 38 | 39 | Reader tbsCertificate; 40 | 41 | // The scope of |input| and |certificate| are limited to this block so we 42 | // don't accidentally confuse them for tbsCertificate later. 43 | { 44 | Reader certificate; 45 | rv = der::ExpectTagAndGetValueAtEnd(der, der::SEQUENCE, certificate); 46 | if (rv != Success) { 47 | return rv; 48 | } 49 | rv = der::SignedData(certificate, tbsCertificate, signedData); 50 | if (rv != Success) { 51 | return rv; 52 | } 53 | rv = der::End(certificate); 54 | if (rv != Success) { 55 | return rv; 56 | } 57 | } 58 | 59 | // TBSCertificate ::= SEQUENCE { 60 | // version [0] EXPLICIT Version DEFAULT v1, 61 | // serialNumber CertificateSerialNumber, 62 | // signature AlgorithmIdentifier, 63 | // issuer Name, 64 | // validity Validity, 65 | // subject Name, 66 | // subjectPublicKeyInfo SubjectPublicKeyInfo, 67 | // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, 68 | // -- If present, version MUST be v2 or v3 69 | // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, 70 | // -- If present, version MUST be v2 or v3 71 | // extensions [3] EXPLICIT Extensions OPTIONAL 72 | // -- If present, version MUST be v3 73 | // } 74 | rv = der::OptionalVersion(tbsCertificate, version); 75 | if (rv != Success) { 76 | return rv; 77 | } 78 | rv = der::CertificateSerialNumber(tbsCertificate, serialNumber); 79 | if (rv != Success) { 80 | return rv; 81 | } 82 | rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature); 83 | if (rv != Success) { 84 | return rv; 85 | } 86 | rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer); 87 | if (rv != Success) { 88 | return rv; 89 | } 90 | rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity); 91 | if (rv != Success) { 92 | return rv; 93 | } 94 | // TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate 95 | // that the name is syntactically valid, if they care. In Gecko we do this 96 | // implicitly by parsing the certificate into a CERTCertificate object. 97 | // Instead of relying on the caller to do this, we should do it ourselves. 98 | rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject); 99 | if (rv != Success) { 100 | return rv; 101 | } 102 | rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, 103 | subjectPublicKeyInfo); 104 | if (rv != Success) { 105 | return rv; 106 | } 107 | 108 | static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED; 109 | 110 | // According to RFC 5280, all fields below this line are forbidden for 111 | // certificate versions less than v3. However, for compatibility reasons, 112 | // we parse v1/v2 certificates in the same way as v3 certificates. So if 113 | // these fields appear in a v1 certificate, they will be used. 114 | 115 | // Ignore issuerUniqueID if present. 116 | if (tbsCertificate.Peek(CSC | 1)) { 117 | rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1); 118 | if (rv != Success) { 119 | return rv; 120 | } 121 | } 122 | 123 | // Ignore subjectUniqueID if present. 124 | if (tbsCertificate.Peek(CSC | 2)) { 125 | rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2); 126 | if (rv != Success) { 127 | return rv; 128 | } 129 | } 130 | 131 | rv = der::OptionalExtensions( 132 | tbsCertificate, CSC | 3, 133 | [this](Reader& extnID, const Input& extnValue, bool critical, 134 | /*out*/ bool& understood) { 135 | return RememberExtension(extnID, extnValue, critical, understood); 136 | }); 137 | if (rv != Success) { 138 | return rv; 139 | } 140 | 141 | // The Netscape Certificate Type extension is an obsolete 142 | // Netscape-proprietary mechanism that we ignore in favor of the standard 143 | // extensions. However, some CAs have issued certificates with the Netscape 144 | // Cert Type extension marked critical. Thus, for compatibility reasons, we 145 | // "understand" this extension by ignoring it when it is not critical, and 146 | // by ensuring that the equivalent standardized extensions are present when 147 | // it is marked critical, based on the assumption that the information in 148 | // the Netscape Cert Type extension is consistent with the information in 149 | // the standard extensions. 150 | // 151 | // Here is a mapping between the Netscape Cert Type extension and the 152 | // standard extensions: 153 | // 154 | // Netscape Cert Type | BasicConstraints.cA | Extended Key Usage 155 | // --------------------+-----------------------+---------------------- 156 | // SSL Server | false | id_kp_serverAuth 157 | // SSL Client | false | id_kp_clientAuth 158 | // S/MIME Client | false | id_kp_emailProtection 159 | // Object Signing | false | id_kp_codeSigning 160 | // SSL Server CA | true | id_pk_serverAuth 161 | // SSL Client CA | true | id_kp_clientAuth 162 | // S/MIME CA | true | id_kp_emailProtection 163 | // Object Signing CA | true | id_kp_codeSigning 164 | if (criticalNetscapeCertificateType.GetLength() > 0 && 165 | (basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) { 166 | return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; 167 | } 168 | 169 | return der::End(tbsCertificate); 170 | } 171 | 172 | Result 173 | BackCert::RememberExtension(Reader& extnID, Input extnValue, 174 | bool critical, /*out*/ bool& understood) 175 | { 176 | understood = false; 177 | 178 | // python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15 179 | static const uint8_t id_ce_keyUsage[] = { 180 | 0x55, 0x1d, 0x0f 181 | }; 182 | // python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17 183 | static const uint8_t id_ce_subjectAltName[] = { 184 | 0x55, 0x1d, 0x11 185 | }; 186 | // python DottedOIDToCode.py id-ce-basicConstraints 2.5.29.19 187 | static const uint8_t id_ce_basicConstraints[] = { 188 | 0x55, 0x1d, 0x13 189 | }; 190 | // python DottedOIDToCode.py id-ce-nameConstraints 2.5.29.30 191 | static const uint8_t id_ce_nameConstraints[] = { 192 | 0x55, 0x1d, 0x1e 193 | }; 194 | // python DottedOIDToCode.py id-ce-certificatePolicies 2.5.29.32 195 | static const uint8_t id_ce_certificatePolicies[] = { 196 | 0x55, 0x1d, 0x20 197 | }; 198 | // python DottedOIDToCode.py id-ce-policyConstraints 2.5.29.36 199 | static const uint8_t id_ce_policyConstraints[] = { 200 | 0x55, 0x1d, 0x24 201 | }; 202 | // python DottedOIDToCode.py id-ce-extKeyUsage 2.5.29.37 203 | static const uint8_t id_ce_extKeyUsage[] = { 204 | 0x55, 0x1d, 0x25 205 | }; 206 | // python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54 207 | static const uint8_t id_ce_inhibitAnyPolicy[] = { 208 | 0x55, 0x1d, 0x36 209 | }; 210 | // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1 211 | static const uint8_t id_pe_authorityInfoAccess[] = { 212 | 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 213 | }; 214 | // python DottedOIDToCode.py id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5 215 | static const uint8_t id_pkix_ocsp_nocheck[] = { 216 | 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05 217 | }; 218 | // python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1 219 | static const uint8_t Netscape_certificate_type[] = { 220 | 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 221 | }; 222 | 223 | Input* out = nullptr; 224 | 225 | // We already enforce the maximum possible constraints for policies so we 226 | // can safely ignore even critical policy constraint extensions. 227 | // 228 | // XXX: Doing it this way won't allow us to detect duplicate 229 | // policyConstraints extensions, but that's OK because (and only because) we 230 | // ignore the extension. 231 | Input dummyPolicyConstraints; 232 | 233 | // We don't need to save the contents of this extension if it is present. We 234 | // just need to handle its presence (it is essentially ignored right now). 235 | Input dummyOCSPNocheck; 236 | 237 | // For compatibility reasons, for some extensions we have to allow empty 238 | // extension values. This would normally interfere with our duplicate 239 | // extension checking code. However, as long as the extensions we allow to 240 | // have empty values are also the ones we implicitly allow duplicates of, 241 | // this will work fine. 242 | bool emptyValueAllowed = false; 243 | 244 | // RFC says "Conforming CAs MUST mark this extension as non-critical" for 245 | // both authorityKeyIdentifier and subjectKeyIdentifier, and we do not use 246 | // them for anything, so we totally ignore them here. 247 | 248 | if (extnID.MatchRest(id_ce_keyUsage)) { 249 | out = &keyUsage; 250 | } else if (extnID.MatchRest(id_ce_subjectAltName)) { 251 | out = &subjectAltName; 252 | } else if (extnID.MatchRest(id_ce_basicConstraints)) { 253 | out = &basicConstraints; 254 | } else if (extnID.MatchRest(id_ce_nameConstraints)) { 255 | out = &nameConstraints; 256 | } else if (extnID.MatchRest(id_ce_certificatePolicies)) { 257 | out = &certificatePolicies; 258 | } else if (extnID.MatchRest(id_ce_policyConstraints)) { 259 | out = &dummyPolicyConstraints; 260 | } else if (extnID.MatchRest(id_ce_extKeyUsage)) { 261 | out = &extKeyUsage; 262 | } else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) { 263 | out = &inhibitAnyPolicy; 264 | } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) { 265 | out = &authorityInfoAccess; 266 | } else if (extnID.MatchRest(id_pkix_ocsp_nocheck) && critical) { 267 | // We need to make sure we don't reject delegated OCSP response signing 268 | // certificates that contain the id-pkix-ocsp-nocheck extension marked as 269 | // critical when validating OCSP responses. Without this, an application 270 | // that implements soft-fail OCSP might ignore a valid Revoked or Unknown 271 | // response, and an application that implements hard-fail OCSP might fail 272 | // to connect to a server given a valid Good response. 273 | out = &dummyOCSPNocheck; 274 | // We allow this extension to have an empty value. 275 | // See http://comments.gmane.org/gmane.ietf.x509/30947 276 | emptyValueAllowed = true; 277 | } else if (extnID.MatchRest(Netscape_certificate_type) && critical) { 278 | out = &criticalNetscapeCertificateType; 279 | } 280 | 281 | if (out) { 282 | // Don't allow an empty value for any extension we understand. This way, we 283 | // can test out->GetLength() != 0 or out->Init() to check for duplicates. 284 | if (extnValue.GetLength() == 0 && !emptyValueAllowed) { 285 | return Result::ERROR_EXTENSION_VALUE_INVALID; 286 | } 287 | if (out->Init(extnValue) != Success) { 288 | // Duplicate extension 289 | return Result::ERROR_EXTENSION_VALUE_INVALID; 290 | } 291 | understood = true; 292 | } 293 | 294 | return Success; 295 | } 296 | 297 | } } // namespace mozilla::pkix 298 | -------------------------------------------------------------------------------- /test/gtest/pkixcheck_CheckKeyUsage_tests.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixgtest.h" 26 | 27 | using namespace mozilla::pkix; 28 | using namespace mozilla::pkix::test; 29 | 30 | namespace mozilla { namespace pkix { 31 | 32 | extern Result CheckKeyUsage(EndEntityOrCA endEntityOrCA, 33 | const Input* encodedKeyUsage, 34 | KeyUsage requiredKeyUsageIfPresent); 35 | 36 | } } // namespace mozilla::pkix 37 | 38 | class pkixcheck_CheckKeyUsage : public ::testing::Test { }; 39 | 40 | #define ASSERT_BAD(x) ASSERT_EQ(Result::ERROR_INADEQUATE_KEY_USAGE, x) 41 | 42 | // Make it easy to define test data for the common, simplest cases. 43 | #define NAMED_SIMPLE_KU(name, unusedBits, bits) \ 44 | const uint8_t name##_bytes[4] = { \ 45 | 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, unusedBits, bits \ 46 | }; \ 47 | const Input name(name##_bytes); 48 | 49 | static const Input empty_null; 50 | 51 | // Note that keyCertSign is really the only interesting case for CA 52 | // certificates since we don't support cRLSign. 53 | 54 | TEST_F(pkixcheck_CheckKeyUsage, EE_none) 55 | { 56 | // The input Input is nullptr. This means the cert had no keyUsage 57 | // extension. This is always valid because no key usage in an end-entity 58 | // means that there are no key usage restrictions. 59 | 60 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, 61 | KeyUsage::noParticularKeyUsageRequired)); 62 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, 63 | KeyUsage::digitalSignature)); 64 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, 65 | KeyUsage::nonRepudiation)); 66 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, 67 | KeyUsage::keyEncipherment)); 68 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, 69 | KeyUsage::dataEncipherment)); 70 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, 71 | KeyUsage::keyAgreement)); 72 | } 73 | 74 | TEST_F(pkixcheck_CheckKeyUsage, EE_empty) 75 | { 76 | // The input Input is empty. The cert had an empty keyUsage extension, 77 | // which is syntactically invalid. 78 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null, 79 | KeyUsage::digitalSignature)); 80 | static const uint8_t dummy = 0x00; 81 | Input empty_nonnull; 82 | ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0)); 83 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull, 84 | KeyUsage::digitalSignature)); 85 | } 86 | 87 | TEST_F(pkixcheck_CheckKeyUsage, CA_none) 88 | { 89 | // A CA certificate does not have a KU extension. 90 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr, 91 | KeyUsage::keyCertSign)); 92 | } 93 | 94 | TEST_F(pkixcheck_CheckKeyUsage, CA_empty) 95 | { 96 | // A CA certificate has an empty KU extension. 97 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_null, 98 | KeyUsage::keyCertSign)); 99 | static const uint8_t dummy = 0x00; 100 | Input empty_nonnull; 101 | ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0)); 102 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull, 103 | KeyUsage::keyCertSign)); 104 | } 105 | 106 | TEST_F(pkixcheck_CheckKeyUsage, maxUnusedBits) 107 | { 108 | NAMED_SIMPLE_KU(encoded, 7, 0x80); 109 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded, 110 | KeyUsage::digitalSignature)); 111 | } 112 | 113 | TEST_F(pkixcheck_CheckKeyUsage, tooManyUnusedBits) 114 | { 115 | static uint8_t oneValueByteData[] = { 116 | 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 8/*unused bits*/, 0x80 117 | }; 118 | static const Input oneValueByte(oneValueByteData); 119 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte, 120 | KeyUsage::digitalSignature)); 121 | 122 | static uint8_t twoValueBytesData[] = { 123 | 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 8/*unused bits*/, 0x01, 0x00 124 | }; 125 | static const Input twoValueBytes(twoValueBytesData); 126 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes, 127 | KeyUsage::digitalSignature)); 128 | } 129 | 130 | TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_NoPaddingBits) 131 | { 132 | static const uint8_t DER_BYTES[] = { 133 | 0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 0/*unused bits*/ 134 | }; 135 | static const Input DER(DER_BYTES); 136 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER, 137 | KeyUsage::digitalSignature)); 138 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER, 139 | KeyUsage::keyCertSign)); 140 | } 141 | 142 | TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_7PaddingBits) 143 | { 144 | static const uint8_t DER_BYTES[] = { 145 | 0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 7/*unused bits*/ 146 | }; 147 | static const Input DER(DER_BYTES); 148 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER, 149 | KeyUsage::digitalSignature)); 150 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER, 151 | KeyUsage::keyCertSign)); 152 | } 153 | 154 | void ASSERT_SimpleCase(uint8_t unusedBits, uint8_t bits, KeyUsage usage) 155 | { 156 | // Test that only the right bit is accepted for the usage for both EE and CA 157 | // certs. 158 | NAMED_SIMPLE_KU(good, unusedBits, bits); 159 | ASSERT_EQ(Success, 160 | CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, usage)); 161 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, usage)); 162 | 163 | // We use (~bits >> unusedBits) << unusedBits) instead of using the same 164 | // calculation that is in CheckKeyUsage to validate that the calculation in 165 | // CheckKeyUsage is correct. 166 | 167 | // Test that none of the other non-padding bits are mistaken for the given 168 | // key usage in the single-byte value case. 169 | NAMED_SIMPLE_KU(notGood, unusedBits, 170 | static_cast((~bits >> unusedBits) << unusedBits)); 171 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, ¬Good, usage)); 172 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, ¬Good, usage)); 173 | 174 | // Test that none of the other non-padding bits are mistaken for the given 175 | // key usage in the two-byte value case. 176 | const uint8_t twoByteNotGoodData[] = { 177 | 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, unusedBits, 178 | static_cast(~bits), 179 | static_cast((0xFFu >> unusedBits) << unusedBits) 180 | }; 181 | Input twoByteNotGood(twoByteNotGoodData); 182 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood, 183 | usage)); 184 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood, usage)); 185 | } 186 | 187 | TEST_F(pkixcheck_CheckKeyUsage, simpleCases) 188 | { 189 | ASSERT_SimpleCase(7, 0x80, KeyUsage::digitalSignature); 190 | ASSERT_SimpleCase(6, 0x40, KeyUsage::nonRepudiation); 191 | ASSERT_SimpleCase(5, 0x20, KeyUsage::keyEncipherment); 192 | ASSERT_SimpleCase(4, 0x10, KeyUsage::dataEncipherment); 193 | ASSERT_SimpleCase(3, 0x08, KeyUsage::keyAgreement); 194 | } 195 | 196 | // Only CAs are allowed to assert keyCertSign. 197 | // End-entity certs may assert it along with other key usages if keyCertSign 198 | // isn't the required key usage. This is for compatibility. 199 | TEST_F(pkixcheck_CheckKeyUsage, keyCertSign) 200 | { 201 | NAMED_SIMPLE_KU(good, 2, 0x04); 202 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, 203 | KeyUsage::keyCertSign)); 204 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, 205 | KeyUsage::keyCertSign)); 206 | 207 | // Test that none of the other non-padding bits are mistaken for the given 208 | // key usage in the one-byte value case. 209 | NAMED_SIMPLE_KU(notGood, 2, 0xFB); 210 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, ¬Good, 211 | KeyUsage::keyCertSign)); 212 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, ¬Good, 213 | KeyUsage::keyCertSign)); 214 | 215 | // Test that none of the other non-padding bits are mistaken for the given 216 | // key usage in the two-byte value case. 217 | static uint8_t twoByteNotGoodData[] = { 218 | 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 2/*unused bits*/, 0xFBu, 0xFCu 219 | }; 220 | static const Input twoByteNotGood(twoByteNotGoodData); 221 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood, 222 | KeyUsage::keyCertSign)); 223 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood, 224 | KeyUsage::keyCertSign)); 225 | 226 | // If an end-entity certificate does assert keyCertSign, this is allowed 227 | // as long as that isn't the required key usage. 228 | NAMED_SIMPLE_KU(digitalSignatureAndKeyCertSign, 2, 0x84); 229 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, 230 | &digitalSignatureAndKeyCertSign, 231 | KeyUsage::digitalSignature)); 232 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, 233 | &digitalSignatureAndKeyCertSign, 234 | KeyUsage::keyCertSign)); 235 | } 236 | 237 | TEST_F(pkixcheck_CheckKeyUsage, unusedBitNotZero) 238 | { 239 | // single byte control case 240 | static uint8_t controlOneValueByteData[] = { 241 | 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 242 | }; 243 | static const Input controlOneValueByte(controlOneValueByteData); 244 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, 245 | &controlOneValueByte, 246 | KeyUsage::digitalSignature)); 247 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, 248 | &controlOneValueByte, 249 | KeyUsage::digitalSignature)); 250 | 251 | // single-byte test case 252 | static uint8_t oneValueByteData[] = { 253 | 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 | 0x01 254 | }; 255 | static const Input oneValueByte(oneValueByteData); 256 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte, 257 | KeyUsage::digitalSignature)); 258 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &oneValueByte, 259 | KeyUsage::digitalSignature)); 260 | 261 | // two-byte control case 262 | static uint8_t controlTwoValueBytesData[] = { 263 | 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/, 264 | 0x80 | 0x01, 0x80 265 | }; 266 | static const Input controlTwoValueBytes(controlTwoValueBytesData); 267 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, 268 | &controlTwoValueBytes, 269 | KeyUsage::digitalSignature)); 270 | ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, 271 | &controlTwoValueBytes, 272 | KeyUsage::digitalSignature)); 273 | 274 | // two-byte test case 275 | static uint8_t twoValueBytesData[] = { 276 | 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/, 277 | 0x80 | 0x01, 0x80 | 0x01 278 | }; 279 | static const Input twoValueBytes(twoValueBytesData); 280 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes, 281 | KeyUsage::digitalSignature)); 282 | ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoValueBytes, 283 | KeyUsage::digitalSignature)); 284 | } 285 | -------------------------------------------------------------------------------- /test/gtest/pkixcheck_CheckSignatureAlgorithm_tests.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2015 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkixder.h" 26 | #include "pkixgtest.h" 27 | 28 | using namespace mozilla::pkix; 29 | using namespace mozilla::pkix::test; 30 | 31 | namespace mozilla { namespace pkix { 32 | 33 | extern Result CheckSignatureAlgorithm( 34 | TrustDomain& trustDomain, EndEntityOrCA endEntityOrCA, 35 | const der::SignedDataWithSignature& signedData, 36 | Input signatureValue); 37 | 38 | } } // namespace mozilla::pkix 39 | 40 | struct CheckSignatureAlgorithmTestParams 41 | { 42 | ByteString signatureAlgorithmValue; 43 | ByteString signatureValue; 44 | unsigned int signatureLengthInBytes; 45 | Result expectedResult; 46 | }; 47 | 48 | #define BS(s) ByteString(s, MOZILLA_PKIX_ARRAY_LENGTH(s)) 49 | 50 | // python DottedOIDToCode.py --tlv sha256WithRSAEncryption 1.2.840.113549.1.1.11 51 | static const uint8_t tlv_sha256WithRSAEncryption[] = { 52 | 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b 53 | }; 54 | 55 | // Same as tlv_sha256WithRSAEncryption, except one without the "0x0b" and with 56 | // the DER length decreased accordingly. 57 | static const uint8_t tlv_sha256WithRSAEncryption_truncated[] = { 58 | 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01 59 | }; 60 | 61 | // python DottedOIDToCode.py --tlv sha-1WithRSAEncryption 1.2.840.113549.1.1.5 62 | static const uint8_t tlv_sha_1WithRSAEncryption[] = { 63 | 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 64 | }; 65 | 66 | // python DottedOIDToCode.py --tlv sha1WithRSASignature 1.3.14.3.2.29 67 | static const uint8_t tlv_sha1WithRSASignature[] = { 68 | 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d 69 | }; 70 | 71 | // python DottedOIDToCode.py --tlv md5WithRSAEncryption 1.2.840.113549.1.1.4 72 | static const uint8_t tlv_md5WithRSAEncryption[] = { 73 | 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 74 | }; 75 | 76 | static const CheckSignatureAlgorithmTestParams 77 | CHECKSIGNATUREALGORITHM_TEST_PARAMS[] = 78 | { 79 | { // Both algorithm IDs are empty 80 | ByteString(), 81 | ByteString(), 82 | 2048 / 8, 83 | Result::ERROR_BAD_DER, 84 | }, 85 | { // signatureAlgorithm is empty, signature is supported. 86 | ByteString(), 87 | BS(tlv_sha256WithRSAEncryption), 88 | 2048 / 8, 89 | Result::ERROR_BAD_DER, 90 | }, 91 | { // signatureAlgorithm is supported, signature is empty. 92 | BS(tlv_sha256WithRSAEncryption), 93 | ByteString(), 94 | 2048 / 8, 95 | Result::ERROR_BAD_DER, 96 | }, 97 | { // Algorithms match, both are supported. 98 | BS(tlv_sha256WithRSAEncryption), 99 | BS(tlv_sha256WithRSAEncryption), 100 | 2048 / 8, 101 | Success 102 | }, 103 | { // Algorithms do not match because signatureAlgorithm is truncated. 104 | BS(tlv_sha256WithRSAEncryption_truncated), 105 | BS(tlv_sha256WithRSAEncryption), 106 | 2048 / 8, 107 | Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED 108 | }, 109 | { // Algorithms do not match because signature is truncated. 110 | BS(tlv_sha256WithRSAEncryption), 111 | BS(tlv_sha256WithRSAEncryption_truncated), 112 | 2048 / 8, 113 | Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED 114 | }, 115 | { // Algorithms do not match, both are supported. 116 | BS(tlv_sha_1WithRSAEncryption), 117 | BS(tlv_sha256WithRSAEncryption), 118 | 2048 / 8, 119 | Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH, 120 | }, 121 | { // Algorithms do not match, both are supported. 122 | BS(tlv_sha256WithRSAEncryption), 123 | BS(tlv_sha_1WithRSAEncryption), 124 | 2048 / 8, 125 | Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH, 126 | }, 127 | { // Algorithms match, both are unsupported. 128 | BS(tlv_md5WithRSAEncryption), 129 | BS(tlv_md5WithRSAEncryption), 130 | 2048 / 8, 131 | Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED 132 | }, 133 | { // signatureAlgorithm is unsupported, signature is supported. 134 | BS(tlv_md5WithRSAEncryption), 135 | BS(tlv_sha256WithRSAEncryption), 136 | 2048 / 8, 137 | Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED 138 | }, 139 | { // signatureAlgorithm is supported, signature is unsupported. 140 | BS(tlv_sha256WithRSAEncryption), 141 | BS(tlv_md5WithRSAEncryption), 142 | 2048 / 8, 143 | Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED 144 | }, 145 | { // Both have the optional NULL parameter. 146 | BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()), 147 | BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()), 148 | 2048 / 8, 149 | Success 150 | }, 151 | { // signatureAlgorithm has the optional NULL parameter, signature doesn't. 152 | BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()), 153 | BS(tlv_sha256WithRSAEncryption), 154 | 2048 / 8, 155 | Success 156 | }, 157 | { // signatureAlgorithm does not have the optional NULL parameter, signature 158 | // does. 159 | BS(tlv_sha256WithRSAEncryption), 160 | BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()), 161 | 2048 / 8, 162 | Success 163 | }, 164 | { // The different OIDs for RSA-with-SHA1 we support are semantically 165 | // equivalent. 166 | BS(tlv_sha1WithRSASignature), 167 | BS(tlv_sha_1WithRSAEncryption), 168 | 2048 / 8, 169 | Success, 170 | }, 171 | { // The different OIDs for RSA-with-SHA1 we support are semantically 172 | // equivalent (opposite order). 173 | BS(tlv_sha_1WithRSAEncryption), 174 | BS(tlv_sha1WithRSASignature), 175 | 2048 / 8, 176 | Success, 177 | }, 178 | { // Algorithms match, both are supported, key size is not a multile of 128 179 | // bits. This test verifies that we're not wrongly rounding up the 180 | // signature size like we did in the original patch for bug 1131767. 181 | BS(tlv_sha256WithRSAEncryption), 182 | BS(tlv_sha256WithRSAEncryption), 183 | (2048 / 8) - 1, 184 | Success 185 | }, 186 | }; 187 | 188 | class pkixcheck_CheckSignatureAlgorithm 189 | : public ::testing::Test 190 | , public ::testing::WithParamInterface 191 | { 192 | }; 193 | 194 | class pkixcheck_CheckSignatureAlgorithm_TrustDomain final 195 | : public EverythingFailsByDefaultTrustDomain 196 | { 197 | public: 198 | explicit pkixcheck_CheckSignatureAlgorithm_TrustDomain( 199 | unsigned int publicKeySizeInBits) 200 | : publicKeySizeInBits(publicKeySizeInBits) 201 | , checkedDigestAlgorithm(false) 202 | , checkedModulusSizeInBits(false) 203 | { 204 | } 205 | 206 | Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA) override 207 | { 208 | checkedDigestAlgorithm = true; 209 | return Success; 210 | } 211 | 212 | Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA endEntityOrCA, 213 | unsigned int modulusSizeInBits) 214 | override 215 | { 216 | EXPECT_EQ(EndEntityOrCA::MustBeEndEntity, endEntityOrCA); 217 | EXPECT_EQ(publicKeySizeInBits, modulusSizeInBits); 218 | checkedModulusSizeInBits = true; 219 | return Success; 220 | } 221 | 222 | const unsigned int publicKeySizeInBits; 223 | bool checkedDigestAlgorithm; 224 | bool checkedModulusSizeInBits; 225 | }; 226 | 227 | TEST_P(pkixcheck_CheckSignatureAlgorithm, CheckSignatureAlgorithm) 228 | { 229 | const CheckSignatureAlgorithmTestParams& params(GetParam()); 230 | 231 | Input signatureValueInput; 232 | ASSERT_EQ(Success, 233 | signatureValueInput.Init(params.signatureValue.data(), 234 | params.signatureValue.length())); 235 | 236 | pkixcheck_CheckSignatureAlgorithm_TrustDomain 237 | trustDomain(params.signatureLengthInBytes * 8); 238 | 239 | der::SignedDataWithSignature signedData; 240 | ASSERT_EQ(Success, 241 | signedData.algorithm.Init(params.signatureAlgorithmValue.data(), 242 | params.signatureAlgorithmValue.length())); 243 | 244 | ByteString dummySignature(params.signatureLengthInBytes, 0xDE); 245 | ASSERT_EQ(Success, 246 | signedData.signature.Init(dummySignature.data(), 247 | dummySignature.length())); 248 | 249 | ASSERT_EQ(params.expectedResult, 250 | CheckSignatureAlgorithm(trustDomain, EndEntityOrCA::MustBeEndEntity, 251 | signedData, signatureValueInput)); 252 | ASSERT_EQ(params.expectedResult == Success, 253 | trustDomain.checkedDigestAlgorithm); 254 | ASSERT_EQ(params.expectedResult == Success, 255 | trustDomain.checkedModulusSizeInBits); 256 | } 257 | 258 | INSTANTIATE_TEST_CASE_P( 259 | pkixcheck_CheckSignatureAlgorithm, pkixcheck_CheckSignatureAlgorithm, 260 | testing::ValuesIn(CHECKSIGNATUREALGORITHM_TEST_PARAMS)); 261 | 262 | class pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain 263 | : public DefaultCryptoTrustDomain 264 | { 265 | public: 266 | explicit pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain( 267 | const ByteString& issuer) 268 | : issuer(issuer) 269 | { 270 | } 271 | 272 | Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, 273 | Input cert, /*out*/ TrustLevel& trustLevel) override 274 | { 275 | trustLevel = InputEqualsByteString(cert, issuer) 276 | ? TrustLevel::TrustAnchor 277 | : TrustLevel::InheritsTrust; 278 | return Success; 279 | } 280 | 281 | Result FindIssuer(Input, IssuerChecker& checker, Time) override 282 | { 283 | EXPECT_FALSE(ENCODING_FAILED(issuer)); 284 | 285 | Input issuerInput; 286 | EXPECT_EQ(Success, issuerInput.Init(issuer.data(), issuer.length())); 287 | 288 | bool keepGoing; 289 | EXPECT_EQ(Success, checker.Check(issuerInput, nullptr, keepGoing)); 290 | EXPECT_FALSE(keepGoing); 291 | 292 | return Success; 293 | } 294 | 295 | Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, 296 | /*optional*/ const Input*, 297 | /*optional*/ const Input*) override 298 | { 299 | return Success; 300 | } 301 | 302 | Result IsChainValid(const DERArray&, Time) override 303 | { 304 | return Success; 305 | } 306 | 307 | ByteString issuer; 308 | }; 309 | 310 | // Test that CheckSignatureAlgorithm actually gets called at some point when 311 | // BuildCertChain is called. 312 | TEST_F(pkixcheck_CheckSignatureAlgorithm, BuildCertChain) 313 | { 314 | ScopedTestKeyPair keyPair(CloneReusedKeyPair()); 315 | ASSERT_TRUE(keyPair.get()); 316 | 317 | ByteString issuerExtensions[2]; 318 | issuerExtensions[0] = CreateEncodedBasicConstraints(true, nullptr, 319 | Critical::No); 320 | ASSERT_FALSE(ENCODING_FAILED(issuerExtensions[0])); 321 | 322 | ByteString issuer(CreateEncodedCertificate(3, 323 | sha256WithRSAEncryption(), 324 | CreateEncodedSerialNumber(1), 325 | CNToDERName("issuer"), 326 | oneDayBeforeNow, oneDayAfterNow, 327 | CNToDERName("issuer"), 328 | *keyPair, 329 | issuerExtensions, 330 | *keyPair, 331 | sha256WithRSAEncryption())); 332 | ASSERT_FALSE(ENCODING_FAILED(issuer)); 333 | 334 | ByteString subject(CreateEncodedCertificate(3, 335 | sha1WithRSAEncryption(), 336 | CreateEncodedSerialNumber(2), 337 | CNToDERName("issuer"), 338 | oneDayBeforeNow, oneDayAfterNow, 339 | CNToDERName("subject"), 340 | *keyPair, 341 | nullptr, 342 | *keyPair, 343 | sha256WithRSAEncryption())); 344 | ASSERT_FALSE(ENCODING_FAILED(subject)); 345 | 346 | Input subjectInput; 347 | ASSERT_EQ(Success, subjectInput.Init(subject.data(), subject.length())); 348 | pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain 349 | trustDomain(issuer); 350 | Result rv = BuildCertChain(trustDomain, subjectInput, Now(), 351 | EndEntityOrCA::MustBeEndEntity, 352 | KeyUsage::noParticularKeyUsageRequired, 353 | KeyPurposeId::anyExtendedKeyUsage, 354 | CertPolicyId::anyPolicy, 355 | nullptr); 356 | ASSERT_EQ(Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH, rv); 357 | } 358 | -------------------------------------------------------------------------------- /lib/pkixbuild.cpp: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #include "pkix/pkix.h" 26 | 27 | #include "pkixcheck.h" 28 | #include "pkixutil.h" 29 | 30 | namespace mozilla { namespace pkix { 31 | 32 | static Result BuildForward(TrustDomain& trustDomain, 33 | const BackCert& subject, 34 | Time time, 35 | KeyUsage requiredKeyUsageIfPresent, 36 | KeyPurposeId requiredEKUIfPresent, 37 | const CertPolicyId& requiredPolicy, 38 | /*optional*/ const Input* stapledOCSPResponse, 39 | unsigned int subCACount); 40 | 41 | TrustDomain::IssuerChecker::IssuerChecker() { } 42 | TrustDomain::IssuerChecker::~IssuerChecker() { } 43 | 44 | // The implementation of TrustDomain::IssuerTracker is in a subclass only to 45 | // hide the implementation from external users. 46 | class PathBuildingStep final : public TrustDomain::IssuerChecker 47 | { 48 | public: 49 | PathBuildingStep(TrustDomain& trustDomain, const BackCert& subject, 50 | Time time, KeyPurposeId requiredEKUIfPresent, 51 | const CertPolicyId& requiredPolicy, 52 | /*optional*/ const Input* stapledOCSPResponse, 53 | unsigned int subCACount, Result deferredSubjectError) 54 | : trustDomain(trustDomain) 55 | , subject(subject) 56 | , time(time) 57 | , requiredEKUIfPresent(requiredEKUIfPresent) 58 | , requiredPolicy(requiredPolicy) 59 | , stapledOCSPResponse(stapledOCSPResponse) 60 | , subCACount(subCACount) 61 | , deferredSubjectError(deferredSubjectError) 62 | , result(Result::FATAL_ERROR_LIBRARY_FAILURE) 63 | , resultWasSet(false) 64 | { 65 | } 66 | 67 | Result Check(Input potentialIssuerDER, 68 | /*optional*/ const Input* additionalNameConstraints, 69 | /*out*/ bool& keepGoing) override; 70 | 71 | Result CheckResult() const; 72 | 73 | private: 74 | TrustDomain& trustDomain; 75 | const BackCert& subject; 76 | const Time time; 77 | const KeyPurposeId requiredEKUIfPresent; 78 | const CertPolicyId& requiredPolicy; 79 | /*optional*/ Input const* const stapledOCSPResponse; 80 | const unsigned int subCACount; 81 | const Result deferredSubjectError; 82 | 83 | // Initialized lazily. 84 | uint8_t subjectSignatureDigestBuf[MAX_DIGEST_SIZE_IN_BYTES]; 85 | der::PublicKeyAlgorithm subjectSignaturePublicKeyAlg; 86 | SignedDigest subjectSignature; 87 | 88 | Result RecordResult(Result currentResult, /*out*/ bool& keepGoing); 89 | Result result; 90 | bool resultWasSet; 91 | 92 | PathBuildingStep(const PathBuildingStep&) = delete; 93 | void operator=(const PathBuildingStep&) = delete; 94 | }; 95 | 96 | Result 97 | PathBuildingStep::RecordResult(Result newResult, /*out*/ bool& keepGoing) 98 | { 99 | if (newResult == Result::ERROR_UNTRUSTED_CERT) { 100 | newResult = Result::ERROR_UNTRUSTED_ISSUER; 101 | } else if (newResult == Result::ERROR_EXPIRED_CERTIFICATE) { 102 | newResult = Result::ERROR_EXPIRED_ISSUER_CERTIFICATE; 103 | } else if (newResult == Result::ERROR_NOT_YET_VALID_CERTIFICATE) { 104 | newResult = Result::ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE; 105 | } 106 | 107 | if (resultWasSet) { 108 | if (result == Success) { 109 | return NotReached("RecordResult called after finding a chain", 110 | Result::FATAL_ERROR_INVALID_STATE); 111 | } 112 | // If every potential issuer has the same problem (e.g. expired) and/or if 113 | // there is only one bad potential issuer, then return a more specific 114 | // error. Otherwise, punt on trying to decide which error should be 115 | // returned by returning the generic Result::ERROR_UNKNOWN_ISSUER error. 116 | if (newResult != Success && newResult != result) { 117 | newResult = Result::ERROR_UNKNOWN_ISSUER; 118 | } 119 | } 120 | 121 | result = newResult; 122 | resultWasSet = true; 123 | keepGoing = result != Success; 124 | return Success; 125 | } 126 | 127 | Result 128 | PathBuildingStep::CheckResult() const 129 | { 130 | if (!resultWasSet) { 131 | return Result::ERROR_UNKNOWN_ISSUER; 132 | } 133 | return result; 134 | } 135 | 136 | // The code that executes in the inner loop of BuildForward 137 | Result 138 | PathBuildingStep::Check(Input potentialIssuerDER, 139 | /*optional*/ const Input* additionalNameConstraints, 140 | /*out*/ bool& keepGoing) 141 | { 142 | BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA, 143 | &subject); 144 | Result rv = potentialIssuer.Init(); 145 | if (rv != Success) { 146 | return RecordResult(rv, keepGoing); 147 | } 148 | 149 | // Simple TrustDomain::FindIssuers implementations may pass in all possible 150 | // CA certificates without any filtering. Because of this, we don't consider 151 | // a mismatched name to be an error. Instead, we just pretend that any 152 | // certificate without a matching name was never passed to us. In particular, 153 | // we treat the case where the TrustDomain only asks us to check CA 154 | // certificates with mismatched names as equivalent to the case where the 155 | // TrustDomain never called Check() at all. 156 | if (!InputsAreEqual(potentialIssuer.GetSubject(), subject.GetIssuer())) { 157 | keepGoing = true; 158 | return Success; 159 | } 160 | 161 | // Loop prevention, done as recommended by RFC4158 Section 5.2 162 | // TODO: this doesn't account for subjectAltNames! 163 | // TODO(perf): This probably can and should be optimized in some way. 164 | bool loopDetected = false; 165 | for (const BackCert* prev = potentialIssuer.childCert; 166 | !loopDetected && prev != nullptr; prev = prev->childCert) { 167 | if (InputsAreEqual(potentialIssuer.GetSubjectPublicKeyInfo(), 168 | prev->GetSubjectPublicKeyInfo()) && 169 | InputsAreEqual(potentialIssuer.GetSubject(), prev->GetSubject())) { 170 | // XXX: error code 171 | return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing); 172 | } 173 | } 174 | 175 | if (potentialIssuer.GetNameConstraints()) { 176 | rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(), 177 | subject, requiredEKUIfPresent); 178 | if (rv != Success) { 179 | return RecordResult(rv, keepGoing); 180 | } 181 | } 182 | 183 | if (additionalNameConstraints) { 184 | rv = CheckNameConstraints(*additionalNameConstraints, subject, 185 | requiredEKUIfPresent); 186 | if (rv != Success) { 187 | return RecordResult(rv, keepGoing); 188 | } 189 | } 190 | 191 | // RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the 192 | // subject public key MUST NOT be used to verify signatures on certificates 193 | // or CRLs unless the corresponding keyCertSign or cRLSign bit is set." 194 | rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign, 195 | requiredEKUIfPresent, requiredPolicy, nullptr, subCACount); 196 | if (rv != Success) { 197 | return RecordResult(rv, keepGoing); 198 | } 199 | 200 | // Calculate the digest of the subject's signed data if we haven't already 201 | // done so. We do this lazily to avoid doing it at all if we backtrack before 202 | // getting to this point. We cache the result to avoid recalculating it if we 203 | // backtrack after getting to this point. 204 | if (subjectSignature.digest.GetLength() == 0) { 205 | rv = DigestSignedData(trustDomain, subject.GetSignedData(), 206 | subjectSignatureDigestBuf, 207 | subjectSignaturePublicKeyAlg, subjectSignature); 208 | if (rv != Success) { 209 | return rv; 210 | } 211 | } 212 | 213 | rv = VerifySignedDigest(trustDomain, subjectSignaturePublicKeyAlg, 214 | subjectSignature, 215 | potentialIssuer.GetSubjectPublicKeyInfo()); 216 | if (rv != Success) { 217 | return RecordResult(rv, keepGoing); 218 | } 219 | 220 | // We avoid doing revocation checking for expired certificates because OCSP 221 | // responders are allowed to forget about expired certificates, and many OCSP 222 | // responders return an error when asked for the status of an expired 223 | // certificate. 224 | if (deferredSubjectError != Result::ERROR_EXPIRED_CERTIFICATE) { 225 | CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(), 226 | subject.GetSerialNumber()); 227 | Time notBefore(Time::uninitialized); 228 | Time notAfter(Time::uninitialized); 229 | // This should never fail. If we're here, we've already checked that the 230 | // given time is in the certificate's validity period. 231 | rv = CheckValidity(subject.GetValidity(), time, ¬Before, ¬After); 232 | if (rv != Success) { 233 | return rv; 234 | } 235 | Duration validityDuration(notAfter, notBefore); 236 | rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time, 237 | validityDuration, stapledOCSPResponse, 238 | subject.GetAuthorityInfoAccess()); 239 | if (rv != Success) { 240 | return RecordResult(rv, keepGoing); 241 | } 242 | } 243 | 244 | return RecordResult(Success, keepGoing); 245 | } 246 | 247 | // Recursively build the path from the given subject certificate to the root. 248 | // 249 | // Be very careful about changing the order of checks. The order is significant 250 | // because it affects which error we return when a certificate or certificate 251 | // chain has multiple problems. See the error ranking documentation in 252 | // pkix/pkix.h. 253 | static Result 254 | BuildForward(TrustDomain& trustDomain, 255 | const BackCert& subject, 256 | Time time, 257 | KeyUsage requiredKeyUsageIfPresent, 258 | KeyPurposeId requiredEKUIfPresent, 259 | const CertPolicyId& requiredPolicy, 260 | /*optional*/ const Input* stapledOCSPResponse, 261 | unsigned int subCACount) 262 | { 263 | Result rv; 264 | 265 | TrustLevel trustLevel; 266 | // If this is an end-entity and not a trust anchor, we defer reporting 267 | // any error found here until after attempting to find a valid chain. 268 | // See the explanation of error prioritization in pkix.h. 269 | rv = CheckIssuerIndependentProperties(trustDomain, subject, time, 270 | requiredKeyUsageIfPresent, 271 | requiredEKUIfPresent, requiredPolicy, 272 | subCACount, trustLevel); 273 | Result deferredEndEntityError = Success; 274 | if (rv != Success) { 275 | if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity && 276 | trustLevel != TrustLevel::TrustAnchor) { 277 | deferredEndEntityError = rv; 278 | } else { 279 | return rv; 280 | } 281 | } 282 | 283 | if (trustLevel == TrustLevel::TrustAnchor) { 284 | // End of the recursion. 285 | 286 | NonOwningDERArray chain; 287 | for (const BackCert* cert = &subject; cert; cert = cert->childCert) { 288 | rv = chain.Append(cert->GetDER()); 289 | if (rv != Success) { 290 | return NotReached("NonOwningDERArray::SetItem failed.", rv); 291 | } 292 | } 293 | 294 | // This must be done here, after the chain is built but before any 295 | // revocation checks have been done. 296 | return trustDomain.IsChainValid(chain, time); 297 | } 298 | 299 | if (subject.endEntityOrCA == EndEntityOrCA::MustBeCA) { 300 | // Avoid stack overflows and poor performance by limiting cert chain 301 | // length. 302 | static const unsigned int MAX_SUBCA_COUNT = 6; 303 | static_assert(1/*end-entity*/ + MAX_SUBCA_COUNT + 1/*root*/ == 304 | NonOwningDERArray::MAX_LENGTH, 305 | "MAX_SUBCA_COUNT and NonOwningDERArray::MAX_LENGTH mismatch."); 306 | if (subCACount >= MAX_SUBCA_COUNT) { 307 | return Result::ERROR_UNKNOWN_ISSUER; 308 | } 309 | ++subCACount; 310 | } else { 311 | assert(subCACount == 0); 312 | } 313 | 314 | // Find a trusted issuer. 315 | 316 | PathBuildingStep pathBuilder(trustDomain, subject, time, 317 | requiredEKUIfPresent, requiredPolicy, 318 | stapledOCSPResponse, subCACount, 319 | deferredEndEntityError); 320 | 321 | // TODO(bug 965136): Add SKI/AKI matching optimizations 322 | rv = trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time); 323 | if (rv != Success) { 324 | return rv; 325 | } 326 | 327 | rv = pathBuilder.CheckResult(); 328 | if (rv != Success) { 329 | return rv; 330 | } 331 | 332 | // If we found a valid chain but deferred reporting an error with the 333 | // end-entity certificate, report it now. 334 | if (deferredEndEntityError != Success) { 335 | return deferredEndEntityError; 336 | } 337 | 338 | // We've built a valid chain from the subject cert up to a trusted root. 339 | return Success; 340 | } 341 | 342 | Result 343 | BuildCertChain(TrustDomain& trustDomain, Input certDER, 344 | Time time, EndEntityOrCA endEntityOrCA, 345 | KeyUsage requiredKeyUsageIfPresent, 346 | KeyPurposeId requiredEKUIfPresent, 347 | const CertPolicyId& requiredPolicy, 348 | /*optional*/ const Input* stapledOCSPResponse) 349 | { 350 | // XXX: Support the legacy use of the subject CN field for indicating the 351 | // domain name the certificate is valid for. 352 | BackCert cert(certDER, endEntityOrCA, nullptr); 353 | Result rv = cert.Init(); 354 | if (rv != Success) { 355 | return rv; 356 | } 357 | 358 | return BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent, 359 | requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse, 360 | 0/*subCACount*/); 361 | } 362 | 363 | } } // namespace mozilla::pkix 364 | -------------------------------------------------------------------------------- /include/pkix/pkixtypes.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 | /* This code is made available to you under your choice of the following sets 4 | * of licensing terms: 5 | */ 6 | /* This Source Code Form is subject to the terms of the Mozilla Public 7 | * License, v. 2.0. If a copy of the MPL was not distributed with this 8 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 | */ 10 | /* Copyright 2013 Mozilla Contributors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | #ifndef mozilla_pkix_pkixtypes_h 26 | #define mozilla_pkix_pkixtypes_h 27 | 28 | #include "pkix/Input.h" 29 | #include "pkix/Time.h" 30 | #include "stdint.h" 31 | 32 | namespace mozilla { namespace pkix { 33 | 34 | enum class DigestAlgorithm 35 | { 36 | sha512 = 1, 37 | sha384 = 2, 38 | sha256 = 3, 39 | sha1 = 4, 40 | }; 41 | 42 | enum class NamedCurve 43 | { 44 | // secp521r1 (OID 1.3.132.0.35, RFC 5480) 45 | secp521r1 = 1, 46 | 47 | // secp384r1 (OID 1.3.132.0.34, RFC 5480) 48 | secp384r1 = 2, 49 | 50 | // secp256r1 (OID 1.2.840.10045.3.1.7, RFC 5480) 51 | secp256r1 = 3, 52 | }; 53 | 54 | struct SignedDigest final 55 | { 56 | Input digest; 57 | DigestAlgorithm digestAlgorithm; 58 | Input signature; 59 | 60 | void operator=(const SignedDigest&) = delete; 61 | }; 62 | 63 | enum class EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 }; 64 | 65 | enum class KeyUsage : uint8_t 66 | { 67 | digitalSignature = 0, 68 | nonRepudiation = 1, 69 | keyEncipherment = 2, 70 | dataEncipherment = 3, 71 | keyAgreement = 4, 72 | keyCertSign = 5, 73 | // cRLSign = 6, 74 | // encipherOnly = 7, 75 | // decipherOnly = 8, 76 | noParticularKeyUsageRequired = 0xff, 77 | }; 78 | 79 | enum class KeyPurposeId 80 | { 81 | anyExtendedKeyUsage = 0, 82 | id_kp_serverAuth = 1, // id-kp-serverAuth 83 | id_kp_clientAuth = 2, // id-kp-clientAuth 84 | id_kp_codeSigning = 3, // id-kp-codeSigning 85 | id_kp_emailProtection = 4, // id-kp-emailProtection 86 | id_kp_OCSPSigning = 9, // id-kp-OCSPSigning 87 | }; 88 | 89 | struct CertPolicyId final 90 | { 91 | uint16_t numBytes; 92 | static const uint16_t MAX_BYTES = 24; 93 | uint8_t bytes[MAX_BYTES]; 94 | 95 | bool IsAnyPolicy() const; 96 | 97 | static const CertPolicyId anyPolicy; 98 | }; 99 | 100 | enum class TrustLevel 101 | { 102 | TrustAnchor = 1, // certificate is a trusted root CA certificate or 103 | // equivalent *for the given policy*. 104 | ActivelyDistrusted = 2, // certificate is known to be bad 105 | InheritsTrust = 3 // certificate must chain to a trust anchor 106 | }; 107 | 108 | // CertID references the information needed to do revocation checking for the 109 | // certificate issued by the given issuer with the given serial number. 110 | // 111 | // issuer must be the DER-encoded issuer field from the certificate for which 112 | // revocation checking is being done, **NOT** the subject field of the issuer 113 | // certificate. (Those two fields must be equal to each other, but they may not 114 | // be encoded exactly the same, and the encoding matters for OCSP.) 115 | // issuerSubjectPublicKeyInfo is the entire DER-encoded subjectPublicKeyInfo 116 | // field from the issuer's certificate. serialNumber is the entire DER-encoded 117 | // serial number from the subject certificate (the certificate for which we are 118 | // checking the revocation status). 119 | struct CertID final 120 | { 121 | public: 122 | CertID(Input issuer, Input issuerSubjectPublicKeyInfo, Input serialNumber) 123 | : issuer(issuer) 124 | , issuerSubjectPublicKeyInfo(issuerSubjectPublicKeyInfo) 125 | , serialNumber(serialNumber) 126 | { 127 | } 128 | const Input issuer; 129 | const Input issuerSubjectPublicKeyInfo; 130 | const Input serialNumber; 131 | 132 | void operator=(const CertID&) = delete; 133 | }; 134 | 135 | class DERArray 136 | { 137 | public: 138 | // Returns the number of DER-encoded items in the array. 139 | virtual size_t GetLength() const = 0; 140 | 141 | // Returns a weak (non-owning) pointer the ith DER-encoded item in the array 142 | // (0-indexed). The result is guaranteed to be non-null if i < GetLength(), 143 | // and the result is guaranteed to be nullptr if i >= GetLength(). 144 | virtual const Input* GetDER(size_t i) const = 0; 145 | protected: 146 | DERArray() { } 147 | virtual ~DERArray() { } 148 | }; 149 | 150 | // Applications control the behavior of path building and verification by 151 | // implementing the TrustDomain interface. The TrustDomain is used for all 152 | // cryptography and for determining which certificates are trusted or 153 | // distrusted. 154 | class TrustDomain 155 | { 156 | public: 157 | virtual ~TrustDomain() { } 158 | 159 | // Determine the level of trust in the given certificate for the given role. 160 | // This will be called for every certificate encountered during path 161 | // building. 162 | // 163 | // When policy.IsAnyPolicy(), then no policy-related checking should be done. 164 | // When !policy.IsAnyPolicy(), then GetCertTrust MUST NOT return with 165 | // trustLevel == TrustAnchor unless the given cert is considered a trust 166 | // anchor *for that policy*. In particular, if the user has marked an 167 | // intermediate certificate as trusted, but that intermediate isn't in the 168 | // list of EV roots, then GetCertTrust must result in 169 | // trustLevel == InheritsTrust instead of trustLevel == TrustAnchor 170 | // (assuming the candidate cert is not actively distrusted). 171 | virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, 172 | const CertPolicyId& policy, 173 | Input candidateCertDER, 174 | /*out*/ TrustLevel& trustLevel) = 0; 175 | 176 | class IssuerChecker 177 | { 178 | public: 179 | // potentialIssuerDER is the complete DER encoding of the certificate to 180 | // be checked as a potential issuer. 181 | // 182 | // If additionalNameConstraints is not nullptr then it must point to an 183 | // encoded NameConstraints extension value; in that case, those name 184 | // constraints will be checked in addition to any any name constraints 185 | // contained in potentialIssuerDER. 186 | virtual Result Check(Input potentialIssuerDER, 187 | /*optional*/ const Input* additionalNameConstraints, 188 | /*out*/ bool& keepGoing) = 0; 189 | protected: 190 | IssuerChecker(); 191 | virtual ~IssuerChecker(); 192 | 193 | IssuerChecker(const IssuerChecker&) = delete; 194 | void operator=(const IssuerChecker&) = delete; 195 | }; 196 | 197 | // Search for a CA certificate with the given name. The implementation must 198 | // call checker.Check with the DER encoding of the potential issuer 199 | // certificate. The implementation must follow these rules: 200 | // 201 | // * The implementation must be reentrant and must limit the amount of stack 202 | // space it uses; see the note on reentrancy and stack usage below. 203 | // * When checker.Check does not return Success then immediately return its 204 | // return value. 205 | // * When checker.Check returns Success and sets keepGoing = false, then 206 | // immediately return Success. 207 | // * When checker.Check returns Success and sets keepGoing = true, then 208 | // call checker.Check again with a different potential issuer certificate, 209 | // if any more are available. 210 | // * When no more potential issuer certificates are available, return 211 | // Success. 212 | // * Don't call checker.Check with the same potential issuer certificate more 213 | // than once in a given call of FindIssuer. 214 | // * The given time parameter may be used to filter out certificates that are 215 | // not valid at the given time, or it may be ignored. 216 | // 217 | // Note on reentrancy and stack usage: checker.Check will attempt to 218 | // recursively build a certificate path from the potential issuer it is given 219 | // to a trusted root, as determined by this TrustDomain. That means that 220 | // checker.Check may call any/all of the methods on this TrustDomain. In 221 | // particular, there will be call stacks that look like this: 222 | // 223 | // BuildCertChain 224 | // [...] 225 | // TrustDomain::FindIssuer 226 | // [...] 227 | // IssuerChecker::Check 228 | // [...] 229 | // TrustDomain::FindIssuer 230 | // [...] 231 | // IssuerChecker::Check 232 | // [...] 233 | // 234 | // checker.Check is responsible for limiting the recursion to a reasonable 235 | // limit. 236 | // 237 | // checker.Check will verify that the subject's issuer field matches the 238 | // potential issuer's subject field. It will also check that the potential 239 | // issuer is valid at the given time. However, if the FindIssuer 240 | // implementation has an efficient way of filtering potential issuers by name 241 | // and/or validity period itself, then it is probably better for performance 242 | // for it to do so. 243 | virtual Result FindIssuer(Input encodedIssuerName, 244 | IssuerChecker& checker, Time time) = 0; 245 | 246 | // Called as soon as we think we have a valid chain but before revocation 247 | // checks are done. This function can be used to compute additional checks, 248 | // especially checks that require the entire certificate chain. This callback 249 | // can also be used to save a copy of the built certificate chain for later 250 | // use. 251 | // 252 | // This function may be called multiple times, regardless of whether it 253 | // returns success or failure. It is guaranteed that BuildCertChain will not 254 | // return Success unless the last call to IsChainValid returns Success. Further, 255 | // it is guaranteed that when BuildCertChain returns Success the last chain 256 | // passed to IsChainValid is the valid chain that should be used for further 257 | // operations that require the whole chain. 258 | // 259 | // Keep in mind, in particular, that if the application saves a copy of the 260 | // certificate chain the last invocation of IsChainValid during a validation, 261 | // it is still possible for BuildCertChain to fail, in which case the 262 | // application must not assume anything about the validity of the last 263 | // certificate chain passed to IsChainValid; especially, it would be very 264 | // wrong to assume that the certificate chain is valid. 265 | // 266 | // certChain.GetDER(0) is the trust anchor. 267 | virtual Result IsChainValid(const DERArray& certChain, Time time) = 0; 268 | 269 | virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA, 270 | const CertID& certID, Time time, 271 | Duration validityDuration, 272 | /*optional*/ const Input* stapledOCSPresponse, 273 | /*optional*/ const Input* aiaExtension) = 0; 274 | 275 | // Check that the given digest algorithm is acceptable for use in signatures. 276 | // 277 | // Return Success if the algorithm is acceptable, 278 | // Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED if the algorithm is not 279 | // acceptable, or another error code if another error occurred. 280 | virtual Result CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg, 281 | EndEntityOrCA endEntityOrCA) = 0; 282 | 283 | // Check that the RSA public key size is acceptable. 284 | // 285 | // Return Success if the key size is acceptable, 286 | // Result::ERROR_INADEQUATE_KEY_SIZE if the key size is not acceptable, 287 | // or another error code if another error occurred. 288 | virtual Result CheckRSAPublicKeyModulusSizeInBits( 289 | EndEntityOrCA endEntityOrCA, 290 | unsigned int modulusSizeInBits) = 0; 291 | 292 | // Verify the given RSA PKCS#1.5 signature on the given digest using the 293 | // given RSA public key. 294 | // 295 | // CheckRSAPublicKeyModulusSizeInBits will be called before calling this 296 | // function, so it is not necessary to repeat those checks here. However, 297 | // VerifyRSAPKCS1SignedDigest *is* responsible for doing the mathematical 298 | // verification of the public key validity as specified in NIST SP 800-56A. 299 | virtual Result VerifyRSAPKCS1SignedDigest( 300 | const SignedDigest& signedDigest, 301 | Input subjectPublicKeyInfo) = 0; 302 | 303 | // Check that the given named ECC curve is acceptable for ECDSA signatures. 304 | // 305 | // Return Success if the curve is acceptable, 306 | // Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE if the curve is not acceptable, 307 | // or another error code if another error occurred. 308 | virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA, 309 | NamedCurve curve) = 0; 310 | 311 | // Verify the given ECDSA signature on the given digest using the given ECC 312 | // public key. 313 | // 314 | // CheckECDSACurveIsAcceptable will be called before calling this function, 315 | // so it is not necessary to repeat that check here. However, 316 | // VerifyECDSASignedDigest *is* responsible for doing the mathematical 317 | // verification of the public key validity as specified in NIST SP 800-56A. 318 | virtual Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, 319 | Input subjectPublicKeyInfo) = 0; 320 | 321 | // Check that the validity duration is acceptable. 322 | // 323 | // Return Success if the validity duration is acceptable, 324 | // Result::ERROR_VALIDITY_TOO_LONG if the validity duration is not acceptable, 325 | // or another error code if another error occurred. 326 | virtual Result CheckValidityIsAcceptable(Time notBefore, Time notAfter, 327 | EndEntityOrCA endEntityOrCA, 328 | KeyPurposeId keyPurpose) = 0; 329 | 330 | // Compute a digest of the data in item using the given digest algorithm. 331 | // 332 | // item contains the data to hash. 333 | // digestBuf points to a buffer to where the digest will be written. 334 | // digestBufLen will be the size of the digest output (20 for SHA-1, 335 | // 32 for SHA-256, etc.). 336 | // 337 | // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our 338 | // other, extensive, memory safety efforts in mozilla::pkix, and we should 339 | // find a way to provide a more-obviously-safe interface. 340 | virtual Result DigestBuf(Input item, 341 | DigestAlgorithm digestAlg, 342 | /*out*/ uint8_t* digestBuf, 343 | size_t digestBufLen) = 0; 344 | protected: 345 | TrustDomain() { } 346 | 347 | TrustDomain(const TrustDomain&) = delete; 348 | void operator=(const TrustDomain&) = delete; 349 | }; 350 | 351 | } } // namespace mozilla::pkix 352 | 353 | #endif // mozilla_pkix_pkixtypes_h 354 | --------------------------------------------------------------------------------