OLD | NEW |
1 // Copyright 2012 The Go Authors. All rights reserved. | 1 // Copyright 2012 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package ssh | 5 package ssh |
6 | 6 |
7 import ( | 7 import ( |
| 8 "bytes" |
8 "crypto/dsa" | 9 "crypto/dsa" |
9 "crypto/rsa" | 10 "crypto/rsa" |
| 11 "encoding/base64" |
10 "math/big" | 12 "math/big" |
11 ) | 13 ) |
12 | 14 |
| 15 // Keytypes supported by OpenSSH 5.9 |
| 16 const ( |
| 17 keyAlgoRSA = "ssh-rsa" |
| 18 keyAlgoDSA = "ssh-dss" |
| 19 keyAlgoECDSA256 = "ecdsa-sha2-nistp256" |
| 20 keyAlgoECDSA384 = "ecdsa-sha2-nistp384" |
| 21 keyAlgoECDSA521 = "ecdsa-sha2-nistp521" |
| 22 ) |
| 23 |
13 // parsePubKey parses a public key according to RFC 4253, section 6.6. | 24 // parsePubKey parses a public key according to RFC 4253, section 6.6. |
14 func parsePubKey(in []byte) (out interface{}, rest []byte, ok bool) { | 25 func parsePubKey(in []byte) (out interface{}, rest []byte, ok bool) { |
15 algo, in, ok := parseString(in) | 26 algo, in, ok := parseString(in) |
16 if !ok { | 27 if !ok { |
17 return | 28 return |
18 } | 29 } |
19 | 30 |
20 switch string(algo) { | 31 switch string(algo) { |
21 case hostAlgoRSA: | 32 case hostAlgoRSA: |
22 return parseRSA(in) | 33 return parseRSA(in) |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
111 length += intLength(key.Y) | 122 length += intLength(key.Y) |
112 | 123 |
113 ret := make([]byte, length) | 124 ret := make([]byte, length) |
114 r := marshalInt(ret, key.P) | 125 r := marshalInt(ret, key.P) |
115 r = marshalInt(r, key.Q) | 126 r = marshalInt(r, key.Q) |
116 r = marshalInt(r, key.G) | 127 r = marshalInt(r, key.G) |
117 marshalInt(r, key.Y) | 128 marshalInt(r, key.Y) |
118 | 129 |
119 return ret | 130 return ret |
120 } | 131 } |
| 132 |
| 133 // parseAuthorizedKey parses a public key in OpenSSH authorized_keys format |
| 134 // (see sshd(8) manual page). in contains the line after the options and key |
| 135 // type fields have been removed. |
| 136 func parseAuthorizedKey(in []byte) (out interface{}, comment string, ok bool) { |
| 137 in = bytes.TrimSpace(in) |
| 138 |
| 139 i := bytes.IndexAny(in, " \t") |
| 140 if i == -1 { |
| 141 i = len(in) |
| 142 } |
| 143 base64Key := in[:i] |
| 144 |
| 145 key := make([]byte, base64.StdEncoding.DecodedLen(len(base64Key))) |
| 146 n, err := base64.StdEncoding.Decode(key, base64Key) |
| 147 if err != nil { |
| 148 return |
| 149 } |
| 150 key = key[:n] |
| 151 out, _, ok = parsePubKey(key) |
| 152 if !ok { |
| 153 return nil, "", false |
| 154 } |
| 155 comment = string(bytes.TrimSpace(in[i:])) |
| 156 return |
| 157 } |
| 158 |
| 159 // ParseAuthorizedKeys parses a public key from an authorized_keys |
| 160 // file used in OpenSSH according to the sshd(8) manual page. |
| 161 func ParseAuthorizedKey(in []byte) (out interface{}, comment string, options []s
tring, rest []byte, ok bool) { |
| 162 for len(in) > 0 { |
| 163 end := bytes.IndexByte(in, '\n') |
| 164 if end != -1 { |
| 165 rest = in[end+1:] |
| 166 in = in[:end] |
| 167 } else { |
| 168 rest = nil |
| 169 } |
| 170 |
| 171 end = bytes.IndexByte(in, '\r') |
| 172 if end != -1 { |
| 173 in = in[:end] |
| 174 } |
| 175 |
| 176 in = bytes.TrimSpace(in) |
| 177 if len(in) == 0 || in[0] == '#' { |
| 178 in = rest |
| 179 continue |
| 180 } |
| 181 |
| 182 i := bytes.IndexAny(in, " \t") |
| 183 if i == -1 { |
| 184 in = rest |
| 185 continue |
| 186 } |
| 187 |
| 188 field := string(in[:i]) |
| 189 switch field { |
| 190 case keyAlgoRSA, keyAlgoDSA: |
| 191 out, comment, ok = parseAuthorizedKey(in[i:]) |
| 192 if ok { |
| 193 return |
| 194 } |
| 195 case keyAlgoECDSA256, keyAlgoECDSA384, keyAlgoECDSA521: |
| 196 // We don't support these keys. |
| 197 in = rest |
| 198 continue |
| 199 case hostAlgoRSACertV01, hostAlgoDSACertV01, |
| 200 hostAlgoECDSA256CertV01, hostAlgoECDSA384CertV01, hostAl
goECDSA521CertV01: |
| 201 // We don't support these certificates. |
| 202 in = rest |
| 203 continue |
| 204 } |
| 205 |
| 206 // No key type recognised. Maybe there's an options field at |
| 207 // the beginning. |
| 208 var b byte |
| 209 inQuote := false |
| 210 var candidateOptions []string |
| 211 optionStart := 0 |
| 212 for i, b = range in { |
| 213 isEnd := !inQuote && (b == ' ' || b == '\t') |
| 214 if (b == ',' && !inQuote) || isEnd { |
| 215 if i-optionStart > 0 { |
| 216 candidateOptions = append(candidateOptio
ns, string(in[optionStart:i])) |
| 217 } |
| 218 optionStart = i + 1 |
| 219 } |
| 220 if isEnd { |
| 221 break |
| 222 } |
| 223 if b == '"' && (i == 0 || (i > 0 && in[i-1] != '\\')) { |
| 224 inQuote = !inQuote |
| 225 } |
| 226 } |
| 227 for i < len(in) && (in[i] == ' ' || in[i] == '\t') { |
| 228 i++ |
| 229 } |
| 230 if i == len(in) { |
| 231 // Invalid line: unmatched quote |
| 232 in = rest |
| 233 continue |
| 234 } |
| 235 |
| 236 in = in[i:] |
| 237 i = bytes.IndexAny(in, " \t") |
| 238 if i == -1 { |
| 239 in = rest |
| 240 continue |
| 241 } |
| 242 |
| 243 field = string(in[:i]) |
| 244 switch field { |
| 245 case keyAlgoRSA, keyAlgoDSA: |
| 246 out, comment, ok = parseAuthorizedKey(in[i:]) |
| 247 if ok { |
| 248 options = candidateOptions |
| 249 return |
| 250 } |
| 251 } |
| 252 |
| 253 in = rest |
| 254 continue |
| 255 } |
| 256 |
| 257 return |
| 258 } |
| 259 |
| 260 // ParsePublicKey parses an ssh public key formatted for use in |
| 261 // on the wire protocol of SSH. |
| 262 func ParsePublicKey(in []byte) (out interface{}, rest []byte, ok bool) { |
| 263 return parsePubKey(in) |
| 264 } |
| 265 |
| 266 // MarshalAuthorizedKey returns a byte stream suitable for inclusion |
| 267 // in an OpenSSH authorized_keys file following the format specified |
| 268 // in the sshd(8) manual page. |
| 269 func MarshalAuthorizedKey(key interface{}) []byte { |
| 270 b := &bytes.Buffer{} |
| 271 switch keyType := key.(type) { |
| 272 case *rsa.PublicKey: |
| 273 b.WriteString(hostAlgoRSA) |
| 274 case *dsa.PublicKey: |
| 275 b.WriteString(hostAlgoDSA) |
| 276 case *OpenSSHCertV01: |
| 277 switch keyType.Key.(type) { |
| 278 case *rsa.PublicKey: |
| 279 b.WriteString(hostAlgoRSACertV01) |
| 280 case *dsa.PublicKey: |
| 281 b.WriteString(hostAlgoDSACertV01) |
| 282 default: |
| 283 panic("unexpected key type") |
| 284 } |
| 285 default: |
| 286 panic("unexpected key type") |
| 287 } |
| 288 |
| 289 b.WriteByte(' ') |
| 290 e := base64.NewEncoder(base64.StdEncoding, b) |
| 291 e.Write(serializePublickey(key)) |
| 292 e.Close() |
| 293 b.WriteByte('\n') |
| 294 return b.Bytes() |
| 295 } |
| 296 |
| 297 // MarshalPublicKey serializes a *rsa.PublicKey, *dsa.PublicKey or |
| 298 // *OpenSSHCertV01 for use on the wire protocol of SSH. It can be |
| 299 // used for comparison with the pubkey argument of ServerConfig's |
| 300 // PublickeyCallback as well as generating an authorized_keys or |
| 301 // host_keys file. |
| 302 func MarshalPublicKey(key interface{}) []byte { |
| 303 return serializePublickey(key) |
| 304 } |
OLD | NEW |