Files
crypto/ssh/certs.go
T
Jonathan Pittman d95b28330d go.crypto/ssh: some cleanup
Simplify MarshalAuthorizedKey by using the algoName func.
Make the algoName func be very specific about supported key types in openssh certs.
Generalize some of the commentary that previously mentioned specific key types.

R=agl, dave
CC=golang-dev
https://golang.org/cl/6938067
2012-12-16 10:47:10 -05:00

313 lines
7.3 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
import (
"crypto/dsa"
"crypto/ecdsa"
"crypto/rsa"
"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
}
// An OpenSSHCertV01 represents an OpenSSH certificate as defined in
// [PROTOCOL.certkeys]?rev=1.8.
type OpenSSHCertV01 struct {
Nonce []byte
Key interface{} // rsa, dsa, or ecdsa *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 CertAlgoRSAv01:
var rsaPubKey *rsa.PublicKey
if rsaPubKey, in, ok = parseRSA(in); !ok {
return
}
cert.Key = rsaPubKey
case CertAlgoDSAv01:
var dsaPubKey *dsa.PublicKey
if dsaPubKey, in, ok = parseDSA(in); !ok {
return
}
cert.Key = dsaPubKey
case CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
var ecdsaPubKey *ecdsa.PublicKey
if ecdsaPubKey, in, ok = parseECDSA(in); !ok {
return
}
cert.Key = ecdsaPubKey
default:
ok = false
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)
case *ecdsa.PublicKey:
k := cert.Key.(*ecdsa.PublicKey)
pubKey = marshalPubECDSA(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
}