Bump crypto package (#9661)

This is the fix up of #9345
This commit is contained in:
Paul Stack 2016-10-27 23:57:25 +01:00 committed by GitHub
parent 1acc73457b
commit f1202c97a5
29 changed files with 4685 additions and 1589 deletions

View File

@ -1,841 +0,0 @@
// Copyright 2013 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.
// We have a implementation in amd64 assembly so this code is only run on
// non-amd64 platforms. The amd64 assembly does not support gccgo.
// +build !amd64 gccgo appengine
package curve25519
// This code is a port of the public domain, "ref10" implementation of
// curve25519 from SUPERCOP 20130419 by D. J. Bernstein.
// fieldElement represents an element of the field GF(2^255 - 19). An element
// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
// context.
type fieldElement [10]int32
func feZero(fe *fieldElement) {
for i := range fe {
fe[i] = 0
}
}
func feOne(fe *fieldElement) {
feZero(fe)
fe[0] = 1
}
func feAdd(dst, a, b *fieldElement) {
for i := range dst {
dst[i] = a[i] + b[i]
}
}
func feSub(dst, a, b *fieldElement) {
for i := range dst {
dst[i] = a[i] - b[i]
}
}
func feCopy(dst, src *fieldElement) {
for i := range dst {
dst[i] = src[i]
}
}
// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0.
//
// Preconditions: b in {0,1}.
func feCSwap(f, g *fieldElement, b int32) {
var x fieldElement
b = -b
for i := range x {
x[i] = b & (f[i] ^ g[i])
}
for i := range f {
f[i] ^= x[i]
}
for i := range g {
g[i] ^= x[i]
}
}
// load3 reads a 24-bit, little-endian value from in.
func load3(in []byte) int64 {
var r int64
r = int64(in[0])
r |= int64(in[1]) << 8
r |= int64(in[2]) << 16
return r
}
// load4 reads a 32-bit, little-endian value from in.
func load4(in []byte) int64 {
var r int64
r = int64(in[0])
r |= int64(in[1]) << 8
r |= int64(in[2]) << 16
r |= int64(in[3]) << 24
return r
}
func feFromBytes(dst *fieldElement, src *[32]byte) {
h0 := load4(src[:])
h1 := load3(src[4:]) << 6
h2 := load3(src[7:]) << 5
h3 := load3(src[10:]) << 3
h4 := load3(src[13:]) << 2
h5 := load4(src[16:])
h6 := load3(src[20:]) << 7
h7 := load3(src[23:]) << 5
h8 := load3(src[26:]) << 4
h9 := load3(src[29:]) << 2
var carry [10]int64
carry[9] = (h9 + 1<<24) >> 25
h0 += carry[9] * 19
h9 -= carry[9] << 25
carry[1] = (h1 + 1<<24) >> 25
h2 += carry[1]
h1 -= carry[1] << 25
carry[3] = (h3 + 1<<24) >> 25
h4 += carry[3]
h3 -= carry[3] << 25
carry[5] = (h5 + 1<<24) >> 25
h6 += carry[5]
h5 -= carry[5] << 25
carry[7] = (h7 + 1<<24) >> 25
h8 += carry[7]
h7 -= carry[7] << 25
carry[0] = (h0 + 1<<25) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
carry[2] = (h2 + 1<<25) >> 26
h3 += carry[2]
h2 -= carry[2] << 26
carry[4] = (h4 + 1<<25) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[6] = (h6 + 1<<25) >> 26
h7 += carry[6]
h6 -= carry[6] << 26
carry[8] = (h8 + 1<<25) >> 26
h9 += carry[8]
h8 -= carry[8] << 26
dst[0] = int32(h0)
dst[1] = int32(h1)
dst[2] = int32(h2)
dst[3] = int32(h3)
dst[4] = int32(h4)
dst[5] = int32(h5)
dst[6] = int32(h6)
dst[7] = int32(h7)
dst[8] = int32(h8)
dst[9] = int32(h9)
}
// feToBytes marshals h to s.
// Preconditions:
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
//
// Write p=2^255-19; q=floor(h/p).
// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
//
// Proof:
// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.
// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4.
//
// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).
// Then 0<y<1.
//
// Write r=h-pq.
// Have 0<=r<=p-1=2^255-20.
// Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1.
//
// Write x=r+19(2^-255)r+y.
// Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
//
// Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))
// so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
func feToBytes(s *[32]byte, h *fieldElement) {
var carry [10]int32
q := (19*h[9] + (1 << 24)) >> 25
q = (h[0] + q) >> 26
q = (h[1] + q) >> 25
q = (h[2] + q) >> 26
q = (h[3] + q) >> 25
q = (h[4] + q) >> 26
q = (h[5] + q) >> 25
q = (h[6] + q) >> 26
q = (h[7] + q) >> 25
q = (h[8] + q) >> 26
q = (h[9] + q) >> 25
// Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20.
h[0] += 19 * q
// Goal: Output h-2^255 q, which is between 0 and 2^255-20.
carry[0] = h[0] >> 26
h[1] += carry[0]
h[0] -= carry[0] << 26
carry[1] = h[1] >> 25
h[2] += carry[1]
h[1] -= carry[1] << 25
carry[2] = h[2] >> 26
h[3] += carry[2]
h[2] -= carry[2] << 26
carry[3] = h[3] >> 25
h[4] += carry[3]
h[3] -= carry[3] << 25
carry[4] = h[4] >> 26
h[5] += carry[4]
h[4] -= carry[4] << 26
carry[5] = h[5] >> 25
h[6] += carry[5]
h[5] -= carry[5] << 25
carry[6] = h[6] >> 26
h[7] += carry[6]
h[6] -= carry[6] << 26
carry[7] = h[7] >> 25
h[8] += carry[7]
h[7] -= carry[7] << 25
carry[8] = h[8] >> 26
h[9] += carry[8]
h[8] -= carry[8] << 26
carry[9] = h[9] >> 25
h[9] -= carry[9] << 25
// h10 = carry9
// Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
// Have h[0]+...+2^230 h[9] between 0 and 2^255-1;
// evidently 2^255 h10-2^255 q = 0.
// Goal: Output h[0]+...+2^230 h[9].
s[0] = byte(h[0] >> 0)
s[1] = byte(h[0] >> 8)
s[2] = byte(h[0] >> 16)
s[3] = byte((h[0] >> 24) | (h[1] << 2))
s[4] = byte(h[1] >> 6)
s[5] = byte(h[1] >> 14)
s[6] = byte((h[1] >> 22) | (h[2] << 3))
s[7] = byte(h[2] >> 5)
s[8] = byte(h[2] >> 13)
s[9] = byte((h[2] >> 21) | (h[3] << 5))
s[10] = byte(h[3] >> 3)
s[11] = byte(h[3] >> 11)
s[12] = byte((h[3] >> 19) | (h[4] << 6))
s[13] = byte(h[4] >> 2)
s[14] = byte(h[4] >> 10)
s[15] = byte(h[4] >> 18)
s[16] = byte(h[5] >> 0)
s[17] = byte(h[5] >> 8)
s[18] = byte(h[5] >> 16)
s[19] = byte((h[5] >> 24) | (h[6] << 1))
s[20] = byte(h[6] >> 7)
s[21] = byte(h[6] >> 15)
s[22] = byte((h[6] >> 23) | (h[7] << 3))
s[23] = byte(h[7] >> 5)
s[24] = byte(h[7] >> 13)
s[25] = byte((h[7] >> 21) | (h[8] << 4))
s[26] = byte(h[8] >> 4)
s[27] = byte(h[8] >> 12)
s[28] = byte((h[8] >> 20) | (h[9] << 6))
s[29] = byte(h[9] >> 2)
s[30] = byte(h[9] >> 10)
s[31] = byte(h[9] >> 18)
}
// feMul calculates h = f * g
// Can overlap h with f or g.
//
// Preconditions:
// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
//
// Postconditions:
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
//
// Notes on implementation strategy:
//
// Using schoolbook multiplication.
// Karatsuba would save a little in some cost models.
//
// Most multiplications by 2 and 19 are 32-bit precomputations;
// cheaper than 64-bit postcomputations.
//
// There is one remaining multiplication by 19 in the carry chain;
// one *19 precomputation can be merged into this,
// but the resulting data flow is considerably less clean.
//
// There are 12 carries below.
// 10 of them are 2-way parallelizable and vectorizable.
// Can get away with 11 carries, but then data flow is much deeper.
//
// With tighter constraints on inputs can squeeze carries into int32.
func feMul(h, f, g *fieldElement) {
f0 := f[0]
f1 := f[1]
f2 := f[2]
f3 := f[3]
f4 := f[4]
f5 := f[5]
f6 := f[6]
f7 := f[7]
f8 := f[8]
f9 := f[9]
g0 := g[0]
g1 := g[1]
g2 := g[2]
g3 := g[3]
g4 := g[4]
g5 := g[5]
g6 := g[6]
g7 := g[7]
g8 := g[8]
g9 := g[9]
g1_19 := 19 * g1 // 1.4*2^29
g2_19 := 19 * g2 // 1.4*2^30; still ok
g3_19 := 19 * g3
g4_19 := 19 * g4
g5_19 := 19 * g5
g6_19 := 19 * g6
g7_19 := 19 * g7
g8_19 := 19 * g8
g9_19 := 19 * g9
f1_2 := 2 * f1
f3_2 := 2 * f3
f5_2 := 2 * f5
f7_2 := 2 * f7
f9_2 := 2 * f9
f0g0 := int64(f0) * int64(g0)
f0g1 := int64(f0) * int64(g1)
f0g2 := int64(f0) * int64(g2)
f0g3 := int64(f0) * int64(g3)
f0g4 := int64(f0) * int64(g4)
f0g5 := int64(f0) * int64(g5)
f0g6 := int64(f0) * int64(g6)
f0g7 := int64(f0) * int64(g7)
f0g8 := int64(f0) * int64(g8)
f0g9 := int64(f0) * int64(g9)
f1g0 := int64(f1) * int64(g0)
f1g1_2 := int64(f1_2) * int64(g1)
f1g2 := int64(f1) * int64(g2)
f1g3_2 := int64(f1_2) * int64(g3)
f1g4 := int64(f1) * int64(g4)
f1g5_2 := int64(f1_2) * int64(g5)
f1g6 := int64(f1) * int64(g6)
f1g7_2 := int64(f1_2) * int64(g7)
f1g8 := int64(f1) * int64(g8)
f1g9_38 := int64(f1_2) * int64(g9_19)
f2g0 := int64(f2) * int64(g0)
f2g1 := int64(f2) * int64(g1)
f2g2 := int64(f2) * int64(g2)
f2g3 := int64(f2) * int64(g3)
f2g4 := int64(f2) * int64(g4)
f2g5 := int64(f2) * int64(g5)
f2g6 := int64(f2) * int64(g6)
f2g7 := int64(f2) * int64(g7)
f2g8_19 := int64(f2) * int64(g8_19)
f2g9_19 := int64(f2) * int64(g9_19)
f3g0 := int64(f3) * int64(g0)
f3g1_2 := int64(f3_2) * int64(g1)
f3g2 := int64(f3) * int64(g2)
f3g3_2 := int64(f3_2) * int64(g3)
f3g4 := int64(f3) * int64(g4)
f3g5_2 := int64(f3_2) * int64(g5)
f3g6 := int64(f3) * int64(g6)
f3g7_38 := int64(f3_2) * int64(g7_19)
f3g8_19 := int64(f3) * int64(g8_19)
f3g9_38 := int64(f3_2) * int64(g9_19)
f4g0 := int64(f4) * int64(g0)
f4g1 := int64(f4) * int64(g1)
f4g2 := int64(f4) * int64(g2)
f4g3 := int64(f4) * int64(g3)
f4g4 := int64(f4) * int64(g4)
f4g5 := int64(f4) * int64(g5)
f4g6_19 := int64(f4) * int64(g6_19)
f4g7_19 := int64(f4) * int64(g7_19)
f4g8_19 := int64(f4) * int64(g8_19)
f4g9_19 := int64(f4) * int64(g9_19)
f5g0 := int64(f5) * int64(g0)
f5g1_2 := int64(f5_2) * int64(g1)
f5g2 := int64(f5) * int64(g2)
f5g3_2 := int64(f5_2) * int64(g3)
f5g4 := int64(f5) * int64(g4)
f5g5_38 := int64(f5_2) * int64(g5_19)
f5g6_19 := int64(f5) * int64(g6_19)
f5g7_38 := int64(f5_2) * int64(g7_19)
f5g8_19 := int64(f5) * int64(g8_19)
f5g9_38 := int64(f5_2) * int64(g9_19)
f6g0 := int64(f6) * int64(g0)
f6g1 := int64(f6) * int64(g1)
f6g2 := int64(f6) * int64(g2)
f6g3 := int64(f6) * int64(g3)
f6g4_19 := int64(f6) * int64(g4_19)
f6g5_19 := int64(f6) * int64(g5_19)
f6g6_19 := int64(f6) * int64(g6_19)
f6g7_19 := int64(f6) * int64(g7_19)
f6g8_19 := int64(f6) * int64(g8_19)
f6g9_19 := int64(f6) * int64(g9_19)
f7g0 := int64(f7) * int64(g0)
f7g1_2 := int64(f7_2) * int64(g1)
f7g2 := int64(f7) * int64(g2)
f7g3_38 := int64(f7_2) * int64(g3_19)
f7g4_19 := int64(f7) * int64(g4_19)
f7g5_38 := int64(f7_2) * int64(g5_19)
f7g6_19 := int64(f7) * int64(g6_19)
f7g7_38 := int64(f7_2) * int64(g7_19)
f7g8_19 := int64(f7) * int64(g8_19)
f7g9_38 := int64(f7_2) * int64(g9_19)
f8g0 := int64(f8) * int64(g0)
f8g1 := int64(f8) * int64(g1)
f8g2_19 := int64(f8) * int64(g2_19)
f8g3_19 := int64(f8) * int64(g3_19)
f8g4_19 := int64(f8) * int64(g4_19)
f8g5_19 := int64(f8) * int64(g5_19)
f8g6_19 := int64(f8) * int64(g6_19)
f8g7_19 := int64(f8) * int64(g7_19)
f8g8_19 := int64(f8) * int64(g8_19)
f8g9_19 := int64(f8) * int64(g9_19)
f9g0 := int64(f9) * int64(g0)
f9g1_38 := int64(f9_2) * int64(g1_19)
f9g2_19 := int64(f9) * int64(g2_19)
f9g3_38 := int64(f9_2) * int64(g3_19)
f9g4_19 := int64(f9) * int64(g4_19)
f9g5_38 := int64(f9_2) * int64(g5_19)
f9g6_19 := int64(f9) * int64(g6_19)
f9g7_38 := int64(f9_2) * int64(g7_19)
f9g8_19 := int64(f9) * int64(g8_19)
f9g9_38 := int64(f9_2) * int64(g9_19)
h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38
h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19
h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38
h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19
h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38
h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19
h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38
h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19
h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38
h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0
var carry [10]int64
// |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38))
// i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8
// |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19))
// i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
// |h0| <= 2^25
// |h4| <= 2^25
// |h1| <= 1.51*2^58
// |h5| <= 1.51*2^58
carry[1] = (h1 + (1 << 24)) >> 25
h2 += carry[1]
h1 -= carry[1] << 25
carry[5] = (h5 + (1 << 24)) >> 25
h6 += carry[5]
h5 -= carry[5] << 25
// |h1| <= 2^24; from now on fits into int32
// |h5| <= 2^24; from now on fits into int32
// |h2| <= 1.21*2^59
// |h6| <= 1.21*2^59
carry[2] = (h2 + (1 << 25)) >> 26
h3 += carry[2]
h2 -= carry[2] << 26
carry[6] = (h6 + (1 << 25)) >> 26
h7 += carry[6]
h6 -= carry[6] << 26
// |h2| <= 2^25; from now on fits into int32 unchanged
// |h6| <= 2^25; from now on fits into int32 unchanged
// |h3| <= 1.51*2^58
// |h7| <= 1.51*2^58
carry[3] = (h3 + (1 << 24)) >> 25
h4 += carry[3]
h3 -= carry[3] << 25
carry[7] = (h7 + (1 << 24)) >> 25
h8 += carry[7]
h7 -= carry[7] << 25
// |h3| <= 2^24; from now on fits into int32 unchanged
// |h7| <= 2^24; from now on fits into int32 unchanged
// |h4| <= 1.52*2^33
// |h8| <= 1.52*2^33
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[8] = (h8 + (1 << 25)) >> 26
h9 += carry[8]
h8 -= carry[8] << 26
// |h4| <= 2^25; from now on fits into int32 unchanged
// |h8| <= 2^25; from now on fits into int32 unchanged
// |h5| <= 1.01*2^24
// |h9| <= 1.51*2^58
carry[9] = (h9 + (1 << 24)) >> 25
h0 += carry[9] * 19
h9 -= carry[9] << 25
// |h9| <= 2^24; from now on fits into int32 unchanged
// |h0| <= 1.8*2^37
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
// |h0| <= 2^25; from now on fits into int32 unchanged
// |h1| <= 1.01*2^24
h[0] = int32(h0)
h[1] = int32(h1)
h[2] = int32(h2)
h[3] = int32(h3)
h[4] = int32(h4)
h[5] = int32(h5)
h[6] = int32(h6)
h[7] = int32(h7)
h[8] = int32(h8)
h[9] = int32(h9)
}
// feSquare calculates h = f*f. Can overlap h with f.
//
// Preconditions:
// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
//
// Postconditions:
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
func feSquare(h, f *fieldElement) {
f0 := f[0]
f1 := f[1]
f2 := f[2]
f3 := f[3]
f4 := f[4]
f5 := f[5]
f6 := f[6]
f7 := f[7]
f8 := f[8]
f9 := f[9]
f0_2 := 2 * f0
f1_2 := 2 * f1
f2_2 := 2 * f2
f3_2 := 2 * f3
f4_2 := 2 * f4
f5_2 := 2 * f5
f6_2 := 2 * f6
f7_2 := 2 * f7
f5_38 := 38 * f5 // 1.31*2^30
f6_19 := 19 * f6 // 1.31*2^30
f7_38 := 38 * f7 // 1.31*2^30
f8_19 := 19 * f8 // 1.31*2^30
f9_38 := 38 * f9 // 1.31*2^30
f0f0 := int64(f0) * int64(f0)
f0f1_2 := int64(f0_2) * int64(f1)
f0f2_2 := int64(f0_2) * int64(f2)
f0f3_2 := int64(f0_2) * int64(f3)
f0f4_2 := int64(f0_2) * int64(f4)
f0f5_2 := int64(f0_2) * int64(f5)
f0f6_2 := int64(f0_2) * int64(f6)
f0f7_2 := int64(f0_2) * int64(f7)
f0f8_2 := int64(f0_2) * int64(f8)
f0f9_2 := int64(f0_2) * int64(f9)
f1f1_2 := int64(f1_2) * int64(f1)
f1f2_2 := int64(f1_2) * int64(f2)
f1f3_4 := int64(f1_2) * int64(f3_2)
f1f4_2 := int64(f1_2) * int64(f4)
f1f5_4 := int64(f1_2) * int64(f5_2)
f1f6_2 := int64(f1_2) * int64(f6)
f1f7_4 := int64(f1_2) * int64(f7_2)
f1f8_2 := int64(f1_2) * int64(f8)
f1f9_76 := int64(f1_2) * int64(f9_38)
f2f2 := int64(f2) * int64(f2)
f2f3_2 := int64(f2_2) * int64(f3)
f2f4_2 := int64(f2_2) * int64(f4)
f2f5_2 := int64(f2_2) * int64(f5)
f2f6_2 := int64(f2_2) * int64(f6)
f2f7_2 := int64(f2_2) * int64(f7)
f2f8_38 := int64(f2_2) * int64(f8_19)
f2f9_38 := int64(f2) * int64(f9_38)
f3f3_2 := int64(f3_2) * int64(f3)
f3f4_2 := int64(f3_2) * int64(f4)
f3f5_4 := int64(f3_2) * int64(f5_2)
f3f6_2 := int64(f3_2) * int64(f6)
f3f7_76 := int64(f3_2) * int64(f7_38)
f3f8_38 := int64(f3_2) * int64(f8_19)
f3f9_76 := int64(f3_2) * int64(f9_38)
f4f4 := int64(f4) * int64(f4)
f4f5_2 := int64(f4_2) * int64(f5)
f4f6_38 := int64(f4_2) * int64(f6_19)
f4f7_38 := int64(f4) * int64(f7_38)
f4f8_38 := int64(f4_2) * int64(f8_19)
f4f9_38 := int64(f4) * int64(f9_38)
f5f5_38 := int64(f5) * int64(f5_38)
f5f6_38 := int64(f5_2) * int64(f6_19)
f5f7_76 := int64(f5_2) * int64(f7_38)
f5f8_38 := int64(f5_2) * int64(f8_19)
f5f9_76 := int64(f5_2) * int64(f9_38)
f6f6_19 := int64(f6) * int64(f6_19)
f6f7_38 := int64(f6) * int64(f7_38)
f6f8_38 := int64(f6_2) * int64(f8_19)
f6f9_38 := int64(f6) * int64(f9_38)
f7f7_38 := int64(f7) * int64(f7_38)
f7f8_38 := int64(f7_2) * int64(f8_19)
f7f9_76 := int64(f7_2) * int64(f9_38)
f8f8_19 := int64(f8) * int64(f8_19)
f8f9_38 := int64(f8) * int64(f9_38)
f9f9_38 := int64(f9) * int64(f9_38)
h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38
h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38
h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19
h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38
h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38
h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38
h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19
h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38
h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38
h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2
var carry [10]int64
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[1] = (h1 + (1 << 24)) >> 25
h2 += carry[1]
h1 -= carry[1] << 25
carry[5] = (h5 + (1 << 24)) >> 25
h6 += carry[5]
h5 -= carry[5] << 25
carry[2] = (h2 + (1 << 25)) >> 26
h3 += carry[2]
h2 -= carry[2] << 26
carry[6] = (h6 + (1 << 25)) >> 26
h7 += carry[6]
h6 -= carry[6] << 26
carry[3] = (h3 + (1 << 24)) >> 25
h4 += carry[3]
h3 -= carry[3] << 25
carry[7] = (h7 + (1 << 24)) >> 25
h8 += carry[7]
h7 -= carry[7] << 25
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[8] = (h8 + (1 << 25)) >> 26
h9 += carry[8]
h8 -= carry[8] << 26
carry[9] = (h9 + (1 << 24)) >> 25
h0 += carry[9] * 19
h9 -= carry[9] << 25
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
h[0] = int32(h0)
h[1] = int32(h1)
h[2] = int32(h2)
h[3] = int32(h3)
h[4] = int32(h4)
h[5] = int32(h5)
h[6] = int32(h6)
h[7] = int32(h7)
h[8] = int32(h8)
h[9] = int32(h9)
}
// feMul121666 calculates h = f * 121666. Can overlap h with f.
//
// Preconditions:
// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
//
// Postconditions:
// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
func feMul121666(h, f *fieldElement) {
h0 := int64(f[0]) * 121666
h1 := int64(f[1]) * 121666
h2 := int64(f[2]) * 121666
h3 := int64(f[3]) * 121666
h4 := int64(f[4]) * 121666
h5 := int64(f[5]) * 121666
h6 := int64(f[6]) * 121666
h7 := int64(f[7]) * 121666
h8 := int64(f[8]) * 121666
h9 := int64(f[9]) * 121666
var carry [10]int64
carry[9] = (h9 + (1 << 24)) >> 25
h0 += carry[9] * 19
h9 -= carry[9] << 25
carry[1] = (h1 + (1 << 24)) >> 25
h2 += carry[1]
h1 -= carry[1] << 25
carry[3] = (h3 + (1 << 24)) >> 25
h4 += carry[3]
h3 -= carry[3] << 25
carry[5] = (h5 + (1 << 24)) >> 25
h6 += carry[5]
h5 -= carry[5] << 25
carry[7] = (h7 + (1 << 24)) >> 25
h8 += carry[7]
h7 -= carry[7] << 25
carry[0] = (h0 + (1 << 25)) >> 26
h1 += carry[0]
h0 -= carry[0] << 26
carry[2] = (h2 + (1 << 25)) >> 26
h3 += carry[2]
h2 -= carry[2] << 26
carry[4] = (h4 + (1 << 25)) >> 26
h5 += carry[4]
h4 -= carry[4] << 26
carry[6] = (h6 + (1 << 25)) >> 26
h7 += carry[6]
h6 -= carry[6] << 26
carry[8] = (h8 + (1 << 25)) >> 26
h9 += carry[8]
h8 -= carry[8] << 26
h[0] = int32(h0)
h[1] = int32(h1)
h[2] = int32(h2)
h[3] = int32(h3)
h[4] = int32(h4)
h[5] = int32(h5)
h[6] = int32(h6)
h[7] = int32(h7)
h[8] = int32(h8)
h[9] = int32(h9)
}
// feInvert sets out = z^-1.
func feInvert(out, z *fieldElement) {
var t0, t1, t2, t3 fieldElement
var i int
feSquare(&t0, z)
for i = 1; i < 1; i++ {
feSquare(&t0, &t0)
}
feSquare(&t1, &t0)
for i = 1; i < 2; i++ {
feSquare(&t1, &t1)
}
feMul(&t1, z, &t1)
feMul(&t0, &t0, &t1)
feSquare(&t2, &t0)
for i = 1; i < 1; i++ {
feSquare(&t2, &t2)
}
feMul(&t1, &t1, &t2)
feSquare(&t2, &t1)
for i = 1; i < 5; i++ {
feSquare(&t2, &t2)
}
feMul(&t1, &t2, &t1)
feSquare(&t2, &t1)
for i = 1; i < 10; i++ {
feSquare(&t2, &t2)
}
feMul(&t2, &t2, &t1)
feSquare(&t3, &t2)
for i = 1; i < 20; i++ {
feSquare(&t3, &t3)
}
feMul(&t2, &t3, &t2)
feSquare(&t2, &t2)
for i = 1; i < 10; i++ {
feSquare(&t2, &t2)
}
feMul(&t1, &t2, &t1)
feSquare(&t2, &t1)
for i = 1; i < 50; i++ {
feSquare(&t2, &t2)
}
feMul(&t2, &t2, &t1)
feSquare(&t3, &t2)
for i = 1; i < 100; i++ {
feSquare(&t3, &t3)
}
feMul(&t2, &t3, &t2)
feSquare(&t2, &t2)
for i = 1; i < 50; i++ {
feSquare(&t2, &t2)
}
feMul(&t1, &t2, &t1)
feSquare(&t1, &t1)
for i = 1; i < 5; i++ {
feSquare(&t1, &t1)
}
feMul(out, &t1, &t0)
}
func scalarMult(out, in, base *[32]byte) {
var e [32]byte
copy(e[:], in[:])
e[0] &= 248
e[31] &= 127
e[31] |= 64
var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement
feFromBytes(&x1, base)
feOne(&x2)
feCopy(&x3, &x1)
feOne(&z3)
swap := int32(0)
for pos := 254; pos >= 0; pos-- {
b := e[pos/8] >> uint(pos&7)
b &= 1
swap ^= int32(b)
feCSwap(&x2, &x3, swap)
feCSwap(&z2, &z3, swap)
swap = int32(b)
feSub(&tmp0, &x3, &z3)
feSub(&tmp1, &x2, &z2)
feAdd(&x2, &x2, &z2)
feAdd(&z2, &x3, &z3)
feMul(&z3, &tmp0, &x2)
feMul(&z2, &z2, &tmp1)
feSquare(&tmp0, &tmp1)
feSquare(&tmp1, &x2)
feAdd(&x3, &z3, &z2)
feSub(&z2, &z3, &z2)
feMul(&x2, &tmp1, &tmp0)
feSub(&tmp1, &tmp1, &tmp0)
feSquare(&z2, &z2)
feMul121666(&z3, &tmp1)
feSquare(&x3, &x3)
feAdd(&tmp0, &tmp0, &z3)
feMul(&z3, &x1, &z2)
feMul(&z2, &tmp1, &tmp0)
}
feCSwap(&x2, &x3, swap)
feCSwap(&z2, &z3, swap)
feInvert(&z2, &z2)
feMul(&x2, &x2, &z2)
feToBytes(out, &x2)
}

View File

@ -4,7 +4,7 @@
// Package curve25519 provides an implementation of scalar multiplication on // Package curve25519 provides an implementation of scalar multiplication on
// the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html // the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html
package curve25519 package curve25519 // import "golang.org/x/crypto/curve25519"
// basePoint is the x coordinate of the generator of the curve. // basePoint is the x coordinate of the generator of the curve.
var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

View File

@ -8,22 +8,9 @@
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
// func freeze(inout *[5]uint64) // func freeze(inout *[5]uint64)
TEXT ·freeze(SB),7,$96-8 TEXT ·freeze(SB),7,$0-8
MOVQ inout+0(FP), DI MOVQ inout+0(FP), DI
MOVQ SP,R11
MOVQ $31,CX
NOTQ CX
ANDQ CX,SP
ADDQ $32,SP
MOVQ R11,0(SP)
MOVQ R12,8(SP)
MOVQ R13,16(SP)
MOVQ R14,24(SP)
MOVQ R15,32(SP)
MOVQ BX,40(SP)
MOVQ BP,48(SP)
MOVQ 0(DI),SI MOVQ 0(DI),SI
MOVQ 8(DI),DX MOVQ 8(DI),DX
MOVQ 16(DI),CX MOVQ 16(DI),CX
@ -81,14 +68,4 @@ REDUCELOOP:
MOVQ CX,16(DI) MOVQ CX,16(DI)
MOVQ R8,24(DI) MOVQ R8,24(DI)
MOVQ R9,32(DI) MOVQ R9,32(DI)
MOVQ 0(SP),R11
MOVQ 8(SP),R12
MOVQ 16(SP),R13
MOVQ 24(SP),R14
MOVQ 32(SP),R15
MOVQ 40(SP),BX
MOVQ 48(SP),BP
MOVQ R11,SP
MOVQ DI,AX
MOVQ SI,DX
RET RET

File diff suppressed because it is too large Load Diff

View File

@ -8,35 +8,21 @@
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
// func mul(dest, a, b *[5]uint64) // func mul(dest, a, b *[5]uint64)
TEXT ·mul(SB),0,$128-24 TEXT ·mul(SB),0,$16-24
MOVQ dest+0(FP), DI MOVQ dest+0(FP), DI
MOVQ a+8(FP), SI MOVQ a+8(FP), SI
MOVQ b+16(FP), DX MOVQ b+16(FP), DX
MOVQ SP,R11
MOVQ $31,CX
NOTQ CX
ANDQ CX,SP
ADDQ $32,SP
MOVQ R11,0(SP)
MOVQ R12,8(SP)
MOVQ R13,16(SP)
MOVQ R14,24(SP)
MOVQ R15,32(SP)
MOVQ BX,40(SP)
MOVQ BP,48(SP)
MOVQ DI,56(SP)
MOVQ DX,CX MOVQ DX,CX
MOVQ 24(SI),DX MOVQ 24(SI),DX
IMUL3Q $19,DX,AX IMUL3Q $19,DX,AX
MOVQ AX,64(SP) MOVQ AX,0(SP)
MULQ 16(CX) MULQ 16(CX)
MOVQ AX,R8 MOVQ AX,R8
MOVQ DX,R9 MOVQ DX,R9
MOVQ 32(SI),DX MOVQ 32(SI),DX
IMUL3Q $19,DX,AX IMUL3Q $19,DX,AX
MOVQ AX,72(SP) MOVQ AX,8(SP)
MULQ 8(CX) MULQ 8(CX)
ADDQ AX,R8 ADDQ AX,R8
ADCQ DX,R9 ADCQ DX,R9
@ -111,11 +97,11 @@ TEXT ·mul(SB),0,$128-24
MULQ 8(CX) MULQ 8(CX)
ADDQ AX,BX ADDQ AX,BX
ADCQ DX,BP ADCQ DX,BP
MOVQ 64(SP),AX MOVQ 0(SP),AX
MULQ 24(CX) MULQ 24(CX)
ADDQ AX,R10 ADDQ AX,R10
ADCQ DX,R11 ADCQ DX,R11
MOVQ 64(SP),AX MOVQ 0(SP),AX
MULQ 32(CX) MULQ 32(CX)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
@ -123,15 +109,15 @@ TEXT ·mul(SB),0,$128-24
MULQ 0(CX) MULQ 0(CX)
ADDQ AX,BX ADDQ AX,BX
ADCQ DX,BP ADCQ DX,BP
MOVQ 72(SP),AX MOVQ 8(SP),AX
MULQ 16(CX) MULQ 16(CX)
ADDQ AX,R10 ADDQ AX,R10
ADCQ DX,R11 ADCQ DX,R11
MOVQ 72(SP),AX MOVQ 8(SP),AX
MULQ 24(CX) MULQ 24(CX)
ADDQ AX,R12 ADDQ AX,R12
ADCQ DX,R13 ADCQ DX,R13
MOVQ 72(SP),AX MOVQ 8(SP),AX
MULQ 32(CX) MULQ 32(CX)
ADDQ AX,R14 ADDQ AX,R14
ADCQ DX,R15 ADCQ DX,R15
@ -178,14 +164,4 @@ TEXT ·mul(SB),0,$128-24
MOVQ R9,16(DI) MOVQ R9,16(DI)
MOVQ AX,24(DI) MOVQ AX,24(DI)
MOVQ R10,32(DI) MOVQ R10,32(DI)
MOVQ 0(SP),R11
MOVQ 8(SP),R12
MOVQ 16(SP),R13
MOVQ 24(SP),R14
MOVQ 32(SP),R15
MOVQ 40(SP),BX
MOVQ 48(SP),BP
MOVQ R11,SP
MOVQ DI,AX
MOVQ SI,DX
RET RET

View File

@ -8,23 +8,10 @@
// +build amd64,!gccgo,!appengine // +build amd64,!gccgo,!appengine
// func square(out, in *[5]uint64) // func square(out, in *[5]uint64)
TEXT ·square(SB),7,$96-16 TEXT ·square(SB),7,$0-16
MOVQ out+0(FP), DI MOVQ out+0(FP), DI
MOVQ in+8(FP), SI MOVQ in+8(FP), SI
MOVQ SP,R11
MOVQ $31,CX
NOTQ CX
ANDQ CX,SP
ADDQ $32, SP
MOVQ R11,0(SP)
MOVQ R12,8(SP)
MOVQ R13,16(SP)
MOVQ R14,24(SP)
MOVQ R15,32(SP)
MOVQ BX,40(SP)
MOVQ BP,48(SP)
MOVQ 0(SI),AX MOVQ 0(SI),AX
MULQ 0(SI) MULQ 0(SI)
MOVQ AX,CX MOVQ AX,CX
@ -140,14 +127,4 @@ TEXT ·square(SB),7,$96-16
MOVQ R9,16(DI) MOVQ R9,16(DI)
MOVQ AX,24(DI) MOVQ AX,24(DI)
MOVQ R10,32(DI) MOVQ R10,32(DI)
MOVQ 0(SP),R11
MOVQ 8(SP),R12
MOVQ 16(SP),R13
MOVQ 24(SP),R14
MOVQ 32(SP),R15
MOVQ 40(SP),BX
MOVQ 48(SP),BP
MOVQ R11,SP
MOVQ DI,AX
MOVQ SI,DX
RET RET

181
vendor/golang.org/x/crypto/ed25519/ed25519.go generated vendored Normal file
View File

@ -0,0 +1,181 @@
// 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 ed25519 implements the Ed25519 signature algorithm. See
// http://ed25519.cr.yp.to/.
//
// These functions are also compatible with the “Ed25519” function defined in
// https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05.
package ed25519
// This code is a port of the public domain, “ref10” implementation of ed25519
// from SUPERCOP.
import (
"crypto"
cryptorand "crypto/rand"
"crypto/sha512"
"crypto/subtle"
"errors"
"io"
"strconv"
"golang.org/x/crypto/ed25519/internal/edwards25519"
)
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
)
// PublicKey is the type of Ed25519 public keys.
type PublicKey []byte
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
type PrivateKey []byte
// Public returns the PublicKey corresponding to priv.
func (priv PrivateKey) Public() crypto.PublicKey {
publicKey := make([]byte, PublicKeySize)
copy(publicKey, priv[32:])
return PublicKey(publicKey)
}
// Sign signs the given message with priv.
// Ed25519 performs two passes over messages to be signed and therefore cannot
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
// indicate the message hasn't been hashed. This can be achieved by passing
// crypto.Hash(0) as the value for opts.
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("ed25519: cannot sign hashed message")
}
return Sign(priv, message), nil
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) {
if rand == nil {
rand = cryptorand.Reader
}
privateKey = make([]byte, PrivateKeySize)
publicKey = make([]byte, PublicKeySize)
_, err = io.ReadFull(rand, privateKey[:32])
if err != nil {
return nil, nil, err
}
digest := sha512.Sum512(privateKey[:32])
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], digest[:])
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
copy(privateKey[32:], publicKeyBytes[:])
copy(publicKey, publicKeyBytes[:])
return publicKey, privateKey, nil
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
if l := len(privateKey); l != PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
h := sha512.New()
h.Write(privateKey[:32])
var digest1, messageDigest, hramDigest [64]byte
var expandedSecretKey [32]byte
h.Sum(digest1[:0])
copy(expandedSecretKey[:], digest1[:])
expandedSecretKey[0] &= 248
expandedSecretKey[31] &= 63
expandedSecretKey[31] |= 64
h.Reset()
h.Write(digest1[32:])
h.Write(message)
h.Sum(messageDigest[:0])
var messageDigestReduced [32]byte
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
var R edwards25519.ExtendedGroupElement
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
var encodedR [32]byte
R.ToBytes(&encodedR)
h.Reset()
h.Write(encodedR[:])
h.Write(privateKey[32:])
h.Write(message)
h.Sum(hramDigest[:0])
var hramDigestReduced [32]byte
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
var s [32]byte
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
signature := make([]byte, SignatureSize)
copy(signature[:], encodedR[:])
copy(signature[32:], s[:])
return signature
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
if l := len(publicKey); l != PublicKeySize {
panic("ed25519: bad public key length: " + strconv.Itoa(l))
}
if len(sig) != SignatureSize || sig[63]&224 != 0 {
return false
}
var A edwards25519.ExtendedGroupElement
var publicKeyBytes [32]byte
copy(publicKeyBytes[:], publicKey)
if !A.FromBytes(&publicKeyBytes) {
return false
}
edwards25519.FeNeg(&A.X, &A.X)
edwards25519.FeNeg(&A.T, &A.T)
h := sha512.New()
h.Write(sig[:32])
h.Write(publicKey[:])
h.Write(message)
var digest [64]byte
h.Sum(digest[:0])
var hReduced [32]byte
edwards25519.ScReduce(&hReduced, &digest)
var R edwards25519.ProjectiveGroupElement
var b [32]byte
copy(b[:], sig[32:])
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
var checkR [32]byte
R.ToBytes(&checkR)
return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
/* // Package agent implements the ssh-agent protocol, and provides both
Package agent implements a client to an ssh-agent daemon. // a client and a server. The client can talk to a standard ssh-agent
// that uses UNIX sockets, and one could implement an alternative
References: // ssh-agent process using the sample server.
[PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD //
*/ // References:
package agent // [PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD
package agent // import "golang.org/x/crypto/ssh/agent"
import ( import (
"bytes" "bytes"
@ -24,6 +25,7 @@ import (
"math/big" "math/big"
"sync" "sync"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
@ -76,6 +78,7 @@ type AddedKey struct {
// See [PROTOCOL.agent], section 3. // See [PROTOCOL.agent], section 3.
const ( const (
agentRequestV1Identities = 1 agentRequestV1Identities = 1
agentRemoveAllV1Identities = 9
// 3.2 Requests from client to agent for protocol 2 key operations // 3.2 Requests from client to agent for protocol 2 key operations
agentAddIdentity = 17 agentAddIdentity = 17
@ -182,10 +185,13 @@ func (k *Key) Marshal() []byte {
return k.Blob return k.Blob
} }
// Verify satisfies the ssh.PublicKey interface, but is not // Verify satisfies the ssh.PublicKey interface.
// implemented for agent keys.
func (k *Key) Verify(data []byte, sig *ssh.Signature) error { func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
return errors.New("agent: agent key does not know how to verify") pubKey, err := ssh.ParsePublicKey(k.Blob)
if err != nil {
return fmt.Errorf("agent: bad public key: %v", err)
}
return pubKey.Verify(data, sig)
} }
type wireKey struct { type wireKey struct {
@ -375,6 +381,8 @@ func unmarshal(packet []byte) (interface{}, error) {
msg = new(identitiesAnswerAgentMsg) msg = new(identitiesAnswerAgentMsg)
case agentSignResponse: case agentSignResponse:
msg = new(signResponseAgentMsg) msg = new(signResponseAgentMsg)
case agentV1IdentitiesAnswer:
msg = new(agentV1IdentityMsg)
default: default:
return nil, fmt.Errorf("agent: unknown type tag %d", packet[0]) return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
} }
@ -385,7 +393,7 @@ func unmarshal(packet []byte) (interface{}, error) {
} }
type rsaKeyMsg struct { type rsaKeyMsg struct {
Type string `sshtype:"17"` Type string `sshtype:"17|25"`
N *big.Int N *big.Int
E *big.Int E *big.Int
D *big.Int D *big.Int
@ -397,7 +405,7 @@ type rsaKeyMsg struct {
} }
type dsaKeyMsg struct { type dsaKeyMsg struct {
Type string `sshtype:"17"` Type string `sshtype:"17|25"`
P *big.Int P *big.Int
Q *big.Int Q *big.Int
G *big.Int G *big.Int
@ -408,7 +416,7 @@ type dsaKeyMsg struct {
} }
type ecdsaKeyMsg struct { type ecdsaKeyMsg struct {
Type string `sshtype:"17"` Type string `sshtype:"17|25"`
Curve string Curve string
KeyBytes []byte KeyBytes []byte
D *big.Int D *big.Int
@ -416,6 +424,14 @@ type ecdsaKeyMsg struct {
Constraints []byte `ssh:"rest"` Constraints []byte `ssh:"rest"`
} }
type ed25519KeyMsg struct {
Type string `sshtype:"17|25"`
Pub []byte
Priv []byte
Comments string
Constraints []byte `ssh:"rest"`
}
// Insert adds a private key to the agent. // Insert adds a private key to the agent.
func (c *client) insertKey(s interface{}, comment string, constraints []byte) error { func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
var req []byte var req []byte
@ -457,6 +473,14 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er
Comments: comment, Comments: comment,
Constraints: constraints, Constraints: constraints,
}) })
case *ed25519.PrivateKey:
req = ssh.Marshal(ed25519KeyMsg{
Type: ssh.KeyAlgoED25519,
Pub: []byte(*k)[32:],
Priv: []byte(*k),
Comments: comment,
Constraints: constraints,
})
default: default:
return fmt.Errorf("agent: unsupported key type %T", s) return fmt.Errorf("agent: unsupported key type %T", s)
} }
@ -477,7 +501,7 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er
} }
type rsaCertMsg struct { type rsaCertMsg struct {
Type string `sshtype:"17"` Type string `sshtype:"17|25"`
CertBytes []byte CertBytes []byte
D *big.Int D *big.Int
Iqmp *big.Int // IQMP = Inverse Q Mod P Iqmp *big.Int // IQMP = Inverse Q Mod P
@ -488,7 +512,7 @@ type rsaCertMsg struct {
} }
type dsaCertMsg struct { type dsaCertMsg struct {
Type string `sshtype:"17"` Type string `sshtype:"17|25"`
CertBytes []byte CertBytes []byte
X *big.Int X *big.Int
Comments string Comments string
@ -496,14 +520,23 @@ type dsaCertMsg struct {
} }
type ecdsaCertMsg struct { type ecdsaCertMsg struct {
Type string `sshtype:"17"` Type string `sshtype:"17|25"`
CertBytes []byte CertBytes []byte
D *big.Int D *big.Int
Comments string Comments string
Constraints []byte `ssh:"rest"` Constraints []byte `ssh:"rest"`
} }
// Insert adds a private key to the agent. If a certificate is given, type ed25519CertMsg struct {
Type string `sshtype:"17|25"`
CertBytes []byte
Pub []byte
Priv []byte
Comments string
Constraints []byte `ssh:"rest"`
}
// Add adds a private key to the agent. If a certificate is given,
// that certificate is added instead as public key. // that certificate is added instead as public key.
func (c *client) Add(key AddedKey) error { func (c *client) Add(key AddedKey) error {
var constraints []byte var constraints []byte
@ -551,6 +584,7 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string
CertBytes: cert.Marshal(), CertBytes: cert.Marshal(),
X: k.X, X: k.X,
Comments: comment, Comments: comment,
Constraints: constraints,
}) })
case *ecdsa.PrivateKey: case *ecdsa.PrivateKey:
req = ssh.Marshal(ecdsaCertMsg{ req = ssh.Marshal(ecdsaCertMsg{
@ -558,6 +592,16 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string
CertBytes: cert.Marshal(), CertBytes: cert.Marshal(),
D: k.D, D: k.D,
Comments: comment, Comments: comment,
Constraints: constraints,
})
case *ed25519.PrivateKey:
req = ssh.Marshal(ed25519CertMsg{
Type: cert.Type(),
CertBytes: cert.Marshal(),
Pub: []byte(*k)[32:],
Priv: []byte(*k),
Comments: comment,
Constraints: constraints,
}) })
default: default:
return fmt.Errorf("agent: unsupported key type %T", s) return fmt.Errorf("agent: unsupported key type %T", s)

View File

@ -11,6 +11,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"sync" "sync"
"time"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
@ -18,6 +19,7 @@ import (
type privKey struct { type privKey struct {
signer ssh.Signer signer ssh.Signer
comment string comment string
expire *time.Time
} }
type keyring struct { type keyring struct {
@ -48,15 +50,9 @@ func (r *keyring) RemoveAll() error {
return nil return nil
} }
// Remove removes all identities with the given public key. // removeLocked does the actual key removal. The caller must already be holding the
func (r *keyring) Remove(key ssh.PublicKey) error { // keyring mutex.
r.mu.Lock() func (r *keyring) removeLocked(want []byte) error {
defer r.mu.Unlock()
if r.locked {
return errLocked
}
want := key.Marshal()
found := false found := false
for i := 0; i < len(r.keys); { for i := 0; i < len(r.keys); {
if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) { if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) {
@ -75,7 +71,18 @@ func (r *keyring) Remove(key ssh.PublicKey) error {
return nil return nil
} }
// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. // Remove removes all identities with the given public key.
func (r *keyring) Remove(key ssh.PublicKey) error {
r.mu.Lock()
defer r.mu.Unlock()
if r.locked {
return errLocked
}
return r.removeLocked(key.Marshal())
}
// Lock locks the agent. Sign and Remove will fail, and List will return an empty list.
func (r *keyring) Lock(passphrase []byte) error { func (r *keyring) Lock(passphrase []byte) error {
r.mu.Lock() r.mu.Lock()
defer r.mu.Unlock() defer r.mu.Unlock()
@ -104,6 +111,17 @@ func (r *keyring) Unlock(passphrase []byte) error {
return nil return nil
} }
// expireKeysLocked removes expired keys from the keyring. If a key was added
// with a lifetimesecs contraint and seconds >= lifetimesecs seconds have
// ellapsed, it is removed. The caller *must* be holding the keyring mutex.
func (r *keyring) expireKeysLocked() {
for _, k := range r.keys {
if k.expire != nil && time.Now().After(*k.expire) {
r.removeLocked(k.signer.PublicKey().Marshal())
}
}
}
// List returns the identities known to the agent. // List returns the identities known to the agent.
func (r *keyring) List() ([]*Key, error) { func (r *keyring) List() ([]*Key, error) {
r.mu.Lock() r.mu.Lock()
@ -113,6 +131,7 @@ func (r *keyring) List() ([]*Key, error) {
return nil, nil return nil, nil
} }
r.expireKeysLocked()
var ids []*Key var ids []*Key
for _, k := range r.keys { for _, k := range r.keys {
pub := k.signer.PublicKey() pub := k.signer.PublicKey()
@ -146,7 +165,17 @@ func (r *keyring) Add(key AddedKey) error {
} }
} }
r.keys = append(r.keys, privKey{signer, key.Comment}) p := privKey{
signer: signer,
comment: key.Comment,
}
if key.LifetimeSecs > 0 {
t := time.Now().Add(time.Duration(key.LifetimeSecs) * time.Second)
p.expire = &t
}
r.keys = append(r.keys, p)
return nil return nil
} }
@ -159,6 +188,7 @@ func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
return nil, errLocked return nil, errLocked
} }
r.expireKeysLocked()
wanted := key.Marshal() wanted := key.Marshal()
for _, k := range r.keys { for _, k := range r.keys {
if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) { if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) {
@ -176,6 +206,7 @@ func (r *keyring) Signers() ([]ssh.Signer, error) {
return nil, errLocked return nil, errLocked
} }
r.expireKeysLocked()
s := make([]ssh.Signer, 0, len(r.keys)) s := make([]ssh.Signer, 0, len(r.keys))
for _, k := range r.keys { for _, k := range r.keys {
s = append(s, k.signer) s = append(s, k.signer)

View File

@ -5,13 +5,18 @@
package agent package agent
import ( import (
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io" "io"
"log" "log"
"math/big" "math/big"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
@ -49,6 +54,9 @@ func marshalKey(k *Key) []byte {
return ssh.Marshal(&record) return ssh.Marshal(&record)
} }
// See [PROTOCOL.agent], section 2.5.1.
const agentV1IdentitiesAnswer = 2
type agentV1IdentityMsg struct { type agentV1IdentityMsg struct {
Numkeys uint32 `sshtype:"2"` Numkeys uint32 `sshtype:"2"`
} }
@ -69,6 +77,10 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
switch data[0] { switch data[0] {
case agentRequestV1Identities: case agentRequestV1Identities:
return &agentV1IdentityMsg{0}, nil return &agentV1IdentityMsg{0}, nil
case agentRemoveAllV1Identities:
return nil, nil
case agentRemoveIdentity: case agentRemoveIdentity:
var req agentRemoveIdentityMsg var req agentRemoveIdentityMsg
if err := ssh.Unmarshal(data, &req); err != nil { if err := ssh.Unmarshal(data, &req); err != nil {
@ -121,6 +133,7 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
return nil, err return nil, err
} }
return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil
case agentRequestIdentities: case agentRequestIdentities:
keys, err := s.agent.List() keys, err := s.agent.List()
if err != nil { if err != nil {
@ -134,30 +147,23 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
rep.Keys = append(rep.Keys, marshalKey(k)...) rep.Keys = append(rep.Keys, marshalKey(k)...)
} }
return rep, nil return rep, nil
case 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 (s *server) insertIdentity(req []byte) error { func parseRSAKey(req []byte) (*AddedKey, error) {
var record struct {
Type string `sshtype:"17"`
Rest []byte `ssh:"rest"`
}
if err := ssh.Unmarshal(req, &record); err != nil {
return err
}
switch record.Type {
case ssh.KeyAlgoRSA:
var k rsaKeyMsg var k rsaKeyMsg
if err := ssh.Unmarshal(req, &k); err != nil { if err := ssh.Unmarshal(req, &k); err != nil {
return err return nil, err
} }
if k.E.BitLen() > 30 {
priv := rsa.PrivateKey{ return nil, errors.New("agent: RSA public exponent too large")
}
priv := &rsa.PrivateKey{
PublicKey: rsa.PublicKey{ PublicKey: rsa.PublicKey{
E: int(k.E.Int64()), E: int(k.E.Int64()),
N: k.N, N: k.N,
@ -167,9 +173,245 @@ func (s *server) insertIdentity(req []byte) error {
} }
priv.Precompute() priv.Precompute()
return s.agent.Add(AddedKey{PrivateKey: &priv, Comment: k.Comments}) return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
} }
return fmt.Errorf("not implemented: %s", record.Type)
func parseEd25519Key(req []byte) (*AddedKey, error) {
var k ed25519KeyMsg
if err := ssh.Unmarshal(req, &k); err != nil {
return nil, err
}
priv := ed25519.PrivateKey(k.Priv)
return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil
}
func parseDSAKey(req []byte) (*AddedKey, error) {
var k dsaKeyMsg
if err := ssh.Unmarshal(req, &k); err != nil {
return nil, err
}
priv := &dsa.PrivateKey{
PublicKey: dsa.PublicKey{
Parameters: dsa.Parameters{
P: k.P,
Q: k.Q,
G: k.G,
},
Y: k.Y,
},
X: k.X,
}
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
}
func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) {
priv = &ecdsa.PrivateKey{
D: privScalar,
}
switch curveName {
case "nistp256":
priv.Curve = elliptic.P256()
case "nistp384":
priv.Curve = elliptic.P384()
case "nistp521":
priv.Curve = elliptic.P521()
default:
return nil, fmt.Errorf("agent: unknown curve %q", curveName)
}
priv.X, priv.Y = elliptic.Unmarshal(priv.Curve, keyBytes)
if priv.X == nil || priv.Y == nil {
return nil, errors.New("agent: point not on curve")
}
return priv, nil
}
func parseEd25519Cert(req []byte) (*AddedKey, error) {
var k ed25519CertMsg
if err := ssh.Unmarshal(req, &k); err != nil {
return nil, err
}
pubKey, err := ssh.ParsePublicKey(k.CertBytes)
if err != nil {
return nil, err
}
priv := ed25519.PrivateKey(k.Priv)
cert, ok := pubKey.(*ssh.Certificate)
if !ok {
return nil, errors.New("agent: bad ED25519 certificate")
}
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
}
func parseECDSAKey(req []byte) (*AddedKey, error) {
var k ecdsaKeyMsg
if err := ssh.Unmarshal(req, &k); err != nil {
return nil, err
}
priv, err := unmarshalECDSA(k.Curve, k.KeyBytes, k.D)
if err != nil {
return nil, err
}
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
}
func parseRSACert(req []byte) (*AddedKey, error) {
var k rsaCertMsg
if err := ssh.Unmarshal(req, &k); err != nil {
return nil, err
}
pubKey, err := ssh.ParsePublicKey(k.CertBytes)
if err != nil {
return nil, err
}
cert, ok := pubKey.(*ssh.Certificate)
if !ok {
return nil, errors.New("agent: bad RSA certificate")
}
// An RSA publickey as marshaled by rsaPublicKey.Marshal() in keys.go
var rsaPub struct {
Name string
E *big.Int
N *big.Int
}
if err := ssh.Unmarshal(cert.Key.Marshal(), &rsaPub); err != nil {
return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err)
}
if rsaPub.E.BitLen() > 30 {
return nil, errors.New("agent: RSA public exponent too large")
}
priv := rsa.PrivateKey{
PublicKey: rsa.PublicKey{
E: int(rsaPub.E.Int64()),
N: rsaPub.N,
},
D: k.D,
Primes: []*big.Int{k.Q, k.P},
}
priv.Precompute()
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
}
func parseDSACert(req []byte) (*AddedKey, error) {
var k dsaCertMsg
if err := ssh.Unmarshal(req, &k); err != nil {
return nil, err
}
pubKey, err := ssh.ParsePublicKey(k.CertBytes)
if err != nil {
return nil, err
}
cert, ok := pubKey.(*ssh.Certificate)
if !ok {
return nil, errors.New("agent: bad DSA certificate")
}
// A DSA publickey as marshaled by dsaPublicKey.Marshal() in keys.go
var w struct {
Name string
P, Q, G, Y *big.Int
}
if err := ssh.Unmarshal(cert.Key.Marshal(), &w); err != nil {
return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err)
}
priv := &dsa.PrivateKey{
PublicKey: dsa.PublicKey{
Parameters: dsa.Parameters{
P: w.P,
Q: w.Q,
G: w.G,
},
Y: w.Y,
},
X: k.X,
}
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
}
func parseECDSACert(req []byte) (*AddedKey, error) {
var k ecdsaCertMsg
if err := ssh.Unmarshal(req, &k); err != nil {
return nil, err
}
pubKey, err := ssh.ParsePublicKey(k.CertBytes)
if err != nil {
return nil, err
}
cert, ok := pubKey.(*ssh.Certificate)
if !ok {
return nil, errors.New("agent: bad ECDSA certificate")
}
// An ECDSA publickey as marshaled by ecdsaPublicKey.Marshal() in keys.go
var ecdsaPub struct {
Name string
ID string
Key []byte
}
if err := ssh.Unmarshal(cert.Key.Marshal(), &ecdsaPub); err != nil {
return nil, err
}
priv, err := unmarshalECDSA(ecdsaPub.ID, ecdsaPub.Key, k.D)
if err != nil {
return nil, err
}
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
}
func (s *server) insertIdentity(req []byte) error {
var record struct {
Type string `sshtype:"17|25"`
Rest []byte `ssh:"rest"`
}
if err := ssh.Unmarshal(req, &record); err != nil {
return err
}
var addedKey *AddedKey
var err error
switch record.Type {
case ssh.KeyAlgoRSA:
addedKey, err = parseRSAKey(req)
case ssh.KeyAlgoDSA:
addedKey, err = parseDSAKey(req)
case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521:
addedKey, err = parseECDSAKey(req)
case ssh.KeyAlgoED25519:
addedKey, err = parseEd25519Key(req)
case ssh.CertAlgoRSAv01:
addedKey, err = parseRSACert(req)
case ssh.CertAlgoDSAv01:
addedKey, err = parseDSACert(req)
case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01:
addedKey, err = parseECDSACert(req)
case ssh.CertAlgoED25519v01:
addedKey, err = parseEd25519Cert(req)
default:
return fmt.Errorf("agent: not implemented: %q", record.Type)
}
if err != nil {
return err
}
return s.agent.Add(*addedKey)
} }
// ServeAgent serves the agent protocol on the given connection. It // ServeAgent serves the agent protocol on the given connection. It

View File

@ -22,6 +22,7 @@ const (
CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com" CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com" CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com" CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
) )
// Certificate types distinguish between host and user // Certificate types distinguish between host and user
@ -401,6 +402,7 @@ var certAlgoNames = map[string]string{
KeyAlgoECDSA256: CertAlgoECDSA256v01, KeyAlgoECDSA256: CertAlgoECDSA256v01,
KeyAlgoECDSA384: CertAlgoECDSA384v01, KeyAlgoECDSA384: CertAlgoECDSA384v01,
KeyAlgoECDSA521: CertAlgoECDSA521v01, KeyAlgoECDSA521: CertAlgoECDSA521v01,
KeyAlgoED25519: CertAlgoED25519v01,
} }
// certToPrivAlgo returns the underlying algorithm for a certificate algorithm. // certToPrivAlgo returns the underlying algorithm for a certificate algorithm.
@ -459,7 +461,7 @@ func (c *Certificate) Marshal() []byte {
func (c *Certificate) Type() string { func (c *Certificate) Type() string {
algo, ok := certAlgoNames[c.Key.Type()] algo, ok := certAlgoNames[c.Key.Type()]
if !ok { if !ok {
panic("unknown cert key type") panic("unknown cert key type " + c.Key.Type())
} }
return algo return algo
} }

View File

@ -67,6 +67,8 @@ type Channel interface {
// boolean, otherwise the return value will be false. Channel // boolean, otherwise the return value will be false. Channel
// requests are out-of-band messages so they may be sent even // requests are out-of-band messages so they may be sent even
// if the data stream is closed or blocked by flow control. // if the data stream is closed or blocked by flow control.
// If the channel is closed before a reply is returned, io.EOF
// is returned.
SendRequest(name string, wantReply bool, payload []byte) (bool, error) SendRequest(name string, wantReply bool, payload []byte) (bool, error)
// Stderr returns an io.ReadWriter that writes to this channel // Stderr returns an io.ReadWriter that writes to this channel
@ -217,7 +219,7 @@ func (c *channel) writePacket(packet []byte) error {
func (c *channel) sendMessage(msg interface{}) error { func (c *channel) sendMessage(msg interface{}) error {
if debugMux { if debugMux {
log.Printf("send %d: %#v", c.mux.chanList.offset, msg) log.Printf("send(%d): %#v", c.mux.chanList.offset, msg)
} }
p := Marshal(msg) p := Marshal(msg)
@ -371,7 +373,7 @@ func (c *channel) close() {
close(c.msg) close(c.msg)
close(c.incomingRequests) close(c.incomingRequests)
c.writeMu.Lock() c.writeMu.Lock()
// This is not necesary for a normal channel teardown, but if // This is not necessary for a normal channel teardown, but if
// there was another error, it is. // there was another error, it is.
c.sentClose = true c.sentClose = true
c.writeMu.Unlock() c.writeMu.Unlock()

View File

@ -7,6 +7,7 @@ package ssh
import ( import (
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/des"
"crypto/rc4" "crypto/rc4"
"crypto/subtle" "crypto/subtle"
"encoding/binary" "encoding/binary"
@ -121,6 +122,9 @@ var cipherModes = map[string]*streamCipherMode{
// 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, 0, nil},
// 3des-cbc is insecure and is disabled by default.
tripledescbcID: {24, des.BlockSize, 0, nil},
} }
// 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
@ -368,12 +372,7 @@ type cbcCipher struct {
oracleCamouflage uint32 oracleCamouflage uint32
} }
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) { func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
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),
@ -387,6 +386,34 @@ func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCi
return cbc, nil return cbc, nil
} }
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
cbc, err := newCBCCipher(c, iv, key, macKey, algs)
if err != nil {
return nil, err
}
return cbc, nil
}
func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
c, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
cbc, err := newCBCCipher(c, iv, key, macKey, algs)
if err != nil {
return nil, err
}
return cbc, nil
}
func maxUInt32(a, b int) uint32 { func maxUInt32(a, b int) uint32 {
if a > b { if a > b {
return uint32(a) return uint32(a)

View File

@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"net" "net"
"sync" "sync"
"time"
) )
// Client implements a traditional SSH client that supports shells, // Client implements a traditional SSH client that supports shells,
@ -96,16 +97,10 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e
c.transport = newClientTransport( c.transport = newClientTransport(
newTransport(c.sshConn.conn, config.Rand, true /* is client */), newTransport(c.sshConn.conn, config.Rand, true /* is client */),
c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr()) c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr())
if err := c.transport.requestKeyChange(); err != nil { if err := c.transport.requestInitialKeyChange(); err != nil {
return err return err
} }
if packet, err := c.transport.readPacket(); err != nil {
return err
} else if packet[0] != msgNewKeys {
return unexpectedMessageError(msgNewKeys, packet[0])
}
// We just did the key change, so the session ID is established. // We just did the key change, so the session ID is established.
c.sessionID = c.transport.getSessionID() c.sessionID = c.transport.getSessionID()
@ -169,7 +164,7 @@ func (c *Client) handleChannelOpens(in <-chan NewChannel) {
// to incoming channels and requests, use net.Dial with NewClientConn // to incoming channels and requests, use net.Dial with NewClientConn
// instead. // instead.
func Dial(network, addr string, config *ClientConfig) (*Client, error) { func Dial(network, addr string, config *ClientConfig) (*Client, error) {
conn, err := net.Dial(network, addr) conn, err := net.DialTimeout(network, addr, config.Timeout)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -210,4 +205,9 @@ type ClientConfig struct {
// string returned from PublicKey.Type method may be used, or // string returned from PublicKey.Type method may be used, or
// any of the CertAlgoXxxx and KeyAlgoXxxx constants. // any of the CertAlgoXxxx and KeyAlgoXxxx constants.
HostKeyAlgorithms []string HostKeyAlgorithms []string
// Timeout is the maximum amount of time for the TCP connection to establish.
//
// A Timeout of zero means no timeout.
Timeout time.Duration
} }

View File

@ -321,8 +321,6 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
return false, msg.Methods, nil return false, msg.Methods, nil
case msgUserAuthSuccess: case msgUserAuthSuccess:
return true, nil, nil return true, nil, nil
case msgDisconnect:
return false, nil, io.EOF
default: default:
return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0]) return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
} }
@ -439,3 +437,37 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
} }
} }
} }
type retryableAuthMethod struct {
authMethod AuthMethod
maxTries int
}
func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) {
for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
ok, methods, err = r.authMethod.auth(session, user, c, rand)
if ok || err != nil { // either success or error terminate
return ok, methods, err
}
}
return ok, methods, err
}
func (r *retryableAuthMethod) method() string {
return r.authMethod.method()
}
// RetryableAuthMethod is a decorator for other auth methods enabling them to
// be retried up to maxTries before considering that AuthMethod itself failed.
// If maxTries is <= 0, will retry indefinitely
//
// This is useful for interactive clients using challenge/response type
// authentication (e.g. Keyboard-Interactive, Password, etc) where the user
// could mistype their response resulting in the server issuing a
// SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
// [keyboard-interactive]); Without this decorator, the non-retryable
// AuthMethod would be removed from future consideration, and never tried again
// (and so the user would never be able to retry their entry).
func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
}

View File

@ -44,10 +44,12 @@ var supportedKexAlgos = []string{
// of authenticating servers) in preference order. // of authenticating servers) in preference order.
var supportedHostKeyAlgos = []string{ var supportedHostKeyAlgos = []string{
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
KeyAlgoRSA, KeyAlgoDSA, KeyAlgoRSA, KeyAlgoDSA,
KeyAlgoED25519,
} }
// supportedMACs specifies a default set of MAC algorithms in preference order. // supportedMACs specifies a default set of MAC algorithms in preference order.

View File

@ -23,7 +23,6 @@ func (e *OpenChannelError) Error() string {
// ConnMetadata holds metadata for the connection. // ConnMetadata holds metadata for the connection.
type ConnMetadata interface { type ConnMetadata interface {
// User returns the user ID for this connection. // User returns the user ID for this connection.
// It is empty if no authentication is used.
User() string User() string
// SessionID returns the sesson hash, also denoted by H. // SessionID returns the sesson hash, also denoted by H.

View File

@ -15,4 +15,4 @@ References:
[PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD [PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
[SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1 [SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
*/ */
package ssh package ssh // import "golang.org/x/crypto/ssh"

View File

@ -29,25 +29,6 @@ type keyingTransport interface {
// direction will be effected if a msgNewKeys message is sent // direction will be effected if a msgNewKeys message is sent
// or received. // or received.
prepareKeyChange(*algorithms, *kexResult) error prepareKeyChange(*algorithms, *kexResult) error
// getSessionID returns the session ID. prepareKeyChange must
// have been called once.
getSessionID() []byte
}
// rekeyingTransport is the interface of handshakeTransport that we
// (internally) expose to ClientConn and ServerConn.
type rekeyingTransport interface {
packetConn
// requestKeyChange asks the remote side to change keys. All
// writes are blocked until the key change succeeds, which is
// signaled by reading a msgNewKeys.
requestKeyChange() error
// getSessionID returns the session ID. This is only valid
// after the first key change has completed.
getSessionID() []byte
} }
// handshakeTransport implements rekeying on top of a keyingTransport // handshakeTransport implements rekeying on top of a keyingTransport
@ -86,6 +67,9 @@ type handshakeTransport struct {
sentInitMsg *kexInitMsg sentInitMsg *kexInitMsg
writtenSinceKex uint64 writtenSinceKex uint64
writeError error writeError error
// The session ID or nil if first kex did not complete yet.
sessionID []byte
} }
func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport { func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport {
@ -122,7 +106,7 @@ func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt
} }
func (t *handshakeTransport) getSessionID() []byte { func (t *handshakeTransport) getSessionID() []byte {
return t.conn.getSessionID() return t.sessionID
} }
func (t *handshakeTransport) id() string { func (t *handshakeTransport) id() string {
@ -177,15 +161,22 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
t.readSinceKex += uint64(len(p)) t.readSinceKex += uint64(len(p))
if debugHandshake { if debugHandshake {
if p[0] == msgChannelData || p[0] == msgChannelExtendedData {
log.Printf("%s got data (packet %d bytes)", t.id(), len(p))
} else {
msg, err := decode(p) msg, err := decode(p)
log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err) log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err)
} }
}
if p[0] != msgKexInit { if p[0] != msgKexInit {
return p, nil return p, nil
} }
err = t.enterKeyExchange(p)
t.mu.Lock() t.mu.Lock()
firstKex := t.sessionID == nil
err = t.enterKeyExchangeLocked(p)
if err != nil { if err != nil {
// drop connection // drop connection
t.conn.Close() t.conn.Close()
@ -193,7 +184,7 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
} }
if debugHandshake { if debugHandshake {
log.Printf("%s exited key exchange, err %v", t.id(), err) log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err)
} }
// Unblock writers. // Unblock writers.
@ -208,28 +199,69 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
} }
t.readSinceKex = 0 t.readSinceKex = 0
return []byte{msgNewKeys}, nil
// By default, a key exchange is hidden from higher layers by
// translating it into msgIgnore.
successPacket := []byte{msgIgnore}
if firstKex {
// sendKexInit() for the first kex waits for
// msgNewKeys so the authentication process is
// guaranteed to happen over an encrypted transport.
successPacket = []byte{msgNewKeys}
} }
return successPacket, nil
}
// keyChangeCategory describes whether a key exchange is the first on a
// connection, or a subsequent one.
type keyChangeCategory bool
const (
firstKeyExchange keyChangeCategory = true
subsequentKeyExchange keyChangeCategory = false
)
// sendKexInit sends a key change message, and returns the message // sendKexInit sends a key change message, and returns the message
// that was sent. After initiating the key change, all writes will be // that was sent. After initiating the key change, all writes will be
// blocked until the change is done, and a failed key change will // blocked until the change is done, and a failed key change will
// close the underlying transport. This function is safe for // close the underlying transport. This function is safe for
// concurrent use by multiple goroutines. // concurrent use by multiple goroutines.
func (t *handshakeTransport) sendKexInit() (*kexInitMsg, []byte, error) { func (t *handshakeTransport) sendKexInit(isFirst keyChangeCategory) error {
var err error
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() // If this is the initial key change, but we already have a sessionID,
return t.sendKexInitLocked() // then do nothing because the key exchange has already completed
// asynchronously.
if !isFirst || t.sessionID == nil {
_, _, err = t.sendKexInitLocked(isFirst)
}
t.mu.Unlock()
if err != nil {
return err
}
if isFirst {
if packet, err := t.readPacket(); err != nil {
return err
} else if packet[0] != msgNewKeys {
return unexpectedMessageError(msgNewKeys, packet[0])
}
}
return nil
}
func (t *handshakeTransport) requestInitialKeyChange() error {
return t.sendKexInit(firstKeyExchange)
} }
func (t *handshakeTransport) requestKeyChange() error { func (t *handshakeTransport) requestKeyChange() error {
_, _, err := t.sendKexInit() return t.sendKexInit(subsequentKeyExchange)
return err
} }
// sendKexInitLocked sends a key change message. t.mu must be locked // sendKexInitLocked sends a key change message. t.mu must be locked
// while this happens. // while this happens.
func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) { func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexInitMsg, []byte, error) {
// kexInits may be sent either in response to the other side, // kexInits may be sent either in response to the other side,
// or because our side wants to initiate a key change, so we // or because our side wants to initiate a key change, so we
// may have already sent a kexInit. In that case, don't send a // may have already sent a kexInit. In that case, don't send a
@ -237,6 +269,7 @@ func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) {
if t.sentInitMsg != nil { if t.sentInitMsg != nil {
return t.sentInitMsg, t.sentInitPacket, nil return t.sentInitMsg, t.sentInitPacket, nil
} }
msg := &kexInitMsg{ msg := &kexInitMsg{
KexAlgos: t.config.KeyExchanges, KexAlgos: t.config.KeyExchanges,
CiphersClientServer: t.config.Ciphers, CiphersClientServer: t.config.Ciphers,
@ -276,7 +309,7 @@ func (t *handshakeTransport) writePacket(p []byte) error {
defer t.mu.Unlock() defer t.mu.Unlock()
if t.writtenSinceKex > t.config.RekeyThreshold { if t.writtenSinceKex > t.config.RekeyThreshold {
t.sendKexInitLocked() t.sendKexInitLocked(subsequentKeyExchange)
} }
for t.sentInitMsg != nil && t.writeError == nil { for t.sentInitMsg != nil && t.writeError == nil {
t.cond.Wait() t.cond.Wait()
@ -300,12 +333,12 @@ func (t *handshakeTransport) Close() error {
return t.conn.Close() return t.conn.Close()
} }
// enterKeyExchange runs the key exchange. // enterKeyExchange runs the key exchange. t.mu must be held while running this.
func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) error {
if debugHandshake { if debugHandshake {
log.Printf("%s entered key exchange", t.id()) log.Printf("%s entered key exchange", t.id())
} }
myInit, myInitPacket, err := t.sendKexInit() myInit, myInitPacket, err := t.sendKexInitLocked(subsequentKeyExchange)
if err != nil { if err != nil {
return err return err
} }
@ -338,7 +371,16 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
} }
// We don't send FirstKexFollows, but we handle receiving it. // We don't send FirstKexFollows, but we handle receiving it.
if otherInit.FirstKexFollows && algs.kex != otherInit.KexAlgos[0] { //
// RFC 4253 section 7 defines the kex and the agreement method for
// first_kex_packet_follows. It states that the guessed packet
// should be ignored if the "kex algorithm and/or the host
// key algorithm is guessed wrong (server and client have
// different preferred algorithm), or if any of the other
// algorithms cannot be agreed upon". The other algorithms have
// already been checked above so the kex algorithm and host key
// algorithm are checked here.
if otherInit.FirstKexFollows && (clientInit.KexAlgos[0] != serverInit.KexAlgos[0] || clientInit.ServerHostKeyAlgos[0] != serverInit.ServerHostKeyAlgos[0]) {
// other side sent a kex message for the wrong algorithm, // other side sent a kex message for the wrong algorithm,
// which we have to ignore. // which we have to ignore.
if _, err := t.conn.readPacket(); err != nil { if _, err := t.conn.readPacket(); err != nil {
@ -362,6 +404,11 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
return err return err
} }
if t.sessionID == nil {
t.sessionID = result.H
}
result.SessionID = t.sessionID
t.conn.prepareKeyChange(algs, result) t.conn.prepareKeyChange(algs, result)
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
return err return err
@ -371,6 +418,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
} else if packet[0] != msgNewKeys { } else if packet[0] != msgNewKeys {
return unexpectedMessageError(msgNewKeys, packet[0]) return unexpectedMessageError(msgNewKeys, packet[0])
} }
return nil return nil
} }

View File

@ -46,7 +46,7 @@ type kexResult struct {
Hash crypto.Hash Hash crypto.Hash
// The session ID, which is the first H computed. This is used // The session ID, which is the first H computed. This is used
// to signal data inside transport. // to derive key material inside the transport.
SessionID []byte SessionID []byte
} }
@ -77,11 +77,11 @@ type kexAlgorithm interface {
// dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement.
type dhGroup struct { type dhGroup struct {
g, p *big.Int g, p, pMinus1 *big.Int
} }
func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) {
if theirPublic.Sign() <= 0 || theirPublic.Cmp(group.p) >= 0 { if theirPublic.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 {
return nil, errors.New("ssh: DH parameter out of bounds") return nil, errors.New("ssh: DH parameter out of bounds")
} }
return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil
@ -90,10 +90,17 @@ func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int,
func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) { func (group *dhGroup) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
hashFunc := crypto.SHA1 hashFunc := crypto.SHA1
x, err := rand.Int(randSource, group.p) var x *big.Int
if err != nil { for {
var err error
if x, err = rand.Int(randSource, group.pMinus1); err != nil {
return nil, err return nil, err
} }
if x.Sign() > 0 {
break
}
}
X := new(big.Int).Exp(group.g, x, group.p) X := new(big.Int).Exp(group.g, x, group.p)
kexDHInit := kexDHInitMsg{ kexDHInit := kexDHInitMsg{
X: X, X: X,
@ -146,10 +153,15 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha
return return
} }
y, err := rand.Int(randSource, group.p) var y *big.Int
if err != nil { for {
if y, err = rand.Int(randSource, group.pMinus1); err != nil {
return return
} }
if y.Sign() > 0 {
break
}
}
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) kInt, err := group.diffieHellman(kexDHInit.X, y)
@ -373,6 +385,7 @@ func init() {
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),
} }
// This is the group called diffie-hellman-group14-sha1 in RFC // This is the group called diffie-hellman-group14-sha1 in RFC
@ -382,6 +395,7 @@ func init() {
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),
} }
kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()} kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}

View File

@ -20,6 +20,8 @@ import (
"io" "io"
"math/big" "math/big"
"strings" "strings"
"golang.org/x/crypto/ed25519"
) )
// These constants represent the algorithm names for key types supported by this // These constants represent the algorithm names for key types supported by this
@ -30,6 +32,7 @@ const (
KeyAlgoECDSA256 = "ecdsa-sha2-nistp256" KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
KeyAlgoECDSA384 = "ecdsa-sha2-nistp384" KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521" KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
KeyAlgoED25519 = "ssh-ed25519"
) )
// parsePubKey parses a public key of the given algorithm. // parsePubKey parses a public key of the given algorithm.
@ -42,14 +45,16 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
return parseDSA(in) return parseDSA(in)
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
return parseECDSA(in) return parseECDSA(in)
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: case KeyAlgoED25519:
return parseED25519(in)
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
cert, err := parseCert(in, certToPrivAlgo(algo)) cert, err := parseCert(in, certToPrivAlgo(algo))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
return cert, nil, nil return cert, nil, nil
} }
return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", err) return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo)
} }
// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format // parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
@ -120,7 +125,7 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey
continue continue
} }
// Strip out the begining of the known_host key. // Strip out the beginning of the known_host key.
// This is either an optional marker or a (set of) hostname(s). // This is either an optional marker or a (set of) hostname(s).
keyFields := bytes.Fields(in) keyFields := bytes.Fields(in)
if len(keyFields) < 3 || len(keyFields) > 5 { if len(keyFields) < 3 || len(keyFields) > 5 {
@ -276,6 +281,12 @@ type PublicKey interface {
Verify(data []byte, sig *Signature) error Verify(data []byte, sig *Signature) error
} }
// CryptoPublicKey, if implemented by a PublicKey,
// returns the underlying crypto.PublicKey form of the key.
type CryptoPublicKey interface {
CryptoPublicKey() crypto.PublicKey
}
// A Signer can create signatures that verify against a public key. // A Signer can create signatures that verify against a public key.
type Signer interface { type Signer interface {
// PublicKey returns an associated PublicKey instance. // PublicKey returns an associated PublicKey instance.
@ -319,6 +330,8 @@ func parseRSA(in []byte) (out PublicKey, rest []byte, err error) {
func (r *rsaPublicKey) Marshal() []byte { func (r *rsaPublicKey) Marshal() []byte {
e := new(big.Int).SetInt64(int64(r.E)) e := new(big.Int).SetInt64(int64(r.E))
// RSA publickey struct layout should match the struct used by
// parseRSACert in the x/crypto/ssh/agent package.
wirekey := struct { wirekey := struct {
Name string Name string
E *big.Int E *big.Int
@ -341,6 +354,10 @@ func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob) return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob)
} }
func (r *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
return (*rsa.PublicKey)(r)
}
type dsaPublicKey dsa.PublicKey type dsaPublicKey dsa.PublicKey
func (r *dsaPublicKey) Type() string { func (r *dsaPublicKey) Type() string {
@ -369,6 +386,8 @@ func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
} }
func (k *dsaPublicKey) Marshal() []byte { func (k *dsaPublicKey) Marshal() []byte {
// DSA publickey struct layout should match the struct used by
// parseDSACert in the x/crypto/ssh/agent package.
w := struct { w := struct {
Name string Name string
P, Q, G, Y *big.Int P, Q, G, Y *big.Int
@ -407,6 +426,10 @@ func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error {
return errors.New("ssh: signature did not verify") return errors.New("ssh: signature did not verify")
} }
func (k *dsaPublicKey) CryptoPublicKey() crypto.PublicKey {
return (*dsa.PublicKey)(k)
}
type dsaPrivateKey struct { type dsaPrivateKey struct {
*dsa.PrivateKey *dsa.PrivateKey
} }
@ -455,6 +478,55 @@ func (key *ecdsaPublicKey) nistID() string {
panic("ssh: unsupported ecdsa key size") panic("ssh: unsupported ecdsa key size")
} }
type ed25519PublicKey ed25519.PublicKey
func (key ed25519PublicKey) Type() string {
return KeyAlgoED25519
}
func parseED25519(in []byte) (out PublicKey, rest []byte, err error) {
var w struct {
KeyBytes []byte
Rest []byte `ssh:"rest"`
}
if err := Unmarshal(in, &w); err != nil {
return nil, nil, err
}
key := ed25519.PublicKey(w.KeyBytes)
return (ed25519PublicKey)(key), w.Rest, nil
}
func (key ed25519PublicKey) Marshal() []byte {
w := struct {
Name string
KeyBytes []byte
}{
KeyAlgoED25519,
[]byte(key),
}
return Marshal(&w)
}
func (key ed25519PublicKey) Verify(b []byte, sig *Signature) error {
if sig.Format != key.Type() {
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type())
}
edKey := (ed25519.PublicKey)(key)
if ok := ed25519.Verify(edKey, b, sig.Blob); !ok {
return errors.New("ssh: signature did not verify")
}
return nil
}
func (k ed25519PublicKey) CryptoPublicKey() crypto.PublicKey {
return ed25519.PublicKey(k)
}
func supportedEllipticCurve(curve elliptic.Curve) bool { func supportedEllipticCurve(curve elliptic.Curve) bool {
return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()
} }
@ -507,6 +579,8 @@ func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
func (key *ecdsaPublicKey) Marshal() []byte { func (key *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(key.Curve, key.X, key.Y)
// ECDSA publickey struct layout should match the struct used by
// parseECDSACert in the x/crypto/ssh/agent package.
w := struct { w := struct {
Name string Name string
ID string ID string
@ -548,6 +622,10 @@ func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
return errors.New("ssh: signature did not verify") return errors.New("ssh: signature did not verify")
} }
func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey {
return (*ecdsa.PublicKey)(k)
}
// 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 corresponding
// Signer instance. ECDSA keys must use P-256, P-384 or P-521. // Signer instance. ECDSA keys must use P-256, P-384 or P-521.
@ -591,13 +669,19 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
hashFunc = crypto.SHA1 hashFunc = crypto.SHA1
case *ecdsaPublicKey: case *ecdsaPublicKey:
hashFunc = ecHash(key.Curve) hashFunc = ecHash(key.Curve)
case ed25519PublicKey:
default: default:
return nil, fmt.Errorf("ssh: unsupported key type %T", key) return nil, fmt.Errorf("ssh: unsupported key type %T", key)
} }
var digest []byte
if hashFunc != 0 {
h := hashFunc.New() h := hashFunc.New()
h.Write(data) h.Write(data)
digest := h.Sum(nil) digest = h.Sum(nil)
} else {
digest = data
}
signature, err := s.signer.Sign(rand, digest, hashFunc) signature, err := s.signer.Sign(rand, digest, hashFunc)
if err != nil { if err != nil {
@ -637,9 +721,9 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
}, nil }, nil
} }
// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or // NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
// any other crypto.Signer and returns a corresponding Signer instance. ECDSA // or ed25519.PublicKey returns a corresponding PublicKey instance.
// keys must use P-256, P-384 or P-521. // ECDSA keys must use P-256, P-384 or P-521.
func NewPublicKey(key interface{}) (PublicKey, error) { func NewPublicKey(key interface{}) (PublicKey, error) {
switch key := key.(type) { switch key := key.(type) {
case *rsa.PublicKey: case *rsa.PublicKey:
@ -651,6 +735,8 @@ func NewPublicKey(key interface{}) (PublicKey, error) {
return (*ecdsaPublicKey)(key), nil return (*ecdsaPublicKey)(key), nil
case *dsa.PublicKey: case *dsa.PublicKey:
return (*dsaPublicKey)(key), nil return (*dsaPublicKey)(key), nil
case ed25519.PublicKey:
return (ed25519PublicKey)(key), nil
default: default:
return nil, fmt.Errorf("ssh: unsupported key type %T", key) return nil, fmt.Errorf("ssh: unsupported key type %T", key)
} }
@ -667,6 +753,14 @@ func ParsePrivateKey(pemBytes []byte) (Signer, error) {
return NewSignerFromKey(key) return NewSignerFromKey(key)
} }
// encryptedBlock tells whether a private key is
// encrypted by examining its Proc-Type header
// for a mention of ENCRYPTED
// according to RFC 1421 Section 4.6.1.1.
func encryptedBlock(block *pem.Block) bool {
return strings.Contains(block.Headers["Proc-Type"], "ENCRYPTED")
}
// ParseRawPrivateKey returns a private key from a PEM encoded private key. It // ParseRawPrivateKey returns a private key from a PEM encoded private key. It
// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. // supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys.
func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
@ -675,6 +769,10 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
return nil, errors.New("ssh: no key found") return nil, errors.New("ssh: no key found")
} }
if encryptedBlock(block) {
return nil, errors.New("ssh: cannot decode encrypted private keys")
}
switch block.Type { switch block.Type {
case "RSA PRIVATE KEY": case "RSA PRIVATE KEY":
return x509.ParsePKCS1PrivateKey(block.Bytes) return x509.ParsePKCS1PrivateKey(block.Bytes)
@ -682,6 +780,8 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
return x509.ParseECPrivateKey(block.Bytes) return x509.ParseECPrivateKey(block.Bytes)
case "DSA PRIVATE KEY": case "DSA PRIVATE KEY":
return ParseDSAPrivateKey(block.Bytes) return ParseDSAPrivateKey(block.Bytes)
case "OPENSSH PRIVATE KEY":
return parseOpenSSHPrivateKey(block.Bytes)
default: default:
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
} }
@ -718,3 +818,63 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
X: k.Pub, X: k.Pub,
}, nil }, nil
} }
// Implemented based on the documentation at
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
magic := append([]byte("openssh-key-v1"), 0)
if !bytes.Equal(magic, key[0:len(magic)]) {
return nil, errors.New("ssh: invalid openssh private key format")
}
remaining := key[len(magic):]
var w struct {
CipherName string
KdfName string
KdfOpts string
NumKeys uint32
PubKey []byte
PrivKeyBlock []byte
}
if err := Unmarshal(remaining, &w); err != nil {
return nil, err
}
pk1 := struct {
Check1 uint32
Check2 uint32
Keytype string
Pub []byte
Priv []byte
Comment string
Pad []byte `ssh:"rest"`
}{}
if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
return nil, err
}
if pk1.Check1 != pk1.Check2 {
return nil, errors.New("ssh: checkint mismatch")
}
// we only handle ed25519 keys currently
if pk1.Keytype != KeyAlgoED25519 {
return nil, errors.New("ssh: unhandled key type")
}
for i, b := range pk1.Pad {
if int(b) != i+1 {
return nil, errors.New("ssh: padding not as expected")
}
}
if len(pk1.Priv) != ed25519.PrivateKeySize {
return nil, errors.New("ssh: private key unexpected length")
}
pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
copy(pk, pk1.Priv)
return &pk, nil
}

View File

@ -13,6 +13,7 @@ import (
"math/big" "math/big"
"reflect" "reflect"
"strconv" "strconv"
"strings"
) )
// These are SSH message type numbers. They are scattered around several // These are SSH message type numbers. They are scattered around several
@ -47,7 +48,7 @@ type disconnectMsg struct {
} }
func (d *disconnectMsg) Error() string { func (d *disconnectMsg) Error() string {
return fmt.Sprintf("ssh: disconnect reason %d: %s", d.Reason, d.Message) return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message)
} }
// See RFC 4253, section 7.1. // See RFC 4253, section 7.1.
@ -124,6 +125,10 @@ type userAuthRequestMsg struct {
Payload []byte `ssh:"rest"` Payload []byte `ssh:"rest"`
} }
// Used for debug printouts of packets.
type userAuthSuccessMsg struct {
}
// See RFC 4252, section 5.1 // See RFC 4252, section 5.1
const msgUserAuthFailure = 51 const msgUserAuthFailure = 51
@ -158,6 +163,13 @@ type channelOpenMsg struct {
const msgChannelExtendedData = 95 const msgChannelExtendedData = 95
const msgChannelData = 94 const msgChannelData = 94
// Used for debug print outs of packets.
type channelDataMsg struct {
PeersId uint32 `sshtype:"94"`
Length uint32
Rest []byte `ssh:"rest"`
}
// See RFC 4254, section 5.1. // See RFC 4254, section 5.1.
const msgChannelOpenConfirm = 91 const msgChannelOpenConfirm = 91
@ -255,17 +267,19 @@ type userAuthPubKeyOkMsg struct {
PubKey []byte PubKey []byte
} }
// typeTag returns the type byte for the given type. The type should // typeTags returns the possible type bytes for the given reflect.Type, which
// be struct. // should be a struct. The possible values are separated by a '|' character.
func typeTag(structType reflect.Type) byte { func typeTags(structType reflect.Type) (tags []byte) {
var tag byte tagStr := structType.Field(0).Tag.Get("sshtype")
var tagStr string
tagStr = structType.Field(0).Tag.Get("sshtype") for _, tag := range strings.Split(tagStr, "|") {
i, err := strconv.Atoi(tagStr) i, err := strconv.Atoi(tag)
if err == nil { if err == nil {
tag = byte(i) tags = append(tags, byte(i))
} }
return tag }
return tags
} }
func fieldError(t reflect.Type, field int, problem string) error { func fieldError(t reflect.Type, field int, problem string) error {
@ -279,19 +293,34 @@ var errShortRead = errors.New("ssh: short read")
// Unmarshal parses data in SSH wire format into a structure. The out // Unmarshal parses data in SSH wire format into a structure. The out
// argument should be a pointer to struct. If the first member of the // argument should be a pointer to struct. If the first member of the
// struct has the "sshtype" tag set to a number in decimal, the packet // struct has the "sshtype" tag set to a '|'-separated set of numbers
// must start that number. In case of error, Unmarshal returns a // in decimal, the packet must start with one of those numbers. In
// ParseError or UnexpectedMessageError. // case of error, Unmarshal returns a ParseError or
// UnexpectedMessageError.
func Unmarshal(data []byte, out interface{}) error { func Unmarshal(data []byte, out interface{}) error {
v := reflect.ValueOf(out).Elem() v := reflect.ValueOf(out).Elem()
structType := v.Type() structType := v.Type()
expectedType := typeTag(structType) expectedTypes := typeTags(structType)
var expectedType byte
if len(expectedTypes) > 0 {
expectedType = expectedTypes[0]
}
if len(data) == 0 { if len(data) == 0 {
return parseError(expectedType) return parseError(expectedType)
} }
if expectedType > 0 {
if data[0] != expectedType { if len(expectedTypes) > 0 {
return unexpectedMessageError(expectedType, data[0]) goodType := false
for _, e := range expectedTypes {
if e > 0 && data[0] == e {
goodType = true
break
}
}
if !goodType {
return fmt.Errorf("ssh: unexpected message type %d (expected one of %v)", data[0], expectedTypes)
} }
data = data[1:] data = data[1:]
} }
@ -375,7 +404,7 @@ func Unmarshal(data []byte, out interface{}) error {
return fieldError(structType, i, "pointer to unsupported type") return fieldError(structType, i, "pointer to unsupported type")
} }
default: default:
return fieldError(structType, i, "unsupported type") return fieldError(structType, i, fmt.Sprintf("unsupported type: %v", t))
} }
} }
@ -398,9 +427,9 @@ func Marshal(msg interface{}) []byte {
func marshalStruct(out []byte, msg interface{}) []byte { func marshalStruct(out []byte, msg interface{}) []byte {
v := reflect.Indirect(reflect.ValueOf(msg)) v := reflect.Indirect(reflect.ValueOf(msg))
msgType := typeTag(v.Type()) msgTypes := typeTags(v.Type())
if msgType > 0 { if len(msgTypes) > 0 {
out = append(out, msgType) out = append(out, msgTypes[0])
} }
for i, n := 0, v.NumField(); i < n; i++ { for i, n := 0, v.NumField(); i < n; i++ {
@ -687,6 +716,8 @@ func decode(packet []byte) (interface{}, error) {
msg = new(kexDHReplyMsg) msg = new(kexDHReplyMsg)
case msgUserAuthRequest: case msgUserAuthRequest:
msg = new(userAuthRequestMsg) msg = new(userAuthRequestMsg)
case msgUserAuthSuccess:
return new(userAuthSuccessMsg), nil
case msgUserAuthFailure: case msgUserAuthFailure:
msg = new(userAuthFailureMsg) msg = new(userAuthFailureMsg)
case msgUserAuthPubKeyOk: case msgUserAuthPubKeyOk:
@ -699,6 +730,8 @@ func decode(packet []byte) (interface{}, error) {
msg = new(globalRequestFailureMsg) msg = new(globalRequestFailureMsg)
case msgChannelOpen: case msgChannelOpen:
msg = new(channelOpenMsg) msg = new(channelOpenMsg)
case msgChannelData:
msg = new(channelDataMsg)
case msgChannelOpenConfirm: case msgChannelOpenConfirm:
msg = new(channelOpenConfirmMsg) msg = new(channelOpenConfirmMsg)
case msgChannelOpenFailure: case msgChannelOpenFailure:

View File

@ -131,6 +131,9 @@ func newMux(p packetConn) *mux {
func (m *mux) sendMessage(msg interface{}) error { func (m *mux) sendMessage(msg interface{}) error {
p := Marshal(msg) p := Marshal(msg)
if debugMux {
log.Printf("send global(%d): %#v", m.chanList.offset, msg)
}
return m.conn.writePacket(p) return m.conn.writePacket(p)
} }
@ -175,18 +178,6 @@ func (m *mux) ackRequest(ok bool, data []byte) error {
return m.sendMessage(globalRequestFailureMsg{Data: data}) return m.sendMessage(globalRequestFailureMsg{Data: data})
} }
// TODO(hanwen): Disconnect is a transport layer message. We should
// probably send and receive Disconnect somewhere in the transport
// code.
// Disconnect sends a disconnect message.
func (m *mux) Disconnect(reason uint32, message string) error {
return m.sendMessage(disconnectMsg{
Reason: reason,
Message: message,
})
}
func (m *mux) Close() error { func (m *mux) Close() error {
return m.conn.Close() return m.conn.Close()
} }
@ -236,11 +227,6 @@ func (m *mux) onePacket() error {
} }
switch packet[0] { switch packet[0] {
case msgNewKeys:
// Ignore notification of key change.
return nil
case msgDisconnect:
return m.handleDisconnect(packet)
case msgChannelOpen: case msgChannelOpen:
return m.handleChannelOpen(packet) return m.handleChannelOpen(packet)
case msgGlobalRequest, msgRequestSuccess, msgRequestFailure: case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
@ -260,18 +246,6 @@ func (m *mux) onePacket() error {
return ch.handlePacket(packet) return ch.handlePacket(packet)
} }
func (m *mux) handleDisconnect(packet []byte) error {
var d disconnectMsg
if err := Unmarshal(packet, &d); err != nil {
return err
}
if debugMux {
log.Printf("caught disconnect: %v", d)
}
return &d
}
func (m *mux) handleGlobalPacket(packet []byte) error { func (m *mux) handleGlobalPacket(packet []byte) error {
msg, err := decode(packet) msg, err := decode(packet)
if err != nil { if err != nil {

View File

@ -188,16 +188,10 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */) tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config) s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
if err := s.transport.requestKeyChange(); err != nil { if err := s.transport.requestInitialKeyChange(); err != nil {
return nil, err return nil, err
} }
if packet, err := s.transport.readPacket(); err != nil {
return nil, err
} else if packet[0] != msgNewKeys {
return nil, unexpectedMessageError(msgNewKeys, packet[0])
}
// We just did the key change, so the session ID is established. // We just did the key change, so the session ID is established.
s.sessionID = s.transport.getSessionID() s.sessionID = s.transport.getSessionID()
@ -230,7 +224,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, case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
return true return true
} }
@ -290,7 +284,6 @@ userAuthLoop:
switch userAuthReq.Method { switch userAuthReq.Method {
case "none": case "none":
if config.NoClientAuth { if config.NoClientAuth {
s.user = ""
authErr = nil authErr = nil
} }
case "password": case "password":

View File

@ -9,6 +9,7 @@ package ssh
import ( import (
"bytes" "bytes"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -281,9 +282,10 @@ func (s *Session) Start(cmd string) error {
// copying stdin, stdout, and stderr, and exits with a zero exit // copying stdin, stdout, and stderr, and exits with a zero exit
// status. // status.
// //
// If the command fails to run or doesn't complete successfully, the // If the remote server does not send an exit status, an error of type
// error is of type *ExitError. Other error types may be // *ExitMissingError is returned. If the command completes
// returned for I/O problems. // unsuccessfully or is interrupted by a signal, the error is of type
// *ExitError. Other error types may be returned for I/O problems.
func (s *Session) Run(cmd string) error { func (s *Session) Run(cmd string) error {
err := s.Start(cmd) err := s.Start(cmd)
if err != nil { if err != nil {
@ -370,9 +372,10 @@ func (s *Session) start() error {
// copying stdin, stdout, and stderr, and exits with a zero exit // copying stdin, stdout, and stderr, and exits with a zero exit
// status. // status.
// //
// If the command fails to run or doesn't complete successfully, the // If the remote server does not send an exit status, an error of type
// error is of type *ExitError. Other error types may be // *ExitMissingError is returned. If the command completes
// returned for I/O problems. // unsuccessfully or is interrupted by a signal, the error is of type
// *ExitError. Other error types may be returned for I/O problems.
func (s *Session) Wait() error { func (s *Session) Wait() error {
if !s.started { if !s.started {
return errors.New("ssh: session not started") return errors.New("ssh: session not started")
@ -400,8 +403,7 @@ func (s *Session) wait(reqs <-chan *Request) error {
for msg := range reqs { for msg := range reqs {
switch msg.Type { switch msg.Type {
case "exit-status": case "exit-status":
d := msg.Payload wm.status = int(binary.BigEndian.Uint32(msg.Payload))
wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
case "exit-signal": case "exit-signal":
var sigval struct { var sigval struct {
Signal string Signal string
@ -431,16 +433,29 @@ func (s *Session) wait(reqs <-chan *Request) error {
if wm.status == -1 { if wm.status == -1 {
// exit-status was never sent from server // exit-status was never sent from server
if wm.signal == "" { if wm.signal == "" {
return errors.New("wait: remote command exited without exit status or exit signal") // signal was not sent either. RFC 4254
// section 6.10 recommends against this
// behavior, but it is allowed, so we let
// clients handle it.
return &ExitMissingError{}
} }
wm.status = 128 wm.status = 128
if _, ok := signals[Signal(wm.signal)]; ok { if _, ok := signals[Signal(wm.signal)]; ok {
wm.status += signals[Signal(wm.signal)] wm.status += signals[Signal(wm.signal)]
} }
} }
return &ExitError{wm} return &ExitError{wm}
} }
// ExitMissingError is returned if a session is torn down cleanly, but
// the server sends no confirmation of the exit status.
type ExitMissingError struct{}
func (e *ExitMissingError) Error() string {
return "wait: remote command exited without exit status or exit signal"
}
func (s *Session) stdin() { func (s *Session) stdin() {
if s.stdinpipe { if s.stdinpipe {
return return
@ -601,5 +616,12 @@ func (w Waitmsg) Lang() string {
} }
func (w Waitmsg) String() string { func (w Waitmsg) String() string {
return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal) str := fmt.Sprintf("Process exited with status %v", w.status)
if w.signal != "" {
str += fmt.Sprintf(" from signal %v", w.signal)
}
if w.msg != "" {
str += fmt.Sprintf(". Reason was: %v", w.msg)
}
return str
} }

View File

@ -13,6 +13,7 @@ import (
const ( const (
gcmCipherID = "aes128-gcm@openssh.com" gcmCipherID = "aes128-gcm@openssh.com"
aes128cbcID = "aes128-cbc" aes128cbcID = "aes128-cbc"
tripledescbcID = "3des-cbc"
) )
// packetConn represents a transport that implements packet based // packetConn represents a transport that implements packet based
@ -39,19 +40,6 @@ type transport struct {
rand io.Reader rand io.Reader
io.Closer io.Closer
// Initial H used for the session ID. Once assigned this does
// not change, even during subsequent key exchanges.
sessionID []byte
}
// getSessionID returns the ID of the SSH connection. The return value
// should not be modified.
func (t *transport) getSessionID() []byte {
if t.sessionID == nil {
panic("session ID not set yet")
}
return t.sessionID
} }
// packetCipher represents a combination of SSH encryption/MAC // packetCipher represents a combination of SSH encryption/MAC
@ -81,12 +69,6 @@ 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 t.sessionID == nil {
t.sessionID = kexResult.H
}
kexResult.SessionID = t.sessionID
if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil { if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil {
return err return err
} else { } else {
@ -114,13 +96,28 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
err = errors.New("ssh: zero length packet") err = errors.New("ssh: zero length packet")
} }
if len(packet) > 0 && packet[0] == msgNewKeys { if len(packet) > 0 {
switch packet[0] {
case msgNewKeys:
select { select {
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:
// Transform a disconnect message into an
// error. Since this is lowest level at which
// we interpret message types, doing it here
// ensures that we don't have to handle it
// elsewhere.
var msg disconnectMsg
if err := Unmarshal(packet, &msg); err != nil {
return nil, err
}
return nil, &msg
}
} }
// The packet may point to an internal buffer, so copy the // The packet may point to an internal buffer, so copy the
@ -223,6 +220,10 @@ func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (pac
return newAESCBCCipher(iv, key, macKey, algs) return newAESCBCCipher(iv, key, macKey, algs)
} }
if algs.Cipher == tripledescbcID {
return newTripleDESCBCCipher(iv, key, macKey, algs)
}
c := &streamPacketCipher{ c := &streamPacketCipher{
mac: macModes[algs.MAC].new(macKey), mac: macModes[algs.MAC].new(macKey),
} }

36
vendor/vendor.json vendored
View File

@ -2169,16 +2169,46 @@
"revisionTime": "2016-10-04T18:54:04Z" "revisionTime": "2016-10-04T18:54:04Z"
}, },
{ {
"checksumSHA1": "9x64JhJGo6z8TS0Q33cGcR64kjA=",
"path": "golang.org/x/crypto/curve25519", "path": "golang.org/x/crypto/curve25519",
"revision": "1f22c0103821b9390939b6776727195525381532" "revision": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
"revisionTime": "2016-10-25T12:52:55Z"
}, },
{ {
"checksumSHA1": "wGb//LjBPNxYHqk+dcLo7BjPXK8=",
"path": "golang.org/x/crypto/ed25519",
"revision": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
"revisionTime": "2016-10-25T12:52:55Z"
},
{
"checksumSHA1": "LXFcVx8I587SnWmKycSDEq9yvK8=",
"path": "golang.org/x/crypto/ed25519/internal/edwards25519",
"revision": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
"revisionTime": "2016-10-25T12:52:55Z"
},
{
"checksumSHA1": "KeaQhXgb4KlZWJM7aIgcJwCKNOg=",
"path": "golang.org/x/crypto/pkcs12",
"revision": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
"revisionTime": "2016-10-25T12:52:55Z"
},
{
"checksumSHA1": "iVJcif9M9uefvvqHCNR9VQrjc/s=",
"path": "golang.org/x/crypto/pkcs12/internal/rc2",
"revision": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
"revisionTime": "2016-10-25T12:52:55Z"
},
{
"checksumSHA1": "LlElMHeTC34ng8eHzjvtUhAgrr8=",
"path": "golang.org/x/crypto/ssh", "path": "golang.org/x/crypto/ssh",
"revision": "1f22c0103821b9390939b6776727195525381532" "revision": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
"revisionTime": "2016-10-25T12:52:55Z"
}, },
{ {
"checksumSHA1": "SJ3Ma3Ozavxpbh1usZWBCnzMKIc=",
"path": "golang.org/x/crypto/ssh/agent", "path": "golang.org/x/crypto/ssh/agent",
"revision": "1f22c0103821b9390939b6776727195525381532" "revision": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
"revisionTime": "2016-10-25T12:52:55Z"
}, },
{ {
"path": "golang.org/x/net/context", "path": "golang.org/x/net/context",