decryptRoomEventSync method

Event decryptRoomEventSync(
  1. String roomId,
  2. Event event
)

Implementation

Event decryptRoomEventSync(String roomId, Event event) {
  if (event.type != EventTypes.Encrypted || event.redacted) {
    return event;
  }
  final content = event.parsedRoomEncryptedContent;
  if (event.type != EventTypes.Encrypted ||
      content.ciphertextMegolm == null) {
    return event;
  }
  Map<String, dynamic> decryptedPayload;
  var canRequestSession = false;
  try {
    if (content.algorithm != AlgorithmTypes.megolmV1AesSha2) {
      throw DecryptException(DecryptException.unknownAlgorithm);
    }
    final sessionId = content.sessionId;
    if (sessionId == null) {
      throw DecryptException(DecryptException.unknownSession);
    }

    final inboundGroupSession =
        keyManager.getInboundGroupSession(roomId, sessionId);
    if (!(inboundGroupSession?.isValid ?? false)) {
      canRequestSession = true;
      throw DecryptException(DecryptException.unknownSession);
    }

    // decrypt errors here may mean we have a bad session key - others might have a better one
    canRequestSession = true;

    final decryptResult = inboundGroupSession!.inboundGroupSession!
        .decrypt(content.ciphertextMegolm!);
    canRequestSession = false;

    // we can't have the key be an int, else json-serializing will fail, thus we need it to be a string
    final messageIndexKey = 'key-${decryptResult.message_index}';
    final messageIndexValue =
        '${event.eventId}|${event.originServerTs.millisecondsSinceEpoch}';
    final haveIndex =
        inboundGroupSession.indexes.containsKey(messageIndexKey);
    if (haveIndex &&
        inboundGroupSession.indexes[messageIndexKey] != messageIndexValue) {
      Logs().e('[Decrypt] Could not decrypt due to a corrupted session.');
      throw DecryptException(DecryptException.channelCorrupted);
    }

    inboundGroupSession.indexes[messageIndexKey] = messageIndexValue;
    if (!haveIndex) {
      // now we persist the udpated indexes into the database.
      // the entry should always exist. In the case it doesn't, the following
      // line *could* throw an error. As that is a future, though, and we call
      // it un-awaited here, nothing happens, which is exactly the result we want
      client.database
          // ignore: discarded_futures
          ?.updateInboundGroupSessionIndexes(
              json.encode(inboundGroupSession.indexes), roomId, sessionId)
          // ignore: discarded_futures
          .onError((e, _) => Logs().e('Ignoring error for updating indexes'));
    }
    decryptedPayload = json.decode(decryptResult.plaintext);
  } catch (exception) {
    // alright, if this was actually by our own outbound group session, we might as well clear it
    if (exception.toString() != DecryptException.unknownSession &&
        (keyManager
                    .getOutboundGroupSession(roomId)
                    ?.outboundGroupSession
                    ?.session_id() ??
                '') ==
            content.sessionId) {
      runInRoot(() async =>
          keyManager.clearOrUseOutboundGroupSession(roomId, wipe: true));
    }
    if (canRequestSession) {
      decryptedPayload = {
        'content': event.content,
        'type': EventTypes.Encrypted,
      };
      decryptedPayload['content']['body'] = exception.toString();
      decryptedPayload['content']['msgtype'] = MessageTypes.BadEncrypted;
      decryptedPayload['content']['can_request_session'] = true;
    } else {
      decryptedPayload = {
        'content': <String, dynamic>{
          'msgtype': MessageTypes.BadEncrypted,
          'body': exception.toString(),
        },
        'type': EventTypes.Encrypted,
      };
    }
  }
  if (event.content['m.relates_to'] != null) {
    decryptedPayload['content']['m.relates_to'] =
        event.content['m.relates_to'];
  }
  return Event(
    content: decryptedPayload['content'],
    type: decryptedPayload['type'],
    senderId: event.senderId,
    eventId: event.eventId,
    room: event.room,
    originServerTs: event.originServerTs,
    unsigned: event.unsigned,
    stateKey: event.stateKey,
    prevContent: event.prevContent,
    status: event.status,
    originalSource: event,
  );
}