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");
+}