mirror of https://gogs.blitter.com/RLabs/xs
95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
|
// Package pbkdf2 implements a modular crypt format for PBKDF2-SHA1,
|
||
|
// PBKDF2-SHA256 and PBKDF-SHA512.
|
||
|
//
|
||
|
// The format is the same as that used by Python's passlib and is compatible.
|
||
|
package pbkdf2
|
||
|
|
||
|
import (
|
||
|
"crypto/rand"
|
||
|
"crypto/sha1"
|
||
|
"crypto/sha256"
|
||
|
"crypto/sha512"
|
||
|
"fmt"
|
||
|
"gopkg.in/hlandau/passlib.v1/abstract"
|
||
|
"gopkg.in/hlandau/passlib.v1/hash/pbkdf2/raw"
|
||
|
"hash"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// An implementation of Scheme implementing a number of PBKDF2 modular crypt
|
||
|
// formats used by Python's passlib ($pbkdf2$, $pbkdf2-sha256$,
|
||
|
// $pbkdf2-sha512$).
|
||
|
//
|
||
|
// Uses RecommendedRounds.
|
||
|
//
|
||
|
// WARNING: SHA1 should not be used for new applications under any
|
||
|
// circumstances. It should be used for legacy compatibility only.
|
||
|
var SHA1Crypter abstract.Scheme
|
||
|
var SHA256Crypter abstract.Scheme
|
||
|
var SHA512Crypter abstract.Scheme
|
||
|
|
||
|
const (
|
||
|
RecommendedRoundsSHA1 = 131000
|
||
|
RecommendedRoundsSHA256 = 29000
|
||
|
RecommendedRoundsSHA512 = 25000
|
||
|
)
|
||
|
|
||
|
const SaltLength = 16
|
||
|
|
||
|
func init() {
|
||
|
SHA1Crypter = New("$pbkdf2$", sha1.New, RecommendedRoundsSHA1)
|
||
|
SHA256Crypter = New("$pbkdf2-sha256$", sha256.New, RecommendedRoundsSHA256)
|
||
|
SHA512Crypter = New("$pbkdf2-sha512$", sha512.New, RecommendedRoundsSHA512)
|
||
|
}
|
||
|
|
||
|
type scheme struct {
|
||
|
Ident string
|
||
|
HashFunc func() hash.Hash
|
||
|
Rounds int
|
||
|
}
|
||
|
|
||
|
func New(ident string, hf func() hash.Hash, rounds int) abstract.Scheme {
|
||
|
return &scheme{
|
||
|
Ident: ident,
|
||
|
HashFunc: hf,
|
||
|
Rounds: rounds,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (s *scheme) Hash(password string) (string, error) {
|
||
|
salt := make([]byte, SaltLength)
|
||
|
_, err := rand.Read(salt)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
hash := raw.Hash([]byte(password), salt, s.Rounds, s.HashFunc)
|
||
|
|
||
|
newHash := fmt.Sprintf("%s%d$%s$%s", s.Ident, s.Rounds, raw.Base64Encode(salt), hash)
|
||
|
return newHash, nil
|
||
|
}
|
||
|
|
||
|
func (s *scheme) Verify(password, stub string) (err error) {
|
||
|
_, rounds, salt, oldHash, err := raw.Parse(stub)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
newHash := raw.Hash([]byte(password), salt, rounds, s.HashFunc)
|
||
|
|
||
|
if len(newHash) == 0 || !abstract.SecureCompare(oldHash, newHash) {
|
||
|
err = abstract.ErrInvalidPassword
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (s *scheme) SupportsStub(stub string) bool {
|
||
|
return strings.HasPrefix(stub, s.Ident)
|
||
|
}
|
||
|
|
||
|
func (s *scheme) NeedsUpdate(stub string) bool {
|
||
|
_, rounds, salt, _, err := raw.Parse(stub)
|
||
|
return err == raw.ErrInvalidRounds || rounds < s.Rounds || len(salt) < SaltLength
|
||
|
}
|