go_sshkeys/kdf/funcs_bcrypt_pbkdf.go

227 lines
4.0 KiB
Go

package kdf
import (
"bytes"
"crypto/rand"
"encoding/binary"
bcryptPbkdf "github.com/dchest/bcrypt_pbkdf"
"r00t2.io/sshkeys/internal"
)
/*
Setup must be called before DeriveKey. It configures a BcryptPbkdf.
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 BcryptPbkdfDefaultSaltLen length.
If rounds is 0, BcryptPbkdfDefaultRounds will be used.
If keyLen is 0, BcryptPbkdfDefaultKeyLen will be used.
*/
func (b *BcryptPbkdf) Setup(secret, salt []byte, rounds, keyLen uint32) (err error) {
if secret == nil || len(secret) == 0 {
err = ErrNoSecret
return
}
if salt == nil || len(salt) == 0 {
salt = make([]byte, BcryptPbkdfDefaultSaltLen)
if _, err = rand.Read(salt); err != nil {
return
}
}
if rounds == 0 {
rounds = BcryptPbkdfDefaultRounds
}
if keyLen == 0 {
keyLen = BcryptPbkdfDefaultKeyLen
}
b.secret = secret
b.salt = salt
b.rounds = rounds
b.keyLen = keyLen
return
}
/*
SetupAuto is used to provide out-of-band configuration if the KDF options were found via GetKdfFromBytes.
You can test this by running KDF.AutoOK.
*/
func (b *BcryptPbkdf) SetupAuto(secret []byte, keyLen uint32) (err error) {
if !b.AutoOK() {
err = ErrUnknownKdf
return
}
if keyLen == 0 {
keyLen = BcryptPbkdfDefaultKeyLen
}
b.secret = secret
b.keyLen = keyLen
return
}
/*
DeriveKey returns the derived key from a BcryptPbkdf.
It must be called *after* Setup.
*/
func (b *BcryptPbkdf) DeriveKey() (key []byte, err error) {
if b.secret == nil {
err = ErrNoSecret
return
}
if b.salt == nil {
err = ErrNoSalt
return
}
if b.rounds == 0 {
err = ErrNoRounds
return
}
if b.keyLen == 0 {
err = ErrNoKeyLen
}
if b.key, err = bcryptPbkdf.Key(b.secret, b.salt, int(b.rounds), int(b.keyLen)); err != nil {
return
}
key = b.key
return
}
// Name returns BcryptPbkdfName.
func (b *BcryptPbkdf) Name() (name string) {
name = BcryptPbkdfName
return
}
// NameBytes returns the byte form of BcryptPbkdf.Name with leading bytecount allocator.
func (b *BcryptPbkdf) NameBytes() (name []byte) {
var nb []byte
var s = b.Name()
nb = []byte(s)
name = make([]byte, 4)
binary.BigEndian.PutUint32(name, uint32(len(nb)))
name = append(name, nb...)
return
}
// PackedBytes returns 3.0 and recursed.
func (b *BcryptPbkdf) PackedBytes() (buf *bytes.Reader, err error) {
var rounds = make([]byte, 4)
var packer *bytes.Reader
var w = new(bytes.Buffer)
// 3.0.0.0 and 3.0.0.0.0
if packer, err = internal.ReadSizeBytes(b.salt, true); err != nil {
return
}
if _, err = packer.WriteTo(w); err != nil {
return
}
// 3.0.0.1
binary.BigEndian.PutUint32(rounds, b.rounds)
if _, err = w.Write(rounds); err != nil {
return
}
// 3.0
if buf, err = internal.ReadSizeBytes(w, true); err != nil {
return
}
return
}
// Rounds returns the number of rounds used in derivation.
func (b *BcryptPbkdf) Rounds() (rounds uint32) {
rounds = b.rounds
return
}
// Salt returns the salt bytes.
func (b *BcryptPbkdf) Salt() (salt []byte) {
salt = b.salt
return
}
/*
AutoOK returns true if a GetKdfFromBytes call was able to fetch the KDF options successfully, in which case the caller may use KDF.SetupAuto.
If false, it will need to be manually configured via KDF.Setup.
*/
func (b *BcryptPbkdf) AutoOK() (ok bool) {
ok = true
if b.salt == nil || len(b.salt) == 0 {
ok = false
}
if b.rounds == 0 {
ok = false
}
return
}
// IsPlain indicates if this KDF actually does derivation (false) or not (true).
func (b *BcryptPbkdf) IsPlain() (plain bool) {
plain = false
return
}
// addSalt adds a salt as parsed from GetKdfFromBytes.
func (b *BcryptPbkdf) addSalt(salt []byte) (err error) {
if salt == nil || len(salt) == 0 {
err = ErrNoSalt
return
}
b.salt = salt
return
}
// addRounds adds the rounds as parsed from GetKdfFromBytes
func (b *BcryptPbkdf) addRounds(rounds uint32) (err error) {
if rounds == 0 {
err = ErrNoRounds
return
}
b.rounds = rounds
return
}