parseCert parsed a certificate's SignatureKey by calling ParsePublicKey again, and only afterwards rejected a certificate used as a signing key. For a chain of nested certificates this recursed once per level, so a key with enough nesting exhausted the goroutine stack and crashed with a fatal stack overflow that recover cannot catch. Reject the signature key by its declared algorithm before parsing it, which bounds parsing. Fixes golang/go#80414 Change-Id: Id51401aae15bfbec006c03ab97635a3b68bd6058 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/802145 Auto-Submit: Nicola Murino <nicola.murino@gmail.com> Reviewed-by: Carlos Amedee <carlos@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org>
562 lines
17 KiB
Go
562 lines
17 KiB
Go
// Copyright 2013 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"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"net"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/ssh/testdata"
|
|
)
|
|
|
|
func TestParseCert(t *testing.T) {
|
|
authKeyBytes := bytes.TrimSuffix(testdata.SSHCertificates["rsa"], []byte(" host.example.com\n"))
|
|
|
|
key, _, _, rest, err := ParseAuthorizedKey(authKeyBytes)
|
|
if err != nil {
|
|
t.Fatalf("ParseAuthorizedKey: %v", err)
|
|
}
|
|
if len(rest) > 0 {
|
|
t.Errorf("rest: got %q, want empty", rest)
|
|
}
|
|
|
|
if _, ok := key.(*Certificate); !ok {
|
|
t.Fatalf("got %v (%T), want *Certificate", key, key)
|
|
}
|
|
|
|
marshaled := MarshalAuthorizedKey(key)
|
|
// Before comparison, remove the trailing newline that
|
|
// MarshalAuthorizedKey adds.
|
|
marshaled = marshaled[:len(marshaled)-1]
|
|
if !bytes.Equal(authKeyBytes, marshaled) {
|
|
t.Errorf("marshaled certificate does not match original: got %q, want %q", marshaled, authKeyBytes)
|
|
}
|
|
}
|
|
|
|
func TestParseCertNestedSignatureKey(t *testing.T) {
|
|
signer, err := NewSignerFromKey(testPrivateKeys["ed25519"])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cert := &Certificate{
|
|
Key: signer.PublicKey(),
|
|
CertType: UserCert,
|
|
ValidBefore: CertTimeInfinity,
|
|
}
|
|
if err := cert.SignCert(rand.Reader, signer); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
inner := *cert
|
|
cert.SignatureKey = &inner
|
|
blob := cert.Marshal()
|
|
|
|
_, err = ParsePublicKey(blob)
|
|
if err == nil {
|
|
t.Fatal("ParsePublicKey: expected error for certificate signed by a certificate, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), "invalid for certificates") {
|
|
t.Errorf("ParsePublicKey: got error %q, want it to mention the signature key is invalid for certificates", err)
|
|
}
|
|
}
|
|
|
|
// Cert generated by ssh-keygen OpenSSH_6.8p1 OS X 10.10.3
|
|
// % ssh-keygen -s ca -I testcert -O source-address=192.168.1.0/24 -O force-command=/bin/sleep user.pub
|
|
// user.pub key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDACh1rt2DXfV3hk6fszSQcQ/rueMId0kVD9U7nl8cfEnFxqOCrNT92g4laQIGl2mn8lsGZfTLg8ksHq3gkvgO3oo/0wHy4v32JeBOHTsN5AL4gfHNEhWeWb50ev47hnTsRIt9P4dxogeUo/hTu7j9+s9lLpEQXCvq6xocXQt0j8MV9qZBBXFLXVT3cWIkSqOdwt/5ZBg+1GSrc7WfCXVWgTk4a20uPMuJPxU4RQwZW6X3+O8Pqo8C3cW0OzZRFP6gUYUKUsTI5WntlS+LAxgw1mZNsozFGdbiOPRnEryE3SRldh9vjDR3tin1fGpA5P7+CEB/bqaXtG3V+F2OkqaMN
|
|
// Critical Options:
|
|
//
|
|
// force-command /bin/sleep
|
|
// source-address 192.168.1.0/24
|
|
//
|
|
// Extensions:
|
|
//
|
|
// permit-X11-forwarding
|
|
// permit-agent-forwarding
|
|
// permit-port-forwarding
|
|
// permit-pty
|
|
// permit-user-rc
|
|
const exampleSSHCertWithOptions = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgDyysCJY0XrO1n03EeRRoITnTPdjENFmWDs9X58PP3VUAAAADAQABAAABAQDACh1rt2DXfV3hk6fszSQcQ/rueMId0kVD9U7nl8cfEnFxqOCrNT92g4laQIGl2mn8lsGZfTLg8ksHq3gkvgO3oo/0wHy4v32JeBOHTsN5AL4gfHNEhWeWb50ev47hnTsRIt9P4dxogeUo/hTu7j9+s9lLpEQXCvq6xocXQt0j8MV9qZBBXFLXVT3cWIkSqOdwt/5ZBg+1GSrc7WfCXVWgTk4a20uPMuJPxU4RQwZW6X3+O8Pqo8C3cW0OzZRFP6gUYUKUsTI5WntlS+LAxgw1mZNsozFGdbiOPRnEryE3SRldh9vjDR3tin1fGpA5P7+CEB/bqaXtG3V+F2OkqaMNAAAAAAAAAAAAAAABAAAACHRlc3RjZXJ0AAAAAAAAAAAAAAAA//////////8AAABLAAAADWZvcmNlLWNvbW1hbmQAAAAOAAAACi9iaW4vc2xlZXAAAAAOc291cmNlLWFkZHJlc3MAAAASAAAADjE5Mi4xNjguMS4wLzI0AAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAwU+c5ui5A8+J/CFpjW8wCa52bEODA808WWQDCSuTG/eMXNf59v9Y8Pk0F1E9dGCosSNyVcB/hacUrc6He+i97+HJCyKavBsE6GDxrjRyxYqAlfcOXi/IVmaUGiO8OQ39d4GHrjToInKvExSUeleQyH4Y4/e27T/pILAqPFL3fyrvMLT5qU9QyIt6zIpa7GBP5+urouNavMprV3zsfIqNBbWypinOQAw823a5wN+zwXnhZrgQiHZ/USG09Y6k98y1dTVz8YHlQVR4D3lpTAsKDKJ5hCH9WU4fdf+lU8OyNGaJ/vz0XNqxcToe1l4numLTnaoSuH89pHryjqurB7lJKwAAAQ8AAAAHc3NoLXJzYQAAAQCaHvUIoPL1zWUHIXLvu96/HU1s/i4CAW2IIEuGgxCUCiFj6vyTyYtgxQxcmbfZf6eaITlS6XJZa7Qq4iaFZh75C1DXTX8labXhRSD4E2t//AIP9MC1rtQC5xo6FmbQ+BoKcDskr+mNACcbRSxs3IL3bwCfWDnIw2WbVox9ZdcthJKk4UoCW4ix4QwdHw7zlddlz++fGEEVhmTbll1SUkycGApPFBsAYRTMupUJcYPIeReBI/m8XfkoMk99bV8ZJQTAd7OekHY2/48Ff53jLmyDjP7kNw1F8OaPtkFs6dGJXta4krmaekPy87j+35In5hFj7yoOqvSbmYUkeX70/GGQ`
|
|
|
|
func TestParseCertWithOptions(t *testing.T) {
|
|
opts := map[string]string{
|
|
"source-address": "192.168.1.0/24",
|
|
"force-command": "/bin/sleep",
|
|
}
|
|
exts := map[string]string{
|
|
"permit-X11-forwarding": "",
|
|
"permit-agent-forwarding": "",
|
|
"permit-port-forwarding": "",
|
|
"permit-pty": "",
|
|
"permit-user-rc": "",
|
|
}
|
|
authKeyBytes := []byte(exampleSSHCertWithOptions)
|
|
|
|
key, _, _, rest, err := ParseAuthorizedKey(authKeyBytes)
|
|
if err != nil {
|
|
t.Fatalf("ParseAuthorizedKey: %v", err)
|
|
}
|
|
if len(rest) > 0 {
|
|
t.Errorf("rest: got %q, want empty", rest)
|
|
}
|
|
cert, ok := key.(*Certificate)
|
|
if !ok {
|
|
t.Fatalf("got %v (%T), want *Certificate", key, key)
|
|
}
|
|
if !reflect.DeepEqual(cert.CriticalOptions, opts) {
|
|
t.Errorf("unexpected critical options - got %v, want %v", cert.CriticalOptions, opts)
|
|
}
|
|
if !reflect.DeepEqual(cert.Extensions, exts) {
|
|
t.Errorf("unexpected Extensions - got %v, want %v", cert.Extensions, exts)
|
|
}
|
|
marshaled := MarshalAuthorizedKey(key)
|
|
// Before comparison, remove the trailing newline that
|
|
// MarshalAuthorizedKey adds.
|
|
marshaled = marshaled[:len(marshaled)-1]
|
|
if !bytes.Equal(authKeyBytes, marshaled) {
|
|
t.Errorf("marshaled certificate does not match original: got %q, want %q", marshaled, authKeyBytes)
|
|
}
|
|
}
|
|
|
|
func TestValidateCert(t *testing.T) {
|
|
key, _, _, _, err := ParseAuthorizedKey(testdata.SSHCertificates["rsa-user-testcertificate"])
|
|
if err != nil {
|
|
t.Fatalf("ParseAuthorizedKey: %v", err)
|
|
}
|
|
validCert, ok := key.(*Certificate)
|
|
if !ok {
|
|
t.Fatalf("got %v (%T), want *Certificate", key, key)
|
|
}
|
|
checker := CertChecker{}
|
|
checker.IsUserAuthority = func(k PublicKey) bool {
|
|
return bytes.Equal(k.Marshal(), validCert.SignatureKey.Marshal())
|
|
}
|
|
|
|
if err := checker.CheckCert("testcertificate", validCert); err != nil {
|
|
t.Errorf("Unable to validate certificate: %v", err)
|
|
}
|
|
invalidCert := &Certificate{
|
|
Key: testPublicKeys["rsa"],
|
|
SignatureKey: testPublicKeys["ecdsa"],
|
|
ValidBefore: CertTimeInfinity,
|
|
Signature: &Signature{},
|
|
}
|
|
if err := checker.CheckCert("testcertificate", invalidCert); err == nil {
|
|
t.Error("Invalid cert signature passed validation")
|
|
}
|
|
}
|
|
|
|
// signSKCert builds an SK-ECDSA signature over cert.bytesForSigning()
|
|
// using caKey as the simulated hardware token. The SK signing flags
|
|
// byte (UP bit and others) is caller-controlled so tests can exercise
|
|
// the user-presence enforcement paths. caApp is the SK application
|
|
// string (typically "ssh:").
|
|
func signSKCert(t *testing.T, cert *Certificate, caKey *ecdsa.PrivateKey, caApp string, flags byte) {
|
|
t.Helper()
|
|
cert.SignatureKey = &skECDSAPublicKey{
|
|
application: caApp,
|
|
PublicKey: caKey.PublicKey,
|
|
}
|
|
cert.Nonce = make([]byte, 32)
|
|
if _, err := rand.Read(cert.Nonce); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
h := sha256.New()
|
|
h.Write([]byte(caApp))
|
|
appDigest := h.Sum(nil)
|
|
h.Reset()
|
|
h.Write(cert.bytesForSigning())
|
|
dataDigest := h.Sum(nil)
|
|
|
|
var counter uint32 = 1
|
|
blob := struct {
|
|
ApplicationDigest []byte `ssh:"rest"`
|
|
Flags byte
|
|
Counter uint32
|
|
MessageDigest []byte `ssh:"rest"`
|
|
}{appDigest, flags, counter, dataDigest}
|
|
h.Reset()
|
|
h.Write(Marshal(blob))
|
|
digest := h.Sum(nil)
|
|
|
|
r, s, err := ecdsa.Sign(rand.Reader, caKey, digest)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cert.Signature = &Signature{
|
|
Format: KeyAlgoSKECDSA256,
|
|
Blob: Marshal(struct{ R, S *big.Int }{r, s}),
|
|
Rest: Marshal(struct {
|
|
Flags byte
|
|
Counter uint32
|
|
}{flags, counter}),
|
|
}
|
|
}
|
|
|
|
// TestCheckCertSKAuthorityNoUPRequired pins the OpenSSH-parity
|
|
// behavior of CertChecker.CheckCert for SK CA signatures: a
|
|
// certificate signed by an SK CA that produced a UP=0 signature is
|
|
// accepted, because the UP bit on a CA signature has no bearing on
|
|
// the current user authentication (see sshkey.c:cert_parse, which
|
|
// passes detailsp==NULL to sshkey_verify). Without this, certs issued
|
|
// by non-interactive SK CAs would fail to verify.
|
|
func TestCheckCertSKAuthorityNoUPRequired(t *testing.T) {
|
|
caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
userKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
userPub, err := NewPublicKey(&userKey.PublicKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
mkCert := func(flags byte) *Certificate {
|
|
c := &Certificate{
|
|
CertType: UserCert,
|
|
Key: userPub,
|
|
ValidBefore: CertTimeInfinity,
|
|
ValidPrincipals: []string{"user"},
|
|
}
|
|
signSKCert(t, c, caKey, "ssh:", flags)
|
|
return c
|
|
}
|
|
|
|
checker := CertChecker{
|
|
IsUserAuthority: func(k PublicKey) bool {
|
|
caSK := &skECDSAPublicKey{application: "ssh:", PublicKey: caKey.PublicKey}
|
|
return bytes.Equal(k.Marshal(), caSK.Marshal())
|
|
},
|
|
}
|
|
|
|
// Both UP=0 and UP=1 CA signatures verify: UP is not enforced
|
|
// here, matching OpenSSH.
|
|
if err := checker.CheckCert("user", mkCert(0)); err != nil {
|
|
t.Errorf("UP=0 CA signature must verify (OpenSSH parity): %v", err)
|
|
}
|
|
if err := checker.CheckCert("user", mkCert(flagUserPresence)); err != nil {
|
|
t.Errorf("UP=1 CA signature must verify: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestValidateCertTime(t *testing.T) {
|
|
cert := Certificate{
|
|
ValidPrincipals: []string{"user"},
|
|
Key: testPublicKeys["rsa"],
|
|
ValidAfter: 50,
|
|
ValidBefore: 100,
|
|
}
|
|
|
|
cert.SignCert(rand.Reader, testSigners["ecdsa"])
|
|
|
|
for ts, ok := range map[int64]bool{
|
|
25: false,
|
|
50: true,
|
|
99: true,
|
|
100: false,
|
|
125: false,
|
|
} {
|
|
checker := CertChecker{
|
|
Clock: func() time.Time { return time.Unix(ts, 0) },
|
|
}
|
|
checker.IsUserAuthority = func(k PublicKey) bool {
|
|
return bytes.Equal(k.Marshal(),
|
|
testPublicKeys["ecdsa"].Marshal())
|
|
}
|
|
|
|
if v := checker.CheckCert("user", &cert); (v == nil) != ok {
|
|
t.Errorf("Authenticate(%d): %v", ts, v)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO(hanwen): tests for
|
|
//
|
|
// host keys:
|
|
// * fallbacks
|
|
|
|
func TestHostKeyCert(t *testing.T) {
|
|
cert := &Certificate{
|
|
ValidPrincipals: []string{"hostname", "hostname.domain", "otherhost"},
|
|
Key: testPublicKeys["rsa"],
|
|
ValidBefore: CertTimeInfinity,
|
|
CertType: HostCert,
|
|
}
|
|
cert.SignCert(rand.Reader, testSigners["ecdsa"])
|
|
|
|
checker := &CertChecker{
|
|
IsHostAuthority: func(p PublicKey, addr string) bool {
|
|
return addr == "hostname:22" && bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal())
|
|
},
|
|
}
|
|
|
|
certSigner, err := NewCertSigner(cert, testSigners["rsa"])
|
|
if err != nil {
|
|
t.Errorf("NewCertSigner: %v", err)
|
|
}
|
|
|
|
for _, test := range []struct {
|
|
addr string
|
|
succeed bool
|
|
certSignerAlgorithms []string // Empty means no algorithm restrictions.
|
|
clientHostKeyAlgorithms []string
|
|
}{
|
|
{addr: "hostname:22", succeed: true},
|
|
{
|
|
addr: "hostname:22",
|
|
succeed: true,
|
|
certSignerAlgorithms: []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512},
|
|
clientHostKeyAlgorithms: []string{CertAlgoRSASHA512v01},
|
|
},
|
|
{
|
|
addr: "hostname:22",
|
|
succeed: false,
|
|
certSignerAlgorithms: []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512},
|
|
clientHostKeyAlgorithms: []string{CertAlgoRSAv01},
|
|
},
|
|
{
|
|
addr: "hostname:22",
|
|
succeed: false,
|
|
certSignerAlgorithms: []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512},
|
|
clientHostKeyAlgorithms: []string{KeyAlgoRSASHA512}, // Not a certificate algorithm.
|
|
},
|
|
{addr: "otherhost:22", succeed: false}, // The certificate is valid for 'otherhost' as hostname, but we only recognize the authority of the signer for the address 'hostname:22'
|
|
{addr: "lasthost:22", succeed: false},
|
|
} {
|
|
c1, c2, err := netPipe()
|
|
if err != nil {
|
|
t.Fatalf("netPipe: %v", err)
|
|
}
|
|
defer c1.Close()
|
|
defer c2.Close()
|
|
|
|
errc := make(chan error)
|
|
|
|
go func() {
|
|
conf := ServerConfig{
|
|
NoClientAuth: true,
|
|
}
|
|
if len(test.certSignerAlgorithms) > 0 {
|
|
mas, err := NewSignerWithAlgorithms(certSigner.(AlgorithmSigner), test.certSignerAlgorithms)
|
|
if err != nil {
|
|
errc <- err
|
|
return
|
|
}
|
|
conf.AddHostKey(mas)
|
|
} else {
|
|
conf.AddHostKey(certSigner)
|
|
}
|
|
_, _, _, err := NewServerConn(c1, &conf)
|
|
errc <- err
|
|
}()
|
|
|
|
config := &ClientConfig{
|
|
User: "user",
|
|
HostKeyCallback: checker.CheckHostKey,
|
|
HostKeyAlgorithms: test.clientHostKeyAlgorithms,
|
|
}
|
|
_, _, _, err = NewClientConn(c2, test.addr, config)
|
|
|
|
if (err == nil) != test.succeed {
|
|
t.Errorf("NewClientConn(%q): %v", test.addr, err)
|
|
}
|
|
|
|
err = <-errc
|
|
if (err == nil) != test.succeed {
|
|
t.Errorf("NewServerConn(%q): %v", test.addr, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
type legacyRSASigner struct {
|
|
Signer
|
|
}
|
|
|
|
func (s *legacyRSASigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
|
v, ok := s.Signer.(AlgorithmSigner)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid signer")
|
|
}
|
|
return v.SignWithAlgorithm(rand, data, KeyAlgoRSA)
|
|
}
|
|
|
|
func TestCertTypes(t *testing.T) {
|
|
algorithmSigner, ok := testSigners["rsa"].(AlgorithmSigner)
|
|
if !ok {
|
|
t.Fatal("rsa test signer does not implement the AlgorithmSigner interface")
|
|
}
|
|
multiAlgoSignerSHA256, err := NewSignerWithAlgorithms(algorithmSigner, []string{KeyAlgoRSASHA256})
|
|
if err != nil {
|
|
t.Fatalf("unable to create multi algorithm signer SHA256: %v", err)
|
|
}
|
|
// Algorithms are in order of preference, we expect rsa-sha2-512 to be used.
|
|
multiAlgoSignerSHA512, err := NewSignerWithAlgorithms(algorithmSigner, []string{KeyAlgoRSASHA512, KeyAlgoRSASHA256})
|
|
if err != nil {
|
|
t.Fatalf("unable to create multi algorithm signer SHA512: %v", err)
|
|
}
|
|
|
|
var testVars = []struct {
|
|
name string
|
|
signer Signer
|
|
algo string
|
|
}{
|
|
{CertAlgoECDSA256v01, testSigners["ecdsap256"], ""},
|
|
{CertAlgoECDSA384v01, testSigners["ecdsap384"], ""},
|
|
{CertAlgoECDSA521v01, testSigners["ecdsap521"], ""},
|
|
{CertAlgoED25519v01, testSigners["ed25519"], ""},
|
|
{CertAlgoRSAv01, testSigners["rsa"], KeyAlgoRSASHA256},
|
|
{"legacyRSASigner", &legacyRSASigner{testSigners["rsa"]}, KeyAlgoRSA},
|
|
{"multiAlgoRSASignerSHA256", multiAlgoSignerSHA256, KeyAlgoRSASHA256},
|
|
{"multiAlgoRSASignerSHA512", multiAlgoSignerSHA512, KeyAlgoRSASHA512},
|
|
{InsecureCertAlgoDSAv01, testSigners["dsa"], ""},
|
|
}
|
|
|
|
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
t.Fatalf("error generating host key: %v", err)
|
|
}
|
|
|
|
signer, err := NewSignerFromKey(k)
|
|
if err != nil {
|
|
t.Fatalf("error generating signer for ssh listener: %v", err)
|
|
}
|
|
|
|
conf := &ServerConfig{
|
|
PublicKeyCallback: func(c ConnMetadata, k PublicKey) (*Permissions, error) {
|
|
return new(Permissions), nil
|
|
},
|
|
}
|
|
conf.AddHostKey(signer)
|
|
|
|
for _, m := range testVars {
|
|
t.Run(m.name, func(t *testing.T) {
|
|
|
|
c1, c2, err := netPipe()
|
|
if err != nil {
|
|
t.Fatalf("netPipe: %v", err)
|
|
}
|
|
defer c1.Close()
|
|
defer c2.Close()
|
|
|
|
go NewServerConn(c1, conf)
|
|
|
|
priv := m.signer
|
|
cert := &Certificate{
|
|
CertType: UserCert,
|
|
Key: priv.PublicKey(),
|
|
}
|
|
if err := cert.SignCert(rand.Reader, priv); err != nil {
|
|
t.Fatalf("error signing certificate: %v", err)
|
|
}
|
|
|
|
certSigner, err := NewCertSigner(cert, priv)
|
|
if err != nil {
|
|
t.Fatalf("error generating cert signer: %v", err)
|
|
}
|
|
|
|
if m.algo != "" && cert.Signature.Format != m.algo {
|
|
t.Errorf("expected %q signature format, got %q", m.algo, cert.Signature.Format)
|
|
}
|
|
|
|
config := &ClientConfig{
|
|
User: "user",
|
|
HostKeyCallback: func(h string, r net.Addr, k PublicKey) error { return nil },
|
|
Auth: []AuthMethod{PublicKeys(certSigner)},
|
|
}
|
|
|
|
_, _, _, err = NewClientConn(c2, "", config)
|
|
if err != nil {
|
|
t.Fatalf("error connecting: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCertSignWithMultiAlgorithmSigner(t *testing.T) {
|
|
type testcase struct {
|
|
sigAlgo string
|
|
algorithms []string
|
|
}
|
|
cases := []testcase{
|
|
{
|
|
sigAlgo: KeyAlgoRSA,
|
|
algorithms: []string{KeyAlgoRSA, KeyAlgoRSASHA512},
|
|
},
|
|
{
|
|
sigAlgo: KeyAlgoRSASHA256,
|
|
algorithms: []string{KeyAlgoRSASHA256, KeyAlgoRSA, KeyAlgoRSASHA512},
|
|
},
|
|
{
|
|
sigAlgo: KeyAlgoRSASHA512,
|
|
algorithms: []string{KeyAlgoRSASHA512, KeyAlgoRSASHA256},
|
|
},
|
|
}
|
|
|
|
cert := &Certificate{
|
|
Key: testPublicKeys["rsa"],
|
|
ValidBefore: CertTimeInfinity,
|
|
CertType: UserCert,
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.sigAlgo, func(t *testing.T) {
|
|
signer, err := NewSignerWithAlgorithms(testSigners["rsa"].(AlgorithmSigner), c.algorithms)
|
|
if err != nil {
|
|
t.Fatalf("NewSignerWithAlgorithms error: %v", err)
|
|
}
|
|
if err := cert.SignCert(rand.Reader, signer); err != nil {
|
|
t.Fatalf("SignCert error: %v", err)
|
|
}
|
|
if cert.Signature.Format != c.sigAlgo {
|
|
t.Fatalf("got signature format %q, want %q", cert.Signature.Format, c.sigAlgo)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCertSignWithCertificate(t *testing.T) {
|
|
cert := &Certificate{
|
|
Key: testPublicKeys["rsa"],
|
|
ValidBefore: CertTimeInfinity,
|
|
CertType: UserCert,
|
|
}
|
|
if err := cert.SignCert(rand.Reader, testSigners["ecdsa"]); err != nil {
|
|
t.Fatalf("SignCert: %v", err)
|
|
}
|
|
signer, err := NewSignerWithAlgorithms(testSigners["rsa"].(AlgorithmSigner), []string{KeyAlgoRSASHA256})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
certSigner, err := NewCertSigner(cert, signer)
|
|
if err != nil {
|
|
t.Fatalf("NewCertSigner: %v", err)
|
|
}
|
|
|
|
cert1 := &Certificate{
|
|
Key: testPublicKeys["ecdsa"],
|
|
ValidBefore: CertTimeInfinity,
|
|
CertType: UserCert,
|
|
}
|
|
|
|
if err := cert1.SignCert(rand.Reader, certSigner); err == nil {
|
|
t.Fatal("successfully signed a certificate using another certificate, it is expected to fail")
|
|
}
|
|
}
|