AES-DH Implementation
Loading...
Searching...
No Matches
util.h
1#pragma once
2
3#include <iostream> // For writing to console.
4#include <array> // For std::array
5
6#include "exchange.h" // To exchange the DH keys.
7#include "aes.h" // For AES Encryption.
8#include "hmac.h" // To generate an HMAC for the message.
9
10
16namespace util {
17 // A macro to prompt, then return from a function.
18 #define prompt_return(msg) {util::prompt(msg); return;}
19 // A macro to prompt, then break from a loop/switch
20 #define prompt_break(msg) {util::prompt(msg); break;}
21 // A macro to prompty, then continue from a loop
22 #define prompt_continue(msg) {util::prompt(msg); continue;}
23
24
29 inline void clear() {std::cout << "\033[2J\033[1;1H";}
30
31
42 template <typename T = bool> inline T input(const std::string& title, const T& error_ret = T()) {
43 T ret;
44
45 // Print the title, get the input.
46 std::cout << title << std::endl;
47 std::cin >> ret;
48
49 // If it failed, clear the buffer and set the error return.
50 auto f = std::cin.fail();
51 if (f) {
52 std::cin.clear();
53 ret = error_ret;
54 }
55
56 // Skip past whatever garbage the user may have added.
57 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
58
59 // Return.
60 return ret;
61 }
62
63
69 inline void prompt(const std::string& message) {
70 std::cout << message << std::endl;
71 std::cout << "Press Enter to Continue" << std::endl;
72 getchar();
73 }
74
75
88 void construct_shared_key(std::array<uint64_t, 4>& sk, const bool& server) {
89 std::cout << "Exchanging Keys..." << std::endl;
90 for (size_t x = 0; x < 4; ++x) {
91 sk[x] = exchange::exchange_keys(server);
92 }
93
94 prompt("Complete! Ensure that the Shared Key matches!");
95 }
96
97
102 void receive_message(const std::array<uint64_t, 4>& sk) {
103
104 std::cout << "Receiving Key Size..." << std::endl;
106
107 std::cout << "Receiving Ciphertext..." << std::endl;
108 auto message = network::recv_string();
109
110 std::cout << "Receiving Nonce..." << std::endl;
111 auto nonce_packet = network::recv_packet();
112
113 // Get the actual Nonce.
114 auto str = std::string(&nonce_packet.data[0], PACKET_SIZE);
115 uint64_t nonce = 0;
116 std::istringstream (str) >> nonce;
117
118 // GCM doesn't include an HMAC.
119 if (nonce_packet.m == network::meta::IV) {
120 try {
121 std::cout << "Message: " << aes::gcm::Dec(message, sk, Nr, nonce) << std::endl;
122 }
123 catch (std::runtime_error& e) {prompt_return(e.what());}
124 }
125
126 else {
127 std::cout << "Receiving HMAC..." << std::endl;
128 auto hmac = network::recv_string();
129
130 // Check that the HMAC matches what we expect. Refuse to decrypt unless it matches.
131 if (hmac != hmac::generate(message, sk, Nr))
132 prompt_return("HMAC does not match! Message has been altered!");
133
134 // A NONCE means we're using CTR.
135 if (nonce_packet.m == network::meta::NONCE) {
136 std::cout << "Message: " << aes::Ctr(message, sk, Nr, nonce) << std::endl;
137 }
138 // An EMPTY means we're using ECB.
139 else if (nonce_packet.m == network::meta::EMPTY) {
140 std::cout << "Message: " << aes::InvCipher(message, sk, Nr) << std::endl;
141 }
142
143 // Something else means the peer did something wrong.
144 else prompt_return("Peer sent invalid packet!");
145 }
146 std::cout << "Press Enter to Continue" << std::endl;
147 getchar();
148 }
149
150
155 void send_message(const std::array<uint64_t, 4>& sk) {
156 // Get the message to encrypt.
157 std::string message;
158 std::cout << "Enter the message:" << std::endl;
159 std::getline(std::cin, message);
160
161 // Get the amount of rounds.
162 auto size = input<int>("What size key?\n1. 128\n2. 192\n3. 256\n", -1);
163 if (size < 1 || size > 3) prompt_return("Invalid selection");
164 uint64_t Nr = size == 1 ? 10 : size == 2 ? 12 : 14;
165
166 // Get the mode.
167 auto option = input<int>("What mode?\n1. ECB\n2. CTR\n3. GCM", -1);
168 if (option < 1 || option > 3) prompt_return("Invalid selection");
169
170 /*
171 * The communication between the peers is as follows:
172 *
173 * INITIATOR RECIPIENT
174 * MESSAGE -->
175 * <-- ACK/REFUSE
176 * NR -->
177 * CIPHERTEXT -->
178 * NONCE/EMPTY/IV --> IV: GCM-DECRYPT
179 * HMAC -->
180 * CHECK HMAC
181 * DECRYPT
182 */
183
184 // Let the peer know we want to send a message.
185 std::cout << "Reaching out to the Peer..." << std::endl;
186 if (network::send_packet({.m = network::meta::MESSAGE}) == -1)
187 prompt_return("Failed to communicate with peer!");
188
189 // Get their response. Be generous with the response
190 auto response = network::recv_packet(30);
191 switch (response.m) {
192 case network::meta::ACK: break;
193 case network::meta::REFUSED: prompt_return("Peer refused to accept message!");
194 case network::meta::ERROR: prompt_return("Could not communicate with peer!");
195 case network::meta::MESSAGE: prompt_return("Cannot send two messages at once! One peer must Listen!");
196 default: prompt_return("Peer sent invalid response!");
197 }
198
199 // Even though ECB doesn't use this, we generate it for the others.
200 const uint64_t nonce = std::rand();
201
202 // Get the cipher.
203 auto cipher = option == 1 ?
204 aes::Cipher(message, sk, Nr) : option == 2 ?
205 aes::Ctr(message, sk, Nr, nonce) :
206 aes::gcm::Enc(message, sk, Nr, nonce);
207
208 if (network::send_value<uint64_t>(Nr) == -1)
209 prompt_return("Failed to send Key Size!");
210
211 // Send that cipher across.
212 if (network::send_string(cipher) == -1)
213 prompt_return("Failed to send ciphertext!");
214
215 // ECB; we send an empty packet as there is no nonce.
216 if (option == 1) {
217 if (network::send_packet({.m = network::meta::EMPTY}) == -1)
218 prompt_return("Failed to send empty packet!");
219 }
220
221 //CTR; we need to create and send the NONCE.
222 else if (option == 2) {
223 if (network::send_value(nonce, network::meta::NONCE) == -1)
224 prompt_return("Failed to send nonce!");
225 }
226
227 // GCM does not generate an HMAC, so just return once we've sent the IV.
228 if (option == 3) {
229 if (network::send_value(nonce, network::meta::IV) == -1)
230 prompt_return("Failed to send IV!");
231 }
232 else {
233 // Generate the HMAC and send it across.
234 auto hmac = hmac::generate(cipher, sk, Nr);
235
236 if (network::send_string(hmac) == -1)
237 prompt_return("Failed to send HMAC!");
238 }
239 }
240
241
247 bool acknowledge(const std::string& what) {
248 // Do we want to accept this?
249 auto response = input<std::string>(what + ": Acknowledge? (y/n)");
250
251 // Send an ACK.
252 if (response == "y" || response == "Y") {
253 network::send_packet({.m = network::meta::ACK});
254 return true;
255 }
256
257 // Send a REFUSED.
258 network::send_packet({.m = network::meta::REFUSED});
259 return false;
260 }
261}
std::string Dec(const std::string &in, const std::array< uint64_t, 4 > &k, const uint64_t Nr, uint64_t nonce)
Decrypt a message with AES-GCM.
Definition aes.h:1049
std::string InvCipher(const std::string &in, const std::array< uint64_t, 4 > &k, const uint64_t &Nr)
Decrypt a message with AES.
Definition aes.h:744
std::string Ctr(const std::string &in, const std::array< uint64_t, 4 > &k, const uint64_t Nr, uint64_t nonce)
An implementation of AES in CTR mode.
Definition aes.h:776
std::string Cipher(const std::string &in, const std::array< uint64_t, 4 > &k, const uint64_t &Nr)
Encrypt a message with AES.
Definition aes.h:716
uint64_t exchange_keys(const bool &server)
Exchange keys on an established connection.
Definition exchange.h:65
This namespace includes the functions needed to generate an HMAC value Using OpenSSL.
Definition hmac.h:14
std::string generate(const std::string &message, const std::array< uint64_t, 4 > &key, const size_t &rounds)
Generate an HMAC for a message.
Definition hmac.h:30
int send_packet(const packet &p, const size_t &timeout=5)
Send a packet.
Definition network.h:68
std::string recv_string(const size_t &timeout=5)
Receive a string.
Definition network.h:188
int send_value(const T &value, const network::meta &type=DATA, const size_t &timeout=5)
Send a value.
Definition network.h:109
int send_string(const std::string &message, const network::meta &type=DATA, const size_t &timeout=5)
Send a string of any size.
Definition network.h:153
const T recv_value(const size_t &timeout=5)
Receive a value.
Definition network.h:131
packet recv_packet(const size_t &timeout=5)
Receive a packet.
Definition network.h:87
The utility namespace.
Definition util.h:16
void construct_shared_key(std::array< uint64_t, 4 > &sk, const bool &server)
Genreate a shared key over a connection.
Definition util.h:88
void receive_message(const std::array< uint64_t, 4 > &sk)
Receive an encrypted message from the peer.
Definition util.h:102
void prompt(const std::string &message)
Prompt the user and wait until they have confirmed it.
Definition util.h:69
bool acknowledge(const std::string &what)
Acknowledge a request from a peer.
Definition util.h:247
void send_message(const std::array< uint64_t, 4 > &sk)
Send an encrypted message to a peer.
Definition util.h:155
T input(const std::string &title, const T &error_ret=T())
std::cin can be a little difficult to use, particularly handling bad input. This sanitized it.
Definition util.h:42
void clear()
Clear the screen.
Definition util.h:29