import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
import 'package:Envelope/models/conversation_users.dart';
|
|
import 'package:Envelope/models/conversations.dart';
|
|
import 'package:pointycastle/export.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import '/utils/encryption/crypto_utils.dart';
|
|
import '/utils/encryption/aes_helper.dart';
|
|
import '/utils/storage/database.dart';
|
|
import '/utils/strings.dart';
|
|
import '/models/friends.dart';
|
|
|
|
const messageTypeSender = 'sender';
|
|
const messageTypeReceiver = 'receiver';
|
|
|
|
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> toJson(Conversation conversation, String messageDataId) async {
|
|
final preferences = await SharedPreferences.getInstance();
|
|
RSAPublicKey publicKey = CryptoUtils.rsaPublicKeyFromPem(preferences.getString('asymmetricPublicKey')!);
|
|
|
|
final userSymmetricKey = AesHelper.deriveKey(generateRandomString(32));
|
|
final symmetricKey = AesHelper.deriveKey(generateRandomString(32));
|
|
|
|
List<Map<String, String>> messages = [];
|
|
|
|
String id = '';
|
|
|
|
List<ConversationUser> conversationUsers = await getConversationUsers(conversation);
|
|
|
|
for (var i = 0; i < conversationUsers.length; i++) {
|
|
ConversationUser user = conversationUsers[i];
|
|
if (preferences.getString('username') == user.username) {
|
|
id = user.id;
|
|
|
|
messages.add({
|
|
'message_data_id': messageDataId,
|
|
'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(
|
|
userSymmetricKey,
|
|
publicKey,
|
|
)),
|
|
'association_key': user.associationKey,
|
|
});
|
|
|
|
continue;
|
|
}
|
|
|
|
Friend friend = await getFriendByFriendId(user.id);
|
|
RSAPublicKey friendPublicKey = CryptoUtils.rsaPublicKeyFromPem(friend.asymmetricPublicKey);
|
|
|
|
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(id.codeUnits)),
|
|
'symmetric_key': AesHelper.aesEncrypt(
|
|
userSymmetricKey,
|
|
Uint8List.fromList(base64.encode(symmetricKey).codeUnits),
|
|
),
|
|
};
|
|
|
|
return jsonEncode(<String, dynamic>{
|
|
'message_data': messageData,
|
|
'message': messages,
|
|
});
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return '''
|
|
|
|
|
|
id: $id
|
|
data: $data
|
|
senderId: $senderId
|
|
senderUsername: $senderUsername
|
|
associationKey: $associationKey
|
|
createdAt: $createdAt
|
|
''';
|
|
}
|
|
|
|
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,
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
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,
|
|
);
|
|
});
|
|
|
|
}
|