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.

194 lines
4.9 KiB

  1. import 'dart:convert';
  2. import 'dart:typed_data';
  3. import 'package:pointycastle/export.dart';
  4. import '/utils/encryption/aes_helper.dart';
  5. import '/utils/encryption/crypto_utils.dart';
  6. import '/utils/storage/database.dart';
  7. Friend findFriendByFriendId(List<Friend> friends, String id) {
  8. for (var friend in friends) {
  9. if (friend.friendId == id) {
  10. return friend;
  11. }
  12. }
  13. // Or return `null`.
  14. throw ArgumentError.value(id, 'id', 'No element with that id');
  15. }
  16. Future<Friend> getFriendByFriendId(String userId) async {
  17. final db = await getDatabaseConnection();
  18. final List<Map<String, dynamic>> maps = await db.query(
  19. 'friends',
  20. where: 'friend_id = ?',
  21. whereArgs: [userId],
  22. );
  23. if (maps.length != 1) {
  24. throw ArgumentError('Invalid user id');
  25. }
  26. return Friend(
  27. id: maps[0]['id'],
  28. userId: maps[0]['user_id'],
  29. friendId: maps[0]['friend_id'],
  30. friendSymmetricKey: maps[0]['symmetric_key'],
  31. publicKey: CryptoUtils.rsaPublicKeyFromPem(maps[0]['asymmetric_public_key']),
  32. acceptedAt: maps[0]['accepted_at'] != null ? DateTime.parse(maps[0]['accepted_at']) : null,
  33. username: maps[0]['username'],
  34. );
  35. }
  36. Future<List<Friend>> getFriends({bool? accepted}) async {
  37. final db = await getDatabaseConnection();
  38. String? where;
  39. if (accepted == true) {
  40. where = 'accepted_at IS NOT NULL';
  41. }
  42. if (accepted == false) {
  43. where = 'accepted_at IS NULL';
  44. }
  45. final List<Map<String, dynamic>> maps = await db.query(
  46. 'friends',
  47. where: where,
  48. );
  49. return List.generate(maps.length, (i) {
  50. return Friend(
  51. id: maps[i]['id'],
  52. userId: maps[i]['user_id'],
  53. friendId: maps[i]['friend_id'],
  54. friendSymmetricKey: maps[i]['symmetric_key'],
  55. publicKey: CryptoUtils.rsaPublicKeyFromPem(maps[i]['asymmetric_public_key']),
  56. acceptedAt: maps[i]['accepted_at'] != null ? DateTime.parse(maps[i]['accepted_at']) : null,
  57. username: maps[i]['username'],
  58. );
  59. });
  60. }
  61. class Friend{
  62. String id;
  63. String userId;
  64. String username;
  65. String friendId;
  66. String friendSymmetricKey;
  67. RSAPublicKey publicKey;
  68. DateTime? acceptedAt;
  69. bool? selected;
  70. Friend({
  71. required this.id,
  72. required this.userId,
  73. required this.username,
  74. required this.friendId,
  75. required this.friendSymmetricKey,
  76. required this.publicKey,
  77. required this.acceptedAt,
  78. this.selected,
  79. });
  80. factory Friend.fromJson(Map<String, dynamic> json, RSAPrivateKey privKey) {
  81. Uint8List idDecrypted = CryptoUtils.rsaDecrypt(
  82. base64.decode(json['friend_id']),
  83. privKey,
  84. );
  85. Uint8List username = CryptoUtils.rsaDecrypt(
  86. base64.decode(json['friend_username']),
  87. privKey,
  88. );
  89. Uint8List symmetricKeyDecrypted = CryptoUtils.rsaDecrypt(
  90. base64.decode(json['symmetric_key']),
  91. privKey,
  92. );
  93. String publicKeyString = AesHelper.aesDecrypt(
  94. symmetricKeyDecrypted,
  95. base64.decode(json['asymmetric_public_key'])
  96. );
  97. RSAPublicKey publicKey = CryptoUtils.rsaPublicKeyFromPem(publicKeyString);
  98. return Friend(
  99. id: json['id'],
  100. userId: json['user_id'],
  101. username: String.fromCharCodes(username),
  102. friendId: String.fromCharCodes(idDecrypted),
  103. friendSymmetricKey: base64.encode(symmetricKeyDecrypted),
  104. publicKey: publicKey,
  105. acceptedAt: json['accepted_at']['Valid'] ?
  106. DateTime.parse(json['accepted_at']['Time']) :
  107. null,
  108. );
  109. }
  110. Map<String, dynamic> payloadJson() {
  111. Uint8List friendIdEncrypted = CryptoUtils.rsaEncrypt(
  112. Uint8List.fromList(friendId.codeUnits),
  113. publicKey,
  114. );
  115. Uint8List usernameEncrypted = CryptoUtils.rsaEncrypt(
  116. Uint8List.fromList(username.codeUnits),
  117. publicKey,
  118. );
  119. Uint8List symmetricKeyEncrypted = CryptoUtils.rsaEncrypt(
  120. Uint8List.fromList(
  121. base64.decode(friendSymmetricKey),
  122. ),
  123. publicKey,
  124. );
  125. var publicKeyEncrypted = AesHelper.aesEncrypt(
  126. base64.decode(friendSymmetricKey),
  127. Uint8List.fromList(CryptoUtils.encodeRSAPublicKeyToPem(publicKey).codeUnits),
  128. );
  129. return {
  130. 'id': id,
  131. 'user_id': userId,
  132. 'friend_id': base64.encode(friendIdEncrypted),
  133. 'friend_username': base64.encode(usernameEncrypted),
  134. 'symmetric_key': base64.encode(symmetricKeyEncrypted),
  135. 'asymmetric_public_key': publicKeyEncrypted,
  136. 'accepted_at': null,
  137. };
  138. }
  139. String publicKeyPem() {
  140. return CryptoUtils.encodeRSAPublicKeyToPem(publicKey);
  141. }
  142. Map<String, dynamic> toMap() {
  143. return {
  144. 'id': id,
  145. 'user_id': userId,
  146. 'username': username,
  147. 'friend_id': friendId,
  148. 'symmetric_key': base64.encode(friendSymmetricKey.codeUnits),
  149. 'asymmetric_public_key': publicKeyPem(),
  150. 'accepted_at': acceptedAt?.toIso8601String(),
  151. };
  152. }
  153. @override
  154. String toString() {
  155. return '''
  156. id: $id
  157. userId: $userId
  158. username: $username
  159. friendId: $friendId
  160. accepted_at: $acceptedAt''';
  161. }
  162. }