Skip to content

Commit 7aa43d5

Browse files
committed
[FAB-3441] bccsp/sw ECDSA/RSA verify test coverage
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 <[email protected]>
1 parent f15b89c commit 7aa43d5

File tree

8 files changed

+213
-42
lines changed

8 files changed

+213
-42
lines changed

bccsp/sw/ecdsa.go

+12
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,15 @@ type ecdsaSigner struct{}
121121
func (s *ecdsaSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) {
122122
return signECDSA(k.(*ecdsaPrivateKey).privKey, digest, opts)
123123
}
124+
125+
type ecdsaPrivateKeyVerifier struct{}
126+
127+
func (v *ecdsaPrivateKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
128+
return verifyECDSA(&(k.(*ecdsaPrivateKey).privKey.PublicKey), signature, digest, opts)
129+
}
130+
131+
type ecdsaPublicKeyKeyVerifier struct{}
132+
133+
func (v *ecdsaPublicKeyKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
134+
return verifyECDSA(k.(*ecdsaPublicKey).pubKey, signature, digest, opts)
135+
}

bccsp/sw/ecdsa_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,15 @@ func TestVerifyECDSA(t *testing.T) {
138138

139139
func TestEcdsaSignerSign(t *testing.T) {
140140
signer := &ecdsaSigner{}
141+
verifierPrivateKey := &ecdsaPrivateKeyVerifier{}
142+
verifierPublicKey := &ecdsaPublicKeyKeyVerifier{}
141143

142144
// Generate a key
143145
lowLevelKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
144146
assert.NoError(t, err)
145147
k := &ecdsaPrivateKey{lowLevelKey}
148+
pk, err := k.PublicKey()
149+
assert.NoError(t, err)
146150

147151
// Sign
148152
msg := []byte("Hello World")
@@ -154,6 +158,14 @@ func TestEcdsaSignerSign(t *testing.T) {
154158
valid, err := verifyECDSA(&lowLevelKey.PublicKey, sigma, msg, nil)
155159
assert.NoError(t, err)
156160
assert.True(t, valid)
161+
162+
valid, err = verifierPrivateKey.Verify(k, sigma, msg, nil)
163+
assert.NoError(t, err)
164+
assert.True(t, valid)
165+
166+
valid, err = verifierPublicKey.Verify(pk, sigma, msg, nil)
167+
assert.NoError(t, err)
168+
assert.True(t, valid)
157169
}
158170

159171
func TestEcdsaPrivateKey(t *testing.T) {

bccsp/sw/impl.go

+19-40
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,20 @@ func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.B
8585
signers[reflect.TypeOf(&ecdsaPrivateKey{})] = &ecdsaSigner{}
8686
signers[reflect.TypeOf(&rsaPrivateKey{})] = &rsaSigner{}
8787

88+
// Set the verifiers
89+
verifiers := make(map[reflect.Type]Verifier)
90+
verifiers[reflect.TypeOf(&ecdsaPrivateKey{})] = &ecdsaPrivateKeyVerifier{}
91+
verifiers[reflect.TypeOf(&ecdsaPublicKey{})] = &ecdsaPublicKeyKeyVerifier{}
92+
verifiers[reflect.TypeOf(&rsaPrivateKey{})] = &rsaPrivateKeyVerifier{}
93+
verifiers[reflect.TypeOf(&rsaPublicKey{})] = &rsaPublicKeyKeyVerifier{}
94+
8895
return &impl{
89-
conf,
90-
keyStore,
91-
encryptors,
92-
decryptors,
93-
signers}, nil
96+
conf: conf,
97+
ks: keyStore,
98+
encryptors: encryptors,
99+
decryptors: decryptors,
100+
signers: signers,
101+
verifiers: verifiers}, nil
94102
}
95103

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

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

696-
// Check key type
697-
switch k.(type) {
698-
case *ecdsaPrivateKey:
699-
return verifyECDSA(&(k.(*ecdsaPrivateKey).privKey.PublicKey), signature, digest, opts)
700-
case *ecdsaPublicKey:
701-
return verifyECDSA(k.(*ecdsaPublicKey).pubKey, signature, digest, opts)
702-
case *rsaPrivateKey:
703-
if opts == nil {
704-
return false, errors.New("Invalid options. It must not be nil.")
705-
}
706-
switch opts.(type) {
707-
case *rsa.PSSOptions:
708-
err := rsa.VerifyPSS(&(k.(*rsaPrivateKey).privKey.PublicKey),
709-
(opts.(*rsa.PSSOptions)).Hash,
710-
digest, signature, opts.(*rsa.PSSOptions))
711-
712-
return err == nil, err
713-
default:
714-
return false, fmt.Errorf("Opts type not recognized [%s]", opts)
715-
}
716-
case *rsaPublicKey:
717-
if opts == nil {
718-
return false, errors.New("Invalid options. It must not be nil.")
719-
}
720-
switch opts.(type) {
721-
case *rsa.PSSOptions:
722-
err := rsa.VerifyPSS(k.(*rsaPublicKey).pubKey,
723-
(opts.(*rsa.PSSOptions)).Hash,
724-
digest, signature, opts.(*rsa.PSSOptions))
725-
726-
return err == nil, err
727-
default:
728-
return false, fmt.Errorf("Opts type not recognized [%s]", opts)
729-
}
730-
default:
705+
verifier, found := csp.verifiers[reflect.TypeOf(k)]
706+
if !found {
731707
return false, fmt.Errorf("Unsupported 'VerifyKey' provided [%v]", k)
732708
}
709+
710+
return verifier.Verify(k, signature, digest, opts)
711+
733712
}
734713

735714
// Encrypt encrypts plaintext using key k.

bccsp/sw/internals.go

+8
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,11 @@ type Signer interface {
4545
// the hash (as digest).
4646
Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error)
4747
}
48+
49+
// Verifier is a BCCSP-like interface that provides verifying algorithms
50+
type Verifier interface {
51+
52+
// Verify verifies signature against key k and digest
53+
// The opts argument should be appropriate for the algorithm used.
54+
Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error)
55+
}

bccsp/sw/mocks/mocks.go

+27
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,30 @@ func (s *Signer) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signat
6868

6969
return s.Value, s.Err
7070
}
71+
72+
type Verifier struct {
73+
KeyArg bccsp.Key
74+
SignatureArg []byte
75+
DigestArg []byte
76+
OptsArg bccsp.SignerOpts
77+
78+
Value bool
79+
Err error
80+
}
81+
82+
func (s *Verifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
83+
if !reflect.DeepEqual(s.KeyArg, k) {
84+
return false, errors.New("invalid key")
85+
}
86+
if !reflect.DeepEqual(s.SignatureArg, signature) {
87+
return false, errors.New("invalid signature")
88+
}
89+
if !reflect.DeepEqual(s.DigestArg, digest) {
90+
return false, errors.New("invalid digest")
91+
}
92+
if !reflect.DeepEqual(s.OptsArg, opts) {
93+
return false, errors.New("invalid opts")
94+
}
95+
96+
return s.Value, s.Err
97+
}

bccsp/sw/rsa.go

+38
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package sw
1818

1919
import (
2020
"crypto/rand"
21+
"crypto/rsa"
2122
"errors"
23+
"fmt"
2224

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

3335
return k.(*rsaPrivateKey).privKey.Sign(rand.Reader, digest, opts)
3436
}
37+
38+
type rsaPrivateKeyVerifier struct{}
39+
40+
func (v *rsaPrivateKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
41+
if opts == nil {
42+
return false, errors.New("Invalid options. It must not be nil.")
43+
}
44+
switch opts.(type) {
45+
case *rsa.PSSOptions:
46+
err := rsa.VerifyPSS(&(k.(*rsaPrivateKey).privKey.PublicKey),
47+
(opts.(*rsa.PSSOptions)).Hash,
48+
digest, signature, opts.(*rsa.PSSOptions))
49+
50+
return err == nil, err
51+
default:
52+
return false, fmt.Errorf("Opts type not recognized [%s]", opts)
53+
}
54+
}
55+
56+
type rsaPublicKeyKeyVerifier struct{}
57+
58+
func (v *rsaPublicKeyKeyVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
59+
if opts == nil {
60+
return false, errors.New("Invalid options. It must not be nil.")
61+
}
62+
switch opts.(type) {
63+
case *rsa.PSSOptions:
64+
err := rsa.VerifyPSS(k.(*rsaPublicKey).pubKey,
65+
(opts.(*rsa.PSSOptions)).Hash,
66+
digest, signature, opts.(*rsa.PSSOptions))
67+
68+
return err == nil, err
69+
default:
70+
return false, fmt.Errorf("Opts type not recognized [%s]", opts)
71+
}
72+
}

bccsp/sw/rsa_test.go

+45-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import (
2323
"crypto/sha256"
2424
"crypto/x509"
2525
"encoding/asn1"
26+
"strings"
2627
"testing"
2728

29+
"github.com/hyperledger/fabric/bccsp/mocks"
2830
"github.com/stretchr/testify/assert"
2931
)
3032

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

9395
func TestRSASignerSign(t *testing.T) {
9496
signer := &rsaSigner{}
97+
verifierPrivateKey := &rsaPrivateKeyVerifier{}
98+
verifierPublicKey := &rsaPublicKeyKeyVerifier{}
9599

96100
// Generate a key
97101
lowLevelKey, err := rsa.GenerateKey(rand.Reader, 1024)
98102
assert.NoError(t, err)
99103
k := &rsaPrivateKey{lowLevelKey}
104+
pk, err := k.PublicKey()
105+
assert.NoError(t, err)
100106

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

123+
opts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: crypto.SHA256}
117124
// Verify against msg, must fail
118-
err = rsa.VerifyPSS(&lowLevelKey.PublicKey, crypto.SHA256, msg, sigma, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: crypto.SHA256})
125+
err = rsa.VerifyPSS(&lowLevelKey.PublicKey, crypto.SHA256, msg, sigma, opts)
119126
assert.Error(t, err)
120127
assert.Contains(t, err.Error(), "crypto/rsa: verification error")
121128

122129
// Verify against digest, must succeed
123-
err = rsa.VerifyPSS(&lowLevelKey.PublicKey, crypto.SHA256, digest, sigma, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: crypto.SHA256})
130+
err = rsa.VerifyPSS(&lowLevelKey.PublicKey, crypto.SHA256, digest, sigma, opts)
131+
assert.NoError(t, err)
132+
133+
valid, err := verifierPrivateKey.Verify(k, sigma, msg, opts)
134+
assert.Error(t, err)
135+
assert.True(t, strings.Contains(err.Error(), "crypto/rsa: verification error"))
136+
137+
valid, err = verifierPrivateKey.Verify(k, sigma, digest, opts)
138+
assert.NoError(t, err)
139+
assert.True(t, valid)
140+
141+
valid, err = verifierPublicKey.Verify(pk, sigma, msg, opts)
142+
assert.Error(t, err)
143+
assert.True(t, strings.Contains(err.Error(), "crypto/rsa: verification error"))
144+
145+
valid, err = verifierPublicKey.Verify(pk, sigma, digest, opts)
124146
assert.NoError(t, err)
147+
assert.True(t, valid)
148+
}
149+
150+
func TestRSAVerifiersInvalidInputs(t *testing.T) {
151+
verifierPrivate := &rsaPrivateKeyVerifier{}
152+
_, err := verifierPrivate.Verify(nil, nil, nil, nil)
153+
assert.Error(t, err)
154+
assert.True(t, strings.Contains(err.Error(), "Invalid options. It must not be nil."))
155+
156+
_, err = verifierPrivate.Verify(nil, nil, nil, &mocks.SignerOpts{})
157+
assert.Error(t, err)
158+
assert.True(t, strings.Contains(err.Error(), "Opts type not recognized ["))
159+
160+
verifierPublic := &rsaPublicKeyKeyVerifier{}
161+
_, err = verifierPublic.Verify(nil, nil, nil, nil)
162+
assert.Error(t, err)
163+
assert.True(t, strings.Contains(err.Error(), "Invalid options. It must not be nil."))
164+
165+
_, err = verifierPublic.Verify(nil, nil, nil, &mocks.SignerOpts{})
166+
assert.Error(t, err)
167+
assert.True(t, strings.Contains(err.Error(), "Opts type not recognized ["))
125168
}

bccsp/sw/verify_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package sw
18+
19+
import (
20+
"errors"
21+
"reflect"
22+
"testing"
23+
24+
mocks2 "github.com/hyperledger/fabric/bccsp/mocks"
25+
"github.com/hyperledger/fabric/bccsp/sw/mocks"
26+
"github.com/stretchr/testify/assert"
27+
)
28+
29+
func TestVerify(t *testing.T) {
30+
expectedKey := &mocks2.MockKey{}
31+
expectetSignature := []byte{1, 2, 3, 4, 5}
32+
expectetDigest := []byte{1, 2, 3, 4}
33+
expectedOpts := &mocks2.SignerOpts{}
34+
expectetValue := true
35+
expectedErr := errors.New("no error")
36+
37+
verifiers := make(map[reflect.Type]Verifier)
38+
verifiers[reflect.TypeOf(&mocks2.MockKey{})] = &mocks.Verifier{
39+
KeyArg: expectedKey,
40+
SignatureArg: expectetSignature,
41+
DigestArg: expectetDigest,
42+
OptsArg: expectedOpts,
43+
Value: expectetValue,
44+
Err: expectedErr,
45+
}
46+
47+
csp := impl{verifiers: verifiers}
48+
49+
value, err := csp.Verify(expectedKey, expectetSignature, expectetDigest, expectedOpts)
50+
assert.Equal(t, expectetValue, value)
51+
assert.Equal(t, expectedErr, err)
52+
}

0 commit comments

Comments
 (0)