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

148 lines
3.5 KiB
Go

// Package sha2crypt implements sha256-crypt and sha512-crypt.
package sha2crypt
import "fmt"
import "expvar"
import "crypto/rand"
import "gopkg.in/hlandau/passlib.v1/hash/sha2crypt/raw"
import "gopkg.in/hlandau/passlib.v1/abstract"
var cSHA2CryptHashCalls = expvar.NewInt("passlib.sha2crypt.hashCalls")
var cSHA2CryptVerifyCalls = expvar.NewInt("passlib.sha2crypt.verifyCalls")
// An implementation of Scheme performing sha256-crypt.
//
// The number of rounds is raw.RecommendedRounds.
var Crypter256 abstract.Scheme
// An implementation of Scheme performing sha512-crypt.
//
// The number of rounds is raw.RecommendedRounds.
var Crypter512 abstract.Scheme
func init() {
Crypter256 = NewCrypter256(raw.RecommendedRounds)
Crypter512 = NewCrypter512(raw.RecommendedRounds)
}
// Returns a Scheme implementing sha256-crypt using the number of rounds
// specified.
func NewCrypter256(rounds int) abstract.Scheme {
return &sha2Crypter{false, rounds}
}
// Returns a Scheme implementing sha512-crypt using the number of rounds
// specified.
func NewCrypter512(rounds int) abstract.Scheme {
return &sha2Crypter{true, rounds}
}
type sha2Crypter struct {
sha512 bool
rounds int
}
// Changes the default rounds for the crypter. Be warned that this
// is a global setting. The default default value is RecommendedRounds.
func (c *sha2Crypter) SetRounds(rounds int) error {
if rounds < raw.MinimumRounds || rounds > raw.MaximumRounds {
return raw.ErrInvalidRounds
}
c.rounds = rounds
return nil
}
func (c *sha2Crypter) SupportsStub(stub string) bool {
if len(stub) < 3 || stub[0] != '$' || stub[2] != '$' {
return false
}
return (stub[1] == '5' && !c.sha512) || (stub[1] == '6' && c.sha512)
}
func (c *sha2Crypter) Hash(password string) (string, error) {
cSHA2CryptHashCalls.Add(1)
stub, err := c.makeStub()
if err != nil {
return "", err
}
_, newHash, _, _, err := c.hash(password, stub)
return newHash, err
}
func (c *sha2Crypter) Verify(password, hash string) (err error) {
cSHA2CryptVerifyCalls.Add(1)
_, newHash, _, _, err := c.hash(password, hash)
if err == nil && !abstract.SecureCompare(hash, newHash) {
err = abstract.ErrInvalidPassword
}
return
}
func (c *sha2Crypter) NeedsUpdate(stub string) bool {
_, salt, _, rounds, err := raw.Parse(stub)
if err != nil {
return false // ...
}
return c.needsUpdate(salt, rounds)
}
func (c *sha2Crypter) needsUpdate(salt string, rounds int) bool {
return rounds < c.rounds || len(salt) < 16
}
var errInvalidStub = fmt.Errorf("invalid sha2 password stub")
func (c *sha2Crypter) hash(password, stub string) (oldHash, newHash, salt string, rounds int, err error) {
isSHA512, salt, oldHash, rounds, err := raw.Parse(stub)
if err != nil {
return "", "", "", 0, err
}
if isSHA512 != c.sha512 {
return "", "", "", 0, errInvalidStub
}
if c.sha512 {
return oldHash, raw.Crypt512(password, salt, rounds), salt, rounds, nil
}
return oldHash, raw.Crypt256(password, salt, rounds), salt, rounds, nil
}
func (c *sha2Crypter) makeStub() (string, error) {
ch := "5"
if c.sha512 {
ch = "6"
}
buf := make([]byte, 12)
_, err := rand.Read(buf)
if err != nil {
return "", err
}
salt := raw.EncodeBase64(buf)[0:16]
if c.rounds == raw.DefaultRounds {
return fmt.Sprintf("$%s$%s", ch, salt), nil
}
return fmt.Sprintf("$%s$rounds=%d$%s", ch, c.rounds, salt), nil
}
func (c *sha2Crypter) String() string {
if c.sha512 {
return fmt.Sprintf("sha512-crypt(%d)", c.rounds)
} else {
return fmt.Sprintf("sha256-crypt(%d)", c.rounds)
}
}
// © 2014 Hugo Landau <hlandau@devever.net> BSD License