Comments (11)
Arg. Indeed that's not good. And impossible to say what the cause is here as it may simply be a password issue. If you have a way to send me the data and the password or if you could debug it yourself, that could be a great first investigation test. I'll try a repro on my side as well.
from sembast.dart.
After some tests, I found that indeed xxtea gives different results on debug and release!
xxtea.encryptToString('value', 'test')
gives 'NMvXe7aiJfgaFlsx' on debug and something different (randomly) on release (but that could be possible). But it also cannot decrypt the debug value:
xxtea.decryptToString('NMvXe7aiJfgaFlsx', 'test')
gives something different each time ('Falue', '\nalue'...) where the first letter is messed up. I don't use xxtea personnally. I have used it as a sample here. I filled an issue here xxtea/xxtea-dart#2
from sembast.dart.
Thanks again for your example as I was able to use it almost as is in my tests (besides the password length issue). I remove some features (Salsa20 only) and used sha256 to generate the password - that seems to work, right?) and kept it as small as possible to act as an example.
https://github.com/tekartik/sembast.dart/blob/master/sembast/test/encrypt_codec.dart
I removed all the references to xxtea.
from sembast.dart.
Thank you very much for your feedback and prompt reply... I was going to dig deeper in the weekend, but you have found the issue already. Well let's wait for his reply :)
from sembast.dart.
well, almost 2 weeks and the developer of xxtea didn't reply... so I'd suggest to use another source of encryption, maybe this?
I've tried and it works. You can easily add the codec or any user could do it, it's pretty straightforward. If anyone needs to encrypt for a release, you can use this helper temporary and open the database like
await dbFactory.openDatabase(dbPath,codec: getEncryptSembastCodec(password: pass););
PS: the default encryption is Salsa20 (you will need a key of 16 chars). You can use AES instead of Salsa20 but keep mind you'll need a key of 32 chars.
import 'dart:convert';
import 'package:meta/meta.dart';
import 'package:sembast/sembast.dart';
import 'package:encrypt/encrypt.dart';
class _EncryptEncoder extends Converter<Map<String, dynamic>, String> {
final String key;
final String signature;
_EncryptEncoder(this.key, this.signature);
@override
String convert(Map<String, dynamic> input) {
String encoded;
switch (signature) {
case "Salsa20":
encoded = Encrypter(Salsa20(Key.fromUtf8(key)))
.encrypt(json.encode(input), iv: IV.fromLength(8))
.base64;
break;
case "AES":
encoded = Encrypter(AES(Key.fromUtf8(key)))
.encrypt(json.encode(input), iv: IV.fromLength(16))
.base64;
break;
default:
throw FormatException('invalid $signature');
break;
}
return encoded;
}
}
class _EncryptDecoder extends Converter<String, Map<String, dynamic>> {
final String key;
final String signature;
_EncryptDecoder(this.key, this.signature);
@override
Map<String, dynamic> convert(String input) {
var decoded;
switch (signature) {
case "Salsa20":
decoded = json.decode(Encrypter(Salsa20(Key.fromUtf8(key)))
.decrypt64(input, iv: IV.fromLength(8)));
break;
case "AES":
decoded = json.decode(Encrypter(AES(Key.fromUtf8(key)))
.decrypt64(input, iv: IV.fromLength(16)));
break;
default:
break;
}
if (decoded is Map) {
return decoded.cast<String, dynamic>();
}
throw FormatException('invalid input $input');
}
}
class _EncryptCodec extends Codec<Map<String, dynamic>, String> {
final String signature;
_EncryptEncoder _encoder;
_EncryptDecoder _decoder;
_EncryptCodec(String password, this.signature) {
_encoder = _EncryptEncoder(password, signature);
_decoder = _EncryptDecoder(password, signature);
}
@override
Converter<String, Map<String, dynamic>> get decoder => _decoder;
@override
Converter<Map<String, dynamic>, String> get encoder => _encoder;
}
// Salsa20 (16 length key required) or AES (32 length key required)
SembastCodec getEncryptSembastCodec(
{@required String password, String signature = "Salsa20"}) =>
SembastCodec(
signature: signature, codec: _EncryptCodec(password, signature));
from sembast.dart.
Yeap indeed. I agree that having an example that does not work on release is not good. I will look into it. The idea is to propose a solution that can be used just by copying one file (+ dependencies). I'll try your proposed encoder. I'd like to have an example that does not require a specific password length. Is there a way to generate a password from any string (hash, padding...)?
from sembast.dart.
yest it does, but you'll need the password hashing lib from the same guy. I didn't test it but it should do the job.
from sembast.dart.
sure, glad to help! I have tested and it's working. You can close this issue since it's solved :)
I just have a small suggestion (I don't know if it has a a noticeable impact, though): maybe change the sha256 to md5 or sha-1 which are computed faster. This salsa20 truncates the password string after the 16th char anyway...
from sembast.dart.
The IV.fromLength(8) in encrypt_codec.dart is using 0 for the IV every time. It should really be using a random IV, and prepending the IV to the cipher text. Something like this:
import 'package:pointycastle/api.dart';
import 'package:pointycastle/stream/salsa20.dart';
class Salsa20Encoder extends Converter<Map<String, dynamic>, String> {
Uint8List password;
Salsa20Encoder(this.password) {
assert(password.length == 32);
}
@override
String convert(Map<String, dynamic> input) {
Uint8List initialValue = randBytes(8);
String encoded = base64.encode(initialValue);
assert(encoded.length == 12);
final Salsa20Engine salsa20 = Salsa20Engine();
salsa20.init(true,
ParametersWithIV<KeyParameter>(KeyParameter(password), initialValue));
encoded += base64.encode(salsa20.process(utf8.encode(jsonEncode(input))));
return encoded;
}
}
class Salsa20Decoder extends Converter<String, Map<String, dynamic>> {
Uint8List password;
Salsa20Decoder(this.password) {
assert(password.length == 32);
}
@override
Map<String, dynamic> convert(String input) {
assert(input.length >= 12);
Uint8List initialValue = base64.decode(input.substring(0, 12));
input = input.substring(12);
final Salsa20Engine salsa20 = Salsa20Engine();
salsa20.init(false,
ParametersWithIV<KeyParameter>(KeyParameter(password), initialValue));
var decoded =
json.decode(utf8.decode(salsa20.process(base64.decode(input))));
if (decoded is Map) {
return decoded.cast<String, dynamic>();
}
throw FormatException('invalid input $input');
}
}
from sembast.dart.
Thanks for the suggestion @GreenAppers I will check how to integrate that. (PR are welcome!)
from sembast.dart.
Thanks again @GreenAppers and @madtocc I think I was able to improve the encrypt codec example. To note that I have not done any benchmark nor extensive extensing, especially on big database so it will remain an example, not a reference (i.e. its implementation and format might change). I kept the solution in one file (+ dependencies) so that it can be easily tried and tuned.
I have also used md5 instead of sha256 since it generates a 16 bytes blobs that matches what Salsa20 expect as a password (and we don't even store the password)
from sembast.dart.
Related Issues (20)
- Serialising object works fine, deserialising fails HOT 2
- How do I chain/make compound filters? HOT 3
- Issue using Sembast in Background Notification - Android HOT 6
- Deleted row in the .db file HOT 7
- Using sync to fetch data HOT 3
- Any database viewer is available to view the sembast db file HOT 1
- Export JsonEncodableCodec type HOT 4
- Copy db file on flutter web for upload / backup HOT 2
- Need a way to reload the database / detect that it was changed by an external process. HOT 4
- Error in mobile web browser HOT 2
- Sembast web limitation HOT 2
- how to rename a store? HOT 2
- Pagination not working HOT 2
- Store record with custom key HOT 1
- Ambiguity around null record value HOT 3
- null check on null value HOT 4
- Problem: record is updated even merged is false HOT 5
- Reload data from database HOT 3
- OutOfMemoryError: Out of Memory HOT 10
- Can not filter by timestamp. HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sembast.dart.