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