scclib
Stable Cloud Computing C++ Library
hash_digest.cc
Go to the documentation of this file.
1 /*
2 BSD 3-Clause License
3 
4 Copyright (c) 2022, Stable Cloud Computing, Inc.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9 1. Redistributions of source code must retain the above copyright notice, this
10  list of conditions and the following disclaimer.
11 
12 2. Redistributions in binary form must reproduce the above copyright notice,
13  this list of conditions and the following disclaimer in the documentation
14  and/or other materials provided with the distribution.
15 
16 3. Neither the name of the copyright holder nor the names of its
17  contributors may be used to endorse or promote products derived from
18  this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <crypto/hash.h>
32 #include <gtest/gtest.h>
33 #include <string>
34 #include <fstream>
35 #include <map>
36 #include <vector>
37 #include <util/iostream.h>
38 #include <util/rwloopbuf.h>
39 #include <encode/hex.h>
40 
53 using namespace std;
54 using namespace scc::encode;
55 using namespace scc::util;
56 using scc::crypto::Hash;
57 using scc::crypto::Hmac;
58 using CharVec = std::vector<char>;
59 
60 static string hash_test = "This is a test sentence to test both hashes and digests. It is a bit longer than the key!";
61 static string hash_test1 = "This is a test sentence to test both hashes and digests. ";
62 static string hash_test2 = "It is a bit longer than the key!";
63 static string hash_key = "This is a keyphrase";
64 static string hmac_sha256 = "dbe8548042b534bd99ddf26b5fc4c2cdfeaf07d8df5427f5794a4445a0c425b2";
65 
66 struct Hash_digest : public testing::Test
67 {
68  map<Hash::Algorithm, Hash> m_digest;
69  map<Hash::Algorithm, string> m_name;
70  map<Hash::Algorithm, string> m_init;
71  map<Hash::Algorithm, string> m_hash;
72  map<Hash::Algorithm, int> m_size;
73 
74  Hash_digest()
75  {
76  if (Hash::supported(Hash::md5_type))
77  {
78  m_digest.insert(std::make_pair(Hash::md5_type, Hash(Hash::md5_type)));
79  m_name[Hash::md5_type] = "md5";
80  m_init[Hash::md5_type] = "d41d8cd98f00b204e9800998ecf8427e";
81  m_hash[Hash::md5_type] = "6630d84e20c4f20a87fcf7e069a2d34e";
82  m_size[Hash::md5_type] = 16;
83  }
84  if (Hash::supported(Hash::sha1_type))
85  {
86  m_digest.insert(std::make_pair(Hash::sha1_type, Hash(Hash::sha1_type)));
87  m_name[Hash::sha1_type] = "sha1";
88  m_init[Hash::sha1_type] = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
89  m_hash[Hash::sha1_type] = "3acf7561f5bd97534e575ba2565c6400b9128412";
90  m_size[Hash::sha1_type] = 20;
91  }
92  if (Hash::supported(Hash::sha224_type))
93  {
94  m_digest.insert(std::make_pair(Hash::sha224_type, Hash(Hash::sha224_type)));
95  m_name[Hash::sha224_type] = "sha224";
96  m_init[Hash::sha224_type] = "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f";
97  m_hash[Hash::sha224_type] = "317746d2199f50c2bf450bf3412d9ff74645aab7c3b747f7779a7b28";
98  m_size[Hash::sha224_type] = 28;
99  }
100  if (Hash::supported(Hash::sha256_type))
101  {
102  m_digest.insert(std::make_pair(Hash::sha256_type, Hash(Hash::sha256_type)));
103  m_name[Hash::sha256_type] = "sha256";
104  m_init[Hash::sha256_type] = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
105  m_hash[Hash::sha256_type] = "382dfbf0cd153aec516de602ee6609ee73d97259cc78d74ea0caa9d5b02afab9";
106  m_size[Hash::sha256_type] = 32;
107  }
108  if (Hash::supported(Hash::sha384_type))
109  {
110  m_digest.insert(std::make_pair(Hash::sha384_type, Hash(Hash::sha384_type)));
111  m_name[Hash::sha384_type] = "sha384";
112  m_init[Hash::sha384_type] = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";
113  m_hash[Hash::sha384_type] = "3e7d844e6b9be37e4e8011dd258682c651ea151bf63897503e2ecffbcdfed3492d513028489be69ac3c3f9fb1649fc19";
114  m_size[Hash::sha384_type] = 48;
115  }
116  if (Hash::supported(Hash::sha512_type))
117  {
118  m_digest.insert(std::make_pair(Hash::sha512_type, Hash(Hash::sha512_type)));
119  m_name[Hash::sha512_type] = "sha512";
120  m_init[Hash::sha512_type] = "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e";
121  m_hash[Hash::sha512_type] = "a99f27da164286aea7f2c2928966dda6ce270b851536a6b8c4242f7e20131aa8260dd2239082cc02cc0f9fd0415e3bbe096868bbba7a59afb8a84188b2ce9cf5";
122  m_size[Hash::sha512_type] = 64;
123  }
124  if (Hash::supported(Hash::sha512_224_type))
125  {
126  m_digest.insert(std::make_pair(Hash::sha512_224_type, Hash(Hash::sha512_224_type)));
127  m_name[Hash::sha512_224_type] = "sha512/224";
128  m_init[Hash::sha512_224_type] = "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4";
129  m_hash[Hash::sha512_224_type] = "2ecce2ef45e929a959b3dd1ea8dbcc19ca644742d74a6d34ec654ac3";
130  m_size[Hash::sha512_224_type] = 28;
131  }
132  if (Hash::supported(Hash::sha512_256_type))
133  {
134  m_digest.insert(std::make_pair(Hash::sha512_256_type, Hash(Hash::sha512_256_type)));
135  m_name[Hash::sha512_256_type] = "sha512/256";
136  m_init[Hash::sha512_256_type] = "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a";
137  m_hash[Hash::sha512_256_type] = "2730a349582f660c6d0660bf7a09b4aa6a8b4a11bb1ab8306950ee93d7d9f258";
138  m_size[Hash::sha512_256_type] = 32;
139  }
140  if (Hash::supported(Hash::sm3_type))
141  {
142  m_digest.insert(std::make_pair(Hash::sm3_type, Hash(Hash::sm3_type)));
143  m_name[Hash::sm3_type] = "sm3";
144  m_init[Hash::sm3_type] = "1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b";
145  m_hash[Hash::sm3_type] = "a4d3f1d0bb8d34696688a434606b5eb3d78e8bcd98ae12621ab36dd0d6a8d9e7";
146  m_size[Hash::sm3_type] = 32;
147  }
148  }
149  virtual ~Hash_digest() {}
150 
151  string final(Hash& h)
152  {
153  CharVec v;
154  h.final(v);
155  return Hex::bin_to_hex(v.data(), v.size());
156  }
157 };
158 
159 TEST_F(Hash_digest, Lengths)
160 {
161  for (auto& d : m_digest)
162  {
163  cout << "Length " << m_name[d.first] << ": " << d.second.size() << endl;
164  ASSERT_EQ(d.second.size(), m_size[d.first]);
165  }
166 }
167 
168 TEST_F(Hash_digest, Init)
169 {
170  for (auto& d : m_digest)
171  {
172  string hex = final(d.second);
173  cout << "Final " << m_name[d.first] << ": " << hex << endl;
174  ASSERT_EQ(hex, m_init[d.first]);
175  }
176 }
177 
178 TEST_F(Hash_digest, Copy_final)
179 {
180  for (auto& d : m_digest)
181  {
182  d.second.update(hash_test);
183  char bin[m_size[d.first]];
184  d.second.final(bin, d.second.size());
185  string hex = Hex::bin_to_hex(CharVec(&bin[0], &bin[m_size[d.first]]));
186  cout << "Init " << m_name[d.first] << ": " << hex << endl;
187  ASSERT_EQ(hex, m_hash[d.first]);
188  }
189 }
190 
191 TEST_F(Hash_digest, Test_one_part)
192 {
193  for (auto& d : m_digest)
194  {
195  d.second.update(hash_test);
196  string hex = final(d.second);
197  ASSERT_EQ(hex, m_hash[d.first]);
198  }
199 }
200 
201 TEST_F(Hash_digest, Get_tag)
202 {
203  for (auto& d : m_digest)
204  {
205  d.second.update(hash_test);
206  CharVec fin;
207  d.second.final(fin);
208 
209  Hash h(d.first);
210  h.update(hash_test);
211 
212  CharVec t;
213  ASSERT_EQ(h.get_tag(t), m_size[d.first]);
214 
215  for (int i = 1; i <= m_size[d.first]; i++)
216  {
217  CharVec x(fin);
218  x.resize(i);
219  ASSERT_EQ(h.get_tag(t, i), i);
220  ASSERT_EQ(x, t);
221  }
222  }
223 }
224 
225 TEST_F(Hash_digest, Test_two_parts)
226 {
227  for (auto& d : m_digest)
228  {
229  d.second.update(hash_test1);
230  d.second.update(hash_test2);
231  string hex = final(d.second);
232  ASSERT_EQ(hex, m_hash[d.first]);
233  }
234 }
235 
236 TEST_F(Hash_digest, Reset_val)
237 {
238  for (auto& d : m_digest)
239  {
240  d.second.update(hash_test);
241  string hex = final(d.second);
242  ASSERT_EQ(hex, m_hash[d.first]);
243 
244  d.second.update(hash_test);
245  hex = final(d.second);
246  ASSERT_EQ(hex, m_hash[d.first]);
247  }
248 }
249 
250 
251 TEST_F(Hash_digest, Reset_cmd)
252 {
253  for (auto& d : m_digest)
254  {
255  d.second.update("bad data");
256  d.second.reset();
257  d.second.update(hash_test);
258  string hex = final(d.second);
259  ASSERT_EQ(hex, m_hash[d.first]);
260  }
261 }
262 
263 TEST_F(Hash_digest, Streams)
264 {
265  for (auto& d : m_digest)
266  {
267  RwLoopBuffer bufrw;
268  scc::crypto::HashReader rd(bufrw, d.second);
269  scc::crypto::HashWriter wr(bufrw, d.second);
270  IoStream ios(rd, wr);
271 
272  ios << hash_test1;
273  ios << hash_test2;
274  ios.flush();
275 
276  string hex = final(d.second);
277  ASSERT_EQ(hex, m_hash[d.first]);
278 
279  string s;
280  ASSERT_TRUE(std::getline(ios, s));
281  ASSERT_EQ(s, hash_test);
282 
283  hex = final(d.second);
284  ASSERT_EQ(hex, m_hash[d.first]);
285  }
286 }
287 
288 TEST_F(Hash_digest, Hmac_lengths)
289 {
290  for (auto& d : m_digest)
291  {
292  cout << "HMAC length " << m_name[d.first] << ": " << d.second.size() << endl;
293  Hmac h(hash_key, d.first);
294  ASSERT_EQ(h.size(), m_size[d.first]);
295  }
296 }
297 
298 TEST_F(Hash_digest, Hmac_compare) { // compare two hmacs using the same key
299  for (auto& d : m_digest)
300  {
301  Hmac h1(hash_key, d.first);
302  h1.update(hash_test);
303  char h1buf[h1.size()];
304  ASSERT_EQ(h1.final(h1buf, h1.size()), h1.size());
305  Hmac h2(hash_key, d.first);
306  h2.update(hash_test1);
307  h2.update(hash_test2);
308  char h2buf[h2.size()];
309  ASSERT_EQ(h2.final(h2buf, h2.size()), h2.size());
310  ASSERT_EQ(h1.size(), h2.size());
311  ASSERT_EQ(memcmp(h1buf, h2buf, h1.size()), 0);
312  }
313 }
314 
315 TEST(hmac_test, Hmac_sanity)
316 {
317  Hmac h(hash_key, Hash::sha256_type);
318  h.update(hash_test1);
319  h.update(hash_test2);
320  char hbuf[h.size()];
321  ASSERT_EQ(h.final(hbuf, h.size()), h.size());
322  string hex1 = Hex::bin_to_hex(CharVec(&hbuf[0], &hbuf[h.size()]));
323  ASSERT_EQ(hex1, hmac_sha256);
324  h.reset();
325  h.update(hash_test);
326  ASSERT_EQ(h.final(hbuf, h.size()), h.size());
327  string hex2 = Hex::bin_to_hex(CharVec(&hbuf[0], &hbuf[h.size()]));
328  ASSERT_EQ(hex2, hmac_sha256);
329 }
330 
331 TEST(hmac_test, Hmac_sanity_move)
332 {
333  Hmac h(hash_key, Hash::sha256_type);
334  h.update(hash_test);
335  Hmac h2 = std::move(h);
336  char hbuf[h2.size()];
337  ASSERT_EQ(h2.final(hbuf, h2.size()), h2.size());
338  string hex1 = Hex::bin_to_hex(CharVec(&hbuf[0], &hbuf[h2.size()]));
339  ASSERT_EQ(hex1, hmac_sha256);
340  ASSERT_THROW(h.final(hbuf, h.size()), std::runtime_error);
341 }
342 
343 TEST_F(Hash_digest, Hmac_reset)
344 {
345  for (auto& d : m_digest)
346  {
347  Hmac h(hash_key, d.first);
348  h.update(hash_test);
349  char hbuf[h.size()];
350  ASSERT_EQ(h.final(hbuf, h.size()), h.size());
351  string hex1 = Hex::bin_to_hex(CharVec(&hbuf[0], &hbuf[h.size()]));
352  h.reset();
353  h.update(hash_test1);
354  h.update(hash_test2);
355  ASSERT_EQ(h.final(hbuf, h.size()), h.size());
356  string hex2 = Hex::bin_to_hex(CharVec(&hbuf[0], &hbuf[h.size()]));
357  ASSERT_EQ(hex1, hex2);
358  }
359 }
360 
361 TEST_F(Hash_digest, hash_vector_validate)
362 {
363  CharVec test(hash_test.begin(), hash_test.end());
364  CharVec test1(hash_test1.begin(), hash_test1.end());
365  CharVec test2(hash_test2.begin(), hash_test2.end());
366  CharVec fin;
367 
368  for (auto& d : m_digest)
369  {
370  d.second.update(test);
371  d.second.final(fin);
372  string hex = Hex::bin_to_hex(fin);
373  ASSERT_EQ(hex, m_hash[d.first]);
374 
375  d.second.reset();
376  d.second.update(test1);
377  d.second.update(test2);
378  d.second.final(fin);
379  hex = Hex::bin_to_hex(fin);
380  ASSERT_EQ(hex, m_hash[d.first]);
381  }
382 }
383 
384 TEST_F(Hash_digest, hmac_vector_validate)
385 {
386  CharVec key(hash_key.begin(), hash_key.end());
387  CharVec test(hash_test.begin(), hash_test.end());
388  CharVec test1(hash_test1.begin(), hash_test1.end());
389  CharVec test2(hash_test2.begin(), hash_test2.end());
390  CharVec fin;
391 
392  for (auto& d : m_digest)
393  {
394  Hmac h(key, d.first);
395  h.update(test);
396  ASSERT_EQ(h.final(fin), h.size());
397  string hex1 = Hex::bin_to_hex(fin);
398  h.reset();
399  h.update(test1);
400  h.update(test2);
401  ASSERT_EQ(h.final(fin), h.size());
402  string hex2 = Hex::bin_to_hex(fin);
403  ASSERT_EQ(hex1, hex2);
404  }
405 }
406 
407 TEST_F(Hash_digest, hash_tag)
408 {
409  using charv = vector<char>;
410  charv d1, d2, d1d2;
411 
412  ifstream f("/dev/urandom");
413  d1.resize(1024);
414  f.read(d1.data(), 1024);
415  d2.resize(1024);
416  f.read(d2.data(), 1024);
417  d1d2 = d1;
418  d1d2.insert(d1d2.end(), d2.begin(), d2.end());
419 
420  for (auto& d : m_hash)
421  {
422  Hash h(d.first);
423 
424  charv inith;
425  h.final(inith);
426 
427  h.reset();
428  h.update(d1);
429  charv d1h;
430  h.final(d1h);
431 
432  h.reset();
433  h.update(d1d2);
434  charv d1d2h;
435  h.final(d1d2h);
436 
437  h.update(d1);
438  CharVec d1tag;
439  h.get_tag(d1tag);
440  ASSERT_EQ(d1h, d1tag);
441 
442  h.update(d2);
443  CharVec d1d2tag;
444  h.get_tag(d1d2tag);
445  ASSERT_EQ(d1d2h, d1d2tag);
446 
447  charv ver;
448  h.final(ver);
449  ASSERT_EQ(d1d2h, ver);
450 
451  CharVec inittag;
452  h.get_tag(inittag);
453  ASSERT_EQ(inittag, inith);
454  }
455 }
Helper class to hash an incoming stream.
Definition: hash.h:215
Helper class to hash an outgoing stream.
Definition: hash.h:239
General one-way hashing algorithms.
Definition: hash.h:88
Hmac, aka hash-based message authentication code.
Definition: hash.h:265
int final(void *, int)
Calculate the final hmac value.
int size() const
Hmac size.
Definition: hash.h:340
Input/output stream wrapper for reader/writer.
Definition: iostream.h:157
Loopback read/write stream buffer.
Definition: rwloopbuf.h:57
One-way hashing and message digests.
Binary to hex string converter.
Base input/output stream classes.
Loopback read/write buffer.
TEST(inet_example, client_server_stream_test)
[Inet client server]
Definition: inet.cc:521