Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes to cryptobox API #712

Merged
merged 6 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -146,5 +146,5 @@ bulletproofs = { git = "https://github.com/dalek-cryptography/bulletproofs", rev
cpuid-bool = { git = "https://github.com/eranrund/RustCrypto-utils", rev = "74f8e04e9d18d93fc6d05c72756c236dc88daa19" }

# We need to patch aes-gcm so we can make some fields/functions/structs pub in order to have a constant time decrypt
aes-gcm = { git = "https://github.com/xoloki/AEADs", rev = "d1a8517d3dd867ed9c5794002add67992a42f6aa" }
cbeck88 marked this conversation as resolved.
Show resolved Hide resolved
aes-gcm = { git = "https://github.com/mobilecoinofficial/AEADs", rev = "d1a8517d3dd867ed9c5794002add67992a42f6aa" }

15 changes: 7 additions & 8 deletions consensus/enclave/trusted/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions consensus/enclave/trusted/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ bulletproofs = { git = "https://github.com/dalek-cryptography/bulletproofs", rev
cpuid-bool = { git = "https://github.com/eranrund/RustCrypto-utils", rev = "74f8e04e9d18d93fc6d05c72756c236dc88daa19" }

# We need to patch aes-gcm so we can make some fields/functions/structs pub in order to have a constant time decrypt
aes-gcm = { git = "https://github.com/xoloki/AEADs", rev = "d1a8517d3dd867ed9c5794002add67992a42f6aa" }

aes-gcm = { git = "https://github.com/mobilecoinofficial/AEADs", rev = "d1a8517d3dd867ed9c5794002add67992a42f6aa" }

[workspace]
5 changes: 3 additions & 2 deletions crypto/box/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ authors = ["MobileCoin"]
edition = "2018"

[dependencies]
aead = "0.3"
aes-gcm = { version = "0.6.0" }
blake2 = { version = "0.9", default-features = false }
digest = { version = "0.9" }
failure = { version = "0.1.8", default-features = false }
hkdf = { version = "0.9.0", default-features = false }
rand_core = { version = "0.5", default-features = false }

mc-crypto-ct-aead = { path = "../ct-aead" }
mc-crypto-keys = { path = "../keys", default-features = false }
rand_core = { version = "0.5", default-features = false }


[dev_dependencies]
mc-util-from-random = { path = "../../util/from-random" }
Expand Down
1 change: 1 addition & 0 deletions crypto/box/src/fixed_buffer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::aead;
use aead::{Buffer, Error};

/// The rust aead crate is organized around a Buffer trait which abstracts
Expand Down
26 changes: 13 additions & 13 deletions crypto/box/src/hkdf_blake2b_aes_128_gcm.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
use crate::traits::{CryptoBox, Error};

use aead::{
generic_array::{
sequence::{Concat, Split},
typenum::{Sum, U12, U16, U32},
GenericArray,
use crate::{
aead::{
generic_array::{
sequence::{Concat, Split},
typenum::{Sum, U12, U16, U32},
GenericArray,
},
Aead, Error as AeadError, NewAead,
},
Aead, Error as AeadError, NewAead,
traits::{CryptoBox, Error},
};

use aes_gcm::Aes128Gcm;
use blake2::Blake2b;
use core::convert::TryFrom;
use hkdf::Hkdf;
use mc_crypto_ct_aead::CtAeadDecrypt;
use mc_crypto_keys::{
CompressedRistrettoPublic, RistrettoPrivate, RistrettoPublic, RISTRETTO_PUBLIC_LEN,
};
Expand Down Expand Up @@ -70,7 +73,7 @@ impl CryptoBox for RistrettoHkdfBlake2bAes128Gcm {
key: &RistrettoPrivate,
tag: &GenericArray<u8, Self::FooterSize>,
buffer: &mut [u8],
) -> Result<(), Error> {
) -> Result<CtDecryptResult, Error> {
// ECDH
use mc_crypto_keys::KexReusablePrivate;
let curve_point_bytes =
Expand All @@ -94,10 +97,7 @@ impl CryptoBox for RistrettoHkdfBlake2bAes128Gcm {
// AES
let mac_ref = <&GenericArray<u8, AesMacLen>>::from(&tag[RISTRETTO_PUBLIC_LEN..]);
let aead = Aes128Gcm::new(aes_key);
aead.decrypt_in_place_detached(&aes_nonce, &aad, buffer, mac_ref)
.map_err(|_| Error::MacFailed)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 bugs here: 1 still using Error::MacFailed, 2 not using the CtAead interface

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was issue (3)


Ok(())
Ok(aead.ct_decrypt_in_place_detached(&aes_nonce, &aad, buffer, mac_ref));
}
}

Expand Down
22 changes: 12 additions & 10 deletions crypto/box/src/hkdf_box.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
use crate::traits::{CryptoBox, Error};

use aead::{
generic_array::{
sequence::{Concat, Split},
typenum::{Sum, Unsigned},
ArrayLength, GenericArray,
use crate::{
aead::{
generic_array::{
sequence::{Concat, Split},
typenum::{Sum, Unsigned},
ArrayLength, GenericArray,
},
AeadInPlace, Error as AeadError, NewAead,
},
AeadInPlace, Error as AeadError, NewAead,
traits::{CryptoBox, Error},
};

use core::{
convert::TryFrom,
marker::PhantomData,
ops::{Add, Sub},
};
use digest::{BlockInput, Digest, FixedOutput, Reset, Update};
use hkdf::Hkdf;
use mc_crypto_ct_aead::CtAeadDecrypt;
use mc_crypto_ct_aead::{CtAeadDecrypt, CtDecryptResult};
use mc_crypto_keys::{Kex, ReprBytes};
use rand_core::{CryptoRng, RngCore};

Expand Down Expand Up @@ -93,7 +95,7 @@ where
key: &KexAlgo::Private,
tag: &GenericArray<u8, Self::FooterSize>,
buffer: &mut [u8],
) -> Result<bool, Error> {
) -> Result<CtDecryptResult, Error> {
// ECDH
use mc_crypto_keys::KexReusablePrivate;
// TODO: In generic_array 0.14 the tag can be split without copying it
Expand Down
13 changes: 5 additions & 8 deletions crypto/box/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
extern crate alloc;

pub use aead::generic_array;
pub use mc_crypto_ct_aead::aead;

mod hkdf_box;
mod traits;
Expand Down Expand Up @@ -55,7 +56,7 @@ mod test {
algo.decrypt(&a, &ciphertext).expect("decryption failed!");
assert_eq!(plaintext.len(), decrypted.len());
assert_eq!(plaintext, &&decrypted[..]);
assert_eq!(success, true);
assert_eq!(bool::from(success), true);
}
}
});
Expand All @@ -76,12 +77,8 @@ mod test {
for plaintext in &[&plaintext1[..], &plaintext2[..]] {
for _reps in 0..50 {
let ciphertext = algo.encrypt(&mut rng, &a_pub, plaintext).unwrap();
let decrypted = algo.decrypt(&not_a, &ciphertext);
if decrypted.is_err() {
assert_eq!(decrypted, Err(Error::MacFailed));
} else {
assert_eq!(decrypted.unwrap().0, false);
}
let (success, _decrypted) = algo.decrypt(&not_a, &ciphertext).unwrap();
assert_eq!(bool::from(success), false);
}
}
});
Expand All @@ -106,7 +103,7 @@ mod test {
.decrypt_fixed_length(&a, &ciphertext)
.expect("decryption failed!");
assert_eq!(plaintext, &decrypted);
assert_eq!(success, true);
assert_eq!(bool::from(success), true);
}
}
});
Expand Down
23 changes: 16 additions & 7 deletions crypto/box/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::aead;
use alloc::vec::Vec;

use aead::{
Expand All @@ -10,10 +11,15 @@ use aead::{
};
use core::ops::{Add, Sub};
use failure::Fail;
use mc_crypto_ct_aead::CtDecryptResult;
use mc_crypto_keys::{Kex, KeyError};
use rand_core::{CryptoRng, RngCore};

/// Error type for decryption
///
/// Note that mac failed is indicated separately from this enum,
/// because a design goal is that we can be constant-time with respect to mac failure,
/// but enum cannot be matched or accessed without branching.
#[derive(PartialEq, Eq, Fail, Debug)]
pub enum Error {
#[fail(display = "Error decoding curvepoint: {}", _0)]
Expand All @@ -23,8 +29,6 @@ pub enum Error {
_0, _1
)]
TooShort(usize, usize),
#[fail(display = "Mac failed")]
MacFailed,
#[fail(display = "Unknown algorithm code: {}", _0)]
UnknownAlgorithm(usize),
#[fail(display = "Wrong magic bytes")]
Expand Down Expand Up @@ -56,13 +60,14 @@ pub trait CryptoBox<KexAlgo: Kex>: Default {
///
/// Returns
/// `Ok(true)` if decryption succeeds. `buffer` contains the plaintext and SHOULD be zeroized after use.
/// `Ok(false) if MAC check fails. `buffer` contains failed plaintext and SHOULD be zeroized after use.
/// `Ok(false) if MAC check / aead fails. `buffer` contains failed plaintext and SHOULD be zeroized after use.
/// `Err` if something is wrong with the cryptogram format.
fn decrypt_in_place_detached(
&self,
key: &KexAlgo::Private,
footer: &GenericArray<u8, Self::FooterSize>,
buffer: &mut [u8],
) -> Result<bool, Error>;
) -> Result<CtDecryptResult, Error>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this (and similar changes) is solution to (2)


// Provided functions
// These functions consume and produce "cryptograms" where the footer bytes
Expand All @@ -87,7 +92,11 @@ pub trait CryptoBox<KexAlgo: Kex>: Default {
/// If status is false then mac check failed and plaintext should be zeroed.
///
/// Meant to mirror aead::decrypt
fn decrypt(&self, key: &KexAlgo::Private, cryptogram: &[u8]) -> Result<(bool, Vec<u8>), Error> {
fn decrypt(
&self,
key: &KexAlgo::Private,
cryptogram: &[u8],
) -> Result<(CtDecryptResult, Vec<u8>), Error> {
Copy link
Contributor Author

@cbeck88 cbeck88 Feb 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's a good argument that this should be made to return Zeroizing<Vec<u8>>, that would mean that all the callers in SDKs etc. dont need to explicitly zeroize this. zeroize is pretty lightweight crate that I think most things already rely on via transaction-core

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this passed deploy, and i decided to punt on zeroize for now because i can't find a good solution for zeroizing generic array. I posted a pull request here:

fizyk20/generic-array#112

let mut result = cryptogram.to_vec();
let status = self.decrypt_in_place(key, &mut result)?;
Ok((status, result))
Expand Down Expand Up @@ -127,7 +136,7 @@ pub trait CryptoBox<KexAlgo: Kex>: Default {
&self,
key: &KexAlgo::Private,
cryptogram: &mut impl aead::Buffer,
) -> Result<bool, Error> {
) -> Result<CtDecryptResult, Error> {
// Extract the footer from end of ciphertext, doing bounds checks
if cryptogram.len() < Self::FooterSize::USIZE {
return Err(Error::TooShort(cryptogram.len(), Self::FooterSize::USIZE));
Expand Down Expand Up @@ -175,7 +184,7 @@ pub trait CryptoBox<KexAlgo: Kex>: Default {
&self,
key: &KexAlgo::Private,
cryptogram: &GenericArray<u8, L>,
) -> Result<(bool, GenericArray<u8, Diff<L, Self::FooterSize>>), Error>
) -> Result<(CtDecryptResult, GenericArray<u8, Diff<L, Self::FooterSize>>), Error>
where
L: ArrayLength<u8>
+ Sub<Self::FooterSize>
Expand Down
31 changes: 14 additions & 17 deletions crypto/box/src/versioned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,24 @@
//! 0 = hkdf_blake2b_aes_128_gcm

use crate::{
aead::{
generic_array::{
arr,
sequence::Concat,
typenum::{Unsigned, U50},
GenericArray,
},
Error as AeadError,
},
hkdf_box::HkdfBox,
traits::{CryptoBox, Error},
};

use aead::{
generic_array::{
arr,
sequence::Concat,
typenum::{Unsigned, U50},
GenericArray,
},
Error as AeadError,
};
use aes_gcm::Aes128Gcm;
use alloc::vec::Vec;
use blake2::Blake2b;
use failure::Fail;
use mc_crypto_ct_aead::CtDecryptResult;
use mc_crypto_keys::{Kex, Ristretto};
use rand_core::{CryptoRng, RngCore};

Expand Down Expand Up @@ -141,7 +142,7 @@ impl CryptoBox<Ristretto> for VersionedCryptoBox {
key: &<Ristretto as Kex>::Private,
footer: &GenericArray<u8, Self::FooterSize>,
buffer: &mut [u8],
) -> Result<bool, Error> {
) -> Result<CtDecryptResult, Error> {
// Note: When generic_array is upreved, this can be tidier using this:
// https://docs.rs/generic-array/0.14.1/src/generic_array/sequence.rs.html#302-320
// For now we have to split as a slice, then convert back to Generic Array.
Expand Down Expand Up @@ -190,7 +191,7 @@ mod test {
algo.decrypt(&a, &ciphertext).expect("decryption failed!");
assert_eq!(plaintext.len(), decrypted.len());
assert_eq!(plaintext, &&decrypted[..]);
assert_eq!(success, true);
assert!(bool::from(success));
}
}
});
Expand All @@ -211,12 +212,8 @@ mod test {
for plaintext in &[&plaintext1[..], &plaintext2[..]] {
for _reps in 0..50 {
let ciphertext = algo.encrypt(&mut rng, &a_pub, plaintext).unwrap();
let decrypted = algo.decrypt(&not_a, &ciphertext);
if decrypted.is_err() {
assert_eq!(decrypted, Err(Error::MacFailed));
} else {
assert_eq!(decrypted.unwrap().0, false);
}
let (success, _decrypted) = algo.decrypt(&not_a, &ciphertext).unwrap();
assert!(!bool::from(success));
}
}
});
Expand Down