github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/security/pkcs11sec/pkcs11_security.go (about)

     1  // Copyright (c) 2020-2022, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package pkcs11sec
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"crypto"
    11  	"crypto/rsa"
    12  	"crypto/tls"
    13  	"crypto/x509"
    14  	"encoding/pem"
    15  	"fmt"
    16  	"io"
    17  	"net/http"
    18  	"os"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/AlecAivazis/survey/v2"
    23  	"github.com/choria-io/go-choria/inter"
    24  	"github.com/miekg/pkcs11"
    25  	"github.com/miekg/pkcs11/p11"
    26  	"github.com/sirupsen/logrus"
    27  
    28  	"github.com/choria-io/go-choria/providers/security/filesec"
    29  )
    30  
    31  // fetched from https://golang.org/src/crypto/rsa/pkcs1v15.go
    32  var hashPrefixes = map[crypto.Hash][]byte{
    33  	crypto.MD5:       {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
    34  	crypto.SHA1:      {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
    35  	crypto.SHA224:    {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
    36  	crypto.SHA256:    {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
    37  	crypto.SHA384:    {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
    38  	crypto.SHA512:    {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
    39  	crypto.MD5SHA1:   {},
    40  	crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14},
    41  }
    42  
    43  type Pkcs11Security struct {
    44  	conf *Config
    45  	log  *logrus.Entry
    46  
    47  	fsec *filesec.FileSecurity
    48  
    49  	cert    *tls.Certificate
    50  	pKey    *PrivateKey
    51  	pin     *string
    52  	session p11.Session
    53  }
    54  
    55  type PrivateKey struct {
    56  	PublicKey  crypto.PublicKey
    57  	PrivateKey *p11.PrivateKey
    58  }
    59  
    60  func (k *PrivateKey) Public() crypto.PublicKey {
    61  	return k.PublicKey
    62  }
    63  
    64  // Sign signs any compatible hash that is sent to it (see hashPrefixes for supported hashes)
    65  // need to handle as many hash types as possible, since this is being used by http/tls driver
    66  func (k *PrivateKey) Sign(_ io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
    67  	prefix, ok := hashPrefixes[opts.HashFunc()]
    68  	if !ok {
    69  		return nil, fmt.Errorf("unknown hash function")
    70  	}
    71  	mechanism := pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil)
    72  	input := append(prefix, msg...)
    73  
    74  	output, err := k.PrivateKey.Sign(*mechanism, input)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	return output, nil
    79  }
    80  
    81  type Config struct {
    82  	// CAFile is the file where the trusted CA cert resides
    83  	CAFile string
    84  
    85  	// PrivilegedUsers is a list of regular expressions that identity privileged users
    86  	PrivilegedUsers []string
    87  
    88  	// AllowList is a list of regular expressions that identity valid users to allow in
    89  	AllowList []string
    90  
    91  	// DisableTLSVerify disables TLS verify in HTTP clients etc
    92  	DisableTLSVerify bool
    93  
    94  	// PKCS11DriverFile points to the dynamic library file to use (usually a .so file)
    95  	PKCS11DriverFile string
    96  
    97  	// PKCS11Slot specifies which slot of the pkcs11 device to use
    98  	PKCS11Slot uint
    99  
   100  	// RemoteSigner is the signer used to sign requests using a remote like AAA Service
   101  	RemoteSigner inter.RequestSigner
   102  }
   103  
   104  func New(opts ...Option) (*Pkcs11Security, error) {
   105  	p := &Pkcs11Security{}
   106  
   107  	for _, opt := range opts {
   108  		err := opt(p)
   109  		if err != nil {
   110  			return nil, err
   111  		}
   112  	}
   113  
   114  	if p.conf == nil {
   115  		return nil, fmt.Errorf("configuration not given")
   116  	}
   117  
   118  	if p.log == nil {
   119  		return nil, fmt.Errorf("logger not given")
   120  	}
   121  
   122  	if p.conf.PKCS11DriverFile == "" {
   123  		return nil, fmt.Errorf("pkcs11: PKCS11DriverFile option is required")
   124  	}
   125  
   126  	if p.pin != nil {
   127  		err := p.loginToToken()
   128  		if err != nil {
   129  			return nil, fmt.Errorf("failed to login to token in New(): %s", err)
   130  		}
   131  	}
   132  
   133  	return p, p.reinit()
   134  }
   135  
   136  func (p *Pkcs11Security) promptForPin() (*string, error) {
   137  	pin := ""
   138  	prompt := &survey.Password{
   139  		Message: "PIN",
   140  	}
   141  	err := survey.AskOne(prompt, &pin)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	return &pin, nil
   146  }
   147  
   148  func (p *Pkcs11Security) loginToToken() error {
   149  	var err error
   150  
   151  	if p.pin == nil {
   152  		p.pin, err = p.promptForPin()
   153  		if err != nil {
   154  			fmt.Printf("err is %s", err.Error())
   155  			return err
   156  		}
   157  	}
   158  
   159  	p.log.Debugf("Attempting to open PKCS11 driver file %s", p.conf.PKCS11DriverFile)
   160  
   161  	module, err := p11.OpenModule(p.conf.PKCS11DriverFile)
   162  	if err != nil {
   163  		return fmt.Errorf("failed to open PKCS11 driver file %s: %s", p.conf.PKCS11DriverFile, err)
   164  	}
   165  
   166  	p.log.Debug("Attempting to fetch PKCS11 driver slots")
   167  
   168  	slots, err := module.Slots()
   169  	if err != nil {
   170  		return fmt.Errorf("failed to fetch PKCS11 driver slots: %s", err)
   171  	}
   172  
   173  	var slot *p11.Slot
   174  	found := false
   175  	for _, aSlot := range slots {
   176  		p.log.Debugf("Found slot %d", aSlot.ID())
   177  
   178  		if aSlot.ID() == p.conf.PKCS11Slot {
   179  			slot = &aSlot
   180  			found = true
   181  			break
   182  		}
   183  	}
   184  	if !found {
   185  		if len(slots) == 1 {
   186  			slot = &slots[0]
   187  		} else {
   188  			return fmt.Errorf("failed to find slot with label %d", p.conf.PKCS11Slot)
   189  		}
   190  	}
   191  	p.log.Debugf("Attempting to open session for selected slot %d", p.conf.PKCS11Slot)
   192  
   193  	session, err := slot.OpenSession()
   194  	if err != nil {
   195  		return fmt.Errorf("failed to open PKCS11 session: %s", err)
   196  	}
   197  
   198  	p.session = session
   199  
   200  	err = session.Login(*p.pin)
   201  	if err != nil {
   202  		if !strings.Contains(err.Error(), "CKR_USER_ALREADY_LOGGED_IN") {
   203  			return fmt.Errorf("failed to login with provided pin: %s", err)
   204  		}
   205  	}
   206  
   207  	p.log.Debug("Attempting to find private key object")
   208  	privateKeyObject, err := session.FindObject([]*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PRIVATE_KEY)})
   209  	if err != nil {
   210  		return fmt.Errorf("failed to find private key object: %s", err)
   211  	}
   212  
   213  	p.log.Debug("Attempting to find certificate object")
   214  	certObject, err := session.FindObject([]*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE)})
   215  	if err != nil {
   216  		return fmt.Errorf("failed to find certificate object: %s", err)
   217  	}
   218  
   219  	certData, err := certObject.Value()
   220  	if err != nil {
   221  		return fmt.Errorf("failed to get certificate object value: %s", err)
   222  	}
   223  
   224  	parsedCert, err := x509.ParseCertificate(certData)
   225  	if err != nil {
   226  		return fmt.Errorf("failed to parse X509 certificate: %s", err)
   227  	}
   228  
   229  	if parsedCert.Subject.CommonName == "" {
   230  		return fmt.Errorf("cert on token must have valid CommonName")
   231  	}
   232  
   233  	pubKey, ok := parsedCert.PublicKey.(crypto.PublicKey)
   234  	if !ok {
   235  		return fmt.Errorf("public key in certificate is not a crypto.PublicKey: %s", err)
   236  	}
   237  
   238  	privateKey := p11.PrivateKey(privateKeyObject)
   239  
   240  	p.pKey = &PrivateKey{
   241  		PublicKey:  pubKey,
   242  		PrivateKey: &privateKey,
   243  	}
   244  
   245  	p.cert = &tls.Certificate{
   246  		Certificate: [][]byte{certData},
   247  		Leaf:        parsedCert,
   248  		PrivateKey:  p.pKey,
   249  	}
   250  
   251  	return nil
   252  }
   253  
   254  // PublicCert is the parsed public certificate
   255  func (p *Pkcs11Security) PublicCert() (*x509.Certificate, error) {
   256  	if p.cert == nil {
   257  		return nil, fmt.Errorf("not logged in")
   258  	}
   259  
   260  	return p.cert.Leaf, nil
   261  }
   262  
   263  func (p *Pkcs11Security) reinit() error {
   264  	var err error
   265  
   266  	fc := filesec.Config{
   267  		AllowList:        p.conf.AllowList,
   268  		DisableTLSVerify: p.conf.DisableTLSVerify,
   269  		PrivilegedUsers:  p.conf.PrivilegedUsers,
   270  		CA:               p.conf.CAFile,
   271  		Certificate:      "unused",
   272  		Identity:         "unused",
   273  		RemoteSigner:     p.conf.RemoteSigner,
   274  	}
   275  
   276  	p.fsec, err = filesec.New(filesec.WithConfig(&fc), filesec.WithLog(p.log))
   277  	if err != nil {
   278  		return err
   279  	}
   280  
   281  	return nil
   282  }
   283  
   284  func (p *Pkcs11Security) Logout() error {
   285  	return p.session.Logout()
   286  }
   287  
   288  func (p *Pkcs11Security) BackingTechnology() inter.SecurityTechnology {
   289  	return p.fsec.BackingTechnology()
   290  }
   291  
   292  func (p *Pkcs11Security) Provider() string {
   293  	return "pkcs11"
   294  }
   295  
   296  func (p *Pkcs11Security) TokenBytes() ([]byte, error) {
   297  	return nil, fmt.Errorf("tokens not available for pkcs11 security provider")
   298  }
   299  
   300  func (p *Pkcs11Security) Enroll(ctx context.Context, wait time.Duration, cb func(digest string, try int)) error {
   301  	return fmt.Errorf("pkcs11 security provider does not support enrollment")
   302  }
   303  
   304  // RemoteSignRequest signs a choria request against using a remote signer and returns a secure request
   305  func (p *Pkcs11Security) RemoteSignRequest(ctx context.Context, str []byte) (signed []byte, err error) {
   306  	return nil, fmt.Errorf("pkcs11 security provider does not support remote signing requests")
   307  }
   308  
   309  func (p *Pkcs11Security) IsRemoteSigning() bool { return false }
   310  
   311  // Validate determines if the node represents a valid SSL configuration
   312  func (p *Pkcs11Security) Validate() ([]string, bool) {
   313  	var errorsList []string
   314  
   315  	stat, err := os.Stat(p.conf.CAFile)
   316  	switch {
   317  	case os.IsNotExist(err):
   318  		errorsList = append(errorsList, err.Error())
   319  	case !stat.Mode().IsRegular():
   320  		errorsList = append(errorsList, fmt.Sprintf("%s is not a regular file", p.conf.CAFile))
   321  	}
   322  
   323  	if p.pin == nil {
   324  		p.log.Debug("Attempting to login to token in Validate()")
   325  		if err := p.loginToToken(); err != nil {
   326  			errorsList = append(errorsList, fmt.Sprintf("failed to login to token in Validate(): %s", err))
   327  		}
   328  	}
   329  
   330  	return errorsList, len(errorsList) == 0
   331  }
   332  
   333  // ChecksumBytes calculates a sha256 checksum for data
   334  func (p *Pkcs11Security) ChecksumBytes(data []byte) []byte {
   335  	return p.fsec.ChecksumBytes(data)
   336  }
   337  
   338  // SignBytes signs a message using a SHA256 PKCS1v15 protocol
   339  func (p *Pkcs11Security) SignBytes(str []byte) ([]byte, error) {
   340  	hashed := p.ChecksumBytes(str)
   341  	mechanism := pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil)
   342  	input := append(hashPrefixes[crypto.SHA256], hashed...)
   343  
   344  	output, err := p.pKey.PrivateKey.Sign(*mechanism, input)
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  	return output, nil
   349  }
   350  
   351  // VerifyByteSignature verify that dat matches signature sig made by the key, if pub cert is empty the active public key will be used
   352  func (p *Pkcs11Security) VerifySignatureBytes(dat []byte, sig []byte, public ...[]byte) (should bool, signer string) {
   353  	if len(public) != 1 {
   354  		p.log.Errorf("Could not process public data: only single signer public data is supported")
   355  		return false, ""
   356  	}
   357  
   358  	pubcert := public[0]
   359  
   360  	var cert *x509.Certificate
   361  	var err error
   362  
   363  	if len(pubcert) > 0 {
   364  		pkpem, _ := pem.Decode(pubcert)
   365  		if pkpem == nil {
   366  			p.log.Errorf("Could not decode PEM data in public key: invalid pem data")
   367  			return false, ""
   368  		}
   369  
   370  		cert, err = x509.ParseCertificate(pkpem.Bytes)
   371  		if err != nil {
   372  			p.log.Errorf("Could not parse decoded PEM data for public key: %s", err)
   373  			return false, ""
   374  		}
   375  	} else {
   376  		cert = p.cert.Leaf
   377  	}
   378  
   379  	rsaPublicKey := cert.PublicKey.(*rsa.PublicKey)
   380  	hashed := p.ChecksumBytes(dat)
   381  
   382  	err = rsa.VerifyPKCS1v15(rsaPublicKey, crypto.SHA256, hashed[:], sig)
   383  	if err != nil {
   384  		p.log.Errorf("Signature verification failed: %s", err)
   385  		return false, ""
   386  	}
   387  
   388  	names := []string{cert.Subject.CommonName}
   389  	names = append(names, cert.DNSNames...)
   390  
   391  	if len(names) == 0 {
   392  		p.log.Errorf("Signature verification failed: no names found in signer certificate")
   393  		return false, ""
   394  	}
   395  
   396  	p.log.Debugf("Verified signature from %s", strings.Join(names, ", "))
   397  
   398  	return true, names[0]
   399  }
   400  
   401  // CallerName creates a choria like caller name in the form of choria=identity
   402  func (p *Pkcs11Security) CallerName() string {
   403  	return fmt.Sprintf("choria=%s", p.cert.Leaf.Subject.CommonName)
   404  }
   405  
   406  // CallerIdentity extracts the identity from a choria like caller name in the form of choria=identity
   407  func (p *Pkcs11Security) CallerIdentity(caller string) (string, error) {
   408  	return p.fsec.CallerIdentity(caller)
   409  }
   410  
   411  // ShouldAllowCaller verifies the public data
   412  func (p *Pkcs11Security) ShouldAllowCaller(name string, callers ...[]byte) (privileged bool, err error) {
   413  	return p.fsec.ShouldAllowCaller(name, callers...)
   414  }
   415  
   416  // VerifyCertificate verifies a certificate is signed with the configured CA and if
   417  // name is not "" that it matches the name given
   418  func (p *Pkcs11Security) VerifyCertificate(certpem []byte, name string) error {
   419  	return p.fsec.VerifyCertificate(certpem, name)
   420  }
   421  
   422  // publicCertPem retrieves the public certificate for this instance
   423  func (p *Pkcs11Security) publicCertPem() (*pem.Block, error) {
   424  	pb := &pem.Block{
   425  		Type:  "CERTIFICATE",
   426  		Bytes: p.cert.Leaf.Raw,
   427  	}
   428  
   429  	return pb, nil
   430  }
   431  
   432  // PublicCertBytes retrieves pem data in textual form for the public certificate of the current identity
   433  func (p *Pkcs11Security) PublicCertBytes() ([]byte, error) {
   434  	pemCert, err := p.publicCertPem()
   435  	if err != nil {
   436  		return nil, fmt.Errorf("failed to run publicCertPem: %s", err)
   437  	}
   438  	var buf bytes.Buffer
   439  	err = pem.Encode(&buf, pemCert)
   440  	if err != nil {
   441  		return nil, fmt.Errorf("failed to run pem.Encode: %s", err)
   442  	}
   443  	return buf.Bytes(), nil
   444  }
   445  
   446  // Identity determines the choria certname
   447  func (p *Pkcs11Security) Identity() string {
   448  	return p.cert.Leaf.Subject.CommonName
   449  }
   450  
   451  // ClientTLSConfig creates a client TLS configuration
   452  func (p *Pkcs11Security) ClientTLSConfig() (*tls.Config, error) {
   453  	return p.TLSConfig()
   454  }
   455  
   456  // TLSConfig creates a TLS configuration for use by NATS, HTTPS etc
   457  func (p *Pkcs11Security) TLSConfig() (*tls.Config, error) {
   458  	caCert, err := os.ReadFile(p.conf.CAFile)
   459  	if err != nil {
   460  		return nil, err
   461  	}
   462  
   463  	caCertPool := x509.NewCertPool()
   464  	caCertPool.AppendCertsFromPEM(caCert)
   465  
   466  	tlsc := &tls.Config{
   467  		MinVersion:   tls.VersionTLS12,
   468  		Certificates: []tls.Certificate{*p.cert},
   469  		GetClientCertificate: func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
   470  			return p.cert, nil
   471  		},
   472  		ClientCAs: caCertPool,
   473  		RootCAs:   caCertPool,
   474  	}
   475  
   476  	if p.conf.DisableTLSVerify {
   477  		tlsc.InsecureSkipVerify = true
   478  	}
   479  
   480  	return tlsc, nil
   481  }
   482  
   483  // SSLContext creates a SSL context loaded with our certs and ca
   484  func (p *Pkcs11Security) SSLContext() (*http.Transport, error) {
   485  	tlsConfig, err := p.TLSConfig()
   486  	if err != nil {
   487  		return nil, err
   488  	}
   489  
   490  	transport := &http.Transport{TLSClientConfig: tlsConfig}
   491  
   492  	return transport, nil
   493  }
   494  
   495  func (p *Pkcs11Security) HTTPClient(secure bool) (*http.Client, error) {
   496  	client := &http.Client{}
   497  
   498  	if secure {
   499  		tlsc, err := p.TLSConfig()
   500  		if err != nil {
   501  			return nil, fmt.Errorf("pkcs11: could not set up HTTP connection: %s", err)
   502  		}
   503  
   504  		client.Transport = &http.Transport{TLSClientConfig: tlsc}
   505  	}
   506  
   507  	return client, nil
   508  }
   509  
   510  func (p *Pkcs11Security) ShouldSignReplies() bool { return false }