diff --git a/bccsp/mocks/mocks.go b/bccsp/mocks/mocks.go index 816456005a5..661e3c2a7a1 100644 --- a/bccsp/mocks/mocks.go +++ b/bccsp/mocks/mocks.go @@ -123,14 +123,16 @@ func (o *SignerOpts) HashFunc() crypto.Hash { return o.HashFuncValue } -type KeyGenOpts struct{} +type KeyGenOpts struct { + EphemeralValue bool +} func (*KeyGenOpts) Algorithm() string { return "Mock KeyGenOpts" } -func (*KeyGenOpts) Ephemeral() bool { - panic("Not yet implemented") +func (o *KeyGenOpts) Ephemeral() bool { + return o.EphemeralValue } type KeyStore struct { diff --git a/bccsp/sw/aes.go b/bccsp/sw/aes.go index 97b6f0fc1a9..30e6bb13dc0 100644 --- a/bccsp/sw/aes.go +++ b/bccsp/sw/aes.go @@ -30,6 +30,10 @@ import ( // GetRandomBytes returns len random looking bytes func GetRandomBytes(len int) ([]byte, error) { + if len < 0 { + return nil, errors.New("Len must be larger than 0") + } + buffer := make([]byte, len) n, err := rand.Read(buffer) diff --git a/bccsp/sw/impl.go b/bccsp/sw/impl.go index 2661f03f53c..3d2785a984b 100644 --- a/bccsp/sw/impl.go +++ b/bccsp/sw/impl.go @@ -19,7 +19,6 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/hmac" - "crypto/rand" "crypto/rsa" "crypto/x509" "errors" @@ -110,6 +109,22 @@ func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.B verifiers: verifiers, hashers: hashers} + // Set the key generators + keyGenerators := make(map[reflect.Type]KeyGenerator) + keyGenerators[reflect.TypeOf(&bccsp.ECDSAKeyGenOpts{})] = &ecdsaKeyGenerator{curve: conf.ellipticCurve} + keyGenerators[reflect.TypeOf(&bccsp.ECDSAP256KeyGenOpts{})] = &ecdsaKeyGenerator{curve: elliptic.P256()} + keyGenerators[reflect.TypeOf(&bccsp.ECDSAP384KeyGenOpts{})] = &ecdsaKeyGenerator{curve: elliptic.P384()} + keyGenerators[reflect.TypeOf(&bccsp.AESKeyGenOpts{})] = &aesKeyGenerator{length: conf.aesBitLength} + keyGenerators[reflect.TypeOf(&bccsp.AES256KeyGenOpts{})] = &aesKeyGenerator{length: 32} + keyGenerators[reflect.TypeOf(&bccsp.AES192KeyGenOpts{})] = &aesKeyGenerator{length: 24} + keyGenerators[reflect.TypeOf(&bccsp.AES128KeyGenOpts{})] = &aesKeyGenerator{length: 16} + keyGenerators[reflect.TypeOf(&bccsp.RSAKeyGenOpts{})] = &rsaKeyGenerator{length: conf.rsaBitLength} + keyGenerators[reflect.TypeOf(&bccsp.RSA1024KeyGenOpts{})] = &rsaKeyGenerator{length: 1024} + keyGenerators[reflect.TypeOf(&bccsp.RSA2048KeyGenOpts{})] = &rsaKeyGenerator{length: 2048} + keyGenerators[reflect.TypeOf(&bccsp.RSA3072KeyGenOpts{})] = &rsaKeyGenerator{length: 3072} + keyGenerators[reflect.TypeOf(&bccsp.RSA4096KeyGenOpts{})] = &rsaKeyGenerator{length: 4096} + impl.keyGenerators = keyGenerators + return impl, nil } @@ -118,11 +133,12 @@ type impl struct { conf *config ks bccsp.KeyStore - encryptors map[reflect.Type]Encryptor - decryptors map[reflect.Type]Decryptor - signers map[reflect.Type]Signer - verifiers map[reflect.Type]Verifier - hashers map[reflect.Type]Hasher + keyGenerators map[reflect.Type]KeyGenerator + encryptors map[reflect.Type]Encryptor + decryptors map[reflect.Type]Decryptor + signers map[reflect.Type]Signer + verifiers map[reflect.Type]Verifier + hashers map[reflect.Type]Hasher } // KeyGen generates a key using opts. @@ -132,115 +148,14 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { return nil, errors.New("Invalid Opts parameter. It must not be nil.") } - // Parse algorithm - switch opts.(type) { - case *bccsp.ECDSAKeyGenOpts: - lowLevelKey, err := ecdsa.GenerateKey(csp.conf.ellipticCurve, rand.Reader) - if err != nil { - return nil, fmt.Errorf("Failed generating ECDSA key [%s]", err) - } - - k = &ecdsaPrivateKey{lowLevelKey} - - case *bccsp.ECDSAP256KeyGenOpts: - lowLevelKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return nil, fmt.Errorf("Failed generating ECDSA P256 key [%s]", err) - } - - k = &ecdsaPrivateKey{lowLevelKey} - - case *bccsp.ECDSAP384KeyGenOpts: - lowLevelKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - return nil, fmt.Errorf("Failed generating ECDSA P384 key [%s]", err) - } - - k = &ecdsaPrivateKey{lowLevelKey} - - case *bccsp.AESKeyGenOpts: - lowLevelKey, err := GetRandomBytes(csp.conf.aesBitLength) - - if err != nil { - return nil, fmt.Errorf("Failed generating AES key [%s]", err) - } - - k = &aesPrivateKey{lowLevelKey, false} - - case *bccsp.AES256KeyGenOpts: - lowLevelKey, err := GetRandomBytes(32) - - if err != nil { - return nil, fmt.Errorf("Failed generating AES 256 key [%s]", err) - } - - k = &aesPrivateKey{lowLevelKey, false} - - case *bccsp.AES192KeyGenOpts: - lowLevelKey, err := GetRandomBytes(24) - - if err != nil { - return nil, fmt.Errorf("Failed generating AES 192 key [%s]", err) - } - - k = &aesPrivateKey{lowLevelKey, false} - - case *bccsp.AES128KeyGenOpts: - lowLevelKey, err := GetRandomBytes(16) - - if err != nil { - return nil, fmt.Errorf("Failed generating AES 128 key [%s]", err) - } - - k = &aesPrivateKey{lowLevelKey, false} - - case *bccsp.RSAKeyGenOpts: - lowLevelKey, err := rsa.GenerateKey(rand.Reader, csp.conf.rsaBitLength) - - if err != nil { - return nil, fmt.Errorf("Failed generating RSA key [%s]", err) - } - - k = &rsaPrivateKey{lowLevelKey} - - case *bccsp.RSA1024KeyGenOpts: - lowLevelKey, err := rsa.GenerateKey(rand.Reader, 1024) - - if err != nil { - return nil, fmt.Errorf("Failed generating RSA 1024 key [%s]", err) - } - - k = &rsaPrivateKey{lowLevelKey} - - case *bccsp.RSA2048KeyGenOpts: - lowLevelKey, err := rsa.GenerateKey(rand.Reader, 2048) - - if err != nil { - return nil, fmt.Errorf("Failed generating RSA 2048 key [%s]", err) - } - - k = &rsaPrivateKey{lowLevelKey} - - case *bccsp.RSA3072KeyGenOpts: - lowLevelKey, err := rsa.GenerateKey(rand.Reader, 3072) - - if err != nil { - return nil, fmt.Errorf("Failed generating RSA 3072 key [%s]", err) - } - - k = &rsaPrivateKey{lowLevelKey} - - case *bccsp.RSA4096KeyGenOpts: - lowLevelKey, err := rsa.GenerateKey(rand.Reader, 4096) - - if err != nil { - return nil, fmt.Errorf("Failed generating RSA 4096 key [%s]", err) - } - - k = &rsaPrivateKey{lowLevelKey} + keyGenerator, found := csp.keyGenerators[reflect.TypeOf(opts)] + if !found { + return nil, fmt.Errorf("Unsupported 'KeyGenOpts' provided [%v]", opts) + } - default: - return nil, fmt.Errorf("Unrecognized KeyGenOpts provided [%s]", opts.Algorithm()) + k, err = keyGenerator.KeyGen(opts) + if err != nil { + return nil, err } // If the key is not Ephemeral, store it. diff --git a/bccsp/sw/internals.go b/bccsp/sw/internals.go index b41837b07ba..09fed6b59e9 100644 --- a/bccsp/sw/internals.go +++ b/bccsp/sw/internals.go @@ -22,6 +22,13 @@ import ( "github.com/hyperledger/fabric/bccsp" ) +// KeyGenerator is a BCCSP-like interface that provides key generation algorithms +type KeyGenerator interface { + + // KeyGen generates a key using opts. + KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) +} + // Encryptor is a BCCSP-like interface that provides encryption algorithms type Encryptor interface { diff --git a/bccsp/sw/keygen.go b/bccsp/sw/keygen.go new file mode 100644 index 00000000000..124fcdc201f --- /dev/null +++ b/bccsp/sw/keygen.go @@ -0,0 +1,67 @@ +/* +Copyright IBM Corp. 2017 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/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "fmt" + + "github.com/hyperledger/fabric/bccsp" +) + +type ecdsaKeyGenerator struct { + curve elliptic.Curve +} + +func (kg *ecdsaKeyGenerator) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { + privKey, err := ecdsa.GenerateKey(kg.curve, rand.Reader) + if err != nil { + return nil, fmt.Errorf("Failed generating ECDSA key for [%v]: [%s]", kg.curve, err) + } + + return &ecdsaPrivateKey{privKey}, nil +} + +type aesKeyGenerator struct { + length int +} + +func (kg *aesKeyGenerator) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { + lowLevelKey, err := GetRandomBytes(int(kg.length)) + if err != nil { + return nil, fmt.Errorf("Failed generating AES %d key [%s]", kg.length, err) + } + + return &aesPrivateKey{lowLevelKey, false}, nil +} + +type rsaKeyGenerator struct { + length int +} + +func (kg *rsaKeyGenerator) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { + lowLevelKey, err := rsa.GenerateKey(rand.Reader, int(kg.length)) + + if err != nil { + return nil, fmt.Errorf("Failed generating RSA %d key [%s]", kg.length, err) + } + + return &rsaPrivateKey{lowLevelKey}, nil +} diff --git a/bccsp/sw/keygen_test.go b/bccsp/sw/keygen_test.go new file mode 100644 index 00000000000..e6ed1a37027 --- /dev/null +++ b/bccsp/sw/keygen_test.go @@ -0,0 +1,109 @@ +/* +Copyright IBM Corp. 2017 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/elliptic" + "errors" + "reflect" + "testing" + + mocks2 "github.com/hyperledger/fabric/bccsp/mocks" + "github.com/hyperledger/fabric/bccsp/sw/mocks" + "github.com/stretchr/testify/assert" +) + +func TestKeyGen(t *testing.T) { + expectedOpts := &mocks2.KeyGenOpts{EphemeralValue: true} + expectetValue := &mocks2.MockKey{} + expectedErr := errors.New("no error") + + keyGenerators := make(map[reflect.Type]KeyGenerator) + keyGenerators[reflect.TypeOf(&mocks2.KeyGenOpts{})] = &mocks.KeyGenerator{ + OptsArg: expectedOpts, + Value: expectetValue, + Err: expectedErr, + } + csp := impl{keyGenerators: keyGenerators} + value, err := csp.KeyGen(expectedOpts) + assert.Equal(t, nil, value) + assert.Equal(t, expectedErr, err) + + keyGenerators = make(map[reflect.Type]KeyGenerator) + keyGenerators[reflect.TypeOf(&mocks2.KeyGenOpts{})] = &mocks.KeyGenerator{ + OptsArg: expectedOpts, + Value: expectetValue, + Err: nil, + } + csp = impl{keyGenerators: keyGenerators} + value, err = csp.KeyGen(expectedOpts) + assert.Equal(t, expectetValue, value) + assert.Equal(t, nil, err) + +} + +func TestECDSAKeyGenerator(t *testing.T) { + kg := &ecdsaKeyGenerator{curve: elliptic.P256()} + + k, err := kg.KeyGen(nil) + assert.NoError(t, err) + + ecdsaK, ok := k.(*ecdsaPrivateKey) + assert.True(t, ok) + assert.NotNil(t, ecdsaK.privKey) + assert.Equal(t, ecdsaK.privKey.Curve, elliptic.P256()) +} + +func TestRSAKeyGenerator(t *testing.T) { + kg := &rsaKeyGenerator{length: 512} + + k, err := kg.KeyGen(nil) + assert.NoError(t, err) + + rsaK, ok := k.(*rsaPrivateKey) + assert.True(t, ok) + assert.NotNil(t, rsaK.privKey) + assert.Equal(t, rsaK.privKey.N.BitLen(), 512) +} + +func TestAESKeyGenerator(t *testing.T) { + kg := &aesKeyGenerator{length: 32} + + k, err := kg.KeyGen(nil) + assert.NoError(t, err) + + aesK, ok := k.(*aesPrivateKey) + assert.True(t, ok) + assert.NotNil(t, aesK.privKey) + assert.Equal(t, len(aesK.privKey), 32) +} + +func TestAESKeyGeneratorInvalidInputs(t *testing.T) { + kg := &aesKeyGenerator{length: -1} + + _, err := kg.KeyGen(nil) + assert.Error(t, err) + assert.Contains(t, err.Error(), "Len must be larger than 0") +} + +func TestRSAKeyGeneratorInvalidInputs(t *testing.T) { + kg := &rsaKeyGenerator{length: -1} + + _, err := kg.KeyGen(nil) + assert.Error(t, err) + assert.Contains(t, err.Error(), "Failed generating RSA -1 key") +} diff --git a/bccsp/sw/mocks/mocks.go b/bccsp/sw/mocks/mocks.go index 359b6a908f2..947a5f93771 100644 --- a/bccsp/sw/mocks/mocks.go +++ b/bccsp/sw/mocks/mocks.go @@ -124,3 +124,18 @@ func (h *Hasher) GetHash(opts bccsp.HashOpts) (hash.Hash, error) { return h.ValueHash, h.Err } + +type KeyGenerator struct { + OptsArg bccsp.KeyGenOpts + + Value bccsp.Key + Err error +} + +func (kg *KeyGenerator) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { + if !reflect.DeepEqual(kg.OptsArg, opts) { + return nil, errors.New("invalid opts") + } + + return kg.Value, kg.Err +} diff --git a/bccsp/sw/sw_test.go b/bccsp/sw/sw_test.go index 6ff4219cfd8..a3f340c31f9 100644 --- a/bccsp/sw/sw_test.go +++ b/bccsp/sw/sw_test.go @@ -37,7 +37,7 @@ func TestKeyGenInvalidInputs(t *testing.T) { _, err = csp.KeyGen(&mocks.KeyGenOpts{}) assert.Error(t, err) - assert.True(t, strings.Contains(err.Error(), "Unrecognized KeyGenOpts provided [")) + assert.True(t, strings.Contains(err.Error(), "Unsupported 'KeyGenOpts' provided [")) _, err = csp.KeyGen(&bccsp.ECDSAP256KeyGenOpts{}) assert.Error(t, err, "Generation of a non-ephemeral key must fail. KeyStore is programmed to fail.")