mirror of https://github.com/schollz/croc.git
Update dependencies
This commit is contained in:
parent
c3cd3d6039
commit
d663ff861b
|
@ -13,6 +13,12 @@
|
|||
packages = [".","util/strutil"]
|
||||
revision = "d0567a9d84a1c40dd7568115ea66f4887bf57b33"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/mars9/crypt"
|
||||
packages = ["."]
|
||||
revision = "65899cf653ff022fe5c7fe504b439feed9e7e0fc"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
|
@ -40,8 +46,8 @@
|
|||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
revision = "9419663f5a44be8b34ca85f08abc5fe1be11f8a3"
|
||||
packages = ["pbkdf2","scrypt","ssh/terminal"]
|
||||
revision = "541b9d50ad47e36efd8fb423e938e59ff1691f68"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -58,6 +64,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "b5f57442f43856cf17500cec2a357c4ea34e5f9eae4298954f62765111b0d5a9"
|
||||
inputs-digest = "b7969c854fd8997ea62c1a9ed968e5eeac091a3fc5650cca64e99e888a0156db"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
crypt/crypt
|
|
@ -0,0 +1,13 @@
|
|||
Copyright (c) 2013 Markus Sonderegger
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,4 @@
|
|||
# crypt
|
||||
|
||||
Package crypt provides password-based encryption and decryption of
|
||||
data streams.
|
|
@ -0,0 +1,165 @@
|
|||
// Package crypt provides password-based encryption and decryption of
|
||||
// data streams.
|
||||
package crypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
blockSize = aes.BlockSize // AES block size
|
||||
version = 1
|
||||
)
|
||||
|
||||
// Crypter encrypt/decrypts with AES (Rijndael) in cipher block counter
|
||||
// mode (CTR) and authenticate with HMAC-SHA.
|
||||
type Crypter struct {
|
||||
HashFunc func() hash.Hash
|
||||
HashSize int
|
||||
Key Key
|
||||
BufSize int
|
||||
}
|
||||
|
||||
func (c *Crypter) encHeader(salt, iv, hmacKey []byte) []byte {
|
||||
keySize := c.Key.Size()
|
||||
headerSize := 1 + keySize + blockSize + c.HashSize
|
||||
|
||||
b := make([]byte, headerSize)
|
||||
b[0] = version
|
||||
copy(b[1:1+keySize], salt)
|
||||
copy(b[1+keySize:1+keySize+blockSize], iv)
|
||||
|
||||
mac := hmac.New(c.HashFunc, hmacKey)
|
||||
mac.Write(b[:1+keySize+blockSize])
|
||||
copy(b[1+keySize+blockSize:], mac.Sum(nil))
|
||||
return b
|
||||
}
|
||||
|
||||
func (c *Crypter) bufSize() int {
|
||||
if c.BufSize == 0 {
|
||||
return 2 * 1024 * 1024
|
||||
}
|
||||
return c.BufSize
|
||||
}
|
||||
|
||||
func (c *Crypter) decHeader(b []byte) ([]byte, []byte, error) {
|
||||
if b[0] != version {
|
||||
return nil, nil, errors.New("malformed encrypted packet")
|
||||
}
|
||||
|
||||
keySize := c.Key.Size()
|
||||
salt := b[1 : 1+keySize]
|
||||
iv := b[1+keySize : 1+keySize+blockSize]
|
||||
return salt, iv, nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts from src until either EOF is reached on src or an
|
||||
// error occurs. A successful Encrypt returns err == nil, not err == EOF.
|
||||
func (c *Crypter) Encrypt(dst io.Writer, src io.Reader) (err error) {
|
||||
salt := make([]byte, c.Key.Size())
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return err
|
||||
}
|
||||
iv := make([]byte, blockSize)
|
||||
if _, err := rand.Read(iv); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aesKey, hmacKey := c.Key.Derive(salt)
|
||||
header := c.encHeader(salt, iv, hmacKey)
|
||||
if _, err := dst.Write(header); err != nil {
|
||||
return err
|
||||
}
|
||||
mac := hmac.New(c.HashFunc, hmacKey)
|
||||
mac.Write(header)
|
||||
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
|
||||
buf := make([]byte, c.bufSize())
|
||||
n := 0
|
||||
for {
|
||||
n, err = src.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
mac.Write(buf[:n])
|
||||
stream.XORKeyStream(buf[:n], buf[:n])
|
||||
if _, err = dst.Write(buf[:n]); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = dst.Write(mac.Sum(nil)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts from src until either EOF is reached on src or an
|
||||
// error occurs. A successful Decrypt returns err == nil, not err == EOF.
|
||||
func (c *Crypter) Decrypt(dst io.Writer, src io.Reader) (err error) {
|
||||
keySize := c.Key.Size()
|
||||
headerSize := 1 + keySize + blockSize + c.HashSize
|
||||
|
||||
header := make([]byte, headerSize)
|
||||
if _, err = src.Read(header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
salt, iv, err := c.decHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
aesKey, hmacKey := c.Key.Derive(salt)
|
||||
|
||||
mac := hmac.New(c.HashFunc, hmacKey)
|
||||
mac.Write(header[:1+keySize+blockSize])
|
||||
|
||||
if !bytes.Equal(header[1+keySize+blockSize:], mac.Sum(nil)) {
|
||||
return errors.New("cannot authenticate header")
|
||||
}
|
||||
mac.Write(header[1+keySize+blockSize:])
|
||||
|
||||
block, err := aes.NewCipher(aesKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stream := cipher.NewCTR(block, iv)
|
||||
buf := make([]byte, c.bufSize()+c.HashSize)
|
||||
n := 0
|
||||
for {
|
||||
n, err = src.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
stream.XORKeyStream(buf[:n-c.HashSize], buf[:n-c.HashSize])
|
||||
mac.Write(buf[:n-c.HashSize])
|
||||
if !bytes.Equal(buf[n-c.HashSize:n], mac.Sum(nil)) {
|
||||
return errors.New("cannot authenticate packet")
|
||||
}
|
||||
if _, err = dst.Write(buf[:n-c.HashSize]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package crypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var plain = [][]byte{
|
||||
[]byte("Nö, ich trinke keinen Tee, ich bin Atheist. --- Helge Schneider"),
|
||||
[]byte("I wish these damn scientists would leave intelligence to the experts. --- Gen. Richard Stillwell (CIA)"),
|
||||
[]byte("I want to die peacefully in my sleep like my grandfather, not screaming in terror like his passengers. --- Charlie Hall"),
|
||||
[]byte("NOTE 3: Each bit has the value either ZERO or ONE. --- ECMA-035 spec"),
|
||||
[]byte("Writing about music is like dancing about architecture. --- Frank Zappa"),
|
||||
[]byte("If you want to go somewhere, goto is the best way to get there. --- K Thompson"),
|
||||
}
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
||||
enc := bytes.NewBuffer(nil)
|
||||
dec := bytes.NewBuffer(nil)
|
||||
password := []byte("test password")
|
||||
c := &Crypter{
|
||||
HashFunc: sha1.New,
|
||||
HashSize: sha1.Size,
|
||||
Key: NewPbkdf2Key(password, 32),
|
||||
}
|
||||
defer c.Key.Reset()
|
||||
|
||||
for _, src := range plain {
|
||||
enc.Reset()
|
||||
dec.Reset()
|
||||
err := c.Encrypt(enc, bytes.NewReader(src))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = c.Decrypt(dec, enc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bytes.Compare(dec.Bytes(), src) != 0 {
|
||||
t.Errorf("encrypt/decrypt error: want %q, got %q", string(src), string(dec.Bytes()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptDecrypt1(t *testing.T) {
|
||||
f, err := os.Open("crypt_test.go")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
encBuf := bytes.NewBuffer(nil)
|
||||
password := []byte("test password")
|
||||
c := &Crypter{
|
||||
HashFunc: sha1.New,
|
||||
HashSize: sha1.Size,
|
||||
Key: NewPbkdf2Key(password, 32),
|
||||
}
|
||||
defer c.Key.Reset()
|
||||
|
||||
err = c.Encrypt(encBuf, f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
decBuf := bytes.NewBuffer(nil)
|
||||
err = c.Decrypt(decBuf, encBuf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
src, err := ioutil.ReadFile("crypt_test.go")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bytes.Compare(decBuf.Bytes(), src) != 0 {
|
||||
t.Errorf("encrypt/decrypt file error: crypt_test.go")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/mars9/crypt"
|
||||
"github.com/mars9/keyring"
|
||||
"github.com/mars9/passwd"
|
||||
)
|
||||
|
||||
var (
|
||||
prompt = flag.Bool("p", false, "prompt to enter a passphrase")
|
||||
decrypt = flag.Bool("d", false, "decrypt infile to oufile")
|
||||
service = flag.String("s", "go-crypto", "keyring service name")
|
||||
username = flag.String("u", os.Getenv("USER"), "keyring username")
|
||||
initKeyring = flag.Bool("i", false, "intialize keyring")
|
||||
)
|
||||
|
||||
func passphrase() ([]byte, error) {
|
||||
if *prompt {
|
||||
password, err := passwd.Get("Enter passphrase: ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get passphrase: %v\n", err)
|
||||
}
|
||||
|
||||
if !*decrypt {
|
||||
confirm, err := passwd.Get("Confirm passphrase: ")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get passphrase: %v\n", err)
|
||||
}
|
||||
if !bytes.Equal(password, confirm) {
|
||||
return nil, fmt.Errorf("Passphrase mismatch, try again.")
|
||||
}
|
||||
}
|
||||
return password, nil
|
||||
}
|
||||
|
||||
ring, err := keyring.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ring.Get(*service, *username)
|
||||
}
|
||||
|
||||
func initialize() error {
|
||||
password, err := passwd.Get("Enter passphrase: ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("get passphrase: %v\n", err)
|
||||
}
|
||||
|
||||
confirm, err := passwd.Get("Confirm passphrase: ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("get passphrase: %v\n", err)
|
||||
}
|
||||
if !bytes.Equal(password, confirm) {
|
||||
return fmt.Errorf("Passphrase mismatch, try again.")
|
||||
}
|
||||
|
||||
ring, err := keyring.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ring.Set(*service, *username, password)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
narg := flag.NArg()
|
||||
if narg > 2 {
|
||||
usage()
|
||||
}
|
||||
if runtime.GOOS == "windows" && narg == 0 {
|
||||
usage()
|
||||
}
|
||||
|
||||
if *initKeyring {
|
||||
if err := initialize(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "initialize keyring: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
password, err := passphrase()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
defer func() {
|
||||
for i := range password {
|
||||
password[i] = 0
|
||||
}
|
||||
}()
|
||||
|
||||
in := os.Stdin
|
||||
out := os.Stdout
|
||||
if narg > 0 {
|
||||
in, err = os.Open(flag.Arg(0))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "open %s: %v\n", flag.Arg(0), err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
if narg == 2 {
|
||||
out, err = os.Create(flag.Arg(1))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "create %s: %v\n", flag.Arg(1), err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer func() {
|
||||
if err := out.Sync(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "sync %s: %v\n", flag.Arg(1), err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := out.Close(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "sync %s: %v\n", flag.Arg(1), err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
c := &crypt.Crypter{
|
||||
HashFunc: sha1.New,
|
||||
HashSize: sha1.Size,
|
||||
Key: crypt.NewPbkdf2Key(password, 32),
|
||||
}
|
||||
|
||||
if !*decrypt {
|
||||
if err := c.Encrypt(out, in); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "encrypt: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
if err := c.Decrypt(out, in); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "decrypt: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
if runtime.GOOS == "windows" {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [options] infile [outfile]\n", os.Args[0])
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [options] [infile] [[outfile]]\n", os.Args[0])
|
||||
}
|
||||
fmt.Fprint(os.Stderr, usageMsg)
|
||||
fmt.Fprintf(os.Stderr, "\nOptions:\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
const usageMsg = `
|
||||
Files are encrypted with AES (Rijndael) in cipher block counter mode
|
||||
(CTR) and authenticate with HMAC-SHA. Encryption and HMAC keys are
|
||||
derived from passphrase using PBKDF2.
|
||||
|
||||
If outfile is not specified, the de-/encrypted data is written to the
|
||||
standard output and if infile is not specified, the de-/encrypted data
|
||||
is read from standard input (reading standard input is not available
|
||||
on windows).
|
||||
`
|
|
@ -0,0 +1,79 @@
|
|||
package crypt
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
// Key defines the key derivation function interface.
|
||||
type Key interface {
|
||||
// Derive returns the AES key and HMAC-SHA key, for the given password,
|
||||
// salt combination.
|
||||
Derive(salt []byte) (aesKey, hmacKey []byte)
|
||||
|
||||
// Size returns the key-size. Key-size should either 16, 24, or 32 to
|
||||
// select AES-128, AES-192, or AES-256.
|
||||
Size() int
|
||||
|
||||
// Reset resets/flushes the key.
|
||||
Reset()
|
||||
}
|
||||
|
||||
type pbkdf2Key struct {
|
||||
password []byte
|
||||
size int
|
||||
}
|
||||
|
||||
// NewPbkdf2Key returns the key derivation function PBKDF2 as defined in
|
||||
// RFC 2898.
|
||||
func NewPbkdf2Key(password []byte, size int) Key {
|
||||
return pbkdf2Key{password: password, size: size}
|
||||
}
|
||||
|
||||
func (k pbkdf2Key) Derive(salt []byte) (aesKey, hmacKey []byte) {
|
||||
key := pbkdf2.Key(k.password, salt, 4096, 2*k.size, sha1.New)
|
||||
aesKey = key[:k.size]
|
||||
hmacKey = key[k.size:]
|
||||
return aesKey, hmacKey
|
||||
}
|
||||
|
||||
func (k pbkdf2Key) Size() int { return k.size }
|
||||
|
||||
func (k pbkdf2Key) Reset() {
|
||||
for i := range k.password {
|
||||
k.password[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
type scryptKey struct {
|
||||
password []byte
|
||||
size int
|
||||
}
|
||||
|
||||
// NewScryptKey returns the scrypt key derivation function as defined in
|
||||
// Colin Percival's paper "Stronger Key Derivation via Sequential
|
||||
// Memory-Hard Functions".
|
||||
func NewScryptKey(password []byte, size int) Key {
|
||||
return scryptKey{password: password, size: size}
|
||||
}
|
||||
|
||||
func (k scryptKey) Derive(salt []byte) (aesKey, hmacKey []byte) {
|
||||
key, err := scrypt.Key(k.password, salt, 16384, 8, 1, 2*k.size)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
aesKey = key[:k.size]
|
||||
hmacKey = key[k.size:]
|
||||
return aesKey, hmacKey
|
||||
}
|
||||
|
||||
func (k scryptKey) Size() int { return k.size }
|
||||
|
||||
func (k scryptKey) Reset() {
|
||||
for i := range k.password {
|
||||
k.password[i] = 0
|
||||
}
|
||||
}
|
|
@ -142,7 +142,7 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
|
|||
//
|
||||
// In the case where CA server does not provide the issued certificate in the response,
|
||||
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
|
||||
// In such scenario the caller can cancel the polling with ctx.
|
||||
// In such a scenario, the caller can cancel the polling with ctx.
|
||||
//
|
||||
// CreateCert returns an error if the CA's response or chain was unreasonably large.
|
||||
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
|
||||
|
@ -257,7 +257,7 @@ func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte,
|
|||
func AcceptTOS(tosURL string) bool { return true }
|
||||
|
||||
// Register creates a new account registration by following the "new-reg" flow.
|
||||
// It returns registered account. The account is not modified.
|
||||
// It returns the registered account. The account is not modified.
|
||||
//
|
||||
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
|
||||
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
|
||||
|
@ -995,6 +995,7 @@ func keyAuth(pub crypto.PublicKey, token string) (string, error) {
|
|||
|
||||
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
|
||||
// with the given SANs and auto-generated public/private key pair.
|
||||
// The Subject Common Name is set to the first SAN to aid debugging.
|
||||
// To create a cert with a custom key pair, specify WithKey option.
|
||||
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
||||
var (
|
||||
|
@ -1033,6 +1034,9 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
|
|||
}
|
||||
}
|
||||
tmpl.DNSNames = san
|
||||
if len(san) > 0 {
|
||||
tmpl.Subject.CommonName = san[0]
|
||||
}
|
||||
|
||||
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
|
||||
if err != nil {
|
||||
|
|
|
@ -1186,6 +1186,9 @@ func TestTLSSNI01ChallengeCert(t *testing.T) {
|
|||
if cert.DNSNames[0] != name {
|
||||
t.Errorf("cert.DNSNames[0] != name: %q vs %q", cert.DNSNames[0], name)
|
||||
}
|
||||
if cn := cert.Subject.CommonName; cn != san {
|
||||
t.Errorf("cert.Subject.CommonName = %q; want %q", cn, san)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSSNI02ChallengeCert(t *testing.T) {
|
||||
|
@ -1219,6 +1222,9 @@ func TestTLSSNI02ChallengeCert(t *testing.T) {
|
|||
if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
|
||||
t.Errorf("%v doesn't have %q", cert.DNSNames, name)
|
||||
}
|
||||
if cn := cert.Subject.CommonName; cn != sanA {
|
||||
t.Errorf("CommonName = %q; want %q", cn, sanA)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSChallengeCertOpt(t *testing.T) {
|
||||
|
|
|
@ -371,7 +371,7 @@ func (m *Manager) createCert(ctx context.Context, domain string) (*tls.Certifica
|
|||
|
||||
// We are the first; state is locked.
|
||||
// Unblock the readers when domain ownership is verified
|
||||
// and the we got the cert or the process failed.
|
||||
// and we got the cert or the process failed.
|
||||
defer state.Unlock()
|
||||
state.locked = false
|
||||
|
||||
|
@ -439,7 +439,7 @@ func (m *Manager) certState(domain string) (*certState, error) {
|
|||
return state, nil
|
||||
}
|
||||
|
||||
// authorizedCert starts domain ownership verification process and requests a new cert upon success.
|
||||
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
|
||||
// The key argument is the certificate private key.
|
||||
func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, domain string) (der [][]byte, leaf *x509.Certificate, err error) {
|
||||
if err := m.verify(ctx, domain); err != nil {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
@ -187,6 +188,10 @@ func Dial(network, addr string, config *ClientConfig) (*Client, error) {
|
|||
// net.Conn underlying the the SSH connection.
|
||||
type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
|
||||
|
||||
// BannerCallback is the function type used for treat the banner sent by
|
||||
// the server. A BannerCallback receives the message sent by the remote server.
|
||||
type BannerCallback func(message string) error
|
||||
|
||||
// A ClientConfig structure is used to configure a Client. It must not be
|
||||
// modified after having been passed to an SSH function.
|
||||
type ClientConfig struct {
|
||||
|
@ -209,6 +214,12 @@ type ClientConfig struct {
|
|||
// FixedHostKey can be used for simplistic host key checks.
|
||||
HostKeyCallback HostKeyCallback
|
||||
|
||||
// BannerCallback is called during the SSH dance to display a custom
|
||||
// server's message. The client configuration can supply this callback to
|
||||
// handle it as wished. The function BannerDisplayStderr can be used for
|
||||
// simplistic display on Stderr.
|
||||
BannerCallback BannerCallback
|
||||
|
||||
// ClientVersion contains the version identification string that will
|
||||
// be used for the connection. If empty, a reasonable default is used.
|
||||
ClientVersion string
|
||||
|
@ -255,3 +266,13 @@ func FixedHostKey(key PublicKey) HostKeyCallback {
|
|||
hk := &fixedHostKey{key}
|
||||
return hk.check
|
||||
}
|
||||
|
||||
// BannerDisplayStderr returns a function that can be used for
|
||||
// ClientConfig.BannerCallback to display banners on os.Stderr.
|
||||
func BannerDisplayStderr() BannerCallback {
|
||||
return func(banner string) error {
|
||||
_, err := os.Stderr.WriteString(banner)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -283,7 +283,9 @@ func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
|
|||
}
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
// TODO(gpaul): add callback to present the banner to the user
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, err
|
||||
}
|
||||
case msgUserAuthPubKeyOk:
|
||||
var msg userAuthPubKeyOkMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
|
@ -325,7 +327,9 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
|
|||
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
// TODO: add callback to present the banner to the user
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
case msgUserAuthFailure:
|
||||
var msg userAuthFailureMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
|
@ -340,6 +344,24 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func handleBannerResponse(c packetConn, packet []byte) error {
|
||||
var msg userAuthBannerMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transport, ok := c.(*handshakeTransport)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if transport.bannerCallback != nil {
|
||||
return transport.bannerCallback(msg.Message)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyboardInteractiveChallenge should print questions, optionally
|
||||
// disabling echoing (e.g. for passwords), and return all the answers.
|
||||
// Challenge may be called multiple times in a single session. After
|
||||
|
@ -385,7 +407,9 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
|||
// like handleAuthResponse, but with less options.
|
||||
switch packet[0] {
|
||||
case msgUserAuthBanner:
|
||||
// TODO: Print banners during userauth.
|
||||
if err := handleBannerResponse(c, packet); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
continue
|
||||
case msgUserAuthInfoRequest:
|
||||
// OK
|
||||
|
|
|
@ -79,3 +79,40 @@ func TestHostKeyCheck(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
func TestBannerCallback(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
NoClientAuth: true,
|
||||
BannerCallback: func(conn ConnMetadata) string {
|
||||
return "Hello World"
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
go NewServerConn(c1, serverConf)
|
||||
|
||||
var receivedBanner string
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
BannerCallback: func(message string) error {
|
||||
receivedBanner = message
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := "Hello World"
|
||||
if receivedBanner != expected {
|
||||
t.Fatalf("got %s; want %s", receivedBanner, expected)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,11 @@ type handshakeTransport struct {
|
|||
dialAddress string
|
||||
remoteAddr net.Addr
|
||||
|
||||
// bannerCallback is non-empty if we are the client and it has been set in
|
||||
// ClientConfig. In that case it is called during the user authentication
|
||||
// dance to handle a custom server's message.
|
||||
bannerCallback BannerCallback
|
||||
|
||||
// Algorithms agreed in the last key exchange.
|
||||
algorithms *algorithms
|
||||
|
||||
|
@ -120,6 +125,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
|
|||
t.dialAddress = dialAddr
|
||||
t.remoteAddr = addr
|
||||
t.hostKeyCallback = config.HostKeyCallback
|
||||
t.bannerCallback = config.BannerCallback
|
||||
if config.HostKeyAlgorithms != nil {
|
||||
t.hostKeyAlgorithms = config.HostKeyAlgorithms
|
||||
} else {
|
||||
|
|
|
@ -23,10 +23,6 @@ const (
|
|||
msgUnimplemented = 3
|
||||
msgDebug = 4
|
||||
msgNewKeys = 21
|
||||
|
||||
// Standard authentication messages
|
||||
msgUserAuthSuccess = 52
|
||||
msgUserAuthBanner = 53
|
||||
)
|
||||
|
||||
// SSH messages:
|
||||
|
@ -137,6 +133,16 @@ type userAuthFailureMsg struct {
|
|||
PartialSuccess bool
|
||||
}
|
||||
|
||||
// See RFC 4252, section 5.1
|
||||
const msgUserAuthSuccess = 52
|
||||
|
||||
// See RFC 4252, section 5.4
|
||||
const msgUserAuthBanner = 53
|
||||
|
||||
type userAuthBannerMsg struct {
|
||||
Message string `sshtype:"53"`
|
||||
}
|
||||
|
||||
// See RFC 4256, section 3.2
|
||||
const msgUserAuthInfoRequest = 60
|
||||
const msgUserAuthInfoResponse = 61
|
||||
|
|
|
@ -95,6 +95,10 @@ type ServerConfig struct {
|
|||
// Note that RFC 4253 section 4.2 requires that this string start with
|
||||
// "SSH-2.0-".
|
||||
ServerVersion string
|
||||
|
||||
// BannerCallback, if present, is called and the return string is sent to
|
||||
// the client after key exchange completed but before authentication.
|
||||
BannerCallback func(conn ConnMetadata) string
|
||||
}
|
||||
|
||||
// AddHostKey adds a private key as a host key. If an existing host
|
||||
|
@ -343,6 +347,19 @@ userAuthLoop:
|
|||
}
|
||||
|
||||
s.user = userAuthReq.User
|
||||
|
||||
if authFailures == 0 && config.BannerCallback != nil {
|
||||
msg := config.BannerCallback(s)
|
||||
if msg != "" {
|
||||
bannerMsg := &userAuthBannerMsg{
|
||||
Message: msg,
|
||||
}
|
||||
if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
perms = nil
|
||||
authErr := errors.New("no auth passed yet")
|
||||
|
||||
|
|
Loading…
Reference in New Issue