Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 1 | // Copyright 2011 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package ssh |
| 6 | |
| 7 | import ( |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 8 | "crypto" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 9 | "crypto/dsa" |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 10 | "crypto/ecdsa" |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 11 | "crypto/elliptic" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 12 | "crypto/rsa" |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 13 | "errors" |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 14 | "fmt" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 15 | "math/big" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 16 | "sync" |
Jonathan Pittman | 6a743c5 | 2013-09-09 13:06:04 -0400 | [diff] [blame^] | 17 | |
| 18 | _ "crypto/sha1" |
| 19 | _ "crypto/sha256" |
| 20 | _ "crypto/sha512" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 21 | ) |
| 22 | |
| 23 | // These are string constants in the SSH protocol. |
| 24 | const ( |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 25 | kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 26 | kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1" |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 27 | kexAlgoECDH256 = "ecdh-sha2-nistp256" |
| 28 | kexAlgoECDH384 = "ecdh-sha2-nistp384" |
| 29 | kexAlgoECDH521 = "ecdh-sha2-nistp521" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 30 | hostAlgoRSA = "ssh-rsa" |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 31 | hostAlgoDSA = "ssh-dss" |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 32 | compressionNone = "none" |
| 33 | serviceUserAuth = "ssh-userauth" |
| 34 | serviceSSH = "ssh-connection" |
| 35 | ) |
| 36 | |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 37 | var supportedKexAlgos = []string{ |
| 38 | kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521, |
| 39 | kexAlgoDH14SHA1, kexAlgoDH1SHA1, |
| 40 | } |
| 41 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 42 | var supportedHostKeyAlgos = []string{hostAlgoRSA} |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 43 | var supportedCompressions = []string{compressionNone} |
| 44 | |
Jonathan Pittman | 6a743c5 | 2013-09-09 13:06:04 -0400 | [diff] [blame^] | 45 | // hashFuncs keeps the mapping of supported algorithms to their respective |
| 46 | // hashes needed for signature verification. |
| 47 | var hashFuncs = map[string]crypto.Hash{ |
| 48 | KeyAlgoRSA: crypto.SHA1, |
| 49 | KeyAlgoDSA: crypto.SHA1, |
| 50 | KeyAlgoECDSA256: crypto.SHA256, |
| 51 | KeyAlgoECDSA384: crypto.SHA384, |
| 52 | KeyAlgoECDSA521: crypto.SHA512, |
| 53 | CertAlgoRSAv01: crypto.SHA1, |
| 54 | CertAlgoDSAv01: crypto.SHA1, |
| 55 | CertAlgoECDSA256v01: crypto.SHA256, |
| 56 | CertAlgoECDSA384v01: crypto.SHA384, |
| 57 | CertAlgoECDSA521v01: crypto.SHA512, |
| 58 | } |
| 59 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 60 | // dhGroup is a multiplicative group suitable for implementing Diffie-Hellman key agreement. |
| 61 | type dhGroup struct { |
| 62 | g, p *big.Int |
| 63 | } |
| 64 | |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 65 | func (group *dhGroup) diffieHellman(theirPublic, myPrivate *big.Int) (*big.Int, error) { |
| 66 | if theirPublic.Sign() <= 0 || theirPublic.Cmp(group.p) >= 0 { |
| 67 | return nil, errors.New("ssh: DH parameter out of bounds") |
| 68 | } |
| 69 | return new(big.Int).Exp(theirPublic, myPrivate, group.p), nil |
| 70 | } |
| 71 | |
Daniel Theophanes | 36a967d | 2012-03-06 11:25:32 -0500 | [diff] [blame] | 72 | // dhGroup1 is the group called diffie-hellman-group1-sha1 in RFC 4253 and |
| 73 | // Oakley Group 2 in RFC 2409. |
| 74 | var dhGroup1 *dhGroup |
| 75 | |
| 76 | var dhGroup1Once sync.Once |
| 77 | |
| 78 | func initDHGroup1() { |
| 79 | p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) |
| 80 | |
| 81 | dhGroup1 = &dhGroup{ |
| 82 | g: new(big.Int).SetInt64(2), |
| 83 | p: p, |
| 84 | } |
| 85 | } |
| 86 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 87 | // dhGroup14 is the group called diffie-hellman-group14-sha1 in RFC 4253 and |
| 88 | // Oakley Group 14 in RFC 3526. |
| 89 | var dhGroup14 *dhGroup |
| 90 | |
| 91 | var dhGroup14Once sync.Once |
| 92 | |
| 93 | func initDHGroup14() { |
| 94 | p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) |
| 95 | |
| 96 | dhGroup14 = &dhGroup{ |
| 97 | g: new(big.Int).SetInt64(2), |
| 98 | p: p, |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | // UnexpectedMessageError results when the SSH message that we received didn't |
| 103 | // match what we wanted. |
| 104 | type UnexpectedMessageError struct { |
| 105 | expected, got uint8 |
| 106 | } |
| 107 | |
| 108 | func (u UnexpectedMessageError) Error() string { |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 109 | return fmt.Sprintf("ssh: unexpected message type %d (expected %d)", u.got, u.expected) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 110 | } |
| 111 | |
| 112 | // ParseError results from a malformed SSH message. |
| 113 | type ParseError struct { |
| 114 | msgType uint8 |
| 115 | } |
| 116 | |
| 117 | func (p ParseError) Error() string { |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 118 | return fmt.Sprintf("ssh: parse error in message type %d", p.msgType) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | type handshakeMagics struct { |
| 122 | clientVersion, serverVersion []byte |
| 123 | clientKexInit, serverKexInit []byte |
| 124 | } |
| 125 | |
| 126 | func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) { |
| 127 | for _, clientAlgo := range clientAlgos { |
| 128 | for _, serverAlgo := range serverAlgos { |
| 129 | if clientAlgo == serverAlgo { |
| 130 | return clientAlgo, true |
| 131 | } |
| 132 | } |
| 133 | } |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 134 | return |
| 135 | } |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 136 | |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 137 | func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCipher string, ok bool) { |
| 138 | for _, clientCipher := range clientCiphers { |
| 139 | for _, serverCipher := range serverCiphers { |
| 140 | // reject the cipher if we have no cipherModes definition |
| 141 | if clientCipher == serverCipher && cipherModes[clientCipher] != nil { |
| 142 | return clientCipher, true |
| 143 | } |
| 144 | } |
| 145 | } |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 146 | return |
| 147 | } |
| 148 | |
| 149 | func findAgreedAlgorithms(transport *transport, clientKexInit, serverKexInit *kexInitMsg) (kexAlgo, hostKeyAlgo string, ok bool) { |
| 150 | kexAlgo, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos) |
| 151 | if !ok { |
| 152 | return |
| 153 | } |
| 154 | |
| 155 | hostKeyAlgo, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos) |
| 156 | if !ok { |
| 157 | return |
| 158 | } |
| 159 | |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 160 | transport.writer.cipherAlgo, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 161 | if !ok { |
| 162 | return |
| 163 | } |
| 164 | |
Dave Cheney | 1582bf0 | 2012-10-30 18:13:59 +1100 | [diff] [blame] | 165 | transport.reader.cipherAlgo, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 166 | if !ok { |
| 167 | return |
| 168 | } |
| 169 | |
| 170 | transport.writer.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer) |
| 171 | if !ok { |
| 172 | return |
| 173 | } |
| 174 | |
| 175 | transport.reader.macAlgo, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient) |
| 176 | if !ok { |
| 177 | return |
| 178 | } |
| 179 | |
| 180 | transport.writer.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer) |
| 181 | if !ok { |
| 182 | return |
| 183 | } |
| 184 | |
| 185 | transport.reader.compressionAlgo, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient) |
| 186 | if !ok { |
| 187 | return |
| 188 | } |
| 189 | |
| 190 | ok = true |
| 191 | return |
| 192 | } |
| 193 | |
| 194 | // Cryptographic configuration common to both ServerConfig and ClientConfig. |
| 195 | type CryptoConfig struct { |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 196 | // The allowed key exchanges algorithms. If unspecified then a |
| 197 | // default set of algorithms is used. |
| 198 | KeyExchanges []string |
| 199 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 200 | // The allowed cipher algorithms. If unspecified then DefaultCipherOrder is |
| 201 | // used. |
| 202 | Ciphers []string |
Dave Cheney | 6de97b5 | 2012-02-27 19:40:52 -0500 | [diff] [blame] | 203 | |
| 204 | // The allowed MAC algorithms. If unspecified then DefaultMACOrder is used. |
| 205 | MACs []string |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 206 | } |
| 207 | |
| 208 | func (c *CryptoConfig) ciphers() []string { |
| 209 | if c.Ciphers == nil { |
| 210 | return DefaultCipherOrder |
| 211 | } |
| 212 | return c.Ciphers |
| 213 | } |
| 214 | |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 215 | func (c *CryptoConfig) kexes() []string { |
| 216 | if c.KeyExchanges == nil { |
| 217 | return defaultKeyExchangeOrder |
| 218 | } |
| 219 | return c.KeyExchanges |
| 220 | } |
| 221 | |
Dave Cheney | 6de97b5 | 2012-02-27 19:40:52 -0500 | [diff] [blame] | 222 | func (c *CryptoConfig) macs() []string { |
| 223 | if c.MACs == nil { |
| 224 | return DefaultMACOrder |
| 225 | } |
| 226 | return c.MACs |
| 227 | } |
| 228 | |
Han-Wen Nienhuys | d7d50b0 | 2013-08-28 10:50:25 -0400 | [diff] [blame] | 229 | // ecHash returns the hash to match the given elliptic curve, see RFC |
| 230 | // 5656, section 6.2.1 |
| 231 | func ecHash(curve elliptic.Curve) crypto.Hash { |
| 232 | bitSize := curve.Params().BitSize |
| 233 | switch { |
| 234 | case bitSize <= 256: |
| 235 | return crypto.SHA256 |
| 236 | case bitSize <= 384: |
| 237 | return crypto.SHA384 |
| 238 | } |
| 239 | return crypto.SHA512 |
| 240 | } |
| 241 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 242 | // serialize a signed slice according to RFC 4254 6.6. |
| 243 | func serializeSignature(algoname string, sig []byte) []byte { |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 244 | // The corresponding private key to a public certificate is always a normal |
| 245 | // private key. For signature serialization purposes, ensure we use the |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 246 | // proper key algorithm name in case the public cert algorithm name is passed. |
Jonathan Pittman | 6a743c5 | 2013-09-09 13:06:04 -0400 | [diff] [blame^] | 247 | algoname = pubAlgoToPrivAlgo(algoname) |
| 248 | |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 249 | length := stringLength(len(algoname)) |
| 250 | length += stringLength(len(sig)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 251 | |
| 252 | ret := make([]byte, length) |
| 253 | r := marshalString(ret, []byte(algoname)) |
| 254 | r = marshalString(r, sig) |
| 255 | |
| 256 | return ret |
| 257 | } |
| 258 | |
Jonathan Pittman | 6a743c5 | 2013-09-09 13:06:04 -0400 | [diff] [blame^] | 259 | func verifySignature(hash []byte, sig *signature, key interface{}) error { |
| 260 | switch pubKey := key.(type) { |
| 261 | case *rsa.PublicKey: |
| 262 | return verifyRSASignature(hash, sig, pubKey) |
| 263 | case *dsa.PublicKey: |
| 264 | return verifyDSASignature(hash, sig, pubKey) |
| 265 | case *ecdsa.PublicKey: |
| 266 | return verifyECDSASignature(hash, sig, pubKey) |
| 267 | case *OpenSSHCertV01: |
| 268 | return verifySignature(hash, sig, pubKey.Key) |
| 269 | } |
| 270 | return fmt.Errorf("ssh: unknown key type %T", key) |
| 271 | } |
| 272 | |
| 273 | func verifyRSASignature(hash []byte, sig *signature, key *rsa.PublicKey) error { |
| 274 | return rsa.VerifyPKCS1v15(key, crypto.SHA1, hash, sig.Blob) |
| 275 | } |
| 276 | |
| 277 | func verifyDSASignature(hash []byte, sig *signature, key *dsa.PublicKey) error { |
| 278 | // Per RFC 4253, section 6.6, |
| 279 | // The value for 'dss_signature_blob' is encoded as a string containing |
| 280 | // r, followed by s (which are 160-bit integers, without lengths or |
| 281 | // padding, unsigned, and in network byte order). |
| 282 | // For DSS purposes, sig.Blob should be exactly 40 bytes in length. |
| 283 | if len(sig.Blob) != 40 { |
| 284 | return fmt.Errorf("ssh: improper dss signature length of %d", len(sig.Blob)) |
| 285 | } |
| 286 | r := new(big.Int).SetBytes(sig.Blob[:20]) |
| 287 | s := new(big.Int).SetBytes(sig.Blob[20:]) |
| 288 | if !dsa.Verify(key, hash, r, s) { |
| 289 | return errors.New("ssh: unable to verify dsa signature") |
| 290 | } |
| 291 | return nil |
| 292 | } |
| 293 | |
| 294 | func verifyECDSASignature(hash []byte, sig *signature, key *ecdsa.PublicKey) error { |
| 295 | // Per RFC 5656, section 3.1.2, |
| 296 | // The ecdsa_signature_blob value has the following specific encoding: |
| 297 | // mpint r |
| 298 | // mpint s |
| 299 | r, rest, ok := parseInt(sig.Blob) |
| 300 | if !ok { |
| 301 | return errors.New("ssh: ecdsa signature blob parse failed") |
| 302 | } |
| 303 | s, rest, ok := parseInt(rest) |
| 304 | if !ok || len(rest) > 0 { |
| 305 | return errors.New("ssh: ecdsa signature blob parse failed") |
| 306 | } |
| 307 | if !ecdsa.Verify(key, hash, r, s) { |
| 308 | return errors.New("ssh: unable to verify ecdsa signature") |
| 309 | } |
| 310 | return nil |
| 311 | } |
| 312 | |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 313 | // serialize a *rsa.PublicKey or *dsa.PublicKey according to RFC 4253 6.6. |
Han-Wen Nienhuys | f17d130 | 2013-09-05 11:36:51 -0400 | [diff] [blame] | 314 | func serializePublicKey(key interface{}) []byte { |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 315 | var pubKeyBytes []byte |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 316 | algoname := algoName(key) |
| 317 | switch key := key.(type) { |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 318 | case *rsa.PublicKey: |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 319 | pubKeyBytes = marshalPubRSA(key) |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 320 | case *dsa.PublicKey: |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 321 | pubKeyBytes = marshalPubDSA(key) |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 322 | case *ecdsa.PublicKey: |
| 323 | pubKeyBytes = marshalPubECDSA(key) |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 324 | case *OpenSSHCertV01: |
| 325 | pubKeyBytes = marshalOpenSSHCertV01(key) |
| 326 | default: |
| 327 | panic("unexpected key type") |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 328 | } |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 329 | |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 330 | length := stringLength(len(algoname)) |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 331 | length += len(pubKeyBytes) |
| 332 | ret := make([]byte, length) |
| 333 | r := marshalString(ret, []byte(algoname)) |
| 334 | copy(r, pubKeyBytes) |
| 335 | return ret |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 336 | } |
| 337 | |
| 338 | func algoName(key interface{}) string { |
| 339 | switch key.(type) { |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 340 | case *rsa.PublicKey: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame] | 341 | return KeyAlgoRSA |
Adam Langley | d33bbf2 | 2012-02-23 10:42:21 -0500 | [diff] [blame] | 342 | case *dsa.PublicKey: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame] | 343 | return KeyAlgoDSA |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 344 | case *ecdsa.PublicKey: |
| 345 | switch key.(*ecdsa.PublicKey).Params().BitSize { |
| 346 | case 256: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame] | 347 | return KeyAlgoECDSA256 |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 348 | case 384: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame] | 349 | return KeyAlgoECDSA384 |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 350 | case 521: |
Jonathan Pittman | 54c65ae | 2012-12-14 10:11:06 -0500 | [diff] [blame] | 351 | return KeyAlgoECDSA521 |
Jonathan Pittman | 94c9f92 | 2012-12-14 05:52:19 +1100 | [diff] [blame] | 352 | } |
Jonathan Pittman | 9b05c27 | 2012-02-24 12:52:06 -0500 | [diff] [blame] | 353 | case *OpenSSHCertV01: |
Jonathan Pittman | d95b283 | 2012-12-16 10:47:10 -0500 | [diff] [blame] | 354 | switch key.(*OpenSSHCertV01).Key.(type) { |
| 355 | case *rsa.PublicKey: |
| 356 | return CertAlgoRSAv01 |
| 357 | case *dsa.PublicKey: |
| 358 | return CertAlgoDSAv01 |
| 359 | case *ecdsa.PublicKey: |
| 360 | switch key.(*OpenSSHCertV01).Key.(*ecdsa.PublicKey).Params().BitSize { |
| 361 | case 256: |
| 362 | return CertAlgoECDSA256v01 |
| 363 | case 384: |
| 364 | return CertAlgoECDSA384v01 |
| 365 | case 521: |
| 366 | return CertAlgoECDSA521v01 |
| 367 | } |
| 368 | } |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 369 | } |
Han-Wen Nienhuys | f17d130 | 2013-09-05 11:36:51 -0400 | [diff] [blame] | 370 | panic(fmt.Sprintf("unexpected key type %T", key)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 371 | } |
| 372 | |
Jonathan Pittman | 6a743c5 | 2013-09-09 13:06:04 -0400 | [diff] [blame^] | 373 | // pubAlgoToPrivAlgo returns the private key algorithm format name that |
| 374 | // corresponds to a given public key algorithm format name. For most |
| 375 | // public keys, the private key algorithm name is the same. For some |
| 376 | // situations, such as openssh certificates, the private key algorithm and |
| 377 | // public key algorithm names differ. This accounts for those situations. |
| 378 | func pubAlgoToPrivAlgo(pubAlgo string) string { |
| 379 | switch pubAlgo { |
| 380 | case CertAlgoRSAv01: |
| 381 | return KeyAlgoRSA |
| 382 | case CertAlgoDSAv01: |
| 383 | return KeyAlgoDSA |
| 384 | case CertAlgoECDSA256v01: |
| 385 | return KeyAlgoECDSA256 |
| 386 | case CertAlgoECDSA384v01: |
| 387 | return KeyAlgoECDSA384 |
| 388 | case CertAlgoECDSA521v01: |
| 389 | return KeyAlgoECDSA521 |
| 390 | } |
| 391 | return pubAlgo |
| 392 | } |
| 393 | |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 394 | // buildDataSignedForAuth returns the data that is signed in order to prove |
| 395 | // posession of a private key. See RFC 4252, section 7. |
| 396 | func buildDataSignedForAuth(sessionId []byte, req userAuthRequestMsg, algo, pubKey []byte) []byte { |
| 397 | user := []byte(req.User) |
| 398 | service := []byte(req.Service) |
| 399 | method := []byte(req.Method) |
| 400 | |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 401 | length := stringLength(len(sessionId)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 402 | length += 1 |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 403 | length += stringLength(len(user)) |
| 404 | length += stringLength(len(service)) |
| 405 | length += stringLength(len(method)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 406 | length += 1 |
Adam Langley | 63f855d | 2012-04-20 15:17:42 -0400 | [diff] [blame] | 407 | length += stringLength(len(algo)) |
| 408 | length += stringLength(len(pubKey)) |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 409 | |
| 410 | ret := make([]byte, length) |
| 411 | r := marshalString(ret, sessionId) |
| 412 | r[0] = msgUserAuthRequest |
| 413 | r = r[1:] |
| 414 | r = marshalString(r, user) |
| 415 | r = marshalString(r, service) |
| 416 | r = marshalString(r, method) |
| 417 | r[0] = 1 |
| 418 | r = r[1:] |
| 419 | r = marshalString(r, algo) |
| 420 | r = marshalString(r, pubKey) |
| 421 | return ret |
| 422 | } |
| 423 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 424 | // safeString sanitises s according to RFC 4251, section 9.2. |
Russ Cox | 470549d | 2012-01-25 15:31:12 -0500 | [diff] [blame] | 425 | // All control characters except tab, carriage return and newline are |
| 426 | // replaced by 0x20. |
| 427 | func safeString(s string) string { |
| 428 | out := []byte(s) |
| 429 | for i, c := range out { |
| 430 | if c < 0x20 && c != 0xd && c != 0xa && c != 0x9 { |
| 431 | out[i] = 0x20 |
| 432 | } |
| 433 | } |
| 434 | return string(out) |
| 435 | } |
Dave Cheney | 79d53bd | 2012-03-04 14:34:24 -0800 | [diff] [blame] | 436 | |
| 437 | func appendU16(buf []byte, n uint16) []byte { |
| 438 | return append(buf, byte(n>>8), byte(n)) |
| 439 | } |
| 440 | |
| 441 | func appendU32(buf []byte, n uint32) []byte { |
| 442 | return append(buf, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) |
| 443 | } |
| 444 | |
| 445 | func appendInt(buf []byte, n int) []byte { |
| 446 | return appendU32(buf, uint32(n)) |
| 447 | } |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 448 | |
Han-Wen Nienhuys | c7df565 | 2013-06-06 10:44:12 -0400 | [diff] [blame] | 449 | func appendString(buf []byte, s string) []byte { |
| 450 | buf = appendU32(buf, uint32(len(s))) |
| 451 | buf = append(buf, s...) |
| 452 | return buf |
| 453 | } |
| 454 | |
| 455 | func appendBool(buf []byte, b bool) []byte { |
| 456 | if b { |
| 457 | buf = append(buf, 1) |
| 458 | } else { |
| 459 | buf = append(buf, 0) |
| 460 | } |
| 461 | return buf |
| 462 | } |
| 463 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 464 | // newCond is a helper to hide the fact that there is no usable zero |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 465 | // value for sync.Cond. |
| 466 | func newCond() *sync.Cond { return sync.NewCond(new(sync.Mutex)) } |
| 467 | |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 468 | // window represents the buffer available to clients |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 469 | // wishing to write to a channel. |
| 470 | type window struct { |
| 471 | *sync.Cond |
| 472 | win uint32 // RFC 4254 5.2 says the window size can grow to 2^32-1 |
| 473 | } |
| 474 | |
| 475 | // add adds win to the amount of window available |
| 476 | // for consumers. |
| 477 | func (w *window) add(win uint32) bool { |
Dave Cheney | 55aa081 | 2012-05-22 12:04:51 +1000 | [diff] [blame] | 478 | // a zero sized window adjust is a noop. |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 479 | if win == 0 { |
Dave Cheney | 55aa081 | 2012-05-22 12:04:51 +1000 | [diff] [blame] | 480 | return true |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 481 | } |
| 482 | w.L.Lock() |
| 483 | if w.win+win < win { |
| 484 | w.L.Unlock() |
| 485 | return false |
| 486 | } |
| 487 | w.win += win |
| 488 | // It is unusual that multiple goroutines would be attempting to reserve |
Adam Langley | 4002be2 | 2012-12-10 18:12:36 -0500 | [diff] [blame] | 489 | // window space, but not guaranteed. Use broadcast to notify all waiters |
Dave Cheney | 8a2e7c9 | 2012-05-11 05:56:44 +1000 | [diff] [blame] | 490 | // that additional window is available. |
| 491 | w.Broadcast() |
| 492 | w.L.Unlock() |
| 493 | return true |
| 494 | } |
| 495 | |
| 496 | // reserve reserves win from the available window capacity. |
| 497 | // If no capacity remains, reserve will block. reserve may |
| 498 | // return less than requested. |
| 499 | func (w *window) reserve(win uint32) uint32 { |
| 500 | w.L.Lock() |
| 501 | for w.win == 0 { |
| 502 | w.Wait() |
| 503 | } |
| 504 | if w.win < win { |
| 505 | win = w.win |
| 506 | } |
| 507 | w.win -= win |
| 508 | w.L.Unlock() |
| 509 | return win |
| 510 | } |