4b9d0677 Andy McCall 18 days ago ↶
1
2
3
4
5
6
7
8
9
10
11
028545df Andy McCall 17 days ago ↶
12
13
14
15
16
17
4b9d0677 Andy McCall 18 days ago ↶
18
19
use super :: * ;
20
use crate :: credentials:: hub_of;
21
use crate :: protocol:: http_get;
028545df Andy McCall 17 days ago ↶
22
use crate :: tlog:: { self , Checkpoint, Entry, Hash, HashReader, Integrated, Note, Tile, TlogError} ;
4b9d0677 Andy McCall 18 days ago ↶
23
use std:: cell:: RefCell;
24
25
26
type TileCache = RefCell< HashMap< ( u32 , u64 ) , ( Tile, Vec< u8 > ) > > ;
27
28
29
struct Tiles < 'a > {
30
hub : & 'a str ,
31
size : u64 ,
32
options : & 'a RemoteOptions,
33
cache : TileCache,
34
}
35
36
impl HashReader for Tiles < '_ > {
37
fn read_hashes ( & self , indexes : & [u64 ]) -> std:: result:: Result< Vec< Hash> , TlogError> {
38
let mut out = Vec :: with_capacity( indexes.len ( ) ) ;
39
for & i in indexes {
40
let ( t, .. ) = Tile:: for_index( i) ;
41
let key = ( t.level, t.n) ;
42
if ! self .cache.borrow ( ) .contains_key ( & key) {
43
let held =
44
( self .size > > ( tlog:: TILE_HEIGHT * t.level) ) - ( t.n < < tlog:: TILE_HEIGHT ) ;
45
let tile = Tile {
46
width: held.min ( tlog:: TILE_WIDTH ) as u32 ,
47
.. t
48
} ;
49
let data = http_get ( & format! ( " {} /log/{} " , self .hub, tile.path ( ) ) , self .options)
50
.map_err ( | e | TlogError:: Read( e.to_string ( ) ) ) ? ;
51
self .cache.borrow_mut ( ) .insert ( key, ( tile, data) ) ;
52
}
53
let cache = self .cache.borrow ( ) ;
54
let ( tile, data) = & cache[ & key] ;
55
out.push ( tile.hash_at ( data, i) ? ) ;
56
}
57
Ok ( out)
58
}
59
}
60
61
62
struct Seen {
63
identity : Identity,
64
checkpoint : Checkpoint,
65
}
66
67
fn file_name ( hub : & str ) -> String {
68
hub.chars ( )
69
.map ( | c | {
70
if c.is_ascii_alphanumeric ( ) | | c = = ' .' | | c = = ' -' {
71
c
72
} else {
73
' _'
74
}
75
} )
76
.collect ( )
77
}
78
79
impl Repo {
80
fn tlog_file ( & self , hub : & str ) -> PathBuf {
81
self .user_config
82
.parent ( )
83
.map ( Path:: to_path_buf)
84
.unwrap_or_else ( default_home)
85
.join ( " tlog" )
86
.join ( file_name ( hub) )
87
}
88
89
fn seen_log ( & self , hub : & str ) -> Option< Seen> {
90
let text = fs:: read_to_string( self .tlog_file ( hub) ) .ok ( ) ? ;
91
let ( key, note) = text.split_once ( ' \n ' ) ? ;
92
let identity = Identity:: decode( & crate :: dsse:: base64_decode( key) ? ) .ok ( ) ? ;
93
let note = Note:: parse( note) .ok ( ) ? ;
94
Some ( Seen {
95
checkpoint: Checkpoint:: parse( & note.text) .ok ( ) ? ,
96
identity,
97
} )
98
}
99
028545df Andy McCall 17 days ago ↶
100
pub ( crate ) fn remember_log ( & self , hub : & str , identity : & Identity, note : & Note) -> Result< ( ) > {
4b9d0677 Andy McCall 18 days ago ↶
101
let path = self .tlog_file ( hub) ;
102
if let Some ( dir) = path.parent ( ) {
103
fs:: create_dir_all( dir) ? ;
104
}
105
let text = format! (
106
" {} \n {} " ,
107
crate :: dsse:: base64( & identity.encode ( ) ) ,
108
note.encode ( )
109
) ;
110
crate :: durable:: write_atomic( & path, text.as_bytes ( ) ) ? ;
111
Ok ( ( ) )
112
}
113
e59512f2 Andy McCall 18 days ago ↶
114
115
116
117
118
pub ( crate ) fn accept_checkpoint (
4b9d0677 Andy McCall 18 days ago ↶
119
& self ,
e59512f2 Andy McCall 18 days ago ↶
120
hub : & str ,
121
text : & str ,
122
options : & RemoteOptions,
028545df Andy McCall 17 days ago ↶
123
) -> Result< std:: result:: Result< Accepted, String> > {
e59512f2 Andy McCall 18 days ago ↶
124
let unchecked = | why : String| Ok ( Err ( why) ) ;
4b9d0677 Andy McCall 18 days ago ↶
125
let note = match Note:: parse( text) {
126
Ok ( n) => n,
127
Err ( e) => return unchecked ( e.to_string ( ) ) ,
128
} ;
129
let cp = match Checkpoint:: parse( & note.text) {
130
Ok ( c) => c,
131
Err ( e) => return unchecked ( e.to_string ( ) ) ,
132
} ;
b333a199 Andy McCall 18 days ago ↶
133
let fetch_key = | | {
134
http_get ( & format! ( " {hub} /.well-known/oxyhub-key" ) , options)
4b9d0677 Andy McCall 18 days ago ↶
135
.and_then ( | b | Ok ( Identity:: decode( & b) ? ) )
b333a199 Andy McCall 18 days ago ↶
136
} ;
137
let mut notes = Vec :: new( ) ;
138
let mut seen = self .seen_log ( hub) ;
139
let mut identity = match & seen {
140
Some ( s) => s.identity.clone ( ) ,
141
None => match fetch_key ( ) {
4b9d0677 Andy McCall 18 days ago ↶
142
Ok ( i) => i,
143
Err ( e) => return unchecked ( format! ( " its key could not be fetched ({e} )" ) ) ,
144
} ,
145
} ;
b333a199 Andy McCall 18 days ago ↶
146
if let Err ( e) = note.verify ( & cp.origin, & identity) {
147
148
149
150
151
let Some ( old) = & seen else {
152
return unchecked ( e.to_string ( ) ) ;
153
} ;
154
match fetch_key ( ) {
155
Ok ( new) if note.verify ( & cp.origin, & new) .is_ok ( ) => {
156
notes.push ( format! (
157
" {hub} 's log is signed by another key than before ({} now, {} then): if its operator did not announce a new key, it may be showing others a different log" ,
158
new.hash ( ) .short ( 12 ) ,
159
old.identity.hash ( ) .short ( 12 )
160
) ) ;
161
identity = new;
162
seen = None ;
163
}
164
_ => return unchecked ( format! ( " its checkpoint does not verify ({e} )" ) ) ,
165
}
166
}
4b9d0677 Andy McCall 18 days ago ↶
167
let two = | why : String| {
168
fail ( format! (
169
" {hub} showed two histories: {why} . Do not trust what it serves; tell its operator. \
170
(What this user saw of its log is in {} .)" ,
e59512f2 Andy McCall 18 days ago ↶
171
self .tlog_file ( hub) .display ( )
4b9d0677 Andy McCall 18 days ago ↶
172
) )
173
} ;
174
if let Some ( s) = & seen {
175
let old = & s.checkpoint;
176
if old.origin ! = cp.origin {
b333a199 Andy McCall 18 days ago ↶
177
178
179
notes.push ( format! (
180
" {hub} 's log is now {} , not {} : checking it afresh" ,
181
cp.origin, old.origin
182
) ) ;
183
} else if cp.size < old.size {
4b9d0677 Andy McCall 18 days ago ↶
184
return two ( format! (
185
" its log has {} entries, fewer than the {} it showed before" ,
186
cp.size, old.size
187
) ) ;
b333a199 Andy McCall 18 days ago ↶
188
} else if cp.size = = old.size & & cp.root ! = old.root {
4b9d0677 Andy McCall 18 days ago ↶
189
return two ( format! ( " two different logs of {} entries" , cp.size) ) ;
b333a199 Andy McCall 18 days ago ↶
190
} else if cp.size > old.size & & old.size > 0 {
4b9d0677 Andy McCall 18 days ago ↶
191
let tiles = Tiles {
e59512f2 Andy McCall 18 days ago ↶
192
hub,
4b9d0677 Andy McCall 18 days ago ↶
193
size: cp.size,
e59512f2 Andy McCall 18 days ago ↶
194
options,
4b9d0677 Andy McCall 18 days ago ↶
195
cache: RefCell:: new( HashMap:: new( ) ) ,
196
} ;
197
match tlog:: prove_consistency( cp.size, old.size, & tiles) .and_then ( | p | {
198
tlog:: check_consistency( & p, cp.size, & cp.root, old.size, & old.root)
199
} ) {
200
Ok ( ( ) ) => { }
201
Err ( TlogError:: ProofFailed) | Err ( TlogError:: BadIndex( _ ) ) => {
202
return two ( format! (
203
" its log of {} entries does not extend the one of {} it showed before" ,
204
cp.size, old.size
205
) )
206
}
207
208
Err ( e) => return unchecked ( format! ( " its tiles could not be read ({e} )" ) ) ,
209
}
210
}
211
}
e59512f2 Andy McCall 18 days ago ↶
212
self .remember_log ( hub, & identity, & note) ? ;
028545df Andy McCall 17 days ago ↶
213
Ok ( Ok ( Accepted {
214
checkpoint: cp,
215
identity,
216
note,
217
notes,
218
} ) )
219
}
220
221
fn witnesses_file ( & self , hub : & str ) -> PathBuf {
222
let mut p = self .tlog_file ( hub) ;
223
p.set_file_name ( format! ( " {} .witnesses" , file_name ( hub) ) ) ;
224
p
225
}
226
227
228
229
pub ( crate ) fn pinned_witnesses ( & self , hub : & str ) -> Vec< ( String, Identity) > {
230
let Ok ( text) = fs:: read_to_string( self .witnesses_file ( hub) ) else {
231
return vec! [ ] ;
232
} ;
233
text.lines ( )
234
.filter_map ( | l | {
235
let ( name, key) = l.split_once ( ' ' ) ? ;
236
let id = Identity:: decode( & crate :: dsse:: base64_decode( key) ? ) .ok ( ) ? ;
237
Some ( ( name.to_string ( ) , id) )
238
} )
239
.collect ( )
240
}
241
242
243
244
245
246
pub ( crate ) fn pin_witnesses (
247
& self ,
248
hub : & str ,
249
listed : & [( String , Vec <u8 >) ],
250
notes : & mut Vec< String> ,
251
) -> Result< Vec< ( String, Identity) > > {
252
let path = self .witnesses_file ( hub) ;
253
let first = ! path.exists ( ) ;
254
let mut pinned = self .pinned_witnesses ( hub) ;
255
let mut changed = false ;
256
for ( name, bytes) in listed {
257
if name.is_empty ( ) | | name.contains ( char :: is_whitespace) {
258
notes.push ( format! (
259
" {hub} lists a witness named {name:?} , which cannot name one"
260
) ) ;
261
continue ;
262
}
263
let Ok ( id) = Identity:: decode( bytes) else {
264
notes.push ( format! (
265
" {hub} lists witness {name} with a key that does not decode"
266
) ) ;
267
continue ;
268
} ;
269
match pinned.iter_mut ( ) .find ( | ( n , _) | n = = name ) {
270
Some ( ( _ , old) ) if old.hash ( ) = = id.hash ( ) => { }
271
Some ( ( _ , old) ) => {
272
notes.push ( format! (
273
" {hub} 's witness {name} has another key than before ({} now, {} then): if its operator did not announce one, {hub} may be naming a witness of its own" ,
274
id.hash ( ) .short ( 12 ) ,
275
old.hash ( ) .short ( 12 )
276
) ) ;
277
* old = id;
278
changed = true ;
279
}
280
None => {
281
if ! first {
282
notes.push ( format! (
283
" {hub} is now cosigned by witness {name} (key {} ), trusted from now on" ,
284
id.hash ( ) .short ( 12 )
285
) ) ;
286
}
287
pinned.push ( ( name.clone ( ) , id) ) ;
288
changed = true ;
289
}
290
}
291
}
292
if changed {
293
if let Some ( dir) = path.parent ( ) {
294
fs:: create_dir_all( dir) ? ;
295
}
296
let text: String = pinned
297
.iter ( )
298
.map ( | ( n , id ) | format! ( " {n} {} \n " , crate :: dsse:: base64( & id.encode ( ) ) ) )
299
.collect ( ) ;
300
crate :: durable:: write_atomic( & path, text.as_bytes ( ) ) ? ;
301
}
302
Ok ( pinned)
e59512f2 Andy McCall 18 days ago ↶
303
}
304
028545df Andy McCall 17 days ago ↶
305
306
307
308
e59512f2 Andy McCall 18 days ago ↶
309
pub ( crate ) fn check_log (
310
& self ,
311
url : & str ,
312
ca : Option< & Path> ,
313
resp : & RefsResponse,
028545df Andy McCall 17 days ago ↶
314
) -> Result< LogCheck> {
315
let mut out = LogCheck:: default( ) ;
e59512f2 Andy McCall 18 days ago ↶
316
let ( Some ( text) , Some ( hub) ) = ( & resp.log, hub_of ( url) ) else {
028545df Andy McCall 17 days ago ↶
317
return Ok ( out) ;
e59512f2 Andy McCall 18 days ago ↶
318
} ;
028545df Andy McCall 17 days ago ↶
319
out.kept = true ;
e59512f2 Andy McCall 18 days ago ↶
320
let options = RemoteOptions {
321
ca: ca.map ( Path:: to_path_buf) ,
322
.. RemoteOptions:: default( )
323
} ;
028545df Andy McCall 17 days ago ↶
324
let accepted = match self .accept_checkpoint ( & hub, text, & options) ? {
b333a199 Andy McCall 18 days ago ↶
325
Ok ( c) => c,
e59512f2 Andy McCall 18 days ago ↶
326
Err ( why) => {
028545df Andy McCall 17 days ago ↶
327
out.notes
328
.push ( format! ( " {hub} 's transparency log was not checked: {why} " ) ) ;
329
out.unchecked = Some ( why) ;
330
return Ok ( out) ;
e59512f2 Andy McCall 18 days ago ↶
331
}
332
} ;
028545df Andy McCall 17 days ago ↶
333
let cp = & accepted.checkpoint;
334
out.notes.extend ( accepted.notes.iter ( ) .cloned ( ) ) ;
335
let witnesses = self .pin_witnesses ( & hub, & resp.witnesses, & mut out.notes) ? ;
4b9d0677 Andy McCall 18 days ago ↶
336
let mut missing = 0 ;
1ec6fdbc Andy McCall 15 days ago ↶
337
338
let mut place = | name : & str ,
339
kind: & str ,
340
a: & SignedAttest,
341
logged: Option< & crate:: protocol:: Logged> ,
342
integration: & [ crate :: protocol:: Integration] |
343
-> std:: result:: Result< Integrated, String> {
344
let Some ( l) = logged else {
4b9d0677 Andy McCall 18 days ago ↶
345
missing + = 1 ;
1ec6fdbc Andy McCall 15 days ago ↶
346
return Err ( " it is not in the log yet" .into ( ) ) ;
4b9d0677 Andy McCall 18 days ago ↶
347
} ;
348
let fits = Entry:: decode( & l.entry) .is_ok_and ( | e | {
1ec6fdbc Andy McCall 15 days ago ↶
349
e.kind = = kind & & e.object = = a.attest.hash ( ) & & e.repo = = a.attest.repo
4b9d0677 Andy McCall 18 days ago ↶
350
} ) ;
351
let included = fits
352
& & tlog:: check_inclusion(
353
& l.proof,
354
cp.size,
355
& cp.root,
356
l.index,
357
& tlog:: leaf_hash( & l.entry) ,
358
)
359
.is_ok ( ) ;
360
if ! included {
028545df Andy McCall 17 days ago ↶
361
out.notes.push ( format! (
1ec6fdbc Andy McCall 15 days ago ↶
362
" {hub} 's log entry {} for {name} does not prove its attestation is in the log" ,
363
l.index
4b9d0677 Andy McCall 18 days ago ↶
364
) ) ;
365
}
1ec6fdbc Andy McCall 15 days ago ↶
366
if ! fits {
028545df Andy McCall 17 days ago ↶
367
Err ( " its log entry is not its attestation's" .to_string ( ) )
368
} else if witnesses.is_empty ( ) {
369
Err ( format! ( " {hub} names no witness" ) )
370
} else {
1ec6fdbc Andy McCall 15 days ago ↶
371
let cosigned: Vec< ( & str , & [Hash]) > = integration
028545df Andy McCall 17 days ago ↶
372
.iter ( )
373
.map ( | i | ( i.note.as_str ( ) , i.proof.as_slice ( ) ) )
374
.collect ( ) ;
375
tlog:: integrated_time(
376
& l.entry,
377
l.index,
378
& cosigned,
379
& cp.origin,
380
& accepted.identity,
381
& witnesses,
382
)
1ec6fdbc Andy McCall 15 days ago ↶
383
}
384
} ;
385
let mut times = HashMap:: new( ) ;
386
for r in & resp.refs {
387
let Some ( a) = & r.attestation else { continue } ;
388
let time = place ( & r.name, " ref" , a, r.logged.as_ref ( ) , & r.integration) ;
389
times.insert ( r.name.clone ( ) , time) ;
390
}
391
392
393
let mut tag_times = HashMap:: new( ) ;
394
for t in & resp.tags {
395
let Some ( a) = & t.attestation else { continue } ;
396
let name = format! ( " tags/{} " , t.name) ;
397
let time = place ( & name, " tag" , a, t.logged.as_ref ( ) , & t.integration) ;
398
tag_times.insert ( t.name.clone ( ) , time) ;
4b9d0677 Andy McCall 18 days ago ↶
399
}
1ec6fdbc Andy McCall 15 days ago ↶
400
out.times = times;
401
out.tag_times = tag_times;
4b9d0677 Andy McCall 18 days ago ↶
402
if missing > 0 {
028545df Andy McCall 17 days ago ↶
403
out.notes.push ( format! (
4b9d0677 Andy McCall 18 days ago ↶
404
" {} of {hub} 's attestations {} not in its transparency log yet (logged within seconds of a push; checked on a later fetch)" ,
405
missing,
406
if missing = = 1 { " is" } else { " are" }
407
) ) ;
028545df Andy McCall 17 days ago ↶
408
}
409
Ok ( out)
410
}
411
}
412
413
414
415
pub ( crate ) struct Accepted {
416
pub checkpoint : Checkpoint,
417
pub identity : Identity,
418
pub note : Note,
419
pub notes : Vec< String> ,
420
}
421
422
423
# [ derive ( Debug, Default ) ]
424
pub ( crate ) struct LogCheck {
425
426
pub kept : bool ,
427
428
pub unchecked : Option< String> ,
429
pub notes : Vec< String> ,
430
431
pub times : HashMap< String, std:: result:: Result< Integrated, String> > ,
1ec6fdbc Andy McCall 15 days ago ↶
432
433
pub tag_times : HashMap< String, std:: result:: Result< Integrated, String> > ,
028545df Andy McCall 17 days ago ↶
434
}
435
436
impl LogCheck {
437
438
pub fn time_of ( & self , name : & str ) -> std:: result:: Result< & Integrated, String> {
1ec6fdbc Andy McCall 15 days ago ↶
439
self .lookup ( & self .times, name)
440
}
441
442
443
pub fn tag_time_of ( & self , name : & str ) -> std:: result:: Result< & Integrated, String> {
444
self .lookup ( & self .tag_times, name)
445
}
446
447
fn lookup < 'a > (
448
& self ,
449
times : & 'a HashMap< String, std:: result:: Result< Integrated, String> > ,
450
name : & str ,
451
) -> std:: result:: Result< & 'a Integrated, String> {
028545df Andy McCall 17 days ago ↶
452
if let Some ( why) = & self .unchecked {
453
return Err ( format! ( " its host's log was not checked ({why} )" ) ) ;
454
}
455
if ! self .kept {
456
return Err ( " its host keeps no transparency log" .into ( ) ) ;
4b9d0677 Andy McCall 18 days ago ↶
457
}
1ec6fdbc Andy McCall 15 days ago ↶
458
match times.get ( name) {
028545df Andy McCall 17 days ago ↶
459
Some ( Ok ( i) ) => Ok ( i) ,
460
Some ( Err ( why) ) => Err ( why.clone ( ) ) ,
461
None => Err ( " it has no log entry" .into ( ) ) ,
462
}
463
}
464
}
465
466
467
468
# [ derive ( Clone, Copy, Debug, PartialEq, Eq ) ]
469
pub ( crate ) enum Timed {
470
471
Integrated,
472
473
474
Fallback,
475
476
477
Disagrees,
478
479
Refused,
480
}
481
482
483
484
pub ( crate ) fn judge (
485
integrated : Option< u64 > ,
486
at : impl Fn( u64 ) -> bool ,
487
before : impl FnOnce( ) -> bool ,
488
) -> Timed {
489
match integrated {
490
Some ( t) if at ( t) => Timed:: Integrated,
491
Some ( _ ) => {
492
if before ( ) {
493
Timed:: Disagrees
494
} else {
495
Timed:: Refused
496
}
497
}
498
None => {
499
if before ( ) {
500
Timed:: Fallback
501
} else {
502
Timed:: Refused
503
}
504
}
505
}
506
}
507
508
509
510
pub ( crate ) fn gap_note ( claimed : u64 , integrated : & Integrated) -> Option< String> {
511
let t = integrated.ms ( ) ;
512
let minutes = | d : u64 | d.div_ceil ( 60_000 ) ;
513
if claimed.saturating_add ( tlog:: TIME_TOLERANCE_MS ) < t {
514
Some ( format! (
515
" claims to be from {} minutes before witness {} first saw it: backdated, held back before it was logged, or signed on a slow clock" ,
516
minutes ( t - claimed) ,
517
integrated.witness
518
) )
519
} else if claimed > t.saturating_add ( tlog:: TIME_TOLERANCE_MS ) {
520
Some ( format! (
521
" claims to be from {} minutes after witness {} saw it: signed on a fast clock" ,
522
minutes ( claimed - t) ,
523
integrated.witness
524
) )
525
} else {
526
None
e59512f2 Andy McCall 18 days ago ↶
527
}
528
}
529
530
531
# [ derive ( Clone, Debug ) ]
532
pub struct LogAudit {
533
pub hub : String,
534
pub checkpoint : Checkpoint,
535
536
pub entries : Vec< Entry> ,
b333a199 Andy McCall 18 days ago ↶
537
538
pub notes : Vec< String> ,
028545df Andy McCall 17 days ago ↶
539
540
541
pub witnessed : Vec< ( String, u64 ) > ,
e59512f2 Andy McCall 18 days ago ↶
542
}
543
544
impl Repo {
545
546
547
548
pub fn audit_log ( & self , url : & str , ca : Option< & Path> ) -> Result< LogAudit> {
549
let hub =
550
hub_of ( url) .ok_or_else ( | | RepoError:: Msg( format! ( " {url} is not a hub's address" ) ) ) ? ;
551
let options = RemoteOptions {
552
ca: ca.map ( Path:: to_path_buf) ,
553
.. RemoteOptions:: default( )
554
} ;
555
let tl = | e : TlogError| RepoError:: Msg( e.to_string ( ) ) ;
556
let text = String :: from_utf8( http_get ( & format! ( " {hub} /log/checkpoint" ) , & options) ? )
557
.map_err ( | _| RepoError:: Msg( format! ( " {hub} 's checkpoint is not text" ) ) ) ? ;
028545df Andy McCall 17 days ago ↶
558
let accepted = match self .accept_checkpoint ( & hub, & text, & options) ? {
e59512f2 Andy McCall 18 days ago ↶
559
Ok ( c) => c,
560
Err ( why) => return fail ( format! ( " {hub} 's log could not be checked: {why} " ) ) ,
561
} ;
028545df Andy McCall 17 days ago ↶
562
let mut notes = accepted.notes;
563
564
565
566
let listed: Vec< ( String, Vec< u8 > ) > = http_get ( & format! ( " {hub} /log/witnesses" ) , & options)
567
.ok ( )
568
.and_then ( | b | String :: from_utf8( b) .ok ( ) )
569
.map ( | t | {
570
t.lines ( )
571
.filter_map ( | l | {
572
let ( n, k) = l.split_once ( ' ' ) ? ;
573
Some ( ( n.to_string ( ) , crate :: dsse:: base64_decode( k.trim ( ) ) ? ) )
574
} )
575
.collect ( )
576
} )
577
.unwrap_or_default ( ) ;
578
let witnesses = self .pin_witnesses ( & hub, & listed, & mut notes) ? ;
579
let mut witnessed = accepted.note.cosignatures ( & witnesses) ;
580
witnessed.sort_by_key ( | ( _, t ) | * t) ;
581
let cp = accepted.checkpoint;
e59512f2 Andy McCall 18 days ago ↶
582
let mut entries = Vec :: with_capacity( cp.size as usize ) ;
583
let mut stored: Vec< Hash> = Vec :: new( ) ;
584
let mut n = 0 u64 ;
585
while n < cp.size {
586
let t = Tile {
587
level: 0 ,
588
n: n > > tlog:: TILE_HEIGHT ,
589
width: ( cp.size - n) .min ( tlog:: TILE_WIDTH ) as u32 ,
590
} ;
591
let bytes = http_get ( & format! ( " {hub} /log/{} " , t.entries_path ( ) ) , & options) ? ;
592
let bundle = tlog:: decode_bundle( & bytes) .map_err ( tl) ? ;
593
if bundle.len ( ) ! = t.width as usize {
594
return fail ( format! (
595
" {hub} 's entry bundle {} holds {} entries, not {} " ,
596
t.entries_path ( ) ,
597
bundle.len ( ) ,
598
t.width
599
) ) ;
600
}
601
for b in bundle {
602
let add =
603
tlog:: stored_hashes_for_leaf( n, tlog:: leaf_hash( & b) , & stored) .map_err ( tl) ? ;
604
stored.extend ( add) ;
605
entries.push ( Entry:: decode( & b) .map_err ( tl) ? ) ;
606
n + = 1 ;
607
}
608
}
609
if tlog:: tree_hash( cp.size, & stored) .map_err ( tl) ? ! = cp.root {
610
return fail ( format! (
611
" {hub} serves entries that do not make the root it signed for {} entries: its log is not the one it attests" ,
612
cp.size
613
) ) ;
614
}
615
Ok ( LogAudit {
616
hub,
617
checkpoint: cp,
618
entries,
b333a199 Andy McCall 18 days ago ↶
619
notes,
028545df Andy McCall 17 days ago ↶
620
witnessed,
e59512f2 Andy McCall 18 days ago ↶
621
} )
4b9d0677 Andy McCall 18 days ago ↶
622
}
623
}