Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test that the encrypted payloads are uniform. #374

Merged
merged 1 commit into from
Oct 24, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions payjoin/src/hpke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,4 +466,72 @@ mod test {
})
);
}

/// Test that the encrypted payloads are uniform.
///
/// This randomized test will generate a false negative with negligible probability
/// if all encrypted messages share an identical bit at a given position by chance.
/// It should fail deterministically if any bit position has a fixed value.
#[test]
fn test_encrypted_payload_bit_uniformity() {
fn generate_messages(count: usize) -> (Vec<Vec<u8>>, Vec<Vec<u8>>) {
let mut messages_a = Vec::with_capacity(count);
let mut messages_b = Vec::with_capacity(count);

for _ in 0..count {
let sender_keypair = HpkeKeyPair::gen_keypair();
let receiver_keypair = HpkeKeyPair::gen_keypair();
let reply_keypair = HpkeKeyPair::gen_keypair();

let plaintext_a = vec![0u8; PADDED_PLAINTEXT_A_LENGTH];
let message_a = encrypt_message_a(
plaintext_a,
reply_keypair.public_key(),
receiver_keypair.public_key(),
)
.expect("encryption should work");

let plaintext_b = vec![0u8; PADDED_PLAINTEXT_B_LENGTH];
let message_b =
encrypt_message_b(plaintext_b, &receiver_keypair, sender_keypair.public_key())
.expect("encryption should work");

messages_a.push(message_a);
messages_b.push(message_b);
}

(messages_a, messages_b)
}

/// Compare each message to the first message, XOR the results,
/// and OR this into an accumulator that starts as all 0x00s.
fn check_uniformity(messages: Vec<Vec<u8>>) {
assert!(!messages.is_empty(), "Messages vector should not be empty");
let reference_message = &messages[0];
let mut accumulator = vec![0u8; PADDED_MESSAGE_BYTES];

for message in &messages[1..] {
assert_eq!(
reference_message.len(),
message.len(),
"Message lengths should be equal"
);
for (acc, (&b_ref, &b)) in
accumulator.iter_mut().zip(reference_message.iter().zip(message.iter()))
{
*acc |= b_ref ^ b;
}
}

assert!(
accumulator.iter().all(|&b| b == 0xFF),
"All bits in the accumulator should be 1"
);
}

let (messages_a, messages_b) = generate_messages(80);
assert_eq!(messages_a[0].len(), messages_b[0].len());
check_uniformity(messages_a);
check_uniformity(messages_b);
}
}
Loading