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 }