From cda34e7ea979bbfcd7cc3ae35be0835816863257 Mon Sep 17 00:00:00 2001 From: Andrew Gallagher Date: Fri, 20 Sep 2024 20:22:01 +0100 Subject: [PATCH] Serialise keyserver preferences and preferred keyserver; add tests --- openpgp/packet/signature.go | 39 +++++++++++++------ openpgp/packet/signature_test.go | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/openpgp/packet/signature.go b/openpgp/packet/signature.go index 435a90b6..3a4b366d 100644 --- a/openpgp/packet/signature.go +++ b/openpgp/packet/signature.go @@ -93,7 +93,7 @@ type Signature struct { PreferredCipherSuites [][2]uint8 IssuerKeyId *uint64 IssuerFingerprint []byte - SignerUserId, PreferredKeyserver *string + SignerUserId *string IsPrimaryId *bool Notations []*Notation IntendedRecipients []*Recipient @@ -110,6 +110,16 @@ type Signature struct { // See RFC 9580, section 5.2.3.22 for details. TrustRegularExpression *string + // KeyserverPrefsValid is set if any keyserver preferences were given. See RFC 9580, section + // 5.2.3.25 for details. + KeyserverPrefsValid bool + KeyserverPrefNoModify bool + + // PreferredKeyserver can be set to a URI where the latest version of the + // key that this signature is made over can be found. See RFC 9580, section + // 5.2.3.26 for details. + PreferredKeyserver string + // PolicyURI can be set to the URI of a document that describes the // policy under which the signature was issued. See RFC 9580, section // 5.2.3.28 for details. @@ -120,11 +130,6 @@ type Signature struct { FlagsValid bool FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage, FlagSplitKey, FlagAuthenticate, FlagGroupKey bool - // KeyserverPrefsValid is set if any keyserver preferences were given. See RFC 9580, section - // 5.2.3.25 for details. - KeyserverPrefsValid bool - KeyserverPrefNoModify bool - // RevocationReason is set if this signature has been revoked. // See RFC 9580, section 5.2.3.31 for details. RevocationReason *ReasonForRevocation @@ -542,8 +547,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r } case prefKeyserverSubpacket: // Preferred keyserver, section 5.2.3.26 - preferredKeyserver := string(subpacket) - sig.PreferredKeyserver = &preferredKeyserver + sig.PreferredKeyserver = string(subpacket) case primaryUserIdSubpacket: // Primary User ID, section 5.2.3.27 if len(subpacket) != 1 { @@ -642,8 +646,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r *sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket[13:21]) } case intendedRecipientSubpacket: - // Intended Recipient Fingerprint - // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-intended-recipient-fingerpr + // Intended Recipient Fingerprint, section 5.2.3.36 if len(subpacket) < 1 { return nil, errors.StructuralError("invalid intended recipient fingerpring length") } @@ -655,8 +658,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r copy(fingerprint, subpacket[1:]) sig.IntendedRecipients = append(sig.IntendedRecipients, &Recipient{int(version), fingerprint}) case prefCipherSuitesSubpacket: - // Preferred AEAD cipher suites - // See https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#name-preferred-aead-ciphersuites + // Preferred AEAD cipher suites, section 5.2.3.15 if len(subpacket)%2 != 0 { err = errors.StructuralError("invalid aead cipher suite length") return @@ -1307,6 +1309,19 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp if len(sig.PreferredCompression) > 0 { subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression}) } + // Keyserver Preferences + // Keyserver preferences may only appear in self-signatures or certification signatures. + if sig.KeyserverPrefsValid { + var prefs byte + if sig.KeyserverPrefNoModify { + prefs |= KeyserverPrefNoModify + } + subpackets = append(subpackets, outputSubpacket{true, keyserverPrefsSubpacket, false, []byte{prefs}}) + } + // Preferred Keyserver + if len(sig.PreferredKeyserver) > 0 { + subpackets = append(subpackets, outputSubpacket{true, prefKeyserverSubpacket, false, []uint8(sig.PreferredKeyserver)}) + } // Primary User ID if sig.IsPrimaryId != nil && *sig.IsPrimaryId { subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}}) diff --git a/openpgp/packet/signature_test.go b/openpgp/packet/signature_test.go index 5443243f..edc43114 100644 --- a/openpgp/packet/signature_test.go +++ b/openpgp/packet/signature_test.go @@ -215,6 +215,71 @@ func TestSignatureWithLifetime(t *testing.T) { } } +func TestSignatureWithPrefKeyserver(t *testing.T) { + testKeyserver := "hkps://example.com" + sig := &Signature{ + SigType: SigTypeGenericCert, + PubKeyAlgo: PubKeyAlgoRSA, + Hash: crypto.SHA256, + PreferredKeyserver: testKeyserver, + KeyserverPrefsValid: true, + KeyserverPrefNoModify: true, + } + + packet, err := Read(readerFromHex(rsaPkDataHex)) + if err != nil { + t.Fatalf("failed to deserialize public key: %v", err) + } + pubKey := packet.(*PublicKey) + + packet, err = Read(readerFromHex(privKeyRSAHex)) + if err != nil { + t.Fatalf("failed to deserialize private key: %v", err) + } + privKey := packet.(*PrivateKey) + + err = privKey.Decrypt([]byte("testing")) + if err != nil { + t.Fatalf("failed to decrypt private key: %v", err) + } + + err = sig.SignUserId("", pubKey, privKey, nil) + if err != nil { + t.Errorf("failed to sign user id: %v", err) + } + + buf := bytes.NewBuffer([]byte{}) + err = sig.Serialize(buf) + if err != nil { + t.Errorf("failed to serialize signature: %v", err) + } + + packet, _ = Read(bytes.NewReader(buf.Bytes())) + sig = packet.(*Signature) + if sig.PreferredKeyserver != testKeyserver { + t.Errorf("preferred keyserver is wrong: %s instead of %s", sig.PreferredKeyserver, testKeyserver) + } + if sig.KeyserverPrefsValid != true { + t.Errorf("keyserver preferences is not true") + } + if sig.KeyserverPrefNoModify != true { + t.Errorf("keyserverNoModify is not true") + } + + for _, subPacket := range sig.rawSubpackets { + if subPacket.subpacketType == prefKeyserverSubpacket { + if subPacket.isCritical { + t.Errorf("preferred keyserver subpacket is marked as critical") + } + } + if subPacket.subpacketType == keyserverPrefsSubpacket { + if subPacket.isCritical { + t.Errorf("keyserver preferences subpacket is marked as critical") + } + } + } +} + func TestSignatureWithPolicyURI(t *testing.T) { testPolicy := "This is a test policy" sig := &Signature{