Source code

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.
#[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,
/// Padme, which limits information leakage to O(log log M) bits while
/// retaining an overhead of max 11.11%, defined as Algorithm 1 in
Padme,
/// 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::Padme => {
// Prevents log2(0), which is undefined.
if content_size < 2 {
return content_size;
}
// E <- floor(log2(L))
// S <- floor(log2(E)) + 1
// z <- E - S
// m <- (1 << z) - 1
// len' <- (L + m) & ~m
let e: u32 = content_size.ilog2(); // l’s floating-point exponent
let s: u32 = e.ilog2() + 1; // number of bits to represent e
let num_zero_bits: u32 = e - s; // number of low bits to set to 0
let bitmask: usize = (1 << num_zero_bits) - 1; // create a bitmask of 1s
(content_size + bitmask) & !bitmask // len': round up to clear last num_zero_bits bits
}
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_step_function() {
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)
);
}
}
#[test]
fn test_padme_exceptions() {
assert_eq!(PaddingMode::Padme.padded_size(0), 0);
assert_eq!(PaddingMode::Padme.padded_size(1), 1);
}
// All values are computed using reference implementation found at
#[test]
fn test_padme_powers_of_two() {
for i in 0u32..32 {
let val = 2usize.pow(i);
assert_eq!(PaddingMode::Padme.padded_size(val), val);
}
}
#[test]
fn test_padme_powers_of_ten() {
let res: [usize; 10] = [
1, 10, 104, 1024, 10240, 100352, 1015808, 10223616, 100663296, 1006632960,
];
for (i, result) in res.iter().enumerate() {
assert_eq!(
PaddingMode::Padme.padded_size(10usize.pow(i as u32)),
*result
);
}
}
#[test]
fn test_padme_rand() {
let vec = [
(441181141, 444596224),
(942823001, 956301312),
(1017891638, 1023410176),
(1045008200, 1056964608),
(2068479553, 2080374784),
(2096246256, 2113929216),
(2523113277, 2550136832),
(3011885937, 3019898880),
(3212797841, 3221225472),
(3886482937, 3892314112),
];
for (val, res) in vec.iter() {
assert_eq!(PaddingMode::Padme.padded_size(*val), *res);
}
}
}