crypto: Proptest the emoji/decimal calculation.

master
Damir Jelić 2020-08-03 17:22:44 +02:00
parent 9f0fbcccf6
commit 26ec0c6368
5 changed files with 89 additions and 20 deletions

View File

@ -63,7 +63,7 @@ impl Sas {
}
/// Get the decimal version of the short auth string.
pub fn decimals(&self) -> Option<(u32, u32, u32)> {
pub fn decimals(&self) -> Option<(u16, u16, u16)> {
self.inner.decimals()
}

View File

@ -45,8 +45,9 @@ default-features = false
features = ["runtime-tokio", "sqlite"]
[dev-dependencies]
tokio = { version = "0.2.21", features = ["rt-threaded", "macros"] }
serde_json = "1.0.56"
tokio = { version = "0.2.22", features = ["rt-threaded", "macros"] }
proptest = "0.10.0"
serde_json = "1.0.57"
tempfile = "3.1.0"
http = "0.2.1"
matrix-sdk-test = { version = "0.1.0", path = "../matrix_sdk_test" }

View File

@ -318,13 +318,15 @@ pub fn get_emoji(
flow_id: &str,
we_started: bool,
) -> Vec<(&'static str, &'static str)> {
let bytes: Vec<u64> = sas
let bytes = sas
.generate_bytes(&extra_info_sas(&ids, &flow_id, we_started), 6)
.expect("Can't generate bytes")
.into_iter()
.map(|b| b as u64)
.collect();
.expect("Can't generate bytes");
bytes_to_emoji(bytes)
}
fn bytes_to_emoji_index(bytes: Vec<u8>) -> Vec<u8> {
let bytes: Vec<u64> = bytes.iter().map(|b| *b as u64).collect();
// Join the 6 bytes into one 64 bit unsigned int. This u64 will contain 48
// bits from our 6 bytes.
let mut num: u64 = bytes[0] << 40;
@ -336,7 +338,7 @@ pub fn get_emoji(
// Take the top 42 bits of our 48 bits from the u64 and convert each 6 bits
// into a 6 bit number.
let numbers = vec![
vec![
((num >> 42) & 63) as u8,
((num >> 36) & 63) as u8,
((num >> 30) & 63) as u8,
@ -344,7 +346,11 @@ pub fn get_emoji(
((num >> 18) & 63) as u8,
((num >> 12) & 63) as u8,
((num >> 6) & 63) as u8,
];
]
}
fn bytes_to_emoji(bytes: Vec<u8>) -> Vec<(&'static str, &'static str)> {
let numbers = bytes_to_emoji_index(bytes);
// Convert the 6 bit number into a emoji/description tuple.
numbers.into_iter().map(emoji_from_index).collect()
@ -369,13 +375,16 @@ pub fn get_emoji(
/// # Panics
///
/// This will panic if the public key of the other side wasn't set.
pub fn get_decimal(sas: &OlmSas, ids: &SasIds, flow_id: &str, we_started: bool) -> (u32, u32, u32) {
let bytes: Vec<u32> = sas
pub fn get_decimal(sas: &OlmSas, ids: &SasIds, flow_id: &str, we_started: bool) -> (u16, u16, u16) {
let bytes = sas
.generate_bytes(&extra_info_sas(&ids, &flow_id, we_started), 5)
.expect("Can't generate bytes")
.into_iter()
.map(|b| b as u32)
.collect();
.expect("Can't generate bytes");
bytes_to_decimal(bytes)
}
fn bytes_to_decimal(bytes: Vec<u8>) -> (u16, u16, u16) {
let bytes: Vec<u16> = bytes.into_iter().map(|b| b as u16).collect();
// This bitwise operation is taken from the [spec]
// [spec]: https://matrix.org/docs/spec/client_server/latest#sas-method-decimal
@ -415,3 +424,62 @@ pub fn content_to_request(
messages,
}
}
#[cfg(test)]
mod test {
use proptest::prelude::*;
use super::{bytes_to_decimal, bytes_to_emoji, bytes_to_emoji_index, emoji_from_index};
#[test]
fn test_emoji_generation() {
let bytes = vec![0, 0, 0, 0, 0, 0];
let index: Vec<(&'static str, &'static str)> = vec![0, 0, 0, 0, 0, 0, 0]
.into_iter()
.map(emoji_from_index)
.collect();
assert_eq!(bytes_to_emoji(bytes), index);
let bytes = vec![0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
let index: Vec<(&'static str, &'static str)> = vec![63, 63, 63, 63, 63, 63, 63]
.into_iter()
.map(emoji_from_index)
.collect();
assert_eq!(bytes_to_emoji(bytes), index);
}
#[test]
fn test_decimal_generation() {
let bytes = vec![0, 0, 0, 0, 0];
let result = bytes_to_decimal(bytes);
assert_eq!(result, (1000, 1000, 1000));
let bytes = vec![0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
let result = bytes_to_decimal(bytes);
assert_eq!(result, (9191, 9191, 9191));
}
proptest! {
#[test]
fn proptest_emoji(bytes in prop::array::uniform6(0u8..)) {
let numbers = bytes_to_emoji_index(bytes.to_vec());
for number in numbers {
prop_assert!(number < 64);
}
}
}
proptest! {
#[test]
fn proptest_decimals(bytes in prop::array::uniform5(0u8..)) {
let (first, second, third) = bytes_to_decimal(bytes.to_vec());
prop_assert!(first <= 9191 && first >= 1000);
prop_assert!(second <= 9191 && second >= 1000);
prop_assert!(third <= 9191 && third >= 1000);
}
}
}

View File

@ -257,7 +257,7 @@ impl Sas {
/// Returns None if we can't yet present the short auth string, otherwise a
/// tuple containing three 4-digit integers that represent the short auth
/// string.
pub fn decimals(&self) -> Option<(u32, u32, u32)> {
pub fn decimals(&self) -> Option<(u16, u16, u16)> {
self.inner.lock().unwrap().decimals()
}
@ -464,7 +464,7 @@ impl InnerSas {
}
}
fn decimals(&self) -> Option<(u32, u32, u32)> {
fn decimals(&self) -> Option<(u16, u16, u16)> {
match self {
InnerSas::KeyRecieved(s) => Some(s.get_decimal()),
InnerSas::MacReceived(s) => Some(s.get_decimal()),

View File

@ -530,7 +530,7 @@ impl SasState<KeyReceived> {
///
/// Returns a tuple containing three 4 digit integer numbers that represent
/// the short auth string.
pub fn get_decimal(&self) -> (u32, u32, u32) {
pub fn get_decimal(&self) -> (u16, u16, u16) {
get_decimal(
&self.inner.lock().unwrap(),
&self.ids,
@ -667,7 +667,7 @@ impl SasState<MacReceived> {
///
/// Returns a tuple containing three 4 digit integer numbers that represent
/// the short auth string.
pub fn get_decimal(&self) -> (u32, u32, u32) {
pub fn get_decimal(&self) -> (u16, u16, u16) {
get_decimal(
&self.inner.lock().unwrap(),
&self.ids,