From ec347f5313ca0b5aec78b2513c10faa479c05175 Mon Sep 17 00:00:00 2001 From: armfazh Date: Fri, 24 Jul 2020 15:28:34 -0700 Subject: [PATCH] Adding decaf group. --- README.md | 3 +- sign/ed25519/ed25519.go | 41 +++++++++------------- sign/ed25519/ed25519_test.go | 19 +++------- sign/ed25519/extra_test.go | 4 +-- sign/ed448/ed448.go | 67 ++++++++++++++++-------------------- sign/ed448/ed448_test.go | 20 +++-------- 6 files changed, 58 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index ed45bfbd0..0827a2426 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,12 @@ Version numbers are [Semvers](https://semver.org/). We release a minor version f | PQ Key Exchange | SIDH | SIDH provide key exchange mechanisms using ephemeral keys. | Post-quantum key exchange in TLS | | PQ Key Exchange | cSIDH | Isogeny based drop-in replacement for Diffie–Hellman | Post-Quantum Key exchange. | | PQ KEM | SIKE | SIKE is a key encapsulation mechanism (KEM). | Post-quantum key exchange in TLS | +| PQ Digital Signatures | Dilithium, Hybrid modes | Lattice (Module LWE) based signature scheme | Post-Quantum PKI | | Key Exchange | X25519, X448 | RFC-7748 provides new key exchange mechanisms based on Montgomery elliptic curves. | TLS 1.3. Secure Shell. | | Key Exchange | FourQ | One of the fastest elliptic curves at 128-bit security level. | Experimental for key agreement and digital signatures. | | Key Exchange / Digital signatures | P-384 | Our optimizations reduce the burden when moving from P-256 to P-384. | ECDSA and ECDH using Suite B at top secret level. | | Digital Signatures | Ed25519, Ed448 | RFC-8032 provides new signature schemes based on Edwards curves. | Digital certificates and authentication. | -| PQ Digital Signatures | Dilithium, Hybrid modes | Lattice (Module LWE) based signature scheme | Post-Quantum PKI | +| Groups | Decaf | Prime-order groups. | Protocols based on the Discrete Logarithm Problem. | ### Work in Progress diff --git a/sign/ed25519/ed25519.go b/sign/ed25519/ed25519.go index 02adf0c67..a989bcb5d 100644 --- a/sign/ed25519/ed25519.go +++ b/sign/ed25519/ed25519.go @@ -43,8 +43,8 @@ import ( "crypto/subtle" "errors" "fmt" + "hash" "io" - "strconv" ) const ( @@ -95,17 +95,14 @@ type PrivateKey []byte // Equal reports whether priv and x have the same value. func (priv PrivateKey) Equal(x crypto.PrivateKey) bool { xx, ok := x.(PrivateKey) - if !ok { - return false - } - return subtle.ConstantTimeCompare(priv, xx) == 1 + return ok && subtle.ConstantTimeCompare(priv, xx) == 1 } // Public returns the PublicKey corresponding to priv. func (priv PrivateKey) Public() crypto.PublicKey { - publicKey := make([]byte, PublicKeySize) + publicKey := make(PublicKey, PublicKeySize) copy(publicKey, priv[SeedSize:]) - return PublicKey(publicKey) + return publicKey } // Seed returns the private key seed corresponding to priv. It is provided for @@ -163,10 +160,9 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { return nil, nil, err } - privateKey := NewKeyFromSeed(seed) - publicKey := make([]byte, PublicKeySize) - copy(publicKey, privateKey[SeedSize:]) - + privateKey := make(PrivateKey, PrivateKeySize) + publicKey := make(PublicKey, PublicKeySize) + newKeyFromSeed(privateKey, publicKey, seed) return publicKey, privateKey, nil } @@ -175,27 +171,29 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { // with RFC 8032. RFC 8032's private keys correspond to seeds in this // package. func NewKeyFromSeed(seed []byte) PrivateKey { - privateKey := make([]byte, PrivateKeySize) - newKeyFromSeed(privateKey, seed) + privateKey := make(PrivateKey, PrivateKeySize) + newKeyFromSeed(privateKey, PublicKey(privateKey[SeedSize:]), seed) return privateKey } -func newKeyFromSeed(privateKey, seed []byte) { +func newKeyFromSeed(privateKey PrivateKey, publicKey PublicKey, seed []byte) { if l := len(seed); l != SeedSize { - panic("ed25519: bad seed length: " + strconv.Itoa(l)) + panic(fmt.Errorf("ed25519: bad seed length: %v", l)) } var P pointR1 k := sha512.Sum512(seed) clamp(k[:]) reduceModOrder(k[:paramB], false) P.fixedMult(k[:paramB]) - copy(privateKey[:SeedSize], seed) _ = P.ToBytes(privateKey[SeedSize:]) + + copy(privateKey[:SeedSize], seed) + copy(publicKey[:PublicKeySize], privateKey[SeedSize:]) } func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { if l := len(privateKey); l != PrivateKeySize { - panic("ed25519: bad private key length: " + strconv.Itoa(l)) + panic(fmt.Errorf("ed25519: bad private key length: %v", l)) } H := sha512.New() @@ -216,10 +214,7 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash prefix, s := h[paramB:], h[:paramB] // 2. Compute SHA-512(dom2(F, C) || prefix || PH(M)) - H.Reset() - writeDom(H, ctx, preHash) - _, _ = H.Write(prefix) _, _ = H.Write(PHM) r := H.Sum(nil) @@ -234,10 +229,7 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash } // 4. Compute SHA512(dom2(F, C) || R || A || PH(M)). - H.Reset() - writeDom(H, ctx, preHash) - _, _ = H.Write(R) _, _ = H.Write(privateKey[SeedSize:]) _, _ = H.Write(PHM) @@ -413,7 +405,8 @@ func isLessThanOrder(x []byte) bool { return x[i] < order[i] } -func writeDom(h io.Writer, ctx []byte, preHash bool) { +func writeDom(h hash.Hash, ctx []byte, preHash bool) { + h.Reset() dom2 := "SigEd25519 no Ed25519 collisions" if len(ctx) > 0 { diff --git a/sign/ed25519/ed25519_test.go b/sign/ed25519/ed25519_test.go index e7e39f5c2..ac734ebc5 100644 --- a/sign/ed25519/ed25519_test.go +++ b/sign/ed25519/ed25519_test.go @@ -1,20 +1,12 @@ package ed25519_test import ( + "crypto/rand" "testing" "github.com/cloudflare/circl/sign/ed25519" ) -type zeroReader struct{} - -func (zeroReader) Read(buf []byte) (int, error) { - for i := range buf { - buf[i] = 0 - } - return len(buf), nil -} - func TestMalleability(t *testing.T) { // https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test // that s be in [0, order). This prevents someone from adding a multiple of @@ -40,9 +32,8 @@ func TestMalleability(t *testing.T) { } func BenchmarkKeyGeneration(b *testing.B) { - var zero zeroReader for i := 0; i < b.N; i++ { - if _, _, err := ed25519.GenerateKey(zero); err != nil { + if _, _, err := ed25519.GenerateKey(rand.Reader); err != nil { b.Fatal(err) } } @@ -57,8 +48,7 @@ func BenchmarkNewKeyFromSeed(b *testing.B) { } func BenchmarkSigning(b *testing.B) { - var zero zeroReader - _, priv, err := ed25519.GenerateKey(zero) + _, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } @@ -71,8 +61,7 @@ func BenchmarkSigning(b *testing.B) { } func BenchmarkVerification(b *testing.B) { - var zero zeroReader - pub, priv, err := ed25519.GenerateKey(zero) + pub, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } diff --git a/sign/ed25519/extra_test.go b/sign/ed25519/extra_test.go index cd1a42bd7..85db8a699 100644 --- a/sign/ed25519/extra_test.go +++ b/sign/ed25519/extra_test.go @@ -149,7 +149,7 @@ func BenchmarkEd25519Ph(b *testing.B) { ctx := "" b.ResetTimer() for i := 0; i < b.N; i++ { - _ = ed25519.SignPh(key, msg, ctx) + ed25519.SignPh(key, msg, ctx) } }) b.Run("Verify", func(b *testing.B) { @@ -171,7 +171,7 @@ func BenchmarkEd25519Ctx(b *testing.B) { _, priv, _ := ed25519.GenerateKey(rand.Reader) b.ResetTimer() for i := 0; i < b.N; i++ { - _ = ed25519.SignWithCtx(priv, msg, ctx) + ed25519.SignWithCtx(priv, msg, ctx) } }) b.Run("Verify", func(b *testing.B) { diff --git a/sign/ed448/ed448.go b/sign/ed448/ed448.go index 07563c217..49c2e269d 100644 --- a/sign/ed448/ed448.go +++ b/sign/ed448/ed448.go @@ -28,10 +28,8 @@ import ( "crypto" cryptoRand "crypto/rand" "crypto/subtle" - "errors" "fmt" "io" - "strconv" sha3 "github.com/cloudflare/circl/internal/shake" "github.com/cloudflare/circl/sign/ed448/internal/goldilocks" @@ -65,7 +63,8 @@ type SignerOptions struct { // Its length must be less or equal than 255 bytes. Context string - // Scheme is an identifier for choosing a signature scheme. + // Scheme is an identifier for choosing a signature scheme. The zero value + // is ED448. Scheme SchemeID } @@ -83,10 +82,7 @@ type PublicKey []byte // Equal reports whether pub and x have the same value. func (pub PublicKey) Equal(x crypto.PublicKey) bool { xx, ok := x.(PublicKey) - if !ok { - return false - } - return bytes.Equal(pub, xx) + return ok && bytes.Equal(pub, xx) } // PrivateKey is the type of Ed448 private keys. It implements crypto.Signer. @@ -95,17 +91,14 @@ type PrivateKey []byte // Equal reports whether priv and x have the same value. func (priv PrivateKey) Equal(x crypto.PrivateKey) bool { xx, ok := x.(PrivateKey) - if !ok { - return false - } - return subtle.ConstantTimeCompare(priv, xx) == 1 + return ok && subtle.ConstantTimeCompare(priv, xx) == 1 } // Public returns the PublicKey corresponding to priv. func (priv PrivateKey) Public() crypto.PublicKey { - publicKey := make([]byte, PublicKeySize) + publicKey := make(PublicKey, PublicKeySize) copy(publicKey, priv[SeedSize:]) - return PublicKey(publicKey) + return publicKey } // Seed returns the private key seed corresponding to priv. It is provided for @@ -143,7 +136,7 @@ func (priv PrivateKey) Sign( case scheme == ED448Ph && opts.HashFunc() == crypto.Hash(0): return SignPh(priv, message, ctx), nil default: - return nil, errors.New("ed448: bad hash algorithm") + return nil, fmt.Errorf("ed448: bad hash algorithm") } } @@ -159,10 +152,9 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { return nil, nil, err } - privateKey := NewKeyFromSeed(seed) - publicKey := make([]byte, PublicKeySize) - copy(publicKey, privateKey[SeedSize:]) - + privateKey := make(PrivateKey, PrivateKeySize) + publicKey := make(PublicKey, PublicKeySize) + newKeyFromSeed(privateKey, publicKey, seed) return publicKey, privateKey, nil } @@ -171,14 +163,14 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { // with RFC 8032. RFC 8032's private keys correspond to seeds in this // package. func NewKeyFromSeed(seed []byte) PrivateKey { - privateKey := make([]byte, PrivateKeySize) - newKeyFromSeed(privateKey, seed) + privateKey := make(PrivateKey, PrivateKeySize) + newKeyFromSeed(privateKey, PublicKey(privateKey[SeedSize:]), seed) return privateKey } -func newKeyFromSeed(privateKey, seed []byte) { +func newKeyFromSeed(privateKey PrivateKey, publicKey PublicKey, seed []byte) { if l := len(seed); l != SeedSize { - panic("ed448: bad seed length: " + strconv.Itoa(l)) + panic(fmt.Errorf("ed448: bad seed length: %v", l)) } var h [hashSize]byte @@ -188,18 +180,21 @@ func newKeyFromSeed(privateKey, seed []byte) { s := &goldilocks.Scalar{} deriveSecretScalar(s, h[:paramB]) - copy(privateKey[:SeedSize], seed) var P goldilocks.Point P.ScalarBaseMult(s) - err := P.Encode(&pair.public) - if err != nil { + var encP [goldilocks.EncodingSize]byte + if err := P.Encode(&encP); err != nil { panic(err) } + + copy(privateKey[:SeedSize], seed) + copy(privateKey[SeedSize:], encP[:]) + copy(publicKey[:PublicKeySize], encP[:]) } func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { if len(ctx) > ContextMaxSize { - panic(fmt.Errorf("ed448: bad context length: " + strconv.Itoa(len(ctx)))) + panic(fmt.Errorf("ed448: bad context length: %v", len(ctx))) } H := sha3.NewShake256() @@ -225,10 +220,7 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash // 2. Compute SHAKE256(dom4(F, C) || prefix || PH(M), 114). var rPM [hashSize]byte - H.Reset() - writeDom(&H, ctx, preHash) - _, _ = H.Write(prefix) _, _ = H.Write(PHM) _, _ = H.Read(rPM[:]) @@ -239,15 +231,14 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash var R goldilocks.Point var encR [goldilocks.EncodingSize]byte R.ScalarBaseMult(r) - err := R.Encode(&encR) + if err := R.Encode(&encR); err != nil { + panic(err) + } // 4. Compute SHAKE256(dom4(F, C) || R || A || PH(M), 114) var hRAM [hashSize]byte - H.Reset() - writeDom(&H, ctx, preHash) - - _, _ = H.Write(R) + _, _ = H.Write(encR[:]) _, _ = H.Write(privateKey[SeedSize:]) _, _ = H.Write(PHM) _, _ = H.Read(hRAM[:]) @@ -333,9 +324,8 @@ func verify(public PublicKey, message, signature, ctx []byte, preHash bool) bool var Q goldilocks.Point Q.CombinedMult(S, k, P) var encR [goldilocks.EncodingSize]byte - err = Q.Encode(&encR) - if err != nil { - return false + if err = Q.Encode(&encR); err != nil { + panic(err) } return bytes.Equal(R, encR[:]) } @@ -400,7 +390,8 @@ func isLessThanOrder(x []byte) bool { return x[paramB-1] == 0 && x[i] < order[i] } -func writeDom(h io.Writer, ctx []byte, preHash bool) { +func writeDom(h *sha3.Shake, ctx []byte, preHash bool) { + h.Reset() dom4 := "SigEd448" _, _ = h.Write([]byte(dom4)) diff --git a/sign/ed448/ed448_test.go b/sign/ed448/ed448_test.go index 91349609d..0b1f061de 100644 --- a/sign/ed448/ed448_test.go +++ b/sign/ed448/ed448_test.go @@ -12,15 +12,6 @@ import ( "github.com/cloudflare/circl/sign/ed448" ) -type zeroReader struct{} - -func (zeroReader) Read(buf []byte) (int, error) { - for i := range buf { - buf[i] = 0 - } - return len(buf), nil -} - func TestEqual(t *testing.T) { public, private, _ := ed448.GenerateKey(rand.Reader) @@ -213,9 +204,8 @@ func TestErrors(t *testing.T) { } func BenchmarkKeyGeneration(b *testing.B) { - var zero zeroReader for i := 0; i < b.N; i++ { - if _, _, err := ed448.GenerateKey(zero); err != nil { + if _, _, err := ed448.GenerateKey(rand.Reader); err != nil { b.Fatal(err) } } @@ -230,8 +220,7 @@ func BenchmarkNewKeyFromSeed(b *testing.B) { } func BenchmarkSigning(b *testing.B) { - var zero zeroReader - _, priv, err := ed448.GenerateKey(zero) + _, priv, err := ed448.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } @@ -245,8 +234,7 @@ func BenchmarkSigning(b *testing.B) { } func BenchmarkVerification(b *testing.B) { - var zero zeroReader - pub, priv, err := ed448.GenerateKey(zero) + pub, priv, err := ed448.GenerateKey(rand.Reader) if err != nil { b.Fatal(err) } @@ -268,7 +256,7 @@ func BenchmarkEd448Ph(b *testing.B) { ctx := "" b.ResetTimer() for i := 0; i < b.N; i++ { - _ = ed448.SignPh(key, msg, ctx) + ed448.SignPh(key, msg, ctx) } }) b.Run("Verify", func(b *testing.B) {