0
0
mirror of https://github.com/rls-moe/nyx synced 2025-09-13 12:04:13 +00:00

MVP, no mod tools or anything but it works

This commit is contained in:
Tim Schuster
2017-03-12 20:37:53 +01:00
parent 70b12c516a
commit 69b0d20825
186 changed files with 44200 additions and 0 deletions

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,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