mirror of https://github.com/mitchell/selfpass.git
Add aes-cbc encryption; add config repo based on shared_preferences
This commit is contained in:
parent
80f9705b19
commit
67744527cc
30
README.md
30
README.md
|
@ -41,12 +41,12 @@ The newest addition to the *selfpass* project is the client built using Flutter,
|
||||||
capable of targeting to iOS, Android, and Desktop. It supports all the same features as the CLI tool
|
capable of targeting to iOS, Android, and Desktop. It supports all the same features as the CLI tool
|
||||||
using GUIs, with all the same safety and encryption as the CLI.
|
using GUIs, with all the same safety and encryption as the CLI.
|
||||||
|
|
||||||
| Goal | Progress | Comment |
|
| Goal | Progress | Comment |
|
||||||
| --- | :---: | --- |
|
| --- | :---: | --- |
|
||||||
| Support mutual TLS. | 100% | |
|
| Support mutual TLS. | 100% | |
|
||||||
| Support credentials CRUD via gRPC. | 25% | TODO: CUD |
|
| Support credentials CRUD via gRPC. | 25% | TODO: CUD |
|
||||||
| Support storage of certs, PK, and host in shared preferences, encrypted. | 100% | |
|
| Support storage of certs, PK, and host in shared preferences, encrypted. | 100% | |
|
||||||
| Support AES-CBC encryption of passes and OTP secrets, using MP and PK. | 50% | TODO: decryption |
|
| Support AES-CBC encryption of passes and OTP secrets, using MP and PK. | 100% | |
|
||||||
|
|
||||||
## Other Info
|
## Other Info
|
||||||
|
|
||||||
|
@ -58,12 +58,12 @@ using GUIs, with all the same safety and encryption as the CLI.
|
||||||
|
|
||||||
**Architectural 3rd-party Technologies in Use (and where)**
|
**Architectural 3rd-party Technologies in Use (and where)**
|
||||||
|
|
||||||
- Golang (services & protobuf)
|
- Golang: services, sp, & protobuf
|
||||||
- Dart (client & protobuf)
|
- Dart: client & protobuf
|
||||||
- Flutter (client)
|
- Flutter: client
|
||||||
- Go-Kit (services)
|
- Go-Kit: services
|
||||||
- gRPC & Protobuf (all)
|
- gRPC/Protobuf: all
|
||||||
- Cobra Commander & Viper Config (spc)
|
- Cobra Commander & Viper Config: sp
|
||||||
- Redis (services)
|
- Redis: services
|
||||||
- Docker (services)
|
- Docker: services
|
||||||
- Debian (docker images and machines)
|
- Debian: docker images & machines
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
PODS:
|
PODS:
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
- flutter_secure_storage (3.2.0):
|
- shared_preferences (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Flutter (from `.symlinks/flutter/ios`)
|
- Flutter (from `.symlinks/flutter/ios`)
|
||||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: ".symlinks/flutter/ios"
|
:path: ".symlinks/flutter/ios"
|
||||||
flutter_secure_storage:
|
shared_preferences:
|
||||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
:path: ".symlinks/plugins/shared_preferences/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a
|
Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a
|
||||||
flutter_secure_storage: 0c5779648ff644110e507909b77a57e620cbbf8b
|
shared_preferences: 1feebfa37bb57264736e16865e7ffae7fc99b523
|
||||||
|
|
||||||
PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09
|
PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09
|
||||||
|
|
||||||
|
|
|
@ -2,24 +2,26 @@ import 'package:flutter/cupertino.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'repositories/grpc_credentials_client.dart';
|
import 'repositories/grpc_credentials_client.dart';
|
||||||
import 'repositories/secure_storage_config.dart';
|
import 'repositories/encrypted_shared_preferences.dart';
|
||||||
|
|
||||||
import 'screens/authentication.dart';
|
import 'screens/authentication.dart';
|
||||||
|
import 'screens/config.dart';
|
||||||
import 'screens/credential.dart';
|
import 'screens/credential.dart';
|
||||||
import 'screens/credentials.dart';
|
import 'screens/credentials.dart';
|
||||||
import 'screens/config.dart';
|
|
||||||
import 'screens/home.dart';
|
import 'screens/home.dart';
|
||||||
|
|
||||||
import 'types/abstracts.dart';
|
import 'types/abstracts.dart';
|
||||||
import 'types/screen_arguments.dart';
|
import 'types/screen_arguments.dart';
|
||||||
|
|
||||||
void main() => runApp(Selfpass());
|
void main() {
|
||||||
|
runApp(Selfpass());
|
||||||
|
}
|
||||||
|
|
||||||
class Selfpass extends StatelessWidget {
|
class Selfpass extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Provider<ConfigRepo>(
|
return Provider<ConfigRepo>(
|
||||||
builder: (BuildContext context) => SecureStorageConfig(),
|
builder: (BuildContext context) => EncryptedSharedPreferences(),
|
||||||
child: CupertinoApp(
|
child: CupertinoApp(
|
||||||
title: 'Selfpass',
|
title: 'Selfpass',
|
||||||
onGenerateRoute: (RouteSettings settings) {
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
@ -36,7 +38,8 @@ class Selfpass extends StatelessWidget {
|
||||||
title = 'Hosts';
|
title = 'Hosts';
|
||||||
builder = (BuildContext context) => Provider<CredentialsRepo>(
|
builder = (BuildContext context) => Provider<CredentialsRepo>(
|
||||||
builder: (BuildContext context) =>
|
builder: (BuildContext context) =>
|
||||||
GRPCCredentialsClient.getInstance(config: settings.arguments),
|
GRPCCredentialsClient.getInstance(
|
||||||
|
config: settings.arguments),
|
||||||
child: Home(),
|
child: Home(),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
class ConfigBase {
|
||||||
|
static const keyPrivateKey = "private_key";
|
||||||
|
static const keyConnectionConfig = "connection_config";
|
||||||
|
static const keyPassword = "password";
|
||||||
|
|
||||||
|
bool passwordMatched = false;
|
||||||
|
String _password;
|
||||||
|
|
||||||
|
String get password {
|
||||||
|
checkIfPasswordMatched();
|
||||||
|
return _password;
|
||||||
|
}
|
||||||
|
|
||||||
|
set password(String password) => _password = password;
|
||||||
|
|
||||||
|
void checkIfPasswordMatched() {
|
||||||
|
if (passwordMatched) return;
|
||||||
|
throw Exception('password not matched yet');
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
passwordMatched = false;
|
||||||
|
_password = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
class EncryptedSharedPreferences extends ConfigBase implements ConfigRepo {
|
||||||
|
@override
|
||||||
|
Future<ConnectionConfig> get connectionConfig async {
|
||||||
|
checkIfPasswordMatched();
|
||||||
|
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final cipherText = prefs.getString(ConfigBase.keyConnectionConfig);
|
||||||
|
|
||||||
|
if (cipherText == null) return null;
|
||||||
|
|
||||||
|
final configJson = crypto.decrypt(cipherText, password);
|
||||||
|
|
||||||
|
return ConnectionConfig.fromJson(json.decode(configJson));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteAll() async {
|
||||||
|
checkIfPasswordMatched();
|
||||||
|
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
prefs.remove(ConfigBase.keyConnectionConfig);
|
||||||
|
prefs.remove(ConfigBase.keyPassword);
|
||||||
|
prefs.remove(ConfigBase.keyPrivateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> matchesPasswordHash(String password) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
passwordMatched = crypto.matchHashedPassword(
|
||||||
|
prefs.getString(ConfigBase.keyPassword),
|
||||||
|
password,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (passwordMatched) this.password = password;
|
||||||
|
|
||||||
|
return passwordMatched;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> get passwordIsSet async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
final isSet = prefs.containsKey(ConfigBase.keyPassword);
|
||||||
|
passwordMatched = !isSet;
|
||||||
|
|
||||||
|
return isSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> get privateKey async {
|
||||||
|
checkIfPasswordMatched();
|
||||||
|
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final cipherText = prefs.getString(ConfigBase.keyPrivateKey);
|
||||||
|
|
||||||
|
return crypto.decrypt(cipherText, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setConnectionConfig(ConnectionConfig config) async {
|
||||||
|
checkIfPasswordMatched();
|
||||||
|
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
final configJson = json.encode(config);
|
||||||
|
|
||||||
|
prefs.setString(
|
||||||
|
ConfigBase.keyConnectionConfig,
|
||||||
|
crypto.encrypt(configJson, password),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setPassword(String password) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
this.password = password;
|
||||||
|
passwordMatched = true;
|
||||||
|
|
||||||
|
prefs.setString(ConfigBase.keyPassword, crypto.hashPassword(password));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setPrivateKey(String key) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
prefs.setString(ConfigBase.keyPrivateKey, crypto.encrypt(key, password));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,91 +0,0 @@
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
||||||
|
|
||||||
import '../types/abstracts.dart';
|
|
||||||
import '../types/connection_config.dart';
|
|
||||||
|
|
||||||
import '../utils/crypto.dart' as crypto;
|
|
||||||
|
|
||||||
class SecureStorageConfig implements ConfigRepo {
|
|
||||||
static const _keyPrivateKey = "private_key";
|
|
||||||
static const _keyConnectionConfig = "connection_config";
|
|
||||||
static const _keyPassword = "password";
|
|
||||||
|
|
||||||
final _storage = FlutterSecureStorage();
|
|
||||||
|
|
||||||
bool _passwordMatched = false;
|
|
||||||
String _password;
|
|
||||||
|
|
||||||
String get password {
|
|
||||||
_checkIfPasswordMatched();
|
|
||||||
return _password;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> setPrivateKey(String key) {
|
|
||||||
_checkIfPasswordMatched();
|
|
||||||
return _storage.write(key: _keyPrivateKey, value: key.replaceAll('-', ''));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> get privateKey {
|
|
||||||
_checkIfPasswordMatched();
|
|
||||||
return _storage.read(key: _keyPrivateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> setPassword(String password) {
|
|
||||||
_checkIfPasswordMatched();
|
|
||||||
|
|
||||||
_password = password;
|
|
||||||
|
|
||||||
return _storage.write(
|
|
||||||
key: _keyPassword, value: crypto.hashPassword(password));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> get passwordIsSet async {
|
|
||||||
final passHash = await _storage.read(key: _keyPassword);
|
|
||||||
|
|
||||||
if (passHash != null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_passwordMatched = true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> matchesPasswordHash(String password) async {
|
|
||||||
_passwordMatched = crypto.matchHashedPassword(
|
|
||||||
await _storage.read(key: _keyPassword), password);
|
|
||||||
|
|
||||||
if (_passwordMatched) _password = password;
|
|
||||||
|
|
||||||
return _passwordMatched;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> setConnectionConfig(ConnectionConfig config) {
|
|
||||||
_checkIfPasswordMatched();
|
|
||||||
return _storage.write(
|
|
||||||
key: _keyConnectionConfig, value: json.encode(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<ConnectionConfig> get connectionConfig async {
|
|
||||||
_checkIfPasswordMatched();
|
|
||||||
final connConfig = await _storage.read(key: _keyConnectionConfig);
|
|
||||||
|
|
||||||
if (connConfig == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ConnectionConfig.fromJson(json.decode(connConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deleteAll() {
|
|
||||||
_checkIfPasswordMatched();
|
|
||||||
return _storage.deleteAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _checkIfPasswordMatched() {
|
|
||||||
if (_passwordMatched) return;
|
|
||||||
throw Exception('password not matched yet');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -58,8 +58,10 @@ class _ConfigState extends State<Config> {
|
||||||
: CupertinoNavigationBar(
|
: CupertinoNavigationBar(
|
||||||
trailing: GestureDetector(
|
trailing: GestureDetector(
|
||||||
onTap: _buildResetAllHandler(context),
|
onTap: _buildResetAllHandler(context),
|
||||||
child: Text('Reset App',
|
child: Text(
|
||||||
style: TextStyle(color: CupertinoColors.destructiveRed)),
|
'Reset',
|
||||||
|
style: TextStyle(color: CupertinoColors.destructiveRed),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
|
@ -30,8 +30,9 @@ class Credentials extends StatelessWidget {
|
||||||
children: [
|
children: [
|
||||||
Text('Decrypting credential...'),
|
Text('Decrypting credential...'),
|
||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.only(top: 10),
|
margin: EdgeInsets.only(top: 10),
|
||||||
child: CupertinoActivityIndicator()),
|
child: CupertinoActivityIndicator(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
@ -44,12 +45,18 @@ class Credentials extends StatelessWidget {
|
||||||
|
|
||||||
final credential = await client.get(id);
|
final credential = await client.get(id);
|
||||||
|
|
||||||
credential.password = crypto.decryptPassword(
|
credential.password = crypto.decrypt(
|
||||||
password, await privateKey, credential.password);
|
credential.password,
|
||||||
|
password,
|
||||||
|
await privateKey,
|
||||||
|
);
|
||||||
|
|
||||||
if (credential.otpSecret != null && credential.otpSecret != '') {
|
if (credential.otpSecret.isNotEmpty) {
|
||||||
credential.otpSecret = crypto.decryptPassword(
|
credential.otpSecret = crypto.decrypt(
|
||||||
password, await privateKey, credential.otpSecret);
|
credential.otpSecret,
|
||||||
|
password,
|
||||||
|
await privateKey,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
|
|
|
@ -92,6 +92,7 @@ class _HomeState extends State<Home> with WidgetsBindingObserver {
|
||||||
const checkPeriod = 30;
|
const checkPeriod = 30;
|
||||||
|
|
||||||
return Timer(Duration(seconds: checkPeriod), () {
|
return Timer(Duration(seconds: checkPeriod), () {
|
||||||
|
_config.reset();
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.pushNamedAndRemoveUntil('/', ModalRoute.withName('/home'));
|
.pushNamedAndRemoveUntil('/', ModalRoute.withName('/home'));
|
||||||
});
|
});
|
||||||
|
@ -127,7 +128,11 @@ class _HomeState extends State<Home> with WidgetsBindingObserver {
|
||||||
}
|
}
|
||||||
|
|
||||||
GestureTapCallback _makeLockOnTapHandler(BuildContext context) {
|
GestureTapCallback _makeLockOnTapHandler(BuildContext context) {
|
||||||
return () => Navigator.of(context).pushReplacementNamed('/');
|
return () {
|
||||||
|
_config.reset();
|
||||||
|
Navigator.of(context)
|
||||||
|
.pushNamedAndRemoveUntil('/', ModalRoute.withName('/home'));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
GestureTapCallback _makeConfigOnTapHandler(BuildContext context) {
|
GestureTapCallback _makeConfigOnTapHandler(BuildContext context) {
|
||||||
|
|
|
@ -24,4 +24,6 @@ abstract class ConfigRepo {
|
||||||
Future<ConnectionConfig> get connectionConfig;
|
Future<ConnectionConfig> get connectionConfig;
|
||||||
|
|
||||||
Future<void> deleteAll();
|
Future<void> deleteAll();
|
||||||
|
|
||||||
|
void reset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,37 @@
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:crypt/crypt.dart';
|
import 'package:crypt/crypt.dart';
|
||||||
import 'package:encrypt/encrypt.dart';
|
import 'package:encrypt/encrypt.dart';
|
||||||
import 'package:password_hash/password_hash.dart';
|
import 'package:password_hash/password_hash.dart';
|
||||||
|
|
||||||
String hashPassword(String password) {
|
String hashPassword(String password) {
|
||||||
const saltSize = 16;
|
final salt = Salt.generateAsBase64String(saltSize);
|
||||||
const saltIntMax = 256;
|
|
||||||
|
|
||||||
final random = Random.secure();
|
|
||||||
final saltInts =
|
|
||||||
List<int>.generate(saltSize, (_) => random.nextInt(saltIntMax));
|
|
||||||
final salt = base64.encode(saltInts);
|
|
||||||
|
|
||||||
return Crypt.sha256(password, salt: salt).toString();
|
return Crypt.sha256(password, salt: salt).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool matchHashedPassword(String hashedPassword, String password) =>
|
bool matchHashedPassword(String hashedPassword, String password) =>
|
||||||
Crypt(hashedPassword).match(password);
|
Crypt(hashedPassword).match(password);
|
||||||
|
|
||||||
String decryptPassword(String masterpass, privateKey, cipherText) {
|
String decrypt(String cipherText, String masterpass, [String privateKey]) {
|
||||||
|
var cipherBytes = base64.decode(cipherText);
|
||||||
|
|
||||||
|
if (privateKey == null) {
|
||||||
|
final saltLength = cipherBytes[0];
|
||||||
|
cipherBytes = cipherBytes.sublist(1);
|
||||||
|
|
||||||
|
privateKey = base64.encode(cipherBytes.sublist(0, saltLength));
|
||||||
|
cipherBytes = cipherBytes.sublist(saltLength);
|
||||||
|
}
|
||||||
|
|
||||||
final key = PBKDF2().generateKey(
|
final key = PBKDF2().generateKey(
|
||||||
masterpass, privateKey, pbkdf2Rounds, keySize,
|
masterpass,
|
||||||
|
privateKey,
|
||||||
|
pbkdf2Rounds,
|
||||||
|
keySize,
|
||||||
);
|
);
|
||||||
|
|
||||||
var cipherBytes = base64.decode(cipherText);
|
|
||||||
final ivBytes = cipherBytes.sublist(0, aesBlockSize);
|
final ivBytes = cipherBytes.sublist(0, aesBlockSize);
|
||||||
cipherBytes = cipherBytes.sublist(aesBlockSize);
|
cipherBytes = cipherBytes.sublist(aesBlockSize);
|
||||||
|
|
||||||
|
@ -34,6 +40,40 @@ String decryptPassword(String masterpass, privateKey, cipherText) {
|
||||||
return encrypter.decrypt(Encrypted(cipherBytes), iv: iv);
|
return encrypter.decrypt(Encrypted(cipherBytes), iv: iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String encrypt(String plainText, String masterpass, [String privateKey]) {
|
||||||
|
bool privateKeyWasEmpty = false;
|
||||||
|
|
||||||
|
if (privateKey == null) {
|
||||||
|
privateKey = Salt.generateAsBase64String(saltSize);
|
||||||
|
privateKeyWasEmpty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final key = PBKDF2().generateKey(
|
||||||
|
masterpass,
|
||||||
|
privateKey,
|
||||||
|
pbkdf2Rounds,
|
||||||
|
keySize,
|
||||||
|
);
|
||||||
|
|
||||||
|
final random = Random.secure();
|
||||||
|
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);
|
||||||
|
cipherBytes.insertAll(0, ivBytes);
|
||||||
|
|
||||||
|
if (privateKeyWasEmpty) {
|
||||||
|
final base64PrivKey = base64.decode(privateKey);
|
||||||
|
cipherBytes.insertAll(0, base64PrivKey);
|
||||||
|
cipherBytes.insert(0, base64PrivKey.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.encode(cipherBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
const saltSize = 16;
|
||||||
const pbkdf2Rounds = 4096;
|
const pbkdf2Rounds = 4096;
|
||||||
const keySize = 32;
|
const keySize = 32;
|
||||||
const aesBlockSize = 16;
|
const aesBlockSize = 16;
|
||||||
|
const byteIntMax = 256;
|
||||||
|
|
|
@ -35,7 +35,7 @@ packages:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "1.0.5"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -97,13 +97,6 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_secure_storage:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: flutter_secure_storage
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "3.2.1+1"
|
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -185,7 +178,7 @@ packages:
|
||||||
name: pedantic
|
name: pedantic
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.8.0+1"
|
||||||
pointycastle:
|
pointycastle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -221,6 +214,13 @@ packages:
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.3"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -284,3 +284,4 @@ packages:
|
||||||
version: "2.0.8"
|
version: "2.0.8"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.2.2 <3.0.0"
|
dart: ">=2.2.2 <3.0.0"
|
||||||
|
flutter: ">=0.1.4 <2.0.0"
|
||||||
|
|
|
@ -28,10 +28,12 @@ dependencies:
|
||||||
provider: ^3.0.0
|
provider: ^3.0.0
|
||||||
|
|
||||||
crypt: ^1.0.7
|
crypt: ^1.0.7
|
||||||
flutter_secure_storage: ^3.2.1
|
|
||||||
password_hash: ^2.0.0
|
password_hash: ^2.0.0
|
||||||
encrypt: ^3.2.0
|
encrypt: ^3.2.0
|
||||||
|
|
||||||
|
# flutter_secure_storage: ^3.2.1
|
||||||
|
shared_preferences: 0.5.3
|
||||||
|
|
||||||
otp: ^1.0.3
|
otp: ^1.0.3
|
||||||
|
|
||||||
selfpass_protobuf:
|
selfpass_protobuf:
|
||||||
|
|
|
@ -57,7 +57,10 @@ func (c credentialsClient) GetAllMetadata(ctx context.Context, sourceHost string
|
||||||
Errors: errch,
|
Errors: errch,
|
||||||
})
|
})
|
||||||
|
|
||||||
srv, err := c.client.GetAllMetadata(ctx, &protobuf.SourceHostRequest{SourceHost: sourceHost})
|
srv, err := c.client.GetAllMetadata(
|
||||||
|
ctx,
|
||||||
|
transport.EncodeSourceHostRequest(endpoints.SourceHostRequest{SourceHost: sourceHost}),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errch <- err
|
errch <- err
|
||||||
return nil, errch
|
return nil, errch
|
||||||
|
|
|
@ -17,11 +17,10 @@ func decodeSourceHostRequest(ctx context.Context, request interface{}) (interfac
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func EncodeSourceHostRequest(ctx context.Context, request interface{}) (interface{}, error) {
|
func EncodeSourceHostRequest(request endpoints.SourceHostRequest) *protobuf.SourceHostRequest {
|
||||||
r := request.(endpoints.SourceHostRequest)
|
return &protobuf.SourceHostRequest{
|
||||||
return protobuf.SourceHostRequest{
|
SourceHost: request.SourceHost,
|
||||||
SourceHost: r.SourceHost,
|
}
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeMetadataStreamResponse(ctx context.Context, response interface{}) (interface{}, error) {
|
func encodeMetadataStreamResponse(ctx context.Context, response interface{}) (interface{}, error) {
|
||||||
|
|
Loading…
Reference in New Issue