1package pgpkeys
2
3import (
4	"bytes"
5	"encoding/base64"
6	"fmt"
7
8	"github.com/keybase/go-crypto/openpgp"
9	"github.com/keybase/go-crypto/openpgp/packet"
10)
11
12// EncryptShares takes an ordered set of byte slices to encrypt and the
13// corresponding base64-encoded public keys to encrypt them with, encrypts each
14// byte slice with the corresponding public key.
15//
16// Note: There is no corresponding test function; this functionality is
17// thoroughly tested in the init and rekey command unit tests
18func EncryptShares(input [][]byte, pgpKeys []string) ([]string, [][]byte, error) {
19	if len(input) != len(pgpKeys) {
20		return nil, nil, fmt.Errorf("mismatch between number items to encrypt and number of PGP keys")
21	}
22	encryptedShares := make([][]byte, 0, len(pgpKeys))
23	entities, err := GetEntities(pgpKeys)
24	if err != nil {
25		return nil, nil, err
26	}
27	for i, entity := range entities {
28		ctBuf := bytes.NewBuffer(nil)
29		pt, err := openpgp.Encrypt(ctBuf, []*openpgp.Entity{entity}, nil, nil, nil)
30		if err != nil {
31			return nil, nil, fmt.Errorf("error setting up encryption for PGP message: %w", err)
32		}
33		_, err = pt.Write(input[i])
34		if err != nil {
35			return nil, nil, fmt.Errorf("error encrypting PGP message: %w", err)
36		}
37		pt.Close()
38		encryptedShares = append(encryptedShares, ctBuf.Bytes())
39	}
40
41	fingerprints, err := GetFingerprints(nil, entities)
42	if err != nil {
43		return nil, nil, err
44	}
45
46	return fingerprints, encryptedShares, nil
47}
48
49// GetFingerprints takes in a list of openpgp Entities and returns the
50// fingerprints. If entities is nil, it will instead parse both entities and
51// fingerprints from the pgpKeys string slice.
52func GetFingerprints(pgpKeys []string, entities []*openpgp.Entity) ([]string, error) {
53	if entities == nil {
54		var err error
55		entities, err = GetEntities(pgpKeys)
56
57		if err != nil {
58			return nil, err
59		}
60	}
61	ret := make([]string, 0, len(entities))
62	for _, entity := range entities {
63		ret = append(ret, fmt.Sprintf("%x", entity.PrimaryKey.Fingerprint))
64	}
65	return ret, nil
66}
67
68// GetEntities takes in a string array of base64-encoded PGP keys and returns
69// the openpgp Entities
70func GetEntities(pgpKeys []string) ([]*openpgp.Entity, error) {
71	ret := make([]*openpgp.Entity, 0, len(pgpKeys))
72	for _, keystring := range pgpKeys {
73		data, err := base64.StdEncoding.DecodeString(keystring)
74		if err != nil {
75			return nil, fmt.Errorf("error decoding given PGP key: %w", err)
76		}
77		entity, err := openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(data)))
78		if err != nil {
79			return nil, fmt.Errorf("error parsing given PGP key: %w", err)
80		}
81		ret = append(ret, entity)
82	}
83	return ret, nil
84}
85
86// DecryptBytes takes in base64-encoded encrypted bytes and the base64-encoded
87// private key and decrypts it. A bytes.Buffer is returned to allow the caller
88// to do useful thing with it (get it as a []byte, get it as a string, use it
89// as an io.Reader, etc), and also because this function doesn't know if what
90// comes out is binary data or a string, so let the caller decide.
91func DecryptBytes(encodedCrypt, privKey string) (*bytes.Buffer, error) {
92	privKeyBytes, err := base64.StdEncoding.DecodeString(privKey)
93	if err != nil {
94		return nil, fmt.Errorf("error decoding base64 private key: %w", err)
95	}
96
97	cryptBytes, err := base64.StdEncoding.DecodeString(encodedCrypt)
98	if err != nil {
99		return nil, fmt.Errorf("error decoding base64 crypted bytes: %w", err)
100	}
101
102	entity, err := openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(privKeyBytes)))
103	if err != nil {
104		return nil, fmt.Errorf("error parsing private key: %w", err)
105	}
106
107	entityList := &openpgp.EntityList{entity}
108	md, err := openpgp.ReadMessage(bytes.NewBuffer(cryptBytes), entityList, nil, nil)
109	if err != nil {
110		return nil, fmt.Errorf("error decrypting the messages: %w", err)
111	}
112
113	ptBuf := bytes.NewBuffer(nil)
114	ptBuf.ReadFrom(md.UnverifiedBody)
115
116	return ptBuf, nil
117}
118