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 string = 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 []byte = make([]byte, 4) var packer *bytes.Reader var w *bytes.Buffer = 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 }