Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(724)

Unified Diff: ssh/keys.go

Issue 6855107: code review 6855107: ssh: add functions for public keys in wire & auth keys ...
Patch Set: diff -r b2b2ae1f5140 https://code.google.com/p/go.crypto Created 11 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | ssh/test/keys_test.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: ssh/keys.go
===================================================================
--- a/ssh/keys.go
+++ b/ssh/keys.go
@@ -5,11 +5,22 @@
package ssh
import (
+ "bytes"
"crypto/dsa"
"crypto/rsa"
+ "encoding/base64"
"math/big"
)
+// Keytypes supported by OpenSSH 5.9
+const (
+ keyAlgoRSA = "ssh-rsa"
+ keyAlgoDSA = "ssh-dss"
+ keyAlgoECDSA256 = "ecdsa-sha2-nistp256"
+ keyAlgoECDSA384 = "ecdsa-sha2-nistp384"
+ keyAlgoECDSA521 = "ecdsa-sha2-nistp521"
+)
+
// parsePubKey parses a public key according to RFC 4253, section 6.6.
func parsePubKey(in []byte) (out interface{}, rest []byte, ok bool) {
algo, in, ok := parseString(in)
@@ -118,3 +129,176 @@
return ret
}
+
+// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
+// (see sshd(8) manual page). in contains the line after the options and key
+// type fields have been removed.
+func parseAuthorizedKey(in []byte) (out interface{}, comment string, ok bool) {
+ in = bytes.TrimSpace(in)
+
+ i := bytes.IndexAny(in, " \t")
+ if i == -1 {
+ i = len(in)
+ }
+ base64Key := in[:i]
+
+ key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key)))
+ n, err := base64.StdEncoding.Decode(key, base64Key)
+ if err != nil {
+ return
+ }
+ key = key[:n]
+ out, _, ok = parsePubKey(key)
+ if !ok {
+ return nil, "", false
+ }
+ comment = string(bytes.TrimSpace(in[i:]))
+ return
+}
+
+// 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 interface{}, comment string, options []string, rest []byte, ok bool) {
+ for len(in) > 0 {
+ end := bytes.IndexByte(in, '\n')
+ if end != -1 {
+ rest = in[end+1:]
+ in = in[:end]
+ } else {
+ rest = nil
+ }
+
+ end = bytes.IndexByte(in, '\r')
+ if end != -1 {
+ in = in[:end]
+ }
+
+ in = bytes.TrimSpace(in)
+ if len(in) == 0 || in[0] == '#' {
+ in = rest
+ continue
+ }
+
+ i := bytes.IndexAny(in, " \t")
+ if i == -1 {
+ in = rest
+ continue
+ }
+
+ field := string(in[:i])
+ switch field {
+ case keyAlgoRSA, keyAlgoDSA:
+ out, comment, ok = parseAuthorizedKey(in[i:])
+ if ok {
+ return
+ }
+ case keyAlgoECDSA256, keyAlgoECDSA384, keyAlgoECDSA521:
+ // We don't support these keys.
+ in = rest
+ continue
+ case hostAlgoRSACertV01, hostAlgoDSACertV01,
+ hostAlgoECDSA256CertV01, hostAlgoECDSA384CertV01, hostAlgoECDSA521CertV01:
+ // We don't support these certificates.
+ in = rest
+ continue
+ }
+
+ // No key type recognised. Maybe there's an options field at
+ // the beginning.
+ var b byte
+ inQuote := false
+ var candidateOptions []string
+ optionStart := 0
+ for i, b = range in {
+ isEnd := !inQuote && (b == ' ' || b == '\t')
+ if (b == ',' && !inQuote) || isEnd {
+ if i-optionStart > 0 {
+ candidateOptions = append(candidateOptions, string(in[optionStart:i]))
+ }
+ optionStart = i + 1
+ }
+ if isEnd {
+ break
+ }
+ if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) {
+ inQuote = !inQuote
+ }
+ }
+ for i < len(in) && (in[i] == ' ' || in[i] == '\t') {
+ i++
+ }
+ if i == len(in) {
+ // Invalid line: unmatched quote
+ in = rest
+ continue
+ }
+
+ in = in[i:]
+ i = bytes.IndexAny(in, " \t")
+ if i == -1 {
+ in = rest
+ continue
+ }
+
+ field = string(in[:i])
+ switch field {
+ case keyAlgoRSA, keyAlgoDSA:
+ out, comment, ok = parseAuthorizedKey(in[i:])
+ if ok {
+ options = candidateOptions
+ return
+ }
+ }
+
+ in = rest
+ continue
+ }
+
+ return
+}
+
+// ParsePublicKey parses an ssh public key formatted for use in
+// on the wire protocol of SSH.
+func ParsePublicKey(in []byte) (out interface{}, rest []byte, ok bool) {
+ return parsePubKey(in)
+}
+
+// MarshalAuthorizedKey returns a byte stream suitable for inclusion
+// in an OpenSSH authorized_keys file following the format specified
+// in the sshd(8) manual page.
+func MarshalAuthorizedKey(key interface{}) []byte {
+ b := &bytes.Buffer{}
+ switch keyType := key.(type) {
+ case *rsa.PublicKey:
+ b.WriteString(hostAlgoRSA)
+ case *dsa.PublicKey:
+ b.WriteString(hostAlgoDSA)
+ case *OpenSSHCertV01:
+ switch keyType.Key.(type) {
+ case *rsa.PublicKey:
+ b.WriteString(hostAlgoRSACertV01)
+ case *dsa.PublicKey:
+ b.WriteString(hostAlgoDSACertV01)
+ default:
+ panic("unexpected key type")
+ }
+ default:
+ panic("unexpected key type")
+ }
+
+ b.WriteByte(' ')
+ e := base64.NewEncoder(base64.StdEncoding, b)
+ e.Write(serializePublickey(key))
+ e.Close()
+ b.WriteByte('\n')
+ return b.Bytes()
+}
+
+// MarshalPublicKey serializes a *rsa.PublicKey, *dsa.PublicKey or
+// *OpenSSHCertV01 for use on the wire protocol of SSH. It can be
+// used for comparison with the pubkey argument of ServerConfig's
+// PublickeyCallback as well as generating an authorized_keys or
+// host_keys file.
+func MarshalPublicKey(key interface{}) []byte {
+ return serializePublickey(key)
+}
« no previous file with comments | « no previous file | ssh/test/keys_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b