github.com/true-sqn/fabric@v2.1.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, pin, 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  	pin      string
    70  
    71  	lib        string
    72  	softVerify bool
    73  	//Immutable flag makes object immutable
    74  	immutable bool
    75  }
    76  
    77  // KeyGen generates a key using opts.
    78  func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) {
    79  	// Validate arguments
    80  	if opts == nil {
    81  		return nil, errors.New("Invalid Opts parameter. It must not be nil")
    82  	}
    83  
    84  	// Parse algorithm
    85  	switch opts.(type) {
    86  	case *bccsp.ECDSAKeyGenOpts:
    87  		ski, pub, err := csp.generateECKey(csp.conf.ellipticCurve, opts.Ephemeral())
    88  		if err != nil {
    89  			return nil, errors.Wrapf(err, "Failed generating ECDSA key")
    90  		}
    91  		k = &ecdsaPrivateKey{ski, ecdsaPublicKey{ski, pub}}
    92  
    93  	case *bccsp.ECDSAP256KeyGenOpts:
    94  		ski, pub, err := csp.generateECKey(oidNamedCurveP256, opts.Ephemeral())
    95  		if err != nil {
    96  			return nil, errors.Wrapf(err, "Failed generating ECDSA P256 key")
    97  		}
    98  
    99  		k = &ecdsaPrivateKey{ski, ecdsaPublicKey{ski, pub}}
   100  
   101  	case *bccsp.ECDSAP384KeyGenOpts:
   102  		ski, pub, err := csp.generateECKey(oidNamedCurveP384, opts.Ephemeral())
   103  		if err != nil {
   104  			return nil, errors.Wrapf(err, "Failed generating ECDSA P384 key")
   105  		}
   106  
   107  		k = &ecdsaPrivateKey{ski, ecdsaPublicKey{ski, pub}}
   108  
   109  	default:
   110  		return csp.BCCSP.KeyGen(opts)
   111  	}
   112  
   113  	return k, nil
   114  }
   115  
   116  // KeyImport imports a key from its raw representation using opts.
   117  // The opts argument should be appropriate for the primitive used.
   118  func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) {
   119  	// Validate arguments
   120  	if raw == nil {
   121  		return nil, errors.New("Invalid raw. Cannot be nil")
   122  	}
   123  
   124  	if opts == nil {
   125  		return nil, errors.New("Invalid Opts parameter. It must not be nil")
   126  	}
   127  
   128  	switch opts.(type) {
   129  
   130  	case *bccsp.X509PublicKeyImportOpts:
   131  		x509Cert, ok := raw.(*x509.Certificate)
   132  		if !ok {
   133  			return nil, errors.New("[X509PublicKeyImportOpts] Invalid raw material. Expected *x509.Certificate")
   134  		}
   135  
   136  		pk := x509Cert.PublicKey
   137  
   138  		switch pk.(type) {
   139  		case *ecdsa.PublicKey:
   140  			return csp.KeyImport(pk, &bccsp.ECDSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral()})
   141  		default:
   142  			return nil, errors.New("Certificate's public key type not recognized. Supported keys: [ECDSA]")
   143  		}
   144  
   145  	default:
   146  		return csp.BCCSP.KeyImport(raw, opts)
   147  
   148  	}
   149  }
   150  
   151  // GetKey returns the key this CSP associates to
   152  // the Subject Key Identifier ski.
   153  func (csp *impl) GetKey(ski []byte) (bccsp.Key, error) {
   154  	pubKey, isPriv, err := csp.getECKey(ski)
   155  	if err == nil {
   156  		if isPriv {
   157  			return &ecdsaPrivateKey{ski, ecdsaPublicKey{ski, pubKey}}, nil
   158  		}
   159  		return &ecdsaPublicKey{ski, pubKey}, nil
   160  	}
   161  	return csp.BCCSP.GetKey(ski)
   162  }
   163  
   164  // Sign signs digest using key k.
   165  // The opts argument should be appropriate for the primitive used.
   166  //
   167  // Note that when a signature of a hash of a larger message is needed,
   168  // the caller is responsible for hashing the larger message and passing
   169  // the hash (as digest).
   170  func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) ([]byte, error) {
   171  	// Validate arguments
   172  	if k == nil {
   173  		return nil, errors.New("Invalid Key. It must not be nil")
   174  	}
   175  	if len(digest) == 0 {
   176  		return nil, errors.New("Invalid digest. Cannot be empty")
   177  	}
   178  
   179  	// Check key type
   180  	switch key := k.(type) {
   181  	case *ecdsaPrivateKey:
   182  		return csp.signECDSA(*key, digest, opts)
   183  	default:
   184  		return csp.BCCSP.Sign(key, digest, opts)
   185  	}
   186  }
   187  
   188  // Verify verifies signature against key k and digest
   189  func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) {
   190  	// Validate arguments
   191  	if k == nil {
   192  		return false, errors.New("Invalid Key. It must not be nil")
   193  	}
   194  	if len(signature) == 0 {
   195  		return false, errors.New("Invalid signature. Cannot be empty")
   196  	}
   197  	if len(digest) == 0 {
   198  		return false, errors.New("Invalid digest. Cannot be empty")
   199  	}
   200  
   201  	// Check key type
   202  	switch key := k.(type) {
   203  	case *ecdsaPrivateKey:
   204  		return csp.verifyECDSA(key.pub, signature, digest, opts)
   205  	case *ecdsaPublicKey:
   206  		return csp.verifyECDSA(*key, signature, digest, opts)
   207  	default:
   208  		return csp.BCCSP.Verify(k, signature, digest, opts)
   209  	}
   210  }
   211  
   212  // Encrypt encrypts plaintext using key k.
   213  // The opts argument should be appropriate for the primitive used.
   214  func (csp *impl) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts) ([]byte, error) {
   215  	// TODO: Add PKCS11 support for encryption, when fabric starts requiring it
   216  	return csp.BCCSP.Encrypt(k, plaintext, opts)
   217  }
   218  
   219  // Decrypt decrypts ciphertext using key k.
   220  // The opts argument should be appropriate for the primitive used.
   221  func (csp *impl) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) ([]byte, error) {
   222  	return csp.BCCSP.Decrypt(k, ciphertext, opts)
   223  }
   224  
   225  // FindPKCS11Lib IS ONLY USED FOR TESTING
   226  // This is a convenience function. Useful to self-configure, for tests where usual configuration is not
   227  // available
   228  func FindPKCS11Lib() (lib, pin, label string) {
   229  	//FIXME: Till we workout the configuration piece, look for the libraries in the familiar places
   230  	lib = os.Getenv("PKCS11_LIB")
   231  	if lib == "" {
   232  		pin = "98765432"
   233  		label = "ForFabric"
   234  		possibilities := []string{
   235  			"/usr/lib/softhsm/libsofthsm2.so",                            //Debian
   236  			"/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so",           //Ubuntu
   237  			"/usr/lib/s390x-linux-gnu/softhsm/libsofthsm2.so",            //Ubuntu
   238  			"/usr/lib/powerpc64le-linux-gnu/softhsm/libsofthsm2.so",      //Power
   239  			"/usr/local/Cellar/softhsm/2.5.0/lib/softhsm/libsofthsm2.so", //MacOS
   240  		}
   241  		for _, path := range possibilities {
   242  			if _, err := os.Stat(path); !os.IsNotExist(err) {
   243  				lib = path
   244  				break
   245  			}
   246  		}
   247  	} else {
   248  		pin = os.Getenv("PKCS11_PIN")
   249  		label = os.Getenv("PKCS11_LABEL")
   250  	}
   251  	return lib, pin, label
   252  }