LogCabin
|
00001 /* Copyright (c) 2012 Stanford University 00002 * Copyright (c) 2015 Diego Ongaro 00003 * 00004 * Permission to use, copy, modify, and distribute this software for any 00005 * purpose with or without fee is hereby granted, provided that the above 00006 * copyright notice and this permission notice appear in all copies. 00007 * 00008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 00009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 00010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 00011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 00012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 00013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 00014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 00015 */ 00016 00017 #include <map> 00018 #include <memory> 00019 #include <mutex> 00020 #include <cstring> 00021 00022 #define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 00023 #include <cryptopp/cryptlib.h> 00024 #include <cryptopp/crc.h> 00025 #include <cryptopp/adler32.h> 00026 #include <cryptopp/md5.h> 00027 #include <cryptopp/sha.h> 00028 #include <cryptopp/whrlpool.h> 00029 #include <cryptopp/tiger.h> 00030 #include <cryptopp/ripemd.h> 00031 00032 #include "Core/Debug.h" 00033 #include "Core/Checksum.h" 00034 #include "Core/STLUtil.h" 00035 #include "Core/StringUtil.h" 00036 #include "Core/Util.h" 00037 00038 namespace LogCabin { 00039 namespace Core { 00040 namespace Checksum { 00041 00042 using Core::Util::downCast; 00043 using Core::StringUtil::format; 00044 00045 namespace { 00046 00047 /** 00048 * Helper for writeChecksum template, to keep code bloat to a minimum. 00049 */ 00050 uint32_t 00051 writeChecksumHelper( 00052 CryptoPP::HashTransformation& hashFn, 00053 const char* name, 00054 std::initializer_list<std::pair<const void*, uint64_t>> data, 00055 char result[MAX_LENGTH]) 00056 { 00057 // Length of name in bytes, not including null character. 00058 const uint32_t nameLength = downCast<uint32_t>(strlen(name)); 00059 // Size in bytes of binary hash function output. 00060 const uint32_t digestSize = hashFn.DigestSize(); 00061 // Size in bytes of name:hexdigest string, including null character. 00062 const uint32_t outputSize = (nameLength + 1 + 00063 digestSize * 2 + 1); 00064 assert(outputSize <= MAX_LENGTH); 00065 00066 // calculate binary digest 00067 uint8_t binary[digestSize]; 00068 for (auto it = data.begin(); it != data.end(); ++it) { 00069 hashFn.Update(static_cast<const uint8_t*>(it->first), 00070 it->second); 00071 } 00072 hashFn.Final(binary); 00073 00074 // copy name and : to result 00075 memcpy(result, name, nameLength); 00076 result += nameLength; 00077 *result = ':'; 00078 ++result; 00079 00080 // add hex digest to result 00081 const char* hexArray = "0123456789abcdef"; 00082 for (uint32_t i = 0; i < digestSize; ++i) { 00083 *result = hexArray[binary[i] >> 4]; 00084 ++result; 00085 *result = hexArray[binary[i] & 15]; 00086 ++result; 00087 } 00088 00089 // add null terminator to result and return total length 00090 *result = '\0'; 00091 return outputSize; 00092 } 00093 00094 /** 00095 * Template to produce functions of type Algorithm when instantiated with a 00096 * CryptoPP::HashTransformation. 00097 */ 00098 template<typename HashFn> 00099 uint32_t 00100 writeChecksum(std::initializer_list<std::pair<const void*, uint64_t>> data, 00101 char result[MAX_LENGTH]) 00102 { 00103 HashFn hashFn; 00104 return writeChecksumHelper(hashFn, 00105 HashFn::StaticAlgorithmName(), 00106 data, 00107 result); 00108 } 00109 00110 /** 00111 * Type for function that calculate the checksum for some data. 00112 * \param data 00113 * An list of (pointer, length) pairs describing what to checksum. 00114 * \param[out] result 00115 * The result of the hash function will be placed here. 00116 * This will be a null-terminated, printable C-string. 00117 * \return 00118 * The number of valid characters in 'output', including the null 00119 * terminator. This is guaranteed to be greater than 1. 00120 */ 00121 typedef uint32_t (*Algorithm)( 00122 std::initializer_list<std::pair<const void*, uint64_t>> data, 00123 char result[MAX_LENGTH]); 00124 00125 /** 00126 * A container for a set of Algorithm implementations. 00127 */ 00128 class Algorithms { 00129 /** 00130 * Helper for constructor(). 00131 */ 00132 template<typename ConcreteHashFunction> 00133 void registerAlgorithm() 00134 { 00135 // Using explicit types here to keep clang++-3.4 with libc++ happy. 00136 byName.insert(std::pair<std::string, Algorithm>( 00137 std::string(ConcreteHashFunction::StaticAlgorithmName()), 00138 Algorithm(writeChecksum<ConcreteHashFunction>))); 00139 } 00140 00141 00142 public: 00143 Algorithms() 00144 : byName() 00145 { 00146 registerAlgorithm<CryptoPP::CRC32>(); 00147 registerAlgorithm<CryptoPP::Adler32>(); 00148 registerAlgorithm<CryptoPP::Weak::MD5>(); 00149 registerAlgorithm<CryptoPP::SHA1>(); 00150 registerAlgorithm<CryptoPP::SHA224>(); 00151 registerAlgorithm<CryptoPP::SHA256>(); 00152 registerAlgorithm<CryptoPP::SHA384>(); 00153 registerAlgorithm<CryptoPP::SHA512>(); 00154 registerAlgorithm<CryptoPP::Whirlpool>(); 00155 registerAlgorithm<CryptoPP::Tiger>(); 00156 registerAlgorithm<CryptoPP::RIPEMD160>(); 00157 registerAlgorithm<CryptoPP::RIPEMD320>(); 00158 registerAlgorithm<CryptoPP::RIPEMD128>(); 00159 registerAlgorithm<CryptoPP::RIPEMD256>(); 00160 } 00161 00162 /** 00163 * Find an algorithm by name. 00164 * \param name 00165 * The short name of the algorithm. Must be null-terminated. 00166 * \return 00167 * The Algorithm if found, NULL otherwise. 00168 */ 00169 Algorithm find(const char* name) 00170 { 00171 // TODO(ongaro): This'll probably create a temporary std::string, which 00172 // might cause a memory allocation. But it's probably nothing to worry 00173 // about, and newer stdlib will hopefully implement a C++11-compliant 00174 // std::string type that contains a few characters without an 00175 // allocation. 00176 auto it = byName.find(name); 00177 if (it == byName.end()) 00178 return NULL; 00179 else 00180 return it->second; 00181 } 00182 00183 std::map<std::string, Algorithm> byName; 00184 } algorithms; 00185 00186 } // namespace LogCabin::Core::Checksum::<anonymous> 00187 00188 std::vector<std::string> 00189 listAlgorithms() 00190 { 00191 return Core::STLUtil::getKeys(algorithms.byName); 00192 } 00193 00194 uint32_t 00195 calculate(const char* algorithm, 00196 const void* data, uint64_t dataLength, 00197 char output[MAX_LENGTH]) 00198 { 00199 return calculate(algorithm, {{data, dataLength}}, output); 00200 } 00201 00202 uint32_t 00203 calculate(const char* algorithm, 00204 std::initializer_list<std::pair<const void*, uint64_t>> data, 00205 char output[MAX_LENGTH]) 00206 { 00207 Algorithm algo = algorithms.find(algorithm); 00208 if (algo == NULL) { 00209 PANIC("The hashing algorithm %s is not available", 00210 algorithm); 00211 } 00212 return (*algo)(data, output); 00213 } 00214 00215 00216 uint32_t 00217 length(const char* checksum, 00218 uint32_t maxChecksumLength) 00219 { 00220 uint32_t len = 0; 00221 while (true) { 00222 if (len == maxChecksumLength || len == MAX_LENGTH) 00223 return 0; 00224 char c = checksum[len]; 00225 ++len; 00226 if (c == '\0') 00227 return len; 00228 } 00229 } 00230 00231 std::string 00232 verify(const char* checksum, 00233 const void* data, uint64_t dataLength) 00234 { 00235 return verify(checksum, {{data, dataLength}}); 00236 } 00237 00238 std::string 00239 verify(const char* checksum, 00240 std::initializer_list<std::pair<const void*, uint64_t>> data) 00241 { 00242 if (!Core::StringUtil::isPrintable(checksum)) 00243 return "The given checksum value is corrupt and not printable."; 00244 00245 Algorithm algo = NULL; 00246 { // find algo 00247 char algorithmName[MAX_LENGTH]; 00248 char* colon = strchr(const_cast<char*>(checksum), ':'); 00249 if (colon == NULL) 00250 return format("Missing colon in checksum: %s", checksum); 00251 00252 // nameLength does not include the null terminator 00253 uint32_t nameLength = downCast<uint32_t>(colon - checksum); 00254 memcpy(algorithmName, checksum, nameLength); 00255 algorithmName[nameLength] = '\0'; 00256 00257 algo = algorithms.find(algorithmName); 00258 if (algo == NULL) 00259 return format("No such checksum algorithm: %s", 00260 algorithmName); 00261 } 00262 00263 // compare calculated checksum with the one given 00264 char calculated[MAX_LENGTH]; 00265 (*algo)(data, calculated); 00266 if (strcmp(calculated, checksum) != 0) { 00267 return format("Checksum doesn't match: expected %s " 00268 "but calculated %s", checksum, calculated); 00269 } 00270 00271 return std::string(); 00272 } 00273 00274 } // namespace LogCabin::Core::Checksum 00275 } // namespace LogCabin::Core 00276 } // namespace LogCabin