ftz.Lyberta.net

Modern C++ goodness

ftz MINT

OSI layer 6 protocol for encrypting network traffic

MINT (MINT Is Not TLS) is a network protocol that is inspired by TLS but is designed for peer-to-peer communication without central authority. The protocol is completely agnostic to the lower layers and can be used anywhere where two or more parties can communicate with messages of arbitrary size. It uses RSA keys with length of at least 2048 bits, Ephemeral Diffie-Hellman or Elliptic Curve Diffie-Hellman key exchange, AES in GCM mode for bulk encryption and SHA256 for hashing. More algorithms can be easily added as they are negotiated during the handshake.

The handshake goes as follows:

  • Negotiation of algorithms. Peers agree on best encryption algorithms they both support.
  • Exchange of ephemeral keys. This provides Perfect Forward Secrecy. All following traffic will be encrypted with agreed key.
  • Exchange of public keys and proving ownership of private keys by signing random challenge.
  • Exchange of signatures of all previous handshake traffic. This proves that there was no active MITM.

Key features

  • Cross platform - written in ISO C++17 (+ #pragma once).
  • Network agnostic - works on top of any reliable protocol.
  • No legacy algorithms supported - uses only strong contemporary algorithms.
  • Mandatory Perfect Forward Secrecy - a compromise of keys will not allow attackers to decrypt previous traffic.
  • Free software - released under the terms of GNU GPLv3 or any later version.

Dependencies

Compiler support

  • G++ 7 or newer with libstdc++

Setting up

1. Install all needed packages

Debian/Ubuntu/Mint and derivatives

# apt-get install build-essential cmake git libcrypto++-dev

2. Install other ftz libraries listed in dependencies section.

Please see README.md of the each library.

3. Clone the repository and switch into it

$ git clone https://gitlab.com/ftz/mint.git
$ cd mint

4. Build the library

$ make

5. Install the library

# make symlink

Physical copy (you will have to do this after each commit)

# make install

How to use

Key management

In order to create, load or save keys you need to use the KeyManager class.

// This code demonstates the creation of key pairs.
#include <iostream>
#include <ftz/MINT/KeyManager.h>

int main()
{
	// Create 2048 bit RSA key pair.
	std::unique_ptr<ftz::MINT::KeyPairBase> keypair =
		ftz::MINT::KeyManager::CreateKeyPair(ftz::MINT::AsymmetricKeyType::RSA,
		2048);
	std::cout << "Type: " << keypair->GetType();
	std::cout << "Size: " << keypair->GetSize();
}

// This code demonstates the loading of key pairs.
#include <ftz/Serialization/FileInputStream.h>
#include <ftz/MINT/KeyManager.h>

int main()
{
	{
		ftz::Serialization::FileInputStream stream{"example1.key"};

		// Loading unencrypted key pair.
		std::unique_ptr<ftz::MINT::KeyPairBase> keypair =
			ftz::MINT::KeyManager::LoadKeyPair(stream);
	} // RAII closes the file.
	{
		ftz::Serialization::FileInputStream stream{"example2.key"};

		// Loading encrypted key pair.
		std::unique_ptr<ftz::MINT::KeyPairBase> keypair2 =
			ftz::MINT::KeyManager::LoadKeyPair(stream, "password");
	} // RAII closes the file.
}

// This code demonstates the saving of key pairs.
#include <ftz/Serialization/FileOutputStream.h>
#include <ftz/MINT/KeyManager.h>

int main()
{
	std::unique_ptr<ftz::MINT::KeyPairBase> keypair = // ...

	// Save the key pair into a file without encryption.
	ftz::Serialization::FileOutputStream stream1{"example1.key"};
	ftz::MINT::KeyManager::SaveKey(*keypair, stream1);
	
	// Save the key pair into a file with encryption.
	ftz::Serialization::FileOutputStream stream2{"example2.key"};
	ftz::MINT::KeyManager::SaveKey(*keypair, stream2,
		ftz::MINT::KeyEncryptionType::PBKDF2_HMAC_SHA256_AES256_GCM,
		"password");
}

Traffic management

All traffic is encapsulated into the Connection class. It requires a valid key pair and a callback to send data since some traffic like handshake is not meant to be dealt with by the user.

// A peer that initiates a connection. This can be a client.
#include <ftz/MINT/Connection.h>

// Callback to send data to the peer. You need to write one.
void SendData(const std::vector<std::byte>& data)
{
	// Send data somehow.
}

int main()
{
	//
	// Establish a low level connection to the peer somehow.
	//
	
	std::unique_ptr<ftz::MINT::KeyPairBase> keypair = // ...
	
	// By this time you should have a lower level connection established to
	// send data.
	ftz::MINT::Connection connection{*keypair, SendData};
	
	// Start the handshake. This will use SendData callback to talk to the peer.
	connection.SendHandshakeRequest();

	// After the connection has started you need to handle all incoming events.
	// This would be called inside the main loop of your application.
	// In a server code only this loop is useful since the server doesn't
	// usually initiate the connection. You will need a separate connection
	// for each peer.
	while (connection.HasEvents())
	{
		ftz::MINT::Event event = connection.GetEvent();
		auto type = static_cast<ftz::MINT::Event::Type>(event.data.which());
		switch (type)
		{
			case ftz::MINT::Event::Type::ConnectionStatus:
			{
				// Connect and disconnect are handled here.
			}
			case ftz::MINT::Event::Type::Alert:
			{
				// Something went wrong. A fatal alert means that connection
				// was terminated. You need to somehow destroy the connection
				// instance in your application.
			}
			case ftz::MINT::Event::Type::ApplicationData:
			{
				// A data for your application has arrived. Handling of it is
				// completely up to you.
			}
		}
	}
}

API reference