Skip to content

Commit c9a1c8a

Browse files
authored
Merge PR #3638: Document and justify bcrypt parameters
2 parents c71893e + bb1fd58 commit c9a1c8a

File tree

4 files changed

+65
-1
lines changed

4 files changed

+65
-1
lines changed

PENDING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ IMPROVEMENTS
3535

3636
* SDK
3737

38+
* \#3638 Add Bcrypt benchmarks & justification of security parameter choice
39+
3840
* Tendermint
3941
* [\#3618] Upgrade to Tendermint 0.30.03
4042

crypto/keys/mintkey/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Security parameter choice
2+
-------------------------
3+
4+
The present Bcrypt security parameter used is 12, which should take about a quarter of a second on midrange consumer hardware (see [Benchmarking](#benchmarking) section below).
5+
6+
For some background into security parameter considerations, see [here](https://auth0.com/blog/hashing-in-action-understanding-bcrypt/) and [here](https://security.stackexchange.com/questions/3959/recommended-of-iterations-when-using-pkbdf2-sha256/3993#3993).
7+
8+
Given our security model, where an attacker would need to already have access to a victim's computer and copy the `~/.gaiacli` directory (as opposed to e.g. web authentication), this parameter choice seems sufficient for the time being. Bcrypt always generates a 448-bit key, so the security in practice is determined by the length & complexity of a user's password and the time taken to generate a Bcrypt key from their password (which we can choose with the security parameter). Users would be well-advised to use difficult-to-guess passwords.
9+
10+
Benchmarking
11+
------------
12+
13+
To run Bcrypt benchmarks:
14+
15+
```bash
16+
go test -v --bench github.com/cosmos/cosmos-sdk/crypto/keys/mintkey
17+
```
18+
19+
On the test machine (midrange ThinkPad; i7 6600U), this results in:
20+
21+
```bash
22+
goos: linux
23+
goarch: amd64
24+
pkg: github.com/cosmos/cosmos-sdk/crypto/keys/mintkey
25+
BenchmarkBcryptGenerateFromPassword/benchmark-security-param-9-4 50 34609268 ns/op
26+
BenchmarkBcryptGenerateFromPassword/benchmark-security-param-10-4 20 67874471 ns/op
27+
BenchmarkBcryptGenerateFromPassword/benchmark-security-param-11-4 10 135515404 ns/op
28+
BenchmarkBcryptGenerateFromPassword/benchmark-security-param-12-4 5 274824600 ns/op
29+
BenchmarkBcryptGenerateFromPassword/benchmark-security-param-13-4 2 547012903 ns/op
30+
BenchmarkBcryptGenerateFromPassword/benchmark-security-param-14-4 1 1083685904 ns/op
31+
BenchmarkBcryptGenerateFromPassword/benchmark-security-param-15-4 1 2183674041 ns/op
32+
PASS
33+
ok github.com/cosmos/cosmos-sdk/crypto/keys/mintkey 12.093s
34+
```
35+
36+
Benchmark results are in nanoseconds, so security parameter 12 takes about a quarter of a second to generate the Bcrypt key, security param 13 takes half a second, and so on.

crypto/keys/mintkey/mintkey.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const (
3434
// variables in runtime), one can cause the user to sign a different tx
3535
// than what they see, which is a significantly cheaper attack then breaking
3636
// a bcrypt hash. (Recall that the nonce still exists to break rainbow tables)
37-
// TODO: Consider increasing default
37+
// For further notes on security parameter choice, see README.md
3838
var BcryptSecurityParameter = 12
3939

4040
//-----------------------------------------------------------------
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package mintkey
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
"golang.org/x/crypto/bcrypt"
9+
10+
"github.com/tendermint/tendermint/crypto"
11+
)
12+
13+
func BenchmarkBcryptGenerateFromPassword(b *testing.B) {
14+
passphrase := []byte("passphrase")
15+
for securityParam := 9; securityParam < 16; securityParam++ {
16+
param := securityParam
17+
b.Run(fmt.Sprintf("benchmark-security-param-%d", param), func(b *testing.B) {
18+
saltBytes := crypto.CRandBytes(16)
19+
b.ResetTimer()
20+
for i := 0; i < b.N; i++ {
21+
_, err := bcrypt.GenerateFromPassword(saltBytes, passphrase, param)
22+
require.Nil(b, err)
23+
}
24+
})
25+
}
26+
}

0 commit comments

Comments
 (0)