Scripting gpg with Go
Written by Jiří Keresteš on 2020-01-23
pgpSometimes, you just need to use gpg
from a script. There's a couple of
ways to achieve your goals, and a lot of dead ends. Read on to learn more!
If you ever looked for a gpg library, you probably know that there are many different projects out there. Unfortunately, most of these do not support all the features you need. Did you say: "card support"? If yes, you are going to have a particularly bad time, because I've yet to see a library other than GPGME (GnuPG Made Easy) with card support. Why don't we use GPGME then? Well, it's a lot of things, but I certainly wouldn't call it "easy". Documentation is often rather cryptic, and using it in a cross-platform build process is something I'd rather avoid for the rest of my life.
Ok, so let's just execute gpg
command with right parameters, right?
Yes, but you have to keep in mind a few caveats. You can generate your usual on-disk
gpg keys using something like this [1]:
$ cat >foo << EOF
%echo Generating a basic OpenPGP key
Key-Type: DSA
Key-Length: 1024
Subkey-Type: ELG-E
Subkey-Length: 1024
Name-Real: Joe Tester
Name-Comment: with stupid passphrase
Name-Email: joe@foo.bar
Expire-Date: 0
Passphrase: abc
# Do a commit here, so that we can later print "done" :-)
%commit
%echo done
EOF
$ gpg --batch --generate-key foo
[...]
$ gpg --list-secret-keys
/tmp/tmp.0NQxB74PEf/pubring.kbx
-------------------------------
sec dsa1024 2016-12-16 [SCA]
768E895903FC1C44045C8CB95EEBDB71E9E849D0
uid [ultimate] Joe Tester (with stupid passphrase) < joe@foo.bar >
ssb elg1024 2016-12-16 [E]
However, there's no --batch
parameter for gpg --card-edit
!
GnuPG has notoriously bad user interface and this is an example. GnuPG version 2.3
should include a new tool gpg-card
[2], which aims to provide a better
interface, but as of January 2020 it still hasn't been released.
You can use some Expect-like[3] library with gpg --card-edit
, but it
usually depends on PTY interfaces, and PTY interfaces are difficult (i.e. often not
supported) on Windows. Or you can dive deep into GnuPG documentation and discover
something called "Esoteric options"[4] (yeah).
Most portable way to script card operations with GnuPG is to to launch gpg with
--command-fd 0
and --status-fd 2
parameters. This makes
gpg
receive commands from stdin and print status output to stdout. I've
promised some Go, so here, have a PoC!
package main
import (
"bytes"
"fmt"
"os/exec"
"log"
)
func main() {
var out bytes.Buffer
proc := exec.Command("gpg", "--card-edit", "--command-fd=0", "--status-fd=2")
name := "Fred Tangerine"
email := "fred.tangerine@trustica.cz"
comment := "scripted key"
expires := "0"
proc.Stdout = &out
proc.Stdin = bytes.NewBufferString("admin\ngenerate\ny\n" + expires + "\n" + name + "\n" + email + "\n" + comment + "\n")
if err := proc.Start(); err != nil {
log.Fatal(err)
}
proc.Wait()
// debugging
fmt.Print(out.String())
}
Of course, you should read status output messages and react to errors and whatnots. But this is just a PoC.
Thanks for reading this rather technical post. If you "just wanted to use Assuan", stay tuned. There'll be another post about Assuan struggles soon. See you next Thursday!
References
- https://www.gnupg.org/documentation/manuals/gnupg-devel/Unattended-GPG-key-generation.html
- https://lists.gnupg.org/pipermail/gnupg-devel/2019-September/034447.html
- Wikipedia contributors. (2019, December 9). Expect. In Wikipedia, The Free Encyclopedia. Retrieved 13:05, January 23, 2020, from https://en.wikipedia.org/w/index.php?title=Expect&oldid=929914083
- https://www.gnupg.org/documentation/manuals/gnupg/GPG-Esoteric-Options.html