hashes

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2026 License: MIT Imports: 19 Imported by: 0

README

hashes — Cached PRF-grade hash factories for ITB

See CONSTRUCTIONS.md for the per-primitive construction descriptions. Several wrappers diverge from the canonical RFC / NIST form of the underlying primitive in deliberate, documented ways — the registry names (aescmac, chacha20, blake2b256, ...) are short identifiers, not assertions of conformance with the RFC / NIST specification of the same name. Read CONSTRUCTIONS.md before assuming RFC compatibility.

Drop-in factories that produce itb.HashFunc{128|256|512} closures for the nine PRF-grade primitives ITB ships with as built-in factories for the C / FFI / mobile shared-library distribution.

Every factory pre-keys its primitive once at construction, reuses a sync.Pool of scratch buffers, and is safe to call concurrently from multiple goroutines. Without this caching, per-pixel hashing would re-key the underlying primitive on every call — the dominant cost in ITB's encrypt / decrypt path.

Each factory accepts a variadic optional fixed key:

  • pass nothing → CSPRNG-generated key (returned alongside the closure for the caller to save — required for cross-process persistence);
  • pass a saved key → restore-side reconstruction of the same closure.

SipHash24 is the one exception: its keying material is the per-call seed components themselves, so it has no internal fixed key and no variadic key argument.

Canonical primitives

In FFI-stable index order:

# Name (FFI) Native width itb type
0 areion256 256 HashFunc256 (paired with BatchHashFunc256)
1 areion512 512 HashFunc512 (paired with BatchHashFunc512)
2 siphash24 128 HashFunc128 (uncached — pure function)
3 aescmac 128 HashFunc128 (cached AES-NI block)
4 blake2b256 256 HashFunc256
5 blake2b512 512 HashFunc512
6 blake2s 256 HashFunc256
7 blake3 256 HashFunc256
8 chacha20 256 HashFunc256

The order is FFI-stable; index 0..8 is exposed through ITB_HashName(idx) in the shared library and re-ordering would break the ABI.

Custom user-primitive builders

Beyond the shipped primitives, the package exposes three builder families that wrap a user-supplied PRF into an itb.HashFunc{128|256|512} closure with correct ITB nonce width preservation by construction. These are for "I want to plug in SHA-256 / Ascon-PRF / Camellia-CMAC / My Own Custom hash primitive as the ITB PRF" use cases.

Builder Wraps Use when
BuildCBCMACChainAbsorb{128,256,512} crypto/cipher.Block (caller-keyed) You have a block cipher (AES, Camellia, ARIA, SM4, ...) and want CBC-MAC chain-absorb
BuildSpongeChainAbsorb{128,256,512} Permute + (rate, capacity, fixedKey) You have an unkeyed permutation (Keccak-f, Ascon-PRF, ...) and want a keyed sponge
BuildARXChainAbsorb{128,256,512} Hash256Fn / Hash512Fn (full hash one-shot) You have a full hash function (SHA-256, SM3, SHA-512, ...) and want safe absorption

Why these matter for ITB security. ITB supports nonce widths of 128, 256 or 512 bits via SetNonceBits. The per-call buffer presented to a HashFunc closure carries a domain-tag byte plus the configured nonce material — 20, 36, or 68 bytes for the three nonce widths respectively. Every byte of the data parameter must reach the digest for ITB's advertised nonce strength to hold.

A naive user-written wrapper can silently truncate the ITB nonce in several ways:

  • crypto/sha256.Sum256(data) wrapped naively into HashFunc512 — SHA-256 output is 32 bytes; the upper 32 bytes of any returned [8]uint64 get zero-padded by naive repacking. ChainHash's per-call XOR-chain consumes the full 64-byte intermediate state, so a constant upper half across calls destroys half the entropy.
  • aes.NewCipher(key).Encrypt(iv, plaintext) with ITB nonce as iv — AES IV is 16 bytes regardless of how long the ITB nonce is. SetNonceBits(512) → effective 128-bit nonce. The advertised property is broken silently.
  • chacha20.NewUnauthenticatedCipher(key, nonce) with ITB nonce as nonce — ChaCha20 nonce slot is 12 bytes. Same trap.

The builders sidestep all three traps by construction: the user supplies the primitive in its natural form, the builder absorbs the full data parameter through the appropriate chain pattern, the resulting closure preserves the full ITB nonce width by construction. No caller-side knowledge of the chain-absorb pattern is required.

When the builder is required vs optional

The builders close the silent-truncation trap constructively, but they are not always strictly required. A user primitive is safely pluggable as a hand-written closure without a builder when both of these hold:

  1. The primitive has native variable-length absorb (Merkle-Damgard tree like BLAKE3, MD chaining like SHA-256/512, sponge with internal absorb loop like Keccak/Ascon — i.e. the primitive's own API accepts arbitrary input length and processes every byte).
  2. The primitive's native output width is at least the required HashFunc width (32 bytes for HashFunc256, 64 bytes for HashFunc512).

The existing custom-factory pattern in the main repo README — "Custom factory pattern (advanced)" is the canonical reference for this case: BLAKE3 via blake3.NewKeyed + h.Write(mixed) satisfies both conditions, so all four seed components are XOR'd into a zero-padded data buffer that BLAKE3 absorbs natively. No chain-absorb needed. The same pattern transfers to BLAKE2b/2s, SHA-256 (for HashFunc256), SHA-512 (for HashFunc512), KangarooTwelve, etc.

A user primitive requires a builder when at least one of these holds:

  1. Output-width upscaling: primitive native output is narrower than the required HashFunc width. SHA-256 (32 bytes) → HashFunc512 (64 bytes) is the classic trap — naive zero-padding of the upper half destroys half the intermediate-state entropy. The builder calls the underlying hash twice with domain separation to fill the full output width safely.
  2. No native variable-length absorb: primitive only handles fixed-width input in isolation. Raw cipher.Block (16-byte block), raw permutation function (320-bit Ascon-p state), block cipher used as a primitive rather than via a higher-level AEAD wrapper. The CBC-MAC or sponge builder constructs the chain externally so all input bytes reach the digest.
  3. Defence against caller-side mistakes: even when (1) and (2) of the "safe handwritten" conditions hold, a builder removes the opportunity to forget seed-component XOR or output-width matching. Useful for casual users / quick experiments / audit-friendly code.
Scenario Builder required? Why
BLAKE3 → HashFunc256 (handwritten via Write) No Native variable-length absorb + native output 32 B matches
BLAKE2b → HashFunc{256,512} (handwritten via Sum) No Same — native variable-length absorb, output width matches
SHA-256 → HashFunc256 No (but builder simplifies) Native MD absorb + 32 B output matches; builder still removes seed-injection responsibility
SHA-256 → HashFunc512 Yes Output-width upscaling — without builder, upper 32 B silently zero-padded
SHA-512 → HashFunc512 No (but builder simplifies) Native MD absorb + 64 B output matches
AES block cipher → HashFunc{128,256,512} Yes Raw block cipher has no native variable-length absorb
Ascon-p / Keccak-f (raw permutation) → HashFunc{128,256,512} Yes Raw permutation has no native variable-length absorb; sponge wrapper needed externally
Camellia / SM4 / ARIA block cipher Yes Same as AES — raw block cipher needs CBC-MAC chain
Example — SHA-256 via the ARX builder
import (
    "crypto/rand"
    "crypto/sha256"

    "github.com/everanium/itb"
    "github.com/everanium/itb/hashes"
)

func main() {
    // Long-lived fixed key (persist alongside ITB seeds for cross-process restore).
    var fixedKey [32]byte
    if _, err := rand.Read(fixedKey[:]); err != nil {
        panic(err)
    }

    // SHA-256 wrapped safely. The builder folds {fixedKey, seed,
    // length, domain} into the absorb buffer so the full ITB nonce
    // (up to 64 bytes for SetNonceBits(512)) reaches the digest.
    sha256Hash := hashes.BuildARXChainAbsorb256(sha256.Sum256, fixedKey[:])

    // Use exactly like a built-in primitive — three independent seed
    // instances (one per ITB role: noise / data / start).
    itb.SetNonceBits(512)
    itb.SetBarrierFill(4)

    noiseSeed, _ := itb.NewSeed256(1024, sha256Hash)
    dataSeed,  _ := itb.NewSeed256(1024, sha256Hash)
    startSeed, _ := itb.NewSeed256(1024, sha256Hash)

    plaintext := []byte("hello SHA-256 via ITB builder")
    ct, _ := itb.Encrypt256(noiseSeed, dataSeed, startSeed, plaintext)
    pt, _ := itb.Decrypt256(noiseSeed, dataSeed, startSeed, ct)

    _ = pt // round-trip; bit-exact recovery of plaintext.
}

The same pattern works for any 32-byte hash. SM3 swap-in: substitute sha256.Sum256 with func(d []byte) [32]byte { return sm3.Sum(d) } (using any SM3 implementation that exposes a one-shot 32-byte digest). For 64-byte digests like SHA-512, use BuildARXChainAbsorb512(sha512.Sum512, fixedKey[:]).

Performance note for builders

Builders dispatch through interface callbacks (cipher.Block.Encrypt) and []byte state buffers, costing 5-15% throughput vs the inline per-primitive closures shipped here (aescmac.go, chacha20.go, ...). The built-in closures use stack-allocated fixed-size state arrays (var state [32]byte), inlined permutation calls, and unsafe.Pointer escape-analysis tricks. The builders are intentionally simpler — they target correctness-by-construction for user primitives, not peak throughput. If you need both correctness and peak throughput for a specific primitive, write a dedicated closure following the hashes/*.go patterns.

BatchHash (4-pixel batched ZMM-asm) is not provided by these builders — the batched arm is inherently primitive-specific (VAES for AES, multi-buffer SHA-NI for SHA-256, etc.) and cannot be templated. Seeds constructed from builder closures leave BatchHash = nil, which makes ITB fall back silently to the per-pixel scalar loop (process_generic.go). Correctness is preserved; throughput on AVX-512 hosts is left on the table.

Usage

Native Go API — generate fresh random keys, persist the encryptor material via itb.Blob{128,256,512}.

Each Blob type packs every seed's hash key + components plus the optional dedicated lockSeed and the captured itb.Set* globals into one self-describing JSON blob; ship the resulting blob_bytes alongside the ciphertext (or out-of-band) and rebuild on the receiver via Blob{N}.Import followed by per-slot factory rewiring.

Areion-SoEM has paired (single, batched, fixedKey) constructors so the AVX-512 batched dispatch path is reachable:


// Sender

import (
	"fmt"
	"github.com/everanium/itb"
	"github.com/everanium/itb/hashes"
)

func main() {

	itb.SetMaxWorkers(8)    // limit to 8 CPU cores (default: all CPUs)
	itb.SetNonceBits(512)   // 512-bit nonce (default: 128-bit)
	itb.SetBarrierFill(4)   // CSPRNG fill margin (default: 1, valid: 1,2,4,8,16,32)

	itb.SetBitSoup(1)       // optional bit-level split ("bit-soup"; default: 0 = byte-level)
	                        // automatically enabled for Single Ouroboros if
	                        // itb.SetLockSoup(1) is enabled or vice versa

	itb.SetLockSoup(1)      // optional Insane Interlocked Mode: per-chunk PRF-keyed
	                        // bit-permutation overlay on top of bit-soup;
	                        // automatically enabled for Single Ouroboros if
	                        // itb.SetBitSoup(1) is enabled or vice versa

	// Four independent CSPRNG-keyed Areion-SoEM-512 paired closures
	// (3 main seeds + 1 optional dedicated lockSeed). Each Pair
	// returns (single, batched, [64]byte fixedKey).
	fnN, batchN, keyN := hashes.Areion512Pair() // random noise hash key generated
	fnD, batchD, keyD := hashes.Areion512Pair() // random data hash key generated
	fnS, batchS, keyS := hashes.Areion512Pair() // random start hash key generated
	fnL, batchL, keyL := hashes.Areion512Pair() // random lock hash key generated
	//fnN, batchN := hashes.Areion512PairWithKey(keyN) // [64]byte key
	//fnD, batchD := hashes.Areion512PairWithKey(keyD) // [64]byte key
	//fnS, batchS := hashes.Areion512PairWithKey(keyS) // [64]byte key
	//fnL, batchL := hashes.Areion512PairWithKey(keyL) // [64]byte key

	ns, _ := itb.NewSeed512(2048, fnN); ns.BatchHash = batchN // random noise CSPRNG seeds, batch enabled
	ds, _ := itb.NewSeed512(2048, fnD); ds.BatchHash = batchD // random data CSPRNG seeds, batch enabled
	ss, _ := itb.NewSeed512(2048, fnS); ss.BatchHash = batchS // random start CSPRNG seeds, batch enabled
	ls, _ := itb.NewSeed512(2048, fnL); ls.BatchHash = batchL // random lock CSPRNG seeds, batch enabled

	// Optional: dedicated lockSeed for the bit-permutation derivation
	// channel. Engages bit_soup or lock_soup before first encrypt
	// (both already on above).
	ns.AttachLockSeed(ls)

	plaintext := []byte("any text or binary data - including 0x00 bytes")

	// Encrypt into RGBWYOPA container
	encrypted, err := itb.Encrypt512(ns, ds, ss, plaintext)
	if err != nil {
        	panic(err)
	}
	fmt.Printf("encrypted: %d bytes\n", len(encrypted))

	// Cross-process persistence — Blob512 packs every seed's
	// [64]byte hash key + Components ([]uint64) plus the optional
	// dedicated lockSeed and the captured itb.Set* globals into
	// one self-describing JSON blob.
	bSrc := &itb.Blob512{}
	blob, _ := bSrc.Export(keyN, keyD, keyS, ns, ds, ss,
		itb.Blob512Opts{KeyL: keyL, LS: ls})

	// Send encrypted payload + blob

}

// Receiver

import (
	"fmt"
	"github.com/everanium/itb"
	"github.com/everanium/itb/hashes"
)

func main() {

	itb.SetMaxWorkers(8)    // limit to 8 CPU cores (default: all CPUs)

	// Receive encrypted payload + blob
	// encrypted := ...; blob := ...

	// Blob512.Import restores per-slot hash keys + Components AND
	// applies the captured globals (NonceBits / BarrierFill /
	// BitSoup / LockSoup) via the process-wide setters. Hash /
	// BatchHash on each restored seed stay nil so the caller wires
	// them from the saved Key* bytes through the matching factory.
	bDst := &itb.Blob512{}
	_ = bDst.Import(blob)

	fnN, batchN := hashes.Areion512PairWithKey(bDst.KeyN)
	fnD, batchD := hashes.Areion512PairWithKey(bDst.KeyD)
	fnS, batchS := hashes.Areion512PairWithKey(bDst.KeyS)
	fnL, batchL := hashes.Areion512PairWithKey(bDst.KeyL)
	bDst.NS.Hash, bDst.NS.BatchHash = fnN, batchN
	bDst.DS.Hash, bDst.DS.BatchHash = fnD, batchD
	bDst.SS.Hash, bDst.SS.BatchHash = fnS, batchS
	bDst.LS.Hash, bDst.LS.BatchHash = fnL, batchL
	bDst.NS.AttachLockSeed(bDst.LS)

	// Decrypt from RGBWYOPA container
	decrypted, err := itb.Decrypt512(bDst.NS, bDst.DS, bDst.SS, encrypted)
	if err != nil {
        	panic(err)
	}
	fmt.Printf("decrypted: %d bytes\n", len(decrypted))

}

BLAKE2b-512 has paired (single, batched, fixedKey) constructors so the AVX-512 ZMM-batched chain-absorb dispatch path is reachable:


// Sender

import (
	"fmt"
	"github.com/everanium/itb"
	"github.com/everanium/itb/hashes"
)

func main() {

	itb.SetMaxWorkers(8)    // limit to 8 CPU cores (default: all CPUs)
	itb.SetNonceBits(512)   // 512-bit nonce (default: 128-bit)
	itb.SetBarrierFill(4)   // CSPRNG fill margin (default: 1, valid: 1,2,4,8,16,32)

	itb.SetBitSoup(1)       // optional bit-level split ("bit-soup"; default: 0 = byte-level)
	                        // automatically enabled for Single Ouroboros if
	                        // itb.SetLockSoup(1) is enabled or vice versa

	itb.SetLockSoup(1)      // optional Insane Interlocked Mode: per-chunk PRF-keyed
	                        // bit-permutation overlay on top of bit-soup;
	                        // automatically enabled for Single Ouroboros if
	                        // itb.SetBitSoup(1) is enabled or vice versa

	// Four independent CSPRNG-keyed BLAKE2b-512 paired closures
	// (3 main seeds + 1 optional dedicated lockSeed). Each Pair
	// returns (single, batched, [64]byte fixedKey).
	fnN, batchN, keyN := hashes.BLAKE2b512Pair() // random noise hash key generated
	fnD, batchD, keyD := hashes.BLAKE2b512Pair() // random data hash key generated
	fnS, batchS, keyS := hashes.BLAKE2b512Pair() // random start hash key generated
	fnL, batchL, keyL := hashes.BLAKE2b512Pair() // random lock hash key generated
	//fnN, batchN := hashes.BLAKE2b512PairWithKey(keyN) // [64]byte key
	//fnD, batchD := hashes.BLAKE2b512PairWithKey(keyD) // [64]byte key
	//fnS, batchS := hashes.BLAKE2b512PairWithKey(keyS) // [64]byte key
	//fnL, batchL := hashes.BLAKE2b512PairWithKey(keyL) // [64]byte key

	ns, _ := itb.NewSeed512(2048, fnN); ns.BatchHash = batchN // random noise CSPRNG seeds, batch enabled
	ds, _ := itb.NewSeed512(2048, fnD); ds.BatchHash = batchD // random data CSPRNG seeds, batch enabled
	ss, _ := itb.NewSeed512(2048, fnS); ss.BatchHash = batchS // random start CSPRNG seeds, batch enabled
	ls, _ := itb.NewSeed512(2048, fnL); ls.BatchHash = batchL // random lock CSPRNG seeds, batch enabled

	// Optional: dedicated lockSeed for the bit-permutation derivation
	// channel — same flow as the Areion-SoEM-512 example above.
	ns.AttachLockSeed(ls)

	plaintext := []byte("any text or binary data - including 0x00 bytes")

	// Encrypt into RGBWYOPA container
	encrypted, err := itb.Encrypt512(ns, ds, ss, plaintext)
	if err != nil {
        	panic(err)
	}
	fmt.Printf("encrypted: %d bytes\n", len(encrypted))

	// Cross-process persistence — Blob512 packs every seed's
	// [64]byte hash key + Components plus the optional dedicated
	// lockSeed and the captured itb.Set* globals into one
	// self-describing JSON blob.
	bSrc := &itb.Blob512{}
	blob, _ := bSrc.Export(keyN, keyD, keyS, ns, ds, ss,
		itb.Blob512Opts{KeyL: keyL, LS: ls})

	// Send encrypted payload + blob

}

// Receiver

import (
	"fmt"
	"github.com/everanium/itb"
	"github.com/everanium/itb/hashes"
)

func main() {

	itb.SetMaxWorkers(8)    // limit to 8 CPU cores (default: all CPUs)

	// Receive encrypted payload + blob
	// encrypted := ...; blob := ...

	// Blob512.Import restores per-slot hash keys + Components AND
	// applies the captured globals via the process-wide setters.
	// Hash / BatchHash on each restored seed stay nil so the caller
	// wires them from the saved Key* bytes through the matching
	// factory.
	bDst := &itb.Blob512{}
	_ = bDst.Import(blob)

	fnN, batchN := hashes.BLAKE2b512PairWithKey(bDst.KeyN)
	fnD, batchD := hashes.BLAKE2b512PairWithKey(bDst.KeyD)
	fnS, batchS := hashes.BLAKE2b512PairWithKey(bDst.KeyS)
	fnL, batchL := hashes.BLAKE2b512PairWithKey(bDst.KeyL)
	bDst.NS.Hash, bDst.NS.BatchHash = fnN, batchN
	bDst.DS.Hash, bDst.DS.BatchHash = fnD, batchD
	bDst.SS.Hash, bDst.SS.BatchHash = fnS, batchS
	bDst.LS.Hash, bDst.LS.BatchHash = fnL, batchL
	bDst.NS.AttachLockSeed(bDst.LS)

	// Decrypt from RGBWYOPA container
	decrypted, err := itb.Decrypt512(bDst.NS, bDst.DS, bDst.SS, encrypted)
	if err != nil {
        	panic(err)
	}
	fmt.Printf("decrypted: %d bytes\n", len(decrypted))

}

SipHash-2-4 has no internal fixed key — paired (single, batched) constructor returns a 2-tuple without a key element:

import (
	"fmt"
	"github.com/everanium/itb"
	"github.com/everanium/itb/hashes"
)

func main() {

	itb.SetMaxWorkers(8)    // limit to 8 CPU cores (default: all CPUs)

	fnN, batchN := hashes.SipHash24Pair()
	fnD, batchD := hashes.SipHash24Pair()
	fnS, batchS := hashes.SipHash24Pair()
	fnL, batchL := hashes.SipHash24Pair()

	ns, _ := itb.NewSeed128(1024, fnN); ns.BatchHash = batchN
	ds, _ := itb.NewSeed128(1024, fnD); ds.BatchHash = batchD
	ss, _ := itb.NewSeed128(1024, fnS); ss.BatchHash = batchS
	ls, _ := itb.NewSeed128(1024, fnL); ls.BatchHash = batchL
	ns.AttachLockSeed(ls)

	plaintext := []byte("any text or binary data - including 0x00 bytes")
	encrypted, _ := itb.Encrypt128(ns, ds, ss, plaintext)

	// Cross-process persistence — Blob128 packs every seed's
	// Components ([]uint64; SipHash-2-4 has no fixed PRF key, so
	// KeyN/KeyD/KeyS/KeyL stay nil) plus the optional dedicated
	// lockSeed and the captured itb.Set* globals.
	bSrc := &itb.Blob128{}
	blob, _ := bSrc.Export(nil, nil, nil, ns, ds, ss,
	    itb.Blob128Opts{LS: ls})

	// Receiver — Import + factory rewiring (SipHash-2-4 has no fixed
	// key to thread through the factory; one fresh SipHash24Pair() per
	// slot).
	bDst := &itb.Blob128{}
	_ = bDst.Import(blob)
	fnN2, batchN2 := hashes.SipHash24Pair()
	fnD2, batchD2 := hashes.SipHash24Pair()
	fnS2, batchS2 := hashes.SipHash24Pair()
	fnL2, batchL2 := hashes.SipHash24Pair()
	bDst.NS.Hash, bDst.NS.BatchHash = fnN2, batchN2
	bDst.DS.Hash, bDst.DS.BatchHash = fnD2, batchD2
	bDst.SS.Hash, bDst.SS.BatchHash = fnS2, batchS2
	bDst.LS.Hash, bDst.LS.BatchHash = fnL2, batchL2
	bDst.NS.AttachLockSeed(bDst.LS)

	decrypted, _ := itb.Decrypt128(bDst.NS, bDst.DS, bDst.SS, encrypted)
	_ = decrypted

}

Name-keyed dispatch (used by the FFI layer; works for any code that selects the primitive at runtime). Same variadic key pattern, but key is []byte (size validated against the primitive's native length):

fn, hashKey, _ := hashes.Make256("blake3") // random
fn, _, _       := hashes.Make256("blake3", saved) // explicit

Easy Mode — Quick start

easy.New (Single Ouroboros) and easy.New3 (Triple Ouroboros) build a high-level *easy.Encryptor around a single hash primitive chosen from the canonical list above. The constructor allocates its own seed material + MAC closure, snapshots the global ITB configuration into a per-instance *itb.Config, and exposes setters that mutate only its own state — two encryptors with different settings can run concurrently without cross- contamination. The state blob carries the PRF fixed key, seed components, MAC key, and (when active) the dedicated lockSeed material; the receiver constructs a matching encryptor with the same (primitive, key_bits, mac, mode) and calls Import to restore.


// Sender

package main

import (
	"fmt"
	"github.com/everanium/itb"
	"github.com/everanium/itb/easy"
)

func main() {
	itb.SetMaxWorkers(8)    // limit to 8 CPU cores (default: all CPUs)

	// Single-Ouroboros (3 seeds) constructor — variadic args by type:
	// string matching hashes.Registry → primitive, string matching
	// macs.Registry → MAC, int → key_bits. Defaults: "areion512" /
	// 1024 / "hmac-blake3". Triple Ouroboros (7 seeds) → easy.New3(...).
	enc := easy.New("blake3", 1024, "hmac-blake3")
	defer enc.Close()

	// Per-instance configuration.
	enc.SetNonceBits(256)   // 256-bit nonce (default: 128-bit)
	enc.SetBarrierFill(4)   // CSPRNG fill margin (default: 1, valid: 1, 2, 4, 8, 16, 32)

	//enc.SetLockSeed(1)    // optional dedicated lockSeed for the bit-permutation
	                        // derivation channel — separates that PRF's keying material
	                        // from the noiseSeed-driven noise-injection channel; auto-
	                        // couples SetLockSoup(1) + SetBitSoup(1). Adds one extra
	                        // seed slot (3 → 4 for Single, 7 → 8 for Triple). Must be
	                        // called BEFORE the first Encrypt — switching mid-session
	                        // panics with easy.ErrLockSeedAfterEncrypt.

	// For cross-process persistence: enc.Export() returns a single
	// JSON blob carrying PRF keys, seed components, MAC key, and
	// (when active) the dedicated lockSeed material.
	blob := enc.Export()
	fmt.Printf("state blob: %d bytes\n", len(blob))
	fmt.Printf("primitive: %s, key_bits: %d, mode: %d, mac: %s\n",
		enc.Primitive, enc.KeyBits, enc.Mode, enc.MACName)

	plaintext := []byte("any text or binary data - including 0x00 bytes")

	// Authenticated encrypt — 32-byte tag is computed across the
	// entire decrypted capacity and embedded inside the RGBWYOPA
	// container, preserving oracle-free deniability.
	encrypted, err := enc.EncryptAuth(plaintext)
	if err != nil {
		panic(err)
	}
	fmt.Printf("encrypted: %d bytes\n", len(encrypted))

	// Send encrypted payload + state blob

}

// Receiver

package main

import (
	"fmt"
	"github.com/everanium/itb"
	"github.com/everanium/itb/easy"
)

func main() {
	itb.SetMaxWorkers(8)    // limit to 8 CPU cores (default: all CPUs)

	// Receive encrypted payload + state blob
	// var encrypted, blob []byte = ..., ...

	// Optional: peek at the blob's metadata before constructing a
	// matching encryptor. Useful when the receiver multiplexes blobs
	// of different shapes (different primitive / mode / MAC choices).
	prim, keyBits, mode, mac := easy.PeekConfig(blob)
	fmt.Printf("peek: primitive=%s, key_bits=%d, mode=%d, mac=%s\n",
		prim, keyBits, mode, mac)

	var dec *easy.Encryptor
	if mode == 1 {
		dec = easy.New(prim, keyBits, mac)
	} else {
		dec = easy.New3(prim, keyBits, mac)
	}
	defer dec.Close()

	// Restore PRF keys, seed components, MAC key, and the per-instance
	// configuration overrides from the saved blob.
	if err := dec.Import(blob); err != nil {
		panic(err)
	}

	decrypted, err := dec.DecryptAuth(encrypted)
	if err != nil {
		panic(err)
	}
	fmt.Printf("decrypted: %s\n", string(decrypted))

}

Easy Mode — Mixed primitives (different PRF per seed slot)

easy.NewMixed and easy.NewMixed3 accept a per-slot primitive spec, allowing the noise / data / start (and optional dedicated lockSeed) seed slots to use different PRF primitives within the same native hash width. The mix-and-match-PRF freedom of the lower-level path, surfaced through Easy Mode without forcing the caller off the high-level constructor. The state blob carries per-slot primitives + per-slot PRF keys; the receiver constructs a matching encryptor with the same spec and calls Import to restore.


// Sender

package main

import (
	"fmt"
	"github.com/everanium/itb"
	"github.com/everanium/itb/easy"
)

func main() {
	itb.SetMaxWorkers(8)    // limit to 8 CPU cores (default: all CPUs)

	// Per-slot primitive selection (Single Ouroboros, 3 + 1 slots).
	// Every name must share the same native hash width — mixing
	// widths is rejected at construction with easy.ErrEasyMixedWidth.
	// Triple Ouroboros mirror — easy.NewMixed3(easy.MixedSpec3{...})
	// takes seven per-slot names (noise + 3 data + 3 start) plus
	// the optional PrimitiveL lockSeed.
	enc := easy.NewMixed(easy.MixedSpec{
		PrimitiveN: "blake3",      // noiseSeed:  BLAKE3
		PrimitiveD: "blake2s",     // dataSeed:   BLAKE2s
		PrimitiveS: "areion256",   // startSeed:  Areion-SoEM-256
		PrimitiveL: "blake2b256",  // dedicated lockSeed (optional;
		                           // empty = no lockSeed slot)
		KeyBits:    1024,
		MACName:    "hmac-blake3",
	})
	defer enc.Close()

	// Per-instance configuration applies as for easy.New.
	enc.SetNonceBits(512)
	enc.SetBarrierFill(4)
	// BitSoup + LockSoup are auto-coupled on the on-direction by
	// PrimitiveL above; explicit calls below are unnecessary but
	// harmless if added.
	//enc.SetBitSoup(1)
	//enc.SetLockSoup(1)

	// Per-slot introspection — Primitive returns the "mixed"
	// literal, PrimitiveAt(slot) returns each slot's name,
	// IsMixed() is the typed predicate.
	fmt.Printf("mixed=%v primitive=%s\n", enc.IsMixed(), enc.Primitive)
	for i := 0; i < 4; i++ {
		fmt.Printf("  slot %d: %s\n", i, enc.PrimitiveAt(i))
	}

	blob := enc.Export()
	fmt.Printf("state blob: %d bytes\n", len(blob))

	plaintext := []byte("mixed-primitive Easy Mode payload")

	// Authenticated encrypt — 32-byte tag is computed across the
	// entire decrypted capacity and embedded inside the RGBWYOPA
	// container, preserving oracle-free deniability.
	encrypted, err := enc.EncryptAuth(plaintext)
	if err != nil {
		panic(err)
	}
	fmt.Printf("encrypted: %d bytes\n", len(encrypted))

	// Send encrypted payload + state blob

}

// Receiver

package main

import (
	"fmt"
	"github.com/everanium/itb"
	"github.com/everanium/itb/easy"
)

func main() {
	itb.SetMaxWorkers(8)    // limit to 8 CPU cores (default: all CPUs)

	// Receive encrypted payload + state blob
	// var encrypted, blob []byte = ..., ...

	// Receiver constructs a matching mixed encryptor — every per-
	// slot primitive name plus key_bits and mac must agree with the
	// sender. Import validates each per-slot primitive against the
	// receiver's bound spec; mismatches surface as
	// easy.ErrMismatch{Field: "primitive"}.
	dec := easy.NewMixed(easy.MixedSpec{
		PrimitiveN: "blake3",
		PrimitiveD: "blake2s",
		PrimitiveS: "areion256",
		PrimitiveL: "blake2b256",
		KeyBits:    1024,
		MACName:    "hmac-blake3",
	})
	defer dec.Close()

	// Restore PRF keys, seed components, MAC key, and the per-
	// instance configuration overrides from the saved blob. Mixed
	// blobs carry mixed:true plus a primitives array; Import on a
	// single-primitive receiver (or vice versa) is rejected as a
	// primitive mismatch.
	if err := dec.Import(blob); err != nil {
		panic(err)
	}

	decrypted, err := dec.DecryptAuth(encrypted)
	if err != nil {
		panic(err)
	}
	fmt.Printf("decrypted: %s\n", string(decrypted))

}

Keyed variants

Every cached factory ships a paired *WithKey form that takes the fixed key as a single non-variadic argument and returns just the closure (no key tuple element):

variadic-short explicit WithKey
Areion256Pair(...key) Areion256PairWithKey(key)
Areion512Pair(...key) Areion512PairWithKey(key)
AESCMAC(...key) AESCMACWithKey(key)
ChaCha20(...key) ChaCha20WithKey(key)
BLAKE2s(...key) BLAKE2sWithKey(key)
BLAKE2b256(...key) BLAKE2b256WithKey(key)
BLAKE2b512(...key) BLAKE2b512WithKey(key)
BLAKE3(...key) BLAKE3WithKey(key)

The variadic short form delegates to WithKey (Go inliner removes the wrapper at compile time), so semantics are identical. Pick whichever reads cleaner at your call site:

  • variadic when the same call site handles both random-key and explicit-key paths (e.g. a config-driven factory that defaults to random when no key is in the config);
  • WithKey when the call site is unambiguously explicit-key (restore path with the key already in scope) and the bare key return value would be redundant noise.

SipHash24 has no WithKey because the seed components themselves are the entire SipHash key; serializing the seed components is sufficient.

Documentation

Overview

Package hashes provides cached, pre-keyed wrappers around the nine PRF-grade hash primitives that ITB ships with as built-in factories for the C / FFI / mobile shared-library distribution.

Every factory in this package returns a function value compatible with one of itb.HashFunc128 / itb.HashFunc256 / itb.HashFunc512 (matching the primitive's native intermediate-state width). The returned closure carries a per-instance random fixed key plus pre-computed primitive state (an AES cipher.Block, a BLAKE3 keyed template, a sync.Pool of scratch buffers); subsequent invocations allocate nothing on the heap.

Without these cached wrappers per-pixel hashing would re-derive every primitive's keyed state on every call, which is the dominant cost in ITB's encrypt / decrypt path. The factories are taken directly from the bench-validated reference wrappers — see BENCH.md for measured throughput across all nine primitives × three ITB key widths (512 / 1024 / 2048).

Canonical names and ordering (used by Registry, Find, Make128, Make256, Make512 and exposed through the FFI surface as the public hash identifier):

areion256, areion512, siphash24, aescmac,
blake2b256, blake2b512, blake2s, blake3, chacha20

Each factory has an optional WithKey variant accepting a fixed key of the primitive's native key length, intended for serialization / deserialization of long-lived seeds across processes (Encrypt today, Decrypt tomorrow). SipHash-2-4 has no WithKey variant — the seed components themselves are the entire SipHash key, and no fixed-key state lives in the factory closure.

All primitives in this package are PRF-grade. The below-spec lab stress controls (CRC128, FNV-1a, MD5) used in REDTEAM.md / SCIENCE.md are intentionally absent here — they are research instruments, not shippable cipher primitives.

Custom-primitive builders

Beyond the nine shipped primitives, the package exposes three builder families for safely wrapping user-supplied PRFs:

These builders exist primarily to close the silent-nonce-truncation trap that a naive user wrapper falls into. A user who writes

func(data []byte, seed [8]uint64) [8]uint64 {
    h := sha256.Sum256(data)
    // ... zero-pad upper 32 bytes ... return [8]uint64{...}
}

silently truncates the upper half of ITB's 512-bit intermediate state to a constant value, destroying half the entropy of ChainHash's per-call XOR chain. The builders absorb the full ITB nonce width into the digest through the appropriate chain pattern; the user only writes a primitive call.

Performance trade-off: the builders dispatch through interface callbacks and []byte state buffers, losing 5-15% throughput vs the inline per-primitive closures shipped in this package. The trade is correctness-by-construction for any user primitive vs peak throughput for the nine built-in primitives. See [CONSTRUCTIONS.md] "Why use builders for custom user primitives" for the silent- truncation failure modes the builders prevent.

Index

Constants

This section is empty.

Variables

View Source
var Registry = [9]Spec{
	{"areion256", W256},
	{"areion512", W512},
	{"siphash24", W128},
	{"aescmac", W128},
	{"blake2b256", W256},
	{"blake2b512", W512},
	{"blake2s", W256},
	{"blake3", W256},
	{"chacha20", W256},
}

Registry lists every shippable PRF-grade primitive in canonical order. The same order is used by the FFI iteration surface (ITB_HashName, ITB_HashWidth) so that index 0..8 is stable across releases.

Functions

func AESCMAC

func AESCMAC(key ...[16]byte) (itb.HashFunc128, [16]byte)

AESCMAC returns a cached itb.HashFunc128 backed by AES along with the 16-byte fixed key the closure is bound to. With no argument the key is freshly generated via crypto/rand; passing a single caller-supplied [16]byte uses that key instead.

The returned key is always the actual key in use — callers on the persistence path must save it (encrypt today, decrypt tomorrow); test fixtures and other throw-away usages can discard via `_`.

Construction:

  • key (16 bytes) is loaded once into a cipher.Block (AES-NI hardware path on amd64 / arm64 hosts that expose the AES round instructions; software AES fallback otherwise);
  • per call: seed0||seed1 is XOR'd into the first 16 data bytes, then encrypted in-place; remaining 16-byte data chunks are XOR'd into state and encrypted; the final 16-byte block is returned as (lo64, hi64).

The cipher.Block is shared across all invocations of the closure (it carries no per-call state), so concurrent goroutines may call the returned function in parallel — Go's stdlib AES Encrypt path is reentrant.

func AESCMACPair

func AESCMACPair(key ...[16]byte) (itb.HashFunc128, itb.BatchHashFunc128, [16]byte)

AESCMACPair returns a fresh (single, batched) AES-CMAC-128 hash pair for itb.Seed128 integration. The two arms share the same internally-generated random 16-byte AES key so per-pixel hashes computed via the batched dispatch match the single-call path bit-exact (the parity invariant required by itb.BatchHashFunc128).

On amd64 with VAES + AVX-512 the batched arm dispatches to a fused ZMM-batched chain-absorb kernel for ITB's three SetNonceBits buf shapes (20 / 36 / 68 byte inputs) — VAESENC on ZMM operates on four independent AES blocks per instruction, so the per-pixel AES-CMAC chain advances four lanes through one VAESENC instead of four serial cipher.Block.Encrypt calls. On hosts without VAES + AVX-512, and for non-{20,36,68} input lengths, the batched arm falls back to four single-call invocations and remains bit-exact.

With no argument a fresh 16-byte AES key is generated via crypto/rand; passing a single caller-supplied [16]byte uses that key instead. The returned key (random or supplied) is always emitted as the third return value — save it for cross-process persistence.

Realistic uplift target: substantial over the upstream crypto/aes-driven scalar dispatch on Rocket Lake; higher on AMD Zen 5 / Sapphire Rapids+ where full-width 512-bit ALUs and VAESENC per-cycle throughput (4 AES rounds/cycle on Zen 5 vs ~2-3 on Rocket Lake) widen the envelope. The gain is a mix of 4-lane parallelism (four independent AES-CMAC chains advancing through one VAESENC) and per-call cipher.Block.Encrypt interface-dispatch amortisation across the lanes.

func AESCMACPairWithKey

func AESCMACPairWithKey(aesKey [16]byte) (itb.HashFunc128, itb.BatchHashFunc128)

AESCMACPairWithKey returns the (single, batched) AES-CMAC-128 pair built around a caller-supplied 16-byte AES key, for the persistence-restore path where the original key has been saved across processes (encrypt today, decrypt tomorrow).

The single arm is identical to AESCMACWithKey(aesKey). The batched arm hot-dispatches to the fused ZMM-batched chain-absorb kernel when all four lanes share an input length in {20, 36, 68}; for any other lane-length configuration it falls back to four single-call invocations of the single arm.

The AES-128 round-key schedule (11 × 16-byte round keys = 176 bytes) is pre-expanded once via aescmacasm.ExpandKeyAES128 and captured by the batched closure; the kernels broadcast each round key to all 4 lanes via VBROADCASTI32X4 at function entry.

func AESCMACWithKey

func AESCMACWithKey(aesKey [16]byte) itb.HashFunc128

AESCMACWithKey returns the AESCMAC closure built around a caller- supplied 16-byte key, intended for serialization paths where the fixed key must persist across processes (Encrypt today / Decrypt tomorrow).

func Areion256Pair

func Areion256Pair(key ...[32]byte) (itb.HashFunc256, itb.BatchHashFunc256, [32]byte)

Areion256Pair returns a fresh (single, batched) Areion-SoEM-256 hash pair for itb.Seed256 integration. The two arms share the same internally-generated random fixed key so that per-pixel hashes computed via the batched dispatch match the single-call path bit-exact (the parity invariant required by itb.BatchHashFunc256).

On amd64 with VAES + AVX-512 the batched arm routes per-pixel hashing four pixels per call through AreionSoEM256x4, yielding ~2× throughput over the single-call path. On hosts without those extensions the batched arm falls back to four single-call invocations and remains bit-exact.

This is a thin wrapper over the in-package itb.MakeAreionSoEM256Hash helper; it exists so that Areion-SoEM-256 fits the same name-keyed factory shape as the rest of the hashes/ package.

With no argument a fresh 32-byte fixed key is generated via crypto/rand; passing a single caller-supplied [32]byte uses that key instead. The returned key (random or supplied) is always emitted as the third return value — save it for cross-process persistence.

func Areion256PairWithKey

func Areion256PairWithKey(fixedKey [32]byte) (itb.HashFunc256, itb.BatchHashFunc256)

Areion256PairWithKey returns the (single, batched) Areion-SoEM-256 pair built around a caller-supplied 32-byte fixed key. Same role as the WithKey variants on the other hashes/ primitives — meant for the persistence-restore path where the original fixed key has been saved across processes (encrypt today, decrypt tomorrow).

Thin wrapper over itb.MakeAreionSoEM256HashWithKey for symmetry with the rest of the hashes/ package's WithKey factories.

func Areion512Pair

func Areion512Pair(key ...[64]byte) (itb.HashFunc512, itb.BatchHashFunc512, [64]byte)

Areion512Pair returns a fresh (single, batched) Areion-SoEM-512 hash pair for itb.Seed512 integration. Same construction principle as Areion256Pair: a fresh random 64-byte fixed key shared between the single-call and batched arms, ensuring bit-exact agreement between the two dispatch paths.

On amd64 with VAES + AVX-512 the batched arm uses the AreionSoEM512x4 ASM kernel; on other hosts both arms degrade to the portable Go fallback while remaining bit-identical. With no argument a fresh 64-byte fixed key is generated via crypto/rand; passing a single caller-supplied [64]byte uses that key instead. The returned key (random or supplied) is always emitted as the third return value — save it for cross-process persistence.

func Areion512PairWithKey

func Areion512PairWithKey(fixedKey [64]byte) (itb.HashFunc512, itb.BatchHashFunc512)

Areion512PairWithKey returns the (single, batched) Areion-SoEM-512 pair built around a caller-supplied 64-byte fixed key. Same role as the WithKey variants on the other hashes/ primitives — meant for the persistence-restore path where the original fixed key has been saved across processes (encrypt today, decrypt tomorrow).

Thin wrapper over itb.MakeAreionSoEM512HashWithKey.

func BLAKE2b256

func BLAKE2b256(key ...[32]byte) (itb.HashFunc256, [32]byte)

BLAKE2b256 returns a cached BLAKE2b-256 itb.HashFunc256 with a freshly-generated 32-byte fixed key.

Construction prepends the fixed key as a 32-byte prefix to the hash input and mixes seed components by XOR over the next 32 bytes: H(key || data ^ seed). blake2b.Sum256 is the entry point (no allocation, no keyed-mode handle), so the closure has zero per-call allocations modulo the pooled scratch buffer.

The payload region is zero-padded out to 32 bytes when len(data) is shorter, ensuring all four seed uint64's contribute regardless of how short the caller's data is — important for ITB which hashes 20-byte (pixel_le + nonce) inputs in the inner loop. BLAKE2b256 returns a cached BLAKE2b-256 itb.HashFunc256 along with the 32-byte fixed key the closure is bound to. With no argument a fresh key is generated via crypto/rand; passing a single caller-supplied [32]byte uses that key instead. Save the returned key for cross-process persistence.

func BLAKE2b256Pair

func BLAKE2b256Pair(key ...[32]byte) (itb.HashFunc256, itb.BatchHashFunc256, [32]byte)

BLAKE2b256Pair returns a fresh (single, batched) BLAKE2b-256 hash pair for itb.Seed256 integration. The two arms share the same internally-generated random 32-byte fixed key so per-pixel hashes computed via the batched dispatch match the single-call path bit-exact (the parity invariant required by itb.BatchHashFunc256).

On amd64 with AVX-512+VL the batched arm dispatches to a fused ZMM-batched chain-absorb kernel for ITB's three SetNonceBits buf shapes (20 / 36 / 68 byte inputs). On hosts without AVX-512+VL, and for non-{20,36,68} input lengths, the batched arm falls back to four single-call invocations and remains bit-exact.

With no argument a fresh 32-byte fixed key is generated via crypto/rand; passing a single caller-supplied [32]byte uses that key instead. The returned key (random or supplied) is always emitted as the third return value — save it for cross-process persistence.

func BLAKE2b256PairWithKey

func BLAKE2b256PairWithKey(fixedKey [32]byte) (itb.HashFunc256, itb.BatchHashFunc256)

BLAKE2b256PairWithKey returns the (single, batched) BLAKE2b-256 pair built around a caller-supplied 32-byte fixed key. Same role as the WithKey variants on the other hashes/ primitives — meant for the persistence-restore path where the original fixed key has been saved across processes (encrypt today, decrypt tomorrow).

The single arm is identical to BLAKE2b256WithKey(fixedKey). The batched arm hot-dispatches to the fused ZMM-batched chain-absorb kernel when all four lanes share an input length in {20, 36, 68}; for any other lane-length configuration it falls back to four single-call invocations of the single arm.

func BLAKE2b256WithKey

func BLAKE2b256WithKey(b2key [32]byte) itb.HashFunc256

BLAKE2b256WithKey returns the BLAKE2b-256 closure built around a caller-supplied 32-byte fixed key, for serialization paths.

The closure runs on the upstream golang.org/x/crypto/blake2b path (which itself uses the BLAKE2b AVX2 kernel on amd64). For ITB throughput-critical use, prefer BLAKE2b256Pair: the batched arm of the pair dispatches to a 4-pixel-parallel AVX-512 ZMM kernel that amortises the per-call overhead the upstream single-pixel path cannot.

func BLAKE2b512

func BLAKE2b512(key ...[64]byte) (itb.HashFunc512, [64]byte)

BLAKE2b512 returns a cached BLAKE2b-512 itb.HashFunc512 with a freshly-generated 64-byte fixed key.

BLAKE2b natively supports 512-bit output and up to a 64-byte key. The construction is identical to BLAKE2b256 modulo widths: H(key || data ^ seed) where the payload is zero-padded out to 64 bytes when shorter, ensuring all eight seed uint64's contribute regardless of how short the caller's data is. BLAKE2b512 returns a cached BLAKE2b-512 itb.HashFunc512 along with the 64-byte fixed key the closure is bound to. With no argument a fresh key is generated via crypto/rand; passing a single caller-supplied [64]byte uses that key instead. Save the returned key for cross-process persistence.

func BLAKE2b512Pair

func BLAKE2b512Pair(key ...[64]byte) (itb.HashFunc512, itb.BatchHashFunc512, [64]byte)

BLAKE2b512Pair returns a fresh (single, batched) BLAKE2b-512 hash pair for itb.Seed512 integration. The two arms share the same internally-generated random 64-byte fixed key so per-pixel hashes computed via the batched dispatch match the single-call path bit-exact (the parity invariant required by itb.BatchHashFunc512).

On amd64 with AVX-512+VL the batched arm dispatches to a fused ZMM-batched chain-absorb kernel for ITB's three SetNonceBits buf shapes (20 / 36 / 68 byte inputs), holding four lane-isolated BLAKE2b states in 16 ZMM registers across all 12 mixing rounds. On hosts without AVX-512+VL, and for non-{20,36,68} input lengths, the batched arm falls back to four single-call invocations and remains bit-exact.

With no argument a fresh 64-byte fixed key is generated via crypto/rand; passing a single caller-supplied [64]byte uses that key instead. The returned key (random or supplied) is always emitted as the third return value — save it for cross-process persistence.

func BLAKE2b512PairWithKey

func BLAKE2b512PairWithKey(fixedKey [64]byte) (itb.HashFunc512, itb.BatchHashFunc512)

BLAKE2b512PairWithKey returns the (single, batched) BLAKE2b-512 pair built around a caller-supplied 64-byte fixed key. Same role as the WithKey variants on the other hashes/ primitives — meant for the persistence-restore path where the original fixed key has been saved across processes (encrypt today, decrypt tomorrow).

The single arm is identical to BLAKE2b512WithKey(fixedKey). The batched arm hot-dispatches to the fused ZMM-batched chain-absorb kernel when all four lanes share an input length in {20, 36, 68}; for any other lane-length configuration it falls back to four single-call invocations of the single arm.

func BLAKE2b512WithKey

func BLAKE2b512WithKey(b2key [64]byte) itb.HashFunc512

BLAKE2b512WithKey returns the BLAKE2b-512 closure built around a caller-supplied 64-byte fixed key, for serialization paths.

The closure runs on the upstream golang.org/x/crypto/blake2b path (which itself uses the BLAKE2b AVX2 kernel on amd64). For ITB throughput-critical use, prefer BLAKE2b512Pair: the batched arm of the pair dispatches to a 4-pixel-parallel AVX-512 ZMM kernel that amortises the per-call overhead the upstream single-pixel path cannot.

func BLAKE2s

func BLAKE2s(key ...[32]byte) (itb.HashFunc256, [32]byte)

BLAKE2s returns a cached BLAKE2s-256 itb.HashFunc256 with a freshly-generated 32-byte fixed key.

Same construction as BLAKE2b256: H(key || data ^ seed) using blake2s.Sum256 (no allocation, no keyed-mode handle). The payload region is zero-padded to 32 bytes for short inputs so all four seed uint64's contribute to the digest. BLAKE2s returns a cached BLAKE2s-256 itb.HashFunc256 along with the 32-byte fixed key the closure is bound to. With no argument a fresh key is generated via crypto/rand; passing a single caller-supplied [32]byte uses that key instead. Save the returned key for cross-process persistence.

func BLAKE2s256Pair

func BLAKE2s256Pair(key ...[32]byte) (itb.HashFunc256, itb.BatchHashFunc256, [32]byte)

BLAKE2s256Pair returns a fresh (single, batched) BLAKE2s-256 hash pair for itb.Seed256 integration. The two arms share the same internally-generated random 32-byte fixed key so per-pixel hashes computed via the batched dispatch match the single-call path bit-exact (the parity invariant required by itb.BatchHashFunc256).

On amd64 with AVX-512+VL the batched arm dispatches to a fused ZMM-batched chain-absorb kernel for ITB's three SetNonceBits buf shapes (20 / 36 / 68 byte inputs). On hosts without AVX-512+VL, and for non-{20,36,68} input lengths, the batched arm falls back to four single-call invocations and remains bit-exact.

With no argument a fresh 32-byte fixed key is generated via crypto/rand; passing a single caller-supplied [32]byte uses that key instead. The returned key (random or supplied) is always emitted as the third return value — save it for cross-process persistence.

func BLAKE2s256PairWithKey

func BLAKE2s256PairWithKey(fixedKey [32]byte) (itb.HashFunc256, itb.BatchHashFunc256)

BLAKE2s256PairWithKey returns the (single, batched) BLAKE2s-256 pair built around a caller-supplied 32-byte fixed key. Same role as the WithKey variants on the other hashes/ primitives — meant for the persistence-restore path where the original fixed key has been saved across processes (encrypt today, decrypt tomorrow).

The single arm is identical to BLAKE2sWithKey(fixedKey). The batched arm hot-dispatches to the fused ZMM-batched chain-absorb kernel when all four lanes share an input length in {20, 36, 68}; for any other lane-length configuration it falls back to four single-call invocations of the single arm.

The ASM kernel returns 8 × uint32 per lane (32 bytes of digest); the closure repacks each lane's 8 uint32 into 4 uint64 for the itb.BatchHashFunc256 contract (LE byte ordering).

func BLAKE2sWithKey

func BLAKE2sWithKey(b2key [32]byte) itb.HashFunc256

BLAKE2sWithKey returns the BLAKE2s-256 closure built around a caller-supplied 32-byte fixed key, for serialization paths.

func BLAKE3

func BLAKE3(key ...[32]byte) (itb.HashFunc256, [32]byte)

BLAKE3 returns a cached BLAKE3-256 itb.HashFunc256 with a freshly- generated 32-byte BLAKE3 key.

The pre-keyed BLAKE3 hasher template is created once via blake3.NewKeyed; each call clones the template instead of re-keying, sidestepping the data race that Reset() on a shared hasher would cause when ITB's process256 dispatches multiple goroutines on the same seed. A sync.Pool of scratch buffers keeps per-call allocation at zero.

Seed components are mixed into the hashed payload as XOR over the first 32 bytes; the input is zero-padded out to 32 bytes when the caller's data is shorter, so all four seed uint64's contribute regardless of how short the caller's data is. BLAKE3 returns a cached BLAKE3-256 itb.HashFunc256 along with the 32-byte BLAKE3 key the closure is bound to. With no argument a fresh key is generated via crypto/rand; passing a single caller-supplied [32]byte uses that key instead. Save the returned key for cross-process persistence.

func BLAKE3WithKey

func BLAKE3WithKey(key [32]byte) itb.HashFunc256

BLAKE3WithKey returns the BLAKE3 closure built around a caller- supplied 32-byte BLAKE3 key, for serialization across processes.

func BLAKE3256Pair

func BLAKE3256Pair(key ...[32]byte) (itb.HashFunc256, itb.BatchHashFunc256, [32]byte)

BLAKE3256Pair returns a fresh (single, batched) BLAKE3-256 hash pair for itb.Seed256 integration. The two arms share the same internally-generated random 32-byte BLAKE3 key so per-pixel hashes computed via the batched dispatch match the single-call path bit-exact (the parity invariant required by itb.BatchHashFunc256).

On amd64 with AVX-512+VL the batched arm dispatches to a fused ZMM-batched chain-absorb kernel for ITB's three SetNonceBits buf shapes (20 / 36 / 68 byte inputs). On hosts without AVX-512+VL, and for non-{20,36,68} input lengths, the batched arm falls back to four single-call invocations and remains bit-exact.

With no argument a fresh 32-byte BLAKE3 key is generated via crypto/rand; passing a single caller-supplied [32]byte uses that key instead. The returned key (random or supplied) is always emitted as the third return value — save it for cross-process persistence.

Realistic uplift target: 1.3-2.0× over the upstream zeebo/blake3 per-call dispatch. github.com/zeebo/blake3 already carries hand-written AVX-512 assembly for the BLAKE3 compression, so the batched arm's gain over upstream is mostly from amortising the per-call Hasher.Clone / Write / Sum overhead across 4 lanes rather than from kernel-internal speedup.

func BLAKE3256PairWithKey

func BLAKE3256PairWithKey(fixedKey [32]byte) (itb.HashFunc256, itb.BatchHashFunc256)

BLAKE3256PairWithKey returns the (single, batched) BLAKE3-256 pair built around a caller-supplied 32-byte BLAKE3 key, for the persistence-restore path where the original key has been saved across processes (encrypt today, decrypt tomorrow).

The single arm is identical to BLAKE3WithKey(key). The batched arm hot-dispatches to the fused ZMM-batched chain-absorb kernel when all four lanes share an input length in {20, 36, 68}; for any other lane-length configuration it falls back to four single-call invocations of the single arm.

The ASM kernel returns 8 × uint32 per lane (32 bytes of digest); the closure repacks each lane's 8 uint32 into 4 uint64 for the itb.BatchHashFunc256 contract (LE byte ordering).

func BuildARXChainAbsorb128

func BuildARXChainAbsorb128(hashFn Hash256Fn, fixedKey []byte) itb.HashFunc128

BuildARXChainAbsorb128 wraps a 32-byte full hash function into an itb.HashFunc128 closure. The closure constructs a single absorb buffer of the form:

buf = fixedKey || lenTag(8) || seed0(8) || seed1(8) || domain(1) || data

then computes hashFn(buf) and returns the first 16 bytes of the digest as a (lo, hi) uint64 pair. The full ITB nonce is part of the data segment and reaches the hash through the underlying hash function's native variable-length absorption.

fixedKey is hashed into the prefix as a long-lived keying source; seed0/seed1 are the per-call PRF key supplied by ITB's ChainHash. Length tag and domain byte protect against length-extension and cross-call collision attacks within the construction.

For SHA-256 wrappers this is the canonical safe usage. The hash function does its own MD chaining internally, so no chain-absorb loop is needed at the builder level — the builder's role is to arrange the buffer correctly so all of {fixedKey, seed, length, domain, data} reach the digest with no silent truncation.

func BuildARXChainAbsorb256

func BuildARXChainAbsorb256(hashFn Hash256Fn, fixedKey []byte) itb.HashFunc256

BuildARXChainAbsorb256 wraps a 32-byte full hash function into an itb.HashFunc256 closure. Same construction as BuildARXChainAbsorb128 but returns the full 32-byte digest as [4]uint64.

func BuildARXChainAbsorb512

func BuildARXChainAbsorb512(hashFn Hash512Fn, fixedKey []byte) itb.HashFunc512

BuildARXChainAbsorb512 wraps a 64-byte full hash function into an itb.HashFunc512 closure. Same construction as BuildARXChainAbsorb128 but uses a Hash512Fn (e.g. crypto/sha512.Sum512) so the full 64-byte digest comes from a single hash call. The fixedKey + seed (4 of 8 components) + length + domain prefix is built once; the remaining 4 seed components are mixed via a second hash call with a different domain marker, and the two 32-byte halves are concatenated.

func BuildCBCMACChainAbsorb128

func BuildCBCMACChainAbsorb128(block cipher.Block) itb.HashFunc128

BuildCBCMACChainAbsorb128 wraps a keyed block cipher into an itb.HashFunc128 closure that absorbs arbitrary-length data via CBC-MAC chain. The full ITB nonce reaches the digest with no silent truncation: data is absorbed in BlockSize()-byte chunks via XOR followed by block.Encrypt; a length tag is folded into the initial state to break the trailing-zero collision class.

The block cipher must have BlockSize() >= 16. AES-128 / AES-192 / AES-256, Camellia, ARIA, SM4 — all qualify. The cipher's key is embedded inside the cipher.Block; the builder does not see the key material.

Construction:

  • state := zeros(BlockSize())
  • state[0:8] = seed0 ^ len(data)
  • state[8:16] = seed1 ^ len(data)
  • state[0:firstChunkLen] ^= data[:firstChunkLen]
  • state = block.Encrypt(state)
  • For each subsequent BlockSize()-byte chunk of data:
  • state[0:chunkLen] ^= data[offset:offset+chunkLen]
  • state = block.Encrypt(state)
  • Output: (uint64_le(state[0:8]), uint64_le(state[8:16]))

The closure runs at least one block.Encrypt call (even for empty data) so the length-tagged initial state is always permuted before output extraction.

func BuildCBCMACChainAbsorb256

func BuildCBCMACChainAbsorb256(block cipher.Block) itb.HashFunc256

BuildCBCMACChainAbsorb256 wraps a keyed block cipher into an itb.HashFunc256 closure. Internally runs two independent CBC-MAC chain-absorb passes over the same data, each domain-separated by a distinct constant XOR'd into the initial state, and concatenates the 16-byte halves into a 32-byte digest. The construction inherits the full nonce absorption property of BuildCBCMACChainAbsorb128 and adds a 2x throughput cost.

func BuildCBCMACChainAbsorb512

func BuildCBCMACChainAbsorb512(block cipher.Block) itb.HashFunc512

BuildCBCMACChainAbsorb512 wraps a keyed block cipher into an itb.HashFunc512 closure. Internally runs four independent CBC-MAC chain-absorb passes over the same data, each domain-separated by a distinct constant XOR'd into the initial state, and concatenates the 16-byte quarters into a 64-byte digest. The construction inherits the full nonce absorption property of BuildCBCMACChainAbsorb128 and adds a 4x throughput cost.

For 64-byte ITB nonce (SetNonceBits(512)) configurations, this closure runs 4 * ceil(68 / BlockSize()) block.Encrypt calls per HashFunc512 invocation. For AES-128 (BlockSize=16) this is 20 Encrypt calls; for a hypothetical 32-byte block cipher 12 Encrypt calls.

func BuildSpongeChainAbsorb128

func BuildSpongeChainAbsorb128(permute Permute, rate, capacity int, fixedKey []byte) itb.HashFunc128

BuildSpongeChainAbsorb128 wraps an unkeyed permutation into a keyed- sponge itb.HashFunc128 closure. The permutation is invoked on a (rate + capacity)-byte state buffer. The fixedKey is XOR'd into the capacity region for keying (standard keyed-sponge pattern). Data is absorbed in rate-byte chunks; output is extracted from the first 16 bytes of the rate region.

Requirements:

  • rate >= 16 (so output extraction is direct, no squeeze loop)
  • capacity >= 16 (so the seed components fit in the capacity slot after fixedKey injection)
  • len(fixedKey) <= capacity

Construction:

  • state := zeros(rate + capacity)
  • copy(state[rate:rate+len(fixedKey)], fixedKey)
  • state[rate:rate+8] ^= LE(seed0)
  • state[rate+8:rate+16] ^= LE(seed1)
  • state[0:8] = LE(len(data) ^ domain)
  • permute(state)
  • For each rate-byte chunk of data:
  • state[0:chunkLen] ^= data[offset:offset+chunkLen]
  • permute(state)
  • Output: (uint64_le(state[0:8]), uint64_le(state[8:16]))

The permutation runs at least once (over the initialized state) even for empty data, so the length-tagged state is always mixed before output.

func BuildSpongeChainAbsorb256

func BuildSpongeChainAbsorb256(permute Permute, rate, capacity int, fixedKey []byte) itb.HashFunc256

BuildSpongeChainAbsorb256 wraps an unkeyed permutation into a keyed- sponge itb.HashFunc256 closure. Internally runs two domain-separated sponge chain-absorb passes and concatenates 16-byte halves. Same guarantees as BuildSpongeChainAbsorb128 at 2x cost.

func BuildSpongeChainAbsorb512

func BuildSpongeChainAbsorb512(permute Permute, rate, capacity int, fixedKey []byte) itb.HashFunc512

BuildSpongeChainAbsorb512 wraps an unkeyed permutation into a keyed- sponge itb.HashFunc512 closure. Internally runs four domain-separated sponge chain-absorb passes and concatenates 16-byte quarters. Same guarantees as BuildSpongeChainAbsorb128 at 4x cost.

func ChaCha20

func ChaCha20(key ...[32]byte) (itb.HashFunc256, [32]byte)

ChaCha20 returns a cached ChaCha20 itb.HashFunc256 with a freshly-generated 32-byte fixed key.

Construction (ARX-only PRF, no S-box / no table lookups): the fixed key is XOR'd with the seed components to derive a per-call 256-bit ChaCha20 key. Data is absorbed CBC-MAC-style into a 32-byte state via repeated `state ← E_K(state ⊕ chunk)`-shaped rounds, where E_K is one ChaCha20 keystream block applied to the state and the counter advances automatically between rounds. A length-tag prefix in the initial state and a 24-byte data window per round (8 bytes of chaining feedback) ensure every byte of the input contributes to the digest regardless of input length — 128-, 256-, and 512-bit nonce configurations all reach the digest with full strength.

Per-call allocation is bounded by the cipher initialisation; the state, length tag, and chain feedback all live on the closure's stack frame. Concurrent goroutines may invoke the returned closure in parallel — there is no shared mutable state. ChaCha20 returns a cached ChaCha20 itb.HashFunc256 along with the 32-byte fixed key the closure is bound to. With no argument the key is freshly generated via crypto/rand; passing a single caller-supplied [32]byte uses that key instead. Save the returned key for cross-process persistence.

func ChaCha20WithKey

func ChaCha20WithKey(fixedKey [32]byte) itb.HashFunc256

ChaCha20WithKey returns the ChaCha20 closure built around a caller-supplied 32-byte fixed key, for serialization paths.

func ChaCha20256Pair

func ChaCha20256Pair(key ...[32]byte) (itb.HashFunc256, itb.BatchHashFunc256, [32]byte)

ChaCha20256Pair returns a fresh (single, batched) ChaCha20-256 hash pair for itb.Seed256 integration. The two arms share the same internally-generated random 32-byte fixed key so per-pixel hashes computed via the batched dispatch match the single-call path bit-exact (the parity invariant required by itb.BatchHashFunc256).

On amd64 with AVX-512+VL the batched arm dispatches to a fused ZMM-batched chain-absorb kernel for ITB's three SetNonceBits buf shapes (20 / 36 / 68 byte inputs). On hosts without AVX-512+VL, and for non-{20,36,68} input lengths, the batched arm falls back to four single-call invocations and remains bit-exact.

With no argument a fresh 32-byte fixed key is generated via crypto/rand; passing a single caller-supplied [32]byte uses that key instead. The returned key (random or supplied) is always emitted as the third return value — save it for cross-process persistence.

Realistic uplift target: 2.5×-4.5× over the upstream golang.org/x/crypto/chacha20 per-call dispatch on Rocket Lake; higher on AMD Zen 5 / Sapphire Rapids+ where full-width 512-bit ALUs and absent AVX-512 frequency throttle widen the envelope. The gain is a mix of 4-lane parallelism (four independent ChaCha20 state evolutions retiring through one ZMM dispatch) and per-call cipher.NewUnauthenticatedCipher / XORKeyStream amortisation across the lanes.

func ChaCha20256PairWithKey

func ChaCha20256PairWithKey(fixedKey [32]byte) (itb.HashFunc256, itb.BatchHashFunc256)

ChaCha20256PairWithKey returns the (single, batched) ChaCha20-256 pair built around a caller-supplied 32-byte fixed key, for the persistence-restore path where the original key has been saved across processes (encrypt today, decrypt tomorrow).

The single arm is identical to ChaCha20WithKey(fixedKey). The batched arm hot-dispatches to the fused ZMM-batched chain-absorb kernel when all four lanes share an input length in {20, 36, 68}; for any other lane-length configuration it falls back to four single-call invocations of the single arm.

The ASM kernel returns 4 × uint64 per lane (32 bytes of state) directly — no intermediate [8]uint32 repacking is required since the ChaCha20 chain-absorb output is the 32-byte CBC-MAC-style state buffer in its native LE byte order.

func Make128

func Make128(name string, key ...[]byte) (itb.HashFunc128, []byte, error)

Make128 returns a fresh cached HashFunc128 for the named primitive along with the fixed key the closure is bound to. Pass a single caller-supplied key slice to use that key; pass nothing to generate a fresh random key (returned alongside the closure for persistence).

SipHash-2-4 has no internal fixed key (its keying material is the per-call seed components), so passing a key for "siphash24" is an error; the second return value is nil for siphash24.

Returns an error when name is unknown, its native width is not 128, or the supplied key size does not match the primitive's native key length.

func Make128Pair

func Make128Pair(name string, key ...[]byte) (itb.HashFunc128, itb.BatchHashFunc128, []byte, error)

Make128Pair returns the (single, batched) HashFunc128 / BatchHashFunc128 pair for primitives with a 4-way batched implementation, plus the fixed key the pair is bound to. The batched arm is nil for primitives that do not implement a batched path. The single arm is bit-exact equivalent to Make128 for the same name and key.

Primitives currently returning a non-nil batched arm:

  • "aescmac" — VAES + AVX-512 ZMM-batched AES-CMAC chain-absorb kernels
  • "siphash24" — AVX-512 ZMM-batched SipHash-2-4 chain-absorb kernels

Variadic key arg follows the same pattern as Make128 / Make256Pair.

func Make256

func Make256(name string, key ...[]byte) (itb.HashFunc256, []byte, error)

Make256 returns a fresh cached HashFunc256 for the named primitive along with the fixed key the closure is bound to. Variadic key arg follows the same pattern as Make128: pass nothing for random key, pass one []byte of the primitive's native key length for explicit.

For "areion256" the batched arm is discarded; use Make256Pair if the per-pixel batched dispatch is needed.

Returns an error when name is unknown, width is not 256, or supplied key size is wrong.

func Make256Pair

func Make256Pair(name string, key ...[]byte) (itb.HashFunc256, itb.BatchHashFunc256, []byte, error)

Make256Pair returns the (single, batched) HashFunc256 / BatchHashFunc256 pair for primitives that have a 4-way batched implementation, plus the fixed key the pair is bound to. The batched arm is nil for primitives that do not implement a batched path. The single arm is bit-exact equivalent to Make256 for the same name and key.

Primitives currently returning a non-nil batched arm:

  • "areion256" — VAES + AVX-512 AreionSoEM256x4 ASM kernel
  • "blake2b256" — AVX-512 ZMM-batched BLAKE2b chain-absorb kernels
  • "blake2s" — AVX-512 ZMM-batched BLAKE2s chain-absorb kernels
  • "blake3" — AVX-512 ZMM-batched BLAKE3 chain-absorb kernels
  • "chacha20" — AVX-512 ZMM-batched ChaCha20 chain-absorb kernels

Variadic key arg follows the same pattern as Make256.

func Make512

func Make512(name string, key ...[]byte) (itb.HashFunc512, []byte, error)

Make512 returns a fresh cached HashFunc512 for the named primitive along with the fixed key the closure is bound to. Variadic key arg follows the same pattern as Make128 / Make256.

For "areion512" the batched arm is discarded; use Make512Pair if the per-pixel batched dispatch is needed.

Returns an error when name is unknown, width is not 512, or supplied key size is wrong.

func Make512Pair

func Make512Pair(name string, key ...[]byte) (itb.HashFunc512, itb.BatchHashFunc512, []byte, error)

Make512Pair returns the (single, batched) HashFunc512 / BatchHashFunc512 pair for primitives with a 4-way batched implementation, plus the fixed key. The batched arm is nil when no batched path exists.

Primitives currently returning a non-nil batched arm:

  • "areion512" — VAES + AVX-512 AreionSoEM512x4 ASM kernel
  • "blake2b512" — AVX-512 ZMM-batched BLAKE2b chain-absorb kernels

func SipHash24

func SipHash24() itb.HashFunc128

SipHash24 returns a SipHash-2-4 itb.HashFunc128 closure.

SipHash-2-4 is a designed PRF whose 128-bit key is supplied per call as the (seed0, seed1) pair — exactly the shape ITB's Seed128 ChainHash128 produces from the seed components. There is no pre-keyed state to cache (no fixed key, no internal hasher object, no scratch buffer) so the closure is a direct call into siphash.

Returns: (low64, high64) of SipHash128(key=(seed0, seed1), data).

No WithKey variant — the seed components are the entire SipHash key. Long-lived seed serialization is a matter of saving Components only.

func SipHash24Pair

func SipHash24Pair() (itb.HashFunc128, itb.BatchHashFunc128)

SipHash24Pair returns a (single, batched) SipHash-2-4-128 hash pair for itb.Seed128 integration. SipHash has no fixed key — the per-call (seed0, seed1) pair is the entire SipHash key — so the factory takes no arguments and returns no key, distinguishing it from the AESCMACPair / AESCMACPairWithKey shape used by the other W128 primitive in the registry.

On amd64 with AVX-512+VL the batched arm dispatches to a fused ZMM-batched chain-absorb kernel for ITB's three SetNonceBits buf shapes (20 / 36 / 68 byte inputs) — the 4 SipHash state words (v0..v3) are held in qwords 0..3 of four ZMM registers (Z0..Z3), and the SipRound body (VPADDQ / VPXORQ / VPROLQ on u64) advances four independent SipHash chains concurrently per instruction. On hosts without AVX-512+VL, and for non-{20,36,68} input lengths, the batched arm falls back to four single-call invocations of dchest/siphash and remains bit-exact.

Realistic uplift target: modest on Rocket Lake (the dchest/siphash scalar path is already very fast, leaving little headroom); larger on AMD Zen 5 / Sapphire Rapids+ where the 4-lane parallel SipRound retires through a full-width 512-bit ALU without the AVX-512 frequency throttle.

Types

type Hash256Fn

type Hash256Fn func(data []byte) [32]byte

Hash256Fn represents a full hash function with 32-byte output, such as crypto/sha256.Sum256. The function must compute the full hash over the input byte slice in one call; it must not retain a reference to the input slice after returning.

type Hash512Fn

type Hash512Fn func(data []byte) [64]byte

Hash512Fn represents a full hash function with 64-byte output, such as crypto/sha512.Sum512.

type Permute

type Permute func(state []byte)

Permute is the type for an unkeyed permutation operating on a state buffer. The implementation must mutate state in place; state length is always rate + capacity bytes. Standard sponge permutations like Keccak-f[1600] or Ascon-p match this signature trivially.

type Spec

type Spec struct {
	Name  string // canonical, FFI-stable identifier (no dashes)
	Width Width  // native intermediate-state width
}

Spec describes one PRF-grade hash primitive shipped with this package.

func Find

func Find(name string) (Spec, bool)

Find returns the Spec for a canonical name and reports whether a match was found.

type Width

type Width int

Width is the native intermediate-state width of a hash primitive. Determines which itb.Seed{128|256|512} type the primitive feeds.

const (
	W128 Width = 128
	W256 Width = 256
	W512 Width = 512
)

Directories

Path Synopsis
internal
aescmacasm
Package aescmacasm holds the AVX-512 + VAES fused chain-absorb kernel implementation of AES-CMAC for the parent hashes/ package.
Package aescmacasm holds the AVX-512 + VAES fused chain-absorb kernel implementation of AES-CMAC for the parent hashes/ package.
blake2basm
Package blake2basm holds the AVX-512 + VL fused chain-absorb kernel implementation of BLAKE2b for the parent hashes/ package.
Package blake2basm holds the AVX-512 + VL fused chain-absorb kernel implementation of BLAKE2b for the parent hashes/ package.
blake2sasm
Package blake2sasm holds the AVX-512 + VL fused chain-absorb kernel implementation of BLAKE2s for the parent hashes/ package.
Package blake2sasm holds the AVX-512 + VL fused chain-absorb kernel implementation of BLAKE2s for the parent hashes/ package.
blake3asm
Package blake3asm holds the AVX-512 + VL fused chain-absorb kernel implementation of BLAKE3 for the parent hashes/ package.
Package blake3asm holds the AVX-512 + VL fused chain-absorb kernel implementation of BLAKE3 for the parent hashes/ package.
chacha20asm
Package chacha20asm holds the AVX-512 + VL fused chain-absorb kernel implementation of ChaCha20 for the parent hashes/ package.
Package chacha20asm holds the AVX-512 + VL fused chain-absorb kernel implementation of ChaCha20 for the parent hashes/ package.
siphashasm
Package siphashasm holds the AVX-512 + VL fused chain-absorb kernel implementation of SipHash-2-4-128 for the parent hashes/ package.
Package siphashasm holds the AVX-512 + VL fused chain-absorb kernel implementation of SipHash-2-4-128 for the parent hashes/ package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL