use crate::core::exec::output::*;
use crate::core::exec::{parser::*, serializer::*, ParseError, ParseErrorKind};
use crate::{
combinators::{
bytes::ExactLen, length::AsLen, mapped::spec::FnSpecMapper, Bind, Mapped, Pair, Refined,
Tail, U8,
},
core::{proof::*, spec::*},
};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use vstd::prelude::*;
use OutputBuf;
verus! {
pub struct BitString<'a, const DER: bool = true> {
unused: u8,
bits: &'a [u8],
}
#[cfg(feature = "alloc")]
pub struct BitStringOwned {
unused: u8,
bits: Vec<u8>,
}
#[verifier::ext_equal]
pub struct BitStringSpec {
pub unused: u8,
pub bits: Seq<u8>,
}
impl<'a, const DER: bool> DeepView for BitString<'a, DER> {
type V = BitStringSpec;
closed spec fn deep_view(&self) -> Self::V {
BitStringSpec { unused: self.unused, bits: self.bits.deep_view() }
}
}
#[cfg(feature = "alloc")]
impl DeepView for BitStringOwned {
type V = BitStringSpec;
closed spec fn deep_view(&self) -> Self::V {
BitStringSpec { unused: self.unused, bits: self.bits.deep_view() }
}
}
impl<'a, const DER: bool> BitString<'a, DER> {
#[verifier::type_invariant]
spec fn wf(&self) -> bool {
self.deep_view().wf::<DER>()
}
pub fn new(unused: u8, bits: &'a [u8]) -> (bs: Self)
requires
unused <= 7,
bits.len() == 0 ==> unused == 0,
DER ==> bits.len() > 0 ==> bits@.last().trailing_zeros() >= unused,
ensures
(bs.deep_view() == BitStringSpec { unused, bits: bits.deep_view() }),
{
BitString { unused, bits }
}
pub fn unused(&self) -> (unused: u8)
ensures
unused == self.deep_view().unused,
self.deep_view().wf::<DER>(),
{
proof {
use_type_invariant(self);
}
self.unused
}
pub fn bits(&self) -> (bits: &'a [u8])
ensures
bits.deep_view() == self.deep_view().bits,
{
self.bits
}
}
#[cfg(feature = "alloc")]
impl BitStringOwned {
#[verifier::type_invariant]
spec fn wf(&self) -> bool {
self.deep_view().wf::<false>()
}
pub fn new(unused: u8, bits: Vec<u8>) -> (value: Self)
requires
unused <= 7,
bits.len() == 0 ==> unused == 0,
ensures
value.deep_view() == (BitStringSpec { unused, bits: bits.deep_view() }),
{
Self { unused, bits }
}
pub fn unused(&self) -> (unused: u8)
ensures
unused == self.deep_view().unused,
self.deep_view().wf::<false>(),
{
proof {
use_type_invariant(self);
}
self.unused
}
pub fn bits(&self) -> (bits: &[u8])
ensures
bits.deep_view() == self.deep_view().bits,
{
self.bits.as_slice()
}
}
impl BitStringSpec {
#[verusfmt::skip]
pub open spec fn wf<const DER: bool>(&self) -> bool {
&&& self.unused <= 7
&&& (self.bits.len() == 0 ==> self.unused == 0)
&&& (DER ==> self.bits.len() > 0 ==> self.bits.last().trailing_zeros() >= self.unused)
}
}
type BitStringFmt<const DER: bool> = Mapped<
Refined<Pair<U8, Tail>, PredFnSpec<(u8, Seq<u8>)>>,
FnSpecMapper<(u8, Seq<u8>), BitStringSpec>,
>;
pub(super) open(super) spec fn bitstring_fmt<const DER: bool>() -> BitStringFmt<DER> {
Mapped {
inner: Refined(
Pair(U8, Tail),
|(unused, bits): (u8, Seq<u8>)| BitStringSpec { unused, bits }.wf::<DER>(),
),
mapper: (
|(unused, bits): (u8, Seq<u8>)| BitStringSpec { unused, bits },
|spec: BitStringSpec| (spec.unused, spec.bits),
),
}
}
pub(crate) proof fn lemma_bit_string_fmt_serialization<const DER: bool>(value: BitStringSpec)
ensures
super::BitStringFmt::<DER>.spec_serialize(value) == Pair(U8, Tail).spec_serialize(
(value.unused, value.bits),
),
super::BitStringFmt::<DER>.byte_len(value) == Pair(U8, Tail).byte_len(
(value.unused, value.bits),
),
{
}
mod derived_specs {
use super::*;
use super::super::BitStringFmt;
impl<const DER: bool> SpecParser for BitStringFmt<DER> {
type PVal = BitStringSpec;
open(super) spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
bitstring_fmt::<DER>().spec_parse(ibuf)
}
}
impl<const DER: bool> Consistency for BitStringFmt<DER> {
type Val = BitStringSpec;
open(super) spec fn consistent(&self, v: Self::Val) -> bool {
bitstring_fmt::<DER>().consistent(v)
}
}
impl<const DER: bool> SpecSerializerDps for BitStringFmt<DER> {
type SValue = BitStringSpec;
open(super) spec fn spec_serialize_dps(&self, v: Self::SValue, obuf: Seq<u8>) -> Seq<u8> {
bitstring_fmt::<DER>().spec_serialize_dps(v, obuf)
}
}
impl<const DER: bool> SpecSerializer for BitStringFmt<DER> {
type SVal = BitStringSpec;
open(super) spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
bitstring_fmt::<DER>().spec_serialize(v)
}
}
impl<const DER: bool> SpecByteLen for BitStringFmt<DER> {
type T = BitStringSpec;
open(super) spec fn byte_len(&self, v: Self::T) -> nat {
bitstring_fmt::<DER>().byte_len(v)
}
}
}
mod derived_proofs {
use super::*;
use super::super::BitStringFmt;
impl<const DER: bool> SafeParser for BitStringFmt<DER> {
proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
bitstring_fmt::<DER>().lemma_parse_safe(ibuf);
}
}
impl<const DER: bool> Productive for BitStringFmt<DER> {
proof fn lemma_productive(&self, s: Seq<u8>) {
bitstring_fmt::<DER>().lemma_productive(s);
}
}
impl<const DER: bool> SoundParser for BitStringFmt<DER> {
proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
bitstring_fmt::<DER>().lemma_parse_sound_consumption(ibuf);
}
proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
bitstring_fmt::<DER>().lemma_parse_sound_value(ibuf);
}
}
impl<const DER: bool> GoodSerializer for BitStringFmt<DER> {
proof fn lemma_serialize_len(&self, v: Self::SVal) {
bitstring_fmt::<DER>().lemma_serialize_len(v);
}
}
impl<const DER: bool> SPRoundTripDps for BitStringFmt<DER> {
proof fn theorem_serialize_dps_parse_roundtrip(&self, v: Self::T, obuf: Seq<u8>) {
bitstring_fmt::<DER>().theorem_serialize_dps_parse_roundtrip(v, obuf);
}
}
impl<const DER: bool> NonMalleable for BitStringFmt<DER> {
proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
bitstring_fmt::<DER>().lemma_parse_non_malleable(buf1, buf2);
}
}
impl<const DER: bool> EquivSerializers for BitStringFmt<DER> {
proof fn lemma_serialize_equiv_on_empty(&self, v: Self::SVal) {
bitstring_fmt::<DER>().lemma_serialize_equiv_on_empty(v);
}
}
}
impl<'i, const DER: bool> Parser<&'i [u8]> for super::BitStringFmt<DER> {
type PT = BitString<'i, DER>;
fn parse(&self, ibuf: &&'i [u8]) -> PResult<Self::PT> {
let (n, (unused, bits)): (usize, (u8, &[u8])) = Pair(U8, Tail).parse(ibuf)?;
if unused > 7 {
return Err(ParseError::custom("Invalid number of unused bits in BIT STRING"));
}
if bits.len() == 0 && unused != 0 {
return Err(ParseError::custom("Invalid number of unused bits in BIT STRING"));
}
if DER && bits.len() > 0 && bits[bits.len() - 1].trailing_zeros() < unused as u32 {
return Err(ParseError::custom("Non-canonical encoding of BIT STRING."));
}
Ok((n, BitString::new(unused, bits)))
}
}
impl<Output: OutputBuf, 'i, const DER: bool> Serializer<
Output,
BitString<'i, DER>,
> for super::BitStringFmt<DER> {
fn serialize_into(&self, v: &BitString<'i, DER>, obuf: &mut Output) {
broadcast use crate::core::exec::output::outbuf_lemmas;
U8.serialize_into(&v.unused, obuf);
Tail.serialize_into(&v.bits, obuf);
}
}
impl<'i, const DER: bool> Prepare<BitString<'i, DER>> for super::BitStringFmt<DER> {
fn prepare(&self, v: &BitString<'i, DER>) -> Result<usize, PreSerializeError> {
proof {
use_type_invariant(v);
}
let n1 = U8.prepare(&v.unused)?;
let n2 = Tail.prepare(&v.bits)?;
let total_len = n1.checked_add(n2).ok_or(PreSerializeError::length_too_large())?;
Ok(total_len)
}
}
impl<'i, const DER: bool> ByteLen<BitString<'i, DER>> for super::BitStringFmt<DER> {
fn length(&self, v: &BitString<'i, DER>) -> usize {
let n1 = U8.length(&v.unused);
let n2 = Tail.length(&v.bits);
n1 + n2
}
}
#[cfg(feature = "alloc")]
impl<Output: OutputBuf> Serializer<Output, BitStringOwned> for super::BitStringFmt<false> {
fn serialize_into(&self, v: &BitStringOwned, obuf: &mut Output) {
broadcast use crate::core::exec::output::outbuf_lemmas;
proof {
use_type_invariant(v);
}
U8.serialize_into(&v.unused, obuf);
Tail.serialize_into(&v.bits.as_slice(), obuf);
}
}
#[cfg(feature = "alloc")]
impl Prepare<BitStringOwned> for super::BitStringFmt<false> {
fn prepare(&self, v: &BitStringOwned) -> Result<usize, PreSerializeError> {
proof {
use_type_invariant(v);
}
let n1 = U8.prepare(&v.unused)?;
let n2 = Tail.prepare(&v.bits.as_slice())?;
n1.checked_add(n2).ok_or(PreSerializeError::length_too_large())
}
}
#[cfg(feature = "alloc")]
impl ByteLen<BitStringOwned> for super::BitStringFmt<false> {
fn length(&self, v: &BitStringOwned) -> usize {
U8.length(&v.unused) + Tail.length(&v.bits.as_slice())
}
}
}