diff --git a/core/crypto/bccsp/bccsp.go b/core/crypto/bccsp/bccsp.go index 15ceb68d8d7..81e745858f0 100644 --- a/core/crypto/bccsp/bccsp.go +++ b/core/crypto/bccsp/bccsp.go @@ -126,7 +126,7 @@ type BCCSP interface { // Verify verifies signature against key k and digest // The opts argument should be appropriate for the algorithm used. - Verify(k Key, signature, digest []byte) (valid bool, err error) + Verify(k Key, signature, digest []byte, opts SignerOpts) (valid bool, err error) // Encrypt encrypts plaintext using key k. // The opts argument should be appropriate for the algorithm used. diff --git a/core/crypto/bccsp/bccsp_opts.go b/core/crypto/bccsp/bccsp_opts.go index 38d7fb5cfa8..f865a3efc3d 100644 --- a/core/crypto/bccsp/bccsp_opts.go +++ b/core/crypto/bccsp/bccsp_opts.go @@ -17,12 +17,16 @@ limitations under the License. package bccsp const ( - // ECDSA Elliptic Curve Digital Signature Algorithm (key gen, import, sign, veirfy). + // ECDSA Elliptic Curve Digital Signature Algorithm (key gen, import, sign, verify), + // at default security level (see primitives package). ECDSA = "ECDSA" // ECDSAReRand ECDSA key re-randomization ECDSAReRand = "ECDSA_RERAND" - // AES Advanced Encryption Standard + // RSA at default security level (see primitives package) + RSA = "RSA" + + // AES Advanced Encryption Standard at default security level (see primitives package) AES = "AES" // HMAC keyed-hash message authentication code @@ -30,7 +34,7 @@ const ( // HMACTruncated256 HMAC truncated at 256 bits. HMACTruncated256 = "HMAC_TRUNCATED_256" - // SHA Secure Hash Algorithm + // SHA Secure Hash Algorithm using default family (see primitives package) SHA = "SHA" ) @@ -185,3 +189,20 @@ type SHAOpts struct { func (opts *SHAOpts) Algorithm() string { return SHA } + +// RSAKeyGenOpts contains options for RSA key generation. +type RSAKeyGenOpts struct { + Temporary bool +} + +// Algorithm returns an identifier for the algorithm to be used +// to generate a key. +func (opts *RSAKeyGenOpts) Algorithm() string { + return RSA +} + +// Ephemeral returns true if the key to generate has to be ephemeral, +// false otherwise. +func (opts *RSAKeyGenOpts) Ephemeral() bool { + return opts.Temporary +} diff --git a/core/crypto/bccsp/signer/signer_test.go b/core/crypto/bccsp/signer/signer_test.go index 8451171da4e..c57cc9fb26f 100644 --- a/core/crypto/bccsp/signer/signer_test.go +++ b/core/crypto/bccsp/signer/signer_test.go @@ -100,7 +100,7 @@ func TestSign(t *testing.T) { t.Fatalf("Failed generating ECDSA signature [%s]", err) } - valid, err := csp.Verify(k, signature, primitives.Hash(msg)) + valid, err := csp.Verify(k, signature, primitives.Hash(msg), nil) if err != nil { t.Fatalf("Failed verifying ECDSA signature [%s]", err) } diff --git a/core/crypto/bccsp/sw/impl.go b/core/crypto/bccsp/sw/impl.go index 307ab424f08..729c01fe9e8 100644 --- a/core/crypto/bccsp/sw/impl.go +++ b/core/crypto/bccsp/sw/impl.go @@ -24,6 +24,8 @@ import ( "fmt" "math/big" + "crypto/rsa" + "github.com/hyperledger/fabric/core/crypto/bccsp" "github.com/hyperledger/fabric/core/crypto/primitives" "github.com/hyperledger/fabric/core/crypto/utils" @@ -101,6 +103,25 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { } } + return k, nil + case bccsp.RSA: + lowLevelKey, err := primitives.NewRSAKey() + + if err != nil { + return nil, fmt.Errorf("Failed generating RSA (2048) key [%s]", err) + } + + k = &rsaPrivateKey{lowLevelKey} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.storePrivateKey(hex.EncodeToString(k.SKI()), lowLevelKey) + if err != nil { + return nil, fmt.Errorf("Failed storing AES key [%s]", err) + } + } + return k, nil default: return nil, fmt.Errorf("Algorithm not recognized [%s]", opts.Algorithm()) @@ -309,6 +330,8 @@ func (csp *impl) GetKey(ski []byte) (k bccsp.Key, err error) { switch key.(type) { case *ecdsa.PrivateKey: return &ecdsaPrivateKey{key.(*ecdsa.PrivateKey)}, nil + case *rsa.PrivateKey: + return &rsaPrivateKey{key.(*rsa.PrivateKey)}, nil default: return nil, errors.New("Key type not recognized") } @@ -350,13 +373,19 @@ func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signat switch k.(type) { case *ecdsaPrivateKey: return k.(*ecdsaPrivateKey).k.Sign(rand.Reader, digest, nil) + case *rsaPrivateKey: + if opts == nil { + return nil, errors.New("Invalid options. Nil.") + } + + return k.(*rsaPrivateKey).k.Sign(rand.Reader, digest, opts) default: return nil, fmt.Errorf("Key type not recognized [%s]", k) } } // Verify verifies signature against key k and digest -func (csp *impl) Verify(k bccsp.Key, signature, digest []byte) (valid bool, err error) { +func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) { // Validate arguments if k == nil { return false, errors.New("Invalid Key. It must not be nil.") @@ -378,6 +407,20 @@ func (csp *impl) Verify(k bccsp.Key, signature, digest []byte) (valid bool, err } return ecdsa.Verify(&(k.(*ecdsaPrivateKey).k.PublicKey), digest, ecdsaSignature.R, ecdsaSignature.S), nil + case *rsaPrivateKey: + if opts == nil { + return false, errors.New("Invalid options. Nil.") + } + switch opts.(type) { + case *rsa.PSSOptions: + err := rsa.VerifyPSS(&(k.(*rsaPrivateKey).k.PublicKey), + (opts.(*rsa.PSSOptions)).Hash, + digest, signature, opts.(*rsa.PSSOptions)) + + return err == nil, err + default: + return false, fmt.Errorf("Opts type not recognized [%s]", opts) + } default: return false, fmt.Errorf("Key type not recognized [%s]", k) } diff --git a/core/crypto/bccsp/sw/impl_test.go b/core/crypto/bccsp/sw/impl_test.go index fd4b5e24013..3f5bd01c2bb 100644 --- a/core/crypto/bccsp/sw/impl_test.go +++ b/core/crypto/bccsp/sw/impl_test.go @@ -20,6 +20,9 @@ import ( "os" "testing" + "crypto" + "crypto/rsa" + "github.com/hyperledger/fabric/core/crypto/bccsp" "github.com/hyperledger/fabric/core/crypto/primitives" "github.com/spf13/viper" @@ -253,7 +256,7 @@ func TestECDSAVerify(t *testing.T) { t.Fatalf("Failed generating ECDSA signature [%s]", err) } - valid, err := csp.Verify(k, signature, digest) + valid, err := csp.Verify(k, signature, digest, nil) if err != nil { t.Fatalf("Failed verifying ECDSA signature [%s]", err) } @@ -287,7 +290,7 @@ func TestECDSAKeyDeriv(t *testing.T) { t.Fatalf("Failed generating ECDSA signature [%s]", err) } - valid, err := csp.Verify(reRandomizedKey, signature, digest) + valid, err := csp.Verify(reRandomizedKey, signature, digest, nil) if err != nil { t.Fatalf("Failed verifying ECDSA signature [%s]", err) } @@ -553,3 +556,198 @@ func TestSHA(t *testing.T) { } } } + +func TestRSAKeyGenEphemeral(t *testing.T) { + csp := getBCCSP(t) + + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating RSA key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating RSA key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating RSA key. Key should be asymmetric") + } +} + +func TestRSAPrivateKeySKI(t *testing.T) { + csp := getBCCSP(t) + + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + ski := k.SKI() + if len(ski) == 0 { + t.Fatal("SKI not valid. Zero length.") + } +} + +func TestRSAKeyGenNonEphemeral(t *testing.T) { + csp := getBCCSP(t) + + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating RSA key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating RSA key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating RSA key. Key should be asymmetric") + } +} + +func TestRSAGetKeyBySKI(t *testing.T) { + csp := getBCCSP(t) + + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + k2, err := csp.GetKey(k.SKI()) + if err != nil { + t.Fatalf("Failed getting RSA key [%s]", err) + } + if k2 == nil { + t.Fatal("Failed getting RSA key. Key must be different from nil") + } + if !k2.Private() { + t.Fatal("Failed getting RSA key. Key should be private") + } + if k2.Symmetric() { + t.Fatal("Failed getting RSA key. Key should be asymmetric") + } + + // Check that the SKIs are the same + if !bytes.Equal(k.SKI(), k2.SKI()) { + t.Fatalf("SKIs are different [%x]!=[%x]", k.SKI(), k2.SKI()) + } +} + +func TestRSAPublicKeyFromPrivateKey(t *testing.T) { + csp := getBCCSP(t) + + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting public key from private RSA key [%s]", err) + } + if pk == nil { + t.Fatal("Failed getting public key from private RSA key. Key must be different from nil") + } + if pk.Private() { + t.Fatal("Failed generating RSA key. Key should be public") + } + if pk.Symmetric() { + t.Fatal("Failed generating RSA key. Key should be asymmetric") + } +} + +func TestRSAPublicKeyBytes(t *testing.T) { + csp := getBCCSP(t) + + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting public key from private RSA key [%s]", err) + } + + raw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed marshalling RSA public key [%s]", err) + } + if len(raw) == 0 { + t.Fatal("Failed marshalling RSA public key. Zero length") + } +} + +func TestRSAPublicKeySKI(t *testing.T) { + csp := getBCCSP(t) + + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting public key from private RSA key [%s]", err) + } + + ski := pk.SKI() + if len(ski) == 0 { + t.Fatal("SKI not valid. Zero length.") + } +} + +func TestRSASign(t *testing.T) { + csp := getBCCSP(t) + + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + msg := []byte("Hello World") + + digest, err := csp.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := csp.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}) + if err != nil { + t.Fatalf("Failed generating RSA signature [%s]", err) + } + if len(signature) == 0 { + t.Fatal("Failed generating RSA key. Signature must be different from nil") + } +} + +func TestRSAVerify(t *testing.T) { + csp := getBCCSP(t) + + k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + msg := []byte("Hello World") + + digest, err := csp.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := csp.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}) + if err != nil { + t.Fatalf("Failed generating RSA signature [%s]", err) + } + + valid, err := csp.Verify(k, signature, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256}) + if err != nil { + t.Fatalf("Failed verifying RSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying RSA signature. Signature not valid.") + } +} diff --git a/core/crypto/bccsp/sw/raskey.go b/core/crypto/bccsp/sw/raskey.go new file mode 100644 index 00000000000..fbb569f418a --- /dev/null +++ b/core/crypto/bccsp/sw/raskey.go @@ -0,0 +1,100 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package sw + +import ( + "crypto/rsa" + "crypto/x509" + "fmt" + + "github.com/hyperledger/fabric/core/crypto/bccsp" + "github.com/hyperledger/fabric/core/crypto/primitives" +) + +type rsaPrivateKey struct { + k *rsa.PrivateKey +} + +// Bytes converts this key to its byte representation, +// if this operation is allowed. +func (k *rsaPrivateKey) Bytes() (raw []byte, err error) { + return +} + +// SKI returns the subject key identifier of this key. +func (k *rsaPrivateKey) SKI() (ski []byte) { + raw := x509.MarshalPKCS1PrivateKey(k.k) + + return primitives.Hash(raw) +} + +// Symmetric returns true if this key is a symmetric key, +// false is this key is asymmetric +func (k *rsaPrivateKey) Symmetric() bool { + return false +} + +// Private returns true if this key is an asymmetric private key, +// false otherwise. +func (k *rsaPrivateKey) Private() bool { + return true +} + +// PublicKey returns the corresponding public key part of an asymmetric public/private key pair. +// This method returns an error in symmetric key schemes. +func (k *rsaPrivateKey) PublicKey() (bccsp.Key, error) { + return &rsaPublicKey{&k.k.PublicKey}, nil +} + +type rsaPublicKey struct { + k *rsa.PublicKey +} + +// Bytes converts this key to its byte representation, +// if this operation is allowed. +func (k *rsaPublicKey) Bytes() (raw []byte, err error) { + raw, err = x509.MarshalPKIXPublicKey(k.k) + if err != nil { + return nil, fmt.Errorf("Failed marshalling key [%s]", err) + } + return +} + +// SKI returns the subject key identifier of this key. +func (k *rsaPublicKey) SKI() (ski []byte) { + raw, _ := primitives.PublicKeyToPEM(k.k, nil) + // TODO: Error should not be thrown. Anyway, move the marshalling at initialization. + + return primitives.Hash(raw) +} + +// Symmetric returns true if this key is a symmetric key, +// false is this key is asymmetric +func (k *rsaPublicKey) Symmetric() bool { + return false +} + +// Private returns true if this key is an asymmetric private key, +// false otherwise. +func (k *rsaPublicKey) Private() bool { + return false +} + +// PublicKey returns the corresponding public key part of an asymmetric public/private key pair. +// This method returns an error in symmetric key schemes. +func (k *rsaPublicKey) PublicKey() (bccsp.Key, error) { + return k, nil +} diff --git a/core/crypto/primitives/init.go b/core/crypto/primitives/init.go index b42a4f5f57f..bb88d972dad 100644 --- a/core/crypto/primitives/init.go +++ b/core/crypto/primitives/init.go @@ -36,9 +36,11 @@ func initSHA2(level int) (err error) { case 256: defaultCurve = elliptic.P256() defaultHash = sha256.New + defaultRSABitSize = 2048 case 384: defaultCurve = elliptic.P384() defaultHash = sha512.New384 + defaultRSABitSize = 3072 default: err = fmt.Errorf("Security level not supported [%d]", level) } @@ -51,9 +53,11 @@ func initSHA3(level int) (err error) { case 256: defaultCurve = elliptic.P256() defaultHash = sha3.New256 + defaultRSABitSize = 2048 case 384: defaultCurve = elliptic.P384() defaultHash = sha3.New384 + defaultRSABitSize = 3072 default: err = fmt.Errorf("Security level not supported [%d]", level) } diff --git a/core/crypto/primitives/keys.go b/core/crypto/primitives/keys.go index 9ab128fbf9c..2fe2c2159de 100644 --- a/core/crypto/primitives/keys.go +++ b/core/crypto/primitives/keys.go @@ -57,6 +57,15 @@ func PrivateKeyToPEM(privateKey interface{}, pwd []byte) ([]byte, error) { Bytes: raw, }, ), nil + case *rsa.PrivateKey: + raw := x509.MarshalPKCS1PrivateKey(x) + + return pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: raw, + }, + ), nil default: return nil, utils.ErrInvalidKey } diff --git a/core/crypto/primitives/rsa.go b/core/crypto/primitives/rsa.go new file mode 100644 index 00000000000..13ede6f4e6e --- /dev/null +++ b/core/crypto/primitives/rsa.go @@ -0,0 +1,35 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package primitives + +import ( + "crypto/rand" + "crypto/rsa" +) + +var ( + defaultRSABitSize int +) + +// GetRSABitLength returns the RSA modulo bit size +func GetRSABitSize() int { + return defaultRSABitSize +} + +// NewRSAKey generates a new RSA Key +func NewRSAKey() (*rsa.PrivateKey, error) { + return rsa.GenerateKey(rand.Reader, defaultRSABitSize) +}