Revert "Bump crypto/ssh package" (#9775)
This commit is contained in:
parent
fc329fbdea
commit
c13aab2568
|
@ -0,0 +1,841 @@
|
||||||
|
// 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)
|
||||||
|
}
|
|
@ -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 // import "golang.org/x/crypto/curve25519"
|
package 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}
|
||||||
|
|
|
@ -8,9 +8,22 @@
|
||||||
// +build amd64,!gccgo,!appengine
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
// func freeze(inout *[5]uint64)
|
// func freeze(inout *[5]uint64)
|
||||||
TEXT ·freeze(SB),7,$0-8
|
TEXT ·freeze(SB),7,$96-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
|
||||||
|
@ -68,4 +81,14 @@ 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
|
@ -8,21 +8,35 @@
|
||||||
// +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,$16-24
|
TEXT ·mul(SB),0,$128-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,0(SP)
|
MOVQ AX,64(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,8(SP)
|
MOVQ AX,72(SP)
|
||||||
MULQ 8(CX)
|
MULQ 8(CX)
|
||||||
ADDQ AX,R8
|
ADDQ AX,R8
|
||||||
ADCQ DX,R9
|
ADCQ DX,R9
|
||||||
|
@ -97,11 +111,11 @@ TEXT ·mul(SB),0,$16-24
|
||||||
MULQ 8(CX)
|
MULQ 8(CX)
|
||||||
ADDQ AX,BX
|
ADDQ AX,BX
|
||||||
ADCQ DX,BP
|
ADCQ DX,BP
|
||||||
MOVQ 0(SP),AX
|
MOVQ 64(SP),AX
|
||||||
MULQ 24(CX)
|
MULQ 24(CX)
|
||||||
ADDQ AX,R10
|
ADDQ AX,R10
|
||||||
ADCQ DX,R11
|
ADCQ DX,R11
|
||||||
MOVQ 0(SP),AX
|
MOVQ 64(SP),AX
|
||||||
MULQ 32(CX)
|
MULQ 32(CX)
|
||||||
ADDQ AX,R12
|
ADDQ AX,R12
|
||||||
ADCQ DX,R13
|
ADCQ DX,R13
|
||||||
|
@ -109,15 +123,15 @@ TEXT ·mul(SB),0,$16-24
|
||||||
MULQ 0(CX)
|
MULQ 0(CX)
|
||||||
ADDQ AX,BX
|
ADDQ AX,BX
|
||||||
ADCQ DX,BP
|
ADCQ DX,BP
|
||||||
MOVQ 8(SP),AX
|
MOVQ 72(SP),AX
|
||||||
MULQ 16(CX)
|
MULQ 16(CX)
|
||||||
ADDQ AX,R10
|
ADDQ AX,R10
|
||||||
ADCQ DX,R11
|
ADCQ DX,R11
|
||||||
MOVQ 8(SP),AX
|
MOVQ 72(SP),AX
|
||||||
MULQ 24(CX)
|
MULQ 24(CX)
|
||||||
ADDQ AX,R12
|
ADDQ AX,R12
|
||||||
ADCQ DX,R13
|
ADCQ DX,R13
|
||||||
MOVQ 8(SP),AX
|
MOVQ 72(SP),AX
|
||||||
MULQ 32(CX)
|
MULQ 32(CX)
|
||||||
ADDQ AX,R14
|
ADDQ AX,R14
|
||||||
ADCQ DX,R15
|
ADCQ DX,R15
|
||||||
|
@ -164,4 +178,14 @@ TEXT ·mul(SB),0,$16-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
|
||||||
|
|
|
@ -8,10 +8,23 @@
|
||||||
// +build amd64,!gccgo,!appengine
|
// +build amd64,!gccgo,!appengine
|
||||||
|
|
||||||
// func square(out, in *[5]uint64)
|
// func square(out, in *[5]uint64)
|
||||||
TEXT ·square(SB),7,$0-16
|
TEXT ·square(SB),7,$96-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
|
||||||
|
@ -127,4 +140,14 @@ TEXT ·square(SB),7,$0-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
|
||||||
|
|
|
@ -1,181 +0,0 @@
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// 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
|
@ -2,14 +2,13 @@
|
||||||
// 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
|
/*
|
||||||
// a client and a server. The client can talk to a standard ssh-agent
|
Package agent implements a client to an ssh-agent daemon.
|
||||||
// that uses UNIX sockets, and one could implement an alternative
|
|
||||||
// ssh-agent process using the sample server.
|
References:
|
||||||
//
|
[PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD
|
||||||
// References:
|
*/
|
||||||
// [PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD
|
package agent
|
||||||
package agent // import "golang.org/x/crypto/ssh/agent"
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -25,7 +24,6 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -77,8 +75,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
|
||||||
|
@ -185,13 +182,10 @@ func (k *Key) Marshal() []byte {
|
||||||
return k.Blob
|
return k.Blob
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify satisfies the ssh.PublicKey interface.
|
// Verify satisfies the ssh.PublicKey interface, but is not
|
||||||
|
// implemented for agent keys.
|
||||||
func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
|
func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
|
||||||
pubKey, err := ssh.ParsePublicKey(k.Blob)
|
return errors.New("agent: agent key does not know how to verify")
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("agent: bad public key: %v", err)
|
|
||||||
}
|
|
||||||
return pubKey.Verify(data, sig)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type wireKey struct {
|
type wireKey struct {
|
||||||
|
@ -381,8 +375,6 @@ 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])
|
||||||
}
|
}
|
||||||
|
@ -393,7 +385,7 @@ func unmarshal(packet []byte) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type rsaKeyMsg struct {
|
type rsaKeyMsg struct {
|
||||||
Type string `sshtype:"17|25"`
|
Type string `sshtype:"17"`
|
||||||
N *big.Int
|
N *big.Int
|
||||||
E *big.Int
|
E *big.Int
|
||||||
D *big.Int
|
D *big.Int
|
||||||
|
@ -405,7 +397,7 @@ type rsaKeyMsg struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type dsaKeyMsg struct {
|
type dsaKeyMsg struct {
|
||||||
Type string `sshtype:"17|25"`
|
Type string `sshtype:"17"`
|
||||||
P *big.Int
|
P *big.Int
|
||||||
Q *big.Int
|
Q *big.Int
|
||||||
G *big.Int
|
G *big.Int
|
||||||
|
@ -416,7 +408,7 @@ type dsaKeyMsg struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ecdsaKeyMsg struct {
|
type ecdsaKeyMsg struct {
|
||||||
Type string `sshtype:"17|25"`
|
Type string `sshtype:"17"`
|
||||||
Curve string
|
Curve string
|
||||||
KeyBytes []byte
|
KeyBytes []byte
|
||||||
D *big.Int
|
D *big.Int
|
||||||
|
@ -424,14 +416,6 @@ 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
|
||||||
|
@ -473,14 +457,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
@ -501,7 +477,7 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er
|
||||||
}
|
}
|
||||||
|
|
||||||
type rsaCertMsg struct {
|
type rsaCertMsg struct {
|
||||||
Type string `sshtype:"17|25"`
|
Type string `sshtype:"17"`
|
||||||
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
|
||||||
|
@ -512,7 +488,7 @@ type rsaCertMsg struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type dsaCertMsg struct {
|
type dsaCertMsg struct {
|
||||||
Type string `sshtype:"17|25"`
|
Type string `sshtype:"17"`
|
||||||
CertBytes []byte
|
CertBytes []byte
|
||||||
X *big.Int
|
X *big.Int
|
||||||
Comments string
|
Comments string
|
||||||
|
@ -520,23 +496,14 @@ type dsaCertMsg struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ecdsaCertMsg struct {
|
type ecdsaCertMsg struct {
|
||||||
Type string `sshtype:"17|25"`
|
Type string `sshtype:"17"`
|
||||||
CertBytes []byte
|
CertBytes []byte
|
||||||
D *big.Int
|
D *big.Int
|
||||||
Comments string
|
Comments string
|
||||||
Constraints []byte `ssh:"rest"`
|
Constraints []byte `ssh:"rest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ed25519CertMsg struct {
|
// Insert adds a private key to the agent. If a certificate is given,
|
||||||
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
|
||||||
|
@ -580,28 +547,17 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string
|
||||||
})
|
})
|
||||||
case *dsa.PrivateKey:
|
case *dsa.PrivateKey:
|
||||||
req = ssh.Marshal(dsaCertMsg{
|
req = ssh.Marshal(dsaCertMsg{
|
||||||
Type: cert.Type(),
|
Type: cert.Type(),
|
||||||
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{
|
||||||
Type: cert.Type(),
|
Type: cert.Type(),
|
||||||
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)
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
@ -19,7 +18,6 @@ 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 {
|
||||||
|
@ -50,9 +48,15 @@ func (r *keyring) RemoveAll() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeLocked does the actual key removal. The caller must already be holding the
|
// Remove removes all identities with the given public key.
|
||||||
// keyring mutex.
|
func (r *keyring) Remove(key ssh.PublicKey) error {
|
||||||
func (r *keyring) removeLocked(want []byte) error {
|
r.mu.Lock()
|
||||||
|
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) {
|
||||||
|
@ -71,18 +75,7 @@ func (r *keyring) removeLocked(want []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes all identities with the given public key.
|
// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list.
|
||||||
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()
|
||||||
|
@ -111,17 +104,6 @@ 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()
|
||||||
|
@ -131,7 +113,6 @@ 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()
|
||||||
|
@ -165,17 +146,7 @@ func (r *keyring) Add(key AddedKey) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p := privKey{
|
r.keys = append(r.keys, privKey{signer, key.Comment})
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -188,7 +159,6 @@ 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) {
|
||||||
|
@ -206,7 +176,6 @@ 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)
|
||||||
|
|
|
@ -5,18 +5,13 @@
|
||||||
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,9 +49,6 @@ 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"`
|
||||||
}
|
}
|
||||||
|
@ -77,10 +69,6 @@ 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 {
|
||||||
|
@ -133,7 +121,6 @@ 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 {
|
||||||
|
@ -147,271 +134,42 @@ 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 parseRSAKey(req []byte) (*AddedKey, error) {
|
|
||||||
var k rsaKeyMsg
|
|
||||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if k.E.BitLen() > 30 {
|
|
||||||
return nil, errors.New("agent: RSA public exponent too large")
|
|
||||||
}
|
|
||||||
priv := &rsa.PrivateKey{
|
|
||||||
PublicKey: rsa.PublicKey{
|
|
||||||
E: int(k.E.Int64()),
|
|
||||||
N: k.N,
|
|
||||||
},
|
|
||||||
D: k.D,
|
|
||||||
Primes: []*big.Int{k.P, k.Q},
|
|
||||||
}
|
|
||||||
priv.Precompute()
|
|
||||||
|
|
||||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
func (s *server) insertIdentity(req []byte) error {
|
||||||
var record struct {
|
var record struct {
|
||||||
Type string `sshtype:"17|25"`
|
Type string `sshtype:"17"`
|
||||||
Rest []byte `ssh:"rest"`
|
Rest []byte `ssh:"rest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ssh.Unmarshal(req, &record); err != nil {
|
if err := ssh.Unmarshal(req, &record); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var addedKey *AddedKey
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch record.Type {
|
switch record.Type {
|
||||||
case ssh.KeyAlgoRSA:
|
case ssh.KeyAlgoRSA:
|
||||||
addedKey, err = parseRSAKey(req)
|
var k rsaKeyMsg
|
||||||
case ssh.KeyAlgoDSA:
|
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||||
addedKey, err = parseDSAKey(req)
|
return err
|
||||||
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 {
|
priv := rsa.PrivateKey{
|
||||||
return err
|
PublicKey: rsa.PublicKey{
|
||||||
|
E: int(k.E.Int64()),
|
||||||
|
N: k.N,
|
||||||
|
},
|
||||||
|
D: k.D,
|
||||||
|
Primes: []*big.Int{k.P, k.Q},
|
||||||
|
}
|
||||||
|
priv.Precompute()
|
||||||
|
|
||||||
|
return s.agent.Add(AddedKey{PrivateKey: &priv, Comment: k.Comments})
|
||||||
}
|
}
|
||||||
return s.agent.Add(*addedKey)
|
return fmt.Errorf("not implemented: %s", record.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeAgent serves the agent protocol on the given connection. It
|
// ServeAgent serves the agent protocol on the given connection. It
|
||||||
|
|
|
@ -22,7 +22,6 @@ 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
|
||||||
|
@ -402,7 +401,6 @@ 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.
|
||||||
|
@ -461,7 +459,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 " + c.Key.Type())
|
panic("unknown cert key type")
|
||||||
}
|
}
|
||||||
return algo
|
return algo
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,6 @@ 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
|
||||||
|
@ -219,7 +217,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)
|
||||||
|
@ -373,7 +371,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 necessary for a normal channel teardown, but if
|
// This is not necesary 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()
|
||||||
|
|
|
@ -7,7 +7,6 @@ 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"
|
||||||
|
@ -122,9 +121,6 @@ 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
|
||||||
|
@ -372,7 +368,12 @@ type cbcCipher struct {
|
||||||
oracleCamouflage uint32
|
oracleCamouflage uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
func newAESCBCCipher(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),
|
||||||
|
@ -386,34 +387,6 @@ func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorith
|
||||||
return cbc, nil
|
return cbc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
|
||||||
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)
|
||||||
|
|
|
@ -9,7 +9,6 @@ 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,
|
||||||
|
@ -97,10 +96,16 @@ 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.requestInitialKeyChange(); err != nil {
|
if err := c.transport.requestKeyChange(); 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()
|
||||||
|
|
||||||
|
@ -164,7 +169,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.DialTimeout(network, addr, config.Timeout)
|
conn, err := net.Dial(network, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -205,9 +210,4 @@ 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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -321,6 +321,8 @@ 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])
|
||||||
}
|
}
|
||||||
|
@ -437,37 +439,3 @@ 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}
|
|
||||||
}
|
|
||||||
|
|
|
@ -44,12 +44,10 @@ 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, CertAlgoED25519v01,
|
CertAlgoECDSA384v01, CertAlgoECDSA521v01,
|
||||||
|
|
||||||
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.
|
||||||
|
|
|
@ -23,6 +23,7 @@ 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.
|
||||||
|
|
|
@ -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 // import "golang.org/x/crypto/ssh"
|
package ssh
|
||||||
|
|
|
@ -29,6 +29,25 @@ 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
|
||||||
|
@ -67,9 +86,6 @@ 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 {
|
||||||
|
@ -106,7 +122,7 @@ func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *handshakeTransport) getSessionID() []byte {
|
func (t *handshakeTransport) getSessionID() []byte {
|
||||||
return t.sessionID
|
return t.conn.getSessionID()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *handshakeTransport) id() string {
|
func (t *handshakeTransport) id() string {
|
||||||
|
@ -161,22 +177,15 @@ 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 {
|
msg, err := decode(p)
|
||||||
log.Printf("%s got data (packet %d bytes)", t.id(), len(p))
|
log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err)
|
||||||
} else {
|
|
||||||
msg, err := decode(p)
|
|
||||||
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()
|
||||||
|
@ -184,7 +193,7 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if debugHandshake {
|
if debugHandshake {
|
||||||
log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err)
|
log.Printf("%s exited key exchange, err %v", t.id(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unblock writers.
|
// Unblock writers.
|
||||||
|
@ -199,69 +208,28 @@ 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(isFirst keyChangeCategory) error {
|
func (t *handshakeTransport) sendKexInit() (*kexInitMsg, []byte, error) {
|
||||||
var err error
|
|
||||||
|
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
// If this is the initial key change, but we already have a sessionID,
|
defer t.mu.Unlock()
|
||||||
// then do nothing because the key exchange has already completed
|
return t.sendKexInitLocked()
|
||||||
// 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 {
|
||||||
return t.sendKexInit(subsequentKeyExchange)
|
_, _, err := t.sendKexInit()
|
||||||
|
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(isFirst keyChangeCategory) (*kexInitMsg, []byte, error) {
|
func (t *handshakeTransport) sendKexInitLocked() (*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
|
||||||
|
@ -269,7 +237,6 @@ func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexI
|
||||||
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,
|
||||||
|
@ -309,7 +276,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(subsequentKeyExchange)
|
t.sendKexInitLocked()
|
||||||
}
|
}
|
||||||
for t.sentInitMsg != nil && t.writeError == nil {
|
for t.sentInitMsg != nil && t.writeError == nil {
|
||||||
t.cond.Wait()
|
t.cond.Wait()
|
||||||
|
@ -333,12 +300,12 @@ func (t *handshakeTransport) Close() error {
|
||||||
return t.conn.Close()
|
return t.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// enterKeyExchange runs the key exchange. t.mu must be held while running this.
|
// enterKeyExchange runs the key exchange.
|
||||||
func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) error {
|
func (t *handshakeTransport) enterKeyExchange(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.sendKexInitLocked(subsequentKeyExchange)
|
myInit, myInitPacket, err := t.sendKexInit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -371,16 +338,7 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
|
@ -404,11 +362,6 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro
|
||||||
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
|
||||||
|
@ -418,7 +371,6 @@ func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) erro
|
||||||
} else if packet[0] != msgNewKeys {
|
} else if packet[0] != msgNewKeys {
|
||||||
return unexpectedMessageError(msgNewKeys, packet[0])
|
return unexpectedMessageError(msgNewKeys, packet[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 derive key material inside the transport.
|
// to signal data inside 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, pMinus1 *big.Int
|
g, p *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.Cmp(bigOne) <= 0 || theirPublic.Cmp(group.pMinus1) >= 0 {
|
if theirPublic.Sign() <= 0 || theirPublic.Cmp(group.p) >= 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,17 +90,10 @@ 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
|
||||||
|
|
||||||
var x *big.Int
|
x, err := rand.Int(randSource, group.p)
|
||||||
for {
|
if err != nil {
|
||||||
var err error
|
return nil, err
|
||||||
if x, err = rand.Int(randSource, group.pMinus1); err != nil {
|
|
||||||
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,
|
||||||
|
@ -153,14 +146,9 @@ func (group *dhGroup) Server(c packetConn, randSource io.Reader, magics *handsha
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var y *big.Int
|
y, err := rand.Int(randSource, group.p)
|
||||||
for {
|
if err != nil {
|
||||||
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)
|
||||||
|
@ -385,7 +373,6 @@ 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
|
||||||
|
@ -395,7 +382,6 @@ 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()}
|
||||||
|
|
|
@ -20,8 +20,6 @@ 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
|
||||||
|
@ -32,7 +30,6 @@ 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.
|
||||||
|
@ -45,16 +42,14 @@ 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 KeyAlgoED25519:
|
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
||||||
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", algo)
|
return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
|
// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
|
||||||
|
@ -125,7 +120,7 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip out the beginning of the known_host key.
|
// Strip out the begining 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 {
|
||||||
|
@ -281,12 +276,6 @@ 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.
|
||||||
|
@ -330,8 +319,6 @@ 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
|
||||||
|
@ -354,10 +341,6 @@ 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 {
|
||||||
|
@ -386,8 +369,6 @@ 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
|
||||||
|
@ -426,10 +407,6 @@ 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
|
||||||
}
|
}
|
||||||
|
@ -478,55 +455,6 @@ 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()
|
||||||
}
|
}
|
||||||
|
@ -579,8 +507,6 @@ 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
|
||||||
|
@ -622,10 +548,6 @@ 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.
|
||||||
|
@ -669,19 +591,13 @@ 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
|
h := hashFunc.New()
|
||||||
if hashFunc != 0 {
|
h.Write(data)
|
||||||
h := hashFunc.New()
|
digest := h.Sum(nil)
|
||||||
h.Write(data)
|
|
||||||
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 {
|
||||||
|
@ -721,9 +637,9 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
|
// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or
|
||||||
// or ed25519.PublicKey returns a corresponding PublicKey instance.
|
// any other crypto.Signer and returns a corresponding Signer instance. ECDSA
|
||||||
// ECDSA keys must use P-256, P-384 or P-521.
|
// 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:
|
||||||
|
@ -735,8 +651,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
@ -753,14 +667,6 @@ 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) {
|
||||||
|
@ -769,10 +675,6 @@ 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)
|
||||||
|
@ -780,8 +682,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
@ -818,63 +718,3 @@ 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
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ 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
|
||||||
|
@ -48,7 +47,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.
|
||||||
|
@ -125,10 +124,6 @@ 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
|
||||||
|
|
||||||
|
@ -163,13 +158,6 @@ 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
|
||||||
|
|
||||||
|
@ -267,19 +255,17 @@ type userAuthPubKeyOkMsg struct {
|
||||||
PubKey []byte
|
PubKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// typeTags returns the possible type bytes for the given reflect.Type, which
|
// typeTag returns the type byte for the given type. The type should
|
||||||
// should be a struct. The possible values are separated by a '|' character.
|
// be struct.
|
||||||
func typeTags(structType reflect.Type) (tags []byte) {
|
func typeTag(structType reflect.Type) byte {
|
||||||
tagStr := structType.Field(0).Tag.Get("sshtype")
|
var tag byte
|
||||||
|
var tagStr string
|
||||||
for _, tag := range strings.Split(tagStr, "|") {
|
tagStr = structType.Field(0).Tag.Get("sshtype")
|
||||||
i, err := strconv.Atoi(tag)
|
i, err := strconv.Atoi(tagStr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tags = append(tags, byte(i))
|
tag = 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 {
|
||||||
|
@ -293,34 +279,19 @@ 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 '|'-separated set of numbers
|
// struct has the "sshtype" tag set to a number in decimal, the packet
|
||||||
// in decimal, the packet must start with one of those numbers. In
|
// must start that number. In case of error, Unmarshal returns a
|
||||||
// case of error, Unmarshal returns a ParseError or
|
// ParseError or UnexpectedMessageError.
|
||||||
// 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()
|
||||||
expectedTypes := typeTags(structType)
|
expectedType := typeTag(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 len(expectedTypes) > 0 {
|
if data[0] != expectedType {
|
||||||
goodType := false
|
return unexpectedMessageError(expectedType, data[0])
|
||||||
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:]
|
||||||
}
|
}
|
||||||
|
@ -404,7 +375,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, fmt.Sprintf("unsupported type: %v", t))
|
return fieldError(structType, i, "unsupported type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,9 +398,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))
|
||||||
msgTypes := typeTags(v.Type())
|
msgType := typeTag(v.Type())
|
||||||
if len(msgTypes) > 0 {
|
if msgType > 0 {
|
||||||
out = append(out, msgTypes[0])
|
out = append(out, msgType)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, n := 0, v.NumField(); i < n; i++ {
|
for i, n := 0, v.NumField(); i < n; i++ {
|
||||||
|
@ -716,8 +687,6 @@ 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:
|
||||||
|
@ -730,8 +699,6 @@ 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:
|
||||||
|
|
|
@ -131,9 +131,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +175,18 @@ 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()
|
||||||
}
|
}
|
||||||
|
@ -227,6 +236,11 @@ 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:
|
||||||
|
@ -246,6 +260,18 @@ 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 {
|
||||||
|
|
|
@ -188,10 +188,16 @@ 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.requestInitialKeyChange(); err != nil {
|
if err := s.transport.requestKeyChange(); 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()
|
||||||
|
|
||||||
|
@ -224,7 +230,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
||||||
|
|
||||||
func isAcceptableAlgo(algo string) bool {
|
func isAcceptableAlgo(algo string) bool {
|
||||||
switch algo {
|
switch algo {
|
||||||
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
|
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -284,6 +290,7 @@ 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":
|
||||||
|
|
|
@ -9,7 +9,6 @@ package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -282,10 +281,9 @@ 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 remote server does not send an exit status, an error of type
|
// If the command fails to run or doesn't complete successfully, the
|
||||||
// *ExitMissingError is returned. If the command completes
|
// error is of type *ExitError. Other error types may be
|
||||||
// unsuccessfully or is interrupted by a signal, the error is of type
|
// returned for I/O problems.
|
||||||
// *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 {
|
||||||
|
@ -372,10 +370,9 @@ 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 remote server does not send an exit status, an error of type
|
// If the command fails to run or doesn't complete successfully, the
|
||||||
// *ExitMissingError is returned. If the command completes
|
// error is of type *ExitError. Other error types may be
|
||||||
// unsuccessfully or is interrupted by a signal, the error is of type
|
// returned for I/O problems.
|
||||||
// *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")
|
||||||
|
@ -403,7 +400,8 @@ 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":
|
||||||
wm.status = int(binary.BigEndian.Uint32(msg.Payload))
|
d := 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
|
||||||
|
@ -433,29 +431,16 @@ 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 == "" {
|
||||||
// signal was not sent either. RFC 4254
|
return errors.New("wait: remote command exited without exit status or exit signal")
|
||||||
// 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
|
||||||
|
@ -616,12 +601,5 @@ func (w Waitmsg) Lang() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w Waitmsg) String() string {
|
func (w Waitmsg) String() string {
|
||||||
str := fmt.Sprintf("Process exited with status %v", w.status)
|
return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal)
|
||||||
if w.signal != "" {
|
|
||||||
str += fmt.Sprintf(" from signal %v", w.signal)
|
|
||||||
}
|
|
||||||
if w.msg != "" {
|
|
||||||
str += fmt.Sprintf(". Reason was: %v", w.msg)
|
|
||||||
}
|
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,8 @@ 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
|
||||||
|
@ -40,6 +39,19 @@ 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
|
||||||
|
@ -69,6 +81,12 @@ 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 {
|
||||||
|
@ -96,27 +114,12 @@ 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 {
|
if len(packet) > 0 && packet[0] == msgNewKeys {
|
||||||
switch packet[0] {
|
select {
|
||||||
case msgNewKeys:
|
case cipher := <-s.pendingKeyChange:
|
||||||
select {
|
s.packetCipher = cipher
|
||||||
case cipher := <-s.pendingKeyChange:
|
default:
|
||||||
s.packetCipher = cipher
|
return nil, errors.New("ssh: got bogus newkeys message.")
|
||||||
default:
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,10 +223,6 @@ 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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -2181,46 +2181,16 @@
|
||||||
"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": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
|
"revision": "1f22c0103821b9390939b6776727195525381532"
|
||||||
"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": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
|
"revision": "1f22c0103821b9390939b6776727195525381532"
|
||||||
"revisionTime": "2016-10-25T12:52:55Z"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "SJ3Ma3Ozavxpbh1usZWBCnzMKIc=",
|
|
||||||
"path": "golang.org/x/crypto/ssh/agent",
|
"path": "golang.org/x/crypto/ssh/agent",
|
||||||
"revision": "ca7e7f10cb9fd9c1a6ff7f60436c086d73714180",
|
"revision": "1f22c0103821b9390939b6776727195525381532"
|
||||||
"revisionTime": "2016-10-25T12:52:55Z"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "golang.org/x/net/context",
|
"path": "golang.org/x/net/context",
|
||||||
|
|
Loading…
Reference in New Issue