summaryrefslogtreecommitdiff
path: root/security/nss/gtests/mozpkix_gtest/pkixcheck_CheckKeyUsage_tests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/gtests/mozpkix_gtest/pkixcheck_CheckKeyUsage_tests.cpp')
-rw-r--r--security/nss/gtests/mozpkix_gtest/pkixcheck_CheckKeyUsage_tests.cpp284
1 files changed, 284 insertions, 0 deletions
diff --git a/security/nss/gtests/mozpkix_gtest/pkixcheck_CheckKeyUsage_tests.cpp b/security/nss/gtests/mozpkix_gtest/pkixcheck_CheckKeyUsage_tests.cpp
new file mode 100644
index 0000000000..136f8719a8
--- /dev/null
+++ b/security/nss/gtests/mozpkix_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, &notGood, usage));
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood, usage));
+
+ // Test that none of the other non-padding bits are mistaken for the given
+ // key usage in the two-byte value case.
+ const uint8_t twoByteNotGoodData[] = {
+ 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, unusedBits,
+ static_cast<uint8_t>(~bits),
+ static_cast<uint8_t>((0xFFu >> unusedBits) << unusedBits)
+ };
+ Input twoByteNotGood(twoByteNotGoodData);
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
+ usage));
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood, usage));
+}
+
+TEST_F(pkixcheck_CheckKeyUsage, simpleCases)
+{
+ ASSERT_SimpleCase(7, 0x80, KeyUsage::digitalSignature);
+ ASSERT_SimpleCase(6, 0x40, KeyUsage::nonRepudiation);
+ ASSERT_SimpleCase(5, 0x20, KeyUsage::keyEncipherment);
+ ASSERT_SimpleCase(4, 0x10, KeyUsage::dataEncipherment);
+ ASSERT_SimpleCase(3, 0x08, KeyUsage::keyAgreement);
+}
+
+// Only CAs are allowed to assert keyCertSign.
+// End-entity certs may assert it along with other key usages if keyCertSign
+// isn't the required key usage. This is for compatibility.
+TEST_F(pkixcheck_CheckKeyUsage, keyCertSign)
+{
+ NAMED_SIMPLE_KU(good, 2, 0x04);
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good,
+ KeyUsage::keyCertSign));
+ ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, &good,
+ KeyUsage::keyCertSign));
+
+ // Test that none of the other non-padding bits are mistaken for the given
+ // key usage in the one-byte value case.
+ NAMED_SIMPLE_KU(notGood, 2, 0xFB);
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &notGood,
+ KeyUsage::keyCertSign));
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood,
+ KeyUsage::keyCertSign));
+
+ // Test that none of the other non-padding bits are mistaken for the given
+ // key usage in the two-byte value case.
+ static uint8_t twoByteNotGoodData[] = {
+ 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 2/*unused bits*/, 0xFBu, 0xFCu
+ };
+ static const Input twoByteNotGood(twoByteNotGoodData);
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
+ KeyUsage::keyCertSign));
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood,
+ KeyUsage::keyCertSign));
+
+ // If an end-entity certificate does assert keyCertSign, this is allowed
+ // as long as that isn't the required key usage.
+ NAMED_SIMPLE_KU(digitalSignatureAndKeyCertSign, 2, 0x84);
+ ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
+ &digitalSignatureAndKeyCertSign,
+ KeyUsage::digitalSignature));
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
+ &digitalSignatureAndKeyCertSign,
+ KeyUsage::keyCertSign));
+}
+
+TEST_F(pkixcheck_CheckKeyUsage, unusedBitNotZero)
+{
+ // single byte control case
+ static uint8_t controlOneValueByteData[] = {
+ 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80
+ };
+ static const Input controlOneValueByte(controlOneValueByteData);
+ ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
+ &controlOneValueByte,
+ KeyUsage::digitalSignature));
+ ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA,
+ &controlOneValueByte,
+ KeyUsage::digitalSignature));
+
+ // single-byte test case
+ static uint8_t oneValueByteData[] = {
+ 0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 | 0x01
+ };
+ static const Input oneValueByte(oneValueByteData);
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte,
+ KeyUsage::digitalSignature));
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &oneValueByte,
+ KeyUsage::digitalSignature));
+
+ // two-byte control case
+ static uint8_t controlTwoValueBytesData[] = {
+ 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
+ 0x80 | 0x01, 0x80
+ };
+ static const Input controlTwoValueBytes(controlTwoValueBytesData);
+ ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
+ &controlTwoValueBytes,
+ KeyUsage::digitalSignature));
+ ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA,
+ &controlTwoValueBytes,
+ KeyUsage::digitalSignature));
+
+ // two-byte test case
+ static uint8_t twoValueBytesData[] = {
+ 0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
+ 0x80 | 0x01, 0x80 | 0x01
+ };
+ static const Input twoValueBytes(twoValueBytesData);
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes,
+ KeyUsage::digitalSignature));
+ ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoValueBytes,
+ KeyUsage::digitalSignature));
+}