diff --git a/include/mtx/common.hpp b/include/mtx/common.hpp index 6dbb8038768736273110c85f9eeee7cd1f763960..37c6c95028fa09cccd9ca26bd3f1d12d272d846a 100644 --- a/include/mtx/common.hpp +++ b/include/mtx/common.hpp @@ -54,5 +54,46 @@ from_json(const json &obj, DeviceKeys &res); void to_json(json &obj, const DeviceKeys &res); +struct JWK +{ + //! Required. Key type. Must be oct. + std::string kty; + //! Required. Key operations. Must at least contain encrypt and decrypt. + std::vector<std::string> key_ops; + //! Required. Algorithm. Must be A256CTR. + std::string alg; + //! Required. The key, encoded as urlsafe unpadded base64. + std::string k; + //! Required. Extractable. Must be true. This is a W3C extension. + bool ext; +}; + +void +from_json(const json &obj, JWK &res); + +void +to_json(json &obj, const JWK &res); + +struct EncryptedFile +{ + //! Required. The URL to the file. + std::string url; + //! Required. A JSON Web Key object. (The encryption key) + JWK key; + //! Required. The Initialisation Vector used by AES-CTR, encoded as unpadded base64. + std::string iv; + //! Required. A map from an algorithm name to a hash of the ciphertext, encoded as unpadded + //! base64. Clients should support the SHA-256 hash, which uses the key sha256. + std::map<std::string, std::string> hashes; + //! Required. Version of the encrypted attachments protocol. Must be v2. + std::string v; +}; + +void +from_json(const json &obj, EncryptedFile &res); + +void +to_json(json &obj, const EncryptedFile &res); + } // namespace crypto } // namespace mtx diff --git a/lib/structs/common.cpp b/lib/structs/common.cpp index 6a1870a0d8236752b59ae4066ce10492add3c3be..20601e9ad8e8d761eb7dcfb3fba172832256eb77 100644 --- a/lib/structs/common.cpp +++ b/lib/structs/common.cpp @@ -44,5 +44,45 @@ to_json(json &obj, const DeviceKeys &res) if (!res.unsigned_info.device_display_name.empty()) obj["unsigned"] = res.unsigned_info; } + +void +from_json(const json &obj, JWK &res) +{ + res.kty = obj.at("kty").get<std::string>(); + res.key_ops = obj.at("key_ops").get<std::vector<std::string>>(); + res.alg = obj.at("alg").get<std::string>(); + res.k = obj.at("k").get<std::string>(); + res.ext = obj.at("ext").get<bool>(); +} + +void +to_json(json &obj, const JWK &res) +{ + obj["kty"] = res.kty; + obj["key_ops"] = res.key_ops; + obj["alg"] = res.alg; + obj["k"] = res.k; + obj["ext"] = res.ext; +} + +void +from_json(const json &obj, EncryptedFile &res) +{ + res.url = obj.at("url").get<std::string>(); + res.key = obj.at("key").get<JWK>(); + res.iv = obj.at("iv").get<std::string>(); + res.hashes = obj.at("hashes").get<std::map<std::string, std::string>>(); + res.v = obj.at("v").get<std::string>(); +} + +void +to_json(json &obj, const EncryptedFile &res) +{ + obj["url"] = res.url; + obj["key"] = res.key; + obj["iv"] = res.iv; + obj["hashes"] = res.hashes; + obj["v"] = res.v; +} } } diff --git a/tests/crypto.cpp b/tests/crypto.cpp index 2c4871ffe36ad93380fd405539af2813e0bd701a..d9cd67c0aa1f22df5be2acd31c0769fdce58c36a 100644 --- a/tests/crypto.cpp +++ b/tests/crypto.cpp @@ -62,3 +62,34 @@ TEST(Crypto, DeviceKeys) EXPECT_EQ(device2.unsigned_info.device_display_name, "Alice's mobile phone"); } + +TEST(Crypto, EncryptedFile) +{ + json j = R"({ + "url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe", + "v": "v2", + "key": { + "alg": "A256CTR", + "ext": true, + "k": "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0", + "key_ops": ["encrypt","decrypt"], + "kty": "oct" + }, + "iv": "w+sE15fzSc0AAAAAAAAAAA", + "hashes": { + "sha256": "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA" + }})"_json; + + EncryptedFile file = j; + // json j2 = file; + + // EXPECT_EQ(j, j2); + EXPECT_EQ(file.v, "v2"); + EXPECT_EQ(file.iv, "w+sE15fzSc0AAAAAAAAAAA"); + EXPECT_EQ(file.hashes.at("sha256"), "fdSLu/YkRx3Wyh3KQabP3rd6+SFiKg5lsJZQHtkSAYA"); + EXPECT_EQ(file.key.alg, "A256CTR"); + EXPECT_EQ(file.key.ext, true); + EXPECT_EQ(file.key.k, "aWF6-32KGYaC3A_FEUCk1Bt0JA37zP0wrStgmdCaW-0"); + EXPECT_EQ(file.key.key_ops.size(), 2); + EXPECT_EQ(file.key.kty, "oct"); +}