The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
hash.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2008 - 2025
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
19
20#include <string>
21#include <sstream>
22#include <string.h>
23#include <assert.h>
24
25extern "C" {
27}
28
29#ifndef __APPLE__
30
31#include <openssl/evp.h>
32
33#else
34
35#include <CommonCrypto/CommonDigest.h>
36
37static_assert(utils::md5::DIGEST_SIZE == CC_MD5_DIGEST_LENGTH, "Constants mismatch");
38
39#endif
40
41namespace {
42
43const std::string hash_prefix = "$H$";
44
45template<std::size_t len>
46std::string encode_hash(const std::array<uint8_t, len>& bytes) {
47 return crypt64::encode(bytes);
48}
49
50template<std::size_t len>
51std::string hexencode_hash(const std::array<uint8_t, len>& input) {
52 std::ostringstream sout;
53 sout << std::hex;
54 for(uint8_t c : input) {
55 sout << static_cast<int>(c);
56 }
57 return sout.str();
58}
59
60}
61
62namespace utils {
63
64md5::md5(const std::string& input) {
65
66#ifndef __APPLE__
67 EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
68 unsigned int md5_digest_len = EVP_MD_size(EVP_md5());
69 assert(utils::md5::DIGEST_SIZE == md5_digest_len);
70
71 // MD5_Init
72 EVP_DigestInit_ex(mdctx, EVP_md5(), nullptr);
73
74 // MD5_Update
75 EVP_DigestUpdate(mdctx, input.c_str(), input.size());
76
77 // MD5_Final
78 EVP_DigestFinal_ex(mdctx, hash.data(), &md5_digest_len);
79 EVP_MD_CTX_free(mdctx);
80#else
81 CC_MD5(input.data(), static_cast<CC_LONG>(input.size()), hash.data());
82#endif
83
84}
85
86int md5::get_iteration_count(const std::string& hash) {
87 return crypt64::decode(hash[3]);
88}
89
90std::string md5::get_salt(const std::string& hash) {
91 return hash.substr(4,8);
92}
93
94bool md5::is_valid_prefix(const std::string& hash)
95{
96 return hash.substr(0,3) == hash_prefix;
97}
98
99bool md5::is_valid_hash(const std::string& hash) {
100 if(hash.size() != 34) return false;
101 if(!is_valid_prefix(hash)) return false;
102
103 const int iteration_count = get_iteration_count(hash);
104 if(iteration_count < 7 || iteration_count > 30) return false;
105
106 return true;
107}
108
109md5::md5(const std::string& password, const std::string& salt, int iteration_count)
110{
111 iteration_count = 1 << iteration_count;
112
113 hash = md5(salt + password).raw_digest();
114 do {
115 hash = md5(std::string(hash.begin(), hash.end()).append(password)).raw_digest();
116 } while(--iteration_count);
117}
118
119std::string md5::hex_digest() const
120{
121 return hexencode_hash<DIGEST_SIZE>(hash);
122}
123
124std::string md5::base64_digest() const
125{
126 return encode_hash<DIGEST_SIZE>(hash);
127}
128
129bcrypt::bcrypt(const std::string& input)
130{
131 assert(is_valid_prefix(input));
132
133 iteration_count_delim_pos = input.find('$', 4);
134 if(iteration_count_delim_pos == std::string::npos)
135 throw hash_error("hash string malformed");
136}
137
138bcrypt bcrypt::from_salted_salt(const std::string& input)
139{
140 bcrypt hash { input };
141 std::string bcrypt_salt = input.substr(0, hash.iteration_count_delim_pos + 23);
142 if(bcrypt_salt.size() >= BCRYPT_HASHSIZE)
143 throw hash_error("hash string too large");
144 strcpy(hash.hash.data(), bcrypt_salt.c_str());
145
146 return hash;
147}
148
149bcrypt bcrypt::from_hash_string(const std::string& input)
150{
151 bcrypt hash { input };
152 if(input.size() >= BCRYPT_HASHSIZE)
153 throw hash_error("hash string too large");
154 strcpy(hash.hash.data(), input.c_str());
155
156 return hash;
157}
158
159bcrypt bcrypt::hash_pw(const std::string& password, bcrypt& salt)
160{
161 bcrypt hash;
162 if(!php_crypt_blowfish_rn(password.c_str(), salt.hash.data(), hash.hash.data(), BCRYPT_HASHSIZE))
163 throw hash_error("failed to hash password");
164
165 return hash;
166}
167
168bool bcrypt::is_valid_prefix(const std::string& hash) {
169 return ((hash.compare(0, 4, "$2a$") == 0)
170 || (hash.compare(0, 4, "$2b$") == 0)
171 || (hash.compare(0, 4, "$2x$") == 0)
172 || (hash.compare(0, 4, "$2y$") == 0));
173}
174
175std::string bcrypt::get_salt() const
176{
177 std::size_t salt_pos = iteration_count_delim_pos + 23;
178 if(salt_pos >= BCRYPT_HASHSIZE)
179 throw hash_error("malformed hash");
180 return std::string(hash.data(), salt_pos);
181}
182
183std::string bcrypt::hex_digest() const
184{
185 return std::string(hash.data());
186}
187
188std::string bcrypt::base64_digest() const
189{
190 return std::string(hash.data());
191}
192
193} // namespace utils
std::size_t iteration_count_delim_pos
Definition hash.hpp:75
virtual std::string base64_digest() const override
Definition hash.cpp:188
static bool is_valid_prefix(const std::string &hash)
Definition hash.cpp:168
std::string get_salt() const
Definition hash.cpp:175
static bcrypt from_hash_string(const std::string &input)
Definition hash.cpp:149
virtual std::string hex_digest() const override
Definition hash.cpp:183
static bcrypt from_salted_salt(const std::string &input)
Definition hash.cpp:138
static bcrypt hash_pw(const std::string &password, bcrypt &salt)
Definition hash.cpp:159
static const unsigned int DIGEST_SIZE
Definition hash.hpp:48
std::array< T, sz > raw_digest() const
Definition hash.hpp:49
std::array< uint8_t, sz > hash
Definition hash.hpp:46
static bool is_valid_prefix(const std::string &hash)
Definition hash.cpp:94
virtual std::string base64_digest() const override
Definition hash.cpp:124
static std::string get_salt(const std::string &hash)
Definition hash.cpp:90
virtual std::string hex_digest() const override
Definition hash.cpp:119
static int get_iteration_count(const std::string &hash)
Definition hash.cpp:86
static bool is_valid_hash(const std::string &hash)
Definition hash.cpp:99
md5(const std::string &input)
Definition hash.cpp:64
char * php_crypt_blowfish_rn(const char *key, const char *setting, char *output, int size)
static const int BCRYPT_HASHSIZE
Definition hash.hpp:25
std::string encode(utils::byte_view bytes)
Definition base64.cpp:233
std::vector< uint8_t > decode(std::string_view in)
Definition base64.cpp:229
#define c