-
Notifications
You must be signed in to change notification settings - Fork 3
/
salt-and-hash.go
134 lines (117 loc) · 3.98 KB
/
salt-and-hash.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package user
import (
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"fmt"
"log"
"golang.org/x/crypto/pbkdf2"
// "github.com/ztrue/tracerr";
"math/big"
)
// Original Python function I simplified it a little
/* def gen_nonce(length):
""" Generates a random string of bytes, base64 encoded """
if length < 1:
return ''
string=base64.b64encode(os.urandom(length),altchars=b'-_')
b64len=4* (length // 3)
if length%3 == 1:
b64len+=2
elif length%3 == 2:
b64len+=3
return string[0:b64len].decode() */
func randomBytes(length uint8) []byte {
if length == 0 {
log.Fatal("Random array of length 0 is not random!")
}
maxx := big.NewInt(256)
maxx = maxx.Exp(maxx, big.NewInt(int64(length)), nil)
var randomInt *big.Int
randomInt, err := rand.Int(rand.Reader, maxx)
if err != nil {
msg := fmt.Sprintf(
"Unable to generate random bytes, error = %v", err)
log.Fatal(msg)
}
return randomInt.Bytes()
}
// GenNonce Generates a random string of bytes, base64 encoded
// Inspired by the discussion in the https://github.com/joestump/python-oauth2/issues/9#
func GenNonce(length uint8) string {
nonceBytes := randomBytes(length)
nonceString := string(nonceBytes)
res := base64.RawURLEncoding.EncodeToString([]byte(nonceString))
return res
}
const saltBytes = 16
// SaltAndHashPassword generates a dynamic salt, hash and return both
// https://habr.com/ru/post/145648/
func SaltAndHashPassword(password string) (saltBase64, dkBase64 string) {
salt := randomBytes(saltBytes)
dk := pbkdf2.Key([]byte(password), salt, 4096, 32, sha1.New)
saltBase64 = base64.RawURLEncoding.EncodeToString(salt)
dkBase64 = base64.RawURLEncoding.EncodeToString(dk)
return
}
// CheckPasswordAgainstSaltAndHash matches password to hash/salt pair
func CheckPasswordAgainstSaltAndHash(password, saltBase64, dkBase64 string) bool {
salt, err := base64.RawURLEncoding.DecodeString(saltBase64)
if err != nil {
return false
}
dk := pbkdf2.Key([]byte(password), salt, 4096, 32, sha1.New)
dkBase642 := base64.RawURLEncoding.EncodeToString(dk)
return (dkBase64 == dkBase642)
}
/*
// http://security.stackexchange.com/questions/110084/parameters-for-pbkdf2-for-password-hashing
type HashingConfig struct {
hashBytes int // size of the generated hash (to be chosen accordint the the chosen algo)
saltBytes int // sise of the salt : larger salt means hashed passwords are more resistant to rainbow table
iterations int // tune so that hashing the password takes about 1 second
algo string
encoding string // hex is readable but base64 is shorter
}
var config = HashingConfig{
hashBytes : 64,
saltBytes : 16,
iterations : 220000,
algo :"sha512",
encoding : "base64" };
/**
* Verify a password using Node's asynchronous pbkdf2 (key derivation) function.
*
* Accepts a hash and salt generated by hashPassword, and returns whether the
* hash matched the password (as a resolved promise).
*/
/* function verifyPassword(password, hashframe) {
// decode and extract hashing parameters
hashframe = Buffer.from(hashframe, config.encoding);
var saltBytes = hashframe.readUInt32BE(0);
var hashBytes = hashframe.length - saltBytes - 8;
var iterations = hashframe.readUInt32BE(4);
var salt = hashframe.slice(8, saltBytes + 8);
var hash = hashframe.slice(8 + saltBytes, saltBytes + hashBytes + 8);
// verify the salt and hash against the password
return crypto.pbkdf2Async(password, salt, iterations, hashBytes, config.algo)
.then(function(verify) {
if (verify.equals(hash)) return Promise.resolve(true);
return Promise.resolve(false) ;
})
}
exports.hashPassword = hashPassword;
exports.verifyPassword = verifyPassword;
*/
// used for testing
/*
console.time("hash");
hashPassword("abc")
.then(function(hash) {
console.log("hashframe", hash.length, hash);
console.timeEnd("hash");
return verifyPassword("abc", hash);
})
.then(function() { console.log("password correct");})
.catch(function(err) { console.log("err", err);})
*/