diff options
author | Jake McGinty <me@jake.su> | 2018-02-21 05:01:42 +0000 |
---|---|---|
committer | Jake McGinty <me@jake.su> | 2018-02-21 05:37:58 +0000 |
commit | b5c51352571e5b59123fd9b5d8c98a92c2a275b7 (patch) | |
tree | ead927d3e387f0ce2e2418995a3f31d6baea327a /src/xchacha20poly1305.rs | |
parent | use Failure crate in snow, get rid of nasty sync adaptors (diff) | |
download | wireguard-rs-b5c51352571e5b59123fd9b5d8c98a92c2a275b7.tar.xz wireguard-rs-b5c51352571e5b59123fd9b5d8c98a92c2a275b7.zip |
mac2 cookies
Diffstat (limited to 'src/xchacha20poly1305.rs')
-rw-r--r-- | src/xchacha20poly1305.rs | 230 |
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); + } +} |