fix: aead unit tests no longer deadlock

* fix: add back unit tests and configure rayon threadpool

* Use env var to set rayon threads.

* Remove `rayon` dev dependency.
This commit is contained in:
th4s
2024-09-02 09:06:38 +02:00
committed by GitHub
parent d179150c39
commit 32df1380a7
3 changed files with 297 additions and 294 deletions

View File

@@ -13,6 +13,12 @@ on:
env:
CARGO_TERM_COLOR: always
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
# We need a higher number of parallel rayon tasks than the default (which is 4)
# in order to prevent a deadlock, c.f.
# - https://github.com/tlsnotary/tlsn/issues/548
# - https://github.com/privacy-scaling-explorations/mpz/issues/178
# 32 seems to be big enough for the foreseeable future
RAYON_NUM_THREADS: 32
jobs:
fmt:

View File

@@ -19,10 +19,10 @@ impl AesGcmError {
}
}
// #[cfg(test)]
// pub(crate) fn kind(&self) -> ErrorKind {
// self.kind
// }
#[cfg(test)]
pub(crate) fn kind(&self) -> ErrorKind {
self.kind
}
pub(crate) fn invalid_tag() -> Self {
Self {

View File

@@ -423,294 +423,291 @@ impl<Ctx: Context> Aead for MpcAesGcm<Ctx> {
#[cfg(test)]
mod tests {
// TODO: The following tests time-out on Github CI pipeline, which is not reproducible local.
// Needs further investigation.
//
// use super::*;
//
// use crate::{
// aes_gcm::{mock::create_mock_aes_gcm_pair, AesGcmConfigBuilder, Role},
// Aead,
// };
// use ::aes_gcm::{aead::AeadInPlace, Aes128Gcm, NewAead, Nonce};
// use mpz_common::executor::STExecutor;
// use mpz_garble::{protocol::deap::mock::create_mock_deap_vm, Memory};
// use serio::channel::MemoryDuplex;
//
// fn reference_impl(
// key: &[u8],
// iv: &[u8],
// explicit_nonce: &[u8],
// plaintext: &[u8],
// aad: &[u8],
// ) -> Vec<u8> {
// let cipher = Aes128Gcm::new_from_slice(key).unwrap();
// let nonce = [iv, explicit_nonce].concat();
// let nonce = Nonce::from_slice(nonce.as_slice());
//
// let mut ciphertext = plaintext.to_vec();
// cipher
// .encrypt_in_place(nonce, aad, &mut ciphertext)
// .unwrap();
//
// ciphertext
// }
//
// async fn setup_pair(
// key: Vec<u8>,
// iv: Vec<u8>,
// ) -> (
// MpcAesGcm<STExecutor<MemoryDuplex>>,
// MpcAesGcm<STExecutor<MemoryDuplex>>,
// ) {
// let (leader_vm, follower_vm) = create_mock_deap_vm();
//
// let leader_key = leader_vm
// .new_public_array_input::<u8>("key", key.len())
// .unwrap();
// let leader_iv = leader_vm
// .new_public_array_input::<u8>("iv", iv.len())
// .unwrap();
//
// leader_vm.assign(&leader_key, key.clone()).unwrap();
// leader_vm.assign(&leader_iv, iv.clone()).unwrap();
//
// let follower_key = follower_vm
// .new_public_array_input::<u8>("key", key.len())
// .unwrap();
// let follower_iv = follower_vm
// .new_public_array_input::<u8>("iv", iv.len())
// .unwrap();
//
// follower_vm.assign(&follower_key, key.clone()).unwrap();
// follower_vm.assign(&follower_iv, iv.clone()).unwrap();
//
// let leader_config = AesGcmConfigBuilder::default()
// .id("test".to_string())
// .role(Role::Leader)
// .build()
// .unwrap();
// let follower_config = AesGcmConfigBuilder::default()
// .id("test".to_string())
// .role(Role::Follower)
// .build()
// .unwrap();
//
// let (mut leader, mut follower) = create_mock_aes_gcm_pair(
// "test",
// (leader_vm, follower_vm),
// leader_config,
// follower_config,
// )
// .await;
//
// futures::try_join!(
// leader.set_key(leader_key, leader_iv),
// follower.set_key(follower_key, follower_iv)
// )
// .unwrap();
//
// futures::try_join!(leader.setup(), follower.setup()).unwrap();
// futures::try_join!(leader.start(), follower.start()).unwrap();
//
// (leader, follower)
// }
//
// #[tokio::test]
// #[ignore = "expensive"]
// async fn test_aes_gcm_encrypt_private() {
// let key = vec![0u8; 16];
// let iv = vec![0u8; 4];
// let explicit_nonce = vec![0u8; 8];
// let plaintext = vec![1u8; 32];
// let aad = vec![2u8; 12];
//
// let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
//
// let (leader_ciphertext, follower_ciphertext) = tokio::try_join!(
// leader.encrypt_private(explicit_nonce.clone(), plaintext.clone(), aad.clone(),),
// follower.encrypt_blind(explicit_nonce.clone(), plaintext.len(), aad.clone())
// )
// .unwrap();
//
// assert_eq!(leader_ciphertext, follower_ciphertext);
// assert_eq!(
// leader_ciphertext,
// reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad)
// );
// }
//
// #[tokio::test]
// #[ignore = "expensive"]
// async fn test_aes_gcm_encrypt_public() {
// let key = vec![0u8; 16];
// let iv = vec![0u8; 4];
// let explicit_nonce = vec![0u8; 8];
// let plaintext = vec![1u8; 32];
// let aad = vec![2u8; 12];
//
// let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
//
// let (leader_ciphertext, follower_ciphertext) = tokio::try_join!(
// leader.encrypt_public(explicit_nonce.clone(), plaintext.clone(), aad.clone(),),
// follower.encrypt_public(explicit_nonce.clone(), plaintext.clone(), aad.clone(),)
// )
// .unwrap();
//
// assert_eq!(leader_ciphertext, follower_ciphertext);
// assert_eq!(
// leader_ciphertext,
// reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad)
// );
// }
//
// #[tokio::test]
// #[ignore = "expensive"]
// async fn test_aes_gcm_decrypt_private() {
// let key = vec![0u8; 16];
// let iv = vec![0u8; 4];
// let explicit_nonce = vec![0u8; 8];
// let plaintext = vec![1u8; 32];
// let aad = vec![2u8; 12];
// let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
//
// let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
//
// let (leader_plaintext, _) = tokio::try_join!(
// leader.decrypt_private(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),),
// follower.decrypt_blind(explicit_nonce.clone(), ciphertext, aad.clone(),)
// )
// .unwrap();
//
// assert_eq!(leader_plaintext, plaintext);
// }
//
// #[tokio::test]
// #[ignore = "expensive"]
// async fn test_aes_gcm_decrypt_private_bad_tag() {
// let key = vec![0u8; 16];
// let iv = vec![0u8; 4];
// let explicit_nonce = vec![0u8; 8];
// let plaintext = vec![1u8; 32];
// let aad = vec![2u8; 12];
// let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
//
// let len = ciphertext.len();
//
// // corrupt tag
// let mut corrupted = ciphertext.clone();
// corrupted[len - 1] -= 1;
//
// let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
//
// // leader receives corrupted tag
// let err = tokio::try_join!(
// leader.decrypt_private(explicit_nonce.clone(), corrupted.clone(), aad.clone(),),
// follower.decrypt_blind(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),)
// )
// .unwrap_err();
// assert_eq!(err.kind(), ErrorKind::Tag);
//
// let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
//
// // follower receives corrupted tag
// let err = tokio::try_join!(
// leader.decrypt_private(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),),
// follower.decrypt_blind(explicit_nonce.clone(), corrupted.clone(), aad.clone(),)
// )
// .unwrap_err();
// assert_eq!(err.kind(), ErrorKind::Tag);
// }
//
// #[tokio::test]
// #[ignore = "expensive"]
// async fn test_aes_gcm_decrypt_public() {
// let key = vec![0u8; 16];
// let iv = vec![0u8; 4];
// let explicit_nonce = vec![0u8; 8];
// let plaintext = vec![1u8; 32];
// let aad = vec![2u8; 12];
// let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
//
// let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
//
// let (leader_plaintext, follower_plaintext) = tokio::try_join!(
// leader.decrypt_public(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),),
// follower.decrypt_public(explicit_nonce.clone(), ciphertext, aad.clone(),)
// )
// .unwrap();
//
// assert_eq!(leader_plaintext, plaintext);
// assert_eq!(leader_plaintext, follower_plaintext);
// }
//
// #[tokio::test]
// #[ignore = "expensive"]
// async fn test_aes_gcm_decrypt_public_bad_tag() {
// let key = vec![0u8; 16];
// let iv = vec![0u8; 4];
// let explicit_nonce = vec![0u8; 8];
// let plaintext = vec![1u8; 32];
// let aad = vec![2u8; 12];
// let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
//
// let len = ciphertext.len();
//
// // Corrupt tag.
// let mut corrupted = ciphertext.clone();
// corrupted[len - 1] -= 1;
//
// let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
//
// // Leader receives corrupted tag.
// let err = tokio::try_join!(
// leader.decrypt_public(explicit_nonce.clone(), corrupted.clone(), aad.clone(),),
// follower.decrypt_public(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),)
// )
// .unwrap_err();
// assert_eq!(err.kind(), ErrorKind::Tag);
//
// let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
//
// // Follower receives corrupted tag.
// let err = tokio::try_join!(
// leader.decrypt_public(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),),
// follower.decrypt_public(explicit_nonce.clone(), corrupted.clone(), aad.clone(),)
// )
// .unwrap_err();
// assert_eq!(err.kind(), ErrorKind::Tag);
// }
//
// #[tokio::test]
// #[ignore = "expensive"]
// async fn test_aes_gcm_verify_tag() {
// let key = vec![0u8; 16];
// let iv = vec![0u8; 4];
// let explicit_nonce = vec![0u8; 8];
// let plaintext = vec![1u8; 32];
// let aad = vec![2u8; 12];
// let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
//
// let len = ciphertext.len();
//
// let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
//
// tokio::try_join!(
// leader.verify_tag(explicit_nonce.clone(), ciphertext.clone(), aad.clone()),
// follower.verify_tag(explicit_nonce.clone(), ciphertext.clone(), aad.clone())
// )
// .unwrap();
//
// //Corrupt tag.
// let mut corrupted = ciphertext.clone();
// corrupted[len - 1] -= 1;
//
// let (leader_res, follower_res) = tokio::join!(
// leader.verify_tag(explicit_nonce.clone(), corrupted.clone(), aad.clone()),
// follower.verify_tag(explicit_nonce.clone(), corrupted, aad.clone())
// );
//
// assert_eq!(leader_res.unwrap_err().kind(), ErrorKind::Tag);
// assert_eq!(follower_res.unwrap_err().kind(), ErrorKind::Tag);
// }
use super::*;
use crate::{
aes_gcm::{mock::create_mock_aes_gcm_pair, AesGcmConfigBuilder, Role},
Aead,
};
use ::aes_gcm::{aead::AeadInPlace, Aes128Gcm, NewAead, Nonce};
use error::ErrorKind;
use mpz_common::executor::STExecutor;
use mpz_garble::{protocol::deap::mock::create_mock_deap_vm, Memory};
use serio::channel::MemoryDuplex;
fn reference_impl(
key: &[u8],
iv: &[u8],
explicit_nonce: &[u8],
plaintext: &[u8],
aad: &[u8],
) -> Vec<u8> {
let cipher = Aes128Gcm::new_from_slice(key).unwrap();
let nonce = [iv, explicit_nonce].concat();
let nonce = Nonce::from_slice(nonce.as_slice());
let mut ciphertext = plaintext.to_vec();
cipher
.encrypt_in_place(nonce, aad, &mut ciphertext)
.unwrap();
ciphertext
}
async fn setup_pair(
key: Vec<u8>,
iv: Vec<u8>,
) -> (
MpcAesGcm<STExecutor<MemoryDuplex>>,
MpcAesGcm<STExecutor<MemoryDuplex>>,
) {
let (leader_vm, follower_vm) = create_mock_deap_vm();
let leader_key = leader_vm
.new_public_array_input::<u8>("key", key.len())
.unwrap();
let leader_iv = leader_vm
.new_public_array_input::<u8>("iv", iv.len())
.unwrap();
leader_vm.assign(&leader_key, key.clone()).unwrap();
leader_vm.assign(&leader_iv, iv.clone()).unwrap();
let follower_key = follower_vm
.new_public_array_input::<u8>("key", key.len())
.unwrap();
let follower_iv = follower_vm
.new_public_array_input::<u8>("iv", iv.len())
.unwrap();
follower_vm.assign(&follower_key, key.clone()).unwrap();
follower_vm.assign(&follower_iv, iv.clone()).unwrap();
let leader_config = AesGcmConfigBuilder::default()
.id("test".to_string())
.role(Role::Leader)
.build()
.unwrap();
let follower_config = AesGcmConfigBuilder::default()
.id("test".to_string())
.role(Role::Follower)
.build()
.unwrap();
let (mut leader, mut follower) = create_mock_aes_gcm_pair(
"test",
(leader_vm, follower_vm),
leader_config,
follower_config,
)
.await;
futures::try_join!(
leader.set_key(leader_key, leader_iv),
follower.set_key(follower_key, follower_iv)
)
.unwrap();
futures::try_join!(leader.setup(), follower.setup()).unwrap();
futures::try_join!(leader.start(), follower.start()).unwrap();
(leader, follower)
}
#[tokio::test]
#[ignore = "expensive"]
async fn test_aes_gcm_encrypt_private() {
let key = vec![0u8; 16];
let iv = vec![0u8; 4];
let explicit_nonce = vec![0u8; 8];
let plaintext = vec![1u8; 32];
let aad = vec![2u8; 12];
let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
let (leader_ciphertext, follower_ciphertext) = tokio::try_join!(
leader.encrypt_private(explicit_nonce.clone(), plaintext.clone(), aad.clone(),),
follower.encrypt_blind(explicit_nonce.clone(), plaintext.len(), aad.clone())
)
.unwrap();
assert_eq!(leader_ciphertext, follower_ciphertext);
assert_eq!(
leader_ciphertext,
reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad)
);
}
#[tokio::test]
#[ignore = "expensive"]
async fn test_aes_gcm_encrypt_public() {
let key = vec![0u8; 16];
let iv = vec![0u8; 4];
let explicit_nonce = vec![0u8; 8];
let plaintext = vec![1u8; 32];
let aad = vec![2u8; 12];
let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
let (leader_ciphertext, follower_ciphertext) = tokio::try_join!(
leader.encrypt_public(explicit_nonce.clone(), plaintext.clone(), aad.clone(),),
follower.encrypt_public(explicit_nonce.clone(), plaintext.clone(), aad.clone(),)
)
.unwrap();
assert_eq!(leader_ciphertext, follower_ciphertext);
assert_eq!(
leader_ciphertext,
reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad)
);
}
#[tokio::test]
#[ignore = "expensive"]
async fn test_aes_gcm_decrypt_private() {
let key = vec![0u8; 16];
let iv = vec![0u8; 4];
let explicit_nonce = vec![0u8; 8];
let plaintext = vec![1u8; 32];
let aad = vec![2u8; 12];
let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
let (leader_plaintext, _) = tokio::try_join!(
leader.decrypt_private(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),),
follower.decrypt_blind(explicit_nonce.clone(), ciphertext, aad.clone(),)
)
.unwrap();
assert_eq!(leader_plaintext, plaintext);
}
#[tokio::test]
#[ignore = "expensive"]
async fn test_aes_gcm_decrypt_private_bad_tag() {
let key = vec![0u8; 16];
let iv = vec![0u8; 4];
let explicit_nonce = vec![0u8; 8];
let plaintext = vec![1u8; 32];
let aad = vec![2u8; 12];
let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
let len = ciphertext.len();
// corrupt tag
let mut corrupted = ciphertext.clone();
corrupted[len - 1] -= 1;
let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
// leader receives corrupted tag
let err = tokio::try_join!(
leader.decrypt_private(explicit_nonce.clone(), corrupted.clone(), aad.clone(),),
follower.decrypt_blind(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),)
)
.unwrap_err();
assert_eq!(err.kind(), ErrorKind::Tag);
let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
// follower receives corrupted tag
let err = tokio::try_join!(
leader.decrypt_private(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),),
follower.decrypt_blind(explicit_nonce.clone(), corrupted.clone(), aad.clone(),)
)
.unwrap_err();
assert_eq!(err.kind(), ErrorKind::Tag);
}
#[tokio::test]
#[ignore = "expensive"]
async fn test_aes_gcm_decrypt_public() {
let key = vec![0u8; 16];
let iv = vec![0u8; 4];
let explicit_nonce = vec![0u8; 8];
let plaintext = vec![1u8; 32];
let aad = vec![2u8; 12];
let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
let (leader_plaintext, follower_plaintext) = tokio::try_join!(
leader.decrypt_public(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),),
follower.decrypt_public(explicit_nonce.clone(), ciphertext, aad.clone(),)
)
.unwrap();
assert_eq!(leader_plaintext, plaintext);
assert_eq!(leader_plaintext, follower_plaintext);
}
#[tokio::test]
#[ignore = "expensive"]
async fn test_aes_gcm_decrypt_public_bad_tag() {
let key = vec![0u8; 16];
let iv = vec![0u8; 4];
let explicit_nonce = vec![0u8; 8];
let plaintext = vec![1u8; 32];
let aad = vec![2u8; 12];
let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
let len = ciphertext.len();
// Corrupt tag.
let mut corrupted = ciphertext.clone();
corrupted[len - 1] -= 1;
let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
// Leader receives corrupted tag.
let err = tokio::try_join!(
leader.decrypt_public(explicit_nonce.clone(), corrupted.clone(), aad.clone(),),
follower.decrypt_public(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),)
)
.unwrap_err();
assert_eq!(err.kind(), ErrorKind::Tag);
let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
// Follower receives corrupted tag.
let err = tokio::try_join!(
leader.decrypt_public(explicit_nonce.clone(), ciphertext.clone(), aad.clone(),),
follower.decrypt_public(explicit_nonce.clone(), corrupted.clone(), aad.clone(),)
)
.unwrap_err();
assert_eq!(err.kind(), ErrorKind::Tag);
}
#[tokio::test]
#[ignore = "expensive"]
async fn test_aes_gcm_verify_tag() {
let key = vec![0u8; 16];
let iv = vec![0u8; 4];
let explicit_nonce = vec![0u8; 8];
let plaintext = vec![1u8; 32];
let aad = vec![2u8; 12];
let ciphertext = reference_impl(&key, &iv, &explicit_nonce, &plaintext, &aad);
let len = ciphertext.len();
let (mut leader, mut follower) = setup_pair(key.clone(), iv.clone()).await;
tokio::try_join!(
leader.verify_tag(explicit_nonce.clone(), ciphertext.clone(), aad.clone()),
follower.verify_tag(explicit_nonce.clone(), ciphertext.clone(), aad.clone())
)
.unwrap();
//Corrupt tag.
let mut corrupted = ciphertext.clone();
corrupted[len - 1] -= 1;
let (leader_res, follower_res) = tokio::join!(
leader.verify_tag(explicit_nonce.clone(), corrupted.clone(), aad.clone()),
follower.verify_tag(explicit_nonce.clone(), corrupted, aad.clone())
);
assert_eq!(leader_res.unwrap_err().kind(), ErrorKind::Tag);
assert_eq!(follower_res.unwrap_err().kind(), ErrorKind::Tag);
}
}