/* * @project The CERN Tape Archive (CTA) * @copyright Copyright © 2021-2022 CERN * @license This program is free software, distributed under the terms of the GNU General Public * Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can * redistribute it and/or modify it under the terms of the GPL Version 3, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * In applying this licence, CERN does not waive the privileges and immunities * granted to it by virtue of its status as an Intergovernmental Organization or * submit itself to any jurisdiction. */ #include #include "ChecksumBlob.hpp" #include "ChecksumBlobSerDeser.hpp" namespace cta::checksum { void ChecksumBlob::insert(ChecksumType type, const std::string &value) { // Validate the length of the checksum size_t expectedLength = 0; switch(type) { case NONE: expectedLength = 0; break; case ADLER32: case CRC32: case CRC32C: expectedLength = 4; break; case MD5: expectedLength = 16; break; case SHA1: expectedLength = 20; break; } if(value.length() > expectedLength) throw exception::ChecksumValueMismatch( "Checksum length type=" + ChecksumTypeName.at(type) + " expected=" + std::to_string(expectedLength) + " actual=" + std::to_string(value.length())); // Pad bytearray to expected length with trailing zeros m_cs[type] = value + std::string(expectedLength-value.length(), 0); } void ChecksumBlob::insert(ChecksumType type, uint32_t value) { // This method is only valid for 32-bit checksums std::string cs; switch(type) { case ADLER32: case CRC32: case CRC32C: for(int i = 0; i < 4; ++i) { cs.push_back(static_cast(value & 0xFF)); value >>= 8; } m_cs[type] = cs; break; default: throw exception::ChecksumTypeMismatch(ChecksumTypeName.at(type) + " is not a 32-bit checksum"); } } void ChecksumBlob::validate(ChecksumType type, const std::string &value) const { auto cs = m_cs.find(type); if(cs == m_cs.end()) throw exception::ChecksumTypeMismatch( "Checksum type " + ChecksumTypeName.at(type) + " not found"); if(cs->second != value) throw exception::ChecksumValueMismatch( "Checksum value expected=0x" + ByteArrayToHex(value) + " actual=0x" + ByteArrayToHex(cs->second)); } void ChecksumBlob::validate(const ChecksumBlob &blob) const { if(m_cs.size() != blob.m_cs.size()) { throw exception::ChecksumBlobSizeMismatch("Checksum blob size does not match. expected=" + std::to_string(m_cs.size()) + " actual=" + std::to_string(blob.m_cs.size())); } auto it1 = m_cs.begin(); auto it2 = blob.m_cs.begin(); for( ; it1 != m_cs.end(); ++it1, ++it2) { if(it1->first != it2->first) throw exception::ChecksumTypeMismatch( "Checksum type expected=" + ChecksumTypeName.at(it1->first) + " actual=" + ChecksumTypeName.at(it2->first)); if(it1->second != it2->second) throw exception::ChecksumValueMismatch( "Checksum value expected=0x" + ByteArrayToHex(it1->second) + " actual=0x" + ByteArrayToHex(it2->second)); } } std::string ChecksumBlob::serialize() const { common::ChecksumBlob p_csb; ChecksumBlobToProtobuf(*this, p_csb); std::string bytearray; p_csb.SerializeToString(&bytearray); return bytearray; } size_t ChecksumBlob::length() const { common::ChecksumBlob p_csb; ChecksumBlobToProtobuf(*this, p_csb); return p_csb.ByteSizeLong(); } void ChecksumBlob::deserialize(const std::string &bytearray) { common::ChecksumBlob p_csb; if(!p_csb.ParseFromString(bytearray)) { throw exception::Exception("ChecksumBlob: deserialization failed"); } ProtobufToChecksumBlob(p_csb, *this); } void ChecksumBlob::deserializeOrSetAdler32(const std::string &bytearray, uint32_t adler32) { common::ChecksumBlob p_csb; // A nullptr value in the CHECKSUM_BLOB column will return an empty bytearray. If the bytearray is empty // or otherwise invalid, default to using the contents of the CHECKSUM_ADLER32 column. if(!bytearray.empty() && p_csb.ParseFromString(bytearray)) { ProtobufToChecksumBlob(p_csb, *this); } else { insert(ADLER32, adler32); } } std::string ChecksumBlob::HexToByteArray(std::string hexString) { std::string bytearray; if(hexString.substr(0,2) == "0x" || hexString.substr(0,2) == "0X") { hexString.erase(0,2); } // ensure we have an even number of hex digits if(hexString.length() % 2 == 1) hexString.insert(0, "0"); for(unsigned int i = 0; i < hexString.length(); i += 2) { uint8_t byte = strtol(hexString.substr(i,2).c_str(), nullptr, 16); bytearray.insert(0,1,byte); } return bytearray; } std::string ChecksumBlob::ByteArrayToHex(const std::string &bytearray) { if(bytearray.empty()) return "0"; std::stringstream value; value << std::hex << std::setfill('0'); for(auto c = bytearray.rbegin(); c != bytearray.rend(); ++c) { value << std::setw(2) << (static_cast(*c) & 0xFF); } return value.str(); } void ChecksumBlob::addFirstChecksumToLog(cta::log::ScopedParamContainer &spc) const{ const auto & csItor = m_cs.begin(); if(csItor != m_cs.end()){ auto & cs = *csItor; std::string checksumTypeParam = "checksumType"; std::string checksumValueParam = "checksumValue"; spc.add(checksumTypeParam,ChecksumTypeName.at(cs.first)) .add(checksumValueParam,ByteArrayToHex(cs.second)); } } std::ostream &operator<<(std::ostream &os, const ChecksumBlob &csb) { os << "[ "; auto num_els = csb.m_cs.size(); for(auto &cs : csb.m_cs) { bool is_last_el = --num_els > 0; os << "{ \"" << ChecksumTypeName.at(cs.first) << "\",0x" << ChecksumBlob::ByteArrayToHex(cs.second) << (is_last_el ? " }," : " }"); } os << " ]"; return os; } } // namespace cta::checksum