HowTo:PGP

Online services are the foundation of distributed computing, but security is often overlooked at the beginnings of these projects. Here, I'll explain a really simple way to lock down access to your precious protocols at the transport layer. There are essentially no changes required to the communication protocol. In addition, the method dovetails with the GnuPG suite for message signature and encryption.

Get Started with GnuPG
Why?

GnuPG uses a chain of public-key certificates, similar to the x.509 certificates that we're all familiar with our web-browsers constantly complaining about. Each certificate has some private information (that the owner keeps) and some identifying public information that is distributed like a user-id, name and birthdate, or the like. The difference with those other numbers is that revealing the public key doesn't compromise your security, like a password social security number would. Instead, the public key is used to check that a communication could only have come from the person knowing the private key.

Things get complicated when more than two people get involved. How do I know a person's public key is trusted to do something? PGP does this by having one key sign another key. If I want my webserver to recognize a set of users, I can create a server private key and use it to sign the public keys of trusted users. Then I have a chain of signatures that validate the user's public key. When the user connects, I use that public key to make sure the user knows the corresponding private key.

Secure internet servers use x.509 certificates in a similar way to prove that you can trust them. Your browser has a list of trusted root certificates from places like Verisign and Thawte. When you connect to a random server using HTTPS, the server gives you a public key. Your browser then checks whether that public key has been signed by one of the trusted root certificates it knows about.

Although similar to x.509 server certificates, PGP doesn't have a centrally based set of trusted root certificate authorities. In fact, anyone can sign for anyone else in the PGP scheme. This makes it much more user-centric. I can, for example, sign source code packages I produce and people who have my public key can verify that they haven't been tampered with. I could produce a hierarchy of keys to sign things like code, purchase orders, or server access. I could use these to delegate those responsibilities. At the same time, I could get those keys signed by other people (like the manager of a coding project, an accountant, or a server admin).

Install
Use a package-manager to look for a gnupg or equivalent.

Create a Keyring
GnuPG does almost everything through options to the gpg command. Following along with,  gpg (GnuPG) 1.4.13; Copyright (C) 2012 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want: (1) RSA and RSA (default) (2) DSA and Elgamal (3) DSA (sign only) (4) RSA (sign only) Your selection? 1 RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) Requested keysize is 2048 bits Please specify how long the key should be valid. 0 = key does not expire  = key expires in n days w = key expires in n weeks m = key expires in n months y = key expires in n years Key is valid for? (0) Key does not expire at all Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID from the Real Name, Comment and Email Address in this form: "Heinrich Heine (Der Dichter) "

Real name: David M. Rogers Email address: predictivestatmechgmail.com Comment: You selected this USER-ID: "David M. Rogers "

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o You need a Passphrase to protect your secret key.

We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. . We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. . gpg: ~/.gnupg/trustdb.gpg: trustdb created gpg: key 262B259C marked as ultimately trusted public and secret key created and signed.

gpg: checking the trustdb gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model gpg: depth: 0 valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u pub  2048R/262B259C 2013-12-20 Key fingerprint = D1CE 9C6A 5099 6753 8FC4 81F5 5B64 9362 262B 259C uid                 David M. Rogers  sub  2048R/E494F149 2013-12-20

Next, you'll want to double-check that it worked and make sure gnupg's directory has no read or write permissions for anyone other than yourself.

For the client/server example, we'll also want to write the public key to a separate file.

Next, we'll generate a certificate belonging to my laptop. I'll choose to use this one for allowing users to connect to a new protocol I'm developing. It can't have a password according the GNUTLS manual.

gpg (GnuPG) 1.4.13; Copyright (C) 2012 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want: (1) RSA and RSA (default) (2) DSA and Elgamal (3) DSA (sign only) (4) RSA (sign only) Your selection? 4 RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) Requested keysize is 2048 bits Please specify how long the key should be valid. 0 = key does not expire  = key expires in n days w = key expires in n weeks m = key expires in n months y = key expires in n years Key is valid for? (0) 3y Key expires at Mon Dec 19 15:04:37 2016 EST Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID from the Real Name, Comment and Email Address in this form: "Heinrich Heine (Der Dichter) "

Real name: kubotan Email address: davidrogersusf.edu Comment: MBPR Laptop You selected this USER-ID: "kubotan (MBPR Laptop) "

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o You need a Passphrase to protect your secret key.

You don't want a passphrase - this is probably a *bad* idea! I will do it anyway. You can change your passphrase at any time, using this program with the option "--edit-key".

We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. . gpg: key 03357392 marked as ultimately trusted public and secret key created and signed.

gpg: checking the trustdb gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model gpg: depth: 0 valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u gpg: next trustdb check due at 2016-12-19 pub  2048R/03357392 2013-12-20 [expires: 2016-12-19] Key fingerprint = C417 3765 AB3A 139B 72C3 F093 D35C FD07 0335 7392 uid                 kubotan (MBPR Laptop) 

Note that this key cannot be used for encryption. You may want to use the command "--edit-key" to generate a subkey for this purpose.

Following the suggestion, I'll sign this one with my user key. gpg (GnuPG) 1.4.13; Copyright (C) 2012 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

Secret key is available.

pub 2048R/03357392  created: 2013-12-20  expires: 2016-12-19  usage: SC                       trust: ultimate      validity: ultimate [ultimate] (1). kubotan (MBPR Laptop) 

gpg> sign

pub 2048R/03357392  created: 2013-12-20  expires: 2016-12-19  usage: SC                       trust: ultimate      validity: ultimate Primary key fingerprint: C417 3765 AB3A 139B 72C3 F093 D35C FD07 0335 7392

kubotan (MBPR Laptop) <davidrogersusf.edu>

This key is due to expire on 2016-12-19. Are you sure that you want to sign this key with your key "David M. Rogers <predictivestatmechgmail.com>" (262B259C)

Really sign? (y/N) y

You need a passphrase to unlock the secret key for user: "David M. Rogers <predictivestatmechgmail.com>" 2048-bit RSA key, ID 262B259C, created 2013-12-20

gpg> quit Save changes? (y/N) y

My signature belongs to kubotan's key now, so GPG saves it there.

gpg: checking the trustdb gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model gpg: depth: 0 valid:   2  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 2u gpg: next trustdb check due at 2016-12-19 /Users/rogers/.gnupg/pubring.gpg

pub  2048R/262B259C 2013-12-20 uid                 David M. Rogers <predictivestatmechgmail.com> sig 3       262B259C 2013-12-20  David M. Rogers <predictivestatmechgmail.com> sub  2048R/E494F149 2013-12-20 sig         262B259C 2013-12-20  David M. Rogers <predictivestatmechgmail.com>

pub  2048R/03357392 2013-12-20 [expires: 2016-12-19] uid                 kubotan (MBPR Laptop) <davidrogersusf.edu> sig 3       03357392 2013-12-20  kubotan (MBPR Laptop) <davidrogersusf.edu> sig         262B259C 2013-12-20  David M. Rogers <predictivestatmechgmail.com>

I need both its public and private sections to use in the server code, so I'll extract them, and mark them self-read-only.

These keys are all you need to sign or encrypt messages as well. To see the usage for those, check the chapters in the GPG Mini-Howto.

Server
Next, let's get started with some client/server code. TLS stands for transport-layer-security. Normal communications (e.g. http/telnet/ftp) merrily go about sending messages (commands, files, output, etc.) over TCP. TLS-enabled communications (e.g. HTTPS/SSH/SFTP) start a connection by figuring out how to encrypt the session. They can also check signatures to authenticate the user and the server. After that, all communications are encrypted to prevent data leaking to the local packet sniffing red-team.

Although incorporating a 'STARTTLS' message into your protocol has been recommended if you need to talk before authenticating, the lazy way is to just authenticate and start TLS at connection. The GNU TLS library makes this especially easy by handling certificates, doing the initial TLS handshake, and encrypting/decrypting messages passed through gnutls_record_send / gnutls_record_recv.

Installation
The GNUTLS library is under heavy development, and I found that the examples in the manual would not run with the package manager's version 2.2. So, I installed nettle 2.7.1 (--enable-shared) and gnutls 3.2.7 from source. Apparently someone's been using strerror in gnutls, and someone else has been replacing strerror with repl_strerror to teach them a lesson - so we have to deal with the collateral damage.

Usage
The server code for OpenPGP authentication and client code for the very similar x.509 authentication are available.

But the best starting place for hacking together a client/server pair authenticating using OpenPGP are the generic implementations in gnutls-3.2.7/src/serv.c and gnutls-3.2.7/src/cli.c. Those commands are bundled with gnutls and are easy to test out with

You will get a segfault if you read an ascii key (.asc) in binary mode or vice-versa.

Server Code
I've added the following code to interface with libixp. It contains all the necessary bits to put the libraries together, including keyring initialization, session creation, and certificate validation. On startup, the client and server should both make calls to

ixp_set_credentials(char *certfile, char *keyfile)

which takes the file paths for the ascii-encoded secret key and the binary-encoded keyring. Just after accepting a socket connection, both client and server call

ixp_init_session(gnutls_certificate_credentials_t cred, unsigned int flags)

to create a session object. Note that the server has to have previously called ixp_init_dh and set flags to GNUTLS_SERVER. The client just has to set flags to GNUTLS_CLIENT.

Peer validation happens during ixp_init_session. The validation is performed by cert_verify_callback, which our code tells gnutls about way back when ixp_set_credentials was called. We have cert_verify_callback setup to test whether the peer (client of the server or server of the client) is in the keyring. If no keyring was loaded, we just check that the peer knows how to sign their own name. Note that this is just a membership test. In this function, we only have the peer's public key. The actual cryptographic validation happened before cert_verify_callback got involved, by checking that the peer possesses the corresponding private key. There's also a nicety function

char *get_peer_name(gnutls_session_t)

that either client/server can call to get the name listed on the peer's (server/client) certificate. Once the gnutls session is setup, it acts like the file descriptor, so I had to replace the fd with a struct holding the session object in ixp. You can see that the read and write calls are now mbtls_read and mbtls_write. The connection is closed cleanly with tls_hangup.

There it is, the client and server code in one neat step. Hopefully this makes adding transport layer security to your protocol that much easier. Please let me know if it does!