Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
elliptic_webauthn.cpp
Go to the documentation of this file.
5
6#include <fc/fwd_impl.hpp>
8#include <fc/log/logger.hpp>
9
10#define RAPIDJSON_NAMESPACE_BEGIN namespace fc::crypto::webauthn::detail::rapidjson {
11#define RAPIDJSON_NAMESPACE_END }
12#include "rapidjson/reader.h"
13
14#include <string>
15
16namespace fc { namespace crypto { namespace webauthn {
17
18namespace detail {
19using namespace std::literals;
20
21struct webauthn_json_handler : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, webauthn_json_handler> {
22 std::string found_challenge;
23 std::string found_origin;
24 std::string found_type;
25
36
37 bool basic_stuff() {
39 return true;
42 return true;
43 }
44 return false;
45 }
46
47 bool Null() {
48 return basic_stuff();
49 }
50 bool Bool(bool) {
51 return basic_stuff();
52 }
53 bool Int(int) {
54 return basic_stuff();
55 }
56 bool Uint(unsigned) {
57 return basic_stuff();
58 }
59 bool Int64(int64_t) {
60 return basic_stuff();
61 }
63 return basic_stuff();
64 }
65 bool Double(double) {
66 return basic_stuff();
67 }
68
69 bool String(const char* str, rapidjson::SizeType length, bool copy) {
70 switch(current_state) {
73 return false;
75 found_challenge = std::string(str, length);
77 return true;
79 found_origin = std::string(str, length);
81 return true;
83 found_type = std::string(str, length);
85 return true;
88 return true;
90 return true;
91 }
92 }
93
94 bool StartObject() {
95 switch(current_state) {
98 return true;
102 return true;
105 return true;
110 return false;
111 }
112 }
113 bool Key(const char* str, rapidjson::SizeType length, bool copy) {
114 switch(current_state) {
120 return false;
122 if("challenge"s == str)
124 else if("origin"s == str)
126 else if("type"s == str)
128 else
130 return true;
131 }
133 return true;
134 }
135 }
136 bool EndObject(rapidjson::SizeType memberCount) {
137 switch(current_state) {
143 return false;
147 return true;
149 return true;
150 }
151 }
152
153 bool StartArray() {
154 switch(current_state) {
158 return true;
161 return true;
167 return false;
168 }
169 }
170 bool EndArray(rapidjson::SizeType elementCount) {
171 switch(current_state) {
178 return false;
182 return true;
183 }
184 }
185};
186} //detail
187
188
191 detail::rapidjson::Reader reader;
192 detail::rapidjson::StringStream ss(c.client_json.c_str());
193 FC_ASSERT(reader.Parse<detail::rapidjson::kParseIterativeFlag>(ss, handler), "Failed to parse client data JSON");
194
195 FC_ASSERT(handler.found_type == "webauthn.get", "webauthn signature type not an assertion");
196
197 std::string challenge_bytes = fc::base64url_decode(handler.found_challenge);
198 FC_ASSERT(fc::sha256(challenge_bytes.data(), challenge_bytes.size()) == digest, "Wrong webauthn challenge");
199
200 char required_origin_scheme[] = "https://";
201 size_t https_len = strlen(required_origin_scheme);
202 FC_ASSERT(handler.found_origin.compare(0, https_len, required_origin_scheme) == 0, "webauthn origin must begin with https://");
203 rpid = handler.found_origin.substr(https_len, handler.found_origin.rfind(':')-https_len);
204
205 constexpr static size_t min_auth_data_size = 37;
206 FC_ASSERT(c.auth_data.size() >= min_auth_data_size, "auth_data not as large as required");
207 if(c.auth_data[32] & 0x01)
208 user_verification_type = user_presence_t::USER_PRESENCE_PRESENT;
209 if(c.auth_data[32] & 0x04)
210 user_verification_type = user_presence_t::USER_PRESENCE_VERIFIED;
211
212 static_assert(min_auth_data_size >= sizeof(fc::sha256), "auth_data min size not enough to store a sha256");
213 FC_ASSERT(memcmp(c.auth_data.data(), fc::sha256::hash(rpid).data(), sizeof(fc::sha256)) == 0, "webauthn rpid hash doesn't match origin");
214
215 //the signature (and thus public key we need to return) will be over
216 // sha256(auth_data || client_data_hash)
217 fc::sha256 client_data_hash = fc::sha256::hash(c.client_json);
219 e.write((char*)c.auth_data.data(), c.auth_data.size());
220 e.write(client_data_hash.data(), client_data_hash.data_size());
221 fc::sha256 signed_digest = e.result();
222
223 //quite a bit of this copied from elliptic_r1, can probably commonize
224 int nV = c.compact_signature.data[0];
225 if (nV<31 || nV>=35)
226 FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" );
227 ecdsa_sig sig = ECDSA_SIG_new();
228 BIGNUM *r = BN_new(), *s = BN_new();
229 BN_bin2bn(&c.compact_signature.data[1],32,r);
230 BN_bin2bn(&c.compact_signature.data[33],32,s);
231 ECDSA_SIG_set0(sig, r, s);
232
233 fc::ec_key key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
234 nV -= 4;
235
236 if (r1::ECDSA_SIG_recover_key_GFp(key, sig, (uint8_t*)signed_digest.data(), signed_digest.data_size(), nV - 27, 0) == 1) {
237 const EC_POINT* point = EC_KEY_get0_public_key(key);
238 const EC_GROUP* group = EC_KEY_get0_group(key);
239 size_t sz = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, (uint8_t*)public_key_data.data, public_key_data.size(), NULL);
240 if(sz == public_key_data.size())
241 return;
242 }
243 FC_THROW_EXCEPTION( exception, "unable to reconstruct public key from signature" );
244}
245
246void public_key::post_init() {
247 FC_ASSERT(rpid.length(), "webauthn pubkey must have non empty rpid");
248}
249
250}}}
const mie::Vuint & r
Definition bn.cpp:28
size_t size() const
Definition array.hpp:35
T data[N]
Definition array.hpp:37
void write(const char *d, uint32_t dlen)
Definition sha256.cpp:59
static sha256 hash(const char *d, uint32_t dlen)
Definition sha256.cpp:44
const char * data() const
Definition sha256.cpp:31
size_t data_size() const
Definition sha256.hpp:23
Defines exception's used by fc.
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
bignum_st BIGNUM
Definition bigint.hpp:7
int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
namespace sysio::chain
Definition authority.cpp:3
fc::sha256 digest(const T &value)
Definition digest.hpp:9
std::string base64url_decode(const std::string &encoded_string)
Definition base64.cpp:156
void copy(const path &from, const path &to)
int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
signed __int64 int64_t
Definition stdint.h:135
unsigned char uint8_t
Definition stdint.h:124
unsigned __int64 uint64_t
Definition stdint.h:136
enum fc::crypto::webauthn::detail::webauthn_json_handler::parse_stat_t current_state
bool Key(const char *str, rapidjson::SizeType length, bool copy)
bool EndObject(rapidjson::SizeType memberCount)
bool String(const char *str, rapidjson::SizeType length, bool copy)
bool EndArray(rapidjson::SizeType elementCount)
char * s