github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/utils/keys/yubikey.go (about)

     1  //go:build piv && !pivtest
     2  
     3  /*
     4  Copyright 2022 Gravitational, Inc.
     5  Licensed under the Apache License, Version 2.0 (the "License");
     6  you may not use this file except in compliance with the License.
     7  You may obtain a copy of the License at
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package keys
    17  
    18  import (
    19  	"context"
    20  	"crypto"
    21  	"crypto/ecdsa"
    22  	"crypto/elliptic"
    23  	"crypto/rand"
    24  	"crypto/sha256"
    25  	"crypto/x509"
    26  	"crypto/x509/pkix"
    27  	"encoding/hex"
    28  	"encoding/json"
    29  	"encoding/pem"
    30  	"errors"
    31  	"fmt"
    32  	"io"
    33  	"math/big"
    34  	"os"
    35  	"strconv"
    36  	"strings"
    37  	"sync"
    38  	"time"
    39  
    40  	"github.com/go-piv/piv-go/piv"
    41  	"github.com/gravitational/trace"
    42  
    43  	"github.com/gravitational/teleport/api"
    44  	attestation "github.com/gravitational/teleport/api/gen/proto/go/attestation/v1"
    45  	"github.com/gravitational/teleport/api/utils/prompt"
    46  	"github.com/gravitational/teleport/api/utils/retryutils"
    47  )
    48  
    49  const (
    50  	// PIVCardTypeYubiKey is the PIV card type assigned to yubiKeys.
    51  	PIVCardTypeYubiKey = "yubikey"
    52  )
    53  
    54  // getOrGenerateYubiKeyPrivateKey connects to a connected yubiKey and gets a private key
    55  // matching the given touch requirement. This private key will either be newly generated
    56  // or previously generated by a Teleport client and reused.
    57  func getOrGenerateYubiKeyPrivateKey(ctx context.Context, requiredKeyPolicy PrivateKeyPolicy, slot PIVSlot) (*PrivateKey, error) {
    58  	// Use the first yubiKey we find.
    59  	y, err := FindYubiKey(0)
    60  	if err != nil {
    61  		return nil, trace.Wrap(err)
    62  	}
    63  
    64  	// If PIN is required, check that PIN and PUK are not the defaults.
    65  	if requiredKeyPolicy.isHardwareKeyPINVerified() {
    66  		if err := y.checkOrSetPIN(ctx); err != nil {
    67  			return nil, trace.Wrap(err)
    68  		}
    69  	}
    70  
    71  	promptOverwriteSlot := func(msg string) error {
    72  		promptQuestion := fmt.Sprintf("%v\nWould you like to overwrite this slot's private key and certificate?", msg)
    73  		if confirmed, confirmErr := prompt.Confirmation(ctx, os.Stderr, prompt.Stdin(), promptQuestion); confirmErr != nil {
    74  			return trace.Wrap(confirmErr)
    75  		} else if !confirmed {
    76  			return trace.Wrap(trace.CompareFailed(msg), "user declined to overwrite slot")
    77  		}
    78  		return nil
    79  	}
    80  
    81  	// If a specific slot was specified, use that. Otherwise, check for a key in the
    82  	// default slot for the given policy and generate a new one if needed.
    83  	var pivSlot piv.Slot
    84  	if slot != "" {
    85  		pivSlot, err = slot.parse()
    86  		if err != nil {
    87  			return nil, trace.Wrap(err)
    88  		}
    89  	} else {
    90  		pivSlot, err = GetDefaultKeySlot(requiredKeyPolicy)
    91  		if err != nil {
    92  			return nil, trace.Wrap(err)
    93  		}
    94  
    95  		// Check the client certificate in the slot.
    96  		switch cert, err := y.getCertificate(pivSlot); {
    97  		case err == nil && (len(cert.Subject.Organization) == 0 || cert.Subject.Organization[0] != certOrgName):
    98  			// Unknown cert found, prompt the user before we overwrite the slot.
    99  			if err := promptOverwriteSlot(nonTeleportCertificateMessage(pivSlot, cert)); err != nil {
   100  				return nil, trace.Wrap(err)
   101  			}
   102  
   103  			// user confirmed, generate a new key.
   104  			fallthrough
   105  		case errors.Is(err, piv.ErrNotFound):
   106  			// no cert found, generate a new key.
   107  			priv, err := y.generatePrivateKeyAndCert(pivSlot, requiredKeyPolicy)
   108  			return priv, trace.Wrap(err)
   109  		case err != nil:
   110  			return nil, trace.Wrap(err)
   111  		}
   112  	}
   113  
   114  	// Get the key in the slot, or generate a new one if needed.
   115  	priv, err := y.getPrivateKey(pivSlot)
   116  	switch {
   117  	case err == nil && !requiredKeyPolicy.IsSatisfiedBy(priv.GetPrivateKeyPolicy()):
   118  		// Key does not meet the required key policy, prompt the user before we overwrite the slot.
   119  		msg := fmt.Sprintf("private key in YubiKey PIV slot %q does not meet private key policy %q.", pivSlot, requiredKeyPolicy)
   120  		if err := promptOverwriteSlot(msg); err != nil {
   121  			return nil, trace.Wrap(err)
   122  		}
   123  
   124  		// user confirmed, generate a new key.
   125  		fallthrough
   126  	case trace.IsNotFound(err):
   127  		// no key found, generate a new key.
   128  		priv, err = y.generatePrivateKeyAndCert(pivSlot, requiredKeyPolicy)
   129  		return priv, trace.Wrap(err)
   130  	case err != nil:
   131  		return nil, trace.Wrap(err)
   132  	}
   133  
   134  	return priv, nil
   135  }
   136  
   137  func GetDefaultKeySlot(policy PrivateKeyPolicy) (piv.Slot, error) {
   138  	switch policy {
   139  	case PrivateKeyPolicyHardwareKey:
   140  		// private_key_policy: hardware_key -> 9a
   141  		return piv.SlotAuthentication, nil
   142  	case PrivateKeyPolicyHardwareKeyTouch:
   143  		// private_key_policy: hardware_key_touch -> 9c
   144  		return piv.SlotSignature, nil
   145  	case PrivateKeyPolicyHardwareKeyTouchAndPIN:
   146  		// private_key_policy: hardware_key_touch_and_pin -> 9d
   147  		return piv.SlotKeyManagement, nil
   148  	case PrivateKeyPolicyHardwareKeyPIN:
   149  		// private_key_policy: hardware_key_pin -> 9e
   150  		return piv.SlotCardAuthentication, nil
   151  	default:
   152  		return piv.Slot{}, trace.BadParameter("unexpected private key policy %v", policy)
   153  	}
   154  }
   155  
   156  func getKeyPolicies(policy PrivateKeyPolicy) (piv.TouchPolicy, piv.PINPolicy, error) {
   157  	switch policy {
   158  	case PrivateKeyPolicyHardwareKey:
   159  		return piv.TouchPolicyNever, piv.PINPolicyNever, nil
   160  	case PrivateKeyPolicyHardwareKeyTouch:
   161  		return piv.TouchPolicyCached, piv.PINPolicyNever, nil
   162  	case PrivateKeyPolicyHardwareKeyPIN:
   163  		return piv.TouchPolicyNever, piv.PINPolicyOnce, nil
   164  	case PrivateKeyPolicyHardwareKeyTouchAndPIN:
   165  		return piv.TouchPolicyCached, piv.PINPolicyOnce, nil
   166  	default:
   167  		return piv.TouchPolicyNever, piv.PINPolicyNever, trace.BadParameter("unexpected private key policy %v", policy)
   168  	}
   169  }
   170  
   171  func nonTeleportCertificateMessage(slot piv.Slot, cert *x509.Certificate) string {
   172  	// Gather a small list of user-readable x509 certificate fields to display to the user.
   173  	sum := sha256.Sum256(cert.Raw)
   174  	fingerPrint := hex.EncodeToString(sum[:])
   175  	return fmt.Sprintf(`Certificate in YubiKey PIV slot %q is not a Teleport client cert:
   176  Slot %s:
   177  	Algorithm:		%v	
   178  	Subject DN:		%v	
   179  	Issuer DN:		%v	
   180  	Serial:			%v	
   181  	Fingerprint:	%v	
   182  	Not before:		%v	
   183  	Not after:		%v
   184  `,
   185  		slot, slot,
   186  		cert.SignatureAlgorithm,
   187  		cert.Subject,
   188  		cert.Issuer,
   189  		cert.SerialNumber,
   190  		fingerPrint,
   191  		cert.NotBefore,
   192  		cert.NotAfter,
   193  	)
   194  }
   195  
   196  // YubiKeyPrivateKey is a YubiKey PIV private key. Cryptographical operations open
   197  // a new temporary connection to the PIV card to perform the operation.
   198  type YubiKeyPrivateKey struct {
   199  	// YubiKey is a specific YubiKey PIV module.
   200  	*YubiKey
   201  
   202  	pivSlot piv.Slot
   203  	signMux sync.Mutex
   204  
   205  	slotCert        *x509.Certificate
   206  	attestationCert *x509.Certificate
   207  	attestation     *piv.Attestation
   208  }
   209  
   210  // yubiKeyPrivateKeyData is marshalable data used to retrieve a specific yubiKey PIV private key.
   211  type yubiKeyPrivateKeyData struct {
   212  	SerialNumber uint32 `json:"serial_number"`
   213  	SlotKey      uint32 `json:"slot_key"`
   214  }
   215  
   216  func parseYubiKeyPrivateKeyData(keyDataBytes []byte) (*PrivateKey, error) {
   217  	var keyData yubiKeyPrivateKeyData
   218  	if err := json.Unmarshal(keyDataBytes, &keyData); err != nil {
   219  		return nil, trace.Wrap(err)
   220  	}
   221  
   222  	pivSlot, err := parsePIVSlot(keyData.SlotKey)
   223  	if err != nil {
   224  		return nil, trace.Wrap(err)
   225  	}
   226  
   227  	y, err := FindYubiKey(keyData.SerialNumber)
   228  	if err != nil {
   229  		return nil, trace.Wrap(err)
   230  	}
   231  
   232  	priv, err := y.getPrivateKey(pivSlot)
   233  	if err != nil {
   234  		return nil, trace.Wrap(err)
   235  	}
   236  
   237  	return priv, nil
   238  }
   239  
   240  // Public returns the public key corresponding to this private key.
   241  func (y *YubiKeyPrivateKey) Public() crypto.PublicKey {
   242  	return y.slotCert.PublicKey
   243  }
   244  
   245  // Sign implements crypto.Signer.
   246  func (y *YubiKeyPrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
   247  	ctx, cancel := context.WithCancel(context.Background())
   248  	defer cancel()
   249  
   250  	// To prevent concurrent calls to Sign from failing due to PIV only handling a
   251  	// single connection, use a lock to queue through signature requests one at a time.
   252  	y.signMux.Lock()
   253  	defer y.signMux.Unlock()
   254  
   255  	signature, err := y.sign(ctx, rand, digest, opts)
   256  	if err != nil {
   257  		return nil, trace.Wrap(err)
   258  	}
   259  
   260  	return signature, nil
   261  }
   262  
   263  func (y *YubiKeyPrivateKey) sign(ctx context.Context, rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
   264  	yk, err := y.open()
   265  	if err != nil {
   266  		return nil, trace.Wrap(err)
   267  	}
   268  	defer yk.Close()
   269  
   270  	var touchPromptDelayTimer *time.Timer
   271  	if y.attestation.TouchPolicy != piv.TouchPolicyNever {
   272  		touchPromptDelayTimer = time.NewTimer(signTouchPromptDelay)
   273  		defer touchPromptDelayTimer.Stop()
   274  
   275  		go func() {
   276  			select {
   277  			case <-touchPromptDelayTimer.C:
   278  				// Prompt for touch after a delay, in case the function succeeds without touch due to a cached touch.
   279  				fmt.Fprintln(os.Stderr, "Tap your YubiKey")
   280  				return
   281  			case <-ctx.Done():
   282  				// touch cached, skip prompt.
   283  				return
   284  			}
   285  		}()
   286  	}
   287  
   288  	promptPIN := func() (string, error) {
   289  		// touch prompt delay is disrupted by pin prompts. To prevent misfired
   290  		// touch prompts, pause the timer for the duration of the pin prompt.
   291  		if touchPromptDelayTimer != nil {
   292  			if touchPromptDelayTimer.Stop() {
   293  				defer touchPromptDelayTimer.Reset(signTouchPromptDelay)
   294  			}
   295  		}
   296  		return prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Enter your YubiKey PIV PIN")
   297  	}
   298  
   299  	auth := piv.KeyAuth{
   300  		PINPrompt: promptPIN,
   301  		PINPolicy: y.attestation.PINPolicy,
   302  	}
   303  
   304  	// YubiKeys with firmware version 5.3.1 have a bug where insVerify(0x20, 0x00, 0x80, nil)
   305  	// clears the PIN cache instead of performing a non-mutable check. This causes the signature
   306  	// with pin policy "once" to fail unless PIN is provided for each call. We can avoid this bug
   307  	// by skipping the insVerify check and instead manually retrying with a PIN prompt only when
   308  	// the signature fails.
   309  	manualRetryWithPIN := false
   310  	fw531 := piv.Version{Major: 5, Minor: 3, Patch: 1}
   311  	if auth.PINPolicy == piv.PINPolicyOnce && y.attestation.Version == fw531 {
   312  		// Set the keys PIN policy to never to skip the insVerify check. If PIN was provided in
   313  		// a previous recent call, the signature will succeed as expected of the "once" policy.
   314  		auth.PINPolicy = piv.PINPolicyNever
   315  		manualRetryWithPIN = true
   316  	}
   317  
   318  	privateKey, err := yk.PrivateKey(y.pivSlot, y.slotCert.PublicKey, auth)
   319  	if err != nil {
   320  		return nil, trace.Wrap(err)
   321  	}
   322  
   323  	signer, ok := privateKey.(crypto.Signer)
   324  	if !ok {
   325  		return nil, trace.BadParameter("private key type %T does not implement crypto.Signer", privateKey)
   326  	}
   327  
   328  	// For generic auth errors, such as when PIN is not provided, the smart card returns the error code 0x6982.
   329  	// The piv-go library wraps error codes like this with a user readable message: "security status not satisfied".
   330  	const pivGenericAuthErrCodeString = "6982"
   331  
   332  	signature, err := signer.Sign(rand, digest, opts)
   333  	switch {
   334  	case err == nil:
   335  		return signature, nil
   336  	case manualRetryWithPIN && strings.Contains(err.Error(), pivGenericAuthErrCodeString):
   337  		pin, err := promptPIN()
   338  		if err != nil {
   339  			return nil, trace.Wrap(err)
   340  		}
   341  		if err := yk.VerifyPIN(pin); err != nil {
   342  			return nil, trace.Wrap(err)
   343  		}
   344  		signature, err := signer.Sign(rand, digest, opts)
   345  		return signature, trace.Wrap(err)
   346  	default:
   347  		return nil, trace.Wrap(err)
   348  	}
   349  }
   350  
   351  func (y *YubiKeyPrivateKey) toPrivateKey() (*PrivateKey, error) {
   352  	keyPEM, err := y.keyPEM()
   353  	if err != nil {
   354  		return nil, trace.Wrap(err)
   355  	}
   356  
   357  	return NewPrivateKey(y, keyPEM)
   358  }
   359  
   360  func (y *YubiKeyPrivateKey) keyPEM() ([]byte, error) {
   361  	keyDataBytes, err := json.Marshal(yubiKeyPrivateKeyData{
   362  		SerialNumber: y.serialNumber,
   363  		SlotKey:      y.pivSlot.Key,
   364  	})
   365  	if err != nil {
   366  		return nil, trace.Wrap(err)
   367  	}
   368  
   369  	return pem.EncodeToMemory(&pem.Block{
   370  		Type:    pivYubiKeyPrivateKeyType,
   371  		Headers: nil,
   372  		Bytes:   keyDataBytes,
   373  	}), nil
   374  }
   375  
   376  // GetAttestationStatement returns an AttestationStatement for this YubiKeyPrivateKey.
   377  func (y *YubiKeyPrivateKey) GetAttestationStatement() *AttestationStatement {
   378  	return &AttestationStatement{
   379  		AttestationStatement: &attestation.AttestationStatement_YubikeyAttestationStatement{
   380  			YubikeyAttestationStatement: &attestation.YubiKeyAttestationStatement{
   381  				SlotCert:        y.slotCert.Raw,
   382  				AttestationCert: y.attestationCert.Raw,
   383  			},
   384  		},
   385  	}
   386  }
   387  
   388  // GetPrivateKeyPolicy returns the PrivateKeyPolicy supported by this YubiKeyPrivateKey.
   389  func (y *YubiKeyPrivateKey) GetPrivateKeyPolicy() PrivateKeyPolicy {
   390  	return GetPrivateKeyPolicyFromAttestation(y.attestation)
   391  }
   392  
   393  // GetPrivateKeyPolicyFromAttestation returns the PrivateKeyPolicy satisfied by the given hardware key attestation.
   394  func GetPrivateKeyPolicyFromAttestation(att *piv.Attestation) PrivateKeyPolicy {
   395  	isTouchPolicy := att.TouchPolicy == piv.TouchPolicyCached ||
   396  		att.TouchPolicy == piv.TouchPolicyAlways
   397  
   398  	isPINPolicy := att.PINPolicy == piv.PINPolicyOnce ||
   399  		att.PINPolicy == piv.PINPolicyAlways
   400  
   401  	switch {
   402  	case isPINPolicy && isTouchPolicy:
   403  		return PrivateKeyPolicyHardwareKeyTouchAndPIN
   404  	case isPINPolicy:
   405  		return PrivateKeyPolicyHardwareKeyPIN
   406  	case isTouchPolicy:
   407  		return PrivateKeyPolicyHardwareKeyTouch
   408  	default:
   409  		return PrivateKeyPolicyHardwareKey
   410  	}
   411  }
   412  
   413  // YubiKey is a specific YubiKey PIV card.
   414  type YubiKey struct {
   415  	// card is a reader name used to find and connect to this yubiKey.
   416  	// This value may change between OS's, or with other system changes.
   417  	card string
   418  	// serialNumber is the yubiKey's 8 digit serial number.
   419  	serialNumber uint32
   420  }
   421  
   422  func newYubiKey(card string) (*YubiKey, error) {
   423  	y := &YubiKey{card: card}
   424  
   425  	yk, err := y.open()
   426  	if err != nil {
   427  		return nil, trace.Wrap(err)
   428  	}
   429  	defer yk.Close()
   430  
   431  	y.serialNumber, err = yk.Serial()
   432  	if err != nil {
   433  		return nil, trace.Wrap(err)
   434  	}
   435  
   436  	return y, nil
   437  }
   438  
   439  // Reset resets the YubiKey PIV module to default settings.
   440  func (y *YubiKey) Reset() error {
   441  	yk, err := y.open()
   442  	if err != nil {
   443  		return trace.Wrap(err)
   444  	}
   445  	defer yk.Close()
   446  
   447  	err = yk.Reset()
   448  	return trace.Wrap(err)
   449  }
   450  
   451  // generatePrivateKeyAndCert generates a new private key and client metadata cert in the given PIV slot.
   452  func (y *YubiKey) generatePrivateKeyAndCert(slot piv.Slot, requiredKeyPolicy PrivateKeyPolicy) (*PrivateKey, error) {
   453  	if err := y.generatePrivateKey(slot, requiredKeyPolicy); err != nil {
   454  		return nil, trace.Wrap(err)
   455  	}
   456  
   457  	if err := y.SetMetadataCertificate(slot, pkix.Name{
   458  		Organization:       []string{certOrgName},
   459  		OrganizationalUnit: []string{api.Version},
   460  	}); err != nil {
   461  		return nil, trace.Wrap(err)
   462  	}
   463  
   464  	return y.getPrivateKey(slot)
   465  }
   466  
   467  // SetMetadataCertificate creates a self signed certificate and stores it in the YubiKey's
   468  // PIV certificate slot. This certificate is purely used as metadata to determine when a
   469  // slot is in used by a Teleport Client and is not fit to be used in cryptographic operations.
   470  // This cert is also useful for users to discern where the key came with tools like `ykman piv info`.
   471  func (y *YubiKey) SetMetadataCertificate(slot piv.Slot, subject pkix.Name) error {
   472  	yk, err := y.open()
   473  	if err != nil {
   474  		return trace.Wrap(err)
   475  	}
   476  	defer yk.Close()
   477  
   478  	cert, err := SelfSignedMetadataCertificate(subject)
   479  	if err != nil {
   480  		return trace.Wrap(err)
   481  	}
   482  
   483  	err = yk.SetCertificate(piv.DefaultManagementKey, slot, cert)
   484  	return trace.Wrap(err)
   485  }
   486  
   487  // getCertificate gets a certificate from the given PIV slot.
   488  func (y *YubiKey) getCertificate(slot piv.Slot) (*x509.Certificate, error) {
   489  	yk, err := y.open()
   490  	if err != nil {
   491  		return nil, trace.Wrap(err)
   492  	}
   493  	defer yk.Close()
   494  
   495  	cert, err := yk.Certificate(slot)
   496  	return cert, trace.Wrap(err)
   497  }
   498  
   499  // generatePrivateKey generates a new private key in the given PIV slot.
   500  func (y *YubiKey) generatePrivateKey(slot piv.Slot, requiredKeyPolicy PrivateKeyPolicy) error {
   501  	yk, err := y.open()
   502  	if err != nil {
   503  		return trace.Wrap(err)
   504  	}
   505  	defer yk.Close()
   506  
   507  	touchPolicy, pinPolicy, err := getKeyPolicies(requiredKeyPolicy)
   508  	if err != nil {
   509  		return trace.Wrap(err)
   510  	}
   511  
   512  	opts := piv.Key{
   513  		Algorithm:   piv.AlgorithmEC256,
   514  		PINPolicy:   pinPolicy,
   515  		TouchPolicy: touchPolicy,
   516  	}
   517  
   518  	_, err = yk.GenerateKey(piv.DefaultManagementKey, slot, opts)
   519  	return trace.Wrap(err)
   520  }
   521  
   522  // getPrivateKey gets an existing private key from the given PIV slot.
   523  func (y *YubiKey) getPrivateKey(slot piv.Slot) (*PrivateKey, error) {
   524  	yk, err := y.open()
   525  	if err != nil {
   526  		return nil, trace.Wrap(err)
   527  	}
   528  	defer yk.Close()
   529  
   530  	slotCert, err := yk.Attest(slot)
   531  	if errors.Is(err, piv.ErrNotFound) {
   532  		return nil, trace.NotFound("private key in YubiKey PIV slot %q not found.", slot.String())
   533  	} else if err != nil {
   534  		return nil, trace.Wrap(err)
   535  	}
   536  
   537  	attCert, err := yk.AttestationCertificate()
   538  	if err != nil {
   539  		return nil, trace.Wrap(err)
   540  	}
   541  
   542  	attestation, err := piv.Verify(attCert, slotCert)
   543  	if err != nil {
   544  		return nil, trace.Wrap(err)
   545  	}
   546  
   547  	priv := &YubiKeyPrivateKey{
   548  		YubiKey:         y,
   549  		pivSlot:         slot,
   550  		slotCert:        slotCert,
   551  		attestationCert: attCert,
   552  		attestation:     attestation,
   553  	}
   554  
   555  	keyPEM, err := priv.keyPEM()
   556  	if err != nil {
   557  		return nil, trace.Wrap(err)
   558  	}
   559  
   560  	key, err := NewPrivateKey(priv, keyPEM)
   561  	if err != nil {
   562  		return nil, trace.Wrap(err)
   563  	}
   564  
   565  	return key, nil
   566  }
   567  
   568  // SetPin sets the YubiKey PIV PIN. This doesn't require user interaction like touch, just the correct old PIN.
   569  func (y *YubiKey) SetPIN(oldPin, newPin string) error {
   570  	yk, err := y.open()
   571  	if err != nil {
   572  		return trace.Wrap(err)
   573  	}
   574  	defer yk.Close()
   575  
   576  	err = yk.SetPIN(oldPin, newPin)
   577  	return trace.Wrap(err)
   578  }
   579  
   580  // checkOrSetPIN prompts the user for PIN and verifies it with the YubiKey.
   581  // If the user provides the default PIN, they will be prompted to set a
   582  // non-default PIN and PUK before continuing.
   583  func (y *YubiKey) checkOrSetPIN(ctx context.Context) error {
   584  	pin, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Enter your YubiKey PIV PIN [blank to use default PIN]")
   585  	if err != nil {
   586  		return trace.Wrap(err)
   587  	}
   588  
   589  	yk, err := y.open()
   590  	if err != nil {
   591  		return trace.Wrap(err)
   592  	}
   593  	defer yk.Close()
   594  
   595  	switch pin {
   596  	case piv.DefaultPIN:
   597  		fmt.Fprintf(os.Stderr, "The default PIN %q is not supported.\n", piv.DefaultPIN)
   598  		fallthrough
   599  	case "":
   600  		if pin, err = setPINAndPUKFromDefault(ctx, yk); err != nil {
   601  			return trace.Wrap(err)
   602  		}
   603  	}
   604  
   605  	return trace.Wrap(yk.VerifyPIN(pin))
   606  }
   607  
   608  // open a connection to YubiKey PIV module. The returned connection should be closed once
   609  // it's been used. The YubiKey PIV module itself takes some additional time to handle closed
   610  // connections, so we use a retry loop to give the PIV module time to close prior connections.
   611  func (y *YubiKey) open() (yk *piv.YubiKey, err error) {
   612  	linearRetry, err := retryutils.NewLinear(retryutils.LinearConfig{
   613  		// If a PIV connection has just been closed, it take ~5 ms to become
   614  		// available to new connections. For this reason, we initially wait a
   615  		// short 10ms before stepping up to a longer 50ms retry.
   616  		First: time.Millisecond * 10,
   617  		Step:  time.Millisecond * 10,
   618  		// Since PIV modules only allow a single connection, it is a bottleneck
   619  		// resource. To maximize usage, we use a short 50ms retry to catch the
   620  		// connection opening up as soon as possible.
   621  		Max: time.Millisecond * 50,
   622  	})
   623  	if err != nil {
   624  		return nil, trace.Wrap(err)
   625  	}
   626  
   627  	// Backoff and retry for up to 1 second.
   628  	retryCtx, cancel := context.WithTimeout(context.Background(), time.Second)
   629  	defer cancel()
   630  
   631  	err = linearRetry.For(retryCtx, func() error {
   632  		yk, err = piv.Open(y.card)
   633  		if err != nil && !isRetryError(err) {
   634  			return retryutils.PermanentRetryError(err)
   635  		}
   636  		return trace.Wrap(err)
   637  	})
   638  	if trace.IsLimitExceeded(err) {
   639  		// Using PIV synchronously causes issues since only one connection is allowed at a time.
   640  		// This shouldn't be an issue for `tsh` which primarily runs consecutively, but Teleport
   641  		// Connect works through callbacks, etc. and may try to open multiple connections at a time.
   642  		// If this error is being emitted more than rarely, the 1 second timeout may need to be increased.
   643  		//
   644  		// It's also possible that the user is running another PIV program, which may hold the PIV
   645  		// connection indefinitely (yubikey-agent). In this case, user action is necessary, so we
   646  		// alert them with this issue.
   647  		return nil, trace.LimitExceeded("could not connect to YubiKey as another application is using it. Please try again once the program that uses the YubiKey, such as yubikey-agent is closed")
   648  	} else if err != nil {
   649  		return nil, trace.Wrap(err)
   650  	}
   651  	return yk, nil
   652  }
   653  
   654  func isRetryError(err error) bool {
   655  	const retryError = "connecting to smart card: the smart card cannot be accessed because of other connections outstanding"
   656  	return strings.Contains(err.Error(), retryError)
   657  }
   658  
   659  // FindYubiKey finds a yubiKey PIV card by serial number. If no serial
   660  // number is provided, the first yubiKey found will be returned.
   661  func FindYubiKey(serialNumber uint32) (*YubiKey, error) {
   662  	yubiKeyCards, err := findYubiKeyCards()
   663  	if err != nil {
   664  		return nil, trace.Wrap(err)
   665  	}
   666  
   667  	if len(yubiKeyCards) == 0 {
   668  		if serialNumber != 0 {
   669  			return nil, trace.ConnectionProblem(nil, "no YubiKey device connected with serial number %d", serialNumber)
   670  		}
   671  		return nil, trace.ConnectionProblem(nil, "no YubiKey device connected")
   672  	}
   673  
   674  	for _, card := range yubiKeyCards {
   675  		y, err := newYubiKey(card)
   676  		if err != nil {
   677  			return nil, trace.Wrap(err)
   678  		}
   679  
   680  		if serialNumber == 0 || y.serialNumber == serialNumber {
   681  			return y, nil
   682  		}
   683  	}
   684  
   685  	return nil, trace.ConnectionProblem(nil, "no YubiKey device connected with serial number %d", serialNumber)
   686  }
   687  
   688  // findYubiKeyCards returns a list of connected yubiKey PIV card names.
   689  func findYubiKeyCards() ([]string, error) {
   690  	cards, err := piv.Cards()
   691  	if err != nil {
   692  		return nil, trace.Wrap(err)
   693  	}
   694  
   695  	var yubiKeyCards []string
   696  	for _, card := range cards {
   697  		if strings.Contains(strings.ToLower(card), PIVCardTypeYubiKey) {
   698  			yubiKeyCards = append(yubiKeyCards, card)
   699  		}
   700  	}
   701  
   702  	return yubiKeyCards, nil
   703  }
   704  
   705  func (s PIVSlot) validate() error {
   706  	_, err := s.parse()
   707  	return trace.Wrap(err)
   708  }
   709  
   710  func (s PIVSlot) parse() (piv.Slot, error) {
   711  	slotKey, err := strconv.ParseUint(string(s), 16, 32)
   712  	if err != nil {
   713  		return piv.Slot{}, trace.Wrap(err)
   714  	}
   715  
   716  	return parsePIVSlot(uint32(slotKey))
   717  }
   718  
   719  func parsePIVSlotString(slotKeyString string) (piv.Slot, error) {
   720  	slotKey, err := strconv.ParseUint(slotKeyString, 16, 32)
   721  	if err != nil {
   722  		return piv.Slot{}, trace.Wrap(err)
   723  	}
   724  
   725  	return parsePIVSlot(uint32(slotKey))
   726  }
   727  
   728  func parsePIVSlot(slotKey uint32) (piv.Slot, error) {
   729  	switch slotKey {
   730  	case piv.SlotAuthentication.Key:
   731  		return piv.SlotAuthentication, nil
   732  	case piv.SlotSignature.Key:
   733  		return piv.SlotSignature, nil
   734  	case piv.SlotKeyManagement.Key:
   735  		return piv.SlotKeyManagement, nil
   736  	case piv.SlotCardAuthentication.Key:
   737  		return piv.SlotCardAuthentication, nil
   738  	default:
   739  		retiredSlot, ok := piv.RetiredKeyManagementSlot(slotKey)
   740  		if !ok {
   741  			return piv.Slot{}, trace.BadParameter("slot %X does not exist", slotKey)
   742  		}
   743  		return retiredSlot, nil
   744  	}
   745  }
   746  
   747  // certOrgName is used to identify Teleport Client self-signed certificates stored in yubiKey PIV slots.
   748  const certOrgName = "teleport"
   749  
   750  func SelfSignedMetadataCertificate(subject pkix.Name) (*x509.Certificate, error) {
   751  	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   752  	if err != nil {
   753  		return nil, trace.Wrap(err)
   754  	}
   755  
   756  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
   757  	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) // see crypto/tls/generate_cert.go
   758  	if err != nil {
   759  		return nil, trace.Wrap(err)
   760  	}
   761  	cert := &x509.Certificate{
   762  		SerialNumber: serialNumber,
   763  		Subject:      subject,
   764  		PublicKey:    priv.Public(),
   765  	}
   766  
   767  	if cert.Raw, err = x509.CreateCertificate(rand.Reader, cert, cert, priv.Public(), priv); err != nil {
   768  		return nil, trace.Wrap(err)
   769  	}
   770  	return cert, nil
   771  }
   772  
   773  // YubiKeys require touch when signing with a private key that requires touch.
   774  // Unfortunately, there is no good way to check whether touch is cached by the
   775  // PIV module at a given time. In order to require touch only when needed, we
   776  // prompt for touch after a short delay when we expect the request would succeed
   777  // if touch were not required.
   778  //
   779  // There are some X factors which determine how long a request may take, such as the
   780  // YubiKey model and firmware version, so the delays below may need to be adjusted to
   781  // suit more models. The durations mentioned below were retrieved from testing with a
   782  // YubiKey 5 nano (5.2.7) and a YubiKey NFC (5.4.3).
   783  const (
   784  	// piv.ECDSAPrivateKey.Sign consistently takes ~70 milliseconds. However, 200ms
   785  	// should be imperceptible to the user and should avoid misfired prompts for
   786  	// slower cards (if there are any).
   787  	signTouchPromptDelay = time.Millisecond * 200
   788  )
   789  
   790  func setPINAndPUKFromDefault(ctx context.Context, yk *piv.YubiKey) (string, error) {
   791  	// YubiKey requires that PIN and PUK be 6-8 characters.
   792  	isValid := func(pin string) bool {
   793  		return len(pin) >= 6 && len(pin) <= 8
   794  	}
   795  
   796  	var pin string
   797  	for {
   798  		fmt.Fprintf(os.Stderr, "Please set a new 6-8 character PIN.\n")
   799  		newPIN, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Enter your new YubiKey PIV PIN")
   800  		if err != nil {
   801  			return "", trace.Wrap(err)
   802  		}
   803  		newPINConfirm, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Confirm your new YubiKey PIV PIN")
   804  		if err != nil {
   805  			return "", trace.Wrap(err)
   806  		}
   807  
   808  		if newPIN != newPINConfirm {
   809  			fmt.Fprintf(os.Stderr, "PINs do not match.\n")
   810  			continue
   811  		}
   812  
   813  		if newPIN == piv.DefaultPIN {
   814  			fmt.Fprintf(os.Stderr, "The default PIN %q is not supported.\n", piv.DefaultPIN)
   815  			continue
   816  		}
   817  
   818  		if !isValid(newPIN) {
   819  			fmt.Fprintf(os.Stderr, "PIN must be 6-8 characters long.\n")
   820  			continue
   821  		}
   822  
   823  		pin = newPIN
   824  		break
   825  	}
   826  
   827  	puk, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Enter your YubiKey PIV PUK to reset PIN [blank to use default PUK]")
   828  	if err != nil {
   829  		return "", trace.Wrap(err)
   830  	}
   831  
   832  	switch puk {
   833  	case piv.DefaultPUK:
   834  		fmt.Fprintf(os.Stderr, "The default PUK %q is not supported.\n", piv.DefaultPUK)
   835  		fallthrough
   836  	case "":
   837  		for {
   838  			fmt.Fprintf(os.Stderr, "Please set a new 6-8 character PUK (used to reset PIN).\n")
   839  			newPUK, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Enter your new YubiKey PIV PUK")
   840  			if err != nil {
   841  				return "", trace.Wrap(err)
   842  			}
   843  			newPUKConfirm, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Confirm your new YubiKey PIV PUK")
   844  			if err != nil {
   845  				return "", trace.Wrap(err)
   846  			}
   847  
   848  			if newPUK != newPUKConfirm {
   849  				fmt.Fprintf(os.Stderr, "PUKs do not match.\n")
   850  				continue
   851  			}
   852  
   853  			if newPUK == piv.DefaultPUK {
   854  				fmt.Fprintf(os.Stderr, "The default PUK %q is not supported.\n", piv.DefaultPUK)
   855  				continue
   856  			}
   857  
   858  			if !isValid(newPUK) {
   859  				fmt.Fprintf(os.Stderr, "PUK must be 6-8 characters long.\n")
   860  				continue
   861  			}
   862  
   863  			if err := yk.SetPUK(piv.DefaultPUK, newPUK); err != nil {
   864  				return "", trace.Wrap(err)
   865  			}
   866  
   867  			puk = newPUK
   868  			break
   869  		}
   870  	}
   871  
   872  	if err := yk.Unblock(puk, pin); err != nil {
   873  		return "", trace.Wrap(err)
   874  	}
   875  
   876  	return pin, nil
   877  }