cmd/age-inspect: classify unknown recipient stanzas

Reported by Joe Doyle of Trail of Bits.
This commit is contained in:
Filippo Valsorda
2026-08-29 19:30:10 +02:00
parent ec60cb56b4
commit 88299a50e4
3 changed files with 41 additions and 7 deletions
+4 -2
View File
@@ -33,7 +33,7 @@ type Metadata struct {
func Inspect(r io.Reader, fileSize int64) (*Metadata, error) {
data := &Metadata{
Version: "age-encryption.org/v1",
Postquantum: "unknown",
Postquantum: "yes",
}
tr := &trackReader{r: r}
@@ -64,8 +64,10 @@ func Inspect(r io.Reader, fileSize int64) (*Metadata, error) {
case "X25519", "ssh-rsa", "ssh-ed25519", "p256tag", "piv-p256":
data.Postquantum = "no"
case "mlkem768x25519", "scrypt", "mlkem768p256tag":
// Keep "yes".
default:
if data.Postquantum != "no" {
data.Postquantum = "yes"
data.Postquantum = "unknown"
}
}
}
+34 -5
View File
@@ -10,14 +10,14 @@ import (
"filippo.io/age/internal/stream"
)
// buildFile serializes a header with a single stanza of the given type,
// buildFile serializes a header with the given stanza types,
// followed by the minimal valid encrypted payload (a 16-byte stream nonce
// and a single empty ChaCha20-Poly1305 chunk).
func buildFile(t *testing.T, stanzaType string) []byte {
func buildFile(t *testing.T, stanzaTypes ...string) []byte {
t.Helper()
hdr := &format.Header{
Recipients: []*format.Stanza{{Type: stanzaType}},
MAC: make([]byte, 32),
hdr := &format.Header{MAC: make([]byte, 32)}
for _, stanzaType := range stanzaTypes {
hdr.Recipients = append(hdr.Recipients, &format.Stanza{Type: stanzaType})
}
buf := &bytes.Buffer{}
if err := hdr.Marshal(buf); err != nil {
@@ -53,6 +53,35 @@ func TestInspectTagStanzas(t *testing.T) {
}
}
func TestInspectPostquantum(t *testing.T) {
tests := []struct {
name string
stanzaTypes []string
want string
}{
{"postquantum", []string{"mlkem768x25519"}, "yes"},
{"classical", []string{"X25519"}, "no"},
{"both", []string{"mlkem768x25519", "X25519"}, "no"},
{"unrecognized", []string{"tpm-ecc"}, "unknown"},
{"postquantum and unrecognized", []string{"mlkem768x25519", "tpm-ecc"}, "unknown"},
{"unrecognized and postquantum", []string{"tpm-ecc", "mlkem768x25519"}, "unknown"},
{"classical and unrecognized", []string{"X25519", "tpm-ecc"}, "no"},
{"unrecognized and classical", []string{"tpm-ecc", "X25519"}, "no"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := buildFile(t, tt.stanzaTypes...)
md, err := Inspect(bytes.NewReader(f), int64(len(f)))
if err != nil {
t.Fatalf("Inspect: %v", err)
}
if md.Postquantum != tt.want {
t.Errorf("Postquantum = %q, want %q", md.Postquantum, tt.want)
}
})
}
}
// readAfterEOFReader returns io.EOF along with the last of its data, and then
// more data from subsequent Reads, like a terminal that received Ctrl-D
// followed by more input.