blob: 285539ae498c1240baf5cfc030633fb7e0d332ea [file] [log] [blame]
// Copyright 2017 The Chromium 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 "base/memory/ptr_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "u2f_apdu_command.h"
#include "u2f_apdu_response.h"
namespace device {
class U2fApduTest : public testing::Test {};
TEST_F(U2fApduTest, TestDeserializeBasic) {
uint8_t cla = 0xAA;
uint8_t ins = 0xAB;
uint8_t p1 = 0xAC;
uint8_t p2 = 0xAD;
std::vector<uint8_t> message = {cla, ins, p1, p2};
std::unique_ptr<U2fApduCommand> cmd =
U2fApduCommand::CreateFromMessage(message);
EXPECT_EQ(static_cast<size_t>(0), cmd->response_length_);
EXPECT_THAT(cmd->data_, testing::ContainerEq(std::vector<uint8_t>()));
EXPECT_EQ(cmd->cla_, cla);
EXPECT_EQ(cmd->ins_, ins);
EXPECT_EQ(cmd->p1_, p1);
EXPECT_EQ(cmd->p2_, p2);
// Invalid length
message = {cla, ins, p1};
EXPECT_EQ(nullptr, U2fApduCommand::CreateFromMessage(message));
message.push_back(p2);
message.push_back(0);
message.push_back(0xFF);
message.push_back(0xFF);
std::vector<uint8_t> oversized(U2fApduCommand::kApduMaxDataLength);
message.insert(message.end(), oversized.begin(), oversized.end());
message.push_back(0);
message.push_back(0);
EXPECT_NE(nullptr, U2fApduCommand::CreateFromMessage(message));
message.push_back(0);
EXPECT_EQ(nullptr, U2fApduCommand::CreateFromMessage(message));
}
TEST_F(U2fApduTest, TestDeserializeComplex) {
uint8_t cla = 0xAA;
uint8_t ins = 0xAB;
uint8_t p1 = 0xAC;
uint8_t p2 = 0xAD;
std::vector<uint8_t> data(
U2fApduCommand::kApduMaxDataLength - U2fApduCommand::kApduMaxHeader - 2,
0x7F);
std::vector<uint8_t> message = {cla, ins, p1, p2, 0};
message.push_back((data.size() >> 8) & 0xff);
message.push_back(data.size() & 0xff);
message.insert(message.end(), data.begin(), data.end());
// Create a message with no response expected
std::unique_ptr<U2fApduCommand> cmd_no_response =
U2fApduCommand::CreateFromMessage(message);
EXPECT_EQ(static_cast<size_t>(0), cmd_no_response->response_length_);
EXPECT_THAT(data, testing::ContainerEq(cmd_no_response->data_));
EXPECT_EQ(cmd_no_response->cla_, cla);
EXPECT_EQ(cmd_no_response->ins_, ins);
EXPECT_EQ(cmd_no_response->p1_, p1);
EXPECT_EQ(cmd_no_response->p2_, p2);
// Add response length to message
message.push_back(0xF1);
message.push_back(0xD0);
std::unique_ptr<U2fApduCommand> cmd =
U2fApduCommand::CreateFromMessage(message);
EXPECT_THAT(data, testing::ContainerEq(cmd->data_));
EXPECT_EQ(cmd->cla_, cla);
EXPECT_EQ(cmd->ins_, ins);
EXPECT_EQ(cmd->p1_, p1);
EXPECT_EQ(cmd->p2_, p2);
EXPECT_EQ(static_cast<size_t>(0xF1D0), cmd->response_length_);
}
TEST_F(U2fApduTest, TestDeserializeResponse) {
U2fApduResponse::Status status;
std::unique_ptr<U2fApduResponse> response;
std::vector<uint8_t> test_vector;
// Invalid length
std::vector<uint8_t> message = {0xAA};
EXPECT_EQ(nullptr, U2fApduResponse::CreateFromMessage(message));
// Valid length and status
status = U2fApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED;
message = {static_cast<uint8_t>(static_cast<uint16_t>(status) >> 8),
static_cast<uint8_t>(status)};
response = U2fApduResponse::CreateFromMessage(message);
ASSERT_NE(nullptr, response);
EXPECT_EQ(U2fApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED,
response->response_status_);
EXPECT_THAT(response->data_, testing::ContainerEq(std::vector<uint8_t>()));
// Valid length and status
status = U2fApduResponse::Status::SW_NO_ERROR;
message = {static_cast<uint8_t>(static_cast<uint16_t>(status) >> 8),
static_cast<uint8_t>(status)};
test_vector = {0x01, 0x02, 0xEF, 0xFF};
message.insert(message.begin(), test_vector.begin(), test_vector.end());
response = U2fApduResponse::CreateFromMessage(message);
ASSERT_NE(nullptr, response);
EXPECT_EQ(U2fApduResponse::Status::SW_NO_ERROR, response->response_status_);
EXPECT_THAT(response->data_, testing::ContainerEq(test_vector));
}
TEST_F(U2fApduTest, TestSerializeCommand) {
auto cmd = std::make_unique<U2fApduCommand>();
cmd->set_cla(0xA);
cmd->set_ins(0xB);
cmd->set_p1(0xC);
cmd->set_p2(0xD);
// No data, no response expected
std::vector<uint8_t> expected({0xA, 0xB, 0xC, 0xD});
ASSERT_THAT(expected, testing::ContainerEq(cmd->GetEncodedCommand()));
EXPECT_THAT(
expected,
testing::ContainerEq(
U2fApduCommand::CreateFromMessage(expected)->GetEncodedCommand()));
// No data, response expected
cmd->set_response_length(0xCAFE);
expected = {0xA, 0xB, 0xC, 0xD, 0x0, 0xCA, 0xFE};
EXPECT_THAT(expected, testing::ContainerEq(cmd->GetEncodedCommand()));
EXPECT_THAT(
expected,
testing::ContainerEq(
U2fApduCommand::CreateFromMessage(expected)->GetEncodedCommand()));
// Data exists, response expected
std::vector<uint8_t> data({0x1, 0x2, 0x3, 0x4});
cmd->set_data(data);
expected = {0xA, 0xB, 0xC, 0xD, 0x0, 0x0, 0x4,
0x1, 0x2, 0x3, 0x4, 0xCA, 0xFE};
EXPECT_THAT(expected, testing::ContainerEq(cmd->GetEncodedCommand()));
EXPECT_THAT(
expected,
testing::ContainerEq(
U2fApduCommand::CreateFromMessage(expected)->GetEncodedCommand()));
// Data exists, no response expected
cmd->set_response_length(0);
expected = {0xA, 0xB, 0xC, 0xD, 0x0, 0x0, 0x4, 0x1, 0x2, 0x3, 0x4};
EXPECT_THAT(expected, testing::ContainerEq(cmd->GetEncodedCommand()));
EXPECT_THAT(
expected,
testing::ContainerEq(
U2fApduCommand::CreateFromMessage(expected)->GetEncodedCommand()));
}
TEST_F(U2fApduTest, TestSerializeEdgeCases) {
auto cmd = std::make_unique<U2fApduCommand>();
cmd->set_cla(0xA);
cmd->set_ins(0xB);
cmd->set_p1(0xC);
cmd->set_p2(0xD);
// Set response length to maximum, which should serialize to 0x0000
cmd->set_response_length(U2fApduCommand::kApduMaxResponseLength);
std::vector<uint8_t> expected = {0xA, 0xB, 0xC, 0xD, 0x0, 0x0, 0x0};
EXPECT_THAT(expected, testing::ContainerEq(cmd->GetEncodedCommand()));
EXPECT_THAT(
expected,
testing::ContainerEq(
U2fApduCommand::CreateFromMessage(expected)->GetEncodedCommand()));
// Maximum data size
std::vector<uint8_t> oversized(U2fApduCommand::kApduMaxDataLength);
cmd->set_data(oversized);
EXPECT_THAT(cmd->GetEncodedCommand(),
testing::ContainerEq(
U2fApduCommand::CreateFromMessage(cmd->GetEncodedCommand())
->GetEncodedCommand()));
}
TEST_F(U2fApduTest, TestCreateSign) {
std::vector<uint8_t> appid(U2fApduCommand::kAppIdDigestLen, 0x01);
std::vector<uint8_t> challenge(U2fApduCommand::kChallengeDigestLen, 0xff);
std::vector<uint8_t> key_handle(U2fApduCommand::kMaxKeyHandleLength);
std::unique_ptr<U2fApduCommand> cmd =
U2fApduCommand::CreateSign(appid, challenge, key_handle);
ASSERT_NE(nullptr, cmd);
EXPECT_THAT(U2fApduCommand::CreateFromMessage(cmd->GetEncodedCommand())
->GetEncodedCommand(),
testing::ContainerEq(cmd->GetEncodedCommand()));
// Expect null result with incorrectly sized key handle
key_handle.push_back(0x0f);
cmd = U2fApduCommand::CreateSign(appid, challenge, key_handle);
EXPECT_EQ(nullptr, cmd);
key_handle.pop_back();
// Expect null result with incorrectly sized appid
appid.pop_back();
cmd = U2fApduCommand::CreateSign(appid, challenge, key_handle);
EXPECT_EQ(nullptr, cmd);
appid.push_back(0xff);
// Expect null result with incorrectly sized challenge
challenge.push_back(0x0);
cmd = U2fApduCommand::CreateSign(appid, challenge, key_handle);
EXPECT_EQ(nullptr, cmd);
}
TEST_F(U2fApduTest, TestCreateRegister) {
std::vector<uint8_t> appid(U2fApduCommand::kAppIdDigestLen, 0x01);
std::vector<uint8_t> challenge(U2fApduCommand::kChallengeDigestLen, 0xff);
std::unique_ptr<U2fApduCommand> cmd =
U2fApduCommand::CreateRegister(appid, challenge);
ASSERT_NE(nullptr, cmd);
EXPECT_THAT(U2fApduCommand::CreateFromMessage(cmd->GetEncodedCommand())
->GetEncodedCommand(),
testing::ContainerEq(cmd->GetEncodedCommand()));
// Expect null result with incorrectly sized appid
appid.push_back(0xff);
cmd = U2fApduCommand::CreateRegister(appid, challenge);
EXPECT_EQ(nullptr, cmd);
appid.pop_back();
// Expect null result with incorrectly sized challenge
challenge.push_back(0xff);
cmd = U2fApduCommand::CreateRegister(appid, challenge);
EXPECT_EQ(nullptr, cmd);
}
TEST_F(U2fApduTest, TestCreateVersion) {
std::unique_ptr<U2fApduCommand> cmd = U2fApduCommand::CreateVersion();
std::vector<uint8_t> expected = {
0x0, U2fApduCommand::kInsU2fVersion, 0x0, 0x0, 0x0, 0x0, 0x0};
EXPECT_THAT(expected, testing::ContainerEq(cmd->GetEncodedCommand()));
EXPECT_THAT(U2fApduCommand::CreateFromMessage(cmd->GetEncodedCommand())
->GetEncodedCommand(),
testing::ContainerEq(cmd->GetEncodedCommand()));
}
TEST_F(U2fApduTest, TestCreateLegacyVersion) {
std::unique_ptr<U2fApduCommand> cmd = U2fApduCommand::CreateLegacyVersion();
// Legacy version command contains 2 extra null bytes compared to ISO 7816-4
// format
std::vector<uint8_t> expected = {
0x0, U2fApduCommand::kInsU2fVersion, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
EXPECT_THAT(expected, testing::ContainerEq(cmd->GetEncodedCommand()));
EXPECT_THAT(U2fApduCommand::CreateFromMessage(cmd->GetEncodedCommand())
->GetEncodedCommand(),
testing::ContainerEq(cmd->GetEncodedCommand()));
}
} // namespace device