...

Source file src/github.com/googleapis/enterprise-certificate-proxy/internal/signer/linux/pkcs11/pkcs11.go

Documentation: github.com/googleapis/enterprise-certificate-proxy/internal/signer/linux/pkcs11

     1  // Copyright 2022 Google LLC.
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  //     https://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  // pkcs11 provides helpers for working with certificates via PKCS#11 APIs
    15  // provided by go-pkcs11
    16  package pkcs11
    17  
    18  import (
    19  	"crypto"
    20  	"crypto/ecdsa"
    21  	"crypto/rand"
    22  	"crypto/rsa"
    23  	"crypto/sha1"
    24  	"crypto/sha256"
    25  	"errors"
    26  	"fmt"
    27  	"hash"
    28  	"io"
    29  	"strconv"
    30  	"strings"
    31  
    32  	"github.com/google/go-pkcs11/pkcs11"
    33  )
    34  
    35  // ParseHexString parses hexadecimal string into uint32
    36  func ParseHexString(str string) (i uint32, err error) {
    37  	stripped := strings.Replace(str, "0x", "", -1)
    38  	resultUint64, err := strconv.ParseUint(stripped, 16, 32)
    39  	if err != nil {
    40  		return 0, err
    41  	}
    42  	return uint32(resultUint64), nil
    43  }
    44  
    45  // Cred returns a Key wrapping the first valid certificate in the pkcs11 module
    46  // matching a given slot and label.
    47  func Cred(pkcs11Module string, slotUint32Str string, label string, userPin string) (*Key, error) {
    48  	module, err := pkcs11.Open(pkcs11Module)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	slotUint32, err := ParseHexString(slotUint32Str)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	kslot, err := module.Slot(slotUint32, pkcs11.Options{PIN: userPin})
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	certs, err := kslot.Objects(pkcs11.Filter{Class: pkcs11.ClassCertificate, Label: label})
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	if len(certs) < 1 {
    67  		return nil, fmt.Errorf("No certificate object was found with label %s.", label)
    68  	}
    69  
    70  	cert, err := certs[0].Certificate()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	x509, err := cert.X509()
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	var kchain [][]byte
    79  	kchain = append(kchain, x509.Raw)
    80  
    81  	pubKeys, err := kslot.Objects(pkcs11.Filter{Class: pkcs11.ClassPublicKey, Label: label})
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	if len(pubKeys) < 1 {
    87  		return nil, fmt.Errorf("No public key object was found with label %s.", label)
    88  	}
    89  
    90  	pubKey, err := pubKeys[0].PublicKey()
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	privkeys, err := kslot.Objects(pkcs11.Filter{Class: pkcs11.ClassPrivateKey, Label: label})
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	if len(privkeys) < 1 {
   101  		return nil, fmt.Errorf("No private key object was found with label %s.", label)
   102  	}
   103  
   104  	privKey, err := privkeys[0].PrivateKey(pubKey)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	ksigner, ok := privKey.(crypto.Signer)
   109  	if !ok {
   110  		return nil, errors.New("PrivateKey does not implement crypto.Signer")
   111  	}
   112  	kdecrypter, _ := privKey.(crypto.Decrypter)
   113  	defaultHash := crypto.SHA256
   114  	return &Key{
   115  		slot:      kslot,
   116  		signer:    ksigner,
   117  		chain:     kchain,
   118  		privKey:   privKey,
   119  		label:     label,
   120  		module:    *module,
   121  		hash:      defaultHash,
   122  		decrypter: kdecrypter,
   123  	}, nil
   124  }
   125  
   126  // Key is a wrapper around the pkcs11 module and uses it to
   127  // implement signing-related methods.
   128  type Key struct {
   129  	slot      *pkcs11.Slot
   130  	signer    crypto.Signer
   131  	chain     [][]byte
   132  	privKey   crypto.PrivateKey
   133  	label     string
   134  	module    pkcs11.Module
   135  	hash      crypto.Hash
   136  	decrypter crypto.Decrypter
   137  }
   138  
   139  // CertificateChain returns the credential as a raw X509 cert chain. This
   140  // contains the public key.
   141  func (k *Key) CertificateChain() [][]byte {
   142  	return k.chain
   143  }
   144  
   145  // Close releases resources held by the credential.
   146  func (k *Key) Close() {
   147  	k.slot.Close()
   148  	k.module.Close()
   149  }
   150  
   151  // Public returns the corresponding public key for this Key.
   152  func (k *Key) Public() crypto.PublicKey {
   153  	return k.signer.Public()
   154  }
   155  
   156  // Sign signs a message.
   157  func (k *Key) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
   158  	return k.signer.Sign(nil, digest, opts)
   159  }
   160  
   161  // Encrypt encrypts a plaintext message digest using the public key. Here, we use standard golang API.
   162  func (k *Key) Encrypt(plaintext []byte, opts any) ([]byte, error) {
   163  	if hash, ok := opts.(crypto.Hash); ok {
   164  		k.hash = hash
   165  	} else {
   166  		return nil, fmt.Errorf("Unsupported encrypt opts: %v", opts)
   167  	}
   168  	publicKey := k.Public()
   169  	_, ok := publicKey.(*rsa.PublicKey)
   170  	if ok {
   171  		return k.encryptRSA(plaintext)
   172  	}
   173  	_, ok = publicKey.(*ecdsa.PublicKey)
   174  	if ok {
   175  		// TODO: Implement encryption for ec keys - https://github.com/googleapis/enterprise-certificate-proxy/issues/95
   176  		return nil, errors.New("encrypt error: EC keys not yet supported")
   177  	}
   178  	return nil, errors.New("encrypt error: Unsupported key type")
   179  }
   180  
   181  // Decrypt decrypts a ciphertext message digest using the private key. Here, we pass off the decryption to pkcs11 library.
   182  func (k *Key) Decrypt(msg []byte, opts crypto.DecrypterOpts) ([]byte, error) {
   183  	if oaepOpts, ok := opts.(*rsa.OAEPOptions); ok {
   184  		k.hash = oaepOpts.Hash
   185  	} else {
   186  		return nil, fmt.Errorf("Unsupported DecrypterOpts: %v", opts)
   187  	}
   188  	if k.decrypter == nil {
   189  		return nil, fmt.Errorf("decrypt error: Decrypter is nil")
   190  	}
   191  	publicKey := k.Public()
   192  	_, ok := publicKey.(*rsa.PublicKey)
   193  	if ok {
   194  		return k.decryptRSAWithPKCS11(msg)
   195  	}
   196  	_, ok = publicKey.(*ecdsa.PublicKey)
   197  	if ok {
   198  		// TODO: Implement decryption for ec keys - https://github.com/googleapis/enterprise-certificate-proxy/issues/95
   199  		return nil, errors.New("decrypt error: EC keys not yet supported")
   200  	}
   201  	return nil, errors.New("decrypt error: Unsupported key type")
   202  }
   203  
   204  func (k *Key) encryptRSA(data []byte) ([]byte, error) {
   205  	publicKey := k.Public()
   206  	rsaPubKey := publicKey.(*rsa.PublicKey)
   207  	hash, err := cryptoHashToHash(k.hash)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	return rsa.EncryptOAEP(hash, rand.Reader, rsaPubKey, data, nil)
   212  }
   213  
   214  func (k *Key) decryptRSAWithPKCS11(encryptedData []byte) ([]byte, error) {
   215  	opts := &rsa.OAEPOptions{Hash: k.hash}
   216  	return k.decrypter.Decrypt(nil, encryptedData, opts)
   217  }
   218  
   219  func cryptoHashToHash(hash crypto.Hash) (hash.Hash, error) {
   220  	switch hash {
   221  	case crypto.SHA256:
   222  		return sha256.New(), nil
   223  	case crypto.SHA1:
   224  		return sha1.New(), nil
   225  	default:
   226  		return nil, errors.New("hash conversion error: Unsupported hash")
   227  	}
   228  }
   229  

View as plain text