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

add openssl support #61

Merged
merged 4 commits into from
May 3, 2019
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test_data/** binary
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ go:

env:
global:
- GOTFLAGS="-race"
matrix:
- BUILD_DEPTYPE=gomod

matrix:
- GOTFLAGS="-race"
- GOTFLAGS="-race -tags=openssl"

# disable travis install
install:
Expand Down
132 changes: 132 additions & 0 deletions fixture_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package crypto_test

import (
"bytes"
"crypto/rand"
"fmt"
"io"
"io/ioutil"
"testing"

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

var message = []byte("Libp2p is the _best_!")

type testCase struct {
keyType crypto_pb.KeyType
gen func(i io.Reader) (crypto.PrivKey, crypto.PubKey, error)
sigDeterministic bool
}

var keyTypes = []testCase{
{
keyType: crypto_pb.KeyType_ECDSA,
gen: crypto.GenerateECDSAKeyPair,
},
{
keyType: crypto_pb.KeyType_Secp256k1,
sigDeterministic: true,
gen: crypto.GenerateSecp256k1Key,
},
{
keyType: crypto_pb.KeyType_RSA,
sigDeterministic: true,
gen: func(i io.Reader) (crypto.PrivKey, crypto.PubKey, error) {
return crypto.GenerateRSAKeyPair(2048, i)
},
},
}

func fname(kt crypto_pb.KeyType, ext string) string {
return fmt.Sprintf("test_data/%d.%s", kt, ext)
}

func TestFixtures(t *testing.T) {
for _, tc := range keyTypes {
t.Run(tc.keyType.String(), func(t *testing.T) {
pubBytes, err := ioutil.ReadFile(fname(tc.keyType, "pub"))
if err != nil {
t.Fatal(err)
}
privBytes, err := ioutil.ReadFile(fname(tc.keyType, "priv"))
if err != nil {
t.Fatal(err)
}
sigBytes, err := ioutil.ReadFile(fname(tc.keyType, "sig"))
if err != nil {
t.Fatal(err)
}
pub, err := crypto.UnmarshalPublicKey(pubBytes)
if err != nil {
t.Fatal(err)
}
pubBytes2, err := pub.Bytes()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(pubBytes2, pubBytes) {
t.Fatal("encoding round-trip failed")
}
priv, err := crypto.UnmarshalPrivateKey(privBytes)
if err != nil {
t.Fatal(err)
}
privBytes2, err := priv.Bytes()
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(privBytes2, privBytes) {
t.Fatal("encoding round-trip failed")
}
ok, err := pub.Verify(message, sigBytes)
if !ok || err != nil {
t.Fatal("failed to validate signature with public key")
}

if tc.sigDeterministic {
sigBytes2, err := priv.Sign(message)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(sigBytes2, sigBytes) {
t.Fatal("signature not deterministic")
}
}
})
}
}

func init() {
// set to true to re-generate test data
if false {
generate()
panic("generated")
}
}

// generate re-generates test data
func generate() {
for _, tc := range keyTypes {
priv, pub, err := tc.gen(rand.Reader)
if err != nil {
panic(err)
}
pubb, err := pub.Bytes()
if err != nil {
panic(err)
}
privb, err := priv.Bytes()
if err != nil {
panic(err)
}
sig, err := priv.Sign(message)
if err != nil {
panic(err)
}
ioutil.WriteFile(fname(tc.keyType, "pub"), pubb, 0666)
ioutil.WriteFile(fname(tc.keyType, "priv"), privb, 0666)
ioutil.WriteFile(fname(tc.keyType, "sig"), sig, 0666)
}
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ require (
github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32
github.com/gogo/protobuf v1.2.1
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16
github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a
golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a h1:/eS3yfGjQKG+9kayBkj0ip1BGhq6zJ3eaVksphxAaek=
github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b h1:+/WWzjwW6gidDJnMKWLKLX1gxn7irUTF1fLpQovfQ5M=
golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
Expand Down
98 changes: 98 additions & 0 deletions openssl_common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// +build openssl

package crypto

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

openssl "github.com/spacemonkeygo/openssl"
)

// define these as separate types so we can add more key types later and reuse
// code.
Copy link
Member Author

Choose a reason for hiding this comment

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

Unfortunately, RSA is the only place where we use the PKCS1v15 signature format, as far as I can tell and I can't find an easy way to get something else from this openssl library.


type opensslPublicKey struct {
key openssl.PublicKey
}

type opensslPrivateKey struct {
key openssl.PrivateKey
}

func unmarshalOpensslPrivateKey(b []byte) (opensslPrivateKey, error) {
sk, err := openssl.LoadPrivateKeyFromDER(b)
if err != nil {
return opensslPrivateKey{}, err
}
return opensslPrivateKey{sk}, nil
}

func unmarshalOpensslPublicKey(b []byte) (opensslPublicKey, error) {
sk, err := openssl.LoadPublicKeyFromDER(b)
if err != nil {
return opensslPublicKey{}, err
}
return opensslPublicKey{sk}, nil
}

// Verify compares a signature against input data
func (pk *opensslPublicKey) Verify(data, sig []byte) (bool, error) {
err := pk.key.VerifyPKCS1v15(openssl.SHA256_Method, data, sig)
return err == nil, err
}

func (pk *opensslPublicKey) Type() pb.KeyType {
switch pk.key.KeyType() {
case openssl.KeyTypeRSA:
return pb.KeyType_RSA
default:
return -1
}
}

// Bytes returns protobuf bytes of a public key
func (pk *opensslPublicKey) Bytes() ([]byte, error) {
return MarshalPublicKey(pk)
}

func (pk *opensslPublicKey) Raw() ([]byte, error) {
return pk.key.MarshalPKIXPublicKeyDER()
}

// Equals checks whether this key is equal to another
func (pk *opensslPublicKey) Equals(k Key) bool {
return KeyEqual(pk, k)
}

// Sign returns a signature of the input data
func (sk *opensslPrivateKey) Sign(message []byte) ([]byte, error) {
return sk.key.SignPKCS1v15(openssl.SHA256_Method, message)
}

// GetPublic returns a public key
func (sk *opensslPrivateKey) GetPublic() PubKey {
return &opensslPublicKey{sk.key}
}

func (sk *opensslPrivateKey) Type() pb.KeyType {
switch sk.key.KeyType() {
case openssl.KeyTypeRSA:
return pb.KeyType_RSA
default:
return -1
}
}

// Bytes returns protobuf bytes from a private key
func (sk *opensslPrivateKey) Bytes() ([]byte, error) {
return MarshalPrivateKey(sk)
}

func (sk *opensslPrivateKey) Raw() ([]byte, error) {
return sk.key.MarshalPKCS1PrivateKeyDER()
}

// Equals checks whether this key is equal to another
func (sk *opensslPrivateKey) Equals(k Key) bool {
return KeyEqual(sk, k)
}
10 changes: 10 additions & 0 deletions rsa_common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package crypto

import (
"errors"
)

// ErrRsaKeyTooSmall is returned when trying to generate or parse an RSA key
// that's smaller than 512 bits. Keys need to be larger enough to sign a 256bit
// hash so this is a reasonable absolute minimum.
var ErrRsaKeyTooSmall = errors.New("rsa keys must be >= 512 bits to be useful")
53 changes: 13 additions & 40 deletions rsa.go → rsa_go.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build !openssl

package crypto

import (
Expand All @@ -13,20 +15,14 @@ import (
sha256 "github.com/minio/sha256-simd"
)

// ErrRsaKeyTooSmall is returned when trying to generate or parse an RSA key
// that's smaller than 512 bits. Keys need to be larger enough to sign a 256bit
// hash so this is a reasonable absolute minimum.
var ErrRsaKeyTooSmall = errors.New("rsa keys must be >= 512 bits to be useful")

// RsaPrivateKey is an rsa private key
type RsaPrivateKey struct {
sk *rsa.PrivateKey
pk *rsa.PublicKey
sk rsa.PrivateKey
}

// RsaPublicKey is an rsa public key
type RsaPublicKey struct {
k *rsa.PublicKey
k rsa.PublicKey
}

// GenerateRSAKeyPair generates a new rsa private and public key
Expand All @@ -38,14 +34,14 @@ func GenerateRSAKeyPair(bits int, src io.Reader) (PrivKey, PubKey, error) {
if err != nil {
return nil, nil, err
}
pk := &priv.PublicKey
return &RsaPrivateKey{sk: priv}, &RsaPublicKey{pk}, nil
pk := priv.PublicKey
return &RsaPrivateKey{sk: *priv}, &RsaPublicKey{pk}, nil
}

// Verify compares a signature against input data
func (pk *RsaPublicKey) Verify(data, sig []byte) (bool, error) {
hashed := sha256.Sum256(data)
err := rsa.VerifyPKCS1v15(pk.k, crypto.SHA256, hashed[:], sig)
err := rsa.VerifyPKCS1v15(&pk.k, crypto.SHA256, hashed[:], sig)
if err != nil {
return false, err
}
Expand All @@ -62,12 +58,7 @@ func (pk *RsaPublicKey) Bytes() ([]byte, error) {
}

func (pk *RsaPublicKey) Raw() ([]byte, error) {
return x509.MarshalPKIXPublicKey(pk.k)
}

// Encrypt returns encrypted bytes from the inpu data
func (pk *RsaPublicKey) Encrypt(b []byte) ([]byte, error) {
return rsa.EncryptPKCS1v15(rand.Reader, pk.k, b)
return x509.MarshalPKIXPublicKey(&pk.k)
}

// Equals checks whether this key is equal to another
Expand All @@ -78,20 +69,12 @@ func (pk *RsaPublicKey) Equals(k Key) bool {
// Sign returns a signature of the input data
func (sk *RsaPrivateKey) Sign(message []byte) ([]byte, error) {
hashed := sha256.Sum256(message)
return rsa.SignPKCS1v15(rand.Reader, sk.sk, crypto.SHA256, hashed[:])
return rsa.SignPKCS1v15(rand.Reader, &sk.sk, crypto.SHA256, hashed[:])
}

// GetPublic returns a public key
func (sk *RsaPrivateKey) GetPublic() PubKey {
if sk.pk == nil {
sk.pk = &sk.sk.PublicKey
}
return &RsaPublicKey{sk.pk}
}

// Decrypt returns decrypted bytes of the input encrypted bytes
func (sk *RsaPrivateKey) Decrypt(b []byte) ([]byte, error) {
return rsa.DecryptPKCS1v15(rand.Reader, sk.sk, b)
return &RsaPublicKey{sk.sk.PublicKey}
}

func (sk *RsaPrivateKey) Type() pb.KeyType {
Expand All @@ -104,7 +87,7 @@ func (sk *RsaPrivateKey) Bytes() ([]byte, error) {
}

func (sk *RsaPrivateKey) Raw() ([]byte, error) {
b := x509.MarshalPKCS1PrivateKey(sk.sk)
b := x509.MarshalPKCS1PrivateKey(&sk.sk)
return b, nil
}

Expand All @@ -122,12 +105,7 @@ func UnmarshalRsaPrivateKey(b []byte) (PrivKey, error) {
if sk.N.BitLen() < 512 {
return nil, ErrRsaKeyTooSmall
}
return &RsaPrivateKey{sk: sk}, nil
}

// MarshalRsaPrivateKey returns the x509 bytes of the private key
func MarshalRsaPrivateKey(k *RsaPrivateKey) []byte {
return x509.MarshalPKCS1PrivateKey(k.sk)
return &RsaPrivateKey{sk: *sk}, nil
}

// UnmarshalRsaPublicKey returns a public key from the input x509 bytes
Expand All @@ -143,10 +121,5 @@ func UnmarshalRsaPublicKey(b []byte) (PubKey, error) {
if pk.N.BitLen() < 512 {
return nil, ErrRsaKeyTooSmall
}
return &RsaPublicKey{pk}, nil
}

// MarshalRsaPublicKey returns the x509 bytes from the public key
func MarshalRsaPublicKey(k *RsaPublicKey) ([]byte, error) {
return x509.MarshalPKIXPublicKey(k.k)
return &RsaPublicKey{*pk}, nil
}
Loading