diff --git a/cdoc/CDoc.cpp b/cdoc/CDoc.cpp index 97e393b..0be43f8 100644 --- a/cdoc/CDoc.cpp +++ b/cdoc/CDoc.cpp @@ -103,7 +103,7 @@ libcdoc::CDocReader::createReader(DataSource *src, bool take_ownership, Configur int version = getCDocFileVersion(src); LOG_DBG("CDocReader::createReader: version {}", version); if (src->seek(0) != libcdoc::OK) return nullptr; - CDocReader *reader; + CDocReader *reader = nullptr; if (version == 1) { reader = new CDoc1Reader(src, take_ownership); } else if (version == 2) { @@ -111,12 +111,16 @@ libcdoc::CDocReader::createReader(DataSource *src, bool take_ownership, Configur } else { if(take_ownership) delete src; - return nullptr; - } - reader->conf = conf; - reader->crypto = crypto; + return nullptr; + } + if (!reader->getLastErrorStr().empty()) { + delete reader; + return nullptr; + } + reader->conf = conf; + reader->crypto = crypto; reader->network = network; - return reader; + return reader; } libcdoc::CDocReader * @@ -125,43 +129,14 @@ libcdoc::CDocReader::createReader(const std::string& path, Configuration *conf, if(path.empty()) return nullptr; auto isrc = make_unique(path); - int version = getCDocFileVersion(isrc.get()); - LOG_DBG("CDocReader::createReader: version {}", version); - if (isrc->seek(0) != libcdoc::OK) - return nullptr; - CDocReader *reader; - if (version == 1) { - reader = new CDoc1Reader(isrc.release(), true); - } else if (version == 2) { - reader = new CDoc2Reader(isrc.release(), true); - } else { - return nullptr; - } - reader->conf = conf; - reader->crypto = crypto; - reader->network = network; - return reader; + return createReader(isrc.release(), true, conf, crypto, network); } libcdoc::CDocReader * libcdoc::CDocReader::createReader(std::istream& ifs, Configuration *conf, CryptoBackend *crypto, NetworkBackend *network) { - libcdoc::IStreamSource *isrc = new libcdoc::IStreamSource(&ifs, false); - int version = getCDocFileVersion(isrc); - LOG_DBG("CDocReader::createReader: version {}", version); - CDocReader *reader; - if (version == 1) { - reader = new CDoc1Reader(isrc, true); - } else if (version == 2) { - reader = new CDoc2Reader(isrc, true); - } else { - delete isrc; - return nullptr; - } - reader->conf = conf; - reader->crypto = crypto; - reader->network = network; - return reader; + auto isrc = make_unique(&ifs, false); + return createReader(isrc.release(), true, conf, crypto, network); } #if LIBCDOC_TESTING diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 1389c74..6b83dbc 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -101,6 +101,8 @@ struct CDoc2Reader::Private { dec.reset(); return rv; } + + static void buildLock(Lock& lock, const cdoc20::header::RecipientRecord& recipient); }; CDoc2Reader::~CDoc2Reader() @@ -213,7 +215,7 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) } } else { std::vector kek_pm; - int result = crypto->deriveHMACExtract(kek_pm, key_material, std::vector(libcdoc::CDoc2::KEKPREMASTER.cbegin(), libcdoc::CDoc2::KEKPREMASTER.cend()), lock_idx); + int result = crypto->deriveHMACExtract(kek_pm, key_material, toUint8Vector(libcdoc::CDoc2::KEKPREMASTER), lock_idx); if (result < 0) { setLastError(crypto->getLastErrorStr(result)); LOG_ERROR("{}", last_error); @@ -409,7 +411,7 @@ CDoc2Reader::beginDecryption(const std::vector& fmk) LOG_TRACE_KEY("cek: {}", cek); priv->dec = std::make_unique(*priv->_src, EVP_chacha20_poly1305(), cek, libcdoc::CDoc2::NONCE_LEN); - std::vector aad(libcdoc::CDoc2::PAYLOAD.cbegin(), libcdoc::CDoc2::PAYLOAD.cend()); + std::vector aad = toUint8Vector(libcdoc::CDoc2::PAYLOAD); aad.insert(aad.end(), priv->header_data.cbegin(), priv->header_data.cend()); aad.insert(aad.end(), priv->headerHMAC.cbegin(), priv->headerHMAC.cend()); if(auto rv = priv->dec->updateAAD(aad); rv != OK) { @@ -487,11 +489,132 @@ CDoc2Reader::finishDecryption() return rv; } +void +CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecord& recipient) +{ + using namespace cdoc20::recipients; + using namespace cdoc20::header; + + lock.label = recipient.key_label()->str(); + lock.encrypted_fmk = toUint8Vector(recipient.encrypted_fmk()); + + if(recipient.fmk_encryption_method() != cdoc20::header::FMKEncryptionMethod::XOR) + { + LOG_WARN("Unsupported FMK encryption method"); + return; + } + switch(recipient.capsule_type()) + { + case Capsule::recipients_ECCPublicKeyCapsule: + if(const auto *key = recipient.capsule_as_recipients_ECCPublicKeyCapsule()) { + if(key->curve() == EllipticCurve::secp384r1) { + lock.type = Lock::Type::PUBLIC_KEY; + lock.pk_type = Lock::PKType::ECC; + lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(key->recipient_public_key())); + lock.setBytes(Lock::Params::KEY_MATERIAL, toUint8Vector(key->sender_public_key())); + LOG_DBG("Load PK: {}", toHex(lock.getBytes(Lock::Params::RCPT_KEY))); + } else { + LOG_ERROR("Unsupported ECC curve: skipping"); + } + } + return; + case Capsule::recipients_RSAPublicKeyCapsule: + if(const auto *key = recipient.capsule_as_recipients_RSAPublicKeyCapsule()) + { + lock.type = Lock::Type::PUBLIC_KEY; + lock.pk_type = Lock::PKType::RSA; + lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(key->recipient_public_key())); + lock.setBytes(Lock::Params::KEY_MATERIAL, toUint8Vector(key->encrypted_kek())); + } + return; + case Capsule::recipients_KeyServerCapsule: + if (const KeyServerCapsule *server = recipient.capsule_as_recipients_KeyServerCapsule()) { + KeyDetailsUnion details = server->recipient_key_details_type(); + switch (details) { + case KeyDetailsUnion::EccKeyDetails: + if(const EccKeyDetails *eccDetails = server->recipient_key_details_as_EccKeyDetails()) { + if(eccDetails->curve() != EllipticCurve::secp384r1) { + LOG_ERROR("Unsupported elliptic curve key type"); + return; + } + lock.pk_type = Lock::PKType::ECC; + lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(eccDetails->recipient_public_key())); + } + break; + case KeyDetailsUnion::RsaKeyDetails: + if(const RsaKeyDetails *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) { + lock.pk_type = Lock::PKType::RSA; + lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(rsaDetails->recipient_public_key())); + } + break; + default: + LOG_ERROR("Unsupported Key Server Details"); + return; + } + lock.type = Lock::Type::SERVER; + lock.setString(Lock::Params::KEYSERVER_ID, server->keyserver_id()->str()); + lock.setString(Lock::Params::TRANSACTION_ID, server->transaction_id()->str()); + } + return; + case Capsule::recipients_SymmetricKeyCapsule: + if(const auto *capsule = recipient.capsule_as_recipients_SymmetricKeyCapsule()) + { + lock.type = Lock::SYMMETRIC_KEY; + lock.setBytes(Lock::SALT, toUint8Vector(capsule->salt())); + } + return; + case Capsule::recipients_PBKDF2Capsule: + if(const auto *capsule = recipient.capsule_as_recipients_PBKDF2Capsule()) { + KDFAlgorithmIdentifier kdf_id = capsule->kdf_algorithm_identifier(); + if (kdf_id != KDFAlgorithmIdentifier::PBKDF2WithHmacSHA256) { + LOG_ERROR("Unsupported KDF algorithm: skipping"); + return; + } + lock.type = Lock::PASSWORD; + lock.setBytes(Lock::SALT, toUint8Vector(capsule->salt())); + lock.setBytes(Lock::PW_SALT, toUint8Vector(capsule->password_salt())); + lock.setInt(Lock::KDF_ITER, capsule->kdf_iterations()); + } + return; + case Capsule::recipients_KeySharesCapsule: + if (const auto *capsule = recipient.capsule_as_recipients_KeySharesCapsule()) { + if (capsule->recipient_type() != cdoc20::recipients::KeyShareRecipientType::SID_MID) { + LOG_ERROR("Invalid keyshare recipient type: {}", (int) capsule->recipient_type()); + return; + } + if (capsule->shares_scheme() != cdoc20::recipients::SharesScheme::N_OF_N) { + LOG_ERROR("Invalid keyshare scheme type: {}", (int) capsule->shares_scheme()); + return; + } + /* url,share_id;url,share_id... */ + std::vector strs; + for (auto cshare : *capsule->shares()) { + std::string id = cshare->share_id()->str(); + std::string url = cshare->server_base_url()->str(); + std::string str = url + "," + id; + LOG_DBG("Keyshare: {}", str); + strs.push_back(std::move(str)); + } + std::string urls = join(strs, ";"); + LOG_DBG("Keyshare urls: {}", urls); + std::vector salt = toUint8Vector(capsule->salt()); + LOG_DBG("Keyshare salt: {}", toHex(salt)); + std::string recipient_id = capsule->recipient_id()->str(); + LOG_DBG("Keyshare recipient id: {}", recipient_id); + lock.type = Lock::SHARE_SERVER; + lock.setString(Lock::SHARE_URLS, urls); + lock.setBytes(Lock::SALT, salt); + lock.setString(Lock::RECIPIENT_ID, recipient_id); + } + return; + default: + LOG_ERROR("Unsupported capsule type"); + } +} + CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) : CDocReader(2), priv(std::make_unique(src, take_ownership)) { - - using namespace cdoc20::recipients; using namespace cdoc20::header; setLastError(t_("Invalid CDoc 2.0 header")); @@ -555,140 +678,7 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) setLastError({}); for(const auto *recipient: *recipients){ - if(recipient->fmk_encryption_method() != FMKEncryptionMethod::XOR) - { - LOG_WARN("Unsupported FMK encryption method: skipping"); - continue; - } - auto fillRecipientPK = [&recipient,&locks = priv->locks] (Lock::PKType pk_type, auto key) -> Lock& { - Lock &k = locks.emplace_back(Lock::Type::PUBLIC_KEY); - k.pk_type = pk_type; - k.setBytes(Lock::Params::RCPT_KEY, std::vector(key->recipient_public_key()->cbegin(), key->recipient_public_key()->cend())); - k.label = recipient->key_label()->str(); - k.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - return k; - }; - switch(recipient->capsule_type()) - { - case Capsule::recipients_ECCPublicKeyCapsule: - if(const auto *key = recipient->capsule_as_recipients_ECCPublicKeyCapsule()) { - if(key->curve() != EllipticCurve::secp384r1) { - LOG_ERROR("Unsupported ECC curve: skipping"); - continue; - } - Lock &k = fillRecipientPK(Lock::PKType::ECC, key); - k.setBytes(Lock::Params::KEY_MATERIAL, std::vector(key->sender_public_key()->cbegin(), key->sender_public_key()->cend())); - LOG_DBG("Load PK: {}", toHex(k.getBytes(Lock::Params::RCPT_KEY))); - } - break; - case Capsule::recipients_RSAPublicKeyCapsule: - if(const auto *key = recipient->capsule_as_recipients_RSAPublicKeyCapsule()) - { - Lock &k = fillRecipientPK(Lock::PKType::RSA, key); - k.setBytes(Lock::Params::KEY_MATERIAL, std::vector(key->encrypted_kek()->cbegin(), key->encrypted_kek()->cend())); - } - break; - case Capsule::recipients_KeyServerCapsule: - if (const KeyServerCapsule *server = recipient->capsule_as_recipients_KeyServerCapsule()) { - KeyDetailsUnion details = server->recipient_key_details_type(); - Lock ckey; - switch (details) { - case KeyDetailsUnion::EccKeyDetails: - if(const EccKeyDetails *eccDetails = server->recipient_key_details_as_EccKeyDetails()) { - if(eccDetails->curve() == EllipticCurve::secp384r1) { - ckey.type = Lock::Type::SERVER; - ckey.pk_type = Lock::PKType::ECC; - ckey.setBytes(Lock::Params::RCPT_KEY, std::vector(eccDetails->recipient_public_key()->cbegin(), eccDetails->recipient_public_key()->cend())); - } else { - LOG_ERROR("Unsupported elliptic curve key type"); - } - } else { - LOG_ERROR("Invalid file format"); - } - break; - case KeyDetailsUnion::RsaKeyDetails: - if(const RsaKeyDetails *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) { - ckey.type = Lock::Type::SERVER; - ckey.pk_type = Lock::PKType::RSA; - ckey.setBytes(Lock::Params::RCPT_KEY, std::vector(rsaDetails->recipient_public_key()->cbegin(), rsaDetails->recipient_public_key()->cend())); - } else { - LOG_ERROR("Invalid file format"); - } - break; - default: - LOG_ERROR("Unsupported Key Server Details: skipping"); - } - if (ckey.type != Lock::Type::INVALID) { - ckey.label = recipient->key_label()->c_str(); - ckey.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - ckey.setString(Lock::Params::KEYSERVER_ID, server->keyserver_id()->str()); - ckey.setString(Lock::Params::TRANSACTION_ID, server->transaction_id()->str()); - priv->locks.push_back(std::move(ckey)); - } - } else { - LOG_ERROR("Invalid file format"); - } - break; - case Capsule::recipients_SymmetricKeyCapsule: - if(const auto *capsule = recipient->capsule_as_recipients_SymmetricKeyCapsule()) - { - Lock &key = priv->locks.emplace_back(Lock::SYMMETRIC_KEY); - key.label = recipient->key_label()->str(); - key.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - key.setBytes(Lock::SALT, std::vector(capsule->salt()->cbegin(), capsule->salt()->cend())); - } - break; - case Capsule::recipients_PBKDF2Capsule: - if(const auto *capsule = recipient->capsule_as_recipients_PBKDF2Capsule()) { - KDFAlgorithmIdentifier kdf_id = capsule->kdf_algorithm_identifier(); - if (kdf_id != KDFAlgorithmIdentifier::PBKDF2WithHmacSHA256) { - LOG_ERROR("Unsupported KDF algorithm: skipping"); - continue; - } - Lock &key = priv->locks.emplace_back(Lock::PASSWORD); - key.label = recipient->key_label()->str(); - key.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - key.setBytes(Lock::SALT, std::vector(capsule->salt()->cbegin(), capsule->salt()->cend())); - key.setBytes(Lock::PW_SALT, std::vector(capsule->password_salt()->cbegin(), capsule->password_salt()->cend())); - key.setInt(Lock::KDF_ITER, capsule->kdf_iterations()); - } - break; - case Capsule::recipients_KeySharesCapsule: - if (const auto *capsule = recipient->capsule_as_recipients_KeySharesCapsule()) { - if (capsule->recipient_type() != cdoc20::recipients::KeyShareRecipientType::SID_MID) { - LOG_ERROR("Invalid keyshare recipient type: {}", (int) capsule->recipient_type()); - continue; - } - if (capsule->shares_scheme() != cdoc20::recipients::SharesScheme::N_OF_N) { - LOG_ERROR("Invalid keyshare scheme type: {}", (int) capsule->shares_scheme()); - continue; - } - /* url,share_id;url,share_id... */ - std::vector strs; - for (auto cshare = capsule->shares()->cbegin(); cshare != capsule->shares()->cend(); ++cshare) { - std::string id = cshare->share_id()->str(); - std::string url = cshare->server_base_url()->str(); - std::string str = url + "," + id; - LOG_DBG("Keyshare: {}", str); - strs.push_back(std::move(str)); - } - std::string urls = join(strs, ";"); - LOG_DBG("Keyshare urls: {}", urls); - std::vector salt(capsule->salt()->cbegin(), capsule->salt()->cend()); - LOG_DBG("Keyshare salt: {}", toHex(salt)); - std::string recipient_id = capsule->recipient_id()->str(); - LOG_DBG("Keyshare recipient id: {}", recipient_id); - Lock &lock = priv->locks.emplace_back(Lock::SHARE_SERVER); - lock.label = recipient->key_label()->str(); - lock.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - lock.setString(Lock::SHARE_URLS, urls); - lock.setBytes(Lock::SALT, salt); - lock.setString(Lock::RECIPIENT_ID, recipient_id); - } - break; - default: - LOG_ERROR("Unsupported Key Details: skipping"); - } + Private::buildLock(priv->locks.emplace_back(), *recipient); } } diff --git a/cdoc/Crypto.cpp b/cdoc/Crypto.cpp index 81ac0c3..9f902ba 100644 --- a/cdoc/Crypto.cpp +++ b/cdoc/Crypto.cpp @@ -621,6 +621,6 @@ result_t DecryptionSource::close() int len = 0; std::vector buffer(EVP_CIPHER_CTX_block_size(ctx.get()), 0); if (SSL_FAILED(EVP_CipherFinal_ex(ctx.get(), buffer.data(), &len), "EVP_CipherFinal_ex")) - return error = CRYPTO_ERROR; + return error = HASH_MISMATCH; return OK; } diff --git a/cdoc/CryptoBackend.h b/cdoc/CryptoBackend.h index c1013c0..56e6447 100644 --- a/cdoc/CryptoBackend.h +++ b/cdoc/CryptoBackend.h @@ -36,8 +36,8 @@ struct Lock; * - decryptRSA for RSA keys * - getSecret for symmetric keys. * - * ECC and symmetric keys have also frontend methods; implementing these allows the program to perform certain cryptographic procedures in controlled - * environment and (in case of symmetric keys) avoid exposing secret keys/passwords. + * ECC and symmetric keys have also frontend methods; implementing these allows the program to perform certain cryptographic procedures in secure + * environment and (in case of symmetric keys) avoid exposing secret keys/passwords to library code. */ struct CDOC_EXPORT CryptoBackend { static constexpr int INVALID_PARAMS = -201; diff --git a/cdoc/Io.h b/cdoc/Io.h index 4590210..dfc0419 100644 --- a/cdoc/Io.h +++ b/cdoc/Io.h @@ -259,8 +259,6 @@ struct CDOC_EXPORT IStreamSource : public DataSource { if(_ifs->bad()) return INPUT_STREAM_ERROR; _ifs->clear(); _ifs->seekg(pos); - //std::cerr << "Stream bad:" << _ifs->bad() << " eof:" << _ifs->eof() << " fail:" << _ifs->fail() << std::endl; - //std::cerr << "tell:" << _ifs->tellg() << std::endl; return bool(_ifs->bad()) ? INPUT_STREAM_ERROR : OK; } diff --git a/cdoc/Lock.h b/cdoc/Lock.h index 99d5f72..3011f87 100644 --- a/cdoc/Lock.h +++ b/cdoc/Lock.h @@ -44,9 +44,10 @@ struct CDOC_EXPORT Lock */ enum Type : unsigned char { /** - * @brief Invalid value + * @brief Valid capsule but not supported by this library version + * */ - INVALID, + UNKNOWN, /** * @brief Symmetric AES key */ @@ -175,7 +176,7 @@ struct CDOC_EXPORT Lock /** * @brief The lock type */ - Type type = Type::INVALID; + Type type = Type::UNKNOWN; /** * @brief algorithm type for public key based locks */ @@ -194,7 +195,7 @@ struct CDOC_EXPORT Lock * @brief check whether lock is valid * @return true if valid */ - bool isValid() const noexcept { return (type != Type::INVALID) && !label.empty() && !encrypted_fmk.empty(); } + bool isValid() const noexcept { return (type != Type::UNKNOWN) && !label.empty() && !encrypted_fmk.empty(); } /** * @brief check whether lock is based on symmetric key * @return true if type is SYMMETRIC_KEY or PASSWORD diff --git a/cdoc/NetworkBackend.cpp b/cdoc/NetworkBackend.cpp index 40163de..3fb52d0 100644 --- a/cdoc/NetworkBackend.cpp +++ b/cdoc/NetworkBackend.cpp @@ -397,8 +397,7 @@ libcdoc::NetworkBackend::fetchKey (std::vector& dst, const std::string& } error = {}; std::string ks = v.get(); - std::vector key_material = fromBase64(ks); - dst.assign(key_material.cbegin(), key_material.cend()); + dst = fromBase64(ks); return libcdoc::OK; } @@ -434,7 +433,7 @@ libcdoc::NetworkBackend::fetchNonce(std::vector& dst, const std::string return NETWORK_ERROR; } std::string nonce_str = v.get(); - dst.assign(nonce_str.cbegin(), nonce_str.cend()); + dst = toUint8Vector(nonce_str); return OK; } diff --git a/cdoc/Utils.h b/cdoc/Utils.h index a028389..a98e4c8 100644 --- a/cdoc/Utils.h +++ b/cdoc/Utils.h @@ -125,6 +125,16 @@ struct urlEncode { friend std::ostream& operator<<(std::ostream& escaped, urlEncode src); }; +std::vector toUint8Vector(const auto* data) +{ + return {data->cbegin(), data->cend()}; +} + +std::vector toUint8Vector(const auto& data) +{ + return {data.cbegin(), data.cend()}; +} + std::string urlDecode(const std::string &src); } // namespace libcdoc diff --git a/test/libcdoc_boost.cpp b/test/libcdoc_boost.cpp index 3b48536..1401dc3 100644 --- a/test/libcdoc_boost.cpp +++ b/test/libcdoc_boost.cpp @@ -489,7 +489,7 @@ BOOST_FIXTURE_TEST_CASE_WITH_DECOR(CDoc2DecryptErrors, DecryptFixture, BOOST_TEST(rdr->beginDecryption(fmk) == libcdoc::OK); BOOST_TEST(rdr->nextFile(fi) == libcdoc::OK); BOOST_TEST(rdr->nextFile(fi) == libcdoc::OK); - BOOST_TEST(rdr->finishDecryption() == libcdoc::CRYPTO_ERROR); + BOOST_TEST(rdr->finishDecryption() == libcdoc::HASH_MISMATCH); delete rdr; // Truncate file, should result zlib error @@ -499,15 +499,15 @@ BOOST_FIXTURE_TEST_CASE_WITH_DECOR(CDoc2DecryptErrors, DecryptFixture, BOOST_TEST(rdr->getFMK(fmk, 0) == libcdoc::OK); BOOST_TEST(rdr->beginDecryption(fmk) == libcdoc::OK); libcdoc::result_t rv = rdr->nextFile(fi); - BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::CRYPTO_ERROR))); + BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::HASH_MISMATCH))); for (int i = 0; i < 4; i++) { rv = rdr->readData(buf, 256); - BOOST_TEST(((rv == 256) || (rv == libcdoc::CRYPTO_ERROR))); + BOOST_TEST(((rv == 256) || (rv == libcdoc::HASH_MISMATCH))); } rv = rdr->nextFile(fi); - BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::CRYPTO_ERROR))); + BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::HASH_MISMATCH))); rv = rdr->readData(buf, 256); - BOOST_TEST(((rv == 255) || (rv == libcdoc::CRYPTO_ERROR))); + BOOST_TEST(((rv == 255) || (rv == libcdoc::HASH_MISMATCH))); BOOST_TEST(rdr->finishDecryption() == libcdoc::WORKFLOW_ERROR); delete rdr; }