Skip to content

Commit

Permalink
BCCSP support for RSA signing
Browse files Browse the repository at this point in the history
This change-set introduces the BCCSP support for the RSA
signing algorithm with verification supporting PSS signatures only.
New tests have been added.
This change-set comes in the context of:
https://jira.hyperledger.org/browse/FAB-354

Change-Id: I54e21fe15561a9a024dfee8b12595dc36f828123
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
  • Loading branch information
adecaro committed Nov 9, 2016
1 parent 216ae65 commit 35af475
Show file tree
Hide file tree
Showing 9 changed files with 418 additions and 8 deletions.
2 changes: 1 addition & 1 deletion core/crypto/bccsp/bccsp.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ type BCCSP interface {

// Verify verifies signature against key k and digest
// The opts argument should be appropriate for the algorithm used.
Verify(k Key, signature, digest []byte) (valid bool, err error)
Verify(k Key, signature, digest []byte, opts SignerOpts) (valid bool, err error)

// Encrypt encrypts plaintext using key k.
// The opts argument should be appropriate for the algorithm used.
Expand Down
27 changes: 24 additions & 3 deletions core/crypto/bccsp/bccsp_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,24 @@ limitations under the License.
package bccsp

const (
// ECDSA Elliptic Curve Digital Signature Algorithm (key gen, import, sign, veirfy).
// ECDSA Elliptic Curve Digital Signature Algorithm (key gen, import, sign, verify),
// at default security level (see primitives package).
ECDSA = "ECDSA"
// ECDSAReRand ECDSA key re-randomization
ECDSAReRand = "ECDSA_RERAND"

// AES Advanced Encryption Standard
// RSA at default security level (see primitives package)
RSA = "RSA"

// AES Advanced Encryption Standard at default security level (see primitives package)
AES = "AES"

// HMAC keyed-hash message authentication code
HMAC = "HMAC"
// HMACTruncated256 HMAC truncated at 256 bits.
HMACTruncated256 = "HMAC_TRUNCATED_256"

// SHA Secure Hash Algorithm
// SHA Secure Hash Algorithm using default family (see primitives package)
SHA = "SHA"
)

Expand Down Expand Up @@ -185,3 +189,20 @@ type SHAOpts struct {
func (opts *SHAOpts) Algorithm() string {
return SHA
}

// RSAKeyGenOpts contains options for RSA key generation.
type RSAKeyGenOpts struct {
Temporary bool
}

// Algorithm returns an identifier for the algorithm to be used
// to generate a key.
func (opts *RSAKeyGenOpts) Algorithm() string {
return RSA
}

// Ephemeral returns true if the key to generate has to be ephemeral,
// false otherwise.
func (opts *RSAKeyGenOpts) Ephemeral() bool {
return opts.Temporary
}
2 changes: 1 addition & 1 deletion core/crypto/bccsp/signer/signer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestSign(t *testing.T) {
t.Fatalf("Failed generating ECDSA signature [%s]", err)
}

valid, err := csp.Verify(k, signature, primitives.Hash(msg))
valid, err := csp.Verify(k, signature, primitives.Hash(msg), nil)
if err != nil {
t.Fatalf("Failed verifying ECDSA signature [%s]", err)
}
Expand Down
45 changes: 44 additions & 1 deletion core/crypto/bccsp/sw/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"fmt"
"math/big"

"crypto/rsa"

"github.com/hyperledger/fabric/core/crypto/bccsp"
"github.com/hyperledger/fabric/core/crypto/primitives"
"github.com/hyperledger/fabric/core/crypto/utils"
Expand Down Expand Up @@ -101,6 +103,25 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) {
}
}

return k, nil
case bccsp.RSA:
lowLevelKey, err := primitives.NewRSAKey()

if err != nil {
return nil, fmt.Errorf("Failed generating RSA (2048) key [%s]", err)
}

k = &rsaPrivateKey{lowLevelKey}

// If the key is not Ephemeral, store it.
if !opts.Ephemeral() {
// Store the key
err = csp.ks.storePrivateKey(hex.EncodeToString(k.SKI()), lowLevelKey)
if err != nil {
return nil, fmt.Errorf("Failed storing AES key [%s]", err)
}
}

return k, nil
default:
return nil, fmt.Errorf("Algorithm not recognized [%s]", opts.Algorithm())
Expand Down Expand Up @@ -309,6 +330,8 @@ func (csp *impl) GetKey(ski []byte) (k bccsp.Key, err error) {
switch key.(type) {
case *ecdsa.PrivateKey:
return &ecdsaPrivateKey{key.(*ecdsa.PrivateKey)}, nil
case *rsa.PrivateKey:
return &rsaPrivateKey{key.(*rsa.PrivateKey)}, nil
default:
return nil, errors.New("Key type not recognized")
}
Expand Down Expand Up @@ -350,13 +373,19 @@ func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signat
switch k.(type) {
case *ecdsaPrivateKey:
return k.(*ecdsaPrivateKey).k.Sign(rand.Reader, digest, nil)
case *rsaPrivateKey:
if opts == nil {
return nil, errors.New("Invalid options. Nil.")
}

return k.(*rsaPrivateKey).k.Sign(rand.Reader, digest, opts)
default:
return nil, fmt.Errorf("Key type not recognized [%s]", k)
}
}

// Verify verifies signature against key k and digest
func (csp *impl) Verify(k bccsp.Key, signature, digest []byte) (valid bool, err error) {
func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) {
// Validate arguments
if k == nil {
return false, errors.New("Invalid Key. It must not be nil.")
Expand All @@ -378,6 +407,20 @@ func (csp *impl) Verify(k bccsp.Key, signature, digest []byte) (valid bool, err
}

return ecdsa.Verify(&(k.(*ecdsaPrivateKey).k.PublicKey), digest, ecdsaSignature.R, ecdsaSignature.S), nil
case *rsaPrivateKey:
if opts == nil {
return false, errors.New("Invalid options. Nil.")
}
switch opts.(type) {
case *rsa.PSSOptions:
err := rsa.VerifyPSS(&(k.(*rsaPrivateKey).k.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)
}
default:
return false, fmt.Errorf("Key type not recognized [%s]", k)
}
Expand Down
202 changes: 200 additions & 2 deletions core/crypto/bccsp/sw/impl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
"os"
"testing"

"crypto"
"crypto/rsa"

"github.com/hyperledger/fabric/core/crypto/bccsp"
"github.com/hyperledger/fabric/core/crypto/primitives"
"github.com/spf13/viper"
Expand Down Expand Up @@ -253,7 +256,7 @@ func TestECDSAVerify(t *testing.T) {
t.Fatalf("Failed generating ECDSA signature [%s]", err)
}

valid, err := csp.Verify(k, signature, digest)
valid, err := csp.Verify(k, signature, digest, nil)
if err != nil {
t.Fatalf("Failed verifying ECDSA signature [%s]", err)
}
Expand Down Expand Up @@ -287,7 +290,7 @@ func TestECDSAKeyDeriv(t *testing.T) {
t.Fatalf("Failed generating ECDSA signature [%s]", err)
}

valid, err := csp.Verify(reRandomizedKey, signature, digest)
valid, err := csp.Verify(reRandomizedKey, signature, digest, nil)
if err != nil {
t.Fatalf("Failed verifying ECDSA signature [%s]", err)
}
Expand Down Expand Up @@ -553,3 +556,198 @@ func TestSHA(t *testing.T) {
}
}
}

func TestRSAKeyGenEphemeral(t *testing.T) {
csp := getBCCSP(t)

k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true})
if err != nil {
t.Fatalf("Failed generating RSA key [%s]", err)
}
if k == nil {
t.Fatal("Failed generating RSA key. Key must be different from nil")
}
if !k.Private() {
t.Fatal("Failed generating RSA key. Key should be private")
}
if k.Symmetric() {
t.Fatal("Failed generating RSA key. Key should be asymmetric")
}
}

func TestRSAPrivateKeySKI(t *testing.T) {
csp := getBCCSP(t)

k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true})
if err != nil {
t.Fatalf("Failed generating RSA key [%s]", err)
}

ski := k.SKI()
if len(ski) == 0 {
t.Fatal("SKI not valid. Zero length.")
}
}

func TestRSAKeyGenNonEphemeral(t *testing.T) {
csp := getBCCSP(t)

k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false})
if err != nil {
t.Fatalf("Failed generating RSA key [%s]", err)
}
if k == nil {
t.Fatal("Failed generating RSA key. Key must be different from nil")
}
if !k.Private() {
t.Fatal("Failed generating RSA key. Key should be private")
}
if k.Symmetric() {
t.Fatal("Failed generating RSA key. Key should be asymmetric")
}
}

func TestRSAGetKeyBySKI(t *testing.T) {
csp := getBCCSP(t)

k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false})
if err != nil {
t.Fatalf("Failed generating RSA key [%s]", err)
}

k2, err := csp.GetKey(k.SKI())
if err != nil {
t.Fatalf("Failed getting RSA key [%s]", err)
}
if k2 == nil {
t.Fatal("Failed getting RSA key. Key must be different from nil")
}
if !k2.Private() {
t.Fatal("Failed getting RSA key. Key should be private")
}
if k2.Symmetric() {
t.Fatal("Failed getting RSA key. Key should be asymmetric")
}

// Check that the SKIs are the same
if !bytes.Equal(k.SKI(), k2.SKI()) {
t.Fatalf("SKIs are different [%x]!=[%x]", k.SKI(), k2.SKI())
}
}

func TestRSAPublicKeyFromPrivateKey(t *testing.T) {
csp := getBCCSP(t)

k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true})
if err != nil {
t.Fatalf("Failed generating RSA key [%s]", err)
}

pk, err := k.PublicKey()
if err != nil {
t.Fatalf("Failed getting public key from private RSA key [%s]", err)
}
if pk == nil {
t.Fatal("Failed getting public key from private RSA key. Key must be different from nil")
}
if pk.Private() {
t.Fatal("Failed generating RSA key. Key should be public")
}
if pk.Symmetric() {
t.Fatal("Failed generating RSA key. Key should be asymmetric")
}
}

func TestRSAPublicKeyBytes(t *testing.T) {
csp := getBCCSP(t)

k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true})
if err != nil {
t.Fatalf("Failed generating RSA key [%s]", err)
}

pk, err := k.PublicKey()
if err != nil {
t.Fatalf("Failed getting public key from private RSA key [%s]", err)
}

raw, err := pk.Bytes()
if err != nil {
t.Fatalf("Failed marshalling RSA public key [%s]", err)
}
if len(raw) == 0 {
t.Fatal("Failed marshalling RSA public key. Zero length")
}
}

func TestRSAPublicKeySKI(t *testing.T) {
csp := getBCCSP(t)

k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true})
if err != nil {
t.Fatalf("Failed generating RSA key [%s]", err)
}

pk, err := k.PublicKey()
if err != nil {
t.Fatalf("Failed getting public key from private RSA key [%s]", err)
}

ski := pk.SKI()
if len(ski) == 0 {
t.Fatal("SKI not valid. Zero length.")
}
}

func TestRSASign(t *testing.T) {
csp := getBCCSP(t)

k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true})
if err != nil {
t.Fatalf("Failed generating RSA key [%s]", err)
}

msg := []byte("Hello World")

digest, err := csp.Hash(msg, &bccsp.SHAOpts{})
if err != nil {
t.Fatalf("Failed computing HASH [%s]", err)
}

signature, err := csp.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256})
if err != nil {
t.Fatalf("Failed generating RSA signature [%s]", err)
}
if len(signature) == 0 {
t.Fatal("Failed generating RSA key. Signature must be different from nil")
}
}

func TestRSAVerify(t *testing.T) {
csp := getBCCSP(t)

k, err := csp.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true})
if err != nil {
t.Fatalf("Failed generating RSA key [%s]", err)
}

msg := []byte("Hello World")

digest, err := csp.Hash(msg, &bccsp.SHAOpts{})
if err != nil {
t.Fatalf("Failed computing HASH [%s]", err)
}

signature, err := csp.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256})
if err != nil {
t.Fatalf("Failed generating RSA signature [%s]", err)
}

valid, err := csp.Verify(k, signature, digest, &rsa.PSSOptions{SaltLength: 32, Hash: crypto.SHA256})
if err != nil {
t.Fatalf("Failed verifying RSA signature [%s]", err)
}
if !valid {
t.Fatal("Failed verifying RSA signature. Signature not valid.")
}
}
Loading

0 comments on commit 35af475

Please sign in to comment.