1use crate::{BSVErrors, Hash, PrivateKey, PublicKey, AES};
2use elliptic_curve::sec1::ToEncodedPoint;
3use k256::PublicKey as K256PublicKey;
4#[cfg(target_arch = "wasm32")]
5use wasm_bindgen::prelude::*;
6#[cfg(target_arch = "wasm32")]
7use wasm_bindgen::{throw_str, JsValue};
8
9pub mod ecies_ciphertext;
10pub use ecies_ciphertext::*;
11
12#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen)]
17#[derive(Clone)]
18pub struct ECIES {}
19
20#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen)]
21#[derive(Clone)]
22pub struct CipherKeys {
23 pub(crate) iv: Vec<u8>,
24 pub(crate) ke: Vec<u8>,
25 pub(crate) km: Vec<u8>,
26}
27
28#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen)]
29impl CipherKeys {
30 pub fn get_iv(&self) -> Vec<u8> {
31 self.iv.clone()
32 }
33
34 pub fn get_ke(&self) -> Vec<u8> {
35 self.ke.clone()
36 }
37
38 pub fn get_km(&self) -> Vec<u8> {
39 self.km.clone()
40 }
41}
42
43impl ECIES {
44 pub(crate) fn encrypt_impl(message: &[u8], private_key: &PrivateKey, recipient_pub_key: &PublicKey, exclude_pub_key: bool) -> Result<ECIESCiphertext, BSVErrors> {
45 let cipher = ECIES::derive_cipher_keys_impl(private_key, recipient_pub_key)?;
46 let cipher_text = AES::encrypt_impl(&cipher.ke, &cipher.iv, message, crate::AESAlgorithms::AES128_CBC)?;
47
48 let mut buffer: Vec<u8> = Vec::new();
49 buffer.extend_from_slice(b"BIE1");
50
51 let r_buf = match exclude_pub_key {
52 true => None,
53 false => {
54 let pub_key = private_key.to_public_key_impl()?.to_compressed_impl()?.to_bytes_impl()?;
55 buffer.extend_from_slice(&pub_key);
56 Some(pub_key)
57 }
58 };
59 buffer.extend_from_slice(&cipher_text);
60
61 let hmac = Hash::sha_256_hmac(&buffer, &cipher.km).to_bytes();
62
63 Ok(ECIESCiphertext {
64 ciphertext_bytes: cipher_text,
65 public_key_bytes: r_buf,
66 hmac_bytes: hmac,
67 keys: Some(cipher),
68 })
69 }
70
71 pub(crate) fn encrypt_with_ephemeral_private_key_impl(message: &[u8], recipient_pub_key: &PublicKey) -> Result<ECIESCiphertext, BSVErrors> {
76 let private_key = PrivateKey::from_random();
77 ECIES::encrypt_impl(message, &private_key, recipient_pub_key, false)
78 }
79
80 pub(crate) fn decrypt_impl(ciphertext: &ECIESCiphertext, recipient_priv_key: &PrivateKey, sender_pub_key: &PublicKey) -> Result<Vec<u8>, BSVErrors> {
81 let cipher_keys = ECIES::derive_cipher_keys_impl(recipient_priv_key, sender_pub_key)?;
82
83 let hmac = &ciphertext.hmac_bytes;
84
85 let mut preimage = b"BIE1".to_vec();
86 if let Some(pk) = &ciphertext.public_key_bytes {
87 preimage.extend_from_slice(pk);
88 }
89 preimage.extend_from_slice(&ciphertext.ciphertext_bytes);
90
91 let verify_hmac = Hash::sha_256_hmac(&preimage, &cipher_keys.km);
92
93 if hmac != &verify_hmac.to_bytes() {
94 return Err(BSVErrors::ECIESError("Invalid Checksum".into()));
95 }
96
97 let plain_text = AES::decrypt_impl(&cipher_keys.ke, &cipher_keys.iv, &ciphertext.ciphertext_bytes, crate::AESAlgorithms::AES128_CBC)?;
98 Ok(plain_text)
99 }
100
101 pub(crate) fn derive_cipher_keys_impl(priv_key: &PrivateKey, pub_key: &PublicKey) -> Result<CipherKeys, BSVErrors> {
102 let private_scalar = *priv_key.secret_key.to_nonzero_scalar();
103 let pub_key_point = K256PublicKey::from_sec1_bytes(&pub_key.to_bytes_impl()?)?.to_projective();
104
105 let shared_point = pub_key_point * private_scalar;
106 let shared_pub = K256PublicKey::from_affine(shared_point.to_affine())?;
107
108 let shared_mod_point = shared_pub.to_encoded_point(true);
109 let hash = Hash::sha_512(shared_mod_point.as_bytes()).to_bytes();
110
111 Ok(CipherKeys {
112 iv: hash[0..16].into(),
113 ke: hash[16..32].into(),
114 km: hash[32..64].into(),
115 })
116 }
117}
118
119#[cfg(target_arch = "wasm32")]
120#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen)]
121impl ECIES {
122 pub fn encrypt(message: &[u8], sender_priv_key: &PrivateKey, recipient_pub_key: &PublicKey, exclude_pub_key: bool) -> Result<ECIESCiphertext, JsValue> {
123 match ECIES::encrypt_impl(message, sender_priv_key, recipient_pub_key, exclude_pub_key) {
124 Ok(v) => Ok(v),
125 Err(e) => Err(JsValue::from_str(&e.to_string())),
126 }
127 }
128
129 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen(js_name = encryptWithEphemeralKey))]
134 pub fn encrypt_with_ephemeral_private_key(message: &[u8], recipient_pub_key: &PublicKey) -> Result<ECIESCiphertext, JsValue> {
135 match ECIES::encrypt_with_ephemeral_private_key_impl(message, recipient_pub_key) {
136 Ok(v) => Ok(v),
137 Err(e) => Err(JsValue::from_str(&e.to_string())),
138 }
139 }
140
141 pub fn decrypt(ciphertext: &ECIESCiphertext, recipient_priv_key: &PrivateKey, sender_pub_key: &PublicKey) -> Result<Vec<u8>, JsValue> {
142 match ECIES::decrypt_impl(ciphertext, recipient_priv_key, sender_pub_key) {
143 Ok(v) => Ok(v),
144 Err(e) => Err(JsValue::from_str(&e.to_string())),
145 }
146 }
147
148 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecies"), wasm_bindgen(js_name = deriveCipherKeys))]
149 pub fn derive_cipher_keys(priv_key: &PrivateKey, pub_key: &PublicKey) -> Result<CipherKeys, JsValue> {
150 match ECIES::derive_cipher_keys_impl(priv_key, pub_key) {
151 Ok(v) => Ok(v),
152 Err(e) => Err(JsValue::from_str(&e.to_string())),
153 }
154 }
155}
156
157#[cfg(not(target_arch = "wasm32"))]
158impl ECIES {
159 pub fn encrypt(message: &[u8], sender_priv_key: &PrivateKey, recipient_pub_key: &PublicKey, exclude_pub_key: bool) -> Result<ECIESCiphertext, BSVErrors> {
160 ECIES::encrypt_impl(message, sender_priv_key, recipient_pub_key, exclude_pub_key)
161 }
162
163 pub fn encrypt_with_ephemeral_private_key(message: &[u8], recipient_pub_key: &PublicKey) -> Result<ECIESCiphertext, BSVErrors> {
168 ECIES::encrypt_with_ephemeral_private_key_impl(message, recipient_pub_key)
169 }
170
171 pub fn decrypt(ciphertext: &ECIESCiphertext, recipient_priv_key: &PrivateKey, sender_pub_key: &PublicKey) -> Result<Vec<u8>, BSVErrors> {
172 ECIES::decrypt_impl(ciphertext, recipient_priv_key, sender_pub_key)
173 }
174
175 pub fn derive_cipher_keys(priv_key: &PrivateKey, pub_key: &PublicKey) -> Result<CipherKeys, BSVErrors> {
176 ECIES::derive_cipher_keys_impl(priv_key, pub_key)
177 }
178}