bcrypt_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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. package bcrypt
  5. import (
  6. "bytes"
  7. "fmt"
  8. "testing"
  9. )
  10. func TestBcryptingIsEasy(t *testing.T) {
  11. pass := []byte("mypassword")
  12. hp, err := GenerateFromPassword(pass, 0)
  13. if err != nil {
  14. t.Fatalf("GenerateFromPassword error: %s", err)
  15. }
  16. if CompareHashAndPassword(hp, pass) != nil {
  17. t.Errorf("%v should hash %s correctly", hp, pass)
  18. }
  19. notPass := "notthepass"
  20. err = CompareHashAndPassword(hp, []byte(notPass))
  21. if err != ErrMismatchedHashAndPassword {
  22. t.Errorf("%v and %s should be mismatched", hp, notPass)
  23. }
  24. }
  25. func TestBcryptingIsCorrect(t *testing.T) {
  26. pass := []byte("allmine")
  27. salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
  28. expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
  29. hash, err := bcrypt(pass, 10, salt)
  30. if err != nil {
  31. t.Fatalf("bcrypt blew up: %v", err)
  32. }
  33. if !bytes.HasSuffix(expectedHash, hash) {
  34. t.Errorf("%v should be the suffix of %v", hash, expectedHash)
  35. }
  36. h, err := newFromHash(expectedHash)
  37. if err != nil {
  38. t.Errorf("Unable to parse %s: %v", string(expectedHash), err)
  39. }
  40. // This is not the safe way to compare these hashes. We do this only for
  41. // testing clarity. Use bcrypt.CompareHashAndPassword()
  42. if err == nil && !bytes.Equal(expectedHash, h.Hash()) {
  43. t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash)
  44. }
  45. }
  46. func TestTooLongPasswordsWork(t *testing.T) {
  47. salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
  48. // One byte over the usual 56 byte limit that blowfish has
  49. tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
  50. tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
  51. hash, err := bcrypt(tooLongPass, 10, salt)
  52. if err != nil {
  53. t.Fatalf("bcrypt blew up on long password: %v", err)
  54. }
  55. if !bytes.HasSuffix(tooLongExpected, hash) {
  56. t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
  57. }
  58. }
  59. type InvalidHashTest struct {
  60. err error
  61. hash []byte
  62. }
  63. var invalidTests = []InvalidHashTest{
  64. {ErrHashTooShort, []byte("$2a$10$fooo")},
  65. {ErrHashTooShort, []byte("$2a")},
  66. {HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
  67. {InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
  68. {InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
  69. }
  70. func TestInvalidHashErrors(t *testing.T) {
  71. check := func(name string, expected, err error) {
  72. if err == nil {
  73. t.Errorf("%s: Should have returned an error", name)
  74. }
  75. if err != nil && err != expected {
  76. t.Errorf("%s gave err %v but should have given %v", name, err, expected)
  77. }
  78. }
  79. for _, iht := range invalidTests {
  80. _, err := newFromHash(iht.hash)
  81. check("newFromHash", iht.err, err)
  82. err = CompareHashAndPassword(iht.hash, []byte("anything"))
  83. check("CompareHashAndPassword", iht.err, err)
  84. }
  85. }
  86. func TestUnpaddedBase64Encoding(t *testing.T) {
  87. original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
  88. encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
  89. encoded := base64Encode(original)
  90. if !bytes.Equal(encodedOriginal, encoded) {
  91. t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
  92. }
  93. decoded, err := base64Decode(encodedOriginal)
  94. if err != nil {
  95. t.Fatalf("base64Decode blew up: %s", err)
  96. }
  97. if !bytes.Equal(decoded, original) {
  98. t.Errorf("Decoded %v should have equaled %v", decoded, original)
  99. }
  100. }
  101. func TestCost(t *testing.T) {
  102. suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C"
  103. for _, vers := range []string{"2a", "2"} {
  104. for _, cost := range []int{4, 10} {
  105. s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix)
  106. h := []byte(s)
  107. actual, err := Cost(h)
  108. if err != nil {
  109. t.Errorf("Cost, error: %s", err)
  110. continue
  111. }
  112. if actual != cost {
  113. t.Errorf("Cost, expected: %d, actual: %d", cost, actual)
  114. }
  115. }
  116. }
  117. _, err := Cost([]byte("$a$a$" + suffix))
  118. if err == nil {
  119. t.Errorf("Cost, malformed but no error returned")
  120. }
  121. }
  122. func TestCostValidationInHash(t *testing.T) {
  123. if testing.Short() {
  124. return
  125. }
  126. pass := []byte("mypassword")
  127. for c := 0; c < MinCost; c++ {
  128. p, _ := newFromPassword(pass, c)
  129. if p.cost != DefaultCost {
  130. t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
  131. }
  132. }
  133. p, _ := newFromPassword(pass, 14)
  134. if p.cost != 14 {
  135. t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
  136. }
  137. hp, _ := newFromHash(p.Hash())
  138. if p.cost != hp.cost {
  139. t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
  140. }
  141. _, err := newFromPassword(pass, 32)
  142. if err == nil {
  143. t.Fatalf("newFromPassword: should return a cost error")
  144. }
  145. if err != InvalidCostError(32) {
  146. t.Errorf("newFromPassword: should return cost error, got %#v", err)
  147. }
  148. }
  149. func TestCostReturnsWithLeadingZeroes(t *testing.T) {
  150. hp, _ := newFromPassword([]byte("abcdefgh"), 7)
  151. cost := hp.Hash()[4:7]
  152. expected := []byte("07$")
  153. if !bytes.Equal(expected, cost) {
  154. t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
  155. }
  156. }
  157. func TestMinorNotRequired(t *testing.T) {
  158. noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
  159. h, err := newFromHash(noMinorHash)
  160. if err != nil {
  161. t.Fatalf("No minor hash blew up: %s", err)
  162. }
  163. if h.minor != 0 {
  164. t.Errorf("Should leave minor version at 0, but was %d", h.minor)
  165. }
  166. if !bytes.Equal(noMinorHash, h.Hash()) {
  167. t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
  168. }
  169. }
  170. func BenchmarkEqual(b *testing.B) {
  171. b.StopTimer()
  172. passwd := []byte("somepasswordyoulike")
  173. hash, _ := GenerateFromPassword(passwd, 10)
  174. b.StartTimer()
  175. for i := 0; i < b.N; i++ {
  176. CompareHashAndPassword(hash, passwd)
  177. }
  178. }
  179. func BenchmarkGeneration(b *testing.B) {
  180. b.StopTimer()
  181. passwd := []byte("mylongpassword1234")
  182. b.StartTimer()
  183. for i := 0; i < b.N; i++ {
  184. GenerateFromPassword(passwd, 10)
  185. }
  186. }