aboutsummaryrefslogtreecommitdiffstats
path: root/src/xchacha20poly1305.rs
diff options
context:
space:
mode:
authorJake McGinty <me@jake.su>2018-02-21 05:01:42 +0000
committerJake McGinty <me@jake.su>2018-02-21 05:37:58 +0000
commitb5c51352571e5b59123fd9b5d8c98a92c2a275b7 (patch)
treeead927d3e387f0ce2e2418995a3f31d6baea327a /src/xchacha20poly1305.rs
parentuse Failure crate in snow, get rid of nasty sync adaptors (diff)
downloadwireguard-rs-b5c51352571e5b59123fd9b5d8c98a92c2a275b7.tar.xz
wireguard-rs-b5c51352571e5b59123fd9b5d8c98a92c2a275b7.zip
mac2 cookies
Diffstat (limited to 'src/xchacha20poly1305.rs')
-rw-r--r--src/xchacha20poly1305.rs230
1 files changed, 230 insertions, 0 deletions
diff --git a/src/xchacha20poly1305.rs b/src/xchacha20poly1305.rs
new file mode 100644
index 0000000..da502c1
--- /dev/null
+++ b/src/xchacha20poly1305.rs
@@ -0,0 +1,230 @@
+use byteorder::{ByteOrder, LittleEndian};
+use chacha20_poly1305_aead;
+use failure::Error;
+use std::io::Cursor;
+use std::num::Wrapping;
+
+// directly ported from wireguard-go's implementation.
+fn hchacha20(key: &[u8], nonce: &[u8], out: &mut [u8]) {
+ let mut v00 = Wrapping(0x61707865 as u32);
+ let mut v01 = Wrapping(0x3320646e as u32);
+ let mut v02 = Wrapping(0x79622d32 as u32);
+ let mut v03 = Wrapping(0x6b206574 as u32);
+
+ let mut v04 = Wrapping(LittleEndian::read_u32(&key[0..]));
+ let mut v05 = Wrapping(LittleEndian::read_u32(&key[4..]));
+ let mut v06 = Wrapping(LittleEndian::read_u32(&key[8..]));
+ let mut v07 = Wrapping(LittleEndian::read_u32(&key[12..]));
+ let mut v08 = Wrapping(LittleEndian::read_u32(&key[16..]));
+ let mut v09 = Wrapping(LittleEndian::read_u32(&key[20..]));
+ let mut v10 = Wrapping(LittleEndian::read_u32(&key[24..]));
+ let mut v11 = Wrapping(LittleEndian::read_u32(&key[28..]));
+ let mut v12 = Wrapping(LittleEndian::read_u32(&nonce[0..]));
+ let mut v13 = Wrapping(LittleEndian::read_u32(&nonce[4..]));
+ let mut v14 = Wrapping(LittleEndian::read_u32(&nonce[8..]));
+ let mut v15 = Wrapping(LittleEndian::read_u32(&nonce[12..]));
+
+ let mut i = 0;
+ while i < 20 {
+ v00 += v04;
+ v12 ^= v00;
+ v12 = (v12 << 16) | (v12 >> 16);
+ v08 += v12;
+ v04 ^= v08;
+ v04 = (v04 << 12) | (v04 >> 20);
+ v00 += v04;
+ v12 ^= v00;
+ v12 = (v12 << 8) | (v12 >> 24);
+ v08 += v12;
+ v04 ^= v08;
+ v04 = (v04 << 7) | (v04 >> 25);
+ v01 += v05;
+ v13 ^= v01;
+ v13 = (v13 << 16) | (v13 >> 16);
+ v09 += v13;
+ v05 ^= v09;
+ v05 = (v05 << 12) | (v05 >> 20);
+ v01 += v05;
+ v13 ^= v01;
+ v13 = (v13 << 8) | (v13 >> 24);
+ v09 += v13;
+ v05 ^= v09;
+ v05 = (v05 << 7) | (v05 >> 25);
+ v02 += v06;
+ v14 ^= v02;
+ v14 = (v14 << 16) | (v14 >> 16);
+ v10 += v14;
+ v06 ^= v10;
+ v06 = (v06 << 12) | (v06 >> 20);
+ v02 += v06;
+ v14 ^= v02;
+ v14 = (v14 << 8) | (v14 >> 24);
+ v10 += v14;
+ v06 ^= v10;
+ v06 = (v06 << 7) | (v06 >> 25);
+ v03 += v07;
+ v15 ^= v03;
+ v15 = (v15 << 16) | (v15 >> 16);
+ v11 += v15;
+ v07 ^= v11;
+ v07 = (v07 << 12) | (v07 >> 20);
+ v03 += v07;
+ v15 ^= v03;
+ v15 = (v15 << 8) | (v15 >> 24);
+ v11 += v15;
+ v07 ^= v11;
+ v07 = (v07 << 7) | (v07 >> 25);
+ v00 += v05;
+ v15 ^= v00;
+ v15 = (v15 << 16) | (v15 >> 16);
+ v10 += v15;
+ v05 ^= v10;
+ v05 = (v05 << 12) | (v05 >> 20);
+ v00 += v05;
+ v15 ^= v00;
+ v15 = (v15 << 8) | (v15 >> 24);
+ v10 += v15;
+ v05 ^= v10;
+ v05 = (v05 << 7) | (v05 >> 25);
+ v01 += v06;
+ v12 ^= v01;
+ v12 = (v12 << 16) | (v12 >> 16);
+ v11 += v12;
+ v06 ^= v11;
+ v06 = (v06 << 12) | (v06 >> 20);
+ v01 += v06;
+ v12 ^= v01;
+ v12 = (v12 << 8) | (v12 >> 24);
+ v11 += v12;
+ v06 ^= v11;
+ v06 = (v06 << 7) | (v06 >> 25);
+ v02 += v07;
+ v13 ^= v02;
+ v13 = (v13 << 16) | (v13 >> 16);
+ v08 += v13;
+ v07 ^= v08;
+ v07 = (v07 << 12) | (v07 >> 20);
+ v02 += v07;
+ v13 ^= v02;
+ v13 = (v13 << 8) | (v13 >> 24);
+ v08 += v13;
+ v07 ^= v08;
+ v07 = (v07 << 7) | (v07 >> 25);
+ v03 += v04;
+ v14 ^= v03;
+ v14 = (v14 << 16) | (v14 >> 16);
+ v09 += v14;
+ v04 ^= v09;
+ v04 = (v04 << 12) | (v04 >> 20);
+ v03 += v04;
+ v14 ^= v03;
+ v14 = (v14 << 8) | (v14 >> 24);
+ v09 += v14;
+ v04 ^= v09;
+ v04 = (v04 << 7) | (v04 >> 25);
+ i += 2;
+ }
+
+ LittleEndian::write_u32(&mut out[0..], v00.0);
+ LittleEndian::write_u32(&mut out[4..], v01.0);
+ LittleEndian::write_u32(&mut out[8..], v02.0);
+ LittleEndian::write_u32(&mut out[12..], v03.0);
+ LittleEndian::write_u32(&mut out[16..], v12.0);
+ LittleEndian::write_u32(&mut out[20..], v13.0);
+ LittleEndian::write_u32(&mut out[24..], v14.0);
+ LittleEndian::write_u32(&mut out[28..], v15.0);
+}
+
+pub fn encrypt(key: &[u8], nonce: &[u8], input: &[u8], aad: &[u8], output: &mut [u8]) -> Result<[u8; 16], Error> {
+ ensure!(key.len() == 32, "wrong key len");
+ ensure!(nonce.len() == 24, "wrong nonce len");
+ ensure!(output.len() == input.len(), "output buffer not same length and input");
+
+ let mut derived_key = [0u8; 32];
+ let mut derived_nonce = [0u8; 12];
+ hchacha20(key, &nonce[..16], &mut derived_key);
+ derived_nonce[4..].copy_from_slice(&nonce[16..]);
+
+ let mut buf = Cursor::new(output);
+ Ok(chacha20_poly1305_aead::encrypt(&derived_key, &derived_nonce, aad, input, &mut buf)?)
+}
+
+pub fn decrypt(key: &[u8], nonce: &[u8], input: &[u8], aad: &[u8], tag: &[u8], output: &mut [u8]) -> Result<(), Error> {
+ ensure!(key.len() == 32, "wrong key len");
+ ensure!(nonce.len() == 24, "wrong nonce len");
+ ensure!(output.len() == input.len(), "output buffer not same length and input");
+
+ let mut derived_key = [0u8; 32];
+ let mut derived_nonce = [0u8; 12];
+ hchacha20(key, &nonce[..16], &mut derived_key);
+ derived_nonce[4..].copy_from_slice(&nonce[16..]);
+
+ let mut buf = Cursor::new(output);
+ Ok(chacha20_poly1305_aead::decrypt(&derived_key, &derived_nonce, aad, input, tag, &mut buf)?)
+}
+
+#[test]
+fn sanity() {
+ let key = &[1u8; 32];
+ let nonce = &[2u8; 24];
+ let aad = &[3u8; 16];
+ let input = b"Readings fluxuated momentarily. It appeared to be a ship, but then it vanished.";
+ let mut enc = vec![0u8; input.len()];
+ let mut dec = vec![0u8; input.len()];
+
+ let tag = encrypt(key, nonce, input, aad, &mut enc).unwrap();
+
+ decrypt(key, nonce, &enc, aad, &tag, &mut dec).unwrap();
+
+ assert!(&input[..] == &dec[..]);
+}
+
+#[test]
+fn wireguard_go_vectors() {
+ use hex::{encode, decode};
+ struct XChaCha20Test {
+ nonce: &'static str,
+ key: &'static str,
+ pt: &'static str,
+ ct: &'static str,
+ };
+ let tests = [
+ XChaCha20Test {
+ nonce: "000000000000000000000000000000000000000000000000",
+ key: "0000000000000000000000000000000000000000000000000000000000000000",
+ pt: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ ct: "789e9689e5208d7fd9e1f3c5b5341f48ef18a13e418998addadd97a3693a987f8e82ecd5c1433bfed1af49750c0f1ff29c4174a05b119aa3a9e8333812e0c0feb1299c5949d895ee01dbf50f8395dd84",
+ },
+ XChaCha20Test {
+ nonce: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
+ key: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
+ pt: "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f",
+ ct: "e1a046aa7f71e2af8b80b6408b2fd8d3a350278cde79c94d9efaa475e1339b3dd490127b",
+ },
+ XChaCha20Test {
+ nonce: "d9a8213e8a697508805c2c171ad54487ead9e3e02d82d5bc",
+ key: "979196dbd78526f2f584f7534db3f5824d8ccfa858ca7e09bdd3656ecd36033c",
+ pt: "43cc6d624e451bbed952c3e071dc6c03392ce11eb14316a94b2fdc98b22fedea",
+ ct: "53c1e8bef2dbb8f2505ec010a7afe21d5a8e6dd8f987e4ea1a2ed5dfbc844ea400db34496fd2153526c6e87c36694200",
+ }
+ ];
+
+ for test in tests.iter() {
+ let nonce = decode(test.nonce).unwrap();
+ let key = decode(test.key).unwrap();
+ let pt = decode(test.pt).unwrap();
+ let ct = decode(test.ct).unwrap();
+
+ let mut buf = vec![0u8; pt.len()];
+ let tag = encrypt(&key, &nonce, &pt, &[], &mut buf[..]).unwrap();
+
+ println!("\nthr {}", test.ct);
+ println!("our {}", encode(&buf));
+ assert!(&[&buf[..], &tag[..]].concat() == &ct);
+
+ decrypt(&key, &nonce, &ct[..pt.len()], &[], &tag, &mut buf[..]).unwrap();
+ println!("\nthr {}", test.pt);
+ println!("our {}", encode(&buf));
+ assert!(&buf == &pt);
+ }
+}