diff --git a/bccsp/sw/ecdsa.go b/bccsp/sw/ecdsa.go index 72d5d13cc11..c04a205ef2e 100644 --- a/bccsp/sw/ecdsa.go +++ b/bccsp/sw/ecdsa.go @@ -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) +} diff --git a/bccsp/sw/ecdsa_test.go b/bccsp/sw/ecdsa_test.go index 0e3ac145d3c..011f3ebcb54 100644 --- a/bccsp/sw/ecdsa_test.go +++ b/bccsp/sw/ecdsa_test.go @@ -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") @@ -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) { diff --git a/bccsp/sw/impl.go b/bccsp/sw/impl.go index 28e1bd5724e..1e00470cf12 100644 --- a/bccsp/sw/impl.go +++ b/bccsp/sw/impl.go @@ -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. @@ -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. @@ -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. diff --git a/bccsp/sw/internals.go b/bccsp/sw/internals.go index f3be3a2d273..1917a662816 100644 --- a/bccsp/sw/internals.go +++ b/bccsp/sw/internals.go @@ -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) +} diff --git a/bccsp/sw/mocks/mocks.go b/bccsp/sw/mocks/mocks.go index 7b608dc22f7..50f2560c4ae 100644 --- a/bccsp/sw/mocks/mocks.go +++ b/bccsp/sw/mocks/mocks.go @@ -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 +} diff --git a/bccsp/sw/rsa.go b/bccsp/sw/rsa.go index 275f599c16e..852d078ad79 100644 --- a/bccsp/sw/rsa.go +++ b/bccsp/sw/rsa.go @@ -18,7 +18,9 @@ package sw import ( "crypto/rand" + "crypto/rsa" "errors" + "fmt" "github.com/hyperledger/fabric/bccsp" ) @@ -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) + } +} diff --git a/bccsp/sw/rsa_test.go b/bccsp/sw/rsa_test.go index 6b9bf62c29a..1e666506736 100644 --- a/bccsp/sw/rsa_test.go +++ b/bccsp/sw/rsa_test.go @@ -23,8 +23,10 @@ import ( "crypto/sha256" "crypto/x509" "encoding/asn1" + "strings" "testing" + "github.com/hyperledger/fabric/bccsp/mocks" "github.com/stretchr/testify/assert" ) @@ -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!!!") @@ -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 [")) } diff --git a/bccsp/sw/verify_test.go b/bccsp/sw/verify_test.go new file mode 100644 index 00000000000..6b89da7cdf2 --- /dev/null +++ b/bccsp/sw/verify_test.go @@ -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) +}