Skip to content

Commit

Permalink
Re-add AES-GCM to default resolver
Browse files Browse the repository at this point in the history
Simplify error handling


Remove allocations and re-enable AES tests


Fix import conflict
  • Loading branch information
BlackHoleFox authored and mcginty committed Feb 12, 2020
1 parent 8eab060 commit 01535e7
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 13 deletions.
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ edition = "2018"
# and -accelerated suffix means that this resolver will be the default used by the Builder.
[features]
default = ["default-resolver"]
default-resolver = ["aes-gcm", "chacha20poly1305", "blake2", "sha2", "x25519-dalek", "rand"]
nightly = ["blake2/simd_opt", "x25519-dalek/nightly", "subtle/nightly"]
default-resolver = ["chacha20poly1305", "blake2", "sha2", "x25519-dalek", "rand"]
ring-resolver = ["ring"]
ring-accelerated = ["ring-resolver", "default-resolver"]
vector-tests = []
Expand All @@ -38,6 +38,7 @@ rand_core = "0.5"
subtle = "2.1"

# default crypto provider
aes-gcm = { version = "0.3", optional = true }
chacha20poly1305 = { version = "0.3", optional = true }
blake2 = { version = "0.8", optional = true }
rand = { version = "0.7", optional = true }
Expand All @@ -56,7 +57,7 @@ serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"
hex = "0.4"
lazy_static = "1.3"
lazy_static = "1.4"

[build-dependencies]
rustc_version = "0.2"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ However, a not all features have been implemented yet (pull requests welcome):
Cryptographic providers are swappable through `Builder::with_resolver()`, but by default it chooses select, artisanal
pure-Rust implementations (see `Cargo.toml` for a quick overview).

### Providers
### Other Providers

#### ring

Expand All @@ -64,7 +64,7 @@ If you enable the `ring-accelerated` feature, Snow will default to choosing `rin
| CSPRNG |||
| 25519 |||
| 448 | | |
| AESGCM | ||
| AESGCM | ||
| ChaChaPoly |||
| SHA256 |||
| SHA512 |||
Expand Down
100 changes: 99 additions & 1 deletion src/resolvers/default.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use arrayref::array_ref;
use blake2::Blake2b;
use blake2::Blake2s;
use aes_gcm;
use sha2::{Digest, Sha256, Sha512};
use rand::rngs::OsRng;
use x25519_dalek as x25519;
Expand Down Expand Up @@ -46,7 +47,7 @@ impl CryptoResolver for DefaultResolver {
fn resolve_cipher(&self, choice: &CipherChoice) -> Option<Box<dyn Cipher>> {
match *choice {
CipherChoice::ChaChaPoly => Some(Box::new(CipherChaChaPoly::default())),
CipherChoice::AESGCM => None,
CipherChoice::AESGCM => Some(Box::new(CipherAesGcm::default())),
}
}

Expand All @@ -66,6 +67,12 @@ struct Dh25519 {
pubkey: [u8; 32],
}

/// Wraps `aes-gcm`'s AES256-GCM implementation.
#[derive(Default)]
struct CipherAesGcm {
key: [u8; 32],
}

/// Wraps `chacha20_poly1305_aead`'s ChaCha20Poly1305 implementation.
#[derive(Default)]
struct CipherChaChaPoly {
Expand Down Expand Up @@ -141,6 +148,58 @@ impl Dh for Dh25519 {
}
}

impl Cipher for CipherAesGcm {
fn name(&self) -> &'static str {
"AESGCM"
}

fn set(&mut self, key: &[u8]) {
copy_slices!(key, &mut self.key)
}

fn encrypt(&self, nonce: u64, authtext: &[u8], plaintext: &[u8], out: &mut[u8]) -> usize {
let aead = aes_gcm::Aes256Gcm::new(self.key.into());

let mut nonce_bytes = [0u8; 12];
copy_slices!(&nonce.to_be_bytes(), &mut nonce_bytes[4..]);

copy_slices!(plaintext, out);

let tag = aead.encrypt_in_place_detached(
&nonce_bytes.into(),
authtext,
&mut out[0..plaintext.len()]
)
.expect("Encryption failed!");

copy_slices!(tag, &mut out[plaintext.len()..]);

plaintext.len() + TAGLEN
}

fn decrypt(&self, nonce: u64, authtext: &[u8], ciphertext: &[u8], out: &mut[u8]) -> Result<usize, ()> {
let aead = aes_gcm::Aes256Gcm::new(self.key.into());

let mut nonce_bytes = [0u8; 12];
copy_slices!(&nonce.to_be_bytes(), &mut nonce_bytes[4..]);

let message_len = ciphertext.len() - TAGLEN;

copy_slices!(ciphertext[..message_len], out);

aead.decrypt_in_place_detached(
&nonce_bytes.into(),
authtext,
&mut out[..message_len],
ciphertext[message_len..].into()
)
.and_then(|_| {
Ok(message_len)
})
.map_err(|_| ())
}
}

impl Cipher for CipherChaChaPoly {

fn name(&self) -> &'static str {
Expand Down Expand Up @@ -462,6 +521,45 @@ mod tests {
assert!(hex::encode(output) == "c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552");
}

#[test]
fn test_aesgcm() {
// AES256-GCM tests - gcm-spec.pdf
// Test Case 13
let key = [0u8; 32];
let nonce = 0u64;
let plaintext = [0u8; 0];
let authtext = [0u8; 0];
let mut ciphertext = [0u8; 16];
let mut cipher1: CipherAesGcm = Default::default();
cipher1.set(&key);
cipher1.encrypt(nonce, &authtext, &plaintext, &mut ciphertext);
assert!(hex::encode(ciphertext) == "530f8afbc74536b9a963b4f1c4cb738b");

let mut resulttext = [0u8; 1];
let mut cipher2: CipherAesGcm = Default::default();
cipher2.set(&key);
cipher2.decrypt(nonce, &authtext, &ciphertext, &mut resulttext).unwrap();
assert!(resulttext[0] == 0);
ciphertext[0] ^= 1;
assert!(cipher2.decrypt(nonce, &authtext, &ciphertext, &mut resulttext).is_err());

// Test Case 14
let plaintext2 = [0u8; 16];
let mut ciphertext2 = [0u8; 32];
let mut cipher3: CipherAesGcm = Default::default();
cipher3.set(&key);
cipher3.encrypt(nonce, &authtext, &plaintext2, &mut ciphertext2);
assert!(hex::encode(ciphertext2) == "cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919");

let mut resulttext2 = [1u8; 16];
let mut cipher4: CipherAesGcm = Default::default();
cipher4.set(&key);
cipher4.decrypt(nonce, &authtext, &ciphertext2, &mut resulttext2).unwrap();
assert!(plaintext2 == resulttext2);
ciphertext2[0] ^= 1;
assert!(cipher4.decrypt(nonce, &authtext, &ciphertext2, &mut resulttext2).is_err());
}

#[test]
fn test_chachapoly_empty() {
//ChaChaPoly round-trip test, empty plaintext
Expand Down
64 changes: 63 additions & 1 deletion tests/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ fn test_noise_state_change() {
}

#[test]
fn test_sanity_session() {
fn test_sanity_chachapoly_session() {
let params: NoiseParams = "Noise_NN_25519_ChaChaPoly_SHA256".parse().unwrap();
let mut h_i = Builder::new(params.clone()).build_initiator().unwrap();
let mut h_r = Builder::new(params).build_responder().unwrap();
Expand All @@ -151,6 +151,68 @@ fn test_sanity_session() {
assert_eq!(&buffer_out[..len], b"hack the planet");
}

#[test]
fn test_sanity_aesgcm_session() {
let params: NoiseParams = "Noise_NN_25519_AESGCM_SHA256".parse().unwrap();
let mut h_i = Builder::new(params.clone()).build_initiator().unwrap();
let mut h_r = Builder::new(params).build_responder().unwrap();

let mut buffer_msg = [0u8; 200];
let mut buffer_out = [0u8; 200];
let len = h_i.write_message(b"abc", &mut buffer_msg).unwrap();
h_r.read_message(&buffer_msg[..len], &mut buffer_out).unwrap();

let len = h_r.write_message(b"defg", &mut buffer_msg).unwrap();
h_i.read_message(&buffer_msg[..len], &mut buffer_out).unwrap();

let mut h_i = h_i.into_transport_mode().unwrap();
let mut h_r = h_r.into_transport_mode().unwrap();

let len = h_i.write_message(b"hack the planet", &mut buffer_msg).unwrap();
let len = h_r.read_message(&buffer_msg[..len], &mut buffer_out).unwrap();
assert_eq!(&buffer_out[..len], b"hack the planet");
}

#[test]
fn test_Npsk0_chachapoly_expected_value() {
let params: NoiseParams = "Noise_Npsk0_25519_ChaChaPoly_SHA256".parse().unwrap();
let mut h_i = Builder::new(params)
.remote_public_key(&x25519::x25519(get_inc_key(0), x25519::X25519_BASEPOINT_BYTES))
.psk(0, &get_inc_key(1))
.fixed_ephemeral_key_for_testing_only(&get_inc_key(32))
.build_initiator().unwrap();

let mut buf = [0u8; 200];
let len = h_i.write_message(&[], &mut buf).unwrap();
assert_eq!(len, 48);

let expected = Vec::<u8>::from_hex("358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd166254deb8a4f6190117dea09aad7546a4658c").unwrap();

println!("\nreality: {}", hex::encode(&buf[..len]));
println!("expected: {}", hex::encode(&expected));
assert_eq!(&buf[..len], &expected[..]);
}

#[test]
fn test_Npsk0_aesgcm_expected_value() {
let params: NoiseParams = "Noise_Npsk0_25519_AESGCM_SHA256".parse().unwrap();
let mut h_i = Builder::new(params)
.remote_public_key(&x25519::x25519(get_inc_key(0), x25519::X25519_BASEPOINT_BYTES))
.psk(0, &get_inc_key(1))
.fixed_ephemeral_key_for_testing_only(&get_inc_key(32))
.build_initiator().unwrap();

let mut buf = [0u8; 200];
let len = h_i.write_message(&[], &mut buf).unwrap();
assert_eq!(len, 48);

let expected = Vec::<u8>::from_hex("358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd1662542044ae563929068930dcf04674526cb9").unwrap();

println!("\nreality: {}", hex::encode(&buf[..len]));
println!("expected: {}", hex::encode(&expected));
assert_eq!(&buf[..len], &expected[..]);
}

#[test]
fn test_Npsk0_expected_value() {
let params: NoiseParams = "Noise_Npsk0_25519_ChaChaPoly_SHA256".parse().unwrap();
Expand Down
8 changes: 1 addition & 7 deletions tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,11 @@ fn test_vectors_from_json(json: &str) {
for vector in test_vectors.vectors {
let params: NoiseParams = vector.protocol_name.parse().unwrap();

if !cfg!(feature = "ring-accelerated") {
if params.cipher == CipherChoice::AESGCM {
ignored += 1;
continue
}
}

if params.dh == DHChoice::Ed448 {
ignored += 1;
continue;
}

let (init, resp) = match build_session_pair(&vector) {
Ok((init, resp)) => (init, resp),
Err(s) => {
Expand Down

0 comments on commit 01535e7

Please sign in to comment.