diff --git a/.gitignore b/.gitignore index 62f1ff88..ebddeaec 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ /.vscode Cargo.lock **/.#* -**/#*# \ No newline at end of file +**/#*# +**/*~ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 36e13043..deba92c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,12 +19,21 @@ name = "cosmian_cover_crypt" # The cdylib is only interesting if the `--features ffi` flag is set on build # This does not seem to be actionable conditionally https://github.com/rust-lang/cargo/issues/4881 +[[example]] +name = "encrypt" +required-features = ["serialization"] + +[[example]] +name = "decrypt" +required-features = ["serialization"] + [features] full_bench = [] hybridized_bench = [] +serialization = [] [dependencies] -cosmian_crypto_core = { git="https://github.com/Cosmian/crypto_core.git", branch="feat/add_neutral_scalars", default-features = false, features = ["ser", "sha3", "aes", "curve25519"] } +cosmian_crypto_core = { git="https://github.com/Cosmian/crypto_core.git", branch="develop", default-features = false, features = ["ser", "sha3", "aes", "curve25519"] } pqc_kyber = { version = "0.4", features = ["std", "hazmat"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/examples/decrypt.rs b/examples/decrypt.rs index bd8867b6..d21d8c55 100644 --- a/examples/decrypt.rs +++ b/examples/decrypt.rs @@ -1,31 +1,26 @@ -fn main() { - #[cfg(feature = "serialization")] - { - use base64::{ - alphabet::STANDARD, - engine::{GeneralPurpose, GeneralPurposeConfig}, - Engine, - }; - use cosmian_cover_crypt::{Covercrypt, EncryptedHeader, UserSecretKey}; - use cosmian_crypto_core::bytes_ser_de::Serializable; +const USK: &str = "sUBriVEsM8HowAKOsIs9Y5p+Xa8JwJf84rlBHXzfkg+BJ6PzK79cT37Wsku5pMVKrfi/eIWhufC2pjYNMBOUAgMAH+waKc6AuwtAaYXXUAJA0K/uc5A22nXEShVXykjZ8QIBuNMsBrpSQaW0hAZ0AFvJTlwSM2BgI8m5k4wj3kQQ9NaP4cK6VvwyFlx7ZwwMZkJoCSgfiPvN4XqeWmiw/hehgjU2HkgYDQJf9QABaUoa74qM42SBnNubVVVnPvO5zrZ++NgoIHt4VPJfsMu157i6bakhOEWJy/CduXYGqedOGsNpj+gq0VsanjqeJ9gZ6sKjCknGoTxbEipIrPUO5jqJLzmJilS0ycAhR1mYlGU9yApPithxeIMaQFAAegOUTVFDFausKPbDZ1IYEWyftONyaaelMyJF4eCQIWILEihLuoscwGkVlYmRttcNVrLDW3yHN/cFQgiBSNVOi/RTLSpdsaMtattwDcaEiRUvCxe2gfaCYQV9zPIsdfAG2TIf2MPLcqUu0EBnIohX/TKyOTavt7HB76d/pCWT7sceu/YldifCYUoWABA8GbnF6DurlVwhfYUKhThnchbP3mgsEUsy+vQlWIwvrwxgpgqaabNSwJIUCXttY6kYNjJbL5pkahaAnxYLRMcvvKfGtzwlX7ewINAFrMSUBua8abWzgIa2NWlt2/ON7tKrVIaqRMYs8KFJSSsQ4bGEhZfBjWEYacAho8bFMuqamQgxaJhyj/J+tZrA24A5ckmPSPeGOIKealWkQmy8f0oAPVsOGaQFcBwLMPe4qRl49tpM6kRgpsyOkeQYOdpQ7Rue0xMKR+UhdPJkzYcGoVlAIGRmvSQzamw0kEYYV6yWr7AHiiqPZ/AuS7RcYUzPB0tSZ9BSdJKSX1jGf2aCbPPD8DxQ6VVPqWOekHshGGBOEzqBPhxtpfQB/lhl2KG/XOgUB6Kqs/zF14oLvlBL2XU2fCaj76ydrdOlo5JSSLKU2pkWASMLNGIM5TUqBsi/D4A9lLlqW5dDOik4ujNLbMAl4Yt4HxOe+pGKhFexfwJBgdCB6IBsatK/nWnIMuW9ZZFbUQG7oTOuFMKvurQ2OsyWa9VA06ldDFWIOQyzgydm2pp3XnXKaJElyYMSE1Ef4KRgEqgYA6J8yNIoI0o092y/HUxAoWWc5PesDmeqbbaLFEFdAqJ4gerNbtS1uAQWIsJzHAC2pGgxmLE7WkaD1IQ3fFgTHPOR97EdlbbM5YGa78mUpHqS0NpYQpbHluRUbyJ9uyIcvAxHewmIKIfDKnZ1z+YG/sIccCY+tFlGxBApSym1tgRBREcgkcYUAogq5nsKY5mvhXxICTCU4WtxZLxl17CC6du5pRSH+9hAMvI2C5R/6udvXkaYc0Ki35WsO5QAHlOVLBwbKLfM9xlhPkFf9qkqgKoMfqEJDcOn79QfOVFFqSfDI9Ep7Wl2YMK3r1apC2gw9xmwjBsMkEYLq2lNH4l0OqGpXDpAQCMwyTEOs5XHjWpsgxIvDId3HDu2DCBKYhUavtYHSgHNUeITVraWmhI9ygkBVMJKLfUGIVq/UwQvhzeVFePNhhN59HuWSjCyonWG6iFQeCCb9Dq0z2DNowiOnVdDWIM5g7BRTgSKBBlE/kArtfaDPyCXENUnwOZDpka63VMCNGak45IkSPT5SN7pdkPW9uIQBaAb04uYPbnALTij4xUzLwkAqXQvjNzwE8/0JzoWmWfdf9zdi1jdzpG1ZTqir0h74gc="; - const USK: &str = "sUBriVEsM8HowAKOsIs9Y5p+Xa8JwJf84rlBHXzfkg+BJ6PzK79cT37Wsku5pMVKrfi/eIWhufC2pjYNMBOUAgMAH+waKc6AuwtAaYXXUAJA0K/uc5A22nXEShVXykjZ8QIBuNMsBrpSQaW0hAZ0AFvJTlwSM2BgI8m5k4wj3kQQ9NaP4cK6VvwyFlx7ZwwMZkJoCSgfiPvN4XqeWmiw/hehgjU2HkgYDQJf9QABaUoa74qM42SBnNubVVVnPvO5zrZ++NgoIHt4VPJfsMu157i6bakhOEWJy/CduXYGqedOGsNpj+gq0VsanjqeJ9gZ6sKjCknGoTxbEipIrPUO5jqJLzmJilS0ycAhR1mYlGU9yApPithxeIMaQFAAegOUTVFDFausKPbDZ1IYEWyftONyaaelMyJF4eCQIWILEihLuoscwGkVlYmRttcNVrLDW3yHN/cFQgiBSNVOi/RTLSpdsaMtattwDcaEiRUvCxe2gfaCYQV9zPIsdfAG2TIf2MPLcqUu0EBnIohX/TKyOTavt7HB76d/pCWT7sceu/YldifCYUoWABA8GbnF6DurlVwhfYUKhThnchbP3mgsEUsy+vQlWIwvrwxgpgqaabNSwJIUCXttY6kYNjJbL5pkahaAnxYLRMcvvKfGtzwlX7ewINAFrMSUBua8abWzgIa2NWlt2/ON7tKrVIaqRMYs8KFJSSsQ4bGEhZfBjWEYacAho8bFMuqamQgxaJhyj/J+tZrA24A5ckmPSPeGOIKealWkQmy8f0oAPVsOGaQFcBwLMPe4qRl49tpM6kRgpsyOkeQYOdpQ7Rue0xMKR+UhdPJkzYcGoVlAIGRmvSQzamw0kEYYV6yWr7AHiiqPZ/AuS7RcYUzPB0tSZ9BSdJKSX1jGf2aCbPPD8DxQ6VVPqWOekHshGGBOEzqBPhxtpfQB/lhl2KG/XOgUB6Kqs/zF14oLvlBL2XU2fCaj76ydrdOlo5JSSLKU2pkWASMLNGIM5TUqBsi/D4A9lLlqW5dDOik4ujNLbMAl4Yt4HxOe+pGKhFexfwJBgdCB6IBsatK/nWnIMuW9ZZFbUQG7oTOuFMKvurQ2OsyWa9VA06ldDFWIOQyzgydm2pp3XnXKaJElyYMSE1Ef4KRgEqgYA6J8yNIoI0o092y/HUxAoWWc5PesDmeqbbaLFEFdAqJ4gerNbtS1uAQWIsJzHAC2pGgxmLE7WkaD1IQ3fFgTHPOR97EdlbbM5YGa78mUpHqS0NpYQpbHluRUbyJ9uyIcvAxHewmIKIfDKnZ1z+YG/sIccCY+tFlGxBApSym1tgRBREcgkcYUAogq5nsKY5mvhXxICTCU4WtxZLxl17CC6du5pRSH+9hAMvI2C5R/6udvXkaYc0Ki35WsO5QAHlOVLBwbKLfM9xlhPkFf9qkqgKoMfqEJDcOn79QfOVFFqSfDI9Ep7Wl2YMK3r1apC2gw9xmwjBsMkEYLq2lNH4l0OqGpXDpAQCMwyTEOs5XHjWpsgxIvDId3HDu2DCBKYhUavtYHSgHNUeITVraWmhI9ygkBVMJKLfUGIVq/UwQvhzeVFePNhhN59HuWSjCyonWG6iFQeCCb9Dq0z2DNowiOnVdDWIM5g7BRTgSKBBlE/kArtfaDPyCXENUnwOZDpka63VMCNGak45IkSPT5SN7pdkPW9uIQBaAb04uYPbnALTij4xUzLwkAqXQvjNzwE8/0JzoWmWfdf9zdi1jdzpG1ZTqir0h74gc="; +const HEADER: &str = "dkKN4Ga3BcJyspF5FntqHYwXI/yoUugzqcF0DPCluyUckuqbpXDRgSWdnyh5k0wCNAD6BgOR3WXjjrg6bF/mR/ICzF1XqtqK5Fn0uZ7FgYkBAbjlkpf7VV3M7BrwEB1FIJ2ZhekRLPayJvvTTdE6DY9ddr3IbuzEiMASs2P5nRabJe+p1xLJ9Yp1+8roV6GgWy37m7ysu24nxtKKPiyCIh29zxt4WjG+rJZOS0iJ2ETEjGPG5yyY7LYsOwsp6tQlb/XGmqd91TxjobKOnRceYRV29E1vj/uwzyT8OtRfWVITK1v1ku4knbWPhXFGVdiUpME+QqZIrjulMPI/AKoABa925Eq8h7UM64p91288I5Lq+S8+ysIaJv2nlydxb9BZBg/NeG5YGD+NIKm0EXoDekmbX91rLktO0cgFYEhNm+43BvLLXoyD7iU2bT7dQKNMUxMyoDvhB40aOkTWKJGCFnnrLf+oSxGDkCWTNaWYCJ3qygxjETGdigQzV6bzaGh2f/pjAzRHwKJHgywW4E2BCGrM4BJZvjhl44sZ14HWLofl3hNzLfrdJLFZz7Ua3PxHlidQ+ZsZSOW61G9Xt5vwHYZDtQOMVcX7tyNTNmMoIlghHjm66H8JDtuiduy08VE+7/Bk2anWhljNX2f2a/+hOjMbzqG+zaAtgpppylkvG/WlR1xLohGlLKdAWYRETIrI0oKi8RKFNHdnuPCZRDHlHDjhc273rPvsn7FlolSSTxbjvyTacB7jNwKfxW32eEK82p04GQyVN5uEBQBZUG4heNnoba54ybeU66PbOOclgE+LoblnvE0lqv8VxbCvbM3VVxTJ0rMj8ZshcxEWDv1v8E1JkGeq3QcDK/Ww1wBDuwLhxY8xbKmD40bmS8nGkRTLDuyhtg009oboT4+SL83Dz5WXEBcwO2m8Noor0QgtMjuyCAR92gvNSvewk03Q6X80W5Rd+tvw6HMwnoevpKge56akgXnQPnx76h78jIrEZ8FDZ7g6dlqPf7Lr9PnWQKhVHZwh1CBN0Rbb0ylumpvkiSCV7kF1tqPE89LO31jmYdULuQmMclKRst+SdXWSM7t2J/CeFmTgSiJ896P95imsC3XcWvmJqk+qlBNynP/Azmn775H59UHj21t8z2sFYwYkDfDRyBSxVLW/08nz+9vVy05qmrUqej/Iv4sHHjE3cgLCD+rjPOHssoFdr4ubG441N+1HpvoxFbo0Yk/fNH9JzqQj09eA4Wx6t3fjOWYJ8CESyK7lKBANYrQ5IdYuY3tk/Z3uR/uYff2pKu6xuisLF66FVC80WokFlvCEoYLLJs/q2/16mMo7cqkbhI0SLJgaAK8wIvJUS64Xxb3+SVVPYkeVFywrAb89J0kg6txXIuOfHxGcDZu7QaEPQUos7pwnbZBeXb/Q1RHbEE6cXr1pGB6wU+0gABNzLLTRNHuu2qm33myLkNQRilOyR7EqrDmLwY3ysGE5tmxYH5+zEu7nR/rkozqwpueGmrDMibRRw14kLRCNh6YkqvKsbSg1Tg0EKZe2eLGG0mmgj5jcYqusRcVtAA=="; - const HEADER: &str = "dkKN4Ga3BcJyspF5FntqHYwXI/yoUugzqcF0DPCluyUckuqbpXDRgSWdnyh5k0wCNAD6BgOR3WXjjrg6bF/mR/ICzF1XqtqK5Fn0uZ7FgYkBAbjlkpf7VV3M7BrwEB1FIJ2ZhekRLPayJvvTTdE6DY9ddr3IbuzEiMASs2P5nRabJe+p1xLJ9Yp1+8roV6GgWy37m7ysu24nxtKKPiyCIh29zxt4WjG+rJZOS0iJ2ETEjGPG5yyY7LYsOwsp6tQlb/XGmqd91TxjobKOnRceYRV29E1vj/uwzyT8OtRfWVITK1v1ku4knbWPhXFGVdiUpME+QqZIrjulMPI/AKoABa925Eq8h7UM64p91288I5Lq+S8+ysIaJv2nlydxb9BZBg/NeG5YGD+NIKm0EXoDekmbX91rLktO0cgFYEhNm+43BvLLXoyD7iU2bT7dQKNMUxMyoDvhB40aOkTWKJGCFnnrLf+oSxGDkCWTNaWYCJ3qygxjETGdigQzV6bzaGh2f/pjAzRHwKJHgywW4E2BCGrM4BJZvjhl44sZ14HWLofl3hNzLfrdJLFZz7Ua3PxHlidQ+ZsZSOW61G9Xt5vwHYZDtQOMVcX7tyNTNmMoIlghHjm66H8JDtuiduy08VE+7/Bk2anWhljNX2f2a/+hOjMbzqG+zaAtgpppylkvG/WlR1xLohGlLKdAWYRETIrI0oKi8RKFNHdnuPCZRDHlHDjhc273rPvsn7FlolSSTxbjvyTacB7jNwKfxW32eEK82p04GQyVN5uEBQBZUG4heNnoba54ybeU66PbOOclgE+LoblnvE0lqv8VxbCvbM3VVxTJ0rMj8ZshcxEWDv1v8E1JkGeq3QcDK/Ww1wBDuwLhxY8xbKmD40bmS8nGkRTLDuyhtg009oboT4+SL83Dz5WXEBcwO2m8Noor0QgtMjuyCAR92gvNSvewk03Q6X80W5Rd+tvw6HMwnoevpKge56akgXnQPnx76h78jIrEZ8FDZ7g6dlqPf7Lr9PnWQKhVHZwh1CBN0Rbb0ylumpvkiSCV7kF1tqPE89LO31jmYdULuQmMclKRst+SdXWSM7t2J/CeFmTgSiJ896P95imsC3XcWvmJqk+qlBNynP/Azmn775H59UHj21t8z2sFYwYkDfDRyBSxVLW/08nz+9vVy05qmrUqej/Iv4sHHjE3cgLCD+rjPOHssoFdr4ubG441N+1HpvoxFbo0Yk/fNH9JzqQj09eA4Wx6t3fjOWYJ8CESyK7lKBANYrQ5IdYuY3tk/Z3uR/uYff2pKu6xuisLF66FVC80WokFlvCEoYLLJs/q2/16mMo7cqkbhI0SLJgaAK8wIvJUS64Xxb3+SVVPYkeVFywrAb89J0kg6txXIuOfHxGcDZu7QaEPQUos7pwnbZBeXb/Q1RHbEE6cXr1pGB6wU+0gABNzLLTRNHuu2qm33myLkNQRilOyR7EqrDmLwY3ysGE5tmxYH5+zEu7nR/rkozqwpueGmrDMibRRw14kLRCNh6YkqvKsbSg1Tg0EKZe2eLGG0mmgj5jcYqusRcVtAA=="; +fn main() { + use base64::{ + alphabet::STANDARD, + engine::{GeneralPurpose, GeneralPurposeConfig}, + Engine, + }; + use cosmian_cover_crypt::{Covercrypt, EncryptedHeader, UserSecretKey}; + use cosmian_crypto_core::bytes_ser_de::Serializable; - let config: GeneralPurposeConfig = GeneralPurposeConfig::default(); - let transcoder: GeneralPurpose = GeneralPurpose::new(&STANDARD, config); + let config: GeneralPurposeConfig = GeneralPurposeConfig::default(); + let transcoder: GeneralPurpose = GeneralPurpose::new(&STANDARD, config); - let cc = Covercrypt::default(); - let usk = UserSecretKey::deserialize(&transcoder.decode(USK.as_bytes()).unwrap()).unwrap(); - let encrypted_header = - EncryptedHeader::deserialize(&transcoder.decode(HEADER.as_bytes()).unwrap()).unwrap(); - for _ in 0..1000 { - encrypted_header - .decrypt(&cc, &usk, None) - .expect("cannot decrypt hybrid header"); - } + let cc = Covercrypt::default(); + let usk = UserSecretKey::deserialize(&transcoder.decode(USK.as_bytes()).unwrap()).unwrap(); + let encrypted_header = + EncryptedHeader::deserialize(&transcoder.decode(HEADER.as_bytes()).unwrap()).unwrap(); + for _ in 0..1000 { + encrypted_header + .decrypt(&cc, &usk, None) + .expect("cannot decrypt hybrid header"); } - #[cfg(not(feature = "serialization"))] - println!("Use the `serialization` feature to run this example") } diff --git a/examples/encrypt.rs b/examples/encrypt.rs index f705327a..b352672b 100644 --- a/examples/encrypt.rs +++ b/examples/encrypt.rs @@ -1,80 +1,65 @@ -fn main() { - // create policy - #[cfg(all(feature = "serialization", feature = "test_utils"))] +use cosmian_cover_crypt::{ + abe_policy::{AccessPolicy, Policy}, + test_utils::policy, + Covercrypt, EncryptedHeader, MasterPublicKey, MasterSecretKey, +}; + +/// Generates a new USK and encrypted header and prints them. +fn generate_new( + cc: &Covercrypt, + policy: &Policy, + _msk: &mut MasterSecretKey, + mpk: &MasterPublicKey, +) { + let access_policy = + AccessPolicy::parse("Department::FIN && Security Level::Top Secret").unwrap(); + + let (_, _header) = EncryptedHeader::generate(cc, policy, mpk, &access_policy, None, None) + .expect("cannot encrypt header"); + + #[cfg(feature = "serialization")] { use base64::{ alphabet::STANDARD, engine::{GeneralPurpose, GeneralPurposeConfig}, Engine, }; - use cosmian_cover_crypt::{ - abe_policy::{AccessPolicy, Policy}, - test_utils::policy, - Covercrypt, EncryptedHeader, MasterPublicKey, MasterSecretKey, - }; - use cosmian_crypto_core::bytes_ser_de::Serializable; - fn generate_new( - cc: &Covercrypt, - policy: &Policy, - _msk: &MasterSecretKey, - mpk: &MasterPublicKey, - ) { - let access_policy = AccessPolicy::from_boolean_expression( - "Department::FIN && Security Level::Top Secret", + use cosmian_crypto_core::bytes_ser_de::Serializable; + let config: GeneralPurposeConfig = GeneralPurposeConfig::default(); + let transcoder: GeneralPurpose = GeneralPurpose::new(&STANDARD, config); + println!( + "USK = {}", + transcoder.encode( + cc.generate_user_secret_key(_msk, &access_policy, policy) + .unwrap() + .serialize() + .unwrap() ) - .unwrap(); - - let (_, _header) = - EncryptedHeader::generate(cc, policy, mpk, &access_policy, None, None) - .expect("cannot encrypt header"); - - #[cfg(feature = "serialization")] - { - let config: GeneralPurposeConfig = GeneralPurposeConfig::default(); - let transcoder: GeneralPurpose = GeneralPurpose::new(&STANDARD, config); - - println!( - "usk = {}", - transcoder.encode( - cc.generate_user_secret_key(_msk, &access_policy, policy) - .unwrap() - .serialize() - .unwrap() - ) - ); - println!( - "header = {}", - transcoder.encode(_header.serialize().unwrap()) - ); - } - } - - let policy = policy().expect("cannot generate policy"); + ); + println!( + "header = {}", + transcoder.encode(_header.serialize().unwrap()) + ); + } +} - let cc = Covercrypt::default(); - let (_msk, mpk) = cc - .generate_master_keys(&policy) - .expect("cannot generate master keys"); +fn main() { + let policy = policy().expect("cannot generate policy"); + let ap = AccessPolicy::parse("Department::FIN && Security Level::Top Secret") + .expect("cannot parse given access policy"); - // Encryption of a hybridized ciphertext - let access_policy = - AccessPolicy::from_boolean_expression("Department::FIN && Security Level::Top Secret") - .unwrap(); + let cc = Covercrypt::default(); + let (mut msk, _) = cc.setup().expect("cannot generate master keys"); + let mpk = cc + .update_master_keys(&policy, &mut msk) + .expect("cannot update master keys"); - // - // Use the following to update `examples/decrypt.rs` constants. - // - generate_new(&cc, &policy, &_msk, &mpk); + generate_new(&cc, &policy, &mut msk, &mpk); - // encrypt header, use loop to add weight in the flamegraph on it - for _ in 0..1000 { - let _encrypted_header = - EncryptedHeader::generate(&cc, &policy, &mpk, &access_policy, None, None) - .expect("cannot encrypt header"); - } + // Encrypt header, use loop to increase its wight in the flame graph. + for _ in 0..1000 { + EncryptedHeader::generate(&cc, &policy, &mpk, &ap, None, None) + .expect("cannot encrypt header"); } - - #[cfg(not(all(feature = "test_utils", feature = "serialization")))] - println!("Use the `serialization` feature to run this example") } diff --git a/src/abe_policy/partitions.rs b/src/abe_policy/coordinates.rs similarity index 82% rename from src/abe_policy/partitions.rs rename to src/abe_policy/coordinates.rs index 490a4402..9259a03b 100644 --- a/src/abe_policy/partitions.rs +++ b/src/abe_policy/coordinates.rs @@ -1,6 +1,6 @@ use std::{hash::Hash, ops::Deref}; -use cosmian_crypto_core::bytes_ser_de::Serializer; +use cosmian_crypto_core::bytes_ser_de::{to_leb128_len, Serializable, Serializer}; use crate::Error; @@ -56,12 +56,10 @@ impl Coordinate { // guard against overflow of the 1024 bytes buffer below if attribute_ids.len() > 200 { return Err(Error::InvalidAttribute( - "The current implementation does not currently support more than 200 attributes \ - for a partition" - .to_string(), + "The current implementation does not currently support more than 200 attributes for a coordinate".to_string(), )); } - // the sort operation allows to get the same `Partition` for : + // the sort operation allows to get the same `Coordinate` for : // `Department::HR && Level::Secret` // and // `Level::Secret && Department::HR` @@ -95,6 +93,23 @@ impl From<&[u8]> for Coordinate { } } +impl Serializable for Coordinate { + type Error = Error; + + fn length(&self) -> usize { + to_leb128_len(self.len()) + self.len() + } + + fn write(&self, ser: &mut Serializer) -> Result { + ser.write_vec(self).map_err(Self::Error::from) + } + + fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + let bytes = de.read_vec()?; + Ok(Self(bytes)) + } +} + #[cfg(test)] mod tests { use cosmian_crypto_core::{bytes_ser_de::Deserializer, reexport::rand_core::CryptoRngCore}; @@ -110,12 +125,11 @@ mod tests { } #[test] - fn test_partitions() -> Result<(), Error> { + fn test_coordinates() -> Result<(), Error> { let mut values: Vec = vec![12, 0, u32::MAX, 1]; - let partition = Coordinate::from_attribute_ids(values.clone())?; - // values are sorted n Partition + let coordinate = Coordinate::from_attribute_ids(values.clone())?; values.sort_unstable(); - let mut de = Deserializer::new(&partition); + let mut de = Deserializer::new(&coordinate); for v in values { let val = de.read_leb128_u64().unwrap() as u32; assert_eq!(v, val); diff --git a/src/abe_policy/mod.rs b/src/abe_policy/mod.rs index d11ff5a3..4aefaffb 100644 --- a/src/abe_policy/mod.rs +++ b/src/abe_policy/mod.rs @@ -9,15 +9,15 @@ mod access_policy; mod attribute; +mod coordinates; mod dimension; -mod partitions; mod policy; mod policy_versions; pub use access_policy::AccessPolicy; pub use attribute::{Attribute, AttributeStatus, Attributes, EncryptionHint}; +pub use coordinates::Coordinate; pub use dimension::{AttributeParameters, Dimension, DimensionBuilder}; -pub use partitions::Coordinate; pub use policy_versions::{LegacyPolicy, PolicyV1, PolicyV2 as Policy}; use serde::{Deserialize, Serialize}; diff --git a/src/abe_policy/policy_versions.rs b/src/abe_policy/policy_versions.rs index 5084aecd..d8ccb46f 100644 --- a/src/abe_policy/policy_versions.rs +++ b/src/abe_policy/policy_versions.rs @@ -15,8 +15,8 @@ pub struct PolicyV2 { /// Version number pub(crate) version: PolicyVersion, /// Last value taken by the attribute. - /// TODO: after some mutations, this counter will become very high, which implies the size of - /// the partitions will become huge (d * log_2(id) * 8/7). + /// TODO: after some mutations, this counter will become very high, which + /// implies the size of the coordinates will become huge (d * log_2(id) * 8/7). pub(crate) last_attribute_value: u32, /// Policy axes: maps axes name to the list of associated attribute names diff --git a/src/core/api.rs b/src/core/api.rs index 9b67e845..17b021af 100644 --- a/src/core/api.rs +++ b/src/core/api.rs @@ -1,6 +1,6 @@ //! Defines the `Covercrypt` API. -use std::{fmt::Debug, sync::Mutex}; +use std::{collections::HashMap, fmt::Debug, sync::Mutex}; use cosmian_crypto_core::{ reexport::rand_core::SeedableRng, Aes256Gcm, CsRng, Dem, FixedSizeCBytes, Instantiable, Nonce, @@ -12,7 +12,7 @@ use super::{ MIN_TRACING_LEVEL, }; use crate::{ - abe_policy::{AccessPolicy, Policy}, + abe_policy::{AccessPolicy, AttributeStatus, Coordinate, EncryptionHint, Policy}, core::{ primitives::{decaps, encaps, refresh, rekey, setup}, Encapsulation, MasterPublicKey, MasterSecretKey, UserSecretKey, SEED_LENGTH, @@ -47,11 +47,26 @@ impl Covercrypt { /// They only hold keys for the origin coordinate: only broadcast /// encapsulations can be created. pub fn setup(&self) -> Result<(MasterSecretKey, MasterPublicKey), Error> { - let msk = setup( + let mut msk = setup( &mut *self.rng.lock().expect("Mutex lock failed!"), MIN_TRACING_LEVEL, )?; + + // Add broadcast coordinate with classic encryption level. + // + // TODO replace this function by `add_coordinates`, + // `remove_coordinates`, `hybridize_coordinates` and + // `deprecate_coordinates`. + update_coordinate_keys( + &mut *self.rng.lock().expect("Mutex lock failed!"), + &mut msk, + HashMap::from_iter([( + Coordinate::from_attribute_ids(vec![])?, + (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), + )]), + )?; let mpk = mpk_keygen(&msk)?; + Ok((msk, mpk)) } diff --git a/src/core/mod.rs b/src/core/mod.rs index 2a50ecd9..10f0ad18 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -18,9 +18,10 @@ use crate::{ pub mod macros; pub mod api; -pub mod pq_kem; pub mod primitives; -// pub mod serialization; + +#[cfg(feature = "serialization")] +pub mod serialization; mod pke; #[cfg(test)] @@ -178,7 +179,7 @@ impl CoordinateKeypair { /// Future MPK will be generated without any key for this coordinate, thus /// disabling encryption for this coordinate. fn drop_encryption_key(&mut self) { - self.elgamal_keypair.drop_pk(); + self.elgamal_keypair.deprecate(); } /// Drop the post-quantum part of this coordinate keypair. @@ -416,9 +417,9 @@ impl MasterSecretKey { ) -> impl Iterator + 'a { self.coordinate_keypairs .iter() - .filter_map(|(coordinate, mut keypairs)| { + .filter_map(|(coordinate, keypairs)| { let pk: Option = - keypairs.next().and_then(|keypair| keypair.public_key()); + keypairs.front().and_then(|keypair| keypair.public_key()); pk.map(|pk| (coordinate.clone(), pk)) }) } diff --git a/src/core/pke/elgamal.rs~ b/src/core/pke/elgamal.rs~ deleted file mode 100644 index d04d3398..00000000 --- a/src/core/pke/elgamal.rs~ +++ /dev/null @@ -1,52 +0,0 @@ -/// Encrypts the given secret using a classic KEM. -/// -/// # Security -/// -/// This is a KEM-DEM PKE implementation using an ElGammal KEM and the one-time -/// pad as DEM. Security therefore relies on the fact that different secrets are -/// never encrypted using the same ElGammal keypair. -/// -/// Provides 128-bit of classical security. -fn elgamal_pke_encrypt( - rng: &mut impl CryptoRngCore, - sk: &R25519PrivateKey, - pk: &R25519PublicKey, - secret: &[u8], -) -> Result<[u8; LENGTH], Error> { - if LENGTH < secret.len() { - return Err(Error::OperationNotPermitted(format!( - "cannot encrypt a secret of size {} in a ciphertext of size {LENGTH}", - secret.len() - ))); - } - let mut shared_secret = [0; LENGTH]; - kdf256!(&mut shared_secret, &(pk * &sk).to_bytes()); - xor_in_place(&mut shared_secret, secret); - Ok(shared_secret) -} - -/// Decrypts the given secret using a classic KEM. -/// -/// # Security -/// -/// This is a KEM-DEM PKE implementation using an ElGammal KEM and the one-time -/// pad as DEM. Security therefore relies on the fact that different secrets are -/// never encrypted using the same ElGammal keypair. -/// -/// Provides 128-bit of classical security. -fn elgamal_pke_decrypt( - sk: &R25519PrivateKey, - pk: &R25519PublicKey, - ctx: &[u8], -) -> Result, Error> { - if ctx.len() < LENGTH { - return Err(Error::OperationNotPermitted(format!( - "cannot decrypt a secret of size {LENGTH} from a ciphertext of size {}", - ctx.len() - ))); - } - let mut ptx = Secret::::default(); - kdf256!(&mut *ptx, &(pk * sk).to_bytes()); - xor_in_place(&mut *ptx, &*ctx); - Ok(ptx) -} diff --git a/src/core/pke/elgamal/mod.rs b/src/core/pke/elgamal/mod.rs index 29c42e36..54f6e684 100644 --- a/src/core/pke/elgamal/mod.rs +++ b/src/core/pke/elgamal/mod.rs @@ -6,24 +6,33 @@ //! Curve points MUST provide: //! - group operations (addition, neutral element); //! - external multiplication with scalars. +//! - implement `Serializable` //! //! Scalars MUST provide: //! - field operations (a fortiori the multiplicative inverse). +//! - implement `Serializable` mod ristretto_25519; -use cosmian_crypto_core::{kdf256, reexport::rand_core::CryptoRngCore, Secret, SymmetricKey}; +use cosmian_crypto_core::{ + bytes_ser_de::Serializable, kdf256, reexport::rand_core::CryptoRngCore, Secret, SymmetricKey, +}; use zeroize::Zeroize; pub use ristretto_25519::{EcPoint, Scalar}; use crate::Error; +/// ElGamal keypair. +/// +/// The public key is optional. The following invariant is maintained: +/// > the public key is `None` iff the keypair is deprecated #[derive(Debug, Clone, PartialEq, Eq)] pub struct Keypair(Scalar, Option); impl Keypair { /// Creates a new keypair. + #[inline(always)] pub fn new(sk: Scalar, pk: EcPoint) -> Self { Self(sk, Some(pk)) } @@ -37,26 +46,74 @@ impl Keypair { } /// Returns a reference on the secret key. + #[inline(always)] pub fn sk(&self) -> &Scalar { &self.0 } - /// Returns a reference on the public key. + /// Returns a reference on the public key if the key is not + /// deprecated. Returns `None` otherwise. + #[inline(always)] pub fn pk(&self) -> Option<&EcPoint> { self.1.as_ref() } - /// Drops the public key. This operation is *irreversible*. - pub fn drop_pk(&mut self) { + /// Deprecate this keypair. + #[inline(always)] + pub fn deprecate(&mut self) { self.1 = None } /// Returns true if the given secret key is contained in this keypair. + #[inline(always)] pub fn contains(&self, sk: &Scalar) -> bool { &self.0 == sk } } +impl Serializable for Keypair { + type Error = Error; + + fn length(&self) -> usize { + self.0.length() + + 1 // option encoding overhead + + self + .1 + .as_ref() + .map(Serializable::length) + .unwrap_or_default() + } + + fn write( + &self, + ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, + ) -> Result { + let mut n = ser.write(&self.0)?; + if let Some(pk) = &self.1 { + n += ser.write_leb128_u64(0)?; + n += ser.write(pk)?; + } else { + n += ser.write_leb128_u64(1)?; + } + Ok(n) + } + + fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + let sk = de.read::()?; + let is_deprecated = de.read_leb128_u64()?; + if 1 == is_deprecated { + Ok(Self(sk, None)) + } else if 0 == is_deprecated { + let pk = de.read::()?; + Ok(Self(sk, Some(pk))) + } else { + Err(Error::ConversionFailed(format!( + "invalid option encoding {is_deprecated}" + ))) + } + } +} + /// One-Time Pad (OTP) encryption of the given plaintext. fn otp_encrypt( key: &SymmetricKey, @@ -72,7 +129,7 @@ fn otp_encrypt( /// One-Time Pad (OTP) decryption of the given ciphertext. fn otp_decrypt( key: &SymmetricKey, - ctx: &[u8], + ctx: &[u8], // authorize byte slices from any object ) -> Result, Error> { if ctx.len() < LENGTH { return Err(Error::OperationNotPermitted(format!( @@ -132,6 +189,7 @@ pub fn decrypt( #[cfg(test)] mod tests { use cosmian_crypto_core::{ + bytes_ser_de::Serializable, reexport::rand_core::{CryptoRngCore, SeedableRng}, CsRng, Secret, }; @@ -174,4 +232,14 @@ mod tests { recipient keypair {recipient_keypair:#?} and plaintext {ptx:#?}", ) } + + #[test] + fn test_elgamal_serialization() { + let mut rng = CsRng::from_entropy(); + let keypair = Keypair::random(&mut rng); + let bytes = keypair.serialize().unwrap(); + assert_eq!(bytes.len(), keypair.length()); + let keypair_ = Keypair::deserialize(&bytes).unwrap(); + assert_eq!(keypair, keypair_); + } } diff --git a/src/core/pke/elgamal/mod.rs~ b/src/core/pke/elgamal/mod.rs~ deleted file mode 100644 index 7486c023..00000000 --- a/src/core/pke/elgamal/mod.rs~ +++ /dev/null @@ -1 +0,0 @@ -mod ristretto_25519; diff --git a/src/core/pke/elgamal/ristretto_25519.rs~ b/src/core/pke/elgamal/ristretto_25519.rs~ deleted file mode 100644 index 91987d2a..00000000 --- a/src/core/pke/elgamal/ristretto_25519.rs~ +++ /dev/null @@ -1,69 +0,0 @@ -use cosmian_crypto_core::{kdf256, reexport::rand_core::CryptoRngCore, Secret, SymmetricKey}; - -pub use R25519PrivateKey as Scalar; -pub use R25519PublicKey as EcPoint; - -pub type ElGamalKeypair = (Scalar, EcPoint); - -/// One-Time Pad (OTP) encryption of the given plaintext. -fn otp_encrypt( - key: &SymmetricKey, - ptx: &Secret, -) -> [u8; LENGTH] { - let mut ctx = [0; LENGTH]; - for pos in 0..LENGTH { - ctx[pos] = key[pos] ^ ptx[pos]; - } - ctx -} - -/// One-Time Pad (OTP) decryption of the given ciphertext. -fn otp_decrypt( - key: &SymmetricKey, - ctx: &[u8; LENGTH], -) -> Secret { - let mut ptx = Secret::::new(); - for pos in 0..LENGTH { - ctx[pos] = key[pos] ^ ptx[pos]; - } - ptx -} - -/// Encrypts the given secret using a classic KEM. -/// -/// # Security -/// -/// This is a KEM-DEM PKE implementation using an ElGammal KEM and the one-time -/// pad as DEM. Security therefore relies on the fact that different secrets are -/// never encrypted using the same ElGammal keypair. -/// -/// Provides 128-bit of classical security. -pub fn encrypt( - rng: &mut impl CryptoRngCore, - sk: &Scalar, - pk: &EcPoint, - ptx: &Secret, -) -> [u8; LENGTH] { - let mut shared_secret = SymmetricKey::::default(); - kdf256!(&mut shared_secret, &(pk * &sk).to_bytes()); - otp_encrypt(&shared_secret, &*ptx) -} - -/// Decrypts the given secret using a classic KEM. -/// -/// # Security -/// -/// This is a KEM-DEM PKE implementation using an ElGammal KEM and the one-time -/// pad as DEM. Security therefore relies on the fact that different secrets are -/// never encrypted using the same ElGammal keypair. -/// -/// Provides 128-bit of classical security. -pub fn decrypt( - sk: &Scalar, - pk: &EcPoint, - ctx: &[u8; LENGTH], -) -> Secret { - let mut shared_secret = SymmetricKey::::default(); - kdf256!(&mut *shared_secret, &(pk * sk).to_bytes()); - otp_decrypt(&*shared_secret, ctx) -} diff --git a/src/core/pke/mod.rs~ b/src/core/pke/mod.rs~ deleted file mode 100644 index 99146c1b..00000000 --- a/src/core/pke/mod.rs~ +++ /dev/null @@ -1 +0,0 @@ -mod elg diff --git a/src/core/pke/postquantum.rs~ b/src/core/pke/postquantum.rs~ deleted file mode 100644 index 79386198..00000000 --- a/src/core/pke/postquantum.rs~ +++ /dev/null @@ -1,38 +0,0 @@ -/// Encrypts the given secret using a post-quantum secure PKE. -/// -/// # Security -/// -/// The current implementation uses IND-CPA-Kyber 768, but plan for generalizing -/// it is on the way. It provides TODO bits of post-quantum security. -fn postquantum_pke_encrypt( - rng: &mut impl CryptoRngCore, - postquantum_pk: &KyberPublicKey, - secret: &[u8], -) -> Result { - if KYBER_INDCPA_BYTES < secret.len() { - return Err(Error::OperationNotPermitted(format!( - "cannot encrypt a secret of size {} in a ciphertext of size {KYBER_INDCPA_BYTES}", - secret.len() - ))); - } - let mut ctx = KyberCiphertext::new(); - let mut coin = Zeroizing::new([0; KYBER_SYMBYTES]); - rng.fill_bytes(&mut *coin); - indcpa_enc(&mut ctx, secret, postquantum_pk, &*coin); - Ok(ctx) -} - -/// Decrypts the given secret using a post-quantum secure PKE. -/// -/// # Security -/// -/// The current implementation uses IND-CPA-Kyber 768, but plan for generalizing -/// it is on the way. It provides TODO bits of post-quantum security. -fn postquantum_pke_decrypt( - postquantum_sk: &KyberSecretKey, - ctx: &KyberCiphertext, -) -> Secret { - let mut secret = Secret::::default(); - indcpa_dec(&mut *secret, ctx, postquantum_sk); - secret -} diff --git a/src/core/pke/postquantum/kyber.rs b/src/core/pke/postquantum/kyber.rs index 42178b26..9a24d1c8 100644 --- a/src/core/pke/postquantum/kyber.rs +++ b/src/core/pke/postquantum/kyber.rs @@ -6,16 +6,19 @@ use std::ops::{Deref, DerefMut}; use crate::Error; -use cosmian_crypto_core::{reexport::rand_core::CryptoRngCore, Secret}; +use cosmian_crypto_core::{bytes_ser_de::Serializable, reexport::rand_core::CryptoRngCore, Secret}; use pqc_kyber::{ indcpa::{indcpa_dec, indcpa_enc, indcpa_keypair}, - KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES, + KYBER_INDCPA_BYTES, KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES, KYBER_SYMBYTES, }; -pub use pqc_kyber::{KYBER_INDCPA_BYTES, KYBER_SYMBYTES}; /// Kyber public key length #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct PublicKey(Box<[u8; KYBER_INDCPA_PUBLICKEYBYTES]>); +pub struct PublicKey(Box<[u8; Self::LENGTH]>); + +impl PublicKey { + pub const LENGTH: usize = KYBER_INDCPA_PUBLICKEYBYTES; +} impl Deref for PublicKey { type Target = [u8]; @@ -31,9 +34,35 @@ impl DerefMut for PublicKey { } } +impl Serializable for PublicKey { + type Error = Error; + + fn length(&self) -> usize { + Self::LENGTH + } + + fn write( + &self, + ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, + ) -> Result { + ser.write_array(self).map_err(Self::Error::from) + } + + fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + de.read_array() + .map(Box::new) + .map(Self) + .map_err(Self::Error::from) + } +} + /// Kyber secret key. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct SecretKey(Secret); +pub struct SecretKey(Secret<{ Self::LENGTH }>); + +impl SecretKey { + pub const LENGTH: usize = KYBER_INDCPA_SECRETKEYBYTES; +} impl Deref for SecretKey { type Target = [u8]; @@ -49,8 +78,29 @@ impl DerefMut for SecretKey { } } +impl Serializable for SecretKey { + type Error = Error; + + fn length(&self) -> usize { + Self::LENGTH + } + + fn write( + &self, + ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, + ) -> Result { + ser.write_array(self).map_err(Self::Error::from) + } + + fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + let mut bytes = de.read_array()?; + let secret = Secret::from_unprotected_bytes(&mut bytes); + Ok(Self(secret)) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Ciphertext(Box<[u8; KYBER_INDCPA_BYTES]>); +pub struct Ciphertext(Box<[u8; Self::LENGTH]>); impl Deref for Ciphertext { type Target = [u8]; @@ -68,7 +118,32 @@ impl DerefMut for Ciphertext { impl Default for Ciphertext { fn default() -> Self { - Self(Box::new([0; KYBER_INDCPA_BYTES])) + Self(Box::new([0; Self::LENGTH])) + } +} + +impl Ciphertext { + pub const LENGTH: usize = KYBER_INDCPA_BYTES; +} + +impl Serializable for Ciphertext { + type Error = Error; + + fn length(&self) -> usize { + Self::LENGTH + } + + fn write( + &self, + ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, + ) -> Result { + ser.write_array(self).map_err(Self::Error::from) + } + + fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + de.read_array::<{ Self::LENGTH }>() + .map(|bytes| Self(Box::new(bytes))) + .map_err(Self::Error::from) } } @@ -76,7 +151,7 @@ impl Default for Ciphertext { pub fn keygen(rng: &mut impl CryptoRngCore) -> (SecretKey, PublicKey) { let (mut sk, mut pk) = ( SecretKey(Secret::new()), - PublicKey(Box::new([0; KYBER_INDCPA_PUBLICKEYBYTES])), + PublicKey(Box::new([0; PublicKey::LENGTH])), ); indcpa_keypair(&mut pk, &mut sk, None, rng); (sk, pk) diff --git a/src/core/pke/postquantum/kyber.rs~ b/src/core/pke/postquantum/kyber.rs~ deleted file mode 100644 index c095b62d..00000000 --- a/src/core/pke/postquantum/kyber.rs~ +++ /dev/null @@ -1,54 +0,0 @@ -//! Exposes a post-quantum PKE for use in the Covercrypt scheme. -//! -//! Current implementation only uses Krystal Kyber, but support for more -//! algorithms may be developed in the future. - -use cosmian_crypto_core::{reexport::rand_core::CryptoRngCore, Secret}; -use pqc_kyber::{ - indcpa::{indcpa_dec, indcpa_enc}, - KYBER_INDCPA_BYTES, KYBER_SYMBYTES, -}; - -use crate::{ - core::pq_kem::kyber::{KyberCiphertext, KyberPublicKey, KyberSecretKey}, - Error, -}; - -/// Encrypts the given secret using a post-quantum secure PKE. -/// -/// # Security -/// -/// The current implementation uses IND-CPA-Kyber 768, but plan for generalizing -/// it is on the way. It provides TODO bits of post-quantum security. -pub fn encrypt( - rng: &mut impl CryptoRngCore, - postquantum_pk: &KyberPublicKey, - secret: &[u8], -) -> Result { - if KYBER_INDCPA_BYTES < secret.len() { - return Err(Error::OperationNotPermitted(format!( - "cannot encrypt a secret of size {} in a ciphertext of size {KYBER_INDCPA_BYTES}", - secret.len() - ))); - } - let mut ctx = KyberCiphertext::default(); - let mut coin = Secret::::default(); - rng.fill_bytes(&mut *coin); - indcpa_enc(&mut ctx, secret, postquantum_pk, &*coin); - Ok(ctx) -} - -/// Decrypts the given secret using a post-quantum secure PKE. -/// -/// # Security -/// -/// The current implementation uses IND-CPA-Kyber 768, but plan for generalizing -/// it is on the way. It provides TODO bits of post-quantum security. -pub fn decrypt( - postquantum_sk: &KyberSecretKey, - ctx: &KyberCiphertext, -) -> Secret { - let mut secret = Secret::::default(); - indcpa_dec(&mut *secret, ctx, postquantum_sk); - secret -} diff --git a/src/core/pke/postquantum/mod.rs b/src/core/pke/postquantum/mod.rs index 586949b9..7cbe03be 100644 --- a/src/core/pke/postquantum/mod.rs +++ b/src/core/pke/postquantum/mod.rs @@ -1,8 +1,10 @@ mod kyber; -use cosmian_crypto_core::reexport::rand_core::CryptoRngCore; +use cosmian_crypto_core::{bytes_ser_de::Serializable, reexport::rand_core::CryptoRngCore}; pub use kyber::{decrypt, encrypt, keygen, Ciphertext, PublicKey, SecretKey}; +use crate::Error; + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Keypair(SecretKey, PublicKey); @@ -31,3 +33,45 @@ impl Keypair { &self.0 == sk } } + +impl Serializable for Keypair { + type Error = Error; + + fn length(&self) -> usize { + self.0.length() + self.1.length() + } + + fn write( + &self, + ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, + ) -> Result { + let mut n = ser.write(&self.0)?; + n += ser.write(&self.1)?; + Ok(n) + } + + fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { + let sk = de.read()?; + let pk = de.read()?; + Ok(Self(sk, pk)) + } +} + +#[cfg(test)] +mod tests { + use cosmian_crypto_core::{ + bytes_ser_de::Serializable, reexport::rand_core::SeedableRng, CsRng, + }; + + use super::Keypair; + + #[test] + fn test_postquantum_keypair_serialization() { + let mut rng = CsRng::from_entropy(); + let keypair = Keypair::random(&mut rng); + let bytes = keypair.serialize().unwrap(); + assert_eq!(bytes.len(), keypair.length()); + let keypair_ = Keypair::deserialize(&bytes).unwrap(); + assert_eq!(keypair, keypair_); + } +} diff --git a/src/core/pke/postquantum/mod.rs~ b/src/core/pke/postquantum/mod.rs~ deleted file mode 100644 index 8fbc3122..00000000 --- a/src/core/pke/postquantum/mod.rs~ +++ /dev/null @@ -1 +0,0 @@ -mod kyber; diff --git a/src/core/pq_kem/kyber.rs b/src/core/pq_kem/kyber.rs deleted file mode 100644 index 8b137891..00000000 --- a/src/core/pq_kem/kyber.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/core/pq_kem/kyber.rs~ b/src/core/pq_kem/kyber.rs~ deleted file mode 100644 index 22cfccdb..00000000 --- a/src/core/pq_kem/kyber.rs~ +++ /dev/null @@ -1,25 +0,0 @@ -use pqc_kyber::{KYBER_INDCPA_BYTES, KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES}; - -/// Kyber public key length -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct KyberPublicKey([u8; KYBER_INDCPA_PUBLICKEYBYTES]); - -impl Deref for KyberPublicKey { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// Kyber secret key. -#[derive(Debug, Clone, PartialEq, Eq, Hash, ZeroizeOnDrop)] -pub struct KyberSecretKey([u8; KYBER_INDCPA_SECRETKEYBYTES]); - -impl Deref for KyberSecretKey { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/src/core/pq_kem/mod.rs b/src/core/pq_kem/mod.rs deleted file mode 100644 index 19c6bc24..00000000 --- a/src/core/pq_kem/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod kyber; diff --git a/src/core/pq_kem/mod.rs~ b/src/core/pq_kem/mod.rs~ deleted file mode 100644 index 8fbc3122..00000000 --- a/src/core/pq_kem/mod.rs~ +++ /dev/null @@ -1 +0,0 @@ -mod kyber; diff --git a/src/core/primitives.rs b/src/core/primitives.rs index 6721118f..164f34b7 100644 --- a/src/core/primitives.rs +++ b/src/core/primitives.rs @@ -320,6 +320,10 @@ pub fn rekey( coordinate, CoordinateKeypair::random(rng, &h, is_hybridized), ); + } else { + return Err(Error::OperationNotPermitted( + "cannot re-key coordinate that does not belong to the MSK".to_string(), + )); } } Ok(()) @@ -420,315 +424,3 @@ fn refresh_coordinate_keys( }) .collect::>() } - -#[cfg(test)] -mod tests { - use cosmian_crypto_core::{reexport::rand_core::SeedableRng, CsRng}; - - #[test] - fn test_kyber() { - let mut rng = CsRng::from_entropy(); - let keypair = pqc_kyber::keypair(&mut rng); - let (ct, ss) = pqc_kyber::encapsulate(&keypair.public, &mut rng).unwrap(); - let res = pqc_kyber::decapsulate(&ct, &keypair.secret).unwrap(); - assert_eq!(ss, res, "Decapsulation failed!"); - } - - // #[test] - // fn test_cover_crypt() -> Result<(), Error> { - // let mut rng = CsRng::from_entropy(); - // let admin_partition = Coordinate::random(&mut rng); - // let dev_partition = Coordinate::random(&mut rng); - - // let universal_coordinates = HashMap::from([ - // ( - // admin_partition.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ( - // dev_partition.clone(), - // (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), - // ), - // ]); - // let decryption_set = HashSet::from([admin_partition.clone(), dev_partition.clone()]); - // let target_set = HashSet::from([admin_partition.clone()]); - - // let msk = setup(&mut rng, MIN_TRACING_LEVEL); - - // let admin_secret_subkeys = msk.coordinate_keys.get_latest(&admin_partition); - // assert!(admin_secret_subkeys.is_some()); - // assert!(admin_secret_subkeys.unwrap().0.is_some()); - - // // The developer partition matches a classic sub-key. - // let dev_secret_subkeys = msk.coordinate_keys.get_latest(&dev_partition); - // assert!(dev_secret_subkeys.is_some()); - // assert!(dev_secret_subkeys.unwrap().0.is_none()); - - // // Generate user secret keys. - // let mut dev_usk = keygen(&mut rng, &msk, &decryption_set[0])?; - // let admin_usk = keygen(&mut rng, &msk, &decryption_set[1])?; - - // // Encapsulate key for the admin target set. - // let (sym_key, encapsulation) = encaps(&mut rng, &mpk, &target_set).unwrap(); - - // // The encapsulation holds a unique, hybridized key encapsulation. - // assert_eq!(encapsulation.encs.len(), 1); - // for key_encapsulation in &encapsulation.encs { - // if let SeedEncapsulation::Classic(_) = key_encapsulation { - // panic!("Wrong hybridization type"); - // } - // } - - // // Developer is unable to decapsulate. - // let res0 = decaps(&dev_usk, &encapsulation); - // assert!(res0.is_err(), "User 0 shouldn't be able to decapsulate!"); - - // // Admin is able to decapsulate. - // let res1 = decaps(&admin_usk, &encapsulation)?; - // assert_eq!(sym_key, res1, "Wrong decapsulation for user 1!"); - - // // Change partitions - // let client_partition = Coordinate(b"client".to_vec()); - // let new_partitions_set = HashMap::from([ - // ( - // dev_partition.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ( - // client_partition.clone(), - // (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), - // ), - // ]); - // let client_target_set = HashSet::from([client_partition.clone()]); - - // update_coordinate_keys(&mut rng, &mut msk, &mut mpk, new_partitions_set)?; - // refresh(&msk, &mut dev_usk, true)?; - - // // The dev partition matches a hybridized sub-key. - // let dev_secret_subkeys = msk.coordinate_keys.get_latest(&dev_partition); - // assert!(dev_secret_subkeys.is_some()); - // assert!(dev_secret_subkeys.unwrap().0.is_some()); - - // // The client partition matches a classic sub-key. - // let client_secret_subkeys = msk.coordinate_keys.get_latest(&client_partition); - // assert!(client_secret_subkeys.is_some()); - // assert!(client_secret_subkeys.unwrap().0.is_none()); - - // // The developer now has a hybridized key. - // assert_eq!(dev_usk.coordinate_keys.count_elements(), 1); - // for key_encapsulation in &encapsulation.encs { - // if let SeedEncapsulation::Classic(_) = key_encapsulation { - // panic!("Wrong hybridization type"); - // } - // } - - // let (sym_key, new_encapsulation) = encaps(&mut rng, &mpk, &client_target_set)?; - - // // Client encapsulation holds a unique, classic key encapsulation. - // assert_eq!(new_encapsulation.encs.len(), 1); - // for key_encapsulation in &new_encapsulation.encs { - // if let SeedEncapsulation::Hybridized(_) = key_encapsulation { - // panic!("Wrong hybridization type"); - // } - // } - - // // The developer is unable to decapsulate. - // let res0 = decaps(&dev_usk, &encapsulation); - // assert!( - // res0.is_err(), - // "User 0 should not be able to decapsulate the old encapsulation." - // ); - - // // The admin is unable to decapsulate. - // let res1 = decaps(&admin_usk, &new_encapsulation); - // assert!( - // res1.is_err(), - // "User 1 should not be able to decapsulate the new encapsulation." - // ); - - // // Client is able to decapsulate. - // let client_usk = keygen(&mut rng, &msk, &HashSet::from([client_partition]))?; - // let res0 = decaps(&client_usk, &new_encapsulation); - // match res0 { - // Err(err) => panic!("Client should be able to decapsulate: {err:?}"), - // Ok(res) => assert_eq!(sym_key, res, "Wrong decapsulation."), - // } - - // Ok(()) - // } - - // #[test] - // fn test_master_keys_update() -> Result<(), Error> { - // let partition_1 = Coordinate(b"1".to_vec()); - // let partition_2 = Coordinate(b"2".to_vec()); - // // partition list - // let partitions_set = HashMap::from([ - // ( - // partition_1.clone(), - // (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), - // ), - // ( - // partition_2.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ]); - // // secure random number generator - // let mut rng = CsRng::from_entropy(); - // // setup scheme - // let (mut msk, mut mpk) = setup(&mut rng, partitions_set); - - // // now remove partition 1 and add partition 3 - // let partition_3 = Coordinate(b"3".to_vec()); - // let new_partitions_set = HashMap::from([ - // ( - // partition_2.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ( - // partition_3.clone(), - // (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), - // ), - // ]); - // update_coordinate_keys(&mut rng, &mut msk, &mut mpk, new_partitions_set)?; - // assert!(!msk.coordinate_keys.contains_key(&partition_1)); - // assert!(msk.coordinate_keys.contains_key(&partition_2)); - // assert!(msk.coordinate_keys.contains_key(&partition_3)); - // assert!(!mpk.coordinate_keys.contains_key(&partition_1)); - // assert!(mpk.coordinate_keys.contains_key(&partition_2)); - // assert!(mpk.coordinate_keys.contains_key(&partition_3)); - // Ok(()) - // } - - // #[test] - // fn test_user_key_refresh() -> Result<(), Error> { - // let partition_1 = Coordinate(b"1".to_vec()); - // let partition_2 = Coordinate(b"2".to_vec()); - // let partition_3 = Coordinate(b"3".to_vec()); - // // partition list - // let partitions_set = HashMap::from([ - // ( - // partition_1.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ( - // partition_2.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ( - // partition_3.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ]); - // // secure random number generator - // let mut rng = CsRng::from_entropy(); - // // setup scheme - // let (mut msk, mut mpk) = setup(&mut rng, partitions_set); - // // create a user key with access to partition 1 and 2 - // let mut usk = keygen( - // &mut rng, - // &msk, - // &HashSet::from([partition_1.clone(), partition_2.clone()]), - // )?; - - // // now remove partition 1 and remove hybrid key from partition 3 - // let new_partition_set = HashMap::from([ - // ( - // partition_2.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ( - // partition_3.clone(), - // (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), - // ), - // ]); - // //Covercrypt the master keys - - // let old_msk = MasterSecretKey::deserialize(msk.serialize()?.as_slice())?; - // update_coordinate_keys(&mut rng, &mut msk, &mut mpk, new_partition_set)?; - // // refresh the user key - // refresh(&msk, &mut usk, true)?; - // // user key kept old access to partition 1 - // assert!(!usk.coordinate_keys.flat_iter().any(|x| { - // x == ( - // &partition_1, - // old_msk.coordinate_keys.get_latest(&partition_1).unwrap(), - // ) - // })); - // assert!(usk.coordinate_keys.flat_iter().any(|x| { - // x == ( - // &partition_2, - // msk.coordinate_keys.get_latest(&partition_2).unwrap(), - // ) - // })); - // // user key kept the old hybrid key for partition 3 - // assert!(!usk.coordinate_keys.flat_iter().any(|x| { - // x == ( - // &partition_3, - // old_msk.coordinate_keys.get_latest(&partition_3).unwrap(), - // ) - // })); - - // // add new key for partition 2 - // rekey( - // &mut rng, - // &mut msk, - // &mut mpk, - // HashSet::from([partition_2.clone()]), - // )?; - // // refresh the user key - // refresh(&msk, &mut usk, true)?; - // let usk_subkeys: Vec<_> = usk - // .coordinate_keys - // .flat_iter() - // .filter(|(part, _)| *part == &partition_2) - // .map(|(_, subkey)| subkey) - // .collect(); - // let msk_subkeys: Vec<_> = msk - // .coordinate_keys - // .get(&partition_2) - // .unwrap() - // .iter() - // .collect(); - // assert_eq!(usk_subkeys.len(), 2); - // assert_eq!(usk_subkeys, msk_subkeys); - - // Ok(()) - // } - - // #[test] - // fn test_user_key_kmac() -> Result<(), Error> { - // let partition_1 = Coordinate(b"1".to_vec()); - // let partition_2 = Coordinate(b"2".to_vec()); - // // partition list - // let partitions_set = HashMap::from([ - // ( - // partition_1.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ( - // partition_2.clone(), - // (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - // ), - // ]); - // // secure random number generator - // let mut rng = CsRng::from_entropy(); - // // setup scheme - // let (msk, _) = setup(&mut rng, partitions_set); - // // create a user key with access to partition 1 and 2 - // let mut usk = keygen(&mut rng, &msk, &HashSet::from([partition_1, partition_2]))?; - - // assert!(verify_usk(&msk, &usk).is_ok()); - // let bytes = usk.serialize()?; - // let usk_ = UserSecretKey::deserialize(&bytes)?; - // assert!(verify_usk(&msk, &usk_).is_ok()); - - // usk.coordinate_keys.create_chain_with_single_value( - // Coordinate(b"3".to_vec()), - // (None, R25519PrivateKey::new(&mut rng)), - // ); - // // KMAC verify will fail after modifying the user key - // assert!(verify_usk(&msk, &usk).is_err()); - - // Ok(()) - // } -} diff --git a/src/core/serialization.rs b/src/core/serialization.rs deleted file mode 100644 index 5457772c..00000000 --- a/src/core/serialization.rs +++ /dev/null @@ -1,538 +0,0 @@ -//! Implements the serialization methods for the `Covercrypt` objects. - -use std::collections::{HashMap, HashSet, LinkedList}; - -use cosmian_crypto_core::{ - bytes_ser_de::{to_leb128_len, Deserializer, Serializable, Serializer}, - FixedSizeCBytes, R25519PrivateKey, R25519PublicKey, RandomFixedSizeCBytes, SymmetricKey, -}; -use pqc_kyber::{KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES}; - -use super::{KMAC_KEY_LENGTH, KMAC_SIG_LENGTH, TAG_LENGTH}; -use crate::{ - abe_policy::Coordinate, - core::{ - Encapsulation, MasterPublicKey, MasterSecretKey, SeedEncapsulation, UserSecretKey, - SEED_LENGTH, - }, - data_struct::{RevisionMap, RevisionVec}, - CleartextHeader, EncryptedHeader, Error, -}; - -/// Returns the byte length of a serialized option -macro_rules! serialize_len_option { - ($option:expr, $value:ident, $method:expr) => {{ - let mut length = 1; - if let Some($value) = &$option { - length += $method; - } - length - }}; -} - -/// Serialize an optional value as a LEB128-encoded unsigned integer followed by -/// the serialization of the contained value if any. -macro_rules! serialize_option { - ($serializer:expr, $n:expr, $option:expr, $value:ident, $method:expr) => {{ - if let Some($value) = &$option { - $n += $serializer.write_leb128_u64(1)?; - $n += $method?; - } else { - $n += $serializer.write_leb128_u64(0)?; - } - }}; -} - -/// Deserialize an optional value from a LEB128-encoded unsigned integer -/// followed by the deserialization of the contained value if any. -macro_rules! deserialize_option { - ($deserializer:expr, $method:expr) => {{ - let is_some = $deserializer.read_leb128_u64()?; - if is_some == 1 { - Some($method) - } else { - None - } - }}; -} - -impl Serializable for MasterPublicKey { - type Error = Error; - - fn length(&self) -> usize { - let mut length = 2 * R25519PublicKey::LENGTH - // subkeys serialization - + to_leb128_len(self.coordinate_keys.len()) - + self.coordinate_keys.len() * R25519PublicKey::LENGTH; - for (partition, (pk_i, _)) in &self.coordinate_keys { - length += to_leb128_len(partition.len()) + partition.len(); - length += serialize_len_option!(pk_i, _value, KYBER_INDCPA_PUBLICKEYBYTES); - } - length - } - - fn write(&self, ser: &mut Serializer) -> Result { - let mut n = ser.write_array(&self.g1.to_bytes())?; - n += ser.write_array(&self.g2.to_bytes())?; - n += ser.write_leb128_u64(self.coordinate_keys.len() as u64)?; - for (partition, (pk_i, h_i)) in &self.coordinate_keys { - n += ser.write_vec(partition)?; - serialize_option!(ser, n, pk_i, value, ser.write_array(value)); - n += ser.write_array(&h_i.to_bytes())?; - } - Ok(n) - } - - fn read(de: &mut Deserializer) -> Result { - let g1 = R25519PublicKey::try_from_bytes(de.read_array::<{ R25519PublicKey::LENGTH }>()?)?; - let g2 = R25519PublicKey::try_from_bytes(de.read_array::<{ R25519PublicKey::LENGTH }>()?)?; - let n_partitions = ::try_from(de.read_leb128_u64()?)?; - let mut subkeys = HashMap::with_capacity(n_partitions); - for _ in 0..n_partitions { - let partition = Coordinate::from(de.read_vec()?); - let pk_i = deserialize_option!(de, KyberPublicKey(de.read_array()?)); - let h_i = - R25519PublicKey::try_from_bytes(de.read_array::<{ R25519PublicKey::LENGTH }>()?)?; - subkeys.insert(partition, (pk_i, h_i)); - } - Ok(Self { - g1, - g2, - coordinate_keys: subkeys, - }) - } -} - -impl Serializable for MasterSecretKey { - type Error = Error; - - fn length(&self) -> usize { - let mut length = 3 * R25519PrivateKey::LENGTH - + self.signing_key.as_ref().map_or_else(|| 0, |key| key.len()) - // subkeys serialization - + to_leb128_len(self.coordinate_keypairs.len()) - + self.coordinate_keypairs.count_elements() * R25519PrivateKey::LENGTH; - for (partition, chain) in &self.coordinate_keypairs.map { - length += to_leb128_len(partition.len()) + partition.len(); - length += to_leb128_len(chain.len()); - for (sk_i, _) in chain { - let x = serialize_len_option!(sk_i, _value, KYBER_INDCPA_SECRETKEYBYTES); - length += x; - } - } - length - } - - fn write(&self, ser: &mut Serializer) -> Result { - let mut n = ser.write_array(&self.s1.to_bytes())?; - n += ser.write_array(&self.s2.to_bytes())?; - n += ser.write_array(&self.s.to_bytes())?; - n += ser.write_leb128_u64(self.coordinate_keypairs.len() as u64)?; - for (partition, chain) in &self.coordinate_keypairs.map { - n += ser.write_vec(partition)?; - n += ser.write_leb128_u64(chain.len() as u64)?; - for (sk_i, x_i) in chain { - serialize_option!(ser, n, sk_i, value, ser.write_array(value)); - n += ser.write_array(&x_i.to_bytes())?; - } - } - if let Some(kmac_key) = &self.signing_key { - n += ser.write_array(kmac_key)?; - } - - Ok(n) - } - - fn read(de: &mut Deserializer) -> Result { - let s1 = - R25519PrivateKey::try_from_bytes(de.read_array::<{ R25519PrivateKey::LENGTH }>()?)?; - let s2 = - R25519PrivateKey::try_from_bytes(de.read_array::<{ R25519PrivateKey::LENGTH }>()?)?; - let s = R25519PrivateKey::try_from_bytes(de.read_array::<{ R25519PrivateKey::LENGTH }>()?)?; - - let n_partitions = ::try_from(de.read_leb128_u64()?)?; - let mut subkeys = RevisionMap::with_capacity(n_partitions); - for _ in 0..n_partitions { - let partition = Coordinate::from(de.read_vec()?); - let n_keys = ::try_from(de.read_leb128_u64()?)?; - let chain: Result, Self::Error> = (0..n_keys) - .map(|_| { - let sk_i = deserialize_option!(de, KyberSecretKey(de.read_array()?)); - let x_i = de.read_array::<{ R25519PrivateKey::LENGTH }>()?; - Ok((sk_i, R25519PrivateKey::try_from_bytes(x_i)?)) - }) - .collect(); - subkeys.map.insert(partition, chain?); - } - - let kmac_key = match de.read_array::<{ KMAC_KEY_LENGTH }>() { - Ok(key_bytes) => Some(SymmetricKey::try_from_bytes(key_bytes)?), - Err(_) => None, - }; - - Ok(Self { - s, - s1, - s2, - coordinate_keypairs: subkeys, - signing_key: kmac_key, - }) - } -} - -impl Serializable for UserSecretKey { - type Error = Error; - - fn length(&self) -> usize { - let mut length = 2 * R25519PrivateKey::LENGTH - + self.msk_signature.as_ref().map_or_else(|| 0, |kmac| kmac.len()) - // subkeys serialization - + to_leb128_len(self.coordinate_keys.len()) - + self.coordinate_keys.count_elements() * R25519PrivateKey::LENGTH; - for (partition, chain) in self.coordinate_keys.iter() { - length += to_leb128_len(partition.len()) + partition.len(); - length += to_leb128_len(chain.len()); - for (sk_i, _) in chain { - length += serialize_len_option!(sk_i, _value, KYBER_INDCPA_SECRETKEYBYTES); - } - } - length - } - - fn write(&self, ser: &mut Serializer) -> Result { - let mut n = ser.write_array(&self.a.to_bytes())?; - n += ser.write_array(&self.b.to_bytes())?; - n += ser.write_leb128_u64(self.coordinate_keys.len() as u64)?; - for (partition, chain) in self.coordinate_keys.iter() { - // write chain partition - n += ser.write_vec(partition)?; - // iterate through all subkeys in the chain - n += ser.write_leb128_u64(chain.len() as u64)?; - for (sk_i, x_i) in chain { - serialize_option!(ser, n, sk_i, value, ser.write_array(value)); - n += ser.write_array(&x_i.to_bytes())?; - } - } - if let Some(kmac) = &self.msk_signature { - n += ser.write_array(kmac)?; - } - Ok(n) - } - - fn read(de: &mut Deserializer) -> Result { - let a = R25519PrivateKey::try_from_bytes(de.read_array::<{ R25519PrivateKey::LENGTH }>()?)?; - let b = R25519PrivateKey::try_from_bytes(de.read_array::<{ R25519PrivateKey::LENGTH }>()?)?; - let n_partitions = ::try_from(de.read_leb128_u64()?)?; - let mut subkeys = RevisionVec::with_capacity(n_partitions); - for _ in 0..n_partitions { - let partition = Coordinate::from(de.read_vec()?); - // read all keys forming a chain and inserting them all at once. - let n_keys = ::try_from(de.read_leb128_u64()?)?; - let new_chain: Result, _> = (0..n_keys) - .map(|_| { - let sk_i = deserialize_option!(de, KyberSecretKey(de.read_array()?)); - let x_i = de.read_array::<{ R25519PrivateKey::LENGTH }>()?; - Ok::<_, Self::Error>((sk_i, R25519PrivateKey::try_from_bytes(x_i)?)) - }) - .collect(); - subkeys.insert_new_chain(partition, new_chain?); - } - let kmac = de.read_array::<{ KMAC_SIG_LENGTH }>().ok(); - - Ok(Self { - a, - b, - coordinate_keys: RefCell::new(subkeys), - msk_signature: kmac, - }) - } -} - -impl Serializable for SeedEncapsulation { - type Error = Error; - - fn length(&self) -> usize { - match self { - Self::Classic(e_i) => 1 + e_i.len(), - Self::Hybridized(epq_i) => 1 + epq_i.len(), - } - } - - fn write(&self, ser: &mut Serializer) -> Result { - let mut n = 0; - match self { - Self::Classic(e_i) => { - n += ser.write_leb128_u64(0)?; - n += ser.write_array(&**e_i)?; - } - Self::Hybridized(epq_i) => { - n += ser.write_leb128_u64(1)?; - n += ser.write_array(&**epq_i)?; - } - } - Ok(n) - } - - fn read(de: &mut Deserializer) -> Result { - let is_hybridized = de.read_leb128_u64()?; - if is_hybridized == 1 { - Ok(Self::Hybridized(Box::new(de.read_array()?))) - } else { - Ok(Self::Classic(Box::new(de.read_array()?))) - } - } -} - -impl Serializable for Encapsulation { - type Error = Error; - - fn length(&self) -> usize { - let mut length = 2 * R25519PublicKey::LENGTH + TAG_LENGTH + to_leb128_len(self.encs.len()); - for key_encasulation in &self.encs { - length += key_encasulation.length(); - } - length - } - - fn write(&self, ser: &mut Serializer) -> Result { - let mut n = ser.write_array(&self.c1.to_bytes())?; - n += ser.write_array(&self.c2.to_bytes())?; - n += ser.write_array(&self.tag)?; - n += ser.write_leb128_u64(self.encs.len() as u64)?; - for key_encapsulation in &self.encs { - n += ser.write(key_encapsulation)?; - } - Ok(n) - } - - fn read(de: &mut Deserializer) -> Result { - let c1 = R25519PublicKey::try_from_bytes(de.read_array::<{ R25519PublicKey::LENGTH }>()?)?; - let c2 = R25519PublicKey::try_from_bytes(de.read_array::<{ R25519PublicKey::LENGTH }>()?)?; - let tag = de.read_array()?; - let n_partitions = ::try_from(de.read_leb128_u64()?)?; - let mut encs = HashSet::with_capacity(n_partitions); - for _ in 0..n_partitions { - let key_encapsulation = de.read()?; - encs.insert(key_encapsulation); - } - Ok(Self { c1, c2, tag, encs }) - } -} - -impl Serializable for EncryptedHeader { - type Error = Error; - - fn length(&self) -> usize { - self.encapsulation.length() - + to_leb128_len( - self.encrypted_metadata - .as_ref() - .map(std::vec::Vec::len) - .unwrap_or_default(), - ) - + self - .encrypted_metadata - .as_ref() - .map(std::vec::Vec::len) - .unwrap_or_default() - } - - /// Tries to serialize the encrypted header. - fn write(&self, ser: &mut Serializer) -> Result { - let mut n = self.encapsulation.write(ser)?; - match &self.encrypted_metadata { - Some(bytes) => n += ser.write_vec(bytes)?, - None => n += ser.write_vec(&[])?, - } - Ok(n) - } - - /// Tries to deserialize the encrypted header. - fn read(de: &mut Deserializer) -> Result { - let encapsulation = de.read::()?; - let ciphertext = de.read_vec()?; - let encrypted_metadata = if ciphertext.is_empty() { - None - } else { - Some(ciphertext) - }; - Ok(Self { - encapsulation, - encrypted_metadata, - }) - } -} - -impl Serializable for CleartextHeader { - type Error = Error; - - fn length(&self) -> usize { - SEED_LENGTH - + to_leb128_len( - self.metadata - .as_ref() - .map(std::vec::Vec::len) - .unwrap_or_default(), - ) - + self - .metadata - .as_ref() - .map(std::vec::Vec::len) - .unwrap_or_default() - } - - /// Tries to serialize the cleartext header. - fn write(&self, ser: &mut Serializer) -> Result { - let mut n = ser.write_array(self.symmetric_key.as_bytes())?; - match &self.metadata { - Some(bytes) => n += ser.write_vec(bytes)?, - None => n += ser.write_vec(&[])?, - } - Ok(n) - } - - /// Tries to deserialize the cleartext header. - fn read(de: &mut Deserializer) -> Result { - let symmetric_key = SymmetricKey::try_from_bytes(de.read_array::()?)?; - let metadata = de.read_vec()?; - let metadata = if metadata.is_empty() { - None - } else { - Some(metadata) - }; - Ok(Self { - symmetric_key, - metadata, - }) - } -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use cosmian_crypto_core::{reexport::rand_core::SeedableRng, CsRng}; - - use super::*; - use crate::{ - abe_policy::{AttributeStatus, EncryptionHint}, - core::primitives::{encaps, keygen, setup}, - }; - - #[test] - fn test_serialization() -> Result<(), Error> { - // Setup - let admin_partition = Coordinate(b"admin".to_vec()); - let dev_partition = Coordinate(b"dev".to_vec()); - let partitions_set = HashMap::from([ - ( - admin_partition.clone(), - (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), - ), - ( - dev_partition.clone(), - (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), - ), - ]); - let user_set = HashSet::from([admin_partition.clone(), dev_partition.clone()]); - let target_set = HashSet::from([admin_partition, dev_partition]); - let mut rng = CsRng::from_entropy(); - - let (msk, mpk) = setup(&mut rng, partitions_set); - - // Check Covercrypt `MasterSecretKey` serialization. - let bytes = msk.serialize()?; - assert_eq!(bytes.len(), msk.length(), "Wrong master secret key length"); - let msk_ = MasterSecretKey::deserialize(&bytes)?; - assert_eq!(msk, msk_, "Wrong `MasterSecretKey` deserialization."); - assert!( - msk_.signing_key.is_some(), - "Wrong `MasterSecretKey` deserialization." - ); - assert_eq!( - msk.signing_key, msk_.signing_key, - "Wrong `MasterSecretKey` deserialization." - ); - - // Check Covercrypt `PublicKey` serialization. - let bytes = mpk.serialize()?; - assert_eq!(bytes.len(), mpk.length(), "Wrong master public key length"); - let mpk_ = MasterPublicKey::deserialize(&bytes)?; - assert_eq!(mpk, mpk_, "Wrong `PublicKey` derserialization."); - - // Check Covercrypt `UserSecretKey` serialization. - let usk = keygen(&mut rng, &msk, &user_set)?; - let bytes = usk.serialize()?; - assert_eq!(bytes.len(), usk.length(), "Wrong user secret key size"); - let usk_ = UserSecretKey::deserialize(&bytes)?; - assert_eq!(usk.a, usk_.a, "Wrong `UserSecretKey` deserialization."); - assert_eq!(usk.b, usk_.b, "Wrong `UserSecretKey` deserialization."); - assert_eq!( - usk.msk_signature, usk_.msk_signature, - "Wrong `UserSecretKey` deserialization." - ); - assert_eq!(usk, usk_, "Wrong `UserSecretKey` deserialization."); - - // Check Covercrypt `Encapsulation` serialization. - let (_, encapsulation) = encaps(&mut rng, &mpk, &target_set)?; - let bytes = encapsulation.serialize()?; - assert_eq!( - bytes.len(), - encapsulation.length(), - "Wrong encapsulation size" - ); - let encapsulation_ = Encapsulation::deserialize(&bytes)?; - assert_eq!( - encapsulation, encapsulation_, - "Wrong `Encapsulation` derserialization." - ); - - // Setup Covercrypt. - #[cfg(feature = "test_utils")] - { - use crate::{abe_policy::AccessPolicy, test_utils::policy, Covercrypt}; - - let cc = Covercrypt::default(); - let policy = policy()?; - let user_policy = AccessPolicy::from_boolean_expression( - "Department::MKG && Security Level::Top Secret", - )?; - let encryption_policy = AccessPolicy::from_boolean_expression( - "Department::MKG && Security Level::High Secret", - )?; - let (msk, mpk) = cc.generate_master_keys(&policy)?; - let usk = cc.generate_user_secret_key(&msk, &user_policy, &policy)?; - - // Check `EncryptedHeader` serialization. - let (_secret_key, encrypted_header) = - EncryptedHeader::generate(&cc, &policy, &mpk, &encryption_policy, None, None)?; - let bytes = encrypted_header.serialize()?; - assert_eq!( - bytes.len(), - encrypted_header.length(), - "Wrong encapsulation size." - ); - let encrypted_header_ = EncryptedHeader::deserialize(&bytes)?; - assert_eq!( - encrypted_header, encrypted_header_, - "Wrong `EncryptedHeader` derserialization." - ); - - // Check `CleartextHeader` serialization. - let cleartext_header = encrypted_header.decrypt(&cc, &usk, None)?; - let bytes = cleartext_header.serialize()?; - assert_eq!( - bytes.len(), - cleartext_header.length(), - "Wrong cleartext header size." - ); - let cleartext_header_ = CleartextHeader::deserialize(&bytes)?; - assert_eq!( - cleartext_header, cleartext_header_, - "Wrong `CleartextHeader` derserialization." - ); - } - - Ok(()) - } -} diff --git a/src/core/serialization/mod.rs b/src/core/serialization/mod.rs new file mode 100644 index 00000000..2be66d14 --- /dev/null +++ b/src/core/serialization/mod.rs @@ -0,0 +1,815 @@ +//! Implements the serialization methods for the `Covercrypt` objects. + +use std::collections::{HashMap, HashSet, LinkedList}; + +use cosmian_crypto_core::{ + bytes_ser_de::{to_leb128_len, Deserializer, Serializable, Serializer}, + FixedSizeCBytes, RandomFixedSizeCBytes, SymmetricKey, +}; + +use super::{ + pke::{ + self, + elgamal::{EcPoint, Scalar}, + postquantum::{self, PublicKey}, + }, + CoordinateKeypair, CoordinatePublicKey, CoordinateSecretKey, TracingPublicKey, + TracingSecretKey, UserId, KMAC_KEY_LENGTH, KMAC_SIG_LENGTH, TAG_LENGTH, +}; +use crate::{ + abe_policy::Coordinate, + core::{ + Encapsulation, MasterPublicKey, MasterSecretKey, SeedEncapsulation, UserSecretKey, + SEED_LENGTH, + }, + data_struct::{RevisionMap, RevisionVec}, + CleartextHeader, EncryptedHeader, Error, +}; + +impl Serializable for TracingPublicKey { + type Error = Error; + + fn length(&self) -> usize { + to_leb128_len(self.0.len()) + self.0.iter().map(Serializable::length).sum::() + } + + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = ser.write_leb128_u64(self.0.len() as u64)?; + for pk in self.0.iter() { + n += ser.write_array(&pk.to_bytes())?; + } + Ok(n) + } + + fn read(de: &mut Deserializer) -> Result { + let n_pk = ::try_from(de.read_leb128_u64()?)?; + let mut tracers = LinkedList::new(); + for _ in 0..n_pk { + let tracer = EcPoint::try_from_bytes(de.read_array::<{ EcPoint::LENGTH }>()?)?; + tracers.push_back(tracer); + } + Ok(Self(tracers)) + } +} + +impl Serializable for CoordinatePublicKey { + type Error = Error; + + fn length(&self) -> usize { + match self { + CoordinatePublicKey::Hybridized { .. } => { + 1 + SEED_LENGTH + pke::postquantum::PublicKey::LENGTH + } + CoordinatePublicKey::Classic { .. } => 1 + SEED_LENGTH, + } + } + + fn write(&self, ser: &mut Serializer) -> Result { + match self { + CoordinatePublicKey::Hybridized { + postquantum_pk, + elgamal_pk, + } => { + let mut n = ser.write_leb128_u64(1)?; + n += ser.write_array(postquantum_pk)?; + n += ser.write_array(&elgamal_pk.to_bytes())?; + Ok(n) + } + CoordinatePublicKey::Classic { elgamal_pk } => { + let mut n = ser.write_leb128_u64(0)?; + n += ser.write_array(&elgamal_pk.to_bytes())?; + Ok(n) + } + } + } + + fn read(de: &mut Deserializer) -> Result { + let is_hybridized = de.read_leb128_u64()?; + if 1 == is_hybridized { + Ok(Self::Hybridized { + postquantum_pk: de.read::()?, + elgamal_pk: de.read::()?, + }) + } else if 0 == is_hybridized { + Ok(Self::Classic { + elgamal_pk: de.read::()?, + }) + } else { + Err(Error::ConversionFailed(format!( + "invalid hybridization flag {is_hybridized}" + ))) + } + } +} + +impl Serializable for MasterPublicKey { + type Error = Error; + + fn length(&self) -> usize { + self.h.length() + + self.tpk.length() + + to_leb128_len(self.coordinate_keys.len()) + + self + .coordinate_keys + .iter() + .map(|(coordinate, pk)| coordinate.length() + pk.length()) + .sum::() + } + + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = ser.write_array(&self.h.to_bytes())?; + n += ser.write(&self.tpk)?; + n += ser.write_leb128_u64(self.coordinate_keys.len() as u64)?; + for (coordinate, pk) in &self.coordinate_keys { + n += ser.write(coordinate)?; + n += ser.write(pk)?; + } + Ok(n) + } + + fn read(de: &mut Deserializer) -> Result { + let h = de.read::()?; + let tpk = de.read::()?; + let n_coordinates = ::try_from(de.read_leb128_u64()?)?; + let mut coordinate_keys = HashMap::with_capacity(n_coordinates); + for _ in 0..n_coordinates { + let coordinate = de.read::()?; + let pk = de.read::()?; + coordinate_keys.insert(coordinate, pk); + } + Ok(Self { + h, + tpk, + coordinate_keys, + }) + } +} + +impl Serializable for TracingSecretKey { + type Error = Error; + + fn length(&self) -> usize { + to_leb128_len(self.users.len()) + + self.users.iter().map(Serializable::length).sum::() + + to_leb128_len(self.tracers.len()) + + self.tracers.iter().map(Serializable::length).sum::() + } + + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = ser.write_leb128_u64(self.users.len() as u64)?; + for id in &self.users { + n += ser.write(id)?; + } + n += ser.write_leb128_u64(self.tracers.len() as u64)?; + for tracer in &self.tracers { + n += ser.write(tracer)?; + } + Ok(n) + } + + fn read(de: &mut Deserializer) -> Result { + let n_users = ::try_from(de.read_leb128_u64()?)?; + let mut users = HashSet::with_capacity(n_users); + for _ in 0..n_users { + let id = de.read()?; + users.insert(id); + } + let n_tracers = ::try_from(de.read_leb128_u64()?)?; + let mut tracers = LinkedList::new(); + for _ in 0..n_tracers { + let t = de.read()?; + tracers.push_back(t); + } + Ok(Self { tracers, users }) + } +} + +impl Serializable for CoordinateKeypair { + type Error = Error; + + fn length(&self) -> usize { + self.elgamal_keypair.length() + + 1 + + self + .postquantum_keypair + .as_ref() + .map(Serializable::length) + .unwrap_or_default() + } + + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = ser.write(&self.elgamal_keypair)?; + if let Some(keypair) = &self.postquantum_keypair { + n += ser.write_leb128_u64(1)?; + n += ser.write(keypair)?; + } else { + n += ser.write_leb128_u64(0)?; + } + Ok(n) + } + + fn read(de: &mut Deserializer) -> Result { + let elgamal_keypair = de.read()?; + let is_hybridized = de.read_leb128_u64()?; + if 1 == is_hybridized { + let postquantum_keypair = de.read()?; + Ok(Self { + elgamal_keypair, + postquantum_keypair: Some(postquantum_keypair), + }) + } else if 0 == is_hybridized { + Ok(Self { + elgamal_keypair, + postquantum_keypair: None, + }) + } else { + Err(Error::ConversionFailed(format!( + "invalid hybridization flag {is_hybridized}" + ))) + } + } +} + +impl Serializable for MasterSecretKey { + type Error = Error; + + fn length(&self) -> usize { + self.s.length() + + self.tsk.length() + + to_leb128_len(self.coordinate_keypairs.len()) + + self + .coordinate_keypairs + .iter() + .map(|(coordinate, chain)| { + coordinate.length() + + to_leb128_len(chain.len()) + + chain.iter().map(Serializable::length).sum::() + }) + .sum::() + + self.signing_key.as_ref().map_or_else(|| 0, |key| key.len()) + } + + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = ser.write(&self.s)?; + n += ser.write(&self.tsk)?; + n += ser.write_leb128_u64(self.coordinate_keypairs.len() as u64)?; + for (coordinate, chain) in &self.coordinate_keypairs.map { + n += ser.write(coordinate)?; + n += ser.write_leb128_u64(to_leb128_len(chain.len()) as u64)?; + for sk in chain { + n += ser.write(sk)?; + } + } + if let Some(kmac_key) = &self.signing_key { + n += ser.write_array(kmac_key)?; + } + Ok(n) + } + + fn read(de: &mut Deserializer) -> Result { + let s = Scalar::try_from_bytes(de.read_array::<{ Scalar::LENGTH }>()?)?; + let tsk = de.read::()?; + let n_coordinates = ::try_from(de.read_leb128_u64()?)?; + println!("n_coordinates: {n_coordinates}"); + let mut coordinate_keypairs = RevisionMap::with_capacity(n_coordinates); + for i in 0..n_coordinates { + println!("reading coordinate {i}"); + let coordinate = de.read()?; + let n_keys = ::try_from(de.read_leb128_u64()?)?; + println!("n_keys {n_keys}"); + let chain = (0..n_keys) + .map(|_| de.read::()) + .collect::, _>>()?; + coordinate_keypairs.map.insert(coordinate, chain); + } + + println!("HEY"); + + let signing_key = if de.value().len() < KMAC_KEY_LENGTH { + None + } else { + Some(SymmetricKey::try_from_bytes( + de.read_array::()?, + )?) + }; + + println!("OH"); + + Ok(Self { + s, + tsk, + coordinate_keypairs, + signing_key, + }) + } +} + +impl Serializable for UserId { + type Error = Error; + + fn length(&self) -> usize { + to_leb128_len(self.0.len()) + self.iter().map(|marker| marker.length()).sum::() + } + + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = ser.write_leb128_u64(self.0.len() as u64)?; + for marker in &self.0 { + n += ser.write(marker)?; + } + Ok(n) + } + + fn read(de: &mut Deserializer) -> Result { + let length = ::try_from(de.read_leb128_u64()?)?; + let mut id = LinkedList::new(); + for _ in 0..length { + let marker = de.read()?; + id.push_back(marker); + } + Ok(Self(id)) + } +} + +impl Serializable for CoordinateSecretKey { + type Error = Error; + + fn length(&self) -> usize { + 1 + match self { + CoordinateSecretKey::Hybridized { + postquantum_sk, + elgamal_sk, + } => elgamal_sk.length() + postquantum_sk.length(), + CoordinateSecretKey::Classic { elgamal_sk } => elgamal_sk.length(), + } + } + + fn write(&self, ser: &mut Serializer) -> Result { + match self { + CoordinateSecretKey::Hybridized { + postquantum_sk, + elgamal_sk, + } => { + let mut n = ser.write_leb128_u64(1)?; + n += ser.write(elgamal_sk)?; + n += ser.write(postquantum_sk)?; + Ok(n) + } + CoordinateSecretKey::Classic { elgamal_sk } => { + let mut n = ser.write_leb128_u64(0)?; + n += ser.write(elgamal_sk)?; + Ok(n) + } + } + } + + fn read(de: &mut Deserializer) -> Result { + let is_hybridized = de.read_leb128_u64()?; + if 1 == is_hybridized { + let elgamal_sk = de.read()?; + let postquantum_sk = de.read()?; + Ok(Self::Hybridized { + postquantum_sk, + elgamal_sk, + }) + } else if 0 == is_hybridized { + Ok(Self::Classic { + elgamal_sk: de.read()?, + }) + } else { + Err(Error::ConversionFailed(format!( + "invalid hybridization flag {is_hybridized}" + ))) + } + } +} + +impl Serializable for UserSecretKey { + type Error = Error; + + fn length(&self) -> usize { + self.id.length() + + to_leb128_len(self.coordinate_keys.len()) + + self + .coordinate_keys + .iter() + .map(|(coordinate, chain)| { + coordinate.length() + + to_leb128_len(chain.len()) + + chain.iter().map(|sk| sk.length()).sum::() + }) + .sum::() + + self + .msk_signature + .as_ref() + .map_or_else(|| 0, |kmac| kmac.len()) + } + + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = ser.write(&self.id)?; + n += ser.write_leb128_u64(self.coordinate_keys.len() as u64)?; + for (coordinate, chain) in self.coordinate_keys.iter() { + n += ser.write(coordinate)?; + n += ser.write_leb128_u64(chain.len() as u64)?; + for sk in chain { + n += ser.write(sk)?; + } + } + if let Some(kmac) = &self.msk_signature { + n += ser.write_array(kmac)?; + } + Ok(n) + } + + fn read(de: &mut Deserializer) -> Result { + let id = de.read::()?; + let n_coordinates = ::try_from(de.read_leb128_u64()?)?; + let mut coordinate_keys = RevisionVec::with_capacity(n_coordinates); + for _ in 0..n_coordinates { + let coordinate = de.read()?; + let n_keys = ::try_from(de.read_leb128_u64()?)?; + let new_chain = (0..n_keys) + .map(|_| de.read::()) + .collect::>()?; + coordinate_keys.insert_new_chain(coordinate, new_chain); + } + let msk_signature = if de.value().len() < KMAC_SIG_LENGTH { + None + } else { + Some(de.read_array::()?) + }; + Ok(Self { + id, + coordinate_keys, + msk_signature, + }) + } +} + +impl Serializable for SeedEncapsulation { + type Error = Error; + + fn length(&self) -> usize { + 1 + match self { + Self::Classic(enc) => enc.len(), + Self::Hybridized(enc) => enc.length(), + } + } + + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = 0; + match self { + Self::Classic(enc) => { + n += ser.write_leb128_u64(0)?; + n += ser.write_array(enc)?; + } + Self::Hybridized(enc) => { + n += ser.write_leb128_u64(1)?; + n += ser.write(enc)?; + } + } + Ok(n) + } + + fn read(de: &mut Deserializer) -> Result { + let is_hybridized = de.read_leb128_u64()?; + if is_hybridized == 1 { + de.read::().map(Self::Hybridized) + } else { + de.read_array::() + .map(Self::Classic) + .map_err(Self::Error::from) + } + } +} + +impl Serializable for Encapsulation { + type Error = Error; + + fn length(&self) -> usize { + TAG_LENGTH + + to_leb128_len(self.traps.len()) + + self.traps.iter().map(Serializable::length).sum::() + + to_leb128_len(self.coordinate_encapsulations.len()) + + self + .coordinate_encapsulations + .iter() + .map(Serializable::length) + .sum::() + } + + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = ser.write_array(&self.tag)?; + n += ser.write_leb128_u64(self.traps.len() as u64)?; + for trap in &self.traps { + n += ser.write(trap)?; + } + n += ser.write_leb128_u64(self.coordinate_encapsulations.len() as u64)?; + for enc in &self.coordinate_encapsulations { + n += ser.write(enc)?; + } + Ok(n) + } + + fn read(de: &mut Deserializer) -> Result { + let tag = de.read_array::()?; + let n_traps = ::try_from(de.read_leb128_u64()?)?; + let mut traps = Vec::with_capacity(n_traps); + for _ in 0..n_traps { + let trap = de.read::()?; + traps.push(trap); + } + let n_encapsulations = ::try_from(de.read_leb128_u64()?)?; + let mut coordinate_encapsulations = HashSet::with_capacity(n_encapsulations); + for _ in 0..n_encapsulations { + let enc = de.read::()?; + coordinate_encapsulations.insert(enc); + } + Ok(Self { + tag, + traps, + coordinate_encapsulations, + }) + } +} + +impl Serializable for EncryptedHeader { + type Error = Error; + + fn length(&self) -> usize { + self.encapsulation.length() + + if let Some(metadata) = &self.encrypted_metadata { + to_leb128_len(to_leb128_len(metadata.len()) + metadata.len()) + } else { + 0 + } + } + + /// Tries to serialize the encrypted header. + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = self.encapsulation.write(ser)?; + match &self.encrypted_metadata { + Some(bytes) => n += ser.write_vec(bytes)?, + None => n += ser.write_vec(&[])?, + } + Ok(n) + } + + /// Tries to deserialize the encrypted header. + fn read(de: &mut Deserializer) -> Result { + let encapsulation = de.read::()?; + let ciphertext = de.read_vec()?; + let encrypted_metadata = if ciphertext.is_empty() { + None + } else { + Some(ciphertext) + }; + Ok(Self { + encapsulation, + encrypted_metadata, + }) + } +} + +impl Serializable for CleartextHeader { + type Error = Error; + + fn length(&self) -> usize { + SEED_LENGTH + + to_leb128_len( + self.metadata + .as_ref() + .map(std::vec::Vec::len) + .unwrap_or_default(), + ) + + self + .metadata + .as_ref() + .map(std::vec::Vec::len) + .unwrap_or_default() + } + + /// Tries to serialize the cleartext header. + fn write(&self, ser: &mut Serializer) -> Result { + let mut n = ser.write_array(self.symmetric_key.as_bytes())?; + match &self.metadata { + Some(bytes) => n += ser.write_vec(bytes)?, + None => n += ser.write_vec(&[])?, + } + Ok(n) + } + + /// Tries to deserialize the cleartext header. + fn read(de: &mut Deserializer) -> Result { + let symmetric_key = SymmetricKey::try_from_bytes(de.read_array::()?)?; + let metadata = de.read_vec()?; + let metadata = if metadata.is_empty() { + None + } else { + Some(metadata) + }; + Ok(Self { + symmetric_key, + metadata, + }) + } +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use cosmian_crypto_core::{reexport::rand_core::SeedableRng, CsRng}; + + use super::*; + use crate::{ + abe_policy::{AttributeStatus, EncryptionHint}, + core::{ + primitives::{encaps, mpk_keygen, setup, update_coordinate_keys, usk_keygen}, + MIN_TRACING_LEVEL, + }, + }; + + #[test] + fn test_coordinate_keypair() { + let mut rng = CsRng::from_entropy(); + let s = Scalar::new(&mut rng); + let h = EcPoint::from(&s); + + { + let ckp = CoordinateKeypair::random(&mut rng, &h, true); + let bytes = ckp.serialize().unwrap(); + assert_eq!(bytes.len(), ckp.length()); + let ckp_ = CoordinateKeypair::deserialize(&bytes).unwrap(); + assert_eq!(ckp, ckp_); + } + + { + let ckp = CoordinateKeypair::random(&mut rng, &h, false); + let bytes = ckp.serialize().unwrap(); + assert_eq!(bytes.len(), ckp.length()); + let ckp_ = CoordinateKeypair::deserialize(&bytes).unwrap(); + assert_eq!(ckp, ckp_); + } + } + + #[test] + fn test_coordinate_pk() { + let mut rng = CsRng::from_entropy(); + + { + let elgamal_pk = EcPoint::from(&Scalar::new(&mut rng)); + let cpk = CoordinatePublicKey::Classic { elgamal_pk }; + let bytes = cpk.serialize().unwrap(); + assert_eq!(bytes.len(), cpk.length()); + let cpk_ = CoordinatePublicKey::deserialize(&bytes).unwrap(); + assert_eq!(cpk, cpk_); + } + + { + let elgamal_pk = EcPoint::from(&Scalar::new(&mut rng)); + let postquantum_pk = postquantum::keygen(&mut rng).1; + let cpk = CoordinatePublicKey::Hybridized { + postquantum_pk, + elgamal_pk, + }; + + let bytes = cpk.serialize().unwrap(); + assert_eq!(bytes.len(), cpk.length()); + let cpk_ = CoordinatePublicKey::deserialize(&bytes).unwrap(); + assert_eq!(cpk, cpk_); + } + } + + #[test] + fn test_tracing_keys() { + let mut rng = CsRng::from_entropy(); + let mut tsk = TracingSecretKey::default(); + for _ in 0..MIN_TRACING_LEVEL + 2 { + tsk.increase_tracing(&mut rng); + } + + { + let bytes = tsk.serialize().unwrap(); + assert_eq!(bytes.len(), tsk.length()); + let tsk_ = TracingSecretKey::deserialize(&bytes).unwrap(); + assert_eq!(tsk, tsk_); + } + } + + #[test] + fn test_serialization() { + let mut rng = CsRng::from_entropy(); + let coordinate_1 = Coordinate::random(&mut rng); + let coordinate_2 = Coordinate::random(&mut rng); + let coordinate_3 = Coordinate::random(&mut rng); + + let universe = HashMap::from([ + ( + coordinate_1.clone(), + (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), + ), + ( + coordinate_2.clone(), + (EncryptionHint::Hybridized, AttributeStatus::EncryptDecrypt), + ), + ( + coordinate_3.clone(), + (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), + ), + ]); + + let user_set = HashSet::from([coordinate_1.clone(), coordinate_3.clone()]); + let target_set = HashSet::from([coordinate_1, coordinate_3]); + let mut rng = CsRng::from_entropy(); + + let mut msk = setup(&mut rng, MIN_TRACING_LEVEL + 2).unwrap(); + update_coordinate_keys(&mut rng, &mut msk, universe).unwrap(); + let mpk = mpk_keygen(&msk).unwrap(); + + // Check Covercrypt `MasterSecretKey` serialization. + { + let bytes = msk.serialize().unwrap(); + assert_eq!(bytes.len(), msk.length(), "Wrong master secret key length"); + let msk_ = MasterSecretKey::deserialize(&bytes).unwrap(); + assert_eq!(msk, msk_, "Wrong `MasterSecretKey` deserialization."); + } + + // Check Covercrypt `PublicKey` serialization. + { + let bytes = mpk.serialize().unwrap(); + assert_eq!(bytes.len(), mpk.length(), "Wrong master public key length"); + let mpk_ = MasterPublicKey::deserialize(&bytes).unwrap(); + assert_eq!(mpk, mpk_, "Wrong `PublicKey` derserialization."); + } + + // Check Covercrypt `UserSecretKey` serialization. + { + let usk = usk_keygen(&mut rng, &mut msk, user_set).unwrap(); + let bytes = usk.serialize().unwrap(); + assert_eq!(bytes.len(), usk.length(), "Wrong user secret key size"); + let usk_ = UserSecretKey::deserialize(&bytes).unwrap(); + assert_eq!(usk, usk_, "Wrong `UserSecretKey` deserialization."); + } + + // Check Covercrypt `Encapsulation` serialization. + { + let (_, encapsulation) = encaps(&mut rng, &mpk, &target_set).unwrap(); + let bytes = encapsulation.serialize().unwrap(); + assert_eq!( + bytes.len(), + encapsulation.length(), + "Wrong encapsulation size" + ); + let encapsulation_ = Encapsulation::deserialize(&bytes).unwrap(); + assert_eq!( + encapsulation, encapsulation_, + "Wrong `Encapsulation` serialization." + ); + } + + // // Setup Covercrypt. + // { + // use crate::{abe_policy::AccessPolicy, test_utils::policy, Covercrypt}; + + // let cc = Covercrypt::default(); + // let policy = policy()?; + // let user_policy = AccessPolicy::from_boolean_expression( + // "Department::MKG && Security Level::Top Secret", + // )?; + // let encryption_policy = AccessPolicy::from_boolean_expression( + // "Department::MKG && Security Level::High Secret", + // )?; + // let (msk, mpk) = cc.generate_master_keys(&policy)?; + // let usk = cc.generate_user_secret_key(&msk, &user_policy, &policy)?; + + // // Check `EncryptedHeader` serialization. + // let (_secret_key, encrypted_header) = + // EncryptedHeader::generate(&cc, &policy, &mpk, &encryption_policy, None, None)?; + // let bytes = encrypted_header.serialize()?; + // assert_eq!( + // bytes.len(), + // encrypted_header.length(), + // "Wrong encapsulation size." + // ); + // let encrypted_header_ = EncryptedHeader::deserialize(&bytes)?; + // assert_eq!( + // encrypted_header, encrypted_header_, + // "Wrong `EncryptedHeader` derserialization." + // ); + + // // Check `CleartextHeader` serialization. + // let cleartext_header = encrypted_header.decrypt(&cc, &usk, None)?; + // let bytes = cleartext_header.serialize()?; + // assert_eq!( + // bytes.len(), + // cleartext_header.length(), + // "Wrong cleartext header size." + // ); + // let cleartext_header_ = CleartextHeader::deserialize(&bytes)?; + // assert_eq!( + // cleartext_header, cleartext_header_, + // "Wrong `CleartextHeader` derserialization." + // ); + // } + } +} diff --git a/src/core/tests.rs b/src/core/tests.rs index 870f8c2c..4417623a 100644 --- a/src/core/tests.rs +++ b/src/core/tests.rs @@ -3,13 +3,8 @@ use std::collections::{HashMap, HashSet}; use cosmian_crypto_core::{reexport::rand_core::SeedableRng, CsRng}; use crate::{ - abe_policy::{ - AccessPolicy, Attribute, AttributeStatus, Coordinate, DimensionBuilder, EncryptionHint, - Policy, - }, - core::primitives::{decaps, encaps, mpk_keygen, update_coordinate_keys}, - test_utils::policy, - Covercrypt, Error, + abe_policy::{AttributeStatus, Coordinate, EncryptionHint}, + core::primitives::{decaps, encaps, mpk_keygen, refresh, rekey, update_coordinate_keys}, }; use super::{ @@ -18,39 +13,64 @@ use super::{ }; /// This test asserts that it is possible to encapsulate a key for a given -/// coordinate and that several users which key is associated with this +/// coordinate and that different users which key is associated with this /// coordinate can open the resulting encapsulation. #[test] -fn test_broadcast() { +fn test_encapsulation() { let mut rng = CsRng::from_entropy(); - let mut msk = setup(&mut rng, MIN_TRACING_LEVEL).unwrap(); - - let coordinate = Coordinate::random(&mut rng); + let other_coordinate = Coordinate::random(&mut rng); + let target_coordinate = Coordinate::random(&mut rng); + let mut msk = setup(&mut rng, MIN_TRACING_LEVEL).unwrap(); update_coordinate_keys( &mut rng, &mut msk, - HashMap::from_iter([( - coordinate.clone(), - (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), - )]), + HashMap::from_iter([ + ( + other_coordinate.clone(), + (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), + ), + ( + target_coordinate.clone(), + (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), + ), + ]), ) .unwrap(); - let mpk = mpk_keygen(&msk).unwrap(); - let (key, enc) = encaps(&mut rng, &mpk, &HashSet::from_iter([coordinate.clone()])).unwrap(); + let (key, enc) = encaps( + &mut rng, + &mpk, + &HashSet::from_iter([target_coordinate.clone()]), + ) + .unwrap(); assert_eq!(enc.coordinate_encapsulations.len(), 1); for _ in 0..3 { - let usk = usk_keygen(&mut rng, &mut msk, HashSet::from_iter([coordinate.clone()])).unwrap(); + let usk = usk_keygen( + &mut rng, + &mut msk, + HashSet::from_iter([target_coordinate.clone()]), + ) + .unwrap(); assert_eq!(usk.coordinate_keys.len(), 1); assert_eq!(Some(&key), decaps(&usk, &enc).unwrap().as_ref()); } + + let usk = usk_keygen( + &mut rng, + &mut msk, + HashSet::from_iter([other_coordinate.clone()]), + ) + .unwrap(); + assert_eq!(usk.coordinate_keys.len(), 1); + assert_eq!(None, decaps(&usk, &enc).unwrap().as_ref()); } /// This test verifies that the correct number of keys is added/removed upon -/// updating the MSK. +/// updating the MSK. It also check that the correct number of coordinate keys +/// are given to the MPK, and removed upon deprecation. #[test] fn test_update() { let mut rng = CsRng::from_entropy(); @@ -105,97 +125,123 @@ fn test_update() { assert_eq!(mpk.coordinate_keys.len(), 5); } +/// This test asserts that re-keyed coordinates allow creating encapsulations +/// using the new keys: old USK cannot open the new ones and new USK cannot open +/// the old ones. #[test] -fn test_master_rekey() -> Result<(), Error> { - let d1 = DimensionBuilder::new( - "D1", - vec![ - ("A", EncryptionHint::Classic), - ("B", EncryptionHint::Classic), - ], - false, - ); - let d2 = DimensionBuilder::new( - "D2", - vec![ - ("A", EncryptionHint::Classic), - ("B", EncryptionHint::Classic), - ], - false, - ); - let mut policy = Policy::new(); - policy.add_dimension(d1)?; - policy.add_dimension(d2)?; - - let cover_crypt = Covercrypt::default(); - let (mut msk, _) = cover_crypt.setup()?; - let _ = cover_crypt.update_master_keys(&policy, &mut msk); - - // There is one key per coordinate. - let mut n_keys = (2 + 1) * (2 + 1); - assert_eq!(msk.coordinate_keypairs.count_elements(), n_keys); - - let rekey_access_policy = AccessPolicy::Attr(Attribute::new("D1", "A")); - cover_crypt.rekey(&rekey_access_policy, &policy, &mut msk)?; - n_keys += 2; - assert_eq!(msk.coordinate_keypairs.count_elements(), n_keys); - - let rekey_access_policy = AccessPolicy::Attr(Attribute::new("D1", "B")); - cover_crypt.rekey(&rekey_access_policy, &policy, &mut msk)?; - n_keys += 2; - assert_eq!(msk.coordinate_keypairs.count_elements(), n_keys); - - let rekey_access_policy = AccessPolicy::Attr(Attribute::new("D2", "A")); - cover_crypt.rekey(&rekey_access_policy, &policy, &mut msk)?; - n_keys += 2; - assert_eq!(msk.coordinate_keypairs.count_elements(), n_keys); - - Ok(()) -} - -#[test] -fn test_refresh_user_key() -> Result<(), Error> { - let policy = policy()?; - let cover_crypt = Covercrypt::default(); - let (mut msk, _) = cover_crypt.setup()?; - let _ = cover_crypt.update_master_keys(&policy, &mut msk); +fn test_rekey() { + let mut rng = CsRng::from_entropy(); + let coordinate_1 = Coordinate::random(&mut rng); + let coordinate_2 = Coordinate::random(&mut rng); + let subspace_1 = HashSet::from_iter([coordinate_1.clone()]); + let subspace_2 = HashSet::from_iter([coordinate_2.clone()]); + let universe = HashSet::from_iter([coordinate_1.clone(), coordinate_2.clone()]); - let ap = AccessPolicy::parse("Department::MKG && Security Level::High Secret")?; - let mut usk = cover_crypt.generate_user_secret_key(&mut msk, &ap, &policy)?; - let original_usk = usk.clone(); + let mut msk = setup(&mut rng, MIN_TRACING_LEVEL).unwrap(); + update_coordinate_keys( + &mut rng, + &mut msk, + HashMap::from_iter([ + ( + coordinate_1.clone(), + (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), + ), + ( + coordinate_2.clone(), + (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), + ), + ]), + ) + .unwrap(); + let mpk = mpk_keygen(&msk).unwrap(); + let mut usk_1 = usk_keygen(&mut rng, &mut msk, subspace_1.clone()).unwrap(); + let mut usk_2 = usk_keygen(&mut rng, &mut msk, subspace_2.clone()).unwrap(); - // Re-key the access policy associated to the user key. - cover_crypt.rekey(&ap, &policy, &mut msk)?; + let (old_key_1, old_enc_1) = encaps(&mut rng, &mpk, &subspace_1).unwrap(); + let (old_key_2, old_enc_2) = encaps(&mut rng, &mpk, &subspace_2).unwrap(); - cover_crypt.refresh_usk(&mut usk, &mut msk, true)?; + // Old USK can open encapsulations associated with their coordinate. assert_eq!( - usk.coordinate_keys.count_elements(), - 2 * original_usk.coordinate_keys.count_elements() + Some(&old_key_1), + decaps(&usk_1, &old_enc_1).unwrap().as_ref() ); - for x_i in original_usk.coordinate_keys.flat_iter() { - assert!(usk.coordinate_keys.flat_iter().any(|x| x == x_i)); + assert_eq!(None, decaps(&usk_1, &old_enc_2).unwrap()); + assert_eq!(Some(old_key_2), decaps(&usk_2, &old_enc_2).unwrap()); + assert_eq!(None, decaps(&usk_2, &old_enc_1).unwrap()); + + // Re-key all space coordinates. + rekey(&mut rng, &mut msk, universe).unwrap(); + let mpk = mpk_keygen(&msk).unwrap(); + + let (new_key_1, new_enc_1) = encaps(&mut rng, &mpk, &subspace_1).unwrap(); + let (new_key_2, new_enc_2) = encaps(&mut rng, &mpk, &subspace_2).unwrap(); + + // Old USK cannot open new encapsulations. + assert_eq!(None, decaps(&usk_1, &new_enc_1).unwrap()); + assert_eq!(None, decaps(&usk_1, &new_enc_2).unwrap()); + assert_eq!(None, decaps(&usk_2, &new_enc_2).unwrap()); + assert_eq!(None, decaps(&usk_2, &new_enc_1).unwrap()); + + // Refresh USK. + // Only the first one keeps its old rights. + refresh(&mut rng, &mut msk, &mut usk_1, true).unwrap(); + refresh(&mut rng, &mut msk, &mut usk_2, false).unwrap(); + + // Refreshed USK can open the new encapsulation. + assert_eq!(Some(new_key_1), decaps(&usk_1, &new_enc_1).unwrap()); + assert_eq!(None, decaps(&usk_1, &new_enc_2).unwrap()); + assert_eq!(Some(new_key_2), decaps(&usk_2, &new_enc_2).unwrap()); + assert_eq!(None, decaps(&usk_2, &new_enc_1).unwrap()); + + // Only USK 1 can still open the old encapsulation. + assert_eq!(Some(old_key_1), decaps(&usk_1, &old_enc_1).unwrap()); + assert_eq!(None, decaps(&usk_1, &old_enc_2).unwrap()); + assert_eq!(None, decaps(&usk_2, &old_enc_2).unwrap()); + assert_eq!(None, decaps(&usk_2, &old_enc_1).unwrap()); +} + +/// This test asserts that forged USK cannot be refreshed. +#[test] +fn test_integrity_check() { + let mut rng = CsRng::from_entropy(); + let coordinate_1 = Coordinate::random(&mut rng); + let coordinate_2 = Coordinate::random(&mut rng); + let subspace_1 = HashSet::from_iter([coordinate_1.clone()]); + let subspace_2 = HashSet::from_iter([coordinate_2.clone()]); + + let mut msk = setup(&mut rng, MIN_TRACING_LEVEL).unwrap(); + update_coordinate_keys( + &mut rng, + &mut msk, + HashMap::from_iter([ + ( + coordinate_1.clone(), + (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), + ), + ( + coordinate_2.clone(), + (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), + ), + ]), + ) + .unwrap(); + let usk_1 = usk_keygen(&mut rng, &mut msk, subspace_1.clone()).unwrap(); + let usk_2 = usk_keygen(&mut rng, &mut msk, subspace_2.clone()).unwrap(); + + // Here we are trying to get access to both USK1 and USK2 rights. + let mut old_forged_usk = usk_1.clone(); + for (key, chain) in usk_2.coordinate_keys.iter() { + old_forged_usk + .coordinate_keys + .insert_new_chain(key.clone(), chain.clone()); } - // refresh the user key but do NOT preserve access to old partitions - cover_crypt.refresh_usk(&mut usk, &mut msk, false)?; - // the user should still have access to the same number of partitions assert_eq!( - usk.coordinate_keys.count_elements(), - original_usk.coordinate_keys.count_elements() - ); - for x_i in original_usk.coordinate_keys.flat_iter() { - assert!(!usk.coordinate_keys.flat_iter().any(|x| x == x_i)); - } - - // try to modify the user key and refresh - let part = Coordinate::from(vec![1, 6]); - usk.coordinate_keys.create_chain_with_single_value( - part.clone(), - msk.coordinate_keypairs - .get_latest(&part) - .unwrap() - .secret_key(), + old_forged_usk.coordinate_keys.count_elements(), + usk_1.coordinate_keys.count_elements() + usk_2.coordinate_keys.count_elements() ); - assert!(cover_crypt.refresh_usk(&mut usk, &mut msk, false).is_err()); - Ok(()) + // The forged key refresh is rejected: no modification is performed on it. + let mut new_forged_usk = old_forged_usk.clone(); + assert!(refresh(&mut rng, &mut msk, &mut new_forged_usk, true).is_err()); + assert_eq!(new_forged_usk, old_forged_usk); } diff --git a/src/core/tests.rs~ b/src/core/tests.rs~ deleted file mode 100644 index 3def70ff..00000000 --- a/src/core/tests.rs~ +++ /dev/null @@ -1,116 +0,0 @@ -#[test] -fn test_update_master_keys() -> Result<(), Error> { - let policy = policy()?; - let cover_crypt = Covercrypt::default(); - let (mut msk, _) = cover_crypt.setup()?; - let mpk = cover_crypt.update_master_keys(&policy, &mut msk)?; - - // same number of subkeys in public and secret key - assert_eq!(mpk.subkeys.len(), 30); - assert_eq!(msk.subkeys.count_elements(), 30); - - // rekey all partitions which include `Department::FIN` - let rekey_access_policy = AccessPolicy::Attr(Attribute::new("Department", "FIN")); - cover_crypt.rekey_master_keys(&rekey_access_policy, &policy, &mut msk, &mut mpk)?; - // public key contains only the last subkeys - assert_eq!(mpk.subkeys.len(), 30); - // secret key stores the 2 old subkeys - assert_eq!(msk.subkeys.count_elements(), 32); - - // remove older subkeys for `Department::FIN` - cover_crypt.prune_master_secret_key(&rekey_access_policy, &policy, &mut msk)?; - // we only keep the last subkeys in the secret key - assert_eq!(msk.subkeys.count_elements(), 30); - - Ok(()) -} - -#[test] -fn test_master_rekey() -> Result<(), Error> { - let d1 = DimensionBuilder::new( - "D1", - vec![ - ("A", EncryptionHint::Classic), - ("B", EncryptionHint::Classic), - ], - false, - ); - let d2 = DimensionBuilder::new( - "D2", - vec![ - ("A", EncryptionHint::Classic), - ("B", EncryptionHint::Classic), - ], - false, - ); - let mut policy = Policy::new(); - policy.add_dimension(d1)?; - policy.add_dimension(d2)?; - - let cover_crypt = Covercrypt::default(); - let (mut msk, mut mpk) = cover_crypt.generate_master_keys(&policy)?; - - // There is one key per coordinate. - let mut n_keys = (2 + 1) * (2 + 1); - assert_eq!(msk.subkeys.count_elements(), n_keys); - - let rekey_access_policy = AccessPolicy::Attr(Attribute::new("D1", "A")); - cover_crypt.rekey_master_keys(&rekey_access_policy, &policy, &mut msk, &mut mpk)?; - n_keys += 2; - assert_eq!(msk.subkeys.count_elements(), n_keys); - - let rekey_access_policy = AccessPolicy::Attr(Attribute::new("D1", "B")); - cover_crypt.rekey_master_keys(&rekey_access_policy, &policy, &mut msk, &mut mpk)?; - n_keys += 2; - assert_eq!(msk.subkeys.count_elements(), n_keys); - - let rekey_access_policy = AccessPolicy::Attr(Attribute::new("D2", "A")); - cover_crypt.rekey_master_keys(&rekey_access_policy, &policy, &mut msk, &mut mpk)?; - n_keys += 2; - assert_eq!(msk.subkeys.count_elements(), n_keys); - - Ok(()) -} - -#[test] -fn test_refresh_user_key() -> Result<(), Error> { - let policy = policy()?; - let cover_crypt = Covercrypt::default(); - let (mut msk, mut mpk) = cover_crypt.generate_master_keys(&policy)?; - - let ap = AccessPolicy::parse("Department::MKG && Security Level::High Secret")?; - let mut usk = cover_crypt.generate_user_secret_key(&msk, &ap, &policy)?; - let original_usk = UserSecretKey::deserialize(usk.serialize()?.as_slice())?; - - // Re-key the access policy associated to the user key. - cover_crypt.rekey_master_keys(&ap, &policy, &mut msk, &mut mpk)?; - - cover_crypt.refresh_usk(&mut usk, &msk, true)?; - assert_eq!( - usk.subkeys.count_elements(), - 2 * original_usk.subkeys.count_elements() - ); - for x_i in original_usk.subkeys.flat_iter() { - assert!(usk.subkeys.flat_iter().any(|x| x == x_i)); - } - // refresh the user key but do NOT preserve access to old partitions - cover_crypt.refresh_usk(&mut usk, &msk, false)?; - // the user should still have access to the same number of partitions - assert_eq!( - usk.subkeys.count_elements(), - original_usk.subkeys.count_elements() - ); - for x_i in original_usk.subkeys.flat_iter() { - assert!(!usk.subkeys.flat_iter().any(|x| x == x_i)); - } - - // try to modify the user key and refresh - let part = Coordinate::from(vec![1, 6]); - usk.subkeys.create_chain_with_single_value( - part.clone(), - msk.subkeys.get_latest(&part).unwrap().clone(), - ); - assert!(cover_crypt.refresh_usk(&mut usk, &msk, false).is_err()); - - Ok(()) -} diff --git a/src/data_struct/revision_map.rs b/src/data_struct/revision_map.rs index 099cb3e7..2c50b671 100644 --- a/src/data_struct/revision_map.rs +++ b/src/data_struct/revision_map.rs @@ -132,8 +132,8 @@ where } /// Iterates through all key/value couples in arbitrary order. - pub fn iter(&self) -> impl Iterator)> { - self.map.iter().map(|(k, v)| (k, v.iter())) + pub fn iter(&self) -> impl Iterator)> { + self.map.iter() } /// Iterates through all revisions of a given key starting with the more diff --git a/src/lib.rs b/src/lib.rs index 1ddabb35..269986ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,9 +29,7 @@ mod error; pub mod abe_policy; pub mod core; pub mod data_struct; -#[cfg(any(test, feature = "test_utils"))] pub mod test_utils; - pub use error::Error; pub use self::core::{ diff --git a/src/test_utils/mod.rs b/src/test_utils/mod.rs index e07b26af..ec00b4a3 100644 --- a/src/test_utils/mod.rs +++ b/src/test_utils/mod.rs @@ -3,8 +3,9 @@ use crate::{ Error, }; -// // pub mod non_regression; +// pub mod non_regression; +/// Creates the test policy. pub fn policy() -> Result { let sec_level = DimensionBuilder::new( "Security Level", @@ -33,372 +34,319 @@ pub fn policy() -> Result { Ok(policy) } -// #[cfg(test)] -// mod tests { -// use cosmian_crypto_core::bytes_ser_de::Serializable; - -// use super::*; -// use crate::{ -// abe_policy::{AccessPolicy, Attribute, Coordinate, LegacyPolicy}, -// Covercrypt, EncryptedHeader, -// }; - -// use crate::UserSecretKey; - -// #[test] -// fn write_policy() { -// let _policy = policy().unwrap(); -// std::fs::write("target/policy.json", serde_json::to_vec(&_policy).unwrap()).unwrap(); -// } - -// /// Read policy from a file. Assert `LegacyPolicy` is convertible into a -// /// `Policy`. -// #[test] -// fn read_policy() { -// // Can read a `Policy` V2 -// let policy_v2_str = include_bytes!("./tests_data/policy_v2.json"); -// Policy::try_from(policy_v2_str.as_slice()).unwrap(); - -// // Can read a `Policy` V1 -// let policy_v1_str = include_bytes!("./tests_data/policy_v1.json"); -// Policy::try_from(policy_v1_str.as_slice()).unwrap(); - -// // Can read a `LegacyPolicy` -// let legacy_policy_str = include_bytes!("./tests_data/legacy_policy.json"); -// serde_json::from_slice::(legacy_policy_str).unwrap(); - -// // Can read `LegacyPolicy` as `Policy` -// Policy::try_from(legacy_policy_str.as_slice()).unwrap(); -// } - -// #[test] -// fn test_add_attribute() -> Result<(), Error> { -// let mut policy = policy()?; -// let cover_crypt = Covercrypt::default(); -// let (mut msk, mut mpk) = cover_crypt.generate_master_keys(&policy)?; - -// let partitions_msk: Vec = msk.subkeys.keys().cloned().collect(); -// let partitions_mpk: Vec = mpk.subkeys.keys().cloned().collect(); -// assert_eq!(partitions_msk.len(), partitions_mpk.len()); -// for p in &partitions_msk { -// assert!(partitions_mpk.contains(p)); -// } - -// // -// // User secret key -// let decryption_policy = AccessPolicy::parse("Security Level::Low Secret")?; -// let mut low_secret_usk = -// cover_crypt.generate_user_secret_key(&msk, &decryption_policy, &policy)?; - -// // add sales department -// policy.add_attribute( -// Attribute::new("Department", "Sales"), -// EncryptionHint::Classic, -// )?; -// // update the master keys -// cover_crypt.update_master_keys(&policy, &mut msk, &mut mpk)?; -// let new_partitions_msk: Vec = msk.subkeys.keys().cloned().collect(); -// let new_partitions_mpk: Vec = mpk.subkeys.keys().cloned().collect(); -// assert_eq!(new_partitions_msk.len(), new_partitions_mpk.len()); -// for p in &new_partitions_msk { -// assert!(new_partitions_mpk.contains(p)); -// } -// assert_eq!(new_partitions_msk.len(), partitions_msk.len() + 6); - -// // -// // Encrypt -// let secret_sales_ap = -// AccessPolicy::parse("Security Level::Low Secret && Department::Sales")?; -// let (_, encrypted_header) = -// EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &secret_sales_ap, None, None)?; - -// // User cannot decrypt new message without refreshing its key -// assert!(encrypted_header -// .decrypt(&cover_crypt, &low_secret_usk, None) -// .is_err()); - -// cover_crypt.refresh_usk(&mut low_secret_usk, &msk, false)?; - -// // TODO: fix this behavior? -// assert!(encrypted_header -// .decrypt(&cover_crypt, &low_secret_usk, None) -// .is_err()); - -// Ok(()) -// } - -// #[test] -// fn test_delete_attribute() -> Result<(), Error> { -// let mut policy = policy()?; -// let cover_crypt = Covercrypt::default(); -// let (mut msk, mut mpk) = cover_crypt.generate_master_keys(&policy)?; - -// let partitions_msk: Vec = msk.subkeys.keys().cloned().collect(); -// let partitions_mpk: Vec = mpk.subkeys.keys().cloned().collect(); -// assert_eq!(partitions_msk.len(), partitions_mpk.len()); -// for p in &partitions_msk { -// assert!(partitions_mpk.contains(p)); -// } - -// // -// // New user secret key -// let decryption_policy = AccessPolicy::parse( -// "Security Level::Top Secret && (Department::FIN || Department::HR)", -// )?; -// let mut top_secret_fin_usk = -// cover_crypt.generate_user_secret_key(&msk, &decryption_policy, &policy)?; - -// // -// // Encrypt -// let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; -// let (_, encrypted_header) = -// EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None)?; - -// // remove the FIN department -// policy.remove_attribute(&Attribute::new("Department", "FIN"))?; - -// // update the master keys -// cover_crypt.update_master_keys(&policy, &mut msk, &mut mpk)?; -// let new_partitions_msk: Vec = msk.subkeys.keys().cloned().collect(); -// let new_partitions_mpk: Vec = mpk.subkeys.keys().cloned().collect(); -// assert_eq!(new_partitions_msk.len(), new_partitions_mpk.len()); -// for p in &new_partitions_msk { -// assert!(new_partitions_mpk.contains(p)); -// } -// // 5 is the size of the security level dimension -// assert_eq!(new_partitions_msk.len(), partitions_msk.len() - 6); - -// assert!(encrypted_header -// .decrypt(&cover_crypt, &top_secret_fin_usk, None) -// .is_ok()); - -// // refresh the user key and preserve access to old partitions -// let _new_decryption_policy = -// AccessPolicy::parse("Security Level::Top Secret && Department::HR")?; - -// // refreshing the user key will remove access to removed partitions even if we -// // keep old rotations -// cover_crypt.refresh_usk(&mut top_secret_fin_usk, &msk, true)?; -// assert!(encrypted_header -// .decrypt(&cover_crypt, &top_secret_fin_usk, None) -// .is_err()); - -// Ok(()) -// } - -// #[test] -// fn test_deactivate_attribute() -> Result<(), Error> { -// let mut policy = policy()?; -// let cover_crypt = Covercrypt::default(); -// let (mut msk, mut mpk) = cover_crypt.generate_master_keys(&policy)?; - -// let partitions_msk: Vec = msk.subkeys.keys().cloned().collect(); -// let partitions_mpk: Vec = mpk.subkeys.keys().cloned().collect(); -// assert_eq!(partitions_msk.len(), partitions_mpk.len()); - -// // -// // New user secret key -// let decryption_policy = AccessPolicy::parse( -// "Security Level::Top Secret && (Department::FIN || Department::HR)", -// )?; -// let mut top_secret_fin_usk = -// cover_crypt.generate_user_secret_key(&msk, &decryption_policy, &policy)?; - -// // -// // Encrypt -// let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; -// let (_, encrypted_header) = -// EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None)?; - -// // remove the FIN department -// policy.disable_attribute(&Attribute::new("Department", "FIN"))?; - -// // update the master keys -// cover_crypt.update_master_keys(&policy, &mut msk, &mut mpk)?; -// let new_partitions_msk: Vec = msk.subkeys.keys().cloned().collect(); -// let new_partitions_mpk: Vec = mpk.subkeys.keys().cloned().collect(); -// // the disabled partition have been removed from mpk -// assert_eq!(new_partitions_msk.len() - 6, new_partitions_mpk.len()); -// // msk has not changed -// assert_eq!(new_partitions_msk.len(), partitions_msk.len()); - -// assert!(encrypted_header -// .decrypt(&cover_crypt, &top_secret_fin_usk, None) -// .is_ok()); - -// // Can not encrypt using deactivated attribute -// let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; - -// assert!( -// EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None) -// .is_err() -// ); - -// // refresh the user key and preserve access to old partitions -// cover_crypt.refresh_usk(&mut top_secret_fin_usk, &msk, true)?; -// assert!(encrypted_header -// .decrypt(&cover_crypt, &top_secret_fin_usk, None) -// .is_ok()); - -// // refresh the user key and remove access to old partitions should still work -// cover_crypt.refresh_usk(&mut top_secret_fin_usk, &msk, false)?; -// assert!(encrypted_header -// .decrypt(&cover_crypt, &top_secret_fin_usk, None) -// .is_ok()); - -// // -// // Rotating the disabled attribute should only change the msk -// let rekey_ap = AccessPolicy::Attr(Attribute::new("Department", "FIN")); -// cover_crypt.rekey_master_keys(&rekey_ap, &policy, &mut msk, &mut mpk)?; -// assert_eq!(msk.subkeys.count_elements() - 8, mpk.subkeys.len()); - -// Ok(()) -// } - -// #[test] -// fn test_rename_attribute() -> Result<(), Error> { -// let mut policy = policy()?; -// let cover_crypt = Covercrypt::default(); -// let (mut msk, mut mpk) = cover_crypt.generate_master_keys(&policy)?; - -// // -// // New user secret key -// let decryption_policy = -// AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; -// let mut top_secret_fin_usk = -// cover_crypt.generate_user_secret_key(&msk, &decryption_policy, &policy)?; - -// // -// // Encrypt -// let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; -// let (_, encrypted_header) = -// EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None)?; - -// // remove the FIN department -// policy.rename_attribute(&Attribute::new("Department", "FIN"), "Finance".to_string())?; - -// // update the master keys -// cover_crypt.update_master_keys(&policy, &mut msk, &mut mpk)?; - -// assert!(encrypted_header -// .decrypt(&cover_crypt, &top_secret_fin_usk, None) -// .is_ok()); - -// // refresh the user key and preserve access to old partitions -// let _new_decryption_policy = -// AccessPolicy::parse("Security Level::Top Secret && Department::Finance")?; -// cover_crypt.refresh_usk(&mut top_secret_fin_usk, &msk, false)?; -// assert!(encrypted_header -// .decrypt(&cover_crypt, &top_secret_fin_usk, None) -// .is_ok()); - -// Ok(()) -// } - -// #[test] -// fn encrypt_decrypt_sym_key() -> Result<(), Error> { -// let policy = policy()?; -// let access_policy = AccessPolicy::parse( -// "(Department::MKG || Department::FIN) && Security Level::Top Secret", -// ) -// .unwrap(); -// let cover_crypt = Covercrypt::default(); -// let (msk, mpk) = cover_crypt.generate_master_keys(&policy)?; -// let (sym_key, encrypted_key) = cover_crypt.encaps( -// &policy, -// &mpk, -// AccessPolicy::parse("Department::MKG && Security Level::Top Secret")?, -// )?; -// let usk = cover_crypt.generate_user_secret_key(&msk, &access_policy, &policy)?; -// let recovered_key = cover_crypt.decaps(&usk, &encrypted_key)?; -// assert_eq!(sym_key, recovered_key, "Wrong decryption of the key!"); -// Ok(()) -// } - -// #[test] -// fn test_single_attribute_in_access_policy() -> Result<(), Error> { -// // -// // Declare policy -// let policy = policy()?; - -// // -// // Setup Covercrypt -// let cover_crypt = Covercrypt::default(); -// let (msk, _master_public_key) = cover_crypt.generate_master_keys(&policy)?; - -// // -// // New user secret key -// let _user_key = cover_crypt.generate_user_secret_key( -// &msk, -// &AccessPolicy::parse("Security Level::Top Secret")?, -// &policy, -// )?; - -// Ok(()) -// } - -// #[test] -// fn test_rotate_then_encrypt() -> Result<(), Error> { -// // -// // Declare policy -// let policy = policy()?; -// let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret")?; - -// // -// // Setup Covercrypt -// let cover_crypt = Covercrypt::default(); -// let (mut msk, mut master_public_key) = cover_crypt.generate_master_keys(&policy)?; - -// // -// // New user secret key -// let mut top_secret_fin_usk = cover_crypt.generate_user_secret_key( -// &msk, -// &AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?, -// &policy, -// )?; - -// // -// // Encrypt -// let (_, encrypted_header) = EncryptedHeader::generate( -// &cover_crypt, -// &policy, -// &master_public_key, -// &top_secret_ap, -// None, -// None, -// )?; - -// let _plaintext_header = -// encrypted_header.decrypt(&cover_crypt, &top_secret_fin_usk, None)?; - -// // -// // Rotate argument (must update master keys) -// let rekey_ap = AccessPolicy::Attr(Attribute::from(("Security Level", "Top Secret"))); -// cover_crypt.rekey_master_keys(&rekey_ap, &policy, &mut msk, &mut master_public_key)?; - -// // -// // Encrypt with new attribute -// let (_, encrypted_header) = EncryptedHeader::generate( -// &cover_crypt, -// &policy, -// &master_public_key, -// &top_secret_ap, -// None, -// None, -// )?; - -// // Decryption fails without refreshing the user key -// assert!(encrypted_header -// .decrypt(&cover_crypt, &top_secret_fin_usk, None) -// .is_err()); - -// cover_crypt.refresh_usk(&mut top_secret_fin_usk, &msk, false)?; - -// // The refreshed key can decrypt the header -// assert!(encrypted_header -// .decrypt(&cover_crypt, &top_secret_fin_usk, None) -// .is_ok()); - -// Ok(()) -// } -// } +#[cfg(test)] +mod tests { + + use super::*; + use crate::{ + abe_policy::{AccessPolicy, Attribute, LegacyPolicy}, + Covercrypt, EncryptedHeader, + }; + + #[test] + fn write_policy() { + let _policy = policy().unwrap(); + std::fs::write("target/policy.json", serde_json::to_vec(&_policy).unwrap()).unwrap(); + } + + /// Read policy from a file. Assert `LegacyPolicy` is convertible into a + /// `Policy`. + #[test] + fn read_policy() { + // Can read a `Policy` V2 + let policy_v2_str = include_bytes!("./tests_data/policy_v2.json"); + Policy::try_from(policy_v2_str.as_slice()).unwrap(); + + // Can read a `Policy` V1 + let policy_v1_str = include_bytes!("./tests_data/policy_v1.json"); + Policy::try_from(policy_v1_str.as_slice()).unwrap(); + + // Can read a `LegacyPolicy` + let legacy_policy_str = include_bytes!("./tests_data/legacy_policy.json"); + serde_json::from_slice::(legacy_policy_str).unwrap(); + + // Can read `LegacyPolicy` as `Policy` + Policy::try_from(legacy_policy_str.as_slice()).unwrap(); + } + + #[test] + fn test_add_attribute() -> Result<(), Error> { + let mut policy = policy()?; + let cover_crypt = Covercrypt::default(); + let (mut msk, _) = cover_crypt.setup()?; + let _ = cover_crypt.update_master_keys(&policy, &mut msk)?; + + let decryption_policy = AccessPolicy::parse("Security Level::Low Secret")?; + let mut low_secret_usk = + cover_crypt.generate_user_secret_key(&mut msk, &decryption_policy, &policy)?; + + policy.add_attribute( + Attribute::new("Department", "Sales"), + EncryptionHint::Classic, + )?; + let mpk = cover_crypt.update_master_keys(&policy, &mut msk)?; + + let secret_sales_ap = + AccessPolicy::parse("Security Level::Low Secret && Department::Sales")?; + let (_, encrypted_header) = + EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &secret_sales_ap, None, None)?; + + // User cannot decrypt new message without refreshing its key + assert!(encrypted_header + .decrypt(&cover_crypt, &low_secret_usk, None) + .unwrap() + .is_none()); + + cover_crypt.refresh_usk(&mut low_secret_usk, &mut msk, false)?; + + assert!(encrypted_header + .decrypt(&cover_crypt, &low_secret_usk, None) + .unwrap() + .is_none()); + + Ok(()) + } + + #[test] + fn test_delete_attribute() -> Result<(), Error> { + let mut policy = policy()?; + let cover_crypt = Covercrypt::default(); + let (mut msk, _) = cover_crypt.setup()?; + let mpk = cover_crypt.update_master_keys(&policy, &mut msk)?; + + // New user secret key + let decryption_policy = AccessPolicy::parse( + "Security Level::Top Secret && (Department::FIN || Department::HR)", + )?; + let mut top_secret_fin_usk = + cover_crypt.generate_user_secret_key(&mut msk, &decryption_policy, &policy)?; + + // Encrypt + let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; + let (_, encrypted_header) = + EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None)?; + + // remove the FIN department + policy.remove_attribute(&Attribute::new("Department", "FIN"))?; + + // update the master keys + let _ = cover_crypt.update_master_keys(&policy, &mut msk)?; + + assert!(encrypted_header + .decrypt(&cover_crypt, &top_secret_fin_usk, None) + .unwrap() + .is_some()); + + // refresh the user key and preserve access to old partitions + let _new_decryption_policy = + AccessPolicy::parse("Security Level::Top Secret && Department::HR")?; + + // refreshing the user key will remove access to removed partitions even if we + // keep old rotations + cover_crypt.refresh_usk(&mut top_secret_fin_usk, &mut msk, true)?; + assert!(encrypted_header + .decrypt(&cover_crypt, &top_secret_fin_usk, None) + .unwrap() + .is_none()); + + Ok(()) + } + + #[test] + fn test_deactivate_attribute() -> Result<(), Error> { + let mut policy = policy()?; + let cover_crypt = Covercrypt::default(); + let (mut msk, _) = cover_crypt.setup()?; + let mpk = cover_crypt.update_master_keys(&policy, &mut msk)?; + + // + // New user secret key + let decryption_policy = AccessPolicy::parse( + "Security Level::Top Secret && (Department::FIN || Department::HR)", + )?; + let mut top_secret_fin_usk = + cover_crypt.generate_user_secret_key(&mut msk, &decryption_policy, &policy)?; + + // + // Encrypt + let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; + let (_, encrypted_header) = + EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None)?; + + // remove the FIN department + policy.disable_attribute(&Attribute::new("Department", "FIN"))?; + + // update the master keys + let mpk = cover_crypt.update_master_keys(&policy, &mut msk)?; + + assert!(encrypted_header + .decrypt(&cover_crypt, &top_secret_fin_usk, None) + .unwrap() + .is_some()); + + // Can not encrypt using deactivated attribute + let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; + + assert!( + EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None) + .is_err() + ); + + // refresh the user key and preserve access to old partitions + cover_crypt.refresh_usk(&mut top_secret_fin_usk, &mut msk, true)?; + assert!(encrypted_header + .decrypt(&cover_crypt, &top_secret_fin_usk, None) + .unwrap() + .is_some()); + + // refresh the user key and remove access to old partitions should still work + cover_crypt.refresh_usk(&mut top_secret_fin_usk, &mut msk, false)?; + assert!(encrypted_header + .decrypt(&cover_crypt, &top_secret_fin_usk, None) + .unwrap() + .is_some()); + + Ok(()) + } + + #[test] + fn test_rename_attribute() -> Result<(), Error> { + let mut policy = policy()?; + let cover_crypt = Covercrypt::default(); + let (mut msk, _) = cover_crypt.setup()?; + let mpk = cover_crypt.update_master_keys(&policy, &mut msk)?; + + // New user secret key + let decryption_policy = + AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; + let mut top_secret_fin_usk = + cover_crypt.generate_user_secret_key(&mut msk, &decryption_policy, &policy)?; + + // Encrypt + let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?; + let (_, encrypted_header) = + EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None)?; + + // remove the FIN department + policy.rename_attribute(&Attribute::new("Department", "FIN"), "Finance".to_string())?; + + // update the master keys + let _ = cover_crypt.update_master_keys(&policy, &mut msk)?; + + assert!(encrypted_header + .decrypt(&cover_crypt, &top_secret_fin_usk, None) + .unwrap() + .is_some()); + + // refresh the user key and preserve access to old partitions + let _new_decryption_policy = + AccessPolicy::parse("Security Level::Top Secret && Department::Finance")?; + cover_crypt.refresh_usk(&mut top_secret_fin_usk, &mut msk, false)?; + assert!(encrypted_header + .decrypt(&cover_crypt, &top_secret_fin_usk, None) + .unwrap() + .is_some()); + + Ok(()) + } + + #[test] + fn encrypt_decrypt_sym_key() -> Result<(), Error> { + let policy = policy()?; + let access_policy = AccessPolicy::parse( + "(Department::MKG || Department::FIN) && Security Level::Top Secret", + ) + .unwrap(); + let cover_crypt = Covercrypt::default(); + let (mut msk, _) = cover_crypt.setup()?; + let mpk = cover_crypt.update_master_keys(&policy, &mut msk)?; + let (sym_key, encrypted_key) = cover_crypt.encaps( + &policy, + &mpk, + AccessPolicy::parse("Department::MKG && Security Level::Top Secret")?, + )?; + let usk = cover_crypt.generate_user_secret_key(&mut msk, &access_policy, &policy)?; + let recovered_key = cover_crypt.decaps(&usk, &encrypted_key)?; + assert_eq!(Some(sym_key), recovered_key, "Wrong decryption of the key!"); + Ok(()) + } + + #[test] + fn test_single_attribute_in_access_policy() -> Result<(), Error> { + // + // Declare policy + let policy = policy()?; + + // + // Setup Covercrypt + let cover_crypt = Covercrypt::default(); + let (mut msk, _) = cover_crypt.setup()?; + let _ = cover_crypt.update_master_keys(&policy, &mut msk)?; + + // + // New user secret key + let _user_key = cover_crypt.generate_user_secret_key( + &mut msk, + &AccessPolicy::parse("Security Level::Top Secret")?, + &policy, + )?; + + Ok(()) + } + + #[test] + fn test_rotate_then_encrypt() -> Result<(), Error> { + // + // Declare policy + let policy = policy()?; + let top_secret_ap = AccessPolicy::parse("Security Level::Top Secret")?; + + // + // Setup Covercrypt + let cover_crypt = Covercrypt::default(); + let (mut msk, _) = cover_crypt.setup()?; + let mpk = cover_crypt.update_master_keys(&policy, &mut msk)?; + + // + // New user secret key + let mut top_secret_fin_usk = cover_crypt.generate_user_secret_key( + &mut msk, + &AccessPolicy::parse("Security Level::Top Secret && Department::FIN")?, + &policy, + )?; + + // + // Encrypt + let (_, encrypted_header) = + EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None)?; + + let _plaintext_header = + encrypted_header.decrypt(&cover_crypt, &top_secret_fin_usk, None)?; + + // + // Rotate argument (must update master keys) + let rekey_ap = AccessPolicy::Attr(Attribute::from(("Security Level", "Top Secret"))); + let mpk = cover_crypt.rekey(&rekey_ap, &policy, &mut msk)?; + + // + // Encrypt with new attribute + let (_, encrypted_header) = + EncryptedHeader::generate(&cover_crypt, &policy, &mpk, &top_secret_ap, None, None)?; + + // Decryption fails without refreshing the user key + assert!(encrypted_header + .decrypt(&cover_crypt, &top_secret_fin_usk, None) + .unwrap() + .is_none()); + + cover_crypt.refresh_usk(&mut top_secret_fin_usk, &mut msk, false)?; + + // The refreshed key can decrypt the header + assert!(encrypted_header + .decrypt(&cover_crypt, &top_secret_fin_usk, None) + .unwrap() + .is_some()); + + Ok(()) + } +}