mirror of
				https://github.com/mitchell/selfpass.git
				synced 2025-10-31 06:05:26 +00:00 
			
		
		
		
	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 | ||||
| using GUIs, with all the same safety and encryption as the CLI. | ||||
| 
 | ||||
| | Goal                                                                     | Progress | Comment          | | ||||
| | ---                                                                      | :---:    | ---              | | ||||
| | Support mutual TLS.                                                      | 100%     |                  | | ||||
| | Support credentials CRUD via gRPC.                                       | 25%      | TODO: CUD        | | ||||
| | 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 | | ||||
| | Goal                                                                     | Progress | Comment   | | ||||
| | ---                                                                      | :---:    | ---       | | ||||
| | Support mutual TLS.                                                      | 100%     |           | | ||||
| | Support credentials CRUD via gRPC.                                       | 25%      | TODO: CUD | | ||||
| | 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.   | 100%     |           | | ||||
| 
 | ||||
| ## 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)** | ||||
| 
 | ||||
| - Golang (services & protobuf) | ||||
| - Dart (client & protobuf) | ||||
| - Flutter (client) | ||||
| - Go-Kit (services) | ||||
| - gRPC & Protobuf (all) | ||||
| - Cobra Commander & Viper Config (spc) | ||||
| - Redis (services) | ||||
| - Docker (services) | ||||
| - Debian (docker images and machines) | ||||
| - Golang: services, sp, & protobuf | ||||
| - Dart: client & protobuf | ||||
| - Flutter: client | ||||
| - Go-Kit: services | ||||
| - gRPC/Protobuf: all | ||||
| - Cobra Commander & Viper Config: sp | ||||
| - Redis: services | ||||
| - Docker: services | ||||
| - Debian: docker images & machines | ||||
|  |  | |||
|  | @ -1,21 +1,21 @@ | |||
| PODS: | ||||
|   - Flutter (1.0.0) | ||||
|   - flutter_secure_storage (3.2.0): | ||||
|   - shared_preferences (0.0.1): | ||||
|     - Flutter | ||||
| 
 | ||||
| DEPENDENCIES: | ||||
|   - 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: | ||||
|   Flutter: | ||||
|     :path: ".symlinks/flutter/ios" | ||||
|   flutter_secure_storage: | ||||
|     :path: ".symlinks/plugins/flutter_secure_storage/ios" | ||||
|   shared_preferences: | ||||
|     :path: ".symlinks/plugins/shared_preferences/ios" | ||||
| 
 | ||||
| SPEC CHECKSUMS: | ||||
|   Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a | ||||
|   flutter_secure_storage: 0c5779648ff644110e507909b77a57e620cbbf8b | ||||
|   shared_preferences: 1feebfa37bb57264736e16865e7ffae7fc99b523 | ||||
| 
 | ||||
| PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09 | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,24 +2,26 @@ import 'package:flutter/cupertino.dart'; | |||
| import 'package:provider/provider.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/config.dart'; | ||||
| import 'screens/credential.dart'; | ||||
| import 'screens/credentials.dart'; | ||||
| import 'screens/config.dart'; | ||||
| import 'screens/home.dart'; | ||||
| 
 | ||||
| import 'types/abstracts.dart'; | ||||
| import 'types/screen_arguments.dart'; | ||||
| 
 | ||||
| void main() => runApp(Selfpass()); | ||||
| void main() { | ||||
|   runApp(Selfpass()); | ||||
| } | ||||
| 
 | ||||
| class Selfpass extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Provider<ConfigRepo>( | ||||
|       builder: (BuildContext context) => SecureStorageConfig(), | ||||
|       builder: (BuildContext context) => EncryptedSharedPreferences(), | ||||
|       child: CupertinoApp( | ||||
|         title: 'Selfpass', | ||||
|         onGenerateRoute: (RouteSettings settings) { | ||||
|  | @ -36,7 +38,8 @@ class Selfpass extends StatelessWidget { | |||
|               title = 'Hosts'; | ||||
|               builder = (BuildContext context) => Provider<CredentialsRepo>( | ||||
|                     builder: (BuildContext context) => | ||||
|                         GRPCCredentialsClient.getInstance(config: settings.arguments), | ||||
|                         GRPCCredentialsClient.getInstance( | ||||
|                             config: settings.arguments), | ||||
|                     child: Home(), | ||||
|                   ); | ||||
|               break; | ||||
|  |  | |||
							
								
								
									
										25
									
								
								client/lib/repositories/config_base.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								client/lib/repositories/config_base.dart
									
									
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										102
									
								
								client/lib/repositories/encrypted_shared_preferences.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								client/lib/repositories/encrypted_shared_preferences.dart
									
									
									
									
									
										Normal file
									
								
							|  | @ -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( | ||||
|               trailing: GestureDetector( | ||||
|                 onTap: _buildResetAllHandler(context), | ||||
|                 child: Text('Reset App', | ||||
|                     style: TextStyle(color: CupertinoColors.destructiveRed)), | ||||
|                 child: Text( | ||||
|                   'Reset', | ||||
|                   style: TextStyle(color: CupertinoColors.destructiveRed), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|       child: Container( | ||||
|  |  | |||
|  | @ -30,8 +30,9 @@ class Credentials extends StatelessWidget { | |||
|               children: [ | ||||
|                 Text('Decrypting credential...'), | ||||
|                 Container( | ||||
|                     margin: EdgeInsets.only(top: 10), | ||||
|                     child: CupertinoActivityIndicator()), | ||||
|                   margin: EdgeInsets.only(top: 10), | ||||
|                   child: CupertinoActivityIndicator(), | ||||
|                 ), | ||||
|               ], | ||||
|             )), | ||||
|           ); | ||||
|  | @ -44,12 +45,18 @@ class Credentials extends StatelessWidget { | |||
| 
 | ||||
|           final credential = await client.get(id); | ||||
| 
 | ||||
|           credential.password = crypto.decryptPassword( | ||||
|               password, await privateKey, credential.password); | ||||
|           credential.password = crypto.decrypt( | ||||
|             credential.password, | ||||
|             password, | ||||
|             await privateKey, | ||||
|           ); | ||||
| 
 | ||||
|           if (credential.otpSecret != null && credential.otpSecret != '') { | ||||
|             credential.otpSecret = crypto.decryptPassword( | ||||
|                 password, await privateKey, credential.otpSecret); | ||||
|           if (credential.otpSecret.isNotEmpty) { | ||||
|             credential.otpSecret = crypto.decrypt( | ||||
|               credential.otpSecret, | ||||
|               password, | ||||
|               await privateKey, | ||||
|             ); | ||||
|           } | ||||
| 
 | ||||
|           Navigator.of(context) | ||||
|  |  | |||
|  | @ -92,6 +92,7 @@ class _HomeState extends State<Home> with WidgetsBindingObserver { | |||
|     const checkPeriod = 30; | ||||
| 
 | ||||
|     return Timer(Duration(seconds: checkPeriod), () { | ||||
|       _config.reset(); | ||||
|       Navigator.of(context) | ||||
|           .pushNamedAndRemoveUntil('/', ModalRoute.withName('/home')); | ||||
|     }); | ||||
|  | @ -127,7 +128,11 @@ class _HomeState extends State<Home> with WidgetsBindingObserver { | |||
|   } | ||||
| 
 | ||||
|   GestureTapCallback _makeLockOnTapHandler(BuildContext context) { | ||||
|     return () => Navigator.of(context).pushReplacementNamed('/'); | ||||
|     return () { | ||||
|       _config.reset(); | ||||
|       Navigator.of(context) | ||||
|           .pushNamedAndRemoveUntil('/', ModalRoute.withName('/home')); | ||||
|     }; | ||||
|   } | ||||
| 
 | ||||
|   GestureTapCallback _makeConfigOnTapHandler(BuildContext context) { | ||||
|  |  | |||
|  | @ -24,4 +24,6 @@ abstract class ConfigRepo { | |||
|   Future<ConnectionConfig> get connectionConfig; | ||||
| 
 | ||||
|   Future<void> deleteAll(); | ||||
| 
 | ||||
|   void reset(); | ||||
| } | ||||
|  |  | |||
|  | @ -1,31 +1,37 @@ | |||
| import 'dart:math'; | ||||
| import 'dart:convert'; | ||||
| import 'dart:math'; | ||||
| import 'dart:typed_data'; | ||||
| 
 | ||||
| import 'package:crypt/crypt.dart'; | ||||
| import 'package:encrypt/encrypt.dart'; | ||||
| import 'package:password_hash/password_hash.dart'; | ||||
| 
 | ||||
| String hashPassword(String password) { | ||||
|   const saltSize = 16; | ||||
|   const saltIntMax = 256; | ||||
| 
 | ||||
|   final random = Random.secure(); | ||||
|   final saltInts = | ||||
|   List<int>.generate(saltSize, (_) => random.nextInt(saltIntMax)); | ||||
|   final salt = base64.encode(saltInts); | ||||
| 
 | ||||
|   final salt = Salt.generateAsBase64String(saltSize); | ||||
|   return Crypt.sha256(password, salt: salt).toString(); | ||||
| } | ||||
| 
 | ||||
| bool matchHashedPassword(String hashedPassword, String 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( | ||||
|     masterpass, privateKey, pbkdf2Rounds, keySize, | ||||
|     masterpass, | ||||
|     privateKey, | ||||
|     pbkdf2Rounds, | ||||
|     keySize, | ||||
|   ); | ||||
| 
 | ||||
|   var cipherBytes = base64.decode(cipherText); | ||||
|   final ivBytes = cipherBytes.sublist(0, aesBlockSize); | ||||
|   cipherBytes = cipherBytes.sublist(aesBlockSize); | ||||
| 
 | ||||
|  | @ -34,6 +40,40 @@ String decryptPassword(String masterpass, privateKey, cipherText) { | |||
|   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 keySize = 32; | ||||
| const aesBlockSize = 16; | ||||
| const byteIntMax = 256; | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ packages: | |||
|       name: boolean_selector | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.0.4" | ||||
|     version: "1.0.5" | ||||
|   charcode: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -97,13 +97,6 @@ packages: | |||
|     description: flutter | ||||
|     source: sdk | ||||
|     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: | ||||
|     dependency: "direct dev" | ||||
|     description: flutter | ||||
|  | @ -185,7 +178,7 @@ packages: | |||
|       name: pedantic | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.7.0" | ||||
|     version: "1.8.0+1" | ||||
|   pointycastle: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|  | @ -221,6 +214,13 @@ packages: | |||
|       relative: true | ||||
|     source: path | ||||
|     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: | ||||
|     dependency: transitive | ||||
|     description: flutter | ||||
|  | @ -284,3 +284,4 @@ packages: | |||
|     version: "2.0.8" | ||||
| sdks: | ||||
|   dart: ">=2.2.2 <3.0.0" | ||||
|   flutter: ">=0.1.4 <2.0.0" | ||||
|  |  | |||
|  | @ -28,10 +28,12 @@ dependencies: | |||
|   provider: ^3.0.0 | ||||
| 
 | ||||
|   crypt: ^1.0.7 | ||||
|   flutter_secure_storage: ^3.2.1 | ||||
|   password_hash: ^2.0.0 | ||||
|   encrypt: ^3.2.0 | ||||
| 
 | ||||
|   # flutter_secure_storage: ^3.2.1 | ||||
|   shared_preferences: 0.5.3 | ||||
| 
 | ||||
|   otp: ^1.0.3 | ||||
| 
 | ||||
|   selfpass_protobuf: | ||||
|  |  | |||
|  | @ -57,7 +57,10 @@ func (c credentialsClient) GetAllMetadata(ctx context.Context, sourceHost string | |||
| 		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 { | ||||
| 		errch <- err | ||||
| 		return nil, errch | ||||
|  |  | |||
|  | @ -17,11 +17,10 @@ func decodeSourceHostRequest(ctx context.Context, request interface{}) (interfac | |||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func EncodeSourceHostRequest(ctx context.Context, request interface{}) (interface{}, error) { | ||||
| 	r := request.(endpoints.SourceHostRequest) | ||||
| 	return protobuf.SourceHostRequest{ | ||||
| 		SourceHost: r.SourceHost, | ||||
| 	}, nil | ||||
| func EncodeSourceHostRequest(request endpoints.SourceHostRequest) *protobuf.SourceHostRequest { | ||||
| 	return &protobuf.SourceHostRequest{ | ||||
| 		SourceHost: request.SourceHost, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func encodeMetadataStreamResponse(ctx context.Context, response interface{}) (interface{}, error) { | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue