You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

475 lines
12 KiB

  1. // Copyright 2014 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. package ssh
  5. import (
  6. "bytes"
  7. "crypto/dsa"
  8. "crypto/ecdsa"
  9. "crypto/elliptic"
  10. "crypto/rand"
  11. "crypto/rsa"
  12. "encoding/base64"
  13. "fmt"
  14. "reflect"
  15. "strings"
  16. "testing"
  17. "golang.org/x/crypto/ed25519"
  18. "golang.org/x/crypto/ssh/testdata"
  19. )
  20. func rawKey(pub PublicKey) interface{} {
  21. switch k := pub.(type) {
  22. case *rsaPublicKey:
  23. return (*rsa.PublicKey)(k)
  24. case *dsaPublicKey:
  25. return (*dsa.PublicKey)(k)
  26. case *ecdsaPublicKey:
  27. return (*ecdsa.PublicKey)(k)
  28. case ed25519PublicKey:
  29. return (ed25519.PublicKey)(k)
  30. case *Certificate:
  31. return k
  32. }
  33. panic("unknown key type")
  34. }
  35. func TestKeyMarshalParse(t *testing.T) {
  36. for _, priv := range testSigners {
  37. pub := priv.PublicKey()
  38. roundtrip, err := ParsePublicKey(pub.Marshal())
  39. if err != nil {
  40. t.Errorf("ParsePublicKey(%T): %v", pub, err)
  41. }
  42. k1 := rawKey(pub)
  43. k2 := rawKey(roundtrip)
  44. if !reflect.DeepEqual(k1, k2) {
  45. t.Errorf("got %#v in roundtrip, want %#v", k2, k1)
  46. }
  47. }
  48. }
  49. func TestUnsupportedCurves(t *testing.T) {
  50. raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
  51. if err != nil {
  52. t.Fatalf("GenerateKey: %v", err)
  53. }
  54. if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P-256") {
  55. t.Fatalf("NewPrivateKey should not succeed with P-224, got: %v", err)
  56. }
  57. if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P-256") {
  58. t.Fatalf("NewPublicKey should not succeed with P-224, got: %v", err)
  59. }
  60. }
  61. func TestNewPublicKey(t *testing.T) {
  62. for _, k := range testSigners {
  63. raw := rawKey(k.PublicKey())
  64. // Skip certificates, as NewPublicKey does not support them.
  65. if _, ok := raw.(*Certificate); ok {
  66. continue
  67. }
  68. pub, err := NewPublicKey(raw)
  69. if err != nil {
  70. t.Errorf("NewPublicKey(%#v): %v", raw, err)
  71. }
  72. if !reflect.DeepEqual(k.PublicKey(), pub) {
  73. t.Errorf("NewPublicKey(%#v) = %#v, want %#v", raw, pub, k.PublicKey())
  74. }
  75. }
  76. }
  77. func TestKeySignVerify(t *testing.T) {
  78. for _, priv := range testSigners {
  79. pub := priv.PublicKey()
  80. data := []byte("sign me")
  81. sig, err := priv.Sign(rand.Reader, data)
  82. if err != nil {
  83. t.Fatalf("Sign(%T): %v", priv, err)
  84. }
  85. if err := pub.Verify(data, sig); err != nil {
  86. t.Errorf("publicKey.Verify(%T): %v", priv, err)
  87. }
  88. sig.Blob[5]++
  89. if err := pub.Verify(data, sig); err == nil {
  90. t.Errorf("publicKey.Verify on broken sig did not fail")
  91. }
  92. }
  93. }
  94. func TestParseRSAPrivateKey(t *testing.T) {
  95. key := testPrivateKeys["rsa"]
  96. rsa, ok := key.(*rsa.PrivateKey)
  97. if !ok {
  98. t.Fatalf("got %T, want *rsa.PrivateKey", rsa)
  99. }
  100. if err := rsa.Validate(); err != nil {
  101. t.Errorf("Validate: %v", err)
  102. }
  103. }
  104. func TestParseECPrivateKey(t *testing.T) {
  105. key := testPrivateKeys["ecdsa"]
  106. ecKey, ok := key.(*ecdsa.PrivateKey)
  107. if !ok {
  108. t.Fatalf("got %T, want *ecdsa.PrivateKey", ecKey)
  109. }
  110. if !validateECPublicKey(ecKey.Curve, ecKey.X, ecKey.Y) {
  111. t.Fatalf("public key does not validate.")
  112. }
  113. }
  114. // See Issue https://github.com/golang/go/issues/6650.
  115. func TestParseEncryptedPrivateKeysFails(t *testing.T) {
  116. const wantSubstring = "encrypted"
  117. for i, tt := range testdata.PEMEncryptedKeys {
  118. _, err := ParsePrivateKey(tt.PEMBytes)
  119. if err == nil {
  120. t.Errorf("#%d key %s: ParsePrivateKey successfully parsed, expected an error", i, tt.Name)
  121. continue
  122. }
  123. if !strings.Contains(err.Error(), wantSubstring) {
  124. t.Errorf("#%d key %s: got error %q, want substring %q", i, tt.Name, err, wantSubstring)
  125. }
  126. }
  127. }
  128. func TestParseDSA(t *testing.T) {
  129. // We actually exercise the ParsePrivateKey codepath here, as opposed to
  130. // using the ParseRawPrivateKey+NewSignerFromKey path that testdata_test.go
  131. // uses.
  132. s, err := ParsePrivateKey(testdata.PEMBytes["dsa"])
  133. if err != nil {
  134. t.Fatalf("ParsePrivateKey returned error: %s", err)
  135. }
  136. data := []byte("sign me")
  137. sig, err := s.Sign(rand.Reader, data)
  138. if err != nil {
  139. t.Fatalf("dsa.Sign: %v", err)
  140. }
  141. if err := s.PublicKey().Verify(data, sig); err != nil {
  142. t.Errorf("Verify failed: %v", err)
  143. }
  144. }
  145. // Tests for authorized_keys parsing.
  146. // getTestKey returns a public key, and its base64 encoding.
  147. func getTestKey() (PublicKey, string) {
  148. k := testPublicKeys["rsa"]
  149. b := &bytes.Buffer{}
  150. e := base64.NewEncoder(base64.StdEncoding, b)
  151. e.Write(k.Marshal())
  152. e.Close()
  153. return k, b.String()
  154. }
  155. func TestMarshalParsePublicKey(t *testing.T) {
  156. pub, pubSerialized := getTestKey()
  157. line := fmt.Sprintf("%s %s user@host", pub.Type(), pubSerialized)
  158. authKeys := MarshalAuthorizedKey(pub)
  159. actualFields := strings.Fields(string(authKeys))
  160. if len(actualFields) == 0 {
  161. t.Fatalf("failed authKeys: %v", authKeys)
  162. }
  163. // drop the comment
  164. expectedFields := strings.Fields(line)[0:2]
  165. if !reflect.DeepEqual(actualFields, expectedFields) {
  166. t.Errorf("got %v, expected %v", actualFields, expectedFields)
  167. }
  168. actPub, _, _, _, err := ParseAuthorizedKey([]byte(line))
  169. if err != nil {
  170. t.Fatalf("cannot parse %v: %v", line, err)
  171. }
  172. if !reflect.DeepEqual(actPub, pub) {
  173. t.Errorf("got %v, expected %v", actPub, pub)
  174. }
  175. }
  176. type authResult struct {
  177. pubKey PublicKey
  178. options []string
  179. comments string
  180. rest string
  181. ok bool
  182. }
  183. func testAuthorizedKeys(t *testing.T, authKeys []byte, expected []authResult) {
  184. rest := authKeys
  185. var values []authResult
  186. for len(rest) > 0 {
  187. var r authResult
  188. var err error
  189. r.pubKey, r.comments, r.options, rest, err = ParseAuthorizedKey(rest)
  190. r.ok = (err == nil)
  191. t.Log(err)
  192. r.rest = string(rest)
  193. values = append(values, r)
  194. }
  195. if !reflect.DeepEqual(values, expected) {
  196. t.Errorf("got %#v, expected %#v", values, expected)
  197. }
  198. }
  199. func TestAuthorizedKeyBasic(t *testing.T) {
  200. pub, pubSerialized := getTestKey()
  201. line := "ssh-rsa " + pubSerialized + " user@host"
  202. testAuthorizedKeys(t, []byte(line),
  203. []authResult{
  204. {pub, nil, "user@host", "", true},
  205. })
  206. }
  207. func TestAuth(t *testing.T) {
  208. pub, pubSerialized := getTestKey()
  209. authWithOptions := []string{
  210. `# comments to ignore before any keys...`,
  211. ``,
  212. `env="HOME=/home/root",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`,
  213. `# comments to ignore, along with a blank line`,
  214. ``,
  215. `env="HOME=/home/root2" ssh-rsa ` + pubSerialized + ` user2@host2`,
  216. ``,
  217. `# more comments, plus a invalid entry`,
  218. `ssh-rsa data-that-will-not-parse user@host3`,
  219. }
  220. for _, eol := range []string{"\n", "\r\n"} {
  221. authOptions := strings.Join(authWithOptions, eol)
  222. rest2 := strings.Join(authWithOptions[3:], eol)
  223. rest3 := strings.Join(authWithOptions[6:], eol)
  224. testAuthorizedKeys(t, []byte(authOptions), []authResult{
  225. {pub, []string{`env="HOME=/home/root"`, "no-port-forwarding"}, "user@host", rest2, true},
  226. {pub, []string{`env="HOME=/home/root2"`}, "user2@host2", rest3, true},
  227. {nil, nil, "", "", false},
  228. })
  229. }
  230. }
  231. func TestAuthWithQuotedSpaceInEnv(t *testing.T) {
  232. pub, pubSerialized := getTestKey()
  233. authWithQuotedSpaceInEnv := []byte(`env="HOME=/home/root dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
  234. testAuthorizedKeys(t, []byte(authWithQuotedSpaceInEnv), []authResult{
  235. {pub, []string{`env="HOME=/home/root dir"`, "no-port-forwarding"}, "user@host", "", true},
  236. })
  237. }
  238. func TestAuthWithQuotedCommaInEnv(t *testing.T) {
  239. pub, pubSerialized := getTestKey()
  240. authWithQuotedCommaInEnv := []byte(`env="HOME=/home/root,dir",no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host`)
  241. testAuthorizedKeys(t, []byte(authWithQuotedCommaInEnv), []authResult{
  242. {pub, []string{`env="HOME=/home/root,dir"`, "no-port-forwarding"}, "user@host", "", true},
  243. })
  244. }
  245. func TestAuthWithQuotedQuoteInEnv(t *testing.T) {
  246. pub, pubSerialized := getTestKey()
  247. authWithQuotedQuoteInEnv := []byte(`env="HOME=/home/\"root dir",no-port-forwarding` + "\t" + `ssh-rsa` + "\t" + pubSerialized + ` user@host`)
  248. authWithDoubleQuotedQuote := []byte(`no-port-forwarding,env="HOME=/home/ \"root dir\"" ssh-rsa ` + pubSerialized + "\t" + `user@host`)
  249. testAuthorizedKeys(t, []byte(authWithQuotedQuoteInEnv), []authResult{
  250. {pub, []string{`env="HOME=/home/\"root dir"`, "no-port-forwarding"}, "user@host", "", true},
  251. })
  252. testAuthorizedKeys(t, []byte(authWithDoubleQuotedQuote), []authResult{
  253. {pub, []string{"no-port-forwarding", `env="HOME=/home/ \"root dir\""`}, "user@host", "", true},
  254. })
  255. }
  256. func TestAuthWithInvalidSpace(t *testing.T) {
  257. _, pubSerialized := getTestKey()
  258. authWithInvalidSpace := []byte(`env="HOME=/home/root dir", no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
  259. #more to follow but still no valid keys`)
  260. testAuthorizedKeys(t, []byte(authWithInvalidSpace), []authResult{
  261. {nil, nil, "", "", false},
  262. })
  263. }
  264. func TestAuthWithMissingQuote(t *testing.T) {
  265. pub, pubSerialized := getTestKey()
  266. authWithMissingQuote := []byte(`env="HOME=/home/root,no-port-forwarding ssh-rsa ` + pubSerialized + ` user@host
  267. env="HOME=/home/root",shared-control ssh-rsa ` + pubSerialized + ` user@host`)
  268. testAuthorizedKeys(t, []byte(authWithMissingQuote), []authResult{
  269. {pub, []string{`env="HOME=/home/root"`, `shared-control`}, "user@host", "", true},
  270. })
  271. }
  272. func TestInvalidEntry(t *testing.T) {
  273. authInvalid := []byte(`ssh-rsa`)
  274. _, _, _, _, err := ParseAuthorizedKey(authInvalid)
  275. if err == nil {
  276. t.Errorf("got valid entry for %q", authInvalid)
  277. }
  278. }
  279. var knownHostsParseTests = []struct {
  280. input string
  281. err string
  282. marker string
  283. comment string
  284. hosts []string
  285. rest string
  286. }{
  287. {
  288. "",
  289. "EOF",
  290. "", "", nil, "",
  291. },
  292. {
  293. "# Just a comment",
  294. "EOF",
  295. "", "", nil, "",
  296. },
  297. {
  298. " \t ",
  299. "EOF",
  300. "", "", nil, "",
  301. },
  302. {
  303. "localhost ssh-rsa {RSAPUB}",
  304. "",
  305. "", "", []string{"localhost"}, "",
  306. },
  307. {
  308. "localhost\tssh-rsa {RSAPUB}",
  309. "",
  310. "", "", []string{"localhost"}, "",
  311. },
  312. {
  313. "localhost\tssh-rsa {RSAPUB}\tcomment comment",
  314. "",
  315. "", "comment comment", []string{"localhost"}, "",
  316. },
  317. {
  318. "localhost\tssh-rsa {RSAPUB}\tcomment comment\n",
  319. "",
  320. "", "comment comment", []string{"localhost"}, "",
  321. },
  322. {
  323. "localhost\tssh-rsa {RSAPUB}\tcomment comment\r\n",
  324. "",
  325. "", "comment comment", []string{"localhost"}, "",
  326. },
  327. {
  328. "localhost\tssh-rsa {RSAPUB}\tcomment comment\r\nnext line",
  329. "",
  330. "", "comment comment", []string{"localhost"}, "next line",
  331. },
  332. {
  333. "localhost,[host2:123]\tssh-rsa {RSAPUB}\tcomment comment",
  334. "",
  335. "", "comment comment", []string{"localhost", "[host2:123]"}, "",
  336. },
  337. {
  338. "@marker \tlocalhost,[host2:123]\tssh-rsa {RSAPUB}",
  339. "",
  340. "marker", "", []string{"localhost", "[host2:123]"}, "",
  341. },
  342. {
  343. "@marker \tlocalhost,[host2:123]\tssh-rsa aabbccdd",
  344. "short read",
  345. "", "", nil, "",
  346. },
  347. }
  348. func TestKnownHostsParsing(t *testing.T) {
  349. rsaPub, rsaPubSerialized := getTestKey()
  350. for i, test := range knownHostsParseTests {
  351. var expectedKey PublicKey
  352. const rsaKeyToken = "{RSAPUB}"
  353. input := test.input
  354. if strings.Contains(input, rsaKeyToken) {
  355. expectedKey = rsaPub
  356. input = strings.Replace(test.input, rsaKeyToken, rsaPubSerialized, -1)
  357. }
  358. marker, hosts, pubKey, comment, rest, err := ParseKnownHosts([]byte(input))
  359. if err != nil {
  360. if len(test.err) == 0 {
  361. t.Errorf("#%d: unexpectedly failed with %q", i, err)
  362. } else if !strings.Contains(err.Error(), test.err) {
  363. t.Errorf("#%d: expected error containing %q, but got %q", i, test.err, err)
  364. }
  365. continue
  366. } else if len(test.err) != 0 {
  367. t.Errorf("#%d: succeeded but expected error including %q", i, test.err)
  368. continue
  369. }
  370. if !reflect.DeepEqual(expectedKey, pubKey) {
  371. t.Errorf("#%d: expected key %#v, but got %#v", i, expectedKey, pubKey)
  372. }
  373. if marker != test.marker {
  374. t.Errorf("#%d: expected marker %q, but got %q", i, test.marker, marker)
  375. }
  376. if comment != test.comment {
  377. t.Errorf("#%d: expected comment %q, but got %q", i, test.comment, comment)
  378. }
  379. if !reflect.DeepEqual(test.hosts, hosts) {
  380. t.Errorf("#%d: expected hosts %#v, but got %#v", i, test.hosts, hosts)
  381. }
  382. if rest := string(rest); rest != test.rest {
  383. t.Errorf("#%d: expected remaining input to be %q, but got %q", i, test.rest, rest)
  384. }
  385. }
  386. }
  387. func TestFingerprintLegacyMD5(t *testing.T) {
  388. pub, _ := getTestKey()
  389. fingerprint := FingerprintLegacyMD5(pub)
  390. want := "fb:61:6d:1a:e3:f0:95:45:3c:a0:79:be:4a:93:63:66" // ssh-keygen -lf -E md5 rsa
  391. if fingerprint != want {
  392. t.Errorf("got fingerprint %q want %q", fingerprint, want)
  393. }
  394. }
  395. func TestFingerprintSHA256(t *testing.T) {
  396. pub, _ := getTestKey()
  397. fingerprint := FingerprintSHA256(pub)
  398. want := "SHA256:Anr3LjZK8YVpjrxu79myrW9Hrb/wpcMNpVvTq/RcBm8" // ssh-keygen -lf rsa
  399. if fingerprint != want {
  400. t.Errorf("got fingerprint %q want %q", fingerprint, want)
  401. }
  402. }