package main import ( "fmt" "os" //"bytes" "io" //_ "embed" "net/http" "path/filepath" "strings" //"encoding/json" //"crypto/rsa" "github.com/ProtonMail/go-crypto/openpgp" "github.com/ProtonMail/go-crypto/openpgp/packet" "github.com/alexflint/go-arg" ) const ( KeyServer string = "127.0.0.1:8080" ) type Arguments struct { CmdEncrypt *ArgEncrypt `arg:"subcommand:encrypt"` CmdDecrypt *ArgDecrypt `arg:"subcommand:decrypt"` } type ArgEncrypt struct { Input string `arg:"positional,required"` Recipient string `arg:"--recipient,required"` Output string `arg:"--output,required"` } type ArgDecrypt struct { Input string `arg:"positional,required"` Output string `arg:"positional,required"` } func run(args *Arguments) error { var err error switch { case args.CmdEncrypt != nil: err = encrypt(args.CmdEncrypt) case args.CmdDecrypt != nil: err = decrypt(args.CmdDecrypt) default: err = fmt.Errorf("unknown command") } return err } type KeyType string const ( PublicKey KeyType = "pubkey" PrivateKey KeyType = "privkey" ) func getKey(search string, keytype KeyType) (openpgp.EntityList, error) { resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s", KeyServer, keytype, search, )) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { errstr := &strings.Builder{} fmt.Fprintf(errstr, "Error getting public key: %s\n", resp.Status) content, err := io.ReadAll(resp.Body) if err != nil { fmt.Fprintf(errstr, "error reading error response: %s\n", err) return nil, fmt.Errorf(errstr.String()) } fmt.Fprintln(errstr, content) return nil, fmt.Errorf(errstr.String()) } return openpgp.ReadKeyRing(resp.Body) } func encrypt(args *ArgEncrypt) error { info, err := os.Stat(args.Input) if err != nil { return err } infile, err := os.Open(args.Input) if err != nil { return err } defer infile.Close() keyring, err := getKey(args.Recipient, PublicKey) hints := &openpgp.FileHints{ IsBinary: true, FileName: filepath.Base(args.Input), ModTime: info.ModTime(), } outfile, err := os.Create(args.Output) if err != nil { return err } defer outfile.Close() writer, err := openpgp.Encrypt(outfile, keyring, nil, hints, nil) if err != nil { return err } defer writer.Close() _, err = io.Copy(writer, infile) return err } func decrypt(args *ArgDecrypt) error { infile, err := os.Open(args.Input) if err != nil { return err } defer infile.Close() var keyring openpgp.EntityList for { p, err := packet.Read(infile) if err == io.EOF { break } else if err != nil { //fmt.Println("packet read error:", err) continue } keypacket, ok := p.(*packet.EncryptedKey) if !ok { continue } keys, err := getKey(fmt.Sprintf("%X", keypacket.KeyId), PrivateKey) if err != nil { fmt.Println(err) continue } keyring = append(keyring, keys...) } if len(keyring) == 0 { return fmt.Errorf("No decryption keys found") } _, err = infile.Seek(0, 0) if err != nil { return err } msg, err := openpgp.ReadMessage(infile, keyring, nil, nil) if err != nil { return err } outfile, err := os.Create(args.Output) if err != nil { return err } defer outfile.Close() _, err = io.Copy(outfile, msg.UnverifiedBody) return err } //func getKeys(filename string) ([]string, error) { // file, err := os.Open(filename) // if err != nil { // return nil, err // } // defer file.Close() // // keys := []string{} // for { // p, err := packet.Read(file) // if err != nil { // if err == io.EOF { // break // } // //fmt.Println("err:", err.Error()) // continue // } // // enckey, ok := p.(*packet.EncryptedKey) // if !ok { // continue // } // // keys = append(keys, fmt.Sprintf("%X", enckey.KeyId)) // fmt.Println("KeyId:", enckey.KeyId) // //fmt.Printf("%#v\n", p) // } // // return keys, nil //} // //func decrypt(input, output string, keys openpgp.EntityList) error { // infile, err := os.Open(input) // if err != nil { // return err // } // defer infile.Close() // // detail, err := openpgp.ReadMessage(infile, keys, nil, nil) // if err != nil { // return err // } // // if detail.SignatureError != nil { // return detail.SignatureError // } // // outfile, err := os.Create(output) // if err != nil { // return err // } // defer outfile.Close() // // _, err = io.Copy(outfile, detail.UnverifiedBody) // return err //} // //func encrypt(input, output string, keys openpgp.EntityList) error { // info, err := os.Stat(input) // if err != nil { // return err // } // // hints := &openpgp.FileHints{ // IsBinary: true, // FileName: input, // ModTime: info.ModTime(), // } // // infile, err := os.Open(input) // if err != nil { // return err // } // defer infile.Close() // // outraw, err := os.Create(output) // if err != nil { // return err // } // defer outraw.Close() // // writer, err := openpgp.Encrypt(outraw, keys, nil, hints, nil) // if err != nil { // return err // } // defer writer.Close() // // _, err = io.Copy(writer, infile) // if err != nil { // return err // } // // return nil //} // //func loadKey() (openpgp.EntityList, error) { // reader := bytes.NewReader(PrivateKey) // return openpgp.ReadArmoredKeyRing(reader) //} // //func loadPub() (openpgp.EntityList, error) { // reader := bytes.NewReader(PublicKey) // return openpgp.ReadArmoredKeyRing(reader) //} func main() { args := &Arguments{} arg.MustParse(args) if err := run(args); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }