| // Copyright 2012 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ssh |
| |
| import ( |
| "bytes" |
| "crypto" |
| "crypto/dsa" |
| "crypto/ecdsa" |
| "crypto/elliptic" |
| "crypto/rsa" |
| "crypto/x509" |
| "encoding/asn1" |
| "encoding/base64" |
| "encoding/pem" |
| "errors" |
| "fmt" |
| "io" |
| "math/big" |
| "strings" |
| ) |
| |
| // These constants represent the algorithm names for key types supported by this |
| // package. |
| const ( |
| KeyAlgoRSA = "ssh-rsa" |
| KeyAlgoDSA = "ssh-dss" |
| KeyAlgoECDSA256 = "ecdsa-sha2-nistp256" |
| KeyAlgoECDSA384 = "ecdsa-sha2-nistp384" |
| KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" |
| ) |
| |
| // parsePubKey parses a public key of the given algorithm. |
| // Use ParsePublicKey for keys with prepended algorithm. |
| func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) { |
| switch algo { |
| case KeyAlgoRSA: |
| return parseRSA(in) |
| case KeyAlgoDSA: |
| return parseDSA(in) |
| case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: |
| return parseECDSA(in) |
| case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: |
| cert, err := parseCert(in, certToPrivAlgo(algo)) |
| if err != nil { |
| return nil, nil, err |
| } |
| return cert, nil, nil |
| } |
| return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", err) |
| } |
| |
| // parseAuthorizedKey parses a public key in OpenSSH authorized_keys format |
| // (see sshd(8) manual page) once the options and key type fields have been |
| // removed. |
| func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) { |
| in = bytes.TrimSpace(in) |
| |
| i := bytes.IndexAny(in, " \t") |
| if i == -1 { |
| i = len(in) |
| } |
| base64Key := in[:i] |
| |
| key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key))) |
| n, err := base64.StdEncoding.Decode(key, base64Key) |
| if err != nil { |
| return nil, "", err |
| } |
| key = key[:n] |
| out, err = ParsePublicKey(key) |
| if err != nil { |
| return nil, "", err |
| } |
| comment = string(bytes.TrimSpace(in[i:])) |
| return out, comment, nil |
| } |
| |
| // ParseKnownHosts parses an entry in the format of the known_hosts file. |
| // |
| // The known_hosts format is documented in the sshd(8) manual page. This |
| // function will parse a single entry from in. On successful return, marker |
| // will contain the optional marker value (i.e. "cert-authority" or "revoked") |
| // or else be empty, hosts will contain the hosts that this entry matches, |
| // pubKey will contain the public key and comment will contain any trailing |
| // comment at the end of the line. See the sshd(8) manual page for the various |
| // forms that a host string can take. |
| // |
| // The unparsed remainder of the input will be returned in rest. This function |
| // can be called repeatedly to parse multiple entries. |
| // |
| // If no entries were found in the input then err will be io.EOF. Otherwise a |
| // non-nil err value indicates a parse error. |
| func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey, comment string, rest []byte, err error) { |
| for len(in) > 0 { |
| end := bytes.IndexByte(in, '\n') |
| if end != -1 { |
| rest = in[end+1:] |
| in = in[:end] |
| } else { |
| rest = nil |
| } |
| |
| end = bytes.IndexByte(in, '\r') |
| if end != -1 { |
| in = in[:end] |
| } |
| |
| in = bytes.TrimSpace(in) |
| if len(in) == 0 || in[0] == '#' { |
| in = rest |
| continue |
| } |
| |
| i := bytes.IndexAny(in, " \t") |
| if i == -1 { |
| in = rest |
| continue |
| } |
| |
| // Strip out the begining of the known_host key. |
| // This is either an optional marker or a (set of) hostname(s). |
| keyFields := bytes.Fields(in) |
| if len(keyFields) < 3 || len(keyFields) > 5 { |
| return "", nil, nil, "", nil, errors.New("ssh: invalid entry in known_hosts data") |
| } |
| |
| // keyFields[0] is either "@cert-authority", "@revoked" or a comma separated |
| // list of hosts |
| marker := "" |
| if keyFields[0][0] == '@' { |
| marker = string(keyFields[0][1:]) |
| keyFields = keyFields[1:] |
| } |
| |
| hosts := string(keyFields[0]) |
| // keyFields[1] contains the key type (e.g. “ssh-rsa”). |
| // However, that information is duplicated inside the |
| // base64-encoded key and so is ignored here. |
| |
| key := bytes.Join(keyFields[2:], []byte(" ")) |
| if pubKey, comment, err = parseAuthorizedKey(key); err != nil { |
| return "", nil, nil, "", nil, err |
| } |
| |
| return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil |
| } |
| |
| return "", nil, nil, "", nil, io.EOF |
| } |
| |
| // ParseAuthorizedKeys parses a public key from an authorized_keys |
| // file used in OpenSSH according to the sshd(8) manual page. |
| func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { |
| for len(in) > 0 { |
| end := bytes.IndexByte(in, '\n') |
| if end != -1 { |
| rest = in[end+1:] |
| in = in[:end] |
| } else { |
| rest = nil |
| } |
| |
| end = bytes.IndexByte(in, '\r') |
| if end != -1 { |
| in = in[:end] |
| } |
| |
| in = bytes.TrimSpace(in) |
| if len(in) == 0 || in[0] == '#' { |
| in = rest |
| continue |
| } |
| |
| i := bytes.IndexAny(in, " \t") |
| if i == -1 { |
| in = rest |
| continue |
| } |
| |
| if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { |
| return out, comment, options, rest, nil |
| } |
| |
| // No key type recognised. Maybe there's an options field at |
| // the beginning. |
| var b byte |
| inQuote := false |
| var candidateOptions []string |
| optionStart := 0 |
| for i, b = range in { |
| isEnd := !inQuote && (b == ' ' || b == '\t') |
| if (b == ',' && !inQuote) || isEnd { |
| if i-optionStart > 0 { |
| candidateOptions = append(candidateOptions, string(in[optionStart:i])) |
| } |
| optionStart = i + 1 |
| } |
| if isEnd { |
| break |
| } |
| if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) { |
| inQuote = !inQuote |
| } |
| } |
| for i < len(in) && (in[i] == ' ' || in[i] == '\t') { |
| i++ |
| } |
| if i == len(in) { |
| // Invalid line: unmatched quote |
| in = rest |
| continue |
| } |
| |
| in = in[i:] |
| i = bytes.IndexAny(in, " \t") |
| if i == -1 { |
| in = rest |
| continue |
| } |
| |
| if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { |
| options = candidateOptions |
| return out, comment, options, rest, nil |
| } |
| |
| in = rest |
| continue |
| } |
| |
| return nil, "", nil, nil, errors.New("ssh: no key found") |
| } |
| |
| // ParsePublicKey parses an SSH public key formatted for use in |
| // the SSH wire protocol according to RFC 4253, section 6.6. |
| func ParsePublicKey(in []byte) (out PublicKey, err error) { |
| algo, in, ok := parseString(in) |
| if !ok { |
| return nil, errShortRead |
| } |
| var rest []byte |
| out, rest, err = parsePubKey(in, string(algo)) |
| if len(rest) > 0 { |
| return nil, errors.New("ssh: trailing junk in public key") |
| } |
| |
| return out, err |
| } |
| |
| // MarshalAuthorizedKey serializes key for inclusion in an OpenSSH |
| // authorized_keys file. The return value ends with newline. |
| func MarshalAuthorizedKey(key PublicKey) []byte { |
| b := &bytes.Buffer{} |
| b.WriteString(key.Type()) |
| b.WriteByte(' ') |
| e := base64.NewEncoder(base64.StdEncoding, b) |
| e.Write(key.Marshal()) |
| e.Close() |
| b.WriteByte('\n') |
| return b.Bytes() |
| } |
| |
| // PublicKey is an abstraction of different types of public keys. |
| type PublicKey interface { |
| // Type returns the key's type, e.g. "ssh-rsa". |
| Type() string |
| |
| // Marshal returns the serialized key data in SSH wire format, |
| // with the name prefix. |
| Marshal() []byte |
| |
| // Verify that sig is a signature on the given data using this |
| // key. This function will hash the data appropriately first. |
| Verify(data []byte, sig *Signature) error |
| } |
| |
| // A Signer can create signatures that verify against a public key. |
| type Signer interface { |
| // PublicKey returns an associated PublicKey instance. |
| PublicKey() PublicKey |
| |
| // Sign returns raw signature for the given data. This method |
| // will apply the hash specified for the keytype to the data. |
| Sign(rand io.Reader, data []byte) (*Signature, error) |
| } |
| |
| type rsaPublicKey rsa.PublicKey |
| |
| func (r *rsaPublicKey) Type() string { |
| return "ssh-rsa" |
| } |
| |
| // parseRSA parses an RSA key according to RFC 4253, section 6.6. |
| func parseRSA(in []byte) (out PublicKey, rest []byte, err error) { |
| var w struct { |
| E *big.Int |
| N *big.Int |
| Rest []byte `ssh:"rest"` |
| } |
| if err := Unmarshal(in, &w); err != nil { |
| return nil, nil, err |
| } |
| |
| if w.E.BitLen() > 24 { |
| return nil, nil, errors.New("ssh: exponent too large") |
| } |
| e := w.E.Int64() |
| if e < 3 || e&1 == 0 { |
| return nil, nil, errors.New("ssh: incorrect exponent") |
| } |
| |
| var key rsa.PublicKey |
| key.E = int(e) |
| key.N = w.N |
| return (*rsaPublicKey)(&key), w.Rest, nil |
| } |
| |
| func (r *rsaPublicKey) Marshal() []byte { |
| e := new(big.Int).SetInt64(int64(r.E)) |
| wirekey := struct { |
| Name string |
| E *big.Int |
| N *big.Int |
| }{ |
| KeyAlgoRSA, |
| e, |
| r.N, |
| } |
| return Marshal(&wirekey) |
| } |
| |
| func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { |
| if sig.Format != r.Type() { |
| return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type()) |
| } |
| h := crypto.SHA1.New() |
| h.Write(data) |
| digest := h.Sum(nil) |
| return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob) |
| } |
| |
| type dsaPublicKey dsa.PublicKey |
| |
| func (r *dsaPublicKey) Type() string { |
| return "ssh-dss" |
| } |
| |
| // parseDSA parses an DSA key according to RFC 4253, section 6.6. |
| func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { |
| var w struct { |
| P, Q, G, Y *big.Int |
| Rest []byte `ssh:"rest"` |
| } |
| if err := Unmarshal(in, &w); err != nil { |
| return nil, nil, err |
| } |
| |
| key := &dsaPublicKey{ |
| Parameters: dsa.Parameters{ |
| P: w.P, |
| Q: w.Q, |
| G: w.G, |
| }, |
| Y: w.Y, |
| } |
| return key, w.Rest, nil |
| } |
| |
| func (k *dsaPublicKey) Marshal() []byte { |
| w := struct { |
| Name string |
| P, Q, G, Y *big.Int |
| }{ |
| k.Type(), |
| k.P, |
| k.Q, |
| k.G, |
| k.Y, |
| } |
| |
| return Marshal(&w) |
| } |
| |
| func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error { |
| if sig.Format != k.Type() { |
| return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) |
| } |
| h := crypto.SHA1.New() |
| h.Write(data) |
| digest := h.Sum(nil) |
| |
| // Per RFC 4253, section 6.6, |
| // The value for 'dss_signature_blob' is encoded as a string containing |
| // r, followed by s (which are 160-bit integers, without lengths or |
| // padding, unsigned, and in network byte order). |
| // For DSS purposes, sig.Blob should be exactly 40 bytes in length. |
| if len(sig.Blob) != 40 { |
| return errors.New("ssh: DSA signature parse error") |
| } |
| r := new(big.Int).SetBytes(sig.Blob[:20]) |
| s := new(big.Int).SetBytes(sig.Blob[20:]) |
| if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) { |
| return nil |
| } |
| return errors.New("ssh: signature did not verify") |
| } |
| |
| type dsaPrivateKey struct { |
| *dsa.PrivateKey |
| } |
| |
| func (k *dsaPrivateKey) PublicKey() PublicKey { |
| return (*dsaPublicKey)(&k.PrivateKey.PublicKey) |
| } |
| |
| func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { |
| h := crypto.SHA1.New() |
| h.Write(data) |
| digest := h.Sum(nil) |
| r, s, err := dsa.Sign(rand, k.PrivateKey, digest) |
| if err != nil { |
| return nil, err |
| } |
| |
| sig := make([]byte, 40) |
| rb := r.Bytes() |
| sb := s.Bytes() |
| |
| copy(sig[20-len(rb):20], rb) |
| copy(sig[40-len(sb):], sb) |
| |
| return &Signature{ |
| Format: k.PublicKey().Type(), |
| Blob: sig, |
| }, nil |
| } |
| |
| type ecdsaPublicKey ecdsa.PublicKey |
| |
| func (key *ecdsaPublicKey) Type() string { |
| return "ecdsa-sha2-" + key.nistID() |
| } |
| |
| func (key *ecdsaPublicKey) nistID() string { |
| switch key.Params().BitSize { |
| case 256: |
| return "nistp256" |
| case 384: |
| return "nistp384" |
| case 521: |
| return "nistp521" |
| } |
| panic("ssh: unsupported ecdsa key size") |
| } |
| |
| func supportedEllipticCurve(curve elliptic.Curve) bool { |
| return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() |
| } |
| |
| // ecHash returns the hash to match the given elliptic curve, see RFC |
| // 5656, section 6.2.1 |
| func ecHash(curve elliptic.Curve) crypto.Hash { |
| bitSize := curve.Params().BitSize |
| switch { |
| case bitSize <= 256: |
| return crypto.SHA256 |
| case bitSize <= 384: |
| return crypto.SHA384 |
| } |
| return crypto.SHA512 |
| } |
| |
| // parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. |
| func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { |
| var w struct { |
| Curve string |
| KeyBytes []byte |
| Rest []byte `ssh:"rest"` |
| } |
| |
| if err := Unmarshal(in, &w); err != nil { |
| return nil, nil, err |
| } |
| |
| key := new(ecdsa.PublicKey) |
| |
| switch w.Curve { |
| case "nistp256": |
| key.Curve = elliptic.P256() |
| case "nistp384": |
| key.Curve = elliptic.P384() |
| case "nistp521": |
| key.Curve = elliptic.P521() |
| default: |
| return nil, nil, errors.New("ssh: unsupported curve") |
| } |
| |
| key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes) |
| if key.X == nil || key.Y == nil { |
| return nil, nil, errors.New("ssh: invalid curve point") |
| } |
| return (*ecdsaPublicKey)(key), w.Rest, nil |
| } |
| |
| func (key *ecdsaPublicKey) Marshal() []byte { |
| // See RFC 5656, section 3.1. |
| keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y) |
| w := struct { |
| Name string |
| ID string |
| Key []byte |
| }{ |
| key.Type(), |
| key.nistID(), |
| keyBytes, |
| } |
| |
| return Marshal(&w) |
| } |
| |
| func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error { |
| if sig.Format != key.Type() { |
| return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type()) |
| } |
| |
| h := ecHash(key.Curve).New() |
| h.Write(data) |
| digest := h.Sum(nil) |
| |
| // Per RFC 5656, section 3.1.2, |
| // The ecdsa_signature_blob value has the following specific encoding: |
| // mpint r |
| // mpint s |
| var ecSig struct { |
| R *big.Int |
| S *big.Int |
| } |
| |
| if err := Unmarshal(sig.Blob, &ecSig); err != nil { |
| return err |
| } |
| |
| if ecdsa.Verify((*ecdsa.PublicKey)(key), digest, ecSig.R, ecSig.S) { |
| return nil |
| } |
| return errors.New("ssh: signature did not verify") |
| } |
| |
| // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, |
| // *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding |
| // Signer instance. ECDSA keys must use P-256, P-384 or P-521. |
| func NewSignerFromKey(key interface{}) (Signer, error) { |
| switch key := key.(type) { |
| case crypto.Signer: |
| return NewSignerFromSigner(key) |
| case *dsa.PrivateKey: |
| return &dsaPrivateKey{key}, nil |
| default: |
| return nil, fmt.Errorf("ssh: unsupported key type %T", key) |
| } |
| } |
| |
| type wrappedSigner struct { |
| signer crypto.Signer |
| pubKey PublicKey |
| } |
| |
| // NewSignerFromSigner takes any crypto.Signer implementation and |
| // returns a corresponding Signer interface. This can be used, for |
| // example, with keys kept in hardware modules. |
| func NewSignerFromSigner(signer crypto.Signer) (Signer, error) { |
| pubKey, err := NewPublicKey(signer.Public()) |
| if err != nil { |
| return nil, err |
| } |
| |
| return &wrappedSigner{signer, pubKey}, nil |
| } |
| |
| func (s *wrappedSigner) PublicKey() PublicKey { |
| return s.pubKey |
| } |
| |
| func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { |
| var hashFunc crypto.Hash |
| |
| switch key := s.pubKey.(type) { |
| case *rsaPublicKey, *dsaPublicKey: |
| hashFunc = crypto.SHA1 |
| case *ecdsaPublicKey: |
| hashFunc = ecHash(key.Curve) |
| default: |
| return nil, fmt.Errorf("ssh: unsupported key type %T", key) |
| } |
| |
| h := hashFunc.New() |
| h.Write(data) |
| digest := h.Sum(nil) |
| |
| signature, err := s.signer.Sign(rand, digest, hashFunc) |
| if err != nil { |
| return nil, err |
| } |
| |
| // crypto.Signer.Sign is expected to return an ASN.1-encoded signature |
| // for ECDSA and DSA, but that's not the encoding expected by SSH, so |
| // re-encode. |
| switch s.pubKey.(type) { |
| case *ecdsaPublicKey, *dsaPublicKey: |
| type asn1Signature struct { |
| R, S *big.Int |
| } |
| asn1Sig := new(asn1Signature) |
| _, err := asn1.Unmarshal(signature, asn1Sig) |
| if err != nil { |
| return nil, err |
| } |
| |
| switch s.pubKey.(type) { |
| case *ecdsaPublicKey: |
| signature = Marshal(asn1Sig) |
| |
| case *dsaPublicKey: |
| signature = make([]byte, 40) |
| r := asn1Sig.R.Bytes() |
| s := asn1Sig.S.Bytes() |
| copy(signature[20-len(r):20], r) |
| copy(signature[40-len(s):40], s) |
| } |
| } |
| |
| return &Signature{ |
| Format: s.pubKey.Type(), |
| Blob: signature, |
| }, nil |
| } |
| |
| // NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or |
| // any other crypto.Signer and returns a corresponding Signer instance. ECDSA |
| // keys must use P-256, P-384 or P-521. |
| func NewPublicKey(key interface{}) (PublicKey, error) { |
| switch key := key.(type) { |
| case *rsa.PublicKey: |
| return (*rsaPublicKey)(key), nil |
| case *ecdsa.PublicKey: |
| if !supportedEllipticCurve(key.Curve) { |
| return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported.") |
| } |
| return (*ecdsaPublicKey)(key), nil |
| case *dsa.PublicKey: |
| return (*dsaPublicKey)(key), nil |
| default: |
| return nil, fmt.Errorf("ssh: unsupported key type %T", key) |
| } |
| } |
| |
| // ParsePrivateKey returns a Signer from a PEM encoded private key. It supports |
| // the same keys as ParseRawPrivateKey. |
| func ParsePrivateKey(pemBytes []byte) (Signer, error) { |
| key, err := ParseRawPrivateKey(pemBytes) |
| if err != nil { |
| return nil, err |
| } |
| |
| return NewSignerFromKey(key) |
| } |
| |
| // ParseRawPrivateKey returns a private key from a PEM encoded private key. It |
| // supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. |
| func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { |
| block, _ := pem.Decode(pemBytes) |
| if block == nil { |
| return nil, errors.New("ssh: no key found") |
| } |
| |
| switch block.Type { |
| case "RSA PRIVATE KEY": |
| return x509.ParsePKCS1PrivateKey(block.Bytes) |
| case "EC PRIVATE KEY": |
| return x509.ParseECPrivateKey(block.Bytes) |
| case "DSA PRIVATE KEY": |
| return ParseDSAPrivateKey(block.Bytes) |
| default: |
| return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) |
| } |
| } |
| |
| // ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as |
| // specified by the OpenSSL DSA man page. |
| func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { |
| var k struct { |
| Version int |
| P *big.Int |
| Q *big.Int |
| G *big.Int |
| Priv *big.Int |
| Pub *big.Int |
| } |
| rest, err := asn1.Unmarshal(der, &k) |
| if err != nil { |
| return nil, errors.New("ssh: failed to parse DSA key: " + err.Error()) |
| } |
| if len(rest) > 0 { |
| return nil, errors.New("ssh: garbage after DSA key") |
| } |
| |
| return &dsa.PrivateKey{ |
| PublicKey: dsa.PublicKey{ |
| Parameters: dsa.Parameters{ |
| P: k.P, |
| Q: k.Q, |
| G: k.G, |
| }, |
| Y: k.Priv, |
| }, |
| X: k.Pub, |
| }, nil |
| } |