Add generatePassword; refactor GestureDetectors to CupertinoButton;

refactor hidden classes fields; refactor repositories to one library
This commit is contained in:
mitchell 2019-07-17 22:20:04 -04:00
parent 67744527cc
commit 5a42345c08
11 changed files with 293 additions and 204 deletions

View File

@ -1,8 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
import 'repositories/grpc_credentials_client.dart';
import 'repositories/encrypted_shared_preferences.dart';
import 'repositories/repositories.dart';
import 'screens/authentication.dart';
import 'screens/config.dart';

View File

@ -1,3 +1,5 @@
part of 'repositories.dart';
class ConfigBase {
static const keyPrivateKey = "private_key";
static const keyConnectionConfig = "connection_config";
@ -11,8 +13,6 @@ class ConfigBase {
return _password;
}
set password(String password) => _password = password;
void checkIfPasswordMatched() {
if (passwordMatched) return;
throw Exception('password not matched yet');

View File

@ -1,13 +1,4 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'config_base.dart';
import '../types/abstracts.dart';
import '../types/connection_config.dart';
import '../utils/crypto.dart' as crypto;
part of 'repositories.dart';
class EncryptedSharedPreferences extends ConfigBase implements ConfigRepo {
@override
@ -19,7 +10,7 @@ class EncryptedSharedPreferences extends ConfigBase implements ConfigRepo {
if (cipherText == null) return null;
final configJson = crypto.decrypt(cipherText, password);
final configJson = crypto.decrypt(cipherText, _password);
return ConnectionConfig.fromJson(json.decode(configJson));
}
@ -44,7 +35,7 @@ class EncryptedSharedPreferences extends ConfigBase implements ConfigRepo {
password,
);
if (passwordMatched) this.password = password;
if (passwordMatched) _password = password;
return passwordMatched;
}
@ -66,7 +57,7 @@ class EncryptedSharedPreferences extends ConfigBase implements ConfigRepo {
final prefs = await SharedPreferences.getInstance();
final cipherText = prefs.getString(ConfigBase.keyPrivateKey);
return crypto.decrypt(cipherText, password);
return crypto.decrypt(cipherText, _password);
}
@override
@ -79,7 +70,7 @@ class EncryptedSharedPreferences extends ConfigBase implements ConfigRepo {
prefs.setString(
ConfigBase.keyConnectionConfig,
crypto.encrypt(configJson, password),
crypto.encrypt(configJson, _password),
);
}
@ -87,7 +78,7 @@ class EncryptedSharedPreferences extends ConfigBase implements ConfigRepo {
Future<void> setPassword(String password) async {
final prefs = await SharedPreferences.getInstance();
this.password = password;
_password = password;
passwordMatched = true;
prefs.setString(ConfigBase.keyPassword, crypto.hashPassword(password));
@ -97,6 +88,6 @@ class EncryptedSharedPreferences extends ConfigBase implements ConfigRepo {
Future<void> setPrivateKey(String key) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString(ConfigBase.keyPrivateKey, crypto.encrypt(key, password));
prefs.setString(ConfigBase.keyPrivateKey, crypto.encrypt(key, _password));
}
}

View File

@ -1,14 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:grpc/grpc.dart';
import 'package:selfpass_protobuf/credentials.pbgrpc.dart' as grpc;
import 'package:selfpass_protobuf/credentials.pb.dart' as protobuf;
import '../types/abstracts.dart';
import '../types/connection_config.dart';
import '../types/credential.dart';
part of 'repositories.dart';
class GRPCCredentialsClient implements CredentialsRepo {
static GRPCCredentialsClient _instance;

View File

@ -0,0 +1,20 @@
library repositories;
import 'dart:convert';
import 'dart:async';
import 'dart:io';
import 'package:grpc/grpc.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:selfpass_protobuf/credentials.pbgrpc.dart' as grpc;
import 'package:selfpass_protobuf/credentials.pb.dart' as protobuf;
import '../types/abstracts.dart';
import '../types/connection_config.dart';
import '../types/credential.dart';
import '../utils/crypto.dart' as crypto;
part 'config_base.dart';
part 'encrypted_shared_preferences.dart';
part 'grpc_credentials_client.dart';

View File

@ -11,18 +11,18 @@ class Authentication extends StatefulWidget {
}
class _AuthenticationState extends State<Authentication> {
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _confirmController = TextEditingController();
bool _invalid = false;
bool _passesDontMatch = false;
ConfigRepo _config;
Future<bool> _passwordIsSet;
final TextEditingController passwordController = TextEditingController();
final TextEditingController confirmController = TextEditingController();
bool invalid = false;
bool passesDontMatch = false;
ConfigRepo config;
Future<bool> passwordIsSet;
@override
didChangeDependencies() {
super.didChangeDependencies();
_config = Provider.of<ConfigRepo>(context);
_passwordIsSet = _config.passwordIsSet;
config = Provider.of<ConfigRepo>(context);
passwordIsSet = config.passwordIsSet;
}
@override
@ -31,18 +31,18 @@ class _AuthenticationState extends State<Authentication> {
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 50.0),
child: FutureBuilder<bool>(
future: _passwordIsSet,
future: passwordIsSet,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) =>
snapshot.connectionState == ConnectionState.done
? Column(
children: _buildColumnChildren(context, snapshot.data))
children: buildColumnChildren(context, snapshot.data))
: Center(child: CupertinoActivityIndicator()),
),
),
);
}
List<Widget> _buildColumnChildren(BuildContext context, bool passwordIsSet) {
List<Widget> buildColumnChildren(BuildContext context, bool passwordIsSet) {
List<Widget> children = [
const Spacer(flex: 4),
const Flexible(child: Text('Master password:')),
@ -51,7 +51,7 @@ class _AuthenticationState extends State<Authentication> {
maxLines: 1,
obscure: true,
autofocus: true,
controller: _passwordController,
controller: passwordController,
),
),
];
@ -62,12 +62,12 @@ class _AuthenticationState extends State<Authentication> {
child: TextField(
maxLines: 1,
obscure: true,
controller: _confirmController,
controller: confirmController,
),
));
}
if (_invalid) {
if (invalid) {
children.add(const Flexible(
child: Text(
'invalid masterpass',
@ -76,7 +76,7 @@ class _AuthenticationState extends State<Authentication> {
));
}
if (_passesDontMatch) {
if (passesDontMatch) {
children.add(const Flexible(
child: Text(
'passwords don\'t match',
@ -85,14 +85,17 @@ class _AuthenticationState extends State<Authentication> {
));
}
children.add(CupertinoButton(
child: Text('Enter'),
onPressed: _buildEnterPressedBuilder(context),
children.add(Container(
padding: EdgeInsets.only(top: 20),
child: CupertinoButton.filled(
child: Text('Enter'),
onPressed: buildEnterPressedBuilder(context),
),
));
if (_passesDontMatch) {
if (passesDontMatch) {
children.add(const Spacer(flex: 1));
} else if (_invalid || !passwordIsSet) {
} else if (invalid || !passwordIsSet) {
children.add(const Spacer(flex: 2));
} else {
children.add(const Spacer(flex: 3));
@ -101,28 +104,28 @@ class _AuthenticationState extends State<Authentication> {
return children;
}
VoidCallback _buildEnterPressedBuilder(BuildContext context) {
VoidCallback buildEnterPressedBuilder(BuildContext context) {
return () async {
if (await _passwordIsSet) {
if (await _config.matchesPasswordHash(_passwordController.text)) {
if (await passwordIsSet) {
if (await config.matchesPasswordHash(passwordController.text)) {
Navigator.of(context).pushReplacementNamed('/home',
arguments: await _config.connectionConfig);
arguments: await config.connectionConfig);
return;
}
this.setState(() => _invalid = true);
this.setState(() => invalid = true);
return;
}
if (_passwordController.text != _confirmController.text) {
if (passwordController.text != confirmController.text) {
this.setState(() {
_passesDontMatch = true;
passesDontMatch = true;
});
return;
}
_config.setPassword(_passwordController.text);
_passwordIsSet = Future<bool>.value(true);
config.setPassword(passwordController.text);
passwordIsSet = Future<bool>.value(true);
Navigator.of(context).pushReplacementNamed('/config');
};
}

View File

@ -18,98 +18,106 @@ class Config extends StatefulWidget {
}
class _ConfigState extends State<Config> {
TextEditingController _hostController;
TextEditingController _caCertController;
TextEditingController _certController;
TextEditingController _privateCertController;
TextEditingController _privateKeyController;
ConnectionConfig _connectionConfig;
String _privateKey;
ConfigRepo _config;
TextEditingController hostController;
TextEditingController caCertController;
TextEditingController certController;
TextEditingController privateCertController;
TextEditingController privateKeyController;
ConnectionConfig connectionConfig;
String privateKey;
ConfigRepo config;
_ConfigState(this._connectionConfig, this._privateKey) {
if (_connectionConfig == null) {
_connectionConfig = ConnectionConfig();
_ConfigState(this.connectionConfig, this.privateKey) {
if (connectionConfig == null) {
connectionConfig = ConnectionConfig();
}
_hostController = TextEditingController(text: _connectionConfig.host);
_certController =
TextEditingController(text: _connectionConfig.certificate);
_caCertController =
TextEditingController(text: _connectionConfig.caCertificate);
_privateCertController =
TextEditingController(text: _connectionConfig.privateCertificate);
hostController = TextEditingController(text: connectionConfig.host);
certController = TextEditingController(text: connectionConfig.certificate);
caCertController =
TextEditingController(text: connectionConfig.caCertificate);
privateCertController =
TextEditingController(text: connectionConfig.privateCertificate);
_privateKeyController = TextEditingController(text: _privateKey);
privateKeyController = TextEditingController(text: privateKey);
}
@override
didChangeDependencies() async {
super.didChangeDependencies();
_config = Provider.of<ConfigRepo>(context);
config = Provider.of<ConfigRepo>(context);
}
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: _connectionConfig.host == null
? null
: CupertinoNavigationBar(
trailing: GestureDetector(
onTap: _buildResetAllHandler(context),
navigationBar: CupertinoNavigationBar(
trailing: connectionConfig?.host == null
? null
: CupertinoButton(
child: Text(
'Reset',
style: TextStyle(color: CupertinoColors.destructiveRed),
),
onPressed: buildResetAllHandler(context),
padding: EdgeInsets.zero,
),
),
),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 30),
child: ListView(children: [
Container(margin: EdgeInsets.only(top: 10), child: Text('Host:')),
TextField(maxLines: 1, controller: _hostController),
TextField(maxLines: 1, controller: hostController),
Container(
margin: EdgeInsets.only(top: 5), child: Text('Private key:')),
TextField(maxLines: 1, controller: _privateKeyController),
TextField(maxLines: 1, controller: privateKeyController),
Container(
margin: EdgeInsets.only(top: 5), child: Text('CA certificate:')),
TextField(maxLines: 5, controller: _caCertController),
TextField(maxLines: 5, controller: caCertController),
Container(
margin: EdgeInsets.only(top: 5),
child: Text('Client certificate:')),
TextField(maxLines: 5, controller: _certController),
TextField(maxLines: 5, controller: certController),
Container(
margin: EdgeInsets.only(top: 5),
child: Text('Private certificate:')),
TextField(maxLines: 5, controller: _privateCertController),
CupertinoButton(
child: Text('Save'), onPressed: _makeSaveOnPressed(context)),
TextField(maxLines: 5, controller: privateCertController),
Container(
padding: EdgeInsets.symmetric(vertical: 20),
margin: EdgeInsets.symmetric(horizontal: 70),
child: CupertinoButton.filled(
child: Text('Save'),
onPressed: makeSaveOnPressed(context),
),
),
]),
),
);
}
GestureTapCallback _buildResetAllHandler(BuildContext context) {
GestureTapCallback buildResetAllHandler(BuildContext context) {
return () {
showCupertinoDialog(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
content: Text('Are you sure?'),
content: Text(
'Are you sure you want to delete all config values and lock the app?',
),
actions: [
CupertinoDialogAction(
isDefaultAction: true,
child: Text('Cancel'),
child: Text('No'),
onPressed: () => Navigator.of(context).pop(),
),
CupertinoDialogAction(
isDestructiveAction: true,
child: Text('Confirm'),
child: Text('Yes'),
onPressed: () async {
_connectionConfig = null;
await _config.deleteAll();
connectionConfig = null;
await config.deleteAll();
Navigator.of(context)
.pushNamedAndRemoveUntil('/', ModalRoute.withName('/'));
.pushNamedAndRemoveUntil('/', ModalRoute.withName('/home'));
},
),
],
@ -118,17 +126,17 @@ class _ConfigState extends State<Config> {
};
}
VoidCallback _makeSaveOnPressed(BuildContext context) {
VoidCallback makeSaveOnPressed(BuildContext context) {
return () async {
final connConfig = ConnectionConfig(
host: _hostController.text,
certificate: _certController.text,
caCertificate: _caCertController.text,
privateCertificate: _privateCertController.text,
host: hostController.text,
certificate: certController.text,
caCertificate: caCertController.text,
privateCertificate: privateCertController.text,
);
await _config.setConnectionConfig(connConfig);
await _config.setPrivateKey(_privateKeyController.text);
await config.setConnectionConfig(connConfig);
await config.setPrivateKey(privateKeyController.text);
Navigator.of(context)
.pushReplacementNamed('/home', arguments: connConfig);

View File

@ -1,7 +1,9 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:otp/otp.dart';
import 'package:provider/provider.dart';
import '../types/abstracts.dart';
import '../types/credential.dart' as types;
import '../widgets/text_field.dart';
@ -16,26 +18,122 @@ class Credential extends StatefulWidget {
}
class _CredentialState extends State<Credential> {
_CredentialControllers _controllers;
Map<String, _FieldBuildConfig> _fieldMap;
types.Credential _credential;
_CredentialControllers controllers;
Map<String, _FieldBuildConfig> fieldMap;
types.Credential credential;
CredentialsRepo client;
_CredentialState(this._credential) : super();
_CredentialState(this.credential) : super() {
controllers = _CredentialControllers.fromCredential(credential);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
client = Provider.of<CredentialsRepo>(context);
}
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(),
navigationBar: CupertinoNavigationBar(
trailing: CupertinoButton(
child: Text(
'Delete',
style: TextStyle(color: CupertinoColors.destructiveRed),
),
onPressed: makeDeleteHandler(context),
padding: EdgeInsets.zero,
),
),
child: Container(
padding: const EdgeInsets.only(top: 15, bottom: 30, left: 30),
child: ListView(
children: _buildFieldRows(context),
children: buildFieldRows(context),
),
),
);
}
Map<String, _FieldBuildConfig> _buildFieldMap(
Function makeDeleteHandler(BuildContext context) {
return () {
showCupertinoDialog(
context: context,
builder: (BuildContext context) => CupertinoAlertDialog(
content: Text('Are you sure you want to delete this credential?'),
actions: <Widget>[
CupertinoDialogAction(
child: Text('No'),
isDefaultAction: true,
onPressed: () {
Navigator.of(context).pop();
},
),
CupertinoDialogAction(
child: Text('Yes'),
isDestructiveAction: true,
onPressed: () async {
await client.delete(credential.meta.id);
Navigator.of(context).pushNamedAndRemoveUntil(
'/home',
ModalRoute.withName('/home'),
);
},
),
],
),
);
};
}
List<Widget> buildFieldRows(BuildContext context) {
List<Widget> rows = [];
fieldMap = buildFieldMap(controllers, credential);
fieldMap.forEach((String prefix, _FieldBuildConfig config) {
rows.add(Container(
margin: EdgeInsets.only(top: 2.5),
child: Text(prefix, style: TextStyle(fontWeight: FontWeight.w600)),
));
final List<Widget> widgets = [
Expanded(
flex: 3,
child: config.mutable
? TextField(
maxLines: 1,
controller: config.controller,
obscure: config.obscured)
: Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Text(config.text)),
),
];
if (config.copyable) {
widgets.add(Expanded(
child: CupertinoButton(
child: Text(config.otp ? 'OTP' : 'Copy'),
onPressed: () => Clipboard.setData(ClipboardData(
text: config.otp
? OTP
.generateTOTPCode(config.controller.text,
DateTime.now().millisecondsSinceEpoch)
.toString()
: config.mutable ? config.controller.text : config.text,
)),
),
));
}
rows.add(Row(children: widgets));
});
return rows;
}
Map<String, _FieldBuildConfig> buildFieldMap(
_CredentialControllers controllers,
types.Credential credential,
) {
@ -55,76 +153,28 @@ class _CredentialState extends State<Credential> {
'Primary:': _FieldBuildConfig(controller: controllers.primary),
};
if (credential.meta.tag != null && credential.meta.tag != '') {
if (credential.meta.tag?.isNotEmpty ?? false) {
fieldMap['Tag'] = _FieldBuildConfig(controller: controllers.tag);
}
if (credential.username != null && credential.username != '') {
if (credential.username?.isNotEmpty ?? false) {
fieldMap['User:'] = _FieldBuildConfig(controller: controllers.username);
}
if (credential.email != null && credential.email != '') {
if (credential.email?.isNotEmpty ?? false) {
fieldMap['Email:'] = _FieldBuildConfig(controller: controllers.email);
}
fieldMap['Password:'] =
_FieldBuildConfig(controller: controllers.password, obscured: true);
if (credential.otpSecret != null && credential.otpSecret != '') {
fieldMap['OTP Secret:'] = _FieldBuildConfig(
if (credential.otpSecret?.isNotEmpty ?? false) {
fieldMap['OTP Key:'] = _FieldBuildConfig(
controller: controllers.otpSecret, obscured: true, otp: true);
}
return fieldMap;
}
List<Widget> _buildFieldRows(BuildContext context) {
List<Widget> rows = [];
_controllers = _CredentialControllers.fromCredential(_credential);
_fieldMap = _buildFieldMap(_controllers, _credential);
_fieldMap.forEach((key, value) {
rows.add(Container(
margin: EdgeInsets.only(top: 2.5),
child: Text(key, style: TextStyle(fontWeight: FontWeight.w600)),
));
final List<Widget> widgets = [
Expanded(
flex: 3,
child: value.mutable
? TextField(
maxLines: 1,
controller: value.controller,
obscure: value.obscured)
: Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Text(value.text)),
),
];
if (value.copyable) {
widgets.add(Expanded(
child: CupertinoButton(
child: Text(value.otp ? 'OTP' : 'Copy'),
onPressed: () => Clipboard.setData(ClipboardData(
text: value.otp
? OTP
.generateTOTPCode(value.controller.text,
DateTime.now().millisecondsSinceEpoch)
.toString()
: value.mutable ? value.controller.text : value.text,
)),
),
));
}
rows.add(Row(children: widgets));
});
return rows;
}
}
class _FieldBuildConfig {

View File

@ -17,11 +17,11 @@ class Home extends StatefulWidget {
}
class _HomeState extends State<Home> with WidgetsBindingObserver {
CredentialsRepo _client;
ConfigRepo _config;
Future<List<Metadata>> _metadatas;
bool _stateIsPaused = false;
Timer _pausedStateTimer;
CredentialsRepo client;
ConfigRepo config;
Future<List<Metadata>> metadatas;
bool stateIsPaused = false;
Timer pausedStateTimer;
@override
void initState() {
@ -33,49 +33,51 @@ class _HomeState extends State<Home> with WidgetsBindingObserver {
void didChangeDependencies() {
super.didChangeDependencies();
_config = Provider.of<ConfigRepo>(context);
_client = Provider.of<CredentialsRepo>(context);
config = Provider.of<ConfigRepo>(context);
client = Provider.of<CredentialsRepo>(context);
_metadatas = _client.getAllMetadata('').toList();
metadatas = client.getAllMetadata('').toList();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
_stateIsPaused = state == AppLifecycleState.paused;
stateIsPaused = state == AppLifecycleState.paused;
if (_stateIsPaused) {
_pausedStateTimer = _newPausedStateTimer();
if (stateIsPaused) {
pausedStateTimer = newPausedStateTimer();
return;
}
if (_pausedStateTimer != null) _pausedStateTimer.cancel();
if (pausedStateTimer != null) pausedStateTimer.cancel();
}
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
child: FutureBuilder<List<Metadata>>(
future: _metadatas,
future: metadatas,
builder: (
BuildContext context,
AsyncSnapshot<List<Metadata>> snapshot,
) =>
(snapshot.connectionState == ConnectionState.done)
? TappableTextList(
tappableText: _buildTappableText(context, snapshot.data))
tappableText: buildTappableText(context, snapshot.data))
: Center(child: CupertinoActivityIndicator()),
),
navigationBar: CupertinoNavigationBar(
leading: GestureDetector(
child: Align(
child: Text('Lock',
style: TextStyle(color: CupertinoColors.destructiveRed)),
alignment: Alignment(-0.9, 0)),
onTap: _makeLockOnTapHandler(context),
leading: CupertinoButton(
child: Text(
'Lock',
style: TextStyle(color: CupertinoColors.destructiveRed),
),
onPressed: makeLockOnTapHandler(context),
padding: EdgeInsets.zero,
),
trailing: GestureDetector(
trailing: CupertinoButton(
child: Icon(CupertinoIcons.gear),
onTap: _makeConfigOnTapHandler(context),
onPressed: makeConfigOnTapHandler(context),
padding: EdgeInsets.zero,
),
),
);
@ -84,21 +86,21 @@ class _HomeState extends State<Home> with WidgetsBindingObserver {
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
if (_pausedStateTimer != null) _pausedStateTimer.cancel();
if (pausedStateTimer != null) pausedStateTimer.cancel();
super.dispose();
}
Timer _newPausedStateTimer() {
Timer newPausedStateTimer() {
const checkPeriod = 30;
return Timer(Duration(seconds: checkPeriod), () {
_config.reset();
config.reset();
Navigator.of(context)
.pushNamedAndRemoveUntil('/', ModalRoute.withName('/home'));
});
}
Map<String, GestureTapCallback> _buildTappableText(
Map<String, GestureTapCallback> buildTappableText(
BuildContext context,
List<Metadata> metadatas,
) {
@ -127,18 +129,18 @@ class _HomeState extends State<Home> with WidgetsBindingObserver {
return tappableText;
}
GestureTapCallback _makeLockOnTapHandler(BuildContext context) {
GestureTapCallback makeLockOnTapHandler(BuildContext context) {
return () {
_config.reset();
config.reset();
Navigator.of(context)
.pushNamedAndRemoveUntil('/', ModalRoute.withName('/home'));
};
}
GestureTapCallback _makeConfigOnTapHandler(BuildContext context) {
GestureTapCallback makeConfigOnTapHandler(BuildContext context) {
return () async => Navigator.of(context).pushNamed('/config',
arguments: ConfigScreenArguments(
connectionConfig: await _config.connectionConfig,
privateKey: await _config.privateKey));
connectionConfig: await config.connectionConfig,
privateKey: await config.privateKey));
}
}

View File

@ -56,11 +56,13 @@ String encrypt(String plainText, String masterpass, [String privateKey]) {
);
final random = Random.secure();
final ivBytes = List<int>.generate(aesBlockSize, (_) => random.nextInt(byteIntMax));
final ivBytes =
List<int>.generate(aesBlockSize, (_) => random.nextInt(byteIntMax));
final iv = IV(Uint8List.fromList(ivBytes));
final encrypter = Encrypter(AES(Key(key), mode: AESMode.cbc));
final cipherBytes = List<int>.from(encrypter.encrypt(plainText, iv: iv).bytes);
final cipherBytes =
List<int>.from(encrypter.encrypt(plainText, iv: iv).bytes);
cipherBytes.insertAll(0, ivBytes);
if (privateKeyWasEmpty) {
@ -72,6 +74,30 @@ String encrypt(String plainText, String masterpass, [String privateKey]) {
return base64.encode(cipherBytes);
}
String generatePassword(int length,
[bool numbers = true, bool specials = true]) {
const alphaValues = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const numberValues = '1234567890';
const specialValues = '!@#\$%^&*()-_=+';
final random = Random.secure();
List<int> values;
if (numbers && specials) {
values = (alphaValues + numberValues + specialValues).codeUnits;
} else if (numbers) {
values = (alphaValues + numberValues).codeUnits;
} else if (specials) {
values = (alphaValues + specialValues).codeUnits;
}
final valuesLen = values.length;
final passValues =
List<int>.generate(length, (_) => values[random.nextInt(valuesLen)]);
return String.fromCharCodes(passValues);
}
const saltSize = 16;
const pbkdf2Rounds = 4096;
const keySize = 32;

View File

@ -35,7 +35,7 @@ packages:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
version: "1.0.4"
charcode:
dependency: transitive
description:
@ -108,7 +108,7 @@ packages:
name: googleapis_auth
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.8"
version: "0.2.10"
grpc:
dependency: "direct main"
description:
@ -178,7 +178,7 @@ packages:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0+1"
version: "1.7.0"
pointycastle:
dependency: transitive
description:
@ -192,7 +192,7 @@ packages:
name: protobuf
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.12"
version: "0.13.15"
provider:
dependency: "direct main"
description: