FreeBSD 12 support (NOTE: xc not yet functional)

Signed-off-by: Russ Magee <rmagee@gmail.com>
This commit is contained in:
Russ Magee 2020-04-26 01:03:29 +00:00
parent 09c2408c06
commit cba36b66f1
891 changed files with 351 additions and 304834 deletions

View file

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

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

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

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

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

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

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

@ -1,15 +0,0 @@
package raw
import (
"golang.org/x/crypto/pbkdf2"
"hash"
)
const (
MinRounds = 1
MaxRounds = 0xffffffff // setting at 32-bit 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))
}

View file

@ -1,30 +0,0 @@
#!/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

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

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

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

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

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

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