247 lines
4.3 KiB
Go
247 lines
4.3 KiB
Go
|
package bcrypt
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"crypto/rand"
|
||
|
"encoding/binary"
|
||
|
|
||
|
"github.com/dchest/bcrypt_pbkdf"
|
||
|
"r00t2.io/sshkeys/internal"
|
||
|
`r00t2.io/sshkeys/kdf/errs`
|
||
|
)
|
||
|
|
||
|
/*
|
||
|
Setup must be called before DeriveKey. It configures a .
|
||
|
|
||
|
If secret is nil or an empty byte slice, ErrNoSecret will be returned.
|
||
|
|
||
|
If salt is nil or an empty byte slice, one will be randomly generated of DefaultSaltLen length.
|
||
|
|
||
|
If rounds is 0, DefaultRounds will be used.
|
||
|
|
||
|
If keyLen is 0, DefaultKeyLen will be used.
|
||
|
*/
|
||
|
func (k *KDF) Setup(secret, salt []byte, rounds, keyLen uint32) (err error) {
|
||
|
|
||
|
if secret == nil || len(secret) == 0 {
|
||
|
err = errs.ErrNoSecret
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if salt == nil || len(salt) == 0 {
|
||
|
salt = make([]byte, DefaultSaltLen)
|
||
|
if _, err = rand.Read(salt); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if rounds == 0 {
|
||
|
rounds = DefaultRounds
|
||
|
}
|
||
|
|
||
|
if keyLen == 0 {
|
||
|
keyLen = DefaultKeyLen
|
||
|
}
|
||
|
|
||
|
k.secret = secret
|
||
|
k.salt = salt
|
||
|
k.rounds = rounds
|
||
|
k.keyLen = keyLen
|
||
|
k.hasSalt = true
|
||
|
k.hasRounds = true
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
SetupAuto is used to provide out-of-band configuration if the KDF options were found via kdf.UnpackKDF.
|
||
|
|
||
|
You can test this by running KDF.AutoOK.
|
||
|
*/
|
||
|
func (k *KDF) SetupAuto(secret []byte, keyLen uint32) (err error) {
|
||
|
|
||
|
if !k.AutoOK() {
|
||
|
err = errs.ErrUnknownKdf
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if keyLen == 0 {
|
||
|
keyLen = DefaultKeyLen
|
||
|
}
|
||
|
|
||
|
k.secret = secret
|
||
|
k.keyLen = keyLen
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
DeriveKey returns the derived key from a configured bcrypt.KDF.
|
||
|
|
||
|
It must be called *after* Setup.
|
||
|
*/
|
||
|
func (k *KDF) DeriveKey() (key []byte, err error) {
|
||
|
|
||
|
if k.secret == nil {
|
||
|
err = errs.ErrNoSecret
|
||
|
return
|
||
|
}
|
||
|
if k.salt == nil {
|
||
|
err = errs.ErrNoSalt
|
||
|
return
|
||
|
}
|
||
|
if k.rounds == 0 {
|
||
|
err = errs.ErrNoRounds
|
||
|
return
|
||
|
}
|
||
|
if k.keyLen == 0 {
|
||
|
err = errs.ErrNoKeyLen
|
||
|
}
|
||
|
|
||
|
if k.key, err = bcrypt_pbkdf.Key(k.secret, k.salt, int(k.rounds), int(k.keyLen)); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
key = k.key
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Name returns bcrypt.Name.
|
||
|
func (k *KDF) Name() (name string) {
|
||
|
|
||
|
name = Name
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// NameBytes returns the byte form of bcrypt.Name with leading bytecount allocator.
|
||
|
func (k *KDF) NameBytes() (name []byte) {
|
||
|
|
||
|
var nb []byte
|
||
|
var s = k.Name()
|
||
|
|
||
|
nb = []byte(s)
|
||
|
|
||
|
name = make([]byte, 4)
|
||
|
binary.BigEndian.PutUint32(name, uint32(len(nb)))
|
||
|
name = append(name, nb...)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// PackedBytes returns block 3.0 and recursed.
|
||
|
func (k *KDF) PackedBytes() (buf *bytes.Reader, err error) {
|
||
|
|
||
|
var rounds = make([]byte, 4)
|
||
|
var packer *bytes.Reader
|
||
|
var w = new(bytes.Buffer)
|
||
|
|
||
|
// block 3.0.0.0 and block 3.0.0.0.0
|
||
|
if packer, err = internal.ReadSizeBytes(k.salt, true); err != nil {
|
||
|
return
|
||
|
}
|
||
|
if _, err = packer.WriteTo(w); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// block 3.0.0.1
|
||
|
binary.BigEndian.PutUint32(rounds, k.rounds)
|
||
|
if _, err = w.Write(rounds); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// block 3.0
|
||
|
if buf, err = internal.ReadSizeBytes(w, true); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Rounds returns the number of rounds used in derivation.
|
||
|
func (k *KDF) Rounds() (rounds uint32) {
|
||
|
|
||
|
rounds = k.rounds
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Salt returns the salt bytes.
|
||
|
func (k *KDF) Salt() (salt []byte) {
|
||
|
|
||
|
salt = k.salt
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
AutoOK returns true if a kdf.UnpackKDF call was able to fetch the bcrypt.KDF options successfully, in which case the caller may use bcrypt.KDF.SetupAuto.
|
||
|
|
||
|
If false, it will need to be manually configured via bcrypt.KDF.Setup.
|
||
|
*/
|
||
|
func (k *KDF) AutoOK() (ok bool) {
|
||
|
|
||
|
ok = true
|
||
|
|
||
|
if k.salt == nil || len(k.salt) == 0 {
|
||
|
ok = false
|
||
|
}
|
||
|
|
||
|
if k.rounds == 0 {
|
||
|
ok = false
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// IsPlain indicates if this kdf.KDF actually does derivation (false) or not (true).
|
||
|
func (k *KDF) IsPlain() (plain bool) {
|
||
|
|
||
|
plain = false
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
AddSalt adds a salt as parsed from kdf.UnpackKDF.
|
||
|
|
||
|
If a salt has already been set, a no-op will be performed.
|
||
|
If you want to use a different salt, you will need to create a new bcrypt.KDF.
|
||
|
*/
|
||
|
func (k *KDF) AddSalt(salt []byte) (err error) {
|
||
|
|
||
|
if salt == nil || len(salt) == 0 {
|
||
|
err = errs.ErrNoSalt
|
||
|
return
|
||
|
}
|
||
|
if k.hasSalt {
|
||
|
return
|
||
|
}
|
||
|
k.salt = salt
|
||
|
k.hasSalt = true
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
AddRounds adds the rounds as parsed from kdf.UnpackKDF.
|
||
|
|
||
|
If the number of rounds has already been set, a no-op will be performed.
|
||
|
If you want to use a different number of rounds, you will need to create a new bcrypt.KDF.
|
||
|
*/
|
||
|
func (k *KDF) AddRounds(rounds uint32) (err error) {
|
||
|
|
||
|
if rounds == 0 {
|
||
|
err = errs.ErrNoRounds
|
||
|
return
|
||
|
}
|
||
|
if k.hasRounds {
|
||
|
return
|
||
|
}
|
||
|
k.rounds = rounds
|
||
|
k.hasRounds = true
|
||
|
|
||
|
return
|
||
|
}
|