Command: bind
The bind family of commands declares a correspondence between
EasyCrypt objects and their counterparts in the boolean-circuit world
used by the circuit family of tactics.
Four variants are available:
bind bitstring— declares that a type is a fixed-size bitstring, by giving an isomorphism withbool listtogether with conversions from/to (signed and unsigned) integers.bind array— declares that a polymorphic type constructor is a fixed-size array, by giving access/update operators plus an isomorphism withlist. The element type need not be a bitstring at the time of binding, but it must become one whenever the array type is used at a circuit-translated site.bind op— declares that a user-defined operator implements one of a fixed catalog of bitvector/array primitives (corresponding to the operators of the QFABV theory of SMT-LIB). A monomorphic and a multi-type form are available.bind circuit— asserts that a user-defined operator is semantically equivalent to a circuit definition given in the low-level specification (.spec) language. These bindings are trusted and become part of the TCB (see the warning below).
Each variant may be prefixed with the locality modifier local or
global, with the usual section-locality semantics. The default,
inside a section, is to be exported; outside a section the modifier is
ignored.
Variant: bind bitstring
Syntax
bind bitstring to_bits from_bits to_uint to_sint of_int type size .
The seven arguments are: the two halves of the type ↔ bool list
isomorphism (to_bits, from_bits), the two integer projections
(to_uint for the unsigned reading and to_sint for the
two’s-complement signed reading), the integer injection
(of_int), the type being bound, and its size in bits (an integer
formula).
The command leaves seven side conditions to be discharged via
realize (in this example with admit; in practice they should
be proved from the type’s defining equations). The axioms are those of
the abstract BV theory in theories/datatypes/QFABV.ec:
Axiom |
Statement |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
two’s-complement reading: |
|
|
Variant: bind array
Syntax
bind array get set tolist oflist type size .
The six arguments are: read/write operators (get of type
'a t -> int -> 'a and set of type 'a t -> int -> 'a -> 'a
t), the two halves of the type ↔ list isomorphism (tolist,
oflist), the (polymorphic) type constructor being bound, and the
array length.
The command leaves six side conditions to be discharged. The axioms
are those of the abstract A theory in
theories/datatypes/QFABV.ec:
Axiom |
Statement |
|---|---|
|
|
|
|
|
for indices |
|
extensional equality: two arrays are equal iff they agree at every in-range index |
|
|
|
out-of-range reads agree across all arrays (i.e. the value at an out-of-range index is unspecified-but-uniform) |
Variant: bind op
Syntax
bind op type operator " name " .bind op [ type₁ & type₂ & … ] operator " name " .The first (monomorphic) form is for operators whose signature mentions
a single bound type. The second (multi-type) form takes a
&-separated list of types inside brackets and is needed for
operators whose signature mixes types — for instance get (a
bitstring and a single-bit bitstring) or ainit (a bitstring index
type and an array element type).
The name string must be one of the operator catalog below.
The [W8 & bool] syntax names the two bitstring types involved in
get’s signature: the bitvector being indexed, and the single-bit
bitvector holding the extracted bit. Since bool is a primitive
type, it must itself be bind bitstring-bound (to size 1) before
it can appear in a multi-type bind op.
Each bind op leaves a single side condition of the form
bv<name>P, stating the semantics of the operator (e.g. bvxorP
for xor, bvgetP for get). Multi-type bindings may also
leave size-relation side conditions, e.g. le_size (one bitstring
size bounds another), eq1_size (a bitstring has size one). These
all come from the corresponding sub-theories of BVOperators in
theories/datatypes/QFABV.ec.
Only this base catalog of operators needs an explicit binding. Operators built on top of them are translated into circuits by recursive descent through their definitions, applying the bindings at the leaves and composing the resulting sub-circuits.
Operator catalog
The name argument to bind op must be one of the following.
The “Types” column shows the shape of the bracketed type list expected
by the multi-type form (BV = a bitstring type, BV[1] = a
bitstring type of size 1, A = an array type); single-BV
operators may also be given to the monomorphic form.
Arithmetic (one BV argument):
add,sub,mul,oppsigned-wrap arithmetic on bitvectors of size
n.udiv,urem,sdiv,sremunsigned and signed division and remainder.
Bitwise (one BV argument):
and,or,xor,notpointwise boolean operations.
Constant shifts (one BV argument):
shl,shr,ashrshift left, logical shift right, arithmetic (sign-extending) shift right.
rol,rorrotate left, rotate right.
Variable shifts (BV & BV — value, amount):
shls,shrs,ashrsshift left / logical shift right / arithmetic shift right where the shift amount is itself a bitvector.
Comparisons (BV[1] & BV — result and operand):
ult,ule,slt,sleunsigned and signed strict and non-strict ordering, returning a one-bit bitvector.
Size manipulation (BV & BV — source and target sizes):
zextend,sextendzero- and sign-extension to a wider bitvector.
truncatetruncation to a narrower bitvector.
insert,extract,aextractinsert/extract a sub-bitvector;
aextractis the arithmetic-extracting variant.concat(BV & BV & BV)concatenation of two bitvectors.
Bit-level indexing:
init(BV[1] & BV)build a bitvector from a function
int -> bit.get(BV & BV[1])extract a single bit at a given index.
Array primitives:
ainit(BV & A)build an array of bitvectors from a function
int -> BV.asliceget(BV & BV & A),asliceset(BV & BV & A)read/write a sub-bitvector slice spanning array cells.
a2b(BV & BV & A),b2a(BV & BV & A)reshape between a single wide bitvector and an array of bitvectors whose concatenation has the same width.
map(BV & BV & A)pointwise map of a bitvector function over an array of bitvectors.
Variant: bind circuit
Syntax
bind circuit op₁ <- " name₁ " , … , opₖ <- " nameₖ " from " file " .
A non-empty comma-separated list of op <- "name" associations is
followed by a mandatory from "<file>" clause naming the
.spec file from which the named circuit definitions are loaded.
Warning
The equivalences declared by bind circuit are trusted: no
proof obligation is generated, and so an incorrect binding silently
becomes part of the trusted computing base of every proof that
uses it. Use bind op whenever possible. The recommended use of
bind circuit is to attach circuit semantics to operators that
are otherwise abstract, treating the binding as an axiomatisation
rather than as a definition — proofs about the operator are then
discharged via the circuit tactic, which makes use of these
semantics.
A typical use looks like the following (this example is shown as plain text rather than as a checked proof because it depends on a spec file outside the example’s working directory):
require import AllCore List QFABV.
type W8.
op to_bits : W8 -> bool list.
op from_bits : bool list -> W8.
op of_int : int -> W8.
op to_uint : W8 -> int.
op to_sint : W8 -> int.
bind bitstring
to_bits from_bits to_uint to_sint of_int W8 8.
(* ... realize side conditions ... *)
op (+^) : W8 -> W8 -> W8.
bind circuit
(+^) <- "BVXOR_8" from "specs.spec".
The definition of the BVXOR_8 circuit, in the
companion specs.spec file, is:
BVXOR_8(w1@8, w2@8) -> @8 =
xor<8>(w1, w2)
Each op named in the binding list must already be declared, its
arity must match the corresponding spec definition, and every argument
and the return type must be bind bitstring-bound to a bitstring of
the size declared in the spec.