Skip to content

Commit

Permalink
Add ML-DSA (FIPS204) (#480)
Browse files Browse the repository at this point in the history
Add ML-DSA (FIPS204)

The final version of FIPS204 has released.

Changes:
* Update to final FIPS 204 standard.
* Remove old mode API and hook Dilithium and ML-DSA into generic signature API.
* Expose support for randomized ML-DSA mode. (It is not exposed in the generic signature API.)
* Remove old AES variants of Dilithium.
* ML-DSA test against reference implementation.
* Fix one bug in key derivation.
* dilithium: remove unused AES code
* mldsa: add ACVP test vectors
* Remove superfluous space
* Update ml-dsa documentation
* Remove dead code
* Avoid to export unsafeSignInternal.
* Update command for golangci-lint.
  • Loading branch information
bwesterb authored Oct 9, 2024
1 parent 2ba992f commit e2bbd01
Show file tree
Hide file tree
Showing 133 changed files with 4,557 additions and 3,774 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ endif
all: build

lint:
$(GOLANGCILINT) run --config $(ETC_DIR)/golangci.yml ./...
$(GOLANGCILINT) run

lint-fix:
$(GOLANGCILINT) run --config $(ETC_DIR)/golangci.yml --fix ./...
$(GOLANGCILINT) run --fix

build:
$(GO) build ./...
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Alternatively, look at the [Cloudflare Go](https://github.com/cloudflare/go/tree
|:---:|

- [Dilithium](./sign/dilithium): modes 2, 3, 5 ([Dilithium](https://pq-crystals.org/dilithium/)).
- [ML-DSA](./sign/mldsa): modes 44, 65, 87 ([FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf)).

### Zero-knowledge Proofs

Expand Down
102 changes: 11 additions & 91 deletions sign/dilithium/dilithium.go
Original file line number Diff line number Diff line change
@@ -1,111 +1,31 @@
//go:generate go run gen.go

// dilithium implements the CRYSTALS-Dilithium signature schemes
// Deprecated. This package implements Dilithium, an early proposal
// for what is now ML-DSA (FIPS 204). An implementation of ML-DSA
// can be found in sign/mldsa.
//
// Dilithium implements the CRYSTALS-Dilithium signature schemes
// as submitted to round3 of the NIST PQC competition and described in
//
// https://pq-crystals.org/dilithium/data/dilithium-specification-round3-20210208.pdf
//
// Each of the eight different modes of Dilithium is implemented by a
// Each of the three different modes of Dilithium is implemented by a
// subpackage. For instance, Dilithium2 (the recommended mode)
// can be found in
//
// github.com/cloudflare/circl/sign/dilithium/mode2
//
// If your choice for mode is fixed compile-time, use the subpackages.
// This package provides a convenient wrapper around all of the subpackages
// so one can be chosen at runtime.
// To choose a scheme at runtime, use the generic signatures API under
//
// github.com/cloudflare/circl/sign/schemes
//
// The authors of Dilithium recommend to combine it with a "pre-quantum"
// signature scheme. The packages
// The packages
//
// github.com/cloudflare/circl/sign/eddilithium2
// github.com/cloudflare/circl/sign/eddilithium3
//
// implement such hybrids of Dilithium2 with Ed25519 respectively and
// implement hybrids of Dilithium2 with Ed25519 respectively and
// Dilithium3 with Ed448. These packages are a drop in replacements for the
// mode subpackages of this package.
package dilithium

import (
"crypto"
"io"
)

// PublicKey is a Dilithium public key.
//
// The structure contains values precomputed during unpacking/key generation
// and is therefore significantly larger than a packed public key.
type PublicKey interface {
// Packs public key
Bytes() []byte
}

// PrivateKey is a Dilithium private key.
//
// The structure contains values precomputed during unpacking/key generation
// and is therefore significantly larger than a packed private key.
type PrivateKey interface {
// Packs private key
Bytes() []byte

crypto.Signer
}

// Mode is a certain configuration of the Dilithium signature scheme.
type Mode interface {
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error)

// NewKeyFromSeed derives a public/private key pair using the given seed.
// Panics if len(seed) != SeedSize()
NewKeyFromSeed(seed []byte) (PublicKey, PrivateKey)

// Sign signs the given message and returns the signature.
// It will panic if sk has not been generated for this mode.
Sign(sk PrivateKey, msg []byte) []byte

// Verify checks whether the given signature by pk on msg is valid.
// It will panic if pk is of the wrong mode.
Verify(pk PublicKey, msg []byte, signature []byte) bool

// Unpacks a public key. Panics if the buffer is not of PublicKeySize()
// length. Precomputes values to speed up subsequent calls to Verify.
PublicKeyFromBytes([]byte) PublicKey

// Unpacks a private key. Panics if the buffer is not
// of PrivateKeySize() length. Precomputes values to speed up subsequent
// calls to Sign(To).
PrivateKeyFromBytes([]byte) PrivateKey

// SeedSize returns the size of the seed for NewKeyFromSeed
SeedSize() int

// PublicKeySize returns the size of a packed PublicKey
PublicKeySize() int

// PrivateKeySize returns the size of a packed PrivateKey
PrivateKeySize() int

// SignatureSize returns the size of a signature
SignatureSize() int

// Name returns the name of this mode
Name() string
}

var modes = make(map[string]Mode)

// ModeNames returns the list of supported modes.
func ModeNames() []string {
names := []string{}
for name := range modes {
names = append(names, name)
}
return names
}

// ModeByName returns the mode with the given name or nil when not supported.
func ModeByName(name string) Mode {
return modes[name]
}
31 changes: 15 additions & 16 deletions sign/dilithium/dilithium_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"encoding/hex"
"testing"

"github.com/cloudflare/circl/sign/schemes"

"github.com/cloudflare/circl/internal/sha3"
)

Expand Down Expand Up @@ -34,29 +36,26 @@ func TestNewKeyFromSeed(t *testing.T) {
"Dilithium5", "3956d812a7961af6e5dad16af15c736c",
"665388291aa01e12e7f94bdc7769db18",
},
{
"Dilithium2-AES", "8466a752b0a09e63e42f66d3174a6471",
"c3f8e705a0d8dfd489b98b205670f393",
},
{
"Dilithium3-AES", "2bb713ba7cb15f3ebf05c4c1fbb1b03c",
"eb2bd8d98630835a3b18594ac436368b",
},
{
"Dilithium5-AES", "a613a08b564ee8717ba4f5ccfddc2693",
"2f541bf6fedd12854d06a6b80090932a",
},
} {
t.Run(tc.name, func(t *testing.T) {
mode := ModeByName(tc.name)
mode := schemes.ByName(tc.name)
if mode == nil {
t.Fatal()
}
var seed [32]byte
pk, sk := mode.NewKeyFromSeed(seed[:])
pk, sk := mode.DeriveKey(seed[:])

ppk, err := pk.MarshalBinary()
if err != nil {
t.Fatal(err)
}
psk, err := sk.MarshalBinary()
if err != nil {
t.Fatal(err)
}

pkh := hexHash(pk.Bytes())
skh := hexHash(sk.Bytes())
pkh := hexHash(ppk)
skh := hexHash(psk)
if pkh != tc.epk {
t.Fatalf("%s expected pk %s, got %s", tc.name, tc.epk, pkh)
}
Expand Down
56 changes: 0 additions & 56 deletions sign/dilithium/example_test.go

This file was deleted.

Loading

0 comments on commit e2bbd01

Please sign in to comment.