@@ -12,49 +12,6 @@ use ring::{
1212} ;
1313use rustls_pki_types:: PrivateKeyDer ;
1414
15- // PKCS#8 PrivateKeyInfo template for a P-256 key generated by p256/pkcs8 0.10,
16- // without the EC public key. This prefix is stable because all lengths and
17- // algorithm identifiers are constant for prime256v1.
18- //
19- // Structure:
20- // PrivateKeyInfo ::= SEQUENCE {
21- // version INTEGER (0),
22- // privateKeyAlgorithm AlgorithmIdentifier {
23- // id-ecPublicKey, prime256v1
24- // },
25- // privateKey OCTET STRING (ECPrivateKey)
26- // }
27- //
28- // ECPrivateKey ::= SEQUENCE {
29- // version INTEGER (1),
30- // privateKey OCTET STRING (32 bytes),
31- // publicKey [1] BIT STRING OPTIONAL
32- // }
33- //
34- // The remaining suffix encodes the [1] publicKey BIT STRING header; the
35- // actual SEC1-encoded uncompressed point (65 bytes) is appended after it.
36- const P256_PKCS8_PREFIX : [ u8 ; 36 ] = [
37- 0x30 , 0x81 , 0x87 , // SEQUENCE, len 0x87
38- 0x02 , 0x01 , 0x00 , // version = 0
39- 0x30 , 0x13 , // SEQUENCE (AlgorithmIdentifier)
40- 0x06 , 0x07 , 0x2a , 0x86 , 0x48 , 0xce , 0x3d , 0x02 , 0x01 , // id-ecPublicKey
41- 0x06 , 0x08 , 0x2a , 0x86 , 0x48 , 0xce , 0x3d , 0x03 , 0x01 , 0x07 , // prime256v1
42- 0x04 , 0x6d , // OCTET STRING, len 0x6d (ECPrivateKey)
43- 0x30 , 0x6b , // SEQUENCE (ECPrivateKey), len 0x6b
44- 0x02 , 0x01 , 0x01 , // version = 1
45- 0x04 , 0x20 , // OCTET STRING, len 32 (private key)
46- ] ;
47-
48- // Context-specific [1] publicKey BIT STRING wrapper; the 65-byte SEC1
49- // uncompressed point (0x04 || X || Y) is appended after this header.
50- const P256_PKCS8_PUBLIC_KEY_PREFIX : [ u8 ; 5 ] = [
51- 0xa1 , 0x44 , // [1] constructed, len 0x44
52- 0x03 , 0x42 , // BIT STRING, len 0x42
53- 0x00 , // number of unused bits
54- ] ;
55-
56- const P256_PKCS8_TOTAL_LEN : usize = 138 ;
57-
5815struct AnySizeKey ( usize ) ;
5916impl KeyType for AnySizeKey {
6017 fn len ( & self ) -> usize {
@@ -110,65 +67,27 @@ fn sha256(data: &[u8]) -> [u8; 32] {
11067
11168/// Derives a X25519 secret from a given key pair.
11269///
113- /// Historically this was implemented as:
114- /// 1. derive a P-256 key pair from `from` using HKDF
115- /// 2. hash `rcgen::KeyPair::serialized_der()` with SHA-256
70+ /// This derives a P-256 scalar from `from` via HKDF, encodes it as PKCS#8
71+ /// DER using the p256 crate, and hashes the result with SHA-256.
11672///
117- /// That made the result sensitive to library-level changes in PKCS#8
118- /// encoding. To avoid this, we now:
119- /// - derive the same P-256 scalar as before
120- /// - encode it using a fixed, Dstack-defined PKCS#8 layout
121- /// - hash that encoding with SHA-256
73+ /// The p256 crate's PKCS#8 encoding is deterministic (canonical ASN.1 DER)
74+ /// and produces output identical to the previous rcgen-based implementation.
12275pub fn derive_dh_secret ( from : & KeyPair , context_data : & [ & [ u8 ] ] ) -> Result < [ u8 ; 32 ] > {
123- use p256:: elliptic_curve:: sec1:: ToEncodedPoint ;
124-
125- // 1. Decode the root CA key from rcgen::KeyPair into a P-256 scalar.
12676 let der_bytes = from. serialized_der ( ) ;
12777 let sk =
12878 p256:: SecretKey :: from_pkcs8_der ( der_bytes) . context ( "failed to decode root secret key" ) ?;
12979 let sk_bytes = sk. as_scalar_primitive ( ) . to_bytes ( ) ;
13080
131- // 2. Derive the same 32-byte scalar as before using HKDF.
13281 let derived_sk_bytes = derive_key ( sk_bytes. as_slice ( ) , context_data, 32 )
13382 . or ( Err ( anyhow ! ( "failed to derive key" ) ) ) ?;
134- let derived_sk_bytes: [ u8 ; 32 ] = derived_sk_bytes
135- . as_slice ( )
136- . try_into ( )
137- . map_err ( |_| anyhow ! ( "unexpected length for derived key" ) ) ?;
13883
139- // 3. Compute the corresponding P-256 public key (uncompressed SEC1).
14084 let derived_sk = p256:: SecretKey :: from_slice ( & derived_sk_bytes)
14185 . context ( "failed to decode derived secret key" ) ?;
142- let public_key = derived_sk. public_key ( ) ;
143- let encoded_point = public_key. to_encoded_point ( false ) ;
144- let public_key_bytes = encoded_point. as_bytes ( ) ; // 0x04 || X || Y (65 bytes)
145-
146- // 4. Build a fixed PKCS#8 PrivateKeyInfo encoding matching the previous
147- // rcgen/pkcs8 output for prime256v1 keys.
148- anyhow:: ensure!(
149- public_key_bytes. len( ) == 65 ,
150- "unexpected P-256 public key length"
151- ) ;
152-
153- let mut pkcs8 = [ 0u8 ; P256_PKCS8_TOTAL_LEN ] ;
154- // Prefix up to the private key OCTET STRING contents.
155- pkcs8[ ..P256_PKCS8_PREFIX . len ( ) ] . copy_from_slice ( & P256_PKCS8_PREFIX ) ;
156-
157- // 32-byte private key.
158- let mut offset = P256_PKCS8_PREFIX . len ( ) ;
159- pkcs8[ offset..offset + 32 ] . copy_from_slice ( & derived_sk_bytes) ;
160- offset += 32 ;
161-
162- // [1] BIT STRING public key header.
163- pkcs8[ offset..offset + P256_PKCS8_PUBLIC_KEY_PREFIX . len ( ) ]
164- . copy_from_slice ( & P256_PKCS8_PUBLIC_KEY_PREFIX ) ;
165- offset += P256_PKCS8_PUBLIC_KEY_PREFIX . len ( ) ;
166-
167- // SEC1-encoded uncompressed public key bytes.
168- pkcs8[ offset..offset + public_key_bytes. len ( ) ] . copy_from_slice ( public_key_bytes) ;
169-
170- let derived_secret = sha256 ( & pkcs8) ;
171- Ok ( derived_secret)
86+ let pkcs8_der = derived_sk
87+ . to_pkcs8_der ( )
88+ . context ( "failed to encode derived secret key" ) ?;
89+
90+ Ok ( sha256 ( pkcs8_der. as_bytes ( ) ) )
17291}
17392
17493#[ cfg( test) ]
0 commit comments