YARP
Yet Another Robot Platform
AuthHMAC.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-FileCopyrightText: 2010 Daniel Krieg <krieg@fias.uni-frankfurt.de>
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
8 
9 #include <yarp/os/Bytes.h>
10 #include <yarp/os/Network.h>
11 #include <yarp/os/Property.h>
12 #include <yarp/os/ResourceFinder.h>
15 
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cstring>
19 #include <ctime>
20 #include <random>
21 #include <string>
22 
23 namespace {
24 YARP_OS_LOG_COMPONENT(AUTHHMAC, "yarp.os.impl.AuthHMAC")
25 } // namespace
26 
27 void show_hmac_debug(unsigned char* hex, unsigned int length, const std::string& context)
28 {
29  char* buf;
30  int off = context.length();
31  buf = new char[length * 3 + off + 2];
32  strcpy(buf, context.c_str());
33  for (unsigned int i = 0; i < length; i++) {
34  sprintf(&(buf[off + i * 3]), "%X ", hex[i]);
35  }
36  yCDebug(AUTHHMAC, "%s\n", buf);
37  delete[] buf;
38 }
39 
40 using namespace yarp::os::impl;
41 using namespace yarp::os;
42 
44  authentication_enabled(false)
45 {
46  memset(&context, 0, sizeof(HMAC_CONTEXT));
47  static bool auth_warning_shown = false;
48  if (auth_warning_shown) {
49  // If the warning was already shown, we have nothing to do.
50  // return as soon as possible
51  return;
52  }
53  std::string key;
55  std::string fname;
56  Network::lock();
59  fname = rf.findFile("auth.conf", opt);
61 
62 
63  if (fname.empty()) {
64  yCDebug(AUTHHMAC, "Cannot find auth.conf file. Authentication disabled.\n");
65  auth_warning_shown = true;
66  return;
67  }
68 
69  Property config;
70  config.fromConfigFile(fname);
71  Bottle group = config.findGroup("AUTH");
72 
73  if (group.isNull()) {
74  yCWarning(AUTHHMAC, "No \"AUTH\" group found in auth.conf file. Authentication disabled.\n");
75  auth_warning_shown = true;
76  return;
77  }
78 
79  key = group.find("key").asString();
80  if (!(key.length() > 0)) {
81  yCWarning(AUTHHMAC, "No \"key\" found in \"AUTH\" group in auth.conf file. Authentication disabled.\n");
82  auth_warning_shown = true;
83  return;
84  }
85 
86  size_t key_len = key.length();
87  auto* tmp = new unsigned char[key_len];
88  strcpy(reinterpret_cast<char*>(tmp), key.c_str());
89  HMAC_INIT(&context, tmp, static_cast<unsigned int>(key_len));
90  delete[] tmp;
91  srand(static_cast<unsigned>(time(nullptr)));
92 
93  if (!authentication_enabled) {
94  yCInfo(AUTHHMAC, "Authentication enabled.\n");
95  authentication_enabled = true;
96  }
97 }
98 
99 
100 bool AuthHMAC::authSource(InputStream* streamIn, OutputStream* streamOut)
101 {
102 
103  if (!authentication_enabled) {
104  return true;
105  }
106 
107  /* ---------------
108  * 3-way auth
109  * Port A
110  * ---------------
111  */
112 
113  unsigned char nonce1[NONCE_LEN];
114  unsigned char nonce2[NONCE_LEN];
115  unsigned char nonce3[NONCE_LEN];
116 
117  unsigned char mac[DIGEST_SIZE];
118  unsigned char mac_check[DIGEST_SIZE];
119 
120  /* ---------------
121  * Send first msg: A->B
122  */
123  fill_nonce(nonce1);
124  HMAC_REINIT(&context);
125  HMAC_UPDATE(&context, nonce1, NONCE_LEN);
126  HMAC_FINAL(&context, mac, DIGEST_SIZE);
127  if (!send_hmac(streamOut, nonce1, mac)) {
128  return false;
129  }
130 
131  /* ---------------
132  * Receive and check second msg: B->A
133  */
134  if (!receive_hmac(streamIn, nonce2, mac)) {
135  return false;
136  }
137 
138  HMAC_REINIT(&context);
139  HMAC_UPDATE(&context, nonce1, NONCE_LEN);
140  HMAC_UPDATE(&context, nonce2, NONCE_LEN);
141  HMAC_FINAL(&context, mac_check, DIGEST_SIZE);
142  if (!check_hmac(mac, mac_check)) {
143  return false;
144  }
145  /* Authentication of B successful */
146 
147 
148  /* ---------------
149  * Send third msg: A->B
150  */
151  fill_nonce(nonce3);
152  HMAC_REINIT(&context);
153  HMAC_UPDATE(&context, nonce1, NONCE_LEN);
154  HMAC_UPDATE(&context, nonce2, NONCE_LEN);
155  HMAC_UPDATE(&context, nonce3, NONCE_LEN);
156  HMAC_FINAL(&context, mac, DIGEST_SIZE);
157  return send_hmac(streamOut, nonce3, mac);
158 }
159 bool AuthHMAC::authDest(InputStream* streamIn, OutputStream* streamOut)
160 {
161 
162  if (!authentication_enabled) {
163  return true;
164  }
165 
166  /* ---------------
167  * 3-way auth
168  * Port B
169  * ---------------
170  */
171 
172  unsigned char nonce1[NONCE_LEN];
173  unsigned char nonce2[NONCE_LEN];
174  unsigned char nonce3[NONCE_LEN];
175 
176  unsigned char mac[DIGEST_SIZE];
177  unsigned char mac_check[DIGEST_SIZE];
178 
179  /* ---------------
180  * Receive and check first msg: A->B
181  */
182  if (!receive_hmac(streamIn, nonce1, mac)) {
183  return false;
184  }
185  HMAC_REINIT(&context);
186  HMAC_UPDATE(&context, nonce1, NONCE_LEN);
187  HMAC_FINAL(&context, mac_check, DIGEST_SIZE);
188  if (!check_hmac(mac, mac_check)) {
189  return false;
190  }
191 
192  /* ---------------
193  * Send second msg: B->A
194  */
195  fill_nonce(nonce2);
196  HMAC_REINIT(&context);
197  HMAC_UPDATE(&context, nonce1, NONCE_LEN);
198  HMAC_UPDATE(&context, nonce2, NONCE_LEN);
199  HMAC_FINAL(&context, mac, DIGEST_SIZE);
200  if (!send_hmac(streamOut, nonce2, mac)) {
201  return false;
202  }
203 
204 
205  /* ---------------
206  * Receive and check third msg: A->B
207  */
208  if (!receive_hmac(streamIn, nonce3, mac)) {
209  return false;
210  }
211  HMAC_REINIT(&context);
212  HMAC_UPDATE(&context, nonce1, NONCE_LEN);
213  HMAC_UPDATE(&context, nonce2, NONCE_LEN);
214  HMAC_UPDATE(&context, nonce3, NONCE_LEN);
215  HMAC_FINAL(&context, mac_check, DIGEST_SIZE);
216  if (!check_hmac(mac, mac_check)) {
217  return false;
218  }
219  /* Authentication of A successful */
220 
221  return true;
222 }
223 
224 
225 bool AuthHMAC::send_hmac(OutputStream* stream, unsigned char* nonce, unsigned char* mac)
226 {
227  Bytes nonce_bytes(reinterpret_cast<char*>(nonce), NONCE_LEN);
228  Bytes mac_bytes(reinterpret_cast<char*>(mac), DIGEST_SIZE);
229  stream->write(nonce_bytes);
230  stream->write(mac_bytes);
231 
232  show_hmac_debug(nonce, NONCE_LEN, "send nonce ");
233  show_hmac_debug(mac, DIGEST_SIZE, "send digest ");
234 
235  return stream->isOk();
236 }
237 
238 bool AuthHMAC::receive_hmac(InputStream* stream, unsigned char* nonce, unsigned char* mac)
239 {
240  Bytes nonce_bytes(reinterpret_cast<char*>(nonce), NONCE_LEN);
241  Bytes mac_bytes(reinterpret_cast<char*>(mac), DIGEST_SIZE);
242  stream->read(nonce_bytes);
243  stream->read(mac_bytes);
244 
245  show_hmac_debug(nonce, NONCE_LEN, "got nonce ");
246  show_hmac_debug(mac, DIGEST_SIZE, "got digest ");
247 
248  return stream->isOk();
249 }
250 
251 bool AuthHMAC::check_hmac(unsigned char* mac, unsigned char* mac_check)
252 {
253  int cmp = memcmp(mac, mac_check, DIGEST_SIZE);
254 
255  std::string check = "digest check ";
256  if (cmp == 0) {
257  check += "successful";
258  } else {
259  check += "FAILED";
260  }
261  show_hmac_debug(mac_check, DIGEST_SIZE, check);
262 
263  return (cmp == 0);
264 }
265 
266 
267 void AuthHMAC::fill_nonce(unsigned char* nonce)
268 {
269  std::random_device rd;
270  std::mt19937 mt(rd());
271  std::uniform_int_distribution<int> dist(0, 255);
272  for (unsigned int i = 0; i < NONCE_LEN; i++) {
273  nonce[i] = static_cast<unsigned char>(dist(mt));
274  }
275 }
void show_hmac_debug(unsigned char *hex, unsigned int length, const std::string &context)
Definition: AuthHMAC.cpp:27
#define HMAC_FINAL
Definition: AuthHMAC.h:20
#define HMAC_INIT
Definition: AuthHMAC.h:17
#define HMAC_UPDATE
Definition: AuthHMAC.h:19
#define NONCE_LEN
Definition: AuthHMAC.h:21
#define DIGEST_SIZE
Definition: AuthHMAC.h:15
#define HMAC_REINIT
Definition: AuthHMAC.h:18
#define HMAC_CONTEXT
Definition: AuthHMAC.h:16
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
bool isNull() const override
Checks if the object is invalid.
Definition: Bottle.cpp:370
Value & find(const std::string &key) const override
Gets a value corresponding to a given keyword.
Definition: Bottle.cpp:287
A simple abstraction for a block of bytes.
Definition: Bytes.h:25
Simple specification of the minimum functions needed from input streams.
Definition: InputStream.h:26
virtual bool isOk() const =0
Check if the stream is ok or in an error state.
virtual int read()
Read and return a single byte.
Definition: InputStream.cpp:20
static void unlock()
Call post() on a global mutual-exclusion semaphore allocated by YARP.
Definition: Network.cpp:1464
static void lock()
Call wait() on a global mutual-exclusion semaphore allocated by YARP.
Definition: Network.cpp:1459
Simple specification of the minimum functions needed from output streams.
Definition: OutputStream.h:22
virtual bool isOk() const =0
Check if the stream is ok or in an error state.
virtual void write(char ch)
Write a single byte to the stream.
A class for storing options and configuration information.
Definition: Property.h:34
bool fromConfigFile(const std::string &fname, bool wipe=true)
Interprets a file as a list of properties.
Definition: Property.cpp:1098
Bottle & findGroup(const std::string &key) const override
Gets a list corresponding to a given keyword.
Definition: Property.cpp:1142
These options are loosely based on http://wiki.icub.org/wiki/YARP_ResourceFinder.
Helper class for finding config files and other external resources.
static ResourceFinder & getResourceFinderSingleton()
Access a ResourceFinder singleton whose lifetime will match that of the YARP library.
std::string findFile(const std::string &name)
Find the full path to a file.
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
AuthHMAC()
Constructor.
Definition: AuthHMAC.cpp:43
bool authDest(yarp::os::InputStream *streamIn, yarp::os::OutputStream *streamOut)
Definition: AuthHMAC.cpp:159
bool authSource(yarp::os::InputStream *streamIn, yarp::os::OutputStream *streamOut)
Definition: AuthHMAC.cpp:100
#define yCInfo(component,...)
Definition: LogComponent.h:132
#define yCWarning(component,...)
Definition: LogComponent.h:143
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:35
The components from which ports and connections are built.
An interface to the operating system, including Port based communication.