Skip to content

Commit

Permalink
[FAB-3441] bccsp/sw ECDSA/RSA verify 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
ECDSA/RSA verify 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 ECDSA/RSA is now at more than 90%

Change-Id: I1f9d6f4fc50a94bd7da9e9f011084555fc739bdc
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed May 3, 2017
1 parent f15b89c commit 7aa43d5
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 42 deletions.
12 changes: 12 additions & 0 deletions bccsp/sw/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,15 @@ type ecdsaSigner struct{}
func (s *ecdsaSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) {
return signECDSA(k.(*ecdsaPrivateKey).privKey, digest, opts)
}

type ecdsaPrivateKeyVerifier struct{}

func (v *ecdsaPrivateKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
return verifyECDSA(&(k.(*ecdsaPrivateKey).privKey.PublicKey), signature, digest, opts)
}

type ecdsaPublicKeyKeyVerifier struct{}

func (v *ecdsaPublicKeyKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
return verifyECDSA(k.(*ecdsaPublicKey).pubKey, signature, digest, opts)
}
12 changes: 12 additions & 0 deletions bccsp/sw/ecdsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,15 @@ func TestVerifyECDSA(t *testing.T) {

func TestEcdsaSignerSign(t *testing.T) {
signer := &ecdsaSigner{}
verifierPrivateKey := &ecdsaPrivateKeyVerifier{}
verifierPublicKey := &ecdsaPublicKeyKeyVerifier{}

// Generate a key
lowLevelKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.NoError(t, err)
k := &ecdsaPrivateKey{lowLevelKey}
pk, err := k.PublicKey()
assert.NoError(t, err)

// Sign
msg := []byte("Hello World")
Expand All @@ -154,6 +158,14 @@ func TestEcdsaSignerSign(t *testing.T) {
valid, err := verifyECDSA(&lowLevelKey.PublicKey, sigma, msg, nil)
assert.NoError(t, err)
assert.True(t, valid)

valid, err = verifierPrivateKey.Verify(k, sigma, msg, nil)
assert.NoError(t, err)
assert.True(t, valid)

valid, err = verifierPublicKey.Verify(pk, sigma, msg, nil)
assert.NoError(t, err)
assert.True(t, valid)
}

func TestEcdsaPrivateKey(t *testing.T) {
Expand Down
59 changes: 19 additions & 40 deletions bccsp/sw/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,20 @@ func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.B
signers[reflect.TypeOf(&ecdsaPrivateKey{})] = &ecdsaSigner{}
signers[reflect.TypeOf(&rsaPrivateKey{})] = &rsaSigner{}

// Set the verifiers
verifiers := make(map[reflect.Type]Verifier)
verifiers[reflect.TypeOf(&ecdsaPrivateKey{})] = &ecdsaPrivateKeyVerifier{}
verifiers[reflect.TypeOf(&ecdsaPublicKey{})] = &ecdsaPublicKeyKeyVerifier{}
verifiers[reflect.TypeOf(&rsaPrivateKey{})] = &rsaPrivateKeyVerifier{}
verifiers[reflect.TypeOf(&rsaPublicKey{})] = &rsaPublicKeyKeyVerifier{}

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

// SoftwareBasedBCCSP is the software-based implementation of the BCCSP.
Expand All @@ -101,6 +109,7 @@ type impl struct {
encryptors map[reflect.Type]Encryptor
decryptors map[reflect.Type]Decryptor
signers map[reflect.Type]Signer
verifiers map[reflect.Type]Verifier
}

// KeyGen generates a key using opts.
Expand Down Expand Up @@ -693,43 +702,13 @@ func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.Signer
return false, errors.New("Invalid digest. Cannot be empty.")
}

// Check key type
switch k.(type) {
case *ecdsaPrivateKey:
return verifyECDSA(&(k.(*ecdsaPrivateKey).privKey.PublicKey), signature, digest, opts)
case *ecdsaPublicKey:
return verifyECDSA(k.(*ecdsaPublicKey).pubKey, signature, digest, opts)
case *rsaPrivateKey:
if opts == nil {
return false, errors.New("Invalid options. It must not be nil.")
}
switch opts.(type) {
case *rsa.PSSOptions:
err := rsa.VerifyPSS(&(k.(*rsaPrivateKey).privKey.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)
}
case *rsaPublicKey:
if opts == nil {
return false, errors.New("Invalid options. It must not be nil.")
}
switch opts.(type) {
case *rsa.PSSOptions:
err := rsa.VerifyPSS(k.(*rsaPublicKey).pubKey,
(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:
verifier, found := csp.verifiers[reflect.TypeOf(k)]
if !found {
return false, fmt.Errorf("Unsupported 'VerifyKey' provided [%v]", k)
}

return verifier.Verify(k, signature, digest, opts)

}

// Encrypt encrypts plaintext using key k.
Expand Down
8 changes: 8 additions & 0 deletions bccsp/sw/internals.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,11 @@ type Signer interface {
// the hash (as digest).
Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error)
}

// Verifier is a BCCSP-like interface that provides verifying algorithms
type Verifier interface {

// Verify verifies signature against key k and digest
// The opts argument should be appropriate for the algorithm used.
Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error)
}
27 changes: 27 additions & 0 deletions bccsp/sw/mocks/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,30 @@ func (s *Signer) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signat

return s.Value, s.Err
}

type Verifier struct {
KeyArg bccsp.Key
SignatureArg []byte
DigestArg []byte
OptsArg bccsp.SignerOpts

Value bool
Err error
}

func (s *Verifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
if !reflect.DeepEqual(s.KeyArg, k) {
return false, errors.New("invalid key")
}
if !reflect.DeepEqual(s.SignatureArg, signature) {
return false, errors.New("invalid signature")
}
if !reflect.DeepEqual(s.DigestArg, digest) {
return false, errors.New("invalid digest")
}
if !reflect.DeepEqual(s.OptsArg, opts) {
return false, errors.New("invalid opts")
}

return s.Value, s.Err
}
38 changes: 38 additions & 0 deletions bccsp/sw/rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ package sw

import (
"crypto/rand"
"crypto/rsa"
"errors"
"fmt"

"github.com/hyperledger/fabric/bccsp"
)
Expand All @@ -32,3 +34,39 @@ func (s *rsaSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (sig

return k.(*rsaPrivateKey).privKey.Sign(rand.Reader, digest, opts)
}

type rsaPrivateKeyVerifier struct{}

func (v *rsaPrivateKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
if opts == nil {
return false, errors.New("Invalid options. It must not be nil.")
}
switch opts.(type) {
case *rsa.PSSOptions:
err := rsa.VerifyPSS(&(k.(*rsaPrivateKey).privKey.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)
}
}

type rsaPublicKeyKeyVerifier struct{}

func (v *rsaPublicKeyKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
if opts == nil {
return false, errors.New("Invalid options. It must not be nil.")
}
switch opts.(type) {
case *rsa.PSSOptions:
err := rsa.VerifyPSS(k.(*rsaPublicKey).pubKey,
(opts.(*rsa.PSSOptions)).Hash,
digest, signature, opts.(*rsa.PSSOptions))

return err == nil, err
default:
return false, fmt.Errorf("Opts type not recognized [%s]", opts)
}
}
47 changes: 45 additions & 2 deletions bccsp/sw/rsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import (
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"strings"
"testing"

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

Expand Down Expand Up @@ -92,11 +94,15 @@ func TestRSAPublicKey(t *testing.T) {

func TestRSASignerSign(t *testing.T) {
signer := &rsaSigner{}
verifierPrivateKey := &rsaPrivateKeyVerifier{}
verifierPublicKey := &rsaPublicKeyKeyVerifier{}

// Generate a key
lowLevelKey, err := rsa.GenerateKey(rand.Reader, 1024)
assert.NoError(t, err)
k := &rsaPrivateKey{lowLevelKey}
pk, err := k.PublicKey()
assert.NoError(t, err)

// Sign
msg := []byte("Hello World!!!")
Expand All @@ -114,12 +120,49 @@ func TestRSASignerSign(t *testing.T) {
sigma, err := signer.Sign(k, digest, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: crypto.SHA256})
assert.NoError(t, err)

opts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: crypto.SHA256}
// Verify against msg, must fail
err = rsa.VerifyPSS(&lowLevelKey.PublicKey, crypto.SHA256, msg, sigma, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: crypto.SHA256})
err = rsa.VerifyPSS(&lowLevelKey.PublicKey, crypto.SHA256, msg, sigma, opts)
assert.Error(t, err)
assert.Contains(t, err.Error(), "crypto/rsa: verification error")

// Verify against digest, must succeed
err = rsa.VerifyPSS(&lowLevelKey.PublicKey, crypto.SHA256, digest, sigma, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: crypto.SHA256})
err = rsa.VerifyPSS(&lowLevelKey.PublicKey, crypto.SHA256, digest, sigma, opts)
assert.NoError(t, err)

valid, err := verifierPrivateKey.Verify(k, sigma, msg, opts)
assert.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "crypto/rsa: verification error"))

valid, err = verifierPrivateKey.Verify(k, sigma, digest, opts)
assert.NoError(t, err)
assert.True(t, valid)

valid, err = verifierPublicKey.Verify(pk, sigma, msg, opts)
assert.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "crypto/rsa: verification error"))

valid, err = verifierPublicKey.Verify(pk, sigma, digest, opts)
assert.NoError(t, err)
assert.True(t, valid)
}

func TestRSAVerifiersInvalidInputs(t *testing.T) {
verifierPrivate := &rsaPrivateKeyVerifier{}
_, err := verifierPrivate.Verify(nil, nil, nil, nil)
assert.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "Invalid options. It must not be nil."))

_, err = verifierPrivate.Verify(nil, nil, nil, &mocks.SignerOpts{})
assert.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "Opts type not recognized ["))

verifierPublic := &rsaPublicKeyKeyVerifier{}
_, err = verifierPublic.Verify(nil, nil, nil, nil)
assert.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "Invalid options. It must not be nil."))

_, err = verifierPublic.Verify(nil, nil, nil, &mocks.SignerOpts{})
assert.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "Opts type not recognized ["))
}
52 changes: 52 additions & 0 deletions bccsp/sw/verify_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
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 (
"errors"
"reflect"
"testing"

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

func TestVerify(t *testing.T) {
expectedKey := &mocks2.MockKey{}
expectetSignature := []byte{1, 2, 3, 4, 5}
expectetDigest := []byte{1, 2, 3, 4}
expectedOpts := &mocks2.SignerOpts{}
expectetValue := true
expectedErr := errors.New("no error")

verifiers := make(map[reflect.Type]Verifier)
verifiers[reflect.TypeOf(&mocks2.MockKey{})] = &mocks.Verifier{
KeyArg: expectedKey,
SignatureArg: expectetSignature,
DigestArg: expectetDigest,
OptsArg: expectedOpts,
Value: expectetValue,
Err: expectedErr,
}

csp := impl{verifiers: verifiers}

value, err := csp.Verify(expectedKey, expectetSignature, expectetDigest, expectedOpts)
assert.Equal(t, expectetValue, value)
assert.Equal(t, expectedErr, err)
}

0 comments on commit 7aa43d5

Please sign in to comment.