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