Skip to content

Commit b17e846

Browse files
committed
[FAB-3441] bccsp/sw ECDSA/RSA sign test coverage
Using the approach discussed in FAB-3465, this change-sets refactors the way ECDSA/RSA sign 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: I08d1b7b840384fc052f8912d7e331c2d28784a44 Signed-off-by: Angelo De Caro <[email protected]>
1 parent f3c61e6 commit b17e846

File tree

8 files changed

+496
-16
lines changed

8 files changed

+496
-16
lines changed

bccsp/sw/ecdsa.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func unmarshalECDSASignature(raw []byte) (*big.Int, *big.Int, error) {
7474
return sig.R, sig.S, nil
7575
}
7676

77-
func (csp *impl) signECDSA(k *ecdsa.PrivateKey, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) {
77+
func signECDSA(k *ecdsa.PrivateKey, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) {
7878
r, s, err := ecdsa.Sign(rand.Reader, k, digest)
7979
if err != nil {
8080
return nil, err
@@ -96,7 +96,7 @@ func (csp *impl) signECDSA(k *ecdsa.PrivateKey, digest []byte, opts bccsp.Signer
9696
return marshalECDSASignature(r, s)
9797
}
9898

99-
func (csp *impl) verifyECDSA(k *ecdsa.PublicKey, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
99+
func verifyECDSA(k *ecdsa.PublicKey, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
100100
r, s, err := unmarshalECDSASignature(signature)
101101
if err != nil {
102102
return false, fmt.Errorf("Failed unmashalling signature [%s]", err)
@@ -115,3 +115,9 @@ func (csp *impl) verifyECDSA(k *ecdsa.PublicKey, signature, digest []byte, opts
115115

116116
return ecdsa.Verify(k, digest, r, s), nil
117117
}
118+
119+
type ecdsaSigner struct{}
120+
121+
func (s *ecdsaSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) {
122+
return signECDSA(k.(*ecdsaPrivateKey).privKey, digest, opts)
123+
}

bccsp/sw/ecdsa_test.go

+226
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
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+
"crypto/ecdsa"
21+
"crypto/elliptic"
22+
"crypto/rand"
23+
"crypto/sha256"
24+
"crypto/x509"
25+
"math/big"
26+
"testing"
27+
28+
"github.com/stretchr/testify/assert"
29+
)
30+
31+
func TestUnmarshalECDSASignature(t *testing.T) {
32+
_, _, err := unmarshalECDSASignature(nil)
33+
assert.Error(t, err)
34+
assert.Contains(t, err.Error(), "Failed unmashalling signature [")
35+
36+
_, _, err = unmarshalECDSASignature([]byte{})
37+
assert.Error(t, err)
38+
assert.Contains(t, err.Error(), "Failed unmashalling signature [")
39+
40+
_, _, err = unmarshalECDSASignature([]byte{0})
41+
assert.Error(t, err)
42+
assert.Contains(t, err.Error(), "Failed unmashalling signature [")
43+
44+
sigma, err := marshalECDSASignature(big.NewInt(-1), big.NewInt(1))
45+
assert.NoError(t, err)
46+
_, _, err = unmarshalECDSASignature(sigma)
47+
assert.Error(t, err)
48+
assert.Contains(t, err.Error(), "Invalid signature. R must be larger than zero")
49+
50+
sigma, err = marshalECDSASignature(big.NewInt(0), big.NewInt(1))
51+
assert.NoError(t, err)
52+
_, _, err = unmarshalECDSASignature(sigma)
53+
assert.Error(t, err)
54+
assert.Contains(t, err.Error(), "Invalid signature. R must be larger than zero")
55+
56+
sigma, err = marshalECDSASignature(big.NewInt(1), big.NewInt(0))
57+
assert.NoError(t, err)
58+
_, _, err = unmarshalECDSASignature(sigma)
59+
assert.Error(t, err)
60+
assert.Contains(t, err.Error(), "Invalid signature. S must be larger than zero")
61+
62+
sigma, err = marshalECDSASignature(big.NewInt(1), big.NewInt(-1))
63+
assert.NoError(t, err)
64+
_, _, err = unmarshalECDSASignature(sigma)
65+
assert.Error(t, err)
66+
assert.Contains(t, err.Error(), "Invalid signature. S must be larger than zero")
67+
68+
sigma, err = marshalECDSASignature(big.NewInt(1), big.NewInt(1))
69+
assert.NoError(t, err)
70+
R, S, err := unmarshalECDSASignature(sigma)
71+
assert.NoError(t, err)
72+
assert.Equal(t, big.NewInt(1), R)
73+
assert.Equal(t, big.NewInt(1), S)
74+
}
75+
76+
func TestSignECDSA(t *testing.T) {
77+
// Generate a key
78+
lowLevelKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
79+
assert.NoError(t, err)
80+
81+
// Induce an error on the underlying ecdsa algorithm
82+
msg := []byte("hello world")
83+
oldN := lowLevelKey.Params().N
84+
defer func() { lowLevelKey.Params().N = oldN }()
85+
lowLevelKey.Params().N = big.NewInt(0)
86+
_, err = signECDSA(lowLevelKey, msg, nil)
87+
assert.Error(t, err)
88+
assert.Contains(t, err.Error(), "zero parameter")
89+
lowLevelKey.Params().N = oldN
90+
91+
oldCurveHalfOrders := curveHalfOrders
92+
curveHalfOrders = nil
93+
defer func() { curveHalfOrders = oldCurveHalfOrders }()
94+
_, err = signECDSA(lowLevelKey, msg, nil)
95+
assert.Error(t, err)
96+
assert.Contains(t, err.Error(), "Curve not recognized [")
97+
curveHalfOrders = oldCurveHalfOrders
98+
}
99+
100+
func TestVerifyECDSA(t *testing.T) {
101+
// Generate a key
102+
lowLevelKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
103+
assert.NoError(t, err)
104+
105+
msg := []byte("hello world")
106+
sigma, err := signECDSA(lowLevelKey, msg, nil)
107+
assert.NoError(t, err)
108+
109+
valid, err := verifyECDSA(&lowLevelKey.PublicKey, sigma, msg, nil)
110+
assert.NoError(t, err)
111+
assert.True(t, valid)
112+
113+
_, err = verifyECDSA(&lowLevelKey.PublicKey, nil, msg, nil)
114+
assert.Error(t, err)
115+
assert.Contains(t, err.Error(), "Failed unmashalling signature [")
116+
117+
oldCurveHalfOrders := curveHalfOrders
118+
curveHalfOrders = nil
119+
defer func() { curveHalfOrders = oldCurveHalfOrders }()
120+
_, err = verifyECDSA(&lowLevelKey.PublicKey, sigma, msg, nil)
121+
assert.Error(t, err)
122+
assert.Contains(t, err.Error(), "Curve not recognized [")
123+
curveHalfOrders = oldCurveHalfOrders
124+
125+
_, err = verifyECDSA(&lowLevelKey.PublicKey, nil, msg, nil)
126+
assert.Error(t, err)
127+
assert.Contains(t, err.Error(), "Failed unmashalling signature [")
128+
129+
R, S, err := unmarshalECDSASignature(sigma)
130+
assert.NoError(t, err)
131+
S.Add(curveHalfOrders[elliptic.P256()], big.NewInt(1))
132+
sigmaWrongS, err := marshalECDSASignature(R, S)
133+
assert.NoError(t, err)
134+
_, err = verifyECDSA(&lowLevelKey.PublicKey, sigmaWrongS, msg, nil)
135+
assert.Error(t, err)
136+
assert.Contains(t, err.Error(), "Invalid S. Must be smaller than half the order [")
137+
}
138+
139+
func TestEcdsaSignerSign(t *testing.T) {
140+
signer := &ecdsaSigner{}
141+
142+
// Generate a key
143+
lowLevelKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
144+
assert.NoError(t, err)
145+
k := &ecdsaPrivateKey{lowLevelKey}
146+
147+
// Sign
148+
msg := []byte("Hello World")
149+
sigma, err := signer.Sign(k, msg, nil)
150+
assert.NoError(t, err)
151+
assert.NotNil(t, sigma)
152+
153+
// Verify
154+
valid, err := verifyECDSA(&lowLevelKey.PublicKey, sigma, msg, nil)
155+
assert.NoError(t, err)
156+
assert.True(t, valid)
157+
}
158+
159+
func TestEcdsaPrivateKey(t *testing.T) {
160+
lowLevelKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
161+
assert.NoError(t, err)
162+
k := &ecdsaPrivateKey{lowLevelKey}
163+
164+
assert.False(t, k.Symmetric())
165+
assert.True(t, k.Private())
166+
167+
_, err = k.Bytes()
168+
assert.Error(t, err)
169+
assert.Contains(t, err.Error(), "Not supported.")
170+
171+
k.privKey = nil
172+
ski := k.SKI()
173+
assert.Nil(t, ski)
174+
175+
k.privKey = lowLevelKey
176+
ski = k.SKI()
177+
raw := elliptic.Marshal(k.privKey.Curve, k.privKey.PublicKey.X, k.privKey.PublicKey.Y)
178+
hash := sha256.New()
179+
hash.Write(raw)
180+
ski2 := hash.Sum(nil)
181+
assert.Equal(t, ski2, ski, "SKI is not computed in the right way.")
182+
183+
pk, err := k.PublicKey()
184+
assert.NoError(t, err)
185+
assert.NotNil(t, pk)
186+
ecdsaPK, ok := pk.(*ecdsaPublicKey)
187+
assert.True(t, ok)
188+
assert.Equal(t, &lowLevelKey.PublicKey, ecdsaPK.pubKey)
189+
}
190+
191+
func TestEcdsaPublicKey(t *testing.T) {
192+
lowLevelKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
193+
assert.NoError(t, err)
194+
k := &ecdsaPublicKey{&lowLevelKey.PublicKey}
195+
196+
assert.False(t, k.Symmetric())
197+
assert.False(t, k.Private())
198+
199+
k.pubKey = nil
200+
ski := k.SKI()
201+
assert.Nil(t, ski)
202+
203+
k.pubKey = &lowLevelKey.PublicKey
204+
ski = k.SKI()
205+
raw := elliptic.Marshal(k.pubKey.Curve, k.pubKey.X, k.pubKey.Y)
206+
hash := sha256.New()
207+
hash.Write(raw)
208+
ski2 := hash.Sum(nil)
209+
assert.Equal(t, ski, ski2, "SKI is not computed in the right way.")
210+
211+
pk, err := k.PublicKey()
212+
assert.NoError(t, err)
213+
assert.Equal(t, k, pk)
214+
215+
bytes, err := k.Bytes()
216+
assert.NoError(t, err)
217+
bytes2, err := x509.MarshalPKIXPublicKey(k.pubKey)
218+
assert.Equal(t, bytes2, bytes, "bytes are not computed in the right way.")
219+
220+
invalidCurve := &elliptic.CurveParams{Name: "P-Invalid"}
221+
invalidCurve.BitSize = 1024
222+
k.pubKey = &ecdsa.PublicKey{Curve: invalidCurve, X: big.NewInt(1), Y: big.NewInt(1)}
223+
_, err = k.Bytes()
224+
assert.Error(t, err)
225+
assert.Contains(t, err.Error(), "Failed marshalling key [")
226+
}

bccsp/sw/impl.go

+18-14
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,17 @@ func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.B
8080
decryptors := make(map[reflect.Type]Decryptor)
8181
decryptors[reflect.TypeOf(&aesPrivateKey{})] = &aescbcpkcs7Decryptor{}
8282

83-
return &impl{conf, keyStore, encryptors, decryptors}, nil
83+
// Set the signers
84+
signers := make(map[reflect.Type]Signer)
85+
signers[reflect.TypeOf(&ecdsaPrivateKey{})] = &ecdsaSigner{}
86+
signers[reflect.TypeOf(&rsaPrivateKey{})] = &rsaSigner{}
87+
88+
return &impl{
89+
conf,
90+
keyStore,
91+
encryptors,
92+
decryptors,
93+
signers}, nil
8494
}
8595

8696
// SoftwareBasedBCCSP is the software-based implementation of the BCCSP.
@@ -90,6 +100,7 @@ type impl struct {
90100

91101
encryptors map[reflect.Type]Encryptor
92102
decryptors map[reflect.Type]Decryptor
103+
signers map[reflect.Type]Signer
93104
}
94105

95106
// KeyGen generates a key using opts.
@@ -661,19 +672,12 @@ func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signat
661672
return nil, errors.New("Invalid digest. Cannot be empty.")
662673
}
663674

664-
// Check key type
665-
switch k.(type) {
666-
case *ecdsaPrivateKey:
667-
return csp.signECDSA(k.(*ecdsaPrivateKey).privKey, digest, opts)
668-
case *rsaPrivateKey:
669-
if opts == nil {
670-
return nil, errors.New("Invalid options. Nil.")
671-
}
672-
673-
return k.(*rsaPrivateKey).privKey.Sign(rand.Reader, digest, opts)
674-
default:
675+
signer, found := csp.signers[reflect.TypeOf(k)]
676+
if !found {
675677
return nil, fmt.Errorf("Unsupported 'SignKey' provided [%v]", k)
676678
}
679+
680+
return signer.Sign(k, digest, opts)
677681
}
678682

679683
// Verify verifies signature against key k and digest
@@ -692,9 +696,9 @@ func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.Signer
692696
// Check key type
693697
switch k.(type) {
694698
case *ecdsaPrivateKey:
695-
return csp.verifyECDSA(&(k.(*ecdsaPrivateKey).privKey.PublicKey), signature, digest, opts)
699+
return verifyECDSA(&(k.(*ecdsaPrivateKey).privKey.PublicKey), signature, digest, opts)
696700
case *ecdsaPublicKey:
697-
return csp.verifyECDSA(k.(*ecdsaPublicKey).pubKey, signature, digest, opts)
701+
return verifyECDSA(k.(*ecdsaPublicKey).pubKey, signature, digest, opts)
698702
case *rsaPrivateKey:
699703
if opts == nil {
700704
return false, errors.New("Invalid options. It must not be nil.")

bccsp/sw/internals.go

+12
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,15 @@ type Decryptor interface {
3333
// The opts argument should be appropriate for the algorithm used.
3434
Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) (plaintext []byte, err error)
3535
}
36+
37+
// Signer is a BCCSP-like interface that provides signing algorithms
38+
type Signer interface {
39+
40+
// Sign signs digest using key k.
41+
// The opts argument should be appropriate for the algorithm used.
42+
//
43+
// Note that when a signature of a hash of a larger message is needed,
44+
// the caller is responsible for hashing the larger message and passing
45+
// the hash (as digest).
46+
Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error)
47+
}

bccsp/sw/mocks/mocks.go

+23
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,26 @@ func (e *Encryptor) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterO
4545

4646
return e.EncValue, e.EncErr
4747
}
48+
49+
type Signer struct {
50+
KeyArg bccsp.Key
51+
DigestArg []byte
52+
OptsArg bccsp.SignerOpts
53+
54+
Value []byte
55+
Err error
56+
}
57+
58+
func (s *Signer) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) {
59+
if !reflect.DeepEqual(s.KeyArg, k) {
60+
return nil, errors.New("invalid key")
61+
}
62+
if !reflect.DeepEqual(s.DigestArg, digest) {
63+
return nil, errors.New("invalid digest")
64+
}
65+
if !reflect.DeepEqual(s.OptsArg, opts) {
66+
return nil, errors.New("invalid opts")
67+
}
68+
69+
return s.Value, s.Err
70+
}

bccsp/sw/rsa.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
"crypto/rand"
21+
"errors"
22+
23+
"github.com/hyperledger/fabric/bccsp"
24+
)
25+
26+
type rsaSigner struct{}
27+
28+
func (s *rsaSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) {
29+
if opts == nil {
30+
return nil, errors.New("Invalid options. Must be different from nil.")
31+
}
32+
33+
return k.(*rsaPrivateKey).privKey.Sign(rand.Reader, digest, opts)
34+
}

0 commit comments

Comments
 (0)