Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
325b735346 | ||
|
|
1eadac50a5 | ||
|
|
b2d7c26edb | ||
|
|
1c17e20020 | ||
|
|
270bf2552c | ||
|
|
1cf1811d71 | ||
|
|
a2edfb5072 | ||
|
|
ff15cd57d1 | ||
|
|
eb61739cd9 | ||
|
|
42c83fffff | ||
|
|
e668aa9b45 | ||
|
|
df0bc9e1bb | ||
|
|
74c2ba9521 | ||
|
|
cf8dcb0f7d | ||
|
|
4f30245552 | ||
|
|
1d57292618 | ||
|
|
8779cbd1c9 | ||
|
|
2aeefc3f8a | ||
|
|
e3cc52e598 | ||
|
|
833695f0a5 | ||
|
|
ec07f4e35b | ||
|
|
b665ba6f6f | ||
|
|
a1aeb9b34e | ||
|
|
28c53ff63c | ||
|
|
3f0842a464 | ||
|
|
e90f1e17ee | ||
|
|
d359caa4a3 | ||
|
|
c5370d2cc6 | ||
|
|
0d375be9b6 | ||
|
|
b4ddeeda5b | ||
|
|
edc325d13a | ||
|
|
eab9315960 | ||
|
|
ddfa82138e | ||
|
|
d08e19beac | ||
|
|
12e1fcd8c0 | ||
|
|
3f8f0645c2 | ||
|
|
5df3b59941 | ||
|
|
2e82bdd171 | ||
|
|
64e0e99383 | ||
|
|
23b1b90df2 | ||
|
|
e98487292d | ||
|
|
183630ada7 | ||
|
|
a9e447dde7 | ||
|
|
64c3993f5c | ||
|
|
5fe8145aca | ||
|
|
043e94c17a | ||
|
|
0d502d7cd6 | ||
|
|
0ff60057bb |
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.12
|
||||
// +build go1.12
|
||||
|
||||
package acme
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
package argon2
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || purego || !gc
|
||||
// +build !amd64 purego !gc
|
||||
|
||||
package argon2
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.7 && amd64 && gc && !purego
|
||||
// +build go1.7,amd64,gc,!purego
|
||||
|
||||
package blake2b
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.7 && amd64 && gc && !purego
|
||||
// +build go1.7,amd64,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.7 && amd64 && gc && !purego
|
||||
// +build !go1.7,amd64,gc,!purego
|
||||
|
||||
package blake2b
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || purego || !gc
|
||||
// +build !amd64 purego !gc
|
||||
|
||||
package blake2b
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
package blake2b
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build 386 && gc && !purego
|
||||
// +build 386,gc,!purego
|
||||
|
||||
package blake2s
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build 386 && gc && !purego
|
||||
// +build 386,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
package blake2s
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !386) || !gc || purego
|
||||
// +build !amd64,!386 !gc purego
|
||||
|
||||
package blake2s
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.9
|
||||
// +build go1.9
|
||||
|
||||
package blake2s
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.11 && gc && !purego
|
||||
// +build go1.11,gc,!purego
|
||||
//go:build gc && !purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.11 && gc && !purego
|
||||
// +build go1.11,gc,!purego
|
||||
//go:build gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!arm64 && !s390x && !ppc64le) || (arm64 && !go1.11) || !gc || purego
|
||||
// +build !arm64,!s390x,!ppc64le arm64,!go1.11 !gc purego
|
||||
//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
// due to the calling conventions and initialization of constants.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
package chacha20
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "textflag.h"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
package chacha20poly1305
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
// This file was originally from https://golang.org/cl/24717 by Vlad Krasnov of CloudFlare.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
// General register allocation
|
||||
@@ -184,11 +183,31 @@ GLOBL ·andMask<>(SB), (NOPTR+RODATA), $240
|
||||
#define shiftD1Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xd2; BYTE $0x04 // PALIGNR $4, X10, X10
|
||||
#define shiftD2Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x04 // PALIGNR $4, X11, X11
|
||||
#define shiftD3Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x04 // PALIGNR $4, X15, X15
|
||||
|
||||
// Some macros
|
||||
|
||||
// ROL rotates the uint32s in register R left by N bits, using temporary T.
|
||||
#define ROL(N, R, T) \
|
||||
MOVO R, T; PSLLL $(N), T; PSRLL $(32-(N)), R; PXOR T, R
|
||||
|
||||
// ROL16 rotates the uint32s in register R left by 16, using temporary T if needed.
|
||||
#ifdef GOAMD64_v2
|
||||
#define ROL16(R, T) PSHUFB ·rol16<>(SB), R
|
||||
#else
|
||||
#define ROL16(R, T) ROL(16, R, T)
|
||||
#endif
|
||||
|
||||
// ROL8 rotates the uint32s in register R left by 8, using temporary T if needed.
|
||||
#ifdef GOAMD64_v2
|
||||
#define ROL8(R, T) PSHUFB ·rol8<>(SB), R
|
||||
#else
|
||||
#define ROL8(R, T) ROL(8, R, T)
|
||||
#endif
|
||||
|
||||
#define chachaQR(A, B, C, D, T) \
|
||||
PADDD B, A; PXOR A, D; PSHUFB ·rol16<>(SB), D \
|
||||
PADDD B, A; PXOR A, D; ROL16(D, T) \
|
||||
PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $12, T; PSRLL $20, B; PXOR T, B \
|
||||
PADDD B, A; PXOR A, D; PSHUFB ·rol8<>(SB), D \
|
||||
PADDD B, A; PXOR A, D; ROL8(D, T) \
|
||||
PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $7, T; PSRLL $25, B; PXOR T, B
|
||||
|
||||
#define chachaQR_AVX2(A, B, C, D, T) \
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || !gc || purego
|
||||
// +build !amd64 !gc purego
|
||||
|
||||
package chacha20poly1305
|
||||
|
||||
|
||||
+7
-6
@@ -733,13 +733,14 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadOptionalASN1Boolean sets *out to the value of the next ASN.1 BOOLEAN or,
|
||||
// if the next bytes are not an ASN.1 BOOLEAN, to the value of defaultValue.
|
||||
// It reports whether the operation was successful.
|
||||
func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool {
|
||||
// ReadOptionalASN1Boolean attempts to read an optional ASN.1 BOOLEAN
|
||||
// explicitly tagged with tag into out and advances. If no element with a
|
||||
// matching tag is present, it sets "out" to defaultValue instead. It reports
|
||||
// whether the read was successful.
|
||||
func (s *String) ReadOptionalASN1Boolean(out *bool, tag asn1.Tag, defaultValue bool) bool {
|
||||
var present bool
|
||||
var child String
|
||||
if !s.ReadOptionalASN1(&child, &present, asn1.BOOLEAN) {
|
||||
if !s.ReadOptionalASN1(&child, &present, tag) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -748,7 +749,7 @@ func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
return s.ReadASN1Boolean(out)
|
||||
return child.ReadASN1Boolean(out)
|
||||
}
|
||||
|
||||
func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool {
|
||||
|
||||
@@ -115,6 +115,28 @@ func TestReadASN1OptionalInteger(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
const defaultBool = false
|
||||
|
||||
var optionalBoolTestData = []readASN1Test{
|
||||
{"empty", []byte{}, 0xa0, true, false},
|
||||
{"invalid", []byte{0xa1, 0x3, 0x1, 0x2, 0x7f}, 0xa1, false, defaultBool},
|
||||
{"missing", []byte{0xa1, 0x3, 0x1, 0x1, 0x7f}, 0xa0, true, defaultBool},
|
||||
{"present", []byte{0xa1, 0x3, 0x1, 0x1, 0xff}, 0xa1, true, true},
|
||||
}
|
||||
|
||||
func TestReadASN1OptionalBoolean(t *testing.T) {
|
||||
for _, test := range optionalBoolTestData {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
in := String(test.in)
|
||||
var out bool
|
||||
ok := in.ReadOptionalASN1Boolean(&out, test.tag, defaultBool)
|
||||
if ok != test.ok || ok && out != test.out.(bool) {
|
||||
t.Errorf("in.ReadOptionalASN1Boolean() = %v, want %v; out = %v, want %v", ok, test.ok, out, test.out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadASN1IntegerSigned(t *testing.T) {
|
||||
testData64 := []struct {
|
||||
in []byte
|
||||
|
||||
@@ -95,6 +95,11 @@ func (b *Builder) AddUint32(v uint32) {
|
||||
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.
|
||||
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))
|
||||
|
||||
@@ -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) {
|
||||
var b Builder
|
||||
b.AddUint64(0xf2fefefcff3cfdfc)
|
||||
|
||||
@@ -81,6 +81,17 @@ func (s *String) ReadUint32(out *uint32) bool {
|
||||
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.
|
||||
// It reports whether the read was successful.
|
||||
func (s *String) ReadUint64(out *uint64) bool {
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
module asm
|
||||
|
||||
go 1.16
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/mmcloughlin/avo v0.4.0
|
||||
golang.org/x/crypto v0.0.0
|
||||
github.com/mmcloughlin/avo v0.5.0
|
||||
golang.org/x/crypto v0.1.0
|
||||
)
|
||||
|
||||
replace golang.org/x/crypto v0.0.0 => ../../../..
|
||||
require (
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
)
|
||||
|
||||
replace golang.org/x/crypto v0.1.0 => ../../../..
|
||||
|
||||
@@ -1,34 +1,51 @@
|
||||
github.com/mmcloughlin/avo v0.4.0 h1:jeHDRktVD+578ULxWpQHkilor6pkdLF7u7EiTzDbfcU=
|
||||
github.com/mmcloughlin/avo v0.4.0/go.mod h1:RW9BfYA3TgO9uCdNrKU2h6J8cPD8ZLznvfgHAeszb1s=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
github.com/mmcloughlin/avo v0.5.0 h1:nAco9/aI9Lg2kiuROBY6BhCI/z0t5jEvJfjWbL8qXLU=
|
||||
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021 h1:giLT+HuUP/gXYrG2Plg9WTjj4qhfgaW424ZIFog3rlk=
|
||||
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.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.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
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.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/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.4.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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
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.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
package field
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || !gc || purego
|
||||
// +build !amd64 !gc purego
|
||||
|
||||
package field
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build arm64 && gc && !purego
|
||||
// +build arm64,gc,!purego
|
||||
|
||||
package field
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build arm64 && gc && !purego
|
||||
// +build arm64,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !arm64 || !gc || purego
|
||||
// +build !arm64 !gc purego
|
||||
|
||||
package field
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module golang.org/x/crypto
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.10.0 // tagx:ignore
|
||||
golang.org/x/sys v0.9.0
|
||||
golang.org/x/term v0.9.0
|
||||
golang.org/x/sys v0.15.0
|
||||
golang.org/x/term v0.15.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.10.0 // indirect
|
||||
require golang.org/x/text v0.14.0 // indirect
|
||||
|
||||
@@ -1,41 +1,8 @@
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||
golang.org/x/sys v0.9.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-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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
|
||||
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||
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.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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
|
||||
golang.org/x/text v0.10.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-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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
|
||||
+3
-1
@@ -56,7 +56,9 @@ func (f *hkdf) Read(p []byte) (int, error) {
|
||||
|
||||
// Fill the rest of the buffer
|
||||
for len(p) > 0 {
|
||||
f.expander.Reset()
|
||||
if f.counter > 1 {
|
||||
f.expander.Reset()
|
||||
}
|
||||
f.expander.Write(f.prev)
|
||||
f.expander.Write(f.info)
|
||||
f.expander.Write([]byte{f.counter})
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !purego
|
||||
// +build !purego
|
||||
|
||||
// Package alias implements memory aliasing tests.
|
||||
package alias
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build purego
|
||||
// +build purego
|
||||
|
||||
// Package alias implements memory aliasing tests.
|
||||
package alias
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.13
|
||||
// +build !go1.13
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.13
|
||||
// +build go1.13
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego
|
||||
// +build !amd64,!ppc64le,!s390x !gc purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
package poly1305
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package testenv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CommandContext is like exec.CommandContext, but:
|
||||
// - skips t if the platform does not support os/exec,
|
||||
// - sends SIGQUIT (if supported by the platform) instead of SIGKILL
|
||||
// in its Cancel function
|
||||
// - if the test has a deadline, adds a Context timeout and WaitDelay
|
||||
// for an arbitrary grace period before the test's deadline expires,
|
||||
// - fails the test if the command does not complete before the test's deadline, and
|
||||
// - sets a Cleanup function that verifies that the test did not leak a subprocess.
|
||||
func CommandContext(t testing.TB, ctx context.Context, name string, args ...string) *exec.Cmd {
|
||||
t.Helper()
|
||||
|
||||
var (
|
||||
cancelCtx context.CancelFunc
|
||||
gracePeriod time.Duration // unlimited unless the test has a deadline (to allow for interactive debugging)
|
||||
)
|
||||
|
||||
if t, ok := t.(interface {
|
||||
testing.TB
|
||||
Deadline() (time.Time, bool)
|
||||
}); ok {
|
||||
if td, ok := t.Deadline(); ok {
|
||||
// Start with a minimum grace period, just long enough to consume the
|
||||
// output of a reasonable program after it terminates.
|
||||
gracePeriod = 100 * time.Millisecond
|
||||
if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
|
||||
scale, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid GO_TEST_TIMEOUT_SCALE: %v", err)
|
||||
}
|
||||
gracePeriod *= time.Duration(scale)
|
||||
}
|
||||
|
||||
// If time allows, increase the termination grace period to 5% of the
|
||||
// test's remaining time.
|
||||
testTimeout := time.Until(td)
|
||||
if gp := testTimeout / 20; gp > gracePeriod {
|
||||
gracePeriod = gp
|
||||
}
|
||||
|
||||
// When we run commands that execute subprocesses, we want to reserve two
|
||||
// grace periods to clean up: one for the delay between the first
|
||||
// termination signal being sent (via the Cancel callback when the Context
|
||||
// expires) and the process being forcibly terminated (via the WaitDelay
|
||||
// field), and a second one for the delay becween the process being
|
||||
// terminated and and the test logging its output for debugging.
|
||||
//
|
||||
// (We want to ensure that the test process itself has enough time to
|
||||
// log the output before it is also terminated.)
|
||||
cmdTimeout := testTimeout - 2*gracePeriod
|
||||
|
||||
if cd, ok := ctx.Deadline(); !ok || time.Until(cd) > cmdTimeout {
|
||||
// Either ctx doesn't have a deadline, or its deadline would expire
|
||||
// after (or too close before) the test has already timed out.
|
||||
// Add a shorter timeout so that the test will produce useful output.
|
||||
ctx, cancelCtx = context.WithTimeout(ctx, cmdTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, name, args...)
|
||||
// Set the Cancel and WaitDelay fields only if present (go 1.20 and later).
|
||||
// TODO: When Go 1.19 is no longer supported, remove this use of reflection
|
||||
// and instead set the fields directly.
|
||||
if cmdCancel := reflect.ValueOf(cmd).Elem().FieldByName("Cancel"); cmdCancel.IsValid() {
|
||||
cmdCancel.Set(reflect.ValueOf(func() error {
|
||||
if cancelCtx != nil && ctx.Err() == context.DeadlineExceeded {
|
||||
// The command timed out due to running too close to the test's deadline.
|
||||
// There is no way the test did that intentionally — it's too close to the
|
||||
// wire! — so mark it as a test failure. That way, if the test expects the
|
||||
// command to fail for some other reason, it doesn't have to distinguish
|
||||
// between that reason and a timeout.
|
||||
t.Errorf("test timed out while running command: %v", cmd)
|
||||
} else {
|
||||
// The command is being terminated due to ctx being canceled, but
|
||||
// apparently not due to an explicit test deadline that we added.
|
||||
// Log that information in case it is useful for diagnosing a failure,
|
||||
// but don't actually fail the test because of it.
|
||||
t.Logf("%v: terminating command: %v", ctx.Err(), cmd)
|
||||
}
|
||||
return cmd.Process.Signal(Sigquit)
|
||||
}))
|
||||
}
|
||||
if cmdWaitDelay := reflect.ValueOf(cmd).Elem().FieldByName("WaitDelay"); cmdWaitDelay.IsValid() {
|
||||
cmdWaitDelay.Set(reflect.ValueOf(gracePeriod))
|
||||
}
|
||||
|
||||
t.Cleanup(func() {
|
||||
if cancelCtx != nil {
|
||||
cancelCtx()
|
||||
}
|
||||
if cmd.Process != nil && cmd.ProcessState == nil {
|
||||
t.Errorf("command was started, but test did not wait for it to complete: %v", cmd)
|
||||
}
|
||||
})
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Command is like exec.Command, but applies the same changes as
|
||||
// testenv.CommandContext (with a default Context).
|
||||
func Command(t testing.TB, name string, args ...string) *exec.Cmd {
|
||||
t.Helper()
|
||||
return CommandContext(t, context.Background(), name, args...)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows || plan9 || (js && wasm) || wasip1
|
||||
|
||||
package testenv
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Sigquit is the signal to send to kill a hanging subprocess.
|
||||
// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill.
|
||||
var Sigquit = os.Kill
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build unix
|
||||
|
||||
package testenv
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Sigquit is the signal to send to kill a hanging subprocess.
|
||||
// Send SIGQUIT to get a stack trace.
|
||||
var Sigquit = syscall.SIGQUIT
|
||||
@@ -3,14 +3,12 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.13
|
||||
// +build go1.13
|
||||
|
||||
package wycheproof
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
func TestEddsa(t *testing.T) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
@@ -28,6 +29,11 @@ const wycheproofModVer = "v0.0.0-20191219022705-2196000605e4"
|
||||
var wycheproofTestVectorsDir string
|
||||
|
||||
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 {
|
||||
log.Printf("skipping test because 'go' command is unavailable: %v", err)
|
||||
os.Exit(0)
|
||||
@@ -42,8 +48,6 @@ func TestMain(m *testing.M) {
|
||||
// can be used in the following tests.
|
||||
path := "github.com/google/wycheproof@" + wycheproofModVer
|
||||
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()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to run `go mod download -json %s`, output: %s", path, output)
|
||||
|
||||
+1
-1
@@ -21,9 +21,9 @@
|
||||
package sign
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/internal/alias"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.7
|
||||
// +build go1.7
|
||||
|
||||
package ocsp
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// This code can be compiled and used to test the otr package against libotr.
|
||||
// See otr_test.go.
|
||||
|
||||
// +build ignore
|
||||
//go:build ignore
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && !purego && gc
|
||||
// +build amd64,!purego,gc
|
||||
|
||||
package salsa
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && !purego && gc
|
||||
// +build amd64,!purego,gc
|
||||
|
||||
// This code was translated into a form compatible with 6a from the public
|
||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && !purego && gc
|
||||
// +build amd64,!purego,gc
|
||||
|
||||
package salsa
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || purego || !gc
|
||||
// +build !amd64 purego !gc
|
||||
|
||||
package salsa
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !gc || purego || !s390x
|
||||
// +build !gc purego !s390x
|
||||
|
||||
package sha3
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || purego || !gc
|
||||
// +build !amd64 purego !gc
|
||||
|
||||
package sha3
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && !purego && gc
|
||||
// +build amd64,!purego,gc
|
||||
|
||||
package sha3
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && !purego && gc
|
||||
// +build amd64,!purego,gc
|
||||
|
||||
// This code was translated into a form compatible with 6a from the public
|
||||
// domain sources at https://github.com/gvanas/KeccakCodePackage
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.4
|
||||
// +build go1.4
|
||||
|
||||
package sha3
|
||||
|
||||
|
||||
+9
-5
@@ -121,11 +121,11 @@ func (d *state) padAndPermute(dsbyte byte) {
|
||||
copyOut(d, d.buf)
|
||||
}
|
||||
|
||||
// Write absorbs more data into the hash's state. It produces an error
|
||||
// if more data is written to the ShakeHash after writing
|
||||
// Write absorbs more data into the hash's state. It panics if any
|
||||
// output has already been read.
|
||||
func (d *state) Write(p []byte) (written int, err error) {
|
||||
if d.state != spongeAbsorbing {
|
||||
panic("sha3: write to sponge after read")
|
||||
panic("sha3: Write after Read")
|
||||
}
|
||||
if d.buf == nil {
|
||||
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
|
||||
// number of output bytes.
|
||||
// number of output bytes. It panics if any output has already been read.
|
||||
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
|
||||
// and summing.
|
||||
dup := d.clone()
|
||||
hash := make([]byte, dup.outputLen)
|
||||
hash := make([]byte, dup.outputLen, 64) // explicit cap to allow stack allocation
|
||||
dup.Read(hash)
|
||||
return append(in, hash...)
|
||||
}
|
||||
|
||||
+6
-5
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
package sha3
|
||||
|
||||
@@ -49,7 +48,7 @@ type asmState struct {
|
||||
buf []byte // care must be taken to ensure cap(buf) is a multiple of rate
|
||||
rate int // equivalent to block size
|
||||
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
|
||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||
}
|
||||
@@ -72,8 +71,10 @@ func newAsmState(function code) *asmState {
|
||||
s.outputLen = 64
|
||||
case shake_128:
|
||||
s.rate = 168
|
||||
s.outputLen = 32
|
||||
case shake_256:
|
||||
s.rate = 136
|
||||
s.outputLen = 64
|
||||
default:
|
||||
panic("sha3: unrecognized function code")
|
||||
}
|
||||
@@ -108,7 +109,7 @@ func (s *asmState) resetBuf() {
|
||||
// It never returns an error.
|
||||
func (s *asmState) Write(b []byte) (int, error) {
|
||||
if s.state != spongeAbsorbing {
|
||||
panic("sha3: write to sponge after read")
|
||||
panic("sha3: Write after Read")
|
||||
}
|
||||
length := len(b)
|
||||
for len(b) > 0 {
|
||||
@@ -192,8 +193,8 @@ func (s *asmState) Read(out []byte) (n int, err error) {
|
||||
// Sum appends the current hash to b and returns the resulting slice.
|
||||
// It does not change the underlying hash state.
|
||||
func (s *asmState) Sum(b []byte) []byte {
|
||||
if s.outputLen == 0 {
|
||||
panic("sha3: cannot call Sum on SHAKE functions")
|
||||
if s.state != spongeAbsorbing {
|
||||
panic("sha3: Sum after Read")
|
||||
}
|
||||
|
||||
// Copy the state to preserve the original.
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build gc && !purego
|
||||
// +build gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
+14
-15
@@ -17,26 +17,25 @@ package sha3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ShakeHash defines the interface to hash functions that
|
||||
// support arbitrary-length output.
|
||||
// ShakeHash defines the interface to hash functions that support
|
||||
// arbitrary-length output. When used as a plain [hash.Hash], it
|
||||
// produces minimum-length outputs that provide full-strength generic
|
||||
// security.
|
||||
type ShakeHash interface {
|
||||
// Write absorbs more data into the hash's state. It panics if input is
|
||||
// written to it after output has been read from it.
|
||||
io.Writer
|
||||
hash.Hash
|
||||
|
||||
// Read reads more output from the hash; reading affects the hash's
|
||||
// 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
|
||||
|
||||
// Clone returns a copy of the ShakeHash in its current state.
|
||||
Clone() ShakeHash
|
||||
|
||||
// Reset resets the ShakeHash to its initial state.
|
||||
Reset()
|
||||
}
|
||||
|
||||
// cSHAKE specific context
|
||||
@@ -81,8 +80,8 @@ func leftEncode(value uint64) []byte {
|
||||
return b[i-1:]
|
||||
}
|
||||
|
||||
func newCShake(N, S []byte, rate int, dsbyte byte) ShakeHash {
|
||||
c := cshakeState{state: &state{rate: rate, dsbyte: dsbyte}}
|
||||
func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash {
|
||||
c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}}
|
||||
|
||||
// leftEncode returns max 9 bytes
|
||||
c.initBlock = make([]byte, 0, 9*2+len(N)+len(S))
|
||||
@@ -119,7 +118,7 @@ func NewShake128() ShakeHash {
|
||||
if h := newShake128Asm(); h != nil {
|
||||
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.
|
||||
@@ -129,7 +128,7 @@ func NewShake256() ShakeHash {
|
||||
if h := newShake256Asm(); h != nil {
|
||||
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,
|
||||
@@ -142,7 +141,7 @@ func NewCShake128(N, S []byte) ShakeHash {
|
||||
if len(N) == 0 && len(S) == 0 {
|
||||
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,
|
||||
@@ -155,7 +154,7 @@ func NewCShake256(N, S []byte) ShakeHash {
|
||||
if len(N) == 0 && len(S) == 0 {
|
||||
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.
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !gc || purego || !s390x
|
||||
// +build !gc purego !s390x
|
||||
|
||||
package sha3
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (!amd64 && !386 && !ppc64le) || purego
|
||||
// +build !amd64,!386,!ppc64le purego
|
||||
|
||||
package sha3
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build (amd64 || 386 || ppc64le) && !purego
|
||||
// +build amd64 386 ppc64le
|
||||
// +build !purego
|
||||
|
||||
package sha3
|
||||
|
||||
|
||||
+10
-5
@@ -16,6 +16,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
@@ -26,7 +27,6 @@ import (
|
||||
"math/big"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
@@ -141,9 +141,14 @@ const (
|
||||
agentAddSmartcardKeyConstrained = 26
|
||||
|
||||
// 3.7 Key constraint identifiers
|
||||
agentConstrainLifetime = 1
|
||||
agentConstrainConfirm = 2
|
||||
agentConstrainExtension = 3
|
||||
agentConstrainLifetime = 1
|
||||
agentConstrainConfirm = 2
|
||||
// Constraint extension identifier up to version 2 of the protocol. A
|
||||
// backward incompatible change will be required if we want to add support
|
||||
// for SSH_AGENT_CONSTRAIN_MAXSIGN which uses the same ID.
|
||||
agentConstrainExtensionV00 = 3
|
||||
// Constraint extension identifier in version 3 and later of the protocol.
|
||||
agentConstrainExtension = 255
|
||||
)
|
||||
|
||||
// maxAgentResponseBytes is the maximum agent reply size that is accepted. This
|
||||
@@ -205,7 +210,7 @@ type constrainLifetimeAgentMsg struct {
|
||||
}
|
||||
|
||||
type constrainExtensionAgentMsg struct {
|
||||
ExtensionName string `sshtype:"3"`
|
||||
ExtensionName string `sshtype:"255|3"`
|
||||
ExtensionDetails []byte
|
||||
|
||||
// Rest is a field used for parsing, not part of message
|
||||
|
||||
@@ -29,6 +29,9 @@ func startOpenSSHAgent(t *testing.T) (client ExtendedAgent, socket string, clean
|
||||
// types supported vary by platform.
|
||||
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")
|
||||
if err != nil {
|
||||
@@ -366,7 +369,8 @@ func TestAuth(t *testing.T) {
|
||||
go func() {
|
||||
conn, _, _, err := ssh.NewServerConn(a, &serverConf)
|
||||
if err != nil {
|
||||
t.Fatalf("Server: %v", err)
|
||||
t.Errorf("NewServerConn error: %v", err)
|
||||
return
|
||||
}
|
||||
conn.Close()
|
||||
}()
|
||||
|
||||
+2
-2
@@ -7,6 +7,7 @@ package agent
|
||||
import (
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
@@ -16,7 +17,6 @@ import (
|
||||
"log"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
@@ -208,7 +208,7 @@ func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse
|
||||
case agentConstrainConfirm:
|
||||
confirmBeforeUse = true
|
||||
constraints = constraints[1:]
|
||||
case agentConstrainExtension:
|
||||
case agentConstrainExtension, agentConstrainExtensionV00:
|
||||
var msg constrainExtensionAgentMsg
|
||||
if err = ssh.Unmarshal(constraints, &msg); err != nil {
|
||||
return 0, false, nil, err
|
||||
|
||||
@@ -53,10 +53,11 @@ func TestSetupForwardAgent(t *testing.T) {
|
||||
incoming := make(chan *ssh.ServerConn, 1)
|
||||
go func() {
|
||||
conn, _, _, err := ssh.NewServerConn(a, &serverConf)
|
||||
if err != nil {
|
||||
t.Fatalf("Server: %v", err)
|
||||
}
|
||||
incoming <- conn
|
||||
if err != nil {
|
||||
t.Errorf("NewServerConn error: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
conf := ssh.ClientConfig{
|
||||
@@ -71,8 +72,10 @@ func TestSetupForwardAgent(t *testing.T) {
|
||||
if err := ForwardToRemote(client, socket); err != nil {
|
||||
t.Fatalf("SetupForwardAgent: %v", err)
|
||||
}
|
||||
|
||||
server := <-incoming
|
||||
if server == nil {
|
||||
t.Fatal("Unable to get server")
|
||||
}
|
||||
ch, reqs, err := server.OpenChannel(channelType, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("OpenChannel(%q): %v", channelType, err)
|
||||
@@ -240,7 +243,11 @@ func TestParseConstraints(t *testing.T) {
|
||||
ExtensionDetails: []byte(fmt.Sprintf("details: %d", i)),
|
||||
}
|
||||
expect = append(expect, ext)
|
||||
data = append(data, agentConstrainExtension)
|
||||
if i%2 == 0 {
|
||||
data = append(data, agentConstrainExtension)
|
||||
} else {
|
||||
data = append(data, agentConstrainExtensionV00)
|
||||
}
|
||||
data = append(data, ssh.Marshal(ext)...)
|
||||
}
|
||||
_, _, extensions, err := parseConstraints(data)
|
||||
|
||||
@@ -6,6 +6,7 @@ package ssh
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
@@ -90,16 +91,16 @@ func BenchmarkEndToEnd(b *testing.B) {
|
||||
go func() {
|
||||
newCh, err := server.Accept()
|
||||
if err != nil {
|
||||
b.Fatalf("Client: %v", err)
|
||||
panic(fmt.Sprintf("Client: %v", err))
|
||||
}
|
||||
ch, incoming, err := newCh.Accept()
|
||||
if err != nil {
|
||||
b.Fatalf("Accept: %v", err)
|
||||
panic(fmt.Sprintf("Accept: %v", err))
|
||||
}
|
||||
go DiscardRequests(incoming)
|
||||
for i := 0; i < b.N; i++ {
|
||||
if _, err := io.ReadFull(ch, output); err != nil {
|
||||
b.Fatalf("ReadFull: %v", err)
|
||||
panic(fmt.Sprintf("ReadFull: %v", err))
|
||||
}
|
||||
}
|
||||
ch.Close()
|
||||
|
||||
+30
-8
@@ -16,8 +16,9 @@ import (
|
||||
|
||||
// Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear
|
||||
// in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms.
|
||||
// Unlike key algorithm names, these are not passed to AlgorithmSigner and don't
|
||||
// appear in the Signature.Format field.
|
||||
// Unlike key algorithm names, these are not passed to AlgorithmSigner nor
|
||||
// returned by MultiAlgorithmSigner and don't appear in the Signature.Format
|
||||
// field.
|
||||
const (
|
||||
CertAlgoRSAv01 = "ssh-rsa-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")
|
||||
}
|
||||
|
||||
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{
|
||||
&openSSHCertSigner{cert, signer}, algorithmSigner}, nil
|
||||
} else {
|
||||
&openSSHCertSigner{cert, signer}, s}, nil
|
||||
default:
|
||||
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,
|
||||
// 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 {
|
||||
c.Nonce = make([]byte, 32)
|
||||
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()
|
||||
|
||||
// Default to KeyAlgoRSASHA512 for ssh-rsa signers.
|
||||
if v, ok := authority.(AlgorithmSigner); ok && v.PublicKey().Type() == KeyAlgoRSA {
|
||||
if v, ok := authority.(MultiAlgorithmSigner); ok {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+94
-6
@@ -187,10 +187,30 @@ func TestHostKeyCert(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
addr string
|
||||
succeed bool
|
||||
addr string
|
||||
succeed bool
|
||||
certSignerAlgorithms []string // Empty means no algorithm restrictions.
|
||||
clientHostKeyAlgorithms []string
|
||||
}{
|
||||
{addr: "hostname:22", succeed: true},
|
||||
{
|
||||
addr: "hostname:22",
|
||||
succeed: true,
|
||||
certSignerAlgorithms: []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512},
|
||||
clientHostKeyAlgorithms: []string{CertAlgoRSASHA512v01},
|
||||
},
|
||||
{
|
||||
addr: "hostname:22",
|
||||
succeed: false,
|
||||
certSignerAlgorithms: []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512},
|
||||
clientHostKeyAlgorithms: []string{CertAlgoRSAv01},
|
||||
},
|
||||
{
|
||||
addr: "hostname:22",
|
||||
succeed: false,
|
||||
certSignerAlgorithms: []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512},
|
||||
clientHostKeyAlgorithms: []string{KeyAlgoRSASHA512}, // Not a certificate algorithm.
|
||||
},
|
||||
{addr: "otherhost:22", succeed: false}, // The certificate is valid for 'otherhost' as hostname, but we only recognize the authority of the signer for the address 'hostname:22'
|
||||
{addr: "lasthost:22", succeed: false},
|
||||
} {
|
||||
@@ -207,14 +227,24 @@ func TestHostKeyCert(t *testing.T) {
|
||||
conf := ServerConfig{
|
||||
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)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
config := &ClientConfig{
|
||||
User: "user",
|
||||
HostKeyCallback: checker.CheckHostKey,
|
||||
User: "user",
|
||||
HostKeyCallback: checker.CheckHostKey,
|
||||
HostKeyAlgorithms: test.clientHostKeyAlgorithms,
|
||||
}
|
||||
_, _, _, 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) {
|
||||
algorithmSigner, ok := testSigners["rsa"].(AlgorithmSigner)
|
||||
if !ok {
|
||||
t.Fatal("rsa test signer does not implement the AlgorithmSigner interface")
|
||||
}
|
||||
multiAlgoSignerSHA256, err := NewSignerWithAlgorithms(algorithmSigner, []string{KeyAlgoRSASHA256})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create multi algorithm signer SHA256: %v", err)
|
||||
}
|
||||
// Algorithms are in order of preference, we expect rsa-sha2-512 to be used.
|
||||
multiAlgoSignerSHA512, err := NewSignerWithAlgorithms(algorithmSigner, []string{KeyAlgoRSASHA512, KeyAlgoRSASHA256})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create multi algorithm signer SHA512: %v", err)
|
||||
}
|
||||
|
||||
var testVars = []struct {
|
||||
name string
|
||||
signer Signer
|
||||
@@ -251,8 +295,10 @@ func TestCertTypes(t *testing.T) {
|
||||
{CertAlgoECDSA384v01, testSigners["ecdsap384"], ""},
|
||||
{CertAlgoECDSA521v01, testSigners["ecdsap521"], ""},
|
||||
{CertAlgoED25519v01, testSigners["ed25519"], ""},
|
||||
{CertAlgoRSAv01, testSigners["rsa"], KeyAlgoRSASHA512},
|
||||
{CertAlgoRSAv01, testSigners["rsa"], KeyAlgoRSASHA256},
|
||||
{"legacyRSASigner", &legacyRSASigner{testSigners["rsa"]}, KeyAlgoRSA},
|
||||
{"multiAlgoRSASignerSHA256", multiAlgoSignerSHA256, KeyAlgoRSASHA256},
|
||||
{"multiAlgoRSASignerSHA512", multiAlgoSignerSHA512, KeyAlgoRSASHA512},
|
||||
{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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+85
-31
@@ -71,7 +71,9 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
for auth := AuthMethod(new(noneAuth)); auth != nil; {
|
||||
ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions)
|
||||
if err != nil {
|
||||
return err
|
||||
// We return the error later if there is no other method left to
|
||||
// try.
|
||||
ok = authFailure
|
||||
}
|
||||
if ok == authSuccess {
|
||||
// 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)
|
||||
}
|
||||
@@ -217,21 +225,45 @@ func (cb publicKeyCallback) method() string {
|
||||
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()
|
||||
|
||||
// Like in sendKexInit, if the public key implements AlgorithmSigner we
|
||||
// assume it supports all algorithms, otherwise only the key format one.
|
||||
as, ok := signer.(AlgorithmSigner)
|
||||
if !ok {
|
||||
return algorithmSignerWrapper{signer}, keyFormat
|
||||
// If the signer implements MultiAlgorithmSigner we use the algorithms it
|
||||
// support, if it implements AlgorithmSigner we assume it supports all
|
||||
// algorithms, otherwise only the key format one.
|
||||
switch s := signer.(type) {
|
||||
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"]
|
||||
if !ok {
|
||||
// If there is no "server-sig-algs" extension, fall back to the key
|
||||
// format algorithm.
|
||||
return as, keyFormat
|
||||
// If there is no "server-sig-algs" extension use the fallback
|
||||
// algorithm.
|
||||
algo, err := getFallbackAlgo()
|
||||
return as, algo, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
// If there is no overlap, try the key anyway with the key format
|
||||
// algorithm, to support servers that fail to list all supported
|
||||
// algorithms.
|
||||
return as, keyFormat
|
||||
// If there is no overlap, return the fallback algorithm to support
|
||||
// servers that fail to list all supported algorithms.
|
||||
algo, err := getFallbackAlgo()
|
||||
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) {
|
||||
@@ -267,14 +306,39 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
return authFailure, nil, err
|
||||
}
|
||||
var methods []string
|
||||
for _, signer := range signers {
|
||||
pub := signer.PublicKey()
|
||||
as, algo := pickSignatureAlgorithm(signer, extensions)
|
||||
var errSigAlgo error
|
||||
|
||||
origSignersLen := len(signers)
|
||||
for idx := 0; idx < len(signers); idx++ {
|
||||
signer := signers[idx]
|
||||
pub := signer.PublicKey()
|
||||
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)
|
||||
if err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
// OpenSSH 7.2-7.7 advertises support for rsa-sha2-256 and rsa-sha2-512
|
||||
// in the "server-sig-algs" extension but doesn't support these
|
||||
// algorithms for certificate authentication, so if the server rejects
|
||||
// the key try to use the obtained algorithm as if "server-sig-algs" had
|
||||
// not been implemented if supported from the algorithm signer.
|
||||
if !ok && idx < origSignersLen && isRSACert(algo) && algo != CertAlgoRSAv01 {
|
||||
if contains(as.Algorithms(), KeyAlgoRSA) {
|
||||
// We retry using the compat algorithm after all signers have
|
||||
// been tried normally.
|
||||
signers = append(signers, &multiAlgorithmSigner{
|
||||
AlgorithmSigner: as,
|
||||
supportedAlgorithms: []string{KeyAlgoRSA},
|
||||
})
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -317,22 +381,12 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
// contain the "publickey" method, do not attempt to authenticate with any
|
||||
// other keys. According to RFC 4252 Section 7, the latter can occur when
|
||||
// additional authentication methods are required.
|
||||
if success == authSuccess || !containsMethod(methods, cb.method()) {
|
||||
if success == authSuccess || !contains(methods, cb.method()) {
|
||||
return success, methods, err
|
||||
}
|
||||
}
|
||||
|
||||
return authFailure, methods, nil
|
||||
}
|
||||
|
||||
func containsMethod(methods []string, method string) bool {
|
||||
for _, m := range methods {
|
||||
if m == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return authFailure, methods, errSigAlgo
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+46
-16
@@ -10,7 +10,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
_ "crypto/sha1"
|
||||
@@ -49,7 +48,8 @@ var supportedKexAlgos = []string{
|
||||
// P384 and P521 are not constant-time yet, but since we don't
|
||||
// reuse ephemeral keys, using them for ECDH should be OK.
|
||||
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
|
||||
kexAlgoDH14SHA256, kexAlgoDH14SHA1, kexAlgoDH1SHA1,
|
||||
kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
|
||||
kexAlgoDH1SHA1,
|
||||
}
|
||||
|
||||
// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
|
||||
@@ -59,8 +59,9 @@ var serverForbiddenKexAlgos = map[string]struct{}{
|
||||
kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
|
||||
}
|
||||
|
||||
// preferredKexAlgos specifies the default preference for key-exchange algorithms
|
||||
// in preference order.
|
||||
// preferredKexAlgos specifies the default preference for key-exchange
|
||||
// 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{
|
||||
kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
|
||||
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
|
||||
@@ -70,12 +71,12 @@ var preferredKexAlgos = []string{
|
||||
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
|
||||
// of authenticating servers) in preference order.
|
||||
var supportedHostKeyAlgos = []string{
|
||||
CertAlgoRSASHA512v01, CertAlgoRSASHA256v01,
|
||||
CertAlgoRSASHA256v01, CertAlgoRSASHA512v01,
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
||||
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
|
||||
|
||||
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||
KeyAlgoRSASHA512, KeyAlgoRSASHA256,
|
||||
KeyAlgoRSASHA256, KeyAlgoRSASHA512,
|
||||
KeyAlgoRSA, KeyAlgoDSA,
|
||||
|
||||
KeyAlgoED25519,
|
||||
@@ -85,7 +86,7 @@ var supportedHostKeyAlgos = []string{
|
||||
// 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.
|
||||
var supportedMACs = []string{
|
||||
"hmac-sha2-512-etm@openssh.com", "hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "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}
|
||||
@@ -119,6 +120,21 @@ 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))
|
||||
}
|
||||
|
||||
func isRSACert(algo string) bool {
|
||||
_, ok := certKeyAlgoNames[algo]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return isRSA(algo)
|
||||
}
|
||||
|
||||
// supportedPubKeyAuthAlgos specifies the supported client public key
|
||||
// authentication algorithms. Note that this doesn't include certificate types
|
||||
// since those use the underlying algorithm. This list is sent to the client if
|
||||
@@ -131,8 +147,6 @@ var supportedPubKeyAuthAlgos = []string{
|
||||
KeyAlgoDSA,
|
||||
}
|
||||
|
||||
var supportedPubKeyAuthAlgosList = strings.Join(supportedPubKeyAuthAlgos, ",")
|
||||
|
||||
// unexpectedMessageError results when the SSH message that we received didn't
|
||||
// match what we wanted.
|
||||
func unexpectedMessageError(expected, got uint8) error {
|
||||
@@ -262,16 +276,16 @@ type Config struct {
|
||||
// unspecified, a size suitable for the chosen cipher is used.
|
||||
RekeyThreshold uint64
|
||||
|
||||
// The allowed key exchanges algorithms. If unspecified then a
|
||||
// default set of algorithms is used.
|
||||
// The allowed key exchanges algorithms. If unspecified then a default set
|
||||
// of algorithms is used. Unsupported values are silently ignored.
|
||||
KeyExchanges []string
|
||||
|
||||
// The allowed cipher algorithms. If unspecified then a sensible
|
||||
// default is used.
|
||||
// The allowed cipher algorithms. If unspecified then a sensible default is
|
||||
// used. Unsupported values are silently ignored.
|
||||
Ciphers []string
|
||||
|
||||
// The allowed MAC algorithms. If unspecified then a sensible default
|
||||
// is used.
|
||||
// The allowed MAC algorithms. If unspecified then a sensible default is
|
||||
// used. Unsupported values are silently ignored.
|
||||
MACs []string
|
||||
}
|
||||
|
||||
@@ -288,7 +302,7 @@ func (c *Config) SetDefaults() {
|
||||
var ciphers []string
|
||||
for _, c := range c.Ciphers {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -297,10 +311,26 @@ func (c *Config) SetDefaults() {
|
||||
if c.KeyExchanges == nil {
|
||||
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 {
|
||||
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 {
|
||||
// cipher specific default
|
||||
|
||||
+5
-5
@@ -82,11 +82,11 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
||||
}
|
||||
|
||||
cases := []testcase{
|
||||
testcase{
|
||||
{
|
||||
name: "standard",
|
||||
},
|
||||
|
||||
testcase{
|
||||
{
|
||||
name: "no common hostkey",
|
||||
serverIn: kexInitMsg{
|
||||
ServerHostKeyAlgos: []string{"hostkey2"},
|
||||
@@ -94,7 +94,7 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
testcase{
|
||||
{
|
||||
name: "no common kex",
|
||||
serverIn: kexInitMsg{
|
||||
KexAlgos: []string{"kex2"},
|
||||
@@ -102,7 +102,7 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
testcase{
|
||||
{
|
||||
name: "no common cipher",
|
||||
serverIn: kexInitMsg{
|
||||
CiphersClientServer: []string{"cipher2"},
|
||||
@@ -110,7 +110,7 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
||||
wantErr: true,
|
||||
},
|
||||
|
||||
testcase{
|
||||
{
|
||||
name: "client decides cipher",
|
||||
serverIn: kexInitMsg{
|
||||
CiphersClientServer: []string{"cipher1", "cipher2"},
|
||||
|
||||
@@ -13,6 +13,7 @@ others.
|
||||
|
||||
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
|
||||
[SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
|
||||
|
||||
|
||||
+83
-3
@@ -7,6 +7,8 @@ package ssh_test
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
@@ -14,6 +16,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
@@ -75,7 +78,6 @@ func ExampleNewServerConn() {
|
||||
if err != nil {
|
||||
log.Fatal("Failed to parse private key: ", err)
|
||||
}
|
||||
|
||||
config.AddHostKey(private)
|
||||
|
||||
// Once a ServerConfig has been configured, connections can be
|
||||
@@ -97,8 +99,15 @@ func ExampleNewServerConn() {
|
||||
}
|
||||
log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
|
||||
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
// The incoming Request channel must be serviced.
|
||||
go ssh.DiscardRequests(reqs)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
ssh.DiscardRequests(reqs)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// Service the incoming Channel channel.
|
||||
for newChannel := range chans {
|
||||
@@ -118,16 +127,22 @@ func ExampleNewServerConn() {
|
||||
// Sessions have out-of-band requests such as "shell",
|
||||
// "pty-req" and "env". Here we handle only the
|
||||
// "shell" request.
|
||||
wg.Add(1)
|
||||
go func(in <-chan *ssh.Request) {
|
||||
for req := range in {
|
||||
req.Reply(req.Type == "shell", nil)
|
||||
}
|
||||
wg.Done()
|
||||
}(requests)
|
||||
|
||||
term := terminal.NewTerminal(channel, "> ")
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer channel.Close()
|
||||
defer func() {
|
||||
channel.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
for {
|
||||
line, err := term.ReadLine()
|
||||
if err != nil {
|
||||
@@ -139,6 +154,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() {
|
||||
// Every client must provide a host key check. Here is a
|
||||
// simple-minded parse of OpenSSH's known_hosts file
|
||||
@@ -318,3 +363,38 @@ func ExampleSession_RequestPty() {
|
||||
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)))
|
||||
}
|
||||
|
||||
+37
-14
@@ -11,6 +11,7 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -50,6 +51,10 @@ type handshakeTransport struct {
|
||||
// connection.
|
||||
hostKeys []Signer
|
||||
|
||||
// publicKeyAuthAlgorithms is non-empty if we are the server. In that case,
|
||||
// it contains the supported client public key authentication algorithms.
|
||||
publicKeyAuthAlgorithms []string
|
||||
|
||||
// hostKeyAlgorithms is non-empty if we are the client. In that case,
|
||||
// we accept these key types from the server as host key.
|
||||
hostKeyAlgorithms []string
|
||||
@@ -141,6 +146,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
|
||||
func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport {
|
||||
t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion)
|
||||
t.hostKeys = config.hostKeys
|
||||
t.publicKeyAuthAlgorithms = config.PublicKeyAuthAlgorithms
|
||||
go t.readLoop()
|
||||
go t.kexLoop()
|
||||
return t
|
||||
@@ -461,19 +467,24 @@ func (t *handshakeTransport) sendKexInit() error {
|
||||
isServer := len(t.hostKeys) > 0
|
||||
if isServer {
|
||||
for _, k := range t.hostKeys {
|
||||
// If k is an AlgorithmSigner, presume it supports all signature algorithms
|
||||
// associated with the key format. (Ideally AlgorithmSigner would have a
|
||||
// method to advertise supported algorithms, but it doesn't. This means that
|
||||
// adding support for a new algorithm is a breaking change, as we will
|
||||
// immediately negotiate it even if existing implementations don't support
|
||||
// it. If that ever happens, we'll have to figure something out.)
|
||||
// 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.)
|
||||
// If k is a MultiAlgorithmSigner, we restrict the signature
|
||||
// algorithms. If k is a AlgorithmSigner, presume it supports all
|
||||
// signature algorithms associated with the key format. 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()
|
||||
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)...)
|
||||
} else {
|
||||
default:
|
||||
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat)
|
||||
}
|
||||
}
|
||||
@@ -642,16 +653,21 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||
|
||||
// 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
|
||||
// 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") {
|
||||
supportedPubKeyAuthAlgosList := strings.Join(t.publicKeyAuthAlgorithms, ",")
|
||||
extInfo := &extInfoMsg{
|
||||
NumExtensions: 1,
|
||||
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)),
|
||||
NumExtensions: 2,
|
||||
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)+4+16+4+1),
|
||||
}
|
||||
extInfo.Payload = appendInt(extInfo.Payload, len("server-sig-algs"))
|
||||
extInfo.Payload = append(extInfo.Payload, "server-sig-algs"...)
|
||||
extInfo.Payload = appendInt(extInfo.Payload, len(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 {
|
||||
return err
|
||||
}
|
||||
@@ -685,9 +701,16 @@ func (a algorithmSignerWrapper) SignWithAlgorithm(rand io.Reader, data []byte, a
|
||||
|
||||
func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner {
|
||||
for _, k := range hostKeys {
|
||||
if s, ok := k.(MultiAlgorithmSigner); ok {
|
||||
if !contains(s.Algorithms(), underlyingAlgo(algo)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if algo == k.PublicKey().Type() {
|
||||
return algorithmSignerWrapper{k}
|
||||
}
|
||||
|
||||
k, ok := k.(AlgorithmSigner)
|
||||
if !ok {
|
||||
continue
|
||||
|
||||
+107
-15
@@ -148,6 +148,7 @@ func TestHandshakeBasic(t *testing.T) {
|
||||
clientDone := make(chan int, 0)
|
||||
gotHalf := make(chan int, 0)
|
||||
const N = 20
|
||||
errorCh := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
defer close(clientDone)
|
||||
@@ -158,7 +159,9 @@ func TestHandshakeBasic(t *testing.T) {
|
||||
for i := 0; i < N; i++ {
|
||||
p := []byte{msgRequestSuccess, byte(i)}
|
||||
if err := trC.writePacket(p); err != nil {
|
||||
t.Fatalf("sendPacket: %v", err)
|
||||
errorCh <- err
|
||||
trC.Close()
|
||||
return
|
||||
}
|
||||
if (i % 10) == 5 {
|
||||
<-gotHalf
|
||||
@@ -177,16 +180,15 @@ func TestHandshakeBasic(t *testing.T) {
|
||||
checker.waitCall <- 1
|
||||
}
|
||||
}
|
||||
errorCh <- nil
|
||||
}()
|
||||
|
||||
// Server checks that client messages come in cleanly
|
||||
i := 0
|
||||
err = nil
|
||||
for ; i < N; i++ {
|
||||
var p []byte
|
||||
p, err = trS.readPacket()
|
||||
if err != nil {
|
||||
break
|
||||
p, err := trS.readPacket()
|
||||
if err != nil && err != io.EOF {
|
||||
t.Fatalf("server error: %v", err)
|
||||
}
|
||||
if (i % 10) == 5 {
|
||||
gotHalf <- 1
|
||||
@@ -198,8 +200,8 @@ func TestHandshakeBasic(t *testing.T) {
|
||||
}
|
||||
}
|
||||
<-clientDone
|
||||
if err != nil && err != io.EOF {
|
||||
t.Fatalf("server error: %v", err)
|
||||
if err := <-errorCh; err != nil {
|
||||
t.Fatalf("sendPacket: %v", err)
|
||||
}
|
||||
if i != N {
|
||||
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
|
||||
// initiated.
|
||||
done := make(chan int, 1)
|
||||
errorCh := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(done)
|
||||
if _, err := trC.readPacket(); err != nil {
|
||||
t.Fatalf("readPacket(client): %v", err)
|
||||
}
|
||||
|
||||
_, err := trC.readPacket()
|
||||
errorCh <- err
|
||||
}()
|
||||
|
||||
<-done
|
||||
if err := <-errorCh; err != nil {
|
||||
t.Fatalf("readPacket(client): %v", err)
|
||||
}
|
||||
|
||||
<-sync.called
|
||||
}
|
||||
|
||||
@@ -618,3 +620,93 @@ func TestNoSHA2Support(t *testing.T) {
|
||||
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"
|
||||
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
|
||||
kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
|
||||
kexAlgoDH16SHA512 = "diffie-hellman-group16-sha512"
|
||||
kexAlgoECDH256 = "ecdh-sha2-nistp256"
|
||||
kexAlgoECDH384 = "ecdh-sha2-nistp384"
|
||||
kexAlgoECDH521 = "ecdh-sha2-nistp521"
|
||||
@@ -430,6 +431,17 @@ func init() {
|
||||
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[kexAlgoECDH384] = &ecdh{elliptic.P384()}
|
||||
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
|
||||
|
||||
@@ -8,6 +8,7 @@ package ssh
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"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))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+338
-57
@@ -11,13 +11,16 @@ import (
|
||||
"crypto/cipher"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
@@ -26,7 +29,6 @@ import (
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/ssh/internal/bcrypt_pbkdf"
|
||||
)
|
||||
|
||||
@@ -295,6 +297,18 @@ func MarshalAuthorizedKey(key PublicKey) []byte {
|
||||
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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Some Signers provided by this package also implement AlgorithmSigner.
|
||||
// Some Signers provided by this package also implement MultiAlgorithmSigner.
|
||||
type Signer interface {
|
||||
// PublicKey returns the associated PublicKey.
|
||||
PublicKey() PublicKey
|
||||
@@ -336,9 +350,9 @@ type Signer interface {
|
||||
// An AlgorithmSigner is a Signer that also supports specifying an algorithm to
|
||||
// use for signing.
|
||||
//
|
||||
// An AlgorithmSigner can't advertise the algorithms it supports, so it should
|
||||
// be prepared to be invoked with every algorithm supported by the public key
|
||||
// format.
|
||||
// An AlgorithmSigner can't advertise the algorithms it supports, unless it also
|
||||
// implements MultiAlgorithmSigner, so it should be prepared to be invoked with
|
||||
// every algorithm supported by the public key format.
|
||||
type AlgorithmSigner interface {
|
||||
Signer
|
||||
|
||||
@@ -349,6 +363,75 @@ type AlgorithmSigner interface {
|
||||
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
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
func (k *dsaPrivateKey) Algorithms() []string {
|
||||
return []string{k.PublicKey().Type()}
|
||||
}
|
||||
|
||||
func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
|
||||
if algorithm != "" && algorithm != k.PublicKey().Type() {
|
||||
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())
|
||||
}
|
||||
|
||||
func (s *wrappedSigner) Algorithms() []string {
|
||||
return algorithmsForKeyFormat(s.pubKey.Type())
|
||||
}
|
||||
|
||||
func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
|
||||
if algorithm == "" {
|
||||
algorithm = s.pubKey.Type()
|
||||
}
|
||||
|
||||
supportedAlgos := algorithmsForKeyFormat(s.pubKey.Type())
|
||||
if !contains(supportedAlgos, algorithm) {
|
||||
if !contains(s.Algorithms(), algorithm) {
|
||||
return nil, fmt.Errorf("ssh: unsupported signature algorithm %q for key format %q", algorithm, s.pubKey.Type())
|
||||
}
|
||||
|
||||
@@ -1142,16 +1232,27 @@ func ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase []byte) (interface{},
|
||||
return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err)
|
||||
}
|
||||
|
||||
var result interface{}
|
||||
|
||||
switch block.Type {
|
||||
case "RSA PRIVATE KEY":
|
||||
return x509.ParsePKCS1PrivateKey(buf)
|
||||
result, err = x509.ParsePKCS1PrivateKey(buf)
|
||||
case "EC PRIVATE KEY":
|
||||
return x509.ParseECPrivateKey(buf)
|
||||
result, err = x509.ParseECPrivateKey(buf)
|
||||
case "DSA PRIVATE KEY":
|
||||
return ParseDSAPrivateKey(buf)
|
||||
result, err = ParseDSAPrivateKey(buf)
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
|
||||
err = fmt.Errorf("ssh: unsupported key type %q", block.Type)
|
||||
}
|
||||
// Because of deficiencies in the format, DecryptPEMBlock does not always
|
||||
// detect an incorrect password. In these cases decrypted DER bytes is
|
||||
// random noise. If the parsing of the key returns an asn1.StructuralError
|
||||
// we return x509.IncorrectPasswordError.
|
||||
if _, ok := err.(asn1.StructuralError); ok {
|
||||
return nil, x509.IncorrectPasswordError
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as
|
||||
@@ -1241,28 +1342,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 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
|
||||
// function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used
|
||||
// as the decrypt function to parse an unencrypted private key. See
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.
|
||||
func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.PrivateKey, error) {
|
||||
const magic = "openssh-key-v1\x00"
|
||||
if len(key) < len(magic) || string(key[:len(magic)]) != magic {
|
||||
if len(key) < len(privateKeyAuthMagic) || string(key[:len(privateKeyAuthMagic)]) != privateKeyAuthMagic {
|
||||
return nil, errors.New("ssh: invalid openssh private key format")
|
||||
}
|
||||
remaining := key[len(magic):]
|
||||
|
||||
var w struct {
|
||||
CipherName string
|
||||
KdfName string
|
||||
KdfOpts string
|
||||
NumKeys uint32
|
||||
PubKey []byte
|
||||
PrivKeyBlock []byte
|
||||
}
|
||||
remaining := key[len(privateKeyAuthMagic):]
|
||||
|
||||
var w openSSHEncryptedPrivateKey
|
||||
if err := Unmarshal(remaining, &w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1284,13 +1463,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk1 := struct {
|
||||
Check1 uint32
|
||||
Check2 uint32
|
||||
Keytype string
|
||||
Rest []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
var pk1 openSSHPrivateKey
|
||||
if err := Unmarshal(privKeyBlock, &pk1); err != nil || pk1.Check1 != pk1.Check2 {
|
||||
if w.CipherName != "none" {
|
||||
return nil, x509.IncorrectPasswordError
|
||||
@@ -1300,18 +1473,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
||||
|
||||
switch pk1.Keytype {
|
||||
case KeyAlgoRSA:
|
||||
// https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773
|
||||
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"`
|
||||
}{}
|
||||
|
||||
var key openSSHRSAPrivateKey
|
||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1337,13 +1499,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
||||
|
||||
return pk, nil
|
||||
case KeyAlgoED25519:
|
||||
key := struct {
|
||||
Pub []byte
|
||||
Priv []byte
|
||||
Comment string
|
||||
Pad []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
var key openSSHEd25519PrivateKey
|
||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1360,14 +1516,7 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
||||
copy(pk, key.Priv)
|
||||
return &pk, nil
|
||||
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
|
||||
key := struct {
|
||||
Curve string
|
||||
Pub []byte
|
||||
D *big.Int
|
||||
Comment string
|
||||
Pad []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
var key openSSHECDSAPrivateKey
|
||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1415,6 +1564,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 {
|
||||
for i, b := range pad {
|
||||
if int(b) != i+1 {
|
||||
@@ -1424,6 +1698,13 @@ func checkOpenSSHKeyPadding(pad []byte) error {
|
||||
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
|
||||
// fingerprint as described by RFC 4716 section 4.
|
||||
func FingerprintLegacyMD5(pubKey PublicKey) string {
|
||||
|
||||
+114
-4
@@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
@@ -15,13 +16,13 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/ssh/testdata"
|
||||
)
|
||||
|
||||
@@ -111,9 +112,9 @@ func TestKeySignVerify(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestKeySignWithAlgorithmVerify(t *testing.T) {
|
||||
for _, priv := range testSigners {
|
||||
if algorithmSigner, ok := priv.(AlgorithmSigner); !ok {
|
||||
t.Errorf("Signers constructed by ssh package should always implement the AlgorithmSigner interface: %T", priv)
|
||||
for k, priv := range testSigners {
|
||||
if algorithmSigner, ok := priv.(MultiAlgorithmSigner); !ok {
|
||||
t.Errorf("Signers %q constructed by ssh package should always implement the MultiAlgorithmSigner interface: %T", k, priv)
|
||||
} else {
|
||||
pub := priv.PublicKey()
|
||||
data := []byte("sign me")
|
||||
@@ -221,6 +222,16 @@ func TestParseEncryptedPrivateKeysWithPassphrase(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEncryptedPrivateKeysWithIncorrectPassphrase(t *testing.T) {
|
||||
pem := testdata.PEMEncryptedKeys[0].PEMBytes
|
||||
for i := 0; i < 4096; i++ {
|
||||
_, err := ParseRawPrivateKeyWithPassphrase(pem, []byte(fmt.Sprintf("%d", i)))
|
||||
if !errors.Is(err, x509.IncorrectPasswordError) {
|
||||
t.Fatalf("expected error: %v, got: %v", x509.IncorrectPasswordError, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDSA(t *testing.T) {
|
||||
// We actually exercise the ParsePrivateKey codepath here, as opposed to
|
||||
// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
|
||||
@@ -281,6 +292,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 {
|
||||
pubKey PublicKey
|
||||
options []string
|
||||
@@ -616,3 +695,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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,9 @@ var macModes = map[string]*macMode{
|
||||
"hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha256.New, key)
|
||||
}},
|
||||
"hmac-sha2-512": {64, false, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha512.New, key)
|
||||
}},
|
||||
"hmac-sha2-256": {32, false, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha256.New, key)
|
||||
}},
|
||||
|
||||
@@ -349,6 +349,20 @@ type userAuthGSSAPIError struct {
|
||||
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
|
||||
// should be a struct. The possible values are separated by a '|' character.
|
||||
func typeTags(structType reflect.Type) (tags []byte) {
|
||||
|
||||
@@ -231,6 +231,12 @@ func (m *mux) onePacket() error {
|
||||
return m.handleChannelOpen(packet)
|
||||
case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
|
||||
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.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user