perf: x10 perfomance improvement on android with better list management
This commit is contained in:
parent
6397e10c05
commit
a7affea1e1
@ -3,7 +3,7 @@
|
|||||||
// -----
|
// -----
|
||||||
// File: benchmark_page.dart
|
// File: benchmark_page.dart
|
||||||
// Created Date: 28/12/2021 15:12:39
|
// Created Date: 28/12/2021 15:12:39
|
||||||
// Last Modified: 24/05/2022 17:23:33
|
// Last Modified: 24/05/2022 23:43:59
|
||||||
// -----
|
// -----
|
||||||
// Copyright (c) 2021
|
// Copyright (c) 2021
|
||||||
|
|
||||||
@ -24,6 +24,51 @@ class BenchmarkPage extends ConsumerWidget {
|
|||||||
final Output keyContent = Output();
|
final Output keyContent = Output();
|
||||||
final Output benchmarkStatus = Output(large: true);
|
final Output benchmarkStatus = Output(large: true);
|
||||||
|
|
||||||
|
Future<void> _benchmarkEncryptionOnly(
|
||||||
|
WidgetRef ref,
|
||||||
|
Cipher cipher,
|
||||||
|
) async {
|
||||||
|
Session state = ref.read(sessionProvider.state).state;
|
||||||
|
AesGcm pc = AesGcm();
|
||||||
|
|
||||||
|
if (state.secretKey.bytes.isEmpty) {
|
||||||
|
benchmarkStatus
|
||||||
|
.print('No SecretKey!\nGo in Key tab and generate or derive one.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarkStatus.print("Benchmark 2/4/8/16/32/64/128/256MB\n");
|
||||||
|
List<int> testedSizes = [2, 4, 8, 16, 32, 64, 128, 256];
|
||||||
|
String csv = "size;encryption time\n";
|
||||||
|
|
||||||
|
var beforeBench = DateTime.now();
|
||||||
|
Cipher.bytesCountPerChunk = Cipher.bytesCountPerChunk;
|
||||||
|
benchmarkStatus
|
||||||
|
.append('[Benchmark] ${Cipher.bytesCountPerChunk} bytes/chunk \n');
|
||||||
|
for (int size in testedSizes) {
|
||||||
|
var b = Uint8List(size * 1000000);
|
||||||
|
csv += "${size * 1000000};";
|
||||||
|
|
||||||
|
// Encryption
|
||||||
|
var before = DateTime.now();
|
||||||
|
var encryptedBigFile = await cipher.encrypt(b);
|
||||||
|
var after = DateTime.now();
|
||||||
|
|
||||||
|
var benchmark =
|
||||||
|
after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
|
||||||
|
benchmarkStatus.append('[$size MB] Encryption took $benchmark ms\n');
|
||||||
|
|
||||||
|
csv += "$benchmark\n";
|
||||||
|
}
|
||||||
|
var afterBench = DateTime.now();
|
||||||
|
var benchmark =
|
||||||
|
afterBench.millisecondsSinceEpoch - beforeBench.millisecondsSinceEpoch;
|
||||||
|
var sum = testedSizes.reduce((a, b) => a + b);
|
||||||
|
benchmarkStatus.append(
|
||||||
|
'Benchmark finished.\nGenerated, and encrypted $sum MB in $benchmark ms');
|
||||||
|
debugPrint("[Benchmark cvs]\n$csv");
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _benchmark(WidgetRef ref, Cipher cipher,
|
Future<void> _benchmark(WidgetRef ref, Cipher cipher,
|
||||||
{bool usePc = false}) async {
|
{bool usePc = false}) async {
|
||||||
Session state = ref.read(sessionProvider.state).state;
|
Session state = ref.read(sessionProvider.state).state;
|
||||||
@ -37,13 +82,14 @@ class BenchmarkPage extends ConsumerWidget {
|
|||||||
|
|
||||||
benchmarkStatus.print("Benchmark 2/4/8/16/32/64/128/256MB\n");
|
benchmarkStatus.print("Benchmark 2/4/8/16/32/64/128/256MB\n");
|
||||||
List<int> testedSizes = [2, 4, 8, 16, 32, 64, 128, 256];
|
List<int> testedSizes = [2, 4, 8, 16, 32, 64, 128, 256];
|
||||||
String csv =
|
String csv = "size;encryption time;decryption time;crypto time\n";
|
||||||
"size;encryption time;encode time;decryption time;crypto time\n";
|
|
||||||
|
|
||||||
var beforeBench = DateTime.now();
|
var beforeBench = DateTime.now();
|
||||||
|
Cipher.bytesCountPerChunk = Cipher.bytesCountPerChunk;
|
||||||
|
benchmarkStatus
|
||||||
|
.append('[Benchmark] ${Cipher.bytesCountPerChunk} bytes/chunk \n');
|
||||||
for (int size in testedSizes) {
|
for (int size in testedSizes) {
|
||||||
var b = ByteData(size * 1000000);
|
var b = Uint8List(size * 1000000);
|
||||||
//var bigFile = Uint8List.view();
|
|
||||||
csv += "${size * 1000000};";
|
csv += "${size * 1000000};";
|
||||||
var cryptoTime = 0;
|
var cryptoTime = 0;
|
||||||
|
|
||||||
@ -51,10 +97,9 @@ class BenchmarkPage extends ConsumerWidget {
|
|||||||
var before = DateTime.now();
|
var before = DateTime.now();
|
||||||
Object encryptedBigFile;
|
Object encryptedBigFile;
|
||||||
if (usePc) {
|
if (usePc) {
|
||||||
encryptedBigFile =
|
encryptedBigFile = pc.encrypt(b, state.secretKey.bytes);
|
||||||
pc.encrypt(b.buffer.asUint8List(), state.secretKey.bytes);
|
|
||||||
} else {
|
} else {
|
||||||
encryptedBigFile = await cipher.encrypt(b.buffer.asUint8List());
|
encryptedBigFile = await cipher.encrypt(b);
|
||||||
}
|
}
|
||||||
var after = DateTime.now();
|
var after = DateTime.now();
|
||||||
|
|
||||||
@ -62,7 +107,7 @@ class BenchmarkPage extends ConsumerWidget {
|
|||||||
after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
|
after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
|
||||||
benchmarkStatus.append('[$size MB] Encryption took $benchmark ms\n');
|
benchmarkStatus.append('[$size MB] Encryption took $benchmark ms\n');
|
||||||
|
|
||||||
csv += "$benchmark;";
|
csv += "$benchmark";
|
||||||
cryptoTime += benchmark;
|
cryptoTime += benchmark;
|
||||||
|
|
||||||
// Decryption
|
// Decryption
|
||||||
@ -129,6 +174,9 @@ class BenchmarkPage extends ConsumerWidget {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
),
|
),
|
||||||
keyContent,
|
keyContent,
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
@ -146,6 +194,17 @@ class BenchmarkPage extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
Button(
|
||||||
|
() => _benchmarkEncryptionOnly(ref, cipher),
|
||||||
|
"NC Persistence",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
benchmarkStatus,
|
benchmarkStatus,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// -----
|
// -----
|
||||||
// File: aes.dart
|
// File: aes.dart
|
||||||
// Created Date: 16/12/2021 16:28:00
|
// Created Date: 16/12/2021 16:28:00
|
||||||
// Last Modified: 23/05/2022 23:06:05
|
// Last Modified: 24/05/2022 23:29:42
|
||||||
// -----
|
// -----
|
||||||
// Copyright (c) 2022
|
// Copyright (c) 2022
|
||||||
|
|
||||||
@ -39,6 +39,7 @@ class AES implements Cipher {
|
|||||||
|
|
||||||
final Map<AESMode, List<AESPadding>> _supported = {
|
final Map<AESMode, List<AESPadding>> _supported = {
|
||||||
AESMode.gcm: [AESPadding.none],
|
AESMode.gcm: [AESPadding.none],
|
||||||
|
AESMode.cbc: [AESPadding.pkcs5],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!_supported[mode]!.contains(padding)) {
|
if (!_supported[mode]!.contains(padding)) {
|
||||||
@ -46,27 +47,35 @@ class AES implements Cipher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> _decrypt(CipherText cipherText) async {
|
||||||
|
return await platform.decryptAsList(
|
||||||
|
[cipherText.iv, cipherText.payload],
|
||||||
|
key.bytes,
|
||||||
|
algorithm.name,
|
||||||
|
) ??
|
||||||
|
Uint8List(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<CipherText> _encrypt(Uint8List data) async {
|
||||||
|
final List<Uint8List> cipherText =
|
||||||
|
await platform.encryptAsList(data, key.bytes, algorithm.name) ??
|
||||||
|
List.empty();
|
||||||
|
return CipherText.fromPairIvAndBytes(
|
||||||
|
cipherText,
|
||||||
|
dataLength: cipherText.last.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> decrypt(CipherText cipherText) async {
|
Future<Uint8List> decrypt(CipherText cipherText) async {
|
||||||
final BytesBuilder decryptedData = BytesBuilder(copy: false);
|
final BytesBuilder decryptedData = BytesBuilder(copy: false);
|
||||||
|
|
||||||
if (cipherText is CipherTextList) {
|
if (cipherText is CipherTextList) {
|
||||||
for (final CipherText ct in cipherText.list) {
|
for (final CipherText ct in cipherText.list) {
|
||||||
final Uint8List d = await platform.decrypt(
|
decryptedData.add(await _decrypt(ct));
|
||||||
ct.bytes,
|
|
||||||
key.bytes,
|
|
||||||
algorithm.name,
|
|
||||||
) ??
|
|
||||||
Uint8List(0);
|
|
||||||
decryptedData.add(d);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final Uint8List d = await platform.decrypt(
|
decryptedData.add(await _decrypt(cipherText));
|
||||||
cipherText.bytes,
|
|
||||||
key.bytes,
|
|
||||||
algorithm.name,
|
|
||||||
) ??
|
|
||||||
Uint8List(0);
|
|
||||||
decryptedData.add(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return decryptedData.toBytes();
|
return decryptedData.toBytes();
|
||||||
@ -75,43 +84,23 @@ class AES implements Cipher {
|
|||||||
@override
|
@override
|
||||||
Future<CipherText> encrypt(Uint8List data) async {
|
Future<CipherText> encrypt(Uint8List data) async {
|
||||||
Uint8List dataToEncrypt;
|
Uint8List dataToEncrypt;
|
||||||
|
|
||||||
final CipherTextList cipherTextList = CipherTextList();
|
final CipherTextList cipherTextList = CipherTextList();
|
||||||
// If data is bigger than 32mB -> split in chunks
|
|
||||||
if (data.length > CipherTextList.chunkSize) {
|
if (data.length > Cipher.bytesCountPerChunk) {
|
||||||
final int chunkNb = (data.length / CipherTextList.chunkSize).ceil();
|
final int chunkNb = (data.length / Cipher.bytesCountPerChunk).ceil();
|
||||||
for (var i = 0; i < chunkNb; i++) {
|
for (var i = 0; i < chunkNb; i++) {
|
||||||
dataToEncrypt = i < (chunkNb - 1)
|
dataToEncrypt = i < (chunkNb - 1)
|
||||||
? data.sublist(
|
? data.sublist(
|
||||||
i * CipherTextList.chunkSize,
|
i * Cipher.bytesCountPerChunk,
|
||||||
(i + 1) * CipherTextList.chunkSize,
|
(i + 1) * Cipher.bytesCountPerChunk,
|
||||||
)
|
)
|
||||||
: data.sublist(i * CipherTextList.chunkSize);
|
: data.sublist(i * Cipher.bytesCountPerChunk);
|
||||||
final Uint8List c = await platform.encrypt(
|
cipherTextList.add(await _encrypt(dataToEncrypt));
|
||||||
dataToEncrypt,
|
|
||||||
key.bytes,
|
|
||||||
algorithm.name,
|
|
||||||
) ??
|
|
||||||
Uint8List(0);
|
|
||||||
cipherTextList.add(
|
|
||||||
CipherText(
|
|
||||||
c.sublist(0, 12),
|
|
||||||
c.sublist(12, c.length - 16),
|
|
||||||
c.sublist(c.length - 16, c.length),
|
|
||||||
),
|
|
||||||
); // TODO(hpcl): generify this
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final Uint8List c =
|
return _encrypt(data);
|
||||||
await platform.encrypt(data, key.bytes, algorithm.name) ??
|
|
||||||
Uint8List(0);
|
|
||||||
|
|
||||||
return CipherText(
|
|
||||||
c.sublist(0, 12),
|
|
||||||
c.sublist(12, c.length - 16),
|
|
||||||
c.sublist(c.length - 16, c.length),
|
|
||||||
); // TODO(hpcl): generify this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cipherTextList;
|
return cipherTextList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
// -----
|
// -----
|
||||||
// File: aes_mode.dart
|
// File: aes_mode.dart
|
||||||
// Created Date: 23/05/2022 22:09:16
|
// Created Date: 23/05/2022 22:09:16
|
||||||
// Last Modified: 23/05/2022 22:10:31
|
// Last Modified: 24/05/2022 23:17:01
|
||||||
// -----
|
// -----
|
||||||
// Copyright (c) 2022
|
// Copyright (c) 2022
|
||||||
|
|
||||||
/// Defines the AES modes of operation.
|
/// Defines the AES modes of operation.
|
||||||
enum AESMode { gcm }
|
enum AESMode { gcm, cbc }
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
// -----
|
// -----
|
||||||
// File: aes_padding.dart
|
// File: aes_padding.dart
|
||||||
// Created Date: 23/05/2022 22:10:17
|
// Created Date: 23/05/2022 22:10:17
|
||||||
// Last Modified: 23/05/2022 22:13:28
|
// Last Modified: 24/05/2022 23:17:25
|
||||||
// -----
|
// -----
|
||||||
// Copyright (c) 2022
|
// Copyright (c) 2022
|
||||||
|
|
||||||
/// Represents different paddings.
|
/// Represents different paddings.
|
||||||
enum AESPadding { none }
|
enum AESPadding { none, pkcs5 }
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// -----
|
// -----
|
||||||
// File: cipher_text.dart
|
// File: cipher_text.dart
|
||||||
// Created Date: 16/12/2021 16:59:53
|
// Created Date: 16/12/2021 16:59:53
|
||||||
// Last Modified: 23/05/2022 23:02:10
|
// Last Modified: 24/05/2022 21:27:44
|
||||||
// -----
|
// -----
|
||||||
// Copyright (c) 2021
|
// Copyright (c) 2021
|
||||||
|
|
||||||
@ -16,23 +16,61 @@ class CipherText extends ByteArray {
|
|||||||
final int _dataLength;
|
final int _dataLength;
|
||||||
final int _tagLength;
|
final int _tagLength;
|
||||||
|
|
||||||
CipherText(Uint8List iv, Uint8List data, Uint8List tag)
|
final Uint8List _iv;
|
||||||
|
|
||||||
|
CipherText(Uint8List iv, Uint8List data, Uint8List? tag)
|
||||||
: _ivLength = iv.length,
|
: _ivLength = iv.length,
|
||||||
_dataLength = data.length,
|
_dataLength = data.length,
|
||||||
_tagLength = tag.length,
|
_tagLength = tag?.length ?? 0,
|
||||||
super(Uint8List.fromList(iv + data + tag));
|
_iv = iv,
|
||||||
|
super((tag != null) ? Uint8List.fromList(data + tag) : data);
|
||||||
|
|
||||||
|
CipherText.fromBytes(
|
||||||
|
Uint8List bytes, {
|
||||||
|
required int ivLength,
|
||||||
|
required int dataLength,
|
||||||
|
int tagLength = 0,
|
||||||
|
}) : _ivLength = ivLength,
|
||||||
|
_dataLength = dataLength,
|
||||||
|
_tagLength = tagLength,
|
||||||
|
_iv = bytes.sublist(0, ivLength),
|
||||||
|
super(bytes.sublist(ivLength, bytes.length - tagLength));
|
||||||
|
|
||||||
|
const CipherText.fromIvAndBytes(
|
||||||
|
Uint8List iv,
|
||||||
|
super.data, {
|
||||||
|
required int dataLength,
|
||||||
|
int tagLength = 0,
|
||||||
|
}) : _ivLength = iv.length,
|
||||||
|
_dataLength = dataLength,
|
||||||
|
_tagLength = tagLength,
|
||||||
|
_iv = iv;
|
||||||
|
|
||||||
|
CipherText.fromPairIvAndBytes(
|
||||||
|
List<Uint8List> pair, {
|
||||||
|
required int dataLength,
|
||||||
|
int tagLength = 0,
|
||||||
|
}) : _ivLength = pair.first.length,
|
||||||
|
_dataLength = dataLength,
|
||||||
|
_tagLength = tagLength,
|
||||||
|
_iv = pair.first,
|
||||||
|
super(pair.last);
|
||||||
|
|
||||||
/// Gets the CipherText IV.
|
/// Gets the CipherText IV.
|
||||||
Uint8List get iv => bytes.sublist(0, _ivLength);
|
Uint8List get iv => _iv;
|
||||||
|
|
||||||
/// Gets the CipherText data.
|
/// Gets the CipherText data.
|
||||||
Uint8List get data => bytes.sublist(_ivLength, _ivLength + _dataLength);
|
Uint8List get data => _tagLength > 0
|
||||||
|
? bytes.sublist(0, _dataLength - _tagLength)
|
||||||
|
: bytes;
|
||||||
|
|
||||||
/// Gets the CipherText tag.
|
/// Gets the CipherText tag.
|
||||||
Uint8List get tag => bytes.sublist(
|
Uint8List get tag => _tagLength > 0
|
||||||
_ivLength + _dataLength,
|
? bytes.sublist(_dataLength - _tagLength, _dataLength)
|
||||||
_ivLength + _dataLength + _tagLength,
|
: Uint8List(0);
|
||||||
);
|
|
||||||
|
/// Gets the CipherText data and tag.
|
||||||
|
Uint8List get payload => bytes;
|
||||||
|
|
||||||
/// Gets the CipherText IV length.
|
/// Gets the CipherText IV length.
|
||||||
int get ivLength => _ivLength;
|
int get ivLength => _ivLength;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// -----
|
// -----
|
||||||
// File: cipher_text_list.dart
|
// File: cipher_text_list.dart
|
||||||
// Created Date: 23/05/2022 22:59:02
|
// Created Date: 23/05/2022 22:59:02
|
||||||
// Last Modified: 23/05/2022 23:05:02
|
// Last Modified: 24/05/2022 20:18:26
|
||||||
// -----
|
// -----
|
||||||
// Copyright (c) 2022
|
// Copyright (c) 2022
|
||||||
|
|
||||||
@ -12,7 +12,6 @@ import 'dart:typed_data';
|
|||||||
import 'package:native_crypto/src/core/cipher_text.dart';
|
import 'package:native_crypto/src/core/cipher_text.dart';
|
||||||
|
|
||||||
class CipherTextList extends CipherText {
|
class CipherTextList extends CipherText {
|
||||||
static const int chunkSize = 33554432;
|
|
||||||
final List<CipherText> _list;
|
final List<CipherText> _list;
|
||||||
|
|
||||||
CipherTextList()
|
CipherTextList()
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// -----
|
// -----
|
||||||
// File: cipher.dart
|
// File: cipher.dart
|
||||||
// Created Date: 16/12/2021 16:28:00
|
// Created Date: 16/12/2021 16:28:00
|
||||||
// Last Modified: 23/05/2022 23:06:20
|
// Last Modified: 24/05/2022 19:55:38
|
||||||
// -----
|
// -----
|
||||||
// Copyright (c) 2021
|
// Copyright (c) 2021
|
||||||
|
|
||||||
@ -18,9 +18,20 @@ import 'package:native_crypto/src/utils/cipher_algorithm.dart';
|
|||||||
/// or decryption - a series of well-defined steps that can
|
/// or decryption - a series of well-defined steps that can
|
||||||
/// be followed as a procedure.
|
/// be followed as a procedure.
|
||||||
abstract class Cipher {
|
abstract class Cipher {
|
||||||
|
/// Returns the size of a chunk of data
|
||||||
|
/// that can be processed by the cipher.
|
||||||
|
static int _bytesCountPerChunk = 33554432;
|
||||||
|
|
||||||
|
static int get bytesCountPerChunk => Cipher._bytesCountPerChunk;
|
||||||
|
|
||||||
|
static set bytesCountPerChunk(int bytesCount) {
|
||||||
|
_bytesCountPerChunk = bytesCount;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the standard algorithm name for this cipher
|
/// Returns the standard algorithm name for this cipher
|
||||||
CipherAlgorithm get algorithm;
|
CipherAlgorithm get algorithm;
|
||||||
|
|
||||||
|
|
||||||
/// Encrypts data.
|
/// Encrypts data.
|
||||||
///
|
///
|
||||||
/// Takes [Uint8List] data as parameter.
|
/// Takes [Uint8List] data as parameter.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package fr.pointcheval.native_crypto_android
|
package fr.pointcheval.native_crypto_android
|
||||||
|
|
||||||
import androidx.annotation.NonNull
|
import androidx.annotation.NonNull
|
||||||
|
import fr.pointcheval.native_crypto_android.interfaces.Cipher
|
||||||
import fr.pointcheval.native_crypto_android.kdf.Pbkdf2
|
import fr.pointcheval.native_crypto_android.kdf.Pbkdf2
|
||||||
import fr.pointcheval.native_crypto_android.keys.SecretKey
|
import fr.pointcheval.native_crypto_android.keys.SecretKey
|
||||||
import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm
|
import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm
|
||||||
@ -23,6 +24,8 @@ class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler {
|
|||||||
private lateinit var channel: MethodChannel
|
private lateinit var channel: MethodChannel
|
||||||
private val name = "plugins.hugop.cl/native_crypto"
|
private val name = "plugins.hugop.cl/native_crypto"
|
||||||
|
|
||||||
|
private var cipherInstance: Cipher? = null
|
||||||
|
|
||||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, name)
|
channel = MethodChannel(flutterPluginBinding.binaryMessenger, name)
|
||||||
channel.setMethodCallHandler(this)
|
channel.setMethodCallHandler(this)
|
||||||
@ -38,11 +41,11 @@ class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler {
|
|||||||
when (call.method) {
|
when (call.method) {
|
||||||
"digest" -> methodCallTask = handleDigest(call.arguments())
|
"digest" -> methodCallTask = handleDigest(call.arguments())
|
||||||
"generateSecretKey" -> methodCallTask = handleGenerateSecretKey(call.arguments())
|
"generateSecretKey" -> methodCallTask = handleGenerateSecretKey(call.arguments())
|
||||||
"generateKeyPair" -> result.notImplemented()
|
|
||||||
"pbkdf2" -> methodCallTask = handlePbkdf2(call.arguments())
|
"pbkdf2" -> methodCallTask = handlePbkdf2(call.arguments())
|
||||||
"encrypt" -> methodCallTask = handleEncrypt(call.arguments())
|
"encryptAsList" -> methodCallTask = handleEncryptAsList(call.arguments())
|
||||||
"decrypt" -> methodCallTask = handleDecrypt(call.arguments())
|
"decryptAsList" -> methodCallTask = handleDecryptAsList(call.arguments())
|
||||||
"generateSharedSecretKey" -> result.notImplemented()
|
"encrypt" -> methodCallTask = handleCrypt(call.arguments(), true)
|
||||||
|
"decrypt" -> methodCallTask = handleCrypt(call.arguments(), false)
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +98,17 @@ class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEncrypt(arguments: Map<String, Any>?): Task<ByteArray> {
|
private fun lazyLoadCipher(cipherAlgorithm: CipherAlgorithm) {
|
||||||
|
if (cipherInstance == null) {
|
||||||
|
cipherInstance = cipherAlgorithm.getCipher()
|
||||||
|
} else {
|
||||||
|
if (cipherInstance!!.algorithm != cipherAlgorithm) {
|
||||||
|
cipherInstance = cipherAlgorithm.getCipher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEncryptAsList(arguments: Map<String, Any>?): Task<List<ByteArray>> {
|
||||||
return Task {
|
return Task {
|
||||||
val data: ByteArray =
|
val data: ByteArray =
|
||||||
Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray
|
Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray
|
||||||
@ -104,13 +117,29 @@ class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler {
|
|||||||
Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String
|
Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String
|
||||||
|
|
||||||
val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm)
|
val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm)
|
||||||
val cipher = cipherAlgorithm.getCipher()
|
lazyLoadCipher(cipherAlgorithm)
|
||||||
|
|
||||||
cipher.encrypt(data, key)
|
cipherInstance!!.encryptAsList(data, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDecrypt(arguments: Map<String, Any>?): Task<ByteArray> {
|
private fun handleDecryptAsList(arguments: Map<String, Any>?): Task<ByteArray> {
|
||||||
|
return Task {
|
||||||
|
val data: List<ByteArray> =
|
||||||
|
Objects.requireNonNull(arguments?.get(Constants.DATA)) as List<ByteArray>
|
||||||
|
val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray
|
||||||
|
val algorithm: String =
|
||||||
|
Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String
|
||||||
|
|
||||||
|
val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm)
|
||||||
|
lazyLoadCipher(cipherAlgorithm)
|
||||||
|
|
||||||
|
cipherInstance!!.decryptAsList(data, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// **EN**Crypt and **DE**Crypt
|
||||||
|
private fun handleCrypt(arguments: Map<String, Any>?, forEncryption: Boolean): Task<ByteArray> {
|
||||||
return Task {
|
return Task {
|
||||||
val data: ByteArray =
|
val data: ByteArray =
|
||||||
Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray
|
Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray
|
||||||
@ -119,9 +148,13 @@ class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler {
|
|||||||
Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String
|
Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String
|
||||||
|
|
||||||
val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm)
|
val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm)
|
||||||
val cipher = cipherAlgorithm.getCipher()
|
lazyLoadCipher(cipherAlgorithm)
|
||||||
|
|
||||||
cipher.decrypt(data, key)
|
if (forEncryption) {
|
||||||
|
cipherInstance!!.encrypt(data, key)
|
||||||
|
} else {
|
||||||
|
cipherInstance!!.decrypt(data, key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@ class AES : Cipher {
|
|||||||
override val algorithm: CipherAlgorithm
|
override val algorithm: CipherAlgorithm
|
||||||
get() = CipherAlgorithm.aes
|
get() = CipherAlgorithm.aes
|
||||||
|
|
||||||
|
var forEncryption: Boolean = true
|
||||||
|
var cipherInstance: javax.crypto.Cipher? = null;
|
||||||
|
var secretKey: SecretKeySpec? = null;
|
||||||
|
|
||||||
/* override fun encrypt(data: ByteArray, key: ByteArray): ByteArray {
|
/* override fun encrypt(data: ByteArray, key: ByteArray): ByteArray {
|
||||||
val sk: SecretKey = SecretKeySpec(key, "AES")
|
val sk: SecretKey = SecretKeySpec(key, "AES")
|
||||||
val cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding")
|
val cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding")
|
||||||
@ -35,25 +39,46 @@ class AES : Cipher {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
override fun encrypt(data: ByteArray, key: ByteArray): ByteArray {
|
override fun encrypt(data: ByteArray, key: ByteArray): ByteArray {
|
||||||
val sk: SecretKey = SecretKeySpec(key, "AES")
|
val list : List<ByteArray> = encryptAsList(data, key)
|
||||||
val cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding")
|
return list.first().plus(list.last())
|
||||||
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, sk)
|
}
|
||||||
// javax.crypto representation = [CIPHERTEXT(n-16)]
|
|
||||||
val bytes = cipher.doFinal(data)
|
override fun encryptAsList(data: ByteArray, key: ByteArray): List<ByteArray> {
|
||||||
val iv = cipher.iv
|
val sk = SecretKeySpec(key, "AES")
|
||||||
|
if (cipherInstance == null || !forEncryption || secretKey != sk) {
|
||||||
|
secretKey = sk
|
||||||
|
forEncryption = true
|
||||||
// native.crypto representation = [IV(16) || CIPHERTEXT(n-16)]
|
// native.crypto representation = [IV(16) || CIPHERTEXT(n-16)]
|
||||||
return iv.plus(bytes)
|
// javax.crypto representation = [CIPHERTEXT(n-16)]
|
||||||
|
cipherInstance = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
|
cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk)
|
||||||
|
}
|
||||||
|
// javax.crypto representation = [CIPHERTEXT(n-16)]
|
||||||
|
val bytes: ByteArray = cipherInstance!!.doFinal(data)
|
||||||
|
val iv: ByteArray = cipherInstance!!.iv
|
||||||
|
// native.crypto representation = [IV(16) || CIPHERTEXT(n-16)]
|
||||||
|
return listOf(iv, bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun decrypt(data: ByteArray, key: ByteArray): ByteArray {
|
override fun decrypt(data: ByteArray, key: ByteArray): ByteArray {
|
||||||
val sk: SecretKey = SecretKeySpec(key, "AES")
|
|
||||||
// native.crypto representation = [IV(16) || CIPHERTEXT(n-16)]
|
|
||||||
val iv: ByteArray = data.take(16).toByteArray()
|
|
||||||
// javax.crypto representation = [CIPHERTEXT(n-16)]
|
// javax.crypto representation = [CIPHERTEXT(n-16)]
|
||||||
|
val iv: ByteArray = data.take(16).toByteArray()
|
||||||
val payload: ByteArray = data.drop(16).toByteArray()
|
val payload: ByteArray = data.drop(16).toByteArray()
|
||||||
|
return decryptAsList(listOf(iv, payload), key)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decryptAsList(data: List<ByteArray>, key: ByteArray): ByteArray {
|
||||||
|
if (cipherInstance == null) {
|
||||||
|
// native.crypto representation = [IV(16) || CIPHERTEXT(n-16)]
|
||||||
|
// javax.crypto representation = [CIPHERTEXT(n-16)]
|
||||||
|
cipherInstance = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||||
|
}
|
||||||
|
val sk = SecretKeySpec(key, "AES")
|
||||||
|
val iv: ByteArray = data.first()
|
||||||
val ivSpec = IvParameterSpec(iv)
|
val ivSpec = IvParameterSpec(iv)
|
||||||
val cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding")
|
cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, ivSpec)
|
||||||
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, sk, ivSpec)
|
forEncryption = false
|
||||||
return cipher.doFinal(payload)
|
val payload: ByteArray = data.last()
|
||||||
|
return cipherInstance!!.doFinal(payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,4 +7,6 @@ interface Cipher {
|
|||||||
|
|
||||||
fun encrypt(data: ByteArray, key: ByteArray): ByteArray
|
fun encrypt(data: ByteArray, key: ByteArray): ByteArray
|
||||||
fun decrypt(data: ByteArray, key: ByteArray): ByteArray
|
fun decrypt(data: ByteArray, key: ByteArray): ByteArray
|
||||||
|
fun encryptAsList(data: ByteArray, key: ByteArray): List<ByteArray>
|
||||||
|
fun decryptAsList(data: List<ByteArray>, key: ByteArray): ByteArray
|
||||||
}
|
}
|
@ -1,4 +1 @@
|
|||||||
include: package:flutter_lints/flutter.yaml
|
include: package:wyatt_analysis/analysis_options.flutter.experimental.yaml
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -2,74 +2,13 @@
|
|||||||
// Email: git@pcl.ovh
|
// Email: git@pcl.ovh
|
||||||
// -----
|
// -----
|
||||||
// File: native_crypto_platform_interface.dart
|
// File: native_crypto_platform_interface.dart
|
||||||
// Created Date: 25/12/2021 16:43:49
|
// Created Date: 24/05/2022 19:39:11
|
||||||
// Last Modified: 25/12/2021 17:39:39
|
// Last Modified: 24/05/2022 19:39:58
|
||||||
// -----
|
// -----
|
||||||
// Copyright (c) 2021
|
// Copyright (c) 2022
|
||||||
|
|
||||||
import 'dart:typed_data';
|
library native_crypto_platform_interface;
|
||||||
|
|
||||||
import './src/method_channel_native_crypto.dart';
|
export 'src/method_channel/method_channel_native_crypto.dart';
|
||||||
import './src/platform_interface.dart';
|
export 'src/platform_interface/native_crypto_platform.dart';
|
||||||
|
export 'src/utils/exception.dart';
|
||||||
/// The interface that implementations of path_provider must implement.
|
|
||||||
///
|
|
||||||
/// Platform implementations should extend this class rather than implement it as `NativeCrypto`
|
|
||||||
/// does not consider newly added methods to be breaking changes. Extending this class
|
|
||||||
/// (using `extends`) ensures that the subclass will get the default implementation, while
|
|
||||||
/// platform implementations that `implements` this interface will be broken by newly added
|
|
||||||
/// [NativeCryptoPlatform] methods.
|
|
||||||
abstract class NativeCryptoPlatform extends PlatformInterface {
|
|
||||||
/// Constructs a NativeCryptoPlatform.
|
|
||||||
NativeCryptoPlatform() : super(token: _token);
|
|
||||||
|
|
||||||
static final Object _token = Object();
|
|
||||||
|
|
||||||
static NativeCryptoPlatform _instance = MethodChannelNativeCrypto();
|
|
||||||
|
|
||||||
/// The default instance of [NativeCryptoPlatform] to use.
|
|
||||||
///
|
|
||||||
/// Defaults to [MethodChannelPathProvider].
|
|
||||||
static NativeCryptoPlatform get instance => _instance;
|
|
||||||
|
|
||||||
/// Platform-specific plugins should set this with their own platform-specific
|
|
||||||
/// class that extends [NativeCryptoPlatform] when they register themselves.
|
|
||||||
static set instance(NativeCryptoPlatform instance) {
|
|
||||||
PlatformInterface.verifyToken(instance, _token);
|
|
||||||
_instance = instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List?> digest(Uint8List data, String algorithm) {
|
|
||||||
throw UnimplementedError('digest is not implemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List?> generateSecretKey(int bitsCount) {
|
|
||||||
throw UnimplementedError('generateSecretKey is not implemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List?> generateKeyPair() {
|
|
||||||
throw UnimplementedError('generateKeyPair is not implemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List?> pbkdf2(String password, String salt, int keyBytesCount,
|
|
||||||
int iterations, String algorithm) {
|
|
||||||
throw UnimplementedError('pbkdf2 is not implemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List?> encrypt(Uint8List data, Uint8List key, String algorithm) {
|
|
||||||
throw UnimplementedError('encrypt is not implemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List?> decrypt(Uint8List data, Uint8List key, String algorithm) {
|
|
||||||
throw UnimplementedError('decrypt is not implemented');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Uint8List?> generateSharedSecretKey(
|
|
||||||
Uint8List salt,
|
|
||||||
int keyBytesCount,
|
|
||||||
Uint8List ephemeralPrivateKey,
|
|
||||||
Uint8List otherPublicKey,
|
|
||||||
String hkdfAlgorithm) {
|
|
||||||
throw UnimplementedError('generateSharedSecretKey is not implemented');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// -----
|
// -----
|
||||||
// File: native_crypto_method_channel.dart
|
// File: native_crypto_method_channel.dart
|
||||||
// Created Date: 25/12/2021 16:58:04
|
// Created Date: 25/12/2021 16:58:04
|
||||||
// Last Modified: 25/12/2021 18:58:53
|
// Last Modified: 24/05/2022 22:59:32
|
||||||
// -----
|
// -----
|
||||||
// Copyright (c) 2021
|
// Copyright (c) 2021
|
||||||
|
|
||||||
@ -11,19 +11,17 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:native_crypto_platform_interface/src/platform_interface/native_crypto_platform.dart';
|
||||||
import '../native_crypto_platform_interface.dart';
|
|
||||||
|
|
||||||
/// An implementation of [NativeCryptoPlatform] that uses method channels.
|
/// An implementation of [NativeCryptoPlatform] that uses method channels.
|
||||||
class MethodChannelNativeCrypto extends NativeCryptoPlatform {
|
class MethodChannelNativeCrypto extends NativeCryptoPlatform {
|
||||||
/// The method channel used to interact with the native platform.
|
/// The method channel used to interact with the native platform.
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
MethodChannel methodChannel =
|
MethodChannel channel = const MethodChannel('plugins.hugop.cl/native_crypto');
|
||||||
const MethodChannel('plugins.hugop.cl/native_crypto');
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List?> digest(Uint8List data, String algorithm) {
|
Future<Uint8List?> digest(Uint8List data, String algorithm) {
|
||||||
return methodChannel.invokeMethod<Uint8List>(
|
return channel.invokeMethod<Uint8List>(
|
||||||
'digest',
|
'digest',
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'data': data,
|
'data': data,
|
||||||
@ -34,7 +32,7 @@ class MethodChannelNativeCrypto extends NativeCryptoPlatform {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List?> generateSecretKey(int bitsCount) {
|
Future<Uint8List?> generateSecretKey(int bitsCount) {
|
||||||
return methodChannel.invokeMethod<Uint8List>(
|
return channel.invokeMethod<Uint8List>(
|
||||||
'generateSecretKey',
|
'generateSecretKey',
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'bitsCount': bitsCount,
|
'bitsCount': bitsCount,
|
||||||
@ -43,14 +41,14 @@ class MethodChannelNativeCrypto extends NativeCryptoPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List?> generateKeyPair() {
|
Future<Uint8List?> pbkdf2(
|
||||||
return methodChannel.invokeMethod<Uint8List>('generateKeyPair');
|
String password,
|
||||||
}
|
String salt,
|
||||||
|
int keyBytesCount,
|
||||||
@override
|
int iterations,
|
||||||
Future<Uint8List?> pbkdf2(String password, String salt, int keyBytesCount,
|
String algorithm,
|
||||||
int iterations, String algorithm) {
|
) {
|
||||||
return methodChannel.invokeMethod<Uint8List>(
|
return channel.invokeMethod<Uint8List>(
|
||||||
'pbkdf2',
|
'pbkdf2',
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'password': password,
|
'password': password,
|
||||||
@ -63,8 +61,44 @@ class MethodChannelNativeCrypto extends NativeCryptoPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List?> encrypt(Uint8List data, Uint8List key, String algorithm) {
|
Future<List<Uint8List>?> encryptAsList(
|
||||||
return methodChannel.invokeMethod<Uint8List>(
|
Uint8List data,
|
||||||
|
Uint8List key,
|
||||||
|
String algorithm,
|
||||||
|
) {
|
||||||
|
return channel.invokeListMethod(
|
||||||
|
'encryptAsList',
|
||||||
|
<String, dynamic>{
|
||||||
|
'data': data,
|
||||||
|
'key': key,
|
||||||
|
'algorithm': algorithm,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List?> decryptAsList(
|
||||||
|
List<Uint8List> data,
|
||||||
|
Uint8List key,
|
||||||
|
String algorithm,
|
||||||
|
) {
|
||||||
|
return channel.invokeMethod<Uint8List>(
|
||||||
|
'decryptAsList',
|
||||||
|
<String, dynamic>{
|
||||||
|
'data': data,
|
||||||
|
'key': key,
|
||||||
|
'algorithm': algorithm,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List?> encrypt(
|
||||||
|
Uint8List data,
|
||||||
|
Uint8List key,
|
||||||
|
String algorithm,
|
||||||
|
) {
|
||||||
|
return channel.invokeMethod<Uint8List>(
|
||||||
'encrypt',
|
'encrypt',
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'data': data,
|
'data': data,
|
||||||
@ -75,8 +109,12 @@ class MethodChannelNativeCrypto extends NativeCryptoPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List?> decrypt(Uint8List data, Uint8List key, String algorithm) {
|
Future<Uint8List?> decrypt(
|
||||||
return methodChannel.invokeMethod<Uint8List>(
|
Uint8List data,
|
||||||
|
Uint8List key,
|
||||||
|
String algorithm,
|
||||||
|
) {
|
||||||
|
return channel.invokeMethod<Uint8List>(
|
||||||
'decrypt',
|
'decrypt',
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'data': data,
|
'data': data,
|
||||||
@ -85,23 +123,4 @@ class MethodChannelNativeCrypto extends NativeCryptoPlatform {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Uint8List?> generateSharedSecretKey(
|
|
||||||
Uint8List salt,
|
|
||||||
int keyBytesCount,
|
|
||||||
Uint8List ephemeralPrivateKey,
|
|
||||||
Uint8List otherPublicKey,
|
|
||||||
String hkdfAlgorithm) {
|
|
||||||
return methodChannel.invokeMethod<Uint8List>(
|
|
||||||
'generateSharedSecretKey',
|
|
||||||
<String, dynamic>{
|
|
||||||
'salt': salt,
|
|
||||||
'keyBytesCount': keyBytesCount,
|
|
||||||
'ephemeralPrivateKey': ephemeralPrivateKey,
|
|
||||||
'otherPublicKey': otherPublicKey,
|
|
||||||
'hkdfAlgorithm': hkdfAlgorithm,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,97 +0,0 @@
|
|||||||
// Author: Hugo Pointcheval
|
|
||||||
// Email: git@pcl.ovh
|
|
||||||
// -----
|
|
||||||
// File: platform_interface.dart
|
|
||||||
// Created Date: 25/12/2021 16:52:56
|
|
||||||
// Last Modified: 27/12/2021 21:25:39
|
|
||||||
// -----
|
|
||||||
// Copyright (c) 2021
|
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
|
|
||||||
/// Base class for platform interfaces.
|
|
||||||
///
|
|
||||||
/// Provides a static helper method for ensuring that platform interfaces are
|
|
||||||
/// implemented using `extends` instead of `implements`.
|
|
||||||
///
|
|
||||||
/// Platform interface classes are expected to have a private static token object which will be
|
|
||||||
/// be passed to [verifyToken] along with a platform interface object for verification.
|
|
||||||
///
|
|
||||||
/// Sample usage:
|
|
||||||
///
|
|
||||||
/// ```dart
|
|
||||||
/// abstract class NativeCryptoPlatform extends PlatformInterface {
|
|
||||||
/// NativeCryptoPlatform() : super(token: _token);
|
|
||||||
///
|
|
||||||
/// static NativeCryptoPlatform _instance = MethodChannelNativeCrypto();
|
|
||||||
///
|
|
||||||
/// static const Object _token = Object();
|
|
||||||
///
|
|
||||||
/// static NativeCryptoPlatform get instance => _instance;
|
|
||||||
///
|
|
||||||
/// /// Platform-specific plugins should set this with their own platform-specific
|
|
||||||
/// /// class that extends [NativeCryptoPlatform] when they register themselves.
|
|
||||||
/// static set instance(NativeCryptoPlatform instance) {
|
|
||||||
/// PlatformInterface.verifyToken(instance, _token);
|
|
||||||
/// _instance = instance;
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Mockito mocks of platform interfaces will fail the verification, in test code only it is possible
|
|
||||||
/// to include the [MockPlatformInterfaceMixin] for the verification to be temporarily disabled. See
|
|
||||||
/// [MockPlatformInterfaceMixin] for a sample of using Mockito to mock a platform interface.
|
|
||||||
abstract class PlatformInterface {
|
|
||||||
/// Pass a private, class-specific `const Object()` as the `token`.
|
|
||||||
PlatformInterface({required Object token}) : _instanceToken = token;
|
|
||||||
|
|
||||||
final Object? _instanceToken;
|
|
||||||
|
|
||||||
/// Ensures that the platform instance has a token that matches the
|
|
||||||
/// provided token and throws [AssertionError] if not.
|
|
||||||
///
|
|
||||||
/// This is used to ensure that implementers are using `extends` rather than
|
|
||||||
/// `implements`.
|
|
||||||
///
|
|
||||||
/// Subclasses of [MockPlatformInterfaceMixin] are assumed to be valid in debug
|
|
||||||
/// builds.
|
|
||||||
///
|
|
||||||
/// This is implemented as a static method so that it cannot be overridden
|
|
||||||
/// with `noSuchMethod`.
|
|
||||||
static void verifyToken(PlatformInterface instance, Object token) {
|
|
||||||
if (instance is MockPlatformInterfaceMixin) {
|
|
||||||
bool assertionsEnabled = false;
|
|
||||||
assert(() {
|
|
||||||
assertionsEnabled = true;
|
|
||||||
return true;
|
|
||||||
}());
|
|
||||||
if (!assertionsEnabled) {
|
|
||||||
throw AssertionError(
|
|
||||||
'`MockPlatformInterfaceMixin` is not intended for use in release builds.');
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!identical(token, instance._instanceToken)) {
|
|
||||||
throw AssertionError(
|
|
||||||
'Platform interfaces must not be implemented with `implements`');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [PlatformInterface] mixin that can be combined with mockito's `Mock`.
|
|
||||||
///
|
|
||||||
/// It passes the [PlatformInterface.verifyToken] check even though it isn't
|
|
||||||
/// using `extends`.
|
|
||||||
///
|
|
||||||
/// This class is intended for use in tests only.
|
|
||||||
///
|
|
||||||
/// Sample usage (assuming NativeCryptoPlatform extends [PlatformInterface]:
|
|
||||||
///
|
|
||||||
/// ```dart
|
|
||||||
/// class NativeCryptoPlatformMock extends Mock
|
|
||||||
/// with MockPlatformInterfaceMixin
|
|
||||||
/// implements NativeCryptoPlatform {}
|
|
||||||
/// ```
|
|
||||||
@visibleForTesting
|
|
||||||
abstract class MockPlatformInterfaceMixin implements PlatformInterface {}
|
|
@ -0,0 +1,92 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: native_crypto_platform_interface.dart
|
||||||
|
// Created Date: 25/12/2021 16:43:49
|
||||||
|
// Last Modified: 24/05/2022 22:58:31
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2021
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:native_crypto_platform_interface/src/method_channel/method_channel_native_crypto.dart';
|
||||||
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||||
|
|
||||||
|
/// The interface that implementations of path_provider must implement.
|
||||||
|
///
|
||||||
|
/// Platform implementations should extend this class rather than implement
|
||||||
|
/// it as `NativeCrypto` does not consider newly added methods to be
|
||||||
|
/// breaking changes. Extending this class (using `extends`) ensures
|
||||||
|
/// that the subclass will get the default implementation, while platform
|
||||||
|
/// implementations that `implements` this interface will be
|
||||||
|
/// broken by newly added [NativeCryptoPlatform] methods.
|
||||||
|
abstract class NativeCryptoPlatform extends PlatformInterface {
|
||||||
|
/// Constructs a NativeCryptoPlatform.
|
||||||
|
NativeCryptoPlatform() : super(token: _token);
|
||||||
|
|
||||||
|
static final Object _token = Object();
|
||||||
|
|
||||||
|
static NativeCryptoPlatform _instance = MethodChannelNativeCrypto();
|
||||||
|
|
||||||
|
/// The default instance of [NativeCryptoPlatform] to use.
|
||||||
|
///
|
||||||
|
/// Defaults to [MethodChannelNativeCrypto].
|
||||||
|
static NativeCryptoPlatform get instance => _instance;
|
||||||
|
|
||||||
|
/// Platform-specific plugins should set this with their own platform-specific
|
||||||
|
/// class that extends [NativeCryptoPlatform] when they register themselves.
|
||||||
|
static set instance(NativeCryptoPlatform instance) {
|
||||||
|
PlatformInterface.verifyToken(instance, _token);
|
||||||
|
_instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> digest(Uint8List data, String algorithm) {
|
||||||
|
throw UnimplementedError('digest is not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> generateSecretKey(int bitsCount) {
|
||||||
|
throw UnimplementedError('generateSecretKey is not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> pbkdf2(
|
||||||
|
String password,
|
||||||
|
String salt,
|
||||||
|
int keyBytesCount,
|
||||||
|
int iterations,
|
||||||
|
String algorithm,
|
||||||
|
) {
|
||||||
|
throw UnimplementedError('pbkdf2 is not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Uint8List>?> encryptAsList(
|
||||||
|
Uint8List data,
|
||||||
|
Uint8List key,
|
||||||
|
String algorithm,
|
||||||
|
) {
|
||||||
|
throw UnimplementedError('encryptAsList is not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> decryptAsList(
|
||||||
|
List<Uint8List> data,
|
||||||
|
Uint8List key,
|
||||||
|
String algorithm,
|
||||||
|
) {
|
||||||
|
throw UnimplementedError('decryptAsList is not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> encrypt(
|
||||||
|
Uint8List data,
|
||||||
|
Uint8List key,
|
||||||
|
String algorithm,
|
||||||
|
) {
|
||||||
|
throw UnimplementedError('encrypt is not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List?> decrypt(
|
||||||
|
Uint8List data,
|
||||||
|
Uint8List key,
|
||||||
|
String algorithm,
|
||||||
|
) {
|
||||||
|
throw UnimplementedError('decrypt is not implemented');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
// Author: Hugo Pointcheval
|
||||||
|
// Email: git@pcl.ovh
|
||||||
|
// -----
|
||||||
|
// File: exception.dart
|
||||||
|
// Created Date: 24/05/2022 18:54:48
|
||||||
|
// Last Modified: 24/05/2022 18:58:39
|
||||||
|
// -----
|
||||||
|
// Copyright (c) 2022
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class NativeCryptoException implements Exception {
|
||||||
|
final String message;
|
||||||
|
const NativeCryptoException(this.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Catches a [PlatformException] and returns an [Exception].
|
||||||
|
///
|
||||||
|
/// If the [Exception] is a [PlatformException],
|
||||||
|
/// a [NativeCryptoException] is returned.
|
||||||
|
Never convertPlatformException(Object exception, StackTrace stackTrace) {
|
||||||
|
if (exception is! Exception || exception is! PlatformException) {
|
||||||
|
Error.throwWithStackTrace(exception, stackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
Error.throwWithStackTrace(
|
||||||
|
platformExceptionToNativeCryptoException(exception, stackTrace),
|
||||||
|
stackTrace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a [PlatformException] into a [NativeCryptoException].
|
||||||
|
///
|
||||||
|
/// A [PlatformException] can only be converted to a [NativeCryptoException]
|
||||||
|
/// if the `details` of the exception exist.
|
||||||
|
NativeCryptoException platformExceptionToNativeCryptoException(
|
||||||
|
PlatformException platformException,
|
||||||
|
StackTrace stackTrace,
|
||||||
|
) {
|
||||||
|
final Map<String, String>? details = platformException.details != null
|
||||||
|
? Map<String, String>.from(
|
||||||
|
platformException.details as Map<String, String>,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
String message = platformException.message ?? '';
|
||||||
|
|
||||||
|
if (details != null) {
|
||||||
|
message = details['message'] ?? message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NativeCryptoException(message);
|
||||||
|
}
|
@ -3,15 +3,21 @@ description: A common interface for NativeCrypto plugin.
|
|||||||
version: 0.1.0
|
version: 0.1.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.15.0 <3.0.0"
|
sdk: ">=2.17.0 <3.0.0"
|
||||||
flutter: ">=2.5.0"
|
flutter: ">=2.5.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
plugin_platform_interface: ^2.1.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
flutter_lints: ^1.0.4
|
wyatt_analysis:
|
||||||
|
git:
|
||||||
|
url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages
|
||||||
|
ref: wyatt_analysis-v2.1.0
|
||||||
|
path: packages/wyatt_analysis
|
Loading…
x
Reference in New Issue
Block a user