Added vendor/ dir to remove dependencies on gopkg.in (down Aug 12 2022)

This commit is contained in:
Russ Magee 2022-08-12 21:30:05 -07:00
parent f24d4d8fdb
commit 7a4560762d
913 changed files with 317350 additions and 0 deletions

39
vendor/gopkg.in/hlandau/passlib.v1/COPYING generated vendored Normal file
View file

@ -0,0 +1,39 @@
passlib is a Golang password verification library strongly inspired by and
derived from Python passlib (<https://pypi.python.org/pypi/passlib>). The BSD
license is preserved and extended to all new code.
License for Passlib
===================
Passlib is (c) `Assurance Technologies <http://www.assurancetechnologies.com>`_,
and is released under the `BSD license <http://www.opensource.org/licenses/bsd-license.php>`_::
Passlib
Copyright (c) 2008-2012 Assurance Technologies, LLC.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Assurance Technologies, nor the names of the
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

11
vendor/gopkg.in/hlandau/passlib.v1/abstract/compare.go generated vendored Normal file
View file

@ -0,0 +1,11 @@
package abstract
import "crypto/subtle"
// Compares two strings (typicaly password hashes) in a secure, constant-time
// fashion. Returns true iff they are equal.
func SecureCompare(a, b string) bool {
ab := []byte(a)
bb := []byte(b)
return subtle.ConstantTimeCompare(ab, bb) == 1
}

15
vendor/gopkg.in/hlandau/passlib.v1/abstract/errors.go generated vendored Normal file
View file

@ -0,0 +1,15 @@
// Package abstract contains the abstract description of the Scheme interface,
// plus supporting error definitions.
package abstract
import "fmt"
// Indicates that password verification failed because the provided password
// does not match the provided hash.
var ErrInvalidPassword = fmt.Errorf("invalid password")
// Indicates that password verification is not possible because the hashing
// scheme used by the hash provided is not supported.
var ErrUnsupportedScheme = fmt.Errorf("unsupported scheme")
// © 2014 Hugo Landau <hlandau@devever.net> MIT License

34
vendor/gopkg.in/hlandau/passlib.v1/abstract/scheme.go generated vendored Normal file
View file

@ -0,0 +1,34 @@
package abstract
// The Scheme interface provides an abstract interface to an implementation
// of a particular password hashing scheme. The Scheme generates password
// hashes from passwords, verifies passwords using password hashes, randomly
// generates new stubs and can determines whether it recognises a given
// stub or hash. It may also decide to issue upgrades.
type Scheme interface {
// Hashes a plaintext UTF-8 password using a modular crypt stub. Returns the
// hashed password in modular crypt format.
//
// A modular crypt stub is a prefix of a hash in modular crypt format which
// expresses all necessary configuration information, such as salt and
// iteration count. For example, for sha256-crypt, a valid stub would be:
//
// $5$rounds=6000$salt
//
// A full modular crypt hash may also be passed as the stub, in which case
// the hash is ignored.
Hash(password string) (string, error)
// Verifies a plaintext UTF-8 password using a modular crypt hash. Returns
// an error if the inputs are malformed or the password does not match.
Verify(password, hash string) (err error)
// Returns true iff this crypter supports the given stub.
SupportsStub(stub string) bool
// Returns true iff this stub needs an update.
NeedsUpdate(stub string) bool
// Make a stub with the configured defaults. The salt is generated randomly.
//MakeStub() (string, error)
}

141
vendor/gopkg.in/hlandau/passlib.v1/default.go generated vendored Normal file
View file

@ -0,0 +1,141 @@
package passlib
import (
"fmt"
"gopkg.in/hlandau/passlib.v1/abstract"
"gopkg.in/hlandau/passlib.v1/hash/argon2"
"gopkg.in/hlandau/passlib.v1/hash/bcrypt"
"gopkg.in/hlandau/passlib.v1/hash/bcryptsha256"
"gopkg.in/hlandau/passlib.v1/hash/pbkdf2"
"gopkg.in/hlandau/passlib.v1/hash/scrypt"
"gopkg.in/hlandau/passlib.v1/hash/sha2crypt"
"time"
)
// This is the first and default set of defaults used by passlib. It prefers
// scrypt-sha256. It is now obsolete.
const Defaults20160922 = "20160922"
// This is the most up-to-date set of defaults preferred by passlib. It prefers
// Argon2i. You must opt into it by calling UseDefaults at startup.
const Defaults20180601 = "20180601"
// This value, when passed to UseDefaults, causes passlib to always use the
// very latest set of defaults. DO NOT use this unless you are sure that
// opportunistic hash upgrades will not cause breakage for your application
// when future versions of passlib are released. See func UseDefaults.
const DefaultsLatest = "latest"
// Default schemes as of 2016-09-22.
var defaultSchemes20160922 = []abstract.Scheme{
scrypt.SHA256Crypter,
argon2.Crypter,
sha2crypt.Crypter512,
sha2crypt.Crypter256,
bcryptsha256.Crypter,
pbkdf2.SHA512Crypter,
pbkdf2.SHA256Crypter,
bcrypt.Crypter,
pbkdf2.SHA1Crypter,
}
// Default schemes as of 2018-06-01.
var defaultSchemes20180601 = []abstract.Scheme{
argon2.Crypter,
scrypt.SHA256Crypter,
sha2crypt.Crypter512,
sha2crypt.Crypter256,
bcryptsha256.Crypter,
pbkdf2.SHA512Crypter,
pbkdf2.SHA256Crypter,
bcrypt.Crypter,
pbkdf2.SHA1Crypter,
}
// The default schemes, most preferred first. The first scheme will be used to
// hash passwords, and any of the schemes may be used to verify existing
// passwords. The contents of this value may change with subsequent releases.
//
// If you want to change this, set DefaultSchemes to a slice to an
// abstract.Scheme array of your own construction, rather than mutating the
// array the slice points to.
//
// To see the default schemes used in the current release of passlib, see
// default.go. See also the UseDefaults function for more information on how
// the list of default schemes is determined. The default value of
// DefaultSchemes (the default defaults) won't change; you need to call
// UseDefaults to allow your application to upgrade to newer hashing schemes
// (or set DefaultSchemes manually, or create a custom context with its own
// schemes set).
var DefaultSchemes []abstract.Scheme
func init() {
DefaultSchemes = defaultSchemes20160922
}
// It is strongly recommended that you call this function like this before using passlib:
//
// passlib.UseDefaults("YYYYMMDD")
//
// where YYYYMMDD is a date. This will be used to select the preferred scheme
// to use. If you do not call UseDefaults, the preferred scheme (the first item
// in the default schemes list) current as of 2016-09-22 will always be used,
// meaning that upgrade will not occur even though better schemes are now
// available.
//
// Note that even if you don't call this function, new schemes will still be
// added to DefaultSchemes over time as non-initial values (items not at index
// 0), so servers will always, by default, be able to validate all schemes
// which passlib supports at any given time.
//
// The reason you must call this function is as follows: If passlib is deployed
// as part of a web application in a multi-server deployment, and passlib is
// updated, and the new version of that application with the updated passlib is
// deployed, that upgrade process is unlikely to be instantaneous. Old versions
// of the web application may continue to run on some servers. If merely
// upgrading passlib caused password hashes to be upgraded to the newer scheme
// on login, the older daemons may not be able to validate these passwords and
// users may have issues logging in. Although this can be ameliorated to some
// extent by introducing a new scheme to passlib, waiting some months, and only
// then making this the default, this could still cause issued if passlib is
// only updated very occasionally.
//
// Thus, you should update your call to UseDefaults only when all servers have
// been upgraded, and it is thus guaranteed that they will all be able to
// verify the new scheme. Making this value loadable from a configuration file
// is recommended.
//
// If you are using a single-server configuration, you can use the special
// value "latest" here (or, equivalently, a date far into the future), which
// will always use the most preferred scheme. This is hazardous in a
// multi-server environment.
//
// The constants beginning 'Defaults' in this package document dates
// which are meaningful to this function. The constant values they are equal to
// will never change, so there is no need to use them instead of string
// literals, although you may if you wish; they are intended mainly as
// documentation as to the significance of various dates.
//
// Example for opting in to the latest set of defaults:
//
// passlib.UseDefaults(passlib.Defaults20180601)
//
func UseDefaults(date string) error {
if date == "latest" {
DefaultSchemes = defaultSchemes20180601
return nil
}
t, err := time.ParseInLocation("20060102", date, time.UTC)
if err != nil {
return fmt.Errorf("invalid time string passed to passlib.UseDefaults: %q", date)
}
if !t.Before(time.Date(2016, 9, 22, 0, 0, 0, 0, time.UTC)) {
DefaultSchemes = defaultSchemes20180601
return nil
}
DefaultSchemes = defaultSchemes20160922
return nil
}

View file

@ -0,0 +1,115 @@
// Package argon2 implements the argon2 password hashing mechanism, wrapped in
// the argon2 encoded format.
package argon2
import (
"crypto/rand"
"encoding/base64"
"fmt"
"strings"
"golang.org/x/crypto/argon2"
"gopkg.in/hlandau/passlib.v1/abstract"
"gopkg.in/hlandau/passlib.v1/hash/argon2/raw"
)
// An implementation of Scheme performing argon2 hashing.
//
// Uses the recommended values for time, memory and threads defined in raw.
var Crypter abstract.Scheme
const saltLength = 16
func init() {
Crypter = New(
raw.RecommendedTime,
raw.RecommendedMemory,
raw.RecommendedThreads,
)
}
// Returns an implementation of Scheme implementing argon2
// with the specified parameters.
func New(time, memory uint32, threads uint8) abstract.Scheme {
return &scheme{
time: time,
memory: memory,
threads: threads,
}
}
type scheme struct {
time, memory uint32
threads uint8
}
func (c *scheme) SetParams(time, memory uint32, threads uint8) error {
c.time = time
c.memory = memory
c.threads = threads
return nil
}
func (c *scheme) SupportsStub(stub string) bool {
return strings.HasPrefix(stub, "$argon2i$")
}
func (c *scheme) Hash(password string) (string, error) {
stub, err := c.makeStub()
if err != nil {
return "", err
}
_, newHash, _, _, _, _, _, err := c.hash(password, stub)
return newHash, err
}
func (c *scheme) Verify(password, hash string) (err error) {
_, newHash, _, _, _, _, _, err := c.hash(password, hash)
if err == nil && !abstract.SecureCompare(hash, newHash) {
err = abstract.ErrInvalidPassword
}
return
}
func (c *scheme) NeedsUpdate(stub string) bool {
salt, _, version, time, memory, threads, err := raw.Parse(stub)
if err != nil {
return false // ...
}
return c.needsUpdate(salt, version, time, memory, threads)
}
func (c *scheme) needsUpdate(salt []byte, version int, time, memory uint32, threads uint8) bool {
return len(salt) < saltLength || version < argon2.Version || time < c.time || memory < c.memory || threads < c.threads
}
func (c *scheme) hash(password, stub string) (oldHashRaw []byte, newHash string, salt []byte, version int, memory, time uint32, threads uint8, err error) {
salt, oldHashRaw, version, time, memory, threads, err = raw.Parse(stub)
if err != nil {
return
}
return oldHashRaw, raw.Argon2(password, salt, time, memory, threads), salt, version, memory, time, threads, nil
}
func (c *scheme) makeStub() (string, error) {
buf := make([]byte, saltLength)
_, err := rand.Read(buf)
if err != nil {
return "", err
}
salt := base64.RawStdEncoding.EncodeToString(buf)
return fmt.Sprintf("$argon2i$v=%d$m=%d,t=%d,p=%d$%s$", argon2.Version, c.memory, c.time, c.threads, salt), nil
}
func (c *scheme) String() string {
return fmt.Sprintf("argon2(%d,%d,%d,%d)", argon2.Version, c.memory, c.time, c.threads)
}

View file

@ -0,0 +1,186 @@
// Package raw provides a raw implementation of the modular-crypt-wrapped Argon2i primitive.
package raw
import (
"encoding/base64"
"fmt"
"golang.org/x/crypto/argon2"
"strconv"
"strings"
)
// The current recommended time value for interactive logins.
const RecommendedTime uint32 = 4
// The current recommended memory for interactive logins.
const RecommendedMemory uint32 = 32 * 1024
// The current recommended number of threads for interactive logins.
const RecommendedThreads uint8 = 4
// Wrapper for golang.org/x/crypto/argon2 implementing a sensible
// hashing interface.
//
// password should be a UTF-8 plaintext password.
// salt should be a random salt value in binary form.
//
// Time, memory, and threads are parameters to argon2.
//
// Returns an argon2 encoded hash.
func Argon2(password string, salt []byte, time, memory uint32, threads uint8) string {
passwordb := []byte(password)
hash := argon2.Key(passwordb, salt, time, memory, threads, 32)
hstr := base64.RawStdEncoding.EncodeToString(hash)
sstr := base64.RawStdEncoding.EncodeToString(salt)
return fmt.Sprintf("$argon2i$v=%d$m=%d,t=%d,p=%d$%s$%s", argon2.Version, memory, time, threads, sstr, hstr)
}
// Indicates that a password hash or stub is invalid.
var ErrInvalidStub = fmt.Errorf("invalid argon2 password stub")
// Indicates that a key-value pair in the configuration part is malformed.
var ErrInvalidKeyValuePair = fmt.Errorf("invalid argon2 key-value pair")
// Indicates that the version part had the wrong number of parameters.
var ErrParseVersion = fmt.Errorf("version section has wrong number of parameters")
// Indicates that the hash config part had the wrong number of parameters.
var ErrParseConfig = fmt.Errorf("hash config section has wrong number of parameters")
// Indicates that the version parameter ("v") was missing in the version part,
// even though it is required.
var ErrMissingVersion = fmt.Errorf("version parameter (v) is missing")
// Indicates that the memory parameter ("m") was mossing in the hash config
// part, even though it is required.
var ErrMissingMemory = fmt.Errorf("memory parameter (m) is missing")
// Indicates that the time parameter ("t") was mossing in the hash config part,
// even though it is required.
var ErrMissingTime = fmt.Errorf("time parameter (t) is missing")
// Indicates that the parallelism parameter ("p") was mossing in the hash config
// part, even though it is required.
var ErrMissingParallelism = fmt.Errorf("parallelism parameter (p) is missing")
// Parses an argon2 encoded hash.
//
// The format is as follows:
//
// $argon2i$v=version$m=memory,t=time,p=threads$salt$hash // hash
// $argon2i$v=version$m=memory,t=time,p=threads$salt // stub
//
func Parse(stub string) (salt, hash []byte, version int, time, memory uint32, parallelism uint8, err error) {
if len(stub) < 26 || !strings.HasPrefix(stub, "$argon2i$") {
err = ErrInvalidStub
return
}
// $argon2i$ v=version$m=memory,t=time,p=threads$salt-base64$hash-base64
parts := strings.Split(stub[9:], "$")
// version-params$hash-config-params$salt[$hash]
if len(parts) < 3 || len(parts) > 4 {
err = ErrInvalidStub
return
}
// Parse the first configuration part, the version parameters.
versionParams, err := parseKeyValuePair(parts[0])
if err != nil {
return
}
// Must be exactly one parameter in the version part.
if len(versionParams) != 1 {
err = ErrParseVersion
return
}
// It must be "v".
val, ok := versionParams["v"]
if !ok {
err = ErrMissingVersion
return
}
version = int(val)
// Parse the second configuration part, the hash config parameters.
hashParams, err := parseKeyValuePair(parts[1])
if err != nil {
return
}
// It must have exactly three parameters.
if len(hashParams) != 3 {
err = ErrParseConfig
return
}
// Memory parameter.
val, ok = hashParams["m"]
if !ok {
err = ErrMissingMemory
return
}
memory = uint32(val)
// Time parameter.
val, ok = hashParams["t"]
if !ok {
err = ErrMissingTime
return
}
time = uint32(val)
// Parallelism parameter.
val, ok = hashParams["p"]
if !ok {
err = ErrMissingParallelism
return
}
parallelism = uint8(val)
// Decode salt.
salt, err = base64.RawStdEncoding.DecodeString(parts[2])
if err != nil {
return
}
// Decode hash if present.
if len(parts) >= 4 {
hash, err = base64.RawStdEncoding.DecodeString(parts[3])
}
return
}
func parseKeyValuePair(pairs string) (result map[string]uint64, err error) {
result = map[string]uint64{}
parameterParts := strings.Split(pairs, ",")
for _, parameter := range parameterParts {
parts := strings.SplitN(parameter, "=", 2)
if len(parts) != 2 {
err = ErrInvalidKeyValuePair
return
}
parsedi, err := strconv.ParseUint(parts[1], 10, 32)
if err != nil {
return result, err
}
result[parts[0]] = parsedi
}
return result, nil
}

View file

@ -0,0 +1,72 @@
// Package bcrypt implements the bcrypt password hashing mechanism.
//
// Please note that bcrypt truncates passwords to 72 characters in length. Consider using
// a more modern hashing scheme such as scrypt or sha-crypt. If you must use bcrypt,
// consider using bcrypt-sha256 instead.
package bcrypt
import "golang.org/x/crypto/bcrypt"
import "gopkg.in/hlandau/passlib.v1/abstract"
import "fmt"
// An implementation of Scheme implementing bcrypt.
//
// Uses RecommendedCost.
var Crypter abstract.Scheme
// The recommended cost for bcrypt. This may change with subsequent releases.
const RecommendedCost = 12
// bcrypt.DefaultCost is a bit low (10), so use 12 instead.
func init() {
Crypter = New(RecommendedCost)
}
// Create a new scheme implementing bcrypt. The recommended cost is RecommendedCost.
func New(cost int) abstract.Scheme {
return &scheme{
Cost: cost,
}
}
type scheme struct {
Cost int
}
func (s *scheme) SupportsStub(stub string) bool {
return len(stub) >= 3 && stub[0] == '$' && stub[1] == '2' &&
(stub[2] == '$' || (len(stub) >= 4 && stub[3] == '$' &&
(stub[2] == 'a' || stub[2] == 'b' || stub[2] == 'y')))
}
func (s *scheme) Hash(password string) (string, error) {
h, err := bcrypt.GenerateFromPassword([]byte(password), s.Cost)
if err != nil {
return "", err
}
return string(h), nil
}
func (s *scheme) Verify(password, hash string) error {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
if err == bcrypt.ErrMismatchedHashAndPassword {
err = abstract.ErrInvalidPassword
}
return err
}
func (s *scheme) NeedsUpdate(stub string) bool {
cost, err := bcrypt.Cost([]byte(stub))
if err != nil {
return false
}
return cost < s.Cost
}
func (s *scheme) String() string {
return fmt.Sprintf("bcrypt(%d)", s.Cost)
}

View file

@ -0,0 +1,96 @@
// 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
}

View file

@ -0,0 +1,94 @@
// 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
}

View file

@ -0,0 +1,20 @@
package raw
import (
"encoding/base64"
"strings"
)
var b64 = base64.RawStdEncoding
func Base64Encode(src []byte) (dst string) {
dst = b64.EncodeToString(src)
dst = strings.Replace(dst, "+", ".", -1)
return
}
func Base64Decode(src string) (dst []byte, err error) {
src = strings.Replace(src, ".", "+", -1)
dst, err = b64.DecodeString(src)
return
}

View file

@ -0,0 +1,62 @@
package raw
import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"strconv"
"strings"
)
// Indicates that a password hash or stub is invalid.
var ErrInvalidStub = fmt.Errorf("invalid stub")
// Indicates that the number of rounds specified is not in the valid range.
var ErrInvalidRounds = fmt.Errorf("invalid number of rounds")
var hashMap = map[string]func() hash.Hash{
"pbkdf2": sha1.New,
"pbkdf2-sha256": sha256.New,
"pbkdf2-sha512": sha512.New,
}
func Parse(stub string) (hashFunc func() hash.Hash, rounds int, salt []byte, hash string, err error) {
// does not start with $pbkdf2
if !strings.HasPrefix(stub, "$pbkdf2") {
err = ErrInvalidStub
return
}
parts := strings.Split(stub, "$")
if f, ok := hashMap[parts[1]]; ok {
hashFunc = f
} else {
err = ErrInvalidStub
return
}
roundsStr := parts[2]
var n uint64
n, err = strconv.ParseUint(roundsStr, 10, 31)
if err != nil {
err = ErrInvalidStub
return
}
rounds = int(n)
if rounds < MinRounds || rounds > MaxRounds {
err = ErrInvalidRounds
return
}
salt, err = Base64Decode(parts[3])
if err != nil {
err = fmt.Errorf("could not decode base64 salt")
return
}
hash = parts[4]
return
}

View file

@ -0,0 +1,15 @@
package raw
import (
"golang.org/x/crypto/pbkdf2"
"hash"
)
const (
MinRounds = 1
MaxRounds = 0x7fffffff // setting at 32-bit signed integer limit for now
)
func Hash(password, salt []byte, rounds int, hf func() hash.Hash) (hash string) {
return Base64Encode(pbkdf2.Key(password, salt, rounds, hf().Size(), hf))
}

30
vendor/gopkg.in/hlandau/passlib.v1/hash/pbkdf2/test.py generated vendored Normal file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env python3
import passlib.hash
import base64
def f(p):
h = passlib.hash.pbkdf2_sha256.hash(p)
print(' {"%s", "%s"},' % (p,h))
f('')
f('a')
f('ab')
f('abc')
f('abcd')
f('abcde')
f('abcdef')
f('abcdefg')
f('abcdefgh')
f('abcdefghi')
f('abcdefghij')
f('abcdefghijk')
f('abcdefghijkl')
f('abcdefghijklm')
f('abcdefghijklmn')
f('abcdefghijklmno')
f('abcdefghijklmnop')
f('qrstuvwxyz012345')
f('67890./')
f('ABCDEFGHIJKLMNOP')
f('QRSTUVWXYZ012345')
for i in range(70):
f(('password'*10)[0:i])

View file

@ -0,0 +1,95 @@
// Package raw provides a raw implementation of the modular-crypt-wrapped scrypt primitive.
package raw
import "golang.org/x/crypto/scrypt"
import "encoding/base64"
import "strings"
import "strconv"
import "fmt"
// The current recommended N value for interactive logins.
const RecommendedN = 16384
// The current recommended r value for interactive logins.
const Recommendedr = 8
// The current recommended p value for interactive logins.
const Recommendedp = 1
// Wrapper for golang.org/x/crypto/scrypt implementing a sensible
// modular crypt interface.
//
// password should be a UTF-8 plaintext password.
// salt should be a random salt value in binary form.
//
// N, r and p are parameters to scrypt.
//
// Returns a modular crypt hash.
func ScryptSHA256(password string, salt []byte, N, r, p int) string {
passwordb := []byte(password)
hash, err := scrypt.Key(passwordb, salt, N, r, p, 32)
if err != nil {
panic(err)
}
hstr := base64.StdEncoding.EncodeToString(hash)
sstr := base64.StdEncoding.EncodeToString(salt)
return fmt.Sprintf("$s2$%d$%d$%d$%s$%s", N, r, p, sstr, hstr)
}
// Indicates that a password hash or stub is invalid.
var ErrInvalidStub = fmt.Errorf("invalid scrypt password stub")
// Parses an scrypt modular hash or stub string.
//
// The format is as follows:
//
// $s2$N$r$p$salt$hash // hash
// $s2$N$r$p$salt // stub
//
func Parse(stub string) (salt, hash []byte, N, r, p int, err error) {
if len(stub) < 10 || !strings.HasPrefix(stub, "$s2$") {
err = ErrInvalidStub
return
}
// $s2$ N$r$p$salt-base64$hash-base64
parts := strings.Split(stub[4:], "$")
if len(parts) < 4 {
err = ErrInvalidStub
return
}
var Ni, ri, pi uint64
Ni, err = strconv.ParseUint(parts[0], 10, 31)
if err != nil {
return
}
ri, err = strconv.ParseUint(parts[1], 10, 31)
if err != nil {
return
}
pi, err = strconv.ParseUint(parts[2], 10, 31)
if err != nil {
return
}
N, r, p = int(Ni), int(ri), int(pi)
salt, err = base64.StdEncoding.DecodeString(parts[3])
if err != nil {
return
}
if len(parts) >= 5 {
hash, err = base64.StdEncoding.DecodeString(parts[4])
}
return
}

View file

@ -0,0 +1,113 @@
// Package scrypt implements the scrypt password hashing mechanism, wrapped in
// the modular crypt format.
package scrypt
import "fmt"
import "expvar"
import "strings"
import "crypto/rand"
import "encoding/base64"
import "gopkg.in/hlandau/passlib.v1/hash/scrypt/raw"
import "gopkg.in/hlandau/passlib.v1/abstract"
var cScryptSHA256HashCalls = expvar.NewInt("passlib.scryptsha256.hashCalls")
var cScryptSHA256VerifyCalls = expvar.NewInt("passlib.scryptsha256.verifyCalls")
// An implementation of Scheme performing scrypt-sha256.
//
// Uses the recommended values for N,r,p defined in raw.
var SHA256Crypter abstract.Scheme
func init() {
SHA256Crypter = NewSHA256(
raw.RecommendedN,
raw.Recommendedr,
raw.Recommendedp,
)
}
// Returns an implementation of Scheme implementing scrypt-sha256
// with the specified parameters.
func NewSHA256(N, r, p int) abstract.Scheme {
return &scryptSHA256Crypter{
nN: N,
r: r,
p: p,
}
}
type scryptSHA256Crypter struct {
nN, r, p int
}
func (c *scryptSHA256Crypter) SetParams(N, r, p int) error {
c.nN = N
c.r = r
c.p = p
return nil
}
func (c *scryptSHA256Crypter) SupportsStub(stub string) bool {
return strings.HasPrefix(stub, "$s2$")
}
func (c *scryptSHA256Crypter) Hash(password string) (string, error) {
cScryptSHA256HashCalls.Add(1)
stub, err := c.makeStub()
if err != nil {
return "", err
}
_, newHash, _, _, _, _, err := c.hash(password, stub)
return newHash, err
}
func (c *scryptSHA256Crypter) Verify(password, hash string) (err error) {
cScryptSHA256VerifyCalls.Add(1)
_, newHash, _, _, _, _, err := c.hash(password, hash)
if err == nil && !abstract.SecureCompare(hash, newHash) {
err = abstract.ErrInvalidPassword
}
return
}
func (c *scryptSHA256Crypter) NeedsUpdate(stub string) bool {
salt, _, N, r, p, err := raw.Parse(stub)
if err != nil {
return false // ...
}
return c.needsUpdate(salt, N, r, p)
}
func (c *scryptSHA256Crypter) needsUpdate(salt []byte, N, r, p int) bool {
return len(salt) < 18 || N < c.nN || r < c.r || p < c.p
}
func (c *scryptSHA256Crypter) hash(password, stub string) (oldHashRaw []byte, newHash string, salt []byte, N, r, p int, err error) {
salt, oldHashRaw, N, r, p, err = raw.Parse(stub)
if err != nil {
return
}
return oldHashRaw, raw.ScryptSHA256(password, salt, N, r, p), salt, N, r, p, nil
}
func (c *scryptSHA256Crypter) makeStub() (string, error) {
buf := make([]byte, 18)
_, err := rand.Read(buf)
if err != nil {
return "", err
}
salt := base64.StdEncoding.EncodeToString(buf)
return fmt.Sprintf("$s2$%d$%d$%d$%s", c.nN, c.r, c.p, salt), nil
}
func (c *scryptSHA256Crypter) String() string {
return fmt.Sprintf("scrypt-sha256(%d,%d,%d)", c.nN, c.r, c.p)
}

View file

@ -0,0 +1,34 @@
package raw
const bmap = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
// Encodes a byte string using the sha2-crypt base64 variant.
func EncodeBase64(b []byte) string {
o := make([]byte, len(b)/3*4+4)
for i, j := 0, 0; i < len(b); {
b1 := b[i]
b2 := byte(0)
b3 := byte(0)
if (i + 1) < len(b) {
b2 = b[i+1]
}
if (i + 2) < len(b) {
b3 = b[i+2]
}
o[j] = bmap[(b1 & 0x3F)]
o[j+1] = bmap[((b1&0xC0)>>6)|((b2&0x0F)<<2)]
o[j+2] = bmap[((b2&0xF0)>>4)|((b3&0x03)<<4)]
o[j+3] = bmap[(b3&0xFC)>>2]
i += 3
j += 4
}
s := string(o)
return s[0 : len(b)*4/3-(len(b)%4)+1]
}
// © 2008-2012 Assurance Technologies LLC. (Python passlib) BSD License
// © 2014 Hugo Landau <hlandau@devever.net> BSD License

View file

@ -0,0 +1,82 @@
package raw
import "fmt"
import "strings"
import "strconv"
// Indicates that a password hash or stub is invalid.
var ErrInvalidStub = fmt.Errorf("invalid stub")
// Indicates that the number of rounds specified is not in the valid range.
var ErrInvalidRounds = fmt.Errorf("invalid number of rounds")
// Scans a sha256-crypt or sha512-crypt modular crypt stub or modular crypt hash
// to determine configuration parameters.
func Parse(stub string) (isSHA512 bool, salt, hash string, rounds int, err error) {
// $5$
if len(stub) < 3 || stub[0] != '$' || stub[2] != '$' {
err = ErrInvalidStub
return
}
if stub[1] == '6' {
isSHA512 = true
} else if stub[1] != '5' {
err = ErrInvalidStub
return
}
rest := stub[3:]
parts := strings.Split(rest, "$")
roundsStr := ""
switch len(parts) {
case 1:
// $5$
// $5$salt
salt = parts[0]
case 2:
// $5$salt$hash
// $5$rounds=1000$salt
if strings.HasPrefix(parts[0], "rounds=") {
roundsStr = parts[0]
salt = parts[1]
} else {
salt = parts[0]
hash = parts[1]
}
case 3:
// $5$rounds=1000$salt$hash
roundsStr = parts[0]
salt = parts[1]
hash = parts[2]
default:
err = ErrInvalidStub
}
if roundsStr != "" {
if !strings.HasPrefix(roundsStr, "rounds=") {
err = ErrInvalidStub
return
}
roundsStr = roundsStr[7:]
var n uint64
n, err = strconv.ParseUint(roundsStr, 10, 31)
if err != nil {
err = ErrInvalidStub
return
}
rounds = int(n)
if rounds < MinimumRounds || rounds > MaximumRounds {
err = ErrInvalidRounds
return
}
} else {
rounds = DefaultRounds
}
return
}

View file

@ -0,0 +1,187 @@
// Package raw provides a raw implementation of the sha256-crypt and sha512-crypt primitives.
package raw
import "io"
import "fmt"
import "hash"
import "crypto/sha256"
import "crypto/sha512"
// The minimum number of rounds permissible for sha256-crypt and sha512-crypt.
const MinimumRounds = 1000
// The maximum number of rounds permissible for sha256-crypt and sha512-crypt.
// Don't use this!
const MaximumRounds = 999999999
// This is the 'default' number of rounds for sha256-crypt and sha512-crypt. If
// this rounds value is used the number of rounds is not explicitly specified
// in the modular crypt format, as it is the default.
const DefaultRounds = 5000
// This is the recommended number of rounds for sha256-crypt and sha512-crypt.
// This may change with subsequent releases of this package. It is recommended
// that you invoke sha256-crypt or sha512-crypt with this value, or a value
// proportional to it.
const RecommendedRounds = 10000
// Calculates sha256-crypt. The password must be in plaintext and be a UTF-8
// string.
//
// The salt must be a valid ASCII between 0 and 16 characters in length
// inclusive.
//
// See the constants in this package for suggested values for rounds.
//
// Rounds must be in the range 1000 <= rounds <= 999999999. The function panics
// if this is not the case.
//
// The output is in modular crypt format.
func Crypt256(password, salt string, rounds int) string {
return "$5" + shaCrypt(password, salt, rounds, sha256.New, transpose256)
}
// Calculates sha256-crypt. The password must be in plaintext and be a UTF-8
// string.
//
// The salt must be a valid ASCII between 0 and 16 characters in length
// inclusive.
//
// See the constants in this package for suggested values for rounds.
//
// Rounds must be in the range 1000 <= rounds <= 999999999. The function panics
// if this is not the case.
//
// The output is in modular crypt format.
func Crypt512(password, salt string, rounds int) string {
return "$6" + shaCrypt(password, salt, rounds, sha512.New, transpose512)
}
func shaCrypt(password, salt string, rounds int, newHash func() hash.Hash, transpose func(b []byte)) string {
if rounds < MinimumRounds || rounds > MaximumRounds {
panic("sha256-crypt rounds must be in 1000 <= rounds <= 999999999")
}
passwordb := []byte(password)
saltb := []byte(salt)
if len(saltb) > 16 {
panic("salt must not exceed 16 bytes")
}
// B
b := newHash()
b.Write(passwordb)
b.Write(saltb)
b.Write(passwordb)
bsum := b.Sum(nil)
// A
a := newHash()
a.Write(passwordb)
a.Write(saltb)
repeat(a, bsum, len(passwordb))
plen := len(passwordb)
for plen != 0 {
if (plen & 1) != 0 {
a.Write(bsum)
} else {
a.Write(passwordb)
}
plen = plen >> 1
}
asum := a.Sum(nil)
// DP
dp := newHash()
for i := 0; i < len(passwordb); i++ {
dp.Write(passwordb)
}
dpsum := dp.Sum(nil)
// P
p := make([]byte, len(passwordb))
repeatTo(p, dpsum)
// DS
ds := newHash()
for i := 0; i < (16 + int(asum[0])); i++ {
ds.Write(saltb)
}
dssum := ds.Sum(nil)[0:len(saltb)]
// S
s := make([]byte, len(saltb))
repeatTo(s, dssum)
// C
cur := asum[:]
for i := 0; i < rounds; i++ {
c := newHash()
if (i & 1) != 0 {
c.Write(p)
} else {
c.Write(cur)
}
if (i % 3) != 0 {
c.Write(s)
}
if (i % 7) != 0 {
c.Write(p)
}
if (i & 1) == 0 {
c.Write(p)
} else {
c.Write(cur)
}
cur = c.Sum(nil)[:]
}
// Transposition
transpose(cur)
// Hash
hstr := EncodeBase64(cur)
if rounds == DefaultRounds {
return fmt.Sprintf("$%s$%s", salt, hstr)
}
return fmt.Sprintf("$rounds=%d$%s$%s", rounds, salt, hstr)
}
func repeat(w io.Writer, b []byte, sz int) {
var i int
for i = 0; (i + len(b)) <= sz; i += len(b) {
w.Write(b)
}
w.Write(b[0 : sz-i])
}
func repeatTo(out []byte, b []byte) {
if len(b) == 0 {
return
}
var i int
for i = 0; (i + len(b)) <= len(out); i += len(b) {
copy(out[i:], b)
}
copy(out[i:], b)
}
func transpose256(b []byte) {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19], b[20], b[21], b[22], b[23], b[24], b[25], b[26], b[27], b[28], b[29], b[30], b[31] =
b[20], b[10], b[0], b[11], b[1], b[21], b[2], b[22], b[12], b[23], b[13], b[3], b[14], b[4], b[24], b[5], b[25], b[15], b[26], b[16], b[6], b[17], b[7], b[27], b[8], b[28], b[18], b[29], b[19], b[9], b[30], b[31]
}
func transpose512(b []byte) {
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19], b[20], b[21], b[22], b[23], b[24], b[25], b[26], b[27], b[28], b[29], b[30], b[31], b[32], b[33], b[34], b[35], b[36], b[37], b[38], b[39], b[40], b[41], b[42], b[43], b[44], b[45], b[46], b[47], b[48], b[49], b[50], b[51], b[52], b[53], b[54], b[55], b[56], b[57], b[58], b[59], b[60], b[61], b[62], b[63] =
b[42], b[21], b[0], b[1], b[43], b[22], b[23], b[2], b[44], b[45], b[24], b[3], b[4], b[46], b[25], b[26], b[5], b[47], b[48], b[27], b[6], b[7], b[49], b[28], b[29], b[8], b[50], b[51], b[30], b[9], b[10], b[52], b[31], b[32], b[11], b[53], b[54], b[33], b[12], b[13], b[55], b[34], b[35], b[14], b[56], b[57], b[36], b[15], b[16], b[58], b[37], b[38], b[17], b[59], b[60], b[39], b[18], b[19], b[61], b[40], b[41], b[20], b[62], b[63]
}
// © 2008-2012 Assurance Technologies LLC. (Python passlib) BSD License
// © 2014 Hugo Landau <hlandau@devever.net> BSD License

View file

@ -0,0 +1,147 @@
// 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

174
vendor/gopkg.in/hlandau/passlib.v1/passlib.go generated vendored Normal file
View file

@ -0,0 +1,174 @@
// Package passlib provides a simple password hashing and verification
// interface abstracting multiple password hashing schemes.
//
// After initialisation, most people need concern themselves only with the
// functions Hash and Verify, which uses the default context and sensible
// defaults.
//
// Library Initialization
//
// You should initialise the library before using it with the following line.
//
// // Call this at application startup.
// passlib.UseDefaults(passlib.Defaults20180601)
//
// See func UseDefaults for details.
package passlib // import "gopkg.in/hlandau/passlib.v1"
import (
"gopkg.in/hlandau/easymetric.v1/cexp"
"gopkg.in/hlandau/passlib.v1/abstract"
)
var cHashCalls = cexp.NewCounter("passlib.ctx.hashCalls")
var cVerifyCalls = cexp.NewCounter("passlib.ctx.verifyCalls")
var cSuccessfulVerifyCalls = cexp.NewCounter("passlib.ctx.successfulVerifyCalls")
var cFailedVerifyCalls = cexp.NewCounter("passlib.ctx.failedVerifyCalls")
var cSuccessfulVerifyCallsWithUpgrade = cexp.NewCounter("passlib.ctx.successfulVerifyCallsWithUpgrade")
var cSuccessfulVerifyCallsDeferringUpgrade = cexp.NewCounter("passlib.ctx.successfulVerifyCallsDeferringUpgrade")
// A password hashing context, that uses a given set of schemes to hash and
// verify passwords.
type Context struct {
// Slice of schemes to use, most preferred first.
//
// If left uninitialized, a sensible default set of schemes will be used.
//
// An upgrade hash (see the newHash return value of the Verify method of the
// abstract.Scheme interface) will be issued whenever a password is validated
// using a scheme which is not the first scheme in this slice.
Schemes []abstract.Scheme
}
func (ctx *Context) schemes() []abstract.Scheme {
if ctx.Schemes == nil {
return DefaultSchemes
}
return ctx.Schemes
}
// Hashes a UTF-8 plaintext password using the context and produces a password hash.
//
// If stub is "", one is generated automaticaly for the preferred password hashing
// scheme; you should specify stub as "" in almost all cases.
//
// The provided or randomly generated stub is used to deterministically hash
// the password. The returned hash is in modular crypt format.
//
// If the context has not been specifically configured, a sensible default policy
// is used. See the fields of Context.
func (ctx *Context) Hash(password string) (hash string, err error) {
cHashCalls.Add(1)
return ctx.schemes()[0].Hash(password)
}
// Verifies a UTF-8 plaintext password using a previously derived password hash
// and the default context. Returns nil err only if the password is valid.
//
// If the hash is determined to be deprecated based on the context policy, and
// the password is valid, the password is hashed using the preferred password
// hashing scheme and returned in newHash. You should use this to upgrade any
// stored password hash in your database.
//
// newHash is empty if the password was not valid or if no upgrade is required.
//
// You should treat any non-nil err as a password verification error.
func (ctx *Context) Verify(password, hash string) (newHash string, err error) {
return ctx.verify(password, hash, true)
}
// Like Verify, but does not hash an upgrade password when upgrade is required.
func (ctx *Context) VerifyNoUpgrade(password, hash string) error {
_, err := ctx.verify(password, hash, false)
return err
}
func (ctx *Context) verify(password, hash string, canUpgrade bool) (newHash string, err error) {
cVerifyCalls.Add(1)
for i, scheme := range ctx.schemes() {
if !scheme.SupportsStub(hash) {
continue
}
err = scheme.Verify(password, hash)
if err != nil {
cFailedVerifyCalls.Add(1)
return "", err
}
cSuccessfulVerifyCalls.Add(1)
if i != 0 || scheme.NeedsUpdate(hash) {
if canUpgrade {
cSuccessfulVerifyCallsWithUpgrade.Add(1)
// If the scheme is not the first scheme, try and rehash with the
// preferred scheme.
if newHash, err2 := ctx.Hash(password); err2 == nil {
return newHash, nil
}
} else {
cSuccessfulVerifyCallsDeferringUpgrade.Add(1)
}
}
return "", nil
}
return "", abstract.ErrUnsupportedScheme
}
// Determines whether a stub or hash needs updating according to the policy of
// the context.
func (ctx *Context) NeedsUpdate(stub string) bool {
for i, scheme := range ctx.schemes() {
if scheme.SupportsStub(stub) {
return i != 0 || scheme.NeedsUpdate(stub)
}
}
return false
}
// The default context, which uses sensible defaults. Most users should not
// reconfigure this. The defaults may change over time, so you may wish
// to reconfigure the context or use a custom context if you want precise
// control over the hashes used.
var DefaultContext Context
// Hashes a UTF-8 plaintext password using the default context and produces a
// password hash. Chooses the preferred password hashing scheme based on the
// configured policy. The default policy is sensible.
func Hash(password string) (hash string, err error) {
return DefaultContext.Hash(password)
}
// Verifies a UTF-8 plaintext password using a previously derived password hash
// and the default context. Returns nil err only if the password is valid.
//
// If the hash is determined to be deprecated based on policy, and the password
// is valid, the password is hashed using the preferred password hashing scheme
// and returned in newHash. You should use this to upgrade any stored password
// hash in your database.
//
// newHash is empty if the password was invalid or no upgrade is required.
//
// You should treat any non-nil err as a password verification error.
func Verify(password, hash string) (newHash string, err error) {
return DefaultContext.Verify(password, hash)
}
// Like Verify, but never upgrades.
func VerifyNoUpgrade(password, hash string) error {
return DefaultContext.VerifyNoUpgrade(password, hash)
}
// Uses the default context to determine whether a stub or hash needs updating.
func NeedsUpdate(stub string) bool {
return DefaultContext.NeedsUpdate(stub)
}
// © 2008-2012 Assurance Technologies LLC. (Python passlib) BSD License
// © 2014 Hugo Landau <hlandau@devever.net> BSD License