1983686f Andy McCall 1 month ago ↶
1
2
3
4
5
6
7
use crate :: cbor:: { self , Value} ;
8
use crate :: hash:: { hash_object, KeyId, ObjectRef, ObjectType} ;
9
use crate :: objects:: { array, bytes, check_version, field, oref, text, uint, ObjectError, FORMAT_VERSION } ;
10
use ed25519_dalek:: ed25519:: signature:: Signer as _ ;
11
use ed25519_dalek:: { Signature as EdSignature, SigningKey as EdSigningKey, VerifyingKey as EdVerifyingKey} ;
12
use ml_dsa:: { EncodedSignature, EncodedVerifyingKey, MlDsa65, Seed, Signature as MlSignature, SigningKey as MlSigningKey, VerifyingKey as MlVerifyingKey} ;
13
use rand:: RngCore;
14
15
pub const ED25519_PK_LEN : usize = 32 ;
16
pub const MLDSA65_PK_LEN : usize = 1952 ;
17
pub const ED25519_SIG_LEN : usize = 64 ;
18
pub const MLDSA65_SIG_LEN : usize = 3309 ;
19
20
pub const ALG_COMPOSITE : u64 = 1 ;
21
22
# [ derive ( Clone, Debug, PartialEq, Eq ) ]
23
pub struct Identity {
24
pub ed25519_pk : [u8 ; ED25519_PK_LEN],
25
pub mldsa65_pk : Vec< u8 > ,
26
pub name : String,
27
pub email : String,
28
pub created : u64 ,
29
pub expires : Option< u64 > ,
30
pub previous : Option< KeyId> ,
31
pub delegates : Vec< KeyId> ,
32
}
33
34
impl Identity {
35
pub fn to_value ( & self ) -> Value {
36
Value:: map( [
37
( 0 , Value:: Uint( FORMAT_VERSION ) ) ,
38
( 1 , Value:: Bytes( self .ed25519_pk.to_vec ( ) ) ) ,
39
( 2 , Value:: Bytes( self .mldsa65_pk.clone ( ) ) ) ,
40
( 3 , Value:: Text( self .name.clone ( ) ) ) ,
41
( 4 , Value:: Text( self .email.clone ( ) ) ) ,
42
( 5 , Value:: Uint( self .created) ) ,
43
( 6 , self .expires.map ( Value:: Uint) .unwrap_or ( Value:: Null) ) ,
44
( 7 , self .previous.map ( | p | Value:: Bytes( p.0. to_vec ( ) ) ) .unwrap_or ( Value:: Null) ) ,
45
( 8 , Value:: Array( self .delegates.iter ( ) .map ( | d | Value:: Bytes( d.0. to_vec ( ) ) ) .collect ( ) ) ) ,
46
] )
47
}
48
pub fn encode ( & self ) -> Vec< u8 > {
49
cbor:: encode( & self .to_value ( ) )
50
}
51
pub fn hash ( & self ) -> KeyId {
52
hash_object ( ObjectType:: Identity, & self .encode ( ) )
53
}
54
pub fn decode ( b : & [u8 ]) -> Result< Identity, ObjectError> {
55
let v = cbor:: decode( b) ? ;
56
let m = v.as_map ( ) .ok_or_else ( | | ObjectError:: Schema( " identity: expected map" .into ( ) ) ) ? ;
57
check_version ( m) ? ;
58
let ed = bytes ( field ( m, 1 , " ed25519_pk" ) ? , " ed25519_pk" ) ? ;
59
let ml = bytes ( field ( m, 2 , " mldsa65_pk" ) ? , " mldsa65_pk" ) ? ;
60
if ed.len ( ) ! = ED25519_PK_LEN | | ml.len ( ) ! = MLDSA65_PK_LEN {
61
return Err ( ObjectError:: Schema( " identity: wrong public key length" .into ( ) ) ) ;
62
}
63
let opt_u = | v : & Value| -> Result< Option< u64 > , ObjectError> { if v.is_null ( ) { Ok ( None ) } else { uint ( v, " expires" ) .map ( Some ) } } ;
64
let opt_r = | v : & Value| -> Result< Option< KeyId> , ObjectError> { if v.is_null ( ) { Ok ( None ) } else { oref ( v, " previous" ) .map ( Some ) } } ;
65
let mut delegates = array ( field ( m, 8 , " delegates" ) ? , " delegates" ) ? .iter ( ) .map ( | d | oref ( d, " delegate" ) ) .collect:: < Result< Vec< _ > , _ > > ( ) ? ;
66
delegates.sort ( ) ;
67
delegates.dedup ( ) ;
68
Ok ( Identity {
69
ed25519_pk: ed.try_into ( ) .unwrap ( ) ,
70
mldsa65_pk: ml.to_vec ( ) ,
71
name: text ( field ( m, 3 , " name" ) ? , " name" ) ? .to_owned ( ) ,
72
email: text ( field ( m, 4 , " email" ) ? , " email" ) ? .to_owned ( ) ,
73
created: uint ( field ( m, 5 , " created" ) ? , " created" ) ? ,
74
expires: opt_u ( field ( m, 6 , " expires" ) ? ) ? ,
75
previous: opt_r ( field ( m, 7 , " previous" ) ? ) ? ,
76
delegates,
77
} )
78
}
79
28fc23d7 Andy McCall 1 month ago ↶
80
81
pub fn is_expired_at ( & self , now_ms : u64 ) -> bool {
82
self .expires.is_some_and ( | e | now_ms > e )
83
}
84
85
86
87
pub fn successor ( & self , delegates : Vec< KeyId> , expires : Option< u64 > , created : u64 ) -> Identity {
88
let mut delegates = delegates;
89
delegates.sort ( ) ;
90
delegates.dedup ( ) ;
91
Identity { previous: Some ( self .hash ( ) ) , delegates, expires, created, .. self .clone ( ) }
92
}
93
1983686f Andy McCall 1 month ago ↶
94
fn ed_key ( & self ) -> Result< EdVerifyingKey, VerifyError> {
95
EdVerifyingKey:: from_bytes( & self .ed25519_pk) .map_err ( | _| VerifyError:: BadKey)
96
}
97
fn ml_key ( & self ) -> Result< MlVerifyingKey< MlDsa65> , VerifyError> {
98
let enc = EncodedVerifyingKey:: < MlDsa65> :: try_from( self .mldsa65_pk.as_slice ( ) ) .map_err ( | _| VerifyError:: BadKey) ? ;
99
Ok ( MlVerifyingKey:: decode( & enc) )
100
}
101
}
102
103
# [ derive ( Clone, Copy, Debug, PartialEq, Eq ) ]
104
pub enum Domain {
105
Change,
106
Attest,
83e0677b Andy McCall 1 month ago ↶
107
Policy,
1983686f Andy McCall 1 month ago ↶
108
}
109
110
impl Domain {
111
pub fn name ( self ) -> & 'static str {
112
match self {
113
Domain:: Change => " change" ,
114
Domain:: Attest => " attest" ,
83e0677b Andy McCall 1 month ago ↶
115
Domain:: Policy => " policy" ,
1983686f Andy McCall 1 month ago ↶
116
}
117
}
118
}
119
120
121
pub fn signing_message ( domain : Domain, key : & KeyId, target : & ObjectRef) -> Vec< u8 > {
122
let mut m = Vec :: with_capacity( 80 ) ;
123
m.extend_from_slice ( b " oxy1 sig " ) ;
124
m.extend_from_slice ( domain.name ( ) .as_bytes ( ) ) ;
125
m.push ( 0 ) ;
126
m.extend_from_slice ( & key.0 ) ;
127
m.extend_from_slice ( & target.0 ) ;
128
m
129
}
130
131
# [ derive ( Clone, PartialEq, Eq ) ]
132
pub struct Signature {
133
pub key : KeyId,
134
pub ed : [u8 ; ED25519_SIG_LEN],
135
pub ml : Vec< u8 > ,
136
}
137
138
impl std:: fmt:: Debug for Signature {
139
fn fmt ( & self , f : & mut std:: fmt:: Formatter< '_ > ) -> std:: fmt:: Result {
140
write! ( f, " Signature(key {:?} )" , self .key)
141
}
142
}
143
144
impl Signature {
145
pub fn to_value ( & self ) -> Value {
146
Value:: map( [
147
( 0 , Value:: Bytes( self .key.0. to_vec ( ) ) ) ,
148
( 1 , Value:: Uint( ALG_COMPOSITE ) ) ,
149
( 2 , Value:: Bytes( self .ed.to_vec ( ) ) ) ,
150
( 3 , Value:: Bytes( self .ml.clone ( ) ) ) ,
151
] )
152
}
153
pub fn from_value ( v : & Value) -> Result< Signature, ObjectError> {
154
let m = v.as_map ( ) .ok_or_else ( | | ObjectError:: Schema( " signature: expected map" .into ( ) ) ) ? ;
155
let alg = uint ( field ( m, 1 , " alg" ) ? , " alg" ) ? ;
156
if alg ! = ALG_COMPOSITE {
157
return Err ( ObjectError:: Schema( format! ( " unknown signature algorithm {alg} " ) ) ) ;
158
}
159
let ed = bytes ( field ( m, 2 , " ed" ) ? , " ed" ) ? ;
160
let ml = bytes ( field ( m, 3 , " ml" ) ? , " ml" ) ? ;
161
if ed.len ( ) ! = ED25519_SIG_LEN | | ml.len ( ) ! = MLDSA65_SIG_LEN {
162
return Err ( ObjectError:: Schema( " signature: wrong length" .into ( ) ) ) ;
163
}
164
Ok ( Signature { key: oref ( field ( m, 0 , " key" ) ? , " key" ) ? , ed: ed.try_into ( ) .unwrap ( ) , ml: ml.to_vec ( ) } )
165
}
166
pub fn encode_list ( sigs : & [Signature]) -> Vec< u8 > {
167
cbor:: encode( & Value:: Array( sigs.iter ( ) .map ( | s | s.to_value ( ) ) .collect ( ) ) )
168
}
169
pub fn decode_list ( b : & [u8 ]) -> Result< Vec< Signature> , ObjectError> {
170
let v = cbor:: decode( b) ? ;
171
array ( & v, " signatures" ) ? .iter ( ) .map ( Signature:: from_value) .collect ( )
172
}
173
}
174
175
# [ derive ( Clone, Copy, Debug, PartialEq, Eq, thiserror::Error ) ]
176
pub enum VerifyError {
177
# [ error ( " signature names a different key than the identity" ) ]
178
KeyMismatch,
179
# [ error ( " identity holds a malformed public key" ) ]
180
BadKey,
181
# [ error ( " Ed25519 signature invalid" ) ]
182
Ed25519,
183
# [ error ( " ML-DSA-65 signature invalid" ) ]
184
MlDsa,
185
}
186
187
188
pub fn verify ( identity : & Identity, sig : & Signature, domain : Domain, target : & ObjectRef) -> Result< ( ) , VerifyError> {
189
let key = identity.hash ( ) ;
190
if sig.key ! = key {
191
return Err ( VerifyError:: KeyMismatch) ;
192
}
193
let msg = signing_message ( domain, & key, target) ;
194
let ed_sig = EdSignature:: from_bytes( & sig.ed) ;
195
identity.ed_key ( ) ? .verify_strict ( & msg, & ed_sig) .map_err ( | _| VerifyError:: Ed25519) ? ;
196
let enc = EncodedSignature:: < MlDsa65> :: try_from( sig.ml.as_slice ( ) ) .map_err ( | _| VerifyError:: MlDsa) ? ;
197
let ml_sig = MlSignature:: < MlDsa65> :: decode( & enc) .ok_or ( VerifyError:: MlDsa) ? ;
198
if ! identity.ml_key ( ) ? .verify_with_context ( & msg, b " " , & ml_sig) {
199
return Err ( VerifyError:: MlDsa) ;
200
}
201
Ok ( ( ) )
202
}
203
204
205
# [ derive ( Clone ) ]
206
pub struct SecretKey {
207
pub ed_seed : [u8 ; 32],
208
pub ml_seed : [u8 ; 32],
209
}
210
211
impl std:: fmt:: Debug for SecretKey {
212
fn fmt ( & self , f : & mut std:: fmt:: Formatter< '_ > ) -> std:: fmt:: Result {
213
f.write_str ( " SecretKey(..)" )
214
}
215
}
216
217
impl SecretKey {
218
pub fn generate ( ) -> SecretKey {
219
let mut ed_seed = [ 0 u8 ; 32 ] ;
220
let mut ml_seed = [ 0 u8 ; 32 ] ;
221
rand:: rngs:: OsRng.fill_bytes ( & mut ed_seed) ;
222
rand:: rngs:: OsRng.fill_bytes ( & mut ml_seed) ;
223
SecretKey { ed_seed, ml_seed }
224
}
225
226
fn ed ( & self ) -> EdSigningKey {
227
EdSigningKey:: from_bytes( & self .ed_seed)
228
}
229
fn ml ( & self ) -> MlSigningKey< MlDsa65> {
230
MlSigningKey:: from_seed( & Seed:: from( self .ml_seed) )
231
}
232
233
234
pub fn identity ( & self , name : & str , email : & str , created : u64 ) -> Identity {
235
Identity {
236
ed25519_pk: self .ed ( ) .verifying_key ( ) .to_bytes ( ) ,
237
mldsa65_pk: self .ml ( ) .expanded_key ( ) .verifying_key ( ) .encode ( ) .as_slice ( ) .to_vec ( ) ,
238
name: name.into ( ) ,
239
email: email.into ( ) ,
240
created,
241
expires: None ,
242
previous: None ,
243
delegates: vec! [ ] ,
244
}
245
}
246
247
pub fn sign ( & self , domain : Domain, key : & KeyId, target : & ObjectRef) -> Signature {
248
let msg = signing_message ( domain, key, target) ;
249
let ed = self .ed ( ) .sign ( & msg) .to_bytes ( ) ;
250
let ml = self .ml ( ) .expanded_key ( ) .sign_deterministic ( & msg, b " " ) .expect ( " empty context is valid" ) .encode ( ) .as_slice ( ) .to_vec ( ) ;
251
Signature { key: * key, ed, ml }
252
}
253
254
pub fn encode ( & self , key : & KeyId) -> Vec< u8 > {
255
cbor:: encode( & Value:: map( [
256
( 0 , Value:: Uint( FORMAT_VERSION ) ) ,
257
( 1 , Value:: Bytes( key.0. to_vec ( ) ) ) ,
258
( 2 , Value:: Bytes( self .ed_seed.to_vec ( ) ) ) ,
259
( 3 , Value:: Bytes( self .ml_seed.to_vec ( ) ) ) ,
260
] ) )
261
}
262
263
pub fn decode ( b : & [u8 ]) -> Result< ( KeyId, SecretKey) , ObjectError> {
264
let v = cbor:: decode( b) ? ;
265
let m = v.as_map ( ) .ok_or_else ( | | ObjectError:: Schema( " secret key: expected map" .into ( ) ) ) ? ;
266
check_version ( m) ? ;
267
let seed = | k : u64 | -> Result< [ u8 ; 32 ] , ObjectError> {
268
bytes ( field ( m, k, " seed" ) ? , " seed" ) ? .try_into ( ) .map_err ( | _| ObjectError:: Schema( " seed: expected 32 bytes" .into ( ) ) )
269
} ;
270
Ok ( ( oref ( field ( m, 1 , " key" ) ? , " key" ) ? , SecretKey { ed_seed: seed ( 2 ) ? , ml_seed: seed ( 3 ) ? } ) )
271
}
272
}
273
274
# [ cfg ( test ) ]
275
mod tests {
276
use super :: * ;
277
278
fn fixed_key ( ) -> SecretKey {
279
SecretKey { ed_seed: [ 1 ; 32 ] , ml_seed: [ 2 ; 32 ] }
280
}
281
282
# [ test ]
283
fn identity_round_trip_and_sizes ( ) {
284
let sk = fixed_key ( ) ;
285
let id = sk.identity ( " Ada" , " [email protected] " , 1_700_000_000_000 ) ;
286
assert_eq! ( id.mldsa65_pk.len ( ) , MLDSA65_PK_LEN ) ;
287
let d = Identity:: decode( & id.encode ( ) ) .unwrap ( ) ;
288
assert_eq! ( d, id) ;
289
assert_eq! ( d.hash ( ) , id.hash ( ) ) ;
290
291
assert_eq! ( fixed_key ( ) .identity ( " Ada" , " [email protected] " , 1_700_000_000_000 ) .hash ( ) , id.hash ( ) ) ;
292
let mut other = id.clone ( ) ;
293
other.delegates.push ( ObjectRef( [ 9 ; 32 ] ) ) ;
294
assert_ne! ( other.hash ( ) , id.hash ( ) ) ;
295
}
296
297
# [ test ]
298
fn sign_and_verify ( ) {
299
let sk = fixed_key ( ) ;
300
let id = sk.identity ( " Ada" , " [email protected] " , 1 ) ;
301
let key = id.hash ( ) ;
302
let target = ObjectRef( [ 7 ; 32 ] ) ;
303
let sig = sk.sign ( Domain:: Change, & key, & target) ;
304
assert_eq! ( sig.ml.len ( ) , MLDSA65_SIG_LEN ) ;
305
assert_eq! ( verify ( & id, & sig, Domain:: Change, & target) , Ok ( ( ) ) ) ;
306
307
assert_eq! ( verify ( & id, & sig, Domain:: Change, & ObjectRef( [ 8 ; 32 ] ) ) , Err ( VerifyError:: Ed25519) ) ;
308
assert_eq! ( verify ( & id, & sig, Domain:: Attest, & target) , Err ( VerifyError:: Ed25519) ) ;
309
310
assert_eq! ( sk.sign ( Domain:: Change, & key, & target) , sig) ;
311
5b1114a0 Andy McCall 1 month ago ↶
312
let l = Signature:: decode_list( & Signature:: encode_list( std:: slice:: from_ref( & sig) ) ) .unwrap ( ) ;
1983686f Andy McCall 1 month ago ↶
313
assert_eq! ( l, vec! [ sig] ) ;
314
}
315
316
# [ test ]
317
fn stripping_or_forging_either_half_fails ( ) {
318
let sk = fixed_key ( ) ;
319
let id = sk.identity ( " Ada" , " a@b" , 1 ) ;
320
let key = id.hash ( ) ;
321
let target = ObjectRef( [ 7 ; 32 ] ) ;
322
let good = sk.sign ( Domain:: Change, & key, & target) ;
323
324
let mut no_ml = good.clone ( ) ;
325
no_ml.ml = vec! [ 0 ; MLDSA65_SIG_LEN ] ;
326
assert_eq! ( verify ( & id, & no_ml, Domain:: Change, & target) , Err ( VerifyError:: MlDsa) ) ;
327
328
let mut bad_ed = good.clone ( ) ;
329
bad_ed.ed[ 10 ] ^ = 1 ;
330
assert_eq! ( verify ( & id, & bad_ed, Domain:: Change, & target) , Err ( VerifyError:: Ed25519) ) ;
331
332
333
let other = SecretKey { ed_seed: [ 3 ; 32 ] , ml_seed: [ 4 ; 32 ] } ;
334
let other_id = other.identity ( " Bob" , " b@b" , 1 ) ;
335
let theirs = other.sign ( Domain:: Change, & other_id.hash ( ) , & target) ;
336
assert_eq! ( verify ( & id, & theirs, Domain:: Change, & target) , Err ( VerifyError:: KeyMismatch) ) ;
337
let mut relabelled = theirs.clone ( ) ;
338
relabelled.key = key;
339
assert_eq! ( verify ( & id, & relabelled, Domain:: Change, & target) , Err ( VerifyError:: Ed25519) ) ;
340
341
342
let mut short = good.to_value ( ) ;
343
if let Value:: Map( m) = & mut short {
344
m.insert ( 3 , Value:: Bytes( vec! [ 0 ; 10 ] ) ) ;
345
}
346
assert! ( Signature:: from_value( & short) .is_err ( ) ) ;
347
}
348
349
# [ test ]
350
fn secret_key_round_trip ( ) {
351
let sk = fixed_key ( ) ;
352
let key = sk.identity ( " x" , " y" , 0 ) .hash ( ) ;
353
let ( k, d) = SecretKey:: decode( & sk.encode ( & key) ) .unwrap ( ) ;
354
assert_eq! ( k, key) ;
355
assert_eq! ( d.ed_seed, sk.ed_seed) ;
356
assert_eq! ( d.ml_seed, sk.ml_seed) ;
357
assert_ne! ( SecretKey:: generate( ) .ed_seed, SecretKey:: generate( ) .ed_seed) ;
358
}
359
}