github.com/gramework/gramework@v1.8.1-0.20231027140105-82555c9057f5/test/grypto_test.go (about)

     1  // Copyright 2017-present Kirill Danshin and Gramework contributors
     2  // Copyright 2019-present Highload LTD (UK CN: 11893420)
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  
    11  package test
    12  
    13  import (
    14  	"testing"
    15  
    16  	"github.com/gramework/gramework/grypto/providers/scrypt"
    17  
    18  	"github.com/gramework/gramework/grypto"
    19  	"github.com/gramework/utils/grand"
    20  	"golang.org/x/crypto/bcrypt"
    21  )
    22  
    23  const (
    24  	cost = 10
    25  )
    26  
    27  // TestSalt128 makes sure the result is always 32 hex characters
    28  func TestSalt128(t *testing.T) {
    29  	if testing.Short() {
    30  		t.Skip("skipping test in short mode.")
    31  		return
    32  	}
    33  	var salt []byte
    34  	for i := 0; i < 256; i++ {
    35  		salt = grypto.Salt128()
    36  		if len(salt) != 16 {
    37  			t.Errorf("Salt128 fail to generate 128 bit salt: %02x", salt)
    38  			t.FailNow()
    39  		}
    40  	}
    41  }
    42  
    43  // TestPasswordSanity makes sure PasswordHash and ComparePassword work well together
    44  func TestPasswordSanity(t *testing.T) {
    45  	if testing.Short() {
    46  		t.Skip("skipping test in short mode.")
    47  		return
    48  	}
    49  	pw := make([]byte, 12)
    50  	pw2 := make([]byte, 12)
    51  	var hash, hash2 []byte
    52  
    53  	for i := 0; i < 8; i++ {
    54  		grand.Read(pw)
    55  		grand.Read(pw2)
    56  		hash = grypto.PasswordHash(pw)
    57  		hash2 = grypto.PasswordHash(pw2)
    58  
    59  		if !grypto.PasswordValid(hash, pw) {
    60  			t.Errorf("PasswordValid should return true for the pair: %s and %s", hash, pw)
    61  			t.FailNow()
    62  		}
    63  		if grypto.PasswordValid(hash2, pw) {
    64  			t.Error("PasswordValid is giving false positive")
    65  			t.FailNow()
    66  		}
    67  	}
    68  }
    69  
    70  // TestPasswordStringSanity makes sure PasswordHashString and ComparePassword work well together
    71  func TestPasswordStringSanity(t *testing.T) {
    72  	if testing.Short() {
    73  		t.Skip("skipping test in short mode.")
    74  		return
    75  	}
    76  	pw := make([]byte, 12)
    77  	pw2 := make([]byte, 12)
    78  	var hash, hash2 []byte
    79  
    80  	for i := 0; i < 8; i++ {
    81  		grand.Read(pw)
    82  		grand.Read(pw2)
    83  		hash = grypto.PasswordHashString(string(pw))
    84  		hash2 = grypto.PasswordHashString(string(pw2))
    85  
    86  		if !grypto.PasswordValid(hash, pw) {
    87  			t.Errorf("PasswordValid should return true for the pair: %s and %s", hash, pw)
    88  			t.FailNow()
    89  		}
    90  		if grypto.PasswordValid(hash2, pw) {
    91  			t.Error("PasswordValid is giving false positive")
    92  			t.FailNow()
    93  		}
    94  	}
    95  }
    96  
    97  // TestPasswordNeedsRehash makes sure TestPasswordNeedsRehash works well
    98  func TestPasswordNeedsRehash(t *testing.T) {
    99  	if testing.Short() {
   100  		t.Skip("skipping test in short mode.")
   101  		return
   102  	}
   103  	pw := make([]byte, 12)
   104  	for i := 0; i < 4; i++ {
   105  		grand.Read(pw)
   106  		hash, _ := bcrypt.GenerateFromPassword(pw, cost-1)
   107  		if !grypto.PasswordNeedsRehash(hash) {
   108  			t.Errorf("PasswordNeedsRehash returned false, expected true")
   109  			t.FailNow()
   110  		}
   111  	}
   112  	for i := 0; i < 4; i++ {
   113  		grand.Read(pw)
   114  		hash, _ := bcrypt.GenerateFromPassword(pw, cost)
   115  		if !grypto.PasswordNeedsRehash(hash) {
   116  			t.Errorf("PasswordNeedsRehash returned false, expected true as of bcrypt -> scrypt migration")
   117  		}
   118  	}
   119  	for i := 0; i < 4; i++ {
   120  		grand.Read(pw)
   121  		hash := scrypt.New().Hash(pw)
   122  		if grypto.PasswordNeedsRehash(hash) {
   123  			t.Errorf("PasswordNeedsRehash returned true, expected false for scrypt hash")
   124  		}
   125  	}
   126  }
   127  
   128  func BenchmarkPassHashAndValidation(b *testing.B) {
   129  	if testing.Short() {
   130  		b.Skip("skipping test in short mode.")
   131  		return
   132  	}
   133  	pws := make([][]byte, b.N)
   134  	for i := 0; i < b.N; i++ {
   135  		pws[i] = make([]byte, 12)
   136  		grand.Read(pws[i])
   137  	}
   138  
   139  	b.ResetTimer()
   140  
   141  	for i := 0; i < b.N; i += 2 {
   142  		hash := grypto.PasswordHash(pws[i])
   143  		if !grypto.PasswordValid(hash, pws[i]) {
   144  			b.Errorf("PasswordValid should return true for the pair: %s and %s", hash, pws[i])
   145  			b.FailNow()
   146  		}
   147  	}
   148  }
   149  
   150  func BenchmarkPassHash(b *testing.B) {
   151  	if testing.Short() {
   152  		b.Skip("skipping test in short mode.")
   153  		return
   154  	}
   155  	pws := make([][]byte, b.N)
   156  	for i := 0; i < b.N; i++ {
   157  		pws[i] = make([]byte, 12)
   158  		grand.Read(pws[i])
   159  	}
   160  
   161  	b.ResetTimer()
   162  
   163  	for i := 0; i < b.N; i++ {
   164  		hash := grypto.PasswordHash(pws[i])
   165  		_ = hash
   166  	}
   167  }