Line data Source code
1 : /*
2 : * Famedly Matrix SDK
3 : * Copyright (C) 2019, 2020, 2021 Famedly GmbH
4 : *
5 : * This program is free software: you can redistribute it and/or modify
6 : * it under the terms of the GNU Affero General Public License as
7 : * published by the Free Software Foundation, either version 3 of the
8 : * License, or (at your option) any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU Affero General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU Affero General Public License
16 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 : */
18 :
19 : import 'dart:convert';
20 :
21 : import 'package:vodozemac/vodozemac.dart' as vod;
22 :
23 : import 'package:matrix/encryption/utils/pickle_key.dart';
24 : import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
25 : import 'package:matrix/matrix.dart';
26 :
27 : class SessionKey {
28 : /// The raw json content of the key
29 : Map<String, dynamic> content = <String, dynamic>{};
30 :
31 : /// Map of stringified-index to event id, so that we can detect replay attacks
32 : Map<String, String> indexes;
33 :
34 : /// Map of userId to map of deviceId to index, that we know that device receivied, e.g. sending it ourself.
35 : /// Used for automatically answering key requests
36 : Map<String, Map<String, int>> allowedAtIndex;
37 :
38 : /// Underlying olm [InboundGroupSession] object
39 : vod.InboundGroupSession? inboundGroupSession;
40 :
41 : /// Key for libolm pickle / unpickle
42 : final String key;
43 :
44 : /// Forwarding keychain
45 6 : List<String> get forwardingCurve25519KeyChain =>
46 12 : (content['forwarding_curve25519_key_chain'] != null
47 6 : ? List<String>.from(content['forwarding_curve25519_key_chain'])
48 : : null) ??
49 4 : <String>[];
50 :
51 : /// Claimed keys of the original sender
52 : late Map<String, String> senderClaimedKeys;
53 :
54 : /// Sender curve25519 key
55 : String senderKey;
56 :
57 : /// Is this session valid?
58 14 : bool get isValid => inboundGroupSession != null;
59 :
60 : /// roomId for this session
61 : String roomId;
62 :
63 : /// Id of this session
64 : String sessionId;
65 :
66 27 : SessionKey({
67 : required this.content,
68 : required this.inboundGroupSession,
69 : required this.key,
70 : Map<String, String>? indexes,
71 : Map<String, Map<String, int>>? allowedAtIndex,
72 : required this.roomId,
73 : required this.sessionId,
74 : required this.senderKey,
75 : required this.senderClaimedKeys,
76 0 : }) : indexes = indexes ?? <String, String>{},
77 0 : allowedAtIndex = allowedAtIndex ?? <String, Map<String, int>>{};
78 :
79 6 : SessionKey.fromDb(StoredInboundGroupSession dbEntry, this.key)
80 12 : : content = Event.getMapFromPayload(dbEntry.content),
81 12 : indexes = Event.getMapFromPayload(dbEntry.indexes)
82 8 : .catchMap((k, v) => MapEntry<String, String>(k, v)),
83 12 : allowedAtIndex = Event.getMapFromPayload(dbEntry.allowedAtIndex)
84 9 : .catchMap((k, v) => MapEntry(k, Map<String, int>.from(v))),
85 6 : roomId = dbEntry.roomId,
86 6 : sessionId = dbEntry.sessionId,
87 6 : senderKey = dbEntry.senderKey {
88 : final parsedSenderClaimedKeys =
89 12 : Event.getMapFromPayload(dbEntry.senderClaimedKeys)
90 12 : .catchMap((k, v) => MapEntry<String, String>(k, v));
91 : // we need to try...catch as the map used to be <String, int> and that will throw an error.
92 12 : senderClaimedKeys = (parsedSenderClaimedKeys.isNotEmpty)
93 : ? parsedSenderClaimedKeys
94 3 : : (content
95 3 : .tryGetMap<String, dynamic>('sender_claimed_keys')
96 0 : ?.catchMap((k, v) => MapEntry<String, String>(k, v)) ??
97 9 : (content['sender_claimed_ed25519_key'] is String
98 0 : ? <String, String>{
99 0 : 'ed25519': content['sender_claimed_ed25519_key'],
100 : }
101 3 : : <String, String>{}));
102 :
103 : try {
104 12 : inboundGroupSession = vod.InboundGroupSession.fromPickleEncrypted(
105 6 : pickle: dbEntry.pickle,
106 12 : pickleKey: key.toPickleKey(),
107 : );
108 : } catch (e, s) {
109 : try {
110 0 : Logs().d('Unable to unpickle inboundGroupSession. Try LibOlm format.');
111 0 : inboundGroupSession = vod.InboundGroupSession.fromOlmPickleEncrypted(
112 0 : pickle: dbEntry.pickle,
113 0 : pickleKey: utf8.encode(key),
114 : );
115 : } catch (_) {
116 0 : Logs().e('[Vodozemac] Unable to unpickle inboundGroupSession', e, s);
117 : rethrow;
118 : }
119 : }
120 : }
121 : }
|