golang.org/x/crypto: sync to the latest version
It includes Jungo's memory-alignment fix for ARM7.
Change-Id: I300b1c4f78df55bb4bfa19a2ec127f1c6950d3c7
diff --git a/go/src/golang.org/x/crypto/README.google b/go/src/golang.org/x/crypto/README.google
index 2fd2c7f..64b4cac 100644
--- a/go/src/golang.org/x/crypto/README.google
+++ b/go/src/golang.org/x/crypto/README.google
@@ -1,5 +1,5 @@
-URL: https://go.googlesource.com/crypto/+archive/02a186af8b62cb007f392270669b91be5527d39c.tar.gz
-Version: 602a186af8b62cb007f392270669b91be5527d39c
+URL: https://go.googlesource.com/crypto/+archive/81bf7719a6b7ce9b665598222362b50122dfc13b.tar.gz
+Version: 81bf7719a6b7ce9b665598222362b50122dfc13b
License: New BSD
License File: LICENSE
diff --git a/go/src/golang.org/x/crypto/ocsp/ocsp.go b/go/src/golang.org/x/crypto/ocsp/ocsp.go
index 602fefa..1850e1b 100644
--- a/go/src/golang.org/x/crypto/ocsp/ocsp.go
+++ b/go/src/golang.org/x/crypto/ocsp/ocsp.go
@@ -87,7 +87,7 @@
type singleResponse struct {
CertID certID
Good asn1.Flag `asn1:"tag:0,optional"`
- Revoked revokedInfo `asn1:"explicit,tag:1,optional"`
+ Revoked revokedInfo `asn1:"tag:1,optional"`
Unknown asn1.Flag `asn1:"tag:2,optional"`
ThisUpdate time.Time `asn1:"generalized"`
NextUpdate time.Time `asn1:"generalized,explicit,tag:0,optional"`
diff --git a/go/src/golang.org/x/crypto/openpgp/keys.go b/go/src/golang.org/x/crypto/openpgp/keys.go
index fe12cfa..bfe3260 100644
--- a/go/src/golang.org/x/crypto/openpgp/keys.go
+++ b/go/src/golang.org/x/crypto/openpgp/keys.go
@@ -464,15 +464,20 @@
func NewEntity(name, comment, email string, config *packet.Config) (*Entity, error) {
currentTime := config.Now()
+ bits := defaultRSAKeyBits
+ if config != nil && config.RSABits != 0 {
+ bits = config.RSABits
+ }
+
uid := packet.NewUserId(name, comment, email)
if uid == nil {
return nil, errors.InvalidArgumentError("user id field contained invalid characters")
}
- signingPriv, err := rsa.GenerateKey(config.Random(), defaultRSAKeyBits)
+ signingPriv, err := rsa.GenerateKey(config.Random(), bits)
if err != nil {
return nil, err
}
- encryptingPriv, err := rsa.GenerateKey(config.Random(), defaultRSAKeyBits)
+ encryptingPriv, err := rsa.GenerateKey(config.Random(), bits)
if err != nil {
return nil, err
}
diff --git a/go/src/golang.org/x/crypto/openpgp/packet/config.go b/go/src/golang.org/x/crypto/openpgp/packet/config.go
index d977cde..c76eecc 100644
--- a/go/src/golang.org/x/crypto/openpgp/packet/config.go
+++ b/go/src/golang.org/x/crypto/openpgp/packet/config.go
@@ -43,6 +43,9 @@
// use a value that is at least 65536. See RFC 4880 Section
// 3.7.1.3.
S2KCount int
+ // RSABits is the number of bits in new RSA keys made with NewEntity.
+ // If zero, then 2048 bit keys are created.
+ RSABits int
}
func (c *Config) Random() io.Reader {
diff --git a/go/src/golang.org/x/crypto/openpgp/packet/opaque.go b/go/src/golang.org/x/crypto/openpgp/packet/opaque.go
index f2e00b9..456d807 100644
--- a/go/src/golang.org/x/crypto/openpgp/packet/opaque.go
+++ b/go/src/golang.org/x/crypto/openpgp/packet/opaque.go
@@ -6,9 +6,10 @@
import (
"bytes"
- "golang.org/x/crypto/openpgp/errors"
"io"
"io/ioutil"
+
+ "golang.org/x/crypto/openpgp/errors"
)
// OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is
@@ -138,7 +139,7 @@
uint32(contents[4])
contents = contents[5:]
}
- if subLen > uint32(len(contents)) {
+ if subLen > uint32(len(contents)) || subLen == 0 {
goto Truncated
}
subPacket.SubType = contents[0]
diff --git a/go/src/golang.org/x/crypto/openpgp/packet/private_key.go b/go/src/golang.org/x/crypto/openpgp/packet/private_key.go
index 9685a34..cddecfa 100644
--- a/go/src/golang.org/x/crypto/openpgp/packet/private_key.go
+++ b/go/src/golang.org/x/crypto/openpgp/packet/private_key.go
@@ -263,6 +263,9 @@
rsaPriv.Primes = make([]*big.Int, 2)
rsaPriv.Primes[0] = new(big.Int).SetBytes(p)
rsaPriv.Primes[1] = new(big.Int).SetBytes(q)
+ if err := rsaPriv.Validate(); err != nil {
+ return err
+ }
rsaPriv.Precompute()
pk.PrivateKey = rsaPriv
pk.Encrypted = false
diff --git a/go/src/golang.org/x/crypto/openpgp/packet/private_key_test.go b/go/src/golang.org/x/crypto/openpgp/packet/private_key_test.go
index 6a6197a..25c8931 100644
--- a/go/src/golang.org/x/crypto/openpgp/packet/private_key_test.go
+++ b/go/src/golang.org/x/crypto/openpgp/packet/private_key_test.go
@@ -56,6 +56,11 @@
}
}
+func TestIssue11505(t *testing.T) {
+ // parsing a rsa private key with p or q == 1 used to panic due to a divide by zero
+ _, _ = Read(readerFromHex("9c3004303030300100000011303030000000000000010130303030303030303030303030303030303030303030303030303030303030303030303030303030303030"))
+}
+
// Generated with `gpg --export-secret-keys "Test Key 2"`
const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec"
diff --git a/go/src/golang.org/x/crypto/openpgp/packet/public_key_v3.go b/go/src/golang.org/x/crypto/openpgp/packet/public_key_v3.go
index 1641248..26337f5 100644
--- a/go/src/golang.org/x/crypto/openpgp/packet/public_key_v3.go
+++ b/go/src/golang.org/x/crypto/openpgp/packet/public_key_v3.go
@@ -95,6 +95,11 @@
return
}
+ // RFC 4880 Section 12.2 requires the low 8 bytes of the
+ // modulus to form the key id.
+ if len(pk.n.bytes) < 8 {
+ return errors.StructuralError("v3 public key modulus is too short")
+ }
if len(pk.e.bytes) > 3 {
err = errors.UnsupportedError("large public exponent")
return
diff --git a/go/src/golang.org/x/crypto/openpgp/read_test.go b/go/src/golang.org/x/crypto/openpgp/read_test.go
index 03bf5d8..3d74ea3 100644
--- a/go/src/golang.org/x/crypto/openpgp/read_test.go
+++ b/go/src/golang.org/x/crypto/openpgp/read_test.go
@@ -8,11 +8,12 @@
"bytes"
_ "crypto/sha512"
"encoding/hex"
- "golang.org/x/crypto/openpgp/errors"
"io"
"io/ioutil"
"strings"
"testing"
+
+ "golang.org/x/crypto/openpgp/errors"
)
func readerFromHex(s string) io.Reader {
@@ -368,6 +369,35 @@
}
}
+func testReadMessageError(t *testing.T, messageHex string) {
+ buf, err := hex.DecodeString(messageHex)
+ if err != nil {
+ t.Errorf("hex.DecodeString(): %v", err)
+ }
+
+ kr, err := ReadKeyRing(new(bytes.Buffer))
+ if err != nil {
+ t.Errorf("ReadKeyring(): %v", err)
+ }
+
+ _, err = ReadMessage(bytes.NewBuffer(buf), kr,
+ func([]Key, bool) ([]byte, error) {
+ return []byte("insecure"), nil
+ }, nil)
+
+ if err == nil {
+ t.Errorf("ReadMessage(): Unexpected nil error")
+ }
+}
+
+func TestIssue11503(t *testing.T) {
+ testReadMessageError(t, "8c040402000aa430aa8228b9248b01fc899a91197130303030")
+}
+
+func TestIssue11504(t *testing.T) {
+ testReadMessageError(t, "9303000130303030303030303030983002303030303030030000000130")
+}
+
const testKey1KeyId = 0xA34D7E18C20C31BB
const testKey3KeyId = 0x338934250CCC0360
diff --git a/go/src/golang.org/x/crypto/openpgp/write_test.go b/go/src/golang.org/x/crypto/openpgp/write_test.go
index 9f8c358..8e9a335 100644
--- a/go/src/golang.org/x/crypto/openpgp/write_test.go
+++ b/go/src/golang.org/x/crypto/openpgp/write_test.go
@@ -10,6 +10,8 @@
"io/ioutil"
"testing"
"time"
+
+ "golang.org/x/crypto/openpgp/packet"
)
func TestSignDetached(t *testing.T) {
@@ -53,11 +55,34 @@
return
}
+ // Check bit-length with no config.
e, err := NewEntity("Test User", "test", "test@example.com", nil)
if err != nil {
t.Errorf("failed to create entity: %s", err)
return
}
+ bl, err := e.PrimaryKey.BitLength()
+ if err != nil {
+ t.Errorf("failed to find bit length: %s", err)
+ }
+ if int(bl) != defaultRSAKeyBits {
+ t.Errorf("BitLength %v, expected %v", defaultRSAKeyBits)
+ }
+
+ // Check bit-length with a config.
+ cfg := &packet.Config{RSABits: 1024}
+ e, err = NewEntity("Test User", "test", "test@example.com", cfg)
+ if err != nil {
+ t.Errorf("failed to create entity: %s", err)
+ return
+ }
+ bl, err = e.PrimaryKey.BitLength()
+ if err != nil {
+ t.Errorf("failed to find bit length: %s", err)
+ }
+ if int(bl) != cfg.RSABits {
+ t.Errorf("BitLength %v, expected %v", bl, cfg.RSABits)
+ }
w := bytes.NewBuffer(nil)
if err := e.SerializePrivate(w, nil); err != nil {
diff --git a/go/src/golang.org/x/crypto/poly1305/poly1305_arm.s b/go/src/golang.org/x/crypto/poly1305/poly1305_arm.s
index c9ceaeb..c153867 100644
--- a/go/src/golang.org/x/crypto/poly1305/poly1305_arm.s
+++ b/go/src/golang.org/x/crypto/poly1305/poly1305_arm.s
@@ -47,6 +47,16 @@
MOVM.IA.W (R13), [R4-R11]
RET
+#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \
+ MOVBU (offset+0)(Rsrc), Rtmp; \
+ MOVBU Rtmp, (offset+0)(Rdst); \
+ MOVBU (offset+1)(Rsrc), Rtmp; \
+ MOVBU Rtmp, (offset+1)(Rdst); \
+ MOVBU (offset+2)(Rsrc), Rtmp; \
+ MOVBU Rtmp, (offset+2)(Rdst); \
+ MOVBU (offset+3)(Rsrc), Rtmp; \
+ MOVBU Rtmp, (offset+3)(Rdst)
+
TEXT poly1305_blocks_armv6<>(SB),4,$-4
MOVM.DB.W [R4, R5, R6, R7, R8, R9, g, R11, R14], (R13)
SUB $128, R13
@@ -66,7 +76,19 @@
CMP $16, R12
BLO poly1305_blocks_armv6_done
poly1305_blocks_armv6_mainloop:
+ WORD $0xe31e0003 // TST R14, #3 not working see issue 5921
+ BEQ poly1305_blocks_armv6_mainloop_aligned
+ ADD $48, R13, g
+ MOVW_UNALIGNED(R14, g, R0, 0)
+ MOVW_UNALIGNED(R14, g, R0, 4)
+ MOVW_UNALIGNED(R14, g, R0, 8)
+ MOVW_UNALIGNED(R14, g, R0, 12)
+ MOVM.IA (g), [R0-R3]
+ ADD $16, R14
+ B poly1305_blocks_armv6_mainloop_loaded
+poly1305_blocks_armv6_mainloop_aligned:
MOVM.IA.W (R14), [R0-R3]
+poly1305_blocks_armv6_mainloop_loaded:
MOVW R0>>26, g
MOVW R1>>20, R11
MOVW R2>>14, R12
@@ -174,6 +196,16 @@
MOVM.IA.W (R13), [R4, R5, R6, R7, R8, R9, g, R11, R14]
RET
+#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \
+ MOVBU.P 1(Rsrc), Rtmp; \
+ MOVBU.P Rtmp, 1(Rdst); \
+ MOVBU.P 1(Rsrc), Rtmp; \
+ MOVBU.P Rtmp, 1(Rdst)
+
+#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \
+ MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \
+ MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp)
+
TEXT poly1305_finish_ext_armv6<>(SB),4,$-4
MOVM.DB.W [R4, R5, R6, R7, R8, R9, g, R11, R14], (R13)
SUB $16, R13, R13
@@ -189,16 +221,32 @@
MOVW R0, 4(R13)
MOVW R0, 8(R13)
MOVW R0, 12(R13)
+ WORD $0xe3110003 // TST R1, #3 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_aligned
WORD $0xe3120008 // TST R2, #8 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip8
- MOVM.IA.W (R1), [g-R11]
- MOVM.IA.W [g-R11], (R9)
+ MOVWP_UNALIGNED(R1, R9, g)
+ MOVWP_UNALIGNED(R1, R9, g)
poly1305_finish_ext_armv6_skip8:
WORD $0xe3120004 // TST $4, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip4
+ MOVWP_UNALIGNED(R1, R9, g)
+poly1305_finish_ext_armv6_skip4:
+ WORD $0xe3120002 // TST $2, R2 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip2
+ MOVHUP_UNALIGNED(R1, R9, g)
+ B poly1305_finish_ext_armv6_skip2
+poly1305_finish_ext_armv6_aligned:
+ WORD $0xe3120008 // TST R2, #8 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip8_aligned
+ MOVM.IA.W (R1), [g-R11]
+ MOVM.IA.W [g-R11], (R9)
+poly1305_finish_ext_armv6_skip8_aligned:
+ WORD $0xe3120004 // TST $4, R2 not working see issue 5921
+ BEQ poly1305_finish_ext_armv6_skip4_aligned
MOVW.P 4(R1), g
MOVW.P g, 4(R9)
-poly1305_finish_ext_armv6_skip4:
+poly1305_finish_ext_armv6_skip4_aligned:
WORD $0xe3120002 // TST $2, R2 not working see issue 5921
BEQ poly1305_finish_ext_armv6_skip2
MOVHU.P 2(R1), g
diff --git a/go/src/golang.org/x/crypto/poly1305/poly1305_test.go b/go/src/golang.org/x/crypto/poly1305/poly1305_test.go
index 2c6d1bc..b3e9231 100644
--- a/go/src/golang.org/x/crypto/poly1305/poly1305_test.go
+++ b/go/src/golang.org/x/crypto/poly1305/poly1305_test.go
@@ -7,6 +7,7 @@
import (
"bytes"
"testing"
+ "unsafe"
)
var testData = []struct {
@@ -34,41 +35,52 @@
},
}
-func TestSum(t *testing.T) {
+func testSum(t *testing.T, unaligned bool) {
var out [16]byte
var key [32]byte
for i, v := range testData {
+ in := v.in
+ if unaligned {
+ in = unalignBytes(in)
+ }
copy(key[:], v.k)
- Sum(&out, v.in, &key)
+ Sum(&out, in, &key)
if !bytes.Equal(out[:], v.correct) {
t.Errorf("%d: expected %x, got %x", i, v.correct, out[:])
}
}
}
-func Benchmark1K(b *testing.B) {
- b.StopTimer()
+func TestSum(t *testing.T) { testSum(t, false) }
+func TestSumUnaligned(t *testing.T) { testSum(t, true) }
+
+func benchmark(b *testing.B, size int, unaligned bool) {
var out [16]byte
var key [32]byte
- in := make([]byte, 1024)
+ in := make([]byte, size)
+ if unaligned {
+ in = unalignBytes(in)
+ }
b.SetBytes(int64(len(in)))
- b.StartTimer()
-
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
Sum(&out, in, &key)
}
}
-func Benchmark64(b *testing.B) {
- b.StopTimer()
- var out [16]byte
- var key [32]byte
- in := make([]byte, 64)
- b.SetBytes(int64(len(in)))
- b.StartTimer()
+func Benchmark64(b *testing.B) { benchmark(b, 64, false) }
+func Benchmark1K(b *testing.B) { benchmark(b, 1024, false) }
+func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) }
+func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) }
- for i := 0; i < b.N; i++ {
- Sum(&out, in, &key)
+func unalignBytes(in []byte) []byte {
+ out := make([]byte, len(in)+1)
+ if uintptr(unsafe.Pointer(&out[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
+ out = out[1:]
+ } else {
+ out = out[:len(in)]
}
+ copy(out, in)
+ return out
}
diff --git a/go/src/golang.org/x/crypto/sha3/keccakKats.json.deflate b/go/src/golang.org/x/crypto/sha3/keccakKats.json.deflate
deleted file mode 100644
index 62e85ae..0000000
--- a/go/src/golang.org/x/crypto/sha3/keccakKats.json.deflate
+++ /dev/null
Binary files differ
diff --git a/go/src/golang.org/x/crypto/ssh/agent/client.go b/go/src/golang.org/x/crypto/ssh/agent/client.go
index f5527e8..8c856a0 100644
--- a/go/src/golang.org/x/crypto/ssh/agent/client.go
+++ b/go/src/golang.org/x/crypto/ssh/agent/client.go
@@ -36,9 +36,8 @@
// in [PROTOCOL.agent] section 2.6.2.
Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
- // Add adds a private key to the agent. If a certificate
- // is given, that certificate is added as public key.
- Add(s interface{}, cert *ssh.Certificate, comment string) error
+ // Add adds a private key to the agent.
+ Add(key AddedKey) error
// Remove removes all identities with the given public key.
Remove(key ssh.PublicKey) error
@@ -56,6 +55,24 @@
Signers() ([]ssh.Signer, error)
}
+// AddedKey describes an SSH key to be added to an Agent.
+type AddedKey struct {
+ // PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
+ // *ecdsa.PrivateKey, which will be inserted into the agent.
+ PrivateKey interface{}
+ // Certificate, if not nil, is communicated to the agent and will be
+ // stored with the key.
+ Certificate *ssh.Certificate
+ // Comment is an optional, free-form string.
+ Comment string
+ // LifetimeSecs, if not zero, is the number of seconds that the
+ // agent will store the key for.
+ LifetimeSecs uint32
+ // ConfirmBeforeUse, if true, requests that the agent confirm with the
+ // user before each use of this key.
+ ConfirmBeforeUse bool
+}
+
// See [PROTOCOL.agent], section 3.
const (
agentRequestV1Identities = 1
@@ -368,36 +385,39 @@
}
type rsaKeyMsg struct {
- Type string `sshtype:"17"`
- N *big.Int
- E *big.Int
- D *big.Int
- Iqmp *big.Int // IQMP = Inverse Q Mod P
- P *big.Int
- Q *big.Int
- Comments string
+ Type string `sshtype:"17"`
+ N *big.Int
+ E *big.Int
+ D *big.Int
+ Iqmp *big.Int // IQMP = Inverse Q Mod P
+ P *big.Int
+ Q *big.Int
+ Comments string
+ Constraints []byte `ssh:"rest"`
}
type dsaKeyMsg struct {
- Type string `sshtype:"17"`
- P *big.Int
- Q *big.Int
- G *big.Int
- Y *big.Int
- X *big.Int
- Comments string
+ Type string `sshtype:"17"`
+ P *big.Int
+ Q *big.Int
+ G *big.Int
+ Y *big.Int
+ X *big.Int
+ Comments string
+ Constraints []byte `ssh:"rest"`
}
type ecdsaKeyMsg struct {
- Type string `sshtype:"17"`
- Curve string
- KeyBytes []byte
- D *big.Int
- Comments string
+ Type string `sshtype:"17"`
+ Curve string
+ KeyBytes []byte
+ D *big.Int
+ Comments string
+ Constraints []byte `ssh:"rest"`
}
// Insert adds a private key to the agent.
-func (c *client) insertKey(s interface{}, comment string) error {
+func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
var req []byte
switch k := s.(type) {
case *rsa.PrivateKey:
@@ -406,37 +426,46 @@
}
k.Precompute()
req = ssh.Marshal(rsaKeyMsg{
- Type: ssh.KeyAlgoRSA,
- N: k.N,
- E: big.NewInt(int64(k.E)),
- D: k.D,
- Iqmp: k.Precomputed.Qinv,
- P: k.Primes[0],
- Q: k.Primes[1],
- Comments: comment,
+ Type: ssh.KeyAlgoRSA,
+ N: k.N,
+ E: big.NewInt(int64(k.E)),
+ D: k.D,
+ Iqmp: k.Precomputed.Qinv,
+ P: k.Primes[0],
+ Q: k.Primes[1],
+ Comments: comment,
+ Constraints: constraints,
})
case *dsa.PrivateKey:
req = ssh.Marshal(dsaKeyMsg{
- Type: ssh.KeyAlgoDSA,
- P: k.P,
- Q: k.Q,
- G: k.G,
- Y: k.Y,
- X: k.X,
- Comments: comment,
+ Type: ssh.KeyAlgoDSA,
+ P: k.P,
+ Q: k.Q,
+ G: k.G,
+ Y: k.Y,
+ X: k.X,
+ Comments: comment,
+ Constraints: constraints,
})
case *ecdsa.PrivateKey:
nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
req = ssh.Marshal(ecdsaKeyMsg{
- Type: "ecdsa-sha2-" + nistID,
- Curve: nistID,
- KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y),
- D: k.D,
- Comments: comment,
+ Type: "ecdsa-sha2-" + nistID,
+ Curve: nistID,
+ KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y),
+ D: k.D,
+ Comments: comment,
+ Constraints: constraints,
})
default:
return fmt.Errorf("agent: unsupported key type %T", s)
}
+
+ // if constraints are present then the message type needs to be changed.
+ if len(constraints) != 0 {
+ req[0] = agentAddIdConstrained
+ }
+
resp, err := c.call(req)
if err != nil {
return err
@@ -448,40 +477,57 @@
}
type rsaCertMsg struct {
- Type string `sshtype:"17"`
- CertBytes []byte
- D *big.Int
- Iqmp *big.Int // IQMP = Inverse Q Mod P
- P *big.Int
- Q *big.Int
- Comments string
+ Type string `sshtype:"17"`
+ CertBytes []byte
+ D *big.Int
+ Iqmp *big.Int // IQMP = Inverse Q Mod P
+ P *big.Int
+ Q *big.Int
+ Comments string
+ Constraints []byte `ssh:"rest"`
}
type dsaCertMsg struct {
- Type string `sshtype:"17"`
- CertBytes []byte
- X *big.Int
- Comments string
+ Type string `sshtype:"17"`
+ CertBytes []byte
+ X *big.Int
+ Comments string
+ Constraints []byte `ssh:"rest"`
}
type ecdsaCertMsg struct {
- Type string `sshtype:"17"`
- CertBytes []byte
- D *big.Int
- Comments string
+ Type string `sshtype:"17"`
+ CertBytes []byte
+ D *big.Int
+ Comments string
+ Constraints []byte `ssh:"rest"`
}
// Insert adds a private key to the agent. If a certificate is given,
// that certificate is added instead as public key.
-func (c *client) Add(s interface{}, cert *ssh.Certificate, comment string) error {
- if cert == nil {
- return c.insertKey(s, comment)
+func (c *client) Add(key AddedKey) error {
+ var constraints []byte
+
+ if secs := key.LifetimeSecs; secs != 0 {
+ constraints = append(constraints, agentConstrainLifetime)
+
+ var secsBytes [4]byte
+ binary.BigEndian.PutUint32(secsBytes[:], secs)
+ constraints = append(constraints, secsBytes[:]...)
+ }
+
+ if key.ConfirmBeforeUse {
+ constraints = append(constraints, agentConstrainConfirm)
+ }
+
+ if cert := key.Certificate; cert == nil {
+ return c.insertKey(key.PrivateKey, key.Comment, constraints)
} else {
- return c.insertCert(s, cert, comment)
+ return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
}
}
-func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string) error {
+func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
var req []byte
switch k := s.(type) {
case *rsa.PrivateKey:
@@ -490,13 +536,14 @@
}
k.Precompute()
req = ssh.Marshal(rsaCertMsg{
- Type: cert.Type(),
- CertBytes: cert.Marshal(),
- D: k.D,
- Iqmp: k.Precomputed.Qinv,
- P: k.Primes[0],
- Q: k.Primes[1],
- Comments: comment,
+ Type: cert.Type(),
+ CertBytes: cert.Marshal(),
+ D: k.D,
+ Iqmp: k.Precomputed.Qinv,
+ P: k.Primes[0],
+ Q: k.Primes[1],
+ Comments: comment,
+ Constraints: constraints,
})
case *dsa.PrivateKey:
req = ssh.Marshal(dsaCertMsg{
@@ -516,6 +563,11 @@
return fmt.Errorf("agent: unsupported key type %T", s)
}
+ // if constraints are present then the message type needs to be changed.
+ if len(constraints) != 0 {
+ req[0] = agentAddIdConstrained
+ }
+
signer, err := ssh.NewSignerFromKey(s)
if err != nil {
return err
diff --git a/go/src/golang.org/x/crypto/ssh/agent/client_test.go b/go/src/golang.org/x/crypto/ssh/agent/client_test.go
index 80e2c2c..ec7198d 100644
--- a/go/src/golang.org/x/crypto/ssh/agent/client_test.go
+++ b/go/src/golang.org/x/crypto/ssh/agent/client_test.go
@@ -78,14 +78,14 @@
}
}
-func testAgent(t *testing.T, key interface{}, cert *ssh.Certificate) {
+func testAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
agent, _, cleanup := startAgent(t)
defer cleanup()
- testAgentInterface(t, agent, key, cert)
+ testAgentInterface(t, agent, key, cert, lifetimeSecs)
}
-func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate) {
+func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
signer, err := ssh.NewSignerFromKey(key)
if err != nil {
t.Fatalf("NewSignerFromKey(%T): %v", key, err)
@@ -100,10 +100,15 @@
// Attempt to insert the key, with certificate if specified.
var pubKey ssh.PublicKey
if cert != nil {
- err = agent.Add(key, cert, "comment")
+ err = agent.Add(AddedKey{
+ PrivateKey: key,
+ Certificate: cert,
+ Comment: "comment",
+ LifetimeSecs: lifetimeSecs,
+ })
pubKey = cert
} else {
- err = agent.Add(key, nil, "comment")
+ err = agent.Add(AddedKey{PrivateKey: key, Comment: "comment", LifetimeSecs: lifetimeSecs})
pubKey = signer.PublicKey()
}
if err != nil {
@@ -135,7 +140,7 @@
func TestAgent(t *testing.T) {
for _, keyType := range []string{"rsa", "dsa", "ecdsa"} {
- testAgent(t, testPrivateKeys[keyType], nil)
+ testAgent(t, testPrivateKeys[keyType], nil, 0)
}
}
@@ -147,7 +152,11 @@
}
cert.SignCert(rand.Reader, testSigners["ecdsa"])
- testAgent(t, testPrivateKeys["rsa"], cert)
+ testAgent(t, testPrivateKeys["rsa"], cert, 0)
+}
+
+func TestConstraints(t *testing.T) {
+ testAgent(t, testPrivateKeys["rsa"], nil, 3600 /* lifetime in seconds */)
}
// netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
@@ -185,7 +194,7 @@
agent, _, cleanup := startAgent(t)
defer cleanup()
- if err := agent.Add(testPrivateKeys["rsa"], nil, "comment"); err != nil {
+ if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil {
t.Errorf("Add: %v", err)
}
@@ -223,10 +232,10 @@
}
func testLockAgent(agent Agent, t *testing.T) {
- if err := agent.Add(testPrivateKeys["rsa"], nil, "comment 1"); err != nil {
+ if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment 1"}); err != nil {
t.Errorf("Add: %v", err)
}
- if err := agent.Add(testPrivateKeys["dsa"], nil, "comment dsa"); err != nil {
+ if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["dsa"], Comment: "comment dsa"}); err != nil {
t.Errorf("Add: %v", err)
}
if keys, err := agent.List(); err != nil {
diff --git a/go/src/golang.org/x/crypto/ssh/agent/keyring.go b/go/src/golang.org/x/crypto/ssh/agent/keyring.go
index 8ac2009..11b72df 100644
--- a/go/src/golang.org/x/crypto/ssh/agent/keyring.go
+++ b/go/src/golang.org/x/crypto/ssh/agent/keyring.go
@@ -125,27 +125,28 @@
}
// Insert adds a private key to the keyring. If a certificate
-// is given, that certificate is added as public key.
-func (r *keyring) Add(priv interface{}, cert *ssh.Certificate, comment string) error {
+// is given, that certificate is added as public key. Note that
+// any constraints given are ignored.
+func (r *keyring) Add(key AddedKey) error {
r.mu.Lock()
defer r.mu.Unlock()
if r.locked {
return errLocked
}
- signer, err := ssh.NewSignerFromKey(priv)
+ signer, err := ssh.NewSignerFromKey(key.PrivateKey)
if err != nil {
return err
}
- if cert != nil {
+ if cert := key.Certificate; cert != nil {
signer, err = ssh.NewCertSigner(cert, signer)
if err != nil {
return err
}
}
- r.keys = append(r.keys, privKey{signer, comment})
+ r.keys = append(r.keys, privKey{signer, key.Comment})
return nil
}
diff --git a/go/src/golang.org/x/crypto/ssh/agent/server.go b/go/src/golang.org/x/crypto/ssh/agent/server.go
index be9df0e..b21a201 100644
--- a/go/src/golang.org/x/crypto/ssh/agent/server.go
+++ b/go/src/golang.org/x/crypto/ssh/agent/server.go
@@ -167,7 +167,7 @@
}
priv.Precompute()
- return s.agent.Add(&priv, nil, k.Comments)
+ return s.agent.Add(AddedKey{PrivateKey: &priv, Comment: k.Comments})
}
return fmt.Errorf("not implemented: %s", record.Type)
}
diff --git a/go/src/golang.org/x/crypto/ssh/agent/server_test.go b/go/src/golang.org/x/crypto/ssh/agent/server_test.go
index def5f8c..ef0ab29 100644
--- a/go/src/golang.org/x/crypto/ssh/agent/server_test.go
+++ b/go/src/golang.org/x/crypto/ssh/agent/server_test.go
@@ -21,7 +21,7 @@
go ServeAgent(NewKeyring(), c2)
- testAgentInterface(t, client, testPrivateKeys["rsa"], nil)
+ testAgentInterface(t, client, testPrivateKeys["rsa"], nil, 0)
}
func TestLockServer(t *testing.T) {
@@ -72,6 +72,6 @@
go ssh.DiscardRequests(reqs)
agentClient := NewClient(ch)
- testAgentInterface(t, agentClient, testPrivateKeys["rsa"], nil)
+ testAgentInterface(t, agentClient, testPrivateKeys["rsa"], nil, 0)
conn.Close()
}
diff --git a/go/src/golang.org/x/crypto/ssh/certs_test.go b/go/src/golang.org/x/crypto/ssh/certs_test.go
index d6c4a33..c5f2e53 100644
--- a/go/src/golang.org/x/crypto/ssh/certs_test.go
+++ b/go/src/golang.org/x/crypto/ssh/certs_test.go
@@ -186,15 +186,15 @@
defer c1.Close()
defer c2.Close()
+ errc := make(chan error)
+
go func() {
conf := ServerConfig{
NoClientAuth: true,
}
conf.AddHostKey(certSigner)
_, _, _, err := NewServerConn(c1, &conf)
- if err != nil {
- t.Fatalf("NewServerConn: %v", err)
- }
+ errc <- err
}()
config := &ClientConfig{
@@ -207,5 +207,10 @@
if (err == nil) != succeed {
t.Fatalf("NewClientConn(%q): %v", name, err)
}
+
+ err = <-errc
+ if (err == nil) != succeed {
+ t.Fatalf("NewServerConn(%q): %v", name, err)
+ }
}
}
diff --git a/go/src/golang.org/x/crypto/ssh/client.go b/go/src/golang.org/x/crypto/ssh/client.go
index 72bd27f..0b9fbe5 100644
--- a/go/src/golang.org/x/crypto/ssh/client.go
+++ b/go/src/golang.org/x/crypto/ssh/client.go
@@ -203,4 +203,11 @@
// ClientVersion contains the version identification string that will
// be used for the connection. If empty, a reasonable default is used.
ClientVersion string
+
+ // HostKeyAlgorithms lists the key types that the client will
+ // accept from the server as host key, in order of
+ // preference. If empty, a reasonable default is used. Any
+ // string returned from PublicKey.Type method may be used, or
+ // any of the CertAlgoXxxx and KeyAlgoXxxx constants.
+ HostKeyAlgorithms []string
}
diff --git a/go/src/golang.org/x/crypto/ssh/common.go b/go/src/golang.org/x/crypto/ssh/common.go
index 0a9df1f..1aef9c0 100644
--- a/go/src/golang.org/x/crypto/ssh/common.go
+++ b/go/src/golang.org/x/crypto/ssh/common.go
@@ -33,6 +33,7 @@
// supportedKexAlgos specifies the supported key-exchange algorithms in
// preference order.
var supportedKexAlgos = []string{
+ kexAlgoCurve25519SHA256,
// P384 and P521 are not constant-time yet, but since we don't
// reuse ephemeral keys, using them for ECDH should be OK.
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
diff --git a/go/src/golang.org/x/crypto/ssh/handshake.go b/go/src/golang.org/x/crypto/ssh/handshake.go
index a1e2c23..12633c8 100644
--- a/go/src/golang.org/x/crypto/ssh/handshake.go
+++ b/go/src/golang.org/x/crypto/ssh/handshake.go
@@ -59,7 +59,14 @@
serverVersion []byte
clientVersion []byte
- hostKeys []Signer // If hostKeys are given, we are the server.
+ // hostKeys is non-empty if we are the server. In that case,
+ // it contains all host keys that can be used to sign the
+ // connection.
+ hostKeys []Signer
+
+ // hostKeyAlgorithms is non-empty if we are the client. In that case,
+ // we accept these key types from the server as host key.
+ hostKeyAlgorithms []string
// On read error, incoming is closed, and readError is set.
incoming chan []byte
@@ -98,6 +105,11 @@
t.dialAddress = dialAddr
t.remoteAddr = addr
t.hostKeyCallback = config.HostKeyCallback
+ if config.HostKeyAlgorithms != nil {
+ t.hostKeyAlgorithms = config.HostKeyAlgorithms
+ } else {
+ t.hostKeyAlgorithms = supportedHostKeyAlgos
+ }
go t.readLoop()
return t
}
@@ -141,6 +153,14 @@
}
t.incoming <- p
}
+
+ // If we can't read, declare the writing part dead too.
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.writeError == nil {
+ t.writeError = t.readError
+ }
+ t.cond.Broadcast()
}
func (t *handshakeTransport) readOnePacket() ([]byte, error) {
@@ -234,7 +254,7 @@
msg.ServerHostKeyAlgos, k.PublicKey().Type())
}
} else {
- msg.ServerHostKeyAlgos = supportedHostKeyAlgos
+ msg.ServerHostKeyAlgos = t.hostKeyAlgorithms
}
packet := Marshal(msg)
@@ -253,10 +273,12 @@
func (t *handshakeTransport) writePacket(p []byte) error {
t.mu.Lock()
+ defer t.mu.Unlock()
+
if t.writtenSinceKex > t.config.RekeyThreshold {
t.sendKexInitLocked()
}
- for t.sentInitMsg != nil {
+ for t.sentInitMsg != nil && t.writeError == nil {
t.cond.Wait()
}
if t.writeError != nil {
@@ -264,17 +286,14 @@
}
t.writtenSinceKex += uint64(len(p))
- var err error
switch p[0] {
case msgKexInit:
- err = errors.New("ssh: only handshakeTransport can send kexInit")
+ return errors.New("ssh: only handshakeTransport can send kexInit")
case msgNewKeys:
- err = errors.New("ssh: only handshakeTransport can send newKeys")
+ return errors.New("ssh: only handshakeTransport can send newKeys")
default:
- err = t.conn.writePacket(p)
+ return t.conn.writePacket(p)
}
- t.mu.Unlock()
- return err
}
func (t *handshakeTransport) Close() error {
diff --git a/go/src/golang.org/x/crypto/ssh/handshake_test.go b/go/src/golang.org/x/crypto/ssh/handshake_test.go
index 613c498..b86d369 100644
--- a/go/src/golang.org/x/crypto/ssh/handshake_test.go
+++ b/go/src/golang.org/x/crypto/ssh/handshake_test.go
@@ -7,8 +7,12 @@
import (
"bytes"
"crypto/rand"
+ "errors"
"fmt"
"net"
+ "runtime"
+ "strings"
+ "sync"
"testing"
)
@@ -68,6 +72,7 @@
serverConf := &ServerConfig{}
serverConf.AddHostKey(testSigners["ecdsa"])
+ serverConf.AddHostKey(testSigners["rsa"])
serverConf.SetDefaults()
server = newServerTransport(trS, v, v, serverConf)
@@ -75,6 +80,9 @@
}
func TestHandshakeBasic(t *testing.T) {
+ if runtime.GOOS == "plan9" {
+ t.Skip("see golang.org/issue/7237")
+ }
checker := &testChecker{}
trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr")
if err != nil {
@@ -309,3 +317,99 @@
<-sync.called
}
+
+// errorKeyingTransport generates errors after a given number of
+// read/write operations.
+type errorKeyingTransport struct {
+ packetConn
+ readLeft, writeLeft int
+}
+
+func (n *errorKeyingTransport) prepareKeyChange(*algorithms, *kexResult) error {
+ return nil
+}
+func (n *errorKeyingTransport) getSessionID() []byte {
+ return nil
+}
+
+func (n *errorKeyingTransport) writePacket(packet []byte) error {
+ if n.writeLeft == 0 {
+ n.Close()
+ return errors.New("barf")
+ }
+
+ n.writeLeft--
+ return n.packetConn.writePacket(packet)
+}
+
+func (n *errorKeyingTransport) readPacket() ([]byte, error) {
+ if n.readLeft == 0 {
+ n.Close()
+ return nil, errors.New("barf")
+ }
+
+ n.readLeft--
+ return n.packetConn.readPacket()
+}
+
+func TestHandshakeErrorHandlingRead(t *testing.T) {
+ for i := 0; i < 20; i++ {
+ testHandshakeErrorHandlingN(t, i, -1)
+ }
+}
+
+func TestHandshakeErrorHandlingWrite(t *testing.T) {
+ for i := 0; i < 20; i++ {
+ testHandshakeErrorHandlingN(t, -1, i)
+ }
+}
+
+// testHandshakeErrorHandlingN runs handshakes, injecting errors. If
+// handshakeTransport deadlocks, the go runtime will detect it and
+// panic.
+func testHandshakeErrorHandlingN(t *testing.T, readLimit, writeLimit int) {
+ msg := Marshal(&serviceRequestMsg{strings.Repeat("x", int(minRekeyThreshold)/4)})
+
+ a, b := memPipe()
+ defer a.Close()
+ defer b.Close()
+
+ key := testSigners["ecdsa"]
+ serverConf := Config{RekeyThreshold: minRekeyThreshold}
+ serverConf.SetDefaults()
+ serverConn := newHandshakeTransport(&errorKeyingTransport{a, readLimit, writeLimit}, &serverConf, []byte{'a'}, []byte{'b'})
+ serverConn.hostKeys = []Signer{key}
+ go serverConn.readLoop()
+
+ clientConf := Config{RekeyThreshold: 10 * minRekeyThreshold}
+ clientConf.SetDefaults()
+ clientConn := newHandshakeTransport(&errorKeyingTransport{b, -1, -1}, &clientConf, []byte{'a'}, []byte{'b'})
+ clientConn.hostKeyAlgorithms = []string{key.PublicKey().Type()}
+ go clientConn.readLoop()
+
+ var wg sync.WaitGroup
+ wg.Add(4)
+
+ for _, hs := range []packetConn{serverConn, clientConn} {
+ go func(c packetConn) {
+ for {
+ err := c.writePacket(msg)
+ if err != nil {
+ break
+ }
+ }
+ wg.Done()
+ }(hs)
+ go func(c packetConn) {
+ for {
+ _, err := c.readPacket()
+ if err != nil {
+ break
+ }
+ }
+ wg.Done()
+ }(hs)
+ }
+
+ wg.Wait()
+}
diff --git a/go/src/golang.org/x/crypto/ssh/kex.go b/go/src/golang.org/x/crypto/ssh/kex.go
index 6a835c7..ea19d53 100644
--- a/go/src/golang.org/x/crypto/ssh/kex.go
+++ b/go/src/golang.org/x/crypto/ssh/kex.go
@@ -8,18 +8,22 @@
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
+ "crypto/subtle"
"crypto/rand"
"errors"
"io"
"math/big"
+
+ "golang.org/x/crypto/curve25519"
)
const (
- kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
- kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
- kexAlgoECDH256 = "ecdh-sha2-nistp256"
- kexAlgoECDH384 = "ecdh-sha2-nistp384"
- kexAlgoECDH521 = "ecdh-sha2-nistp521"
+ kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
+ kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
+ kexAlgoECDH256 = "ecdh-sha2-nistp256"
+ kexAlgoECDH384 = "ecdh-sha2-nistp384"
+ kexAlgoECDH521 = "ecdh-sha2-nistp521"
+ kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org"
)
// kexResult captures the outcome of a key exchange.
@@ -383,4 +387,140 @@
kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
+ kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
+}
+
+// curve25519sha256 implements the curve25519-sha256@libssh.org key
+// agreement protocol, as described in
+// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt
+type curve25519sha256 struct{}
+
+type curve25519KeyPair struct {
+ priv [32]byte
+ pub [32]byte
+}
+
+func (kp *curve25519KeyPair) generate(rand io.Reader) error {
+ if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
+ return err
+ }
+ curve25519.ScalarBaseMult(&kp.pub, &kp.priv)
+ return nil
+}
+
+// curve25519Zeros is just an array of 32 zero bytes so that we have something
+// convenient to compare against in order to reject curve25519 points with the
+// wrong order.
+var curve25519Zeros [32]byte
+
+func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
+ var kp curve25519KeyPair
+ if err := kp.generate(rand); err != nil {
+ return nil, err
+ }
+ if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil {
+ return nil, err
+ }
+
+ packet, err := c.readPacket()
+ if err != nil {
+ return nil, err
+ }
+
+ var reply kexECDHReplyMsg
+ if err = Unmarshal(packet, &reply); err != nil {
+ return nil, err
+ }
+ if len(reply.EphemeralPubKey) != 32 {
+ return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
+ }
+
+ var servPub, secret [32]byte
+ copy(servPub[:], reply.EphemeralPubKey)
+ curve25519.ScalarMult(&secret, &kp.priv, &servPub)
+ if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
+ return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
+ }
+
+ h := crypto.SHA256.New()
+ magics.write(h)
+ writeString(h, reply.HostKey)
+ writeString(h, kp.pub[:])
+ writeString(h, reply.EphemeralPubKey)
+
+ kInt := new(big.Int).SetBytes(secret[:])
+ K := make([]byte, intLength(kInt))
+ marshalInt(K, kInt)
+ h.Write(K)
+
+ return &kexResult{
+ H: h.Sum(nil),
+ K: K,
+ HostKey: reply.HostKey,
+ Signature: reply.Signature,
+ Hash: crypto.SHA256,
+ }, nil
+}
+
+func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
+ packet, err := c.readPacket()
+ if err != nil {
+ return
+ }
+ var kexInit kexECDHInitMsg
+ if err = Unmarshal(packet, &kexInit); err != nil {
+ return
+ }
+
+ if len(kexInit.ClientPubKey) != 32 {
+ return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
+ }
+
+ var kp curve25519KeyPair
+ if err := kp.generate(rand); err != nil {
+ return nil, err
+ }
+
+ var clientPub, secret [32]byte
+ copy(clientPub[:], kexInit.ClientPubKey)
+ curve25519.ScalarMult(&secret, &kp.priv, &clientPub)
+ if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
+ return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
+ }
+
+ hostKeyBytes := priv.PublicKey().Marshal()
+
+ h := crypto.SHA256.New()
+ magics.write(h)
+ writeString(h, hostKeyBytes)
+ writeString(h, kexInit.ClientPubKey)
+ writeString(h, kp.pub[:])
+
+ kInt := new(big.Int).SetBytes(secret[:])
+ K := make([]byte, intLength(kInt))
+ marshalInt(K, kInt)
+ h.Write(K)
+
+ H := h.Sum(nil)
+
+ sig, err := signAndMarshal(priv, rand, H)
+ if err != nil {
+ return nil, err
+ }
+
+ reply := kexECDHReplyMsg{
+ EphemeralPubKey: kp.pub[:],
+ HostKey: hostKeyBytes,
+ Signature: sig,
+ }
+ if err := c.writePacket(Marshal(&reply)); err != nil {
+ return nil, err
+ }
+ return &kexResult{
+ H: H,
+ K: K,
+ HostKey: hostKeyBytes,
+ Signature: sig,
+ Hash: crypto.SHA256,
+ }, nil
}
diff --git a/go/src/golang.org/x/crypto/ssh/kex_test.go b/go/src/golang.org/x/crypto/ssh/kex_test.go
index 0db5f9b..12ca0ac 100644
--- a/go/src/golang.org/x/crypto/ssh/kex_test.go
+++ b/go/src/golang.org/x/crypto/ssh/kex_test.go
@@ -26,10 +26,12 @@
var magics handshakeMagics
go func() {
r, e := kex.Client(a, rand.Reader, &magics)
+ a.Close()
c <- kexResultErr{r, e}
}()
go func() {
r, e := kex.Server(b, rand.Reader, &magics, testSigners["ecdsa"])
+ b.Close()
s <- kexResultErr{r, e}
}()
diff --git a/go/src/golang.org/x/crypto/ssh/mempipe_test.go b/go/src/golang.org/x/crypto/ssh/mempipe_test.go
index 92519dd..8697cd6 100644
--- a/go/src/golang.org/x/crypto/ssh/mempipe_test.go
+++ b/go/src/golang.org/x/crypto/ssh/mempipe_test.go
@@ -76,7 +76,7 @@
return &t1, &t2
}
-func TestmemPipe(t *testing.T) {
+func TestMemPipe(t *testing.T) {
a, b := memPipe()
if err := a.writePacket([]byte{42}); err != nil {
t.Fatalf("writePacket: %v", err)
diff --git a/go/src/golang.org/x/crypto/ssh/session_test.go b/go/src/golang.org/x/crypto/ssh/session_test.go
index 7ce44f5..f7f0f76 100644
--- a/go/src/golang.org/x/crypto/ssh/session_test.go
+++ b/go/src/golang.org/x/crypto/ssh/session_test.go
@@ -718,3 +718,57 @@
t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing authentication method")
}
}
+
+func TestHostKeyAlgorithms(t *testing.T) {
+ serverConf := &ServerConfig{
+ NoClientAuth: true,
+ }
+ serverConf.AddHostKey(testSigners["rsa"])
+ serverConf.AddHostKey(testSigners["ecdsa"])
+
+ connect := func(clientConf *ClientConfig, want string) {
+ var alg string
+ clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error {
+ alg = key.Type()
+ return nil
+ }
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ go NewServerConn(c1, serverConf)
+ _, _, _, err = NewClientConn(c2, "", clientConf)
+ if err != nil {
+ t.Fatalf("NewClientConn: %v", err)
+ }
+ if alg != want {
+ t.Errorf("selected key algorithm %s, want %s", alg, want)
+ }
+ }
+
+ // By default, we get the preferred algorithm, which is ECDSA 256.
+
+ clientConf := &ClientConfig{}
+ connect(clientConf, KeyAlgoECDSA256)
+
+ // Client asks for RSA explicitly.
+ clientConf.HostKeyAlgorithms = []string{KeyAlgoRSA}
+ connect(clientConf, KeyAlgoRSA)
+
+ c1, c2, err := netPipe()
+ if err != nil {
+ t.Fatalf("netPipe: %v", err)
+ }
+ defer c1.Close()
+ defer c2.Close()
+
+ go NewServerConn(c1, serverConf)
+ clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"}
+ _, _, _, err = NewClientConn(c2, "", clientConf)
+ if err == nil {
+ t.Fatal("succeeded connecting with unknown hostkey algorithm")
+ }
+}
diff --git a/go/src/golang.org/x/crypto/ssh/tcpip.go b/go/src/golang.org/x/crypto/ssh/tcpip.go
index 4ecad0b..6151241 100644
--- a/go/src/golang.org/x/crypto/ssh/tcpip.go
+++ b/go/src/golang.org/x/crypto/ssh/tcpip.go
@@ -355,6 +355,9 @@
lport: uint32(lport),
}
ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
+ if err != nil {
+ return nil, err
+ }
go DiscardRequests(in)
return ch, err
}
diff --git a/go/src/golang.org/x/crypto/ssh/test/agent_unix_test.go b/go/src/golang.org/x/crypto/ssh/test/agent_unix_test.go
index 502e24f..f481253 100644
--- a/go/src/golang.org/x/crypto/ssh/test/agent_unix_test.go
+++ b/go/src/golang.org/x/crypto/ssh/test/agent_unix_test.go
@@ -21,7 +21,16 @@
defer conn.Close()
keyring := agent.NewKeyring()
- keyring.Add(testPrivateKeys["dsa"], nil, "")
+ if err := keyring.Add(agent.AddedKey{PrivateKey: testPrivateKeys["dsa"]}); err != nil {
+ t.Fatalf("Error adding key: %s", err)
+ }
+ if err := keyring.Add(agent.AddedKey{
+ PrivateKey: testPrivateKeys["dsa"],
+ ConfirmBeforeUse: true,
+ LifetimeSecs: 3600,
+ }); err != nil {
+ t.Fatalf("Error adding key with constraints: %s", err)
+ }
pub := testPublicKeys["dsa"]
sess, err := conn.NewSession()
diff --git a/go/src/golang.org/x/crypto/ssh/test/session_test.go b/go/src/golang.org/x/crypto/ssh/test/session_test.go
index fbd1044..c0e714b 100644
--- a/go/src/golang.org/x/crypto/ssh/test/session_test.go
+++ b/go/src/golang.org/x/crypto/ssh/test/session_test.go
@@ -319,3 +319,22 @@
}
}
}
+
+func TestKeyExchanges(t *testing.T) {
+ var config ssh.Config
+ config.SetDefaults()
+ kexOrder := config.KeyExchanges
+ for _, kex := range kexOrder {
+ server := newServer(t)
+ defer server.Shutdown()
+ conf := clientConfig()
+ // Don't fail if sshd doesnt have the kex.
+ conf.KeyExchanges = append([]string{kex}, kexOrder...)
+ conn, err := server.TryDial(conf)
+ if err == nil {
+ conn.Close()
+ } else {
+ t.Errorf("failed for kex %q", kex)
+ }
+ }
+}
diff --git a/go/src/golang.org/x/crypto/tea/cipher.go b/go/src/golang.org/x/crypto/tea/cipher.go
new file mode 100644
index 0000000..9c13d12
--- /dev/null
+++ b/go/src/golang.org/x/crypto/tea/cipher.go
@@ -0,0 +1,109 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package tea implements the TEA algorithm, as defined in Needham and
+// Wheeler's 1994 technical report, “TEA, a Tiny Encryption Algorithm”. See
+// http://www.cix.co.uk/~klockstone/tea.pdf for details.
+
+package tea
+
+import (
+ "crypto/cipher"
+ "encoding/binary"
+ "errors"
+)
+
+const (
+ // BlockSize is the size of a TEA block, in bytes.
+ BlockSize = 8
+
+ // KeySize is the size of a TEA key, in bytes.
+ KeySize = 16
+
+ // delta is the TEA key schedule constant.
+ delta = 0x9e3779b9
+
+ // numRounds is the standard number of rounds in TEA.
+ numRounds = 64
+)
+
+// tea is an instance of the TEA cipher with a particular key.
+type tea struct {
+ key [16]byte
+ rounds int
+}
+
+// NewCipher returns an instance of the TEA cipher with the standard number of
+// rounds. The key argument must be 16 bytes long.
+func NewCipher(key []byte) (cipher.Block, error) {
+ return NewCipherWithRounds(key, numRounds)
+}
+
+// NewCipherWithRounds returns an instance of the TEA cipher with a given
+// number of rounds, which must be even. The key argument must be 16 bytes
+// long.
+func NewCipherWithRounds(key []byte, rounds int) (cipher.Block, error) {
+ if len(key) != 16 {
+ return nil, errors.New("tea: incorrect key size")
+ }
+
+ if rounds&1 != 0 {
+ return nil, errors.New("tea: odd number of rounds specified")
+ }
+
+ c := &tea{
+ rounds: rounds,
+ }
+ copy(c.key[:], key)
+
+ return c, nil
+}
+
+// BlockSize returns the TEA block size, which is eight bytes. It is necessary
+// to satisfy the Block interface in the package "crypto/cipher".
+func (*tea) BlockSize() int {
+ return BlockSize
+}
+
+// Encrypt encrypts the 8 byte buffer src using the key in t and stores the
+// result in dst. Note that for amounts of data larger than a block, it is not
+// safe to just call Encrypt on successive blocks; instead, use an encryption
+// mode like CBC (see crypto/cipher/cbc.go).
+func (t *tea) Encrypt(dst, src []byte) {
+ e := binary.BigEndian
+ v0, v1 := e.Uint32(src), e.Uint32(src[4:])
+ k0, k1, k2, k3 := e.Uint32(t.key[0:]), e.Uint32(t.key[4:]), e.Uint32(t.key[8:]), e.Uint32(t.key[12:])
+
+ sum := uint32(0)
+ delta := uint32(delta)
+
+ for i := 0; i < t.rounds/2; i++ {
+ sum += delta
+ v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)
+ v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)
+ }
+
+ e.PutUint32(dst, v0)
+ e.PutUint32(dst[4:], v1)
+}
+
+// Decrypt decrypts the 8 byte buffer src using the key in t and stores the
+// result in dst.
+func (t *tea) Decrypt(dst, src []byte) {
+ e := binary.BigEndian
+ v0, v1 := e.Uint32(src), e.Uint32(src[4:])
+ k0, k1, k2, k3 := e.Uint32(t.key[0:]), e.Uint32(t.key[4:]), e.Uint32(t.key[8:]), e.Uint32(t.key[12:])
+
+ delta := uint32(delta)
+ sum := delta * uint32(t.rounds/2) // in general, sum = delta * n
+
+ for i := 0; i < t.rounds/2; i++ {
+ v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3)
+ v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1)
+ sum -= delta
+ }
+
+ e.PutUint32(dst, v0)
+ e.PutUint32(dst[4:], v1)
+}
diff --git a/go/src/golang.org/x/crypto/tea/tea_test.go b/go/src/golang.org/x/crypto/tea/tea_test.go
new file mode 100644
index 0000000..eb98d1e
--- /dev/null
+++ b/go/src/golang.org/x/crypto/tea/tea_test.go
@@ -0,0 +1,93 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package tea
+
+import (
+ "bytes"
+ "testing"
+)
+
+// A sample test key for when we just want to initialize a cipher
+var testKey = []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}
+
+// Test that the block size for tea is correct
+func TestBlocksize(t *testing.T) {
+ c, err := NewCipher(testKey)
+ if err != nil {
+ t.Fatalf("NewCipher returned error: %s", err)
+ }
+
+ if result := c.BlockSize(); result != BlockSize {
+ t.Errorf("cipher.BlockSize returned %d, but expected %d", result, BlockSize)
+ }
+}
+
+// Test that invalid key sizes return an error
+func TestInvalidKeySize(t *testing.T) {
+ var key [KeySize + 1]byte
+
+ if _, err := NewCipher(key[:]); err == nil {
+ t.Errorf("invalid key size %d didn't result in an error.", len(key))
+ }
+
+ if _, err := NewCipher(key[:KeySize-1]); err == nil {
+ t.Errorf("invalid key size %d didn't result in an error.", KeySize-1)
+ }
+}
+
+// Test Vectors
+type teaTest struct {
+ rounds int
+ key []byte
+ plaintext []byte
+ ciphertext []byte
+}
+
+var teaTests = []teaTest{
+ // These were sourced from https://github.com/froydnj/ironclad/blob/master/testing/test-vectors/tea.testvec
+ {
+ numRounds,
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x41, 0xea, 0x3a, 0x0a, 0x94, 0xba, 0xa9, 0x40},
+ },
+ {
+ numRounds,
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ []byte{0x31, 0x9b, 0xbe, 0xfb, 0x01, 0x6a, 0xbd, 0xb2},
+ },
+ {
+ 16,
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xed, 0x28, 0x5d, 0xa1, 0x45, 0x5b, 0x33, 0xc1},
+ },
+}
+
+// Test encryption
+func TestCipherEncrypt(t *testing.T) {
+ // Test encryption with standard 64 rounds
+ for i, test := range teaTests {
+ c, err := NewCipherWithRounds(test.key, test.rounds)
+ if err != nil {
+ t.Fatalf("#%d: NewCipher returned error: %s", i, err)
+ }
+
+ var ciphertext [BlockSize]byte
+ c.Encrypt(ciphertext[:], test.plaintext)
+
+ if !bytes.Equal(ciphertext[:], test.ciphertext) {
+ t.Errorf("#%d: incorrect ciphertext. Got %x, wanted %x", i, ciphertext, test.ciphertext)
+ }
+
+ var plaintext2 [BlockSize]byte
+ c.Decrypt(plaintext2[:], ciphertext[:])
+
+ if !bytes.Equal(plaintext2[:], test.plaintext) {
+ t.Errorf("#%d: incorrect plaintext. Got %x, wanted %x", i, plaintext2, test.plaintext)
+ }
+ }
+}