xs/vendor/gopkg.in/hlandau/passlib.v1/hash/bcryptsha256/bcryptsha256.go

97 lines
2.5 KiB
Go
Raw Permalink Normal View History

// Package bcryptsha256 implements bcrypt with a SHA256 prehash in a format that is compatible with Python passlib's equivalent bcrypt-sha256 scheme.
//
// This is preferred over bcrypt because the prehash essentially renders bcrypt's password length
// limitation irrelevant; although of course it is less compatible.
package bcryptsha256
import "gopkg.in/hlandau/passlib.v1/abstract"
import "gopkg.in/hlandau/passlib.v1/hash/bcrypt"
import "encoding/base64"
import "crypto/sha256"
import "strings"
import "fmt"
type scheme struct {
underlying abstract.Scheme
cost int
}
// An implementation of Scheme implementing Python passlib's `$bcrypt-sha256$`
// bcrypt variant. This is bcrypt with a SHA256 prehash, which removes bcrypt's
// password length limitation.
var Crypter abstract.Scheme
// The recommended cost for bcrypt-sha256. This may change with subsequent releases.
const RecommendedCost = bcrypt.RecommendedCost
func init() {
Crypter = New(bcrypt.RecommendedCost)
}
// Instantiates a new Scheme implementing bcrypt with the given cost.
//
// The recommended cost is RecommendedCost.
func New(cost int) abstract.Scheme {
return &scheme{
underlying: bcrypt.New(cost),
cost: cost,
}
}
func (s *scheme) Hash(password string) (string, error) {
p := s.prehash(password)
h, err := s.underlying.Hash(p)
if err != nil {
return "", err
}
return mangle(h), nil
}
func (s *scheme) Verify(password, hash string) error {
p := s.prehash(password)
return s.underlying.Verify(p, demangle(hash))
}
func (s *scheme) prehash(password string) string {
h := sha256.New()
h.Write([]byte(password))
v := base64.StdEncoding.EncodeToString(h.Sum(nil))
return v
}
func (s *scheme) SupportsStub(stub string) bool {
return strings.HasPrefix(stub, "$bcrypt-sha256$") && s.underlying.SupportsStub(demangle(stub))
}
func (s *scheme) NeedsUpdate(stub string) bool {
return s.underlying.NeedsUpdate(demangle(stub))
}
func (s *scheme) String() string {
return fmt.Sprintf("bcrypt-sha256(%d)", s.cost)
}
func demangle(stub string) string {
if strings.HasPrefix(stub, "$bcrypt-sha256$2") {
parts := strings.Split(stub[15:], "$")
// 0: 2a,12
// 1: salt
// 2: hash
parts0 := strings.Split(parts[0], ",")
return "$" + parts0[0] + "$" + fmt.Sprintf("%02s", parts0[1]) + "$" + parts[1] + parts[2]
} else {
return stub
}
}
func mangle(hash string) string {
parts := strings.Split(hash[1:], "$")
// 0: 2a
// 1: rounds
// 2: salt + hash
salt := parts[2][0:22]
h := parts[2][22:]
return "$bcrypt-sha256$" + parts[0] + "," + parts[1] + "$" + salt + "$" + h
}