diff --git a/src/SiVaContainer.cpp b/src/SiVaContainer.cpp index 759b23a12..9108f56bb 100644 --- a/src/SiVaContainer.cpp +++ b/src/SiVaContainer.cpp @@ -29,6 +29,8 @@ #include "DataFile_p.h" #include "XMLDocument.h" #include "crypto/Connect.h" +#include "crypto/TS.h" +#include "util/DateTime.h" #include "util/File.h" #include "json.hpp" @@ -41,47 +43,6 @@ using namespace digidoc::util; using namespace std; using json = nlohmann::json; -template -constexpr T base64_enc_size(T n) noexcept -{ - return ((n + 2) / 3) << 2; -} - -static auto base64_decode(string_view data) { - static constexpr array T{ - 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, - 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, - 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x3E, 0x64, 0x64, 0x64, 0x3F, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, - 0x64, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, - 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x64, 0x64, 0x64, 0x64, 0x64, - 0x64, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x64, 0x64, 0x64, 0x64, 0x64 - }; - - string buf; - buf.reserve(base64_enc_size(data.size())); - auto out = make_unique(std::move(buf)); - int value = 0; - int bits = -8; - for(auto c: data) - { - if(c == '\r' || c == '\n' || c == ' ' || static_cast(c) > 128) - continue; - uint8_t check = T[c]; - if(check == 0x64) - break; - value = (value << 6) + check; - if(bits += 6; bits < 0) - continue; - out->put(char((value >> bits) & 0xFF)); - bits -= 8; - } - return out; -} - - - class SiVaContainer::Private { public: @@ -197,7 +158,7 @@ SiVaContainer::SiVaContainer(const string &path, ContainerOpenCB *cb, bool useHa break; size_t pos = b64.size(); - b64.resize(b64.size() + base64_enc_size(buf.size())); + b64.resize(b64.size() + ((buf.size() + 2) / 3) * 4); int size = EVP_EncodeBlock((unsigned char*)&b64[pos], buf.data(), int(is->gcount())); b64.resize(pos + size); } @@ -255,6 +216,11 @@ SiVaContainer::SiVaContainer(const string &path, ContainerOpenCB *cb, bool useHa s->_postalCode = signatureProductionPlace.value("postalCode", {}); s->_country = signatureProductionPlace.value("countryName", {}); } + for(const json &tsa: info.value("archiveTimeStamps", {})) + { + TS ts(from_base64(tsa.value("content", {}))); + s->_archiveTimeStamps.push_back({ts.cert(), util::date::to_string(ts.time())}); + } } for(const json &certificate: signature.value("certificates", {})) { @@ -265,8 +231,8 @@ SiVaContainer::SiVaContainer(const string &path, ContainerOpenCB *cb, bool useHa s->_ocspCertificate = X509Cert(der, X509Cert::Der); if(certificate["type"] == "SIGNATURE_TIMESTAMP") s->_tsCertificate = X509Cert(der, X509Cert::Der); - if(certificate["type"] == "ARCHIVE_TIMESTAMP") - s->_tsaCertificate = X509Cert(der, X509Cert::Der); + if(certificate["type"] == "ARCHIVE_TIMESTAMP" && s->_archiveTimeStamps.empty()) + s->_archiveTimeStamps.push_back({X509Cert(der, X509Cert::Der), {}}); } for(const json &error: signature.value("errors", {})) { @@ -353,7 +319,7 @@ unique_ptr SiVaContainer::parseDDoc(const unique_ptr &ddoc, bo THROW("Currently supports only content types EMBEDDED_BASE64 for DDOC format"); if(contentType != "EMBEDDED_BASE64") continue; - d->dataFiles.push_back(new DataFilePrivate(base64_decode(dataFile), + d->dataFiles.push_back(new DataFilePrivate(make_unique(from_base64(dataFile)), string(dataFile["Filename"]), string(dataFile["MimeType"]), string(dataFile["Id"]))); diff --git a/src/SiVaContainer.h b/src/SiVaContainer.h index 935d938e6..ad7b9f4eb 100644 --- a/src/SiVaContainer.h +++ b/src/SiVaContainer.h @@ -59,7 +59,7 @@ class SignatureSiVa final: public Signature std::string TimeStampTime() const final { return _tsTime; } //TSA profile properties - X509Cert ArchiveTimeStampCertificate() const final { return _tsaCertificate; } + std::vector ArchiveTimeStamps() const final { return _archiveTimeStamps; } // Other std::vector messageImprint() const final { return _messageImprint; } @@ -68,7 +68,8 @@ class SignatureSiVa final: public Signature SignatureSiVa() = default; DISABLE_COPY(SignatureSiVa); - X509Cert _signingCertificate, _ocspCertificate, _tsCertificate, _tsaCertificate; + X509Cert _signingCertificate, _ocspCertificate, _tsCertificate; + std::vector _archiveTimeStamps; std::string _id, _profile, _signedBy, _signatureMethod, _signingTime, _indication, _subIndication, _signatureLevel; std::string _bestTime, _tsTime, _ocspTime; std::string _city, _stateOrProvince, _postalCode, _country; diff --git a/src/XMLDocument.h b/src/XMLDocument.h index 36e8c280c..280cbf1b7 100644 --- a/src/XMLDocument.h +++ b/src/XMLDocument.h @@ -32,40 +32,45 @@ #include #include -#include - +#include #include namespace digidoc { #define VERSION_CHECK(major, minor, patch) (((major)<<16)|((minor)<<8)|(patch)) -static std::vector from_base64(std::string_view data) +template> +static constexpr R from_base64(std::string_view data) { - static constexpr std::string_view whitespace {" \n\r\f\t\v"}; - std::vector result(EVP_DECODE_LENGTH(data.size()), 0); - size_t dataPos = 0; - int size = 0; - auto ctx = make_unique_ptr(EVP_ENCODE_CTX_new()); - EVP_DecodeInit(ctx.get()); - - for(auto pos = data.find_first_of(whitespace); - !data.empty(); - pos = data.find_first_of(whitespace), dataPos += size_t(size)) - { - auto sub = data.substr(0, pos); - if(pos == std::string_view::npos) - data = {}; - else - data.remove_prefix(pos + 1); - if(EVP_DecodeUpdate(ctx.get(), &result[dataPos], &size, (const unsigned char*)sub.data(), int(sub.size())) == -1) - THROW("Invalid Base64 Binary"); - } - - if(EVP_DecodeFinal(ctx.get(), &result[dataPos], &size) == 1) - result.resize(dataPos + size_t(size)); - else - result.clear(); + constexpr auto T = std::to_array({ + 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x3E, 0x64, 0x64, 0x64, 0x3F, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x64, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x64, 0x64, 0x64, 0x64, 0x64, + 0x64, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x64, 0x64, 0x64, 0x64, 0x64 + }); + R result(data.size() * 3 / 4 + 2, 0); + auto it = result.begin(); + uint32_t value = 0; + int bits = -8; + for(auto c: data) + { + constexpr std::string_view whitespace{" \t\n\r\f\v"}; + if(static_cast(c) > 127 || whitespace.contains(c)) + continue; + uint8_t check = T[static_cast(c)]; + if(check == 0x64) + break; + value = (value << 6) + check; + if(bits += 6; bits < 0) + continue; + *it++ = typename R::value_type((value >> bits) & 0xFF); + bits -= 8; + } + result.erase(it, result.end()); return result; }