libDwmCredence-0.1.32
DwmCredence Class Library

Introduction

This class library may be used for secure, authenticated network communication over TCP. The main three classes are Dwm::Credence::Peer, Dwm::Credence::KeyStash and Dwm::Credence::KnownKeys. Utilizing just these three classes, it is relatively easy to create secure TCP applications.

The library depends on libsodium, boost::asio and libDwm.

Since I'm using libsodium, I'm using XChaCha20Poly1305 for encryption. User and server authentication uses signatures produced and validated with Ed25519 keys.

No passwords are used, only key files (which aren't password protected). This is intentional since my main usage is in applications that are not launched interactively. I may later add password protected keys.

Main Library Abstractions

Peer

The Peer class encapsulates a connection, via TCP or a UNIX domain socket. It is the main interface through which connections are initiated (via the Connect() member), accepted (via the Accept() member), and authenticated (via the Authenticate() member). It is also the interface through which messages are sent (via the Send() member) and received (via the Receive() member).

Connection Establishment

A client utilizes the Connect() member to connect to a server. A server accepts incoming connections via the Accept() member. As part of the connection setup, a shared encryption key is derived by each side. This key is ephemeral (only used for this connection), and used to encrypt all future traffic between the peers.

Mutual Authentication

The Authenticate() member of the Peer class performs mutual authentication. It verifies the claimed identity of the remote service and provides verifiable evidence of the identity of the local application to the remote service. It returns true if authentication succeeds, false if it fails.

Send and Receive Messages

Messages are exchanged using the Send() and Receive() members of the Peer class. These are member function templates, and use Dwm::StreamIO functionality from libDwm to allow sending and receiving all types supported directly by libDwm as well as all types which implement the requirements of the Dwm::HasStreamRead and Dwm::HasStreamWrite concepts.

Key Stash and Known Keys

Key Stash

Known Keys

History

This library came about when I needed a replacement for Crypto++ (needed by libDwmAuth). In my own applications, libDwmAuth was replaced by libDwmCredence. The new name is a hint that it's not the same as libDwmAuth under the hood, and allowed me to migrate my applications as I had time.

Platforms

I only maintain support for 4 platforms: FreeBSD, macOS, desktop linux and Raspbian (now Raspberry Pi OS). FreeBSD is my operating system of choice for servers and macOS is my operating system of choice for desktops and laptops. I have several Raspberry Pis I utilize for various tasks, and Ubuntu VMs and Ubuntu workstations.

Examples

Assumptions

The examples assume that you have created your key files by running credence keygen, and that they are present in the default directory (~/.credence). They also assume that you have your own public key (from ~/.credence/id_ed25519.pub) in the default ~/.credence/known_keys file.

Simple echo

Simple echo client

1#include "DwmCredencePeer.hh"
2
3int main(int argc, char *argv[])
4{
5 using namespace std;
6 using namespace Dwm;
7
8 int rc = 1;
9
10 if (argc < 3) {
11 cerr << "Usage: " << argv[0] << " host port\n";
12 return 1;
13 }
14
15 Credence::Peer peer;
16 if (peer.Connect(argv[1], std::stoul(argv[2]))) {
17 Credence::KeyStash keyStash;
18 Credence::KnownKeys knownKeys;
19 if (peer.Authenticate(keyStash, knownKeys)) {
20 rc = 0;
21 string msg;
22 while (std::getline(cin, msg)) {
23 if (! peer.Send(msg)) { rc = 1; break; }
24 if (! peer.Receive(msg)) { rc = 1; break; }
25 cout << msg << '\n';
26 if (msg == "Goodbye") { break; }
27 }
28 }
29 else {
30 cerr << "Failed to authenticate to " << argv[1]
31 << " port " << argv[2] << '\n';
32 }
33 }
34 else {
35 cerr << "Failed to connect to " << argv[1]
36 << " port " << argv[2] << '\n';
37 }
38
39 return rc;
40}
Dwm::Credence::Peer class declaration.
Encapsulates storage of an Ed25519KeyPair in a filesystem.
Definition DwmCredenceKeyStash.hh:56
Encapsulates the storage of a set of known public keys.
Definition DwmCredenceKnownKeys.hh:60
Encapsulate a network peer.
Definition DwmCredencePeer.hh:68
bool Send(const T &msg)
Sends the given msg to the peer.
Definition DwmCredencePeer.hh:146
bool Connect(const std::string &host, uint16_t port, std::chrono::milliseconds timeOut=std::chrono::milliseconds(5000))
Used by a client to connect to host at the given port, waiting timeOut for success.
bool Receive(T &msg)
Receives the given msg from the peer.
Definition DwmCredencePeer.hh:178
bool Authenticate(const KeyStash &keyStash, const KnownKeys &knownKeys)
Using the given keyStash and knownKeys, authenticate our identity to the peer and verify the peer's i...

Simple echo server

Note that this server only accepts one client, and will exit after communicating with the client. In other words, it's not typical, but instead is a minimal illustration.

1#include "DwmCredencePeer.hh"
2
3using namespace std;
4using namespace boost::asio;
5using namespace Dwm;
6
7//----------------------------------------------------------------------------
8static ip::tcp::socket AcceptSocket(io_context & ioContext,
9 const string & addr, uint16_t port)
10{
11 ip::tcp::endpoint endPoint(ip::address::from_string(addr), port);
12 ip::tcp::acceptor acc(ioContext, endPoint);
13 boost::asio::ip::tcp::acceptor::reuse_address option(true);
14 acc.set_option(option);
15 acc.non_blocking(false);
16
17 ip::tcp::endpoint client;
18 ip::tcp::socket sock(ioContext);
19 acc.accept(sock, client);
20 sock.native_non_blocking(false);
21 return sock;
22}
23
24//----------------------------------------------------------------------------
25static bool AcceptPeer(io_context & ioContext, const string & addr,
26 const string & port, Credence::Peer & peer)
27{
28 bool rc = false;
29 try {
30 ip::tcp::socket sock = AcceptSocket(ioContext, addr, std::stoul(port));
31 if (sock.is_open()) {
32 rc = peer.Accept(std::move(sock));
33 }
34 }
35 catch (std::exception & ex) {
36 cerr << "Exception: " << ex.what() << '\n';
37 }
38 return rc;
39}
40
41//----------------------------------------------------------------------------
42int main(int argc, char *argv[])
43{
44 if (argc < 3) {
45 cerr << "Usage: " << argv[0] << " addr port\n";
46 return 1;
47 }
48
49 int rc = 1;
50 io_context ioContext;
51 Credence::Peer peer;
52 if (AcceptPeer(ioContext, argv[1], argv[2], peer)) {
53 Credence::KeyStash keyStash;
54 Credence::KnownKeys knownKeys;
55 if (peer.Authenticate(keyStash, knownKeys)) {
56 rc = 0;
57 string msg;
58 do {
59 if (! peer.Receive(msg)) { rc = 1; break; }
60 if (! peer.Send(msg)) { rc = 1; break; }
61 } while (msg != "Goodbye");
62 }
63 else {
64 cerr << "Authentication failed\n";
65 }
66 }
67 else {
68 cerr << "AcceptPeer failed\n";
69 }
70
71 return rc;
72}
bool Accept(boost::asio::ip::tcp::socket &&s)
Used by a server to accept a new connection on the given TCP socket s.