pax_global_header00006660000000000000000000000064130140726420014511gustar00rootroot0000000000000052 comment=2325946f714c95de4a6088202c402fbdfa64163b simple-scrypt-1.2.0/000077500000000000000000000000001301407264200143245ustar00rootroot00000000000000simple-scrypt-1.2.0/.gitignore000066400000000000000000000004261301407264200163160ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof *.DS_Store simple-scrypt-1.2.0/.travis.yml000066400000000000000000000003061301407264200164340ustar00rootroot00000000000000language: go sudo: false go: - 1.2 - 1.3 - 1.4 - 1.5 - 1.6 - 1.7 - tip script: - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d -s .) - go tool vet . - go test -v ./... simple-scrypt-1.2.0/LICENSE000066400000000000000000000021301301407264200153250ustar00rootroot00000000000000The 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. simple-scrypt-1.2.0/README.md000066400000000000000000000121561301407264200156100ustar00rootroot00000000000000# 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. simple-scrypt-1.2.0/scrypt.go000066400000000000000000000205301301407264200161770ustar00rootroot00000000000000// 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() } simple-scrypt-1.2.0/scrypt_test.go000066400000000000000000000113441301407264200172410ustar00rootroot00000000000000package 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 - 1, 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 - 1, 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) }