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