Skip to content

Commit

Permalink
Merge pull request #44 from tekartik/dart3a
Browse files Browse the repository at this point in the history
Dart3a
  • Loading branch information
alextekartik authored Oct 2, 2023
2 parents 2adaf79 + aa62c1e commit ab4a7b9
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 61 deletions.
6 changes: 6 additions & 0 deletions app_crypto/lib/encrypt.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
export 'package:tekartik_app_crypto/src/encrypt.dart'
show encrypt, decrypt, StringEncrypter, defaultEncryptedFromRawPassword;

export 'src/aes.dart'
show
aesEncrypterFromPassword,
encryptTextPassword16FromText,
aesDecrypt,
aesEncrypt;
export 'src/salsa20.dart' show salsa20EncrypterFromPassword;
91 changes: 91 additions & 0 deletions app_crypto/lib/src/aes.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import 'dart:convert';

import 'package:crypto/crypto.dart';
import 'package:encrypt/encrypt.dart';

import 'encrypt.dart';

/// Simple md5 hash
String encryptTextPassword16FromText(String password) {
return base64Encode(md5.convert(utf8.encode(password)).bytes)
.substring(0, 16);
}

/// aes encrypted using any password (use MD5), random data prepended
StringEncrypter aesEncrypterFromPassword(String password) {
return _AesStringEncrypter(_aesEncrypterFromPassword(password));
}

Encrypter _aesEncrypterFromPassword(String password) {
final key = Key.fromUtf8(password); // _generateEncryptPassword(password));
return Encrypter(AES(key));
}

class _AesStringEncrypter implements StringEncrypter {
final Encrypter encrypter;

_AesStringEncrypter(this.encrypter);
@override
String decrypt(String encrypted) {
// Read the initial value that was prepended
assert(encrypted.length >= 24);
final iv = base64.decode(encrypted.substring(0, 24));

// Extract the real input
encrypted = encrypted.substring(24);

// Decode the input
var decoded = encrypter.decrypt64(encrypted, iv: IV(iv));
return decoded;
}

IV _generateIv() {
final iv = IV.fromSecureRandom(16);
return iv;
}

@override
String encrypt(String input) {
final iv = _generateIv();

// Encode the input value
final encoded = encrypter.encrypt(input, iv: iv).base64;

var ivEncoded = iv.base64;
assert(ivEncoded.length == 24);
// Prepend the initial value
return '$ivEncoded$encoded';
}
}

class AesWithIVEntrypter extends _AesStringEncrypter {
final IV iv;

/// Must be 16 bytes
final String password;
AesWithIVEntrypter(this.password, this.iv)
: super(_aesEncrypterFromPassword(password));
@override
IV _generateIv() {
return iv;
}
}

/// encrypt the [decoded] text using [password].
///
/// [password] must be ascii character of length 16, 24 or 32.
///
/// returns a base 64 encrypted string.
///
/// Encryption used is AES.
String aesEncrypt(String decoded, String password) =>
aesEncrypterFromPassword(password).encrypt(decoded);

/// decrypt the [encoded] text using [password].
///
/// [encoded] is base 64 string got from the encrypt method using the same
/// [password]
///
/// Encryption used is AES.
String aesDecrypt(String encoded, String password) =>
aesEncrypterFromPassword(password).decrypt(encoded);
6 changes: 4 additions & 2 deletions app_crypto/lib/src/encrypt.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:typed_data';

import 'package:encrypt/encrypt.dart';

/// encrypt the [decoded] text using [password].
Expand All @@ -9,7 +11,7 @@ import 'package:encrypt/encrypt.dart';
/// Encryption used is AES.
String _encrypt(String decoded, String password) {
final key = Key.fromUtf8(password);
final iv = IV.fromLength(16);
final iv = IV(Uint8List(16));
final encrypter = Encrypter(AES(key));
return encrypter.encrypt(decoded, iv: iv).base64;
}
Expand All @@ -22,7 +24,7 @@ String _encrypt(String decoded, String password) {
/// Encryption used is AES.
String _decrypt(String encoded, String password) {
final key = Key.fromUtf8(password);
final iv = IV.fromLength(16);
final iv = IV(Uint8List(16));
final encrypter = Encrypter(AES(key));
return encrypter.decrypt(Encrypted.fromBase64(encoded), iv: iv);
}
Expand Down
2 changes: 1 addition & 1 deletion app_crypto/lib/src/encrypt_codec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class EncryptCodec with Codec<String, String> {
EncryptConverter(encrypter: encrypter);
}

/// String password
/// String password - fixed IV with 0
EncryptCodec defaultEncryptCodec({required String rawPassword}) =>
EncryptCodec(encrypter: defaultEncryptedFromRawPassword(rawPassword));

Expand Down
11 changes: 10 additions & 1 deletion app_crypto/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,24 @@ dependencies:
ref: dart3a
version: '>=0.10.7'
meta: '>=1.1.6'
encrypt: ^5.0.3
encrypt: '>=3.0.0' # <=5.0.3'
# Temp aes issue limited to 5.0.1
# encrypt: '<=5.0.3'
crypto:
# Temp web compile issue
pointycastle: '>=3.2.0-rc0'

dev_dependencies:
dev_test:
test: '>=1.5.0'
# downgrade testing
file: '>=7.0.0'
build_runner: '>=1.2.7'
build_test: any
build_web_compilers: any
process_run: '>=0.12.0+1'

_dependency_overrides:
encrypt:
git:
url: https://github.com/leocavalcante/encrypt
105 changes: 90 additions & 15 deletions app_crypto/test/aes_test.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,98 @@
import 'package:encrypt/encrypt.dart';
import 'package:tekartik_app_crypto/encrypt.dart';
import 'package:tekartik_app_crypto/src/aes.dart';
import 'package:tekartik_app_crypto/src/generate_password.dart'
show generatePassword;
import 'package:test/test.dart';

String encrypt(String decoded, String password) {
final key = Key.fromUtf8(password);
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key));
return encrypter.encrypt(decoded, iv: iv).base64;
}
void main() {
void aesRoundTrip(String decoded, String password) {
var encrypter = aesEncrypterFromPassword(password);
var encrypted = encrypter.encrypt(decoded);
print('${decoded.length}:${encrypted.length}');
encrypter = aesEncrypterFromPassword(password);
expect(encrypter.decrypt(encrypted), decoded);
}

String decrypt(String encoded, String password) {
final key = Key.fromUtf8(password);
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key));
return encrypter.decrypt(Encrypted.fromBase64(encoded), iv: iv);
}
test('aes encrypt decrypt', () {
var password = r'E4x*$TwbkJC-xK4KGC4zJF9j*Rh&WLgR';
var encrypter = AesWithIVEntrypter(password, IV.allZerosOfLength(16));

void main() {
test('AES encrypt/decrypt', () {
expect(encrypt('test', password), 'amGhyRRLUIoE59IiEys5Vw==');
expect(encrypter.encrypt('test'),
'AAAAAAAAAAAAAAAAAAAAAA==amGhyRRLUIoE59IiEys5Vw==');
expect(
encrypter.decrypt('AAAAAAAAAAAAAAAAAAAAAA==amGhyRRLUIoE59IiEys5Vw=='),
'test');
});

test('aes decrypt', () {
var password = r'E4x*$TwbkJC-xK4KGC4zJF9j*Rh&WLgR';
expect(decrypt('amGhyRRLUIoE59IiEys5Vw==', password), 'test');
var encrypter = aesEncrypterFromPassword(password);

expect(
encrypter.encrypt('test'),
isNot(
'kEH3mkatSK4yiDu95hZj3Q==0aN1ouMw1HhKY6L8sibkgA==')); // - different each time
expect(
encrypter.decrypt('kEH3mkatSK4yiDu95hZj3Q==0aN1ouMw1HhKY6L8sibkgA=='),
'test');
expect(
aesDecrypt(
'kEH3mkatSK4yiDu95hZj3Q==0aN1ouMw1HhKY6L8sibkgA==', password),
'test');
expect(aesEncrypt('test', password),
isNot('19/EiVx5ICKR/IpS05DYmA==rqLU4PjkNP8W/SiI1dVgAA=='));
expect(
aesDecrypt(
'19/EiVx5ICKR/IpS05DYmA==rqLU4PjkNP8W/SiI1dVgAA==', password),
'test');
expect(aesDecrypt(aesEncrypt('test', password), password), 'test');
});
test('aes', () {
var aes = aesEncrypterFromPassword(encryptTextPassword16FromText('test'));
var encrypted = aes.encrypt('test');
aes = aesEncrypterFromPassword(encryptTextPassword16FromText('test'));
expect(aes.decrypt(encrypted), 'test');

String textWithLength(int length) {
return List.generate(length, (i) => i.toString().substring(0, 1)).join();
}

aesRoundTrip('test', encryptTextPassword16FromText('test'));
//aesRoundTrip('', '');
aesRoundTrip('1', encryptTextPassword16FromText('2'));
aesRoundTrip(textWithLength(4096),
encryptTextPassword16FromText(textWithLength(4096)));
// _salsa20RoundTrip(textWithLength(40960), textWithLength(40960));
aesRoundTrip(textWithLength(4096000),
encryptTextPassword16FromText(textWithLength(4096000)));

var password = generatePassword();
aes = aesEncrypterFromPassword(password);
var sw = Stopwatch()..start();
var count = 1000;
for (var i = 0; i < count; i++) {
aes.decrypt(aes.encrypt(textWithLength(count * 10)));
}
print('aes round trip: ${sw.elapsedMilliseconds} ms');
sw = Stopwatch()..start();

for (var i = 0; i < count; i++) {
aesDecrypt(aesEncrypt(textWithLength(count * 10), password), password);
}
print('aesDecrypt/aesEncrypt: ${sw.elapsedMilliseconds} ms');

var encrypteds = List.generate(count, (index) => '');
sw = Stopwatch()..start();
for (var i = 0; i < count; i++) {
encrypteds[i] = aes.encrypt(textWithLength(count * 10));
}
print('aes encrypt: ${sw.elapsedMilliseconds} ms');
sw = Stopwatch()..start();
for (var i = 0; i < count; i++) {
aes.decrypt(encrypteds[i]);
}
print('aes decrypt: ${sw.elapsedMilliseconds} ms');
});
}
44 changes: 2 additions & 42 deletions app_crypto/test/crypto_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ void main() {
expect(decrypt(encrypt(decoded, password), password), decoded);
}

void salsa20RoundTrip(String decoded, String password) {
var encrypter = salsa20EncrypterFromPassword(password);
var encrypted = encrypter.encrypt(decoded);
print('${decoded.length}:${encrypted.length}');
encrypter = salsa20EncrypterFromPassword(password);
expect(encrypter.decrypt(encrypted), decoded);
}

test('test', () {
var password = r'E4x*$TwbkJC-xK4KGC4zJF9j*Rh&WLgR';
expect(encrypt('test', password), 'amGhyRRLUIoE59IiEys5Vw==');
Expand Down Expand Up @@ -45,37 +37,15 @@ void main() {
}
});

test('generatePassword', () {
expect(generatePassword().length, 32);
expect(generatePassword(length: 64).length, 64);
});

test('salsa20', () {
var salsa = salsa20EncrypterFromPassword('test');
var encrypted = salsa.encrypt('test');
salsa = salsa20EncrypterFromPassword('test');
expect(salsa.decrypt(encrypted), 'test');

test('legacy', () {
String textWithLength(int length) {
return List.generate(length, (i) => i.toString().substring(0, 1)).join();
}

salsa20RoundTrip('test', 'test');
salsa20RoundTrip('', '');
salsa20RoundTrip('1', '2');
salsa20RoundTrip(textWithLength(4096), textWithLength(4096));
// _salsa20RoundTrip(textWithLength(40960), textWithLength(40960));
salsa20RoundTrip(textWithLength(4096000), textWithLength(4096000));

var password = generatePassword();
salsa = salsa20EncrypterFromPassword(password);

var sw = Stopwatch()..start();
var count = 1000;
for (var i = 0; i < count; i++) {
salsa.decrypt(salsa.encrypt(textWithLength(count * 10)));
}
print('salsa: ${sw.elapsedMilliseconds} ms');
sw = Stopwatch()..start();

for (var i = 0; i < count; i++) {
decrypt(encrypt(textWithLength(count * 10), password), password);
Expand All @@ -84,16 +54,6 @@ void main() {

var encrypteds = List.generate(count, (index) => '');
sw = Stopwatch()..start();
for (var i = 0; i < count; i++) {
encrypteds[i] = salsa.encrypt(textWithLength(count * 10));
}
print('salsa encrypt: ${sw.elapsedMilliseconds} ms');
sw = Stopwatch()..start();
for (var i = 0; i < count; i++) {
salsa.decrypt(encrypteds[i]);
}
print('salsa decrypt: ${sw.elapsedMilliseconds} ms');
sw = Stopwatch()..start();
for (var i = 0; i < count; i++) {
encrypteds[i] = encrypt(textWithLength(count * 10), password);
}
Expand Down
2 changes: 2 additions & 0 deletions app_crypto/test/password_generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ void main() {
test('generatePassword', () {
expect(generatePassword().length, 32);
expect(generatePassword(length: 10).length, 10);

expect(generatePassword(length: 64).length, 64);
});
}
28 changes: 28 additions & 0 deletions app_crypto/test/raw_aes_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'dart:typed_data';

import 'package:encrypt/encrypt.dart';
import 'package:test/test.dart';

String aesEncrypt(String decoded, String password) {
final key = Key.fromUtf8(password);
// final iv = IV.fromLength(16);
final iv = IV(Uint8List(16));
final encrypter = Encrypter(AES(key));
return encrypter.encrypt(decoded, iv: iv).base64;
}

String aesDecrypt(String encoded, String password) {
final key = Key.fromUtf8(password);
// final iv = IV.fromLength(16);
final iv = IV(Uint8List(16));
final encrypter = Encrypter(AES(key));
return encrypter.decrypt(Encrypted.fromBase64(encoded), iv: iv);
}

void main() {
test('AES encrypt/decrypt', () {
var password = r'E4x*$TwbkJC-xK4KGC4zJF9j*Rh&WLgR';
expect(aesEncrypt('test', password), 'amGhyRRLUIoE59IiEys5Vw==');
expect(aesDecrypt('amGhyRRLUIoE59IiEys5Vw==', password), 'test');
});
}
Loading

0 comments on commit ab4a7b9

Please sign in to comment.