Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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

176 changes: 33 additions & 143 deletions README.md

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -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

140 changes: 140 additions & 0 deletions docs/ALGORITHMS.md

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions docs/HPKE.md
Original file line number Diff line number Diff line change
@@ -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.

56 changes: 56 additions & 0 deletions docs/INTEROP.md
Original file line number Diff line number Diff line change
@@ -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`.

58 changes: 58 additions & 0 deletions docs/NONCE_MANAGEMENT.md
Original file line number Diff line number Diff line change
@@ -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.

43 changes: 43 additions & 0 deletions docs/PQ.md
Original file line number Diff line number Diff line change
@@ -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.

38 changes: 38 additions & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
@@ -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.

Loading