capycrypt/ecc/encryptable.rs
1use crate::{
2 sha3::{
3 aux_functions::byte_utils::{bytes_to_scalar, get_random_bytes, xor_bytes},
4 shake_functions::kmac_xof,
5 },
6 Message, OperationError, SecParam,
7};
8use tiny_ed448_goldilocks::curve::{extended_edwards::ExtendedPoint, field::scalar::Scalar};
9
10pub trait KeyEncryptable {
11 fn key_encrypt(&mut self, pub_key: &ExtendedPoint, d: SecParam);
12 fn key_decrypt(&mut self, pw: &[u8]) -> Result<(), OperationError>;
13}
14
15impl KeyEncryptable for Message {
16 /// # Asymmetric Encryption
17 /// Encrypts a [`Message`] in place under the (Schnorr/ECDHIES) public key 𝑉.
18 /// Operates under Schnorr/ECDHIES principle in that shared symmetric key is
19 /// exchanged with recipient. SECURITY NOTE: ciphertext length == plaintext length
20 /// ## Replaces:
21 /// * `Message.data` with result of encryption.
22 /// * `Message.t` with keyed hash of plaintext.
23 /// * `Message.asym_nonce` with z, as defined below.
24 /// ## Algorithm:
25 /// * k ← Random(448); k ← 4k
26 /// * W ← kV; 𝑍 ← k*𝑮
27 /// * (ke || ka) ← kmac_xof(W x , “”, 448 * 2, “P”)
28 /// * c ← kmac_xof(ke, “”, |m|, “PKE”) ⊕ m
29 /// * t ← kmac_xof(ka, m, 448, “PKA”)
30 /// ## Arguments:
31 /// * pub_key: [`ExtendedPoint`] : X coordinate of public key 𝑉
32 /// * d: u64: Requested security strength in bits. Can only be 224, 256, 384, or 512.
33 #[allow(non_snake_case)]
34 fn key_encrypt(&mut self, pub_key: &ExtendedPoint, d: SecParam) {
35 self.d = Some(d);
36 let k = bytes_to_scalar(&get_random_bytes(56)).mul_mod(&Scalar::from(4_u64));
37 let w = (*pub_key * k).to_affine();
38 let Z = (ExtendedPoint::generator() * k).to_affine();
39
40 let ke_ka = kmac_xof(&w.x.to_bytes(), &[], 448 * 2, "PK", d);
41 let (ke, ka) = ke_ka.split_at(ke_ka.len() / 2);
42
43 let t = kmac_xof(ka, &self.msg, 448, "PKA", d);
44
45 let msg_len = self.msg.len();
46 xor_bytes(&mut self.msg, &kmac_xof(ke, &[], msg_len * 8, "PKE", d));
47
48 self.digest = t;
49 self.asym_nonce = Some(Z.to_extended());
50 }
51
52 /// # Asymmetric Decryption
53 /// Decrypts a [`Message`] in place under private key.
54 /// Operates under Schnorr/ECDHIES principle in that shared symmetric key is
55 /// derived from 𝑍.
56 ///
57 /// ## Replaces:
58 /// * `Message.data` with result of decryption.
59 /// * `Message.op_result` with result of comparision of `Message.t` == keyed hash of decryption.
60 ///
61 /// ## Algorithm:
62 /// * s ← KMACXOF256(pw, “”, 448, “K”); s ← 4s
63 /// * W ← sZ
64 /// * (ke || ka) ← KMACXOF256(W x , “”, 448 * 2, “P”)
65 /// * m ← KMACXOF256(ke, “”, |c|, “PKE”) ⊕ c
66 /// * t’ ← KMACXOF256(ka, m, 448, “PKA”)
67 ///
68 /// ## Arguments:
69 /// * pw: &[u8]: password used to generate ```CurvePoint``` encryption key.
70 /// * d: u64: encryption security strength in bits. Can only be 224, 256, 384, or 512.
71 #[allow(non_snake_case)]
72 fn key_decrypt(&mut self, pw: &[u8]) -> Result<(), OperationError> {
73 let Z = self.asym_nonce.ok_or(OperationError::SymNonceNotSet)?;
74 let d = self.d.ok_or(OperationError::SecurityParameterNotSet)?;
75
76 let s_bytes = kmac_xof(pw, &[], 448, "SK", d);
77 let s = bytes_to_scalar(&s_bytes).mul_mod(&Scalar::from(4_u64));
78 let Z = (Z * s).to_affine();
79
80 let ke_ka = kmac_xof(&Z.x.to_bytes(), &[], 448 * 2, "PK", d);
81 let (ke, ka) = ke_ka.split_at(ke_ka.len() / 2);
82
83 let xor_result = kmac_xof(ke, &[], self.msg.len() * 8, "PKE", d);
84 xor_bytes(&mut self.msg, &xor_result);
85
86 let t_p = kmac_xof(ka, &self.msg, 448, "PKA", d);
87
88 if self.digest == t_p {
89 Ok(())
90 } else {
91 xor_bytes(&mut self.msg, &xor_result);
92 Err(OperationError::KeyDecryptionError)
93 }
94 }
95}