forked from TrueCloudLab/restic
Vendor github.com/elithrar/simple-scrypt
This commit is contained in:
parent
84c14e623d
commit
7e6fc15ece
5 changed files with 634 additions and 0 deletions
6
vendor/manifest
vendored
6
vendor/manifest
vendored
|
@ -7,6 +7,12 @@
|
|||
"revision": "18419ee53958df28fcfc9490fe6123bd59e237bb",
|
||||
"branch": "HEAD"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/elithrar/simple-scrypt",
|
||||
"repository": "https://github.com/elithrar/simple-scrypt",
|
||||
"revision": "cbb1ebac08e2ca5495a43f4ef5555e61a7ec7677",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/jessevdk/go-flags",
|
||||
"repository": "https://github.com/jessevdk/go-flags",
|
||||
|
|
22
vendor/src/github.com/elithrar/simple-scrypt/LICENSE
vendored
Normal file
22
vendor/src/github.com/elithrar/simple-scrypt/LICENSE
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Matthew Silverlock (matt@eatsleeprepeat.net)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
155
vendor/src/github.com/elithrar/simple-scrypt/README.md
vendored
Normal file
155
vendor/src/github.com/elithrar/simple-scrypt/README.md
vendored
Normal file
|
@ -0,0 +1,155 @@
|
|||
# simple-scrypt
|
||||
[![GoDoc](https://godoc.org/github.com/elithrar/simple-scrypt?status.svg)](https://godoc.org/github.com/elithrar/simple-scrypt) [![Build Status](https://travis-ci.org/elithrar/simple-scrypt.svg?branch=master)](https://travis-ci.org/elithrar/simple-scrypt)
|
||||
|
||||
simple-scrypt provides a convenience wrapper around Go's existing
|
||||
[scrypt](http://golang.org/x/crypto/scrypt) package that makes it easier to
|
||||
securely derive strong keys ("hash user passwords"). This library allows you to:
|
||||
|
||||
* Generate a scrypt derived key with a crytographically secure salt and sane
|
||||
default parameters for N, r and p.
|
||||
* Upgrade the parameters used to generate keys as hardware improves by storing
|
||||
them with the derived key (the scrypt spec. doesn't allow for this by
|
||||
default).
|
||||
* Provide your own parameters (if you wish to).
|
||||
|
||||
The API closely mirrors Go's [bcrypt](https://golang.org/x/crypto/bcrypt)
|
||||
library in an effort to make it easy to migrate—and because it's an easy to grok
|
||||
API.
|
||||
|
||||
## Installation
|
||||
|
||||
With a [working Go toolchain](https://golang.org/doc/code.html):
|
||||
|
||||
```sh
|
||||
go get -u github.com/elithrar/simple-scrypt
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
simple-scrypt doesn't try to re-invent the wheel or do anything "special". It
|
||||
wraps the `scrypt.Key` function as thinly as possible, generates a
|
||||
crytographically secure salt for you using Go's `crypto/rand` package, and
|
||||
returns the derived key with the parameters prepended:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import(
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/elithrar/simple-scrypt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// e.g. r.PostFormValue("password")
|
||||
passwordFromForm := "prew8fid9hick6c"
|
||||
|
||||
// Generates a derived key of the form "N$r$p$salt$dk" where N, r and p are defined as per
|
||||
// Colin Percival's scrypt paper: http://www.tarsnap.com/scrypt/scrypt.pdf
|
||||
// scrypt.Defaults (N=16384, r=8, p=1) makes it easy to provide these parameters, and
|
||||
// (should you wish) provide your own values via the scrypt.Params type.
|
||||
hash, err := scrypt.GenerateFromPassword([]byte(passwordFromForm), scrypt.DefaultParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print the derived key with its parameters prepended.
|
||||
fmt.Printf("%s\n", hash)
|
||||
|
||||
// Uses the parameters from the existing derived key. Return an error if they don't match.
|
||||
err := scrypt.CompareHashAndPassword(hash, []byte(passwordFromForm))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Upgrading Parameters
|
||||
|
||||
Upgrading derived keys from a set of parameters to a "stronger" set of parameters
|
||||
as hardware improves, or as you scale (and move your auth process to separate
|
||||
hardware), can be pretty useful. Here's how to do it with simple-scrypt:
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// SCENE: We've successfully authenticated a user, compared their submitted
|
||||
// (cleartext) password against the derived key stored in our database, and
|
||||
// now want to upgrade the parameters (more rounds, more parallelism) to
|
||||
// reflect some shiny new hardware we just purchased. As the user is logging
|
||||
// in, we can retrieve the parameters used to generate their key, and if
|
||||
// they don't match our "new" parameters, we can re-generate the key while
|
||||
// we still have the cleartext password in memory
|
||||
// (e.g. before the HTTP request ends).
|
||||
current, err := scrypt.Cost(hash)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Now to check them against our own Params struct (e.g. using reflect.DeepEquals)
|
||||
// and determine whether we want to generate a new key with our "upgraded" parameters.
|
||||
slower := scrypt.Params{
|
||||
N: 32768,
|
||||
R: 8,
|
||||
P: 2,
|
||||
SaltLen: 16,
|
||||
DKLen: 32,
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(current, slower) {
|
||||
// Re-generate the key with the slower parameters
|
||||
// here using scrypt.GenerateFromPassword
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Automatically Determining Parameters
|
||||
|
||||
Thanks to the work by [tgulacsi](https://github.com/tgulacsi), you can have simple-scrypt
|
||||
automatically determine the optimal parameters for you (time vs. memory). You should run this once
|
||||
on program startup, as calibrating parameters can be an expensive operation.
|
||||
|
||||
```go
|
||||
var params scrypt.Params
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
// 500ms, 64MB of RAM per hash.
|
||||
params, err = scrypt.Calibrate(500*time.Millisecond, 64, Params{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
func RegisterUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure you validate: not empty, not too long, etc.
|
||||
email := r.PostFormValue("email")
|
||||
pass := r.PostFormValue("password")
|
||||
|
||||
// Use our calibrated parameters
|
||||
hash, err := scrypt.GenerateFromPassword([]byte(pass), params)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Save to DB, etc.
|
||||
}
|
||||
```
|
||||
|
||||
Be aware that increasing these, whilst making it harder to brute-force the resulting hash, also
|
||||
increases the risk of a denial-of-service attack against your server. A surge in authenticate
|
||||
attempts (even if legitimate!) could consume all available resources.
|
||||
|
||||
## License
|
||||
|
||||
MIT Licensed. See LICENSE file for details.
|
||||
|
295
vendor/src/github.com/elithrar/simple-scrypt/scrypt.go
vendored
Normal file
295
vendor/src/github.com/elithrar/simple-scrypt/scrypt.go
vendored
Normal file
|
@ -0,0 +1,295 @@
|
|||
// Package scrypt provides a convenience wrapper around Go's existing scrypt package
|
||||
// that makes it easier to securely derive strong keys from weak
|
||||
// inputs (i.e. user passwords).
|
||||
// The package provides password generation, constant-time comparison and
|
||||
// parameter upgrading for scrypt derived keys.
|
||||
package scrypt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
// Constants
|
||||
const (
|
||||
maxInt = 1<<31 - 1
|
||||
minDKLen = 16 // the minimum derived key length in bytes.
|
||||
minSaltLen = 8 // the minimum allowed salt length in bytes.
|
||||
)
|
||||
|
||||
// Params describes the input parameters to the scrypt
|
||||
// key derivation function as per Colin Percival's scrypt
|
||||
// paper: http://www.tarsnap.com/scrypt/scrypt.pdf
|
||||
type Params struct {
|
||||
N int // CPU/memory cost parameter (logN)
|
||||
R int // block size parameter (octets)
|
||||
P int // parallelisation parameter (positive int)
|
||||
SaltLen int // bytes to use as salt (octets)
|
||||
DKLen int // length of the derived key (octets)
|
||||
}
|
||||
|
||||
// DefaultParams provides sensible default inputs into the scrypt function
|
||||
// for interactive use (i.e. web applications).
|
||||
// These defaults will consume approxmiately 16MB of memory (128 * r * N).
|
||||
// The default key length is 256 bits.
|
||||
var DefaultParams = Params{N: 16384, R: 8, P: 1, SaltLen: 16, DKLen: 32}
|
||||
|
||||
// ErrInvalidHash is returned when failing to parse a provided scrypt
|
||||
// hash and/or parameters.
|
||||
var ErrInvalidHash = errors.New("scrypt: the provided hash is not in the correct format")
|
||||
|
||||
// ErrInvalidParams is returned when the cost parameters (N, r, p), salt length
|
||||
// or derived key length are invalid.
|
||||
var ErrInvalidParams = errors.New("scrypt: the parameters provided are invalid")
|
||||
|
||||
// ErrMismatchedHashAndPassword is returned when a password (hashed) and
|
||||
// given hash do not match.
|
||||
var ErrMismatchedHashAndPassword = errors.New("scrypt: the hashed password does not match the hash of the given password")
|
||||
|
||||
// GenerateRandomBytes returns securely generated random bytes.
|
||||
// It will return an error if the system's secure random
|
||||
// number generator fails to function correctly, in which
|
||||
// case the caller should not continue.
|
||||
func GenerateRandomBytes(n int) ([]byte, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
// err == nil only if len(b) == n
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// GenerateFromPassword returns the derived key of the password using the
|
||||
// parameters provided. The parameters are prepended to the derived key and
|
||||
// separated by the "$" character (0x24).
|
||||
// If the parameters provided are less than the minimum acceptable values,
|
||||
// an error will be returned.
|
||||
func GenerateFromPassword(password []byte, params Params) ([]byte, error) {
|
||||
salt, err := GenerateRandomBytes(params.SaltLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := params.Check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// scrypt.Key returns the raw scrypt derived key.
|
||||
dk, err := scrypt.Key(password, salt, params.N, params.R, params.P, params.DKLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prepend the params and the salt to the derived key, each separated
|
||||
// by a "$" character. The salt and the derived key are hex encoded.
|
||||
return []byte(fmt.Sprintf("%d$%d$%d$%x$%x", params.N, params.R, params.P, salt, dk)), nil
|
||||
}
|
||||
|
||||
// CompareHashAndPassword compares a derived key with the possible cleartext
|
||||
// equivalent. The parameters used in the provided derived key are used.
|
||||
// The comparison performed by this function is constant-time. It returns nil
|
||||
// on success, and an error if the derived keys do not match.
|
||||
func CompareHashAndPassword(hash []byte, password []byte) error {
|
||||
// Decode existing hash, retrieve params and salt.
|
||||
params, salt, dk, err := decodeHash(hash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// scrypt the cleartext password with the same parameters and salt
|
||||
other, err := scrypt.Key(password, salt, params.N, params.R, params.P, params.DKLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Constant time comparison
|
||||
if subtle.ConstantTimeCompare(dk, other) == 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ErrMismatchedHashAndPassword
|
||||
}
|
||||
|
||||
// Check checks that the parameters are valid for input into the
|
||||
// scrypt key derivation function.
|
||||
func (p *Params) Check() error {
|
||||
// Validate N
|
||||
if p.N > maxInt || p.N <= 1 || p.N%2 != 0 {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate r
|
||||
if p.R < 1 || p.R > maxInt {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate p
|
||||
if p.P < 1 || p.P > maxInt {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate that r & p don't exceed 2^30 and that N, r, p values don't
|
||||
// exceed the limits defined by the scrypt algorithm.
|
||||
if uint64(p.R)*uint64(p.P) >= 1<<30 || p.R > maxInt/128/p.P || p.R > maxInt/256 || p.N > maxInt/128/p.R {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate the salt length
|
||||
if p.SaltLen < minSaltLen || p.SaltLen > maxInt {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
// Validate the derived key length
|
||||
if p.DKLen < minDKLen || p.DKLen > maxInt {
|
||||
return ErrInvalidParams
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeHash extracts the parameters, salt and derived key from the
|
||||
// provided hash. It returns an error if the hash format is invalid and/or
|
||||
// the parameters are invalid.
|
||||
func decodeHash(hash []byte) (Params, []byte, []byte, error) {
|
||||
vals := strings.Split(string(hash), "$")
|
||||
|
||||
// P, N, R, salt, scrypt derived key
|
||||
if len(vals) != 5 {
|
||||
return Params{}, nil, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
var params Params
|
||||
var err error
|
||||
|
||||
params.N, err = strconv.Atoi(vals[0])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
params.R, err = strconv.Atoi(vals[1])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
params.P, err = strconv.Atoi(vals[2])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
|
||||
salt, err := hex.DecodeString(vals[3])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
params.SaltLen = len(salt)
|
||||
|
||||
dk, err := hex.DecodeString(vals[4])
|
||||
if err != nil {
|
||||
return params, nil, nil, ErrInvalidHash
|
||||
}
|
||||
params.DKLen = len(dk)
|
||||
|
||||
if err := params.Check(); err != nil {
|
||||
return params, nil, nil, err
|
||||
}
|
||||
|
||||
return params, salt, dk, nil
|
||||
}
|
||||
|
||||
// Cost returns the scrypt parameters used to generate the derived key. This
|
||||
// allows a package user to increase the cost (in time & resources) used as
|
||||
// computational performance increases over time.
|
||||
func Cost(hash []byte) (Params, error) {
|
||||
params, _, _, err := decodeHash(hash)
|
||||
|
||||
return params, err
|
||||
}
|
||||
|
||||
// Calibrate returns the hardest parameters (not weaker than the given params),
|
||||
// allowed by the given limits.
|
||||
// The returned params will not use more memory than the given (MiB);
|
||||
// will not take more time than the given timeout, but more than timeout/2.
|
||||
//
|
||||
//
|
||||
// The default timeout (when the timeout arg is zero) is 200ms.
|
||||
// The default memMiBytes (when memMiBytes is zero) is 16MiB.
|
||||
// The default parameters (when params == Params{}) is DefaultParams.
|
||||
func Calibrate(timeout time.Duration, memMiBytes int, params Params) (Params, error) {
|
||||
p := params
|
||||
if p.N == 0 || p.R == 0 || p.P == 0 || p.SaltLen == 0 || p.DKLen == 0 {
|
||||
p = DefaultParams
|
||||
} else if err := p.Check(); err != nil {
|
||||
return p, err
|
||||
}
|
||||
if timeout == 0 {
|
||||
timeout = 200 * time.Millisecond
|
||||
}
|
||||
if memMiBytes == 0 {
|
||||
memMiBytes = 16
|
||||
}
|
||||
salt, err := GenerateRandomBytes(p.SaltLen)
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
password := []byte("weakpassword")
|
||||
|
||||
// First, we calculate the minimal required time.
|
||||
start := time.Now()
|
||||
if _, err := scrypt.Key(password, salt, p.N, p.R, p.P, p.DKLen); err != nil {
|
||||
return p, err
|
||||
}
|
||||
dur := time.Since(start)
|
||||
|
||||
for dur < timeout && p.N < maxInt>>1 {
|
||||
p.N <<= 1
|
||||
}
|
||||
|
||||
// Memory usage is at least 128 * r * N, see
|
||||
// http://blog.ircmaxell.com/2014/03/why-i-dont-recommend-scrypt.html
|
||||
// or https://drupal.org/comment/4675994#comment-4675994
|
||||
|
||||
var again bool
|
||||
memBytes := memMiBytes << 20
|
||||
// If we'd use more memory then the allowed, we can tune the memory usage
|
||||
for 128*p.R*p.N > memBytes {
|
||||
if p.R > 1 {
|
||||
// by lowering r
|
||||
p.R--
|
||||
} else if p.N > 16 {
|
||||
again = true
|
||||
p.N >>= 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !again {
|
||||
return p, p.Check()
|
||||
}
|
||||
|
||||
// We have to compensate the lowering of N, by increasing p.
|
||||
for i := 0; i < 10 && p.P > 0; i++ {
|
||||
start := time.Now()
|
||||
if _, err := scrypt.Key(password, salt, p.N, p.R, p.P, p.DKLen); err != nil {
|
||||
return p, err
|
||||
}
|
||||
dur := time.Since(start)
|
||||
if dur < timeout/2 {
|
||||
p.P = int(float64(p.P)*float64(timeout/dur) + 1)
|
||||
} else if dur > timeout && p.P > 1 {
|
||||
p.P--
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return p, p.Check()
|
||||
}
|
156
vendor/src/github.com/elithrar/simple-scrypt/scrypt_test.go
vendored
Normal file
156
vendor/src/github.com/elithrar/simple-scrypt/scrypt_test.go
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
package scrypt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Test cases
|
||||
var (
|
||||
testLengths = []int{1, 8, 16, 32, 100, 500, 2500}
|
||||
password = "super-secret-password"
|
||||
)
|
||||
|
||||
var testParams = []struct {
|
||||
pass bool
|
||||
params Params
|
||||
}{
|
||||
{true, Params{16384, 8, 1, 32, 64}},
|
||||
{true, Params{16384, 8, 1, 16, 32}},
|
||||
{true, Params{65536, 8, 1, 16, 64}},
|
||||
{true, Params{1048576, 8, 2, 64, 128}},
|
||||
{false, Params{-1, 8, 1, 16, 32}}, // invalid N
|
||||
{false, Params{0, 8, 1, 16, 32}}, // invalid N
|
||||
{false, Params{1 << 31, 8, 1, 16, 32}}, // invalid N
|
||||
{false, Params{16384, 0, 12, 16, 32}}, // invalid R
|
||||
{false, Params{16384, 8, 0, 16, 32}}, // invalid R > maxInt/128/P
|
||||
{false, Params{16384, 1 << 24, 1, 16, 32}}, // invalid R > maxInt/256
|
||||
{false, Params{1 << 31, 8, 0, 16, 32}}, // invalid p < 0
|
||||
{false, Params{4096, 8, 1, 5, 32}}, // invalid SaltLen
|
||||
{false, Params{4096, 8, 1, 16, 2}}, // invalid DKLen
|
||||
}
|
||||
|
||||
var testHashes = []struct {
|
||||
pass bool
|
||||
hash string
|
||||
}{
|
||||
{false, "1$8$1$9003d0e8e69482843e6bd560c2c9cd94$1976f233124e0ee32bb2678eb1b0ed668eb66cff6fa43279d1e33f6e81af893b"}, // N too small
|
||||
{false, "$9003d0e8e69482843e6bd560c2c9cd94$1976f233124e0ee32bb2678eb1b0ed668eb66cff6fa43279d1e33f6e81af893b"}, // too short
|
||||
{false, "16384#8#1#18fbc325efa37402d27c3c2172900cbf$d4e5e1b9eedc1a6a14aad6624ab57b7b42ae75b9c9845fde32de765835f2aaf9"}, // incorrect separators
|
||||
{false, "16384$nogood$1$18fbc325efa37402d27c3c2172900cbf$d4e5e1b9eedc1a6a14aad6624ab57b7b42ae75b9c9845fde32de765835f2aaf9"}, // invalid R
|
||||
{false, "16384$8$abc1$18fbc325efa37402d27c3c2172900cbf$d4e5e1b9eedc1a6a14aad6624ab57b7b42ae75b9c9845fde32de765835f2aaf9"}, // invalid P
|
||||
{false, "16384$8$1$Tk9QRQ==$d4e5e1b9eedc1a6a14aad6624ab57b7b42ae75b9c9845fde32de765835f2aaf9"}, // invalid salt (not hex)
|
||||
{false, "16384$8$1$18fbc325efa37402d27c3c2172900cbf$42ae====/75b9c9845fde32de765835f2aaf9"}, // invalid dk (not hex)
|
||||
}
|
||||
|
||||
func TestGenerateRandomBytes(t *testing.T) {
|
||||
for _, v := range testLengths {
|
||||
_, err := GenerateRandomBytes(v)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate random bytes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateFromPassword(t *testing.T) {
|
||||
for _, v := range testParams {
|
||||
_, err := GenerateFromPassword([]byte(password), v.params)
|
||||
if err != nil && v.pass == true {
|
||||
t.Fatalf("no error was returned when expected for params: %+v", v.params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareHashAndPassword(t *testing.T) {
|
||||
hash, err := GenerateFromPassword([]byte(password), DefaultParams)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := CompareHashAndPassword(hash, []byte(password)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := CompareHashAndPassword(hash, []byte("invalid-password")); err == nil {
|
||||
t.Fatalf("mismatched passwords did not produce an error")
|
||||
}
|
||||
|
||||
invalidHash := []byte("$166$$11$a2ad56a415af5")
|
||||
if err := CompareHashAndPassword(invalidHash, []byte(password)); err == nil {
|
||||
t.Fatalf("did not identify an invalid hash")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCost(t *testing.T) {
|
||||
hash, err := GenerateFromPassword([]byte(password), DefaultParams)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
params, err := Cost(hash)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(params, DefaultParams) {
|
||||
t.Fatal("cost mismatch: parameters used did not match those retrieved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeHash(t *testing.T) {
|
||||
for _, v := range testHashes {
|
||||
_, err := Cost([]byte(v.hash))
|
||||
if err == nil && v.pass == false {
|
||||
t.Fatal("invalid hash: did not correctly detect invalid password hash")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalibrate(t *testing.T) {
|
||||
timeout := 500 * time.Millisecond
|
||||
for testNum, tc := range []struct {
|
||||
MemMiB int
|
||||
}{
|
||||
{64},
|
||||
{32},
|
||||
{16},
|
||||
{8},
|
||||
{1},
|
||||
} {
|
||||
var (
|
||||
p Params
|
||||
err error
|
||||
)
|
||||
p, err = Calibrate(timeout, tc.MemMiB, p)
|
||||
if err != nil {
|
||||
t.Fatalf("%d. %#v: %v", testNum, p, err)
|
||||
}
|
||||
if (128*p.R*p.N)>>20 > tc.MemMiB {
|
||||
t.Errorf("%d. wanted memory limit %d, got %d.", testNum, tc.MemMiB, (128*p.R*p.N)>>20)
|
||||
}
|
||||
start := time.Now()
|
||||
_, err = GenerateFromPassword([]byte(password), p)
|
||||
dur := time.Since(start)
|
||||
t.Logf("GenerateFromPassword with %#v took %s (%v)", p, dur, err)
|
||||
if err != nil {
|
||||
t.Fatalf("%d. GenerateFromPassword with %#v: %v", testNum, p, err)
|
||||
}
|
||||
if dur < timeout/2 {
|
||||
t.Errorf("%d. GenerateFromPassword was too fast (wanted around %s, got %s) with %#v.", testNum, timeout, dur, p)
|
||||
} else if timeout*2 < dur {
|
||||
t.Errorf("%d. GenerateFromPassword took too long (wanted around %s, got %s) with %#v.", testNum, timeout, dur, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleCalibrate() {
|
||||
p, err := Calibrate(1*time.Second, 128, Params{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
dk, err := GenerateFromPassword([]byte("super-secret-password"), p)
|
||||
fmt.Printf("generated password is %q (%v)", dk, err)
|
||||
}
|
Loading…
Reference in a new issue