macs

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: May 20, 2026 License: MIT Imports: 8 Imported by: 0

README

macs — Cached PRF-grade MAC factories for ITB Authenticated Encryption

Drop-in factories that produce itb.MACFunc closures for the three shipped MAC primitives. All three produce a 32-byte tag and accept a 32-byte (or longer for the HMAC variants) key. The fixed 32-byte tag size lets bindings size their authenticated payload buffer the same way regardless of which MAC was selected.

Every factory pre-keys its primitive once at construction and is safe to call concurrently from multiple goroutines.

Canonical primitives

In FFI-stable index order:

# Name (FFI) Key size Tag size Caching
0 kmac256 ≥16 B 32 B cSHAKE256 template with key absorbed once, Clone() per call
1 hmac-sha256 ≥16 B 32 B sync.Pool of hmac.New(sha256.New, key) instances
2 hmac-blake3 32 B 32 B blake3.NewKeyed(key) template with Clone() per call

kmac256 follows NIST SP 800-185 with output length L = 256 bits. hmac-sha256 follows RFC 2104 with SHA-256. hmac-blake3 uses BLAKE3's native keyed mode (no nested HMAC wrapper — BLAKE3-keyed is itself a sound keyed PRF; see BLAKE3 spec section 6).

Usage

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

Each Blob type packs every seed's hash key + components, the optional dedicated lockSeed, AND the MAC key + name into one self-describing JSON blob alongside the captured itb.Set* globals; 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 + macs.Make(blob.MACName, blob.MACKey).

Areion-SoEM has paired (single, batched, fixedKey) constructors so the AVX-512 batched dispatch path is reachable, paired with HMAC-BLAKE3 as the authentication primitive:


// Sender

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

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)

	// HMAC-BLAKE3 — fastest of the three MACs through the AVX-512 ASM kernel.
	var macKey [32]byte
	rand.Read(macKey[:])
	mac, _ := macs.HMACBLAKE3(macKey[:])

	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 := itb.EncryptAuthenticated512(ns, ds, ss, plaintext, mac)
	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 MAC key + name into one self-describing
	// JSON blob alongside the captured itb.Set* globals.
	bSrc := &itb.Blob512{}
	blob, _ := bSrc.Export(keyN, keyD, keyS, ns, ds, ss,
		itb.Blob512Opts{
			KeyL: keyL, LS: ls,
			MACKey: macKey[:], MACName: "hmac-blake3",
		})

	// Send encrypted payload + blob

}

// Receiver

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

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, the
	// MAC key + name, 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)

	mac, _ := macs.Make(bDst.MACName, bDst.MACKey)

	// Authenticated decrypt — any single-bit tamper triggers MAC failure
	// (no oracle leak about which byte was tampered).
	decrypted, err := itb.DecryptAuthenticated512(bDst.NS, bDst.DS, bDst.SS, encrypted, mac)
	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, paired with HMAC-BLAKE3 as the authentication primitive:


// Sender

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

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 + HMAC-BLAKE3 example.
	ns.AttachLockSeed(ls)

	// HMAC-BLAKE3 — fastest of the three MACs through the AVX-512 ASM kernel.
	var macKey [32]byte
	rand.Read(macKey[:])
	mac, _ := macs.HMACBLAKE3(macKey[:])

	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 := itb.EncryptAuthenticated512(ns, ds, ss, plaintext, mac)
	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 MAC key + name into one self-describing
	// JSON blob alongside the captured itb.Set* globals.
	bSrc := &itb.Blob512{}
	blob, _ := bSrc.Export(keyN, keyD, keyS, ns, ds, ss,
		itb.Blob512Opts{
			KeyL: keyL, LS: ls,
			MACKey: macKey[:], MACName: "hmac-blake3",
		})

	// Send encrypted payload + blob

}

// Receiver

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

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, the
	// MAC key + name, 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)

	mac, _ := macs.Make(bDst.MACName, bDst.MACKey)

	// Authenticated decrypt — any single-bit tamper triggers MAC failure
	// (no oracle leak about which byte was tampered).
	decrypted, err := itb.DecryptAuthenticated512(bDst.NS, bDst.DS, bDst.SS, encrypted, mac)
	if err != nil {
		panic(err)
	}
	fmt.Printf("decrypted: %d bytes\n", len(decrypted))

}

SipHash-2-4 + HMAC-SHA256 — paired (single, batched) constructor for the hash, lockSeed-engaged via SetBitSoup, MAC + hash keys captured through Blob128:

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

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



	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)

	// HMAC-SHA256 — universal interoperability standard (RFC 4231).
	var macKey [32]byte
	rand.Read(macKey[:])
	mac, _ := macs.HMACSHA256(macKey[:])

	plaintext := []byte("any text or binary data - including 0x00 bytes")
	// Authenticated encrypt into RGBWYOPA container with embedded 32-byte tag
	encrypted, _ := itb.EncryptAuthenticated128(ns, ds, ss, plaintext, mac)

	// Cross-process persistence — Blob128 packs Components + the
	// optional dedicated lockSeed (KeyN/KeyD/KeyS/KeyL stay nil since
	// SipHash-2-4 has no internal fixed key) plus the MAC key + name.
	bSrc := &itb.Blob128{}
	blob, _ := bSrc.Export(nil, nil, nil, ns, ds, ss,
	    itb.Blob128Opts{
		LS:     ls,
		MACKey: macKey[:], MACName: "hmac-sha256",
	})
	_ = blob // ship alongside the ciphertext

}

Name-keyed dispatch (used by the FFI layer; works for any code that selects the MAC primitive at runtime). The key is []byte (size validated against the primitive's minimum / fixed length):

mac, _ := macs.Make("hmac-sha256", key)

KMAC256 has a WithCustomization counterpart for domain separation across distinct usages of the same key. HMAC-SHA256 and HMAC-BLAKE3 take the key as their only argument — there is no separate WithKey variant since the key is already the only state the factory holds.

Easy Mode — Quick start

easy.New (Single Ouroboros) and easy.New3 (Triple Ouroboros) build a high-level *easy.Encryptor around a single hash primitive plus a single MAC primitive chosen from the canonical list above. The MAC argument flips the encryptor's EncryptAuth / DecryptAuth path between the three options: "hmac-blake3" (default — fastest under AVX-512+VL), "hmac-sha256" (dedicated HMAC-SHA-256, the conservative pick), and "kmac256" (KMAC256 — slowest under AVX-512+VL on hosts where the KMAC256 single-call asm is engaged). The MAC key is generated fresh from crypto/rand alongside the seed material at construction; the state blob carries the MAC key + name and the receiver restores everything via Import. Two encryptors with different MAC choices can run concurrently without cross-contamination — the per- instance *itb.Config snapshot isolates each.


// 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 constructor — picks the BLAKE3 PRF on the
	// hash side and HMAC-BLAKE3 on the MAC side. Triple Ouroboros
	// (7 seeds) → easy.New3(...). The MAC choice is independent of
	// the hash choice; any valid (primitive × MAC) pair works.
	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 + name,
	// 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 via the bound MAC closure (here
	// HMAC-BLAKE3) 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 = ..., ...

	// Peek at the blob's metadata before constructing a matching
	// encryptor — primitive / key_bits / mode / mac must all agree
	// with the sender or Import returns easy.ErrMismatch with the
	// offending field tag.
	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. DecryptAuth
	// surfaces MAC verification failures as itb.ErrMACFailure on the
	// underlying error chain.
	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 MAC primitive is a separate one-per- encryptor choice (one of kmac256 / hmac-sha256 / hmac-blake3) and rides through the same constructor. The state blob carries per-slot primitives + per-slot PRF keys + the MAC key + name; 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))

}

Why these three

ITB's MAC-Inside-Encrypt construction places the 32-byte tag inside the encrypted container. The barrier dispersal (process128 / 256 / 512) destroys the plaintext / tag boundary an attacker could otherwise observe; under SetLockSoup(1) the bit-permutation layer further obscures the payload region. So the MAC primitive itself only has to be a sound keyed PRF — the surrounding ITB construction handles placement-hiding, replay-resistance (per-message nonce), and CCA-resistance.

Three primitives keep the choice tractable:

  • kmac256 — modern NIST-standard keyed XOF (SP 800-185), based on the well-vetted Keccak permutation. But slowest.
  • hmac-sha256 — universal interoperability standard, hardware-accelerated through SHA-NI on amd64 / arm64 where the underlying CPU exposes the SHA-256 round instructions.
  • hmac-blake3 — fastest of the three through BLAKE3's AVX-512 ASM kernel.

Validation

  • hmac-sha256 is bit-exactly cross-checked against RFC 4231 test vectors in macs_test.go.
  • hmac-blake3 rests on the upstream github.com/zeebo/blake3 project's own keyed-mode KAT.
  • kmac256 is bit-exactly cross-checked against four KAT vectors generated from pycryptodome 3.23.0 (Crypto.Hash.KMAC256, an audited NIST SP 800-185 reference implementation): three are L = 256 analogues of NIST SP 800-185 Annex A samples 4 / 5 / 6 (sample 4 message 00 01 02 03, sample 5 with customization My Tagged Application, sample 6 with the 200-byte 0x00..0xC7 message), plus a degenerate empty-message case. Reproduce via the python snippet shown in the test file.
  • All three primitives pass TestITBAuthIntegration (3 MACs × 3 hash widths × encrypt/decrypt round trip + bit-flip tamper rejection).

Documentation

Overview

Package macs provides cached, pre-keyed wrappers around the three PRF-grade Message Authentication Codes that ITB ships with as built-in factories for the C / FFI / mobile shared-library distribution.

All three primitives produce a 32-byte tag and accept a 32-byte (or longer in the HMAC case) key. The shared 32-byte tag size means consumers do not have to vary their authenticated-payload layout based on which MAC was selected — a binding-friendly invariant.

Canonical names (FFI-stable iteration order, exposed via the shared library's ITB_MACName entry point):

kmac256, hmac-sha256, hmac-blake3

Every factory takes a key byte slice and returns a closure matching itb.MACFunc (`func(data []byte) []byte`). The closure pre-keys its primitive once (cached cSHAKE256 absorb-state for KMAC256, sync.Pool of pre-keyed hmac.Hash instances for HMAC-SHA256, blake3.Hasher template for HMAC-BLAKE3) so per-call invocation carries no key-derivation overhead.

Standards conformance:

  • kmac256 — NIST SP 800-185 Section 4.3.1, output L = 256 bits; bit-exact KAT cross-checked against pycryptodome's KMAC256 implementation.
  • hmac-sha256 — RFC 2104 / FIPS 198-1 with SHA-256; bit-exact KAT against RFC 4231 vectors.
  • hmac-blake3 — BLAKE3 native keyed mode (BLAKE3 spec §6), covered by upstream zeebo/blake3 keyed-mode KAT and the ITB Auth round-trip integration test in this package.

Why these three. ITB's MAC-Inside-Encrypt construction places the 32-byte tag inside the encrypted container, where the barrier dispersal (process256 / process128 / process512) already destroys any plaintext / tag boundary the attacker could see; under SetLockSoup(1) the bit-permutation layer further obscures the payload region. Combined, this means the MAC primitive itself only has to be a sound keyed PRF — the surrounding ITB construction takes care of placement-hiding, replay-resistance (via the per-message nonce), and CCA-resistance.

All three factories are PRF-grade and stateless across the FFI boundary; concurrent goroutines may call the returned closure in parallel.

Index

Constants

This section is empty.

Variables

View Source
var Registry = [3]Spec{
	{Name: "kmac256", KeySize: 32, TagSize: 32, MinKeyBytes: 16},
	{Name: "hmac-sha256", KeySize: 32, TagSize: 32, MinKeyBytes: 16},
	{Name: "hmac-blake3", KeySize: 32, TagSize: 32, MinKeyBytes: 32},
}

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

Functions

func HMACBLAKE3

func HMACBLAKE3(key []byte) (itb.MACFunc, error)

HMACBLAKE3 returns a cached BLAKE3-keyed itb.MACFunc.

BLAKE3 has a native keyed mode (`blake3.NewKeyed(key32)`) that derives the per-message hashing state from a 32-byte key once; this is the recommended construction for "HMAC-BLAKE3" (no nested HMAC wrapper is required because BLAKE3-keyed is itself a sound keyed PRF — see the BLAKE3 spec, section 6).

Caching strategy: the keyed template hasher is built once at factory construction. Each call clones the template (cheap — internal state copy), writes the data, finalizes to 32 bytes, and is discarded. Concurrent goroutines may invoke the returned closure in parallel since each holds its own clone.

Key length must be exactly 32 bytes. Shorter keys are rejected.

func HMACSHA256

func HMACSHA256(key []byte) (itb.MACFunc, error)

HMACSHA256 returns a cached HMAC-SHA256 itb.MACFunc keyed by key. Tag size is 32 bytes regardless of key length. HMAC accepts keys of arbitrary length (RFC 2104) — short keys get zero-padded to the SHA-256 block size, long keys get hashed first; the per-package Make dispatcher applies a 16-byte minimum on top of this for ITB keying-discipline reasons, but HMACSHA256 itself only rejects an empty key.

Caching strategy: the underlying hash.Hash returned by hmac.New(sha256.New, key) carries the key-XOR'd ipad SHA256 state after construction. A sync.Pool of these instances lets each call take a pre-keyed hasher from the pool, Reset() it (restoring to post-ipad state), Write the data, finalize, and return it — no per-call key-derivation cost. The pool is the standard idiom for reusing mutable hasher state across goroutines.

func KMAC256

func KMAC256(key []byte) (itb.MACFunc, error)

KMAC256 returns a cached KMAC256 itb.MACFunc keyed by key.

KMAC256 is defined in NIST SP 800-185 Section 4 as

KMAC256(K, X, L, S) = cSHAKE256(newX, L, "KMAC", S)

where newX = bytepad(encode_string(K), 136) || X || right_encode(L) and L is the requested output length in bits (256 here, 32 bytes).

The shipped factory uses an empty customization string S; pass a non-empty S via KMAC256WithCustomization when domain separation across multiple distinct usages of the same key is required.

Caching strategy: cSHAKE256 carries internal sponge state, and the absorb phase up through bytepad(encode_string(K), 136) is keyspecific. The factory absorbs that prefix into a template cSHAKE256 once, then clones the template per call to absorb the message and emit 32 output bytes. Cloning is cheap (sponge state copy) and concurrent goroutines may invoke the closure in parallel.

Key length must be at least 16 bytes (NIST SP 800-185 places no hard lower bound, but ITB enforces 16-byte minimum to stay aligned with its own keying discipline).

func KMAC256WithCustomization

func KMAC256WithCustomization(key, customization []byte) (itb.MACFunc, error)

KMAC256WithCustomization is KMAC256 with a non-empty customization string S, intended for callers that need domain separation across multiple distinct usages of the same key.

func Make

func Make(name string, key []byte) (itb.MACFunc, error)

Make returns a fresh cached itb.MACFunc for the named primitive, keyed by key. Returns an error when name is unknown or key is shorter than the primitive's MinKeyBytes.

Types

type Spec

type Spec struct {
	Name        string // canonical FFI-stable identifier
	KeySize     int    // recommended key size in bytes
	TagSize     int    // tag size in bytes (constant per primitive)
	MinKeyBytes int    // minimum acceptable key length (for HMAC variants)
}

Spec describes one shipped MAC primitive.

func Find

func Find(name string) (Spec, bool)

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

Jump to

Keyboard shortcuts

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