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

97
vendor/gopkg.in/hlandau/easymetric.v1/cexp/cexp.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
package cexp
import "gopkg.in/hlandau/measurable.v1"
import "sync/atomic"
// Counter
type Counter struct {
name string
value int64
}
func (c *Counter) MsName() string {
return c.name
}
func (c *Counter) MsInt64() int64 {
return atomic.LoadInt64(&c.value)
}
func (c *Counter) Get() int64 {
return c.MsInt64()
}
// v must be non-negative.
func (c *Counter) Add(v int64) {
atomic.AddInt64(&c.value, v)
}
func (c *Counter) Inc() {
c.Add(1)
}
func (c *Counter) MsType() measurable.Type {
return measurable.CounterType
}
func NewCounter(name string) *Counter {
c := &Counter{
name: name,
}
measurable.Register(c)
return c
}
// Gauge
type Gauge struct {
name string
value int64
}
func (c *Gauge) MsName() string {
return c.name
}
func (c *Gauge) MsInt64() int64 {
return atomic.LoadInt64(&c.value)
}
func (c *Gauge) Add(v int64) {
atomic.AddInt64(&c.value, v)
}
func (c *Gauge) Sub(v int64) {
c.Add(-v)
}
func (c *Gauge) Set(v int64) {
atomic.StoreInt64(&c.value, v)
}
func (c *Gauge) Get() int64 {
return c.MsInt64()
}
func (c *Gauge) Inc() {
c.Add(1)
}
func (c *Gauge) Dec() {
c.Add(-1)
}
func (c *Gauge) MsType() measurable.Type {
return measurable.GaugeType
}
func NewGauge(name string) *Gauge {
c := &Gauge{
name: name,
}
measurable.Register(c)
return c
}

82
vendor/gopkg.in/hlandau/measurable.v1/README.md generated vendored Normal file
View File

@@ -0,0 +1,82 @@
Measurable: The useless Go metric registration package that doesn't do anything
===============================================================================
[![GoDoc](https://godoc.org/gopkg.in/hlandau/measurable.v1?status.svg)](https://godoc.org/gopkg.in/hlandau/measurable.v1)
Measurable is a Go library for managing the registration of metrics such as
counters and gauges, no matter how that metric data is eventually consumed.
The most noteworthy feature of measurable is that it doesn't do anything. It
contains no functionality for providing metric data to any external service,
and it contains no actual metric implementations.
The purpose of measurable is to act as an [integration
nexus](https://www.devever.net/~hl/nexuses), essentially a matchmaker between
metric sources and metric consumers. This creates the important feature that
your application's metrics can be expressed completely independently of *how*
those metrics are exported.
Measurable doesn't implement any metric or metric export logic because it
strives to be a neutral intermediary, which abstracts the interface between
metrics and metric exporters.
**Import as:** `gopkg.in/hlandau/measurable.v1`
Measurable
----------
A Measurable is an object that represents some metric. It is obliged only to
implement the following interface:
```go
type Measurable interface {
MsName() string
MsType() Type
}
```
Measurable is designed around interface upgrades. If you want to actually
do anything with a Measurable, you must attempt to cast it to an interface
with the methods you need. A Measurable is not obliged to implement any
interface besides Measurable, but almost always will.
Here are some common interfaces implemented by Measurables, in descending order
of importance:
- `MsName() string` — get the Measurable name.
- `MsType() Type` — get the Measurable type.
- `MsInt64() int64` — get the Measurable as an int64.
- `String() string` — the standard Go `String()` interface.
All Measurables should implement `MsName() string` and `MsType() Type`.
Measurable-specific methods should always be prefixed by `Ms` so it is clear
they are intended for consumption by Measurable consumers.
`MsName`, `MsType` and `MsInt64` should suffice for most consumers of Counter
and Gauge metric types.
Metrics should be named in lowercase using dots to create a hierarchy and
dashes to separate words, e.g. `someserver.http.request-count`. These metric
names may be transmuted by consumers as necessary for some graphing systems,
such as Prometheus (which allows only underscores).
Standard Bindings
-----------------
For a package which makes it easy to register and consume measurables, see the
[easymetric](https://github.com/hlandau/easymetric) package.
Of course, nothing requires you to use the easymetric package. You are free to escew it and make your own.
Background Reading
------------------
- [On Nexuses](https://www.devever.net/~hl/nexuses)
- See also: [Configurable](https://github.com/hlandau/configurable)
Licence
-------
© 2015 Hugo Landau <hlandau@devever.net> MIT License

189
vendor/gopkg.in/hlandau/measurable.v1/measurable.go generated vendored Normal file
View File

@@ -0,0 +1,189 @@
// Package measurable provides a functionality-free integration nexus for
// metric registration.
//
// Measurable is a Go package for connecting service metrics and metric consumers.
//
// The most noteworthy feature of measurable is that it doesn't do anything.
// It contains no functionality for defining or exporting metrics.
//
// The purpose of measurable is to act as an integration nexus
// (https://www.devever.net/~hl/nexuses), essentially a matchmaker between
// application metrics and metric consumers. This creates the important feature
// that your application's metrics can be defined completely independently of
// *how* those metrics are defined.
//
// Measurable doesn't implement any metric definition or export logic because it
// strives to be a neutral intermediary, which abstracts the interface between
// measurables and measurable consumers
//
// Pursuant to this, package measurable is this and only this: an interface
// Measurable which all metrics must implement, and a facility for registering
// Measurables and visiting them.
package measurable // import "gopkg.in/hlandau/measurable.v1"
import "sync"
import "fmt"
// Measurable is the interface which must be implemented by any metric item to
// be used with package measurable. In the current version, v1, it contains
// only the MsName() and MsType() methods. All other functionality must be
// obtained by interface upgrades.
type Measurable interface {
// Returns the name of the metric. Names should be in the style
// "alpha.beta.gamma-delta", for example "foo.http.requests.count". That is,
// names should be lowercase, should express a hierarchy separated by dots,
// and have words separated by dashes.
//
// Some Measurable consumers may mutate these names to satisfy naming
// restrictions applied by some graphing systems.
MsName() string
// Return the Measurable type. You can, of course, invent your own Measurable
// types, though consumers won't necessarily know what to do with them.
MsType() Type
}
var measurablesMutex sync.RWMutex
var measurables = map[string]Measurable{}
// Registers a top-level Configurable.
func Register(measurable Measurable) {
measurablesMutex.Lock()
defer measurablesMutex.Unlock()
if measurable == nil {
panic("cannot register nil measurable")
}
name := measurable.MsName()
if name == "" {
panic("measurable cannot have empty name")
}
_, exists := measurables[name]
if exists {
panic(fmt.Sprintf("A measurable with the same name already exists: %s", name))
}
measurables[name] = measurable
callRegistrationHooks(measurable, RegisterEvent)
}
func Unregister(measurableName string) {
measurablesMutex.Lock()
defer measurablesMutex.Unlock()
measurable, ok := measurables[measurableName]
if !ok {
return
}
callRegistrationHooks(measurable, UnregisterEvent)
delete(measurables, measurableName)
}
func Get(measurableName string) Measurable {
measurablesMutex.RLock()
defer measurablesMutex.RUnlock()
return measurables[measurableName]
}
// Visits all registered top-level Measurables.
//
// Returning a non-nil error short-circuits the iteration process and returns
// that error.
func Visit(do func(measurable Measurable) error) error {
measurablesMutex.Lock()
defer measurablesMutex.Unlock()
for _, measurable := range measurables {
err := do(measurable)
if err != nil {
return err
}
}
return nil
}
// Represents a measurable type.
type Type uint32
const (
// A CounterType Measurable represents a non-negative integral value
// that monotonously increases. It must implement `MsInt64() int64`.
CounterType Type = 0x436E7472
// A GaugeType Measurable represents an integral value that varies over
// time. It must implement `MsInt64() int64`.
GaugeType = 0x47617567
)
// Registration hooks.
type HookEvent int
const (
// This event is issued when a measurable is registered.
RegisterEvent HookEvent = iota
// This event is issued when a registration hook is registered. It is issued
// for every measurable which has already been registered.
RegisterCatchupEvent
// This event is issued when a measurable is unregistered.
UnregisterEvent
)
type HookFunc func(measurable Measurable, hookEvent HookEvent)
var hooksMutex sync.RWMutex
var hooks = map[interface{}]HookFunc{}
// Register for notifications on metric registration. The key must be usable as
// a key in a map and identifies the hook. No other hook with the same key must
// already exist.
//
// NOTE: The hook will be called for all registrations which already exist.
// This ensures that no registrations are missed in a threadsafe manner.
// For these calls, the event will be EventRegisterCatchup.
//
// The hook must not register or unregister registration hooks or metrics.
func RegisterHook(key interface{}, hook HookFunc) {
measurablesMutex.RLock()
defer measurablesMutex.RUnlock()
registerHook(key, hook)
for _, m := range measurables {
hook(m, RegisterCatchupEvent)
}
}
func registerHook(key interface{}, hook HookFunc) {
hooksMutex.Lock()
defer hooksMutex.Unlock()
_, exists := hooks[key]
if exists {
panic(fmt.Sprintf("A metric registration hook with the same key already exists: %+v", key))
}
hooks[key] = hook
}
// Unregister an existing hook.
func UnregisterHook(key interface{}) {
hooksMutex.Lock()
defer hooksMutex.Unlock()
delete(hooks, key)
}
func callRegistrationHooks(measurable Measurable, event HookEvent) {
hooksMutex.RLock()
defer hooksMutex.RUnlock()
for _, v := range hooks {
v(measurable, event)
}
}

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)
}

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