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: 25/05/2022 17:16:12 | // Last Modified: 26/05/2022 20:19:28 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2021 | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| @ -40,7 +40,7 @@ class BenchmarkPage extends ConsumerWidget { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     List<int> testedSizes = [2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50]; |     List<int> testedSizes = [2, 4, 8, 16, 32, 64, 128, 256]; | ||||||
|     int multiplier = pow(2, 20).toInt(); // MiB |     int multiplier = pow(2, 20).toInt(); // MiB | ||||||
| 
 | 
 | ||||||
|     benchmarkStatus.print("[Benchmark] Sizes: ${testedSizes.join('/')}MiB\n"); |     benchmarkStatus.print("[Benchmark] Sizes: ${testedSizes.join('/')}MiB\n"); | ||||||
| @ -86,7 +86,7 @@ class BenchmarkPage extends ConsumerWidget { | |||||||
|         if (usePc) { |         if (usePc) { | ||||||
|           pc.decrypt(encryptedBigFile as Uint8List, state.secretKey.bytes); |           pc.decrypt(encryptedBigFile as Uint8List, state.secretKey.bytes); | ||||||
|         } else { |         } else { | ||||||
|           await cipher.decrypt(encryptedBigFile as CipherText); |           await cipher.decrypt(encryptedBigFile as CipherTextWrapper); | ||||||
|         } |         } | ||||||
|         after = DateTime.now(); |         after = DateTime.now(); | ||||||
|         benchmark = |         benchmark = | ||||||
| @ -105,7 +105,7 @@ class BenchmarkPage extends ConsumerWidget { | |||||||
|         .appendln('[Benchmark] Finished: ${sum}MiB in $benchmark ms'); |         .appendln('[Benchmark] Finished: ${sum}MiB in $benchmark ms'); | ||||||
|     benchmarkStatus.appendln('[Benchmark] Check the console for csv data'); |     benchmarkStatus.appendln('[Benchmark] Check the console for csv data'); | ||||||
|     benchmarkStatus.appendln(csv); |     benchmarkStatus.appendln(csv); | ||||||
|     print(csv); |     debugPrint(csv); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void _clear() { |   void _clear() { | ||||||
| @ -135,7 +135,7 @@ class BenchmarkPage extends ConsumerWidget { | |||||||
|     } |     } | ||||||
|     keyContent.print(state.secretKey.bytes.toString()); |     keyContent.print(state.secretKey.bytes.toString()); | ||||||
| 
 | 
 | ||||||
|     AES cipher = AES(state.secretKey, AESMode.gcm); |     AES cipher = AES(state.secretKey); | ||||||
| 
 | 
 | ||||||
|     return SingleChildScrollView( |     return SingleChildScrollView( | ||||||
|       child: Padding( |       child: Padding( | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: cipher_page.dart | // File: cipher_page.dart | ||||||
| // Created Date: 28/12/2021 13:33:15 | // Created Date: 28/12/2021 13:33:15 | ||||||
| // Last Modified: 25/05/2022 10:49:30 | // Last Modified: 26/05/2022 20:39:37 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2021 | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| @ -12,10 +12,10 @@ import 'dart:typed_data'; | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
| import 'package:native_crypto/native_crypto.dart'; | import 'package:native_crypto/native_crypto.dart'; | ||||||
|  | import 'package:native_crypto/native_crypto_ext.dart'; | ||||||
| import 'package:native_crypto_example/widgets/button.dart'; | import 'package:native_crypto_example/widgets/button.dart'; | ||||||
| 
 | 
 | ||||||
| import '../session.dart'; | import '../session.dart'; | ||||||
| import '../utils.dart'; |  | ||||||
| import '../widgets/output.dart'; | import '../widgets/output.dart'; | ||||||
| 
 | 
 | ||||||
| // ignore: must_be_immutable | // ignore: must_be_immutable | ||||||
| @ -26,8 +26,8 @@ class CipherPage extends ConsumerWidget { | |||||||
|   final Output encryptionStatus = Output(); |   final Output encryptionStatus = Output(); | ||||||
|   final Output decryptionStatus = Output(); |   final Output decryptionStatus = Output(); | ||||||
| 
 | 
 | ||||||
|   final TextEditingController _plainTextController = TextEditingController(); |   final TextEditingController _plainTextController = TextEditingController()..text = 'PlainText'; | ||||||
|   CipherText? cipherText; |   CipherTextWrapper? cipherText; | ||||||
| 
 | 
 | ||||||
|   Future<void> _encrypt(WidgetRef ref, Cipher cipher) async { |   Future<void> _encrypt(WidgetRef ref, Cipher cipher) async { | ||||||
|     Session state = ref.read(sessionProvider.state).state; |     Session state = ref.read(sessionProvider.state).state; | ||||||
| @ -41,13 +41,10 @@ class CipherPage extends ConsumerWidget { | |||||||
|     } else { |     } else { | ||||||
|       var stringToBytes = plainText.toBytes(); |       var stringToBytes = plainText.toBytes(); | ||||||
|       cipherText = await cipher.encrypt(stringToBytes); |       cipherText = await cipher.encrypt(stringToBytes); | ||||||
|       encryptionStatus.print('String successfully encrypted.\n'); |       encryptionStatus.print('String successfully encrypted:\n'); | ||||||
|       encryptionStatus.append("Nonce: " + | 
 | ||||||
|           cipherText!.iv.toString() + |       CipherText unwrap = cipherText!.unwrap<CipherText>(); | ||||||
|           "\nData: " + |       encryptionStatus.append(unwrap.base16); | ||||||
|           cipherText!.data.toString() + |  | ||||||
|           "\nTag: " + |  | ||||||
|           cipherText!.tag.toString()); |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -56,17 +53,19 @@ class CipherPage extends ConsumerWidget { | |||||||
|       decryptionStatus.print('Encrypt before altering CipherText!'); |       decryptionStatus.print('Encrypt before altering CipherText!'); | ||||||
|     } else { |     } else { | ||||||
|       // Add 1 to the first byte |       // Add 1 to the first byte | ||||||
|       Uint8List _altered = cipherText!.data; |       Uint8List _altered = cipherText!.unwrap<CipherText>().bytes; | ||||||
|       _altered[0] += 1; |       _altered[0] += 1; | ||||||
|       // Recreate cipher text with altered data |       // Recreate cipher text with altered data | ||||||
|       cipherText = CipherText(cipherText!.iv, _altered, cipherText!.tag); |       cipherText = CipherTextWrapper.fromBytes( | ||||||
|       encryptionStatus.print('String successfully encrypted.\n'); |         _altered, | ||||||
|       encryptionStatus.append("Nonce: " + |         12, | ||||||
|           cipherText!.iv.toString() + |         _altered.length - 28, | ||||||
|           "\nData: " + |         16, | ||||||
|           cipherText!.data.toString() + |       ); | ||||||
|           "\nTag: " + |       encryptionStatus.print('String successfully encrypted:\n'); | ||||||
|           cipherText!.tag.toString()); | 
 | ||||||
|  |       CipherText unwrap = cipherText!.unwrap(); | ||||||
|  |       encryptionStatus.appendln(unwrap.base16); | ||||||
|       decryptionStatus.print('CipherText altered!\nDecryption will fail.'); |       decryptionStatus.print('CipherText altered!\nDecryption will fail.'); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -114,7 +113,7 @@ class CipherPage extends ConsumerWidget { | |||||||
|     } |     } | ||||||
|     keyContent.print(state.secretKey.bytes.toString()); |     keyContent.print(state.secretKey.bytes.toString()); | ||||||
| 
 | 
 | ||||||
|     AES cipher = AES(state.secretKey, AESMode.gcm); |     AES cipher = AES(state.secretKey); | ||||||
|     return SingleChildScrollView( |     return SingleChildScrollView( | ||||||
|       child: Padding( |       child: Padding( | ||||||
|         padding: const EdgeInsets.all(8.0), |         padding: const EdgeInsets.all(8.0), | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: kdf_page.dart | // File: kdf_page.dart | ||||||
| // Created Date: 28/12/2021 13:40:34 | // Created Date: 28/12/2021 13:40:34 | ||||||
| // Last Modified: 23/05/2022 22:49:06 | // Last Modified: 26/05/2022 20:30:31 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2021 | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| @ -12,10 +12,10 @@ import 'dart:typed_data'; | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter_riverpod/flutter_riverpod.dart'; | import 'package:flutter_riverpod/flutter_riverpod.dart'; | ||||||
| import 'package:native_crypto/native_crypto.dart'; | import 'package:native_crypto/native_crypto.dart'; | ||||||
|  | import 'package:native_crypto/native_crypto_ext.dart'; | ||||||
| import 'package:native_crypto_example/widgets/button.dart'; | import 'package:native_crypto_example/widgets/button.dart'; | ||||||
| 
 | 
 | ||||||
| import '../session.dart'; | import '../session.dart'; | ||||||
| import '../utils.dart'; |  | ||||||
| import '../widgets/output.dart'; | import '../widgets/output.dart'; | ||||||
| 
 | 
 | ||||||
| class KdfPage extends ConsumerWidget { | class KdfPage extends ConsumerWidget { | ||||||
| @ -26,8 +26,8 @@ class KdfPage extends ConsumerWidget { | |||||||
|   final Output pbkdf2Status = Output(); |   final Output pbkdf2Status = Output(); | ||||||
|   final Output hashStatus = Output(large: true); |   final Output hashStatus = Output(large: true); | ||||||
| 
 | 
 | ||||||
|   final TextEditingController _pwdTextController = TextEditingController(); |   final TextEditingController _pwdTextController = TextEditingController()..text = 'Password'; | ||||||
|   final TextEditingController _messageTextController = TextEditingController(); |   final TextEditingController _messageTextController = TextEditingController()..text = 'Message'; | ||||||
| 
 | 
 | ||||||
|   Future<void> _generate(WidgetRef ref) async { |   Future<void> _generate(WidgetRef ref) async { | ||||||
|     Session state = ref.read(sessionProvider.state).state; |     Session state = ref.read(sessionProvider.state).state; | ||||||
| @ -66,7 +66,7 @@ class KdfPage extends ConsumerWidget { | |||||||
|     } else { |     } else { | ||||||
|       Uint8List hash = await hasher.digest(message.toBytes()); |       Uint8List hash = await hasher.digest(message.toBytes()); | ||||||
|       hashStatus.print( |       hashStatus.print( | ||||||
|           'Message successfully hashed with $hasher :${hash.toStr(to: Encoding.hex)}'); |           'Message successfully hashed with $hasher :${hash.toStr(to: Encoding.base16)}'); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,52 +0,0 @@ | |||||||
| // 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; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
							
								
								
									
										11
									
								
								packages/native_crypto/lib/native_crypto_ext.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packages/native_crypto/lib/native_crypto_ext.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: native_crypto_ext.dart | ||||||
|  | // Created Date: 26/05/2022 19:36:54 | ||||||
|  | // Last Modified: 26/05/2022 19:38:44 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2022 | ||||||
|  | 
 | ||||||
|  | export 'src/utils/encoding.dart'; | ||||||
|  | export 'src/utils/extensions.dart'; | ||||||
| @ -1,49 +0,0 @@ | |||||||
| // Author: Hugo Pointcheval |  | ||||||
| // Email: git@pcl.ovh |  | ||||||
| // ----- |  | ||||||
| // File: aes_builder.dart |  | ||||||
| // Created Date: 28/12/2021 12:03:11 |  | ||||||
| // Last Modified: 25/05/2022 10:47:11 |  | ||||||
| // ----- |  | ||||||
| // Copyright (c) 2021 |  | ||||||
| 
 |  | ||||||
| import 'package:native_crypto/src/ciphers/aes/aes.dart'; |  | ||||||
| import 'package:native_crypto/src/interfaces/builder.dart'; |  | ||||||
| import 'package:native_crypto/src/keys/secret_key.dart'; |  | ||||||
| import 'package:native_crypto_platform_interface/native_crypto_platform_interface.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 const CipherInitException( |  | ||||||
|           message: 'You must specify or generate a secret key.', |  | ||||||
|           code: 'missing_key', |  | ||||||
|         ); |  | ||||||
|       } else { |  | ||||||
|         _sk = await _fsk; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     return AES(_sk!, _mode); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @ -3,8 +3,8 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: builders.dart | // File: builders.dart | ||||||
| // Created Date: 23/05/2022 22:56:03 | // Created Date: 23/05/2022 22:56:03 | ||||||
| // Last Modified: 23/05/2022 22:56:12 | // Last Modified: 26/05/2022 19:22:19 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| export 'aes_builder.dart'; | export 'decryption_builder.dart'; | ||||||
|  | |||||||
| @ -0,0 +1,46 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: decryption_builder.dart | ||||||
|  | // Created Date: 26/05/2022 19:07:52 | ||||||
|  | // Last Modified: 26/05/2022 19:21:00 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2022 | ||||||
|  | 
 | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:native_crypto/src/core/cipher_text_wrapper.dart'; | ||||||
|  | import 'package:native_crypto/src/interfaces/cipher.dart'; | ||||||
|  | 
 | ||||||
|  | class DecryptionBuilder extends StatelessWidget { | ||||||
|  |   final Cipher cipher; | ||||||
|  |   final CipherTextWrapper data; | ||||||
|  |   final Widget Function(BuildContext context) onLoading; | ||||||
|  |   final Widget Function(BuildContext context, Object error) onError; | ||||||
|  |   final Widget Function(BuildContext context, Uint8List plainText) onSuccess; | ||||||
|  | 
 | ||||||
|  |   const DecryptionBuilder({ | ||||||
|  |     super.key, | ||||||
|  |     required this.cipher, | ||||||
|  |     required this.data, | ||||||
|  |     required this.onLoading, | ||||||
|  |     required this.onError, | ||||||
|  |     required this.onSuccess, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return FutureBuilder<Uint8List>( | ||||||
|  |       future: cipher.decrypt(data), | ||||||
|  |       builder: (context, snapshot) { | ||||||
|  |         if (snapshot.hasData) { | ||||||
|  |           return onSuccess(context, snapshot.data!); | ||||||
|  |         } else if (snapshot.hasError) { | ||||||
|  |           return onError(context, snapshot.error!); | ||||||
|  |         } | ||||||
|  |         return onLoading(context); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -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: 25/05/2022 21:17:10 | // Last Modified: 26/05/2022 19:43:22 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| @ -13,88 +13,120 @@ import 'package:native_crypto/src/ciphers/aes/aes_key_size.dart'; | |||||||
| import 'package:native_crypto/src/ciphers/aes/aes_mode.dart'; | import 'package:native_crypto/src/ciphers/aes/aes_mode.dart'; | ||||||
| import 'package:native_crypto/src/ciphers/aes/aes_padding.dart'; | import 'package:native_crypto/src/ciphers/aes/aes_padding.dart'; | ||||||
| import 'package:native_crypto/src/core/cipher_text.dart'; | import 'package:native_crypto/src/core/cipher_text.dart'; | ||||||
| import 'package:native_crypto/src/core/cipher_text_list.dart'; | import 'package:native_crypto/src/core/cipher_text_wrapper.dart'; | ||||||
| import 'package:native_crypto/src/interfaces/cipher.dart'; | import 'package:native_crypto/src/interfaces/cipher.dart'; | ||||||
| import 'package:native_crypto/src/keys/secret_key.dart'; | import 'package:native_crypto/src/keys/secret_key.dart'; | ||||||
| import 'package:native_crypto/src/platform.dart'; | import 'package:native_crypto/src/platform.dart'; | ||||||
| import 'package:native_crypto/src/utils/cipher_algorithm.dart'; | import 'package:native_crypto/src/utils/cipher_algorithm.dart'; | ||||||
|  | import 'package:native_crypto/src/utils/extensions.dart'; | ||||||
| import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; | import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; | ||||||
| 
 | 
 | ||||||
| export 'package:native_crypto/src/ciphers/aes/aes_key_size.dart'; | /// An AES cipher. | ||||||
| export 'package:native_crypto/src/ciphers/aes/aes_mode.dart'; | /// | ||||||
| export 'package:native_crypto/src/ciphers/aes/aes_padding.dart'; | /// [AES] is a [Cipher] that can be used to encrypt or decrypt data. | ||||||
| 
 |  | ||||||
| class AES implements Cipher { | class AES implements Cipher { | ||||||
|   final SecretKey key; |   final SecretKey _key; | ||||||
|   final AESMode mode; |   final AESMode mode; | ||||||
|   final AESPadding padding; |   final AESPadding padding; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   CipherAlgorithm get algorithm => CipherAlgorithm.aes; |   CipherAlgorithm get algorithm => CipherAlgorithm.aes; | ||||||
| 
 | 
 | ||||||
|   AES(this.key, this.mode, {this.padding = AESPadding.none}) { |   AES(SecretKey key, [this.mode = AESMode.gcm, this.padding = AESPadding.none]) | ||||||
|     if (!AESKeySize.supportedSizes.contains(key.bytes.length * 8)) { |       : _key = key { | ||||||
|       throw const CipherInitException( |     if (!AESKeySize.supportedSizes.contains(key.bitLength)) { | ||||||
|         message: 'Invalid key length!', |       throw NativeCryptoException( | ||||||
|         code: 'invalid_key_length', |         message: 'Invalid key size! ' | ||||||
|  |             'Expected: ${AESKeySize.supportedSizes.join(', ')} bits', | ||||||
|  |         code: NativeCryptoExceptionCode.invalid_key_length.code, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     final Map<AESMode, List<AESPadding>> _supported = { |  | ||||||
|       AESMode.gcm: [AESPadding.none], |  | ||||||
|     }; |  | ||||||
| 
 | 
 | ||||||
|     if (!_supported[mode]!.contains(padding)) { |     if (!mode.supportedPaddings.contains(padding)) { | ||||||
|       throw const CipherInitException( |       throw NativeCryptoException( | ||||||
|         message: 'Invalid padding!', |         message: 'Invalid padding! ' | ||||||
|         code: 'invalid_padding', |             'Expected: ${mode.supportedPaddings.join(', ')}', | ||||||
|  |         code: NativeCryptoExceptionCode.invalid_padding.code, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<Uint8List> _decrypt(CipherText cipherText) async { |   Future<Uint8List> _decrypt(CipherText cipherText, | ||||||
|     return await platform.decryptAsList( |       {int chunkCount = 0,}) async { | ||||||
|           [cipherText.iv, cipherText.payload], |     final Uint8List? decrypted = await platform.decrypt( | ||||||
|           key.bytes, |       cipherText.bytes, | ||||||
|  |       _key.bytes, | ||||||
|       algorithm.name, |       algorithm.name, | ||||||
|         ) ?? |     ); | ||||||
|         Uint8List(0); | 
 | ||||||
|  |     if (decrypted.isNull) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Platform returned null when decrypting chunk #$chunkCount', | ||||||
|  |         code: NativeCryptoExceptionCode.platform_returned_null.code, | ||||||
|  |       ); | ||||||
|  |     } else if (decrypted!.isEmpty) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Platform returned no data when decrypting chunk #$chunkCount', | ||||||
|  |         code: NativeCryptoExceptionCode.platform_returned_empty_data.code, | ||||||
|  |       ); | ||||||
|  |     } else { | ||||||
|  |       return decrypted; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<CipherText> _encrypt(Uint8List data) async { |   Future<CipherText> _encrypt(Uint8List data, {int chunkCount = 0}) async { | ||||||
|     final List<Uint8List> cipherText = |     final Uint8List? encrypted = await platform.encrypt( | ||||||
|         await platform.encryptAsList(data, key.bytes, algorithm.name) ?? |       data, | ||||||
|             List.empty(); |       _key.bytes, | ||||||
|     return CipherText.fromPairIvAndBytes( |       algorithm.name, | ||||||
|       cipherText, |  | ||||||
|       dataLength: cipherText.last.length - 16, |  | ||||||
|       tagLength: 16, |  | ||||||
|     ); |     ); | ||||||
|  | 
 | ||||||
|  |     if (encrypted.isNull) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Platform returned null when encrypting chunk #$chunkCount', | ||||||
|  |         code: NativeCryptoExceptionCode.platform_returned_null.code, | ||||||
|  |       ); | ||||||
|  |     } else if (encrypted!.isEmpty) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Platform returned no data when encrypting chunk #$chunkCount', | ||||||
|  |         code: NativeCryptoExceptionCode.platform_returned_empty_data.code, | ||||||
|  |       ); | ||||||
|  |     } else { | ||||||
|  |       return CipherText.fromBytes( | ||||||
|  |         12, | ||||||
|  |         encrypted.length - 28, | ||||||
|  |         16, | ||||||
|  |         CipherAlgorithm.aes, | ||||||
|  |         encrypted, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<Uint8List> decrypt(CipherText cipherText) async { |   Future<Uint8List> decrypt(CipherTextWrapper cipherText) async { | ||||||
|     final BytesBuilder decryptedData = BytesBuilder(copy: false); |     final BytesBuilder decryptedData = BytesBuilder(copy: false); | ||||||
| 
 | 
 | ||||||
|     if (cipherText is CipherTextList) { |     if (cipherText.isList) { | ||||||
|       for (final CipherText ct in cipherText.list) { |       int chunkCount = 0; | ||||||
|         decryptedData.add(await _decrypt(ct)); |       for (final CipherText chunk in cipherText.list) { | ||||||
|  |         decryptedData.add(await _decrypt(chunk, chunkCount: chunkCount++)); | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       decryptedData.add(await _decrypt(cipherText)); |       decryptedData.add(await _decrypt(cipherText.single)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return decryptedData.toBytes(); |     return decryptedData.toBytes(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Future<CipherText> encrypt(Uint8List data) async { |   Future<CipherTextWrapper> encrypt(Uint8List data) async { | ||||||
|  |     CipherTextWrapper cipherTextWrapper; | ||||||
|     Uint8List dataToEncrypt; |     Uint8List dataToEncrypt; | ||||||
| 
 |  | ||||||
|     final CipherTextList cipherTextList = CipherTextList(); |  | ||||||
| 
 |  | ||||||
|     if (data.length > Cipher.bytesCountPerChunk) { |  | ||||||
|     final int chunkNb = (data.length / Cipher.bytesCountPerChunk).ceil(); |     final int chunkNb = (data.length / Cipher.bytesCountPerChunk).ceil(); | ||||||
|  | 
 | ||||||
|  |     if (chunkNb > 1) { | ||||||
|  |       cipherTextWrapper = CipherTextWrapper.empty(); | ||||||
|       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( | ||||||
| @ -102,11 +134,12 @@ class AES implements Cipher { | |||||||
|                 (i + 1) * Cipher.bytesCountPerChunk, |                 (i + 1) * Cipher.bytesCountPerChunk, | ||||||
|               ) |               ) | ||||||
|             : data.sublist(i * Cipher.bytesCountPerChunk); |             : data.sublist(i * Cipher.bytesCountPerChunk); | ||||||
|         cipherTextList.add(await _encrypt(dataToEncrypt)); |         cipherTextWrapper.add(await _encrypt(dataToEncrypt, chunkCount: i)); | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       return _encrypt(data); |       cipherTextWrapper = CipherTextWrapper.single(await _encrypt(data)); | ||||||
|     } |     } | ||||||
|     return cipherTextList; | 
 | ||||||
|  |     return cipherTextWrapper; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: aes_key_size.dart | // File: aes_key_size.dart | ||||||
| // Created Date: 23/05/2022 22:10:07 | // Created Date: 23/05/2022 22:10:07 | ||||||
| // Last Modified: 23/05/2022 22:33:32 | // Last Modified: 26/05/2022 18:45:01 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| @ -13,9 +13,13 @@ enum AESKeySize { | |||||||
|   bits192(192), |   bits192(192), | ||||||
|   bits256(256); |   bits256(256); | ||||||
| 
 | 
 | ||||||
|  |   /// Returns the number of bits supported by an [AESKeySize]. | ||||||
|   static final List<int> supportedSizes = [128, 192, 256]; |   static final List<int> supportedSizes = [128, 192, 256]; | ||||||
| 
 | 
 | ||||||
|  |   /// Returns the number of bits in this [AESKeySize]. | ||||||
|   final int bits; |   final int bits; | ||||||
|  | 
 | ||||||
|  |   /// Returns the number of bytes in this [AESKeySize]. | ||||||
|   int get bytes => bits ~/ 8; |   int get bytes => bits ~/ 8; | ||||||
| 
 | 
 | ||||||
|   const AESKeySize(this.bits); |   const AESKeySize(this.bits); | ||||||
|  | |||||||
| @ -3,9 +3,18 @@ | |||||||
| // ----- | // ----- | ||||||
| // 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: 25/05/2022 09:23:54 | // Last Modified: 26/05/2022 18:41:31 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
|  | import 'package:native_crypto/src/ciphers/aes/aes_padding.dart'; | ||||||
|  | 
 | ||||||
| /// Defines the AES modes of operation. | /// Defines the AES modes of operation. | ||||||
| enum AESMode { gcm } | enum AESMode { | ||||||
|  |   gcm([AESPadding.none]); | ||||||
|  | 
 | ||||||
|  |   /// Returns the list of supported [AESPadding] for this [AESMode]. | ||||||
|  |   final List<AESPadding> supportedPaddings; | ||||||
|  | 
 | ||||||
|  |   const AESMode(this.supportedPaddings); | ||||||
|  | } | ||||||
|  | |||||||
| @ -3,49 +3,78 @@ | |||||||
| // ----- | // ----- | ||||||
| // 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: 26/05/2022 16:22:49 | // Last Modified: 26/05/2022 19:43:57 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2021 | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| import 'dart:typed_data'; | import 'dart:typed_data'; | ||||||
| 
 | 
 | ||||||
|  | import 'package:native_crypto/src/core/cipher_text_wrapper.dart'; | ||||||
|  | import 'package:native_crypto/src/interfaces/byte_array.dart'; | ||||||
|  | import 'package:native_crypto/src/interfaces/cipher.dart'; | ||||||
| import 'package:native_crypto/src/utils/cipher_algorithm.dart'; | import 'package:native_crypto/src/utils/cipher_algorithm.dart'; | ||||||
| import 'package:native_crypto/src/utils/extensions.dart'; | import 'package:native_crypto/src/utils/extensions.dart'; | ||||||
| import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; | import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; | ||||||
| 
 | 
 | ||||||
| /// Represents a cipher text in Native Crypto. | /// Represents a cipher text in NativeCrypto. | ||||||
| /// | /// | ||||||
| /// It is represented as a [List] of [Uint8List] like: | /// [CipherText] is a [ByteArray] that can be used to store encrypted data. | ||||||
|  | /// It is represented like: | ||||||
| /// ```txt | /// ```txt | ||||||
| /// [[NONCE], [MESSAGE + TAG]] | /// [IV + MESSAGE + TAG] | ||||||
| /// ``` | /// ``` | ||||||
| /// where: | /// where: | ||||||
| /// - `[NONCE]` is a [Uint8List] of length [CipherText.ivLength] | /// - IV's length is [CipherText.ivLength] bytes. | ||||||
| /// - `[MESSAGE + TAG]` is a [Uint8List] of length [CipherText.dataLength] | /// - MESSAGE's length is [CipherText.messageLength] bytes. | ||||||
|  | /// - TAG's length is [CipherText.tagLength] bytes. | ||||||
| ///  | ///  | ||||||
| /// To  | /// Check [CipherTextWrapper] for more information. | ||||||
| /// | class CipherText extends ByteArray { | ||||||
| /// So accessing just the Message or just the Tag is costly and should be  |  | ||||||
| /// done only when needed. |  | ||||||
| class CipherText { |  | ||||||
|   final int _ivLength; |   final int _ivLength; | ||||||
|   final int _messageLength; |   final int _messageLength; | ||||||
|   final int _tagLength; |   final int _tagLength; | ||||||
| 
 | 
 | ||||||
|   final CipherAlgorithm? _cipherAlgorithm; |   final CipherAlgorithm? _cipherAlgorithm; | ||||||
| 
 | 
 | ||||||
|   final Uint8List? _iv; |   const CipherText._( | ||||||
|   final Uint8List? _data; // Contains the message + tag (if any) |  | ||||||
| 
 |  | ||||||
|   CipherText._( |  | ||||||
|     this._ivLength, |     this._ivLength, | ||||||
|     this._messageLength, |     this._messageLength, | ||||||
|     this._tagLength, |     this._tagLength, | ||||||
|     this._cipherAlgorithm, |     this._cipherAlgorithm, | ||||||
|     this._iv, |     super.bytes, | ||||||
|     this._data, |  | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|  |   factory CipherText.fromBytes( | ||||||
|  |     int ivLength, | ||||||
|  |     int messageLength, | ||||||
|  |     int tagLength, | ||||||
|  |     CipherAlgorithm? cipherAlgorithm, | ||||||
|  |     Uint8List bytes, | ||||||
|  |   ) { | ||||||
|  |     if (bytes.length != ivLength + messageLength + tagLength) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Invalid cipher text length! ' | ||||||
|  |             'Expected: ${ivLength + messageLength + tagLength} bytes', | ||||||
|  |         code: NativeCryptoExceptionCode.invalid_argument.code, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (messageLength > Cipher.bytesCountPerChunk) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Cipher text is too big! Consider using chunks.', | ||||||
|  |         code: NativeCryptoExceptionCode.invalid_argument.code, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return CipherText._( | ||||||
|  |       ivLength, | ||||||
|  |       messageLength, | ||||||
|  |       tagLength, | ||||||
|  |       cipherAlgorithm, | ||||||
|  |       bytes, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   /// Gets the [CipherAlgorithm] used to encrypt the [CipherText]. |   /// Gets the [CipherAlgorithm] used to encrypt the [CipherText]. | ||||||
|   CipherAlgorithm get cipherAlgorithm { |   CipherAlgorithm get cipherAlgorithm { | ||||||
|     if (_cipherAlgorithm.isNotNull) { |     if (_cipherAlgorithm.isNotNull) { | ||||||
| @ -58,89 +87,12 @@ class CipherText { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Gets the [Uint8List] of the [CipherText]'s IV. |  | ||||||
|   Uint8List get iv { |  | ||||||
|     if (_iv.isNotNull) { |  | ||||||
|       return _iv!; |  | ||||||
|     } else { |  | ||||||
|       throw NativeCryptoException( |  | ||||||
|         message: 'IV is not specified', |  | ||||||
|         code: NativeCryptoExceptionCode.invalid_data.code, |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   /// Gets the length of the [CipherText]'s IV. |   /// Gets the length of the [CipherText]'s IV. | ||||||
|   int get ivLength => _ivLength; |   int get ivLength => _ivLength; | ||||||
| 
 | 
 | ||||||
|   /// Gets the [Uint8List] of the [CipherText]'s data. |   /// Gets the length of the [CipherText]'s Message. | ||||||
|   Uint8List get data { |   int get messageLength => _messageLength; | ||||||
|     if (_data.isNotNull) { |  | ||||||
|       return _data!; |  | ||||||
|     } else { |  | ||||||
|       throw NativeCryptoException( |  | ||||||
|         message: 'Data is not specified', |  | ||||||
|         code: NativeCryptoExceptionCode.invalid_data.code, |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   /// Gets the length of the [CipherText]'s data. |   /// Gets the length of the [CipherText]'s Tag. | ||||||
|   int get dataLength => _messageLength + _tagLength; |   int get tagLength => _tagLength; | ||||||
| 
 |  | ||||||
|   // 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. |  | ||||||
|   // Uint8List get iv => _iv; |  | ||||||
| 
 |  | ||||||
|   // /// Gets the CipherText data. |  | ||||||
|   // Uint8List get data => _tagLength > 0 |  | ||||||
|   //     ? bytes.sublist(0, _dataLength - _tagLength) |  | ||||||
|   //     : bytes; |  | ||||||
| 
 |  | ||||||
|   // /// Gets the CipherText tag. |  | ||||||
|   // Uint8List get tag => _tagLength > 0 |  | ||||||
|   //     ? bytes.sublist(_dataLength - _tagLength, _dataLength) |  | ||||||
|   //     : Uint8List(0); |  | ||||||
| 
 |  | ||||||
|   // /// Gets the CipherText data and tag. |  | ||||||
|   // Uint8List get payload => bytes; |  | ||||||
| 
 |  | ||||||
|   // /// 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; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,26 +0,0 @@ | |||||||
| // Author: Hugo Pointcheval |  | ||||||
| // Email: git@pcl.ovh |  | ||||||
| // ----- |  | ||||||
| // File: cipher_text_list.dart |  | ||||||
| // Created Date: 23/05/2022 22:59:02 |  | ||||||
| // Last Modified: 24/05/2022 20:18:26 |  | ||||||
| // ----- |  | ||||||
| // Copyright (c) 2022 |  | ||||||
| 
 |  | ||||||
| import 'dart:typed_data'; |  | ||||||
| 
 |  | ||||||
| import 'package:native_crypto/src/core/cipher_text.dart'; |  | ||||||
| 
 |  | ||||||
| class CipherTextList extends CipherText { |  | ||||||
|   final List<CipherText> _list; |  | ||||||
| 
 |  | ||||||
|   CipherTextList() |  | ||||||
|       : _list = [], |  | ||||||
|         super(Uint8List(0), Uint8List(0), Uint8List(0)); |  | ||||||
| 
 |  | ||||||
|   void add(CipherText cipherText) { |  | ||||||
|     _list.add(cipherText); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   List<CipherText> get list => _list; |  | ||||||
| } |  | ||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: cipher_text_wrapper.dart | // File: cipher_text_wrapper.dart | ||||||
| // Created Date: 26/05/2022 14:27:32 | // Created Date: 26/05/2022 14:27:32 | ||||||
| // Last Modified: 26/05/2022 15:53:46 | // Last Modified: 26/05/2022 20:32:38 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| @ -12,31 +12,85 @@ import 'dart:typed_data'; | |||||||
| import 'package:native_crypto/native_crypto.dart'; | import 'package:native_crypto/native_crypto.dart'; | ||||||
| import 'package:native_crypto/src/utils/extensions.dart'; | import 'package:native_crypto/src/utils/extensions.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Wrapper for [CipherText] | ||||||
|  | /// | ||||||
|  | /// Typically, this object is the result of an encryption operation. | ||||||
|  | /// For decryption you have to build this before using it. | ||||||
| class CipherTextWrapper { | class CipherTextWrapper { | ||||||
|   final CipherText? _single; |   final CipherText? _single; | ||||||
|   final List<CipherText>? _list; |   final List<CipherText>? _list; | ||||||
| 
 | 
 | ||||||
|   CipherTextWrapper._(this._single, this._list); |   CipherTextWrapper._(this._single, this._list); | ||||||
| 
 | 
 | ||||||
|  |   /// Creates a [CipherTextWrapper] from a [CipherText]. | ||||||
|   factory CipherTextWrapper.single(CipherText cipherText) => |   factory CipherTextWrapper.single(CipherText cipherText) => | ||||||
|       CipherTextWrapper._(cipherText, null); |       CipherTextWrapper._(cipherText, null); | ||||||
| 
 | 
 | ||||||
|  |   /// Creates a [CipherTextWrapper] from a [List] of [CipherText]. | ||||||
|   factory CipherTextWrapper.list(List<CipherText> cipherTexts) => |   factory CipherTextWrapper.list(List<CipherText> cipherTexts) => | ||||||
|       CipherTextWrapper._(null, cipherTexts); |       CipherTextWrapper._(null, cipherTexts); | ||||||
| 
 | 
 | ||||||
|  |   /// Creates an empty [List] in a [CipherTextWrapper]. | ||||||
|  |   /// | ||||||
|  |   /// This is useful when you want to create a [CipherTextWrapper] then | ||||||
|  |   /// fill it with data. | ||||||
|  |   factory CipherTextWrapper.empty() => CipherTextWrapper._(null, []); | ||||||
|  | 
 | ||||||
|  |   /// Creates a [CipherTextWrapper] from a [Uint8List]. | ||||||
|  |   /// | ||||||
|  |   /// This is a convenience method to create a [CipherTextWrapper] | ||||||
|  |   /// from a [Uint8List]. It tries to detect if the [Uint8List] is a | ||||||
|  |   /// single [CipherText] or a list of [CipherText]. | ||||||
|  |   /// | ||||||
|  |   /// You can customize the chunk size by passing a [chunkSize] parameter. | ||||||
|  |   /// The default chunk size is [Cipher.bytesCountPerChunk]. | ||||||
|  |   /// | ||||||
|  |   /// Throw an [NativeCryptoExceptionCode] with | ||||||
|  |   /// [NativeCryptoExceptionCode.invalid_argument] if the [Uint8List] is | ||||||
|  |   /// not a valid [CipherText] or a [List] of [CipherText]. | ||||||
|   factory CipherTextWrapper.fromBytes( |   factory CipherTextWrapper.fromBytes( | ||||||
|       // Uint8List bytes, { |     Uint8List bytes, | ||||||
|       // required int ivLength, |     int ivLength, | ||||||
|       // required int dataLength, |     int messageLength, | ||||||
|       // int tagLength = 0, |     int tagLength, { | ||||||
|       // int? chunkSize, |     CipherAlgorithm? cipherAlgorithm, | ||||||
|       // } |     int? chunkSize, | ||||||
|       ) { |   }) { | ||||||
|         // TODO(hpcl): implement fromBytes |     chunkSize ??= Cipher.bytesCountPerChunk; | ||||||
|     throw UnimplementedError(); |     Cipher.bytesCountPerChunk = chunkSize; | ||||||
|  | 
 | ||||||
|  |     if (bytes.length <= chunkSize) { | ||||||
|  |       return CipherTextWrapper.single( | ||||||
|  |         CipherText.fromBytes( | ||||||
|  |           ivLength, | ||||||
|  |           messageLength, | ||||||
|  |           tagLength, | ||||||
|  |           cipherAlgorithm, | ||||||
|  |           bytes, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } else { | ||||||
|  |       final cipherTexts = <CipherText>[]; | ||||||
|  |       for (var i = 0; i < bytes.length; i += chunkSize) { | ||||||
|  |         final chunk = bytes.sublist(i, i + chunkSize); | ||||||
|  |         cipherTexts.add( | ||||||
|  |           CipherText.fromBytes( | ||||||
|  |             ivLength, | ||||||
|  |             messageLength, | ||||||
|  |             tagLength, | ||||||
|  |             cipherAlgorithm, | ||||||
|  |             chunk, | ||||||
|  |           ), | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |       return CipherTextWrapper.list(cipherTexts); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Checks if the [CipherText] is a single [CipherText]. | ||||||
|   bool get isSingle => _single.isNotNull; |   bool get isSingle => _single.isNotNull; | ||||||
|  | 
 | ||||||
|  |   /// Checks if the [CipherText] is a [List] of [CipherText]. | ||||||
|   bool get isList => _list.isNotNull; |   bool get isList => _list.isNotNull; | ||||||
| 
 | 
 | ||||||
|   /// Gets the [CipherText] if it's a single one. |   /// Gets the [CipherText] if it's a single one. | ||||||
| @ -70,7 +124,7 @@ class CipherTextWrapper { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// Gets the raw [Uint8List] of the [CipherText] or [List] of [CipherText]. |   /// Gets the raw [Uint8List] of the [CipherText] or [List] of [CipherText]. | ||||||
|   Uint8List get raw { |   Uint8List get bytes { | ||||||
|     if (isSingle) { |     if (isSingle) { | ||||||
|       return single.bytes; |       return single.bytes; | ||||||
|     } else { |     } else { | ||||||
| @ -78,6 +132,9 @@ class CipherTextWrapper { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Gets the number of parts of the [CipherText] or [List] of [CipherText]. | ||||||
|  |   /// | ||||||
|  |   /// Check [Cipher.bytesCountPerChunk] for more information. | ||||||
|   int get chunkCount { |   int get chunkCount { | ||||||
|     _single.isNull; |     _single.isNull; | ||||||
|     if (_single.isNotNull) { |     if (_single.isNotNull) { | ||||||
| @ -86,4 +143,41 @@ class CipherTextWrapper { | |||||||
|       return _list?.length ?? 0; |       return _list?.length ?? 0; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   /// Gets the [CipherText] or the [List] of [CipherText]. | ||||||
|  |   /// | ||||||
|  |   /// Throws [NativeCryptoException] with | ||||||
|  |   /// [NativeCryptoExceptionCode.invalid_data] if it's not a single or a list or | ||||||
|  |   /// if [T] is not [CipherText] or [List] of [CipherText]. | ||||||
|  |   T unwrap<T>() { | ||||||
|  |     if (isSingle && T == CipherText) { | ||||||
|  |       return single as T; | ||||||
|  |     } else if (isList && T == List<CipherText>) { | ||||||
|  |       return list as T; | ||||||
|  |     } else { | ||||||
|  |       final String type = | ||||||
|  |           isSingle ? 'CipherText' : (isList ? 'List<CipherText>' : 'unknown'); | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'CipherTextWrapper is not a $T but a $type, ' | ||||||
|  |             'you should use unwrap<$type>()', | ||||||
|  |         code: NativeCryptoExceptionCode.invalid_data.code, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void add(CipherText cipherText) { | ||||||
|  |     if (isSingle) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'CipherTextWrapper is already single', | ||||||
|  |         code: NativeCryptoExceptionCode.invalid_data.code, | ||||||
|  |       ); | ||||||
|  |     } else if (isList) { | ||||||
|  |       _list!.add(cipherText); | ||||||
|  |     } else { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'CipherTextWrapper is not single or list', | ||||||
|  |         code: NativeCryptoExceptionCode.invalid_data.code, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,9 +3,9 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: core.dart | // File: core.dart | ||||||
| // Created Date: 23/05/2022 23:05:26 | // Created Date: 23/05/2022 23:05:26 | ||||||
| // Last Modified: 25/05/2022 10:44:32 | // Last Modified: 26/05/2022 17:10:25 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| export 'cipher_text.dart'; | export 'cipher_text.dart'; | ||||||
| export 'cipher_text_list.dart'; | export 'cipher_text_wrapper.dart'; | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								packages/native_crypto/lib/src/interfaces/base_key.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								packages/native_crypto/lib/src/interfaces/base_key.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | // Author: Hugo Pointcheval | ||||||
|  | // Email: git@pcl.ovh | ||||||
|  | // ----- | ||||||
|  | // File: base_key.dart | ||||||
|  | // Created Date: 16/12/2021 16:28:00 | ||||||
|  | // Last Modified: 26/05/2022 17:40:38 | ||||||
|  | // ----- | ||||||
|  | // Copyright (c) 2021 | ||||||
|  | 
 | ||||||
|  | import 'package:native_crypto/src/interfaces/byte_array.dart'; | ||||||
|  | 
 | ||||||
|  | /// Represents a key in NativeCrypto. | ||||||
|  | /// | ||||||
|  | /// [BaseKey] is a [ByteArray] that can be used to store keys. | ||||||
|  | /// | ||||||
|  | /// This interface is implemented by all the key classes. | ||||||
|  | abstract class BaseKey extends ByteArray { | ||||||
|  |   const BaseKey(super.bytes); | ||||||
|  |   BaseKey.fromBase16(super.encoded) : super.fromBase16(); | ||||||
|  |   BaseKey.fromBase64(super.encoded) : super.fromBase64(); | ||||||
|  |   BaseKey.fromUtf8(super.input) : super.fromUtf8(); | ||||||
|  | } | ||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: byte_array.dart | // File: byte_array.dart | ||||||
| // Created Date: 16/12/2021 17:54:16 | // Created Date: 16/12/2021 17:54:16 | ||||||
| // Last Modified: 26/05/2022 14:25:05 | // Last Modified: 26/05/2022 17:13:27 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2021 | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| @ -13,6 +13,9 @@ import 'package:flutter/foundation.dart'; | |||||||
| import 'package:native_crypto/src/utils/encoding.dart'; | import 'package:native_crypto/src/utils/encoding.dart'; | ||||||
| import 'package:native_crypto/src/utils/extensions.dart'; | import 'package:native_crypto/src/utils/extensions.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Represents a byte array. | ||||||
|  | /// | ||||||
|  | /// [ByteArray] wraps a [Uint8List] and provides some useful conversion methods. | ||||||
| @immutable | @immutable | ||||||
| abstract class ByteArray { | abstract class ByteArray { | ||||||
|   final Uint8List _bytes; |   final Uint8List _bytes; | ||||||
| @ -56,6 +59,12 @@ abstract class ByteArray { | |||||||
|   /// Gets the [ByteArray] bytes as an UTF-16 representation. |   /// Gets the [ByteArray] bytes as an UTF-16 representation. | ||||||
|   String get utf16 => _bytes.toStr(); |   String get utf16 => _bytes.toStr(); | ||||||
| 
 | 
 | ||||||
|  |   /// Gets the [ByteArray] length in bytes. | ||||||
|  |   int get length => _bytes.length; | ||||||
|  | 
 | ||||||
|  |   /// Gets the [ByteArray] length in bits. | ||||||
|  |   int get bitLength => _bytes.length * 8; | ||||||
|  | 
 | ||||||
|   @override |   @override | ||||||
|   bool operator ==(Object other) { |   bool operator ==(Object other) { | ||||||
|     if (other is ByteArray) { |     if (other is ByteArray) { | ||||||
|  | |||||||
| @ -3,44 +3,48 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: cipher.dart | // File: cipher.dart | ||||||
| // Created Date: 16/12/2021 16:28:00 | // Created Date: 16/12/2021 16:28:00 | ||||||
| // Last Modified: 24/05/2022 19:55:38 | // Last Modified: 26/05/2022 17:38:26 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2021 | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| import 'dart:typed_data'; | import 'dart:typed_data'; | ||||||
| 
 | 
 | ||||||
| import 'package:native_crypto/src/core/cipher_text.dart'; | import 'package:native_crypto/src/core/cipher_text_wrapper.dart'; | ||||||
| import 'package:native_crypto/src/utils/cipher_algorithm.dart'; | import 'package:native_crypto/src/utils/cipher_algorithm.dart'; | ||||||
| 
 | 
 | ||||||
| /// Represents a cipher. | /// Represents a cipher in NativeCrypto. | ||||||
| /// | /// | ||||||
| /// In cryptography, a cipher is an algorithm for performing encryption | /// In cryptography, a [Cipher] is an algorithm for performing encryption | ||||||
| /// 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. | ||||||
|  | ///  | ||||||
|  | /// This interface is implemented by all the ciphers in NativeCrypto. | ||||||
| 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 _bytesCountPerChunk = 33554432; | ||||||
| 
 | 
 | ||||||
|  |   /// Returns the size of a chunk of data  | ||||||
|  |   /// that can be processed by the [Cipher]. | ||||||
|   static int get bytesCountPerChunk => Cipher._bytesCountPerChunk; |   static int get bytesCountPerChunk => Cipher._bytesCountPerChunk; | ||||||
| 
 | 
 | ||||||
|  |   /// Sets the size of a chunk of data | ||||||
|  |   /// that can be processed by the [Cipher]. | ||||||
|   static set bytesCountPerChunk(int bytesCount) { |   static set bytesCountPerChunk(int bytesCount) { | ||||||
|     _bytesCountPerChunk = bytesCount; |     _bytesCountPerChunk = bytesCount; | ||||||
|   } |   } | ||||||
|    |    | ||||||
|   /// Returns the standard algorithm name for this cipher |   /// Returns the standard algorithm for this [Cipher]. | ||||||
|   CipherAlgorithm get algorithm; |   CipherAlgorithm get algorithm; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|   /// Encrypts data. |   /// Encrypts the [data]. | ||||||
|   /// |   /// | ||||||
|   /// Takes [Uint8List] data as parameter. |   /// Takes [Uint8List] data as parameter. | ||||||
|   /// Returns a [CipherText]. |   /// Returns a [CipherTextWrapper]. | ||||||
|   Future<CipherText> encrypt(Uint8List data); |   Future<CipherTextWrapper> encrypt(Uint8List data); | ||||||
| 
 | 
 | ||||||
|   /// Decrypts cipher text. |   /// Decrypts the [cipherText] | ||||||
|   /// |   /// | ||||||
|   /// Takes [CipherText] as parameter. |   /// Takes [CipherTextWrapper] as parameter. | ||||||
|   /// And returns plain text data as [Uint8List]. |   /// And returns plain text data as [Uint8List]. | ||||||
|   Future<Uint8List> decrypt(CipherText cipherText); |   Future<Uint8List> decrypt(CipherTextWrapper cipherText); | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,12 +3,12 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: interfaces.dart | // File: interfaces.dart | ||||||
| // Created Date: 23/05/2022 23:03:47 | // Created Date: 23/05/2022 23:03:47 | ||||||
| // Last Modified: 23/05/2022 23:10:15 | // Last Modified: 26/05/2022 17:41:06 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
|  | export 'base_key.dart'; | ||||||
| export 'builder.dart'; | export 'builder.dart'; | ||||||
| export 'byte_array.dart'; | export 'byte_array.dart'; | ||||||
| export 'cipher.dart'; | export 'cipher.dart'; | ||||||
| // export 'key.dart'; |  | ||||||
| export 'keyderivation.dart'; | export 'keyderivation.dart'; | ||||||
|  | |||||||
| @ -1,18 +0,0 @@ | |||||||
| // Author: Hugo Pointcheval |  | ||||||
| // Email: git@pcl.ovh |  | ||||||
| // ----- |  | ||||||
| // File: key.dart |  | ||||||
| // Created Date: 16/12/2021 16:28:00 |  | ||||||
| // Last Modified: 23/05/2022 23:02:10 |  | ||||||
| // ----- |  | ||||||
| // Copyright (c) 2021 |  | ||||||
| 
 |  | ||||||
| import 'package:native_crypto/src/interfaces/byte_array.dart'; |  | ||||||
| 
 |  | ||||||
| /// A class representing a key. |  | ||||||
| abstract class Key extends ByteArray { |  | ||||||
|   const Key(super.bytes); |  | ||||||
|   Key.fromBase16(super.encoded) : super.fromBase16(); |  | ||||||
|   Key.fromBase64(super.encoded) : super.fromBase64(); |  | ||||||
|   Key.fromUtf8(super.input) : super.fromUtf8(); |  | ||||||
| } |  | ||||||
| @ -3,18 +3,21 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: kdf.dart | // File: kdf.dart | ||||||
| // Created Date: 18/12/2021 11:56:43 | // Created Date: 18/12/2021 11:56:43 | ||||||
| // Last Modified: 23/05/2022 22:37:04 | // Last Modified: 26/05/2022 18:47:15 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2021 | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| import 'package:native_crypto/src/keys/secret_key.dart'; | import 'package:native_crypto/src/keys/secret_key.dart'; | ||||||
| import 'package:native_crypto/src/utils/kdf_algorithm.dart'; | import 'package:native_crypto/src/utils/kdf_algorithm.dart'; | ||||||
| 
 | 
 | ||||||
| /// Represents a Key Derivation Function | /// Represents a Key Derivation Function (KDF) in NativeCrypto. | ||||||
|  | ///  | ||||||
|  | /// [KeyDerivation] function is a function that takes some  | ||||||
|  | /// parameters and returns a [SecretKey]. | ||||||
| abstract class KeyDerivation { | abstract class KeyDerivation { | ||||||
|   /// Returns the standard algorithm name for this key derivation function |   /// Returns the standard algorithm for this key derivation function | ||||||
|   KdfAlgorithm get algorithm; |   KdfAlgorithm get algorithm; | ||||||
| 
 | 
 | ||||||
|   /// Derive key |   /// Derive a [SecretKey]. | ||||||
|   Future<SecretKey> derive(); |   Future<SecretKey> derive(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: pbkdf2.dart | // File: pbkdf2.dart | ||||||
| // Created Date: 17/12/2021 14:50:42 | // Created Date: 17/12/2021 14:50:42 | ||||||
| // Last Modified: 25/05/2022 10:45:00 | // Last Modified: 26/05/2022 18:51:59 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2021 | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| @ -12,10 +12,15 @@ import 'dart:typed_data'; | |||||||
| import 'package:native_crypto/src/interfaces/keyderivation.dart'; | import 'package:native_crypto/src/interfaces/keyderivation.dart'; | ||||||
| import 'package:native_crypto/src/keys/secret_key.dart'; | import 'package:native_crypto/src/keys/secret_key.dart'; | ||||||
| import 'package:native_crypto/src/platform.dart'; | import 'package:native_crypto/src/platform.dart'; | ||||||
|  | import 'package:native_crypto/src/utils/extensions.dart'; | ||||||
| import 'package:native_crypto/src/utils/hash_algorithm.dart'; | import 'package:native_crypto/src/utils/hash_algorithm.dart'; | ||||||
| import 'package:native_crypto/src/utils/kdf_algorithm.dart'; | import 'package:native_crypto/src/utils/kdf_algorithm.dart'; | ||||||
| import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; | import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Represent a PBKDF2 Key Derivation Function (KDF) in NativeCrypto. | ||||||
|  | /// | ||||||
|  | /// [Pbkdf2] is a function that takes password, salt, iteration count and | ||||||
|  | /// derive a [SecretKey] of specified length. | ||||||
| class Pbkdf2 extends KeyDerivation { | class Pbkdf2 extends KeyDerivation { | ||||||
|   final int _keyBytesCount; |   final int _keyBytesCount; | ||||||
|   final int _iterations; |   final int _iterations; | ||||||
| @ -35,20 +40,42 @@ class Pbkdf2 extends KeyDerivation { | |||||||
|   @override |   @override | ||||||
|   Future<SecretKey> derive({String? password, String? salt}) async { |   Future<SecretKey> derive({String? password, String? salt}) async { | ||||||
|     if (password == null || salt == null) { |     if (password == null || salt == null) { | ||||||
|       throw const KeyDerivationException( |       throw NativeCryptoException( | ||||||
|         message: "Password or Salt can't be null!", |         message: 'Password and salt cannot be null. ' | ||||||
|         code: 'invalid_password_or_salt', |             'Here is the password: $password, here is the salt: $salt', | ||||||
|  |         code: NativeCryptoExceptionCode.invalid_argument.code, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     final Uint8List derivation = (await platform.pbkdf2( |     final Uint8List? derivation = await platform.pbkdf2( | ||||||
|       password, |       password, | ||||||
|       salt, |       salt, | ||||||
|       _keyBytesCount, |       _keyBytesCount, | ||||||
|       _iterations, |       _iterations, | ||||||
|       _hash.name, |       _hash.name, | ||||||
|         )) ?? |     ); | ||||||
|         Uint8List(0); | 
 | ||||||
|  |     if (derivation.isNull) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Failed to derive a key! Platform returned null.', | ||||||
|  |         code: NativeCryptoExceptionCode.platform_returned_null.code, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (derivation!.isEmpty) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Failed to derive a key! Platform returned no data.', | ||||||
|  |         code: NativeCryptoExceptionCode.platform_returned_empty_data.code, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (derivation.length != _keyBytesCount) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Failed to derive a key! Platform returned ' | ||||||
|  |             '${derivation.length} bytes, but expected $_keyBytesCount bytes.', | ||||||
|  |         code: NativeCryptoExceptionCode.platform_returned_invalid_data.code, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return SecretKey(derivation); |     return SecretKey(derivation); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -3,46 +3,54 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: secret_key.dart | // File: secret_key.dart | ||||||
| // Created Date: 28/12/2021 13:36:54 | // Created Date: 28/12/2021 13:36:54 | ||||||
| // Last Modified: 26/05/2022 11:56:06 | // Last Modified: 26/05/2022 19:26:35 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2021 | // Copyright (c) 2021 | ||||||
| 
 | 
 | ||||||
| import 'dart:typed_data'; | import 'dart:typed_data'; | ||||||
| 
 | 
 | ||||||
| import 'package:native_crypto/src/interfaces/key.dart'; | import 'package:native_crypto/src/interfaces/base_key.dart'; | ||||||
|  | import 'package:native_crypto/src/interfaces/cipher.dart'; | ||||||
| import 'package:native_crypto/src/platform.dart'; | import 'package:native_crypto/src/platform.dart'; | ||||||
|  | import 'package:native_crypto/src/utils/extensions.dart'; | ||||||
| import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; | import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; | ||||||
| 
 | 
 | ||||||
| /// A class representing a secret key. | /// Represents a secret key in NativeCrypto. | ||||||
| /// A secret key is a key that is not accessible by anyone else. | /// | ||||||
| /// It is used to encrypt and decrypt data. | /// [SecretKey] is a [BaseKey] that can be used to store secret keys. | ||||||
| class SecretKey extends Key { | /// A [SecretKey] is a key that can be used to encrypt or decrypt data with | ||||||
|  | /// a symmetric [Cipher]. | ||||||
|  | class SecretKey extends BaseKey { | ||||||
|   const SecretKey(super.bytes); |   const SecretKey(super.bytes); | ||||||
|   SecretKey.fromBase16(super.encoded) : super.fromBase16(); |   SecretKey.fromBase16(super.encoded) : super.fromBase16(); | ||||||
|   SecretKey.fromBase64(super.encoded) : super.fromBase64(); |   SecretKey.fromBase64(super.encoded) : super.fromBase64(); | ||||||
|   SecretKey.fromUtf8(super.input) : super.fromUtf8(); |   SecretKey.fromUtf8(super.input) : super.fromUtf8(); | ||||||
| 
 | 
 | ||||||
|   static Future<SecretKey> fromSecureRandom(int bitsCount) async { |   static Future<SecretKey> fromSecureRandom(int bitsCount) async { | ||||||
|  |     Uint8List? key; | ||||||
|     try { |     try { | ||||||
|       final Uint8List? _key = await platform.generateSecretKey(bitsCount); |       key = await platform.generateSecretKey(bitsCount); | ||||||
| 
 |  | ||||||
|       if (_key == null || _key.isEmpty) { |  | ||||||
|         throw const KeyException( |  | ||||||
|           message: 'Could not generate secret key, platform returned null', |  | ||||||
|           code: 'platform_returned_null', |  | ||||||
|         ); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       return SecretKey(_key); |  | ||||||
|     } catch (e, s) { |     } catch (e, s) { | ||||||
|       if (e is KeyException) { |       throw NativeCryptoException( | ||||||
|         rethrow; |  | ||||||
|       } |  | ||||||
|       throw KeyException( |  | ||||||
|         message: '$e', |         message: '$e', | ||||||
|         code: 'failed_to_generate_secret_key', |         code: NativeCryptoExceptionCode.platform_throws.code, | ||||||
|         stackTrace: s, |         stackTrace: s, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  |     if (key.isNull) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Failed to generate a secret key! Platform returned null.', | ||||||
|  |         code: NativeCryptoExceptionCode.platform_returned_null.code, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (key!.isEmpty) { | ||||||
|  |       throw NativeCryptoException( | ||||||
|  |         message: 'Failed to generate a secret key! ' | ||||||
|  |             'Platform returned no data.', | ||||||
|  |         code: NativeCryptoExceptionCode.platform_returned_empty_data.code, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return SecretKey(key); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,9 +3,9 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: cipher_algorithm.dart | // File: cipher_algorithm.dart | ||||||
| // Created Date: 23/05/2022 22:07:54 | // Created Date: 23/05/2022 22:07:54 | ||||||
| // Last Modified: 23/05/2022 22:33:56 | // Last Modified: 26/05/2022 18:52:32 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| /// Represents different cipher algorithms | /// Represents different cipher algorithms | ||||||
| enum CipherAlgorithm { aes, rsa } | enum CipherAlgorithm { aes } | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: extensions.dart | // File: extensions.dart | ||||||
| // Created Date: 26/05/2022 12:12:48 | // Created Date: 26/05/2022 12:12:48 | ||||||
| // Last Modified: 26/05/2022 15:49:38 | // Last Modified: 26/05/2022 18:52:48 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| @ -30,8 +30,8 @@ extension ListIntX on List<int> { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| extension ListUint8ListX on List<Uint8List> { | extension ListUint8ListX on List<Uint8List> { | ||||||
|   /// Reduce a [List] of [Uint8List] to a [Uint8List]. |  | ||||||
|    |    | ||||||
|  |   /// Reduce a [List] of [Uint8List] to a [Uint8List]. | ||||||
|   Uint8List sum() { |   Uint8List sum() { | ||||||
|     for (var i = 1; i < length; i++) { |     for (var i = 1; i < length; i++) { | ||||||
|       first.addAll(this[i]); |       first.addAll(this[i]); | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: hash_algorithm.dart | // File: hash_algorithm.dart | ||||||
| // Created Date: 23/05/2022 22:01:59 | // Created Date: 23/05/2022 22:01:59 | ||||||
| // Last Modified: 23/05/2022 22:47:08 | // Last Modified: 26/05/2022 18:53:38 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| @ -11,12 +11,13 @@ import 'dart:typed_data'; | |||||||
| 
 | 
 | ||||||
| import 'package:native_crypto/src/platform.dart'; | import 'package:native_crypto/src/platform.dart'; | ||||||
| 
 | 
 | ||||||
|  | /// Defines the hash algorithms. | ||||||
| enum HashAlgorithm { | enum HashAlgorithm { | ||||||
|   sha256, |   sha256, | ||||||
|   sha384, |   sha384, | ||||||
|   sha512; |   sha512; | ||||||
|    |    | ||||||
|   /// Hashes a message |   /// Digest the [data] using this [HashAlgorithm]. | ||||||
|   Future<Uint8List> digest(Uint8List data) async { |   Future<Uint8List> digest(Uint8List data) async { | ||||||
|     final Uint8List hash = (await platform.digest(data, name)) ?? Uint8List(0); |     final Uint8List hash = (await platform.digest(data, name)) ?? Uint8List(0); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,8 +3,9 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: kdf_algorithm.dart | // File: kdf_algorithm.dart | ||||||
| // Created Date: 23/05/2022 22:36:24 | // Created Date: 23/05/2022 22:36:24 | ||||||
| // Last Modified: 23/05/2022 22:36:36 | // Last Modified: 26/05/2022 18:53:50 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
|  | /// Represents different key derivation functions | ||||||
| enum KdfAlgorithm { pbkdf2 } | enum KdfAlgorithm { pbkdf2 } | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| // ----- | // ----- | ||||||
| // File: secret_key_test.dart | // File: secret_key_test.dart | ||||||
| // Created Date: 26/05/2022 10:52:41 | // Created Date: 26/05/2022 10:52:41 | ||||||
| // Last Modified: 26/05/2022 12:07:33 | // Last Modified: 26/05/2022 19:24:44 | ||||||
| // ----- | // ----- | ||||||
| // Copyright (c) 2022 | // Copyright (c) 2022 | ||||||
| 
 | 
 | ||||||
| @ -42,10 +42,10 @@ void main() { | |||||||
|       await expectLater( |       await expectLater( | ||||||
|         () => SecretKey.fromSecureRandom(5), |         () => SecretKey.fromSecureRandom(5), | ||||||
|         throwsA( |         throwsA( | ||||||
|           isA<KeyException>().having( |           isA<NativeCryptoException>().having( | ||||||
|             (e) => e.code, |             (e) => e.code, | ||||||
|             'code', |             'code', | ||||||
|             'platform_returned_null', |             'platform_returned_empty_data', | ||||||
|           ), |           ), | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
| @ -59,7 +59,7 @@ void main() { | |||||||
|       await expectLater( |       await expectLater( | ||||||
|         () => SecretKey.fromSecureRandom(5), |         () => SecretKey.fromSecureRandom(5), | ||||||
|         throwsA( |         throwsA( | ||||||
|           isA<KeyException>().having( |           isA<NativeCryptoException>().having( | ||||||
|             (e) => e.code, |             (e) => e.code, | ||||||
|             'code', |             'code', | ||||||
|             'platform_returned_null', |             'platform_returned_null', | ||||||
| @ -81,7 +81,7 @@ void main() { | |||||||
|       await expectLater( |       await expectLater( | ||||||
|         () => SecretKey.fromSecureRandom(5), |         () => SecretKey.fromSecureRandom(5), | ||||||
|         throwsA( |         throwsA( | ||||||
|           isA<KeyException>() |           isA<NativeCryptoException>() | ||||||
|               .having( |               .having( | ||||||
|                 (e) => e.message, |                 (e) => e.message, | ||||||
|                 'message', |                 'message', | ||||||
| @ -90,7 +90,7 @@ void main() { | |||||||
|               .having( |               .having( | ||||||
|                 (e) => e.code, |                 (e) => e.code, | ||||||
|                 'code', |                 'code', | ||||||
|                 'failed_to_generate_secret_key', |                 'platform_throws', | ||||||
|               ), |               ), | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user