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.

175 lines
4.4 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:Envelope/components/custom_title_bar.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:pointycastle/impl.dart';
  6. import 'package:qr_code_scanner/qr_code_scanner.dart';
  7. import 'package:sqflite/sqflite.dart';
  8. import 'package:uuid/uuid.dart';
  9. import 'package:http/http.dart' as http;
  10. import '/models/friends.dart';
  11. import '/models/my_profile.dart';
  12. import '/utils/encryption/aes_helper.dart';
  13. import '/utils/encryption/crypto_utils.dart';
  14. import '/utils/storage/database.dart';
  15. import '/utils/strings.dart';
  16. import '/utils/storage/session_cookie.dart';
  17. import 'flash_message.dart';
  18. class QrReader extends StatefulWidget {
  19. const QrReader({
  20. Key? key,
  21. }) : super(key: key);
  22. @override
  23. State<QrReader> createState() => _QrReaderState();
  24. }
  25. class _QrReaderState extends State<QrReader> {
  26. final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
  27. Barcode? result;
  28. QRViewController? controller;
  29. // In order to get hot reload to work we need to pause the camera if the platform
  30. // is android, or resume the camera if the platform is iOS.
  31. @override
  32. void reassemble() {
  33. super.reassemble();
  34. if (Platform.isAndroid) {
  35. controller!.pauseCamera();
  36. } else if (Platform.isIOS) {
  37. controller!.resumeCamera();
  38. }
  39. }
  40. @override
  41. Widget build(BuildContext context) {
  42. return Scaffold(
  43. appBar: CustomTitleBar(
  44. title: Text(
  45. 'Add Friend',
  46. style: TextStyle(
  47. fontSize: 16,
  48. fontWeight: FontWeight.w600,
  49. color: Theme.of(context).appBarTheme.toolbarTextStyle?.color
  50. )
  51. ),
  52. showBack: true,
  53. backgroundColor: Colors.transparent,
  54. ),
  55. body: Column(
  56. children: <Widget>[
  57. Expanded(
  58. flex: 5,
  59. child: QRView(
  60. key: qrKey,
  61. onQRViewCreated: _onQRViewCreated,
  62. formatsAllowed: const [BarcodeFormat.qrcode],
  63. overlay: QrScannerOverlayShape(),
  64. ),
  65. ),
  66. ],
  67. ),
  68. );
  69. }
  70. void _onQRViewCreated(QRViewController controller) {
  71. this.controller = controller;
  72. controller.scannedDataStream.listen((scanData) {
  73. addFriend(scanData)
  74. .then((dynamic ret) {
  75. if (ret) {
  76. // Delay exit to prevent exit mid way through rendering
  77. Future.delayed(Duration.zero, () {
  78. Navigator.of(context).pop();
  79. });
  80. }
  81. });
  82. });
  83. }
  84. @override
  85. void dispose() {
  86. controller?.dispose();
  87. super.dispose();
  88. }
  89. Future<bool> addFriend(Barcode scanData) async {
  90. Map<String, dynamic> friendJson = jsonDecode(scanData.code!);
  91. RSAPublicKey publicKey = CryptoUtils.rsaPublicKeyFromPem(
  92. String.fromCharCodes(
  93. base64.decode(
  94. friendJson['k']
  95. )
  96. )
  97. );
  98. MyProfile profile = await MyProfile.getProfile();
  99. var uuid = const Uuid();
  100. final symmetricKey1 = AesHelper.deriveKey(generateRandomString(32));
  101. final symmetricKey2 = AesHelper.deriveKey(generateRandomString(32));
  102. Friend request1 = Friend(
  103. id: uuid.v4(),
  104. userId: friendJson['i'],
  105. username: profile.username,
  106. friendId: profile.id,
  107. friendSymmetricKey: base64.encode(symmetricKey1),
  108. publicKey: profile.publicKey!,
  109. acceptedAt: DateTime.now(),
  110. );
  111. Friend request2 = Friend(
  112. id: uuid.v4(),
  113. userId: profile.id,
  114. friendId: friendJson['i'],
  115. username: friendJson['u'],
  116. friendSymmetricKey: base64.encode(symmetricKey2),
  117. publicKey: publicKey,
  118. acceptedAt: DateTime.now(),
  119. );
  120. String payload = jsonEncode([
  121. request1.payloadJson(),
  122. request2.payloadJson(),
  123. ]);
  124. var resp = await http.post(
  125. await MyProfile.getServerUrl('api/v1/auth/friend_request/qr_code'),
  126. headers: <String, String>{
  127. 'Content-Type': 'application/json; charset=UTF-8',
  128. 'cookie': await getSessionCookie(),
  129. },
  130. body: payload,
  131. );
  132. if (resp.statusCode != 200) {
  133. showMessage(
  134. 'Failed to add friend, please try again later',
  135. context
  136. );
  137. return false;
  138. }
  139. final db = await getDatabaseConnection();
  140. await db.insert(
  141. 'friends',
  142. request1.toMap(),
  143. conflictAlgorithm: ConflictAlgorithm.replace,
  144. );
  145. await db.insert(
  146. 'friends',
  147. request2.toMap(),
  148. conflictAlgorithm: ConflictAlgorithm.replace,
  149. );
  150. return true;
  151. }
  152. }