mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 08:21:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			306 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			306 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
		
			Executable file
		
	
	
	
	
// Copyright 2014 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/dsa"
 | 
						|
	"crypto/ecdsa"
 | 
						|
	"crypto/elliptic"
 | 
						|
	"crypto/rand"
 | 
						|
	"crypto/rsa"
 | 
						|
	"encoding/base64"
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	"github.com/gogits/gogs/modules/crypto/ssh/testdata"
 | 
						|
)
 | 
						|
 | 
						|
func rawKey(pub PublicKey) interface{} {
 | 
						|
	switch k := pub.(type) {
 | 
						|
	case *rsaPublicKey:
 | 
						|
		return (*rsa.PublicKey)(k)
 | 
						|
	case *dsaPublicKey:
 | 
						|
		return (*dsa.PublicKey)(k)
 | 
						|
	case *ecdsaPublicKey:
 | 
						|
		return (*ecdsa.PublicKey)(k)
 | 
						|
	case *Certificate:
 | 
						|
		return k
 | 
						|
	}
 | 
						|
	panic("unknown key type")
 | 
						|
}
 | 
						|
 | 
						|
func TestKeyMarshalParse(t *testing.T) {
 | 
						|
	for _, priv := range testSigners {
 | 
						|
		pub := priv.PublicKey()
 | 
						|
		roundtrip, err := ParsePublicKey(pub.Marshal())
 | 
						|
		if err != nil {
 | 
						|
			t.Errorf("ParsePublicKey(%T): %v", pub, err)
 | 
						|
		}
 | 
						|
 | 
						|
		k1 := rawKey(pub)
 | 
						|
		k2 := rawKey(roundtrip)
 | 
						|
 | 
						|
		if !reflect.DeepEqual(k1, k2) {
 | 
						|
			t.Errorf("got %#v in roundtrip, want %#v", k2, k1)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestUnsupportedCurves(t *testing.T) {
 | 
						|
	raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("GenerateKey: %v", err)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P256") {
 | 
						|
		t.Fatalf("NewPrivateKey should not succeed with P224, got: %v", err)
 | 
						|
	}
 | 
						|
 | 
						|
	if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P256") {
 | 
						|
		t.Fatalf("NewPublicKey should not succeed with P224, got: %v", err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestNewPublicKey(t *testing.T) {
 | 
						|
	for _, k := range testSigners {
 | 
						|
		raw := rawKey(k.PublicKey())
 | 
						|
		// Skip certificates, as NewPublicKey does not support them.
 | 
						|
		if _, ok := raw.(*Certificate); ok {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		pub, err := NewPublicKey(raw)
 | 
						|
		if err != nil {
 | 
						|
			t.Errorf("NewPublicKey(%#v): %v", raw, err)
 | 
						|
		}
 | 
						|
		if !reflect.DeepEqual(k.PublicKey(), pub) {
 | 
						|
			t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey())
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestKeySignVerify(t *testing.T) {
 | 
						|
	for _, priv := range testSigners {
 | 
						|
		pub := priv.PublicKey()
 | 
						|
 | 
						|
		data := []byte("sign me")
 | 
						|
		sig, err := priv.Sign(rand.Reader, data)
 | 
						|
		if err != nil {
 | 
						|
			t.Fatalf("Sign(%T): %v", priv, err)
 | 
						|
		}
 | 
						|
 | 
						|
		if err := pub.Verify(data, sig); err != nil {
 | 
						|
			t.Errorf("publicKey.Verify(%T): %v", priv, err)
 | 
						|
		}
 | 
						|
		sig.Blob[5]++
 | 
						|
		if err := pub.Verify(data, sig); err == nil {
 | 
						|
			t.Errorf("publicKey.Verify on broken sig did not fail")
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestParseRSAPrivateKey(t *testing.T) {
 | 
						|
	key := testPrivateKeys["rsa"]
 | 
						|
 | 
						|
	rsa, ok := key.(*rsa.PrivateKey)
 | 
						|
	if !ok {
 | 
						|
		t.Fatalf("got %T, want *rsa.PrivateKey", rsa)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := rsa.Validate(); err != nil {
 | 
						|
		t.Errorf("Validate: %v", err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestParseECPrivateKey(t *testing.T) {
 | 
						|
	key := testPrivateKeys["ecdsa"]
 | 
						|
 | 
						|
	ecKey, ok := key.(*ecdsa.PrivateKey)
 | 
						|
	if !ok {
 | 
						|
		t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey)
 | 
						|
	}
 | 
						|
 | 
						|
	if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) {
 | 
						|
		t.Fatalf("public key does not validate.")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestParseDSA(t *testing.T) {
 | 
						|
	// We actually exercise the ParsePrivateKey codepath here, as opposed to
 | 
						|
	// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
 | 
						|
	// uses.
 | 
						|
	s, err := ParsePrivateKey(testdata.PEMBytes["dsa"])
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("ParsePrivateKey returned error: %s", err)
 | 
						|
	}
 | 
						|
 | 
						|
	data := []byte("sign me")
 | 
						|
	sig, err := s.Sign(rand.Reader, data)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("dsa.Sign: %v", err)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := s.PublicKey().Verify(data, sig); err != nil {
 | 
						|
		t.Errorf("Verify failed: %v", err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Tests for authorized_keys parsing.
 | 
						|
 | 
						|
// getTestKey returns a public key, and its base64 encoding.
 | 
						|
func getTestKey() (PublicKey, string) {
 | 
						|
	k := testPublicKeys["rsa"]
 | 
						|
 | 
						|
	b := &bytes.Buffer{}
 | 
						|
	e := base64.NewEncoder(base64.StdEncoding, b)
 | 
						|
	e.Write(k.Marshal())
 | 
						|
	e.Close()
 | 
						|
 | 
						|
	return k, b.String()
 | 
						|
}
 | 
						|
 | 
						|
func TestMarshalParsePublicKey(t *testing.T) {
 | 
						|
	pub, pubSerialized := getTestKey()
 | 
						|
	line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized)
 | 
						|
 | 
						|
	authKeys := MarshalAuthorizedKey(pub)
 | 
						|
	actualFields := strings.Fields(string(authKeys))
 | 
						|
	if len(actualFields) == 0 {
 | 
						|
		t.Fatalf("failed authKeys: %v", authKeys)
 | 
						|
	}
 | 
						|
 | 
						|
	// drop the comment
 | 
						|
	expectedFields := strings.Fields(line)[0:2]
 | 
						|
 | 
						|
	if !reflect.DeepEqual(actualFields, expectedFields) {
 | 
						|
		t.Errorf("got %v, expected %v", actualFields, expectedFields)
 | 
						|
	}
 | 
						|
 | 
						|
	actPub, _, _, _, err := ParseAuthorizedKey([]byte(line))
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("cannot parse %v: %v", line, err)
 | 
						|
	}
 | 
						|
	if !reflect.DeepEqual(actPub, pub) {
 | 
						|
		t.Errorf("got %v, expected %v", actPub, pub)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type authResult struct {
 | 
						|
	pubKey   PublicKey
 | 
						|
	options  []string
 | 
						|
	comments string
 | 
						|
	rest     string
 | 
						|
	ok       bool
 | 
						|
}
 | 
						|
 | 
						|
func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) {
 | 
						|
	rest := authKeys
 | 
						|
	var values []authResult
 | 
						|
	for len(rest) > 0 {
 | 
						|
		var r authResult
 | 
						|
		var err error
 | 
						|
		r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest)
 | 
						|
		r.ok = (err == nil)
 | 
						|
		t.Log(err)
 | 
						|
		r.rest = string(rest)
 | 
						|
		values = append(values, r)
 | 
						|
	}
 | 
						|
 | 
						|
	if !reflect.DeepEqual(values, expected) {
 | 
						|
		t.Errorf("got %#v, expected %#v", values, expected)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestAuthorizedKeyBasic(t *testing.T) {
 | 
						|
	pub, pubSerialized := getTestKey()
 | 
						|
	line := "ssh-rsa " + pubSerialized + " user@host"
 | 
						|
	testAuthorizedKeys(t, []byte(line),
 | 
						|
		[]authResult{
 | 
						|
			{pub, nil, "user@host", "", true},
 | 
						|
		})
 | 
						|
}
 | 
						|
 | 
						|
func TestAuth(t *testing.T) {
 | 
						|
	pub, pubSerialized := getTestKey()
 | 
						|
	authWithOptions := []string{
 | 
						|
		`# comments to ignore before any keys...`,
 | 
						|
		``,
 | 
						|
		`env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`,
 | 
						|
		`# comments to ignore, along with a blank line`,
 | 
						|
		``,
 | 
						|
		`env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`,
 | 
						|
		``,
 | 
						|
		`# more comments, plus a invalid entry`,
 | 
						|
		`ssh-rsa data-that-will-not-parse user@host3`,
 | 
						|
	}
 | 
						|
	for _, eol := range []string{"\n", "\r\n"} {
 | 
						|
		authOptions := strings.Join(authWithOptions, eol)
 | 
						|
		rest2 := strings.Join(authWithOptions[3:], eol)
 | 
						|
		rest3 := strings.Join(authWithOptions[6:], eol)
 | 
						|
		testAuthorizedKeys(t, []byte(authOptions), []authResult{
 | 
						|
			{pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true},
 | 
						|
			{pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true},
 | 
						|
			{nil, nil, "", "", false},
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
 | 
						|
	pub, pubSerialized := getTestKey()
 | 
						|
	authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
 | 
						|
	testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{
 | 
						|
		{pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestAuthWithQuotedCommaInEnv(t *testing.T) {
 | 
						|
	pub, pubSerialized := getTestKey()
 | 
						|
	authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + `   user@host`)
 | 
						|
	testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{
 | 
						|
		{pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestAuthWithQuotedQuoteInEnv(t *testing.T) {
 | 
						|
	pub, pubSerialized := getTestKey()
 | 
						|
	authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + `   user@host`)
 | 
						|
	authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`)
 | 
						|
	testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{
 | 
						|
		{pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true},
 | 
						|
	})
 | 
						|
 | 
						|
	testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{
 | 
						|
		{pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestAuthWithInvalidSpace(t *testing.T) {
 | 
						|
	_, pubSerialized := getTestKey()
 | 
						|
	authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
 | 
						|
#more to follow but still no valid keys`)
 | 
						|
	testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{
 | 
						|
		{nil, nil, "", "", false},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestAuthWithMissingQuote(t *testing.T) {
 | 
						|
	pub, pubSerialized := getTestKey()
 | 
						|
	authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
 | 
						|
env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`)
 | 
						|
 | 
						|
	testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{
 | 
						|
		{pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestInvalidEntry(t *testing.T) {
 | 
						|
	authInvalid := []byte(`ssh-rsa`)
 | 
						|
	_, _, _, _, err := ParseAuthorizedKey(authInvalid)
 | 
						|
	if err == nil {
 | 
						|
		t.Errorf("got valid entry for %q", authInvalid)
 | 
						|
	}
 | 
						|
}
 |