This Go package provides common interface implementation to encode a raw string password (example, during registration on a site), or later verify it (example, while login in to the site).
Already supported hash types:
const (
Bcrypt EncoderType = "bcrypt" // use bcrypt hash
Scrypt EncoderType = "scrypt" // use scrypt hash
Pbkdf2 EncoderType = "pbkdf2" // use pbkdf2 hash
Argon2 EncoderType = "argon2" // use argon2 hash
Hkdf EncoderType = "hkdf" // use hkdf hash
Hmac EncoderType = "hmac" // use hmac hash
)Each hash type implements the Encoder interface
type Encoder interface {
// Encode returns the hash value of the given data
Encode(src string) (string, error)
// Verify compares a encoded data with its possible plaintext equivalent
Verify(hash string, rawData string) (bool, error)
// GetSalt Returns the salt if exists, otherwise nil
GetSalt() ([]byte, error)
// Hash Generate and return a hash value in []byte format
Hash(src string) ([]byte, error)
}- After using the
**New()**method to obtain an Encoder instance, you can call its**Encode()**method to obtain the hash result of the specified data. - use the
**Verify()**method to compares a encoded data with its possible plaintext equivalent. GetSalt()method can get the automatically generated random string salt or the salt provided by yourself.- When you call the
**New()**method, you can also provide zero or more**Options**to help initialize an**Encoder**instance. **Hash()**method returns the unencoded hash value in**[]byte**format**Verify()**method can only verify the result returned by**Encode()**method, not**Hash()**method. The result returned by**Hash()**is handled by the user.
go get gopkg.in/encoder.v1Run go test in the package's directory to run test case.
Argon2WIKI
Argon2 is a key derivation function that was selected as the winner of the 2015 Password Hashing Competition.
hash result example: $argon2id$v=19$m=65536,t=1,p=4$Te27FDFdjc7lofyIqKc4FA$XLkAG/lwiZGVvq5jVMTAIgqV2NZGDXSKFvKoIuCx/Pc
package main
import (
"encoding/base64"
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// Using the default options
// types.Argon2 types.Pbkdf2 types.Bcrypt types.Hkdf types.Scrypt
encoding := encoder.New(types.Argon2) // or use encoder.NewArgon2Encoder()
hash, err := encoding.Encode(data)
if err != nil {
return
}
fmt.Println(hash)
salt, _ := encoding.GetSalt()
fmt.Println("salt: ", base64.StdEncoding.EncodeToString(salt))
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}zero or more options can be used each time, supported options for argon2:
WithMemoryThe amount of memory used by the Argon2 algorithm (in kibibytes), default value is 64 * 1024WithIterationsThe number of iterations (or passes) over the memory, default value is 1WithThreadsThe number of threads (or lanes) used by the algorithm, default value is 4WithSaltLenLength of the random salt. 16 bytes is recommended for password hashing, default value is 16WithKeyLenLength of the generated key (or password hash). 16 bytes or more is recommended, default value is 32WithSaltSpecify the salt, do not use the automatically generated salt, default automatically generate random strings
package main
import (
"encoding/base64"
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/argon2"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// set salt length and threads
encoding := encoder.New(types.Argon2, argon2.WithSaltLen(32), argon2.WithThreads(8))
// encoder.NewArgon2Encoder(argon2.WithSaltLen(32), argon2.WithThreads(8))
hash, err := encoding.Encode(data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(hash)
salt, _ := encoding.GetSalt()
fmt.Println("salt: ", base64.StdEncoding.EncodeToString(salt))
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}
BcryptWIKI
Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.
bcryptdoes not need to specify a salt, salt is automatically generated every time, and theGetSaltmethod will not return a salt. For the same value, the hash results of multiple executions are different, but each hash result can pass theVerifymethod, so only one result needs to be saved.
hash result example: $2a$10$jIlPSogBSONL.Y2pO1F3H.1KmpnhKMp9npWs2Q2X9rGiE0ZetrAC2
package main
import (
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// Using the default options
// types.Argon2 types.Pbkdf2 types.Bcrypt types.Hkdf types.Scrypt
encoding := encoder.New(types.Bcrypt) // or use encoder.NewBcryptEncoder()
hash, err := encoding.Encode(data)
if err != nil {
return
}
fmt.Println(hash)
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}zero or more options can be used each time, supported options for bcrypt:
WithCostin order to adjust for greater computational power, default value is 10, min is 4 max is 31, The larger the value, the more time-consuming
package main
import (
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/bcrypt"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// set cost
encoding := encoder.New(types.Bcrypt, bcrypt.WithCost(10))
// encoder.NewBcryptEncoder(bcrypt.WithCost(10))
hash, err := encoding.Encode(data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(hash)
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}
PBKDF2(Password-Based Key Derivation Function 2) WIKI
PBKDF2 applies a pseudorandom function, such as hash-based message authentication code (HMAC), to the input password or passphrase along with a salt value and repeats the process many times to produce a derived key, which can then be used as a cryptographic key in subsequent operations. The added computational work makes password cracking much more difficult, and is known as key stretching.
You can specify the
hashfunction used when hashing, the default issha256.New. Other hash functions available include:
- md5.New
- sha1.New
- sha256.New224
- sha256.New
- sha512.New
- sha512.New384
- sha512.New512_224
- sha512.New512_256
hash result example: c46f9f21b89a22a36df3c1de5a91a85f5b4656f616e79d2e589b3b32a4648e80
package main
import (
"encoding/base64"
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// Using the default options
// types.Argon2 types.Pbkdf2 types.Bcrypt types.Hkdf types.Scrypt
encoding := encoder.New(types.Pbkdf2) // or use encoder.NewPbkdf2Encoder()
hash, err := encoding.Encode(data)
if err != nil {
return
}
fmt.Println(hash)
salt, _ := encoding.GetSalt()
fmt.Println("salt: ", base64.StdEncoding.EncodeToString(salt))
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}zero or more options can be used each time, supported options for pbkdf2:
WithHasFuncHash function used this time, default value is sha256.NewWithKeyLenLength of the generated key, default value is 32WithIterationsThe number of iterations, the more times, the longer it takes to encrypt and decrypt, default value is 10000WithSaltLenLength of the random salt, default value is 16WithSaltSpecify the salt, do not use the automatically generated salt, default automatically generate random strings
package main
import (
"crypto/sha512"
"encoding/base64"
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/pbkdf2"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// set cost
encoding := encoder.New(types.Pbkdf2, pbkdf2.WithHasFunc(sha512.New), pbkdf2.WithIterations(20000))
// encoding := encoder.NewPbkdf2Encoder(pbkdf2.WithHasFunc(sha512.New), pbkdf2.WithIterations(20000))
hash, err := encoding.Encode(data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(hash)
salt, _ := encoding.GetSalt()
fmt.Println("salt: ", base64.StdEncoding.EncodeToString(salt))
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}
ScryptWIKI
scrypt is a cryptographic derivation algorithm created by Colin Percival. Using the scrypt algorithm to generate derived keys requires a lot of memory. The scrypt algorithm was published as the RFC 7914 standard in 2016.
The main function of the password derivation algorithm is to generate a series of derivative passwords according to the initialized master password. This algorithm is mainly to resist the attack of brute force cracking. By increasing the complexity of password generation, it also increases the difficulty of brute force cracking
hash result example: d3bec465e05605b71678ba83be09c602ff589c78d3f2edb9fb0e83a9f34fd81f
package main
import (
"encoding/base64"
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// set cost
encoding := encoder.New(types.Scrypt)
// encoding := encoder.NewScryptEncoder()
hash, err := encoding.Encode(data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(hash)
salt, _ := encoding.GetSalt()
fmt.Println("salt: ", base64.StdEncoding.EncodeToString(salt))
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}zero or more options can be used each time, supported options for scrypt:
WithSaltLenLength of the random salt. default value is 16WithSaltSpecify the salt, do not use the automatically generated salt, default automatically generate random stringsWithNCPU/memory cost parameter, default value is 32768WithRblock size parameter, default value is 8WithPparallelisation parameter, default value is 8
package main
import (
"encoding/base64"
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/scrypt"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// set cost
encoding := encoder.New(types.Scrypt, scrypt.WithSaltLen(32))
// encoder.NewScryptEncoder(scrypt.WithSaltLen(32))
hash, err := encoding.Encode(data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(hash)
salt, _ := encoding.GetSalt()
fmt.Println("salt: ", base64.StdEncoding.EncodeToString(salt))
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}
HkdfHMAC-based KDF(key derivation function) WIKI
HKDF is a simple key derivation function (KDF) based on the HMAC message authentication code. It was initially proposed by its authors as a building block in various protocols and applications, as well as to discourage the proliferation of multiple KDF mechanisms. The main approach HKDF follows is the "extract-then-expand" paradigm, where the KDF logically consists of two modules: the first stage takes the input keying material and "extracts" from it a fixed-length pseudorandom key, and then the second stage "expands" this key into several additional pseudorandom keys (the output of the KDF).
It can be used, for example, to convert shared secrets exchanged via Diffie–Hellman into key material suitable for use in encryption, integrity checking or authentication
hash result example: 8477efcdc326dd85fb6713e9bd6a44fba66917a5d9e890a657bed2d7c6294c01
package main
import (
"encoding/base64"
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// set cost
encoding := encoder.New(types.Hkdf)
// encoder.NewHkdfEncoder()
hash, err := encoding.Encode(data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(hash)
salt, _ := encoding.GetSalt()
fmt.Println("salt: ", base64.StdEncoding.EncodeToString(salt))
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}zero or more options can be used each time, supported options for scrypt:
WithSaltLenLength of the random salt. default value is 16WithSaltSpecify the salt, do not use the automatically generated salt, default automatically generate random stringsWithHashLenThe length of the generated hash, default value is 32WithInfocalling HMAC as the message field, default value is ""WithHasFuncHash function used this time, default value issha256.New
package main
import (
"encoding/base64"
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/hkdf"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// set cost
encoding := encoder.New(types.Hkdf, hkdf.WithSaltLen(32), hkdf.WithHashLen(64))
// encoder.NewHkdfEncoder(hkdf.WithSaltLen(32), hkdf.WithHashLen(64))
hash, err := encoding.Encode(data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(hash)
salt, _ := encoding.GetSalt()
fmt.Println("salt: ", base64.StdEncoding.EncodeToString(salt))
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}
hmacKeyed-Hashing for Message Authentication WIKI
In cryptography, an HMAC (sometimes expanded as either keyed-hash message authentication code or hash-based message authentication code) is a specific type of message authentication code (MAC) involving a cryptographic hash function and a secret cryptographic key. As with any MAC, it may be used to simultaneously verify both the data integrity and authenticity of a message.
HMAC can provide authentication using a shared secret instead of using digital signatures with asymmetric cryptography. It trades off the need for a complex public key infrastructure by delegating the key exchange to the communicating parties, who are responsible for establishing and using a trusted channel to agree on the key prior to communication.
hash result example: 1bc6681912e7162213dd29ffa4518300b5a19496c181ebc7d694b40f679ed5eb
package main
import (
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// set cost
encoding := encoder.New(types.Hmac)
// encoder.NewHmacEncoder()
hash, err := encoding.Encode(data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(hash)
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}zero or more options can be used each time, supported options for hmac:
WithKeyis the secret key. default value is 16WithHasFuncHash function used this time, default value issha256.New
package main
import (
"crypto/sha512"
"fmt"
"gopkg.in/encoder.v1"
"gopkg.in/encoder.v1/hmac"
"gopkg.in/encoder.v1/types"
)
func main() {
data := "hello world"
// set cost
encoding := encoder.New(types.Hkdf, hmac.WithKey("my secrets"), hmac.WithHasFunc(sha512.New))
// encoder.NewHmacEncoder(hmac.WithKey("my secrets"), hmac.WithHasFunc(sha512.New))
hash, err := encoding.Encode(data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(hash)
verify, err := encoding.Verify(hash, data)
if err != nil {
return
}
if verify {
fmt.Println("match")
}
}