diff options
Diffstat (limited to 'crypto/crypto_test.go')
| -rw-r--r-- | crypto/crypto_test.go | 207 |
1 files changed, 87 insertions, 120 deletions
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 444f847..1fa5a0c 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -30,11 +30,7 @@ import ( "os" "testing" - "golang.org/x/sys/unix" - "github.com/google/fscrypt/metadata" - "github.com/google/fscrypt/security" - "github.com/google/fscrypt/util" ) // Reader that always returns the same byte @@ -53,19 +49,14 @@ func makeKey(b byte, n int) (*Key, error) { } var ( - fakeValidDescriptor = "0123456789abcdef" - fakeSalt = bytes.Repeat([]byte{'a'}, metadata.SaltLen) - fakePassword = []byte("password") - defaultService = unix.FS_KEY_DESC_PREFIX - - fakeValidPolicyKey, _ = makeKey(42, metadata.PolicyKeyLen) - fakeInvalidPolicyKey, _ = makeKey(42, metadata.PolicyKeyLen-1) - fakeWrappingKey, _ = makeKey(17, metadata.InternalKeyLen) + fakeSalt = bytes.Repeat([]byte{'a'}, metadata.SaltLen) + fakePassword = []byte("password") - testUser, _ = util.EffectiveUser() + fakeValidPolicyKey, _ = makeKey(42, metadata.PolicyKeyLen) + fakeWrappingKey, _ = makeKey(17, metadata.InternalKeyLen) ) -// As the passpharase hashing function clears the passphrase, we need to make +// As the passphrase 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)) @@ -73,7 +64,9 @@ func fakePassphraseKey() (*Key, error) { // 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 +// +// 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 *metadata.HashingCosts @@ -85,6 +78,12 @@ var hashTestCases = []hashTestCase{ costs: &metadata.HashingCosts{Time: 1, Memory: 1 << 10, Parallelism: 1}, hexHash: "a66f5398e33761bf161fdf1273e99b148f07d88d12d85b7673fddd723f95ec34", }, + // Make sure we maintain our backwards compatible behavior, where + // Parallelism is truncated to 8-bits unless TruncationFixed is true. + { + costs: &metadata.HashingCosts{Time: 1, Memory: 1 << 10, Parallelism: 257}, + hexHash: "a66f5398e33761bf161fdf1273e99b148f07d88d12d85b7673fddd723f95ec34", + }, { costs: &metadata.HashingCosts{Time: 10, Memory: 1 << 10, Parallelism: 1}, hexHash: "5fa2cb89db1f7413ba1776258b7c8ee8c377d122078d28fe1fd645c353787f50", @@ -97,6 +96,15 @@ var hashTestCases = []hashTestCase{ costs: &metadata.HashingCosts{Time: 1, Memory: 1 << 10, Parallelism: 10}, hexHash: "b7c3d7a0be222680b5ea3af3fb1a0b7b02b92cbd7007821dc8b84800c86c7783", }, + { + costs: &metadata.HashingCosts{Time: 1, Memory: 1 << 11, Parallelism: 255}, + hexHash: "d51af3775bbdd0cba31d96fd6d921d9de27f521ceffe667618cd7624f6643071", + }, + // Adding TruncationFixed shouldn't matter if Parallelism < 256. + { + costs: &metadata.HashingCosts{Time: 1, Memory: 1 << 11, Parallelism: 255, TruncationFixed: true}, + hexHash: "d51af3775bbdd0cba31d96fd6d921d9de27f521ceffe667618cd7624f6643071", + }, } // Checks that len(array) == expected @@ -158,7 +166,7 @@ func TestZeroLength(t *testing.T) { } defer key1.Wipe() if key1.data != nil { - t.Error("FIxed length key from reader contained data") + t.Error("Fixed length key from reader contained data") } key2, err := NewKeyFromReader(bytes.NewReader(nil)) @@ -171,7 +179,7 @@ func TestZeroLength(t *testing.T) { } } -// Test that enabling the disabling memory locking succeeds even if a key is +// Test that enabling then disabling memory locking succeeds even if a key is // active when the variable changes. func TestEnableDisableMemoryLocking(t *testing.T) { // Mlock on for creation, off for wiping @@ -242,47 +250,10 @@ func TestKeyLargeResize(t *testing.T) { } } -// Adds and removes a key with various services. -func TestAddRemoveKeys(t *testing.T) { - for _, service := range []string{defaultService, "ext4:", "f2fs:"} { - validDescription := service + fakeValidDescriptor - if err := InsertPolicyKey(fakeValidPolicyKey, validDescription, testUser); err != nil { - t.Error(err) - } - if err := security.RemoveKey(validDescription, testUser); err != nil { - t.Error(err) - } - } -} - -// Adds a key twice (both should succeed) -func TestAddTwice(t *testing.T) { - validDescription := defaultService + fakeValidDescriptor - InsertPolicyKey(fakeValidPolicyKey, validDescription, testUser) - if InsertPolicyKey(fakeValidPolicyKey, validDescription, testUser) != nil { - t.Error("InsertPolicyKey should not fail if key already exists") - } - security.RemoveKey(validDescription, testUser) -} - -// Makes sure a key fails with bad policy or service -func TestBadAddKeys(t *testing.T) { - validDescription := defaultService + fakeValidDescriptor - if InsertPolicyKey(fakeInvalidPolicyKey, validDescription, testUser) == nil { - security.RemoveKey(validDescription, testUser) - t.Error("InsertPolicyKey should fail with bad policy key") - } - invalidDescription := "ext4" + fakeValidDescriptor - if InsertPolicyKey(fakeValidPolicyKey, invalidDescription, testUser) == nil { - security.RemoveKey(invalidDescription, testUser) - t.Error("InsertPolicyKey should fail with bad service") - } -} - // Check that we can create random keys. All this test does to test the // "randomness" is generate a page of random bytes and attempts compression. // If the data can be compressed it is probably not very random. This isn't -// indented to be a sufficient test for randomness (which is impossible), but a +// intended to be a sufficient test for randomness (which is impossible), but a // way to catch simple regressions (key is all zeros or contains a repeating // pattern). func TestRandomKeyGen(t *testing.T) { @@ -303,7 +274,7 @@ func TestBigKeyGen(t *testing.T) { case nil: key.Wipe() return - case ErrKeyLock: + case ErrMlockUlimit: // Don't fail just because "ulimit -l" is too low. return default: @@ -456,7 +427,7 @@ func TestWrongUnwrappingKeyLength(t *testing.T) { } } -// Wraping twice with the same keys should give different components +// Wrapping twice with the same keys should give different components func TestWrapTwiceDistinct(t *testing.T) { data1, err := Wrap(fakeWrappingKey, fakeValidPolicyKey) if err != nil { @@ -472,14 +443,14 @@ 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 { - tweek[0]++ +// Attempts to Unwrap data with key after altering tweak, should fail +func testFailWithTweak(key *Key, data *metadata.WrappedKeyData, tweak []byte) error { + tweak[0]++ key, err := Unwrap(key, data) if err == nil { key.Wipe() } - tweek[0]-- + tweak[0]-- return err } @@ -489,7 +460,7 @@ func TestUnwrapWrongKey(t *testing.T) { if err != nil { t.Fatal(err) } - if testFailWithTweek(fakeWrappingKey, data, fakeWrappingKey.data) == nil { + if testFailWithTweak(fakeWrappingKey, data, fakeWrappingKey.data) == nil { t.Error("using a different wrapping key should make unwrap fail") } } @@ -499,96 +470,92 @@ func TestUnwrapWrongData(t *testing.T) { if err != nil { t.Fatal(err) } - if testFailWithTweek(fakeWrappingKey, data, data.EncryptedKey) == nil { + if testFailWithTweak(fakeWrappingKey, data, data.EncryptedKey) == nil { t.Error("changing encryption key should make unwrap fail") } - if testFailWithTweek(fakeWrappingKey, data, data.IV) == nil { + if testFailWithTweak(fakeWrappingKey, data, data.IV) == nil { t.Error("changing IV should make unwrap fail") } - if testFailWithTweek(fakeWrappingKey, data, data.Hmac) == nil { + if testFailWithTweak(fakeWrappingKey, data, data.Hmac) == nil { t.Error("changing HMAC should make unwrap fail") } } -// 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() +func TestComputeKeyDescriptorV1(t *testing.T) { + descriptor, err := ComputeKeyDescriptor(fakeValidPolicyKey, 1) if err != nil { t.Fatal(err) } - defer pk.Wipe() - - 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) + if descriptor != "8290608a029c5aae" { + t.Errorf("wrong v1 descriptor: %s", descriptor) } } -func TestBadMemory(t *testing.T) { - pk, err := fakePassphraseKey() +func TestComputeKeyDescriptorV2(t *testing.T) { + descriptor, err := ComputeKeyDescriptor(fakeValidPolicyKey, 2) if err != nil { t.Fatal(err) } - defer pk.Wipe() + if descriptor != "2139f52bf8386ee99845818ac7e91c4a" { + t.Errorf("wrong v2 descriptor: %s", descriptor) + } +} - costs := *hashTestCases[0].costs - costs.Memory = 7 - _, err = PassphraseHash(pk, fakeSalt, &costs) +func TestComputeKeyDescriptorBadVersion(t *testing.T) { + _, err := ComputeKeyDescriptor(fakeValidPolicyKey, 0) if err == nil { - t.Errorf("memory cost of %d should be invalid", costs.Memory) + t.Error("computing key descriptor with bad version should fail") } } -func TestBadParallelism(t *testing.T) { +// Run our test cases for passphrase hashing +func TestPassphraseHashing(t *testing.T) { pk, err := fakePassphraseKey() if err != nil { t.Fatal(err) } defer pk.Wipe() - 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) + for i, testCase := range hashTestCases { + if err := testCase.costs.CheckValidity(); err != nil { + t.Errorf("Hash test %d: for costs=%+v hashing failed: %v", i, testCase.costs, err) + continue + } + hash, err := PassphraseHash(pk, fakeSalt, testCase.costs) + if err != nil { + t.Errorf("Hash test %d: for costs=%+v hashing failed: %v", i, testCase.costs, err) + continue + } + 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 TestBadSalt(t *testing.T) { - pk, err := fakePassphraseKey() - if err != nil { - t.Fatal(err) - } - defer pk.Wipe() +var badCosts = []*metadata.HashingCosts{ + // Bad Time costs + {Time: 0, Memory: 1 << 11, Parallelism: 1}, + {Time: 1 << 33, Memory: 1 << 11, Parallelism: 1}, + // Bad Memory costs + {Time: 1, Memory: 5, Parallelism: 1}, + {Time: 1, Memory: 1 << 33, Parallelism: 1}, + // Bad Parallelism costs + {Time: 1, Memory: 1 << 11, Parallelism: 0, TruncationFixed: false}, + {Time: 1, Memory: 1 << 11, Parallelism: 0, TruncationFixed: true}, + {Time: 1, Memory: 1 << 11, Parallelism: 256, TruncationFixed: false}, + {Time: 1, Memory: 1 << 11, Parallelism: 256, TruncationFixed: true}, + {Time: 1, Memory: 1 << 11, Parallelism: 257, TruncationFixed: true}, +} - _, err = PassphraseHash(pk, []byte{1, 2, 3, 4}, hashTestCases[0].costs) - if err == nil { - t.Error("too short of salt should be invalid") +func TestBadParameters(t *testing.T) { + for i, costs := range badCosts { + if costs.CheckValidity() == nil { + t.Errorf("Hash test %d: expected error for costs=%+v", i, costs) + } } } |