| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
| /* This code is made available to you under your choice of the following sets |
| * of licensing terms: |
| */ |
| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| */ |
| /* Copyright 2013 Mozilla Contributors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "pkixgtest.h" |
| |
| using namespace mozilla::pkix; |
| using namespace mozilla::pkix::test; |
| |
| namespace mozilla { namespace pkix { |
| |
| extern Result CheckKeyUsage(EndEntityOrCA endEntityOrCA, |
| const Input* encodedKeyUsage, |
| KeyUsage requiredKeyUsageIfPresent); |
| |
| } } // namespace mozilla::pkix |
| |
| class pkixcheck_CheckKeyUsage : public ::testing::Test { }; |
| |
| #define ASSERT_BAD(x) ASSERT_EQ(Result::ERROR_INADEQUATE_KEY_USAGE, x) |
| |
| // Make it easy to define test data for the common, simplest cases. |
| #define NAMED_SIMPLE_KU(name, unusedBits, bits) \ |
| const uint8_t name##_bytes[4] = { \ |
| 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, unusedBits, bits \ |
| }; \ |
| const Input name(name##_bytes); |
| |
| static const Input empty_null; |
| |
| // Note that keyCertSign is really the only interesting case for CA |
| // certificates since we don't support cRLSign. |
| |
| TEST_F(pkixcheck_CheckKeyUsage, EE_none) |
| { |
| // The input Input is nullptr. This means the cert had no keyUsage |
| // extension. This is always valid because no key usage in an end-entity |
| // means that there are no key usage restrictions. |
| |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, |
| KeyUsage::noParticularKeyUsageRequired)); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, |
| KeyUsage::digitalSignature)); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, |
| KeyUsage::nonRepudiation)); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, |
| KeyUsage::keyEncipherment)); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, |
| KeyUsage::dataEncipherment)); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr, |
| KeyUsage::keyAgreement)); |
| } |
| |
| TEST_F(pkixcheck_CheckKeyUsage, EE_empty) |
| { |
| // The input Input is empty. The cert had an empty keyUsage extension, |
| // which is syntactically invalid. |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null, |
| KeyUsage::digitalSignature)); |
| static const uint8_t dummy = 0x00; |
| Input empty_nonnull; |
| ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull, |
| KeyUsage::digitalSignature)); |
| } |
| |
| TEST_F(pkixcheck_CheckKeyUsage, CA_none) |
| { |
| // A CA certificate does not have a KU extension. |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr, |
| KeyUsage::keyCertSign)); |
| } |
| |
| TEST_F(pkixcheck_CheckKeyUsage, CA_empty) |
| { |
| // A CA certificate has an empty KU extension. |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_null, |
| KeyUsage::keyCertSign)); |
| static const uint8_t dummy = 0x00; |
| Input empty_nonnull; |
| ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull, |
| KeyUsage::keyCertSign)); |
| } |
| |
| TEST_F(pkixcheck_CheckKeyUsage, maxUnusedBits) |
| { |
| NAMED_SIMPLE_KU(encoded, 7, 0x80); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded, |
| KeyUsage::digitalSignature)); |
| } |
| |
| TEST_F(pkixcheck_CheckKeyUsage, tooManyUnusedBits) |
| { |
| static uint8_t oneValueByteData[] = { |
| 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 8/*unused bits*/, 0x80 |
| }; |
| static const Input oneValueByte(oneValueByteData); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte, |
| KeyUsage::digitalSignature)); |
| |
| static uint8_t twoValueBytesData[] = { |
| 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 8/*unused bits*/, 0x01, 0x00 |
| }; |
| static const Input twoValueBytes(twoValueBytesData); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes, |
| KeyUsage::digitalSignature)); |
| } |
| |
| TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_NoPaddingBits) |
| { |
| static const uint8_t DER_BYTES[] = { |
| 0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 0/*unused bits*/ |
| }; |
| static const Input DER(DER_BYTES); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER, |
| KeyUsage::digitalSignature)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER, |
| KeyUsage::keyCertSign)); |
| } |
| |
| TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_7PaddingBits) |
| { |
| static const uint8_t DER_BYTES[] = { |
| 0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 7/*unused bits*/ |
| }; |
| static const Input DER(DER_BYTES); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER, |
| KeyUsage::digitalSignature)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER, |
| KeyUsage::keyCertSign)); |
| } |
| |
| void ASSERT_SimpleCase(uint8_t unusedBits, uint8_t bits, KeyUsage usage) |
| { |
| // Test that only the right bit is accepted for the usage for both EE and CA |
| // certs. |
| NAMED_SIMPLE_KU(good, unusedBits, bits); |
| ASSERT_EQ(Success, |
| CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, usage)); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, usage)); |
| |
| // We use (~bits >> unusedBits) << unusedBits) instead of using the same |
| // calculation that is in CheckKeyUsage to validate that the calculation in |
| // CheckKeyUsage is correct. |
| |
| // Test that none of the other non-padding bits are mistaken for the given |
| // key usage in the single-byte value case. |
| NAMED_SIMPLE_KU(notGood, unusedBits, |
| static_cast<uint8_t>((~bits >> unusedBits) << unusedBits)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, ¬Good, usage)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, ¬Good, usage)); |
| |
| // Test that none of the other non-padding bits are mistaken for the given |
| // key usage in the two-byte value case. |
| const uint8_t twoByteNotGoodData[] = { |
| 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, unusedBits, |
| static_cast<uint8_t>(~bits), |
| static_cast<uint8_t>((0xFFu >> unusedBits) << unusedBits) |
| }; |
| Input twoByteNotGood(twoByteNotGoodData); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood, |
| usage)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood, usage)); |
| } |
| |
| TEST_F(pkixcheck_CheckKeyUsage, simpleCases) |
| { |
| ASSERT_SimpleCase(7, 0x80, KeyUsage::digitalSignature); |
| ASSERT_SimpleCase(6, 0x40, KeyUsage::nonRepudiation); |
| ASSERT_SimpleCase(5, 0x20, KeyUsage::keyEncipherment); |
| ASSERT_SimpleCase(4, 0x10, KeyUsage::dataEncipherment); |
| ASSERT_SimpleCase(3, 0x08, KeyUsage::keyAgreement); |
| } |
| |
| // Only CAs are allowed to assert keyCertSign. |
| // End-entity certs may assert it along with other key usages if keyCertSign |
| // isn't the required key usage. This is for compatibility. |
| TEST_F(pkixcheck_CheckKeyUsage, keyCertSign) |
| { |
| NAMED_SIMPLE_KU(good, 2, 0x04); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, |
| KeyUsage::keyCertSign)); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, |
| KeyUsage::keyCertSign)); |
| |
| // Test that none of the other non-padding bits are mistaken for the given |
| // key usage in the one-byte value case. |
| NAMED_SIMPLE_KU(notGood, 2, 0xFB); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, ¬Good, |
| KeyUsage::keyCertSign)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, ¬Good, |
| KeyUsage::keyCertSign)); |
| |
| // Test that none of the other non-padding bits are mistaken for the given |
| // key usage in the two-byte value case. |
| static uint8_t twoByteNotGoodData[] = { |
| 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 2/*unused bits*/, 0xFBu, 0xFCu |
| }; |
| static const Input twoByteNotGood(twoByteNotGoodData); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood, |
| KeyUsage::keyCertSign)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood, |
| KeyUsage::keyCertSign)); |
| |
| // If an end-entity certificate does assert keyCertSign, this is allowed |
| // as long as that isn't the required key usage. |
| NAMED_SIMPLE_KU(digitalSignatureAndKeyCertSign, 2, 0x84); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, |
| &digitalSignatureAndKeyCertSign, |
| KeyUsage::digitalSignature)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, |
| &digitalSignatureAndKeyCertSign, |
| KeyUsage::keyCertSign)); |
| } |
| |
| TEST_F(pkixcheck_CheckKeyUsage, unusedBitNotZero) |
| { |
| // single byte control case |
| static uint8_t controlOneValueByteData[] = { |
| 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 |
| }; |
| static const Input controlOneValueByte(controlOneValueByteData); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, |
| &controlOneValueByte, |
| KeyUsage::digitalSignature)); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, |
| &controlOneValueByte, |
| KeyUsage::digitalSignature)); |
| |
| // single-byte test case |
| static uint8_t oneValueByteData[] = { |
| 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 | 0x01 |
| }; |
| static const Input oneValueByte(oneValueByteData); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte, |
| KeyUsage::digitalSignature)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &oneValueByte, |
| KeyUsage::digitalSignature)); |
| |
| // two-byte control case |
| static uint8_t controlTwoValueBytesData[] = { |
| 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/, |
| 0x80 | 0x01, 0x80 |
| }; |
| static const Input controlTwoValueBytes(controlTwoValueBytesData); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, |
| &controlTwoValueBytes, |
| KeyUsage::digitalSignature)); |
| ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, |
| &controlTwoValueBytes, |
| KeyUsage::digitalSignature)); |
| |
| // two-byte test case |
| static uint8_t twoValueBytesData[] = { |
| 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/, |
| 0x80 | 0x01, 0x80 | 0x01 |
| }; |
| static const Input twoValueBytes(twoValueBytesData); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes, |
| KeyUsage::digitalSignature)); |
| ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoValueBytes, |
| KeyUsage::digitalSignature)); |
| } |