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 }