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