@@ -16,6 +16,7 @@ use std::error::Error;
16
16
use std:: fs:: File ;
17
17
use std:: path:: Path ;
18
18
use tiny_keccak:: Keccak ;
19
+ use web3:: transports:: Http ;
19
20
use web3:: {
20
21
contract:: { Contract , Options } ,
21
22
futures:: Future ,
@@ -34,8 +35,10 @@ pub struct EthereumAdapter {
34
35
keystore_json : String ,
35
36
keystore_pwd : Password ,
36
37
config : Config ,
37
- tokens_verified : RefCell < HashMap < String , Session > > ,
38
- tokens_for_auth : RefCell < HashMap < String , String > > ,
38
+ // Auth tokens that we have verified (tokenId => session)
39
+ session_tokens : RefCell < HashMap < String , Session > > ,
40
+ // Auth tokens that we've generated to authenticate with someone (address => token)
41
+ authorization_tokens : RefCell < HashMap < String , String > > ,
39
42
wallet : RefCell < Option < SafeAccount > > ,
40
43
}
41
44
@@ -59,8 +62,8 @@ impl Adapter for EthereumAdapter {
59
62
Ok ( Self {
60
63
keystore_json,
61
64
keystore_pwd : keystore_pwd. into ( ) ,
62
- tokens_verified : RefCell :: new ( HashMap :: new ( ) ) ,
63
- tokens_for_auth : RefCell :: new ( HashMap :: new ( ) ) ,
65
+ session_tokens : RefCell :: new ( HashMap :: new ( ) ) ,
66
+ authorization_tokens : RefCell :: new ( HashMap :: new ( ) ) ,
64
67
wallet : RefCell :: new ( None ) ,
65
68
config : config. to_owned ( ) ,
66
69
} )
@@ -87,12 +90,11 @@ impl Adapter for EthereumAdapter {
87
90
fn whoami ( & self ) -> AdapterResult < String > {
88
91
match self . wallet . borrow ( ) . clone ( ) {
89
92
Some ( wallet) => {
90
- let public = & wallet
93
+ let public = wallet
91
94
. public ( & self . keystore_pwd )
92
95
. map_err ( |_| map_error ( "Failed to get public key" ) ) ?;
93
- let address = format ! ( "{:?}" , public_to_address( public) ) ;
94
- let checksum_address = eth_checksum:: checksum ( & address) ;
95
- Ok ( checksum_address)
96
+ let address = format ! ( "{:?}" , public_to_address( & public) ) ;
97
+ Ok ( eth_checksum:: checksum ( & address) )
96
98
}
97
99
None => Err ( AdapterError :: Configuration (
98
100
"Unlock wallet before use" . to_string ( ) ,
@@ -137,16 +139,7 @@ impl Adapter for EthereumAdapter {
137
139
}
138
140
139
141
fn validate_channel ( & self , channel : & Channel ) -> AdapterResult < bool > {
140
- let ( _eloop, transport) = web3:: transports:: Http :: new ( & self . config . ethereum_network )
141
- . map_err ( |_| map_error ( "Failed to initialise web3 transport" ) ) ?;
142
- let web3 = web3:: Web3 :: new ( transport) ;
143
- let contract_address = Address :: from_slice ( self . config . ethereum_core_address . as_bytes ( ) ) ;
144
-
145
- let contract = Contract :: from_json ( web3. eth ( ) , contract_address, & ADEXCORE_ABI )
146
- . map_err ( |_| map_error ( "Failed to initialise web3 transport" ) ) ?;
147
-
148
142
let eth_channel: EthereumChannel = channel. into ( ) ;
149
-
150
143
let channel_id = eth_channel
151
144
. hash_hex ( & self . config . ethereum_core_address )
152
145
. map_err ( |_| map_error ( "Failed to hash the channel id" ) ) ?;
@@ -157,14 +150,7 @@ impl Adapter for EthereumAdapter {
157
150
) ) ;
158
151
}
159
152
160
- let validators: Vec < & str > = channel
161
- . spec
162
- . validators
163
- . into_iter ( )
164
- . map ( |v| & v. id [ ..] )
165
- . collect ( ) ;
166
- let invalid_address_checkum = check_address_checksum ( & validators) ;
167
- if invalid_address_checkum {
153
+ if check_validator_id_checksum ( channel) {
168
154
return Err ( AdapterError :: Configuration (
169
155
"channel.validators: all addresses are checksummed" . to_string ( ) ,
170
156
) ) ;
@@ -173,13 +159,19 @@ impl Adapter for EthereumAdapter {
173
159
let is_channel_valid = EthereumAdapter :: is_channel_valid ( & self . config , channel) ;
174
160
if is_channel_valid. is_err ( ) {
175
161
return Err ( AdapterError :: InvalidChannel (
176
- is_channel_valid. err ( ) . unwrap ( ) . to_string ( ) ,
162
+ is_channel_valid
163
+ . err ( )
164
+ . expect ( "failed to get channel error" )
165
+ . to_string ( ) ,
177
166
) ) ;
178
167
}
179
-
180
168
// query the blockchain for the channel status
181
- let contract_query = contract. query ( "states" , channel_id, None , Options :: default ( ) , None ) ;
182
- let channel_status: U256 = contract_query
169
+ let contract_address = Address :: from_slice ( self . config . ethereum_core_address . as_bytes ( ) ) ;
170
+ let contract = get_contract ( & self . config , contract_address, & ADEXCORE_ABI )
171
+ . map_err ( |_| map_error ( "failed to init core contract" ) ) ?;
172
+
173
+ let channel_status: U256 = contract
174
+ . query ( "states" , channel_id, None , Options :: default ( ) , None )
183
175
. wait ( )
184
176
. map_err ( |_| map_error ( "contract channel status query failed" ) ) ?;
185
177
@@ -193,40 +185,49 @@ impl Adapter for EthereumAdapter {
193
185
}
194
186
195
187
fn session_from_token ( & self , token : & str ) -> AdapterResult < Session > {
196
- let token_id = token. to_owned ( ) [ ..16 ] . to_string ( ) ;
197
- let result = self . tokens_verified . borrow_mut ( ) ;
198
- if result. get ( & token_id) . is_some ( ) {
199
- return Ok ( result. get ( & token_id) . unwrap ( ) . to_owned ( ) ) ;
188
+ let token_id = token. to_owned ( ) [ token. len ( ) - 16 ..] . to_string ( ) ;
189
+
190
+ let mut session_tokens = self . session_tokens . borrow_mut ( ) ;
191
+ if session_tokens. get ( & token_id) . is_some ( ) {
192
+ return Ok ( session_tokens
193
+ . get ( & token_id)
194
+ . expect ( "failed to get session" )
195
+ . to_owned ( ) ) ;
200
196
}
201
197
202
- let verified = match ewt_verify ( & token) {
198
+ let parts: Vec < & str > = token. split ( '.' ) . collect ( ) ;
199
+ let ( header_encoded, payload_encoded, token_encoded) =
200
+ match ( parts. get ( 0 ) , parts. get ( 1 ) , parts. get ( 2 ) ) {
201
+ ( Some ( header_encoded) , Some ( payload_encoded) , Some ( token_encoded) ) => {
202
+ ( header_encoded, payload_encoded, token_encoded)
203
+ }
204
+ ( _, _, _) => {
205
+ return Err ( AdapterError :: Failed ( format ! (
206
+ "{} token string is incorrect" ,
207
+ token
208
+ ) ) )
209
+ }
210
+ } ;
211
+
212
+ let verified = match ewt_verify ( header_encoded, payload_encoded, token_encoded) {
203
213
Ok ( v) => v,
204
214
Err ( e) => return Err ( AdapterError :: EwtVerifyFailed ( e. to_string ( ) ) ) ,
205
215
} ;
206
216
207
- let whoami = self . whoami ( ) ?;
208
- if whoami != verified. from {
217
+ if self . whoami ( ) ? != verified. payload . id {
209
218
return Err ( AdapterError :: Configuration (
210
219
"token payload.id !== whoami(): token was not intended for us" . to_string ( ) ,
211
220
) ) ;
212
221
}
213
222
214
223
let sess = match & verified. payload . identity {
215
224
Some ( identity) => {
216
- let ( _eloop, transport) =
217
- web3:: transports:: Http :: new ( & self . config . ethereum_network )
218
- . map_err ( |_| map_error ( "Failed to initialise web3 transport" ) ) ?;
219
- let web3 = web3:: Web3 :: new ( transport) ;
220
-
221
- let contract_address =
222
- Address :: from_slice ( self . config . ethereum_core_address . as_bytes ( ) ) ;
223
-
224
- let contract = Contract :: from_json ( web3. eth ( ) , contract_address, & IDENTITY_ABI )
225
+ let contract_address = Address :: from_slice ( identity. as_bytes ( ) ) ;
226
+ let contract = get_contract ( & self . config , contract_address, & IDENTITY_ABI )
225
227
. map_err ( |_| map_error ( "failed to init identity contract" ) ) ?;
226
228
227
- let contract_query =
228
- contract. query ( "privileges" , verified. from , None , Options :: default ( ) , None ) ;
229
- let priviledge_level: U256 = contract_query
229
+ let priviledge_level: U256 = contract
230
+ . query ( "privileges" , verified. from , None , Options :: default ( ) , None )
230
231
. wait ( )
231
232
. map_err ( |_| map_error ( "failed query priviledge level on contract" ) ) ?;
232
233
@@ -246,32 +247,29 @@ impl Adapter for EthereumAdapter {
246
247
} ,
247
248
} ;
248
249
249
- self . tokens_verified
250
- . borrow_mut ( )
251
- . insert ( token_id, sess. clone ( ) ) ;
250
+ session_tokens. insert ( token_id, sess. clone ( ) ) ;
252
251
Ok ( sess)
253
252
}
254
253
255
254
fn get_auth ( & self , validator : & ValidatorDesc ) -> AdapterResult < String > {
256
- let tokens_for_auth = self . tokens_for_auth . borrow ( ) ;
255
+ let mut authorization_tokens = self . authorization_tokens . borrow_mut ( ) ;
257
256
match (
258
257
self . wallet . borrow ( ) . clone ( ) ,
259
- tokens_for_auth . get ( & validator. id ) ,
258
+ authorization_tokens . get ( & validator. id ) ,
260
259
) {
261
260
( Some ( _) , Some ( token) ) => Ok ( token. to_owned ( ) ) ,
262
261
( Some ( wallet) , None ) => {
262
+ let era = Utc :: now ( ) . timestamp ( ) as f64 / 60000.0 ;
263
263
let payload = Payload {
264
264
id : validator. id . clone ( ) ,
265
- era : Utc :: now ( ) . timestamp ( ) ,
265
+ era : era . floor ( ) as i64 ,
266
266
identity : None ,
267
267
address : None ,
268
268
} ;
269
269
let token = ewt_sign ( & wallet, & self . keystore_pwd , & payload)
270
270
. map_err ( |_| map_error ( "Failed to sign token" ) ) ?;
271
271
272
- self . tokens_for_auth
273
- . borrow_mut ( )
274
- . insert ( validator. id . clone ( ) , token. clone ( ) ) ;
272
+ authorization_tokens. insert ( validator. id . clone ( ) , token. clone ( ) ) ;
275
273
276
274
Ok ( token)
277
275
}
@@ -282,11 +280,12 @@ impl Adapter for EthereumAdapter {
282
280
}
283
281
}
284
282
285
- fn check_address_checksum ( addresses : & [ & str ] ) -> bool {
286
- let invalid_address_checkum: Vec < & str > = addresses
287
- . iter ( )
288
- . cloned ( )
289
- . filter ( |address| * address != eth_checksum:: checksum ( address) )
283
+ fn check_validator_id_checksum ( channel : & Channel ) -> bool {
284
+ let invalid_address_checkum: Vec < & ValidatorDesc > = channel
285
+ . spec
286
+ . validators
287
+ . into_iter ( )
288
+ . filter ( |v| v. id != eth_checksum:: checksum ( & v. id ) )
290
289
. collect ( ) ;
291
290
292
291
invalid_address_checkum. is_empty ( )
@@ -307,6 +306,22 @@ fn hash_message(message: &str) -> [u8; 32] {
307
306
res
308
307
}
309
308
309
+ fn map_error ( err : & str ) -> AdapterError {
310
+ AdapterError :: Failed ( err. to_string ( ) )
311
+ }
312
+
313
+ fn get_contract (
314
+ config : & Config ,
315
+ contract_address : Address ,
316
+ abi : & [ u8 ] ,
317
+ ) -> Result < Contract < Http > , Box < dyn Error > > {
318
+ let ( _eloop, transport) = web3:: transports:: Http :: new ( & config. ethereum_network ) ?;
319
+ let web3 = web3:: Web3 :: new ( transport) ;
320
+ let contract = Contract :: from_json ( web3. eth ( ) , contract_address, abi) ?;
321
+
322
+ Ok ( contract)
323
+ }
324
+
310
325
// Ethereum Web Tokens
311
326
#[ derive( Clone , Debug , Serialize , Deserialize ) ]
312
327
pub struct Payload {
@@ -346,8 +361,10 @@ pub fn ewt_sign(
346
361
base64:: URL_SAFE_NO_PAD ,
347
362
) ;
348
363
349
- let payload_encoded =
350
- base64:: encode_config ( & serde_json:: to_string ( payload) ?, base64:: URL_SAFE_NO_PAD ) ;
364
+ let payload_encoded = base64:: encode_config (
365
+ & serde_json:: to_string ( payload) ?. as_bytes ( ) ,
366
+ base64:: URL_SAFE_NO_PAD ,
367
+ ) ;
351
368
352
369
let message = Message :: from_slice ( & hash_message ( & format ! (
353
370
"{}.{}" ,
@@ -367,18 +384,25 @@ pub fn ewt_sign(
367
384
Ok ( format ! ( "{}.{}.{}" , header_encoded, payload_encoded, token) )
368
385
}
369
386
370
- pub fn ewt_verify ( token : & str ) -> Result < VerifyPayload , Box < dyn Error > > {
371
- let parts: Vec < String > = token. split ( '.' ) . map ( ToString :: to_string) . collect ( ) ;
372
-
373
- let message = Message :: from_slice ( & hash_message ( & format ! ( "{}.{}" , parts[ 0 ] , parts[ 1 ] ) ) ) ;
387
+ pub fn ewt_verify (
388
+ header_encoded : & str ,
389
+ payload_encoded : & str ,
390
+ token : & str ,
391
+ ) -> Result < VerifyPayload , Box < dyn Error > > {
392
+ let message = Message :: from_slice ( & hash_message ( & format ! (
393
+ "{}.{}" ,
394
+ header_encoded, payload_encoded
395
+ ) ) ) ;
374
396
375
- let decoded_signature = base64:: decode_config ( & parts [ 2 ] , base64:: URL_SAFE_NO_PAD ) ?;
397
+ let decoded_signature = base64:: decode_config ( & token , base64:: URL_SAFE_NO_PAD ) ?;
376
398
let signature = Signature :: from_electrum ( & decoded_signature) ;
377
399
378
400
let address = public_to_address ( & recover ( & signature, & message) ?) ;
379
401
380
- let payload_string =
381
- String :: from_utf8 ( base64:: decode_config ( & parts[ 1 ] , base64:: URL_SAFE_NO_PAD ) ?) ?;
402
+ let payload_string = String :: from_utf8 ( base64:: decode_config (
403
+ & payload_encoded,
404
+ base64:: URL_SAFE_NO_PAD ,
405
+ ) ?) ?;
382
406
let payload: Payload = serde_json:: from_str ( & payload_string) ?;
383
407
384
408
let verified_payload = VerifyPayload {
@@ -389,10 +413,6 @@ pub fn ewt_verify(token: &str) -> Result<VerifyPayload, Box<dyn Error>> {
389
413
Ok ( verified_payload)
390
414
}
391
415
392
- fn map_error ( err : & str ) -> AdapterError {
393
- AdapterError :: Failed ( err. to_string ( ) )
394
- }
395
-
396
416
#[ cfg( test) ]
397
417
mod test {
398
418
use super :: * ;
@@ -471,7 +491,10 @@ mod test {
471
491
472
492
let expected_verification_response =
473
493
r#"VerifyPayload { from: "0x2bDeAFAE53940669DaA6F519373f686c1f3d3393", payload: Payload { id: "awesomeValidator", era: 100000, address: Some("0x2bDeAFAE53940669DaA6F519373f686c1f3d3393"), identity: None } }"# ;
474
- let verification = ewt_verify ( & expected) . expect ( "Failed to verify ewt token" ) ;
494
+
495
+ let parts: Vec < & str > = expected. split ( '.' ) . collect ( ) ;
496
+ let verification =
497
+ ewt_verify ( parts[ 0 ] , parts[ 1 ] , parts[ 2 ] ) . expect ( "Failed to verify ewt token" ) ;
475
498
476
499
assert_eq ! (
477
500
expected_verification_response,
0 commit comments