diff options
| author | Adam Langley <agl@golang.org> | 2014-04-09 13:57:52 -0700 |
|---|---|---|
| committer | Adam Langley <agl@golang.org> | 2014-04-09 13:57:52 -0700 |
| commit | fa50e7408b9ef89ff2965535b59f1a0010c0770b (patch) | |
| tree | e045a3f48f9ffd3bb712002f8f9f6fd489e8f7ef /ssh/keys.go | |
| parent | 8f45c680ceb25c200b8c301d9184532aeb7cb36e (diff) | |
| download | go-x-crypto-fa50e7408b9ef89ff2965535b59f1a0010c0770b.tar.xz | |
go.crypto/ssh: import gosshnew.
See https://groups.google.com/d/msg/Golang-nuts/AoVxQ4bB5XQ/i8kpMxdbVlEJ
R=hanwen
CC=golang-codereviews
https://golang.org/cl/86190043
Diffstat (limited to 'ssh/keys.go')
| -rw-r--r-- | ssh/keys.go | 367 |
1 files changed, 193 insertions, 174 deletions
diff --git a/ssh/keys.go b/ssh/keys.go index b41fefc..e8af511 100644 --- a/ssh/keys.go +++ b/ssh/keys.go @@ -33,7 +33,7 @@ const ( // parsePubKey parses a public key of the given algorithm. // Use ParsePublicKey for keys with prepended algorithm. -func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, ok bool) { +func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err error) { switch algo { case KeyAlgoRSA: return parseRSA(in) @@ -42,15 +42,19 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, ok bool case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: return parseECDSA(in) case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01: - return parseOpenSSHCertV01(in, algo) + cert, err := parseCert(in, certToPrivAlgo(algo)) + if err != nil { + return nil, nil, err + } + return cert, nil, nil } - return nil, nil, false + return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", err) } // parseAuthorizedKey parses a public key in OpenSSH authorized_keys format // (see sshd(8) manual page) once the options and key type fields have been // removed. -func parseAuthorizedKey(in []byte) (out PublicKey, comment string, ok bool) { +func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) { in = bytes.TrimSpace(in) i := bytes.IndexAny(in, " \t") @@ -62,20 +66,20 @@ func parseAuthorizedKey(in []byte) (out PublicKey, comment string, ok bool) { key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key))) n, err := base64.StdEncoding.Decode(key, base64Key) if err != nil { - return + return nil, "", err } key = key[:n] - out, _, ok = ParsePublicKey(key) - if !ok { - return nil, "", false + out, err = ParsePublicKey(key) + if err != nil { + return nil, "", err } comment = string(bytes.TrimSpace(in[i:])) - return + return out, comment, nil } // ParseAuthorizedKeys parses a public key from an authorized_keys // file used in OpenSSH according to the sshd(8) manual page. -func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, ok bool) { +func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { for len(in) > 0 { end := bytes.IndexByte(in, '\n') if end != -1 { @@ -102,8 +106,8 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str continue } - if out, comment, ok = parseAuthorizedKey(in[i:]); ok { - return + if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { + return out, comment, options, rest, nil } // No key type recognised. Maybe there's an options field at @@ -143,38 +147,42 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str continue } - if out, comment, ok = parseAuthorizedKey(in[i:]); ok { + if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { options = candidateOptions - return + return out, comment, options, rest, nil } in = rest continue } - return + return nil, "", nil, nil, errors.New("ssh: no key found") } // ParsePublicKey parses an SSH public key formatted for use in // the SSH wire protocol according to RFC 4253, section 6.6. -func ParsePublicKey(in []byte) (out PublicKey, rest []byte, ok bool) { +func ParsePublicKey(in []byte) (out PublicKey, err error) { algo, in, ok := parseString(in) if !ok { - return + return nil, errShortRead + } + var rest []byte + out, rest, err = parsePubKey(in, string(algo)) + if len(rest) > 0 { + return nil, errors.New("ssh: trailing junk in public key") } - return parsePubKey(in, string(algo)) + return out, err } -// MarshalAuthorizedKey returns a byte stream suitable for inclusion -// in an OpenSSH authorized_keys file following the format specified -// in the sshd(8) manual page. +// MarshalAuthorizedKey serializes key for inclusion in an OpenSSH +// authorized_keys file. The return value ends with newline. func MarshalAuthorizedKey(key PublicKey) []byte { b := &bytes.Buffer{} - b.WriteString(key.PublicKeyAlgo()) + b.WriteString(key.Type()) b.WriteByte(' ') e := base64.NewEncoder(base64.StdEncoding, b) - e.Write(MarshalPublicKey(key)) + e.Write(key.Marshal()) e.Close() b.WriteByte('\n') return b.Bytes() @@ -182,84 +190,81 @@ func MarshalAuthorizedKey(key PublicKey) []byte { // PublicKey is an abstraction of different types of public keys. type PublicKey interface { - // PrivateKeyAlgo returns the name of the encryption system. - PrivateKeyAlgo() string - - // PublicKeyAlgo returns the algorithm for the public key, - // which may be different from PrivateKeyAlgo for certificates. - PublicKeyAlgo() string + // Type returns the key's type, e.g. "ssh-rsa". + Type() string // Marshal returns the serialized key data in SSH wire format, - // without the name prefix. Callers should typically use - // MarshalPublicKey(). + // with the name prefix. Marshal() []byte // Verify that sig is a signature on the given data using this // key. This function will hash the data appropriately first. - Verify(data []byte, sigBlob []byte) bool + Verify(data []byte, sig *Signature) error } -// A Signer is can create signatures that verify against a public key. +// A Signer can create signatures that verify against a public key. type Signer interface { // PublicKey returns an associated PublicKey instance. PublicKey() PublicKey // Sign returns raw signature for the given data. This method // will apply the hash specified for the keytype to the data. - Sign(rand io.Reader, data []byte) ([]byte, error) + Sign(rand io.Reader, data []byte) (*Signature, error) } type rsaPublicKey rsa.PublicKey -func (r *rsaPublicKey) PrivateKeyAlgo() string { +func (r *rsaPublicKey) Type() string { return "ssh-rsa" } -func (r *rsaPublicKey) PublicKeyAlgo() string { - return r.PrivateKeyAlgo() -} - // parseRSA parses an RSA key according to RFC 4253, section 6.6. -func parseRSA(in []byte) (out PublicKey, rest []byte, ok bool) { - key := new(rsa.PublicKey) - - bigE, in, ok := parseInt(in) - if !ok || bigE.BitLen() > 24 { - return +func parseRSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + E *big.Int + N *big.Int + Rest []byte `ssh:"rest"` } - e := bigE.Int64() - if e < 3 || e&1 == 0 { - ok = false - return + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err } - key.E = int(e) - if key.N, in, ok = parseInt(in); !ok { - return + if w.E.BitLen() > 24 { + return nil, nil, errors.New("ssh: exponent too large") + } + e := w.E.Int64() + if e < 3 || e&1 == 0 { + return nil, nil, errors.New("ssh: incorrect exponent") } - ok = true - return (*rsaPublicKey)(key), in, ok + var key rsa.PublicKey + key.E = int(e) + key.N = w.N + return (*rsaPublicKey)(&key), w.Rest, nil } func (r *rsaPublicKey) Marshal() []byte { - // See RFC 4253, section 6.6. e := new(big.Int).SetInt64(int64(r.E)) - length := intLength(e) - length += intLength(r.N) - - ret := make([]byte, length) - rest := marshalInt(ret, e) - marshalInt(rest, r.N) - - return ret + wirekey := struct { + Name string + E *big.Int + N *big.Int + }{ + KeyAlgoRSA, + e, + r.N, + } + return Marshal(&wirekey) } -func (r *rsaPublicKey) Verify(data []byte, sig []byte) bool { +func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != r.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type()) + } h := crypto.SHA1.New() h.Write(data) digest := h.Sum(nil) - return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig) == nil + return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob) } type rsaPrivateKey struct { @@ -270,64 +275,66 @@ func (r *rsaPrivateKey) PublicKey() PublicKey { return (*rsaPublicKey)(&r.PrivateKey.PublicKey) } -func (r *rsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) { +func (r *rsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { h := crypto.SHA1.New() h.Write(data) digest := h.Sum(nil) - return rsa.SignPKCS1v15(rand, r.PrivateKey, crypto.SHA1, digest) + blob, err := rsa.SignPKCS1v15(rand, r.PrivateKey, crypto.SHA1, digest) + if err != nil { + return nil, err + } + return &Signature{ + Format: r.PublicKey().Type(), + Blob: blob, + }, nil } type dsaPublicKey dsa.PublicKey -func (r *dsaPublicKey) PrivateKeyAlgo() string { +func (r *dsaPublicKey) Type() string { return "ssh-dss" } -func (r *dsaPublicKey) PublicKeyAlgo() string { - return r.PrivateKeyAlgo() -} - // parseDSA parses an DSA key according to RFC 4253, section 6.6. -func parseDSA(in []byte) (out PublicKey, rest []byte, ok bool) { - key := new(dsa.PublicKey) - - if key.P, in, ok = parseInt(in); !ok { - return - } - - if key.Q, in, ok = parseInt(in); !ok { - return +func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { + var w struct { + P, Q, G, Y *big.Int + Rest []byte `ssh:"rest"` } - - if key.G, in, ok = parseInt(in); !ok { - return + if err := Unmarshal(in, &w); err != nil { + return nil, nil, err } - if key.Y, in, ok = parseInt(in); !ok { - return + key := &dsaPublicKey{ + Parameters: dsa.Parameters{ + P: w.P, + Q: w.Q, + G: w.G, + }, + Y: w.Y, } - - ok = true - return (*dsaPublicKey)(key), in, ok + return key, w.Rest, nil } -func (r *dsaPublicKey) Marshal() []byte { - // See RFC 4253, section 6.6. - length := intLength(r.P) - length += intLength(r.Q) - length += intLength(r.G) - length += intLength(r.Y) - - ret := make([]byte, length) - rest := marshalInt(ret, r.P) - rest = marshalInt(rest, r.Q) - rest = marshalInt(rest, r.G) - marshalInt(rest, r.Y) +func (k *dsaPublicKey) Marshal() []byte { + w := struct { + Name string + P, Q, G, Y *big.Int + }{ + k.Type(), + k.P, + k.Q, + k.G, + k.Y, + } - return ret + return Marshal(&w) } -func (k *dsaPublicKey) Verify(data []byte, sigBlob []byte) bool { +func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != k.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type()) + } h := crypto.SHA1.New() h.Write(data) digest := h.Sum(nil) @@ -337,12 +344,15 @@ func (k *dsaPublicKey) Verify(data []byte, sigBlob []byte) bool { // r, followed by s (which are 160-bit integers, without lengths or // padding, unsigned, and in network byte order). // For DSS purposes, sig.Blob should be exactly 40 bytes in length. - if len(sigBlob) != 40 { - return false + if len(sig.Blob) != 40 { + return errors.New("ssh: DSA signature parse error") } - r := new(big.Int).SetBytes(sigBlob[:20]) - s := new(big.Int).SetBytes(sigBlob[20:]) - return dsa.Verify((*dsa.PublicKey)(k), digest, r, s) + r := new(big.Int).SetBytes(sig.Blob[:20]) + s := new(big.Int).SetBytes(sig.Blob[20:]) + if dsa.Verify((*dsa.PublicKey)(k), digest, r, s) { + return nil + } + return errors.New("ssh: signature did not verify") } type dsaPrivateKey struct { @@ -353,7 +363,7 @@ func (k *dsaPrivateKey) PublicKey() PublicKey { return (*dsaPublicKey)(&k.PrivateKey.PublicKey) } -func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) { +func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { h := crypto.SHA1.New() h.Write(data) digest := h.Sum(nil) @@ -363,14 +373,21 @@ func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) { } sig := make([]byte, 40) - copy(sig[:20], r.Bytes()) - copy(sig[20:], s.Bytes()) - return sig, nil + rb := r.Bytes() + sb := s.Bytes() + + copy(sig[20-len(rb):20], rb) + copy(sig[40-len(sb):], sb) + + return &Signature{ + Format: k.PublicKey().Type(), + Blob: sig, + }, nil } type ecdsaPublicKey ecdsa.PublicKey -func (key *ecdsaPublicKey) PrivateKeyAlgo() string { +func (key *ecdsaPublicKey) Type() string { return "ecdsa-sha2-" + key.nistID() } @@ -387,7 +404,7 @@ func (key *ecdsaPublicKey) nistID() string { } func supportedEllipticCurve(curve elliptic.Curve) bool { - return (curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()) + return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521() } // ecHash returns the hash to match the given elliptic curve, see RFC @@ -403,15 +420,11 @@ func ecHash(curve elliptic.Curve) crypto.Hash { return crypto.SHA512 } -func (key *ecdsaPublicKey) PublicKeyAlgo() string { - return key.PrivateKeyAlgo() -} - // parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. -func parseECDSA(in []byte) (out PublicKey, rest []byte, ok bool) { - var identifier []byte - if identifier, in, ok = parseString(in); !ok { - return +func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { + identifier, in, ok := parseString(in) + if !ok { + return nil, nil, errShortRead } key := new(ecdsa.PublicKey) @@ -424,38 +437,42 @@ func parseECDSA(in []byte) (out PublicKey, rest []byte, ok bool) { case "nistp521": key.Curve = elliptic.P521() default: - ok = false - return + return nil, nil, errors.New("ssh: unsupported curve") } var keyBytes []byte if keyBytes, in, ok = parseString(in); !ok { - return + return nil, nil, errShortRead } key.X, key.Y = elliptic.Unmarshal(key.Curve, keyBytes) if key.X == nil || key.Y == nil { - ok = false - return + return nil, nil, errors.New("ssh: invalid curve point") } - return (*ecdsaPublicKey)(key), in, ok + return (*ecdsaPublicKey)(key), in, nil } func (key *ecdsaPublicKey) Marshal() []byte { // See RFC 5656, section 3.1. keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y) + w := struct { + Name string + ID string + Key []byte + }{ + key.Type(), + key.nistID(), + keyBytes, + } - ID := key.nistID() - length := stringLength(len(ID)) - length += stringLength(len(keyBytes)) - - ret := make([]byte, length) - r := marshalString(ret, []byte(ID)) - r = marshalString(r, keyBytes) - return ret + return Marshal(&w) } -func (key *ecdsaPublicKey) Verify(data []byte, sigBlob []byte) bool { +func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error { + if sig.Format != key.Type() { + return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type()) + } + h := ecHash(key.Curve).New() h.Write(data) digest := h.Sum(nil) @@ -464,15 +481,19 @@ func (key *ecdsaPublicKey) Verify(data []byte, sigBlob []byte) bool { // The ecdsa_signature_blob value has the following specific encoding: // mpint r // mpint s - r, rest, ok := parseInt(sigBlob) - if !ok { - return false + var ecSig struct { + R *big.Int + S *big.Int + } + + if err := Unmarshal(sig.Blob, &ecSig); err != nil { + return err } - s, rest, ok := parseInt(rest) - if !ok || len(rest) > 0 { - return false + + if ecdsa.Verify((*ecdsa.PublicKey)(key), digest, ecSig.R, ecSig.S) { + return nil } - return ecdsa.Verify((*ecdsa.PublicKey)(key), digest, r, s) + return errors.New("ssh: signature did not verify") } type ecdsaPrivateKey struct { @@ -483,7 +504,7 @@ func (k *ecdsaPrivateKey) PublicKey() PublicKey { return (*ecdsaPublicKey)(&k.PrivateKey.PublicKey) } -func (k *ecdsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) { +func (k *ecdsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) { h := ecHash(k.PrivateKey.PublicKey.Curve).New() h.Write(data) digest := h.Sum(nil) @@ -495,10 +516,13 @@ func (k *ecdsaPrivateKey) Sign(rand io.Reader, data []byte) ([]byte, error) { sig := make([]byte, intLength(r)+intLength(s)) rest := marshalInt(sig, r) marshalInt(rest, s) - return sig, nil + return &Signature{ + Format: k.PublicKey().Type(), + Blob: sig, + }, nil } -// NewPrivateKey takes a pointer to rsa, dsa or ecdsa PrivateKey +// NewSignerFromKey takes a pointer to rsa, dsa or ecdsa PrivateKey // returns a corresponding Signer instance. EC keys should use P256, // P384 or P521. func NewSignerFromKey(k interface{}) (Signer, error) { @@ -540,54 +564,49 @@ func NewPublicKey(k interface{}) (PublicKey, error) { return sshKey, nil } -// ParsePublicKey parses a PEM encoded private key. It supports -// PKCS#1, RSA, DSA and ECDSA private keys. +// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports +// the same keys as ParseRawPrivateKey. func ParsePrivateKey(pemBytes []byte) (Signer, error) { + key, err := ParseRawPrivateKey(pemBytes) + if err != nil { + return nil, err + } + + return NewSignerFromKey(key) +} + +// ParseRawPrivateKey returns a private key from a PEM encoded private key. It +// supports RSA (PKCS#1), DSA (OpenSSL), and ECDSA private keys. +func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) { block, _ := pem.Decode(pemBytes) if block == nil { return nil, errors.New("ssh: no key found") } - var rawkey interface{} switch block.Type { case "RSA PRIVATE KEY": - rsa, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return nil, err - } - rawkey = rsa + return x509.ParsePKCS1PrivateKey(block.Bytes) case "EC PRIVATE KEY": - ec, err := x509.ParseECPrivateKey(block.Bytes) - if err != nil { - return nil, err - } - rawkey = ec + return x509.ParseECPrivateKey(block.Bytes) case "DSA PRIVATE KEY": - ec, err := parseDSAPrivate(block.Bytes) - if err != nil { - return nil, err - } - rawkey = ec + return ParseDSAPrivateKey(block.Bytes) default: return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type) } - - return NewSignerFromKey(rawkey) } -// parseDSAPrivate parses a DSA key in ASN.1 DER encoding, as -// documented in the OpenSSL DSA manpage. -// TODO(hanwen): move this in to crypto/x509 after the Go 1.2 freeze. -func parseDSAPrivate(p []byte) (*dsa.PrivateKey, error) { - k := struct { +// ParseDSAPrivateKey returns a DSA private key from its ASN.1 DER encoding, as +// specified by the OpenSSL DSA man page. +func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) { + var k struct { Version int P *big.Int Q *big.Int G *big.Int Priv *big.Int Pub *big.Int - }{} - rest, err := asn1.Unmarshal(p, &k) + } + rest, err := asn1.Unmarshal(der, &k) if err != nil { return nil, errors.New("ssh: failed to parse DSA key: " + err.Error()) } |
