From f6ff610af7917ba7db009e98ebc76894cdebcc31 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 24 Oct 2025 16:54:46 +0200 Subject: [PATCH] Document ML-KEM benchmarks and polish Kyber docs --- README.md | 4 ++++ benchmark.md | 11 +++++++++++ internal/kyber/ccakem.go | 6 +++--- internal/kyber/cpapke.go | 4 ++-- internal/kyber/internal.go | 4 ++-- internal/kyber/keys.go | 6 +++--- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 238761f..354a4e5 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ recovered, _ := hybrid.Decapsulate(hybridSK, ct) - All tests: `go test ./...` - With race detector: `go test -race ./...` +- ML-KEM / ML-DSA KATs: `go test ./test/pq -run TestMLKEMKAT` and `go test ./test/sig -run TestMLDSAKAT` Tests include KAT suites for ASCON, Xoodyak, ChaCha20‑Poly1305, AES-GCM-SIV, and AES-SIV (RFC 5297), plus tamper checks on tags and ciphertext. @@ -194,6 +195,9 @@ Run locally: ```bash go test ./test/... -bench=. -benchmem + +# Focused ML-KEM profiling +go test ./test/pq -bench=BenchmarkMLKEM -benchmem ``` On Windows PowerShell, quote the empty test pattern with double quotes: diff --git a/benchmark.md b/benchmark.md index 2f7fc13..f6eca4b 100644 --- a/benchmark.md +++ b/benchmark.md @@ -112,6 +112,17 @@ Benchmarks on **AMD Ryzen 7 5800X 8-Core Processor** (Windows, amd64) | **Encapsulate** | 15,826 | - | 2,169 B | 31 | | **Decapsulate** | 15,982 | 0.40 | 1,945 B | 28 | +### ML-KEM (Kyber) + +| Algorithm | Operation | Operations/sec | ns/op | Speed (MB/s) | Memory/op | Allocs/op | +|----------------|--------------|----------------|---------|--------------|-----------|-----------| +| **ML-KEM-512** | Encapsulate | 6,448 | 155,085 | - | 13,248 B | 793 | +| **ML-KEM-512** | Decapsulate | 4,691 | 213,172 | 0.15 | 15,552 B | 1,050 | +| **ML-KEM-768** | Encapsulate | 3,752 | 266,542 | - | 22,560 B | 1,565 | +| **ML-KEM-768** | Decapsulate | 3,557 | 281,125 | 0.11 | 26,016 B | 1,950 | +| **ML-KEM-1024** | Encapsulate | 3,065 | 326,309 | - | 34,704 B | 2,593 | +| **ML-KEM-1024** | Decapsulate | 2,452 | 407,897 | 0.08 | 39,056 B | 3,106 | + ### Envelope | Operation | Operations/sec | Speed (MB/s) | Memory/op | Allocs/op | diff --git a/internal/kyber/ccakem.go b/internal/kyber/ccakem.go index c6cc335..c9802a0 100644 --- a/internal/kyber/ccakem.go +++ b/internal/kyber/ccakem.go @@ -23,7 +23,7 @@ func (k *Kyber) KeyGen(seed []byte) ([]byte, []byte) { // Encaps generates a shared secret and the encryption of said shared secret using a given public key. // A 32 byte long seed can be given as argument (coins). If a nil seed is given, the seed is generated using Go crypto's random number generator. // The shared secret and ciphertext returned are packed into byte arrays. -// If an error occurs during the encaps process, nil arrays are returned. +// If an error occurs during the encapsulation process, nil arrays are returned. func (k *Kyber) Encaps(packedPK, coins []byte) ([]byte, []byte) { if len(packedPK) != k.SIZEPK() { return nil, nil @@ -61,10 +61,10 @@ func (k *Kyber) Encaps(packedPK, coins []byte) ([]byte, []byte) { return c[:], ss[:] } -// Decaps decryps a ciphertext given a secret key and checks its validity. +// Decaps decrypts a ciphertext given a secret key and checks its validity. // The secret key and ciphertext must be give as packed byte array. // The recovered shared secret is returned as byte array. -// If an error occurs durirng the decapsulation process, a nil shared secret is returned. +// If an error occurs during the decapsulation process, a nil shared secret is returned. func (k *Kyber) Decaps(packedSK, c []byte) []byte { if len(c) != k.SIZEC() || len(packedSK) != k.SIZESK() { return nil diff --git a/internal/kyber/cpapke.go b/internal/kyber/cpapke.go index c6223fd..dd8fdda 100644 --- a/internal/kyber/cpapke.go +++ b/internal/kyber/cpapke.go @@ -54,7 +54,7 @@ func (k *Kyber) PKEKeyGen(seed []byte) ([]byte, []byte) { // Encrypt generates the encryption of a message using a public key. // A 32 byte long seed can be given as argument (r). If a nil seed is given, the seed is generated using Go crypto's random number generator. // The ciphertext returned is packed into a byte array. -// If an error occurs during the encrpytion process, a nil array is returned. +// If an error occurs during the encryption process, a nil array is returned. func (k *Kyber) Encrypt(packedPK, msg, r []byte) []byte { if len(msg) < n/8 { @@ -118,7 +118,7 @@ func (k *Kyber) Encrypt(packedPK, msg, r []byte) []byte { // Decrypt decrypts a ciphertext given a secret key. // The secret key and ciphertext must be give as packed byte array. // The recovered message is returned as byte array. -// If an error occurs durirng the decryption process (wrong key format for example), a nil message is returned. +// If an error occurs during the decryption process (wrong key format for example), a nil message is returned. func (k *Kyber) Decrypt(packedSK, c []byte) []byte { if len(c) != k.SIZEC() || len(packedSK) != k.SIZEPKESK() { return nil diff --git a/internal/kyber/internal.go b/internal/kyber/internal.go index 54ff939..1812e60 100644 --- a/internal/kyber/internal.go +++ b/internal/kyber/internal.go @@ -1,9 +1,9 @@ package kyber -// Mat is used to hold A +// Mat is used to hold A. type Mat []Vec -// expandSeed generated a KxK array of polynomials woth coefficients in [0, Q) +// expandSeed generates a K×K matrix of polynomials with coefficients in [0, Q). func expandSeed(rho []byte, transpose bool, K int) Mat { m := make(Mat, K) for i := 0; i < K; i++ { diff --git a/internal/kyber/keys.go b/internal/kyber/keys.go index b19ef7c..6c013b2 100644 --- a/internal/kyber/keys.go +++ b/internal/kyber/keys.go @@ -6,18 +6,18 @@ import ( "crypto/sha3" ) -// PublicKey holds the pk strct +// PublicKey holds the packed Kyber public key material. type PublicKey struct { T Vec //NTT(t) Rho []byte //32 } -// PKEPrivateKey holds the ak strct for Kyber's PKE scheme +// PKEPrivateKey holds the packed secret vector for Kyber's PKE scheme. type PKEPrivateKey struct { S Vec //NTT(s) } -// PrivateKey holds the sk struct +// PrivateKey holds the full Kyber CCA private key components. type PrivateKey struct { Z []byte SkP []byte