import 'dart:convert'; import 'dart:typed_data'; import 'package:Envelope/models/conversation_users.dart'; import 'package:Envelope/models/friends.dart'; import 'package:Envelope/models/my_profile.dart'; import 'package:pointycastle/export.dart'; import 'package:sqflite/sqflite.dart'; import 'package:uuid/uuid.dart'; import '/utils/encryption/crypto_utils.dart'; import '/utils/encryption/aes_helper.dart'; import '/utils/storage/database.dart'; import '/utils/strings.dart'; enum ConversationStatus { complete, pending, error, } class Conversation { String id; String userId; String conversationDetailId; String symmetricKey; bool admin; String name; ConversationStatus status; Conversation({ required this.id, required this.userId, required this.conversationDetailId, required this.symmetricKey, required this.admin, required this.name, required this.status, }); factory Conversation.fromJson(Map json, RSAPrivateKey privKey) { var symmetricKeyDecrypted = CryptoUtils.rsaDecrypt( base64.decode(json['symmetric_key']), privKey, ); var detailId = AesHelper.aesDecrypt( symmetricKeyDecrypted, base64.decode(json['conversation_detail_id']), ); var admin = AesHelper.aesDecrypt( symmetricKeyDecrypted, base64.decode(json['admin']), ); return Conversation( id: json['id'], userId: json['user_id'], conversationDetailId: detailId, symmetricKey: base64.encode(symmetricKeyDecrypted), admin: admin == 'true', name: 'Unknown', status: ConversationStatus.complete, ); } Future> toJson() async { MyProfile profile = await MyProfile.getProfile(); var symKey = base64.decode(symmetricKey); List users = await getConversationUsers(this); List userConversations = []; for (var x in users) { print(x.toMap()); } for (ConversationUser user in users) { if (profile.id == user.userId) { userConversations.add({ 'id': user.id, 'user_id': profile.id, 'conversation_detail_id': AesHelper.aesEncrypt(symKey, Uint8List.fromList(id.codeUnits)), 'admin': AesHelper.aesEncrypt(symKey, Uint8List.fromList((admin ? 'true' : 'false').codeUnits)), 'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(symKey, profile.publicKey!)), }); continue; } Friend friend = await getFriendByFriendId(user.userId); RSAPublicKey pubKey = CryptoUtils.rsaPublicKeyFromPem(friend.asymmetricPublicKey); userConversations.add({ 'id': user.id, 'user_id': friend.userId, 'conversation_detail_id': AesHelper.aesEncrypt(symKey, Uint8List.fromList(id.codeUnits)), 'admin': AesHelper.aesEncrypt(symKey, Uint8List.fromList((admin ? 'true' : 'false').codeUnits)), 'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(symKey, pubKey)), }); } for (var x in userConversations) { print(x); } return { 'id': id, 'name': AesHelper.aesEncrypt(symKey, Uint8List.fromList(name.codeUnits)), 'users': AesHelper.aesEncrypt(symKey, Uint8List.fromList(jsonEncode(users).codeUnits)), 'user_conversations': userConversations, }; } @override String toString() { return ''' id: $id userId: $userId name: $name admin: $admin'''; } Map toMap() { return { 'id': id, 'user_id': userId, 'conversation_detail_id': conversationDetailId, 'symmetric_key': symmetricKey, 'admin': admin ? 1 : 0, 'name': name, 'status': status.index, }; } } Future createConversation(String title, List friends) async { final db = await getDatabaseConnection(); MyProfile profile = await MyProfile.getProfile(); var uuid = const Uuid(); final String conversationId = uuid.v4(); Uint8List symmetricKey = AesHelper.deriveKey(generateRandomString(32)); String associationKey = generateRandomString(32); Conversation conversation = Conversation( id: conversationId, userId: profile.id, conversationDetailId: '', symmetricKey: base64.encode(symmetricKey), admin: true, name: title, status: ConversationStatus.pending, ); await db.insert( 'conversations', conversation.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); await db.insert( 'conversation_users', ConversationUser( id: uuid.v4(), userId: profile.id, conversationId: conversationId, username: profile.username, associationKey: associationKey, admin: true, ).toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); for (Friend friend in friends) { await db.insert( 'conversation_users', ConversationUser( id: uuid.v4(), userId: friend.friendId, conversationId: conversationId, username: friend.username, associationKey: associationKey, admin: false, ).toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); } return conversation; } Conversation findConversationByDetailId(List conversations, String id) { for (var conversation in conversations) { if (conversation.conversationDetailId == id) { return conversation; } } // Or return `null`. throw ArgumentError.value(id, "id", "No element with that id"); } // A method that retrieves all the dogs from the dogs table. Future> getConversations() async { final db = await getDatabaseConnection(); final List> maps = await db.query('conversations'); return List.generate(maps.length, (i) { return Conversation( id: maps[i]['id'], userId: maps[i]['user_id'], conversationDetailId: maps[i]['conversation_detail_id'], symmetricKey: maps[i]['symmetric_key'], admin: maps[i]['admin'] == 1, name: maps[i]['name'], status: ConversationStatus.values[maps[i]['status']], ); }); } Future getConversationById(String id) async { final db = await getDatabaseConnection(); final List> maps = await db.query( 'conversations', where: 'id = ?', whereArgs: [id], ); if (maps.length != 1) { throw ArgumentError('Invalid user id'); } return Conversation( id: maps[0]['id'], userId: maps[0]['user_id'], conversationDetailId: maps[0]['conversation_detail_id'], symmetricKey: maps[0]['symmetric_key'], admin: maps[0]['admin'] == 1, name: maps[0]['name'], status: ConversationStatus.values[maps[0]['status']], ); }