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.

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