Skip to content
This repository has been archived by the owner on Dec 7, 2019. It is now read-only.

Commit

Permalink
Merge pull request #35 from c3systems/forPR
Browse files Browse the repository at this point in the history
Added ECDSA; Added RSA tests; Fixed linting errors; Handling all un-handled errors
  • Loading branch information
Stebalien authored Sep 28, 2018
2 parents 137c71c + 5d08597 commit 0f79fbe
Show file tree
Hide file tree
Showing 12 changed files with 513 additions and 56 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.DS_Store

# vim
.swp
*~
*.swp
*.swo
186 changes: 186 additions & 0 deletions ecdsa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package crypto

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/asn1"
"errors"
"io"
"math/big"

pb "github.com/libp2p/go-libp2p-crypto/pb"

sha256 "github.com/minio/sha256-simd"
)

// ECDSAPrivateKey is an implementation of an ECDSA private key
type ECDSAPrivateKey struct {
priv *ecdsa.PrivateKey
}

// ECDSAPublicKey is an implementation of an ECDSA public key
type ECDSAPublicKey struct {
pub *ecdsa.PublicKey
}

// ECDSASig holds the r and s values of an ECDSA signature
type ECDSASig struct {
R, S *big.Int
}

var (
// ErrNotECDSAPubKey is returned when the public key passed is not an ecdsa public key
ErrNotECDSAPubKey = errors.New("not an ecdsa public key")
// ErrNilSig is returned when the signature is nil
ErrNilSig = errors.New("sig is nil")
// ErrNilPrivateKey is returned when a nil private key is provided
ErrNilPrivateKey = errors.New("private key is nil")
// ECDSACurve is the default ecdsa curve used
ECDSACurve = elliptic.P256()
)

// GenerateECDSAKeyPair generates a new ecdsa private and public key
func GenerateECDSAKeyPair(src io.Reader) (PrivKey, PubKey, error) {
return GenerateECDSAKeyPairWithCurve(ECDSACurve, src)
}

// GenerateECDSAKeyPairWithCurve generates a new ecdsa private and public key with a speicified curve
func GenerateECDSAKeyPairWithCurve(curve elliptic.Curve, src io.Reader) (PrivKey, PubKey, error) {
priv, err := ecdsa.GenerateKey(curve, src)
if err != nil {
return nil, nil, err
}

return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
}

// ECDSAKeyPairFromKey generates a new ecdsa private and public key from an input private key
func ECDSAKeyPairFromKey(priv *ecdsa.PrivateKey) (PrivKey, PubKey, error) {
if priv == nil {
return nil, nil, ErrNilPrivateKey
}

return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
}

// MarshalECDSAPrivateKey returns x509 bytes from a private key
func MarshalECDSAPrivateKey(ePriv ECDSAPrivateKey) ([]byte, error) {
return x509.MarshalECPrivateKey(ePriv.priv)
}

// MarshalECDSAPublicKey returns x509 bytes from a public key
func MarshalECDSAPublicKey(ePub ECDSAPublicKey) ([]byte, error) {
return x509.MarshalPKIXPublicKey(ePub.pub)
}

// UnmarshalECDSAPrivateKey returns a private key from x509 bytes
func UnmarshalECDSAPrivateKey(data []byte) (PrivKey, error) {
priv, err := x509.ParseECPrivateKey(data)
if err != nil {
return nil, err
}

return &ECDSAPrivateKey{priv}, nil
}

// UnmarshalECDSAPublicKey returns the public key from x509 bytes
func UnmarshalECDSAPublicKey(data []byte) (PubKey, error) {
pubIfc, err := x509.ParsePKIXPublicKey(data)
if err != nil {
return nil, err
}

pub, ok := pubIfc.(*ecdsa.PublicKey)
if !ok {
return nil, ErrNotECDSAPubKey
}

return &ECDSAPublicKey{pub}, nil
}

// Bytes returns the private key as protobuf bytes
func (ePriv *ECDSAPrivateKey) Bytes() ([]byte, error) {
return MarshalPrivateKey(ePriv)
}

// Type returns the key type
func (ePriv *ECDSAPrivateKey) Type() pb.KeyType {
return pb.KeyType_ECDSA
}

// Raw returns x509 bytes from a private key
func (ePriv *ECDSAPrivateKey) Raw() ([]byte, error) {
return x509.MarshalECPrivateKey(ePriv.priv)
}

// Equals compares to private keys
func (ePriv *ECDSAPrivateKey) Equals(o Key) bool {
oPriv, ok := o.(*ECDSAPrivateKey)
if !ok {
return false
}

return ePriv.priv.D.Cmp(oPriv.priv.D) == 0
}

// Sign returns the signature of the input data
func (ePriv *ECDSAPrivateKey) Sign(data []byte) ([]byte, error) {
hash := sha256.Sum256(data)
r, s, err := ecdsa.Sign(rand.Reader, ePriv.priv, hash[:])
if err != nil {
return nil, err
}

return asn1.Marshal(ECDSASig{
R: r,
S: s,
})
}

// GetPublic returns a public key
func (ePriv *ECDSAPrivateKey) GetPublic() PubKey {
return &ECDSAPublicKey{&ePriv.priv.PublicKey}
}

// Bytes returns the public key as protobuf bytes
func (ePub *ECDSAPublicKey) Bytes() ([]byte, error) {
return MarshalPublicKey(ePub)
}

// Type returns the key type
func (ePub *ECDSAPublicKey) Type() pb.KeyType {
return pb.KeyType_ECDSA
}

// Raw returns x509 bytes from a public key
func (ePub ECDSAPublicKey) Raw() ([]byte, error) {
return x509.MarshalPKIXPublicKey(ePub.pub)
}

// Equals compares to public keys
func (ePub *ECDSAPublicKey) Equals(o Key) bool {
oPub, ok := o.(*ECDSAPublicKey)
if !ok {
return false
}

return ePub.pub.X != nil && ePub.pub.Y != nil && oPub.pub.X != nil && oPub.pub.Y != nil &&
0 == ePub.pub.X.Cmp(oPub.pub.X) && 0 == ePub.pub.Y.Cmp(oPub.pub.Y)
}

// Verify compares data to a signature
func (ePub *ECDSAPublicKey) Verify(data, sigBytes []byte) (bool, error) {
sig := new(ECDSASig)
if _, err := asn1.Unmarshal(sigBytes, sig); err != nil {
return false, err
}
if sig == nil {
return false, ErrNilSig
}

hash := sha256.Sum256(data)

return ecdsa.Verify(ePub.pub, hash[:], sig.R, sig.S), nil
}
96 changes: 96 additions & 0 deletions ecdsa_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package crypto

import (
"crypto/rand"
"testing"
)

func TestECDSABasicSignAndVerify(t *testing.T) {
priv, pub, err := GenerateECDSAKeyPair(rand.Reader)
if err != nil {
t.Fatal(err)
}

data := []byte("hello! and welcome to some awesome crypto primitives")

sig, err := priv.Sign(data)
if err != nil {
t.Fatal(err)
}

ok, err := pub.Verify(data, sig)
if err != nil {
t.Fatal(err)
}

if !ok {
t.Fatal("signature didnt match")
}

// change data
data[0] = ^data[0]
ok, err = pub.Verify(data, sig)
if err != nil {
t.Fatal(err)
}

if ok {
t.Fatal("signature matched and shouldn't")
}
}

func TestECDSASignZero(t *testing.T) {
priv, pub, err := GenerateECDSAKeyPair(rand.Reader)
if err != nil {
t.Fatal(err)
}

data := make([]byte, 0)
sig, err := priv.Sign(data)
if err != nil {
t.Fatal(err)
}

ok, err := pub.Verify(data, sig)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("signature didn't match")
}
}

func TestECDSAMarshalLoop(t *testing.T) {
priv, pub, err := GenerateECDSAKeyPair(rand.Reader)
if err != nil {
t.Fatal(err)
}

privB, err := priv.Bytes()
if err != nil {
t.Fatal(err)
}

privNew, err := UnmarshalPrivateKey(privB)
if err != nil {
t.Fatal(err)
}

if !priv.Equals(privNew) || !privNew.Equals(priv) {
t.Fatal("keys are not equal")
}

pubB, err := pub.Bytes()
if err != nil {
t.Fatal(err)
}
pubNew, err := UnmarshalPublicKey(pubB)
if err != nil {
t.Fatal(err)
}

if !pub.Equals(pubNew) || !pubNew.Equals(pub) {
t.Fatal("keys are not equal")
}

}
12 changes: 12 additions & 0 deletions ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@ import (
"io"

pb "github.com/libp2p/go-libp2p-crypto/pb"

"golang.org/x/crypto/ed25519"
)

// Ed25519PrivateKey is an ed25519 private key
type Ed25519PrivateKey struct {
k ed25519.PrivateKey
}

// Ed25519PublicKey is an ed25519 public key
type Ed25519PublicKey struct {
k ed25519.PublicKey
}

// GenerateEd25519Key generate a new ed25519 private and public key pair
func GenerateEd25519Key(src io.Reader) (PrivKey, PubKey, error) {
pub, priv, err := ed25519.GenerateKey(src)
if err != nil {
Expand All @@ -36,6 +40,7 @@ func (k *Ed25519PrivateKey) Type() pb.KeyType {
return pb.KeyType_Ed25519
}

// Bytes marshals an ed25519 private key to protobuf bytes
func (k *Ed25519PrivateKey) Bytes() ([]byte, error) {
return MarshalPrivateKey(k)
}
Expand All @@ -55,6 +60,7 @@ func (k *Ed25519PrivateKey) pubKeyBytes() []byte {
return k.k[ed25519.PrivateKeySize-ed25519.PublicKeySize:]
}

// Equals compares two ed25519 private keys
func (k *Ed25519PrivateKey) Equals(o Key) bool {
edk, ok := o.(*Ed25519PrivateKey)
if !ok {
Expand All @@ -64,10 +70,12 @@ func (k *Ed25519PrivateKey) Equals(o Key) bool {
return bytes.Equal(k.k, edk.k)
}

// GetPublic returns an ed25519 public key from a private key
func (k *Ed25519PrivateKey) GetPublic() PubKey {
return &Ed25519PublicKey{k: k.pubKeyBytes()}
}

// Sign returns a signature from an input message
func (k *Ed25519PrivateKey) Sign(msg []byte) ([]byte, error) {
return ed25519.Sign(k.k, msg), nil
}
Expand All @@ -76,6 +84,7 @@ func (k *Ed25519PublicKey) Type() pb.KeyType {
return pb.KeyType_Ed25519
}

// Bytes returns a ed25519 public key as protobuf bytes
func (k *Ed25519PublicKey) Bytes() ([]byte, error) {
return MarshalPublicKey(k)
}
Expand All @@ -84,6 +93,7 @@ func (k *Ed25519PublicKey) Raw() ([]byte, error) {
return k.k, nil
}

// Equals compares two ed25519 public keys
func (k *Ed25519PublicKey) Equals(o Key) bool {
edk, ok := o.(*Ed25519PublicKey)
if !ok {
Expand All @@ -93,6 +103,7 @@ func (k *Ed25519PublicKey) Equals(o Key) bool {
return bytes.Equal(k.k, edk.k)
}

// Verify checks a signature agains the input data
func (k *Ed25519PublicKey) Verify(data []byte, sig []byte) (bool, error) {
return ed25519.Verify(k.k, data, sig), nil
}
Expand All @@ -106,6 +117,7 @@ func UnmarshalEd25519PublicKey(data []byte) (PubKey, error) {
}, nil
}

// UnmarshalEd25519PrivateKey returns a private key from input bytes
func UnmarshalEd25519PrivateKey(data []byte) (PrivKey, error) {
switch len(data) {
case ed25519.PrivateKeySize + ed25519.PublicKeySize:
Expand Down
Loading

0 comments on commit 0f79fbe

Please sign in to comment.