// 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 "components/apdu/apdu_command.h" #include "components/apdu/apdu_response.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace apdu { TEST(ApduTest, TestDeserializeBasic) { uint8_t cla = 0xAA; uint8_t ins = 0xAB; uint8_t p1 = 0xAC; uint8_t p2 = 0xAD; std::vector message({cla, ins, p1, p2}); const auto cmd = ApduCommand::CreateFromMessage(message); ASSERT_TRUE(cmd); EXPECT_EQ(0u, cmd->response_length_); EXPECT_TRUE(cmd->data_.empty()); EXPECT_EQ(cla, cmd->cla_); EXPECT_EQ(ins, cmd->ins_); EXPECT_EQ(p1, cmd->p1_); EXPECT_EQ(p2, cmd->p2_); // Invalid length. message = {cla, ins, p1}; EXPECT_FALSE(ApduCommand::CreateFromMessage(message)); message.push_back(p2); message.push_back(0); // Set APDU command data size as maximum. message.push_back(0xFF); message.push_back(0xFF); message.resize(message.size() + ApduCommand::kApduMaxDataLength); // Set maximum response size. message.push_back(0); message.push_back(0); // |message| is APDU encoded byte array with maximum data length. EXPECT_TRUE(ApduCommand::CreateFromMessage(message)); message.push_back(0); // |message| encoding containing data of size maximum data length + 1. EXPECT_FALSE(ApduCommand::CreateFromMessage(message)); } TEST(ApduTest, TestDeserializeComplex) { uint8_t cla = 0xAA; uint8_t ins = 0xAB; uint8_t p1 = 0xAC; uint8_t p2 = 0xAD; std::vector data( ApduCommand::kApduMaxDataLength - ApduCommand::kApduMaxHeader - 2, 0x7F); std::vector 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. const auto cmd_no_response = ApduCommand::CreateFromMessage(message); ASSERT_TRUE(cmd_no_response); EXPECT_EQ(0u, cmd_no_response->response_length_); EXPECT_THAT(data, ::testing::ContainerEq(cmd_no_response->data_)); EXPECT_EQ(cla, cmd_no_response->cla_); EXPECT_EQ(ins, cmd_no_response->ins_); EXPECT_EQ(p1, cmd_no_response->p1_); EXPECT_EQ(p2, cmd_no_response->p2_); // Add response length to message. message.push_back(0xF1); message.push_back(0xD0); const auto cmd = ApduCommand::CreateFromMessage(message); ASSERT_TRUE(cmd); EXPECT_THAT(data, ::testing::ContainerEq(cmd->data_)); EXPECT_EQ(cla, cmd->cla_); EXPECT_EQ(ins, cmd->ins_); EXPECT_EQ(p1, cmd->p1_); EXPECT_EQ(p2, cmd->p2_); EXPECT_EQ(static_cast(0xF1D0), cmd->response_length_); } TEST(ApduTest, TestDeserializeResponse) { ApduResponse::Status status; std::vector test_vector; // Invalid length. std::vector message({0xAA}); EXPECT_FALSE(ApduResponse::CreateFromMessage(message)); // Valid length and status. status = ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED; message = {static_cast(static_cast(status) >> 8), static_cast(status)}; auto response = ApduResponse::CreateFromMessage(message); ASSERT_TRUE(response); EXPECT_EQ(ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED, response->response_status_); EXPECT_THAT(response->data_, ::testing::ContainerEq(std::vector())); // Valid length and status. status = ApduResponse::Status::SW_NO_ERROR; message = {static_cast(static_cast(status) >> 8), static_cast(status)}; test_vector = {0x01, 0x02, 0xEF, 0xFF}; message.insert(message.begin(), test_vector.begin(), test_vector.end()); response = ApduResponse::CreateFromMessage(message); ASSERT_TRUE(response); EXPECT_EQ(ApduResponse::Status::SW_NO_ERROR, response->response_status_); EXPECT_THAT(response->data_, ::testing::ContainerEq(test_vector)); } TEST(ApduTest, TestSerializeCommand) { ApduCommand cmd; cmd.set_cla(0xA); cmd.set_ins(0xB); cmd.set_p1(0xC); cmd.set_p2(0xD); // No data, no response expected. std::vector expected({0xA, 0xB, 0xC, 0xD}); ASSERT_THAT(expected, ::testing::ContainerEq(cmd.GetEncodedCommand())); auto deserialized_cmd = ApduCommand::CreateFromMessage(expected); ASSERT_TRUE(deserialized_cmd); EXPECT_THAT(expected, ::testing::ContainerEq(deserialized_cmd->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())); deserialized_cmd = ApduCommand::CreateFromMessage(expected); ASSERT_TRUE(deserialized_cmd); EXPECT_THAT(expected, ::testing::ContainerEq(deserialized_cmd->GetEncodedCommand())); // Data exists, response expected. std::vector 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())); deserialized_cmd = ApduCommand::CreateFromMessage(expected); ASSERT_TRUE(deserialized_cmd); EXPECT_THAT(expected, ::testing::ContainerEq(deserialized_cmd->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( ApduCommand::CreateFromMessage(expected)->GetEncodedCommand())); } TEST(ApduTest, TestSerializeEdgeCases) { ApduCommand cmd; 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(ApduCommand::kApduMaxResponseLength); std::vector expected({0xA, 0xB, 0xC, 0xD, 0x0, 0x0, 0x0}); EXPECT_THAT(expected, ::testing::ContainerEq(cmd.GetEncodedCommand())); auto deserialized_cmd = ApduCommand::CreateFromMessage(expected); ASSERT_TRUE(deserialized_cmd); EXPECT_THAT(expected, ::testing::ContainerEq(deserialized_cmd->GetEncodedCommand())); // Maximum data size. std::vector oversized(ApduCommand::kApduMaxDataLength); cmd.set_data(oversized); deserialized_cmd = ApduCommand::CreateFromMessage(cmd.GetEncodedCommand()); ASSERT_TRUE(deserialized_cmd); EXPECT_THAT(cmd.GetEncodedCommand(), ::testing::ContainerEq(deserialized_cmd->GetEncodedCommand())); } } // namespace apdu