1 /* 2 Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License, version 2.0, 6 as published by the Free Software Foundation. 7 8 This program is also distributed with certain software (including 9 but not limited to OpenSSL) that is licensed under separate terms, 10 as designated in a particular file or component or in included license 11 documentation. The authors of MySQL hereby grant you an additional 12 permission to link the program and your derivative works with the 13 separately licensed software that they have included with MySQL. 14 15 This program 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 General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 #ifndef ROUTER_KDF_SHA_CRYPT_INCLUDED 25 #define ROUTER_KDF_SHA_CRYPT_INCLUDED 26 27 #include <string> 28 #include <utility> // std::pair 29 #include <vector> 30 31 #include "digest.h" 32 #include "mcf_error.h" 33 #include "mysqlrouter/http_auth_backend_lib_export.h" 34 35 /** 36 * sha256_crypt and sha512_crypt are SHA based crypt() key derivation functions. 37 * @see https://www.akkadia.org/drepper/SHA-crypt.txt 38 * 39 * caching_sha2_password is key derivation function taken from an internal 40 * MySQL authentication mechanism 41 */ 42 class HTTP_AUTH_BACKEND_LIB_EXPORT ShaCrypt { 43 public: 44 enum class Type { Sha256, Sha512, CachingSha2Password }; 45 static std::string salt(); 46 static std::string derive(Type digest, unsigned long rounds, 47 const std::string &salt, 48 const std::string &password); 49 50 private: 51 /** 52 * crypt specific base64 encode. 53 * 54 * different alphabet than RFC4648 55 */ 56 static std::string base64_encode(const std::vector<uint8_t> &data); 57 }; 58 59 class ShaCryptMcfType { 60 public: 61 using Type = ShaCrypt::Type; 62 name(Type type)63 static std::pair<bool, std::string> name(Type type) noexcept { 64 switch (type) { 65 case Type::Sha256: 66 return std::make_pair(true, kTypeSha256); 67 case Type::Sha512: 68 return std::make_pair(true, kTypeSha512); 69 case Type::CachingSha2Password: 70 return std::make_pair(true, kTypeCachingSha2Password); 71 } 72 73 return std::make_pair(false, std::string{}); 74 } 75 type(const std::string & name)76 static std::pair<bool, Type> type(const std::string &name) noexcept { 77 if (name == kTypeSha256) { 78 return std::make_pair(true, Type::Sha256); 79 } else if (name == kTypeSha512) { 80 return std::make_pair(true, Type::Sha512); 81 } else if (name == kTypeCachingSha2Password) { 82 return std::make_pair(true, Type::CachingSha2Password); 83 } 84 85 return std::make_pair(false, Type{}); 86 } 87 supports_name(const std::string & name)88 static bool supports_name(const std::string &name) noexcept { 89 if (name == kTypeSha256) { 90 return true; 91 } else if (name == kTypeSha512) { 92 return true; 93 } else if (name == kTypeCachingSha2Password) { 94 return true; 95 } 96 97 return false; 98 } 99 100 private: 101 static constexpr char kTypeSha256[] = "5"; 102 static constexpr char kTypeSha512[] = "6"; 103 static constexpr char kTypeCachingSha2Password[] = "A"; 104 }; 105 106 /** 107 * MCF reader/writer for ShaCrypt 108 */ 109 class HTTP_AUTH_BACKEND_LIB_EXPORT ShaCryptMcfAdaptor { 110 public: 111 using mcf_type = ShaCryptMcfType; 112 using kdf_type = ShaCrypt; 113 114 using Type = mcf_type::Type; 115 116 /** 117 * number of rounds if no rounds was specified in from_mcf(). 118 */ 119 static constexpr unsigned long kDefaultRounds = 5000; 120 /** 121 * minimum rounds. 122 */ 123 static constexpr unsigned long kMinRounds = 1000; 124 /** 125 * maximum rounds. 126 */ 127 static constexpr unsigned long kMaxRounds = 999999999; 128 /** 129 * maximum length of the salt. 130 * 131 * only the first kMaxSaltLength bytes of the salt will be used. 132 */ 133 static constexpr size_t kMaxSaltLength = 16; 134 ShaCryptMcfAdaptor(Type digest,unsigned long rounds,const std::string & salt,const std::string & checksum)135 ShaCryptMcfAdaptor(Type digest, unsigned long rounds, const std::string &salt, 136 const std::string &checksum) 137 : digest_{digest}, rounds_{rounds}, salt_{salt}, checksum_{checksum} { 138 // limit salt, for caching_sha2_password salt has a fixed length of 20 139 if (digest != Type::CachingSha2Password && salt_.size() > kMaxSaltLength) { 140 salt_.resize(kMaxSaltLength); 141 } 142 143 // limit rounds to allowed range 144 if (rounds_ < kMinRounds) rounds_ = kMinRounds; 145 if (rounds_ > kMaxRounds) rounds_ = kMaxRounds; 146 } 147 148 /** 149 * name of the digest according to MCF. 150 * 151 * - 5 for SHA256 152 * - 6 for SHA512 153 * - A for caching_sha2_password 154 */ mcf_digest_name()155 std::string mcf_digest_name() const { 156 auto r = mcf_type::name(digest()); 157 if (r.first) return r.second; 158 159 throw std::invalid_argument("failed to map digest to a name"); 160 } 161 162 /** 163 * checkum. 164 * 165 * in crypt-specific base64 encoding 166 */ checksum()167 std::string checksum() const { return checksum_; } 168 169 /** 170 * salt. 171 * 172 * @pre must be [a-z0-9]* 173 */ salt()174 std::string salt() const { return salt_; } 175 176 /** 177 * 178 */ digest()179 Type digest() const { return digest_; } 180 181 /** 182 * rounds. 183 * 184 * number of rounds the hash will be applied 185 */ rounds()186 unsigned long rounds() const { return rounds_; } 187 188 /** 189 * build ShaCrypt from a MCF notation. 190 * 191 * - ${prefix}$rounds={rounds}${salt}${checksum} 192 * - ${prefix}$rounds={rounds}${salt} 193 * - ${prefix}${salt}${checksum} 194 * - ${prefix}${salt} 195 * 196 * prefix 197 * : [56] (5 is SHA256, 6 is SHA512) 198 * 199 * rounds 200 * : [0-9]+ 201 * 202 * salt 203 * : [^$]* 204 * 205 * checksum 206 * : [./a-zA-Z0-0]* 207 */ 208 static ShaCryptMcfAdaptor from_mcf(const std::string &data); 209 210 /** 211 * encode to MCF. 212 * 213 * MCF (Modular Crypt Format) 214 */ 215 std::string to_mcf() const; 216 217 /** 218 * hash a password into checksum. 219 * 220 * updates checksum 221 */ hash(const std::string & password)222 void hash(const std::string &password) { 223 checksum_ = kdf_type::derive(digest_, rounds_, salt_, password); 224 } 225 supports_mcf_id(const std::string mcf_id)226 static bool supports_mcf_id(const std::string mcf_id) { 227 return mcf_type::supports_name(mcf_id); 228 } 229 validate(const std::string & mcf_line,const std::string & password)230 static std::error_code validate(const std::string &mcf_line, 231 const std::string &password) { 232 try { 233 auto me = from_mcf(mcf_line); 234 if (kdf_type::derive(me.digest(), me.rounds(), me.salt(), password) == 235 me.checksum()) { 236 return {}; 237 } else { 238 return make_error_code(McfErrc::kPasswordNotMatched); 239 } 240 } catch (const std::exception &) { 241 // treat all exceptions as parse-errors 242 return make_error_code(McfErrc::kParseError); 243 } 244 } 245 246 private: 247 Type digest_; 248 unsigned long rounds_; 249 std::string salt_; 250 std::string checksum_; 251 }; 252 253 class CachingSha2Adaptor : public ShaCryptMcfAdaptor { 254 public: 255 static ShaCryptMcfAdaptor from_mcf(const std::string &crypt_data); 256 257 static constexpr unsigned long kCachingSha2SaltLength = 20; 258 }; 259 260 #endif 261