From c99a629c6a1d237eff3710a413464f718af7f8a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20BR=C3=89ZOT?= Date: Wed, 31 Jan 2024 09:38:05 +0100 Subject: [PATCH] wip: fix: serialization --- Cargo.toml | 13 +- examples/decrypt.rs | 45 +- examples/encrypt.rs | 116 ++-- src/core/api.rs | 21 +- src/core/mod.rs | 2 + src/core/primitives.rs | 4 + src/core/serialization/mod.rs | 552 ++++++++++++++++++ .../mod.rs~} | 6 +- src/core/tests.rs | 252 ++++---- src/core/tests.rs~ | 116 ---- src/lib.rs | 2 - src/test_utils/mod.rs | 3 +- 12 files changed, 811 insertions(+), 321 deletions(-) create mode 100644 src/core/serialization/mod.rs rename src/core/{serialization.rs => serialization/mod.rs~} (99%) delete mode 100644 src/core/tests.rs~ diff --git a/Cargo.toml b/Cargo.toml index 36e13043..46d8e2fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,18 @@ 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"] } @@ -32,5 +41,5 @@ tiny-keccak = { version = "2.0.2", features = ["shake", "kmac"] } zeroize = "1.6.0" [dev-dependencies] -base64 = { version = "0.21.0" } -criterion = { version = "0.4", features = ["html_reports"], default_features = false } +# base64 = { version = "0.21.0" } +# criterion = { version = "0.4", features = ["html_reports"], default_features = false } 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..eb541f84 100644 --- a/examples/encrypt.rs +++ b/examples/encrypt.rs @@ -1,80 +1,64 @@ -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", + let config: GeneralPurposeConfig = GeneralPurposeConfig::default(); + let transcoder: GeneralPurpose = GeneralPurpose::new(&STANDARD, config); + println!( + "USK = {}", + transcoder.encode( + cc.generate_user_secret_key(&mut _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/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..9a39cfd5 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -20,6 +20,8 @@ pub mod macros; pub mod api; pub mod pq_kem; pub mod primitives; + +// #[cfg(feature = "serialization")] // pub mod serialization; mod pke; diff --git a/src/core/primitives.rs b/src/core/primitives.rs index 6721118f..7e4d1554 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(()) diff --git a/src/core/serialization/mod.rs b/src/core/serialization/mod.rs new file mode 100644 index 00000000..54036a18 --- /dev/null +++ b/src/core/serialization/mod.rs @@ -0,0 +1,552 @@ +//! 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::{TracingPublicKey, 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 TracingPublicKey { + type Error = Error; + + fn length(&self) -> usize { + todo!() + } + + fn write(&self, ser: &mut Serializer) -> Result { + todo!() + } + + fn read(de: &mut Deserializer) -> Result { + todo!() + } +} + +impl Serializable for MasterPublicKey { + type Error = Error; + + fn length(&self) -> usize { + let mut length = 2 * R25519PublicKey::LENGTH + + 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` 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." + ); + } + + Ok(()) + } +} diff --git a/src/core/serialization.rs b/src/core/serialization/mod.rs~ similarity index 99% rename from src/core/serialization.rs rename to src/core/serialization/mod.rs~ index 5457772c..5a81cec0 100644 --- a/src/core/serialization.rs +++ b/src/core/serialization/mod.rs~ @@ -56,12 +56,13 @@ macro_rules! deserialize_option { }}; } +impl Serializable for TracingPublicKey {} + 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 { @@ -484,11 +485,10 @@ mod tests { let encapsulation_ = Encapsulation::deserialize(&bytes)?; assert_eq!( encapsulation, encapsulation_, - "Wrong `Encapsulation` derserialization." + "Wrong `Encapsulation` serialization." ); // Setup Covercrypt. - #[cfg(feature = "test_utils")] { use crate::{abe_policy::AccessPolicy, test_utils::policy, Covercrypt}; 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/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..8ab3174b 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",