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.

218 lines
11 KiB

  1. import 'dart:typed_data';
  2. import 'dart:convert';
  3. import 'package:flutter/material.dart';
  4. import 'package:http/http.dart' as http;
  5. import 'package:shared_preferences/shared_preferences.dart';
  6. import '/utils/encryption/aes_helper.dart';
  7. import '/utils/storage/encryption_keys.dart';
  8. import '/utils/encryption/crypto_utils.dart';
  9. class SignupResponse {
  10. final String status;
  11. final String message;
  12. const SignupResponse({
  13. required this.status,
  14. required this.message,
  15. });
  16. factory SignupResponse.fromJson(Map<String, dynamic> json) {
  17. return SignupResponse(
  18. status: json['status'],
  19. message: json['message'],
  20. );
  21. }
  22. }
  23. Future<SignupResponse> signUp(context, String username, String password, String confirmPassword) async {
  24. var keyPair = CryptoUtils.generateRSAKeyPair();
  25. var rsaPubPem = CryptoUtils.encodeRSAPublicKeyToPem(keyPair.publicKey);
  26. var rsaPrivPem = CryptoUtils.encodeRSAPrivateKeyToPem(keyPair.privateKey);
  27. var encRsaPriv = AesHelper.aesEncrypt(password, Uint8List.fromList(rsaPrivPem.codeUnits));
  28. // TODO: Check for timeout here
  29. final resp = await http.post(
  30. Uri.parse('http://192.168.1.5:8080/api/v1/signup'),
  31. headers: <String, String>{
  32. 'Content-Type': 'application/json; charset=UTF-8',
  33. },
  34. body: jsonEncode(<String, String>{
  35. 'username': username,
  36. 'password': password,
  37. 'confirm_password': confirmPassword,
  38. 'asymmetric_public_key': rsaPubPem,
  39. 'asymmetric_private_key': encRsaPriv,
  40. }),
  41. );
  42. SignupResponse response = SignupResponse.fromJson(jsonDecode(resp.body));
  43. if (resp.statusCode != 201) {
  44. throw Exception(response.message);
  45. }
  46. debugPrint(rsaPubPem);
  47. debugPrint(rsaPrivPem);
  48. debugPrint(resp.body);
  49. return response;
  50. }
  51. class Signup extends StatelessWidget {
  52. const Signup({Key? key}) : super(key: key);
  53. static const String _title = 'Envelope';
  54. @override
  55. Widget build(BuildContext context) {
  56. return Scaffold(
  57. backgroundColor: Colors.cyan,
  58. appBar: AppBar(
  59. title: null,
  60. automaticallyImplyLeading: true,
  61. //`true` if you want Flutter to automatically add Back Button when needed,
  62. //or `false` if you want to force your own back button every where
  63. leading: IconButton(icon: const Icon(Icons.arrow_back),
  64. //onPressed:() => Navigator.pop(context, false),
  65. onPressed:() => {
  66. Navigator.pop(context)
  67. }
  68. )
  69. ),
  70. body: const SafeArea(
  71. child: SignupWidget(),
  72. )
  73. );
  74. }
  75. }
  76. class SignupWidget extends StatefulWidget {
  77. const SignupWidget({Key? key}) : super(key: key);
  78. @override
  79. State<SignupWidget> createState() => _SignupWidgetState();
  80. }
  81. class _SignupWidgetState extends State<SignupWidget> {
  82. final _formKey = GlobalKey<FormState>();
  83. TextEditingController usernameController = TextEditingController();
  84. TextEditingController passwordController = TextEditingController();
  85. TextEditingController passwordConfirmController = TextEditingController();
  86. @override
  87. Widget build(BuildContext context) {
  88. const TextStyle _inputTextStyle = TextStyle(fontSize: 18, color: Colors.black);
  89. final ButtonStyle _buttonStyle = ElevatedButton.styleFrom(
  90. primary: Colors.white,
  91. onPrimary: Colors.cyan,
  92. minimumSize: const Size.fromHeight(50),
  93. padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
  94. textStyle: const TextStyle(
  95. fontSize: 20,
  96. fontWeight: FontWeight.bold,
  97. color: Colors.red,
  98. ),
  99. );
  100. return Center(
  101. child: Form(
  102. key: _formKey,
  103. child: Center(
  104. child: Padding(
  105. padding: const EdgeInsets.all(15),
  106. child: Column(
  107. mainAxisAlignment: MainAxisAlignment.center,
  108. crossAxisAlignment: CrossAxisAlignment.center,
  109. children: [
  110. const Text('Sign Up', style: TextStyle(fontSize: 35, color: Colors.white),),
  111. const SizedBox(height: 30),
  112. TextFormField(
  113. controller: usernameController,
  114. decoration: const InputDecoration(
  115. hintText: 'Username',
  116. ),
  117. style: _inputTextStyle,
  118. // The validator receives the text that the user has entered.
  119. validator: (value) {
  120. if (value == null || value.isEmpty) {
  121. return 'Create a username';
  122. }
  123. return null;
  124. },
  125. ),
  126. const SizedBox(height: 5),
  127. TextFormField(
  128. controller: passwordController,
  129. obscureText: true,
  130. enableSuggestions: false,
  131. autocorrect: false,
  132. decoration: const InputDecoration(
  133. hintText: 'Password',
  134. ),
  135. style: _inputTextStyle,
  136. // The validator receives the text that the user has entered.
  137. validator: (value) {
  138. if (value == null || value.isEmpty) {
  139. return 'Enter a password';
  140. }
  141. return null;
  142. },
  143. ),
  144. const SizedBox(height: 5),
  145. TextFormField(
  146. controller: passwordConfirmController,
  147. obscureText: true,
  148. enableSuggestions: false,
  149. autocorrect: false,
  150. decoration: const InputDecoration(
  151. hintText: 'Password',
  152. ),
  153. style: _inputTextStyle,
  154. // The validator receives the text that the user has entered.
  155. validator: (value) {
  156. if (value == null || value.isEmpty) {
  157. return 'Confirm your password';
  158. }
  159. if (value != passwordController.text) {
  160. return 'Passwords do not match';
  161. }
  162. return null;
  163. },
  164. ),
  165. const SizedBox(height: 5),
  166. ElevatedButton(
  167. style: _buttonStyle,
  168. onPressed: () {
  169. if (_formKey.currentState!.validate()) {
  170. ScaffoldMessenger.of(context).showSnackBar(
  171. const SnackBar(content: Text('Processing Data')),
  172. );
  173. signUp(
  174. context,
  175. usernameController.text,
  176. passwordController.text,
  177. passwordConfirmController.text
  178. ).then((value) {
  179. Navigator.of(context).popUntil((route) => route.isFirst);
  180. }).catchError((error) {
  181. print(error); // TODO: Show error on interface
  182. });
  183. }
  184. },
  185. child: const Text('Submit'),
  186. ),
  187. ],
  188. )
  189. )
  190. )
  191. )
  192. );
  193. }
  194. }