mirror of
https://github.com/ergochat/ergo.git
synced 2025-12-20 02:00:11 -08:00
first draft of atheme migration code
This commit is contained in:
parent
c060113c74
commit
7a6413ea2c
25 changed files with 1423 additions and 63 deletions
7
vendor/github.com/GehirnInc/crypt/.travis.yml
generated
vendored
Normal file
7
vendor/github.com/GehirnInc/crypt/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- master
|
||||
script:
|
||||
- go test -v -race ./...
|
||||
8
vendor/github.com/GehirnInc/crypt/AUTHORS.md
generated
vendored
Normal file
8
vendor/github.com/GehirnInc/crypt/AUTHORS.md
generated
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
### Initial author
|
||||
|
||||
[Jeramey Crawford](https://github.com/jeramey)
|
||||
|
||||
### Other authors
|
||||
|
||||
- [Jonas mg](https://github.com/kless)
|
||||
- [Kohei YOSHIDA](https://github.com/yosida95)
|
||||
26
vendor/github.com/GehirnInc/crypt/LICENSE
generated
vendored
Normal file
26
vendor/github.com/GehirnInc/crypt/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
Copyright (c) 2012, Jeramey Crawford <jeramey@antihe.ro>
|
||||
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.
|
||||
|
||||
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
|
||||
HOLDER 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.
|
||||
61
vendor/github.com/GehirnInc/crypt/README.rst
generated
vendored
Normal file
61
vendor/github.com/GehirnInc/crypt/README.rst
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
.. image:: https://travis-ci.org/GehirnInc/crypt.svg?branch=master
|
||||
:target: https://travis-ci.org/GehirnInc/crypt
|
||||
|
||||
crypt - A password hashing library for Go
|
||||
=========================================
|
||||
crypt provides pure golang implementations of UNIX's crypt(3).
|
||||
|
||||
The goal of crypt is to bring a library of many common and popular password
|
||||
hashing algorithms to Go and to provide a simple and consistent interface to
|
||||
each of them. As every hashing method is implemented in pure Go, this library
|
||||
should be as portable as Go itself.
|
||||
|
||||
All hashing methods come with a test suite which verifies their operation
|
||||
against itself as well as the output of other password hashing implementations
|
||||
to ensure compatibility with them.
|
||||
|
||||
I hope you find this library to be useful and easy to use!
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
To install crypt, use the *go get* command.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
go get github.com/GehirnInc/crypt
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
.. code-block:: go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/GehirnInc/crypt"
|
||||
_ "github.com/GehirnInc/crypt/sha256_crypt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
crypt := crypt.SHA256.New()
|
||||
ret, _ := crypt.Generate([]byte("secret"), []byte("$5$salt"))
|
||||
fmt.Println(ret)
|
||||
|
||||
err := crypt.Verify(ret, []byte("secret"))
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// $5$salt$kpa26zwgX83BPSR8d7w93OIXbFt/d3UOTZaAu5vsTM6
|
||||
// <nil>
|
||||
}
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
The documentation is available on GoDoc_.
|
||||
|
||||
.. _GoDoc: https://godoc.org/github.com/GehirnInc/crypt
|
||||
59
vendor/github.com/GehirnInc/crypt/common/base64.go
generated
vendored
Normal file
59
vendor/github.com/GehirnInc/crypt/common/base64.go
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// (C) Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>. All
|
||||
// rights reserved. Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package common
|
||||
|
||||
const (
|
||||
alphabet = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
)
|
||||
|
||||
// Base64_24Bit is a variant of Base64 encoding, commonly used with password
|
||||
// hashing algorithms to encode the result of their checksum output.
|
||||
//
|
||||
// The algorithm operates on up to 3 bytes at a time, encoding the following
|
||||
// 6-bit sequences into up to 4 hash64 ASCII bytes.
|
||||
//
|
||||
// 1. Bottom 6 bits of the first byte
|
||||
// 2. Top 2 bits of the first byte, and bottom 4 bits of the second byte.
|
||||
// 3. Top 4 bits of the second byte, and bottom 2 bits of the third byte.
|
||||
// 4. Top 6 bits of the third byte.
|
||||
//
|
||||
// This encoding method does not emit padding bytes as Base64 does.
|
||||
func Base64_24Bit(src []byte) []byte {
|
||||
if len(src) == 0 {
|
||||
return []byte{} // TODO: return nil
|
||||
}
|
||||
|
||||
dstlen := (len(src)*8 + 5) / 6
|
||||
dst := make([]byte, dstlen)
|
||||
|
||||
di, si := 0, 0
|
||||
n := len(src) / 3 * 3
|
||||
for si < n {
|
||||
val := uint(src[si+2])<<16 | uint(src[si+1])<<8 | uint(src[si])
|
||||
dst[di+0] = alphabet[val&0x3f]
|
||||
dst[di+1] = alphabet[val>>6&0x3f]
|
||||
dst[di+2] = alphabet[val>>12&0x3f]
|
||||
dst[di+3] = alphabet[val>>18]
|
||||
di += 4
|
||||
si += 3
|
||||
}
|
||||
|
||||
rem := len(src) - si
|
||||
if rem == 0 {
|
||||
return dst
|
||||
}
|
||||
|
||||
val := uint(src[si+0])
|
||||
if rem == 2 {
|
||||
val |= uint(src[si+1]) << 8
|
||||
}
|
||||
|
||||
dst[di+0] = alphabet[val&0x3f]
|
||||
dst[di+1] = alphabet[val>>6&0x3f]
|
||||
if rem == 2 {
|
||||
dst[di+2] = alphabet[val>>12]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
10
vendor/github.com/GehirnInc/crypt/common/doc.go
generated
vendored
Normal file
10
vendor/github.com/GehirnInc/crypt/common/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// (C) Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>. All
|
||||
// rights reserved. Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package common contains routines used by multiple password hashing
|
||||
// algorithms.
|
||||
//
|
||||
// Generally, you will never import this package directly. Many of the
|
||||
// *_crypt packages will import this package if they require it.
|
||||
package common
|
||||
148
vendor/github.com/GehirnInc/crypt/common/salt.go
generated
vendored
Normal file
148
vendor/github.com/GehirnInc/crypt/common/salt.go
generated
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
// (C) Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>. All
|
||||
// rights reserved. Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSaltPrefix = errors.New("invalid magic prefix")
|
||||
ErrSaltFormat = errors.New("invalid salt format")
|
||||
ErrSaltRounds = errors.New("invalid rounds")
|
||||
)
|
||||
|
||||
const (
|
||||
roundsPrefix = "rounds="
|
||||
)
|
||||
|
||||
// Salt represents a salt.
|
||||
type Salt struct {
|
||||
MagicPrefix []byte
|
||||
|
||||
SaltLenMin int
|
||||
SaltLenMax int
|
||||
|
||||
RoundsMin int
|
||||
RoundsMax int
|
||||
RoundsDefault int
|
||||
}
|
||||
|
||||
// Generate generates a random salt of a given length.
|
||||
//
|
||||
// The length is set thus:
|
||||
//
|
||||
// length > SaltLenMax: length = SaltLenMax
|
||||
// length < SaltLenMin: length = SaltLenMin
|
||||
func (s *Salt) Generate(length int) []byte {
|
||||
if length > s.SaltLenMax {
|
||||
length = s.SaltLenMax
|
||||
} else if length < s.SaltLenMin {
|
||||
length = s.SaltLenMin
|
||||
}
|
||||
|
||||
saltLen := (length * 6 / 8)
|
||||
if (length*6)%8 != 0 {
|
||||
saltLen += 1
|
||||
}
|
||||
salt := make([]byte, saltLen)
|
||||
rand.Read(salt)
|
||||
|
||||
out := make([]byte, len(s.MagicPrefix)+length)
|
||||
copy(out, s.MagicPrefix)
|
||||
copy(out[len(s.MagicPrefix):], Base64_24Bit(salt))
|
||||
return out
|
||||
}
|
||||
|
||||
// GenerateWRounds creates a random salt with the random bytes being of the
|
||||
// length provided, and the rounds parameter set as specified.
|
||||
//
|
||||
// The parameters are set thus:
|
||||
//
|
||||
// length > SaltLenMax: length = SaltLenMax
|
||||
// length < SaltLenMin: length = SaltLenMin
|
||||
//
|
||||
// rounds < 0: rounds = RoundsDefault
|
||||
// rounds < RoundsMin: rounds = RoundsMin
|
||||
// rounds > RoundsMax: rounds = RoundsMax
|
||||
//
|
||||
// If rounds is equal to RoundsDefault, then the "rounds=" part of the salt is
|
||||
// removed.
|
||||
func (s *Salt) GenerateWRounds(length, rounds int) []byte {
|
||||
if length > s.SaltLenMax {
|
||||
length = s.SaltLenMax
|
||||
} else if length < s.SaltLenMin {
|
||||
length = s.SaltLenMin
|
||||
}
|
||||
if rounds < 0 {
|
||||
rounds = s.RoundsDefault
|
||||
} else if rounds < s.RoundsMin {
|
||||
rounds = s.RoundsMin
|
||||
} else if rounds > s.RoundsMax {
|
||||
rounds = s.RoundsMax
|
||||
}
|
||||
|
||||
saltLen := (length * 6 / 8)
|
||||
if (length*6)%8 != 0 {
|
||||
saltLen += 1
|
||||
}
|
||||
salt := make([]byte, saltLen)
|
||||
rand.Read(salt)
|
||||
|
||||
roundsText := ""
|
||||
if rounds != s.RoundsDefault {
|
||||
roundsText = roundsPrefix + strconv.Itoa(rounds) + "$"
|
||||
}
|
||||
|
||||
out := make([]byte, len(s.MagicPrefix)+len(roundsText)+length)
|
||||
copy(out, s.MagicPrefix)
|
||||
copy(out[len(s.MagicPrefix):], []byte(roundsText))
|
||||
copy(out[len(s.MagicPrefix)+len(roundsText):], Base64_24Bit(salt))
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *Salt) Decode(raw []byte) (salt []byte, rounds int, isRoundsDef bool, rest []byte, err error) {
|
||||
tokens := bytes.SplitN(raw, []byte{'$'}, 4)
|
||||
if len(tokens) < 3 {
|
||||
err = ErrSaltFormat
|
||||
return
|
||||
}
|
||||
if !bytes.HasPrefix(raw, s.MagicPrefix) {
|
||||
err = ErrSaltPrefix
|
||||
return
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(tokens[2], []byte(roundsPrefix)) {
|
||||
if len(tokens) < 4 {
|
||||
err = ErrSaltFormat
|
||||
return
|
||||
}
|
||||
salt = tokens[3]
|
||||
|
||||
rounds, err = strconv.Atoi(string(tokens[2][len(roundsPrefix):]))
|
||||
if err != nil {
|
||||
err = ErrSaltRounds
|
||||
return
|
||||
}
|
||||
if rounds < s.RoundsMin {
|
||||
rounds = s.RoundsMin
|
||||
}
|
||||
if rounds > s.RoundsMax {
|
||||
rounds = s.RoundsMax
|
||||
}
|
||||
isRoundsDef = true
|
||||
} else {
|
||||
salt = tokens[2]
|
||||
rounds = s.RoundsDefault
|
||||
}
|
||||
if len(salt) > s.SaltLenMax {
|
||||
salt = salt[0:s.SaltLenMax]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
121
vendor/github.com/GehirnInc/crypt/crypt.go
generated
vendored
Normal file
121
vendor/github.com/GehirnInc/crypt/crypt.go
generated
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
// (C) Copyright 2013, Jonas mg. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package crypt provides interface for password crypt functions and collects
|
||||
// common constants.
|
||||
package crypt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/GehirnInc/crypt/common"
|
||||
)
|
||||
|
||||
var ErrKeyMismatch = errors.New("hashed value is not the hash of the given password")
|
||||
|
||||
// Crypter is the common interface implemented by all crypt functions.
|
||||
type Crypter interface {
|
||||
// Generate performs the hashing algorithm, returning a full hash suitable
|
||||
// for storage and later password verification.
|
||||
//
|
||||
// If the salt is empty, a randomly-generated salt will be generated with a
|
||||
// length of SaltLenMax and number RoundsDefault of rounds.
|
||||
//
|
||||
// Any error only can be got when the salt argument is not empty.
|
||||
Generate(key, salt []byte) (string, error)
|
||||
|
||||
// Verify compares a hashed key with its possible key equivalent.
|
||||
// Returns nil on success, or an error on failure; if the hashed key is
|
||||
// diffrent, the error is "ErrKeyMismatch".
|
||||
Verify(hashedKey string, key []byte) error
|
||||
|
||||
// Cost returns the hashing cost (in rounds) used to create the given hashed
|
||||
// key.
|
||||
//
|
||||
// When, in the future, the hashing cost of a key needs to be increased in
|
||||
// order to adjust for greater computational power, this function allows one
|
||||
// to establish which keys need to be updated.
|
||||
//
|
||||
// The algorithms based in MD5-crypt use a fixed value of rounds.
|
||||
Cost(hashedKey string) (int, error)
|
||||
|
||||
// SetSalt sets a different salt. It is used to easily create derivated
|
||||
// algorithms, i.e. "apr1_crypt" from "md5_crypt".
|
||||
SetSalt(salt common.Salt)
|
||||
}
|
||||
|
||||
// Crypt identifies a crypt function that is implemented in another package.
|
||||
type Crypt uint
|
||||
|
||||
const (
|
||||
APR1 Crypt = 1 + iota // import github.com/GehirnInc/crypt/apr1_crypt
|
||||
MD5 // import github.com/GehirnInc/crypt/md5_crypt
|
||||
SHA256 // import github.com/GehirnInc/crypt/sha256_crypt
|
||||
SHA512 // import github.com/GehirnInc/crypt/sha512_crypt
|
||||
maxCrypt
|
||||
)
|
||||
|
||||
var crypts = make([]func() Crypter, maxCrypt)
|
||||
|
||||
// New returns new Crypter making the Crypt c.
|
||||
// New panics if the Crypt c is unavailable.
|
||||
func (c Crypt) New() Crypter {
|
||||
if c > 0 && c < maxCrypt {
|
||||
f := crypts[c]
|
||||
if f != nil {
|
||||
return f()
|
||||
}
|
||||
}
|
||||
panic("crypt: requested crypt function is unavailable")
|
||||
}
|
||||
|
||||
// Available reports whether the Crypt c is available.
|
||||
func (c Crypt) Available() bool {
|
||||
return c > 0 && c < maxCrypt && crypts[c] != nil
|
||||
}
|
||||
|
||||
var cryptPrefixes = make([]string, maxCrypt)
|
||||
|
||||
// RegisterCrypt registers a function that returns a new instance of the given
|
||||
// crypt function. This is intended to be called from the init function in
|
||||
// packages that implement crypt functions.
|
||||
func RegisterCrypt(c Crypt, f func() Crypter, prefix string) {
|
||||
if c >= maxCrypt {
|
||||
panic("crypt: RegisterHash of unknown crypt function")
|
||||
}
|
||||
crypts[c] = f
|
||||
cryptPrefixes[c] = prefix
|
||||
}
|
||||
|
||||
// New returns a new crypter.
|
||||
func New(c Crypt) Crypter {
|
||||
return c.New()
|
||||
}
|
||||
|
||||
// IsHashSupported returns true if hashedKey has a supported prefix.
|
||||
// NewFromHash will not panic for this hashedKey
|
||||
func IsHashSupported(hashedKey string) bool {
|
||||
for i := range cryptPrefixes {
|
||||
prefix := cryptPrefixes[i]
|
||||
if crypts[i] != nil && strings.HasPrefix(hashedKey, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// NewFromHash returns a new Crypter using the prefix in the given hashed key.
|
||||
func NewFromHash(hashedKey string) Crypter {
|
||||
for i := range cryptPrefixes {
|
||||
prefix := cryptPrefixes[i]
|
||||
if crypts[i] != nil && strings.HasPrefix(hashedKey, prefix) {
|
||||
crypt := Crypt(uint(i))
|
||||
return crypt.New()
|
||||
}
|
||||
}
|
||||
|
||||
panic("crypt: unknown crypt function")
|
||||
}
|
||||
41
vendor/github.com/GehirnInc/crypt/internal/utils.go
generated
vendored
Normal file
41
vendor/github.com/GehirnInc/crypt/internal/utils.go
generated
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) 2015 Kohei YOSHIDA. All rights reserved.
|
||||
// This software is licensed under the 3-Clause BSD License
|
||||
// that can be found in LICENSE file.
|
||||
package internal
|
||||
|
||||
const (
|
||||
cleanBytesLen = 64
|
||||
)
|
||||
|
||||
var (
|
||||
cleanBytes = make([]byte, cleanBytesLen)
|
||||
)
|
||||
|
||||
func CleanSensitiveData(b []byte) {
|
||||
l := len(b)
|
||||
|
||||
for ; l > cleanBytesLen; l -= cleanBytesLen {
|
||||
copy(b[l-cleanBytesLen:l], cleanBytes)
|
||||
}
|
||||
|
||||
if l > 0 {
|
||||
copy(b[0:l], cleanBytes[0:l])
|
||||
}
|
||||
}
|
||||
|
||||
func RepeatByteSequence(input []byte, length int) []byte {
|
||||
var (
|
||||
sequence = make([]byte, length)
|
||||
unit = len(input)
|
||||
)
|
||||
|
||||
j := length / unit * unit
|
||||
for i := 0; i < j; i += unit {
|
||||
copy(sequence[i:length], input)
|
||||
}
|
||||
if j < length {
|
||||
copy(sequence[j:length], input[0:length-j])
|
||||
}
|
||||
|
||||
return sequence
|
||||
}
|
||||
143
vendor/github.com/GehirnInc/crypt/md5_crypt/md5_crypt.go
generated
vendored
Normal file
143
vendor/github.com/GehirnInc/crypt/md5_crypt/md5_crypt.go
generated
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
// (C) Copyright 2012, Jeramey Crawford <jeramey@antihe.ro>. All
|
||||
// rights reserved. Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package md5_crypt implements the standard Unix MD5-crypt algorithm created by
|
||||
// Poul-Henning Kamp for FreeBSD.
|
||||
package md5_crypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/subtle"
|
||||
|
||||
"github.com/GehirnInc/crypt"
|
||||
"github.com/GehirnInc/crypt/common"
|
||||
"github.com/GehirnInc/crypt/internal"
|
||||
)
|
||||
|
||||
func init() {
|
||||
crypt.RegisterCrypt(crypt.MD5, New, MagicPrefix)
|
||||
}
|
||||
|
||||
// NOTE: Cisco IOS only allows salts of length 4.
|
||||
|
||||
const (
|
||||
MagicPrefix = "$1$"
|
||||
SaltLenMin = 1 // Real minimum is 0, but that isn't useful.
|
||||
SaltLenMax = 8
|
||||
RoundsDefault = 1000
|
||||
)
|
||||
|
||||
type crypter struct{ Salt common.Salt }
|
||||
|
||||
// New returns a new crypt.Crypter computing the MD5-crypt password hashing.
|
||||
func New() crypt.Crypter {
|
||||
return &crypter{
|
||||
common.Salt{
|
||||
MagicPrefix: []byte(MagicPrefix),
|
||||
SaltLenMin: SaltLenMin,
|
||||
SaltLenMax: SaltLenMax,
|
||||
RoundsDefault: RoundsDefault,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *crypter) Generate(key, salt []byte) (result string, err error) {
|
||||
if len(salt) == 0 {
|
||||
salt = c.Salt.Generate(SaltLenMax)
|
||||
}
|
||||
salt, _, _, _, err = c.Salt.Decode(salt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
keyLen := len(key)
|
||||
h := md5.New()
|
||||
|
||||
// Compute sumB
|
||||
h.Write(key)
|
||||
h.Write(salt)
|
||||
h.Write(key)
|
||||
sumB := h.Sum(nil)
|
||||
|
||||
// Compute sumA
|
||||
h.Reset()
|
||||
h.Write(key)
|
||||
h.Write(c.Salt.MagicPrefix)
|
||||
h.Write(salt)
|
||||
h.Write(internal.RepeatByteSequence(sumB, keyLen))
|
||||
// The original implementation now does something weird:
|
||||
// For every 1 bit in the key, the first 0 is added to the buffer
|
||||
// For every 0 bit, the first character of the key
|
||||
// This does not seem to be what was intended but we have to follow this to
|
||||
// be compatible.
|
||||
for i := keyLen; i > 0; i >>= 1 {
|
||||
if i%2 == 0 {
|
||||
h.Write(key[0:1])
|
||||
} else {
|
||||
h.Write([]byte{0})
|
||||
}
|
||||
}
|
||||
sumA := h.Sum(nil)
|
||||
internal.CleanSensitiveData(sumB)
|
||||
|
||||
// In fear of password crackers here comes a quite long loop which just
|
||||
// processes the output of the previous round again.
|
||||
// We cannot ignore this here.
|
||||
for i := 0; i < RoundsDefault; i++ {
|
||||
h.Reset()
|
||||
|
||||
// Add key or last result.
|
||||
if i%2 != 0 {
|
||||
h.Write(key)
|
||||
} else {
|
||||
h.Write(sumA)
|
||||
}
|
||||
// Add salt for numbers not divisible by 3.
|
||||
if i%3 != 0 {
|
||||
h.Write(salt)
|
||||
}
|
||||
// Add key for numbers not divisible by 7.
|
||||
if i%7 != 0 {
|
||||
h.Write(key)
|
||||
}
|
||||
// Add key or last result.
|
||||
if i&1 != 0 {
|
||||
h.Write(sumA)
|
||||
} else {
|
||||
h.Write(key)
|
||||
}
|
||||
copy(sumA, h.Sum(nil))
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
buf.Grow(len(c.Salt.MagicPrefix) + len(salt) + 1 + 22)
|
||||
buf.Write(c.Salt.MagicPrefix)
|
||||
buf.Write(salt)
|
||||
buf.WriteByte('$')
|
||||
buf.Write(common.Base64_24Bit([]byte{
|
||||
sumA[12], sumA[6], sumA[0],
|
||||
sumA[13], sumA[7], sumA[1],
|
||||
sumA[14], sumA[8], sumA[2],
|
||||
sumA[15], sumA[9], sumA[3],
|
||||
sumA[5], sumA[10], sumA[4],
|
||||
sumA[11],
|
||||
}))
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (c *crypter) Verify(hashedKey string, key []byte) error {
|
||||
newHash, err := c.Generate(key, []byte(hashedKey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(newHash), []byte(hashedKey)) != 1 {
|
||||
return crypt.ErrKeyMismatch
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *crypter) Cost(hashedKey string) (int, error) { return RoundsDefault, nil }
|
||||
|
||||
func (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt }
|
||||
Loading…
Add table
Add a link
Reference in a new issue