@@ -24,6 +24,9 @@ const SIGNATURE_PREFIX: &[u8] = b"CompositeAlgorithmSignatures2025";
2424/// Label is the signature label for ML-DSA-65-Ed25519-SHA512.
2525const SIGNATURE_DOMAIN : & [ u8 ] = b"COMPSIG-MLDSA65-Ed25519-SHA512" ;
2626
27+ /// OID is the ASN.1 object identifier for MLDSA65-Ed25519-SHA512.
28+ const OID : ObjectIdentifier = ObjectIdentifier :: new_unwrap ( "1.3.6.1.5.5.7.6.48" ) ;
29+
2730/// SecretKey is an ML-DSA-65 private key paired with an Ed25519 private key for
2831/// creating and verifying quantum resistant digital signatures.
2932#[ derive( Clone ) ]
@@ -68,22 +71,17 @@ impl SecretKey {
6871 // Parse the DER encoded container
6972 let info = pkcs8:: PrivateKeyInfo :: from_der ( der) ?;
7073
71- // Ensure the algorithm OID matches MLDSA65-Ed25519-SHA512 (1.3.6.1.5.5.7.6.48)
72- if info. algorithm . oid . to_string ( ) != "1.3.6.1.5.5.7.6.48" {
74+ // Ensure the algorithm OID matches MLDSA65-Ed25519-SHA512
75+ if info. algorithm . oid != OID {
7376 return Err ( "not a composite ML-DSA-65-Ed25519-SHA512 private key" . into ( ) ) ;
7477 }
7578 // Private key is ML-DSA seed (32) || Ed25519 seed (32) = 64 bytes
76- let key_bytes = info. private_key ;
77- if key_bytes. len ( ) != 64 {
78- return Err ( "composite private key must be 64 bytes" . into ( ) ) ;
79- }
80- let ml_seed: [ u8 ; 32 ] = key_bytes[ ..32 ] . try_into ( ) ?;
81- let ed_seed: [ u8 ; 32 ] = key_bytes[ 32 ..64 ] . try_into ( ) ?;
82-
83- let ml_key = mldsa:: SecretKey :: from_seed ( & ml_seed) ;
84- let ed_key = eddsa:: SecretKey :: from_bytes ( & ed_seed) ;
79+ let seed: [ u8 ; 64 ] = info
80+ . private_key
81+ . try_into ( )
82+ . map_err ( |_| "composite private key must be 64 bytes" ) ?;
8583
86- Ok ( Self { ml_key , ed_key } )
84+ Ok ( Self :: from_seed ( & seed ) )
8785 }
8886
8987 /// from_pem parses a PEM string into a private key.
@@ -118,13 +116,11 @@ impl SecretKey {
118116 // Create the MLDSA65-Ed25519-SHA512 algorithm identifier; parameters
119117 // MUST be absent
120118 let alg = pkcs8:: AlgorithmIdentifierRef {
121- oid : ObjectIdentifier :: new_unwrap ( "1.3.6.1.5.5.7.6.48" ) ,
119+ oid : OID ,
122120 parameters : None ,
123121 } ;
124122 // The private key is ML-DSA seed (32) || Ed25519 seed (32) = 64 bytes
125- let mut key_bytes = Vec :: with_capacity ( 64 ) ;
126- key_bytes. extend_from_slice ( & self . ml_key . to_seed ( ) ) ;
127- key_bytes. extend_from_slice ( & self . ed_key . to_bytes ( ) ) ;
123+ let key_bytes = self . to_seed ( ) ;
128124
129125 let info = pkcs8:: PrivateKeyInfo {
130126 algorithm : alg,
@@ -209,22 +205,19 @@ impl PublicKey {
209205 let info: SubjectPublicKeyInfo < AlgorithmIdentifier < AnyRef > , BitStringRef > =
210206 SubjectPublicKeyInfo :: from_der ( der) ?;
211207
212- // Ensure the algorithm OID matches MLDSA65-Ed25519-SHA512 (1.3.6.1.5.5.7.6.48)
213- if info. algorithm . oid . to_string ( ) != "1.3.6.1.5.5.7.6.48" {
208+ // Ensure the algorithm OID matches MLDSA65-Ed25519-SHA512
209+ if info. algorithm . oid != OID {
214210 return Err ( "not a composite ML-DSA-65-Ed25519-SHA512 public key" . into ( ) ) ;
215211 }
216212 // Public key is ML-DSA-65 (1952 bytes) || Ed25519 (32 bytes) = 1984 bytes
217- let key_bytes = info. subject_public_key . as_bytes ( ) . unwrap ( ) ;
218- if key_bytes. len ( ) != 1984 {
219- return Err ( "composite public key must be 1984 bytes" . into ( ) ) ;
220- }
221- let ml_bytes: [ u8 ; 1952 ] = key_bytes[ ..1952 ] . try_into ( ) ?;
222- let ed_bytes: [ u8 ; 32 ] = key_bytes[ 1952 ..] . try_into ( ) ?;
223-
224- let ml_key = mldsa:: PublicKey :: from_bytes ( & ml_bytes) ;
225- let ed_key = eddsa:: PublicKey :: from_bytes ( & ed_bytes) ?;
226-
227- Ok ( Self { ml_key, ed_key } )
213+ let key_bytes: [ u8 ; 1984 ] = info
214+ . subject_public_key
215+ . as_bytes ( )
216+ . ok_or ( "invalid public key bit string" ) ?
217+ . try_into ( )
218+ . map_err ( |_| "composite public key must be 1984 bytes" ) ?;
219+
220+ Self :: from_bytes ( & key_bytes)
228221 }
229222
230223 /// from_pem parses a PEM string into a public key.
@@ -251,13 +244,11 @@ impl PublicKey {
251244 // Create the MLDSA65-Ed25519-SHA512 algorithm identifier; parameters
252245 // MUST be absent
253246 let alg = spki:: AlgorithmIdentifierRef {
254- oid : ObjectIdentifier :: new_unwrap ( "1.3.6.1.5.5.7.6.48" ) ,
247+ oid : OID ,
255248 parameters : None ,
256249 } ;
257250 // The public key info is the BITSTRING of the two keys concatenated
258- let mut key_bytes = Vec :: with_capacity ( 1984 ) ;
259- key_bytes. extend_from_slice ( & self . ml_key . to_bytes ( ) ) ;
260- key_bytes. extend_from_slice ( & self . ed_key . to_bytes ( ) ) ;
251+ let key_bytes = self . to_bytes ( ) ;
261252
262253 let info = SubjectPublicKeyInfo :: < AnyRef , BitStringRef > {
263254 algorithm : alg,
0 commit comments