Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d2ee975ef | ||
|
|
4e5a26183e | ||
|
|
152cdb1503 | ||
|
|
fdfe1f8531 | ||
|
|
b8ffc16e10 | ||
|
|
7e6fbd82c8 | ||
|
|
bda2f3f5cf | ||
|
|
325b735346 | ||
|
|
1eadac50a5 | ||
|
|
b2d7c26edb | ||
|
|
1c17e20020 | ||
|
|
270bf2552c | ||
|
|
1cf1811d71 | ||
|
|
a2edfb5072 | ||
|
|
ff15cd57d1 | ||
|
|
eb61739cd9 | ||
|
|
42c83fffff | ||
|
|
e668aa9b45 | ||
|
|
df0bc9e1bb | ||
|
|
74c2ba9521 | ||
|
|
cf8dcb0f7d | ||
|
|
4f30245552 | ||
|
|
1d57292618 | ||
|
|
8779cbd1c9 | ||
|
|
2aeefc3f8a |
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.12
|
//go:build go1.12
|
||||||
// +build go1.12
|
|
||||||
|
|
||||||
package acme
|
package acme
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && gc && !purego
|
//go:build amd64 && gc && !purego
|
||||||
// +build amd64,gc,!purego
|
|
||||||
|
|
||||||
package argon2
|
package argon2
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && gc && !purego
|
//go:build amd64 && gc && !purego
|
||||||
// +build amd64,gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
@@ -200,8 +199,8 @@ TEXT ·mixBlocksSSE2(SB), 4, $0-32
|
|||||||
MOVQ out+0(FP), DX
|
MOVQ out+0(FP), DX
|
||||||
MOVQ a+8(FP), AX
|
MOVQ a+8(FP), AX
|
||||||
MOVQ b+16(FP), BX
|
MOVQ b+16(FP), BX
|
||||||
MOVQ a+24(FP), CX
|
MOVQ c+24(FP), CX
|
||||||
MOVQ $128, BP
|
MOVQ $128, DI
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
MOVOU 0(AX), X0
|
MOVOU 0(AX), X0
|
||||||
@@ -214,7 +213,7 @@ loop:
|
|||||||
ADDQ $16, BX
|
ADDQ $16, BX
|
||||||
ADDQ $16, CX
|
ADDQ $16, CX
|
||||||
ADDQ $16, DX
|
ADDQ $16, DX
|
||||||
SUBQ $2, BP
|
SUBQ $2, DI
|
||||||
JA loop
|
JA loop
|
||||||
RET
|
RET
|
||||||
|
|
||||||
@@ -223,8 +222,8 @@ TEXT ·xorBlocksSSE2(SB), 4, $0-32
|
|||||||
MOVQ out+0(FP), DX
|
MOVQ out+0(FP), DX
|
||||||
MOVQ a+8(FP), AX
|
MOVQ a+8(FP), AX
|
||||||
MOVQ b+16(FP), BX
|
MOVQ b+16(FP), BX
|
||||||
MOVQ a+24(FP), CX
|
MOVQ c+24(FP), CX
|
||||||
MOVQ $128, BP
|
MOVQ $128, DI
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
MOVOU 0(AX), X0
|
MOVOU 0(AX), X0
|
||||||
@@ -239,6 +238,6 @@ loop:
|
|||||||
ADDQ $16, BX
|
ADDQ $16, BX
|
||||||
ADDQ $16, CX
|
ADDQ $16, CX
|
||||||
ADDQ $16, DX
|
ADDQ $16, DX
|
||||||
SUBQ $2, BP
|
SUBQ $2, DI
|
||||||
JA loop
|
JA loop
|
||||||
RET
|
RET
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !amd64 || purego || !gc
|
//go:build !amd64 || purego || !gc
|
||||||
// +build !amd64 purego !gc
|
|
||||||
|
|
||||||
package argon2
|
package argon2
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.7 && amd64 && gc && !purego
|
//go:build amd64 && gc && !purego
|
||||||
// +build go1.7,amd64,gc,!purego
|
|
||||||
|
|
||||||
package blake2b
|
package blake2b
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.7 && amd64 && gc && !purego
|
//go:build amd64 && gc && !purego
|
||||||
// +build go1.7,amd64,gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
// Copyright 2016 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 !go1.7 && amd64 && gc && !purego
|
|
||||||
// +build !go1.7,amd64,gc,!purego
|
|
||||||
|
|
||||||
package blake2b
|
|
||||||
|
|
||||||
import "golang.org/x/sys/cpu"
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
useSSE4 = cpu.X86.HasSSE41
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:noescape
|
|
||||||
func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
|
||||||
|
|
||||||
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
|
||||||
if useSSE4 {
|
|
||||||
hashBlocksSSE4(h, c, flag, blocks)
|
|
||||||
} else {
|
|
||||||
hashBlocksGeneric(h, c, flag, blocks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && gc && !purego
|
//go:build amd64 && gc && !purego
|
||||||
// +build amd64,gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !amd64 || purego || !gc
|
//go:build !amd64 || purego || !gc
|
||||||
// +build !amd64 purego !gc
|
|
||||||
|
|
||||||
package blake2b
|
package blake2b
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.9
|
|
||||||
// +build go1.9
|
|
||||||
|
|
||||||
package blake2b
|
package blake2b
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build 386 && gc && !purego
|
//go:build 386 && gc && !purego
|
||||||
// +build 386,gc,!purego
|
|
||||||
|
|
||||||
package blake2s
|
package blake2s
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build 386 && gc && !purego
|
//go:build 386 && gc && !purego
|
||||||
// +build 386,gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && gc && !purego
|
//go:build amd64 && gc && !purego
|
||||||
// +build amd64,gc,!purego
|
|
||||||
|
|
||||||
package blake2s
|
package blake2s
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && gc && !purego
|
//go:build amd64 && gc && !purego
|
||||||
// +build amd64,gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (!amd64 && !386) || !gc || purego
|
//go:build (!amd64 && !386) || !gc || purego
|
||||||
// +build !amd64,!386 !gc purego
|
|
||||||
|
|
||||||
package blake2s
|
package blake2s
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.9
|
//go:build go1.9
|
||||||
// +build go1.9
|
|
||||||
|
|
||||||
package blake2s
|
package blake2s
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
package chacha20
|
package chacha20
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego
|
//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego
|
||||||
// +build !arm64,!s390x,!ppc64le !gc purego
|
|
||||||
|
|
||||||
package chacha20
|
package chacha20
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
package chacha20
|
package chacha20
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
// due to the calling conventions and initialization of constants.
|
// due to the calling conventions and initialization of constants.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
package chacha20
|
package chacha20
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
#include "go_asm.h"
|
#include "go_asm.h"
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
package chacha20poly1305
|
package chacha20poly1305
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
// This file was originally from https://golang.org/cl/24717 by Vlad Krasnov of CloudFlare.
|
// This file was originally from https://golang.org/cl/24717 by Vlad Krasnov of CloudFlare.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
// General register allocation
|
// 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 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 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
|
#define shiftD3Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x04 // PALIGNR $4, X15, X15
|
||||||
|
|
||||||
// Some macros
|
// 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) \
|
#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 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
|
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) \
|
#define chachaQR_AVX2(A, B, C, D, T) \
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !amd64 || !gc || purego
|
//go:build !amd64 || !gc || purego
|
||||||
// +build !amd64 !gc purego
|
|
||||||
|
|
||||||
package chacha20poly1305
|
package chacha20poly1305
|
||||||
|
|
||||||
|
|||||||
+7
-6
@@ -733,13 +733,14 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOptionalASN1Boolean sets *out to the value of the next ASN.1 BOOLEAN or,
|
// ReadOptionalASN1Boolean attempts to read an optional ASN.1 BOOLEAN
|
||||||
// if the next bytes are not an ASN.1 BOOLEAN, to the value of defaultValue.
|
// explicitly tagged with tag into out and advances. If no element with a
|
||||||
// It reports whether the operation was successful.
|
// matching tag is present, it sets "out" to defaultValue instead. It reports
|
||||||
func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool {
|
// whether the read was successful.
|
||||||
|
func (s *String) ReadOptionalASN1Boolean(out *bool, tag asn1.Tag, defaultValue bool) bool {
|
||||||
var present bool
|
var present bool
|
||||||
var child String
|
var child String
|
||||||
if !s.ReadOptionalASN1(&child, &present, asn1.BOOLEAN) {
|
if !s.ReadOptionalASN1(&child, &present, tag) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,7 +749,7 @@ func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.ReadASN1Boolean(out)
|
return child.ReadASN1Boolean(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool {
|
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) {
|
func TestReadASN1IntegerSigned(t *testing.T) {
|
||||||
testData64 := []struct {
|
testData64 := []struct {
|
||||||
in []byte
|
in []byte
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
module asm
|
module asm
|
||||||
|
|
||||||
go 1.16
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/mmcloughlin/avo v0.4.0
|
github.com/mmcloughlin/avo v0.5.0
|
||||||
golang.org/x/crypto v0.0.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.5.0 h1:nAco9/aI9Lg2kiuROBY6BhCI/z0t5jEvJfjWbL8qXLU=
|
||||||
github.com/mmcloughlin/avo v0.4.0/go.mod h1:RW9BfYA3TgO9uCdNrKU2h6J8cPD8ZLznvfgHAeszb1s=
|
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
|
||||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
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-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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
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-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-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-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-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-20220520151302-bc2c85ada10a/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021/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-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.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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
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-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=
|
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.
|
// 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
|
//go:build amd64 && gc && !purego
|
||||||
// +build amd64,gc,!purego
|
|
||||||
|
|
||||||
package field
|
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.
|
// 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
|
//go:build amd64 && gc && !purego
|
||||||
// +build amd64,gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !amd64 || !gc || purego
|
//go:build !amd64 || !gc || purego
|
||||||
// +build !amd64 !gc purego
|
|
||||||
|
|
||||||
package field
|
package field
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build arm64 && gc && !purego
|
//go:build arm64 && gc && !purego
|
||||||
// +build arm64,gc,!purego
|
|
||||||
|
|
||||||
package field
|
package field
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build arm64 && gc && !purego
|
//go:build arm64 && gc && !purego
|
||||||
// +build arm64,gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !arm64 || !gc || purego
|
//go:build !arm64 || !gc || purego
|
||||||
// +build !arm64 !gc purego
|
|
||||||
|
|
||||||
package field
|
package field
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
module golang.org/x/crypto
|
module golang.org/x/crypto
|
||||||
|
|
||||||
go 1.17
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/net v0.10.0 // tagx:ignore
|
golang.org/x/net v0.10.0 // tagx:ignore
|
||||||
golang.org/x/sys v0.13.0
|
golang.org/x/sys v0.15.0
|
||||||
golang.org/x/term v0.13.0
|
golang.org/x/term v0.15.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require golang.org/x/text v0.13.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 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
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/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/term v0.0.0-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.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
|
||||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-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=
|
|
||||||
|
|||||||
+3
-1
@@ -56,7 +56,9 @@ func (f *hkdf) Read(p []byte) (int, error) {
|
|||||||
|
|
||||||
// Fill the rest of the buffer
|
// Fill the rest of the buffer
|
||||||
for len(p) > 0 {
|
for len(p) > 0 {
|
||||||
f.expander.Reset()
|
if f.counter > 1 {
|
||||||
|
f.expander.Reset()
|
||||||
|
}
|
||||||
f.expander.Write(f.prev)
|
f.expander.Write(f.prev)
|
||||||
f.expander.Write(f.info)
|
f.expander.Write(f.info)
|
||||||
f.expander.Write([]byte{f.counter})
|
f.expander.Write([]byte{f.counter})
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !purego
|
//go:build !purego
|
||||||
// +build !purego
|
|
||||||
|
|
||||||
// Package alias implements memory aliasing tests.
|
// Package alias implements memory aliasing tests.
|
||||||
package alias
|
package alias
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build purego
|
//go:build purego
|
||||||
// +build purego
|
|
||||||
|
|
||||||
// Package alias implements memory aliasing tests.
|
// Package alias implements memory aliasing tests.
|
||||||
package alias
|
package alias
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !go1.13
|
//go:build !go1.13
|
||||||
// +build !go1.13
|
|
||||||
|
|
||||||
package poly1305
|
package poly1305
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.13
|
//go:build go1.13
|
||||||
// +build go1.13
|
|
||||||
|
|
||||||
package poly1305
|
package poly1305
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego
|
//go:build (!amd64 && !ppc64le && !s390x) || !gc || purego
|
||||||
// +build !amd64,!ppc64le,!s390x !gc purego
|
|
||||||
|
|
||||||
package poly1305
|
package poly1305
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
package poly1305
|
package poly1305
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
package poly1305
|
package poly1305
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
package poly1305
|
package poly1305
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.13
|
//go:build go1.13
|
||||||
// +build go1.13
|
|
||||||
|
|
||||||
package wycheproof
|
package wycheproof
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.7
|
//go:build go1.7
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
package ocsp
|
package ocsp
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// This code can be compiled and used to test the otr package against libotr.
|
// This code can be compiled and used to test the otr package against libotr.
|
||||||
// See otr_test.go.
|
// See otr_test.go.
|
||||||
|
|
||||||
// +build ignore
|
//go:build ignore
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && !purego && gc
|
//go:build amd64 && !purego && gc
|
||||||
// +build amd64,!purego,gc
|
|
||||||
|
|
||||||
package salsa
|
package salsa
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && !purego && gc
|
//go:build amd64 && !purego && gc
|
||||||
// +build amd64,!purego,gc
|
|
||||||
|
|
||||||
// This code was translated into a form compatible with 6a from the public
|
// This code was translated into a form compatible with 6a from the public
|
||||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && !purego && gc
|
//go:build amd64 && !purego && gc
|
||||||
// +build amd64,!purego,gc
|
|
||||||
|
|
||||||
package salsa
|
package salsa
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !amd64 || purego || !gc
|
//go:build !amd64 || purego || !gc
|
||||||
// +build !amd64 purego !gc
|
|
||||||
|
|
||||||
package salsa
|
package salsa
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !gc || purego || !s390x
|
//go:build !gc || purego || !s390x
|
||||||
// +build !gc purego !s390x
|
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !amd64 || purego || !gc
|
//go:build !amd64 || purego || !gc
|
||||||
// +build !amd64 purego !gc
|
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && !purego && gc
|
//go:build amd64 && !purego && gc
|
||||||
// +build amd64,!purego,gc
|
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build amd64 && !purego && gc
|
//go:build amd64 && !purego && gc
|
||||||
// +build amd64,!purego,gc
|
|
||||||
|
|
||||||
// This code was translated into a form compatible with 6a from the public
|
// This code was translated into a form compatible with 6a from the public
|
||||||
// domain sources at https://github.com/gvanas/KeccakCodePackage
|
// domain sources at https://github.com/gvanas/KeccakCodePackage
|
||||||
@@ -320,9 +319,9 @@
|
|||||||
MOVQ rDi, _si(oState); \
|
MOVQ rDi, _si(oState); \
|
||||||
MOVQ rDo, _so(oState) \
|
MOVQ rDo, _so(oState) \
|
||||||
|
|
||||||
// func keccakF1600(state *[25]uint64)
|
// func keccakF1600(a *[25]uint64)
|
||||||
TEXT ·keccakF1600(SB), 0, $200-8
|
TEXT ·keccakF1600(SB), 0, $200-8
|
||||||
MOVQ state+0(FP), rpState
|
MOVQ a+0(FP), rpState
|
||||||
|
|
||||||
// Convert the user state into an internal state
|
// Convert the user state into an internal state
|
||||||
NOTQ _be(rpState)
|
NOTQ _be(rpState)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build go1.4
|
//go:build go1.4
|
||||||
// +build go1.4
|
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build gc && !purego
|
//go:build gc && !purego
|
||||||
// +build gc,!purego
|
|
||||||
|
|
||||||
#include "textflag.h"
|
#include "textflag.h"
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !gc || purego || !s390x
|
//go:build !gc || purego || !s390x
|
||||||
// +build !gc purego !s390x
|
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (!amd64 && !386 && !ppc64le) || purego
|
//go:build (!amd64 && !386 && !ppc64le) || purego
|
||||||
// +build !amd64,!386,!ppc64le purego
|
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build (amd64 || 386 || ppc64le) && !purego
|
//go:build (amd64 || 386 || ppc64le) && !purego
|
||||||
// +build amd64 386 ppc64le
|
|
||||||
// +build !purego
|
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
|
|||||||
+9
-4
@@ -141,9 +141,14 @@ const (
|
|||||||
agentAddSmartcardKeyConstrained = 26
|
agentAddSmartcardKeyConstrained = 26
|
||||||
|
|
||||||
// 3.7 Key constraint identifiers
|
// 3.7 Key constraint identifiers
|
||||||
agentConstrainLifetime = 1
|
agentConstrainLifetime = 1
|
||||||
agentConstrainConfirm = 2
|
agentConstrainConfirm = 2
|
||||||
agentConstrainExtension = 3
|
// 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
|
// maxAgentResponseBytes is the maximum agent reply size that is accepted. This
|
||||||
@@ -205,7 +210,7 @@ type constrainLifetimeAgentMsg struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type constrainExtensionAgentMsg struct {
|
type constrainExtensionAgentMsg struct {
|
||||||
ExtensionName string `sshtype:"3"`
|
ExtensionName string `sshtype:"255|3"`
|
||||||
ExtensionDetails []byte
|
ExtensionDetails []byte
|
||||||
|
|
||||||
// Rest is a field used for parsing, not part of message
|
// Rest is a field used for parsing, not part of message
|
||||||
|
|||||||
+1
-1
@@ -208,7 +208,7 @@ func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse
|
|||||||
case agentConstrainConfirm:
|
case agentConstrainConfirm:
|
||||||
confirmBeforeUse = true
|
confirmBeforeUse = true
|
||||||
constraints = constraints[1:]
|
constraints = constraints[1:]
|
||||||
case agentConstrainExtension:
|
case agentConstrainExtension, agentConstrainExtensionV00:
|
||||||
var msg constrainExtensionAgentMsg
|
var msg constrainExtensionAgentMsg
|
||||||
if err = ssh.Unmarshal(constraints, &msg); err != nil {
|
if err = ssh.Unmarshal(constraints, &msg); err != nil {
|
||||||
return 0, false, nil, err
|
return 0, false, nil, err
|
||||||
|
|||||||
@@ -243,7 +243,11 @@ func TestParseConstraints(t *testing.T) {
|
|||||||
ExtensionDetails: []byte(fmt.Sprintf("details: %d", i)),
|
ExtensionDetails: []byte(fmt.Sprintf("details: %d", i)),
|
||||||
}
|
}
|
||||||
expect = append(expect, ext)
|
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)...)
|
data = append(data, ssh.Marshal(ext)...)
|
||||||
}
|
}
|
||||||
_, _, extensions, err := parseConstraints(data)
|
_, _, extensions, err := parseConstraints(data)
|
||||||
|
|||||||
+20
-8
@@ -187,9 +187,11 @@ type channel struct {
|
|||||||
pending *buffer
|
pending *buffer
|
||||||
extPending *buffer
|
extPending *buffer
|
||||||
|
|
||||||
// windowMu protects myWindow, the flow-control window.
|
// windowMu protects myWindow, the flow-control window, and myConsumed,
|
||||||
windowMu sync.Mutex
|
// the number of bytes consumed since we last increased myWindow
|
||||||
myWindow uint32
|
windowMu sync.Mutex
|
||||||
|
myWindow uint32
|
||||||
|
myConsumed uint32
|
||||||
|
|
||||||
// writeMu serializes calls to mux.conn.writePacket() and
|
// writeMu serializes calls to mux.conn.writePacket() and
|
||||||
// protects sentClose and packetPool. This mutex must be
|
// protects sentClose and packetPool. This mutex must be
|
||||||
@@ -332,14 +334,24 @@ func (ch *channel) handleData(packet []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *channel) adjustWindow(n uint32) error {
|
func (c *channel) adjustWindow(adj uint32) error {
|
||||||
c.windowMu.Lock()
|
c.windowMu.Lock()
|
||||||
// Since myWindow is managed on our side, and can never exceed
|
// Since myConsumed and myWindow are managed on our side, and can never
|
||||||
// the initial window setting, we don't worry about overflow.
|
// exceed the initial window setting, we don't worry about overflow.
|
||||||
c.myWindow += uint32(n)
|
c.myConsumed += adj
|
||||||
|
var sendAdj uint32
|
||||||
|
if (channelWindowSize-c.myWindow > 3*c.maxIncomingPayload) ||
|
||||||
|
(c.myWindow < channelWindowSize/2) {
|
||||||
|
sendAdj = c.myConsumed
|
||||||
|
c.myConsumed = 0
|
||||||
|
c.myWindow += sendAdj
|
||||||
|
}
|
||||||
c.windowMu.Unlock()
|
c.windowMu.Unlock()
|
||||||
|
if sendAdj == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return c.sendMessage(windowAdjustMsg{
|
return c.sendMessage(windowAdjustMsg{
|
||||||
AdditionalBytes: uint32(n),
|
AdditionalBytes: sendAdj,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -82,7 +82,7 @@ func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan
|
|||||||
|
|
||||||
if err := conn.clientHandshake(addr, &fullConf); err != nil {
|
if err := conn.clientHandshake(addr, &fullConf); err != nil {
|
||||||
c.Close()
|
c.Close()
|
||||||
return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err)
|
return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %w", err)
|
||||||
}
|
}
|
||||||
conn.mux = newMux(conn.transport)
|
conn.mux = newMux(conn.transport)
|
||||||
return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil
|
return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil
|
||||||
|
|||||||
+19
-1
@@ -307,7 +307,10 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
|||||||
}
|
}
|
||||||
var methods []string
|
var methods []string
|
||||||
var errSigAlgo error
|
var errSigAlgo error
|
||||||
for _, signer := range signers {
|
|
||||||
|
origSignersLen := len(signers)
|
||||||
|
for idx := 0; idx < len(signers); idx++ {
|
||||||
|
signer := signers[idx]
|
||||||
pub := signer.PublicKey()
|
pub := signer.PublicKey()
|
||||||
as, algo, err := pickSignatureAlgorithm(signer, extensions)
|
as, algo, err := pickSignatureAlgorithm(signer, extensions)
|
||||||
if err != nil && errSigAlgo == nil {
|
if err != nil && errSigAlgo == nil {
|
||||||
@@ -321,6 +324,21 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return authFailure, nil, err
|
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 {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-2
@@ -7,6 +7,9 @@ package ssh
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -207,9 +210,12 @@ func TestBannerCallback(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewClientConn(t *testing.T) {
|
func TestNewClientConn(t *testing.T) {
|
||||||
|
errHostKeyMismatch := errors.New("host key mismatch")
|
||||||
|
|
||||||
for _, tt := range []struct {
|
for _, tt := range []struct {
|
||||||
name string
|
name string
|
||||||
user string
|
user string
|
||||||
|
simulateHostKeyMismatch HostKeyCallback
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "good user field for ConnMetadata",
|
name: "good user field for ConnMetadata",
|
||||||
@@ -219,6 +225,13 @@ func TestNewClientConn(t *testing.T) {
|
|||||||
name: "empty user field for ConnMetadata",
|
name: "empty user field for ConnMetadata",
|
||||||
user: "",
|
user: "",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "host key mismatch",
|
||||||
|
user: "testuser",
|
||||||
|
simulateHostKeyMismatch: func(hostname string, remote net.Addr, key PublicKey) error {
|
||||||
|
return fmt.Errorf("%w: %s", errHostKeyMismatch, bytes.TrimSpace(MarshalAuthorizedKey(key)))
|
||||||
|
},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c1, c2, err := netPipe()
|
c1, c2, err := netPipe()
|
||||||
@@ -243,8 +256,16 @@ func TestNewClientConn(t *testing.T) {
|
|||||||
},
|
},
|
||||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tt.simulateHostKeyMismatch != nil {
|
||||||
|
clientConf.HostKeyCallback = tt.simulateHostKeyMismatch
|
||||||
|
}
|
||||||
|
|
||||||
clientConn, _, _, err := NewClientConn(c2, "", clientConf)
|
clientConn, _, _, err := NewClientConn(c2, "", clientConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if tt.simulateHostKeyMismatch != nil && errors.Is(err, errHostKeyMismatch) {
|
||||||
|
return
|
||||||
|
}
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-3
@@ -10,7 +10,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
_ "crypto/sha1"
|
_ "crypto/sha1"
|
||||||
@@ -128,6 +127,14 @@ func isRSA(algo string) bool {
|
|||||||
return contains(algos, underlyingAlgo(algo))
|
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
|
// supportedPubKeyAuthAlgos specifies the supported client public key
|
||||||
// authentication algorithms. Note that this doesn't include certificate types
|
// authentication algorithms. Note that this doesn't include certificate types
|
||||||
// since those use the underlying algorithm. This list is sent to the client if
|
// since those use the underlying algorithm. This list is sent to the client if
|
||||||
@@ -140,8 +147,6 @@ var supportedPubKeyAuthAlgos = []string{
|
|||||||
KeyAlgoDSA,
|
KeyAlgoDSA,
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportedPubKeyAuthAlgosList = strings.Join(supportedPubKeyAuthAlgos, ",")
|
|
||||||
|
|
||||||
// unexpectedMessageError results when the SSH message that we received didn't
|
// unexpectedMessageError results when the SSH message that we received didn't
|
||||||
// match what we wanted.
|
// match what we wanted.
|
||||||
func unexpectedMessageError(expected, got uint8) error {
|
func unexpectedMessageError(expected, got uint8) error {
|
||||||
|
|||||||
+16
-2
@@ -16,6 +16,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
@@ -98,8 +99,15 @@ func ExampleNewServerConn() {
|
|||||||
}
|
}
|
||||||
log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])
|
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.
|
// 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.
|
// Service the incoming Channel channel.
|
||||||
for newChannel := range chans {
|
for newChannel := range chans {
|
||||||
@@ -119,16 +127,22 @@ func ExampleNewServerConn() {
|
|||||||
// Sessions have out-of-band requests such as "shell",
|
// Sessions have out-of-band requests such as "shell",
|
||||||
// "pty-req" and "env". Here we handle only the
|
// "pty-req" and "env". Here we handle only the
|
||||||
// "shell" request.
|
// "shell" request.
|
||||||
|
wg.Add(1)
|
||||||
go func(in <-chan *ssh.Request) {
|
go func(in <-chan *ssh.Request) {
|
||||||
for req := range in {
|
for req := range in {
|
||||||
req.Reply(req.Type == "shell", nil)
|
req.Reply(req.Type == "shell", nil)
|
||||||
}
|
}
|
||||||
|
wg.Done()
|
||||||
}(requests)
|
}(requests)
|
||||||
|
|
||||||
term := terminal.NewTerminal(channel, "> ")
|
term := terminal.NewTerminal(channel, "> ")
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer channel.Close()
|
defer func() {
|
||||||
|
channel.Close()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
for {
|
for {
|
||||||
line, err := term.ReadLine()
|
line, err := term.ReadLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+59
-4
@@ -11,6 +11,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,6 +35,16 @@ type keyingTransport interface {
|
|||||||
// direction will be effected if a msgNewKeys message is sent
|
// direction will be effected if a msgNewKeys message is sent
|
||||||
// or received.
|
// or received.
|
||||||
prepareKeyChange(*algorithms, *kexResult) error
|
prepareKeyChange(*algorithms, *kexResult) error
|
||||||
|
|
||||||
|
// setStrictMode sets the strict KEX mode, notably triggering
|
||||||
|
// sequence number resets on sending or receiving msgNewKeys.
|
||||||
|
// If the sequence number is already > 1 when setStrictMode
|
||||||
|
// is called, an error is returned.
|
||||||
|
setStrictMode() error
|
||||||
|
|
||||||
|
// setInitialKEXDone indicates to the transport that the initial key exchange
|
||||||
|
// was completed
|
||||||
|
setInitialKEXDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
// handshakeTransport implements rekeying on top of a keyingTransport
|
// handshakeTransport implements rekeying on top of a keyingTransport
|
||||||
@@ -50,6 +61,10 @@ type handshakeTransport struct {
|
|||||||
// connection.
|
// connection.
|
||||||
hostKeys []Signer
|
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,
|
// hostKeyAlgorithms is non-empty if we are the client. In that case,
|
||||||
// we accept these key types from the server as host key.
|
// we accept these key types from the server as host key.
|
||||||
hostKeyAlgorithms []string
|
hostKeyAlgorithms []string
|
||||||
@@ -95,6 +110,10 @@ type handshakeTransport struct {
|
|||||||
|
|
||||||
// The session ID or nil if first kex did not complete yet.
|
// The session ID or nil if first kex did not complete yet.
|
||||||
sessionID []byte
|
sessionID []byte
|
||||||
|
|
||||||
|
// strictMode indicates if the other side of the handshake indicated
|
||||||
|
// that we should be following the strict KEX protocol restrictions.
|
||||||
|
strictMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type pendingKex struct {
|
type pendingKex struct {
|
||||||
@@ -141,6 +160,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
|
|||||||
func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport {
|
func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byte, config *ServerConfig) *handshakeTransport {
|
||||||
t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion)
|
t := newHandshakeTransport(conn, &config.Config, clientVersion, serverVersion)
|
||||||
t.hostKeys = config.hostKeys
|
t.hostKeys = config.hostKeys
|
||||||
|
t.publicKeyAuthAlgorithms = config.PublicKeyAuthAlgorithms
|
||||||
go t.readLoop()
|
go t.readLoop()
|
||||||
go t.kexLoop()
|
go t.kexLoop()
|
||||||
return t
|
return t
|
||||||
@@ -203,7 +223,10 @@ func (t *handshakeTransport) readLoop() {
|
|||||||
close(t.incoming)
|
close(t.incoming)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if p[0] == msgIgnore || p[0] == msgDebug {
|
// If this is the first kex, and strict KEX mode is enabled,
|
||||||
|
// we don't ignore any messages, as they may be used to manipulate
|
||||||
|
// the packet sequence numbers.
|
||||||
|
if !(t.sessionID == nil && t.strictMode) && (p[0] == msgIgnore || p[0] == msgDebug) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
t.incoming <- p
|
t.incoming <- p
|
||||||
@@ -435,6 +458,11 @@ func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) {
|
|||||||
return successPacket, nil
|
return successPacket, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
kexStrictClient = "kex-strict-c-v00@openssh.com"
|
||||||
|
kexStrictServer = "kex-strict-s-v00@openssh.com"
|
||||||
|
)
|
||||||
|
|
||||||
// sendKexInit sends a key change message.
|
// sendKexInit sends a key change message.
|
||||||
func (t *handshakeTransport) sendKexInit() error {
|
func (t *handshakeTransport) sendKexInit() error {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
@@ -448,7 +476,6 @@ func (t *handshakeTransport) sendKexInit() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
msg := &kexInitMsg{
|
msg := &kexInitMsg{
|
||||||
KexAlgos: t.config.KeyExchanges,
|
|
||||||
CiphersClientServer: t.config.Ciphers,
|
CiphersClientServer: t.config.Ciphers,
|
||||||
CiphersServerClient: t.config.Ciphers,
|
CiphersServerClient: t.config.Ciphers,
|
||||||
MACsClientServer: t.config.MACs,
|
MACsClientServer: t.config.MACs,
|
||||||
@@ -458,6 +485,13 @@ func (t *handshakeTransport) sendKexInit() error {
|
|||||||
}
|
}
|
||||||
io.ReadFull(rand.Reader, msg.Cookie[:])
|
io.ReadFull(rand.Reader, msg.Cookie[:])
|
||||||
|
|
||||||
|
// We mutate the KexAlgos slice, in order to add the kex-strict extension algorithm,
|
||||||
|
// and possibly to add the ext-info extension algorithm. Since the slice may be the
|
||||||
|
// user owned KeyExchanges, we create our own slice in order to avoid using user
|
||||||
|
// owned memory by mistake.
|
||||||
|
msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+2) // room for kex-strict and ext-info
|
||||||
|
msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...)
|
||||||
|
|
||||||
isServer := len(t.hostKeys) > 0
|
isServer := len(t.hostKeys) > 0
|
||||||
if isServer {
|
if isServer {
|
||||||
for _, k := range t.hostKeys {
|
for _, k := range t.hostKeys {
|
||||||
@@ -482,17 +516,24 @@ func (t *handshakeTransport) sendKexInit() error {
|
|||||||
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat)
|
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.sessionID == nil {
|
||||||
|
msg.KexAlgos = append(msg.KexAlgos, kexStrictServer)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
msg.ServerHostKeyAlgos = t.hostKeyAlgorithms
|
msg.ServerHostKeyAlgos = t.hostKeyAlgorithms
|
||||||
|
|
||||||
// As a client we opt in to receiving SSH_MSG_EXT_INFO so we know what
|
// As a client we opt in to receiving SSH_MSG_EXT_INFO so we know what
|
||||||
// algorithms the server supports for public key authentication. See RFC
|
// algorithms the server supports for public key authentication. See RFC
|
||||||
// 8308, Section 2.1.
|
// 8308, Section 2.1.
|
||||||
|
//
|
||||||
|
// We also send the strict KEX mode extension algorithm, in order to opt
|
||||||
|
// into the strict KEX mode.
|
||||||
if firstKeyExchange := t.sessionID == nil; firstKeyExchange {
|
if firstKeyExchange := t.sessionID == nil; firstKeyExchange {
|
||||||
msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+1)
|
|
||||||
msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...)
|
|
||||||
msg.KexAlgos = append(msg.KexAlgos, "ext-info-c")
|
msg.KexAlgos = append(msg.KexAlgos, "ext-info-c")
|
||||||
|
msg.KexAlgos = append(msg.KexAlgos, kexStrictClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
packet := Marshal(msg)
|
packet := Marshal(msg)
|
||||||
@@ -598,6 +639,13 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if t.sessionID == nil && ((isClient && contains(serverInit.KexAlgos, kexStrictServer)) || (!isClient && contains(clientInit.KexAlgos, kexStrictClient))) {
|
||||||
|
t.strictMode = true
|
||||||
|
if err := t.conn.setStrictMode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We don't send FirstKexFollows, but we handle receiving it.
|
// We don't send FirstKexFollows, but we handle receiving it.
|
||||||
//
|
//
|
||||||
// RFC 4253 section 7 defines the kex and the agreement method for
|
// RFC 4253 section 7 defines the kex and the agreement method for
|
||||||
@@ -649,6 +697,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||||||
// message with the server-sig-algs extension if the client supports it. See
|
// message with the server-sig-algs extension if the client supports it. See
|
||||||
// RFC 8308, Sections 2.4 and 3.1, and [PROTOCOL], Section 1.9.
|
// RFC 8308, Sections 2.4 and 3.1, and [PROTOCOL], Section 1.9.
|
||||||
if !isClient && firstKeyExchange && contains(clientInit.KexAlgos, "ext-info-c") {
|
if !isClient && firstKeyExchange && contains(clientInit.KexAlgos, "ext-info-c") {
|
||||||
|
supportedPubKeyAuthAlgosList := strings.Join(t.publicKeyAuthAlgorithms, ",")
|
||||||
extInfo := &extInfoMsg{
|
extInfo := &extInfoMsg{
|
||||||
NumExtensions: 2,
|
NumExtensions: 2,
|
||||||
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)+4+16+4+1),
|
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)+4+16+4+1),
|
||||||
@@ -672,6 +721,12 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||||||
return unexpectedMessageError(msgNewKeys, packet[0])
|
return unexpectedMessageError(msgNewKeys, packet[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if firstKeyExchange {
|
||||||
|
// Indicates to the transport that the first key exchange is completed
|
||||||
|
// after receiving SSH_MSG_NEWKEYS.
|
||||||
|
t.conn.setInitialKEXDone()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -395,6 +395,10 @@ func (n *errorKeyingTransport) readPacket() ([]byte, error) {
|
|||||||
return n.packetConn.readPacket()
|
return n.packetConn.readPacket()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *errorKeyingTransport) setStrictMode() error { return nil }
|
||||||
|
|
||||||
|
func (n *errorKeyingTransport) setInitialKEXDone() {}
|
||||||
|
|
||||||
func TestHandshakeErrorHandlingRead(t *testing.T) {
|
func TestHandshakeErrorHandlingRead(t *testing.T) {
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
testHandshakeErrorHandlingN(t, i, -1, false)
|
testHandshakeErrorHandlingN(t, i, -1, false)
|
||||||
@@ -710,3 +714,308 @@ func TestPickIncompatibleHostKeyAlgo(t *testing.T) {
|
|||||||
t.Fatal("incompatible signer returned")
|
t.Fatal("incompatible signer returned")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStrictKEXResetSeqFirstKEX(t *testing.T) {
|
||||||
|
if runtime.GOOS == "plan9" {
|
||||||
|
t.Skip("see golang.org/issue/7237")
|
||||||
|
}
|
||||||
|
|
||||||
|
checker := &syncChecker{
|
||||||
|
waitCall: make(chan int, 10),
|
||||||
|
called: make(chan int, 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.waitCall <- 1
|
||||||
|
trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("handshakePair: %v", err)
|
||||||
|
}
|
||||||
|
<-checker.called
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
trC.Close()
|
||||||
|
trS.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Throw away the msgExtInfo packet sent during the handshake by the server
|
||||||
|
_, err = trC.readPacket()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("readPacket failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the handshake transports before checking the sequence number to
|
||||||
|
// avoid races.
|
||||||
|
trC.Close()
|
||||||
|
trS.Close()
|
||||||
|
|
||||||
|
// check that the sequence number counters. We reset after msgNewKeys, but
|
||||||
|
// then the server immediately writes msgExtInfo, and we close the
|
||||||
|
// transports so we expect read 2, write 0 on the client and read 1, write 1
|
||||||
|
// on the server.
|
||||||
|
if trC.conn.(*transport).reader.seqNum != 2 || trC.conn.(*transport).writer.seqNum != 0 ||
|
||||||
|
trS.conn.(*transport).reader.seqNum != 1 || trS.conn.(*transport).writer.seqNum != 1 {
|
||||||
|
t.Errorf(
|
||||||
|
"unexpected sequence counters:\nclient: reader %d (expected 2), writer %d (expected 0)\nserver: reader %d (expected 1), writer %d (expected 1)",
|
||||||
|
trC.conn.(*transport).reader.seqNum,
|
||||||
|
trC.conn.(*transport).writer.seqNum,
|
||||||
|
trS.conn.(*transport).reader.seqNum,
|
||||||
|
trS.conn.(*transport).writer.seqNum,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrictKEXResetSeqSuccessiveKEX(t *testing.T) {
|
||||||
|
if runtime.GOOS == "plan9" {
|
||||||
|
t.Skip("see golang.org/issue/7237")
|
||||||
|
}
|
||||||
|
|
||||||
|
checker := &syncChecker{
|
||||||
|
waitCall: make(chan int, 10),
|
||||||
|
called: make(chan int, 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.waitCall <- 1
|
||||||
|
trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("handshakePair: %v", err)
|
||||||
|
}
|
||||||
|
<-checker.called
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
trC.Close()
|
||||||
|
trS.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Throw away the msgExtInfo packet sent during the handshake by the server
|
||||||
|
_, err = trC.readPacket()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("readPacket failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write and read five packets on either side to bump the sequence numbers
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if err := trC.writePacket([]byte{msgRequestSuccess}); err != nil {
|
||||||
|
t.Fatalf("writePacket failed: %s", err)
|
||||||
|
}
|
||||||
|
if _, err := trS.readPacket(); err != nil {
|
||||||
|
t.Fatalf("readPacket failed: %s", err)
|
||||||
|
}
|
||||||
|
if err := trS.writePacket([]byte{msgRequestSuccess}); err != nil {
|
||||||
|
t.Fatalf("writePacket failed: %s", err)
|
||||||
|
}
|
||||||
|
if _, err := trC.readPacket(); err != nil {
|
||||||
|
t.Fatalf("readPacket failed: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request a key exchange, which should cause the sequence numbers to reset
|
||||||
|
checker.waitCall <- 1
|
||||||
|
trC.requestKeyExchange()
|
||||||
|
<-checker.called
|
||||||
|
|
||||||
|
// write a packet on the client, and then read it, to verify the key change has actually happened, since
|
||||||
|
// the HostKeyCallback is called _during_ the handshake, so isn't actually indicative of the handshake
|
||||||
|
// finishing.
|
||||||
|
dummyPacket := []byte{99}
|
||||||
|
if err := trS.writePacket(dummyPacket); err != nil {
|
||||||
|
t.Fatalf("writePacket failed: %s", err)
|
||||||
|
}
|
||||||
|
if p, err := trC.readPacket(); err != nil {
|
||||||
|
t.Fatalf("readPacket failed: %s", err)
|
||||||
|
} else if !bytes.Equal(p, dummyPacket) {
|
||||||
|
t.Fatalf("unexpected packet: got %x, want %x", p, dummyPacket)
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the handshake transports before checking the sequence number to
|
||||||
|
// avoid races.
|
||||||
|
trC.Close()
|
||||||
|
trS.Close()
|
||||||
|
|
||||||
|
if trC.conn.(*transport).reader.seqNum != 2 || trC.conn.(*transport).writer.seqNum != 0 ||
|
||||||
|
trS.conn.(*transport).reader.seqNum != 1 || trS.conn.(*transport).writer.seqNum != 1 {
|
||||||
|
t.Errorf(
|
||||||
|
"unexpected sequence counters:\nclient: reader %d (expected 2), writer %d (expected 0)\nserver: reader %d (expected 1), writer %d (expected 1)",
|
||||||
|
trC.conn.(*transport).reader.seqNum,
|
||||||
|
trC.conn.(*transport).writer.seqNum,
|
||||||
|
trS.conn.(*transport).reader.seqNum,
|
||||||
|
trS.conn.(*transport).writer.seqNum,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSeqNumIncrease(t *testing.T) {
|
||||||
|
if runtime.GOOS == "plan9" {
|
||||||
|
t.Skip("see golang.org/issue/7237")
|
||||||
|
}
|
||||||
|
|
||||||
|
checker := &syncChecker{
|
||||||
|
waitCall: make(chan int, 10),
|
||||||
|
called: make(chan int, 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.waitCall <- 1
|
||||||
|
trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("handshakePair: %v", err)
|
||||||
|
}
|
||||||
|
<-checker.called
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
trC.Close()
|
||||||
|
trS.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Throw away the msgExtInfo packet sent during the handshake by the server
|
||||||
|
_, err = trC.readPacket()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("readPacket failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write and read five packets on either side to bump the sequence numbers
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if err := trC.writePacket([]byte{msgRequestSuccess}); err != nil {
|
||||||
|
t.Fatalf("writePacket failed: %s", err)
|
||||||
|
}
|
||||||
|
if _, err := trS.readPacket(); err != nil {
|
||||||
|
t.Fatalf("readPacket failed: %s", err)
|
||||||
|
}
|
||||||
|
if err := trS.writePacket([]byte{msgRequestSuccess}); err != nil {
|
||||||
|
t.Fatalf("writePacket failed: %s", err)
|
||||||
|
}
|
||||||
|
if _, err := trC.readPacket(); err != nil {
|
||||||
|
t.Fatalf("readPacket failed: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the handshake transports before checking the sequence number to
|
||||||
|
// avoid races.
|
||||||
|
trC.Close()
|
||||||
|
trS.Close()
|
||||||
|
|
||||||
|
if trC.conn.(*transport).reader.seqNum != 7 || trC.conn.(*transport).writer.seqNum != 5 ||
|
||||||
|
trS.conn.(*transport).reader.seqNum != 6 || trS.conn.(*transport).writer.seqNum != 6 {
|
||||||
|
t.Errorf(
|
||||||
|
"unexpected sequence counters:\nclient: reader %d (expected 7), writer %d (expected 5)\nserver: reader %d (expected 6), writer %d (expected 6)",
|
||||||
|
trC.conn.(*transport).reader.seqNum,
|
||||||
|
trC.conn.(*transport).writer.seqNum,
|
||||||
|
trS.conn.(*transport).reader.seqNum,
|
||||||
|
trS.conn.(*transport).writer.seqNum,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrictKEXUnexpectedMsg(t *testing.T) {
|
||||||
|
if runtime.GOOS == "plan9" {
|
||||||
|
t.Skip("see golang.org/issue/7237")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that unexpected messages during the handshake cause failure
|
||||||
|
_, _, err := handshakePair(&ClientConfig{HostKeyCallback: func(hostname string, remote net.Addr, key PublicKey) error { return nil }}, "addr", true)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("handshake should fail when there are unexpected messages during the handshake")
|
||||||
|
}
|
||||||
|
|
||||||
|
trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: func(hostname string, remote net.Addr, key PublicKey) error { return nil }}, "addr", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("handshake failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that ignore/debug pacekts are still ignored outside of the handshake
|
||||||
|
if err := trC.writePacket([]byte{msgIgnore}); err != nil {
|
||||||
|
t.Fatalf("writePacket failed: %s", err)
|
||||||
|
}
|
||||||
|
if err := trC.writePacket([]byte{msgDebug}); err != nil {
|
||||||
|
t.Fatalf("writePacket failed: %s", err)
|
||||||
|
}
|
||||||
|
dummyPacket := []byte{99}
|
||||||
|
if err := trC.writePacket(dummyPacket); err != nil {
|
||||||
|
t.Fatalf("writePacket failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p, err := trS.readPacket(); err != nil {
|
||||||
|
t.Fatalf("readPacket failed: %s", err)
|
||||||
|
} else if !bytes.Equal(p, dummyPacket) {
|
||||||
|
t.Fatalf("unexpected packet: got %x, want %x", p, dummyPacket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrictKEXMixed(t *testing.T) {
|
||||||
|
// Test that we still support a mixed connection, where one side sends kex-strict but the other
|
||||||
|
// side doesn't.
|
||||||
|
|
||||||
|
a, b, err := netPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("netPipe failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var trC, trS keyingTransport
|
||||||
|
|
||||||
|
trC = newTransport(a, rand.Reader, true)
|
||||||
|
trS = newTransport(b, rand.Reader, false)
|
||||||
|
trS = addNoiseTransport(trS)
|
||||||
|
|
||||||
|
clientConf := &ClientConfig{HostKeyCallback: func(hostname string, remote net.Addr, key PublicKey) error { return nil }}
|
||||||
|
clientConf.SetDefaults()
|
||||||
|
|
||||||
|
v := []byte("version")
|
||||||
|
client := newClientTransport(trC, v, v, clientConf, "addr", a.RemoteAddr())
|
||||||
|
|
||||||
|
serverConf := &ServerConfig{}
|
||||||
|
serverConf.AddHostKey(testSigners["ecdsa"])
|
||||||
|
serverConf.AddHostKey(testSigners["rsa"])
|
||||||
|
serverConf.SetDefaults()
|
||||||
|
|
||||||
|
transport := newHandshakeTransport(trS, &serverConf.Config, []byte("version"), []byte("version"))
|
||||||
|
transport.hostKeys = serverConf.hostKeys
|
||||||
|
transport.publicKeyAuthAlgorithms = serverConf.PublicKeyAuthAlgorithms
|
||||||
|
|
||||||
|
readOneFailure := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
if _, err := transport.readOnePacket(true); err != nil {
|
||||||
|
readOneFailure <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Basically sendKexInit, but without the kex-strict extension algorithm
|
||||||
|
msg := &kexInitMsg{
|
||||||
|
KexAlgos: transport.config.KeyExchanges,
|
||||||
|
CiphersClientServer: transport.config.Ciphers,
|
||||||
|
CiphersServerClient: transport.config.Ciphers,
|
||||||
|
MACsClientServer: transport.config.MACs,
|
||||||
|
MACsServerClient: transport.config.MACs,
|
||||||
|
CompressionClientServer: supportedCompressions,
|
||||||
|
CompressionServerClient: supportedCompressions,
|
||||||
|
ServerHostKeyAlgos: []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA},
|
||||||
|
}
|
||||||
|
packet := Marshal(msg)
|
||||||
|
// writePacket destroys the contents, so save a copy.
|
||||||
|
packetCopy := make([]byte, len(packet))
|
||||||
|
copy(packetCopy, packet)
|
||||||
|
if err := transport.pushPacket(packetCopy); err != nil {
|
||||||
|
t.Fatalf("pushPacket: %s", err)
|
||||||
|
}
|
||||||
|
transport.sentInitMsg = msg
|
||||||
|
transport.sentInitPacket = packet
|
||||||
|
|
||||||
|
if err := transport.getWriteError(); err != nil {
|
||||||
|
t.Fatalf("getWriteError failed: %s", err)
|
||||||
|
}
|
||||||
|
var request *pendingKex
|
||||||
|
select {
|
||||||
|
case err = <-readOneFailure:
|
||||||
|
t.Fatalf("server readOnePacket failed: %s", err)
|
||||||
|
case request = <-transport.startKex:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect the following calls to fail if the side which does not support
|
||||||
|
// kex-strict sends unexpected/ignored packets during the handshake, even if
|
||||||
|
// the other side does support kex-strict.
|
||||||
|
|
||||||
|
if err := transport.enterKeyExchange(request.otherInit); err != nil {
|
||||||
|
t.Fatalf("enterKeyExchange failed: %s", err)
|
||||||
|
}
|
||||||
|
if err := client.waitSession(); err != nil {
|
||||||
|
t.Fatalf("client.waitSession: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+15
-4
@@ -1232,16 +1232,27 @@ func ParseRawPrivateKeyWithPassphrase(pemBytes, passphrase []byte) (interface{},
|
|||||||
return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err)
|
return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var result interface{}
|
||||||
|
|
||||||
switch block.Type {
|
switch block.Type {
|
||||||
case "RSA PRIVATE KEY":
|
case "RSA PRIVATE KEY":
|
||||||
return x509.ParsePKCS1PrivateKey(buf)
|
result, err = x509.ParsePKCS1PrivateKey(buf)
|
||||||
case "EC PRIVATE KEY":
|
case "EC PRIVATE KEY":
|
||||||
return x509.ParseECPrivateKey(buf)
|
result, err = x509.ParseECPrivateKey(buf)
|
||||||
case "DSA PRIVATE KEY":
|
case "DSA PRIVATE KEY":
|
||||||
return ParseDSAPrivateKey(buf)
|
result, err = ParseDSAPrivateKey(buf)
|
||||||
default:
|
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
|
// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -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) {
|
func TestParseDSA(t *testing.T) {
|
||||||
// We actually exercise the ParsePrivateKey codepath here, as opposed to
|
// We actually exercise the ParsePrivateKey codepath here, as opposed to
|
||||||
// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
|
// using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
|
||||||
|
|||||||
+17
-3
@@ -13,9 +13,10 @@ import (
|
|||||||
// An in-memory packetConn. It is safe to call Close and writePacket
|
// An in-memory packetConn. It is safe to call Close and writePacket
|
||||||
// from different goroutines.
|
// from different goroutines.
|
||||||
type memTransport struct {
|
type memTransport struct {
|
||||||
eof bool
|
eof bool
|
||||||
pending [][]byte
|
pending [][]byte
|
||||||
write *memTransport
|
write *memTransport
|
||||||
|
writeCount uint64
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
*sync.Cond
|
*sync.Cond
|
||||||
}
|
}
|
||||||
@@ -63,9 +64,16 @@ func (t *memTransport) writePacket(p []byte) error {
|
|||||||
copy(c, p)
|
copy(c, p)
|
||||||
t.write.pending = append(t.write.pending, c)
|
t.write.pending = append(t.write.pending, c)
|
||||||
t.write.Cond.Signal()
|
t.write.Cond.Signal()
|
||||||
|
t.writeCount++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *memTransport) getWriteCount() uint64 {
|
||||||
|
t.write.Lock()
|
||||||
|
defer t.write.Unlock()
|
||||||
|
return t.writeCount
|
||||||
|
}
|
||||||
|
|
||||||
func memPipe() (a, b packetConn) {
|
func memPipe() (a, b packetConn) {
|
||||||
t1 := memTransport{}
|
t1 := memTransport{}
|
||||||
t2 := memTransport{}
|
t2 := memTransport{}
|
||||||
@@ -81,6 +89,9 @@ func TestMemPipe(t *testing.T) {
|
|||||||
if err := a.writePacket([]byte{42}); err != nil {
|
if err := a.writePacket([]byte{42}); err != nil {
|
||||||
t.Fatalf("writePacket: %v", err)
|
t.Fatalf("writePacket: %v", err)
|
||||||
}
|
}
|
||||||
|
if wc := a.(*memTransport).getWriteCount(); wc != 1 {
|
||||||
|
t.Fatalf("got %v, want 1", wc)
|
||||||
|
}
|
||||||
if err := a.Close(); err != nil {
|
if err := a.Close(); err != nil {
|
||||||
t.Fatal("Close: ", err)
|
t.Fatal("Close: ", err)
|
||||||
}
|
}
|
||||||
@@ -95,6 +106,9 @@ func TestMemPipe(t *testing.T) {
|
|||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
t.Fatalf("got %v, %v, want EOF", p, err)
|
t.Fatalf("got %v, %v, want EOF", p, err)
|
||||||
}
|
}
|
||||||
|
if wc := b.(*memTransport).getWriteCount(); wc != 0 {
|
||||||
|
t.Fatalf("got %v, want 0", wc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoubleClose(t *testing.T) {
|
func TestDoubleClose(t *testing.T) {
|
||||||
|
|||||||
+120
-42
@@ -10,7 +10,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func muxPair() (*mux, *mux) {
|
func muxPair() (*mux, *mux) {
|
||||||
@@ -112,7 +111,11 @@ func TestMuxReadWrite(t *testing.T) {
|
|||||||
|
|
||||||
magic := "hello world"
|
magic := "hello world"
|
||||||
magicExt := "hello stderr"
|
magicExt := "hello stderr"
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
_, err := s.Write([]byte(magic))
|
_, err := s.Write([]byte(magic))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Write: %v", err)
|
t.Errorf("Write: %v", err)
|
||||||
@@ -152,13 +155,15 @@ func TestMuxChannelOverflow(t *testing.T) {
|
|||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
defer mux.Close()
|
defer mux.Close()
|
||||||
|
|
||||||
wDone := make(chan int, 1)
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
||||||
t.Errorf("could not fill window: %v", err)
|
t.Errorf("could not fill window: %v", err)
|
||||||
}
|
}
|
||||||
writer.Write(make([]byte, 1))
|
writer.Write(make([]byte, 1))
|
||||||
wDone <- 1
|
|
||||||
}()
|
}()
|
||||||
writer.remoteWin.waitWriterBlocked()
|
writer.remoteWin.waitWriterBlocked()
|
||||||
|
|
||||||
@@ -175,7 +180,40 @@ func TestMuxChannelOverflow(t *testing.T) {
|
|||||||
if _, err := reader.SendRequest("hello", true, nil); err == nil {
|
if _, err := reader.SendRequest("hello", true, nil); err == nil {
|
||||||
t.Errorf("SendRequest succeeded.")
|
t.Errorf("SendRequest succeeded.")
|
||||||
}
|
}
|
||||||
<-wDone
|
}
|
||||||
|
|
||||||
|
func TestMuxChannelReadUnblock(t *testing.T) {
|
||||||
|
reader, writer, mux := channelPair(t)
|
||||||
|
defer reader.Close()
|
||||||
|
defer writer.Close()
|
||||||
|
defer mux.Close()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
||||||
|
t.Errorf("could not fill window: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := writer.Write(make([]byte, 1)); err != nil {
|
||||||
|
t.Errorf("Write: %v", err)
|
||||||
|
}
|
||||||
|
writer.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
writer.remoteWin.waitWriterBlocked()
|
||||||
|
|
||||||
|
buf := make([]byte, 32768)
|
||||||
|
for {
|
||||||
|
_, err := reader.Read(buf)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Read: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxChannelCloseWriteUnblock(t *testing.T) {
|
func TestMuxChannelCloseWriteUnblock(t *testing.T) {
|
||||||
@@ -184,20 +222,21 @@ func TestMuxChannelCloseWriteUnblock(t *testing.T) {
|
|||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
defer mux.Close()
|
defer mux.Close()
|
||||||
|
|
||||||
wDone := make(chan int, 1)
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
||||||
t.Errorf("could not fill window: %v", err)
|
t.Errorf("could not fill window: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
|
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
|
||||||
t.Errorf("got %v, want EOF for unblock write", err)
|
t.Errorf("got %v, want EOF for unblock write", err)
|
||||||
}
|
}
|
||||||
wDone <- 1
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
writer.remoteWin.waitWriterBlocked()
|
writer.remoteWin.waitWriterBlocked()
|
||||||
reader.Close()
|
reader.Close()
|
||||||
<-wDone
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxConnectionCloseWriteUnblock(t *testing.T) {
|
func TestMuxConnectionCloseWriteUnblock(t *testing.T) {
|
||||||
@@ -206,20 +245,21 @@ func TestMuxConnectionCloseWriteUnblock(t *testing.T) {
|
|||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
defer mux.Close()
|
defer mux.Close()
|
||||||
|
|
||||||
wDone := make(chan int, 1)
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
if _, err := writer.Write(make([]byte, channelWindowSize)); err != nil {
|
||||||
t.Errorf("could not fill window: %v", err)
|
t.Errorf("could not fill window: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
|
if _, err := writer.Write(make([]byte, 1)); err != io.EOF {
|
||||||
t.Errorf("got %v, want EOF for unblock write", err)
|
t.Errorf("got %v, want EOF for unblock write", err)
|
||||||
}
|
}
|
||||||
wDone <- 1
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
writer.remoteWin.waitWriterBlocked()
|
writer.remoteWin.waitWriterBlocked()
|
||||||
mux.Close()
|
mux.Close()
|
||||||
<-wDone
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxReject(t *testing.T) {
|
func TestMuxReject(t *testing.T) {
|
||||||
@@ -227,7 +267,12 @@ func TestMuxReject(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
ch, ok := <-server.incomingChannels
|
ch, ok := <-server.incomingChannels
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Error("cannot accept channel")
|
t.Error("cannot accept channel")
|
||||||
@@ -267,6 +312,7 @@ func TestMuxChannelRequest(t *testing.T) {
|
|||||||
|
|
||||||
var received int
|
var received int
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
for r := range server.incomingRequests {
|
for r := range server.incomingRequests {
|
||||||
@@ -295,7 +341,6 @@ func TestMuxChannelRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
t.Errorf("SendRequest(no): %v", ok)
|
t.Errorf("SendRequest(no): %v", ok)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Close()
|
client.Close()
|
||||||
@@ -389,13 +434,8 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
|
|||||||
|
|
||||||
// Wait for the server to send the keepalive message and receive back a
|
// Wait for the server to send the keepalive message and receive back a
|
||||||
// response.
|
// response.
|
||||||
select {
|
if err := <-kDone; err != nil {
|
||||||
case err := <-kDone:
|
t.Fatal(err)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
case <-time.After(10 * time.Second):
|
|
||||||
t.Fatalf("server never received ack")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm client hasn't closed.
|
// Confirm client hasn't closed.
|
||||||
@@ -403,13 +443,9 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
|
|||||||
t.Fatalf("failed to send keepalive: %v", err)
|
t.Fatalf("failed to send keepalive: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
// Wait for the server to shut down.
|
||||||
case err := <-kDone:
|
if err := <-kDone; err != nil {
|
||||||
if err != nil {
|
t.Fatal(err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
case <-time.After(10 * time.Second):
|
|
||||||
t.Fatalf("server never shut down")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -525,11 +561,7 @@ func TestMuxClosedChannel(t *testing.T) {
|
|||||||
defer ch.Close()
|
defer ch.Close()
|
||||||
|
|
||||||
// Wait for the server to close the channel and send the keepalive.
|
// Wait for the server to close the channel and send the keepalive.
|
||||||
select {
|
<-kDone
|
||||||
case <-kDone:
|
|
||||||
case <-time.After(10 * time.Second):
|
|
||||||
t.Fatalf("server never received ack")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the channel closed.
|
// Make sure the channel closed.
|
||||||
if _, ok := <-ch.incomingRequests; ok {
|
if _, ok := <-ch.incomingRequests; ok {
|
||||||
@@ -541,22 +573,29 @@ func TestMuxClosedChannel(t *testing.T) {
|
|||||||
t.Fatalf("failed to send keepalive: %v", err)
|
t.Fatalf("failed to send keepalive: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
// Wait for the server to shut down.
|
||||||
case <-kDone:
|
<-kDone
|
||||||
case <-time.After(10 * time.Second):
|
|
||||||
t.Fatalf("server never shut down")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxGlobalRequest(t *testing.T) {
|
func TestMuxGlobalRequest(t *testing.T) {
|
||||||
|
var sawPeek bool
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
defer func() {
|
||||||
|
wg.Wait()
|
||||||
|
if !sawPeek {
|
||||||
|
t.Errorf("never saw 'peek' request")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
clientMux, serverMux := muxPair()
|
clientMux, serverMux := muxPair()
|
||||||
defer serverMux.Close()
|
defer serverMux.Close()
|
||||||
defer clientMux.Close()
|
defer clientMux.Close()
|
||||||
|
|
||||||
var seen bool
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
for r := range serverMux.incomingRequests {
|
for r := range serverMux.incomingRequests {
|
||||||
seen = seen || r.Type == "peek"
|
sawPeek = sawPeek || r.Type == "peek"
|
||||||
if r.WantReply {
|
if r.WantReply {
|
||||||
err := r.Reply(r.Type == "yes",
|
err := r.Reply(r.Type == "yes",
|
||||||
append([]byte(r.Type), r.Payload...))
|
append([]byte(r.Type), r.Payload...))
|
||||||
@@ -586,10 +625,6 @@ func TestMuxGlobalRequest(t *testing.T) {
|
|||||||
t.Errorf("SendRequest(\"no\", true, \"a\"): %v %v %v",
|
t.Errorf("SendRequest(\"no\", true, \"a\"): %v %v %v",
|
||||||
ok, data, err)
|
ok, data, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !seen {
|
|
||||||
t.Errorf("never saw 'peek' request")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxGlobalRequestUnblock(t *testing.T) {
|
func TestMuxGlobalRequestUnblock(t *testing.T) {
|
||||||
@@ -739,7 +774,13 @@ func TestMuxMaxPacketSize(t *testing.T) {
|
|||||||
t.Errorf("could not send packet")
|
t.Errorf("could not send packet")
|
||||||
}
|
}
|
||||||
|
|
||||||
go a.SendRequest("hello", false, nil)
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
a.SendRequest("hello", false, nil)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
_, ok := <-b.incomingRequests
|
_, ok := <-b.incomingRequests
|
||||||
if ok {
|
if ok {
|
||||||
@@ -747,6 +788,43 @@ func TestMuxMaxPacketSize(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMuxChannelWindowDeferredUpdates(t *testing.T) {
|
||||||
|
s, c, mux := channelPair(t)
|
||||||
|
cTransport := mux.conn.(*memTransport)
|
||||||
|
defer s.Close()
|
||||||
|
defer c.Close()
|
||||||
|
defer mux.Close()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
|
||||||
|
data := make([]byte, 1024)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
_, err := s.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Write: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
cWritesInit := cTransport.getWriteCount()
|
||||||
|
buf := make([]byte, 1)
|
||||||
|
for i := 0; i < len(data); i++ {
|
||||||
|
n, err := c.Read(buf)
|
||||||
|
if n != len(buf) || err != nil {
|
||||||
|
t.Fatalf("Read: %v, %v", n, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cWrites := cTransport.getWriteCount() - cWritesInit
|
||||||
|
// reading 1 KiB should not cause any window updates to be sent, but allow
|
||||||
|
// for some unexpected writes
|
||||||
|
if cWrites > 30 {
|
||||||
|
t.Fatalf("reading 1 KiB from channel caused %v writes", cWrites)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Don't ship code with debug=true.
|
// Don't ship code with debug=true.
|
||||||
func TestDebug(t *testing.T) {
|
func TestDebug(t *testing.T) {
|
||||||
if debugMux {
|
if debugMux {
|
||||||
|
|||||||
+23
-4
@@ -64,6 +64,13 @@ type ServerConfig struct {
|
|||||||
// Config contains configuration shared between client and server.
|
// Config contains configuration shared between client and server.
|
||||||
Config
|
Config
|
||||||
|
|
||||||
|
// PublicKeyAuthAlgorithms specifies the supported client public key
|
||||||
|
// authentication algorithms. Note that this should not include certificate
|
||||||
|
// types since those use the underlying algorithm. This list is sent to the
|
||||||
|
// client if it supports the server-sig-algs extension. Order is irrelevant.
|
||||||
|
// If unspecified then a default set of algorithms is used.
|
||||||
|
PublicKeyAuthAlgorithms []string
|
||||||
|
|
||||||
hostKeys []Signer
|
hostKeys []Signer
|
||||||
|
|
||||||
// NoClientAuth is true if clients are allowed to connect without
|
// NoClientAuth is true if clients are allowed to connect without
|
||||||
@@ -201,9 +208,20 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
|
|||||||
if fullConf.MaxAuthTries == 0 {
|
if fullConf.MaxAuthTries == 0 {
|
||||||
fullConf.MaxAuthTries = 6
|
fullConf.MaxAuthTries = 6
|
||||||
}
|
}
|
||||||
|
if len(fullConf.PublicKeyAuthAlgorithms) == 0 {
|
||||||
|
fullConf.PublicKeyAuthAlgorithms = supportedPubKeyAuthAlgos
|
||||||
|
} else {
|
||||||
|
for _, algo := range fullConf.PublicKeyAuthAlgorithms {
|
||||||
|
if !contains(supportedPubKeyAuthAlgos, algo) {
|
||||||
|
c.Close()
|
||||||
|
return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Check if the config contains any unsupported key exchanges
|
// Check if the config contains any unsupported key exchanges
|
||||||
for _, kex := range fullConf.KeyExchanges {
|
for _, kex := range fullConf.KeyExchanges {
|
||||||
if _, ok := serverForbiddenKexAlgos[kex]; ok {
|
if _, ok := serverForbiddenKexAlgos[kex]; ok {
|
||||||
|
c.Close()
|
||||||
return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex)
|
return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,7 +339,7 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
|
|||||||
return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
|
return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *connection,
|
func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, token []byte, s *connection,
|
||||||
sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) {
|
sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) {
|
||||||
gssAPIServer := gssapiConfig.Server
|
gssAPIServer := gssapiConfig.Server
|
||||||
defer gssAPIServer.DeleteSecContext()
|
defer gssAPIServer.DeleteSecContext()
|
||||||
@@ -331,7 +349,7 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c
|
|||||||
outToken []byte
|
outToken []byte
|
||||||
needContinue bool
|
needContinue bool
|
||||||
)
|
)
|
||||||
outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(firstToken)
|
outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, nil, nil
|
return err, nil, nil
|
||||||
}
|
}
|
||||||
@@ -353,6 +371,7 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c
|
|||||||
if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
|
if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
token = userAuthGSSAPITokenReq.Token
|
||||||
}
|
}
|
||||||
packet, err := s.transport.readPacket()
|
packet, err := s.transport.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -524,7 +543,7 @@ userAuthLoop:
|
|||||||
return nil, parseError(msgUserAuthRequest)
|
return nil, parseError(msgUserAuthRequest)
|
||||||
}
|
}
|
||||||
algo := string(algoBytes)
|
algo := string(algoBytes)
|
||||||
if !contains(supportedPubKeyAuthAlgos, underlyingAlgo(algo)) {
|
if !contains(config.PublicKeyAuthAlgorithms, underlyingAlgo(algo)) {
|
||||||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
|
authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -591,7 +610,7 @@ userAuthLoop:
|
|||||||
// algorithm name that corresponds to algo with
|
// algorithm name that corresponds to algo with
|
||||||
// sig.Format. This is usually the same, but
|
// sig.Format. This is usually the same, but
|
||||||
// for certs, the names differ.
|
// for certs, the names differ.
|
||||||
if !contains(supportedPubKeyAuthAlgos, sig.Format) {
|
if !contains(config.PublicKeyAuthAlgorithms, sig.Format) {
|
||||||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
|
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
// 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 ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClientAuthRestrictedPublicKeyAlgos(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
key Signer
|
||||||
|
wantError bool
|
||||||
|
}{
|
||||||
|
{"rsa", testSigners["rsa"], false},
|
||||||
|
{"dsa", testSigners["dsa"], true},
|
||||||
|
{"ed25519", testSigners["ed25519"], true},
|
||||||
|
} {
|
||||||
|
c1, c2, err := netPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("netPipe: %v", err)
|
||||||
|
}
|
||||||
|
defer c1.Close()
|
||||||
|
defer c2.Close()
|
||||||
|
serverConf := &ServerConfig{
|
||||||
|
PublicKeyAuthAlgorithms: []string{KeyAlgoRSASHA256, KeyAlgoRSASHA512},
|
||||||
|
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
serverConf.AddHostKey(testSigners["ecdsap256"])
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
NewServerConn(c1, serverConf)
|
||||||
|
}()
|
||||||
|
|
||||||
|
clientConf := ClientConfig{
|
||||||
|
User: "user",
|
||||||
|
Auth: []AuthMethod{
|
||||||
|
PublicKeys(tt.key),
|
||||||
|
},
|
||||||
|
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||||
|
if err != nil {
|
||||||
|
if !tt.wantError {
|
||||||
|
t.Errorf("%s: got unexpected error %q", tt.name, err.Error())
|
||||||
|
}
|
||||||
|
} else if tt.wantError {
|
||||||
|
t.Errorf("%s: succeeded, but want error", tt.name)
|
||||||
|
}
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewServerConnValidationErrors(t *testing.T) {
|
||||||
|
serverConf := &ServerConfig{
|
||||||
|
PublicKeyAuthAlgorithms: []string{CertAlgoRSAv01},
|
||||||
|
}
|
||||||
|
c := &markerConn{}
|
||||||
|
_, _, _, err := NewServerConn(c, serverConf)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("NewServerConn with invalid public key auth algorithms succeeded")
|
||||||
|
}
|
||||||
|
if !c.isClosed() {
|
||||||
|
t.Fatal("NewServerConn with invalid public key auth algorithms left connection open")
|
||||||
|
}
|
||||||
|
if c.isUsed() {
|
||||||
|
t.Fatal("NewServerConn with invalid public key auth algorithms used connection")
|
||||||
|
}
|
||||||
|
|
||||||
|
serverConf = &ServerConfig{
|
||||||
|
Config: Config{
|
||||||
|
KeyExchanges: []string{kexAlgoDHGEXSHA256},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c = &markerConn{}
|
||||||
|
_, _, _, err = NewServerConn(c, serverConf)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("NewServerConn with unsupported key exchange succeeded")
|
||||||
|
}
|
||||||
|
if !c.isClosed() {
|
||||||
|
t.Fatal("NewServerConn with unsupported key exchange left connection open")
|
||||||
|
}
|
||||||
|
if c.isUsed() {
|
||||||
|
t.Fatal("NewServerConn with unsupported key exchange used connection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type markerConn struct {
|
||||||
|
closed uint32
|
||||||
|
used uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *markerConn) isClosed() bool {
|
||||||
|
return atomic.LoadUint32(&c.closed) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *markerConn) isUsed() bool {
|
||||||
|
return atomic.LoadUint32(&c.used) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *markerConn) Close() error {
|
||||||
|
atomic.StoreUint32(&c.closed, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *markerConn) Read(b []byte) (n int, err error) {
|
||||||
|
atomic.StoreUint32(&c.used, 1)
|
||||||
|
if atomic.LoadUint32(&c.closed) != 0 {
|
||||||
|
return 0, net.ErrClosed
|
||||||
|
} else {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *markerConn) Write(b []byte) (n int, err error) {
|
||||||
|
atomic.StoreUint32(&c.used, 1)
|
||||||
|
if atomic.LoadUint32(&c.closed) != 0 {
|
||||||
|
return 0, net.ErrClosed
|
||||||
|
} else {
|
||||||
|
return 0, io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*markerConn) LocalAddr() net.Addr { return nil }
|
||||||
|
func (*markerConn) RemoteAddr() net.Addr { return nil }
|
||||||
|
|
||||||
|
func (*markerConn) SetDeadline(t time.Time) error { return nil }
|
||||||
|
func (*markerConn) SetReadDeadline(t time.Time) error { return nil }
|
||||||
|
func (*markerConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||||
+59
-12
@@ -13,6 +13,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
@@ -27,8 +28,14 @@ func dial(handler serverType, t *testing.T) *Client {
|
|||||||
t.Fatalf("netPipe: %v", err)
|
t.Fatalf("netPipe: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer c1.Close()
|
defer func() {
|
||||||
|
c1.Close()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
conf := ServerConfig{
|
conf := ServerConfig{
|
||||||
NoClientAuth: true,
|
NoClientAuth: true,
|
||||||
}
|
}
|
||||||
@@ -39,7 +46,11 @@ func dial(handler serverType, t *testing.T) *Client {
|
|||||||
t.Errorf("Unable to handshake: %v", err)
|
t.Errorf("Unable to handshake: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go DiscardRequests(reqs)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
DiscardRequests(reqs)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
for newCh := range chans {
|
for newCh := range chans {
|
||||||
if newCh.ChannelType() != "session" {
|
if newCh.ChannelType() != "session" {
|
||||||
@@ -52,8 +63,10 @@ func dial(handler serverType, t *testing.T) *Client {
|
|||||||
t.Errorf("Accept: %v", err)
|
t.Errorf("Accept: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
handler(ch, inReqs, t)
|
handler(ch, inReqs, t)
|
||||||
|
wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
if err := conn.Wait(); err != io.EOF {
|
if err := conn.Wait(); err != io.EOF {
|
||||||
@@ -338,8 +351,13 @@ func TestServerWindow(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer session.Close()
|
defer session.Close()
|
||||||
result := make(chan []byte)
|
|
||||||
|
|
||||||
|
serverStdin, err := session.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("StdinPipe failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(chan []byte)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(result)
|
defer close(result)
|
||||||
echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
|
echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
|
||||||
@@ -355,10 +373,6 @@ func TestServerWindow(t *testing.T) {
|
|||||||
result <- echoedBuf.Bytes()
|
result <- echoedBuf.Bytes()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
serverStdin, err := session.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("StdinPipe failed: %v", err)
|
|
||||||
}
|
|
||||||
written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes)
|
written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to copy origBuf to serverStdin: %v", err)
|
t.Errorf("failed to copy origBuf to serverStdin: %v", err)
|
||||||
@@ -648,29 +662,44 @@ func TestSessionID(t *testing.T) {
|
|||||||
User: "user",
|
User: "user",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
|
||||||
srvErrCh := make(chan error, 1)
|
srvErrCh := make(chan error, 1)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
conn, chans, reqs, err := NewServerConn(c1, serverConf)
|
conn, chans, reqs, err := NewServerConn(c1, serverConf)
|
||||||
srvErrCh <- err
|
srvErrCh <- err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serverID <- conn.SessionID()
|
serverID <- conn.SessionID()
|
||||||
go DiscardRequests(reqs)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
DiscardRequests(reqs)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
for ch := range chans {
|
for ch := range chans {
|
||||||
ch.Reject(Prohibited, "")
|
ch.Reject(Prohibited, "")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
cliErrCh := make(chan error, 1)
|
cliErrCh := make(chan error, 1)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
|
conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
|
||||||
cliErrCh <- err
|
cliErrCh <- err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
clientID <- conn.SessionID()
|
clientID <- conn.SessionID()
|
||||||
go DiscardRequests(reqs)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
DiscardRequests(reqs)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
for ch := range chans {
|
for ch := range chans {
|
||||||
ch.Reject(Prohibited, "")
|
ch.Reject(Prohibited, "")
|
||||||
}
|
}
|
||||||
@@ -738,6 +767,8 @@ func TestHostKeyAlgorithms(t *testing.T) {
|
|||||||
serverConf.AddHostKey(testSigners["rsa"])
|
serverConf.AddHostKey(testSigners["rsa"])
|
||||||
serverConf.AddHostKey(testSigners["ecdsa"])
|
serverConf.AddHostKey(testSigners["ecdsa"])
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
connect := func(clientConf *ClientConfig, want string) {
|
connect := func(clientConf *ClientConfig, want string) {
|
||||||
var alg string
|
var alg string
|
||||||
clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error {
|
clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error {
|
||||||
@@ -751,7 +782,11 @@ func TestHostKeyAlgorithms(t *testing.T) {
|
|||||||
defer c1.Close()
|
defer c1.Close()
|
||||||
defer c2.Close()
|
defer c2.Close()
|
||||||
|
|
||||||
go NewServerConn(c1, serverConf)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
NewServerConn(c1, serverConf)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
_, _, _, err = NewClientConn(c2, "", clientConf)
|
_, _, _, err = NewClientConn(c2, "", clientConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewClientConn: %v", err)
|
t.Fatalf("NewClientConn: %v", err)
|
||||||
@@ -785,7 +820,11 @@ func TestHostKeyAlgorithms(t *testing.T) {
|
|||||||
defer c1.Close()
|
defer c1.Close()
|
||||||
defer c2.Close()
|
defer c2.Close()
|
||||||
|
|
||||||
go NewServerConn(c1, serverConf)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
NewServerConn(c1, serverConf)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"}
|
clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"}
|
||||||
_, _, _, err = NewClientConn(c2, "", clientConf)
|
_, _, _, err = NewClientConn(c2, "", clientConf)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -818,14 +857,22 @@ func TestServerClientAuthCallback(t *testing.T) {
|
|||||||
User: someUsername,
|
User: someUsername,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
t.Cleanup(wg.Wait)
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
_, chans, reqs, err := NewServerConn(c1, serverConf)
|
_, chans, reqs, err := NewServerConn(c1, serverConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("server handshake: %v", err)
|
t.Errorf("server handshake: %v", err)
|
||||||
userCh <- "error"
|
userCh <- "error"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go DiscardRequests(reqs)
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
DiscardRequests(reqs)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
for ch := range chans {
|
for ch := range chans {
|
||||||
ch.Reject(Prohibited, "")
|
ch.Reject(Prohibited, "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -332,6 +333,40 @@ func (l *tcpListener) Addr() net.Addr {
|
|||||||
return l.laddr
|
return l.laddr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DialContext initiates a connection to the addr from the remote host.
|
||||||
|
//
|
||||||
|
// The provided Context must be non-nil. If the context expires before the
|
||||||
|
// connection is complete, an error is returned. Once successfully connected,
|
||||||
|
// any expiration of the context will not affect the connection.
|
||||||
|
//
|
||||||
|
// See func Dial for additional information.
|
||||||
|
func (c *Client) DialContext(ctx context.Context, n, addr string) (net.Conn, error) {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
type connErr struct {
|
||||||
|
conn net.Conn
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
ch := make(chan connErr)
|
||||||
|
go func() {
|
||||||
|
conn, err := c.Dial(n, addr)
|
||||||
|
select {
|
||||||
|
case ch <- connErr{conn, err}:
|
||||||
|
case <-ctx.Done():
|
||||||
|
if conn != nil {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case res := <-ch:
|
||||||
|
return res.conn, res.err
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dial initiates a connection to the addr from the remote host.
|
// Dial initiates a connection to the addr from the remote host.
|
||||||
// The resulting connection has a zero LocalAddr() and RemoteAddr().
|
// The resulting connection has a zero LocalAddr() and RemoteAddr().
|
||||||
func (c *Client) Dial(n, addr string) (net.Conn, error) {
|
func (c *Client) Dial(n, addr string) (net.Conn, error) {
|
||||||
|
|||||||
@@ -5,7 +5,10 @@
|
|||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAutoPortListenBroken(t *testing.T) {
|
func TestAutoPortListenBroken(t *testing.T) {
|
||||||
@@ -18,3 +21,33 @@ func TestAutoPortListenBroken(t *testing.T) {
|
|||||||
t.Errorf("version %q marked as broken", works)
|
t.Errorf("version %q marked as broken", works)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClientImplementsDialContext(t *testing.T) {
|
||||||
|
type ContextDialer interface {
|
||||||
|
DialContext(context.Context, string, string) (net.Conn, error)
|
||||||
|
}
|
||||||
|
// Belt and suspenders assertion, since package net does not
|
||||||
|
// declare a ContextDialer type.
|
||||||
|
var _ ContextDialer = &net.Dialer{}
|
||||||
|
var _ ContextDialer = &Client{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientDialContextWithCancel(t *testing.T) {
|
||||||
|
c := &Client{}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
_, err := c.DialContext(ctx, "tcp", "localhost:1000")
|
||||||
|
if err != context.Canceled {
|
||||||
|
t.Errorf("DialContext: got nil error, expected %v", context.Canceled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientDialContextWithDeadline(t *testing.T) {
|
||||||
|
c := &Client{}
|
||||||
|
ctx, cancel := context.WithDeadline(context.Background(), time.Now())
|
||||||
|
defer cancel()
|
||||||
|
_, err := c.DialContext(ctx, "tcp", "localhost:1000")
|
||||||
|
if err != context.DeadlineExceeded {
|
||||||
|
t.Errorf("DialContext: got nil error, expected %v", context.DeadlineExceeded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !windows && !js && !wasip1
|
//go:build !windows && !js && !wasip1
|
||||||
// +build !windows,!js,!wasip1
|
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
// direct-tcpip and direct-streamlocal functional tests
|
// direct-tcpip and direct-streamlocal functional tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -47,7 +47,11 @@ func testDial(t *testing.T, n, listenAddr string, x dialTester) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
conn, err := sshConn.Dial(n, l.Addr().String())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
conn, err := sshConn.DialContext(ctx, n, l.Addr().String())
|
||||||
|
// Canceling the context after dial should have no effect
|
||||||
|
// on the opened connection.
|
||||||
|
cancel()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Dial: %v", err)
|
t.Fatalf("Dial: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
// (for linux) in file ./sshd_test_pw.c.
|
// (for linux) in file ./sshd_test_pw.c.
|
||||||
|
|
||||||
//go:build linux
|
//go:build linux
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// 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 test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
type exitStatusMsg struct {
|
||||||
|
Status uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// goServer is a test Go SSH server that accepts public key and certificate
|
||||||
|
// authentication and replies with a 0 exit status to any exec request without
|
||||||
|
// running any commands.
|
||||||
|
type goTestServer struct {
|
||||||
|
listener net.Listener
|
||||||
|
config *ssh.ServerConfig
|
||||||
|
done <-chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestServer(config *ssh.ServerConfig) (*goTestServer, error) {
|
||||||
|
server := &goTestServer{
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
listener, err := net.Listen("tcp", "127.0.0.1:")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
server.listener = listener
|
||||||
|
done := make(chan struct{}, 1)
|
||||||
|
server.done = done
|
||||||
|
go server.acceptConnections(done)
|
||||||
|
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *goTestServer) port() (string, error) {
|
||||||
|
_, port, err := net.SplitHostPort(s.listener.Addr().String())
|
||||||
|
return port, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *goTestServer) acceptConnections(done chan<- struct{}) {
|
||||||
|
defer close(done)
|
||||||
|
|
||||||
|
for {
|
||||||
|
c, err := s.listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, chans, reqs, err := ssh.NewServerConn(c, s.config)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go ssh.DiscardRequests(reqs)
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
for newChannel := range chans {
|
||||||
|
if newChannel.ChannelType() != "session" {
|
||||||
|
newChannel.Reject(ssh.UnknownChannelType, "unknown channel type")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, requests, err := newChannel.Accept()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(in <-chan *ssh.Request) {
|
||||||
|
for req := range in {
|
||||||
|
ok := false
|
||||||
|
switch req.Type {
|
||||||
|
case "exec":
|
||||||
|
ok = true
|
||||||
|
go func() {
|
||||||
|
channel.SendRequest("exit-status", false, ssh.Marshal(&exitStatusMsg{Status: 0}))
|
||||||
|
channel.Close()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if req.WantReply {
|
||||||
|
req.Reply(ok, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(requests)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *goTestServer) Close() error {
|
||||||
|
err := s.listener.Close()
|
||||||
|
// wait for the accept loop to exit
|
||||||
|
<-s.done
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !windows && !js && !wasip1
|
//go:build !windows && !js && !wasip1
|
||||||
// +build !windows,!js,!wasip1
|
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
// 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 test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/internal/testenv"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/crypto/ssh/testdata"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sshClient(t *testing.T) string {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping test that executes OpenSSH in -short mode")
|
||||||
|
}
|
||||||
|
sshCLI := os.Getenv("SSH_CLI_PATH")
|
||||||
|
if sshCLI == "" {
|
||||||
|
sshCLI = "ssh"
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
sshCLI, err = exec.LookPath(sshCLI)
|
||||||
|
if err != nil {
|
||||||
|
t.Skipf("Can't find an ssh(1) client to test against: %v", err)
|
||||||
|
}
|
||||||
|
return sshCLI
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSSHCLIAuth(t *testing.T) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skipf("always fails on Windows, see #64403")
|
||||||
|
}
|
||||||
|
sshCLI := sshClient(t)
|
||||||
|
dir := t.TempDir()
|
||||||
|
keyPrivPath := filepath.Join(dir, "rsa")
|
||||||
|
|
||||||
|
for fn, content := range map[string][]byte{
|
||||||
|
keyPrivPath: testdata.PEMBytes["rsa"],
|
||||||
|
keyPrivPath + ".pub": ssh.MarshalAuthorizedKey(testPublicKeys["rsa"]),
|
||||||
|
filepath.Join(dir, "rsa-cert.pub"): testdata.SSHCertificates["rsa-user-testcertificate"],
|
||||||
|
} {
|
||||||
|
if err := os.WriteFile(fn, content, 0600); err != nil {
|
||||||
|
t.Fatalf("WriteFile(%q): %v", fn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
certChecker := ssh.CertChecker{
|
||||||
|
IsUserAuthority: func(k ssh.PublicKey) bool {
|
||||||
|
return bytes.Equal(k.Marshal(), testPublicKeys["ca"].Marshal())
|
||||||
|
},
|
||||||
|
UserKeyFallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
|
||||||
|
if conn.User() == "testpubkey" && bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &ssh.ServerConfig{
|
||||||
|
PublicKeyCallback: certChecker.Authenticate,
|
||||||
|
}
|
||||||
|
config.AddHostKey(testSigners["rsa"])
|
||||||
|
|
||||||
|
server, err := newTestServer(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to start test server: %v", err)
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
port, err := server.port()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to get server port: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test public key authentication.
|
||||||
|
cmd := testenv.Command(t, sshCLI, "-vvv", "-i", keyPrivPath, "-o", "StrictHostKeyChecking=no",
|
||||||
|
"-p", port, "testpubkey@127.0.0.1", "true")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("public key authentication failed, error: %v, command output %q", err, string(out))
|
||||||
|
}
|
||||||
|
// Test SSH user certificate authentication.
|
||||||
|
// The username must match one of the principals included in the certificate.
|
||||||
|
// The certificate "rsa-user-testcertificate" has "testcertificate" as principal.
|
||||||
|
cmd = testenv.Command(t, sshCLI, "-vvv", "-i", keyPrivPath, "-o", "StrictHostKeyChecking=no",
|
||||||
|
"-p", port, "testcertificate@127.0.0.1", "true")
|
||||||
|
out, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("user certificate authentication failed, error: %v, command output %q", err, string(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
// Run sshd:
|
// Run sshd:
|
||||||
// LD_PRELOAD="sshd_test_pw.so" TEST_USER="..." TEST_PASSWD="..." sshd ...
|
// LD_PRELOAD="sshd_test_pw.so" TEST_USER="..." TEST_PASSWD="..." sshd ...
|
||||||
|
|
||||||
// +build ignore
|
//go:build ignore
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || plan9 || solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || plan9 || solaris
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd plan9 solaris
|
|
||||||
|
|
||||||
package test
|
package test
|
||||||
|
|
||||||
|
|||||||
Vendored
+6
@@ -195,6 +195,12 @@ var SSHCertificates = map[string][]byte{
|
|||||||
"rsa-sha2-256": []byte(`ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgOyK28gunJkM60qp4EbsYAjgbUsyjS8u742OLjipIgc0AAAADAQABAAAAgQC8A6FGHDiWCSREAXCq6yBfNVr0xCVG2CzvktFNRpue+RXrGs/2a6ySEJQb3IYquw7HlJgu6fg3WIWhOmHCjfpG0PrL4CRwbqQ2LaPPXhJErWYejcD8Di00cF3677+G10KMZk9RXbmHtuBFZT98wxg8j+ZsBMqGM1+7yrWUvynswQAAAAAAAAAAAAAAAgAAABRob3N0LmV4YW1wbGUuY29tLWtleQAAABQAAAAQaG9zdC5leGFtcGxlLmNvbQAAAABeSMJ4AAAAAHBPBLwAAAAAAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQC+D11D0hEbn2Vglv4YRJ8pZNyHjIGmvth3DWOQrq++2vH2MujmGQDxfr4SVE9GpMBlKU3lwGbpgIBxAg6yZcNSfo6PWVU9ACg6NMFO+yMzc2MaG+/naQdNjSewywF5j2rkNO2XOaViRVSrZroe2B/aY2LTV0jDl8nu5NOjwRs1/s7SLe5z1rw/X0dpmXk0qJY3gQhmR8HZZ1dhEkJUGwaBCPd0T8asSYf1Ag2rUD4aQ28r3q69mbwfWOOa6rMemVZruUV5dzHwVNVNtVv+ImtnYtz8m8g+K0plaGptHn3KsaOnASkh3tujhaE7kvc4HR9Igli9+76jhZie3h/dTN5zAAABFAAAAAxyc2Etc2hhMi0yNTYAAAEAbG4De/+QiqopPS3O1H7ySeEUCY56qmdgr02sFErnihdXPDaWXUXxacvJHaEtLrSTSaPL/3v3iKvjLWDOHaQ5c+cN9J7Tqzso7RQCXZD2nK9bwCUyBoiDyBCRe8w4DQEtfL5okpVzQsSAiojQ8hBohMOpy3gFfXrdm4PVC1ZKqlZh4fAc7ajieRq/Tpq2xOLdHwxkcgPNR83WVHva6K9/xjev/5n227/gkHo0qbGs8YYDOFXIEhENi+B23IzxdNVieWdyQpYpe0C2i95Jhyo0wJmaFY2ArruTS+D1jGQQpMPvAQRy26/A5hI83GLhpwyhrN/M8wCxzAhyPL6Ieuh5tQ== host.example.com
|
"rsa-sha2-256": []byte(`ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgOyK28gunJkM60qp4EbsYAjgbUsyjS8u742OLjipIgc0AAAADAQABAAAAgQC8A6FGHDiWCSREAXCq6yBfNVr0xCVG2CzvktFNRpue+RXrGs/2a6ySEJQb3IYquw7HlJgu6fg3WIWhOmHCjfpG0PrL4CRwbqQ2LaPPXhJErWYejcD8Di00cF3677+G10KMZk9RXbmHtuBFZT98wxg8j+ZsBMqGM1+7yrWUvynswQAAAAAAAAAAAAAAAgAAABRob3N0LmV4YW1wbGUuY29tLWtleQAAABQAAAAQaG9zdC5leGFtcGxlLmNvbQAAAABeSMJ4AAAAAHBPBLwAAAAAAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQC+D11D0hEbn2Vglv4YRJ8pZNyHjIGmvth3DWOQrq++2vH2MujmGQDxfr4SVE9GpMBlKU3lwGbpgIBxAg6yZcNSfo6PWVU9ACg6NMFO+yMzc2MaG+/naQdNjSewywF5j2rkNO2XOaViRVSrZroe2B/aY2LTV0jDl8nu5NOjwRs1/s7SLe5z1rw/X0dpmXk0qJY3gQhmR8HZZ1dhEkJUGwaBCPd0T8asSYf1Ag2rUD4aQ28r3q69mbwfWOOa6rMemVZruUV5dzHwVNVNtVv+ImtnYtz8m8g+K0plaGptHn3KsaOnASkh3tujhaE7kvc4HR9Igli9+76jhZie3h/dTN5zAAABFAAAAAxyc2Etc2hhMi0yNTYAAAEAbG4De/+QiqopPS3O1H7ySeEUCY56qmdgr02sFErnihdXPDaWXUXxacvJHaEtLrSTSaPL/3v3iKvjLWDOHaQ5c+cN9J7Tqzso7RQCXZD2nK9bwCUyBoiDyBCRe8w4DQEtfL5okpVzQsSAiojQ8hBohMOpy3gFfXrdm4PVC1ZKqlZh4fAc7ajieRq/Tpq2xOLdHwxkcgPNR83WVHva6K9/xjev/5n227/gkHo0qbGs8YYDOFXIEhENi+B23IzxdNVieWdyQpYpe0C2i95Jhyo0wJmaFY2ArruTS+D1jGQQpMPvAQRy26/A5hI83GLhpwyhrN/M8wCxzAhyPL6Ieuh5tQ== host.example.com
|
||||||
`),
|
`),
|
||||||
"rsa-sha2-512": []byte(`ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgFGv4IpXfs4L/Y0b3rmUdPFhWoUrVnXuPxXr6aHGs7wgAAAADAQABAAAAgQC8A6FGHDiWCSREAXCq6yBfNVr0xCVG2CzvktFNRpue+RXrGs/2a6ySEJQb3IYquw7HlJgu6fg3WIWhOmHCjfpG0PrL4CRwbqQ2LaPPXhJErWYejcD8Di00cF3677+G10KMZk9RXbmHtuBFZT98wxg8j+ZsBMqGM1+7yrWUvynswQAAAAAAAAAAAAAAAgAAABRob3N0LmV4YW1wbGUuY29tLWtleQAAABQAAAAQaG9zdC5leGFtcGxlLmNvbQAAAABeSMRYAAAAAHBPBp4AAAAAAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQC+D11D0hEbn2Vglv4YRJ8pZNyHjIGmvth3DWOQrq++2vH2MujmGQDxfr4SVE9GpMBlKU3lwGbpgIBxAg6yZcNSfo6PWVU9ACg6NMFO+yMzc2MaG+/naQdNjSewywF5j2rkNO2XOaViRVSrZroe2B/aY2LTV0jDl8nu5NOjwRs1/s7SLe5z1rw/X0dpmXk0qJY3gQhmR8HZZ1dhEkJUGwaBCPd0T8asSYf1Ag2rUD4aQ28r3q69mbwfWOOa6rMemVZruUV5dzHwVNVNtVv+ImtnYtz8m8g+K0plaGptHn3KsaOnASkh3tujhaE7kvc4HR9Igli9+76jhZie3h/dTN5zAAABFAAAAAxyc2Etc2hhMi01MTIAAAEAnF4fVj6mm+UFeNCIf9AKJCv9WzymjjPvzzmaMWWkPWqoV0P0m5SiYfvbY9SbA73Blpv8SOr0DmpublF183kodREia4KyVuC8hLhSCV2Y16hy9MBegOZMepn80w+apj7Rn9QCz5OfEakDdztp6OWTBtqxnZFcTQ4XrgFkNWeWRElGdEvAVNn2WHwHi4EIdz0mdv48Imv5SPlOuW862ZdFG4Do1dUfDIiGsBofLlgcyIYlf+eNHul6sBeUkuwFxisMpI5DQzNp8PX1g/QJA2wzwT674PTqDXNttKjyh50Fdr4sXxm9Gz1+jVLoESvFNa55ERdSyAqNu4wTy11MZsWwSA== host.example.com
|
"rsa-sha2-512": []byte(`ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgFGv4IpXfs4L/Y0b3rmUdPFhWoUrVnXuPxXr6aHGs7wgAAAADAQABAAAAgQC8A6FGHDiWCSREAXCq6yBfNVr0xCVG2CzvktFNRpue+RXrGs/2a6ySEJQb3IYquw7HlJgu6fg3WIWhOmHCjfpG0PrL4CRwbqQ2LaPPXhJErWYejcD8Di00cF3677+G10KMZk9RXbmHtuBFZT98wxg8j+ZsBMqGM1+7yrWUvynswQAAAAAAAAAAAAAAAgAAABRob3N0LmV4YW1wbGUuY29tLWtleQAAABQAAAAQaG9zdC5leGFtcGxlLmNvbQAAAABeSMRYAAAAAHBPBp4AAAAAAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQC+D11D0hEbn2Vglv4YRJ8pZNyHjIGmvth3DWOQrq++2vH2MujmGQDxfr4SVE9GpMBlKU3lwGbpgIBxAg6yZcNSfo6PWVU9ACg6NMFO+yMzc2MaG+/naQdNjSewywF5j2rkNO2XOaViRVSrZroe2B/aY2LTV0jDl8nu5NOjwRs1/s7SLe5z1rw/X0dpmXk0qJY3gQhmR8HZZ1dhEkJUGwaBCPd0T8asSYf1Ag2rUD4aQ28r3q69mbwfWOOa6rMemVZruUV5dzHwVNVNtVv+ImtnYtz8m8g+K0plaGptHn3KsaOnASkh3tujhaE7kvc4HR9Igli9+76jhZie3h/dTN5zAAABFAAAAAxyc2Etc2hhMi01MTIAAAEAnF4fVj6mm+UFeNCIf9AKJCv9WzymjjPvzzmaMWWkPWqoV0P0m5SiYfvbY9SbA73Blpv8SOr0DmpublF183kodREia4KyVuC8hLhSCV2Y16hy9MBegOZMepn80w+apj7Rn9QCz5OfEakDdztp6OWTBtqxnZFcTQ4XrgFkNWeWRElGdEvAVNn2WHwHi4EIdz0mdv48Imv5SPlOuW862ZdFG4Do1dUfDIiGsBofLlgcyIYlf+eNHul6sBeUkuwFxisMpI5DQzNp8PX1g/QJA2wzwT674PTqDXNttKjyh50Fdr4sXxm9Gz1+jVLoESvFNa55ERdSyAqNu4wTy11MZsWwSA== host.example.com
|
||||||
|
`),
|
||||||
|
// Assumes "ca" key above in file named "ca", sign a user cert for "rsa.pub"
|
||||||
|
// using "testcertificate" as principal:
|
||||||
|
//
|
||||||
|
// ssh-keygen -s ca -I username -n testcertificate rsa.pub
|
||||||
|
"rsa-user-testcertificate": []byte(`ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgr0MnhSf+KkQWEQmweJOGsJfOrUt80pQZDaI9YiwSfDUAAAADAQABAAAAgQC8A6FGHDiWCSREAXCq6yBfNVr0xCVG2CzvktFNRpue+RXrGs/2a6ySEJQb3IYquw7HlJgu6fg3WIWhOmHCjfpG0PrL4CRwbqQ2LaPPXhJErWYejcD8Di00cF3677+G10KMZk9RXbmHtuBFZT98wxg8j+ZsBMqGM1+7yrWUvynswQAAAAAAAAAAAAAAAQAAAAh1c2VybmFtZQAAABMAAAAPdGVzdGNlcnRpZmljYXRlAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQC+D11D0hEbn2Vglv4YRJ8pZNyHjIGmvth3DWOQrq++2vH2MujmGQDxfr4SVE9GpMBlKU3lwGbpgIBxAg6yZcNSfo6PWVU9ACg6NMFO+yMzc2MaG+/naQdNjSewywF5j2rkNO2XOaViRVSrZroe2B/aY2LTV0jDl8nu5NOjwRs1/s7SLe5z1rw/X0dpmXk0qJY3gQhmR8HZZ1dhEkJUGwaBCPd0T8asSYf1Ag2rUD4aQ28r3q69mbwfWOOa6rMemVZruUV5dzHwVNVNtVv+ImtnYtz8m8g+K0plaGptHn3KsaOnASkh3tujhaE7kvc4HR9Igli9+76jhZie3h/dTN5zAAABFAAAAAxyc2Etc2hhMi01MTIAAAEAFuA+67KvnlmcodIp0Lv4mR9UW/CHghAaN1csBJTkI8mx3wXKyIPTsS2uXboEhWD0a7S9gps2SEwC5m6E3kV2Rzg7aH1S03GZqMvVlK2VHe7fzuoW2yOKk6yEPjeTF0pKCFbUQ6mce8pRpD/zdvjG0Z287XM3c3Axlrn7qq7TS0MDTjEZ/dsUNFHxep3co/HuAsWVWPVDItr/FPnvZ6WVH1yc8N/AJn0gLHobkGgug22vI9rNIge1wrnXxU9BUSouzkau/PQsrCQapnn+I1H7HaQt0wdk45nxMP+ags+HRI9qpX/p8WDn6+zpqYqN/nfw2aoytyaJqhsV32Teuqtrgg== rsa.pub
|
||||||
`),
|
`),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+27
-5
@@ -49,6 +49,9 @@ type transport struct {
|
|||||||
rand io.Reader
|
rand io.Reader
|
||||||
isClient bool
|
isClient bool
|
||||||
io.Closer
|
io.Closer
|
||||||
|
|
||||||
|
strictMode bool
|
||||||
|
initialKEXDone bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// packetCipher represents a combination of SSH encryption/MAC
|
// packetCipher represents a combination of SSH encryption/MAC
|
||||||
@@ -74,6 +77,18 @@ type connectionState struct {
|
|||||||
pendingKeyChange chan packetCipher
|
pendingKeyChange chan packetCipher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *transport) setStrictMode() error {
|
||||||
|
if t.reader.seqNum != 1 {
|
||||||
|
return errors.New("ssh: sequence number != 1 when strict KEX mode requested")
|
||||||
|
}
|
||||||
|
t.strictMode = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *transport) setInitialKEXDone() {
|
||||||
|
t.initialKEXDone = true
|
||||||
|
}
|
||||||
|
|
||||||
// prepareKeyChange sets up key material for a keychange. The key changes in
|
// prepareKeyChange sets up key material for a keychange. The key changes in
|
||||||
// both directions are triggered by reading and writing a msgNewKey packet
|
// both directions are triggered by reading and writing a msgNewKey packet
|
||||||
// respectively.
|
// respectively.
|
||||||
@@ -112,11 +127,12 @@ func (t *transport) printPacket(p []byte, write bool) {
|
|||||||
// Read and decrypt next packet.
|
// Read and decrypt next packet.
|
||||||
func (t *transport) readPacket() (p []byte, err error) {
|
func (t *transport) readPacket() (p []byte, err error) {
|
||||||
for {
|
for {
|
||||||
p, err = t.reader.readPacket(t.bufReader)
|
p, err = t.reader.readPacket(t.bufReader, t.strictMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) {
|
// in strict mode we pass through DEBUG and IGNORE packets only during the initial KEX
|
||||||
|
if len(p) == 0 || (t.strictMode && !t.initialKEXDone) || (p[0] != msgIgnore && p[0] != msgDebug) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,7 +143,7 @@ func (t *transport) readPacket() (p []byte, err error) {
|
|||||||
return p, err
|
return p, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
|
func (s *connectionState) readPacket(r *bufio.Reader, strictMode bool) ([]byte, error) {
|
||||||
packet, err := s.packetCipher.readCipherPacket(s.seqNum, r)
|
packet, err := s.packetCipher.readCipherPacket(s.seqNum, r)
|
||||||
s.seqNum++
|
s.seqNum++
|
||||||
if err == nil && len(packet) == 0 {
|
if err == nil && len(packet) == 0 {
|
||||||
@@ -140,6 +156,9 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
|
|||||||
select {
|
select {
|
||||||
case cipher := <-s.pendingKeyChange:
|
case cipher := <-s.pendingKeyChange:
|
||||||
s.packetCipher = cipher
|
s.packetCipher = cipher
|
||||||
|
if strictMode {
|
||||||
|
s.seqNum = 0
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("ssh: got bogus newkeys message")
|
return nil, errors.New("ssh: got bogus newkeys message")
|
||||||
}
|
}
|
||||||
@@ -170,10 +189,10 @@ func (t *transport) writePacket(packet []byte) error {
|
|||||||
if debugTransport {
|
if debugTransport {
|
||||||
t.printPacket(packet, true)
|
t.printPacket(packet, true)
|
||||||
}
|
}
|
||||||
return t.writer.writePacket(t.bufWriter, t.rand, packet)
|
return t.writer.writePacket(t.bufWriter, t.rand, packet, t.strictMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error {
|
func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte, strictMode bool) error {
|
||||||
changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
|
changeKeys := len(packet) > 0 && packet[0] == msgNewKeys
|
||||||
|
|
||||||
err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet)
|
err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet)
|
||||||
@@ -188,6 +207,9 @@ func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []
|
|||||||
select {
|
select {
|
||||||
case cipher := <-s.pendingKeyChange:
|
case cipher := <-s.pendingKeyChange:
|
||||||
s.packetCipher = cipher
|
s.packetCipher = cipher
|
||||||
|
if strictMode {
|
||||||
|
s.seqNum = 0
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic("ssh: no key material for msgNewKeys")
|
panic("ssh: no key material for msgNewKeys")
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user