mirror of https://github.com/k3s-io/k3s
89 lines
1.8 KiB
Go
89 lines
1.8 KiB
Go
|
package hash
|
||
|
|
||
|
import (
|
||
|
"crypto/rand"
|
||
|
"crypto/subtle"
|
||
|
"encoding/base64"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
"golang.org/x/crypto/scrypt"
|
||
|
)
|
||
|
|
||
|
// Version is the hashing format version
|
||
|
const Version = 1
|
||
|
const hashFormat = "$%d:%x:%d:%d:%d:%s"
|
||
|
|
||
|
// SCrypt contains all of the variables needed for scrypt hashing
|
||
|
type SCrypt struct {
|
||
|
N int
|
||
|
R int
|
||
|
P int
|
||
|
KeyLen int
|
||
|
SaltLen int
|
||
|
}
|
||
|
|
||
|
// NewSCrypt returns a scrypt hasher with recommended default values
|
||
|
func NewSCrypt() Hasher {
|
||
|
return SCrypt{
|
||
|
N: 15,
|
||
|
R: 8,
|
||
|
P: 1,
|
||
|
KeyLen: 64,
|
||
|
SaltLen: 8,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// CreateHash will return a hashed version of the secretKey, or an error
|
||
|
func (s SCrypt) CreateHash(secretKey string) (string, error) {
|
||
|
salt := make([]byte, s.SaltLen)
|
||
|
|
||
|
_, err := rand.Read(salt)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
dk, err := scrypt.Key([]byte(secretKey), salt, 1<<s.N, s.R, s.P, s.KeyLen)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
enc := base64.RawStdEncoding.EncodeToString(dk)
|
||
|
hash := fmt.Sprintf(hashFormat, Version, salt, s.N, s.R, s.P, enc)
|
||
|
|
||
|
return hash, nil
|
||
|
}
|
||
|
|
||
|
// VerifyHash will compare a secretKey and a hash, and return nil if they match
|
||
|
func (s SCrypt) VerifyHash(hash, secretKey string) error {
|
||
|
var (
|
||
|
version, n uint
|
||
|
r, p int
|
||
|
enc string
|
||
|
salt []byte
|
||
|
)
|
||
|
_, err := fmt.Sscanf(hash, hashFormat, &version, &salt, &n, &r, &p, &enc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if version != Version {
|
||
|
return fmt.Errorf("hash version %d does not match package version %d", version, Version)
|
||
|
}
|
||
|
|
||
|
dk, err := base64.RawStdEncoding.DecodeString(enc)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
verify, err := scrypt.Key([]byte(secretKey), salt, 1<<n, r, p, len(dk))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if subtle.ConstantTimeCompare(dk, verify) != 1 {
|
||
|
return errors.New("hash does not match")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|