/mtgnet-encryption

MTGNet client library extension for encrypted connections.

Primary LanguageCommon Lisp

mtgnet-encryption

mtgnet-encryption is an extenion to the cl-mtgnet client library for the MTGNet RPC protocol that provides encrypted connections.

Note: this extension currently depends on a version cl-mtgnet that is not yet available in quicklisp, and on the cl-sodium bindings for the Sodium library, which are still incomplete.

Usage

(cr:sodium-init)
(let ((framer ...)
      (transport ..)
      (secret (mtgnet.encryption:generate-secret))))
      (connection (make-encrypted-connection framer transport secret)))
  ;; Use the connection as usual.
  (mtgnet:connect connection)
  (mtgnet:invoke-rpc-method connection ...)
  ...
  ;; Secrets must be explicitly freed.
  (mtgnet.encryption:free-secret secret))

Once an encrypted-rpc-connection instance is created, it is a drop-in replacement for regular rpc-connection connection objects, except for the conditions it throws. General errors encrypting or decrypting data will be signalled, and unauthorized client connections will cause a client-not-authorized error to be signalled (see below).

(cr:sodium-init) must be called before using Sodium functions are called, or the process will be killed.

Protocol

The protocol provided by this library aims to:

  1. be cryptographically strong
  2. offer authentication as a guard against man-in-the-middle attacks
  3. offer perfect forward secrecy to mitigate the effects of a future key compromise.

To accomplish this, the protocol uses a static signing keypair (Ed25519) to identify each party (providing authentication), and generates an ephemeral ECDH keypair (Curve25519) for each session. Libsodium is used to provide the cryptographic primitives.

On connection, each party should:

  1. Generate an ephemeral ECDH keypair.
  2. Sign the public part of the ephemeral keypair with their static signing key.
  3. Send a frame to the remote end consisting of their public signing key followed by the signed ephemeral public key.

After sending the initial handshake data to the remote end, each party should

  1. Read the public signing key and signed ephemeral public key from the remote end.
  2. Verify the signature on the ephemeral public key. If the public signing key is not recognized, or the signature fails validation, the connection should be dropped.
  3. Use the ephemeral public key from the remote end and the local ephemeral secret key to encrypt all future frames in the session using libsodium’s crypto_box_easy API or equivalent algorithms. Encrypted frames consist of the nonce used for encryption followed by the encrypted data.

API

Package mtgnet.encryption

The mtgnet.encryption package provides the consumer-level API for this module, using the primitives provided by mtgnet.crypto. Under normal circumstances, this is the only package users should need to use.

encrypted-rpc-connection

The class of encrypted rpc connection objects returned by make-encrypted-connection.

make-encrypted-connection

(make-encrypted-connection framer transport secret-key &optional (authorized-keys '(t))) constructs a new encrypted connection. framer and transport are the MTGNet framer and transport objects for the connection. secret-key is the secret key that is used to drive the ECDH key exchange, and must be either a base64 string (which will be decoded with mtgnet.encryption:decode-secret-key), or a secret object of the sort returned by mtgnet.encryption:decode-secret-key or mtgnet.encryption:generate-secret.

authorized-keys is a list of public keys that are permitted to do a key exchange over this connection. If the list contains t (the default), any public key may be used in an exchange. This offers a limited form of authentication for connections.

free-secret

Secrets created by the library use memory-protected regions that are allocated manually and not subject to garbage collection. You must call free-secret on any secrets produced by generate-secret or decode-secret to deallocate them.

perform-handshake

Since server’s construct a connection out of an already-connected socket, they normally don’t call mtgnet:connect. perform-handshake is used to perform the connection handshake in that case (call this before you submit calls over the connection).

secret-size

+secret-size+ is the size in bytes of the private keys used in the protocol, like those generated by generate secret. Secrets passed to make-encrypted-connection must be this size.

publickey-size

+publickey-size+ is the size in byts of the public keys used in the connection, like those returned by compute-public-key.

generate-secret

Return a new secret object with a freshly-generated private key in it. You must call free-secret on the result when you’re done with it.

generate-encoded-secret

Return a new private key as a base-64 encoded string. Useful for humans and config files.

decode-secret-key

Return a new secret object for the private key encoded in a base-64 string, like those produced by generate-encoded-secret.

compute-public-key

Given a private key in a secret object, return the matching public key. Useful for reporting the public key (used for authentication) of the secret stored in a config file.

Package mtgnet.crypto

The mtgnet.crypto package provides the low-level cryptographic primitives used in the encryption protocol. Users should normally not need anything in here.

with-secret

(with-secret (ptr-var) body) is a macro that takes a secret (a foreign-pointer) and allows read-only access to it for the duration of body, restoring the no-access memory protection to it afterwards. All secret objects generated by this library have no-access memory protection applied to them, and can only be read in the context of a with-secret body or the process will be killed.

The library uses this macro where appropriate internally – don’t call library functions inside the body of with-secret or you may get memory-access errors.

ecdh-session-key

(ecdh-session-key secret public) takes a private key in a secret object and a public key, and precomputes a new ECDH session key. The key is a precomputed key produced by sodium’s crypto_box_beforenm function. This key will be shared between the local and remote ends, and can be used with crypto_box_easy_afternm and crypto_box_open_easy_afternm to encrypt data.

keypair-secret

Given a keypair, return the secret key.

keypair-public

Given a keypair, return the public key.

destroy-keypair!

Deallocate a keypair and set its parts to nil to avoid unintentional use.

generate-ecdh-secret

Generate and return a new ECDH secret object. Note that this not the sort of secret produced by mtgnet.encryption:generate-secret (see the protocol section).

compute-ecdh-public-key

Given an ECDH private key in a secret object, return the matching public key.

generate-ecdh-keypair

Generate a new ECDH keypair.

generate-signing-secret

Generate a new secret key used for signatures (see the protocol section).

compute-signing-public-key

Given a private signing key, return the matching public key.

generate-signing-keypair

Generate a new keypair to be used for signatures.

generate-encoded-signing-secret

Same as generate-signing-secret, except instead of returning a secret object, it returns a base64-encoded string. Useful for generating new keys for humans.

decode-secret

Takes a base64-encoded string of the secret data and returns a secret object for that data. Useful for reading secret from e.g. config files.

signed-bytes

Given a private signing key and a byte-vector, returned the bytes with an attached signature from that private key.

extract-signed-bytes

Given a public key and set of bytes with an attached signature, verify the signature with the public key and returned the signed bytes. If the signature is invalid, signals an invalid-signature-error.

invalid-signature-error

Condition signalled when the signature attached to some data fails validation for a public key.