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.

151 lines
4.4 KiB

  1. import 'dart:io' show Platform;
  2. import 'dart:convert';
  3. import 'dart:typed_data';
  4. import 'package:firebase_messaging/firebase_messaging.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:http/http.dart' as http;
  7. import 'package:pointycastle/impl.dart';
  8. import '/components/custom_circle_avatar.dart';
  9. import '/data_models/user_search.dart';
  10. import '/database/models/my_profile.dart';
  11. import '/utils/encryption/aes_helper.dart';
  12. import '/utils/storage/session_cookie.dart';
  13. import '/utils/strings.dart';
  14. import '/utils/encryption/crypto_utils.dart';
  15. @immutable
  16. class UserSearchResult extends StatefulWidget {
  17. final UserSearch user;
  18. const UserSearchResult({
  19. Key? key,
  20. required this.user,
  21. }) : super(key: key);
  22. @override
  23. _UserSearchResultState createState() => _UserSearchResultState();
  24. }
  25. class _UserSearchResultState extends State<UserSearchResult>{
  26. bool showFailed = false;
  27. @override
  28. Widget build(BuildContext context) {
  29. return Center(
  30. child: Padding(
  31. padding: const EdgeInsets.only(top: 30),
  32. child: Column(
  33. crossAxisAlignment: CrossAxisAlignment.center,
  34. children: <Widget>[
  35. CustomCircleAvatar(
  36. initials: widget.user.username[0].toUpperCase(),
  37. icon: const Icon(Icons.person, size: 80),
  38. radius: 50,
  39. ),
  40. const SizedBox(height: 10),
  41. Text(
  42. widget.user.username,
  43. style: const TextStyle(
  44. fontSize: 35,
  45. ),
  46. ),
  47. const SizedBox(height: 30),
  48. TextButton(
  49. onPressed: sendFriendRequest,
  50. child: Text(
  51. 'Send Friend Request',
  52. style: TextStyle(
  53. color: Theme.of(context).colorScheme.onPrimary,
  54. fontSize: 20,
  55. ),
  56. ),
  57. style: ButtonStyle(
  58. backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.primary),
  59. padding: MaterialStateProperty.all<EdgeInsets>(
  60. const EdgeInsets.only(left: 20, right: 20, top: 8, bottom: 8)),
  61. ),
  62. ),
  63. showFailed ? const SizedBox(height: 20) : const SizedBox.shrink(),
  64. failedMessage(context),
  65. ],
  66. ),
  67. ),
  68. );
  69. }
  70. Widget failedMessage(BuildContext context) {
  71. if (!showFailed) {
  72. return const SizedBox.shrink();
  73. }
  74. return Text(
  75. 'Failed to send friend request',
  76. style: TextStyle(
  77. color: Theme.of(context).colorScheme.error,
  78. fontSize: 16,
  79. ),
  80. );
  81. }
  82. Future<void> sendFriendRequest() async {
  83. MyProfile profile = await MyProfile.getProfile();
  84. String publicKeyString = CryptoUtils.encodeRSAPublicKeyToPem(profile.publicKey!);
  85. RSAPublicKey friendPublicKey = CryptoUtils.rsaPublicKeyFromPem(widget.user.publicKey);
  86. final symmetricKey = AesHelper.deriveKey(generateRandomString(32));
  87. String? token = await FirebaseMessaging.instance.getToken();
  88. String payloadJson = jsonEncode({
  89. 'user_id': widget.user.id,
  90. 'friend_id': base64.encode(CryptoUtils.rsaEncrypt(
  91. Uint8List.fromList(profile.id.codeUnits),
  92. friendPublicKey,
  93. )),
  94. 'friend_username': base64.encode(CryptoUtils.rsaEncrypt(
  95. Uint8List.fromList(profile.username.codeUnits),
  96. friendPublicKey,
  97. )),
  98. 'symmetric_key': base64.encode(CryptoUtils.rsaEncrypt(
  99. Uint8List.fromList(symmetricKey),
  100. friendPublicKey,
  101. )),
  102. 'asymmetric_public_key': AesHelper.aesEncrypt(
  103. symmetricKey,
  104. Uint8List.fromList(publicKeyString.codeUnits),
  105. ),
  106. 'tokens': token == null ? {} : [{
  107. 'device_token': {
  108. 'device_type': base64.encode(CryptoUtils.rsaEncrypt(
  109. Uint8List.fromList((Platform.isAndroid ? 'android' : 'ios').codeUnits),
  110. friendPublicKey,
  111. )),
  112. 'token': base64.encode(CryptoUtils.rsaEncrypt(
  113. Uint8List.fromList(token.codeUnits),
  114. friendPublicKey,
  115. )),
  116. }
  117. }],
  118. });
  119. var resp = await http.post(
  120. await MyProfile.getServerUrl('api/v1/auth/friend_request'),
  121. headers: {
  122. 'cookie': await getSessionCookie(),
  123. },
  124. body: payloadJson,
  125. );
  126. if (resp.statusCode != 200) {
  127. showFailed = true;
  128. setState(() {});
  129. return;
  130. }
  131. Navigator.pop(context);
  132. }
  133. }