// 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 ( "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" ) // Certificate types are used to specify whether a certificate is for identification // of a user or a host. Current identities are defined in [PROTOCOL.certkeys]. const ( UserCert = 1 HostCert = 2 ) type signature struct { Format string Blob []byte } type tuple struct { Name string Data string } const ( maxUint64 = 1<<64 - 1 maxInt64 = 1<<63 - 1 ) // CertTime represents an unsigned 64-bit time value in seconds starting from // UNIX epoch. We use CertTime instead of time.Time in order to properly handle // the "infinite" time value ^0, which would become negative when expressed as // an int64. type CertTime uint64 func (ct CertTime) Time() time.Time { if ct > maxInt64 { return time.Unix(maxInt64, 0) } return time.Unix(int64(ct), 0) } func (ct CertTime) IsInfinite() bool { return ct == maxUint64 } // An OpenSSHCertV01 represents an OpenSSH certificate as defined in // [PROTOCOL.certkeys]?rev=1.8. type OpenSSHCertV01 struct { Nonce []byte Key PublicKey Serial uint64 Type uint32 KeyId string ValidPrincipals []string ValidAfter, ValidBefore CertTime CriticalOptions []tuple Extensions []tuple Reserved []byte SignatureKey PublicKey Signature *signature } // validateOpenSSHCertV01Signature uses the cert's SignatureKey to verify that // the cert's Signature.Blob is the result of signing the cert bytes starting // from the algorithm string and going up to and including the SignatureKey. func validateOpenSSHCertV01Signature(cert *OpenSSHCertV01) bool { return cert.SignatureKey.Verify(cert.BytesForSigning(), cert.Signature.Blob) } var certAlgoNames = map[string]string{ KeyAlgoRSA: CertAlgoRSAv01, KeyAlgoDSA: CertAlgoDSAv01, KeyAlgoECDSA256: CertAlgoECDSA256v01, KeyAlgoECDSA384: CertAlgoECDSA384v01, KeyAlgoECDSA521: CertAlgoECDSA521v01, } // 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 *OpenSSHCertV01) marshal(includeAlgo, includeSig bool) []byte { algoName := cert.PublicKeyAlgo() pubKey := cert.Key.Marshal() sigKey := MarshalPublicKey(cert.SignatureKey) var length int if includeAlgo { length += stringLength(len(algoName)) } length += stringLength(len(cert.Nonce)) length += len(pubKey) length += 8 // Length of Serial length += 4 // Length of Type length += stringLength(len(cert.KeyId)) length += lengthPrefixedNameListLength(cert.ValidPrincipals) length += 8 // Length of ValidAfter length += 8 // Length of ValidBefore length += tupleListLength(cert.CriticalOptions) length += tupleListLength(cert.Extensions) length += stringLength(len(cert.Reserved)) length += stringLength(len(sigKey)) if includeSig { length += signatureLength(cert.Signature) } ret := make([]byte, length) r := ret if includeAlgo { r = marshalString(r, []byte(algoName)) } r = marshalString(r, cert.Nonce) copy(r, pubKey) r = r[len(pubKey):] r = marshalUint64(r, cert.Serial) r = marshalUint32(r, cert.Type) r = marshalString(r, []byte(cert.KeyId)) r = marshalLengthPrefixedNameList(r, cert.ValidPrincipals) r = marshalUint64(r, uint64(cert.ValidAfter)) r = marshalUint64(r, uint64(cert.ValidBefore)) r = marshalTupleList(r, cert.CriticalOptions) r = marshalTupleList(r, cert.Extensions) r = marshalString(r, cert.Reserved) r = marshalString(r, sigKey) if includeSig { r = marshalSignature(r, cert.Signature) } if len(r) > 0 { panic("ssh: internal error, marshaling certificate did not fill the entire buffer") } return ret } func (cert *OpenSSHCertV01) BytesForSigning() []byte { return cert.marshal(true, false) } func (cert *OpenSSHCertV01) Marshal() []byte { return cert.marshal(false, true) } func (c *OpenSSHCertV01) PublicKeyAlgo() string { algo, ok := certAlgoNames[c.Key.PublicKeyAlgo()] if !ok { panic("unknown cert key type") } return algo } func (c *OpenSSHCertV01) PrivateKeyAlgo() string { return c.Key.PrivateKeyAlgo() } func (c *OpenSSHCertV01) Verify(data []byte, sig []byte) bool { return c.Key.Verify(data, sig) } func parseOpenSSHCertV01(in []byte, algo string) (out *OpenSSHCertV01, rest []byte, ok bool) { cert := new(OpenSSHCertV01) if cert.Nonce, in, ok = parseString(in); !ok { return } privAlgo := certToPrivAlgo(algo) cert.Key, in, ok = parsePubKey(in, privAlgo) if !ok { return } // We test PublicKeyAlgo to make sure we don't use some weird sub-cert. if cert.Key.PublicKeyAlgo() != privAlgo { ok = false return } if cert.Serial, in, ok = parseUint64(in); !ok { return } if cert.Type, in, ok = parseUint32(in); !ok { return } keyId, in, ok := parseString(in) if !ok { return } cert.KeyId = string(keyId) if cert.ValidPrincipals, in, ok = parseLengthPrefixedNameList(in); !ok { return } va, in, ok := parseUint64(in) if !ok { return } cert.ValidAfter = CertTime(va) vb, in, ok := parseUint64(in) if !ok { return } cert.ValidBefore = CertTime(vb) if cert.CriticalOptions, in, ok = parseTupleList(in); !ok { return } if cert.Extensions, in, ok = parseTupleList(in); !ok { return } if cert.Reserved, in, ok = parseString(in); !ok { return } sigKey, in, ok := parseString(in) if !ok { return } if cert.SignatureKey, _, ok = ParsePublicKey(sigKey); !ok { return } if cert.Signature, in, ok = parseSignature(in); !ok { return } ok = true return cert, in, ok } func lengthPrefixedNameListLength(namelist []string) int { length := 4 // length prefix for list for _, name := range namelist { length += 4 // length prefix for name length += len(name) } return length } func marshalLengthPrefixedNameList(to []byte, namelist []string) []byte { length := uint32(lengthPrefixedNameListLength(namelist) - 4) to = marshalUint32(to, length) for _, name := range namelist { to = marshalString(to, []byte(name)) } return to } func parseLengthPrefixedNameList(in []byte) (out []string, rest []byte, ok bool) { list, rest, ok := parseString(in) if !ok { return } for len(list) > 0 { var next []byte if next, list, ok = parseString(list); !ok { return nil, nil, false } out = append(out, string(next)) } ok = true return } func tupleListLength(tupleList []tuple) int { length := 4 // length prefix for list for _, t := range tupleList { length += 4 // length prefix for t.Name length += len(t.Name) length += 4 // length prefix for t.Data length += len(t.Data) } return length } func marshalTupleList(to []byte, tuplelist []tuple) []byte { length := uint32(tupleListLength(tuplelist) - 4) to = marshalUint32(to, length) for _, t := range tuplelist { to = marshalString(to, []byte(t.Name)) to = marshalString(to, []byte(t.Data)) } return to } func parseTupleList(in []byte) (out []tuple, rest []byte, ok bool) { list, rest, ok := parseString(in) if !ok { return } for len(list) > 0 { var name, data []byte var ok bool name, list, ok = parseString(list) if !ok { return nil, nil, false } data, list, ok = parseString(list) if !ok { return nil, nil, false } out = append(out, tuple{string(name), string(data)}) } ok = true return } func signatureLength(sig *signature) int { length := 4 // length prefix for signature length += stringLength(len(sig.Format)) length += stringLength(len(sig.Blob)) return length } func marshalSignature(to []byte, sig *signature) []byte { length := uint32(signatureLength(sig) - 4) to = marshalUint32(to, length) to = marshalString(to, []byte(sig.Format)) to = marshalString(to, sig.Blob) return to } func parseSignatureBody(in []byte) (out *signature, rest []byte, ok bool) { var format []byte if format, in, ok = parseString(in); !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) { var sigBytes []byte if sigBytes, rest, ok = parseString(in); !ok { return } out, sigBytes, ok = parseSignatureBody(sigBytes) if !ok || len(sigBytes) > 0 { return nil, nil, false } return }