Skip to content

Commit

Permalink
[FAB-3441] bccsp/sw Hash 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
hashing 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 Hash is now at more than 85%

Change-Id: Ib9c5b45da54241d959c09f7b0351f3163be0c6ef
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed May 5, 2017
1 parent add0af3 commit 1b7b163
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 157 deletions.
6 changes: 6 additions & 0 deletions bccsp/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,9 @@ func (*KeyImportOpts) Ephemeral() bool {
}

type EncrypterOpts struct{}

type HashOpts struct{}

func (HashOpts) Algorithm() string {
return "Mock HashOpts"
}
37 changes: 37 additions & 0 deletions bccsp/sw/hash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
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 (
"hash"

"github.com/hyperledger/fabric/bccsp"
)

type hasher struct {
hash func() hash.Hash
}

func (c *hasher) Hash(msg []byte, opts bccsp.HashOpts) (hash []byte, err error) {
h := c.hash()
h.Write(msg)
return h.Sum(nil), nil
}

func (c *hasher) GetHash(opts bccsp.HashOpts) (h hash.Hash, err error) {
return c.hash(), nil
}
84 changes: 84 additions & 0 deletions bccsp/sw/hash_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
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/sha256"
"errors"
"reflect"
"testing"

mocks2 "github.com/hyperledger/fabric/bccsp/mocks"
"github.com/hyperledger/fabric/bccsp/sw/mocks"
"github.com/stretchr/testify/assert"
)

func TestHash(t *testing.T) {
expectetMsg := []byte{1, 2, 3, 4}
expectedOpts := &mocks2.HashOpts{}
expectetValue := []byte{1, 2, 3, 4, 5}
expectedErr := errors.New("no error")

hashers := make(map[reflect.Type]Hasher)
hashers[reflect.TypeOf(&mocks2.HashOpts{})] = &mocks.Hasher{
MsgArg: expectetMsg,
OptsArg: expectedOpts,
Value: expectetValue,
Err: expectedErr,
}

csp := impl{hashers: hashers}

value, err := csp.Hash(expectetMsg, expectedOpts)
assert.Equal(t, expectetValue, value)
assert.Equal(t, expectedErr, err)
}

func TestGetHash(t *testing.T) {
expectedOpts := &mocks2.HashOpts{}
expectetValue := sha256.New()
expectedErr := errors.New("no error")

hashers := make(map[reflect.Type]Hasher)
hashers[reflect.TypeOf(&mocks2.HashOpts{})] = &mocks.Hasher{
OptsArg: expectedOpts,
ValueHash: expectetValue,
Err: expectedErr,
}

csp := impl{hashers: hashers}

value, err := csp.GetHash(expectedOpts)
assert.Equal(t, expectetValue, value)
assert.Equal(t, expectedErr, err)
}

func TestHasher(t *testing.T) {
hasher := &hasher{hash: sha256.New}

msg := []byte("Hello World")
out, err := hasher.Hash(msg, nil)
assert.NoError(t, err)
h := sha256.New()
h.Write(msg)
out2 := h.Sum(nil)
assert.Equal(t, out, out2)

hf, err := hasher.GetHash(nil)
assert.NoError(t, err)
assert.Equal(t, hf, sha256.New())
}
69 changes: 32 additions & 37 deletions bccsp/sw/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ import (
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
"errors"
"fmt"
"hash"
"math/big"
"reflect"

"crypto/sha256"
"crypto/sha512"

"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/bccsp/utils"
"github.com/hyperledger/fabric/common/flogging"
Expand Down Expand Up @@ -92,13 +93,24 @@ func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.B
verifiers[reflect.TypeOf(&rsaPrivateKey{})] = &rsaPrivateKeyVerifier{}
verifiers[reflect.TypeOf(&rsaPublicKey{})] = &rsaPublicKeyKeyVerifier{}

return &impl{
// Set the hashers
hashers := make(map[reflect.Type]Hasher)
hashers[reflect.TypeOf(&bccsp.SHAOpts{})] = &hasher{hash: conf.hashFunction}
hashers[reflect.TypeOf(&bccsp.SHA256Opts{})] = &hasher{hash: sha256.New}
hashers[reflect.TypeOf(&bccsp.SHA384Opts{})] = &hasher{hash: sha512.New384}
hashers[reflect.TypeOf(&bccsp.SHA3_256Opts{})] = &hasher{hash: sha3.New256}
hashers[reflect.TypeOf(&bccsp.SHA3_384Opts{})] = &hasher{hash: sha3.New384}

impl := &impl{
conf: conf,
ks: keyStore,
encryptors: encryptors,
decryptors: decryptors,
signers: signers,
verifiers: verifiers}, nil
verifiers: verifiers,
hashers: hashers}

return impl, nil
}

// SoftwareBasedBCCSP is the software-based implementation of the BCCSP.
Expand All @@ -110,6 +122,7 @@ type impl struct {
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 Down Expand Up @@ -619,51 +632,33 @@ func (csp *impl) GetKey(ski []byte) (k bccsp.Key, err error) {

// Hash hashes messages msg using options opts.
func (csp *impl) Hash(msg []byte, opts bccsp.HashOpts) (digest []byte, err error) {
var h hash.Hash
// Validate arguments
if opts == nil {
h = csp.conf.hashFunction()
} else {
switch opts.(type) {
case *bccsp.SHAOpts:
h = csp.conf.hashFunction()
case *bccsp.SHA256Opts:
h = sha256.New()
case *bccsp.SHA384Opts:
h = sha512.New384()
case *bccsp.SHA3_256Opts:
h = sha3.New256()
case *bccsp.SHA3_384Opts:
h = sha3.New384()
default:
return nil, fmt.Errorf("Algorithm not recognized [%s]", opts.Algorithm())
}
return nil, errors.New("Invalid opts. It must not be nil.")
}

h.Write(msg)
return h.Sum(nil), nil
hasher, found := csp.hashers[reflect.TypeOf(opts)]
if !found {
return nil, fmt.Errorf("Unsupported 'HashOpt' provided [%v]", opts)
}

return hasher.Hash(msg, opts)
}

// GetHash returns and instance of hash.Hash using options opts.
// If opts is nil then the default hash function is returned.
func (csp *impl) GetHash(opts bccsp.HashOpts) (h hash.Hash, err error) {
// Validate arguments
if opts == nil {
return csp.conf.hashFunction(), nil
return nil, errors.New("Invalid opts. It must not be nil.")
}

switch opts.(type) {
case *bccsp.SHAOpts:
return csp.conf.hashFunction(), nil
case *bccsp.SHA256Opts:
return sha256.New(), nil
case *bccsp.SHA384Opts:
return sha512.New384(), nil
case *bccsp.SHA3_256Opts:
return sha3.New256(), nil
case *bccsp.SHA3_384Opts:
return sha3.New384(), nil
default:
return nil, fmt.Errorf("Algorithm not recognized [%s]", opts.Algorithm())
hasher, found := csp.hashers[reflect.TypeOf(opts)]
if !found {
return nil, fmt.Errorf("Unsupported 'HashOpt' provided [%v]", opts)
}

return hasher.GetHash(opts)
}

// Sign signs digest using key k.
Expand Down
Loading

0 comments on commit 1b7b163

Please sign in to comment.