github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/bcrypt/bcrypt_test.go (about) 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 bcrypt 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "testing" 12 ) 13 14 func TestBcryptingIsEasy(t *testing.T) { 15 t.Parallel() 16 17 pass := []byte("mypassword") 18 salt := []byte("1234567890123456") 19 hp, err := GenerateFromPassword(salt, pass, 0) 20 if err != nil { 21 t.Fatalf("GenerateFromPassword error: %s", err) 22 } 23 24 if CompareHashAndPassword(hp, pass) != nil { 25 t.Errorf("%v should hash %s correctly", hp, pass) 26 } 27 28 notPass := "notthepass" 29 err = CompareHashAndPassword(hp, []byte(notPass)) 30 if !errors.Is(err, ErrMismatchedHashAndPassword) { 31 t.Errorf("%v and %s should be mismatched", hp, notPass) 32 } 33 } 34 35 func TestBcryptingIsCorrect(t *testing.T) { 36 t.Parallel() 37 38 pass := []byte("allmine") 39 salt := []byte("XajjQvNhvvRt5GSeFk1xFe") 40 expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga") 41 42 hash, err := bcrypt(pass, 10, salt) 43 if err != nil { 44 t.Fatalf("bcrypt blew up: %v", err) 45 } 46 if !bytes.HasSuffix(expectedHash, hash) { 47 t.Errorf("%v should be the suffix of %v", hash, expectedHash) 48 } 49 50 h, err := newFromHash(expectedHash) 51 if err != nil { 52 t.Errorf("Unable to parse %s: %v", string(expectedHash), err) 53 } 54 55 // This is not the safe way to compare these hashes. We do this only for 56 // testing clarity. Use bcrypt.CompareHashAndPassword() 57 if err == nil && !bytes.Equal(expectedHash, h.Hash()) { 58 t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash) 59 } 60 } 61 62 func TestVeryShortPasswords(t *testing.T) { 63 t.Parallel() 64 65 key := []byte("k") 66 salt := []byte("XajjQvNhvvRt5GSeFk1xFe") 67 _, err := bcrypt(key, 10, salt) 68 if err != nil { 69 t.Errorf("One byte key resulted in error: %s", err) 70 } 71 } 72 73 func TestTooLongPasswordsWork(t *testing.T) { 74 t.Parallel() 75 76 salt := []byte("XajjQvNhvvRt5GSeFk1xFe") 77 // One byte over the usual 56 byte limit that blowfish has 78 tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456") 79 tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C") 80 hash, err := bcrypt(tooLongPass, 10, salt) 81 if err != nil { 82 t.Fatalf("bcrypt blew up on long password: %v", err) 83 } 84 if !bytes.HasSuffix(tooLongExpected, hash) { 85 t.Errorf("%v should be the suffix of %v", hash, tooLongExpected) 86 } 87 } 88 89 type InvalidHashTest struct { 90 err error 91 hash []byte 92 } 93 94 var invalidTests = []InvalidHashTest{ 95 {ErrHashTooShort, []byte("$2a$10$fooo")}, 96 {ErrHashTooShort, []byte("$2a")}, 97 {HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")}, 98 {InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")}, 99 {InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")}, 100 } 101 102 func TestInvalidHashErrors(t *testing.T) { 103 t.Parallel() 104 105 check := func(name string, expected, err error) { 106 if err == nil { 107 t.Errorf("%s: Should have returned an error", name) 108 } 109 if err != nil && !errors.Is(err, expected) { 110 t.Errorf("%s gave err %v but should have given %v", name, err, expected) 111 } 112 } 113 for _, iht := range invalidTests { 114 _, err := newFromHash(iht.hash) 115 check("newFromHash", iht.err, err) 116 err = CompareHashAndPassword(iht.hash, []byte("anything")) 117 check("CompareHashAndPassword", iht.err, err) 118 } 119 } 120 121 func TestUnpaddedBase64Encoding(t *testing.T) { 122 t.Parallel() 123 124 original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30} 125 encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe") 126 127 encoded := base64Encode(original) 128 129 if !bytes.Equal(encodedOriginal, encoded) { 130 t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal) 131 } 132 133 decoded, err := base64Decode(encodedOriginal) 134 if err != nil { 135 t.Fatalf("base64Decode blew up: %s", err) 136 } 137 138 if !bytes.Equal(decoded, original) { 139 t.Errorf("Decoded %v should have equaled %v", decoded, original) 140 } 141 } 142 143 func TestCost(t *testing.T) { 144 t.Parallel() 145 146 suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C" 147 for _, vers := range []string{"2a", "2"} { 148 for _, cost := range []int{4, 10} { 149 s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix) 150 h := []byte(s) 151 actual, err := Cost(h) 152 if err != nil { 153 t.Errorf("Cost, error: %s", err) 154 continue 155 } 156 if actual != cost { 157 t.Errorf("Cost, expected: %d, actual: %d", cost, actual) 158 } 159 } 160 } 161 _, err := Cost([]byte("$a$a$" + suffix)) 162 if err == nil { 163 t.Errorf("Cost, malformed but no error returned") 164 } 165 } 166 167 func TestCostValidationInHash(t *testing.T) { 168 t.Parallel() 169 170 if testing.Short() { 171 return 172 } 173 174 salt := []byte("1234567890123456") 175 pass := []byte("mypassword") 176 177 for c := 0; c < MinCost; c++ { 178 p, _ := newFromPassword(salt, pass, c) 179 if p.cost != DefaultCost { 180 t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost) 181 } 182 } 183 184 p, _ := newFromPassword(salt, pass, 14) 185 if p.cost != 14 { 186 t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost) 187 } 188 189 hp, _ := newFromHash(p.Hash()) 190 if p.cost != hp.cost { 191 t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost) 192 } 193 194 _, err := newFromPassword(salt, pass, 32) 195 if err == nil { 196 t.Fatalf("newFromPassword: should return a cost error") 197 } 198 if !errors.Is(err, InvalidCostError(32)) { 199 t.Errorf("newFromPassword: should return cost error, got %#v", err) 200 } 201 } 202 203 func TestCostReturnsWithLeadingZeroes(t *testing.T) { 204 t.Parallel() 205 206 salt := []byte("1234567890123456") 207 hp, _ := newFromPassword(salt, []byte("abcdefgh"), 7) 208 cost := hp.Hash()[4:7] 209 expected := []byte("07$") 210 211 if !bytes.Equal(expected, cost) { 212 t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected) 213 } 214 } 215 216 func TestMinorNotRequired(t *testing.T) { 217 t.Parallel() 218 219 noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga") 220 h, err := newFromHash(noMinorHash) 221 if err != nil { 222 t.Fatalf("No minor hash blew up: %s", err) 223 } 224 if h.minor != 0 { 225 t.Errorf("Should leave minor version at 0, but was %d", h.minor) 226 } 227 228 if !bytes.Equal(noMinorHash, h.Hash()) { 229 t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash()) 230 } 231 } 232 233 func BenchmarkEqual(b *testing.B) { 234 b.StopTimer() 235 passwd := []byte("somepasswordyoulike") 236 salt := []byte("1234567890123456") 237 hash, _ := GenerateFromPassword(salt, passwd, DefaultCost) 238 b.StartTimer() 239 for i := 0; i < b.N; i++ { 240 CompareHashAndPassword(hash, passwd) 241 } 242 } 243 244 func BenchmarkDefaultCost(b *testing.B) { 245 b.StopTimer() 246 passwd := []byte("mylongpassword1234") 247 salt := []byte("1234567890123456") 248 b.StartTimer() 249 for i := 0; i < b.N; i++ { 250 GenerateFromPassword(salt, passwd, DefaultCost) 251 } 252 } 253 254 // See Issue https://github.com/golang/go/issues/20425. 255 func TestNoSideEffectsFromCompare(t *testing.T) { 256 t.Parallel() 257 258 source := []byte("passw0rd123456") 259 password := source[:len(source)-6] 260 token := source[len(source)-6:] 261 want := make([]byte, len(source)) 262 copy(want, source) 263 264 wantHash := []byte("$2a$10$LK9XRuhNxHHCvjX3tdkRKei1QiCDUKrJRhZv7WWZPuQGRUM92rOUa") 265 _ = CompareHashAndPassword(wantHash, password) 266 267 got := bytes.Join([][]byte{password, token}, []byte("")) 268 if !bytes.Equal(got, want) { 269 t.Errorf("got=%q want=%q", got, want) 270 } 271 }