| // 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<uint8_t> 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<uint8_t> data( |
| ApduCommand::kApduMaxDataLength - ApduCommand::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. |
| 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<size_t>(0xF1D0), cmd->response_length_); |
| } |
| |
| TEST(ApduTest, TestDeserializeResponse) { |
| ApduResponse::Status status; |
| std::vector<uint8_t> test_vector; |
| // Invalid length. |
| std::vector<uint8_t> message({0xAA}); |
| EXPECT_FALSE(ApduResponse::CreateFromMessage(message)); |
| // Valid length and status. |
| status = ApduResponse::Status::SW_CONDITIONS_NOT_SATISFIED; |
| message = {static_cast<uint8_t>(static_cast<uint16_t>(status) >> 8), |
| static_cast<uint8_t>(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<uint8_t>())); |
| // Valid length and status. |
| status = ApduResponse::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 = 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<uint8_t> 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<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())); |
| 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<uint8_t> 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<uint8_t> 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 |