#include "exception.h"
#include "lowercase_hex_string_of.h"
+#include <cassert>
+#include <crypto/sha2.h>
+
void CtapMessageProcessor::SetEncryptedTunnel(std::unique_ptr<IEncryptedTunnel> encryptedTunnel)
{
m_encryptedTunnel = std::move(encryptedTunnel);
auto bv = BufferView{resp.data() + 1, resp.size() - 1};
res.mcResp.Deserialize(bv);
+
+ assert(options.rp);
+ VerifyRpIdHash(options.rp->id, res.mcResp.m_authData.m_rpIdHash);
LogDebug("credentialId: " << LowercaseHexStringOf(
res.mcResp.m_authData.m_attestationData->m_credentialId));
auto bv = BufferView{resp.data() + 1, resp.size() - 1};
res.gaResp.Deserialize(bv);
+ VerifyRpIdHash(options.rpId, res.gaResp.m_authData.m_rpIdHash);
ShutdownMessage shutdown;
Buffer shutdownMsg;
getInfo.Deserialize(bv);
return getInfo;
}
+
+void VerifyRpIdHash(const char *rpId, const BufferView &rpIdHash)
+{
+ assert(rpId);
+ CryptoBuffer rpIdBuffer(rpId, rpId + strlen(rpId));
+ auto expected = Crypto::Sha256Hash(rpIdBuffer);
+ if (BufferView(expected.data(), expected.size()) != rpIdHash)
+ THROW_UNKNOWN("RP id hash does not match");
+}
--- /dev/null
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+#include "ctap_message_processor.h"
+#include "exception.h"
+
+#include <gtest/gtest.h>
+
+TEST(CtapMessageProcessor, RpIdValidation)
+{
+ constexpr uint8_t blob[] = "\x11\x94\x22\x8d\xa8\xfd\xbd\xee\xfd\x26\x1b\xd7\xb6\x59\x5c\xfd"
+ "\x70\xa5\x0d\x70\xc6\x40\x7b\xcf\x01\x3d\xe9\x6d\x4e\xfb\x17\xde";
+
+ BufferView view(blob, sizeof(blob) - 1);
+
+ EXPECT_NO_THROW(VerifyRpIdHash("acme.com", view));
+ EXPECT_THROW(VerifyRpIdHash("acme.co", view), Exception::Unknown);
+ view.remove_prefix(1);
+ EXPECT_THROW(VerifyRpIdHash("acme.com", view), Exception::Unknown);
+}
#include "exception.h"
#include "message.h"
+#include <ctap_message_processor.h>
#include <gtest/gtest.h>
namespace {
// example make credential response received from iPhone authenticator (w/o CTAP message type
// prefix)
constexpr uint8_t blob[] =
- "\x00\xa4\x01\x64\x6e\x6f\x6e\x65\x02\x58\x98\xa3\x79\xa6\xf6\xee\xaf\xb9\xa5\x5e\x37\x8c"
- "\x11\x80\x34\xe2\x75\x1e\x68\x2f\xab\x9f\x2d\x30\xab\x13\xd2\x12\x55\x86\xce\x19\x47\x5d"
+ "\x00\xa4\x01\x64\x6e\x6f\x6e\x65\x02\x58\x98\x11\x94\x22\x8d\xa8\xfd\xbd\xee\xfd\x26\x1b"
+ "\xd7\xb6\x59\x5c\xfd\x70\xa5\x0d\x70\xc6\x40\x7b\xcf\x01\x3d\xe9\x6d\x4e\xfb\x17\xde\x5d"
"\x00\x00\x00\x00\xfb\xfc\x30\x07\x15\x4e\x4e\xcc\x8c\x0b\x6e\x02\x05\x57\xd7\xbd\x00\x14"
"\x92\xa5\x07\x16\x15\x56\x78\x93\x64\x86\x22\xaf\xca\x66\x3d\x24\x50\x13\x8c\x58\xa5\x01"
"\x02\x03\x26\x20\x01\x21\x58\x20\xc8\x39\x05\x61\x42\x33\x3e\x54\xcd\x7d\xed\x55\xb7\x64"
ASSERT_EQ(msg.m_authDataRaw.size(), 152);
AssertEq(msg.m_authDataRaw, blob + 11, msg.m_authDataRaw.size());
AssertEq(msg.m_authData.m_rpIdHash, blob + 11, 32);
+ ASSERT_NO_THROW(VerifyRpIdHash("acme.com", msg.m_authData.m_rpIdHash));
ASSERT_EQ(msg.m_authData.m_flags, blob[43]);
ASSERT_TRUE(msg.m_authData.m_attestationData.has_value());
AssertEq(msg.m_authData.m_attestationData->m_credentialId, blob + 66, 20);
// example make credential response received from Android authenticator (w/o CTAP message type
// prefix)
constexpr uint8_t blob[] =
- "\x00\xa3\x01\x64\x6e\x6f\x6e\x65\x02\x58\xa4\xa3\x79\xa6\xf6\xee\xaf\xb9\xa5\x5e\x37\x8c"
- "\x11\x80\x34\xe2\x75\x1e\x68\x2f\xab\x9f\x2d\x30\xab\x13\xd2\x12\x55\x86\xce\x19\x47\x45"
+ "\x00\xa3\x01\x64\x6e\x6f\x6e\x65\x02\x58\xa4\x11\x94\x22\x8d\xa8\xfd\xbd\xee\xfd\x26\x1b"
+ "\xd7\xb6\x59\x5c\xfd\x70\xa5\x0d\x70\xc6\x40\x7b\xcf\x01\x3d\xe9\x6d\x4e\xfb\x17\xde\x45"
"\x00\x00\x00\x00\x53\x41\x4d\x53\x55\x4e\x47\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20"
"\x8c\x5b\x4d\x2f\x62\x5f\xa0\xcf\x61\x44\xba\x1a\xa3\x2f\x5b\x4b\x65\xff\x21\x7c\xcb\x0d"
"\x24\x10\x16\xa3\xb8\xc8\x2c\x94\xe6\x7f\xa5\x01\x02\x03\x26\x20\x01\x21\x58\x20\xf8\xa3"
ASSERT_EQ(msg.m_authDataRaw.size(), 164);
AssertEq(msg.m_authDataRaw, blob + 11, msg.m_authDataRaw.size());
AssertEq(msg.m_authData.m_rpIdHash, blob + 11, 32);
+ ASSERT_NO_THROW(VerifyRpIdHash("acme.com", msg.m_authData.m_rpIdHash));
ASSERT_EQ(msg.m_authData.m_flags, blob[43]);
ASSERT_TRUE(msg.m_authData.m_attestationData.has_value());
AssertEq(msg.m_authData.m_attestationData->m_credentialId, blob + 66, 32);
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"
+ "\x02\x58\x25\x11\x94\x22\x8d\xa8\xfd\xbd\xee\xfd\x26\x1b\xd7\xb6\x59\x5c\xfd\x70\xa5\x0d"
+ "\x70\xc6\x40\x7b\xcf\x01\x3d\xe9\x6d\x4e\xfb\x17\xde\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"
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_NO_THROW(VerifyRpIdHash("acme.com", msg.m_authData.m_rpIdHash));
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);