async-matrix 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/async_matrix_e2ee/src/lib.rs +43 -2
- data/lib/async/matrix/e2ee.rb +37 -0
- data/lib/async/matrix/version.rb +1 -1
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fbcd762ae4584a018455d875317aed89d4a40751b3c5ded892451411d95cf89c
|
|
4
|
+
data.tar.gz: 49f07effb6009235cab4ac84d727a4b34cde54c7406463dedd625f037eac7a81
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d01ca57fe8befccd8d0e66e23b2fe52ee76cdc934785106d620c29f267467aa387437097c0e23861de8fe0da4f71e3193f201b43bc12d0bf6184343cb7038759
|
|
7
|
+
data.tar.gz: df8f42bfd7c0b163b17707be2412ef16f75350246ee2ddfed29217046c81277040800d75e8058da4e0d83a67c5ad39db30b5cdc2ce3b396035194325d8af4752
|
|
@@ -20,8 +20,9 @@ use vodozemac::olm::{
|
|
|
20
20
|
SessionConfig as OlmSessionConfig, SessionPickle,
|
|
21
21
|
};
|
|
22
22
|
use vodozemac::megolm::{
|
|
23
|
-
GroupSession as MegolmGroupSession, GroupSessionPickle,
|
|
24
|
-
|
|
23
|
+
ExportedSessionKey, GroupSession as MegolmGroupSession, GroupSessionPickle,
|
|
24
|
+
InboundGroupSession as MegolmInbound, InboundGroupSessionPickle, MegolmMessage,
|
|
25
|
+
SessionConfig as MegolmSessionConfig, SessionKey,
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
// --- helpers ---------------------------------------------------------------
|
|
@@ -236,6 +237,27 @@ impl InboundGroupSession {
|
|
|
236
237
|
))))
|
|
237
238
|
}
|
|
238
239
|
|
|
240
|
+
/// Build from a base64 **exported** session key.
|
|
241
|
+
///
|
|
242
|
+
/// This is the format room keys travel in outside of `m.room_key`: server-side
|
|
243
|
+
/// key backup (`m.megolm_backup.v1.curve25519-aes-sha2`) and
|
|
244
|
+
/// `m.forwarded_room_key` both carry an `ExportedSessionKey`, which is
|
|
245
|
+
/// version 1 and carries no signature — so `new` (which requires a signed
|
|
246
|
+
/// version 2 `SessionKey`) cannot accept them. Without this, a client can
|
|
247
|
+
/// never read history it did not receive a live `m.room_key` for.
|
|
248
|
+
///
|
|
249
|
+
/// The resulting session is NOT signature-verified, because an exported key
|
|
250
|
+
/// has no signature to verify. That is inherent to the format: the sender
|
|
251
|
+
/// identity of such a session is only as trustworthy as whoever handed it
|
|
252
|
+
/// over, which is why the spec treats backup-restored keys as unverified.
|
|
253
|
+
fn import(exported_key: String) -> Result<Self, Error> {
|
|
254
|
+
let key = ExportedSessionKey::from_base64(&exported_key).map_err(rt_err)?;
|
|
255
|
+
Ok(InboundGroupSession(RefCell::new(MegolmInbound::import(
|
|
256
|
+
&key,
|
|
257
|
+
MegolmSessionConfig::version_1(),
|
|
258
|
+
))))
|
|
259
|
+
}
|
|
260
|
+
|
|
239
261
|
fn session_id(&self) -> String {
|
|
240
262
|
self.0.borrow().session_id()
|
|
241
263
|
}
|
|
@@ -244,6 +266,19 @@ impl InboundGroupSession {
|
|
|
244
266
|
self.0.borrow().first_known_index()
|
|
245
267
|
}
|
|
246
268
|
|
|
269
|
+
/// Export this session at `index`, base64, for uploading to key backup or
|
|
270
|
+
/// forwarding to another device. Returns nil when the ratchet has already
|
|
271
|
+
/// moved past `index` and can no longer produce a key for it — megolm
|
|
272
|
+
/// ratchets forward only, so an earlier index is unrecoverable by design.
|
|
273
|
+
fn export_at(&self, index: u32) -> Option<String> {
|
|
274
|
+
self.0.borrow_mut().export_at(index).map(|key| key.to_base64())
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/// Export at the earliest index this session can still reach.
|
|
278
|
+
fn export_at_first_known_index(&self) -> String {
|
|
279
|
+
self.0.borrow().export_at_first_known_index().to_base64()
|
|
280
|
+
}
|
|
281
|
+
|
|
247
282
|
/// Decrypts a base64 megolm message; returns `[plaintext, message_index]`.
|
|
248
283
|
fn decrypt(&self, message: String) -> Result<(String, u32), Error> {
|
|
249
284
|
let msg = MegolmMessage::from_base64(&message).map_err(rt_err)?;
|
|
@@ -316,11 +351,17 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
316
351
|
|
|
317
352
|
let inbound = namespace.define_class("InboundGroupSession", ruby.class_object())?;
|
|
318
353
|
inbound.define_singleton_method("new", function!(InboundGroupSession::new, 1))?;
|
|
354
|
+
inbound.define_singleton_method("import", function!(InboundGroupSession::import, 1))?;
|
|
319
355
|
inbound.define_singleton_method("from_pickle", function!(InboundGroupSession::from_pickle, 2))?;
|
|
320
356
|
inbound.define_method("session_id", method!(InboundGroupSession::session_id, 0))?;
|
|
321
357
|
inbound.define_method("first_known_index", method!(InboundGroupSession::first_known_index, 0))?;
|
|
322
358
|
inbound.define_method("decrypt", method!(InboundGroupSession::decrypt, 1))?;
|
|
323
359
|
inbound.define_method("pickle", method!(InboundGroupSession::pickle, 1))?;
|
|
360
|
+
inbound.define_method("export_at", method!(InboundGroupSession::export_at, 1))?;
|
|
361
|
+
inbound.define_method(
|
|
362
|
+
"export_at_first_known_index",
|
|
363
|
+
method!(InboundGroupSession::export_at_first_known_index, 0),
|
|
364
|
+
)?;
|
|
324
365
|
|
|
325
366
|
namespace.define_module_function("verify_signature", function!(verify_signature, 3))?;
|
|
326
367
|
|
data/lib/async/matrix/e2ee.rb
CHANGED
|
@@ -56,6 +56,43 @@ __END__
|
|
|
56
56
|
inbound.session_id.should == group.session_id
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
it "imports an exported session key" do
|
|
60
|
+
# The path room keys take out of server-side key backup and out of
|
|
61
|
+
# m.forwarded_room_key. Without .import these are unusable, and a client
|
|
62
|
+
# can never read history it did not get a live m.room_key for.
|
|
63
|
+
group = Async::Matrix::E2EE::GroupSession.new
|
|
64
|
+
inbound = Async::Matrix::E2EE::InboundGroupSession.new(group.session_key)
|
|
65
|
+
message = group.encrypt("restored from backup")
|
|
66
|
+
|
|
67
|
+
exported = inbound.export_at_first_known_index
|
|
68
|
+
restored = Async::Matrix::E2EE::InboundGroupSession.import(exported)
|
|
69
|
+
|
|
70
|
+
restored.session_id.should == group.session_id
|
|
71
|
+
restored.decrypt(message).first.should == "restored from backup"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "rejects an exported key passed to .new, which requires a signature" do
|
|
75
|
+
# .new takes a version-2 SessionKey and verifies its signature; an exported
|
|
76
|
+
# key is version 1 and unsigned, so it must go through .import instead.
|
|
77
|
+
group = Async::Matrix::E2EE::GroupSession.new
|
|
78
|
+
inbound = Async::Matrix::E2EE::InboundGroupSession.new(group.session_key)
|
|
79
|
+
exported = inbound.export_at_first_known_index
|
|
80
|
+
|
|
81
|
+
lambda {
|
|
82
|
+
Async::Matrix::E2EE::InboundGroupSession.new(exported)
|
|
83
|
+
}.should.raise(RuntimeError)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "exports at a given message index" do
|
|
87
|
+
group = Async::Matrix::E2EE::GroupSession.new
|
|
88
|
+
inbound = Async::Matrix::E2EE::InboundGroupSession.new(group.session_key)
|
|
89
|
+
3.times { |i| group.encrypt("msg #{i}") }
|
|
90
|
+
|
|
91
|
+
Async::Matrix::E2EE::InboundGroupSession
|
|
92
|
+
.import(inbound.export_at(0))
|
|
93
|
+
.first_known_index.should == 0
|
|
94
|
+
end
|
|
95
|
+
|
|
59
96
|
it "round-trips an olm 1:1 message" do
|
|
60
97
|
alice = Async::Matrix::E2EE::Account.new
|
|
61
98
|
bob = Async::Matrix::E2EE::Account.new
|
data/lib/async/matrix/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async-matrix
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nathan Kidd
|
|
@@ -191,6 +191,20 @@ dependencies:
|
|
|
191
191
|
- - "~>"
|
|
192
192
|
- !ruby/object:Gem::Version
|
|
193
193
|
version: '1.2'
|
|
194
|
+
- !ruby/object:Gem::Dependency
|
|
195
|
+
name: lefthook
|
|
196
|
+
requirement: !ruby/object:Gem::Requirement
|
|
197
|
+
requirements:
|
|
198
|
+
- - "~>"
|
|
199
|
+
- !ruby/object:Gem::Version
|
|
200
|
+
version: '2.1'
|
|
201
|
+
type: :development
|
|
202
|
+
prerelease: false
|
|
203
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
204
|
+
requirements:
|
|
205
|
+
- - "~>"
|
|
206
|
+
- !ruby/object:Gem::Version
|
|
207
|
+
version: '2.1'
|
|
194
208
|
description: Async-native Matrix protocol primitives built on the Socketry async ecosystem.
|
|
195
209
|
Provides well-known discovery, event notification, and application service models.
|
|
196
210
|
email:
|