Merge pull request #1702 from restic/update-simple-scrypt

Update github.com/elithrar/simple-scrypt
This commit is contained in:
Alexander Neumann 2018-04-02 20:07:18 +02:00
commit 4478d633e2
3 changed files with 62 additions and 45 deletions

6
Gopkg.lock generated
View File

@ -52,8 +52,8 @@
[[projects]]
name = "github.com/elithrar/simple-scrypt"
packages = ["."]
revision = "2325946f714c95de4a6088202c402fbdfa64163b"
version = "v1.2.0"
revision = "d150773194090feb6c897805a7bcea8d49544e2c"
version = "v1.3.0"
[[projects]]
name = "github.com/go-ini/ini"
@ -184,7 +184,7 @@
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["context","context/ctxhttp","idna","lex/httplex"]
packages = ["context","context/ctxhttp","http2","http2/hpack","idna","lex/httplex"]
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
[[projects]]

View File

@ -214,8 +214,7 @@ func Cost(hash []byte) (Params, error) {
return params, err
}
// Calibrate returns the hardest parameters (not weaker than the given params),
// allowed by the given limits.
// Calibrate returns the hardest parameters, 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.
//
@ -242,54 +241,69 @@ func Calibrate(timeout time.Duration, memMiBytes int, params Params) (Params, er
}
password := []byte("weakpassword")
// First, we calculate the minimal required time.
// r is fixed to 8 and should not be used to tune the memory usage
// if the cache lines of future processors are bigger, then r should be increased
// see: https://blog.filippo.io/the-scrypt-parameters/
p.R = 8
// Scrypt runs p independent mixing functions with a memory requirement of roughly
// 128 * r * N. Depending on the implementation these can be run sequentially or parallel.
// The go implementation runs them sequentially, therefore p can be used to adjust the execution time of scrypt.
// we start with p=1 and only increase it if we have to
p.P = 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
// calculate N based on the desired memory usage
memBytes := memMiBytes << 20
p.N = 1
for 128*int64(p.R)*int64(p.N) < int64(memBytes) {
p.N <<= 1
}
p.N >>= 1
// calculate the current execution 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
}
// reduce N if scrypt takes to long
for dur > timeout {
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()
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
}
dur = time.Since(start)
}
// try to reach desired timeout by increasing p
// the further away we are from timeout the bigger the steps should be
for dur < timeout {
// the theoretical optimal p; can not be used because of inaccurate measuring
optimalP := int(int64(timeout) / (int64(dur) / int64(p.P)))
if optimalP > p.P+1 {
// use average between optimal p and current p
p.P = (p.P + optimalP) / 2
} else {
p.P++
}
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)
}
// lower by one to get shorter duration than timeout
p.P--
return p, p.Check()
}

View File

@ -114,6 +114,9 @@ func TestCalibrate(t *testing.T) {
for testNum, tc := range []struct {
MemMiB int
}{
{512},
{256},
{128},
{64},
{32},
{16},
@ -139,9 +142,9 @@ func TestCalibrate(t *testing.T) {
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)
t.Errorf("%d. GenerateFromPassword was too fast (expected between %s and %s, got %s) with %#v.", testNum, timeout/2, timeout+timeout/2, dur, p)
} else if timeout+timeout/2 < dur {
t.Errorf("%d. GenerateFromPassword took too long (expected between %s and %s, got %s) with %#v.", testNum, timeout/2, timeout+timeout/2, dur, p)
}
}
}