key.go

  1package identity
  2
  3import (
  4	"bytes"
  5	"encoding/json"
  6	"fmt"
  7	"io"
  8	"strings"
  9	"time"
 10
 11	"github.com/pkg/errors"
 12	"golang.org/x/crypto/openpgp"
 13	"golang.org/x/crypto/openpgp/armor"
 14	"golang.org/x/crypto/openpgp/packet"
 15
 16	"github.com/MichaelMure/git-bug/repository"
 17)
 18
 19type Key struct {
 20	public  *packet.PublicKey
 21	private *packet.PrivateKey
 22}
 23
 24// GenerateKey generate a keypair (public+private)
 25func GenerateKey() *Key {
 26	entity, err := openpgp.NewEntity("", "", "", &packet.Config{
 27		// The armored format doesn't include the creation time, which makes the round-trip data not being fully equal.
 28		// We don't care about the creation time so we can set it to the zero value.
 29		Time: func() time.Time {
 30			return time.Time{}
 31		},
 32	})
 33	if err != nil {
 34		panic(err)
 35	}
 36	return &Key{
 37		public:  entity.PrimaryKey,
 38		private: entity.PrivateKey,
 39	}
 40}
 41
 42// generatePublicKey generate only a public key (only useful for testing)
 43// See GenerateKey for the details.
 44func generatePublicKey() *Key {
 45	k := GenerateKey()
 46	k.private = nil
 47	return k
 48}
 49
 50func (k *Key) MarshalJSON() ([]byte, error) {
 51	var buf bytes.Buffer
 52	w, err := armor.Encode(&buf, openpgp.PublicKeyType, nil)
 53	if err != nil {
 54		return nil, err
 55	}
 56	err = k.public.Serialize(w)
 57	if err != nil {
 58		return nil, err
 59	}
 60	err = w.Close()
 61	if err != nil {
 62		return nil, err
 63	}
 64	return json.Marshal(buf.String())
 65}
 66
 67func (k *Key) UnmarshalJSON(data []byte) error {
 68	var armored string
 69	err := json.Unmarshal(data, &armored)
 70	if err != nil {
 71		return err
 72	}
 73
 74	block, err := armor.Decode(strings.NewReader(armored))
 75	if err == io.EOF {
 76		return fmt.Errorf("no armored data found")
 77	}
 78	if err != nil {
 79		return err
 80	}
 81
 82	if block.Type != openpgp.PublicKeyType {
 83		return fmt.Errorf("invalid key type")
 84	}
 85
 86	reader := packet.NewReader(block.Body)
 87	p, err := reader.Next()
 88	if err != nil {
 89		return errors.Wrap(err, "failed to read public key packet")
 90	}
 91
 92	public, ok := p.(*packet.PublicKey)
 93	if !ok {
 94		return errors.New("got no packet.publicKey")
 95	}
 96
 97	// The armored format doesn't include the creation time, which makes the round-trip data not being fully equal.
 98	// We don't care about the creation time so we can set it to the zero value.
 99	public.CreationTime = time.Time{}
100
101	k.public = public
102	return nil
103}
104
105func (k *Key) Validate() error {
106	if k.public == nil {
107		return fmt.Errorf("nil public key")
108	}
109	if !k.public.CanSign() {
110		return fmt.Errorf("public key can't sign")
111	}
112
113	if k.private != nil {
114		if !k.private.CanSign() {
115			return fmt.Errorf("private key can't sign")
116		}
117	}
118
119	return nil
120}
121
122func (k *Key) Clone() *Key {
123	clone := &Key{}
124
125	pub := *k.public
126	clone.public = &pub
127
128	if k.private != nil {
129		priv := *k.private
130		clone.private = &priv
131	}
132
133	return clone
134}
135
136func (k *Key) EnsurePrivateKey(repo repository.RepoKeyring) error {
137	if k.private != nil {
138		return nil
139	}
140
141	// item, err := repo.Keyring().Get(k.Fingerprint())
142	// if err != nil {
143	// 	return fmt.Errorf("no private key found for %s", k.Fingerprint())
144	// }
145	//
146
147	panic("TODO")
148}
149
150func (k *Key) Fingerprint() string {
151	return string(k.public.Fingerprint[:])
152}
153
154func (k *Key) PGPEntity() *openpgp.Entity {
155	return &openpgp.Entity{
156		PrimaryKey: k.public,
157		PrivateKey: k.private,
158	}
159}
160
161var _ openpgp.KeyRing = &PGPKeyring{}
162
163// PGPKeyring implement a openpgp.KeyRing from an slice of Key
164type PGPKeyring []*Key
165
166func (pk PGPKeyring) KeysById(id uint64) []openpgp.Key {
167	var result []openpgp.Key
168	for _, key := range pk {
169		if key.public.KeyId == id {
170			result = append(result, openpgp.Key{
171				PublicKey:  key.public,
172				PrivateKey: key.private,
173			})
174		}
175	}
176	return result
177}
178
179func (pk PGPKeyring) KeysByIdUsage(id uint64, requiredUsage byte) []openpgp.Key {
180	// the only usage we care about is the ability to sign, which all keys should already be capable of
181	return pk.KeysById(id)
182}
183
184func (pk PGPKeyring) DecryptionKeys() []openpgp.Key {
185	result := make([]openpgp.Key, len(pk))
186	for i, key := range pk {
187		result[i] = openpgp.Key{
188			PublicKey:  key.public,
189			PrivateKey: key.private,
190		}
191	}
192	return result
193}