Collections for Bend 2, written in stock Bend and benchmarked against optimized C implementations of the same algorithms.
| Container | Module | Notes |
|---|---|---|
| Dynamic array | src/containers/dynamic_array.bend |
packed storage, amortized push |
| Deque | src/containers/deque.bend |
two-list deque |
| FIFO queue | src/containers/queue.bend |
two-list queue |
| Stack | src/containers/stack.bend |
|
| Simple / priority queue | src/containers/{simple,priority}_queue.bend |
facades over queue and heap |
| Binary heap | src/containers/binary_heap.bend |
packed-array min-heap, static comparator |
| Doubly linked list | src/containers/doubly_linked_list.bend |
arena-backed, stable handles |
| Intrusive doubly linked list | src/containers/intrusive_doubly_linked_list.bend |
application-owned nodes, O(1) membership edits; guide; contributed by Ryan Berckmans in #5 |
| List iterator | src/containers/dlist_iterator.bend |
owning bidirectional iterator |
| Tree map | src/containers/balanced_search_tree.bend |
indexed red-black tree |
| Bitset | src/containers/bitset.bend |
packed words |
| Bit list | src/containers/bitlist.bend |
growable packed bits, optional limit (SSZ Bitlist[N]) |
| Hash map | src/containers/hash_table.bend |
String keys, Base.Map-style API |
| LRU cache | src/containers/lru.bend |
String keys, lifetimes, 64-bit metrics |
| SHA-256 | src/crypto/sha/sha256.bend |
FIPS 180-4, from bend-sha256 |
| SHA-256 (packed) | src/crypto/sha/packed/sha256.bend |
the same hash over a packed Array<U32> and a byte length, no list on the path; laws in proofs/crypto/sha/packed/laws.bend |
| Keccak-256 | src/crypto/keccak/keccak.bend |
Ethereum Keccak-256 (MIT), from bend-keccak |
| BLAKE2s | src/crypto/blake/blake2s/blake2s.bend |
RFC 7693, 32-byte digest |
| BLAKE2b | src/crypto/blake/blake2b/blake2b.bend |
RFC 7693, 64-byte digest |
| BLAKE3 | src/crypto/blake/blake3/blake3.bend |
hash mode, 32-byte digest |
| SHA-512 | src/crypto/sha512/sha512.bend |
FIPS 180-4, 64-byte digest; proved equal to spec/crypto/sha512.bend for every input |
| SHA3-256 | src/crypto/sha3/sha3_256.bend |
FIPS 202 on the Keccak-f[1600] of keccak/; proved equal to spec/crypto/sha3.bend |
| Hashing facade | src/crypto/hash.bend |
one-shot sha256/sha512/sha3_256 and an incremental Hasher (new_*, update, digest); see below |
| Constant-time compare | src/crypto/subtle.bend |
eq(a, b) on byte lists (or lists of packed 32-bit words), one pass, proved True exactly when a == b |
| HMAC-SHA256 | src/crypto/mac.bend |
RFC 2104: sign(key, msg), verify(key, msg, tag) (constant-time subtle.eq); proved equal to the RFC/FIPS 198-1 spec spec/crypto/hmac.bend for every input, verify(k, m, sign(k, m)) and rejection of every other tag; RFC 4231 vectors; contracts |
| HKDF-SHA256 | src/crypto/kdf.bend |
RFC 5869: extract, expand, hkdf, lengths above 255 * 32 are the value LengthTooLarge; proved equal to spec/crypto/hkdf.bend for every input, output length and prefix laws; RFC 5869 vectors |
| AES | src/crypto/aes/aes.bend |
FIPS 197 block cipher, 128/192/256-bit keys; S-box as a constant-time Boyar-Peralta circuit (no table); proved equal to the FIPS 197 specification for every key and block, see docs/CRYPTO_CONTRACTS.md |
| AES-GCM | src/crypto/aesgcm.bend |
SP 800-38D AES-128-GCM and AES-256-GCM, 12-byte nonces, 16-byte tags appended, tag checked with subtle.eq; runs a constant-time bitsliced AES (BearSSL aes_ct layout) with a one-pass GHASH, about 7x BearSSL's constant-time C; proved equal to the SP 800-38D specification, decrypt(encrypt(x)) == Some(x), a wrong tag gives None |
| Argon2id | src/crypto/argon2/argon2.bend |
RFC 9106 (v0x13), with secret and associated data; proved equal to spec/crypto/argon2/ for every input that fits (m' <= 2^23 blocks) |
| Password hashing | src/crypto/password.bend |
Argon2id PHC strings: hash_password, hash_password_os, verify_password, needs_rehash; see below |
| ChaCha20 | src/crypto/chacha/chacha20.bend |
RFC 8439: chacha20_block, chacha20(key, counter, nonce, bytes), HChaCha20 and XChaCha20 (draft-irtf-cfrg-xchacha-03); the core src/crypto/chacha/core.bend takes the number of double rounds (ChaCha8/12 reuse it); proved equal to the RFC spec spec/crypto/chacha.bend for every input and round count, decryption is encryption; RFC 8439 / XChaCha vectors; contracts |
| Poly1305 | src/crypto/poly1305/poly1305.bend |
RFC 8439: poly1305(key, msg), verify (constant-time subtle.eq); arithmetic mod 2^130-5 on 5 radix-2^26 natural-number limbs (poly1305-donna / HACL* Field32xN layout), proved equal to the Nat spec spec/crypto/poly1305.bend for every input, and mac_aead equal to the RFC 8439 2.8 tag of mac_data; RFC 8439 vectors |
| AEAD | src/crypto/aead.bend |
encrypt(alg, key, nonce, aad, pt), decrypt(alg, key, nonce, aad, ct) for CHACHA20_POLY1305 (RFC 8439), XCHACHA20_POLY1305, AES_128_GCM and AES_256_GCM (SP 800-38D); proved: each algorithm equals its spec, decrypt(encrypt(x)) == Some(x), every tag other than the expected one is rejected; RFC 8439 / XChaCha / GCM vectors, Wycheproof, differential test against cryptography |
| X25519 key exchange | src/crypto/kex.bend |
RFC 7748: generate_keypair(seed), generate_keypair_os(), shared_secret(sk, pk) (all-zero result rejected); field arithmetic mod 2^255 - 19 in src/crypto/curve25519/; proved equal to spec/crypto/curve25519/x25519.bend |
| Ed25519 signatures | src/crypto/sign.bend |
RFC 8032 section 5.1: generate_keypair, sign, verify (cofactorless, S < L enforced); proved equal to spec/crypto/ed25519.bend |
| secp256k1 | src/crypto/secp256k1.bend |
SEC 1/SEC 2 ECDSA with RFC 6979 deterministic nonces and low-S output (sign, sign_compact, verify, verify_strict), public key recovery (recover), Ethereum eth_address and the ECRECOVER precompile (ecrecover), BIP-340 Schnorr (schnorr_pubkey, schnorr_sign, schnorr_verify), SEC 1 key encodings, generate_keypair(seed) / generate_keypair_os(); complete (Renes-Costello-Batina) point formulas and masked double-and-add; every function proved equal to its specification spec/crypto/secp256k1/ (over the natural numbers) for every input; RFC 6979, BIP-340 (19), Wycheproof and ecrecover vectors plus a differential test against cryptography and the BIP-340 reference code; contracts |
| Integer math | src/math/natural.bend |
Python-style math integer functions, see below |
| Math per type | src/math/generic.bend, src/math/f64.bend |
the same functions for U32, U64, F32 and a software F64, see below |
| Fixed-width integers | src/math/fixed.bend, src/math/number.bend |
Rust's checked_/wrapping_/saturating_/overflowing_ families for U32 and U64, bit counts, primality, bytes, extended gcd, see below |
| Random numbers | src/math/random.bend (src/math/random/) |
Go's math/rand/v2 bit for bit: a Source interface for any seeded generator, ChaCha8 (C2SP chacha8rand) and PCG-DXSM sources, unbiased bounded integers (Lemire), floats, Fisher-Yates shuffles; see below |
| Secure random | src/crypto/random.bend |
ChaCha8Rand generator: seeded or OS-seeded (IO.random_u32), bytes, read_words (bytes into a packed Array<U32>), uint_below, shuffle; contract and caveats |
The hash map and the LRU follow Base's conventions: signatures are
quantity-polymorphic (a, -V: Kind(a), as Base.Map uses), and reads that
copy a value out (get, peek) take -V: Data on the &2 instance, like
Map.get.
src/math/natural.bend is the exact-integer part of a Python-style math
library, after the design reference's roadmap (numeric built-ins and the
math module's integer functions first), on Bend's natural numbers:
| Function | Python | Result |
|---|---|---|
gcd(a, b), gcd_all(xs) |
math.gcd |
greatest common divisor; gcd_all([]) == 0 |
lcm(a, b), lcm_all(xs) |
math.lcm |
least common multiple; lcm_all([]) == 1 |
isqrt(n) |
math.isqrt |
r*r <= n < (r+1)*(r+1) (Heron's iteration) |
iroot(n, k) |
— | r^k <= n < (r+1)^k, Domain for k == 0 |
ilog(n, b) |
— | b^r <= n < b^(r+1), Domain for n == 0 or b < 2 |
factorial(n), perm(n, k), comb(n, k) |
math.factorial/perm/comb |
n!, n!/(n-k)!, C(n, k); 0 when k > n |
prod(xs), sum(xs) |
math.prod, sum |
prod([]) == 1 |
pow_mod(b, e, m) |
pow(b, e, m) |
b^e mod m, ZeroDivision for m == 0 |
mod_inverse(a, m) |
pow(a, -1, m) |
a*x == 1 (mod m), NotInvertible when not coprime |
divmod(a, b) |
divmod |
QR{a // b, a % b}, ZeroDivision for b == 0 |
bit_length(n) |
int.bit_length |
2^(L-1) <= n < 2^L |
clamp(x, lo, hi) |
— | x limited to [lo, hi], Domain for hi < lo |
Errors are values (MathError: ZeroDivision, Domain, NotInvertible),
following the reference's error model. The contract is
spec/math/natural.bend: one <Function>.<clause> proposition per
guarantee (Gcd.greatest, Isqrt.lt_succ, PowMod.zero_modulus, ...;
divisibility is dvd(d, a), a quotient with its equation), and
proofs/math/natural/proof.bend proves every clause under its name
(statement shapes follow Lean 4 Mathlib, the Why3 gallery and HACL*; each
file cites its source; coverage in docs/MATH_CONTRACTS.md), and
tools/check_math.py compares ~5000 random and edge-case calls with
CPython. Elementary float functions (exp, sin, ...), cmath,
fractions, decimal, statistics and random are not included:
exact rationals need unbounded integers, and Bend's native backend stops at
2^48 - 1 per Nat, so results and intermediates of these functions must stay
below that at run time (the proofs are over all naturals). The same
functions for U32, U64, F32 and a software binary64 follow below.
src/math/generic.bend writes the functions above once, as templates over a
numeric interface (src/math/num.bend): a type takes part through two
functions passed as templates, ~op: Op<T> -> T (its arithmetic, one
constructor per operation) and ~test: Test<T> -> Bool (comparisons and
overflow tests). Templates are substituted at compile time and the match on
the operation's constructor is resolved there, so each call compiles to the
instance's native code:
import ./src/math/generic.bend as G
import ./src/math/instances.bend as I
import ./src/math/f64.bend as F64
import ./src/math/u64.bend as W
G.gcd(~U32, ~I.u32_op, ~I.u32_is, 12, 18) # 6
G.comb(~W.U64, ~I.u64_op, ~I.u64_is, n, k) # Done{C(n, k)} or Fail{Overflow}
G.clamp(~F32, ~I.f32_op, ~I.f32_is, x, lo, hi)
G.pow(~F64.F64, ~F64.f64_op, ~F64.f64_is, x, 10n)| Instance | Type | Functions |
|---|---|---|
I.u32_op, I.u32_is |
Base U32 |
all of them |
I.u64_op, I.u64_is |
src/math/u64.bend's two-word U64 (fast arithmetic in src/math/w64.bend) |
all of them |
I.f32_op, I.f32_is |
Base F32 |
min max clamp abs sign sum prod pow |
F64.f64_op, F64.f64_is |
src/math/f64.bend's software binary64 |
min max clamp abs sign sum prod pow |
The integer functions (gcd lcm gcd_all lcm_all isqrt iroot ilog factorial perm comb pow_mod mod_inverse divmod bit_length) need an unsigned integer
instance; the ordered ones (min max clamp abs sign sum prod pow) any
instance. Fixed widths are checked: a result (or a partial product of sum,
prod, lcm_all) that does not fit is Fail{Overflow}, never a wrapped
value (the design reference's rule for fixed-width integers). comb reduces
by a gcd at every step and pow_mod / mod_inverse multiply mod m without
overflow, so they fail only when the true result does not fit. isqrt is
the instance's own: an F32 square-root estimate corrected exactly (U32), plus
one Newton step on 64 bits (U64).
src/math/w64.bend is the U64 arithmetic: only native U32 operations and
Nat values below 2^48 (the runtime's bound), with 64/64 division by a
normalized 32-bit quotient estimate (at most two over, Knuth's Theorem B)
and a 128-bit product for mulmod.
src/math/f64.bend is IEEE-754 binary64 in software on two U32 words
(Bend 2 has only F32): Berkeley SoftFloat 3e's algorithms (addMags,
subMags, f64_mul, roundPackToF64, normRoundPackToF64) on 64-bit
significands, exact long division by 32-bit quotient digits and an integer
square root with one Newton step, each rounded once to nearest-even, with
subnormals, signed zeros, infinities and NaN as IEEE 754; lt le eq,
neg abs copysign, the classification predicates and of_nat; and the
rest of the design reference's float foundation, each result an exact
integer at a scale rounded once: trunc floor ceil round (ties to even),
to_u64 to_u32 floor_u64 ceil_u64 round_u64 of_u64 of_u32 (errors as
values), frexp ldexp ulp, nextafter fmin fmax, is_normal is_subnormal is_integer to_bits of_bits64, modf, exact fmod and IEEE remainder,
isclose and as_integer_ratio. Every clause of spec/math/f64.bend is
proved (proofs/math/typed/f64*.bend).
These instances are stated and tested, and mostly not proved (the Nat
functions above are the proved reference; at U32, abs, min, max,
sign and clamp are proved in proofs/math/typed/u32.bend). Their contracts are in spec/math/:
generic.bend relates every typed function to the proved Nat one
(Gcd.agrees, Comb.checked: the Nat result, or Overflow exactly when it
needs more than the width) and states the float ones through the type's
IEEE operations (FSum.fold, FPow.binary); instances.bend states what
each instance operation computes; w64.bend the 64-bit arithmetic;
f64.bend is an executable binary64 reference in Flocq's style (the exact
result of each operation, rounded once to nearest-even). The evidence:
tools/check_generic.py compares every function on every type with Python
(U32/U64 with overflow detection, F32 through numpy float32, F64 through
Python floats), naming the clause of each case; tools/check_f64.py
compares the software binary64 with the machine's doubles on random bit
patterns of every class (zeros, subnormals, normals, infinities, NaN,
cancellations, ties), and tools/check_f64x.py the rounding, conversion,
exponent, neighbour, remainder and ratio functions with CPython's math
(the design reference's appendix A special cases included);
tools/check_f64_spec.py tests spec/math/f64.bend
itself against the machine's doubles through a line-by-line mirror; and
proofs/math/typed/examples.bend has the proof checker evaluate the integer
clauses and instance laws at concrete U32 inputs. All run in
tools/validate.py; docs/MATH_CONTRACTS.md has the function-by-type
matrix.
src/math/fixed.bend gives U32 and U64 (src/math/u64.bend's two-word
U64) the four families Rust defines for u32/u64 (design reference
section 2.6), plus the integer extras of sections 3.2 and 10;
src/math/number.bend has the Nat versions of the extras:
Functions (u32_ and u64_ prefixed) |
Result |
|---|---|
checked_add sub mul div rem pow shl shr |
Some exact result, None on overflow, a zero divisor, or a shift >= the width |
wrapping_add sub mul pow shl shr |
the result modulo 2^w; shifts by the amount mod w |
saturating_add sub mul pow |
clamped to [0, 2^w - 1] |
overflowing_add sub mul pow shl shr |
OV{wrapping value, overflowed} |
bit_count |
number of 1 bits (int.bit_count, count_ones) |
to_bytes_le/be, from_bytes_le/be |
w/8 bytes as U32s in [0, 256); from_bytes is None for any other length or a value >= 256 |
u32_is_prime, u32_next_prime |
trial division (exact); the least prime above n below 2^32, or None |
NB.bit_count, NB.egcd, NB.is_prime (Nat) |
1 bits; EG{g, x, y, neg} with a x - b y == g (b y - a x when neg), g == gcd(a, b); primality |
Unsigned division never overflows, so Rust's wrapping_, saturating_ and
overflowing_ div/rem are plain division, and div_euclid/rem_euclid
equal div/rem for unsigned types (the remainder is already
non-negative); only checked_div/checked_rem add something (the zero
divisor). is_prime/next_prime at U64 are not provided: the fast
deterministic Miller-Rabin needs the base-set theorem ("bases 2..37 decide
every n < 2^64"), which is a finite computation over all 64-bit composites
and not provable here without an axiom, and exact trial division is too
slow at 64 bits.
Every function is proved against spec/math/fixed.bend and
spec/math/number.bend (77 clauses, gate proofs/math/number/proof.bend:
the checked results are the Nat operation on the values when it fits, the
wrapping ones that result modulo 2^w, a wrapping difference plus b is a plus
2^w exactly when a < b, next_prime returns the least prime above n or
None only when none is left below 2^32, the bytes are the base-256 digits
of the value). tools/check_fixed.py compares every function with Python
integers under Rust's semantics, naming the clause of each case.
src/crypto/hash.bend is the entry point for hashing byte lists
(List<&2, U32>, each value < 256):
import ./src/crypto/hash.bend as Hash
Hash.sha256(bytes) # 32 bytes (FIPS 180-4)
Hash.sha512(bytes) # 64 bytes (FIPS 180-4)
Hash.sha3_256(bytes) # 32 bytes (FIPS 202)
h = Hash.update(Hash.update(Hash.new_sha512(), part1), part2)
Hash.digest(h) # == Hash.sha512(part1 ++ part2)Every function is proved against its standard's executable specification
(spec/crypto/sha.bend, sha512.bend, sha3.bend), and the incremental
Hasher is proved equal to the one-shot hash for every split of the input
(proofs/crypto/hash/laws.bend); src/crypto/subtle.bend's eq compares
digests and tags without an early exit, proved to return True exactly on
equal lists. docs/CRYPTO_CONTRACTS.md lists the clauses and their evidence;
tools/check_crypto_hash.py tests all of it against Python's hashlib.
src/crypto/password.bend stores Argon2id hashes as PHC strings
($argon2id$v=19$m=..,t=..,p=..$salt$hash, unpadded base64), as the
reference implementation and argon2-cffi write them:
import ./src/crypto/password.bend as PW
PW.hash_password(pw, salt, PW.owasp()) # Some{"$argon2id$v=19$m=19456,t=2,p=1$..."}, None if out of range
PW.hash_password_os(pw, PW.rfc9106()) # IO: the same with a fresh 16-byte salt (IO.random_u32)
PW.verify_password(pw, encoded) # recompute and compare with subtle.eq
PW.needs_rehash(encoded, PW.owasp()) # not Argon2id v=19 with exactly these parametersowasp() is m = 19 MiB, t = 2, p = 1; rfc9106() is RFC 9106 section 4's
second recommendation (m = 64 MiB, t = 3, p = 4); both use 16-byte salts and
32-byte tags, and any PW.Params{m, t, p, tag, salt} works. The core,
src/crypto/argon2/argon2.bend's argon2id(pw, salt, key, ad, t, m, p, T),
also takes a secret and associated data. Proved: the implementation equals
the RFC 9106 specification (spec/crypto/argon2/, on the BLAKE2b
specification) for every input whose memory fits (m' <= 2^23 KiB), a hash
returned by hash_password verifies with its password and needs no rehash
with its parameters, and parse(format(x)) == Some{x} for PHC strings
(docs/CRYPTO_CONTRACTS.md). tools/check_argon2.py tests the RFC 9106
section 5.3 vector and compares tags and PHC strings with argon2-cffi.
src/math/random.bend follows Go's math/rand/v2: a generator is a value
built from a seed the caller chooses, and every call returns its value next
to the advanced generator (Bend is pure). The same seed gives Go's exact
sequence (Go's test vectors pass).
import ./src/math/random.bend as R
import ./src/math/random/pcg.bend as PCG
import ./src/math/u64.bend as W
def gen() -> PCG.PCG:
R.pcg(W.U64{1, 0}, W.U64{2, 0}) # Go: rand.New(rand.NewPCG(1, 2))
# a die roll and the advanced generator # Go: r.Uint64N(6)
def roll(g: PCG.PCG) -> W.U64 & PCG.PCG:
R.uint64n(~PCG.PCG, ~R.pcg_next, g, W.U64{6, 0})
def deal(g: PCG.PCG, +cards: List<&2, U32>) -> List<&2, U32> & PCG.PCG:
R.shuffle(~U32, ~PCG.PCG, ~R.pcg_next, g, cards) # Go: r.Shuffle
# shuffle the first n slots of an array in place # Go: r.Shuffle on a slice
def deal_array(g: PCG.PCG, a: Array<U32>, +n: Nat) -> Array<U32> & PCG.PCG:
R.shuffle_array(~U32, ~PCG.PCG, ~R.pcg_next, g, a, n)A source is any state type S with ~next: S -> U64 & S, passed as
templates like src/math/num.bend's ~op: every function of
src/math/random/rand.bend (uint64 uint32 int64 int32 uint64n/uint_below uint32n intn int_range float64 shuffle shuffle_array perm) is written once for all
sources, and so will math/statistics be (a normal(~S, ~next, s) on top of
float64). Sources: R.chacha8(seed) (Go's ChaCha8, C2SP chacha8rand,
a 32-byte seed) and R.pcg(seed1, seed2) (Go's PCG, 128-bit LCG with the
DXSM output). uint64n is Lemire's nearly divisionless method exactly as
Go's (a power of two masks, otherwise multiply and reject while the low half
is below 2^64 mod n), with at most 128 draws where Go loops forever.
Proved for every input (proofs/math/random/proof.bend,
proof_draws.bend, proof_pcg.bend, proof_float.bend;
docs/MATH_CONTRACTS.md): the ChaCha8 generator outputs C2SP's stream for
every key and seed; a PCG step is the 128-bit LCG and the output DXSM on
naturals; uint64n computes the specification's draw and is below n for
every source; Lemire's rejection is exactly unbiased (for every width, bound
and k < n, exactly floor(2^w / n) source outputs draw k); shuffle and
perm return permutations for every source (Mathlib's List.Perm, by
counts), and shuffle_array permutes the array's slots (shuffle itself
goes through an array: linear time, not quadratic); float64 is m 2^-53 exactly and below 1; the word slices and the
bounded wrappers (uint32n, intn, int_range) are what they say. Tested: Go's vectors and a Python mirror of Go on random seeds
and call sequences, plus a chi-square smoke test (tools/check_random.py).
src/crypto/random.bend is the secure generator: ChaCha8Rand keyed from
the OS (from_os(), eight IO.random_u32) or from a secret seed (new),
with bytes (Go's ChaCha8.Read), uint_below and shuffle; proofs and
security caveats in docs/CRYPTO_CONTRACTS.md.
import ./src/crypto/kex.bend as Kex
import ./src/crypto/sign.bend as Sign
Kex.generate_keypair(seed) # Some{Keypair{secret, public}} for 32 bytes
Kex.shared_secret(sk, pk) # X25519(sk, pk), None if all zero or malformed
Sign.generate_keypair(seed) # Ed25519 key pair from a 32-byte seed
Sign.sign(sk, msg) # Some{64-byte signature}
Sign.verify(pk, msg, sig) # True iff valid (S >= L rejected)
Sign.signing_key(seed) # Some{expanded key}: sign_with_key(k, msg) per message
Sign.context(xs) # curve constants and base point, computed once;
# generate_keypair_ctx, signing_key_ctx, verify_ctx take itX25519 (RFC 7748) and Ed25519 (RFC 8032) run on one field implementation
(src/crypto/curve25519/fe.bend, 15 limbs of 17 bits with products on the
32-bit multiplier, every operation proved to compute its value mod p with
bounded limbs). Each facade is proved
equal to a transcription of its RFC (spec/crypto/curve25519/x25519.bend,
spec/crypto/ed25519.bend) for every input, with SHA-512 proved equal to
FIPS 180-4; tools/check_curve25519.py runs the RFC vectors and a
differential test against Python's cryptography. Secret-dependent steps are
branch-free (selection by arithmetic, fixed bit counts); Bend has no timing
model, so constant time is a property of the code's shape, not a theorem.
The library and its laws are published on BendHub (MIT):
| Package | Hash | Contents |
|---|---|---|
[email protected] |
0xd9a2fae439ac7ff9e21e0853948f94fe |
the library (main.bend: every public module) |
[email protected] |
0x993b989cb899a5e5c6facb3d6fbccf8f |
every law: imports the three parts below by name |
[email protected] |
0x5c489f5d9646d7cc9aa3dd8137e9dc07 |
containers, the shared proof library, END_TO_END, PROOF |
[email protected] |
0xf86f5f1d9a594d5a5cff999100e01d03 |
math |
[email protected] |
0xa7e654f9780078ca65bf9e187da99d3e |
crypto and random |
Import any module by its path in the package:
import bend-collections@1.0.0.0/src/containers/hash_table.bend as HashMap
import bend-collections@1.0.0.0/src/crypto/aead.bend as AEAD
import bend-collections@1.0.0.0/src/math/random.bend as Randand the laws the same way, one package's proof root at a time or all at once:
import bend-collections-laws-crypto@1.0.0.0/proofs/crypto/aead/proof.bend as AeadLaws
import bend-collections-laws@1.0.0.0/laws.bend as LawsBendHub caps a package at 16 MiB, so the laws are published in three parts
(laws_containers.bend, laws_math.bend, laws_crypto.bend, one proof root
each, 64 in total) and laws.bend imports the three by name. A name resolves to
a content hash, and every fetched file is checked against it, so an import never
changes under you. Checking all the laws at once takes about 12 GB of memory
and a large stack (ulimit -s unlimited plus
BUN_JSC_maxPerThreadStackUsage=1073741824); checking one part or one root does
not. Version 1.0.0.0 was published from commit 8e660ee.
src/containers/ the collections, their internals (internal/) and API types (types/)
src/math/ integer math (natural.bend), the same per type (num, generic, instances),
software binary64 (f64), 64-bit words (u64, w64), fixed-width U32/U64
families and number theory (fixed, number), hashing, powers of two,
random numbers (random.bend, random/: Go's math/rand/v2)
src/crypto/ SHA-256 (sha/), SHA-512 (sha512/), Keccak-256 (keccak/), SHA3-256 (sha3/),
BLAKE2s, BLAKE2b and BLAKE3 (blake/), the hashing facade (hash.bend),
constant-time comparison (subtle.bend), HMAC and HKDF (mac.bend, kdf.bend),
AES and GCM (aes/, aesgcm.bend), ChaCha20 (chacha/), Poly1305 (poly1305/)
and the AEAD facade (aead.bend, aead/), Argon2id (argon2/) and password
hashing (password.bend), X25519 and Ed25519 (curve25519/, ed25519/,
kex.bend, sign.bend), the secure random generator (random.bend)
spec/ the specifications, mirroring src/: what each module does,
independent of how
containers/<pkg>.bend the abstract model and its contract: every SPARK
formal-container Post clause as a `<Subprogram>.<clause>`
proposition (docs/SPARK_CONTRACTS.md); a spec spanning
several files is a directory with a main.bend
crypto/ FIPS 180-4 SHA-256 and SHA-512, the Keccak sponge, FIPS 202
SHA3-256, RFC 7693 BLAKE2, BLAKE3, list equality (subtle),
FIPS 198-1 HMAC and RFC 5869 HKDF, FIPS 197 AES and SP 800-38D
GCM (aes/: GF(2) polynomials, the cipher, GHASH/GCTR/GCM),
RFC 8439 ChaCha20, Poly1305 and ChaCha20-Poly1305,
XChaCha20(-Poly1305), RFC 9106 Argon2id (argon2/), RFC 7748
X25519 and RFC 8032 Ed25519 (curve25519/, ed25519.bend)
math/<module>.bend each src/math module's contract as `<Function>.<clause>`
propositions (docs/MATH_CONTRACTS.md): proved for
natural, u64, hash and pow2; stated and tested for the
typed versions (generic, instances, w64, f64)
lib/ shared model definitions (lists, the SPARK sequence
predicates, order laws, U32 sequences, the 64-bit word
model)
proofs/ only proofs: one package per src package, mirroring src/,
each importing its spec from spec/:
containers/<pkg>/ the lemmas, the .src sources they expand from, and
proof.bend (the package's entry point: the refinement
theorems and the proof of every contract clause)
math/<pkg>/ the same for src/math: natural/proof.bend proves every
clause of spec/math/natural.bend, math/proof.bend those
of u64, hash and pow2, number/proof.bend those of
number and fixed; typed/examples.bend checks the
typed clauses at concrete U32 inputs
crypto/<pkg>/ the same for src/crypto: sha/ proves SHA-256 equal to its
executable FIPS 180-4 specification for every input,
keccak/ the packed API equal to the independent sponge
specification (padding, absorption, rejection, all words),
sha512/ and sha3/ likewise, hash/ the facade and the
incremental hasher, subtle/ eq, mac/ and kdf/ HMAC and
HKDF, aes/ AES and AES-GCM, chacha/, poly1305/, aead/
equal to RFC 8439 and the AEAD laws, argon2/ Argon2id and
the password facade, curve25519/ and ed25519/ X25519 and
Ed25519, random/ the secure generator; docs/CRYPTO_CONTRACTS.md
lib/ proof library shared by the packages (logic, Nat, lists,
U32 words, arrays, order laws)
PROOF.bend the whole library; END_TO_END.bend the public laws
prove.py checks every proof and every spec
tests/ native test drivers, one per container; oracles in tests/support/
benchmarks/ bend/ and native/ (C) drivers, workload table, runner
tools/ proof generators (mac.py expands the .src proof sources),
validation and differential tests: see tools/README.md
Bend 2.0.34 (the release; pinned in tools/toolchain.json), clang and
Python 3.
bend tests/<container>/main.bend # each container's test driver
bend tests/math/natural.bend -o build/math/natural && python3 tools/check_math.py # math vs CPython
python3 tools/check_crypto_hash.py # subtle, SHA-512, SHA3-256, the hash facade vs hashlib
python3 tools/check_mac.py # HMAC-SHA256 / HKDF-SHA256: RFC 4231 / RFC 5869, hmac, `cryptography`
python3 tools/check_aes.py # AES / AES-GCM: FIPS 197, NIST CAVP GCM vectors, `cryptography`
python3 tools/check_argon2.py # Argon2id (RFC 9106 vector) and PHC strings vs argon2-cffi
python3 tools/check_chacha.py # ChaCha20/XChaCha20/AEADs vs `cryptography`, Wycheproof
python3 tools/check_poly1305.py # Poly1305 and its spec mirror vs `cryptography`
python3 tools/check_curve25519.py # X25519 / Ed25519: RFC 7748 / RFC 8032 vectors, `cryptography`
bend tests/math/random.bend -o build/math/random && python3 tools/check_random.py # random vs Go
python3 tools/backend_diff.py --quick # every module: `bend file.bend` vs the C build vs the JS buildThe proofs are about the Bend source; the compiler underneath them is not
verified. tools/backend_diff.py runs every public module on the same seeded
random and edge-case inputs through the three execution paths of the toolchain
(bend file.bend args, the native C backend, the JavaScript backend) and fails
on any differing line. What it has found is in
docs/BACKEND_BUGS.md; the coverage is in
tools/README.md.
python3 benchmarks/bench.py --build
python3 benchmarks/full_sweep.py # calibrated, every size
python3 benchmarks/natural.py --report build/bench/math.json # src/math/natural.bendEvery container and both hashes run the same algorithm in Bend (native C backend) and in C, with identical inputs and a checksum that must match. Results and the differences to C: BENCHMARK.md.