crypto/ssh/certs.go

305 строки
7.1 KiB
Go

// 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
// References
// [PROTOCOL.certkeys]: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys
import (
"crypto/dsa"
"crypto/rsa"
"time"
)
// String constants in [PROTOCOL.certkeys] for certificate algorithm names.
const (
hostAlgoRSACertV01 = "ssh-rsa-cert-v01@openssh.com"
hostAlgoDSACertV01 = "ssh-dss-cert-v01@openssh.com"
hostAlgoECDSA256CertV01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
hostAlgoECDSA384CertV01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
hostAlgoECDSA521CertV01 = "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
}
// An OpenSSHCertV01 represents an OpenSSH certificate as defined in
// [PROTOCOL.certkeys] rev 1.8. Supported formats include
// ssh-rsa-cert-v01@openssh.com and ssh-dss-cert-v01@openssh.com.
type OpenSSHCertV01 struct {
Nonce []byte
Key interface{} // rsa or dsa *PublicKey
Serial uint64
Type uint32
KeyId string
ValidPrincipals []string
ValidAfter, ValidBefore time.Time
CriticalOptions []tuple
Extensions []tuple
Reserved []byte
SignatureKey interface{} // rsa, dsa, or ecdsa *PublicKey
Signature *signature
}
func parseOpenSSHCertV01(in []byte, algo string) (out *OpenSSHCertV01, rest []byte, ok bool) {
cert := new(OpenSSHCertV01)
if cert.Nonce, in, ok = parseString(in); !ok {
return
}
switch algo {
case hostAlgoRSACertV01:
var rsaPubKey *rsa.PublicKey
if rsaPubKey, in, ok = parseRSA(in); !ok {
return
}
cert.Key = rsaPubKey
case hostAlgoDSACertV01:
var dsaPubKey *dsa.PublicKey
if dsaPubKey, in, ok = parseDSA(in); !ok {
return
}
cert.Key = dsaPubKey
default:
return
}
if cert.Serial, in, ok = parseUint64(in); !ok {
return
}
if cert.Type, in, ok = parseUint32(in); !ok || cert.Type != UserCert && cert.Type != HostCert {
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 = time.Unix(int64(va), 0)
vb, in, ok := parseUint64(in)
if !ok {
return
}
cert.ValidBefore = time.Unix(int64(vb), 0)
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 = parsePubKey(sigKey); !ok {
return
}
if cert.Signature, in, ok = parseSignature(in); !ok {
return
}
ok = true
return cert, in, ok
}
func marshalOpenSSHCertV01(cert *OpenSSHCertV01) []byte {
var pubKey []byte
switch cert.Key.(type) {
case *rsa.PublicKey:
k := cert.Key.(*rsa.PublicKey)
pubKey = marshalPubRSA(k)
case *dsa.PublicKey:
k := cert.Key.(*dsa.PublicKey)
pubKey = marshalPubDSA(k)
default:
panic("ssh: unknown public key type in cert")
}
sigKey := serializePublickey(cert.SignatureKey)
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))
length += signatureLength(cert.Signature)
ret := make([]byte, length)
r := marshalString(ret, 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.Unix()))
r = marshalUint64(r, uint64(cert.ValidBefore.Unix()))
r = marshalTupleList(r, cert.CriticalOptions)
r = marshalTupleList(r, cert.Extensions)
r = marshalString(r, cert.Reserved)
r = marshalString(r, sigKey)
r = marshalSignature(r, cert.Signature)
if len(r) > 0 {
panic("internal error")
}
return ret
}
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 parseSignature(in []byte) (out *signature, rest []byte, ok bool) {
var sigBytes, format []byte
sig := new(signature)
if sigBytes, rest, ok = parseString(in); !ok {
return
}
if format, sigBytes, ok = parseString(sigBytes); !ok {
return
}
sig.Format = string(format)
if sig.Blob, sigBytes, ok = parseString(sigBytes); !ok {
return
}
return sig, rest, ok
}