go_sshkeys/kdf/bcrypt/funcs.go

247 lines
4.3 KiB
Go
Raw Permalink Normal View History

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
}