From 750f8cd2a894bff0712ebae674ebba7c92384e25 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 15 Jul 2020 17:04:06 +0200 Subject: [PATCH 01/20] WIP signature API --- sign/api.go | 413 +++++++++++++++++++++++++++++++++++++++++++ sign/api_test.go | 139 +++++++++++++++ sign/doc.go | 2 - sign/eddilithium3.go | 108 +++++++++++ 4 files changed, 660 insertions(+), 2 deletions(-) create mode 100644 sign/api.go create mode 100644 sign/api_test.go delete mode 100644 sign/doc.go create mode 100644 sign/eddilithium3.go diff --git a/sign/api.go b/sign/api.go new file mode 100644 index 000000000..dad463bf9 --- /dev/null +++ b/sign/api.go @@ -0,0 +1,413 @@ +// package sign provides a unified interface to all signature schemes +// supported by Circl. +package sign + +import ( + "bytes" + "crypto" + "encoding" + "encoding/asn1" + "encoding/pem" + "errors" + "fmt" + "io" + "strings" + + "crypto/x509/pkix" +) + +var schemes = [...]Scheme{ + EdDilithium3, +} + +type SignatureOpts struct { + // If non-empty, includes the given context in the signature if supported + // and will cause an error during signing otherwise. + Context string +} + +// A public key is used to verify a signature set by the corresponding private +// key. +type PublicKey interface { + // Returns the signature scheme for this public key. + Scheme() Scheme + + encoding.BinaryMarshaler +} + +// A private key allows one to create signatures. +type PrivateKey interface { + // Returns the signature scheme for this private key. + Scheme() Scheme + + // For compatibility with Go standard library + crypto.Signer + + encoding.BinaryMarshaler +} + +// A Scheme represents a specific instance of a signature scheme. +type Scheme interface { + // GenerateKey creates a new key-pair. + GenerateKey() (PublicKey, PrivateKey, error) + + // Creates a signature using the PrivateKey on the given message and + // returns the signature. opts are additional options which can be nil. + Sign(sk PrivateKey, message []byte, opts *SignatureOpts) []byte + + // Checks whether the given signature is a valid signature set by + // the private key corresponding to the given public key on the + // given message. opts are additional options which can be nil. + Verify(pk PublicKey, message []byte, signature []byte, opts *SignatureOpts) bool + + // Deterministically derives a keypair from a seed. If you're unsure, + // you're better off using GenerateKey(). + // + // Panics if seed is not of length SeedSize(). + DeriveKey(seed []byte) (PublicKey, PrivateKey) + + // Unmarshals a PublicKey from the provided buffer. + UnmarshalBinaryPublicKey([]byte) (PublicKey, error) + + // Unmarshals a PublicKey from the provided buffer. + UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error) + + // Size of binary marshalled public keys + PublicKeySize() uint + + // Size of binary marshalled public keys + PrivateKeySize() uint + + // Name of the scheme + Name() string + + // Size of signatures + SignatureSize() uint + + // Size of seeds + SeedSize() uint +} + +// Additional methods when the signature scheme is supported in X509. +type CertificateScheme interface { + // Return the appropriate OIDs for this instance. It is implicitly + // assumed that the encoding is simple: e.g. uses the same OID for + // signature and public key like Ed25519. + Oid() asn1.ObjectIdentifier +} + +// Additional methods when the signature scheme is supported in TLS. +type TLSScheme interface { + TLSIdentifier() uint +} + +// SchemeByName returns the scheme with the given name and nil if it is not +// supported. Use ListSchemes() to list supported schemes. +// Names are case insensitive. +func SchemeByName(name string) Scheme { + // XXX add a (compile time?) lookup table + name = strings.ToLower(name) + for _, scheme := range schemes { + if strings.ToLower(scheme.Name()) == name { + return scheme + } + } + return nil +} + +// ListSchemeNames returns the names of all schemes supported. +func ListSchemeNames() []string { + ret := []string{} + for _, scheme := range schemes { + ret = append(ret, scheme.Name()) + } + return ret +} + +func SchemeByOid(oid asn1.ObjectIdentifier) Scheme { + // XXX add a (compile time?) lookup table + for _, scheme := range schemes { + certScheme, ok := scheme.(CertificateScheme) + if !ok { + continue + } + if certScheme.Oid().Equal(oid) { + return scheme + } + } + return nil +} + +func SchemeByTLSIdentifier(id uint) Scheme { + // XXX add a (compile time?) lookup table + for _, scheme := range schemes { + tlsScheme, ok := scheme.(TLSScheme) + if !ok { + continue + } + if tlsScheme.TLSIdentifier() == id { + return scheme + } + } + return nil +} + +func UnmarshalPEMPublicKey(data []byte) (PublicKey, error) { + block, rest := pem.Decode(data) + if len(rest) != 0 { + return nil, errors.New("trailing") + } + + pk, err := UnmarshalPKIXPublicKey(block.Bytes) + if err != nil { + return nil, err + } + + return pk, nil +} + +func MarshalPEMPublicKey(pk PublicKey) ([]byte, error) { + data, err := MarshalPKIXPublicKey(pk) + if err != nil { + return nil, err + } + str := pem.EncodeToMemory( + &pem.Block{ + Type: fmt.Sprintf("%s PUBLIC KEY", pk.Scheme().Name()), + Bytes: data, + }, + ) + return str, nil +} + +func UnmarshalPKIXPublicKey(data []byte) (PublicKey, error) { + var pkix struct { + Raw asn1.RawContent + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString + } + if rest, err := asn1.Unmarshal(data, &pkix); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("trailing data") + } + scheme := SchemeByOid(pkix.Algorithm.Algorithm) + if scheme == nil { + return nil, errors.New("unsupported public key algorithm") + } + return scheme.UnmarshalBinaryPublicKey(pkix.PublicKey.RightAlign()) +} + +func MarshalPKIXPublicKey(pk PublicKey) ([]byte, error) { + scheme := pk.Scheme() + certScheme, ok := scheme.(CertificateScheme) + if !ok { + return nil, errors.New("only supported for CertificateScheme") + } + + data, err := pk.MarshalBinary() + if err != nil { + return nil, err + } + + pkix := struct { + pkix.AlgorithmIdentifier + asn1.BitString + }{ + pkix.AlgorithmIdentifier{ + Algorithm: certScheme.Oid(), + }, + asn1.BitString{ + Bytes: data, + BitLength: len(data) * 8, + }, + } + + return asn1.Marshal(pkix) +} + +func UnmarshalPEMPrivateKey(data []byte) (PrivateKey, error) { + block, rest := pem.Decode(data) + if len(rest) != 0 { + return nil, errors.New("trailing") + } + + sk, err := UnmarshalPKIXPrivateKey(block.Bytes) + if err != nil { + return nil, err + } + + return sk, nil +} + +func MarshalPEMPrivateKey(sk PrivateKey) ([]byte, error) { + data, err := MarshalPKIXPrivateKey(sk) + if err != nil { + return nil, err + } + str := pem.EncodeToMemory( + &pem.Block{ + Type: fmt.Sprintf("%s PRIVATE KEY", sk.Scheme().Name()), + Bytes: data, + }, + ) + return str, nil +} + +func UnmarshalPKIXPrivateKey(data []byte) (PrivateKey, error) { + var pkix struct { + Version int + Algorithm pkix.AlgorithmIdentifier + PrivateKey []byte + } + if rest, err := asn1.Unmarshal(data, &pkix); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("trailing data") + } + scheme := SchemeByOid(pkix.Algorithm.Algorithm) + if scheme == nil { + return nil, errors.New("unsupported public key algorithm") + } + var sk []byte + if rest, err := asn1.Unmarshal(pkix.PrivateKey, &sk); err != nil { + return nil, err + } else if len(rest) > 0 { + return nil, errors.New("trailing data") + } + return scheme.UnmarshalBinaryPrivateKey(sk) +} + +func MarshalPKIXPrivateKey(sk PrivateKey) ([]byte, error) { + scheme := sk.Scheme() + certScheme, ok := scheme.(CertificateScheme) + if !ok { + return nil, errors.New("only supported for CertificateScheme") + } + + data, err := sk.MarshalBinary() + if err != nil { + return nil, err + } + + data, err = asn1.Marshal(data) + if err != nil { + return nil, err + } + + pkix := struct { + Version int + Algorithm pkix.AlgorithmIdentifier + PrivateKey []byte + }{ + 0, + pkix.AlgorithmIdentifier{ + Algorithm: certScheme.Oid(), + }, + data, + } + + return asn1.Marshal(pkix) +} + +// Go1.15 adds {PublicKey,PrivateKey}.Equal(). Until then, we can use that +// we use this. + +func PublicKeysEqual(a, b PublicKey) bool { + if a.Scheme() != b.Scheme() { + return false + } + ap, err := a.MarshalBinary() + if err != nil { + return false + } + bp, err := b.MarshalBinary() + if err != nil { + return false + } + return bytes.Equal(ap, bp) +} + +func PrivateKeysEqual(a, b PrivateKey) bool { + if a.Scheme() != b.Scheme() { + return false + } + ap, err := a.MarshalBinary() + if err != nil { + return false + } + bp, err := b.MarshalBinary() + if err != nil { + return false + } + return bytes.Equal(ap, bp) +} + +// We would like the following from our signatures API: +// +// 1) Have the main types such as Scheme, PublicKey and PrivateKey +// defined in circl/sign. +// 2) Have all built-in signature schemes available without having to import +// secondary modules. +// 3) Get the Scheme from a Public/PrivateKey using the Scheme() memberfunction. +// +// Because of this we cannot use the Private/PublicKey types of the original +// package, because that original package needs to import the circl/sign +// package for the definition of Scheme (because of 3 and 1). Conversely, +// because of 2, the circl/sign package needs to import the original package. +// We fix this by simply wrapping the Public/PrivateKeys together with the +// appropriate scheme. + +func wrapPublicKey(pk schemelessPublicKey, scheme Scheme) PublicKey { + return &wrappedPublicKey{pk, scheme} +} + +func wrapPrivateKey(sk schemelessPrivateKey, scheme Scheme) PrivateKey { + return &wrappedPrivateKey{sk, scheme} +} + +// PublicKey minus Scheme() +type schemelessPublicKey interface { + encoding.BinaryMarshaler +} + +// PrivateKey minus Scheme() +type schemelessPrivateKey interface { + crypto.Signer + encoding.BinaryMarshaler +} + +type wrappedPublicKey struct { + wrappee schemelessPublicKey + scheme Scheme +} + +func (pk *wrappedPublicKey) Scheme() Scheme { + return pk.scheme +} + +func (pk *wrappedPublicKey) MarshalBinary() ([]byte, error) { + return pk.wrappee.MarshalBinary() +} + +type wrappedPrivateKey struct { + wrappee schemelessPrivateKey + scheme Scheme +} + +func (sk *wrappedPrivateKey) Scheme() Scheme { + return sk.scheme +} + +func (sk *wrappedPrivateKey) Sign(rand io.Reader, msg []byte, + opts crypto.SignerOpts) (signature []byte, err error) { + return sk.wrappee.Sign(rand, msg, opts) +} + +func (sk *wrappedPrivateKey) Public() crypto.PublicKey { + return wrapPublicKey(sk.wrappee.Public().(schemelessPublicKey), sk.scheme) +} + +func (sk *wrappedPrivateKey) MarshalBinary() ([]byte, error) { + return sk.wrappee.MarshalBinary() +} diff --git a/sign/api_test.go b/sign/api_test.go new file mode 100644 index 000000000..0c3c86615 --- /dev/null +++ b/sign/api_test.go @@ -0,0 +1,139 @@ +package sign_test + +import ( + "fmt" + "testing" + + "github.com/cloudflare/circl/sign" +) + +func TestPEM(t *testing.T) { + names := sign.ListSchemeNames() + for _, name := range names { + scheme := sign.SchemeByName(name) + if scheme == nil { + t.Fatal() + } + + _, ok := scheme.(sign.CertificateScheme) + if !ok { + continue + } + + pk, sk, err := scheme.GenerateKey() + if err != nil { + t.Fatal() + } + + packedPk, err := sign.MarshalPEMPublicKey(pk) + if err != nil { + t.Fatal() + } + + pk2, err := sign.UnmarshalPEMPublicKey(packedPk) + if err != nil { + t.Fatal() + } + if !sign.PublicKeysEqual(pk2, pk) { + t.Fatal() + } + + packedSk, err := sign.MarshalPEMPrivateKey(sk) + if err != nil { + t.Fatal() + } + + sk2, err := sign.UnmarshalPEMPrivateKey(packedSk) + if err != nil { + t.Fatal() + } + + if !sign.PrivateKeysEqual(sk2, sk) { + t.Fatal() + } + } +} + +func TestApi(t *testing.T) { + names := sign.ListSchemeNames() + for _, name := range names { + scheme := sign.SchemeByName(name) + if scheme == nil { + t.Fatal() + } + + pk, sk, err := scheme.GenerateKey() + if err != nil { + t.Fatal() + } + + packedPk, err := pk.MarshalBinary() + if err != nil { + t.Fatal() + } + + if uint(len(packedPk)) != scheme.PublicKeySize() { + t.Fatal() + } + + packedSk, err := sk.MarshalBinary() + if err != nil { + t.Fatal() + } + + if uint(len(packedSk)) != scheme.PrivateKeySize() { + t.Fatal() + } + + pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk) + if err != nil { + t.Fatal() + } + + sk2, err := scheme.UnmarshalBinaryPrivateKey(packedSk) + if err != nil { + t.Fatal() + } + + if !sign.PrivateKeysEqual(sk, sk2) { + t.Fatal() + } + + if !sign.PublicKeysEqual(pk, pk2) { + t.Fatal() + } + + msg := []byte(fmt.Sprintf("Signing with %s", name)) + sig := scheme.Sign(sk, msg, nil) + + if scheme.SignatureSize() != uint(len(sig)) { + t.Fatal() + } + + if !scheme.Verify(pk2, msg, sig, nil) { + t.Fatal() + } + + sig[0]++ + if scheme.Verify(pk2, msg, sig, nil) { + t.Fatal() + } + + if scheme.Name() != name { + t.Fatal() + } + + if pk.Scheme() != scheme { + t.Fatal() + } + + if sk.Scheme() != scheme { + t.Fatal() + } + + pk3 := sk.Public().(sign.PublicKey) + if !sign.PublicKeysEqual(pk3, pk) { + t.Fatal() + } + } +} diff --git a/sign/doc.go b/sign/doc.go deleted file mode 100644 index d3fe8ef04..000000000 --- a/sign/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package sign provides variety of digital signature schemes. -package sign diff --git a/sign/eddilithium3.go b/sign/eddilithium3.go new file mode 100644 index 000000000..9c0c6650c --- /dev/null +++ b/sign/eddilithium3.go @@ -0,0 +1,108 @@ +package sign + +import ( + "github.com/cloudflare/circl/sign/eddilithium3" + + cryptoRand "crypto/rand" + "encoding/asn1" + "errors" +) + +type edDilithium3Scheme struct{} + +var EdDilithium3 Scheme = &edDilithium3Scheme{} + +func (s *edDilithium3Scheme) GenerateKey() (PublicKey, PrivateKey, error) { + pk, sk, err := eddilithium3.GenerateKey(cryptoRand.Reader) + if err != nil { + return nil, nil, err + } + return wrapPublicKey(pk, EdDilithium3), wrapPrivateKey(sk, EdDilithium3), nil +} + +func (s *edDilithium3Scheme) Sign(sk PrivateKey, message []byte, + opts *SignatureOpts) []byte { + sig := make([]byte, eddilithium3.SignatureSize) + if opts != nil && opts.Context != "" { + panic("Does not support context") + } + eddilithium3.SignTo( + sk.(*wrappedPrivateKey).wrappee.(*eddilithium3.PrivateKey), + message, + sig, + ) + return sig +} + +func (s *edDilithium3Scheme) Verify(pk PublicKey, message, signature []byte, + opts *SignatureOpts) bool { + if opts != nil && opts.Context != "" { + panic("Does not support context") + } + return eddilithium3.Verify( + pk.(*wrappedPublicKey).wrappee.(*eddilithium3.PublicKey), + message, + signature, + ) +} + +func (s *edDilithium3Scheme) DeriveKey(seed []byte) (PublicKey, PrivateKey) { + if len(seed) != eddilithium3.SeedSize { + panic("Wrong seed size") + } + var tmp [eddilithium3.SeedSize]byte + copy(tmp[:], seed) + pk, sk := eddilithium3.NewKeyFromSeed(&tmp) + return wrapPublicKey(pk, EdDilithium3), wrapPrivateKey(sk, EdDilithium3) +} + +func (s *edDilithium3Scheme) UnmarshalBinaryPublicKey(buf []byte) ( + PublicKey, error) { + if len(buf) != eddilithium3.PublicKeySize { + return nil, errors.New("wrong size for public key") + } + var tmp [eddilithium3.PublicKeySize]byte + var ret eddilithium3.PublicKey + copy(tmp[:], buf) + ret.Unpack(&tmp) + return wrapPublicKey(&ret, EdDilithium3), nil +} + +func (s *edDilithium3Scheme) UnmarshalBinaryPrivateKey(buf []byte) (PrivateKey, error) { + if len(buf) != eddilithium3.PrivateKeySize { + return nil, errors.New("wrong size for private key") + } + var tmp [eddilithium3.PrivateKeySize]byte + var ret eddilithium3.PrivateKey + copy(tmp[:], buf) + ret.Unpack(&tmp) + return wrapPrivateKey(&ret, EdDilithium3), nil +} + +func (s *edDilithium3Scheme) PublicKeySize() uint { + return eddilithium3.PublicKeySize +} + +func (s *edDilithium3Scheme) PrivateKeySize() uint { + return eddilithium3.PrivateKeySize +} + +func (s *edDilithium3Scheme) Name() string { + return "Ed25519-Dilithium3" +} + +func (s *edDilithium3Scheme) SignatureSize() uint { + return eddilithium3.SignatureSize +} + +func (s *edDilithium3Scheme) SeedSize() uint { + return eddilithium3.SeedSize +} + +func (s *edDilithium3Scheme) Oid() asn1.ObjectIdentifier { + return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 9} +} + +func (s *edDilithium3Scheme) TLSIdentifier() uint { + return 0xfe61 // temp +} From 50efdbf55fa773f7acffe36467896d89081ff62c Mon Sep 17 00:00:00 2001 From: armfazh Date: Fri, 24 Jul 2020 22:59:23 -0700 Subject: [PATCH 02/20] API example for eddilithium3. --- sign/api.go | 413 ------------------------------- sign/api/api.go | 27 ++ sign/api/api_test.go | 88 +++++++ sign/api_test.go | 139 ----------- sign/eddilithium3.go | 108 -------- sign/eddilithium3/eddilithium.go | 6 + sign/eddilithium3/signapi.go | 83 +++++++ sign/sign.go | 102 ++++++++ 8 files changed, 306 insertions(+), 660 deletions(-) delete mode 100644 sign/api.go create mode 100644 sign/api/api.go create mode 100644 sign/api/api_test.go delete mode 100644 sign/api_test.go delete mode 100644 sign/eddilithium3.go create mode 100644 sign/eddilithium3/signapi.go create mode 100644 sign/sign.go diff --git a/sign/api.go b/sign/api.go deleted file mode 100644 index dad463bf9..000000000 --- a/sign/api.go +++ /dev/null @@ -1,413 +0,0 @@ -// package sign provides a unified interface to all signature schemes -// supported by Circl. -package sign - -import ( - "bytes" - "crypto" - "encoding" - "encoding/asn1" - "encoding/pem" - "errors" - "fmt" - "io" - "strings" - - "crypto/x509/pkix" -) - -var schemes = [...]Scheme{ - EdDilithium3, -} - -type SignatureOpts struct { - // If non-empty, includes the given context in the signature if supported - // and will cause an error during signing otherwise. - Context string -} - -// A public key is used to verify a signature set by the corresponding private -// key. -type PublicKey interface { - // Returns the signature scheme for this public key. - Scheme() Scheme - - encoding.BinaryMarshaler -} - -// A private key allows one to create signatures. -type PrivateKey interface { - // Returns the signature scheme for this private key. - Scheme() Scheme - - // For compatibility with Go standard library - crypto.Signer - - encoding.BinaryMarshaler -} - -// A Scheme represents a specific instance of a signature scheme. -type Scheme interface { - // GenerateKey creates a new key-pair. - GenerateKey() (PublicKey, PrivateKey, error) - - // Creates a signature using the PrivateKey on the given message and - // returns the signature. opts are additional options which can be nil. - Sign(sk PrivateKey, message []byte, opts *SignatureOpts) []byte - - // Checks whether the given signature is a valid signature set by - // the private key corresponding to the given public key on the - // given message. opts are additional options which can be nil. - Verify(pk PublicKey, message []byte, signature []byte, opts *SignatureOpts) bool - - // Deterministically derives a keypair from a seed. If you're unsure, - // you're better off using GenerateKey(). - // - // Panics if seed is not of length SeedSize(). - DeriveKey(seed []byte) (PublicKey, PrivateKey) - - // Unmarshals a PublicKey from the provided buffer. - UnmarshalBinaryPublicKey([]byte) (PublicKey, error) - - // Unmarshals a PublicKey from the provided buffer. - UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error) - - // Size of binary marshalled public keys - PublicKeySize() uint - - // Size of binary marshalled public keys - PrivateKeySize() uint - - // Name of the scheme - Name() string - - // Size of signatures - SignatureSize() uint - - // Size of seeds - SeedSize() uint -} - -// Additional methods when the signature scheme is supported in X509. -type CertificateScheme interface { - // Return the appropriate OIDs for this instance. It is implicitly - // assumed that the encoding is simple: e.g. uses the same OID for - // signature and public key like Ed25519. - Oid() asn1.ObjectIdentifier -} - -// Additional methods when the signature scheme is supported in TLS. -type TLSScheme interface { - TLSIdentifier() uint -} - -// SchemeByName returns the scheme with the given name and nil if it is not -// supported. Use ListSchemes() to list supported schemes. -// Names are case insensitive. -func SchemeByName(name string) Scheme { - // XXX add a (compile time?) lookup table - name = strings.ToLower(name) - for _, scheme := range schemes { - if strings.ToLower(scheme.Name()) == name { - return scheme - } - } - return nil -} - -// ListSchemeNames returns the names of all schemes supported. -func ListSchemeNames() []string { - ret := []string{} - for _, scheme := range schemes { - ret = append(ret, scheme.Name()) - } - return ret -} - -func SchemeByOid(oid asn1.ObjectIdentifier) Scheme { - // XXX add a (compile time?) lookup table - for _, scheme := range schemes { - certScheme, ok := scheme.(CertificateScheme) - if !ok { - continue - } - if certScheme.Oid().Equal(oid) { - return scheme - } - } - return nil -} - -func SchemeByTLSIdentifier(id uint) Scheme { - // XXX add a (compile time?) lookup table - for _, scheme := range schemes { - tlsScheme, ok := scheme.(TLSScheme) - if !ok { - continue - } - if tlsScheme.TLSIdentifier() == id { - return scheme - } - } - return nil -} - -func UnmarshalPEMPublicKey(data []byte) (PublicKey, error) { - block, rest := pem.Decode(data) - if len(rest) != 0 { - return nil, errors.New("trailing") - } - - pk, err := UnmarshalPKIXPublicKey(block.Bytes) - if err != nil { - return nil, err - } - - return pk, nil -} - -func MarshalPEMPublicKey(pk PublicKey) ([]byte, error) { - data, err := MarshalPKIXPublicKey(pk) - if err != nil { - return nil, err - } - str := pem.EncodeToMemory( - &pem.Block{ - Type: fmt.Sprintf("%s PUBLIC KEY", pk.Scheme().Name()), - Bytes: data, - }, - ) - return str, nil -} - -func UnmarshalPKIXPublicKey(data []byte) (PublicKey, error) { - var pkix struct { - Raw asn1.RawContent - Algorithm pkix.AlgorithmIdentifier - PublicKey asn1.BitString - } - if rest, err := asn1.Unmarshal(data, &pkix); err != nil { - return nil, err - } else if len(rest) != 0 { - return nil, errors.New("trailing data") - } - scheme := SchemeByOid(pkix.Algorithm.Algorithm) - if scheme == nil { - return nil, errors.New("unsupported public key algorithm") - } - return scheme.UnmarshalBinaryPublicKey(pkix.PublicKey.RightAlign()) -} - -func MarshalPKIXPublicKey(pk PublicKey) ([]byte, error) { - scheme := pk.Scheme() - certScheme, ok := scheme.(CertificateScheme) - if !ok { - return nil, errors.New("only supported for CertificateScheme") - } - - data, err := pk.MarshalBinary() - if err != nil { - return nil, err - } - - pkix := struct { - pkix.AlgorithmIdentifier - asn1.BitString - }{ - pkix.AlgorithmIdentifier{ - Algorithm: certScheme.Oid(), - }, - asn1.BitString{ - Bytes: data, - BitLength: len(data) * 8, - }, - } - - return asn1.Marshal(pkix) -} - -func UnmarshalPEMPrivateKey(data []byte) (PrivateKey, error) { - block, rest := pem.Decode(data) - if len(rest) != 0 { - return nil, errors.New("trailing") - } - - sk, err := UnmarshalPKIXPrivateKey(block.Bytes) - if err != nil { - return nil, err - } - - return sk, nil -} - -func MarshalPEMPrivateKey(sk PrivateKey) ([]byte, error) { - data, err := MarshalPKIXPrivateKey(sk) - if err != nil { - return nil, err - } - str := pem.EncodeToMemory( - &pem.Block{ - Type: fmt.Sprintf("%s PRIVATE KEY", sk.Scheme().Name()), - Bytes: data, - }, - ) - return str, nil -} - -func UnmarshalPKIXPrivateKey(data []byte) (PrivateKey, error) { - var pkix struct { - Version int - Algorithm pkix.AlgorithmIdentifier - PrivateKey []byte - } - if rest, err := asn1.Unmarshal(data, &pkix); err != nil { - return nil, err - } else if len(rest) != 0 { - return nil, errors.New("trailing data") - } - scheme := SchemeByOid(pkix.Algorithm.Algorithm) - if scheme == nil { - return nil, errors.New("unsupported public key algorithm") - } - var sk []byte - if rest, err := asn1.Unmarshal(pkix.PrivateKey, &sk); err != nil { - return nil, err - } else if len(rest) > 0 { - return nil, errors.New("trailing data") - } - return scheme.UnmarshalBinaryPrivateKey(sk) -} - -func MarshalPKIXPrivateKey(sk PrivateKey) ([]byte, error) { - scheme := sk.Scheme() - certScheme, ok := scheme.(CertificateScheme) - if !ok { - return nil, errors.New("only supported for CertificateScheme") - } - - data, err := sk.MarshalBinary() - if err != nil { - return nil, err - } - - data, err = asn1.Marshal(data) - if err != nil { - return nil, err - } - - pkix := struct { - Version int - Algorithm pkix.AlgorithmIdentifier - PrivateKey []byte - }{ - 0, - pkix.AlgorithmIdentifier{ - Algorithm: certScheme.Oid(), - }, - data, - } - - return asn1.Marshal(pkix) -} - -// Go1.15 adds {PublicKey,PrivateKey}.Equal(). Until then, we can use that -// we use this. - -func PublicKeysEqual(a, b PublicKey) bool { - if a.Scheme() != b.Scheme() { - return false - } - ap, err := a.MarshalBinary() - if err != nil { - return false - } - bp, err := b.MarshalBinary() - if err != nil { - return false - } - return bytes.Equal(ap, bp) -} - -func PrivateKeysEqual(a, b PrivateKey) bool { - if a.Scheme() != b.Scheme() { - return false - } - ap, err := a.MarshalBinary() - if err != nil { - return false - } - bp, err := b.MarshalBinary() - if err != nil { - return false - } - return bytes.Equal(ap, bp) -} - -// We would like the following from our signatures API: -// -// 1) Have the main types such as Scheme, PublicKey and PrivateKey -// defined in circl/sign. -// 2) Have all built-in signature schemes available without having to import -// secondary modules. -// 3) Get the Scheme from a Public/PrivateKey using the Scheme() memberfunction. -// -// Because of this we cannot use the Private/PublicKey types of the original -// package, because that original package needs to import the circl/sign -// package for the definition of Scheme (because of 3 and 1). Conversely, -// because of 2, the circl/sign package needs to import the original package. -// We fix this by simply wrapping the Public/PrivateKeys together with the -// appropriate scheme. - -func wrapPublicKey(pk schemelessPublicKey, scheme Scheme) PublicKey { - return &wrappedPublicKey{pk, scheme} -} - -func wrapPrivateKey(sk schemelessPrivateKey, scheme Scheme) PrivateKey { - return &wrappedPrivateKey{sk, scheme} -} - -// PublicKey minus Scheme() -type schemelessPublicKey interface { - encoding.BinaryMarshaler -} - -// PrivateKey minus Scheme() -type schemelessPrivateKey interface { - crypto.Signer - encoding.BinaryMarshaler -} - -type wrappedPublicKey struct { - wrappee schemelessPublicKey - scheme Scheme -} - -func (pk *wrappedPublicKey) Scheme() Scheme { - return pk.scheme -} - -func (pk *wrappedPublicKey) MarshalBinary() ([]byte, error) { - return pk.wrappee.MarshalBinary() -} - -type wrappedPrivateKey struct { - wrappee schemelessPrivateKey - scheme Scheme -} - -func (sk *wrappedPrivateKey) Scheme() Scheme { - return sk.scheme -} - -func (sk *wrappedPrivateKey) Sign(rand io.Reader, msg []byte, - opts crypto.SignerOpts) (signature []byte, err error) { - return sk.wrappee.Sign(rand, msg, opts) -} - -func (sk *wrappedPrivateKey) Public() crypto.PublicKey { - return wrapPublicKey(sk.wrappee.Public().(schemelessPublicKey), sk.scheme) -} - -func (sk *wrappedPrivateKey) MarshalBinary() ([]byte, error) { - return sk.wrappee.MarshalBinary() -} diff --git a/sign/api/api.go b/sign/api/api.go new file mode 100644 index 000000000..bed580b28 --- /dev/null +++ b/sign/api/api.go @@ -0,0 +1,27 @@ +// Package api contains a register of signature algorithms. +package api + +import ( + "github.com/cloudflare/circl/sign" + "github.com/cloudflare/circl/sign/eddilithium3" +) + +var allSchemes [sign.SchemeCount]sign.Scheme +var allSchemeNames map[string]sign.Scheme + +func init() { + allSchemeNames = make(map[string]sign.Scheme) + register(eddilithium3.Scheme) +} + +func register(s sign.Scheme) { + allSchemes[s.ID()] = s + allSchemeNames[s.Name()] = s +} + +// SchemeByName returns the scheme with the given name and nil if it is not +// supported. +func SchemeByName(name string) sign.Scheme { return allSchemeNames[name] } + +// AllSchemes returns all signature schemes supported. +func AllSchemes() []sign.Scheme { a := allSchemes; return a[:] } diff --git a/sign/api/api_test.go b/sign/api/api_test.go new file mode 100644 index 000000000..66890fa8d --- /dev/null +++ b/sign/api/api_test.go @@ -0,0 +1,88 @@ +package api_test + +import ( + "fmt" + "testing" + + "github.com/cloudflare/circl/sign/api" +) + +func TestApi(t *testing.T) { + allSchemes := api.AllSchemes() + for _, scheme := range allSchemes { + t.Logf("%v: %v\n", scheme.ID(), scheme.Name()) + if scheme == nil { + t.Fatal() + } + + pk, sk, err := scheme.GenerateKey() + if err != nil { + t.Fatal() + } + + packedPk, err := pk.MarshalBinary() + if err != nil { + t.Fatal() + } + + if uint(len(packedPk)) != scheme.PublicKeySize() { + t.Fatal() + } + + packedSk, err := sk.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if uint(len(packedSk)) != scheme.PrivateKeySize() { + t.Fatal() + } + + pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk) + if err != nil { + t.Fatal(err) + } + + sk2, err := scheme.UnmarshalBinaryPrivateKey(packedSk) + if err != nil { + t.Fatal(err) + } + + if !sk.Equal(sk2) { + t.Fatal() + } + + if !pk.Equal(pk2) { + t.Fatal() + } + + msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name())) + sig := scheme.Sign(sk, msg, nil) + + if scheme.SignatureSize() != uint(len(sig)) { + t.Fatal() + } + + if !scheme.Verify(pk2, msg, sig, nil) { + t.Fatal() + } + + sig[0]++ + if scheme.Verify(pk2, msg, sig, nil) { + t.Fatal() + } + + scheme2 := api.SchemeByName(scheme.Name()) + if scheme2 == nil || scheme2 != scheme { + t.Fatal() + } + + if pk.Scheme() != scheme { + t.Fatal() + } + + if sk.Scheme() != scheme { + t.Fatal() + } + } +} diff --git a/sign/api_test.go b/sign/api_test.go deleted file mode 100644 index 0c3c86615..000000000 --- a/sign/api_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package sign_test - -import ( - "fmt" - "testing" - - "github.com/cloudflare/circl/sign" -) - -func TestPEM(t *testing.T) { - names := sign.ListSchemeNames() - for _, name := range names { - scheme := sign.SchemeByName(name) - if scheme == nil { - t.Fatal() - } - - _, ok := scheme.(sign.CertificateScheme) - if !ok { - continue - } - - pk, sk, err := scheme.GenerateKey() - if err != nil { - t.Fatal() - } - - packedPk, err := sign.MarshalPEMPublicKey(pk) - if err != nil { - t.Fatal() - } - - pk2, err := sign.UnmarshalPEMPublicKey(packedPk) - if err != nil { - t.Fatal() - } - if !sign.PublicKeysEqual(pk2, pk) { - t.Fatal() - } - - packedSk, err := sign.MarshalPEMPrivateKey(sk) - if err != nil { - t.Fatal() - } - - sk2, err := sign.UnmarshalPEMPrivateKey(packedSk) - if err != nil { - t.Fatal() - } - - if !sign.PrivateKeysEqual(sk2, sk) { - t.Fatal() - } - } -} - -func TestApi(t *testing.T) { - names := sign.ListSchemeNames() - for _, name := range names { - scheme := sign.SchemeByName(name) - if scheme == nil { - t.Fatal() - } - - pk, sk, err := scheme.GenerateKey() - if err != nil { - t.Fatal() - } - - packedPk, err := pk.MarshalBinary() - if err != nil { - t.Fatal() - } - - if uint(len(packedPk)) != scheme.PublicKeySize() { - t.Fatal() - } - - packedSk, err := sk.MarshalBinary() - if err != nil { - t.Fatal() - } - - if uint(len(packedSk)) != scheme.PrivateKeySize() { - t.Fatal() - } - - pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk) - if err != nil { - t.Fatal() - } - - sk2, err := scheme.UnmarshalBinaryPrivateKey(packedSk) - if err != nil { - t.Fatal() - } - - if !sign.PrivateKeysEqual(sk, sk2) { - t.Fatal() - } - - if !sign.PublicKeysEqual(pk, pk2) { - t.Fatal() - } - - msg := []byte(fmt.Sprintf("Signing with %s", name)) - sig := scheme.Sign(sk, msg, nil) - - if scheme.SignatureSize() != uint(len(sig)) { - t.Fatal() - } - - if !scheme.Verify(pk2, msg, sig, nil) { - t.Fatal() - } - - sig[0]++ - if scheme.Verify(pk2, msg, sig, nil) { - t.Fatal() - } - - if scheme.Name() != name { - t.Fatal() - } - - if pk.Scheme() != scheme { - t.Fatal() - } - - if sk.Scheme() != scheme { - t.Fatal() - } - - pk3 := sk.Public().(sign.PublicKey) - if !sign.PublicKeysEqual(pk3, pk) { - t.Fatal() - } - } -} diff --git a/sign/eddilithium3.go b/sign/eddilithium3.go deleted file mode 100644 index 9c0c6650c..000000000 --- a/sign/eddilithium3.go +++ /dev/null @@ -1,108 +0,0 @@ -package sign - -import ( - "github.com/cloudflare/circl/sign/eddilithium3" - - cryptoRand "crypto/rand" - "encoding/asn1" - "errors" -) - -type edDilithium3Scheme struct{} - -var EdDilithium3 Scheme = &edDilithium3Scheme{} - -func (s *edDilithium3Scheme) GenerateKey() (PublicKey, PrivateKey, error) { - pk, sk, err := eddilithium3.GenerateKey(cryptoRand.Reader) - if err != nil { - return nil, nil, err - } - return wrapPublicKey(pk, EdDilithium3), wrapPrivateKey(sk, EdDilithium3), nil -} - -func (s *edDilithium3Scheme) Sign(sk PrivateKey, message []byte, - opts *SignatureOpts) []byte { - sig := make([]byte, eddilithium3.SignatureSize) - if opts != nil && opts.Context != "" { - panic("Does not support context") - } - eddilithium3.SignTo( - sk.(*wrappedPrivateKey).wrappee.(*eddilithium3.PrivateKey), - message, - sig, - ) - return sig -} - -func (s *edDilithium3Scheme) Verify(pk PublicKey, message, signature []byte, - opts *SignatureOpts) bool { - if opts != nil && opts.Context != "" { - panic("Does not support context") - } - return eddilithium3.Verify( - pk.(*wrappedPublicKey).wrappee.(*eddilithium3.PublicKey), - message, - signature, - ) -} - -func (s *edDilithium3Scheme) DeriveKey(seed []byte) (PublicKey, PrivateKey) { - if len(seed) != eddilithium3.SeedSize { - panic("Wrong seed size") - } - var tmp [eddilithium3.SeedSize]byte - copy(tmp[:], seed) - pk, sk := eddilithium3.NewKeyFromSeed(&tmp) - return wrapPublicKey(pk, EdDilithium3), wrapPrivateKey(sk, EdDilithium3) -} - -func (s *edDilithium3Scheme) UnmarshalBinaryPublicKey(buf []byte) ( - PublicKey, error) { - if len(buf) != eddilithium3.PublicKeySize { - return nil, errors.New("wrong size for public key") - } - var tmp [eddilithium3.PublicKeySize]byte - var ret eddilithium3.PublicKey - copy(tmp[:], buf) - ret.Unpack(&tmp) - return wrapPublicKey(&ret, EdDilithium3), nil -} - -func (s *edDilithium3Scheme) UnmarshalBinaryPrivateKey(buf []byte) (PrivateKey, error) { - if len(buf) != eddilithium3.PrivateKeySize { - return nil, errors.New("wrong size for private key") - } - var tmp [eddilithium3.PrivateKeySize]byte - var ret eddilithium3.PrivateKey - copy(tmp[:], buf) - ret.Unpack(&tmp) - return wrapPrivateKey(&ret, EdDilithium3), nil -} - -func (s *edDilithium3Scheme) PublicKeySize() uint { - return eddilithium3.PublicKeySize -} - -func (s *edDilithium3Scheme) PrivateKeySize() uint { - return eddilithium3.PrivateKeySize -} - -func (s *edDilithium3Scheme) Name() string { - return "Ed25519-Dilithium3" -} - -func (s *edDilithium3Scheme) SignatureSize() uint { - return eddilithium3.SignatureSize -} - -func (s *edDilithium3Scheme) SeedSize() uint { - return eddilithium3.SeedSize -} - -func (s *edDilithium3Scheme) Oid() asn1.ObjectIdentifier { - return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 9} -} - -func (s *edDilithium3Scheme) TLSIdentifier() uint { - return 0xfe61 // temp -} diff --git a/sign/eddilithium3/eddilithium.go b/sign/eddilithium3/eddilithium.go index 50bf3d664..628a40727 100644 --- a/sign/eddilithium3/eddilithium.go +++ b/sign/eddilithium3/eddilithium.go @@ -8,6 +8,7 @@ import ( "io" "github.com/cloudflare/circl/internal/shake" + "github.com/cloudflare/circl/sign" "github.com/cloudflare/circl/sign/dilithium/mode3" "github.com/cloudflare/circl/sign/ed25519" ) @@ -185,6 +186,11 @@ func (sk *PrivateKey) UnmarshalBinary(data []byte) error { return nil } +func (sk *PrivateKey) Scheme() sign.Scheme { return Scheme } +func (pk *PublicKey) Scheme() sign.Scheme { return Scheme } +func (sk *PrivateKey) Equal(sign.PrivateKey) bool { return true /* TODO */ } +func (pk *PublicKey) Equal(sign.PublicKey) bool { return true /* TODO */ } + // Sign signs the given message. // // opts.HashFunc() must return zero, which can be achieved by passing diff --git a/sign/eddilithium3/signapi.go b/sign/eddilithium3/signapi.go new file mode 100644 index 000000000..a53bc1799 --- /dev/null +++ b/sign/eddilithium3/signapi.go @@ -0,0 +1,83 @@ +package eddilithium3 + +import ( + "crypto/rand" + "encoding/asn1" + + "github.com/cloudflare/circl/sign" +) + +// Scheme is +const Scheme = scheme(sign.EdDilithium3) + +type scheme sign.SchemeID + +func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } +func (scheme) Name() string { return "Ed25519-Dilithium3" } +func (scheme) PublicKeySize() uint { return PublicKeySize } +func (scheme) PrivateKeySize() uint { return PrivateKeySize } +func (scheme) SignatureSize() uint { return SignatureSize } +func (scheme) SeedSize() uint { return SeedSize } +func (scheme) TLSIdentifier() uint { return 0xfe61 /* temp*/ } +func (scheme) Oid() asn1.ObjectIdentifier { + return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 9} +} + +func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { + return GenerateKey(rand.Reader) +} + +func (scheme) Sign( + sk sign.PrivateKey, + message []byte, + opts *sign.SignatureOpts) []byte { + priv, ok := sk.(*PrivateKey) + if !ok { + panic(sign.ErrType) + } + var sig [SignatureSize]byte + SignTo(priv, message, sig[:]) + return sig[:] +} + +func (scheme) Verify( + pk sign.PublicKey, + message, signature []byte, + opts *sign.SignatureOpts) bool { + pub, ok := pk.(*PublicKey) + if !ok { + panic(sign.ErrType) + } + return Verify(pub, message, signature) +} + +func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { + if len(seed) != SeedSize { + panic(sign.ErrSeedSize) + } + var tmp [SeedSize]byte + copy(tmp[:], seed) + return NewKeyFromSeed(&tmp) +} + +func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, sign.ErrPubKeySize + } + var tmp [PublicKeySize]byte + copy(tmp[:], buf) + var ret PublicKey + ret.Unpack(&tmp) + return &ret, nil +} + +func (scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, sign.ErrPrivKeySize + } + var tmp [PrivateKeySize]byte + copy(tmp[:], buf) + var ret PrivateKey + ret.Unpack(&tmp) + return &ret, nil +} diff --git a/sign/sign.go b/sign/sign.go new file mode 100644 index 000000000..5d01501e1 --- /dev/null +++ b/sign/sign.go @@ -0,0 +1,102 @@ +// Package sign provides unified interfaces for signature schemes. +package sign + +import ( + "crypto" + "encoding" + "errors" +) + +// SchemeID is an identifier of a signature scheme. +type SchemeID uint8 + +const ( + // EdDilithium3 is + EdDilithium3 SchemeID = iota + // SchemeCount is the number of supported signature algorithms. + SchemeCount +) + +type SignatureOpts struct { + crypto.Hash + // If non-empty, includes the given context in the signature if supported + // and will cause an error during signing otherwise. + Context string +} + +// A public key is used to verify a signature set by the corresponding private +// key. +type PublicKey interface { + // Returns the signature scheme for this public key. + Scheme() Scheme + Equal(PublicKey) bool + encoding.BinaryMarshaler +} + +// A private key allows one to create signatures. +type PrivateKey interface { + // Returns the signature scheme for this private key. + Scheme() Scheme + + Equal(PrivateKey) bool + // For compatibility with Go standard library + crypto.Signer + + encoding.BinaryMarshaler +} + +// A Scheme represents a specific instance of a signature scheme. +type Scheme interface { + // Name of the scheme + Name() string + + // ID of the scheme + ID() SchemeID + + // GenerateKey creates a new key-pair. + GenerateKey() (PublicKey, PrivateKey, error) + + // Creates a signature using the PrivateKey on the given message and + // returns the signature. opts are additional options which can be nil. + Sign(sk PrivateKey, message []byte, opts *SignatureOpts) []byte + + // Checks whether the given signature is a valid signature set by + // the private key corresponding to the given public key on the + // given message. opts are additional options which can be nil. + Verify(pk PublicKey, message []byte, signature []byte, opts *SignatureOpts) bool + + // Deterministically derives a keypair from a seed. If you're unsure, + // you're better off using GenerateKey(). + // + // Panics if seed is not of length SeedSize(). + DeriveKey(seed []byte) (PublicKey, PrivateKey) + + // Unmarshals a PublicKey from the provided buffer. + UnmarshalBinaryPublicKey([]byte) (PublicKey, error) + + // Unmarshals a PublicKey from the provided buffer. + UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error) + + // Size of binary marshalled public keys + PublicKeySize() uint + + // Size of binary marshalled public keys + PrivateKeySize() uint + + // Size of signatures + SignatureSize() uint + + // Size of seeds + SeedSize() uint +} + +var ( + // ErrType is + ErrType = errors.New("types mismatch") + // ErrSeedSize is + ErrSeedSize = errors.New("wrong seed size") + // ErrPubKeySize is + ErrPubKeySize = errors.New("wrong size for public key") + // ErrPrivKeySize is + ErrPrivKeySize = errors.New("wrong size for private key") +) From 309e00a6040dbe1184652599cbd2c3f1f3221fd5 Mon Sep 17 00:00:00 2001 From: armfazh Date: Sat, 25 Jul 2020 02:13:49 -0700 Subject: [PATCH 03/20] Adding the remainder signatures. --- sign/api/api.go | 6 +++ sign/api/api_test.go | 12 +++-- sign/ed25519/ed25519.go | 45 ++++++++++++----- sign/ed25519/pubkey.go | 2 +- sign/ed25519/pubkey112.go | 14 ------ sign/ed25519/signapi.go | 75 +++++++++++++++++++++++++++++ sign/ed448/ed448.go | 33 ++++++++----- sign/ed448/signapi.go | 75 +++++++++++++++++++++++++++++ sign/eddilithium3/eddilithium.go | 8 +-- sign/eddilithium4/eddilithium.go | 12 +++-- sign/eddilithium4/signapi.go | 83 ++++++++++++++++++++++++++++++++ sign/sign.go | 13 +++-- 12 files changed, 326 insertions(+), 52 deletions(-) create mode 100644 sign/ed25519/signapi.go create mode 100644 sign/ed448/signapi.go create mode 100644 sign/eddilithium4/signapi.go diff --git a/sign/api/api.go b/sign/api/api.go index bed580b28..161b1d34e 100644 --- a/sign/api/api.go +++ b/sign/api/api.go @@ -3,7 +3,10 @@ package api import ( "github.com/cloudflare/circl/sign" + "github.com/cloudflare/circl/sign/ed25519" + "github.com/cloudflare/circl/sign/ed448" "github.com/cloudflare/circl/sign/eddilithium3" + "github.com/cloudflare/circl/sign/eddilithium4" ) var allSchemes [sign.SchemeCount]sign.Scheme @@ -11,7 +14,10 @@ var allSchemeNames map[string]sign.Scheme func init() { allSchemeNames = make(map[string]sign.Scheme) + register(ed25519.Scheme) + register(ed448.Scheme) register(eddilithium3.Scheme) + register(eddilithium4.Scheme) } func register(s sign.Scheme) { diff --git a/sign/api/api_test.go b/sign/api/api_test.go index 66890fa8d..78b134f37 100644 --- a/sign/api/api_test.go +++ b/sign/api/api_test.go @@ -1,9 +1,11 @@ package api_test import ( + "crypto" "fmt" "testing" + "github.com/cloudflare/circl/sign" "github.com/cloudflare/circl/sign/api" ) @@ -57,18 +59,22 @@ func TestApi(t *testing.T) { } msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name())) - sig := scheme.Sign(sk, msg, nil) + opts := &sign.SignatureOpts{ + Hash: crypto.Hash(0), + Context: "a context", + } + sig := scheme.Sign(sk, msg, opts) if scheme.SignatureSize() != uint(len(sig)) { t.Fatal() } - if !scheme.Verify(pk2, msg, sig, nil) { + if !scheme.Verify(pk2, msg, sig, opts) { t.Fatal() } sig[0]++ - if scheme.Verify(pk2, msg, sig, nil) { + if scheme.Verify(pk2, msg, sig, opts) { t.Fatal() } diff --git a/sign/ed25519/ed25519.go b/sign/ed25519/ed25519.go index 02adf0c67..e7fe29312 100644 --- a/sign/ed25519/ed25519.go +++ b/sign/ed25519/ed25519.go @@ -45,6 +45,8 @@ import ( "fmt" "io" "strconv" + + "github.com/cloudflare/circl/sign" ) const ( @@ -95,17 +97,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 @@ -117,6 +116,28 @@ func (priv PrivateKey) Seed() []byte { return seed } +func (priv PrivateKey) Scheme() sign.Scheme { return Scheme } + +func (pub PublicKey) Scheme() sign.Scheme { return Scheme } + +func (priv PrivateKey) MarshalBinary() (data []byte, err error) { + privateKey := make(PrivateKey, PrivateKeySize) + copy(privateKey, priv) + return privateKey, nil +} + +func (pub PublicKey) MarshalBinary() (data []byte, err error) { + publicKey := make(PublicKey, PublicKeySize) + copy(publicKey, pub) + return publicKey, nil +} + +// Equal reports whether pub and x have the same value. +func (pub PublicKey) Equal(x crypto.PublicKey) bool { + xx, ok := x.(PublicKey) + return ok && bytes.Equal(pub, xx) +} + // Sign creates a signature of a message with priv key. // This function is compatible with crypto.ed25519 and also supports the // three signature variants defined in RFC-8032, namely Ed25519 (or pure @@ -164,7 +185,7 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) { } privateKey := NewKeyFromSeed(seed) - publicKey := make([]byte, PublicKeySize) + publicKey := make(PublicKey, PublicKeySize) copy(publicKey, privateKey[SeedSize:]) return publicKey, privateKey, nil @@ -175,7 +196,7 @@ 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) + privateKey := make(PrivateKey, PrivateKeySize) newKeyFromSeed(privateKey, seed) return privateKey } @@ -193,7 +214,7 @@ func newKeyFromSeed(privateKey, seed []byte) { _ = P.ToBytes(privateKey[SeedSize:]) } -func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { +func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { if l := len(privateKey); l != PrivateKeySize { panic("ed25519: bad private key length: " + strconv.Itoa(l)) } @@ -260,7 +281,7 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash // It will panic if len(privateKey) is not PrivateKeySize. func Sign(privateKey PrivateKey, message []byte) []byte { signature := make([]byte, SignatureSize) - sign(signature, privateKey, message, []byte(""), false) + signAll(signature, privateKey, message, []byte(""), false) return signature } @@ -277,7 +298,7 @@ func SignPh(privateKey PrivateKey, message []byte, ctx string) []byte { } signature := make([]byte, SignatureSize) - sign(signature, privateKey, message, []byte(ctx), true) + signAll(signature, privateKey, message, []byte(ctx), true) return signature } @@ -293,7 +314,7 @@ func SignWithCtx(privateKey PrivateKey, message []byte, ctx string) []byte { } signature := make([]byte, SignatureSize) - sign(signature, privateKey, message, []byte(ctx), false) + signAll(signature, privateKey, message, []byte(ctx), false) return signature } diff --git a/sign/ed25519/pubkey.go b/sign/ed25519/pubkey.go index 2e5084139..ff4180598 100644 --- a/sign/ed25519/pubkey.go +++ b/sign/ed25519/pubkey.go @@ -5,4 +5,4 @@ package ed25519 import cryptoEd25519 "crypto/ed25519" // PublicKey is the type of Ed25519 public keys. -type PublicKey = cryptoEd25519.PublicKey +type PublicKey cryptoEd25519.PublicKey diff --git a/sign/ed25519/pubkey112.go b/sign/ed25519/pubkey112.go index abcc1e6d6..46f66113d 100644 --- a/sign/ed25519/pubkey112.go +++ b/sign/ed25519/pubkey112.go @@ -2,19 +2,5 @@ package ed25519 -import ( - "bytes" - "crypto" -) - // PublicKey is the type of Ed25519 public keys. 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) -} diff --git a/sign/ed25519/signapi.go b/sign/ed25519/signapi.go new file mode 100644 index 000000000..3e77974ef --- /dev/null +++ b/sign/ed25519/signapi.go @@ -0,0 +1,75 @@ +package ed25519 + +import ( + "crypto/rand" + "encoding/asn1" + + "github.com/cloudflare/circl/sign" +) + +// Scheme is +const Scheme = scheme(sign.Ed25519) + +type scheme sign.SchemeID + +func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } +func (scheme) Name() string { return "Ed25519" } +func (scheme) PublicKeySize() uint { return PublicKeySize } +func (scheme) PrivateKeySize() uint { return PrivateKeySize } +func (scheme) SignatureSize() uint { return SignatureSize } +func (scheme) SeedSize() uint { return SeedSize } +func (scheme) TLSIdentifier() uint { return 0xfe61 /* TODO */ } +func (scheme) Oid() asn1.ObjectIdentifier { + return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 15, 10 /* TODO */} +} + +func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { + return GenerateKey(rand.Reader) +} + +func (scheme) Sign( + sk sign.PrivateKey, + message []byte, + opts *sign.SignatureOpts) []byte { + priv, ok := sk.(PrivateKey) + if !ok { + panic(sign.ErrType) + } + return Sign(priv, message) +} + +func (scheme) Verify( + pk sign.PublicKey, + message, signature []byte, + opts *sign.SignatureOpts) bool { + pub, ok := pk.(PublicKey) + if !ok { + panic(sign.ErrType) + } + return Verify(pub, message, signature) +} + +func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { + privateKey := NewKeyFromSeed(seed) + publicKey := make(PublicKey, PublicKeySize) + copy(publicKey, privateKey[SeedSize:]) + return publicKey, privateKey +} + +func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { + if len(buf) < PublicKeySize { + return nil, sign.ErrPubKeySize + } + pub := make(PublicKey, PublicKeySize) + copy(pub, buf[:PublicKeySize]) + return pub, nil +} + +func (scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { + if len(buf) < PrivateKeySize { + return nil, sign.ErrPrivKeySize + } + priv := make(PrivateKey, PrivateKeySize) + copy(priv, buf[:PrivateKeySize]) + return priv, nil +} diff --git a/sign/ed448/ed448.go b/sign/ed448/ed448.go index b8d044329..6e87c97b3 100644 --- a/sign/ed448/ed448.go +++ b/sign/ed448/ed448.go @@ -35,6 +35,7 @@ import ( "github.com/cloudflare/circl/ecc/goldilocks" sha3 "github.com/cloudflare/circl/internal/shake" + "github.com/cloudflare/circl/sign" ) const ( @@ -83,10 +84,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,10 +93,7 @@ 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. @@ -117,6 +112,22 @@ func (priv PrivateKey) Seed() []byte { return seed } +func (priv PrivateKey) Scheme() sign.Scheme { return Scheme } + +func (pub PublicKey) Scheme() sign.Scheme { return Scheme } + +func (priv PrivateKey) MarshalBinary() (data []byte, err error) { + privateKey := make(PrivateKey, PrivateKeySize) + copy(privateKey, priv) + return privateKey, nil +} + +func (pub PublicKey) MarshalBinary() (data []byte, err error) { + publicKey := make(PublicKey, PublicKeySize) + copy(publicKey, pub) + return publicKey, nil +} + // Sign creates a signature of a message given a key pair. // This function supports all the two signature variants defined in RFC-8032, // namely Ed448 (or pure EdDSA) and Ed448Ph. @@ -192,7 +203,7 @@ func newKeyFromSeed(privateKey, seed []byte) { _ = goldilocks.Curve{}.ScalarBaseMult(s).ToBytes(privateKey[SeedSize:]) } -func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { +func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) { if len(ctx) > ContextMaxSize { panic(fmt.Errorf("ed448: bad context length: " + strconv.Itoa(len(ctx)))) } @@ -264,7 +275,7 @@ func sign(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash // It will panic if len(privateKey) is not PrivateKeySize. func Sign(priv PrivateKey, message []byte, ctx string) []byte { signature := make([]byte, SignatureSize) - sign(signature, priv, message, []byte(ctx), false) + signAll(signature, priv, message, []byte(ctx), false) return signature } @@ -275,7 +286,7 @@ func Sign(priv PrivateKey, message []byte, ctx string) []byte { // 255. It can be empty. func SignPh(priv PrivateKey, message []byte, ctx string) []byte { signature := make([]byte, SignatureSize) - sign(signature, priv, message, []byte(ctx), true) + signAll(signature, priv, message, []byte(ctx), true) return signature } diff --git a/sign/ed448/signapi.go b/sign/ed448/signapi.go new file mode 100644 index 000000000..ef38530f0 --- /dev/null +++ b/sign/ed448/signapi.go @@ -0,0 +1,75 @@ +package ed448 + +import ( + "crypto/rand" + "encoding/asn1" + + "github.com/cloudflare/circl/sign" +) + +// Scheme is +const Scheme = scheme(sign.Ed448) + +type scheme sign.SchemeID + +func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } +func (scheme) Name() string { return "Ed448" } +func (scheme) PublicKeySize() uint { return PublicKeySize } +func (scheme) PrivateKeySize() uint { return PrivateKeySize } +func (scheme) SignatureSize() uint { return SignatureSize } +func (scheme) SeedSize() uint { return SeedSize } +func (scheme) TLSIdentifier() uint { return 0xfe61 /* TODO */ } +func (scheme) Oid() asn1.ObjectIdentifier { + return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 17, 10 /* TODO */} +} + +func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { + return GenerateKey(rand.Reader) +} + +func (scheme) Sign( + sk sign.PrivateKey, + message []byte, + opts *sign.SignatureOpts) []byte { + priv, ok := sk.(PrivateKey) + if !ok { + panic(sign.ErrType) + } + return Sign(priv, message, opts.Context) +} + +func (scheme) Verify( + pk sign.PublicKey, + message, signature []byte, + opts *sign.SignatureOpts) bool { + pub, ok := pk.(PublicKey) + if !ok { + panic(sign.ErrType) + } + return Verify(pub, message, signature, opts.Context) +} + +func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { + privateKey := NewKeyFromSeed(seed) + publicKey := make(PublicKey, PublicKeySize) + copy(publicKey, privateKey[SeedSize:]) + return publicKey, privateKey +} + +func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { + if len(buf) < PublicKeySize { + return nil, sign.ErrPubKeySize + } + pub := make(PublicKey, PublicKeySize) + copy(pub, buf[:PublicKeySize]) + return pub, nil +} + +func (scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { + if len(buf) < PrivateKeySize { + return nil, sign.ErrPrivKeySize + } + priv := make(PrivateKey, PrivateKeySize) + copy(priv, buf[:PrivateKeySize]) + return priv, nil +} diff --git a/sign/eddilithium3/eddilithium.go b/sign/eddilithium3/eddilithium.go index 628a40727..660b13510 100644 --- a/sign/eddilithium3/eddilithium.go +++ b/sign/eddilithium3/eddilithium.go @@ -186,10 +186,10 @@ func (sk *PrivateKey) UnmarshalBinary(data []byte) error { return nil } -func (sk *PrivateKey) Scheme() sign.Scheme { return Scheme } -func (pk *PublicKey) Scheme() sign.Scheme { return Scheme } -func (sk *PrivateKey) Equal(sign.PrivateKey) bool { return true /* TODO */ } -func (pk *PublicKey) Equal(sign.PublicKey) bool { return true /* TODO */ } +func (sk *PrivateKey) Scheme() sign.Scheme { return Scheme } +func (pk *PublicKey) Scheme() sign.Scheme { return Scheme } +func (sk *PrivateKey) Equal(crypto.PrivateKey) bool { return true /* TODO */ } +func (pk *PublicKey) Equal(crypto.PublicKey) bool { return true /* TODO */ } // Sign signs the given message. // diff --git a/sign/eddilithium4/eddilithium.go b/sign/eddilithium4/eddilithium.go index b5b391488..2e62623af 100644 --- a/sign/eddilithium4/eddilithium.go +++ b/sign/eddilithium4/eddilithium.go @@ -8,6 +8,7 @@ import ( "io" "github.com/cloudflare/circl/internal/shake" + "github.com/cloudflare/circl/sign" "github.com/cloudflare/circl/sign/dilithium/mode4" "github.com/cloudflare/circl/sign/ed448" ) @@ -161,7 +162,7 @@ func (sk *PrivateKey) MarshalBinary() ([]byte, error) { // UnmarshalBinary the public key from data. func (pk *PublicKey) UnmarshalBinary(data []byte) error { if len(data) != PublicKeySize { - return errors.New("packed public key must be of eddilithium3.PublicKeySize bytes") + return errors.New("packed public key must be of eddilithium4.PublicKeySize bytes") } var buf [PublicKeySize]byte copy(buf[:], data) @@ -172,7 +173,7 @@ func (pk *PublicKey) UnmarshalBinary(data []byte) error { // UnmarshalBinary unpacks the private key from data. func (sk *PrivateKey) UnmarshalBinary(data []byte) error { if len(data) != PrivateKeySize { - return errors.New("packed private key must be of eddilithium3.PrivateKeySize bytes") + return errors.New("packed private key must be of eddilithium4.PrivateKeySize bytes") } var buf [PrivateKeySize]byte copy(buf[:], data) @@ -180,6 +181,11 @@ func (sk *PrivateKey) UnmarshalBinary(data []byte) error { return nil } +func (sk *PrivateKey) Scheme() sign.Scheme { return Scheme } +func (pk *PublicKey) Scheme() sign.Scheme { return Scheme } +func (sk *PrivateKey) Equal(crypto.PrivateKey) bool { return true /* TODO */ } +func (pk *PublicKey) Equal(crypto.PublicKey) bool { return true /* TODO */ } + // Sign signs the given message. // // opts.HashFunc() must return zero, which can be achieved by passing @@ -194,7 +200,7 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( var sig [SignatureSize]byte if opts.HashFunc() != crypto.Hash(0) { - return nil, errors.New("eddilithium3: cannot sign hashed message") + return nil, errors.New("eddilithium4: cannot sign hashed message") } SignTo(sk, msg, sig[:]) diff --git a/sign/eddilithium4/signapi.go b/sign/eddilithium4/signapi.go new file mode 100644 index 000000000..d731019c6 --- /dev/null +++ b/sign/eddilithium4/signapi.go @@ -0,0 +1,83 @@ +package eddilithium4 + +import ( + "crypto/rand" + "encoding/asn1" + + "github.com/cloudflare/circl/sign" +) + +// Scheme is +const Scheme = scheme(sign.EdDilithium4) + +type scheme sign.SchemeID + +func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } +func (scheme) Name() string { return "Ed448-Dilithium4" } +func (scheme) PublicKeySize() uint { return PublicKeySize } +func (scheme) PrivateKeySize() uint { return PrivateKeySize } +func (scheme) SignatureSize() uint { return SignatureSize } +func (scheme) SeedSize() uint { return SeedSize } +func (scheme) TLSIdentifier() uint { return 0xfe61 /* TODO */ } +func (scheme) Oid() asn1.ObjectIdentifier { + return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 10 /* TODO */} +} + +func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { + return GenerateKey(rand.Reader) +} + +func (scheme) Sign( + sk sign.PrivateKey, + message []byte, + opts *sign.SignatureOpts) []byte { + priv, ok := sk.(*PrivateKey) + if !ok { + panic(sign.ErrType) + } + var sig [SignatureSize]byte + SignTo(priv, message, sig[:]) + return sig[:] +} + +func (scheme) Verify( + pk sign.PublicKey, + message, signature []byte, + opts *sign.SignatureOpts) bool { + pub, ok := pk.(*PublicKey) + if !ok { + panic(sign.ErrType) + } + return Verify(pub, message, signature) +} + +func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { + if len(seed) != SeedSize { + panic(sign.ErrSeedSize) + } + var tmp [SeedSize]byte + copy(tmp[:], seed) + return NewKeyFromSeed(&tmp) +} + +func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { + if len(buf) != PublicKeySize { + return nil, sign.ErrPubKeySize + } + var tmp [PublicKeySize]byte + copy(tmp[:], buf) + var ret PublicKey + ret.Unpack(&tmp) + return &ret, nil +} + +func (scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { + if len(buf) != PrivateKeySize { + return nil, sign.ErrPrivKeySize + } + var tmp [PrivateKeySize]byte + copy(tmp[:], buf) + var ret PrivateKey + ret.Unpack(&tmp) + return &ret, nil +} diff --git a/sign/sign.go b/sign/sign.go index 5d01501e1..26ec3a501 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -11,8 +11,12 @@ import ( type SchemeID uint8 const ( + Ed25519 SchemeID = iota + Ed448 // EdDilithium3 is - EdDilithium3 SchemeID = iota + EdDilithium3 + // EdDilithium4 is + EdDilithium4 // SchemeCount is the number of supported signature algorithms. SchemeCount ) @@ -29,8 +33,9 @@ type SignatureOpts struct { type PublicKey interface { // Returns the signature scheme for this public key. Scheme() Scheme - Equal(PublicKey) bool + Equal(crypto.PublicKey) bool encoding.BinaryMarshaler + crypto.PublicKey } // A private key allows one to create signatures. @@ -38,10 +43,10 @@ type PrivateKey interface { // Returns the signature scheme for this private key. Scheme() Scheme - Equal(PrivateKey) bool + Equal(crypto.PrivateKey) bool // For compatibility with Go standard library crypto.Signer - + crypto.PrivateKey encoding.BinaryMarshaler } From 354d6d53109947a030c325c4217d035646496fb3 Mon Sep 17 00:00:00 2001 From: armfazh Date: Sat, 25 Jul 2020 02:14:20 -0700 Subject: [PATCH 04/20] Adding the pki package. --- pki/pki.go | 179 ++++++++++++++++++++++++++++++++++++++++++++++++ pki/pki_test.go | 54 +++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 pki/pki.go create mode 100644 pki/pki_test.go diff --git a/pki/pki.go b/pki/pki.go new file mode 100644 index 000000000..54268b110 --- /dev/null +++ b/pki/pki.go @@ -0,0 +1,179 @@ +package pki + +import ( + "crypto/x509/pkix" + "encoding/asn1" + "encoding/pem" + "errors" + "fmt" + + "github.com/cloudflare/circl/sign" + "github.com/cloudflare/circl/sign/api" +) + +var allSchemesByOID map[string]sign.Scheme +var allSchemesByTLS map[uint]sign.Scheme + +func init() { + allSchemesByOID = make(map[string]sign.Scheme) + for _, scheme := range api.AllSchemes() { + if cert, ok := scheme.(CertificateScheme); ok { + allSchemesByOID[cert.Oid().String()] = scheme + } + } + + allSchemesByTLS = make(map[uint]sign.Scheme) + for _, scheme := range api.AllSchemes() { + if tlsScheme, ok := scheme.(TLSScheme); ok { + allSchemesByTLS[tlsScheme.TLSIdentifier()] = scheme + } + } +} + +func SchemeByOid(oid asn1.ObjectIdentifier) sign.Scheme { return allSchemesByOID[oid.String()] } + +func SchemeByTLSID(id uint) sign.Scheme { return allSchemesByTLS[id] } + +// Additional methods when the signature scheme is supported in X509. +type CertificateScheme interface { + // Return the appropriate OIDs for this instance. It is implicitly + // assumed that the encoding is simple: e.g. uses the same OID for + // signature and public key like Ed25519. + Oid() asn1.ObjectIdentifier +} + +// Additional methods when the signature scheme is supported in TLS. +type TLSScheme interface { + TLSIdentifier() uint +} + +func UnmarshalPEMPublicKey(data []byte) (sign.PublicKey, error) { + block, rest := pem.Decode(data) + if len(rest) != 0 { + return nil, errors.New("trailing") + } + + return UnmarshalPKIXPublicKey(block.Bytes) +} + +func UnmarshalPKIXPublicKey(data []byte) (sign.PublicKey, error) { + var pkix struct { + Raw asn1.RawContent + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString + } + if rest, err := asn1.Unmarshal(data, &pkix); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("trailing data") + } + scheme := SchemeByOid(pkix.Algorithm.Algorithm) + if scheme == nil { + return nil, errors.New("unsupported public key algorithm") + } + return scheme.UnmarshalBinaryPublicKey(pkix.PublicKey.RightAlign()) +} + +func UnmarshalPEMPrivateKey(data []byte) (sign.PrivateKey, error) { + block, rest := pem.Decode(data) + if len(rest) != 0 { + return nil, errors.New("trailing") + } + + return UnmarshalPKIXPrivateKey(block.Bytes) +} + +func UnmarshalPKIXPrivateKey(data []byte) (sign.PrivateKey, error) { + var pkix struct { + Version int + Algorithm pkix.AlgorithmIdentifier + PrivateKey []byte + } + if rest, err := asn1.Unmarshal(data, &pkix); err != nil { + return nil, err + } else if len(rest) != 0 { + return nil, errors.New("trailing data") + } + scheme := SchemeByOid(pkix.Algorithm.Algorithm) + if scheme == nil { + return nil, errors.New("unsupported public key algorithm") + } + var sk []byte + if rest, err := asn1.Unmarshal(pkix.PrivateKey, &sk); err != nil { + return nil, err + } else if len(rest) > 0 { + return nil, errors.New("trailing data") + } + return scheme.UnmarshalBinaryPrivateKey(sk) +} + +func MarshalPEMPublicKey(pk sign.PublicKey) ([]byte, error) { + data, err := MarshalPKIXPublicKey(pk) + if err != nil { + return nil, err + } + str := pem.EncodeToMemory(&pem.Block{ + Type: fmt.Sprintf("%s PUBLIC KEY", pk.Scheme().Name()), + Bytes: data, + }) + return str, nil +} + +func MarshalPKIXPublicKey(pk sign.PublicKey) ([]byte, error) { + data, err := pk.MarshalBinary() + if err != nil { + return nil, err + } + + scheme := pk.Scheme() + return asn1.Marshal(struct { + pkix.AlgorithmIdentifier + asn1.BitString + }{ + pkix.AlgorithmIdentifier{ + Algorithm: scheme.(CertificateScheme).Oid(), + }, + asn1.BitString{ + Bytes: data, + BitLength: len(data) * 8, + }, + }) +} + +func MarshalPEMPrivateKey(sk sign.PrivateKey) ([]byte, error) { + data, err := MarshalPKIXPrivateKey(sk) + if err != nil { + return nil, err + } + str := pem.EncodeToMemory(&pem.Block{ + Type: fmt.Sprintf("%s PRIVATE KEY", sk.Scheme().Name()), + Bytes: data, + }, + ) + return str, nil +} + +func MarshalPKIXPrivateKey(sk sign.PrivateKey) ([]byte, error) { + data, err := sk.MarshalBinary() + if err != nil { + return nil, err + } + + data, err = asn1.Marshal(data) + if err != nil { + return nil, err + } + + scheme := sk.Scheme() + return asn1.Marshal(struct { + Version int + Algorithm pkix.AlgorithmIdentifier + PrivateKey []byte + }{ + 0, + pkix.AlgorithmIdentifier{ + Algorithm: scheme.(CertificateScheme).Oid(), + }, + data, + }) +} diff --git a/pki/pki_test.go b/pki/pki_test.go new file mode 100644 index 000000000..9b3644663 --- /dev/null +++ b/pki/pki_test.go @@ -0,0 +1,54 @@ +package pki_test + +import ( + "testing" + + "github.com/cloudflare/circl/pki" + "github.com/cloudflare/circl/sign/api" +) + +func TestPEM(t *testing.T) { + for _, scheme := range api.AllSchemes() { + t.Logf("%v: %v\n", scheme.ID(), scheme.Name()) + if scheme == nil { + t.Fatal() + } + + _, ok := scheme.(pki.CertificateScheme) + if !ok { + continue + } + + pk, sk, err := scheme.GenerateKey() + if err != nil { + t.Fatal(err) + } + + packedPk, err := pki.MarshalPEMPublicKey(pk) + if err != nil { + t.Fatal(err) + } + + pk2, err := pki.UnmarshalPEMPublicKey(packedPk) + if err != nil { + t.Fatal(err) + } + if !pk.Equal(pk2) { + t.Fatal() + } + + packedSk, err := pki.MarshalPEMPrivateKey(sk) + if err != nil { + t.Fatal(err) + } + + sk2, err := pki.UnmarshalPEMPrivateKey(packedSk) + if err != nil { + t.Fatal(err) + } + + if !sk.Equal(sk2) { + t.Fatal() + } + } +} From 088a9442994e09a57b4c0bfd6f813f6f423e19ac Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 29 Jul 2020 13:06:47 +0200 Subject: [PATCH 05/20] Several changes - For sizes, changes uint to int - Implement Equal() for eddilithium{3,4} - Set OID() and TLSIdentifier() --- sign/api/api_test.go | 6 ++--- sign/dilithium/mode1/dilithium.go | 18 +++++++++++++ sign/dilithium/mode1aes/dilithium.go | 18 +++++++++++++ sign/dilithium/mode2/dilithium.go | 18 +++++++++++++ sign/dilithium/mode2aes/dilithium.go | 18 +++++++++++++ sign/dilithium/mode3/dilithium.go | 18 +++++++++++++ sign/dilithium/mode3/internal/dilithium.go | 27 +++++++++++++++++++ .../mode3/internal/dilithium_test.go | 26 +++++++++--------- sign/dilithium/mode3aes/dilithium.go | 18 +++++++++++++ sign/dilithium/mode4/dilithium.go | 18 +++++++++++++ sign/dilithium/mode4aes/dilithium.go | 18 +++++++++++++ sign/dilithium/templates/modePkg.templ.go | 18 +++++++++++++ sign/ed25519/ed25519_test.go | 18 +++++++++++++ sign/ed25519/signapi.go | 16 +++++------ sign/ed448/signapi.go | 16 +++++------ sign/eddilithium3/eddilithium.go | 22 ++++++++++++--- sign/eddilithium3/eddilithium_test.go | 18 ++++++------- sign/eddilithium3/signapi.go | 14 +++++----- sign/eddilithium4/eddilithium.go | 22 ++++++++++++--- sign/eddilithium4/signapi.go | 16 +++++------ sign/sign.go | 8 +++--- 21 files changed, 301 insertions(+), 70 deletions(-) diff --git a/sign/api/api_test.go b/sign/api/api_test.go index 78b134f37..6b1d9ea60 100644 --- a/sign/api/api_test.go +++ b/sign/api/api_test.go @@ -27,7 +27,7 @@ func TestApi(t *testing.T) { t.Fatal() } - if uint(len(packedPk)) != scheme.PublicKeySize() { + if len(packedPk) != scheme.PublicKeySize() { t.Fatal() } @@ -36,7 +36,7 @@ func TestApi(t *testing.T) { t.Fatal(err) } - if uint(len(packedSk)) != scheme.PrivateKeySize() { + if len(packedSk) != scheme.PrivateKeySize() { t.Fatal() } @@ -65,7 +65,7 @@ func TestApi(t *testing.T) { } sig := scheme.Sign(sk, msg, opts) - if scheme.SignatureSize() != uint(len(sig)) { + if scheme.SignatureSize() != len(sig) { t.Fatal() } diff --git a/sign/dilithium/mode1/dilithium.go b/sign/dilithium/mode1/dilithium.go index 0004c0ff6..feb170592 100644 --- a/sign/dilithium/mode1/dilithium.go +++ b/sign/dilithium/mode1/dilithium.go @@ -171,3 +171,21 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( func (sk *PrivateKey) Public() crypto.PublicKey { return (*PublicKey)((*internal.PrivateKey)(sk).Public()) } + +// Equal returns whether the two private keys equal. +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(castOther)) +} + +// Equal returns whether the two public keys equal. +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return (*internal.PublicKey)(pk).Equal((*internal.PublicKey)(castOther)) +} diff --git a/sign/dilithium/mode1aes/dilithium.go b/sign/dilithium/mode1aes/dilithium.go index a12721ef1..adce1edcd 100644 --- a/sign/dilithium/mode1aes/dilithium.go +++ b/sign/dilithium/mode1aes/dilithium.go @@ -171,3 +171,21 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( func (sk *PrivateKey) Public() crypto.PublicKey { return (*PublicKey)((*internal.PrivateKey)(sk).Public()) } + +// Equal returns whether the two private keys equal. +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(castOther)) +} + +// Equal returns whether the two public keys equal. +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return (*internal.PublicKey)(pk).Equal((*internal.PublicKey)(castOther)) +} diff --git a/sign/dilithium/mode2/dilithium.go b/sign/dilithium/mode2/dilithium.go index 88d0fe5ac..cf1765c43 100644 --- a/sign/dilithium/mode2/dilithium.go +++ b/sign/dilithium/mode2/dilithium.go @@ -171,3 +171,21 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( func (sk *PrivateKey) Public() crypto.PublicKey { return (*PublicKey)((*internal.PrivateKey)(sk).Public()) } + +// Equal returns whether the two private keys equal. +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(castOther)) +} + +// Equal returns whether the two public keys equal. +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return (*internal.PublicKey)(pk).Equal((*internal.PublicKey)(castOther)) +} diff --git a/sign/dilithium/mode2aes/dilithium.go b/sign/dilithium/mode2aes/dilithium.go index 1ed4ddf87..76ec7acac 100644 --- a/sign/dilithium/mode2aes/dilithium.go +++ b/sign/dilithium/mode2aes/dilithium.go @@ -171,3 +171,21 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( func (sk *PrivateKey) Public() crypto.PublicKey { return (*PublicKey)((*internal.PrivateKey)(sk).Public()) } + +// Equal returns whether the two private keys equal. +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(castOther)) +} + +// Equal returns whether the two public keys equal. +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return (*internal.PublicKey)(pk).Equal((*internal.PublicKey)(castOther)) +} diff --git a/sign/dilithium/mode3/dilithium.go b/sign/dilithium/mode3/dilithium.go index ed1df699f..da24a2aac 100644 --- a/sign/dilithium/mode3/dilithium.go +++ b/sign/dilithium/mode3/dilithium.go @@ -171,3 +171,21 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( func (sk *PrivateKey) Public() crypto.PublicKey { return (*PublicKey)((*internal.PrivateKey)(sk).Public()) } + +// Equal returns whether the two private keys equal. +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(castOther)) +} + +// Equal returns whether the two public keys equal. +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return (*internal.PublicKey)(pk).Equal((*internal.PublicKey)(castOther)) +} diff --git a/sign/dilithium/mode3/internal/dilithium.go b/sign/dilithium/mode3/internal/dilithium.go index 331e66932..54c137c5e 100644 --- a/sign/dilithium/mode3/internal/dilithium.go +++ b/sign/dilithium/mode3/internal/dilithium.go @@ -2,6 +2,7 @@ package internal import ( cryptoRand "crypto/rand" + "crypto/subtle" "io" "github.com/cloudflare/circl/internal/shake" @@ -415,3 +416,29 @@ func (sk *PrivateKey) Public() *PublicKey { pk.t1.PackT1(pk.t1p[:]) return pk } + +// Equal returns whether the two public keys are equal +func (pk *PublicKey) Equal(other *PublicKey) bool { + return pk.rho == other.rho && pk.t1 == other.t1 +} + +// Equal returns whether the two private keys are equal +func (sk *PrivateKey) Equal(other *PrivateKey) bool { + ret := (subtle.ConstantTimeCompare(sk.rho[:], other.rho[:]) & + subtle.ConstantTimeCompare(sk.key[:], other.key[:]) & + subtle.ConstantTimeCompare(sk.tr[:], other.tr[:])) + + acc := uint32(0) + for i := 0; i < L; i++ { + for j := 0; j < common.N; j++ { + acc |= sk.s1[i][j] ^ other.s1[i][j] + } + } + for i := 0; i < K; i++ { + for j := 0; j < common.N; j++ { + acc |= sk.s2[i][j] ^ other.s2[i][j] + acc |= sk.t0[i][j] ^ other.t0[i][j] + } + } + return (ret & subtle.ConstantTimeEq(int32(acc), 0)) == 1 +} diff --git a/sign/dilithium/mode3/internal/dilithium_test.go b/sign/dilithium/mode3/internal/dilithium_test.go index 117236d02..0ed3d3ab0 100644 --- a/sign/dilithium/mode3/internal/dilithium_test.go +++ b/sign/dilithium/mode3/internal/dilithium_test.go @@ -86,13 +86,16 @@ func TestSignThenVerifyAndPkSkPacking(t *testing.T) { var seed [common.SeedSize]byte var sig [SignatureSize]byte var msg [8]byte - var pkb1, pkb2 [PublicKeySize]byte - var skb1, skb2 [PrivateKeySize]byte + var pkb [PublicKeySize]byte + var skb [PrivateKeySize]byte var pk2 PublicKey var sk2 PrivateKey for i := uint64(0); i < 100; i++ { binary.LittleEndian.PutUint64(seed[:], i) pk, sk := NewKeyFromSeed(&seed) + if !sk.Equal(sk) { + t.Fatal() + } for j := uint64(0); j < 10; j++ { binary.LittleEndian.PutUint64(msg[:], j) SignTo(sk, msg[:], sig[:]) @@ -100,16 +103,14 @@ func TestSignThenVerifyAndPkSkPacking(t *testing.T) { t.Fatal() } } - pk.Pack(&pkb1) - pk2.Unpack(&pkb1) - pk2.Pack(&pkb2) - if pkb1 != pkb2 { + pk.Pack(&pkb) + pk2.Unpack(&pkb) + if !pk.Equal(&pk2) { t.Fatal() } - sk.Pack(&skb1) - sk2.Unpack(&skb1) - sk2.Pack(&skb2) - if skb1 != skb2 { + sk.Pack(&skb) + sk2.Unpack(&skb) + if !sk.Equal(&sk2) { t.Fatal() } } @@ -121,10 +122,7 @@ func TestPublicFromPrivate(t *testing.T) { binary.LittleEndian.PutUint64(seed[:], i) pk, sk := NewKeyFromSeed(&seed) pk2 := sk.Public() - var pkb1, pkb2 [PublicKeySize]byte - pk.Pack(&pkb1) - pk2.Pack(&pkb2) - if pkb1 != pkb2 { + if !pk.Equal(pk2) { t.Fatal() } } diff --git a/sign/dilithium/mode3aes/dilithium.go b/sign/dilithium/mode3aes/dilithium.go index e3a17f6de..07c46f673 100644 --- a/sign/dilithium/mode3aes/dilithium.go +++ b/sign/dilithium/mode3aes/dilithium.go @@ -171,3 +171,21 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( func (sk *PrivateKey) Public() crypto.PublicKey { return (*PublicKey)((*internal.PrivateKey)(sk).Public()) } + +// Equal returns whether the two private keys equal. +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(castOther)) +} + +// Equal returns whether the two public keys equal. +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return (*internal.PublicKey)(pk).Equal((*internal.PublicKey)(castOther)) +} diff --git a/sign/dilithium/mode4/dilithium.go b/sign/dilithium/mode4/dilithium.go index e18481d95..d6f51537e 100644 --- a/sign/dilithium/mode4/dilithium.go +++ b/sign/dilithium/mode4/dilithium.go @@ -171,3 +171,21 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( func (sk *PrivateKey) Public() crypto.PublicKey { return (*PublicKey)((*internal.PrivateKey)(sk).Public()) } + +// Equal returns whether the two private keys equal. +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(castOther)) +} + +// Equal returns whether the two public keys equal. +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return (*internal.PublicKey)(pk).Equal((*internal.PublicKey)(castOther)) +} diff --git a/sign/dilithium/mode4aes/dilithium.go b/sign/dilithium/mode4aes/dilithium.go index 19adeffd6..a00e74ce9 100644 --- a/sign/dilithium/mode4aes/dilithium.go +++ b/sign/dilithium/mode4aes/dilithium.go @@ -171,3 +171,21 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( func (sk *PrivateKey) Public() crypto.PublicKey { return (*PublicKey)((*internal.PrivateKey)(sk).Public()) } + +// Equal returns whether the two private keys equal. +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(castOther)) +} + +// Equal returns whether the two public keys equal. +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return (*internal.PublicKey)(pk).Equal((*internal.PublicKey)(castOther)) +} diff --git a/sign/dilithium/templates/modePkg.templ.go b/sign/dilithium/templates/modePkg.templ.go index ff7623179..19e0404db 100644 --- a/sign/dilithium/templates/modePkg.templ.go +++ b/sign/dilithium/templates/modePkg.templ.go @@ -175,3 +175,21 @@ func (sk *PrivateKey) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ( func (sk *PrivateKey) Public() crypto.PublicKey { return (*PublicKey)((*internal.PrivateKey)(sk).Public()) } + +// Equal returns whether the two private keys equal. +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return (*internal.PrivateKey)(sk).Equal((*internal.PrivateKey)(castOther)) +} + +// Equal returns whether the two public keys equal. +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return (*internal.PublicKey)(pk).Equal((*internal.PublicKey)(castOther)) +} diff --git a/sign/ed25519/ed25519_test.go b/sign/ed25519/ed25519_test.go index e7e39f5c2..0e738426a 100644 --- a/sign/ed25519/ed25519_test.go +++ b/sign/ed25519/ed25519_test.go @@ -39,6 +39,24 @@ func TestMalleability(t *testing.T) { } } +func TestPublic(t *testing.T) { + var zero zeroReader + pub, priv, err := ed25519.GenerateKey(zero) + if !priv.Equal(priv) { + t.Fatal() + } + if !pub.Equal(pub) { + t.Fatal() + } + if err != nil { + t.Fatal(err) + } + pub2 := priv.Public() + if !pub.Equal(pub2) { + t.Fatal() + } +} + func BenchmarkKeyGeneration(b *testing.B) { var zero zeroReader for i := 0; i < b.N; i++ { diff --git a/sign/ed25519/signapi.go b/sign/ed25519/signapi.go index 3e77974ef..9169de0a4 100644 --- a/sign/ed25519/signapi.go +++ b/sign/ed25519/signapi.go @@ -12,15 +12,15 @@ const Scheme = scheme(sign.Ed25519) type scheme sign.SchemeID -func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } -func (scheme) Name() string { return "Ed25519" } -func (scheme) PublicKeySize() uint { return PublicKeySize } -func (scheme) PrivateKeySize() uint { return PrivateKeySize } -func (scheme) SignatureSize() uint { return SignatureSize } -func (scheme) SeedSize() uint { return SeedSize } -func (scheme) TLSIdentifier() uint { return 0xfe61 /* TODO */ } +func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } +func (scheme) Name() string { return "Ed25519" } +func (scheme) PublicKeySize() int { return PublicKeySize } +func (scheme) PrivateKeySize() int { return PrivateKeySize } +func (scheme) SignatureSize() int { return SignatureSize } +func (scheme) SeedSize() int { return SeedSize } +func (scheme) TLSIdentifier() uint { return 0x0807 } func (scheme) Oid() asn1.ObjectIdentifier { - return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 15, 10 /* TODO */} + return asn1.ObjectIdentifier{1, 3, 101, 112} } func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { diff --git a/sign/ed448/signapi.go b/sign/ed448/signapi.go index ef38530f0..22bedbe36 100644 --- a/sign/ed448/signapi.go +++ b/sign/ed448/signapi.go @@ -12,15 +12,15 @@ const Scheme = scheme(sign.Ed448) type scheme sign.SchemeID -func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } -func (scheme) Name() string { return "Ed448" } -func (scheme) PublicKeySize() uint { return PublicKeySize } -func (scheme) PrivateKeySize() uint { return PrivateKeySize } -func (scheme) SignatureSize() uint { return SignatureSize } -func (scheme) SeedSize() uint { return SeedSize } -func (scheme) TLSIdentifier() uint { return 0xfe61 /* TODO */ } +func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } +func (scheme) Name() string { return "Ed448" } +func (scheme) PublicKeySize() int { return PublicKeySize } +func (scheme) PrivateKeySize() int { return PrivateKeySize } +func (scheme) SignatureSize() int { return SignatureSize } +func (scheme) SeedSize() int { return SeedSize } +func (scheme) TLSIdentifier() uint { return 0x0808 } func (scheme) Oid() asn1.ObjectIdentifier { - return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 17, 10 /* TODO */} + return asn1.ObjectIdentifier{1, 3, 101, 113} } func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { diff --git a/sign/eddilithium3/eddilithium.go b/sign/eddilithium3/eddilithium.go index 660b13510..8d9c6e3e3 100644 --- a/sign/eddilithium3/eddilithium.go +++ b/sign/eddilithium3/eddilithium.go @@ -186,10 +186,24 @@ func (sk *PrivateKey) UnmarshalBinary(data []byte) error { return nil } -func (sk *PrivateKey) Scheme() sign.Scheme { return Scheme } -func (pk *PublicKey) Scheme() sign.Scheme { return Scheme } -func (sk *PrivateKey) Equal(crypto.PrivateKey) bool { return true /* TODO */ } -func (pk *PublicKey) Equal(crypto.PublicKey) bool { return true /* TODO */ } +func (sk *PrivateKey) Scheme() sign.Scheme { return Scheme } +func (pk *PublicKey) Scheme() sign.Scheme { return Scheme } + +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return castOther.e.Equal(sk.e) && castOther.d.Equal(&sk.d) +} + +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return castOther.e.Equal(pk.e) && castOther.d.Equal(&pk.d) +} // Sign signs the given message. // diff --git a/sign/eddilithium3/eddilithium_test.go b/sign/eddilithium3/eddilithium_test.go index 4647b0f46..745955452 100644 --- a/sign/eddilithium3/eddilithium_test.go +++ b/sign/eddilithium3/eddilithium_test.go @@ -66,8 +66,8 @@ func TestSignThenVerifyAndPkSkPacking(t *testing.T) { var seed [eddilithium3.SeedSize]byte var sig [eddilithium3.SignatureSize]byte var msg [8]byte - var pkb1, pkb2 [eddilithium3.PublicKeySize]byte - var skb1, skb2 [eddilithium3.PrivateKeySize]byte + var pkb [eddilithium3.PublicKeySize]byte + var skb [eddilithium3.PrivateKeySize]byte var pk2 eddilithium3.PublicKey var sk2 eddilithium3.PrivateKey for i := uint64(0); i < 100; i++ { @@ -80,16 +80,14 @@ func TestSignThenVerifyAndPkSkPacking(t *testing.T) { t.Fatal() } } - pk.Pack(&pkb1) - pk2.Unpack(&pkb1) - pk2.Pack(&pkb2) - if pkb1 != pkb2 { + pk.Pack(&pkb) + pk2.Unpack(&pkb) + if !pk.Equal(&pk2) { t.Fatal() } - sk.Pack(&skb1) - sk2.Unpack(&skb1) - sk2.Pack(&skb2) - if skb1 != skb2 { + sk.Pack(&skb) + sk2.Unpack(&skb) + if !sk.Equal(&sk2) { t.Fatal() } } diff --git a/sign/eddilithium3/signapi.go b/sign/eddilithium3/signapi.go index a53bc1799..48f033394 100644 --- a/sign/eddilithium3/signapi.go +++ b/sign/eddilithium3/signapi.go @@ -12,13 +12,13 @@ const Scheme = scheme(sign.EdDilithium3) type scheme sign.SchemeID -func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } -func (scheme) Name() string { return "Ed25519-Dilithium3" } -func (scheme) PublicKeySize() uint { return PublicKeySize } -func (scheme) PrivateKeySize() uint { return PrivateKeySize } -func (scheme) SignatureSize() uint { return SignatureSize } -func (scheme) SeedSize() uint { return SeedSize } -func (scheme) TLSIdentifier() uint { return 0xfe61 /* temp*/ } +func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } +func (scheme) Name() string { return "Ed25519-Dilithium3" } +func (scheme) PublicKeySize() int { return PublicKeySize } +func (scheme) PrivateKeySize() int { return PrivateKeySize } +func (scheme) SignatureSize() int { return SignatureSize } +func (scheme) SeedSize() int { return SeedSize } +func (scheme) TLSIdentifier() uint { return 0xfe61 /* temp*/ } func (scheme) Oid() asn1.ObjectIdentifier { return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 9} } diff --git a/sign/eddilithium4/eddilithium.go b/sign/eddilithium4/eddilithium.go index 2e62623af..07df79bce 100644 --- a/sign/eddilithium4/eddilithium.go +++ b/sign/eddilithium4/eddilithium.go @@ -181,10 +181,24 @@ func (sk *PrivateKey) UnmarshalBinary(data []byte) error { return nil } -func (sk *PrivateKey) Scheme() sign.Scheme { return Scheme } -func (pk *PublicKey) Scheme() sign.Scheme { return Scheme } -func (sk *PrivateKey) Equal(crypto.PrivateKey) bool { return true /* TODO */ } -func (pk *PublicKey) Equal(crypto.PublicKey) bool { return true /* TODO */ } +func (sk *PrivateKey) Scheme() sign.Scheme { return Scheme } +func (pk *PublicKey) Scheme() sign.Scheme { return Scheme } + +func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool { + castOther, ok := other.(*PrivateKey) + if !ok { + return false + } + return castOther.e.Equal(sk.e) && castOther.d.Equal(&sk.d) +} + +func (pk *PublicKey) Equal(other crypto.PublicKey) bool { + castOther, ok := other.(*PublicKey) + if !ok { + return false + } + return castOther.e.Equal(pk.e) && castOther.d.Equal(&pk.d) +} // Sign signs the given message. // diff --git a/sign/eddilithium4/signapi.go b/sign/eddilithium4/signapi.go index d731019c6..27e381bd8 100644 --- a/sign/eddilithium4/signapi.go +++ b/sign/eddilithium4/signapi.go @@ -12,15 +12,15 @@ const Scheme = scheme(sign.EdDilithium4) type scheme sign.SchemeID -func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } -func (scheme) Name() string { return "Ed448-Dilithium4" } -func (scheme) PublicKeySize() uint { return PublicKeySize } -func (scheme) PrivateKeySize() uint { return PrivateKeySize } -func (scheme) SignatureSize() uint { return SignatureSize } -func (scheme) SeedSize() uint { return SeedSize } -func (scheme) TLSIdentifier() uint { return 0xfe61 /* TODO */ } +func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } +func (scheme) Name() string { return "Ed448-Dilithium4" } +func (scheme) PublicKeySize() int { return PublicKeySize } +func (scheme) PrivateKeySize() int { return PrivateKeySize } +func (scheme) SignatureSize() int { return SignatureSize } +func (scheme) SeedSize() int { return SeedSize } +func (scheme) TLSIdentifier() uint { return 0xfe62 /* temp */ } func (scheme) Oid() asn1.ObjectIdentifier { - return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 10 /* TODO */} + return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 10} } func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { diff --git a/sign/sign.go b/sign/sign.go index 26ec3a501..c844baae0 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -83,16 +83,16 @@ type Scheme interface { UnmarshalBinaryPrivateKey([]byte) (PrivateKey, error) // Size of binary marshalled public keys - PublicKeySize() uint + PublicKeySize() int // Size of binary marshalled public keys - PrivateKeySize() uint + PrivateKeySize() int // Size of signatures - SignatureSize() uint + SignatureSize() int // Size of seeds - SeedSize() uint + SeedSize() int } var ( From f17b719df3dc3d98341b85fb83277a69cdb11568 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 12:19:58 +0200 Subject: [PATCH 06/20] Remove unused scheme IDs --- sign/api/api.go | 20 ++++++++++---------- sign/api/api_test.go | 2 +- sign/ed25519/signapi.go | 32 +++++++++++++++----------------- sign/ed448/signapi.go | 32 +++++++++++++++----------------- sign/eddilithium3/signapi.go | 32 +++++++++++++++----------------- sign/eddilithium4/signapi.go | 32 +++++++++++++++----------------- sign/sign.go | 17 ----------------- 7 files changed, 71 insertions(+), 96 deletions(-) diff --git a/sign/api/api.go b/sign/api/api.go index 161b1d34e..44cd2e90d 100644 --- a/sign/api/api.go +++ b/sign/api/api.go @@ -9,20 +9,20 @@ import ( "github.com/cloudflare/circl/sign/eddilithium4" ) -var allSchemes [sign.SchemeCount]sign.Scheme +var allSchemes = [...]sign.Scheme{ + ed25519.Scheme, + ed448.Scheme, + eddilithium3.Scheme, + eddilithium4.Scheme, +} + var allSchemeNames map[string]sign.Scheme func init() { allSchemeNames = make(map[string]sign.Scheme) - register(ed25519.Scheme) - register(ed448.Scheme) - register(eddilithium3.Scheme) - register(eddilithium4.Scheme) -} - -func register(s sign.Scheme) { - allSchemes[s.ID()] = s - allSchemeNames[s.Name()] = s + for _, scheme := range allSchemes { + allSchemeNames[scheme.Name()] = scheme + } } // SchemeByName returns the scheme with the given name and nil if it is not diff --git a/sign/api/api_test.go b/sign/api/api_test.go index 6b1d9ea60..7fe034c7c 100644 --- a/sign/api/api_test.go +++ b/sign/api/api_test.go @@ -12,7 +12,7 @@ import ( func TestApi(t *testing.T) { allSchemes := api.AllSchemes() for _, scheme := range allSchemes { - t.Logf("%v: %v\n", scheme.ID(), scheme.Name()) + t.Logf("%v\n", scheme.Name()) if scheme == nil { t.Fatal() } diff --git a/sign/ed25519/signapi.go b/sign/ed25519/signapi.go index 9169de0a4..ea721a1f4 100644 --- a/sign/ed25519/signapi.go +++ b/sign/ed25519/signapi.go @@ -7,27 +7,25 @@ import ( "github.com/cloudflare/circl/sign" ) -// Scheme is -const Scheme = scheme(sign.Ed25519) +var Scheme sign.Scheme = &scheme{} -type scheme sign.SchemeID +type scheme struct{} -func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } -func (scheme) Name() string { return "Ed25519" } -func (scheme) PublicKeySize() int { return PublicKeySize } -func (scheme) PrivateKeySize() int { return PrivateKeySize } -func (scheme) SignatureSize() int { return SignatureSize } -func (scheme) SeedSize() int { return SeedSize } -func (scheme) TLSIdentifier() uint { return 0x0807 } -func (scheme) Oid() asn1.ObjectIdentifier { +func (*scheme) Name() string { return "Ed25519" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SignatureSize() int { return SignatureSize } +func (*scheme) SeedSize() int { return SeedSize } +func (*scheme) TLSIdentifier() uint { return 0x0807 } +func (*scheme) Oid() asn1.ObjectIdentifier { return asn1.ObjectIdentifier{1, 3, 101, 112} } -func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { +func (*scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { return GenerateKey(rand.Reader) } -func (scheme) Sign( +func (*scheme) Sign( sk sign.PrivateKey, message []byte, opts *sign.SignatureOpts) []byte { @@ -38,7 +36,7 @@ func (scheme) Sign( return Sign(priv, message) } -func (scheme) Verify( +func (*scheme) Verify( pk sign.PublicKey, message, signature []byte, opts *sign.SignatureOpts) bool { @@ -49,14 +47,14 @@ func (scheme) Verify( return Verify(pub, message, signature) } -func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { +func (*scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { privateKey := NewKeyFromSeed(seed) publicKey := make(PublicKey, PublicKeySize) copy(publicKey, privateKey[SeedSize:]) return publicKey, privateKey } -func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { if len(buf) < PublicKeySize { return nil, sign.ErrPubKeySize } @@ -65,7 +63,7 @@ func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { return pub, nil } -func (scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { if len(buf) < PrivateKeySize { return nil, sign.ErrPrivKeySize } diff --git a/sign/ed448/signapi.go b/sign/ed448/signapi.go index 22bedbe36..04f5311fc 100644 --- a/sign/ed448/signapi.go +++ b/sign/ed448/signapi.go @@ -7,27 +7,25 @@ import ( "github.com/cloudflare/circl/sign" ) -// Scheme is -const Scheme = scheme(sign.Ed448) +var Scheme sign.Scheme = &scheme{} -type scheme sign.SchemeID +type scheme struct{} -func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } -func (scheme) Name() string { return "Ed448" } -func (scheme) PublicKeySize() int { return PublicKeySize } -func (scheme) PrivateKeySize() int { return PrivateKeySize } -func (scheme) SignatureSize() int { return SignatureSize } -func (scheme) SeedSize() int { return SeedSize } -func (scheme) TLSIdentifier() uint { return 0x0808 } -func (scheme) Oid() asn1.ObjectIdentifier { +func (*scheme) Name() string { return "Ed448" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SignatureSize() int { return SignatureSize } +func (*scheme) SeedSize() int { return SeedSize } +func (*scheme) TLSIdentifier() uint { return 0x0808 } +func (*scheme) Oid() asn1.ObjectIdentifier { return asn1.ObjectIdentifier{1, 3, 101, 113} } -func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { +func (*scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { return GenerateKey(rand.Reader) } -func (scheme) Sign( +func (*scheme) Sign( sk sign.PrivateKey, message []byte, opts *sign.SignatureOpts) []byte { @@ -38,7 +36,7 @@ func (scheme) Sign( return Sign(priv, message, opts.Context) } -func (scheme) Verify( +func (*scheme) Verify( pk sign.PublicKey, message, signature []byte, opts *sign.SignatureOpts) bool { @@ -49,14 +47,14 @@ func (scheme) Verify( return Verify(pub, message, signature, opts.Context) } -func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { +func (*scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { privateKey := NewKeyFromSeed(seed) publicKey := make(PublicKey, PublicKeySize) copy(publicKey, privateKey[SeedSize:]) return publicKey, privateKey } -func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { if len(buf) < PublicKeySize { return nil, sign.ErrPubKeySize } @@ -65,7 +63,7 @@ func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { return pub, nil } -func (scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { if len(buf) < PrivateKeySize { return nil, sign.ErrPrivKeySize } diff --git a/sign/eddilithium3/signapi.go b/sign/eddilithium3/signapi.go index 48f033394..8184e3d7c 100644 --- a/sign/eddilithium3/signapi.go +++ b/sign/eddilithium3/signapi.go @@ -7,27 +7,25 @@ import ( "github.com/cloudflare/circl/sign" ) -// Scheme is -const Scheme = scheme(sign.EdDilithium3) +var Scheme sign.Scheme = &scheme{} -type scheme sign.SchemeID +type scheme struct{} -func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } -func (scheme) Name() string { return "Ed25519-Dilithium3" } -func (scheme) PublicKeySize() int { return PublicKeySize } -func (scheme) PrivateKeySize() int { return PrivateKeySize } -func (scheme) SignatureSize() int { return SignatureSize } -func (scheme) SeedSize() int { return SeedSize } -func (scheme) TLSIdentifier() uint { return 0xfe61 /* temp*/ } -func (scheme) Oid() asn1.ObjectIdentifier { +func (*scheme) Name() string { return "Ed25519-Dilithium3" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SignatureSize() int { return SignatureSize } +func (*scheme) SeedSize() int { return SeedSize } +func (*scheme) TLSIdentifier() uint { return 0xfe61 /* temp*/ } +func (*scheme) Oid() asn1.ObjectIdentifier { return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 9} } -func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { +func (*scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { return GenerateKey(rand.Reader) } -func (scheme) Sign( +func (*scheme) Sign( sk sign.PrivateKey, message []byte, opts *sign.SignatureOpts) []byte { @@ -40,7 +38,7 @@ func (scheme) Sign( return sig[:] } -func (scheme) Verify( +func (*scheme) Verify( pk sign.PublicKey, message, signature []byte, opts *sign.SignatureOpts) bool { @@ -51,7 +49,7 @@ func (scheme) Verify( return Verify(pub, message, signature) } -func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { +func (*scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { if len(seed) != SeedSize { panic(sign.ErrSeedSize) } @@ -60,7 +58,7 @@ func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { return NewKeyFromSeed(&tmp) } -func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { if len(buf) != PublicKeySize { return nil, sign.ErrPubKeySize } @@ -71,7 +69,7 @@ func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { return &ret, nil } -func (scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { if len(buf) != PrivateKeySize { return nil, sign.ErrPrivKeySize } diff --git a/sign/eddilithium4/signapi.go b/sign/eddilithium4/signapi.go index 27e381bd8..83d898177 100644 --- a/sign/eddilithium4/signapi.go +++ b/sign/eddilithium4/signapi.go @@ -7,27 +7,25 @@ import ( "github.com/cloudflare/circl/sign" ) -// Scheme is -const Scheme = scheme(sign.EdDilithium4) +var Scheme sign.Scheme = &scheme{} -type scheme sign.SchemeID +type scheme struct{} -func (scheme) ID() sign.SchemeID { return sign.SchemeID(Scheme) } -func (scheme) Name() string { return "Ed448-Dilithium4" } -func (scheme) PublicKeySize() int { return PublicKeySize } -func (scheme) PrivateKeySize() int { return PrivateKeySize } -func (scheme) SignatureSize() int { return SignatureSize } -func (scheme) SeedSize() int { return SeedSize } -func (scheme) TLSIdentifier() uint { return 0xfe62 /* temp */ } -func (scheme) Oid() asn1.ObjectIdentifier { +func (*scheme) Name() string { return "Ed448-Dilithium4" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SignatureSize() int { return SignatureSize } +func (*scheme) SeedSize() int { return SeedSize } +func (*scheme) TLSIdentifier() uint { return 0xfe62 /* temp */ } +func (*scheme) Oid() asn1.ObjectIdentifier { return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 10} } -func (scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { +func (*scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { return GenerateKey(rand.Reader) } -func (scheme) Sign( +func (*scheme) Sign( sk sign.PrivateKey, message []byte, opts *sign.SignatureOpts) []byte { @@ -40,7 +38,7 @@ func (scheme) Sign( return sig[:] } -func (scheme) Verify( +func (*scheme) Verify( pk sign.PublicKey, message, signature []byte, opts *sign.SignatureOpts) bool { @@ -51,7 +49,7 @@ func (scheme) Verify( return Verify(pub, message, signature) } -func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { +func (*scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { if len(seed) != SeedSize { panic(sign.ErrSeedSize) } @@ -60,7 +58,7 @@ func (scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { return NewKeyFromSeed(&tmp) } -func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { +func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { if len(buf) != PublicKeySize { return nil, sign.ErrPubKeySize } @@ -71,7 +69,7 @@ func (scheme) UnmarshalBinaryPublicKey(buf []byte) (sign.PublicKey, error) { return &ret, nil } -func (scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { +func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (sign.PrivateKey, error) { if len(buf) != PrivateKeySize { return nil, sign.ErrPrivKeySize } diff --git a/sign/sign.go b/sign/sign.go index c844baae0..b864eb751 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -7,20 +7,6 @@ import ( "errors" ) -// SchemeID is an identifier of a signature scheme. -type SchemeID uint8 - -const ( - Ed25519 SchemeID = iota - Ed448 - // EdDilithium3 is - EdDilithium3 - // EdDilithium4 is - EdDilithium4 - // SchemeCount is the number of supported signature algorithms. - SchemeCount -) - type SignatureOpts struct { crypto.Hash // If non-empty, includes the given context in the signature if supported @@ -55,9 +41,6 @@ type Scheme interface { // Name of the scheme Name() string - // ID of the scheme - ID() SchemeID - // GenerateKey creates a new key-pair. GenerateKey() (PublicKey, PrivateKey, error) From 0ccb4a1da4923e9e1f2898eb0f61851693b8fde4 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 12:23:36 +0200 Subject: [PATCH 07/20] Use subtests --- pki/pki_test.go | 83 ++++++++++++----------- sign/api/api_test.go | 157 ++++++++++++++++++++++--------------------- 2 files changed, 121 insertions(+), 119 deletions(-) diff --git a/pki/pki_test.go b/pki/pki_test.go index 9b3644663..90ea61916 100644 --- a/pki/pki_test.go +++ b/pki/pki_test.go @@ -9,46 +9,47 @@ import ( func TestPEM(t *testing.T) { for _, scheme := range api.AllSchemes() { - t.Logf("%v: %v\n", scheme.ID(), scheme.Name()) - if scheme == nil { - t.Fatal() - } - - _, ok := scheme.(pki.CertificateScheme) - if !ok { - continue - } - - pk, sk, err := scheme.GenerateKey() - if err != nil { - t.Fatal(err) - } - - packedPk, err := pki.MarshalPEMPublicKey(pk) - if err != nil { - t.Fatal(err) - } - - pk2, err := pki.UnmarshalPEMPublicKey(packedPk) - if err != nil { - t.Fatal(err) - } - if !pk.Equal(pk2) { - t.Fatal() - } - - packedSk, err := pki.MarshalPEMPrivateKey(sk) - if err != nil { - t.Fatal(err) - } - - sk2, err := pki.UnmarshalPEMPrivateKey(packedSk) - if err != nil { - t.Fatal(err) - } - - if !sk.Equal(sk2) { - t.Fatal() - } + t.Run(scheme.Name(), func(t *testing.T) { + if scheme == nil { + t.Fatal() + } + + _, ok := scheme.(pki.CertificateScheme) + if !ok { + return + } + + pk, sk, err := scheme.GenerateKey() + if err != nil { + t.Fatal(err) + } + + packedPk, err := pki.MarshalPEMPublicKey(pk) + if err != nil { + t.Fatal(err) + } + + pk2, err := pki.UnmarshalPEMPublicKey(packedPk) + if err != nil { + t.Fatal(err) + } + if !pk.Equal(pk2) { + t.Fatal() + } + + packedSk, err := pki.MarshalPEMPrivateKey(sk) + if err != nil { + t.Fatal(err) + } + + sk2, err := pki.UnmarshalPEMPrivateKey(packedSk) + if err != nil { + t.Fatal(err) + } + + if !sk.Equal(sk2) { + t.Fatal() + } + }) } } diff --git a/sign/api/api_test.go b/sign/api/api_test.go index 7fe034c7c..f9b029417 100644 --- a/sign/api/api_test.go +++ b/sign/api/api_test.go @@ -12,83 +12,84 @@ import ( func TestApi(t *testing.T) { allSchemes := api.AllSchemes() for _, scheme := range allSchemes { - t.Logf("%v\n", scheme.Name()) - if scheme == nil { - t.Fatal() - } - - pk, sk, err := scheme.GenerateKey() - if err != nil { - t.Fatal() - } - - packedPk, err := pk.MarshalBinary() - if err != nil { - t.Fatal() - } - - if len(packedPk) != scheme.PublicKeySize() { - t.Fatal() - } - - packedSk, err := sk.MarshalBinary() - if err != nil { - t.Fatal(err) - } - - if len(packedSk) != scheme.PrivateKeySize() { - t.Fatal() - } - - pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk) - if err != nil { - t.Fatal(err) - } - - sk2, err := scheme.UnmarshalBinaryPrivateKey(packedSk) - if err != nil { - t.Fatal(err) - } - - if !sk.Equal(sk2) { - t.Fatal() - } - - if !pk.Equal(pk2) { - t.Fatal() - } - - msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name())) - opts := &sign.SignatureOpts{ - Hash: crypto.Hash(0), - Context: "a context", - } - sig := scheme.Sign(sk, msg, opts) - - if scheme.SignatureSize() != len(sig) { - t.Fatal() - } - - if !scheme.Verify(pk2, msg, sig, opts) { - t.Fatal() - } - - sig[0]++ - if scheme.Verify(pk2, msg, sig, opts) { - t.Fatal() - } - - scheme2 := api.SchemeByName(scheme.Name()) - if scheme2 == nil || scheme2 != scheme { - t.Fatal() - } - - if pk.Scheme() != scheme { - t.Fatal() - } - - if sk.Scheme() != scheme { - t.Fatal() - } + t.Run(scheme.Name(), func(t *testing.T) { + if scheme == nil { + t.Fatal() + } + + pk, sk, err := scheme.GenerateKey() + if err != nil { + t.Fatal() + } + + packedPk, err := pk.MarshalBinary() + if err != nil { + t.Fatal() + } + + if len(packedPk) != scheme.PublicKeySize() { + t.Fatal() + } + + packedSk, err := sk.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + if len(packedSk) != scheme.PrivateKeySize() { + t.Fatal() + } + + pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk) + if err != nil { + t.Fatal(err) + } + + sk2, err := scheme.UnmarshalBinaryPrivateKey(packedSk) + if err != nil { + t.Fatal(err) + } + + if !sk.Equal(sk2) { + t.Fatal() + } + + if !pk.Equal(pk2) { + t.Fatal() + } + + msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name())) + opts := &sign.SignatureOpts{ + Hash: crypto.Hash(0), + Context: "a context", + } + sig := scheme.Sign(sk, msg, opts) + + if scheme.SignatureSize() != len(sig) { + t.Fatal() + } + + if !scheme.Verify(pk2, msg, sig, opts) { + t.Fatal() + } + + sig[0]++ + if scheme.Verify(pk2, msg, sig, opts) { + t.Fatal() + } + + scheme2 := api.SchemeByName(scheme.Name()) + if scheme2 == nil || scheme2 != scheme { + t.Fatal() + } + + if pk.Scheme() != scheme { + t.Fatal() + } + + if sk.Scheme() != scheme { + t.Fatal() + } + }) } } From 6af2c6b0595d8c727a28bb3e8ad2b31653203c6f Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 12:27:10 +0200 Subject: [PATCH 08/20] t.Fatal() -> t.FailNow() --- pki/pki_test.go | 6 +++--- sign/api/api_test.go | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pki/pki_test.go b/pki/pki_test.go index 90ea61916..9bd26cad2 100644 --- a/pki/pki_test.go +++ b/pki/pki_test.go @@ -11,7 +11,7 @@ func TestPEM(t *testing.T) { for _, scheme := range api.AllSchemes() { t.Run(scheme.Name(), func(t *testing.T) { if scheme == nil { - t.Fatal() + t.FailNow() } _, ok := scheme.(pki.CertificateScheme) @@ -34,7 +34,7 @@ func TestPEM(t *testing.T) { t.Fatal(err) } if !pk.Equal(pk2) { - t.Fatal() + t.FailNow() } packedSk, err := pki.MarshalPEMPrivateKey(sk) @@ -48,7 +48,7 @@ func TestPEM(t *testing.T) { } if !sk.Equal(sk2) { - t.Fatal() + t.FailNow() } }) } diff --git a/sign/api/api_test.go b/sign/api/api_test.go index f9b029417..bc4196f6e 100644 --- a/sign/api/api_test.go +++ b/sign/api/api_test.go @@ -14,21 +14,21 @@ func TestApi(t *testing.T) { for _, scheme := range allSchemes { t.Run(scheme.Name(), func(t *testing.T) { if scheme == nil { - t.Fatal() + t.FailNow() } pk, sk, err := scheme.GenerateKey() if err != nil { - t.Fatal() + t.FailNow() } packedPk, err := pk.MarshalBinary() if err != nil { - t.Fatal() + t.FailNow() } if len(packedPk) != scheme.PublicKeySize() { - t.Fatal() + t.FailNow() } packedSk, err := sk.MarshalBinary() @@ -37,7 +37,7 @@ func TestApi(t *testing.T) { } if len(packedSk) != scheme.PrivateKeySize() { - t.Fatal() + t.FailNow() } pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk) @@ -51,11 +51,11 @@ func TestApi(t *testing.T) { } if !sk.Equal(sk2) { - t.Fatal() + t.FailNow() } if !pk.Equal(pk2) { - t.Fatal() + t.FailNow() } msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name())) @@ -66,29 +66,29 @@ func TestApi(t *testing.T) { sig := scheme.Sign(sk, msg, opts) if scheme.SignatureSize() != len(sig) { - t.Fatal() + t.FailNow() } if !scheme.Verify(pk2, msg, sig, opts) { - t.Fatal() + t.FailNow() } sig[0]++ if scheme.Verify(pk2, msg, sig, opts) { - t.Fatal() + t.FailNow() } scheme2 := api.SchemeByName(scheme.Name()) if scheme2 == nil || scheme2 != scheme { - t.Fatal() + t.FailNow() } if pk.Scheme() != scheme { - t.Fatal() + t.FailNow() } if sk.Scheme() != scheme { - t.Fatal() + t.FailNow() } }) } From 2ba0a07c411454ac471a459ad2a87bebdf49610a Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 12:29:29 +0200 Subject: [PATCH 09/20] ed25519 test fix --- sign/ed25519/ed25519_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sign/ed25519/ed25519_test.go b/sign/ed25519/ed25519_test.go index 0e738426a..13977873a 100644 --- a/sign/ed25519/ed25519_test.go +++ b/sign/ed25519/ed25519_test.go @@ -42,18 +42,18 @@ func TestMalleability(t *testing.T) { func TestPublic(t *testing.T) { var zero zeroReader pub, priv, err := ed25519.GenerateKey(zero) + if err != nil { + t.Fatal(err) + } if !priv.Equal(priv) { - t.Fatal() + t.FailNow() } if !pub.Equal(pub) { - t.Fatal() - } - if err != nil { - t.Fatal(err) + t.FailNow() } pub2 := priv.Public() if !pub.Equal(pub2) { - t.Fatal() + t.FailNow() } } From ee300cb7414ad022bce22485133636b50c63e261 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 12:33:08 +0200 Subject: [PATCH 10/20] rename and document errors --- sign/ed25519/signapi.go | 4 ++-- sign/ed448/signapi.go | 4 ++-- sign/eddilithium3/signapi.go | 4 ++-- sign/eddilithium4/signapi.go | 4 ++-- sign/sign.go | 18 +++++++++++++----- 5 files changed, 21 insertions(+), 13 deletions(-) diff --git a/sign/ed25519/signapi.go b/sign/ed25519/signapi.go index ea721a1f4..2a787d181 100644 --- a/sign/ed25519/signapi.go +++ b/sign/ed25519/signapi.go @@ -31,7 +31,7 @@ func (*scheme) Sign( opts *sign.SignatureOpts) []byte { priv, ok := sk.(PrivateKey) if !ok { - panic(sign.ErrType) + panic(sign.ErrTypeMismatch) } return Sign(priv, message) } @@ -42,7 +42,7 @@ func (*scheme) Verify( opts *sign.SignatureOpts) bool { pub, ok := pk.(PublicKey) if !ok { - panic(sign.ErrType) + panic(sign.ErrTypeMismatch) } return Verify(pub, message, signature) } diff --git a/sign/ed448/signapi.go b/sign/ed448/signapi.go index 04f5311fc..58ef25fa3 100644 --- a/sign/ed448/signapi.go +++ b/sign/ed448/signapi.go @@ -31,7 +31,7 @@ func (*scheme) Sign( opts *sign.SignatureOpts) []byte { priv, ok := sk.(PrivateKey) if !ok { - panic(sign.ErrType) + panic(sign.ErrTypeMismatch) } return Sign(priv, message, opts.Context) } @@ -42,7 +42,7 @@ func (*scheme) Verify( opts *sign.SignatureOpts) bool { pub, ok := pk.(PublicKey) if !ok { - panic(sign.ErrType) + panic(sign.ErrTypeMismatch) } return Verify(pub, message, signature, opts.Context) } diff --git a/sign/eddilithium3/signapi.go b/sign/eddilithium3/signapi.go index 8184e3d7c..4e387fe79 100644 --- a/sign/eddilithium3/signapi.go +++ b/sign/eddilithium3/signapi.go @@ -31,7 +31,7 @@ func (*scheme) Sign( opts *sign.SignatureOpts) []byte { priv, ok := sk.(*PrivateKey) if !ok { - panic(sign.ErrType) + panic(sign.ErrTypeMismatch) } var sig [SignatureSize]byte SignTo(priv, message, sig[:]) @@ -44,7 +44,7 @@ func (*scheme) Verify( opts *sign.SignatureOpts) bool { pub, ok := pk.(*PublicKey) if !ok { - panic(sign.ErrType) + panic(sign.ErrTypeMismatch) } return Verify(pub, message, signature) } diff --git a/sign/eddilithium4/signapi.go b/sign/eddilithium4/signapi.go index 83d898177..394122acb 100644 --- a/sign/eddilithium4/signapi.go +++ b/sign/eddilithium4/signapi.go @@ -31,7 +31,7 @@ func (*scheme) Sign( opts *sign.SignatureOpts) []byte { priv, ok := sk.(*PrivateKey) if !ok { - panic(sign.ErrType) + panic(sign.ErrTypeMismatch) } var sig [SignatureSize]byte SignTo(priv, message, sig[:]) @@ -44,7 +44,7 @@ func (*scheme) Verify( opts *sign.SignatureOpts) bool { pub, ok := pk.(*PublicKey) if !ok { - panic(sign.ErrType) + panic(sign.ErrTypeMismatch) } return Verify(pub, message, signature) } diff --git a/sign/sign.go b/sign/sign.go index b864eb751..04da9cc13 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -9,6 +9,7 @@ import ( type SignatureOpts struct { crypto.Hash + // If non-empty, includes the given context in the signature if supported // and will cause an error during signing otherwise. Context string @@ -79,12 +80,19 @@ type Scheme interface { } var ( - // ErrType is - ErrType = errors.New("types mismatch") - // ErrSeedSize is + // ErrTypeMismatch is the error returned if types of, for instance, private + // and public keys don't match + ErrTypeMismatch = errors.New("types mismatch") + + // ErrSeedSize is the error returned if the provided seed is of the wrong + // size. ErrSeedSize = errors.New("wrong seed size") - // ErrPubKeySize is + + // ErrPubKeySize is the error returned if the provided public key is of + // the wrong size. ErrPubKeySize = errors.New("wrong size for public key") - // ErrPrivKeySize is + + // ErrPubKeySize is the error returned if the provided private key is of + // the wrong size. ErrPrivKeySize = errors.New("wrong size for private key") ) From df7e379aec1be3aafe2eb4a34daa4244b1ba2aad Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 12:35:42 +0200 Subject: [PATCH 11/20] nicer multiline function signatures --- sign/ed25519/signapi.go | 6 ++++-- sign/ed448/signapi.go | 6 ++++-- sign/eddilithium3/signapi.go | 6 ++++-- sign/eddilithium4/signapi.go | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/sign/ed25519/signapi.go b/sign/ed25519/signapi.go index 2a787d181..17fa23a1f 100644 --- a/sign/ed25519/signapi.go +++ b/sign/ed25519/signapi.go @@ -28,7 +28,8 @@ func (*scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { func (*scheme) Sign( sk sign.PrivateKey, message []byte, - opts *sign.SignatureOpts) []byte { + opts *sign.SignatureOpts, +) []byte { priv, ok := sk.(PrivateKey) if !ok { panic(sign.ErrTypeMismatch) @@ -39,7 +40,8 @@ func (*scheme) Sign( func (*scheme) Verify( pk sign.PublicKey, message, signature []byte, - opts *sign.SignatureOpts) bool { + opts *sign.SignatureOpts, +) bool { pub, ok := pk.(PublicKey) if !ok { panic(sign.ErrTypeMismatch) diff --git a/sign/ed448/signapi.go b/sign/ed448/signapi.go index 58ef25fa3..fd5a02fd6 100644 --- a/sign/ed448/signapi.go +++ b/sign/ed448/signapi.go @@ -28,7 +28,8 @@ func (*scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { func (*scheme) Sign( sk sign.PrivateKey, message []byte, - opts *sign.SignatureOpts) []byte { + opts *sign.SignatureOpts, +) []byte { priv, ok := sk.(PrivateKey) if !ok { panic(sign.ErrTypeMismatch) @@ -39,7 +40,8 @@ func (*scheme) Sign( func (*scheme) Verify( pk sign.PublicKey, message, signature []byte, - opts *sign.SignatureOpts) bool { + opts *sign.SignatureOpts, +) bool { pub, ok := pk.(PublicKey) if !ok { panic(sign.ErrTypeMismatch) diff --git a/sign/eddilithium3/signapi.go b/sign/eddilithium3/signapi.go index 4e387fe79..eb1e2c95c 100644 --- a/sign/eddilithium3/signapi.go +++ b/sign/eddilithium3/signapi.go @@ -28,7 +28,8 @@ func (*scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { func (*scheme) Sign( sk sign.PrivateKey, message []byte, - opts *sign.SignatureOpts) []byte { + opts *sign.SignatureOpts, +) []byte { priv, ok := sk.(*PrivateKey) if !ok { panic(sign.ErrTypeMismatch) @@ -41,7 +42,8 @@ func (*scheme) Sign( func (*scheme) Verify( pk sign.PublicKey, message, signature []byte, - opts *sign.SignatureOpts) bool { + opts *sign.SignatureOpts, +) bool { pub, ok := pk.(*PublicKey) if !ok { panic(sign.ErrTypeMismatch) diff --git a/sign/eddilithium4/signapi.go b/sign/eddilithium4/signapi.go index 394122acb..2be7368e5 100644 --- a/sign/eddilithium4/signapi.go +++ b/sign/eddilithium4/signapi.go @@ -28,7 +28,8 @@ func (*scheme) GenerateKey() (sign.PublicKey, sign.PrivateKey, error) { func (*scheme) Sign( sk sign.PrivateKey, message []byte, - opts *sign.SignatureOpts) []byte { + opts *sign.SignatureOpts, +) []byte { priv, ok := sk.(*PrivateKey) if !ok { panic(sign.ErrTypeMismatch) @@ -41,7 +42,8 @@ func (*scheme) Sign( func (*scheme) Verify( pk sign.PublicKey, message, signature []byte, - opts *sign.SignatureOpts) bool { + opts *sign.SignatureOpts, +) bool { pub, ok := pk.(*PublicKey) if !ok { panic(sign.ErrTypeMismatch) From 2a6949c72310dcf1acbb7f3ec25190c9899c95c7 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 12:53:31 +0200 Subject: [PATCH 12/20] Use context properly; remove unused hash --- sign/api/api_test.go | 16 ++++++++++++---- sign/ed25519/signapi.go | 21 +++++++++++++++------ sign/ed448/signapi.go | 25 +++++++++++++++++-------- sign/eddilithium3/signapi.go | 19 +++++++++++++------ sign/eddilithium4/signapi.go | 19 +++++++++++++------ sign/sign.go | 17 +++++++++++------ 6 files changed, 81 insertions(+), 36 deletions(-) diff --git a/sign/api/api_test.go b/sign/api/api_test.go index bc4196f6e..f041cd4fb 100644 --- a/sign/api/api_test.go +++ b/sign/api/api_test.go @@ -1,7 +1,6 @@ package api_test import ( - "crypto" "fmt" "testing" @@ -12,6 +11,7 @@ import ( func TestApi(t *testing.T) { allSchemes := api.AllSchemes() for _, scheme := range allSchemes { + scheme := scheme t.Run(scheme.Name(), func(t *testing.T) { if scheme == nil { t.FailNow() @@ -59,9 +59,9 @@ func TestApi(t *testing.T) { } msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name())) - opts := &sign.SignatureOpts{ - Hash: crypto.Hash(0), - Context: "a context", + opts := &sign.SignatureOpts{} + if scheme.SupportsContext() { + opts.Context = "A context" } sig := scheme.Sign(sk, msg, opts) @@ -73,6 +73,14 @@ func TestApi(t *testing.T) { t.FailNow() } + if scheme.SupportsContext() { + opts2 := opts + opts2.Context = "Wrong context" + if scheme.Verify(pk2, msg, sig, opts2) { + t.FailNow() + } + } + sig[0]++ if scheme.Verify(pk2, msg, sig, opts) { t.FailNow() diff --git a/sign/ed25519/signapi.go b/sign/ed25519/signapi.go index 17fa23a1f..7903fa233 100644 --- a/sign/ed25519/signapi.go +++ b/sign/ed25519/signapi.go @@ -11,12 +11,13 @@ var Scheme sign.Scheme = &scheme{} type scheme struct{} -func (*scheme) Name() string { return "Ed25519" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SignatureSize() int { return SignatureSize } -func (*scheme) SeedSize() int { return SeedSize } -func (*scheme) TLSIdentifier() uint { return 0x0807 } +func (*scheme) Name() string { return "Ed25519" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SignatureSize() int { return SignatureSize } +func (*scheme) SeedSize() int { return SeedSize } +func (*scheme) TLSIdentifier() uint { return 0x0807 } +func (*scheme) SupportsContext() bool { return false } func (*scheme) Oid() asn1.ObjectIdentifier { return asn1.ObjectIdentifier{1, 3, 101, 112} } @@ -34,6 +35,9 @@ func (*scheme) Sign( if !ok { panic(sign.ErrTypeMismatch) } + if opts != nil && opts.Context != "" { + panic(sign.ErrContextNotSupported) + } return Sign(priv, message) } @@ -46,6 +50,11 @@ func (*scheme) Verify( if !ok { panic(sign.ErrTypeMismatch) } + if opts != nil { + if opts.Context != "" { + panic(sign.ErrContextNotSupported) + } + } return Verify(pub, message, signature) } diff --git a/sign/ed448/signapi.go b/sign/ed448/signapi.go index fd5a02fd6..5389f56cf 100644 --- a/sign/ed448/signapi.go +++ b/sign/ed448/signapi.go @@ -11,12 +11,13 @@ var Scheme sign.Scheme = &scheme{} type scheme struct{} -func (*scheme) Name() string { return "Ed448" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SignatureSize() int { return SignatureSize } -func (*scheme) SeedSize() int { return SeedSize } -func (*scheme) TLSIdentifier() uint { return 0x0808 } +func (*scheme) Name() string { return "Ed448" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SignatureSize() int { return SignatureSize } +func (*scheme) SeedSize() int { return SeedSize } +func (*scheme) TLSIdentifier() uint { return 0x0808 } +func (*scheme) SupportsContext() bool { return true } func (*scheme) Oid() asn1.ObjectIdentifier { return asn1.ObjectIdentifier{1, 3, 101, 113} } @@ -34,7 +35,11 @@ func (*scheme) Sign( if !ok { panic(sign.ErrTypeMismatch) } - return Sign(priv, message, opts.Context) + ctx := "" + if opts != nil { + ctx = opts.Context + } + return Sign(priv, message, ctx) } func (*scheme) Verify( @@ -46,7 +51,11 @@ func (*scheme) Verify( if !ok { panic(sign.ErrTypeMismatch) } - return Verify(pub, message, signature, opts.Context) + ctx := "" + if opts != nil { + ctx = opts.Context + } + return Verify(pub, message, signature, ctx) } func (*scheme) DeriveKey(seed []byte) (sign.PublicKey, sign.PrivateKey) { diff --git a/sign/eddilithium3/signapi.go b/sign/eddilithium3/signapi.go index eb1e2c95c..b2bb76cf5 100644 --- a/sign/eddilithium3/signapi.go +++ b/sign/eddilithium3/signapi.go @@ -11,12 +11,13 @@ var Scheme sign.Scheme = &scheme{} type scheme struct{} -func (*scheme) Name() string { return "Ed25519-Dilithium3" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SignatureSize() int { return SignatureSize } -func (*scheme) SeedSize() int { return SeedSize } -func (*scheme) TLSIdentifier() uint { return 0xfe61 /* temp*/ } +func (*scheme) Name() string { return "Ed25519-Dilithium3" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SignatureSize() int { return SignatureSize } +func (*scheme) SeedSize() int { return SeedSize } +func (*scheme) TLSIdentifier() uint { return 0xfe61 /* temp*/ } +func (*scheme) SupportsContext() bool { return false } func (*scheme) Oid() asn1.ObjectIdentifier { return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 9} } @@ -34,6 +35,9 @@ func (*scheme) Sign( if !ok { panic(sign.ErrTypeMismatch) } + if opts != nil && opts.Context != "" { + panic(sign.ErrContextNotSupported) + } var sig [SignatureSize]byte SignTo(priv, message, sig[:]) return sig[:] @@ -48,6 +52,9 @@ func (*scheme) Verify( if !ok { panic(sign.ErrTypeMismatch) } + if opts != nil && opts.Context != "" { + panic(sign.ErrContextNotSupported) + } return Verify(pub, message, signature) } diff --git a/sign/eddilithium4/signapi.go b/sign/eddilithium4/signapi.go index 2be7368e5..9b2a7e07e 100644 --- a/sign/eddilithium4/signapi.go +++ b/sign/eddilithium4/signapi.go @@ -11,12 +11,13 @@ var Scheme sign.Scheme = &scheme{} type scheme struct{} -func (*scheme) Name() string { return "Ed448-Dilithium4" } -func (*scheme) PublicKeySize() int { return PublicKeySize } -func (*scheme) PrivateKeySize() int { return PrivateKeySize } -func (*scheme) SignatureSize() int { return SignatureSize } -func (*scheme) SeedSize() int { return SeedSize } -func (*scheme) TLSIdentifier() uint { return 0xfe62 /* temp */ } +func (*scheme) Name() string { return "Ed448-Dilithium4" } +func (*scheme) PublicKeySize() int { return PublicKeySize } +func (*scheme) PrivateKeySize() int { return PrivateKeySize } +func (*scheme) SignatureSize() int { return SignatureSize } +func (*scheme) SeedSize() int { return SeedSize } +func (*scheme) TLSIdentifier() uint { return 0xfe62 /* temp */ } +func (*scheme) SupportsContext() bool { return false } func (*scheme) Oid() asn1.ObjectIdentifier { return asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 44363, 45, 10} } @@ -34,6 +35,9 @@ func (*scheme) Sign( if !ok { panic(sign.ErrTypeMismatch) } + if opts != nil && opts.Context != "" { + panic(sign.ErrContextNotSupported) + } var sig [SignatureSize]byte SignTo(priv, message, sig[:]) return sig[:] @@ -48,6 +52,9 @@ func (*scheme) Verify( if !ok { panic(sign.ErrTypeMismatch) } + if opts != nil && opts.Context != "" { + panic(sign.ErrContextNotSupported) + } return Verify(pub, message, signature) } diff --git a/sign/sign.go b/sign/sign.go index 04da9cc13..56b6095ae 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -8,8 +8,6 @@ import ( ) type SignatureOpts struct { - crypto.Hash - // If non-empty, includes the given context in the signature if supported // and will cause an error during signing otherwise. Context string @@ -77,22 +75,29 @@ type Scheme interface { // Size of seeds SeedSize() int + + // Returns whether contexts are supported + SupportsContext() bool } var ( - // ErrTypeMismatch is the error returned if types of, for instance, private + // ErrTypeMismatch is the error used if types of, for instance, private // and public keys don't match ErrTypeMismatch = errors.New("types mismatch") - // ErrSeedSize is the error returned if the provided seed is of the wrong + // ErrSeedSize is the error used if the provided seed is of the wrong // size. ErrSeedSize = errors.New("wrong seed size") - // ErrPubKeySize is the error returned if the provided public key is of + // ErrPubKeySize is the error used if the provided public key is of // the wrong size. ErrPubKeySize = errors.New("wrong size for public key") - // ErrPubKeySize is the error returned if the provided private key is of + // ErrPrivKeySize is the error used if the provided private key is of // the wrong size. ErrPrivKeySize = errors.New("wrong size for private key") + + // ErrContextNotSupported is the error used if a context is not + // supported + ErrContextNotSupported = errors.New("context not supported") ) From 46dbc617c3265e52b8478471113231f1307c458e Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 12:56:13 +0200 Subject: [PATCH 13/20] Check for PEM block types and don't include scheme name in public key --- pki/pki.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index 54268b110..4ecc17a23 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -6,6 +6,7 @@ import ( "encoding/pem" "errors" "fmt" + "strings" "github.com/cloudflare/circl/sign" "github.com/cloudflare/circl/sign/api" @@ -50,7 +51,10 @@ type TLSScheme interface { func UnmarshalPEMPublicKey(data []byte) (sign.PublicKey, error) { block, rest := pem.Decode(data) if len(rest) != 0 { - return nil, errors.New("trailing") + return nil, errors.New("trailing data") + } + if !strings.HasSuffix(block.Type, "PUBLIC KEY") { + return nil, errors.New("pem block type is not public key") } return UnmarshalPKIXPublicKey(block.Bytes) @@ -79,6 +83,9 @@ func UnmarshalPEMPrivateKey(data []byte) (sign.PrivateKey, error) { if len(rest) != 0 { return nil, errors.New("trailing") } + if !strings.HasSuffix(block.Type, "PRIVATE KEY") { + return nil, errors.New("pem block type is not private key") + } return UnmarshalPKIXPrivateKey(block.Bytes) } @@ -113,7 +120,7 @@ func MarshalPEMPublicKey(pk sign.PublicKey) ([]byte, error) { return nil, err } str := pem.EncodeToMemory(&pem.Block{ - Type: fmt.Sprintf("%s PUBLIC KEY", pk.Scheme().Name()), + Type: "PUBLIC KEY", Bytes: data, }) return str, nil From ef376fe53761e733d41ee8fa0b510b79ce559191 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 13:04:02 +0200 Subject: [PATCH 14/20] deduplicate structure --- pki/pki.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index 4ecc17a23..30b31a9a9 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -15,6 +15,12 @@ import ( var allSchemesByOID map[string]sign.Scheme var allSchemesByTLS map[uint]sign.Scheme +type pkixPrivKey struct { + Version int + Algorithm pkix.AlgorithmIdentifier + PrivateKey []byte +} + func init() { allSchemesByOID = make(map[string]sign.Scheme) for _, scheme := range api.AllSchemes() { @@ -91,11 +97,7 @@ func UnmarshalPEMPrivateKey(data []byte) (sign.PrivateKey, error) { } func UnmarshalPKIXPrivateKey(data []byte) (sign.PrivateKey, error) { - var pkix struct { - Version int - Algorithm pkix.AlgorithmIdentifier - PrivateKey []byte - } + var pkix pkixPrivKey if rest, err := asn1.Unmarshal(data, &pkix); err != nil { return nil, err } else if len(rest) != 0 { @@ -172,11 +174,7 @@ func MarshalPKIXPrivateKey(sk sign.PrivateKey) ([]byte, error) { } scheme := sk.Scheme() - return asn1.Marshal(struct { - Version int - Algorithm pkix.AlgorithmIdentifier - PrivateKey []byte - }{ + return asn1.Marshal(pkixPrivKey{ 0, pkix.AlgorithmIdentifier{ Algorithm: scheme.(CertificateScheme).Oid(), From 656402875b429e1bf73178396e24578a361ef967 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 5 Aug 2020 13:11:44 +0200 Subject: [PATCH 15/20] satisfy linter --- pki/pki_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pki/pki_test.go b/pki/pki_test.go index 9bd26cad2..1f24cb27f 100644 --- a/pki/pki_test.go +++ b/pki/pki_test.go @@ -9,6 +9,7 @@ import ( func TestPEM(t *testing.T) { for _, scheme := range api.AllSchemes() { + scheme := scheme t.Run(scheme.Name(), func(t *testing.T) { if scheme == nil { t.FailNow() From 3f119a585f3d0661a4bbee0e8fca61cfe3e94dad Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Thu, 6 Aug 2020 12:18:46 +0200 Subject: [PATCH 16/20] rename sign/api to sign/schemes --- pki/pki.go | 6 +++--- pki/pki_test.go | 4 ++-- sign/{api/api.go => schemes/schemes.go} | 12 ++++++------ sign/{api/api_test.go => schemes/schemes_test.go} | 8 ++++---- sign/sign.go | 4 ++++ 5 files changed, 19 insertions(+), 15 deletions(-) rename sign/{api/api.go => schemes/schemes.go} (61%) rename sign/{api/api_test.go => schemes/schemes_test.go} (92%) diff --git a/pki/pki.go b/pki/pki.go index 30b31a9a9..1727d8a8b 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -9,7 +9,7 @@ import ( "strings" "github.com/cloudflare/circl/sign" - "github.com/cloudflare/circl/sign/api" + "github.com/cloudflare/circl/sign/schemes" ) var allSchemesByOID map[string]sign.Scheme @@ -23,14 +23,14 @@ type pkixPrivKey struct { func init() { allSchemesByOID = make(map[string]sign.Scheme) - for _, scheme := range api.AllSchemes() { + for _, scheme := range schemes.All() { if cert, ok := scheme.(CertificateScheme); ok { allSchemesByOID[cert.Oid().String()] = scheme } } allSchemesByTLS = make(map[uint]sign.Scheme) - for _, scheme := range api.AllSchemes() { + for _, scheme := range schemes.All() { if tlsScheme, ok := scheme.(TLSScheme); ok { allSchemesByTLS[tlsScheme.TLSIdentifier()] = scheme } diff --git a/pki/pki_test.go b/pki/pki_test.go index 1f24cb27f..07d3ddd70 100644 --- a/pki/pki_test.go +++ b/pki/pki_test.go @@ -4,11 +4,11 @@ import ( "testing" "github.com/cloudflare/circl/pki" - "github.com/cloudflare/circl/sign/api" + "github.com/cloudflare/circl/sign/schemes" ) func TestPEM(t *testing.T) { - for _, scheme := range api.AllSchemes() { + for _, scheme := range schemes.All() { scheme := scheme t.Run(scheme.Name(), func(t *testing.T) { if scheme == nil { diff --git a/sign/api/api.go b/sign/schemes/schemes.go similarity index 61% rename from sign/api/api.go rename to sign/schemes/schemes.go index 44cd2e90d..587435251 100644 --- a/sign/api/api.go +++ b/sign/schemes/schemes.go @@ -1,5 +1,5 @@ -// Package api contains a register of signature algorithms. -package api +// Package schemes contains a register of signature algorithms. +package schemes import ( "github.com/cloudflare/circl/sign" @@ -25,9 +25,9 @@ func init() { } } -// SchemeByName returns the scheme with the given name and nil if it is not +// ByName returns the scheme with the given name and nil if it is not // supported. -func SchemeByName(name string) sign.Scheme { return allSchemeNames[name] } +func ByName(name string) sign.Scheme { return allSchemeNames[name] } -// AllSchemes returns all signature schemes supported. -func AllSchemes() []sign.Scheme { a := allSchemes; return a[:] } +// All returns all signature schemes supported. +func All() []sign.Scheme { a := allSchemes; return a[:] } diff --git a/sign/api/api_test.go b/sign/schemes/schemes_test.go similarity index 92% rename from sign/api/api_test.go rename to sign/schemes/schemes_test.go index f041cd4fb..b311a23a7 100644 --- a/sign/api/api_test.go +++ b/sign/schemes/schemes_test.go @@ -1,15 +1,15 @@ -package api_test +package schemes_test import ( "fmt" "testing" "github.com/cloudflare/circl/sign" - "github.com/cloudflare/circl/sign/api" + "github.com/cloudflare/circl/sign/schemes" ) func TestApi(t *testing.T) { - allSchemes := api.AllSchemes() + allSchemes := schemes.All() for _, scheme := range allSchemes { scheme := scheme t.Run(scheme.Name(), func(t *testing.T) { @@ -86,7 +86,7 @@ func TestApi(t *testing.T) { t.FailNow() } - scheme2 := api.SchemeByName(scheme.Name()) + scheme2 := schemes.ByName(scheme.Name()) if scheme2 == nil || scheme2 != scheme { t.FailNow() } diff --git a/sign/sign.go b/sign/sign.go index 56b6095ae..f16425493 100644 --- a/sign/sign.go +++ b/sign/sign.go @@ -1,4 +1,8 @@ // Package sign provides unified interfaces for signature schemes. +// +// A register of schemes is available in the package +// +// github.com/cloudflare/circl/sign/schemes package sign import ( From e93ed248f3055e0ae44a2667d836f1ce8000831f Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 12 Aug 2020 19:20:28 +0200 Subject: [PATCH 17/20] schemes.ByName: make case insensitive --- sign/schemes/schemes.go | 10 ++++++++-- sign/schemes/schemes_test.go | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/sign/schemes/schemes.go b/sign/schemes/schemes.go index 587435251..d1ae65cee 100644 --- a/sign/schemes/schemes.go +++ b/sign/schemes/schemes.go @@ -2,6 +2,8 @@ package schemes import ( + "strings" + "github.com/cloudflare/circl/sign" "github.com/cloudflare/circl/sign/ed25519" "github.com/cloudflare/circl/sign/ed448" @@ -21,13 +23,17 @@ var allSchemeNames map[string]sign.Scheme func init() { allSchemeNames = make(map[string]sign.Scheme) for _, scheme := range allSchemes { - allSchemeNames[scheme.Name()] = scheme + allSchemeNames[strings.ToLower(scheme.Name())] = scheme } } // ByName returns the scheme with the given name and nil if it is not // supported. -func ByName(name string) sign.Scheme { return allSchemeNames[name] } +// +// Names are case insensitive. +func ByName(name string) sign.Scheme { + return allSchemeNames[strings.ToLower(name)] +} // All returns all signature schemes supported. func All() []sign.Scheme { a := allSchemes; return a[:] } diff --git a/sign/schemes/schemes_test.go b/sign/schemes/schemes_test.go index b311a23a7..89704f825 100644 --- a/sign/schemes/schemes_test.go +++ b/sign/schemes/schemes_test.go @@ -8,6 +8,12 @@ import ( "github.com/cloudflare/circl/sign/schemes" ) +func TestCaseSensitivity(t *testing.T) { + if schemes.ByName("ed25519") != schemes.ByName("Ed25519") { + t.Fatal() + } +} + func TestApi(t *testing.T) { allSchemes := schemes.All() for _, scheme := range allSchemes { From f3c2794db958f4e5e74bb86dee773449974b6ddd Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 12 Aug 2020 19:22:17 +0200 Subject: [PATCH 18/20] Use Fail instead of FailNow --- pki/pki_test.go | 6 +++--- sign/schemes/schemes_test.go | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pki/pki_test.go b/pki/pki_test.go index 07d3ddd70..7412dd0ae 100644 --- a/pki/pki_test.go +++ b/pki/pki_test.go @@ -12,7 +12,7 @@ func TestPEM(t *testing.T) { scheme := scheme t.Run(scheme.Name(), func(t *testing.T) { if scheme == nil { - t.FailNow() + t.Fatal() } _, ok := scheme.(pki.CertificateScheme) @@ -35,7 +35,7 @@ func TestPEM(t *testing.T) { t.Fatal(err) } if !pk.Equal(pk2) { - t.FailNow() + t.Fatal() } packedSk, err := pki.MarshalPEMPrivateKey(sk) @@ -49,7 +49,7 @@ func TestPEM(t *testing.T) { } if !sk.Equal(sk2) { - t.FailNow() + t.Fatal() } }) } diff --git a/sign/schemes/schemes_test.go b/sign/schemes/schemes_test.go index 89704f825..1a9870394 100644 --- a/sign/schemes/schemes_test.go +++ b/sign/schemes/schemes_test.go @@ -20,21 +20,21 @@ func TestApi(t *testing.T) { scheme := scheme t.Run(scheme.Name(), func(t *testing.T) { if scheme == nil { - t.FailNow() + t.Fatal() } pk, sk, err := scheme.GenerateKey() if err != nil { - t.FailNow() + t.Fatal() } packedPk, err := pk.MarshalBinary() if err != nil { - t.FailNow() + t.Fatal() } if len(packedPk) != scheme.PublicKeySize() { - t.FailNow() + t.Fatal() } packedSk, err := sk.MarshalBinary() @@ -43,7 +43,7 @@ func TestApi(t *testing.T) { } if len(packedSk) != scheme.PrivateKeySize() { - t.FailNow() + t.Fatal() } pk2, err := scheme.UnmarshalBinaryPublicKey(packedPk) @@ -57,11 +57,11 @@ func TestApi(t *testing.T) { } if !sk.Equal(sk2) { - t.FailNow() + t.Fatal() } if !pk.Equal(pk2) { - t.FailNow() + t.Fatal() } msg := []byte(fmt.Sprintf("Signing with %s", scheme.Name())) @@ -72,37 +72,37 @@ func TestApi(t *testing.T) { sig := scheme.Sign(sk, msg, opts) if scheme.SignatureSize() != len(sig) { - t.FailNow() + t.Fatal() } if !scheme.Verify(pk2, msg, sig, opts) { - t.FailNow() + t.Fatal() } if scheme.SupportsContext() { opts2 := opts opts2.Context = "Wrong context" if scheme.Verify(pk2, msg, sig, opts2) { - t.FailNow() + t.Fatal() } } sig[0]++ if scheme.Verify(pk2, msg, sig, opts) { - t.FailNow() + t.Fatal() } scheme2 := schemes.ByName(scheme.Name()) if scheme2 == nil || scheme2 != scheme { - t.FailNow() + t.Fatal() } if pk.Scheme() != scheme { - t.FailNow() + t.Fatal() } if sk.Scheme() != scheme { - t.FailNow() + t.Fatal() } }) } From 96ed392c00165d2510f7bb616e0cea47a7556752 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 19 Aug 2020 11:03:58 +0200 Subject: [PATCH 19/20] Update pki/pki.go Co-authored-by: Armando Faz --- pki/pki.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pki/pki.go b/pki/pki.go index 1727d8a8b..a5687843f 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -155,7 +155,7 @@ func MarshalPEMPrivateKey(sk sign.PrivateKey) ([]byte, error) { return nil, err } str := pem.EncodeToMemory(&pem.Block{ - Type: fmt.Sprintf("%s PRIVATE KEY", sk.Scheme().Name()), + Type: sk.Scheme().Name() + " PRIVATE KEY", Bytes: data, }, ) From f62944581bc463d789324bb9e104e7865e9fb1f8 Mon Sep 17 00:00:00 2001 From: Bas Westerbaan Date: Wed, 19 Aug 2020 11:16:40 +0200 Subject: [PATCH 20/20] comments armfazh --- pki/pki.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pki/pki.go b/pki/pki.go index a5687843f..a8a52e4b9 100644 --- a/pki/pki.go +++ b/pki/pki.go @@ -5,7 +5,6 @@ import ( "encoding/asn1" "encoding/pem" "errors" - "fmt" "strings" "github.com/cloudflare/circl/sign" @@ -23,14 +22,11 @@ type pkixPrivKey struct { func init() { allSchemesByOID = make(map[string]sign.Scheme) + allSchemesByTLS = make(map[uint]sign.Scheme) for _, scheme := range schemes.All() { if cert, ok := scheme.(CertificateScheme); ok { allSchemesByOID[cert.Oid().String()] = scheme } - } - - allSchemesByTLS = make(map[uint]sign.Scheme) - for _, scheme := range schemes.All() { if tlsScheme, ok := scheme.(TLSScheme); ok { allSchemesByTLS[tlsScheme.TLSIdentifier()] = scheme }