github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/vault/helper/pgpkeys/flag.go (about) 1 package pgpkeys 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "errors" 7 "fmt" 8 "os" 9 "strings" 10 11 "github.com/hashicorp/errwrap" 12 "github.com/keybase/go-crypto/openpgp" 13 ) 14 15 // PGPPubKeyFiles implements the flag.Value interface and allows parsing and 16 // reading a list of PGP public key files. 17 type PubKeyFilesFlag []string 18 19 func (p *PubKeyFilesFlag) String() string { 20 return fmt.Sprint(*p) 21 } 22 23 func (p *PubKeyFilesFlag) Set(val string) error { 24 if len(*p) > 0 { 25 return errors.New("can only be specified once") 26 } 27 28 keys, err := ParsePGPKeys(strings.Split(val, ",")) 29 if err != nil { 30 return err 31 } 32 33 *p = PubKeyFilesFlag(keys) 34 return nil 35 } 36 37 // ParsePGPKeys takes a list of PGP keys and parses them either using keybase 38 // or reading them from disk and returns the "expanded" list of pgp keys in 39 // the same order. 40 func ParsePGPKeys(keyfiles []string) ([]string, error) { 41 keys := make([]string, len(keyfiles)) 42 43 keybaseMap, err := FetchKeybasePubkeys(keyfiles) 44 if err != nil { 45 return nil, err 46 } 47 48 for i, keyfile := range keyfiles { 49 keyfile = strings.TrimSpace(keyfile) 50 51 if strings.HasPrefix(keyfile, kbPrefix) { 52 key, ok := keybaseMap[keyfile] 53 if !ok || key == "" { 54 return nil, fmt.Errorf("keybase user %q not found", strings.TrimPrefix(keyfile, kbPrefix)) 55 } 56 keys[i] = key 57 continue 58 } 59 60 pgpStr, err := ReadPGPFile(keyfile) 61 if err != nil { 62 return nil, err 63 } 64 keys[i] = pgpStr 65 } 66 67 return keys, nil 68 } 69 70 // ReadPGPFile reads the given PGP file from disk. 71 func ReadPGPFile(path string) (string, error) { 72 if path[0] == '@' { 73 path = path[1:] 74 } 75 f, err := os.Open(path) 76 if err != nil { 77 return "", err 78 } 79 defer f.Close() 80 buf := bytes.NewBuffer(nil) 81 _, err = buf.ReadFrom(f) 82 if err != nil { 83 return "", err 84 } 85 86 // First parse as an armored keyring file, if that doesn't work, treat it as a straight binary/b64 string 87 keyReader := bytes.NewReader(buf.Bytes()) 88 entityList, err := openpgp.ReadArmoredKeyRing(keyReader) 89 if err == nil { 90 if len(entityList) != 1 { 91 return "", fmt.Errorf("more than one key found in file %q", path) 92 } 93 if entityList[0] == nil { 94 return "", fmt.Errorf("primary key was nil for file %q", path) 95 } 96 97 serializedEntity := bytes.NewBuffer(nil) 98 err = entityList[0].Serialize(serializedEntity) 99 if err != nil { 100 return "", errwrap.Wrapf(fmt.Sprintf("error serializing entity for file %q: {{err}}", path), err) 101 } 102 103 return base64.StdEncoding.EncodeToString(serializedEntity.Bytes()), nil 104 } 105 106 _, err = base64.StdEncoding.DecodeString(buf.String()) 107 if err == nil { 108 return buf.String(), nil 109 } 110 return base64.StdEncoding.EncodeToString(buf.Bytes()), nil 111 112 }