code.gitea.io/gitea@v1.19.3/modules/auth/password/hash/hash_test.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package hash 5 6 import ( 7 "encoding/hex" 8 "strconv" 9 "strings" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 ) 14 15 type testSaltHasher string 16 17 func (t testSaltHasher) HashWithSaltBytes(password string, salt []byte) string { 18 return password + "$" + string(salt) + "$" + string(t) 19 } 20 21 func Test_registerHasher(t *testing.T) { 22 MustRegister("Test_registerHasher", func(config string) testSaltHasher { 23 return testSaltHasher(config) 24 }) 25 26 assert.Panics(t, func() { 27 MustRegister("Test_registerHasher", func(config string) testSaltHasher { 28 return testSaltHasher(config) 29 }) 30 }) 31 32 assert.Error(t, Register("Test_registerHasher", func(config string) testSaltHasher { 33 return testSaltHasher(config) 34 })) 35 36 assert.Equal(t, "password$salt$", 37 Parse("Test_registerHasher").PasswordSaltHasher.HashWithSaltBytes("password", []byte("salt"))) 38 39 assert.Equal(t, "password$salt$config", 40 Parse("Test_registerHasher$config").PasswordSaltHasher.HashWithSaltBytes("password", []byte("salt"))) 41 42 delete(availableHasherFactories, "Test_registerHasher") 43 } 44 45 func TestParse(t *testing.T) { 46 hashAlgorithmsToTest := []string{} 47 for plainHashAlgorithmNames := range availableHasherFactories { 48 hashAlgorithmsToTest = append(hashAlgorithmsToTest, plainHashAlgorithmNames) 49 } 50 for _, aliased := range aliasAlgorithmNames { 51 if strings.Contains(aliased, "$") { 52 hashAlgorithmsToTest = append(hashAlgorithmsToTest, aliased) 53 } 54 } 55 for _, algorithmName := range hashAlgorithmsToTest { 56 t.Run(algorithmName, func(t *testing.T) { 57 algo := Parse(algorithmName) 58 assert.NotNil(t, algo, "Algorithm %s resulted in an empty algorithm", algorithmName) 59 }) 60 } 61 } 62 63 func TestHashing(t *testing.T) { 64 hashAlgorithmsToTest := []string{} 65 for plainHashAlgorithmNames := range availableHasherFactories { 66 hashAlgorithmsToTest = append(hashAlgorithmsToTest, plainHashAlgorithmNames) 67 } 68 for _, aliased := range aliasAlgorithmNames { 69 if strings.Contains(aliased, "$") { 70 hashAlgorithmsToTest = append(hashAlgorithmsToTest, aliased) 71 } 72 } 73 74 runTests := func(password, salt string, shouldPass bool) { 75 for _, algorithmName := range hashAlgorithmsToTest { 76 t.Run(algorithmName, func(t *testing.T) { 77 output, err := Parse(algorithmName).Hash(password, salt) 78 if shouldPass { 79 assert.NoError(t, err) 80 assert.NotEmpty(t, output, "output for %s was empty", algorithmName) 81 } else { 82 assert.Error(t, err) 83 } 84 85 assert.Equal(t, Parse(algorithmName).VerifyPassword(password, output, salt), shouldPass) 86 }) 87 } 88 } 89 90 // Test with new salt format. 91 runTests(strings.Repeat("a", 16), hex.EncodeToString([]byte{0x01, 0x02, 0x03}), true) 92 93 // Test with legacy salt format. 94 runTests(strings.Repeat("a", 16), strings.Repeat("b", 10), true) 95 96 // Test with invalid salt. 97 runTests(strings.Repeat("a", 16), "a", false) 98 } 99 100 // vectors were generated using the current codebase. 101 var vectors = []struct { 102 algorithms []string 103 password string 104 salt string 105 output string 106 shouldfail bool 107 }{ 108 { 109 algorithms: []string{"bcrypt", "bcrypt$10"}, 110 password: "abcdef", 111 salt: strings.Repeat("a", 10), 112 output: "$2a$10$fjtm8BsQ2crym01/piJroenO3oSVUBhSLKaGdTYJ4tG0ePVCrU0G2", 113 shouldfail: false, 114 }, 115 { 116 algorithms: []string{"scrypt", "scrypt$65536$16$2$50"}, 117 password: "abcdef", 118 salt: strings.Repeat("a", 10), 119 output: "3b571d0c07c62d42b7bad3dbf18fb0cd67d4d8cd4ad4c6928e1090e5b2a4a84437c6fd2627d897c0e7e65025ca62b67a0002", 120 shouldfail: false, 121 }, 122 { 123 algorithms: []string{"argon2", "argon2$2$65536$8$50"}, 124 password: "abcdef", 125 salt: strings.Repeat("a", 10), 126 output: "551f089f570f989975b6f7c6a8ff3cf89bc486dd7bbe87ed4d80ad4362f8ee599ec8dda78dac196301b98456402bcda775dc", 127 shouldfail: false, 128 }, 129 { 130 algorithms: []string{"pbkdf2", "pbkdf2$10000$50"}, 131 password: "abcdef", 132 salt: strings.Repeat("a", 10), 133 output: "ab48d5471b7e6ed42d10001db88c852ff7303c788e49da5c3c7b63d5adf96360303724b74b679223a3dea8a242d10abb1913", 134 shouldfail: false, 135 }, 136 { 137 algorithms: []string{"bcrypt", "bcrypt$10"}, 138 password: "abcdef", 139 salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), 140 output: "$2a$10$qhgm32w9ZpqLygugWJsLjey8xRGcaq9iXAfmCeNBXxddgyoaOC3Gq", 141 shouldfail: false, 142 }, 143 { 144 algorithms: []string{"scrypt", "scrypt$65536$16$2$50"}, 145 password: "abcdef", 146 salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), 147 output: "25fe5f66b43fa4eb7b6717905317cd2223cf841092dc8e0a1e8c75720ad4846cb5d9387303e14bc3c69faa3b1c51ef4b7de1", 148 shouldfail: false, 149 }, 150 { 151 algorithms: []string{"argon2", "argon2$2$65536$8$50"}, 152 password: "abcdef", 153 salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), 154 output: "9c287db63a91d18bb1414b703216da4fc431387c1ae7c8acdb280222f11f0929831055dbfd5126a3b48566692e83ec750d2a", 155 shouldfail: false, 156 }, 157 { 158 algorithms: []string{"pbkdf2", "pbkdf2$10000$50"}, 159 password: "abcdef", 160 salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), 161 output: "45d6cdc843d65cf0eda7b90ab41435762a282f7df013477a1c5b212ba81dbdca2edf1ecc4b5cb05956bb9e0c37ab29315d78", 162 shouldfail: false, 163 }, 164 { 165 algorithms: []string{"pbkdf2$320000$50"}, 166 password: "abcdef", 167 salt: hex.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04}), 168 output: "84e233114499e8721da80e85568e5b7b5900b3e49a30845fcda9d1e1756da4547d70f8740ac2b4a5d82f88cebcd27f21bfe2", 169 shouldfail: false, 170 }, 171 { 172 algorithms: []string{"pbkdf2", "pbkdf2$10000$50"}, 173 password: "abcdef", 174 salt: "", 175 output: "", 176 shouldfail: true, 177 }, 178 } 179 180 // Ensure that the current code will correctly verify against the test vectors. 181 func TestVectors(t *testing.T) { 182 for i, vector := range vectors { 183 for _, algorithm := range vector.algorithms { 184 t.Run(strconv.Itoa(i)+": "+algorithm, func(t *testing.T) { 185 pa := Parse(algorithm) 186 assert.Equal(t, !vector.shouldfail, pa.VerifyPassword(vector.password, vector.output, vector.salt)) 187 }) 188 } 189 } 190 }