diff options
Diffstat (limited to 'vendor/golang.org/x/crypto/ssh/certs.go')
-rw-r--r-- | vendor/golang.org/x/crypto/ssh/certs.go | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go new file mode 100644 index 0000000..6331c94 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/certs.go | |||
@@ -0,0 +1,503 @@ | |||
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 | "errors" | ||
10 | "fmt" | ||
11 | "io" | ||
12 | "net" | ||
13 | "sort" | ||
14 | "time" | ||
15 | ) | ||
16 | |||
17 | // These constants from [PROTOCOL.certkeys] represent the algorithm names | ||
18 | // for certificate types supported by this package. | ||
19 | const ( | ||
20 | CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com" | ||
21 | CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com" | ||
22 | CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com" | ||
23 | CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com" | ||
24 | CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com" | ||
25 | CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com" | ||
26 | ) | ||
27 | |||
28 | // Certificate types distinguish between host and user | ||
29 | // certificates. The values can be set in the CertType field of | ||
30 | // Certificate. | ||
31 | const ( | ||
32 | UserCert = 1 | ||
33 | HostCert = 2 | ||
34 | ) | ||
35 | |||
36 | // Signature represents a cryptographic signature. | ||
37 | type Signature struct { | ||
38 | Format string | ||
39 | Blob []byte | ||
40 | } | ||
41 | |||
42 | // CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that | ||
43 | // a certificate does not expire. | ||
44 | const CertTimeInfinity = 1<<64 - 1 | ||
45 | |||
46 | // An Certificate represents an OpenSSH certificate as defined in | ||
47 | // [PROTOCOL.certkeys]?rev=1.8. | ||
48 | type Certificate struct { | ||
49 | Nonce []byte | ||
50 | Key PublicKey | ||
51 | Serial uint64 | ||
52 | CertType uint32 | ||
53 | KeyId string | ||
54 | ValidPrincipals []string | ||
55 | ValidAfter uint64 | ||
56 | ValidBefore uint64 | ||
57 | Permissions | ||
58 | Reserved []byte | ||
59 | SignatureKey PublicKey | ||
60 | Signature *Signature | ||
61 | } | ||
62 | |||
63 | // genericCertData holds the key-independent part of the certificate data. | ||
64 | // Overall, certificates contain an nonce, public key fields and | ||
65 | // key-independent fields. | ||
66 | type genericCertData struct { | ||
67 | Serial uint64 | ||
68 | CertType uint32 | ||
69 | KeyId string | ||
70 | ValidPrincipals []byte | ||
71 | ValidAfter uint64 | ||
72 | ValidBefore uint64 | ||
73 | CriticalOptions []byte | ||
74 | Extensions []byte | ||
75 | Reserved []byte | ||
76 | SignatureKey []byte | ||
77 | Signature []byte | ||
78 | } | ||
79 | |||
80 | func marshalStringList(namelist []string) []byte { | ||
81 | var to []byte | ||
82 | for _, name := range namelist { | ||
83 | s := struct{ N string }{name} | ||
84 | to = append(to, Marshal(&s)...) | ||
85 | } | ||
86 | return to | ||
87 | } | ||
88 | |||
89 | type optionsTuple struct { | ||
90 | Key string | ||
91 | Value []byte | ||
92 | } | ||
93 | |||
94 | type optionsTupleValue struct { | ||
95 | Value string | ||
96 | } | ||
97 | |||
98 | // serialize a map of critical options or extensions | ||
99 | // issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, | ||
100 | // we need two length prefixes for a non-empty string value | ||
101 | func marshalTuples(tups map[string]string) []byte { | ||
102 | keys := make([]string, 0, len(tups)) | ||
103 | for key := range tups { | ||
104 | keys = append(keys, key) | ||
105 | } | ||
106 | sort.Strings(keys) | ||
107 | |||
108 | var ret []byte | ||
109 | for _, key := range keys { | ||
110 | s := optionsTuple{Key: key} | ||
111 | if value := tups[key]; len(value) > 0 { | ||
112 | s.Value = Marshal(&optionsTupleValue{value}) | ||
113 | } | ||
114 | ret = append(ret, Marshal(&s)...) | ||
115 | } | ||
116 | return ret | ||
117 | } | ||
118 | |||
119 | // issue #10569 - per [PROTOCOL.certkeys] and SSH implementation, | ||
120 | // we need two length prefixes for a non-empty option value | ||
121 | func parseTuples(in []byte) (map[string]string, error) { | ||
122 | tups := map[string]string{} | ||
123 | var lastKey string | ||
124 | var haveLastKey bool | ||
125 | |||
126 | for len(in) > 0 { | ||
127 | var key, val, extra []byte | ||
128 | var ok bool | ||
129 | |||
130 | if key, in, ok = parseString(in); !ok { | ||
131 | return nil, errShortRead | ||
132 | } | ||
133 | keyStr := string(key) | ||
134 | // according to [PROTOCOL.certkeys], the names must be in | ||
135 | // lexical order. | ||
136 | if haveLastKey && keyStr <= lastKey { | ||
137 | return nil, fmt.Errorf("ssh: certificate options are not in lexical order") | ||
138 | } | ||
139 | lastKey, haveLastKey = keyStr, true | ||
140 | // the next field is a data field, which if non-empty has a string embedded | ||
141 | if val, in, ok = parseString(in); !ok { | ||
142 | return nil, errShortRead | ||
143 | } | ||
144 | if len(val) > 0 { | ||
145 | val, extra, ok = parseString(val) | ||
146 | if !ok { | ||
147 | return nil, errShortRead | ||
148 | } | ||
149 | if len(extra) > 0 { | ||
150 | return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value") | ||
151 | } | ||
152 | tups[keyStr] = string(val) | ||
153 | } else { | ||
154 | tups[keyStr] = "" | ||
155 | } | ||
156 | } | ||
157 | return tups, nil | ||
158 | } | ||
159 | |||
160 | func parseCert(in []byte, privAlgo string) (*Certificate, error) { | ||
161 | nonce, rest, ok := parseString(in) | ||
162 | if !ok { | ||
163 | return nil, errShortRead | ||
164 | } | ||
165 | |||
166 | key, rest, err := parsePubKey(rest, privAlgo) | ||
167 | if err != nil { | ||
168 | return nil, err | ||
169 | } | ||
170 | |||
171 | var g genericCertData | ||
172 | if err := Unmarshal(rest, &g); err != nil { | ||
173 | return nil, err | ||
174 | } | ||
175 | |||
176 | c := &Certificate{ | ||
177 | Nonce: nonce, | ||
178 | Key: key, | ||
179 | Serial: g.Serial, | ||
180 | CertType: g.CertType, | ||
181 | KeyId: g.KeyId, | ||
182 | ValidAfter: g.ValidAfter, | ||
183 | ValidBefore: g.ValidBefore, | ||
184 | } | ||
185 | |||
186 | for principals := g.ValidPrincipals; len(principals) > 0; { | ||
187 | principal, rest, ok := parseString(principals) | ||
188 | if !ok { | ||
189 | return nil, errShortRead | ||
190 | } | ||
191 | c.ValidPrincipals = append(c.ValidPrincipals, string(principal)) | ||
192 | principals = rest | ||
193 | } | ||
194 | |||
195 | c.CriticalOptions, err = parseTuples(g.CriticalOptions) | ||
196 | if err != nil { | ||
197 | return nil, err | ||
198 | } | ||
199 | c.Extensions, err = parseTuples(g.Extensions) | ||
200 | if err != nil { | ||
201 | return nil, err | ||
202 | } | ||
203 | c.Reserved = g.Reserved | ||
204 | k, err := ParsePublicKey(g.SignatureKey) | ||
205 | if err != nil { | ||
206 | return nil, err | ||
207 | } | ||
208 | |||
209 | c.SignatureKey = k | ||
210 | c.Signature, rest, ok = parseSignatureBody(g.Signature) | ||
211 | if !ok || len(rest) > 0 { | ||
212 | return nil, errors.New("ssh: signature parse error") | ||
213 | } | ||
214 | |||
215 | return c, nil | ||
216 | } | ||
217 | |||
218 | type openSSHCertSigner struct { | ||
219 | pub *Certificate | ||
220 | signer Signer | ||
221 | } | ||
222 | |||
223 | // NewCertSigner returns a Signer that signs with the given Certificate, whose | ||
224 | // private key is held by signer. It returns an error if the public key in cert | ||
225 | // doesn't match the key used by signer. | ||
226 | func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) { | ||
227 | if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { | ||
228 | return nil, errors.New("ssh: signer and cert have different public key") | ||
229 | } | ||
230 | |||
231 | return &openSSHCertSigner{cert, signer}, nil | ||
232 | } | ||
233 | |||
234 | func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) { | ||
235 | return s.signer.Sign(rand, data) | ||
236 | } | ||
237 | |||
238 | func (s *openSSHCertSigner) PublicKey() PublicKey { | ||
239 | return s.pub | ||
240 | } | ||
241 | |||
242 | const sourceAddressCriticalOption = "source-address" | ||
243 | |||
244 | // CertChecker does the work of verifying a certificate. Its methods | ||
245 | // can be plugged into ClientConfig.HostKeyCallback and | ||
246 | // ServerConfig.PublicKeyCallback. For the CertChecker to work, | ||
247 | // minimally, the IsAuthority callback should be set. | ||
248 | type CertChecker struct { | ||
249 | // SupportedCriticalOptions lists the CriticalOptions that the | ||
250 | // server application layer understands. These are only used | ||
251 | // for user certificates. | ||
252 | SupportedCriticalOptions []string | ||
253 | |||
254 | // IsAuthority should return true if the key is recognized as | ||
255 | // an authority. This allows for certificates to be signed by other | ||
256 | // certificates. | ||
257 | IsAuthority func(auth PublicKey) bool | ||
258 | |||
259 | // Clock is used for verifying time stamps. If nil, time.Now | ||
260 | // is used. | ||
261 | Clock func() time.Time | ||
262 | |||
263 | // UserKeyFallback is called when CertChecker.Authenticate encounters a | ||
264 | // public key that is not a certificate. It must implement validation | ||
265 | // of user keys or else, if nil, all such keys are rejected. | ||
266 | UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) | ||
267 | |||
268 | // HostKeyFallback is called when CertChecker.CheckHostKey encounters a | ||
269 | // public key that is not a certificate. It must implement host key | ||
270 | // validation or else, if nil, all such keys are rejected. | ||
271 | HostKeyFallback func(addr string, remote net.Addr, key PublicKey) error | ||
272 | |||
273 | // IsRevoked is called for each certificate so that revocation checking | ||
274 | // can be implemented. It should return true if the given certificate | ||
275 | // is revoked and false otherwise. If nil, no certificates are | ||
276 | // considered to have been revoked. | ||
277 | IsRevoked func(cert *Certificate) bool | ||
278 | } | ||
279 | |||
280 | // CheckHostKey checks a host key certificate. This method can be | ||
281 | // plugged into ClientConfig.HostKeyCallback. | ||
282 | func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error { | ||
283 | cert, ok := key.(*Certificate) | ||
284 | if !ok { | ||
285 | if c.HostKeyFallback != nil { | ||
286 | return c.HostKeyFallback(addr, remote, key) | ||
287 | } | ||
288 | return errors.New("ssh: non-certificate host key") | ||
289 | } | ||
290 | if cert.CertType != HostCert { | ||
291 | return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) | ||
292 | } | ||
293 | |||
294 | return c.CheckCert(addr, cert) | ||
295 | } | ||
296 | |||
297 | // Authenticate checks a user certificate. Authenticate can be used as | ||
298 | // a value for ServerConfig.PublicKeyCallback. | ||
299 | func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) { | ||
300 | cert, ok := pubKey.(*Certificate) | ||
301 | if !ok { | ||
302 | if c.UserKeyFallback != nil { | ||
303 | return c.UserKeyFallback(conn, pubKey) | ||
304 | } | ||
305 | return nil, errors.New("ssh: normal key pairs not accepted") | ||
306 | } | ||
307 | |||
308 | if cert.CertType != UserCert { | ||
309 | return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) | ||
310 | } | ||
311 | |||
312 | if err := c.CheckCert(conn.User(), cert); err != nil { | ||
313 | return nil, err | ||
314 | } | ||
315 | |||
316 | return &cert.Permissions, nil | ||
317 | } | ||
318 | |||
319 | // CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and | ||
320 | // the signature of the certificate. | ||
321 | func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { | ||
322 | if c.IsRevoked != nil && c.IsRevoked(cert) { | ||
323 | return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial) | ||
324 | } | ||
325 | |||
326 | for opt, _ := range cert.CriticalOptions { | ||
327 | // sourceAddressCriticalOption will be enforced by | ||
328 | // serverAuthenticate | ||
329 | if opt == sourceAddressCriticalOption { | ||
330 | continue | ||
331 | } | ||
332 | |||
333 | found := false | ||
334 | for _, supp := range c.SupportedCriticalOptions { | ||
335 | if supp == opt { | ||
336 | found = true | ||
337 | break | ||
338 | } | ||
339 | } | ||
340 | if !found { | ||
341 | return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt) | ||
342 | } | ||
343 | } | ||
344 | |||
345 | if len(cert.ValidPrincipals) > 0 { | ||
346 | // By default, certs are valid for all users/hosts. | ||
347 | found := false | ||
348 | for _, p := range cert.ValidPrincipals { | ||
349 | if p == principal { | ||
350 | found = true | ||
351 | break | ||
352 | } | ||
353 | } | ||
354 | if !found { | ||
355 | return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals) | ||
356 | } | ||
357 | } | ||
358 | |||
359 | if !c.IsAuthority(cert.SignatureKey) { | ||
360 | return fmt.Errorf("ssh: certificate signed by unrecognized authority") | ||
361 | } | ||
362 | |||
363 | clock := c.Clock | ||
364 | if clock == nil { | ||
365 | clock = time.Now | ||
366 | } | ||
367 | |||
368 | unixNow := clock().Unix() | ||
369 | if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) { | ||
370 | return fmt.Errorf("ssh: cert is not yet valid") | ||
371 | } | ||
372 | if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) { | ||
373 | return fmt.Errorf("ssh: cert has expired") | ||
374 | } | ||
375 | if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil { | ||
376 | return fmt.Errorf("ssh: certificate signature does not verify") | ||
377 | } | ||
378 | |||
379 | return nil | ||
380 | } | ||
381 | |||
382 | // SignCert sets c.SignatureKey to the authority's public key and stores a | ||
383 | // Signature, by authority, in the certificate. | ||
384 | func (c *Certificate) SignCert(rand io.Reader, authority Signer) error { | ||
385 | c.Nonce = make([]byte, 32) | ||
386 | if _, err := io.ReadFull(rand, c.Nonce); err != nil { | ||
387 | return err | ||
388 | } | ||
389 | c.SignatureKey = authority.PublicKey() | ||
390 | |||
391 | sig, err := authority.Sign(rand, c.bytesForSigning()) | ||
392 | if err != nil { | ||
393 | return err | ||
394 | } | ||
395 | c.Signature = sig | ||
396 | return nil | ||
397 | } | ||
398 | |||
399 | var certAlgoNames = map[string]string{ | ||
400 | KeyAlgoRSA: CertAlgoRSAv01, | ||
401 | KeyAlgoDSA: CertAlgoDSAv01, | ||
402 | KeyAlgoECDSA256: CertAlgoECDSA256v01, | ||
403 | KeyAlgoECDSA384: CertAlgoECDSA384v01, | ||
404 | KeyAlgoECDSA521: CertAlgoECDSA521v01, | ||
405 | KeyAlgoED25519: CertAlgoED25519v01, | ||
406 | } | ||
407 | |||
408 | // certToPrivAlgo returns the underlying algorithm for a certificate algorithm. | ||
409 | // Panics if a non-certificate algorithm is passed. | ||
410 | func certToPrivAlgo(algo string) string { | ||
411 | for privAlgo, pubAlgo := range certAlgoNames { | ||
412 | if pubAlgo == algo { | ||
413 | return privAlgo | ||
414 | } | ||
415 | } | ||
416 | panic("unknown cert algorithm") | ||
417 | } | ||
418 | |||
419 | func (cert *Certificate) bytesForSigning() []byte { | ||
420 | c2 := *cert | ||
421 | c2.Signature = nil | ||
422 | out := c2.Marshal() | ||
423 | // Drop trailing signature length. | ||
424 | return out[:len(out)-4] | ||
425 | } | ||
426 | |||
427 | // Marshal serializes c into OpenSSH's wire format. It is part of the | ||
428 | // PublicKey interface. | ||
429 | func (c *Certificate) Marshal() []byte { | ||
430 | generic := genericCertData{ | ||
431 | Serial: c.Serial, | ||
432 | CertType: c.CertType, | ||
433 | KeyId: c.KeyId, | ||
434 | ValidPrincipals: marshalStringList(c.ValidPrincipals), | ||
435 | ValidAfter: uint64(c.ValidAfter), | ||
436 | ValidBefore: uint64(c.ValidBefore), | ||
437 | CriticalOptions: marshalTuples(c.CriticalOptions), | ||
438 | Extensions: marshalTuples(c.Extensions), | ||
439 | Reserved: c.Reserved, | ||
440 | SignatureKey: c.SignatureKey.Marshal(), | ||
441 | } | ||
442 | if c.Signature != nil { | ||
443 | generic.Signature = Marshal(c.Signature) | ||
444 | } | ||
445 | genericBytes := Marshal(&generic) | ||
446 | keyBytes := c.Key.Marshal() | ||
447 | _, keyBytes, _ = parseString(keyBytes) | ||
448 | prefix := Marshal(&struct { | ||
449 | Name string | ||
450 | Nonce []byte | ||
451 | Key []byte `ssh:"rest"` | ||
452 | }{c.Type(), c.Nonce, keyBytes}) | ||
453 | |||
454 | result := make([]byte, 0, len(prefix)+len(genericBytes)) | ||
455 | result = append(result, prefix...) | ||
456 | result = append(result, genericBytes...) | ||
457 | return result | ||
458 | } | ||
459 | |||
460 | // Type returns the key name. It is part of the PublicKey interface. | ||
461 | func (c *Certificate) Type() string { | ||
462 | algo, ok := certAlgoNames[c.Key.Type()] | ||
463 | if !ok { | ||
464 | panic("unknown cert key type " + c.Key.Type()) | ||
465 | } | ||
466 | return algo | ||
467 | } | ||
468 | |||
469 | // Verify verifies a signature against the certificate's public | ||
470 | // key. It is part of the PublicKey interface. | ||
471 | func (c *Certificate) Verify(data []byte, sig *Signature) error { | ||
472 | return c.Key.Verify(data, sig) | ||
473 | } | ||
474 | |||
475 | func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) { | ||
476 | format, in, ok := parseString(in) | ||
477 | if !ok { | ||
478 | return | ||
479 | } | ||
480 | |||
481 | out = &Signature{ | ||
482 | Format: string(format), | ||
483 | } | ||
484 | |||
485 | if out.Blob, in, ok = parseString(in); !ok { | ||
486 | return | ||
487 | } | ||
488 | |||
489 | return out, in, ok | ||
490 | } | ||
491 | |||
492 | func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) { | ||
493 | sigBytes, rest, ok := parseString(in) | ||
494 | if !ok { | ||
495 | return | ||
496 | } | ||
497 | |||
498 | out, trailing, ok := parseSignatureBody(sigBytes) | ||
499 | if !ok || len(trailing) > 0 { | ||
500 | return nil, nil, false | ||
501 | } | ||
502 | return | ||
503 | } | ||