dcrypt_kem/ecdh/p521/
mod.rs1use crate::error::Error as KemError;
19use dcrypt_algorithms::ec::p521 as ec_p521;
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 EcdhP521;
31
32#[derive(Clone, Zeroize)]
34pub struct EcdhP521PublicKey([u8; ec_p521::P521_POINT_COMPRESSED_SIZE]);
35
36impl AsRef<[u8]> for EcdhP521PublicKey {
37 fn as_ref(&self) -> &[u8] {
38 &self.0
39 }
40}
41
42impl AsMut<[u8]> for EcdhP521PublicKey {
43 fn as_mut(&mut self) -> &mut [u8] {
44 &mut self.0
45 }
46}
47
48#[derive(Clone, Zeroize, ZeroizeOnDrop)]
50pub struct EcdhP521SecretKey(SecretBuffer<{ ec_p521::P521_SCALAR_SIZE }>);
51
52impl AsRef<[u8]> for EcdhP521SecretKey {
53 fn as_ref(&self) -> &[u8] {
54 self.0.as_ref()
55 }
56}
57
58#[derive(Clone, Zeroize, ZeroizeOnDrop)]
60pub struct EcdhP521SharedSecret(ApiKey);
61
62impl AsRef<[u8]> for EcdhP521SharedSecret {
63 fn as_ref(&self) -> &[u8] {
64 self.0.as_ref()
65 }
66}
67
68#[derive(Clone)]
70pub struct EcdhP521Ciphertext([u8; ec_p521::P521_POINT_COMPRESSED_SIZE]);
71
72impl AsRef<[u8]> for EcdhP521Ciphertext {
73 fn as_ref(&self) -> &[u8] {
74 &self.0
75 }
76}
77
78impl AsMut<[u8]> for EcdhP521Ciphertext {
79 fn as_mut(&mut self) -> &mut [u8] {
80 &mut self.0
81 }
82}
83
84impl EcdhP521PublicKey {
86 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
87 if bytes.len() != ec_p521::P521_POINT_COMPRESSED_SIZE {
88 return Err(ApiError::InvalidLength {
89 context: "EcdhP521PublicKey::from_bytes",
90 expected: ec_p521::P521_POINT_COMPRESSED_SIZE,
91 actual: bytes.len(),
92 });
93 }
94 let point = ec_p521::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: "EcdhP521PublicKey::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_p521::P521_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 EcdhP521PublicKey {
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 EcdhP521SecretKey {
123 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
124 if bytes.len() != ec_p521::P521_SCALAR_SIZE {
125 return Err(ApiError::InvalidLength {
126 context: "EcdhP521SecretKey::from_bytes",
127 expected: ec_p521::P521_SCALAR_SIZE,
128 actual: bytes.len(),
129 });
130 }
131 let mut buffer_bytes = [0u8; ec_p521::P521_SCALAR_SIZE];
132 buffer_bytes.copy_from_slice(bytes);
133 let buffer = SecretBuffer::new(buffer_bytes);
134 let scalar = ec_p521::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 EcdhP521SecretKey {
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 EcdhP521SharedSecret {
155 pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
156 Zeroizing::new(self.0.as_ref().to_vec())
157 }
158}
159
160impl SerializeSecret for EcdhP521SharedSecret {
161 fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
162 Ok(Self(ApiKey::new(bytes)))
163 }
164 fn to_bytes_zeroizing(&self) -> Zeroizing<Vec<u8>> {
165 self.to_bytes()
166 }
167}
168
169impl EcdhP521Ciphertext {
171 pub fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
172 if bytes.len() != ec_p521::P521_POINT_COMPRESSED_SIZE {
173 return Err(ApiError::InvalidLength {
174 context: "EcdhP521Ciphertext::from_bytes",
175 expected: ec_p521::P521_POINT_COMPRESSED_SIZE,
176 actual: bytes.len(),
177 });
178 }
179 let point = ec_p521::Point::deserialize_compressed(bytes)
180 .map_err(|e| ApiError::from(KemError::from(e)))?;
181 if point.is_identity() {
182 return Err(ApiError::InvalidCiphertext {
183 context: "EcdhP521Ciphertext::from_bytes",
184 #[cfg(feature = "std")]
185 message: "Ephemeral public key cannot be the identity point".to_string(),
186 });
187 }
188 let mut ct_bytes = [0u8; ec_p521::P521_POINT_COMPRESSED_SIZE];
189 ct_bytes.copy_from_slice(bytes);
190 Ok(Self(ct_bytes))
191 }
192 pub fn to_bytes(&self) -> Vec<u8> {
193 self.0.to_vec()
194 }
195}
196
197impl Serialize for EcdhP521Ciphertext {
198 fn from_bytes(bytes: &[u8]) -> ApiResult<Self> {
199 Self::from_bytes(bytes)
200 }
201 fn to_bytes(&self) -> Vec<u8> {
202 self.to_bytes()
203 }
204}
205
206impl Kem for EcdhP521 {
207 type PublicKey = EcdhP521PublicKey;
208 type SecretKey = EcdhP521SecretKey;
209 type SharedSecret = EcdhP521SharedSecret;
210 type Ciphertext = EcdhP521Ciphertext;
211 type KeyPair = (Self::PublicKey, Self::SecretKey);
212
213 fn name() -> &'static str {
214 "ECDH-P521"
215 }
216
217 fn keypair<R: CryptoRng + RngCore>(rng: &mut R) -> ApiResult<Self::KeyPair> {
218 let (sk_scalar, pk_point) =
219 ec_p521::generate_keypair(rng).map_err(|e| ApiError::from(KemError::from(e)))?;
220 let public_key = EcdhP521PublicKey(pk_point.serialize_compressed());
221 let secret_key = EcdhP521SecretKey(sk_scalar.as_secret_buffer().clone());
222 Ok((public_key, secret_key))
223 }
224
225 fn public_key(keypair: &Self::KeyPair) -> Self::PublicKey {
226 keypair.0.clone()
227 }
228
229 fn secret_key(keypair: &Self::KeyPair) -> Self::SecretKey {
230 keypair.1.clone()
231 }
232
233 fn encapsulate<R: CryptoRng + RngCore>(
234 rng: &mut R,
235 public_key_recipient: &Self::PublicKey,
236 ) -> ApiResult<(Self::Ciphertext, Self::SharedSecret)> {
237 let pk_r_point = ec_p521::Point::deserialize_compressed(&public_key_recipient.0)
238 .map_err(|e| ApiError::from(KemError::from(e)))?;
239 if pk_r_point.is_identity() {
240 return Err(ApiError::InvalidKey {
241 context: "ECDH-P521 encapsulate",
242 #[cfg(feature = "std")]
243 message: "Recipient public key cannot be the identity point".to_string(),
244 });
245 }
246 let (ephemeral_scalar, ephemeral_point) =
247 ec_p521::generate_keypair(rng).map_err(|e| ApiError::from(KemError::from(e)))?;
248 let ciphertext = EcdhP521Ciphertext(ephemeral_point.serialize_compressed());
249 let shared_point = ec_p521::scalar_mult(&ephemeral_scalar, &pk_r_point)
250 .map_err(|e| ApiError::from(KemError::from(e)))?;
251 if shared_point.is_identity() {
252 return Err(ApiError::DecryptionFailed {
253 context: "ECDH-P521 encapsulate",
254 #[cfg(feature = "std")]
255 message: "Shared point is the identity".to_string(),
256 });
257 }
258 let x_coord_bytes = shared_point.x_coordinate_bytes();
259 let mut kdf_ikm = Vec::with_capacity(
260 ec_p521::P521_FIELD_ELEMENT_SIZE + 2 * ec_p521::P521_POINT_COMPRESSED_SIZE,
261 );
262 kdf_ikm.extend_from_slice(&x_coord_bytes);
263 kdf_ikm.extend_from_slice(&ephemeral_point.serialize_compressed());
264 kdf_ikm.extend_from_slice(&public_key_recipient.0);
265 let info: Option<&[u8]> = Some(b"ECDH-P521-KEM");
266 let ss_bytes = ec_p521::kdf_hkdf_sha512_for_ecdh_kem(&kdf_ikm, info)
267 .map_err(|e| ApiError::from(KemError::from(e)))?;
268 let shared_secret = EcdhP521SharedSecret(ApiKey::new(&ss_bytes));
269 drop(ephemeral_scalar);
270 Ok((ciphertext, shared_secret))
271 }
272
273 fn decapsulate(
274 secret_key_recipient: &Self::SecretKey,
275 ciphertext_ephemeral_pk: &Self::Ciphertext,
276 ) -> ApiResult<Self::SharedSecret> {
277 let scalar_result = ec_p521::Scalar::from_secret_buffer(secret_key_recipient.0.clone());
278 let sk_r_scalar = match scalar_result {
279 Ok(scalar) => scalar,
280 Err(e) => return Err(ApiError::from(KemError::from(e))),
281 };
282 let q_e_point = ec_p521::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-P521 decapsulate",
287 #[cfg(feature = "std")]
288 message: "Ephemeral public key cannot be the identity point".to_string(),
289 });
290 }
291 let shared_point = ec_p521::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-P521 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_p521::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_p521::P521_FIELD_ELEMENT_SIZE + 2 * ec_p521::P521_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-P521-KEM");
310 let ss_bytes = ec_p521::kdf_hkdf_sha512_for_ecdh_kem(&kdf_ikm, info)
311 .map_err(|e| ApiError::from(KemError::from(e)))?;
312 let shared_secret = EcdhP521SharedSecret(ApiKey::new(&ss_bytes));
313 Ok(shared_secret)
314 }
315}
316
317#[cfg(test)]
318mod tests;