|
|
|
@ -1,10 +1,13 @@
|
|
|
|
|
import 'dart:convert'; |
|
|
|
|
import 'dart:typed_data'; |
|
|
|
|
|
|
|
|
|
import 'package:logging/logging.dart'; |
|
|
|
|
import 'package:crypto/crypto.dart'; |
|
|
|
|
import 'package:cryptography/cryptography.dart' as cryptography; |
|
|
|
|
import 'package:pointycastle/export.dart'; |
|
|
|
|
|
|
|
|
|
final _logger = Logger('protected_salt_generator'); |
|
|
|
|
|
|
|
|
|
class ProtectedSaltGenerator { |
|
|
|
|
factory ProtectedSaltGenerator(Uint8List key) { |
|
|
|
|
final hash = sha256.convert(key).bytes as Uint8List; |
|
|
|
@ -24,6 +27,10 @@ class ProtectedSaltGenerator {
|
|
|
|
|
|
|
|
|
|
String decryptBase64(String protectedValue) { |
|
|
|
|
final bytes = base64.decode(protectedValue); |
|
|
|
|
if (bytes.isEmpty) { |
|
|
|
|
_logger.warning('decoded base64 data has length 0'); |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
final result = _cipher.process(bytes); |
|
|
|
|
final decrypted = utf8.decode(result); |
|
|
|
|
return decrypted; |
|
|
|
@ -36,34 +43,54 @@ class ProtectedSaltGenerator {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
class ChachaProtectedSaltGenerator implements ProtectedSaltGenerator { |
|
|
|
|
ChachaProtectedSaltGenerator._(this._secretKey, this._nonce); |
|
|
|
|
ChachaProtectedSaltGenerator._(this._secretKey, this._nonce, this._state); |
|
|
|
|
|
|
|
|
|
factory ChachaProtectedSaltGenerator.create(Uint8List key) { |
|
|
|
|
final hash = sha512.convert(key); |
|
|
|
|
final secretKey = hash.bytes.sublist(0, 32); |
|
|
|
|
final nonce = hash.bytes.sublist(32, 32 + 12); |
|
|
|
|
|
|
|
|
|
return ChachaProtectedSaltGenerator._( |
|
|
|
|
cryptography.SecretKey(secretKey), cryptography.SecretKey(nonce)); |
|
|
|
|
cryptography.SecretKey(secretKey), |
|
|
|
|
cryptography.SecretKey(nonce), |
|
|
|
|
cryptography.chacha20.newState(cryptography.SecretKey(secretKey), |
|
|
|
|
nonce: cryptography.SecretKey(nonce))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final cryptography.SecretKey _secretKey; |
|
|
|
|
final cryptography.SecretKey _nonce; |
|
|
|
|
final cryptography.KeyStreamCipherState _state; |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
StreamCipher get _cipher => throw UnimplementedError(); |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
String decryptBase64(String protectedValue) { |
|
|
|
|
final result = cryptography.chacha20 |
|
|
|
|
.decrypt(base64.decode(protectedValue), _secretKey, nonce: _nonce); |
|
|
|
|
return utf8.decode(result); |
|
|
|
|
final bytes = base64.decode(protectedValue); |
|
|
|
|
if (bytes.isEmpty) { |
|
|
|
|
_logger.warning('decoded base64 data has length 0'); |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
final result = _state.convert(bytes); |
|
|
|
|
// try { |
|
|
|
|
_logger.fine('decoding protected value.'); |
|
|
|
|
final ret = utf8.decode(result); |
|
|
|
|
_logger.fine('Successfully decoded stuff.'); |
|
|
|
|
return ret; |
|
|
|
|
// } on FormatException catch (e, stackTrace) { |
|
|
|
|
// final ret = utf8.decode(result, allowMalformed: true); |
|
|
|
|
// _logger.severe( |
|
|
|
|
// 'Error while decoding utf8. ignoring malformed. result: {$ret}', |
|
|
|
|
// e, |
|
|
|
|
// stackTrace); |
|
|
|
|
// return ret; |
|
|
|
|
// } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
String encryptToBase64(String plainValue) { |
|
|
|
|
final input = utf8.encode(plainValue) as Uint8List; |
|
|
|
|
final encrypted = |
|
|
|
|
cryptography.chacha20.encrypt(input, _secretKey, nonce: _nonce); |
|
|
|
|
final encrypted = _state.convert(input); |
|
|
|
|
return base64.encode(encrypted); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|