OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / golang.org / x / crypto / ssh / certs.go
diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go
new file mode 100644 (file)
index 0000000..b1f0220
--- /dev/null
@@ -0,0 +1,519 @@
+// 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"
+       "errors"
+       "fmt"
+       "io"
+       "net"
+       "sort"
+       "time"
+)
+
+// These constants from [PROTOCOL.certkeys] represent the algorithm names
+// for certificate types supported by this package.
+const (
+       CertAlgoRSAv01      = "ssh-rsa-cert-v01@openssh.com"
+       CertAlgoDSAv01      = "ssh-dss-cert-v01@openssh.com"
+       CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
+       CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
+       CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
+       CertAlgoED25519v01  = "ssh-ed25519-cert-v01@openssh.com"
+)
+
+// Certificate types distinguish between host and user
+// certificates. The values can be set in the CertType field of
+// Certificate.
+const (
+       UserCert = 1
+       HostCert = 2
+)
+
+// Signature represents a cryptographic signature.
+type Signature struct {
+       Format string
+       Blob   []byte
+}
+
+// CertTimeInfinity can be used for OpenSSHCertV01.ValidBefore to indicate that
+// a certificate does not expire.
+const CertTimeInfinity = 1<<64 - 1
+
+// An Certificate represents an OpenSSH certificate as defined in
+// [PROTOCOL.certkeys]?rev=1.8.
+type Certificate struct {
+       Nonce           []byte
+       Key             PublicKey
+       Serial          uint64
+       CertType        uint32
+       KeyId           string
+       ValidPrincipals []string
+       ValidAfter      uint64
+       ValidBefore     uint64
+       Permissions
+       Reserved     []byte
+       SignatureKey PublicKey
+       Signature    *Signature
+}
+
+// genericCertData holds the key-independent part of the certificate data.
+// Overall, certificates contain an nonce, public key fields and
+// key-independent fields.
+type genericCertData struct {
+       Serial          uint64
+       CertType        uint32
+       KeyId           string
+       ValidPrincipals []byte
+       ValidAfter      uint64
+       ValidBefore     uint64
+       CriticalOptions []byte
+       Extensions      []byte
+       Reserved        []byte
+       SignatureKey    []byte
+       Signature       []byte
+}
+
+func marshalStringList(namelist []string) []byte {
+       var to []byte
+       for _, name := range namelist {
+               s := struct{ N string }{name}
+               to = append(to, Marshal(&s)...)
+       }
+       return to
+}
+
+type optionsTuple struct {
+       Key   string
+       Value []byte
+}
+
+type optionsTupleValue struct {
+       Value string
+}
+
+// serialize a map of critical options or extensions
+// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation,
+// we need two length prefixes for a non-empty string value
+func marshalTuples(tups map[string]string) []byte {
+       keys := make([]string, 0, len(tups))
+       for key := range tups {
+               keys = append(keys, key)
+       }
+       sort.Strings(keys)
+
+       var ret []byte
+       for _, key := range keys {
+               s := optionsTuple{Key: key}
+               if value := tups[key]; len(value) > 0 {
+                       s.Value = Marshal(&optionsTupleValue{value})
+               }
+               ret = append(ret, Marshal(&s)...)
+       }
+       return ret
+}
+
+// issue #10569 - per [PROTOCOL.certkeys] and SSH implementation,
+// we need two length prefixes for a non-empty option value
+func parseTuples(in []byte) (map[string]string, error) {
+       tups := map[string]string{}
+       var lastKey string
+       var haveLastKey bool
+
+       for len(in) > 0 {
+               var key, val, extra []byte
+               var ok bool
+
+               if key, in, ok = parseString(in); !ok {
+                       return nil, errShortRead
+               }
+               keyStr := string(key)
+               // according to [PROTOCOL.certkeys], the names must be in
+               // lexical order.
+               if haveLastKey && keyStr <= lastKey {
+                       return nil, fmt.Errorf("ssh: certificate options are not in lexical order")
+               }
+               lastKey, haveLastKey = keyStr, true
+               // the next field is a data field, which if non-empty has a string embedded
+               if val, in, ok = parseString(in); !ok {
+                       return nil, errShortRead
+               }
+               if len(val) > 0 {
+                       val, extra, ok = parseString(val)
+                       if !ok {
+                               return nil, errShortRead
+                       }
+                       if len(extra) > 0 {
+                               return nil, fmt.Errorf("ssh: unexpected trailing data after certificate option value")
+                       }
+                       tups[keyStr] = string(val)
+               } else {
+                       tups[keyStr] = ""
+               }
+       }
+       return tups, nil
+}
+
+func parseCert(in []byte, privAlgo string) (*Certificate, error) {
+       nonce, rest, ok := parseString(in)
+       if !ok {
+               return nil, errShortRead
+       }
+
+       key, rest, err := parsePubKey(rest, privAlgo)
+       if err != nil {
+               return nil, err
+       }
+
+       var g genericCertData
+       if err := Unmarshal(rest, &g); err != nil {
+               return nil, err
+       }
+
+       c := &Certificate{
+               Nonce:       nonce,
+               Key:         key,
+               Serial:      g.Serial,
+               CertType:    g.CertType,
+               KeyId:       g.KeyId,
+               ValidAfter:  g.ValidAfter,
+               ValidBefore: g.ValidBefore,
+       }
+
+       for principals := g.ValidPrincipals; len(principals) > 0; {
+               principal, rest, ok := parseString(principals)
+               if !ok {
+                       return nil, errShortRead
+               }
+               c.ValidPrincipals = append(c.ValidPrincipals, string(principal))
+               principals = rest
+       }
+
+       c.CriticalOptions, err = parseTuples(g.CriticalOptions)
+       if err != nil {
+               return nil, err
+       }
+       c.Extensions, err = parseTuples(g.Extensions)
+       if err != nil {
+               return nil, err
+       }
+       c.Reserved = g.Reserved
+       k, err := ParsePublicKey(g.SignatureKey)
+       if err != nil {
+               return nil, err
+       }
+
+       c.SignatureKey = k
+       c.Signature, rest, ok = parseSignatureBody(g.Signature)
+       if !ok || len(rest) > 0 {
+               return nil, errors.New("ssh: signature parse error")
+       }
+
+       return c, nil
+}
+
+type openSSHCertSigner struct {
+       pub    *Certificate
+       signer Signer
+}
+
+// NewCertSigner returns a Signer that signs with the given Certificate, whose
+// private key is held by signer. It returns an error if the public key in cert
+// doesn't match the key used by signer.
+func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) {
+       if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
+               return nil, errors.New("ssh: signer and cert have different public key")
+       }
+
+       return &openSSHCertSigner{cert, signer}, nil
+}
+
+func (s *openSSHCertSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
+       return s.signer.Sign(rand, data)
+}
+
+func (s *openSSHCertSigner) PublicKey() PublicKey {
+       return s.pub
+}
+
+const sourceAddressCriticalOption = "source-address"
+
+// CertChecker does the work of verifying a certificate. Its methods
+// can be plugged into ClientConfig.HostKeyCallback and
+// ServerConfig.PublicKeyCallback. For the CertChecker to work,
+// minimally, the IsAuthority callback should be set.
+type CertChecker struct {
+       // SupportedCriticalOptions lists the CriticalOptions that the
+       // server application layer understands. These are only used
+       // for user certificates.
+       SupportedCriticalOptions []string
+
+       // IsUserAuthority should return true if the key is recognized as an
+       // authority for the given user certificate. This allows for
+       // certificates to be signed by other certificates. This must be set
+       // if this CertChecker will be checking user certificates.
+       IsUserAuthority func(auth PublicKey) bool
+
+       // IsHostAuthority should report whether the key is recognized as
+       // an authority for this host. This allows for certificates to be
+       // signed by other keys, and for those other keys to only be valid
+       // signers for particular hostnames. This must be set if this
+       // CertChecker will be checking host certificates.
+       IsHostAuthority func(auth PublicKey, address string) bool
+
+       // Clock is used for verifying time stamps. If nil, time.Now
+       // is used.
+       Clock func() time.Time
+
+       // UserKeyFallback is called when CertChecker.Authenticate encounters a
+       // public key that is not a certificate. It must implement validation
+       // of user keys or else, if nil, all such keys are rejected.
+       UserKeyFallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
+
+       // HostKeyFallback is called when CertChecker.CheckHostKey encounters a
+       // public key that is not a certificate. It must implement host key
+       // validation or else, if nil, all such keys are rejected.
+       HostKeyFallback HostKeyCallback
+
+       // IsRevoked is called for each certificate so that revocation checking
+       // can be implemented. It should return true if the given certificate
+       // is revoked and false otherwise. If nil, no certificates are
+       // considered to have been revoked.
+       IsRevoked func(cert *Certificate) bool
+}
+
+// CheckHostKey checks a host key certificate. This method can be
+// plugged into ClientConfig.HostKeyCallback.
+func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) error {
+       cert, ok := key.(*Certificate)
+       if !ok {
+               if c.HostKeyFallback != nil {
+                       return c.HostKeyFallback(addr, remote, key)
+               }
+               return errors.New("ssh: non-certificate host key")
+       }
+       if cert.CertType != HostCert {
+               return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType)
+       }
+       if !c.IsHostAuthority(cert.SignatureKey, addr) {
+               return fmt.Errorf("ssh: no authorities for hostname: %v", addr)
+       }
+
+       hostname, _, err := net.SplitHostPort(addr)
+       if err != nil {
+               return err
+       }
+
+       // Pass hostname only as principal for host certificates (consistent with OpenSSH)
+       return c.CheckCert(hostname, cert)
+}
+
+// Authenticate checks a user certificate. Authenticate can be used as
+// a value for ServerConfig.PublicKeyCallback.
+func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permissions, error) {
+       cert, ok := pubKey.(*Certificate)
+       if !ok {
+               if c.UserKeyFallback != nil {
+                       return c.UserKeyFallback(conn, pubKey)
+               }
+               return nil, errors.New("ssh: normal key pairs not accepted")
+       }
+
+       if cert.CertType != UserCert {
+               return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType)
+       }
+       if !c.IsUserAuthority(cert.SignatureKey) {
+               return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority")
+       }
+
+       if err := c.CheckCert(conn.User(), cert); err != nil {
+               return nil, err
+       }
+
+       return &cert.Permissions, nil
+}
+
+// CheckCert checks CriticalOptions, ValidPrincipals, revocation, timestamp and
+// the signature of the certificate.
+func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
+       if c.IsRevoked != nil && c.IsRevoked(cert) {
+               return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial)
+       }
+
+       for opt, _ := range cert.CriticalOptions {
+               // sourceAddressCriticalOption will be enforced by
+               // serverAuthenticate
+               if opt == sourceAddressCriticalOption {
+                       continue
+               }
+
+               found := false
+               for _, supp := range c.SupportedCriticalOptions {
+                       if supp == opt {
+                               found = true
+                               break
+                       }
+               }
+               if !found {
+                       return fmt.Errorf("ssh: unsupported critical option %q in certificate", opt)
+               }
+       }
+
+       if len(cert.ValidPrincipals) > 0 {
+               // By default, certs are valid for all users/hosts.
+               found := false
+               for _, p := range cert.ValidPrincipals {
+                       if p == principal {
+                               found = true
+                               break
+                       }
+               }
+               if !found {
+                       return fmt.Errorf("ssh: principal %q not in the set of valid principals for given certificate: %q", principal, cert.ValidPrincipals)
+               }
+       }
+
+       clock := c.Clock
+       if clock == nil {
+               clock = time.Now
+       }
+
+       unixNow := clock().Unix()
+       if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) {
+               return fmt.Errorf("ssh: cert is not yet valid")
+       }
+       if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) {
+               return fmt.Errorf("ssh: cert has expired")
+       }
+       if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil {
+               return fmt.Errorf("ssh: certificate signature does not verify")
+       }
+
+       return nil
+}
+
+// SignCert sets c.SignatureKey to the authority's public key and stores a
+// Signature, by authority, in the certificate.
+func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
+       c.Nonce = make([]byte, 32)
+       if _, err := io.ReadFull(rand, c.Nonce); err != nil {
+               return err
+       }
+       c.SignatureKey = authority.PublicKey()
+
+       sig, err := authority.Sign(rand, c.bytesForSigning())
+       if err != nil {
+               return err
+       }
+       c.Signature = sig
+       return nil
+}
+
+var certAlgoNames = map[string]string{
+       KeyAlgoRSA:      CertAlgoRSAv01,
+       KeyAlgoDSA:      CertAlgoDSAv01,
+       KeyAlgoECDSA256: CertAlgoECDSA256v01,
+       KeyAlgoECDSA384: CertAlgoECDSA384v01,
+       KeyAlgoECDSA521: CertAlgoECDSA521v01,
+       KeyAlgoED25519:  CertAlgoED25519v01,
+}
+
+// certToPrivAlgo returns the underlying algorithm for a certificate algorithm.
+// Panics if a non-certificate algorithm is passed.
+func certToPrivAlgo(algo string) string {
+       for privAlgo, pubAlgo := range certAlgoNames {
+               if pubAlgo == algo {
+                       return privAlgo
+               }
+       }
+       panic("unknown cert algorithm")
+}
+
+func (cert *Certificate) bytesForSigning() []byte {
+       c2 := *cert
+       c2.Signature = nil
+       out := c2.Marshal()
+       // Drop trailing signature length.
+       return out[:len(out)-4]
+}
+
+// Marshal serializes c into OpenSSH's wire format. It is part of the
+// PublicKey interface.
+func (c *Certificate) Marshal() []byte {
+       generic := genericCertData{
+               Serial:          c.Serial,
+               CertType:        c.CertType,
+               KeyId:           c.KeyId,
+               ValidPrincipals: marshalStringList(c.ValidPrincipals),
+               ValidAfter:      uint64(c.ValidAfter),
+               ValidBefore:     uint64(c.ValidBefore),
+               CriticalOptions: marshalTuples(c.CriticalOptions),
+               Extensions:      marshalTuples(c.Extensions),
+               Reserved:        c.Reserved,
+               SignatureKey:    c.SignatureKey.Marshal(),
+       }
+       if c.Signature != nil {
+               generic.Signature = Marshal(c.Signature)
+       }
+       genericBytes := Marshal(&generic)
+       keyBytes := c.Key.Marshal()
+       _, keyBytes, _ = parseString(keyBytes)
+       prefix := Marshal(&struct {
+               Name  string
+               Nonce []byte
+               Key   []byte `ssh:"rest"`
+       }{c.Type(), c.Nonce, keyBytes})
+
+       result := make([]byte, 0, len(prefix)+len(genericBytes))
+       result = append(result, prefix...)
+       result = append(result, genericBytes...)
+       return result
+}
+
+// Type returns the key name. It is part of the PublicKey interface.
+func (c *Certificate) Type() string {
+       algo, ok := certAlgoNames[c.Key.Type()]
+       if !ok {
+               panic("unknown cert key type " + c.Key.Type())
+       }
+       return algo
+}
+
+// Verify verifies a signature against the certificate's public
+// key. It is part of the PublicKey interface.
+func (c *Certificate) Verify(data []byte, sig *Signature) error {
+       return c.Key.Verify(data, sig)
+}
+
+func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) {
+       format, in, ok := parseString(in)
+       if !ok {
+               return
+       }
+
+       out = &Signature{
+               Format: string(format),
+       }
+
+       if out.Blob, in, ok = parseString(in); !ok {
+               return
+       }
+
+       return out, in, ok
+}
+
+func parseSignature(in []byte) (out *Signature, rest []byte, ok bool) {
+       sigBytes, rest, ok := parseString(in)
+       if !ok {
+               return
+       }
+
+       out, trailing, ok := parseSignatureBody(sigBytes)
+       if !ok || len(trailing) > 0 {
+               return nil, nil, false
+       }
+       return
+}