Fix/Update #1
| @ -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,20 +174,34 @@ class BenchmarkPage extends ConsumerWidget { | |||||||
|               alignment: Alignment.centerLeft, |               alignment: Alignment.centerLeft, | ||||||
|             ), |             ), | ||||||
|             keyContent, |             keyContent, | ||||||
|             Row( |             Column( | ||||||
|               mainAxisAlignment: MainAxisAlignment.spaceEvenly, |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|               children: [ |               children: [ | ||||||
|                 Button( |                 Row( | ||||||
|                   () => _benchmark(ref, cipher), |                   mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||||
|                   "NativeCrypto", |                   children: [ | ||||||
|  |                     Button( | ||||||
|  |                       () => _benchmark(ref, cipher), | ||||||
|  |                       "NativeCrypto", | ||||||
|  |                     ), | ||||||
|  |                     Button( | ||||||
|  |                       () => _benchmark(ref, cipher, usePc: true), | ||||||
|  |                       "PointyCastle", | ||||||
|  |                     ), | ||||||
|  |                     Button( | ||||||
|  |                       _clear, | ||||||
|  |                       "Clear", | ||||||
|  |                     ), | ||||||
|  |                   ], | ||||||
|                 ), |                 ), | ||||||
|                 Button( |                 Row( | ||||||
|                   () => _benchmark(ref, cipher, usePc: true), |                   mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||||
|                   "PointyCastle", |                   children: [ | ||||||
|                 ), |                     Button( | ||||||
|                 Button( |                       () => _benchmarkEncryptionOnly(ref, cipher), | ||||||
|                   _clear, |                       "NC Persistence", | ||||||
|                   "Clear", |                     ), | ||||||
|  |                   ], | ||||||
|                 ), |                 ), | ||||||
|               ], |               ], | ||||||
|             ), |             ), | ||||||
|  | |||||||
| @ -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,22 +98,17 @@ class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun handleEncrypt(arguments: Map<String, Any>?): Task<ByteArray> { |     private fun lazyLoadCipher(cipherAlgorithm: CipherAlgorithm) { | ||||||
|         return Task { |         if (cipherInstance == null) { | ||||||
|             val data: ByteArray = |             cipherInstance = cipherAlgorithm.getCipher() | ||||||
|                 Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray |         } else { | ||||||
|             val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray |             if (cipherInstance!!.algorithm != cipherAlgorithm) { | ||||||
|             val algorithm: String = |                 cipherInstance = cipherAlgorithm.getCipher() | ||||||
|                 Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String |             } | ||||||
| 
 |  | ||||||
|             val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) |  | ||||||
|             val cipher = cipherAlgorithm.getCipher() |  | ||||||
| 
 |  | ||||||
|             cipher.encrypt(data, key) |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun handleDecrypt(arguments: Map<String, Any>?): Task<ByteArray> { |     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 | ||||||
| @ -119,9 +117,44 @@ 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) |             cipherInstance!!.encryptAsList(data, key) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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 { | ||||||
|  |             val data: ByteArray = | ||||||
|  |                 Objects.requireNonNull(arguments?.get(Constants.DATA)) as 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) | ||||||
|  | 
 | ||||||
|  |             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) |     } | ||||||
|  | 
 | ||||||
|  |     override fun encryptAsList(data: ByteArray, key: ByteArray): List<ByteArray> { | ||||||
|  |         val sk = SecretKeySpec(key, "AES") | ||||||
|  |         if (cipherInstance == null || !forEncryption || secretKey != sk) { | ||||||
|  |             secretKey = sk | ||||||
|  |             forEncryption = true | ||||||
|  |             // native.crypto representation = [IV(16) || CIPHERTEXT(n-16)] | ||||||
|  |             // 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)] |         // javax.crypto representation = [CIPHERTEXT(n-16)] | ||||||
|         val bytes = cipher.doFinal(data) |         val bytes: ByteArray = cipherInstance!!.doFinal(data) | ||||||
|         val iv = cipher.iv |         val iv: ByteArray = cipherInstance!!.iv | ||||||
|         // native.crypto representation = [IV(16) || CIPHERTEXT(n-16)] |         // native.crypto representation = [IV(16) || CIPHERTEXT(n-16)] | ||||||
|         return iv.plus(bytes) |         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