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.

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