Revision control
Copy as Markdown
Other Tools
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Copyright by contributors to this project.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
/// Padding used when sending an encrypted group message.
#[cfg_attr(all(feature = "ffi", not(test)), safer_ffi_gen::ffi_type)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum PaddingMode {
/// Step function based on the size of the message being sent.
/// The amount of padding used will increase with the size of the original
/// message.
#[default]
StepFunction,
/// No padding.
None,
}
impl PaddingMode {
pub(super) fn padded_size(&self, content_size: usize) -> usize {
match self {
PaddingMode::StepFunction => {
// The padding hides all but 2 most significant bits of `length`. The hidden bits are replaced
// by zeros and then the next number is taken to make sure the message fits.
let blind = 1
<< ((content_size + 1)
.next_power_of_two()
.max(256)
.trailing_zeros()
- 3);
(content_size | (blind - 1)) + 1
}
PaddingMode::None => content_size,
}
}
}
#[cfg(test)]
mod tests {
use super::PaddingMode;
use alloc::vec;
use alloc::vec::Vec;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test as test;
#[derive(serde::Deserialize, serde::Serialize)]
struct TestCase {
input: usize,
output: usize,
}
#[cfg_attr(coverage_nightly, coverage(off))]
fn generate_message_padding_test_vector() -> Vec<TestCase> {
let mut test_cases = vec![];
for x in 1..1024 {
test_cases.push(TestCase {
input: x,
output: PaddingMode::StepFunction.padded_size(x),
});
}
test_cases
}
fn load_test_cases() -> Vec<TestCase> {
load_test_case_json!(
message_padding_test_vector,
generate_message_padding_test_vector()
)
}
#[test]
fn test_no_padding() {
for i in [0, 100, 1000, 10000] {
assert_eq!(PaddingMode::None.padded_size(i), i)
}
}
#[test]
fn test_padding_length() {
assert_eq!(PaddingMode::StepFunction.padded_size(0), 32);
// Short
assert_eq!(PaddingMode::StepFunction.padded_size(63), 64);
assert_eq!(PaddingMode::StepFunction.padded_size(64), 96);
assert_eq!(PaddingMode::StepFunction.padded_size(65), 96);
// Almost long and almost short
assert_eq!(PaddingMode::StepFunction.padded_size(127), 128);
assert_eq!(PaddingMode::StepFunction.padded_size(128), 160);
assert_eq!(PaddingMode::StepFunction.padded_size(129), 160);
// One length from each of the 4 buckets between 256 and 512
assert_eq!(PaddingMode::StepFunction.padded_size(260), 320);
assert_eq!(PaddingMode::StepFunction.padded_size(330), 384);
assert_eq!(PaddingMode::StepFunction.padded_size(390), 448);
assert_eq!(PaddingMode::StepFunction.padded_size(490), 512);
// All test cases
let test_cases: Vec<TestCase> = load_test_cases();
for test_case in test_cases {
assert_eq!(
test_case.output,
PaddingMode::StepFunction.padded_size(test_case.input)
);
}
}
}