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.

169 lines
4.8 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:Capsule/utils/storage/get_file.dart';
  4. import 'package:flutter_dotenv/flutter_dotenv.dart';
  5. import 'package:pointycastle/impl.dart';
  6. import 'package:shared_preferences/shared_preferences.dart';
  7. import '/utils/encryption/aes_helper.dart';
  8. import '/utils/encryption/crypto_utils.dart';
  9. // TODO: Replace this with the prod url when server is deployed
  10. String defaultServerUrl = dotenv.env['SERVER_URL'] ?? 'http://localhost:8080/';
  11. class MyProfile {
  12. String id;
  13. String username;
  14. String? friendId;
  15. RSAPrivateKey? privateKey;
  16. RSAPublicKey? publicKey;
  17. String? symmetricKey;
  18. DateTime? loggedInAt;
  19. File? image;
  20. String? imageLink;
  21. String messageExpiryDefault = 'no_expiry';
  22. MyProfile({
  23. required this.id,
  24. required this.username,
  25. this.friendId,
  26. this.privateKey,
  27. this.publicKey,
  28. this.symmetricKey,
  29. this.loggedInAt,
  30. this.image,
  31. this.imageLink,
  32. required this.messageExpiryDefault,
  33. });
  34. factory MyProfile._fromJson(Map<String, dynamic> json) {
  35. DateTime loggedInAt = DateTime.now();
  36. if (json.containsKey('logged_in_at')) {
  37. loggedInAt = DateTime.parse(json['logged_in_at']);
  38. }
  39. RSAPrivateKey privateKey = CryptoUtils.rsaPrivateKeyFromPem(json['asymmetric_private_key']);
  40. RSAPublicKey publicKey = CryptoUtils.rsaPublicKeyFromPem(json['asymmetric_public_key']);
  41. return MyProfile(
  42. id: json['user_id'],
  43. username: json['username'],
  44. privateKey: privateKey,
  45. publicKey: publicKey,
  46. symmetricKey: json['symmetric_key'],
  47. loggedInAt: loggedInAt,
  48. messageExpiryDefault: json['message_expiry_default'],
  49. image: json['file'] != null ? File(json['file']) : null,
  50. imageLink: json['image_link'],
  51. );
  52. }
  53. @override
  54. String toString() {
  55. return '''
  56. user_id: $id
  57. username: $username
  58. logged_in_at: $loggedInAt
  59. public_key: $publicKey
  60. private_key: $privateKey
  61. ''';
  62. }
  63. String toJson() {
  64. return jsonEncode(<String, dynamic>{
  65. 'user_id': id,
  66. 'username': username,
  67. 'asymmetric_private_key': privateKey != null ?
  68. CryptoUtils.encodeRSAPrivateKeyToPem(privateKey!) :
  69. null,
  70. 'asymmetric_public_key': publicKey != null ?
  71. CryptoUtils.encodeRSAPublicKeyToPem(publicKey!) :
  72. null,
  73. 'symmetric_key': symmetricKey,
  74. 'logged_in_at': loggedInAt?.toIso8601String(),
  75. 'message_expiry_default': messageExpiryDefault,
  76. 'file': image?.path,
  77. 'image_link': imageLink,
  78. });
  79. }
  80. static Future<MyProfile> login(Map<String, dynamic> json, String password) async {
  81. json['asymmetric_private_key'] = AesHelper.aesDecrypt(
  82. password,
  83. base64.decode(json['asymmetric_private_key'])
  84. );
  85. json['symmetric_key'] = base64.encode(CryptoUtils.rsaDecrypt(
  86. base64.decode(json['symmetric_key']),
  87. CryptoUtils.rsaPrivateKeyFromPem(json['asymmetric_private_key']),
  88. ));
  89. if (json['image_link'] != '') {
  90. File profileIcon = await getFile(
  91. '$defaultServerUrl/files/${['image_link']}',
  92. json['user_id'],
  93. json['symmetric_key'],
  94. );
  95. json['file'] = profileIcon.path;
  96. }
  97. MyProfile profile = MyProfile._fromJson(json);
  98. final preferences = await SharedPreferences.getInstance();
  99. preferences.setString('profile', profile.toJson());
  100. return profile;
  101. }
  102. static Future<void> logout() async {
  103. final preferences = await SharedPreferences.getInstance();
  104. preferences.remove('profile');
  105. }
  106. static Future<MyProfile> getProfile() async {
  107. final preferences = await SharedPreferences.getInstance();
  108. String? profileJson = preferences.getString('profile');
  109. if (profileJson == null) {
  110. throw Exception('No profile');
  111. }
  112. return MyProfile._fromJson(json.decode(profileJson));
  113. }
  114. static Future<bool> isLoggedIn() async {
  115. MyProfile profile = await MyProfile.getProfile();
  116. if (profile.loggedInAt == null) {
  117. return false;
  118. }
  119. return profile.loggedInAt!.add(const Duration(hours: 12)).isAfter(
  120. (DateTime.now())
  121. );
  122. }
  123. static Future<RSAPrivateKey> getPrivateKey() async {
  124. MyProfile profile = await MyProfile.getProfile();
  125. if (profile.privateKey == null) {
  126. throw Exception('Could not get privateKey');
  127. }
  128. return profile.privateKey!;
  129. }
  130. static setServerUrl(String url) async {
  131. final preferences = await SharedPreferences.getInstance();
  132. preferences.setString('server_url', url);
  133. }
  134. static Future<Uri> getServerUrl(String path) async {
  135. final preferences = await SharedPreferences.getInstance();
  136. String? baseUrl = preferences.getString('server_url');
  137. if (baseUrl == null) {
  138. setServerUrl(defaultServerUrl);
  139. return Uri.parse('$defaultServerUrl$path');
  140. }
  141. return Uri.parse('$baseUrl$path');
  142. }
  143. }