From 4c2df57ff8ed2fc7ace7672bcaec533f4ada671d Mon Sep 17 00:00:00 2001 From: Zorchenhimer Date: Sun, 4 Aug 2024 17:34:41 -0400 Subject: [PATCH] Initial commit --- ciphertext/.gitignore | 1 + cleartext/.gitignore | 2 + cleartext/dummy-text.txt | 46 ++++ client.go | 294 ++++++++++++++++++++++++ go.mod | 15 ++ go.sum | 61 +++++ keys/aliases.sh | 2 + keys/customer-pub.gpg | Bin 0 -> 6857 bytes keys/generate-keys.go | 123 ++++++++++ keys/gpg-generated/cust-a.pub | 41 ++++ keys/gpg-generated/cust-b.pub | 51 ++++ keys/gpg-generated/internal-private.asc | 105 +++++++++ keys/gpg-generated/internal-public.asc | 52 +++++ keys/internal.gpg | Bin 0 -> 6857 bytes server.go | 245 ++++++++++++++++++++ 15 files changed, 1038 insertions(+) create mode 100644 ciphertext/.gitignore create mode 100644 cleartext/.gitignore create mode 100644 cleartext/dummy-text.txt create mode 100644 client.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 keys/aliases.sh create mode 100644 keys/customer-pub.gpg create mode 100644 keys/generate-keys.go create mode 100644 keys/gpg-generated/cust-a.pub create mode 100644 keys/gpg-generated/cust-b.pub create mode 100644 keys/gpg-generated/internal-private.asc create mode 100644 keys/gpg-generated/internal-public.asc create mode 100644 keys/internal.gpg create mode 100644 server.go diff --git a/ciphertext/.gitignore b/ciphertext/.gitignore new file mode 100644 index 0000000..8a18982 --- /dev/null +++ b/ciphertext/.gitignore @@ -0,0 +1 @@ +*.pgp diff --git a/cleartext/.gitignore b/cleartext/.gitignore new file mode 100644 index 0000000..4e083f8 --- /dev/null +++ b/cleartext/.gitignore @@ -0,0 +1,2 @@ +*.txt +!dummy-text.txt diff --git a/cleartext/dummy-text.txt b/cleartext/dummy-text.txt new file mode 100644 index 0000000..0ebbdfe --- /dev/null +++ b/cleartext/dummy-text.txt @@ -0,0 +1,46 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi consequat +egestas mi sed rhoncus. Duis et nulla nec neque mattis bibendum. Ut volutpat +erat vitae velit sodales suscipit. Nunc malesuada dolor a dolor tempor feugiat. +Donec ut pretium elit, nec malesuada turpis. Aliquam gravida, augue sit amet +pretium ullamcorper, erat ex euismod augue, eleifend consequat massa odio vel +justo. Donec non lacinia sem. Nullam eu est pulvinar, gravida urna eu, +fermentum felis. Fusce sodales pharetra diam, a vehicula elit varius nec. +Suspendisse eget augue eget nunc placerat pulvinar auctor sed enim. + +Nulla malesuada scelerisque semper. Proin et mi a odio ornare sodales in id +sapien. Integer eget quam imperdiet, blandit mi sed, mattis est. Nam nec libero +dolor. Aenean varius magna vitae risus imperdiet, iaculis volutpat erat +faucibus. Phasellus aliquam luctus nisi, vitae ullamcorper elit ultricies nec. +Etiam eu felis non dui fermentum iaculis. Vestibulum volutpat, justo id pretium +pharetra, lorem nisi luctus metus, sed hendrerit nulla sem eget sapien. +Suspendisse ipsum purus, fermentum ut felis et, ultricies vulputate lectus. Sed +eget molestie metus. Quisque vel sem eget felis faucibus viverra. Interdum et +malesuada fames ac ante ipsum primis in faucibus. Morbi varius ullamcorper leo +sed volutpat. Cras varius ipsum mauris, a efficitur neque sodales at. Nunc eget +mauris dui. + +Aenean euismod lectus sit amet justo convallis, sit amet molestie nisi luctus. +Maecenas arcu nulla, rhoncus vel odio vitae, iaculis facilisis nibh. Ut +interdum ex a libero tristique, id eleifend nunc bibendum. Pellentesque +hendrerit nisl enim, vehicula tincidunt dui faucibus vitae. Nulla a facilisis +justo. Quisque vehicula diam at nisi facilisis efficitur. Pellentesque rhoncus +ipsum id ligula aliquet, rhoncus vehicula neque malesuada. Ut felis felis, +pretium non accumsan a, vulputate feugiat dolor. Nullam gravida risus sem, +vitae rhoncus mauris facilisis ac. Nullam iaculis non quam nec dignissim. Nam +sagittis fringilla pharetra. + +Quisque in nunc felis. Sed ut risus convallis nibh sodales luctus. Sed lobortis +sem eros, sit amet lacinia ex sagittis vitae. Vestibulum at dolor pharetra, +aliquam lacus ut, consequat nulla. Nulla nulla metus, scelerisque et porttitor +id, blandit elementum purus. Mauris sagittis sit amet lacus vitae luctus. Etiam +nisl urna, ornare a tincidunt eu, pretium vitae mi. Nam rutrum blandit mollis. +Donec eu tortor sollicitudin, fermentum lorem eu, euismod odio. In hac +habitasse platea dictumst. + +Aliquam pulvinar arcu non ex aliquet, non facilisis nibh lacinia. Praesent id +dictum purus. Morbi nec velit posuere, aliquam arcu sit amet, gravida urna. +Etiam blandit ligula et nibh suscipit ultricies. Quisque lorem mauris, interdum +ut nisl id, tincidunt dignissim leo. Suspendisse potenti. Phasellus et urna +quis ipsum gravida sagittis. Sed id consectetur odio. Pellentesque elit eros, +dignissim sit amet sodales sit amet, mollis nec tellus. Cras consectetur nec +ante porttitor condimentum. diff --git a/client.go b/client.go new file mode 100644 index 0000000..649e9c0 --- /dev/null +++ b/client.go @@ -0,0 +1,294 @@ +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) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1a20123 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module main + +go 1.22.4 + +require ( + github.com/ProtonMail/go-crypto v1.0.0 + github.com/alexflint/go-arg v1.5.1 +) + +require ( + github.com/alexflint/go-scalar v1.2.0 // indirect + github.com/cloudflare/circl v1.3.3 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/sys v0.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..69db3cd --- /dev/null +++ b/go.sum @@ -0,0 +1,61 @@ +github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= +github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/alexflint/go-arg v1.5.1 h1:nBuWUCpuRy0snAG+uIJ6N0UvYxpxA0/ghA/AaHxlT8Y= +github.com/alexflint/go-arg v1.5.1/go.mod h1:A7vTJzvjoaSTypg4biM5uYNTkJ27SkNTArtYXnlqVO8= +github.com/alexflint/go-scalar v1.2.0 h1:WR7JPKkeNpnYIOfHRa7ivM21aWAdHD0gEWHCx+WQBRw= +github.com/alexflint/go-scalar v1.2.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/keys/aliases.sh b/keys/aliases.sh new file mode 100644 index 0000000..375c96e --- /dev/null +++ b/keys/aliases.sh @@ -0,0 +1,2 @@ +alias gpg-customer="gpg --no-default-keyring --keyring=/home/nick/code/pgp/keys/customer-pub.gpg" +alias gpg-internal="gpg --no-default-keyring --keyring=/home/nick/code/pgp/keys/internal.gpg" diff --git a/keys/customer-pub.gpg b/keys/customer-pub.gpg new file mode 100644 index 0000000000000000000000000000000000000000..2ee1f9d1b3cb8f29756eff938679c483a7dfef21 GIT binary patch literal 6857 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 VNx^49X08g79)&29XsA){e*u(qAwU2C literal 0 HcmV?d00001 diff --git a/server.go b/server.go new file mode 100644 index 0000000..5876370 --- /dev/null +++ b/server.go @@ -0,0 +1,245 @@ +package main + +import ( + "fmt" + "os" + "net/http" + "syscall" + "os/signal" + "time" + "context" + //"encoding/json" + "strings" + + "github.com/ProtonMail/go-crypto/openpgp" + //"github.com/ProtonMail/go-crypto/openpgp/packet" +) + +type Server struct { + PublicKeys openpgp.EntityList + PrivateKeys openpgp.EntityList + + //PubkeyGroups []*openpgp.Entity +} + +func NewServer(publicKeys, privateKeys []string) (*Server, error) { + pub, err := loadKeysFromFiles(publicKeys) + if err != nil { + return nil, err + } + + priv, err := loadKeysFromFiles(privateKeys) + if err != nil { + return nil, err + } + + return &Server{PublicKeys: pub, PrivateKeys: priv}, nil +} + +func loadKeysFromFiles(filenames []string) (openpgp.EntityList, error) { + var keyring openpgp.EntityList + + for _, f := range filenames { + file, err := os.Open(f) + if err != nil { + return nil, err + } + + keys, err := openpgp.ReadArmoredKeyRing(file) + if err != nil { + file.Close() + return nil, err + } + file.Close() + + keyring = append(keyring, keys...) + } + + return keyring, nil +} + +func (s *Server) handler_publickey(w http.ResponseWriter, r *http.Request) { + path := strings.Split(strings.Trim(r.URL.Path, "/"), "/") + if len(path) < 2 { + fmt.Fprintln(w, "missing path stuff") + return + } + fmt.Println(r.URL.Path) + + search := strings.ToLower(path[1]) + fmt.Println("searching for", search) + + key := s.findPubKey(search) + if key == nil { + http.Error(w, "Key not found", 500) + return + } + + err := key.Serialize(w) + if err != nil { + fmt.Printf("Serialization error: %s\n", err) + } +} + +func (s *Server) findPubKey(search string) *openpgp.Entity { + for _, k := range s.PublicKeys { + for _, id := range k.Identities { + if strings.ToLower(id.UserId.Name) == search { + return k + } + + if strings.ToLower(id.UserId.Email) == search { + return k + } + } + } + + return nil +} + +func (s *Server) handler_privatekey(w http.ResponseWriter, r *http.Request) { + path := strings.Split(strings.Trim(r.URL.Path, "/"), "/") + if len(path) < 2 { + fmt.Fprintln(w, "missing path stuff") + return + } + fmt.Println(r.URL.Path) + + search := strings.ToLower(path[1]) + fmt.Println("searching for", search) + + key := s.findPrivKey(search) + if key == nil { + http.Error(w, "Key not found", 500) + return + } + + err := key.SerializePrivate(w, nil) + if err != nil { + fmt.Printf("Serialization error: %s\n", err) + } +} + +func (s *Server) findPrivKey(search string) *openpgp.Entity { + for _, k := range s.PrivateKeys { + for _, id := range k.Identities { + if strings.ToLower(id.UserId.Name) == search { + return k + } + + if strings.ToLower(id.UserId.Email) == search { + return k + } + } + + for _, sub := range k.Subkeys { + if fmt.Sprintf("%x", sub.PrivateKey.KeyId) == search { + return k + } + } + } + + return nil +} + +func (s *Server) handler_debug(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Public Keys:") + for _, key := range s.PublicKeys { + //fmt.Fprintf(w, "%#v\n", key.PrimaryIdentity()) + fmt.Fprintf(w,"%s\n\t%X\n\t%X\n", + key.PrimaryIdentity().Name, + key.PrimaryKey.Fingerprint, + key.PrimaryKey.KeyId, + ) + + //j, err := json.Marshal(key) + //if err != nil { + // fmt.Fprintf(w, "ERROR: %s", err) + // return + //} + + //fmt.Fprintf(w,"\t\t%s\n", j) + } + + fmt.Fprintln(w, "\nPrivate Keys:") + for _, key := range s.PrivateKeys { + //fmt.Fprintf(w, "%#v\n", key.PrimaryIdentity()) + fmt.Fprintf(w,"%s\n\t%X\n\t%X\n", + key.PrimaryIdentity().Name, + key.PrimaryKey.Fingerprint, + key.PrivateKey.KeyId, + ) + + for _, sub := range key.Subkeys { + fmt.Fprintf(w, "\t>%X\n\t>%X\n", + sub.PublicKey.KeyId, + sub.PrivateKey.KeyId, + ) + } + } +} + +func handler_default(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "henlo") +} + +func run() error { + sv, err := NewServer( + []string{ + "keys/gpg-generated/cust-a.pub", + "keys/gpg-generated/cust-b.pub", + "keys/gpg-generated/internal-public.asc", + }, + []string{ + "keys/gpg-generated/internal-private.asc", + }, + ) + if err != nil { + return err + } + + mux := http.NewServeMux() + mux.HandleFunc("/pubkey/", sv.handler_publickey) + mux.HandleFunc("/privkey/", sv.handler_privatekey) + mux.HandleFunc("/debug/", sv.handler_debug) + mux.HandleFunc("/", handler_default) + + server := &http.Server{ + Addr: ":8080", + Handler: mux, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + MaxHeaderBytes: 1 << 20, + } + + done := make(chan os.Signal, 1) + signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + go func() { + fmt.Println("listening on", server.Addr) + if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + fmt.Println("Listen: ", err) + } + }() + + <-done + fmt.Println("stopping") + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := server.Shutdown(ctx); err != nil { + fmt.Println("Clean shutdown failed: ", err) + } + fmt.Println("goodbye") + + return nil +} + +func main() { + err := run() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +}