Mercurial > prosody-modules
comparison mod_cloud_notify_encrypted/mod_cloud_notify_encrypted.lua @ 4327:beb3342f1137
mod_cloud_notify_encrypted: New module for Encrypted Push Notifications
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Tue, 12 Jan 2021 15:43:26 +0000 |
| parents | |
| children | 2a5164162708 |
comparison
equal
deleted
inserted
replaced
| 4326:f6fdefc5c6ac | 4327:beb3342f1137 |
|---|---|
| 1 local base64 = require "util.encodings".base64; | |
| 2 local ciphers = require "openssl.cipher"; | |
| 3 local jid = require "util.jid"; | |
| 4 local json = require "util.json"; | |
| 5 local random = require "util.random"; | |
| 6 local st = require "util.stanza"; | |
| 7 | |
| 8 local xmlns_jmi = "urn:xmpp:jingle-message:0"; | |
| 9 local xmlns_push = "urn:xmpp:push:0"; | |
| 10 local xmlns_push_encrypt = "tigase:push:encrypt:0"; | |
| 11 local xmlns_push_encrypt_aes_128_gcm = "tigase:push:encrypt:aes-128-gcm"; | |
| 12 | |
| 13 -- https://xeps.tigase.net//docs/push-notifications/encrypt/#41-discovering-support | |
| 14 local function account_disco_info(event) | |
| 15 event.reply:tag("feature", {var=xmlns_push_encrypt}):up(); | |
| 16 event.reply:tag("feature", {var=xmlns_push_encrypt_aes_128_gcm}):up(); | |
| 17 end | |
| 18 module:hook("account-disco-info", account_disco_info); | |
| 19 | |
| 20 function handle_register(event) | |
| 21 local encrypt = event.stanza:get_child("encrypt", xmlns_push_encrypt); | |
| 22 if not encrypt then return; end | |
| 23 | |
| 24 local algorithm = encrypt.attr.alg; | |
| 25 if algorithm ~= "aes-128-gcm" then | |
| 26 event.origin.send(st.error_reply( | |
| 27 event.stanza, "modify", "feature-not-implemented", "Unknown encryption algorithm" | |
| 28 )); | |
| 29 return false; | |
| 30 end | |
| 31 | |
| 32 local key_base64 = encrypt:get_text(); | |
| 33 local key_binary = base64.decode(key_base64); | |
| 34 if not key_binary or #key_binary ~= 16 then | |
| 35 event.origin.send(st.error_reply( | |
| 36 event.stanza, "modify", "bad-request", "Invalid encryption key" | |
| 37 )); | |
| 38 return false; | |
| 39 end | |
| 40 | |
| 41 event.push_info.encryption = { | |
| 42 algorithm = algorithm; | |
| 43 key_base64 = key_base64; | |
| 44 }; | |
| 45 end | |
| 46 | |
| 47 function handle_push(event) | |
| 48 local encryption = event.push_info.encryption; | |
| 49 if not encryption then return; end | |
| 50 | |
| 51 if encryption.algorithm ~= "aes-128-gcm" then | |
| 52 event.reason = "Unsupported encryption algorithm: "..tostring(encryption.algorithm); | |
| 53 return true; | |
| 54 end | |
| 55 | |
| 56 local push_summary = event.push_summary; | |
| 57 | |
| 58 local original_stanza = event.original_stanza; | |
| 59 | |
| 60 local push_payload = { | |
| 61 unread = push_summary["message-count"]; | |
| 62 sender = push_summary["last-message-sender"]; | |
| 63 }; | |
| 64 | |
| 65 if original_stanza.name == "message" then | |
| 66 if original_stanza.attr.type == "groupchat" then | |
| 67 push_payload.type = "groupchat"; | |
| 68 push_payload.nickname = jid.resource(original_stanza.attr.from); | |
| 69 elseif original_stanza.attr.type ~= "error" then | |
| 70 local jmi_propose = original_stanza:get_child("propose", xmlns_jmi); | |
| 71 if jmi_propose then | |
| 72 push_payload.type = "call"; | |
| 73 push_payload.sid = jmi_propose.attr.id; | |
| 74 else | |
| 75 push_payload.type = "chat"; | |
| 76 end | |
| 77 end | |
| 78 elseif original_stanza.name == "presence" | |
| 79 and original_stanza.attr.type == "subscribe" then | |
| 80 push_payload.type = "subscribe"; | |
| 81 end | |
| 82 | |
| 83 local iv = random.bytes(12); | |
| 84 local key_binary = base64.decode(encryption.key_base64); | |
| 85 local push_json = json.encode(push_payload); | |
| 86 | |
| 87 local encrypted_payload = ciphers.new("AES-128-GCM"):encrypt(key_binary, iv):final(push_json); | |
| 88 local encrypted_element = st.stanza("encrypted", { xmlns = xmlns_push_encrypt, iv = base64.encode(iv) }) | |
| 89 :text(encrypted_payload); | |
| 90 -- Replace the unencrypted notification with the encrypted one | |
| 91 event.notification_stanza | |
| 92 :get_child("pubsub", "http://jabber.org/protocol/pubsub") | |
| 93 :get_child("publish") | |
| 94 :get_child("item") | |
| 95 :remove_children("notification", xmlns_push) | |
| 96 :add_child(encrypted_element); | |
| 97 end | |
| 98 | |
| 99 module:hook("cloud_notify/registration", handle_register); | |
| 100 module:hook("cloud_notify/push", handle_push); |
