Skip to content

Commit

Permalink
[FAB-3441] bccsp/sw KeyGen test coverage
Browse files Browse the repository at this point in the history
Using the approach discussed in FAB-3465,
this change-sets refactors the way key generatiion
is done at bccsp/sw.
Essentially, the switch has been replaced by a map.
The approach decouples the testing of the bccsp interface
implementation from the cryptographic algorithms.
Test-coverage of the entire bccsp/sw is now at more than 85%y

Change-Id: I6447cb6774a6dfcbd3139b22a7cc62666f39382d
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed May 5, 2017
1 parent 1b7b163 commit a3665e3
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 118 deletions.
8 changes: 5 additions & 3 deletions bccsp/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions bccsp/sw/aes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
143 changes: 29 additions & 114 deletions bccsp/sw/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"errors"
Expand Down Expand Up @@ -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
}

Expand All @@ -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.
Expand All @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions bccsp/sw/internals.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
67 changes: 67 additions & 0 deletions bccsp/sw/keygen.go
Original file line number Diff line number Diff line change
@@ -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
}
109 changes: 109 additions & 0 deletions bccsp/sw/keygen_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
Loading

0 comments on commit a3665e3

Please sign in to comment.