From 88d8976c126df0f81fa83f73cdbf873ff13a7aa9 Mon Sep 17 00:00:00 2001 From: Spiegel Date: Fri, 15 May 2026 15:22:38 +0900 Subject: [PATCH] Extend key-version dependent parsing for v6 --- parse/tags/key_version_test.go | 101 +++++++++++++++++++++++++++++++++ parse/tags/pubkey.go | 17 ++---- parse/tags/seckey.go | 13 +++-- parse/tags/sub33.go | 2 +- parse/tags/sub35.go | 2 +- parse/tags/tag01.go | 8 +-- parse/tags/tag02.go | 18 +++--- parse/tags/tag03.go | 6 +- parse/tags/tag04.go | 8 +-- 9 files changed, 137 insertions(+), 38 deletions(-) create mode 100644 parse/tags/key_version_test.go diff --git a/parse/tags/key_version_test.go b/parse/tags/key_version_test.go new file mode 100644 index 0000000..4c9c572 --- /dev/null +++ b/parse/tags/key_version_test.go @@ -0,0 +1,101 @@ +package tags + +import ( + "strings" + "testing" + + "github.com/ProtonMail/go-crypto/openpgp/packet" + + "github.com/goark/gpgpdump/parse/context" + "github.com/goark/gpgpdump/parse/reader" + "github.com/goark/gpgpdump/parse/values" +) + +func TestTag01FingerprintVer6(t *testing.T) { + tag := &tag01{tagInfo{cxt: context.New(), reader: reader.New(make([]byte, 32))}} + item, err := tag.fingerprint(6) + if err != nil { + t.Fatalf("fingerprint(6) error = %v", err) + } + if item == nil { + t.Fatal("fingerprint(6) = nil, want non-nil") + } + if tag.reader.Rest() != 0 { + t.Fatalf("reader.Rest() = %d, want 0", tag.reader.Rest()) + } +} + +func TestTag04FingerprintVer6(t *testing.T) { + tag := &tag04{tagInfo{cxt: context.New(), reader: reader.New(make([]byte, 32))}} + item, err := tag.fingerprint(6) + if err != nil { + t.Fatalf("fingerprint(6) error = %v", err) + } + if item == nil { + t.Fatal("fingerprint(6) = nil, want non-nil") + } + if tag.reader.Rest() != 0 { + t.Fatalf("reader.Rest() = %d, want 0", tag.reader.Rest()) + } +} + +func TestSub33Version6Note(t *testing.T) { + body := append([]byte{0x06}, make([]byte, 32)...) + s := newSub33(context.New(), values.SuboacketID(33), body) + item, err := s.Parse() + if err != nil { + t.Fatalf("Parse() error = %v", err) + } + if len(item.Items) == 0 { + t.Fatal("items is empty") + } + if item.Items[0].Note != "need 32 octets length" { + t.Fatalf("Version note = %q, want %q", item.Items[0].Note, "need 32 octets length") + } +} + +func TestSub35Version6Note(t *testing.T) { + body := append([]byte{0x06}, make([]byte, 32)...) + s := newSub35(context.New(), values.SuboacketID(35), body) + item, err := s.Parse() + if err != nil { + t.Fatalf("Parse() error = %v", err) + } + if len(item.Items) == 0 { + t.Fatal("items is empty") + } + if item.Items[0].Note != "need 32 octets length" { + t.Fatalf("Version note = %q, want %q", item.Items[0].Note, "need 32 octets length") + } +} + +func TestTag02Version6UsesV5Layout(t *testing.T) { + // version(6), sig type, pubid(RSA), hashid, hashed len(4), unhashed len(4), hash left(2), salt(16), rsa sig mpi + body := []byte{0x06, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + body = append(body, make([]byte, 16)...) + body = append(body, []byte{0x00, 0x08, 0x01}...) + op := &packet.OpaquePacket{Tag: 2, Contents: body} + cxt := context.New(context.Set(context.DEBUG, true), context.Set(context.UTC, true)) + item, err := NewTag(op, cxt).Parse() + if err != nil { + t.Fatalf("Parse() error = %v", err) + } + if !strings.Contains(item.String(), "Random values used as salt") { + t.Fatal("output does not contain v5/v6 salt field") + } +} + +func TestTag03Version6UsesV5Layout(t *testing.T) { + // version(6), count, symid, aeadid(EAX), count, s2k(simple,sha1), iv(16) + body := []byte{0x06, 0x04, 0x09, 0x01, 0x02, 0x00, 0x02} + body = append(body, make([]byte, 16)...) + op := &packet.OpaquePacket{Tag: 3, Contents: body} + cxt := context.New(context.Set(context.DEBUG, true), context.Set(context.UTC, true)) + item, err := NewTag(op, cxt).Parse() + if err != nil { + t.Fatalf("Parse() error = %v", err) + } + if !strings.Contains(item.String(), "AEAD Algorithm") { + t.Fatal("output does not contain v5/v6 AEAD field") + } +} diff --git a/parse/tags/pubkey.go b/parse/tags/pubkey.go index 087686b..fad93e5 100644 --- a/parse/tags/pubkey.go +++ b/parse/tags/pubkey.go @@ -29,18 +29,13 @@ func newPubkey(cxt *context.Context, reader *reader.Reader, pubVer *values.Versi // Parse Public-key packet func (p *pubkeyInfo) Parse(parent *result.Item) error { - switch true { - case p.pubVer.IsDraft(): - return p.parseV5(parent) - case p.pubVer.IsCurrent(): + switch p.pubVer.Number() { + case 3: + return p.parseV3(parent) + case 4: return p.parseV4(parent) - case p.pubVer.IsOld(): - switch p.pubVer.Number() { - case 3: - return p.parseV3(parent) - default: - } - default: + case 5, 6: + return p.parseV5(parent) } return nil } diff --git a/parse/tags/seckey.go b/parse/tags/seckey.go index 4088f41..67915cd 100644 --- a/parse/tags/seckey.go +++ b/parse/tags/seckey.go @@ -69,7 +69,7 @@ func (p *seckeyInfo) Parse(parent *result.Item) error { parent.Add(aeadid.ToItem(p.cxt.Debug())) } // [Optional] Only for a version 5 packet, and if string-to-key usage octet was 255, 254, or 253, an one-octet count of the following field. - if p.pubVer.Number() == 5 { + if p.isV5Style() { switch usage { case 253, 254, 255: ct, err := rOpt.ReadByte() @@ -109,7 +109,7 @@ func (p *seckeyInfo) Parse(parent *result.Item) error { parent.Add(iv) } - if p.pubVer.Number() == 5 && usage != 0 { + if p.isV5Style() && usage != 0 { if rOpt.Rest() > 0 { parent.Add(values.RawData(rOpt, "Unknown data", p.cxt.Debug())) } @@ -162,7 +162,7 @@ func (p *seckeyInfo) Parse(parent *result.Item) error { } } - if p.pubVer.Number() == 5 { + if p.isV5Style() { if p.reader.Rest() > 0 { parent.Add(values.RawData(p.reader, "Unknown data", p.cxt.Debug())) } @@ -173,7 +173,7 @@ func (p *seckeyInfo) Parse(parent *result.Item) error { // getField1 returns reader.Reader for optional fields func (p *seckeyInfo) getField1(usage byte) (*reader.Reader, error) { // Only for a version 5 packet where the secret key material is encrypted (that is, where the previous octet is not zero), a one-octet scalar octet count of the cumulative length of all the following optional string-to-key parameter fields. - if p.pubVer.Number() == 5 && usage != 0 { + if p.isV5Style() && usage != 0 { l, err := p.reader.ReadByte() if err != nil { return nil, errs.New("illegal length of option field", errs.WithCause(err)) @@ -191,6 +191,11 @@ func (p *seckeyInfo) getField1(usage byte) (*reader.Reader, error) { return p.reader, nil } +func (p *seckeyInfo) isV5Style() bool { + v := p.pubVer.Number() + return v == 5 || v == 6 +} + // iv returns context.Item for Initialization Vector func (p *seckeyInfo) iv(rOpt *reader.Reader, usage byte, symid values.SymID, aeadid values.AEADID) (*result.Item, error) { sz64 := int64(symid.IVLen()) diff --git a/parse/tags/sub33.go b/parse/tags/sub33.go index 13d316e..02a3894 100644 --- a/parse/tags/sub33.go +++ b/parse/tags/sub33.go @@ -35,7 +35,7 @@ func (s *sub33) Parse() (*result.Item, error) { switch ver { case 4: itm.Note = "need 20 octets length" - case 5: + case 5, 6: itm.Note = "need 32 octets length" default: itm.Note = values.Unknown diff --git a/parse/tags/sub35.go b/parse/tags/sub35.go index ccefabf..ab311b6 100644 --- a/parse/tags/sub35.go +++ b/parse/tags/sub35.go @@ -35,7 +35,7 @@ func (s *sub35) Parse() (*result.Item, error) { switch ver { case 4: itm.Note = "need 20 octets length" - case 5: + case 5, 6: itm.Note = "need 32 octets length" default: itm.Note = values.Unknown diff --git a/parse/tags/tag01.go b/parse/tags/tag01.go index aa9a7ba..2091ebe 100644 --- a/parse/tags/tag01.go +++ b/parse/tags/tag01.go @@ -32,13 +32,13 @@ func (t *tag01) Parse() (*result.Item, error) { } version := values.PubSessKeyVer(v) rootInfo.Add(version.ToItem(t.cxt.Debug())) - switch true { - case version.IsCurrent(): + switch version.Number() { + case 3: _, err := t.parseV3(rootInfo) if err != nil { return rootInfo, errs.Wrap(err) } - case version.IsDraft(): + case 5, 6: _, err := t.parseV5(rootInfo) if err != nil { return rootInfo, errs.Wrap(err) @@ -102,7 +102,7 @@ func (t *tag01) fingerprint(ver byte) (*result.Item, error) { switch ver { case 4: n = 20 - case 5: + case 5, 6: n = 32 } if n == 0 { diff --git a/parse/tags/tag02.go b/parse/tags/tag02.go index 8601673..fad5b10 100644 --- a/parse/tags/tag02.go +++ b/parse/tags/tag02.go @@ -34,23 +34,21 @@ func (t *tag02) Parse() (*result.Item, error) { version := values.SigVer(v) rootInfo.Add(version.ToItem(t.cxt.Debug())) - switch true { - case version.IsDraft(): - _, err := t.parseV5(rootInfo) + switch version.Number() { + case 3: + _, err := t.parseV3(rootInfo) if err != nil { return rootInfo, errs.Wrap(err) } - case version.IsCurrent(): + case 4: _, err := t.parseV4(rootInfo) if err != nil { return rootInfo, errs.Wrap(err) } - case version.IsOld(): - if version.Number() == 3 { - _, err2 := t.parseV3(rootInfo) - if err2 != nil { - return rootInfo, errs.Wrap(err) - } + case 5, 6: + _, err := t.parseV5(rootInfo) + if err != nil { + return rootInfo, errs.Wrap(err) } } diff --git a/parse/tags/tag03.go b/parse/tags/tag03.go index 46fe20a..8f3360a 100644 --- a/parse/tags/tag03.go +++ b/parse/tags/tag03.go @@ -34,13 +34,13 @@ func (t *tag03) Parse() (*result.Item, error) { version := values.SymSessKeyVer(v) rootInfo.Add(version.ToItem(t.cxt.Debug())) - switch true { - case version.IsCurrent(): + switch version.Number() { + case 4: _, err := t.parseV4(rootInfo) if err != nil { return rootInfo, errs.Wrap(err) } - case version.IsDraft(): + case 5, 6: _, err := t.parseV5(rootInfo) if err != nil { return rootInfo, errs.Wrap(err) diff --git a/parse/tags/tag04.go b/parse/tags/tag04.go index 0c58038..6ca8788 100644 --- a/parse/tags/tag04.go +++ b/parse/tags/tag04.go @@ -31,13 +31,13 @@ func (t *tag04) Parse() (*result.Item, error) { } version := values.OneSigVer(v) rootInfo.Add(version.ToItem(t.cxt.Debug())) - switch true { - case version.IsCurrent(): + switch version.Number() { + case 3: _, err := t.parseV3(rootInfo) if err != nil { return rootInfo, errs.Wrap(err) } - case version.IsDraft(): + case 5, 6: _, err := t.parseV5(rootInfo) if err != nil { return rootInfo, errs.Wrap(err) @@ -150,7 +150,7 @@ func (t *tag04) randomValue(rv []byte) *result.Item { } func (t *tag04) fingerprint(ver byte) (*result.Item, error) { - if ver != 5 { + if ver != 5 && ver != 6 { return nil, errs.New("illegal key version number", errs.WithContext("key_version", int(ver))) } var n int64 = 32