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)
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());
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;
};
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());
+}