Skip to content

Commit

Permalink
Add Notation data support in subpackets
Browse files Browse the repository at this point in the history
  • Loading branch information
wussler committed Dec 6, 2022
1 parent cf6655e commit dc1905b
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 0 deletions.
42 changes: 42 additions & 0 deletions openpgp/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,48 @@ func TestNewEntityPrivateSerialization(t *testing.T) {
}
}

func TestNotationPacket(t *testing.T) {
keys, err := ReadArmoredKeyRing(bytes.NewBufferString(keyWithNotation))
if err != nil {
t.Fatal(err)
}

if len(keys) != 1 {
t.Errorf("Failed to accept key, %d", len(keys))
}

identity := keys[0].Identities["Testt <test@exa>"]

if numSigs, numExpected := len(identity.Signatures), 1; numSigs != numExpected {
t.Fatalf("got %d signatures, expected %d", numSigs, numExpected)
}

notations := identity.Signatures[0].Notations
if numSigs, numExpected := len(notations), 2; numSigs != numExpected {
t.Fatalf("got %d Data Notation subpackets, expected %d", numSigs, numExpected)
}

if notations[0].IsHumanReadable() != true {
t.Fatalf("got false, expected true")
}

if notations[0].GetName() != "[email protected]" {
t.Fatalf("got %s, expected [email protected]", notations[0].GetName())
}

if notations[0].GetStringValue() != "2" {
t.Fatalf("got %s, expected 2", notations[0].GetName())
}

if notations[1].GetName() != "[email protected]" {
t.Fatalf("got %s, expected [email protected]", notations[0].GetName())
}

if notations[1].GetStringValue() != "3" {
t.Fatalf("got %s, expected 3", notations[0].GetName())
}
}

func TestEntityPrivateSerialization(t *testing.T) {
keys, err := ReadArmoredKeyRing(bytes.NewBufferString(armoredPrivateKeyBlock))
if err != nil {
Expand Down
32 changes: 32 additions & 0 deletions openpgp/keys_test_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,3 +518,35 @@ XLCBln+wdewpU4ChEffMUDRBfqfQco/YsMqWV7bHJHAO0eC/DMKCjyU90xdH7R/d
QgqsfguR1PqPuJxpXV4bSr6CGAAAAA==
=MSvh
-----END PGP PRIVATE KEY BLOCK-----`

const keyWithNotation = `-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFzQOToBCADd0Pwh8edZ6gR3x49L1PaBPtiAQUr1QDUDWeNes8co5MTFl5hG
lHzptt+VD0JGucuIkvi34f5z2ZbInAV/xYDX3kSYefy6LB8XJD527I/o9bqY1P7T
PjtTZ4emcqNGkGhV2hNGV+hFcTevUS9Ty4vGg6P7X6RjfjeTrClHelJT8+9IiH+4
0h4X/Y1hwoijRWanYnZjuAUIrOXnG76iknXQRGc8th8iI0oIZfKQomfF0K5lXFhH
SU8Yvmik3vCTLHC6Ce0GVRCTIcU0/Xi2MK/Yrg9bGzSblHxomLU0NT6pee+2UjqR
BZXOAPLY66Lsh1oqxQ6ihVnOmbraU9glAGm1ABEBAAG0EFRlc3R0IDx0ZXN0QGV4
YT6JAYoEEwEIAHQCGwMFCQPCZwAFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQQZ
jHIqS6wzbp2qrkRXnQGzq+FUDgUCXNA5VBoUgAAAAAAQAAF0ZXN0QGV4YW1wbGUu
Y29tMhoUgAAAAAAQAAF0ZXN0QGV4YW1wbGUuY29tMwAKCRBXnQGzq+FUDoLiCACn
ls1iy0hT59Xt3o3tmmxe1jLzkbQEprR6MMfZamtex5/BHViu2HPAu5i13mXyBRnJ
4Zvd/HUxJukP3tdQyJIlZFe8XwloMoRAA37KOZ5QGyKH8Jxq3LcAcQOOkFtWgr+Z
JbjUKF1IuqCsK6SYB8f7SVKgpZk/kqG3HE3gk72ONnqdvwOa9cIhAuZScdgZ+PLC
6W/0+IrnQIasvKeEWeK4u6/NYT35HUsUE/9Z6WKF+qxJnp5Pi2Q5cio6bFlGDNQb
+MiuiEb3Mzb3ev2PVg7WELBRXOg8QlCxrghqfi1SH791mmiyGK+GIQgnjRwMejTh
dNsnHYag/KAewag55AQvuQENBFzQOToBCADJD+auK+Opo1q3ZLxODMyw5//XHJH4
0vQPNawyBiOdBuneWHF3jfDwGa+lOftUx1abSwsq+Qs955THgLVSiJvivHWVy8pN
tPv0XLa9rMj2wh/OmckbcgzSMeJJIz09bTj095ONPGYW2D4AcpkOc+b5bkqV6r+N
yk9nopPJNCNqYYJtecTClDT5haRKBP5XjXRVsIXva/nHZGXKQLX8iWG2D5DOJNDP
ZkAEoIPg+7J85Q3u2iSFPnLPzKHlMAoQW8d9RAEYyJ6WqiILUIDShhvXg+RIkzri
wY/WkvhB/Kpj0r1SRbNhWRpmOWCR+0a2uHaLz9X0KTP7WMqQbmIdpRgZABEBAAGJ
ATwEGAEIACYWIQQZjHIqS6wzbp2qrkRXnQGzq+FUDgUCXNA5OgIbDAUJA8JnAAAK
CRBXnQGzq+FUDgI6B/9Far0CUR6rWvUiviBY4P5oe44I9P9P7ilWmum1cIQWxMyF
0sc5tRcVLpMomURlrDz0TR5GNs+nuGAHTRBfN7VO0Y+R/LyEd1Rf80ONObXOqzMp
vF9CdW3a7W4WicZwnGgUOImTICazR2VmR+RREdZshqrOCaOnuKmN3QwGH1zzFwJA
sTbLoNMdBv8SEARaRVOWPM1HwJ701mMYF48FqhHd5uinH/ZCeBhqrBfhmXa68FWx
xuyJz6ttl5Fp4nsB3waQdgPGZJ9NUrGfopLUZ44xDuJjBONd7rbYOh71TWbHd8wG
V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
=et/d
-----END PGP PUBLIC KEY BLOCK-----`
45 changes: 45 additions & 0 deletions openpgp/packet/notation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package packet

// Notation type represents a Notation Data subpacket
// see https://tools.ietf.org/html/rfc4880#section-5.2.3.16
type Notation struct {
flags []byte
name string
value []byte
critical bool
}

func (not *Notation) IsHumanReadable() (bool) {
return not.flags[0] & 0x80 == 0x80
}

func (not *Notation) GetName() (string) {
return not.name
}

func (not *Notation) GetBinaryValue() ([]byte) {
return not.value
}

func (not *Notation) GetStringValue() (string) {
return string(not.value)
}

func (not *Notation) IsCritical() (bool) {
return not.critical
}

func (not *Notation) getData() ([]byte) {
nameLen := len(not.name)
valueLen := len(not.value)
nameData := []byte(not.name)

data := not.flags
data[4] = byte(nameLen >> 8)
data[5] = byte(nameLen)
data[6] = byte(valueLen >> 8)
data[7] = byte(valueLen)

data = append(data, nameData...)
return append(data, not.value...)
}
31 changes: 31 additions & 0 deletions openpgp/packet/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type Signature struct {
IssuerFingerprint []byte
SignerUserId *string
IsPrimaryId *bool
Notations []Notation

// PolicyURI can be set to the URI of a document that describes the
// policy under which the signature was issued. See RFC 4880, section
Expand Down Expand Up @@ -224,6 +225,7 @@ const (
keyExpirationSubpacket signatureSubpacketType = 9
prefSymmetricAlgosSubpacket signatureSubpacketType = 11
issuerSubpacket signatureSubpacketType = 16
notationDataSubpacket signatureSubpacketType = 20
prefHashAlgosSubpacket signatureSubpacketType = 21
prefCompressionSubpacket signatureSubpacketType = 22
primaryUserIdSubpacket signatureSubpacketType = 25
Expand Down Expand Up @@ -330,6 +332,24 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r
}
sig.IssuerKeyId = new(uint64)
*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket)
case notationDataSubpacket:
// Notation data, section 5.2.3.16
nameLength := uint32(subpacket[4])<<8 | uint32(subpacket[5])
valueLength := uint32(subpacket[6])<<8 | uint32(subpacket[7])

if len(subpacket) != (int(nameLength) + int(valueLength) + 8) {
err = errors.StructuralError("notation data subpacket with bad length")
return
}

notation := Notation{
flags: subpacket[0:4],
name: string(subpacket[8: (nameLength + 8)]),
value: subpacket[(nameLength + 8) : (valueLength + nameLength + 8)],
critical: isCritical,
}

sig.Notations = append(sig.Notations, notation)
case prefHashAlgosSubpacket:
// Preferred hash algorithms, section 5.2.3.8
if !isHashed {
Expand Down Expand Up @@ -885,6 +905,17 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{flags}})
}

for _, notation := range sig.Notations {
subpackets = append(
subpackets,
outputSubpacket{
true,
notationDataSubpacket,
notation.IsCritical(),
notation.getData(),
})
}

// The following subpackets may only appear in self-signatures.

var features = byte(0x00)
Expand Down

0 comments on commit dc1905b

Please sign in to comment.