1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 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*int64(p.R)*int64(p.N) > int64(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()
}
|