Password Hashing and Verification with KDF in Go
12 Dec 2019Great attention must be paid when doing the storage of password authentication information. Any incautious behavior may lead to the leak of password.
KDF (key derivation function) could derive secret keys with different length from the password, so it is quite suitable for password hashing and verification.
There are several KDF algorithms commonly used for password hashing, like bcrypt, scrypt, Argon2, PBKDF2, etc. According to the password hashing competition from 2013 to 2015, Argon2 was selected as the winner. Therefore, Argon2 is currently the most recommended algorithm for password hashing.
Do it in Go
Go provides the cryptography package golang.org/x/crypto which includes some common KDFs:
- Argon2, golang.org/x/crypto/argon2.
- Bcrypt, golang.org/x/crypto/bcrypt.
- Scrypt, golang.org/x/crypto/scrypt.
- PBKDF2, golang.org/x/crypto/pbkdf2.
- HKDF, golang.org/x/crypto/hkdf.
KDFCrypt package
It is not very convenient to use golang.org/x/crypto
directly.
Some extra works are needed, like generation of random salt, preserving
salt and parameters, etc.
KDFCrypt package is thus created to simplify the process of password hashing and verification.
KDFCrypt provides the common interface for various KDFs to do password hashing and verification. Here is a simple example:
package main
import (
"fmt"
"github.com/xianghuzhao/kdfcrypt"
)
func main() {
encoded, _ := kdfcrypt.Encode("password", &kdfcrypt.Option{
Algorithm: "argon2id",
Param: "m=65536,t=1,p=4",
RandomSaltLength: 16,
HashLength: 32,
})
// $argon2id$v=19,m=65536,t=1,p=4$mD+rvcR+6nuAV6MJFOmDjw$IqfwTPk9RMGeOv4pCE1QiURuSoi655GUVjcQAk81eXM
fmt.Println(encoded)
match, _ := kdfcrypt.Verify("password", encoded)
fmt.Println(match) // true
}
KDFCrypt defines two functions Encode
and Verify
.
Encode
could generate an encoded string according to the parameters.
The encoded string looks like:
$argon2id$v=19,m=4096,t=1,p=1$4ns1ibGJDR6IQufkbT8E/w$WQ2lAwbDhZmZQMCMg74L00OHUFzn/IvbwDaxU6bgIys
$ KDF $ param $ salt (base64) $ hash (base64)
This string contains full information for password verification,
which could be safely saved and verified with function Verify
.
kdfcrypt.Option
The Option struct is passed as argument for Encode.
- Algorithm: Could be one of
argon2id
,argon2i
,scrypt
,pbkdf
,hkdf
. - Param: String for the KDF param. Different items are separated by comma ”,“. The detailed items vary among different KDFs. Check the details in README.
- RandomSaltLength: The length for the random salt in byte.
- Salt: Salt for the hash. If Salt is not empty,
RandomSaltLength
will be ignored. - HashLength: The length of the hash result in byte.
Although various algorithms are provided,
argon2id
is still the most recommended one. Thebcrypt
is not included, becausegolang.org/x/crypto/bcrypt
does not provide a function for generating keys directly and used as a KDF. I once tried to addbcrypt
, but this would make interfaces and data complicated and inconsistent, so I just removed it.
Use key from KDF directly
Sometimes the keys are needed directly,
which could also be achieved with kdfcrypt
.
For details of the functions, please check the
GoDoc.
Generation of AES-256 key
AES-256
algorithm needs the key with 32 bytes,
and it could be generated like this:
kdf, err := kdfcrypt.CreateKDF("argon2id", "m=4096,t=1,p=1")
salt, err := kdfcrypt.GenerateRandomSalt(16)
aes256Key, err := kdf.Derive("password", salt, 32)