blob: f38de9898ca2c0258085c6fe301e2ee8aea6bf4e [file] [log] [blame]
Jonathan Pittman9b05c272012-02-24 12:52:06 -05001// 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 (
Shawn Ledbetter887809b2012-12-10 17:43:09 -05008 "bytes"
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -04009 "crypto"
Jonathan Pittman9b05c272012-02-24 12:52:06 -050010 "crypto/dsa"
Jonathan Pittman94c9f922012-12-14 05:52:19 +110011 "crypto/ecdsa"
12 "crypto/elliptic"
Ryuzo Yamamotoede567c2016-11-04 15:41:44 -040013 "crypto/md5"
Jonathan Pittman9b05c272012-02-24 12:52:06 -050014 "crypto/rsa"
Ryuzo Yamamotoede567c2016-11-04 15:41:44 -040015 "crypto/sha256"
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -040016 "crypto/x509"
Han-Wen Nienhuys4e058122013-09-26 11:17:52 -040017 "encoding/asn1"
Shawn Ledbetter887809b2012-12-10 17:43:09 -050018 "encoding/base64"
Ryuzo Yamamotoede567c2016-11-04 15:41:44 -040019 "encoding/hex"
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -040020 "encoding/pem"
21 "errors"
22 "fmt"
23 "io"
Jonathan Pittman9b05c272012-02-24 12:52:06 -050024 "math/big"
Peter Moody3760e012015-12-23 13:51:54 -080025 "strings"
Martin Garton1e61df82016-04-30 22:10:58 +010026
27 "golang.org/x/crypto/ed25519"
Jonathan Pittman9b05c272012-02-24 12:52:06 -050028)
29
Jonathan Pittmand95b2832012-12-16 10:47:10 -050030// These constants represent the algorithm names for key types supported by this
31// package.
Shawn Ledbetter887809b2012-12-10 17:43:09 -050032const (
Jonathan Pittman54c65ae2012-12-14 10:11:06 -050033 KeyAlgoRSA = "ssh-rsa"
34 KeyAlgoDSA = "ssh-dss"
35 KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
36 KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
37 KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
Martin Garton1e61df82016-04-30 22:10:58 +010038 KeyAlgoED25519 = "ssh-ed25519"
Shawn Ledbetter887809b2012-12-10 17:43:09 -050039)
40
JP Sugarbroad15d8abf2013-10-09 12:56:09 -040041// parsePubKey parses a public key of the given algorithm.
42// Use ParsePublicKey for keys with prepended algorithm.
Adam Langleyfa50e742014-04-09 13:57:52 -070043func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) {
JP Sugarbroad15d8abf2013-10-09 12:56:09 -040044 switch algo {
Jonathan Pittman54c65ae2012-12-14 10:11:06 -050045 case KeyAlgoRSA:
Jonathan Pittman9b05c272012-02-24 12:52:06 -050046 return parseRSA(in)
Jonathan Pittman54c65ae2012-12-14 10:11:06 -050047 case KeyAlgoDSA:
Jonathan Pittman9b05c272012-02-24 12:52:06 -050048 return parseDSA(in)
Jonathan Pittman54c65ae2012-12-14 10:11:06 -050049 case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
Jonathan Pittman94c9f922012-12-14 05:52:19 +110050 return parseECDSA(in)
Martin Garton1e61df82016-04-30 22:10:58 +010051 case KeyAlgoED25519:
52 return parseED25519(in)
53 case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
Adam Langleyfa50e742014-04-09 13:57:52 -070054 cert, err := parseCert(in, certToPrivAlgo(algo))
55 if err != nil {
56 return nil, nil, err
57 }
58 return cert, nil, nil
Jonathan Pittman9b05c272012-02-24 12:52:06 -050059 }
Martin Garton1e61df82016-04-30 22:10:58 +010060 return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo)
Jonathan Pittman94c9f922012-12-14 05:52:19 +110061}
62
Shawn Ledbetter887809b2012-12-10 17:43:09 -050063// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
64// (see sshd(8) manual page) once the options and key type fields have been
65// removed.
Adam Langleyfa50e742014-04-09 13:57:52 -070066func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) {
Shawn Ledbetter887809b2012-12-10 17:43:09 -050067 in = bytes.TrimSpace(in)
68
69 i := bytes.IndexAny(in, " \t")
70 if i == -1 {
71 i = len(in)
72 }
73 base64Key := in[:i]
74
75 key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key)))
76 n, err := base64.StdEncoding.Decode(key, base64Key)
77 if err != nil {
Adam Langleyfa50e742014-04-09 13:57:52 -070078 return nil, "", err
Shawn Ledbetter887809b2012-12-10 17:43:09 -050079 }
80 key = key[:n]
Adam Langleyfa50e742014-04-09 13:57:52 -070081 out, err = ParsePublicKey(key)
82 if err != nil {
83 return nil, "", err
Shawn Ledbetter887809b2012-12-10 17:43:09 -050084 }
85 comment = string(bytes.TrimSpace(in[i:]))
Adam Langleyfa50e742014-04-09 13:57:52 -070086 return out, comment, nil
Shawn Ledbetter887809b2012-12-10 17:43:09 -050087}
88
Peter Moody3760e012015-12-23 13:51:54 -080089// ParseKnownHosts parses an entry in the format of the known_hosts file.
90//
91// The known_hosts format is documented in the sshd(8) manual page. This
92// function will parse a single entry from in. On successful return, marker
93// will contain the optional marker value (i.e. "cert-authority" or "revoked")
94// or else be empty, hosts will contain the hosts that this entry matches,
95// pubKey will contain the public key and comment will contain any trailing
96// comment at the end of the line. See the sshd(8) manual page for the various
97// forms that a host string can take.
98//
99// The unparsed remainder of the input will be returned in rest. This function
100// can be called repeatedly to parse multiple entries.
101//
102// If no entries were found in the input then err will be io.EOF. Otherwise a
103// non-nil err value indicates a parse error.
104func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey, comment string, rest []byte, err error) {
105 for len(in) > 0 {
106 end := bytes.IndexByte(in, '\n')
107 if end != -1 {
108 rest = in[end+1:]
109 in = in[:end]
110 } else {
111 rest = nil
112 }
113
114 end = bytes.IndexByte(in, '\r')
115 if end != -1 {
116 in = in[:end]
117 }
118
119 in = bytes.TrimSpace(in)
120 if len(in) == 0 || in[0] == '#' {
121 in = rest
122 continue
123 }
124
125 i := bytes.IndexAny(in, " \t")
126 if i == -1 {
127 in = rest
128 continue
129 }
130
Martin Hamrle077efaa2016-07-06 09:13:05 +0200131 // Strip out the beginning of the known_host key.
Peter Moody3760e012015-12-23 13:51:54 -0800132 // This is either an optional marker or a (set of) hostname(s).
133 keyFields := bytes.Fields(in)
134 if len(keyFields) < 3 || len(keyFields) > 5 {
135 return "", nil, nil, "", nil, errors.New("ssh: invalid entry in known_hosts data")
136 }
137
138 // keyFields[0] is either "@cert-authority", "@revoked" or a comma separated
139 // list of hosts
140 marker := ""
141 if keyFields[0][0] == '@' {
142 marker = string(keyFields[0][1:])
143 keyFields = keyFields[1:]
144 }
145
146 hosts := string(keyFields[0])
147 // keyFields[1] contains the key type (e.g. “ssh-rsa”).
148 // However, that information is duplicated inside the
149 // base64-encoded key and so is ignored here.
150
151 key := bytes.Join(keyFields[2:], []byte(" "))
152 if pubKey, comment, err = parseAuthorizedKey(key); err != nil {
153 return "", nil, nil, "", nil, err
154 }
155
156 return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil
157 }
158
159 return "", nil, nil, "", nil, io.EOF
160}
161
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500162// ParseAuthorizedKeys parses a public key from an authorized_keys
163// file used in OpenSSH according to the sshd(8) manual page.
Adam Langleyfa50e742014-04-09 13:57:52 -0700164func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) {
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500165 for len(in) > 0 {
166 end := bytes.IndexByte(in, '\n')
167 if end != -1 {
168 rest = in[end+1:]
169 in = in[:end]
170 } else {
171 rest = nil
172 }
173
174 end = bytes.IndexByte(in, '\r')
175 if end != -1 {
176 in = in[:end]
177 }
178
179 in = bytes.TrimSpace(in)
180 if len(in) == 0 || in[0] == '#' {
181 in = rest
182 continue
183 }
184
185 i := bytes.IndexAny(in, " \t")
186 if i == -1 {
187 in = rest
188 continue
189 }
190
Adam Langleyfa50e742014-04-09 13:57:52 -0700191 if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
192 return out, comment, options, rest, nil
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500193 }
194
195 // No key type recognised. Maybe there's an options field at
196 // the beginning.
197 var b byte
198 inQuote := false
199 var candidateOptions []string
200 optionStart := 0
201 for i, b = range in {
202 isEnd := !inQuote && (b == ' ' || b == '\t')
203 if (b == ',' && !inQuote) || isEnd {
204 if i-optionStart > 0 {
205 candidateOptions = append(candidateOptions, string(in[optionStart:i]))
206 }
207 optionStart = i + 1
208 }
209 if isEnd {
210 break
211 }
212 if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) {
213 inQuote = !inQuote
214 }
215 }
216 for i < len(in) && (in[i] == ' ' || in[i] == '\t') {
217 i++
218 }
219 if i == len(in) {
220 // Invalid line: unmatched quote
221 in = rest
222 continue
223 }
224
225 in = in[i:]
226 i = bytes.IndexAny(in, " \t")
227 if i == -1 {
228 in = rest
229 continue
230 }
231
Adam Langleyfa50e742014-04-09 13:57:52 -0700232 if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
Jonathan Pittman9112f502013-10-22 15:12:41 -0400233 options = candidateOptions
Adam Langleyfa50e742014-04-09 13:57:52 -0700234 return out, comment, options, rest, nil
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500235 }
236
237 in = rest
238 continue
239 }
240
Adam Langleyfa50e742014-04-09 13:57:52 -0700241 return nil, "", nil, nil, errors.New("ssh: no key found")
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500242}
243
244// ParsePublicKey parses an SSH public key formatted for use in
JP Sugarbroad15d8abf2013-10-09 12:56:09 -0400245// the SSH wire protocol according to RFC 4253, section 6.6.
Adam Langleyfa50e742014-04-09 13:57:52 -0700246func ParsePublicKey(in []byte) (out PublicKey, err error) {
JP Sugarbroad15d8abf2013-10-09 12:56:09 -0400247 algo, in, ok := parseString(in)
248 if !ok {
Adam Langleyfa50e742014-04-09 13:57:52 -0700249 return nil, errShortRead
250 }
251 var rest []byte
252 out, rest, err = parsePubKey(in, string(algo))
253 if len(rest) > 0 {
254 return nil, errors.New("ssh: trailing junk in public key")
JP Sugarbroad15d8abf2013-10-09 12:56:09 -0400255 }
256
Adam Langleyfa50e742014-04-09 13:57:52 -0700257 return out, err
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500258}
259
Adam Langleyfa50e742014-04-09 13:57:52 -0700260// MarshalAuthorizedKey serializes key for inclusion in an OpenSSH
261// authorized_keys file. The return value ends with newline.
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400262func MarshalAuthorizedKey(key PublicKey) []byte {
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500263 b := &bytes.Buffer{}
Adam Langleyfa50e742014-04-09 13:57:52 -0700264 b.WriteString(key.Type())
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500265 b.WriteByte(' ')
266 e := base64.NewEncoder(base64.StdEncoding, b)
Adam Langleyfa50e742014-04-09 13:57:52 -0700267 e.Write(key.Marshal())
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500268 e.Close()
269 b.WriteByte('\n')
270 return b.Bytes()
271}
272
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400273// PublicKey is an abstraction of different types of public keys.
274type PublicKey interface {
Adam Langleyfa50e742014-04-09 13:57:52 -0700275 // Type returns the key's type, e.g. "ssh-rsa".
276 Type() string
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400277
278 // Marshal returns the serialized key data in SSH wire format,
Adam Langleyfa50e742014-04-09 13:57:52 -0700279 // with the name prefix.
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400280 Marshal() []byte
281
282 // Verify that sig is a signature on the given data using this
283 // key. This function will hash the data appropriately first.
Adam Langleyfa50e742014-04-09 13:57:52 -0700284 Verify(data []byte, sig *Signature) error
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400285}
286
Martin Garton69267d22016-06-10 08:32:25 +0100287// CryptoPublicKey, if implemented by a PublicKey,
288// returns the underlying crypto.PublicKey form of the key.
289type CryptoPublicKey interface {
290 CryptoPublicKey() crypto.PublicKey
291}
292
Adam Langleyfa50e742014-04-09 13:57:52 -0700293// A Signer can create signatures that verify against a public key.
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400294type Signer interface {
295 // PublicKey returns an associated PublicKey instance.
296 PublicKey() PublicKey
297
298 // Sign returns raw signature for the given data. This method
299 // will apply the hash specified for the keytype to the data.
Adam Langleyfa50e742014-04-09 13:57:52 -0700300 Sign(rand io.Reader, data []byte) (*Signature, error)
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400301}
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400302
303type rsaPublicKey rsa.PublicKey
304
Adam Langleyfa50e742014-04-09 13:57:52 -0700305func (r *rsaPublicKey) Type() string {
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400306 return "ssh-rsa"
307}
308
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400309// parseRSA parses an RSA key according to RFC 4253, section 6.6.
Adam Langleyfa50e742014-04-09 13:57:52 -0700310func parseRSA(in []byte) (out PublicKey, rest []byte, err error) {
311 var w struct {
312 E *big.Int
313 N *big.Int
314 Rest []byte `ssh:"rest"`
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400315 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700316 if err := Unmarshal(in, &w); err != nil {
317 return nil, nil, err
318 }
319
320 if w.E.BitLen() > 24 {
321 return nil, nil, errors.New("ssh: exponent too large")
322 }
323 e := w.E.Int64()
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400324 if e < 3 || e&1 == 0 {
Adam Langleyfa50e742014-04-09 13:57:52 -0700325 return nil, nil, errors.New("ssh: incorrect exponent")
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400326 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700327
328 var key rsa.PublicKey
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400329 key.E = int(e)
Adam Langleyfa50e742014-04-09 13:57:52 -0700330 key.N = w.N
331 return (*rsaPublicKey)(&key), w.Rest, nil
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400332}
333
334func (r *rsaPublicKey) Marshal() []byte {
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400335 e := new(big.Int).SetInt64(int64(r.E))
Peter Moodye84a34b2016-04-19 09:49:29 -0700336 // RSA publickey struct layout should match the struct used by
337 // parseRSACert in the x/crypto/ssh/agent package.
Adam Langleyfa50e742014-04-09 13:57:52 -0700338 wirekey := struct {
339 Name string
340 E *big.Int
341 N *big.Int
342 }{
343 KeyAlgoRSA,
344 e,
345 r.N,
346 }
347 return Marshal(&wirekey)
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400348}
349
Adam Langleyfa50e742014-04-09 13:57:52 -0700350func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
351 if sig.Format != r.Type() {
352 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type())
353 }
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400354 h := crypto.SHA1.New()
355 h.Write(data)
356 digest := h.Sum(nil)
Adam Langleyfa50e742014-04-09 13:57:52 -0700357 return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob)
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400358}
359
Martin Garton69267d22016-06-10 08:32:25 +0100360func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
361 return (*rsa.PublicKey)(r)
362}
363
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400364type dsaPublicKey dsa.PublicKey
365
Adam Langleyfa50e742014-04-09 13:57:52 -0700366func (r *dsaPublicKey) Type() string {
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400367 return "ssh-dss"
368}
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400369
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400370// parseDSA parses an DSA key according to RFC 4253, section 6.6.
Adam Langleyfa50e742014-04-09 13:57:52 -0700371func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
372 var w struct {
373 P, Q, G, Y *big.Int
374 Rest []byte `ssh:"rest"`
375 }
376 if err := Unmarshal(in, &w); err != nil {
377 return nil, nil, err
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400378 }
379
Adam Langleyfa50e742014-04-09 13:57:52 -0700380 key := &dsaPublicKey{
381 Parameters: dsa.Parameters{
382 P: w.P,
383 Q: w.Q,
384 G: w.G,
385 },
386 Y: w.Y,
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400387 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700388 return key, w.Rest, nil
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400389}
390
Adam Langleyfa50e742014-04-09 13:57:52 -0700391func (k *dsaPublicKey) Marshal() []byte {
Peter Moodye84a34b2016-04-19 09:49:29 -0700392 // DSA publickey struct layout should match the struct used by
393 // parseDSACert in the x/crypto/ssh/agent package.
Adam Langleyfa50e742014-04-09 13:57:52 -0700394 w := struct {
395 Name string
396 P, Q, G, Y *big.Int
397 }{
398 k.Type(),
399 k.P,
400 k.Q,
401 k.G,
402 k.Y,
403 }
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400404
Adam Langleyfa50e742014-04-09 13:57:52 -0700405 return Marshal(&w)
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400406}
407
Adam Langleyfa50e742014-04-09 13:57:52 -0700408func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error {
409 if sig.Format != k.Type() {
410 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
411 }
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400412 h := crypto.SHA1.New()
413 h.Write(data)
414 digest := h.Sum(nil)
415
416 // Per RFC 4253, section 6.6,
417 // The value for 'dss_signature_blob' is encoded as a string containing
418 // r, followed by s (which are 160-bit integers, without lengths or
419 // padding, unsigned, and in network byte order).
420 // For DSS purposes, sig.Blob should be exactly 40 bytes in length.
Adam Langleyfa50e742014-04-09 13:57:52 -0700421 if len(sig.Blob) != 40 {
422 return errors.New("ssh: DSA signature parse error")
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400423 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700424 r := new(big.Int).SetBytes(sig.Blob[:20])
425 s := new(big.Int).SetBytes(sig.Blob[20:])
426 if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) {
427 return nil
428 }
429 return errors.New("ssh: signature did not verify")
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400430}
431
Martin Garton69267d22016-06-10 08:32:25 +0100432func (k *dsaPublicKey) CryptoPublicKey() crypto.PublicKey {
433 return (*dsa.PublicKey)(k)
434}
435
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400436type dsaPrivateKey struct {
437 *dsa.PrivateKey
438}
439
440func (k *dsaPrivateKey) PublicKey() PublicKey {
441 return (*dsaPublicKey)(&k.PrivateKey.PublicKey)
442}
443
Adam Langleyfa50e742014-04-09 13:57:52 -0700444func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400445 h := crypto.SHA1.New()
446 h.Write(data)
447 digest := h.Sum(nil)
448 r, s, err := dsa.Sign(rand, k.PrivateKey, digest)
449 if err != nil {
450 return nil, err
451 }
452
453 sig := make([]byte, 40)
Adam Langleyfa50e742014-04-09 13:57:52 -0700454 rb := r.Bytes()
455 sb := s.Bytes()
456
457 copy(sig[20-len(rb):20], rb)
458 copy(sig[40-len(sb):], sb)
459
460 return &Signature{
461 Format: k.PublicKey().Type(),
462 Blob: sig,
463 }, nil
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400464}
465
466type ecdsaPublicKey ecdsa.PublicKey
467
Adam Langleyfa50e742014-04-09 13:57:52 -0700468func (key *ecdsaPublicKey) Type() string {
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400469 return "ecdsa-sha2-" + key.nistID()
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400470}
471
472func (key *ecdsaPublicKey) nistID() string {
473 switch key.Params().BitSize {
474 case 256:
475 return "nistp256"
476 case 384:
477 return "nistp384"
478 case 521:
479 return "nistp521"
480 }
481 panic("ssh: unsupported ecdsa key size")
482}
483
Martin Garton1e61df82016-04-30 22:10:58 +0100484type ed25519PublicKey ed25519.PublicKey
485
486func (key ed25519PublicKey) Type() string {
487 return KeyAlgoED25519
488}
489
490func parseED25519(in []byte) (out PublicKey, rest []byte, err error) {
491 var w struct {
492 KeyBytes []byte
493 Rest []byte `ssh:"rest"`
494 }
495
496 if err := Unmarshal(in, &w); err != nil {
497 return nil, nil, err
498 }
499
500 key := ed25519.PublicKey(w.KeyBytes)
501
502 return (ed25519PublicKey)(key), w.Rest, nil
503}
504
505func (key ed25519PublicKey) Marshal() []byte {
506 w := struct {
507 Name string
508 KeyBytes []byte
509 }{
510 KeyAlgoED25519,
511 []byte(key),
512 }
513 return Marshal(&w)
514}
515
516func (key ed25519PublicKey) Verify(b []byte, sig *Signature) error {
517 if sig.Format != key.Type() {
518 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type())
519 }
520
521 edKey := (ed25519.PublicKey)(key)
522 if ok := ed25519.Verify(edKey, b, sig.Blob); !ok {
523 return errors.New("ssh: signature did not verify")
524 }
525
526 return nil
527}
528
Martin Garton69267d22016-06-10 08:32:25 +0100529func (k ed25519PublicKey) CryptoPublicKey() crypto.PublicKey {
530 return ed25519.PublicKey(k)
531}
532
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400533func supportedEllipticCurve(curve elliptic.Curve) bool {
Adam Langleyfa50e742014-04-09 13:57:52 -0700534 return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400535}
536
537// ecHash returns the hash to match the given elliptic curve, see RFC
538// 5656, section 6.2.1
539func ecHash(curve elliptic.Curve) crypto.Hash {
540 bitSize := curve.Params().BitSize
541 switch {
542 case bitSize <= 256:
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400543 return crypto.SHA256
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400544 case bitSize <= 384:
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400545 return crypto.SHA384
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400546 }
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400547 return crypto.SHA512
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400548}
549
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400550// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1.
Adam Langleyfa50e742014-04-09 13:57:52 -0700551func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
Matt Bostockd5c5f172015-08-16 22:25:54 +0100552 var w struct {
553 Curve string
554 KeyBytes []byte
555 Rest []byte `ssh:"rest"`
556 }
557
558 if err := Unmarshal(in, &w); err != nil {
559 return nil, nil, err
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400560 }
561
562 key := new(ecdsa.PublicKey)
563
Matt Bostockd5c5f172015-08-16 22:25:54 +0100564 switch w.Curve {
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400565 case "nistp256":
566 key.Curve = elliptic.P256()
567 case "nistp384":
568 key.Curve = elliptic.P384()
569 case "nistp521":
570 key.Curve = elliptic.P521()
571 default:
Adam Langleyfa50e742014-04-09 13:57:52 -0700572 return nil, nil, errors.New("ssh: unsupported curve")
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400573 }
574
Matt Bostockd5c5f172015-08-16 22:25:54 +0100575 key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes)
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400576 if key.X == nil || key.Y == nil {
Adam Langleyfa50e742014-04-09 13:57:52 -0700577 return nil, nil, errors.New("ssh: invalid curve point")
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400578 }
Matt Bostockd5c5f172015-08-16 22:25:54 +0100579 return (*ecdsaPublicKey)(key), w.Rest, nil
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400580}
581
582func (key *ecdsaPublicKey) Marshal() []byte {
583 // See RFC 5656, section 3.1.
584 keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y)
Peter Moodye84a34b2016-04-19 09:49:29 -0700585 // ECDSA publickey struct layout should match the struct used by
586 // parseECDSACert in the x/crypto/ssh/agent package.
Adam Langleyfa50e742014-04-09 13:57:52 -0700587 w := struct {
588 Name string
589 ID string
590 Key []byte
591 }{
592 key.Type(),
593 key.nistID(),
594 keyBytes,
595 }
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400596
Adam Langleyfa50e742014-04-09 13:57:52 -0700597 return Marshal(&w)
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400598}
599
Adam Langleyfa50e742014-04-09 13:57:52 -0700600func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
601 if sig.Format != key.Type() {
602 return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type())
603 }
604
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400605 h := ecHash(key.Curve).New()
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400606 h.Write(data)
607 digest := h.Sum(nil)
608
609 // Per RFC 5656, section 3.1.2,
610 // The ecdsa_signature_blob value has the following specific encoding:
611 // mpint r
612 // mpint s
Adam Langleyfa50e742014-04-09 13:57:52 -0700613 var ecSig struct {
614 R *big.Int
615 S *big.Int
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400616 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700617
618 if err := Unmarshal(sig.Blob, &ecSig); err != nil {
619 return err
Han-Wen Nienhuyse62b2ae2013-09-13 14:25:14 -0400620 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700621
622 if ecdsa.Verify((*ecdsa.PublicKey)(key), digest, ecSig.R, ecSig.S) {
623 return nil
624 }
625 return errors.New("ssh: signature did not verify")
Shawn Ledbetter887809b2012-12-10 17:43:09 -0500626}
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400627
Martin Garton69267d22016-06-10 08:32:25 +0100628func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey {
629 return (*ecdsa.PublicKey)(k)
630}
631
Evan Brodere74b0352015-06-12 19:11:01 +0200632// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
633// *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding
634// Signer instance. ECDSA keys must use P-256, P-384 or P-521.
635func NewSignerFromKey(key interface{}) (Signer, error) {
636 switch key := key.(type) {
637 case crypto.Signer:
638 return NewSignerFromSigner(key)
639 case *dsa.PrivateKey:
640 return &dsaPrivateKey{key}, nil
641 default:
642 return nil, fmt.Errorf("ssh: unsupported key type %T", key)
643 }
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400644}
645
Evan Brodere74b0352015-06-12 19:11:01 +0200646type wrappedSigner struct {
647 signer crypto.Signer
648 pubKey PublicKey
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400649}
650
Evan Brodere74b0352015-06-12 19:11:01 +0200651// NewSignerFromSigner takes any crypto.Signer implementation and
652// returns a corresponding Signer interface. This can be used, for
653// example, with keys kept in hardware modules.
654func NewSignerFromSigner(signer crypto.Signer) (Signer, error) {
655 pubKey, err := NewPublicKey(signer.Public())
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400656 if err != nil {
657 return nil, err
658 }
659
Evan Brodere74b0352015-06-12 19:11:01 +0200660 return &wrappedSigner{signer, pubKey}, nil
661}
662
663func (s *wrappedSigner) PublicKey() PublicKey {
664 return s.pubKey
665}
666
667func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
668 var hashFunc crypto.Hash
669
670 switch key := s.pubKey.(type) {
671 case *rsaPublicKey, *dsaPublicKey:
672 hashFunc = crypto.SHA1
673 case *ecdsaPublicKey:
674 hashFunc = ecHash(key.Curve)
Martin Garton1e61df82016-04-30 22:10:58 +0100675 case ed25519PublicKey:
Evan Brodere74b0352015-06-12 19:11:01 +0200676 default:
677 return nil, fmt.Errorf("ssh: unsupported key type %T", key)
678 }
679
Martin Garton1e61df82016-04-30 22:10:58 +0100680 var digest []byte
681 if hashFunc != 0 {
682 h := hashFunc.New()
683 h.Write(data)
684 digest = h.Sum(nil)
685 } else {
686 digest = data
687 }
Evan Brodere74b0352015-06-12 19:11:01 +0200688
689 signature, err := s.signer.Sign(rand, digest, hashFunc)
690 if err != nil {
691 return nil, err
692 }
693
694 // crypto.Signer.Sign is expected to return an ASN.1-encoded signature
695 // for ECDSA and DSA, but that's not the encoding expected by SSH, so
696 // re-encode.
697 switch s.pubKey.(type) {
698 case *ecdsaPublicKey, *dsaPublicKey:
699 type asn1Signature struct {
700 R, S *big.Int
701 }
702 asn1Sig := new(asn1Signature)
703 _, err := asn1.Unmarshal(signature, asn1Sig)
704 if err != nil {
705 return nil, err
706 }
707
708 switch s.pubKey.(type) {
709 case *ecdsaPublicKey:
710 signature = Marshal(asn1Sig)
711
712 case *dsaPublicKey:
713 signature = make([]byte, 40)
714 r := asn1Sig.R.Bytes()
715 s := asn1Sig.S.Bytes()
716 copy(signature[20-len(r):20], r)
717 copy(signature[40-len(s):40], s)
718 }
719 }
720
Adam Langleyfa50e742014-04-09 13:57:52 -0700721 return &Signature{
Evan Brodere74b0352015-06-12 19:11:01 +0200722 Format: s.pubKey.Type(),
723 Blob: signature,
Adam Langleyfa50e742014-04-09 13:57:52 -0700724 }, nil
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400725}
726
Martin Garton1e61df82016-04-30 22:10:58 +0100727// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
Dmitry Savintsev4cd25d62016-07-26 15:00:17 +0200728// or ed25519.PublicKey returns a corresponding PublicKey instance.
729// ECDSA keys must use P-256, P-384 or P-521.
Evan Brodere74b0352015-06-12 19:11:01 +0200730func NewPublicKey(key interface{}) (PublicKey, error) {
731 switch key := key.(type) {
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400732 case *rsa.PublicKey:
Evan Brodere74b0352015-06-12 19:11:01 +0200733 return (*rsaPublicKey)(key), nil
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400734 case *ecdsa.PublicKey:
Evan Brodere74b0352015-06-12 19:11:01 +0200735 if !supportedEllipticCurve(key.Curve) {
736 return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported.")
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400737 }
Evan Brodere74b0352015-06-12 19:11:01 +0200738 return (*ecdsaPublicKey)(key), nil
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400739 case *dsa.PublicKey:
Evan Brodere74b0352015-06-12 19:11:01 +0200740 return (*dsaPublicKey)(key), nil
Martin Garton1e61df82016-04-30 22:10:58 +0100741 case ed25519.PublicKey:
742 return (ed25519PublicKey)(key), nil
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400743 default:
Evan Brodere74b0352015-06-12 19:11:01 +0200744 return nil, fmt.Errorf("ssh: unsupported key type %T", key)
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400745 }
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400746}
747
Adam Langleyfa50e742014-04-09 13:57:52 -0700748// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports
749// the same keys as ParseRawPrivateKey.
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400750func ParsePrivateKey(pemBytes []byte) (Signer, error) {
Adam Langleyfa50e742014-04-09 13:57:52 -0700751 key, err := ParseRawPrivateKey(pemBytes)
752 if err != nil {
753 return nil, err
754 }
755
756 return NewSignerFromKey(key)
757}
758
Emmanuel Odekea20de3f2016-09-23 01:39:13 -0700759// encryptedBlock tells whether a private key is
760// encrypted by examining its Proc-Type header
761// for a mention of ENCRYPTED
762// according to RFC 1421 Section 4.6.1.1.
763func encryptedBlock(block *pem.Block) bool {
764 return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED")
765}
766
Adam Langleyfa50e742014-04-09 13:57:52 -0700767// ParseRawPrivateKey returns a private key from a PEM encoded private key. It
768// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
769func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400770 block, _ := pem.Decode(pemBytes)
771 if block == nil {
772 return nil, errors.New("ssh: no key found")
773 }
774
Emmanuel Odekea20de3f2016-09-23 01:39:13 -0700775 if encryptedBlock(block) {
776 return nil, errors.New("ssh: cannot decode encrypted private keys")
777 }
778
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400779 switch block.Type {
780 case "RSA PRIVATE KEY":
Adam Langleyfa50e742014-04-09 13:57:52 -0700781 return x509.ParsePKCS1PrivateKey(block.Bytes)
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400782 case "EC PRIVATE KEY":
Adam Langleyfa50e742014-04-09 13:57:52 -0700783 return x509.ParseECPrivateKey(block.Bytes)
Han-Wen Nienhuys4e058122013-09-26 11:17:52 -0400784 case "DSA PRIVATE KEY":
Adam Langleyfa50e742014-04-09 13:57:52 -0700785 return ParseDSAPrivateKey(block.Bytes)
Martin Garton1e61df82016-04-30 22:10:58 +0100786 case "OPENSSH PRIVATE KEY":
787 return parseOpenSSHPrivateKey(block.Bytes)
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400788 default:
789 return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
790 }
Han-Wen Nienhuys934c14f2013-09-19 14:45:31 -0400791}
Han-Wen Nienhuys4e058122013-09-26 11:17:52 -0400792
Adam Langleyfa50e742014-04-09 13:57:52 -0700793// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as
794// specified by the OpenSSL DSA man page.
795func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
796 var k struct {
Han-Wen Nienhuys4e058122013-09-26 11:17:52 -0400797 Version int
798 P *big.Int
799 Q *big.Int
800 G *big.Int
Han-Wen Nienhuys4e058122013-09-26 11:17:52 -0400801 Pub *big.Int
Paul Quernab8224632017-01-17 17:10:46 -0800802 Priv *big.Int
Adam Langleyfa50e742014-04-09 13:57:52 -0700803 }
804 rest, err := asn1.Unmarshal(der, &k)
Han-Wen Nienhuys4e058122013-09-26 11:17:52 -0400805 if err != nil {
806 return nil, errors.New("ssh: failed to parse DSA key: " + err.Error())
807 }
808 if len(rest) > 0 {
809 return nil, errors.New("ssh: garbage after DSA key")
810 }
811
812 return &dsa.PrivateKey{
813 PublicKey: dsa.PublicKey{
814 Parameters: dsa.Parameters{
815 P: k.P,
816 Q: k.Q,
817 G: k.G,
818 },
Paul Quernab8224632017-01-17 17:10:46 -0800819 Y: k.Pub,
Han-Wen Nienhuys4e058122013-09-26 11:17:52 -0400820 },
Paul Quernab8224632017-01-17 17:10:46 -0800821 X: k.Priv,
Han-Wen Nienhuys4e058122013-09-26 11:17:52 -0400822 }, nil
823}
Martin Garton1e61df82016-04-30 22:10:58 +0100824
825// Implemented based on the documentation at
826// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
827func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
828 magic := append([]byte("openssh-key-v1"), 0)
829 if !bytes.Equal(magic, key[0:len(magic)]) {
830 return nil, errors.New("ssh: invalid openssh private key format")
831 }
832 remaining := key[len(magic):]
833
834 var w struct {
835 CipherName string
836 KdfName string
837 KdfOpts string
838 NumKeys uint32
839 PubKey []byte
840 PrivKeyBlock []byte
841 }
842
843 if err := Unmarshal(remaining, &w); err != nil {
844 return nil, err
845 }
846
847 pk1 := struct {
848 Check1 uint32
849 Check2 uint32
850 Keytype string
851 Pub []byte
852 Priv []byte
853 Comment string
854 Pad []byte `ssh:"rest"`
855 }{}
856
857 if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
858 return nil, err
859 }
860
861 if pk1.Check1 != pk1.Check2 {
862 return nil, errors.New("ssh: checkint mismatch")
863 }
864
865 // we only handle ed25519 keys currently
866 if pk1.Keytype != KeyAlgoED25519 {
867 return nil, errors.New("ssh: unhandled key type")
868 }
869
870 for i, b := range pk1.Pad {
871 if int(b) != i+1 {
872 return nil, errors.New("ssh: padding not as expected")
873 }
874 }
875
876 if len(pk1.Priv) != ed25519.PrivateKeySize {
877 return nil, errors.New("ssh: private key unexpected length")
878 }
879
880 pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
881 copy(pk, pk1.Priv)
882 return &pk, nil
883}
Ryuzo Yamamotoede567c2016-11-04 15:41:44 -0400884
885// FingerprintLegacyMD5 returns the user presentation of the key's
886// fingerprint as described by RFC 4716 section 4.
887func FingerprintLegacyMD5(pubKey PublicKey) string {
888 md5sum := md5.Sum(pubKey.Marshal())
889 hexarray := make([]string, len(md5sum))
890 for i, c := range md5sum {
891 hexarray[i] = hex.EncodeToString([]byte{c})
892 }
893 return strings.Join(hexarray, ":")
894}
895
896// FingerprintSHA256 returns the user presentation of the key's
897// fingerprint as unpadded base64 encoded sha256 hash.
898// This format was introduced from OpenSSH 6.8.
899// https://www.openssh.com/txt/release-6.8
900// https://tools.ietf.org/html/rfc4648#section-3.2 (unpadded base64 encoding)
901func FingerprintSHA256(pubKey PublicKey) string {
902 sha256sum := sha256.Sum256(pubKey.Marshal())
903 hash := base64.RawStdEncoding.EncodeToString(sha256sum[:])
904 return "SHA256:" + hash
905}