1use rand_core::{CryptoRng, CryptoRngCore, OsRng, RngCore};
36
37use crate::asn1::ObjectIdentifier;
38use crate::constants::{AES_GCM_NONCE_SIZE, AES_GCM_TAG_SIZE, EC_PUBKEY_COMPRESSED_SIZE, TIGHTBEAM_ECIES_KDF_INFO};
39use crate::der::oid::AssociatedOid;
40
41use crate::crypto::aead::{Aead, Aes256Gcm, KeyInit, Payload};
42use crate::crypto::kdf::{ecies_kdf, HkdfSha3_256, KdfError};
43use crate::crypto::secret::{Secret, SecretSlice};
44use crate::crypto::sign::ecdsa::k256::ecdh::EphemeralSecret;
45use crate::crypto::sign::ecdsa::k256::elliptic_curve::sec1::ToEncodedPoint;
46use crate::crypto::sign::ecdsa::k256::{PublicKey, SecretKey};
47
48struct RngWrapper<'a>(&'a mut dyn CryptoRngCore);
59
60impl RngCore for RngWrapper<'_> {
61 fn next_u32(&mut self) -> u32 {
62 self.0.next_u32()
63 }
64
65 fn next_u64(&mut self) -> u64 {
66 self.0.next_u64()
67 }
68
69 fn fill_bytes(&mut self, dest: &mut [u8]) {
70 self.0.fill_bytes(dest)
71 }
72
73 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> core::result::Result<(), rand_core::Error> {
74 self.0.try_fill_bytes(dest)
75 }
76}
77
78impl CryptoRng for RngWrapper<'_> {}
79
80pub trait EciesPublicKeyOps: Clone + PartialEq + Eq {
85 type SecretKey: EciesSecretKeyOps<PublicKey = Self>;
87
88 const PUBLIC_KEY_SIZE: usize;
90
91 fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self>
93 where
94 Self: Sized;
95
96 fn to_bytes(&self) -> Vec<u8>;
98}
99
100pub trait EciesSecretKeyOps: Clone {
102 type PublicKey: EciesPublicKeyOps;
104
105 const SECRET_KEY_SIZE: usize;
107
108 fn random<R: CryptoRng + RngCore>(rng: &mut R) -> Self;
110
111 fn public_key(&self) -> Self::PublicKey;
113
114 fn diffie_hellman(&self, public_key: &Self::PublicKey) -> SecretSlice<u8>;
116}
117
118pub trait EciesEphemeral {
120 type PublicKey: EciesPublicKeyOps;
122
123 fn generate_ephemeral(
125 recipient_pubkey: &Self::PublicKey,
126 rng: &mut dyn rand_core::CryptoRngCore,
127 ) -> Result<(Vec<u8>, SecretSlice<u8>)>;
128}
129
130#[cfg(feature = "derive")]
135use crate::Errorizable;
136
137#[cfg_attr(feature = "derive", derive(Errorizable))]
139#[derive(Debug, Clone)]
140pub enum EciesError {
141 #[cfg_attr(feature = "derive", error("Invalid ECIES ciphertext format"))]
143 InvalidCiphertext,
144
145 #[cfg_attr(feature = "derive", error("Invalid ECIES public key: {0}"))]
147 InvalidPublicKey(crate::crypto::sign::ecdsa::k256::elliptic_curve::Error),
148
149 #[cfg_attr(feature = "derive", error("Invalid ECIES secret key: {0}"))]
151 InvalidSecretKey(crate::crypto::sign::ecdsa::k256::elliptic_curve::Error),
152
153 #[cfg_attr(feature = "derive", error("ECIES encryption failed: {0}"))]
155 EncryptionFailed(crate::crypto::aead::Error),
156
157 #[cfg_attr(feature = "derive", error("ECIES decryption failed: {0}"))]
159 DecryptionFailed(crate::crypto::aead::Error),
160
161 #[cfg_attr(feature = "derive", error("ECIES key derivation failed: {0}"))]
163 #[cfg_attr(feature = "derive", from)]
164 Kdf(#[cfg_attr(feature = "derive", from)] KdfError),
165}
166
167crate::impl_error_display!(EciesError {
168 InvalidCiphertext => "Invalid ECIES ciphertext format",
169 InvalidPublicKey(e) => "Invalid ECIES public key: {e}",
170 InvalidSecretKey(e) => "Invalid ECIES secret key: {e}",
171 EncryptionFailed(e) => "ECIES encryption failed: {e}",
172 DecryptionFailed(e) => "ECIES decryption failed: {e}",
173 Kdf(e) => "ECIES key derivation failed: {e}",
174});
175
176pub type Result<T> = core::result::Result<T, EciesError>;
178
179impl EciesPublicKeyOps for PublicKey {
184 type SecretKey = SecretKey;
185
186 const PUBLIC_KEY_SIZE: usize = EC_PUBKEY_COMPRESSED_SIZE;
187
188 fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self> {
189 PublicKey::from_sec1_bytes(bytes.as_ref()).map_err(EciesError::InvalidPublicKey)
190 }
191
192 fn to_bytes(&self) -> Vec<u8> {
193 let point = self.to_encoded_point(true);
194 point.as_bytes().to_vec()
195 }
196}
197
198impl EciesSecretKeyOps for SecretKey {
199 type PublicKey = PublicKey;
200
201 const SECRET_KEY_SIZE: usize = 32;
202
203 fn random<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
204 SecretKey::random(rng)
205 }
206
207 fn public_key(&self) -> Self::PublicKey {
208 SecretKey::public_key(self)
209 }
210
211 fn diffie_hellman(&self, public_key: &Self::PublicKey) -> SecretSlice<u8> {
212 let shared_secret = k256::ecdh::diffie_hellman(self.to_nonzero_scalar(), public_key.as_affine());
213 let v = shared_secret.raw_secret_bytes().to_vec().into_boxed_slice();
214 Secret::from(v)
215 }
216}
217
218impl core::convert::TryFrom<SecretSlice<u8>> for SecretKey {
219 type Error = EciesError;
220 fn try_from(bytes: SecretSlice<u8>) -> Result<Self> {
221 use crate::crypto::secret::ToInsecure;
222 let raw = bytes.to_insecure().map_err(EciesError::from)?;
223 SecretKey::from_slice(&raw).map_err(EciesError::InvalidSecretKey)
224 }
225}
226
227impl From<&SecretKey> for SecretSlice<u8> {
228 fn from(sk: &SecretKey) -> Self {
229 let v = SecretKey::to_bytes(sk).to_vec();
230 Secret::from(v.into_boxed_slice())
231 }
232}
233
234impl EciesEphemeral for SecretKey {
235 type PublicKey = PublicKey;
236
237 fn generate_ephemeral(
238 recipient_pubkey: &Self::PublicKey,
239 rng: &mut dyn CryptoRngCore,
240 ) -> Result<(Vec<u8>, SecretSlice<u8>)> {
241 let mut wrapper = RngWrapper(rng);
242 let ephemeral_secret = EphemeralSecret::random(&mut wrapper);
243 let ephemeral_pubkey = ephemeral_secret.public_key();
244
245 let shared_secret = ephemeral_secret.diffie_hellman(recipient_pubkey);
247
248 let ephemeral_point = ephemeral_pubkey.to_encoded_point(true);
249 let ephemeral_bytes = ephemeral_point.as_bytes().to_vec();
250 let shared_bytes = Secret::from(shared_secret.raw_secret_bytes().to_vec().into_boxed_slice());
251 Ok((ephemeral_bytes, shared_bytes))
252 }
253}
254
255pub trait EciesMessageOps: Sized {
257 const PUBKEY_SIZE: usize;
259
260 fn from_bytes(bytes: &[u8]) -> Result<Self>;
262
263 fn to_bytes(&self) -> Vec<u8>;
265
266 fn ephemeral_pubkey(&self) -> &[u8];
268
269 fn ciphertext(&self) -> &[u8];
271}
272
273pub struct Secp256k1EciesMessage {
281 ephemeral_pubkey: Vec<u8>,
283 ciphertext: Vec<u8>,
285}
286
287impl Secp256k1EciesMessage {
288 const MIN_CIPHERTEXT_SIZE: usize = AES_GCM_NONCE_SIZE + AES_GCM_TAG_SIZE;
290
291 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self> {
293 let bytes = bytes.as_ref();
294 if bytes.len() < EC_PUBKEY_COMPRESSED_SIZE + Self::MIN_CIPHERTEXT_SIZE {
295 return Err(EciesError::InvalidCiphertext);
296 }
297
298 let ephemeral_pubkey = bytes[0..EC_PUBKEY_COMPRESSED_SIZE].to_vec();
299 let ciphertext = bytes[EC_PUBKEY_COMPRESSED_SIZE..].to_vec();
300 if ciphertext.len() < Self::MIN_CIPHERTEXT_SIZE {
301 return Err(EciesError::InvalidCiphertext);
302 }
303
304 Ok(Self { ephemeral_pubkey, ciphertext })
305 }
306
307 pub fn to_bytes(&self) -> Vec<u8> {
309 let mut bytes = Vec::with_capacity(self.ephemeral_pubkey.len() + self.ciphertext.len());
310 bytes.extend_from_slice(&self.ephemeral_pubkey);
311 bytes.extend_from_slice(&self.ciphertext);
312 bytes
313 }
314
315 #[cfg(test)]
317 pub(crate) fn ephemeral_pubkey_mut(&mut self) -> &mut Vec<u8> {
318 &mut self.ephemeral_pubkey
319 }
320
321 #[cfg(test)]
323 pub(crate) fn ciphertext_mut(&mut self) -> &mut Vec<u8> {
324 &mut self.ciphertext
325 }
326}
327
328impl EciesMessageOps for Secp256k1EciesMessage {
329 const PUBKEY_SIZE: usize = EC_PUBKEY_COMPRESSED_SIZE;
330
331 fn from_bytes(bytes: &[u8]) -> Result<Self> {
332 Self::from_bytes(bytes)
333 }
334
335 fn to_bytes(&self) -> Vec<u8> {
336 Self::to_bytes(self)
337 }
338
339 fn ephemeral_pubkey(&self) -> &[u8] {
340 &self.ephemeral_pubkey
341 }
342
343 fn ciphertext(&self) -> &[u8] {
344 &self.ciphertext
345 }
346}
347
348pub fn encrypt<PK, P, R, M>(
365 recipient_pubkey: &PK,
366 plaintext: P,
367 associated_data: Option<&[u8]>,
368 rng: Option<&mut R>,
369) -> Result<M>
370where
371 PK: EciesPublicKeyOps,
372 PK::SecretKey: EciesEphemeral<PublicKey = PK>,
373 P: AsRef<[u8]>,
374 R: CryptoRng + RngCore,
375 M: EciesMessageOps,
376{
377 let plaintext = plaintext.as_ref();
378
379 macro_rules! do_encrypt {
381 ($rng:expr) => {{
382 let (ephemeral_bytes, shared_secret) = PK::SecretKey::generate_ephemeral(recipient_pubkey, $rng)?;
384
385 let k_enc = ecies_kdf::<HkdfSha3_256>(&ephemeral_bytes, shared_secret, TIGHTBEAM_ECIES_KDF_INFO, None)?;
387
388 let key = crate::crypto::utils::key_from_slice(&k_enc[..32]);
390 let cipher = Aes256Gcm::new(&key);
391
392 let mut nonce_bytes = [0u8; AES_GCM_NONCE_SIZE];
394 $rng.fill_bytes(&mut nonce_bytes);
395 let nonce = crate::crypto::utils::nonce_from_slice::<Aes256Gcm>(&nonce_bytes);
396
397 let payload = match associated_data {
399 Some(aad) => Payload { msg: plaintext, aad },
400 None => Payload { msg: plaintext, aad: b"" },
401 };
402
403 let ciphertext = cipher.encrypt(&nonce, payload).map_err(EciesError::EncryptionFailed)?;
405 let encrypted_len = ciphertext.len();
406 let mut final_ciphertext = Vec::with_capacity(AES_GCM_NONCE_SIZE + encrypted_len);
407 final_ciphertext.extend_from_slice(&nonce_bytes);
408 final_ciphertext.extend_from_slice(&ciphertext);
409
410 let total_len = ephemeral_bytes.len() + final_ciphertext.len();
412 let mut wire_bytes = Vec::with_capacity(total_len);
413 wire_bytes.extend_from_slice(&ephemeral_bytes);
414 wire_bytes.extend_from_slice(&final_ciphertext);
415 M::from_bytes(&wire_bytes)
416 }};
417 }
418
419 match rng {
421 Some(r) => do_encrypt!(r),
422 None => do_encrypt!(&mut OsRng),
423 }
424}
425
426pub fn decrypt<SK, M>(recipient_seckey: &SK, message: &M, associated_data: Option<&[u8]>) -> Result<SecretSlice<u8>>
427where
428 SK: EciesSecretKeyOps,
429 M: EciesMessageOps,
430{
431 let ephemeral_pubkey = <SK::PublicKey as EciesPublicKeyOps>::from_bytes(message.ephemeral_pubkey())?;
433 let shared_secret = recipient_seckey.diffie_hellman(&ephemeral_pubkey);
435 let k_enc = ecies_kdf::<HkdfSha3_256>(message.ephemeral_pubkey(), shared_secret, TIGHTBEAM_ECIES_KDF_INFO, None)?;
439
440 let ciphertext_bytes = message.ciphertext();
442 if ciphertext_bytes.len() < AES_GCM_NONCE_SIZE + AES_GCM_TAG_SIZE {
443 return Err(EciesError::InvalidCiphertext);
444 }
445 let nonce = crate::crypto::utils::nonce_from_slice::<Aes256Gcm>(&ciphertext_bytes[0..AES_GCM_NONCE_SIZE]);
446 let ciphertext_with_tag = &ciphertext_bytes[AES_GCM_NONCE_SIZE..];
447
448 let key = crate::crypto::utils::key_from_slice(&k_enc[..32]);
450 let cipher = Aes256Gcm::new(&key);
451
452 let payload = match associated_data {
454 Some(aad) => Payload { msg: ciphertext_with_tag, aad },
455 None => Payload { msg: ciphertext_with_tag, aad: b"" },
456 };
457
458 let plaintext = cipher.decrypt(&nonce, payload).map_err(EciesError::DecryptionFailed)?;
460 Ok(Secret::from(plaintext.into_boxed_slice()))
461}
462
463#[cfg(feature = "x509")]
464crate::define_oid_wrapper!(
465 EciesSecp256k1Oid,
467 "1.3.132.1.12.0"
468);
469
470#[cfg(feature = "x509")]
489pub struct EciesEncryptor {
490 recipient_pubkey: PublicKey,
491}
492
493#[cfg(feature = "x509")]
494impl EciesEncryptor {
495 pub fn new(recipient_pubkey: PublicKey) -> Self {
497 Self { recipient_pubkey }
498 }
499
500 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self> {
502 let pubkey = PublicKey::from_bytes(bytes)?;
503 Ok(Self::new(pubkey))
504 }
505}
506
507#[cfg(feature = "x509")]
508impl crate::crypto::aead::Encryptor<EciesSecp256k1Oid> for EciesEncryptor {
509 fn encrypt_content(
510 &self,
511 data: impl AsRef<[u8]>,
512 _nonce: impl AsRef<[u8]>, content_type: Option<ObjectIdentifier>,
514 ) -> crate::error::Result<crate::EncryptedContentInfo> {
515 let ecies_msg: Secp256k1EciesMessage =
517 encrypt(&self.recipient_pubkey, data.as_ref(), None, None::<&mut OsRng>)?;
518
519 let encrypted_bytes = ecies_msg.to_bytes();
521 let content_type = content_type.unwrap_or(crate::oids::DATA);
522
523 let content_enc_alg = crate::AlgorithmIdentifier { oid: EciesSecp256k1Oid::OID, parameters: None };
525 let encrypted_content = Some(crate::der::asn1::OctetString::new(encrypted_bytes)?);
526
527 Ok(crate::EncryptedContentInfo { content_type, content_enc_alg, encrypted_content })
528 }
529}
530
531#[cfg(feature = "x509")]
542pub struct EciesDecryptor {
543 secret_key: SecretKey,
544}
545
546#[cfg(feature = "x509")]
547impl EciesDecryptor {
548 pub fn new(secret_key: SecretKey) -> Self {
550 Self { secret_key }
551 }
552}
553
554#[cfg(feature = "x509")]
555impl crate::crypto::aead::Decryptor for EciesDecryptor {
556 fn decrypt_content(&self, info: &crate::EncryptedContentInfo) -> crate::error::Result<Vec<u8>> {
557 let encrypted_bytes = info
559 .encrypted_content
560 .as_ref()
561 .ok_or(crate::TightBeamError::MissingEncryptionInfo)?
562 .as_bytes();
563
564 let ecies_msg = Secp256k1EciesMessage::from_bytes(encrypted_bytes)?;
566 let plaintext = decrypt(&self.secret_key, &ecies_msg, None)?;
568
569 use crate::crypto::secret::ToInsecure;
571 Ok(plaintext.to_insecure()?.to_vec())
572 }
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578 use crate::crypto::secret::ToInsecure;
579 use rand_core::OsRng;
580
581 fn keypair() -> (SecretKey, PublicKey) {
583 let mut rng = OsRng;
584 let secret = SecretKey::random(&mut rng);
585 let public = secret.public_key();
586 (secret, public)
587 }
588
589 fn roundtrip(plaintext: &[u8], aad: Option<&[u8]>) -> Result<()> {
591 let (secret, public) = keypair();
592 let encrypted = encrypt::<_, _, _, Secp256k1EciesMessage>(&public, plaintext, aad, None::<&mut OsRng>)?;
593 let decrypted = decrypt(&secret, &encrypted, aad)?;
594 assert_eq!(plaintext, &decrypted.to_insecure().map_err(EciesError::from)?[..]);
595 Ok(())
596 }
597
598 #[test]
599 fn test_ecies_encryption() -> Result<()> {
600 let cases = [
602 (&b"Hello, ECIES!"[..], None),
603 (b"Secret message", Some(&b"authenticated data"[..])),
604 (b"", None),
605 (b"The quick brown fox jumps over the lazy dog. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", None),
607 (b"\x00\x01\x02\x03\xFF\xFE\xFD\xFC", None),
608 (b"Payload", Some(&b"version:1|timestamp:12345|nonce:abcdef"[..])),
609 ];
610
611 for (plaintext, aad) in cases {
612 roundtrip(plaintext, aad)?;
613 }
614
615 Ok(())
616 }
617
618 #[test]
619 fn test_aad_validation() -> Result<()> {
620 let mut rng = OsRng;
621 let (secret, public) = keypair();
622 let plaintext = b"Secret message";
623 let correct_aad = b"authenticated data";
624
625 let encrypted =
627 encrypt::<_, _, _, Secp256k1EciesMessage>(&public, plaintext, Some(correct_aad), Some(&mut rng))?;
628 let cases = [
630 (Some(&correct_aad[..]), true),
631 (Some(&b"wrong data"[..]), false),
632 (None, false),
633 (Some(&b""[..]), false),
634 ];
635
636 for (aad, should_succeed) in cases {
637 let result = decrypt(&secret, &encrypted, aad);
638 assert_eq!(result.is_ok(), should_succeed);
639 if should_succeed {
640 assert_eq!(&plaintext[..], &result?.to_insecure().map_err(EciesError::from)?[..]);
641 }
642 }
643
644 Ok(())
645 }
646
647 #[test]
648 fn test_serialization() -> Result<()> {
649 let (secret, public) = keypair();
650 let plaintext = b"Test serialization";
651
652 let encrypted = encrypt::<_, _, _, Secp256k1EciesMessage>(&public, plaintext, None, None::<&mut OsRng>)?;
654 let bytes = encrypted.to_bytes();
655 let parsed = Secp256k1EciesMessage::from_bytes(&bytes)?;
656 let decrypted = decrypt(&secret, &parsed, None)?;
657 assert_eq!(&plaintext[..], &decrypted.to_insecure().map_err(EciesError::from)?[..]);
658
659 let secret_bytes: SecretSlice<u8> = (&secret).into();
661 let public_bytes = public.to_bytes();
662
663 let secret2 = SecretKey::try_from(secret_bytes)?;
664 let public2 = PublicKey::from_bytes(&public_bytes)?;
665
666 assert_eq!(public.to_bytes(), public2.to_bytes());
667 assert_eq!(secret.public_key().to_bytes(), secret2.public_key().to_bytes());
668
669 Ok(())
670 }
671
672 #[test]
673 fn test_security_properties() -> Result<()> {
674 let plaintext = b"Sensitive data";
675
676 let (_, public1) = keypair();
678 let (secret2, _) = keypair();
679
680 let encrypted = encrypt::<_, _, _, Secp256k1EciesMessage>(&public1, plaintext, None, None::<&mut OsRng>)?;
681 let result = decrypt(&secret2, &encrypted, None);
682
683 if let Ok(decrypted) = result {
684 assert_ne!(&plaintext[..], &decrypted.to_insecure().map_err(EciesError::from)?[..]);
685 }
686
687 let (secret, public) = keypair();
689
690 let tamper_functions: [fn(&mut Secp256k1EciesMessage); 4] = [
691 |msg| {
692 if let Some(byte) = msg.ciphertext_mut().last_mut() {
693 *byte ^= 0xFF;
694 }
695 },
696 |msg| {
697 if let Some(byte) = msg.ciphertext_mut().first_mut() {
698 *byte ^= 0xFF;
699 }
700 },
701 |msg| {
702 let len = msg.ciphertext_mut().len().saturating_sub(1);
703 msg.ciphertext_mut().truncate(len);
704 },
705 |msg| {
706 if let Some(byte) = msg.ephemeral_pubkey_mut().first_mut() {
707 *byte ^= 0xFF;
708 }
709 },
710 ];
711
712 for tamper_fn in tamper_functions {
713 let mut encrypted =
714 encrypt::<_, _, _, Secp256k1EciesMessage>(&public, plaintext, None, None::<&mut OsRng>)?;
715 tamper_fn(&mut encrypted);
716 assert!(decrypt(&secret, &encrypted, None).is_err());
717 }
718
719 Ok(())
720 }
721
722 #[test]
723 fn test_edge_cases() -> Result<()> {
724 let invalid_ciphertexts = [
726 vec![],
727 vec![0u8; 32],
728 vec![0u8; 33], vec![0u8; 45], ];
731
732 for data in invalid_ciphertexts {
733 assert!(Secp256k1EciesMessage::from_bytes(&data).is_err());
734 }
735
736 assert!(PublicKey::from_bytes([0xFFu8; 33]).is_err());
738 assert!(SecretKey::try_from(Secret::from(vec![0x00u8; 32].into_boxed_slice())).is_err());
739
740 Ok(())
741 }
742}