From 9bfe969c7d88a27fdc752e9f45c2761d6e424243 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Thu, 26 May 2022 16:25:35 +0200 Subject: [PATCH] test: (WIP) add mocks and tests for secret key --- packages/native_crypto/pubspec.yaml | 5 +- .../mocks/mock_native_crypto_platform.dart | 192 ++++++++++++++++++ .../test/src/secret_key_test.dart | 125 ++++++++++++ 3 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 packages/native_crypto/test/mocks/mock_native_crypto_platform.dart create mode 100644 packages/native_crypto/test/src/secret_key_test.dart diff --git a/packages/native_crypto/pubspec.yaml b/packages/native_crypto/pubspec.yaml index a613749..72b0d59 100644 --- a/packages/native_crypto/pubspec.yaml +++ b/packages/native_crypto/pubspec.yaml @@ -2,7 +2,7 @@ name: native_crypto description: Fast and secure cryptography for Flutter. version: 0.1.1 -# publish_to: 'none' +publish_to: 'none' environment: sdk: ">=2.17.0 <3.0.0" @@ -34,6 +34,9 @@ dev_dependencies: flutter_test: sdk: flutter + mockito: ^5.2.0 + plugin_platform_interface: ^2.1.2 + wyatt_analysis: git: url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages diff --git a/packages/native_crypto/test/mocks/mock_native_crypto_platform.dart b/packages/native_crypto/test/mocks/mock_native_crypto_platform.dart new file mode 100644 index 0000000..6e69cb9 --- /dev/null +++ b/packages/native_crypto/test/mocks/mock_native_crypto_platform.dart @@ -0,0 +1,192 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: mock_native_crypto_platform.dart +// Created Date: 25/05/2022 23:34:34 +// Last Modified: 26/05/2022 11:40:24 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +class MockNativeCryptoPlatform extends Fake + with MockPlatformInterfaceMixin + implements NativeCryptoPlatform { + Uint8List? data; + List? dataAsList; + Uint8List? key; + String? algorithm; + int? bitsCount; + String? password; + String? salt; + int? keyBytesCount; + int? iterations; + + Uint8List? Function()? response; + List? Function()? responseAsList; + + // ignore: use_setters_to_change_properties + void setResponse(Uint8List? Function()? response) { + this.response = response; + } + + // ignore: use_setters_to_change_properties + void setResponseAsList(List? Function()? responseAsList) { + this.responseAsList = responseAsList; + } + + void setDecryptExpectations({ + required Uint8List data, + required Uint8List key, + required String algorithm, + }) { + this.data = data; + this.key = key; + this.algorithm = algorithm; + } + + @override + Future decrypt( + Uint8List data, + Uint8List key, + String algorithm, + ) async { + expect(data, this.data); + expect(key, this.key); + expect(algorithm, this.algorithm); + return response?.call(); + } + + void setDecryptAsListExpectations({ + required List data, + required Uint8List key, + required String algorithm, + }) { + dataAsList = data; + this.key = key; + this.algorithm = algorithm; + } + + @override + Future decryptAsList( + List data, + Uint8List key, + String algorithm, + ) async { + expect(data, dataAsList); + expect(key, this.key); + expect(algorithm, this.algorithm); + + return response?.call(); + } + + void setDigestExpectations({ + required Uint8List data, + required String algorithm, + }) { + this.data = data; + this.algorithm = algorithm; + } + + @override + Future digest(Uint8List data, String algorithm) async { + expect(data, this.data); + expect(algorithm, this.algorithm); + + return response?.call(); + } + + void setEncryptExpectations({ + required Uint8List data, + required Uint8List key, + required String algorithm, + }) { + this.data = data; + this.key = key; + this.algorithm = algorithm; + } + + @override + Future encrypt( + Uint8List data, + Uint8List key, + String algorithm, + ) async { + expect(data, this.data); + expect(key, this.key); + expect(algorithm, this.algorithm); + + return response?.call(); + } + + void setEncryptAsListExpectations({ + required Uint8List data, + required Uint8List key, + required String algorithm, + }) => + setEncryptExpectations( + data: data, + key: key, + algorithm: algorithm, + ); + + @override + Future?> encryptAsList( + Uint8List data, + Uint8List key, + String algorithm, + ) async { + expect(data, this.data); + expect(key, this.key); + expect(algorithm, this.algorithm); + + return responseAsList?.call(); + } + + // ignore: use_setters_to_change_properties + void setGenerateKeyExpectations({required int bitsCount}) { + this.bitsCount = bitsCount; + } + + @override + Future generateSecretKey(int bitsCount) async { + expect(bitsCount, this.bitsCount); + + return response?.call(); + } + + void setPbkdf2Expectations({ + required String password, + required String salt, + required int keyBytesCount, + required int iterations, + required String algorithm, + }) { + this.password = password; + this.salt = salt; + this.iterations = iterations; + this.keyBytesCount = keyBytesCount; + this.algorithm = algorithm; + } + + @override + Future pbkdf2( + String password, + String salt, + int keyBytesCount, + int iterations, + String algorithm, + ) async { + expect(password, this.password); + expect(salt, this.salt); + expect(keyBytesCount, this.keyBytesCount); + expect(iterations, this.iterations); + expect(algorithm, this.algorithm); + + return response?.call(); + } +} diff --git a/packages/native_crypto/test/src/secret_key_test.dart b/packages/native_crypto/test/src/secret_key_test.dart new file mode 100644 index 0000000..1a98e24 --- /dev/null +++ b/packages/native_crypto/test/src/secret_key_test.dart @@ -0,0 +1,125 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: secret_key_test.dart +// Created Date: 26/05/2022 10:52:41 +// Last Modified: 26/05/2022 12:07:33 +// ----- +// Copyright (c) 2022 + +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto/src/keys/secret_key.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; + + group('fromSecureRandom', () { + test('handles returning random bytes', () async { + mock + ..setGenerateKeyExpectations(bitsCount: 5) + ..setResponse(() => Uint8List.fromList([1, 2, 3, 4, 5])); + + final SecretKey secretKey = await SecretKey.fromSecureRandom(5); + + expect( + secretKey.bytes, + Uint8List.fromList([1, 2, 3, 4, 5]), + ); + }); + + test('handles returning empty list', () async { + mock + ..setGenerateKeyExpectations(bitsCount: 5) + ..setResponse(() => Uint8List(0)); + + await expectLater( + () => SecretKey.fromSecureRandom(5), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_null', + ), + ), + ); + }); + + test('handles returning null', () async { + mock + ..setGenerateKeyExpectations(bitsCount: 5) + ..setResponse(() => null); + + await expectLater( + () => SecretKey.fromSecureRandom(5), + throwsA( + isA().having( + (e) => e.code, + 'code', + 'platform_returned_null', + ), + ), + ); + }); + + test('handles throwing PlatformException', () async { + mock + ..setGenerateKeyExpectations(bitsCount: 5) + ..setResponse( + () => throw PlatformException( + code: 'native_crypto', + message: 'dummy error', + ), + ); + + await expectLater( + () => SecretKey.fromSecureRandom(5), + throwsA( + isA() + .having( + (e) => e.message, + 'message', + 'PlatformException(native_crypto, dummy error, null, null)', + ) + .having( + (e) => e.code, + 'code', + 'failed_to_generate_secret_key', + ), + ), + ); + }); + }); + + group('Constructors', () { + test('handles Uint8List', () { + final SecretKey key = SecretKey(Uint8List.fromList([1, 2, 3, 4, 5])); + + expect(key.bytes, Uint8List.fromList([1, 2, 3, 4, 5])); + }); + + test('handles base16', () { + final SecretKey key = SecretKey.fromBase16('0102030405'); + + expect(key.bytes, Uint8List.fromList([1, 2, 3, 4, 5])); + }); + + test('handles base64', () { + final SecretKey key = SecretKey.fromBase64('AQIDBAU='); + + expect(key.bytes, Uint8List.fromList([1, 2, 3, 4, 5])); + }); + + test('handles utf8', () { + final SecretKey key = SecretKey.fromUtf8('ABCDE'); + + expect(key.bytes, Uint8List.fromList([65, 66, 67, 68, 69])); + }); + }); +}