1use alloc::vec::Vec;
2use core::fmt;
3
4use rand::CryptoRng;
5
6use super::{
7 IesError, IesScheme, crypto_box::CryptoBox, ecgfp5_eidos::adapter, message::SealedMessage,
8};
9use crate::{
10 Felt,
11 aead::{aead_eidos::AeadEidos, xchacha::XChaCha},
12 dsa::{
13 ecdsa_k256_keccak::PUBLIC_KEY_BYTES as K256_PUBLIC_KEY_BYTES,
14 eddsa_25519_sha512::PUBLIC_KEY_BYTES as X25519_PUBLIC_KEY_BYTES,
15 },
16 ecdh::{KeyAgreementScheme, ecgfp5, k256::K256, x25519::X25519},
17 utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
18};
19
20type K256XChaCha20Poly1305 = CryptoBox<K256, XChaCha>;
25type X25519XChaCha20Poly1305 = CryptoBox<X25519, XChaCha>;
27type K256AeadEidos = CryptoBox<K256, AeadEidos>;
29type X25519AeadEidos = CryptoBox<X25519, AeadEidos>;
31
32macro_rules! impl_seal_bytes_with_associated_data {
37 ($($variant:path => $crypto_box:ty, $ephemeral_variant:path;)*) => {
38 pub fn seal_bytes_with_associated_data<R: CryptoRng>(
44 &self,
45 rng: &mut R,
46 plaintext: &[u8],
47 associated_data: &[u8],
48 ) -> Result<SealedMessage, IesError> {
49 match self {
50 SealingKey::Ecgfp5Eidos(key) => {
51 adapter::seal_bytes(key, rng, plaintext, associated_data)
52 },
53 $(
54 $variant(key) => {
55 let scheme = self.scheme();
56 let (ciphertext, ephemeral) = <$crypto_box>::seal_bytes_with_associated_data(
57 rng,
58 key,
59 scheme,
60 plaintext,
61 associated_data,
62 )?;
63
64 Ok(SealedMessage {
65 ephemeral_key: $ephemeral_variant(ephemeral),
66 ciphertext,
67 })
68 }
69 )*
70 }
71 }
72 };
73}
74
75macro_rules! impl_seal_elements_with_associated_data {
77 ($($variant:path => $crypto_box:ty, $ephemeral_variant:path;)*) => {
78 pub fn seal_elements_with_associated_data<R: CryptoRng>(
84 &self,
85 rng: &mut R,
86 plaintext: &[Felt],
87 associated_data: &[Felt],
88 ) -> Result<SealedMessage, IesError> {
89 match self {
90 SealingKey::Ecgfp5Eidos(key) => {
91 adapter::seal_elements(key, rng, plaintext, associated_data)
92 },
93 $(
94 $variant(key) => {
95 let scheme = self.scheme();
96 let (ciphertext, ephemeral) = <$crypto_box>::seal_elements_with_associated_data(
97 rng,
98 key,
99 scheme,
100 plaintext,
101 associated_data,
102 )?;
103
104 Ok(SealedMessage {
105 ephemeral_key: $ephemeral_variant(ephemeral),
106 ciphertext,
107 })
108 }
109 )*
110 }
111 }
112 };
113}
114
115macro_rules! impl_unseal_bytes_with_associated_data {
117 ($($variant:path => $crypto_box:ty, $ephemeral_variant:path;)*) => {
118 pub fn unseal_bytes_with_associated_data(
127 &self,
128 sealed_message: SealedMessage,
129 associated_data: &[u8],
130 ) -> Result<Vec<u8>, IesError> {
131 let self_algo = self.scheme() as u8;
132 let msg_algo = sealed_message.ephemeral_key.scheme() as u8;
133
134 let compatible = self_algo == msg_algo;
135 if !compatible {
136 return Err(IesError::SchemeMismatch);
137 }
138
139 let SealedMessage { ephemeral_key, ciphertext } = sealed_message;
140
141 match (self, ephemeral_key) {
142 (UnsealingKey::Ecgfp5Eidos(key), EphemeralPublicKey::Ecgfp5Eidos(ephemeral)) => {
143 adapter::open_bytes(key, ephemeral, &ciphertext, associated_data)
144 },
145 $(
146 ($variant(key), $ephemeral_variant(ephemeral)) => {
147 <$crypto_box>::unseal_bytes_with_associated_data(
148 key,
149 &ephemeral,
150 self.scheme(),
151 &ciphertext,
152 associated_data,
153 )
154 }
155 )*
156 _ => Err(IesError::SchemeMismatch),
157 }
158 }
159 };
160}
161
162macro_rules! impl_unseal_elements_with_associated_data {
164 ($($variant:path => $crypto_box:ty, $ephemeral_variant:path;)*) => {
165 pub fn unseal_elements_with_associated_data(
174 &self,
175 sealed_message: SealedMessage,
176 associated_data: &[Felt],
177 ) -> Result<Vec<Felt>, IesError> {
178 let self_algo = self.scheme() as u8;
179 let msg_algo = sealed_message.ephemeral_key.scheme() as u8;
180
181 let compatible = self_algo == msg_algo;
182 if !compatible {
183 return Err(IesError::SchemeMismatch);
184 }
185
186 let SealedMessage { ephemeral_key, ciphertext } = sealed_message;
187
188 match (self, ephemeral_key) {
189 (UnsealingKey::Ecgfp5Eidos(key), EphemeralPublicKey::Ecgfp5Eidos(ephemeral)) => {
190 adapter::open_elements(key, ephemeral, &ciphertext, associated_data)
191 },
192 $(
193 ($variant(key), $ephemeral_variant(ephemeral)) => {
194 <$crypto_box>::unseal_elements_with_associated_data(
195 key,
196 &ephemeral,
197 self.scheme(),
198 &ciphertext,
199 associated_data,
200 )
201 }
202 )*
203 _ => Err(IesError::SchemeMismatch),
204 }
205 }
206 };
207}
208
209#[derive(Debug, Clone, PartialEq, Eq)]
214#[cfg_attr(
215 all(feature = "arbitrary", test),
216 miden_test_serialization_macros::serialization_test
217)]
218pub enum SealingKey {
219 K256XChaCha20Poly1305(crate::dsa::ecdsa_k256_keccak::PublicKey),
220 X25519XChaCha20Poly1305(crate::dsa::eddsa_25519_sha512::PublicKey),
221 K256AeadEidos(crate::dsa::ecdsa_k256_keccak::PublicKey),
222 X25519AeadEidos(crate::dsa::eddsa_25519_sha512::PublicKey),
223 Ecgfp5Eidos(ecgfp5::PublicKey),
224}
225
226impl SealingKey {
227 pub fn scheme(&self) -> IesScheme {
229 match self {
230 SealingKey::K256XChaCha20Poly1305(_) => IesScheme::K256XChaCha20Poly1305,
231 SealingKey::X25519XChaCha20Poly1305(_) => IesScheme::X25519XChaCha20Poly1305,
232 SealingKey::K256AeadEidos(_) => IesScheme::K256AeadEidos,
233 SealingKey::X25519AeadEidos(_) => IesScheme::X25519AeadEidos,
234 SealingKey::Ecgfp5Eidos(_) => IesScheme::Ecgfp5Eidos,
235 }
236 }
237
238 pub fn seal_bytes<R: CryptoRng>(
243 &self,
244 rng: &mut R,
245 plaintext: &[u8],
246 ) -> Result<SealedMessage, IesError> {
247 self.seal_bytes_with_associated_data(rng, plaintext, &[])
248 }
249
250 impl_seal_bytes_with_associated_data! {
251 SealingKey::K256XChaCha20Poly1305 => K256XChaCha20Poly1305, EphemeralPublicKey::K256XChaCha20Poly1305;
252 SealingKey::X25519XChaCha20Poly1305 => X25519XChaCha20Poly1305, EphemeralPublicKey::X25519XChaCha20Poly1305;
253 SealingKey::K256AeadEidos => K256AeadEidos, EphemeralPublicKey::K256AeadEidos;
254 SealingKey::X25519AeadEidos => X25519AeadEidos, EphemeralPublicKey::X25519AeadEidos;
255 }
256
257 pub fn seal_elements<R: CryptoRng>(
262 &self,
263 rng: &mut R,
264 plaintext: &[Felt],
265 ) -> Result<SealedMessage, IesError> {
266 self.seal_elements_with_associated_data(rng, plaintext, &[])
267 }
268
269 impl_seal_elements_with_associated_data! {
270 SealingKey::K256XChaCha20Poly1305 => K256XChaCha20Poly1305, EphemeralPublicKey::K256XChaCha20Poly1305;
271 SealingKey::X25519XChaCha20Poly1305 => X25519XChaCha20Poly1305, EphemeralPublicKey::X25519XChaCha20Poly1305;
272 SealingKey::K256AeadEidos => K256AeadEidos, EphemeralPublicKey::K256AeadEidos;
273 SealingKey::X25519AeadEidos => X25519AeadEidos, EphemeralPublicKey::X25519AeadEidos;
274 }
275}
276
277impl fmt::Display for SealingKey {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 write!(f, "{} sealing key", self.scheme())
280 }
281}
282
283impl Serializable for SealingKey {
284 fn write_into<W: ByteWriter>(&self, target: &mut W) {
285 target.write_u8(self.scheme().into());
286
287 match self {
288 SealingKey::K256XChaCha20Poly1305(key) => key.write_into(target),
289 SealingKey::X25519XChaCha20Poly1305(key) => key.write_into(target),
290 SealingKey::K256AeadEidos(key) => key.write_into(target),
291 SealingKey::X25519AeadEidos(key) => key.write_into(target),
292 SealingKey::Ecgfp5Eidos(key) => key.write_into(target),
293 }
294 }
295}
296
297impl Deserializable for SealingKey {
298 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
299 let scheme = IesScheme::try_from(source.read_u8()?)
300 .map_err(|_| DeserializationError::InvalidValue("Unsupported IES scheme".into()))?;
301
302 match scheme {
303 IesScheme::K256XChaCha20Poly1305 => {
304 let key = crate::dsa::ecdsa_k256_keccak::PublicKey::read_from(source)?;
305 Ok(SealingKey::K256XChaCha20Poly1305(key))
306 },
307 IesScheme::X25519XChaCha20Poly1305 => {
308 let key = crate::dsa::eddsa_25519_sha512::PublicKey::read_from(source)?;
309 Ok(SealingKey::X25519XChaCha20Poly1305(key))
310 },
311 IesScheme::K256AeadEidos => {
312 let key = crate::dsa::ecdsa_k256_keccak::PublicKey::read_from(source)?;
313 Ok(SealingKey::K256AeadEidos(key))
314 },
315 IesScheme::X25519AeadEidos => {
316 let key = crate::dsa::eddsa_25519_sha512::PublicKey::read_from(source)?;
317 Ok(SealingKey::X25519AeadEidos(key))
318 },
319 IesScheme::Ecgfp5Eidos => {
320 Ok(SealingKey::Ecgfp5Eidos(ecgfp5::PublicKey::read_from(source)?))
321 },
322 }
323 }
324}
325
326pub enum UnsealingKey {
331 K256XChaCha20Poly1305(crate::dsa::ecdsa_k256_keccak::KeyExchangeKey),
332 X25519XChaCha20Poly1305(crate::dsa::eddsa_25519_sha512::KeyExchangeKey),
333 K256AeadEidos(crate::dsa::ecdsa_k256_keccak::KeyExchangeKey),
334 X25519AeadEidos(crate::dsa::eddsa_25519_sha512::KeyExchangeKey),
335 Ecgfp5Eidos(ecgfp5::SecretKey),
336}
337
338impl UnsealingKey {
339 pub fn scheme(&self) -> IesScheme {
341 match self {
342 UnsealingKey::K256XChaCha20Poly1305(_) => IesScheme::K256XChaCha20Poly1305,
343 UnsealingKey::X25519XChaCha20Poly1305(_) => IesScheme::X25519XChaCha20Poly1305,
344 UnsealingKey::K256AeadEidos(_) => IesScheme::K256AeadEidos,
345 UnsealingKey::X25519AeadEidos(_) => IesScheme::X25519AeadEidos,
346 UnsealingKey::Ecgfp5Eidos(_) => IesScheme::Ecgfp5Eidos,
347 }
348 }
349
350 pub fn scheme_name(&self) -> &'static str {
352 self.scheme().name()
353 }
354
355 pub fn unseal_bytes(&self, sealed_message: SealedMessage) -> Result<Vec<u8>, IesError> {
360 self.unseal_bytes_with_associated_data(sealed_message, &[])
361 }
362
363 impl_unseal_bytes_with_associated_data! {
364 UnsealingKey::K256XChaCha20Poly1305 => K256XChaCha20Poly1305, EphemeralPublicKey::K256XChaCha20Poly1305;
365 UnsealingKey::X25519XChaCha20Poly1305 => X25519XChaCha20Poly1305, EphemeralPublicKey::X25519XChaCha20Poly1305;
366 UnsealingKey::K256AeadEidos => K256AeadEidos, EphemeralPublicKey::K256AeadEidos;
367 UnsealingKey::X25519AeadEidos => X25519AeadEidos, EphemeralPublicKey::X25519AeadEidos;
368 }
369
370 pub fn unseal_elements(&self, sealed_message: SealedMessage) -> Result<Vec<Felt>, IesError> {
375 self.unseal_elements_with_associated_data(sealed_message, &[])
376 }
377
378 impl_unseal_elements_with_associated_data! {
379 UnsealingKey::K256XChaCha20Poly1305 => K256XChaCha20Poly1305, EphemeralPublicKey::K256XChaCha20Poly1305;
380 UnsealingKey::X25519XChaCha20Poly1305 => X25519XChaCha20Poly1305, EphemeralPublicKey::X25519XChaCha20Poly1305;
381 UnsealingKey::K256AeadEidos => K256AeadEidos, EphemeralPublicKey::K256AeadEidos;
382 UnsealingKey::X25519AeadEidos => X25519AeadEidos, EphemeralPublicKey::X25519AeadEidos;
383 }
384}
385
386impl fmt::Display for UnsealingKey {
387 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388 write!(f, "{} unsealing key", self.scheme())
389 }
390}
391
392impl Serializable for UnsealingKey {
393 fn write_into<W: ByteWriter>(&self, target: &mut W) {
394 target.write_u8(self.scheme().into());
395
396 match self {
397 UnsealingKey::K256XChaCha20Poly1305(key) => key.write_into(target),
398 UnsealingKey::X25519XChaCha20Poly1305(key) => key.write_into(target),
399 UnsealingKey::K256AeadEidos(key) => key.write_into(target),
400 UnsealingKey::X25519AeadEidos(key) => key.write_into(target),
401 UnsealingKey::Ecgfp5Eidos(key) => key.write_into(target),
402 }
403 }
404}
405
406impl Deserializable for UnsealingKey {
407 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
408 let scheme = IesScheme::try_from(source.read_u8()?)
409 .map_err(|_| DeserializationError::InvalidValue("Unsupported IES scheme".into()))?;
410
411 match scheme {
412 IesScheme::K256XChaCha20Poly1305 => {
413 let key = crate::dsa::ecdsa_k256_keccak::KeyExchangeKey::read_from(source)?;
414 Ok(UnsealingKey::K256XChaCha20Poly1305(key))
415 },
416 IesScheme::X25519XChaCha20Poly1305 => {
417 let key = crate::dsa::eddsa_25519_sha512::KeyExchangeKey::read_from(source)?;
418 Ok(UnsealingKey::X25519XChaCha20Poly1305(key))
419 },
420 IesScheme::K256AeadEidos => {
421 let key = crate::dsa::ecdsa_k256_keccak::KeyExchangeKey::read_from(source)?;
422 Ok(UnsealingKey::K256AeadEidos(key))
423 },
424 IesScheme::X25519AeadEidos => {
425 let key = crate::dsa::eddsa_25519_sha512::KeyExchangeKey::read_from(source)?;
426 Ok(UnsealingKey::X25519AeadEidos(key))
427 },
428 IesScheme::Ecgfp5Eidos => {
429 Ok(UnsealingKey::Ecgfp5Eidos(ecgfp5::SecretKey::read_from(source)?))
430 },
431 }
432 }
433}
434
435#[derive(Debug, Clone, PartialEq, Eq)]
440pub(super) enum EphemeralPublicKey {
441 K256XChaCha20Poly1305(crate::ecdh::k256::EphemeralPublicKey),
442 X25519XChaCha20Poly1305(crate::ecdh::x25519::EphemeralPublicKey),
443 K256AeadEidos(crate::ecdh::k256::EphemeralPublicKey),
444 X25519AeadEidos(crate::ecdh::x25519::EphemeralPublicKey),
445 Ecgfp5Eidos(ecgfp5::PublicKey),
446}
447
448impl EphemeralPublicKey {
449 pub fn scheme(&self) -> IesScheme {
451 match self {
452 EphemeralPublicKey::K256XChaCha20Poly1305(_) => IesScheme::K256XChaCha20Poly1305,
453 EphemeralPublicKey::X25519XChaCha20Poly1305(_) => IesScheme::X25519XChaCha20Poly1305,
454 EphemeralPublicKey::K256AeadEidos(_) => IesScheme::K256AeadEidos,
455 EphemeralPublicKey::X25519AeadEidos(_) => IesScheme::X25519AeadEidos,
456 EphemeralPublicKey::Ecgfp5Eidos(_) => IesScheme::Ecgfp5Eidos,
457 }
458 }
459
460 pub fn to_bytes(&self) -> Vec<u8> {
462 match self {
463 EphemeralPublicKey::K256XChaCha20Poly1305(key) => key.to_bytes(),
464 EphemeralPublicKey::X25519XChaCha20Poly1305(key) => key.to_bytes(),
465 EphemeralPublicKey::K256AeadEidos(key) => key.to_bytes(),
466 EphemeralPublicKey::X25519AeadEidos(key) => key.to_bytes(),
467 EphemeralPublicKey::Ecgfp5Eidos(key) => key.to_bytes().to_vec(),
468 }
469 }
470
471 pub fn from_bytes(scheme: IesScheme, bytes: &[u8]) -> Result<Self, IesError> {
473 let expected_len = match scheme {
474 IesScheme::K256XChaCha20Poly1305 | IesScheme::K256AeadEidos => K256_PUBLIC_KEY_BYTES,
475 IesScheme::X25519XChaCha20Poly1305 | IesScheme::X25519AeadEidos => {
476 X25519_PUBLIC_KEY_BYTES
477 },
478 IesScheme::Ecgfp5Eidos => 40,
479 };
480
481 if bytes.len() != expected_len {
482 return Err(IesError::EphemeralPublicKeyDeserializationFailed);
483 }
484
485 match scheme {
486 IesScheme::Ecgfp5Eidos => {
487 let bytes = bytes
488 .try_into()
489 .map_err(|_| IesError::EphemeralPublicKeyDeserializationFailed)?;
490 let key = ecgfp5::PublicKey::from_bytes(bytes)
491 .map_err(|_| IesError::EphemeralPublicKeyDeserializationFailed)?;
492 Ok(EphemeralPublicKey::Ecgfp5Eidos(key))
493 },
494 IesScheme::K256XChaCha20Poly1305 => {
495 let key =
496 <K256 as KeyAgreementScheme>::EphemeralPublicKey::read_from_bytes_with_budget(
497 bytes,
498 expected_len,
499 )
500 .map_err(|_| IesError::EphemeralPublicKeyDeserializationFailed)?;
501 Ok(EphemeralPublicKey::K256XChaCha20Poly1305(key))
502 },
503 IesScheme::K256AeadEidos => {
504 let key =
505 <K256 as KeyAgreementScheme>::EphemeralPublicKey::read_from_bytes_with_budget(
506 bytes,
507 expected_len,
508 )
509 .map_err(|_| IesError::EphemeralPublicKeyDeserializationFailed)?;
510 Ok(EphemeralPublicKey::K256AeadEidos(key))
511 },
512 IesScheme::X25519XChaCha20Poly1305 => {
513 let key =
514 <X25519 as KeyAgreementScheme>::EphemeralPublicKey::read_from_bytes_with_budget(
515 bytes,
516 expected_len,
517 )
518 .map_err(|_| IesError::EphemeralPublicKeyDeserializationFailed)?;
519 Ok(EphemeralPublicKey::X25519XChaCha20Poly1305(key))
520 },
521 IesScheme::X25519AeadEidos => {
522 let key =
523 <X25519 as KeyAgreementScheme>::EphemeralPublicKey::read_from_bytes_with_budget(
524 bytes,
525 expected_len,
526 )
527 .map_err(|_| IesError::EphemeralPublicKeyDeserializationFailed)?;
528 Ok(EphemeralPublicKey::X25519AeadEidos(key))
529 },
530 }
531 }
532}
533
534#[cfg(any(test, feature = "arbitrary"))]
538mod key_arbitrary {
539 use proptest::prelude::*;
540 use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
541
542 use super::{EphemeralPublicKey, SealingKey};
543 use crate::dsa::{
544 ecdsa_k256_keccak::KeyExchangeKey as K256KeyExchangeKey,
545 eddsa_25519_sha512::PublicKey as Ed25519PublicKey,
546 };
547
548 fn seed32(a: u64) -> [u8; 32] {
549 let mut seed = [0u8; 32];
550 seed[..8].copy_from_slice(&a.to_be_bytes());
551 seed
552 }
553
554 fn k256_key_exchange_key(seed: [u8; 32]) -> K256KeyExchangeKey {
555 let mut rng = ChaCha20Rng::from_seed(seed);
556 K256KeyExchangeKey::with_rng(&mut rng)
557 }
558
559 fn k256_public_key(seed: [u8; 32]) -> crate::dsa::ecdsa_k256_keccak::PublicKey {
560 k256_key_exchange_key(seed).public_key()
561 }
562
563 fn ecgfp5_public_key(seed: [u8; 32]) -> crate::ecdh::ecgfp5::PublicKey {
564 let mut rng = ChaCha20Rng::from_seed(seed);
565 crate::ecdh::ecgfp5::SecretKey::with_rng(&mut rng).public_key()
566 }
567
568 impl Arbitrary for SealingKey {
569 type Parameters = ();
570 type Strategy = BoxedStrategy<Self>;
571
572 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
573 prop_oneof![
575 (0u64..)
576 .prop_map(|a| Self::K256XChaCha20Poly1305(k256_public_key(seed32(a))))
577 .boxed(),
578 any::<Ed25519PublicKey>().prop_map(Self::X25519XChaCha20Poly1305).boxed(),
579 (0u64..).prop_map(|a| Self::K256AeadEidos(k256_public_key(seed32(a)))).boxed(),
580 any::<Ed25519PublicKey>().prop_map(Self::X25519AeadEidos).boxed(),
581 (0u64..).prop_map(|a| Self::Ecgfp5Eidos(ecgfp5_public_key(seed32(a)))).boxed(),
582 ]
583 .boxed()
584 }
585 }
586
587 impl Arbitrary for EphemeralPublicKey {
588 type Parameters = ();
589 type Strategy = BoxedStrategy<Self>;
590
591 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
592 prop_oneof![
593 any::<crate::ecdh::k256::EphemeralPublicKey>()
594 .prop_map(Self::K256XChaCha20Poly1305)
595 .boxed(),
596 any::<crate::ecdh::x25519::EphemeralPublicKey>()
597 .prop_map(Self::X25519XChaCha20Poly1305)
598 .boxed(),
599 any::<crate::ecdh::k256::EphemeralPublicKey>()
600 .prop_map(Self::K256AeadEidos)
601 .boxed(),
602 any::<crate::ecdh::x25519::EphemeralPublicKey>()
603 .prop_map(Self::X25519AeadEidos)
604 .boxed(),
605 (0u64..).prop_map(|a| Self::Ecgfp5Eidos(ecgfp5_public_key(seed32(a)))).boxed(),
606 ]
607 .boxed()
608 }
609 }
610}