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.

160 lines
4.0 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'dart:typed_data';
  4. import 'package:Envelope/utils/storage/session_cookie.dart';
  5. import 'package:Envelope/utils/storage/write_file.dart';
  6. import 'package:http/http.dart' as http;
  7. import 'package:mime/mime.dart';
  8. import 'package:pointycastle/pointycastle.dart';
  9. import 'package:uuid/uuid.dart';
  10. import '/models/conversations.dart';
  11. import '/models/messages.dart';
  12. import '/utils/encryption/aes_helper.dart';
  13. import '/utils/encryption/crypto_utils.dart';
  14. import '/utils/strings.dart';
  15. class ImageMessage extends Message {
  16. File file;
  17. ImageMessage({
  18. id,
  19. symmetricKey,
  20. userSymmetricKey,
  21. senderId,
  22. senderUsername,
  23. associationKey,
  24. createdAt,
  25. failedToSend,
  26. required this.file,
  27. }) : super(
  28. id: id,
  29. symmetricKey: symmetricKey,
  30. userSymmetricKey: userSymmetricKey,
  31. senderId: senderId,
  32. senderUsername: senderUsername,
  33. associationKey: associationKey,
  34. createdAt: createdAt,
  35. failedToSend: failedToSend,
  36. );
  37. static Future<ImageMessage> fromJson(Map<String, dynamic> json, RSAPrivateKey privKey) async {
  38. var userSymmetricKey = CryptoUtils.rsaDecrypt(
  39. base64.decode(json['symmetric_key']),
  40. privKey,
  41. );
  42. var symmetricKey = AesHelper.aesDecrypt(
  43. userSymmetricKey,
  44. base64.decode(json['message_data']['symmetric_key']),
  45. );
  46. var senderId = AesHelper.aesDecrypt(
  47. base64.decode(symmetricKey),
  48. base64.decode(json['message_data']['sender_id']),
  49. );
  50. var resp = await http.get(
  51. Uri.parse(json['message_data']['attachment']['image_link']),
  52. headers: {
  53. 'cookie': await getSessionCookie(),
  54. }
  55. );
  56. if (resp.statusCode != 200) {
  57. throw Exception('Could not get attachment file');
  58. }
  59. var data = AesHelper.aesDecryptBytes(
  60. base64.decode(symmetricKey),
  61. resp.bodyBytes,
  62. );
  63. File file = await writeImage(
  64. '${json['id']}',
  65. data,
  66. );
  67. return ImageMessage(
  68. id: json['id'],
  69. symmetricKey: symmetricKey,
  70. userSymmetricKey: base64.encode(userSymmetricKey),
  71. senderId: senderId,
  72. senderUsername: 'Unknown',
  73. associationKey: json['association_key'],
  74. createdAt: json['created_at'],
  75. failedToSend: false,
  76. file: file,
  77. );
  78. }
  79. @override
  80. Map<String, dynamic> toMap() {
  81. return {
  82. 'id': id,
  83. 'symmetric_key': symmetricKey,
  84. 'user_symmetric_key': userSymmetricKey,
  85. 'file': file.path,
  86. 'sender_id': senderId,
  87. 'sender_username': senderUsername,
  88. 'association_key': associationKey,
  89. 'created_at': createdAt,
  90. 'failed_to_send': failedToSend ? 1 : 0,
  91. };
  92. }
  93. Future<Map<String, dynamic>> payloadJson(Conversation conversation) async {
  94. final String messageDataId = (const Uuid()).v4();
  95. final symmetricKey = AesHelper.deriveKey(generateRandomString(32));
  96. final userSymmetricKey = AesHelper.deriveKey(generateRandomString(32));
  97. List<Map<String, String>> messages = await super.payloadJsonBase(
  98. symmetricKey,
  99. userSymmetricKey,
  100. conversation,
  101. id,
  102. messageDataId,
  103. );
  104. Map<String, dynamic> messageData = {
  105. 'id': messageDataId,
  106. 'sender_id': AesHelper.aesEncrypt(symmetricKey, Uint8List.fromList(senderId.codeUnits)),
  107. 'symmetric_key': AesHelper.aesEncrypt(
  108. userSymmetricKey,
  109. Uint8List.fromList(base64.encode(symmetricKey).codeUnits),
  110. ),
  111. 'attachment': {
  112. 'data': AesHelper.aesEncrypt(symmetricKey, Uint8List.fromList(file.readAsBytesSync())),
  113. 'mimetype': lookupMimeType(file.path),
  114. 'extension': getExtension(file.path),
  115. }
  116. };
  117. return <String, dynamic>{
  118. 'message_data': messageData,
  119. 'message': messages,
  120. };
  121. }
  122. @override
  123. String getContent() {
  124. return 'Image';
  125. }
  126. @override
  127. String toString() {
  128. return '''
  129. id: $id
  130. file: ${file.path},
  131. senderId: $senderId
  132. senderUsername: $senderUsername
  133. associationKey: $associationKey
  134. createdAt: $createdAt
  135. ''';
  136. }
  137. }