@@ -23,10 +23,10 @@ use crate::ffi;
2323use crate :: ffi:: cbs_init;
2424
2525/// Seed size (32 bytes, shared across all ML-DSA parameter sets).
26- pub const SEED_BYTES : usize = ffi:: MLDSA_SEED_BYTES as usize ;
26+ pub const PRIVATE_KEY_SEED_BYTES : usize = ffi:: MLDSA_SEED_BYTES as usize ;
2727
2828/// Raw bytes of a private key seed ([`SEED_BYTES`] long).
29- pub type MlDsaSeed = [ u8 ; SEED_BYTES ] ;
29+ pub type MlDsaPrivateKeySeed = [ u8 ; PRIVATE_KEY_SEED_BYTES ] ;
3030
3131/// ML-DSA parameter set selection.
3232#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
@@ -62,11 +62,13 @@ impl Algorithm {
6262}
6363
6464/// An ML-DSA public key (any parameter set).
65+ #[ derive( Clone ) ]
6566pub struct MlDsaPublicKey {
6667 algorithm : Algorithm ,
6768 inner : PublicKeyInner ,
6869}
6970
71+ #[ derive( Clone ) ]
7072enum PublicKeyInner {
7173 MlDsa44 ( Box < ffi:: MLDSA44_public_key > ) ,
7274 MlDsa65 ( Box < ffi:: MLDSA65_public_key > ) ,
@@ -76,7 +78,7 @@ enum PublicKeyInner {
7678/// An ML-DSA private key (any parameter set).
7779pub struct MlDsaPrivateKey {
7880 algorithm : Algorithm ,
79- seed : MlDsaSeed ,
81+ seed : MlDsaPrivateKeySeed ,
8082 inner : PrivateKeyInner ,
8183}
8284
@@ -86,6 +88,12 @@ enum PrivateKeyInner {
8688 MlDsa87 ( Box < ffi:: MLDSA87_private_key > ) ,
8789}
8890
91+ impl Clone for MlDsaPrivateKey {
92+ fn clone ( & self ) -> Self {
93+ Self :: from_seed ( self . algorithm , & self . seed ) . unwrap ( )
94+ }
95+ }
96+
8997impl MlDsaPrivateKey {
9098 /// Generates a random ML-DSA key pair.
9199 ///
@@ -96,14 +104,14 @@ impl MlDsaPrivateKey {
96104 match algorithm {
97105 Algorithm :: MlDsa44 => {
98106 let mut pub_bytes = [ 0u8 ; ffi:: MLDSA44_PUBLIC_KEY_BYTES as usize ] ;
99- let mut seed = [ 0u8 ; SEED_BYTES ] ;
107+ let mut seed = [ 0u8 ; PRIVATE_KEY_SEED_BYTES ] ;
100108 let mut priv_key: MaybeUninit < ffi:: MLDSA44_private_key > = MaybeUninit :: uninit ( ) ;
101109 cvt ( ffi:: MLDSA44_generate_key (
102110 pub_bytes. as_mut_ptr ( ) ,
103111 seed. as_mut_ptr ( ) ,
104112 priv_key. as_mut_ptr ( ) ,
105113 ) ) ?;
106- let public_key = MlDsaPublicKey :: from_bytes ( algorithm, & pub_bytes) ?;
114+ let public_key = MlDsaPublicKey :: from_slice ( algorithm, & pub_bytes) ?;
107115 Ok ( (
108116 public_key,
109117 MlDsaPrivateKey {
@@ -115,14 +123,14 @@ impl MlDsaPrivateKey {
115123 }
116124 Algorithm :: MlDsa65 => {
117125 let mut pub_bytes = [ 0u8 ; ffi:: MLDSA65_PUBLIC_KEY_BYTES as usize ] ;
118- let mut seed = [ 0u8 ; SEED_BYTES ] ;
126+ let mut seed = [ 0u8 ; PRIVATE_KEY_SEED_BYTES ] ;
119127 let mut priv_key: MaybeUninit < ffi:: MLDSA65_private_key > = MaybeUninit :: uninit ( ) ;
120128 cvt ( ffi:: MLDSA65_generate_key (
121129 pub_bytes. as_mut_ptr ( ) ,
122130 seed. as_mut_ptr ( ) ,
123131 priv_key. as_mut_ptr ( ) ,
124132 ) ) ?;
125- let public_key = MlDsaPublicKey :: from_bytes ( algorithm, & pub_bytes) ?;
133+ let public_key = MlDsaPublicKey :: from_slice ( algorithm, & pub_bytes) ?;
126134 Ok ( (
127135 public_key,
128136 MlDsaPrivateKey {
@@ -134,14 +142,14 @@ impl MlDsaPrivateKey {
134142 }
135143 Algorithm :: MlDsa87 => {
136144 let mut pub_bytes = [ 0u8 ; ffi:: MLDSA87_PUBLIC_KEY_BYTES as usize ] ;
137- let mut seed = [ 0u8 ; SEED_BYTES ] ;
145+ let mut seed = [ 0u8 ; PRIVATE_KEY_SEED_BYTES ] ;
138146 let mut priv_key: MaybeUninit < ffi:: MLDSA87_private_key > = MaybeUninit :: uninit ( ) ;
139147 cvt ( ffi:: MLDSA87_generate_key (
140148 pub_bytes. as_mut_ptr ( ) ,
141149 seed. as_mut_ptr ( ) ,
142150 priv_key. as_mut_ptr ( ) ,
143151 ) ) ?;
144- let public_key = MlDsaPublicKey :: from_bytes ( algorithm, & pub_bytes) ?;
152+ let public_key = MlDsaPublicKey :: from_slice ( algorithm, & pub_bytes) ?;
145153 Ok ( (
146154 public_key,
147155 MlDsaPrivateKey {
@@ -156,7 +164,7 @@ impl MlDsaPrivateKey {
156164 }
157165
158166 /// Regenerates a private key from a seed value.
159- pub fn from_seed ( algorithm : Algorithm , seed : & MlDsaSeed ) -> Result < Self , ErrorStack > {
167+ pub fn from_seed ( algorithm : Algorithm , seed : & MlDsaPrivateKeySeed ) -> Result < Self , ErrorStack > {
160168 unsafe {
161169 ffi:: init ( ) ;
162170 match algorithm {
@@ -209,7 +217,7 @@ impl MlDsaPrivateKey {
209217 }
210218
211219 /// Returns the seed bytes for this private key.
212- pub fn seed ( & self ) -> & MlDsaSeed {
220+ pub fn seed_bytes ( & self ) -> & MlDsaPrivateKeySeed {
213221 & self . seed
214222 }
215223
@@ -261,12 +269,20 @@ impl MlDsaPrivateKey {
261269
262270impl MlDsaPublicKey {
263271 /// Parses a public key from its serialized form.
264- pub fn from_bytes ( algorithm : Algorithm , bytes : & [ u8 ] ) -> Result < Self , ErrorStack > {
272+ pub fn from_slice (
273+ algorithm : Algorithm ,
274+ serialized_public_key : & [ u8 ] ,
275+ ) -> Result < Self , ErrorStack > {
276+ ffi:: init ( ) ;
277+
278+ if serialized_public_key. len ( ) != algorithm. public_key_bytes ( ) {
279+ return Err ( ErrorStack :: internal_error_str ( "invalid public key length" ) ) ;
280+ }
281+ let mut cbs = cbs_init ( serialized_public_key) ;
282+
265283 unsafe {
266- ffi:: init ( ) ;
267284 match algorithm {
268285 Algorithm :: MlDsa44 => {
269- let mut cbs = cbs_init ( bytes) ;
270286 let mut key: MaybeUninit < ffi:: MLDSA44_public_key > = MaybeUninit :: uninit ( ) ;
271287 cvt ( ffi:: MLDSA44_parse_public_key ( key. as_mut_ptr ( ) , & mut cbs) ) ?;
272288 if cbs. len != 0 {
@@ -280,7 +296,6 @@ impl MlDsaPublicKey {
280296 } )
281297 }
282298 Algorithm :: MlDsa65 => {
283- let mut cbs = cbs_init ( bytes) ;
284299 let mut key: MaybeUninit < ffi:: MLDSA65_public_key > = MaybeUninit :: uninit ( ) ;
285300 cvt ( ffi:: MLDSA65_parse_public_key ( key. as_mut_ptr ( ) , & mut cbs) ) ?;
286301 if cbs. len != 0 {
@@ -294,7 +309,6 @@ impl MlDsaPublicKey {
294309 } )
295310 }
296311 Algorithm :: MlDsa87 => {
297- let mut cbs = cbs_init ( bytes) ;
298312 let mut key: MaybeUninit < ffi:: MLDSA87_public_key > = MaybeUninit :: uninit ( ) ;
299313 cvt ( ffi:: MLDSA87_parse_public_key ( key. as_mut_ptr ( ) , & mut cbs) ) ?;
300314 if cbs. len != 0 {
@@ -398,9 +412,12 @@ mod tests {
398412 fn sign_and_verify( ) {
399413 let ( pk, sk) = MlDsaPrivateKey :: generate( $alg) . unwrap( ) ;
400414 let msg = b"test message" ;
401- let sig = sk. sign( msg) . unwrap( ) ;
402- assert_eq!( sig. len( ) , $alg. signature_bytes( ) ) ;
403- assert!( pk. verify( msg, & sig) . is_ok( ) ) ;
415+ let sig1 = sk. sign( msg) . unwrap( ) ;
416+ let sig2 = sk. clone( ) . sign( msg) . unwrap( ) ;
417+ assert_eq!( sig1. len( ) , $alg. signature_bytes( ) ) ;
418+ assert!( pk. verify( msg, & sig1) . is_ok( ) ) ;
419+ assert!( pk. verify( msg, & sig2) . is_ok( ) ) ;
420+ assert!( pk. clone( ) . verify( msg, & sig1) . is_ok( ) ) ;
404421 }
405422
406423 #[ test]
@@ -422,18 +439,20 @@ mod tests {
422439 #[ test]
423440 fn seed_roundtrip( ) {
424441 let ( pk, sk) = MlDsaPrivateKey :: generate( $alg) . unwrap( ) ;
425- let sk2 = MlDsaPrivateKey :: from_seed( $alg, sk. seed ( ) ) . unwrap( ) ;
442+ let sk2 = MlDsaPrivateKey :: from_seed( $alg, sk. seed_bytes ( ) ) . unwrap( ) ;
426443 let msg = b"seed roundtrip" ;
427- let sig = sk2. sign( msg) . unwrap( ) ;
428- assert!( pk. verify( msg, & sig) . is_ok( ) ) ;
444+ let sig1 = sk2. sign( msg) . unwrap( ) ;
445+ let sig2 = sk2. clone( ) . sign( msg) . unwrap( ) ;
446+ assert!( pk. verify( msg, & sig1) . is_ok( ) ) ;
447+ assert!( pk. verify( msg, & sig2) . is_ok( ) ) ;
429448 }
430449
431450 #[ test]
432451 fn debug_redacts_seed( ) {
433452 let ( _, sk) = MlDsaPrivateKey :: generate( $alg) . unwrap( ) ;
434453 let dbg = format!( "{:?}" , sk) ;
435454 assert!( dbg. contains( "redacted" ) ) ;
436- assert!( !dbg. contains( & format!( "{:?}" , sk. seed ( ) ) ) ) ;
455+ assert!( !dbg. contains( & format!( "{:?}" , sk. seed_bytes ( ) ) ) ) ;
437456 }
438457 }
439458 } ;
0 commit comments