Skip to main content

sentc_crypto_std_keys/core/asym/
ecies.rs

1use 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
95//__________________________________________________________________________________________________
96//internally function
97
98pub(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	//put the ephemeral public key in front of the aes encrypt, so we can use it later for decrypt
123	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	//get the ephemeral public key which we put in front of the encrypted data
137	//should not panic because we checked the length
138	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	//this works because we used the receiver static public key for encrypt
148	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	//use here the ephemeral and only once!
158	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		//too short ciphertext: text must be min 32 long, output was 88 long
239		let encrypted = &encrypted[..(encrypted.len() - 56)];
240
241		let decrypted_result = sk.decrypt(encrypted);
242
243		assert!(matches!(decrypted_result, Err(DecryptionFailedCiphertextShort)));
244	}
245}