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}