diff --git a/internal/testkit/testkit.go b/internal/testkit/testkit.go index 603fd15..ba03d50 100644 --- a/internal/testkit/testkit.go +++ b/internal/testkit/testkit.go @@ -90,6 +90,11 @@ func (f *TestFile) ArgsLine(args ...string) { f.TextLine(strings.Join(append([]string{"->"}, args...), " ")) } +func (f *TestFile) UnreadArgsLine() []string { + line := strings.TrimPrefix(f.UnreadLine(), "-> ") + return strings.Split(line, " ") +} + var b64 = base64.RawStdEncoding.EncodeToString func (f *TestFile) Body(body []byte) { @@ -160,6 +165,10 @@ func (f *TestFile) ScryptRecordPassphrase(passphrase string) { func (f *TestFile) ScryptNoRecordPassphrase(passphrase string, workFactor int) { salt := f.Rand(16) + f.ScryptNoRecordPassphraseWithSalt(passphrase, workFactor, salt) +} + +func (f *TestFile) ScryptNoRecordPassphraseWithSalt(passphrase string, workFactor int, salt []byte) { f.ArgsLine("scrypt", b64(salt), strconv.Itoa(workFactor)) key, err := scrypt.Key([]byte(passphrase), append([]byte("age-encryption.org/v1/scrypt"), salt...), 1< i.maxWorkFactor { return nil, fmt.Errorf("scrypt work factor too large: %v", logN) } - if logN <= 0 { + if logN <= 0 { // unreachable return nil, fmt.Errorf("invalid scrypt work factor: %v", logN) } salt = append([]byte(scryptLabel), salt...) k, err := scrypt.Key(i.password, salt, 1< scrypt 10 +W0mMthyhNJOV3debCwkQcUlNx/i6Ss/A07aQCrG5Gcw +--- 1QsPcEbBSylfP4apakJqtDBJMrpd81rPuSLTCvdZx6E +¬]?7åPqÓ¦ F—¹ •Â÷õÛ®è zŒ(rŠóÎ| \ No newline at end of file diff --git a/testdata/testkit/scrypt_salt_short b/testdata/testkit/scrypt_salt_short new file mode 100644 index 0000000..a95f460 Binary files /dev/null and b/testdata/testkit/scrypt_salt_short differ diff --git a/testdata/testkit/scrypt_uppercase b/testdata/testkit/scrypt_uppercase new file mode 100644 index 0000000..88bd1e4 Binary files /dev/null and b/testdata/testkit/scrypt_uppercase differ diff --git a/testdata/testkit/scrypt_work_factor_hex b/testdata/testkit/scrypt_work_factor_hex new file mode 100644 index 0000000..a1feca9 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_hex differ diff --git a/testdata/testkit/scrypt_work_factor_leading_garbage b/testdata/testkit/scrypt_work_factor_leading_garbage new file mode 100644 index 0000000..bf3bfd5 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_leading_garbage differ diff --git a/testdata/testkit/scrypt_work_factor_leading_plus b/testdata/testkit/scrypt_work_factor_leading_plus new file mode 100644 index 0000000..57f400e Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_leading_plus differ diff --git a/testdata/testkit/scrypt_work_factor_leading_zero_decimal b/testdata/testkit/scrypt_work_factor_leading_zero_decimal new file mode 100644 index 0000000..303e2f9 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_leading_zero_decimal differ diff --git a/testdata/testkit/scrypt_work_factor_leading_zero_octal b/testdata/testkit/scrypt_work_factor_leading_zero_octal new file mode 100644 index 0000000..46c12f8 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_leading_zero_octal differ diff --git a/testdata/testkit/scrypt_work_factor_missing b/testdata/testkit/scrypt_work_factor_missing new file mode 100644 index 0000000..62c14e2 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_missing differ diff --git a/testdata/testkit/scrypt_work_factor_negative b/testdata/testkit/scrypt_work_factor_negative new file mode 100644 index 0000000..eaf5400 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_negative differ diff --git a/testdata/testkit/scrypt_work_factor_overflow b/testdata/testkit/scrypt_work_factor_overflow new file mode 100644 index 0000000..03aeb03 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_overflow differ diff --git a/testdata/testkit/scrypt_work_factor_trailing_garbage b/testdata/testkit/scrypt_work_factor_trailing_garbage new file mode 100644 index 0000000..bf3bfd5 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_trailing_garbage differ diff --git a/testdata/testkit/scrypt_work_factor_wrong b/testdata/testkit/scrypt_work_factor_wrong new file mode 100644 index 0000000..209e1f6 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_wrong differ diff --git a/testdata/testkit/scrypt_work_factor_zero b/testdata/testkit/scrypt_work_factor_zero new file mode 100644 index 0000000..9d8c278 Binary files /dev/null and b/testdata/testkit/scrypt_work_factor_zero differ diff --git a/tests/scrypt_bad_tag.go b/tests/scrypt_bad_tag.go new file mode 100644 index 0000000..0675075 --- /dev/null +++ b/tests/scrypt_bad_tag.go @@ -0,0 +1,27 @@ +// 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 ( + "encoding/base64" + + "filippo.io/age/internal/testkit" +) + +func main() { + f := testkit.NewTestFile() + f.VersionLine("v1") + f.Scrypt("password", 10) + body, _ := base64.RawStdEncoding.DecodeString(f.UnreadLine()) + body[len(body)-1] ^= 0xff + f.TextLine(base64.RawStdEncoding.EncodeToString(body)) + f.HMAC() + f.Payload("age") + f.ExpectNoMatch() + f.Comment("the ChaCha20Poly1305 authentication tag on the body of the scrypt stanza is wrong") + f.Generate() +} diff --git a/tests/scrypt_extra_argument.go b/tests/scrypt_extra_argument.go new file mode 100644 index 0000000..baa3d7a --- /dev/null +++ b/tests/scrypt_extra_argument.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") + f.Scrypt("password", 10) + body, args := f.UnreadLine(), f.UnreadLine() + f.TextLine(args + " 10") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Comment("the base64 encoding of the share is not canonical") + f.Generate() +} diff --git a/tests/scrypt_not_canonical_body.go b/tests/scrypt_not_canonical_body.go new file mode 100644 index 0000000..d0a0304 --- /dev/null +++ b/tests/scrypt_not_canonical_body.go @@ -0,0 +1,22 @@ +// 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) + body := f.UnreadLine() + f.TextLine(testkit.NotCanonicalBase64(body)) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Comment("the base64 encoding of the share is not canonical") + f.Generate() +} diff --git a/tests/scrypt_not_canonical_salt.go b/tests/scrypt_not_canonical_salt.go new file mode 100644 index 0000000..38f27d2 --- /dev/null +++ b/tests/scrypt_not_canonical_salt.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], testkit.NotCanonicalBase64(args[1]), args[2]) + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_salt_long.go b/tests/scrypt_salt_long.go new file mode 100644 index 0000000..578cb8f --- /dev/null +++ b/tests/scrypt_salt_long.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("password") + f.ScryptNoRecordPassphraseWithSalt("password", 10, f.Rand(20)) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_salt_missing.go b/tests/scrypt_salt_missing.go new file mode 100644 index 0000000..a07715d --- /dev/null +++ b/tests/scrypt_salt_missing.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") + f.ScryptRecordPassphrase("password") + f.ScryptNoRecordPassphraseWithSalt("password", 10, nil) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[2]) + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_salt_short.go b/tests/scrypt_salt_short.go new file mode 100644 index 0000000..e277af8 --- /dev/null +++ b/tests/scrypt_salt_short.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("password") + f.ScryptNoRecordPassphraseWithSalt("password", 10, f.Rand(12)) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_uppercase.go b/tests/scrypt_uppercase.go new file mode 100644 index 0000000..836a4d1 --- /dev/null +++ b/tests/scrypt_uppercase.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine("Scrypt", args[1], args[2]) + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectNoMatch() + f.Generate() +} diff --git a/tests/scrypt_work_factor_hex.go b/tests/scrypt_work_factor_hex.go new file mode 100644 index 0000000..fc43e4b --- /dev/null +++ b/tests/scrypt_work_factor_hex.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], "0xa") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_leading_garbage.go b/tests/scrypt_work_factor_leading_garbage.go new file mode 100644 index 0000000..0b1a9a2 --- /dev/null +++ b/tests/scrypt_work_factor_leading_garbage.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], "aaaa10") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_leading_plus.go b/tests/scrypt_work_factor_leading_plus.go new file mode 100644 index 0000000..34dc4af --- /dev/null +++ b/tests/scrypt_work_factor_leading_plus.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], "+10") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_leading_zero_decimal.go b/tests/scrypt_work_factor_leading_zero_decimal.go new file mode 100644 index 0000000..09c2dce --- /dev/null +++ b/tests/scrypt_work_factor_leading_zero_decimal.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], "010") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_leading_zero_octal.go b/tests/scrypt_work_factor_leading_zero_octal.go new file mode 100644 index 0000000..24be6af --- /dev/null +++ b/tests/scrypt_work_factor_leading_zero_octal.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], "012") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_missing.go b/tests/scrypt_work_factor_missing.go new file mode 100644 index 0000000..18e6d06 --- /dev/null +++ b/tests/scrypt_work_factor_missing.go @@ -0,0 +1,22 @@ +// 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", 18) // cmd/age default + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1]) + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_negative.go b/tests/scrypt_work_factor_negative.go new file mode 100644 index 0000000..ed264b0 --- /dev/null +++ b/tests/scrypt_work_factor_negative.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], "-10") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_overflow.go b/tests/scrypt_work_factor_overflow.go new file mode 100644 index 0000000..3520057 --- /dev/null +++ b/tests/scrypt_work_factor_overflow.go @@ -0,0 +1,27 @@ +// 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 ( + "math" + "strconv" + + "filippo.io/age/internal/testkit" +) + +func main() { + f := testkit.NewTestFile() + f.VersionLine("v1") + f.Scrypt("password", 10) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], strconv.FormatUint(math.MaxInt64+1+10, 10)) + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_trailing_garbage.go b/tests/scrypt_work_factor_trailing_garbage.go new file mode 100644 index 0000000..0b1a9a2 --- /dev/null +++ b/tests/scrypt_work_factor_trailing_garbage.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], "aaaa10") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/scrypt_work_factor_wrong.go b/tests/scrypt_work_factor_wrong.go new file mode 100644 index 0000000..a004999 --- /dev/null +++ b/tests/scrypt_work_factor_wrong.go @@ -0,0 +1,22 @@ +// 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", 18) // cmd/go default + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], "10") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectNoMatch() + f.Generate() +} diff --git a/tests/scrypt_work_factor_zero.go b/tests/scrypt_work_factor_zero.go new file mode 100644 index 0000000..bc193f8 --- /dev/null +++ b/tests/scrypt_work_factor_zero.go @@ -0,0 +1,22 @@ +// 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) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine(args[0], args[1], "0") + f.TextLine(body) + f.HMAC() + f.Payload("age") + f.ExpectHeaderFailure() + f.Generate() +} diff --git a/tests/x25519_lowercase.go b/tests/x25519_lowercase.go index 7bdce73..1c7bb94 100644 --- a/tests/x25519_lowercase.go +++ b/tests/x25519_lowercase.go @@ -6,18 +6,14 @@ package main -import ( - "strings" - - "filippo.io/age/internal/testkit" -) +import "filippo.io/age/internal/testkit" func main() { f := testkit.NewTestFile() f.VersionLine("v1") f.X25519(testkit.TestX25519Recipient) - body, args := f.UnreadLine(), f.UnreadLine() - f.TextLine(strings.Replace(args, "X25519", "x25519", -1)) + body, args := f.UnreadLine(), f.UnreadArgsLine() + f.ArgsLine("x25519", args[1]) f.TextLine(body) f.HMAC() f.Payload("age") diff --git a/tests/x25519_not_canonical_body.go b/tests/x25519_not_canonical_body.go index ee49f96..b1bd4b5 100644 --- a/tests/x25519_not_canonical_body.go +++ b/tests/x25519_not_canonical_body.go @@ -12,8 +12,7 @@ func main() { f := testkit.NewTestFile() f.VersionLine("v1") f.X25519(testkit.TestX25519Recipient) - body, args := f.UnreadLine(), f.UnreadLine() - f.TextLine(args) + body := f.UnreadLine() f.TextLine(testkit.NotCanonicalBase64(body)) f.HMAC() f.Payload("age")