sentc_crypto_std_keys/core/asym/
ecies.rs1use alloc::vec::Vec;
2
3use hkdf::Hkdf;
4use rand_core::{CryptoRng, RngCore};
5use sentc_crypto_core::cryptomat::{Pk, SignK, Sk, StaticKeyPair, SymKey, VerifyK};
6use sentc_crypto_core::{as_ref_bytes_single_value, crypto_alg_str_impl, try_from_bytes_owned_single_value, Error};
7use sha2::Sha256;
8use x25519_dalek::{EphemeralSecret, PublicKey, StaticSecret};
9
10use crate::core::asym::SecretKey;
11use crate::core::sym::aes_gcm::{raw_decrypt as aes_decrypt, raw_encrypt as aes_encrypt, AesKey};
12use crate::get_rand;
13
14pub const ECIES_OUTPUT: &str = "ECIES-ed25519";
15
16const HKDF_INFO: &[u8; 13] = b"ecies-ed25519";
17
18const PUBLIC_KEY_LENGTH: usize = 32;
19
20#[derive(Clone)]
21pub struct EciesPk([u8; 32]);
22
23try_from_bytes_owned_single_value!(EciesPk);
24crypto_alg_str_impl!(EciesPk, ECIES_OUTPUT);
25as_ref_bytes_single_value!(EciesPk);
26
27impl Into<crate::core::asym::PublicKey> for EciesPk
28{
29 fn into(self) -> crate::core::asym::PublicKey
30 {
31 crate::core::asym::PublicKey::Ecies(self)
32 }
33}
34
35impl Pk for EciesPk
36{
37 fn sign_public_key<S: SignK>(&self, sign_key: &S) -> Result<S::Signature, Error>
38 {
39 sign_key.sign_only(self.0)
40 }
41
42 fn verify_public_key<V: VerifyK>(&self, verify_key: &V, sig: &V::Signature) -> Result<bool, Error>
43 {
44 verify_key.verify_only(sig, &self.0)
45 }
46
47 fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>, Error>
48 {
49 encrypt_internally(&self.0.into(), data, &mut get_rand())
50 }
51}
52
53pub struct EciesSk([u8; 32]);
54
55try_from_bytes_owned_single_value!(EciesSk);
56crypto_alg_str_impl!(EciesSk, ECIES_OUTPUT);
57as_ref_bytes_single_value!(EciesSk);
58
59impl Into<SecretKey> for EciesSk
60{
61 fn into(self) -> SecretKey
62 {
63 SecretKey::Ecies(self)
64 }
65}
66
67impl Sk for EciesSk
68{
69 fn encrypt_by_master_key<M: SymKey>(&self, master_key: &M) -> Result<Vec<u8>, Error>
70 {
71 master_key.encrypt(&self.0)
72 }
73
74 fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, Error>
75 {
76 decrypt_internally(&self.0.into(), ciphertext)
77 }
78}
79
80pub struct EciesKeyPair;
81
82impl StaticKeyPair for EciesKeyPair
83{
84 type SecretKey = EciesSk;
85 type PublicKey = EciesPk;
86
87 fn generate_static_keypair() -> Result<(Self::SecretKey, Self::PublicKey), Error>
88 {
89 let (sk, pk) = generate_static_keypair_internally(&mut get_rand());
90
91 Ok((EciesSk(sk.to_bytes()), EciesPk(pk.to_bytes())))
92 }
93}
94
95pub(super) fn generate_static_keypair_internally<R: CryptoRng + RngCore>(rng: &mut R) -> (StaticSecret, PublicKey)
99{
100 let sk = StaticSecret::random_from_rng(rng);
101 let pk = PublicKey::from(&sk);
102
103 (sk, pk)
104}
105
106fn generate_keypair_internally<R: CryptoRng + RngCore>(rng: &mut R) -> (EphemeralSecret, PublicKey)
107{
108 let sk = EphemeralSecret::random_from_rng(rng);
109 let pk = PublicKey::from(&sk);
110
111 (sk, pk)
112}
113
114pub(super) fn encrypt_internally<R: CryptoRng + RngCore>(receiver_pub: &PublicKey, data: &[u8], rng: &mut R) -> Result<Vec<u8>, Error>
115{
116 let (ep_sk, ep_pk) = generate_keypair_internally(rng);
117
118 let aes_key = encapsulate(ep_sk, receiver_pub);
119
120 let encrypted = aes_encrypt(&aes_key, data)?;
121
122 let mut cipher_text = Vec::with_capacity(PUBLIC_KEY_LENGTH + encrypted.len());
124 cipher_text.extend_from_slice(&ep_pk.to_bytes());
125 cipher_text.extend_from_slice(&encrypted);
126
127 Ok(cipher_text)
128}
129
130pub(super) fn decrypt_internally(receiver_sec: &StaticSecret, ciphertext: &[u8]) -> Result<Vec<u8>, Error>
131{
132 if ciphertext.len() <= PUBLIC_KEY_LENGTH {
133 return Err(Error::DecryptionFailedCiphertextShort);
134 }
135
136 let ep_pk_bytes: [u8; 32] = match ciphertext[..PUBLIC_KEY_LENGTH].try_into() {
139 Err(_e) => return Err(Error::DecryptionFailedCiphertextShort),
140 Ok(bytes) => bytes,
141 };
142
143 let ep_pk = PublicKey::from(ep_pk_bytes);
144
145 let encrypted = &ciphertext[PUBLIC_KEY_LENGTH..];
146
147 let aes_key = decapsulate(receiver_sec, &ep_pk);
149
150 let decrypted = aes_decrypt(&aes_key, encrypted)?;
151
152 Ok(decrypted)
153}
154
155fn encapsulate(ep_sk: EphemeralSecret, peer_pk: &PublicKey) -> AesKey
156{
157 let shared = ep_sk.diffie_hellman(peer_pk);
159
160 hkdf_sha256(shared.as_bytes())
161}
162
163fn decapsulate(sk: &StaticSecret, ep_pk: &PublicKey) -> AesKey
164{
165 let shared = sk.diffie_hellman(ep_pk);
166
167 hkdf_sha256(shared.as_bytes())
168}
169
170fn hkdf_sha256(ikm: &[u8]) -> AesKey
171{
172 let h = Hkdf::<Sha256>::new(None, ikm);
173 let mut out = [0u8; 32];
174
175 h.expand(HKDF_INFO, &mut out).unwrap();
176
177 out
178}
179
180#[cfg(test)]
181mod test
182{
183 use core::str::from_utf8;
184
185 use sentc_crypto_core::Error::{DecryptionFailed, DecryptionFailedCiphertextShort};
186
187 use super::*;
188
189 #[test]
190 fn test_key_gen()
191 {
192 let _ = EciesKeyPair::generate_static_keypair().unwrap();
193 }
194
195 #[test]
196 fn test_encrypt_and_decrypt()
197 {
198 let (sk, pk) = EciesKeyPair::generate_static_keypair().unwrap();
199
200 let text = "Hello world üöäéèßê°";
201
202 let encrypted = pk.encrypt(text.as_bytes()).unwrap();
203
204 let decrypted = sk.decrypt(&encrypted).unwrap();
205
206 assert_eq!(text.as_bytes(), decrypted);
207
208 let decrypted_text = from_utf8(&decrypted).unwrap();
209
210 assert_eq!(text, decrypted_text);
211 }
212
213 #[test]
214 fn test_not_decrypt_with_wrong_key()
215 {
216 let (_sk, pk) = EciesKeyPair::generate_static_keypair().unwrap();
217
218 let (sk, _pk) = EciesKeyPair::generate_static_keypair().unwrap();
219
220 let text = "Hello world üöäéèßê°";
221
222 let encrypted = pk.encrypt(text.as_bytes()).unwrap();
223
224 let decrypted_result = sk.decrypt(&encrypted);
225
226 assert!(matches!(decrypted_result, Err(DecryptionFailed)));
227 }
228
229 #[test]
230 fn test_not_decrypt_with_wrong_ciphertext()
231 {
232 let (sk, pk) = EciesKeyPair::generate_static_keypair().unwrap();
233
234 let text = "Hello world üöäéèßê°";
235
236 let encrypted = pk.encrypt(text.as_bytes()).unwrap();
237
238 let encrypted = &encrypted[..(encrypted.len() - 56)];
240
241 let decrypted_result = sk.decrypt(encrypted);
242
243 assert!(matches!(decrypted_result, Err(DecryptionFailedCiphertextShort)));
244 }
245}