1#define BOOST_TEST_MODULE webauthn_test_mod
2#include <boost/test/included/unit_test.hpp>
3#include <boost/algorithm/string.hpp>
12using namespace std::literals;
15 std::vector<uint8_t>& auth_data,
16 const std::string&
json) {
21 e.
write((
char*)auth_data.data(), auth_data.size());
45BOOST_AUTO_TEST_SUITE(webauthn_suite)
49 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
52 std::vector<uint8_t> auth_data(37);
54 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
56 BOOST_CHECK_EQUAL(wa_pub, make_webauthn_sig(priv, auth_data,
json).recover(d,
true));
61 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_PRESENT,
"fctesting.invalid");
64 std::vector<uint8_t> auth_data(37);
65 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
66 auth_data[32] |= 0x04;
68 BOOST_CHECK_NE(wa_pub, make_webauthn_sig(priv, auth_data,
json).recover(d,
true));
73 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_PRESENT,
"fctesting.invalid");
76 std::vector<uint8_t> auth_data(37);
78 memcpy(auth_data.data(), mallory_origin_hash.
data(),
sizeof(mallory_origin_hash));
80 BOOST_CHECK_NE(wa_pub, make_webauthn_sig(priv, auth_data,
json).recover(d,
true));
85 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
88 std::vector<uint8_t> auth_data(37);
89 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
91 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
92 return e.to_detail_string().find(
"webauthn origin must begin with https://") != std::string::npos;
98 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
101 std::vector<uint8_t> auth_data(37);
102 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
104 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
105 return e.to_detail_string().find(
"webauthn origin must begin with https://") != std::string::npos;
111 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
114 std::vector<uint8_t> auth_data(37);
115 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
117 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
118 return e.to_detail_string().find(
"webauthn origin must begin with https://") != std::string::npos;
124 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
127 std::vector<uint8_t> auth_data(37);
128 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
130 BOOST_CHECK_EQUAL(wa_pub, make_webauthn_sig(priv, auth_data,
json).recover(d,
true));
135 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
138 std::vector<uint8_t> auth_data(37);
139 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
141 BOOST_CHECK_EQUAL(wa_pub, make_webauthn_sig(priv, auth_data,
json).recover(d,
true));
146 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
147 std::string
json =
"{\"origin\":\"https://fctesting.invalid\",\"type\":\"webauthn.get\",\"challenge\":\"" +
"blahBLAH"s +
"\"}";
149 std::vector<uint8_t> auth_data(37);
150 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
153 return e.
to_detail_string().find(
"sha256: size mismatch") != std::string::npos;
159 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
160 std::string
json =
"{\"origin\":\"https://fctesting.invalid\",\"type\":\"webauthn.get\",\"challenge\":\"" +
"hello@world$"s +
"\"}";
162 std::vector<uint8_t> auth_data(37);
163 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
166 return e.
to_detail_string().find(
"encountered non-base64 character") != std::string::npos;
172 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
174 std::string
json =
"{\"origin\":\"https://fctesting.invalid\",\"type\":\"webauthn.get\",\"challenge\":\"" +
fc::base64url_encode(other_digest.
data(), other_digest.
data_size()) +
"\"}";
176 std::vector<uint8_t> auth_data(37);
177 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
179 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
180 return e.to_detail_string().find(
"Wrong webauthn challenge") != std::string::npos;
187 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
190 std::vector<uint8_t> auth_data(37);
191 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
193 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
194 return e.to_detail_string().find(
"webauthn signature type not an assertion") != std::string::npos;
200 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
203 std::vector<uint8_t> auth_data(37);
205 memcpy(auth_data.data(), origin_hash_corrupt.
data(),
sizeof(origin_hash_corrupt));
208 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
209 return e.to_detail_string().find(
"webauthn rpid hash doesn't match origin") != std::string::npos;
215 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
218 std::vector<uint8_t> auth_data(1);
220 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
221 return e.to_detail_string().find(
"auth_data not as large as required") != std::string::npos;
227 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
230 std::vector<uint8_t> auth_data(37);
231 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
233 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
234 return e.to_detail_string().find(
"webauthn origin must begin with https://") != std::string::npos;
240 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
243 std::vector<uint8_t> auth_data(37);
244 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
246 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
247 return e.to_detail_string().find(
"webauthn signature type not an assertion") != std::string::npos;
253 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
254 std::string
json =
"{\"origin\":\"https://fctesting.invalid\",\"type\":\"webauthn.get\"}";
256 std::vector<uint8_t> auth_data(37);
257 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
260 return e.
to_detail_string().find(
"sha256: size mismatch") != std::string::npos;
266 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
267 std::string
json =
"{\"origin\":\"https://fctesting.invalid\",\"cool\":\"beans\",\"obj\":{\"array\":[4, 5, 6]},"
270 std::vector<uint8_t> auth_data(37);
271 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
273 BOOST_CHECK_EQUAL(wa_pub, make_webauthn_sig(priv, auth_data,
json).recover(d,
true));
278 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
279 std::string
json =
"hey man"s;
281 std::vector<uint8_t> auth_data(37);
282 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
284 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
285 return e.to_detail_string().find(
"Failed to parse client data JSON") != std::string::npos;
291 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
294 std::vector<uint8_t> auth_data(37);
295 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
305 bool failed_recovery =
false;
306 bool failed_compare =
false;
310 recovered_pub = sig.
recover(d,
true);
311 failed_compare = !(wa_pub == recovered_pub);
314 failed_recovery = e.
to_detail_string().find(
"unable to reconstruct public key from signature") != std::string::npos;
318 BOOST_CHECK_EQUAL(failed_recovery || failed_compare,
true);
323 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
326 std::vector<uint8_t> auth_data(37);
327 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
337 bool failed_recovery =
false;
338 bool failed_compare =
false;
342 recovered_pub = sig.
recover(d,
true);
343 failed_compare = !(wa_pub == recovered_pub);
346 failed_recovery = e.
to_detail_string().find(
"unable to reconstruct public key from signature") != std::string::npos;
350 BOOST_CHECK_EQUAL(failed_recovery || failed_compare,
true);
357 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
360 std::vector<uint8_t> auth_data(37);
361 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
363 BOOST_CHECK_NE(wa_pub, make_webauthn_sig(other_priv, auth_data,
json).recover(d,
true));
368 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
371 std::vector<uint8_t> auth_data(37);
372 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
374 BOOST_CHECK_EXCEPTION(make_webauthn_sig(priv, auth_data,
json).recover(d,
true), fc::assert_exception, [](
const fc::assert_exception& e) {
375 return e.to_detail_string().find(
"Failed to parse client data JSON") != std::string::npos;
385 BOOST_CHECK_EXCEPTION(
fc::raw::unpack(ds,
pubkey), fc::assert_exception, [](
const fc::assert_exception& e) {
386 return e.to_detail_string().find(
"webauthn pubkey must have non empty rpid") != std::string::npos;
392 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
395 boost::erase_all(
json,
"=");
397 std::vector<uint8_t> auth_data(37);
398 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
400 BOOST_CHECK_EQUAL(wa_pub, make_webauthn_sig(priv, auth_data,
json).recover(d,
true));
407 webauthn::public_key wa_pub(pub.serialize(), webauthn::public_key::user_presence_t::USER_PRESENCE_NONE,
"fctesting.invalid");
410 boost::erase_all(
json,
"=");
412 std::vector<uint8_t> auth_data(37);
413 memcpy(auth_data.data(), origin_hash.
data(),
sizeof(origin_hash));
416 return e.
to_detail_string().find(
"encountered non-base64 character") != std::string::npos;
420BOOST_AUTO_TEST_SUITE_END()
an elliptic curve private key.
compact_signature sign_compact(const fc::sha256 &digest) const
static private_key generate()
contains only the public point of an elliptic curve key.
public_key recover(const sha256 &digest, bool check_canonical) const
Used to generate a useful error report when an exception is thrown.
std::string to_detail_string(log_level ll=log_level::all) const
void write(const char *d, uint32_t dlen)
static sha256 hash(const char *d, uint32_t dlen)
const char * data() const
void unpack(Stream &s, std::deque< T > &value)
void pack(Stream &s, const std::deque< T > &value)
std::string base64url_encode(unsigned char const *bytes_to_encode, unsigned int in_len)
BOOST_AUTO_TEST_CASE(good)
memcpy((char *) pInfo->slotDescription, s, l)