44
55use foreign_types_shared:: ForeignType ;
66use openssl_sys as ffi;
7+ #[ cfg( CRYPTOGRAPHY_IS_AWSLC ) ]
78use std:: os:: raw:: c_int;
89
910use crate :: { cvt, cvt_p, OpenSSLResult } ;
1011
12+ #[ cfg( CRYPTOGRAPHY_IS_AWSLC ) ]
1113pub const PKEY_ID : openssl:: pkey:: Id = openssl:: pkey:: Id :: from_raw ( ffi:: NID_kem ) ;
1214
15+ pub fn is_mlkem_pkey_type ( id : openssl:: pkey:: Id ) -> bool {
16+ cfg_if:: cfg_if! {
17+ if #[ cfg( CRYPTOGRAPHY_IS_BORINGSSL ) ] {
18+ let raw = id. as_raw( ) ;
19+ raw == ffi:: NID_ML_KEM_768 || raw == ffi:: NID_ML_KEM_1024
20+ } else if #[ cfg( CRYPTOGRAPHY_IS_AWSLC ) ] {
21+ id == PKEY_ID
22+ }
23+ }
24+ }
25+
1326#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
1427pub enum MlKemVariant {
1528 MlKem768 ,
1629 MlKem1024 ,
1730}
1831
1932impl MlKemVariant {
33+ #[ cfg( CRYPTOGRAPHY_IS_AWSLC ) ]
2034 pub fn nid ( self ) -> c_int {
2135 match self {
2236 MlKemVariant :: MlKem768 => ffi:: NID_MLKEM768 ,
@@ -27,20 +41,44 @@ impl MlKemVariant {
2741 pub fn from_pkey < T : openssl:: pkey:: HasPublic > (
2842 pkey : & openssl:: pkey:: PKeyRef < T > ,
2943 ) -> MlKemVariant {
30- // AWS-LC is missing the equivalent `EVP_PKEY_pqdsa_get_type`, so we
31- // are using the key size as a discriminator to find the variant.
32- let len = pkey
33- . raw_public_key ( )
34- . expect ( "valid ML-KEM public key" )
35- . len ( ) ;
36- match len {
37- 1184 => MlKemVariant :: MlKem768 ,
38- 1568 => MlKemVariant :: MlKem1024 ,
39- _ => panic ! ( "Unsupported ML-KEM variant" ) ,
44+ cfg_if:: cfg_if! {
45+ if #[ cfg( CRYPTOGRAPHY_IS_BORINGSSL ) ] {
46+ match pkey. id( ) . as_raw( ) {
47+ ffi:: NID_ML_KEM_768 => MlKemVariant :: MlKem768 ,
48+ ffi:: NID_ML_KEM_1024 => MlKemVariant :: MlKem1024 ,
49+ _ => panic!( "Unsupported ML-KEM variant" ) ,
50+ }
51+ } else if #[ cfg( CRYPTOGRAPHY_IS_AWSLC ) ] {
52+ // AWS-LC is missing the equivalent `EVP_PKEY_pqdsa_get_type`,
53+ // so we are using the key size as a discriminator to find the
54+ // variant.
55+ let len = pkey
56+ . raw_public_key( )
57+ . expect( "valid ML-KEM public key" )
58+ . len( ) ;
59+ match len {
60+ 1184 => MlKemVariant :: MlKem768 ,
61+ 1568 => MlKemVariant :: MlKem1024 ,
62+ _ => panic!( "Unsupported ML-KEM variant" ) ,
63+ }
64+ }
65+ }
66+ }
67+ }
68+
69+ #[ cfg( CRYPTOGRAPHY_IS_BORINGSSL ) ]
70+ fn evp_pkey_alg ( variant : MlKemVariant ) -> * const ffi:: EVP_PKEY_ALG {
71+ // SAFETY: These functions return static, non-null pointers to the
72+ // EVP_PKEY_ALG for each ML-KEM variant.
73+ unsafe {
74+ match variant {
75+ MlKemVariant :: MlKem768 => ffi:: EVP_pkey_ml_kem_768 ( ) ,
76+ MlKemVariant :: MlKem1024 => ffi:: EVP_pkey_ml_kem_1024 ( ) ,
4077 }
4178 }
4279}
4380
81+ #[ cfg( CRYPTOGRAPHY_IS_AWSLC ) ]
4482extern "C" {
4583 // Manually declared because this function is in an experimental header
4684 // in AWS-LC (April 2026).
@@ -57,49 +95,74 @@ pub fn new_raw_private_key(
5795 variant : MlKemVariant ,
5896 seed : & [ u8 ] ,
5997) -> OpenSSLResult < openssl:: pkey:: PKey < openssl:: pkey:: Private > > {
60- let ctx = openssl:: pkey_ctx:: PkeyCtx :: new_id ( PKEY_ID ) ?;
61- // SAFETY: ctx is a valid EVP_PKEY_CTX for KEM.
62- unsafe {
63- cvt ( ffi:: EVP_PKEY_CTX_kem_set_params (
64- ctx. as_ptr ( ) ,
65- variant. nid ( ) ,
66- ) ) ?
67- } ;
68- // SAFETY: ctx is a valid EVP_PKEY_CTX with KEM params set.
69- unsafe { cvt ( ffi:: EVP_PKEY_keygen_init ( ctx. as_ptr ( ) ) ) ? } ;
70-
71- let mut pkey: * mut ffi:: EVP_PKEY = std:: ptr:: null_mut ( ) ;
72- let mut seed_len = seed. len ( ) ;
73- // SAFETY: ctx is initialized for keygen, seed points to valid memory.
74- unsafe {
75- cvt ( EVP_PKEY_keygen_deterministic (
76- ctx. as_ptr ( ) ,
77- & mut pkey,
78- seed. as_ptr ( ) ,
79- & mut seed_len,
80- ) ) ?;
98+ cfg_if:: cfg_if! {
99+ if #[ cfg( CRYPTOGRAPHY_IS_BORINGSSL ) ] {
100+ // SAFETY: EVP_PKEY_from_private_seed creates a new EVP_PKEY from
101+ // the seed. evp_pkey_alg returns a valid algorithm pointer.
102+ unsafe {
103+ let pkey = cvt_p( ffi:: EVP_PKEY_from_private_seed (
104+ evp_pkey_alg( variant) ,
105+ seed. as_ptr( ) ,
106+ seed. len( ) ,
107+ ) ) ?;
108+ Ok ( openssl:: pkey:: PKey :: from_ptr( pkey) )
109+ }
110+ } else if #[ cfg( CRYPTOGRAPHY_IS_AWSLC ) ] {
111+ let ctx = openssl:: pkey_ctx:: PkeyCtx :: new_id( PKEY_ID ) ?;
112+ // SAFETY: ctx is a valid EVP_PKEY_CTX for KEM.
113+ unsafe {
114+ cvt( ffi:: EVP_PKEY_CTX_kem_set_params (
115+ ctx. as_ptr( ) ,
116+ variant. nid( ) ,
117+ ) ) ?
118+ } ;
119+ // SAFETY: ctx is a valid EVP_PKEY_CTX with KEM params set.
120+ unsafe { cvt( ffi:: EVP_PKEY_keygen_init ( ctx. as_ptr( ) ) ) ? } ;
121+
122+ let mut pkey: * mut ffi:: EVP_PKEY = std:: ptr:: null_mut( ) ;
123+ let mut seed_len = seed. len( ) ;
124+ // SAFETY: ctx is initialized for keygen, seed points to valid memory.
125+ unsafe {
126+ cvt( EVP_PKEY_keygen_deterministic (
127+ ctx. as_ptr( ) ,
128+ & mut pkey,
129+ seed. as_ptr( ) ,
130+ & mut seed_len,
131+ ) ) ?;
132+ }
133+ assert_eq!( seed_len, 64 ) ;
134+ // SAFETY: EVP_PKEY_keygen_deterministic succeeded, pkey is valid.
135+ let pkey = unsafe { openssl:: pkey:: PKey :: from_ptr( pkey) } ;
136+ Ok ( pkey)
137+ }
81138 }
82- let expected_seed_len = match variant {
83- MlKemVariant :: MlKem768 | MlKemVariant :: MlKem1024 => 64 ,
84- } ;
85- assert_eq ! ( seed_len, expected_seed_len) ;
86- // SAFETY: EVP_PKEY_keygen_deterministic succeeded, pkey is valid.
87- let pkey = unsafe { openssl:: pkey:: PKey :: from_ptr ( pkey) } ;
88- Ok ( pkey)
89139}
90140
91141pub fn new_raw_public_key (
92142 variant : MlKemVariant ,
93143 data : & [ u8 ] ,
94144) -> OpenSSLResult < openssl:: pkey:: PKey < openssl:: pkey:: Public > > {
95- // SAFETY: data points to valid memory of the given length.
96- unsafe {
97- let pkey = cvt_p ( ffi:: EVP_PKEY_kem_new_raw_public_key (
98- variant. nid ( ) ,
99- data. as_ptr ( ) ,
100- data. len ( ) ,
101- ) ) ?;
102- Ok ( openssl:: pkey:: PKey :: from_ptr ( pkey) )
145+ cfg_if:: cfg_if! {
146+ if #[ cfg( CRYPTOGRAPHY_IS_BORINGSSL ) ] {
147+ let nid = match variant {
148+ MlKemVariant :: MlKem768 => ffi:: NID_ML_KEM_768 ,
149+ MlKemVariant :: MlKem1024 => ffi:: NID_ML_KEM_1024 ,
150+ } ;
151+ openssl:: pkey:: PKey :: public_key_from_raw_bytes(
152+ data,
153+ openssl:: pkey:: Id :: from_raw( nid) ,
154+ )
155+ } else if #[ cfg( CRYPTOGRAPHY_IS_AWSLC ) ] {
156+ // SAFETY: data points to valid memory of the given length.
157+ unsafe {
158+ let pkey = cvt_p( ffi:: EVP_PKEY_kem_new_raw_public_key (
159+ variant. nid( ) ,
160+ data. as_ptr( ) ,
161+ data. len( ) ,
162+ ) ) ?;
163+ Ok ( openssl:: pkey:: PKey :: from_ptr( pkey) )
164+ }
165+ }
103166 }
104167}
105168
@@ -111,6 +174,12 @@ pub fn encapsulate(
111174 MlKemVariant :: MlKem1024 => ( 1568 , 32 ) ,
112175 } ;
113176 let ctx = openssl:: pkey_ctx:: PkeyCtx :: new ( pkey) ?;
177+ #[ cfg( CRYPTOGRAPHY_IS_BORINGSSL ) ]
178+ {
179+ // SAFETY: ctx is a valid EVP_PKEY_CTX for the KEM operation.
180+ let res = unsafe { ffi:: EVP_PKEY_encapsulate_init ( ctx. as_ptr ( ) , std:: ptr:: null ( ) ) } ;
181+ cvt ( res) ?;
182+ }
114183
115184 let mut ciphertext = vec ! [ 0u8 ; ct_bytes] ;
116185 let mut shared_secret = vec ! [ 0u8 ; ss_bytes] ;
@@ -136,10 +205,14 @@ pub fn decapsulate(
136205 ciphertext : & [ u8 ] ,
137206) -> OpenSSLResult < Vec < u8 > > {
138207 let ctx = openssl:: pkey_ctx:: PkeyCtx :: new ( pkey) ?;
208+ #[ cfg( CRYPTOGRAPHY_IS_BORINGSSL ) ]
209+ {
210+ // SAFETY: ctx is a valid EVP_PKEY_CTX for the KEM operation.
211+ let res = unsafe { ffi:: EVP_PKEY_decapsulate_init ( ctx. as_ptr ( ) , std:: ptr:: null ( ) ) } ;
212+ cvt ( res) ?;
213+ }
139214
140- let ss_bytes: usize = match MlKemVariant :: from_pkey ( pkey) {
141- MlKemVariant :: MlKem768 | MlKemVariant :: MlKem1024 => 32 ,
142- } ;
215+ let ss_bytes: usize = 32 ;
143216 let mut shared_secret = vec ! [ 0u8 ; ss_bytes] ;
144217 let mut ss_len = ss_bytes;
145218 // SAFETY: ctx is a valid EVP_PKEY_CTX, buffers are correctly sized.
0 commit comments