Skip to content

Commit

Permalink
Adding decaf group.
Browse files Browse the repository at this point in the history
  • Loading branch information
armfazh committed Jul 24, 2020
1 parent 796b37e commit ec347f5
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 96 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -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

Expand Down
41 changes: 17 additions & 24 deletions sign/ed25519/ed25519.go
Expand Up @@ -43,8 +43,8 @@ import (
"crypto/subtle"
"errors"
"fmt"
"hash"
"io"
"strconv"
)

const (
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand All @@ -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()
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
19 changes: 4 additions & 15 deletions 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
Expand All @@ -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)
}
}
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions sign/ed25519/extra_test.go
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
67 changes: 29 additions & 38 deletions sign/ed448/ed448.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}

Expand All @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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")
}
}

Expand All @@ -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
}

Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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[:])
Expand All @@ -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[:])
Expand Down Expand Up @@ -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[:])
}
Expand Down Expand Up @@ -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))

Expand Down

0 comments on commit ec347f5

Please sign in to comment.