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 --- a/cmd/age/testdata/fail_large_filekey_scrypt.age +++ /dev/null @@ -1,7 +0,0 @@ -age-encryption.org/v1 --> scrypt z8U9dYMQuK1fFdvtpQYLEQ 10 -5SVjw1bbFCZLdI1FR7RqfTd3yWo4KS1ikOjvz60Bpqhrv0W6o6/2oszxZEm1gEUC - ---- YXxSwONGPBbV7woMuEFTYuA03qTYUF1k0Y8j/NDEu1o - -! i.f{dIE] n$!b2 \ No newline at end of file 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 --- a/cmd/age/testdata/fail_scrypt_work_factor_23.age +++ /dev/null @@ -1,5 +0,0 @@ -age-encryption.org/v1 --> scrypt 1Q6WlGmsRulbN7bmUw8A1Q 23 -GP2lnzFuk1dgEkcMPmK6KkmuOm5gIWJzLeuwGcRsvAY ---- vXvOsVbDbMc0x5Js1FS6k1ViOJ3H2ZdSUZo9bfvbzmU -=œv>vhKM'NeS\(_ \ No newline at end of file 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 --- a/cmd/age/testdata/good_scrypt_work_factor_10.age +++ /dev/null @@ -1,5 +0,0 @@ -age-encryption.org/v1 --> scrypt qEa/WztCd2KJ4mKwNf1Yrw 10 -TQZ4GpAaH4aR4oSDWZTgeRT4wRby4jwmtB02dElWmVQ ---- kOiEP6uoMyK9GKIsV77o4oaPuEr2Q0vdcu+1RKC3lLU -hoPV w\5~4nEod>rOmۨ \ No newline at end of file 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< X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE +-> scrypt 7s9ix86RtDMnTmjU8vkTTA 10 +0U4Pbxsl9pr9g4nHjPgkvtYkNrGiYJ43x1vbM5X5mhg +--- f2AoyFXU2R5Cn7s38vH1pFkuKqzPh3ibwwHc/7y6RRU +[. #wυ=aYkz66ڦRL \ No newline at end of file diff --git a/testdata/testkit/scrypt_no_match b/testdata/testkit/scrypt_no_match new file mode 100644 index 0000000..994d647 Binary files /dev/null and b/testdata/testkit/scrypt_no_match differ diff --git a/testdata/testkit/scrypt_work_factor_23 b/testdata/testkit/scrypt_work_factor_23 new file mode 100644 index 0000000..c37ca07 --- /dev/null +++ b/testdata/testkit/scrypt_work_factor_23 @@ -0,0 +1,9 @@ +expect: header failure +file key: 59454c4c4f57205355424d4152494e45 +comment: work factor is very high, would take a long time to compute + +age-encryption.org/v1 +-> scrypt rF0/NwblUHHTpgQgRpe5CQ 23 +qW9eVsT0NVb/Vswtw8kPIxUnaYmm9Px1dYmq2+4+qZA +--- 38TpQMxQRRNMfmYYpBX6DDrPx4/QY5UmJnhPyVoX/cw +]?7PqӦ F ۮ z(r| \ No newline at end of file diff --git a/testdata/stanza_empty_body.test b/testdata/testkit/stanza_empty_body similarity index 100% rename from testdata/stanza_empty_body.test rename to testdata/testkit/stanza_empty_body diff --git a/testdata/stanza_empty_last_line.test b/testdata/testkit/stanza_empty_last_line similarity index 100% rename from testdata/stanza_empty_last_line.test rename to testdata/testkit/stanza_empty_last_line diff --git a/testdata/stanza_missing_body.test b/testdata/testkit/stanza_missing_body similarity index 100% rename from testdata/stanza_missing_body.test rename to testdata/testkit/stanza_missing_body diff --git a/testdata/stanza_missing_final_line.test b/testdata/testkit/stanza_missing_final_line similarity index 100% rename from testdata/stanza_missing_final_line.test rename to testdata/testkit/stanza_missing_final_line diff --git a/testdata/stanza_multiple_short_lines.test b/testdata/testkit/stanza_multiple_short_lines similarity index 100% rename from testdata/stanza_multiple_short_lines.test rename to testdata/testkit/stanza_multiple_short_lines diff --git a/testdata/stream_empty_payload.test b/testdata/testkit/stream_empty_payload similarity index 100% rename from testdata/stream_empty_payload.test rename to testdata/testkit/stream_empty_payload diff --git a/testdata/stream_last_chunk_empty.test b/testdata/testkit/stream_last_chunk_empty similarity index 99% rename from testdata/stream_last_chunk_empty.test rename to testdata/testkit/stream_last_chunk_empty index 8e03661..0795d14 100644 Binary files a/testdata/stream_last_chunk_empty.test and b/testdata/testkit/stream_last_chunk_empty differ diff --git a/testdata/stream_last_chunk_full.test b/testdata/testkit/stream_last_chunk_full similarity index 100% rename from testdata/stream_last_chunk_full.test rename to testdata/testkit/stream_last_chunk_full diff --git a/testdata/testkit/valid_characters b/testdata/testkit/valid_characters new file mode 100644 index 0000000..0c88a6e --- /dev/null +++ b/testdata/testkit/valid_characters @@ -0,0 +1,12 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> !"#$%&' ()*+,-./ 01234567 89:;<=>? @ABCDEFG HIJKLMNO PQRSTUVW XYZ[\]^_ `abcdefg hijklmno pqrstuvw xyz{|}~ + +-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc +EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U +--- XdSsgCFKtyPBxU0ard+ElUYUfOp6XQtDhzDGFUCLbjo +bΑ3'NhLL[R,1f \ No newline at end of file diff --git a/testdata/x25519.test b/testdata/testkit/x25519 similarity index 100% rename from testdata/x25519.test rename to testdata/testkit/x25519 diff --git a/testdata/testkit/x25519_multiple_recipients b/testdata/testkit/x25519_multiple_recipients new file mode 100644 index 0000000..27c772c --- /dev/null +++ b/testdata/testkit/x25519_multiple_recipients @@ -0,0 +1,12 @@ +expect: success +payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab +file key: 59454c4c4f57205355424d4152494e45 +identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6 + +age-encryption.org/v1 +-> X25519 ajtqAvDEkVNr2B7zUOtq2mAQXDSBlNrVAuM/dKb5sT4 +0evrK/HQXVsQ4YaDe+659l5OQzvAzD2ytLGHQLQiqxg +-> X25519 0qC7u6AbLxuwnM8tPFOWVtWZn/ZZe7z7gcsP5kgA0FI +T/PZg76MmVt2IaLntrxppzDnzeFDYHsHFcnTnhbRLQ8 +--- 7W07ef2PhsTAl74pn+9vSj/Xzukwa6SuTqMc16cdBk0 +5TB9 Kom^OY %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