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