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.

163 lines
4.1 KiB

  1. import 'dart:convert';
  2. import 'dart:typed_data';
  3. import 'package:Envelope/models/conversation_users.dart';
  4. import 'package:Envelope/models/friends.dart';
  5. import 'package:Envelope/models/my_profile.dart';
  6. import 'package:pointycastle/export.dart';
  7. import 'package:sqflite/sqflite.dart';
  8. import 'package:uuid/uuid.dart';
  9. import '/utils/encryption/crypto_utils.dart';
  10. import '/utils/encryption/aes_helper.dart';
  11. import '/utils/storage/database.dart';
  12. import '/utils/strings.dart';
  13. Conversation findConversationByDetailId(List<Conversation> conversations, String id) {
  14. for (var conversation in conversations) {
  15. if (conversation.conversationDetailId == id) {
  16. return conversation;
  17. }
  18. }
  19. // Or return `null`.
  20. throw ArgumentError.value(id, "id", "No element with that id");
  21. }
  22. class Conversation {
  23. String id;
  24. String userId;
  25. String conversationDetailId;
  26. String symmetricKey;
  27. bool admin;
  28. String name;
  29. Conversation({
  30. required this.id,
  31. required this.userId,
  32. required this.conversationDetailId,
  33. required this.symmetricKey,
  34. required this.admin,
  35. required this.name,
  36. });
  37. factory Conversation.fromJson(Map<String, dynamic> json, RSAPrivateKey privKey) {
  38. var symmetricKeyDecrypted = CryptoUtils.rsaDecrypt(
  39. base64.decode(json['symmetric_key']),
  40. privKey,
  41. );
  42. var detailId = AesHelper.aesDecrypt(
  43. symmetricKeyDecrypted,
  44. base64.decode(json['conversation_detail_id']),
  45. );
  46. var admin = AesHelper.aesDecrypt(
  47. symmetricKeyDecrypted,
  48. base64.decode(json['admin']),
  49. );
  50. return Conversation(
  51. id: json['id'],
  52. userId: json['user_id'],
  53. conversationDetailId: detailId,
  54. symmetricKey: base64.encode(symmetricKeyDecrypted),
  55. admin: admin == 'true',
  56. name: 'Unknown',
  57. );
  58. }
  59. @override
  60. String toString() {
  61. return '''
  62. id: $id
  63. userId: $userId
  64. name: $name
  65. admin: $admin''';
  66. }
  67. Map<String, dynamic> toMap() {
  68. return {
  69. 'id': id,
  70. 'user_id': userId,
  71. 'conversation_detail_id': conversationDetailId,
  72. 'symmetric_key': symmetricKey,
  73. 'admin': admin ? 1 : 0,
  74. 'name': name,
  75. };
  76. }
  77. }
  78. Future<Conversation> createConversation(String title, List<Friend> friends) async {
  79. final db = await getDatabaseConnection();
  80. MyProfile profile = await MyProfile.getProfile();
  81. var uuid = const Uuid();
  82. final String conversationId = uuid.v4();
  83. Uint8List symmetricKey = AesHelper.deriveKey(generateRandomString(32));
  84. String associationKey = generateRandomString(32);
  85. Conversation conversation = Conversation(
  86. id: conversationId,
  87. userId: profile.id,
  88. conversationDetailId: '',
  89. symmetricKey: base64.encode(symmetricKey),
  90. admin: true,
  91. name: title,
  92. );
  93. await db.insert(
  94. 'conversations',
  95. conversation.toMap(),
  96. conflictAlgorithm: ConflictAlgorithm.replace,
  97. );
  98. await db.insert(
  99. 'conversation_users',
  100. ConversationUser(
  101. id: uuid.v4(),
  102. conversationId: conversationId,
  103. username: profile.username,
  104. associationKey: associationKey,
  105. admin: false,
  106. ).toMap(),
  107. conflictAlgorithm: ConflictAlgorithm.replace,
  108. );
  109. for (Friend friend in friends) {
  110. await db.insert(
  111. 'conversation_users',
  112. ConversationUser(
  113. id: uuid.v4(),
  114. conversationId: conversationId,
  115. username: friend.username,
  116. associationKey: associationKey,
  117. admin: false,
  118. ).toMap(),
  119. conflictAlgorithm: ConflictAlgorithm.replace,
  120. );
  121. }
  122. return conversation;
  123. }
  124. // A method that retrieves all the dogs from the dogs table.
  125. Future<List<Conversation>> getConversations() async {
  126. final db = await getDatabaseConnection();
  127. final List<Map<String, dynamic>> maps = await db.query('conversations');
  128. return List.generate(maps.length, (i) {
  129. return Conversation(
  130. id: maps[i]['id'],
  131. userId: maps[i]['user_id'],
  132. conversationDetailId: maps[i]['conversation_detail_id'],
  133. symmetricKey: maps[i]['symmetric_key'],
  134. admin: maps[i]['admin'] == 1,
  135. name: maps[i]['name'],
  136. );
  137. });
  138. }