Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions docs/rfc9580-coverage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# RFC 9580 Coverage (Current State)

This note summarizes current implementation status for features related to
RFC 4880, RFC 5581, RFC 6637, and draft RFC 4880bis / RFC 9580.

## Summary

- Stable base support is present for RFC 4880, RFC 5581, and RFC 6637.
- Partial support for draft RFC 4880bis features is present (mainly version 5 era features).
- RFC 9580 finalization coverage is incomplete, especially around version 6 semantics.

## Implemented (Confirmed)

1. Packet and subpacket IDs for AEAD-era features
- Tag 20 AEAD Encrypted Data Packet exists.
- parse/tags/tag20.go
- parse/values/tagid.go
- Subpacket 39 Preferred AEAD Ciphersuites exists.
- parse/tags/sub39.go
- parse/values/subpacketid.go
- Subpacket 33 Issuer Fingerprint exists with v4/v5 handling notes.
- parse/tags/sub33.go

2. AEAD algorithm model
- AEAD algorithm IDs and IV/tag lengths are modeled.
- parse/values/aeadid.go

3. S2K Argon2 support
- S2K ID 4 Argon2 parsing exists.
- parse/s2k/s2k.go
- parse/values/s2kid.go

4. Version 5 packet handling (draft marker)
- Version model treats 5 as draft for multiple packet families.
- parse/values/version.go
- Secret key and secret subkey tests include Version 5 (draft) examples.
- parse/tags/tag05_test.go
- parse/tags/tag07_test.go

5. SEIPD v2 parser path exists
- Tag 18 supports version 1 and version 2 parsing branches.
- parse/tags/tag18.go

## Partial / Inconsistent

1. Chunk size interpretation differs between Tag 18 and Tag 20
- Tag 20 converts encoded chunk parameter to actual size (1 << (c + 6)).
- parse/tags/tag20.go
- Tag 18 currently exposes the raw one-octet value as plain integer.
- parse/tags/tag18.go

2. Draft-oriented wording remains in output and tests
- Version 5 is labeled as draft in the Version model.
- parse/values/version.go
- Existing expected outputs in tests reflect draft wording.
- parse/tags/tag05_test.go
- parse/tags/tag07_test.go

## Missing / Likely Gaps for RFC 9580

1. Version 6-oriented paths are not visible in version helpers
- Current helper constructors only encode old/current/draft sets around v4/v5.
- parse/values/version.go

2. Key-version gated fingerprint handling may be too narrow
- One-pass signature packet path currently only accepts key version 5.
- parse/tags/tag04.go
- Public-key encrypted session key packet path recognizes key version 4/5 only.
- parse/tags/tag01.go

3. No obvious v6-focused tests or test vectors in parser tests
- Current tests include v5 vectors and draft labels.
- parse/tags/tag02_test.go
- parse/tags/tag05_test.go
- parse/tags/tag07_test.go

## Proposed Implementation Order (Small PR Units)

1. Normalize feature inventory in docs and wording
- Decide whether Version 5 should still be surfaced as draft in user-visible output.

2. Align chunk size behavior
- Make Tag 18 chunk-size rendering consistent with Tag 20.
- Add/adjust tests for expected value format.

3. Add v6 version model and packet handling gates
- Extend version helpers for v6-aware labeling where required.
- Update tag01/tag04 key-version checks and fingerprint-length logic as needed.

4. Add test vectors for v6 paths
- Introduce focused parser tests before broad refactors.

5. Update README and architecture notes
- Keep claimed support level synchronized with actual parser behavior.

## Validation Checklist per PR

- task test
- task govulncheck
- Expected output snapshots updated only where behavior intentionally changed
6 changes: 4 additions & 2 deletions parse/tags/tag18.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ func (t *tag18) parseV2(rootInfo *result.Item) (*result.Item, error) {
if err != nil {
return rootInfo, errs.New("illegal chunk size", errs.WithCause(err))
}
chunkSize := uint64(1) << (sz + 6)
rootInfo.Add(result.NewItem(
result.Name("chunk size"),
result.Value(strconv.Itoa(int(sz))),
result.Name("Chunk size"),
result.Value(strconv.FormatUint(chunkSize, 10)),
result.DumpStr(values.DumpByteString(byte(sz), true)),
))
// [04] Thirty-two octets of salt.
s, err := t.reader.ReadBytes(32)
Expand Down
15 changes: 15 additions & 0 deletions parse/tags/tag18_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

var (
tag18Body1 = []byte{0x01, 0x6a, 0xe6, 0x71, 0xca, 0xff, 0xf6, 0xb1, 0xff, 0x3f, 0x71, 0xc8, 0x77, 0x45, 0x88, 0x51, 0xff, 0xe3, 0xf2, 0xc3, 0x95, 0x57, 0xe7, 0x29, 0x80, 0xe8, 0xe5, 0x86, 0x7c, 0xea, 0x98, 0xf4, 0x04, 0xb3, 0x8a, 0xf8, 0x88, 0xc8, 0x91, 0xf7, 0x56, 0x7b, 0xcb, 0xad, 0x75, 0x40, 0x48, 0xd1, 0x5a, 0x3f, 0x3f, 0x2c, 0x1d, 0xe4, 0x36, 0xbb, 0xe9, 0xf7, 0x77, 0xb2, 0xb8, 0x2a, 0x44, 0x03, 0xbe, 0x78, 0xe2, 0x05, 0x3b, 0x44, 0xb6, 0xd8, 0x4e, 0x61, 0xa5, 0x43, 0x05, 0x76, 0x8a, 0x3c, 0x64}
tag18Body2 = []byte{0x02, 0x09, 0x02, 0x0e, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0xde, 0xad, 0xbe, 0xef}
)

const (
Expand All @@ -28,6 +29,19 @@ const (
01 6a e6 71 ca ff f6 b1 ff 3f 71 c8 77 45 88 51 ff e3 f2 c3 95 57 e7 29 80 e8 e5 86 7c ea 98 f4 04 b3 8a f8 88 c8 91 f7 56 7b cb ad 75 40 48 d1 5a 3f 3f 2c 1d e4 36 bb e9 f7 77 b2 b8 2a 44 03 be 78 e2 05 3b 44 b6 d8 4e 61 a5 43 05 76 8a 3c 64
Encrypted data (plain text + MDC SHA1(20 bytes); sym alg is specified in sym-key encrypted session key)
6a e6 71 ca ff f6 b1 ff 3f 71 c8 77 45 88 51 ff e3 f2 c3 95 57 e7 29 80 e8 e5 86 7c ea 98 f4 04 b3 8a f8 88 c8 91 f7 56 7b cb ad 75 40 48 d1 5a 3f 3f 2c 1d e4 36 bb e9 f7 77 b2 b8 2a 44 03 be 78 e2 05 3b 44 b6 d8 4e 61 a5 43 05 76 8a 3c 64
`
tag18Result21 = `Sym. Encrypted Integrity Protected Data Packet (tag 18) (40 bytes)
02 09 02 0e 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f de ad be ef
Symmetric Algorithm: AES with 256-bit key (sym 9)
09
AEAD Algorithm: OCB mode <RFC7253> (aead 2)
02
Chunk size: 1048576
0e
salt
00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
Encrypted data (4 bytes)
de ad be ef
`
)

Expand All @@ -42,6 +56,7 @@ func TestTag1(t *testing.T) {
{tag: 18, content: tag18Body1, ktm: nil, cxt: context.ModeNotSpecified, res: tag18Result11},
{tag: 18, content: tag18Body1, ktm: nil, cxt: context.ModePubEnc, res: tag18Result12},
{tag: 18, content: tag18Body1, ktm: nil, cxt: context.ModeSymEnc, res: tag18Result13},
{tag: 18, content: tag18Body2, ktm: nil, cxt: context.ModeNotSpecified, res: tag18Result21},
}
for _, tc := range testCases {
op := &packet.OpaquePacket{Tag: tc.tag, Contents: tc.content}
Expand Down
Loading