#[cfg(any(verus, verus_keep_ghost))]
use vstd::prelude::*;
#[cfg(not(any(verus, verus_keep_ghost)))]
#[doc(hidden)]
macro_rules! verus {
($($tt:tt)*) => {};
}
verus! {
pub open spec fn spec_is_restricted_ipv4(o0: u8, o1: u8, o2: u8, o3: u8) -> bool {
o0 == 0
|| o0 == 10
|| (o0 == 100 && o1 >= 64 && o1 <= 127)
|| o0 == 127
|| (o0 == 169 && o1 == 254)
|| (o0 == 172 && o1 >= 16 && o1 <= 31)
|| (o0 == 192 && o1 == 0 && o2 == 0)
|| (o0 == 192 && o1 == 0 && o2 == 2)
|| (o0 == 192 && o1 == 88 && o2 == 99)
|| (o0 == 192 && o1 == 168)
|| (o0 == 198 && (o1 == 18 || o1 == 19))
|| (o0 == 198 && o1 == 51 && o2 == 100)
|| (o0 == 203 && o1 == 0 && o2 == 113)
|| (o0 >= 224 && o0 <= 239)
|| o0 >= 240
}
pub proof fn proof_rfc1918_10_restricted(o1: u8, o2: u8, o3: u8)
ensures spec_is_restricted_ipv4(10, o1, o2, o3)
{
}
pub proof fn proof_rfc1918_172_restricted(o1: u8, o2: u8, o3: u8)
requires o1 >= 16 && o1 <= 31
ensures spec_is_restricted_ipv4(172, o1, o2, o3)
{
}
pub proof fn proof_rfc1918_192_restricted(o2: u8, o3: u8)
ensures spec_is_restricted_ipv4(192, 168, o2, o3)
{
}
pub proof fn proof_loopback_restricted(o1: u8, o2: u8, o3: u8)
ensures spec_is_restricted_ipv4(127, o1, o2, o3)
{
}
pub proof fn proof_cloud_metadata_restricted()
ensures spec_is_restricted_ipv4(169, 254, 169, 254)
{
}
pub proof fn proof_cgnat_restricted(o1: u8, o2: u8, o3: u8)
requires o1 >= 64 && o1 <= 127
ensures spec_is_restricted_ipv4(100, o1, o2, o3)
{
}
pub proof fn proof_public_ip_not_restricted()
ensures !spec_is_restricted_ipv4(8, 8, 8, 8)
{
}
pub enum VerusStateStatus {
Uninitialized,
Pending { created_at: nat, ttl: nat },
Consumed { consumed_at: nat },
Expired { expired_at: nat },
}
pub struct VerusOAuthStateModel {
pub status: VerusStateStatus,
pub consumption_count: nat,
}
impl VerusOAuthStateModel {
pub open spec fn new() -> Self {
VerusOAuthStateModel {
status: VerusStateStatus::Uninitialized,
consumption_count: 0,
}
}
pub open spec fn insert(self, current_tick: nat, ttl: nat) -> Self {
match self.status {
VerusStateStatus::Uninitialized => VerusOAuthStateModel {
status: VerusStateStatus::Pending { created_at: current_tick, ttl },
consumption_count: self.consumption_count,
},
_ => self,
}
}
pub open spec fn take_state(self, current_tick: nat) -> (Self, bool) {
match self.status {
VerusStateStatus::Pending { created_at, ttl } => {
if current_tick < created_at + ttl {
(
VerusOAuthStateModel {
status: VerusStateStatus::Consumed { consumed_at: current_tick },
consumption_count: self.consumption_count + 1,
},
true,
)
} else {
(
VerusOAuthStateModel {
status: VerusStateStatus::Expired { expired_at: current_tick },
consumption_count: self.consumption_count,
},
false,
)
}
},
_ => (self, false),
}
}
}
pub proof fn theorem_single_use_state_consumption(
s0: VerusOAuthStateModel,
insert_tick: nat,
ttl: nat,
take1_tick: nat,
take2_tick: nat,
)
requires
s0.status == VerusStateStatus::Uninitialized,
s0.consumption_count == 0,
ttl > 0,
take2_tick >= take1_tick,
take1_tick >= insert_tick,
ensures
({
let s1 = s0.insert(insert_tick, ttl);
let (s2, taken1) = s1.take_state(take1_tick);
let (s3, taken2) = s2.take_state(take2_tick);
!taken2
&& s3.consumption_count <= 1
&& (taken1 ==> s2.status == VerusStateStatus::Consumed { consumed_at: take1_tick })
&& (!taken1 ==> s2.status == VerusStateStatus::Expired { expired_at: take1_tick })
})
{
}
pub proof fn lemma_post_consumption_terminality(
s_consumed: VerusOAuthStateModel,
any_tick: nat,
)
requires
s_consumed.status matches VerusStateStatus::Consumed { .. },
ensures
({
let (s_next, taken) = s_consumed.take_state(any_tick);
!taken && s_next == s_consumed
})
{
}
pub open spec fn is_unreserved_ascii(c: u8) -> bool {
(c >= 0x30 && c <= 0x39)
|| (c >= 0x41 && c <= 0x5a)
|| (c >= 0x61 && c <= 0x7a)
|| c == 0x2d
|| c == 0x2e
|| c == 0x5f
|| c == 0x7e
}
pub open spec fn spec_pkce_verifier_valid(len: nat, all_unreserved: bool) -> bool {
len >= 43 && len <= 128 && all_unreserved
}
pub proof fn theorem_pkce_short_verifier_rejected(len: nat, all_unreserved: bool)
requires len < 43
ensures !spec_pkce_verifier_valid(len, all_unreserved)
{
}
pub proof fn theorem_pkce_long_verifier_rejected(len: nat, all_unreserved: bool)
requires len > 128
ensures !spec_pkce_verifier_valid(len, all_unreserved)
{
}
pub proof fn theorem_pkce_invalid_character_rejected(len: nat)
requires len >= 43 && len <= 128
ensures !spec_pkce_verifier_valid(len, false)
{
}
pub proof fn theorem_pkce_valid_bounds_accepted(len: nat)
requires len >= 43 && len <= 128
ensures spec_pkce_verifier_valid(len, true)
{
}
pub open spec fn spec_slices_equal(a: (u8, u8), b: (u8, u8)) -> bool {
a.0 == b.0 && a.1 == b.1
}
pub open spec fn spec_constant_time_diff(a: (u8, u8), b: (u8, u8)) -> u8 {
(a.0 ^ b.0) | (a.1 ^ b.1)
}
proof fn lemma_xor_zero_iff_eq(a: u8, b: u8)
ensures (a ^ b) == 0 <==> a == b
{
assert((a ^ b) == 0 <==> a == b) by(bit_vector);
}
proof fn lemma_or_zero(a: u8, b: u8)
ensures (a | b) == 0 <==> (a == 0 && b == 0)
{
assert((a | b) == 0 <==> (a == 0 && b == 0)) by(bit_vector);
}
proof fn lemma_or_nonzero_left(a: u8, b: u8)
requires a != 0
ensures (a | b) != 0
{
assert((a | b) == 0 <==> (a == 0 && b == 0)) by(bit_vector);
}
pub proof fn theorem_constant_time_eq_soundness(a: (u8, u8), b: (u8, u8))
ensures spec_constant_time_diff(a, b) == 0 <==> spec_slices_equal(a, b)
{
let d0 = a.0 ^ b.0;
let d1 = a.1 ^ b.1;
assert(spec_constant_time_diff(a, b) == (d0 | d1));
lemma_or_zero(d0, d1);
if (d0 | d1) == 0 {
assert(d0 == 0);
assert(d1 == 0);
lemma_xor_zero_iff_eq(a.0, b.0);
lemma_xor_zero_iff_eq(a.1, b.1);
assert(a.0 == b.0 && a.1 == b.1);
assert(spec_slices_equal(a, b));
}
if spec_slices_equal(a, b) {
assert(a.0 == b.0);
assert(a.1 == b.1);
lemma_xor_zero_iff_eq(a.0, b.0);
lemma_xor_zero_iff_eq(a.1, b.1);
assert(d0 == 0);
assert(d1 == 0);
lemma_or_zero(d0, d1);
assert(spec_constant_time_diff(a, b) == 0);
}
}
pub proof fn theorem_constant_time_eq_mismatched_first_octet(a: (u8, u8), b: (u8, u8))
requires a.0 != b.0
ensures !spec_slices_equal(a, b) && spec_constant_time_diff(a, b) != 0
{
lemma_xor_zero_iff_eq(a.0, b.0);
assert(a.0 != b.0);
let d0 = a.0 ^ b.0;
let d1 = a.1 ^ b.1;
assert(spec_constant_time_diff(a, b) == (d0 | d1));
lemma_xor_zero_iff_eq(a.0, b.0);
if d0 == 0 {
lemma_xor_zero_iff_eq(a.0, b.0);
assert(a.0 == b.0);
assert(false);
}
assert(d0 != 0);
lemma_or_nonzero_left(d0, d1);
assert(a.0 != b.0);
assert(!spec_slices_equal(a, b));
}
}