gmsm/mlkem/mlkem1024.go
github-actions[bot] 1979d24faa
Merge develop into main (#370)
* build(deps): bump github/codeql-action from 3.29.11 to 3.30.0 (#361)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.11 to 3.30.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](3c3833e0f8...2d92b76c45)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump codecov/codecov-action from 5.5.0 to 5.5.1 (#362)

Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.0 to 5.5.1.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](fdcc847654...5a1091511a)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: 5.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump actions/setup-go from 5.5.0 to 6.0.0 (#363)

Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.5.0 to 6.0.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](d35c59abb0...4469467582)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github/codeql-action from 3.30.0 to 3.30.1 (#364)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.0 to 3.30.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](2d92b76c45...f1f6e5f6af)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump step-security/harden-runner from 2.13.0 to 2.13.1 (#367)

Bumps [step-security/harden-runner](https://github.com/step-security/harden-runner) from 2.13.0 to 2.13.1.
- [Release notes](https://github.com/step-security/harden-runner/releases)
- [Commits](ec9f2d5744...f4a75cfd61)

---
updated-dependencies:
- dependency-name: step-security/harden-runner
  dependency-version: 2.13.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github/codeql-action from 3.30.1 to 3.30.2 (#368)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.1 to 3.30.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](f1f6e5f6af...d3678e237b)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(mlkem): initialize mlkem from golang standard library

* chore(mlkem): refactoring, reduce alloc times

* build(deps): bump github/codeql-action from 3.30.2 to 3.30.3 (#369)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.2 to 3.30.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](d3678e237b...192325c861)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* doc(README): include MLKEM

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sun Yimin <emmansun@users.noreply.github.com>
2025-09-11 08:48:21 +08:00

413 lines
12 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2025 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// Code generated by generate.go. DO NOT EDIT.
//go:build go1.24
package mlkem
import (
"bytes"
"crypto/sha3"
"crypto/subtle"
"errors"
"io"
)
// A DecapsulationKey1024 is the secret key used to decapsulate a shared key from a
// ciphertext. It includes various precomputed values.
type DecapsulationKey1024 struct {
d [32]byte // decapsulation key seed
z [32]byte // implicit rejection sampling seed
ρ [32]byte // rho, sampleNTT seed for A, stored for the encapsulation key
h [32]byte // H(ek), stored for ML-KEM.Decaps_internal
encryptionKey1024
decryptionKey1024
}
// Seed returns the decapsulation key as a 64-byte seed in the "d || z" form.
//
// The decapsulation key must be kept secret.
func (dk *DecapsulationKey1024) Seed() []byte {
var b [SeedSize]byte
copy(b[:], dk.d[:])
copy(b[32:], dk.z[:])
return b[:]
}
// Bytes returns the decapsulation key as a byte slice
// using the full expanded NIST encoding.
func (dk *DecapsulationKey1024) Bytes() []byte {
b := make([]byte, 0, DecapsulationKeySize1024)
// ByteEncode₁₂(s)
for i := range dk.s {
b = polyByteEncode(b, dk.s[i])
}
// ByteEncode₁₂(t) || ρ
for i := range dk.t {
b = polyByteEncode(b, dk.t[i])
}
b = append(b, dk.ρ[:]...)
// H(ek) || z
b = append(b, dk.h[:]...)
b = append(b, dk.z[:]...)
return b
}
// EncapsulationKey returns the public encapsulation key necessary to produce
// ciphertexts.
func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 {
return &EncapsulationKey1024{
ρ: dk.ρ,
h: dk.h,
encryptionKey1024: dk.encryptionKey1024,
}
}
// An EncapsulationKey1024 is the public key used to produce ciphertexts to be
// decapsulated by the corresponding [DecapsulationKey1024].
type EncapsulationKey1024 struct {
ρ [32]byte // sampleNTT seed for A
h [32]byte // H(ek)
encryptionKey1024
}
// Bytes returns the encapsulation key as a byte slice.
func (ek *EncapsulationKey1024) Bytes() []byte {
// The actual logic is in a separate function to outline this allocation.
b := make([]byte, 0, EncapsulationKeySize1024)
return ek.bytes(b)
}
func (ek *EncapsulationKey1024) bytes(b []byte) []byte {
for i := range ek.t {
b = polyByteEncode(b, ek.t[i])
}
b = append(b, ek.ρ[:]...)
return b
}
// encryptionKey1024 is the parsed and expanded form of a PKE encryption key.
type encryptionKey1024 struct {
t [k1024]nttElement // ByteDecode₁₂(ek[:384k])
a [k1024 * k1024]nttElement // A[i*k+j] = sampleNTT(ρ, j, i)
}
// decryptionKey1024 is the parsed and expanded form of a PKE decryption key.
type decryptionKey1024 struct {
s [k1024]nttElement // ByteDecode₁₂(dk[:decryptionKey1024Size])
}
// GenerateKey1024 generates a new decapsulation key. The decapsulation key must be kept secret.
// See FIPS 203, Algorithm 19.
func GenerateKey1024(rand io.Reader) (*DecapsulationKey1024, error) {
// The actual logic is in a separate function to outline this allocation.
dk := &DecapsulationKey1024{}
return generateKey1024(dk, rand)
}
func generateKey1024(dk *DecapsulationKey1024, rand io.Reader) (*DecapsulationKey1024, error) {
var d [32]byte
if _, err := io.ReadFull(rand, d[:]); err != nil {
return nil, err
}
var z [32]byte
if _, err := io.ReadFull(rand, z[:]); err != nil {
return nil, err
}
kemKeyGen1024(dk, &d, &z)
return dk, nil
}
// NewDecapsulationKeyFromSeed1024 parses a decapsulation key from a 64-byte
// seed in the "d || z" form. The seed must be uniformly random.
func NewDecapsulationKeyFromSeed1024(seed []byte) (*DecapsulationKey1024, error) {
// The actual logic is in a separate function to outline this allocation.
dk := &DecapsulationKey1024{}
return newKeyFromSeed1024(dk, seed)
}
func newKeyFromSeed1024(dk *DecapsulationKey1024, seed []byte) (*DecapsulationKey1024, error) {
if len(seed) != SeedSize {
return nil, errors.New("mlkem: invalid seed length")
}
d := (*[32]byte)(seed[:32])
z := (*[32]byte)(seed[32:])
kemKeyGen1024(dk, d, z)
return dk, nil
}
// NewDecapsulationKey1024 parses a decapsulation key from its expanded NIST format.
func NewDecapsulationKey1024(b []byte) (*DecapsulationKey1024, error) {
if len(b) != DecapsulationKeySize1024 {
return nil, errors.New("mlkem: invalid decapsulation key length")
}
dk := &DecapsulationKey1024{}
for i := range dk.s {
var err error
dk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12])
if err != nil {
return nil, errors.New("mlkem: invalid secret key encoding")
}
b = b[encodingSize12:]
}
ek, err := NewEncapsulationKey1024(b[:EncapsulationKeySize1024])
if err != nil {
return nil, err
}
dk.ρ = ek.ρ
dk.h = ek.h
dk.encryptionKey1024 = ek.encryptionKey1024
b = b[EncapsulationKeySize1024:]
if !bytes.Equal(dk.h[:], b[:32]) {
return nil, errors.New("mlkem: inconsistent H(ek) in encoded bytes")
}
copy(dk.z[:], b[32:])
return dk, nil
}
// kemKeyGen1024 generates a decapsulation key.
//
// It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and
// K-PKE.KeyGen according to FIPS 203, Algorithm 13. The two are merged to save
// copies and allocations.
func kemKeyGen1024(dk *DecapsulationKey1024, d, z *[32]byte) {
dk.d = *d
dk.z = *z
g := sha3.New512()
g.Write(d[:])
g.Write([]byte{k1024}) // Module dimension as a domain separator.
G := g.Sum(make([]byte, 0, 64))
ρ, σ := G[:32], G[32:] // rho, sigma
dk.ρ = [32]byte(ρ)
A := &dk.a
for i := range byte(k1024) {
for j := range byte(k1024) {
A[i*k1024+j] = sampleNTT(ρ, j, i)
}
}
var N byte
s := &dk.s
for i := range s {
s[i] = ntt(samplePolyCBD(σ, N, η1_1024))
N++
}
e := make([]nttElement, k1024)
for i := range e {
e[i] = ntt(samplePolyCBD(σ, N, η1_1024))
N++
}
t := &dk.t
for i := range t { // t = A ◦ s + e
t[i] = e[i]
for j := range s {
t[i] = polyAdd(t[i], nttMul(A[i*k1024+j], s[j]))
}
}
H := sha3.New256()
ek := dk.EncapsulationKey().Bytes()
H.Write(ek)
H.Sum(dk.h[:0])
}
// Encapsulate generates a shared key and an associated ciphertext from an
// encapsulation key.
//
// The shared key must be kept secret. See FIPS 203, Algorithm 20.
func (ek *EncapsulationKey1024) Encapsulate(rand io.Reader) (sharedKey, ciphertext []byte, err error) {
// The actual logic is in a separate function to outline this allocation.
var cc [CiphertextSize1024]byte
return ek.encapsulate(&cc, rand)
}
func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte, rand io.Reader) (sharedKey, ciphertext []byte, err error) {
var m [messageSize]byte
if _, err := io.ReadFull(rand, m[:]); err != nil {
return nil, nil, err
}
sharedKey, ciphertext = kemEncaps1024(cc, ek, &m)
return sharedKey, ciphertext, nil
}
// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for
// use in tests.
func (ek *EncapsulationKey1024) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) {
cc := &[CiphertextSize1024]byte{}
return kemEncaps1024(cc, ek, m)
}
// kemEncaps1024 generates a shared key and an associated ciphertext.
//
// It implements ML-KEM.Encaps_internal according to FIPS 203, Algorithm 17.
func kemEncaps1024(cc *[CiphertextSize1024]byte, ek *EncapsulationKey1024, m *[messageSize]byte) (K, c []byte) {
g := sha3.New512()
g.Write(m[:])
g.Write(ek.h[:])
G := g.Sum(nil)
K, r := G[:SharedKeySize], G[SharedKeySize:]
c = pkeEncrypt1024(cc, &ek.encryptionKey1024, m, r)
return K, c
}
// NewEncapsulationKey1024 parses an encapsulation key from its encoded form.
// If the encapsulation key is not valid, NewEncapsulationKey1024 returns an error.
func NewEncapsulationKey1024(encapsulationKey []byte) (*EncapsulationKey1024, error) {
// The actual logic is in a separate function to outline this allocation.
ek := &EncapsulationKey1024{}
return parseEK1024(ek, encapsulationKey)
}
// parseEK1024 parses an encryption key from its encoded form.
//
// It implements the initial stages of K-PKE.Encrypt according to FIPS 203,
// Algorithm 14.
func parseEK1024(ek *EncapsulationKey1024, ekPKE []byte) (*EncapsulationKey1024, error) {
if len(ekPKE) != EncapsulationKeySize1024 {
return nil, errors.New("mlkem: invalid encapsulation key length")
}
h := sha3.New256()
h.Write(ekPKE)
h.Sum(ek.h[:0])
for i := range ek.t {
var err error
ek.t[i], err = polyByteDecode[nttElement](ekPKE[:encodingSize12])
if err != nil {
return nil, err
}
ekPKE = ekPKE[encodingSize12:]
}
copy(ek.ρ[:], ekPKE)
for i := range byte(k1024) {
for j := range byte(k1024) {
ek.a[i*k1024+j] = sampleNTT(ek.ρ[:], j, i)
}
}
return ek, nil
}
// pkeEncrypt1024 encrypt a plaintext message.
//
// It implements K-PKE.Encrypt according to FIPS 203, Algorithm 14, although the
// computation of t and AT is done in parseEK1024.
func pkeEncrypt1024(cc *[CiphertextSize1024]byte, ex *encryptionKey1024, m *[messageSize]byte, rnd []byte) []byte {
var N byte
r, e1 := make([]nttElement, k1024), make([]ringElement, k1024)
for i := range r {
r[i] = ntt(samplePolyCBD(rnd, N, η1_1024))
N++
}
for i := range e1 {
e1[i] = samplePolyCBD(rnd, N, η2_1024)
N++
}
e2 := samplePolyCBD(rnd, N, η2_1024)
u := make([]ringElement, k1024) // NTT⁻¹(AT ◦ r) + e1
for i := range u {
u[i] = e1[i]
for j := range r {
// Note that i and j are inverted, as we need the transposed of A.
u[i] = polyAdd(u[i], inverseNTT(nttMul(ex.a[j*k1024+i], r[j])))
}
}
μ := ringDecodeAndDecompress1(m)
var vNTT nttElement // t⊺ ◦ r
for i := range ex.t {
vNTT = polyAdd(vNTT, nttMul(ex.t[i], r[i]))
}
v := polyAdd(polyAdd(inverseNTT(vNTT), e2), μ)
c := cc[:0]
for _, f := range u {
c = ringCompressAndEncode11(c, f)
}
c = ringCompressAndEncode5(c, v)
return c
}
// Decapsulate generates a shared key from a ciphertext and a decapsulation key.
// If the ciphertext is not valid, Decapsulate returns an error.
//
// The shared key must be kept secret.
func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) {
if len(ciphertext) != CiphertextSize1024 {
return nil, errors.New("mlkem: invalid ciphertext length")
}
c := (*[CiphertextSize1024]byte)(ciphertext)
// Note that the hash check (step 3 of the decapsulation input check from
// FIPS 203, Section 7.3) is foregone as a DecapsulationKey is always
// validly generated by ML-KEM.KeyGen_internal.
return kemDecaps1024(dk, c), nil
}
// kemDecaps1024 produces a shared key from a ciphertext.
//
// It implements ML-KEM.Decaps_internal according to FIPS 203, Algorithm 18.
func kemDecaps1024(dk *DecapsulationKey1024, c *[CiphertextSize1024]byte) (K []byte) {
m := pkeDecrypt1024(&dk.decryptionKey1024, c)
g := sha3.New512()
g.Write(m[:])
g.Write(dk.h[:])
G := g.Sum(make([]byte, 0, 64))
Kprime, r := G[:SharedKeySize], G[SharedKeySize:]
J := sha3.NewSHAKE256()
J.Write(dk.z[:])
J.Write(c[:])
Kout := make([]byte, SharedKeySize)
J.Read(Kout)
var cc [CiphertextSize1024]byte
c1 := pkeEncrypt1024(&cc, &dk.encryptionKey1024, (*[32]byte)(m), r)
subtle.ConstantTimeCopy(subtle.ConstantTimeCompare(c[:], c1), Kout, Kprime)
return Kout
}
// pkeDecrypt1024 decrypts a ciphertext.
//
// It implements K-PKE.Decrypt according to FIPS 203, Algorithm 15,
// although s is retained from kemKeyGen1024.
func pkeDecrypt1024(dx *decryptionKey1024, c *[CiphertextSize1024]byte) []byte {
u := make([]ringElement, k1024)
for i := range u {
b := (*[encodingSize11]byte)(c[encodingSize11*i : encodingSize11*(i+1)])
u[i] = ringDecodeAndDecompress11(b)
}
b := (*[encodingSize5]byte)(c[encodingSize11*k1024:])
v := ringDecodeAndDecompress5(b)
var mask nttElement // s⊺ ◦ NTT(u)
for i := range dx.s {
mask = polyAdd(mask, nttMul(dx.s[i], ntt(u[i])))
}
w := polySub(v, inverseNTT(mask))
return ringCompressAndEncode1(nil, w)
}