NAWA  0.8
Web Application Framework for C++
Argon2HashingEngine.cpp
Go to the documentation of this file.
1 
6 /*
7  * Copyright (C) 2019-2021 Tobias Flaig.
8  *
9  * This file is part of nawa.
10  *
11  * nawa is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License,
13  * version 3, as published by the Free Software Foundation.
14  *
15  * nawa is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with nawa. If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include <argon2.h>
25 #include <cstring>
26 #include <nawa/Exception.h>
28 #include <nawa/util/encoding.h>
29 #include <random>
30 #include <regex>
31 
32 using namespace nawa;
33 using namespace std;
34 
35 struct hashing::Argon2HashingEngine::Data {
36  Algorithm algorithm;
37  uint32_t timeCost;
38  uint32_t memoryCost;
39  uint32_t parallelism;
40  string salt;
41  size_t hashLen;
43  Data(Algorithm algorithm, uint32_t timeCost, uint32_t memoryCost, uint32_t parallelism, string salt, size_t hashLen)
44  : algorithm(algorithm), timeCost(timeCost), memoryCost(memoryCost), parallelism(parallelism),
45  salt(move(salt)), hashLen(hashLen) {}
46 };
47 
48 NAWA_DEFAULT_DESTRUCTOR_IMPL_WITH_NS(hashing, Argon2HashingEngine)
49 
51  uint32_t timeCost, uint32_t memoryCost, uint32_t parallelism,
52  string salt, size_t hashLen) {
53  data = make_unique<Data>(algorithm, timeCost, memoryCost, parallelism, move(salt), hashLen);
54 }
55 
56 string hashing::Argon2HashingEngine::generateHash(string input) const {
57 
58  // check validity of parameters
59  if (!data->salt.empty() && data->salt.length() < ARGON2_MIN_SALT_LENGTH) {
60  throw Exception(__PRETTY_FUNCTION__, 10,
61  "Provided user-defined salt is not long enough");
62  }
63 
64  string actualSalt = data->salt;
65  if (data->salt.empty()) {
66  // generate random salt (16 bytes)
67  random_device rd;
68  stringstream sstr;
69  if (rd.entropy() == 32) {
70  uniform_int_distribution<uint32_t> distribution(0, 0xffffffff);
71  for (int i = 0; i < 4; ++i) {
72  uint32_t val = distribution(rd);
73  sstr << (char) (val & 0xff);
74  sstr << (char) ((val >> 8) & 0xff);
75  sstr << (char) ((val >> 16) & 0xff);
76  sstr << (char) ((val >> 24) & 0xff);
77  }
78  } else {
79  uniform_int_distribution<uint16_t> distribution(0, 0xffff);
80  for (int i = 0; i < 8; ++i) {
81  uint16_t val = distribution(rd);
82  sstr << (char) (val & 0xff);
83  sstr << (char) ((val >> 8) & 0xff);
84  }
85  }
86  actualSalt = sstr.str();
87  }
88 
89  int errorCode = 0;
90  size_t encodedHashCeil = 50 + (actualSalt.length() * 4) / 3 + (data->hashLen * 4) / 3;
91  char c_hash[encodedHashCeil];
92  memset(c_hash, '\0', encodedHashCeil);
93 
94  switch (data->algorithm) {
95  case Algorithm::ARGON2I:
96  errorCode = argon2i_hash_encoded(data->timeCost, data->memoryCost, data->parallelism,
97  (void*) input.c_str(), input.length(),
98  (void*) actualSalt.c_str(), actualSalt.length(), data->hashLen, c_hash,
99  encodedHashCeil);
100  break;
101  case Algorithm::ARGON2D:
102  errorCode = argon2d_hash_encoded(data->timeCost, data->memoryCost, data->parallelism,
103  (void*) input.c_str(), input.length(),
104  (void*) actualSalt.c_str(), actualSalt.length(), data->hashLen, c_hash,
105  encodedHashCeil);
106  break;
107  case Algorithm::ARGON2ID:
108  errorCode = argon2id_hash_encoded(data->timeCost, data->memoryCost, data->parallelism,
109  (void*) input.c_str(), input.length(),
110  (void*) actualSalt.c_str(), actualSalt.length(), data->hashLen, c_hash,
111  encodedHashCeil);
112  break;
113  }
114 
115  // error handling
116  if (errorCode != ARGON2_OK) {
117  throw Exception(__PRETTY_FUNCTION__, 10,
118  string("Argon2 error: ") + argon2_error_message(errorCode));
119  }
120 
121  return string(c_hash);
122 }
123 
124 bool hashing::Argon2HashingEngine::verifyHash(string input, string hash) const {
125 
126  // split the hash and create a new object with the properties of the hash
127  regex rgx(
128  R"(\$argon2(i|d|id)\$(v=([0-9]+))?\$m=([0-9]+),t=([0-9]+),p=([0-9]+)\$([A-Za-z0-9+\/]+={0,2})\$([A-Za-z0-9+\/]+={0,2}))");
129  smatch matches;
130  regex_match(hash, matches, rgx);
131  Algorithm algorithm1;
132  uint32_t version1;
133  uint32_t memoryCost1;
134  uint32_t timeCost1;
135  uint32_t parallelism1;
136  string salt1;
137  string hash1;
138  if (matches.size() == 9) {
139  if (matches[1] == "d")
140  algorithm1 = Algorithm::ARGON2D;
141  else if (matches[1] == "id")
142  algorithm1 = Algorithm::ARGON2ID;
143  else
144  algorithm1 = Algorithm::ARGON2I;
145  try {
146  version1 = stoul(matches[3]);
147  memoryCost1 = stoul(matches[4]);
148  timeCost1 = stoul(matches[5]);
149  parallelism1 = stoul(matches[6]);
150  salt1 = encoding::base64Decode(matches[7]);
151  hash1 = encoding::base64Decode(matches[8]);
152  } catch (...) {
153  return false;
154  }
155  } else {
156  return false;
157  }
158  if (version1 != 19) {
159  return false;
160  }
161 
162  auto engine1 = Argon2HashingEngine(algorithm1, timeCost1, memoryCost1, parallelism1, salt1, hash1.length());
163  string inputHash;
164  try {
165  inputHash = engine1.generateHash(input);
166  inputHash = encoding::base64Decode(inputHash.substr(inputHash.find_last_of('$') + 1));
167  } catch (Exception const&) {
168  return false;
169  }
170 
171  if (hash1.length() != inputHash.length())
172  return false;
173 
174  auto u1 = (const unsigned char*) hash1.c_str();
175  auto u2 = (const unsigned char*) inputHash.c_str();
176 
177  int ret = 0;
178  for (int i = 0; i < hash1.length(); ++i)
179  ret |= (u1[i] ^ u2[i]);
180 
181  return ret == 0;
182 }
Exception class that can be used by apps to catch errors resulting from nawa function calls.
std::string generateHash(std::string input) const override
bool verifyHash(std::string input, std::string hash) const override
Namespace containing functions for text encoding and decoding.
#define NAWA_DEFAULT_DESTRUCTOR_IMPL_WITH_NS(Namespace, Class)
Definition: macros.h:37
std::string base64Decode(std::string const &input)
Definition: encoding.cpp:286
Definition: AppInit.h:31