Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ML-DSA (FIPS204) #480

Merged
merged 11 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
bwesterb marked this conversation as resolved.
Show resolved Hide resolved

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