mirror of
https://github.com/Threnklyn/jira.git
synced 2026-06-07 13:33:32 +02:00
update dependencies
This commit is contained in:
-3
@@ -1,3 +0,0 @@
|
||||
This repository holds supplementary Go cryptography libraries.
|
||||
|
||||
To submit changes to this repository, see https://golang.org/doc/contribute.html.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# Go Cryptography
|
||||
|
||||
This repository holds supplementary Go cryptography libraries.
|
||||
|
||||
## Download/Install
|
||||
|
||||
The easiest way to install is to run `go get -u golang.org/x/crypto/...`. You
|
||||
can also manually git clone the repository to `$GOPATH/src/golang.org/x/crypto`.
|
||||
|
||||
## Report Issues / Send Patches
|
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit changes to
|
||||
this repository, see https://golang.org/doc/contribute.html.
|
||||
|
||||
The main issue tracker for the crypto repository is located at
|
||||
https://github.com/golang/go/issues. Prefix your issue with "x/crypto:" in the
|
||||
subject line, so it is easy to find.
|
||||
|
||||
Note that contributions to the cryptography package receive additional scrutiny
|
||||
due to their sensitive nature. Patches may take longer than normal to receive
|
||||
feedback.
|
||||
-32
@@ -51,38 +51,6 @@ const (
|
||||
maxNonces = 100
|
||||
)
|
||||
|
||||
// CertOption is an optional argument type for Client methods which manipulate
|
||||
// certificate data.
|
||||
type CertOption interface {
|
||||
privateCertOpt()
|
||||
}
|
||||
|
||||
// WithKey creates an option holding a private/public key pair.
|
||||
// The private part signs a certificate, and the public part represents the signee.
|
||||
func WithKey(key crypto.Signer) CertOption {
|
||||
return &certOptKey{key}
|
||||
}
|
||||
|
||||
type certOptKey struct {
|
||||
key crypto.Signer
|
||||
}
|
||||
|
||||
func (*certOptKey) privateCertOpt() {}
|
||||
|
||||
// WithTemplate creates an option for specifying a certificate template.
|
||||
// See x509.CreateCertificate for template usage details.
|
||||
//
|
||||
// In TLSSNIxChallengeCert methods, the template is also used as parent,
|
||||
// resulting in a self-signed certificate.
|
||||
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
|
||||
func WithTemplate(t *x509.Certificate) CertOption {
|
||||
return (*certOptTemplate)(t)
|
||||
}
|
||||
|
||||
type certOptTemplate x509.Certificate
|
||||
|
||||
func (*certOptTemplate) privateCertOpt() {}
|
||||
|
||||
// Client is an ACME client.
|
||||
// The only required field is Key. An example of creating a client with a new key
|
||||
// is as follows:
|
||||
|
||||
+34
@@ -5,6 +5,8 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -293,3 +295,35 @@ func (e *wireError) error(h http.Header) *Error {
|
||||
Header: h,
|
||||
}
|
||||
}
|
||||
|
||||
// CertOption is an optional argument type for the TLSSNIxChallengeCert methods for
|
||||
// customizing a temporary certificate for TLS-SNI challenges.
|
||||
type CertOption interface {
|
||||
privateCertOpt()
|
||||
}
|
||||
|
||||
// WithKey creates an option holding a private/public key pair.
|
||||
// The private part signs a certificate, and the public part represents the signee.
|
||||
func WithKey(key crypto.Signer) CertOption {
|
||||
return &certOptKey{key}
|
||||
}
|
||||
|
||||
type certOptKey struct {
|
||||
key crypto.Signer
|
||||
}
|
||||
|
||||
func (*certOptKey) privateCertOpt() {}
|
||||
|
||||
// WithTemplate creates an option for specifying a certificate template.
|
||||
// See x509.CreateCertificate for template usage details.
|
||||
//
|
||||
// In TLSSNIxChallengeCert methods, the template is also used as parent,
|
||||
// resulting in a self-signed certificate.
|
||||
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
|
||||
func WithTemplate(t *x509.Certificate) CertOption {
|
||||
return (*certOptTemplate)(t)
|
||||
}
|
||||
|
||||
type certOptTemplate x509.Certificate
|
||||
|
||||
func (*certOptTemplate) privateCertOpt() {}
|
||||
|
||||
+2
-2
@@ -13,10 +13,10 @@ package ed25519
|
||||
// from SUPERCOP.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
@@ -177,5 +177,5 @@ func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||
|
||||
var checkR [32]byte
|
||||
R.ToBytes(&checkR)
|
||||
return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
|
||||
return bytes.Equal(sig[:32], checkR[:])
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// Copyright 2017 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 md4_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/md4"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
h := md4.New()
|
||||
data := "These pretzels are making me thirsty."
|
||||
io.WriteString(h, data)
|
||||
fmt.Printf("%x", h.Sum(nil))
|
||||
// Output: 48c4e365090b30a32f084c4888deceaa
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
// Copyright 2017 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 auth authenticates a message using a secret key.
|
||||
|
||||
The Sum function, viewed as a function of the message for a uniform random
|
||||
key, is designed to meet the standard notion of unforgeability. This means
|
||||
that an attacker cannot find authenticators for any messages not authenticated
|
||||
by the sender, even if the attacker has adaptively influenced the messages
|
||||
authenticated by the sender. For a formal definition see, e.g., Section 2.4
|
||||
of Bellare, Kilian, and Rogaway, "The security of the cipher block chaining
|
||||
message authentication code," Journal of Computer and System Sciences 61 (2000),
|
||||
362–399; http://www-cse.ucsd.edu/~mihir/papers/cbc.html.
|
||||
|
||||
auth does not make any promises regarding "strong" unforgeability; perhaps
|
||||
one valid authenticator can be converted into another valid authenticator for
|
||||
the same message. NaCl also does not make any promises regarding "truncated
|
||||
unforgeability."
|
||||
|
||||
This package is interoperable with NaCl: https://nacl.cr.yp.to/auth.html.
|
||||
*/
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha512"
|
||||
)
|
||||
|
||||
const (
|
||||
// Size is the size, in bytes, of an authenticated digest.
|
||||
Size = 32
|
||||
// KeySize is the size, in bytes, of an authentication key.
|
||||
KeySize = 32
|
||||
)
|
||||
|
||||
// Sum generates an authenticator for m using a secret key and returns the
|
||||
// 32-byte digest.
|
||||
func Sum(m []byte, key *[KeySize]byte) *[Size]byte {
|
||||
mac := hmac.New(sha512.New, key[:])
|
||||
mac.Write(m)
|
||||
out := new([KeySize]byte)
|
||||
copy(out[:], mac.Sum(nil)[:Size])
|
||||
return out
|
||||
}
|
||||
|
||||
// Verify checks that digest is a valid authenticator of message m under the
|
||||
// given secret key. Verify does not leak timing information.
|
||||
func Verify(digest []byte, m []byte, key *[32]byte) bool {
|
||||
if len(digest) != Size {
|
||||
return false
|
||||
}
|
||||
mac := hmac.New(sha512.New, key[:])
|
||||
mac.Write(m)
|
||||
expectedMAC := mac.Sum(nil) // first 256 bits of 512-bit sum
|
||||
return hmac.Equal(digest, expectedMAC[:Size])
|
||||
}
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
// Copyright 2017 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 auth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
rand "crypto/rand"
|
||||
mrand "math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test cases are from RFC 4231, and match those present in the tests directory
|
||||
// of the download here: https://nacl.cr.yp.to/install.html
|
||||
var testCases = []struct {
|
||||
key [32]byte
|
||||
msg []byte
|
||||
out [32]byte
|
||||
}{
|
||||
{
|
||||
key: [32]byte{
|
||||
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||
0x0b, 0x0b, 0x0b, 0x0b,
|
||||
},
|
||||
msg: []byte("Hi There"),
|
||||
out: [32]byte{
|
||||
0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d,
|
||||
0x4f, 0xf0, 0xb4, 0x24, 0x1a, 0x1d, 0x6c, 0xb0,
|
||||
0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78,
|
||||
0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: [32]byte{'J', 'e', 'f', 'e'},
|
||||
msg: []byte("what do ya want for nothing?"),
|
||||
out: [32]byte{
|
||||
0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2,
|
||||
0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3,
|
||||
0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6,
|
||||
0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: [32]byte{
|
||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xaa, 0xaa, 0xaa, 0xaa,
|
||||
},
|
||||
msg: []byte{ // 50 bytes of 0xdd
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
|
||||
0xdd, 0xdd,
|
||||
},
|
||||
out: [32]byte{
|
||||
0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84,
|
||||
0xef, 0xb0, 0xf0, 0x75, 0x6c, 0x89, 0x0b, 0xe9,
|
||||
0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36,
|
||||
0x55, 0xf8, 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: [32]byte{
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19,
|
||||
},
|
||||
msg: []byte{
|
||||
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
|
||||
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
|
||||
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
|
||||
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
|
||||
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
|
||||
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
|
||||
0xcd, 0xcd,
|
||||
},
|
||||
out: [32]byte{
|
||||
0xb0, 0xba, 0x46, 0x56, 0x37, 0x45, 0x8c, 0x69,
|
||||
0x90, 0xe5, 0xa8, 0xc5, 0xf6, 0x1d, 0x4a, 0xf7,
|
||||
0xe5, 0x76, 0xd9, 0x7f, 0xf9, 0x4b, 0x87, 0x2d,
|
||||
0xe7, 0x6f, 0x80, 0x50, 0x36, 0x1e, 0xe3, 0xdb,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSum(t *testing.T) {
|
||||
for i, test := range testCases {
|
||||
tag := Sum(test.msg, &test.key)
|
||||
if !bytes.Equal(tag[:], test.out[:]) {
|
||||
t.Errorf("#%d: Sum: got\n%x\nwant\n%x", i, tag, test.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerify(t *testing.T) {
|
||||
wrongMsg := []byte("unknown msg")
|
||||
|
||||
for i, test := range testCases {
|
||||
if !Verify(test.out[:], test.msg, &test.key) {
|
||||
t.Errorf("#%d: Verify(%x, %q, %x) failed", i, test.out, test.msg, test.key)
|
||||
}
|
||||
if Verify(test.out[:], wrongMsg, &test.key) {
|
||||
t.Errorf("#%d: Verify(%x, %q, %x) unexpectedly passed", i, test.out, wrongMsg, test.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStress(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("exhaustiveness test")
|
||||
}
|
||||
|
||||
var key [32]byte
|
||||
msg := make([]byte, 10000)
|
||||
prng := mrand.New(mrand.NewSource(0))
|
||||
|
||||
// copied from tests/auth5.c in nacl
|
||||
for i := 0; i < 10000; i++ {
|
||||
if _, err := rand.Read(key[:]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := rand.Read(msg[:i]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tag := Sum(msg[:i], &key)
|
||||
if !Verify(tag[:], msg[:i], &key) {
|
||||
t.Errorf("#%d: unexpected failure from Verify", i)
|
||||
}
|
||||
if i > 0 {
|
||||
msgIndex := prng.Intn(i)
|
||||
oldMsgByte := msg[msgIndex]
|
||||
msg[msgIndex] += byte(1 + prng.Intn(255))
|
||||
if Verify(tag[:], msg[:i], &key) {
|
||||
t.Errorf("#%d: unexpected success from Verify after corrupting message", i)
|
||||
}
|
||||
msg[msgIndex] = oldMsgByte
|
||||
|
||||
tag[prng.Intn(len(tag))] += byte(1 + prng.Intn(255))
|
||||
if Verify(tag[:], msg[:i], &key) {
|
||||
t.Errorf("#%d: unexpected success from Verify after corrupting authenticator", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAuth(b *testing.B) {
|
||||
var key [32]byte
|
||||
if _, err := rand.Read(key[:]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
buf := make([]byte, 1024)
|
||||
if _, err := rand.Read(buf[:]); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(buf)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
tag := Sum(buf, &key)
|
||||
if Verify(tag[:], buf, &key) == false {
|
||||
b.Fatal("unexpected failure from Verify")
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright 2017 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 auth_test
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/nacl/auth"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
// Load your secret key from a safe place and reuse it across multiple
|
||||
// Sum calls. (Obviously don't use this example key for anything
|
||||
// real.) If you want to convert a passphrase to a key, use a suitable
|
||||
// package like bcrypt or scrypt.
|
||||
secretKeyBytes, err := hex.DecodeString("6368616e676520746869732070617373776f726420746f206120736563726574")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var secretKey [32]byte
|
||||
copy(secretKey[:], secretKeyBytes)
|
||||
|
||||
mac := auth.Sum([]byte("hello world"), &secretKey)
|
||||
fmt.Printf("%x\n", *mac)
|
||||
result := auth.Verify(mac[:], []byte("hello world"), &secretKey)
|
||||
fmt.Println(result)
|
||||
badResult := auth.Verify(mac[:], []byte("different message"), &secretKey)
|
||||
fmt.Println(badResult)
|
||||
// Output: eca5a521f3d77b63f567fb0cb6f5f2d200641bc8dada42f60c5f881260c30317
|
||||
// true
|
||||
// false
|
||||
}
|
||||
+3
-3
@@ -88,10 +88,10 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) ([]byte, CipherFunc
|
||||
return nil, ske.CipherFunc, errors.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc)))
|
||||
}
|
||||
plaintextKey = plaintextKey[1:]
|
||||
if l := len(plaintextKey); l == 0 || l%cipherFunc.blockSize() != 0 {
|
||||
return nil, cipherFunc, errors.StructuralError("length of decrypted key not a multiple of block size")
|
||||
if l, cipherKeySize := len(plaintextKey), cipherFunc.KeySize(); l != cipherFunc.KeySize() {
|
||||
return nil, cipherFunc, errors.StructuralError("length of decrypted key (" + strconv.Itoa(l) + ") " +
|
||||
"not equal to cipher keysize (" + strconv.Itoa(cipherKeySize) + ")")
|
||||
}
|
||||
|
||||
return plaintextKey, cipherFunc, nil
|
||||
}
|
||||
|
||||
|
||||
+48
-34
@@ -61,43 +61,57 @@ func TestSymmetricKeyEncrypted(t *testing.T) {
|
||||
const symmetricallyEncryptedHex = "8c0d04030302371a0b38d884f02060c91cf97c9973b8e58e028e9501708ccfe618fb92afef7fa2d80ddadd93cf"
|
||||
const symmetricallyEncryptedContentsHex = "cb1062004d14c4df636f6e74656e74732e0a"
|
||||
|
||||
func TestSerializeSymmetricKeyEncrypted(t *testing.T) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
passphrase := []byte("testing")
|
||||
const cipherFunc = CipherAES128
|
||||
config := &Config{
|
||||
DefaultCipher: cipherFunc,
|
||||
func TestSerializeSymmetricKeyEncryptedCiphers(t *testing.T) {
|
||||
tests := [...]struct {
|
||||
cipherFunc CipherFunction
|
||||
name string
|
||||
}{
|
||||
{Cipher3DES, "Cipher3DES"},
|
||||
{CipherCAST5, "CipherCAST5"},
|
||||
{CipherAES128, "CipherAES128"},
|
||||
{CipherAES192, "CipherAES192"},
|
||||
{CipherAES256, "CipherAES256"},
|
||||
}
|
||||
|
||||
key, err := SerializeSymmetricKeyEncrypted(buf, passphrase, config)
|
||||
if err != nil {
|
||||
t.Errorf("failed to serialize: %s", err)
|
||||
return
|
||||
}
|
||||
for _, test := range tests {
|
||||
var buf bytes.Buffer
|
||||
passphrase := []byte("testing")
|
||||
config := &Config{
|
||||
DefaultCipher: test.cipherFunc,
|
||||
}
|
||||
|
||||
p, err := Read(buf)
|
||||
if err != nil {
|
||||
t.Errorf("failed to reparse: %s", err)
|
||||
return
|
||||
}
|
||||
ske, ok := p.(*SymmetricKeyEncrypted)
|
||||
if !ok {
|
||||
t.Errorf("parsed a different packet type: %#v", p)
|
||||
return
|
||||
}
|
||||
key, err := SerializeSymmetricKeyEncrypted(&buf, passphrase, config)
|
||||
if err != nil {
|
||||
t.Errorf("cipher(%s) failed to serialize: %s", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if ske.CipherFunc != config.DefaultCipher {
|
||||
t.Errorf("SKE cipher function is %d (expected %d)", ske.CipherFunc, config.DefaultCipher)
|
||||
}
|
||||
parsedKey, parsedCipherFunc, err := ske.Decrypt(passphrase)
|
||||
if err != nil {
|
||||
t.Errorf("failed to decrypt reparsed SKE: %s", err)
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(key, parsedKey) {
|
||||
t.Errorf("keys don't match after Decrypt: %x (original) vs %x (parsed)", key, parsedKey)
|
||||
}
|
||||
if parsedCipherFunc != cipherFunc {
|
||||
t.Errorf("cipher function doesn't match after Decrypt: %d (original) vs %d (parsed)", cipherFunc, parsedCipherFunc)
|
||||
p, err := Read(&buf)
|
||||
if err != nil {
|
||||
t.Errorf("cipher(%s) failed to reparse: %s", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
ske, ok := p.(*SymmetricKeyEncrypted)
|
||||
if !ok {
|
||||
t.Errorf("cipher(%s) parsed a different packet type: %#v", test.name, p)
|
||||
continue
|
||||
}
|
||||
|
||||
if ske.CipherFunc != config.DefaultCipher {
|
||||
t.Errorf("cipher(%s) SKE cipher function is %d (expected %d)", test.name, ske.CipherFunc, config.DefaultCipher)
|
||||
}
|
||||
parsedKey, parsedCipherFunc, err := ske.Decrypt(passphrase)
|
||||
if err != nil {
|
||||
t.Errorf("cipher(%s) failed to decrypt reparsed SKE: %s", test.name, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(key, parsedKey) {
|
||||
t.Errorf("cipher(%s) keys don't match after Decrypt: %x (original) vs %x (parsed)", test.name, key, parsedKey)
|
||||
}
|
||||
if parsedCipherFunc != test.cipherFunc {
|
||||
t.Errorf("cipher(%s) cipher function doesn't match after Decrypt: %d (original) vs %d (parsed)",
|
||||
test.name, test.cipherFunc, parsedCipherFunc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -19,5 +19,6 @@ func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
|
||||
if len(in) == 0 {
|
||||
return
|
||||
}
|
||||
_ = out[len(in)-1]
|
||||
salsa2020XORKeyStream(&out[0], &in[0], uint64(len(in)), &counter[0], &key[0])
|
||||
}
|
||||
|
||||
+19
@@ -33,3 +33,22 @@ func TestCore208(t *testing.T) {
|
||||
t.Errorf("expected %x, got %x", out, in)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutOfBoundsWrite(t *testing.T) {
|
||||
// encrypted "0123456789"
|
||||
cipherText := []byte{170, 166, 196, 104, 175, 121, 68, 44, 174, 51}
|
||||
var counter [16]byte
|
||||
var key [32]byte
|
||||
want := "abcdefghij"
|
||||
plainText := []byte(want)
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
t.Error("XORKeyStream expected to panic on len(dst) < len(src), but didn't")
|
||||
}
|
||||
if plainText[3] == '3' {
|
||||
t.Errorf("XORKeyStream did out of bounds write, want %v, got %v", want, string(plainText))
|
||||
}
|
||||
}()
|
||||
XORKeyStream(plainText[:3], cipherText, &counter, &key)
|
||||
}
|
||||
|
||||
+2
-3
@@ -42,9 +42,8 @@ type state struct {
|
||||
storage [maxRate]byte
|
||||
|
||||
// Specific to SHA-3 and SHAKE.
|
||||
fixedOutput bool // whether this is a fixed-output-length instance
|
||||
outputLen int // the default output size in bytes
|
||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||
outputLen int // the default output size in bytes
|
||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||
}
|
||||
|
||||
// BlockSize returns the rate of sponge underlying this hash function.
|
||||
|
||||
+31
-7
@@ -57,6 +57,17 @@ type Agent interface {
|
||||
Signers() ([]ssh.Signer, error)
|
||||
}
|
||||
|
||||
// ConstraintExtension describes an optional constraint defined by users.
|
||||
type ConstraintExtension struct {
|
||||
// ExtensionName consist of a UTF-8 string suffixed by the
|
||||
// implementation domain following the naming scheme defined
|
||||
// in Section 4.2 of [RFC4251], e.g. "foo@example.com".
|
||||
ExtensionName string
|
||||
// ExtensionDetails contains the actual content of the extended
|
||||
// constraint.
|
||||
ExtensionDetails []byte
|
||||
}
|
||||
|
||||
// AddedKey describes an SSH key to be added to an Agent.
|
||||
type AddedKey struct {
|
||||
// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
|
||||
@@ -73,6 +84,9 @@ type AddedKey struct {
|
||||
// ConfirmBeforeUse, if true, requests that the agent confirm with the
|
||||
// user before each use of this key.
|
||||
ConfirmBeforeUse bool
|
||||
// ConstraintExtensions are the experimental or private-use constraints
|
||||
// defined by users.
|
||||
ConstraintExtensions []ConstraintExtension
|
||||
}
|
||||
|
||||
// See [PROTOCOL.agent], section 3.
|
||||
@@ -94,8 +108,9 @@ const (
|
||||
agentAddSmartcardKeyConstrained = 26
|
||||
|
||||
// 3.7 Key constraint identifiers
|
||||
agentConstrainLifetime = 1
|
||||
agentConstrainConfirm = 2
|
||||
agentConstrainLifetime = 1
|
||||
agentConstrainConfirm = 2
|
||||
agentConstrainExtension = 3
|
||||
)
|
||||
|
||||
// maxAgentResponseBytes is the maximum agent reply size that is accepted. This
|
||||
@@ -151,6 +166,19 @@ type publicKey struct {
|
||||
Rest []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// 3.7 Key constraint identifiers
|
||||
type constrainLifetimeAgentMsg struct {
|
||||
LifetimeSecs uint32 `sshtype:"1"`
|
||||
}
|
||||
|
||||
type constrainExtensionAgentMsg struct {
|
||||
ExtensionName string `sshtype:"3"`
|
||||
ExtensionDetails []byte
|
||||
|
||||
// Rest is a field used for parsing, not part of message
|
||||
Rest []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// Key represents a protocol 2 public key as defined in
|
||||
// [PROTOCOL.agent], section 2.5.2.
|
||||
type Key struct {
|
||||
@@ -542,11 +570,7 @@ 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[:]...)
|
||||
constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...)
|
||||
}
|
||||
|
||||
if key.ConfirmBeforeUse {
|
||||
|
||||
+47
-16
@@ -19,8 +19,8 @@ import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// startAgent executes ssh-agent, and returns a Agent interface to it.
|
||||
func startAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
|
||||
// startOpenSSHAgent executes ssh-agent, and returns a Agent interface to it.
|
||||
func startOpenSSHAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
|
||||
if testing.Short() {
|
||||
// ssh-agent is not always available, and the key
|
||||
// types supported vary by platform.
|
||||
@@ -79,16 +79,32 @@ func startAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
|
||||
}
|
||||
}
|
||||
|
||||
func testAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
// startKeyringAgent uses Keyring to simulate a ssh-agent Server and returns a client.
|
||||
func startKeyringAgent(t *testing.T) (client Agent, cleanup func()) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
go ServeAgent(NewKeyring(), c2)
|
||||
|
||||
return NewClient(c1), func() {
|
||||
c1.Close()
|
||||
c2.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func testOpenSSHAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
testAgentInterface(t, agent, key, cert, lifetimeSecs)
|
||||
}
|
||||
|
||||
func testKeyring(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
a := NewKeyring()
|
||||
testAgentInterface(t, a, key, cert, lifetimeSecs)
|
||||
func testKeyringAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
agent, cleanup := startKeyringAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
testAgentInterface(t, agent, key, cert, lifetimeSecs)
|
||||
}
|
||||
|
||||
func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
@@ -159,8 +175,8 @@ func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Ce
|
||||
|
||||
func TestAgent(t *testing.T) {
|
||||
for _, keyType := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
|
||||
testAgent(t, testPrivateKeys[keyType], nil, 0)
|
||||
testKeyring(t, testPrivateKeys[keyType], nil, 1)
|
||||
testOpenSSHAgent(t, testPrivateKeys[keyType], nil, 0)
|
||||
testKeyringAgent(t, testPrivateKeys[keyType], nil, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,8 +188,8 @@ func TestCert(t *testing.T) {
|
||||
}
|
||||
cert.SignCert(rand.Reader, testSigners["ecdsa"])
|
||||
|
||||
testAgent(t, testPrivateKeys["rsa"], cert, 0)
|
||||
testKeyring(t, testPrivateKeys["rsa"], cert, 1)
|
||||
testOpenSSHAgent(t, testPrivateKeys["rsa"], cert, 0)
|
||||
testKeyringAgent(t, testPrivateKeys["rsa"], cert, 0)
|
||||
}
|
||||
|
||||
// netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
|
||||
@@ -203,7 +219,7 @@ func netPipe() (net.Conn, net.Conn, error) {
|
||||
}
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
a, b, err := netPipe()
|
||||
@@ -247,8 +263,14 @@ func TestAuth(t *testing.T) {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func TestLockClient(t *testing.T) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
func TestLockOpenSSHAgent(t *testing.T) {
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
testLockAgent(agent, t)
|
||||
}
|
||||
|
||||
func TestLockKeyringAgent(t *testing.T) {
|
||||
agent, cleanup := startKeyringAgent(t)
|
||||
defer cleanup()
|
||||
testLockAgent(agent, t)
|
||||
}
|
||||
@@ -308,10 +330,19 @@ func testLockAgent(agent Agent, t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentLifetime(t *testing.T) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
func testOpenSSHAgentLifetime(t *testing.T) {
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
testAgentLifetime(t, agent)
|
||||
}
|
||||
|
||||
func testKeyringAgentLifetime(t *testing.T) {
|
||||
agent, cleanup := startKeyringAgent(t)
|
||||
defer cleanup()
|
||||
testAgentLifetime(t, agent)
|
||||
}
|
||||
|
||||
func testAgentLifetime(t *testing.T, agent Agent) {
|
||||
for _, keyType := range []string{"rsa", "dsa", "ecdsa"} {
|
||||
// Add private keys to the agent.
|
||||
err := agent.Add(AddedKey{
|
||||
|
||||
+81
-9
@@ -106,7 +106,7 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
|
||||
return nil, s.agent.Lock(req.Passphrase)
|
||||
|
||||
case agentUnlock:
|
||||
var req agentLockMsg
|
||||
var req agentUnlockMsg
|
||||
if err := ssh.Unmarshal(data, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -155,6 +155,44 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
|
||||
return nil, fmt.Errorf("unknown opcode %d", data[0])
|
||||
}
|
||||
|
||||
func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse bool, extensions []ConstraintExtension, err error) {
|
||||
for len(constraints) != 0 {
|
||||
switch constraints[0] {
|
||||
case agentConstrainLifetime:
|
||||
lifetimeSecs = binary.BigEndian.Uint32(constraints[1:5])
|
||||
constraints = constraints[5:]
|
||||
case agentConstrainConfirm:
|
||||
confirmBeforeUse = true
|
||||
constraints = constraints[1:]
|
||||
case agentConstrainExtension:
|
||||
var msg constrainExtensionAgentMsg
|
||||
if err = ssh.Unmarshal(constraints, &msg); err != nil {
|
||||
return 0, false, nil, err
|
||||
}
|
||||
extensions = append(extensions, ConstraintExtension{
|
||||
ExtensionName: msg.ExtensionName,
|
||||
ExtensionDetails: msg.ExtensionDetails,
|
||||
})
|
||||
constraints = msg.Rest
|
||||
default:
|
||||
return 0, false, nil, fmt.Errorf("unknown constraint type: %d", constraints[0])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setConstraints(key *AddedKey, constraintBytes []byte) error {
|
||||
lifetimeSecs, confirmBeforeUse, constraintExtensions, err := parseConstraints(constraintBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key.LifetimeSecs = lifetimeSecs
|
||||
key.ConfirmBeforeUse = confirmBeforeUse
|
||||
key.ConstraintExtensions = constraintExtensions
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseRSAKey(req []byte) (*AddedKey, error) {
|
||||
var k rsaKeyMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
@@ -173,7 +211,11 @@ func parseRSAKey(req []byte) (*AddedKey, error) {
|
||||
}
|
||||
priv.Precompute()
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseEd25519Key(req []byte) (*AddedKey, error) {
|
||||
@@ -182,7 +224,12 @@ func parseEd25519Key(req []byte) (*AddedKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
priv := ed25519.PrivateKey(k.Priv)
|
||||
return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil
|
||||
|
||||
addedKey := &AddedKey{PrivateKey: &priv, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseDSAKey(req []byte) (*AddedKey, error) {
|
||||
@@ -202,7 +249,11 @@ func parseDSAKey(req []byte) (*AddedKey, error) {
|
||||
X: k.X,
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) {
|
||||
@@ -243,7 +294,12 @@ func parseEd25519Cert(req []byte) (*AddedKey, error) {
|
||||
if !ok {
|
||||
return nil, errors.New("agent: bad ED25519 certificate")
|
||||
}
|
||||
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
|
||||
addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseECDSAKey(req []byte) (*AddedKey, error) {
|
||||
@@ -257,7 +313,11 @@ func parseECDSAKey(req []byte) (*AddedKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseRSACert(req []byte) (*AddedKey, error) {
|
||||
@@ -300,7 +360,11 @@ func parseRSACert(req []byte) (*AddedKey, error) {
|
||||
}
|
||||
priv.Precompute()
|
||||
|
||||
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseDSACert(req []byte) (*AddedKey, error) {
|
||||
@@ -338,7 +402,11 @@ func parseDSACert(req []byte) (*AddedKey, error) {
|
||||
X: k.X,
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseECDSACert(req []byte) (*AddedKey, error) {
|
||||
@@ -371,7 +439,11 @@ func parseECDSACert(req []byte) (*AddedKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func (s *server) insertIdentity(req []byte) error {
|
||||
|
||||
+51
-1
@@ -8,6 +8,9 @@ import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
pseudorand "math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -40,7 +43,7 @@ func TestSetupForwardAgent(t *testing.T) {
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
_, socket, cleanup := startAgent(t)
|
||||
_, socket, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
serverConf := ssh.ServerConfig{
|
||||
@@ -207,3 +210,50 @@ func TestCertTypes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConstraints(t *testing.T) {
|
||||
// Test LifetimeSecs
|
||||
var msg = constrainLifetimeAgentMsg{pseudorand.Uint32()}
|
||||
lifetimeSecs, _, _, err := parseConstraints(ssh.Marshal(msg))
|
||||
if err != nil {
|
||||
t.Fatalf("parseConstraints: %v", err)
|
||||
}
|
||||
if lifetimeSecs != msg.LifetimeSecs {
|
||||
t.Errorf("got lifetime %v, want %v", lifetimeSecs, msg.LifetimeSecs)
|
||||
}
|
||||
|
||||
// Test ConfirmBeforeUse
|
||||
_, confirmBeforeUse, _, err := parseConstraints([]byte{agentConstrainConfirm})
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
if !confirmBeforeUse {
|
||||
t.Error("got comfirmBeforeUse == false")
|
||||
}
|
||||
|
||||
// Test ConstraintExtensions
|
||||
var data []byte
|
||||
var expect []ConstraintExtension
|
||||
for i := 0; i < 10; i++ {
|
||||
var ext = ConstraintExtension{
|
||||
ExtensionName: fmt.Sprintf("name%d", i),
|
||||
ExtensionDetails: []byte(fmt.Sprintf("details: %d", i)),
|
||||
}
|
||||
expect = append(expect, ext)
|
||||
data = append(data, agentConstrainExtension)
|
||||
data = append(data, ssh.Marshal(ext)...)
|
||||
}
|
||||
_, _, extensions, err := parseConstraints(data)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expect, extensions) {
|
||||
t.Errorf("got extension %v, want %v", extensions, expect)
|
||||
}
|
||||
|
||||
// Test Unknown Constraint
|
||||
_, _, _, err = parseConstraints([]byte{128})
|
||||
if err == nil || !strings.Contains(err.Error(), "unknown constraint") {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ type ServerConfig struct {
|
||||
PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
|
||||
|
||||
// PublicKeyCallback, if non-nil, is called when a client
|
||||
// offers a public key for authentication. It must return true
|
||||
// offers a public key for authentication. It must return a nil error
|
||||
// if the given public key can be used to authenticate the
|
||||
// given user. For example, see CertChecker.Authenticate. A
|
||||
// call to this function does not guarantee that the key
|
||||
|
||||
+20
@@ -231,6 +231,26 @@ func (s *Session) RequestSubsystem(subsystem string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// RFC 4254 Section 6.7.
|
||||
type ptyWindowChangeMsg struct {
|
||||
Columns uint32
|
||||
Rows uint32
|
||||
Width uint32
|
||||
Height uint32
|
||||
}
|
||||
|
||||
// WindowChange informs the remote host about a terminal window dimension change to h rows and w columns.
|
||||
func (s *Session) WindowChange(h, w int) error {
|
||||
req := ptyWindowChangeMsg{
|
||||
Columns: uint32(w),
|
||||
Rows: uint32(h),
|
||||
Width: uint32(w * 8),
|
||||
Height: uint32(h * 8),
|
||||
}
|
||||
_, err := s.ch.SendRequest("window-change", false, Marshal(&req))
|
||||
return err
|
||||
}
|
||||
|
||||
// RFC 4254 Section 6.9.
|
||||
type signalMsg struct {
|
||||
Signal string
|
||||
|
||||
+4
@@ -19,6 +19,8 @@ package terminal // import "golang.org/x/crypto/ssh/terminal"
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// State contains the state of a terminal.
|
||||
@@ -50,6 +52,8 @@ func MakeRaw(fd int) (*State, error) {
|
||||
newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
|
||||
newState.Cflag &^= syscall.CSIZE | syscall.PARENB
|
||||
newState.Cflag |= syscall.CS8
|
||||
newState.Cc[unix.VMIN] = 1
|
||||
newState.Cc[unix.VTIME] = 0
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@
|
||||
|
||||
package terminal
|
||||
|
||||
import "syscall"
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
const ioctlWriteTermios = syscall.TIOCSETA
|
||||
const ioctlReadTermios = unix.TIOCGETA
|
||||
const ioctlWriteTermios = unix.TIOCSETA
|
||||
|
||||
+4
-5
@@ -4,8 +4,7 @@
|
||||
|
||||
package terminal
|
||||
|
||||
// These constants are declared here, rather than importing
|
||||
// them from the syscall package as some syscall packages, even
|
||||
// on linux, for example gccgo, do not declare them.
|
||||
const ioctlReadTermios = 0x5401 // syscall.TCGETS
|
||||
const ioctlWriteTermios = 0x5402 // syscall.TCSETS
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TCGETS
|
||||
const ioctlWriteTermios = unix.TCSETS
|
||||
|
||||
+23
-76
@@ -17,53 +17,7 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
enableLineInput = 2
|
||||
enableEchoInput = 4
|
||||
enableProcessedInput = 1
|
||||
enableWindowInput = 8
|
||||
enableMouseInput = 16
|
||||
enableInsertMode = 32
|
||||
enableQuickEditMode = 64
|
||||
enableExtendedFlags = 128
|
||||
enableAutoPosition = 256
|
||||
enableProcessedOutput = 1
|
||||
enableWrapAtEolOutput = 2
|
||||
)
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var (
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
)
|
||||
|
||||
type (
|
||||
short int16
|
||||
word uint16
|
||||
|
||||
coord struct {
|
||||
x short
|
||||
y short
|
||||
}
|
||||
smallRect struct {
|
||||
left short
|
||||
top short
|
||||
right short
|
||||
bottom short
|
||||
}
|
||||
consoleScreenBufferInfo struct {
|
||||
size coord
|
||||
cursorPosition coord
|
||||
attributes word
|
||||
window smallRect
|
||||
maximumWindowSize coord
|
||||
}
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type State struct {
|
||||
@@ -73,8 +27,8 @@ type State struct {
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
err := windows.GetConsoleMode(windows.Handle(fd), &st)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
@@ -82,14 +36,12 @@ func IsTerminal(fd int) bool {
|
||||
// restored.
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
var st uint32
|
||||
_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
|
||||
_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{st}, nil
|
||||
}
|
||||
@@ -98,9 +50,8 @@ func MakeRaw(fd int) (*State, error) {
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
var st uint32
|
||||
_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{st}, nil
|
||||
}
|
||||
@@ -108,25 +59,23 @@ func GetState(fd int) (*State, error) {
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, state *State) error {
|
||||
_, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0)
|
||||
return err
|
||||
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
var info consoleScreenBufferInfo
|
||||
_, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0)
|
||||
if e != 0 {
|
||||
return 0, 0, error(e)
|
||||
var info windows.ConsoleScreenBufferInfo
|
||||
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(info.size.x), int(info.size.y), nil
|
||||
return int(info.Size.X), int(info.Size.Y), nil
|
||||
}
|
||||
|
||||
// passwordReader is an io.Reader that reads from a specific Windows HANDLE.
|
||||
type passwordReader int
|
||||
|
||||
func (r passwordReader) Read(buf []byte) (int, error) {
|
||||
return syscall.Read(syscall.Handle(r), buf)
|
||||
return windows.Read(windows.Handle(r), buf)
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
@@ -134,21 +83,19 @@ func (r passwordReader) Read(buf []byte) (int, error) {
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, error) {
|
||||
var st uint32
|
||||
_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
old := st
|
||||
|
||||
st &^= (enableEchoInput)
|
||||
st |= (enableProcessedInput | enableLineInput | enableProcessedOutput)
|
||||
_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
st &^= (windows.ENABLE_ECHO_INPUT)
|
||||
st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0)
|
||||
windows.SetConsoleMode(windows.Handle(fd), old)
|
||||
}()
|
||||
|
||||
return readPasswordLine(passwordReader(fd))
|
||||
|
||||
+48
@@ -276,6 +276,54 @@ func TestValidTerminalMode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowChange(t *testing.T) {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
conn := server.Dial(clientConfig())
|
||||
defer conn.Close()
|
||||
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("session failed: %v", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to acquire stdout pipe: %s", err)
|
||||
}
|
||||
|
||||
stdin, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to acquire stdin pipe: %s", err)
|
||||
}
|
||||
|
||||
tm := ssh.TerminalModes{ssh.ECHO: 0}
|
||||
if err = session.RequestPty("xterm", 80, 40, tm); err != nil {
|
||||
t.Fatalf("req-pty failed: %s", err)
|
||||
}
|
||||
|
||||
if err := session.WindowChange(100, 100); err != nil {
|
||||
t.Fatalf("window-change failed: %s", err)
|
||||
}
|
||||
|
||||
err = session.Shell()
|
||||
if err != nil {
|
||||
t.Fatalf("session failed: %s", err)
|
||||
}
|
||||
|
||||
stdin.Write([]byte("stty size && exit\n"))
|
||||
|
||||
var buf bytes.Buffer
|
||||
if _, err := io.Copy(&buf, stdout); err != nil {
|
||||
t.Fatalf("reading failed: %s", err)
|
||||
}
|
||||
|
||||
if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "100 100") {
|
||||
t.Fatalf("terminal WindowChange failure: expected \"100 100\" stty output, got %s", sttyOutput)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCiphers(t *testing.T) {
|
||||
var config ssh.Config
|
||||
config.SetDefaults()
|
||||
|
||||
Reference in New Issue
Block a user