]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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 | ||
5 | package ssh | |
6 | ||
7 | import ( | |
8 | "bytes" | |
9 | "crypto" | |
10 | "crypto/dsa" | |
11 | "crypto/ecdsa" | |
12 | "crypto/elliptic" | |
13 | "crypto/md5" | |
14 | "crypto/rsa" | |
15 | "crypto/sha256" | |
16 | "crypto/x509" | |
17 | "encoding/asn1" | |
18 | "encoding/base64" | |
19 | "encoding/hex" | |
20 | "encoding/pem" | |
21 | "errors" | |
22 | "fmt" | |
23 | "io" | |
24 | "math/big" | |
25 | "strings" | |
26 | ||
27 | "golang.org/x/crypto/ed25519" | |
28 | ) | |
29 | ||
30 | // These constants represent the algorithm names for key types supported by this | |
31 | // package. | |
32 | const ( | |
33 | KeyAlgoRSA = "ssh-rsa" | |
34 | KeyAlgoDSA = "ssh-dss" | |
35 | KeyAlgoECDSA256 = "ecdsa-sha2-nistp256" | |
36 | KeyAlgoECDSA384 = "ecdsa-sha2-nistp384" | |
37 | KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" | |
38 | KeyAlgoED25519 = "ssh-ed25519" | |
39 | ) | |
40 | ||
41 | // parsePubKey parses a public key of the given algorithm. | |
42 | // Use ParsePublicKey for keys with prepended algorithm. | |
43 | func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) { | |
44 | switch algo { | |
45 | case KeyAlgoRSA: | |
46 | return parseRSA(in) | |
47 | case KeyAlgoDSA: | |
48 | return parseDSA(in) | |
49 | case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: | |
50 | return parseECDSA(in) | |
51 | case KeyAlgoED25519: | |
52 | return parseED25519(in) | |
53 | case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01: | |
54 | cert, err := parseCert(in, certToPrivAlgo(algo)) | |
55 | if err != nil { | |
56 | return nil, nil, err | |
57 | } | |
58 | return cert, nil, nil | |
59 | } | |
60 | return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo) | |
61 | } | |
62 | ||
63 | // 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. | |
66 | func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) { | |
67 | 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 { | |
78 | return nil, "", err | |
79 | } | |
80 | key = key[:n] | |
81 | out, err = ParsePublicKey(key) | |
82 | if err != nil { | |
83 | return nil, "", err | |
84 | } | |
85 | comment = string(bytes.TrimSpace(in[i:])) | |
86 | return out, comment, nil | |
87 | } | |
88 | ||
89 | // 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. | |
104 | func 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 | ||
131 | // Strip out the beginning of the known_host key. | |
132 | // 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 | ||
162 | // ParseAuthorizedKeys parses a public key from an authorized_keys | |
163 | // file used in OpenSSH according to the sshd(8) manual page. | |
164 | func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { | |
165 | 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 | ||
191 | if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { | |
192 | return out, comment, options, rest, nil | |
193 | } | |
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 | ||
232 | if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { | |
233 | options = candidateOptions | |
234 | return out, comment, options, rest, nil | |
235 | } | |
236 | ||
237 | in = rest | |
238 | continue | |
239 | } | |
240 | ||
241 | return nil, "", nil, nil, errors.New("ssh: no key found") | |
242 | } | |
243 | ||
244 | // ParsePublicKey parses an SSH public key formatted for use in | |
245 | // the SSH wire protocol according to RFC 4253, section 6.6. | |
246 | func ParsePublicKey(in []byte) (out PublicKey, err error) { | |
247 | algo, in, ok := parseString(in) | |
248 | if !ok { | |
249 | 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") | |
255 | } | |
256 | ||
257 | return out, err | |
258 | } | |
259 | ||
260 | // MarshalAuthorizedKey serializes key for inclusion in an OpenSSH | |
261 | // authorized_keys file. The return value ends with newline. | |
262 | func MarshalAuthorizedKey(key PublicKey) []byte { | |
263 | b := &bytes.Buffer{} | |
264 | b.WriteString(key.Type()) | |
265 | b.WriteByte(' ') | |
266 | e := base64.NewEncoder(base64.StdEncoding, b) | |
267 | e.Write(key.Marshal()) | |
268 | e.Close() | |
269 | b.WriteByte('\n') | |
270 | return b.Bytes() | |
271 | } | |
272 | ||
273 | // PublicKey is an abstraction of different types of public keys. | |
274 | type PublicKey interface { | |
275 | // Type returns the key's type, e.g. "ssh-rsa". | |
276 | Type() string | |
277 | ||
278 | // Marshal returns the serialized key data in SSH wire format, | |
279 | // with the name prefix. | |
280 | 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. | |
284 | Verify(data []byte, sig *Signature) error | |
285 | } | |
286 | ||
287 | // CryptoPublicKey, if implemented by a PublicKey, | |
288 | // returns the underlying crypto.PublicKey form of the key. | |
289 | type CryptoPublicKey interface { | |
290 | CryptoPublicKey() crypto.PublicKey | |
291 | } | |
292 | ||
293 | // A Signer can create signatures that verify against a public key. | |
294 | type 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. | |
300 | Sign(rand io.Reader, data []byte) (*Signature, error) | |
301 | } | |
302 | ||
303 | type rsaPublicKey rsa.PublicKey | |
304 | ||
305 | func (r *rsaPublicKey) Type() string { | |
306 | return "ssh-rsa" | |
307 | } | |
308 | ||
309 | // parseRSA parses an RSA key according to RFC 4253, section 6.6. | |
310 | func 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"` | |
315 | } | |
316 | 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() | |
324 | if e < 3 || e&1 == 0 { | |
325 | return nil, nil, errors.New("ssh: incorrect exponent") | |
326 | } | |
327 | ||
328 | var key rsa.PublicKey | |
329 | key.E = int(e) | |
330 | key.N = w.N | |
331 | return (*rsaPublicKey)(&key), w.Rest, nil | |
332 | } | |
333 | ||
334 | func (r *rsaPublicKey) Marshal() []byte { | |
335 | e := new(big.Int).SetInt64(int64(r.E)) | |
336 | // RSA publickey struct layout should match the struct used by | |
337 | // parseRSACert in the x/crypto/ssh/agent package. | |
338 | 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) | |
348 | } | |
349 | ||
350 | func (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 | } | |
354 | h := crypto.SHA1.New() | |
355 | h.Write(data) | |
356 | digest := h.Sum(nil) | |
357 | return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob) | |
358 | } | |
359 | ||
360 | func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey { | |
361 | return (*rsa.PublicKey)(r) | |
362 | } | |
363 | ||
364 | type dsaPublicKey dsa.PublicKey | |
365 | ||
366 | func (r *dsaPublicKey) Type() string { | |
367 | return "ssh-dss" | |
368 | } | |
369 | ||
370 | // parseDSA parses an DSA key according to RFC 4253, section 6.6. | |
371 | func 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 | |
378 | } | |
379 | ||
380 | key := &dsaPublicKey{ | |
381 | Parameters: dsa.Parameters{ | |
382 | P: w.P, | |
383 | Q: w.Q, | |
384 | G: w.G, | |
385 | }, | |
386 | Y: w.Y, | |
387 | } | |
388 | return key, w.Rest, nil | |
389 | } | |
390 | ||
391 | func (k *dsaPublicKey) Marshal() []byte { | |
392 | // DSA publickey struct layout should match the struct used by | |
393 | // parseDSACert in the x/crypto/ssh/agent package. | |
394 | 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 | } | |
404 | ||
405 | return Marshal(&w) | |
406 | } | |
407 | ||
408 | func (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 | } | |
412 | 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. | |
421 | if len(sig.Blob) != 40 { | |
422 | return errors.New("ssh: DSA signature parse error") | |
423 | } | |
424 | 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") | |
430 | } | |
431 | ||
432 | func (k *dsaPublicKey) CryptoPublicKey() crypto.PublicKey { | |
433 | return (*dsa.PublicKey)(k) | |
434 | } | |
435 | ||
436 | type dsaPrivateKey struct { | |
437 | *dsa.PrivateKey | |
438 | } | |
439 | ||
440 | func (k *dsaPrivateKey) PublicKey() PublicKey { | |
441 | return (*dsaPublicKey)(&k.PrivateKey.PublicKey) | |
442 | } | |
443 | ||
444 | func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { | |
445 | 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) | |
454 | 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 | |
464 | } | |
465 | ||
466 | type ecdsaPublicKey ecdsa.PublicKey | |
467 | ||
468 | func (key *ecdsaPublicKey) Type() string { | |
469 | return "ecdsa-sha2-" + key.nistID() | |
470 | } | |
471 | ||
472 | func (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 | ||
484 | type ed25519PublicKey ed25519.PublicKey | |
485 | ||
486 | func (key ed25519PublicKey) Type() string { | |
487 | return KeyAlgoED25519 | |
488 | } | |
489 | ||
490 | func 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 | ||
505 | func (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 | ||
516 | func (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 | ||
529 | func (k ed25519PublicKey) CryptoPublicKey() crypto.PublicKey { | |
530 | return ed25519.PublicKey(k) | |
531 | } | |
532 | ||
533 | func supportedEllipticCurve(curve elliptic.Curve) bool { | |
534 | return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() | |
535 | } | |
536 | ||
537 | // ecHash returns the hash to match the given elliptic curve, see RFC | |
538 | // 5656, section 6.2.1 | |
539 | func ecHash(curve elliptic.Curve) crypto.Hash { | |
540 | bitSize := curve.Params().BitSize | |
541 | switch { | |
542 | case bitSize <= 256: | |
543 | return crypto.SHA256 | |
544 | case bitSize <= 384: | |
545 | return crypto.SHA384 | |
546 | } | |
547 | return crypto.SHA512 | |
548 | } | |
549 | ||
550 | // parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. | |
551 | func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { | |
552 | 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 | |
560 | } | |
561 | ||
562 | key := new(ecdsa.PublicKey) | |
563 | ||
564 | switch w.Curve { | |
565 | 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: | |
572 | return nil, nil, errors.New("ssh: unsupported curve") | |
573 | } | |
574 | ||
575 | key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes) | |
576 | if key.X == nil || key.Y == nil { | |
577 | return nil, nil, errors.New("ssh: invalid curve point") | |
578 | } | |
579 | return (*ecdsaPublicKey)(key), w.Rest, nil | |
580 | } | |
581 | ||
582 | func (key *ecdsaPublicKey) Marshal() []byte { | |
583 | // See RFC 5656, section 3.1. | |
584 | keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y) | |
585 | // ECDSA publickey struct layout should match the struct used by | |
586 | // parseECDSACert in the x/crypto/ssh/agent package. | |
587 | w := struct { | |
588 | Name string | |
589 | ID string | |
590 | Key []byte | |
591 | }{ | |
592 | key.Type(), | |
593 | key.nistID(), | |
594 | keyBytes, | |
595 | } | |
596 | ||
597 | return Marshal(&w) | |
598 | } | |
599 | ||
600 | func (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 | ||
605 | h := ecHash(key.Curve).New() | |
606 | 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 | |
613 | var ecSig struct { | |
614 | R *big.Int | |
615 | S *big.Int | |
616 | } | |
617 | ||
618 | if err := Unmarshal(sig.Blob, &ecSig); err != nil { | |
619 | return err | |
620 | } | |
621 | ||
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") | |
626 | } | |
627 | ||
628 | func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey { | |
629 | return (*ecdsa.PublicKey)(k) | |
630 | } | |
631 | ||
632 | // 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. | |
635 | func 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 | } | |
644 | } | |
645 | ||
646 | type wrappedSigner struct { | |
647 | signer crypto.Signer | |
648 | pubKey PublicKey | |
649 | } | |
650 | ||
651 | // 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. | |
654 | func NewSignerFromSigner(signer crypto.Signer) (Signer, error) { | |
655 | pubKey, err := NewPublicKey(signer.Public()) | |
656 | if err != nil { | |
657 | return nil, err | |
658 | } | |
659 | ||
660 | return &wrappedSigner{signer, pubKey}, nil | |
661 | } | |
662 | ||
663 | func (s *wrappedSigner) PublicKey() PublicKey { | |
664 | return s.pubKey | |
665 | } | |
666 | ||
667 | func (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) | |
675 | case ed25519PublicKey: | |
676 | default: | |
677 | return nil, fmt.Errorf("ssh: unsupported key type %T", key) | |
678 | } | |
679 | ||
680 | 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 | } | |
688 | ||
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 | ||
721 | return &Signature{ | |
722 | Format: s.pubKey.Type(), | |
723 | Blob: signature, | |
724 | }, nil | |
725 | } | |
726 | ||
727 | // NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey, | |
728 | // or ed25519.PublicKey returns a corresponding PublicKey instance. | |
729 | // ECDSA keys must use P-256, P-384 or P-521. | |
730 | func NewPublicKey(key interface{}) (PublicKey, error) { | |
731 | switch key := key.(type) { | |
732 | case *rsa.PublicKey: | |
733 | return (*rsaPublicKey)(key), nil | |
734 | case *ecdsa.PublicKey: | |
735 | if !supportedEllipticCurve(key.Curve) { | |
736 | return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported.") | |
737 | } | |
738 | return (*ecdsaPublicKey)(key), nil | |
739 | case *dsa.PublicKey: | |
740 | return (*dsaPublicKey)(key), nil | |
741 | case ed25519.PublicKey: | |
742 | return (ed25519PublicKey)(key), nil | |
743 | default: | |
744 | return nil, fmt.Errorf("ssh: unsupported key type %T", key) | |
745 | } | |
746 | } | |
747 | ||
748 | // ParsePrivateKey returns a Signer from a PEM encoded private key. It supports | |
749 | // the same keys as ParseRawPrivateKey. | |
750 | func ParsePrivateKey(pemBytes []byte) (Signer, error) { | |
751 | key, err := ParseRawPrivateKey(pemBytes) | |
752 | if err != nil { | |
753 | return nil, err | |
754 | } | |
755 | ||
756 | return NewSignerFromKey(key) | |
757 | } | |
758 | ||
759 | // 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. | |
763 | func encryptedBlock(block *pem.Block) bool { | |
764 | return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED") | |
765 | } | |
766 | ||
767 | // ParseRawPrivateKey returns a private key from a PEM encoded private key. It | |
768 | // supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. | |
769 | func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { | |
770 | block, _ := pem.Decode(pemBytes) | |
771 | if block == nil { | |
772 | return nil, errors.New("ssh: no key found") | |
773 | } | |
774 | ||
775 | if encryptedBlock(block) { | |
776 | return nil, errors.New("ssh: cannot decode encrypted private keys") | |
777 | } | |
778 | ||
779 | switch block.Type { | |
780 | case "RSA PRIVATE KEY": | |
781 | return x509.ParsePKCS1PrivateKey(block.Bytes) | |
782 | case "EC PRIVATE KEY": | |
783 | return x509.ParseECPrivateKey(block.Bytes) | |
784 | case "DSA PRIVATE KEY": | |
785 | return ParseDSAPrivateKey(block.Bytes) | |
786 | case "OPENSSH PRIVATE KEY": | |
787 | return parseOpenSSHPrivateKey(block.Bytes) | |
788 | default: | |
789 | return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) | |
790 | } | |
791 | } | |
792 | ||
793 | // ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as | |
794 | // specified by the OpenSSL DSA man page. | |
795 | func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { | |
796 | var k struct { | |
797 | Version int | |
798 | P *big.Int | |
799 | Q *big.Int | |
800 | G *big.Int | |
801 | Pub *big.Int | |
802 | Priv *big.Int | |
803 | } | |
804 | rest, err := asn1.Unmarshal(der, &k) | |
805 | 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 | }, | |
819 | Y: k.Pub, | |
820 | }, | |
821 | X: k.Priv, | |
822 | }, nil | |
823 | } | |
824 | ||
825 | // Implemented based on the documentation at | |
826 | // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key | |
827 | func 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 | } | |
884 | ||
885 | // FingerprintLegacyMD5 returns the user presentation of the key's | |
886 | // fingerprint as described by RFC 4716 section 4. | |
887 | func 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) | |
901 | func FingerprintSHA256(pubKey PublicKey) string { | |
902 | sha256sum := sha256.Sum256(pubKey.Marshal()) | |
903 | hash := base64.RawStdEncoding.EncodeToString(sha256sum[:]) | |
904 | return "SHA256:" + hash | |
905 | } |