| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Copyright (C) 2018 Apple Inc. All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "config.h" |
| |
| #if ENABLE(WEB_AUTHN) |
| |
| #include <WebCore/ApduCommand.h> |
| #include <WebCore/ApduResponse.h> |
| #include <wtf/Optional.h> |
| |
| namespace TestWebKitAPI { |
| |
| using namespace apdu; |
| |
| TEST(ApduTest, TestDeserializeBasic) |
| { |
| uint8_t cla = 0xAA; |
| uint8_t ins = 0xAB; |
| uint8_t p1 = 0xAC; |
| uint8_t p2 = 0xAD; |
| Vector<uint8_t> message({ cla, ins, p1, p2 }); |
| auto cmd = ApduCommand::createFromMessage(message); |
| ASSERT_TRUE(cmd); |
| EXPECT_EQ(0u, cmd->responseLength()); |
| EXPECT_TRUE(cmd->data().isEmpty()); |
| 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.append(p2); |
| message.append(0); |
| // Set APDU command data size as maximum. |
| message.append(0xFF); |
| message.append(0xFF); |
| message.resize(message.size() + ApduCommand::kApduMaxDataLength); |
| // Set maximum response size. |
| message.append(0); |
| message.append(0); |
| // |message| is APDU encoded byte array with maximum data length. |
| EXPECT_TRUE(ApduCommand::createFromMessage(message)); |
| message.append(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; |
| Vector<uint8_t> data(ApduCommand::kApduMaxDataLength - ApduCommand::kApduMaxHeader - 2, 0x7F); |
| Vector<uint8_t> message = { cla, ins, p1, p2, 0 }; |
| message.append((data.size() >> 8) & 0xff); |
| message.append(data.size() & 0xff); |
| message.appendVector(data); |
| |
| // Create a message with no response expected. |
| auto cmdNoResponse = ApduCommand::createFromMessage(message); |
| ASSERT_TRUE(cmdNoResponse); |
| EXPECT_EQ(0u, cmdNoResponse->responseLength()); |
| EXPECT_EQ(data, cmdNoResponse->data()); |
| EXPECT_EQ(cla, cmdNoResponse->cla()); |
| EXPECT_EQ(ins, cmdNoResponse->ins()); |
| EXPECT_EQ(p1, cmdNoResponse->p1()); |
| EXPECT_EQ(p2, cmdNoResponse->p2()); |
| |
| // Add response length to message. |
| message.append(0xF1); |
| message.append(0xD0); |
| auto cmd = ApduCommand::createFromMessage(message); |
| ASSERT_TRUE(cmd); |
| EXPECT_EQ(data, 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->responseLength()); |
| } |
| |
| TEST(ApduTest, TestDeserializeResponse) |
| { |
| ApduResponse::Status status; |
| Vector<uint8_t> testVector; |
| // Invalid length. |
| 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->status()); |
| EXPECT_EQ(response->data(), 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)}; |
| testVector = { 0x01, 0x02, 0xEF, 0xFF }; |
| message.insertVector(0, testVector); |
| response = ApduResponse::createFromMessage(message); |
| ASSERT_TRUE(response); |
| EXPECT_EQ(ApduResponse::Status::SW_NO_ERROR, response->status()); |
| EXPECT_EQ(response->data(), testVector); |
| } |
| |
| TEST(ApduTest, TestSerializeCommand) |
| { |
| ApduCommand cmd; |
| cmd.setCla(0xA); |
| cmd.setIns(0xB); |
| cmd.setP1(0xC); |
| cmd.setP2(0xD); |
| // No data, no response expected. |
| Vector<uint8_t> expected({ 0xA, 0xB, 0xC, 0xD }); |
| ASSERT(expected == cmd.getEncodedCommand()); |
| auto deserializedCmd = ApduCommand::createFromMessage(expected); |
| ASSERT_TRUE(deserializedCmd); |
| EXPECT_EQ(expected, deserializedCmd->getEncodedCommand()); |
| // No data, response expected. |
| cmd.setResponseLength(0xCAFE); |
| expected = { 0xA, 0xB, 0xC, 0xD, 0x0, 0xCA, 0xFE }; |
| EXPECT_EQ(expected, cmd.getEncodedCommand()); |
| deserializedCmd = ApduCommand::createFromMessage(expected); |
| ASSERT_TRUE(deserializedCmd); |
| EXPECT_EQ(expected, deserializedCmd->getEncodedCommand()); |
| // Data exists, response expected. |
| Vector<uint8_t> data({ 0x1, 0x2, 0x3, 0x4 }); |
| cmd.setData(WTFMove(data)); |
| expected = { 0xA, 0xB, 0xC, 0xD, 0x0, 0x0, 0x4, 0x1, 0x2, 0x3, 0x4, 0xCA, 0xFE }; |
| EXPECT_EQ(expected, cmd.getEncodedCommand()); |
| deserializedCmd = ApduCommand::createFromMessage(expected); |
| ASSERT_TRUE(deserializedCmd); |
| EXPECT_EQ(expected, deserializedCmd->getEncodedCommand()); |
| // Data exists, no response expected. |
| cmd.setResponseLength(0); |
| expected = { 0xA, 0xB, 0xC, 0xD, 0x0, 0x0, 0x4, 0x1, 0x2, 0x3, 0x4 }; |
| EXPECT_EQ(expected, cmd.getEncodedCommand()); |
| EXPECT_EQ(expected, ApduCommand::createFromMessage(expected)->getEncodedCommand()); |
| } |
| |
| TEST(ApduTest, TestSerializeEdgeCases) |
| { |
| ApduCommand cmd; |
| cmd.setCla(0xA); |
| cmd.setIns(0xB); |
| cmd.setP1(0xC); |
| cmd.setP2(0xD); |
| // Set response length to maximum, which should serialize to 0x0000. |
| cmd.setResponseLength(ApduCommand::kApduMaxResponseLength); |
| Vector<uint8_t> expected({ 0xA, 0xB, 0xC, 0xD, 0x0, 0x0, 0x0 }); |
| EXPECT_EQ(expected, cmd.getEncodedCommand()); |
| auto deserializedCmd = ApduCommand::createFromMessage(expected); |
| ASSERT_TRUE(deserializedCmd); |
| EXPECT_EQ(expected, deserializedCmd->getEncodedCommand()); |
| // Maximum data size. |
| Vector<uint8_t> oversized(ApduCommand::kApduMaxDataLength); |
| cmd.setData(WTFMove(oversized)); |
| deserializedCmd = ApduCommand::createFromMessage(cmd.getEncodedCommand()); |
| ASSERT_TRUE(deserializedCmd); |
| EXPECT_EQ(cmd.getEncodedCommand(), deserializedCmd->getEncodedCommand()); |
| } |
| |
| } // namespace TestWebKitAPI |
| |
| #endif // ENABLE(WEB_AUTHN) |