/* -*- 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.
  uint8_t paddingBits = (static_cast<uint8_t>(~bits) >> unusedBits) << unusedBits;
  NAMED_SIMPLE_KU(notGood, unusedBits, paddingBits);
  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));
}

TEST_F(pkixcheck_CheckKeyUsage, trailingData)
{
  static uint8_t keyUsageWithTrailingDataData[] = {
    0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80,
    // The BIT STRING has already ended, but there's trailing data
    0xab, 0xba
  };
  static const Input keyUsageWithTrailingDataBytes(keyUsageWithTrailingDataData);
  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
                           &keyUsageWithTrailingDataBytes,
                           KeyUsage::digitalSignature));
  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA,
                           &keyUsageWithTrailingDataBytes,
                           KeyUsage::digitalSignature));
}
