blob: 0208fc0e1c34bdbc1e046dbafddc6680f618a9d4 [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "u2fd/allowlisting_util.h"
#include <functional>
#include <limits>
#include <memory>
#include <attestation/proto_bindings/interface.pb.h>
#include <base/optional.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <policy/device_policy.h>
#include <policy/libpolicy.h>
#include <policy/mock_device_policy.h>
#include <policy/mock_libpolicy.h>
#include "u2fd/util.h"
namespace u2f {
namespace {
using testing::_;
using testing::DoAll;
using testing::Return;
using testing::ReturnRef;
using testing::SetArgPointee;
using testing::StrictMock;
// Certificate, before attestation data is appended.
constexpr uint8_t kCertificateHeader[2] = {0x30, 0x82};
constexpr uint8_t kCertificateLength[2] = {0x01, 0x2c}; // = 300
constexpr uint8_t kCertificateBody[300] = {[0 ... 299] = 0xff};
// Data returned from attestationd.
constexpr char kTpmMetadata[109] = {[0 ... 108] = 0x1a};
constexpr char kTpmSignature[256] = {[0 ... 255] = 0x1e};
// Data loaded from policy.
constexpr char kDeviceId[36 + 1 /* null terminator */] =
"aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb";
// Certificate, after attestation data is appended. Elements not listed here are
// unchanged.
constexpr uint8_t kFinalCertificateMetadataHeader[2] = {
0x04 /* Octet String */, sizeof(kTpmMetadata) /* Metadata Length */};
constexpr uint8_t kFinalCertificateSignatureHeader[4] = {
0x04, // Octet String
0x82, // Long Form Length, 2 bytes
0x01, 0x00 // Signature Length: 256
};
constexpr uint8_t kFinalCertificateDeviceIdHeader[2] = {
0x13 /* Printable String */,
sizeof(kDeviceId) - 1 /* Device Id Length (excl. null terminator) */};
// New length of the certificate body is the original length, plus all appended
// data and associated headers.
//
// Original Length: 300 +
// Metadata Header: 2 +
// Metadata 109 +
// Signature Header: 4 +
// Signature: 256 +
// Device Id Header: 2 +
// Device Id: 36
// = 709
constexpr uint8_t kFinalCertificateLength[2] = {0x02, 0xc5};
constexpr int kMaxAsn1FieldSize = std::numeric_limits<uint16_t>::max();
class AllowlistingUtilTest : public ::testing::Test {
public:
AllowlistingUtilTest() : util_(CreateMockAttestationdCallback()) {}
void SetUp() override {
mock_policy_provider_ = new StrictMock<policy::MockPolicyProvider>();
util_.SetPolicyProviderForTest(
std::unique_ptr<policy::PolicyProvider>(mock_policy_provider_));
attestationd_called_ = false;
expect_attestationd_call_ = false;
}
void TearDown() override {
EXPECT_EQ(expect_attestationd_call_, attestationd_called_);
}
protected:
// Build and return a standard G2F certificate.
std::vector<uint8_t> BuildCert() {
std::vector<uint8_t> cert;
util::AppendToVector(kCertificateHeader, &cert);
util::AppendToVector(kCertificateLength, &cert);
util::AppendToVector(kCertificateBody, &cert);
expected_attestationd_cert_size_ = cert.size();
return cert;
}
// Builds a success reply for the specified cert, which will be returned by
// any expected calls to attestationd.
void ReturnAttestationSuccessReply(const std::vector<uint8_t>& cert) {
attestationd_reply_ = attestation::GetCertifiedNvIndexReply();
attestationd_reply_->set_status(attestation::STATUS_SUCCESS);
attestationd_reply_->mutable_certified_data()->append(kTpmMetadata,
sizeof(kTpmMetadata));
attestationd_reply_->mutable_certified_data()->append(
reinterpret_cast<const char*>(cert.data()), cert.size());
attestationd_reply_->mutable_signature()->append(kTpmSignature,
sizeof(kTpmSignature));
}
// Expect a call to attestationd, and respond with a success message
// containing a certified copy of the G2F certificate stored in cert_ (call
// BuildCert() first).
void ExpectAttestationCall() { expect_attestationd_call_ = true; }
// Expect a call to get Device Id, and respond with a success response
// including the the specified |id|.
void ExpectGetDeviceId(std::string id) {
EXPECT_CALL(*mock_policy_provider_, Reload()).WillOnce(Return(true));
EXPECT_CALL(*mock_policy_provider_, GetDevicePolicy())
.WillOnce(ReturnRef(mock_device_policy_));
EXPECT_CALL(mock_device_policy_, GetDeviceDirectoryApiId(_))
.WillOnce(DoAll(SetArgPointee<0>(id), Return(true)));
}
// Get a copy of the certificate, as we expect it to be once the allowlisting
// data has been appended.
std::vector<uint8_t> GetExpectedCertWithAllowlistData() {
std::vector<uint8_t> cert;
// Header with updated length.
util::AppendToVector(kCertificateHeader, &cert);
util::AppendToVector(kFinalCertificateLength, &cert);
// Original Body.
util::AppendToVector(kCertificateBody, &cert);
// TPM Metadata.
util::AppendToVector(kFinalCertificateMetadataHeader, &cert);
util::AppendToVector(kTpmMetadata, &cert);
// Signature.
util::AppendToVector(kFinalCertificateSignatureHeader, &cert);
util::AppendToVector(kTpmSignature, &cert);
// Device Id.
util::AppendToVector(kFinalCertificateDeviceIdHeader, &cert);
util::AppendToVector(std::string(kDeviceId), &cert);
return cert;
}
// Attempt to append data to the specified cert, and check it fails.
void ExpectAppendDataFails(std::vector<uint8_t>* cert) {
std::vector<uint8_t> cert_original = *cert;
EXPECT_FALSE(util_.AppendDataToCert(cert));
// Check we didn't modify cert.
EXPECT_EQ(cert_original, *cert);
}
private:
std::function<base::Optional<attestation::GetCertifiedNvIndexReply>(int)>
CreateMockAttestationdCallback() {
return [this](int size) {
// Check we were expecting this.
EXPECT_TRUE(expect_attestationd_call_);
// We should only ever have at most one call.
EXPECT_FALSE(attestationd_called_);
attestationd_called_ = true;
EXPECT_EQ(expected_attestationd_cert_size_, size);
return attestationd_reply_;
};
}
// Whether we expect a call to attestationd, and if so, the size of the cert
// parameter passed.
bool expect_attestationd_call_;
int expected_attestationd_cert_size_;
// Actual behavior.
bool attestationd_called_;
protected:
AllowlistingUtil util_;
StrictMock<policy::MockPolicyProvider>* mock_policy_provider_; // Not Owned.
StrictMock<policy::MockDevicePolicy> mock_device_policy_;
// If called, what we should return from 'attestationd'
base::Optional<attestation::GetCertifiedNvIndexReply> attestationd_reply_;
};
TEST_F(AllowlistingUtilTest, AppendDataSuccess) {
std::vector<uint8_t> cert = BuildCert();
ExpectAttestationCall();
ReturnAttestationSuccessReply(cert);
ExpectGetDeviceId(kDeviceId);
// Sanity Check.
EXPECT_NE(GetExpectedCertWithAllowlistData(), cert);
EXPECT_TRUE(util_.AppendDataToCert(&cert));
// Check contents of resulting cert.
EXPECT_EQ(GetExpectedCertWithAllowlistData(), cert);
}
TEST_F(AllowlistingUtilTest, AppendDataNullCertificate) {
EXPECT_FALSE(util_.AppendDataToCert(nullptr));
}
TEST_F(AllowlistingUtilTest, AppendDataCertTooShort) {
std::vector<uint8_t> cert;
util::AppendToVector(kCertificateHeader, &cert);
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataCertUnexpectedFirstBytes) {
std::vector<uint8_t> cert = {0xff, 0xff, 0xff, 0xff};
EXPECT_FALSE(util_.AppendDataToCert(&cert));
ExpectAppendDataFails(&cert);
cert[0] = kCertificateHeader[0];
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataPolicyReloadFailure) {
std::vector<uint8_t> cert = BuildCert();
EXPECT_CALL(*mock_policy_provider_, Reload()).WillOnce(Return(false));
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataDeviceIdMissing) {
std::vector<uint8_t> cert = BuildCert();
EXPECT_CALL(*mock_policy_provider_, Reload()).WillOnce(Return(true));
EXPECT_CALL(*mock_policy_provider_, GetDevicePolicy())
.WillOnce(ReturnRef(mock_device_policy_));
EXPECT_CALL(mock_device_policy_, GetDeviceDirectoryApiId(_))
.WillOnce(Return(false));
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataAttestationdCallFails) {
std::vector<uint8_t> cert = BuildCert();
ExpectGetDeviceId(kDeviceId);
// Expect the call, we haven't set the reply so will return base::nullopt.
ExpectAttestationCall();
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataAttestationdReturnsError) {
std::vector<uint8_t> cert = BuildCert();
ExpectAttestationCall();
ReturnAttestationSuccessReply(cert);
// Override with failure status.
attestationd_reply_->set_status(attestation::STATUS_NOT_AVAILABLE);
ExpectGetDeviceId(kDeviceId);
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataCertifiedDataTooShort) {
std::vector<uint8_t> cert = BuildCert();
ExpectAttestationCall();
ReturnAttestationSuccessReply(cert);
// Resize certified data to be smaller than the original cert.
attestationd_reply_->mutable_certified_data()->resize(
sizeof(kCertificateBody));
ExpectGetDeviceId(kDeviceId);
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataCertPrefixTooLong) {
std::vector<uint8_t> cert = BuildCert();
ReturnAttestationSuccessReply(cert);
// Append enough data that the prefix will be too long to store as an ASN1
// field with two byte length.
attestationd_reply_->mutable_certified_data()->append(kMaxAsn1FieldSize, 'a');
ExpectGetDeviceId(kDeviceId);
ExpectAttestationCall();
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataSignatureTooLong) {
std::vector<uint8_t> cert = BuildCert();
ExpectAttestationCall();
ReturnAttestationSuccessReply(cert);
// Append enough data that the prefix will be too long to store as an ASN1
// field with two byte length.
attestationd_reply_->mutable_signature()->append(kMaxAsn1FieldSize, 'a');
ExpectGetDeviceId(kDeviceId);
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataDeviceIdTooLong) {
std::vector<uint8_t> cert = BuildCert();
ExpectAttestationCall();
ReturnAttestationSuccessReply(cert);
std::string device_id_too_long(kMaxAsn1FieldSize + 1, 'a');
ExpectGetDeviceId(device_id_too_long);
ExpectAppendDataFails(&cert);
}
TEST_F(AllowlistingUtilTest, AppendDataAppendedDataTooLong) {
std::vector<uint8_t> cert = BuildCert();
ExpectAttestationCall();
ReturnAttestationSuccessReply(cert);
// Append enough data that overall, the total appended data will cause the
// size of the modified cert to be too long to store as an ASN1 field with two
// byte length.
attestationd_reply_->mutable_signature()->append(
// The signature is 256 bytes so we'll still exceed the max size.
kMaxAsn1FieldSize - 100, 'a');
ExpectGetDeviceId(kDeviceId);
ExpectAppendDataFails(&cert);
}
} // namespace
} // namespace u2f