use vitaminc_aead::tags;
use super::{
body, node, Cursor, ErrorKind, Invalid, MapKey, ARRAY, HEADER, MAGIC, MAP, MAX_DEPTH, SOME,
VERSION,
};
fn any_bytes<const N: usize>() -> ([u8; N], usize) {
let bytes: [u8; N] = kani::any();
let len: usize = kani::any();
kani::assume(len <= N);
(bytes, len)
}
#[kani::proof]
#[kani::unwind(8)]
fn the_header_is_refused_exactly_as_its_rules_say() {
let (bytes, len) = any_bytes::<6>();
let input = &bytes[..len];
let magic = len >= 3 && bytes[0] == MAGIC[0] && bytes[1] == MAGIC[1] && bytes[2] == MAGIC[2];
let whole = len >= HEADER.len();
match body(input) {
Ok(rest) => {
assert!(whole && magic && bytes[3] == VERSION);
assert!(core::ptr::eq(rest, &input[HEADER.len()..]));
}
Err(e) if e.kind() == ErrorKind::NotABlob => assert!(!whole || !magic),
Err(e) => {
assert!(e.kind() == ErrorKind::UnsupportedVersion);
assert!(whole && magic && bytes[3] != VERSION);
}
}
}
#[kani::proof]
fn a_count_is_never_more_than_the_bytes_left() {
let (bytes, len) = any_bytes::<8>();
let pos: usize = kani::any();
kani::assume(pos <= len);
let mut cursor = Cursor {
buf: &bytes[..len],
pos,
};
let left = len - pos;
let expected = (left >= 4)
.then(|| u32::from_le_bytes([bytes[pos], bytes[pos + 1], bytes[pos + 2], bytes[pos + 3]]))
.filter(|&n| n as usize <= left - 4);
match cursor.count() {
Ok(n) => {
assert!(expected == Some(n as u32));
assert!(cursor.pos == pos + 4);
assert!(n <= len - cursor.pos);
}
Err(_) => assert!(expected.is_none()),
}
}
const INPUT: usize = 6;
fn any_cursor(bytes: &[u8; INPUT]) -> Cursor<'_> {
let len: usize = kani::any();
kani::assume(len <= INPUT);
let pos: usize = kani::any();
kani::assume(pos <= len);
Cursor {
buf: &bytes[..len],
pos,
}
}
fn plainly_valid(buf: &[u8], pos: usize) -> Option<usize> {
match buf.get(pos) {
Some(&tags::NULL) => Some(1),
Some(&tags::UINT8) if buf.len() - pos >= 2 => Some(2),
_ => None,
}
}
#[kani::proof]
#[kani::unwind(8)]
fn reading_a_scalar_stays_in_bounds() {
let bytes: [u8; INPUT] = kani::any();
let mut cursor = any_cursor(&bytes);
let (buf, pos) = (cursor.buf, cursor.pos);
let valid = plainly_valid(buf, pos);
match cursor.scalar() {
Ok(scalar) => {
assert!(cursor.pos > pos);
assert!(core::ptr::eq(scalar, &buf[pos..cursor.pos]));
assert!(valid.is_none_or(|n| scalar.len() == n));
}
Err(_) => {
assert!(valid.is_none());
assert!(cursor.pos == pos);
}
}
}
#[kani::proof]
#[kani::unwind(8)]
fn reading_an_object_key_stays_in_bounds() {
let bytes: [u8; INPUT] = kani::any();
let mut cursor = any_cursor(&bytes);
let (buf, pos) = (cursor.buf, cursor.pos);
let left = buf.len() - pos;
let expected = (left >= 4)
.then(|| u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]) as usize)
.filter(|&n| n <= left - 4)
.filter(|&n| utf8(&buf[pos + 4..pos + 4 + n]));
match cursor.key() {
Ok(key) => {
assert!(expected == Some(key.len()));
assert!(core::ptr::eq(key.as_bytes(), &buf[pos + 4..cursor.pos]));
}
Err(_) => assert!(expected.is_none()),
}
}
#[kani::proof]
#[kani::unwind(8)]
fn a_four_byte_object_key_is_read_exactly_when_it_is_utf8() {
let text: [u8; 4] = kani::any();
let mut buf = [4, 0, 0, 0, 0, 0, 0, 0];
buf[4..].copy_from_slice(&text);
let mut cursor = Cursor { buf: &buf, pos: 0 };
assert!(cursor.key().is_ok() == utf8(&text));
}
fn utf8(bytes: &[u8]) -> bool {
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
let (n, low, high) = match b {
0x00..=0x7F => (1, 0, 0),
0xC2..=0xDF => (2, 0x80, 0xBF),
0xE0 => (3, 0xA0, 0xBF),
0xE1..=0xEC | 0xEE..=0xEF => (3, 0x80, 0xBF),
0xED => (3, 0x80, 0x9F),
0xF0 => (4, 0x90, 0xBF),
0xF1..=0xF3 => (4, 0x80, 0xBF),
0xF4 => (4, 0x80, 0x8F),
_ => return false,
};
if i + n > bytes.len() {
return false;
}
if n > 1 && !(low..=high).contains(&bytes[i + 1]) {
return false;
}
if bytes[i + 2.min(n)..i + n]
.iter()
.any(|&c| !(0x80..=0xBF).contains(&c))
{
return false;
}
i += n;
}
true
}
fn a_key_tag(tag: u8) -> bool {
matches!(
tag,
0x0C | 0x0E | 0x04 | 0x05 | 0x10 | 0x0D | 0x0F | 0x06 | 0x07 | 0x11 | 0x0A | 0x0B
)
}
#[kani::proof]
#[kani::unwind(14)]
fn reading_a_map_key_admits_only_its_kinds() {
let bytes: [u8; INPUT] = kani::any();
let mut cursor = any_cursor(&bytes);
let (buf, pos) = (cursor.buf, cursor.pos);
let a_u8 = buf.get(pos) == Some(&tags::UINT8) && buf.len() - pos >= 2;
match cursor.map_key() {
Ok(MapKey(key)) => {
assert!(cursor.pos > pos);
assert!(core::ptr::eq(key, &buf[pos..cursor.pos]));
assert!(a_key_tag(buf[pos]));
}
Err(_) => assert!(!a_u8),
}
}
#[kani::proof]
#[kani::unwind(19)]
fn reading_a_wide_scalar_takes_exactly_its_width() {
let mut bytes: [u8; 17] = kani::any();
for (tag, width) in [
(tags::INT64, 8),
(tags::UINT64, 8),
(tags::FLOAT64, 8),
(tags::INT128, 16),
(tags::UINT128, 16),
] {
bytes[0] = tag;
for len in 0..=bytes.len() {
let buf = &bytes[..len];
let whole = len > width;
let mut cursor = Cursor { buf, pos: 0 };
match cursor.scalar() {
Ok(scalar) => assert!(whole && core::ptr::eq(scalar, &buf[..1 + width])),
Err(_) => assert!(!whole && cursor.pos == 0),
}
let mut cursor = Cursor { buf, pos: 0 };
assert!(cursor.map_key().is_ok() == (whole && tag != tags::FLOAT64));
}
}
}
#[kani::proof]
#[kani::unwind(4)]
fn the_depth_bound_is_128() {
let (bytes, len) = any_bytes::<4>();
for depth in [MAX_DEPTH + 1, MAX_DEPTH + 2, usize::MAX] {
let mut cursor = Cursor {
buf: &bytes[..len],
pos: 0,
};
assert!(node(&mut cursor, depth) == Err(Invalid::Malformed));
assert!(cursor.pos == 0);
}
assert!(MAX_DEPTH == 128);
assert!(
node(
&mut Cursor {
buf: &[tags::NULL],
pos: 0
},
MAX_DEPTH
) == Ok(())
);
}
fn passes_children_down_a_level(container: &[u8]) {
let at = |depth| {
node(
&mut Cursor {
buf: container,
pos: 0,
},
depth,
)
};
assert!(at(MAX_DEPTH - 1) == Ok(()));
assert!(at(MAX_DEPTH) == Err(Invalid::Malformed));
}
#[kani::proof]
#[kani::unwind(4)]
fn a_some_passes_its_value_down_a_level() {
passes_children_down_a_level(&[SOME, tags::NULL]);
}
#[kani::proof]
#[kani::unwind(4)]
fn an_array_passes_its_elements_down_a_level() {
passes_children_down_a_level(&[ARRAY, 1, 0, 0, 0, tags::NULL]);
}
#[kani::proof]
#[kani::unwind(14)]
fn a_map_passes_its_values_down_a_level() {
let map = [MAP, 1, 0, 0, 0, tags::UINT8, 1, tags::NULL];
let mut cursor = Cursor { buf: &map, pos: 0 };
assert!(node(&mut cursor, MAX_DEPTH) == Err(Invalid::Malformed));
}
const INTEGERS: [(u8, usize); 10] = [
(tags::INT8, 1),
(tags::INT16, 2),
(tags::INT32, 4),
(tags::INT64, 8),
(tags::INT128, 16),
(tags::UINT8, 1),
(tags::UINT16, 2),
(tags::UINT32, 4),
(tags::UINT64, 8),
(tags::UINT128, 16),
];
fn any_integer_key((tag, width): (u8, usize)) -> ([u8; 17], usize) {
let payload: [u8; 16] = kani::any();
let mut node = [0; 17];
node[0] = tag;
node[1..=width].copy_from_slice(&payload[..width]);
(node, 1 + width)
}
#[derive(PartialEq, Eq)]
enum Value {
Signed(i128),
Huge(u128),
}
fn value(node: &[u8]) -> Value {
let payload = &node[1..];
let mut word = [0; 16];
word[..payload.len()].copy_from_slice(payload);
let unsigned = u128::from_le_bytes(word);
let signed = |bits: u32| (unsigned << (128 - bits)) as i128 >> (128 - bits);
Value::Signed(match node[0] {
tags::INT8 => signed(8),
tags::INT16 => signed(16),
tags::INT32 => signed(32),
tags::INT64 => signed(64),
tags::INT128 => unsigned as i128,
_ => match i128::try_from(unsigned) {
Ok(v) => v,
Err(_) => return Value::Huge(unsigned),
},
})
}
fn integer_keys_from(first: usize) {
let (a, a_len) = any_integer_key(INTEGERS[first]);
let a = &a[..a_len];
for second in INTEGERS.into_iter().skip(first) {
let (b, b_len) = any_integer_key(second);
let b = &b[..b_len];
let same = MapKey(a).identity() == MapKey(b).identity();
assert!(same == (value(a) == value(b)));
}
}
macro_rules! integer_key_harnesses {
($($name:ident = $first:expr;)*) => {$(
#[kani::proof]
#[kani::unwind(18)]
fn $name() {
integer_keys_from($first);
}
)*};
}
integer_key_harnesses! {
integer_keys_equal_by_value_from_i8 = 0;
integer_keys_equal_by_value_from_i16 = 1;
integer_keys_equal_by_value_from_i32 = 2;
integer_keys_equal_by_value_from_i64 = 3;
integer_keys_equal_by_value_from_i128 = 4;
integer_keys_equal_by_value_from_u8 = 5;
integer_keys_equal_by_value_from_u16 = 6;
integer_keys_equal_by_value_from_u32 = 7;
integer_keys_equal_by_value_from_u64 = 8;
integer_keys_equal_by_value_from_u128 = 9;
}
fn counted(tag: u8, payload: &[u8; 2], len: usize) -> ([u8; 7], usize) {
let mut node = [0; 7];
node[0] = tag;
node[1] = len as u8;
node[5..5 + len].copy_from_slice(&payload[..len]);
(node, 5 + len)
}
#[kani::proof]
#[kani::unwind(8)]
fn text_and_bytes_keys_equal_by_their_bytes() {
let (p, q): ([u8; 2], [u8; 2]) = (kani::any(), kani::any());
for tag in [tags::STRING, tags::BYTES] {
for m in 0..=2 {
for n in 0..=2 {
let (a, a_len) = counted(tag, &p, m);
let (b, b_len) = counted(tag, &q, n);
let same = MapKey(&a[..a_len]).identity() == MapKey(&b[..b_len]).identity();
assert!(same == (p[..m] == q[..n]));
}
}
}
}
#[kani::proof]
#[kani::unwind(18)]
fn keys_of_different_kinds_are_never_one_key() {
let ints = INTEGERS.map(any_integer_key);
let ints = ints
.each_ref()
.map(|(node, len)| MapKey(&node[..*len]).identity());
let payload: [u8; 2] = kani::any();
for len in 0..=payload.len() {
let (text, text_len) = counted(tags::STRING, &payload, len);
let (bytes, bytes_len) = counted(tags::BYTES, &payload, len);
let text = MapKey(&text[..text_len]).identity();
let bytes = MapKey(&bytes[..bytes_len]).identity();
assert!(text != bytes);
for int in &ints {
assert!(text != *int);
assert!(bytes != *int);
}
}
}