testdata: add a few more testkit vectors

This commit is contained in:
Filippo Valsorda 2022-06-15 18:54:09 +02:00
parent e49b1f9afe
commit a787511e01
25 changed files with 342 additions and 21 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -6,7 +6,6 @@ package testkit
import (
"bytes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
@ -31,8 +30,9 @@ var TestX25519Recipient, _ = curve25519.X25519(TestX25519Identity, curve25519.Ba
type TestFile struct {
Buf bytes.Buffer
rand io.Reader
Rand func(n int) []byte
fileKey []byte
streamKey []byte
nonce [12]byte
payload bytes.Buffer
@ -41,19 +41,19 @@ type TestFile struct {
identities []string
}
type zeroReader struct{}
func (zeroReader) Read(p []byte) (int, error) {
for n := range p {
p[n] = 0
}
return len(p), nil
}
func NewTestFile() *TestFile {
c, _ := chacha20.NewUnauthenticatedCipher(
[]byte("TEST RANDOMNESS TEST RANDOMNESS!"), make([]byte, chacha20.NonceSize))
return &TestFile{rand: cipher.StreamReader{c, zeroReader{}}, expect: "success"}
rand := func(n int) []byte {
out := make([]byte, n)
c.XORKeyStream(out, out)
return out
}
return &TestFile{Rand: rand, expect: "success", fileKey: TestFileKey}
}
func (f *TestFile) FileKey(key []byte) {
f.fileKey = key
}
func (f *TestFile) TextLine(s string) {
@ -96,18 +96,25 @@ func (f *TestFile) AEADBody(key, body []byte) {
}
func (f *TestFile) X25519(identity []byte) {
f.X25519RecordIdentity(identity)
f.X25519NoRecordIdentity(identity)
}
func (f *TestFile) X25519RecordIdentity(identity []byte) {
id, _ := bech32.Encode("AGE-SECRET-KEY-", identity)
f.identities = append(f.identities, id)
}
func (f *TestFile) X25519NoRecordIdentity(identity []byte) {
recipient, _ := curve25519.X25519(identity, curve25519.Basepoint)
ephemeral := make([]byte, 32)
f.rand.Read(ephemeral)
ephemeral := f.Rand(32)
share, _ := curve25519.X25519(ephemeral, curve25519.Basepoint)
f.ArgsLine("X25519", b64(share))
secret, _ := curve25519.X25519(ephemeral, recipient)
key := make([]byte, 32)
hkdf.New(sha256.New, secret, append(share, recipient...),
[]byte("age-encryption.org/v1/X25519")).Read(key)
f.AEADBody(key, TestFileKey)
f.AEADBody(key, f.fileKey)
}
func (f *TestFile) HMACLine(h []byte) {
@ -116,7 +123,7 @@ func (f *TestFile) HMACLine(h []byte) {
func (f *TestFile) HMAC() {
key := make([]byte, 32)
hkdf.New(sha256.New, TestFileKey, nil, []byte("header")).Read(key)
hkdf.New(sha256.New, f.fileKey, nil, []byte("header")).Read(key)
h := hmac.New(sha256.New, key)
h.Write(f.Buf.Bytes())
h.Write([]byte("---"))
@ -124,10 +131,9 @@ func (f *TestFile) HMAC() {
}
func (f *TestFile) Nonce() {
nonce := make([]byte, 16)
f.rand.Read(nonce)
nonce := f.Rand(16)
f.streamKey = make([]byte, 32)
hkdf.New(sha256.New, TestFileKey, nonce, []byte("payload")).Read(f.streamKey)
hkdf.New(sha256.New, f.fileKey, nonce, []byte("payload")).Read(f.streamKey)
f.Buf.Write(nonce)
}
@ -167,6 +173,7 @@ func (f *TestFile) Generate() {
if f.expect == "success" {
fmt.Printf("payload: %x\n", sha256.Sum256(f.payload.Bytes()))
}
fmt.Printf("file key: %x\n", f.fileKey)
for _, id := range f.identities {
fmt.Printf("identity: %s\n", id)
}

21
testdata/bad_hmac.go vendored Normal file
View file

@ -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.X25519(testkit.TestX25519Identity)
f.FileKey(f.Rand(16))
f.HMAC()
f.FileKey(testkit.TestFileKey)
f.Payload("age")
f.ExpectHeaderFailure()
f.Generate()
}

9
testdata/bad_hmac.test vendored Normal file
View file

@ -0,0 +1,9 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
--- UGnodA32FkH3AOl9BHP6biSZTkNvu8B99I++7JpqZGk
[æè. ½Ó#ÈwÏ…=a×Yök×z©66Ú¦<01>âRùÛL

18
testdata/empty_payload.go vendored Normal file
View file

@ -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.X25519(testkit.TestX25519Recipient)
f.HMAC()
f.Payload("")
f.Generate()
}

10
testdata/empty_payload.test vendored Normal file
View file

@ -0,0 +1,10 @@
expect: success
payload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U
--- Vn+54jqiiUCE+WZcEVY3f1sqHjlu/z1LCQ/T7Xm7qI0
îÏbÇδ3'NhÔòùL­.OÏ>RŠA0Þ«ïC6åU

21
testdata/long_file_key.go vendored Normal file
View file

@ -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.X25519(testkit.TestX25519Identity)
f.HMAC()
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("the file key must be checked to be 16 bytes before decrypting it")
f.Generate()
}

10
testdata/long_file_key.test vendored Normal file
View file

@ -0,0 +1,10 @@
expect: header failure
file key: 41204c4f4e4745522059454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
comment: the file key must be checked to be 16 bytes before decrypting it
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
nlObGn0CSA4pxiaG3W6nLlaFFuHmqW+bFC6sJmbsJ9yFesgSok1K0AI
--- C49Jo3+j4I6jWB2tldSs1jVAXbv0mOTAnwdT+5vOiBg
îÏbÇδ3'NhÔòùLc÷(­çñ ÐtÿÇ<C3BF>P²)€x1

21
testdata/no_match.go vendored Normal file
View file

@ -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")
identity := f.Rand(32)
f.X25519RecordIdentity(identity)
f.X25519NoRecordIdentity(testkit.TestX25519Recipient)
f.HMAC()
f.Payload("age")
f.ExpectHeaderFailure()
f.Generate()
}

9
testdata/no_match.test vendored Normal file
View file

@ -0,0 +1,9 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-143WN7DCXU4G8R5AXQSSYD9AEPYDNT3HXSLWSPK36CDU6E8M59SSSAGZ3KG
age-encryption.org/v1
-> X25519 ajtqAvDEkVNr2B7zUOtq2mAQXDSBlNrVAuM/dKb5sT4
HUKtz0R2j5Bl2ER7HhAZrURikCFpiIjNa0KjHcjbAGU
--- rrpTlvKEKrK3EqhoOPJeP1KE8O1d2arrRez77mwekRc
ÝßrÐo¼«Wß= 1$­×ýo€x»ø<C2BB>-ØyG^·½^ˆ

20
testdata/stanza_empty_body.go vendored Normal file
View file

@ -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.X25519(testkit.TestX25519Recipient)
f.ArgsLine("empty")
f.Body([]byte(""))
f.HMAC()
f.Payload("age")
f.Generate()
}

12
testdata/stanza_empty_body.test vendored Normal file
View file

@ -0,0 +1,12 @@
expect: success
payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U
-> empty
--- 697zSC9pa/ZLNIaXGtuwcUobmxv+Dpx48Hv0papk5c0
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

24
testdata/stanza_empty_last_line.go vendored Normal file
View file

@ -0,0 +1,24 @@
// 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 (
"bytes"
"filippo.io/age/internal/testkit"
)
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Recipient)
f.ArgsLine("stanza")
f.Body(bytes.Repeat([]byte("A"), 48))
f.HMAC()
f.Payload("age")
f.Generate()
}

13
testdata/stanza_empty_last_line.test vendored Normal file
View file

@ -0,0 +1,13 @@
expect: success
payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U
-> stanza
QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB
--- +3PC416gxa7Mk7WxpX0kb6DVfSuCun0niGre+G4bZhE
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

22
testdata/stanza_missing_body.go vendored Normal file
View file

@ -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.X25519(testkit.TestX25519Recipient)
f.ArgsLine("empty")
// Missing body.
f.HMAC()
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("every stanza must end with a short body line, even if empty")
f.Generate()
}

11
testdata/stanza_missing_body.test vendored Normal file
View file

@ -0,0 +1,11 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
comment: every stanza must end with a short body line, even if empty
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U
-> empty
--- CDgFIIJ1wE4CpW6zG+LVZ6/G/RCNTH6ZUVGp2NbeIkU
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

26
testdata/stanza_missing_final_line.go vendored Normal file
View file

@ -0,0 +1,26 @@
// 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 (
"strings"
"filippo.io/age/internal/testkit"
)
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Recipient)
f.ArgsLine("stanza")
f.TextLine(strings.Repeat("A", 64))
f.HMAC()
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("every stanza must end with a short body line")
f.Generate()
}

12
testdata/stanza_missing_final_line.test vendored Normal file
View file

@ -0,0 +1,12 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
comment: every stanza must end with a short body line
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U
-> stanza
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
--- GRjUy1ShNhFoV3cQikdtUZqDeDEZSrbtNXUgDtDbwC8
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

27
testdata/stanza_multiple_short_lines.go vendored Normal file
View file

@ -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 (
"strings"
"filippo.io/age/internal/testkit"
)
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Recipient)
f.ArgsLine("stanza")
f.TextLine(strings.Repeat("A", 32))
f.TextLine(strings.Repeat("A", 32))
f.HMAC()
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("a short body line ends the stanza")
f.Generate()
}

View file

@ -0,0 +1,13 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
comment: a short body line ends the stanza
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U
-> stanza
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
--- ct87HSIMoTC4nUsQva+8AeKc2bK2q8b9sPjRhjuf1us
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

View file

@ -1,5 +1,6 @@
expect: success
payload: 013f54400c82da08037759ada907a8b864e97de81c088a182062c4b5622fd2ab
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
age-encryption.org/v1

View file

@ -9,7 +9,6 @@ import (
"crypto/sha256"
"encoding/hex"
"flag"
"fmt"
"io"
"log"
"os"
@ -27,15 +26,26 @@ func TestMain(m *testing.M) {
genFlag := flag.Bool("generate", false, "regenerate test files")
flag.Parse()
if *genFlag {
log.SetFlags(0)
tests, err := filepath.Glob("testdata/*.test")
if err != nil {
log.Fatal(err)
}
for _, test := range tests {
os.Remove(test)
}
generators, err := filepath.Glob("testdata/*.go")
if err != nil {
log.Fatal(err)
}
for _, generator := range generators {
vector := strings.TrimSuffix(generator, ".go") + ".test"
fmt.Fprintf(os.Stderr, "%s -> %s\n", generator, vector)
log.Printf("%s -> %s\n", generator, vector)
out, err := exec.Command("go", "run", generator).Output()
if err != nil {
if err, ok := err.(*exec.ExitError); ok {
log.Fatalf("%s", err.Stderr)
}
log.Fatal(err)
}
os.WriteFile(vector, out, 0664)
@ -104,6 +114,8 @@ func testVector(t *testing.T, test []byte) {
t.Fatal(err)
}
identities = append(identities, i)
case "file key":
// Ignored.
case "comment":
t.Log(value)
default:
@ -114,6 +126,7 @@ func testVector(t *testing.T, test []byte) {
r, err := age.Decrypt(bytes.NewReader(test), identities...)
if err != nil {
if expectHeaderFailure {
t.Log(err)
return
}
t.Fatal("unexpected header error:", err)
@ -123,6 +136,7 @@ func testVector(t *testing.T, test []byte) {
out, err := io.ReadAll(r)
if err != nil {
if expectPayloadFailure {
t.Log(err)
return
}
t.Fatal("unexpected payload error:", err)