diff --git a/.gitattributes b/.gitattributes index a369d6c..bb7ec5e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ *.age binary -*.test binary +testdata/testkit/* binary diff --git a/cmd/age/testdata/fail_large_filekey_scrypt.age b/cmd/age/testdata/fail_large_filekey_scrypt.age deleted file mode 100644 index 4951b64..0000000 Binary files a/cmd/age/testdata/fail_large_filekey_scrypt.age and /dev/null differ diff --git a/cmd/age/testdata/fail_scrypt_and_x25519.age b/cmd/age/testdata/fail_scrypt_and_x25519.age deleted file mode 100644 index 25f39f0..0000000 Binary files a/cmd/age/testdata/fail_scrypt_and_x25519.age and /dev/null differ diff --git a/cmd/age/testdata/fail_scrypt_work_factor_23.age b/cmd/age/testdata/fail_scrypt_work_factor_23.age deleted file mode 100644 index 16846b1..0000000 Binary files a/cmd/age/testdata/fail_scrypt_work_factor_23.age and /dev/null differ diff --git a/cmd/age/testdata/good_scrypt_work_factor_10.age b/cmd/age/testdata/good_scrypt_work_factor_10.age deleted file mode 100644 index 0e1d6eb..0000000 Binary files a/cmd/age/testdata/good_scrypt_work_factor_10.age and /dev/null differ diff --git a/internal/testkit/testkit.go b/internal/testkit/testkit.go index e459095..fd37579 100644 --- a/internal/testkit/testkit.go +++ b/internal/testkit/testkit.go @@ -12,6 +12,7 @@ import ( "fmt" "io" "os" + "strconv" "strings" "filippo.io/age/internal/bech32" @@ -19,6 +20,7 @@ import ( "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/hkdf" + "golang.org/x/crypto/scrypt" ) var TestFileKey = []byte("YELLOW SUBMARINE") @@ -32,13 +34,14 @@ type TestFile struct { Buf bytes.Buffer Rand func(n int) []byte - fileKey []byte - streamKey []byte - nonce [12]byte - payload bytes.Buffer - expect string - comment string - identities []string + fileKey []byte + streamKey []byte + nonce [12]byte + payload bytes.Buffer + expect string + comment string + identities []string + passphrases []string } func NewTestFile() *TestFile { @@ -117,6 +120,23 @@ func (f *TestFile) X25519NoRecordIdentity(identity []byte) { f.AEADBody(key, f.fileKey) } +func (f *TestFile) Scrypt(passphrase string, workFactor int) { + f.ScryptRecordPassphrase(passphrase) + f.ScryptNoRecordPassphrase(passphrase, workFactor) +} + +func (f *TestFile) ScryptRecordPassphrase(passphrase string) { + f.passphrases = append(f.passphrases, passphrase) +} + +func (f *TestFile) ScryptNoRecordPassphrase(passphrase string, workFactor int) { + salt := f.Rand(16) + f.ArgsLine("scrypt", b64(salt), strconv.Itoa(workFactor)) + key, _ := scrypt.Key([]byte(passphrase), append([]byte("age-encryption.org/v1/scrypt"), salt...), + 1< %s\n", generator, vector) out, err := exec.Command("go", "run", generator).Output() if err != nil { @@ -56,7 +57,7 @@ func TestMain(m *testing.M) { } func TestVectors(t *testing.T) { - tests, err := filepath.Glob("testdata/*.test") + tests, err := filepath.Glob("testdata/testkit/*") if err != nil { log.Fatal(err) } @@ -65,8 +66,7 @@ func TestVectors(t *testing.T) { if err != nil { t.Fatal(err) } - name := strings.TrimPrefix(test, "testdata/") - name = strings.TrimSuffix(name, ".test") + name := strings.TrimPrefix(test, "testdata/testkit/") t.Run(name, func(t *testing.T) { testVector(t, contents) }) @@ -114,6 +114,12 @@ func testVector(t *testing.T, test []byte) { t.Fatal(err) } identities = append(identities, i) + case "passphrase": + i, err := age.NewScryptIdentity(value) + if err != nil { + t.Fatal(err) + } + identities = append(identities, i) case "file key": // Ignored. case "comment": diff --git a/testdata/bad_hmac.go b/tests/bad_hmac.go similarity index 100% rename from testdata/bad_hmac.go rename to tests/bad_hmac.go diff --git a/testdata/long_file_key.go b/tests/long_file_key.go similarity index 100% rename from testdata/long_file_key.go rename to tests/long_file_key.go diff --git a/tests/long_file_key_scrypt.go b/tests/long_file_key_scrypt.go new file mode 100644 index 0000000..1ba3260 --- /dev/null +++ b/tests/long_file_key_scrypt.go @@ -0,0 +1,21 @@ +// Copyright 2022 The age Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import "filippo.io/age/internal/testkit" + +func main() { + f := testkit.NewTestFile() + f.FileKey([]byte("A LONGER YELLOW SUBMARINE")) + f.VersionLine("v1") + f.Scrypt("password", 10) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Comment("the file key must be checked to be 16 bytes before decrypting it") + f.Generate() +} diff --git a/tests/scrypt.go b/tests/scrypt.go new file mode 100644 index 0000000..53f540f --- /dev/null +++ b/tests/scrypt.go @@ -0,0 +1,18 @@ +// Copyright 2022 The age Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import "filippo.io/age/internal/testkit" + +func main() { + f := testkit.NewTestFile() + f.VersionLine("v1") + f.Scrypt("password", 10) + f.HMAC() + f.Payload("age") + f.Generate() +} diff --git a/tests/scrypt_and_x25519.go b/tests/scrypt_and_x25519.go new file mode 100644 index 0000000..03704d0 --- /dev/null +++ b/tests/scrypt_and_x25519.go @@ -0,0 +1,21 @@ +// Copyright 2022 The age Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import "filippo.io/age/internal/testkit" + +func main() { + f := testkit.NewTestFile() + f.VersionLine("v1") + f.X25519NoRecordIdentity(testkit.TestX25519Identity) + f.Scrypt("password", 10) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Comment("scrypt stanzas must be alone in the header") + f.Generate() +} diff --git a/tests/scrypt_no_match.go b/tests/scrypt_no_match.go new file mode 100644 index 0000000..a90f53f --- /dev/null +++ b/tests/scrypt_no_match.go @@ -0,0 +1,20 @@ +// Copyright 2022 The age Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import "filippo.io/age/internal/testkit" + +func main() { + f := testkit.NewTestFile() + f.VersionLine("v1") + f.ScryptRecordPassphrase("wrong") + f.ScryptNoRecordPassphrase("password", 10) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_23.go b/tests/scrypt_work_factor_23.go new file mode 100644 index 0000000..7586195 --- /dev/null +++ b/tests/scrypt_work_factor_23.go @@ -0,0 +1,23 @@ +// Copyright 2022 The age Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import "filippo.io/age/internal/testkit" + +func main() { + f := testkit.NewTestFile() + f.VersionLine("v1") + // Hardcoded because it would be too slow to regenerate every time. + // f.Scrypt("password", 23) + f.ArgsLine("scrypt", "rF0/NwblUHHTpgQgRpe5CQ", "23") + f.TextLine("qW9eVsT0NVb/Vswtw8kPIxUnaYmm9Px1dYmq2+4+qZA") + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Comment("work factor is very high, would take a long time to compute") + f.Generate() +} diff --git a/testdata/stanza_empty_body.go b/tests/stanza_empty_body.go similarity index 100% rename from testdata/stanza_empty_body.go rename to tests/stanza_empty_body.go diff --git a/testdata/stanza_empty_last_line.go b/tests/stanza_empty_last_line.go similarity index 100% rename from testdata/stanza_empty_last_line.go rename to tests/stanza_empty_last_line.go diff --git a/testdata/stanza_missing_body.go b/tests/stanza_missing_body.go similarity index 100% rename from testdata/stanza_missing_body.go rename to tests/stanza_missing_body.go diff --git a/testdata/stanza_missing_final_line.go b/tests/stanza_missing_final_line.go similarity index 100% rename from testdata/stanza_missing_final_line.go rename to tests/stanza_missing_final_line.go diff --git a/testdata/stanza_multiple_short_lines.go b/tests/stanza_multiple_short_lines.go similarity index 100% rename from testdata/stanza_multiple_short_lines.go rename to tests/stanza_multiple_short_lines.go diff --git a/testdata/stream_empty_payload.go b/tests/stream_empty_payload.go similarity index 100% rename from testdata/stream_empty_payload.go rename to tests/stream_empty_payload.go diff --git a/testdata/stream_last_chunk_empty.go b/tests/stream_last_chunk_empty.go similarity index 100% rename from testdata/stream_last_chunk_empty.go rename to tests/stream_last_chunk_empty.go diff --git a/testdata/stream_last_chunk_full.go b/tests/stream_last_chunk_full.go similarity index 100% rename from testdata/stream_last_chunk_full.go rename to tests/stream_last_chunk_full.go diff --git a/tests/valid_characters.go b/tests/valid_characters.go new file mode 100644 index 0000000..b617d69 --- /dev/null +++ b/tests/valid_characters.go @@ -0,0 +1,21 @@ +// Copyright 2022 The age Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import "filippo.io/age/internal/testkit" + +func main() { + f := testkit.NewTestFile() + f.VersionLine("v1") + f.ArgsLine("!\"#$%&'", "()*+,-./", "01234567", "89:;<=>?", "@ABCDEFG", + "HIJKLMNO", "PQRSTUVW", "XYZ[\\]^_", "`abcdefg", "hijklmno", "pqrstuvw", "xyz{|}~") + f.Body([]byte("")) + f.X25519(testkit.TestX25519Recipient) + f.HMAC() + f.Payload("age") + f.Generate() +} diff --git a/testdata/x25519.go b/tests/x25519.go similarity index 100% rename from testdata/x25519.go rename to tests/x25519.go diff --git a/tests/x25519_multiple_recipients.go b/tests/x25519_multiple_recipients.go new file mode 100644 index 0000000..983e0c6 --- /dev/null +++ b/tests/x25519_multiple_recipients.go @@ -0,0 +1,19 @@ +// Copyright 2022 The age Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import "filippo.io/age/internal/testkit" + +func main() { + f := testkit.NewTestFile() + f.VersionLine("v1") + f.X25519NoRecordIdentity(f.Rand(32)) + f.X25519(testkit.TestX25519Recipient) + f.HMAC() + f.Payload("age") + f.Generate() +} diff --git a/testdata/no_match.go b/tests/x25519_no_match.go similarity index 100% rename from testdata/no_match.go rename to tests/x25519_no_match.go