1package identity
2
3import (
4 "encoding/hex"
5 "encoding/json"
6 "fmt"
7 "strings"
8
9 "github.com/pkg/errors"
10 "golang.org/x/crypto/openpgp/armor"
11 "golang.org/x/crypto/openpgp/packet"
12)
13
14// Key hold a cryptographic public key
15type Key struct {
16 // PubKey is the armored PGP public key.
17 armoredPublicKey string
18
19 // memoized decoded public key
20 publicKey *packet.PublicKey
21}
22
23type keyJSON struct {
24 ArmoredPublicKey string `json:"armored_pub_key"`
25}
26
27func NewKeyFromArmored(armoredPGPKey string) (*Key, error) {
28 publicKey, err := parsePublicKey(armoredPGPKey)
29 if err != nil {
30 return nil, err
31 }
32
33 return &Key{armoredPGPKey, publicKey}, nil
34}
35
36func (k *Key) MarshalJSON() ([]byte, error) {
37 return json.Marshal(keyJSON{
38 ArmoredPublicKey: k.armoredPublicKey,
39 })
40}
41
42func (k *Key) UnmarshalJSON(data []byte) error {
43 var aux keyJSON
44
45 if err := json.Unmarshal(data, &aux); err != nil {
46 return err
47 }
48
49 k.armoredPublicKey = aux.ArmoredPublicKey
50
51 return nil
52}
53
54func (k *Key) Validate() error {
55 if k.publicKey != nil {
56 return nil
57 }
58
59 publicKey, err := parsePublicKey(k.armoredPublicKey)
60 if err != nil {
61 return errors.Wrap(err, "invalid public key")
62 }
63 k.publicKey = publicKey
64
65 return nil
66}
67
68func (k *Key) Clone() *Key {
69 clone := *k
70 return &clone
71}
72
73func (k *Key) publicKey() *packet.PublicKey {
74 if k.publicKey != nil {
75 return k.publicKey
76 }
77
78 publicKey, err := parsePublicKey(k.armoredPublicKey)
79 if err != nil {
80 // Coding problem, a key should be validated before use
81 panic("invalid key: " + err.Error())
82 }
83
84 k.publicKey = publicKey
85 return k.publicKey
86}
87
88func (k Key) Armored() string {
89 return k.armoredPublicKey
90}
91
92func (k Key) Fingerprint() string {
93 return encodeKeyFingerprint(k.publicKey().Fingerprint)
94}
95
96func parsePublicKey(armoredPublicKey string) (*packet.PublicKey, error) {
97 block, err := armor.Decode(strings.NewReader(armoredPublicKey))
98 if err != nil {
99 return nil, errors.Wrap(err, "failed to decode armored public key")
100 }
101
102 reader := packet.NewReader(block.Body)
103 p, err := reader.Next()
104 if err != nil {
105 return nil, errors.Wrap(err, "failed to read public key packet")
106 }
107
108 publicKey, ok := p.(*packet.PublicKey)
109 if !ok {
110 return nil, errors.New("got no packet.publicKey")
111 }
112
113 return publicKey, nil
114}
115
116// decodeKeyFingerprint decodes a 40 hex digits long fingerprint into bytes.
117func decodeKeyFingerprint(keyFingerprint string) ([20]byte, error) {
118 var fingerprint [20]byte
119 fingerprintBytes, err := hex.DecodeString(keyFingerprint)
120 if err != nil {
121 return fingerprint, err
122 }
123 if len(fingerprintBytes) != 20 {
124 return fingerprint, fmt.Errorf("expected 20 bytes not %d", len(fingerprintBytes))
125 }
126 copy(fingerprint[:], fingerprintBytes)
127 return fingerprint, nil
128}
129
130// encodeKeyFingerprint encode a byte representation of a fingerprint into a 40 hex digits string.
131func encodeKeyFingerprint(fingerprint [20]byte) string {
132 return strings.ToUpper(hex.EncodeToString(fingerprint[:]))
133}