Secure communication with End-to-End encryption
Example applications
emCrypt includes all algorithms, functions and cryptography primitives to implement various security schemes and protocols. emSSL (SSL and TLS) and emSSH are most advanced protocols, using emCrypt as its foundation.
This application demonstrates how to easily enable end-to-end encrypted communication between two clients which may be directly connected or communicate via a server. The source code is a reference for authenticated and encrypted peer-to-peer communication in less than 300 lines of code.
Example Application
The Secure Communication Example demonstrates the use authentication and end-to-end encryption using a password.
Secure Communication Server
Secure Communication Client
Encrypted Communication on the wire
When the first instance of the application is started, it runs as the "server", waiting for a connection. When a second instance is started, it runs as the "client" and connects to the server.
In both cases the password needs to be entered, and it has to be the same for server and client.
When the client connects to the server, the server challenges the client to make sure it uses the same password, to authenticate it. Once the client is authenticated, server and client can compute the session key used to encrypt further communication. Now data can be sent from client to server and vice-versa.
Note: The application is built to open a connection on localhost. Both instances have to run on the same host. This is only done to make it easily usable without any firewall configuration. The example can be reused to open connections to any host or even be tunneled via a server both clients connect to.
End-to-End Encryption with emCrypt
For the end-to-end encryption this example uses the password-based encryption scheme (PBES2) and a symmetric key algorithm on two different keystreams to establish a secured full-duplex communication channel.
AES is chosen as the symmetric key algorithm.
It is used as a stream cipher in Output Feedback (OFB) mode or Counter with CBC-MAC (CCM) mode to encrypt each communication stream individually rather than each message.
While OFB mode only encrypts the stream, CCM mode also adds an authentication tag to each message, which ensures that the message has not been altered in transport.
Due to the symmetric key algorithm, both peers need to share the same keys, which are based on a password known to both sides.
To authenticate the client the Challenge Handshake Authentication Protocol (CHAP) is used.
CHAP makes sure that the peer knows the shared secret (the password) without transmitting it.
The session keys and keystreams are derived from the password and a random value using Password Based Key Derivation Function (PBKDF2), which makes sure that a new set of keys is used for each connection.
AES, PBKDF2, and SHA256 (used for CHAP), are available as part of emCrypt with ready-to-use APIs.
With these functions, end-to-end encryption can easily be implemented in any application.
Source Listing
/*********************************************************************
* (c) SEGGER Microcontroller GmbH *
* The Embedded Experts *
* www.segger.com *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
File : MAIN_SERVER.c
Purpose : Demonstrate how to setup a server/ client with E2EE using emCrypt.
*/
/*********************************************************************
*
* #include section
*
**********************************************************************
*/
#include "CRYPTO.h"
#include "SYS.h"
#include <stdio.h>
#include <stdlib.h>
/*********************************************************************
*
* Defines, fixed
*
**********************************************************************
*/
#define MODE_NONE 0
#define MODE_OFB 1
#define MODE_CCM 2
/*********************************************************************
*
* Defines, configurable
*
**********************************************************************
*/
#define AES_MODE MODE_OFB
#define COPYRIGHT_STRING "emCrypt Communication Sample, (c) 2019 SEGGER Microcontroller GmbH."
#define DEFAULT_PASSWORD "Secret" // Default password
#define SERVER_LISTENER_PORT (19099) // TCP/IP port that server listens to
#define MAX_MSG_LEN (2048) // Maximum size (bytes) for a message. This includes a terminating \0.
#define MAX_MSG_OVERHEAD (128) // Maximum overhead from the encryption algorithm which is sent in addition th the message.
#define SERVER_CONN_ACCEPT (0x00) // Value sent by server on success
#define SERVER_CONN_DENY (0xFF) // Value sent by server on fail
#define TAG_LEN 16 // TagLen must be 4, 6, 8, 10, 12, 14, or 16.
#define IV_LEN 8 // Length of IV for CCM Mode. Must be between 7 and 13
/*********************************************************************
*
* Local types
*
**********************************************************************
*/
typedef struct {
CRYPTO_AES_CONTEXT CipherContext;
#if AES_MODE == MODE_OFB
U8 aBlock[CRYPTO_AES_BLOCK_SIZE];
unsigned Index;
#elif AES_MODE == MODE_CCM
U8* pIV;
#endif
} KEYSTREAM;
typedef struct {
U8 abPassword[32];
U32 PasswordLen;
U8 abSalt[16];
U32 SaltLen;
struct {
U8 aToClientKey[CRYPTO_AES256_KEY_SIZE];
U8 aToClientIV[CRYPTO_AES_BLOCK_SIZE];
U8 aToServerKey[CRYPTO_AES256_KEY_SIZE];
U8 aToServerIV[CRYPTO_AES_BLOCK_SIZE];
} Session;
//
KEYSTREAM ToClientKeystream;
KEYSTREAM ToServerKeystream;
} CONNECTION_STATE;
typedef struct {
SYS_SOCKET_HANDLE hSock;
CONNECTION_STATE* pState;
int IsServer;
} THREAD_INFO;
/*********************************************************************
*
* Static data
*
**********************************************************************
*/
static volatile int _ErrorOccurred; // Used by threads to determine if an error occurred
/*********************************************************************
*
* Static code
*
**********************************************************************
*/
/*********************************************************************
*
* _DeriveKey()
*
* Function description
* Derive session keys.
*
* Parameters
* pState - Pointer to connection state.
*/
static void _DeriveKey(CONNECTION_STATE *pState) {
//
// Derive session data from "shared secret"
//
CRYPTO_PBKDF2_HMAC_SHA256_Calc(pState->abSalt, pState->SaltLen,
pState->abPassword, pState->PasswordLen,
10000,
(U8 *)&pState->Session, sizeof(pState->Session));
CRYPTO_AES_InitEncrypt(&pState->ToClientKeystream.CipherContext, pState->Session.aToClientKey, sizeof(pState->Session.aToClientKey));
CRYPTO_AES_InitEncrypt(&pState->ToServerKeystream.CipherContext, pState->Session.aToServerKey, sizeof(pState->Session.aToServerKey));
#if AES_MODE == MODE_OFB
CRYPTO_AES_Encrypt(&pState->ToClientKeystream.CipherContext, pState->ToClientKeystream.aBlock, pState->Session.aToClientIV);
CRYPTO_AES_Encrypt(&pState->ToServerKeystream.CipherContext, pState->ToServerKeystream.aBlock, pState->Session.aToServerIV);
pState->ToClientKeystream.Index = 0;
pState->ToServerKeystream.Index = 0;
#elif AES_MODE == MODE_CCM
pState->ToClientKeystream.pIV = pState->Session.aToClientIV;
pState->ToServerKeystream.pIV = pState->Session.aToServerIV;
#endif
}
/*********************************************************************
*
* _Encrypt()
*
* Function description
* Encrypt plain data.
*
* Parameters
* pState - Pointer to connection state.
* pSrc - Source buffer.
* pDest - Destination buffer.
* NumBytes - Length of buffers in bytes.
*
* Return Value
* >= 0: Number of bytes to be sent.
*/
static int _Encrypt(CONNECTION_STATE *pState, const void* pSrc, void* pDest, U32 NumBytes, KEYSTREAM* pKeystream) {
U8* pCipher;
#if AES_MODE == MODE_OFB
U32 i;
CRYPTO_WRU32LE(pDest, NumBytes);
pCipher = (U8*)pDest + 4;
memcpy(pCipher, pSrc, NumBytes);
for (i = 0; i < NumBytes; ++i) {
pCipher[i] ^= pKeystream->aBlock[pKeystream->Index];
if (++pKeystream->Index == CRYPTO_AES_BLOCK_SIZE) {
CRYPTO_AES_Encrypt(&pKeystream->CipherContext, pKeystream->aBlock, pKeystream->aBlock);
pKeystream->Index = 0;
}
}
return NumBytes + 4;
#elif AES_MODE == MODE_CCM
CRYPTO_WRU32LE(pDest, NumBytes + TAG_LEN); // Write actual data length to buffer
CRYPTO_AES_CCM_Encrypt(&pKeystream->CipherContext, &pDest[4 + TAG_LEN], &pDest[4], TAG_LEN, (const U8*)pSrc, NumBytes, NULL, 0, pKeystream->pIV, IV_LEN); // Encrypt data and add authentication tag
CRYPTO_IncCTRBE(pKeystream->pIV, IV_LEN, 1); // Increment counter for next round
return (NumBytes + TAG_LEN) + 4;
#elif AES_MODE == MODE_NONE
CRYPTO_WRU32LE(pDest, NumBytes);
pCipher = (U8*)pDest + 4;
memcpy(pCipher, pSrc, NumBytes);
return NumBytes + 4;
#else
#error "Unknown mode!"
#endif
}
/*********************************************************************
*
* _Decrypt()
*
* Function description
* Decrypt encrypted data.
*
* Parameters
* pState - Pointer to connection state.
* pSrc - Source buffer.
* pDest - Destination buffer.
* NumBytes - Length of buffers in bytes.
*
* Return Value
* == 0: OK. (in CCM Mode: Calculated tag and given tag are identical).
* != 0: Error (in CCM Mode: Calculated tag and given tag are not identical).
*/
static int _Decrypt(CONNECTION_STATE *pState, const void* pSrc, void* pDest, U32 NumBytes, KEYSTREAM* pKeystream) {
int Result;
U8* pCipher;
#if AES_MODE == MODE_OFB
U32 i;
pCipher = (U8*)pDest;
memcpy(pCipher, pSrc, NumBytes);
for (i = 0; i < NumBytes; ++i) {
pCipher[i] ^= pKeystream->aBlock[pKeystream->Index];
if (++pKeystream->Index == CRYPTO_AES_BLOCK_SIZE) {
CRYPTO_AES_Encrypt(&pKeystream->CipherContext, pKeystream->aBlock, pKeystream->aBlock);
pKeystream->Index = 0;
Result = 0;
}
}
#elif AES_MODE == MODE_CCM
Result = CRYPTO_CIPHER_AES_CCM_Decrypt(&pKeystream->CipherContext, pDest, pSrc, TAG_LEN, pSrc + TAG_LEN, NumBytes - TAG_LEN, NULL, 0, pKeystream->pIV, IV_LEN);
CRYPTO_IncCTRBE(pKeystream->pIV, IV_LEN, 1);
#elif AES_MODE == MODE_NONE
Result = 0;
pCipher = (U8*)pDest;
memcpy(pCipher, pSrc, NumBytes);
#else
#error "Unknown mode!"
#endif
return Result;
}
/*********************************************************************
*
* _SendThread()
*
* Function description
* Thread that is responsible for handling data to send from the server to the client.
*/
static SYS_THREAD_PROC_EX_TYPE _SendThread(void* p) {
int Result;
int r;
U32 Len;
char acIn [MAX_MSG_LEN];
U8 abOut[MAX_MSG_LEN + MAX_MSG_OVERHEAD];
THREAD_INFO* pInfo;
KEYSTREAM* pKeystream;
Result = 0;
pInfo = (THREAD_INFO*)p;
//
// Set keystream to use for sending.
//
if (pInfo->IsServer == 1) {
pKeystream = &pInfo->pState->ToClientKeystream;
} else {
pKeystream = &pInfo->pState->ToServerKeystream;
}
do {
SYS_ConsoleGetString("", acIn, MAX_MSG_LEN);
Len = strlen(acIn) + 1; // Include terminating \0
Len = _Encrypt(pInfo->pState, acIn, abOut, Len, pKeystream);
r = SYS_SOCKET_Send(pInfo->hSock, abOut, Len);
if (r != Len) { // Failed to send data? => Done
printf("ERROR: Failed to send data to client\n");
Result = -1;
_ErrorOccurred = 1;
break;
}
} while (_ErrorOccurred == 0);
return (SYS_THREAD_PROC_EX_R_TYPE)0;
}
/*********************************************************************
*
* _RecvThread()
*
* Function description
* Thread that is responsible for handling data to receive by the server from the client.
*/
static SYS_THREAD_PROC_EX_TYPE _RecvThread(void* p) {
int Result;
int r;
U32 Len;
U8 abIn [MAX_MSG_LEN + MAX_MSG_OVERHEAD];
U8 abOut[MAX_MSG_LEN];
THREAD_INFO* pInfo;
KEYSTREAM* pKeystream;
Result = 0;
pInfo = (THREAD_INFO*)p;
if (pInfo->IsServer == 1) {
pKeystream = &pInfo->pState->ToServerKeystream;
} else {
pKeystream = &pInfo->pState->ToClientKeystream;
}
do {
r = SYS_SOCKET_IsReadable(pInfo->hSock, 100); // Poll for data every 100 ms
if (r == 0) { // Nothing to read yet? => Try again.
continue;
}
r = SYS_SOCKET_Receive(pInfo->hSock, abIn, 4);
if (r <= 0) {
printf("ERROR: Failed to receive data length from client\n");
Result = -1;
_ErrorOccurred = 1;
break;
}
Len = CRYPTO_RDU32LE(&abIn[0]);
r = SYS_SOCKET_Receive(pInfo->hSock, &abIn[4], Len);
if (r <= 0) {
printf("ERROR: Failed to receive data from client\n");
Result = -1;
_ErrorOccurred = 1;
break;
}
_Decrypt(pInfo->pState, &abIn[4], abOut, Len, pKeystream);
printf("%s\n", abOut);
} while (_ErrorOccurred == 0);
return (SYS_THREAD_PROC_EX_R_TYPE)0;
}
/*********************************************************************
*
* _RunServer()
*
* Function description
* Runs server
*
* Parameters
* hSock Opened TCP socket listening on SERVER_LISTENER_PORT
*
*/
static int _RunServer(SYS_SOCKET_HANDLE hSockListen) {
U32 HashLen;
U32 TmpLen;
int Result;
int r;
char acPass[32];
const char* sPass;
SYS_SOCKET_HANDLE hSockClient;
U8 abTmp[256]; // Must be large enough to fit password + salt
U8 abHash[32];
U8 abClient[32];
U8 Stat;
CONNECTION_STATE* pState;
THREAD_INFO* pThreadInfo;
//
// Start with title
//
Result = 0;
pState = NULL;
pThreadInfo = NULL;
hSockClient = SYS_SOCKET_INVALID_HANDLE;
printf("\n\n");
printf(COPYRIGHT_STRING "\n");
printf("Operating in server mode\n");
#if AES_MODE == MODE_OFB
printf("The AES OFB encryption mode is used.\n");
#elif AES_MODE == MODE_CCM
printf("The AES CCM encryption mode is used.\n");
#elif AES_MODE == MODE_NONE
printf("No encryption mode is used.\n");
#else
#error "Unknown mode!"
#endif
printf("\n\n");
//
// Generate random 16 byte salt
//
pState = (CONNECTION_STATE*)malloc(sizeof(CONNECTION_STATE));
if (pState == NULL) {
printf("ERROR: Insufficient memory.\n");
Result = -1;
goto Done;
}
memset(pState, 0, sizeof(CONNECTION_STATE));
CRYPTO_Init();
pState->SaltLen = sizeof(pState->abSalt);
CRYPTO_RNG_Get(pState->abSalt, pState->SaltLen);
//
// Ask user for password to use
//
printf("Set the password (Default = \"%s\") ", DEFAULT_PASSWORD);
SYS_ConsoleGetString("> ", acPass, sizeof(acPass));
sPass = (acPass[0]) ? &acPass[0] : DEFAULT_PASSWORD;
//
// Append password to salt and generate hash
// The resulting hash is the result of the challenge used by CHAP to authenticate a client.
//
pState->PasswordLen = strlen(sPass);
memcpy(pState->abPassword, sPass, pState->PasswordLen + 1);
HashLen = sizeof(abHash);
memcpy(&abTmp[0], pState->abSalt, pState->SaltLen);
memcpy(&abTmp[pState->SaltLen], pState->abPassword, pState->PasswordLen);
TmpLen = pState->PasswordLen + pState->SaltLen;
CRYPTO_SHA256_Calc(abHash, HashLen, abTmp, TmpLen);
//
// Wait for localhost client to connect
//
printf("Waiting for client to connect (Port = %d)...", SERVER_LISTENER_PORT);
hSockClient = SYS_SOCKET_Accept(hSockListen);
if (hSockListen == SYS_SOCKET_INVALID_HANDLE) { // Failed to open socket? => Done
printf("ERROR: An error occurred while waiting for a client to connect.\n");
Result = -1;
goto Done;
}
printf("OK\n");
//
// After a client has connected, send challenge to client and wait for it to "solve" the challenge.
//
// <Direction> <NumBytes> <Explanation>
// S -> C 16 Salt (Challenge)
// C -> S 32 Hash (Result)
// S -> C 1 Server status
//
SYS_SOCKET_SetBlocking(hSockClient); // Make sure socket operations are blocking
r = SYS_SOCKET_Send(hSockClient, pState->abSalt, pState->SaltLen);
if (r != pState->SaltLen) { // Failed to send challenge? => Done
printf("ERROR: An error occurred while sending data to the client.\n");
Result = -1;
goto Done;
}
r = SYS_SOCKET_Receive(hSockClient, abClient, HashLen);
if (r != HashLen) { // Failed to receive data? => Done
printf("ERROR: An error occurred while receiving data from the client.\n");
Result = -1;
goto Done;
}
r = memcmp(abClient, abHash, HashLen); // Compare client's response with expected hash value
Stat = (r == 0) ? SERVER_CONN_ACCEPT : SERVER_CONN_DENY;
SYS_SOCKET_Send(hSockClient, &Stat, 1); // Tell client if connection was accepted or denied
if (r != 0) { // Client's response is not correct? => Done
printf("ERROR: Client failed to register (Incorrect password?).\n");
Result = -1;
goto Done;
}
//
// After a successful authentication, E2EE communication between server and client can begin.
// For that, a key is derived from the password and salt.
//
_DeriveKey(pState);
pThreadInfo = (THREAD_INFO*)malloc(sizeof(THREAD_INFO));
if (pThreadInfo == NULL) {
printf("ERROR: Insufficient memory.\n");
Result = -1;
goto Done;
}
pThreadInfo->hSock = hSockClient;
pThreadInfo->pState = pState;
pThreadInfo->IsServer = 1;
SYS_SOCKET_SetNonBlocking(hSockClient);
printf("\n========== Secure connection established. ==========\n========== Type anything and press <Enter> to send. ==========\n\n");
SYS_CreateThreadEx(_SendThread, pThreadInfo, NULL, "Sender thread", 0);
SYS_CreateThreadEx(_RecvThread, pThreadInfo, NULL, "Receiver thread", 0);
//
// From here on, the other threads handle further communication
//
while(_ErrorOccurred == 0);
SYS_Sleep(100); // If one thread reported an error, the other might still access some resources. Give the other thread enought time to terminate.
Done:
//
// Clean-up
//
if (hSockClient != SYS_SOCKET_INVALID_HANDLE) {
SYS_SOCKET_Close(hSockClient);
}
if (hSockListen != SYS_SOCKET_INVALID_HANDLE) {
SYS_SOCKET_Close(hSockListen);
}
if (pState) {
free(pState);
}
if (pThreadInfo) {
free(pThreadInfo);
}
return Result;
}
/*********************************************************************
*
* _RunClient()
*
* Function description
* Runs client
*
* Parameters
* hSock Opened TCP socket
*/
static int _RunClient(SYS_SOCKET_HANDLE hSock) {
int Result;
int r;
U32 TmpLen;
U32 HashLen;
U8 abTmp[256]; // Must be large enough to fit password + salt
U8 abHash[32];
U8 Stat;
char acPass[32];
const char* sPass;
CONNECTION_STATE* pState;
THREAD_INFO* pThreadInfo;
//
// Start with title
//
Result = 0;
pState = NULL;
pThreadInfo = NULL;
printf("\n\n");
printf(COPYRIGHT_STRING "\n");
printf("Operating in client mode\n");
#if AES_MODE == MODE_OFB
printf("The AES OFB encryption mode is used.\n");
#elif AES_MODE == MODE_CCM
printf("The AES CCM encryption mode is used.\n");
#elif AES_MODE == MODE_NONE
printf("No encryption mode is used.\n");
#else
#error "Unknown mode!"
#endif
printf("\n\n");
//
// Connect to server
//
printf("Connecting to server...");
SYS_SOCKET_SetBlocking(hSock);
r = SYS_SOCKET_Connect(hSock, SYS_SOCKET_IP_ADDR_LOCALHOST, SERVER_LISTENER_PORT);
if (r != 0) { // Failed to connect? => Done
printf("ERROR: Failed to connect to server.\n");
Result = -1;
goto Done;
}
printf("OK\n");
//
// Prompt user for password.
// The password is needed for solving the server's challenge correctly
//
pState = (CONNECTION_STATE*)malloc(sizeof(CONNECTION_STATE));
if (pState == NULL) {
printf("ERROR: Insufficient memory.\n");
Result = -1;
goto Done;
}
memset(pState, 0, sizeof(CONNECTION_STATE));
printf("Please enter the password (Default: \"%s\") ", DEFAULT_PASSWORD);
SYS_ConsoleGetString("> ", acPass, sizeof(acPass));
sPass = (acPass[0]) ? &acPass[0] : DEFAULT_PASSWORD;
pState->PasswordLen = strlen(sPass);
memcpy(pState->abPassword, sPass, pState->PasswordLen + 1);
//
// Receive and solve challenge from server
// <Direction> <NumBytes> <Explanation>
// S -> C 16 Salt (Challenge)
//
pState->SaltLen = sizeof(pState->abSalt);
r = SYS_SOCKET_Receive(hSock, pState->abSalt, pState->SaltLen);
if (r != pState->SaltLen) {
printf("ERROR: Failed to receive data from server.\n");
Result = -1;
goto Done;
}
memcpy(&abTmp[0], pState->abSalt, pState->SaltLen);
memcpy(&abTmp[pState->SaltLen], pState->abPassword, pState->PasswordLen);
TmpLen = pState->PasswordLen + pState->SaltLen;
HashLen = sizeof(abHash);
CRYPTO_Init();
CRYPTO_SHA256_Calc(abHash, HashLen, abTmp, TmpLen);
//
// Register at server using result of challenge
// <Direction> <NumBytes> <Explanation>
// C -> S 32 Hash (Result)
// S -> C 1 Server status
//
r = SYS_SOCKET_Send(hSock, abHash, HashLen);
if (r != HashLen) {
printf("ERROR: Failed to send data to server.\n");
Result = -1;
goto Done;
}
r = SYS_SOCKET_Receive(hSock, &Stat, 1); // Get status of connection
if (r != 1) {
printf("ERROR: Failed to receive data to server.\n");
Result = -1;
goto Done;
}
if (Stat != SERVER_CONN_ACCEPT) {
printf("ERROR: Server denied connection (Incorrect password?).\n");
Result = -1;
goto Done;
}
//
// After a successful authentication, E2EE communication between server and client can begin.
// For that, a key is derived from the password and salt.
//
_DeriveKey(pState);
pThreadInfo = (THREAD_INFO*)malloc(sizeof(THREAD_INFO));
if (pThreadInfo == NULL) {
printf("ERROR: Insufficient memory.\n");
Result = -1;
goto Done;
}
pThreadInfo->hSock = hSock;
pThreadInfo->pState = pState;
pThreadInfo->IsServer = 0;
SYS_SOCKET_SetNonBlocking(hSock);
printf("\n========== Secure connection established. ==========\n========== Type anything and press <Enter> to send. ==========\n\n");
SYS_CreateThreadEx(_SendThread, pThreadInfo, NULL, "Sender thread", 0);
SYS_CreateThreadEx(_RecvThread, pThreadInfo, NULL, "Receiver thread", 0);
//
// From here on, the other threads handle further communication
//
while(_ErrorOccurred == 0);
SYS_Sleep(100); // If one thread reported an error, the other might still access some resources. Give the other thread enought time to terminate.
Done:
//
// Clean-up
//
if (hSock != SYS_SOCKET_INVALID_HANDLE) {
SYS_SOCKET_Close(hSock);
}
if (pState) {
free(pState);
}
if (pThreadInfo) {
free(pThreadInfo);
}
return Result;
}
/*********************************************************************
*
* Public code
*
**********************************************************************
*/
/*********************************************************************
*
* main()
*
* Function description
* Main entry point for application.
*/
int main(void) {
int Result;
int r;
SYS_SOCKET_HANDLE hSock;
//
// Determine if this is the server or client side
//
Result = 0;
hSock = SYS_SOCKET_OpenTCP();
if (hSock == SYS_SOCKET_INVALID_HANDLE) { // Failed to open socket? => Done
printf("ERROR: Failed to open TCP socket.\n");
Result = -1;
goto Done;
}
r = SYS_SOCKET_ListenAtTCPAddr(hSock, SYS_SOCKET_IP_ADDR_LOCALHOST, SERVER_LISTENER_PORT, 1);
if (r == 0) { // We are able to listen at server listener port? => Run server
Result = _RunServer(hSock); // Blocking until server stops
} else { // We cannot listen at server listener port? => Server must already be running so run client.
Result = _RunClient(hSock); // Blocking until client stops
}
Done:
return Result;
}
/*************************** End of file ****************************/
Further Use
The example demonstrates a simple use case of authentication and end-to-end encryption.
Key Agreement
In the example, the data used for authentication, the password and the salt, are used to generate the key for the encrypted communication, using PBKDF2. Other key agreement mechanisms, such as Diffie-Hellman Key Exchange (DH or ECDH) could be used to get an encryption key that is not associated with the authentication parameters.