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. /// 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() self.inner.decimals()
} }

View File

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

View File

@ -318,13 +318,15 @@ pub fn get_emoji(
flow_id: &str, flow_id: &str,
we_started: bool, we_started: bool,
) -> Vec<(&'static str, &'static str)> { ) -> Vec<(&'static str, &'static str)> {
let bytes: Vec<u64> = sas let bytes = sas
.generate_bytes(&extra_info_sas(&ids, &flow_id, we_started), 6) .generate_bytes(&extra_info_sas(&ids, &flow_id, we_started), 6)
.expect("Can't generate bytes") .expect("Can't generate bytes");
.into_iter()
.map(|b| b as u64)
.collect();
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 // Join the 6 bytes into one 64 bit unsigned int. This u64 will contain 48
// bits from our 6 bytes. // bits from our 6 bytes.
let mut num: u64 = bytes[0] << 40; 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 // Take the top 42 bits of our 48 bits from the u64 and convert each 6 bits
// into a 6 bit number. // into a 6 bit number.
let numbers = vec![ vec![
((num >> 42) & 63) as u8, ((num >> 42) & 63) as u8,
((num >> 36) & 63) as u8, ((num >> 36) & 63) as u8,
((num >> 30) & 63) as u8, ((num >> 30) & 63) as u8,
@ -344,7 +346,11 @@ pub fn get_emoji(
((num >> 18) & 63) as u8, ((num >> 18) & 63) as u8,
((num >> 12) & 63) as u8, ((num >> 12) & 63) as u8,
((num >> 6) & 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. // Convert the 6 bit number into a emoji/description tuple.
numbers.into_iter().map(emoji_from_index).collect() numbers.into_iter().map(emoji_from_index).collect()
@ -369,13 +375,16 @@ pub fn get_emoji(
/// # Panics /// # Panics
/// ///
/// This will panic if the public key of the other side wasn't set. /// 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) { pub fn get_decimal(sas: &OlmSas, ids: &SasIds, flow_id: &str, we_started: bool) -> (u16, u16, u16) {
let bytes: Vec<u32> = sas let bytes = sas
.generate_bytes(&extra_info_sas(&ids, &flow_id, we_started), 5) .generate_bytes(&extra_info_sas(&ids, &flow_id, we_started), 5)
.expect("Can't generate bytes") .expect("Can't generate bytes");
.into_iter()
.map(|b| b as u32) bytes_to_decimal(bytes)
.collect(); }
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] // This bitwise operation is taken from the [spec]
// [spec]: https://matrix.org/docs/spec/client_server/latest#sas-method-decimal // [spec]: https://matrix.org/docs/spec/client_server/latest#sas-method-decimal
@ -415,3 +424,62 @@ pub fn content_to_request(
messages, 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 /// 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 /// tuple containing three 4-digit integers that represent the short auth
/// string. /// string.
pub fn decimals(&self) -> Option<(u32, u32, u32)> { pub fn decimals(&self) -> Option<(u16, u16, u16)> {
self.inner.lock().unwrap().decimals() 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 { match self {
InnerSas::KeyRecieved(s) => Some(s.get_decimal()), InnerSas::KeyRecieved(s) => Some(s.get_decimal()),
InnerSas::MacReceived(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 /// Returns a tuple containing three 4 digit integer numbers that represent
/// the short auth string. /// the short auth string.
pub fn get_decimal(&self) -> (u32, u32, u32) { pub fn get_decimal(&self) -> (u16, u16, u16) {
get_decimal( get_decimal(
&self.inner.lock().unwrap(), &self.inner.lock().unwrap(),
&self.ids, &self.ids,
@ -667,7 +667,7 @@ impl SasState<MacReceived> {
/// ///
/// Returns a tuple containing three 4 digit integer numbers that represent /// Returns a tuple containing three 4 digit integer numbers that represent
/// the short auth string. /// the short auth string.
pub fn get_decimal(&self) -> (u32, u32, u32) { pub fn get_decimal(&self) -> (u16, u16, u16) {
get_decimal( get_decimal(
&self.inner.lock().unwrap(), &self.inner.lock().unwrap(),
&self.ids, &self.ids,