mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
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 <filippo@golang.org> Reviewed-by: Roland Shoemaker <roland@golang.org> Auto-Submit: Filippo Valsorda <filippo@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
parent
b2f7a2154a
commit
9aaef91d3e
32
api/next/70122.txt
Normal file
32
api/next/70122.txt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
pkg crypto/mlkem, const CiphertextSize1024 = 1568 #70122
|
||||||
|
pkg crypto/mlkem, const CiphertextSize1024 ideal-int #70122
|
||||||
|
pkg crypto/mlkem, const CiphertextSize768 = 1088 #70122
|
||||||
|
pkg crypto/mlkem, const CiphertextSize768 ideal-int #70122
|
||||||
|
pkg crypto/mlkem, const EncapsulationKeySize1024 = 1568 #70122
|
||||||
|
pkg crypto/mlkem, const EncapsulationKeySize1024 ideal-int #70122
|
||||||
|
pkg crypto/mlkem, const EncapsulationKeySize768 = 1184 #70122
|
||||||
|
pkg crypto/mlkem, const EncapsulationKeySize768 ideal-int #70122
|
||||||
|
pkg crypto/mlkem, const SeedSize = 64 #70122
|
||||||
|
pkg crypto/mlkem, const SeedSize ideal-int #70122
|
||||||
|
pkg crypto/mlkem, const SharedKeySize = 32 #70122
|
||||||
|
pkg crypto/mlkem, const SharedKeySize ideal-int #70122
|
||||||
|
pkg crypto/mlkem, func GenerateKey1024() (*DecapsulationKey1024, error) #70122
|
||||||
|
pkg crypto/mlkem, func GenerateKey768() (*DecapsulationKey768, error) #70122
|
||||||
|
pkg crypto/mlkem, func NewDecapsulationKey1024([]uint8) (*DecapsulationKey1024, error) #70122
|
||||||
|
pkg crypto/mlkem, func NewDecapsulationKey768([]uint8) (*DecapsulationKey768, error) #70122
|
||||||
|
pkg crypto/mlkem, func NewEncapsulationKey1024([]uint8) (*EncapsulationKey1024, error) #70122
|
||||||
|
pkg crypto/mlkem, func NewEncapsulationKey768([]uint8) (*EncapsulationKey768, error) #70122
|
||||||
|
pkg crypto/mlkem, method (*DecapsulationKey1024) Bytes() []uint8 #70122
|
||||||
|
pkg crypto/mlkem, method (*DecapsulationKey1024) Decapsulate([]uint8) ([]uint8, error) #70122
|
||||||
|
pkg crypto/mlkem, method (*DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 #70122
|
||||||
|
pkg crypto/mlkem, method (*DecapsulationKey768) Bytes() []uint8 #70122
|
||||||
|
pkg crypto/mlkem, method (*DecapsulationKey768) Decapsulate([]uint8) ([]uint8, error) #70122
|
||||||
|
pkg crypto/mlkem, method (*DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 #70122
|
||||||
|
pkg crypto/mlkem, method (*EncapsulationKey1024) Bytes() []uint8 #70122
|
||||||
|
pkg crypto/mlkem, method (*EncapsulationKey1024) Encapsulate() ([]uint8, []uint8) #70122
|
||||||
|
pkg crypto/mlkem, method (*EncapsulationKey768) Bytes() []uint8 #70122
|
||||||
|
pkg crypto/mlkem, method (*EncapsulationKey768) Encapsulate() ([]uint8, []uint8) #70122
|
||||||
|
pkg crypto/mlkem, type DecapsulationKey1024 struct #70122
|
||||||
|
pkg crypto/mlkem, type DecapsulationKey768 struct #70122
|
||||||
|
pkg crypto/mlkem, type EncapsulationKey1024 struct #70122
|
||||||
|
pkg crypto/mlkem, type EncapsulationKey768 struct #70122
|
3
doc/next/6-stdlib/4-mlkem.md
Normal file
3
doc/next/6-stdlib/4-mlkem.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
A new `crypto/mlkem` package was added, implementing ML-KEM (formerly known as
|
||||||
|
Kyber), as specified in [NIST FIPS 203](https://doi.org/10.6028/NIST.FIPS.203).
|
||||||
|
<!-- go.dev/issue/70122 -->
|
1
doc/next/6-stdlib/99-minor/crypto/mlkem/70122.md
Normal file
1
doc/next/6-stdlib/99-minor/crypto/mlkem/70122.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!-- This is a new package; covered in 6-stdlib/4-mlkem.md. -->
|
96
src/crypto/mlkem/mlkem1024.go
Normal file
96
src/crypto/mlkem/mlkem1024.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2023 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package mlkem
|
||||||
|
|
||||||
|
import "crypto/internal/fips140/mlkem"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CiphertextSize1024 is the size of a ciphertext produced by the 1024-bit
|
||||||
|
// variant of ML-KEM.
|
||||||
|
CiphertextSize1024 = 1568
|
||||||
|
|
||||||
|
// EncapsulationKeySize1024 is the size of an encapsulation key for the
|
||||||
|
// 1024-bit variant of ML-KEM.
|
||||||
|
EncapsulationKeySize1024 = 1568
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecapsulationKey1024 is the secret key used to decapsulate a shared key
|
||||||
|
// from a ciphertext. It includes various precomputed values.
|
||||||
|
type DecapsulationKey1024 struct {
|
||||||
|
key *mlkem.DecapsulationKey1024
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKey1024 generates a new decapsulation key, drawing random bytes from
|
||||||
|
// crypto/rand. The decapsulation key must be kept secret.
|
||||||
|
func GenerateKey1024() (*DecapsulationKey1024, error) {
|
||||||
|
key, err := mlkem.GenerateKey1024()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DecapsulationKey1024{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecapsulationKey1024 parses a decapsulation key from a 64-byte seed in the
|
||||||
|
// "d || z" form. The seed must be uniformly random.
|
||||||
|
func NewDecapsulationKey1024(seed []byte) (*DecapsulationKey1024, error) {
|
||||||
|
key, err := mlkem.NewDecapsulationKey1024(seed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DecapsulationKey1024{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form.
|
||||||
|
//
|
||||||
|
// The decapsulation key must be kept secret.
|
||||||
|
func (dk *DecapsulationKey1024) Bytes() []byte {
|
||||||
|
return dk.key.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
return dk.key.Decapsulate(ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncapsulationKey returns the public encapsulation key necessary to produce
|
||||||
|
// ciphertexts.
|
||||||
|
func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 {
|
||||||
|
return &EncapsulationKey1024{dk.key.EncapsulationKey()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An EncapsulationKey1024 is the public key used to produce ciphertexts to be
|
||||||
|
// decapsulated by the corresponding DecapsulationKey1024.
|
||||||
|
type EncapsulationKey1024 struct {
|
||||||
|
key *mlkem.EncapsulationKey1024
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
key, err := mlkem.NewEncapsulationKey1024(encapsulationKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &EncapsulationKey1024{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the encapsulation key as a byte slice.
|
||||||
|
func (ek *EncapsulationKey1024) Bytes() []byte {
|
||||||
|
return ek.key.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encapsulate generates a shared key and an associated ciphertext from an
|
||||||
|
// encapsulation key, drawing random bytes from crypto/rand.
|
||||||
|
//
|
||||||
|
// The shared key must be kept secret.
|
||||||
|
func (ek *EncapsulationKey1024) Encapsulate() (ciphertext, sharedKey []byte) {
|
||||||
|
return ek.key.Encapsulate()
|
||||||
|
}
|
106
src/crypto/mlkem/mlkem768.go
Normal file
106
src/crypto/mlkem/mlkem768.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Copyright 2023 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package mlkem implements the quantum-resistant key encapsulation method
|
||||||
|
// ML-KEM (formerly known as Kyber), as specified in [NIST FIPS 203].
|
||||||
|
//
|
||||||
|
// [NIST FIPS 203]: https://doi.org/10.6028/NIST.FIPS.203
|
||||||
|
package mlkem
|
||||||
|
|
||||||
|
import "crypto/internal/fips140/mlkem"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SharedKeySize is the size of a shared key produced by ML-KEM.
|
||||||
|
SharedKeySize = 32
|
||||||
|
|
||||||
|
// SeedSize is the size of a seed used to generate a decapsulation key.
|
||||||
|
SeedSize = 64
|
||||||
|
|
||||||
|
// CiphertextSize768 is the size of a ciphertext produced by the 768-bit
|
||||||
|
// variant of ML-KEM.
|
||||||
|
CiphertextSize768 = 1088
|
||||||
|
|
||||||
|
// EncapsulationKeySize768 is the size of an encapsulation key for the
|
||||||
|
// 768-bit variant of ML-KEM.
|
||||||
|
EncapsulationKeySize768 = 1184
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecapsulationKey768 is the secret key used to decapsulate a shared key
|
||||||
|
// from a ciphertext. It includes various precomputed values.
|
||||||
|
type DecapsulationKey768 struct {
|
||||||
|
key *mlkem.DecapsulationKey768
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKey768 generates a new decapsulation key, drawing random bytes from
|
||||||
|
// crypto/rand. The decapsulation key must be kept secret.
|
||||||
|
func GenerateKey768() (*DecapsulationKey768, error) {
|
||||||
|
key, err := mlkem.GenerateKey768()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DecapsulationKey768{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecapsulationKey768 parses a decapsulation key from a 64-byte seed in the
|
||||||
|
// "d || z" form. The seed must be uniformly random.
|
||||||
|
func NewDecapsulationKey768(seed []byte) (*DecapsulationKey768, error) {
|
||||||
|
key, err := mlkem.NewDecapsulationKey768(seed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DecapsulationKey768{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form.
|
||||||
|
//
|
||||||
|
// The decapsulation key must be kept secret.
|
||||||
|
func (dk *DecapsulationKey768) Bytes() []byte {
|
||||||
|
return dk.key.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) {
|
||||||
|
return dk.key.Decapsulate(ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncapsulationKey returns the public encapsulation key necessary to produce
|
||||||
|
// ciphertexts.
|
||||||
|
func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 {
|
||||||
|
return &EncapsulationKey768{dk.key.EncapsulationKey()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An EncapsulationKey768 is the public key used to produce ciphertexts to be
|
||||||
|
// decapsulated by the corresponding DecapsulationKey768.
|
||||||
|
type EncapsulationKey768 struct {
|
||||||
|
key *mlkem.EncapsulationKey768
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncapsulationKey768 parses an encapsulation key from its encoded form. If
|
||||||
|
// the encapsulation key is not valid, NewEncapsulationKey768 returns an error.
|
||||||
|
func NewEncapsulationKey768(encapsulationKey []byte) (*EncapsulationKey768, error) {
|
||||||
|
key, err := mlkem.NewEncapsulationKey768(encapsulationKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &EncapsulationKey768{key}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the encapsulation key as a byte slice.
|
||||||
|
func (ek *EncapsulationKey768) Bytes() []byte {
|
||||||
|
return ek.key.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encapsulate generates a shared key and an associated ciphertext from an
|
||||||
|
// encapsulation key, drawing random bytes from crypto/rand.
|
||||||
|
//
|
||||||
|
// The shared key must be kept secret.
|
||||||
|
func (ek *EncapsulationKey768) Encapsulate() (ciphertext, sharedKey []byte) {
|
||||||
|
return ek.key.Encapsulate()
|
||||||
|
}
|
@ -2,16 +2,13 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package fipstest_test
|
package mlkem
|
||||||
|
|
||||||
// TODO(fips, #70122): move this to crypto/mlkem once it exists.
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
. "crypto/internal/fips140/mlkem"
|
"crypto/internal/fips140/mlkem"
|
||||||
"crypto/internal/fips140/sha3"
|
"crypto/internal/fips140/sha3"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
_ "embed"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"testing"
|
"testing"
|
||||||
@ -192,7 +189,7 @@ func TestAccumulated(t *testing.T) {
|
|||||||
o.Write(ek.Bytes())
|
o.Write(ek.Bytes())
|
||||||
|
|
||||||
s.Read(msg[:])
|
s.Read(msg[:])
|
||||||
ct, k := ek.EncapsulateInternal(&msg)
|
ct, k := ek.key.EncapsulateInternal(&msg)
|
||||||
o.Write(ct)
|
o.Write(ct)
|
||||||
o.Write(k)
|
o.Write(k)
|
||||||
|
|
||||||
@ -226,7 +223,7 @@ func BenchmarkKeyGen(b *testing.B) {
|
|||||||
rand.Read(z[:])
|
rand.Read(z[:])
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
dk := GenerateKeyInternal768(&d, &z)
|
dk := mlkem.GenerateKeyInternal768(&d, &z)
|
||||||
sink ^= dk.EncapsulationKey().Bytes()[0]
|
sink ^= dk.EncapsulationKey().Bytes()[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,7 +244,7 @@ func BenchmarkEncaps(b *testing.B) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
c, K := ek.EncapsulateInternal(&m)
|
c, K := ek.key.EncapsulateInternal(&m)
|
||||||
sink ^= c[0] ^ K[0]
|
sink ^= c[0] ^ K[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -307,3 +304,30 @@ func BenchmarkRoundTrip(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that the constants from the public API match the corresponding values from the internal API.
|
||||||
|
func TestConstantSizes(t *testing.T) {
|
||||||
|
if SharedKeySize != mlkem.SharedKeySize {
|
||||||
|
t.Errorf("SharedKeySize mismatch: got %d, want %d", SharedKeySize, mlkem.SharedKeySize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if SeedSize != mlkem.SeedSize {
|
||||||
|
t.Errorf("SeedSize mismatch: got %d, want %d", SeedSize, mlkem.SeedSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if CiphertextSize768 != mlkem.CiphertextSize768 {
|
||||||
|
t.Errorf("CiphertextSize768 mismatch: got %d, want %d", CiphertextSize768, mlkem.CiphertextSize768)
|
||||||
|
}
|
||||||
|
|
||||||
|
if EncapsulationKeySize768 != mlkem.EncapsulationKeySize768 {
|
||||||
|
t.Errorf("EncapsulationKeySize768 mismatch: got %d, want %d", EncapsulationKeySize768, mlkem.EncapsulationKeySize768)
|
||||||
|
}
|
||||||
|
|
||||||
|
if CiphertextSize1024 != mlkem.CiphertextSize1024 {
|
||||||
|
t.Errorf("CiphertextSize1024 mismatch: got %d, want %d", CiphertextSize1024, mlkem.CiphertextSize1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
if EncapsulationKeySize1024 != mlkem.EncapsulationKeySize1024 {
|
||||||
|
t.Errorf("EncapsulationKeySize1024 mismatch: got %d, want %d", EncapsulationKeySize1024, mlkem.EncapsulationKeySize1024)
|
||||||
|
}
|
||||||
|
}
|
@ -522,6 +522,8 @@ var depsRules = `
|
|||||||
|
|
||||||
crypto/hmac < crypto/pbkdf2;
|
crypto/hmac < crypto/pbkdf2;
|
||||||
|
|
||||||
|
crypto/internal/fips140/mlkem < crypto/mlkem;
|
||||||
|
|
||||||
crypto/aes,
|
crypto/aes,
|
||||||
crypto/des,
|
crypto/des,
|
||||||
crypto/ecdh,
|
crypto/ecdh,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user