Sure, we could do Gophers like HTTPS. Or... we could create a new *item type*.
How about *e* for *encrypted*? Like the *w* for *write* type, it depends on the client sending more information.
For example:
$ **echo "Alex/menu" | cat - ~/.gnupg/key.asc | nc alexschroeder.ch 70** -----BEGIN PGP MESSAGE----- hQQMAxHFg2RFKaRcAR//WbDO20XJPSTAyzTMGK26+krowPXYuWY0k0qfhedyMESK cx+w37SzFPkJJVVQO021GHLMORT3ABG41QPY8SwEhkVG9uWTm70+zh4GJA56OjBA huA97yRdlRuUQzSs3MsZyr3i5zRzecKUI99oqZLAMrx4sWiicD/ndUNIPHljUlZ6 tP0Kaw8SNMpbV6PEkJnNqNn0cEvcRwP+ZOq8wu0aSLLyJHbmGF1ceq7fsTfWCh7N 1kzTK8DJVFVKeHM7+X48eKu6GJXNH/vgpaiO33ivUhFTQ35uxzuU5KXZDvoHYEgM 8vDX5NNTV8gPeyALR27SVj2gl2RT8az7TRrC1gdv91ZnjRqdoIilikNnrRl5JDx9 1o5N7nE2vJwiZ4yL5kIdFuu/SmikRsj0Ec6OkE9QiQgENfbs4B00TLA6sWHAnfgY STmxBd8VWCndiE794oD+ieBW5vIWuTKRteniRRQh38r+JWSTJttD6cQh7vdarGfj NEwaIwpbCqAA1xhqAujJh5/VTGQW4fegshhRmwCszpyOy4dn0haSAe8t6zYn15Y2 Ot2Ppi73D2Jc2R9sLrb6e3Hvfj/dZliUp3l6DDA9Gv8jK4ACUQt6kJSx1vKmO4Oz O7+hKQGq+DCV99QCq9lFls6YJU6aMmelgz+COTqoUSZAe4by+/VQ+ZtaXtXfb6ye Q+FjTSHT21AInmy0w5rhaFi6Sl+b4r7eHzjPCLW7cEkX0FOv6V/Qx4hBkbix4/m4 RbgkwvWM0PEqfd6ayyNwU++hvpF76FVxMT1Kqz76bsJveO+/XWJy/2NMjlycElvg nu21qpgISB1ZgTkxqzovZ86APopiS8872B4QvWxBumYGlRDQhZaNbcWTlF8MUpqA WDx+rXqL3O5WCEk45049B0kkjrpTarSHtXUEIDdHIXhse6IiDdCnHpv+lfft+Sqq bMUX2QBI2E24u1y330uwnbkOMuLTDi4xZOJUpmrSyFTzhwZ77e2pq2fwSIxOs779 R9T4zljS2Vt8LBdIr9IfcABKkEB/Yg4MnXZyhxbXPHOougQ3CEoygPrPrTx9sUc7 r+Z5pQv3QUduzzmLNiAzhCRIHG9mqXquaYsOejMwhmg3UvqzPrAVkzBMwu5ZqSEh /5NOqCXOnYctbjuu3nSkeDvS9tX92/ijpDjztvl6fbYRLNcSinpYm+QJp+ubCOkU SzCFZ7JKNHsD4CxE5kwzokP8801zpYjltzBGl6qG1DHt3uXfgdnvkE5cicIyE327 jfzdcAMEq9DYuT8dw61pUcEXWGYrl72Jmj86LQXBC5m8dlDn9LitU+hcOG0P4Ln5 v6mYiXExu1UFReVILqrj4XDeVLw6IjD/ifPzt5whqtJYAQkdixhbSzwRbHmSyzTF MgLiUZgh87w9KtVgJ31Ig+BqzuAQHtb7RY/xLvZFPBftikxY8vi1eAwLQ6TVE+59 3RI2hjTQtb/21I2pNRGxx/ffIgnlB+VX/g== =xfw+ -----END PGP MESSAGE-----
So... we wouldn’t keep the requests a secret, but we’d keep the data sent back a secret, encrypted for the individual making the request. And the server itself can have a public key. The response is both encrypted and signed, and possibly includes the public key, and so we “know” the identity of the server.
As the client, we need the following:
1. we need access to `gpg`
2. we need to import the public signature of the server when we get our first reponse
3. we need to warn the user when the public key of the server changed between requests
Keeping our requests secret would require the server to not hang up, or it would require us to send the selector as part of the encrypted package. Doable, I guess? This needs more thought.
As I now have a Gopher server (for this wiki) and a Gopher client (for Emacs) to hack, I should make some experiments.
I guess here is how it would work:
We need to get the public key of the server, for example using this:
$ **echo Site_GPG_Key \ | nc alexschroeder.ch 70 \ | gpg --import**
Then we need a message containing our own public key and our request, encrypted for the server.
$ **(gpg --export --armor DF9446EB7B7846387CCC018BC78CA29BACECFEAE; \ echo Alex | gpg --encrypt --armor --recipient alexschroeder.ch; ) \ > message.txt**
The message now contains a `PGP PUBLIC KEY BLOCK` and a `PGP MESSAGE`.
This is what we send to the server, using a special selector to indicate That we have an encrypted payload.
$ **echo do/gpg | cat - message.txt | nc alexschroeder.ch 70**
On the server side, we need to import the key of the person making the request:
$ **gpg --import < message.txt** gpg: key ACECFEAE: public key "Alex Schroeder <kensanata@keybase.io>" imported gpg: Total number processed: 1 gpg: imported: 1 (RSA: 1)
We need to remember this key for later!
So how about extracting this from the message:
$ **gpg --import < message.txt 2>&1 \ | sed -n 's/.*key \([^:]*\).*/\1/p'** ACECFEAE $ **sed -n "/-----BEGIN PGP MESSAGE-----/,/-----END PGP MESSAGE-----/p" \ < message.txt \ | gpg --decrypt --passphrase-file passphrase.txt --output request.txt** Reading passphrase from file descriptor 3 You need a passphrase to unlock the secret key for user: "alexschroeder.ch" 2048-bit RSA key, ID B4E46A78, created 2018-01-12 (main key ID AC23889A) gpg: encrypted with 2048-bit RSA key, ID B4E46A78, created 2018-01-12 "alexschroeder.ch"
And now we have our request on the server side here:
$ **cat request.txt** Alex
And for the reply:
$ **gpg --encrypt --armor --sign --recipient ACECFEAE \ --passphrase-file passphrase.txt --trust-model always \ < haiku.txt** Reading passphrase from file descriptor 3 You need a passphrase to unlock the secret key for user: "alexschroeder.ch" 2048-bit RSA key, ID AC23889A, created 2018-01-12 -----BEGIN PGP MESSAGE----- Version: GnuPG v1 hQQMAxHFg2RFKaRcAR//exdg5fKlKWnbqO9yFnOUG+GZTRqVHWOesybP4AoDXzs1 jvisAaDpDhzbW8DXWXLQUFblckqtCFZyVMWO0VFNIV5cOBnIKn5/U6lJeeyXZ9hn Z1Rbcr60bz2yN6nf3G/c7A10Rlg+FumMSzS8GhpodeYTJILl0oiGDAbxE//VLAVT voc1rIKA6vIX2rwL8IOsiKt1TL/HusjPqB+hXhAjT03p++LgLM9dgY6nRYoCZ7O6 JWZqu9UI+vAIqKylZKg+1Q5CpqinufRDW9h3jQNdDtAzTggS8aJJGa+d9v98cAjH HqSP2aUmO3uh5YYalXETJ1x0YBZphIuf/426/4dj6qWlhIduKwTRsXr1akZEGAYF kN+uRio+Y7vWbv/xmrg+Cw2iripCEKcDhpQESt63Us+MHOepSgkICJN7sLwPwR1Q h7jHFg6wzj3m9kbAB79CWuzxdvWecCGFwsedJgv3e2OZ4EVTGTttNrs+lwyKRNrw ebBEH7qlK96D2f0+Z8Hp7GWZe6s1Zd14u17ulyokv9LkiEhk4uccoa0GsG9mmdZ2 WWTkF+IKXn/5nrK4FLUE0Rkl2Uf0u4GiKG08bIMuBLYQT5kRqmL9p8l0xwEnwaud 2o4eLOvFDXYeSN8TydQwcsEqrT+uInLOsLQxa6o6u/hHnvRmY/0wSaye1qigjo5R yn5dTsZN65NXEKQOcFO3Zwf72YG5YLfad49BU17mWEYwwEInVRGg4meTurxkkgES VW2dy7Fs8y9lJf1vFweWy4QoTRKcF6jByJWerUm6Tx2UoLEyv4YHChQOJbKm0wDa GND4M8WxGApX6/K69PCS4YJQSH8znT2R0v0H7/nrnftx/YukkqXGqcrqKs1VUaTf OZZvTNcB/E3+N+Q5m4fQX8EhH89f0MGLro6zM8z+DldJRrjBD0JuVbn6r55F11kC rIQuup9S1BjnfGRqmNSmKRoqEAjKhTKk/BeBazHq0wnVmjXYs5haAbk6mn4lmR7x BW1ffSzr+LuybgH4CSmFBdbPgGaOG/eO5x9pDzuW0Es6jbhUy4oNusoJqdJ+2gfq tzM/Sr6+/dgysWgjNmzlMALdtxQ+IgKv0mn4S+zOAE2RBa+76IM1F1CNWdxIP98U GnQjN4LhXI2jrc1KEeqo74uJQzVQhtlJRL3Ik9o5sZmyWFlpJRQ81/L1IE9puIc6 vFoTLy5mvu58eboIoIViFkPBwE6EThz7iPyaMt9ZvTvmIfce1bEeWGVYJlOIfc2f WnihRB11O+BpDrr+ReZXG9xNYNmlV8DLvEAdVsWJLiq23Gs2PUzZFFz1JR+YziEk Ti8OwHalvthRzMvxu+0J6L0/wNsbmuhhU7ogF2MJiNLA2QH9nRF44J8g5pAQqNSG 1uhEeLAYbzauSGU7CWwk38m8Y6m8jR5mjX/VT3LAoklgN6HRLzhVCxwcxO2BLM/1 BsowldsQvKlh9MMqY5tCMKdXqG4PgQTU+HwZuZ0qKqm4Xg42Nr60BS+xhjCOyp35 DvJUBI9JEjR0zqzmtgI8LKVG1UvNAAIClQBDhFgAW9wVrTuTQ3m7FEay9FeXBA1u xpPSnuoPlYSiS04cTXlO5JNAsnrmubv+Qw21U+g/FMH/GFgKg5WAxyFdzrAKeWxN KkfniJmy4gDj/AqlhdhWq3iSnR07gloQHBTySkakSs0UiiP+nqTTeOviBuCnKlRa kHy6AE8dKjHR1YrcSKS2g0IBx6mGsBwC/Z0bJETGpx+8XkyNN4C0hNvVh0NjEqyN QGNvg1+CnpdZueuYucOm3WMRHWpRh0UPh5rgHAoXjPMWbvmKHyZyE+zp03Y4FA1o oJi4JoveScC0majYSx1EaYCZGDhhEThl/fcM5As66DZSBWMLYa/hV1+xxZR1GGic 4pzW1UnJV7d6zl0= =pJzD -----END PGP MESSAGE-----
This message gets sent back over the wire and I can decrypt it.
$ **gpg --decrypt < response.txt** gpg: verschl"usselt mit 8192-Bit RSA Schl"ussel, ID 11C583644529A45C, erzeugt 2015-03-01 "Alex Schroeder <kensanata@keybase.io>" old pond frog leaps in water's sound gpg: Signatur vom Fri Jan 12 15:54:53 2018 CET gpg: mittels RSA-Schl"ussel 9F8EE9F5AC23889A gpg: Korrekte Signatur von "alexschroeder.ch" [unbekannt] gpg: WARNUNG: Ein Schl"ussel ohne gesichertes Vertrauen wird benutzt!
I guess we want to delete the key on the server after sending the reply, right? The sad part is that ideally, the client would generate a new key every now and then, and these would be throwaway keys, of course. We don’t actually want to help prove which requests we sent to the server.
It might work.
It might be too much work.
#Gopher #Cryptography
(Please contact me if you want to remove your comment.)
⁂
Maybe we should use TLS after all.
First, I generated a server key and certificate using the following:
$ openssl req -new -x509 -days 365 -nodes -config gopher-server.cnf \ -out gopher-server.pem -keyout gopher-server.pem
This saves both the private key and the certificate in the PEM file. The CNF file just holds a few defaults:
RANDFILE = ./openssl.rnd [ req ] default_bits = 2048 encrypt_key = yes distinguished_name = req_dn x509_extensions = cert_type utf8 = yes [ req_dn ] countryName = Country Name (2 letter code) countryName_default = CH stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = Zürich localityName = Locality Name (eg, city) localityName_default = Zürich organizationName = Organization Name (eg, company) organizationName_default = Alex Schroeder organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = Head Desk commonName = Common Name (FQDN of your server) commonName_default = localhost emailAddress = Email Address emailAddress_default = alex@gnu.org [ cert_type ] nsCertType = server
Be sure to change `localhost` to your real server’s domain name if you do the same!
I use the PEM file to start the Gopher server using Net::Server::Proto::SSL (this depends on IO::Socket::SSL):
OddMuse->run( proto => 'ssl', SSL_cert_file => 'stuff/gopher-server.pem', SSL_key_file => 'stuff/gopher-server.pem', );
When I start the server:
$ perl stuff/gopher-server.pl Running ./wiki.pl 2018/01/19-10:39:49 OddMuse (type Net::Server::Fork) starting! pid(4418) Resolved [*]:20203 to [0.0.0.0]:20203, IPv4 Binding to SSL port 20203 on host 0.0.0.0 with IPv4
This looks good.
Now to test it using a command line client. First, no TLS:
$ echo Alex | nc localhost 20203
This fails and that’s good. Now try without certificate validation:
$ echo Alex | gnutls-cli --no-ca-verification localhost:20203 |<1>| There was a non-CA certificate in the trusted list: O=Entrust.net,OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.),OU=(c) 1999 Entrust.net Limited,CN=Entrust.net Certification Authority (2048). Processed 172 CA certificate(s). Resolving 'localhost:20203'... Connecting to '127.0.0.1:20203'... - Certificate type: X.509 - Got a certificate list of 1 certificates. - Certificate[0] info: - subject `EMAIL=alex@gnu.org,CN=localhost,OU=Head Desk,O=Alex Schroeder,L=Zürich,ST=Zürich,C=CH', issuer `EMAIL=alex@gnu.org,CN=localhost,OU=Head Desk,O=Alex Schroeder,L=Zürich,ST=Zürich,C=CH', serial 0x00e44cda1d0cfffee5, RSA key 2048 bits, signed using RSA-SHA256, activated `2018-01-19 10:10:02 UTC', expires `2019-01-19 10:10:02 UTC', pin-sha256="n29iQd9rKSopyBka++t4zPflTD0EnPB67ix90SPZuNA=" Public Key ID: sha1:89599664e456cdbcfb5041e8586f2305f3f8856a sha256:9f6f6241df6b292a29c8191afbeb78ccf7e54c3d049cf07aee2c7dd123d9b8d0 Public Key PIN: pin-sha256:n29iQd9rKSopyBka++t4zPflTD0EnPB67ix90SPZuNA= Public key's random art: +--[ RSA 2048]----+ | .+ .+o+. | | + o *+o. | | * +.+o..| | * .. +o=. | | o S E=.. | | .o | | o | | . | | | +-----------------+ - Description: (TLS1.2)-(ECDHE-RSA-SECP256R1)-(AES-128-GCM) - Session ID: B6:26:85:D2:49:20:EC:9F:50:D2:A7:97:AA:0F:18:A9:1B:F3:11:AB:CB:3A:9B:79:0F:61:9E:4A:F4:A9:2B:C7 - Ephemeral EC Diffie-Hellman parameters - Using curve: SECP256R1 - Curve size: 256 bits - Version: TLS1.2 - Key Exchange: ECDHE-RSA - Server Signature: RSA-SHA512 - Cipher: AES-128-GCM - MAC: AEAD - Compression: NULL - Options: safe renegotiation, - Handshake was completed - Simple Client Mode: My best friend is [[Berta]]. Tags: [[tag:Friends]]
OK, at the end of the output, there is the text of our page!
Now let’s see whether we can get get rid of `--no-ca-validation`. First, we save the certificate to a local file and then we use it in our request:
$ gnutls-cli --save-cert=server.pem localhost:20203 $ echo Alex | gnutls-cli --x509cafile=server.pem localhost:20203
This works!
So, in theory at least, we have all the ingredients for `gophers://alexschroeder.ch` – except there is no standard port to use for gopher over TLS. Oh well. I might as well use 7443.
OK, done. Let’s test it!
$ gnutls-cli --save-cert=server.pem alexschroeder.ch:7443 $ echo Alex | gnutls-cli --x509cafile=server.pem alexschroeder.ch:7443
This works as well!
Here’s the TOFU variant, *Trust On First Use*. “This option will, in addition to certificate authentication, perform authentication based on previously seen public keys, a model similar to SSH authentication.”
First, send no request at all:
$ gnutls-cli --tofu alexschroeder.ch:7443
Answer `y` to the question of whether you want trust it. You’re then left hanging as the server waits for your selector. Type “Alex” + Return and you should get an answer. If you don’t, you will get a timeout after 15 seconds.
This stores the server in `~/.gnutls/known_hosts` and from then on, no extra options are required:
$ echo Alex | gnutls-cli alexschroeder.ch:7443
All right, server is done!
– Alex Schroeder 2018-01-19 11:00 MEZ
---
And if you want a real client to go along with it, try my SSL branch of gopher.el which does *only* SSL, so I guess it will only work for `alexschroeder:7443`.
– Alex 2018-01-19 13:07 UTC
---
I added this to my SSL branch of VF-1 and this branch was later merged into Solderpunk’s VF-1.
Here’s how I start it:
$ vf1 --tls alexschroeder.ch:7443
I love it.
– Alex 2018-01-19 13:17 UTC
---
And here’s how to run a server with encryption, if you have the certificate with all the other certificates in its chain in one file and the private key in another file. This is how I just tested it to make sure the Gopher server uses the same certificate as my website. The certificates from my website are from Let's Encrypt using an older version of dehydrated called `letsencrypt.sh`.
/home/alex/perl5/perlbrew/perls/perl-5.24.0/bin/perl \ /home/alex/farm/gopher-server2.pl \ --setsid --user=alex --group=alex \ --host=alexschroeder.ch --port=7443 --log_level=3 \ --log_file=/home/alex/farm/gopher-server2-ssl.log \ --pid_file=/home/alex/farm/gopher-server2-ssl.pid \ --wiki=/home/alex/farm/wiki.pl \ --wiki_dir=/home/alex/alexschroeder \ --wiki_pages=SiteMap \ --wiki_pages=About \ --wiki_pages=Gopher \ --wiki_cert_file=/etc/letsencrypt.sh/certs/alexschroeder.ch/fullchain.pem \ --wiki_key_file=/etc/letsencrypt.sh/certs/alexschroeder.ch/privkey.pem
– Alex 2018-01-21 18:42 UTC
---
hello!
please don’t do it this way
use of .onion addresses gets you all the nice features like authentication, confidentiality etc. without any of the complexity that is https.
let gopher stay simple
– anonymous 2018-04-11 19:40 UTC
---
From a developer perspective, implementing TLS was extremely easy. Plop standard library into client and server, done. Adding TLS is not adding HTTPS.
– Alex Schroeder 2018-04-11 22:22 UTC
---
Tor uses TLS, therefore only adds complexity on top of TLS.
– Anonymous 2018-05-11 12:56 UTC
---
Here is an alternative by @solene: How to add TLS to your gopher server. Uses `sslh` and `relayd` to sniff protocol.
How to add TLS to your gopher server
– Alex Schroeder 2019-03-08 10:29 UTC