diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..eff1926 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,27 @@ +# Contributing to Cryptonite-go + +## Development Setup +```bash +git clone https://github.com/AeonDave/cryptonite-go +cd cryptonite-go +go test ./... +``` + +## Guidelines + +- **Tests required**: Add KAT vectors for new algorithms +- **Formatting**: Run `go fmt ./...` and `golangci-lint run` +- **Performance**: Include benchmarks in `test/` +- **Documentation**: Update docs/ if adding features + +## Adding Test Vectors + +See [docs/TESTING.md](docs/TESTING.md) for KAT format. + +## PR Checklist + +- [ ] Tests pass (`go test -race ./...`) +- [ ] Benchmarks included +- [ ] Documentation updated +- [ ] Commit messages descriptive + diff --git a/README.md b/README.md index 0ff185e..75f3502 100644 --- a/README.md +++ b/README.md @@ -28,151 +28,41 @@ Minimal, modern, ultra-fast, dependency-free cryptography go library, using only go get github.com/AeonDave/cryptonite-go ``` -## Supported algorithms - -### AEAD - -| Algorithm | Constructor(s) | Key | Nonce | Tag | Notes | RFC / Spec | -|--------------------|------------------------------------------------|-----------|---------------------|-----|------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------| -| ASCON-128a | `aead.NewAscon128()` | 16B | 16B | 16B | NIST LwC winner | [FIPS 208](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.208.pdf) | -| ASCON-80pq | `aead.NewAscon80pq()` | 20B | 16B | 16B | PQ-hardened variant | [FIPS 208](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.208.pdf) | -| GIFT-COFB | `aead.NewGiftCofb()` | 16B | 16B | 16B | Ultra-lightweight finalist | [IACR 2018/803](https://eprint.iacr.org/2018/803.pdf) | -| SKINNY-AEAD-M1 | `aead.NewSkinnyAead()` | 16B | 16B | 16B | Tweakable block-cipher AEAD (128-bit tag) | [NIST LwC Round 1 submission](https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-1/submissions/SKINNY.pdf) | -| Xoodyak-Encrypt | `aead.NewXoodyak()` | 16B | 16B | 16B | Cyclist mode | [Xoodyak specification](https://keccak.team/files/Xoodyak-specification.pdf) | -| ChaCha20-Poly1305 | `aead.NewChaCha20Poly1305()` | 32B | 12B | 16B | RFC 8439 layout | [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439.html) | -| XChaCha20-Poly1305 | `aead.NewXChaCha20Poly1305()` | 32B | 24B | 16B | Derives nonce via HChaCha20 | [draft-irtf-cfrg-xchacha-03](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03) | -| AES-GCM | `aead.NewAESGCM()` | 16/24/32B | 12B | 16B | AES-NI optional | [NIST SP 800-38D](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf) | -| AES-GCM-SIV | `aead.NewAesGcmSiv()` | 16/32B | 12B | 16B | Nonce misuse resistant | [RFC 8452](https://www.rfc-editor.org/rfc/rfc8452.html) | -| AES-SIV (128/256) | `aead.NewAES128SIV()`
`aead.NewAES256SIV()` | 32B / 64B | Deterministic (AAD) | 16B | Deterministic SIV construction; optional multi-AD support via `aead.MultiAssociatedData` | [RFC 5297](https://www.rfc-editor.org/rfc/rfc5297.html) | -| Deoxys-II-256-128 | `aead.NewDeoxysII128()` | 32B | 15B | 16B | NIST LwC finalist | [NIST LWC finalist spec](https://csrc.nist.gov/csrc/media/Projects/lightweight-cryptography/documents/finalists/deoxys-spec-final.pdf) | - -### Hashing - -Every hashing entry point lives under the `hash` package so callers can rely on the uniform `hash.Hasher` interface or -the Go `hash.Hash` type without importing algorithm-specific subpackages. - -| Algorithm | Streaming constructor | Single-shot helper(s) | Notes | RFC / Spec | -|--------------|--------------------------------------------------|-------------------------------------------------|----------------------------------------------------|------------------------------------------------------------------------------| -| SHA3-224 | `hash.NewSHA3224()` | `hash.NewSHA3224Hasher()` / `hash.Sum224` | 224-bit (28B) digest | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | -| SHA3-256 | `hash.NewSHA3256()` | `hash.NewSHA3256Hasher()` / `hash.Sum256` | 256-bit (32B) digest | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | -| SHA3-384 | `hash.NewSHA3384()` | `hash.NewSHA3384Hasher()` / `hash.Sum384` | 384-bit (48B) digest | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | -| SHA3-512 | `hash.NewSHA3512()` | `hash.NewSHA3512Hasher()` / `hash.Sum512` | 512-bit (64B) digest | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | -| BLAKE2b | `hash.NewBlake2b()` / `hash.NewBlake2bBuilder()` | `hash.NewBlake2bHasher()` | Configurable 1–64B digest, optional keyed MAC mode | [RFC 7693](https://www.rfc-editor.org/rfc/rfc7693.html) | -| BLAKE2s | `hash.NewBlake2s()` / `hash.NewBlake2sBuilder()` | `hash.NewBlake2sHasher()` | Configurable 1–32B digest, optional keyed MAC mode | [RFC 7693](https://www.rfc-editor.org/rfc/rfc7693.html) | -| Xoodyak Hash | `hash.NewXoodyak()` | `hash.NewXoodyakHasher()` / `hash.SumXoodyak()` | 32B Cyclist hash | [Xoodyak specification](https://keccak.team/files/Xoodyak-specification.pdf) | - -#### SP 800-185 constructions - -| Algorithm | Helper(s) | Notes | RFC / Spec | -|-----------------------|----------------------------------------------------------------------------------------|-----------------------------------------------|----------------------------------------------------------------------------------------------| -| TupleHash128 / 256 | `hash.TupleHash128(tuple, outLen, customization)` / `hash.TupleHash256` | Tuple of byte-strings, optional customization | [NIST SP 800-185](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf) | -| ParallelHash128 / 256 | `hash.ParallelHash128(msg, blockSize, outLen, customization)` / `hash.ParallelHash256` | Parallel-friendly hashing for large messages | [NIST SP 800-185](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf) | - -### XOF (Extendable-output function) - -Constructors live under the dedicated `xof` package and return the shared `xof.XOF` interface so extendable-output -primitives can be swapped transparently. - -| Algorithm | Constructor | Notes | RFC / Spec | -|-------------|------------------|--------------------------------------------|------------------------------------------------------------------------------| -| SHAKE128 | `xof.SHAKE128()` | Arbitrary-length output (FIPS 202) | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | -| SHAKE256 | `xof.SHAKE256()` | Wider security margin, arbitrary output | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | -| BLAKE2b XOF | `xof.Blake2b()` | Supports fixed-length and streaming output | [BLAKE2 XOF](https://www.blake2.net/blake2x.pdf) | -| BLAKE2s XOF | `xof.Blake2s()` | Lightweight XOF with keyed support | [BLAKE2 XOF](https://www.blake2.net/blake2x.pdf) | -| Xoodyak XOF | `xof.Xoodyak()` | Cyclist extendable-output mode | [Xoodyak specification](https://keccak.team/files/Xoodyak-specification.pdf) | - -### KDF (Key derivation function) - -| Algorithm | Deriver constructor | Single-shot helper(s) | Notes | RFC / Spec | -|---------------------|------------------------------------------------------|-----------------------------------------------------------------------------|--------------------------------------------------------------|---------------------------------------------------------| -| HKDF-SHA256 | `kdf.NewHKDFSHA256()` | `kdf.HKDFSHA256()`
`kdf.HKDFSHA256Extract()`
`kdf.HKDFSHA256Expand()` | Max length 255 × 32B (RFC 5869) | [RFC 5869](https://www.rfc-editor.org/rfc/rfc5869.html) | -| HKDF (generic hash) | `kdf.NewHKDF(func() hash.Hash)` | `kdf.HKDF()`
`kdf.HKDFExtractWith()`
`kdf.HKDFExpandWith()` | Length bound = 255 × hash.Size() | [RFC 5869](https://www.rfc-editor.org/rfc/rfc5869.html) | -| HKDF-BLAKE2b | `kdf.NewHKDFBlake2b()` | `kdf.HKDFBlake2b()` | 64B digest variant | [RFC 5869](https://www.rfc-editor.org/rfc/rfc5869.html) | -| PBKDF2-SHA1 | `kdf.NewPBKDF2SHA1()` | `kdf.PBKDF2SHA1()`
`kdf.PBKDF2SHA1Into()` | See `kdf.CheckParams` for policy checks | [RFC 8018](https://www.rfc-editor.org/rfc/rfc8018.html) | -| PBKDF2-SHA256 | `kdf.NewPBKDF2SHA256()` | `kdf.PBKDF2SHA256()`
`kdf.PBKDF2SHA256Into()` | Iterations > 0; variable output length | [RFC 8018](https://www.rfc-editor.org/rfc/rfc8018.html) | -| Argon2id | `kdf.NewArgon2id()`
`kdf.NewArgon2idWithParams()` | `kdf.Argon2id()` | RFC 9106 Argon2id; defaults to time=1, memory=64MiB, lanes=1 | [RFC 9106](https://www.rfc-editor.org/rfc/rfc9106.html) | -| scrypt | `kdf.NewScrypt(n, r, p)` | `kdf.Scrypt()` | RFC 7914 constraints on n,r,p; variable output length | [RFC 7914](https://www.rfc-editor.org/rfc/rfc7914.html) | - -### MAC (Message authentication code) - -| Algorithm | Entry points | Key | Tag | Notes | RFC / Spec | -|-------------|-------------------------------------------------------------------------|----------------|-----|-------------------------------------|---------------------------------------------------------| -| HMAC-SHA256 | `mac.Sum(key, data)`
`mac.Verify(key, data, tag)` | Any length | 32B | Single-shot helpers over SHA-256 | [RFC 2104](https://www.rfc-editor.org/rfc/rfc2104.html) | -| Poly1305 | `mac.NewPoly1305(key)`
`mac.SumPoly1305()`
`mac.VerifyPoly1305()` | 32B (one-time) | 16B | One-time key per message (RFC 7539) | [RFC 7539](https://www.rfc-editor.org/rfc/rfc7539.html) | - -### Stream ciphers - -`stream.NewChaCha20` and `stream.NewXChaCha20` expose the shared `stream.Stream` interface (with `Reset`, `KeyStream`, -and `XORKeyStream`) so applications can swap keystream generators without touching call sites. - -| Algorithm | Constructor | Key | Nonce | Notes | RFC / Spec | -|-----------|-------------------------|-----------|-------|------------------------------------------------|---------------------------------------------------------------------------| -| AES-CTR | `stream.NewAESCTR()` | 16/24/32B | 12B | 96-bit nonce with 32-bit counter (NIST layout) | [NIST SP 800-38A](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf) | -| ChaCha20 | `stream.NewChaCha20()` | 32B | 12B | IETF variant with configurable counter | [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439.html) | -| XChaCha20 | `stream.NewXChaCha20()` | 32B | 24B | HChaCha20-derived subkeys and raw keystream | [draft-irtf-cfrg-xchacha-03](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03) | - - -### Block ciphers - -Block primitives are instantiated through `block.NewAES128` / `block.NewAES256`, both returning the shared -`block.Cipher` interface. - -| Algorithm | Constructor | Key | Block | Notes | RFC / Spec | -|-----------|---------------------|-----|-------|------------------------------|----------------------------------------------------------------------| -| AES-128 | `block.NewAES128()` | 16B | 16B | Thin wrapper over stdlib AES | [FIPS 197](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf) | -| AES-256 | `block.NewAES256()` | 32B | 16B | Thin wrapper over stdlib AES | [FIPS 197](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf) | - -### Signatures (ECDSA, EdDSA) - -| Algorithm | Constructor(s) | Public | Private | Signature | Notes | RFC / Spec | -|-------------|----------------------|--------------------|------------|----------------------|---------------------------------------------------------------------------------|--------------------------------------------------------------------------| -| Ed25519 | `sig.NewEd25519()` | 32B | 64B | 64B | Deterministic; `sig.FromSeed(32B)` supported | [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032.html) | -| ECDSA P-256 | `sig.NewECDSAP256()` | 65B (uncompressed) | 32B scalar | ASN.1 DER (variable) | Helpers: `sig.GenerateKeyECDSAP256`, `sig.SignECDSAP256`, `sig.VerifyECDSAP256` | [FIPS 186-5](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf) | - -Ed25519 builds directly on Go's standard library implementation (`crypto/ed25519`) and re-exports the canonical buffer -sizes via `sig.PublicKeySize`, `sig.PrivateKeySize`, `sig.SeedSize`, and `sig.SignatureSize`. The package also provides -an alias for `ed25519.Options` together with helpers that cover all RFC 8032 variants: - -- `sig.SignWithOptions(priv, msg, opts)` – supports standard, context-bound, and pre-hash signing through the stdlib's - `ed25519.PrivateKey.Sign` API. -- `sig.VerifyWithOptions(pub, msg, sig, opts)` – validates inputs, defaults to RFC 8032 parameters, and dispatches to - `crypto/ed25519.VerifyWithOptions`. - -These additions allow advanced Ed25519 flows without reimplementing the algorithm or importing `crypto/ed25519` at call -sites. - -| Algorithm | Constructor | Public | Private | Shared | Notes | RFC / Spec | -|-----------|------------------|--------------------|------------|--------|-----------------------------------------|--------------------------------------------------------------------------| -| X25519 | `ecdh.NewX25519()` | 32B | 32B | 32B | RFC 7748 (crypto/ecdh) | [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748.html) | -| P-256 | `ecdh.NewP256()` | 65B (uncompressed) | 32B scalar | 32B | Uncompressed public: 0x04 \|\| X \|\| Y | [FIPS 186-5](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf) | -| P-384 | `ecdh.NewP384()` | 97B (uncompressed) | 48B scalar | 48B | Uncompressed public: 0x04 \|\| X \|\| Y | [FIPS 186-5](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf) | - -### Post-quantum key encapsulation - -The `kem` package defines a shared `kem.KEM` interface together with a deployable -hybrid construction: - -* `kem.New()` - classical KEM adapter built on top of the existing - `ecdh` helpers (deployable today, pure Go, stdlib only). The adapter lives in - the `kem` package to highlight that it provides classical security and can be - reused by non-PQ code paths. -* `pq.NewHybridX25519()` - versioned hybrid format that composes the X25519 - exchange with an optional ML-KEM component. Callers can inject a vetted ML-KEM - implementation via `pq.NewHybrid(classical, mlkem)` without changing encoded - formats or downstream APIs. +## Supported Algorithms -Key material and ciphertexts produced by the hybrid construction are encoded as -`version || len(classical) || classical || len(pq) || pq`, providing forwards -compatibility when the PQ component is introduced. - -To encrypt payloads, the package also includes the convenience `pq.Seal` and -`pq.Open` helpers which perform the standard `KEM → HKDF → AEAD` flow. The -envelope format embeds the encapsulated key (length-prefixed), a key-schedule -identifier, and the AEAD ciphertext so that the receiver can deterministically -reproduce the derived key/nonce pair. The key schedule currently covers modern -AEADs such as ChaCha20-Poly1305, AES-256-GCM, AES-GCM-SIV, XChaCha20-Poly1305, -ASCON-128a, Deoxys-II-256-128, and the AES-SIV family. +### AEAD (Authenticated Encryption) +- **Mainstream**: AES-GCM, ChaCha20-Poly1305, XChaCha20-Poly1305, AES-GCM-SIV +- **Lightweight**: ASCON-128a/80pq ⭐ (NIST winner), Xoodyak, GIFT-COFB, SKINNY, Deoxys-II +- **Nonce-misuse resistant**: AES-SIV, AES-GCM-SIV +### Hashing & XOF +- **Fast**: BLAKE2b/s (742 MB/s), SHA-3 family +- **Streaming**: SHAKE128/256, BLAKE2 XOF, Xoodyak +- **Specialized**: TupleHash, ParallelHash (SP 800-185) + +### Key Derivation (KDF) +- **Modern**: HKDF-SHA256/BLAKE2b, Argon2id, scrypt +- **Password**: PBKDF2-SHA1/SHA256 + +### MAC & Stream Ciphers +- **MAC**: HMAC-SHA256, Poly1305 (3+ GB/s) +- **Stream**: ChaCha20, XChaCha20, AES-CTR + +### Public Key Crypto +- **Signatures**: Ed25519, ECDSA P-256 +- **Key Exchange**: X25519, ECDH P-256/P-384 +- **Post-Quantum**: Hybrid X25519+ML-KEM ready (via `pq` package) + +
+📋 Full algorithm matrix with specs + +See [docs/ALGORITHMS.md](docs/ALGORITHMS.md) for: +- Complete key/nonce/tag sizes +- RFC/FIPS references +- Interoperability notes +- Test vectors + +
## API Common interface in `aead/aead.go`: diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..146b7bc --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,22 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| main | :white_check_mark: | + +## Reporting a Vulnerability + +**DO NOT open public issues for security vulnerabilities.** + +Email: [your-email] or use GitHub's private vulnerability reporting. + +We aim to respond within 48 hours. + +## Known Limitations + +- No independent security audit conducted +- Constant-time guarantees: best-effort (review before deploying in side-channel sensitive environments) +- Caller responsible for nonce uniqueness and key zeroization + diff --git a/docs/ALGORITHMS.md b/docs/ALGORITHMS.md new file mode 100644 index 0000000..ee5241c --- /dev/null +++ b/docs/ALGORITHMS.md @@ -0,0 +1,140 @@ +# Algorithm Reference + +This document captures the complete algorithm matrix for Cryptonite-go. It mirrors the tables previously hosted in +`README.md` and includes key sizes, nonce formats, tags, and primary specification references. + +## AEAD + +| Algorithm | Constructor(s) | Key | Nonce | Tag | Notes | RFC / Spec | +|--------------------|------------------------------------------------|-----------|---------------------|-----|-----------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------| +| ASCON-128a | `aead.NewAscon128()` | 16B | 16B | 16B | NIST LwC winner | [FIPS 208](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.208.pdf) | +| ASCON-80pq | `aead.NewAscon80pq()` | 20B | 16B | 16B | PQ-hardened variant | [FIPS 208](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.208.pdf) | +| GIFT-COFB | `aead.NewGiftCofb()` | 16B | 16B | 16B | Ultra-lightweight finalist | [IACR 2018/803](https://eprint.iacr.org/2018/803.pdf) | +| SKINNY-AEAD-M1 | `aead.NewSkinnyAead()` | 16B | 16B | 16B | Tweakable block-cipher AEAD (128-bit tag) | [NIST LwC Round 1 submission](https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-1/submissions/SKINNY.pdf) | +| Xoodyak-Encrypt | `aead.NewXoodyak()` | 16B | 16B | 16B | Cyclist mode | [Xoodyak specification](https://keccak.team/files/Xoodyak-specification.pdf) | +| ChaCha20-Poly1305 | `aead.NewChaCha20Poly1305()` | 32B | 12B | 16B | RFC 8439 layout | [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439.html) | +| XChaCha20-Poly1305 | `aead.NewXChaCha20Poly1305()` | 32B | 24B | 16B | Derives nonce via HChaCha20 | [draft-irtf-cfrg-xchacha-03](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03) | +| AES-GCM | `aead.NewAESGCM()` | 16/24/32B | 12B | 16B | AES-NI optional | [NIST SP 800-38D](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf) | +| AES-GCM-SIV | `aead.NewAesGcmSiv()` | 16/32B | 12B | 16B | Nonce misuse resistant | [RFC 8452](https://www.rfc-editor.org/rfc/rfc8452.html) | +| AES-SIV (128/256) | `aead.NewAES128SIV()`
`aead.NewAES256SIV()` | 32B / 64B | Deterministic (AAD) | 16B | Deterministic SIV construction; optional multi-AD support via `aead.MultiAssociatedData` | [RFC 5297](https://www.rfc-editor.org/rfc/rfc5297.html) | +| Deoxys-II-256-128 | `aead.NewDeoxysII128()` | 32B | 15B | 16B | NIST LwC finalist | [NIST LWC finalist spec](https://csrc.nist.gov/csrc/media/Projects/lightweight-cryptography/documents/finalists/deoxys-spec-final.pdf) | + +## Hashing + +Every hashing entry point lives under the `hash` package so callers can rely on the uniform `hash.Hasher` interface or +the Go `hash.Hash` type without importing algorithm-specific subpackages. + +| Algorithm | Streaming constructor | Single-shot helper(s) | Notes | RFC / Spec | +|--------------|--------------------------------------------------|-------------------------------------------------|-------------------------------------------------|------------------------------------------------------------------------------| +| SHA3-224 | `hash.NewSHA3224()` | `hash.NewSHA3224Hasher()` / `hash.Sum224` | 224-bit (28B) digest | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | +| SHA3-256 | `hash.NewSHA3256()` | `hash.NewSHA3256Hasher()` / `hash.Sum256` | 256-bit (32B) digest | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | +| SHA3-384 | `hash.NewSHA3384()` | `hash.NewSHA3384Hasher()` / `hash.Sum384` | 384-bit (48B) digest | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | +| SHA3-512 | `hash.NewSHA3512()` | `hash.NewSHA3512Hasher()` / `hash.Sum512` | 512-bit (64B) digest | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | +| BLAKE2b | `hash.NewBlake2b()` / `hash.NewBlake2bBuilder()` | `hash.NewBlake2bHasher()` | Configurable 1–64B digest, optional keyed MAC mode | [RFC 7693](https://www.rfc-editor.org/rfc/rfc7693.html) | +| BLAKE2s | `hash.NewBlake2s()` / `hash.NewBlake2sBuilder()` | `hash.NewBlake2sHasher()` | Configurable 1–32B digest, optional keyed MAC mode | [RFC 7693](https://www.rfc-editor.org/rfc/rfc7693.html) | +| Xoodyak Hash | `hash.NewXoodyak()` | `hash.NewXoodyakHasher()` / `hash.SumXoodyak()` | 32B Cyclist hash | [Xoodyak specification](https://keccak.team/files/Xoodyak-specification.pdf) | + +### SP 800-185 constructions + +| Algorithm | Helper(s) | Notes | RFC / Spec | +|-----------------------|----------------------------------------------------------------------------------------|---------------------------------------------------|----------------------------------------------------------------------------------------------| +| TupleHash128 / 256 | `hash.TupleHash128(tuple, outLen, customization)` / `hash.TupleHash256` | Tuple of byte-strings, optional customization | [NIST SP 800-185](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf) | +| ParallelHash128 / 256 | `hash.ParallelHash128(msg, blockSize, outLen, customization)` / `hash.ParallelHash256` | Parallel-friendly hashing for large messages | [NIST SP 800-185](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf) | + +## XOF (Extendable-output function) + +Constructors live under the dedicated `xof` package and return the shared `xof.XOF` interface so extendable-output +primitives can be swapped transparently. + +| Algorithm | Constructor | Notes | RFC / Spec | +|-------------|------------------|--------------------------------------------|-------------------------------------------------| +| SHAKE128 | `xof.NewShake128()` | 256-bit security level; arbitrary output length | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | +| SHAKE256 | `xof.NewShake256()` | 512-bit security level; arbitrary output length | [FIPS 202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | +| BLAKE2XOF | `xof.NewBlake2XOF()` | BLAKE2b-based extendable-output mode | [RFC 7693](https://www.rfc-editor.org/rfc/rfc7693.html) | +| Xoodyak XOF | `xof.NewXoodyakXOF()` | Cyclist XOF variant | [Xoodyak specification](https://keccak.team/files/Xoodyak-specification.pdf) | + +## Key Derivation (KDF) + +| Algorithm | Constructor / Helper(s) | Notes | RFC / Spec | +|-----------|--------------------------------------------------------------|--------------------------------------------------------------|-----------------------------------------------------------------------------| +| HKDF | `kdf.NewHKDF(sha256)` / `kdf.NewHKDF(blake2b)` | Modern extract-and-expand with pluggable hash | [RFC 5869](https://www.rfc-editor.org/rfc/rfc5869.html) | +| Argon2id | `kdf.Argon2id(params)` | Memory-hard password hashing | [RFC 9106](https://www.rfc-editor.org/rfc/rfc9106.html) | +| scrypt | `kdf.Scrypt(params)` | Memory-hard password hashing | [RFC 7914](https://www.rfc-editor.org/rfc/rfc7914.html) | +| PBKDF2 | `kdf.PBKDF2(password, salt, iter, keyLen, hashFunc)` | Password-based KDF with SHA-1 / SHA-256 | [PKCS #5 v2.1](https://www.rfc-editor.org/rfc/rfc8018.html) | + +## MAC + +| Algorithm | Helper(s) | Key | Tag | Notes | RFC / Spec | +|-------------|---------------------------------------------------------------------|------------|-----|------------------------------------------------|--------------------------------------------------| +| HMAC-SHA256 | `mac.Sum(key, data)`
`mac.Verify(key, data, tag)` | Any length | 32B | Single-shot helpers over SHA-256 | [RFC 2104](https://www.rfc-editor.org/rfc/rfc2104.html) | +| Poly1305 | `mac.NewPoly1305(key)`
`mac.SumPoly1305()`
`mac.VerifyPoly1305()` | 32B (one-time) | 16B | One-time key per message (RFC 7539) | [RFC 7539](https://www.rfc-editor.org/rfc/rfc7539.html) | + +## Stream ciphers + +`stream.NewChaCha20` and `stream.NewXChaCha20` expose the shared `stream.Stream` interface (with `Reset`, `KeyStream`, +and `XORKeyStream`) so applications can swap keystream generators without touching call sites. + +| Algorithm | Constructor | Key | Nonce | Notes | RFC / Spec | +|-----------|-------------------------|-----------|-------|------------------------------------------------|----------------------------------------------------------------------------------------| +| AES-CTR | `stream.NewAESCTR()` | 16/24/32B | 12B | 96-bit nonce with 32-bit counter (NIST layout) | [NIST SP 800-38A](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf) | +| ChaCha20 | `stream.NewChaCha20()` | 32B | 12B | IETF variant with configurable counter | [RFC 8439](https://www.rfc-editor.org/rfc/rfc8439.html) | +| XChaCha20 | `stream.NewXChaCha20()` | 32B | 24B | HChaCha20-derived subkeys and raw keystream | [draft-irtf-cfrg-xchacha-03](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03) | + +## Block ciphers + +Block primitives are instantiated through `block.NewAES128` / `block.NewAES256`, both returning the shared +`block.Cipher` interface. + +| Algorithm | Constructor | Key | Block | Notes | RFC / Spec | +|-----------|---------------------|-----|-------|------------------------------|--------------------------------------------------| +| AES-128 | `block.NewAES128()` | 16B | 16B | Thin wrapper over stdlib AES | [FIPS 197](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf) | +| AES-256 | `block.NewAES256()` | 32B | 16B | Thin wrapper over stdlib AES | [FIPS 197](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf) | + +## Signatures + +| Algorithm | Constructor(s) | Public | Private | Signature | Notes | RFC / Spec | +|-------------|----------------------|--------------------|------------|----------------------|------------------------------------------------------|--------------------------------------------------------------------------| +| Ed25519 | `sig.NewEd25519()` | 32B | 64B | 64B | Deterministic; `sig.FromSeed(32B)` supported | [RFC 8032](https://www.rfc-editor.org/rfc/rfc8032.html) | +| ECDSA P-256 | `sig.NewECDSAP256()` | 65B (uncompressed) | 32B scalar | ASN.1 DER (variable) | Helpers: `sig.GenerateKeyECDSAP256`, `sig.SignECDSAP256`, `sig.VerifyECDSAP256` | [FIPS 186-5](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf) | + +Ed25519 builds directly on Go's standard library implementation (`crypto/ed25519`) and re-exports the canonical buffer +sizes via `sig.PublicKeySize`, `sig.PrivateKeySize`, `sig.SeedSize`, and `sig.SignatureSize`. The package also provides +an alias for `ed25519.Options` together with helpers that cover all RFC 8032 variants: + +- `sig.SignWithOptions(priv, msg, opts)` – supports standard, context-bound, and pre-hash signing through the stdlib's + `ed25519.PrivateKey.Sign` API. +- `sig.VerifyWithOptions(pub, msg, sig, opts)` – validates inputs, defaults to RFC 8032 parameters, and dispatches to + `crypto/ed25519.VerifyWithOptions`. + +These additions allow advanced Ed25519 flows without reimplementing the algorithm or importing `crypto/ed25519` at call +sites. + +## Key Exchange (ECDH) + +| Algorithm | Constructor | Public | Private | Shared | Notes | RFC / Spec | +|-----------|------------------|--------------------|------------|--------|-----------------------------------------|--------------------------------------------------------------------------| +| X25519 | `ecdh.NewX25519()` | 32B | 32B | 32B | RFC 7748 (crypto/ecdh) | [RFC 7748](https://www.rfc-editor.org/rfc/rfc7748.html) | +| P-256 | `ecdh.NewP256()` | 65B (uncompressed) | 32B scalar | 32B | Uncompressed public: 0x04 || X || Y | [FIPS 186-5](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf) | +| P-384 | `ecdh.NewP384()` | 97B (uncompressed) | 48B scalar | 48B | Uncompressed public: 0x04 || X || Y | [FIPS 186-5](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf) | + +## Post-quantum key encapsulation + +The `kem` package defines a shared `kem.KEM` interface together with a deployable hybrid construction: + +- `kem.New()` - classical KEM adapter built on top of the existing `ecdh` helpers (deployable today, pure Go, stdlib only). + The adapter lives in the `kem` package to highlight that it provides classical security and can be reused by non-PQ + code paths. +- `pq.NewHybridX25519()` - versioned hybrid format that composes the X25519 exchange with an optional ML-KEM component. + Callers can inject a vetted ML-KEM implementation via `pq.NewHybrid(classical, mlkem)` without changing encoded + formats or downstream APIs. + +Key material and ciphertexts produced by the hybrid construction are encoded as +`version || len(classical) || classical || len(pq) || pq`, providing forwards compatibility when the PQ component is +introduced. + +To encrypt payloads, the package also includes the convenience `pq.Seal` and `pq.Open` helpers which perform the +standard `KEM → HKDF → AEAD` flow. The envelope format embeds the encapsulated key (length-prefixed), a key-schedule +identifier, and the AEAD ciphertext so that the receiver can deterministically reproduce the derived key/nonce pair. The +key schedule currently covers modern AEADs such as ChaCha20-Poly1305, AES-256-GCM, AES-GCM-SIV, XChaCha20-Poly1305, +ASCON-128a, Deoxys-II-256-128, and the AES-SIV family. + diff --git a/docs/HPKE.md b/docs/HPKE.md new file mode 100644 index 0000000..30322f3 --- /dev/null +++ b/docs/HPKE.md @@ -0,0 +1,39 @@ +# Hybrid Public Key Encryption (HPKE) + +Cryptonite-go implements RFC 9180 Hybrid Public Key Encryption (HPKE) in the `hpke` package. This document explains the +available modes, suite configuration, and integration guidance. + +## Supported Modes and Suites + +- **Base mode (mode 0)** is fully supported. Auth, PSK, and AuthPSK modes share the same API surface and are reachable by + supplying the appropriate options structures. +- Predefined ciphersuite helpers mirror RFC 9180 labels: + - `hpke.SuiteX25519ChaCha20` → KEM: DHKEM(X25519, HKDF-SHA256), KDF: HKDF-SHA256, AEAD: ChaCha20-Poly1305. + - `hpke.SuiteX25519AESGCM` → KEM: DHKEM(X25519, HKDF-SHA256), KDF: HKDF-SHA256, AEAD: AES-256-GCM. + - `hpke.SuiteP256ChaCha20` → KEM: DHKEM(P-256, HKDF-SHA256), KDF: HKDF-SHA256, AEAD: ChaCha20-Poly1305. + +Callers can construct custom suites via `hpke.NewSuite(kemID, kdfID, aeadID)` which validates identifiers against the +registry. + +## Usage Pattern + +1. **Setup** – The sender creates a context with `suite.SetupBaseSender(publicKey, info)` which returns the encapsulated + key (`enc`) and a `Context` instance. The receiver calls `suite.SetupBaseReceiver(enc, privateKey, info)`. +2. **Seal/Open** – Use `ctx.Seal(nonce, aad, pt)` and `ctx.Open(nonce, aad, ct)` to protect data. Nonces are 96-bit values + that increment per message. +3. **Export** – Derive exporter secrets via `ctx.Export(secret, length)` for key schedule chaining. + +## Deterministic Nonces + +HPKE leaves nonce generation to the application. Combine the exporter interface with the HKDF-based helpers in +`secret.NewDeterministicNonce` to derive deterministic nonces that are bound to the encapsulated key and associated data. + +## Integration with `pq` + +The `pq` package wraps HPKE contexts to produce hybrid envelopes that mix classical and post-quantum KEMs. The flow is: + +1. Call `pq.NewHybrid(classicalKEM, mlkem)` to construct the hybrid encapsulation object. +2. Use `pq.Seal` / `pq.Open` to perform HPKE-based key establishment with AEAD payload encryption. +3. The resulting format encodes HPKE metadata, AEAD identifiers, and ciphertext to ensure deterministic re-derivation of + keys during decryption. + diff --git a/docs/INTEROP.md b/docs/INTEROP.md new file mode 100644 index 0000000..d2c924d --- /dev/null +++ b/docs/INTEROP.md @@ -0,0 +1,56 @@ +# Interoperability Guide + +Cryptonite-go aims to interoperate with existing cryptography stacks. This guide documents wire formats, encoding rules, +and compatibility notes. + +## AEAD Payloads + +- **Ciphertext layout** – All AEAD encryptors return `ciphertext || tag`. For streaming AEADs, tags are always appended to + the end of the slice. +- **Associated Data** – Provided associated data is not included in the ciphertext and must be supplied verbatim during + decryption. +- **Nonce encoding** – Callers are responsible for storing nonces alongside ciphertext. Use big-endian encoding when + serializing counters. + +## Signature Encodings + +- **Ed25519** – Signatures are 64-byte raw buffers. Public keys are 32-byte little-endian values as defined in RFC 8032. +- **ECDSA P-256** – Public keys default to uncompressed SEC1 format (`0x04 || X || Y`). Signatures follow ASN.1 DER. + +## ECDH Keys + +- `ecdh.NewX25519` expects 32-byte private keys and outputs 32-byte Montgomery u-coordinates. Use the standard base point + defined in RFC 7748. +- `ecdh.NewP256` / `NewP384` accept scalar private keys and return uncompressed SEC1 public points. + +## HPKE Records + +- HPKE `enc` values are written exactly as produced by the underlying KEM (32 bytes for X25519, 65 for P-256). +- Ciphertexts include a 96-bit nonce supplied by the caller, AEAD ciphertext, and tag. Persist the nonce to decrypt. + +## PQ Hybrid Envelopes + +Hybrid envelopes emitted by `pq.Seal` are length-prefixed structures: + +``` +version (1 byte) +classical_ct_len (2 bytes, big-endian) +classical_ct (variable) +pq_ct_len (2 bytes, big-endian) +pq_ct (variable) +aead_id (1 byte) +nonce (len depends on AEAD) +ciphertext (variable) +tag (16 bytes typical) +``` + +Decoders must validate lengths before slicing the input to prevent panics. Unknown `version` values should trigger a +feature negotiation or graceful failure. + +## Hashing and KDFs + +- Hash functions follow the same digest outputs as Go's standard library implementations, so interoperability is + straightforward. +- HKDF uses little-endian uint16 labels when deriving AEAD keys for the hybrid envelope format, matching the docs in + `pq`. + diff --git a/docs/NONCE_MANAGEMENT.md b/docs/NONCE_MANAGEMENT.md new file mode 100644 index 0000000..b352e52 --- /dev/null +++ b/docs/NONCE_MANAGEMENT.md @@ -0,0 +1,58 @@ +# Nonce and Counter Management + +Cryptonite-go offers both deterministic SIV constructions and classic nonce-based AEADs. Correctly managing nonces and +counters is critical for security; this guide summarizes best practices across the library. + +## General Guidelines + +- **Uniqueness is mandatory**: For nonce-based AEADs (AES-GCM, ChaCha20-Poly1305, ASCON, etc.) never reuse a nonce with +the same key. Reuse undermines confidentiality and integrity. +- **Leverage `secret.Nonce` helpers**: The `secret` package exposes reference types for random nonces and monotonic + counters. Use them to reduce mistakes and enable zeroization via `Destroy()`. +- **Avoid truncating randomness**: When generating nonces, use the exact byte length required by the algorithm. Trimming + randomness to a smaller size introduces collisions. +- **Secure storage**: Persist counters alongside encrypted data so you can resume sequences safely after restarts. +- **Parallelization**: When encrypting in parallel, partition the nonce space (e.g., prefix worker ID bits) or derive + subkeys with HKDF so each worker owns an independent key/nonce pair. + +## AEAD-specific Recommendations + +### ChaCha20-Poly1305 and AES-GCM + +- Use 96-bit nonces (`12 bytes`). Compose a deterministic nonce by concatenating a 32-bit big-endian counter with a + 64-bit random prefix, or derive nonces via HKDF when using key encapsulation flows (see `pq.Seal`). +- For AES-GCM counters, ensure the low 32 bits increment monotonically and never wrap. Callers should detect counter + exhaustion and rotate keys before the 2³² limit. + +### XChaCha20-Poly1305 + +- Supply a full 192-bit (`24-byte`) nonce. The constructor internally runs HChaCha20 to derive a subkey and 96-bit nonce + for ChaCha20, so uniqueness of the 192-bit input is sufficient. + +### AES-GCM-SIV and AES-SIV + +- These constructions are nonce-misuse resistant but **not** misuse-proof. Repeating nonces will not leak plaintext, yet + it still enables replay. Generate high-entropy nonces and include strict associated data for replay detection. +- When using `aead.MultiAssociatedData`, keep the ordering of pieces consistent between encryption and decryption. + +### Lightweight AEADs (ASCON, SKINNY, GIFT-COFB, Xoodyak) + +- Follow the same 128-bit nonce uniqueness rules. For microcontrollers, monotonic counters stored in flash are + acceptable as long as updates are atomic (use double-buffering or sequence numbers). + +## Counter Utilities + +The `secret` package provides reusable counter implementations: + +- `secret.NewMonotonicCounter(size)` – returns a fixed-width big-endian counter. Use `Increment()` to advance and + `Bytes()` to obtain the current value. +- `secret.NewDeterministicNonce(key, salt, info)` – HKDF-based determinstic nonce derivation that binds context strings + to the output. +- `secret.Zeroize()` – helper to clear sensitive buffers once a nonce or counter leaves scope. + +## Zeroization Checklist + +1. Destroy nonce/counter structures once completed (`defer nonce.Destroy()`). +2. Wipe temporary copies of nonces stored in slices or structs. +3. Guard against double-destruction by setting references to `nil` after zeroization. + diff --git a/docs/PQ.md b/docs/PQ.md new file mode 100644 index 0000000..34f7344 --- /dev/null +++ b/docs/PQ.md @@ -0,0 +1,43 @@ +# Post-Quantum Integration + +Cryptonite-go approaches post-quantum readiness through hybrid KEM constructions that pair classical elliptic-curve +schemes with pluggable ML-KEM implementations. + +## Hybrid Format Overview + +- **Versioning** – Hybrids produced by `pq.NewHybrid` begin with a version byte so new PQ algorithms can be introduced + without breaking decoders. +- **Length prefixes** – Classical and PQ ciphertexts are both length-prefixed, enabling variable-size payloads and simple + framing. +- **Key schedule** – Derived shared secrets feed HKDF (SHA-256) to produce AEAD keys and nonces. The HKDF info string + encodes the chosen AEAD to ensure domain separation. + +## Classical Component + +The default classical KEM is `kem.New()`, which wraps X25519 or P-256 ECDH and exposes the shared `kem.KEM` interface. +Callers can substitute their own classical implementation if they match the same interface (GenerateKey, Encapsulate, +Decapsulate). + +## ML-KEM Component + +To integrate ML-KEM (Kyber/ML-KEM 512/768/1024): + +1. Import or implement a Go type satisfying `kem.KEM` for the desired ML-KEM parameter set. +2. Pass it to `pq.NewHybrid(classical, mlkem)`. +3. Ensure the ML-KEM public key, secret key, and ciphertext encodings follow the draft FIPS 203 byte layouts. + +## Envelope Helpers + +`pq.Seal` and `pq.Open` provide a turnkey envelope construction: + +- The sender encapsulates via both classical and PQ components, concatenates the ciphertexts, and derives AEAD keys. +- The receiver decapsulates each component independently and recombines secrets. If the PQ side fails, implementations + should treat the message as invalid even if the classical component succeeds. + +## Key Management Tips + +- **Rotate keys** regularly: publish new hybrid public keys once PQ implementations are updated. +- **Secure storage**: Store ML-KEM secret keys with strong access controls; they are typically larger than classical keys. +- **Fallbacks**: If the PQ component is unavailable, degrade gracefully by using the classical KEM only, but log the event + so operators are aware of reduced security. + diff --git a/docs/TESTING.md b/docs/TESTING.md new file mode 100644 index 0000000..01f133a --- /dev/null +++ b/docs/TESTING.md @@ -0,0 +1,38 @@ +# Testing Guidance + +Cryptonite-go relies heavily on deterministic Known Answer Tests (KATs), fuzzing harnesses, and regression suites. This +document covers how to extend the testing matrix when adding new primitives. + +## Running the Test Suite + +```bash +go test ./... +go test ./test/... +go test -race ./... +``` + +Run these commands before submitting pull requests. Include `-bench` benchmarks when performance regressions are a +concern. + +## Adding KAT Vectors + +1. Place vector files under the relevant package's `testdata/` directory or the shared `test/` folder. +2. Use JSON or the existing delimiter-separated formats for consistency. +3. Document the vector source (RFC, NIST, academic paper) in a comment within the test file. +4. Ensure tests cover both valid and invalid cases (e.g., tag tampering, nonce misuse). + +## Fuzzing + +- Leverage Go's built-in fuzzing (`go test -fuzz=Fuzz*`) for stateful primitives. +- Fuzz targets live under `test/fuzz/`. If you add a new fuzz target, include seed corpus files to accelerate discovery. + +## Benchmarks + +- Benchmarks reside in `test/benchmark_*.go` or package-level `_test.go` files. +- Include throughput numbers for new primitives and compare them against baseline implementations. + +## Continuous Integration Expectations + +- Pull requests should run `go fmt ./...`, `go vet ./...`, and `golangci-lint run` locally before submission. +- Keep benchmark output in PR descriptions when performance optimizations are proposed. +