Skip to content

Commit dabc794

Browse files
committed
webauthn support
1 parent 7de4582 commit dabc794

17 files changed

Lines changed: 384 additions & 16 deletions

libraries/chain/authorization_manager.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <eosio/chain/generated_transaction_object.hpp>
1515
#include <boost/tuple/tuple_io.hpp>
1616
#include <eosio/chain/database_utils.hpp>
17+
#include <eosio/chain/protocol_state_object.hpp>
1718

1819

1920
namespace eosio { namespace chain {
@@ -140,6 +141,10 @@ namespace eosio { namespace chain {
140141
time_point initial_creation_time
141142
)
142143
{
144+
for(const key_weight& k: auth.keys)
145+
EOS_ASSERT(k.key.which() < _db.get<protocol_state_object>().num_supported_key_types, unactivated_key_type,
146+
"Unactivated key type used when creating permission");
147+
143148
auto creation_time = initial_creation_time;
144149
if( creation_time == time_point() ) {
145150
creation_time = _control.pending_block_time();
@@ -167,6 +172,10 @@ namespace eosio { namespace chain {
167172
time_point initial_creation_time
168173
)
169174
{
175+
for(const key_weight& k: auth.keys)
176+
EOS_ASSERT(k.key.which() < _db.get<protocol_state_object>().num_supported_key_types, unactivated_key_type,
177+
"Unactivated key type used when creating permission");
178+
170179
auto creation_time = initial_creation_time;
171180
if( creation_time == time_point() ) {
172181
creation_time = _control.pending_block_time();
@@ -188,6 +197,10 @@ namespace eosio { namespace chain {
188197
}
189198

190199
void authorization_manager::modify_permission( const permission_object& permission, const authority& auth ) {
200+
for(const key_weight& k: auth.keys)
201+
EOS_ASSERT(k.key.which() < _db.get<protocol_state_object>().num_supported_key_types, unactivated_key_type,
202+
"Unactivated key type used when modifying permission");
203+
191204
_db.modify( permission, [&](permission_object& po) {
192205
po.auth = auth;
193206
po.last_updated = _control.pending_block_time();

libraries/chain/controller.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ struct controller_impl {
312312
set_activation_handler<builtin_protocol_feature_t::preactivate_feature>();
313313
set_activation_handler<builtin_protocol_feature_t::replace_deferred>();
314314
set_activation_handler<builtin_protocol_feature_t::get_sender>();
315+
set_activation_handler<builtin_protocol_feature_t::webauthn_key>();
315316

316317
self.irreversible_block.connect([this](const block_state_ptr& bsp) {
317318
wasmif.current_lib(bsp->block_num);
@@ -1040,7 +1041,8 @@ struct controller_impl {
10401041
|| (code == contract_whitelist_exception::code_value)
10411042
|| (code == contract_blacklist_exception::code_value)
10421043
|| (code == action_blacklist_exception::code_value)
1043-
|| (code == key_blacklist_exception::code_value);
1044+
|| (code == key_blacklist_exception::code_value)
1045+
|| (code == sig_variable_size_limit_exception::code_value);
10441046
}
10451047

10461048
bool scheduled_failure_is_subjective( const fc::exception& e ) const {
@@ -2931,6 +2933,10 @@ bool controller::is_ram_billing_in_notify_allowed()const {
29312933
return my->conf.disable_all_subjective_mitigations || !is_producing_block() || my->conf.allow_ram_billing_in_notify;
29322934
}
29332935

2936+
uint32_t controller::configured_subjective_signature_length_limit()const {
2937+
return my->conf.maximum_variable_signature_length;
2938+
}
2939+
29342940
void controller::validate_expiration( const transaction& trx )const { try {
29352941
const auto& chain_configuration = get_global_properties().configuration;
29362942

@@ -3072,6 +3078,13 @@ void controller_impl::on_activation<builtin_protocol_feature_t::replace_deferred
30723078
}
30733079
}
30743080

3081+
template<>
3082+
void controller_impl::on_activation<builtin_protocol_feature_t::webauthn_key>() {
3083+
db.modify( db.get<protocol_state_object>(), [&]( auto& ps ) {
3084+
ps.num_supported_key_types = 3;
3085+
} );
3086+
}
3087+
30753088
/// End of protocol feature activation handlers
30763089

30773090
} } /// eosio::chain

libraries/chain/include/eosio/chain/authority.hpp

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,78 @@
1111

1212
namespace eosio { namespace chain {
1313

14+
using shared_public_key_data = fc::static_variant<fc::ecc::public_key_shim, fc::crypto::r1::public_key_shim, shared_string>;
15+
16+
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
17+
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
18+
19+
struct shared_public_key {
20+
shared_public_key( shared_public_key_data&& p ) :
21+
pubkey(std::move(p)) {}
22+
23+
operator public_key_type() const {
24+
fc::crypto::public_key::storage_type public_key_storage;
25+
pubkey.visit(overloaded {
26+
[&](const auto& k1r1) {
27+
public_key_storage = k1r1;
28+
},
29+
[&](const shared_string& wa) {
30+
fc::datastream ds(wa.data(), wa.size());
31+
fc::crypto::webauthn::public_key pub;
32+
fc::raw::unpack(ds, pub);
33+
public_key_storage = pub;
34+
}
35+
});
36+
return std::move(public_key_storage);
37+
}
38+
39+
operator string() const {
40+
return (string)this->operator public_key_type();
41+
}
42+
43+
shared_public_key_data pubkey;
44+
45+
friend bool operator == ( const shared_public_key& lhs, const shared_public_key& rhs ) {
46+
if(lhs.pubkey.which() != rhs.pubkey.which())
47+
return false;
48+
49+
return lhs.pubkey.visit<bool>(overloaded {
50+
[&](const fc::ecc::public_key_shim& k1) {
51+
return k1._data == rhs.pubkey.get<fc::ecc::public_key_shim>()._data;
52+
},
53+
[&](const fc::crypto::r1::public_key_shim& r1) {
54+
return r1._data == rhs.pubkey.get<fc::crypto::r1::public_key_shim>()._data;
55+
},
56+
[&](const shared_string& wa) {
57+
return wa == rhs.pubkey.get<shared_string>();
58+
}
59+
});
60+
}
61+
62+
friend bool operator==(const shared_public_key& l, const public_key_type& r) {
63+
if(l.pubkey.which() != r._storage.which())
64+
return false;
65+
66+
return l.pubkey.visit<bool>(overloaded {
67+
[&](const fc::ecc::public_key_shim& k1) {
68+
return k1._data == r._storage.get<fc::ecc::public_key_shim>()._data;
69+
},
70+
[&](const fc::crypto::r1::public_key_shim& r1) {
71+
return r1._data == r._storage.get<fc::crypto::r1::public_key_shim>()._data;
72+
},
73+
[&](const shared_string& wa) {
74+
fc::datastream ds(wa.data(), wa.size());
75+
fc::crypto::webauthn::public_key pub;
76+
fc::raw::unpack(ds, pub);
77+
return pub == r._storage.get<fc::crypto::webauthn::public_key>();
78+
}
79+
});
80+
}
81+
82+
friend bool operator==(const public_key_type& l, const shared_public_key& r) {
83+
return r == l;
84+
}
85+
};
1486

1587
struct permission_level_weight {
1688
permission_level permission;
@@ -30,6 +102,23 @@ struct key_weight {
30102
}
31103
};
32104

105+
106+
struct shared_key_weight {
107+
shared_key_weight(shared_public_key_data&& k, const weight_type& w) :
108+
key(std::move(k)), weight(w) {}
109+
110+
operator key_weight() const {
111+
return key_weight{key, weight};
112+
}
113+
114+
shared_public_key key;
115+
weight_type weight;
116+
117+
friend bool operator == ( const shared_key_weight& lhs, const shared_key_weight& rhs ) {
118+
return tie( lhs.key, lhs.weight ) == tie( rhs.key, rhs.weight );
119+
}
120+
};
121+
33122
struct wait_weight {
34123
uint32_t wait_sec;
35124
weight_type weight;
@@ -100,14 +189,31 @@ struct shared_authority {
100189

101190
shared_authority& operator=(const authority& a) {
102191
threshold = a.threshold;
103-
keys = decltype(keys)(a.keys.begin(), a.keys.end(), keys.get_allocator());
192+
keys.clear();
193+
keys.reserve(a.keys.size());
194+
for(const key_weight& k : a.keys) {
195+
k.key._storage.visit(overloaded {
196+
[&](const auto& k1r1) {
197+
keys.emplace_back(k1r1, k.weight);
198+
},
199+
[&](const fc::crypto::webauthn::public_key& wa) {
200+
fc::datastream<size_t> dsz;
201+
fc::raw::pack(dsz, wa);
202+
shared_string wa_ss(dsz.tellp(), boost::container::default_init, keys.get_allocator());
203+
fc::datastream<char*> ds(wa_ss.data(), wa_ss.size());
204+
fc::raw::pack(ds, wa);
205+
206+
keys.emplace_back(std::move(wa_ss), k.weight);
207+
}
208+
});
209+
}
104210
accounts = decltype(accounts)(a.accounts.begin(), a.accounts.end(), accounts.get_allocator());
105211
waits = decltype(waits)(a.waits.begin(), a.waits.end(), waits.get_allocator());
106212
return *this;
107213
}
108214

109215
uint32_t threshold = 0;
110-
shared_vector<key_weight> keys;
216+
shared_vector<shared_key_weight> keys;
111217
shared_vector<permission_level_weight> accounts;
112218
shared_vector<wait_weight> waits;
113219

@@ -202,4 +308,6 @@ FC_REFLECT(eosio::chain::permission_level_weight, (permission)(weight) )
202308
FC_REFLECT(eosio::chain::key_weight, (key)(weight) )
203309
FC_REFLECT(eosio::chain::wait_weight, (wait_sec)(weight) )
204310
FC_REFLECT(eosio::chain::authority, (threshold)(keys)(accounts)(waits) )
311+
FC_REFLECT(eosio::chain::shared_key_weight, (key)(weight) )
205312
FC_REFLECT(eosio::chain::shared_authority, (threshold)(keys)(accounts)(waits) )
313+
FC_REFLECT(eosio::chain::shared_public_key, (pubkey))

libraries/chain/include/eosio/chain/authority_checker.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace eosio { namespace chain {
2121
namespace detail {
2222

2323
// Order of the template types in the static_variant matters to meta_permission_comparator.
24-
using meta_permission = static_variant<permission_level_weight, key_weight, wait_weight>;
24+
using meta_permission = static_variant<permission_level_weight, shared_key_weight, wait_weight>;
2525

2626
struct get_weight_visitor {
2727
using result_type = uint32_t;
@@ -224,7 +224,7 @@ namespace detail {
224224
return total_weight;
225225
}
226226

227-
uint32_t operator()(const key_weight& permission) {
227+
uint32_t operator()(const shared_key_weight& permission) {
228228
auto itr = boost::find( checker.provided_keys, permission.key );
229229
if( itr != checker.provided_keys.end() ) {
230230
checker._used_keys[itr - checker.provided_keys.begin()] = true;

libraries/chain/include/eosio/chain/chain_snapshot.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ struct chain_snapshot_header {
1515
* 2: Updated chain snapshot for v1.8.0 initial protocol features release:
1616
* - Incompatible with version 1.
1717
* - Adds new indices for: protocol_state_object and account_ram_correction_object
18+
* 3: Updated for v2.0.0 protocol features:
19+
* - WebAuthn keys
1820
*/
1921

2022
static constexpr uint32_t minimum_compatible_version = 2;
21-
static constexpr uint32_t current_version = 2;
23+
static constexpr uint32_t current_version = 3;
2224

2325
uint32_t version = current_version;
2426

libraries/chain/include/eosio/chain/config.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const static uint16_t default_max_inline_action_depth = 4;
8181
const static uint16_t default_max_auth_depth = 6;
8282
const static uint32_t default_sig_cpu_bill_pct = 50 * percent_1; // billable percentage of signature recovery
8383
const static uint16_t default_controller_thread_pool_size = 2;
84+
const static uint32_t default_max_variable_signature_length = 16384u;
8485

8586
const static uint32_t min_net_usage_delta_between_base_and_max_for_trx = 10*1024;
8687
// Should be large enough to allow recovery from badly set blockchain parameters without a hard fork

libraries/chain/include/eosio/chain/controller.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ namespace eosio { namespace chain {
7676
bool disable_replay_opts = false;
7777
bool contracts_console = false;
7878
bool allow_ram_billing_in_notify = false;
79+
uint32_t maximum_variable_signature_length = chain::config::default_max_variable_signature_length;
7980
bool disable_all_subjective_mitigations = false; //< for testing purposes only
8081

8182
genesis_state genesis;
@@ -240,6 +241,11 @@ namespace eosio { namespace chain {
240241

241242
bool is_ram_billing_in_notify_allowed()const;
242243

244+
//This is only an accessor to the user configured subjective limit: i.e. it does not do a
245+
// check similar to is_ram_billing_in_notify_allowed() to check if controller is currently
246+
// producing a block
247+
uint32_t configured_subjective_signature_length_limit()const;
248+
243249
void add_resource_greylist(const account_name &name);
244250
void remove_resource_greylist(const account_name &name);
245251
bool is_resource_greylisted(const account_name &name) const;

libraries/chain/include/eosio/chain/exceptions.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ namespace eosio { namespace chain {
198198
3010013, "Invalid fixed key" )
199199
FC_DECLARE_DERIVED_EXCEPTION( symbol_type_exception, chain_type_exception,
200200
3010014, "Invalid symbol" )
201+
FC_DECLARE_DERIVED_EXCEPTION( unactivated_key_type, chain_type_exception,
202+
3010015, "Key type is not a currently activated type" )
203+
FC_DECLARE_DERIVED_EXCEPTION( unactivated_signature_type, chain_type_exception,
204+
3010016, "Signature type is not a currently activated type" )
201205

202206

203207
FC_DECLARE_DERIVED_EXCEPTION( fork_database_exception, chain_exception,
@@ -401,7 +405,8 @@ namespace eosio { namespace chain {
401405
3100008, "Feature is currently unsupported" )
402406
FC_DECLARE_DERIVED_EXCEPTION( node_management_success, misc_exception,
403407
3100009, "Node management operation successfully executed" )
404-
408+
FC_DECLARE_DERIVED_EXCEPTION( sig_variable_size_limit_exception, misc_exception,
409+
3100010, "Variable length component of signature too large" )
405410

406411

407412
FC_DECLARE_DERIVED_EXCEPTION( plugin_exception, chain_exception,

libraries/chain/include/eosio/chain/protocol_feature_manager.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ enum class builtin_protocol_feature_t : uint32_t {
2424
only_bill_first_authorizer,
2525
forward_setcode,
2626
get_sender,
27-
ram_restrictions
27+
ram_restrictions,
28+
webauthn_key
2829
};
2930

3031
struct protocol_feature_subjective_restrictions {

libraries/chain/include/eosio/chain/transaction_metadata.hpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,23 @@ class transaction_metadata {
4343
transaction_metadata operator=(transaction_metadata&) = delete;
4444
transaction_metadata operator=(transaction_metadata&&) = delete;
4545

46-
explicit transaction_metadata( const signed_transaction& t, packed_transaction::compression_type c = packed_transaction::none )
46+
explicit transaction_metadata( const signed_transaction& t, uint32_t max_variable_sig_size = UINT32_MAX, packed_transaction::compression_type c = packed_transaction::none )
4747
: _packed_trx( std::make_shared<packed_transaction>( t, c ) )
4848
, _id( t.id() )
4949
, _signed_id( digest_type::hash( *_packed_trx ) ) {
50+
check_variable_sig_size(max_variable_sig_size);
5051
}
5152

52-
explicit transaction_metadata( const packed_transaction_ptr& ptrx )
53+
explicit transaction_metadata( const packed_transaction_ptr& ptrx, uint32_t max_variable_sig_size = UINT32_MAX )
5354
: _packed_trx( ptrx )
5455
, _id( ptrx->id() )
5556
, _signed_id( digest_type::hash( *_packed_trx ) ) {
57+
check_variable_sig_size(max_variable_sig_size);
58+
}
59+
60+
void check_variable_sig_size(uint32_t max) {
61+
for(const signature_type& sig : _packed_trx->get_signed_transaction().signatures)
62+
EOS_ASSERT(sig.variable_size() <= max, sig_variable_size_limit_exception, "signature variable length component size (${s}) greater than subjective maximum (${m})", ("s", sig.variable_size())("m", max));
5663
}
5764

5865
const packed_transaction_ptr& packed_trx()const { return _packed_trx; }

0 commit comments

Comments
 (0)