blob: aa33fca3bc761f4da219fdf2e1df4f2b487167b3 [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/u2f_msg_handler.h"
#include <iostream>
#include <memory>
#include <regex> // NOLINT(build/c++11)
#include <string>
#include <base/strings/string_number_conversions.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <metrics/metrics_library_mock.h>
#include "u2fd/mock_allowlisting_util.h"
#include "u2fd/mock_tpm_vendor_cmd.h"
#include "u2fd/mock_user_state.h"
namespace u2f {
namespace {
using ::testing::_;
using ::testing::ContainsRegex;
using ::testing::Matcher;
using ::testing::MatchesRegex;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
std::string AdpuToHexString(const U2fResponseAdpu& adpu) {
std::string adpu_str;
adpu.ToString(&adpu_str);
return base::HexEncode(adpu_str.c_str(), adpu_str.size());
}
brillo::SecureBlob ArrayToSecureBlob(const char* array) {
brillo::SecureBlob blob;
CHECK(brillo::SecureBlob::HexStringToSecureBlob(array, &blob));
return blob;
}
MATCHER_P(MsgEqStr, expected, "") {
std::string match_hex = AdpuToHexString(arg);
if (match_hex == expected) {
return true;
}
*result_listener << match_hex
<< " did not match expected value: " << expected;
return false;
}
MATCHER_P(StructEqStr, expected, "") {
std::string arg_hex = base::HexEncode(&arg, sizeof(arg));
if (arg_hex == expected) {
return true;
}
*result_listener << arg_hex << " did not match expected value: " << expected;
return false;
}
MATCHER_P(StructMatchesRegex, pattern, "") {
std::string arg_hex = base::HexEncode(&arg, sizeof(arg));
if (std::regex_match(arg_hex, std::regex(pattern))) {
return true;
}
*result_listener << arg_hex << " did not match regex: " << pattern;
return false;
}
// Dummy User State.
constexpr char kUserSecret[65] = {[0 ... 63] = 'E', '\0'};
constexpr uint8_t kCounter = 37;
class U2fMessageHandlerTest : public ::testing::Test {
public:
void SetUp() override { CreateHandler(false, false); }
void TearDown() override {
EXPECT_EQ(presence_requested_expected_, presence_requested_count_);
}
protected:
void CreateHandler(bool allow_legacy_kh, bool allow_g2f_attestation) {
mock_allowlisting_util_ = new StrictMock<MockAllowlistingUtil>();
handler_.reset(new U2fMessageHandler(
std::unique_ptr<AllowlistingUtil>(mock_allowlisting_util_),
[this]() { presence_requested_count_++; }, &mock_user_state_,
&mock_tpm_proxy_, &mock_metrics_, allow_legacy_kh,
allow_g2f_attestation));
}
void ExpectGetUserSecret() { ExpectGetUserSecretForTimes(1); }
void ExpectGetUserSecretForTimes(int times) {
EXPECT_CALL(mock_user_state_, GetUserSecret())
.Times(times)
.WillRepeatedly(Return(ArrayToSecureBlob(kUserSecret)));
}
void ExpectGetUserSecretFails() {
EXPECT_CALL(mock_user_state_, GetUserSecret())
.WillOnce(Return(base::Optional<brillo::SecureBlob>()));
}
void ExpectGetCounter() {
EXPECT_CALL(mock_user_state_, GetCounter())
.WillOnce(Return(base::Optional<std::vector<uint8_t>>({kCounter})));
}
void ExpectGetCounterFails() {
EXPECT_CALL(mock_user_state_, GetCounter())
.WillOnce(Return(base::Optional<std::vector<uint8_t>>()));
}
void ExpectIncrementCounter() {
EXPECT_CALL(mock_user_state_, IncrementCounter()).WillOnce(Return(true));
}
void ExpectIncrementCounterFails() {
EXPECT_CALL(mock_user_state_, IncrementCounter()).WillOnce(Return(false));
}
U2fResponseAdpu ProcessMsg(const std::string& hex) {
std::vector<uint8_t> bytes;
CHECK(base::HexStringToBytes(hex, &bytes));
return handler_->ProcessMsg(std::string(bytes.begin(), bytes.end()));
}
U2fResponseAdpu ProcessMsg(const std::string& hex,
const std::string& hex2,
const std::string& hex3,
const std::string& hex4) {
return ProcessMsg(hex + hex2 + hex3 + hex4);
}
void CheckResponseForMsg(const std::string& request,
const std::string& response) {
EXPECT_THAT(ProcessMsg(request), MsgEqStr(response));
}
void CheckResponseForMsg(const std::string& request,
const std::string& request2,
const std::string& request3,
const std::string& request4,
const std::string& response) {
EXPECT_THAT(ProcessMsg(request, request2, request3, request4),
MsgEqStr(response));
}
StrictMock<MockAllowlistingUtil>* mock_allowlisting_util_; // Not Owned.
StrictMock<MockTpmVendorCommandProxy> mock_tpm_proxy_;
StrictMock<MockUserState> mock_user_state_;
NiceMock<MetricsLibraryMock> mock_metrics_;
std::unique_ptr<U2fMessageHandler> handler_;
int presence_requested_expected_ = 0;
private:
int presence_requested_count_ = 0;
};
// U2F Error Codes.
constexpr char kErrorResponseWrongData[] = "6A80";
constexpr char kErrorResponseConditionsNotSatisfied[] = "6985";
constexpr char kErrorResponseWrongLength[] = "6700";
constexpr char kErrorResponseInsNotSupported[] = "6D00";
constexpr char kErrorResponseClaNotSupported[] = "6E00";
constexpr char kErrorResponseWtf[] = "6F00";
// Dummy U2F Message Parameters
constexpr char kAppId[65] = {[0 ... 63] = 'A', '\0'};
constexpr char kChallenge[65] = {[0 ... 63] = 'C', '\0'};
constexpr char kMaxResponseSize[] = "FF";
// Cr50 Response Codes
constexpr uint32_t kCr50Success = 0;
constexpr uint32_t kCr50NotAllowed = 0x507;
constexpr uint32_t kCr50PasswordRequired = 0x50a;
// U2F_REGISTER
//
////////////////////////////////////////////////////////////////////////////////
// Message format: CLA,INS{U2F_REGISTER},P1{U2F_AUTH_ENFORCE},P2,MSG_SIZE
// All fields one byte.
constexpr char kRequestRegisterPrefix[] = "0001030040";
constexpr char kRequestRegisterShort[] = "0001030000";
TEST_F(U2fMessageHandlerTest, RegisterSuccess) {
ExpectGetUserSecret();
// See U2F_GENERATE_REQ in //platform/ec/include/u2f.h
std::string expected_cr50_request_regex =
"(AA){32}" // AppId
"(EE){32}" // User Secret
"03" // U2F_AUTH_ENFORCE
"(00){32}"; // Auth-time secret hash (unused)
struct u2f_generate_resp cr50_response = {
.keyHandle = {.origin_seed = {[0 ... 31] = 0xFD},
.hmac = {[0 ... 31] = 0xFD}}};
EXPECT_CALL(mock_tpm_proxy_,
SendU2fGenerate(StructMatchesRegex(expected_cr50_request_regex),
Matcher<u2f_generate_resp*>(_)))
.WillOnce(DoAll(SetArgPointee<1>(cr50_response), Return(kCr50Success)));
std::string adpu_response = AdpuToHexString(
ProcessMsg(kRequestRegisterPrefix, kChallenge, kAppId, kMaxResponseSize));
// See U2F Raw Message Formats Spec
std::string expected_response_regex =
"05" // Reserved Byte
"[0-9A-F]{130}" // Public Key
"40(FD){64}" // Key Handle (matches cr50 response)
".*" // Attestation Cert + Signature
"9000"; // U2F_SW_NO_ERROR
// Just a basic sanity check, the correctness of message contents is tested by
// integration tests.
EXPECT_THAT(adpu_response, MatchesRegex(expected_response_regex));
}
// Errors detected during parsing; should not read user state or call cr50.
TEST_F(U2fMessageHandlerTest, RegisterShortMsg) {
CheckResponseForMsg(kRequestRegisterShort, kErrorResponseWrongLength);
}
TEST_F(U2fMessageHandlerTest, RegisterInvalidFlags) {
std::string prefix = kRequestRegisterPrefix;
prefix[5] = '0'; // Set invalid P1 flag.
CheckResponseForMsg(prefix, kChallenge, kAppId, kMaxResponseSize,
kErrorResponseWtf);
}
TEST_F(U2fMessageHandlerTest, RegisterChromeDummyWinkRequest) {
std::string app_id;
std::string challenge;
// Chrome Sends a bogus request with these parameters, see chromium
// //src/device/fido/fido_constants.cc
for (int i = 0; i < 32; i++) {
app_id.append("41");
challenge.append("42");
}
CheckResponseForMsg(kRequestRegisterPrefix, challenge, app_id,
kMaxResponseSize, kErrorResponseConditionsNotSatisfied);
}
// Errors while reading state, should not call cr50.
TEST_F(U2fMessageHandlerTest, RegisterSecretNotAvailable) {
ExpectGetUserSecretFails();
CheckResponseForMsg(kRequestRegisterPrefix, kChallenge, kAppId,
kMaxResponseSize, kErrorResponseWtf);
}
// Errors returned by cr50.
TEST_F(U2fMessageHandlerTest, RegisterNoPresence) {
ExpectGetUserSecret();
EXPECT_CALL(mock_tpm_proxy_,
SendU2fGenerate(_, Matcher<u2f_generate_resp*>(_)))
.WillOnce(Return(kCr50NotAllowed));
CheckResponseForMsg(kRequestRegisterPrefix, kChallenge, kAppId,
kMaxResponseSize, kErrorResponseConditionsNotSatisfied);
presence_requested_expected_ = 1;
}
TEST_F(U2fMessageHandlerTest, RegisterCr50UnknownError) {
ExpectGetUserSecret();
EXPECT_CALL(mock_tpm_proxy_,
SendU2fGenerate(_, Matcher<u2f_generate_resp*>(_)))
.WillOnce(Return(4324523 /* Random Unknown Error */));
CheckResponseForMsg(kRequestRegisterPrefix, kChallenge, kAppId,
kMaxResponseSize, kErrorResponseWtf);
}
// U2F_REGISTER with G2F Attestation
//
////////////////////////////////////////////////////////////////////////////////
// Message format:
// CLA,INS{U2F_REGISTER},P1{U2F_AUTH_ENFORCE|G2F_ATTEST},P2,MSG_SIZE All fields
// one byte.
constexpr char kRequestRegisterG2fPrefix[] = "0001830040";
// See U2F_GENERATE_REQ in //platform/ec/include/u2f.h
constexpr char kCr50ExpectedGenReqRegex[] =
"(AA){32}" // AppId
"(EE){32}" // User Secret
"03" // U2F_AUTH_ENFORCE | G2F_ATTEST
"(00){32}"; // Auth-time secret hash (unused)
// Dummy generate response.
constexpr struct u2f_generate_resp kCr50GenResp = {
.keyHandle = {.origin_seed = {[0 ... 31] = 0xFD},
.hmac = {[0 ... 31] = 0xFD}}}; // cr50_gen_resp
// Example of a cert that would be returned by cr50.
constexpr char kDummyG2fCert[] =
"308201363081DDA0030201020210442D32429223D041240350303716EE6B300A06082A8648"
"CE3D040302300F310D300B06035504031304637235303022180F3230303030313031303030"
"3030305A180F32303939313233313233353935395A300F310D300B06035504031304637235"
"303059301306072A8648CE3D020106082A8648CE3D030107034200045165719A9975F6FD30"
"CC2516C22FE841F65F9D2EE7B8B72F76807AEBD8CA3376005C7FA86453E4B10DB7BFAD5D2B"
"D00DB4A7C4845AD06D686ACD0252387618ECA31730153013060B2B0601040182E51C020101"
"040403020308300A06082A8648CE3D0403020348003045022100F09976F373920FEF8205C4"
"B1FB1DA21EB9F3F176B7DF433A1ADE0F3F38B721960220179D9B9051BFCCCC90BA6BB42B86"
"111D7A9C4FB56DFD39FB426081DD027AD609";
std::string GetDummyG2fCert() {
static std::string cert_str = []() {
std::vector<uint8_t> cert;
base::HexStringToBytes(kDummyG2fCert, &cert);
return std::string(reinterpret_cast<char*>(cert.data()), cert.size());
}();
return cert_str;
}
TEST_F(U2fMessageHandlerTest, RegisterG2fWhenDisabledSuccess) {
// G2F Attestation is disabled (default flags set in test fixture), so we
// should not see any additional calls to cr50 for G2F attestation.
ExpectGetUserSecret();
EXPECT_CALL(mock_tpm_proxy_,
SendU2fGenerate(StructMatchesRegex(kCr50ExpectedGenReqRegex),
Matcher<u2f_generate_resp*>(_)))
.WillOnce(DoAll(SetArgPointee<1>(kCr50GenResp), Return(kCr50Success)));
std::string adpu_response = AdpuToHexString(ProcessMsg(
kRequestRegisterG2fPrefix, kChallenge, kAppId, kMaxResponseSize));
// See U2F Raw Message Formats Spec
std::string expected_response_regex =
"05" // Reserved Byte
"[0-9A-F]{130}" // Public Key
"40(FD){64}" // Key Handle (matches kCr50GenResp)
".*" // Attestation Cert + Signature
"9000"; // U2F_SW_NO_ERROR
// Just a basic sanity check, the correctness of message contents is tested by
// integration tests.
EXPECT_THAT(adpu_response, MatchesRegex(expected_response_regex));
}
TEST_F(U2fMessageHandlerTest, RegisterG2fSuccess) {
CreateHandler(false /* legacy fallback */, true /* g2f_attest */);
// Once to create the key, once for attestation.
ExpectGetUserSecretForTimes(2);
EXPECT_CALL(mock_tpm_proxy_,
SendU2fGenerate(StructMatchesRegex(kCr50ExpectedGenReqRegex),
Matcher<u2f_generate_resp*>(_)))
.WillOnce(DoAll(SetArgPointee<1>(kCr50GenResp), Return(kCr50Success)));
EXPECT_CALL(mock_tpm_proxy_, GetG2fCertificate(_))
.WillOnce(DoAll(SetArgPointee<0>(GetDummyG2fCert()), Return(0)));
EXPECT_CALL(*mock_allowlisting_util_, AppendDataToCert(_))
.WillOnce(Return(true));
// See U2F_ATTEST_REQ in //platform/ec/include/u2f.h
std::string expected_cr50_attest_req_regex =
"(EE){32}" // User Secret
"00" // Format
"C2" // Data Length
// See U2F Raw Message formats for format of data to sign.
"00(A){64}(C){64}(FD){64}(0){254}"; // Data
struct u2f_attest_resp cr50_attest_resp = {.sig_r = {[0 ... 31] = 0xFF},
.sig_s = {[0 ... 31] = 0x55}};
EXPECT_CALL(
mock_tpm_proxy_,
SendU2fAttest(StructMatchesRegex(expected_cr50_attest_req_regex), _))
.WillOnce(
DoAll(SetArgPointee<1>(cr50_attest_resp), Return(kCr50Success)));
std::string adpu_response = AdpuToHexString(ProcessMsg(
kRequestRegisterG2fPrefix, kChallenge, kAppId, kMaxResponseSize));
// See U2F Raw Message Formats Spec
std::string expected_response_regex =
"05" // Reserved Byte
"[0-9A-F]{130}" // Public Key
"40(FD){64}" // Key Handle (matches kCr50GenResp)
".*" // Attestation Cert + Signature
"9000"; // U2F_SW_NO_ERROR
// Just a basic sanity check, the correctness of message contents is tested by
// integration tests.
EXPECT_THAT(adpu_response, MatchesRegex(expected_response_regex));
}
// Error from cr50
TEST_F(U2fMessageHandlerTest, RegisterG2fNoPresence) {
CreateHandler(false /* legacy fallback */, true /* g2f_attest */);
ExpectGetUserSecret();
EXPECT_CALL(mock_tpm_proxy_,
SendU2fGenerate(_, Matcher<u2f_generate_resp*>(_)))
.WillOnce(Return(kCr50NotAllowed));
CheckResponseForMsg(kRequestRegisterG2fPrefix, kChallenge, kAppId,
kMaxResponseSize, kErrorResponseConditionsNotSatisfied);
presence_requested_expected_ = 1;
}
TEST_F(U2fMessageHandlerTest, RegisterG2fAttestSecretNotAvailable) {
CreateHandler(false /* legacy fallback */, true /* g2f_attest */);
// Called first to create the key, succeed.
// Called again for attestation, fail.
EXPECT_CALL(mock_user_state_, GetUserSecret())
.WillOnce(Return(ArrayToSecureBlob(kUserSecret)))
.WillOnce(Return(base::Optional<brillo::SecureBlob>()));
EXPECT_CALL(mock_tpm_proxy_,
SendU2fGenerate(StructMatchesRegex(kCr50ExpectedGenReqRegex),
Matcher<u2f_generate_resp*>(_)))
.WillOnce(DoAll(SetArgPointee<1>(kCr50GenResp), Return(kCr50Success)));
EXPECT_CALL(mock_tpm_proxy_, GetG2fCertificate(_))
.WillOnce(DoAll(SetArgPointee<0>(GetDummyG2fCert()), Return(0)));
CheckResponseForMsg(kRequestRegisterG2fPrefix, kChallenge, kAppId,
kMaxResponseSize, kErrorResponseWtf);
}
TEST_F(U2fMessageHandlerTest, RegisterG2fAttestFails) {
CreateHandler(false /* legacy fallback */, true /* g2f_attest */);
// Once to create the key, once for attestation.
ExpectGetUserSecretForTimes(2);
EXPECT_CALL(mock_tpm_proxy_,
SendU2fGenerate(StructMatchesRegex(kCr50ExpectedGenReqRegex),
Matcher<u2f_generate_resp*>(_)))
.WillOnce(DoAll(SetArgPointee<1>(kCr50GenResp), Return(kCr50Success)));
EXPECT_CALL(mock_tpm_proxy_, GetG2fCertificate(_))
.WillOnce(DoAll(SetArgPointee<0>(GetDummyG2fCert()), Return(0)));
EXPECT_CALL(mock_tpm_proxy_, SendU2fAttest(_, _))
.WillOnce(Return(kCr50NotAllowed));
CheckResponseForMsg(kRequestRegisterG2fPrefix, kChallenge, kAppId,
kMaxResponseSize, kErrorResponseWtf);
}
// U2F_AUTHENTICATE
//
////////////////////////////////////////////////////////////////////////////////
// Message format: CLA,INS{U2F_AUTHENTICATE},P1{U2F_AUTH_ENFORCE},P2,MSG_SIZE
constexpr char kRequestAuthenticatePrefix[] = "0002030042";
// Message format: KH_LENGTH,KH_BYTE
constexpr char kRequestAuthenticateKeyHandle[] = "0100";
// All fields one byte.
TEST_F(U2fMessageHandlerTest, AuthenticateSuccess) {
ExpectGetCounter();
ExpectGetUserSecret();
std::string expected_cr50_request_regex =
"(AA){32}" // AppId
"(EE){32}" // User Secret
"(00){64}" // Key Handle
"[0-9A-F]{64}" // Hash
"03"; // U2F_AUTH_ENFORCE
struct u2f_sign_resp cr50_response = {.sig_r = {[0 ... 31] = 0xFF},
.sig_s = {[0 ... 31] = 0x55}};
EXPECT_CALL(mock_tpm_proxy_,
SendU2fSign(Matcher<const u2f_sign_req&>(
StructMatchesRegex(expected_cr50_request_regex)),
_))
.WillOnce(DoAll(SetArgPointee<1>(cr50_response), Return(kCr50Success)));
ExpectIncrementCounter();
std::string adpu_response =
AdpuToHexString(ProcessMsg(kRequestAuthenticatePrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle));
// Just basic sanity check, validity of the message is tested in integration
// tests.
std::string expected_response_regex =
"01" // User Presence Asserted
".*" // DER encoding
"(FF){32}" // sig_r
".*" // DER encoding
"(55){32}" // sig_s
"9000"; // U2F_SW_NO_ERRROR
EXPECT_THAT(adpu_response, MatchesRegex(expected_response_regex));
}
TEST_F(U2fMessageHandlerTest, AuthenticateWithFallbackSuccess) {
CreateHandler(true /* legacy kh fallback */, false /* g2f attestation */);
ExpectGetCounter();
ExpectGetUserSecret();
// The purpose of this test is to check that the LEGACY_KH_FALLBACK flag is
// passed to cr50.
std::string expected_cr50_request_regex =
"(AA){32}" // AppId
"(EE){32}" // User Secret
"(00){64}" // Key Handle
"[0-9A-F]{64}" // Hash
"43"; // U2F_AUTH_ENFORCE | LEGACY_KH_FALLBACK
EXPECT_CALL(mock_tpm_proxy_,
SendU2fSign(Matcher<const u2f_sign_req&>(
StructMatchesRegex(expected_cr50_request_regex)),
_))
.WillOnce(Return(kCr50Success));
ExpectIncrementCounter();
std::string adpu_response =
AdpuToHexString(ProcessMsg(kRequestAuthenticatePrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle));
// Just check that is succeeds; the rest is tested in the previous test case.
EXPECT_THAT(adpu_response, MatchesRegex(".*9000"));
}
// Errors reading state, should not call cr50.
TEST_F(U2fMessageHandlerTest, AuthenticateSecretNotAvailable) {
ExpectGetCounter();
ExpectGetUserSecretFails();
CheckResponseForMsg(kRequestAuthenticatePrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle, kErrorResponseWtf);
}
TEST_F(U2fMessageHandlerTest, AuthenticateCounterNotAvailable) {
ExpectGetCounterFails();
CheckResponseForMsg(kRequestAuthenticatePrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle, kErrorResponseWtf);
}
// Errors returned by cr50.
TEST_F(U2fMessageHandlerTest, AuthenticateNoPresence) {
ExpectGetCounter();
ExpectGetUserSecret();
EXPECT_CALL(mock_tpm_proxy_, SendU2fSign(Matcher<const u2f_sign_req&>(_), _))
.WillOnce(Return(kCr50NotAllowed));
CheckResponseForMsg(kRequestAuthenticatePrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle,
kErrorResponseConditionsNotSatisfied);
presence_requested_expected_ = 1;
}
TEST_F(U2fMessageHandlerTest, AuthenticateInvalidKeyHandle) {
ExpectGetCounter();
ExpectGetUserSecret();
EXPECT_CALL(mock_tpm_proxy_, SendU2fSign(Matcher<const u2f_sign_req&>(_), _))
.WillOnce(Return(kCr50PasswordRequired));
CheckResponseForMsg(kRequestAuthenticatePrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle, kErrorResponseWrongData);
}
TEST_F(U2fMessageHandlerTest, AuthenticateCr50UnknownError) {
ExpectGetCounter();
ExpectGetUserSecret();
EXPECT_CALL(mock_tpm_proxy_, SendU2fSign(Matcher<const u2f_sign_req&>(_), _))
.WillOnce(Return(43423 /* Random Unknown Error */));
CheckResponseForMsg(kRequestAuthenticatePrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle, kErrorResponseWtf);
}
// Error updating state.
TEST_F(U2fMessageHandlerTest, AuthenticateCounterCouldNotUpdate) {
ExpectGetCounter();
ExpectGetUserSecret();
struct u2f_sign_resp cr50_response = {.sig_r = {[0 ... 31] = 0xFF},
.sig_s = {[0 ... 31] = 0x55}};
EXPECT_CALL(mock_tpm_proxy_, SendU2fSign(Matcher<const u2f_sign_req&>(_), _))
.WillOnce(DoAll(SetArgPointee<1>(cr50_response), Return(kCr50Success)));
ExpectIncrementCounterFails();
CheckResponseForMsg(kRequestAuthenticatePrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle, kErrorResponseWtf);
}
// U2F_AUTHENTICATE "Check Only"
//
////////////////////////////////////////////////////////////////////////////////
// Message format: CLA,INS{U2F_AUTHENTICATE},P1{U2F_AUTH_CHECK_ONLY},P2,MSG_SIZE
constexpr char kRequestAuthenticateCheckOnlyPrefix[] = "0002070042";
TEST_F(U2fMessageHandlerTest, AuthenticateCheckOnlySuccess) {
ExpectGetUserSecret();
std::string expected_cr50_request_regex =
"(AA){32}" // AppId
"(EE){32}" // User Secret
"(00){64}" // Key Handle
"[0-9A-F]{64}" // Hash
"07"; // U2F_AUTH_CHECK_ONLY
EXPECT_CALL(mock_tpm_proxy_,
SendU2fSign(Matcher<const u2f_sign_req&>(
StructMatchesRegex(expected_cr50_request_regex)),
nullptr))
.WillOnce(Return(kCr50Success));
// A success response for this kind of message is indicate by a 'presence
// required' error.
CheckResponseForMsg(kRequestAuthenticateCheckOnlyPrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle,
kErrorResponseConditionsNotSatisfied);
}
TEST_F(U2fMessageHandlerTest, AuthenticateCheckOnlyInvalidKeyHandle) {
ExpectGetUserSecret();
std::string expected_cr50_request_regex =
"(AA){32}" // AppId
"(EE){32}" // User Secret
"(00){64}" // Key Handle
"[0-9A-F]{64}" // Hash
"07"; // U2F_AUTH_CHECK_ONLY
EXPECT_CALL(mock_tpm_proxy_,
SendU2fSign(Matcher<const u2f_sign_req&>(
StructMatchesRegex(expected_cr50_request_regex)),
nullptr))
.WillOnce(Return(kCr50PasswordRequired));
CheckResponseForMsg(kRequestAuthenticateCheckOnlyPrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle, kErrorResponseWrongData);
}
TEST_F(U2fMessageHandlerTest, AuthenticateCheckOnlySecretNotAvailable) {
ExpectGetUserSecretFails();
CheckResponseForMsg(kRequestAuthenticateCheckOnlyPrefix, kChallenge, kAppId,
kRequestAuthenticateKeyHandle, kErrorResponseWtf);
}
// U2F_VERSION
//
////////////////////////////////////////////////////////////////////////////////
// Message format: CLA,INS{U2F_VERSION},P1,P1,MSG_LENGTH,[RESPONSE_LENGTH]
// All fields one byte.
constexpr char kRequestVersion[] = "0003000000";
constexpr char kRequestVersionInvalidLength[] = "0003000001FF00";
// "U2F_V2", plus U2F_SW_NO_ERROR response code.
constexpr char kSuccessResponseVersion[] = "5532465F56329000";
TEST_F(U2fMessageHandlerTest, Version) {
CheckResponseForMsg(kRequestVersion, kSuccessResponseVersion);
}
TEST_F(U2fMessageHandlerTest, VersionInvalidMsg) {
CheckResponseForMsg(kRequestVersionInvalidLength, kErrorResponseWrongLength);
}
// Misc Invalid Messages
//
////////////////////////////////////////////////////////////////////////////////
// Message format: CLA,INS,P1,P2.
// All fields occupy byte.
constexpr char kRequestInvalidIns[] = "00430000";
constexpr char kRequestInvalidCla[] = "FFFFFFFF";
constexpr char kRequestInvalidMsg[] = "00";
TEST_F(U2fMessageHandlerTest, UnknownIns) {
CheckResponseForMsg(kRequestInvalidIns, kErrorResponseInsNotSupported);
}
TEST_F(U2fMessageHandlerTest, InvalidCla) {
CheckResponseForMsg(kRequestInvalidCla, kErrorResponseClaNotSupported);
}
TEST_F(U2fMessageHandlerTest, InvalidMsg) {
CheckResponseForMsg(kRequestInvalidMsg, kErrorResponseWtf);
}
} // namespace
} // namespace u2f