github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/integration/pkcs11/pkcs11_test.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/elliptic"
    12  	"crypto/rand"
    13  	"crypto/sha256"
    14  	"crypto/x509"
    15  	"crypto/x509/pkix"
    16  	"encoding/asn1"
    17  	"encoding/hex"
    18  	"encoding/pem"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"math/big"
    22  	"os"
    23  	"path/filepath"
    24  	"syscall"
    25  	"time"
    26  
    27  	bpkcs11 "github.com/osdi23p228/fabric/bccsp/pkcs11"
    28  	"github.com/osdi23p228/fabric/integration/nwo"
    29  	"github.com/osdi23p228/fabric/integration/nwo/fabricconfig"
    30  	"github.com/miekg/pkcs11"
    31  	. "github.com/onsi/ginkgo"
    32  	. "github.com/onsi/gomega"
    33  	"github.com/tedsuo/ifrit"
    34  )
    35  
    36  var _ = Describe("PKCS11 enabled network", func() {
    37  	var (
    38  		tempDir string
    39  		network *nwo.Network
    40  		process ifrit.Process
    41  	)
    42  
    43  	BeforeEach(func() {
    44  		var err error
    45  		tempDir, err = ioutil.TempDir("", "p11")
    46  		Expect(err).NotTo(HaveOccurred())
    47  
    48  		network = nwo.New(nwo.BasicSolo(), tempDir, nil, StartPort(), components)
    49  		network.GenerateConfigTree()
    50  		network.Bootstrap()
    51  
    52  		By("configuring PKCS11 artifacts")
    53  		setupPKCS11(network)
    54  
    55  		By("starting fabric processes")
    56  		networkRunner := network.NetworkGroupRunner()
    57  		process = ifrit.Invoke(networkRunner)
    58  		Eventually(process.Ready(), network.EventuallyTimeout).Should(BeClosed())
    59  	})
    60  
    61  	AfterEach(func() {
    62  		if process != nil {
    63  			process.Signal(syscall.SIGTERM)
    64  			Eventually(process.Wait(), network.EventuallyTimeout).Should(Receive())
    65  		}
    66  		network.Cleanup()
    67  		os.RemoveAll(tempDir)
    68  	})
    69  
    70  	It("executes transactions against a basic solo network", func() {
    71  		chaincode := nwo.Chaincode{
    72  			Name:            "mycc",
    73  			Version:         "0.0",
    74  			Path:            components.Build("github.com/osdi23p228/fabric/integration/chaincode/simple/cmd"),
    75  			Lang:            "binary",
    76  			PackageFile:     filepath.Join(tempDir, "simplecc.tar.gz"),
    77  			Ctor:            `{"Args":["init","a","100","b","200"]}`,
    78  			SignaturePolicy: `AND ('Org1MSP.member','Org2MSP.member')`,
    79  			Sequence:        "1",
    80  			InitRequired:    true,
    81  			Label:           "my_prebuilt_chaincode",
    82  		}
    83  
    84  		orderer := network.Orderer("orderer")
    85  		network.CreateAndJoinChannels(orderer)
    86  
    87  		nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.PeersWithChannel("testchannel")...)
    88  		nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
    89  		runQueryInvokeQuery(network, orderer, network.Peer("Org1", "peer0"), "testchannel")
    90  	})
    91  })
    92  
    93  func setupPKCS11(network *nwo.Network) {
    94  	lib, pin, label := bpkcs11.FindPKCS11Lib()
    95  
    96  	By("establishing a PKCS11 session")
    97  	ctx, sess := setupPKCS11Ctx(lib, label, pin)
    98  	defer ctx.Destroy()
    99  	defer ctx.CloseSession(sess)
   100  
   101  	configurePeerPKCS11(ctx, sess, network)
   102  	configureOrdererPKCS11(ctx, sess, network)
   103  
   104  	bccspConfig := &fabricconfig.BCCSP{
   105  		Default: "PKCS11",
   106  		PKCS11: &fabricconfig.PKCS11{
   107  			Security: 256,
   108  			Hash:     "SHA2",
   109  			Pin:      pin,
   110  			Label:    label,
   111  			Library:  lib,
   112  		},
   113  	}
   114  
   115  	By("updating bccsp peer config")
   116  	for _, peer := range network.Peers {
   117  		peerConfig := network.ReadPeerConfig(peer)
   118  		peerConfig.Peer.BCCSP = bccspConfig
   119  		network.WritePeerConfig(peer, peerConfig)
   120  	}
   121  
   122  	By("updating bccsp orderer config")
   123  	orderer := network.Orderer("orderer")
   124  	ordererConfig := network.ReadOrdererConfig(orderer)
   125  	ordererConfig.General.BCCSP = bccspConfig
   126  	network.WriteOrdererConfig(orderer, ordererConfig)
   127  }
   128  
   129  func configurePeerPKCS11(ctx *pkcs11.Ctx, sess pkcs11.SessionHandle, network *nwo.Network) {
   130  	for _, peer := range network.Peers {
   131  		orgName := peer.Organization
   132  
   133  		peerPubKey, peerCSR, peerSerial := createCSR(ctx, sess, orgName, "peer")
   134  		adminPubKey, adminCSR, adminSerial := createCSR(ctx, sess, orgName, "admin")
   135  		userPubKey, userCSR, userSerial := createCSR(ctx, sess, orgName, "client")
   136  
   137  		domain := network.Organization(orgName).Domain
   138  
   139  		// Retrieves org CA cert
   140  		orgCAPath := network.PeerOrgCADir(network.Organization(orgName))
   141  		caBytes, err := ioutil.ReadFile(filepath.Join(orgCAPath, fmt.Sprintf("ca.%s-cert.pem", domain)))
   142  		Expect(err).NotTo(HaveOccurred())
   143  
   144  		By("Updating the peer signcerts")
   145  		newOrdererPemCert := buildCert(caBytes, orgCAPath, peerCSR, peerSerial, peerPubKey)
   146  		updateMSPFolder(network.PeerLocalMSPDir(peer), fmt.Sprintf("peer.%s-cert.pem", domain), newOrdererPemCert)
   147  
   148  		By("Updating the peer admin user signcerts")
   149  		newAdminPemCert := buildCert(caBytes, orgCAPath, adminCSR, adminSerial, adminPubKey)
   150  		orgAdminMSPPath := network.PeerUserMSPDir(peer, "Admin")
   151  		updateMSPFolder(orgAdminMSPPath, fmt.Sprintf("Admin@%s-cert.pem", domain), newAdminPemCert)
   152  
   153  		By("Updating the peer user1 signcerts")
   154  		newUserPemCert := buildCert(caBytes, orgCAPath, userCSR, userSerial, userPubKey)
   155  		orgUserMSPPath := network.PeerUserMSPDir(peer, "User1")
   156  		updateMSPFolder(orgUserMSPPath, fmt.Sprintf("User1@%s-cert.pem", domain), newUserPemCert)
   157  	}
   158  }
   159  
   160  func configureOrdererPKCS11(ctx *pkcs11.Ctx, sess pkcs11.SessionHandle, network *nwo.Network) {
   161  	orderer := network.Orderer("orderer")
   162  	orgName := orderer.Organization
   163  	domain := network.Organization(orgName).Domain
   164  
   165  	ordererPubKey, ordererCSR, ordererSerial := createCSR(ctx, sess, orgName, "orderer")
   166  	adminPubKey, adminCSR, adminSerial := createCSR(ctx, sess, orgName, "admin")
   167  
   168  	// Retrieves org CA cert
   169  	orgCAPath := network.OrdererOrgCADir(network.Organization(orgName))
   170  	caBytes, err := ioutil.ReadFile(filepath.Join(orgCAPath, fmt.Sprintf("ca.%s-cert.pem", domain)))
   171  	Expect(err).NotTo(HaveOccurred())
   172  
   173  	By("Updating the orderer signcerts")
   174  	newOrdererPemCert := buildCert(caBytes, orgCAPath, ordererCSR, ordererSerial, ordererPubKey)
   175  	updateMSPFolder(network.OrdererLocalMSPDir(orderer), fmt.Sprintf("orderer.%s-cert.pem", domain), newOrdererPemCert)
   176  
   177  	By("Updating the orderer admin user signcerts")
   178  	newAdminPemCert := buildCert(caBytes, orgCAPath, adminCSR, adminSerial, adminPubKey)
   179  	orgAdminMSPPath := network.OrdererUserMSPDir(orderer, "Admin")
   180  	updateMSPFolder(orgAdminMSPPath, fmt.Sprintf("Admin@%s-cert.pem", domain), newAdminPemCert)
   181  }
   182  
   183  // Creates pkcs11 context and session
   184  func setupPKCS11Ctx(lib, label, pin string) (*pkcs11.Ctx, pkcs11.SessionHandle) {
   185  	ctx := pkcs11.New(lib)
   186  
   187  	err := ctx.Initialize()
   188  	Expect(err).NotTo(HaveOccurred())
   189  
   190  	slot := findPKCS11Slot(ctx, label)
   191  	Expect(slot).Should(BeNumerically(">", 0), "Could not find slot with label %s", label)
   192  
   193  	sess, err := ctx.OpenSession(slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
   194  	Expect(err).NotTo(HaveOccurred())
   195  
   196  	// Login
   197  	err = ctx.Login(sess, pkcs11.CKU_USER, pin)
   198  	Expect(err).NotTo(HaveOccurred())
   199  
   200  	return ctx, sess
   201  }
   202  
   203  // Identifies pkcs11 slot using specified label
   204  func findPKCS11Slot(ctx *pkcs11.Ctx, label string) uint {
   205  	slots, err := ctx.GetSlotList(true)
   206  	Expect(err).NotTo(HaveOccurred())
   207  
   208  	for _, s := range slots {
   209  		tokInfo, err := ctx.GetTokenInfo(s)
   210  		Expect(err).NotTo(HaveOccurred())
   211  
   212  		if tokInfo.Label == label {
   213  			return s
   214  		}
   215  	}
   216  
   217  	return 0
   218  }
   219  
   220  // Creates CSR for provided organization and organizational unit
   221  func createCSR(ctx *pkcs11.Ctx, sess pkcs11.SessionHandle, org, ou string) (*ecdsa.PublicKey, *x509.CertificateRequest, *big.Int) {
   222  	pubKey, pkcs11Key := generateKeyPair(ctx, sess)
   223  
   224  	csrTemplate := x509.CertificateRequest{
   225  		Subject: pkix.Name{
   226  			Country:            []string{"US"},
   227  			Province:           []string{"California"},
   228  			Locality:           []string{"San Francisco"},
   229  			Organization:       []string{fmt.Sprintf("%s.example.com", org)},
   230  			OrganizationalUnit: []string{ou},
   231  			CommonName:         fmt.Sprintf("peer.%s.example.com", org),
   232  		},
   233  		SignatureAlgorithm: x509.ECDSAWithSHA256,
   234  	}
   235  
   236  	csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, pkcs11Key)
   237  	Expect(err).NotTo(HaveOccurred())
   238  
   239  	csr, err := x509.ParseCertificateRequest(csrBytes)
   240  	Expect(err).NotTo(HaveOccurred())
   241  	err = csr.CheckSignature()
   242  	Expect(err).NotTo(HaveOccurred())
   243  
   244  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
   245  	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
   246  	Expect(err).NotTo(HaveOccurred())
   247  
   248  	return pubKey, csr, serialNumber
   249  }
   250  
   251  func buildCert(caBytes []byte, org1CAPath string, csr *x509.CertificateRequest, serialNumber *big.Int, pubKey *ecdsa.PublicKey) []byte {
   252  	pemBlock, _ := pem.Decode(caBytes)
   253  	Expect(pemBlock).NotTo(BeNil())
   254  
   255  	caCert, err := x509.ParseCertificate(pemBlock.Bytes)
   256  	Expect(err).NotTo(HaveOccurred())
   257  
   258  	keyBytes, err := ioutil.ReadFile(filepath.Join(org1CAPath, "priv_sk"))
   259  	Expect(err).NotTo(HaveOccurred())
   260  
   261  	pemBlock, _ = pem.Decode(keyBytes)
   262  	Expect(pemBlock).NotTo(BeNil())
   263  	key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
   264  	Expect(err).NotTo(HaveOccurred())
   265  	caKey := key.(*ecdsa.PrivateKey)
   266  
   267  	certTemplate := &x509.Certificate{
   268  		Signature:          csr.Signature,
   269  		SignatureAlgorithm: csr.SignatureAlgorithm,
   270  		PublicKey:          csr.PublicKey,
   271  		PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
   272  
   273  		SerialNumber:          serialNumber,
   274  		NotBefore:             time.Now().Add(-1 * time.Minute).UTC(),
   275  		NotAfter:              time.Now().Add(365 * 24 * time.Hour).UTC(),
   276  		BasicConstraintsValid: true,
   277  
   278  		Subject:     csr.Subject,
   279  		KeyUsage:    x509.KeyUsageDigitalSignature,
   280  		ExtKeyUsage: []x509.ExtKeyUsage{},
   281  	}
   282  
   283  	// Use root CA to create and sign cert
   284  	signedCert, err := x509.CreateCertificate(rand.Reader, certTemplate, caCert, pubKey, caKey)
   285  	Expect(err).NotTo(HaveOccurred())
   286  
   287  	return pem.EncodeToMemory(&pem.Block{Bytes: signedCert, Type: "CERTIFICATE"})
   288  }
   289  
   290  // Overwrites existing cert and removes private key from keystore folder
   291  func updateMSPFolder(path, certName string, cert []byte) {
   292  	// Overwrite existing certificate with new certificate
   293  	err := ioutil.WriteFile(filepath.Join(path, "signcerts", certName), cert, 0644)
   294  	Expect(err).NotTo(HaveOccurred())
   295  
   296  	// delete the existing private key - this is stored in the hsm
   297  	adminKSCert := filepath.Join(path, "keystore", "priv_sk")
   298  	err = os.Remove(adminKSCert)
   299  	Expect(err).NotTo(HaveOccurred())
   300  }
   301  
   302  // Generating key pair in HSM, convert, and return keys
   303  func generateKeyPair(ctx *pkcs11.Ctx, sess pkcs11.SessionHandle) (*ecdsa.PublicKey, *P11ECDSAKey) {
   304  	publabel, privlabel := "BCPUB7", "BCPRV7"
   305  
   306  	curve := asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} // secp256r1 Curve
   307  
   308  	marshaledOID, err := asn1.Marshal(curve)
   309  	Expect(err).NotTo(HaveOccurred())
   310  
   311  	pubAttrs := []*pkcs11.Attribute{
   312  		pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_EC),
   313  		pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY),
   314  		pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
   315  		pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true),
   316  		pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, marshaledOID),
   317  
   318  		pkcs11.NewAttribute(pkcs11.CKA_ID, publabel),
   319  		pkcs11.NewAttribute(pkcs11.CKA_LABEL, publabel),
   320  	}
   321  
   322  	privAttrs := []*pkcs11.Attribute{
   323  		pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_EC),
   324  		pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY),
   325  		pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
   326  		pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
   327  
   328  		pkcs11.NewAttribute(pkcs11.CKA_ID, privlabel),
   329  		pkcs11.NewAttribute(pkcs11.CKA_LABEL, privlabel),
   330  
   331  		pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false),
   332  		pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true),
   333  	}
   334  
   335  	pubK, privK, err := ctx.GenerateKeyPair(
   336  		sess,
   337  		[]*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_EC_KEY_PAIR_GEN, nil)},
   338  		pubAttrs,
   339  		privAttrs,
   340  	)
   341  	Expect(err).NotTo(HaveOccurred())
   342  
   343  	ecpt := ecPoint(ctx, sess, pubK)
   344  	Expect(ecpt).NotTo(BeEmpty(), "CKA_EC_POINT not found")
   345  
   346  	hash := sha256.Sum256(ecpt)
   347  	ski := hash[:]
   348  
   349  	setskiT := []*pkcs11.Attribute{
   350  		pkcs11.NewAttribute(pkcs11.CKA_ID, ski),
   351  		pkcs11.NewAttribute(pkcs11.CKA_LABEL, hex.EncodeToString(ski)),
   352  	}
   353  
   354  	err = ctx.SetAttributeValue(sess, pubK, setskiT)
   355  	Expect(err).NotTo(HaveOccurred())
   356  
   357  	err = ctx.SetAttributeValue(sess, privK, setskiT)
   358  	Expect(err).NotTo(HaveOccurred())
   359  
   360  	// convert pub key to rsa types
   361  	nistCurve := elliptic.P256()
   362  	x, y := elliptic.Unmarshal(nistCurve, ecpt)
   363  	if x == nil {
   364  		Expect(x).NotTo(BeNil(), "Failed Unmarshaling Public Key")
   365  	}
   366  
   367  	pubKey := &ecdsa.PublicKey{Curve: nistCurve, X: x, Y: y}
   368  
   369  	pkcs11Key := &P11ECDSAKey{
   370  		ctx:              ctx,
   371  		session:          sess,
   372  		publicKey:        pubKey,
   373  		privateKeyHandle: privK,
   374  	}
   375  
   376  	return pubKey, pkcs11Key
   377  }
   378  
   379  // SoftHSM reports extra two bytes before the uncompressed point
   380  // see /bccsp/pkcs11/pkcs11.go::ecPoint() for additional details
   381  func ecPoint(pkcs11lib *pkcs11.Ctx, session pkcs11.SessionHandle, key pkcs11.ObjectHandle) (ecpt []byte) {
   382  	template := []*pkcs11.Attribute{
   383  		pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, nil),
   384  		pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, nil),
   385  	}
   386  
   387  	attr, err := pkcs11lib.GetAttributeValue(session, key, template)
   388  	if err != nil {
   389  		Expect(err).NotTo(HaveOccurred(), "PKCS11: get(EC point)")
   390  	}
   391  
   392  	for _, a := range attr {
   393  		if a.Type != pkcs11.CKA_EC_POINT {
   394  			continue
   395  		}
   396  
   397  		switch {
   398  		case ((len(a.Value) % 2) == 0) && (byte(0x04) == a.Value[0]) && (byte(0x04) == a.Value[len(a.Value)-1]):
   399  			ecpt = a.Value[0 : len(a.Value)-1] // Trim trailing 0x04
   400  		case byte(0x04) == a.Value[0] && byte(0x04) == a.Value[2]:
   401  			ecpt = a.Value[2:len(a.Value)]
   402  		default:
   403  			ecpt = a.Value
   404  		}
   405  	}
   406  
   407  	return ecpt
   408  }