Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3cc52e598 | ||
|
|
833695f0a5 | ||
|
|
ec07f4e35b | ||
|
|
b665ba6f6f | ||
|
|
a1aeb9b34e | ||
|
|
28c53ff63c | ||
|
|
3f0842a464 | ||
|
|
e90f1e17ee | ||
|
|
d359caa4a3 | ||
|
|
c5370d2cc6 | ||
|
|
0d375be9b6 | ||
|
|
b4ddeeda5b | ||
|
|
edc325d13a | ||
|
|
eab9315960 | ||
|
|
ddfa82138e | ||
|
|
d08e19beac | ||
|
|
12e1fcd8c0 | ||
|
|
3f8f0645c2 | ||
|
|
5df3b59941 | ||
|
|
2e82bdd171 | ||
|
|
64e0e99383 | ||
|
|
23b1b90df2 |
@@ -2,8 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.11 && gc && !purego
|
//go:build gc && !purego
|
||||||
// +build go1.11,gc,!purego
|
// +build gc,!purego
|
||||||
|
|
||||||
package chacha20
|
package chacha20
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.11 && gc && !purego
|
//go:build gc && !purego
|
||||||
// +build go1.11,gc,!purego
|
// +build gc,!purego
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (!arm64 && !s390x && !ppc64le) || (arm64 && !go1.11) || !gc || purego
|
//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego
|
||||||
// +build !arm64,!s390x,!ppc64le arm64,!go1.11 !gc purego
|
// +build !arm64,!s390x,!ppc64le !gc purego
|
||||||
|
|
||||||
package chacha20
|
package chacha20
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,11 @@ func (b *Builder) AddUint32(v uint32) {
|
|||||||
b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddUint48 appends a big-endian, 48-bit value to the byte string.
|
||||||
|
func (b *Builder) AddUint48(v uint64) {
|
||||||
|
b.add(byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||||
|
}
|
||||||
|
|
||||||
// AddUint64 appends a big-endian, 64-bit value to the byte string.
|
// AddUint64 appends a big-endian, 64-bit value to the byte string.
|
||||||
func (b *Builder) AddUint64(v uint64) {
|
func (b *Builder) AddUint64(v uint64) {
|
||||||
b.add(byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
b.add(byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||||
|
|||||||
@@ -179,6 +179,27 @@ func TestUint32(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUint48(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
var u uint64 = 0xfefcff3cfdfc
|
||||||
|
b.AddUint48(u)
|
||||||
|
if err := builderBytesEq(&b, 254, 252, 255, 60, 253, 252); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s String = b.BytesOrPanic()
|
||||||
|
var v uint64
|
||||||
|
if !s.ReadUint48(&v) {
|
||||||
|
t.Error("ReadUint48() = false, want true")
|
||||||
|
}
|
||||||
|
if v != u {
|
||||||
|
t.Errorf("v = %x, want %x", v, u)
|
||||||
|
}
|
||||||
|
if len(s) != 0 {
|
||||||
|
t.Errorf("len(s) = %d, want 0", len(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUint64(t *testing.T) {
|
func TestUint64(t *testing.T) {
|
||||||
var b Builder
|
var b Builder
|
||||||
b.AddUint64(0xf2fefefcff3cfdfc)
|
b.AddUint64(0xf2fefefcff3cfdfc)
|
||||||
|
|||||||
@@ -81,6 +81,17 @@ func (s *String) ReadUint32(out *uint32) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadUint48 decodes a big-endian, 48-bit value into out and advances over it.
|
||||||
|
// It reports whether the read was successful.
|
||||||
|
func (s *String) ReadUint48(out *uint64) bool {
|
||||||
|
v := s.read(6)
|
||||||
|
if v == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*out = uint64(v[0])<<40 | uint64(v[1])<<32 | uint64(v[2])<<24 | uint64(v[3])<<16 | uint64(v[4])<<8 | uint64(v[5])
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ReadUint64 decodes a big-endian, 64-bit value into out and advances over it.
|
// ReadUint64 decodes a big-endian, 64-bit value into out and advances over it.
|
||||||
// It reports whether the read was successful.
|
// It reports whether the read was successful.
|
||||||
func (s *String) ReadUint64(out *uint64) bool {
|
func (s *String) ReadUint64(out *uint64) bool {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ go 1.17
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/net v0.10.0 // tagx:ignore
|
golang.org/x/net v0.10.0 // tagx:ignore
|
||||||
golang.org/x/sys v0.10.0
|
golang.org/x/sys v0.13.0
|
||||||
golang.org/x/term v0.10.0
|
golang.org/x/term v0.13.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require golang.org/x/text v0.11.0 // indirect
|
require golang.org/x/text v0.13.0 // indirect
|
||||||
|
|||||||
@@ -19,21 +19,21 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
|||||||
@@ -8,9 +8,8 @@
|
|||||||
package wycheproof
|
package wycheproof
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEddsa(t *testing.T) {
|
func TestEddsa(t *testing.T) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@@ -28,6 +29,11 @@ const wycheproofModVer = "v0.0.0-20191219022705-2196000605e4"
|
|||||||
var wycheproofTestVectorsDir string
|
var wycheproofTestVectorsDir string
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
flag.Parse()
|
||||||
|
if flag.Lookup("test.short").Value.(flag.Getter).Get().(bool) {
|
||||||
|
log.Println("skipping test that downloads testdata via 'go mod download' in short mode")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
if _, err := exec.LookPath("go"); err != nil {
|
if _, err := exec.LookPath("go"); err != nil {
|
||||||
log.Printf("skipping test because 'go' command is unavailable: %v", err)
|
log.Printf("skipping test because 'go' command is unavailable: %v", err)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
@@ -42,8 +48,6 @@ func TestMain(m *testing.M) {
|
|||||||
// can be used in the following tests.
|
// can be used in the following tests.
|
||||||
path := "github.com/google/wycheproof@" + wycheproofModVer
|
path := "github.com/google/wycheproof@" + wycheproofModVer
|
||||||
cmd := exec.Command("go", "mod", "download", "-json", path)
|
cmd := exec.Command("go", "mod", "download", "-json", path)
|
||||||
// TODO: enable the sumdb once the Trybots proxy supports it.
|
|
||||||
cmd.Env = append(os.Environ(), "GONOSUMDB=*")
|
|
||||||
output, err := cmd.Output()
|
output, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to run `go mod download -json %s`, output: %s", path, output)
|
log.Fatalf("failed to run `go mod download -json %s`, output: %s", path, output)
|
||||||
|
|||||||
+1
-1
@@ -21,9 +21,9 @@
|
|||||||
package sign
|
package sign
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
"golang.org/x/crypto/internal/alias"
|
"golang.org/x/crypto/internal/alias"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+9
-5
@@ -121,11 +121,11 @@ func (d *state) padAndPermute(dsbyte byte) {
|
|||||||
copyOut(d, d.buf)
|
copyOut(d, d.buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write absorbs more data into the hash's state. It produces an error
|
// Write absorbs more data into the hash's state. It panics if any
|
||||||
// if more data is written to the ShakeHash after writing
|
// output has already been read.
|
||||||
func (d *state) Write(p []byte) (written int, err error) {
|
func (d *state) Write(p []byte) (written int, err error) {
|
||||||
if d.state != spongeAbsorbing {
|
if d.state != spongeAbsorbing {
|
||||||
panic("sha3: write to sponge after read")
|
panic("sha3: Write after Read")
|
||||||
}
|
}
|
||||||
if d.buf == nil {
|
if d.buf == nil {
|
||||||
d.buf = d.storage.asBytes()[:0]
|
d.buf = d.storage.asBytes()[:0]
|
||||||
@@ -182,12 +182,16 @@ func (d *state) Read(out []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sum applies padding to the hash state and then squeezes out the desired
|
// Sum applies padding to the hash state and then squeezes out the desired
|
||||||
// number of output bytes.
|
// number of output bytes. It panics if any output has already been read.
|
||||||
func (d *state) Sum(in []byte) []byte {
|
func (d *state) Sum(in []byte) []byte {
|
||||||
|
if d.state != spongeAbsorbing {
|
||||||
|
panic("sha3: Sum after Read")
|
||||||
|
}
|
||||||
|
|
||||||
// Make a copy of the original hash so that caller can keep writing
|
// Make a copy of the original hash so that caller can keep writing
|
||||||
// and summing.
|
// and summing.
|
||||||
dup := d.clone()
|
dup := d.clone()
|
||||||
hash := make([]byte, dup.outputLen)
|
hash := make([]byte, dup.outputLen, 64) // explicit cap to allow stack allocation
|
||||||
dup.Read(hash)
|
dup.Read(hash)
|
||||||
return append(in, hash...)
|
return append(in, hash...)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -49,7 +49,7 @@ type asmState struct {
|
|||||||
buf []byte // care must be taken to ensure cap(buf) is a multiple of rate
|
buf []byte // care must be taken to ensure cap(buf) is a multiple of rate
|
||||||
rate int // equivalent to block size
|
rate int // equivalent to block size
|
||||||
storage [3072]byte // underlying storage for buf
|
storage [3072]byte // underlying storage for buf
|
||||||
outputLen int // output length if fixed, 0 if not
|
outputLen int // output length for full security
|
||||||
function code // KIMD/KLMD function code
|
function code // KIMD/KLMD function code
|
||||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||||
}
|
}
|
||||||
@@ -72,8 +72,10 @@ func newAsmState(function code) *asmState {
|
|||||||
s.outputLen = 64
|
s.outputLen = 64
|
||||||
case shake_128:
|
case shake_128:
|
||||||
s.rate = 168
|
s.rate = 168
|
||||||
|
s.outputLen = 32
|
||||||
case shake_256:
|
case shake_256:
|
||||||
s.rate = 136
|
s.rate = 136
|
||||||
|
s.outputLen = 64
|
||||||
default:
|
default:
|
||||||
panic("sha3: unrecognized function code")
|
panic("sha3: unrecognized function code")
|
||||||
}
|
}
|
||||||
@@ -108,7 +110,7 @@ func (s *asmState) resetBuf() {
|
|||||||
// It never returns an error.
|
// It never returns an error.
|
||||||
func (s *asmState) Write(b []byte) (int, error) {
|
func (s *asmState) Write(b []byte) (int, error) {
|
||||||
if s.state != spongeAbsorbing {
|
if s.state != spongeAbsorbing {
|
||||||
panic("sha3: write to sponge after read")
|
panic("sha3: Write after Read")
|
||||||
}
|
}
|
||||||
length := len(b)
|
length := len(b)
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
@@ -192,8 +194,8 @@ func (s *asmState) Read(out []byte) (n int, err error) {
|
|||||||
// Sum appends the current hash to b and returns the resulting slice.
|
// Sum appends the current hash to b and returns the resulting slice.
|
||||||
// It does not change the underlying hash state.
|
// It does not change the underlying hash state.
|
||||||
func (s *asmState) Sum(b []byte) []byte {
|
func (s *asmState) Sum(b []byte) []byte {
|
||||||
if s.outputLen == 0 {
|
if s.state != spongeAbsorbing {
|
||||||
panic("sha3: cannot call Sum on SHAKE functions")
|
panic("sha3: Sum after Read")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the state to preserve the original.
|
// Copy the state to preserve the original.
|
||||||
|
|||||||
+14
-15
@@ -17,26 +17,25 @@ package sha3
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ShakeHash defines the interface to hash functions that
|
// ShakeHash defines the interface to hash functions that support
|
||||||
// support arbitrary-length output.
|
// arbitrary-length output. When used as a plain [hash.Hash], it
|
||||||
|
// produces minimum-length outputs that provide full-strength generic
|
||||||
|
// security.
|
||||||
type ShakeHash interface {
|
type ShakeHash interface {
|
||||||
// Write absorbs more data into the hash's state. It panics if input is
|
hash.Hash
|
||||||
// written to it after output has been read from it.
|
|
||||||
io.Writer
|
|
||||||
|
|
||||||
// Read reads more output from the hash; reading affects the hash's
|
// Read reads more output from the hash; reading affects the hash's
|
||||||
// state. (ShakeHash.Read is thus very different from Hash.Sum)
|
// state. (ShakeHash.Read is thus very different from Hash.Sum)
|
||||||
// It never returns an error.
|
// It never returns an error, but subsequent calls to Write or Sum
|
||||||
|
// will panic.
|
||||||
io.Reader
|
io.Reader
|
||||||
|
|
||||||
// Clone returns a copy of the ShakeHash in its current state.
|
// Clone returns a copy of the ShakeHash in its current state.
|
||||||
Clone() ShakeHash
|
Clone() ShakeHash
|
||||||
|
|
||||||
// Reset resets the ShakeHash to its initial state.
|
|
||||||
Reset()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cSHAKE specific context
|
// cSHAKE specific context
|
||||||
@@ -81,8 +80,8 @@ func leftEncode(value uint64) []byte {
|
|||||||
return b[i-1:]
|
return b[i-1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCShake(N, S []byte, rate int, dsbyte byte) ShakeHash {
|
func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash {
|
||||||
c := cshakeState{state: &state{rate: rate, dsbyte: dsbyte}}
|
c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}}
|
||||||
|
|
||||||
// leftEncode returns max 9 bytes
|
// leftEncode returns max 9 bytes
|
||||||
c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
|
c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
|
||||||
@@ -119,7 +118,7 @@ func NewShake128() ShakeHash {
|
|||||||
if h := newShake128Asm(); h != nil {
|
if h := newShake128Asm(); h != nil {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
return &state{rate: rate128, dsbyte: dsbyteShake}
|
return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
|
// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
|
||||||
@@ -129,7 +128,7 @@ func NewShake256() ShakeHash {
|
|||||||
if h := newShake256Asm(); h != nil {
|
if h := newShake256Asm(); h != nil {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
return &state{rate: rate256, dsbyte: dsbyteShake}
|
return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
|
// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
|
||||||
@@ -142,7 +141,7 @@ func NewCShake128(N, S []byte) ShakeHash {
|
|||||||
if len(N) == 0 && len(S) == 0 {
|
if len(N) == 0 && len(S) == 0 {
|
||||||
return NewShake128()
|
return NewShake128()
|
||||||
}
|
}
|
||||||
return newCShake(N, S, rate128, dsbyteCShake)
|
return newCShake(N, S, rate128, 32, dsbyteCShake)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
|
// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
|
||||||
@@ -155,7 +154,7 @@ func NewCShake256(N, S []byte) ShakeHash {
|
|||||||
if len(N) == 0 && len(S) == 0 {
|
if len(N) == 0 && len(S) == 0 {
|
||||||
return NewShake256()
|
return NewShake256()
|
||||||
}
|
}
|
||||||
return newCShake(N, S, rate256, dsbyteCShake)
|
return newCShake(N, S, rate256, 64, dsbyteCShake)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShakeSum128 writes an arbitrary-length digest of data into hash.
|
// ShakeSum128 writes an arbitrary-length digest of data into hash.
|
||||||
|
|||||||
+1
-1
@@ -16,6 +16,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/dsa"
|
"crypto/dsa"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
@@ -26,7 +27,6 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ func startOpenSSHAgent(t *testing.T) (client ExtendedAgent, socket string, clean
|
|||||||
// types supported vary by platform.
|
// types supported vary by platform.
|
||||||
t.Skip("skipping test due to -short")
|
t.Skip("skipping test due to -short")
|
||||||
}
|
}
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("skipping on windows, we don't support connecting to the ssh-agent via a named pipe")
|
||||||
|
}
|
||||||
|
|
||||||
bin, err := exec.LookPath("ssh-agent")
|
bin, err := exec.LookPath("ssh-agent")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -366,7 +369,8 @@ func TestAuth(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
conn, _, _, err := ssh.NewServerConn(a, &serverConf)
|
conn, _, _, err := ssh.NewServerConn(a, &serverConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Server: %v", err)
|
t.Errorf("NewServerConn error: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}()
|
}()
|
||||||
|
|||||||
+1
-1
@@ -7,6 +7,7 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"crypto/dsa"
|
"crypto/dsa"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
@@ -16,7 +17,6 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -53,10 +53,11 @@ func TestSetupForwardAgent(t *testing.T) {
|
|||||||
incoming := make(chan *ssh.ServerConn, 1)
|
incoming := make(chan *ssh.ServerConn, 1)
|
||||||
go func() {
|
go func() {
|
||||||
conn, _, _, err := ssh.NewServerConn(a, &serverConf)
|
conn, _, _, err := ssh.NewServerConn(a, &serverConf)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Server: %v", err)
|
|
||||||
}
|
|
||||||
incoming <- conn
|
incoming <- conn
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("NewServerConn error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
conf := ssh.ClientConfig{
|
conf := ssh.ClientConfig{
|
||||||
@@ -71,8 +72,10 @@ func TestSetupForwardAgent(t *testing.T) {
|
|||||||
if err := ForwardToRemote(client, socket); err != nil {
|
if err := ForwardToRemote(client, socket); err != nil {
|
||||||
t.Fatalf("SetupForwardAgent: %v", err)
|
t.Fatalf("SetupForwardAgent: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
server := <-incoming
|
server := <-incoming
|
||||||
|
if server == nil {
|
||||||
|
t.Fatal("Unable to get server")
|
||||||
|
}
|
||||||
ch, reqs, err := server.OpenChannel(channelType, nil)
|
ch, reqs, err := server.OpenChannel(channelType, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenChannel(%q): %v", channelType, err)
|
t.Fatalf("OpenChannel(%q): %v", channelType, err)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package ssh
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -90,16 +91,16 @@ func BenchmarkEndToEnd(b *testing.B) {
|
|||||||
go func() {
|
go func() {
|
||||||
newCh, err := server.Accept()
|
newCh, err := server.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("Client: %v", err)
|
panic(fmt.Sprintf("Client: %v", err))
|
||||||
}
|
}
|
||||||
ch, incoming, err := newCh.Accept()
|
ch, incoming, err := newCh.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("Accept: %v", err)
|
panic(fmt.Sprintf("Accept: %v", err))
|
||||||
}
|
}
|
||||||
go DiscardRequests(incoming)
|
go DiscardRequests(incoming)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
if _, err := io.ReadFull(ch, output); err != nil {
|
if _, err := io.ReadFull(ch, output); err != nil {
|
||||||
b.Fatalf("ReadFull: %v", err)
|
panic(fmt.Sprintf("ReadFull: %v", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ch.Close()
|
ch.Close()
|
||||||
|
|||||||
+30
-8
@@ -16,8 +16,9 @@ import (
|
|||||||
|
|
||||||
// Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear
|
// Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear
|
||||||
// in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms.
|
// in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms.
|
||||||
// Unlike key algorithm names, these are not passed to AlgorithmSigner and don't
|
// Unlike key algorithm names, these are not passed to AlgorithmSigner nor
|
||||||
// appear in the Signature.Format field.
|
// returned by MultiAlgorithmSigner and don't appear in the Signature.Format
|
||||||
|
// field.
|
||||||
const (
|
const (
|
||||||
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
|
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
|
||||||
CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
|
CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
|
||||||
@@ -255,10 +256,17 @@ func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) {
|
|||||||
return nil, errors.New("ssh: signer and cert have different public key")
|
return nil, errors.New("ssh: signer and cert have different public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
if algorithmSigner, ok := signer.(AlgorithmSigner); ok {
|
switch s := signer.(type) {
|
||||||
|
case MultiAlgorithmSigner:
|
||||||
|
return &multiAlgorithmSigner{
|
||||||
|
AlgorithmSigner: &algorithmOpenSSHCertSigner{
|
||||||
|
&openSSHCertSigner{cert, signer}, s},
|
||||||
|
supportedAlgorithms: s.Algorithms(),
|
||||||
|
}, nil
|
||||||
|
case AlgorithmSigner:
|
||||||
return &algorithmOpenSSHCertSigner{
|
return &algorithmOpenSSHCertSigner{
|
||||||
&openSSHCertSigner{cert, signer}, algorithmSigner}, nil
|
&openSSHCertSigner{cert, signer}, s}, nil
|
||||||
} else {
|
default:
|
||||||
return &openSSHCertSigner{cert, signer}, nil
|
return &openSSHCertSigner{cert, signer}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,7 +440,9 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SignCert signs the certificate with an authority, setting the Nonce,
|
// SignCert signs the certificate with an authority, setting the Nonce,
|
||||||
// SignatureKey, and Signature fields.
|
// SignatureKey, and Signature fields. If the authority implements the
|
||||||
|
// MultiAlgorithmSigner interface the first algorithm in the list is used. This
|
||||||
|
// is useful if you want to sign with a specific algorithm.
|
||||||
func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
|
func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
|
||||||
c.Nonce = make([]byte, 32)
|
c.Nonce = make([]byte, 32)
|
||||||
if _, err := io.ReadFull(rand, c.Nonce); err != nil {
|
if _, err := io.ReadFull(rand, c.Nonce); err != nil {
|
||||||
@@ -440,8 +450,20 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
|
|||||||
}
|
}
|
||||||
c.SignatureKey = authority.PublicKey()
|
c.SignatureKey = authority.PublicKey()
|
||||||
|
|
||||||
// Default to KeyAlgoRSASHA512 for ssh-rsa signers.
|
if v, ok := authority.(MultiAlgorithmSigner); ok {
|
||||||
if v, ok := authority.(AlgorithmSigner); ok && v.PublicKey().Type() == KeyAlgoRSA {
|
if len(v.Algorithms()) == 0 {
|
||||||
|
return errors.New("the provided authority has no signature algorithm")
|
||||||
|
}
|
||||||
|
// Use the first algorithm in the list.
|
||||||
|
sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), v.Algorithms()[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Signature = sig
|
||||||
|
return nil
|
||||||
|
} else if v, ok := authority.(AlgorithmSigner); ok && v.PublicKey().Type() == KeyAlgoRSA {
|
||||||
|
// Default to KeyAlgoRSASHA512 for ssh-rsa signers.
|
||||||
|
// TODO: consider using KeyAlgoRSASHA256 as default.
|
||||||
sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), KeyAlgoRSASHA512)
|
sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), KeyAlgoRSASHA512)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+94
-6
@@ -187,10 +187,30 @@ func TestHostKeyCert(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
addr string
|
addr string
|
||||||
succeed bool
|
succeed bool
|
||||||
|
certSignerAlgorithms []string // Empty means no algorithm restrictions.
|
||||||
|
clientHostKeyAlgorithms []string
|
||||||
}{
|
}{
|
||||||
{addr: "hostname:22", succeed: true},
|
{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: "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},
|
{addr: "lasthost:22", succeed: false},
|
||||||
} {
|
} {
|
||||||
@@ -207,14 +227,24 @@ func TestHostKeyCert(t *testing.T) {
|
|||||||
conf := ServerConfig{
|
conf := ServerConfig{
|
||||||
NoClientAuth: true,
|
NoClientAuth: true,
|
||||||
}
|
}
|
||||||
conf.AddHostKey(certSigner)
|
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)
|
_, _, _, err := NewServerConn(c1, &conf)
|
||||||
errc <- err
|
errc <- err
|
||||||
}()
|
}()
|
||||||
|
|
||||||
config := &ClientConfig{
|
config := &ClientConfig{
|
||||||
User: "user",
|
User: "user",
|
||||||
HostKeyCallback: checker.CheckHostKey,
|
HostKeyCallback: checker.CheckHostKey,
|
||||||
|
HostKeyAlgorithms: test.clientHostKeyAlgorithms,
|
||||||
}
|
}
|
||||||
_, _, _, err = NewClientConn(c2, test.addr, config)
|
_, _, _, err = NewClientConn(c2, test.addr, config)
|
||||||
|
|
||||||
@@ -242,6 +272,20 @@ func (s *legacyRSASigner) Sign(rand io.Reader, data []byte) (*Signature, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCertTypes(t *testing.T) {
|
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 {
|
var testVars = []struct {
|
||||||
name string
|
name string
|
||||||
signer Signer
|
signer Signer
|
||||||
@@ -251,8 +295,10 @@ func TestCertTypes(t *testing.T) {
|
|||||||
{CertAlgoECDSA384v01, testSigners["ecdsap384"], ""},
|
{CertAlgoECDSA384v01, testSigners["ecdsap384"], ""},
|
||||||
{CertAlgoECDSA521v01, testSigners["ecdsap521"], ""},
|
{CertAlgoECDSA521v01, testSigners["ecdsap521"], ""},
|
||||||
{CertAlgoED25519v01, testSigners["ed25519"], ""},
|
{CertAlgoED25519v01, testSigners["ed25519"], ""},
|
||||||
{CertAlgoRSAv01, testSigners["rsa"], KeyAlgoRSASHA512},
|
{CertAlgoRSAv01, testSigners["rsa"], KeyAlgoRSASHA256},
|
||||||
{"legacyRSASigner", &legacyRSASigner{testSigners["rsa"]}, KeyAlgoRSA},
|
{"legacyRSASigner", &legacyRSASigner{testSigners["rsa"]}, KeyAlgoRSA},
|
||||||
|
{"multiAlgoRSASignerSHA256", multiAlgoSignerSHA256, KeyAlgoRSASHA256},
|
||||||
|
{"multiAlgoRSASignerSHA512", multiAlgoSignerSHA512, KeyAlgoRSASHA512},
|
||||||
{CertAlgoDSAv01, testSigners["dsa"], ""},
|
{CertAlgoDSAv01, testSigners["dsa"], ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,3 +364,45 @@ func TestCertTypes(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCertSignWithMultiAlgorithmSigner(t *testing.T) {
|
||||||
|
type testcase struct {
|
||||||
|
sigAlgo string
|
||||||
|
algoritms []string
|
||||||
|
}
|
||||||
|
cases := []testcase{
|
||||||
|
{
|
||||||
|
sigAlgo: KeyAlgoRSA,
|
||||||
|
algoritms: []string{KeyAlgoRSA, KeyAlgoRSASHA512},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sigAlgo: KeyAlgoRSASHA256,
|
||||||
|
algoritms: []string{KeyAlgoRSASHA256, KeyAlgoRSA, KeyAlgoRSASHA512},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
sigAlgo: KeyAlgoRSASHA512,
|
||||||
|
algoritms: []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.algoritms)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+66
-30
@@ -71,7 +71,9 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
|||||||
for auth := AuthMethod(new(noneAuth)); auth != nil; {
|
for auth := AuthMethod(new(noneAuth)); auth != nil; {
|
||||||
ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions)
|
ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
// We return the error later if there is no other method left to
|
||||||
|
// try.
|
||||||
|
ok = authFailure
|
||||||
}
|
}
|
||||||
if ok == authSuccess {
|
if ok == authSuccess {
|
||||||
// success
|
// success
|
||||||
@@ -101,6 +103,12 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if auth == nil && err != nil {
|
||||||
|
// We have an error and there are no other authentication methods to
|
||||||
|
// try, so we return it.
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
|
return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
|
||||||
}
|
}
|
||||||
@@ -217,21 +225,45 @@ func (cb publicKeyCallback) method() string {
|
|||||||
return "publickey"
|
return "publickey"
|
||||||
}
|
}
|
||||||
|
|
||||||
func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (as AlgorithmSigner, algo string) {
|
func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (MultiAlgorithmSigner, string, error) {
|
||||||
|
var as MultiAlgorithmSigner
|
||||||
keyFormat := signer.PublicKey().Type()
|
keyFormat := signer.PublicKey().Type()
|
||||||
|
|
||||||
// Like in sendKexInit, if the public key implements AlgorithmSigner we
|
// If the signer implements MultiAlgorithmSigner we use the algorithms it
|
||||||
// assume it supports all algorithms, otherwise only the key format one.
|
// support, if it implements AlgorithmSigner we assume it supports all
|
||||||
as, ok := signer.(AlgorithmSigner)
|
// algorithms, otherwise only the key format one.
|
||||||
if !ok {
|
switch s := signer.(type) {
|
||||||
return algorithmSignerWrapper{signer}, keyFormat
|
case MultiAlgorithmSigner:
|
||||||
|
as = s
|
||||||
|
case AlgorithmSigner:
|
||||||
|
as = &multiAlgorithmSigner{
|
||||||
|
AlgorithmSigner: s,
|
||||||
|
supportedAlgorithms: algorithmsForKeyFormat(underlyingAlgo(keyFormat)),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
as = &multiAlgorithmSigner{
|
||||||
|
AlgorithmSigner: algorithmSignerWrapper{signer},
|
||||||
|
supportedAlgorithms: []string{underlyingAlgo(keyFormat)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFallbackAlgo := func() (string, error) {
|
||||||
|
// Fallback to use if there is no "server-sig-algs" extension or a
|
||||||
|
// common algorithm cannot be found. We use the public key format if the
|
||||||
|
// MultiAlgorithmSigner supports it, otherwise we return an error.
|
||||||
|
if !contains(as.Algorithms(), underlyingAlgo(keyFormat)) {
|
||||||
|
return "", fmt.Errorf("ssh: no common public key signature algorithm, server only supports %q for key type %q, signer only supports %v",
|
||||||
|
underlyingAlgo(keyFormat), keyFormat, as.Algorithms())
|
||||||
|
}
|
||||||
|
return keyFormat, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
extPayload, ok := extensions["server-sig-algs"]
|
extPayload, ok := extensions["server-sig-algs"]
|
||||||
if !ok {
|
if !ok {
|
||||||
// If there is no "server-sig-algs" extension, fall back to the key
|
// If there is no "server-sig-algs" extension use the fallback
|
||||||
// format algorithm.
|
// algorithm.
|
||||||
return as, keyFormat
|
algo, err := getFallbackAlgo()
|
||||||
|
return as, algo, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// The server-sig-algs extension only carries underlying signature
|
// The server-sig-algs extension only carries underlying signature
|
||||||
@@ -245,15 +277,22 @@ func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (as Alg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keyAlgos := algorithmsForKeyFormat(keyFormat)
|
// Filter algorithms based on those supported by MultiAlgorithmSigner.
|
||||||
|
var keyAlgos []string
|
||||||
|
for _, algo := range algorithmsForKeyFormat(keyFormat) {
|
||||||
|
if contains(as.Algorithms(), underlyingAlgo(algo)) {
|
||||||
|
keyAlgos = append(keyAlgos, algo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos)
|
algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If there is no overlap, try the key anyway with the key format
|
// If there is no overlap, return the fallback algorithm to support
|
||||||
// algorithm, to support servers that fail to list all supported
|
// servers that fail to list all supported algorithms.
|
||||||
// algorithms.
|
algo, err := getFallbackAlgo()
|
||||||
return as, keyFormat
|
return as, algo, err
|
||||||
}
|
}
|
||||||
return as, algo
|
return as, algo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) {
|
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) {
|
||||||
@@ -267,10 +306,17 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
|||||||
return authFailure, nil, err
|
return authFailure, nil, err
|
||||||
}
|
}
|
||||||
var methods []string
|
var methods []string
|
||||||
|
var errSigAlgo error
|
||||||
for _, signer := range signers {
|
for _, signer := range signers {
|
||||||
pub := signer.PublicKey()
|
pub := signer.PublicKey()
|
||||||
as, algo := pickSignatureAlgorithm(signer, extensions)
|
as, algo, err := pickSignatureAlgorithm(signer, extensions)
|
||||||
|
if err != nil && errSigAlgo == nil {
|
||||||
|
// If we cannot negotiate a signature algorithm store the first
|
||||||
|
// error so we can return it to provide a more meaningful message if
|
||||||
|
// no other signers work.
|
||||||
|
errSigAlgo = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
ok, err := validateKey(pub, algo, user, c)
|
ok, err := validateKey(pub, algo, user, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return authFailure, nil, err
|
return authFailure, nil, err
|
||||||
@@ -317,22 +363,12 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
|||||||
// contain the "publickey" method, do not attempt to authenticate with any
|
// contain the "publickey" method, do not attempt to authenticate with any
|
||||||
// other keys. According to RFC 4252 Section 7, the latter can occur when
|
// other keys. According to RFC 4252 Section 7, the latter can occur when
|
||||||
// additional authentication methods are required.
|
// additional authentication methods are required.
|
||||||
if success == authSuccess || !containsMethod(methods, cb.method()) {
|
if success == authSuccess || !contains(methods, cb.method()) {
|
||||||
return success, methods, err
|
return success, methods, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return authFailure, methods, nil
|
return authFailure, methods, errSigAlgo
|
||||||
}
|
|
||||||
|
|
||||||
func containsMethod(methods []string, method string) bool {
|
|
||||||
for _, m := range methods {
|
|
||||||
if m == method {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateKey validates the key provided is acceptable to the server.
|
// validateKey validates the key provided is acceptable to the server.
|
||||||
|
|||||||
@@ -955,3 +955,330 @@ func TestAuthMethodGSSAPIWithMIC(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCompatibleAlgoAndSignatures(t *testing.T) {
|
||||||
|
type testcase struct {
|
||||||
|
algo string
|
||||||
|
sigFormat string
|
||||||
|
compatible bool
|
||||||
|
}
|
||||||
|
testcases := []*testcase{
|
||||||
|
{
|
||||||
|
KeyAlgoRSA,
|
||||||
|
KeyAlgoRSA,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoRSA,
|
||||||
|
KeyAlgoRSASHA256,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoRSA,
|
||||||
|
KeyAlgoRSASHA512,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoRSASHA256,
|
||||||
|
KeyAlgoRSA,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoRSASHA512,
|
||||||
|
KeyAlgoRSA,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoRSASHA512,
|
||||||
|
KeyAlgoRSASHA256,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoRSASHA256,
|
||||||
|
KeyAlgoRSASHA512,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoRSASHA512,
|
||||||
|
KeyAlgoRSASHA512,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertAlgoRSAv01,
|
||||||
|
KeyAlgoRSA,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertAlgoRSAv01,
|
||||||
|
KeyAlgoRSASHA256,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertAlgoRSAv01,
|
||||||
|
KeyAlgoRSASHA512,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertAlgoRSASHA256v01,
|
||||||
|
KeyAlgoRSASHA512,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertAlgoRSASHA512v01,
|
||||||
|
KeyAlgoRSASHA512,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertAlgoRSASHA512v01,
|
||||||
|
KeyAlgoRSASHA256,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertAlgoRSASHA256v01,
|
||||||
|
CertAlgoRSAv01,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertAlgoRSAv01,
|
||||||
|
CertAlgoRSASHA512v01,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoECDSA256,
|
||||||
|
KeyAlgoRSA,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoECDSA256,
|
||||||
|
KeyAlgoECDSA521,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoECDSA256,
|
||||||
|
KeyAlgoECDSA256,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoECDSA256,
|
||||||
|
KeyAlgoED25519,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
KeyAlgoED25519,
|
||||||
|
KeyAlgoED25519,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range testcases {
|
||||||
|
if isAlgoCompatible(c.algo, c.sigFormat) != c.compatible {
|
||||||
|
t.Errorf("algorithm %q, signature format %q, expected compatible to be %t", c.algo, c.sigFormat, c.compatible)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPickSignatureAlgorithm(t *testing.T) {
|
||||||
|
type testcase struct {
|
||||||
|
name string
|
||||||
|
extensions map[string][]byte
|
||||||
|
}
|
||||||
|
cases := []testcase{
|
||||||
|
{
|
||||||
|
name: "server with empty server-sig-algs",
|
||||||
|
extensions: map[string][]byte{
|
||||||
|
"server-sig-algs": []byte(``),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "server with no server-sig-algs",
|
||||||
|
extensions: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
signer, ok := testSigners["rsa"].(MultiAlgorithmSigner)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("rsa test signer does not implement the MultiAlgorithmSigner interface")
|
||||||
|
}
|
||||||
|
// The signer supports the public key algorithm which is then returned.
|
||||||
|
_, algo, err := pickSignatureAlgorithm(signer, c.extensions)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("got %v, want no error", err)
|
||||||
|
}
|
||||||
|
if algo != signer.PublicKey().Type() {
|
||||||
|
t.Fatalf("got algo %q, want %q", algo, signer.PublicKey().Type())
|
||||||
|
}
|
||||||
|
// Test a signer that uses a certificate algorithm as the public key
|
||||||
|
// type.
|
||||||
|
cert := &Certificate{
|
||||||
|
CertType: UserCert,
|
||||||
|
Key: signer.PublicKey(),
|
||||||
|
}
|
||||||
|
cert.SignCert(rand.Reader, signer)
|
||||||
|
|
||||||
|
certSigner, err := NewCertSigner(cert, signer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error generating cert signer: %v", err)
|
||||||
|
}
|
||||||
|
// The signer supports the public key algorithm and the
|
||||||
|
// public key format is a certificate type so the cerificate
|
||||||
|
// algorithm matching the key format must be returned
|
||||||
|
_, algo, err = pickSignatureAlgorithm(certSigner, c.extensions)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("got %v, want no error", err)
|
||||||
|
}
|
||||||
|
if algo != certSigner.PublicKey().Type() {
|
||||||
|
t.Fatalf("got algo %q, want %q", algo, certSigner.PublicKey().Type())
|
||||||
|
}
|
||||||
|
signer, err = NewSignerWithAlgorithms(signer.(AlgorithmSigner), []string{KeyAlgoRSASHA512, KeyAlgoRSASHA256})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create signer with algorithms: %v", err)
|
||||||
|
}
|
||||||
|
// The signer does not support the public key algorithm so an error
|
||||||
|
// is returned.
|
||||||
|
_, _, err = pickSignatureAlgorithm(signer, c.extensions)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("got no error, no common public key signature algorithm error expected")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// configurablePublicKeyCallback is a public key callback that allows to
|
||||||
|
// configure the signature algorithm and format. This way we can emulate the
|
||||||
|
// behavior of buggy clients.
|
||||||
|
type configurablePublicKeyCallback struct {
|
||||||
|
signer AlgorithmSigner
|
||||||
|
signatureAlgo string
|
||||||
|
signatureFormat string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cb configurablePublicKeyCallback) method() string {
|
||||||
|
return "publickey"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cb configurablePublicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) {
|
||||||
|
pub := cb.signer.PublicKey()
|
||||||
|
|
||||||
|
ok, err := validateKey(pub, cb.signatureAlgo, user, c)
|
||||||
|
if err != nil {
|
||||||
|
return authFailure, nil, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return authFailure, nil, fmt.Errorf("invalid public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey := pub.Marshal()
|
||||||
|
data := buildDataSignedForAuth(session, userAuthRequestMsg{
|
||||||
|
User: user,
|
||||||
|
Service: serviceSSH,
|
||||||
|
Method: cb.method(),
|
||||||
|
}, cb.signatureAlgo, pubKey)
|
||||||
|
sign, err := cb.signer.SignWithAlgorithm(rand, data, underlyingAlgo(cb.signatureFormat))
|
||||||
|
if err != nil {
|
||||||
|
return authFailure, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := Marshal(sign)
|
||||||
|
sig := make([]byte, stringLength(len(s)))
|
||||||
|
marshalString(sig, s)
|
||||||
|
msg := publickeyAuthMsg{
|
||||||
|
User: user,
|
||||||
|
Service: serviceSSH,
|
||||||
|
Method: cb.method(),
|
||||||
|
HasSig: true,
|
||||||
|
Algoname: cb.signatureAlgo,
|
||||||
|
PubKey: pubKey,
|
||||||
|
Sig: sig,
|
||||||
|
}
|
||||||
|
p := Marshal(&msg)
|
||||||
|
if err := c.writePacket(p); err != nil {
|
||||||
|
return authFailure, nil, err
|
||||||
|
}
|
||||||
|
var success authResult
|
||||||
|
success, methods, err := handleAuthResponse(c)
|
||||||
|
if err != nil {
|
||||||
|
return authFailure, nil, err
|
||||||
|
}
|
||||||
|
if success == authSuccess || !contains(methods, cb.method()) {
|
||||||
|
return success, methods, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return authFailure, methods, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPublicKeyAndAlgoCompatibility(t *testing.T) {
|
||||||
|
cert := &Certificate{
|
||||||
|
Key: testPublicKeys["rsa"],
|
||||||
|
ValidBefore: CertTimeInfinity,
|
||||||
|
CertType: UserCert,
|
||||||
|
}
|
||||||
|
cert.SignCert(rand.Reader, testSigners["ecdsa"])
|
||||||
|
certSigner, err := NewCertSigner(cert, testSigners["rsa"])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewCertSigner: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConfig := &ClientConfig{
|
||||||
|
User: "user",
|
||||||
|
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||||
|
Auth: []AuthMethod{
|
||||||
|
configurablePublicKeyCallback{
|
||||||
|
signer: certSigner.(AlgorithmSigner),
|
||||||
|
signatureAlgo: KeyAlgoRSASHA256,
|
||||||
|
signatureFormat: KeyAlgoRSASHA256,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := tryAuth(t, clientConfig); err == nil {
|
||||||
|
t.Error("cert login passed with incompatible public key type and algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientAuthGPGAgentCompat(t *testing.T) {
|
||||||
|
clientConfig := &ClientConfig{
|
||||||
|
User: "testuser",
|
||||||
|
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||||
|
Auth: []AuthMethod{
|
||||||
|
// algorithm rsa-sha2-512 and signature format ssh-rsa.
|
||||||
|
configurablePublicKeyCallback{
|
||||||
|
signer: testSigners["rsa"].(AlgorithmSigner),
|
||||||
|
signatureAlgo: KeyAlgoRSASHA512,
|
||||||
|
signatureFormat: KeyAlgoRSA,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := tryAuth(t, clientConfig); err != nil {
|
||||||
|
t.Fatalf("unable to dial remote side: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCertAuthOpenSSHCompat(t *testing.T) {
|
||||||
|
cert := &Certificate{
|
||||||
|
Key: testPublicKeys["rsa"],
|
||||||
|
ValidBefore: CertTimeInfinity,
|
||||||
|
CertType: UserCert,
|
||||||
|
}
|
||||||
|
cert.SignCert(rand.Reader, testSigners["ecdsa"])
|
||||||
|
certSigner, err := NewCertSigner(cert, testSigners["rsa"])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewCertSigner: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConfig := &ClientConfig{
|
||||||
|
User: "user",
|
||||||
|
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||||
|
Auth: []AuthMethod{
|
||||||
|
// algorithm ssh-rsa-cert-v01@openssh.com and signature format
|
||||||
|
// rsa-sha2-256.
|
||||||
|
configurablePublicKeyCallback{
|
||||||
|
signer: certSigner.(AlgorithmSigner),
|
||||||
|
signatureAlgo: CertAlgoRSAv01,
|
||||||
|
signatureFormat: KeyAlgoRSASHA256,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := tryAuth(t, clientConfig); err != nil {
|
||||||
|
t.Fatalf("unable to dial remote side: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -254,3 +254,93 @@ func TestNewClientConn(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnsupportedAlgorithm(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
config Config
|
||||||
|
wantError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"unsupported KEX",
|
||||||
|
Config{
|
||||||
|
KeyExchanges: []string{"unsupported"},
|
||||||
|
},
|
||||||
|
"no common algorithm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unsupported and supported KEXs",
|
||||||
|
Config{
|
||||||
|
KeyExchanges: []string{"unsupported", kexAlgoCurve25519SHA256},
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unsupported cipher",
|
||||||
|
Config{
|
||||||
|
Ciphers: []string{"unsupported"},
|
||||||
|
},
|
||||||
|
"no common algorithm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unsupported and supported ciphers",
|
||||||
|
Config{
|
||||||
|
Ciphers: []string{"unsupported", chacha20Poly1305ID},
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unsupported MAC",
|
||||||
|
Config{
|
||||||
|
MACs: []string{"unsupported"},
|
||||||
|
// MAC is used for non AAED ciphers.
|
||||||
|
Ciphers: []string{"aes256-ctr"},
|
||||||
|
},
|
||||||
|
"no common algorithm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unsupported and supported MACs",
|
||||||
|
Config{
|
||||||
|
MACs: []string{"unsupported", "hmac-sha2-256-etm@openssh.com"},
|
||||||
|
// MAC is used for non AAED ciphers.
|
||||||
|
Ciphers: []string{"aes256-ctr"},
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c1, c2, err := netPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("netPipe: %v", err)
|
||||||
|
}
|
||||||
|
defer c1.Close()
|
||||||
|
defer c2.Close()
|
||||||
|
|
||||||
|
serverConf := &ServerConfig{
|
||||||
|
Config: tt.config,
|
||||||
|
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||||
|
return &Permissions{}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
serverConf.AddHostKey(testSigners["rsa"])
|
||||||
|
go NewServerConn(c1, serverConf)
|
||||||
|
|
||||||
|
clientConf := &ClientConfig{
|
||||||
|
User: "testuser",
|
||||||
|
Config: tt.config,
|
||||||
|
Auth: []AuthMethod{
|
||||||
|
Password("testpw"),
|
||||||
|
},
|
||||||
|
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||||
|
}
|
||||||
|
_, _, _, err = NewClientConn(c2, "", clientConf)
|
||||||
|
if err != nil {
|
||||||
|
if tt.wantError == "" || !strings.Contains(err.Error(), tt.wantError) {
|
||||||
|
t.Errorf("%s: got error %q, missing %q", tt.name, err.Error(), tt.wantError)
|
||||||
|
}
|
||||||
|
} else if tt.wantError != "" {
|
||||||
|
t.Errorf("%s: succeeded, but want error string %q", tt.name, tt.wantError)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+38
-13
@@ -49,7 +49,8 @@ var supportedKexAlgos = []string{
|
|||||||
// P384 and P521 are not constant-time yet, but since we don't
|
// P384 and P521 are not constant-time yet, but since we don't
|
||||||
// reuse ephemeral keys, using them for ECDH should be OK.
|
// reuse ephemeral keys, using them for ECDH should be OK.
|
||||||
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
|
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
|
||||||
kexAlgoDH14SHA256, kexAlgoDH14SHA1, kexAlgoDH1SHA1,
|
kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
|
||||||
|
kexAlgoDH1SHA1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
|
// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
|
||||||
@@ -59,8 +60,9 @@ var serverForbiddenKexAlgos = map[string]struct{}{
|
|||||||
kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
|
kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
|
||||||
}
|
}
|
||||||
|
|
||||||
// preferredKexAlgos specifies the default preference for key-exchange algorithms
|
// preferredKexAlgos specifies the default preference for key-exchange
|
||||||
// in preference order.
|
// algorithms in preference order. The diffie-hellman-group16-sha512 algorithm
|
||||||
|
// is disabled by default because it is a bit slower than the others.
|
||||||
var preferredKexAlgos = []string{
|
var preferredKexAlgos = []string{
|
||||||
kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
|
kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
|
||||||
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
|
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
|
||||||
@@ -70,12 +72,12 @@ var preferredKexAlgos = []string{
|
|||||||
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
|
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
|
||||||
// of authenticating servers) in preference order.
|
// of authenticating servers) in preference order.
|
||||||
var supportedHostKeyAlgos = []string{
|
var supportedHostKeyAlgos = []string{
|
||||||
CertAlgoRSASHA512v01, CertAlgoRSASHA256v01,
|
CertAlgoRSASHA256v01, CertAlgoRSASHA512v01,
|
||||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
||||||
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
|
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
|
||||||
|
|
||||||
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||||
KeyAlgoRSASHA512, KeyAlgoRSASHA256,
|
KeyAlgoRSASHA256, KeyAlgoRSASHA512,
|
||||||
KeyAlgoRSA, KeyAlgoDSA,
|
KeyAlgoRSA, KeyAlgoDSA,
|
||||||
|
|
||||||
KeyAlgoED25519,
|
KeyAlgoED25519,
|
||||||
@@ -85,7 +87,7 @@ var supportedHostKeyAlgos = []string{
|
|||||||
// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
|
// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
|
||||||
// because they have reached the end of their useful life.
|
// because they have reached the end of their useful life.
|
||||||
var supportedMACs = []string{
|
var supportedMACs = []string{
|
||||||
"hmac-sha2-512-etm@openssh.com", "hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96",
|
"hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96",
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportedCompressions = []string{compressionNone}
|
var supportedCompressions = []string{compressionNone}
|
||||||
@@ -119,6 +121,13 @@ func algorithmsForKeyFormat(keyFormat string) []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isRSA returns whether algo is a supported RSA algorithm, including certificate
|
||||||
|
// algorithms.
|
||||||
|
func isRSA(algo string) bool {
|
||||||
|
algos := algorithmsForKeyFormat(KeyAlgoRSA)
|
||||||
|
return contains(algos, underlyingAlgo(algo))
|
||||||
|
}
|
||||||
|
|
||||||
// supportedPubKeyAuthAlgos specifies the supported client public key
|
// supportedPubKeyAuthAlgos specifies the supported client public key
|
||||||
// authentication algorithms. Note that this doesn't include certificate types
|
// authentication algorithms. Note that this doesn't include certificate types
|
||||||
// since those use the underlying algorithm. This list is sent to the client if
|
// since those use the underlying algorithm. This list is sent to the client if
|
||||||
@@ -262,16 +271,16 @@ type Config struct {
|
|||||||
// unspecified, a size suitable for the chosen cipher is used.
|
// unspecified, a size suitable for the chosen cipher is used.
|
||||||
RekeyThreshold uint64
|
RekeyThreshold uint64
|
||||||
|
|
||||||
// The allowed key exchanges algorithms. If unspecified then a
|
// The allowed key exchanges algorithms. If unspecified then a default set
|
||||||
// default set of algorithms is used.
|
// of algorithms is used. Unsupported values are silently ignored.
|
||||||
KeyExchanges []string
|
KeyExchanges []string
|
||||||
|
|
||||||
// The allowed cipher algorithms. If unspecified then a sensible
|
// The allowed cipher algorithms. If unspecified then a sensible default is
|
||||||
// default is used.
|
// used. Unsupported values are silently ignored.
|
||||||
Ciphers []string
|
Ciphers []string
|
||||||
|
|
||||||
// The allowed MAC algorithms. If unspecified then a sensible default
|
// The allowed MAC algorithms. If unspecified then a sensible default is
|
||||||
// is used.
|
// used. Unsupported values are silently ignored.
|
||||||
MACs []string
|
MACs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +297,7 @@ func (c *Config) SetDefaults() {
|
|||||||
var ciphers []string
|
var ciphers []string
|
||||||
for _, c := range c.Ciphers {
|
for _, c := range c.Ciphers {
|
||||||
if cipherModes[c] != nil {
|
if cipherModes[c] != nil {
|
||||||
// reject the cipher if we have no cipherModes definition
|
// Ignore the cipher if we have no cipherModes definition.
|
||||||
ciphers = append(ciphers, c)
|
ciphers = append(ciphers, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,10 +306,26 @@ func (c *Config) SetDefaults() {
|
|||||||
if c.KeyExchanges == nil {
|
if c.KeyExchanges == nil {
|
||||||
c.KeyExchanges = preferredKexAlgos
|
c.KeyExchanges = preferredKexAlgos
|
||||||
}
|
}
|
||||||
|
var kexs []string
|
||||||
|
for _, k := range c.KeyExchanges {
|
||||||
|
if kexAlgoMap[k] != nil {
|
||||||
|
// Ignore the KEX if we have no kexAlgoMap definition.
|
||||||
|
kexs = append(kexs, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.KeyExchanges = kexs
|
||||||
|
|
||||||
if c.MACs == nil {
|
if c.MACs == nil {
|
||||||
c.MACs = supportedMACs
|
c.MACs = supportedMACs
|
||||||
}
|
}
|
||||||
|
var macs []string
|
||||||
|
for _, m := range c.MACs {
|
||||||
|
if macModes[m] != nil {
|
||||||
|
// Ignore the MAC if we have no macModes definition.
|
||||||
|
macs = append(macs, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.MACs = macs
|
||||||
|
|
||||||
if c.RekeyThreshold == 0 {
|
if c.RekeyThreshold == 0 {
|
||||||
// cipher specific default
|
// cipher specific default
|
||||||
|
|||||||
+5
-5
@@ -82,11 +82,11 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cases := []testcase{
|
cases := []testcase{
|
||||||
testcase{
|
{
|
||||||
name: "standard",
|
name: "standard",
|
||||||
},
|
},
|
||||||
|
|
||||||
testcase{
|
{
|
||||||
name: "no common hostkey",
|
name: "no common hostkey",
|
||||||
serverIn: kexInitMsg{
|
serverIn: kexInitMsg{
|
||||||
ServerHostKeyAlgos: []string{"hostkey2"},
|
ServerHostKeyAlgos: []string{"hostkey2"},
|
||||||
@@ -94,7 +94,7 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
testcase{
|
{
|
||||||
name: "no common kex",
|
name: "no common kex",
|
||||||
serverIn: kexInitMsg{
|
serverIn: kexInitMsg{
|
||||||
KexAlgos: []string{"kex2"},
|
KexAlgos: []string{"kex2"},
|
||||||
@@ -102,7 +102,7 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
testcase{
|
{
|
||||||
name: "no common cipher",
|
name: "no common cipher",
|
||||||
serverIn: kexInitMsg{
|
serverIn: kexInitMsg{
|
||||||
CiphersClientServer: []string{"cipher2"},
|
CiphersClientServer: []string{"cipher2"},
|
||||||
@@ -110,7 +110,7 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
testcase{
|
{
|
||||||
name: "client decides cipher",
|
name: "client decides cipher",
|
||||||
serverIn: kexInitMsg{
|
serverIn: kexInitMsg{
|
||||||
CiphersClientServer: []string{"cipher1", "cipher2"},
|
CiphersClientServer: []string{"cipher1", "cipher2"},
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ others.
|
|||||||
|
|
||||||
References:
|
References:
|
||||||
|
|
||||||
|
[PROTOCOL]: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL?rev=HEAD
|
||||||
[PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
|
[PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
|
||||||
[SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
|
[SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
|
||||||
|
|
||||||
|
|||||||
+67
-1
@@ -7,6 +7,8 @@ package ssh_test
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@@ -75,7 +77,6 @@ func ExampleNewServerConn() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to parse private key: ", err)
|
log.Fatal("Failed to parse private key: ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.AddHostKey(private)
|
config.AddHostKey(private)
|
||||||
|
|
||||||
// Once a ServerConfig has been configured, connections can be
|
// Once a ServerConfig has been configured, connections can be
|
||||||
@@ -139,6 +140,36 @@ func ExampleNewServerConn() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleServerConfig_AddHostKey() {
|
||||||
|
// Minimal ServerConfig supporting only password authentication.
|
||||||
|
config := &ssh.ServerConfig{
|
||||||
|
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
|
||||||
|
// Should use constant-time compare (or better, salt+hash) in
|
||||||
|
// a production setting.
|
||||||
|
if c.User() == "testuser" && string(pass) == "tiger" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("password rejected for %q", c.User())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
privateBytes, err := os.ReadFile("id_rsa")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to load private key: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
private, err := ssh.ParsePrivateKey(privateBytes)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to parse private key: ", err)
|
||||||
|
}
|
||||||
|
// Restrict host key algorithms to disable ssh-rsa.
|
||||||
|
signer, err := ssh.NewSignerWithAlgorithms(private.(ssh.AlgorithmSigner), []string{ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to create private key with restricted algorithms: ", err)
|
||||||
|
}
|
||||||
|
config.AddHostKey(signer)
|
||||||
|
}
|
||||||
|
|
||||||
func ExampleClientConfig_HostKeyCallback() {
|
func ExampleClientConfig_HostKeyCallback() {
|
||||||
// Every client must provide a host key check. Here is a
|
// Every client must provide a host key check. Here is a
|
||||||
// simple-minded parse of OpenSSH's known_hosts file
|
// simple-minded parse of OpenSSH's known_hosts file
|
||||||
@@ -318,3 +349,38 @@ func ExampleSession_RequestPty() {
|
|||||||
log.Fatal("failed to start shell: ", err)
|
log.Fatal("failed to start shell: ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleCertificate_SignCert() {
|
||||||
|
// Sign a certificate with a specific algorithm.
|
||||||
|
privateKey, err := rsa.GenerateKey(rand.Reader, 3072)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("unable to generate RSA key: ", err)
|
||||||
|
}
|
||||||
|
publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("unable to get RSA public key: ", err)
|
||||||
|
}
|
||||||
|
caKey, err := rsa.GenerateKey(rand.Reader, 3072)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("unable to generate CA key: ", err)
|
||||||
|
}
|
||||||
|
signer, err := ssh.NewSignerFromKey(caKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("unable to generate signer from key: ", err)
|
||||||
|
}
|
||||||
|
mas, err := ssh.NewSignerWithAlgorithms(signer.(ssh.AlgorithmSigner), []string{ssh.KeyAlgoRSASHA256})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("unable to create signer with algoritms: ", err)
|
||||||
|
}
|
||||||
|
certificate := ssh.Certificate{
|
||||||
|
Key: publicKey,
|
||||||
|
CertType: ssh.UserCert,
|
||||||
|
}
|
||||||
|
if err := certificate.SignCert(rand.Reader, mas); err != nil {
|
||||||
|
log.Fatal("unable to sign certificate: ", err)
|
||||||
|
}
|
||||||
|
// Save the public key to a file and check that rsa-sha-256 is used for
|
||||||
|
// signing:
|
||||||
|
// ssh-keygen -L -f <path to the file>
|
||||||
|
fmt.Println(string(ssh.MarshalAuthorizedKey(&certificate)))
|
||||||
|
}
|
||||||
|
|||||||
+30
-14
@@ -461,19 +461,24 @@ func (t *handshakeTransport) sendKexInit() error {
|
|||||||
isServer := len(t.hostKeys) > 0
|
isServer := len(t.hostKeys) > 0
|
||||||
if isServer {
|
if isServer {
|
||||||
for _, k := range t.hostKeys {
|
for _, k := range t.hostKeys {
|
||||||
// If k is an AlgorithmSigner, presume it supports all signature algorithms
|
// If k is a MultiAlgorithmSigner, we restrict the signature
|
||||||
// associated with the key format. (Ideally AlgorithmSigner would have a
|
// algorithms. If k is a AlgorithmSigner, presume it supports all
|
||||||
// method to advertise supported algorithms, but it doesn't. This means that
|
// signature algorithms associated with the key format. If k is not
|
||||||
// adding support for a new algorithm is a breaking change, as we will
|
// an AlgorithmSigner, we can only assume it only supports the
|
||||||
// immediately negotiate it even if existing implementations don't support
|
// algorithms that matches the key format. (This means that Sign
|
||||||
// it. If that ever happens, we'll have to figure something out.)
|
// can't pick a different default).
|
||||||
// If k is not an AlgorithmSigner, we can only assume it only supports the
|
|
||||||
// algorithms that matches the key format. (This means that Sign can't pick
|
|
||||||
// a different default.)
|
|
||||||
keyFormat := k.PublicKey().Type()
|
keyFormat := k.PublicKey().Type()
|
||||||
if _, ok := k.(AlgorithmSigner); ok {
|
|
||||||
|
switch s := k.(type) {
|
||||||
|
case MultiAlgorithmSigner:
|
||||||
|
for _, algo := range algorithmsForKeyFormat(keyFormat) {
|
||||||
|
if contains(s.Algorithms(), underlyingAlgo(algo)) {
|
||||||
|
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case AlgorithmSigner:
|
||||||
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algorithmsForKeyFormat(keyFormat)...)
|
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algorithmsForKeyFormat(keyFormat)...)
|
||||||
} else {
|
default:
|
||||||
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat)
|
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -642,16 +647,20 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||||||
|
|
||||||
// On the server side, after the first SSH_MSG_NEWKEYS, send a SSH_MSG_EXT_INFO
|
// On the server side, after the first SSH_MSG_NEWKEYS, send a SSH_MSG_EXT_INFO
|
||||||
// message with the server-sig-algs extension if the client supports it. See
|
// message with the server-sig-algs extension if the client supports it. See
|
||||||
// RFC 8308, Sections 2.4 and 3.1.
|
// RFC 8308, Sections 2.4 and 3.1, and [PROTOCOL], Section 1.9.
|
||||||
if !isClient && firstKeyExchange && contains(clientInit.KexAlgos, "ext-info-c") {
|
if !isClient && firstKeyExchange && contains(clientInit.KexAlgos, "ext-info-c") {
|
||||||
extInfo := &extInfoMsg{
|
extInfo := &extInfoMsg{
|
||||||
NumExtensions: 1,
|
NumExtensions: 2,
|
||||||
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)),
|
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)+4+16+4+1),
|
||||||
}
|
}
|
||||||
extInfo.Payload = appendInt(extInfo.Payload, len("server-sig-algs"))
|
extInfo.Payload = appendInt(extInfo.Payload, len("server-sig-algs"))
|
||||||
extInfo.Payload = append(extInfo.Payload, "server-sig-algs"...)
|
extInfo.Payload = append(extInfo.Payload, "server-sig-algs"...)
|
||||||
extInfo.Payload = appendInt(extInfo.Payload, len(supportedPubKeyAuthAlgosList))
|
extInfo.Payload = appendInt(extInfo.Payload, len(supportedPubKeyAuthAlgosList))
|
||||||
extInfo.Payload = append(extInfo.Payload, supportedPubKeyAuthAlgosList...)
|
extInfo.Payload = append(extInfo.Payload, supportedPubKeyAuthAlgosList...)
|
||||||
|
extInfo.Payload = appendInt(extInfo.Payload, len("ping@openssh.com"))
|
||||||
|
extInfo.Payload = append(extInfo.Payload, "ping@openssh.com"...)
|
||||||
|
extInfo.Payload = appendInt(extInfo.Payload, 1)
|
||||||
|
extInfo.Payload = append(extInfo.Payload, "0"...)
|
||||||
if err := t.conn.writePacket(Marshal(extInfo)); err != nil {
|
if err := t.conn.writePacket(Marshal(extInfo)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -685,9 +694,16 @@ func (a algorithmSignerWrapper) SignWithAlgorithm(rand io.Reader, data []byte, a
|
|||||||
|
|
||||||
func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner {
|
func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner {
|
||||||
for _, k := range hostKeys {
|
for _, k := range hostKeys {
|
||||||
|
if s, ok := k.(MultiAlgorithmSigner); ok {
|
||||||
|
if !contains(s.Algorithms(), underlyingAlgo(algo)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if algo == k.PublicKey().Type() {
|
if algo == k.PublicKey().Type() {
|
||||||
return algorithmSignerWrapper{k}
|
return algorithmSignerWrapper{k}
|
||||||
}
|
}
|
||||||
|
|
||||||
k, ok := k.(AlgorithmSigner)
|
k, ok := k.(AlgorithmSigner)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
|
|||||||
+107
-15
@@ -148,6 +148,7 @@ func TestHandshakeBasic(t *testing.T) {
|
|||||||
clientDone := make(chan int, 0)
|
clientDone := make(chan int, 0)
|
||||||
gotHalf := make(chan int, 0)
|
gotHalf := make(chan int, 0)
|
||||||
const N = 20
|
const N = 20
|
||||||
|
errorCh := make(chan error, 1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(clientDone)
|
defer close(clientDone)
|
||||||
@@ -158,7 +159,9 @@ func TestHandshakeBasic(t *testing.T) {
|
|||||||
for i := 0; i < N; i++ {
|
for i := 0; i < N; i++ {
|
||||||
p := []byte{msgRequestSuccess, byte(i)}
|
p := []byte{msgRequestSuccess, byte(i)}
|
||||||
if err := trC.writePacket(p); err != nil {
|
if err := trC.writePacket(p); err != nil {
|
||||||
t.Fatalf("sendPacket: %v", err)
|
errorCh <- err
|
||||||
|
trC.Close()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (i % 10) == 5 {
|
if (i % 10) == 5 {
|
||||||
<-gotHalf
|
<-gotHalf
|
||||||
@@ -177,16 +180,15 @@ func TestHandshakeBasic(t *testing.T) {
|
|||||||
checker.waitCall <- 1
|
checker.waitCall <- 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
errorCh <- nil
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Server checks that client messages come in cleanly
|
// Server checks that client messages come in cleanly
|
||||||
i := 0
|
i := 0
|
||||||
err = nil
|
|
||||||
for ; i < N; i++ {
|
for ; i < N; i++ {
|
||||||
var p []byte
|
p, err := trS.readPacket()
|
||||||
p, err = trS.readPacket()
|
if err != nil && err != io.EOF {
|
||||||
if err != nil {
|
t.Fatalf("server error: %v", err)
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if (i % 10) == 5 {
|
if (i % 10) == 5 {
|
||||||
gotHalf <- 1
|
gotHalf <- 1
|
||||||
@@ -198,8 +200,8 @@ func TestHandshakeBasic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
<-clientDone
|
<-clientDone
|
||||||
if err != nil && err != io.EOF {
|
if err := <-errorCh; err != nil {
|
||||||
t.Fatalf("server error: %v", err)
|
t.Fatalf("sendPacket: %v", err)
|
||||||
}
|
}
|
||||||
if i != N {
|
if i != N {
|
||||||
t.Errorf("received %d messages, want 10.", i)
|
t.Errorf("received %d messages, want 10.", i)
|
||||||
@@ -345,16 +347,16 @@ func TestHandshakeAutoRekeyRead(t *testing.T) {
|
|||||||
|
|
||||||
// While we read out the packet, a key change will be
|
// While we read out the packet, a key change will be
|
||||||
// initiated.
|
// initiated.
|
||||||
done := make(chan int, 1)
|
errorCh := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(done)
|
_, err := trC.readPacket()
|
||||||
if _, err := trC.readPacket(); err != nil {
|
errorCh <- err
|
||||||
t.Fatalf("readPacket(client): %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
<-done
|
if err := <-errorCh; err != nil {
|
||||||
|
t.Fatalf("readPacket(client): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
<-sync.called
|
<-sync.called
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,3 +620,93 @@ func TestNoSHA2Support(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultiAlgoSignerHandshake(t *testing.T) {
|
||||||
|
algorithmSigner, ok := testSigners["rsa"].(AlgorithmSigner)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("rsa test signer does not implement the AlgorithmSigner interface")
|
||||||
|
}
|
||||||
|
multiAlgoSigner, err := NewSignerWithAlgorithms(algorithmSigner, []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create multi algorithm signer: %v", err)
|
||||||
|
}
|
||||||
|
c1, c2, err := netPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("netPipe: %v", err)
|
||||||
|
}
|
||||||
|
defer c1.Close()
|
||||||
|
defer c2.Close()
|
||||||
|
|
||||||
|
serverConf := &ServerConfig{
|
||||||
|
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||||
|
return &Permissions{}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
serverConf.AddHostKey(multiAlgoSigner)
|
||||||
|
go NewServerConn(c1, serverConf)
|
||||||
|
|
||||||
|
clientConf := &ClientConfig{
|
||||||
|
User: "test",
|
||||||
|
Auth: []AuthMethod{Password("testpw")},
|
||||||
|
HostKeyCallback: FixedHostKey(testSigners["rsa"].PublicKey()),
|
||||||
|
HostKeyAlgorithms: []string{KeyAlgoRSASHA512},
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, _, err := NewClientConn(c2, "", clientConf); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiAlgoSignerNoCommonHostKeyAlgo(t *testing.T) {
|
||||||
|
algorithmSigner, ok := testSigners["rsa"].(AlgorithmSigner)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("rsa test signer does not implement the AlgorithmSigner interface")
|
||||||
|
}
|
||||||
|
multiAlgoSigner, err := NewSignerWithAlgorithms(algorithmSigner, []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create multi algorithm signer: %v", err)
|
||||||
|
}
|
||||||
|
c1, c2, err := netPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("netPipe: %v", err)
|
||||||
|
}
|
||||||
|
defer c1.Close()
|
||||||
|
defer c2.Close()
|
||||||
|
|
||||||
|
// ssh-rsa is disabled server side
|
||||||
|
serverConf := &ServerConfig{
|
||||||
|
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||||
|
return &Permissions{}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
serverConf.AddHostKey(multiAlgoSigner)
|
||||||
|
go NewServerConn(c1, serverConf)
|
||||||
|
|
||||||
|
// the client only supports ssh-rsa
|
||||||
|
clientConf := &ClientConfig{
|
||||||
|
User: "test",
|
||||||
|
Auth: []AuthMethod{Password("testpw")},
|
||||||
|
HostKeyCallback: FixedHostKey(testSigners["rsa"].PublicKey()),
|
||||||
|
HostKeyAlgorithms: []string{KeyAlgoRSA},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, _, err = NewClientConn(c2, "", clientConf)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded connecting with no common hostkey algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPickIncompatibleHostKeyAlgo(t *testing.T) {
|
||||||
|
algorithmSigner, ok := testSigners["rsa"].(AlgorithmSigner)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("rsa test signer does not implement the AlgorithmSigner interface")
|
||||||
|
}
|
||||||
|
multiAlgoSigner, err := NewSignerWithAlgorithms(algorithmSigner, []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create multi algorithm signer: %v", err)
|
||||||
|
}
|
||||||
|
signer := pickHostKey([]Signer{multiAlgoSigner}, KeyAlgoRSA)
|
||||||
|
if signer != nil {
|
||||||
|
t.Fatal("incompatible signer returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+12
@@ -23,6 +23,7 @@ const (
|
|||||||
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
|
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
|
||||||
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
|
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
|
||||||
kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
|
kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
|
||||||
|
kexAlgoDH16SHA512 = "diffie-hellman-group16-sha512"
|
||||||
kexAlgoECDH256 = "ecdh-sha2-nistp256"
|
kexAlgoECDH256 = "ecdh-sha2-nistp256"
|
||||||
kexAlgoECDH384 = "ecdh-sha2-nistp384"
|
kexAlgoECDH384 = "ecdh-sha2-nistp384"
|
||||||
kexAlgoECDH521 = "ecdh-sha2-nistp521"
|
kexAlgoECDH521 = "ecdh-sha2-nistp521"
|
||||||
@@ -430,6 +431,17 @@ func init() {
|
|||||||
hashFunc: crypto.SHA256,
|
hashFunc: crypto.SHA256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is the group called diffie-hellman-group16-sha512 in RFC
|
||||||
|
// 8268 and Oakley Group 16 in RFC 3526.
|
||||||
|
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", 16)
|
||||||
|
|
||||||
|
kexAlgoMap[kexAlgoDH16SHA512] = &dhGroup{
|
||||||
|
g: new(big.Int).SetInt64(2),
|
||||||
|
p: p,
|
||||||
|
pMinus1: new(big.Int).Sub(p, bigOne),
|
||||||
|
hashFunc: crypto.SHA512,
|
||||||
|
}
|
||||||
|
|
||||||
kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
|
kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
|
||||||
kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
|
kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
|
||||||
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
|
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package ssh
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -63,3 +64,43 @@ func TestKexes(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkKexes(b *testing.B) {
|
||||||
|
type kexResultErr struct {
|
||||||
|
result *kexResult
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, kex := range kexAlgoMap {
|
||||||
|
b.Run(name, func(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
t1, t2 := memPipe()
|
||||||
|
|
||||||
|
s := make(chan kexResultErr, 1)
|
||||||
|
c := make(chan kexResultErr, 1)
|
||||||
|
var magics handshakeMagics
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
r, e := kex.Client(t1, rand.Reader, &magics)
|
||||||
|
t1.Close()
|
||||||
|
c <- kexResultErr{r, e}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
r, e := kex.Server(t2, rand.Reader, &magics, testSigners["ecdsa"].(AlgorithmSigner), testSigners["ecdsa"].PublicKey().Type())
|
||||||
|
t2.Close()
|
||||||
|
s <- kexResultErr{r, e}
|
||||||
|
}()
|
||||||
|
|
||||||
|
clientRes := <-c
|
||||||
|
serverRes := <-s
|
||||||
|
|
||||||
|
if clientRes.err != nil {
|
||||||
|
panic(fmt.Sprintf("client: %v", clientRes.err))
|
||||||
|
}
|
||||||
|
if serverRes.err != nil {
|
||||||
|
panic(fmt.Sprintf("server: %v", serverRes.err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+323
-53
@@ -11,13 +11,16 @@ import (
|
|||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/dsa"
|
"crypto/dsa"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -26,7 +29,6 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
"golang.org/x/crypto/ssh/internal/bcrypt_pbkdf"
|
"golang.org/x/crypto/ssh/internal/bcrypt_pbkdf"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -295,6 +297,18 @@ func MarshalAuthorizedKey(key PublicKey) []byte {
|
|||||||
return b.Bytes()
|
return b.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalPrivateKey returns a PEM block with the private key serialized in the
|
||||||
|
// OpenSSH format.
|
||||||
|
func MarshalPrivateKey(key crypto.PrivateKey, comment string) (*pem.Block, error) {
|
||||||
|
return marshalOpenSSHPrivateKey(key, comment, unencryptedOpenSSHMarshaler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalPrivateKeyWithPassphrase returns a PEM block holding the encrypted
|
||||||
|
// private key serialized in the OpenSSH format.
|
||||||
|
func MarshalPrivateKeyWithPassphrase(key crypto.PrivateKey, comment string, passphrase []byte) (*pem.Block, error) {
|
||||||
|
return marshalOpenSSHPrivateKey(key, comment, passphraseProtectedOpenSSHMarshaler(passphrase))
|
||||||
|
}
|
||||||
|
|
||||||
// PublicKey represents a public key using an unspecified algorithm.
|
// PublicKey represents a public key using an unspecified algorithm.
|
||||||
//
|
//
|
||||||
// Some PublicKeys provided by this package also implement CryptoPublicKey.
|
// Some PublicKeys provided by this package also implement CryptoPublicKey.
|
||||||
@@ -321,7 +335,7 @@ type CryptoPublicKey interface {
|
|||||||
|
|
||||||
// A Signer can create signatures that verify against a public key.
|
// A Signer can create signatures that verify against a public key.
|
||||||
//
|
//
|
||||||
// Some Signers provided by this package also implement AlgorithmSigner.
|
// Some Signers provided by this package also implement MultiAlgorithmSigner.
|
||||||
type Signer interface {
|
type Signer interface {
|
||||||
// PublicKey returns the associated PublicKey.
|
// PublicKey returns the associated PublicKey.
|
||||||
PublicKey() PublicKey
|
PublicKey() PublicKey
|
||||||
@@ -336,9 +350,9 @@ type Signer interface {
|
|||||||
// An AlgorithmSigner is a Signer that also supports specifying an algorithm to
|
// An AlgorithmSigner is a Signer that also supports specifying an algorithm to
|
||||||
// use for signing.
|
// use for signing.
|
||||||
//
|
//
|
||||||
// An AlgorithmSigner can't advertise the algorithms it supports, so it should
|
// An AlgorithmSigner can't advertise the algorithms it supports, unless it also
|
||||||
// be prepared to be invoked with every algorithm supported by the public key
|
// implements MultiAlgorithmSigner, so it should be prepared to be invoked with
|
||||||
// format.
|
// every algorithm supported by the public key format.
|
||||||
type AlgorithmSigner interface {
|
type AlgorithmSigner interface {
|
||||||
Signer
|
Signer
|
||||||
|
|
||||||
@@ -349,6 +363,75 @@ type AlgorithmSigner interface {
|
|||||||
SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error)
|
SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MultiAlgorithmSigner is an AlgorithmSigner that also reports the algorithms
|
||||||
|
// supported by that signer.
|
||||||
|
type MultiAlgorithmSigner interface {
|
||||||
|
AlgorithmSigner
|
||||||
|
|
||||||
|
// Algorithms returns the available algorithms in preference order. The list
|
||||||
|
// must not be empty, and it must not include certificate types.
|
||||||
|
Algorithms() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSignerWithAlgorithms returns a signer restricted to the specified
|
||||||
|
// algorithms. The algorithms must be set in preference order. The list must not
|
||||||
|
// be empty, and it must not include certificate types. An error is returned if
|
||||||
|
// the specified algorithms are incompatible with the public key type.
|
||||||
|
func NewSignerWithAlgorithms(signer AlgorithmSigner, algorithms []string) (MultiAlgorithmSigner, error) {
|
||||||
|
if len(algorithms) == 0 {
|
||||||
|
return nil, errors.New("ssh: please specify at least one valid signing algorithm")
|
||||||
|
}
|
||||||
|
var signerAlgos []string
|
||||||
|
supportedAlgos := algorithmsForKeyFormat(underlyingAlgo(signer.PublicKey().Type()))
|
||||||
|
if s, ok := signer.(*multiAlgorithmSigner); ok {
|
||||||
|
signerAlgos = s.Algorithms()
|
||||||
|
} else {
|
||||||
|
signerAlgos = supportedAlgos
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, algo := range algorithms {
|
||||||
|
if !contains(supportedAlgos, algo) {
|
||||||
|
return nil, fmt.Errorf("ssh: algorithm %q is not supported for key type %q",
|
||||||
|
algo, signer.PublicKey().Type())
|
||||||
|
}
|
||||||
|
if !contains(signerAlgos, algo) {
|
||||||
|
return nil, fmt.Errorf("ssh: algorithm %q is restricted for the provided signer", algo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &multiAlgorithmSigner{
|
||||||
|
AlgorithmSigner: signer,
|
||||||
|
supportedAlgorithms: algorithms,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type multiAlgorithmSigner struct {
|
||||||
|
AlgorithmSigner
|
||||||
|
supportedAlgorithms []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *multiAlgorithmSigner) Algorithms() []string {
|
||||||
|
return s.supportedAlgorithms
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *multiAlgorithmSigner) isAlgorithmSupported(algorithm string) bool {
|
||||||
|
if algorithm == "" {
|
||||||
|
algorithm = underlyingAlgo(s.PublicKey().Type())
|
||||||
|
}
|
||||||
|
for _, algo := range s.supportedAlgorithms {
|
||||||
|
if algorithm == algo {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *multiAlgorithmSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
|
||||||
|
if !s.isAlgorithmSupported(algorithm) {
|
||||||
|
return nil, fmt.Errorf("ssh: algorithm %q is not supported: %v", algorithm, s.supportedAlgorithms)
|
||||||
|
}
|
||||||
|
return s.AlgorithmSigner.SignWithAlgorithm(rand, data, algorithm)
|
||||||
|
}
|
||||||
|
|
||||||
type rsaPublicKey rsa.PublicKey
|
type rsaPublicKey rsa.PublicKey
|
||||||
|
|
||||||
func (r *rsaPublicKey) Type() string {
|
func (r *rsaPublicKey) Type() string {
|
||||||
@@ -512,6 +595,10 @@ func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
|||||||
return k.SignWithAlgorithm(rand, data, k.PublicKey().Type())
|
return k.SignWithAlgorithm(rand, data, k.PublicKey().Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *dsaPrivateKey) Algorithms() []string {
|
||||||
|
return []string{k.PublicKey().Type()}
|
||||||
|
}
|
||||||
|
|
||||||
func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
|
func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
|
||||||
if algorithm != "" && algorithm != k.PublicKey().Type() {
|
if algorithm != "" && algorithm != k.PublicKey().Type() {
|
||||||
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
|
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
|
||||||
@@ -961,13 +1048,16 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
|||||||
return s.SignWithAlgorithm(rand, data, s.pubKey.Type())
|
return s.SignWithAlgorithm(rand, data, s.pubKey.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *wrappedSigner) Algorithms() []string {
|
||||||
|
return algorithmsForKeyFormat(s.pubKey.Type())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
|
func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
|
||||||
if algorithm == "" {
|
if algorithm == "" {
|
||||||
algorithm = s.pubKey.Type()
|
algorithm = s.pubKey.Type()
|
||||||
}
|
}
|
||||||
|
|
||||||
supportedAlgos := algorithmsForKeyFormat(s.pubKey.Type())
|
if !contains(s.Algorithms(), algorithm) {
|
||||||
if !contains(supportedAlgos, algorithm) {
|
|
||||||
return nil, fmt.Errorf("ssh: unsupported signature algorithm %q for key format %q", algorithm, s.pubKey.Type())
|
return nil, fmt.Errorf("ssh: unsupported signature algorithm %q for key format %q", algorithm, s.pubKey.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1241,28 +1331,106 @@ func passphraseProtectedOpenSSHKey(passphrase []byte) openSSHDecryptFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unencryptedOpenSSHMarshaler(privKeyBlock []byte) ([]byte, string, string, string, error) {
|
||||||
|
key := generateOpenSSHPadding(privKeyBlock, 8)
|
||||||
|
return key, "none", "none", "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func passphraseProtectedOpenSSHMarshaler(passphrase []byte) openSSHEncryptFunc {
|
||||||
|
return func(privKeyBlock []byte) ([]byte, string, string, string, error) {
|
||||||
|
salt := make([]byte, 16)
|
||||||
|
if _, err := rand.Read(salt); err != nil {
|
||||||
|
return nil, "", "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := struct {
|
||||||
|
Salt []byte
|
||||||
|
Rounds uint32
|
||||||
|
}{salt, 16}
|
||||||
|
|
||||||
|
// Derive key to encrypt the private key block.
|
||||||
|
k, err := bcrypt_pbkdf.Key(passphrase, salt, int(opts.Rounds), 32+aes.BlockSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add padding matching the block size of AES.
|
||||||
|
keyBlock := generateOpenSSHPadding(privKeyBlock, aes.BlockSize)
|
||||||
|
|
||||||
|
// Encrypt the private key using the derived secret.
|
||||||
|
|
||||||
|
dst := make([]byte, len(keyBlock))
|
||||||
|
key, iv := k[:32], k[32:]
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
stream := cipher.NewCTR(block, iv)
|
||||||
|
stream.XORKeyStream(dst, keyBlock)
|
||||||
|
|
||||||
|
return dst, "aes256-ctr", "bcrypt", string(Marshal(opts)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const privateKeyAuthMagic = "openssh-key-v1\x00"
|
||||||
|
|
||||||
type openSSHDecryptFunc func(CipherName, KdfName, KdfOpts string, PrivKeyBlock []byte) ([]byte, error)
|
type openSSHDecryptFunc func(CipherName, KdfName, KdfOpts string, PrivKeyBlock []byte) ([]byte, error)
|
||||||
|
type openSSHEncryptFunc func(PrivKeyBlock []byte) (ProtectedKeyBlock []byte, cipherName, kdfName, kdfOptions string, err error)
|
||||||
|
|
||||||
|
type openSSHEncryptedPrivateKey struct {
|
||||||
|
CipherName string
|
||||||
|
KdfName string
|
||||||
|
KdfOpts string
|
||||||
|
NumKeys uint32
|
||||||
|
PubKey []byte
|
||||||
|
PrivKeyBlock []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type openSSHPrivateKey struct {
|
||||||
|
Check1 uint32
|
||||||
|
Check2 uint32
|
||||||
|
Keytype string
|
||||||
|
Rest []byte `ssh:"rest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type openSSHRSAPrivateKey struct {
|
||||||
|
N *big.Int
|
||||||
|
E *big.Int
|
||||||
|
D *big.Int
|
||||||
|
Iqmp *big.Int
|
||||||
|
P *big.Int
|
||||||
|
Q *big.Int
|
||||||
|
Comment string
|
||||||
|
Pad []byte `ssh:"rest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type openSSHEd25519PrivateKey struct {
|
||||||
|
Pub []byte
|
||||||
|
Priv []byte
|
||||||
|
Comment string
|
||||||
|
Pad []byte `ssh:"rest"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type openSSHECDSAPrivateKey struct {
|
||||||
|
Curve string
|
||||||
|
Pub []byte
|
||||||
|
D *big.Int
|
||||||
|
Comment string
|
||||||
|
Pad []byte `ssh:"rest"`
|
||||||
|
}
|
||||||
|
|
||||||
// parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt
|
// parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt
|
||||||
// function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used
|
// function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used
|
||||||
// as the decrypt function to parse an unencrypted private key. See
|
// as the decrypt function to parse an unencrypted private key. See
|
||||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.
|
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.
|
||||||
func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) {
|
func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) {
|
||||||
const magic = "openssh-key-v1\x00"
|
if len(key) < len(privateKeyAuthMagic) || string(key[:len(privateKeyAuthMagic)]) != privateKeyAuthMagic {
|
||||||
if len(key) < len(magic) || string(key[:len(magic)]) != magic {
|
|
||||||
return nil, errors.New("ssh: invalid openssh private key format")
|
return nil, errors.New("ssh: invalid openssh private key format")
|
||||||
}
|
}
|
||||||
remaining := key[len(magic):]
|
remaining := key[len(privateKeyAuthMagic):]
|
||||||
|
|
||||||
var w struct {
|
|
||||||
CipherName string
|
|
||||||
KdfName string
|
|
||||||
KdfOpts string
|
|
||||||
NumKeys uint32
|
|
||||||
PubKey []byte
|
|
||||||
PrivKeyBlock []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var w openSSHEncryptedPrivateKey
|
||||||
if err := Unmarshal(remaining, &w); err != nil {
|
if err := Unmarshal(remaining, &w); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -1284,13 +1452,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pk1 := struct {
|
var pk1 openSSHPrivateKey
|
||||||
Check1 uint32
|
|
||||||
Check2 uint32
|
|
||||||
Keytype string
|
|
||||||
Rest []byte `ssh:"rest"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
if err := Unmarshal(privKeyBlock, &pk1); err != nil || pk1.Check1 != pk1.Check2 {
|
if err := Unmarshal(privKeyBlock, &pk1); err != nil || pk1.Check1 != pk1.Check2 {
|
||||||
if w.CipherName != "none" {
|
if w.CipherName != "none" {
|
||||||
return nil, x509.IncorrectPasswordError
|
return nil, x509.IncorrectPasswordError
|
||||||
@@ -1300,18 +1462,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
|||||||
|
|
||||||
switch pk1.Keytype {
|
switch pk1.Keytype {
|
||||||
case KeyAlgoRSA:
|
case KeyAlgoRSA:
|
||||||
// https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773
|
var key openSSHRSAPrivateKey
|
||||||
key := struct {
|
|
||||||
N *big.Int
|
|
||||||
E *big.Int
|
|
||||||
D *big.Int
|
|
||||||
Iqmp *big.Int
|
|
||||||
P *big.Int
|
|
||||||
Q *big.Int
|
|
||||||
Comment string
|
|
||||||
Pad []byte `ssh:"rest"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -1337,13 +1488,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
|||||||
|
|
||||||
return pk, nil
|
return pk, nil
|
||||||
case KeyAlgoED25519:
|
case KeyAlgoED25519:
|
||||||
key := struct {
|
var key openSSHEd25519PrivateKey
|
||||||
Pub []byte
|
|
||||||
Priv []byte
|
|
||||||
Comment string
|
|
||||||
Pad []byte `ssh:"rest"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -1360,14 +1505,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
|||||||
copy(pk, key.Priv)
|
copy(pk, key.Priv)
|
||||||
return &pk, nil
|
return &pk, nil
|
||||||
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
|
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
|
||||||
key := struct {
|
var key openSSHECDSAPrivateKey
|
||||||
Curve string
|
|
||||||
Pub []byte
|
|
||||||
D *big.Int
|
|
||||||
Comment string
|
|
||||||
Pad []byte `ssh:"rest"`
|
|
||||||
}{}
|
|
||||||
|
|
||||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -1415,6 +1553,131 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func marshalOpenSSHPrivateKey(key crypto.PrivateKey, comment string, encrypt openSSHEncryptFunc) (*pem.Block, error) {
|
||||||
|
var w openSSHEncryptedPrivateKey
|
||||||
|
var pk1 openSSHPrivateKey
|
||||||
|
|
||||||
|
// Random check bytes.
|
||||||
|
var check uint32
|
||||||
|
if err := binary.Read(rand.Reader, binary.BigEndian, &check); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pk1.Check1 = check
|
||||||
|
pk1.Check2 = check
|
||||||
|
w.NumKeys = 1
|
||||||
|
|
||||||
|
// Use a []byte directly on ed25519 keys.
|
||||||
|
if k, ok := key.(*ed25519.PrivateKey); ok {
|
||||||
|
key = *k
|
||||||
|
}
|
||||||
|
|
||||||
|
switch k := key.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
E := new(big.Int).SetInt64(int64(k.PublicKey.E))
|
||||||
|
// Marshal public key:
|
||||||
|
// E and N are in reversed order in the public and private key.
|
||||||
|
pubKey := struct {
|
||||||
|
KeyType string
|
||||||
|
E *big.Int
|
||||||
|
N *big.Int
|
||||||
|
}{
|
||||||
|
KeyAlgoRSA,
|
||||||
|
E, k.PublicKey.N,
|
||||||
|
}
|
||||||
|
w.PubKey = Marshal(pubKey)
|
||||||
|
|
||||||
|
// Marshal private key.
|
||||||
|
key := openSSHRSAPrivateKey{
|
||||||
|
N: k.PublicKey.N,
|
||||||
|
E: E,
|
||||||
|
D: k.D,
|
||||||
|
Iqmp: k.Precomputed.Qinv,
|
||||||
|
P: k.Primes[0],
|
||||||
|
Q: k.Primes[1],
|
||||||
|
Comment: comment,
|
||||||
|
}
|
||||||
|
pk1.Keytype = KeyAlgoRSA
|
||||||
|
pk1.Rest = Marshal(key)
|
||||||
|
case ed25519.PrivateKey:
|
||||||
|
pub := make([]byte, ed25519.PublicKeySize)
|
||||||
|
priv := make([]byte, ed25519.PrivateKeySize)
|
||||||
|
copy(pub, k[32:])
|
||||||
|
copy(priv, k)
|
||||||
|
|
||||||
|
// Marshal public key.
|
||||||
|
pubKey := struct {
|
||||||
|
KeyType string
|
||||||
|
Pub []byte
|
||||||
|
}{
|
||||||
|
KeyAlgoED25519, pub,
|
||||||
|
}
|
||||||
|
w.PubKey = Marshal(pubKey)
|
||||||
|
|
||||||
|
// Marshal private key.
|
||||||
|
key := openSSHEd25519PrivateKey{
|
||||||
|
Pub: pub,
|
||||||
|
Priv: priv,
|
||||||
|
Comment: comment,
|
||||||
|
}
|
||||||
|
pk1.Keytype = KeyAlgoED25519
|
||||||
|
pk1.Rest = Marshal(key)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
var curve, keyType string
|
||||||
|
switch name := k.Curve.Params().Name; name {
|
||||||
|
case "P-256":
|
||||||
|
curve = "nistp256"
|
||||||
|
keyType = KeyAlgoECDSA256
|
||||||
|
case "P-384":
|
||||||
|
curve = "nistp384"
|
||||||
|
keyType = KeyAlgoECDSA384
|
||||||
|
case "P-521":
|
||||||
|
curve = "nistp521"
|
||||||
|
keyType = KeyAlgoECDSA521
|
||||||
|
default:
|
||||||
|
return nil, errors.New("ssh: unhandled elliptic curve " + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub := elliptic.Marshal(k.Curve, k.PublicKey.X, k.PublicKey.Y)
|
||||||
|
|
||||||
|
// Marshal public key.
|
||||||
|
pubKey := struct {
|
||||||
|
KeyType string
|
||||||
|
Curve string
|
||||||
|
Pub []byte
|
||||||
|
}{
|
||||||
|
keyType, curve, pub,
|
||||||
|
}
|
||||||
|
w.PubKey = Marshal(pubKey)
|
||||||
|
|
||||||
|
// Marshal private key.
|
||||||
|
key := openSSHECDSAPrivateKey{
|
||||||
|
Curve: curve,
|
||||||
|
Pub: pub,
|
||||||
|
D: k.D,
|
||||||
|
Comment: comment,
|
||||||
|
}
|
||||||
|
pk1.Keytype = keyType
|
||||||
|
pk1.Rest = Marshal(key)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("ssh: unsupported key type %T", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
// Add padding and encrypt the key if necessary.
|
||||||
|
w.PrivKeyBlock, w.CipherName, w.KdfName, w.KdfOpts, err = encrypt(Marshal(pk1))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b := Marshal(w)
|
||||||
|
block := &pem.Block{
|
||||||
|
Type: "OPENSSH PRIVATE KEY",
|
||||||
|
Bytes: append([]byte(privateKeyAuthMagic), b...),
|
||||||
|
}
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
func checkOpenSSHKeyPadding(pad []byte) error {
|
func checkOpenSSHKeyPadding(pad []byte) error {
|
||||||
for i, b := range pad {
|
for i, b := range pad {
|
||||||
if int(b) != i+1 {
|
if int(b) != i+1 {
|
||||||
@@ -1424,6 +1687,13 @@ func checkOpenSSHKeyPadding(pad []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateOpenSSHPadding(block []byte, blockSize int) []byte {
|
||||||
|
for i, l := 0, len(block); (l+i)%blockSize != 0; i++ {
|
||||||
|
block = append(block, byte(i+1))
|
||||||
|
}
|
||||||
|
return block
|
||||||
|
}
|
||||||
|
|
||||||
// FingerprintLegacyMD5 returns the user presentation of the key's
|
// FingerprintLegacyMD5 returns the user presentation of the key's
|
||||||
// fingerprint as described by RFC 4716 section 4.
|
// fingerprint as described by RFC 4716 section 4.
|
||||||
func FingerprintLegacyMD5(pubKey PublicKey) string {
|
func FingerprintLegacyMD5(pubKey PublicKey) string {
|
||||||
|
|||||||
+103
-4
@@ -8,6 +8,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/dsa"
|
"crypto/dsa"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
@@ -21,7 +22,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
"golang.org/x/crypto/ssh/testdata"
|
"golang.org/x/crypto/ssh/testdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,9 +111,9 @@ func TestKeySignVerify(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestKeySignWithAlgorithmVerify(t *testing.T) {
|
func TestKeySignWithAlgorithmVerify(t *testing.T) {
|
||||||
for _, priv := range testSigners {
|
for k, priv := range testSigners {
|
||||||
if algorithmSigner, ok := priv.(AlgorithmSigner); !ok {
|
if algorithmSigner, ok := priv.(MultiAlgorithmSigner); !ok {
|
||||||
t.Errorf("Signers constructed by ssh package should always implement the AlgorithmSigner interface: %T", priv)
|
t.Errorf("Signers %q constructed by ssh package should always implement the MultiAlgorithmSigner interface: %T", k, priv)
|
||||||
} else {
|
} else {
|
||||||
pub := priv.PublicKey()
|
pub := priv.PublicKey()
|
||||||
data := []byte("sign me")
|
data := []byte("sign me")
|
||||||
@@ -281,6 +281,74 @@ func TestMarshalParsePublicKey(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMarshalPrivateKey(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
}{
|
||||||
|
{"rsa-openssh-format"},
|
||||||
|
{"ed25519"},
|
||||||
|
{"p256-openssh-format"},
|
||||||
|
{"p384-openssh-format"},
|
||||||
|
{"p521-openssh-format"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
expected, ok := testPrivateKeys[tt.name]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("cannot find key %s", tt.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := MarshalPrivateKey(expected, "test@golang.org")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot marshal %s: %v", tt.name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := ParseRawPrivateKey(pem.EncodeToMemory(block))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot parse %s: %v", tt.name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expected, key) {
|
||||||
|
t.Errorf("unexpected marshaled key %s", tt.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalPrivateKeyWithPassphrase(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
}{
|
||||||
|
{"rsa-openssh-format"},
|
||||||
|
{"ed25519"},
|
||||||
|
{"p256-openssh-format"},
|
||||||
|
{"p384-openssh-format"},
|
||||||
|
{"p521-openssh-format"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
expected, ok := testPrivateKeys[tt.name]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("cannot find key %s", tt.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := MarshalPrivateKeyWithPassphrase(expected, "test@golang.org", []byte("test-passphrase"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot marshal %s: %v", tt.name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := ParseRawPrivateKeyWithPassphrase(pem.EncodeToMemory(block), []byte("test-passphrase"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot parse %s: %v", tt.name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expected, key) {
|
||||||
|
t.Errorf("unexpected marshaled key %s", tt.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type testAuthResult struct {
|
type testAuthResult struct {
|
||||||
pubKey PublicKey
|
pubKey PublicKey
|
||||||
options []string
|
options []string
|
||||||
@@ -616,3 +684,34 @@ func TestSKKeys(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewSignerWithAlgos(t *testing.T) {
|
||||||
|
algorithSigner, ok := testSigners["rsa"].(AlgorithmSigner)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("rsa test signer does not implement the AlgorithmSigner interface")
|
||||||
|
}
|
||||||
|
_, err := NewSignerWithAlgorithms(algorithSigner, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("signer with algos created with no algorithms")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewSignerWithAlgorithms(algorithSigner, []string{KeyAlgoED25519})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("signer with algos created with invalid algorithms")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewSignerWithAlgorithms(algorithSigner, []string{CertAlgoRSASHA256v01})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("signer with algos created with certificate algorithms")
|
||||||
|
}
|
||||||
|
|
||||||
|
mas, err := NewSignerWithAlgorithms(algorithSigner, []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unable to create signer with valid algorithms: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewSignerWithAlgorithms(mas, []string{KeyAlgoRSA})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("signer with algos created with restricted algorithms")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -349,6 +349,20 @@ type userAuthGSSAPIError struct {
|
|||||||
LanguageTag string
|
LanguageTag string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transport layer OpenSSH extension. See [PROTOCOL], section 1.9
|
||||||
|
const msgPing = 192
|
||||||
|
|
||||||
|
type pingMsg struct {
|
||||||
|
Data string `sshtype:"192"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transport layer OpenSSH extension. See [PROTOCOL], section 1.9
|
||||||
|
const msgPong = 193
|
||||||
|
|
||||||
|
type pongMsg struct {
|
||||||
|
Data string `sshtype:"193"`
|
||||||
|
}
|
||||||
|
|
||||||
// typeTags returns the possible type bytes for the given reflect.Type, which
|
// typeTags returns the possible type bytes for the given reflect.Type, which
|
||||||
// should be a struct. The possible values are separated by a '|' character.
|
// should be a struct. The possible values are separated by a '|' character.
|
||||||
func typeTags(structType reflect.Type) (tags []byte) {
|
func typeTags(structType reflect.Type) (tags []byte) {
|
||||||
|
|||||||
@@ -231,6 +231,12 @@ func (m *mux) onePacket() error {
|
|||||||
return m.handleChannelOpen(packet)
|
return m.handleChannelOpen(packet)
|
||||||
case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
|
case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
|
||||||
return m.handleGlobalPacket(packet)
|
return m.handleGlobalPacket(packet)
|
||||||
|
case msgPing:
|
||||||
|
var msg pingMsg
|
||||||
|
if err := Unmarshal(packet, &msg); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal ping@openssh.com message: %w", err)
|
||||||
|
}
|
||||||
|
return m.sendMessage(pongMsg(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// assume a channel packet.
|
// assume a channel packet.
|
||||||
|
|||||||
+85
-38
@@ -5,6 +5,8 @@
|
|||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -29,14 +31,21 @@ func channelPair(t *testing.T) (*channel, *channel, *mux) {
|
|||||||
go func() {
|
go func() {
|
||||||
newCh, ok := <-s.incomingChannels
|
newCh, ok := <-s.incomingChannels
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("No incoming channel")
|
t.Error("no incoming channel")
|
||||||
|
close(res)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if newCh.ChannelType() != "chan" {
|
if newCh.ChannelType() != "chan" {
|
||||||
t.Fatalf("got type %q want chan", newCh.ChannelType())
|
t.Errorf("got type %q want chan", newCh.ChannelType())
|
||||||
|
newCh.Reject(Prohibited, fmt.Sprintf("got type %q want chan", newCh.ChannelType()))
|
||||||
|
close(res)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
ch, _, err := newCh.Accept()
|
ch, _, err := newCh.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Accept %v", err)
|
t.Errorf("accept: %v", err)
|
||||||
|
close(res)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
res <- ch.(*channel)
|
res <- ch.(*channel)
|
||||||
}()
|
}()
|
||||||
@@ -45,8 +54,12 @@ func channelPair(t *testing.T) (*channel, *channel, *mux) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("OpenChannel: %v", err)
|
t.Fatalf("OpenChannel: %v", err)
|
||||||
}
|
}
|
||||||
|
w := <-res
|
||||||
|
if w == nil {
|
||||||
|
t.Fatal("unable to get write channel")
|
||||||
|
}
|
||||||
|
|
||||||
return <-res, ch, c
|
return w, ch, c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that stderr and stdout can be addressed from different
|
// Test that stderr and stdout can be addressed from different
|
||||||
@@ -74,14 +87,14 @@ func TestMuxChannelExtendedThreadSafety(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
c, err := io.ReadAll(reader)
|
c, err := io.ReadAll(reader)
|
||||||
if string(c) != magic {
|
if string(c) != magic {
|
||||||
t.Fatalf("stdout read got %q, want %q (error %s)", c, magic, err)
|
t.Errorf("stdout read got %q, want %q (error %s)", c, magic, err)
|
||||||
}
|
}
|
||||||
rd.Done()
|
rd.Done()
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
c, err := io.ReadAll(reader.Stderr())
|
c, err := io.ReadAll(reader.Stderr())
|
||||||
if string(c) != magic {
|
if string(c) != magic {
|
||||||
t.Fatalf("stderr read got %q, want %q (error %s)", c, magic, err)
|
t.Errorf("stderr read got %q, want %q (error %s)", c, magic, err)
|
||||||
}
|
}
|
||||||
rd.Done()
|
rd.Done()
|
||||||
}()
|
}()
|
||||||
@@ -102,11 +115,13 @@ func TestMuxReadWrite(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
_, err := s.Write([]byte(magic))
|
_, err := s.Write([]byte(magic))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Write: %v", err)
|
t.Errorf("Write: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
_, err = s.Extended(1).Write([]byte(magicExt))
|
_, err = s.Extended(1).Write([]byte(magicExt))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Write: %v", err)
|
t.Errorf("Write: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -215,10 +230,13 @@ func TestMuxReject(t *testing.T) {
|
|||||||
go func() {
|
go func() {
|
||||||
ch, ok := <-server.incomingChannels
|
ch, ok := <-server.incomingChannels
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("Accept")
|
t.Error("cannot accept channel")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if ch.ChannelType() != "ch" || string(ch.ExtraData()) != "extra" {
|
if ch.ChannelType() != "ch" || string(ch.ExtraData()) != "extra" {
|
||||||
t.Fatalf("unexpected channel: %q, %q", ch.ChannelType(), ch.ExtraData())
|
t.Errorf("unexpected channel: %q, %q", ch.ChannelType(), ch.ExtraData())
|
||||||
|
ch.Reject(RejectionReason(UnknownChannelType), UnknownChannelType.String())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
ch.Reject(RejectionReason(42), "message")
|
ch.Reject(RejectionReason(42), "message")
|
||||||
}()
|
}()
|
||||||
@@ -294,7 +312,7 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
|
|||||||
defer serverPipe.Close()
|
defer serverPipe.Close()
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
kDone := make(chan struct{})
|
kDone := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
// Ignore unknown channel messages that don't want a reply.
|
// Ignore unknown channel messages that don't want a reply.
|
||||||
err := serverPipe.writePacket(Marshal(channelRequestMsg{
|
err := serverPipe.writePacket(Marshal(channelRequestMsg{
|
||||||
@@ -304,7 +322,8 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
|
|||||||
RequestSpecificData: []byte{},
|
RequestSpecificData: []byte{},
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("send: %v", err)
|
kDone <- fmt.Errorf("send: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a keepalive, which should get a channel failure message
|
// Send a keepalive, which should get a channel failure message
|
||||||
@@ -316,44 +335,53 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
|
|||||||
RequestSpecificData: []byte{},
|
RequestSpecificData: []byte{},
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("send: %v", err)
|
kDone <- fmt.Errorf("send: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
packet, err := serverPipe.readPacket()
|
packet, err := serverPipe.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("read packet: %v", err)
|
kDone <- fmt.Errorf("read packet: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
decoded, err := decode(packet)
|
decoded, err := decode(packet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("decode failed: %v", err)
|
kDone <- fmt.Errorf("decode failed: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch msg := decoded.(type) {
|
switch msg := decoded.(type) {
|
||||||
case *channelRequestFailureMsg:
|
case *channelRequestFailureMsg:
|
||||||
if msg.PeersID != 2 {
|
if msg.PeersID != 2 {
|
||||||
t.Fatalf("received response to wrong message: %v", msg)
|
kDone <- fmt.Errorf("received response to wrong message: %v", msg)
|
||||||
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unexpected channel message: %v", msg)
|
kDone <- fmt.Errorf("unexpected channel message: %v", msg)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
kDone <- struct{}{}
|
kDone <- nil
|
||||||
|
|
||||||
// Receive and respond to the keepalive to confirm the mux is
|
// Receive and respond to the keepalive to confirm the mux is
|
||||||
// still processing requests.
|
// still processing requests.
|
||||||
packet, err = serverPipe.readPacket()
|
packet, err = serverPipe.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("read packet: %v", err)
|
kDone <- fmt.Errorf("read packet: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if packet[0] != msgGlobalRequest {
|
if packet[0] != msgGlobalRequest {
|
||||||
t.Fatalf("expected global request")
|
kDone <- errors.New("expected global request")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
|
err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
|
||||||
Data: []byte{},
|
Data: []byte{},
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to send failure msg: %v", err)
|
kDone <- fmt.Errorf("failed to send failure msg: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
close(kDone)
|
close(kDone)
|
||||||
@@ -362,7 +390,10 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
|
|||||||
// Wait for the server to send the keepalive message and receive back a
|
// Wait for the server to send the keepalive message and receive back a
|
||||||
// response.
|
// response.
|
||||||
select {
|
select {
|
||||||
case <-kDone:
|
case err := <-kDone:
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
case <-time.After(10 * time.Second):
|
case <-time.After(10 * time.Second):
|
||||||
t.Fatalf("server never received ack")
|
t.Fatalf("server never received ack")
|
||||||
}
|
}
|
||||||
@@ -373,7 +404,10 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-kDone:
|
case err := <-kDone:
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
case <-time.After(10 * time.Second):
|
case <-time.After(10 * time.Second):
|
||||||
t.Fatalf("server never shut down")
|
t.Fatalf("server never shut down")
|
||||||
}
|
}
|
||||||
@@ -385,20 +419,23 @@ func TestMuxClosedChannel(t *testing.T) {
|
|||||||
defer serverPipe.Close()
|
defer serverPipe.Close()
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
kDone := make(chan struct{})
|
kDone := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
// Open the channel.
|
// Open the channel.
|
||||||
packet, err := serverPipe.readPacket()
|
packet, err := serverPipe.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("read packet: %v", err)
|
kDone <- fmt.Errorf("read packet: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if packet[0] != msgChannelOpen {
|
if packet[0] != msgChannelOpen {
|
||||||
t.Fatalf("expected chan open")
|
kDone <- errors.New("expected chan open")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var openMsg channelOpenMsg
|
var openMsg channelOpenMsg
|
||||||
if err := Unmarshal(packet, &openMsg); err != nil {
|
if err := Unmarshal(packet, &openMsg); err != nil {
|
||||||
t.Fatalf("unmarshal: %v", err)
|
kDone <- fmt.Errorf("unmarshal: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send back the opened channel confirmation.
|
// Send back the opened channel confirmation.
|
||||||
@@ -409,7 +446,8 @@ func TestMuxClosedChannel(t *testing.T) {
|
|||||||
MaxPacketSize: channelMaxPacket,
|
MaxPacketSize: channelMaxPacket,
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("send: %v", err)
|
kDone <- fmt.Errorf("send: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the channel.
|
// Close the channel.
|
||||||
@@ -417,7 +455,8 @@ func TestMuxClosedChannel(t *testing.T) {
|
|||||||
PeersID: openMsg.PeersID,
|
PeersID: openMsg.PeersID,
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("send: %v", err)
|
kDone <- fmt.Errorf("send: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a keepalive message on the channel we just closed.
|
// Send a keepalive message on the channel we just closed.
|
||||||
@@ -428,43 +467,51 @@ func TestMuxClosedChannel(t *testing.T) {
|
|||||||
RequestSpecificData: []byte{},
|
RequestSpecificData: []byte{},
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("send: %v", err)
|
kDone <- fmt.Errorf("send: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive the channel closed response.
|
// Receive the channel closed response.
|
||||||
packet, err = serverPipe.readPacket()
|
packet, err = serverPipe.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("read packet: %v", err)
|
kDone <- fmt.Errorf("read packet: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if packet[0] != msgChannelClose {
|
if packet[0] != msgChannelClose {
|
||||||
t.Fatalf("expected channel close")
|
kDone <- errors.New("expected channel close")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive the keepalive response failure.
|
// Receive the keepalive response failure.
|
||||||
packet, err = serverPipe.readPacket()
|
packet, err = serverPipe.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("read packet: %v", err)
|
kDone <- fmt.Errorf("read packet: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if packet[0] != msgChannelFailure {
|
if packet[0] != msgChannelFailure {
|
||||||
t.Fatalf("expected channel close")
|
kDone <- errors.New("expected channel failure")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
kDone <- struct{}{}
|
kDone <- nil
|
||||||
|
|
||||||
// Receive and respond to the keepalive to confirm the mux is
|
// Receive and respond to the keepalive to confirm the mux is
|
||||||
// still processing requests.
|
// still processing requests.
|
||||||
packet, err = serverPipe.readPacket()
|
packet, err = serverPipe.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("read packet: %v", err)
|
kDone <- fmt.Errorf("read packet: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if packet[0] != msgGlobalRequest {
|
if packet[0] != msgGlobalRequest {
|
||||||
t.Fatalf("expected global request")
|
kDone <- errors.New("expected global request")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
|
err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
|
||||||
Data: []byte{},
|
Data: []byte{},
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to send failure msg: %v", err)
|
kDone <- fmt.Errorf("failed to send failure msg: %w", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
close(kDone)
|
close(kDone)
|
||||||
|
|||||||
+30
-2
@@ -370,6 +370,25 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c
|
|||||||
return authErr, perms, nil
|
return authErr, perms, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isAlgoCompatible checks if the signature format is compatible with the
|
||||||
|
// selected algorithm taking into account edge cases that occur with old
|
||||||
|
// clients.
|
||||||
|
func isAlgoCompatible(algo, sigFormat string) bool {
|
||||||
|
// Compatibility for old clients.
|
||||||
|
//
|
||||||
|
// For certificate authentication with OpenSSH 7.2-7.7 signature format can
|
||||||
|
// be rsa-sha2-256 or rsa-sha2-512 for the algorithm
|
||||||
|
// ssh-rsa-cert-v01@openssh.com.
|
||||||
|
//
|
||||||
|
// With gpg-agent < 2.2.6 the algorithm can be rsa-sha2-256 or rsa-sha2-512
|
||||||
|
// for signature format ssh-rsa.
|
||||||
|
if isRSA(algo) && isRSA(sigFormat) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Standard case: the underlying algorithm must match the signature format.
|
||||||
|
return underlyingAlgo(algo) == sigFormat
|
||||||
|
}
|
||||||
|
|
||||||
// ServerAuthError represents server authentication errors and is
|
// ServerAuthError represents server authentication errors and is
|
||||||
// sometimes returned by NewServerConn. It appends any authentication
|
// sometimes returned by NewServerConn. It appends any authentication
|
||||||
// errors that may occur, and is returned if all of the authentication
|
// errors that may occur, and is returned if all of the authentication
|
||||||
@@ -557,7 +576,16 @@ userAuthLoop:
|
|||||||
if !ok || len(payload) > 0 {
|
if !ok || len(payload) > 0 {
|
||||||
return nil, parseError(msgUserAuthRequest)
|
return nil, parseError(msgUserAuthRequest)
|
||||||
}
|
}
|
||||||
|
// Ensure the declared public key algo is compatible with the
|
||||||
|
// decoded one. This check will ensure we don't accept e.g.
|
||||||
|
// ssh-rsa-cert-v01@openssh.com algorithm with ssh-rsa public
|
||||||
|
// key type. The algorithm and public key type must be
|
||||||
|
// consistent: both must be certificate algorithms, or neither.
|
||||||
|
if !contains(algorithmsForKeyFormat(pubKey.Type()), algo) {
|
||||||
|
authErr = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q",
|
||||||
|
pubKey.Type(), algo)
|
||||||
|
break
|
||||||
|
}
|
||||||
// Ensure the public key algo and signature algo
|
// Ensure the public key algo and signature algo
|
||||||
// are supported. Compare the private key
|
// are supported. Compare the private key
|
||||||
// algorithm name that corresponds to algo with
|
// algorithm name that corresponds to algo with
|
||||||
@@ -567,7 +595,7 @@ userAuthLoop:
|
|||||||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
|
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if underlyingAlgo(algo) != sig.Format {
|
if !isAlgoCompatible(algo, sig.Format) {
|
||||||
authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo)
|
authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-3
@@ -36,7 +36,8 @@ func dial(handler serverType, t *testing.T) *Client {
|
|||||||
|
|
||||||
conn, chans, reqs, err := NewServerConn(c1, &conf)
|
conn, chans, reqs, err := NewServerConn(c1, &conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unable to handshake: %v", err)
|
t.Errorf("Unable to handshake: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
go DiscardRequests(reqs)
|
go DiscardRequests(reqs)
|
||||||
|
|
||||||
@@ -647,10 +648,12 @@ func TestSessionID(t *testing.T) {
|
|||||||
User: "user",
|
User: "user",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srvErrCh := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
conn, chans, reqs, err := NewServerConn(c1, serverConf)
|
conn, chans, reqs, err := NewServerConn(c1, serverConf)
|
||||||
|
srvErrCh <- err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("server handshake: %v", err)
|
return
|
||||||
}
|
}
|
||||||
serverID <- conn.SessionID()
|
serverID <- conn.SessionID()
|
||||||
go DiscardRequests(reqs)
|
go DiscardRequests(reqs)
|
||||||
@@ -659,10 +662,12 @@ func TestSessionID(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
cliErrCh := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
|
conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
|
||||||
|
cliErrCh <- err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("client handshake: %v", err)
|
return
|
||||||
}
|
}
|
||||||
clientID <- conn.SessionID()
|
clientID <- conn.SessionID()
|
||||||
go DiscardRequests(reqs)
|
go DiscardRequests(reqs)
|
||||||
@@ -671,6 +676,14 @@ func TestSessionID(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if err := <-srvErrCh; err != nil {
|
||||||
|
t.Fatalf("server handshake: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := <-cliErrCh; err != nil {
|
||||||
|
t.Fatalf("client handshake: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
s := <-serverID
|
s := <-serverID
|
||||||
c := <-clientID
|
c := <-clientID
|
||||||
if bytes.Compare(s, c) != 0 {
|
if bytes.Compare(s, c) != 0 {
|
||||||
|
|||||||
@@ -77,27 +77,27 @@ func (ctx *multiAuthTestCtx) kbdIntCb(user, instruction string, questions []stri
|
|||||||
func TestMultiAuth(t *testing.T) {
|
func TestMultiAuth(t *testing.T) {
|
||||||
testCases := []multiAuthTestCase{
|
testCases := []multiAuthTestCase{
|
||||||
// Test password,publickey authentication, assert that password callback is called 1 time
|
// Test password,publickey authentication, assert that password callback is called 1 time
|
||||||
multiAuthTestCase{
|
{
|
||||||
authMethods: []string{"password", "publickey"},
|
authMethods: []string{"password", "publickey"},
|
||||||
expectedPasswordCbs: 1,
|
expectedPasswordCbs: 1,
|
||||||
},
|
},
|
||||||
// Test keyboard-interactive,publickey authentication, assert that keyboard-interactive callback is called 1 time
|
// Test keyboard-interactive,publickey authentication, assert that keyboard-interactive callback is called 1 time
|
||||||
multiAuthTestCase{
|
{
|
||||||
authMethods: []string{"keyboard-interactive", "publickey"},
|
authMethods: []string{"keyboard-interactive", "publickey"},
|
||||||
expectedKbdIntCbs: 1,
|
expectedKbdIntCbs: 1,
|
||||||
},
|
},
|
||||||
// Test publickey,password authentication, assert that password callback is called 1 time
|
// Test publickey,password authentication, assert that password callback is called 1 time
|
||||||
multiAuthTestCase{
|
{
|
||||||
authMethods: []string{"publickey", "password"},
|
authMethods: []string{"publickey", "password"},
|
||||||
expectedPasswordCbs: 1,
|
expectedPasswordCbs: 1,
|
||||||
},
|
},
|
||||||
// Test publickey,keyboard-interactive authentication, assert that keyboard-interactive callback is called 1 time
|
// Test publickey,keyboard-interactive authentication, assert that keyboard-interactive callback is called 1 time
|
||||||
multiAuthTestCase{
|
{
|
||||||
authMethods: []string{"publickey", "keyboard-interactive"},
|
authMethods: []string{"publickey", "keyboard-interactive"},
|
||||||
expectedKbdIntCbs: 1,
|
expectedKbdIntCbs: 1,
|
||||||
},
|
},
|
||||||
// Test password,password authentication, assert that password callback is called 2 times
|
// Test password,password authentication, assert that password callback is called 2 times
|
||||||
multiAuthTestCase{
|
{
|
||||||
authMethods: []string{"password", "password"},
|
authMethods: []string{"password", "password"},
|
||||||
expectedPasswordCbs: 2,
|
expectedPasswordCbs: 2,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ func TestValidTerminalMode(t *testing.T) {
|
|||||||
t.Fatalf("session failed: %s", err)
|
t.Fatalf("session failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := io.WriteString(stdin, "echo SHELL $SHELL && stty -a && exit\n"); err != nil {
|
if _, err := io.WriteString(stdin, "echo && echo SHELL $SHELL && stty -a && exit\n"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@ func TestValidTerminalMode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if testing.Verbose() {
|
if testing.Verbose() {
|
||||||
t.Logf("echo SHELL $SHELL && stty -a && exit:\n%s", buf)
|
t.Logf("echo && echo SHELL $SHELL && stty -a && exit:\n%s", buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
shellLine := regexp.MustCompile("(?m)^SHELL (.*)$").FindStringSubmatch(buf.String())
|
shellLine := regexp.MustCompile("(?m)^SHELL (.*)$").FindStringSubmatch(buf.String())
|
||||||
@@ -410,6 +410,9 @@ func TestKeyExchanges(t *testing.T) {
|
|||||||
// are not included in the default list of supported kex so we have to add them
|
// are not included in the default list of supported kex so we have to add them
|
||||||
// here manually.
|
// here manually.
|
||||||
kexOrder = append(kexOrder, "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256")
|
kexOrder = append(kexOrder, "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256")
|
||||||
|
// The key exchange algorithms diffie-hellman-group16-sha512 is disabled by
|
||||||
|
// default so we add it here manually.
|
||||||
|
kexOrder = append(kexOrder, "diffie-hellman-group16-sha512")
|
||||||
for _, kex := range kexOrder {
|
for _, kex := range kexOrder {
|
||||||
t.Run(kex, func(t *testing.T) {
|
t.Run(kex, func(t *testing.T) {
|
||||||
server := newServer(t)
|
server := newServer(t)
|
||||||
|
|||||||
+149
-115
@@ -360,6 +360,55 @@ maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D
|
|||||||
lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv
|
lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv
|
||||||
KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
|
KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
# CN=Atos TrustedRoot Root CA ECC TLS 2021,O=Atos,C=DE
|
||||||
|
# b2fae53e14ccd7ab9212064701ae279c1d8988facb775fa8a008914e663988a8
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w
|
||||||
|
LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w
|
||||||
|
CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0
|
||||||
|
MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF
|
||||||
|
Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI
|
||||||
|
zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X
|
||||||
|
tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4
|
||||||
|
AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2
|
||||||
|
KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD
|
||||||
|
aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu
|
||||||
|
CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo
|
||||||
|
9H1/IISpQuQo
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
# CN=Atos TrustedRoot Root CA RSA TLS 2021,O=Atos,C=DE
|
||||||
|
# 81a9088ea59fb364c548a6f85559099b6f0405efbf18e5324ec9f457ba00112f
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----
|
||||||
# CN=Autoridad de Certificacion Firmaprofesional CIF A62634068,C=ES
|
# CN=Autoridad de Certificacion Firmaprofesional CIF A62634068,C=ES
|
||||||
# 57de0583efd2b26e0361da99da9df4648def7ee8441c3b728afa9bcde0f9b26a
|
# 57de0583efd2b26e0361da99da9df4648def7ee8441c3b728afa9bcde0f9b26a
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
@@ -1275,99 +1324,6 @@ r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
|
|||||||
/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
|
/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
|
||||||
gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
|
gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
# CN=E-Tugra Certification Authority,OU=E-Tugra Sertifikasyon Merkezi,O=E-Tuğra EBG Bilişim Teknolojileri ve Hizmetleri A.Ş.,L=Ankara,C=TR
|
|
||||||
# b0bfd52bb0d7d9bd92bf5d4dc13da255c02c542f378365ea893911f55e55f23c
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV
|
|
||||||
BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC
|
|
||||||
aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV
|
|
||||||
BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1
|
|
||||||
Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz
|
|
||||||
MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+
|
|
||||||
BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp
|
|
||||||
em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
|
|
||||||
ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY
|
|
||||||
B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH
|
|
||||||
D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF
|
|
||||||
Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo
|
|
||||||
q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D
|
|
||||||
k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH
|
|
||||||
fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut
|
|
||||||
dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM
|
|
||||||
ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8
|
|
||||||
zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
|
|
||||||
rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX
|
|
||||||
U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6
|
|
||||||
Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5
|
|
||||||
XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF
|
|
||||||
Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR
|
|
||||||
HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY
|
|
||||||
GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c
|
|
||||||
77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3
|
|
||||||
+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK
|
|
||||||
vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6
|
|
||||||
FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl
|
|
||||||
yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P
|
|
||||||
AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD
|
|
||||||
y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d
|
|
||||||
NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
# CN=E-Tugra Global Root CA ECC v3,OU=E-Tugra Trust Center,O=E-Tugra EBG A.S.,L=Ankara,C=TR
|
|
||||||
# 873f4685fa7f563625252e6d36bcd7f16fc24951f264e47e1b954f4908cdca13
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMw
|
|
||||||
gYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVn
|
|
||||||
cmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYD
|
|
||||||
VQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIEVDQyB2MzAeFw0yMDAzMTgwOTQ2
|
|
||||||
NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5r
|
|
||||||
YXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1Z3Jh
|
|
||||||
IFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBF
|
|
||||||
Q0MgdjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQ
|
|
||||||
KczLWYHMjLiSF4mDKpL2w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YK
|
|
||||||
fWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMB
|
|
||||||
Af8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQzPUwHQYDVR0OBBYEFP+C
|
|
||||||
MXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNp
|
|
||||||
ADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/6
|
|
||||||
7W4WAie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFx
|
|
||||||
vmjkI6TZraE3
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
# CN=E-Tugra Global Root CA RSA v3,OU=E-Tugra Trust Center,O=E-Tugra EBG A.S.,L=Ankara,C=TR
|
|
||||||
# ef66b0b10a3cdb9f2e3648c76bd2af18ead2bfe6f117655e28c4060da1a3f4c2
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
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
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
# CN=Entrust Root Certification Authority - EC1,OU=See www.entrust.net/legal-terms+OU=(c) 2012 Entrust\, Inc. - for authorized use only,O=Entrust\, Inc.,C=US
|
# CN=Entrust Root Certification Authority - EC1,OU=See www.entrust.net/legal-terms+OU=(c) 2012 Entrust\, Inc. - for authorized use only,O=Entrust\, Inc.,C=US
|
||||||
# 02ed0eb28c14da45165c566791700d6451d7fb56f0b2ab1d3b8eb070e56edff5
|
# 02ed0eb28c14da45165c566791700d6451d7fb56f0b2ab1d3b8eb070e56edff5
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
@@ -1997,28 +1953,6 @@ kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+
|
|||||||
vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU
|
vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU
|
||||||
YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ==
|
YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
# CN=Hongkong Post Root CA 1,O=Hongkong Post,C=HK
|
|
||||||
# f9e67d336c51002ac054c632022d66dda2e7e3fff10ad061ed31d8bbb410cfb2
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx
|
|
||||||
FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg
|
|
||||||
Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG
|
|
||||||
A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr
|
|
||||||
b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
|
||||||
AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ
|
|
||||||
jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn
|
|
||||||
PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh
|
|
||||||
ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9
|
|
||||||
nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h
|
|
||||||
q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED
|
|
||||||
MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC
|
|
||||||
mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3
|
|
||||||
7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB
|
|
||||||
oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs
|
|
||||||
EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO
|
|
||||||
fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi
|
|
||||||
AmvZWg==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
# CN=Hongkong Post Root CA 3,O=Hongkong Post,L=Hong Kong,ST=Hong Kong,C=HK
|
# CN=Hongkong Post Root CA 3,O=Hongkong Post,L=Hong Kong,ST=Hong Kong,C=HK
|
||||||
# 5a2fc03f0c83b090bbfa40604b0988446c7636183df9846e17101a447fb8efd6
|
# 5a2fc03f0c83b090bbfa40604b0988446c7636183df9846e17101a447fb8efd6
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
@@ -2668,6 +2602,56 @@ nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf
|
|||||||
oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY
|
oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY
|
||||||
Ic2wBlX7Jz9TkHCpBB5XJ7k=
|
Ic2wBlX7Jz9TkHCpBB5XJ7k=
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
# CN=SSL.com TLS ECC Root CA 2022,O=SSL Corporation,C=US
|
||||||
|
# c32ffd9f46f936d16c3673990959434b9ad60aafbb9e7cf33654f144cc1ba143
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw
|
||||||
|
CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT
|
||||||
|
U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2
|
||||||
|
MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh
|
||||||
|
dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG
|
||||||
|
ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm
|
||||||
|
acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN
|
||||||
|
SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME
|
||||||
|
GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW
|
||||||
|
uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp
|
||||||
|
15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN
|
||||||
|
b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
# CN=SSL.com TLS RSA Root CA 2022,O=SSL Corporation,C=US
|
||||||
|
# 8faf7d2e2cb4709bb8e0b33666bf75a5dd45b5de480f8ea8d4bfe6bebc17f2ed
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
# CN=SZAFIR ROOT CA2,O=Krajowa Izba Rozliczeniowa S.A.,C=PL
|
# CN=SZAFIR ROOT CA2,O=Krajowa Izba Rozliczeniowa S.A.,C=PL
|
||||||
# a1339d33281a0b56e557d3d32b1ce7f9367eb094bd5fa72a7e5004c8ded7cafe
|
# a1339d33281a0b56e557d3d32b1ce7f9367eb094bd5fa72a7e5004c8ded7cafe
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
@@ -2691,6 +2675,56 @@ oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy
|
|||||||
d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg
|
d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg
|
||||||
LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
|
LvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
# CN=Sectigo Public Server Authentication Root E46,O=Sectigo Limited,C=GB
|
||||||
|
# c90f26f0fb1b4018b22227519b5ca2b53e2ca5b3be5cf18efe1bef47380c5383
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw
|
||||||
|
CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T
|
||||||
|
ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN
|
||||||
|
MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG
|
||||||
|
A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT
|
||||||
|
ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||||
|
IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC
|
||||||
|
WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+
|
||||||
|
6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B
|
||||||
|
Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa
|
||||||
|
qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q
|
||||||
|
4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
# CN=Sectigo Public Server Authentication Root R46,O=Sectigo Limited,C=GB
|
||||||
|
# 7bb647a62aeeac88bf257aa522d01ffea395e0ab45c73f93f65654ec38f25a06
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf
|
||||||
|
MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD
|
||||||
|
Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw
|
||||||
|
HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY
|
||||||
|
MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp
|
||||||
|
YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB
|
||||||
|
AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa
|
||||||
|
ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz
|
||||||
|
SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf
|
||||||
|
iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X
|
||||||
|
ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3
|
||||||
|
IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS
|
||||||
|
VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE
|
||||||
|
SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu
|
||||||
|
+Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt
|
||||||
|
8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L
|
||||||
|
HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt
|
||||||
|
zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P
|
||||||
|
AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c
|
||||||
|
mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ
|
||||||
|
YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52
|
||||||
|
gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA
|
||||||
|
Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB
|
||||||
|
JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX
|
||||||
|
DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui
|
||||||
|
TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5
|
||||||
|
dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65
|
||||||
|
LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp
|
||||||
|
0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY
|
||||||
|
QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL
|
||||||
|
-----END CERTIFICATE-----
|
||||||
# CN=Secure Global CA,O=SecureTrust Corporation,C=US
|
# CN=Secure Global CA,O=SecureTrust Corporation,C=US
|
||||||
# 4200f5043ac8590ebb527d209ed1503029fbcbd41ca1b506ec27f15ade7dac69
|
# 4200f5043ac8590ebb527d209ed1503029fbcbd41ca1b506ec27f15ade7dac69
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
|
|||||||
Reference in New Issue
Block a user