github.com/kaituanwang/hyperledger@v2.0.1+incompatible/bccsp/pkcs11/impl.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package pkcs11
     8  
     9  import (
    10  	"crypto/ecdsa"
    11  	"crypto/x509"
    12  	"os"
    13  
    14  	"github.com/hyperledger/fabric/bccsp"
    15  	"github.com/hyperledger/fabric/bccsp/sw"
    16  	"github.com/hyperledger/fabric/common/flogging"
    17  	"github.com/miekg/pkcs11"
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  var (
    22  	logger           = flogging.MustGetLogger("bccsp_p11")
    23  	sessionCacheSize = 10
    24  )
    25  
    26  // New WithParams returns a new instance of the software-based BCCSP
    27  // set at the passed security level, hash family and KeyStore.
    28  func New(opts PKCS11Opts, keyStore bccsp.KeyStore) (bccsp.BCCSP, error) {
    29  	// Init config
    30  	conf := &config{}
    31  	err := conf.setSecurityLevel(opts.SecLevel, opts.HashFamily)
    32  	if err != nil {
    33  		return nil, errors.Wrapf(err, "Failed initializing configuration")
    34  	}
    35  
    36  	// Check KeyStore
    37  	if keyStore == nil {
    38  		return nil, errors.New("Invalid bccsp.KeyStore instance. It must be different from nil")
    39  	}
    40  
    41  	swCSP, err := sw.NewWithParams(opts.SecLevel, opts.HashFamily, keyStore)
    42  	if err != nil {
    43  		return nil, errors.Wrapf(err, "Failed initializing fallback SW BCCSP")
    44  	}
    45  
    46  	lib := opts.Library
    47  	pin := opts.Pin
    48  	label := opts.Label
    49  	ctx, slot, session, err := loadLib(lib, pin, label)
    50  	if err != nil {
    51  		return nil, errors.Wrapf(err, "Failed initializing PKCS11 library %s %s",
    52  			lib, label)
    53  	}
    54  
    55  	sessions := make(chan pkcs11.SessionHandle, sessionCacheSize)
    56  	csp := &impl{swCSP, conf, ctx, sessions, slot, lib, opts.SoftVerify, opts.Immutable}
    57  	csp.returnSession(*session)
    58  	return csp, nil
    59  }
    60  
    61  type impl struct {
    62  	bccsp.BCCSP
    63  
    64  	conf *config
    65  
    66  	ctx      *pkcs11.Ctx
    67  	sessions chan pkcs11.SessionHandle
    68  	slot     uint
    69  
    70  	lib        string
    71  	softVerify bool
    72  	//Immutable flag makes object immutable
    73  	immutable bool
    74  }
    75  
    76  // KeyGen generates a key using opts.
    77  func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) {
    78  	// Validate arguments
    79  	if opts == nil {
    80  		return nil, errors.New("Invalid Opts parameter. It must not be nil")
    81  	}
    82  
    83  	// Parse algorithm
    84  	switch opts.(type) {
    85  	case *bccsp.ECDSAKeyGenOpts:
    86  		ski, pub, err := csp.generateECKey(csp.conf.ellipticCurve, opts.Ephemeral())
    87  		if err != nil {
    88  			return nil, errors.Wrapf(err, "Failed generating ECDSA key")
    89  		}
    90  		k = &ecdsaPrivateKey{ski, ecdsaPublicKey{ski, pub}}
    91  
    92  	case *bccsp.ECDSAP256KeyGenOpts:
    93  		ski, pub, err := csp.generateECKey(oidNamedCurveP256, opts.Ephemeral())
    94  		if err != nil {
    95  			return nil, errors.Wrapf(err, "Failed generating ECDSA P256 key")
    96  		}
    97  
    98  		k = &ecdsaPrivateKey{ski, ecdsaPublicKey{ski, pub}}
    99  
   100  	case *bccsp.ECDSAP384KeyGenOpts:
   101  		ski, pub, err := csp.generateECKey(oidNamedCurveP384, opts.Ephemeral())
   102  		if err != nil {
   103  			return nil, errors.Wrapf(err, "Failed generating ECDSA P384 key")
   104  		}
   105  
   106  		k = &ecdsaPrivateKey{ski, ecdsaPublicKey{ski, pub}}
   107  
   108  	default:
   109  		return csp.BCCSP.KeyGen(opts)
   110  	}
   111  
   112  	return k, nil
   113  }
   114  
   115  // KeyImport imports a key from its raw representation using opts.
   116  // The opts argument should be appropriate for the primitive used.
   117  func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) {
   118  	// Validate arguments
   119  	if raw == nil {
   120  		return nil, errors.New("Invalid raw. Cannot be nil")
   121  	}
   122  
   123  	if opts == nil {
   124  		return nil, errors.New("Invalid Opts parameter. It must not be nil")
   125  	}
   126  
   127  	switch opts.(type) {
   128  
   129  	case *bccsp.X509PublicKeyImportOpts:
   130  		x509Cert, ok := raw.(*x509.Certificate)
   131  		if !ok {
   132  			return nil, errors.New("[X509PublicKeyImportOpts] Invalid raw material. Expected *x509.Certificate")
   133  		}
   134  
   135  		pk := x509Cert.PublicKey
   136  
   137  		switch pk.(type) {
   138  		case *ecdsa.PublicKey:
   139  			return csp.KeyImport(pk, &bccsp.ECDSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral()})
   140  		default:
   141  			return nil, errors.New("Certificate's public key type not recognized. Supported keys: [ECDSA]")
   142  		}
   143  
   144  	default:
   145  		return csp.BCCSP.KeyImport(raw, opts)
   146  
   147  	}
   148  }
   149  
   150  // GetKey returns the key this CSP associates to
   151  // the Subject Key Identifier ski.
   152  func (csp *impl) GetKey(ski []byte) (bccsp.Key, error) {
   153  	pubKey, isPriv, err := csp.getECKey(ski)
   154  	if err == nil {
   155  		if isPriv {
   156  			return &ecdsaPrivateKey{ski, ecdsaPublicKey{ski, pubKey}}, nil
   157  		}
   158  		return &ecdsaPublicKey{ski, pubKey}, nil
   159  	}
   160  	return csp.BCCSP.GetKey(ski)
   161  }
   162  
   163  // Sign signs digest using key k.
   164  // The opts argument should be appropriate for the primitive used.
   165  //
   166  // Note that when a signature of a hash of a larger message is needed,
   167  // the caller is responsible for hashing the larger message and passing
   168  // the hash (as digest).
   169  func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) ([]byte, error) {
   170  	// Validate arguments
   171  	if k == nil {
   172  		return nil, errors.New("Invalid Key. It must not be nil")
   173  	}
   174  	if len(digest) == 0 {
   175  		return nil, errors.New("Invalid digest. Cannot be empty")
   176  	}
   177  
   178  	// Check key type
   179  	switch key := k.(type) {
   180  	case *ecdsaPrivateKey:
   181  		return csp.signECDSA(*key, digest, opts)
   182  	default:
   183  		return csp.BCCSP.Sign(key, digest, opts)
   184  	}
   185  }
   186  
   187  // Verify verifies signature against key k and digest
   188  func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) {
   189  	// Validate arguments
   190  	if k == nil {
   191  		return false, errors.New("Invalid Key. It must not be nil")
   192  	}
   193  	if len(signature) == 0 {
   194  		return false, errors.New("Invalid signature. Cannot be empty")
   195  	}
   196  	if len(digest) == 0 {
   197  		return false, errors.New("Invalid digest. Cannot be empty")
   198  	}
   199  
   200  	// Check key type
   201  	switch key := k.(type) {
   202  	case *ecdsaPrivateKey:
   203  		return csp.verifyECDSA(key.pub, signature, digest, opts)
   204  	case *ecdsaPublicKey:
   205  		return csp.verifyECDSA(*key, signature, digest, opts)
   206  	default:
   207  		return csp.BCCSP.Verify(k, signature, digest, opts)
   208  	}
   209  }
   210  
   211  // Encrypt encrypts plaintext using key k.
   212  // The opts argument should be appropriate for the primitive used.
   213  func (csp *impl) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts) ([]byte, error) {
   214  	// TODO: Add PKCS11 support for encryption, when fabric starts requiring it
   215  	return csp.BCCSP.Encrypt(k, plaintext, opts)
   216  }
   217  
   218  // Decrypt decrypts ciphertext using key k.
   219  // The opts argument should be appropriate for the primitive used.
   220  func (csp *impl) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) ([]byte, error) {
   221  	return csp.BCCSP.Decrypt(k, ciphertext, opts)
   222  }
   223  
   224  // FindPKCS11Lib IS ONLY USED FOR TESTING
   225  // This is a convenience function. Useful to self-configure, for tests where usual configuration is not
   226  // available
   227  func FindPKCS11Lib() (lib, pin, label string) {
   228  	//FIXME: Till we workout the configuration piece, look for the libraries in the familiar places
   229  	lib = os.Getenv("PKCS11_LIB")
   230  	if lib == "" {
   231  		pin = "98765432"
   232  		label = "ForFabric"
   233  		possibilities := []string{
   234  			"/usr/lib/softhsm/libsofthsm2.so",                            //Debian
   235  			"/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so",           //Ubuntu
   236  			"/usr/lib/s390x-linux-gnu/softhsm/libsofthsm2.so",            //Ubuntu
   237  			"/usr/lib/powerpc64le-linux-gnu/softhsm/libsofthsm2.so",      //Power
   238  			"/usr/local/Cellar/softhsm/2.5.0/lib/softhsm/libsofthsm2.so", //MacOS
   239  		}
   240  		for _, path := range possibilities {
   241  			if _, err := os.Stat(path); !os.IsNotExist(err) {
   242  				lib = path
   243  				break
   244  			}
   245  		}
   246  	} else {
   247  		pin = os.Getenv("PKCS11_PIN")
   248  		label = os.Getenv("PKCS11_LABEL")
   249  	}
   250  	return lib, pin, label
   251  }