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}