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.

163 lines
4.1 KiB

  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:Envelope/utils/storage/session_cookie.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_dotenv/flutter_dotenv.dart';
  6. import 'package:pointycastle/impl.dart';
  7. import 'package:qr_code_scanner/qr_code_scanner.dart';
  8. import 'package:sqflite/sqflite.dart';
  9. import 'package:uuid/uuid.dart';
  10. import 'package:http/http.dart' as http;
  11. import '/models/friends.dart';
  12. import '/models/my_profile.dart';
  13. import '/utils/encryption/aes_helper.dart';
  14. import '/utils/encryption/crypto_utils.dart';
  15. import '/utils/storage/database.dart';
  16. import '/utils/strings.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. body: Column(
  44. children: <Widget>[
  45. Expanded(
  46. flex: 5,
  47. child: QRView(
  48. key: qrKey,
  49. onQRViewCreated: _onQRViewCreated,
  50. formatsAllowed: const [BarcodeFormat.qrcode],
  51. overlay: QrScannerOverlayShape(),
  52. ),
  53. ),
  54. ],
  55. ),
  56. );
  57. }
  58. void _onQRViewCreated(QRViewController controller) {
  59. this.controller = controller;
  60. controller.scannedDataStream.listen((scanData) {
  61. addFriend(scanData)
  62. .then((dynamic ret) {
  63. if (ret) {
  64. // Delay exit to prevent exit mid way through rendering
  65. Future.delayed(Duration.zero, () {
  66. Navigator.of(context).pop();
  67. });
  68. }
  69. });
  70. });
  71. }
  72. @override
  73. void dispose() {
  74. controller?.dispose();
  75. super.dispose();
  76. }
  77. Future<bool> addFriend(Barcode scanData) async {
  78. Map<String, dynamic> friendJson = jsonDecode(scanData.code!);
  79. RSAPublicKey publicKey = CryptoUtils.rsaPublicKeyFromPem(
  80. String.fromCharCodes(
  81. base64.decode(
  82. friendJson['k']
  83. )
  84. )
  85. );
  86. MyProfile profile = await MyProfile.getProfile();
  87. var uuid = const Uuid();
  88. final symmetricKey1 = AesHelper.deriveKey(generateRandomString(32));
  89. final symmetricKey2 = AesHelper.deriveKey(generateRandomString(32));
  90. Friend request1 = Friend(
  91. id: uuid.v4(),
  92. userId: friendJson['i'],
  93. username: profile.username,
  94. friendId: profile.id,
  95. friendSymmetricKey: base64.encode(symmetricKey1),
  96. publicKey: profile.publicKey!,
  97. acceptedAt: DateTime.now(),
  98. );
  99. Friend request2 = Friend(
  100. id: uuid.v4(),
  101. userId: profile.id,
  102. friendId: friendJson['i'],
  103. username: friendJson['u'],
  104. friendSymmetricKey: base64.encode(symmetricKey2),
  105. publicKey: publicKey,
  106. acceptedAt: DateTime.now(),
  107. );
  108. String payload = jsonEncode([
  109. request1.payloadJson(),
  110. request2.payloadJson(),
  111. ]);
  112. var resp = await http.post(
  113. Uri.parse('${dotenv.env["SERVER_URL"]}api/v1/auth/friend_request/qr_code'),
  114. headers: <String, String>{
  115. 'Content-Type': 'application/json; charset=UTF-8',
  116. 'cookie': await getSessionCookie(),
  117. },
  118. body: payload,
  119. );
  120. if (resp.statusCode != 200) {
  121. showMessage(
  122. 'Failed to add friend, please try again later',
  123. context
  124. );
  125. return false;
  126. }
  127. final db = await getDatabaseConnection();
  128. await db.insert(
  129. 'friends',
  130. request1.toMap(),
  131. conflictAlgorithm: ConflictAlgorithm.replace,
  132. );
  133. await db.insert(
  134. 'friends',
  135. request2.toMap(),
  136. conflictAlgorithm: ConflictAlgorithm.replace,
  137. );
  138. return true;
  139. }
  140. }