diff --git a/Cargo.toml b/Cargo.toml index 6b273af..8112009 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ rand = "0.8.5" serde = { version = "1.0.203", features = ["derive"] } serde_with = "3.8.1" sha2 = "0.10.8" +test-strategy = "0.4.0" thiserror = "1.0.61" tokio = { version = "1.37", features = ["full"] } tokio-util = { version = "0.7.11", features = ["io-util"] } @@ -58,7 +59,6 @@ cddl = "0.9.4" criterion = { version = "0.5.1", features = ["async_tokio"] } criterion-macro = "0.4.0" tempfile = "3.10.1" -test-strategy = "0.4.0" tracing-test = { version = "0.2.5", features = ["no-env-filter"] } [[bench]] diff --git a/cddl/registry.cddl b/cddl/registry.cddl index cab6c2d..e9778d9 100644 --- a/cddl/registry.cddl +++ b/cddl/registry.cddl @@ -16,9 +16,6 @@ RRR_Registry = { succession_nonce_length_in_bytes: uint .ge 16, root_predecessor_nonce: bstr, ; .size (16..1000), ; The upper bound is arbitrary TODO: not supported by cddl-rs }, - encryption: { - segment_padding_to_bytes: uint, - }, ? verifying_keys: [ * RRR_VerifyingKey ], } diff --git a/src/record/mod.rs b/src/record/mod.rs index 2573b97..26a9bb9 100644 --- a/src/record/mod.rs +++ b/src/record/mod.rs @@ -16,7 +16,7 @@ use proptest_arbitrary_interop::arb; use proptest_derive::Arbitrary; use segment::{ FragmentFileNameBytes, FragmentKey, FragmentReadSuccess, KdfUsageFragmentParameters, - RecordNonce, RecordParameters, RecordVersion, Segment, SegmentMetadata, + RecordNonce, RecordParameters, RecordVersion, Segment, SegmentEncryption, SegmentMetadata, }; use serde::{Deserialize, Serialize}; use std::ops::{Deref, DerefMut}; @@ -217,7 +217,7 @@ impl Record { let fragment_file_guard = fragment_file.lock_read().await?; let segment = Segment::read_fragment( ®istry.config.verifying_keys, - ®istry.config, + ®istry.config.kdf, fragment_file_guard, &fragment_key, ) @@ -300,7 +300,7 @@ impl Record { record_version: RecordVersion, max_collision_resolution_attempts: u64, split_at: &[usize], // TODO: Should be generated from the serialized record length. - encryption_algorithm: Option<&EncryptionAlgorithm>, + encryption: Option<&SegmentEncryption>, open_options: &OpenOptions, ) -> Result { let hashed_key = hash_record_path.hash_record_path(registry).await?; @@ -414,10 +414,10 @@ impl Record { segment .write_fragment( signing_keys, - ®istry.config, + ®istry.config.kdf, &mut fragment_file_guard, fragment_key, - encryption_algorithm, + encryption, ) .await?; diff --git a/src/record/segment.rs b/src/record/segment.rs index 91b46af..6000f9a 100644 --- a/src/record/segment.rs +++ b/src/record/segment.rs @@ -5,7 +5,7 @@ use crate::crypto::encryption::{Decrypt, Encrypt, EncryptionAlgorithm}; use crate::crypto::signature::{SigningKey, VerifyingKey}; use crate::error::{Error, Result}; use crate::record::{HashedRecordKey, RecordKey}; -use crate::registry::{RegistryConfig, RegistryConfigKdf}; +use crate::registry::RegistryConfigKdf; use crate::utils::serde::{BytesOrAscii, BytesOrHexString, Secret}; use async_scoped::TokioScope; use coset::cbor::tag; @@ -355,7 +355,7 @@ impl<'a> From<&'a Segment> for SegmentSerde<'a> { impl Segment { pub async fn read_fragment( verifying_keys: &[VerifyingKey], - config: &RegistryConfig, + kdf_params: &RegistryConfigKdf, mut read: impl AsyncRead + Unpin + Send, fragment_key: &FragmentKey, ) -> Result { @@ -433,7 +433,7 @@ impl Segment { }) })?; let encryption_key = fragment_key - .derive_encryption_key(&config.kdf, &encryption_algorithm) + .derive_encryption_key(kdf_params, &encryption_algorithm) .await?; let plaintext = encrypted.decrypt( &[], @@ -443,10 +443,6 @@ impl Segment { }, )?; - if plaintext.len() != *config.encryption.segment_padding_to_bytes as usize { - return Err(Error::MalformedFragment); - } - let mut plaintext_cursor = Cursor::new(plaintext.as_slice()); let plaintext_value: cbor::Value = coset::cbor::de::from_reader(&mut plaintext_cursor).map_err(Error::CborDe)?; @@ -478,41 +474,38 @@ impl Segment { pub async fn write_fragment( &self, signing_keys: &[SigningKey], - config: &RegistryConfig, + kdf_params: &RegistryConfigKdf, write: impl AsyncWrite + AsyncSeek + Unpin + Send, fragment_key: &FragmentKey, - encryption_algorithm: Option<&EncryptionAlgorithm>, + encryption_algorithm: Option<&SegmentEncryption>, ) -> Result<()> { let output_value = cbor::Value::serialized(self).map_err(Error::Cbor)?; // Encrypt the fragment data, if an encryption algorithm was provided. - let output_value = if let Some(encryption_algorithm) = encryption_algorithm { + let output_value = if let Some(encryption) = encryption_algorithm { let plaintext = { let mut plaintext = output_value.to_vec().map_err(Error::Coset)?; let padding_length = u64::try_from(plaintext.len()) .ok() .and_then(|plaintext_length| { - config - .encryption - .segment_padding_to_bytes - .checked_sub(plaintext_length) + encryption.padding_to_bytes.checked_sub(plaintext_length) }) .ok_or_else(|| Error::SegmentTooLarge { length: plaintext.len() as u64, - max_length: *config.encryption.segment_padding_to_bytes, + max_length: encryption.padding_to_bytes, })?; plaintext.extend(std::iter::repeat(0_u8).take(padding_length as usize)); plaintext }; let encryption_key = fragment_key - .derive_encryption_key(&config.kdf, encryption_algorithm) + .derive_encryption_key(kdf_params, &encryption.algorithm) .await?; let encrypted = coset::CoseEncrypt0Builder::new() .protected( HeaderBuilder::new() // Placed in the `protected` fields according to // https://datatracker.ietf.org/doc/html/rfc8152#section-3.1 - .algorithm((*encryption_algorithm).into()) + .algorithm(encryption.algorithm.into()) // .content_type("application/binary".to_string()) // TODO .build(), ) @@ -520,7 +513,7 @@ impl Segment { &plaintext, &[], Encrypt { - algorithm: encryption_algorithm, + algorithm: &encryption.algorithm, key: &encryption_key, }, )? @@ -607,3 +600,10 @@ pub struct SegmentWithContext<'record> { pub record: Cow<'record, Segment>, pub context: SegmentContext, } + +#[derive(Debug, Clone, test_strategy::Arbitrary)] +pub struct SegmentEncryption { + pub algorithm: EncryptionAlgorithm, + #[strategy(256..=2048_u64)] + pub padding_to_bytes: u64, +} diff --git a/src/registry.rs b/src/registry.rs index 98be837..2e85458 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -1,10 +1,11 @@ use crate::cbor::{TAG_RRR_REGISTRY, TAG_SELF_DESCRIBED_CBOR}; -use crate::crypto::encryption::EncryptionAlgorithm; use crate::crypto::kdf::KdfAlgorithm; use crate::crypto::password_hash::PasswordHashAlgorithm; use crate::crypto::signature::{SigningKey, VerifyingKey}; use crate::error::{Error, InvalidParameterError, OptionExt, Result}; -use crate::record::segment::{FragmentFileNameBytes, RecordNonce, RecordVersion}; +use crate::record::segment::{ + FragmentFileNameBytes, RecordNonce, RecordVersion, SegmentEncryption, +}; use crate::record::{HashRecordPath, Record, RecordReadVersionSuccess, SuccessionNonce}; use crate::utils::serde::{BytesOrHexString, Secret}; use async_fd_lock::{LockRead, LockWrite}; @@ -382,16 +383,10 @@ impl Arbitrary for RegistryConfigKdf { } } -#[derive(Arbitrary, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct RegistryConfigEncryption { - pub segment_padding_to_bytes: ConfigParam, -} - #[derive(Arbitrary, Clone, Debug, PartialEq, Eq)] pub struct RegistryConfig { pub hash: RegistryConfigHash, pub kdf: RegistryConfigKdf, - pub encryption: RegistryConfigEncryption, pub verifying_keys: Vec, } @@ -404,7 +399,6 @@ pub struct RegistryConfig { struct RegistryConfigSerde { hash: RegistryConfigHash, kdf: RegistryConfigKdf, - encryption: RegistryConfigEncryption, #[serde_as(as = "Vec")] verifying_keys: Vec, } @@ -414,7 +408,6 @@ impl From for RegistryConfigSerde { Self { hash: value.hash, kdf: value.kdf, - encryption: value.encryption, verifying_keys: value.verifying_keys, } } @@ -425,7 +418,6 @@ impl From for RegistryConfig { Self { hash: value.hash, kdf: value.kdf, - encryption: value.encryption, verifying_keys: value.verifying_keys, } } @@ -655,7 +647,7 @@ impl Registry { record_version: RecordVersion, max_collision_resolution_attempts: u64, split_at: &[usize], - encryption_algorithm: Option<&EncryptionAlgorithm>, + encryption: Option<&SegmentEncryption>, overwrite: bool, ) -> Result { let open_options = { @@ -676,7 +668,7 @@ impl Registry { record_version, max_collision_resolution_attempts, split_at, - encryption_algorithm, + encryption, &open_options, ) .await diff --git a/tests/cddl.rs b/tests/cddl.rs index c488ae8..7a61647 100644 --- a/tests/cddl.rs +++ b/tests/cddl.rs @@ -9,9 +9,10 @@ use coset::CborSerializable; use itertools::Itertools; use rrr::{ cbor::{SerializeExt, Value, ValueExt}, - crypto::encryption::EncryptionAlgorithm, record::{ - segment::{FragmentKey, KdfUsage, KdfUsageFragmentParameters, RecordParameters}, + segment::{ + FragmentKey, KdfUsage, KdfUsageFragmentParameters, RecordParameters, SegmentEncryption, + }, Record, RecordKey, }, }; @@ -97,7 +98,7 @@ fn verify_cddl_segment(args: RegistryConfigWithSigningKeysAndSegment) { #[traced_test] async fn verify_cddl_fragment( args: RegistryConfigWithSigningKeysAndSegment, - encryption_algorithm: Option, + encryption: Option, ) { let RegistryConfigWithSigningKeysAndSegment { registry_config_with_signing_keys: @@ -127,10 +128,10 @@ async fn verify_cddl_fragment( segment .write_fragment( &signing_keys, - &config, + &config.kdf, &mut fragment_bytes, &fragment_key, - encryption_algorithm.as_ref(), + encryption.as_ref(), ) .await .unwrap(); diff --git a/tests/test.rs b/tests/test.rs index 669d56a..6c01cc3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -3,11 +3,10 @@ use futures::FutureExt; use prop::collection::vec; use proptest::prelude::*; use rrr::error::Error; -use rrr::record::segment::{RecordNonce, RecordVersion}; +use rrr::record::segment::{RecordNonce, RecordVersion, SegmentEncryption}; use rrr::record::{RecordName, SuccessionNonce}; use rrr::utils::serde::BytesOrHexString; use rrr::{ - crypto::encryption::EncryptionAlgorithm, record::{HashedRecordKey, Record, RecordKey}, registry::Registry, }; @@ -128,7 +127,7 @@ async fn prop_registry( config_with_signing_keys: RegistryConfigWithSigningKeys, #[strategy(arb_record_test_tree(3, 16, 3, 2))] record_test_tree: RecordTestNode, - encryption_algorithm: Option, + encryption: Option, ) { dbg!(&record_test_tree); @@ -159,7 +158,7 @@ async fn prop_registry( *record_version, max_collision_resolution_attempts, &[], // TODO - encryption_algorithm.as_ref(), // TODO + encryption.as_ref(), // TODO false, ) .await;