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

import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:Envelope/utils/storage/session_cookie.dart';
import 'package:Envelope/utils/storage/write_file.dart';
import 'package:http/http.dart' as http;
import 'package:mime/mime.dart';
import 'package:pointycastle/pointycastle.dart';
import 'package:uuid/uuid.dart';
import '/models/conversations.dart';
import '/models/messages.dart';
import '/utils/encryption/aes_helper.dart';
import '/utils/encryption/crypto_utils.dart';
import '/utils/strings.dart';
class ImageMessage extends Message {
File file;
ImageMessage({
id,
symmetricKey,
userSymmetricKey,
senderId,
senderUsername,
associationKey,
createdAt,
failedToSend,
required this.file,
}) : super(
id: id,
symmetricKey: symmetricKey,
userSymmetricKey: userSymmetricKey,
senderId: senderId,
senderUsername: senderUsername,
associationKey: associationKey,
createdAt: createdAt,
failedToSend: failedToSend,
);
static Future<ImageMessage> fromJson(Map<String, dynamic> json, RSAPrivateKey privKey) async {
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 resp = await http.get(
Uri.parse(json['message_data']['attachment']['image_link']),
headers: {
'cookie': await getSessionCookie(),
}
);
if (resp.statusCode != 200) {
throw Exception('Could not get attachment file');
}
var data = AesHelper.aesDecryptBytes(
base64.decode(symmetricKey),
resp.bodyBytes,
);
File file = await writeImage(
'${json['id']}',
data,
);
return ImageMessage(
id: json['id'],
symmetricKey: symmetricKey,
userSymmetricKey: base64.encode(userSymmetricKey),
senderId: senderId,
senderUsername: 'Unknown',
associationKey: json['association_key'],
createdAt: json['created_at'],
failedToSend: false,
file: file,
);
}
@override
Map<String, dynamic> toMap() {
return {
'id': id,
'symmetric_key': symmetricKey,
'user_symmetric_key': userSymmetricKey,
'file': file.path,
'sender_id': senderId,
'sender_username': senderUsername,
'association_key': associationKey,
'created_at': createdAt,
'failed_to_send': failedToSend ? 1 : 0,
};
}
Future<Map<String, dynamic>> payloadJson(Conversation conversation) async {
final String messageDataId = (const Uuid()).v4();
final symmetricKey = AesHelper.deriveKey(generateRandomString(32));
final userSymmetricKey = AesHelper.deriveKey(generateRandomString(32));
List<Map<String, String>> messages = await super.payloadJsonBase(
symmetricKey,
userSymmetricKey,
conversation,
id,
messageDataId,
);
Map<String, dynamic> messageData = {
'id': messageDataId,
'sender_id': AesHelper.aesEncrypt(symmetricKey, Uint8List.fromList(senderId.codeUnits)),
'symmetric_key': AesHelper.aesEncrypt(
userSymmetricKey,
Uint8List.fromList(base64.encode(symmetricKey).codeUnits),
),
'attachment': {
'data': AesHelper.aesEncrypt(symmetricKey, Uint8List.fromList(file.readAsBytesSync())),
'mimetype': lookupMimeType(file.path),
'extension': getExtension(file.path),
}
};
return <String, dynamic>{
'message_data': messageData,
'message': messages,
};
}
@override
String getContent() {
return 'Image';
}
@override
String toString() {
return '''
id: $id
file: ${file.path},
senderId: $senderId
senderUsername: $senderUsername
associationKey: $associationKey
createdAt: $createdAt
''';
}
}