Get Assertion response parsing 44/307844/14
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Wed, 13 Mar 2024 09:27:10 +0000 (10:27 +0100)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 26 Mar 2024 13:01:22 +0000 (14:01 +0100)
Change-Id: I1b095096095d159b27e4df899da23ada75b5b691

srcs/message.cpp
srcs/message.h
tests/message_tests.cpp

index 76bd94d43580cca58e32503ebd4c7b1435e6acfd..924f089888091158ba636bc4864f6224dc284c4b 100644 (file)
@@ -188,6 +188,18 @@ constexpr int64_t KEY_GA_CMD_PIN_UV_AUTH_PROTOCOL = 0x07;
 constexpr int64_t KEY_GA_CMD_ENTERPRISE_ATTESTATION = 0x08;
 constexpr int64_t KEY_GA_CMD_ATTESTATION_FORMAT_PREFERENCE = 0x09;
 
+// Get Assertion response
+constexpr int64_t KEY_GA_RSP_CREDENTIAL = 0x01;
+constexpr int64_t KEY_GA_RSP_AUTH_DATA = 0x02;
+constexpr int64_t KEY_GA_RSP_SIGNATURE = 0x03;
+constexpr int64_t KEY_GA_RSP_USER = 0x04;
+constexpr int64_t KEY_GA_RSP_NUMBER_OF_CREDENTIALS = 0x05;
+constexpr int64_t KEY_GA_RSP_USER_SELECTED = 0x06;
+constexpr int64_t KEY_GA_RSP_LARGE_BLOB_KEY = 0x07;
+constexpr int64_t KEY_GA_RSP_UNSIGNED_EXTENSIONS_OUTPUT = 0x08;
+constexpr int64_t KEY_GA_RSP_EP_ATT = 0x09;
+constexpr int64_t KEY_GA_RSP_ATT_STMT = 0x0A;
+
 void SerializePubkeyCredDescriptors(CborEncoding::SortedMap &map,
                                     const CborEncoding::Key &key,
                                     const wauthn_pubkey_cred_descriptors_s &credentials)
@@ -710,6 +722,53 @@ void MakeCredentialResponse::Notify(IMessageObserver &observer)
     observer.HandleMakeCredentialResponse(*this);
 }
 
+void GetAssertionResponse::Deserialize(BufferView &input)
+{
+    CtapResponse::Deserialize(input);
+
+    auto helper = CborParsing::Parser::Create(input.data(), input.size());
+    auto map = helper.EnterMap();
+
+    {
+        auto credentialMap = map.EnterMapAt(KEY_GA_RSP_CREDENTIAL).value();
+
+        m_credentialId = credentialMap.GetByteStringAt("id").value();
+
+        auto type = credentialMap.GetTextStringAt("type").value();
+        if (type != CREDENTIAL_TYPE_PUBLIC_KEY)
+            THROW_UNKNOWN("Unexpected key type: " << type);
+
+        // TODO optional transports
+    }
+
+    DeserializeAuthData(map.GetByteStringAt(KEY_GA_RSP_AUTH_DATA).value());
+
+    if (m_authData.m_attestationData.has_value())
+        THROW_UNKNOWN("Unexpected attestation data in Get Assertion response");
+
+    m_signature = map.GetByteStringAt(KEY_GA_RSP_SIGNATURE).value();
+
+    if (auto userMap = map.EnterMapAt(KEY_GA_RSP_USER)) {
+        m_userId = userMap->GetByteStringAt("id").value();
+        m_userName = userMap->GetTextStringAt("name").value();
+        m_userDisplayName = userMap->GetTextStringAt("displayName").value();
+    }
+
+    /*
+     * TODO:
+     *  KEY_GA_CMD_OPTIONS,
+     *  KEY_GA_CMD_PIN_UV_AUTH_PARAM,
+     *  KEY_GA_CMD_PIN_UV_AUTH_PROTOCOL,
+     *  KEY_GA_CMD_ENTERPRISE_ATTESTATION,
+     *  KEY_GA_CMD_ATTESTATION_FORMAT_PREFERENCE
+     */
+}
+
+void GetAssertionResponse::Notify(IMessageObserver &observer)
+{
+    observer.HandleGetAssertionResponse(*this);
+}
+
 void PostHandshakeResponse::Deserialize(BufferView &input)
 {
     auto helper = CborParsing::Parser::Create(input.data(), input.size());
index 6f202642e0470959cf1cfbe7d08e0be7d455fb66..d9ce655ffce10e7e83960bfde9ea631533e69dfe 100644 (file)
@@ -194,9 +194,22 @@ public:
     Buffer m_largeBlobKey;
 };
 
+class GetAssertionResponse : public CtapResponse {
+public:
+    void Deserialize(BufferView &input) override;
+    void Notify(IMessageObserver &observer) override;
+
+    Buffer m_credentialId;
+    Buffer m_signature;
+    Buffer m_userId;
+    std::string m_userName;
+    std::string m_userDisplayName;
+};
+
 class IMessageObserver {
 public:
     virtual void HandleMakeCredentialResponse(const MakeCredentialResponse &response) = 0;
+    virtual void HandleGetAssertionResponse(const GetAssertionResponse &response) = 0;
     virtual ~IMessageObserver() = default;
 };
 
index 3b4ad2dbb56a440d9a98fd83f25bc89054479dd4..251802569f0f21d1cb731f092ea5b1954c59effc 100644 (file)
@@ -622,3 +622,33 @@ TEST(Messages, ParseMakeCredentialResponse2)
     ASSERT_FALSE(msg.m_epAtt.has_value());
     ASSERT_TRUE(msg.m_largeBlobKey.empty());
 }
+
+TEST(Messages, ParseGetAssertionResponse)
+{
+    // sample response received from iPhone authenticator during manual testing
+    constexpr uint8_t blob[] =
+        "\x00\xa5\x01\xa2\x62\x69\x64\x54\xc7\x3c\x11\x45\x4b\x19\x08\xe5\x2c\x16\x8d\x45\x81\xf5"
+        "\x9f\xb1\x15\x00\xbf\x51\x64\x74\x79\x70\x65\x6a\x70\x75\x62\x6c\x69\x63\x2d\x6b\x65\x79"
+        "\x02\x58\x25\x23\xf3\x84\xb5\x42\x6e\x09\x7e\x53\x43\x0c\xbd\x25\xec\x20\x11\xd3\xcf\xb1"
+        "\x4a\x7f\x67\xa8\x29\x94\xfe\x7f\xe7\xfe\x1b\xca\xdc\x1d\x00\x00\x00\x00\x03\x58\x48\x30"
+        "\x46\x02\x21\x00\xe2\x6e\xb7\xab\x29\x48\xcb\x1c\x72\xc9\xe2\x91\xc9\x74\xff\x2b\x44\xd9"
+        "\xdf\x8a\xb2\x3c\xc9\x5a\xc0\xe7\xb8\x65\xc4\xe5\xde\x08\x02\x21\x00\xb1\xea\x2c\x5f\xf3"
+        "\x41\x71\x0f\x18\x04\x77\xe1\x73\xf9\xbc\xac\x0f\x01\x14\x91\x33\x79\xca\xe2\x87\x22\xbb"
+        "\x8b\xda\xe2\x77\x22\x04\xa3\x62\x69\x64\x48\x00\x01\x02\x03\x04\x05\x06\x07\x64\x6e\x61"
+        "\x6d\x65\x60\x6b\x64\x69\x73\x70\x6c\x61\x79\x4e\x61\x6d\x65\x60\x08\xa0";
+    BufferView view(blob, sizeof(blob) - 1);
+
+    GetAssertionResponse msg;
+    ASSERT_NO_THROW(msg.Deserialize(view));
+
+    ASSERT_GE(msg.m_authDataRaw.size(), 37);
+    AssertEq(msg.m_authDataRaw, blob + 47, msg.m_authDataRaw.size());
+    AssertEq(msg.m_authData.m_rpIdHash, blob + 47, 32);
+    ASSERT_EQ(msg.m_authData.m_flags, blob[79]);
+    ASSERT_FALSE(msg.m_authData.m_attestationData.has_value());
+    AssertEq(msg.m_credentialId, blob + 8, 20);
+    AssertEq(msg.m_signature, blob + 87, 72);
+    AssertEq(msg.m_userId, blob + 165, 8);
+    ASSERT_TRUE(msg.m_userName.empty());
+    ASSERT_TRUE(msg.m_userDisplayName.empty());
+}