Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

ECDSA verification support #98

Merged
merged 2 commits into from
Aug 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions data/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import (
)

const (
KeyIDLength = sha256.Size * 2
KeyTypeEd25519 = "ed25519"
KeyIDLength = sha256.Size * 2
KeyTypeEd25519 = "ed25519"
KeyTypeECDSA_SHA2_P256 = "ecdsa-sha2-nistp256"
)

type Signed struct {
Expand Down
41 changes: 40 additions & 1 deletion verify/verifiers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package verify

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"encoding/asn1"
"math/big"

"github.com/flynn/go-tuf/data"
"golang.org/x/crypto/ed25519"
)
Expand All @@ -19,7 +25,8 @@ type Verifier interface {

// Verifiers is used to map key types to Verifier instances.
var Verifiers = map[string]Verifier{
data.KeyTypeEd25519: ed25519Verifier{},
data.KeyTypeEd25519: ed25519Verifier{},
data.KeyTypeECDSA_SHA2_P256: p256Verifier{},
}

type ed25519Verifier struct{}
Expand All @@ -34,3 +41,35 @@ func (ed25519Verifier) Verify(key, msg, sig []byte) error {
func (ed25519Verifier) ValidKey(k []byte) bool {
return len(k) == ed25519.PublicKeySize
}

type ecdsaSignature struct {
R, S *big.Int
}

type p256Verifier struct{}

func (p256Verifier) Verify(key, msg, sigBytes []byte) error {
x, y := elliptic.Unmarshal(elliptic.P256(), key)
k := &ecdsa.PublicKey{
Curve: elliptic.P256(),
X: x,
Y: y,
}

var sig ecdsaSignature
if _, err := asn1.Unmarshal(sigBytes, &sig); err != nil {
return ErrInvalid
}

hash := sha256.Sum256(msg)

if !ecdsa.Verify(k, hash[:], sig.R, sig.S) {
return ErrInvalid
}
return nil
}

func (p256Verifier) ValidKey(k []byte) bool {
x, _ := elliptic.Unmarshal(elliptic.P256(), k)
return x != nil
}
12 changes: 1 addition & 11 deletions verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/flynn/go-tuf/data"
"github.com/tent/canonical-json-go"
"golang.org/x/crypto/ed25519"
)

type signedMeta struct {
Expand Down Expand Up @@ -62,15 +61,7 @@ func (db *DB) VerifySignatures(s *data.Signed, role string) error {
}

valid := make(map[string]struct{})
var sigBytes [ed25519.SignatureSize]byte
for _, sig := range s.Signatures {
if _, ok := Verifiers[sig.Method]; !ok {
return ErrWrongMethod
}
if len(sig.Signature) != len(sigBytes) {
return ErrInvalid
}

if !roleData.ValidKey(sig.KeyID) {
continue
}
Expand All @@ -79,8 +70,7 @@ func (db *DB) VerifySignatures(s *data.Signed, role string) error {
continue
}

copy(sigBytes[:], sig.Signature)
if err := Verifiers[sig.Method].Verify(key.Value.Public, msg, sigBytes[:]); err != nil {
if err := Verifiers[key.Type].Verify(key.Value.Public, msg, sig.Signature); err != nil {
return err
}
valid[sig.KeyID] = struct{}{}
Expand Down
59 changes: 54 additions & 5 deletions verify/verify_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package verify

import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"io"
"testing"
"time"

Expand All @@ -18,6 +24,31 @@ type VerifySuite struct{}

var _ = Suite(&VerifySuite{})

type ecdsaSigner struct {
*ecdsa.PrivateKey
}

func (s ecdsaSigner) PublicData() *data.Key {
pub := s.Public().(*ecdsa.PublicKey)
return &data.Key{
Type: data.KeyTypeECDSA_SHA2_P256,
Value: data.KeyValue{Public: elliptic.Marshal(pub.Curve, pub.X, pub.Y)},
}
}

func (s ecdsaSigner) Sign(rand io.Reader, msg []byte, opts crypto.SignerOpts) ([]byte, error) {
hash := sha256.Sum256(msg)
return s.PrivateKey.Sign(rand, hash[:], crypto.SHA256)
}

func (s ecdsaSigner) ID() string {
return s.PublicData().ID()
}

func (ecdsaSigner) Type() string {
return data.KeyTypeECDSA_SHA2_P256
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these definitions belong in the test file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. This patch intentionally implements only verification in the exposed code. This is in preparation for supporting ECDSA signing keys via PKCS11 in external devices like the Yubikey and HSMs. If you can store keys locally, Ed25519 should be used. We're only adding support for ECDSA because the dedicated hardware devices do not support Ed25519.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha.

func (VerifySuite) Test(c *C) {
type test struct {
name string
Expand Down Expand Up @@ -45,11 +76,6 @@ func (VerifySuite) Test(c *C) {
role: "foo",
err: ErrUnknownRole,
},
{
name: "wrong signature method",
mut: func(t *test) { t.s.Signatures[0].Method = "foo" },
err: ErrWrongMethod,
},
{
name: "signature wrong length",
mut: func(t *test) { t.s.Signatures[0].Signature = []byte{0} },
Expand Down Expand Up @@ -139,6 +165,29 @@ func (VerifySuite) Test(c *C) {
exp: &expiredTime,
err: ErrExpired{expiredTime},
},
{
name: "valid ecdsa signature",
mut: func(t *test) {
k, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
s := ecdsaSigner{k}
sign.Sign(t.s, s)
t.s.Signatures = t.s.Signatures[1:]
t.keys = []*data.Key{s.PublicData()}
t.roles["root"].KeyIDs = []string{s.PublicData().ID()}
},
},
{
name: "invalid ecdsa signature",
mut: func(t *test) {
k, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
s := ecdsaSigner{k}
sign.Sign(t.s, s)
t.s.Signatures[1].Signature[0]++
t.keys = append(t.keys, s.PublicData())
t.roles["root"].KeyIDs = append(t.roles["root"].KeyIDs, s.PublicData().ID())
},
err: ErrInvalid,
},
}
for _, t := range tests {
if t.role == "" {
Expand Down