Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

197 lines
5.3 KiB

import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:uuid/uuid.dart';
import '/models/conversation_users.dart';
import '/models/conversations.dart';
import '/models/my_profile.dart';
import '/models/friends.dart';
import '/utils/encryption/aes_helper.dart';
import '/utils/encryption/crypto_utils.dart';
import '/utils/storage/database.dart';
import '/utils/strings.dart';
const messageTypeReceiver = 'receiver';
const messageTypeSender = 'sender';
Future<List<Message>> getMessagesForThread(Conversation conversation) async {
final db = await getDatabaseConnection();
final List<Map<String, dynamic>> maps = await db.rawQuery(
'''
SELECT * FROM messages WHERE association_key IN (
SELECT association_key FROM conversation_users WHERE conversation_id = ?
)
ORDER BY created_at DESC;
''',
[conversation.id]
);
return List.generate(maps.length, (i) {
return Message(
id: maps[i]['id'],
symmetricKey: maps[i]['symmetric_key'],
userSymmetricKey: maps[i]['user_symmetric_key'],
data: maps[i]['data'],
senderId: maps[i]['sender_id'],
senderUsername: maps[i]['sender_username'],
associationKey: maps[i]['association_key'],
createdAt: maps[i]['created_at'],
failedToSend: maps[i]['failed_to_send'] == 1,
);
});
}
class Message {
String id;
String symmetricKey;
String userSymmetricKey;
String data;
String senderId;
String senderUsername;
String associationKey;
String createdAt;
bool failedToSend;
Message({
required this.id,
required this.symmetricKey,
required this.userSymmetricKey,
required this.data,
required this.senderId,
required this.senderUsername,
required this.associationKey,
required this.createdAt,
required this.failedToSend,
});
factory Message.fromJson(Map<String, dynamic> json, RSAPrivateKey privKey) {
var userSymmetricKey = CryptoUtils.rsaDecrypt(
base64.decode(json['symmetric_key']),
privKey,
);
var symmetricKey = AesHelper.aesDecrypt(
userSymmetricKey,
base64.decode(json['message_data']['symmetric_key']),
);
var senderId = AesHelper.aesDecrypt(
base64.decode(symmetricKey),
base64.decode(json['message_data']['sender_id']),
);
var data = AesHelper.aesDecrypt(
base64.decode(symmetricKey),
base64.decode(json['message_data']['data']),
);
return Message(
id: json['id'],
symmetricKey: symmetricKey,
userSymmetricKey: base64.encode(userSymmetricKey),
data: data,
senderId: senderId,
senderUsername: 'Unknown',
associationKey: json['association_key'],
createdAt: json['created_at'],
failedToSend: false,
);
}
Future<String> payloadJson(Conversation conversation, String messageId) async {
MyProfile profile = await MyProfile.getProfile();
if (profile.publicKey == null) {
throw Exception('Could not get profile.publicKey');
}
RSAPublicKey publicKey = profile.publicKey!;
final String messageDataId = (const Uuid()).v4();
final userSymmetricKey = AesHelper.deriveKey(generateRandomString(32));
final symmetricKey = AesHelper.deriveKey(generateRandomString(32));
List<Map<String, String>> messages = [];
List<ConversationUser> conversationUsers = await getConversationUsers(conversation);
for (var i = 0; i < conversationUsers.length; i++) {
ConversationUser user = conversationUsers[i];
if (profile.id == user.userId) {
id = user.id;
messages.add({
'id': messageId,
'message_data_id': messageDataId,
'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(
userSymmetricKey,
publicKey,
)),
'association_key': user.associationKey,
});
continue;
}
ConversationUser conversationUser = await getConversationUser(conversation, user.userId);
RSAPublicKey friendPublicKey = conversationUser.publicKey;
messages.add({
'message_data_id': messageDataId,
'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(
userSymmetricKey,
friendPublicKey,
)),
'association_key': user.associationKey,
});
}
Map<String, String> messageData = {
'id': messageDataId,
'data': AesHelper.aesEncrypt(symmetricKey, Uint8List.fromList(data.codeUnits)),
'sender_id': AesHelper.aesEncrypt(symmetricKey, Uint8List.fromList(senderId.codeUnits)),
'symmetric_key': AesHelper.aesEncrypt(
userSymmetricKey,
Uint8List.fromList(base64.encode(symmetricKey).codeUnits),
),
};
return jsonEncode(<String, dynamic>{
'message_data': messageData,
'message': messages,
});
}
Map<String, dynamic> toMap() {
return {
'id': id,
'symmetric_key': symmetricKey,
'user_symmetric_key': userSymmetricKey,
'data': data,
'sender_id': senderId,
'sender_username': senderUsername,
'association_key': associationKey,
'created_at': createdAt,
'failed_to_send': failedToSend ? 1 : 0,
};
}
@override
String toString() {
return '''
id: $id
data: $data
senderId: $senderId
senderUsername: $senderUsername
associationKey: $associationKey
createdAt: $createdAt
''';
}
}