LogCabin
Core/Checksum.cc
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines