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.

221 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 '/views/main/conversation_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 Scaffold(
  58. backgroundColor: Colors.cyan,
  59. appBar: AppBar(
  60. title: null,
  61. automaticallyImplyLeading: true,
  62. //`true` if you want Flutter to automatically add Back Button when needed,
  63. //or `false` if you want to force your own back button every where
  64. leading: IconButton(icon: const Icon(Icons.arrow_back),
  65. //onPressed:() => Navigator.pop(context, false),
  66. onPressed:() => {
  67. Navigator.pop(context)
  68. }
  69. )
  70. ),
  71. body: const SafeArea(
  72. child: SignupWidget(),
  73. )
  74. );
  75. }
  76. }
  77. class SignupWidget extends StatefulWidget {
  78. const SignupWidget({Key? key}) : super(key: key);
  79. @override
  80. State<SignupWidget> createState() => _SignupWidgetState();
  81. }
  82. class _SignupWidgetState extends State<SignupWidget> {
  83. final _formKey = GlobalKey<FormState>();
  84. TextEditingController usernameController = TextEditingController();
  85. TextEditingController passwordController = TextEditingController();
  86. TextEditingController passwordConfirmController = TextEditingController();
  87. @override
  88. Widget build(BuildContext context) {
  89. const TextStyle _inputTextStyle = TextStyle(fontSize: 18, color: Colors.black);
  90. final ButtonStyle _buttonStyle = ElevatedButton.styleFrom(
  91. primary: Colors.white,
  92. onPrimary: Colors.cyan,
  93. minimumSize: const Size.fromHeight(50),
  94. padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
  95. textStyle: const TextStyle(
  96. fontSize: 20,
  97. fontWeight: FontWeight.bold,
  98. color: Colors.red,
  99. ),
  100. );
  101. return Center(
  102. child: Form(
  103. key: _formKey,
  104. child: Center(
  105. child: Padding(
  106. padding: const EdgeInsets.all(15),
  107. child: Column(
  108. mainAxisAlignment: MainAxisAlignment.center,
  109. crossAxisAlignment: CrossAxisAlignment.center,
  110. children: [
  111. const Text('Sign Up', style: TextStyle(fontSize: 35, color: Colors.white),),
  112. const SizedBox(height: 30),
  113. TextFormField(
  114. controller: usernameController,
  115. decoration: const InputDecoration(
  116. hintText: 'Username',
  117. ),
  118. style: _inputTextStyle,
  119. // The validator receives the text that the user has entered.
  120. validator: (value) {
  121. if (value == null || value.isEmpty) {
  122. return 'Create a username';
  123. }
  124. return null;
  125. },
  126. ),
  127. const SizedBox(height: 5),
  128. TextFormField(
  129. controller: passwordController,
  130. obscureText: true,
  131. enableSuggestions: false,
  132. autocorrect: false,
  133. decoration: const InputDecoration(
  134. hintText: 'Password',
  135. ),
  136. style: _inputTextStyle,
  137. // The validator receives the text that the user has entered.
  138. validator: (value) {
  139. if (value == null || value.isEmpty) {
  140. return 'Enter a password';
  141. }
  142. return null;
  143. },
  144. ),
  145. const SizedBox(height: 5),
  146. TextFormField(
  147. controller: passwordConfirmController,
  148. obscureText: true,
  149. enableSuggestions: false,
  150. autocorrect: false,
  151. decoration: const InputDecoration(
  152. hintText: 'Password',
  153. ),
  154. style: _inputTextStyle,
  155. // The validator receives the text that the user has entered.
  156. validator: (value) {
  157. if (value == null || value.isEmpty) {
  158. return 'Confirm your password';
  159. }
  160. if (value != passwordController.text) {
  161. return 'Passwords do not match';
  162. }
  163. return null;
  164. },
  165. ),
  166. const SizedBox(height: 5),
  167. ElevatedButton(
  168. style: _buttonStyle,
  169. onPressed: () {
  170. if (_formKey.currentState!.validate()) {
  171. ScaffoldMessenger.of(context).showSnackBar(
  172. const SnackBar(content: Text('Processing Data')),
  173. );
  174. signUp(
  175. context,
  176. usernameController.text,
  177. passwordController.text,
  178. passwordConfirmController.text
  179. ).then((value) {
  180. Navigator.of(context).popUntil((route) => route.isFirst);
  181. }).catchError((error) {
  182. print(error); // TODO: Show error on interface
  183. });
  184. }
  185. },
  186. child: const Text('Submit'),
  187. ),
  188. ],
  189. )
  190. )
  191. )
  192. )
  193. );
  194. }
  195. }