aboutsummaryrefslogtreecommitdiff
path: root/crypto/crypto_test.go
diff options
context:
space:
mode:
authorJoe Richey <joerichey@google.com>2017-03-02 17:32:50 -0800
committerJoe Richey joerichey@google.com <joerichey@google.com>2017-05-02 13:39:18 -0700
commit3f9c09b1e0901248c96c47e392a2888c40b2f182 (patch)
treeabdf39c4db24c9b44826ab1838b609d4463ffb8d /crypto/crypto_test.go
parentee10adc91e79bca395a6b069797a99863fc957dd (diff)
crypto: passphrase hashing with Argon2
This commit adds in the PassphraseHash function which hashes the provided passphrase (in key form) using Argon2id. This cost parameters for Argon2id and that salt are both fed into the function. It also includes tests and benchmarks for the passphrase hashing. Change-Id: I060db3e71213c756d45ce5603a0a59d3d7a1e609
Diffstat (limited to 'crypto/crypto_test.go')
-rw-r--r--crypto/crypto_test.go148
1 files changed, 144 insertions, 4 deletions
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index fe5edf1..471d3ed 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -24,11 +24,12 @@ import (
"compress/zlib"
"crypto/aes"
"crypto/sha256"
+ "encoding/hex"
"fmt"
- "fscrypt/metadata"
- "fscrypt/util"
"os"
"testing"
+
+ . "fscrypt/metadata"
)
// Reader that always returns the same byte
@@ -48,15 +49,51 @@ func makeKey(b byte, n int) (*Key, error) {
var fakeValidDescriptor = "0123456789abcdef"
var fakeInvalidDescriptor = "123456789abcdef"
+var fakeSalt = bytes.Repeat([]byte{'a'}, SaltLen)
+var fakePassword = []byte("password")
var fakeValidPolicyKey, _ = makeKey(42, PolicyKeyLen)
var fakeInvalidPolicyKey, _ = makeKey(42, PolicyKeyLen-1)
var fakeWrappingKey, _ = makeKey(17, InternalKeyLen)
+// As the passpharase hashing function clears the passphrase, we need to make
+// a new passphrase key for each test
+func fakePassphraseKey() (*Key, error) {
+ return NewFixedLengthKeyFromReader(bytes.NewReader(fakePassword), len(fakePassword))
+}
+
+// Values for test cases pulled from argon2 command line tool.
+// To generate run:
+// echo "password" | argon2 "aaaaaaaaaaaaaaaa" -id -t <t> -m <m> -p <p> -l 32
+// where costs.Time = <t>, costs.Memory = 2^<m>, and costs.Parallelism = <p>.
+type hashTestCase struct {
+ costs *HashingCosts
+ hexHash string
+}
+
+var hashTestCases = []hashTestCase{
+ {
+ costs: &HashingCosts{Time: 1, Memory: 1 << 10, Parallelism: 1},
+ hexHash: "a66f5398e33761bf161fdf1273e99b148f07d88d12d85b7673fddd723f95ec34",
+ },
+ {
+ costs: &HashingCosts{Time: 10, Memory: 1 << 10, Parallelism: 1},
+ hexHash: "5fa2cb89db1f7413ba1776258b7c8ee8c377d122078d28fe1fd645c353787f50",
+ },
+ {
+ costs: &HashingCosts{Time: 1, Memory: 1 << 15, Parallelism: 1},
+ hexHash: "f474a213ed14d16ead619568000939b938ddfbd2ac4a82d253afa81b5ebaef84",
+ },
+ {
+ costs: &HashingCosts{Time: 1, Memory: 1 << 10, Parallelism: 10},
+ hexHash: "b7c3d7a0be222680b5ea3af3fb1a0b7b02b92cbd7007821dc8b84800c86c7783",
+ },
+}
+
// Checks that len(array) == expected
func lengthCheck(name string, array []byte, expected int) error {
if len(array) != expected {
- return util.InvalidLengthError(name, expected, len(array))
+ return fmt.Errorf("length of %s should be %d", name, expected)
}
return nil
}
@@ -320,7 +357,7 @@ func TestWrapTwiceDistinct(t *testing.T) {
}
// Attempts to Unwrap data with key after altering tweek, should fail
-func testFailWithTweek(key *Key, data *metadata.WrappedKeyData, tweek []byte) error {
+func testFailWithTweek(key *Key, data *WrappedKeyData, tweek []byte) error {
tweek[0]++
_, err := Unwrap(key, data)
tweek[0]--
@@ -354,6 +391,69 @@ func TestUnwrapWrongData(t *testing.T) {
}
}
+// Run our test cases for passphrase hashing
+func TestPassphraseHashing(t *testing.T) {
+ for i, testCase := range hashTestCases {
+ pk, err := fakePassphraseKey()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer pk.Wipe()
+
+ hash, err := PassphraseHash(pk, fakeSalt, testCase.costs)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer hash.Wipe()
+
+ actual := hex.EncodeToString(hash.data)
+ if actual != testCase.hexHash {
+ t.Errorf("Hash test %d: for costs=%+v expected hash of %q got %q",
+ i, testCase.costs, testCase.hexHash, actual)
+ }
+ }
+}
+
+func TestBadTime(t *testing.T) {
+ pk, err := fakePassphraseKey()
+ if err != nil {
+ t.Fatal(err)
+ }
+ costs := *hashTestCases[0].costs
+ costs.Time = 0
+ _, err = PassphraseHash(pk, fakeSalt, &costs)
+ if err == nil {
+ t.Errorf("time cost of %d should be invalid", costs.Time)
+ }
+}
+
+func TestBadMemory(t *testing.T) {
+ pk, err := fakePassphraseKey()
+ if err != nil {
+ t.Fatal(err)
+ }
+ costs := *hashTestCases[0].costs
+ costs.Memory = 7
+ _, err = PassphraseHash(pk, fakeSalt, &costs)
+ if err == nil {
+ t.Errorf("memory cost of %d should be invalid", costs.Memory)
+ }
+}
+
+func TestBadParallelism(t *testing.T) {
+ pk, err := fakePassphraseKey()
+ if err != nil {
+ t.Fatal(err)
+ }
+ costs := *hashTestCases[0].costs
+ costs.Parallelism = 1 << 24
+ costs.Memory = 1 << 27 // Running n threads requires at least 8*n memory
+ _, err = PassphraseHash(pk, fakeSalt, &costs)
+ if err == nil {
+ t.Errorf("parallelism cost of %d should be invalid", costs.Parallelism)
+ }
+}
+
func BenchmarkWrap(b *testing.B) {
for n := 0; n < b.N; n++ {
Wrap(fakeWrappingKey, fakeValidPolicyKey)
@@ -391,3 +491,43 @@ func BenchmarkRandomWrapUnwrap(b *testing.B) {
sk.Wipe()
}
}
+
+func benchmarkPassphraseHashing(b *testing.B, costs *HashingCosts) {
+ for n := 0; n < b.N; n++ {
+ pk, err := fakePassphraseKey()
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer pk.Wipe()
+ hash, err := PassphraseHash(pk, fakeSalt, costs)
+ hash.Wipe()
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkPassphraseHashing_1MB_1Thread(b *testing.B) {
+ benchmarkPassphraseHashing(b,
+ &HashingCosts{Time: 1, Memory: 1 << 10, Parallelism: 1})
+}
+
+func BenchmarkPassphraseHashing_1GB_1Thread(b *testing.B) {
+ benchmarkPassphraseHashing(b,
+ &HashingCosts{Time: 1, Memory: 1 << 20, Parallelism: 1})
+}
+
+func BenchmarkPassphraseHashing_128MB_1Thread(b *testing.B) {
+ benchmarkPassphraseHashing(b,
+ &HashingCosts{Time: 1, Memory: 1 << 17, Parallelism: 1})
+}
+
+func BenchmarkPassphraseHashing_128MB_8Thread(b *testing.B) {
+ benchmarkPassphraseHashing(b,
+ &HashingCosts{Time: 1, Memory: 1 << 17, Parallelism: 8})
+}
+
+func BenchmarkPassphraseHashing_128MB_8Pass(b *testing.B) {
+ benchmarkPassphraseHashing(b,
+ &HashingCosts{Time: 8, Memory: 1 << 17, Parallelism: 1})
+}