Compare commits

...

1 Commits

Author SHA1 Message Date
Matthew Hooker
e5f6958803
update ssh library 2018-04-22 19:47:37 -07:00
25 changed files with 1664 additions and 356 deletions

View File

@ -0,0 +1,198 @@
// 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.
// Package ChaCha20 implements the core ChaCha20 function as specified in https://tools.ietf.org/html/rfc7539#section-2.3.
package chacha20
import "encoding/binary"
const rounds = 20
// core applies the ChaCha20 core function to 16-byte input in, 32-byte key k,
// and 16-byte constant c, and puts the result into 64-byte array out.
func core(out *[64]byte, in *[16]byte, k *[32]byte) {
j0 := uint32(0x61707865)
j1 := uint32(0x3320646e)
j2 := uint32(0x79622d32)
j3 := uint32(0x6b206574)
j4 := binary.LittleEndian.Uint32(k[0:4])
j5 := binary.LittleEndian.Uint32(k[4:8])
j6 := binary.LittleEndian.Uint32(k[8:12])
j7 := binary.LittleEndian.Uint32(k[12:16])
j8 := binary.LittleEndian.Uint32(k[16:20])
j9 := binary.LittleEndian.Uint32(k[20:24])
j10 := binary.LittleEndian.Uint32(k[24:28])
j11 := binary.LittleEndian.Uint32(k[28:32])
j12 := binary.LittleEndian.Uint32(in[0:4])
j13 := binary.LittleEndian.Uint32(in[4:8])
j14 := binary.LittleEndian.Uint32(in[8:12])
j15 := binary.LittleEndian.Uint32(in[12:16])
x0, x1, x2, x3, x4, x5, x6, x7 := j0, j1, j2, j3, j4, j5, j6, j7
x8, x9, x10, x11, x12, x13, x14, x15 := j8, j9, j10, j11, j12, j13, j14, j15
for i := 0; i < rounds; i += 2 {
x0 += x4
x12 ^= x0
x12 = (x12 << 16) | (x12 >> (16))
x8 += x12
x4 ^= x8
x4 = (x4 << 12) | (x4 >> (20))
x0 += x4
x12 ^= x0
x12 = (x12 << 8) | (x12 >> (24))
x8 += x12
x4 ^= x8
x4 = (x4 << 7) | (x4 >> (25))
x1 += x5
x13 ^= x1
x13 = (x13 << 16) | (x13 >> 16)
x9 += x13
x5 ^= x9
x5 = (x5 << 12) | (x5 >> 20)
x1 += x5
x13 ^= x1
x13 = (x13 << 8) | (x13 >> 24)
x9 += x13
x5 ^= x9
x5 = (x5 << 7) | (x5 >> 25)
x2 += x6
x14 ^= x2
x14 = (x14 << 16) | (x14 >> 16)
x10 += x14
x6 ^= x10
x6 = (x6 << 12) | (x6 >> 20)
x2 += x6
x14 ^= x2
x14 = (x14 << 8) | (x14 >> 24)
x10 += x14
x6 ^= x10
x6 = (x6 << 7) | (x6 >> 25)
x3 += x7
x15 ^= x3
x15 = (x15 << 16) | (x15 >> 16)
x11 += x15
x7 ^= x11
x7 = (x7 << 12) | (x7 >> 20)
x3 += x7
x15 ^= x3
x15 = (x15 << 8) | (x15 >> 24)
x11 += x15
x7 ^= x11
x7 = (x7 << 7) | (x7 >> 25)
x0 += x5
x15 ^= x0
x15 = (x15 << 16) | (x15 >> 16)
x10 += x15
x5 ^= x10
x5 = (x5 << 12) | (x5 >> 20)
x0 += x5
x15 ^= x0
x15 = (x15 << 8) | (x15 >> 24)
x10 += x15
x5 ^= x10
x5 = (x5 << 7) | (x5 >> 25)
x1 += x6
x12 ^= x1
x12 = (x12 << 16) | (x12 >> 16)
x11 += x12
x6 ^= x11
x6 = (x6 << 12) | (x6 >> 20)
x1 += x6
x12 ^= x1
x12 = (x12 << 8) | (x12 >> 24)
x11 += x12
x6 ^= x11
x6 = (x6 << 7) | (x6 >> 25)
x2 += x7
x13 ^= x2
x13 = (x13 << 16) | (x13 >> 16)
x8 += x13
x7 ^= x8
x7 = (x7 << 12) | (x7 >> 20)
x2 += x7
x13 ^= x2
x13 = (x13 << 8) | (x13 >> 24)
x8 += x13
x7 ^= x8
x7 = (x7 << 7) | (x7 >> 25)
x3 += x4
x14 ^= x3
x14 = (x14 << 16) | (x14 >> 16)
x9 += x14
x4 ^= x9
x4 = (x4 << 12) | (x4 >> 20)
x3 += x4
x14 ^= x3
x14 = (x14 << 8) | (x14 >> 24)
x9 += x14
x4 ^= x9
x4 = (x4 << 7) | (x4 >> 25)
}
x0 += j0
x1 += j1
x2 += j2
x3 += j3
x4 += j4
x5 += j5
x6 += j6
x7 += j7
x8 += j8
x9 += j9
x10 += j10
x11 += j11
x12 += j12
x13 += j13
x14 += j14
x15 += j15
binary.LittleEndian.PutUint32(out[0:4], x0)
binary.LittleEndian.PutUint32(out[4:8], x1)
binary.LittleEndian.PutUint32(out[8:12], x2)
binary.LittleEndian.PutUint32(out[12:16], x3)
binary.LittleEndian.PutUint32(out[16:20], x4)
binary.LittleEndian.PutUint32(out[20:24], x5)
binary.LittleEndian.PutUint32(out[24:28], x6)
binary.LittleEndian.PutUint32(out[28:32], x7)
binary.LittleEndian.PutUint32(out[32:36], x8)
binary.LittleEndian.PutUint32(out[36:40], x9)
binary.LittleEndian.PutUint32(out[40:44], x10)
binary.LittleEndian.PutUint32(out[44:48], x11)
binary.LittleEndian.PutUint32(out[48:52], x12)
binary.LittleEndian.PutUint32(out[52:56], x13)
binary.LittleEndian.PutUint32(out[56:60], x14)
binary.LittleEndian.PutUint32(out[60:64], x15)
}
// XORKeyStream crypts bytes from in to out using the given key and counters.
// In and out must overlap entirely or not at all. Counter contains the raw
// ChaCha20 counter bytes (i.e. block counter followed by nonce).
func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
var block [64]byte
var counterCopy [16]byte
copy(counterCopy[:], counter[:])
for len(in) >= 64 {
core(&block, &counterCopy, key)
for i, x := range block {
out[i] = in[i] ^ x
}
u := uint32(1)
for i := 0; i < 4; i++ {
u += uint32(counterCopy[i])
counterCopy[i] = byte(u)
u >>= 8
}
in = in[64:]
out = out[64:]
}
if len(in) > 0 {
core(&block, &counterCopy, key)
for i, v := range in {
out[i] = v ^ block[i]
}
}
}

33
vendor/golang.org/x/crypto/poly1305/poly1305.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2012 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 poly1305 implements Poly1305 one-time message authentication code as
specified in https://cr.yp.to/mac/poly1305-20050329.pdf.
Poly1305 is a fast, one-time authentication function. It is infeasible for an
attacker to generate an authenticator for a message without the key. However, a
key must only be used for a single message. Authenticating two different
messages with the same key allows an attacker to forge authenticators for other
messages with the same key.
Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
used with a fixed key in order to generate one-time keys from an nonce.
However, in this package AES isn't used and the one-time key is specified
directly.
*/
package poly1305 // import "golang.org/x/crypto/poly1305"
import "crypto/subtle"
// TagSize is the size, in bytes, of a poly1305 authenticator.
const TagSize = 16
// Verify returns true if mac is a valid authenticator for m with the given
// key.
func Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
var tmp [16]byte
Sum(&tmp, m, key)
return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
}

22
vendor/golang.org/x/crypto/poly1305/sum_amd64.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2012 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.
// +build amd64,!gccgo,!appengine
package poly1305
// This function is implemented in sum_amd64.s
//go:noescape
func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
// Sum generates an authenticator for m using a one-time key and puts the
// 16-byte result into out. Authenticating two different messages with the same
// key allows an attacker to forge messages at will.
func Sum(out *[16]byte, m []byte, key *[32]byte) {
var mPtr *byte
if len(m) > 0 {
mPtr = &m[0]
}
poly1305(out, mPtr, uint64(len(m)), key)
}

125
vendor/golang.org/x/crypto/poly1305/sum_amd64.s generated vendored Normal file
View File

@ -0,0 +1,125 @@
// Copyright 2012 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.
// +build amd64,!gccgo,!appengine
#include "textflag.h"
#define POLY1305_ADD(msg, h0, h1, h2) \
ADDQ 0(msg), h0; \
ADCQ 8(msg), h1; \
ADCQ $1, h2; \
LEAQ 16(msg), msg
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \
MOVQ r0, AX; \
MULQ h0; \
MOVQ AX, t0; \
MOVQ DX, t1; \
MOVQ r0, AX; \
MULQ h1; \
ADDQ AX, t1; \
ADCQ $0, DX; \
MOVQ r0, t2; \
IMULQ h2, t2; \
ADDQ DX, t2; \
\
MOVQ r1, AX; \
MULQ h0; \
ADDQ AX, t1; \
ADCQ $0, DX; \
MOVQ DX, h0; \
MOVQ r1, t3; \
IMULQ h2, t3; \
MOVQ r1, AX; \
MULQ h1; \
ADDQ AX, t2; \
ADCQ DX, t3; \
ADDQ h0, t2; \
ADCQ $0, t3; \
\
MOVQ t0, h0; \
MOVQ t1, h1; \
MOVQ t2, h2; \
ANDQ $3, h2; \
MOVQ t2, t0; \
ANDQ $0xFFFFFFFFFFFFFFFC, t0; \
ADDQ t0, h0; \
ADCQ t3, h1; \
ADCQ $0, h2; \
SHRQ $2, t3, t2; \
SHRQ $2, t3; \
ADDQ t2, h0; \
ADCQ t3, h1; \
ADCQ $0, h2
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
GLOBL ·poly1305Mask<>(SB), RODATA, $16
// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
TEXT ·poly1305(SB), $0-32
MOVQ out+0(FP), DI
MOVQ m+8(FP), SI
MOVQ mlen+16(FP), R15
MOVQ key+24(FP), AX
MOVQ 0(AX), R11
MOVQ 8(AX), R12
ANDQ ·poly1305Mask<>(SB), R11 // r0
ANDQ ·poly1305Mask<>+8(SB), R12 // r1
XORQ R8, R8 // h0
XORQ R9, R9 // h1
XORQ R10, R10 // h2
CMPQ R15, $16
JB bytes_between_0_and_15
loop:
POLY1305_ADD(SI, R8, R9, R10)
multiply:
POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14)
SUBQ $16, R15
CMPQ R15, $16
JAE loop
bytes_between_0_and_15:
TESTQ R15, R15
JZ done
MOVQ $1, BX
XORQ CX, CX
XORQ R13, R13
ADDQ R15, SI
flush_buffer:
SHLQ $8, BX, CX
SHLQ $8, BX
MOVB -1(SI), R13
XORQ R13, BX
DECQ SI
DECQ R15
JNZ flush_buffer
ADDQ BX, R8
ADCQ CX, R9
ADCQ $0, R10
MOVQ $16, R15
JMP multiply
done:
MOVQ R8, AX
MOVQ R9, BX
SUBQ $0xFFFFFFFFFFFFFFFB, AX
SBBQ $0xFFFFFFFFFFFFFFFF, BX
SBBQ $3, R10
CMOVQCS R8, AX
CMOVQCS R9, BX
MOVQ key+24(FP), R8
ADDQ 16(R8), AX
ADCQ 24(R8), BX
MOVQ AX, 0(DI)
MOVQ BX, 8(DI)
RET

22
vendor/golang.org/x/crypto/poly1305/sum_arm.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2015 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.
// +build arm,!gccgo,!appengine,!nacl
package poly1305
// This function is implemented in sum_arm.s
//go:noescape
func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte)
// Sum generates an authenticator for m using a one-time key and puts the
// 16-byte result into out. Authenticating two different messages with the same
// key allows an attacker to forge messages at will.
func Sum(out *[16]byte, m []byte, key *[32]byte) {
var mPtr *byte
if len(m) > 0 {
mPtr = &m[0]
}
poly1305_auth_armv6(out, mPtr, uint32(len(m)), key)
}

427
vendor/golang.org/x/crypto/poly1305/sum_arm.s generated vendored Normal file
View File

@ -0,0 +1,427 @@
// Copyright 2015 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.
// +build arm,!gccgo,!appengine,!nacl
#include "textflag.h"
// This code was translated into a form compatible with 5a from the public
// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305.
DATA ·poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
DATA ·poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
DATA ·poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
DATA ·poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
DATA ·poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
GLOBL ·poly1305_init_constants_armv6<>(SB), 8, $20
// Warning: the linker may use R11 to synthesize certain instructions. Please
// take care and verify that no synthetic instructions use it.
TEXT poly1305_init_ext_armv6<>(SB), NOSPLIT, $0
// Needs 16 bytes of stack and 64 bytes of space pointed to by R0. (It
// might look like it's only 60 bytes of space but the final four bytes
// will be written by another function.) We need to skip over four
// bytes of stack because that's saving the value of 'g'.
ADD $4, R13, R8
MOVM.IB [R4-R7], (R8)
MOVM.IA.W (R1), [R2-R5]
MOVW $·poly1305_init_constants_armv6<>(SB), R7
MOVW R2, R8
MOVW R2>>26, R9
MOVW R3>>20, g
MOVW R4>>14, R11
MOVW R5>>8, R12
ORR R3<<6, R9, R9
ORR R4<<12, g, g
ORR R5<<18, R11, R11
MOVM.IA (R7), [R2-R6]
AND R8, R2, R2
AND R9, R3, R3
AND g, R4, R4
AND R11, R5, R5
AND R12, R6, R6
MOVM.IA.W [R2-R6], (R0)
EOR R2, R2, R2
EOR R3, R3, R3
EOR R4, R4, R4
EOR R5, R5, R5
EOR R6, R6, R6
MOVM.IA.W [R2-R6], (R0)
MOVM.IA.W (R1), [R2-R5]
MOVM.IA [R2-R6], (R0)
ADD $20, R13, R0
MOVM.DA (R0), [R4-R7]
RET
#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \
MOVBU (offset+0)(Rsrc), Rtmp; \
MOVBU Rtmp, (offset+0)(Rdst); \
MOVBU (offset+1)(Rsrc), Rtmp; \
MOVBU Rtmp, (offset+1)(Rdst); \
MOVBU (offset+2)(Rsrc), Rtmp; \
MOVBU Rtmp, (offset+2)(Rdst); \
MOVBU (offset+3)(Rsrc), Rtmp; \
MOVBU Rtmp, (offset+3)(Rdst)
TEXT poly1305_blocks_armv6<>(SB), NOSPLIT, $0
// Needs 24 bytes of stack for saved registers and then 88 bytes of
// scratch space after that. We assume that 24 bytes at (R13) have
// already been used: four bytes for the link register saved in the
// prelude of poly1305_auth_armv6, four bytes for saving the value of g
// in that function and 16 bytes of scratch space used around
// poly1305_finish_ext_armv6_skip1.
ADD $24, R13, R12
MOVM.IB [R4-R8, R14], (R12)
MOVW R0, 88(R13)
MOVW R1, 92(R13)
MOVW R2, 96(R13)
MOVW R1, R14
MOVW R2, R12
MOVW 56(R0), R8
WORD $0xe1180008 // TST R8, R8 not working see issue 5921
EOR R6, R6, R6
MOVW.EQ $(1<<24), R6
MOVW R6, 84(R13)
ADD $116, R13, g
MOVM.IA (R0), [R0-R9]
MOVM.IA [R0-R4], (g)
CMP $16, R12
BLO poly1305_blocks_armv6_done
poly1305_blocks_armv6_mainloop:
WORD $0xe31e0003 // TST R14, #3 not working see issue 5921
BEQ poly1305_blocks_armv6_mainloop_aligned
ADD $100, R13, g
MOVW_UNALIGNED(R14, g, R0, 0)
MOVW_UNALIGNED(R14, g, R0, 4)
MOVW_UNALIGNED(R14, g, R0, 8)
MOVW_UNALIGNED(R14, g, R0, 12)
MOVM.IA (g), [R0-R3]
ADD $16, R14
B poly1305_blocks_armv6_mainloop_loaded
poly1305_blocks_armv6_mainloop_aligned:
MOVM.IA.W (R14), [R0-R3]
poly1305_blocks_armv6_mainloop_loaded:
MOVW R0>>26, g
MOVW R1>>20, R11
MOVW R2>>14, R12
MOVW R14, 92(R13)
MOVW R3>>8, R4
ORR R1<<6, g, g
ORR R2<<12, R11, R11
ORR R3<<18, R12, R12
BIC $0xfc000000, R0, R0
BIC $0xfc000000, g, g
MOVW 84(R13), R3
BIC $0xfc000000, R11, R11
BIC $0xfc000000, R12, R12
ADD R0, R5, R5
ADD g, R6, R6
ORR R3, R4, R4
ADD R11, R7, R7
ADD $116, R13, R14
ADD R12, R8, R8
ADD R4, R9, R9
MOVM.IA (R14), [R0-R4]
MULLU R4, R5, (R11, g)
MULLU R3, R5, (R14, R12)
MULALU R3, R6, (R11, g)
MULALU R2, R6, (R14, R12)
MULALU R2, R7, (R11, g)
MULALU R1, R7, (R14, R12)
ADD R4<<2, R4, R4
ADD R3<<2, R3, R3
MULALU R1, R8, (R11, g)
MULALU R0, R8, (R14, R12)
MULALU R0, R9, (R11, g)
MULALU R4, R9, (R14, R12)
MOVW g, 76(R13)
MOVW R11, 80(R13)
MOVW R12, 68(R13)
MOVW R14, 72(R13)
MULLU R2, R5, (R11, g)
MULLU R1, R5, (R14, R12)
MULALU R1, R6, (R11, g)
MULALU R0, R6, (R14, R12)
MULALU R0, R7, (R11, g)
MULALU R4, R7, (R14, R12)
ADD R2<<2, R2, R2
ADD R1<<2, R1, R1
MULALU R4, R8, (R11, g)
MULALU R3, R8, (R14, R12)
MULALU R3, R9, (R11, g)
MULALU R2, R9, (R14, R12)
MOVW g, 60(R13)
MOVW R11, 64(R13)
MOVW R12, 52(R13)
MOVW R14, 56(R13)
MULLU R0, R5, (R11, g)
MULALU R4, R6, (R11, g)
MULALU R3, R7, (R11, g)
MULALU R2, R8, (R11, g)
MULALU R1, R9, (R11, g)
ADD $52, R13, R0
MOVM.IA (R0), [R0-R7]
MOVW g>>26, R12
MOVW R4>>26, R14
ORR R11<<6, R12, R12
ORR R5<<6, R14, R14
BIC $0xfc000000, g, g
BIC $0xfc000000, R4, R4
ADD.S R12, R0, R0
ADC $0, R1, R1
ADD.S R14, R6, R6
ADC $0, R7, R7
MOVW R0>>26, R12
MOVW R6>>26, R14
ORR R1<<6, R12, R12
ORR R7<<6, R14, R14
BIC $0xfc000000, R0, R0
BIC $0xfc000000, R6, R6
ADD R14<<2, R14, R14
ADD.S R12, R2, R2
ADC $0, R3, R3
ADD R14, g, g
MOVW R2>>26, R12
MOVW g>>26, R14
ORR R3<<6, R12, R12
BIC $0xfc000000, g, R5
BIC $0xfc000000, R2, R7
ADD R12, R4, R4
ADD R14, R0, R0
MOVW R4>>26, R12
BIC $0xfc000000, R4, R8
ADD R12, R6, R9
MOVW 96(R13), R12
MOVW 92(R13), R14
MOVW R0, R6
CMP $32, R12
SUB $16, R12, R12
MOVW R12, 96(R13)
BHS poly1305_blocks_armv6_mainloop
poly1305_blocks_armv6_done:
MOVW 88(R13), R12
MOVW R5, 20(R12)
MOVW R6, 24(R12)
MOVW R7, 28(R12)
MOVW R8, 32(R12)
MOVW R9, 36(R12)
ADD $48, R13, R0
MOVM.DA (R0), [R4-R8, R14]
RET
#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \
MOVBU.P 1(Rsrc), Rtmp; \
MOVBU.P Rtmp, 1(Rdst); \
MOVBU.P 1(Rsrc), Rtmp; \
MOVBU.P Rtmp, 1(Rdst)
#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \
MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \
MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp)
// func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]key)
TEXT ·poly1305_auth_armv6(SB), $196-16
// The value 196, just above, is the sum of 64 (the size of the context
// structure) and 132 (the amount of stack needed).
//
// At this point, the stack pointer (R13) has been moved down. It
// points to the saved link register and there's 196 bytes of free
// space above it.
//
// The stack for this function looks like:
//
// +---------------------
// |
// | 64 bytes of context structure
// |
// +---------------------
// |
// | 112 bytes for poly1305_blocks_armv6
// |
// +---------------------
// | 16 bytes of final block, constructed at
// | poly1305_finish_ext_armv6_skip8
// +---------------------
// | four bytes of saved 'g'
// +---------------------
// | lr, saved by prelude <- R13 points here
// +---------------------
MOVW g, 4(R13)
MOVW out+0(FP), R4
MOVW m+4(FP), R5
MOVW mlen+8(FP), R6
MOVW key+12(FP), R7
ADD $136, R13, R0 // 136 = 4 + 4 + 16 + 112
MOVW R7, R1
// poly1305_init_ext_armv6 will write to the stack from R13+4, but
// that's ok because none of the other values have been written yet.
BL poly1305_init_ext_armv6<>(SB)
BIC.S $15, R6, R2
BEQ poly1305_auth_armv6_noblocks
ADD $136, R13, R0
MOVW R5, R1
ADD R2, R5, R5
SUB R2, R6, R6
BL poly1305_blocks_armv6<>(SB)
poly1305_auth_armv6_noblocks:
ADD $136, R13, R0
MOVW R5, R1
MOVW R6, R2
MOVW R4, R3
MOVW R0, R5
MOVW R1, R6
MOVW R2, R7
MOVW R3, R8
AND.S R2, R2, R2
BEQ poly1305_finish_ext_armv6_noremaining
EOR R0, R0
ADD $8, R13, R9 // 8 = offset to 16 byte scratch space
MOVW R0, (R9)
MOVW R0, 4(R9)
MOVW R0, 8(R9)
MOVW R0, 12(R9)
WORD $0xe3110003 // TST R1, #3 not working see issue 5921
BEQ poly1305_finish_ext_armv6_aligned
WORD $0xe3120008 // TST R2, #8 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip8
MOVWP_UNALIGNED(R1, R9, g)
MOVWP_UNALIGNED(R1, R9, g)
poly1305_finish_ext_armv6_skip8:
WORD $0xe3120004 // TST $4, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip4
MOVWP_UNALIGNED(R1, R9, g)
poly1305_finish_ext_armv6_skip4:
WORD $0xe3120002 // TST $2, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip2
MOVHUP_UNALIGNED(R1, R9, g)
B poly1305_finish_ext_armv6_skip2
poly1305_finish_ext_armv6_aligned:
WORD $0xe3120008 // TST R2, #8 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip8_aligned
MOVM.IA.W (R1), [g-R11]
MOVM.IA.W [g-R11], (R9)
poly1305_finish_ext_armv6_skip8_aligned:
WORD $0xe3120004 // TST $4, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip4_aligned
MOVW.P 4(R1), g
MOVW.P g, 4(R9)
poly1305_finish_ext_armv6_skip4_aligned:
WORD $0xe3120002 // TST $2, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip2
MOVHU.P 2(R1), g
MOVH.P g, 2(R9)
poly1305_finish_ext_armv6_skip2:
WORD $0xe3120001 // TST $1, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip1
MOVBU.P 1(R1), g
MOVBU.P g, 1(R9)
poly1305_finish_ext_armv6_skip1:
MOVW $1, R11
MOVBU R11, 0(R9)
MOVW R11, 56(R5)
MOVW R5, R0
ADD $8, R13, R1
MOVW $16, R2
BL poly1305_blocks_armv6<>(SB)
poly1305_finish_ext_armv6_noremaining:
MOVW 20(R5), R0
MOVW 24(R5), R1
MOVW 28(R5), R2
MOVW 32(R5), R3
MOVW 36(R5), R4
MOVW R4>>26, R12
BIC $0xfc000000, R4, R4
ADD R12<<2, R12, R12
ADD R12, R0, R0
MOVW R0>>26, R12
BIC $0xfc000000, R0, R0
ADD R12, R1, R1
MOVW R1>>26, R12
BIC $0xfc000000, R1, R1
ADD R12, R2, R2
MOVW R2>>26, R12
BIC $0xfc000000, R2, R2
ADD R12, R3, R3
MOVW R3>>26, R12
BIC $0xfc000000, R3, R3
ADD R12, R4, R4
ADD $5, R0, R6
MOVW R6>>26, R12
BIC $0xfc000000, R6, R6
ADD R12, R1, R7
MOVW R7>>26, R12
BIC $0xfc000000, R7, R7
ADD R12, R2, g
MOVW g>>26, R12
BIC $0xfc000000, g, g
ADD R12, R3, R11
MOVW $-(1<<26), R12
ADD R11>>26, R12, R12
BIC $0xfc000000, R11, R11
ADD R12, R4, R9
MOVW R9>>31, R12
SUB $1, R12
AND R12, R6, R6
AND R12, R7, R7
AND R12, g, g
AND R12, R11, R11
AND R12, R9, R9
MVN R12, R12
AND R12, R0, R0
AND R12, R1, R1
AND R12, R2, R2
AND R12, R3, R3
AND R12, R4, R4
ORR R6, R0, R0
ORR R7, R1, R1
ORR g, R2, R2
ORR R11, R3, R3
ORR R9, R4, R4
ORR R1<<26, R0, R0
MOVW R1>>6, R1
ORR R2<<20, R1, R1
MOVW R2>>12, R2
ORR R3<<14, R2, R2
MOVW R3>>18, R3
ORR R4<<8, R3, R3
MOVW 40(R5), R6
MOVW 44(R5), R7
MOVW 48(R5), g
MOVW 52(R5), R11
ADD.S R6, R0, R0
ADC.S R7, R1, R1
ADC.S g, R2, R2
ADC.S R11, R3, R3
MOVM.IA [R0-R3], (R8)
MOVW R5, R12
EOR R0, R0, R0
EOR R1, R1, R1
EOR R2, R2, R2
EOR R3, R3, R3
EOR R4, R4, R4
EOR R5, R5, R5
EOR R6, R6, R6
EOR R7, R7, R7
MOVM.IA.W [R0-R7], (R12)
MOVM.IA [R0-R7], (R12)
MOVW 4(R13), g
RET

View File

@ -57,6 +57,17 @@ type Agent interface {
Signers() ([]ssh.Signer, error) Signers() ([]ssh.Signer, error)
} }
// ConstraintExtension describes an optional constraint defined by users.
type ConstraintExtension struct {
// ExtensionName consist of a UTF-8 string suffixed by the
// implementation domain following the naming scheme defined
// in Section 4.2 of [RFC4251], e.g. "foo@example.com".
ExtensionName string
// ExtensionDetails contains the actual content of the extended
// constraint.
ExtensionDetails []byte
}
// AddedKey describes an SSH key to be added to an Agent. // AddedKey describes an SSH key to be added to an Agent.
type AddedKey struct { type AddedKey struct {
// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or // PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
@ -73,6 +84,9 @@ type AddedKey struct {
// ConfirmBeforeUse, if true, requests that the agent confirm with the // ConfirmBeforeUse, if true, requests that the agent confirm with the
// user before each use of this key. // user before each use of this key.
ConfirmBeforeUse bool ConfirmBeforeUse bool
// ConstraintExtensions are the experimental or private-use constraints
// defined by users.
ConstraintExtensions []ConstraintExtension
} }
// See [PROTOCOL.agent], section 3. // See [PROTOCOL.agent], section 3.
@ -84,7 +98,7 @@ const (
agentAddIdentity = 17 agentAddIdentity = 17
agentRemoveIdentity = 18 agentRemoveIdentity = 18
agentRemoveAllIdentities = 19 agentRemoveAllIdentities = 19
agentAddIdConstrained = 25 agentAddIDConstrained = 25
// 3.3 Key-type independent requests from client to agent // 3.3 Key-type independent requests from client to agent
agentAddSmartcardKey = 20 agentAddSmartcardKey = 20
@ -94,8 +108,9 @@ 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
) )
// maxAgentResponseBytes is the maximum agent reply size that is accepted. This // maxAgentResponseBytes is the maximum agent reply size that is accepted. This
@ -151,6 +166,19 @@ type publicKey struct {
Rest []byte `ssh:"rest"` Rest []byte `ssh:"rest"`
} }
// 3.7 Key constraint identifiers
type constrainLifetimeAgentMsg struct {
LifetimeSecs uint32 `sshtype:"1"`
}
type constrainExtensionAgentMsg struct {
ExtensionName string `sshtype:"3"`
ExtensionDetails []byte
// Rest is a field used for parsing, not part of message
Rest []byte `ssh:"rest"`
}
// Key represents a protocol 2 public key as defined in // Key represents a protocol 2 public key as defined in
// [PROTOCOL.agent], section 2.5.2. // [PROTOCOL.agent], section 2.5.2.
type Key struct { type Key struct {
@ -487,7 +515,7 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er
// if constraints are present then the message type needs to be changed. // if constraints are present then the message type needs to be changed.
if len(constraints) != 0 { if len(constraints) != 0 {
req[0] = agentAddIdConstrained req[0] = agentAddIDConstrained
} }
resp, err := c.call(req) resp, err := c.call(req)
@ -542,22 +570,18 @@ func (c *client) Add(key AddedKey) error {
var constraints []byte var constraints []byte
if secs := key.LifetimeSecs; secs != 0 { if secs := key.LifetimeSecs; secs != 0 {
constraints = append(constraints, agentConstrainLifetime) constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...)
var secsBytes [4]byte
binary.BigEndian.PutUint32(secsBytes[:], secs)
constraints = append(constraints, secsBytes[:]...)
} }
if key.ConfirmBeforeUse { if key.ConfirmBeforeUse {
constraints = append(constraints, agentConstrainConfirm) constraints = append(constraints, agentConstrainConfirm)
} }
if cert := key.Certificate; cert == nil { cert := key.Certificate
if cert == nil {
return c.insertKey(key.PrivateKey, key.Comment, constraints) return c.insertKey(key.PrivateKey, key.Comment, constraints)
} else {
return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
} }
return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
} }
func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error { func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
@ -609,7 +633,7 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string
// if constraints are present then the message type needs to be changed. // if constraints are present then the message type needs to be changed.
if len(constraints) != 0 { if len(constraints) != 0 {
req[0] = agentAddIdConstrained req[0] = agentAddIDConstrained
} }
signer, err := ssh.NewSignerFromKey(s) signer, err := ssh.NewSignerFromKey(s)

View File

@ -106,7 +106,7 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
return nil, s.agent.Lock(req.Passphrase) return nil, s.agent.Lock(req.Passphrase)
case agentUnlock: case agentUnlock:
var req agentLockMsg var req agentUnlockMsg
if err := ssh.Unmarshal(data, &req); err != nil { if err := ssh.Unmarshal(data, &req); err != nil {
return nil, err return nil, err
} }
@ -148,13 +148,51 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
} }
return rep, nil return rep, nil
case agentAddIdConstrained, agentAddIdentity: case agentAddIDConstrained, agentAddIdentity:
return nil, s.insertIdentity(data) return nil, s.insertIdentity(data)
} }
return nil, fmt.Errorf("unknown opcode %d", data[0]) return nil, fmt.Errorf("unknown opcode %d", data[0])
} }
func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse bool, extensions []ConstraintExtension, err error) {
for len(constraints) != 0 {
switch constraints[0] {
case agentConstrainLifetime:
lifetimeSecs = binary.BigEndian.Uint32(constraints[1:5])
constraints = constraints[5:]
case agentConstrainConfirm:
confirmBeforeUse = true
constraints = constraints[1:]
case agentConstrainExtension:
var msg constrainExtensionAgentMsg
if err = ssh.Unmarshal(constraints, &msg); err != nil {
return 0, false, nil, err
}
extensions = append(extensions, ConstraintExtension{
ExtensionName: msg.ExtensionName,
ExtensionDetails: msg.ExtensionDetails,
})
constraints = msg.Rest
default:
return 0, false, nil, fmt.Errorf("unknown constraint type: %d", constraints[0])
}
}
return
}
func setConstraints(key *AddedKey, constraintBytes []byte) error {
lifetimeSecs, confirmBeforeUse, constraintExtensions, err := parseConstraints(constraintBytes)
if err != nil {
return err
}
key.LifetimeSecs = lifetimeSecs
key.ConfirmBeforeUse = confirmBeforeUse
key.ConstraintExtensions = constraintExtensions
return nil
}
func parseRSAKey(req []byte) (*AddedKey, error) { func parseRSAKey(req []byte) (*AddedKey, error) {
var k rsaKeyMsg var k rsaKeyMsg
if err := ssh.Unmarshal(req, &k); err != nil { if err := ssh.Unmarshal(req, &k); err != nil {
@ -173,7 +211,11 @@ func parseRSAKey(req []byte) (*AddedKey, error) {
} }
priv.Precompute() priv.Precompute()
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
if err := setConstraints(addedKey, k.Constraints); err != nil {
return nil, err
}
return addedKey, nil
} }
func parseEd25519Key(req []byte) (*AddedKey, error) { func parseEd25519Key(req []byte) (*AddedKey, error) {
@ -182,7 +224,12 @@ func parseEd25519Key(req []byte) (*AddedKey, error) {
return nil, err return nil, err
} }
priv := ed25519.PrivateKey(k.Priv) priv := ed25519.PrivateKey(k.Priv)
return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil
addedKey := &AddedKey{PrivateKey: &priv, Comment: k.Comments}
if err := setConstraints(addedKey, k.Constraints); err != nil {
return nil, err
}
return addedKey, nil
} }
func parseDSAKey(req []byte) (*AddedKey, error) { func parseDSAKey(req []byte) (*AddedKey, error) {
@ -202,7 +249,11 @@ func parseDSAKey(req []byte) (*AddedKey, error) {
X: k.X, X: k.X,
} }
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
if err := setConstraints(addedKey, k.Constraints); err != nil {
return nil, err
}
return addedKey, nil
} }
func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) { func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) {
@ -243,7 +294,12 @@ func parseEd25519Cert(req []byte) (*AddedKey, error) {
if !ok { if !ok {
return nil, errors.New("agent: bad ED25519 certificate") return nil, errors.New("agent: bad ED25519 certificate")
} }
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}
if err := setConstraints(addedKey, k.Constraints); err != nil {
return nil, err
}
return addedKey, nil
} }
func parseECDSAKey(req []byte) (*AddedKey, error) { func parseECDSAKey(req []byte) (*AddedKey, error) {
@ -257,7 +313,11 @@ func parseECDSAKey(req []byte) (*AddedKey, error) {
return nil, err return nil, err
} }
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
if err := setConstraints(addedKey, k.Constraints); err != nil {
return nil, err
}
return addedKey, nil
} }
func parseRSACert(req []byte) (*AddedKey, error) { func parseRSACert(req []byte) (*AddedKey, error) {
@ -300,7 +360,11 @@ func parseRSACert(req []byte) (*AddedKey, error) {
} }
priv.Precompute() priv.Precompute()
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}
if err := setConstraints(addedKey, k.Constraints); err != nil {
return nil, err
}
return addedKey, nil
} }
func parseDSACert(req []byte) (*AddedKey, error) { func parseDSACert(req []byte) (*AddedKey, error) {
@ -338,7 +402,11 @@ func parseDSACert(req []byte) (*AddedKey, error) {
X: k.X, X: k.X,
} }
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}
if err := setConstraints(addedKey, k.Constraints); err != nil {
return nil, err
}
return addedKey, nil
} }
func parseECDSACert(req []byte) (*AddedKey, error) { func parseECDSACert(req []byte) (*AddedKey, error) {
@ -371,7 +439,11 @@ func parseECDSACert(req []byte) (*AddedKey, error) {
return nil, err return nil, err
} }
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}
if err := setConstraints(addedKey, k.Constraints); err != nil {
return nil, err
}
return addedKey, nil
} }
func (s *server) insertIdentity(req []byte) error { func (s *server) insertIdentity(req []byte) error {

View File

@ -51,13 +51,12 @@ func (b *buffer) write(buf []byte) {
} }
// eof closes the buffer. Reads from the buffer once all // eof closes the buffer. Reads from the buffer once all
// the data has been consumed will receive os.EOF. // the data has been consumed will receive io.EOF.
func (b *buffer) eof() error { func (b *buffer) eof() {
b.Cond.L.Lock() b.Cond.L.Lock()
b.closed = true b.closed = true
b.Cond.Signal() b.Cond.Signal()
b.Cond.L.Unlock() b.Cond.L.Unlock()
return nil
} }
// Read reads data from the internal buffer in buf. Reads will block // Read reads data from the internal buffer in buf. Reads will block

View File

@ -44,7 +44,9 @@ type Signature struct {
const CertTimeInfinity = 1<<64 - 1 const CertTimeInfinity = 1<<64 - 1
// An Certificate represents an OpenSSH certificate as defined in // An Certificate represents an OpenSSH certificate as defined in
// [PROTOCOL.certkeys]?rev=1.8. // [PROTOCOL.certkeys]?rev=1.8. The Certificate type implements the
// PublicKey interface, so it can be unmarshaled using
// ParsePublicKey.
type Certificate struct { type Certificate struct {
Nonce []byte Nonce []byte
Key PublicKey Key PublicKey
@ -298,8 +300,17 @@ func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey)
if cert.CertType != HostCert { if cert.CertType != HostCert {
return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType)
} }
if !c.IsHostAuthority(cert.SignatureKey, addr) {
return fmt.Errorf("ssh: no authorities for hostname: %v", addr)
}
return c.CheckCert(addr, cert) hostname, _, err := net.SplitHostPort(addr)
if err != nil {
return err
}
// Pass hostname only as principal for host certificates (consistent with OpenSSH)
return c.CheckCert(hostname, cert)
} }
// Authenticate checks a user certificate. Authenticate can be used as // Authenticate checks a user certificate. Authenticate can be used as
@ -316,6 +327,9 @@ func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permis
if cert.CertType != UserCert { if cert.CertType != UserCert {
return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType)
} }
if !c.IsUserAuthority(cert.SignatureKey) {
return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority")
}
if err := c.CheckCert(conn.User(), cert); err != nil { if err := c.CheckCert(conn.User(), cert); err != nil {
return nil, err return nil, err
@ -328,10 +342,10 @@ func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permis
// the signature of the certificate. // the signature of the certificate.
func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
if c.IsRevoked != nil && c.IsRevoked(cert) { if c.IsRevoked != nil && c.IsRevoked(cert) {
return fmt.Errorf("ssh: certicate serial %d revoked", cert.Serial) return fmt.Errorf("ssh: certificate serial %d revoked", cert.Serial)
} }
for opt, _ := range cert.CriticalOptions { for opt := range cert.CriticalOptions {
// sourceAddressCriticalOption will be enforced by // sourceAddressCriticalOption will be enforced by
// serverAuthenticate // serverAuthenticate
if opt == sourceAddressCriticalOption { if opt == sourceAddressCriticalOption {
@ -364,16 +378,6 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
} }
} }
// if this is a host cert, principal is the remote hostname as passed
// to CheckHostCert.
if cert.CertType == HostCert && !c.IsHostAuthority(cert.SignatureKey, principal) {
return fmt.Errorf("ssh: no authorities for hostname: %v", principal)
}
if cert.CertType == UserCert && !c.IsUserAuthority(cert.SignatureKey) {
return fmt.Errorf("ssh: certificate signed by unrecognized authority")
}
clock := c.Clock clock := c.Clock
if clock == nil { if clock == nil {
clock = time.Now clock = time.Now

View File

@ -205,32 +205,32 @@ type channel struct {
// writePacket sends a packet. If the packet is a channel close, it updates // writePacket sends a packet. If the packet is a channel close, it updates
// sentClose. This method takes the lock c.writeMu. // sentClose. This method takes the lock c.writeMu.
func (c *channel) writePacket(packet []byte) error { func (ch *channel) writePacket(packet []byte) error {
c.writeMu.Lock() ch.writeMu.Lock()
if c.sentClose { if ch.sentClose {
c.writeMu.Unlock() ch.writeMu.Unlock()
return io.EOF return io.EOF
} }
c.sentClose = (packet[0] == msgChannelClose) ch.sentClose = (packet[0] == msgChannelClose)
err := c.mux.conn.writePacket(packet) err := ch.mux.conn.writePacket(packet)
c.writeMu.Unlock() ch.writeMu.Unlock()
return err return err
} }
func (c *channel) sendMessage(msg interface{}) error { func (ch *channel) sendMessage(msg interface{}) error {
if debugMux { if debugMux {
log.Printf("send(%d): %#v", c.mux.chanList.offset, msg) log.Printf("send(%d): %#v", ch.mux.chanList.offset, msg)
} }
p := Marshal(msg) p := Marshal(msg)
binary.BigEndian.PutUint32(p[1:], c.remoteId) binary.BigEndian.PutUint32(p[1:], ch.remoteId)
return c.writePacket(p) return ch.writePacket(p)
} }
// WriteExtended writes data to a specific extended stream. These streams are // WriteExtended writes data to a specific extended stream. These streams are
// used, for example, for stderr. // used, for example, for stderr.
func (c *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) { func (ch *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err error) {
if c.sentEOF { if ch.sentEOF {
return 0, io.EOF return 0, io.EOF
} }
// 1 byte message type, 4 bytes remoteId, 4 bytes data length // 1 byte message type, 4 bytes remoteId, 4 bytes data length
@ -241,16 +241,16 @@ func (c *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err er
opCode = msgChannelExtendedData opCode = msgChannelExtendedData
} }
c.writeMu.Lock() ch.writeMu.Lock()
packet := c.packetPool[extendedCode] packet := ch.packetPool[extendedCode]
// We don't remove the buffer from packetPool, so // We don't remove the buffer from packetPool, so
// WriteExtended calls from different goroutines will be // WriteExtended calls from different goroutines will be
// flagged as errors by the race detector. // flagged as errors by the race detector.
c.writeMu.Unlock() ch.writeMu.Unlock()
for len(data) > 0 { for len(data) > 0 {
space := min(c.maxRemotePayload, len(data)) space := min(ch.maxRemotePayload, len(data))
if space, err = c.remoteWin.reserve(space); err != nil { if space, err = ch.remoteWin.reserve(space); err != nil {
return n, err return n, err
} }
if want := headerLength + space; uint32(cap(packet)) < want { if want := headerLength + space; uint32(cap(packet)) < want {
@ -262,13 +262,13 @@ func (c *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err er
todo := data[:space] todo := data[:space]
packet[0] = opCode packet[0] = opCode
binary.BigEndian.PutUint32(packet[1:], c.remoteId) binary.BigEndian.PutUint32(packet[1:], ch.remoteId)
if extendedCode > 0 { if extendedCode > 0 {
binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode)) binary.BigEndian.PutUint32(packet[5:], uint32(extendedCode))
} }
binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo))) binary.BigEndian.PutUint32(packet[headerLength-4:], uint32(len(todo)))
copy(packet[headerLength:], todo) copy(packet[headerLength:], todo)
if err = c.writePacket(packet); err != nil { if err = ch.writePacket(packet); err != nil {
return n, err return n, err
} }
@ -276,14 +276,14 @@ func (c *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err er
data = data[len(todo):] data = data[len(todo):]
} }
c.writeMu.Lock() ch.writeMu.Lock()
c.packetPool[extendedCode] = packet ch.packetPool[extendedCode] = packet
c.writeMu.Unlock() ch.writeMu.Unlock()
return n, err return n, err
} }
func (c *channel) handleData(packet []byte) error { func (ch *channel) handleData(packet []byte) error {
headerLen := 9 headerLen := 9
isExtendedData := packet[0] == msgChannelExtendedData isExtendedData := packet[0] == msgChannelExtendedData
if isExtendedData { if isExtendedData {
@ -303,7 +303,7 @@ func (c *channel) handleData(packet []byte) error {
if length == 0 { if length == 0 {
return nil return nil
} }
if length > c.maxIncomingPayload { if length > ch.maxIncomingPayload {
// TODO(hanwen): should send Disconnect? // TODO(hanwen): should send Disconnect?
return errors.New("ssh: incoming packet exceeds maximum payload size") return errors.New("ssh: incoming packet exceeds maximum payload size")
} }
@ -313,21 +313,21 @@ func (c *channel) handleData(packet []byte) error {
return errors.New("ssh: wrong packet length") return errors.New("ssh: wrong packet length")
} }
c.windowMu.Lock() ch.windowMu.Lock()
if c.myWindow < length { if ch.myWindow < length {
c.windowMu.Unlock() ch.windowMu.Unlock()
// TODO(hanwen): should send Disconnect with reason? // TODO(hanwen): should send Disconnect with reason?
return errors.New("ssh: remote side wrote too much") return errors.New("ssh: remote side wrote too much")
} }
c.myWindow -= length ch.myWindow -= length
c.windowMu.Unlock() ch.windowMu.Unlock()
if extended == 1 { if extended == 1 {
c.extPending.write(data) ch.extPending.write(data)
} else if extended > 0 { } else if extended > 0 {
// discard other extended data. // discard other extended data.
} else { } else {
c.pending.write(data) ch.pending.write(data)
} }
return nil return nil
} }
@ -384,31 +384,31 @@ func (c *channel) close() {
// responseMessageReceived is called when a success or failure message is // responseMessageReceived is called when a success or failure message is
// received on a channel to check that such a message is reasonable for the // received on a channel to check that such a message is reasonable for the
// given channel. // given channel.
func (c *channel) responseMessageReceived() error { func (ch *channel) responseMessageReceived() error {
if c.direction == channelInbound { if ch.direction == channelInbound {
return errors.New("ssh: channel response message received on inbound channel") return errors.New("ssh: channel response message received on inbound channel")
} }
if c.decided { if ch.decided {
return errors.New("ssh: duplicate response received for channel") return errors.New("ssh: duplicate response received for channel")
} }
c.decided = true ch.decided = true
return nil return nil
} }
func (c *channel) handlePacket(packet []byte) error { func (ch *channel) handlePacket(packet []byte) error {
switch packet[0] { switch packet[0] {
case msgChannelData, msgChannelExtendedData: case msgChannelData, msgChannelExtendedData:
return c.handleData(packet) return ch.handleData(packet)
case msgChannelClose: case msgChannelClose:
c.sendMessage(channelCloseMsg{PeersId: c.remoteId}) ch.sendMessage(channelCloseMsg{PeersID: ch.remoteId})
c.mux.chanList.remove(c.localId) ch.mux.chanList.remove(ch.localId)
c.close() ch.close()
return nil return nil
case msgChannelEOF: case msgChannelEOF:
// RFC 4254 is mute on how EOF affects dataExt messages but // RFC 4254 is mute on how EOF affects dataExt messages but
// it is logical to signal EOF at the same time. // it is logical to signal EOF at the same time.
c.extPending.eof() ch.extPending.eof()
c.pending.eof() ch.pending.eof()
return nil return nil
} }
@ -419,24 +419,24 @@ func (c *channel) handlePacket(packet []byte) error {
switch msg := decoded.(type) { switch msg := decoded.(type) {
case *channelOpenFailureMsg: case *channelOpenFailureMsg:
if err := c.responseMessageReceived(); err != nil { if err := ch.responseMessageReceived(); err != nil {
return err return err
} }
c.mux.chanList.remove(msg.PeersId) ch.mux.chanList.remove(msg.PeersID)
c.msg <- msg ch.msg <- msg
case *channelOpenConfirmMsg: case *channelOpenConfirmMsg:
if err := c.responseMessageReceived(); err != nil { if err := ch.responseMessageReceived(); err != nil {
return err return err
} }
if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize) return fmt.Errorf("ssh: invalid MaxPacketSize %d from peer", msg.MaxPacketSize)
} }
c.remoteId = msg.MyId ch.remoteId = msg.MyID
c.maxRemotePayload = msg.MaxPacketSize ch.maxRemotePayload = msg.MaxPacketSize
c.remoteWin.add(msg.MyWindow) ch.remoteWin.add(msg.MyWindow)
c.msg <- msg ch.msg <- msg
case *windowAdjustMsg: case *windowAdjustMsg:
if !c.remoteWin.add(msg.AdditionalBytes) { if !ch.remoteWin.add(msg.AdditionalBytes) {
return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes) return fmt.Errorf("ssh: invalid window update for %d bytes", msg.AdditionalBytes)
} }
case *channelRequestMsg: case *channelRequestMsg:
@ -444,12 +444,12 @@ func (c *channel) handlePacket(packet []byte) error {
Type: msg.Request, Type: msg.Request,
WantReply: msg.WantReply, WantReply: msg.WantReply,
Payload: msg.RequestSpecificData, Payload: msg.RequestSpecificData,
ch: c, ch: ch,
} }
c.incomingRequests <- &req ch.incomingRequests <- &req
default: default:
c.msg <- msg ch.msg <- msg
} }
return nil return nil
} }
@ -488,23 +488,23 @@ func (e *extChannel) Read(data []byte) (n int, err error) {
return e.ch.ReadExtended(data, e.code) return e.ch.ReadExtended(data, e.code)
} }
func (c *channel) Accept() (Channel, <-chan *Request, error) { func (ch *channel) Accept() (Channel, <-chan *Request, error) {
if c.decided { if ch.decided {
return nil, nil, errDecidedAlready return nil, nil, errDecidedAlready
} }
c.maxIncomingPayload = channelMaxPacket ch.maxIncomingPayload = channelMaxPacket
confirm := channelOpenConfirmMsg{ confirm := channelOpenConfirmMsg{
PeersId: c.remoteId, PeersID: ch.remoteId,
MyId: c.localId, MyID: ch.localId,
MyWindow: c.myWindow, MyWindow: ch.myWindow,
MaxPacketSize: c.maxIncomingPayload, MaxPacketSize: ch.maxIncomingPayload,
} }
c.decided = true ch.decided = true
if err := c.sendMessage(confirm); err != nil { if err := ch.sendMessage(confirm); err != nil {
return nil, nil, err return nil, nil, err
} }
return c, c.incomingRequests, nil return ch, ch.incomingRequests, nil
} }
func (ch *channel) Reject(reason RejectionReason, message string) error { func (ch *channel) Reject(reason RejectionReason, message string) error {
@ -512,7 +512,7 @@ func (ch *channel) Reject(reason RejectionReason, message string) error {
return errDecidedAlready return errDecidedAlready
} }
reject := channelOpenFailureMsg{ reject := channelOpenFailureMsg{
PeersId: ch.remoteId, PeersID: ch.remoteId,
Reason: reason, Reason: reason,
Message: message, Message: message,
Language: "en", Language: "en",
@ -541,7 +541,7 @@ func (ch *channel) CloseWrite() error {
} }
ch.sentEOF = true ch.sentEOF = true
return ch.sendMessage(channelEOFMsg{ return ch.sendMessage(channelEOFMsg{
PeersId: ch.remoteId}) PeersID: ch.remoteId})
} }
func (ch *channel) Close() error { func (ch *channel) Close() error {
@ -550,7 +550,7 @@ func (ch *channel) Close() error {
} }
return ch.sendMessage(channelCloseMsg{ return ch.sendMessage(channelCloseMsg{
PeersId: ch.remoteId}) PeersID: ch.remoteId})
} }
// Extended returns an io.ReadWriter that sends and receives data on the given, // Extended returns an io.ReadWriter that sends and receives data on the given,
@ -577,7 +577,7 @@ func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (boo
} }
msg := channelRequestMsg{ msg := channelRequestMsg{
PeersId: ch.remoteId, PeersID: ch.remoteId,
Request: name, Request: name,
WantReply: wantReply, WantReply: wantReply,
RequestSpecificData: payload, RequestSpecificData: payload,
@ -614,11 +614,11 @@ func (ch *channel) ackRequest(ok bool) error {
var msg interface{} var msg interface{}
if !ok { if !ok {
msg = channelRequestFailureMsg{ msg = channelRequestFailureMsg{
PeersId: ch.remoteId, PeersID: ch.remoteId,
} }
} else { } else {
msg = channelRequestSuccessMsg{ msg = channelRequestSuccessMsg{
PeersId: ch.remoteId, PeersID: ch.remoteId,
} }
} }
return ch.sendMessage(msg) return ch.sendMessage(msg)

View File

@ -16,6 +16,9 @@ import (
"hash" "hash"
"io" "io"
"io/ioutil" "io/ioutil"
"golang.org/x/crypto/internal/chacha20"
"golang.org/x/crypto/poly1305"
) )
const ( const (
@ -53,78 +56,78 @@ func newRC4(key, iv []byte) (cipher.Stream, error) {
return rc4.NewCipher(key) return rc4.NewCipher(key)
} }
type streamCipherMode struct { type cipherMode struct {
keySize int keySize int
ivSize int ivSize int
skip int create func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error)
createFunc func(key, iv []byte) (cipher.Stream, error)
} }
func (c *streamCipherMode) createStream(key, iv []byte) (cipher.Stream, error) { func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
if len(key) < c.keySize { return func(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
panic("ssh: key length too small for cipher") stream, err := createFunc(key, iv)
} if err != nil {
if len(iv) < c.ivSize { return nil, err
panic("ssh: iv too small for cipher")
}
stream, err := c.createFunc(key[:c.keySize], iv[:c.ivSize])
if err != nil {
return nil, err
}
var streamDump []byte
if c.skip > 0 {
streamDump = make([]byte, 512)
}
for remainingToDump := c.skip; remainingToDump > 0; {
dumpThisTime := remainingToDump
if dumpThisTime > len(streamDump) {
dumpThisTime = len(streamDump)
} }
stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
remainingToDump -= dumpThisTime
}
return stream, nil var streamDump []byte
if skip > 0 {
streamDump = make([]byte, 512)
}
for remainingToDump := skip; remainingToDump > 0; {
dumpThisTime := remainingToDump
if dumpThisTime > len(streamDump) {
dumpThisTime = len(streamDump)
}
stream.XORKeyStream(streamDump[:dumpThisTime], streamDump[:dumpThisTime])
remainingToDump -= dumpThisTime
}
mac := macModes[algs.MAC].new(macKey)
return &streamPacketCipher{
mac: mac,
etm: macModes[algs.MAC].etm,
macResult: make([]byte, mac.Size()),
cipher: stream,
}, nil
}
} }
// cipherModes documents properties of supported ciphers. Ciphers not included // cipherModes documents properties of supported ciphers. Ciphers not included
// are not supported and will not be negotiated, even if explicitly requested in // are not supported and will not be negotiated, even if explicitly requested in
// ClientConfig.Crypto.Ciphers. // ClientConfig.Crypto.Ciphers.
var cipherModes = map[string]*streamCipherMode{ var cipherModes = map[string]*cipherMode{
// Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms // Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms
// are defined in the order specified in the RFC. // are defined in the order specified in the RFC.
"aes128-ctr": {16, aes.BlockSize, 0, newAESCTR}, "aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
"aes192-ctr": {24, aes.BlockSize, 0, newAESCTR}, "aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
"aes256-ctr": {32, aes.BlockSize, 0, newAESCTR}, "aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
// Ciphers from RFC4345, which introduces security-improved arcfour ciphers. // Ciphers from RFC4345, which introduces security-improved arcfour ciphers.
// They are defined in the order specified in the RFC. // They are defined in the order specified in the RFC.
"arcfour128": {16, 0, 1536, newRC4}, "arcfour128": {16, 0, streamCipherMode(1536, newRC4)},
"arcfour256": {32, 0, 1536, newRC4}, "arcfour256": {32, 0, streamCipherMode(1536, newRC4)},
// Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol. // Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol.
// Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and // Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and
// RC4) has problems with weak keys, and should be used with caution." // RC4) has problems with weak keys, and should be used with caution."
// RFC4345 introduces improved versions of Arcfour. // RFC4345 introduces improved versions of Arcfour.
"arcfour": {16, 0, 0, newRC4}, "arcfour": {16, 0, streamCipherMode(0, newRC4)},
// AES-GCM is not a stream cipher, so it is constructed with a // AEAD ciphers
// special case. If we add any more non-stream ciphers, we gcmCipherID: {16, 12, newGCMCipher},
// should invest a cleaner way to do this. chacha20Poly1305ID: {64, 0, newChaCha20Cipher},
gcmCipherID: {16, 12, 0, nil},
// CBC mode is insecure and so is not included in the default config. // CBC mode is insecure and so is not included in the default config.
// (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely // (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely
// needed, it's possible to specify a custom Config to enable it. // needed, it's possible to specify a custom Config to enable it.
// You should expect that an active attacker can recover plaintext if // You should expect that an active attacker can recover plaintext if
// you do. // you do.
aes128cbcID: {16, aes.BlockSize, 0, nil}, aes128cbcID: {16, aes.BlockSize, newAESCBCCipher},
// 3des-cbc is insecure and is disabled by default. // 3des-cbc is insecure and is not included in the default
tripledescbcID: {24, des.BlockSize, 0, nil}, // config.
tripledescbcID: {24, des.BlockSize, newTripleDESCBCCipher},
} }
// prefixLen is the length of the packet prefix that contains the packet length // prefixLen is the length of the packet prefix that contains the packet length
@ -304,7 +307,7 @@ type gcmCipher struct {
buf []byte buf []byte
} }
func newGCMCipher(iv, key, macKey []byte) (packetCipher, error) { func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
c, err := aes.NewCipher(key) c, err := aes.NewCipher(key)
if err != nil { if err != nil {
return nil, err return nil, err
@ -372,7 +375,7 @@ func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
} }
length := binary.BigEndian.Uint32(c.prefix[:]) length := binary.BigEndian.Uint32(c.prefix[:])
if length > maxPacket { if length > maxPacket {
return nil, errors.New("ssh: max packet length exceeded.") return nil, errors.New("ssh: max packet length exceeded")
} }
if cap(c.buf) < int(length+gcmTagSize) { if cap(c.buf) < int(length+gcmTagSize) {
@ -392,7 +395,9 @@ func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
c.incIV() c.incIV()
padding := plain[0] padding := plain[0]
if padding < 4 || padding >= 20 { if padding < 4 {
// padding is a byte, so it automatically satisfies
// the maximum size, which is 255.
return nil, fmt.Errorf("ssh: illegal padding %d", padding) return nil, fmt.Errorf("ssh: illegal padding %d", padding)
} }
@ -420,7 +425,7 @@ type cbcCipher struct {
oracleCamouflage uint32 oracleCamouflage uint32
} }
func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
cbc := &cbcCipher{ cbc := &cbcCipher{
mac: macModes[algs.MAC].new(macKey), mac: macModes[algs.MAC].new(macKey),
decrypter: cipher.NewCBCDecrypter(c, iv), decrypter: cipher.NewCBCDecrypter(c, iv),
@ -434,13 +439,13 @@ func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorith
return cbc, nil return cbc, nil
} }
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
c, err := aes.NewCipher(key) c, err := aes.NewCipher(key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cbc, err := newCBCCipher(c, iv, key, macKey, algs) cbc, err := newCBCCipher(c, key, iv, macKey, algs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -448,13 +453,13 @@ func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCi
return cbc, nil return cbc, nil
} }
func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { func newTripleDESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
c, err := des.NewTripleDESCipher(key) c, err := des.NewTripleDESCipher(key)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cbc, err := newCBCCipher(c, iv, key, macKey, algs) cbc, err := newCBCCipher(c, key, iv, macKey, algs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -546,11 +551,11 @@ func (c *cbcCipher) readPacketLeaky(seqNum uint32, r io.Reader) ([]byte, error)
c.packetData = c.packetData[:entirePacketSize] c.packetData = c.packetData[:entirePacketSize]
} }
if n, err := io.ReadFull(r, c.packetData[firstBlockLength:]); err != nil { n, err := io.ReadFull(r, c.packetData[firstBlockLength:])
if err != nil {
return nil, err return nil, err
} else {
c.oracleCamouflage -= uint32(n)
} }
c.oracleCamouflage -= uint32(n)
remainingCrypted := c.packetData[firstBlockLength:macStart] remainingCrypted := c.packetData[firstBlockLength:macStart]
c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted) c.decrypter.CryptBlocks(remainingCrypted, remainingCrypted)
@ -625,3 +630,142 @@ func (c *cbcCipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, pack
return nil return nil
} }
const chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
// chacha20Poly1305Cipher implements the chacha20-poly1305@openssh.com
// AEAD, which is described here:
//
// https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00
//
// the methods here also implement padding, which RFC4253 Section 6
// also requires of stream ciphers.
type chacha20Poly1305Cipher struct {
lengthKey [32]byte
contentKey [32]byte
buf []byte
}
func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
if len(key) != 64 {
panic(len(key))
}
c := &chacha20Poly1305Cipher{
buf: make([]byte, 256),
}
copy(c.contentKey[:], key[:32])
copy(c.lengthKey[:], key[32:])
return c, nil
}
// The Poly1305 key is obtained by encrypting 32 0-bytes.
var chacha20PolyKeyInput [32]byte
func (c *chacha20Poly1305Cipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
var counter [16]byte
binary.BigEndian.PutUint64(counter[8:], uint64(seqNum))
var polyKey [32]byte
chacha20.XORKeyStream(polyKey[:], chacha20PolyKeyInput[:], &counter, &c.contentKey)
encryptedLength := c.buf[:4]
if _, err := io.ReadFull(r, encryptedLength); err != nil {
return nil, err
}
var lenBytes [4]byte
chacha20.XORKeyStream(lenBytes[:], encryptedLength, &counter, &c.lengthKey)
length := binary.BigEndian.Uint32(lenBytes[:])
if length > maxPacket {
return nil, errors.New("ssh: invalid packet length, packet too large")
}
contentEnd := 4 + length
packetEnd := contentEnd + poly1305.TagSize
if uint32(cap(c.buf)) < packetEnd {
c.buf = make([]byte, packetEnd)
copy(c.buf[:], encryptedLength)
} else {
c.buf = c.buf[:packetEnd]
}
if _, err := io.ReadFull(r, c.buf[4:packetEnd]); err != nil {
return nil, err
}
var mac [poly1305.TagSize]byte
copy(mac[:], c.buf[contentEnd:packetEnd])
if !poly1305.Verify(&mac, c.buf[:contentEnd], &polyKey) {
return nil, errors.New("ssh: MAC failure")
}
counter[0] = 1
plain := c.buf[4:contentEnd]
chacha20.XORKeyStream(plain, plain, &counter, &c.contentKey)
padding := plain[0]
if padding < 4 {
// padding is a byte, so it automatically satisfies
// the maximum size, which is 255.
return nil, fmt.Errorf("ssh: illegal padding %d", padding)
}
if int(padding)+1 >= len(plain) {
return nil, fmt.Errorf("ssh: padding %d too large", padding)
}
plain = plain[1 : len(plain)-int(padding)]
return plain, nil
}
func (c *chacha20Poly1305Cipher) writePacket(seqNum uint32, w io.Writer, rand io.Reader, payload []byte) error {
var counter [16]byte
binary.BigEndian.PutUint64(counter[8:], uint64(seqNum))
var polyKey [32]byte
chacha20.XORKeyStream(polyKey[:], chacha20PolyKeyInput[:], &counter, &c.contentKey)
// There is no blocksize, so fall back to multiple of 8 byte
// padding, as described in RFC 4253, Sec 6.
const packetSizeMultiple = 8
padding := packetSizeMultiple - (1+len(payload))%packetSizeMultiple
if padding < 4 {
padding += packetSizeMultiple
}
// size (4 bytes), padding (1), payload, padding, tag.
totalLength := 4 + 1 + len(payload) + padding + poly1305.TagSize
if cap(c.buf) < totalLength {
c.buf = make([]byte, totalLength)
} else {
c.buf = c.buf[:totalLength]
}
binary.BigEndian.PutUint32(c.buf, uint32(1+len(payload)+padding))
chacha20.XORKeyStream(c.buf, c.buf[:4], &counter, &c.lengthKey)
c.buf[4] = byte(padding)
copy(c.buf[5:], payload)
packetEnd := 5 + len(payload) + padding
if _, err := io.ReadFull(rand, c.buf[5+len(payload):packetEnd]); err != nil {
return err
}
counter[0] = 1
chacha20.XORKeyStream(c.buf[4:], c.buf[4:packetEnd], &counter, &c.contentKey)
var mac [poly1305.TagSize]byte
poly1305.Sum(&mac, c.buf[:packetEnd], &polyKey)
copy(c.buf[packetEnd:], mac[:])
if _, err := w.Write(c.buf); err != nil {
return err
}
return nil
}

View File

@ -9,6 +9,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"os"
"sync" "sync"
"time" "time"
) )
@ -187,6 +188,10 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) {
// net.Conn underlying the the SSH connection. // net.Conn underlying the the SSH connection.
type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
// BannerCallback is the function type used for treat the banner sent by
// the server. A BannerCallback receives the message sent by the remote server.
type BannerCallback func(message string) error
// A ClientConfig structure is used to configure a Client. It must not be // A ClientConfig structure is used to configure a Client. It must not be
// modified after having been passed to an SSH function. // modified after having been passed to an SSH function.
type ClientConfig struct { type ClientConfig struct {
@ -209,6 +214,12 @@ type ClientConfig struct {
// FixedHostKey can be used for simplistic host key checks. // FixedHostKey can be used for simplistic host key checks.
HostKeyCallback HostKeyCallback HostKeyCallback HostKeyCallback
// BannerCallback is called during the SSH dance to display a custom
// server's message. The client configuration can supply this callback to
// handle it as wished. The function BannerDisplayStderr can be used for
// simplistic display on Stderr.
BannerCallback BannerCallback
// ClientVersion contains the version identification string that will // ClientVersion contains the version identification string that will
// be used for the connection. If empty, a reasonable default is used. // be used for the connection. If empty, a reasonable default is used.
ClientVersion string ClientVersion string
@ -255,3 +266,13 @@ func FixedHostKey(key PublicKey) HostKeyCallback {
hk := &fixedHostKey{key} hk := &fixedHostKey{key}
return hk.check return hk.check
} }
// BannerDisplayStderr returns a function that can be used for
// ClientConfig.BannerCallback to display banners on os.Stderr.
func BannerDisplayStderr() BannerCallback {
return func(banner string) error {
_, err := os.Stderr.WriteString(banner)
return err
}
}

View File

@ -11,6 +11,14 @@ import (
"io" "io"
) )
type authResult int
const (
authFailure authResult = iota
authPartialSuccess
authSuccess
)
// clientAuthenticate authenticates with the remote server. See RFC 4252. // clientAuthenticate authenticates with the remote server. See RFC 4252.
func (c *connection) clientAuthenticate(config *ClientConfig) error { func (c *connection) clientAuthenticate(config *ClientConfig) error {
// initiate user auth session // initiate user auth session
@ -37,11 +45,12 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
if err != nil { if err != nil {
return err return err
} }
if ok { if ok == authSuccess {
// success // success
return nil return nil
} else if ok == authFailure {
tried[auth.method()] = true
} }
tried[auth.method()] = true
if methods == nil { if methods == nil {
methods = lastMethods methods = lastMethods
} }
@ -82,7 +91,7 @@ type AuthMethod interface {
// If authentication is not successful, a []string of alternative // If authentication is not successful, a []string of alternative
// method names is returned. If the slice is nil, it will be ignored // method names is returned. If the slice is nil, it will be ignored
// and the previous set of possible methods will be reused. // and the previous set of possible methods will be reused.
auth(session []byte, user string, p packetConn, rand io.Reader) (bool, []string, error) auth(session []byte, user string, p packetConn, rand io.Reader) (authResult, []string, error)
// method returns the RFC 4252 method name. // method returns the RFC 4252 method name.
method() string method() string
@ -91,13 +100,13 @@ type AuthMethod interface {
// "none" authentication, RFC 4252 section 5.2. // "none" authentication, RFC 4252 section 5.2.
type noneAuth int type noneAuth int
func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { func (n *noneAuth) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
if err := c.writePacket(Marshal(&userAuthRequestMsg{ if err := c.writePacket(Marshal(&userAuthRequestMsg{
User: user, User: user,
Service: serviceSSH, Service: serviceSSH,
Method: "none", Method: "none",
})); err != nil { })); err != nil {
return false, nil, err return authFailure, nil, err
} }
return handleAuthResponse(c) return handleAuthResponse(c)
@ -111,7 +120,7 @@ func (n *noneAuth) method() string {
// a function call, e.g. by prompting the user. // a function call, e.g. by prompting the user.
type passwordCallback func() (password string, err error) type passwordCallback func() (password string, err error)
func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
type passwordAuthMsg struct { type passwordAuthMsg struct {
User string `sshtype:"50"` User string `sshtype:"50"`
Service string Service string
@ -125,7 +134,7 @@ func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand
// The program may only find out that the user doesn't have a password // The program may only find out that the user doesn't have a password
// when prompting. // when prompting.
if err != nil { if err != nil {
return false, nil, err return authFailure, nil, err
} }
if err := c.writePacket(Marshal(&passwordAuthMsg{ if err := c.writePacket(Marshal(&passwordAuthMsg{
@ -135,7 +144,7 @@ func (cb passwordCallback) auth(session []byte, user string, c packetConn, rand
Reply: false, Reply: false,
Password: pw, Password: pw,
})); err != nil { })); err != nil {
return false, nil, err return authFailure, nil, err
} }
return handleAuthResponse(c) return handleAuthResponse(c)
@ -178,7 +187,7 @@ func (cb publicKeyCallback) method() string {
return "publickey" return "publickey"
} }
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
// Authentication is performed by sending an enquiry to test if a key is // Authentication is performed by sending an enquiry to test if a key is
// acceptable to the remote. If the key is acceptable, the client will // acceptable to the remote. If the key is acceptable, the client will
// attempt to authenticate with the valid key. If not the client will repeat // attempt to authenticate with the valid key. If not the client will repeat
@ -186,13 +195,13 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
signers, err := cb() signers, err := cb()
if err != nil { if err != nil {
return false, nil, err return authFailure, nil, err
} }
var methods []string var methods []string
for _, signer := range signers { for _, signer := range signers {
ok, err := validateKey(signer.PublicKey(), user, c) ok, err := validateKey(signer.PublicKey(), user, c)
if err != nil { if err != nil {
return false, nil, err return authFailure, nil, err
} }
if !ok { if !ok {
continue continue
@ -206,7 +215,7 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
Method: cb.method(), Method: cb.method(),
}, []byte(pub.Type()), pubKey)) }, []byte(pub.Type()), pubKey))
if err != nil { if err != nil {
return false, nil, err return authFailure, nil, err
} }
// manually wrap the serialized signature in a string // manually wrap the serialized signature in a string
@ -224,24 +233,24 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
} }
p := Marshal(&msg) p := Marshal(&msg)
if err := c.writePacket(p); err != nil { if err := c.writePacket(p); err != nil {
return false, nil, err return authFailure, nil, err
} }
var success bool var success authResult
success, methods, err = handleAuthResponse(c) success, methods, err = handleAuthResponse(c)
if err != nil { if err != nil {
return false, nil, err return authFailure, nil, err
} }
// If authentication succeeds or the list of available methods does not // If authentication succeeds or the list of available methods does not
// contain the "publickey" method, do not attempt to authenticate with any // contain the "publickey" method, do not attempt to authenticate with any
// other keys. According to RFC 4252 Section 7, the latter can occur when // other keys. According to RFC 4252 Section 7, the latter can occur when
// additional authentication methods are required. // additional authentication methods are required.
if success || !containsMethod(methods, cb.method()) { if success == authSuccess || !containsMethod(methods, cb.method()) {
return success, methods, err return success, methods, err
} }
} }
return false, methods, nil return authFailure, methods, nil
} }
func containsMethod(methods []string, method string) bool { func containsMethod(methods []string, method string) bool {
@ -283,7 +292,9 @@ func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
} }
switch packet[0] { switch packet[0] {
case msgUserAuthBanner: case msgUserAuthBanner:
// TODO(gpaul): add callback to present the banner to the user if err := handleBannerResponse(c, packet); err != nil {
return false, err
}
case msgUserAuthPubKeyOk: case msgUserAuthPubKeyOk:
var msg userAuthPubKeyOkMsg var msg userAuthPubKeyOkMsg
if err := Unmarshal(packet, &msg); err != nil { if err := Unmarshal(packet, &msg); err != nil {
@ -316,30 +327,53 @@ func PublicKeysCallback(getSigners func() (signers []Signer, err error)) AuthMet
// handleAuthResponse returns whether the preceding authentication request succeeded // handleAuthResponse returns whether the preceding authentication request succeeded
// along with a list of remaining authentication methods to try next and // along with a list of remaining authentication methods to try next and
// an error if an unexpected response was received. // an error if an unexpected response was received.
func handleAuthResponse(c packetConn) (bool, []string, error) { func handleAuthResponse(c packetConn) (authResult, []string, error) {
for { for {
packet, err := c.readPacket() packet, err := c.readPacket()
if err != nil { if err != nil {
return false, nil, err return authFailure, nil, err
} }
switch packet[0] { switch packet[0] {
case msgUserAuthBanner: case msgUserAuthBanner:
// TODO: add callback to present the banner to the user if err := handleBannerResponse(c, packet); err != nil {
return authFailure, nil, err
}
case msgUserAuthFailure: case msgUserAuthFailure:
var msg userAuthFailureMsg var msg userAuthFailureMsg
if err := Unmarshal(packet, &msg); err != nil { if err := Unmarshal(packet, &msg); err != nil {
return false, nil, err return authFailure, nil, err
} }
return false, msg.Methods, nil if msg.PartialSuccess {
return authPartialSuccess, msg.Methods, nil
}
return authFailure, msg.Methods, nil
case msgUserAuthSuccess: case msgUserAuthSuccess:
return true, nil, nil return authSuccess, nil, nil
default: default:
return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0]) return authFailure, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
} }
} }
} }
func handleBannerResponse(c packetConn, packet []byte) error {
var msg userAuthBannerMsg
if err := Unmarshal(packet, &msg); err != nil {
return err
}
transport, ok := c.(*handshakeTransport)
if !ok {
return nil
}
if transport.bannerCallback != nil {
return transport.bannerCallback(msg.Message)
}
return nil
}
// KeyboardInteractiveChallenge should print questions, optionally // KeyboardInteractiveChallenge should print questions, optionally
// disabling echoing (e.g. for passwords), and return all the answers. // disabling echoing (e.g. for passwords), and return all the answers.
// Challenge may be called multiple times in a single session. After // Challenge may be called multiple times in a single session. After
@ -349,7 +383,7 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
// both CLI and GUI environments. // both CLI and GUI environments.
type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error) type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error)
// KeyboardInteractive returns a AuthMethod using a prompt/response // KeyboardInteractive returns an AuthMethod using a prompt/response
// sequence controlled by the server. // sequence controlled by the server.
func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod { func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod {
return challenge return challenge
@ -359,7 +393,7 @@ func (cb KeyboardInteractiveChallenge) method() string {
return "keyboard-interactive" return "keyboard-interactive"
} }
func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) { func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packetConn, rand io.Reader) (authResult, []string, error) {
type initiateMsg struct { type initiateMsg struct {
User string `sshtype:"50"` User string `sshtype:"50"`
Service string Service string
@ -373,37 +407,42 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
Service: serviceSSH, Service: serviceSSH,
Method: "keyboard-interactive", Method: "keyboard-interactive",
})); err != nil { })); err != nil {
return false, nil, err return authFailure, nil, err
} }
for { for {
packet, err := c.readPacket() packet, err := c.readPacket()
if err != nil { if err != nil {
return false, nil, err return authFailure, nil, err
} }
// like handleAuthResponse, but with less options. // like handleAuthResponse, but with less options.
switch packet[0] { switch packet[0] {
case msgUserAuthBanner: case msgUserAuthBanner:
// TODO: Print banners during userauth. if err := handleBannerResponse(c, packet); err != nil {
return authFailure, nil, err
}
continue continue
case msgUserAuthInfoRequest: case msgUserAuthInfoRequest:
// OK // OK
case msgUserAuthFailure: case msgUserAuthFailure:
var msg userAuthFailureMsg var msg userAuthFailureMsg
if err := Unmarshal(packet, &msg); err != nil { if err := Unmarshal(packet, &msg); err != nil {
return false, nil, err return authFailure, nil, err
} }
return false, msg.Methods, nil if msg.PartialSuccess {
return authPartialSuccess, msg.Methods, nil
}
return authFailure, msg.Methods, nil
case msgUserAuthSuccess: case msgUserAuthSuccess:
return true, nil, nil return authSuccess, nil, nil
default: default:
return false, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0]) return authFailure, nil, unexpectedMessageError(msgUserAuthInfoRequest, packet[0])
} }
var msg userAuthInfoRequestMsg var msg userAuthInfoRequestMsg
if err := Unmarshal(packet, &msg); err != nil { if err := Unmarshal(packet, &msg); err != nil {
return false, nil, err return authFailure, nil, err
} }
// Manually unpack the prompt/echo pairs. // Manually unpack the prompt/echo pairs.
@ -413,7 +452,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
for i := 0; i < int(msg.NumPrompts); i++ { for i := 0; i < int(msg.NumPrompts); i++ {
prompt, r, ok := parseString(rest) prompt, r, ok := parseString(rest)
if !ok || len(r) == 0 { if !ok || len(r) == 0 {
return false, nil, errors.New("ssh: prompt format error") return authFailure, nil, errors.New("ssh: prompt format error")
} }
prompts = append(prompts, string(prompt)) prompts = append(prompts, string(prompt))
echos = append(echos, r[0] != 0) echos = append(echos, r[0] != 0)
@ -421,16 +460,16 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
} }
if len(rest) != 0 { if len(rest) != 0 {
return false, nil, errors.New("ssh: extra data following keyboard-interactive pairs") return authFailure, nil, errors.New("ssh: extra data following keyboard-interactive pairs")
} }
answers, err := cb(msg.User, msg.Instruction, prompts, echos) answers, err := cb(msg.User, msg.Instruction, prompts, echos)
if err != nil { if err != nil {
return false, nil, err return authFailure, nil, err
} }
if len(answers) != len(prompts) { if len(answers) != len(prompts) {
return false, nil, errors.New("ssh: not enough answers from keyboard-interactive callback") return authFailure, nil, errors.New("ssh: not enough answers from keyboard-interactive callback")
} }
responseLength := 1 + 4 responseLength := 1 + 4
for _, a := range answers { for _, a := range answers {
@ -446,7 +485,7 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
} }
if err := c.writePacket(serialized); err != nil { if err := c.writePacket(serialized); err != nil {
return false, nil, err return authFailure, nil, err
} }
} }
} }
@ -456,10 +495,10 @@ type retryableAuthMethod struct {
maxTries int maxTries int
} }
func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) { func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok authResult, methods []string, err error) {
for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ { for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
ok, methods, err = r.authMethod.auth(session, user, c, rand) ok, methods, err = r.authMethod.auth(session, user, c, rand)
if ok || err != nil { // either success or error terminate if ok != authFailure || err != nil { // either success, partial success or error terminate
return ok, methods, err return ok, methods, err
} }
} }

View File

@ -24,11 +24,21 @@ const (
serviceSSH = "ssh-connection" serviceSSH = "ssh-connection"
) )
// supportedCiphers specifies the supported ciphers in preference order. // supportedCiphers lists ciphers we support but might not recommend.
var supportedCiphers = []string{ var supportedCiphers = []string{
"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-ctr", "aes192-ctr", "aes256-ctr",
"aes128-gcm@openssh.com", "aes128-gcm@openssh.com",
"arcfour256", "arcfour128", chacha20Poly1305ID,
"arcfour256", "arcfour128", "arcfour",
aes128cbcID,
tripledescbcID,
}
// preferredCiphers specifies the default preference for ciphers.
var preferredCiphers = []string{
"aes128-gcm@openssh.com",
chacha20Poly1305ID,
"aes128-ctr", "aes192-ctr", "aes256-ctr",
} }
// supportedKexAlgos specifies the supported key-exchange algorithms in // supportedKexAlgos specifies the supported key-exchange algorithms in
@ -211,7 +221,7 @@ func (c *Config) SetDefaults() {
c.Rand = rand.Reader c.Rand = rand.Reader
} }
if c.Ciphers == nil { if c.Ciphers == nil {
c.Ciphers = supportedCiphers c.Ciphers = preferredCiphers
} }
var ciphers []string var ciphers []string
for _, c := range c.Ciphers { for _, c := range c.Ciphers {
@ -242,7 +252,7 @@ func (c *Config) SetDefaults() {
// buildDataSignedForAuth returns the data that is signed in order to prove // buildDataSignedForAuth returns the data that is signed in order to prove
// possession of a private key. See RFC 4252, section 7. // possession of a private key. See RFC 4252, section 7.
func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { func buildDataSignedForAuth(sessionID []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte {
data := struct { data := struct {
Session []byte Session []byte
Type byte Type byte
@ -253,7 +263,7 @@ func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubK
Algo []byte Algo []byte
PubKey []byte PubKey []byte
}{ }{
sessionId, sessionID,
msgUserAuthRequest, msgUserAuthRequest,
req.User, req.User,
req.Service, req.Service,

View File

@ -25,7 +25,7 @@ type ConnMetadata interface {
// User returns the user ID for this connection. // User returns the user ID for this connection.
User() string User() string
// SessionID returns the sesson hash, also denoted by H. // SessionID returns the session hash, also denoted by H.
SessionID() []byte SessionID() []byte
// ClientVersion returns the client's version string as hashed // ClientVersion returns the client's version string as hashed

View File

@ -78,6 +78,11 @@ type handshakeTransport struct {
dialAddress string dialAddress string
remoteAddr net.Addr remoteAddr net.Addr
// bannerCallback is non-empty if we are the client and it has been set in
// ClientConfig. In that case it is called during the user authentication
// dance to handle a custom server's message.
bannerCallback BannerCallback
// Algorithms agreed in the last key exchange. // Algorithms agreed in the last key exchange.
algorithms *algorithms algorithms *algorithms
@ -120,6 +125,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
t.dialAddress = dialAddr t.dialAddress = dialAddr
t.remoteAddr = addr t.remoteAddr = addr
t.hostKeyCallback = config.HostKeyCallback t.hostKeyCallback = config.HostKeyCallback
t.bannerCallback = config.BannerCallback
if config.HostKeyAlgorithms != nil { if config.HostKeyAlgorithms != nil {
t.hostKeyAlgorithms = config.HostKeyAlgorithms t.hostKeyAlgorithms = config.HostKeyAlgorithms
} else { } else {

View File

@ -119,7 +119,7 @@ func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handsha
return nil, err return nil, err
} }
kInt, err := group.diffieHellman(kexDHReply.Y, x) ki, err := group.diffieHellman(kexDHReply.Y, x)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -129,8 +129,8 @@ func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handsha
writeString(h, kexDHReply.HostKey) writeString(h, kexDHReply.HostKey)
writeInt(h, X) writeInt(h, X)
writeInt(h, kexDHReply.Y) writeInt(h, kexDHReply.Y)
K := make([]byte, intLength(kInt)) K := make([]byte, intLength(ki))
marshalInt(K, kInt) marshalInt(K, ki)
h.Write(K) h.Write(K)
return &kexResult{ return &kexResult{
@ -164,7 +164,7 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha
} }
Y := new(big.Int).Exp(group.g, y, group.p) Y := new(big.Int).Exp(group.g, y, group.p)
kInt, err := group.diffieHellman(kexDHInit.X, y) ki, err := group.diffieHellman(kexDHInit.X, y)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -177,8 +177,8 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha
writeInt(h, kexDHInit.X) writeInt(h, kexDHInit.X)
writeInt(h, Y) writeInt(h, Y)
K := make([]byte, intLength(kInt)) K := make([]byte, intLength(ki))
marshalInt(K, kInt) marshalInt(K, ki)
h.Write(K) h.Write(K)
H := h.Sum(nil) H := h.Sum(nil)
@ -383,8 +383,8 @@ func init() {
// 4253 and Oakley Group 2 in RFC 2409. // 4253 and Oakley Group 2 in RFC 2409.
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
g: new(big.Int).SetInt64(2), g: new(big.Int).SetInt64(2),
p: p, p: p,
pMinus1: new(big.Int).Sub(p, bigOne), pMinus1: new(big.Int).Sub(p, bigOne),
} }
@ -393,8 +393,8 @@ func init() {
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
g: new(big.Int).SetInt64(2), g: new(big.Int).SetInt64(2),
p: p, p: p,
pMinus1: new(big.Int).Sub(p, bigOne), pMinus1: new(big.Int).Sub(p, bigOne),
} }
@ -462,9 +462,9 @@ func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handsh
writeString(h, kp.pub[:]) writeString(h, kp.pub[:])
writeString(h, reply.EphemeralPubKey) writeString(h, reply.EphemeralPubKey)
kInt := new(big.Int).SetBytes(secret[:]) ki := new(big.Int).SetBytes(secret[:])
K := make([]byte, intLength(kInt)) K := make([]byte, intLength(ki))
marshalInt(K, kInt) marshalInt(K, ki)
h.Write(K) h.Write(K)
return &kexResult{ return &kexResult{
@ -510,9 +510,9 @@ func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handsh
writeString(h, kexInit.ClientPubKey) writeString(h, kexInit.ClientPubKey)
writeString(h, kp.pub[:]) writeString(h, kp.pub[:])
kInt := new(big.Int).SetBytes(secret[:]) ki := new(big.Int).SetBytes(secret[:])
K := make([]byte, intLength(kInt)) K := make([]byte, intLength(ki))
marshalInt(K, kInt) marshalInt(K, ki)
h.Write(K) h.Write(K)
H := h.Sum(nil) H := h.Sum(nil)

View File

@ -276,7 +276,8 @@ type PublicKey interface {
Type() string Type() string
// Marshal returns the serialized key data in SSH wire format, // Marshal returns the serialized key data in SSH wire format,
// with the name prefix. // with the name prefix. To unmarshal the returned data, use
// the ParsePublicKey function.
Marshal() []byte Marshal() []byte
// Verify that sig is a signature on the given data using this // Verify that sig is a signature on the given data using this
@ -363,10 +364,21 @@ func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
type dsaPublicKey dsa.PublicKey type dsaPublicKey dsa.PublicKey
func (r *dsaPublicKey) Type() string { func (k *dsaPublicKey) Type() string {
return "ssh-dss" return "ssh-dss"
} }
func checkDSAParams(param *dsa.Parameters) error {
// SSH specifies FIPS 186-2, which only provided a single size
// (1024 bits) DSA key. FIPS 186-3 allows for larger key
// sizes, which would confuse SSH.
if l := param.P.BitLen(); l != 1024 {
return fmt.Errorf("ssh: unsupported DSA key size %d", l)
}
return nil
}
// parseDSA parses an DSA key according to RFC 4253, section 6.6. // parseDSA parses an DSA key according to RFC 4253, section 6.6.
func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
var w struct { var w struct {
@ -377,13 +389,18 @@ func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
return nil, nil, err return nil, nil, err
} }
param := dsa.Parameters{
P: w.P,
Q: w.Q,
G: w.G,
}
if err := checkDSAParams(&param); err != nil {
return nil, nil, err
}
key := &dsaPublicKey{ key := &dsaPublicKey{
Parameters: dsa.Parameters{ Parameters: param,
P: w.P, Y: w.Y,
Q: w.Q,
G: w.G,
},
Y: w.Y,
} }
return key, w.Rest, nil return key, w.Rest, nil
} }
@ -465,12 +482,12 @@ func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
type ecdsaPublicKey ecdsa.PublicKey type ecdsaPublicKey ecdsa.PublicKey
func (key *ecdsaPublicKey) Type() string { func (k *ecdsaPublicKey) Type() string {
return "ecdsa-sha2-" + key.nistID() return "ecdsa-sha2-" + k.nistID()
} }
func (key *ecdsaPublicKey) nistID() string { func (k *ecdsaPublicKey) nistID() string {
switch key.Params().BitSize { switch k.Params().BitSize {
case 256: case 256:
return "nistp256" return "nistp256"
case 384: case 384:
@ -483,7 +500,7 @@ func (key *ecdsaPublicKey) nistID() string {
type ed25519PublicKey ed25519.PublicKey type ed25519PublicKey ed25519.PublicKey
func (key ed25519PublicKey) Type() string { func (k ed25519PublicKey) Type() string {
return KeyAlgoED25519 return KeyAlgoED25519
} }
@ -502,23 +519,23 @@ func parseED25519(in []byte) (out PublicKey, rest []byte, err error) {
return (ed25519PublicKey)(key), w.Rest, nil return (ed25519PublicKey)(key), w.Rest, nil
} }
func (key ed25519PublicKey) Marshal() []byte { func (k ed25519PublicKey) Marshal() []byte {
w := struct { w := struct {
Name string Name string
KeyBytes []byte KeyBytes []byte
}{ }{
KeyAlgoED25519, KeyAlgoED25519,
[]byte(key), []byte(k),
} }
return Marshal(&w) return Marshal(&w)
} }
func (key ed25519PublicKey) Verify(b []byte, sig *Signature) error { func (k ed25519PublicKey) Verify(b []byte, sig *Signature) error {
if sig.Format != key.Type() { if sig.Format != k.Type() {
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type()) return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
} }
edKey := (ed25519.PublicKey)(key) edKey := (ed25519.PublicKey)(k)
if ok := ed25519.Verify(edKey, b, sig.Blob); !ok { if ok := ed25519.Verify(edKey, b, sig.Blob); !ok {
return errors.New("ssh: signature did not verify") return errors.New("ssh: signature did not verify")
} }
@ -579,9 +596,9 @@ func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
return (*ecdsaPublicKey)(key), w.Rest, nil return (*ecdsaPublicKey)(key), w.Rest, nil
} }
func (key *ecdsaPublicKey) Marshal() []byte { func (k *ecdsaPublicKey) Marshal() []byte {
// See RFC 5656, section 3.1. // See RFC 5656, section 3.1.
keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y) keyBytes := elliptic.Marshal(k.Curve, k.X, k.Y)
// ECDSA publickey struct layout should match the struct used by // ECDSA publickey struct layout should match the struct used by
// parseECDSACert in the x/crypto/ssh/agent package. // parseECDSACert in the x/crypto/ssh/agent package.
w := struct { w := struct {
@ -589,20 +606,20 @@ func (key *ecdsaPublicKey) Marshal() []byte {
ID string ID string
Key []byte Key []byte
}{ }{
key.Type(), k.Type(),
key.nistID(), k.nistID(),
keyBytes, keyBytes,
} }
return Marshal(&w) return Marshal(&w)
} }
func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error { func (k *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
if sig.Format != key.Type() { if sig.Format != k.Type() {
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type()) return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
} }
h := ecHash(key.Curve).New() h := ecHash(k.Curve).New()
h.Write(data) h.Write(data)
digest := h.Sum(nil) digest := h.Sum(nil)
@ -619,7 +636,7 @@ func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
return err return err
} }
if ecdsa.Verify((*ecdsa.PublicKey)(key), digest, ecSig.R, ecSig.S) { if ecdsa.Verify((*ecdsa.PublicKey)(k), digest, ecSig.R, ecSig.S) {
return nil return nil
} }
return errors.New("ssh: signature did not verify") return errors.New("ssh: signature did not verify")
@ -630,19 +647,28 @@ func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey {
} }
// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
// *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding // *ecdsa.PrivateKey or any other crypto.Signer and returns a
// Signer instance. ECDSA keys must use P-256, P-384 or P-521. // corresponding Signer instance. ECDSA keys must use P-256, P-384 or
// P-521. DSA keys must use parameter size L1024N160.
func NewSignerFromKey(key interface{}) (Signer, error) { func NewSignerFromKey(key interface{}) (Signer, error) {
switch key := key.(type) { switch key := key.(type) {
case crypto.Signer: case crypto.Signer:
return NewSignerFromSigner(key) return NewSignerFromSigner(key)
case *dsa.PrivateKey: case *dsa.PrivateKey:
return &dsaPrivateKey{key}, nil return newDSAPrivateKey(key)
default: default:
return nil, fmt.Errorf("ssh: unsupported key type %T", key) return nil, fmt.Errorf("ssh: unsupported key type %T", key)
} }
} }
func newDSAPrivateKey(key *dsa.PrivateKey) (Signer, error) {
if err := checkDSAParams(&key.PublicKey.Parameters); err != nil {
return nil, err
}
return &dsaPrivateKey{key}, nil
}
type wrappedSigner struct { type wrappedSigner struct {
signer crypto.Signer signer crypto.Signer
pubKey PublicKey pubKey PublicKey
@ -733,7 +759,7 @@ func NewPublicKey(key interface{}) (PublicKey, error) {
return (*rsaPublicKey)(key), nil return (*rsaPublicKey)(key), nil
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
if !supportedEllipticCurve(key.Curve) { if !supportedEllipticCurve(key.Curve) {
return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported.") return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported")
} }
return (*ecdsaPublicKey)(key), nil return (*ecdsaPublicKey)(key), nil
case *dsa.PublicKey: case *dsa.PublicKey:
@ -756,6 +782,18 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) {
return NewSignerFromKey(key) return NewSignerFromKey(key)
} }
// ParsePrivateKeyWithPassphrase returns a Signer from a PEM encoded private
// key and passphrase. It supports the same keys as
// ParseRawPrivateKeyWithPassphrase.
func ParsePrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (Signer, error) {
key, err := ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase)
if err != nil {
return nil, err
}
return NewSignerFromKey(key)
}
// encryptedBlock tells whether a private key is // encryptedBlock tells whether a private key is
// encrypted by examining its Proc-Type header // encrypted by examining its Proc-Type header
// for a mention of ENCRYPTED // for a mention of ENCRYPTED
@ -790,6 +828,43 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
} }
} }
// ParseRawPrivateKeyWithPassphrase returns a private key decrypted with
// passphrase from a PEM encoded private key. If wrong passphrase, return
// x509.IncorrectPasswordError.
func ParseRawPrivateKeyWithPassphrase(pemBytes, passPhrase []byte) (interface{}, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("ssh: no key found")
}
buf := block.Bytes
if encryptedBlock(block) {
if x509.IsEncryptedPEMBlock(block) {
var err error
buf, err = x509.DecryptPEMBlock(block, passPhrase)
if err != nil {
if err == x509.IncorrectPasswordError {
return nil, err
}
return nil, fmt.Errorf("ssh: cannot decode encrypted private keys: %v", err)
}
}
}
switch block.Type {
case "RSA PRIVATE KEY":
return x509.ParsePKCS1PrivateKey(buf)
case "EC PRIVATE KEY":
return x509.ParseECPrivateKey(buf)
case "DSA PRIVATE KEY":
return ParseDSAPrivateKey(buf)
case "OPENSSH PRIVATE KEY":
return parseOpenSSHPrivateKey(buf)
default:
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
}
}
// 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
// specified by the OpenSSL DSA man page. // specified by the OpenSSL DSA man page.
func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {

View File

@ -23,10 +23,6 @@ const (
msgUnimplemented = 3 msgUnimplemented = 3
msgDebug = 4 msgDebug = 4
msgNewKeys = 21 msgNewKeys = 21
// Standard authentication messages
msgUserAuthSuccess = 52
msgUserAuthBanner = 53
) )
// SSH messages: // SSH messages:
@ -137,6 +133,18 @@ type userAuthFailureMsg struct {
PartialSuccess bool PartialSuccess bool
} }
// See RFC 4252, section 5.1
const msgUserAuthSuccess = 52
// See RFC 4252, section 5.4
const msgUserAuthBanner = 53
type userAuthBannerMsg struct {
Message string `sshtype:"53"`
// unused, but required to allow message parsing
Language string
}
// See RFC 4256, section 3.2 // See RFC 4256, section 3.2
const msgUserAuthInfoRequest = 60 const msgUserAuthInfoRequest = 60
const msgUserAuthInfoResponse = 61 const msgUserAuthInfoResponse = 61
@ -154,7 +162,7 @@ const msgChannelOpen = 90
type channelOpenMsg struct { type channelOpenMsg struct {
ChanType string `sshtype:"90"` ChanType string `sshtype:"90"`
PeersId uint32 PeersID uint32
PeersWindow uint32 PeersWindow uint32
MaxPacketSize uint32 MaxPacketSize uint32
TypeSpecificData []byte `ssh:"rest"` TypeSpecificData []byte `ssh:"rest"`
@ -165,7 +173,7 @@ const msgChannelData = 94
// Used for debug print outs of packets. // Used for debug print outs of packets.
type channelDataMsg struct { type channelDataMsg struct {
PeersId uint32 `sshtype:"94"` PeersID uint32 `sshtype:"94"`
Length uint32 Length uint32
Rest []byte `ssh:"rest"` Rest []byte `ssh:"rest"`
} }
@ -174,8 +182,8 @@ type channelDataMsg struct {
const msgChannelOpenConfirm = 91 const msgChannelOpenConfirm = 91
type channelOpenConfirmMsg struct { type channelOpenConfirmMsg struct {
PeersId uint32 `sshtype:"91"` PeersID uint32 `sshtype:"91"`
MyId uint32 MyID uint32
MyWindow uint32 MyWindow uint32
MaxPacketSize uint32 MaxPacketSize uint32
TypeSpecificData []byte `ssh:"rest"` TypeSpecificData []byte `ssh:"rest"`
@ -185,7 +193,7 @@ type channelOpenConfirmMsg struct {
const msgChannelOpenFailure = 92 const msgChannelOpenFailure = 92
type channelOpenFailureMsg struct { type channelOpenFailureMsg struct {
PeersId uint32 `sshtype:"92"` PeersID uint32 `sshtype:"92"`
Reason RejectionReason Reason RejectionReason
Message string Message string
Language string Language string
@ -194,7 +202,7 @@ type channelOpenFailureMsg struct {
const msgChannelRequest = 98 const msgChannelRequest = 98
type channelRequestMsg struct { type channelRequestMsg struct {
PeersId uint32 `sshtype:"98"` PeersID uint32 `sshtype:"98"`
Request string Request string
WantReply bool WantReply bool
RequestSpecificData []byte `ssh:"rest"` RequestSpecificData []byte `ssh:"rest"`
@ -204,28 +212,28 @@ type channelRequestMsg struct {
const msgChannelSuccess = 99 const msgChannelSuccess = 99
type channelRequestSuccessMsg struct { type channelRequestSuccessMsg struct {
PeersId uint32 `sshtype:"99"` PeersID uint32 `sshtype:"99"`
} }
// See RFC 4254, section 5.4. // See RFC 4254, section 5.4.
const msgChannelFailure = 100 const msgChannelFailure = 100
type channelRequestFailureMsg struct { type channelRequestFailureMsg struct {
PeersId uint32 `sshtype:"100"` PeersID uint32 `sshtype:"100"`
} }
// See RFC 4254, section 5.3 // See RFC 4254, section 5.3
const msgChannelClose = 97 const msgChannelClose = 97
type channelCloseMsg struct { type channelCloseMsg struct {
PeersId uint32 `sshtype:"97"` PeersID uint32 `sshtype:"97"`
} }
// See RFC 4254, section 5.3 // See RFC 4254, section 5.3
const msgChannelEOF = 96 const msgChannelEOF = 96
type channelEOFMsg struct { type channelEOFMsg struct {
PeersId uint32 `sshtype:"96"` PeersID uint32 `sshtype:"96"`
} }
// See RFC 4254, section 4 // See RFC 4254, section 4
@ -255,7 +263,7 @@ type globalRequestFailureMsg struct {
const msgChannelWindowAdjust = 93 const msgChannelWindowAdjust = 93
type windowAdjustMsg struct { type windowAdjustMsg struct {
PeersId uint32 `sshtype:"93"` PeersID uint32 `sshtype:"93"`
AdditionalBytes uint32 AdditionalBytes uint32
} }

View File

@ -278,7 +278,7 @@ func (m *mux) handleChannelOpen(packet []byte) error {
if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 { if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
failMsg := channelOpenFailureMsg{ failMsg := channelOpenFailureMsg{
PeersId: msg.PeersId, PeersID: msg.PeersID,
Reason: ConnectionFailed, Reason: ConnectionFailed,
Message: "invalid request", Message: "invalid request",
Language: "en_US.UTF-8", Language: "en_US.UTF-8",
@ -287,7 +287,7 @@ func (m *mux) handleChannelOpen(packet []byte) error {
} }
c := m.newChannel(msg.ChanType, channelInbound, msg.TypeSpecificData) c := m.newChannel(msg.ChanType, channelInbound, msg.TypeSpecificData)
c.remoteId = msg.PeersId c.remoteId = msg.PeersID
c.maxRemotePayload = msg.MaxPacketSize c.maxRemotePayload = msg.MaxPacketSize
c.remoteWin.add(msg.PeersWindow) c.remoteWin.add(msg.PeersWindow)
m.incomingChannels <- c m.incomingChannels <- c
@ -313,7 +313,7 @@ func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) {
PeersWindow: ch.myWindow, PeersWindow: ch.myWindow,
MaxPacketSize: ch.maxIncomingPayload, MaxPacketSize: ch.maxIncomingPayload,
TypeSpecificData: extra, TypeSpecificData: extra,
PeersId: ch.localId, PeersID: ch.localId,
} }
if err := m.sendMessage(open); err != nil { if err := m.sendMessage(open); err != nil {
return nil, err return nil, err

View File

@ -14,23 +14,34 @@ import (
) )
// The Permissions type holds fine-grained permissions that are // The Permissions type holds fine-grained permissions that are
// specific to a user or a specific authentication method for a // specific to a user or a specific authentication method for a user.
// user. Permissions, except for "source-address", must be enforced in // The Permissions value for a successful authentication attempt is
// the server application layer, after successful authentication. The // available in ServerConn, so it can be used to pass information from
// Permissions are passed on in ServerConn so a server implementation // the user-authentication phase to the application layer.
// can honor them.
type Permissions struct { type Permissions struct {
// Critical options restrict default permissions. Common // CriticalOptions indicate restrictions to the default
// restrictions are "source-address" and "force-command". If // permissions, and are typically used in conjunction with
// the server cannot enforce the restriction, or does not // user certificates. The standard for SSH certificates
// recognize it, the user should not authenticate. // defines "force-command" (only allow the given command to
// execute) and "source-address" (only allow connections from
// the given address). The SSH package currently only enforces
// the "source-address" critical option. It is up to server
// implementations to enforce other critical options, such as
// "force-command", by checking them after the SSH handshake
// is successful. In general, SSH servers should reject
// connections that specify critical options that are unknown
// or not supported.
CriticalOptions map[string]string CriticalOptions map[string]string
// Extensions are extra functionality that the server may // Extensions are extra functionality that the server may
// offer on authenticated connections. Common extensions are // offer on authenticated connections. Lack of support for an
// "permit-agent-forwarding", "permit-X11-forwarding". Lack of // extension does not preclude authenticating a user. Common
// support for an extension does not preclude authenticating a // extensions are "permit-agent-forwarding",
// user. // "permit-X11-forwarding". The Go SSH library currently does
// not act on any extension, and it is up to server
// implementations to honor them. Extensions can be used to
// pass data from the authentication callbacks to the server
// application layer.
Extensions map[string]string Extensions map[string]string
} }
@ -55,9 +66,14 @@ type ServerConfig struct {
// attempts to authenticate using a password. // attempts to authenticate using a password.
PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error) PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
// PublicKeyCallback, if non-nil, is called when a client attempts public // PublicKeyCallback, if non-nil, is called when a client
// key authentication. It must return true if the given public key is // offers a public key for authentication. It must return a nil error
// valid for the given user. For example, see CertChecker.Authenticate. // if the given public key can be used to authenticate the
// given user. For example, see CertChecker.Authenticate. A
// call to this function does not guarantee that the key
// offered is in fact used to authenticate. To record any data
// depending on the public key, store it inside a
// Permissions.Extensions entry.
PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error) PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
// KeyboardInteractiveCallback, if non-nil, is called when // KeyboardInteractiveCallback, if non-nil, is called when
@ -79,6 +95,10 @@ type ServerConfig struct {
// Note that RFC 4253 section 4.2 requires that this string start with // Note that RFC 4253 section 4.2 requires that this string start with
// "SSH-2.0-". // "SSH-2.0-".
ServerVersion string ServerVersion string
// BannerCallback, if present, is called and the return string is sent to
// the client after key exchange completed but before authentication.
BannerCallback func(conn ConnMetadata) string
} }
// AddHostKey adds a private key as a host key. If an existing host // AddHostKey adds a private key as a host key. If an existing host
@ -146,6 +166,9 @@ type ServerConn struct {
// unsuccessful, it closes the connection and returns an error. The // unsuccessful, it closes the connection and returns an error. The
// Request and NewChannel channels must be serviced, or the connection // Request and NewChannel channels must be serviced, or the connection
// will hang. // will hang.
//
// The returned error may be of type *ServerAuthError for
// authentication errors.
func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) { func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
fullConf := *config fullConf := *config
fullConf.SetDefaults() fullConf.SetDefaults()
@ -236,7 +259,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
func isAcceptableAlgo(algo string) bool { func isAcceptableAlgo(algo string) bool {
switch algo { switch algo {
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519, case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
return true return true
} }
return false return false
@ -272,12 +295,39 @@ 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)
} }
// ServerAuthError represents server authentication errors and is
// sometimes returned by NewServerConn. It appends any authentication
// errors that may occur, and is returned if all of the authentication
// methods provided by the user failed to authenticate.
type ServerAuthError struct {
// Errors contains authentication errors returned by the authentication
// callback methods. The first entry is typically ErrNoAuth.
Errors []error
}
func (l ServerAuthError) Error() string {
var errs []string
for _, err := range l.Errors {
errs = append(errs, err.Error())
}
return "[" + strings.Join(errs, ", ") + "]"
}
// ErrNoAuth is the error value returned if no
// authentication method has been passed yet. This happens as a normal
// part of the authentication loop, since the client first tries
// 'none' authentication to discover available methods.
// It is returned in ServerAuthError.Errors from NewServerConn.
var ErrNoAuth = errors.New("ssh: no auth passed yet")
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
sessionID := s.transport.getSessionID() sessionID := s.transport.getSessionID()
var cache pubKeyCache var cache pubKeyCache
var perms *Permissions var perms *Permissions
authFailures := 0 authFailures := 0
var authErrs []error
var displayedBanner bool
userAuthLoop: userAuthLoop:
for { for {
@ -296,6 +346,9 @@ userAuthLoop:
var userAuthReq userAuthRequestMsg var userAuthReq userAuthRequestMsg
if packet, err := s.transport.readPacket(); err != nil { if packet, err := s.transport.readPacket(); err != nil {
if err == io.EOF {
return nil, &ServerAuthError{Errors: authErrs}
}
return nil, err return nil, err
} else if err = Unmarshal(packet, &userAuthReq); err != nil { } else if err = Unmarshal(packet, &userAuthReq); err != nil {
return nil, err return nil, err
@ -306,8 +359,22 @@ userAuthLoop:
} }
s.user = userAuthReq.User s.user = userAuthReq.User
if !displayedBanner && config.BannerCallback != nil {
displayedBanner = true
msg := config.BannerCallback(s)
if msg != "" {
bannerMsg := &userAuthBannerMsg{
Message: msg,
}
if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
return nil, err
}
}
}
perms = nil perms = nil
authErr := errors.New("no auth passed yet") authErr := ErrNoAuth
switch userAuthReq.Method { switch userAuthReq.Method {
case "none": case "none":
@ -432,6 +499,8 @@ userAuthLoop:
authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method) authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method)
} }
authErrs = append(authErrs, authErr)
if config.AuthLogCallback != nil { if config.AuthLogCallback != nil {
config.AuthLogCallback(s, userAuthReq.Method, authErr) config.AuthLogCallback(s, userAuthReq.Method, authErr)
} }

View File

@ -231,6 +231,26 @@ func (s *Session) RequestSubsystem(subsystem string) error {
return err return err
} }
// RFC 4254 Section 6.7.
type ptyWindowChangeMsg struct {
Columns uint32
Rows uint32
Width uint32
Height uint32
}
// WindowChange informs the remote host about a terminal window dimension change to h rows and w columns.
func (s *Session) WindowChange(h, w int) error {
req := ptyWindowChangeMsg{
Columns: uint32(w),
Rows: uint32(h),
Width: uint32(w * 8),
Height: uint32(h * 8),
}
_, err := s.ch.SendRequest("window-change", false, Marshal(&req))
return err
}
// RFC 4254 Section 6.9. // RFC 4254 Section 6.9.
type signalMsg struct { type signalMsg struct {
Signal string Signal string
@ -386,7 +406,7 @@ func (s *Session) Wait() error {
s.stdinPipeWriter.Close() s.stdinPipeWriter.Close()
} }
var copyError error var copyError error
for _ = range s.copyFuncs { for range s.copyFuncs {
if err := <-s.errors; err != nil && copyError == nil { if err := <-s.errors; err != nil && copyError == nil {
copyError = err copyError = err
} }

View File

@ -6,6 +6,7 @@ package ssh
import ( import (
"bufio" "bufio"
"bytes"
"errors" "errors"
"io" "io"
"log" "log"
@ -76,17 +77,17 @@ type connectionState struct {
// both directions are triggered by reading and writing a msgNewKey packet // both directions are triggered by reading and writing a msgNewKey packet
// respectively. // respectively.
func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error { func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil { ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult)
if err != nil {
return err return err
} else {
t.reader.pendingKeyChange <- ciph
} }
t.reader.pendingKeyChange <- ciph
if ciph, err := newPacketCipher(t.writer.dir, algs.w, kexResult); err != nil { ciph, err = newPacketCipher(t.writer.dir, algs.w, kexResult)
if err != nil {
return err return err
} else {
t.writer.pendingKeyChange <- ciph
} }
t.writer.pendingKeyChange <- ciph
return nil return nil
} }
@ -139,7 +140,7 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
case cipher := <-s.pendingKeyChange: case cipher := <-s.pendingKeyChange:
s.packetCipher = cipher s.packetCipher = cipher
default: default:
return nil, errors.New("ssh: got bogus newkeys message.") return nil, errors.New("ssh: got bogus newkeys message")
} }
case msgDisconnect: case msgDisconnect:
@ -232,52 +233,22 @@ var (
clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}} clientKeys = direction{[]byte{'A'}, []byte{'C'}, []byte{'E'}}
) )
// generateKeys generates key material for IV, MAC and encryption.
func generateKeys(d direction, algs directionAlgorithms, kex *kexResult) (iv, key, macKey []byte) {
cipherMode := cipherModes[algs.Cipher]
macMode := macModes[algs.MAC]
iv = make([]byte, cipherMode.ivSize)
key = make([]byte, cipherMode.keySize)
macKey = make([]byte, macMode.keySize)
generateKeyMaterial(iv, d.ivTag, kex)
generateKeyMaterial(key, d.keyTag, kex)
generateKeyMaterial(macKey, d.macKeyTag, kex)
return
}
// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as // setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
// described in RFC 4253, section 6.4. direction should either be serverKeys // described in RFC 4253, section 6.4. direction should either be serverKeys
// (to setup server->client keys) or clientKeys (for client->server keys). // (to setup server->client keys) or clientKeys (for client->server keys).
func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) { func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
iv, key, macKey := generateKeys(d, algs, kex) cipherMode := cipherModes[algs.Cipher]
macMode := macModes[algs.MAC]
if algs.Cipher == gcmCipherID { iv := make([]byte, cipherMode.ivSize)
return newGCMCipher(iv, key, macKey) key := make([]byte, cipherMode.keySize)
} macKey := make([]byte, macMode.keySize)
if algs.Cipher == aes128cbcID { generateKeyMaterial(iv, d.ivTag, kex)
return newAESCBCCipher(iv, key, macKey, algs) generateKeyMaterial(key, d.keyTag, kex)
} generateKeyMaterial(macKey, d.macKeyTag, kex)
if algs.Cipher == tripledescbcID { return cipherModes[algs.Cipher].create(key, iv, macKey, algs)
return newTripleDESCBCCipher(iv, key, macKey, algs)
}
c := &streamPacketCipher{
mac: macModes[algs.MAC].new(macKey),
etm: macModes[algs.MAC].etm,
}
c.macResult = make([]byte, c.mac.Size())
var err error
c.cipher, err = cipherModes[algs.Cipher].createStream(key, iv)
if err != nil {
return nil, err
}
return c, nil
} }
// generateKeyMaterial fills out with key material generated from tag, K, H // generateKeyMaterial fills out with key material generated from tag, K, H
@ -342,7 +313,7 @@ func readVersion(r io.Reader) ([]byte, error) {
var ok bool var ok bool
var buf [1]byte var buf [1]byte
for len(versionString) < maxVersionStringBytes { for length := 0; length < maxVersionStringBytes; length++ {
_, err := io.ReadFull(r, buf[:]) _, err := io.ReadFull(r, buf[:])
if err != nil { if err != nil {
return nil, err return nil, err
@ -350,6 +321,13 @@ func readVersion(r io.Reader) ([]byte, error) {
// The RFC says that the version should be terminated with \r\n // The RFC says that the version should be terminated with \r\n
// but several SSH servers actually only send a \n. // but several SSH servers actually only send a \n.
if buf[0] == '\n' { if buf[0] == '\n' {
if !bytes.HasPrefix(versionString, []byte("SSH-")) {
// RFC 4253 says we need to ignore all version string lines
// except the one containing the SSH version (provided that
// all the lines do not exceed 255 bytes in total).
versionString = versionString[:0]
continue
}
ok = true ok = true
break break
} }

30
vendor/vendor.json vendored
View File

@ -1349,28 +1349,40 @@
"revision": "7682e7e3945130cf3cde089834664f68afdd1523", "revision": "7682e7e3945130cf3cde089834664f68afdd1523",
"revisionTime": "2016-10-03T20:54:26Z" "revisionTime": "2016-10-03T20:54:26Z"
}, },
{
"checksumSHA1": "hfABw6DX9B4Ma+88qDDGz9qY45s=",
"path": "golang.org/x/crypto/internal/chacha20",
"revision": "e73bf333ef8920dbb52ad18d4bd38ad9d9bc76d7",
"revisionTime": "2018-04-20T16:33:09Z"
},
{ {
"checksumSHA1": "MCeXr2RNeiG1XG6V+er1OR0qyeo=", "checksumSHA1": "MCeXr2RNeiG1XG6V+er1OR0qyeo=",
"path": "golang.org/x/crypto/md4", "path": "golang.org/x/crypto/md4",
"revision": "1f22c0103821b9390939b6776727195525381532" "revision": "1f22c0103821b9390939b6776727195525381532"
}, },
{ {
"checksumSHA1": "comxRp9FBqgZMXW1ZJjxkGw9VwA=", "checksumSHA1": "zq0rCEQJmO9Qbi24TiWVnarX4Mw=",
"path": "golang.org/x/crypto/ssh", "path": "golang.org/x/crypto/poly1305",
"revision": "0fe963104e9d1877082f8fb38f816fcd97eb1d10", "revision": "e73bf333ef8920dbb52ad18d4bd38ad9d9bc76d7",
"revisionTime": "2017-05-16T11:44:04Z" "revisionTime": "2018-04-20T16:33:09Z"
}, },
{ {
"checksumSHA1": "SJ3Ma3Ozavxpbh1usZWBCnzMKIc=", "checksumSHA1": "P23qZ/7bvTeahYJCUbcIDtqCzus=",
"path": "golang.org/x/crypto/ssh",
"revision": "e73bf333ef8920dbb52ad18d4bd38ad9d9bc76d7",
"revisionTime": "2018-04-20T16:33:09Z"
},
{
"checksumSHA1": "NMRX0onGReaL9IfLr0XQ3kl5Id0=",
"path": "golang.org/x/crypto/ssh/agent", "path": "golang.org/x/crypto/ssh/agent",
"revision": "453249f01cfeb54c3d549ddb75ff152ca243f9d8", "revision": "e73bf333ef8920dbb52ad18d4bd38ad9d9bc76d7",
"revisionTime": "2017-02-08T20:51:15Z" "revisionTime": "2018-04-20T16:33:09Z"
}, },
{ {
"checksumSHA1": "BGm8lKZmvJbf/YOJLeL1rw2WVjA=", "checksumSHA1": "BGm8lKZmvJbf/YOJLeL1rw2WVjA=",
"path": "golang.org/x/crypto/ssh/terminal", "path": "golang.org/x/crypto/ssh/terminal",
"revision": "88942b9c40a4c9d203b82b3731787b672d6e809b", "revision": "e73bf333ef8920dbb52ad18d4bd38ad9d9bc76d7",
"revisionTime": "2018-03-21T23:38:19Z" "revisionTime": "2018-04-20T16:33:09Z"
}, },
{ {
"checksumSHA1": "GtamqiJoL7PGHsN454AoffBFMa8=", "checksumSHA1": "GtamqiJoL7PGHsN454AoffBFMa8=",