1// Copyright 2012 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package ssh 6 7import ( 8 "bytes" 9 "crypto" 10 "crypto/aes" 11 "crypto/cipher" 12 "crypto/dsa" 13 "crypto/ecdsa" 14 "crypto/elliptic" 15 "crypto/md5" 16 "crypto/rsa" 17 "crypto/sha256" 18 "crypto/x509" 19 "encoding/asn1" 20 "encoding/base64" 21 "encoding/hex" 22 "encoding/pem" 23 "errors" 24 "fmt" 25 "io" 26 "math/big" 27 "strings" 28 29 "golang.org/x/crypto/ed25519" 30 "golang.org/x/crypto/ssh/internal/bcrypt_pbkdf" 31) 32 33// These constants represent the algorithm names for key types supported by this 34// package. 35const ( 36 KeyAlgoRSA = "ssh-rsa" 37 KeyAlgoDSA = "ssh-dss" 38 KeyAlgoECDSA256 = "ecdsa-sha2-nistp256" 39 KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com" 40 KeyAlgoECDSA384 = "ecdsa-sha2-nistp384" 41 KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" 42 KeyAlgoED25519 = "ssh-ed25519" 43 KeyAlgoSKED25519 = "sk-ssh-ed25519@openssh.com" 44) 45 46// These constants represent non-default signature algorithms that are supported 47// as algorithm parameters to AlgorithmSigner.SignWithAlgorithm methods. See 48// [PROTOCOL.agent] section 4.5.1 and 49// https://tools.ietf.org/html/draft-ietf-curdle-rsa-sha2-10 50const ( 51 SigAlgoRSA = "ssh-rsa" 52 SigAlgoRSASHA2256 = "rsa-sha2-256" 53 SigAlgoRSASHA2512 = "rsa-sha2-512" 54) 55 56// parsePubKey parses a public key of the given algorithm. 57// Use ParsePublicKey for keys with prepended algorithm. 58func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) { 59 switch algo { 60 case KeyAlgoRSA: 61 return parseRSA(in) 62 case KeyAlgoDSA: 63 return parseDSA(in) 64 case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: 65 return parseECDSA(in) 66 case KeyAlgoSKECDSA256: 67 return parseSKECDSA(in) 68 case KeyAlgoED25519: 69 return parseED25519(in) 70 case KeyAlgoSKED25519: 71 return parseSKEd25519(in) 72 case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01: 73 cert, err := parseCert(in, certToPrivAlgo(algo)) 74 if err != nil { 75 return nil, nil, err 76 } 77 return cert, nil, nil 78 } 79 return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo) 80} 81 82// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format 83// (see sshd(8) manual page) once the options and key type fields have been 84// removed. 85func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) { 86 in = bytes.TrimSpace(in) 87 88 i := bytes.IndexAny(in, " \t") 89 if i == -1 { 90 i = len(in) 91 } 92 base64Key := in[:i] 93 94 key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key))) 95 n, err := base64.StdEncoding.Decode(key, base64Key) 96 if err != nil { 97 return nil, "", err 98 } 99 key = key[:n] 100 out, err = ParsePublicKey(key) 101 if err != nil { 102 return nil, "", err 103 } 104 comment = string(bytes.TrimSpace(in[i:])) 105 return out, comment, nil 106} 107 108// ParseKnownHosts parses an entry in the format of the known_hosts file. 109// 110// The known_hosts format is documented in the sshd(8) manual page. This 111// function will parse a single entry from in. On successful return, marker 112// will contain the optional marker value (i.e. "cert-authority" or "revoked") 113// or else be empty, hosts will contain the hosts that this entry matches, 114// pubKey will contain the public key and comment will contain any trailing 115// comment at the end of the line. See the sshd(8) manual page for the various 116// forms that a host string can take. 117// 118// The unparsed remainder of the input will be returned in rest. This function 119// can be called repeatedly to parse multiple entries. 120// 121// If no entries were found in the input then err will be io.EOF. Otherwise a 122// non-nil err value indicates a parse error. 123func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey, comment string, rest []byte, err error) { 124 for len(in) > 0 { 125 end := bytes.IndexByte(in, '\n') 126 if end != -1 { 127 rest = in[end+1:] 128 in = in[:end] 129 } else { 130 rest = nil 131 } 132 133 end = bytes.IndexByte(in, '\r') 134 if end != -1 { 135 in = in[:end] 136 } 137 138 in = bytes.TrimSpace(in) 139 if len(in) == 0 || in[0] == '#' { 140 in = rest 141 continue 142 } 143 144 i := bytes.IndexAny(in, " \t") 145 if i == -1 { 146 in = rest 147 continue 148 } 149 150 // Strip out the beginning of the known_host key. 151 // This is either an optional marker or a (set of) hostname(s). 152 keyFields := bytes.Fields(in) 153 if len(keyFields) < 3 || len(keyFields) > 5 { 154 return "", nil, nil, "", nil, errors.New("ssh: invalid entry in known_hosts data") 155 } 156 157 // keyFields[0] is either "@cert-authority", "@revoked" or a comma separated 158 // list of hosts 159 marker := "" 160 if keyFields[0][0] == '@' { 161 marker = string(keyFields[0][1:]) 162 keyFields = keyFields[1:] 163 } 164 165 hosts := string(keyFields[0]) 166 // keyFields[1] contains the key type (e.g. “ssh-rsa”). 167 // However, that information is duplicated inside the 168 // base64-encoded key and so is ignored here. 169 170 key := bytes.Join(keyFields[2:], []byte(" ")) 171 if pubKey, comment, err = parseAuthorizedKey(key); err != nil { 172 return "", nil, nil, "", nil, err 173 } 174 175 return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil 176 } 177 178 return "", nil, nil, "", nil, io.EOF 179} 180 181// ParseAuthorizedKeys parses a public key from an authorized_keys 182// file used in OpenSSH according to the sshd(8) manual page. 183func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { 184 for len(in) > 0 { 185 end := bytes.IndexByte(in, '\n') 186 if end != -1 { 187 rest = in[end+1:] 188 in = in[:end] 189 } else { 190 rest = nil 191 } 192 193 end = bytes.IndexByte(in, '\r') 194 if end != -1 { 195 in = in[:end] 196 } 197 198 in = bytes.TrimSpace(in) 199 if len(in) == 0 || in[0] == '#' { 200 in = rest 201 continue 202 } 203 204 i := bytes.IndexAny(in, " \t") 205 if i == -1 { 206 in = rest 207 continue 208 } 209 210 if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { 211 return out, comment, options, rest, nil 212 } 213 214 // No key type recognised. Maybe there's an options field at 215 // the beginning. 216 var b byte 217 inQuote := false 218 var candidateOptions []string 219 optionStart := 0 220 for i, b = range in { 221 isEnd := !inQuote && (b == ' ' || b == '\t') 222 if (b == ',' && !inQuote) || isEnd { 223 if i-optionStart > 0 { 224 candidateOptions = append(candidateOptions, string(in[optionStart:i])) 225 } 226 optionStart = i + 1 227 } 228 if isEnd { 229 break 230 } 231 if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) { 232 inQuote = !inQuote 233 } 234 } 235 for i < len(in) && (in[i] == ' ' || in[i] == '\t') { 236 i++ 237 } 238 if i == len(in) { 239 // Invalid line: unmatched quote 240 in = rest 241 continue 242 } 243 244 in = in[i:] 245 i = bytes.IndexAny(in, " \t") 246 if i == -1 { 247 in = rest 248 continue 249 } 250 251 if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { 252 options = candidateOptions 253 return out, comment, options, rest, nil 254 } 255 256 in = rest 257 continue 258 } 259 260 return nil, "", nil, nil, errors.New("ssh: no key found") 261} 262 263// ParsePublicKey parses an SSH public key formatted for use in 264// the SSH wire protocol according to RFC 4253, section 6.6. 265func ParsePublicKey(in []byte) (out PublicKey, err error) { 266 algo, in, ok := parseString(in) 267 if !ok { 268 return nil, errShortRead 269 } 270 var rest []byte 271 out, rest, err = parsePubKey(in, string(algo)) 272 if len(rest) > 0 { 273 return nil, errors.New("ssh: trailing junk in public key") 274 } 275 276 return out, err 277} 278 279// MarshalAuthorizedKey serializes key for inclusion in an OpenSSH 280// authorized_keys file. The return value ends with newline. 281func MarshalAuthorizedKey(key PublicKey) []byte { 282 b := &bytes.Buffer{} 283 b.WriteString(key.Type()) 284 b.WriteByte(' ') 285 e := base64.NewEncoder(base64.StdEncoding, b) 286 e.Write(key.Marshal()) 287 e.Close() 288 b.WriteByte('\n') 289 return b.Bytes() 290} 291 292// PublicKey is an abstraction of different types of public keys. 293type PublicKey interface { 294 // Type returns the key's type, e.g. "ssh-rsa". 295 Type() string 296 297 // Marshal returns the serialized key data in SSH wire format, 298 // with the name prefix. To unmarshal the returned data, use 299 // the ParsePublicKey function. 300 Marshal() []byte 301 302 // Verify that sig is a signature on the given data using this 303 // key. This function will hash the data appropriately first. 304 Verify(data []byte, sig *Signature) error 305} 306 307// CryptoPublicKey, if implemented by a PublicKey, 308// returns the underlying crypto.PublicKey form of the key. 309type CryptoPublicKey interface { 310 CryptoPublicKey() crypto.PublicKey 311} 312 313// A Signer can create signatures that verify against a public key. 314type Signer interface { 315 // PublicKey returns an associated PublicKey instance. 316 PublicKey() PublicKey 317 318 // Sign returns raw signature for the given data. This method 319 // will apply the hash specified for the keytype to the data. 320 Sign(rand io.Reader, data []byte) (*Signature, error) 321} 322 323// A AlgorithmSigner is a Signer that also supports specifying a specific 324// algorithm to use for signing. 325type AlgorithmSigner interface { 326 Signer 327 328 // SignWithAlgorithm is like Signer.Sign, but allows specification of a 329 // non-default signing algorithm. See the SigAlgo* constants in this 330 // package for signature algorithms supported by this package. Callers may 331 // pass an empty string for the algorithm in which case the AlgorithmSigner 332 // will use its default algorithm. 333 SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) 334} 335 336type rsaPublicKey rsa.PublicKey 337 338func (r *rsaPublicKey) Type() string { 339 return "ssh-rsa" 340} 341 342// parseRSA parses an RSA key according to RFC 4253, section 6.6. 343func parseRSA(in []byte) (out PublicKey, rest []byte, err error) { 344 var w struct { 345 E *big.Int 346 N *big.Int 347 Rest []byte `ssh:"rest"` 348 } 349 if err := Unmarshal(in, &w); err != nil { 350 return nil, nil, err 351 } 352 353 if w.E.BitLen() > 24 { 354 return nil, nil, errors.New("ssh: exponent too large") 355 } 356 e := w.E.Int64() 357 if e < 3 || e&1 == 0 { 358 return nil, nil, errors.New("ssh: incorrect exponent") 359 } 360 361 var key rsa.PublicKey 362 key.E = int(e) 363 key.N = w.N 364 return (*rsaPublicKey)(&key), w.Rest, nil 365} 366 367func (r *rsaPublicKey) Marshal() []byte { 368 e := new(big.Int).SetInt64(int64(r.E)) 369 // RSA publickey struct layout should match the struct used by 370 // parseRSACert in the x/crypto/ssh/agent package. 371 wirekey := struct { 372 Name string 373 E *big.Int 374 N *big.Int 375 }{ 376 KeyAlgoRSA, 377 e, 378 r.N, 379 } 380 return Marshal(&wirekey) 381} 382 383func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { 384 var hash crypto.Hash 385 switch sig.Format { 386 case SigAlgoRSA: 387 hash = crypto.SHA1 388 case SigAlgoRSASHA2256: 389 hash = crypto.SHA256 390 case SigAlgoRSASHA2512: 391 hash = crypto.SHA512 392 default: 393 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type()) 394 } 395 h := hash.New() 396 h.Write(data) 397 digest := h.Sum(nil) 398 return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), hash, digest, sig.Blob) 399} 400 401func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey { 402 return (*rsa.PublicKey)(r) 403} 404 405type dsaPublicKey dsa.PublicKey 406 407func (k *dsaPublicKey) Type() string { 408 return "ssh-dss" 409} 410 411func checkDSAParams(param *dsa.Parameters) error { 412 // SSH specifies FIPS 186-2, which only provided a single size 413 // (1024 bits) DSA key. FIPS 186-3 allows for larger key 414 // sizes, which would confuse SSH. 415 if l := param.P.BitLen(); l != 1024 { 416 return fmt.Errorf("ssh: unsupported DSA key size %d", l) 417 } 418 419 return nil 420} 421 422// parseDSA parses an DSA key according to RFC 4253, section 6.6. 423func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { 424 var w struct { 425 P, Q, G, Y *big.Int 426 Rest []byte `ssh:"rest"` 427 } 428 if err := Unmarshal(in, &w); err != nil { 429 return nil, nil, err 430 } 431 432 param := dsa.Parameters{ 433 P: w.P, 434 Q: w.Q, 435 G: w.G, 436 } 437 if err := checkDSAParams(¶m); err != nil { 438 return nil, nil, err 439 } 440 441 key := &dsaPublicKey{ 442 Parameters: param, 443 Y: w.Y, 444 } 445 return key, w.Rest, nil 446} 447 448func (k *dsaPublicKey) Marshal() []byte { 449 // DSA publickey struct layout should match the struct used by 450 // parseDSACert in the x/crypto/ssh/agent package. 451 w := struct { 452 Name string 453 P, Q, G, Y *big.Int 454 }{ 455 k.Type(), 456 k.P, 457 k.Q, 458 k.G, 459 k.Y, 460 } 461 462 return Marshal(&w) 463} 464 465func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error { 466 if sig.Format != k.Type() { 467 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) 468 } 469 h := crypto.SHA1.New() 470 h.Write(data) 471 digest := h.Sum(nil) 472 473 // Per RFC 4253, section 6.6, 474 // The value for 'dss_signature_blob' is encoded as a string containing 475 // r, followed by s (which are 160-bit integers, without lengths or 476 // padding, unsigned, and in network byte order). 477 // For DSS purposes, sig.Blob should be exactly 40 bytes in length. 478 if len(sig.Blob) != 40 { 479 return errors.New("ssh: DSA signature parse error") 480 } 481 r := new(big.Int).SetBytes(sig.Blob[:20]) 482 s := new(big.Int).SetBytes(sig.Blob[20:]) 483 if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) { 484 return nil 485 } 486 return errors.New("ssh: signature did not verify") 487} 488 489func (k *dsaPublicKey) CryptoPublicKey() crypto.PublicKey { 490 return (*dsa.PublicKey)(k) 491} 492 493type dsaPrivateKey struct { 494 *dsa.PrivateKey 495} 496 497func (k *dsaPrivateKey) PublicKey() PublicKey { 498 return (*dsaPublicKey)(&k.PrivateKey.PublicKey) 499} 500 501func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { 502 return k.SignWithAlgorithm(rand, data, "") 503} 504 505func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { 506 if algorithm != "" && algorithm != k.PublicKey().Type() { 507 return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) 508 } 509 510 h := crypto.SHA1.New() 511 h.Write(data) 512 digest := h.Sum(nil) 513 r, s, err := dsa.Sign(rand, k.PrivateKey, digest) 514 if err != nil { 515 return nil, err 516 } 517 518 sig := make([]byte, 40) 519 rb := r.Bytes() 520 sb := s.Bytes() 521 522 copy(sig[20-len(rb):20], rb) 523 copy(sig[40-len(sb):], sb) 524 525 return &Signature{ 526 Format: k.PublicKey().Type(), 527 Blob: sig, 528 }, nil 529} 530 531type ecdsaPublicKey ecdsa.PublicKey 532 533func (k *ecdsaPublicKey) Type() string { 534 return "ecdsa-sha2-" + k.nistID() 535} 536 537func (k *ecdsaPublicKey) nistID() string { 538 switch k.Params().BitSize { 539 case 256: 540 return "nistp256" 541 case 384: 542 return "nistp384" 543 case 521: 544 return "nistp521" 545 } 546 panic("ssh: unsupported ecdsa key size") 547} 548 549type ed25519PublicKey ed25519.PublicKey 550 551func (k ed25519PublicKey) Type() string { 552 return KeyAlgoED25519 553} 554 555func parseED25519(in []byte) (out PublicKey, rest []byte, err error) { 556 var w struct { 557 KeyBytes []byte 558 Rest []byte `ssh:"rest"` 559 } 560 561 if err := Unmarshal(in, &w); err != nil { 562 return nil, nil, err 563 } 564 565 if l := len(w.KeyBytes); l != ed25519.PublicKeySize { 566 return nil, nil, fmt.Errorf("invalid size %d for Ed25519 public key", l) 567 } 568 569 return ed25519PublicKey(w.KeyBytes), w.Rest, nil 570} 571 572func (k ed25519PublicKey) Marshal() []byte { 573 w := struct { 574 Name string 575 KeyBytes []byte 576 }{ 577 KeyAlgoED25519, 578 []byte(k), 579 } 580 return Marshal(&w) 581} 582 583func (k ed25519PublicKey) Verify(b []byte, sig *Signature) error { 584 if sig.Format != k.Type() { 585 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) 586 } 587 if l := len(k); l != ed25519.PublicKeySize { 588 return fmt.Errorf("ssh: invalid size %d for Ed25519 public key", l) 589 } 590 591 if ok := ed25519.Verify(ed25519.PublicKey(k), b, sig.Blob); !ok { 592 return errors.New("ssh: signature did not verify") 593 } 594 595 return nil 596} 597 598func (k ed25519PublicKey) CryptoPublicKey() crypto.PublicKey { 599 return ed25519.PublicKey(k) 600} 601 602func supportedEllipticCurve(curve elliptic.Curve) bool { 603 return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() 604} 605 606// ecHash returns the hash to match the given elliptic curve, see RFC 607// 5656, section 6.2.1 608func ecHash(curve elliptic.Curve) crypto.Hash { 609 bitSize := curve.Params().BitSize 610 switch { 611 case bitSize <= 256: 612 return crypto.SHA256 613 case bitSize <= 384: 614 return crypto.SHA384 615 } 616 return crypto.SHA512 617} 618 619// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. 620func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { 621 var w struct { 622 Curve string 623 KeyBytes []byte 624 Rest []byte `ssh:"rest"` 625 } 626 627 if err := Unmarshal(in, &w); err != nil { 628 return nil, nil, err 629 } 630 631 key := new(ecdsa.PublicKey) 632 633 switch w.Curve { 634 case "nistp256": 635 key.Curve = elliptic.P256() 636 case "nistp384": 637 key.Curve = elliptic.P384() 638 case "nistp521": 639 key.Curve = elliptic.P521() 640 default: 641 return nil, nil, errors.New("ssh: unsupported curve") 642 } 643 644 key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes) 645 if key.X == nil || key.Y == nil { 646 return nil, nil, errors.New("ssh: invalid curve point") 647 } 648 return (*ecdsaPublicKey)(key), w.Rest, nil 649} 650 651func (k *ecdsaPublicKey) Marshal() []byte { 652 // See RFC 5656, section 3.1. 653 keyBytes := elliptic.Marshal(k.Curve, k.X, k.Y) 654 // ECDSA publickey struct layout should match the struct used by 655 // parseECDSACert in the x/crypto/ssh/agent package. 656 w := struct { 657 Name string 658 ID string 659 Key []byte 660 }{ 661 k.Type(), 662 k.nistID(), 663 keyBytes, 664 } 665 666 return Marshal(&w) 667} 668 669func (k *ecdsaPublicKey) Verify(data []byte, sig *Signature) error { 670 if sig.Format != k.Type() { 671 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) 672 } 673 674 h := ecHash(k.Curve).New() 675 h.Write(data) 676 digest := h.Sum(nil) 677 678 // Per RFC 5656, section 3.1.2, 679 // The ecdsa_signature_blob value has the following specific encoding: 680 // mpint r 681 // mpint s 682 var ecSig struct { 683 R *big.Int 684 S *big.Int 685 } 686 687 if err := Unmarshal(sig.Blob, &ecSig); err != nil { 688 return err 689 } 690 691 if ecdsa.Verify((*ecdsa.PublicKey)(k), digest, ecSig.R, ecSig.S) { 692 return nil 693 } 694 return errors.New("ssh: signature did not verify") 695} 696 697func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey { 698 return (*ecdsa.PublicKey)(k) 699} 700 701// skFields holds the additional fields present in U2F/FIDO2 signatures. 702// See openssh/PROTOCOL.u2f 'SSH U2F Signatures' for details. 703type skFields struct { 704 // Flags contains U2F/FIDO2 flags such as 'user present' 705 Flags byte 706 // Counter is a monotonic signature counter which can be 707 // used to detect concurrent use of a private key, should 708 // it be extracted from hardware. 709 Counter uint32 710} 711 712type skECDSAPublicKey struct { 713 // application is a URL-like string, typically "ssh:" for SSH. 714 // see openssh/PROTOCOL.u2f for details. 715 application string 716 ecdsa.PublicKey 717} 718 719func (k *skECDSAPublicKey) Type() string { 720 return KeyAlgoSKECDSA256 721} 722 723func (k *skECDSAPublicKey) nistID() string { 724 return "nistp256" 725} 726 727func parseSKECDSA(in []byte) (out PublicKey, rest []byte, err error) { 728 var w struct { 729 Curve string 730 KeyBytes []byte 731 Application string 732 Rest []byte `ssh:"rest"` 733 } 734 735 if err := Unmarshal(in, &w); err != nil { 736 return nil, nil, err 737 } 738 739 key := new(skECDSAPublicKey) 740 key.application = w.Application 741 742 if w.Curve != "nistp256" { 743 return nil, nil, errors.New("ssh: unsupported curve") 744 } 745 key.Curve = elliptic.P256() 746 747 key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes) 748 if key.X == nil || key.Y == nil { 749 return nil, nil, errors.New("ssh: invalid curve point") 750 } 751 752 return key, w.Rest, nil 753} 754 755func (k *skECDSAPublicKey) Marshal() []byte { 756 // See RFC 5656, section 3.1. 757 keyBytes := elliptic.Marshal(k.Curve, k.X, k.Y) 758 w := struct { 759 Name string 760 ID string 761 Key []byte 762 Application string 763 }{ 764 k.Type(), 765 k.nistID(), 766 keyBytes, 767 k.application, 768 } 769 770 return Marshal(&w) 771} 772 773func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error { 774 if sig.Format != k.Type() { 775 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) 776 } 777 778 h := ecHash(k.Curve).New() 779 h.Write([]byte(k.application)) 780 appDigest := h.Sum(nil) 781 782 h.Reset() 783 h.Write(data) 784 dataDigest := h.Sum(nil) 785 786 var ecSig struct { 787 R *big.Int 788 S *big.Int 789 } 790 if err := Unmarshal(sig.Blob, &ecSig); err != nil { 791 return err 792 } 793 794 var skf skFields 795 if err := Unmarshal(sig.Rest, &skf); err != nil { 796 return err 797 } 798 799 blob := struct { 800 ApplicationDigest []byte `ssh:"rest"` 801 Flags byte 802 Counter uint32 803 MessageDigest []byte `ssh:"rest"` 804 }{ 805 appDigest, 806 skf.Flags, 807 skf.Counter, 808 dataDigest, 809 } 810 811 original := Marshal(blob) 812 813 h.Reset() 814 h.Write(original) 815 digest := h.Sum(nil) 816 817 if ecdsa.Verify((*ecdsa.PublicKey)(&k.PublicKey), digest, ecSig.R, ecSig.S) { 818 return nil 819 } 820 return errors.New("ssh: signature did not verify") 821} 822 823type skEd25519PublicKey struct { 824 // application is a URL-like string, typically "ssh:" for SSH. 825 // see openssh/PROTOCOL.u2f for details. 826 application string 827 ed25519.PublicKey 828} 829 830func (k *skEd25519PublicKey) Type() string { 831 return KeyAlgoSKED25519 832} 833 834func parseSKEd25519(in []byte) (out PublicKey, rest []byte, err error) { 835 var w struct { 836 KeyBytes []byte 837 Application string 838 Rest []byte `ssh:"rest"` 839 } 840 841 if err := Unmarshal(in, &w); err != nil { 842 return nil, nil, err 843 } 844 845 if l := len(w.KeyBytes); l != ed25519.PublicKeySize { 846 return nil, nil, fmt.Errorf("invalid size %d for Ed25519 public key", l) 847 } 848 849 key := new(skEd25519PublicKey) 850 key.application = w.Application 851 key.PublicKey = ed25519.PublicKey(w.KeyBytes) 852 853 return key, w.Rest, nil 854} 855 856func (k *skEd25519PublicKey) Marshal() []byte { 857 w := struct { 858 Name string 859 KeyBytes []byte 860 Application string 861 }{ 862 KeyAlgoSKED25519, 863 []byte(k.PublicKey), 864 k.application, 865 } 866 return Marshal(&w) 867} 868 869func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error { 870 if sig.Format != k.Type() { 871 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) 872 } 873 if l := len(k.PublicKey); l != ed25519.PublicKeySize { 874 return fmt.Errorf("invalid size %d for Ed25519 public key", l) 875 } 876 877 h := sha256.New() 878 h.Write([]byte(k.application)) 879 appDigest := h.Sum(nil) 880 881 h.Reset() 882 h.Write(data) 883 dataDigest := h.Sum(nil) 884 885 var edSig struct { 886 Signature []byte `ssh:"rest"` 887 } 888 889 if err := Unmarshal(sig.Blob, &edSig); err != nil { 890 return err 891 } 892 893 var skf skFields 894 if err := Unmarshal(sig.Rest, &skf); err != nil { 895 return err 896 } 897 898 blob := struct { 899 ApplicationDigest []byte `ssh:"rest"` 900 Flags byte 901 Counter uint32 902 MessageDigest []byte `ssh:"rest"` 903 }{ 904 appDigest, 905 skf.Flags, 906 skf.Counter, 907 dataDigest, 908 } 909 910 original := Marshal(blob) 911 912 if ok := ed25519.Verify(k.PublicKey, original, edSig.Signature); !ok { 913 return errors.New("ssh: signature did not verify") 914 } 915 916 return nil 917} 918 919// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, 920// *ecdsa.PrivateKey or any other crypto.Signer and returns a 921// corresponding Signer instance. ECDSA keys must use P-256, P-384 or 922// P-521. DSA keys must use parameter size L1024N160. 923func NewSignerFromKey(key interface{}) (Signer, error) { 924 switch key := key.(type) { 925 case crypto.Signer: 926 return NewSignerFromSigner(key) 927 case *dsa.PrivateKey: 928 return newDSAPrivateKey(key) 929 default: 930 return nil, fmt.Errorf("ssh: unsupported key type %T", key) 931 } 932} 933 934func newDSAPrivateKey(key *dsa.PrivateKey) (Signer, error) { 935 if err := checkDSAParams(&key.PublicKey.Parameters); err != nil { 936 return nil, err 937 } 938 939 return &dsaPrivateKey{key}, nil 940} 941 942type wrappedSigner struct { 943 signer crypto.Signer 944 pubKey PublicKey 945} 946 947// NewSignerFromSigner takes any crypto.Signer implementation and 948// returns a corresponding Signer interface. This can be used, for 949// example, with keys kept in hardware modules. 950func NewSignerFromSigner(signer crypto.Signer) (Signer, error) { 951 pubKey, err := NewPublicKey(signer.Public()) 952 if err != nil { 953 return nil, err 954 } 955 956 return &wrappedSigner{signer, pubKey}, nil 957} 958 959func (s *wrappedSigner) PublicKey() PublicKey { 960 return s.pubKey 961} 962 963func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { 964 return s.SignWithAlgorithm(rand, data, "") 965} 966 967func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) { 968 var hashFunc crypto.Hash 969 970 if _, ok := s.pubKey.(*rsaPublicKey); ok { 971 // RSA keys support a few hash functions determined by the requested signature algorithm 972 switch algorithm { 973 case "", SigAlgoRSA: 974 algorithm = SigAlgoRSA 975 hashFunc = crypto.SHA1 976 case SigAlgoRSASHA2256: 977 hashFunc = crypto.SHA256 978 case SigAlgoRSASHA2512: 979 hashFunc = crypto.SHA512 980 default: 981 return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) 982 } 983 } else { 984 // The only supported algorithm for all other key types is the same as the type of the key 985 if algorithm == "" { 986 algorithm = s.pubKey.Type() 987 } else if algorithm != s.pubKey.Type() { 988 return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm) 989 } 990 991 switch key := s.pubKey.(type) { 992 case *dsaPublicKey: 993 hashFunc = crypto.SHA1 994 case *ecdsaPublicKey: 995 hashFunc = ecHash(key.Curve) 996 case ed25519PublicKey: 997 default: 998 return nil, fmt.Errorf("ssh: unsupported key type %T", key) 999 } 1000 } 1001 1002 var digest []byte 1003 if hashFunc != 0 { 1004 h := hashFunc.New() 1005 h.Write(data) 1006 digest = h.Sum(nil) 1007 } else { 1008 digest = data 1009 } 1010 1011 signature, err := s.signer.Sign(rand, digest, hashFunc) 1012 if err != nil { 1013 return nil, err 1014 } 1015 1016 // crypto.Signer.Sign is expected to return an ASN.1-encoded signature 1017 // for ECDSA and DSA, but that's not the encoding expected by SSH, so 1018 // re-encode. 1019 switch s.pubKey.(type) { 1020 case *ecdsaPublicKey, *dsaPublicKey: 1021 type asn1Signature struct { 1022 R, S *big.Int 1023 } 1024 asn1Sig := new(asn1Signature) 1025 _, err := asn1.Unmarshal(signature, asn1Sig) 1026 if err != nil { 1027 return nil, err 1028 } 1029 1030 switch s.pubKey.(type) { 1031 case *ecdsaPublicKey: 1032 signature = Marshal(asn1Sig) 1033 1034 case *dsaPublicKey: 1035 signature = make([]byte, 40) 1036 r := asn1Sig.R.Bytes() 1037 s := asn1Sig.S.Bytes() 1038 copy(signature[20-len(r):20], r) 1039 copy(signature[40-len(s):40], s) 1040 } 1041 } 1042 1043 return &Signature{ 1044 Format: algorithm, 1045 Blob: signature, 1046 }, nil 1047} 1048 1049// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey, 1050// or ed25519.PublicKey returns a corresponding PublicKey instance. 1051// ECDSA keys must use P-256, P-384 or P-521. 1052func NewPublicKey(key interface{}) (PublicKey, error) { 1053 switch key := key.(type) { 1054 case *rsa.PublicKey: 1055 return (*rsaPublicKey)(key), nil 1056 case *ecdsa.PublicKey: 1057 if !supportedEllipticCurve(key.Curve) { 1058 return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported") 1059 } 1060 return (*ecdsaPublicKey)(key), nil 1061 case *dsa.PublicKey: 1062 return (*dsaPublicKey)(key), nil 1063 case ed25519.PublicKey: 1064 if l := len(key); l != ed25519.PublicKeySize { 1065 return nil, fmt.Errorf("ssh: invalid size %d for Ed25519 public key", l) 1066 } 1067 return ed25519PublicKey(key), nil 1068 default: 1069 return nil, fmt.Errorf("ssh: unsupported key type %T", key) 1070 } 1071} 1072 1073// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports 1074// the same keys as ParseRawPrivateKey. If the private key is encrypted, it 1075// will return a PassphraseMissingError. 1076func ParsePrivateKey(pemBytes []byte) (Signer, error) { 1077 key, err := ParseRawPrivateKey(pemBytes) 1078 if err != nil { 1079 return nil, err 1080 } 1081 1082 return NewSignerFromKey(key) 1083} 1084 1085// ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private 1086// key and passphrase. It supports the same keys as 1087// ParseRawPrivateKeyWithPassphrase. 1088func ParsePrivateKeyWithPassphrase(pemBytes, passphrase []byte) (Signer, error) { 1089 key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase) 1090 if err != nil { 1091 return nil, err 1092 } 1093 1094 return NewSignerFromKey(key) 1095} 1096 1097// encryptedBlock tells whether a private key is 1098// encrypted by examining its Proc-Type header 1099// for a mention of ENCRYPTED 1100// according to RFC 1421 Section 4.6.1.1. 1101func encryptedBlock(block *pem.Block) bool { 1102 return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED") 1103} 1104 1105// A PassphraseMissingError indicates that parsing this private key requires a 1106// passphrase. Use ParsePrivateKeyWithPassphrase. 1107type PassphraseMissingError struct { 1108 // PublicKey will be set if the private key format includes an unencrypted 1109 // public key along with the encrypted private key. 1110 PublicKey PublicKey 1111} 1112 1113func (*PassphraseMissingError) Error() string { 1114 return "ssh: this private key is passphrase protected" 1115} 1116 1117// ParseRawPrivateKey returns a private key from a PEM encoded private key. It 1118// supports RSA (PKCS#1), PKCS#8, DSA (OpenSSL), and ECDSA private keys. If the 1119// private key is encrypted, it will return a PassphraseMissingError. 1120func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { 1121 block, _ := pem.Decode(pemBytes) 1122 if block == nil { 1123 return nil, errors.New("ssh: no key found") 1124 } 1125 1126 if encryptedBlock(block) { 1127 return nil, &PassphraseMissingError{} 1128 } 1129 1130 switch block.Type { 1131 case "RSA PRIVATE KEY": 1132 return x509.ParsePKCS1PrivateKey(block.Bytes) 1133 // RFC5208 - https://tools.ietf.org/html/rfc5208 1134 case "PRIVATE KEY": 1135 return x509.ParsePKCS8PrivateKey(block.Bytes) 1136 case "EC PRIVATE KEY": 1137 return x509.ParseECPrivateKey(block.Bytes) 1138 case "DSA PRIVATE KEY": 1139 return ParseDSAPrivateKey(block.Bytes) 1140 case "OPENSSH PRIVATE KEY": 1141 return parseOpenSSHPrivateKey(block.Bytes, unencryptedOpenSSHKey) 1142 default: 1143 return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) 1144 } 1145} 1146 1147// ParseRawPrivateKeyWithPassphrase returns a private key decrypted with 1148// passphrase from a PEM encoded private key. If the passphrase is wrong, it 1149// will return x509.IncorrectPasswordError. 1150func ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase []byte) (interface{}, error) { 1151 block, _ := pem.Decode(pemBytes) 1152 if block == nil { 1153 return nil, errors.New("ssh: no key found") 1154 } 1155 1156 if block.Type == "OPENSSH PRIVATE KEY" { 1157 return parseOpenSSHPrivateKey(block.Bytes, passphraseProtectedOpenSSHKey(passphrase)) 1158 } 1159 1160 if !encryptedBlock(block) || !x509.IsEncryptedPEMBlock(block) { 1161 return nil, errors.New("ssh: not an encrypted key") 1162 } 1163 1164 buf, err := x509.DecryptPEMBlock(block, passphrase) 1165 if err != nil { 1166 if err == x509.IncorrectPasswordError { 1167 return nil, err 1168 } 1169 return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err) 1170 } 1171 1172 switch block.Type { 1173 case "RSA PRIVATE KEY": 1174 return x509.ParsePKCS1PrivateKey(buf) 1175 case "EC PRIVATE KEY": 1176 return x509.ParseECPrivateKey(buf) 1177 case "DSA PRIVATE KEY": 1178 return ParseDSAPrivateKey(buf) 1179 default: 1180 return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) 1181 } 1182} 1183 1184// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as 1185// specified by the OpenSSL DSA man page. 1186func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { 1187 var k struct { 1188 Version int 1189 P *big.Int 1190 Q *big.Int 1191 G *big.Int 1192 Pub *big.Int 1193 Priv *big.Int 1194 } 1195 rest, err := asn1.Unmarshal(der, &k) 1196 if err != nil { 1197 return nil, errors.New("ssh: failed to parse DSA key: " + err.Error()) 1198 } 1199 if len(rest) > 0 { 1200 return nil, errors.New("ssh: garbage after DSA key") 1201 } 1202 1203 return &dsa.PrivateKey{ 1204 PublicKey: dsa.PublicKey{ 1205 Parameters: dsa.Parameters{ 1206 P: k.P, 1207 Q: k.Q, 1208 G: k.G, 1209 }, 1210 Y: k.Pub, 1211 }, 1212 X: k.Priv, 1213 }, nil 1214} 1215 1216func unencryptedOpenSSHKey(cipherName, kdfName, kdfOpts string, privKeyBlock []byte) ([]byte, error) { 1217 if kdfName != "none" || cipherName != "none" { 1218 return nil, &PassphraseMissingError{} 1219 } 1220 if kdfOpts != "" { 1221 return nil, errors.New("ssh: invalid openssh private key") 1222 } 1223 return privKeyBlock, nil 1224} 1225 1226func passphraseProtectedOpenSSHKey(passphrase []byte) openSSHDecryptFunc { 1227 return func(cipherName, kdfName, kdfOpts string, privKeyBlock []byte) ([]byte, error) { 1228 if kdfName == "none" || cipherName == "none" { 1229 return nil, errors.New("ssh: key is not password protected") 1230 } 1231 if kdfName != "bcrypt" { 1232 return nil, fmt.Errorf("ssh: unknown KDF %q, only supports %q", kdfName, "bcrypt") 1233 } 1234 1235 var opts struct { 1236 Salt string 1237 Rounds uint32 1238 } 1239 if err := Unmarshal([]byte(kdfOpts), &opts); err != nil { 1240 return nil, err 1241 } 1242 1243 k, err := bcrypt_pbkdf.Key(passphrase, []byte(opts.Salt), int(opts.Rounds), 32+16) 1244 if err != nil { 1245 return nil, err 1246 } 1247 key, iv := k[:32], k[32:] 1248 1249 c, err := aes.NewCipher(key) 1250 if err != nil { 1251 return nil, err 1252 } 1253 switch cipherName { 1254 case "aes256-ctr": 1255 ctr := cipher.NewCTR(c, iv) 1256 ctr.XORKeyStream(privKeyBlock, privKeyBlock) 1257 case "aes256-cbc": 1258 if len(privKeyBlock)%c.BlockSize() != 0 { 1259 return nil, fmt.Errorf("ssh: invalid encrypted private key length, not a multiple of the block size") 1260 } 1261 cbc := cipher.NewCBCDecrypter(c, iv) 1262 cbc.CryptBlocks(privKeyBlock, privKeyBlock) 1263 default: 1264 return nil, fmt.Errorf("ssh: unknown cipher %q, only supports %q or %q", cipherName, "aes256-ctr", "aes256-cbc") 1265 } 1266 1267 return privKeyBlock, nil 1268 } 1269} 1270 1271type openSSHDecryptFunc func(CipherName, KdfName, KdfOpts string, PrivKeyBlock []byte) ([]byte, error) 1272 1273// parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt 1274// function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used 1275// as the decrypt function to parse an unencrypted private key. See 1276// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key. 1277func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) { 1278 const magic = "openssh-key-v1\x00" 1279 if len(key) < len(magic) || string(key[:len(magic)]) != magic { 1280 return nil, errors.New("ssh: invalid openssh private key format") 1281 } 1282 remaining := key[len(magic):] 1283 1284 var w struct { 1285 CipherName string 1286 KdfName string 1287 KdfOpts string 1288 NumKeys uint32 1289 PubKey []byte 1290 PrivKeyBlock []byte 1291 } 1292 1293 if err := Unmarshal(remaining, &w); err != nil { 1294 return nil, err 1295 } 1296 if w.NumKeys != 1 { 1297 // We only support single key files, and so does OpenSSH. 1298 // https://github.com/openssh/openssh-portable/blob/4103a3ec7/sshkey.c#L4171 1299 return nil, errors.New("ssh: multi-key files are not supported") 1300 } 1301 1302 privKeyBlock, err := decrypt(w.CipherName, w.KdfName, w.KdfOpts, w.PrivKeyBlock) 1303 if err != nil { 1304 if err, ok := err.(*PassphraseMissingError); ok { 1305 pub, errPub := ParsePublicKey(w.PubKey) 1306 if errPub != nil { 1307 return nil, fmt.Errorf("ssh: failed to parse embedded public key: %v", errPub) 1308 } 1309 err.PublicKey = pub 1310 } 1311 return nil, err 1312 } 1313 1314 pk1 := struct { 1315 Check1 uint32 1316 Check2 uint32 1317 Keytype string 1318 Rest []byte `ssh:"rest"` 1319 }{} 1320 1321 if err := Unmarshal(privKeyBlock, &pk1); err != nil || pk1.Check1 != pk1.Check2 { 1322 if w.CipherName != "none" { 1323 return nil, x509.IncorrectPasswordError 1324 } 1325 return nil, errors.New("ssh: malformed OpenSSH key") 1326 } 1327 1328 switch pk1.Keytype { 1329 case KeyAlgoRSA: 1330 // https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773 1331 key := struct { 1332 N *big.Int 1333 E *big.Int 1334 D *big.Int 1335 Iqmp *big.Int 1336 P *big.Int 1337 Q *big.Int 1338 Comment string 1339 Pad []byte `ssh:"rest"` 1340 }{} 1341 1342 if err := Unmarshal(pk1.Rest, &key); err != nil { 1343 return nil, err 1344 } 1345 1346 if err := checkOpenSSHKeyPadding(key.Pad); err != nil { 1347 return nil, err 1348 } 1349 1350 pk := &rsa.PrivateKey{ 1351 PublicKey: rsa.PublicKey{ 1352 N: key.N, 1353 E: int(key.E.Int64()), 1354 }, 1355 D: key.D, 1356 Primes: []*big.Int{key.P, key.Q}, 1357 } 1358 1359 if err := pk.Validate(); err != nil { 1360 return nil, err 1361 } 1362 1363 pk.Precompute() 1364 1365 return pk, nil 1366 case KeyAlgoED25519: 1367 key := struct { 1368 Pub []byte 1369 Priv []byte 1370 Comment string 1371 Pad []byte `ssh:"rest"` 1372 }{} 1373 1374 if err := Unmarshal(pk1.Rest, &key); err != nil { 1375 return nil, err 1376 } 1377 1378 if len(key.Priv) != ed25519.PrivateKeySize { 1379 return nil, errors.New("ssh: private key unexpected length") 1380 } 1381 1382 if err := checkOpenSSHKeyPadding(key.Pad); err != nil { 1383 return nil, err 1384 } 1385 1386 pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize)) 1387 copy(pk, key.Priv) 1388 return &pk, nil 1389 case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: 1390 key := struct { 1391 Curve string 1392 Pub []byte 1393 D *big.Int 1394 Comment string 1395 Pad []byte `ssh:"rest"` 1396 }{} 1397 1398 if err := Unmarshal(pk1.Rest, &key); err != nil { 1399 return nil, err 1400 } 1401 1402 if err := checkOpenSSHKeyPadding(key.Pad); err != nil { 1403 return nil, err 1404 } 1405 1406 var curve elliptic.Curve 1407 switch key.Curve { 1408 case "nistp256": 1409 curve = elliptic.P256() 1410 case "nistp384": 1411 curve = elliptic.P384() 1412 case "nistp521": 1413 curve = elliptic.P521() 1414 default: 1415 return nil, errors.New("ssh: unhandled elliptic curve: " + key.Curve) 1416 } 1417 1418 X, Y := elliptic.Unmarshal(curve, key.Pub) 1419 if X == nil || Y == nil { 1420 return nil, errors.New("ssh: failed to unmarshal public key") 1421 } 1422 1423 if key.D.Cmp(curve.Params().N) >= 0 { 1424 return nil, errors.New("ssh: scalar is out of range") 1425 } 1426 1427 x, y := curve.ScalarBaseMult(key.D.Bytes()) 1428 if x.Cmp(X) != 0 || y.Cmp(Y) != 0 { 1429 return nil, errors.New("ssh: public key does not match private key") 1430 } 1431 1432 return &ecdsa.PrivateKey{ 1433 PublicKey: ecdsa.PublicKey{ 1434 Curve: curve, 1435 X: X, 1436 Y: Y, 1437 }, 1438 D: key.D, 1439 }, nil 1440 default: 1441 return nil, errors.New("ssh: unhandled key type") 1442 } 1443} 1444 1445func checkOpenSSHKeyPadding(pad []byte) error { 1446 for i, b := range pad { 1447 if int(b) != i+1 { 1448 return errors.New("ssh: padding not as expected") 1449 } 1450 } 1451 return nil 1452} 1453 1454// FingerprintLegacyMD5 returns the user presentation of the key's 1455// fingerprint as described by RFC 4716 section 4. 1456func FingerprintLegacyMD5(pubKey PublicKey) string { 1457 md5sum := md5.Sum(pubKey.Marshal()) 1458 hexarray := make([]string, len(md5sum)) 1459 for i, c := range md5sum { 1460 hexarray[i] = hex.EncodeToString([]byte{c}) 1461 } 1462 return strings.Join(hexarray, ":") 1463} 1464 1465// FingerprintSHA256 returns the user presentation of the key's 1466// fingerprint as unpadded base64 encoded sha256 hash. 1467// This format was introduced from OpenSSH 6.8. 1468// https://www.openssh.com/txt/release-6.8 1469// https://tools.ietf.org/html/rfc4648#section-3.2 (unpadded base64 encoding) 1470func FingerprintSHA256(pubKey PublicKey) string { 1471 sha256sum := sha256.Sum256(pubKey.Marshal()) 1472 hash := base64.RawStdEncoding.EncodeToString(sha256sum[:]) 1473 return "SHA256:" + hash 1474} 1475