Skip to content

Commit

Permalink
[WIP] Add ML-KEM (FIPS 203).
Browse files Browse the repository at this point in the history
ML-KEM is not final yet, so this implementation isn't final either.

We keep Kyber around (for now) as it's currently widely deployed.
Code differences between them are minimal anyway.
  • Loading branch information
bwesterb committed Jan 4, 2024
1 parent ede59c5 commit 0edf39b
Show file tree
Hide file tree
Showing 12 changed files with 1,323 additions and 34 deletions.
36 changes: 33 additions & 3 deletions kem/kyber/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ package main

import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"path"
"strings"
"text/template"
)
Expand All @@ -17,15 +19,43 @@ type Instance struct {
Name string
}

func (m Instance) KemName() string {
if m.NIST() {
return m.Name
}
return m.Name + ".CCAKEM"
}

func (m Instance) NIST() bool {
return strings.HasPrefix(m.Name, "ML-KEM")
}

func (m Instance) PkePkg() string {
if !m.NIST() {
return m.Pkg()
}
return strings.ReplaceAll(m.Pkg(), "mlkem", "kyber")
}

func (m Instance) Pkg() string {
return strings.ToLower(m.Name)
return strings.ToLower(strings.ReplaceAll(m.Name, "-", ""))
}

func (m Instance) PkgPath() string {
if m.NIST() {
return path.Join("..", "mlkem", m.Pkg())
}
return m.Pkg()
}

var (
Instances = []Instance{
{Name: "Kyber512"},
{Name: "Kyber768"},
{Name: "Kyber1024"},
{Name: "ML-KEM-512"},
{Name: "ML-KEM-768"},
{Name: "ML-KEM-1024"},
}
TemplateWarning = "// Code generated from"
)
Expand All @@ -51,15 +81,15 @@ func generatePackageFiles() {
// Formating output code
code, err := format.Source(buf.Bytes())
if err != nil {
panic("error formating code")
panic(fmt.Sprintf("error formating code: %v", err))
}

res := string(code)
offset := strings.Index(res, TemplateWarning)
if offset == -1 {
panic("Missing template warning in pkg.templ.go")
}
err = ioutil.WriteFile(mode.Pkg()+"/kyber.go", []byte(res[offset:]), 0o644)
err = ioutil.WriteFile(mode.PkgPath()+"/kyber.go", []byte(res[offset:]), 0o644)
if err != nil {
panic(err)
}
Expand Down
28 changes: 21 additions & 7 deletions kem/kyber/kat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"bytes"
"crypto/sha256"
"fmt"
"strings"
"testing"

"github.com/cloudflare/circl/internal/nist"
Expand All @@ -22,6 +23,11 @@ func TestPQCgenKATKem(t *testing.T) {
{"Kyber1024", "89248f2f33f7f4f7051729111f3049c409a933ec904aedadf035f30fa5646cd5"},
{"Kyber768", "a1e122cad3c24bc51622e4c242d8b8acbcd3f618fee4220400605ca8f9ea02c2"},
{"Kyber512", "e9c2bd37133fcb40772f81559f14b1f58dccd1c816701be9ba6214d43baf4547"},

// Computed from reference implementation standard branch
{"ML-KEM-512", "4b88ac7643ff60209af1175e025f354272e88df827a0ce1c056e403629b88e04"},
{"ML-KEM-768", "21b4a1e1ea34a13c26a9da5eeb9325afb5ca11596ca6f3704c3f2637e3ea7524"},
{"ML-KEM-1024", "6471398b0a728ee1ef39e93bb89b526fbf59587a3662edadbcfc6c88a512cd71"},
}
for _, kat := range kats {
kat := kat
Expand All @@ -45,18 +51,26 @@ func testPQCgenKATKem(t *testing.T, name, expected string) {
}
f := sha256.New()
g := nist.NewDRBG(&seed)
fmt.Fprintf(f, "# %s\n\n", name)

// The "standard" branch reference implementation still uses Kyber
// as name instead of ML-KEM.
fmt.Fprintf(f, "# %s\n\n", strings.ReplaceAll(name, "ML-KEM-", "Kyber"))
for i := 0; i < 100; i++ {
g.Fill(seed[:])
fmt.Fprintf(f, "count = %d\n", i)
fmt.Fprintf(f, "seed = %X\n", seed)
g2 := nist.NewDRBG(&seed)

// This is not equivalent to g2.Fill(kseed[:]). As the reference
// implementation calls randombytes twice generating the keypair,
// we have to do that as well.
g2.Fill(kseed[:32])
g2.Fill(kseed[32:])
if strings.HasPrefix(name, "ML-KEM") {
// https://github.com/pq-crystals/kyber/commit/830e0ba1a7fdba6cde03f8139b0d41ad2102b860
g2.Fill(kseed[:])
} else {
// This is not equivalent to g2.Fill(kseed[:]). As the reference
// implementation calls randombytes twice generating the keypair,
// we have to do that as well.
g2.Fill(kseed[:32])
g2.Fill(kseed[32:])
}

g2.Fill(eseed)
pk, sk := scheme.DeriveKeyPair(kseed)
Expand All @@ -73,6 +87,6 @@ func testPQCgenKATKem(t *testing.T, name, expected string) {
fmt.Fprintf(f, "ss = %X\n\n", ss)
}
if fmt.Sprintf("%x", f.Sum(nil)) != expected {
t.Fatal()
t.Fatalf("%s %x %s", name, f.Sum(nil), expected)
}
}
14 changes: 8 additions & 6 deletions kem/kyber/kyber1024/kyber.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions kem/kyber/kyber512/kyber.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions kem/kyber/kyber768/kyber.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 37 additions & 6 deletions kem/kyber/templates/pkg.templ.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions kem/mlkem/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Package mlkem implements IND-CCA2 secure ML-KEM key encapsulation
// mechanism (KEM) as defined in FIPS 203.
package kyber

// See ../kyber/gen.go and ../kyber/kat_test.go.

0 comments on commit 0edf39b

Please sign in to comment.