arx_kw/
g.rs

1use crate::{
2    generate,
3    util,
4    lqb,
5    ArxKW,
6    ArxKwError,
7    InvalidLengthError,
8    AuthTag,
9    ConstantTimeEq
10};
11
12/// The ARX-8-2-4-G variant. Has a key length of 32 bytes and a maximum input length of 64 bytes.
13/// See the [`ArxKW`] trait for usage.
14pub struct G;
15impl G {
16    /// The length of the secret key used by this variant of ARX-KW, in bytes
17    #[must_use]
18    #[cfg(not(tarpaulin_include))]
19    pub const fn key_length() -> usize {
20        32
21    }
22    
23    /// Returns the maximum length in bytes for the input to `G::encrypt`  and `G::decrypt` 
24    ///
25    /// ---
26    /// This is the same for the plaintext input when encrypting and ciphertext input when
27    /// decrypting, but is **not** the same for all variants of ARX-KW. Specifically, the *E* and *G*
28    /// variants are defined only for plaintext/ciphertext inputs of no more than 512 bits, the length
29    /// of a ChaCha Block, whereas *EX* and *GX* do not have this limitation.
30    #[cfg(not(tarpaulin_include))]
31    #[must_use]
32    pub const fn max_input_length() -> usize {
33        64
34    }
35}
36
37
38impl ArxKW for G {
39    type Key = [u8; Self::key_length()];
40    fn encrypt(key: &Self::Key, plaintext: &[u8]) -> Result<(Vec<u8>, AuthTag), ArxKwError> {
41        if plaintext.len() > Self::max_input_length() {
42            Err(ArxKwError::InvalidLength(InvalidLengthError::UpTo(plaintext.len(), Self::max_input_length())))
43        } else {
44            let (k1,k2) = generate::subkeys(key)?;
45            let authentication_tag = util::sip_array_keyed(&k1, plaintext);
46            let ciphertext = lqb::chacha8_encrypt(&k2, authentication_tag.as_ref(), plaintext)?;
47            Ok((ciphertext, authentication_tag))
48        }
49    }
50    
51
52    fn decrypt(key: &Self::Key, ciphertext: &[u8], authentication_tag: &AuthTag) -> Result<Vec<u8>, ArxKwError> {
53        if ciphertext.len() > Self::max_input_length() {
54            Err(ArxKwError::InvalidLength(InvalidLengthError::UpTo(ciphertext.len(), Self::max_input_length())))
55        } else {
56            let (k1,k2) = generate::subkeys(key)?;
57            let p_prime = lqb::chacha8_encrypt(&k2, authentication_tag.as_ref(), ciphertext)?;
58            let t_prime = util::sip_array_keyed(&k1, &p_prime);
59            if bool::from(t_prime.ct_eq(authentication_tag)) {
60                return Ok(p_prime);
61            }
62            Err(ArxKwError::BadTags(t_prime, *authentication_tag))
63        }
64    }
65}
66
67
68#[cfg(test)]
69mod tests {
70    use hex::FromHex;
71    use anyhow::Result;
72    use super::{G,ArxKW};
73    use crate::{AuthTag,assert_ct_eq,ConstantTimeEq};
74
75    #[test]
76    fn test_encrypt() -> Result<()> {
77
78        let k = <[u8;32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
79        let p = <[u8;32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
80        let p_bad = [0xaf; 69];
81        let res = G::encrypt(&k, &p_bad);
82        assert!(res.is_err());
83        let t_expected = AuthTag(<[u8;16]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc65")?);
84        let c_expected = <[u8;32]>::from_hex("f63830f5148a039b6aacc4b9b6bc281d7704d906e4b5d91e045a62cdfc25eb10")?;
85        let (c,t) = G::encrypt(&k,&p)?;
86        assert_ct_eq!(c, &c_expected);
87        assert_ct_eq!(t, &t_expected);
88        Ok(())
89    }
90
91    #[test]
92    fn test_encrypt_blob() -> Result<()> {
93        let k = <[u8;32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
94        let p = <[u8;32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
95        let blob_expected = <[u8;48]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc65f63830f5148a039b6aacc4b9b6bc281d7704d906e4b5d91e045a62cdfc25eb10")?;
96        let blob = G::encrypt_blob(&k, &p)?;
97        assert_ct_eq!(blob_expected, &blob);
98        Ok(())
99    }
100
101    #[test]
102    fn test_decrypt_blob() -> Result<()> {
103        let k = <[u8;32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
104        let p_expected = <[u8;32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
105        let blob = <[u8;48]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc65f63830f5148a039b6aacc4b9b6bc281d7704d906e4b5d91e045a62cdfc25eb10")?;
106        let p = G::decrypt_blob(&k, &blob)?;
107        assert_ct_eq!(p, &p_expected);
108        Ok(())
109    }
110
111    #[test]
112    fn test_decrypt() -> Result<()> {
113        let k = <[u8;32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
114        let p_expected = <[u8;32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
115        let p_bad = [0xaf; 69];
116        let t_expected = AuthTag(<[u8;16]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc65")?);
117        let res = G::decrypt(&k, &p_bad, &t_expected);
118        assert!(res.is_err());
119        let c = <[u8;32]>::from_hex("f63830f5148a039b6aacc4b9b6bc281d7704d906e4b5d91e045a62cdfc25eb10")?;
120        let p = G::decrypt(&k,&c,&t_expected)?;
121        assert_eq!(p, p_expected);
122        Ok(())
123    }
124
125    #[test]
126    fn test_decrypt_bad() -> Result<()> {
127        let k = <[u8;32]>::from_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")?;
128        let p_expected = <[u8;32]>::from_hex("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")?;
129        let c = <[u8;32]>::from_hex("f63830f5148a039b6aacc4b9b6bc281d7704d906e4b5d91e045a62cdfc25eb10")?;
130        let c_bad = [0xaf; 69];
131        let t_expected = AuthTag(<[u8;16]>::from_hex("016325cf6a3c4b2e3b039675e1ccbc65")?);
132        let res = G::decrypt(&k, &c_bad, &t_expected);
133        assert!(res.is_err());
134        let p = G::decrypt(&k,&c,&t_expected)?;
135        assert_eq!(p, p_expected);
136        let t_bad = AuthTag(<[u8;16]>::from_hex("dab325cf6a3c4b2e3b039675e1ccbc65")?); // first 3 hex digits should be 016
137        let res = G::decrypt(&k, &c, &t_bad);
138        assert!(res.is_err());
139        Ok(())
140    }
141}