Fix/Update #1
| @ -1,3 +1,35 @@ | |||||||
|  | ## 0.1.0 | ||||||
|  | 
 | ||||||
|  | > Breaking changes ! | ||||||
|  | 
 | ||||||
|  | * Follow **Federated Plugin** Flutter standard. | ||||||
|  | 
 | ||||||
|  | ## 0.0.6 | ||||||
|  | 
 | ||||||
|  | * Add KeyPair generation. | ||||||
|  | * Rework exposed API. | ||||||
|  | 
 | ||||||
|  | ## 0.0.5 | ||||||
|  | 
 | ||||||
|  | * New API. | ||||||
|  | * Add digest support. | ||||||
|  | * Clean platform specific code base. | ||||||
|  | 
 | ||||||
|  | ## 0.0.4 | ||||||
|  | 
 | ||||||
|  | * Improve AES. | ||||||
|  | 
 | ||||||
|  | ## 0.0.3 | ||||||
|  | 
 | ||||||
|  | * Add PBKDF2 support. | ||||||
|  | * Add exceptions. | ||||||
|  | * Improve documentation. | ||||||
|  | 
 | ||||||
|  | ## 0.0.2 | ||||||
|  | 
 | ||||||
|  | * Add different key size support. | ||||||
|  | * Improve performances. | ||||||
|  | 
 | ||||||
| ## 0.0.1 | ## 0.0.1 | ||||||
| 
 | 
 | ||||||
| * TODO: Describe initial release. | * First AES cross-platform encryption & decryption implementation. | ||||||
|  | |||||||
| @ -1 +1,23 @@ | |||||||
| TODO: Add your license here. | NativeCrypto | ||||||
|  | 
 | ||||||
|  | MIT License | ||||||
|  | 
 | ||||||
|  | Copyright (c) 2019 - 2022 Hugo Pointcheval | ||||||
|  | 
 | ||||||
|  | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | of this software and associated documentation files (the "Software"), to deal | ||||||
|  | in the Software without restriction, including without limitation the rights | ||||||
|  | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  | copies of the Software, and to permit persons to whom the Software is | ||||||
|  | furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  | The above copyright notice and this permission notice shall be included in all | ||||||
|  | copies or substantial portions of the Software. | ||||||
|  | 
 | ||||||
|  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||||
|  | SOFTWARE. | ||||||
| @ -1,6 +1,6 @@ | |||||||
| # native_crypto | # NativeCrypto | ||||||
| 
 | 
 | ||||||
| A new flutter plugin project. | Fast and powerful cryptographic functions thanks to **javax.crypto** and **CryptoKit**. | ||||||
| 
 | 
 | ||||||
| ## Getting Started | ## Getting Started | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ android { | |||||||
|     defaultConfig { |     defaultConfig { | ||||||
|         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). |         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). | ||||||
|         applicationId "fr.pointcheval.native_crypto_example" |         applicationId "fr.pointcheval.native_crypto_example" | ||||||
|         minSdkVersion flutter.minSdkVersion |         minSdkVersion 26 | ||||||
|         targetSdkVersion flutter.targetSdkVersion |         targetSdkVersion flutter.targetSdkVersion | ||||||
|         versionCode flutterVersionCode.toInteger() |         versionCode flutterVersionCode.toInteger() | ||||||
|         versionName flutterVersionName |         versionName flutterVersionName | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
|    <application |    <application | ||||||
|         android:label="native_crypto_example" |         android:label="native_crypto_example" | ||||||
|         android:name="${applicationName}" |         android:name="${applicationName}" | ||||||
|  |         android:largeHeap="true" | ||||||
|         android:icon="@mipmap/ic_launcher"> |         android:icon="@mipmap/ic_launcher"> | ||||||
|         <activity |         <activity | ||||||
|             android:name=".MainActivity" |             android:name=".MainActivity" | ||||||
|  | |||||||
							
								
								
									
										68
									
								
								native_crypto/example/lib/home.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								native_crypto/example/lib/home.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: home.dart | ||||||
|  | // Created Date: 28/12/2021 13:48:36 | ||||||
|  | // Last Modified: 28/12/2021 15:18:03 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:native_crypto_example/pages/benchmark_page.dart'; | ||||||
|  | 
 | ||||||
|  | import 'pages/cipher_page.dart'; | ||||||
|  | import 'pages/kdf_page.dart'; | ||||||
|  | 
 | ||||||
|  | class Home extends StatefulWidget { | ||||||
|  |   const Home({Key? key}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _HomeState createState() => _HomeState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _HomeState extends State<Home> { | ||||||
|  |   int _currentIndex = 0; | ||||||
|  |   final List<Widget> _children = [ | ||||||
|  |     KdfPage(), | ||||||
|  |     CipherPage(), | ||||||
|  |     BenchmarkPage() | ||||||
|  |   ]; | ||||||
|  | 
 | ||||||
|  |   void onTabTapped(int index) { | ||||||
|  |     setState(() { | ||||||
|  |       _currentIndex = index; | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Scaffold( | ||||||
|  |       appBar: AppBar( | ||||||
|  |         centerTitle: true, | ||||||
|  |         title: const Text('Native Crypto'), | ||||||
|  |       ), | ||||||
|  |       body: _children[_currentIndex], | ||||||
|  |       bottomNavigationBar: BottomNavigationBar( | ||||||
|  |         selectedItemColor: Colors.blue, | ||||||
|  |         unselectedItemColor: Colors.black, | ||||||
|  |         showUnselectedLabels: true, | ||||||
|  |         onTap: onTabTapped, // new | ||||||
|  |         currentIndex: _currentIndex, // new | ||||||
|  |         items: const [ | ||||||
|  |           BottomNavigationBarItem( | ||||||
|  |             icon: Icon(Icons.vpn_key), | ||||||
|  |             label: 'Key', | ||||||
|  |           ), | ||||||
|  |           BottomNavigationBarItem( | ||||||
|  |             icon: Icon(Icons.lock), | ||||||
|  |             label: 'Encryption', | ||||||
|  |           ), | ||||||
|  |           BottomNavigationBarItem( | ||||||
|  |             icon: Icon(Icons.timer), | ||||||
|  |             label: 'Benchmark', | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,62 +1,25 @@ | |||||||
| import 'package:flutter/material.dart'; | // Author: Hugo Pointcheval | ||||||
| import 'dart:async'; | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: main.dart | ||||||
|  | // Created Date: 27/12/2021 21:15:12 | ||||||
|  | // Last Modified: 28/12/2021 13:51:36 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:native_crypto/native_crypto.dart'; | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
|  | import 'package:native_crypto_example/home.dart'; | ||||||
| 
 | 
 | ||||||
| void main() { | void main() { | ||||||
|   runApp(const MyApp()); |   runApp(const ProviderScope(child: MyApp())); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class MyApp extends StatefulWidget { | class MyApp extends StatelessWidget { | ||||||
|   const MyApp({Key? key}) : super(key: key); |   const MyApp({Key? key}) : super(key: key); | ||||||
| 
 | 
 | ||||||
|   @override |  | ||||||
|   State<MyApp> createState() => _MyAppState(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class _MyAppState extends State<MyApp> { |  | ||||||
|   String _platformVersion = 'Unknown'; |  | ||||||
| 
 |  | ||||||
|   @override |  | ||||||
|   void initState() { |  | ||||||
|     super.initState(); |  | ||||||
|     initPlatformState(); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // Platform messages are asynchronous, so we initialize in an async method. |  | ||||||
|   Future<void> initPlatformState() async { |  | ||||||
|     String platformVersion; |  | ||||||
|     // Platform messages may fail, so we use a try/catch PlatformException. |  | ||||||
|     // We also handle the message potentially returning null. |  | ||||||
|     try { |  | ||||||
|       platformVersion = |  | ||||||
|           await NativeCrypto.platformVersion ?? 'Unknown platform version'; |  | ||||||
|     } on PlatformException { |  | ||||||
|       platformVersion = 'Failed to get platform version.'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // If the widget was removed from the tree while the asynchronous platform |  | ||||||
|     // message was in flight, we want to discard the reply rather than calling |  | ||||||
|     // setState to update our non-existent appearance. |  | ||||||
|     if (!mounted) return; |  | ||||||
| 
 |  | ||||||
|     setState(() { |  | ||||||
|       _platformVersion = platformVersion; |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return MaterialApp( |     return const MaterialApp(home: Home()); | ||||||
|       home: Scaffold( |  | ||||||
|         appBar: AppBar( |  | ||||||
|           title: const Text('Plugin example app'), |  | ||||||
|         ), |  | ||||||
|         body: Center( |  | ||||||
|           child: Text('Running on: $_platformVersion\n'), |  | ||||||
|         ), |  | ||||||
|       ), |  | ||||||
|     ); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										137
									
								
								native_crypto/example/lib/pages/benchmark_page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								native_crypto/example/lib/pages/benchmark_page.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,137 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: benchmark_page.dart | ||||||
|  | // Created Date: 28/12/2021 15:12:39 | ||||||
|  | // Last Modified: 28/12/2021 15:21:05 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
|  | import 'package:native_crypto/native_crypto.dart'; | ||||||
|  | import 'package:native_crypto_example/widgets/button.dart'; | ||||||
|  | 
 | ||||||
|  | import '../session.dart'; | ||||||
|  | import '../widgets/output.dart'; | ||||||
|  | 
 | ||||||
|  | class BenchmarkPage extends ConsumerWidget { | ||||||
|  |   BenchmarkPage({Key? key}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   final Output keyContent = Output(); | ||||||
|  |   final Output benchmarkStatus = Output(large: true); | ||||||
|  | 
 | ||||||
|  |   Future<void> _benchmark(WidgetRef ref, Cipher cipher) async { | ||||||
|  |     Session state = ref.read(sessionProvider.state).state; | ||||||
|  | 
 | ||||||
|  |     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;encode time;decryption time;crypto time\n"; | ||||||
|  | 
 | ||||||
|  |     var beforeBench = DateTime.now(); | ||||||
|  |     for (int size in testedSizes) { | ||||||
|  |       var bigFile = Uint8List(size * 1000000); | ||||||
|  |       csv += "${size * 1000000};"; | ||||||
|  |       var cryptoTime = 0; | ||||||
|  | 
 | ||||||
|  |       // Encryption | ||||||
|  |       var before = DateTime.now(); | ||||||
|  |       var encryptedBigFile = await cipher.encrypt(bigFile); | ||||||
|  |       var after = DateTime.now(); | ||||||
|  | 
 | ||||||
|  |       var benchmark = | ||||||
|  |           after.millisecondsSinceEpoch - before.millisecondsSinceEpoch; | ||||||
|  |       benchmarkStatus.append('[$size MB] Encryption took $benchmark ms\n'); | ||||||
|  | 
 | ||||||
|  |       csv += "$benchmark;"; | ||||||
|  |       cryptoTime += benchmark; | ||||||
|  | 
 | ||||||
|  |       // Decryption | ||||||
|  |       before = DateTime.now(); | ||||||
|  |       await cipher.decrypt(encryptedBigFile); | ||||||
|  |       after = DateTime.now(); | ||||||
|  | 
 | ||||||
|  |       benchmark = after.millisecondsSinceEpoch - before.millisecondsSinceEpoch; | ||||||
|  |       benchmarkStatus.append('[$size MB] Decryption took $benchmark ms\n'); | ||||||
|  | 
 | ||||||
|  |       csv += "$benchmark;"; | ||||||
|  |       cryptoTime += benchmark; | ||||||
|  |       csv += "$cryptoTime\n"; | ||||||
|  |     } | ||||||
|  |     var afterBench = DateTime.now(); | ||||||
|  |     var benchmark = | ||||||
|  |         afterBench.millisecondsSinceEpoch - beforeBench.millisecondsSinceEpoch; | ||||||
|  |     var sum = testedSizes.reduce((a, b) => a + b); | ||||||
|  |     benchmarkStatus.append( | ||||||
|  |         'Benchmark finished.\nGenerated, encrypted and decrypted $sum MB in $benchmark ms'); | ||||||
|  |     debugPrint("[Benchmark cvs]\n$csv"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void _clear() { | ||||||
|  |     benchmarkStatus.clear(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     Session state = ref.read(sessionProvider.state).state; | ||||||
|  |     if (state.secretKey.bytes.isEmpty) { | ||||||
|  |       keyContent | ||||||
|  |           .print('No SecretKey!\nGo in Key tab and generate or derive one.'); | ||||||
|  |       return SingleChildScrollView( | ||||||
|  |         child: Padding( | ||||||
|  |           padding: const EdgeInsets.all(8.0), | ||||||
|  |           child: Column( | ||||||
|  |             children: [ | ||||||
|  |               const Align( | ||||||
|  |                 child: Text("Secret Key"), | ||||||
|  |                 alignment: Alignment.centerLeft, | ||||||
|  |               ), | ||||||
|  |               keyContent, | ||||||
|  |             ], | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     keyContent.print(state.secretKey.bytes.toString()); | ||||||
|  | 
 | ||||||
|  |     AES cipher = AES(state.secretKey, AESMode.gcm); | ||||||
|  | 
 | ||||||
|  |     return SingleChildScrollView( | ||||||
|  |       child: Padding( | ||||||
|  |         padding: const EdgeInsets.all(8.0), | ||||||
|  |         child: Column( | ||||||
|  |           children: [ | ||||||
|  |             const Align( | ||||||
|  |               child: Text("Secret Key"), | ||||||
|  |               alignment: Alignment.centerLeft, | ||||||
|  |             ), | ||||||
|  |             keyContent, | ||||||
|  |             Row( | ||||||
|  |               mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||||
|  |               children: [ | ||||||
|  |                 Button( | ||||||
|  |                   () => _benchmark(ref, cipher), | ||||||
|  |                   "Launch benchmark", | ||||||
|  |                 ), | ||||||
|  |                 Button( | ||||||
|  |                   _clear, | ||||||
|  |                   "Clear", | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             benchmarkStatus, | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										158
									
								
								native_crypto/example/lib/pages/cipher_page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								native_crypto/example/lib/pages/cipher_page.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,158 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: cipher_page.dart | ||||||
|  | // Created Date: 28/12/2021 13:33:15 | ||||||
|  | // Last Modified: 28/12/2021 15:20:43 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
|  | import 'package:native_crypto/native_crypto.dart'; | ||||||
|  | import 'package:native_crypto_example/widgets/button.dart'; | ||||||
|  | 
 | ||||||
|  | import '../session.dart'; | ||||||
|  | import '../utils.dart'; | ||||||
|  | import '../widgets/output.dart'; | ||||||
|  | 
 | ||||||
|  | // ignore: must_be_immutable | ||||||
|  | class CipherPage extends ConsumerWidget { | ||||||
|  |   CipherPage({Key? key}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   final Output keyContent = Output(); | ||||||
|  |   final Output encryptionStatus = Output(); | ||||||
|  |   final Output decryptionStatus = Output(); | ||||||
|  | 
 | ||||||
|  |   final TextEditingController _plainTextController = TextEditingController(); | ||||||
|  |   CipherText? cipherText; | ||||||
|  | 
 | ||||||
|  |   Future<void> _encrypt(WidgetRef ref, Cipher cipher) async { | ||||||
|  |     Session state = ref.read(sessionProvider.state).state; | ||||||
|  |     final plainText = _plainTextController.text.trim(); | ||||||
|  | 
 | ||||||
|  |     if (state.secretKey.bytes.isEmpty) { | ||||||
|  |       encryptionStatus | ||||||
|  |           .print('No SecretKey!\nGo in Key tab and generate or derive one.'); | ||||||
|  |     } else if (plainText.isEmpty) { | ||||||
|  |       encryptionStatus.print('Entry is empty'); | ||||||
|  |     } else { | ||||||
|  |       var stringToBytes = plainText.toBytes(); | ||||||
|  |       cipherText = await cipher.encrypt(stringToBytes); | ||||||
|  |       encryptionStatus.print('String successfully encrypted.\n'); | ||||||
|  |       encryptionStatus.append("Nonce: " + | ||||||
|  |           cipherText!.iv.toString() + | ||||||
|  |           "\nData: " + | ||||||
|  |           cipherText!.data.toString() + | ||||||
|  |           "\nTag: " + | ||||||
|  |           cipherText!.tag.toString()); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> _alter() async { | ||||||
|  |     if (cipherText == null) { | ||||||
|  |       decryptionStatus.print('Encrypt before altering CipherText!'); | ||||||
|  |     } else { | ||||||
|  |       // Add 1 to the first byte | ||||||
|  |       Uint8List _altered = cipherText!.data; | ||||||
|  |       _altered[0] += 1; | ||||||
|  |       // Recreate cipher text with altered data | ||||||
|  |       cipherText = CipherText(cipherText!.iv, _altered, cipherText!.tag); | ||||||
|  |       encryptionStatus.print('String successfully encrypted.\n'); | ||||||
|  |       encryptionStatus.append("Nonce: " + | ||||||
|  |           cipherText!.iv.toString() + | ||||||
|  |           "\nData: " + | ||||||
|  |           cipherText!.data.toString() + | ||||||
|  |           "\nTag: " + | ||||||
|  |           cipherText!.tag.toString()); | ||||||
|  |       decryptionStatus.print('CipherText altered!\nDecryption will fail.'); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void _decrypt(WidgetRef ref, Cipher cipher) async { | ||||||
|  |     Session state = ref.read(sessionProvider.state).state; | ||||||
|  | 
 | ||||||
|  |     if (state.secretKey.bytes.isEmpty) { | ||||||
|  |       decryptionStatus | ||||||
|  |           .print('No SecretKey!\nGo in Key tab and generate or derive one.'); | ||||||
|  |     } else if (cipherText == null) { | ||||||
|  |       decryptionStatus.print('Encrypt before decrypting!'); | ||||||
|  |     } else { | ||||||
|  |       try { | ||||||
|  |         Uint8List plainText = await cipher.decrypt(cipherText!); | ||||||
|  |         var bytesToString = plainText.toStr(); | ||||||
|  |         decryptionStatus | ||||||
|  |             .print('String successfully decrypted:\n\n$bytesToString'); | ||||||
|  |       } on DecryptionException catch (e) { | ||||||
|  |         decryptionStatus.print(e.message); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     Session state = ref.read(sessionProvider.state).state; | ||||||
|  |     if (state.secretKey.bytes.isEmpty) { | ||||||
|  |       keyContent | ||||||
|  |           .print('No SecretKey!\nGo in Key tab and generate or derive one.'); | ||||||
|  |       return SingleChildScrollView( | ||||||
|  |         child: Padding( | ||||||
|  |           padding: const EdgeInsets.all(8.0), | ||||||
|  |           child: Column( | ||||||
|  |             children: [ | ||||||
|  |               const Align( | ||||||
|  |                 child: Text("Secret Key"), | ||||||
|  |                 alignment: Alignment.centerLeft, | ||||||
|  |               ), | ||||||
|  |               keyContent, | ||||||
|  |             ], | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     keyContent.print(state.secretKey.bytes.toString()); | ||||||
|  | 
 | ||||||
|  |     AES cipher = AES(state.secretKey, AESMode.gcm); | ||||||
|  |     return SingleChildScrollView( | ||||||
|  |       child: Padding( | ||||||
|  |         padding: const EdgeInsets.all(8.0), | ||||||
|  |         child: Column( | ||||||
|  |           children: [ | ||||||
|  |             const Align( | ||||||
|  |               child: Text("Secret Key"), | ||||||
|  |               alignment: Alignment.centerLeft, | ||||||
|  |             ), | ||||||
|  |             keyContent, | ||||||
|  |             TextField( | ||||||
|  |               controller: _plainTextController, | ||||||
|  |               decoration: const InputDecoration( | ||||||
|  |                 hintText: 'Plain text', | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             Button( | ||||||
|  |               () => _encrypt(ref, cipher), | ||||||
|  |               "Encrypt", | ||||||
|  |             ), | ||||||
|  |             encryptionStatus, | ||||||
|  |             Row( | ||||||
|  |               mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||||
|  |               children: [ | ||||||
|  |                 Button( | ||||||
|  |                   _alter, | ||||||
|  |                   "Alter cipher", | ||||||
|  |                 ), | ||||||
|  |                 Button( | ||||||
|  |                   () => _decrypt(ref, cipher), | ||||||
|  |                   "Decrypt", | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             decryptionStatus, | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										130
									
								
								native_crypto/example/lib/pages/kdf_page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								native_crypto/example/lib/pages/kdf_page.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,130 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: kdf_page.dart | ||||||
|  | // Created Date: 28/12/2021 13:40:34 | ||||||
|  | // Last Modified: 28/12/2021 15:14:12 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
|  | import 'package:native_crypto/native_crypto.dart'; | ||||||
|  | import 'package:native_crypto_example/widgets/button.dart'; | ||||||
|  | 
 | ||||||
|  | import '../session.dart'; | ||||||
|  | import '../utils.dart'; | ||||||
|  | import '../widgets/output.dart'; | ||||||
|  | 
 | ||||||
|  | class KdfPage extends ConsumerWidget { | ||||||
|  |   KdfPage({Key? key}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   final Output keyContent = Output(); | ||||||
|  |   final Output keyStatus = Output(); | ||||||
|  |   final Output pbkdf2Status = Output(); | ||||||
|  |   final Output hashStatus = Output(large: true); | ||||||
|  | 
 | ||||||
|  |   final TextEditingController _pwdTextController = TextEditingController(); | ||||||
|  |   final TextEditingController _messageTextController = TextEditingController(); | ||||||
|  | 
 | ||||||
|  |   Future<void> _generate(WidgetRef ref) async { | ||||||
|  |     Session state = ref.read(sessionProvider.state).state; | ||||||
|  |     try { | ||||||
|  |       SecretKey sk = await SecretKey.fromSecureRandom(256); | ||||||
|  |       state.setKey(sk); | ||||||
|  |       keyStatus.print( | ||||||
|  |           "SecretKey successfully generated.\nLength: ${state.secretKey.bytes.length} bytes"); | ||||||
|  |       keyContent.print(state.secretKey.bytes.toString()); | ||||||
|  |       debugPrint("As hex :\n${sk.base16}"); | ||||||
|  |     } catch (e) { | ||||||
|  |       keyStatus.print(e.toString()); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> _pbkdf2(WidgetRef ref) async { | ||||||
|  |     Session state = ref.read(sessionProvider.state).state; | ||||||
|  |     final password = _pwdTextController.text.trim(); | ||||||
|  | 
 | ||||||
|  |     if (password.isEmpty) { | ||||||
|  |       pbkdf2Status.print('Password is empty'); | ||||||
|  |     } else { | ||||||
|  |       PBKDF2 _pbkdf2 = PBKDF2(32, 1000, algorithm: HashAlgorithm.sha512); | ||||||
|  |       SecretKey sk = await _pbkdf2.derive(password: password, salt: 'salt'); | ||||||
|  |       state.setKey(sk); | ||||||
|  |       pbkdf2Status.print('Key successfully derived.'); | ||||||
|  |       keyContent.print(state.secretKey.bytes.toString()); | ||||||
|  |       debugPrint("As hex :\n${sk.base16}"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> _hash(Hasher hasher) async { | ||||||
|  |     final message = _messageTextController.text.trim(); | ||||||
|  |     if (message.isEmpty) { | ||||||
|  |       hashStatus.print('Message is empty'); | ||||||
|  |     } else { | ||||||
|  |       Uint8List hash = await hasher.digest(message.toBytes()); | ||||||
|  |       hashStatus.print( | ||||||
|  |           'Message successfully hashed with ${hasher.algorithm} :${hash.toStr(to: Encoding.hex)}'); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context, WidgetRef ref) { | ||||||
|  |     return SingleChildScrollView( | ||||||
|  |       child: Padding( | ||||||
|  |         padding: const EdgeInsets.all(8.0), | ||||||
|  |         child: Column( | ||||||
|  |           children: [ | ||||||
|  |             const Align( | ||||||
|  |               child: Text("SecretKey"), | ||||||
|  |               alignment: Alignment.centerLeft, | ||||||
|  |             ), | ||||||
|  |             keyContent, | ||||||
|  |             Button( | ||||||
|  |               () => _generate(ref), | ||||||
|  |               "Generate SecretKey", | ||||||
|  |             ), | ||||||
|  |             keyStatus, | ||||||
|  |             TextField( | ||||||
|  |               controller: _pwdTextController, | ||||||
|  |               decoration: const InputDecoration( | ||||||
|  |                 hintText: 'Password', | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             Button( | ||||||
|  |               () => _pbkdf2(ref), | ||||||
|  |               "Apply PBKDF2", | ||||||
|  |             ), | ||||||
|  |             pbkdf2Status, | ||||||
|  |             TextField( | ||||||
|  |               controller: _messageTextController, | ||||||
|  |               decoration: const InputDecoration( | ||||||
|  |                 hintText: 'Message', | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             Row( | ||||||
|  |               mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||||
|  |               children: [ | ||||||
|  |                 Button( | ||||||
|  |                   () => _hash(SHA256()), | ||||||
|  |                   "SHA256", | ||||||
|  |                 ), | ||||||
|  |                 Button( | ||||||
|  |                   () => _hash(SHA384()), | ||||||
|  |                   "SHA384", | ||||||
|  |                 ), | ||||||
|  |                 Button( | ||||||
|  |                   () => _hash(SHA512()), | ||||||
|  |                   "SHA512", | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             hashStatus, | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								native_crypto/example/lib/session.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								native_crypto/example/lib/session.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: session.dart | ||||||
|  | // Created Date: 28/12/2021 13:54:29 | ||||||
|  | // Last Modified: 28/12/2021 13:58:49 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
|  | import 'package:native_crypto/native_crypto.dart'; | ||||||
|  | 
 | ||||||
|  | class Session { | ||||||
|  |   SecretKey secretKey; | ||||||
|  |   Session() : secretKey = SecretKey(Uint8List(0)); | ||||||
|  | 
 | ||||||
|  |   void setKey(SecretKey sk) { secretKey = sk; } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Providers | ||||||
|  | 
 | ||||||
|  | final sessionProvider = StateProvider<Session>((ref) => Session()); | ||||||
							
								
								
									
										52
									
								
								native_crypto/example/lib/utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								native_crypto/example/lib/utils.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: utils.dart | ||||||
|  | // Created Date: 16/12/2021 16:28:00 | ||||||
|  | // Last Modified: 28/12/2021 14:40:21 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
|  | enum Encoding { utf16, base64, hex } | ||||||
|  | 
 | ||||||
|  | extension StringX on String { | ||||||
|  |   Uint8List toBytes({final from = Encoding.utf16}) { | ||||||
|  |     Uint8List bytes = Uint8List(0); | ||||||
|  |     switch (from) { | ||||||
|  |       case Encoding.utf16: | ||||||
|  |         bytes = Uint8List.fromList(runes.toList()); | ||||||
|  |         break; | ||||||
|  |       case Encoding.base64: | ||||||
|  |         bytes = base64.decode(this); | ||||||
|  |         break; | ||||||
|  |       case Encoding.hex: | ||||||
|  |       bytes = Uint8List.fromList( | ||||||
|  |         List.generate( | ||||||
|  |           length ~/ 2, | ||||||
|  |           (i) => int.parse(substring(i * 2, (i * 2) + 2), radix: 16), | ||||||
|  |         ).toList(), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return bytes; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extension Uint8ListX on Uint8List { | ||||||
|  |   String toStr({final to = Encoding.utf16}) { | ||||||
|  |     String str = ""; | ||||||
|  |     switch (to) { | ||||||
|  |       case Encoding.utf16: | ||||||
|  |         str = String.fromCharCodes(this); | ||||||
|  |         break; | ||||||
|  |       case Encoding.base64: | ||||||
|  |         str = base64.encode(this); | ||||||
|  |         break; | ||||||
|  |       case Encoding.hex: | ||||||
|  |         str = map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(); | ||||||
|  |     } | ||||||
|  |     return str; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								native_crypto/example/lib/widgets/button.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								native_crypto/example/lib/widgets/button.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: button.dart | ||||||
|  | // Created Date: 28/12/2021 13:31:17 | ||||||
|  | // Last Modified: 28/12/2021 13:31:34 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | class Button extends StatelessWidget { | ||||||
|  |   final void Function() onPressed; | ||||||
|  |   final String label; | ||||||
|  | 
 | ||||||
|  |   const Button(this.onPressed, this.label, {Key? key}) : super(key: key); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return ElevatedButton( | ||||||
|  |       onPressed: onPressed, | ||||||
|  |       style: TextButton.styleFrom( | ||||||
|  |         primary: Colors.blue, | ||||||
|  |       ), | ||||||
|  |       child: Text( | ||||||
|  |         label, | ||||||
|  |         style: const TextStyle(color: Colors.white), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										61
									
								
								native_crypto/example/lib/widgets/output.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								native_crypto/example/lib/widgets/output.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: output.dart | ||||||
|  | // Created Date: 28/12/2021 13:31:39 | ||||||
|  | // Last Modified: 28/12/2021 14:12:11 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | // ignore: must_be_immutable | ||||||
|  | class Output extends StatelessWidget { | ||||||
|  |   late TextEditingController controller; | ||||||
|  |   final bool large; | ||||||
|  |   final bool editable; | ||||||
|  | 
 | ||||||
|  |   Output({ | ||||||
|  |     Key? key, | ||||||
|  |     TextEditingController? controller, | ||||||
|  |     this.large = false, | ||||||
|  |     this.editable = false, | ||||||
|  |   }) : super(key: key) { | ||||||
|  |     this.controller = controller ?? TextEditingController(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void print(String message) { | ||||||
|  |     debugPrint(message); | ||||||
|  |     controller.text = message; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void append(String message) { | ||||||
|  |     debugPrint(message); | ||||||
|  |     controller.text += message; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void appendln(String message) { | ||||||
|  |     debugPrint(message); | ||||||
|  |     controller.text += message + "\n"; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void clear() { | ||||||
|  |     controller.clear(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String read() { | ||||||
|  |     return controller.text; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return TextField( | ||||||
|  |       enableInteractiveSelection: true, | ||||||
|  |       readOnly: editable ? false : true, | ||||||
|  |       minLines: large ? 3 : 1, | ||||||
|  |       maxLines: large ? 500 : 5, | ||||||
|  |       decoration: const InputDecoration(border: OutlineInputBorder()), | ||||||
|  |       controller: controller, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -29,6 +29,7 @@ dependencies: | |||||||
|   # The following adds the Cupertino Icons font to your application. |   # The following adds the Cupertino Icons font to your application. | ||||||
|   # Use with the CupertinoIcons class for iOS style icons. |   # Use with the CupertinoIcons class for iOS style icons. | ||||||
|   cupertino_icons: ^1.0.2 |   cupertino_icons: ^1.0.2 | ||||||
|  |   flutter_riverpod: ^1.0.3 | ||||||
| 
 | 
 | ||||||
| dev_dependencies: | dev_dependencies: | ||||||
|   flutter_test: |   flutter_test: | ||||||
| @ -39,7 +40,7 @@ dev_dependencies: | |||||||
|   # activated in the `analysis_options.yaml` file located at the root of your |   # activated in the `analysis_options.yaml` file located at the root of your | ||||||
|   # package. See that file for information about deactivating specific lint |   # package. See that file for information about deactivating specific lint | ||||||
|   # rules and activating additional ones. |   # rules and activating additional ones. | ||||||
|   flutter_lints: ^1.0.0 |   flutter_lints: ^1.0.4 | ||||||
| 
 | 
 | ||||||
| # For information on the generic Dart part of this file, see the | # For information on the generic Dart part of this file, see the | ||||||
| # following page: https://dart.dev/tools/pub/pubspec | # following page: https://dart.dev/tools/pub/pubspec | ||||||
|  | |||||||
| @ -1,17 +1,27 @@ | |||||||
| // You have generated a new plugin project without | // Author: Hugo Pointcheval | ||||||
| // specifying the `--platforms` flag. A plugin project supports no platforms is generated. | // Email: git@pcl.ovh | ||||||
| // To add platforms, run `flutter create -t plugin --platforms <platforms> .` under the same | // ----- | ||||||
| // directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. | // File: native_crypto.dart | ||||||
|  | // Created Date: 16/12/2021 16:28:00 | ||||||
|  | // Last Modified: 28/12/2021 15:06:48 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| import 'dart:async'; | export 'src/byte_array.dart'; | ||||||
|  | export 'src/cipher.dart'; | ||||||
|  | export 'src/cipher_text.dart'; | ||||||
|  | export 'src/ciphers/aes.dart'; | ||||||
|  | export 'src/exceptions.dart'; | ||||||
|  | export 'src/hasher.dart'; | ||||||
|  | export 'src/hashers/sha256.dart'; | ||||||
|  | export 'src/hashers/sha384.dart'; | ||||||
|  | export 'src/hashers/sha512.dart'; | ||||||
|  | export 'src/kdf/pbkdf2.dart'; | ||||||
|  | export 'src/keyderivation.dart'; | ||||||
|  | export 'src/keys/secret_key.dart'; | ||||||
|  | export 'src/utils.dart'; | ||||||
| 
 | 
 | ||||||
| import 'package:flutter/services.dart'; | const String version = "0.1.0"; | ||||||
| 
 | const String author = "Hugo Pointcheval"; | ||||||
| class NativeCrypto { | const String website = "https://hugo.pointcheval.fr/"; | ||||||
|   static const MethodChannel _channel = MethodChannel('native_crypto'); | const List<String> repositories = ["https://github.com/hugo-pcl/native-crypto-flutter", "https://git.pointcheval.fr/NativeCrypto/native-crypto-flutter"]; | ||||||
| 
 |  | ||||||
|   static Future<String?> get platformVersion async { |  | ||||||
|     final String? version = await _channel.invokeMethod('getPlatformVersion'); |  | ||||||
|     return version; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								native_crypto/lib/src/builder.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								native_crypto/lib/src/builder.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: builder.dart | ||||||
|  | // Created Date: 28/12/2021 12:02:34 | ||||||
|  | // Last Modified: 28/12/2021 12:32:12 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | abstract class Builder<T> { | ||||||
|  |   Future<T> build(); | ||||||
|  | } | ||||||
							
								
								
									
										46
									
								
								native_crypto/lib/src/builders/aes_builder.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								native_crypto/lib/src/builders/aes_builder.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: aes_builder.dart | ||||||
|  | // Created Date: 28/12/2021 12:03:11 | ||||||
|  | // Last Modified: 28/12/2021 13:39:23 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import '../builder.dart'; | ||||||
|  | import '../ciphers/aes.dart'; | ||||||
|  | import '../exceptions.dart'; | ||||||
|  | import '../keys/secret_key.dart'; | ||||||
|  | 
 | ||||||
|  | class AESBuilder implements Builder<AES> { | ||||||
|  |   SecretKey? _sk; | ||||||
|  |   Future<SecretKey>? _fsk; | ||||||
|  |   AESMode _mode = AESMode.gcm; | ||||||
|  | 
 | ||||||
|  |   AESBuilder withGeneratedKey(int bitsCount) { | ||||||
|  |     _fsk = SecretKey.fromSecureRandom(bitsCount); | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   AESBuilder withKey(SecretKey secretKey) { | ||||||
|  |     _sk = secretKey; | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   AESBuilder using(AESMode mode) { | ||||||
|  |     _mode = mode; | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<AES> build() async { | ||||||
|  |     if (_sk == null) { | ||||||
|  |       if (_fsk == null) { | ||||||
|  |         throw CipherInitException("You must specify or generate a secret key."); | ||||||
|  |       } else { | ||||||
|  |         _sk = await _fsk; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return AES(_sk!, _mode); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										72
									
								
								native_crypto/lib/src/byte_array.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								native_crypto/lib/src/byte_array.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: byte_array.dart | ||||||
|  | // Created Date: 16/12/2021 17:54:16 | ||||||
|  | // Last Modified: 27/12/2021 21:51:36 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'utils.dart'; | ||||||
|  | import 'dart:convert' as convert; | ||||||
|  | 
 | ||||||
|  | class ByteArray { | ||||||
|  |   Uint8List _bytes; | ||||||
|  | 
 | ||||||
|  |   ByteArray(this._bytes); | ||||||
|  | 
 | ||||||
|  |   /// Creates an ByteArray object from a hexdecimal string. | ||||||
|  |   ByteArray.fromBase16(String encoded) : _bytes = Utils.decodeHexString(encoded); | ||||||
|  | 
 | ||||||
|  |   /// Creates an ByteArray object from a Base64 string. | ||||||
|  |   ByteArray.fromBase64(String encoded) | ||||||
|  |       : _bytes = convert.base64.decode(encoded); | ||||||
|  | 
 | ||||||
|  |   /// Creates an ByteArray object from a UTF-8 string. | ||||||
|  |   ByteArray.fromUtf8(String input) | ||||||
|  |       : _bytes = Uint8List.fromList(convert.utf8.encode(input)); | ||||||
|  | 
 | ||||||
|  |   /// Creates an ByteArray object from a length. | ||||||
|  |   ByteArray.fromLength(int length) : _bytes = Uint8List(length); | ||||||
|  | 
 | ||||||
|  |   /// Gets the ByteArray bytes. | ||||||
|  |   // ignore: unnecessary_getters_setters | ||||||
|  |   Uint8List get bytes => _bytes; | ||||||
|  | 
 | ||||||
|  |   /// Sets the ByteArray bytes. | ||||||
|  |   set bytes(Uint8List value) => _bytes = value; | ||||||
|  | 
 | ||||||
|  |   /// Gets the ByteArray bytes as a Hexadecimal representation. | ||||||
|  |   String get base16 => | ||||||
|  |       _bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(); | ||||||
|  | 
 | ||||||
|  |   /// Gets the ByteArray bytes as a Base64 representation. | ||||||
|  |   String get base64 => convert.base64.encode(_bytes); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool operator ==(other) { | ||||||
|  |     if (other is ByteArray) { | ||||||
|  |       for (int i = 0; i < _bytes.length; i++) { | ||||||
|  |         if (_bytes[i] != other._bytes[i]) { | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |        | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   int get hashCode { | ||||||
|  |     int hash = 0; | ||||||
|  |     for (int i = 0; i < _bytes.length; i++) { | ||||||
|  |       hash = _bytes[i] + (hash << 6) + (hash << 16) - hash; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return hash; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								native_crypto/lib/src/cipher.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								native_crypto/lib/src/cipher.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: cipher.dart | ||||||
|  | // Created Date: 16/12/2021 16:28:00 | ||||||
|  | // Last Modified: 28/12/2021 12:25:38 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'cipher_text.dart'; | ||||||
|  | 
 | ||||||
|  | /// Represents different cipher algorithms | ||||||
|  | enum CipherAlgorithm { aes, rsa } | ||||||
|  | 
 | ||||||
|  | /// Represents a cipher. | ||||||
|  | /// | ||||||
|  | /// In cryptography, a cipher is an algorithm for performing encryption | ||||||
|  | /// or decryption - a series of well-defined steps that can | ||||||
|  | /// be followed as a procedure. | ||||||
|  | abstract class Cipher { | ||||||
|  |   /// Returns the standard algorithm name for this cipher | ||||||
|  |   CipherAlgorithm get algorithm; | ||||||
|  | 
 | ||||||
|  |   /// Encrypts data. | ||||||
|  |   /// | ||||||
|  |   /// Takes [Uint8List] data as parameter. | ||||||
|  |   /// Returns a [CipherText]. | ||||||
|  |   Future<CipherText> encrypt(Uint8List data); | ||||||
|  | 
 | ||||||
|  |   /// Decrypts cipher text. | ||||||
|  |   /// | ||||||
|  |   /// Takes [CipherText] as parameter. | ||||||
|  |   /// And returns plain text data as [Uint8List]. | ||||||
|  |   Future<Uint8List> decrypt(CipherText cipherText); | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								native_crypto/lib/src/cipher_text.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								native_crypto/lib/src/cipher_text.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: cipher_text.dart | ||||||
|  | // Created Date: 16/12/2021 16:59:53 | ||||||
|  | // Last Modified: 27/12/2021 22:32:06 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'byte_array.dart'; | ||||||
|  | 
 | ||||||
|  | class CipherText extends ByteArray { | ||||||
|  |   final int _ivLength; | ||||||
|  |   final int _dataLength; | ||||||
|  |   final int _tagLength; | ||||||
|  |    | ||||||
|  |   CipherText(Uint8List iv, Uint8List data, Uint8List tag) : _ivLength = iv.length, _dataLength = data.length, _tagLength = tag.length, super(Uint8List.fromList(iv + data + tag)); | ||||||
|  |    | ||||||
|  |   /// Gets the CipherText IV. | ||||||
|  |   Uint8List get iv => bytes.sublist(0, _ivLength); | ||||||
|  | 
 | ||||||
|  |   /// Gets the CipherText data. | ||||||
|  |   Uint8List get data => bytes.sublist(_ivLength, _ivLength + _dataLength); | ||||||
|  | 
 | ||||||
|  |   /// Gets the CipherText tag. | ||||||
|  |   Uint8List get tag => bytes.sublist(_ivLength + _dataLength, _ivLength + _dataLength + _tagLength); | ||||||
|  | 
 | ||||||
|  |   /// Gets the CipherText IV length. | ||||||
|  |   int get ivLength => _ivLength; | ||||||
|  | 
 | ||||||
|  |   /// Gets the CipherText data length. | ||||||
|  |   int get dataLength => _dataLength; | ||||||
|  | 
 | ||||||
|  |   /// Gets the CipherText tag length. | ||||||
|  |   int get tagLength => _tagLength; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class CipherTextList extends CipherText { | ||||||
|  |   static const int chunkSize = 33554432; | ||||||
|  |   final List<CipherText> _list; | ||||||
|  | 
 | ||||||
|  |   CipherTextList() : _list = [], super(Uint8List(0), Uint8List(0), Uint8List(0)); | ||||||
|  | 
 | ||||||
|  |   void add(CipherText cipherText) { | ||||||
|  |     _list.add(cipherText); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get list => _list; | ||||||
|  | } | ||||||
							
								
								
									
										112
									
								
								native_crypto/lib/src/ciphers/aes.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								native_crypto/lib/src/ciphers/aes.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: aes.dart | ||||||
|  | // Created Date: 16/12/2021 16:28:00 | ||||||
|  | // Last Modified: 28/12/2021 13:39:00 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import '../cipher.dart'; | ||||||
|  | import '../cipher_text.dart'; | ||||||
|  | import '../exceptions.dart'; | ||||||
|  | import '../keys/secret_key.dart'; | ||||||
|  | import '../platform.dart'; | ||||||
|  | import '../utils.dart'; | ||||||
|  | 
 | ||||||
|  | /// Defines the AES modes of operation. | ||||||
|  | enum AESMode { gcm } | ||||||
|  | 
 | ||||||
|  | /// Defines all available key sizes. | ||||||
|  | enum AESKeySize { bits128, bits192, bits256 } | ||||||
|  | 
 | ||||||
|  | /// Represents different paddings. | ||||||
|  | enum AESPadding { none } | ||||||
|  | 
 | ||||||
|  | extension AESKeySizeExtension on AESKeySize { | ||||||
|  |   static final Map<AESKeySize, int> sizes = <AESKeySize, int>{ | ||||||
|  |     AESKeySize.bits128: 128, | ||||||
|  |     AESKeySize.bits192: 192, | ||||||
|  |     AESKeySize.bits256: 256, | ||||||
|  |   }; | ||||||
|  |   static final List<int> supportedSizes = sizes.values.toList(growable: false); | ||||||
|  |   int get length { | ||||||
|  |     return sizes[this]!; // this is safe because `this` is listed in the enum | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class AES implements Cipher { | ||||||
|  |   final SecretKey key; | ||||||
|  |   final AESMode mode; | ||||||
|  |   final AESPadding padding; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   CipherAlgorithm get algorithm => CipherAlgorithm.aes; | ||||||
|  | 
 | ||||||
|  |   AES(this.key, this.mode, {this.padding = AESPadding.none}) { | ||||||
|  |     if (!AESKeySizeExtension.supportedSizes.contains(key.bytes.length * 8)) { | ||||||
|  |       throw CipherInitException("Invalid key length!"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Map<AESMode, List<AESPadding>> _supported = { | ||||||
|  |       AESMode.gcm: [AESPadding.none], | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     if (!_supported[mode]!.contains(padding)) { | ||||||
|  |       throw CipherInitException("Invalid padding!"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Uint8List> decrypt(CipherText cipherText) async { | ||||||
|  |     BytesBuilder decryptedData = BytesBuilder(copy: false); | ||||||
|  |     if (cipherText is CipherTextList) { | ||||||
|  |       for (CipherText ct in cipherText.list) { | ||||||
|  |         Uint8List d = await platform.decrypt( | ||||||
|  |                 ct.bytes, key.bytes, Utils.enumToStr(algorithm)) ?? | ||||||
|  |             Uint8List(0); | ||||||
|  |         decryptedData.add(d); | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       Uint8List d = await platform.decrypt( | ||||||
|  |               cipherText.bytes, key.bytes, Utils.enumToStr(algorithm)) ?? | ||||||
|  |           Uint8List(0); | ||||||
|  |       decryptedData.add(d); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return decryptedData.toBytes(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<CipherText> encrypt(Uint8List data) async { | ||||||
|  |     Uint8List dataToEncrypt; | ||||||
|  |     CipherTextList cipherTextList = CipherTextList(); | ||||||
|  |     // If data is bigger than 32mB -> split in chunks | ||||||
|  |     if (data.length > CipherTextList.chunkSize) { | ||||||
|  |       int chunkNb = (data.length / CipherTextList.chunkSize).ceil(); | ||||||
|  |       for (var i = 0; i < chunkNb; i++) { | ||||||
|  |         dataToEncrypt = i < (chunkNb - 1) | ||||||
|  |             ? data.sublist(i * CipherTextList.chunkSize, (i + 1) * CipherTextList.chunkSize) | ||||||
|  |             : data.sublist(i * CipherTextList.chunkSize); | ||||||
|  |         Uint8List c = await platform.encrypt( | ||||||
|  |           dataToEncrypt, | ||||||
|  |           key.bytes, | ||||||
|  |           Utils.enumToStr(algorithm) | ||||||
|  |         ) ?? Uint8List(0); | ||||||
|  |         cipherTextList.add(CipherText(c.sublist(0, 12), c.sublist(12, c.length - 16), c.sublist(c.length - 16, c.length))); // TODO: generify this | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       Uint8List c = await platform.encrypt( | ||||||
|  |         data, | ||||||
|  |         key.bytes, | ||||||
|  |         Utils.enumToStr(algorithm) | ||||||
|  |       ) ?? Uint8List(0); | ||||||
|  | 
 | ||||||
|  |       return CipherText(c.sublist(0, 12), c.sublist(12, c.length - 16), c.sublist(c.length - 16, c.length)); // TODO: generify this | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return cipherTextList; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								native_crypto/lib/src/exceptions.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								native_crypto/lib/src/exceptions.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: exceptions.dart | ||||||
|  | // Created Date: 16/12/2021 16:28:00 | ||||||
|  | // Last Modified: 27/12/2021 23:28:31 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | class NativeCryptoException implements Exception { | ||||||
|  |   String message; | ||||||
|  |   NativeCryptoException(this.message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class UtilsException extends NativeCryptoException { | ||||||
|  |   UtilsException(message) : super(message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class KeyException extends NativeCryptoException { | ||||||
|  |   KeyException(message) : super(message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class KeyDerivationException extends NativeCryptoException { | ||||||
|  |   KeyDerivationException(message) : super(message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class CipherInitException extends NativeCryptoException { | ||||||
|  |   CipherInitException(message) : super(message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class EncryptionException extends NativeCryptoException { | ||||||
|  |   EncryptionException(message) : super(message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class DecryptionException extends NativeCryptoException { | ||||||
|  |   DecryptionException(message) : super(message); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class NotImplementedException extends NativeCryptoException { | ||||||
|  |   NotImplementedException(message) : super(message); | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								native_crypto/lib/src/hasher.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								native_crypto/lib/src/hasher.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: hasher.dart | ||||||
|  | // Created Date: 16/12/2021 16:28:00 | ||||||
|  | // Last Modified: 27/12/2021 22:06:29 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'platform.dart'; | ||||||
|  | import 'utils.dart'; | ||||||
|  | 
 | ||||||
|  | enum HashAlgorithm { sha256, sha384, sha512 } | ||||||
|  | 
 | ||||||
|  | abstract class Hasher { | ||||||
|  |   /// Returns the standard algorithm name for this digest | ||||||
|  |   HashAlgorithm get algorithm; | ||||||
|  | 
 | ||||||
|  |   /// Hashes a message | ||||||
|  |   Future<Uint8List> digest(Uint8List data) async { | ||||||
|  |     Uint8List hash = (await platform.digest(data, Utils.enumToStr(algorithm))) ?? Uint8List(0); | ||||||
|  |      | ||||||
|  |     return hash; | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								native_crypto/lib/src/hashers/sha256.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								native_crypto/lib/src/hashers/sha256.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: sha256.dart | ||||||
|  | // Created Date: 17/12/2021 11:31:20 | ||||||
|  | // Last Modified: 18/12/2021 12:09:33 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import '../hasher.dart'; | ||||||
|  | 
 | ||||||
|  | class SHA256 extends Hasher{ | ||||||
|  |   @override | ||||||
|  |   HashAlgorithm get algorithm => HashAlgorithm.sha256; | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								native_crypto/lib/src/hashers/sha384.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								native_crypto/lib/src/hashers/sha384.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: sha384.dart | ||||||
|  | // Created Date: 17/12/2021 11:31:53 | ||||||
|  | // Last Modified: 18/12/2021 12:09:45 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import '../hasher.dart'; | ||||||
|  | 
 | ||||||
|  | class SHA384 extends Hasher{ | ||||||
|  |   @override | ||||||
|  |   HashAlgorithm get algorithm => HashAlgorithm.sha384; | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								native_crypto/lib/src/hashers/sha512.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								native_crypto/lib/src/hashers/sha512.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: sha512.dart | ||||||
|  | // Created Date: 17/12/2021 11:32:14 | ||||||
|  | // Last Modified: 18/12/2021 12:09:58 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import '../hasher.dart'; | ||||||
|  | 
 | ||||||
|  | class SHA512 extends Hasher{ | ||||||
|  |   @override | ||||||
|  |   HashAlgorithm get algorithm => HashAlgorithm.sha512; | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								native_crypto/lib/src/kdf/pbkdf2.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								native_crypto/lib/src/kdf/pbkdf2.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: pbkdf2.dart | ||||||
|  | // Created Date: 17/12/2021 14:50:42 | ||||||
|  | // Last Modified: 28/12/2021 13:38:50 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import '../exceptions.dart'; | ||||||
|  | import '../hasher.dart'; | ||||||
|  | import '../keyderivation.dart'; | ||||||
|  | import '../keys/secret_key.dart'; | ||||||
|  | import '../platform.dart'; | ||||||
|  | import '../utils.dart'; | ||||||
|  | 
 | ||||||
|  | class PBKDF2 extends KeyDerivation { | ||||||
|  |   final int _keyBytesCount; | ||||||
|  |   final int _iterations; | ||||||
|  |   final HashAlgorithm _hash; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   KdfAlgorithm get algorithm => KdfAlgorithm.pbkdf2; | ||||||
|  | 
 | ||||||
|  |   PBKDF2( | ||||||
|  |     int keyBytesCount, | ||||||
|  |     int iterations, { | ||||||
|  |     HashAlgorithm algorithm = HashAlgorithm.sha256, | ||||||
|  |   })  : _keyBytesCount = keyBytesCount, | ||||||
|  |         _iterations = iterations, | ||||||
|  |         _hash = algorithm; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<SecretKey> derive({String? password, String? salt}) async { | ||||||
|  |     if (password == null || salt == null) { | ||||||
|  |       throw KeyDerivationException("Password or Salt can't be null!"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Uint8List derivation = (await platform.pbkdf2( | ||||||
|  |       password, | ||||||
|  |       salt, | ||||||
|  |       _keyBytesCount, | ||||||
|  |       _iterations, | ||||||
|  |       Utils.enumToStr(_hash), | ||||||
|  |     )) ?? Uint8List(0); | ||||||
|  | 
 | ||||||
|  |     return SecretKey(derivation); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								native_crypto/lib/src/key.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								native_crypto/lib/src/key.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: key.dart | ||||||
|  | // Created Date: 16/12/2021 16:28:00 | ||||||
|  | // Last Modified: 28/12/2021 13:37:50 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'byte_array.dart'; | ||||||
|  | 
 | ||||||
|  | /// A class representing a key. | ||||||
|  | class Key extends ByteArray { | ||||||
|  |   Key(Uint8List bytes) : super(bytes); | ||||||
|  |   Key.fromBase16(String encoded) : super.fromBase16(encoded); | ||||||
|  |   Key.fromBase64(String encoded) : super.fromBase64(encoded); | ||||||
|  |   Key.fromUtf8(String input) : super.fromUtf8(input); | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								native_crypto/lib/src/keyderivation.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								native_crypto/lib/src/keyderivation.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: kdf.dart | ||||||
|  | // Created Date: 18/12/2021 11:56:43 | ||||||
|  | // Last Modified: 28/12/2021 13:38:02 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import './keys/secret_key.dart'; | ||||||
|  | 
 | ||||||
|  | enum KdfAlgorithm { pbkdf2 } | ||||||
|  | 
 | ||||||
|  | /// Represents a Key Derivation Function | ||||||
|  | abstract class KeyDerivation { | ||||||
|  |   /// Returns the standard algorithm name for this key derivation function | ||||||
|  |   KdfAlgorithm get algorithm; | ||||||
|  | 
 | ||||||
|  |   /// Derive key | ||||||
|  |   Future<SecretKey> derive(); | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								native_crypto/lib/src/keys/secret_key.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								native_crypto/lib/src/keys/secret_key.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: secret_key.dart | ||||||
|  | // Created Date: 28/12/2021 13:36:54 | ||||||
|  | // Last Modified: 28/12/2021 13:37:45 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/services.dart'; | ||||||
|  | 
 | ||||||
|  | import '../exceptions.dart'; | ||||||
|  | import '../key.dart'; | ||||||
|  | import '../platform.dart'; | ||||||
|  | 
 | ||||||
|  | /// A class representing a secret key. | ||||||
|  | /// A secret key is a key that is not accessible by anyone else. | ||||||
|  | /// It is used to encrypt and decrypt data. | ||||||
|  | class SecretKey extends Key { | ||||||
|  |   SecretKey(Uint8List bytes) : super(bytes); | ||||||
|  |   SecretKey.fromBase16(String encoded) : super.fromBase16(encoded); | ||||||
|  |   SecretKey.fromBase64(String encoded) : super.fromBase64(encoded); | ||||||
|  |   SecretKey.fromUtf8(String input) : super.fromUtf8(input); | ||||||
|  | 
 | ||||||
|  |   static Future<SecretKey> fromSecureRandom(int bitsCount) async { | ||||||
|  |     try { | ||||||
|  |       Uint8List _key = (await platform.generateSecretKey(bitsCount)) ?? Uint8List(0); | ||||||
|  |        | ||||||
|  |       return SecretKey(_key); | ||||||
|  |     } on PlatformException catch (e) { | ||||||
|  |       throw KeyException(e); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								native_crypto/lib/src/platform.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								native_crypto/lib/src/platform.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: platform.dart | ||||||
|  | // Created Date: 27/12/2021 22:03:58 | ||||||
|  | // Last Modified: 27/12/2021 22:04:30 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; | ||||||
|  | 
 | ||||||
|  | NativeCryptoPlatform platform = NativeCryptoPlatform.instance; | ||||||
							
								
								
									
										83
									
								
								native_crypto/lib/src/utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								native_crypto/lib/src/utils.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: utils.dart | ||||||
|  | // Created Date: 16/12/2021 16:28:00 | ||||||
|  | // Last Modified: 27/12/2021 22:04:07 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'cipher.dart'; | ||||||
|  | import 'exceptions.dart'; | ||||||
|  | import 'hasher.dart'; | ||||||
|  | import 'keyderivation.dart'; | ||||||
|  | 
 | ||||||
|  | class Utils { | ||||||
|  |   /// Returns enum value to string, without the enum name | ||||||
|  |   static String enumToStr(dynamic enumValue) { | ||||||
|  |     return enumValue.toString().split('.').last; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns enum list as string list | ||||||
|  |   static List<String> enumToList<T>(List<T> enumValues) { | ||||||
|  |     List<String> _res = []; | ||||||
|  |     for (T enumValue in enumValues) { | ||||||
|  |       _res.add(enumToStr(enumValue)); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return _res; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns enum from string | ||||||
|  |   static T strToEnum<T>(String str, List<T> enumValues) { | ||||||
|  |     for (T enumValue in enumValues) { | ||||||
|  |       if (enumToStr(enumValue) == str) { | ||||||
|  |         return enumValue; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     throw UtilsException('Invalid enum value: $str'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns [HashAlgorithm] from his name. | ||||||
|  |   static HashAlgorithm getHashAlgorithm(String algorithm) { | ||||||
|  |     return strToEnum<HashAlgorithm>(algorithm.toLowerCase(), HashAlgorithm.values); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns all available [HashAlgorithm] as String list | ||||||
|  |   static List<String> getAvailableHashAlgorithms() { | ||||||
|  |     return enumToList<HashAlgorithm>(HashAlgorithm.values); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns [KdfAlgorithm] from his name. | ||||||
|  |   static KdfAlgorithm getKdfAlgorithm(String algorithm) { | ||||||
|  |     return strToEnum<KdfAlgorithm>(algorithm.toLowerCase(), KdfAlgorithm.values); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns all available [KdfAlgorithm] as String list | ||||||
|  |   static List<String> getAvailableKdfAlgorithms() { | ||||||
|  |     return enumToList<KdfAlgorithm>(KdfAlgorithm.values); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns [CipherAlgorithm] from his name. | ||||||
|  |   static CipherAlgorithm getCipherAlgorithm(String algorithm) { | ||||||
|  |     return strToEnum<CipherAlgorithm>(algorithm.toLowerCase(), CipherAlgorithm.values); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns all available [CipherAlgorithm] as String list | ||||||
|  |   static List<String> getAvailableCipherAlgorithms() { | ||||||
|  |     return enumToList<CipherAlgorithm>(CipherAlgorithm.values); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |  static  Uint8List decodeHexString(String input) { | ||||||
|  |     assert(input.length % 2 == 0, 'Input needs to be an even length.'); | ||||||
|  | 
 | ||||||
|  |     return Uint8List.fromList( | ||||||
|  |       List.generate( | ||||||
|  |         input.length ~/ 2, | ||||||
|  |         (i) => int.parse(input.substring(i * 2, (i * 2) + 2), radix: 16), | ||||||
|  |       ).toList(), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,6 +1,6 @@ | |||||||
| name: native_crypto | name: native_crypto | ||||||
| description: Fast and secure cryptography for Flutter. | description: Fast and secure cryptography for Flutter. | ||||||
| version: 0.0.7 | version: 0.1.0 | ||||||
| publish_to: 'none' | publish_to: 'none' | ||||||
| 
 | 
 | ||||||
| environment: | environment: | ||||||
| @ -11,9 +11,15 @@ dependencies: | |||||||
|   flutter: |   flutter: | ||||||
|     sdk: flutter |     sdk: flutter | ||||||
|    |    | ||||||
|  |   native_crypto_android: | ||||||
|  |     path: ../native_crypto_android | ||||||
|  | 
 | ||||||
|   native_crypto_ios: |   native_crypto_ios: | ||||||
|     path: ../native_crypto_ios |     path: ../native_crypto_ios | ||||||
| 
 | 
 | ||||||
|  |   native_crypto_platform_interface: | ||||||
|  |     path: ../native_crypto_platform_interface | ||||||
|  | 
 | ||||||
| dev_dependencies: | dev_dependencies: | ||||||
|   flutter_test: |   flutter_test: | ||||||
|     sdk: flutter |     sdk: flutter | ||||||
| @ -22,7 +28,7 @@ dev_dependencies: | |||||||
| flutter: | flutter: | ||||||
|   plugin: |   plugin: | ||||||
|     platforms: |     platforms: | ||||||
|       # android: |       android: | ||||||
|       #   default_package: native_crypto_android |         default_package: native_crypto_android | ||||||
|       ios: |       ios: | ||||||
|         default_package: native_crypto_ios |         default_package: native_crypto_ios | ||||||
| @ -1,23 +1 @@ | |||||||
| import 'package:flutter/services.dart'; | // TODO | ||||||
| import 'package:flutter_test/flutter_test.dart'; |  | ||||||
| import 'package:native_crypto/native_crypto.dart'; |  | ||||||
| 
 |  | ||||||
| void main() { |  | ||||||
|   const MethodChannel channel = MethodChannel('native_crypto'); |  | ||||||
| 
 |  | ||||||
|   TestWidgetsFlutterBinding.ensureInitialized(); |  | ||||||
| 
 |  | ||||||
|   setUp(() { |  | ||||||
|     channel.setMockMethodCallHandler((MethodCall methodCall) async { |  | ||||||
|       return '42'; |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   tearDown(() { |  | ||||||
|     channel.setMockMethodCallHandler(null); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   test('getPlatformVersion', () async { |  | ||||||
|     expect(await NativeCrypto.platformVersion, '42'); |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user