blob: 6fa5af03e81e2908176b173c75f84ba51bd80455 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "trunks/tpm_u2f.h"
#include <optional>
#include <string>
#include <brillo/secure_blob.h>
#include <gtest/gtest.h>
#include "trunks/cr50_headers/u2f.h"
#include "trunks/error_codes.h"
namespace trunks {
namespace {
brillo::Blob GetAppId() {
return brillo::Blob(U2F_APPID_SIZE, 0);
}
brillo::SecureBlob GetUserSecret() {
return brillo::SecureBlob(U2F_USER_SECRET_SIZE, 1);
}
brillo::Blob GetAuthTimeSecretHash() {
return brillo::Blob(SHA256_DIGEST_SIZE, 2);
}
brillo::Blob GetPublicKey() {
return brillo::Blob(U2F_EC_POINT_SIZE, 3);
}
brillo::Blob GetKeyHandle() {
return brillo::Blob(U2F_V0_KH_SIZE, 4);
}
brillo::Blob GetVersionedKeyHandle() {
return brillo::Blob(U2F_V1_KH_SIZE, 5);
}
brillo::SecureBlob GetAuthTimeSecret() {
return brillo::SecureBlob(U2F_AUTH_TIME_SECRET_SIZE, 6);
}
brillo::Blob GetHashToSign() {
return brillo::Blob(U2F_P256_SIZE, 7);
}
brillo::Blob GetSignatureR() {
return brillo::Blob(U2F_P256_SIZE, 8);
}
brillo::Blob GetSignatureS() {
return brillo::Blob(U2F_P256_SIZE, 9);
}
brillo::Blob GetAttestData() {
return brillo::Blob(100, 10);
}
std::string GetU2fGenerateResp() {
return brillo::BlobToString(
brillo::CombineBlobs({GetPublicKey(), GetKeyHandle()}));
}
std::string GetU2fGenerateVersionedResp() {
return brillo::BlobToString(
brillo::CombineBlobs({GetPublicKey(), GetVersionedKeyHandle()}));
}
std::string GetU2fSignResp() {
return brillo::BlobToString(
brillo::CombineBlobs({GetSignatureR(), GetSignatureS()}));
}
} // namespace
// A placeholder test fixture to prevent typos.
class TpmU2fTest : public testing::Test {};
TEST_F(TpmU2fTest, SerializeU2fGenerate) {
const brillo::Blob kInvalidAppId(31, 1);
const brillo::SecureBlob kInvalidUserSecret(31, 1);
std::string out;
// Incorrect app_id size.
EXPECT_EQ(Serialize_u2f_generate_t(0, kInvalidAppId, GetUserSecret(),
/*consume=*/true,
/*up_required=*/true, std::nullopt, &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// Incorrect user_secret size.
EXPECT_EQ(Serialize_u2f_generate_t(0, GetAppId(), kInvalidUserSecret,
/*consume=*/true,
/*up_required=*/true, std::nullopt, &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// Invalid version.
EXPECT_EQ(Serialize_u2f_generate_t(2, GetAppId(), GetUserSecret(),
/*consume=*/true,
/*up_required=*/true, std::nullopt, &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// auth_time_secret_hash should be nullopt for v0 requests.
EXPECT_EQ(Serialize_u2f_generate_t(0, GetAppId(), GetUserSecret(),
/*consume=*/true,
/*up_required=*/true,
GetAuthTimeSecretHash(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// auth_time_secret_hash shouldn't be nullopt for v1 requests.
EXPECT_EQ(Serialize_u2f_generate_t(1, GetAppId(), GetUserSecret(),
/*consume=*/true,
/*up_required=*/true, std::nullopt, &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// Valid v0 requests.
EXPECT_EQ(Serialize_u2f_generate_t(0, GetAppId(), GetUserSecret(),
/*consume=*/false,
/*up_required=*/false, std::nullopt, &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.length(), sizeof(u2f_generate_req));
EXPECT_EQ(Serialize_u2f_generate_t(0, GetAppId(), GetUserSecret(),
/*consume=*/false,
/*up_required=*/true, std::nullopt, &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.length(), sizeof(u2f_generate_req));
// Valid v1 requests.
EXPECT_EQ(Serialize_u2f_generate_t(1, GetAppId(), GetUserSecret(),
/*consume=*/true,
/*up_required=*/false,
GetAuthTimeSecretHash(), &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.length(), sizeof(u2f_generate_req));
EXPECT_EQ(Serialize_u2f_generate_t(1, GetAppId(), GetUserSecret(),
/*consume=*/true,
/*up_required=*/true,
GetAuthTimeSecretHash(), &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.length(), sizeof(u2f_generate_req));
}
TEST_F(TpmU2fTest, ParseU2fGenerate) {
brillo::Blob public_key, key_handle;
// Incorrect version.
EXPECT_EQ(
Parse_u2f_generate_t(GetU2fGenerateResp(), 2, &public_key, &key_handle),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(public_key.empty());
EXPECT_TRUE(key_handle.empty());
// Incorrect response size.
EXPECT_EQ(
Parse_u2f_generate_t(GetU2fGenerateResp(), 1, &public_key, &key_handle),
SAPI_RC_BAD_SIZE);
EXPECT_TRUE(public_key.empty());
EXPECT_TRUE(key_handle.empty());
EXPECT_EQ(Parse_u2f_generate_t(GetU2fGenerateVersionedResp(), 0, &public_key,
&key_handle),
SAPI_RC_BAD_SIZE);
EXPECT_TRUE(public_key.empty());
EXPECT_TRUE(key_handle.empty());
// Valid responses.
EXPECT_EQ(
Parse_u2f_generate_t(GetU2fGenerateResp(), 0, &public_key, &key_handle),
TPM_RC_SUCCESS);
EXPECT_EQ(public_key, GetPublicKey());
EXPECT_EQ(key_handle, GetKeyHandle());
EXPECT_EQ(Parse_u2f_generate_t(GetU2fGenerateVersionedResp(), 1, &public_key,
&key_handle),
TPM_RC_SUCCESS);
EXPECT_EQ(public_key, GetPublicKey());
EXPECT_EQ(key_handle, GetVersionedKeyHandle());
}
TEST_F(TpmU2fTest, SerializeU2fSign) {
const brillo::Blob kInvalidAppId(31, 1);
const brillo::SecureBlob kInvalidUserSecret(31, 1);
const brillo::Blob kInvalidHashToSign(31, 1);
const brillo::SecureBlob kInvalidAuthTimeSecret(31, 1);
std::string out;
// Incorrect app_id size.
EXPECT_EQ(Serialize_u2f_sign_t(0, kInvalidAppId, GetUserSecret(),
std::nullopt, std::nullopt,
/*check_only=*/true,
/*consume=*/false,
/*up_required=*/false, GetKeyHandle(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// Incorrect user_secret size.
EXPECT_EQ(Serialize_u2f_sign_t(0, GetAppId(), kInvalidUserSecret,
std::nullopt, std::nullopt,
/*check_only=*/true,
/*consume=*/false,
/*up_required=*/false, GetKeyHandle(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// hash_to_sign should be nullopt for check_only requests.
EXPECT_EQ(Serialize_u2f_sign_t(0, GetAppId(), GetUserSecret(), std::nullopt,
GetHashToSign(),
/*check_only=*/true,
/*consume=*/false,
/*up_required=*/false, GetKeyHandle(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// hash_to_sign should be valid for non-check_only requests.
EXPECT_EQ(Serialize_u2f_sign_t(0, GetAppId(), GetUserSecret(), std::nullopt,
std::nullopt,
/*check_only=*/false,
/*consume=*/false,
/*up_required=*/false, GetKeyHandle(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
EXPECT_EQ(Serialize_u2f_sign_t(0, GetAppId(), GetUserSecret(), std::nullopt,
kInvalidHashToSign,
/*check_only=*/false,
/*consume=*/false,
/*up_required=*/false, GetKeyHandle(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// auth_time_secret should be nullopt for v0 requests.
EXPECT_EQ(Serialize_u2f_sign_t(0, GetAppId(), GetUserSecret(),
GetAuthTimeSecret(), GetHashToSign(),
/*check_only=*/false,
/*consume=*/false,
/*up_required=*/false, GetKeyHandle(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// auth_time_secret should be valid (if present) for v1 requests.
EXPECT_EQ(Serialize_u2f_sign_t(1, GetAppId(), GetUserSecret(),
kInvalidAuthTimeSecret, GetHashToSign(),
/*check_only=*/false,
/*consume=*/false,
/*up_required=*/false, GetVersionedKeyHandle(),
&out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// key_handle size should correct for requests of each version.
EXPECT_EQ(Serialize_u2f_sign_t(
0, GetAppId(), GetUserSecret(), std::nullopt, std::nullopt,
/*check_only=*/true,
/*consume=*/false,
/*up_required=*/false, GetVersionedKeyHandle(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
EXPECT_EQ(Serialize_u2f_sign_t(1, GetAppId(), GetUserSecret(), std::nullopt,
std::nullopt,
/*check_only=*/true,
/*consume=*/false,
/*up_required=*/false, GetKeyHandle(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// Invalid version.
EXPECT_EQ(Serialize_u2f_sign_t(2, GetAppId(), GetUserSecret(), std::nullopt,
std::nullopt,
/*check_only=*/true,
/*consume=*/false,
/*up_required=*/false, GetKeyHandle(), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// Valid v0 requests.
EXPECT_EQ(Serialize_u2f_sign_t(0, GetAppId(), GetUserSecret(), std::nullopt,
std::nullopt,
/*check_only=*/true,
/*consume=*/false,
/*up_required=*/false, GetKeyHandle(), &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.size(), sizeof(u2f_sign_req));
EXPECT_EQ(Serialize_u2f_sign_t(0, GetAppId(), GetUserSecret(), std::nullopt,
GetHashToSign(),
/*check_only=*/false,
/*consume=*/true,
/*up_required=*/true, GetKeyHandle(), &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.size(), sizeof(u2f_sign_req));
// Valid v1 requests.
EXPECT_EQ(Serialize_u2f_sign_t(
1, GetAppId(), GetUserSecret(), std::nullopt, std::nullopt,
/*check_only=*/true,
/*consume=*/false,
/*up_required=*/false, GetVersionedKeyHandle(), &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.size(), sizeof(u2f_sign_versioned_req));
EXPECT_EQ(Serialize_u2f_sign_t(
1, GetAppId(), GetUserSecret(), std::nullopt, GetHashToSign(),
/*check_only=*/false,
/*consume=*/true,
/*up_required=*/true, GetVersionedKeyHandle(), &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.size(), sizeof(u2f_sign_versioned_req));
EXPECT_EQ(
Serialize_u2f_sign_t(1, GetAppId(), GetUserSecret(), GetAuthTimeSecret(),
GetHashToSign(),
/*check_only=*/false,
/*consume=*/true,
/*up_required=*/true, GetVersionedKeyHandle(), &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.size(), sizeof(u2f_sign_versioned_req));
}
TEST_F(TpmU2fTest, ParseU2fSign) {
const std::string kInvalidU2fSignResp(63, 1);
brillo::Blob sig_r, sig_s;
// Incorrect response size.
EXPECT_EQ(Parse_u2f_sign_t(kInvalidU2fSignResp, &sig_r, &sig_s),
SAPI_RC_BAD_SIZE);
EXPECT_TRUE(sig_r.empty());
EXPECT_TRUE(sig_s.empty());
// Valid response.
EXPECT_EQ(Parse_u2f_sign_t(GetU2fSignResp(), &sig_r, &sig_s), TPM_RC_SUCCESS);
EXPECT_EQ(sig_r, GetSignatureR());
EXPECT_EQ(sig_s, GetSignatureS());
}
TEST_F(TpmU2fTest, SerializeU2fAttest) {
constexpr uint8_t kFormat = 0;
const brillo::SecureBlob kInvalidUserSecret(31, 1);
std::string out;
// Incorrect user_secret size.
EXPECT_EQ(Serialize_u2f_attest_t(kInvalidUserSecret, kFormat, GetAttestData(),
&out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// Invalid data size.
EXPECT_EQ(
Serialize_u2f_attest_t(GetUserSecret(), kFormat,
brillo::Blob(U2F_MAX_ATTEST_SIZE + 1, 1), &out),
SAPI_RC_BAD_PARAMETER);
EXPECT_TRUE(out.empty());
// Valid response.
brillo::Blob data = GetAttestData();
EXPECT_EQ(Serialize_u2f_attest_t(GetUserSecret(), kFormat, data, &out),
TPM_RC_SUCCESS);
EXPECT_EQ(out.size(), offsetof(u2f_attest_req, data) + data.size());
}
} // namespace trunks