idkey/idkey.go

131 lines
2.3 KiB
Go
Raw Permalink Normal View History

2020-01-06 14:17:27 +08:00
package idkey
2020-01-06 14:16:08 +08:00
import (
"bytes"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"golang.org/x/crypto/argon2"
"strings"
)
2020-10-21 14:58:40 +08:00
// Data 密文编码结构
2020-01-06 14:16:08 +08:00
type Data struct {
EncodeOptions // 加密选项
Hash []byte // 密码密文
Salt []byte // 加密盐值
}
2020-10-21 14:54:59 +08:00
// EncodeOptions 密文编码选项
type EncodeOptions struct {
2020-01-06 14:16:08 +08:00
Time uint32 // 时间参数
Memory uint32 // 内存参数
Threads uint8 // 线程参数
KeyLen uint32 // 密文长度
}
2020-10-21 14:54:59 +08:00
// Encode 将密码编码成密文
func Encode(password []byte, options *EncodeOptions) string {
2020-01-06 14:16:08 +08:00
salt := generateSalt(16)
data := &Data{
Hash: nil,
Salt: salt,
}
if options != nil {
data.EncodeOptions = *options
} else {
data.EncodeOptions = EncodeOptions{
Time: 1,
Memory: 64 * 1024,
Threads: 4,
KeyLen: 32,
}
2020-01-06 14:16:08 +08:00
}
hash := argon2.IDKey(
2020-01-07 10:31:00 +08:00
password,
2020-01-06 14:16:08 +08:00
data.Salt,
data.Time,
data.Memory,
data.Threads,
data.KeyLen,
)
return fmt.Sprintf(
"$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
argon2.Version,
data.Memory,
data.Time,
data.Threads,
base64.RawStdEncoding.EncodeToString(salt),
base64.RawStdEncoding.EncodeToString(hash),
)
}
2020-10-21 14:58:40 +08:00
// Decode 获取密文编码结构
2020-01-06 14:16:08 +08:00
func Decode(passwordHash string) (data *Data, err error) {
data = &Data{}
params := strings.Split(passwordHash, "$")
if len(params) != 6 {
err = errors.New("input error")
return
}
var version int
_, err = fmt.Sscanf(
params[2],
"v=%d",
&version,
)
if err != nil {
return
}
if version != argon2.Version {
err = errors.New("not support")
return
}
_, err = fmt.Sscanf(
params[3],
"m=%d,t=%d,p=%d",
&data.Memory,
&data.Time,
&data.Threads,
)
if err != nil {
return
}
salt, err := base64.RawStdEncoding.DecodeString(params[4])
if err != nil {
return
}
data.Salt = salt
hash, err := base64.RawStdEncoding.DecodeString(params[5])
if err != nil {
return
}
data.Hash = hash
data.KeyLen = uint32(len(hash))
return
}
2020-10-21 14:54:59 +08:00
// Verify 验证密码编码后是否等于密文
2020-01-07 10:31:00 +08:00
func Verify(password []byte, passwordHash string) bool {
2020-01-06 14:16:08 +08:00
data, err := Decode(passwordHash)
if err != nil {
return false
}
hash := argon2.IDKey(
2020-01-07 10:31:00 +08:00
password,
2020-01-06 14:16:08 +08:00
data.Salt,
data.Time,
data.Memory,
data.Threads,
data.KeyLen,
)
return bytes.Equal(hash, data.Hash)
}
func generateSalt(l int) (b []byte) {
b = make([]byte, l)
_, _ = rand.Read(b)
return
}