key.go

  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}