diff --git a/packages/native_crypto/test/src/aes_cipher_test.dart b/packages/native_crypto/test/src/aes_cipher_test.dart new file mode 100644 index 0000000..f1d0f39 --- /dev/null +++ b/packages/native_crypto/test/src/aes_cipher_test.dart @@ -0,0 +1,323 @@ +// 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() + .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(), + ); + }); + }); + + 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().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().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().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().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() + .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().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().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().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().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() + .having( + (e) => e.message, + 'message', + contains( + 'PlatformException(native_crypto, dummy error, null, null)', + ), + ) + .having( + (e) => e.code, + 'code', + 'platform_throws', + ), + ), + ); + }); + }); +} diff --git a/packages/native_crypto/test/src/cipher_text_wrapper_test.dart b/packages/native_crypto/test/src/cipher_text_wrapper_test.dart index 7477900..8dc0116 100644 --- a/packages/native_crypto/test/src/cipher_text_wrapper_test.dart +++ b/packages/native_crypto/test/src/cipher_text_wrapper_test.dart @@ -3,7 +3,7 @@ // ----- // File: cipher_text_wrapper_test.dart // Created Date: 26/05/2022 21:35:41 -// Last Modified: 26/05/2022 22:27:31 +// Last Modified: 27/05/2022 13:46:54 // ----- // Copyright (c) 2022 @@ -293,7 +293,6 @@ void main() { final wrapper = CipherTextWrapper.fromBytes( Uint8List.fromList([1, 2, 3]), ivLength: 1, - messageLength: 1, tagLength: 1, ); expect(wrapper.isSingle, isTrue); @@ -301,11 +300,10 @@ void main() { }); test('creates list from bytes when too big', () { - Cipher.bytesCountPerChunk = 3; + Cipher.bytesCountPerChunk = 1; final wrapper = CipherTextWrapper.fromBytes( Uint8List.fromList([1, 2, 3, 4, 5, 6]), ivLength: 1, - messageLength: 1, tagLength: 1, ); expect(wrapper.isList, isTrue); @@ -317,11 +315,35 @@ void main() { CipherTextWrapper.fromBytes( Uint8List.fromList([1, 2, 3]), ivLength: 1, - messageLength: 1, tagLength: 1, chunkSize: 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() + .having( + (e) => e.code, + 'code', + 'invalid_argument', + ) + .having( + (e) => e.message, + 'message', + contains('on chunk #'), + ), + ), + ); + }); }); }