dcrypt_kem/ecdh/k256/
mod.rs1use crate::error::Error as KemError;
19use dcrypt_algorithms::ec::k256 as ec_k256;
20use dcrypt_api::{
21 error::Error as ApiError,
22 traits::serialize::{Serialize, SerializeSecret},
23 Kem, Key as ApiKey, Result as ApiResult,
24};
25use dcrypt_common::security::SecretBuffer;
26use rand::{CryptoRng, RngCore};
27use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
28
29pub struct EcdhK256;
31
32#[derive(Clone, Zeroize)]
34pub struct EcdhK256PublicKey([u8; ec_k256::K256_POINT_COMPRESSED_SIZE]);
35
36impl AsRef<[u8]> for EcdhK256PublicKey {
37 fn as_ref(&self) -> &[u8] {
38 &self.0
39 }
40}
41
42impl AsMut<[u8]> for EcdhK256PublicKey {
43 fn as_mut(&mut self) -> &mut [u8] {
44 &mut self.0
45 }
46}
47
48#[derive(Clone, Zeroize, ZeroizeOnDrop)]
50pub struct EcdhK256SecretKey(SecretBuffer<{ ec_k256::K256_SCALAR_SIZE }>);
51
52impl AsRef<[u8]> for EcdhK256SecretKey {
53 fn as_ref(&self) -> &[u8] {
54 self.0.as_ref()
55 }
56}
57
58#[derive(Clone, Zeroize, ZeroizeOnDrop)]
60pub struct EcdhK256SharedSecret(ApiKey);
61
62impl AsRef<[u8]> for EcdhK256SharedSecret {
63 fn as_ref(&self) -> &[u8] {
64 self.0.as_ref()
65 }
66}
67
68#[derive(Clone)]
70pub struct EcdhK256Ciphertext([u8; ec_k256::K256_POINT_COMPRESSED_SIZE]);
71
72impl AsRef<[u8]> for EcdhK256Ciphertext {
73 fn as_ref(&self) -> &[u8] {
74 &self.0
75 }
76}
77
78impl AsMut<[u8]> for EcdhK256Ciphertext {
79 fn as_mut(&mut self) -> &mut [u8] {
80 &mut self.0
81 }
82}
83
84impl EcdhK256PublicKey {
86 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
87 if bytes.len() != ec_k256::K256_POINT_COMPRESSED_SIZE {
88 return Err(ApiError::InvalidLength {
89 context: "EcdhK256PublicKey::from_bytes",
90 expected: ec_k256::K256_POINT_COMPRESSED_SIZE,
91 actual: bytes.len(),
92 });
93 }
94 let point = ec_k256::Point::deserialize_compressed(bytes)
95 .map_err(|e| ApiError::from(KemError::from(e)))?;
96 if point.is_identity() {
97 return Err(ApiError::InvalidKey {
98 context: "EcdhK256PublicKey::from_bytes",
99 #[cfg(feature = "std")]
100 message: "Public key cannot be the identity point".to_string(),
101 });
102 }
103 let mut key_bytes = [0u8; ec_k256::K256_POINT_COMPRESSED_SIZE];
104 key_bytes.copy_from_slice(bytes);
105 Ok(Self(key_bytes))
106 }
107 pub fn to_bytes(&self) -> Vec<u8> {
108 self.0.to_vec()
109 }
110}
111
112impl Serialize for EcdhK256PublicKey {
113 fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
114 Self::from_bytes(bytes)
115 }
116 fn to_bytes(&self) -> Vec<u8> {
117 self.to_bytes()
118 }
119}
120
121impl EcdhK256SecretKey {
123 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
124 if bytes.len() != ec_k256::K256_SCALAR_SIZE {
125 return Err(ApiError::InvalidLength {
126 context: "EcdhK256SecretKey::from_bytes",
127 expected: ec_k256::K256_SCALAR_SIZE,
128 actual: bytes.len(),
129 });
130 }
131 let mut buffer_bytes = [0u8; ec_k256::K256_SCALAR_SIZE];
132 buffer_bytes.copy_from_slice(bytes);
133 let buffer = SecretBuffer::new(buffer_bytes);
134 let scalar = ec_k256::Scalar::from_secret_buffer(buffer.clone())
135 .map_err(|e| ApiError::from(KemError::from(e)))?;
136 drop(scalar);
137 Ok(Self(buffer))
138 }
139 pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
140 Zeroizing::new(self.0.as_ref().to_vec())
141 }
142}
143
144impl SerializeSecret for EcdhK256SecretKey {
145 fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
146 Self::from_bytes(bytes)
147 }
148 fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
149 self.to_bytes()
150 }
151}
152
153impl EcdhK256SharedSecret {
155 pub fn to_bytes(&self) -> Vec<u8> {
156 self.0.as_ref().to_vec()
157 }
158 pub fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
159 Zeroizing::new(self.0.as_ref().to_vec())
160 }
161}
162
163impl SerializeSecret for EcdhK256SharedSecret {
164 fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
165 Ok(Self(ApiKey::new(bytes)))
166 }
167 fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
168 self.to_bytes_zeroizing()
169 }
170}
171
172impl EcdhK256Ciphertext {
174 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
175 if bytes.len() != ec_k256::K256_POINT_COMPRESSED_SIZE {
176 return Err(ApiError::InvalidLength {
177 context: "EcdhK256Ciphertext::from_bytes",
178 expected: ec_k256::K256_POINT_COMPRESSED_SIZE,
179 actual: bytes.len(),
180 });
181 }
182 let point = ec_k256::Point::deserialize_compressed(bytes)
183 .map_err(|e| ApiError::from(KemError::from(e)))?;
184 if point.is_identity() {
185 return Err(ApiError::InvalidCiphertext {
186 context: "EcdhK256Ciphertext::from_bytes",
187 #[cfg(feature = "std")]
188 message: "Ephemeral public key cannot be the identity point".to_string(),
189 });
190 }
191 let mut ct_bytes = [0u8; ec_k256::K256_POINT_COMPRESSED_SIZE];
192 ct_bytes.copy_from_slice(bytes);
193 Ok(Self(ct_bytes))
194 }
195 pub fn to_bytes(&self) -> Vec<u8> {
196 self.0.to_vec()
197 }
198}
199
200impl Serialize for EcdhK256Ciphertext {
201 fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
202 Self::from_bytes(bytes)
203 }
204 fn to_bytes(&self) -> Vec<u8> {
205 self.to_bytes()
206 }
207}
208
209impl Kem for EcdhK256 {
210 type PublicKey = EcdhK256PublicKey;
211 type SecretKey = EcdhK256SecretKey;
212 type SharedSecret = EcdhK256SharedSecret;
213 type Ciphertext = EcdhK256Ciphertext;
214 type KeyPair = (Self::PublicKey, Self::SecretKey);
215
216 fn name() -> &'static str {
217 "ECDH-K256"
218 }
219
220 fn keypair<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Self::KeyPair> {
221 let (sk_scalar, pk_point) =
222 ec_k256::generate_keypair(rng).map_err(|e| ApiError::from(KemError::from(e)))?;
223 let public_key = EcdhK256PublicKey(pk_point.serialize_compressed());
224 let secret_key = EcdhK256SecretKey(sk_scalar.as_secret_buffer().clone());
225 Ok((public_key, secret_key))
226 }
227
228 fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
229 keypair.0.clone()
230 }
231
232 fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
233 keypair.1.clone()
234 }
235
236 fn encapsulate<R: CryptoRng + RngCore>(
237 rng: &mut R,
238 public_key_recipient: &Self::PublicKey,
239 ) -> ApiResult<(Self::Ciphertext, Self::SharedSecret)> {
240 let pk_r_point = ec_k256::Point::deserialize_compressed(&public_key_recipient.0)
241 .map_err(|e| ApiError::from(KemError::from(e)))?;
242 if pk_r_point.is_identity() {
243 return Err(ApiError::InvalidKey {
244 context: "ECDH-K256 encapsulate",
245 #[cfg(feature = "std")]
246 message: "Recipient public key cannot be the identity point".to_string(),
247 });
248 }
249 let (ephemeral_scalar, ephemeral_point) =
250 ec_k256::generate_keypair(rng).map_err(|e| ApiError::from(KemError::from(e)))?;
251 let ciphertext = EcdhK256Ciphertext(ephemeral_point.serialize_compressed());
252 let shared_point = ec_k256::scalar_mult(&ephemeral_scalar, &pk_r_point)
253 .map_err(|e| ApiError::from(KemError::from(e)))?;
254 if shared_point.is_identity() {
255 return Err(ApiError::DecryptionFailed {
256 context: "ECDH-K256 encapsulate",
257 #[cfg(feature = "std")]
258 message: "Shared point is the identity".to_string(),
259 });
260 }
261 let x_coord_bytes = shared_point.x_coordinate_bytes();
262 let mut kdf_ikm = Vec::with_capacity(
263 ec_k256::K256_FIELD_ELEMENT_SIZE + 2 * ec_k256::K256_POINT_COMPRESSED_SIZE,
264 );
265 kdf_ikm.extend_from_slice(&x_coord_bytes);
266 kdf_ikm.extend_from_slice(&ephemeral_point.serialize_compressed());
267 kdf_ikm.extend_from_slice(&public_key_recipient.0);
268 let info: Option<&[u8]> = Some(b"ECDH-K256-KEM");
269 let ss_bytes = ec_k256::kdf_hkdf_sha256_for_ecdh_kem(&kdf_ikm, info)
270 .map_err(|e| ApiError::from(KemError::from(e)))?;
271 let shared_secret = EcdhK256SharedSecret(ApiKey::new(&ss_bytes));
272 drop(ephemeral_scalar);
273 Ok((ciphertext, shared_secret))
274 }
275
276 fn decapsulate(
277 secret_key_recipient: &Self::SecretKey,
278 ciphertext_ephemeral_pk: &Self::Ciphertext,
279 ) -> ApiResult<Self::SharedSecret> {
280 let sk_r_scalar = ec_k256::Scalar::from_secret_buffer(secret_key_recipient.0.clone())
281 .map_err(|e| ApiError::from(KemError::from(e)))?;
282 let q_e_point = ec_k256::Point::deserialize_compressed(&ciphertext_ephemeral_pk.0)
283 .map_err(|e| ApiError::from(KemError::from(e)))?;
284 if q_e_point.is_identity() {
285 return Err(ApiError::InvalidCiphertext {
286 context: "ECDH-K256 decapsulate",
287 #[cfg(feature = "std")]
288 message: "Ephemeral public key cannot be the identity point".to_string(),
289 });
290 }
291 let shared_point = ec_k256::scalar_mult(&sk_r_scalar, &q_e_point)
292 .map_err(|e| ApiError::from(KemError::from(e)))?;
293 if shared_point.is_identity() {
294 return Err(ApiError::DecryptionFailed {
295 context: "ECDH-K256 decapsulate",
296 #[cfg(feature = "std")]
297 message: "Shared point is the identity".to_string(),
298 });
299 }
300 let x_coord_bytes = shared_point.x_coordinate_bytes();
301 let q_r_point = ec_k256::scalar_mult_base_g(&sk_r_scalar)
302 .map_err(|e| ApiError::from(KemError::from(e)))?;
303 let mut kdf_ikm = Vec::with_capacity(
304 ec_k256::K256_FIELD_ELEMENT_SIZE + 2 * ec_k256::K256_POINT_COMPRESSED_SIZE,
305 );
306 kdf_ikm.extend_from_slice(&x_coord_bytes);
307 kdf_ikm.extend_from_slice(&ciphertext_ephemeral_pk.0);
308 kdf_ikm.extend_from_slice(&q_r_point.serialize_compressed());
309 let info: Option<&[u8]> = Some(b"ECDH-K256-KEM");
310 let ss_bytes = ec_k256::kdf_hkdf_sha256_for_ecdh_kem(&kdf_ikm, info)
311 .map_err(|e| ApiError::from(KemError::from(e)))?;
312 let shared_secret = EcdhK256SharedSecret(ApiKey::new(&ss_bytes));
313 Ok(shared_secret)
314 }
315}
316
317#[cfg(test)]
318mod tests;