This directory contains a large set of test vectors for the age file encryption format, as well as a framework to easily generate them.
The test suite can be applied to any age implementation, regardless of the language
it's implemented in, and the level of abstraction of its interface.
For the simplest, most universal integration, the implementation can just attempt
to decrypt the test files, check the operation only succeeds if expect
is
success
, and compare the decrypted payload. Test vectors involving
unimplemented features (such as passphrase encryption or armoring) can be ignored.
These vectors can't be used to test the encryption direction end-to-end (because
there is no strict specification for how to reproducibly inject randomness
in the process), however, they can be used to test that parsing and encoding
operations round-trip in various edge cases. Specifically, armored
vectors can
be used to test round-trips of armor decode/encode (accounting for CRLF/LF and
trailing and leading spaces tolerance), any vector that's not a header failure
can be used to test round-trips of header decode/encode, and any success
vector
can be used to test round-trips of STREAM decrypt/encrypt (with the help of the
file key
).
For an example of how to use this test suite, check the reference Go implementation.
If testing a Go program, you can import the c2sp.org/CCTV/age
module and use
the embedded filesystem.
Otherwise, you can use git-subtree
to include a copy of the vectors in your
project. The license allows this without attribution.
git fetch https://github.com/C2SP/CCTV
TEMPDIR=$(mktemp -d)
git worktree add $TEMPDIR FETCH_HEAD
SPLIT=$(cd $TEMPDIR && git subtree split -P age/testdata --annotate 'testkit: ')
git worktree remove $TEMPDIR
git subtree add -P testkit $SPLIT
To update the vectors, repeat the process with git subtree merge
instead of
git subtree add
.
Each file in the testdata
folder is a separate test vector, meant to be
processed independently. Each vector is meant to test only one failure (or
success) scenario.
The file is in two parts: first a textual header, then an empty line, and then an age encrypted file. The textual header is a series of key-value pairs, separated by a colon and a space, each on their own line.
The following header keys are defined. Files with unknown keys should be ignored.
-
expect
This key defines what the result of decrypting the file should be. Distinguishing between different types of failure can be important to avoid masking what should be a lower-level error (for example, a parsing error) with a higher-level error (for example, incorrect HMAC because the incorrectly parsed file was re-encoded before calculating the HMAC). However, they might not map effectively to your API, in which case you should consider aliasing indistinguishable cases. It can take the following values.
-
success
The file should decrypt correctly all the way to EOF, and the payload should match the expected hash (see below).
-
no match
The header should parse successfully, but none of the recipient stanzas can be unwrapped.
-
HMAC failure
The header should parse successfully, and a file key can be unwrapped, but the header HMAC should not match.
-
header failure
The header should fail to parse successfully.
-
payload failure
The header should parse successfully, and a file key can be unwrapped, but the STREAM-encrypted payload doesn't decrypt successfully all the way to EOF. Whatever payload decrypted successfully before encountering the error must be checked against the payload hash (see below).
-
armor failure
The ASCII armor should fail to parse successfully.
-
-
payload
This is a hex-encoded SHA-256 hash of the payload. All the plaintext that would have been released to the application by the API must match this hash, even if the decryption eventually fails.
-
identity
This is a Bech32 encoded X25519 identity that should be used to unwrap recipient stanzas. This key can appear multiple times.
-
passphrase
This is a passphrase that should be used to unwrap
scrypt
recipient stanzas. This key can appear multiple times. -
armored
This key will be
yes
if the file is supposed to be encoded in the optional ASCII armor. -
file key
This is the hex-encoded file key, to aid debugging. This can be ignored.
-
comment
This is a textual comment that explains the vector. This can be ignored.
Every test vector in testdata
is generated by a corresponding file in
internal/tests
, using the framework in internal/testkit
.
To generate the files, run
go generate ./...
The vectors in the testdata
folder and the files in this top-level directory
are available under the terms of the
Zero-Clause BSD (reproduced below),
CC0 1.0, or
Unlicense license, to your choice.
Copyright (c) 2022 The age Authors
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
The software is provided "as is" and the author disclaims all warranties with regard to this software including all implied warranties of merchantability and fitness. In no event shall the author be liable for any special, direct, indirect, or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software.