Changeset View
Changeset View
Standalone View
Standalone View
src/tests/crypto/key-export-test.cpp
- This file was added.
| /* | |||||
| * This file is part of libkazv. | |||||
| * SPDX-FileCopyrightText: 2025 tusooa <tusooa@kazv.moe> | |||||
| * SPDX-License-Identifier: AGPL-3.0-or-later | |||||
| */ | |||||
| #include <libkazv-config.hpp> | |||||
| #include <catch2/catch_test_macros.hpp> | |||||
| #include <key-export.hpp> | |||||
| using namespace Kazv; | |||||
| static const std::string password = "test"; | |||||
| static const std::string salt = "\x5e\x91\xe3\x75\xcc\x7b\xaf\x04\x91\xfe\xe3\x07\x89\x58\x1c\xb8"; | |||||
| static const std::size_t iterations = 1000000; | |||||
| static const ByteArray iv(16, 0); | |||||
| static const ByteArray expEncryptionKey = {5, 60, 69, 180, 233, 146, 45, 161, 158, 115, 248, 156, 6, 242, 138, 45, 103, 156, 226, 255, 176, 37, 30, 182, 138, 0, 240, 157, 217, 116, 141, 128}; | |||||
| static const ByteArray expHmacKey = {182, 206, 253, 28, 253, 171, 93, 193, 200, 217, 248, 152, 204, 174, 180, 159, 155, 28, 176, 208, 169, 99, 170, 121, 180, 4, 129, 245, 19, 47, 75, 182}; | |||||
| static const std::string backupFile = R"(-----BEGIN MEGOLM SESSION DATA----- | |||||
| AV6R43XMe68Ekf7jB4lYHLgAAAAAAAAAAAAAAAAAAAAAAA9CQG628kByLP7LApTtbvhnpgFnUUJ | |||||
| +tRMkpw4zcGoTOJya9/lawfRWKjd8LZeuHKdNLkEhfIAE16Xmqv+uU8oEASPxjLDOMjsgBKLMRx | |||||
| /iwUR7Aoe4wjuwEcdEEOW+T6ffjUz5LmEJcI14qZ1wXUPk1pnNmz+4nX8+a9UxgEpAN7vsmilwz | |||||
| P4PXNubhvGsqtZpy44pP6Td0alYgwVfTXqWB1KokMjuQE+2q6/Jb6U/z5D5nv8ArcJL04cD0U6r | |||||
| ySsRWI9Jra2OcKFQxgLeVpRAiP6/sRyl9k1n6eiSOfmGkZ+qnvOfZsQh7Wupgh6zRe8LNEtrYZh | |||||
| FpSaCE+0U8I5hZJrWNBDFfHg+rtzB4BEk0YwpD3rVcWEsk8kKqHmEulEqIXckd1SbSG7y7H1ADB | |||||
| 7mjAY7qWetMizPXD+I8MDUnU1TF3Jv3CIZfZY7BHh2WukmiORlpN4H5s/Wwq2oCIk7qXhCHFvaF | |||||
| uj+XytIz6TmkEVZfXK9zqUCwCU+VYSGl9GVAezO8CZ6aEJes95yYqRxfADdJG2Vtd0oXwrpR0xV | |||||
| 1GO+0JJ3xKicVX6U77iMtJbL1Lge32QvbAcv8o6mcaW28xeeYPrccMIRa3vLtuSDDqKC79S9bIP | |||||
| 2U5F+MHn+5dqMeXcG9K2hS91gsBQMAvX6 | |||||
| -----END MEGOLM SESSION DATA-----)"; | |||||
| static const std::string nonJsonFile = R"(-----BEGIN MEGOLM SESSION DATA----- | |||||
| AV6R43XMe68Ekf7jB4lYHLgAAAAAAAAAAAAAAAAAAAAAAA9CQE628kByLP7LApTtbvhnpgFnUUJ | |||||
| +tRMkpw4zcGoTOJya9/lawfRWKjd8LZeuHKdNLkEhfIAE16Xmqv+uU8oEASPxjLDOMjsgBKLMRx | |||||
| /iwUR7Aoe4wjuwEcdEEOW+T6ffjUz5LmEJcI14qZ1wXUPk1pnNmz+4nX8+a9UxgEpAN7vsmilwz | |||||
| P4PXNubhvGsqtZpy44pP6Td0alYgwVfTXqWB1KokMjuQE+2q6/Jb6U/z5D5nv8ArcJL04cD0U6r | |||||
| ySsRWI9Jra2OcKFQxgLeVpRAiP6/sRyl9k1n6eiSOfmGkZ+qnvOfZsQh7Wupgh6zRe8LNEtrYZh | |||||
| FpSaCE+0U8I5hZJrWNBDFfHg+rtzB4BEk0YwpD3rVcWEsk8kKqHmEulEqIXckd1SbSG7y7H1ADB | |||||
| 7mjAY7qWetMizPXD+I8MDUnU1TF3Jv3CIZfZY7BHh2WukmiORlpN4H5s/Wwq2oCIk7qXhCHFvaF | |||||
| uj+XytIz6TmkEVZfXK9zqUCwCU+VYSGl9GVAezO8CZ6aEJes95yYqRxfADdJG2Vtd0oXwrpR0xV | |||||
| 1GO+0JJ3xKicVX6U77iMtJbL1Lge32QvbAcv8o6mcaW28xeeYPrccMIRa3vLtuSDDrgHHUiZDSE | |||||
| kuZ4FJ70pzD12mQoT/0L5yVCaKP8ibtRd | |||||
| -----END MEGOLM SESSION DATA-----)"; | |||||
| static const nlohmann::json expectedJson = R"([{ | |||||
| "algorithm": "m.megolm.v1.aes-sha2", | |||||
| "forwarding_curve25519_key_chain": [ | |||||
| "hPQNcabIABgGnx3/ACv/jmMmiQHoeFfuLB17tzWp6Hw" | |||||
| ], | |||||
| "room_id": "!Cuyf34gef24t:localhost", | |||||
| "sender_claimed_keys": { | |||||
| "ed25519": "aj40p+aw64yPIdsxoog8jhPu9i7l7NcFRecuOQblE3Y" | |||||
| }, | |||||
| "sender_key": "RF3s+E7RkTQTGF2d8Deol0FkQvgII2aJDf3/Jp5mxVU", | |||||
| "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ", | |||||
| "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8Llf..." | |||||
| }])"_json; | |||||
| TEST_CASE("deriveKeyExportKey", "[crypto][key-export]") | |||||
| { | |||||
| auto [encryptionKey, hmacKey] = deriveKeyExportKey(password, ByteArray(salt.begin(), salt.end()), iterations); | |||||
| REQUIRE(encryptionKey == expEncryptionKey); | |||||
| REQUIRE(hmacKey == expHmacKey); | |||||
| } | |||||
| TEST_CASE("decryptKeyExport", "[crypto][key-export]") | |||||
| { | |||||
| auto res = decryptKeyExport(backupFile, password); | |||||
| REQUIRE(res.has_value()); | |||||
| REQUIRE(res.value() == expectedJson); | |||||
| } | |||||
| TEST_CASE("decryptKeyExport error handling", "[crypto][key-export]") | |||||
| { | |||||
| WHEN("no header") | |||||
| { | |||||
| auto res = decryptKeyExport(backupFile.substr(1), password); | |||||
| REQUIRE(!res.has_value()); | |||||
| REQUIRE(res.reason() == DecryptKeyExportErrorCodes::FILE_MALFORMED); | |||||
| } | |||||
| WHEN("no footer") | |||||
| { | |||||
| auto res = decryptKeyExport(backupFile.substr(0, backupFile.size() - 1), password); | |||||
| REQUIRE(!res.has_value()); | |||||
| REQUIRE(res.reason() == DecryptKeyExportErrorCodes::FILE_MALFORMED); | |||||
| } | |||||
| WHEN("file too small") | |||||
| { | |||||
| auto res = decryptKeyExport(R"(-----BEGIN MEGOLM SESSION DATA----- | |||||
| AV6R43XMe68Ekf7jB4lYHLgAAAAAAAAAAAA | |||||
| -----END MEGOLM SESSION DATA-----)", password); | |||||
| REQUIRE(!res.has_value()); | |||||
| REQUIRE(res.reason() == DecryptKeyExportErrorCodes::FILE_MALFORMED); | |||||
| } | |||||
| WHEN("version is not 1") | |||||
| { | |||||
| auto modifiedBackupFile = backupFile; | |||||
| modifiedBackupFile[37] = '1'; | |||||
| auto res = decryptKeyExport(modifiedBackupFile, password); | |||||
| REQUIRE(!res.has_value()); | |||||
| REQUIRE(res.reason() == DecryptKeyExportErrorCodes::VERSION_UNSUPPORTED); | |||||
| } | |||||
| WHEN("does not pass HMAC") | |||||
| { | |||||
| auto modifiedBackupFile = backupFile; | |||||
| modifiedBackupFile[400] = '1'; | |||||
| auto res = decryptKeyExport(modifiedBackupFile, password); | |||||
| REQUIRE(!res.has_value()); | |||||
| REQUIRE(res.reason() == DecryptKeyExportErrorCodes::HMAC_FAILED); | |||||
| } | |||||
| WHEN("decrypted content is not json") | |||||
| { | |||||
| auto res = decryptKeyExport(nonJsonFile, password); | |||||
| REQUIRE(!res.has_value()); | |||||
| REQUIRE(res.reason() == DecryptKeyExportErrorCodes::NOT_JSON); | |||||
| } | |||||
| } | |||||