anothertls/crypto/chacha20/
poly1305.rs

1/*
2 * Copyright (c) 2023, Tobias Müller <git@tsmr.eu>
3 *
4 * Spec: https://datatracker.ietf.org/doc/html/rfc8439
5 *
6 */
7
8use crate::crypto::chacha20::ChaCha20;
9use crate::net::alert::TlsError;
10use crate::{crypto::Cipher, utils::bytes};
11use ibig::ibig;
12
13use super::cipher::ChaCha20Block;
14
15#[derive(Default)]
16pub struct Poly1305();
17
18impl Poly1305 {
19    pub fn key_gen(key: &[u8], iv: &[u8]) -> Vec<u8> {
20        let mut chacha20_block = ChaCha20Block::init(key, iv, 0).unwrap();
21        chacha20_block.get_block()[..32].to_vec()
22    }
23
24    pub fn mac(key: &[u8], msg: &[u8]) -> [u8; 16] {
25        let mut r = bytes::to_ibig_le(&key[..16]);
26        r &= ibig!(_0ffffffc0ffffffc0ffffffc0fffffff base 16);
27        let s = bytes::to_ibig_le(&key[16..32]);
28
29        let mut a = ibig!(0);
30        let p = ibig!(_3fffffffffffffffffffffffffffffffb base 16);
31
32        for i in 1..=(msg.len() as f32 / 16.0).ceil() as usize {
33            let n = if i * 16 > msg.len() {
34                let mut buf = vec![];
35                buf.extend_from_slice(&msg[(i - 1) * 16..]);
36                buf.push(0x01);
37                bytes::to_ibig_le(&buf)
38            } else {
39                let mut n = bytes::to_ibig_le(&msg[(i - 1) * 16..i * 16]);
40                n += ibig!(_100000000000000000000000000000000 base 16);
41                n
42            };
43            a += n;
44            a = (r.clone() * a) % p.clone();
45        }
46        a += s;
47        let mut ret = bytes::ibig_to_vec(a, bytes::ByteOrder::Little);
48        // convert to u128
49        ret.resize(16, 0);
50        ret.try_into().unwrap()
51    }
52    pub fn pad16(input: &mut Vec<u8>, x: usize) {
53        if x % 16 != 0 {
54            input.resize(input.len() + (16 - (x % 16)), 0)
55        }
56    }
57    pub fn get_mac_data(ciphertext: &[u8], additional_data: &[u8]) -> Vec<u8> {
58        let mut mac_data = additional_data.to_vec();
59        Poly1305::pad16(&mut mac_data, additional_data.len());
60        mac_data.extend_from_slice(ciphertext);
61        Poly1305::pad16(&mut mac_data, ciphertext.len());
62
63        mac_data.extend_from_slice(&bytes::u64_to_bytes_le(additional_data.len() as u64));
64        mac_data.extend_from_slice(&bytes::u64_to_bytes_le(ciphertext.len() as u64));
65        mac_data
66    }
67}
68
69impl Cipher for Poly1305 {
70    fn encrypt(
71        &self,
72        key: &[u8],
73        iv: &[u8],
74        plaintext: &[u8],
75        additional_data: &[u8],
76    ) -> Result<(Vec<u8>, [u8; 16]), TlsError> {
77        let ciphertext = ChaCha20::encrypt(plaintext, key, iv, 1).unwrap();
78        let otk = Poly1305::key_gen(key, iv);
79        let tag = Poly1305::mac(&otk, &Poly1305::get_mac_data(&ciphertext, additional_data));
80        Ok((ciphertext, tag))
81    }
82
83    fn decrypt(
84        &self,
85        key: &[u8],
86        iv: &[u8],
87        ciphertext: &[u8],
88        additional_data: &[u8],
89        auth_tag: &[u8],
90    ) -> Result<Vec<u8>, TlsError> {
91        let otk = Poly1305::key_gen(key, iv);
92        let tag = Poly1305::mac(&otk, &Poly1305::get_mac_data(ciphertext, additional_data));
93        if tag == auth_tag {
94            if let Some(plaintext) = ChaCha20::decrypt(ciphertext, key, iv, 1) {
95                return Ok(plaintext);
96            }
97        }
98        Err(TlsError::BadRecordMac)
99    }
100
101    fn get_cipher_suite(&self) -> crate::crypto::CipherSuite {
102        crate::crypto::CipherSuite::TLS_CHACHA20_POLY1305_SHA256
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::Poly1305;
109    use crate::crypto::ciphersuite::Cipher;
110    use crate::utils::bytes;
111
112    #[test]
113    fn test_poly1305_mac() {
114        let key =
115            bytes::from_hex("85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b");
116        let msg =
117            bytes::from_hex("43727970746f6772617068696320466f72756d2052657365617263682047726f7570");
118
119        let expected = bytes::from_hex("a8061dc1305136c6c22b8baf0c0127a9");
120
121        assert_eq!(expected, Poly1305::mac(&key, &msg));
122    }
123
124    #[test]
125    fn test_poly1305_key_gen() {
126        let key =
127            bytes::from_hex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f");
128        let nonce = bytes::from_hex("000000000001020304050607");
129        let expected =
130            bytes::from_hex("8ad5a08b905f81cc815040274ab29471a833b637e3fd0da508dbb8e2fdd1a646");
131
132        assert_eq!(expected, Poly1305::key_gen(&key, &nonce));
133    }
134    #[test]
135    fn test_poly1305_aead_de_encrypt() {
136        let plaintext = bytes::from_hex("4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e");
137        let aad = bytes::from_hex("50515253c0c1c2c3c4c5c6c7");
138        let key =
139            bytes::from_hex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f");
140        let iv = bytes::from_hex("070000004041424344454647");
141        let ciphertext = bytes::from_hex("d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116");
142        let tag = bytes::from_hex("1ae10b594f09e26a7e902ecbd0600691");
143
144        let encrypted = Poly1305::default()
145            .encrypt(&key, &iv, &plaintext, &aad)
146            .unwrap();
147
148        assert_eq!(encrypted.0, ciphertext);
149        assert_eq!(&encrypted.1, &tag.as_slice());
150
151        let decrypted = Poly1305::default()
152            .decrypt(&key, &iv, &ciphertext, &aad, &tag)
153            .unwrap();
154
155        assert_eq!(decrypted, plaintext);
156    }
157
158    #[test]
159    fn test_poly1305_aead_decrypt() {
160        let ciphertext = bytes::from_hex("64a0861575861af460f062c79be643bd5e805cfd345cf389f108670ac76c8cb24c6cfc18755d43eea09ee94e382d26b0bdb7b73c321b0100d4f03b7f355894cf332f830e710b97ce98c8a84abd0b948114ad176e008d33bd60f982b1ff37c8559797a06ef4f0ef61c186324e2b3506383606907b6a7c02b0f9f6157b53c867e4b9166c767b804d46a59b5216cde7a4e99040c5a40433225ee282a1b0a06c523eaf4534d7f83fa1155b0047718cbc546a0d072b04b3564eea1b422273f548271a0bb2316053fa76991955ebd63159434ecebb4e466dae5a1073a6727627097a1049e617d91d361094fa68f0ff77987130305beaba2eda04df997b714d6c6f2c29a6ad5cb4022b02709b");
161
162        let key =
163            bytes::from_hex("1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0");
164        let aad = bytes::from_hex("f33388860000000000004e91");
165        let tag = bytes::from_hex("eead9d67890cbb22392336fea1851f38");
166        let iv = bytes::from_hex("000000000102030405060708");
167        let plaintext = bytes::from_hex("496e7465726e65742d4472616674732061726520647261667420646f63756d656e74732076616c696420666f722061206d6178696d756d206f6620736978206d6f6e74687320616e64206d617920626520757064617465642c207265706c616365642c206f72206f62736f6c65746564206279206f7468657220646f63756d656e747320617420616e792074696d652e20497420697320696e617070726f70726961746520746f2075736520496e7465726e65742d447261667473206173207265666572656e6365206d6174657269616c206f7220746f2063697465207468656d206f74686572207468616e206173202fe2809c776f726b20696e2070726f67726573732e2fe2809d");
168
169        let decrypted = Poly1305::default()
170            .decrypt(&key, &iv, &ciphertext, &aad, &tag)
171            .unwrap();
172
173        assert_eq!(decrypted, plaintext);
174    }
175}