The Battle for Wesnoth  1.17.0-dev
hash.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
3  by Thomas Baumhauer <thomas.baumhauer@NOSPAMgmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "hash.hpp"
17 
18 #include "serialization/base64.hpp"
19 
20 #include <iostream>
21 #include <string>
22 #include <sstream>
23 #include <string.h>
24 #include <assert.h>
25 
26 extern "C" {
28 }
29 
30 #ifndef __APPLE__
31 
32 #include <openssl/sha.h>
33 #include <openssl/md5.h>
34 
35 static_assert(utils::md5::DIGEST_SIZE == MD5_DIGEST_LENGTH, "Constants mismatch");
36 static_assert(utils::sha1::DIGEST_SIZE == SHA_DIGEST_LENGTH, "Constants mismatch");
37 
38 #else
39 
40 #include <CommonCrypto/CommonDigest.h>
41 
42 static_assert(utils::md5::DIGEST_SIZE == CC_MD5_DIGEST_LENGTH, "Constants mismatch");
43 static_assert(utils::sha1::DIGEST_SIZE == CC_SHA1_DIGEST_LENGTH, "Constants mismatch");
44 
45 #endif
46 
47 namespace {
48 
49 const std::string hash_prefix = "$H$";
50 
51 template<std::size_t len>
52 std::string encode_hash(const std::array<uint8_t, len>& bytes) {
53  utils::byte_string_view view{bytes.data(), len};
54  return crypt64::encode(view);
55 }
56 
57 template<std::size_t len>
58 std::string hexencode_hash(const std::array<uint8_t, len>& input) {
59  std::ostringstream sout;
60  sout << std::hex;
61  for(uint8_t c : input) {
62  sout << static_cast<int>(c);
63  }
64  return sout.str();
65 }
66 
67 }
68 
69 namespace utils {
70 
71 md5::md5(const std::string& input) {
72 
73 #ifndef __APPLE__
74  MD5_CTX md5_worker;
75  MD5_Init(&md5_worker);
76  MD5_Update(&md5_worker, input.data(), input.size());
77  MD5_Final(hash.data(), &md5_worker);
78 #else
79  CC_MD5(input.data(), static_cast<CC_LONG>(input.size()), hash.data());
80 #endif
81 
82 }
83 
84 int md5::get_iteration_count(const std::string& hash) {
85  return crypt64::decode(hash[3]);
86 }
87 
88 std::string md5::get_salt(const std::string& hash) {
89  return hash.substr(4,8);
90 }
91 
92 bool md5::is_valid_prefix(const std::string& hash)
93 {
94  return hash.substr(0,3) == hash_prefix;
95 }
96 
97 bool md5::is_valid_hash(const std::string& hash) {
98  if(hash.size() != 34) return false;
99  if(!is_valid_prefix(hash)) return false;
100 
101  const int iteration_count = get_iteration_count(hash);
102  if(iteration_count < 7 || iteration_count > 30) return false;
103 
104  return true;
105 }
106 
107 md5::md5(const std::string& password, const std::string& salt, int iteration_count)
108 {
109  iteration_count = 1 << iteration_count;
110 
111  hash = md5(salt + password).raw_digest();
112  do {
113  hash = md5(std::string(hash.begin(), hash.end()).append(password)).raw_digest();
114  } while(--iteration_count);
115 }
116 
117 std::string md5::hex_digest() const
118 {
119  return hexencode_hash<DIGEST_SIZE>(hash);
120 }
121 
122 std::string md5::base64_digest() const
123 {
124  return encode_hash<DIGEST_SIZE>(hash);
125 }
126 
127 sha1::sha1(const std::string& str)
128 {
129 #ifndef __APPLE__
130  SHA_CTX hasher;
131  SHA1_Init(&hasher);
132  SHA1_Update(&hasher, str.data(), str.size());
133  SHA1_Final(hash.data(), &hasher);
134 #else
135  CC_MD5(str.data(), static_cast<CC_LONG>(str.size()), hash.data());
136 #endif
137 }
138 
139 std::string sha1::hex_digest() const
140 {
141  return hexencode_hash<DIGEST_SIZE>(hash);
142 }
143 
144 std::string sha1::base64_digest() const
145 {
146  return encode_hash<DIGEST_SIZE>(hash);
147 }
148 
149 bcrypt::bcrypt(const std::string& input)
150 {
151  assert(is_valid_prefix(input));
152 
153  iteration_count_delim_pos = input.find('$', 4);
154  if(iteration_count_delim_pos == std::string::npos)
155  throw hash_error("hash string malformed");
156 }
157 
158 bcrypt bcrypt::from_salted_salt(const std::string& input)
159 {
160  bcrypt hash { input };
161  std::string bcrypt_salt = input.substr(0, hash.iteration_count_delim_pos + 23);
162  if(bcrypt_salt.size() >= BCRYPT_HASHSIZE)
163  throw hash_error("hash string too large");
164  strcpy(hash.hash.data(), bcrypt_salt.c_str());
165 
166  return hash;
167 }
168 
169 bcrypt bcrypt::from_hash_string(const std::string& input)
170 {
171  bcrypt hash { input };
172  if(input.size() >= BCRYPT_HASHSIZE)
173  throw hash_error("hash string too large");
174  strcpy(hash.hash.data(), input.c_str());
175 
176  return hash;
177 }
178 
179 bcrypt bcrypt::hash_pw(const std::string& password, bcrypt& salt)
180 {
181  bcrypt hash;
182  if(!php_crypt_blowfish_rn(password.c_str(), salt.hash.data(), hash.hash.data(), BCRYPT_HASHSIZE))
183  throw hash_error("failed to hash password");
184 
185  return hash;
186 }
187 
188 bool bcrypt::is_valid_prefix(const std::string& hash) {
189  return ((hash.compare(0, 4, "$2a$") == 0)
190  || (hash.compare(0, 4, "$2b$") == 0)
191  || (hash.compare(0, 4, "$2x$") == 0)
192  || (hash.compare(0, 4, "$2y$") == 0));
193 }
194 
195 std::string bcrypt::get_salt() const
196 {
197  std::size_t salt_pos = iteration_count_delim_pos + 23;
198  if(salt_pos >= BCRYPT_HASHSIZE)
199  throw hash_error("malformed hash");
200  return std::string(hash.data(), salt_pos);
201 }
202 
203 std::string bcrypt::hex_digest() const
204 {
205  return std::string(hash.data());
206 }
207 
208 std::string bcrypt::base64_digest() const
209 {
210  return std::string(hash.data());
211 }
212 
213 } // namespace utils
std::array< T, sz > raw_digest() const
Definition: hash.hpp:49
std::array< T, sz > hash
Definition: hash.hpp:46
static const int DIGEST_SIZE
Definition: hash.hpp:48
std::string encode(utils::byte_string_view bytes)
Definition: base64.cpp:230
static const int BCRYPT_HASHSIZE
Definition: hash.hpp:25
std::basic_string_view< uint8_t > byte_string_view
Definition: base64.hpp:23
std::vector< uint8_t > decode(std::string_view in)
Definition: base64.cpp:226
std::string password(const std::string &server, const std::string &login)
char * php_crypt_blowfish_rn(const char *key, const char *setting, char *output, int size)
mock_char c