Skip to content

Commit 9aaef91

Browse files
cpugopherbot
authored andcommitted
crypto/mlkem: init package
This commit exposes the crypto/internal/mlkem package as a public crypto package based on the linked proposal. Since we've already implemented this internal to the FIPS boundary this largely defers to that implementation. Updates #70122 Change-Id: I5ec9c2783c4d44583244c6d16597704a51e9b738 Reviewed-on: https://go-review.googlesource.com/c/go/+/630240 Reviewed-by: Filippo Valsorda <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent b2f7a21 commit 9aaef91

File tree

7 files changed

+272
-8
lines changed

7 files changed

+272
-8
lines changed

api/next/70122.txt

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
pkg crypto/mlkem, const CiphertextSize1024 = 1568 #70122
2+
pkg crypto/mlkem, const CiphertextSize1024 ideal-int #70122
3+
pkg crypto/mlkem, const CiphertextSize768 = 1088 #70122
4+
pkg crypto/mlkem, const CiphertextSize768 ideal-int #70122
5+
pkg crypto/mlkem, const EncapsulationKeySize1024 = 1568 #70122
6+
pkg crypto/mlkem, const EncapsulationKeySize1024 ideal-int #70122
7+
pkg crypto/mlkem, const EncapsulationKeySize768 = 1184 #70122
8+
pkg crypto/mlkem, const EncapsulationKeySize768 ideal-int #70122
9+
pkg crypto/mlkem, const SeedSize = 64 #70122
10+
pkg crypto/mlkem, const SeedSize ideal-int #70122
11+
pkg crypto/mlkem, const SharedKeySize = 32 #70122
12+
pkg crypto/mlkem, const SharedKeySize ideal-int #70122
13+
pkg crypto/mlkem, func GenerateKey1024() (*DecapsulationKey1024, error) #70122
14+
pkg crypto/mlkem, func GenerateKey768() (*DecapsulationKey768, error) #70122
15+
pkg crypto/mlkem, func NewDecapsulationKey1024([]uint8) (*DecapsulationKey1024, error) #70122
16+
pkg crypto/mlkem, func NewDecapsulationKey768([]uint8) (*DecapsulationKey768, error) #70122
17+
pkg crypto/mlkem, func NewEncapsulationKey1024([]uint8) (*EncapsulationKey1024, error) #70122
18+
pkg crypto/mlkem, func NewEncapsulationKey768([]uint8) (*EncapsulationKey768, error) #70122
19+
pkg crypto/mlkem, method (*DecapsulationKey1024) Bytes() []uint8 #70122
20+
pkg crypto/mlkem, method (*DecapsulationKey1024) Decapsulate([]uint8) ([]uint8, error) #70122
21+
pkg crypto/mlkem, method (*DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 #70122
22+
pkg crypto/mlkem, method (*DecapsulationKey768) Bytes() []uint8 #70122
23+
pkg crypto/mlkem, method (*DecapsulationKey768) Decapsulate([]uint8) ([]uint8, error) #70122
24+
pkg crypto/mlkem, method (*DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 #70122
25+
pkg crypto/mlkem, method (*EncapsulationKey1024) Bytes() []uint8 #70122
26+
pkg crypto/mlkem, method (*EncapsulationKey1024) Encapsulate() ([]uint8, []uint8) #70122
27+
pkg crypto/mlkem, method (*EncapsulationKey768) Bytes() []uint8 #70122
28+
pkg crypto/mlkem, method (*EncapsulationKey768) Encapsulate() ([]uint8, []uint8) #70122
29+
pkg crypto/mlkem, type DecapsulationKey1024 struct #70122
30+
pkg crypto/mlkem, type DecapsulationKey768 struct #70122
31+
pkg crypto/mlkem, type EncapsulationKey1024 struct #70122
32+
pkg crypto/mlkem, type EncapsulationKey768 struct #70122

doc/next/6-stdlib/4-mlkem.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
A new `crypto/mlkem` package was added, implementing ML-KEM (formerly known as
2+
Kyber), as specified in [NIST FIPS 203](https://doi.org/10.6028/NIST.FIPS.203).
3+
<!-- go.dev/issue/70122 -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!-- This is a new package; covered in 6-stdlib/4-mlkem.md. -->

src/crypto/mlkem/mlkem1024.go

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package mlkem
6+
7+
import "crypto/internal/fips140/mlkem"
8+
9+
const (
10+
// CiphertextSize1024 is the size of a ciphertext produced by the 1024-bit
11+
// variant of ML-KEM.
12+
CiphertextSize1024 = 1568
13+
14+
// EncapsulationKeySize1024 is the size of an encapsulation key for the
15+
// 1024-bit variant of ML-KEM.
16+
EncapsulationKeySize1024 = 1568
17+
)
18+
19+
// DecapsulationKey1024 is the secret key used to decapsulate a shared key
20+
// from a ciphertext. It includes various precomputed values.
21+
type DecapsulationKey1024 struct {
22+
key *mlkem.DecapsulationKey1024
23+
}
24+
25+
// GenerateKey1024 generates a new decapsulation key, drawing random bytes from
26+
// crypto/rand. The decapsulation key must be kept secret.
27+
func GenerateKey1024() (*DecapsulationKey1024, error) {
28+
key, err := mlkem.GenerateKey1024()
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
return &DecapsulationKey1024{key}, nil
34+
}
35+
36+
// NewDecapsulationKey1024 parses a decapsulation key from a 64-byte seed in the
37+
// "d || z" form. The seed must be uniformly random.
38+
func NewDecapsulationKey1024(seed []byte) (*DecapsulationKey1024, error) {
39+
key, err := mlkem.NewDecapsulationKey1024(seed)
40+
if err != nil {
41+
return nil, err
42+
}
43+
44+
return &DecapsulationKey1024{key}, nil
45+
}
46+
47+
// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form.
48+
//
49+
// The decapsulation key must be kept secret.
50+
func (dk *DecapsulationKey1024) Bytes() []byte {
51+
return dk.key.Bytes()
52+
}
53+
54+
// Decapsulate generates a shared key from a ciphertext and a decapsulation
55+
// key. If the ciphertext is not valid, Decapsulate returns an error.
56+
//
57+
// The shared key must be kept secret.
58+
func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) {
59+
return dk.key.Decapsulate(ciphertext)
60+
}
61+
62+
// EncapsulationKey returns the public encapsulation key necessary to produce
63+
// ciphertexts.
64+
func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 {
65+
return &EncapsulationKey1024{dk.key.EncapsulationKey()}
66+
}
67+
68+
// An EncapsulationKey1024 is the public key used to produce ciphertexts to be
69+
// decapsulated by the corresponding DecapsulationKey1024.
70+
type EncapsulationKey1024 struct {
71+
key *mlkem.EncapsulationKey1024
72+
}
73+
74+
// NewEncapsulationKey1024 parses an encapsulation key from its encoded form. If
75+
// the encapsulation key is not valid, NewEncapsulationKey1024 returns an error.
76+
func NewEncapsulationKey1024(encapsulationKey []byte) (*EncapsulationKey1024, error) {
77+
key, err := mlkem.NewEncapsulationKey1024(encapsulationKey)
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
return &EncapsulationKey1024{key}, nil
83+
}
84+
85+
// Bytes returns the encapsulation key as a byte slice.
86+
func (ek *EncapsulationKey1024) Bytes() []byte {
87+
return ek.key.Bytes()
88+
}
89+
90+
// Encapsulate generates a shared key and an associated ciphertext from an
91+
// encapsulation key, drawing random bytes from crypto/rand.
92+
//
93+
// The shared key must be kept secret.
94+
func (ek *EncapsulationKey1024) Encapsulate() (ciphertext, sharedKey []byte) {
95+
return ek.key.Encapsulate()
96+
}

src/crypto/mlkem/mlkem768.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Package mlkem implements the quantum-resistant key encapsulation method
6+
// ML-KEM (formerly known as Kyber), as specified in [NIST FIPS 203].
7+
//
8+
// [NIST FIPS 203]: https://doi.org/10.6028/NIST.FIPS.203
9+
package mlkem
10+
11+
import "crypto/internal/fips140/mlkem"
12+
13+
const (
14+
// SharedKeySize is the size of a shared key produced by ML-KEM.
15+
SharedKeySize = 32
16+
17+
// SeedSize is the size of a seed used to generate a decapsulation key.
18+
SeedSize = 64
19+
20+
// CiphertextSize768 is the size of a ciphertext produced by the 768-bit
21+
// variant of ML-KEM.
22+
CiphertextSize768 = 1088
23+
24+
// EncapsulationKeySize768 is the size of an encapsulation key for the
25+
// 768-bit variant of ML-KEM.
26+
EncapsulationKeySize768 = 1184
27+
)
28+
29+
// DecapsulationKey768 is the secret key used to decapsulate a shared key
30+
// from a ciphertext. It includes various precomputed values.
31+
type DecapsulationKey768 struct {
32+
key *mlkem.DecapsulationKey768
33+
}
34+
35+
// GenerateKey768 generates a new decapsulation key, drawing random bytes from
36+
// crypto/rand. The decapsulation key must be kept secret.
37+
func GenerateKey768() (*DecapsulationKey768, error) {
38+
key, err := mlkem.GenerateKey768()
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
return &DecapsulationKey768{key}, nil
44+
}
45+
46+
// NewDecapsulationKey768 parses a decapsulation key from a 64-byte seed in the
47+
// "d || z" form. The seed must be uniformly random.
48+
func NewDecapsulationKey768(seed []byte) (*DecapsulationKey768, error) {
49+
key, err := mlkem.NewDecapsulationKey768(seed)
50+
if err != nil {
51+
return nil, err
52+
}
53+
54+
return &DecapsulationKey768{key}, nil
55+
}
56+
57+
// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form.
58+
//
59+
// The decapsulation key must be kept secret.
60+
func (dk *DecapsulationKey768) Bytes() []byte {
61+
return dk.key.Bytes()
62+
}
63+
64+
// Decapsulate generates a shared key from a ciphertext and a decapsulation
65+
// key. If the ciphertext is not valid, Decapsulate returns an error.
66+
//
67+
// The shared key must be kept secret.
68+
func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) {
69+
return dk.key.Decapsulate(ciphertext)
70+
}
71+
72+
// EncapsulationKey returns the public encapsulation key necessary to produce
73+
// ciphertexts.
74+
func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 {
75+
return &EncapsulationKey768{dk.key.EncapsulationKey()}
76+
}
77+
78+
// An EncapsulationKey768 is the public key used to produce ciphertexts to be
79+
// decapsulated by the corresponding DecapsulationKey768.
80+
type EncapsulationKey768 struct {
81+
key *mlkem.EncapsulationKey768
82+
}
83+
84+
// NewEncapsulationKey768 parses an encapsulation key from its encoded form. If
85+
// the encapsulation key is not valid, NewEncapsulationKey768 returns an error.
86+
func NewEncapsulationKey768(encapsulationKey []byte) (*EncapsulationKey768, error) {
87+
key, err := mlkem.NewEncapsulationKey768(encapsulationKey)
88+
if err != nil {
89+
return nil, err
90+
}
91+
92+
return &EncapsulationKey768{key}, nil
93+
}
94+
95+
// Bytes returns the encapsulation key as a byte slice.
96+
func (ek *EncapsulationKey768) Bytes() []byte {
97+
return ek.key.Bytes()
98+
}
99+
100+
// Encapsulate generates a shared key and an associated ciphertext from an
101+
// encapsulation key, drawing random bytes from crypto/rand.
102+
//
103+
// The shared key must be kept secret.
104+
func (ek *EncapsulationKey768) Encapsulate() (ciphertext, sharedKey []byte) {
105+
return ek.key.Encapsulate()
106+
}

src/crypto/internal/fips140test/mlkem_test.go renamed to src/crypto/mlkem/mlkem_test.go

+32-8
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,13 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
package fipstest_test
6-
7-
// TODO(fips, #70122): move this to crypto/mlkem once it exists.
5+
package mlkem
86

97
import (
108
"bytes"
11-
. "crypto/internal/fips140/mlkem"
9+
"crypto/internal/fips140/mlkem"
1210
"crypto/internal/fips140/sha3"
1311
"crypto/rand"
14-
_ "embed"
1512
"encoding/hex"
1613
"flag"
1714
"testing"
@@ -192,7 +189,7 @@ func TestAccumulated(t *testing.T) {
192189
o.Write(ek.Bytes())
193190

194191
s.Read(msg[:])
195-
ct, k := ek.EncapsulateInternal(&msg)
192+
ct, k := ek.key.EncapsulateInternal(&msg)
196193
o.Write(ct)
197194
o.Write(k)
198195

@@ -226,7 +223,7 @@ func BenchmarkKeyGen(b *testing.B) {
226223
rand.Read(z[:])
227224
b.ResetTimer()
228225
for i := 0; i < b.N; i++ {
229-
dk := GenerateKeyInternal768(&d, &z)
226+
dk := mlkem.GenerateKeyInternal768(&d, &z)
230227
sink ^= dk.EncapsulationKey().Bytes()[0]
231228
}
232229
}
@@ -247,7 +244,7 @@ func BenchmarkEncaps(b *testing.B) {
247244
if err != nil {
248245
b.Fatal(err)
249246
}
250-
c, K := ek.EncapsulateInternal(&m)
247+
c, K := ek.key.EncapsulateInternal(&m)
251248
sink ^= c[0] ^ K[0]
252249
}
253250
}
@@ -307,3 +304,30 @@ func BenchmarkRoundTrip(b *testing.B) {
307304
}
308305
})
309306
}
307+
308+
// Test that the constants from the public API match the corresponding values from the internal API.
309+
func TestConstantSizes(t *testing.T) {
310+
if SharedKeySize != mlkem.SharedKeySize {
311+
t.Errorf("SharedKeySize mismatch: got %d, want %d", SharedKeySize, mlkem.SharedKeySize)
312+
}
313+
314+
if SeedSize != mlkem.SeedSize {
315+
t.Errorf("SeedSize mismatch: got %d, want %d", SeedSize, mlkem.SeedSize)
316+
}
317+
318+
if CiphertextSize768 != mlkem.CiphertextSize768 {
319+
t.Errorf("CiphertextSize768 mismatch: got %d, want %d", CiphertextSize768, mlkem.CiphertextSize768)
320+
}
321+
322+
if EncapsulationKeySize768 != mlkem.EncapsulationKeySize768 {
323+
t.Errorf("EncapsulationKeySize768 mismatch: got %d, want %d", EncapsulationKeySize768, mlkem.EncapsulationKeySize768)
324+
}
325+
326+
if CiphertextSize1024 != mlkem.CiphertextSize1024 {
327+
t.Errorf("CiphertextSize1024 mismatch: got %d, want %d", CiphertextSize1024, mlkem.CiphertextSize1024)
328+
}
329+
330+
if EncapsulationKeySize1024 != mlkem.EncapsulationKeySize1024 {
331+
t.Errorf("EncapsulationKeySize1024 mismatch: got %d, want %d", EncapsulationKeySize1024, mlkem.EncapsulationKeySize1024)
332+
}
333+
}

src/go/build/deps_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,8 @@ var depsRules = `
522522
523523
crypto/hmac < crypto/pbkdf2;
524524
525+
crypto/internal/fips140/mlkem < crypto/mlkem;
526+
525527
crypto/aes,
526528
crypto/des,
527529
crypto/ecdh,

0 commit comments

Comments
 (0)