...

Source file src/golang.org/x/crypto/bcrypt/bcrypt_test.go

Documentation: golang.org/x/crypto/bcrypt

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

View as plain text