feat: Better encryption / verification
This commit is contained in:
parent
81d86174c4
commit
274ca76db8
8 changed files with 140 additions and 48 deletions
|
|
@ -62,20 +62,30 @@ class _EncryptionButtonState extends State<EncryptionButton> {
|
|||
.onSync
|
||||
.stream
|
||||
.listen((s) => setState(() => null));
|
||||
return FutureBuilder<List<DeviceKeys>>(
|
||||
future: widget.room.encrypted ? widget.room.getUserDeviceKeys() : null,
|
||||
return FutureBuilder<List<User>>(
|
||||
future:
|
||||
widget.room.encrypted ? widget.room.requestParticipants() : null,
|
||||
builder: (BuildContext context, snapshot) {
|
||||
Color color;
|
||||
if (widget.room.encrypted && snapshot.hasData) {
|
||||
var data = snapshot.data;
|
||||
final deviceKeysList = data;
|
||||
color = Colors.orange;
|
||||
if (deviceKeysList.indexWhere((DeviceKeys deviceKeys) =>
|
||||
deviceKeys.verified == false &&
|
||||
deviceKeys.blocked == false) ==
|
||||
-1) {
|
||||
color = Colors.black.withGreen(220).withOpacity(0.75);
|
||||
final users = snapshot.data;
|
||||
users.removeWhere((u) =>
|
||||
!{Membership.invite, Membership.join}.contains(u.membership) ||
|
||||
!widget.room.client.userDeviceKeys.containsKey(u.id));
|
||||
var allUsersValid = true;
|
||||
var oneUserInvalid = false;
|
||||
for (final u in users) {
|
||||
final status = widget.room.client.userDeviceKeys[u.id].verified;
|
||||
if (status != UserVerifiedStatus.verified) {
|
||||
allUsersValid = false;
|
||||
}
|
||||
if (status == UserVerifiedStatus.unknownDevice) {
|
||||
oneUserInvalid = true;
|
||||
}
|
||||
}
|
||||
color = oneUserInvalid
|
||||
? Colors.red
|
||||
: (allUsersValid ? Colors.green : Colors.orange);
|
||||
} else if (!widget.room.encrypted &&
|
||||
widget.room.joinRules != JoinRules.public) {
|
||||
color = null;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:famedlysdk/encryption.dart';
|
||||
import 'package:fluffychat/components/dialogs/simple_dialogs.dart';
|
||||
import 'package:fluffychat/components/message_content.dart';
|
||||
import 'package:fluffychat/components/reply_content.dart';
|
||||
|
|
@ -13,6 +14,8 @@ import '../avatar.dart';
|
|||
import '../matrix.dart';
|
||||
import '../message_reactions.dart';
|
||||
import 'state_message.dart';
|
||||
import '../../views/key_verification.dart';
|
||||
import '../../utils/app_route.dart';
|
||||
|
||||
class Message extends StatelessWidget {
|
||||
final Event event;
|
||||
|
|
@ -37,6 +40,36 @@ class Message extends StatelessWidget {
|
|||
/// of touchscreen.
|
||||
static bool useMouse = false;
|
||||
|
||||
void _verifyOrRequestKey(BuildContext context) async {
|
||||
final client = Matrix.of(context).client;
|
||||
if (client.isUnknownSession && client.encryption.crossSigning.enabled) {
|
||||
final req =
|
||||
await client.userDeviceKeys[client.userID].startVerification();
|
||||
req.onUpdate = () async {
|
||||
if (req.state == KeyVerificationState.done) {
|
||||
for (var i = 0; i < 12; i++) {
|
||||
if (await client.encryption.keyManager.isCached()) {
|
||||
break;
|
||||
}
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
}
|
||||
final timeline = await event.room.getTimeline();
|
||||
timeline.requestKeys();
|
||||
timeline.cancelSubscriptions();
|
||||
}
|
||||
};
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
KeyVerificationView(request: req),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||
event.getDisplayEvent(timeline).requestKey());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (event.type == EventTypes.Unknown) {
|
||||
|
|
@ -137,12 +170,13 @@ class Message extends StatelessWidget {
|
|||
RaisedButton(
|
||||
color: color.withAlpha(100),
|
||||
child: Text(
|
||||
L10n.of(context).requestPermission,
|
||||
client.isUnknownSession &&
|
||||
client.encryption.crossSigning.enabled
|
||||
? L10n.of(context).verify
|
||||
: L10n.of(context).requestPermission,
|
||||
style: TextStyle(color: textColor),
|
||||
),
|
||||
onPressed: () => SimpleDialogs(context)
|
||||
.tryRequestWithLoadingDialog(
|
||||
displayEvent.requestKey()),
|
||||
onPressed: () => _verifyOrRequestKey(context),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Opacity(
|
||||
|
|
|
|||
|
|
@ -269,10 +269,21 @@ class MatrixState extends State<Matrix> {
|
|||
});
|
||||
onKeyVerificationRequestSub ??= client.onKeyVerificationRequest.stream
|
||||
.listen((KeyVerification request) async {
|
||||
var hidPopup = false;
|
||||
request.onUpdate = () {
|
||||
if (!hidPopup &&
|
||||
{KeyVerificationState.done, KeyVerificationState.error}
|
||||
.contains(request.state)) {
|
||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
||||
}
|
||||
hidPopup = true;
|
||||
};
|
||||
if (await SimpleDialogs(context).askConfirmation(
|
||||
titleText: L10n.of(context).newVerificationRequest,
|
||||
contentText: L10n.of(context).askVerificationRequest(request.userId),
|
||||
)) {
|
||||
request.onUpdate = null;
|
||||
hidPopup = true;
|
||||
await request.acceptVerification();
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
|
|
@ -281,6 +292,8 @@ class MatrixState extends State<Matrix> {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
request.onUpdate = null;
|
||||
hidPopup = true;
|
||||
await request.rejectVerification();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
import '../utils/presence_extension.dart';
|
||||
import 'dialogs/simple_dialogs.dart';
|
||||
import 'matrix.dart';
|
||||
import '../views/key_verification.dart';
|
||||
import '../utils/app_route.dart';
|
||||
|
||||
class UserBottomSheet extends StatelessWidget {
|
||||
final User user;
|
||||
|
|
@ -72,9 +74,22 @@ class UserBottomSheet extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
void _verifyAction(BuildContext context) async {
|
||||
final client = Matrix.of(context).client;
|
||||
final req = await client.userDeviceKeys[user.id].startVerification();
|
||||
await Navigator.of(context).push(
|
||||
AppRoute.defaultRoute(
|
||||
context,
|
||||
KeyVerificationView(request: req),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final presence = Matrix.of(context).client.presences[user.id];
|
||||
final client = Matrix.of(context).client;
|
||||
final presence = client.presences[user.id];
|
||||
final verificationStatus = client.userDeviceKeys[user.id]?.verified;
|
||||
var items = <PopupMenuEntry<String>>[];
|
||||
|
||||
if (onMention != null) {
|
||||
|
|
@ -145,6 +160,21 @@ class UserBottomSheet extends StatelessWidget {
|
|||
),
|
||||
title: Text(user.calcDisplayname()),
|
||||
actions: [
|
||||
if (verificationStatus != null)
|
||||
InkWell(
|
||||
child: Icon(
|
||||
Icons.lock,
|
||||
color: {
|
||||
UserVerifiedStatus.unknownDevice: Colors.red,
|
||||
UserVerifiedStatus.verified: Colors.green,
|
||||
}[verificationStatus] ??
|
||||
Colors.orange,
|
||||
),
|
||||
onTap: () =>
|
||||
verificationStatus == UserVerifiedStatus.unknown
|
||||
? _verifyAction(context)
|
||||
: null,
|
||||
),
|
||||
if (user.id != Matrix.of(context).client.userID)
|
||||
PopupMenuButton(
|
||||
itemBuilder: (_) => items,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue