11import canonicalize from "canonicalize" ;
22import {
3- BadNextKeySpecifiedError ,
4- BadOptionsSpecifiedError ,
5- BadSignatureError ,
6- MalformedHashChainError ,
7- MalformedIndexChainError ,
3+ BadNextKeySpecifiedError ,
4+ BadOptionsSpecifiedError ,
5+ BadSignatureError ,
6+ MalformedHashChainError ,
7+ MalformedIndexChainError ,
88} from "../errors/errors" ;
99import { isSubsetOf } from "../utils/array" ;
1010import { hash } from "../utils/hash" ;
1111import {
12- isGenesisOptions ,
13- isRotationOptions ,
14- type CreateLogEventOptions ,
15- type GenesisLogOptions ,
16- type LogEvent ,
17- type RotationLogOptions ,
18- type VerifierCallback ,
12+ isGenesisOptions ,
13+ isRotationOptions ,
14+ type CreateLogEventOptions ,
15+ type GenesisLogOptions ,
16+ type LogEvent ,
17+ type RotationLogOptions ,
18+ type VerifierCallback ,
1919} from "./log.types" ;
2020import type { StorageSpec } from "./storage/storage-spec" ;
2121
@@ -27,122 +27,122 @@ import type { StorageSpec } from "./storage/storage-spec";
2727// TODO: Create a specification link inside our docs for how generation of identifier works
2828
2929export class IDLogManager {
30- repository : StorageSpec < LogEvent , LogEvent > ;
30+ repository : StorageSpec < LogEvent , LogEvent > ;
3131
32- constructor ( repository : StorageSpec < LogEvent , LogEvent > ) {
33- this . repository = repository ;
34- }
32+ constructor ( repository : StorageSpec < LogEvent , LogEvent > ) {
33+ this . repository = repository ;
34+ }
3535
36- static async validateLogChain (
37- log : LogEvent [ ] ,
38- verifyCallback : VerifierCallback ,
39- ) {
40- let currIndex = 0 ;
41- let currentNextKeyHashesSeen : string [ ] = [ ] ;
42- let lastUpdateKeysSeen : string [ ] = [ ] ;
43- let lastHash : string | null = null ;
36+ static async validateLogChain (
37+ log : LogEvent [ ] ,
38+ verifyCallback : VerifierCallback ,
39+ ) {
40+ let currIndex = 0 ;
41+ let currentNextKeyHashesSeen : string [ ] = [ ] ;
42+ let lastUpdateKeysSeen : string [ ] = [ ] ;
43+ let lastHash : string | null = null ;
4444
45- for ( const e of log ) {
46- const [ _index , _hash ] = e . versionId . split ( "-" ) ;
47- const index = Number ( _index ) ;
48- if ( currIndex !== index ) throw new MalformedIndexChainError ( ) ;
49- const hashedUpdateKeys = await Promise . all (
50- e . updateKeys . map ( async ( k ) => await hash ( k ) ) ,
51- ) ;
52- if ( index > 0 ) {
53- const updateKeysSeen = isSubsetOf (
54- hashedUpdateKeys ,
55- currentNextKeyHashesSeen ,
56- ) ;
57- if ( ! updateKeysSeen || lastHash !== _hash )
58- throw new MalformedHashChainError ( ) ;
59- }
45+ for ( const e of log ) {
46+ const [ _index , _hash ] = e . versionId . split ( "-" ) ;
47+ const index = Number ( _index ) ;
48+ if ( currIndex !== index ) throw new MalformedIndexChainError ( ) ;
49+ const hashedUpdateKeys = await Promise . all (
50+ e . updateKeys . map ( async ( k ) => await hash ( k ) ) ,
51+ ) ;
52+ if ( index > 0 ) {
53+ const updateKeysSeen = isSubsetOf (
54+ hashedUpdateKeys ,
55+ currentNextKeyHashesSeen ,
56+ ) ;
57+ if ( ! updateKeysSeen || lastHash !== _hash )
58+ throw new MalformedHashChainError ( ) ;
59+ }
6060
61- currentNextKeyHashesSeen = e . nextKeyHashes ;
62- await IDLogManager . verifyLogEventProof (
63- e ,
64- lastUpdateKeysSeen . length > 0 ? lastUpdateKeysSeen : e . updateKeys ,
65- verifyCallback ,
66- ) ;
67- lastUpdateKeysSeen = e . updateKeys ;
68- currIndex ++ ;
69- lastHash = await hash ( canonicalize ( e ) as string ) ;
70- }
71- return true ;
72- }
61+ currentNextKeyHashesSeen = e . nextKeyHashes ;
62+ await IDLogManager . verifyLogEventProof (
63+ e ,
64+ lastUpdateKeysSeen . length > 0 ? lastUpdateKeysSeen : e . updateKeys ,
65+ verifyCallback ,
66+ ) ;
67+ lastUpdateKeysSeen = e . updateKeys ;
68+ currIndex ++ ;
69+ lastHash = await hash ( canonicalize ( e ) as string ) ;
70+ }
71+ return true ;
72+ }
7373
74- private static async verifyLogEventProof (
75- e : LogEvent ,
76- currentUpdateKeys : string [ ] ,
77- verifyCallback : VerifierCallback ,
78- ) {
79- const proof = e . proof ;
80- const copy = JSON . parse ( JSON . stringify ( e ) ) ;
81- // biome-ignore lint/performance/noDelete: we need to delete proof completely
82- delete copy . proof ;
83- const canonicalJson = canonicalize ( copy ) ;
84- let verified = false ;
85- if ( ! proof ) throw new Error ( ) ;
86- for ( const key of currentUpdateKeys ) {
87- const signValidates = await verifyCallback (
88- canonicalJson as string ,
89- proof ,
90- key ,
91- ) ;
92- if ( signValidates ) verified = true ;
93- }
94- if ( ! verified ) throw new BadSignatureError ( ) ;
95- }
74+ private static async verifyLogEventProof (
75+ e : LogEvent ,
76+ currentUpdateKeys : string [ ] ,
77+ verifyCallback : VerifierCallback ,
78+ ) {
79+ const proof = e . proof ;
80+ const copy = JSON . parse ( JSON . stringify ( e ) ) ;
81+ // biome-ignore lint/performance/noDelete: we need to delete proof completely
82+ delete copy . proof ;
83+ const canonicalJson = canonicalize ( copy ) ;
84+ let verified = false ;
85+ if ( ! proof ) throw new BadSignatureError ( "No proof found in the log event." ) ;
86+ for ( const key of currentUpdateKeys ) {
87+ const signValidates = await verifyCallback (
88+ canonicalJson as string ,
89+ proof ,
90+ key ,
91+ ) ;
92+ if ( signValidates ) verified = true ;
93+ }
94+ if ( ! verified ) throw new BadSignatureError ( ) ;
95+ }
9696
97- private async appendEntry ( entries : LogEvent [ ] , options : RotationLogOptions ) {
98- const { signer, nextKeyHashes, nextKeySigner } = options ;
99- const latestEntry = entries [ entries . length - 1 ] ;
100- const logHash = await hash ( latestEntry ) ;
101- const index = Number ( latestEntry . versionId . split ( "-" ) [ 0 ] ) + 1 ;
97+ private async appendEntry ( entries : LogEvent [ ] , options : RotationLogOptions ) {
98+ const { signer, nextKeyHashes, nextKeySigner } = options ;
99+ const latestEntry = entries [ entries . length - 1 ] ;
100+ const logHash = await hash ( latestEntry ) ;
101+ const index = Number ( latestEntry . versionId . split ( "-" ) [ 0 ] ) + 1 ;
102102
103- const currKeyHash = await hash ( nextKeySigner . pubKey ) ;
104- if ( ! latestEntry . nextKeyHashes . includes ( currKeyHash ) )
105- throw new BadNextKeySpecifiedError ( ) ;
103+ const currKeyHash = await hash ( nextKeySigner . pubKey ) ;
104+ if ( ! latestEntry . nextKeyHashes . includes ( currKeyHash ) )
105+ throw new BadNextKeySpecifiedError ( ) ;
106106
107- const logEvent : LogEvent = {
108- id : latestEntry . id ,
109- versionTime : new Date ( Date . now ( ) ) ,
110- versionId : `${ index } -${ logHash } ` ,
111- updateKeys : [ nextKeySigner . pubKey ] ,
112- nextKeyHashes : nextKeyHashes ,
113- method : "w3id:v0.0.0" ,
114- } ;
107+ const logEvent : LogEvent = {
108+ id : latestEntry . id ,
109+ versionTime : new Date ( Date . now ( ) ) ,
110+ versionId : `${ index } -${ logHash } ` ,
111+ updateKeys : [ nextKeySigner . pubKey ] ,
112+ nextKeyHashes : nextKeyHashes ,
113+ method : "w3id:v0.0.0" ,
114+ } ;
115115
116- const proof = await signer . sign ( canonicalize ( logEvent ) as string ) ;
117- logEvent . proof = proof ;
116+ const proof = await signer . sign ( canonicalize ( logEvent ) as string ) ;
117+ logEvent . proof = proof ;
118118
119- await this . repository . create ( logEvent ) ;
120- return logEvent ;
121- }
119+ await this . repository . create ( logEvent ) ;
120+ return logEvent ;
121+ }
122122
123- private async createGenesisEntry ( options : GenesisLogOptions ) {
124- const { id, nextKeyHashes, signer } = options ;
125- const logEvent : LogEvent = {
126- id,
127- versionId : `0-${ id . split ( "@" ) [ 1 ] } ` ,
128- versionTime : new Date ( Date . now ( ) ) ,
129- updateKeys : [ signer . pubKey ] ,
130- nextKeyHashes : nextKeyHashes ,
131- method : "w3id:v0.0.0" ,
132- } ;
133- const proof = await signer . sign ( canonicalize ( logEvent ) as string ) ;
134- logEvent . proof = proof ;
135- await this . repository . create ( logEvent ) ;
136- return logEvent ;
137- }
123+ private async createGenesisEntry ( options : GenesisLogOptions ) {
124+ const { id, nextKeyHashes, signer } = options ;
125+ const logEvent : LogEvent = {
126+ id,
127+ versionId : `0-${ id . split ( "@" ) [ 1 ] } ` ,
128+ versionTime : new Date ( Date . now ( ) ) ,
129+ updateKeys : [ signer . pubKey ] ,
130+ nextKeyHashes : nextKeyHashes ,
131+ method : "w3id:v0.0.0" ,
132+ } ;
133+ const proof = await signer . sign ( canonicalize ( logEvent ) as string ) ;
134+ logEvent . proof = proof ;
135+ await this . repository . create ( logEvent ) ;
136+ return logEvent ;
137+ }
138138
139- async createLogEvent ( options : CreateLogEventOptions ) {
140- const entries = await this . repository . findMany ( { } ) ;
141- if ( entries . length > 0 ) {
142- if ( ! isRotationOptions ( options ) ) throw new BadOptionsSpecifiedError ( ) ;
143- return this . appendEntry ( entries , options ) ;
144- }
145- if ( ! isGenesisOptions ( options ) ) throw new BadOptionsSpecifiedError ( ) ;
146- return this . createGenesisEntry ( options ) ;
147- }
139+ async createLogEvent ( options : CreateLogEventOptions ) {
140+ const entries = await this . repository . findMany ( { } ) ;
141+ if ( entries . length > 0 ) {
142+ if ( ! isRotationOptions ( options ) ) throw new BadOptionsSpecifiedError ( ) ;
143+ return this . appendEntry ( entries , options ) ;
144+ }
145+ if ( ! isGenesisOptions ( options ) ) throw new BadOptionsSpecifiedError ( ) ;
146+ return this . createGenesisEntry ( options ) ;
147+ }
148148}
0 commit comments