Compare commits
	
		
			No commits in common. "cf4227fb582a7487194dc51d4631919c296790a8" and "96f9aad1b314bb749c1cf1997f41b09c949fe797" have entirely different histories.
		
	
	
		
			cf4227fb58
			...
			96f9aad1b3
		
	
		
							
								
								
									
										13
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								.drone.yml
									
									
									
									
									
								
							@ -1,13 +0,0 @@
 | 
				
			|||||||
kind: pipeline
 | 
					 | 
				
			||||||
type: docker
 | 
					 | 
				
			||||||
name: default
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
steps:
 | 
					 | 
				
			||||||
- name: test:all
 | 
					 | 
				
			||||||
  image: cirrusci/flutter:stable
 | 
					 | 
				
			||||||
  commands:
 | 
					 | 
				
			||||||
    - PATH=$PATH:$HOME/.pub-cache/bin
 | 
					 | 
				
			||||||
    - flutter doctor
 | 
					 | 
				
			||||||
    - flutter pub global activate melos
 | 
					 | 
				
			||||||
    - melos bs
 | 
					 | 
				
			||||||
    - melos run test:all
 | 
					 | 
				
			||||||
							
								
								
									
										18
									
								
								melos.yaml
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								melos.yaml
									
									
									
									
									
								
							@ -16,24 +16,6 @@ scripts:
 | 
				
			|||||||
    run: melos run analyze && melos run format
 | 
					    run: melos run analyze && melos run format
 | 
				
			||||||
    description: Run all static analysis checks.
 | 
					    description: Run all static analysis checks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  test:all:
 | 
					 | 
				
			||||||
    run: |
 | 
					 | 
				
			||||||
      melos run test --no-select
 | 
					 | 
				
			||||||
    description: |
 | 
					 | 
				
			||||||
      Run all tests available.
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  test:
 | 
					 | 
				
			||||||
    run: |
 | 
					 | 
				
			||||||
      melos exec -c 6 --fail-fast -- \
 | 
					 | 
				
			||||||
        "flutter test --no-pub --no-test-assets"
 | 
					 | 
				
			||||||
    description: Run `flutter test` for a specific package.
 | 
					 | 
				
			||||||
    select-package:
 | 
					 | 
				
			||||||
      dir-exists:
 | 
					 | 
				
			||||||
        - test
 | 
					 | 
				
			||||||
      ignore:
 | 
					 | 
				
			||||||
        - "*web*"
 | 
					 | 
				
			||||||
        - "*example*"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  analyze:
 | 
					  analyze:
 | 
				
			||||||
    run: |
 | 
					    run: |
 | 
				
			||||||
      melos exec -c 10 -- \
 | 
					      melos exec -c 10 -- \
 | 
				
			||||||
 | 
				
			|||||||
@ -91,8 +91,8 @@ class BenchmarkPage extends ConsumerWidget {
 | 
				
			|||||||
        after = DateTime.now();
 | 
					        after = DateTime.now();
 | 
				
			||||||
        benchmark =
 | 
					        benchmark =
 | 
				
			||||||
            after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
 | 
					            after.millisecondsSinceEpoch - before.millisecondsSinceEpoch;
 | 
				
			||||||
        benchmarkStatus.appendln(
 | 
					        benchmarkStatus
 | 
				
			||||||
            '[Benchmark] ${size}MiB => Decryption took $benchmark ms');
 | 
					            .appendln('[Benchmark] ${size}MiB => Decryption took $benchmark ms');
 | 
				
			||||||
        csvLine.write(';$benchmark');
 | 
					        csvLine.write(';$benchmark');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      csv += csvLine.toString() + '\n';
 | 
					      csv += csvLine.toString() + '\n';
 | 
				
			||||||
 | 
				
			|||||||
@ -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: 27/05/2022 16:42:10
 | 
					// Last Modified: 26/05/2022 20:39:37
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2021
 | 
					// Copyright (c) 2021
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,8 +26,7 @@ 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';
 | 
				
			||||||
    ..text = 'PlainText';
 | 
					 | 
				
			||||||
  CipherTextWrapper? cipherText;
 | 
					  CipherTextWrapper? cipherText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<void> _encrypt(WidgetRef ref, Cipher cipher) async {
 | 
					  Future<void> _encrypt(WidgetRef ref, Cipher cipher) async {
 | 
				
			||||||
@ -59,8 +58,9 @@ class CipherPage extends ConsumerWidget {
 | 
				
			|||||||
      // Recreate cipher text with altered data
 | 
					      // Recreate cipher text with altered data
 | 
				
			||||||
      cipherText = CipherTextWrapper.fromBytes(
 | 
					      cipherText = CipherTextWrapper.fromBytes(
 | 
				
			||||||
        _altered,
 | 
					        _altered,
 | 
				
			||||||
        ivLength: AESMode.gcm.ivLength,
 | 
					        12,
 | 
				
			||||||
        tagLength: AESMode.gcm.tagLength,
 | 
					        _altered.length - 28,
 | 
				
			||||||
 | 
					        16,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      encryptionStatus.print('String successfully encrypted:\n');
 | 
					      encryptionStatus.print('String successfully encrypted:\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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: 26/05/2022 21:09:47
 | 
					// Last Modified: 26/05/2022 20:30:31
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2021
 | 
					// Copyright (c) 2021
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,10 +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';
 | 
				
			||||||
    ..text = 'Password';
 | 
					  final TextEditingController _messageTextController = TextEditingController()..text = 'Message';
 | 
				
			||||||
  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;
 | 
				
			||||||
@ -52,11 +50,7 @@ class KdfPage extends ConsumerWidget {
 | 
				
			|||||||
    if (password.isEmpty) {
 | 
					    if (password.isEmpty) {
 | 
				
			||||||
      pbkdf2Status.print('Password is empty');
 | 
					      pbkdf2Status.print('Password is empty');
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      Pbkdf2 _pbkdf2 = Pbkdf2(
 | 
					      Pbkdf2 _pbkdf2 = Pbkdf2(32, 1000, algorithm: HashAlgorithm.sha512);
 | 
				
			||||||
        keyBytesCount: 32,
 | 
					 | 
				
			||||||
        iterations: 1000,
 | 
					 | 
				
			||||||
        algorithm: HashAlgorithm.sha512,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      SecretKey sk = await _pbkdf2.derive(password: password, salt: 'salt');
 | 
					      SecretKey sk = await _pbkdf2.derive(password: password, salt: 'salt');
 | 
				
			||||||
      state.setKey(sk);
 | 
					      state.setKey(sk);
 | 
				
			||||||
      pbkdf2Status.print('Key successfully derived.');
 | 
					      pbkdf2Status.print('Key successfully derived.');
 | 
				
			||||||
 | 
				
			|||||||
@ -3,12 +3,10 @@
 | 
				
			|||||||
// -----
 | 
					// -----
 | 
				
			||||||
// File: aes_gcm.dart
 | 
					// File: aes_gcm.dart
 | 
				
			||||||
// Created Date: 24/05/2022 16:34:54
 | 
					// Created Date: 24/05/2022 16:34:54
 | 
				
			||||||
// Last Modified: 27/05/2022 17:36:31
 | 
					// Last Modified: 24/05/2022 17:15:22
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2022
 | 
					// Copyright (c) 2022
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ignore_for_file: implementation_imports
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import 'dart:typed_data';
 | 
					import 'dart:typed_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:pointycastle/export.dart';
 | 
					import 'package:pointycastle/export.dart';
 | 
				
			||||||
 | 
				
			|||||||
@ -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: 27/05/2022 12:13:28
 | 
					// Last Modified: 26/05/2022 19:43:22
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2022
 | 
					// Copyright (c) 2022
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -21,10 +21,6 @@ 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';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export 'aes_key_size.dart';
 | 
					 | 
				
			||||||
export 'aes_mode.dart';
 | 
					 | 
				
			||||||
export 'aes_padding.dart';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// An AES cipher.
 | 
					/// An AES cipher.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// [AES] is a [Cipher] that can be used to encrypt or decrypt data.
 | 
					/// [AES] is a [Cipher] that can be used to encrypt or decrypt data.
 | 
				
			||||||
@ -46,6 +42,7 @@ class AES implements Cipher {
 | 
				
			|||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!mode.supportedPaddings.contains(padding)) {
 | 
					    if (!mode.supportedPaddings.contains(padding)) {
 | 
				
			||||||
      throw NativeCryptoException(
 | 
					      throw NativeCryptoException(
 | 
				
			||||||
        message: 'Invalid padding! '
 | 
					        message: 'Invalid padding! '
 | 
				
			||||||
@ -55,25 +52,13 @@ class AES implements Cipher {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<Uint8List> _decrypt(
 | 
					  Future<Uint8List> _decrypt(CipherText cipherText,
 | 
				
			||||||
    CipherText cipherText, {
 | 
					      {int chunkCount = 0,}) async {
 | 
				
			||||||
    int chunkCount = 0,
 | 
					    final Uint8List? decrypted = await platform.decrypt(
 | 
				
			||||||
  }) async {
 | 
					      cipherText.bytes,
 | 
				
			||||||
    Uint8List? decrypted;
 | 
					      _key.bytes,
 | 
				
			||||||
 | 
					      algorithm.name,
 | 
				
			||||||
    try {
 | 
					    );
 | 
				
			||||||
      decrypted = await platform.decrypt(
 | 
					 | 
				
			||||||
        cipherText.bytes,
 | 
					 | 
				
			||||||
        _key.bytes,
 | 
					 | 
				
			||||||
        algorithm.name,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    } catch (e, s) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: '$e',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.platform_throws.code,
 | 
					 | 
				
			||||||
        stackTrace: s,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (decrypted.isNull) {
 | 
					    if (decrypted.isNull) {
 | 
				
			||||||
      throw NativeCryptoException(
 | 
					      throw NativeCryptoException(
 | 
				
			||||||
@ -91,21 +76,11 @@ class AES implements Cipher {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<CipherText> _encrypt(Uint8List data, {int chunkCount = 0}) async {
 | 
					  Future<CipherText> _encrypt(Uint8List data, {int chunkCount = 0}) async {
 | 
				
			||||||
    Uint8List? encrypted;
 | 
					    final Uint8List? encrypted = await platform.encrypt(
 | 
				
			||||||
 | 
					      data,
 | 
				
			||||||
    try {
 | 
					      _key.bytes,
 | 
				
			||||||
      encrypted = await platform.encrypt(
 | 
					      algorithm.name,
 | 
				
			||||||
        data,
 | 
					    );
 | 
				
			||||||
        _key.bytes,
 | 
					 | 
				
			||||||
        algorithm.name,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    } catch (e, s) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: '$e on chunk #$chunkCount',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.platform_throws.code,
 | 
					 | 
				
			||||||
        stackTrace: s,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (encrypted.isNull) {
 | 
					    if (encrypted.isNull) {
 | 
				
			||||||
      throw NativeCryptoException(
 | 
					      throw NativeCryptoException(
 | 
				
			||||||
@ -118,21 +93,13 @@ class AES implements Cipher {
 | 
				
			|||||||
        code: NativeCryptoExceptionCode.platform_returned_empty_data.code,
 | 
					        code: NativeCryptoExceptionCode.platform_returned_empty_data.code,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      try {
 | 
					      return CipherText.fromBytes(
 | 
				
			||||||
        return CipherText.fromBytes(
 | 
					        12,
 | 
				
			||||||
          encrypted,
 | 
					        encrypted.length - 28,
 | 
				
			||||||
          ivLength: 12,
 | 
					        16,
 | 
				
			||||||
          messageLength: encrypted.length - 28,
 | 
					        CipherAlgorithm.aes,
 | 
				
			||||||
          tagLength: 16,
 | 
					        encrypted,
 | 
				
			||||||
          cipherAlgorithm: CipherAlgorithm.aes,
 | 
					      );
 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
      } on NativeCryptoException catch (e, s) {
 | 
					 | 
				
			||||||
        throw NativeCryptoException(
 | 
					 | 
				
			||||||
          message: '${e.message} on chunk #$chunkCount',
 | 
					 | 
				
			||||||
          code: e.code,
 | 
					 | 
				
			||||||
          stackTrace: s,
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -154,9 +121,6 @@ class AES implements Cipher {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Future<CipherTextWrapper> encrypt(Uint8List data) async {
 | 
					  Future<CipherTextWrapper> encrypt(Uint8List data) async {
 | 
				
			||||||
    if (data.isEmpty) {
 | 
					 | 
				
			||||||
      return CipherTextWrapper.empty();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    CipherTextWrapper cipherTextWrapper;
 | 
					    CipherTextWrapper cipherTextWrapper;
 | 
				
			||||||
    Uint8List dataToEncrypt;
 | 
					    Uint8List dataToEncrypt;
 | 
				
			||||||
    final int chunkNb = (data.length / Cipher.bytesCountPerChunk).ceil();
 | 
					    final int chunkNb = (data.length / Cipher.bytesCountPerChunk).ceil();
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
// -----
 | 
					// -----
 | 
				
			||||||
// 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: 26/05/2022 21:03:26
 | 
					// Last Modified: 26/05/2022 18:41:31
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2022
 | 
					// Copyright (c) 2022
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -11,20 +11,10 @@ import 'package:native_crypto/src/ciphers/aes/aes_padding.dart';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// Defines the AES modes of operation.
 | 
					/// Defines the AES modes of operation.
 | 
				
			||||||
enum AESMode {
 | 
					enum AESMode {
 | 
				
			||||||
  gcm([AESPadding.none], 12, 16);
 | 
					  gcm([AESPadding.none]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Returns the list of supported [AESPadding] for this [AESMode].
 | 
					  /// Returns the list of supported [AESPadding] for this [AESMode].
 | 
				
			||||||
  final List<AESPadding> supportedPaddings;
 | 
					  final List<AESPadding> supportedPaddings;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Returns the default IV length for this [AESMode].
 | 
					  const AESMode(this.supportedPaddings);
 | 
				
			||||||
  final int ivLength;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Returns the default tag length for this [AESMode].
 | 
					 | 
				
			||||||
  final int tagLength;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const AESMode(
 | 
					 | 
				
			||||||
    this.supportedPaddings, [
 | 
					 | 
				
			||||||
    this.ivLength = 16,
 | 
					 | 
				
			||||||
    this.tagLength = 0,
 | 
					 | 
				
			||||||
  ]);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
// -----
 | 
					// -----
 | 
				
			||||||
// File: cipher_text.dart
 | 
					// File: cipher_text.dart
 | 
				
			||||||
// Created Date: 16/12/2021 16:59:53
 | 
					// Created Date: 16/12/2021 16:59:53
 | 
				
			||||||
// Last Modified: 27/05/2022 12:09:47
 | 
					// Last Modified: 26/05/2022 19:43:57
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2021
 | 
					// Copyright (c) 2021
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -45,35 +45,16 @@ class CipherText extends ByteArray {
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  factory CipherText.fromBytes(
 | 
					  factory CipherText.fromBytes(
 | 
				
			||||||
    Uint8List bytes, {
 | 
					    int ivLength,
 | 
				
			||||||
    required int ivLength,
 | 
					    int messageLength,
 | 
				
			||||||
    required int tagLength,
 | 
					    int tagLength,
 | 
				
			||||||
    int? messageLength,
 | 
					 | 
				
			||||||
    CipherAlgorithm? cipherAlgorithm,
 | 
					    CipherAlgorithm? cipherAlgorithm,
 | 
				
			||||||
  }) {
 | 
					    Uint8List bytes,
 | 
				
			||||||
    messageLength ??= bytes.length - ivLength - tagLength;
 | 
					  ) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (ivLength.isNegative ||
 | 
					 | 
				
			||||||
        messageLength.isNegative ||
 | 
					 | 
				
			||||||
        tagLength.isNegative) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: 'Invalid length! Must be positive.',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.invalid_argument.code,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (bytes.isEmpty) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: 'Passed data is empty!',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.invalid_argument.code,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (bytes.length != ivLength + messageLength + tagLength) {
 | 
					    if (bytes.length != ivLength + messageLength + tagLength) {
 | 
				
			||||||
      throw NativeCryptoException(
 | 
					      throw NativeCryptoException(
 | 
				
			||||||
        message: 'Invalid cipher text length! '
 | 
					        message: 'Invalid cipher text length! '
 | 
				
			||||||
            'Expected: ${ivLength + messageLength + tagLength} bytes '
 | 
					            'Expected: ${ivLength + messageLength + tagLength} bytes',
 | 
				
			||||||
            'got: ${bytes.length} bytes.',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.invalid_argument.code,
 | 
					        code: NativeCryptoExceptionCode.invalid_argument.code,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -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: 27/05/2022 13:43:29
 | 
					// Last Modified: 26/05/2022 20:32:38
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2022
 | 
					// Copyright (c) 2022
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,47 +49,39 @@ class CipherTextWrapper {
 | 
				
			|||||||
  /// [NativeCryptoExceptionCode.invalid_argument] if the [Uint8List] is
 | 
					  /// [NativeCryptoExceptionCode.invalid_argument] if the [Uint8List] is
 | 
				
			||||||
  /// not a valid [CipherText] or a [List] of [CipherText].
 | 
					  /// 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 tagLength,
 | 
					    int messageLength,
 | 
				
			||||||
 | 
					    int tagLength, {
 | 
				
			||||||
    CipherAlgorithm? cipherAlgorithm,
 | 
					    CipherAlgorithm? cipherAlgorithm,
 | 
				
			||||||
    int? chunkSize,
 | 
					    int? chunkSize,
 | 
				
			||||||
  }) {
 | 
					  }) {
 | 
				
			||||||
    chunkSize ??= Cipher.bytesCountPerChunk;
 | 
					    chunkSize ??= Cipher.bytesCountPerChunk;
 | 
				
			||||||
    Cipher.bytesCountPerChunk = chunkSize;
 | 
					    Cipher.bytesCountPerChunk = chunkSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final int messageLength = bytes.length - ivLength - tagLength;
 | 
					    if (bytes.length <= chunkSize) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (messageLength <= chunkSize) {
 | 
					 | 
				
			||||||
      return CipherTextWrapper.single(
 | 
					      return CipherTextWrapper.single(
 | 
				
			||||||
        CipherText.fromBytes(
 | 
					        CipherText.fromBytes(
 | 
				
			||||||
 | 
					          ivLength,
 | 
				
			||||||
 | 
					          messageLength,
 | 
				
			||||||
 | 
					          tagLength,
 | 
				
			||||||
 | 
					          cipherAlgorithm,
 | 
				
			||||||
          bytes,
 | 
					          bytes,
 | 
				
			||||||
          ivLength: ivLength,
 | 
					 | 
				
			||||||
          tagLength: tagLength,
 | 
					 | 
				
			||||||
          cipherAlgorithm: cipherAlgorithm,
 | 
					 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      final cipherTexts = <CipherText>[];
 | 
					      final cipherTexts = <CipherText>[];
 | 
				
			||||||
      for (var i = 0; i < bytes.length; i += chunkSize + ivLength + tagLength) {
 | 
					      for (var i = 0; i < bytes.length; i += chunkSize) {
 | 
				
			||||||
        final chunk = bytes.trySublist(i, i + chunkSize + ivLength + tagLength);
 | 
					        final chunk = bytes.sublist(i, i + chunkSize);
 | 
				
			||||||
 | 
					        cipherTexts.add(
 | 
				
			||||||
        try {
 | 
					          CipherText.fromBytes(
 | 
				
			||||||
          cipherTexts.add(
 | 
					            ivLength,
 | 
				
			||||||
            CipherText.fromBytes(
 | 
					            messageLength,
 | 
				
			||||||
              chunk,
 | 
					            tagLength,
 | 
				
			||||||
              ivLength: ivLength,
 | 
					            cipherAlgorithm,
 | 
				
			||||||
              tagLength: tagLength,
 | 
					            chunk,
 | 
				
			||||||
              cipherAlgorithm: cipherAlgorithm,
 | 
					          ),
 | 
				
			||||||
            ),
 | 
					        );
 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        } on NativeCryptoException catch (e, s) {
 | 
					 | 
				
			||||||
          throw NativeCryptoException(
 | 
					 | 
				
			||||||
            message: '${e.message} on chunk #$i',
 | 
					 | 
				
			||||||
            code: e.code,
 | 
					 | 
				
			||||||
            stackTrace: s,
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return CipherTextWrapper.list(cipherTexts);
 | 
					      return CipherTextWrapper.list(cipherTexts);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -136,7 +128,7 @@ class CipherTextWrapper {
 | 
				
			|||||||
    if (isSingle) {
 | 
					    if (isSingle) {
 | 
				
			||||||
      return single.bytes;
 | 
					      return single.bytes;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return list.map((cipherText) => cipherText.bytes).toList().combine();
 | 
					      return list.map((cipherText) => cipherText.bytes).toList().sum();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
// -----
 | 
					// -----
 | 
				
			||||||
// File: cipher.dart
 | 
					// File: cipher.dart
 | 
				
			||||||
// Created Date: 16/12/2021 16:28:00
 | 
					// Created Date: 16/12/2021 16:28:00
 | 
				
			||||||
// Last Modified: 26/05/2022 21:21:07
 | 
					// Last Modified: 26/05/2022 17:38:26
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2021
 | 
					// Copyright (c) 2021
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,11 +20,7 @@ import 'package:native_crypto/src/utils/cipher_algorithm.dart';
 | 
				
			|||||||
/// 
 | 
					/// 
 | 
				
			||||||
/// This interface is implemented by all the ciphers in NativeCrypto.
 | 
					/// This interface is implemented by all the ciphers in NativeCrypto.
 | 
				
			||||||
abstract class Cipher {
 | 
					abstract class Cipher {
 | 
				
			||||||
  static const int _bytesCountPerChunkDefault = 33554432;
 | 
					  static int _bytesCountPerChunk = 33554432;
 | 
				
			||||||
  static int _bytesCountPerChunk = _bytesCountPerChunkDefault;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Returns the default number of bytes per chunk.
 | 
					 | 
				
			||||||
  static int get defaultBytesCountPerChunk => _bytesCountPerChunkDefault;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Returns the size of a chunk of data 
 | 
					  /// Returns the size of a chunk of data 
 | 
				
			||||||
  /// that can be processed by the [Cipher].
 | 
					  /// that can be processed by the [Cipher].
 | 
				
			||||||
@ -39,6 +35,7 @@ abstract class Cipher {
 | 
				
			|||||||
  /// Returns the standard algorithm for this [Cipher].
 | 
					  /// Returns the standard algorithm for this [Cipher].
 | 
				
			||||||
  CipherAlgorithm get algorithm;
 | 
					  CipherAlgorithm get algorithm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Encrypts the [data].
 | 
					  /// Encrypts the [data].
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Takes [Uint8List] data as parameter.
 | 
					  /// Takes [Uint8List] data as parameter.
 | 
				
			||||||
 | 
				
			|||||||
@ -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: 26/05/2022 23:19:46
 | 
					// Last Modified: 26/05/2022 18:51:59
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2021
 | 
					// Copyright (c) 2021
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -29,64 +29,31 @@ class Pbkdf2 extends KeyDerivation {
 | 
				
			|||||||
  @override
 | 
					  @override
 | 
				
			||||||
  KdfAlgorithm get algorithm => KdfAlgorithm.pbkdf2;
 | 
					  KdfAlgorithm get algorithm => KdfAlgorithm.pbkdf2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Pbkdf2({
 | 
					  Pbkdf2(
 | 
				
			||||||
    required int keyBytesCount,
 | 
					    int keyBytesCount,
 | 
				
			||||||
    required int iterations,
 | 
					    int iterations, {
 | 
				
			||||||
    HashAlgorithm algorithm = HashAlgorithm.sha256,
 | 
					    HashAlgorithm algorithm = HashAlgorithm.sha256,
 | 
				
			||||||
  })  : _keyBytesCount = keyBytesCount,
 | 
					  })  : _keyBytesCount = keyBytesCount,
 | 
				
			||||||
        _iterations = iterations,
 | 
					        _iterations = iterations,
 | 
				
			||||||
        _hash = algorithm {
 | 
					        _hash = algorithm;
 | 
				
			||||||
    if (keyBytesCount < 0) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: 'keyBytesCount must be positive.',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.invalid_argument.code,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (iterations <= 0) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: 'iterations must be strictly positive.',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.invalid_argument.code,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Future<SecretKey> derive({String? password, String? salt}) async {
 | 
					  Future<SecretKey> derive({String? password, String? salt}) async {
 | 
				
			||||||
    Uint8List? derivation;
 | 
					    if (password == null || salt == null) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (_keyBytesCount == 0) {
 | 
					 | 
				
			||||||
      return SecretKey(Uint8List(0));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (password.isNull) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					      throw NativeCryptoException(
 | 
				
			||||||
        message: 'Password cannot be null.',
 | 
					        message: 'Password and salt cannot be null. '
 | 
				
			||||||
 | 
					            'Here is the password: $password, here is the salt: $salt',
 | 
				
			||||||
        code: NativeCryptoExceptionCode.invalid_argument.code,
 | 
					        code: NativeCryptoExceptionCode.invalid_argument.code,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (salt.isNull) {
 | 
					    final Uint8List? derivation = await platform.pbkdf2(
 | 
				
			||||||
      throw NativeCryptoException(
 | 
					      password,
 | 
				
			||||||
        message: 'Salt cannot be null.',
 | 
					      salt,
 | 
				
			||||||
        code: NativeCryptoExceptionCode.invalid_argument.code,
 | 
					      _keyBytesCount,
 | 
				
			||||||
      );
 | 
					      _iterations,
 | 
				
			||||||
    }
 | 
					      _hash.name,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      derivation = await platform.pbkdf2(
 | 
					 | 
				
			||||||
        password!,
 | 
					 | 
				
			||||||
        salt!,
 | 
					 | 
				
			||||||
        _keyBytesCount,
 | 
					 | 
				
			||||||
        _iterations,
 | 
					 | 
				
			||||||
        _hash.name,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    } catch (e, s) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: '$e',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.platform_throws.code,
 | 
					 | 
				
			||||||
        stackTrace: s,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (derivation.isNull) {
 | 
					    if (derivation.isNull) {
 | 
				
			||||||
      throw NativeCryptoException(
 | 
					      throw NativeCryptoException(
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
// -----
 | 
					// -----
 | 
				
			||||||
// 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 23:13:10
 | 
					// Last Modified: 26/05/2022 19:26:35
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2021
 | 
					// Copyright (c) 2021
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,10 +28,6 @@ class SecretKey extends BaseKey {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  static Future<SecretKey> fromSecureRandom(int bitsCount) async {
 | 
					  static Future<SecretKey> fromSecureRandom(int bitsCount) async {
 | 
				
			||||||
    Uint8List? key;
 | 
					    Uint8List? key;
 | 
				
			||||||
    if (bitsCount == 0) {
 | 
					 | 
				
			||||||
      return SecretKey(Uint8List(0));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      key = await platform.generateSecretKey(bitsCount);
 | 
					      key = await platform.generateSecretKey(bitsCount);
 | 
				
			||||||
    } catch (e, s) {
 | 
					    } catch (e, s) {
 | 
				
			||||||
 | 
				
			|||||||
@ -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: 27/05/2022 12:26:55
 | 
					// Last Modified: 26/05/2022 18:52:48
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2022
 | 
					// Copyright (c) 2022
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -30,10 +30,14 @@ 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 combine() {
 | 
					  Uint8List sum() {
 | 
				
			||||||
    if (isEmpty) return Uint8List(0);
 | 
					    for (var i = 1; i < length; i++) {
 | 
				
			||||||
    return reduce((value, element) => value.plus(element));
 | 
					      first.addAll(this[i]);
 | 
				
			||||||
 | 
					      removeAt(i);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return first;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -86,16 +90,6 @@ extension Uint8ListX on Uint8List {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Returns a concatenation of this with the other [Uint8List].
 | 
					  /// Returns a concatenation of this with the other [Uint8List].
 | 
				
			||||||
  Uint8List plus(final Uint8List other) => [...this, ...other].toTypedList();
 | 
					  Uint8List operator +(final Uint8List other) =>
 | 
				
			||||||
 | 
					      [...this, ...other].toTypedList();
 | 
				
			||||||
  /// Returns a sublist of this from the [start] index to the [end] index.
 | 
					 | 
				
			||||||
  /// If [end] is greater than the length of the list, it is set to the length
 | 
					 | 
				
			||||||
  Uint8List trySublist(int start, [int? end]) {
 | 
					 | 
				
			||||||
    if (isEmpty) return this;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    int ending = end ?? length;
 | 
					 | 
				
			||||||
    if (ending > length) ending = length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return sublist(start, ending);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,15 +3,13 @@
 | 
				
			|||||||
// -----
 | 
					// -----
 | 
				
			||||||
// 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: 26/05/2022 22:59:04
 | 
					// Last Modified: 26/05/2022 18:53:38
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2022
 | 
					// Copyright (c) 2022
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'dart:typed_data';
 | 
					import 'dart:typed_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Defines the hash algorithms.
 | 
					/// Defines the hash algorithms.
 | 
				
			||||||
enum HashAlgorithm {
 | 
					enum HashAlgorithm {
 | 
				
			||||||
@ -21,30 +19,7 @@ enum HashAlgorithm {
 | 
				
			|||||||
  
 | 
					  
 | 
				
			||||||
  /// Digest the [data] using this [HashAlgorithm].
 | 
					  /// Digest the [data] using this [HashAlgorithm].
 | 
				
			||||||
  Future<Uint8List> digest(Uint8List data) async {
 | 
					  Future<Uint8List> digest(Uint8List data) async {
 | 
				
			||||||
    Uint8List? hash;
 | 
					    final Uint8List hash = (await platform.digest(data, name)) ?? Uint8List(0);
 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      hash = await platform.digest(data, name);
 | 
					 | 
				
			||||||
    } catch (e, s) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: '$e',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.platform_throws.code,
 | 
					 | 
				
			||||||
        stackTrace: s,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (hash.isNull) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: 'Failed to digest data! Platform returned null.',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.platform_returned_null.code,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (hash!.isEmpty) {
 | 
					 | 
				
			||||||
      throw NativeCryptoException(
 | 
					 | 
				
			||||||
        message: 'Failed to digest data! Platform returned no data.',
 | 
					 | 
				
			||||||
        code: NativeCryptoExceptionCode.platform_returned_empty_data.code,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return hash;
 | 
					    return hash;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,323 +0,0 @@
 | 
				
			|||||||
// Author: Hugo Pointcheval
 | 
					 | 
				
			||||||
// Email: git@pcl.ovh
 | 
					 | 
				
			||||||
// -----
 | 
					 | 
				
			||||||
// File: aes_cipher_test.dart
 | 
					 | 
				
			||||||
// Created Date: 26/05/2022 23:20:53
 | 
					 | 
				
			||||||
// Last Modified: 27/05/2022 16:39:44
 | 
					 | 
				
			||||||
// -----
 | 
					 | 
				
			||||||
// Copyright (c) 2022
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import 'dart:typed_data';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import 'package:flutter/services.dart';
 | 
					 | 
				
			||||||
import 'package:flutter_test/flutter_test.dart';
 | 
					 | 
				
			||||||
import 'package:native_crypto/native_crypto.dart';
 | 
					 | 
				
			||||||
import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import '../mocks/mock_native_crypto_platform.dart';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void main() {
 | 
					 | 
				
			||||||
  final MockNativeCryptoPlatform mock = MockNativeCryptoPlatform();
 | 
					 | 
				
			||||||
  NativeCryptoPlatform.instance = mock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setUp(() {
 | 
					 | 
				
			||||||
    Cipher.bytesCountPerChunk = Cipher.defaultBytesCountPerChunk;
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  group('Constructor', () {
 | 
					 | 
				
			||||||
    test('throws on invalid key length', () {
 | 
					 | 
				
			||||||
      expect(
 | 
					 | 
				
			||||||
        () => AES(SecretKey(Uint8List(0))),
 | 
					 | 
				
			||||||
        throwsA(
 | 
					 | 
				
			||||||
          isA<NativeCryptoException>()
 | 
					 | 
				
			||||||
              .having(
 | 
					 | 
				
			||||||
                (e) => e.code,
 | 
					 | 
				
			||||||
                'code',
 | 
					 | 
				
			||||||
                'invalid_key_length',
 | 
					 | 
				
			||||||
              )
 | 
					 | 
				
			||||||
              .having(
 | 
					 | 
				
			||||||
                (e) => e.message,
 | 
					 | 
				
			||||||
                'message',
 | 
					 | 
				
			||||||
                contains('Invalid key'),
 | 
					 | 
				
			||||||
              ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('creates a valid instance', () {
 | 
					 | 
				
			||||||
      expect(
 | 
					 | 
				
			||||||
        AES(
 | 
					 | 
				
			||||||
          SecretKey(Uint8List(16)),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        isA<AES>(),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  group('encrypt', () {
 | 
					 | 
				
			||||||
    test('returns a valid cipher text wrapper', () async {
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setEncryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(16),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(() => Uint8List(16 + 28));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(
 | 
					 | 
				
			||||||
        await aes.encrypt(Uint8List(16)),
 | 
					 | 
				
			||||||
        isA<CipherTextWrapper>().having((e) => e.isSingle, 'is single', isTrue),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('returns a valid cipher text with multiple chunks', () async {
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setEncryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(16),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(() => Uint8List(16 + 28)); // Returns 1 encrypted chunk
 | 
					 | 
				
			||||||
      Cipher.bytesCountPerChunk = 16;
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(
 | 
					 | 
				
			||||||
        await aes.encrypt(Uint8List(16 * 3)),
 | 
					 | 
				
			||||||
        isA<CipherTextWrapper>().having((e) => e.isList, 'is list', isTrue),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('handles returning empty list', () async {
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setEncryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(16),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(() => Uint8List(0));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expectLater(
 | 
					 | 
				
			||||||
        () => aes.encrypt(Uint8List(16)),
 | 
					 | 
				
			||||||
        throwsA(
 | 
					 | 
				
			||||||
          isA<NativeCryptoException>().having(
 | 
					 | 
				
			||||||
            (e) => e.code,
 | 
					 | 
				
			||||||
            'code',
 | 
					 | 
				
			||||||
            'platform_returned_empty_data',
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('handles returning null', () async {
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setEncryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(16),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(() => null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expectLater(
 | 
					 | 
				
			||||||
        () => aes.encrypt(Uint8List(16)),
 | 
					 | 
				
			||||||
        throwsA(
 | 
					 | 
				
			||||||
          isA<NativeCryptoException>().having(
 | 
					 | 
				
			||||||
            (e) => e.code,
 | 
					 | 
				
			||||||
            'code',
 | 
					 | 
				
			||||||
            'platform_returned_null',
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('handles throwing PlatformException', () async {
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setEncryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(16),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(
 | 
					 | 
				
			||||||
          () => throw PlatformException(
 | 
					 | 
				
			||||||
            code: 'native_crypto',
 | 
					 | 
				
			||||||
            message: 'dummy error',
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expectLater(
 | 
					 | 
				
			||||||
        () => aes.encrypt(Uint8List(16)),
 | 
					 | 
				
			||||||
        throwsA(
 | 
					 | 
				
			||||||
          isA<NativeCryptoException>()
 | 
					 | 
				
			||||||
              .having(
 | 
					 | 
				
			||||||
                (e) => e.message,
 | 
					 | 
				
			||||||
                'message',
 | 
					 | 
				
			||||||
                contains(
 | 
					 | 
				
			||||||
                  'PlatformException(native_crypto, dummy error, null, null)',
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
              )
 | 
					 | 
				
			||||||
              .having(
 | 
					 | 
				
			||||||
                (e) => e.code,
 | 
					 | 
				
			||||||
                'code',
 | 
					 | 
				
			||||||
                'platform_throws',
 | 
					 | 
				
			||||||
              ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  group('decrypt', () {
 | 
					 | 
				
			||||||
    test('returns a valid Uint8List', () async {
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setDecryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(16 + 28),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(() => Uint8List(16));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
      final bytes = Uint8List(16 + 28);
 | 
					 | 
				
			||||||
      final wrapper = CipherTextWrapper.fromBytes(
 | 
					 | 
				
			||||||
        bytes,
 | 
					 | 
				
			||||||
        ivLength: 12,
 | 
					 | 
				
			||||||
        tagLength: 16,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(
 | 
					 | 
				
			||||||
        await aes.decrypt(wrapper),
 | 
					 | 
				
			||||||
        isA<Uint8List>().having((e) => e.length, 'length', 16),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('returns a valid Uint8List on decrypting multiple chunks', () async {
 | 
					 | 
				
			||||||
      const int chunkSize = 8;
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setDecryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(chunkSize + 28),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(() => Uint8List(chunkSize));
 | 
					 | 
				
			||||||
      Cipher.bytesCountPerChunk = chunkSize;
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
      final bytes = Uint8List((chunkSize + 28) * 3);
 | 
					 | 
				
			||||||
      final wrapper = CipherTextWrapper.fromBytes(
 | 
					 | 
				
			||||||
        bytes,
 | 
					 | 
				
			||||||
        ivLength: 12,
 | 
					 | 
				
			||||||
        tagLength: 16,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(
 | 
					 | 
				
			||||||
        await aes.decrypt(wrapper),
 | 
					 | 
				
			||||||
        isA<Uint8List>().having((e) => e.length, 'length', chunkSize * 3),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('handles returning empty list', () async {
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setDecryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(16 + 28),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(() => Uint8List(0));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
      final bytes = Uint8List(16 + 28);
 | 
					 | 
				
			||||||
      final wrapper = CipherTextWrapper.fromBytes(
 | 
					 | 
				
			||||||
        bytes,
 | 
					 | 
				
			||||||
        ivLength: 12,
 | 
					 | 
				
			||||||
        tagLength: 16,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expectLater(
 | 
					 | 
				
			||||||
        () => aes.decrypt(wrapper),
 | 
					 | 
				
			||||||
        throwsA(
 | 
					 | 
				
			||||||
          isA<NativeCryptoException>().having(
 | 
					 | 
				
			||||||
            (e) => e.code,
 | 
					 | 
				
			||||||
            'code',
 | 
					 | 
				
			||||||
            'platform_returned_empty_data',
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('handles returning null', () async {
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setDecryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(16 + 28),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(() => null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
      final bytes = Uint8List(16 + 28);
 | 
					 | 
				
			||||||
      final wrapper = CipherTextWrapper.fromBytes(
 | 
					 | 
				
			||||||
        bytes,
 | 
					 | 
				
			||||||
        ivLength: 12,
 | 
					 | 
				
			||||||
        tagLength: 16,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expectLater(
 | 
					 | 
				
			||||||
        () => aes.decrypt(wrapper),
 | 
					 | 
				
			||||||
        throwsA(
 | 
					 | 
				
			||||||
          isA<NativeCryptoException>().having(
 | 
					 | 
				
			||||||
            (e) => e.code,
 | 
					 | 
				
			||||||
            'code',
 | 
					 | 
				
			||||||
            'platform_returned_null',
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('handles throwing PlatformException', () async {
 | 
					 | 
				
			||||||
      mock
 | 
					 | 
				
			||||||
        ..setDecryptExpectations(
 | 
					 | 
				
			||||||
          data: Uint8List(16 + 28),
 | 
					 | 
				
			||||||
          key: Uint8List(16),
 | 
					 | 
				
			||||||
          algorithm: 'aes',
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        ..setResponse(
 | 
					 | 
				
			||||||
          () => throw PlatformException(
 | 
					 | 
				
			||||||
            code: 'native_crypto',
 | 
					 | 
				
			||||||
            message: 'dummy error',
 | 
					 | 
				
			||||||
          ),
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      final aes = AES(SecretKey(Uint8List(16)));
 | 
					 | 
				
			||||||
      final bytes = Uint8List(16 + 28);
 | 
					 | 
				
			||||||
      final wrapper = CipherTextWrapper.fromBytes(
 | 
					 | 
				
			||||||
        bytes,
 | 
					 | 
				
			||||||
        ivLength: 12,
 | 
					 | 
				
			||||||
        tagLength: 16,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expectLater(
 | 
					 | 
				
			||||||
        () => aes.decrypt(wrapper),
 | 
					 | 
				
			||||||
        throwsA(
 | 
					 | 
				
			||||||
          isA<NativeCryptoException>()
 | 
					 | 
				
			||||||
              .having(
 | 
					 | 
				
			||||||
                (e) => e.message,
 | 
					 | 
				
			||||||
                'message',
 | 
					 | 
				
			||||||
                contains(
 | 
					 | 
				
			||||||
                  'PlatformException(native_crypto, dummy error, null, null)',
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
              )
 | 
					 | 
				
			||||||
              .having(
 | 
					 | 
				
			||||||
                (e) => e.code,
 | 
					 | 
				
			||||||
                'code',
 | 
					 | 
				
			||||||
                'platform_throws',
 | 
					 | 
				
			||||||
              ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -3,7 +3,7 @@
 | 
				
			|||||||
// -----
 | 
					// -----
 | 
				
			||||||
// File: cipher_text_wrapper_test.dart
 | 
					// File: cipher_text_wrapper_test.dart
 | 
				
			||||||
// Created Date: 26/05/2022 21:35:41
 | 
					// Created Date: 26/05/2022 21:35:41
 | 
				
			||||||
// Last Modified: 27/05/2022 13:46:54
 | 
					// Last Modified: 26/05/2022 22:27:31
 | 
				
			||||||
// -----
 | 
					// -----
 | 
				
			||||||
// Copyright (c) 2022
 | 
					// Copyright (c) 2022
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -293,6 +293,7 @@ void main() {
 | 
				
			|||||||
      final wrapper = CipherTextWrapper.fromBytes(
 | 
					      final wrapper = CipherTextWrapper.fromBytes(
 | 
				
			||||||
        Uint8List.fromList([1, 2, 3]),
 | 
					        Uint8List.fromList([1, 2, 3]),
 | 
				
			||||||
        ivLength: 1,
 | 
					        ivLength: 1,
 | 
				
			||||||
 | 
					        messageLength: 1,
 | 
				
			||||||
        tagLength: 1,
 | 
					        tagLength: 1,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      expect(wrapper.isSingle, isTrue);
 | 
					      expect(wrapper.isSingle, isTrue);
 | 
				
			||||||
@ -300,10 +301,11 @@ void main() {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    test('creates list from bytes when too big', () {
 | 
					    test('creates list from bytes when too big', () {
 | 
				
			||||||
      Cipher.bytesCountPerChunk = 1;
 | 
					      Cipher.bytesCountPerChunk = 3;
 | 
				
			||||||
      final wrapper = CipherTextWrapper.fromBytes(
 | 
					      final wrapper = CipherTextWrapper.fromBytes(
 | 
				
			||||||
        Uint8List.fromList([1, 2, 3, 4, 5, 6]),
 | 
					        Uint8List.fromList([1, 2, 3, 4, 5, 6]),
 | 
				
			||||||
        ivLength: 1,
 | 
					        ivLength: 1,
 | 
				
			||||||
 | 
					        messageLength: 1,
 | 
				
			||||||
        tagLength: 1,
 | 
					        tagLength: 1,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      expect(wrapper.isList, isTrue);
 | 
					      expect(wrapper.isList, isTrue);
 | 
				
			||||||
@ -315,35 +317,11 @@ void main() {
 | 
				
			|||||||
      CipherTextWrapper.fromBytes(
 | 
					      CipherTextWrapper.fromBytes(
 | 
				
			||||||
        Uint8List.fromList([1, 2, 3]),
 | 
					        Uint8List.fromList([1, 2, 3]),
 | 
				
			||||||
        ivLength: 1,
 | 
					        ivLength: 1,
 | 
				
			||||||
 | 
					        messageLength: 1,
 | 
				
			||||||
        tagLength: 1,
 | 
					        tagLength: 1,
 | 
				
			||||||
        chunkSize: 3,
 | 
					        chunkSize: 3,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
      expect(Cipher.bytesCountPerChunk, 3);
 | 
					      expect(Cipher.bytesCountPerChunk, 3);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					 | 
				
			||||||
    test('throws if trying to build list with bad parameters', () {
 | 
					 | 
				
			||||||
      Cipher.bytesCountPerChunk = 1; // length of a message
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(
 | 
					 | 
				
			||||||
        () => CipherTextWrapper.fromBytes(
 | 
					 | 
				
			||||||
          Uint8List.fromList([1, 2, 3, 4, 5, 6]),
 | 
					 | 
				
			||||||
          ivLength: 2,
 | 
					 | 
				
			||||||
          tagLength: 1,
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
        throwsA(
 | 
					 | 
				
			||||||
          isA<NativeCryptoException>()
 | 
					 | 
				
			||||||
              .having(
 | 
					 | 
				
			||||||
                (e) => e.code,
 | 
					 | 
				
			||||||
                'code',
 | 
					 | 
				
			||||||
                'invalid_argument',
 | 
					 | 
				
			||||||
              )
 | 
					 | 
				
			||||||
              .having(
 | 
					 | 
				
			||||||
                (e) => e.message,
 | 
					 | 
				
			||||||
                'message',
 | 
					 | 
				
			||||||
                contains('on chunk #'),
 | 
					 | 
				
			||||||
              ),
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -171,5 +171,6 @@ void main() {
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user