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  }