From 0d1ce1376795bea9f11d87de1e69f454c0a42fb5 Mon Sep 17 00:00:00 2001 From: Aron Wussler Date: Mon, 8 Apr 2024 11:38:52 +0200 Subject: [PATCH] Add parsing and serializing interfaces for keyrings (#275) * Add parsing and serializing interfaces for keyrings * Make error check more compact --- CHANGELOG.md | 12 ++++++++++++ crypto/keyring.go | 42 ++++++++++++++++++++++++++++++++++++++++++ crypto/keyring_test.go | 15 +++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40e5a43..6f4d87a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added +- API to serialize KeyRings to binary data: + ```go + func (keyRing *KeyRing) Serialize() ([]byte, error) + ``` +- API to parse KeyRings from binary data: + ```go + func NewKeyRingFromBinary(binKeys []byte) (*KeyRing, error) + ``` + ## [2.7.5] 2023-31-01 ### Added diff --git a/crypto/keyring.go b/crypto/keyring.go index 201f5ee..e6b2f68 100644 --- a/crypto/keyring.go +++ b/crypto/keyring.go @@ -49,6 +49,29 @@ func (keyRing *KeyRing) AddKey(key *Key) error { return nil } +// NewKeyRingFromBinary creates a new keyring with all the keys contained in the unarmored binary data. +// Note that it accepts only unlocked or public keys, as KeyRing cannot contain locked keys. +func NewKeyRingFromBinary(binKeys []byte) (*KeyRing, error) { + entities, err := openpgp.ReadKeyRing(bytes.NewReader(binKeys)) + if err != nil { + return nil, errors.Wrap(err, "gopenpgp: error in reading keyring") + } + + keyring := &KeyRing{} + for _, entity := range entities { + key, err := NewKeyFromEntity(entity) + if err != nil { + return nil, errors.Wrap(err, "gopenpgp: error in reading keyring") + } + + if err = keyring.AddKey(key); err != nil { + return nil, errors.Wrap(err, "gopenpgp: error in reading keyring") + } + } + + return keyring, nil +} + // --- Extract keys from keyring // GetKeys returns openpgp keys contained in this KeyRing. @@ -88,6 +111,25 @@ func (keyRing *KeyRing) getSigningEntity() (*openpgp.Entity, error) { return signEntity, nil } +// Serialize serializes a KeyRing to binary data. +func (keyRing *KeyRing) Serialize() ([]byte, error) { + var buffer bytes.Buffer + + for _, entity := range keyRing.entities { + var err error + if entity.PrivateKey == nil { + err = entity.Serialize(&buffer) + } else { + err = entity.SerializePrivateWithoutSigning(&buffer, nil) + } + if err != nil { + return nil, errors.Wrap(err, "gopenpgp: error in serializing keyring") + } + } + + return buffer.Bytes(), nil +} + // --- Extract info from key // CountEntities returns the number of entities in the keyring. diff --git a/crypto/keyring_test.go b/crypto/keyring_test.go index cbfb4be..ca4a277 100644 --- a/crypto/keyring_test.go +++ b/crypto/keyring_test.go @@ -148,6 +148,21 @@ func TestMultipleKeyRing(t *testing.T) { assert.Exactly(t, 1, singleKeyRing.CountDecryptionEntities()) } +func TestSerializeParse(t *testing.T) { + serialized, err := keyRingTestMultiple.Serialize() + assert.Nil(t, err) + + parsed, err := NewKeyRingFromBinary(serialized) + assert.Nil(t, err) + + assert.Exactly(t, 3, len(parsed.GetKeys())) + for i, parsedKey := range parsed.GetKeys() { + expectedKey, err := keyRingTestMultiple.GetKey(i) + assert.Nil(t, err) + assert.Exactly(t, parsedKey.GetFingerprint(), expectedKey.GetFingerprint()) + } +} + func TestClearPrivateKey(t *testing.T) { keyRingCopy, err := keyRingTestMultiple.Copy() if err != nil {