1
0

bcrypt_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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 TestVeryShortPasswords(t *testing.T) {
  47. key := []byte("k")
  48. salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
  49. _, err := bcrypt(key, 10, salt)
  50. if err != nil {
  51. t.Errorf("One byte key resulted in error: %s", err)
  52. }
  53. }
  54. func TestTooLongPasswordsWork(t *testing.T) {
  55. salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
  56. // One byte over the usual 56 byte limit that blowfish has
  57. tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
  58. tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
  59. hash, err := bcrypt(tooLongPass, 10, salt)
  60. if err != nil {
  61. t.Fatalf("bcrypt blew up on long password: %v", err)
  62. }
  63. if !bytes.HasSuffix(tooLongExpected, hash) {
  64. t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
  65. }
  66. }
  67. type InvalidHashTest struct {
  68. err error
  69. hash []byte
  70. }
  71. var invalidTests = []InvalidHashTest{
  72. {ErrHashTooShort, []byte("$2a$10$fooo")},
  73. {ErrHashTooShort, []byte("$2a")},
  74. {HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
  75. {InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
  76. {InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
  77. }
  78. func TestInvalidHashErrors(t *testing.T) {
  79. check := func(name string, expected, err error) {
  80. if err == nil {
  81. t.Errorf("%s: Should have returned an error", name)
  82. }
  83. if err != nil && err != expected {
  84. t.Errorf("%s gave err %v but should have given %v", name, err, expected)
  85. }
  86. }
  87. for _, iht := range invalidTests {
  88. _, err := newFromHash(iht.hash)
  89. check("newFromHash", iht.err, err)
  90. err = CompareHashAndPassword(iht.hash, []byte("anything"))
  91. check("CompareHashAndPassword", iht.err, err)
  92. }
  93. }
  94. func TestUnpaddedBase64Encoding(t *testing.T) {
  95. original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
  96. encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
  97. encoded := base64Encode(original)
  98. if !bytes.Equal(encodedOriginal, encoded) {
  99. t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
  100. }
  101. decoded, err := base64Decode(encodedOriginal)
  102. if err != nil {
  103. t.Fatalf("base64Decode blew up: %s", err)
  104. }
  105. if !bytes.Equal(decoded, original) {
  106. t.Errorf("Decoded %v should have equaled %v", decoded, original)
  107. }
  108. }
  109. func TestCost(t *testing.T) {
  110. suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C"
  111. for _, vers := range []string{"2a", "2"} {
  112. for _, cost := range []int{4, 10} {
  113. s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix)
  114. h := []byte(s)
  115. actual, err := Cost(h)
  116. if err != nil {
  117. t.Errorf("Cost, error: %s", err)
  118. continue
  119. }
  120. if actual != cost {
  121. t.Errorf("Cost, expected: %d, actual: %d", cost, actual)
  122. }
  123. }
  124. }
  125. _, err := Cost([]byte("$a$a$" + suffix))
  126. if err == nil {
  127. t.Errorf("Cost, malformed but no error returned")
  128. }
  129. }
  130. func TestCostValidationInHash(t *testing.T) {
  131. if testing.Short() {
  132. return
  133. }
  134. pass := []byte("mypassword")
  135. for c := 0; c < MinCost; c++ {
  136. p, _ := newFromPassword(pass, c)
  137. if p.cost != DefaultCost {
  138. t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
  139. }
  140. }
  141. p, _ := newFromPassword(pass, 14)
  142. if p.cost != 14 {
  143. t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
  144. }
  145. hp, _ := newFromHash(p.Hash())
  146. if p.cost != hp.cost {
  147. t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
  148. }
  149. _, err := newFromPassword(pass, 32)
  150. if err == nil {
  151. t.Fatalf("newFromPassword: should return a cost error")
  152. }
  153. if err != InvalidCostError(32) {
  154. t.Errorf("newFromPassword: should return cost error, got %#v", err)
  155. }
  156. }
  157. func TestCostReturnsWithLeadingZeroes(t *testing.T) {
  158. hp, _ := newFromPassword([]byte("abcdefgh"), 7)
  159. cost := hp.Hash()[4:7]
  160. expected := []byte("07$")
  161. if !bytes.Equal(expected, cost) {
  162. t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
  163. }
  164. }
  165. func TestMinorNotRequired(t *testing.T) {
  166. noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
  167. h, err := newFromHash(noMinorHash)
  168. if err != nil {
  169. t.Fatalf("No minor hash blew up: %s", err)
  170. }
  171. if h.minor != 0 {
  172. t.Errorf("Should leave minor version at 0, but was %d", h.minor)
  173. }
  174. if !bytes.Equal(noMinorHash, h.Hash()) {
  175. t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
  176. }
  177. }
  178. func BenchmarkEqual(b *testing.B) {
  179. b.StopTimer()
  180. passwd := []byte("somepasswordyoulike")
  181. hash, _ := GenerateFromPassword(passwd, 10)
  182. b.StartTimer()
  183. for i := 0; i < b.N; i++ {
  184. CompareHashAndPassword(hash, passwd)
  185. }
  186. }
  187. func BenchmarkGeneration(b *testing.B) {
  188. b.StopTimer()
  189. passwd := []byte("mylongpassword1234")
  190. b.StartTimer()
  191. for i := 0; i < b.N; i++ {
  192. GenerateFromPassword(passwd, 10)
  193. }
  194. }
  195. // See Issue https://github.com/golang/go/issues/20425.
  196. func TestNoSideEffectsFromCompare(t *testing.T) {
  197. source := []byte("passw0rd123456")
  198. password := source[:len(source)-6]
  199. token := source[len(source)-6:]
  200. want := make([]byte, len(source))
  201. copy(want, source)
  202. wantHash := []byte("$2a$10$LK9XRuhNxHHCvjX3tdkRKei1QiCDUKrJRhZv7WWZPuQGRUM92rOUa")
  203. _ = CompareHashAndPassword(wantHash, password)
  204. got := bytes.Join([][]byte{password, token}, []byte(""))
  205. if !bytes.Equal(got, want) {
  206. t.Errorf("got=%q want=%q", got, want)
  207. }
  208. }