From 38cb0a59888031e660c924e617327a103905f5f6 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 22 Feb 2023 17:24:46 +0100 Subject: [PATCH 01/21] build: add vscode settings --- .vscode/settings.json | 19 +++++++++++++++++++ .../ios/Classes/utils/FileParameters.swift | 8 ++++++++ 2 files changed, 27 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 packages/native_crypto_ios/ios/Classes/utils/FileParameters.swift diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e1b09e8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,19 @@ +{ + "bloc.newCubitTemplate.type": "equatable", + "psi-header.config": { + "blankLinesAfter": 0, + "forceToTop": true, + }, + "psi-header.templates": [ + { + "language": "*", + "template": [ + "Copyright 2019-<> <>", + "", + "Use of this source code is governed by an MIT-style", + "license that can be found in the LICENSE file or at", + "https://opensource.org/licenses/MIT.", + ] + } + ], +} \ No newline at end of file diff --git a/packages/native_crypto_ios/ios/Classes/utils/FileParameters.swift b/packages/native_crypto_ios/ios/Classes/utils/FileParameters.swift new file mode 100644 index 0000000..efbc0ae --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/utils/FileParameters.swift @@ -0,0 +1,8 @@ +// +// FileParameters.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 07/01/2023. +// + +import Foundation -- 2.47.2 From f47c352efbbfe34a1d2dad52c021d2941465f19f Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 22 Feb 2023 17:25:20 +0100 Subject: [PATCH 02/21] docs: add uml models --- resources/models/aes_classes.puml | 25 +++++++++ resources/models/aes_dss.puml | 44 ++++++++++++++++ resources/models/cipher_text_classes.puml | 63 +++++++++++++++++++++++ resources/models/cipher_text_dss.puml | 19 +++++++ resources/models/digest_classes.puml | 25 +++++++++ resources/models/digest_dss.puml | 29 +++++++++++ resources/models/generator_classes.puml | 9 ++++ resources/models/generator_dss.puml | 15 ++++++ resources/models/kdf_classes.puml | 17 ++++++ resources/models/kdf_dss.puml | 20 +++++++ resources/models/key_classes.puml | 40 ++++++++++++++ resources/models/key_dss.puml | 17 ++++++ 12 files changed, 323 insertions(+) create mode 100644 resources/models/aes_classes.puml create mode 100644 resources/models/aes_dss.puml create mode 100644 resources/models/cipher_text_classes.puml create mode 100644 resources/models/cipher_text_dss.puml create mode 100644 resources/models/digest_classes.puml create mode 100644 resources/models/digest_dss.puml create mode 100644 resources/models/generator_classes.puml create mode 100644 resources/models/generator_dss.puml create mode 100644 resources/models/kdf_classes.puml create mode 100644 resources/models/kdf_dss.puml create mode 100644 resources/models/key_classes.puml create mode 100644 resources/models/key_dss.puml diff --git a/resources/models/aes_classes.puml b/resources/models/aes_classes.puml new file mode 100644 index 0000000..fa0ae97 --- /dev/null +++ b/resources/models/aes_classes.puml @@ -0,0 +1,25 @@ +@startuml aes_classes + +abstract class Cipher { + encrypt(plainText: Uint8List): CipherText + decrypt(cipherText: CipherText): Uint8List + encryptFile(plainTextFile: Path, cipherTextFile: Path) + decryptFile(cipherTextFile: Path, plainTextFile: Path) +} + +class AES extends Cipher { + key: SecretKey + mode: AESMode + padding: Padding + chunkSize: int + + encrypt(plainText: Uint8List): CipherText + decrypt(cipherText: CipherText): Uint8List + encryptFile(plainTextFile: Path, cipherTextFile: Path) + decryptFile(cipherTextFile: Path, plainTextFile: Path) + + encryptWithIV(plainText: Uint8List, iv: Uint8List): AESCipherChunk + decryptWithIV(cipherChunk: AESCipherChunk, iv: Uint8List): Uint8List +} + +@enduml \ No newline at end of file diff --git a/resources/models/aes_dss.puml b/resources/models/aes_dss.puml new file mode 100644 index 0000000..dd2d7d3 --- /dev/null +++ b/resources/models/aes_dss.puml @@ -0,0 +1,44 @@ +@startuml aes_dss + +actor user +participant AES as aes +participant CipherText as ct +participant CipherChunk as cc +participant NativeCrypto as nc + +user -> aes : new(key: SecretKey, mode: Mode, padding: Padding, chunkSize: int) +activate aes +aes --> user : AES +user -> aes : encrypt(plainText: Uint8List) + +loop for each chunk in plainText + aes -> nc : encrypt(chunk: Uint8List, key: Uint8List, "aes/gcm/NoPadding") + nc --> aes : Uint8List + aes -> cc : new(chunk: Uint8List) + cc --> aes : CipherChunk +end + +aes -> ct : new(chunks: List) +ct --> aes : CipherText +aes --> user : CipherText + +user -> aes : decrypt(cipherText: CipherText) +loop for each chunk in cipherText.chunks + aes -> nc : decrypt(chunk: Uint8List, key: Uint8List, "aes/gcm/NoPadding") + nc --> aes : Uint8List + aes --> aes : concat Uint8List +end + +aes --> user : Uint8List + +user -> aes : encryptFile(plainTextFile: File, cipherTextFile: File) +aes -> nc : encryptFile(plainTextFile: File, cipherTextFile: File, key: Uint8List, "aes/gcm/NoPadding") +nc --> aes : void +aes --> user : void + +user -> aes : decryptFile(cipherTextFile: File, plainTextFile: File) +aes -> nc : decryptFile(cipherTextFile: File, plainTextFile: File, key: Uint8List, "aes/gcm/NoPadding") +nc --> aes : void +aes --> user : void + +@enduml \ No newline at end of file diff --git a/resources/models/cipher_text_classes.puml b/resources/models/cipher_text_classes.puml new file mode 100644 index 0000000..ee96cb1 --- /dev/null +++ b/resources/models/cipher_text_classes.puml @@ -0,0 +1,63 @@ +@startuml cipher_text_classes + +abstract class ByteArray { + bytes : Uint8List + length : int + + ByteArray(bytes: Uint8List) + + fromList(list: List) + fromLength(length: int, {fill: int = 0}) + fromUtf16(encoded: String) + fromUtf8(encoded: String) + fromBase64(encoded: String) + fromBase16(encoded: String) + + toList() : List + toUtf16() : String + toUtf8() : String + toBase64() : String + toBase16() : String +} + +class CipherChunk extends ByteArray { + CipherChunk(bytes: Uint8List) + + fromList(list: List) + fromUtf16(encoded: String) + fromUtf8(encoded: String) + fromBase64(encoded: String) + fromBase16(encoded: String) +} + +class CipherText extends ByteArray { + chunkSize : int + chunks : List + + CipherText(bytes: Uint8List, {chunkSize: int = 33554432}) + + fromList(list: List) + fromUtf16(encoded: String) + fromUtf8(encoded: String) + fromBase64(encoded: String) + fromBase16(encoded: String) + + toList() : List + toUtf16() : String + toUtf8() : String + toBase64() : String + toBase16() : String + + fromChunks(chunks: List) + toChunks() : List + toBytes() : Uint8List +} + +class AESCipherChunk extends CipherChunk { + iv : Uint8List + message : Uint8List + tag : Uint8List +} + + +@enduml \ No newline at end of file diff --git a/resources/models/cipher_text_dss.puml b/resources/models/cipher_text_dss.puml new file mode 100644 index 0000000..653102d --- /dev/null +++ b/resources/models/cipher_text_dss.puml @@ -0,0 +1,19 @@ +@startuml cipher_text_dss + +actor user +participant CipherText as ct +participant CipherChunk as cc + +user -> ct : new(bytes) +loop for each chunk + ct -> cc : new(bytes) + cc --> ct +end +ct --> user : CipherText + +user -> ct : new(bytes, chunkSize: bytes.length) +ct -> cc : new(bytes) +cc --> ct +ct --> user : CipherText + +@enduml \ No newline at end of file diff --git a/resources/models/digest_classes.puml b/resources/models/digest_classes.puml new file mode 100644 index 0000000..f1bd9fc --- /dev/null +++ b/resources/models/digest_classes.puml @@ -0,0 +1,25 @@ +@startuml digest_classes + +abstract class Hash { + digest(data: Uint8List): Uint8List +} + +abstract class Hmac { + digest(data: Uint8List, key: Uint8List): Uint8List +} + +Hmac o-- Hash + +class Sha256 extends Hash { + static instance: Sha256 +} + +class Sha512 extends Hash { + static instance: Sha512 +} + +class HmacSha256 extends Hmac { + static instance: HmacSha256 +} + +@enduml \ No newline at end of file diff --git a/resources/models/digest_dss.puml b/resources/models/digest_dss.puml new file mode 100644 index 0000000..d4d21ce --- /dev/null +++ b/resources/models/digest_dss.puml @@ -0,0 +1,29 @@ +@startuml digest_dss + +actor user +participant Flutter as flt +participant Sha256 as sha +participant HmacSha256 as hmac +participant NativeCrypto as nc + +user -> flt : getDigest("sha256") +flt -> sha : getInstance() +sha --> flt : Sha256 +flt --> user : Sha256 + +user -> sha : digest(data) +sha --> nc : hash(data, "sha256") +nc --> sha : digest +sha --> user : digest + +user -> flt : getDigest("hmacSha256") +flt -> hmac : getInstance() +hmac --> flt : HmacSha256 +flt --> user : HmacSha256 + +user -> hmac : digest(data) +hmac --> nc : hmac(data, key, "sha256") +nc --> hmac : digest +hmac --> user : digest + +@enduml \ No newline at end of file diff --git a/resources/models/generator_classes.puml b/resources/models/generator_classes.puml new file mode 100644 index 0000000..a5cfad1 --- /dev/null +++ b/resources/models/generator_classes.puml @@ -0,0 +1,9 @@ +@startuml generator_classes + +abstract class Random { + generate(bytes: int): Uint8List +} + +class SecureRandom extends Random {} + +@enduml \ No newline at end of file diff --git a/resources/models/generator_dss.puml b/resources/models/generator_dss.puml new file mode 100644 index 0000000..97a877c --- /dev/null +++ b/resources/models/generator_dss.puml @@ -0,0 +1,15 @@ +@startuml generator_dss + +actor user +participant SecureRandom as rand +participant NativeCrypto as nc + +user -> rand : new() +rand --> user : SecureRandom + +user -> rand : generate(32) +rand -> nc : generateRandomBytes(32) +nc --> rand : Uint8List(32) +rand --> user : Uint8List(32) + +@enduml \ No newline at end of file diff --git a/resources/models/kdf_classes.puml b/resources/models/kdf_classes.puml new file mode 100644 index 0000000..6af06ae --- /dev/null +++ b/resources/models/kdf_classes.puml @@ -0,0 +1,17 @@ +@startuml kdf_classes + +abstract class KeyDerivationFunction { + derive(keyMaterial: Uint8List) : Uint8List + verify(keyMaterial: Uint8List, expected: Uint8List) : bool +} + +class PBKDF2 extends KeyDerivationFunction { + hashAlgorithm: Hash + iterations: int + salt: Uint8List + length: int + + call({password: String}) : SecretKey +} + +@enduml \ No newline at end of file diff --git a/resources/models/kdf_dss.puml b/resources/models/kdf_dss.puml new file mode 100644 index 0000000..3850996 --- /dev/null +++ b/resources/models/kdf_dss.puml @@ -0,0 +1,20 @@ +@startuml kdf_dss + +actor user +participant Pbkdf2 as kdf +participant NativeCrypto as nc + +user -> kdf: new(hash, iterations, salt, length) +kdf--> user : Pbkdf2 + +user -> kdf: derive(password) +kdf--> nc : pbkdf2(password, hash, iterations, salt, length) +nc --> kdf: Uint8List(length) +kdf--> user : SecretKey + +user -> kdf : verify(password, key) +kdf--> nc : pbkdf2(password, hash, iterations, salt, length) +nc --> kdf: Uint8List(length) +kdf--> user : bool + +@enduml \ No newline at end of file diff --git a/resources/models/key_classes.puml b/resources/models/key_classes.puml new file mode 100644 index 0000000..07fbdc7 --- /dev/null +++ b/resources/models/key_classes.puml @@ -0,0 +1,40 @@ +@startuml key_classes + +abstract class ByteArray { + bytes : Uint8List + length : int + + fromList(list: List ) + fromLength(length: int, {fill: int = 0}) + fromUtf16(encoded: String) + fromUtf8(encoded: String) + fromBase64(encoded: String) + fromBase16(encoded: String) + + toList() : List + toUtf16() : String + toUtf8() : String + toBase64() : String + toBase16() : String + +} + +abstract class Key extends ByteArray { + fromList(list: List ) + fromUtf16(encoded: String) + fromUtf8(encoded: String) + fromBase64(encoded: String) + fromBase16(encoded: String) +} + +class SecretKey extends Key { + fromList(list: List) + fromUtf16(encoded: String) + fromUtf8(encoded: String) + fromBase64(encoded: String) + fromBase16(encoded: String) + + async fromSecureRandom(bytes: int) +} + +@enduml \ No newline at end of file diff --git a/resources/models/key_dss.puml b/resources/models/key_dss.puml new file mode 100644 index 0000000..135a850 --- /dev/null +++ b/resources/models/key_dss.puml @@ -0,0 +1,17 @@ +@startuml key_dss + +actor user +participant SecretKey as sk +participant SecureRandom as rand +participant NativeCrypto as nc + +user -> sk : fromSecureRandom(32) +sk -> rand : new() +rand --> sk : SecureRandom +sk -> rand : generate(32) +rand -> nc : generateRandomBytes(32) +nc --> rand : Uint8List(32) +rand --> sk : Uint8List(32) +sk --> user : SecretKey + +@enduml \ No newline at end of file -- 2.47.2 From ff981b2361e16a16d90cb9c6c9c8b5d9aea7a3cf Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 22 Feb 2023 17:25:58 +0100 Subject: [PATCH 03/21] docs: add android/ios development instructions --- README.md | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 507b7ec..6ec789e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ +/* + * Copyright 2019-2023 Hugo Pointcheval + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */

- +

Fast and powerful cryptographic functions for Flutter.

@@ -130,4 +137,22 @@ Then, you can decrypt your message. ```dart Uint8List message = await cipher.decrypt(wrapper); -``` \ No newline at end of file +``` + +## Development + +### Android + +> https://docs.flutter.dev/development/packages-and-plugins/developing-packages#step-2b-add-android-platform-code-ktjava + +* Launch Android Studio. +* Select Open an existing Android Studio Project in the Welcome to Android Studio dialog, or select File > Open from the menu, and select the `packages/native_crypto/example/android/build.gradle` file. +* In the Gradle Sync dialog, select OK. +* In the Android Gradle Plugin Update dialog, select Don’t remind me again for this project. + +### iOS + +> https://docs.flutter.dev/development/packages-and-plugins/developing-packages#step-2c-add-ios-platform-code-swifthm + +* Launch Xcode. +* Select File > Open, and select the `packages/native_crypto/example/ios/Runner.xcworkspace` file. -- 2.47.2 From 0a040d29710565ffeb2cbccd5c2ca4c2ef65cb9b Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 22 Feb 2023 17:27:58 +0100 Subject: [PATCH 04/21] feat(interface)!: add pigeon + add hmac + remove useless decryption method --- .../analysis_options.yaml | 7 +- .../lib/native_crypto_platform_interface.dart | 23 +- .../lib/src/core/enums/exception_code.dart | 84 ++ .../lib/src/core/enums/methods.dart | 17 + .../lib/src/core/exceptions/exception.dart | 100 +++ .../basic_message_channel_native_crypto.dart | 204 +++++ .../method_channel_native_crypto.dart | 161 ++++ .../src/interface/native_crypto_platform.dart | 120 +++ .../method_channel_native_crypto.dart | 154 ---- .../lib/src/pigeon/messages.pigeon.dart | 829 ++++++++++++++++++ .../lib/src/pigeon/test_api.dart | 316 +++++++ .../native_crypto_platform.dart | 92 -- .../lib/src/utils/exception.dart | 126 --- .../pigeons/copyright_header.txt | 6 + .../pigeons/messages.dart | 222 +++++ .../pubspec.yaml | 10 +- .../method_channel_native_crypto_test.dart | 175 ---- .../test/native_crypto_exception_test.dart | 72 ++ .../native_crypto_platform_test.dart | 170 +--- 19 files changed, 2187 insertions(+), 701 deletions(-) create mode 100644 packages/native_crypto_platform_interface/lib/src/core/enums/exception_code.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/core/enums/methods.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/core/exceptions/exception.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/implementations/method_channel_native_crypto.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/interface/native_crypto_platform.dart delete mode 100644 packages/native_crypto_platform_interface/lib/src/method_channel/method_channel_native_crypto.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/pigeon/messages.pigeon.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/pigeon/test_api.dart delete mode 100644 packages/native_crypto_platform_interface/lib/src/platform_interface/native_crypto_platform.dart delete mode 100644 packages/native_crypto_platform_interface/lib/src/utils/exception.dart create mode 100644 packages/native_crypto_platform_interface/pigeons/copyright_header.txt create mode 100644 packages/native_crypto_platform_interface/pigeons/messages.dart delete mode 100644 packages/native_crypto_platform_interface/test/method_channel/method_channel_native_crypto_test.dart create mode 100644 packages/native_crypto_platform_interface/test/native_crypto_exception_test.dart diff --git a/packages/native_crypto_platform_interface/analysis_options.yaml b/packages/native_crypto_platform_interface/analysis_options.yaml index 82177cd..224f249 100644 --- a/packages/native_crypto_platform_interface/analysis_options.yaml +++ b/packages/native_crypto_platform_interface/analysis_options.yaml @@ -1 +1,6 @@ -include: package:wyatt_analysis/analysis_options.flutter.yaml \ No newline at end of file +include: package:wyatt_analysis/analysis_options.flutter.yaml + +analyzer: + exclude: + - "**/*.pigeon.dart" + - "lib/src/pigeon/test_api.dart" \ No newline at end of file diff --git a/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart index c85bda1..5e2f256 100644 --- a/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart +++ b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart @@ -1,14 +1,15 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: native_crypto_platform_interface.dart -// Created Date: 24/05/2022 19:39:11 -// Last Modified: 24/05/2022 19:39:58 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +/// The interface that implementations of native_crypto must implement. library native_crypto_platform_interface; -export 'src/method_channel/method_channel_native_crypto.dart'; -export 'src/platform_interface/native_crypto_platform.dart'; -export 'src/utils/exception.dart'; +export 'src/core/enums/exception_code.dart'; +export 'src/core/enums/methods.dart'; +export 'src/core/exceptions/exception.dart'; +export 'src/implementations/basic_message_channel_native_crypto.dart'; +export 'src/implementations/method_channel_native_crypto.dart'; +export 'src/interface/native_crypto_platform.dart'; diff --git a/packages/native_crypto_platform_interface/lib/src/core/enums/exception_code.dart b/packages/native_crypto_platform_interface/lib/src/core/enums/exception_code.dart new file mode 100644 index 0000000..910b9d9 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/core/enums/exception_code.dart @@ -0,0 +1,84 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +enum NativeCryptoExceptionCode { + /// The method is not implemented on the platform side. + platformMethodNotImplemented, + + /// Platform returned invalid data. + /// Can be null, empty, or not the expected type. + platformReturnedInvalidData, // TODO(hpcl): remove this + + /// The platforms returned null. + nullError, + + /// The algorithm is not supported. + algorithmNotSupported, + + /// The key is not valid. Like a bad length or format. + invalidKey, + + /// The data is not valid. + invalidData, + + /// The parameters are not valid. Like an invalid IV. + invalidParameters, + + /// Authentication failed. Like a bad MAC or tag. + authenticationError, + + /// An I/O error occurred. + ioError, + + /// Key derivation failed. + keyDerivationError, + + /// Channel error. Like a bad channel or a bad message. + channelError, + + /// An unknown error occurred. + unknownError; + + /// Returns code of the [NativeCryptoExceptionCode]. + /// ```dart + /// print(NativeCryptoExceptionCode.platformMethodNotImplemented.code) + /// // => platform_method_not_implemented + /// ``` + String get code { + switch (name.length) { + case 0: + return name; + case 1: + return name.toLowerCase(); + default: + return name + .splitMapJoin( + RegExp('[A-Z]'), + onMatch: (m) => ' ${m[0]}', + onNonMatch: (n) => n, + ) + .trim() + .splitMapJoin( + RegExp(r'\s+|-+|_+|\.+'), + onMatch: (m) => '_', + onNonMatch: (n) => n.toLowerCase(), + ); + } + } + + /// Returns the [NativeCryptoExceptionCode] from the given [code]. + static NativeCryptoExceptionCode from(String code) { + for (final value in values) { + if (value.code == code) { + return value; + } + } + return unknownError; + } + + @override + String toString() => code; +} diff --git a/packages/native_crypto_platform_interface/lib/src/core/enums/methods.dart b/packages/native_crypto_platform_interface/lib/src/core/enums/methods.dart new file mode 100644 index 0000000..024b575 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/core/enums/methods.dart @@ -0,0 +1,17 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +enum NativeCryptoMethod { + hash, + hmac, + generateSecureRandom, + pbkdf2, + encrypt, + decrypt, + encryptFile, + decryptFile, + encryptWithIV +} diff --git a/packages/native_crypto_platform_interface/lib/src/core/exceptions/exception.dart b/packages/native_crypto_platform_interface/lib/src/core/exceptions/exception.dart new file mode 100644 index 0000000..9143b13 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/core/exceptions/exception.dart @@ -0,0 +1,100 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:equatable/equatable.dart'; +import 'package:flutter/services.dart'; +import 'package:native_crypto_platform_interface/src/core/enums/exception_code.dart'; + +/// An exception thrown by the native crypto plugin. +class NativeCryptoException extends Equatable implements Exception { + /// Creates a new [NativeCryptoException]. + const NativeCryptoException({ + required this.code, + this.message, + this.stackTrace, + }); + + /// Creates a new [NativeCryptoException] from a [PlatformException]. + factory NativeCryptoException.fromPlatformException( + PlatformException platformException, + StackTrace stackTrace, + ) { + final Map? details = platformException.details != null + ? Map.from( + platformException.details as Map, + ) + : null; + + String code = platformException.code.split('(').first; + String message = platformException.message ?? ''; + + if (details != null) { + code = details['code'] ?? code; + message = details['message'] ?? message; + } + + return NativeCryptoException( + code: NativeCryptoExceptionCode.from(code), + message: message, + stackTrace: stackTrace, + ); + } + + /// The standardised error code. + final NativeCryptoExceptionCode code; + + /// The long form message of the exception. + final String? message; + + /// The stack trace which provides information to the user about the call + /// sequence that triggered an exception + final StackTrace? stackTrace; + + static Never convertPlatformException( + Object exception, + StackTrace stackTrace, + ) { + // If the exception is not a PlatformException, throw it as is. + if (exception is! Exception || exception is! PlatformException) { + Error.throwWithStackTrace(exception, stackTrace); + } + + // Otherwise, throw a NativeCryptoException. + Error.throwWithStackTrace( + NativeCryptoException.fromPlatformException(exception, stackTrace), + stackTrace, + ); + } + + NativeCryptoException copyWith({ + NativeCryptoExceptionCode? code, + String? message, + StackTrace? stackTrace, + }) => + NativeCryptoException( + code: code ?? this.code, + message: message ?? this.message, + stackTrace: stackTrace ?? this.stackTrace, + ); + + @override + String toString() { + final output = StringBuffer('[NativeCrypto/$code]'); + + if (message != null) { + output.write(' $message'); + } + + if (stackTrace != null) { + output.write('\n\n$stackTrace'); + } + + return output.toString(); + } + + @override + List get props => [code, message, stackTrace]; +} diff --git a/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart b/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart new file mode 100644 index 0000000..993a000 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart @@ -0,0 +1,204 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/foundation.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; +import 'package:native_crypto_platform_interface/src/pigeon/messages.pigeon.dart'; + +/// An implementation of [NativeCryptoPlatform] that uses Pigeon generated code. +class BasicMessageChannelNativeCrypto extends NativeCryptoPlatform { + /// The Pigeon API used to interact with the native platform. + @visibleForTesting + NativeCryptoAPI api = NativeCryptoAPI(); + + @override + Future hash(Uint8List data, {required String algorithm}) async { + try { + return api + .hash( + HashRequest( + data: data, + algorithm: algorithm, + ), + ) + .then((value) => value.hash); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future hmac( + Uint8List data, { + required Uint8List key, + required String algorithm, + }) async { + try { + return api + .hmac( + HmacRequest( + data: data, + key: key, + algorithm: algorithm, + ), + ) + .then((value) => value.hmac); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future generateSecureRandom(int length) async { + try { + return api + .generateSecureRandom( + GenerateSecureRandomRequest( + length: length, + ), + ) + .then((value) => value.random); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future pbkdf2({ + required Uint8List password, + required Uint8List salt, + required int length, + required int iterations, + required String hashAlgorithm, + }) async { + try { + return api + .pbkdf2( + Pbkdf2Request( + password: password, + salt: salt, + length: length, + iterations: iterations, + hashAlgorithm: hashAlgorithm, + ), + ) + .then((value) => value.key); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future encrypt( + Uint8List plainText, { + required Uint8List key, + required String algorithm, + }) async { + try { + return api + .encrypt( + EncryptRequest( + plainText: plainText, + key: key, + algorithm: algorithm, + ), + ) + .then((value) => value.cipherText); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future decrypt( + Uint8List cipherText, { + required Uint8List key, + required String algorithm, + }) async { + try { + return api + .decrypt( + DecryptRequest( + cipherText: cipherText, + key: key, + algorithm: algorithm, + ), + ) + .then((value) => value.plainText); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future encryptFile({ + required String plainTextPath, + required String cipherTextPath, + required Uint8List key, + required String algorithm, + }) async { + try { + return api + .encryptFile( + EncryptFileRequest( + plainTextPath: plainTextPath, + cipherTextPath: cipherTextPath, + key: key, + algorithm: algorithm, + ), + ) + .then((value) => value.success); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future decryptFile({ + required String cipherTextPath, + required String plainTextPath, + required Uint8List key, + required String algorithm, + }) async { + try { + return api + .decryptFile( + DecryptFileRequest( + cipherTextPath: cipherTextPath, + plainTextPath: plainTextPath, + key: key, + algorithm: algorithm, + ), + ) + .then((value) => value.success); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future encryptWithIV({ + required Uint8List plainText, + required Uint8List iv, + required Uint8List key, + required String algorithm, + }) async { + try { + return api + .encryptWithIV( + EncryptWithIVRequest( + plainText: plainText, + iv: iv, + key: key, + algorithm: algorithm, + ), + ) + .then((value) => value.cipherText); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } +} diff --git a/packages/native_crypto_platform_interface/lib/src/implementations/method_channel_native_crypto.dart b/packages/native_crypto_platform_interface/lib/src/implementations/method_channel_native_crypto.dart new file mode 100644 index 0000000..d51f79e --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/implementations/method_channel_native_crypto.dart @@ -0,0 +1,161 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +/// An implementation of [NativeCryptoPlatform] that uses method channels. +class MethodChannelNativeCrypto extends NativeCryptoPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + MethodChannel channel = const MethodChannel('plugins.hugop.cl/native_crypto'); + + Future _invokeMethod( + NativeCryptoMethod method, [ + Map? arguments, + ]) async { + try { + return await channel.invokeMethod(method.name, arguments); + } on PlatformException catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future hash(Uint8List data, {required String algorithm}) => + _invokeMethod( + NativeCryptoMethod.hash, + { + 'data': data, + 'algorithm': algorithm, + }, + ); + + @override + Future hmac( + Uint8List data, { + required Uint8List key, + required String algorithm, + }) => + _invokeMethod( + NativeCryptoMethod.hmac, + { + 'data': data, + 'key': key, + 'algorithm': algorithm, + }, + ); + + @override + Future generateSecureRandom(int length) => + _invokeMethod( + NativeCryptoMethod.generateSecureRandom, + { + 'length': length, + }, + ); + + @override + Future pbkdf2({ + required Uint8List password, + required Uint8List salt, + required int length, + required int iterations, + required String hashAlgorithm, + }) => + _invokeMethod( + NativeCryptoMethod.pbkdf2, + { + 'password': password, + 'salt': salt, + 'length': length, + 'iterations': iterations, + 'hashAlgorithm': hashAlgorithm, + }, + ); + + @override + Future encrypt( + Uint8List plainText, { + required Uint8List key, + required String algorithm, + }) => + _invokeMethod( + NativeCryptoMethod.encrypt, + { + 'plainText': plainText, + 'key': key, + 'algorithm': algorithm, + }, + ); + + @override + Future decrypt( + Uint8List cipherText, { + required Uint8List key, + required String algorithm, + }) => + _invokeMethod( + NativeCryptoMethod.decrypt, + { + 'cipherText': cipherText, + 'key': key, + 'algorithm': algorithm, + }, + ); + + @override + Future encryptFile({ + required String plainTextPath, + required String cipherTextPath, + required Uint8List key, + required String algorithm, + }) => + _invokeMethod( + NativeCryptoMethod.encryptFile, + { + 'plainTextPath': plainTextPath, + 'cipherTextPath': cipherTextPath, + 'key': key, + 'algorithm': algorithm, + }, + ); + + @override + Future decryptFile({ + required String cipherTextPath, + required String plainTextPath, + required Uint8List key, + required String algorithm, + }) => + _invokeMethod( + NativeCryptoMethod.decryptFile, + { + 'cipherTextPath': cipherTextPath, + 'plainTextPath': plainTextPath, + 'key': key, + 'algorithm': algorithm, + }, + ); + + @override + Future encryptWithIV({ + required Uint8List plainText, + required Uint8List iv, + required Uint8List key, + required String algorithm, + }) => + _invokeMethod( + NativeCryptoMethod.encryptWithIV, + { + 'plainText': plainText, + 'iv': iv, + 'key': key, + 'algorithm': algorithm, + }, + ); +} diff --git a/packages/native_crypto_platform_interface/lib/src/interface/native_crypto_platform.dart b/packages/native_crypto_platform_interface/lib/src/interface/native_crypto_platform.dart new file mode 100644 index 0000000..6a953ad --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/interface/native_crypto_platform.dart @@ -0,0 +1,120 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; +import 'package:native_crypto_platform_interface/src/implementations/method_channel_native_crypto.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +/// The interface that implementations of native_crypto must implement. +/// +/// Platform implementations should extend this class rather than implement +/// it as `NativeCrypto` does not consider newly added methods to be +/// breaking changes. Extending this class (using `extends`) ensures +/// that the subclass will get the default implementation, while platform +/// implementations that `implements` this interface will be +/// broken by newly added [NativeCryptoPlatform] methods. +abstract class NativeCryptoPlatform extends PlatformInterface { + /// Constructs a NativeCryptoPlatform. + NativeCryptoPlatform() : super(token: _token); + + static final Object _token = Object(); + + static NativeCryptoPlatform _instance = MethodChannelNativeCrypto(); + + /// The default instance of [NativeCryptoPlatform] to use. + /// + /// Defaults to [MethodChannelNativeCrypto]. + static NativeCryptoPlatform get instance => _instance; + + /// Platform-specific plugins should set this with their own platform-specific + /// class that extends [NativeCryptoPlatform] when they register themselves. + static set instance(NativeCryptoPlatform instance) { + PlatformInterface.verify(instance, _token); + _instance = instance; + } + + /// Returns the hash of the given data. + Future hash(Uint8List data, {required String algorithm}) { + throw UnimplementedError('hash is not implemented'); + } + + /// Returns the hmac of the given data using the given key. + Future hmac( + Uint8List data, { + required Uint8List key, + required String algorithm, + }) { + throw UnimplementedError('hmac is not implemented'); + } + + /// Generates a secure random of the given length in bytes. + Future generateSecureRandom(int length) { + throw UnimplementedError('generateSecureRandom is not implemented'); + } + + /// Derives a key from the given password and salt using pbkdf2. + Future pbkdf2({ + required Uint8List password, + required Uint8List salt, + required int length, + required int iterations, + required String hashAlgorithm, + }) { + throw UnimplementedError('pbkdf2 is not implemented'); + } + + /// Encrypts the given data using the given key and algorithm. + Future encrypt( + Uint8List plainText, { + required Uint8List key, + required String algorithm, + }) { + throw UnimplementedError('encrypt is not implemented'); + } + + /// Decrypts the given data using the given key and algorithm. + Future decrypt( + Uint8List cipherText, { + required Uint8List key, + required String algorithm, + }) { + throw UnimplementedError('decrypt is not implemented'); + } + + /// Encrypts the given file using the given key and algorithm. + Future encryptFile({ + required String plainTextPath, + required String cipherTextPath, + required Uint8List key, + required String algorithm, + }) { + throw UnimplementedError('encryptFile is not implemented'); + } + + /// Decrypts the given file using the given key and algorithm. + Future decryptFile({ + required String cipherTextPath, + required String plainTextPath, + required Uint8List key, + required String algorithm, + }) { + throw UnimplementedError('decryptFile is not implemented'); + } + + /// Encrypts the given data using the given key, algorithm and iv. + /// + /// Users should use [encrypt] instead if they don't need to specify the iv. + Future encryptWithIV({ + required Uint8List plainText, + required Uint8List iv, + required Uint8List key, + required String algorithm, + }) { + throw UnimplementedError('encryptWithIV is not implemented'); + } +} diff --git a/packages/native_crypto_platform_interface/lib/src/method_channel/method_channel_native_crypto.dart b/packages/native_crypto_platform_interface/lib/src/method_channel/method_channel_native_crypto.dart deleted file mode 100644 index 0559ed4..0000000 --- a/packages/native_crypto_platform_interface/lib/src/method_channel/method_channel_native_crypto.dart +++ /dev/null @@ -1,154 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: native_crypto_method_channel.dart -// Created Date: 25/12/2021 16:58:04 -// Last Modified: 25/05/2022 10:40:29 -// ----- -// Copyright (c) 2021 - -import 'dart:typed_data'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; - -/// An implementation of [NativeCryptoPlatform] that uses method channels. -class MethodChannelNativeCrypto extends NativeCryptoPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - MethodChannel channel = const MethodChannel('plugins.hugop.cl/native_crypto'); - - @override - Future digest(Uint8List data, String algorithm) async { - try { - return await channel.invokeMethod( - 'digest', - { - 'data': data, - 'algorithm': algorithm, - }, - ); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } - - @override - Future generateSecretKey(int bitsCount) async { - try { - return await channel.invokeMethod( - 'generateSecretKey', - { - 'bitsCount': bitsCount, - }, - ); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } - - @override - Future pbkdf2( - String password, - String salt, - int keyBytesCount, - int iterations, - String algorithm, - ) async { - try { - return await channel.invokeMethod( - 'pbkdf2', - { - 'password': password, - 'salt': salt, - 'keyBytesCount': keyBytesCount, - 'iterations': iterations, - 'algorithm': algorithm, - }, - ); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } - - @override - Future?> encryptAsList( - Uint8List data, - Uint8List key, - String algorithm, - ) async { - try { - return await channel.invokeListMethod( - 'encryptAsList', - { - 'data': data, - 'key': key, - 'algorithm': algorithm, - }, - ); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } - - @override - Future decryptAsList( - List data, - Uint8List key, - String algorithm, - ) async { - try { - return await channel.invokeMethod( - 'decryptAsList', - { - 'data': data, - 'key': key, - 'algorithm': algorithm, - }, - ); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } - - @override - Future encrypt( - Uint8List data, - Uint8List key, - String algorithm, - ) async { - try { - return await channel.invokeMethod( - 'encrypt', - { - 'data': data, - 'key': key, - 'algorithm': algorithm, - }, - ); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } - - @override - Future decrypt( - Uint8List data, - Uint8List key, - String algorithm, - ) async { - try { - return await channel.invokeMethod( - 'decrypt', - { - 'data': data, - 'key': key, - 'algorithm': algorithm, - }, - ); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } -} diff --git a/packages/native_crypto_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/native_crypto_platform_interface/lib/src/pigeon/messages.pigeon.dart new file mode 100644 index 0000000..7f8a4a4 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -0,0 +1,829 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +class HashRequest { + HashRequest({ + this.data, + this.algorithm, + }); + + Uint8List? data; + + String? algorithm; + + Object encode() { + return [ + data, + algorithm, + ]; + } + + static HashRequest decode(Object result) { + result as List; + return HashRequest( + data: result[0] as Uint8List?, + algorithm: result[1] as String?, + ); + } +} + +class HashResponse { + HashResponse({ + this.hash, + }); + + Uint8List? hash; + + Object encode() { + return [ + hash, + ]; + } + + static HashResponse decode(Object result) { + result as List; + return HashResponse( + hash: result[0] as Uint8List?, + ); + } +} + +class HmacRequest { + HmacRequest({ + this.data, + this.key, + this.algorithm, + }); + + Uint8List? data; + + Uint8List? key; + + String? algorithm; + + Object encode() { + return [ + data, + key, + algorithm, + ]; + } + + static HmacRequest decode(Object result) { + result as List; + return HmacRequest( + data: result[0] as Uint8List?, + key: result[1] as Uint8List?, + algorithm: result[2] as String?, + ); + } +} + +class HmacResponse { + HmacResponse({ + this.hmac, + }); + + Uint8List? hmac; + + Object encode() { + return [ + hmac, + ]; + } + + static HmacResponse decode(Object result) { + result as List; + return HmacResponse( + hmac: result[0] as Uint8List?, + ); + } +} + +class GenerateSecureRandomRequest { + GenerateSecureRandomRequest({ + this.length, + }); + + int? length; + + Object encode() { + return [ + length, + ]; + } + + static GenerateSecureRandomRequest decode(Object result) { + result as List; + return GenerateSecureRandomRequest( + length: result[0] as int?, + ); + } +} + +class GenerateSecureRandomResponse { + GenerateSecureRandomResponse({ + this.random, + }); + + Uint8List? random; + + Object encode() { + return [ + random, + ]; + } + + static GenerateSecureRandomResponse decode(Object result) { + result as List; + return GenerateSecureRandomResponse( + random: result[0] as Uint8List?, + ); + } +} + +class Pbkdf2Request { + Pbkdf2Request({ + this.password, + this.salt, + this.length, + this.iterations, + this.hashAlgorithm, + }); + + Uint8List? password; + + Uint8List? salt; + + int? length; + + int? iterations; + + String? hashAlgorithm; + + Object encode() { + return [ + password, + salt, + length, + iterations, + hashAlgorithm, + ]; + } + + static Pbkdf2Request decode(Object result) { + result as List; + return Pbkdf2Request( + password: result[0] as Uint8List?, + salt: result[1] as Uint8List?, + length: result[2] as int?, + iterations: result[3] as int?, + hashAlgorithm: result[4] as String?, + ); + } +} + +class Pbkdf2Response { + Pbkdf2Response({ + this.key, + }); + + Uint8List? key; + + Object encode() { + return [ + key, + ]; + } + + static Pbkdf2Response decode(Object result) { + result as List; + return Pbkdf2Response( + key: result[0] as Uint8List?, + ); + } +} + +class EncryptRequest { + EncryptRequest({ + this.plainText, + this.key, + this.algorithm, + }); + + Uint8List? plainText; + + Uint8List? key; + + String? algorithm; + + Object encode() { + return [ + plainText, + key, + algorithm, + ]; + } + + static EncryptRequest decode(Object result) { + result as List; + return EncryptRequest( + plainText: result[0] as Uint8List?, + key: result[1] as Uint8List?, + algorithm: result[2] as String?, + ); + } +} + +class EncryptResponse { + EncryptResponse({ + this.cipherText, + }); + + Uint8List? cipherText; + + Object encode() { + return [ + cipherText, + ]; + } + + static EncryptResponse decode(Object result) { + result as List; + return EncryptResponse( + cipherText: result[0] as Uint8List?, + ); + } +} + +class DecryptRequest { + DecryptRequest({ + this.cipherText, + this.key, + this.algorithm, + }); + + Uint8List? cipherText; + + Uint8List? key; + + String? algorithm; + + Object encode() { + return [ + cipherText, + key, + algorithm, + ]; + } + + static DecryptRequest decode(Object result) { + result as List; + return DecryptRequest( + cipherText: result[0] as Uint8List?, + key: result[1] as Uint8List?, + algorithm: result[2] as String?, + ); + } +} + +class DecryptResponse { + DecryptResponse({ + this.plainText, + }); + + Uint8List? plainText; + + Object encode() { + return [ + plainText, + ]; + } + + static DecryptResponse decode(Object result) { + result as List; + return DecryptResponse( + plainText: result[0] as Uint8List?, + ); + } +} + +class EncryptFileRequest { + EncryptFileRequest({ + this.plainTextPath, + this.cipherTextPath, + this.key, + this.algorithm, + }); + + String? plainTextPath; + + String? cipherTextPath; + + Uint8List? key; + + String? algorithm; + + Object encode() { + return [ + plainTextPath, + cipherTextPath, + key, + algorithm, + ]; + } + + static EncryptFileRequest decode(Object result) { + result as List; + return EncryptFileRequest( + plainTextPath: result[0] as String?, + cipherTextPath: result[1] as String?, + key: result[2] as Uint8List?, + algorithm: result[3] as String?, + ); + } +} + +class EncryptFileResponse { + EncryptFileResponse({ + this.success, + }); + + bool? success; + + Object encode() { + return [ + success, + ]; + } + + static EncryptFileResponse decode(Object result) { + result as List; + return EncryptFileResponse( + success: result[0] as bool?, + ); + } +} + +class DecryptFileRequest { + DecryptFileRequest({ + this.cipherTextPath, + this.plainTextPath, + this.key, + this.algorithm, + }); + + String? cipherTextPath; + + String? plainTextPath; + + Uint8List? key; + + String? algorithm; + + Object encode() { + return [ + cipherTextPath, + plainTextPath, + key, + algorithm, + ]; + } + + static DecryptFileRequest decode(Object result) { + result as List; + return DecryptFileRequest( + cipherTextPath: result[0] as String?, + plainTextPath: result[1] as String?, + key: result[2] as Uint8List?, + algorithm: result[3] as String?, + ); + } +} + +class DecryptFileResponse { + DecryptFileResponse({ + this.success, + }); + + bool? success; + + Object encode() { + return [ + success, + ]; + } + + static DecryptFileResponse decode(Object result) { + result as List; + return DecryptFileResponse( + success: result[0] as bool?, + ); + } +} + +class EncryptWithIVRequest { + EncryptWithIVRequest({ + this.plainText, + this.iv, + this.key, + this.algorithm, + }); + + Uint8List? plainText; + + Uint8List? iv; + + Uint8List? key; + + String? algorithm; + + Object encode() { + return [ + plainText, + iv, + key, + algorithm, + ]; + } + + static EncryptWithIVRequest decode(Object result) { + result as List; + return EncryptWithIVRequest( + plainText: result[0] as Uint8List?, + iv: result[1] as Uint8List?, + key: result[2] as Uint8List?, + algorithm: result[3] as String?, + ); + } +} + +class _NativeCryptoAPICodec extends StandardMessageCodec { + const _NativeCryptoAPICodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is DecryptFileRequest) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is DecryptFileResponse) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is DecryptRequest) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is DecryptResponse) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is EncryptFileRequest) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is EncryptFileResponse) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is EncryptRequest) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is EncryptResponse) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is EncryptWithIVRequest) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is GenerateSecureRandomRequest) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is GenerateSecureRandomResponse) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else if (value is HashRequest) { + buffer.putUint8(139); + writeValue(buffer, value.encode()); + } else if (value is HashResponse) { + buffer.putUint8(140); + writeValue(buffer, value.encode()); + } else if (value is HmacRequest) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); + } else if (value is HmacResponse) { + buffer.putUint8(142); + writeValue(buffer, value.encode()); + } else if (value is Pbkdf2Request) { + buffer.putUint8(143); + writeValue(buffer, value.encode()); + } else if (value is Pbkdf2Response) { + buffer.putUint8(144); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return DecryptFileRequest.decode(readValue(buffer)!); + case 129: + return DecryptFileResponse.decode(readValue(buffer)!); + case 130: + return DecryptRequest.decode(readValue(buffer)!); + case 131: + return DecryptResponse.decode(readValue(buffer)!); + case 132: + return EncryptFileRequest.decode(readValue(buffer)!); + case 133: + return EncryptFileResponse.decode(readValue(buffer)!); + case 134: + return EncryptRequest.decode(readValue(buffer)!); + case 135: + return EncryptResponse.decode(readValue(buffer)!); + case 136: + return EncryptWithIVRequest.decode(readValue(buffer)!); + case 137: + return GenerateSecureRandomRequest.decode(readValue(buffer)!); + case 138: + return GenerateSecureRandomResponse.decode(readValue(buffer)!); + case 139: + return HashRequest.decode(readValue(buffer)!); + case 140: + return HashResponse.decode(readValue(buffer)!); + case 141: + return HmacRequest.decode(readValue(buffer)!); + case 142: + return HmacResponse.decode(readValue(buffer)!); + case 143: + return Pbkdf2Request.decode(readValue(buffer)!); + case 144: + return Pbkdf2Response.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class NativeCryptoAPI { + /// Constructor for [NativeCryptoAPI]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + NativeCryptoAPI({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = _NativeCryptoAPICodec(); + + Future hash(HashRequest arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.hash', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_request]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as HashResponse?)!; + } + } + + Future hmac(HmacRequest arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.hmac', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_request]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as HmacResponse?)!; + } + } + + Future generateSecureRandom(GenerateSecureRandomRequest arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_request]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as GenerateSecureRandomResponse?)!; + } + } + + Future pbkdf2(Pbkdf2Request arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.pbkdf2', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_request]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as Pbkdf2Response?)!; + } + } + + Future encrypt(EncryptRequest arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encrypt', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_request]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as EncryptResponse?)!; + } + } + + Future decrypt(DecryptRequest arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.decrypt', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_request]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as DecryptResponse?)!; + } + } + + Future encryptFile(EncryptFileRequest arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptFile', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_request]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as EncryptFileResponse?)!; + } + } + + Future decryptFile(DecryptFileRequest arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.decryptFile', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_request]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as DecryptFileResponse?)!; + } + } + + Future encryptWithIV(EncryptWithIVRequest arg_request) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_request]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as EncryptResponse?)!; + } + } +} diff --git a/packages/native_crypto_platform_interface/lib/src/pigeon/test_api.dart b/packages/native_crypto_platform_interface/lib/src/pigeon/test_api.dart new file mode 100644 index 0000000..6687736 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/pigeon/test_api.dart @@ -0,0 +1,316 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import +// ignore_for_file: avoid_relative_lib_imports +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'messages.pigeon.dart'; + +class _TestNativeCryptoAPICodec extends StandardMessageCodec { + const _TestNativeCryptoAPICodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is DecryptFileRequest) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else if (value is DecryptFileResponse) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is DecryptRequest) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is DecryptResponse) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is EncryptFileRequest) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is EncryptFileResponse) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is EncryptRequest) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is EncryptResponse) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is EncryptWithIVRequest) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is GenerateSecureRandomRequest) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is GenerateSecureRandomResponse) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else if (value is HashRequest) { + buffer.putUint8(139); + writeValue(buffer, value.encode()); + } else if (value is HashResponse) { + buffer.putUint8(140); + writeValue(buffer, value.encode()); + } else if (value is HmacRequest) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); + } else if (value is HmacResponse) { + buffer.putUint8(142); + writeValue(buffer, value.encode()); + } else if (value is Pbkdf2Request) { + buffer.putUint8(143); + writeValue(buffer, value.encode()); + } else if (value is Pbkdf2Response) { + buffer.putUint8(144); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return DecryptFileRequest.decode(readValue(buffer)!); + case 129: + return DecryptFileResponse.decode(readValue(buffer)!); + case 130: + return DecryptRequest.decode(readValue(buffer)!); + case 131: + return DecryptResponse.decode(readValue(buffer)!); + case 132: + return EncryptFileRequest.decode(readValue(buffer)!); + case 133: + return EncryptFileResponse.decode(readValue(buffer)!); + case 134: + return EncryptRequest.decode(readValue(buffer)!); + case 135: + return EncryptResponse.decode(readValue(buffer)!); + case 136: + return EncryptWithIVRequest.decode(readValue(buffer)!); + case 137: + return GenerateSecureRandomRequest.decode(readValue(buffer)!); + case 138: + return GenerateSecureRandomResponse.decode(readValue(buffer)!); + case 139: + return HashRequest.decode(readValue(buffer)!); + case 140: + return HashResponse.decode(readValue(buffer)!); + case 141: + return HmacRequest.decode(readValue(buffer)!); + case 142: + return HmacResponse.decode(readValue(buffer)!); + case 143: + return Pbkdf2Request.decode(readValue(buffer)!); + case 144: + return Pbkdf2Response.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestNativeCryptoAPI { + static const MessageCodec codec = _TestNativeCryptoAPICodec(); + + HashResponse hash(HashRequest request); + + HmacResponse hmac(HmacRequest request); + + GenerateSecureRandomResponse generateSecureRandom(GenerateSecureRandomRequest request); + + Pbkdf2Response pbkdf2(Pbkdf2Request request); + + EncryptResponse encrypt(EncryptRequest request); + + DecryptResponse decrypt(DecryptRequest request); + + EncryptFileResponse encryptFile(EncryptFileRequest request); + + DecryptFileResponse decryptFile(DecryptFileRequest request); + + EncryptResponse encryptWithIV(EncryptWithIVRequest request); + + static void setup(TestNativeCryptoAPI? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.hash', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hash was null.'); + final List args = (message as List?)!; + final HashRequest? arg_request = (args[0] as HashRequest?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hash was null, expected non-null HashRequest.'); + final HashResponse output = api.hash(arg_request!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.hmac', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hmac was null.'); + final List args = (message as List?)!; + final HmacRequest? arg_request = (args[0] as HmacRequest?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hmac was null, expected non-null HmacRequest.'); + final HmacResponse output = api.hmac(arg_request!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom was null.'); + final List args = (message as List?)!; + final GenerateSecureRandomRequest? arg_request = (args[0] as GenerateSecureRandomRequest?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom was null, expected non-null GenerateSecureRandomRequest.'); + final GenerateSecureRandomResponse output = api.generateSecureRandom(arg_request!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.pbkdf2', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null.'); + final List args = (message as List?)!; + final Pbkdf2Request? arg_request = (args[0] as Pbkdf2Request?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null, expected non-null Pbkdf2Request.'); + final Pbkdf2Response output = api.pbkdf2(arg_request!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encrypt', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encrypt was null.'); + final List args = (message as List?)!; + final EncryptRequest? arg_request = (args[0] as EncryptRequest?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encrypt was null, expected non-null EncryptRequest.'); + final EncryptResponse output = api.encrypt(arg_request!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.decrypt', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decrypt was null.'); + final List args = (message as List?)!; + final DecryptRequest? arg_request = (args[0] as DecryptRequest?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decrypt was null, expected non-null DecryptRequest.'); + final DecryptResponse output = api.decrypt(arg_request!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptFile', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFile was null.'); + final List args = (message as List?)!; + final EncryptFileRequest? arg_request = (args[0] as EncryptFileRequest?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFile was null, expected non-null EncryptFileRequest.'); + final EncryptFileResponse output = api.encryptFile(arg_request!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.decryptFile', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decryptFile was null.'); + final List args = (message as List?)!; + final DecryptFileRequest? arg_request = (args[0] as DecryptFileRequest?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decryptFile was null, expected non-null DecryptFileRequest.'); + final DecryptFileResponse output = api.decryptFile(arg_request!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV was null.'); + final List args = (message as List?)!; + final EncryptWithIVRequest? arg_request = (args[0] as EncryptWithIVRequest?); + assert(arg_request != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV was null, expected non-null EncryptWithIVRequest.'); + final EncryptResponse output = api.encryptWithIV(arg_request!); + return [output]; + }); + } + } + } +} diff --git a/packages/native_crypto_platform_interface/lib/src/platform_interface/native_crypto_platform.dart b/packages/native_crypto_platform_interface/lib/src/platform_interface/native_crypto_platform.dart deleted file mode 100644 index b09c0d3..0000000 --- a/packages/native_crypto_platform_interface/lib/src/platform_interface/native_crypto_platform.dart +++ /dev/null @@ -1,92 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: native_crypto_platform_interface.dart -// Created Date: 25/12/2021 16:43:49 -// Last Modified: 25/05/2022 22:11:02 -// ----- -// Copyright (c) 2021 - -import 'dart:typed_data'; - -import 'package:native_crypto_platform_interface/src/method_channel/method_channel_native_crypto.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -/// The interface that implementations of path_provider must implement. -/// -/// Platform implementations should extend this class rather than implement -/// it as `NativeCrypto` does not consider newly added methods to be -/// breaking changes. Extending this class (using `extends`) ensures -/// that the subclass will get the default implementation, while platform -/// implementations that `implements` this interface will be -/// broken by newly added [NativeCryptoPlatform] methods. -abstract class NativeCryptoPlatform extends PlatformInterface { - /// Constructs a NativeCryptoPlatform. - NativeCryptoPlatform() : super(token: _token); - - static final Object _token = Object(); - - static NativeCryptoPlatform _instance = MethodChannelNativeCrypto(); - - /// The default instance of [NativeCryptoPlatform] to use. - /// - /// Defaults to [MethodChannelNativeCrypto]. - static NativeCryptoPlatform get instance => _instance; - - /// Platform-specific plugins should set this with their own platform-specific - /// class that extends [NativeCryptoPlatform] when they register themselves. - static set instance(NativeCryptoPlatform instance) { - PlatformInterface.verify(instance, _token); - _instance = instance; - } - - Future digest(Uint8List data, String algorithm) { - throw UnimplementedError('digest is not implemented'); - } - - Future generateSecretKey(int bitsCount) { - throw UnimplementedError('generateSecretKey is not implemented'); - } - - Future pbkdf2( - String password, - String salt, - int keyBytesCount, - int iterations, - String algorithm, - ) { - throw UnimplementedError('pbkdf2 is not implemented'); - } - - Future?> encryptAsList( - Uint8List data, - Uint8List key, - String algorithm, - ) { - throw UnimplementedError('encryptAsList is not implemented'); - } - - Future decryptAsList( - List data, - Uint8List key, - String algorithm, - ) { - throw UnimplementedError('decryptAsList is not implemented'); - } - - Future encrypt( - Uint8List data, - Uint8List key, - String algorithm, - ) { - throw UnimplementedError('encrypt is not implemented'); - } - - Future decrypt( - Uint8List data, - Uint8List key, - String algorithm, - ) { - throw UnimplementedError('decrypt is not implemented'); - } -} diff --git a/packages/native_crypto_platform_interface/lib/src/utils/exception.dart b/packages/native_crypto_platform_interface/lib/src/utils/exception.dart deleted file mode 100644 index b46bb4a..0000000 --- a/packages/native_crypto_platform_interface/lib/src/utils/exception.dart +++ /dev/null @@ -1,126 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: exception.dart -// Created Date: 24/05/2022 18:54:48 -// Last Modified: 26/05/2022 20:36:04 -// ----- -// Copyright (c) 2022 - -// ignore_for_file: constant_identifier_names - -import 'dart:developer'; - -import 'package:flutter/services.dart'; - -enum NativeCryptoExceptionCode { - unknown, - not_implemented, - invalid_argument, - invalid_key, - invalid_key_length, - invalid_algorithm, - invalid_padding, - invalid_mode, - invalid_cipher, - invalid_data, - platform_not_supported, - platform_throws, - platform_returned_invalid_data, - platform_returned_empty_data, - platform_returned_null; - - String get code => toString().split('.').last.toLowerCase(); -} - -class NativeCryptoException implements Exception { - NativeCryptoException({ - this.message, - String? code, - this.stackTrace, - }) : code = code ?? NativeCryptoExceptionCode.unknown.code; - - /// The long form message of the exception. - final String? message; - - /// The optional code to accommodate the message. - final String code; - - /// The stack trace which provides information to the user about the call - /// sequence that triggered an exception - final StackTrace? stackTrace; - - @override - String toString() { - String output = '[NativeCryptoException/$code] $message'; - - if (stackTrace != null) { - output += '\n\n${stackTrace.toString()}'; - } - - return output; - } - - /// Catches a [PlatformException] and returns an [Exception]. - /// - /// If the [Exception] is a [PlatformException], - /// a [NativeCryptoException] is returned. - static Never convertPlatformException( - Object exception, - StackTrace stackTrace, - ) { - log(exception.toString()); - if (exception is! Exception || exception is! PlatformException) { - Error.throwWithStackTrace(exception, stackTrace); - } - - Error.throwWithStackTrace( - NativeCryptoException.fromPlatformException(exception, stackTrace), - stackTrace, - ); - } - - /// Converts a [PlatformException] into a [NativeCryptoException]. - /// - /// A [PlatformException] can only be converted to a [NativeCryptoException] - /// if the `details` of the exception exist. - factory NativeCryptoException.fromPlatformException( - PlatformException platformException, - StackTrace stackTrace, - ) { - final Map? details = platformException.details != null - ? Map.from( - platformException.details as Map, - ) - : null; - - String code = NativeCryptoExceptionCode.unknown.code; - String message = platformException.message ?? ''; - - if (details != null) { - code = details['code'] ?? code; - message = details['message'] ?? message; - } - - return NativeCryptoException( - message: message, - code: code, - stackTrace: stackTrace, - ); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (identical(this, other)) return true; - - return other is NativeCryptoException && - other.message == message && - other.code == code && - other.stackTrace == stackTrace; - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => message.hashCode ^ code.hashCode ^ stackTrace.hashCode; -} diff --git a/packages/native_crypto_platform_interface/pigeons/copyright_header.txt b/packages/native_crypto_platform_interface/pigeons/copyright_header.txt new file mode 100644 index 0000000..2701758 --- /dev/null +++ b/packages/native_crypto_platform_interface/pigeons/copyright_header.txt @@ -0,0 +1,6 @@ +Copyright 2019-2023 Hugo Pointcheval + +Use of this source code is governed by an MIT-style +license that can be found in the LICENSE file or at +https://opensource.org/licenses/MIT. +-- \ No newline at end of file diff --git a/packages/native_crypto_platform_interface/pigeons/messages.dart b/packages/native_crypto_platform_interface/pigeons/messages.dart new file mode 100644 index 0000000..e2802cd --- /dev/null +++ b/packages/native_crypto_platform_interface/pigeons/messages.dart @@ -0,0 +1,222 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + copyrightHeader: 'pigeons/copyright_header.txt', + dartOut: 'lib/src/pigeon/messages.pigeon.dart', + // We export in the lib folder to expose the class to other packages. + dartTestOut: 'lib/src/pigeon/test_api.dart', + javaOut: + '../native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java', + javaOptions: JavaOptions( + package: 'fr.pointcheval.native_crypto_android', + className: 'GeneratedAndroidNativeCrypto', + ), + objcHeaderOut: '../native_crypto_ios/ios/Classes/Public/messages.g.h', + objcSourceOut: '../native_crypto_ios/ios/Classes/messages.g.m', + ), +) +class HashRequest { + const HashRequest({ + this.data, + this.algorithm, + }); + + final Uint8List? data; + final String? algorithm; +} + +class HashResponse { + const HashResponse({ + this.hash, + }); + + final Uint8List? hash; +} + +class HmacRequest { + const HmacRequest({ + this.data, + this.key, + this.algorithm, + }); + + final Uint8List? data; + final Uint8List? key; + final String? algorithm; +} + +class HmacResponse { + const HmacResponse({ + this.hmac, + }); + + final Uint8List? hmac; +} + +class GenerateSecureRandomRequest { + const GenerateSecureRandomRequest({ + this.length, + }); + + final int? length; +} + +class GenerateSecureRandomResponse { + const GenerateSecureRandomResponse({ + this.random, + }); + + final Uint8List? random; +} + +class Pbkdf2Request { + const Pbkdf2Request({ + this.password, + this.salt, + this.length, + this.iterations, + this.hashAlgorithm, + }); + + final Uint8List? password; + final Uint8List? salt; + final int? length; + final int? iterations; + final String? hashAlgorithm; +} + +class Pbkdf2Response { + const Pbkdf2Response({ + this.key, + }); + + final Uint8List? key; +} + +class EncryptRequest { + const EncryptRequest({ + this.plainText, + this.key, + this.algorithm, + }); + + final Uint8List? plainText; + final Uint8List? key; + final String? algorithm; +} + +class EncryptResponse { + const EncryptResponse({ + this.cipherText, + }); + + final Uint8List? cipherText; +} + +class DecryptRequest { + const DecryptRequest({ + this.cipherText, + this.key, + this.algorithm, + }); + + final Uint8List? cipherText; + final Uint8List? key; + final String? algorithm; +} + +class DecryptResponse { + const DecryptResponse({ + this.plainText, + }); + + final Uint8List? plainText; +} + +class EncryptFileRequest { + const EncryptFileRequest({ + this.plainTextPath, + this.cipherTextPath, + this.key, + this.algorithm, + }); + + final String? plainTextPath; + final String? cipherTextPath; + final Uint8List? key; + final String? algorithm; +} + +class EncryptFileResponse { + const EncryptFileResponse({ + this.success, + }); + + final bool? success; +} + +class DecryptFileRequest { + const DecryptFileRequest({ + this.cipherTextPath, + this.plainTextPath, + this.key, + this.algorithm, + }); + + final String? cipherTextPath; + final String? plainTextPath; + final Uint8List? key; + final String? algorithm; +} + +class DecryptFileResponse { + const DecryptFileResponse({ + this.success, + }); + + final bool? success; +} + +class EncryptWithIVRequest { + const EncryptWithIVRequest({ + this.plainText, + this.iv, + this.key, + this.algorithm, + }); + + final Uint8List? plainText; + final Uint8List? iv; + final Uint8List? key; + final String? algorithm; +} + +@HostApi(dartHostTestHandler: 'TestNativeCryptoAPI') +abstract class NativeCryptoAPI { + HashResponse hash(HashRequest request); + HmacResponse hmac(HmacRequest request); + + GenerateSecureRandomResponse generateSecureRandom( + GenerateSecureRandomRequest request, + ); + + Pbkdf2Response pbkdf2(Pbkdf2Request request); + + EncryptResponse encrypt(EncryptRequest request); + + DecryptResponse decrypt(DecryptRequest request); + + EncryptFileResponse encryptFile(EncryptFileRequest request); + + DecryptFileResponse decryptFile(DecryptFileRequest request); + + EncryptResponse encryptWithIV(EncryptWithIVRequest request); +} diff --git a/packages/native_crypto_platform_interface/pubspec.yaml b/packages/native_crypto_platform_interface/pubspec.yaml index 21ec587..c3f9532 100644 --- a/packages/native_crypto_platform_interface/pubspec.yaml +++ b/packages/native_crypto_platform_interface/pubspec.yaml @@ -7,19 +7,19 @@ environment: flutter: ">=2.5.0" dependencies: - flutter: - sdk: flutter + equatable: ^2.0.5 + flutter: { sdk: flutter } plugin_platform_interface: ^2.1.3 dev_dependencies: - flutter_test: - sdk: flutter + flutter_test: { sdk: flutter } mockito: ^5.3.2 + pigeon: ^9.0.0 wyatt_analysis: hosted: url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ name: wyatt_analysis - version: 2.3.0 \ No newline at end of file + version: 2.4.0 diff --git a/packages/native_crypto_platform_interface/test/method_channel/method_channel_native_crypto_test.dart b/packages/native_crypto_platform_interface/test/method_channel/method_channel_native_crypto_test.dart deleted file mode 100644 index 28c01a1..0000000 --- a/packages/native_crypto_platform_interface/test/method_channel/method_channel_native_crypto_test.dart +++ /dev/null @@ -1,175 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: method_channel_native_crypto_test.dart -// Created Date: 25/05/2022 22:47:41 -// Last Modified: 25/05/2022 23:22:44 -// ----- -// Copyright (c) 2022 - -import 'dart:typed_data'; - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:native_crypto_platform_interface/src/method_channel/method_channel_native_crypto.dart'; - -void main() { - TestWidgetsFlutterBinding - .ensureInitialized(); // Required for setMockMethodCallHandler - - group('$MethodChannelNativeCrypto', () { - const MethodChannel channel = - MethodChannel('plugins.hugop.cl/native_crypto'); - final List log = []; - final MethodChannelNativeCrypto nativeCrypto = MethodChannelNativeCrypto(); - - TestDefaultBinaryMessengerBinding.instance?.defaultBinaryMessenger - .setMockMethodCallHandler(channel, (MethodCall call) async { - log.add(call); - return null; - }); - - // Run after each test. - tearDown(log.clear); - - test('digest', () async { - await nativeCrypto.digest(Uint8List(0), 'sha256'); - expect( - log, - [ - isMethodCall( - 'digest', - arguments: { - 'data': Uint8List(0), - 'algorithm': 'sha256', - }, - ), - ], - ); - }); - - test('generateSecretKey', () async { - await nativeCrypto.generateSecretKey(256); - expect( - log, - [ - isMethodCall( - 'generateSecretKey', - arguments: { - 'bitsCount': 256, - }, - ), - ], - ); - }); - - test('pbkdf2', () async { - await nativeCrypto.pbkdf2( - 'password', - 'salt', - 32, - 10000, - 'sha256', - ); - expect( - log, - [ - isMethodCall( - 'pbkdf2', - arguments: { - 'password': 'password', - 'salt': 'salt', - 'keyBytesCount': 32, - 'iterations': 10000, - 'algorithm': 'sha256', - }, - ), - ], - ); - }); - - test('encryptAsList', () async { - await nativeCrypto.encryptAsList( - Uint8List(0), - Uint8List(0), - 'aes', - ); - expect( - log, - [ - isMethodCall( - 'encryptAsList', - arguments: { - 'data': Uint8List(0), - 'key': Uint8List(0), - 'algorithm': 'aes', - }, - ), - ], - ); - }); - - test('decryptAsList', () async { - await nativeCrypto.decryptAsList( - [Uint8List(0)], - Uint8List(0), - 'aes', - ); - expect( - log, - [ - isMethodCall( - 'decryptAsList', - arguments: { - 'data': [Uint8List(0)], - 'key': Uint8List(0), - 'algorithm': 'aes', - }, - ), - ], - ); - }); - - test('encrypt', () async { - await nativeCrypto.encrypt( - Uint8List(0), - Uint8List(0), - 'aes', - ); - expect( - log, - [ - isMethodCall( - 'encrypt', - arguments: { - 'data': Uint8List(0), - 'key': Uint8List(0), - 'algorithm': 'aes', - }, - ), - ], - ); - }); - - test('decrypt', () async { - await nativeCrypto.decrypt( - Uint8List(0), - Uint8List(0), - 'aes', - ); - expect( - log, - [ - isMethodCall( - 'decrypt', - arguments: { - 'data': Uint8List(0), - 'key': Uint8List(0), - 'algorithm': 'aes', - }, - ), - ], - ); - }); - }); -} diff --git a/packages/native_crypto_platform_interface/test/native_crypto_exception_test.dart b/packages/native_crypto_platform_interface/test/native_crypto_exception_test.dart new file mode 100644 index 0000000..0e8cbb0 --- /dev/null +++ b/packages/native_crypto_platform_interface/test/native_crypto_exception_test.dart @@ -0,0 +1,72 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto_platform_interface/src/core/enums/exception_code.dart'; +import 'package:native_crypto_platform_interface/src/core/exceptions/exception.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('$NativeCryptoException', () { + test('should return a formatted message with only code', () async { + const e = NativeCryptoException( + code: NativeCryptoExceptionCode.unknownError, + ); + + expect(e.toString(), '[NativeCrypto/unknown_error]'); + }); + + test('should return a formatted message', () async { + const e = NativeCryptoException( + code: NativeCryptoExceptionCode.unknownError, + message: 'foo', + ); + + expect(e.toString(), '[NativeCrypto/unknown_error] foo'); + }); + + test('should return a formatted message with a stack trace', () async { + const e = NativeCryptoException( + code: NativeCryptoExceptionCode.unknownError, + message: 'foo', + ); + + expect(e.toString(), '[NativeCrypto/unknown_error] foo'); + }); + + test('should return a formatted message with a stack trace', () async { + final e = NativeCryptoException( + code: NativeCryptoExceptionCode.unknownError, + message: 'foo', + stackTrace: StackTrace.current, + ); + + // Anything with a stack trace adds 2 blanks lines following the message. + expect(e.toString(), startsWith('[NativeCrypto/unknown_error] foo\n\n')); + }); + + test('should override the == operator', () async { + const e1 = NativeCryptoException( + code: NativeCryptoExceptionCode.unknownError, + message: 'foo', + ); + + const e2 = NativeCryptoException( + code: NativeCryptoExceptionCode.unknownError, + message: 'foo', + ); + + const e3 = NativeCryptoException( + code: NativeCryptoExceptionCode.unknownError, + message: 'foo', + ); + + expect(e1 == e2, true); + expect(e1 != e3, false); + }); + }); +} diff --git a/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart b/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart index acd0f9d..8c1c890 100644 --- a/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart +++ b/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart @@ -1,43 +1,42 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: native_crypto_platform_test.dart -// Created Date: 25/05/2022 21:43:25 -// Last Modified: 25/05/2022 23:26:18 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. -import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; -import 'package:native_crypto_platform_interface/src/platform_interface/native_crypto_platform.dart'; +import 'package:native_crypto_platform_interface/src/implementations/basic_message_channel_native_crypto.dart'; +import 'package:native_crypto_platform_interface/src/implementations/method_channel_native_crypto.dart'; +import 'package:native_crypto_platform_interface/src/interface/native_crypto_platform.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +class ImplementsNativeCryptoPlatform + // ignore: prefer_mixin + with Mock + implements NativeCryptoPlatform {} + +class ExtendsNativeCryptoPlatform extends NativeCryptoPlatform {} + +class NativeCryptoMockPlatform extends Mock + with + // ignore: prefer_mixin, plugin_platform_interface needs to migrate to use `mixin` + MockPlatformInterfaceMixin + implements + NativeCryptoPlatform {} + void main() { - late ExtendsNativeCryptoPlatform nativeCryptoPlatform; + TestWidgetsFlutterBinding.ensureInitialized(); group('$NativeCryptoPlatform', () { - setUpAll(() { - nativeCryptoPlatform = ExtendsNativeCryptoPlatform(); - }); - test('Constructor', () { - expect(nativeCryptoPlatform, isA()); - expect(nativeCryptoPlatform, isA()); + // should allow read of default app from native + test('$MethodChannelNativeCrypto is the default instance', () { + expect(NativeCryptoPlatform.instance, isA()); }); - test('get.instance', () { - expect( - NativeCryptoPlatform.instance, - isA(), - ); - }); - test('set.instance', () { + test('Can be extended', () { NativeCryptoPlatform.instance = ExtendsNativeCryptoPlatform(); - expect( - NativeCryptoPlatform.instance, - isA(), - ); }); test('Cannot be implemented with `implements`', () { @@ -45,122 +44,19 @@ void main() { () { NativeCryptoPlatform.instance = ImplementsNativeCryptoPlatform(); }, - throwsA(isInstanceOf()), + throwsA(anything), ); }); test('Can be mocked with `implements`', () { - final MockNativeCryptoPlatform mock = MockNativeCryptoPlatform(); + final NativeCryptoMockPlatform mock = NativeCryptoMockPlatform(); NativeCryptoPlatform.instance = mock; }); - test('Can be extended', () { - NativeCryptoPlatform.instance = ExtendsNativeCryptoPlatform(); - }); - - test('throws if .digest() not implemented', () async { - await expectLater( - () => nativeCryptoPlatform.digest(Uint8List(0), 'sha256'), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'digest is not implemented', - ), - ), - ); - }); - - test('throws if .generateSecretKey() not implemented', () async { - await expectLater( - () => nativeCryptoPlatform.generateSecretKey(256), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'generateSecretKey is not implemented', - ), - ), - ); - }); - - test('throws if .pbkdf2() not implemented', () async { - await expectLater( - () => nativeCryptoPlatform.pbkdf2('password', 'salt', 0, 0, 'sha256'), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'pbkdf2 is not implemented', - ), - ), - ); - }); - - test('throws if .encryptAsList() not implemented', () async { - await expectLater( - () => nativeCryptoPlatform.encryptAsList( - Uint8List(0), - Uint8List(0), - 'aes', - ), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'encryptAsList is not implemented', - ), - ), - ); - }); - - test('throws if .decryptAsList() not implemented', () async { - await expectLater( - () => nativeCryptoPlatform - .decryptAsList([Uint8List(0)], Uint8List(0), 'aes'), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'decryptAsList is not implemented', - ), - ), - ); - }); - - test('throws if .encrypt() not implemented', () async { - await expectLater( - () => nativeCryptoPlatform.encrypt(Uint8List(0), Uint8List(0), 'aes'), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'encrypt is not implemented', - ), - ), - ); - }); - - test('throws if .decrypt() not implemented', () async { - await expectLater( - () => nativeCryptoPlatform.decrypt(Uint8List(0), Uint8List(0), 'aes'), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'decrypt is not implemented', - ), - ), - ); + test('Can set with $BasicMessageChannelNativeCrypto', () { + final BasicMessageChannelNativeCrypto pigeon = + BasicMessageChannelNativeCrypto(); + NativeCryptoPlatform.instance = pigeon; }); }); } - -class ExtendsNativeCryptoPlatform extends NativeCryptoPlatform {} - -class ImplementsNativeCryptoPlatform extends Mock - implements NativeCryptoPlatform {} - -class MockNativeCryptoPlatform extends Mock - with MockPlatformInterfaceMixin - implements NativeCryptoPlatform {} -- 2.47.2 From ccb51adbc485a2e5d15736fdbfcfc7f9ad3a7b81 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 22 Feb 2023 17:31:12 +0100 Subject: [PATCH 05/21] feat(ios): generate pigeon messages --- .../ios/Classes/Public/messages.g.h | 188 ++++ .../ios/Classes/messages.g.m | 868 ++++++++++++++++++ 2 files changed, 1056 insertions(+) create mode 100644 packages/native_crypto_ios/ios/Classes/Public/messages.g.h create mode 100644 packages/native_crypto_ios/ios/Classes/messages.g.m diff --git a/packages/native_crypto_ios/ios/Classes/Public/messages.g.h b/packages/native_crypto_ios/ios/Classes/Public/messages.g.h new file mode 100644 index 0000000..6caeca0 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/Public/messages.g.h @@ -0,0 +1,188 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#import + +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +@class HashRequest; +@class HashResponse; +@class HmacRequest; +@class HmacResponse; +@class GenerateSecureRandomRequest; +@class GenerateSecureRandomResponse; +@class Pbkdf2Request; +@class Pbkdf2Response; +@class EncryptRequest; +@class EncryptResponse; +@class DecryptRequest; +@class DecryptResponse; +@class EncryptFileRequest; +@class EncryptFileResponse; +@class DecryptFileRequest; +@class DecryptFileResponse; +@class EncryptWithIVRequest; + +@interface HashRequest : NSObject ++ (instancetype)makeWithData:(nullable FlutterStandardTypedData *)data + algorithm:(nullable NSString *)algorithm; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * data; +@property(nonatomic, copy, nullable) NSString * algorithm; +@end + +@interface HashResponse : NSObject ++ (instancetype)makeWithHash:(nullable FlutterStandardTypedData *)hash; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * hash; +@end + +@interface HmacRequest : NSObject ++ (instancetype)makeWithData:(nullable FlutterStandardTypedData *)data + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * data; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; +@property(nonatomic, copy, nullable) NSString * algorithm; +@end + +@interface HmacResponse : NSObject ++ (instancetype)makeWithHmac:(nullable FlutterStandardTypedData *)hmac; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * hmac; +@end + +@interface GenerateSecureRandomRequest : NSObject ++ (instancetype)makeWithLength:(nullable NSNumber *)length; +@property(nonatomic, strong, nullable) NSNumber * length; +@end + +@interface GenerateSecureRandomResponse : NSObject ++ (instancetype)makeWithRandom:(nullable FlutterStandardTypedData *)random; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * random; +@end + +@interface Pbkdf2Request : NSObject ++ (instancetype)makeWithPassword:(nullable FlutterStandardTypedData *)password + salt:(nullable FlutterStandardTypedData *)salt + length:(nullable NSNumber *)length + iterations:(nullable NSNumber *)iterations + hashAlgorithm:(nullable NSString *)hashAlgorithm; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * password; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * salt; +@property(nonatomic, strong, nullable) NSNumber * length; +@property(nonatomic, strong, nullable) NSNumber * iterations; +@property(nonatomic, copy, nullable) NSString * hashAlgorithm; +@end + +@interface Pbkdf2Response : NSObject ++ (instancetype)makeWithKey:(nullable FlutterStandardTypedData *)key; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; +@end + +@interface EncryptRequest : NSObject ++ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * plainText; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; +@property(nonatomic, copy, nullable) NSString * algorithm; +@end + +@interface EncryptResponse : NSObject ++ (instancetype)makeWithCipherText:(nullable FlutterStandardTypedData *)cipherText; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * cipherText; +@end + +@interface DecryptRequest : NSObject ++ (instancetype)makeWithCipherText:(nullable FlutterStandardTypedData *)cipherText + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * cipherText; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; +@property(nonatomic, copy, nullable) NSString * algorithm; +@end + +@interface DecryptResponse : NSObject ++ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * plainText; +@end + +@interface EncryptFileRequest : NSObject ++ (instancetype)makeWithPlainTextPath:(nullable NSString *)plainTextPath + cipherTextPath:(nullable NSString *)cipherTextPath + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm; +@property(nonatomic, copy, nullable) NSString * plainTextPath; +@property(nonatomic, copy, nullable) NSString * cipherTextPath; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; +@property(nonatomic, copy, nullable) NSString * algorithm; +@end + +@interface EncryptFileResponse : NSObject ++ (instancetype)makeWithSuccess:(nullable NSNumber *)success; +@property(nonatomic, strong, nullable) NSNumber * success; +@end + +@interface DecryptFileRequest : NSObject ++ (instancetype)makeWithCipherTextPath:(nullable NSString *)cipherTextPath + plainTextPath:(nullable NSString *)plainTextPath + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm; +@property(nonatomic, copy, nullable) NSString * cipherTextPath; +@property(nonatomic, copy, nullable) NSString * plainTextPath; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; +@property(nonatomic, copy, nullable) NSString * algorithm; +@end + +@interface DecryptFileResponse : NSObject ++ (instancetype)makeWithSuccess:(nullable NSNumber *)success; +@property(nonatomic, strong, nullable) NSNumber * success; +@end + +@interface EncryptWithIVRequest : NSObject ++ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText + iv:(nullable FlutterStandardTypedData *)iv + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * plainText; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * iv; +@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; +@property(nonatomic, copy, nullable) NSString * algorithm; +@end + +/// The codec used by NativeCryptoAPI. +NSObject *NativeCryptoAPIGetCodec(void); + +@protocol NativeCryptoAPI +/// @return `nil` only when `error != nil`. +- (nullable HashResponse *)hashRequest:(HashRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable HmacResponse *)hmacRequest:(HmacRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable GenerateSecureRandomResponse *)generateSecureRandomRequest:(GenerateSecureRandomRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable Pbkdf2Response *)pbkdf2Request:(Pbkdf2Request *)request error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable EncryptResponse *)encryptRequest:(EncryptRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable DecryptResponse *)decryptRequest:(DecryptRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable EncryptFileResponse *)encryptFileRequest:(EncryptFileRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable DecryptFileResponse *)decryptFileRequest:(DecryptFileRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable EncryptResponse *)encryptWithIVRequest:(EncryptWithIVRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; +@end + +extern void NativeCryptoAPISetup(id binaryMessenger, NSObject *_Nullable api); + +NS_ASSUME_NONNULL_END diff --git a/packages/native_crypto_ios/ios/Classes/messages.g.m b/packages/native_crypto_ios/ios/Classes/messages.g.m new file mode 100644 index 0000000..75ff6c3 --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/messages.g.m @@ -0,0 +1,868 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#import "messages.g.h" +#import + +#if !__has_feature(objc_arc) +#error File requires ARC to be enabled. +#endif + +static NSArray *wrapResult(id result, FlutterError *error) { + if (error) { + return @[ + error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] + ]; + } + return @[ result ?: [NSNull null] ]; +} +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface HashRequest () ++ (HashRequest *)fromList:(NSArray *)list; ++ (nullable HashRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface HashResponse () ++ (HashResponse *)fromList:(NSArray *)list; ++ (nullable HashResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface HmacRequest () ++ (HmacRequest *)fromList:(NSArray *)list; ++ (nullable HmacRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface HmacResponse () ++ (HmacResponse *)fromList:(NSArray *)list; ++ (nullable HmacResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface GenerateSecureRandomRequest () ++ (GenerateSecureRandomRequest *)fromList:(NSArray *)list; ++ (nullable GenerateSecureRandomRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface GenerateSecureRandomResponse () ++ (GenerateSecureRandomResponse *)fromList:(NSArray *)list; ++ (nullable GenerateSecureRandomResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface Pbkdf2Request () ++ (Pbkdf2Request *)fromList:(NSArray *)list; ++ (nullable Pbkdf2Request *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface Pbkdf2Response () ++ (Pbkdf2Response *)fromList:(NSArray *)list; ++ (nullable Pbkdf2Response *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface EncryptRequest () ++ (EncryptRequest *)fromList:(NSArray *)list; ++ (nullable EncryptRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface EncryptResponse () ++ (EncryptResponse *)fromList:(NSArray *)list; ++ (nullable EncryptResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface DecryptRequest () ++ (DecryptRequest *)fromList:(NSArray *)list; ++ (nullable DecryptRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface DecryptResponse () ++ (DecryptResponse *)fromList:(NSArray *)list; ++ (nullable DecryptResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface EncryptFileRequest () ++ (EncryptFileRequest *)fromList:(NSArray *)list; ++ (nullable EncryptFileRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface EncryptFileResponse () ++ (EncryptFileResponse *)fromList:(NSArray *)list; ++ (nullable EncryptFileResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface DecryptFileRequest () ++ (DecryptFileRequest *)fromList:(NSArray *)list; ++ (nullable DecryptFileRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface DecryptFileResponse () ++ (DecryptFileResponse *)fromList:(NSArray *)list; ++ (nullable DecryptFileResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface EncryptWithIVRequest () ++ (EncryptWithIVRequest *)fromList:(NSArray *)list; ++ (nullable EncryptWithIVRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@implementation HashRequest ++ (instancetype)makeWithData:(nullable FlutterStandardTypedData *)data + algorithm:(nullable NSString *)algorithm { + HashRequest* pigeonResult = [[HashRequest alloc] init]; + pigeonResult.data = data; + pigeonResult.algorithm = algorithm; + return pigeonResult; +} ++ (HashRequest *)fromList:(NSArray *)list { + HashRequest *pigeonResult = [[HashRequest alloc] init]; + pigeonResult.data = GetNullableObjectAtIndex(list, 0); + pigeonResult.algorithm = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable HashRequest *)nullableFromList:(NSArray *)list { + return (list) ? [HashRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.data ?: [NSNull null]), + (self.algorithm ?: [NSNull null]), + ]; +} +@end + +@implementation HashResponse ++ (instancetype)makeWithHash:(nullable FlutterStandardTypedData *)hash { + HashResponse* pigeonResult = [[HashResponse alloc] init]; + pigeonResult.hash = hash; + return pigeonResult; +} ++ (HashResponse *)fromList:(NSArray *)list { + HashResponse *pigeonResult = [[HashResponse alloc] init]; + pigeonResult.hash = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable HashResponse *)nullableFromList:(NSArray *)list { + return (list) ? [HashResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.hash ?: [NSNull null]), + ]; +} +@end + +@implementation HmacRequest ++ (instancetype)makeWithData:(nullable FlutterStandardTypedData *)data + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm { + HmacRequest* pigeonResult = [[HmacRequest alloc] init]; + pigeonResult.data = data; + pigeonResult.key = key; + pigeonResult.algorithm = algorithm; + return pigeonResult; +} ++ (HmacRequest *)fromList:(NSArray *)list { + HmacRequest *pigeonResult = [[HmacRequest alloc] init]; + pigeonResult.data = GetNullableObjectAtIndex(list, 0); + pigeonResult.key = GetNullableObjectAtIndex(list, 1); + pigeonResult.algorithm = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable HmacRequest *)nullableFromList:(NSArray *)list { + return (list) ? [HmacRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.data ?: [NSNull null]), + (self.key ?: [NSNull null]), + (self.algorithm ?: [NSNull null]), + ]; +} +@end + +@implementation HmacResponse ++ (instancetype)makeWithHmac:(nullable FlutterStandardTypedData *)hmac { + HmacResponse* pigeonResult = [[HmacResponse alloc] init]; + pigeonResult.hmac = hmac; + return pigeonResult; +} ++ (HmacResponse *)fromList:(NSArray *)list { + HmacResponse *pigeonResult = [[HmacResponse alloc] init]; + pigeonResult.hmac = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable HmacResponse *)nullableFromList:(NSArray *)list { + return (list) ? [HmacResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.hmac ?: [NSNull null]), + ]; +} +@end + +@implementation GenerateSecureRandomRequest ++ (instancetype)makeWithLength:(nullable NSNumber *)length { + GenerateSecureRandomRequest* pigeonResult = [[GenerateSecureRandomRequest alloc] init]; + pigeonResult.length = length; + return pigeonResult; +} ++ (GenerateSecureRandomRequest *)fromList:(NSArray *)list { + GenerateSecureRandomRequest *pigeonResult = [[GenerateSecureRandomRequest alloc] init]; + pigeonResult.length = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable GenerateSecureRandomRequest *)nullableFromList:(NSArray *)list { + return (list) ? [GenerateSecureRandomRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.length ?: [NSNull null]), + ]; +} +@end + +@implementation GenerateSecureRandomResponse ++ (instancetype)makeWithRandom:(nullable FlutterStandardTypedData *)random { + GenerateSecureRandomResponse* pigeonResult = [[GenerateSecureRandomResponse alloc] init]; + pigeonResult.random = random; + return pigeonResult; +} ++ (GenerateSecureRandomResponse *)fromList:(NSArray *)list { + GenerateSecureRandomResponse *pigeonResult = [[GenerateSecureRandomResponse alloc] init]; + pigeonResult.random = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable GenerateSecureRandomResponse *)nullableFromList:(NSArray *)list { + return (list) ? [GenerateSecureRandomResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.random ?: [NSNull null]), + ]; +} +@end + +@implementation Pbkdf2Request ++ (instancetype)makeWithPassword:(nullable FlutterStandardTypedData *)password + salt:(nullable FlutterStandardTypedData *)salt + length:(nullable NSNumber *)length + iterations:(nullable NSNumber *)iterations + hashAlgorithm:(nullable NSString *)hashAlgorithm { + Pbkdf2Request* pigeonResult = [[Pbkdf2Request alloc] init]; + pigeonResult.password = password; + pigeonResult.salt = salt; + pigeonResult.length = length; + pigeonResult.iterations = iterations; + pigeonResult.hashAlgorithm = hashAlgorithm; + return pigeonResult; +} ++ (Pbkdf2Request *)fromList:(NSArray *)list { + Pbkdf2Request *pigeonResult = [[Pbkdf2Request alloc] init]; + pigeonResult.password = GetNullableObjectAtIndex(list, 0); + pigeonResult.salt = GetNullableObjectAtIndex(list, 1); + pigeonResult.length = GetNullableObjectAtIndex(list, 2); + pigeonResult.iterations = GetNullableObjectAtIndex(list, 3); + pigeonResult.hashAlgorithm = GetNullableObjectAtIndex(list, 4); + return pigeonResult; +} ++ (nullable Pbkdf2Request *)nullableFromList:(NSArray *)list { + return (list) ? [Pbkdf2Request fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.password ?: [NSNull null]), + (self.salt ?: [NSNull null]), + (self.length ?: [NSNull null]), + (self.iterations ?: [NSNull null]), + (self.hashAlgorithm ?: [NSNull null]), + ]; +} +@end + +@implementation Pbkdf2Response ++ (instancetype)makeWithKey:(nullable FlutterStandardTypedData *)key { + Pbkdf2Response* pigeonResult = [[Pbkdf2Response alloc] init]; + pigeonResult.key = key; + return pigeonResult; +} ++ (Pbkdf2Response *)fromList:(NSArray *)list { + Pbkdf2Response *pigeonResult = [[Pbkdf2Response alloc] init]; + pigeonResult.key = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable Pbkdf2Response *)nullableFromList:(NSArray *)list { + return (list) ? [Pbkdf2Response fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.key ?: [NSNull null]), + ]; +} +@end + +@implementation EncryptRequest ++ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm { + EncryptRequest* pigeonResult = [[EncryptRequest alloc] init]; + pigeonResult.plainText = plainText; + pigeonResult.key = key; + pigeonResult.algorithm = algorithm; + return pigeonResult; +} ++ (EncryptRequest *)fromList:(NSArray *)list { + EncryptRequest *pigeonResult = [[EncryptRequest alloc] init]; + pigeonResult.plainText = GetNullableObjectAtIndex(list, 0); + pigeonResult.key = GetNullableObjectAtIndex(list, 1); + pigeonResult.algorithm = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable EncryptRequest *)nullableFromList:(NSArray *)list { + return (list) ? [EncryptRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.plainText ?: [NSNull null]), + (self.key ?: [NSNull null]), + (self.algorithm ?: [NSNull null]), + ]; +} +@end + +@implementation EncryptResponse ++ (instancetype)makeWithCipherText:(nullable FlutterStandardTypedData *)cipherText { + EncryptResponse* pigeonResult = [[EncryptResponse alloc] init]; + pigeonResult.cipherText = cipherText; + return pigeonResult; +} ++ (EncryptResponse *)fromList:(NSArray *)list { + EncryptResponse *pigeonResult = [[EncryptResponse alloc] init]; + pigeonResult.cipherText = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable EncryptResponse *)nullableFromList:(NSArray *)list { + return (list) ? [EncryptResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.cipherText ?: [NSNull null]), + ]; +} +@end + +@implementation DecryptRequest ++ (instancetype)makeWithCipherText:(nullable FlutterStandardTypedData *)cipherText + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm { + DecryptRequest* pigeonResult = [[DecryptRequest alloc] init]; + pigeonResult.cipherText = cipherText; + pigeonResult.key = key; + pigeonResult.algorithm = algorithm; + return pigeonResult; +} ++ (DecryptRequest *)fromList:(NSArray *)list { + DecryptRequest *pigeonResult = [[DecryptRequest alloc] init]; + pigeonResult.cipherText = GetNullableObjectAtIndex(list, 0); + pigeonResult.key = GetNullableObjectAtIndex(list, 1); + pigeonResult.algorithm = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable DecryptRequest *)nullableFromList:(NSArray *)list { + return (list) ? [DecryptRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.cipherText ?: [NSNull null]), + (self.key ?: [NSNull null]), + (self.algorithm ?: [NSNull null]), + ]; +} +@end + +@implementation DecryptResponse ++ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText { + DecryptResponse* pigeonResult = [[DecryptResponse alloc] init]; + pigeonResult.plainText = plainText; + return pigeonResult; +} ++ (DecryptResponse *)fromList:(NSArray *)list { + DecryptResponse *pigeonResult = [[DecryptResponse alloc] init]; + pigeonResult.plainText = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable DecryptResponse *)nullableFromList:(NSArray *)list { + return (list) ? [DecryptResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.plainText ?: [NSNull null]), + ]; +} +@end + +@implementation EncryptFileRequest ++ (instancetype)makeWithPlainTextPath:(nullable NSString *)plainTextPath + cipherTextPath:(nullable NSString *)cipherTextPath + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm { + EncryptFileRequest* pigeonResult = [[EncryptFileRequest alloc] init]; + pigeonResult.plainTextPath = plainTextPath; + pigeonResult.cipherTextPath = cipherTextPath; + pigeonResult.key = key; + pigeonResult.algorithm = algorithm; + return pigeonResult; +} ++ (EncryptFileRequest *)fromList:(NSArray *)list { + EncryptFileRequest *pigeonResult = [[EncryptFileRequest alloc] init]; + pigeonResult.plainTextPath = GetNullableObjectAtIndex(list, 0); + pigeonResult.cipherTextPath = GetNullableObjectAtIndex(list, 1); + pigeonResult.key = GetNullableObjectAtIndex(list, 2); + pigeonResult.algorithm = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable EncryptFileRequest *)nullableFromList:(NSArray *)list { + return (list) ? [EncryptFileRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.plainTextPath ?: [NSNull null]), + (self.cipherTextPath ?: [NSNull null]), + (self.key ?: [NSNull null]), + (self.algorithm ?: [NSNull null]), + ]; +} +@end + +@implementation EncryptFileResponse ++ (instancetype)makeWithSuccess:(nullable NSNumber *)success { + EncryptFileResponse* pigeonResult = [[EncryptFileResponse alloc] init]; + pigeonResult.success = success; + return pigeonResult; +} ++ (EncryptFileResponse *)fromList:(NSArray *)list { + EncryptFileResponse *pigeonResult = [[EncryptFileResponse alloc] init]; + pigeonResult.success = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable EncryptFileResponse *)nullableFromList:(NSArray *)list { + return (list) ? [EncryptFileResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.success ?: [NSNull null]), + ]; +} +@end + +@implementation DecryptFileRequest ++ (instancetype)makeWithCipherTextPath:(nullable NSString *)cipherTextPath + plainTextPath:(nullable NSString *)plainTextPath + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm { + DecryptFileRequest* pigeonResult = [[DecryptFileRequest alloc] init]; + pigeonResult.cipherTextPath = cipherTextPath; + pigeonResult.plainTextPath = plainTextPath; + pigeonResult.key = key; + pigeonResult.algorithm = algorithm; + return pigeonResult; +} ++ (DecryptFileRequest *)fromList:(NSArray *)list { + DecryptFileRequest *pigeonResult = [[DecryptFileRequest alloc] init]; + pigeonResult.cipherTextPath = GetNullableObjectAtIndex(list, 0); + pigeonResult.plainTextPath = GetNullableObjectAtIndex(list, 1); + pigeonResult.key = GetNullableObjectAtIndex(list, 2); + pigeonResult.algorithm = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable DecryptFileRequest *)nullableFromList:(NSArray *)list { + return (list) ? [DecryptFileRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.cipherTextPath ?: [NSNull null]), + (self.plainTextPath ?: [NSNull null]), + (self.key ?: [NSNull null]), + (self.algorithm ?: [NSNull null]), + ]; +} +@end + +@implementation DecryptFileResponse ++ (instancetype)makeWithSuccess:(nullable NSNumber *)success { + DecryptFileResponse* pigeonResult = [[DecryptFileResponse alloc] init]; + pigeonResult.success = success; + return pigeonResult; +} ++ (DecryptFileResponse *)fromList:(NSArray *)list { + DecryptFileResponse *pigeonResult = [[DecryptFileResponse alloc] init]; + pigeonResult.success = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable DecryptFileResponse *)nullableFromList:(NSArray *)list { + return (list) ? [DecryptFileResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.success ?: [NSNull null]), + ]; +} +@end + +@implementation EncryptWithIVRequest ++ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText + iv:(nullable FlutterStandardTypedData *)iv + key:(nullable FlutterStandardTypedData *)key + algorithm:(nullable NSString *)algorithm { + EncryptWithIVRequest* pigeonResult = [[EncryptWithIVRequest alloc] init]; + pigeonResult.plainText = plainText; + pigeonResult.iv = iv; + pigeonResult.key = key; + pigeonResult.algorithm = algorithm; + return pigeonResult; +} ++ (EncryptWithIVRequest *)fromList:(NSArray *)list { + EncryptWithIVRequest *pigeonResult = [[EncryptWithIVRequest alloc] init]; + pigeonResult.plainText = GetNullableObjectAtIndex(list, 0); + pigeonResult.iv = GetNullableObjectAtIndex(list, 1); + pigeonResult.key = GetNullableObjectAtIndex(list, 2); + pigeonResult.algorithm = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable EncryptWithIVRequest *)nullableFromList:(NSArray *)list { + return (list) ? [EncryptWithIVRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + (self.plainText ?: [NSNull null]), + (self.iv ?: [NSNull null]), + (self.key ?: [NSNull null]), + (self.algorithm ?: [NSNull null]), + ]; +} +@end + +@interface NativeCryptoAPICodecReader : FlutterStandardReader +@end +@implementation NativeCryptoAPICodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 128: + return [DecryptFileRequest fromList:[self readValue]]; + case 129: + return [DecryptFileResponse fromList:[self readValue]]; + case 130: + return [DecryptRequest fromList:[self readValue]]; + case 131: + return [DecryptResponse fromList:[self readValue]]; + case 132: + return [EncryptFileRequest fromList:[self readValue]]; + case 133: + return [EncryptFileResponse fromList:[self readValue]]; + case 134: + return [EncryptRequest fromList:[self readValue]]; + case 135: + return [EncryptResponse fromList:[self readValue]]; + case 136: + return [EncryptWithIVRequest fromList:[self readValue]]; + case 137: + return [GenerateSecureRandomRequest fromList:[self readValue]]; + case 138: + return [GenerateSecureRandomResponse fromList:[self readValue]]; + case 139: + return [HashRequest fromList:[self readValue]]; + case 140: + return [HashResponse fromList:[self readValue]]; + case 141: + return [HmacRequest fromList:[self readValue]]; + case 142: + return [HmacResponse fromList:[self readValue]]; + case 143: + return [Pbkdf2Request fromList:[self readValue]]; + case 144: + return [Pbkdf2Response fromList:[self readValue]]; + default: + return [super readValueOfType:type]; + } +} +@end + +@interface NativeCryptoAPICodecWriter : FlutterStandardWriter +@end +@implementation NativeCryptoAPICodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[DecryptFileRequest class]]) { + [self writeByte:128]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[DecryptFileResponse class]]) { + [self writeByte:129]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[DecryptRequest class]]) { + [self writeByte:130]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[DecryptResponse class]]) { + [self writeByte:131]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[EncryptFileRequest class]]) { + [self writeByte:132]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[EncryptFileResponse class]]) { + [self writeByte:133]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[EncryptRequest class]]) { + [self writeByte:134]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[EncryptResponse class]]) { + [self writeByte:135]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[EncryptWithIVRequest class]]) { + [self writeByte:136]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[GenerateSecureRandomRequest class]]) { + [self writeByte:137]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[GenerateSecureRandomResponse class]]) { + [self writeByte:138]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[HashRequest class]]) { + [self writeByte:139]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[HashResponse class]]) { + [self writeByte:140]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[HmacRequest class]]) { + [self writeByte:141]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[HmacResponse class]]) { + [self writeByte:142]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[Pbkdf2Request class]]) { + [self writeByte:143]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[Pbkdf2Response class]]) { + [self writeByte:144]; + [self writeValue:[value toList]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface NativeCryptoAPICodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation NativeCryptoAPICodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[NativeCryptoAPICodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[NativeCryptoAPICodecReader alloc] initWithData:data]; +} +@end + +NSObject *NativeCryptoAPIGetCodec() { + static FlutterStandardMessageCodec *sSharedObject = nil; + static dispatch_once_t sPred = 0; + dispatch_once(&sPred, ^{ + NativeCryptoAPICodecReaderWriter *readerWriter = [[NativeCryptoAPICodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} + +void NativeCryptoAPISetup(id binaryMessenger, NSObject *api) { + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.hash" + binaryMessenger:binaryMessenger + codec:NativeCryptoAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(hashRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(hashRequest:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + HashRequest *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + HashResponse *output = [api hashRequest:arg_request error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.hmac" + binaryMessenger:binaryMessenger + codec:NativeCryptoAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(hmacRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(hmacRequest:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + HmacRequest *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + HmacResponse *output = [api hmacRequest:arg_request error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom" + binaryMessenger:binaryMessenger + codec:NativeCryptoAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(generateSecureRandomRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(generateSecureRandomRequest:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + GenerateSecureRandomRequest *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + GenerateSecureRandomResponse *output = [api generateSecureRandomRequest:arg_request error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.pbkdf2" + binaryMessenger:binaryMessenger + codec:NativeCryptoAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(pbkdf2Request:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(pbkdf2Request:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + Pbkdf2Request *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + Pbkdf2Response *output = [api pbkdf2Request:arg_request error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.encrypt" + binaryMessenger:binaryMessenger + codec:NativeCryptoAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(encryptRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(encryptRequest:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + EncryptRequest *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + EncryptResponse *output = [api encryptRequest:arg_request error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.decrypt" + binaryMessenger:binaryMessenger + codec:NativeCryptoAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(decryptRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(decryptRequest:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + DecryptRequest *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + DecryptResponse *output = [api decryptRequest:arg_request error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.encryptFile" + binaryMessenger:binaryMessenger + codec:NativeCryptoAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(encryptFileRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(encryptFileRequest:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + EncryptFileRequest *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + EncryptFileResponse *output = [api encryptFileRequest:arg_request error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.decryptFile" + binaryMessenger:binaryMessenger + codec:NativeCryptoAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(decryptFileRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(decryptFileRequest:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + DecryptFileRequest *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + DecryptFileResponse *output = [api decryptFileRequest:arg_request error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV" + binaryMessenger:binaryMessenger + codec:NativeCryptoAPIGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(encryptWithIVRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(encryptWithIVRequest:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + EncryptWithIVRequest *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + EncryptResponse *output = [api encryptWithIVRequest:arg_request error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} -- 2.47.2 From d8cf8dddc4e60b49cf7d5959f4690273d238e5a6 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 22 Feb 2023 17:31:58 +0100 Subject: [PATCH 06/21] feat(android): generate pigeon messages --- .../GeneratedAndroidNativeCrypto.java | 1579 +++++++++++++++++ 1 file changed, 1579 insertions(+) create mode 100644 packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java diff --git a/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java b/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java new file mode 100644 index 0000000..d48520e --- /dev/null +++ b/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java @@ -0,0 +1,1579 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.0.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package fr.pointcheval.native_crypto_android; + +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MessageCodec; +import io.flutter.plugin.common.StandardMessageCodec; +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** Generated class from Pigeon. */ +@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) +public class GeneratedAndroidNativeCrypto { + @NonNull + private static ArrayList wrapError(@NonNull Throwable exception) { + ArrayList errorList = new ArrayList(3); + errorList.add(exception.toString()); + errorList.add(exception.getClass().getSimpleName()); + errorList.add( + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + return errorList; + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class HashRequest { + private @Nullable byte[] data; + + public @Nullable byte[] getData() { + return data; + } + + public void setData(@Nullable byte[] setterArg) { + this.data = setterArg; + } + + private @Nullable String algorithm; + + public @Nullable String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] data; + + public @NonNull Builder setData(@Nullable byte[] setterArg) { + this.data = setterArg; + return this; + } + + private @Nullable String algorithm; + + public @NonNull Builder setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + return this; + } + + public @NonNull HashRequest build() { + HashRequest pigeonReturn = new HashRequest(); + pigeonReturn.setData(data); + pigeonReturn.setAlgorithm(algorithm); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(2); + toListResult.add(data); + toListResult.add(algorithm); + return toListResult; + } + + static @NonNull HashRequest fromList(@NonNull ArrayList list) { + HashRequest pigeonResult = new HashRequest(); + Object data = list.get(0); + pigeonResult.setData((byte[]) data); + Object algorithm = list.get(1); + pigeonResult.setAlgorithm((String) algorithm); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class HashResponse { + private @Nullable byte[] hash; + + public @Nullable byte[] getHash() { + return hash; + } + + public void setHash(@Nullable byte[] setterArg) { + this.hash = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] hash; + + public @NonNull Builder setHash(@Nullable byte[] setterArg) { + this.hash = setterArg; + return this; + } + + public @NonNull HashResponse build() { + HashResponse pigeonReturn = new HashResponse(); + pigeonReturn.setHash(hash); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(hash); + return toListResult; + } + + static @NonNull HashResponse fromList(@NonNull ArrayList list) { + HashResponse pigeonResult = new HashResponse(); + Object hash = list.get(0); + pigeonResult.setHash((byte[]) hash); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class HmacRequest { + private @Nullable byte[] data; + + public @Nullable byte[] getData() { + return data; + } + + public void setData(@Nullable byte[] setterArg) { + this.data = setterArg; + } + + private @Nullable byte[] key; + + public @Nullable byte[] getKey() { + return key; + } + + public void setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + } + + private @Nullable String algorithm; + + public @Nullable String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] data; + + public @NonNull Builder setData(@Nullable byte[] setterArg) { + this.data = setterArg; + return this; + } + + private @Nullable byte[] key; + + public @NonNull Builder setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + return this; + } + + private @Nullable String algorithm; + + public @NonNull Builder setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + return this; + } + + public @NonNull HmacRequest build() { + HmacRequest pigeonReturn = new HmacRequest(); + pigeonReturn.setData(data); + pigeonReturn.setKey(key); + pigeonReturn.setAlgorithm(algorithm); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(3); + toListResult.add(data); + toListResult.add(key); + toListResult.add(algorithm); + return toListResult; + } + + static @NonNull HmacRequest fromList(@NonNull ArrayList list) { + HmacRequest pigeonResult = new HmacRequest(); + Object data = list.get(0); + pigeonResult.setData((byte[]) data); + Object key = list.get(1); + pigeonResult.setKey((byte[]) key); + Object algorithm = list.get(2); + pigeonResult.setAlgorithm((String) algorithm); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class HmacResponse { + private @Nullable byte[] hmac; + + public @Nullable byte[] getHmac() { + return hmac; + } + + public void setHmac(@Nullable byte[] setterArg) { + this.hmac = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] hmac; + + public @NonNull Builder setHmac(@Nullable byte[] setterArg) { + this.hmac = setterArg; + return this; + } + + public @NonNull HmacResponse build() { + HmacResponse pigeonReturn = new HmacResponse(); + pigeonReturn.setHmac(hmac); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(hmac); + return toListResult; + } + + static @NonNull HmacResponse fromList(@NonNull ArrayList list) { + HmacResponse pigeonResult = new HmacResponse(); + Object hmac = list.get(0); + pigeonResult.setHmac((byte[]) hmac); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class GenerateSecureRandomRequest { + private @Nullable Long length; + + public @Nullable Long getLength() { + return length; + } + + public void setLength(@Nullable Long setterArg) { + this.length = setterArg; + } + + public static final class Builder { + + private @Nullable Long length; + + public @NonNull Builder setLength(@Nullable Long setterArg) { + this.length = setterArg; + return this; + } + + public @NonNull GenerateSecureRandomRequest build() { + GenerateSecureRandomRequest pigeonReturn = new GenerateSecureRandomRequest(); + pigeonReturn.setLength(length); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(length); + return toListResult; + } + + static @NonNull GenerateSecureRandomRequest fromList(@NonNull ArrayList list) { + GenerateSecureRandomRequest pigeonResult = new GenerateSecureRandomRequest(); + Object length = list.get(0); + pigeonResult.setLength((length == null) ? null : ((length instanceof Integer) ? (Integer) length : (Long) length)); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class GenerateSecureRandomResponse { + private @Nullable byte[] random; + + public @Nullable byte[] getRandom() { + return random; + } + + public void setRandom(@Nullable byte[] setterArg) { + this.random = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] random; + + public @NonNull Builder setRandom(@Nullable byte[] setterArg) { + this.random = setterArg; + return this; + } + + public @NonNull GenerateSecureRandomResponse build() { + GenerateSecureRandomResponse pigeonReturn = new GenerateSecureRandomResponse(); + pigeonReturn.setRandom(random); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(random); + return toListResult; + } + + static @NonNull GenerateSecureRandomResponse fromList(@NonNull ArrayList list) { + GenerateSecureRandomResponse pigeonResult = new GenerateSecureRandomResponse(); + Object random = list.get(0); + pigeonResult.setRandom((byte[]) random); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class Pbkdf2Request { + private @Nullable byte[] password; + + public @Nullable byte[] getPassword() { + return password; + } + + public void setPassword(@Nullable byte[] setterArg) { + this.password = setterArg; + } + + private @Nullable byte[] salt; + + public @Nullable byte[] getSalt() { + return salt; + } + + public void setSalt(@Nullable byte[] setterArg) { + this.salt = setterArg; + } + + private @Nullable Long length; + + public @Nullable Long getLength() { + return length; + } + + public void setLength(@Nullable Long setterArg) { + this.length = setterArg; + } + + private @Nullable Long iterations; + + public @Nullable Long getIterations() { + return iterations; + } + + public void setIterations(@Nullable Long setterArg) { + this.iterations = setterArg; + } + + private @Nullable String hashAlgorithm; + + public @Nullable String getHashAlgorithm() { + return hashAlgorithm; + } + + public void setHashAlgorithm(@Nullable String setterArg) { + this.hashAlgorithm = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] password; + + public @NonNull Builder setPassword(@Nullable byte[] setterArg) { + this.password = setterArg; + return this; + } + + private @Nullable byte[] salt; + + public @NonNull Builder setSalt(@Nullable byte[] setterArg) { + this.salt = setterArg; + return this; + } + + private @Nullable Long length; + + public @NonNull Builder setLength(@Nullable Long setterArg) { + this.length = setterArg; + return this; + } + + private @Nullable Long iterations; + + public @NonNull Builder setIterations(@Nullable Long setterArg) { + this.iterations = setterArg; + return this; + } + + private @Nullable String hashAlgorithm; + + public @NonNull Builder setHashAlgorithm(@Nullable String setterArg) { + this.hashAlgorithm = setterArg; + return this; + } + + public @NonNull Pbkdf2Request build() { + Pbkdf2Request pigeonReturn = new Pbkdf2Request(); + pigeonReturn.setPassword(password); + pigeonReturn.setSalt(salt); + pigeonReturn.setLength(length); + pigeonReturn.setIterations(iterations); + pigeonReturn.setHashAlgorithm(hashAlgorithm); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(5); + toListResult.add(password); + toListResult.add(salt); + toListResult.add(length); + toListResult.add(iterations); + toListResult.add(hashAlgorithm); + return toListResult; + } + + static @NonNull Pbkdf2Request fromList(@NonNull ArrayList list) { + Pbkdf2Request pigeonResult = new Pbkdf2Request(); + Object password = list.get(0); + pigeonResult.setPassword((byte[]) password); + Object salt = list.get(1); + pigeonResult.setSalt((byte[]) salt); + Object length = list.get(2); + pigeonResult.setLength((length == null) ? null : ((length instanceof Integer) ? (Integer) length : (Long) length)); + Object iterations = list.get(3); + pigeonResult.setIterations((iterations == null) ? null : ((iterations instanceof Integer) ? (Integer) iterations : (Long) iterations)); + Object hashAlgorithm = list.get(4); + pigeonResult.setHashAlgorithm((String) hashAlgorithm); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class Pbkdf2Response { + private @Nullable byte[] key; + + public @Nullable byte[] getKey() { + return key; + } + + public void setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] key; + + public @NonNull Builder setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + return this; + } + + public @NonNull Pbkdf2Response build() { + Pbkdf2Response pigeonReturn = new Pbkdf2Response(); + pigeonReturn.setKey(key); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(key); + return toListResult; + } + + static @NonNull Pbkdf2Response fromList(@NonNull ArrayList list) { + Pbkdf2Response pigeonResult = new Pbkdf2Response(); + Object key = list.get(0); + pigeonResult.setKey((byte[]) key); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class EncryptRequest { + private @Nullable byte[] plainText; + + public @Nullable byte[] getPlainText() { + return plainText; + } + + public void setPlainText(@Nullable byte[] setterArg) { + this.plainText = setterArg; + } + + private @Nullable byte[] key; + + public @Nullable byte[] getKey() { + return key; + } + + public void setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + } + + private @Nullable String algorithm; + + public @Nullable String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] plainText; + + public @NonNull Builder setPlainText(@Nullable byte[] setterArg) { + this.plainText = setterArg; + return this; + } + + private @Nullable byte[] key; + + public @NonNull Builder setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + return this; + } + + private @Nullable String algorithm; + + public @NonNull Builder setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + return this; + } + + public @NonNull EncryptRequest build() { + EncryptRequest pigeonReturn = new EncryptRequest(); + pigeonReturn.setPlainText(plainText); + pigeonReturn.setKey(key); + pigeonReturn.setAlgorithm(algorithm); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(3); + toListResult.add(plainText); + toListResult.add(key); + toListResult.add(algorithm); + return toListResult; + } + + static @NonNull EncryptRequest fromList(@NonNull ArrayList list) { + EncryptRequest pigeonResult = new EncryptRequest(); + Object plainText = list.get(0); + pigeonResult.setPlainText((byte[]) plainText); + Object key = list.get(1); + pigeonResult.setKey((byte[]) key); + Object algorithm = list.get(2); + pigeonResult.setAlgorithm((String) algorithm); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class EncryptResponse { + private @Nullable byte[] cipherText; + + public @Nullable byte[] getCipherText() { + return cipherText; + } + + public void setCipherText(@Nullable byte[] setterArg) { + this.cipherText = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] cipherText; + + public @NonNull Builder setCipherText(@Nullable byte[] setterArg) { + this.cipherText = setterArg; + return this; + } + + public @NonNull EncryptResponse build() { + EncryptResponse pigeonReturn = new EncryptResponse(); + pigeonReturn.setCipherText(cipherText); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(cipherText); + return toListResult; + } + + static @NonNull EncryptResponse fromList(@NonNull ArrayList list) { + EncryptResponse pigeonResult = new EncryptResponse(); + Object cipherText = list.get(0); + pigeonResult.setCipherText((byte[]) cipherText); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class DecryptRequest { + private @Nullable byte[] cipherText; + + public @Nullable byte[] getCipherText() { + return cipherText; + } + + public void setCipherText(@Nullable byte[] setterArg) { + this.cipherText = setterArg; + } + + private @Nullable byte[] key; + + public @Nullable byte[] getKey() { + return key; + } + + public void setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + } + + private @Nullable String algorithm; + + public @Nullable String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] cipherText; + + public @NonNull Builder setCipherText(@Nullable byte[] setterArg) { + this.cipherText = setterArg; + return this; + } + + private @Nullable byte[] key; + + public @NonNull Builder setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + return this; + } + + private @Nullable String algorithm; + + public @NonNull Builder setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + return this; + } + + public @NonNull DecryptRequest build() { + DecryptRequest pigeonReturn = new DecryptRequest(); + pigeonReturn.setCipherText(cipherText); + pigeonReturn.setKey(key); + pigeonReturn.setAlgorithm(algorithm); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(3); + toListResult.add(cipherText); + toListResult.add(key); + toListResult.add(algorithm); + return toListResult; + } + + static @NonNull DecryptRequest fromList(@NonNull ArrayList list) { + DecryptRequest pigeonResult = new DecryptRequest(); + Object cipherText = list.get(0); + pigeonResult.setCipherText((byte[]) cipherText); + Object key = list.get(1); + pigeonResult.setKey((byte[]) key); + Object algorithm = list.get(2); + pigeonResult.setAlgorithm((String) algorithm); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class DecryptResponse { + private @Nullable byte[] plainText; + + public @Nullable byte[] getPlainText() { + return plainText; + } + + public void setPlainText(@Nullable byte[] setterArg) { + this.plainText = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] plainText; + + public @NonNull Builder setPlainText(@Nullable byte[] setterArg) { + this.plainText = setterArg; + return this; + } + + public @NonNull DecryptResponse build() { + DecryptResponse pigeonReturn = new DecryptResponse(); + pigeonReturn.setPlainText(plainText); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(plainText); + return toListResult; + } + + static @NonNull DecryptResponse fromList(@NonNull ArrayList list) { + DecryptResponse pigeonResult = new DecryptResponse(); + Object plainText = list.get(0); + pigeonResult.setPlainText((byte[]) plainText); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class EncryptFileRequest { + private @Nullable String plainTextPath; + + public @Nullable String getPlainTextPath() { + return plainTextPath; + } + + public void setPlainTextPath(@Nullable String setterArg) { + this.plainTextPath = setterArg; + } + + private @Nullable String cipherTextPath; + + public @Nullable String getCipherTextPath() { + return cipherTextPath; + } + + public void setCipherTextPath(@Nullable String setterArg) { + this.cipherTextPath = setterArg; + } + + private @Nullable byte[] key; + + public @Nullable byte[] getKey() { + return key; + } + + public void setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + } + + private @Nullable String algorithm; + + public @Nullable String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + } + + public static final class Builder { + + private @Nullable String plainTextPath; + + public @NonNull Builder setPlainTextPath(@Nullable String setterArg) { + this.plainTextPath = setterArg; + return this; + } + + private @Nullable String cipherTextPath; + + public @NonNull Builder setCipherTextPath(@Nullable String setterArg) { + this.cipherTextPath = setterArg; + return this; + } + + private @Nullable byte[] key; + + public @NonNull Builder setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + return this; + } + + private @Nullable String algorithm; + + public @NonNull Builder setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + return this; + } + + public @NonNull EncryptFileRequest build() { + EncryptFileRequest pigeonReturn = new EncryptFileRequest(); + pigeonReturn.setPlainTextPath(plainTextPath); + pigeonReturn.setCipherTextPath(cipherTextPath); + pigeonReturn.setKey(key); + pigeonReturn.setAlgorithm(algorithm); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(4); + toListResult.add(plainTextPath); + toListResult.add(cipherTextPath); + toListResult.add(key); + toListResult.add(algorithm); + return toListResult; + } + + static @NonNull EncryptFileRequest fromList(@NonNull ArrayList list) { + EncryptFileRequest pigeonResult = new EncryptFileRequest(); + Object plainTextPath = list.get(0); + pigeonResult.setPlainTextPath((String) plainTextPath); + Object cipherTextPath = list.get(1); + pigeonResult.setCipherTextPath((String) cipherTextPath); + Object key = list.get(2); + pigeonResult.setKey((byte[]) key); + Object algorithm = list.get(3); + pigeonResult.setAlgorithm((String) algorithm); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class EncryptFileResponse { + private @Nullable Boolean success; + + public @Nullable Boolean getSuccess() { + return success; + } + + public void setSuccess(@Nullable Boolean setterArg) { + this.success = setterArg; + } + + public static final class Builder { + + private @Nullable Boolean success; + + public @NonNull Builder setSuccess(@Nullable Boolean setterArg) { + this.success = setterArg; + return this; + } + + public @NonNull EncryptFileResponse build() { + EncryptFileResponse pigeonReturn = new EncryptFileResponse(); + pigeonReturn.setSuccess(success); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(success); + return toListResult; + } + + static @NonNull EncryptFileResponse fromList(@NonNull ArrayList list) { + EncryptFileResponse pigeonResult = new EncryptFileResponse(); + Object success = list.get(0); + pigeonResult.setSuccess((Boolean) success); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class DecryptFileRequest { + private @Nullable String cipherTextPath; + + public @Nullable String getCipherTextPath() { + return cipherTextPath; + } + + public void setCipherTextPath(@Nullable String setterArg) { + this.cipherTextPath = setterArg; + } + + private @Nullable String plainTextPath; + + public @Nullable String getPlainTextPath() { + return plainTextPath; + } + + public void setPlainTextPath(@Nullable String setterArg) { + this.plainTextPath = setterArg; + } + + private @Nullable byte[] key; + + public @Nullable byte[] getKey() { + return key; + } + + public void setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + } + + private @Nullable String algorithm; + + public @Nullable String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + } + + public static final class Builder { + + private @Nullable String cipherTextPath; + + public @NonNull Builder setCipherTextPath(@Nullable String setterArg) { + this.cipherTextPath = setterArg; + return this; + } + + private @Nullable String plainTextPath; + + public @NonNull Builder setPlainTextPath(@Nullable String setterArg) { + this.plainTextPath = setterArg; + return this; + } + + private @Nullable byte[] key; + + public @NonNull Builder setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + return this; + } + + private @Nullable String algorithm; + + public @NonNull Builder setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + return this; + } + + public @NonNull DecryptFileRequest build() { + DecryptFileRequest pigeonReturn = new DecryptFileRequest(); + pigeonReturn.setCipherTextPath(cipherTextPath); + pigeonReturn.setPlainTextPath(plainTextPath); + pigeonReturn.setKey(key); + pigeonReturn.setAlgorithm(algorithm); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(4); + toListResult.add(cipherTextPath); + toListResult.add(plainTextPath); + toListResult.add(key); + toListResult.add(algorithm); + return toListResult; + } + + static @NonNull DecryptFileRequest fromList(@NonNull ArrayList list) { + DecryptFileRequest pigeonResult = new DecryptFileRequest(); + Object cipherTextPath = list.get(0); + pigeonResult.setCipherTextPath((String) cipherTextPath); + Object plainTextPath = list.get(1); + pigeonResult.setPlainTextPath((String) plainTextPath); + Object key = list.get(2); + pigeonResult.setKey((byte[]) key); + Object algorithm = list.get(3); + pigeonResult.setAlgorithm((String) algorithm); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class DecryptFileResponse { + private @Nullable Boolean success; + + public @Nullable Boolean getSuccess() { + return success; + } + + public void setSuccess(@Nullable Boolean setterArg) { + this.success = setterArg; + } + + public static final class Builder { + + private @Nullable Boolean success; + + public @NonNull Builder setSuccess(@Nullable Boolean setterArg) { + this.success = setterArg; + return this; + } + + public @NonNull DecryptFileResponse build() { + DecryptFileResponse pigeonReturn = new DecryptFileResponse(); + pigeonReturn.setSuccess(success); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(1); + toListResult.add(success); + return toListResult; + } + + static @NonNull DecryptFileResponse fromList(@NonNull ArrayList list) { + DecryptFileResponse pigeonResult = new DecryptFileResponse(); + Object success = list.get(0); + pigeonResult.setSuccess((Boolean) success); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class EncryptWithIVRequest { + private @Nullable byte[] plainText; + + public @Nullable byte[] getPlainText() { + return plainText; + } + + public void setPlainText(@Nullable byte[] setterArg) { + this.plainText = setterArg; + } + + private @Nullable byte[] iv; + + public @Nullable byte[] getIv() { + return iv; + } + + public void setIv(@Nullable byte[] setterArg) { + this.iv = setterArg; + } + + private @Nullable byte[] key; + + public @Nullable byte[] getKey() { + return key; + } + + public void setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + } + + private @Nullable String algorithm; + + public @Nullable String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + } + + public static final class Builder { + + private @Nullable byte[] plainText; + + public @NonNull Builder setPlainText(@Nullable byte[] setterArg) { + this.plainText = setterArg; + return this; + } + + private @Nullable byte[] iv; + + public @NonNull Builder setIv(@Nullable byte[] setterArg) { + this.iv = setterArg; + return this; + } + + private @Nullable byte[] key; + + public @NonNull Builder setKey(@Nullable byte[] setterArg) { + this.key = setterArg; + return this; + } + + private @Nullable String algorithm; + + public @NonNull Builder setAlgorithm(@Nullable String setterArg) { + this.algorithm = setterArg; + return this; + } + + public @NonNull EncryptWithIVRequest build() { + EncryptWithIVRequest pigeonReturn = new EncryptWithIVRequest(); + pigeonReturn.setPlainText(plainText); + pigeonReturn.setIv(iv); + pigeonReturn.setKey(key); + pigeonReturn.setAlgorithm(algorithm); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList(4); + toListResult.add(plainText); + toListResult.add(iv); + toListResult.add(key); + toListResult.add(algorithm); + return toListResult; + } + + static @NonNull EncryptWithIVRequest fromList(@NonNull ArrayList list) { + EncryptWithIVRequest pigeonResult = new EncryptWithIVRequest(); + Object plainText = list.get(0); + pigeonResult.setPlainText((byte[]) plainText); + Object iv = list.get(1); + pigeonResult.setIv((byte[]) iv); + Object key = list.get(2); + pigeonResult.setKey((byte[]) key); + Object algorithm = list.get(3); + pigeonResult.setAlgorithm((String) algorithm); + return pigeonResult; + } + } + + private static class NativeCryptoAPICodec extends StandardMessageCodec { + public static final NativeCryptoAPICodec INSTANCE = new NativeCryptoAPICodec(); + + private NativeCryptoAPICodec() {} + + @Override + protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { + switch (type) { + case (byte) 128: + return DecryptFileRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 129: + return DecryptFileResponse.fromList((ArrayList) readValue(buffer)); + case (byte) 130: + return DecryptRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 131: + return DecryptResponse.fromList((ArrayList) readValue(buffer)); + case (byte) 132: + return EncryptFileRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 133: + return EncryptFileResponse.fromList((ArrayList) readValue(buffer)); + case (byte) 134: + return EncryptRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 135: + return EncryptResponse.fromList((ArrayList) readValue(buffer)); + case (byte) 136: + return EncryptWithIVRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 137: + return GenerateSecureRandomRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 138: + return GenerateSecureRandomResponse.fromList((ArrayList) readValue(buffer)); + case (byte) 139: + return HashRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 140: + return HashResponse.fromList((ArrayList) readValue(buffer)); + case (byte) 141: + return HmacRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 142: + return HmacResponse.fromList((ArrayList) readValue(buffer)); + case (byte) 143: + return Pbkdf2Request.fromList((ArrayList) readValue(buffer)); + case (byte) 144: + return Pbkdf2Response.fromList((ArrayList) readValue(buffer)); + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { + if (value instanceof DecryptFileRequest) { + stream.write(128); + writeValue(stream, ((DecryptFileRequest) value).toList()); + } else if (value instanceof DecryptFileResponse) { + stream.write(129); + writeValue(stream, ((DecryptFileResponse) value).toList()); + } else if (value instanceof DecryptRequest) { + stream.write(130); + writeValue(stream, ((DecryptRequest) value).toList()); + } else if (value instanceof DecryptResponse) { + stream.write(131); + writeValue(stream, ((DecryptResponse) value).toList()); + } else if (value instanceof EncryptFileRequest) { + stream.write(132); + writeValue(stream, ((EncryptFileRequest) value).toList()); + } else if (value instanceof EncryptFileResponse) { + stream.write(133); + writeValue(stream, ((EncryptFileResponse) value).toList()); + } else if (value instanceof EncryptRequest) { + stream.write(134); + writeValue(stream, ((EncryptRequest) value).toList()); + } else if (value instanceof EncryptResponse) { + stream.write(135); + writeValue(stream, ((EncryptResponse) value).toList()); + } else if (value instanceof EncryptWithIVRequest) { + stream.write(136); + writeValue(stream, ((EncryptWithIVRequest) value).toList()); + } else if (value instanceof GenerateSecureRandomRequest) { + stream.write(137); + writeValue(stream, ((GenerateSecureRandomRequest) value).toList()); + } else if (value instanceof GenerateSecureRandomResponse) { + stream.write(138); + writeValue(stream, ((GenerateSecureRandomResponse) value).toList()); + } else if (value instanceof HashRequest) { + stream.write(139); + writeValue(stream, ((HashRequest) value).toList()); + } else if (value instanceof HashResponse) { + stream.write(140); + writeValue(stream, ((HashResponse) value).toList()); + } else if (value instanceof HmacRequest) { + stream.write(141); + writeValue(stream, ((HmacRequest) value).toList()); + } else if (value instanceof HmacResponse) { + stream.write(142); + writeValue(stream, ((HmacResponse) value).toList()); + } else if (value instanceof Pbkdf2Request) { + stream.write(143); + writeValue(stream, ((Pbkdf2Request) value).toList()); + } else if (value instanceof Pbkdf2Response) { + stream.write(144); + writeValue(stream, ((Pbkdf2Response) value).toList()); + } else { + super.writeValue(stream, value); + } + } + } + + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface NativeCryptoAPI { + + @NonNull + HashResponse hash(@NonNull HashRequest request); + + @NonNull + HmacResponse hmac(@NonNull HmacRequest request); + + @NonNull + GenerateSecureRandomResponse generateSecureRandom(@NonNull GenerateSecureRandomRequest request); + + @NonNull + Pbkdf2Response pbkdf2(@NonNull Pbkdf2Request request); + + @NonNull + EncryptResponse encrypt(@NonNull EncryptRequest request); + + @NonNull + DecryptResponse decrypt(@NonNull DecryptRequest request); + + @NonNull + EncryptFileResponse encryptFile(@NonNull EncryptFileRequest request); + + @NonNull + DecryptFileResponse decryptFile(@NonNull DecryptFileRequest request); + + @NonNull + EncryptResponse encryptWithIV(@NonNull EncryptWithIVRequest request); + + /** The codec used by NativeCryptoAPI. */ + static MessageCodec getCodec() { + return NativeCryptoAPICodec.INSTANCE; + } + /**Sets up an instance of `NativeCryptoAPI` to handle messages through the `binaryMessenger`. */ + static void setup(BinaryMessenger binaryMessenger, NativeCryptoAPI api) { + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.hash", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + HashRequest requestArg = (HashRequest) args.get(0); + if (requestArg == null) { + throw new NullPointerException("requestArg unexpectedly null."); + } + HashResponse output = api.hash(requestArg); + wrapped.add(0, output); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.hmac", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + HmacRequest requestArg = (HmacRequest) args.get(0); + if (requestArg == null) { + throw new NullPointerException("requestArg unexpectedly null."); + } + HmacResponse output = api.hmac(requestArg); + wrapped.add(0, output); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + GenerateSecureRandomRequest requestArg = (GenerateSecureRandomRequest) args.get(0); + if (requestArg == null) { + throw new NullPointerException("requestArg unexpectedly null."); + } + GenerateSecureRandomResponse output = api.generateSecureRandom(requestArg); + wrapped.add(0, output); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.pbkdf2", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + Pbkdf2Request requestArg = (Pbkdf2Request) args.get(0); + if (requestArg == null) { + throw new NullPointerException("requestArg unexpectedly null."); + } + Pbkdf2Response output = api.pbkdf2(requestArg); + wrapped.add(0, output); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encrypt", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + EncryptRequest requestArg = (EncryptRequest) args.get(0); + if (requestArg == null) { + throw new NullPointerException("requestArg unexpectedly null."); + } + EncryptResponse output = api.encrypt(requestArg); + wrapped.add(0, output); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.decrypt", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + DecryptRequest requestArg = (DecryptRequest) args.get(0); + if (requestArg == null) { + throw new NullPointerException("requestArg unexpectedly null."); + } + DecryptResponse output = api.decrypt(requestArg); + wrapped.add(0, output); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptFile", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + EncryptFileRequest requestArg = (EncryptFileRequest) args.get(0); + if (requestArg == null) { + throw new NullPointerException("requestArg unexpectedly null."); + } + EncryptFileResponse output = api.encryptFile(requestArg); + wrapped.add(0, output); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.decryptFile", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + DecryptFileRequest requestArg = (DecryptFileRequest) args.get(0); + if (requestArg == null) { + throw new NullPointerException("requestArg unexpectedly null."); + } + DecryptFileResponse output = api.decryptFile(requestArg); + wrapped.add(0, output); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + ArrayList args = (ArrayList) message; + assert args != null; + EncryptWithIVRequest requestArg = (EncryptWithIVRequest) args.get(0); + if (requestArg == null) { + throw new NullPointerException("requestArg unexpectedly null."); + } + EncryptResponse output = api.encryptWithIV(requestArg); + wrapped.add(0, output); + } catch (Error | RuntimeException exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } +} -- 2.47.2 From 8044ccfa4383d9d2a1c9ddba1fc63857ac774431 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 22 Feb 2023 20:13:51 +0100 Subject: [PATCH 07/21] feat(interface): make api injectable for test --- .../lib/native_crypto_platform_interface.dart | 2 ++ .../basic_message_channel_native_crypto.dart | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart index 5e2f256..dbfa27c 100644 --- a/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart +++ b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart @@ -13,3 +13,5 @@ export 'src/core/exceptions/exception.dart'; export 'src/implementations/basic_message_channel_native_crypto.dart'; export 'src/implementations/method_channel_native_crypto.dart'; export 'src/interface/native_crypto_platform.dart'; +export 'src/pigeon/messages.pigeon.dart'; +export 'src/pigeon/test_api.dart'; diff --git a/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart b/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart index 993a000..a1df83b 100644 --- a/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart +++ b/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart @@ -6,13 +6,18 @@ import 'package:flutter/foundation.dart'; import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; -import 'package:native_crypto_platform_interface/src/pigeon/messages.pigeon.dart'; /// An implementation of [NativeCryptoPlatform] that uses Pigeon generated code. class BasicMessageChannelNativeCrypto extends NativeCryptoPlatform { + /// Creates a new instance of [BasicMessageChannelNativeCrypto]. + /// + /// The [api] parameter permits to override the default Pigeon API used to + /// interact with the native platform. This is useful for testing. + BasicMessageChannelNativeCrypto({NativeCryptoAPI? api}) + : api = api ?? NativeCryptoAPI(); + /// The Pigeon API used to interact with the native platform. - @visibleForTesting - NativeCryptoAPI api = NativeCryptoAPI(); + final NativeCryptoAPI api; @override Future hash(Uint8List data, {required String algorithm}) async { -- 2.47.2 From c8ff1149d73515172112c7e4149885c447651237 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 22 Feb 2023 20:16:40 +0100 Subject: [PATCH 08/21] feat(api)!: rework full api with better object oriented architecture --- packages/native_crypto/analysis_options.yaml | 2 +- packages/native_crypto/lib/native_crypto.dart | 30 +- .../native_crypto/lib/native_crypto_ext.dart | 11 - .../lib/src/builders/builders.dart | 14 +- .../lib/src/builders/decryption_builder.dart | 76 ++-- .../lib/src/builders/encryption_builder.dart | 61 +++ .../lib/src/ciphers/aes/aes.dart | 399 ++++++++++++------ .../lib/src/ciphers/aes/aes_cipher_chunk.dart | 67 +++ .../lib/src/ciphers/aes/aes_key_size.dart | 22 +- .../lib/src/ciphers/aes/aes_mode.dart | 36 +- .../lib/src/ciphers/aes/aes_padding.dart | 20 +- .../lib/src/ciphers/ciphers.dart | 13 +- .../lib/src/core/cipher_text.dart | 117 ----- .../lib/src/core/cipher_text_wrapper.dart | 191 --------- .../lib/src/core/constants/constants.dart | 13 + packages/native_crypto/lib/src/core/core.dart | 20 +- .../lib/src/core/enums/encoding.dart | 17 + .../lib/src/core/enums/enums.dart | 8 + .../lib/src/core/enums/hash_algorithm.dart | 15 + .../lib/src/core/extensions/extensions.dart | 10 + .../core/extensions/list_int_extension.dart | 12 + .../extensions/list_uint8_list_extension.dart | 19 + .../src/core/extensions/string_extension.dart | 36 ++ .../core/extensions/uint8_list_extension.dart | 69 +++ .../lib/src/core/utils/chunk_factory.dart | 12 + .../lib/src/core/utils/cipher_text.dart | 47 +++ .../lib/src/core/utils/platform.dart | 9 + .../lib/src/core/utils/utils.dart | 8 + .../native_crypto/lib/src/digest/digest.dart | 8 + .../native_crypto/lib/src/digest/hash.dart | 62 +++ .../native_crypto/lib/src/digest/hmac.dart | 67 +++ .../lib/src/domain/base_key.dart | 37 ++ .../{interfaces => domain}/byte_array.dart | 84 ++-- .../native_crypto/lib/src/domain/cipher.dart | 32 ++ .../lib/src/domain/cipher_chunk.dart | 31 ++ .../native_crypto/lib/src/domain/domain.dart | 14 + .../native_crypto/lib/src/domain/hash.dart | 24 ++ .../native_crypto/lib/src/domain/hmac.dart | 25 ++ .../src/domain/key_derivation_function.dart | 27 ++ .../native_crypto/lib/src/domain/random.dart | 18 + .../lib/src/interfaces/base_key.dart | 22 - .../lib/src/interfaces/builder.dart | 14 - .../lib/src/interfaces/cipher.dart | 53 --- .../lib/src/interfaces/interfaces.dart | 14 - .../lib/src/interfaces/keyderivation.dart | 23 - packages/native_crypto/lib/src/kdf/kdf.dart | 10 - .../native_crypto/lib/src/kdf/pbkdf2.dart | 176 ++++---- packages/native_crypto/lib/src/keys/keys.dart | 10 - .../lib/src/keys/secret_key.dart | 75 ++-- packages/native_crypto/lib/src/platform.dart | 12 - .../lib/src/random/secure_random.dart | 50 +++ .../lib/src/utils/cipher_algorithm.dart | 11 - .../native_crypto/lib/src/utils/encoding.dart | 10 - .../lib/src/utils/extensions.dart | 101 ----- .../lib/src/utils/hash_algorithm.dart | 51 --- .../lib/src/utils/kdf_algorithm.dart | 11 - packages/native_crypto/pubspec.yaml | 27 +- packages/native_crypto/pubspec_overrides.yaml | 8 + .../test/mocks/mock_native_crypto_api.dart | 193 +++++++++ .../mocks/mock_native_crypto_platform.dart | 192 --------- .../test/src/aes_cipher_test.dart | 323 -------------- .../test/src/cipher_text_test.dart | 192 --------- .../test/src/cipher_text_wrapper_test.dart | 349 --------------- .../native_crypto/test/src/digest_test.dart | 116 +++++ .../test/src/hash_algorithm_test.dart | 128 ------ .../native_crypto/test/src/pbkdf2_test.dart | 389 ++++++----------- .../native_crypto/test/src/random_test.dart | 63 +++ .../test/src/secret_key_test.dart | 115 +---- 68 files changed, 1861 insertions(+), 2660 deletions(-) delete mode 100644 packages/native_crypto/lib/native_crypto_ext.dart create mode 100644 packages/native_crypto/lib/src/builders/encryption_builder.dart create mode 100644 packages/native_crypto/lib/src/ciphers/aes/aes_cipher_chunk.dart delete mode 100644 packages/native_crypto/lib/src/core/cipher_text.dart delete mode 100644 packages/native_crypto/lib/src/core/cipher_text_wrapper.dart create mode 100644 packages/native_crypto/lib/src/core/constants/constants.dart create mode 100644 packages/native_crypto/lib/src/core/enums/encoding.dart create mode 100644 packages/native_crypto/lib/src/core/enums/enums.dart create mode 100644 packages/native_crypto/lib/src/core/enums/hash_algorithm.dart create mode 100644 packages/native_crypto/lib/src/core/extensions/extensions.dart create mode 100644 packages/native_crypto/lib/src/core/extensions/list_int_extension.dart create mode 100644 packages/native_crypto/lib/src/core/extensions/list_uint8_list_extension.dart create mode 100644 packages/native_crypto/lib/src/core/extensions/string_extension.dart create mode 100644 packages/native_crypto/lib/src/core/extensions/uint8_list_extension.dart create mode 100644 packages/native_crypto/lib/src/core/utils/chunk_factory.dart create mode 100644 packages/native_crypto/lib/src/core/utils/cipher_text.dart create mode 100644 packages/native_crypto/lib/src/core/utils/platform.dart create mode 100644 packages/native_crypto/lib/src/core/utils/utils.dart create mode 100644 packages/native_crypto/lib/src/digest/digest.dart create mode 100644 packages/native_crypto/lib/src/digest/hash.dart create mode 100644 packages/native_crypto/lib/src/digest/hmac.dart create mode 100644 packages/native_crypto/lib/src/domain/base_key.dart rename packages/native_crypto/lib/src/{interfaces => domain}/byte_array.dart (61%) create mode 100644 packages/native_crypto/lib/src/domain/cipher.dart create mode 100644 packages/native_crypto/lib/src/domain/cipher_chunk.dart create mode 100644 packages/native_crypto/lib/src/domain/domain.dart create mode 100644 packages/native_crypto/lib/src/domain/hash.dart create mode 100644 packages/native_crypto/lib/src/domain/hmac.dart create mode 100644 packages/native_crypto/lib/src/domain/key_derivation_function.dart create mode 100644 packages/native_crypto/lib/src/domain/random.dart delete mode 100644 packages/native_crypto/lib/src/interfaces/base_key.dart delete mode 100644 packages/native_crypto/lib/src/interfaces/builder.dart delete mode 100644 packages/native_crypto/lib/src/interfaces/cipher.dart delete mode 100644 packages/native_crypto/lib/src/interfaces/interfaces.dart delete mode 100644 packages/native_crypto/lib/src/interfaces/keyderivation.dart delete mode 100644 packages/native_crypto/lib/src/kdf/kdf.dart delete mode 100644 packages/native_crypto/lib/src/keys/keys.dart delete mode 100644 packages/native_crypto/lib/src/platform.dart create mode 100644 packages/native_crypto/lib/src/random/secure_random.dart delete mode 100644 packages/native_crypto/lib/src/utils/cipher_algorithm.dart delete mode 100644 packages/native_crypto/lib/src/utils/encoding.dart delete mode 100644 packages/native_crypto/lib/src/utils/extensions.dart delete mode 100644 packages/native_crypto/lib/src/utils/hash_algorithm.dart delete mode 100644 packages/native_crypto/lib/src/utils/kdf_algorithm.dart create mode 100644 packages/native_crypto/pubspec_overrides.yaml create mode 100644 packages/native_crypto/test/mocks/mock_native_crypto_api.dart delete mode 100644 packages/native_crypto/test/mocks/mock_native_crypto_platform.dart delete mode 100644 packages/native_crypto/test/src/aes_cipher_test.dart delete mode 100644 packages/native_crypto/test/src/cipher_text_test.dart delete mode 100644 packages/native_crypto/test/src/cipher_text_wrapper_test.dart create mode 100644 packages/native_crypto/test/src/digest_test.dart delete mode 100644 packages/native_crypto/test/src/hash_algorithm_test.dart create mode 100644 packages/native_crypto/test/src/random_test.dart diff --git a/packages/native_crypto/analysis_options.yaml b/packages/native_crypto/analysis_options.yaml index db48808..82177cd 100644 --- a/packages/native_crypto/analysis_options.yaml +++ b/packages/native_crypto/analysis_options.yaml @@ -1 +1 @@ -include: package:wyatt_analysis/analysis_options.flutter.experimental.yaml \ No newline at end of file +include: package:wyatt_analysis/analysis_options.flutter.yaml \ No newline at end of file diff --git a/packages/native_crypto/lib/native_crypto.dart b/packages/native_crypto/lib/native_crypto.dart index 2b294a4..2a7dd10 100644 --- a/packages/native_crypto/lib/native_crypto.dart +++ b/packages/native_crypto/lib/native_crypto.dart @@ -1,11 +1,8 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: native_crypto.dart -// Created Date: 16/12/2021 16:28:00 -// Last Modified: 26/05/2022 12:10:42 -// ----- -// Copyright (c) 2021 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. /// Fast and powerful cryptographic functions /// thanks to javax.crypto, CommonCrypto and CryptoKit. @@ -13,18 +10,13 @@ /// Author: Hugo Pointcheval library native_crypto; -export 'package:native_crypto_platform_interface/src/utils/exception.dart'; +export 'package:native_crypto_platform_interface/src/core/exceptions/exception.dart'; export 'src/builders/builders.dart'; export 'src/ciphers/ciphers.dart'; export 'src/core/core.dart'; -export 'src/interfaces/interfaces.dart'; -export 'src/kdf/kdf.dart'; -export 'src/keys/keys.dart'; -// Utils -export 'src/utils/cipher_algorithm.dart'; -export 'src/utils/hash_algorithm.dart'; -export 'src/utils/kdf_algorithm.dart'; - -// ignore: constant_identifier_names -const String AUTHOR = 'Hugo Pointcheval'; +export 'src/digest/digest.dart'; +export 'src/domain/domain.dart'; +export 'src/kdf/pbkdf2.dart'; +export 'src/keys/secret_key.dart'; +export 'src/random/secure_random.dart'; diff --git a/packages/native_crypto/lib/native_crypto_ext.dart b/packages/native_crypto/lib/native_crypto_ext.dart deleted file mode 100644 index 0e9502e..0000000 --- a/packages/native_crypto/lib/native_crypto_ext.dart +++ /dev/null @@ -1,11 +0,0 @@ -// 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'; diff --git a/packages/native_crypto/lib/src/builders/builders.dart b/packages/native_crypto/lib/src/builders/builders.dart index d846197..df13d09 100644 --- a/packages/native_crypto/lib/src/builders/builders.dart +++ b/packages/native_crypto/lib/src/builders/builders.dart @@ -1,10 +1,8 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: builders.dart -// Created Date: 23/05/2022 22:56:03 -// Last Modified: 26/05/2022 19:22:19 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. export 'decryption_builder.dart'; +export 'encryption_builder.dart'; diff --git a/packages/native_crypto/lib/src/builders/decryption_builder.dart b/packages/native_crypto/lib/src/builders/decryption_builder.dart index 998fdcf..c9d6dbe 100644 --- a/packages/native_crypto/lib/src/builders/decryption_builder.dart +++ b/packages/native_crypto/lib/src/builders/decryption_builder.dart @@ -1,46 +1,60 @@ -// 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 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. 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'; +import 'package:native_crypto/src/core/utils/cipher_text.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; +import 'package:native_crypto/src/domain/cipher.dart'; +import 'package:native_crypto/src/domain/cipher_chunk.dart'; +/// {@template decryption_builder} +/// A [StatelessWidget] that builds a [FutureBuilder] that will decrypt a +/// [CipherText] using a [Cipher]. +/// {@endtemplate} +class DecryptionBuilder extends StatelessWidget { + /// {@macro decryption_builder} const DecryptionBuilder({ - super.key, required this.cipher, - required this.data, + required this.cipherText, required this.onLoading, required this.onError, required this.onSuccess, + super.key, }); + /// The [Cipher] that will be used to decrypt the [CipherText]. + final Cipher cipher; + + /// The [CipherText] that will be decrypted. + final CipherText cipherText; + + /// The [Widget] that will be displayed while the [CipherText] is being + /// decrypted. + final Widget Function(BuildContext context) onLoading; + + /// The [Widget] that will be displayed if an error occurs while decrypting + /// the [CipherText]. + final Widget Function(BuildContext context, Object error) onError; + + /// The [Widget] that will be displayed once the [CipherText] has been + /// decrypted. + final Widget Function(BuildContext context, Uint8List plainText) onSuccess; + @override - Widget build(BuildContext context) { - return FutureBuilder( - 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); - }, - ); - } + Widget build(BuildContext context) => FutureBuilder( + future: cipher.decrypt(cipherText), + builder: (context, snapshot) { + if (snapshot.hasData) { + return onSuccess(context, snapshot.data!); + } else if (snapshot.hasError) { + return onError(context, snapshot.error!); + } + return onLoading(context); + }, + ); } diff --git a/packages/native_crypto/lib/src/builders/encryption_builder.dart b/packages/native_crypto/lib/src/builders/encryption_builder.dart new file mode 100644 index 0000000..5782730 --- /dev/null +++ b/packages/native_crypto/lib/src/builders/encryption_builder.dart @@ -0,0 +1,61 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:native_crypto/src/core/utils/cipher_text.dart'; + +import 'package:native_crypto/src/domain/cipher.dart'; +import 'package:native_crypto/src/domain/cipher_chunk.dart'; + +/// {@template encryption_builder} +/// A [StatelessWidget] that builds a [FutureBuilder] that will +/// encrypt a [Uint8List] using a [Cipher]. +/// {@endtemplate} +class EncryptionBuilder extends StatelessWidget { + /// {@macro encryption_builder} + const EncryptionBuilder({ + required this.cipher, + required this.plainText, + required this.onLoading, + required this.onError, + required this.onSuccess, + super.key, + }); + + /// The [Cipher] that will be used to encrypt the [Uint8List]. + final Cipher cipher; + + /// The [Uint8List] that will be encrypted. + final Uint8List plainText; + + /// The [Widget] that will be displayed while the [Uint8List] is being + /// encrypted. + final Widget Function(BuildContext context) onLoading; + + /// The [Widget] that will be displayed if an error occurs while encrypting + /// the [Uint8List]. + final Widget Function(BuildContext context, Object error) onError; + + /// The [Widget] that will be displayed once the [Uint8List] has been + /// encrypted. + final Widget Function(BuildContext context, CipherText cipherText) + onSuccess; + + @override + Widget build(BuildContext context) => FutureBuilder>( + future: cipher.encrypt(plainText), + builder: (context, snapshot) { + if (snapshot.hasData) { + return onSuccess(context, snapshot.data!); + } else if (snapshot.hasError) { + return onError(context, snapshot.error!); + } + return onLoading(context); + }, + ); +} diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes.dart b/packages/native_crypto/lib/src/ciphers/aes/aes.dart index b1113fc..8eda12e 100644 --- a/packages/native_crypto/lib/src/ciphers/aes/aes.dart +++ b/packages/native_crypto/lib/src/ciphers/aes/aes.dart @@ -1,181 +1,300 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: aes.dart -// Created Date: 16/12/2021 16:28:00 -// Last Modified: 27/05/2022 12:13:28 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +import 'dart:io'; import 'dart:typed_data'; +import 'package:native_crypto/src/ciphers/aes/aes_cipher_chunk.dart'; 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_padding.dart'; -import 'package:native_crypto/src/core/cipher_text.dart'; -import 'package:native_crypto/src/core/cipher_text_wrapper.dart'; -import 'package:native_crypto/src/interfaces/cipher.dart'; +import 'package:native_crypto/src/core/constants/constants.dart'; +import 'package:native_crypto/src/core/extensions/uint8_list_extension.dart'; +import 'package:native_crypto/src/core/utils/cipher_text.dart'; +import 'package:native_crypto/src/core/utils/platform.dart'; +import 'package:native_crypto/src/domain/cipher.dart'; import 'package:native_crypto/src/keys/secret_key.dart'; -import 'package:native_crypto/src/platform.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'; +export 'aes_cipher_chunk.dart'; export 'aes_key_size.dart'; export 'aes_mode.dart'; export 'aes_padding.dart'; -/// An AES cipher. +/// {@template aes} +/// AES cipher. /// -/// [AES] is a [Cipher] that can be used to encrypt or decrypt data. -class AES implements Cipher { - final SecretKey _key; +/// [AES] is a symmetric cipher which means that the same key is used to encrypt +/// and decrypt the data. +/// {@endtemplate} +class AES implements Cipher { + const AES({ + required this.key, + required this.mode, + required this.padding, + this.chunkSize = Constants.defaultChunkSize, + }); + + static const String _algorithm = 'aes'; + + /// The key used to encrypt and decrypt the data. + final SecretKey key; + + /// The [AESMode] used by this [AES]. final AESMode mode; + + /// The [AESPadding] used by this [AES]. final AESPadding padding; - @override - CipherAlgorithm get algorithm => CipherAlgorithm.aes; + /// The size of the cipher text chunks. + final int chunkSize; - AES(SecretKey key, [this.mode = AESMode.gcm, this.padding = AESPadding.none]) - : _key = key { - if (!AESKeySize.supportedSizes.contains(key.bitLength)) { - throw NativeCryptoException( - message: 'Invalid key size! ' - 'Expected: ${AESKeySize.supportedSizes.join(', ')} bits', - code: NativeCryptoExceptionCode.invalid_key_length.code, + @override + Future decrypt(CipherText cipherText) async { + final BytesBuilder plainText = BytesBuilder(copy: false); + final chunks = cipherText.chunks; + + int i = 0; + for (final chunk in chunks) { + plainText.add(await _decryptChunk(chunk.bytes, count: i++)); + } + + return plainText.toBytes(); + } + + @override + Future decryptFile(File cipherTextFile, File plainTextFile) { + if (!cipherTextFile.existsSync()) { + throw ArgumentError.value( + cipherTextFile.path, + 'cipherTextFile.path', + 'File does not exist!', + ); + } + + if (plainTextFile.existsSync()) { + throw ArgumentError.value( + plainTextFile.path, + 'plainTextFile.path', + 'File already exists!', + ); + } + + return platform.decryptFile( + cipherTextPath: cipherTextFile.path, + plainTextPath: plainTextFile.path, + key: key.bytes, + algorithm: _algorithm, + ); + } + + @override + Future> encrypt(Uint8List plainText) async { + final chunks = []; + final chunkedPlainText = plainText.chunked(chunkSize); + + int i = 0; + for (final plainTextChunk in chunkedPlainText) { + final bytes = await _encryptChunk(plainTextChunk, count: i++); + chunks.add( + AESCipherChunk( + bytes, + ivLength: mode.ivLength, + tagLength: mode.tagLength, + ), + ); + } + + return CipherText.fromChunks( + chunks, + chunkFactory: (bytes) => AESCipherChunk( + bytes, + ivLength: mode.ivLength, + tagLength: mode.tagLength, + ), + chunkSize: chunkSize, + ); + } + + @override + Future encryptFile(File plainTextFile, File cipherTextFile) { + if (!plainTextFile.existsSync()) { + throw ArgumentError.value( + plainTextFile.path, + 'plainTextFile.path', + 'File does not exist!', + ); + } + + if (cipherTextFile.existsSync()) { + throw ArgumentError.value( + cipherTextFile.path, + 'cipherTextFile.path', + 'File already exists!', + ); + } + + return platform.encryptFile( + plainTextPath: plainTextFile.path, + cipherTextPath: cipherTextFile.path, + key: key.bytes, + algorithm: _algorithm, + ); + } + + /// Encrypts the [plainText] with the [iv] chosen by the Flutter side. + /// + /// Prefer using [encrypt] instead which will generate a + /// random [iv] on the native side. + /// + /// Note: this method doesn't chunk the data. It can lead to memory issues + /// if the [plainText] is too big. Use [encrypt] instead. + Future encryptWithIV( + Uint8List plainText, + Uint8List iv, + ) async { + // Check if the cipher is correctly initialized + _isCorrectlyInitialized(); + + if (iv.length != mode.ivLength) { + throw ArgumentError.value( + iv.length, + 'iv.length', + 'Invalid iv length! ' + 'Expected: ${mode.ivLength}', + ); + } + + final bytes = await platform.encryptWithIV( + plainText: plainText, + iv: iv, + key: key.bytes, + algorithm: _algorithm, + ); + + // TODO(hpcl): move these checks to the platform interface + if (bytes == null) { + throw const NativeCryptoException( + code: NativeCryptoExceptionCode.nullError, + message: 'Platform returned null bytes', + ); + } + + if (bytes.isEmpty) { + throw const NativeCryptoException( + code: NativeCryptoExceptionCode.invalidData, + message: 'Platform returned no data', + ); + } + + return AESCipherChunk( + bytes, + ivLength: mode.ivLength, + tagLength: mode.tagLength, + ); + } + + /// Ensures that the cipher is correctly initialized. + bool _isCorrectlyInitialized() { + final keySize = key.length * 8; + if (!AESKeySize.supportedSizes.contains(keySize)) { + throw ArgumentError.value( + keySize, + 'keySize', + 'Invalid key size! ' + 'Expected: ${AESKeySize.supportedSizes.join(', ')}', ); } if (!mode.supportedPaddings.contains(padding)) { - throw NativeCryptoException( - message: 'Invalid padding! ' + throw ArgumentError.value( + padding, + 'padding', + 'Invalid padding! ' 'Expected: ${mode.supportedPaddings.join(', ')}', - code: NativeCryptoExceptionCode.invalid_padding.code, ); } + + return true; } - Future _decrypt( - CipherText cipherText, { - int chunkCount = 0, + /// Encrypts the plain text chunk. + Future _encryptChunk(Uint8List plainChunk, {int count = 0}) async { + // Check if the cipher is correctly initialized + _isCorrectlyInitialized(); + + Uint8List? bytes; + + try { + bytes = await platform.encrypt( + plainChunk, + key: key.bytes, + algorithm: _algorithm, + ); + } on NativeCryptoException catch (e) { + throw e.copyWith( + message: 'Failed to encrypt chunk #$count: ${e.message}', + ); + } + + // TODO(hpcl): move these checks to the platform interface + if (bytes == null) { + throw NativeCryptoException( + code: NativeCryptoExceptionCode.nullError, + message: 'Platform returned null bytes on chunk #$count', + ); + } + + if (bytes.isEmpty) { + throw NativeCryptoException( + code: NativeCryptoExceptionCode.invalidData, + message: 'Platform returned no data on chunk #$count', + ); + } + + return bytes; + } + + /// Decrypts the cipher text chunk. + Future _decryptChunk( + Uint8List cipherChunk, { + int count = 0, }) async { - Uint8List? decrypted; + // Check if the cipher is correctly initialized + _isCorrectlyInitialized(); + + Uint8List? bytes; try { - decrypted = await platform.decrypt( - cipherText.bytes, - _key.bytes, - algorithm.name, + bytes = await platform.decrypt( + cipherChunk, + key: key.bytes, + algorithm: _algorithm, ); - } catch (e, s) { + } on NativeCryptoException catch (e) { + throw e.copyWith( + message: 'Failed to decrypt chunk #$count: ${e.message}', + ); + } + + // TODO(hpcl): move these checks to the platform interface + if (bytes == null) { throw NativeCryptoException( - message: '$e', - code: NativeCryptoExceptionCode.platform_throws.code, - stackTrace: s, + code: NativeCryptoExceptionCode.nullError, + message: 'Platform returned null bytes on chunk #$count', ); } - if (decrypted.isNull) { + if (bytes.isEmpty) { 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 _encrypt(Uint8List data, {int chunkCount = 0}) async { - Uint8List? encrypted; - - try { - encrypted = await platform.encrypt( - data, - _key.bytes, - algorithm.name, - ); - } catch (e, s) { - throw NativeCryptoException( - message: '$e on chunk #$chunkCount', - code: NativeCryptoExceptionCode.platform_throws.code, - stackTrace: s, + code: NativeCryptoExceptionCode.invalidData, + message: 'Platform returned no data on chunk #$count', ); } - 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 { - try { - return CipherText.fromBytes( - encrypted, - ivLength: 12, - messageLength: encrypted.length - 28, - tagLength: 16, - cipherAlgorithm: CipherAlgorithm.aes, - ); - } on NativeCryptoException catch (e, s) { - throw NativeCryptoException( - message: '${e.message} on chunk #$chunkCount', - code: e.code, - stackTrace: s, - ); - } - } - } - - @override - Future decrypt(CipherTextWrapper cipherText) async { - final BytesBuilder decryptedData = BytesBuilder(copy: false); - - if (cipherText.isList) { - int chunkCount = 0; - for (final CipherText chunk in cipherText.list) { - decryptedData.add(await _decrypt(chunk, chunkCount: chunkCount++)); - } - } else { - decryptedData.add(await _decrypt(cipherText.single)); - } - - return decryptedData.toBytes(); - } - - @override - Future encrypt(Uint8List data) async { - if (data.isEmpty) { - return CipherTextWrapper.empty(); - } - CipherTextWrapper cipherTextWrapper; - Uint8List dataToEncrypt; - final int chunkNb = (data.length / Cipher.bytesCountPerChunk).ceil(); - - if (chunkNb > 1) { - cipherTextWrapper = CipherTextWrapper.empty(); - for (var i = 0; i < chunkNb; i++) { - dataToEncrypt = i < (chunkNb - 1) - ? data.sublist( - i * Cipher.bytesCountPerChunk, - (i + 1) * Cipher.bytesCountPerChunk, - ) - : data.sublist(i * Cipher.bytesCountPerChunk); - cipherTextWrapper.add(await _encrypt(dataToEncrypt, chunkCount: i)); - } - } else { - cipherTextWrapper = CipherTextWrapper.single(await _encrypt(data)); - } - - return cipherTextWrapper; + return bytes; } } diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_cipher_chunk.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_cipher_chunk.dart new file mode 100644 index 0000000..7f8bb45 --- /dev/null +++ b/packages/native_crypto/lib/src/ciphers/aes/aes_cipher_chunk.dart @@ -0,0 +1,67 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/src/domain/cipher_chunk.dart'; + +class AESCipherChunk extends CipherChunk { + const AESCipherChunk( + super.bytes, { + required this.ivLength, + required this.tagLength, + }); + + /// Creates a [AESCipherChunk] from a [List]. + AESCipherChunk.fromList( + super.list, { + required this.ivLength, + required this.tagLength, + }) : super.fromList(); + + /// Creates a [AESCipherChunk] from a [String] encoded in UTF-8. + AESCipherChunk.fromUtf8( + super.encoded, { + required this.ivLength, + required this.tagLength, + }) : super.fromUtf8(); + + /// Creates a [AESCipherChunk] from a [String] encoded in UTF-16. + AESCipherChunk.fromUtf16( + super.encoded, { + required this.ivLength, + required this.tagLength, + }) : super.fromUtf16(); + + /// Creates a [AESCipherChunk] from a [String] encoded in Hexadecimal. + AESCipherChunk.fromBase16( + super.encoded, { + required this.ivLength, + required this.tagLength, + }) : super.fromBase16(); + + /// Creates a [AESCipherChunk] from a [String] encoded in Base64. + AESCipherChunk.fromBase64( + super.encoded, { + required this.ivLength, + required this.tagLength, + }) : super.fromBase64(); + + /// Intialization vector length. + final int ivLength; + + /// Tag length. + final int tagLength; + + /// Returns the initialization vector, or nonce of the [AESCipherChunk]. + Uint8List get iv => bytes.sublist(0, ivLength); + + /// Returns the tag of the [AESCipherChunk]. + Uint8List get tag => bytes.sublist(bytes.length - tagLength, bytes.length); + + /// Returns the message of the [AESCipherChunk]. + Uint8List get message => bytes.sublist(ivLength, bytes.length - tagLength); +} diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart index befa22f..7bc8f26 100644 --- a/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart +++ b/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart @@ -1,18 +1,20 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: aes_key_size.dart -// Created Date: 23/05/2022 22:10:07 -// Last Modified: 26/05/2022 18:45:01 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. -/// Defines all available key sizes. +/// {@template aes_key_size} +/// Defines the key size of an AES cipher. +/// {@endtemplate} enum AESKeySize { bits128(128), bits192(192), bits256(256); + /// {@macro aes_key_size} + const AESKeySize(this.bits); + /// Returns the number of bits supported by an [AESKeySize]. static final List supportedSizes = [128, 192, 256]; @@ -21,6 +23,4 @@ enum AESKeySize { /// Returns the number of bytes in this [AESKeySize]. int get bytes => bits ~/ 8; - - const AESKeySize(this.bits); } diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_mode.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_mode.dart index 4bbc7c4..9e34304 100644 --- a/packages/native_crypto/lib/src/ciphers/aes/aes_mode.dart +++ b/packages/native_crypto/lib/src/ciphers/aes/aes_mode.dart @@ -1,17 +1,29 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: aes_mode.dart -// Created Date: 23/05/2022 22:09:16 -// Last Modified: 26/05/2022 21:03:26 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. import 'package:native_crypto/src/ciphers/aes/aes_padding.dart'; +import 'package:native_crypto/src/core/constants/constants.dart'; +/// {@template aes_mode} /// Defines the AES modes of operation. +/// {@endtemplate} enum AESMode { - gcm([AESPadding.none], 12, 16); + /// GCM mode. + gcm( + [AESPadding.none], + Constants.aesGcmNonceLength, + Constants.aesGcmTagLength, + ); + + /// {@macro aes_mode} + const AESMode( + this.supportedPaddings, [ + this.ivLength = 16, + this.tagLength = 0, + ]); /// Returns the list of supported [AESPadding] for this [AESMode]. final List supportedPaddings; @@ -21,10 +33,4 @@ enum AESMode { /// Returns the default tag length for this [AESMode]. final int tagLength; - - const AESMode( - this.supportedPaddings, [ - this.ivLength = 16, - this.tagLength = 0, - ]); } diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_padding.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_padding.dart index 343ae03..0d26a9f 100644 --- a/packages/native_crypto/lib/src/ciphers/aes/aes_padding.dart +++ b/packages/native_crypto/lib/src/ciphers/aes/aes_padding.dart @@ -1,11 +1,11 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: aes_padding.dart -// Created Date: 23/05/2022 22:10:17 -// Last Modified: 25/05/2022 09:23:49 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. -/// Represents different paddings. -enum AESPadding { none } +/// Padding used for AES encryption. +enum AESPadding { + /// No padding. + none, +} diff --git a/packages/native_crypto/lib/src/ciphers/ciphers.dart b/packages/native_crypto/lib/src/ciphers/ciphers.dart index edae6a4..2b87da6 100644 --- a/packages/native_crypto/lib/src/ciphers/ciphers.dart +++ b/packages/native_crypto/lib/src/ciphers/ciphers.dart @@ -1,10 +1,7 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: ciphers.dart -// Created Date: 23/05/2022 22:56:30 -// Last Modified: 23/05/2022 22:56:47 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. export 'aes/aes.dart'; diff --git a/packages/native_crypto/lib/src/core/cipher_text.dart b/packages/native_crypto/lib/src/core/cipher_text.dart deleted file mode 100644 index 6accd59..0000000 --- a/packages/native_crypto/lib/src/core/cipher_text.dart +++ /dev/null @@ -1,117 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: cipher_text.dart -// Created Date: 16/12/2021 16:59:53 -// Last Modified: 27/05/2022 12:09:47 -// ----- -// Copyright (c) 2021 - -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/extensions.dart'; -import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; - -/// Represents a cipher text in NativeCrypto. -/// -/// [CipherText] is a [ByteArray] that can be used to store encrypted data. -/// It is represented like: -/// ```txt -/// [IV + MESSAGE + TAG] -/// ``` -/// where: -/// - IV's length is [CipherText.ivLength] bytes. -/// - MESSAGE's length is [CipherText.messageLength] bytes. -/// - TAG's length is [CipherText.tagLength] bytes. -/// -/// Check [CipherTextWrapper] for more information. -class CipherText extends ByteArray { - final int _ivLength; - final int _messageLength; - final int _tagLength; - - final CipherAlgorithm? _cipherAlgorithm; - - const CipherText._( - this._ivLength, - this._messageLength, - this._tagLength, - this._cipherAlgorithm, - super.bytes, - ); - - factory CipherText.fromBytes( - Uint8List bytes, { - required int ivLength, - required int tagLength, - int? messageLength, - CipherAlgorithm? cipherAlgorithm, - }) { - 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) { - throw NativeCryptoException( - message: 'Invalid cipher text length! ' - 'Expected: ${ivLength + messageLength + tagLength} bytes ' - 'got: ${bytes.length} 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]. - CipherAlgorithm get cipherAlgorithm { - if (_cipherAlgorithm.isNotNull) { - return _cipherAlgorithm!; - } else { - throw NativeCryptoException( - message: 'Cipher algorithm is not specified', - code: NativeCryptoExceptionCode.invalid_cipher.code, - ); - } - } - - /// Gets the length of the [CipherText]'s IV. - int get ivLength => _ivLength; - - /// Gets the length of the [CipherText]'s Message. - int get messageLength => _messageLength; - - /// Gets the length of the [CipherText]'s Tag. - int get tagLength => _tagLength; -} diff --git a/packages/native_crypto/lib/src/core/cipher_text_wrapper.dart b/packages/native_crypto/lib/src/core/cipher_text_wrapper.dart deleted file mode 100644 index 75a9dae..0000000 --- a/packages/native_crypto/lib/src/core/cipher_text_wrapper.dart +++ /dev/null @@ -1,191 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: cipher_text_wrapper.dart -// Created Date: 26/05/2022 14:27:32 -// Last Modified: 27/05/2022 13:43:29 -// ----- -// Copyright (c) 2022 - -import 'dart:typed_data'; - -import 'package:native_crypto/native_crypto.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 { - final CipherText? _single; - final List? _list; - - CipherTextWrapper._(this._single, this._list); - - /// Creates a [CipherTextWrapper] from a [CipherText]. - factory CipherTextWrapper.single(CipherText cipherText) => - CipherTextWrapper._(cipherText, null); - - /// Creates a [CipherTextWrapper] from a [List] of [CipherText]. - factory CipherTextWrapper.list(List 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( - Uint8List bytes, { - required int ivLength, - required int tagLength, - CipherAlgorithm? cipherAlgorithm, - int? chunkSize, - }) { - chunkSize ??= Cipher.bytesCountPerChunk; - Cipher.bytesCountPerChunk = chunkSize; - - final int messageLength = bytes.length - ivLength - tagLength; - - if (messageLength <= chunkSize) { - return CipherTextWrapper.single( - CipherText.fromBytes( - bytes, - ivLength: ivLength, - tagLength: tagLength, - cipherAlgorithm: cipherAlgorithm, - ), - ); - } else { - final cipherTexts = []; - for (var i = 0; i < bytes.length; i += chunkSize + ivLength + tagLength) { - final chunk = bytes.trySublist(i, i + chunkSize + ivLength + tagLength); - - try { - cipherTexts.add( - CipherText.fromBytes( - chunk, - ivLength: ivLength, - tagLength: tagLength, - cipherAlgorithm: cipherAlgorithm, - ), - ); - } on NativeCryptoException catch (e, s) { - throw NativeCryptoException( - message: '${e.message} on chunk #$i', - code: e.code, - stackTrace: s, - ); - } - } - return CipherTextWrapper.list(cipherTexts); - } - } - - /// Checks if the [CipherText] is a single [CipherText]. - bool get isSingle => _single.isNotNull; - - /// Checks if the [CipherText] is a [List] of [CipherText]. - bool get isList => _list.isNotNull; - - /// Gets the [CipherText] if it's a single one. - /// - /// Throws [NativeCryptoException] with - /// [NativeCryptoExceptionCode.invalid_data] if it's not a single one. - CipherText get single { - if (isSingle) { - return _single!; - } else { - throw NativeCryptoException( - message: 'CipherTextWrapper is not single', - code: NativeCryptoExceptionCode.invalid_data.code, - ); - } - } - - /// Gets the [List] of [CipherText] if it's a list. - /// - /// Throws [NativeCryptoException] with - /// [NativeCryptoExceptionCode.invalid_data] if it's not a list. - List get list { - if (isList) { - return _list!; - } else { - throw NativeCryptoException( - message: 'CipherTextWrapper is not list', - code: NativeCryptoExceptionCode.invalid_data.code, - ); - } - } - - /// Gets the raw [Uint8List] of the [CipherText] or [List] of [CipherText]. - Uint8List get bytes { - if (isSingle) { - return single.bytes; - } else { - return list.map((cipherText) => cipherText.bytes).toList().combine(); - } - } - - /// Gets the number of parts of the [CipherText] or [List] of [CipherText]. - /// - /// Check [Cipher.bytesCountPerChunk] for more information. - int get chunkCount { - _single.isNull; - if (_single.isNotNull) { - return 1; - } else { - 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() { - if (isSingle && T == CipherText) { - return single as T; - } else if (isList && T == List) { - return list as T; - } else { - final String type = - isSingle ? 'CipherText' : (isList ? 'List' : '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, - ); - } - } -} diff --git a/packages/native_crypto/lib/src/core/constants/constants.dart b/packages/native_crypto/lib/src/core/constants/constants.dart new file mode 100644 index 0000000..c9b13ae --- /dev/null +++ b/packages/native_crypto/lib/src/core/constants/constants.dart @@ -0,0 +1,13 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +abstract class Constants { + /// The default chunk size in bytes used for encryption and decryption. + static const int defaultChunkSize = 33554432; + + static const int aesGcmNonceLength = 12; + static const int aesGcmTagLength = 16; +} diff --git a/packages/native_crypto/lib/src/core/core.dart b/packages/native_crypto/lib/src/core/core.dart index 32ad783..7a26b2a 100644 --- a/packages/native_crypto/lib/src/core/core.dart +++ b/packages/native_crypto/lib/src/core/core.dart @@ -1,11 +1,11 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: core.dart -// Created Date: 23/05/2022 23:05:26 -// Last Modified: 26/05/2022 17:10:25 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. -export 'cipher_text.dart'; -export 'cipher_text_wrapper.dart'; +export './constants/constants.dart'; +export './enums/enums.dart'; +export './extensions/extensions.dart'; +export './utils/utils.dart'; +export 'utils/platform.dart'; diff --git a/packages/native_crypto/lib/src/core/enums/encoding.dart b/packages/native_crypto/lib/src/core/enums/encoding.dart new file mode 100644 index 0000000..2b69508 --- /dev/null +++ b/packages/native_crypto/lib/src/core/enums/encoding.dart @@ -0,0 +1,17 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +/// An encoding used to convert a byte array to a string and vice-versa. +enum Encoding { + /// UTF-8 encoding, as defined by the Unicode standard. + utf8, + /// UTF-16 encoding, as defined by the Unicode standard. + utf16, + /// Base64 encoding, as defined by RFC 4648. + base64, + /// Hexadecimal encoding. + base16, +} diff --git a/packages/native_crypto/lib/src/core/enums/enums.dart b/packages/native_crypto/lib/src/core/enums/enums.dart new file mode 100644 index 0000000..bc34b6b --- /dev/null +++ b/packages/native_crypto/lib/src/core/enums/enums.dart @@ -0,0 +1,8 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +export 'encoding.dart'; +export 'hash_algorithm.dart'; diff --git a/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart b/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart new file mode 100644 index 0000000..7748efb --- /dev/null +++ b/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart @@ -0,0 +1,15 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +/// The hash algorithm to use in Message Digest and HMAC. +enum HashAlgorithm { + /// The SHA-256 hash algorithm. + sha256, + /// The SHA-384 hash algorithm. + sha384, + /// The SHA-512 hash algorithm. + sha512, +} diff --git a/packages/native_crypto/lib/src/core/extensions/extensions.dart b/packages/native_crypto/lib/src/core/extensions/extensions.dart new file mode 100644 index 0000000..f10b73b --- /dev/null +++ b/packages/native_crypto/lib/src/core/extensions/extensions.dart @@ -0,0 +1,10 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +export 'list_int_extension.dart'; +export 'list_uint8_list_extension.dart'; +export 'string_extension.dart'; +export 'uint8_list_extension.dart'; diff --git a/packages/native_crypto/lib/src/core/extensions/list_int_extension.dart b/packages/native_crypto/lib/src/core/extensions/list_int_extension.dart new file mode 100644 index 0000000..5ae7252 --- /dev/null +++ b/packages/native_crypto/lib/src/core/extensions/list_int_extension.dart @@ -0,0 +1,12 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +extension ListIntExtension on List { + /// Converts a [List] of int to a [Uint8List]. + Uint8List toTypedList() => Uint8List.fromList(this); +} diff --git a/packages/native_crypto/lib/src/core/extensions/list_uint8_list_extension.dart b/packages/native_crypto/lib/src/core/extensions/list_uint8_list_extension.dart new file mode 100644 index 0000000..78dfe75 --- /dev/null +++ b/packages/native_crypto/lib/src/core/extensions/list_uint8_list_extension.dart @@ -0,0 +1,19 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/extensions/uint8_list_extension.dart'; + +extension ListUint8ListExtension on List { + /// Reduce a [List] of [Uint8List] to a [Uint8List]. + Uint8List combine() { + if (isEmpty) { + return Uint8List(0); + } + return reduce((value, element) => value | element); + } +} diff --git a/packages/native_crypto/lib/src/core/extensions/string_extension.dart b/packages/native_crypto/lib/src/core/extensions/string_extension.dart new file mode 100644 index 0000000..3a342b7 --- /dev/null +++ b/packages/native_crypto/lib/src/core/extensions/string_extension.dart @@ -0,0 +1,36 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/enums/encoding.dart'; +import 'package:native_crypto/src/core/extensions/list_int_extension.dart'; + +extension StringExtension on String { + /// Converts a [String] to a [Uint8List] using the specified [Encoding]. + Uint8List toBytes({Encoding from = Encoding.utf16}) { + Uint8List bytes; + switch (from) { + case Encoding.utf8: + bytes = utf8.encode(this).toTypedList(); + break; + case Encoding.utf16: + bytes = runes.toList().toTypedList(); + break; + case Encoding.base64: + bytes = base64.decode(this); + break; + case Encoding.base16: + assert(length.isEven, 'String needs to be an even length.'); + bytes = List.generate( + length ~/ 2, + (i) => int.parse(substring(i * 2, (i * 2) + 2), radix: 16), + ).toList().toTypedList(); + } + return bytes; + } +} diff --git a/packages/native_crypto/lib/src/core/extensions/uint8_list_extension.dart b/packages/native_crypto/lib/src/core/extensions/uint8_list_extension.dart new file mode 100644 index 0000000..daf870a --- /dev/null +++ b/packages/native_crypto/lib/src/core/extensions/uint8_list_extension.dart @@ -0,0 +1,69 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/enums/encoding.dart'; +import 'package:native_crypto/src/core/extensions/list_int_extension.dart'; + +extension Uint8ListExtension on Uint8List { + /// Returns a concatenation of this with the other [Uint8List]. + Uint8List plus(Uint8List other) => [...this, ...other].toTypedList(); + + /// Returns a concatenation of this with the other + /// [Uint8List] using the `|` operator as a shortcut. + Uint8List operator |(Uint8List other) => plus(other); + + /// 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); + } + + /// Returns a [List] of [Uint8List] of the specified [chunkSize]. + List chunked(int chunkSize) { + if (isEmpty) { + return []; + } + + return List.generate( + (length / chunkSize).ceil(), + (i) => trySublist(i * chunkSize, (i * chunkSize) + chunkSize), + ); + } + + /// Converts a [Uint8List] to a [String] using the specified [Encoding]. + String toStr({Encoding to = Encoding.utf16}) { + String str; + switch (to) { + case Encoding.utf8: + str = utf8.decode(this); + break; + case Encoding.utf16: + str = String.fromCharCodes(this); + break; + case Encoding.base64: + str = base64.encode(this); + break; + case Encoding.base16: + str = List.generate( + length, + (i) => this[i].toRadixString(16).padLeft(2, '0'), + ).join(); + } + return str; + } +} diff --git a/packages/native_crypto/lib/src/core/utils/chunk_factory.dart b/packages/native_crypto/lib/src/core/utils/chunk_factory.dart new file mode 100644 index 0000000..afbc6b4 --- /dev/null +++ b/packages/native_crypto/lib/src/core/utils/chunk_factory.dart @@ -0,0 +1,12 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/src/domain/cipher_chunk.dart'; + +/// A factory that creates a [CipherChunk] of type [T] from a [Uint8List]. +typedef ChunkFactory = T Function(Uint8List chunk); diff --git a/packages/native_crypto/lib/src/core/utils/cipher_text.dart b/packages/native_crypto/lib/src/core/utils/cipher_text.dart new file mode 100644 index 0000000..d9e6977 --- /dev/null +++ b/packages/native_crypto/lib/src/core/utils/cipher_text.dart @@ -0,0 +1,47 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/constants/constants.dart'; +import 'package:native_crypto/src/core/extensions/uint8_list_extension.dart'; +import 'package:native_crypto/src/core/utils/chunk_factory.dart'; +import 'package:native_crypto/src/domain/byte_array.dart'; +import 'package:native_crypto/src/domain/cipher_chunk.dart'; + +/// {@template cipher_text} +/// A [CipherText] is a [ByteArray] that is used to store a text encrypted by a +/// Cipher. +/// {@endtemplate} +class CipherText extends ByteArray { + /// {@macro cipher_text} + CipherText( + super.bytes, { + required ChunkFactory this.chunkFactory, + this.chunkSize = Constants.defaultChunkSize, + }) : chunks = bytes.chunked(chunkSize).map(chunkFactory).toList(); + + /// Creates a [CipherText] from a [List] of [CipherChunk]. + CipherText.fromChunks( + this.chunks, { + required ChunkFactory this.chunkFactory, + this.chunkSize = Constants.defaultChunkSize, + }) : super( + chunks.fold( + Uint8List(0), + (acc, chunk) => acc | chunk.bytes, + ), + ); + + /// Factory used to create [CipherChunk] from an Uint8List. + final ChunkFactory? chunkFactory; + + /// List of [CipherChunk] that compose the [CipherText]. + final List chunks; + + /// Size of one chunk. + final int chunkSize; +} diff --git a/packages/native_crypto/lib/src/core/utils/platform.dart b/packages/native_crypto/lib/src/core/utils/platform.dart new file mode 100644 index 0000000..0555237 --- /dev/null +++ b/packages/native_crypto/lib/src/core/utils/platform.dart @@ -0,0 +1,9 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +NativeCryptoPlatform platform = NativeCryptoPlatform.instance; diff --git a/packages/native_crypto/lib/src/core/utils/utils.dart b/packages/native_crypto/lib/src/core/utils/utils.dart new file mode 100644 index 0000000..a673ed2 --- /dev/null +++ b/packages/native_crypto/lib/src/core/utils/utils.dart @@ -0,0 +1,8 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +export 'chunk_factory.dart'; +export 'cipher_text.dart'; diff --git a/packages/native_crypto/lib/src/digest/digest.dart b/packages/native_crypto/lib/src/digest/digest.dart new file mode 100644 index 0000000..d5ab308 --- /dev/null +++ b/packages/native_crypto/lib/src/digest/digest.dart @@ -0,0 +1,8 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +export 'hash.dart'; +export 'hmac.dart'; diff --git a/packages/native_crypto/lib/src/digest/hash.dart b/packages/native_crypto/lib/src/digest/hash.dart new file mode 100644 index 0000000..30f33c7 --- /dev/null +++ b/packages/native_crypto/lib/src/digest/hash.dart @@ -0,0 +1,62 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/enums/hash_algorithm.dart'; +import 'package:native_crypto/src/core/utils/platform.dart'; +import 'package:native_crypto/src/domain/hash.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +class _Hash extends Hash { + const _Hash(this.algorithm); + + @override + Future digest(Uint8List data) async { + final hash = await platform.hash(data, algorithm: algorithm.name); + + // TODO(hpcl): move these checks to the platform interface + if (hash == null) { + throw const NativeCryptoException( + code: NativeCryptoExceptionCode.nullError, + message: 'Platform returned null bytes', + ); + } + + if (hash.isEmpty) { + throw const NativeCryptoException( + code: NativeCryptoExceptionCode.invalidData, + message: 'Platform returned no data.', + ); + } + + return hash; + } + + @override + final HashAlgorithm algorithm; +} + +/// A [Hash] that uses the SHA-256 algorithm. +class Sha256 extends _Hash { + factory Sha256() => _instance ??= Sha256._(); + Sha256._() : super(HashAlgorithm.sha256); + static Sha256? _instance; +} + +/// A [Hash] that uses the SHA-384 algorithm. +class Sha384 extends _Hash { + factory Sha384() => _instance ??= Sha384._(); + Sha384._() : super(HashAlgorithm.sha384); + static Sha384? _instance; +} + +/// A [Hash] that uses the SHA-512 algorithm. +class Sha512 extends _Hash { + factory Sha512() => _instance ??= Sha512._(); + Sha512._() : super(HashAlgorithm.sha512); + static Sha512? _instance; +} diff --git a/packages/native_crypto/lib/src/digest/hmac.dart b/packages/native_crypto/lib/src/digest/hmac.dart new file mode 100644 index 0000000..69419b0 --- /dev/null +++ b/packages/native_crypto/lib/src/digest/hmac.dart @@ -0,0 +1,67 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/enums/hash_algorithm.dart'; +import 'package:native_crypto/src/core/utils/platform.dart'; +import 'package:native_crypto/src/domain/hmac.dart'; +import 'package:native_crypto/src/keys/secret_key.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +class _Hmac extends Hmac { + const _Hmac(this.algorithm); + + @override + Future digest(Uint8List data, SecretKey key) async { + final hmac = await platform.hmac( + data, + key: key.bytes, + algorithm: algorithm.name, + ); + + // TODO(hpcl): move these checks to the platform interface + if (hmac == null) { + throw const NativeCryptoException( + code: NativeCryptoExceptionCode.nullError, + message: 'Platform returned null bytes', + ); + } + + if (hmac.isEmpty) { + throw const NativeCryptoException( + code: NativeCryptoExceptionCode.invalidData, + message: 'Platform returned no data.', + ); + } + + return hmac; + } + + @override + final HashAlgorithm algorithm; +} + +/// A [Hmac] that uses the SHA-256 algorithm. +class HmacSha256 extends _Hmac { + factory HmacSha256() => _instance ??= HmacSha256._(); + HmacSha256._() : super(HashAlgorithm.sha256); + static HmacSha256? _instance; +} + +/// A [Hmac] that uses the SHA-384 algorithm. +class HmacSha384 extends _Hmac { + factory HmacSha384() => _instance ??= HmacSha384._(); + HmacSha384._() : super(HashAlgorithm.sha384); + static HmacSha384? _instance; +} + +/// A [Hmac] that uses the SHA-512 algorithm. +class HmacSha512 extends _Hmac { + factory HmacSha512() => _instance ??= HmacSha512._(); + HmacSha512._() : super(HashAlgorithm.sha512); + static HmacSha512? _instance; +} diff --git a/packages/native_crypto/lib/src/domain/base_key.dart b/packages/native_crypto/lib/src/domain/base_key.dart new file mode 100644 index 0000000..2dc92cb --- /dev/null +++ b/packages/native_crypto/lib/src/domain/base_key.dart @@ -0,0 +1,37 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:native_crypto/src/domain/byte_array.dart'; + +/// {@template base_key} +/// 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. +/// +/// Note: [BaseKey] is named [BaseKey] instead of Key to avoid conflicts with +/// the Key class from Flutter. +/// {@endtemplate} +abstract class BaseKey extends ByteArray { + /// {@macro base_key} + const BaseKey(super.bytes); + + /// Creates a [BaseKey] from a [List]. + BaseKey.fromList(super.list) : super.fromList(); + + /// Creates a [BaseKey] from a [String] encoded in UTF-8. + BaseKey.fromUtf8(super.encoded) : super.fromUtf8(); + + /// Creates a [BaseKey] from a [String] encoded in UTF-16. + BaseKey.fromUtf16(super.encoded) : super.fromUtf16(); + + /// Creates a [BaseKey] from a [String] encoded in Hexadecimal. + BaseKey.fromBase16(super.encoded) : super.fromBase16(); + + /// Creates a [BaseKey] from a [String] encoded in Base64. + BaseKey.fromBase64(super.encoded) : super.fromBase64(); +} diff --git a/packages/native_crypto/lib/src/interfaces/byte_array.dart b/packages/native_crypto/lib/src/domain/byte_array.dart similarity index 61% rename from packages/native_crypto/lib/src/interfaces/byte_array.dart rename to packages/native_crypto/lib/src/domain/byte_array.dart index 7cdef3a..46bae1c 100644 --- a/packages/native_crypto/lib/src/interfaces/byte_array.dart +++ b/packages/native_crypto/lib/src/domain/byte_array.dart @@ -1,48 +1,49 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: byte_array.dart -// Created Date: 16/12/2021 17:54:16 -// Last Modified: 26/05/2022 17:13:27 -// ----- -// Copyright (c) 2021 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. import 'dart:typed_data'; -import 'package:flutter/foundation.dart'; -import 'package:native_crypto/src/utils/encoding.dart'; -import 'package:native_crypto/src/utils/extensions.dart'; +import 'package:equatable/equatable.dart'; +import 'package:native_crypto/src/core/enums/encoding.dart'; +import 'package:native_crypto/src/core/extensions/list_int_extension.dart'; +import 'package:native_crypto/src/core/extensions/string_extension.dart'; +import 'package:native_crypto/src/core/extensions/uint8_list_extension.dart'; +/// {@template byte_array} /// Represents a byte array. /// /// [ByteArray] wraps a [Uint8List] and provides some useful conversion methods. -@immutable -abstract class ByteArray { - final Uint8List _bytes; - - /// Creates a [ByteArray] from a [Uint8List]. +/// {@endtemplate} +abstract class ByteArray extends Equatable { + /// {@macro byte_array} const ByteArray(this._bytes); - /// Creates a [ByteArray] object from a hexdecimal string. - ByteArray.fromBase16(String encoded) - : _bytes = encoded.toBytes(from: Encoding.base16); + /// Creates a [ByteArray] object from a [List] of [int]. + ByteArray.fromList(List list) : _bytes = list.toTypedList(); - /// Creates a [ByteArray] object from a Base64 string. - ByteArray.fromBase64(String encoded) - : _bytes = encoded.toBytes(from: Encoding.base64); + /// Creates an empty [ByteArray] object from a length. + ByteArray.fromLength(int length, {int fill = 0}) + : _bytes = Uint8List(length)..fillRange(0, length, fill); + + /// Creates a [ByteArray] object from an UTF-16 string. + ByteArray.fromUtf16(String encoded) : _bytes = encoded.toBytes(); /// Creates a [ByteArray] object from an UTF-8 string. ByteArray.fromUtf8(String encoded) : _bytes = encoded.toBytes(from: Encoding.utf8); - /// Creates a [ByteArray] object from an UTF-16 string. - ByteArray.fromUtf16(String encoded) : _bytes = encoded.toBytes(); + /// Creates a [ByteArray] object from a Base64 string. + ByteArray.fromBase64(String encoded) + : _bytes = encoded.toBytes(from: Encoding.base64); - /// Creates an empty [ByteArray] object from a length. - ByteArray.fromLength(int length) : _bytes = Uint8List(length); + /// Creates a [ByteArray] object from a hexdecimal string. + ByteArray.fromBase16(String encoded) + : _bytes = encoded.toBytes(from: Encoding.base16); - /// Creates a [ByteArray] object from a [List] of [int]. - ByteArray.fromList(List list) : _bytes = list.toTypedList(); + final Uint8List _bytes; /// Gets the [ByteArray] bytes. Uint8List get bytes => _bytes; @@ -62,31 +63,6 @@ abstract class ByteArray { /// Gets the [ByteArray] length in bytes. int get length => _bytes.length; - /// Gets the [ByteArray] length in bits. - int get bitLength => _bytes.length * 8; - @override - bool operator ==(Object other) { - if (other is ByteArray) { - for (int i = 0; i < _bytes.length; i++) { - if (_bytes[i] != other._bytes[i]) { - return false; - } - } - - return true; - } - - return false; - } - - @override - int get hashCode { - int hash = 0; - for (int i = 0; i < _bytes.length; i++) { - hash = _bytes[i] + (hash << 6) + (hash << 16) - hash; - } - - return hash; - } + List get props => [_bytes]; } diff --git a/packages/native_crypto/lib/src/domain/cipher.dart b/packages/native_crypto/lib/src/domain/cipher.dart new file mode 100644 index 0000000..6ad3ae1 --- /dev/null +++ b/packages/native_crypto/lib/src/domain/cipher.dart @@ -0,0 +1,32 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/utils/cipher_text.dart'; +import 'package:native_crypto/src/domain/cipher_chunk.dart'; + +/// {@template cipher} +/// Abstract class that defines the behavior of a Cipher. +/// {@endtemplate} +abstract class Cipher { + /// {@macro cipher} + const Cipher(); + /// Encrypts a [Uint8List] and returns a [CipherText]. + Future> encrypt(Uint8List plainText); + + /// Decrypts a [CipherText] and returns a [Uint8List]. + Future decrypt(CipherText cipherText); + + /// Encrypts a File located at [plainTextFile] and saves the result + /// at [cipherTextFile]. + Future encryptFile(File plainTextFile, File cipherTextFile); + + /// Decrypts a File located at [cipherTextFile] and saves the result + /// at [plainTextFile]. + Future decryptFile(File cipherTextFile, File plainTextFile); +} diff --git a/packages/native_crypto/lib/src/domain/cipher_chunk.dart b/packages/native_crypto/lib/src/domain/cipher_chunk.dart new file mode 100644 index 0000000..2af6666 --- /dev/null +++ b/packages/native_crypto/lib/src/domain/cipher_chunk.dart @@ -0,0 +1,31 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:native_crypto/src/domain/byte_array.dart'; + +/// {@template cipher_chunk} +/// A [CipherChunk] is a [ByteArray] that is used to store a chunk of data +/// encrypted by a Cipher. +/// {@endtemplate} +abstract class CipherChunk extends ByteArray { + /// {@macro cipher_chunk} + const CipherChunk(super.bytes); + + /// Creates a [CipherChunk] from a [List]. + CipherChunk.fromList(super.list) : super.fromList(); + + /// Creates a [CipherChunk] from a [String] encoded in UTF-8. + CipherChunk.fromUtf8(super.encoded) : super.fromUtf8(); + + /// Creates a [CipherChunk] from a [String] encoded in UTF-16. + CipherChunk.fromUtf16(super.encoded) : super.fromUtf16(); + + /// Creates a [CipherChunk] from a [String] encoded in Hexadecimal. + CipherChunk.fromBase16(super.encoded) : super.fromBase16(); + + /// Creates a [CipherChunk] from a [String] encoded in Base64. + CipherChunk.fromBase64(super.encoded) : super.fromBase64(); +} diff --git a/packages/native_crypto/lib/src/domain/domain.dart b/packages/native_crypto/lib/src/domain/domain.dart new file mode 100644 index 0000000..58cc8b7 --- /dev/null +++ b/packages/native_crypto/lib/src/domain/domain.dart @@ -0,0 +1,14 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +export 'base_key.dart'; +export 'byte_array.dart'; +export 'cipher.dart'; +export 'cipher_chunk.dart'; +export 'hash.dart'; +export 'hmac.dart'; +export 'key_derivation_function.dart'; +export 'random.dart'; diff --git a/packages/native_crypto/lib/src/domain/hash.dart b/packages/native_crypto/lib/src/domain/hash.dart new file mode 100644 index 0000000..f1e181a --- /dev/null +++ b/packages/native_crypto/lib/src/domain/hash.dart @@ -0,0 +1,24 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/enums/hash_algorithm.dart'; + +/// {@template hash} +/// A [Hash] is a one-way function that takes arbitrary-sized data and +/// outputs a fixed-sized hash value. +/// {@endtemplate} +abstract class Hash { + /// {@macro hash} + const Hash(); + + /// The [HashAlgorithm] used by this [Hash]. + HashAlgorithm get algorithm; + + /// Digests the given [data] and returns the hash. + Future digest(Uint8List data); +} diff --git a/packages/native_crypto/lib/src/domain/hmac.dart b/packages/native_crypto/lib/src/domain/hmac.dart new file mode 100644 index 0000000..45b1923 --- /dev/null +++ b/packages/native_crypto/lib/src/domain/hmac.dart @@ -0,0 +1,25 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/enums/hash_algorithm.dart'; +import 'package:native_crypto/src/keys/secret_key.dart'; + +/// {@template hmac} +/// A HMAC is a cryptographic hash that uses a key to sign a message. +/// The receiver verifies the hash by recomputing it using the same key. +/// {@endtemplate} +abstract class Hmac { + /// {@macro hmac} + const Hmac(); + + /// The [HashAlgorithm] used by this [Hmac]. + HashAlgorithm get algorithm; + + /// Digests the given [data] and returns the hmac. + Future digest(Uint8List data, SecretKey key); +} diff --git a/packages/native_crypto/lib/src/domain/key_derivation_function.dart b/packages/native_crypto/lib/src/domain/key_derivation_function.dart new file mode 100644 index 0000000..d337a6b --- /dev/null +++ b/packages/native_crypto/lib/src/domain/key_derivation_function.dart @@ -0,0 +1,27 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +/// {@template key_derivation_function} +/// A [KeyDerivationFunction] is a function that derives a key from an +/// [Uint8List] key material. +/// {@endtemplate} +abstract class KeyDerivationFunction { + /// {@macro key_derivation_function} + const KeyDerivationFunction(); + + /// Derives a key from a [keyMaterial]. + Future derive( + Uint8List keyMaterial, + ); + + /// Verifies a [keyMaterial] against an [expected] value. + Future verify( + Uint8List keyMaterial, + Uint8List expected, + ); +} diff --git a/packages/native_crypto/lib/src/domain/random.dart b/packages/native_crypto/lib/src/domain/random.dart new file mode 100644 index 0000000..7592e21 --- /dev/null +++ b/packages/native_crypto/lib/src/domain/random.dart @@ -0,0 +1,18 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +/// {@template random} +/// A [Random] is a source of random bytes. +/// {@endtemplate} +abstract class Random { + /// {@macro random} + const Random(); + + /// Generates a random [Uint8List] of [length] bytes. + Future generate(int length); +} diff --git a/packages/native_crypto/lib/src/interfaces/base_key.dart b/packages/native_crypto/lib/src/interfaces/base_key.dart deleted file mode 100644 index e8ac27e..0000000 --- a/packages/native_crypto/lib/src/interfaces/base_key.dart +++ /dev/null @@ -1,22 +0,0 @@ -// 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(); -} diff --git a/packages/native_crypto/lib/src/interfaces/builder.dart b/packages/native_crypto/lib/src/interfaces/builder.dart deleted file mode 100644 index a1b39aa..0000000 --- a/packages/native_crypto/lib/src/interfaces/builder.dart +++ /dev/null @@ -1,14 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: builder.dart -// Created Date: 28/12/2021 12:02:34 -// Last Modified: 23/05/2022 22:38:44 -// ----- -// Copyright (c) 2021 - -// ignore_for_file: one_member_abstracts - -abstract class Builder { - Future build(); -} diff --git a/packages/native_crypto/lib/src/interfaces/cipher.dart b/packages/native_crypto/lib/src/interfaces/cipher.dart deleted file mode 100644 index 58b0d4a..0000000 --- a/packages/native_crypto/lib/src/interfaces/cipher.dart +++ /dev/null @@ -1,53 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: cipher.dart -// Created Date: 16/12/2021 16:28:00 -// Last Modified: 26/05/2022 21:21:07 -// ----- -// Copyright (c) 2021 - -import 'dart:typed_data'; - -import 'package:native_crypto/src/core/cipher_text_wrapper.dart'; -import 'package:native_crypto/src/utils/cipher_algorithm.dart'; - -/// Represents a cipher in NativeCrypto. -/// -/// In cryptography, a [Cipher] is an algorithm for performing encryption -/// or decryption - a series of well-defined steps that can -/// be followed as a procedure. -/// -/// This interface is implemented by all the ciphers in NativeCrypto. -abstract class Cipher { - static const int _bytesCountPerChunkDefault = 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 - /// that can be processed by the [Cipher]. - 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) { - _bytesCountPerChunk = bytesCount; - } - - /// Returns the standard algorithm for this [Cipher]. - CipherAlgorithm get algorithm; - - /// Encrypts the [data]. - /// - /// Takes [Uint8List] data as parameter. - /// Returns a [CipherTextWrapper]. - Future encrypt(Uint8List data); - - /// Decrypts the [cipherText] - /// - /// Takes [CipherTextWrapper] as parameter. - /// And returns plain text data as [Uint8List]. - Future decrypt(CipherTextWrapper cipherText); -} diff --git a/packages/native_crypto/lib/src/interfaces/interfaces.dart b/packages/native_crypto/lib/src/interfaces/interfaces.dart deleted file mode 100644 index ef47be3..0000000 --- a/packages/native_crypto/lib/src/interfaces/interfaces.dart +++ /dev/null @@ -1,14 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: interfaces.dart -// Created Date: 23/05/2022 23:03:47 -// Last Modified: 26/05/2022 17:41:06 -// ----- -// Copyright (c) 2022 - -export 'base_key.dart'; -export 'builder.dart'; -export 'byte_array.dart'; -export 'cipher.dart'; -export 'keyderivation.dart'; diff --git a/packages/native_crypto/lib/src/interfaces/keyderivation.dart b/packages/native_crypto/lib/src/interfaces/keyderivation.dart deleted file mode 100644 index ccdbb4b..0000000 --- a/packages/native_crypto/lib/src/interfaces/keyderivation.dart +++ /dev/null @@ -1,23 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: kdf.dart -// Created Date: 18/12/2021 11:56:43 -// Last Modified: 26/05/2022 18:47:15 -// ----- -// Copyright (c) 2021 - -import 'package:native_crypto/src/keys/secret_key.dart'; -import 'package:native_crypto/src/utils/kdf_algorithm.dart'; - -/// Represents a Key Derivation Function (KDF) in NativeCrypto. -/// -/// [KeyDerivation] function is a function that takes some -/// parameters and returns a [SecretKey]. -abstract class KeyDerivation { - /// Returns the standard algorithm for this key derivation function - KdfAlgorithm get algorithm; - - /// Derive a [SecretKey]. - Future derive(); -} diff --git a/packages/native_crypto/lib/src/kdf/kdf.dart b/packages/native_crypto/lib/src/kdf/kdf.dart deleted file mode 100644 index cb7d609..0000000 --- a/packages/native_crypto/lib/src/kdf/kdf.dart +++ /dev/null @@ -1,10 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: kdf.dart -// Created Date: 23/05/2022 22:57:11 -// Last Modified: 23/05/2022 23:04:15 -// ----- -// Copyright (c) 2022 - -export 'pbkdf2.dart'; diff --git a/packages/native_crypto/lib/src/kdf/pbkdf2.dart b/packages/native_crypto/lib/src/kdf/pbkdf2.dart index 8ccdadd..21373af 100644 --- a/packages/native_crypto/lib/src/kdf/pbkdf2.dart +++ b/packages/native_crypto/lib/src/kdf/pbkdf2.dart @@ -1,115 +1,93 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: pbkdf2.dart -// Created Date: 17/12/2021 14:50:42 -// Last Modified: 26/05/2022 23:19:46 -// ----- -// Copyright (c) 2021 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. -import 'dart:typed_data'; - -import 'package:native_crypto/src/interfaces/keyderivation.dart'; -import 'package:native_crypto/src/keys/secret_key.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/kdf_algorithm.dart'; +import 'package:flutter/foundation.dart'; +import 'package:native_crypto/native_crypto.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 { - final int _keyBytesCount; - final int _iterations; - final HashAlgorithm _hash; +/// {@template pbkdf2} +/// A PBKDF2 is a password-based key derivation function. +/// {@endtemplate} +class Pbkdf2 extends KeyDerivationFunction { + /// {@macro pbkdf2} + const Pbkdf2({ + required this.hashAlgorithm, + required this.iterations, + required this.salt, + required this.length, + }); + + /// The [HashAlgorithm] used by this [Pbkdf2]. + final HashAlgorithm hashAlgorithm; + + /// The number of iterations. + final int iterations; + + /// The salt. + final Uint8List salt; + + /// The length of the derived key in bytes. + final int length; @override - KdfAlgorithm get algorithm => KdfAlgorithm.pbkdf2; + Future derive(Uint8List keyMaterial) async { + if (length == 0) { + // If the length is 0, return an empty list + return Uint8List(0); + } - Pbkdf2({ - required int keyBytesCount, - required int iterations, - HashAlgorithm algorithm = HashAlgorithm.sha256, - }) : _keyBytesCount = keyBytesCount, - _iterations = iterations, - _hash = algorithm { - if (keyBytesCount < 0) { - throw NativeCryptoException( - message: 'keyBytesCount must be positive.', - code: NativeCryptoExceptionCode.invalid_argument.code, - ); + if (length < 0) { + throw ArgumentError.value(length, 'length', 'must be positive'); } if (iterations <= 0) { - throw NativeCryptoException( - message: 'iterations must be strictly positive.', - code: NativeCryptoExceptionCode.invalid_argument.code, + throw ArgumentError.value( + iterations, + 'iterations', + 'must greater than 0', ); } + + // Call the platform interface to derive the key + final bytes = await platform.pbkdf2( + password: keyMaterial, + salt: salt, + iterations: iterations, + length: length, + hashAlgorithm: hashAlgorithm.name, + ); + + // TODO(hpcl): move these checks to the platform interface + if (bytes == null) { + throw const NativeCryptoException( + code: NativeCryptoExceptionCode.nullError, + message: 'Platform returned null bytes', + ); + } + + if (bytes.length != length) { + throw NativeCryptoException( + code: NativeCryptoExceptionCode.invalidData, + message: 'Platform returned bytes of wrong length: ' + 'expected $length, got ${bytes.length}', + ); + } + + return bytes; } @override - Future derive({String? password, String? salt}) async { - Uint8List? derivation; + Future verify(Uint8List keyMaterial, Uint8List expected) => + derive(keyMaterial).then((actual) { + if (actual.length != expected.length) { + return false; + } + return listEquals(actual, expected); + }); - if (_keyBytesCount == 0) { - return SecretKey(Uint8List(0)); - } - if (password.isNull) { - throw NativeCryptoException( - message: 'Password cannot be null.', - code: NativeCryptoExceptionCode.invalid_argument.code, - ); - } - - if (salt.isNull) { - throw NativeCryptoException( - message: 'Salt cannot be null.', - code: NativeCryptoExceptionCode.invalid_argument.code, - ); - } - - 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) { - 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); - } + Future call({required String password}) => + derive(password.toBytes()).then(SecretKey.new); } diff --git a/packages/native_crypto/lib/src/keys/keys.dart b/packages/native_crypto/lib/src/keys/keys.dart deleted file mode 100644 index 912bb39..0000000 --- a/packages/native_crypto/lib/src/keys/keys.dart +++ /dev/null @@ -1,10 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: keys.dart -// Created Date: 23/05/2022 23:04:04 -// Last Modified: 23/05/2022 23:04:07 -// ----- -// Copyright (c) 2022 - -export 'secret_key.dart'; diff --git a/packages/native_crypto/lib/src/keys/secret_key.dart b/packages/native_crypto/lib/src/keys/secret_key.dart index e30b87b..3c6b0c5 100644 --- a/packages/native_crypto/lib/src/keys/secret_key.dart +++ b/packages/native_crypto/lib/src/keys/secret_key.dart @@ -1,60 +1,43 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: secret_key.dart -// Created Date: 28/12/2021 13:36:54 -// Last Modified: 26/05/2022 23:13:10 -// ----- -// Copyright (c) 2021 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. -import 'dart:typed_data'; - -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/utils/extensions.dart'; -import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; +import 'package:native_crypto/src/domain/base_key.dart'; +import 'package:native_crypto/src/random/secure_random.dart'; +/// {@template secret_key} /// Represents a secret key in NativeCrypto. /// /// [SecretKey] is a [BaseKey] that can be used to store secret keys. /// A [SecretKey] is a key that can be used to encrypt or decrypt data with -/// a symmetric [Cipher]. +/// a symmetric Cipher. +/// {@endtemplate} class SecretKey extends BaseKey { + /// {@macro secret_key} const SecretKey(super.bytes); + + /// Creates a [SecretKey] from a [List]. + SecretKey.fromList(super.list) : super.fromList(); + + /// Creates a [SecretKey] from a [String] encoded in UTF-8. + SecretKey.fromUtf8(super.encoded) : super.fromUtf8(); + + /// Creates a [SecretKey] from a [String] encoded in UTF-16. + SecretKey.fromUtf16(super.encoded) : super.fromUtf16(); + + /// Creates a [SecretKey] from a [String] encoded in Hexadecimal. SecretKey.fromBase16(super.encoded) : super.fromBase16(); + + /// Creates a [SecretKey] from a [String] encoded in Base64. SecretKey.fromBase64(super.encoded) : super.fromBase64(); - SecretKey.fromUtf8(super.input) : super.fromUtf8(); - static Future fromSecureRandom(int bitsCount) async { - Uint8List? key; - if (bitsCount == 0) { - return SecretKey(Uint8List(0)); - } + /// Generates a random [SecretKey] of the given [length] in bytes. + static Future fromSecureRandom(int length) async { + const random = SecureRandom(); + final bytes = await random.generate(length); - try { - key = await platform.generateSecretKey(bitsCount); - } catch (e, s) { - throw NativeCryptoException( - message: '$e', - code: NativeCryptoExceptionCode.platform_throws.code, - 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); + return SecretKey(bytes); } } diff --git a/packages/native_crypto/lib/src/platform.dart b/packages/native_crypto/lib/src/platform.dart deleted file mode 100644 index 5d62b5e..0000000 --- a/packages/native_crypto/lib/src/platform.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: platform.dart -// Created Date: 27/12/2021 22:03:58 -// Last Modified: 25/05/2022 10:09:18 -// ----- -// Copyright (c) 2021 - -import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; - -NativeCryptoPlatform platform = NativeCryptoPlatform.instance; diff --git a/packages/native_crypto/lib/src/random/secure_random.dart b/packages/native_crypto/lib/src/random/secure_random.dart new file mode 100644 index 0000000..c769e29 --- /dev/null +++ b/packages/native_crypto/lib/src/random/secure_random.dart @@ -0,0 +1,50 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/src/core/utils/platform.dart'; +import 'package:native_crypto/src/domain/random.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +/// {@template secure_random} +/// A [SecureRandom] is a source of secure random bytes. +/// {@endtemplate} +class SecureRandom extends Random { + /// {@macro secure_random} + const SecureRandom(); + + @override + Future generate(int length) async { + if (length < 0) { + throw ArgumentError.value(length, 'length', 'must be positive'); + } + if (length == 0) { + // If the length is 0, return an empty list + return Uint8List(0); + } + // Call the platform interface to generate the secure random bytes + final bytes = await platform.generateSecureRandom(length); + + // TODO(hpcl): move these checks to the platform interface + if (bytes == null) { + throw const NativeCryptoException( + code: NativeCryptoExceptionCode.nullError, + message: 'Platform returned null bytes', + ); + } + + if (bytes.length != length) { + throw NativeCryptoException( + code: NativeCryptoExceptionCode.invalidData, + message: 'Platform returned bytes of wrong length: ' + 'expected $length, got ${bytes.length}', + ); + } + + return bytes; + } +} diff --git a/packages/native_crypto/lib/src/utils/cipher_algorithm.dart b/packages/native_crypto/lib/src/utils/cipher_algorithm.dart deleted file mode 100644 index 2ba968c..0000000 --- a/packages/native_crypto/lib/src/utils/cipher_algorithm.dart +++ /dev/null @@ -1,11 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: cipher_algorithm.dart -// Created Date: 23/05/2022 22:07:54 -// Last Modified: 26/05/2022 18:52:32 -// ----- -// Copyright (c) 2022 - -/// Represents different cipher algorithms -enum CipherAlgorithm { aes } diff --git a/packages/native_crypto/lib/src/utils/encoding.dart b/packages/native_crypto/lib/src/utils/encoding.dart deleted file mode 100644 index b7ddd80..0000000 --- a/packages/native_crypto/lib/src/utils/encoding.dart +++ /dev/null @@ -1,10 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: encoding.dart -// Created Date: 26/05/2022 12:12:34 -// Last Modified: 26/05/2022 12:18:09 -// ----- -// Copyright (c) 2022 - -enum Encoding { utf8, utf16, base64, base16 } diff --git a/packages/native_crypto/lib/src/utils/extensions.dart b/packages/native_crypto/lib/src/utils/extensions.dart deleted file mode 100644 index fc2a799..0000000 --- a/packages/native_crypto/lib/src/utils/extensions.dart +++ /dev/null @@ -1,101 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: extensions.dart -// Created Date: 26/05/2022 12:12:48 -// Last Modified: 27/05/2022 12:26:55 -// ----- -// Copyright (c) 2022 - -import 'dart:convert'; -import 'dart:developer' as developer; -import 'dart:typed_data'; - -import 'package:native_crypto/src/utils/encoding.dart'; - -extension ObjectX on Object? { - /// Returns `true` if the object is `null`. - bool get isNull => this == null; - - /// Returns `true` if the object is **not** `null`. - bool get isNotNull => this != null; - - /// Prints the object to the console. - void log() => developer.log(toString()); -} - -extension ListIntX on List { - /// Converts a [List] of int to a [Uint8List]. - Uint8List toTypedList() => Uint8List.fromList(this); -} - -extension ListUint8ListX on List { - /// Reduce a [List] of [Uint8List] to a [Uint8List]. - Uint8List combine() { - if (isEmpty) return Uint8List(0); - return reduce((value, element) => value.plus(element)); - } -} - -extension StringX on String { - /// Converts a [String] to a [Uint8List] using the specified [Encoding]. - Uint8List toBytes({final Encoding from = Encoding.utf16}) { - Uint8List bytes; - switch (from) { - case Encoding.utf8: - bytes = utf8.encode(this).toTypedList(); - break; - case Encoding.utf16: - bytes = runes.toList().toTypedList(); - break; - case Encoding.base64: - bytes = base64.decode(this); - break; - case Encoding.base16: - assert(length.isEven, 'String needs to be an even length.'); - bytes = List.generate( - length ~/ 2, - (i) => int.parse(substring(i * 2, (i * 2) + 2), radix: 16), - ).toList().toTypedList(); - } - return bytes; - } -} - -extension Uint8ListX on Uint8List { - /// Converts a [Uint8List] to a [String] using the specified [Encoding]. - String toStr({final Encoding to = Encoding.utf16}) { - String str; - switch (to) { - case Encoding.utf8: - str = utf8.decode(this); - break; - case Encoding.utf16: - str = String.fromCharCodes(this); - break; - case Encoding.base64: - str = base64.encode(this); - break; - case Encoding.base16: - str = List.generate( - length, - (i) => this[i].toRadixString(16).padLeft(2, '0'), - ).join(); - } - return str; - } - - /// Returns a concatenation of this with the other [Uint8List]. - Uint8List plus(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); - } -} diff --git a/packages/native_crypto/lib/src/utils/hash_algorithm.dart b/packages/native_crypto/lib/src/utils/hash_algorithm.dart deleted file mode 100644 index 4538ef5..0000000 --- a/packages/native_crypto/lib/src/utils/hash_algorithm.dart +++ /dev/null @@ -1,51 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: hash_algorithm.dart -// Created Date: 23/05/2022 22:01:59 -// Last Modified: 26/05/2022 22:59:04 -// ----- -// Copyright (c) 2022 - -import 'dart:typed_data'; - -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. -enum HashAlgorithm { - sha256, - sha384, - sha512; - - /// Digest the [data] using this [HashAlgorithm]. - Future digest(Uint8List data) async { - Uint8List? hash; - 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; - } -} diff --git a/packages/native_crypto/lib/src/utils/kdf_algorithm.dart b/packages/native_crypto/lib/src/utils/kdf_algorithm.dart deleted file mode 100644 index 68d6a76..0000000 --- a/packages/native_crypto/lib/src/utils/kdf_algorithm.dart +++ /dev/null @@ -1,11 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: kdf_algorithm.dart -// Created Date: 23/05/2022 22:36:24 -// Last Modified: 26/05/2022 18:53:50 -// ----- -// Copyright (c) 2022 - -/// Represents different key derivation functions -enum KdfAlgorithm { pbkdf2 } diff --git a/packages/native_crypto/pubspec.yaml b/packages/native_crypto/pubspec.yaml index 72b0d59..e78c351 100644 --- a/packages/native_crypto/pubspec.yaml +++ b/packages/native_crypto/pubspec.yaml @@ -2,22 +2,21 @@ 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" flutter: ">=2.5.0" dependencies: - flutter: - sdk: flutter - + flutter: { sdk: flutter } + native_crypto_android: git: url: https://github.com/hugo-pcl/native-crypto-flutter.git ref: native_crypto_android-v0.1.1 path: packages/native_crypto_android - + native_crypto_ios: git: url: https://github.com/hugo-pcl/native-crypto-flutter.git @@ -29,19 +28,19 @@ dependencies: url: https://github.com/hugo-pcl/native-crypto-flutter.git ref: native_crypto_platform_interface-v0.1.1 path: packages/native_crypto_platform_interface + equatable: ^2.0.5 dev_dependencies: - flutter_test: - sdk: flutter + flutter_test: { sdk: flutter } - mockito: ^5.2.0 - plugin_platform_interface: ^2.1.2 + mockito: ^5.3.2 + plugin_platform_interface: ^2.1.3 wyatt_analysis: - git: - url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages - ref: wyatt_analysis-v2.1.0 - path: packages/wyatt_analysis + hosted: + url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ + name: wyatt_analysis + version: 2.4.0 flutter: plugin: @@ -49,4 +48,4 @@ flutter: android: default_package: native_crypto_android ios: - default_package: native_crypto_ios \ No newline at end of file + default_package: native_crypto_ios diff --git a/packages/native_crypto/pubspec_overrides.yaml b/packages/native_crypto/pubspec_overrides.yaml new file mode 100644 index 0000000..53d57bc --- /dev/null +++ b/packages/native_crypto/pubspec_overrides.yaml @@ -0,0 +1,8 @@ +# melos_managed_dependency_overrides: native_crypto_android,native_crypto_ios,native_crypto_platform_interface,native_crypto_web +dependency_overrides: + native_crypto_android: + path: ../native_crypto_android + native_crypto_ios: + path: ../native_crypto_ios + native_crypto_platform_interface: + path: ../native_crypto_platform_interface diff --git a/packages/native_crypto/test/mocks/mock_native_crypto_api.dart b/packages/native_crypto/test/mocks/mock_native_crypto_api.dart new file mode 100644 index 0000000..e8b4afa --- /dev/null +++ b/packages/native_crypto/test/mocks/mock_native_crypto_api.dart @@ -0,0 +1,193 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +class MockNativeCryptoAPI implements NativeCryptoAPI { + static Uint8List? Function(int length)? generateSecureRandomFn; + + static Uint8List? Function( + Uint8List data, + String algorithm, + )? hashFn; + + static Uint8List? Function( + Uint8List data, + Uint8List key, + String algorithm, + )? hmacFn; + + static Uint8List? Function( + Uint8List cipherText, + Uint8List key, + String algorithm, + )? decryptFn; + + static bool? Function( + String cipherTextPath, + String plainTextPath, + Uint8List key, + String algorithm, + )? decryptFileFn; + + static Uint8List? Function( + Uint8List plainText, + Uint8List key, + String algorithm, + )? encryptFn; + + static bool? Function( + String plainTextPath, + String cipherTextPath, + Uint8List key, + String algorithm, + )? encryptFileFn; + + static Uint8List? Function( + Uint8List plainText, + Uint8List key, + Uint8List iv, + String algorithm, + )? encryptWithIVFn; + + static Uint8List? Function( + Uint8List password, + Uint8List salt, + int iterations, + int length, + String algorithm, + )? pbkdf2Fn; + + @override + Future decrypt(DecryptRequest argRequest) async => + decryptFn != null + ? DecryptResponse( + plainText: decryptFn!( + argRequest.cipherText!, + argRequest.key!, + argRequest.algorithm!, + ), + ) + : DecryptResponse( + plainText: Uint8List.fromList([1, 2, 3]), + ); + + @override + Future decryptFile( + DecryptFileRequest argRequest, + ) async => + decryptFileFn != null + ? DecryptFileResponse( + success: decryptFileFn!( + argRequest.cipherTextPath!, + argRequest.plainTextPath!, + argRequest.key!, + argRequest.algorithm!, + ), + ) + : DecryptFileResponse(success: true); + + @override + Future encrypt(EncryptRequest argRequest) async => + encryptFn != null + ? EncryptResponse( + cipherText: encryptFn!( + argRequest.plainText!, + argRequest.key!, + argRequest.algorithm!, + ), + ) + : EncryptResponse( + cipherText: Uint8List.fromList([1, 2, 3]), + ); + + @override + Future encryptFile( + EncryptFileRequest argRequest, + ) async => + encryptFileFn != null + ? EncryptFileResponse( + success: encryptFileFn!( + argRequest.plainTextPath!, + argRequest.cipherTextPath!, + argRequest.key!, + argRequest.algorithm!, + ), + ) + : EncryptFileResponse(success: true); + + @override + Future encryptWithIV( + EncryptWithIVRequest argRequest, + ) async => + encryptWithIVFn != null + ? EncryptResponse( + cipherText: encryptWithIVFn!( + argRequest.plainText!, + argRequest.key!, + argRequest.iv!, + argRequest.algorithm!, + ), + ) + : EncryptResponse( + cipherText: Uint8List.fromList([1, 2, 3]), + ); + + @override + Future generateSecureRandom( + GenerateSecureRandomRequest argRequest, + ) async => + generateSecureRandomFn != null + ? GenerateSecureRandomResponse( + random: generateSecureRandomFn!(argRequest.length!), + ) + : GenerateSecureRandomResponse( + random: Uint8List.fromList([1, 2, 3]), + ); + + @override + Future hash(HashRequest argRequest) async => hashFn != null + ? HashResponse( + hash: hashFn!( + argRequest.data!, + argRequest.algorithm!, + ), + ) + : HashResponse( + hash: Uint8List.fromList([1, 2, 3]), + ); + + @override + Future hmac(HmacRequest argRequest) async => hmacFn != null + ? HmacResponse( + hmac: hmacFn!( + argRequest.data!, + argRequest.key!, + argRequest.algorithm!, + ), + ) + : HmacResponse( + hmac: Uint8List.fromList([1, 2, 3]), + ); + + @override + Future pbkdf2(Pbkdf2Request argRequest) async => + pbkdf2Fn != null + ? Pbkdf2Response( + key: pbkdf2Fn!( + argRequest.password!, + argRequest.salt!, + argRequest.iterations!, + argRequest.length!, + argRequest.hashAlgorithm!, + ), + ) + : Pbkdf2Response( + key: Uint8List.fromList([1, 2, 3]), + ); +} diff --git a/packages/native_crypto/test/mocks/mock_native_crypto_platform.dart b/packages/native_crypto/test/mocks/mock_native_crypto_platform.dart deleted file mode 100644 index 6e69cb9..0000000 --- a/packages/native_crypto/test/mocks/mock_native_crypto_platform.dart +++ /dev/null @@ -1,192 +0,0 @@ -// 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/aes_cipher_test.dart b/packages/native_crypto/test/src/aes_cipher_test.dart deleted file mode 100644 index f1d0f39..0000000 --- a/packages/native_crypto/test/src/aes_cipher_test.dart +++ /dev/null @@ -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() - .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_test.dart b/packages/native_crypto/test/src/cipher_text_test.dart deleted file mode 100644 index 16d98a5..0000000 --- a/packages/native_crypto/test/src/cipher_text_test.dart +++ /dev/null @@ -1,192 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: cipher_text_test.dart -// Created Date: 26/05/2022 20:45:38 -// Last Modified: 26/05/2022 21:29:51 -// ----- -// Copyright (c) 2022 - -import 'dart:typed_data'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:native_crypto/native_crypto.dart'; - -void main() { - setUp(() { - Cipher.bytesCountPerChunk = Cipher.defaultBytesCountPerChunk; - }); - - group('fromBytes', () { - test('throws if length is not the one expected', () { - final Uint8List bytes = Uint8List.fromList([1, 2, 3, 4, 5]); - expect( - () => CipherText.fromBytes( - bytes, - ivLength: 1, - messageLength: 1, - tagLength: 1, - ), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_argument', - ) - .having( - (e) => e.message, - 'message', - contains('Invalid cipher text length'), - ), - ), - ); - }); - - test('throws if length is bigger than expected', () { - final Uint8List bytes = Uint8List.fromList([1, 3, 3, 3, 1]); - Cipher.bytesCountPerChunk = 2; - expect( - () => CipherText.fromBytes( - bytes, - ivLength: 1, - messageLength: 3, - tagLength: 1, - ), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_argument', - ) - .having( - (e) => e.message, - 'message', - contains('Cipher text is too big'), - ), - ), - ); - }); - - test('throws if data is empty', () { - final Uint8List bytes = Uint8List(0); - expect( - () => CipherText.fromBytes( - bytes, - ivLength: 1, - messageLength: 3, - tagLength: 1, - ), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_argument', - ) - .having( - (e) => e.message, - 'message', - contains('Passed data is empty'), - ), - ), - ); - }); - - test('throws if one of the length is negative', () { - final Uint8List bytes = Uint8List(0); - expect( - () => CipherText.fromBytes( - bytes, - ivLength: -1, - messageLength: 1, - tagLength: 1, - ), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_argument', - ) - .having( - (e) => e.message, - 'message', - contains('Invalid length'), - ), - ), - ); - }); - }); - - group('get.cipherAlgorithm', () { - test('throws if not set', () { - final CipherText cipherText = CipherText.fromBytes( - Uint8List.fromList([1, 2, 3]), - ivLength: 1, - messageLength: 1, - tagLength: 1, - ); - expect( - () => cipherText.cipherAlgorithm, - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_cipher', - ) - .having( - (e) => e.message, - 'message', - contains('Cipher algorithm is not specified'), - ), - ), - ); - }); - - test('returns the expected value', () { - final CipherText cipherText = CipherText.fromBytes( - Uint8List.fromList([1, 2, 3]), - ivLength: 1, - messageLength: 1, - tagLength: 1, - cipherAlgorithm: CipherAlgorithm.aes, - ); - expect(cipherText.cipherAlgorithm, CipherAlgorithm.aes); - }); - }); - - group('Lengths', () { - test('get.ivLength returns the expected value', () { - final CipherText cipherText = CipherText.fromBytes( - Uint8List.fromList([1, 2, 3]), - ivLength: 1, - messageLength: 1, - tagLength: 1, - ); - expect(cipherText.ivLength, 1); - }); - - test('get.messageLength returns the expected value', () { - final CipherText cipherText = CipherText.fromBytes( - Uint8List.fromList([1, 2, 3]), - ivLength: 1, - messageLength: 1, - tagLength: 1, - ); - expect(cipherText.messageLength, 1); - }); - - test('get.tagLength returns the expected value', () { - final CipherText cipherText = CipherText.fromBytes( - Uint8List.fromList([1, 2, 3]), - ivLength: 1, - messageLength: 1, - tagLength: 1, - ); - expect(cipherText.tagLength, 1); - }); - }); -} diff --git a/packages/native_crypto/test/src/cipher_text_wrapper_test.dart b/packages/native_crypto/test/src/cipher_text_wrapper_test.dart deleted file mode 100644 index 8dc0116..0000000 --- a/packages/native_crypto/test/src/cipher_text_wrapper_test.dart +++ /dev/null @@ -1,349 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: cipher_text_wrapper_test.dart -// Created Date: 26/05/2022 21:35:41 -// Last Modified: 27/05/2022 13:46:54 -// ----- -// Copyright (c) 2022 - -import 'dart:typed_data'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:native_crypto/native_crypto.dart'; - -void main() { - late CipherText single; - late List list; - - setUp(() { - Cipher.bytesCountPerChunk = Cipher.defaultBytesCountPerChunk; - single = CipherText.fromBytes( - Uint8List.fromList([1, 2, 3]), - ivLength: 1, - messageLength: 1, - tagLength: 1, - ); - list = [ - CipherText.fromBytes( - Uint8List.fromList([1, 2, 3]), - ivLength: 1, - messageLength: 1, - tagLength: 1, - ), - CipherText.fromBytes( - Uint8List.fromList([4, 5, 6]), - ivLength: 1, - messageLength: 1, - tagLength: 1, - ), - ]; - }); - - group('single', () { - test('makes isSingle true', () { - final wrapper = CipherTextWrapper.single(single); - expect(wrapper.isSingle, isTrue); - }); - - test('makes isList false', () { - final wrapper = CipherTextWrapper.single(single); - expect(wrapper.isList, isFalse); - }); - - test('makes CipherText the single value', () { - final wrapper = CipherTextWrapper.single(single); - expect(wrapper.single, single); - }); - - test('throws when trying to get list', () { - final wrapper = CipherTextWrapper.single(single); - expect( - () => wrapper.list, - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_data', - ) - .having( - (e) => e.message, - 'message', - contains('is not list'), - ), - ), - ); - }); - - test('makes wrapper returns bytes of CipherText', () { - final wrapper = CipherTextWrapper.single(single); - expect(wrapper.bytes, single.bytes); - }); - - test('makes chunkCount = 1', () { - final wrapper = CipherTextWrapper.single(single); - expect(wrapper.chunkCount, 1); - }); - - test('makes unwrap() returns only CipherText', () { - final wrapper = CipherTextWrapper.single(single); - expect(wrapper.unwrap(), single); - }); - - test('makes unwrap() throws when trying to unwrap List', () { - final wrapper = CipherTextWrapper.single(single); - expect( - () => wrapper.unwrap>(), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_data', - ) - .having( - (e) => e.message, - 'message', - contains('you should use unwrap'), - ), - ), - ); - }); - - test('makes adding is not supported', () { - final wrapper = CipherTextWrapper.single(single); - expect( - () => wrapper.add(single), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_data', - ) - .having( - (e) => e.message, - 'message', - contains('is already single'), - ), - ), - ); - }); - }); - - group('list', () { - test('makes isList true', () { - final wrapper = CipherTextWrapper.list(list); - expect(wrapper.isList, isTrue); - }); - - test('makes isSingle false', () { - final wrapper = CipherTextWrapper.list(list); - expect(wrapper.isSingle, isFalse); - }); - - test('makes List the list value', () { - final wrapper = CipherTextWrapper.list(list); - expect(wrapper.list, list); - }); - - test('throws when trying to get single', () { - final wrapper = CipherTextWrapper.list(list); - expect( - () => wrapper.single, - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_data', - ) - .having( - (e) => e.message, - 'message', - contains('is not single'), - ), - ), - ); - }); - - test('makes wrapper returns bytes of all CipherText joined', () { - final wrapper = CipherTextWrapper.list(list); - expect(wrapper.bytes, Uint8List.fromList([1, 2, 3, 4, 5, 6])); - }); - - test('makes chunkCount = 2', () { - final wrapper = CipherTextWrapper.list(list); - expect(wrapper.chunkCount, 2); - }); - - test('makes unwrap() returns List', () { - final wrapper = CipherTextWrapper.list(list); - expect(wrapper.unwrap>(), list); - }); - - test('makes unwrap() throws when trying to unwrap single', () { - final wrapper = CipherTextWrapper.list(list); - expect( - () => wrapper.unwrap(), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_data', - ) - .having( - (e) => e.message, - 'message', - contains('you should use unwrap'), - ), - ), - ); - }); - - test('makes adding is supported', () { - final originalList = List.from(list); - final wrapper = CipherTextWrapper.list(list)..add(single); - printOnFailure(list.length.toString()); - expect(wrapper.list, [...originalList, single]); - }); - }); - - group('empty', () { - test('makes isList true', () { - final wrapper = CipherTextWrapper.empty(); - expect(wrapper.isList, isTrue); - }); - - test('makes isSingle false', () { - final wrapper = CipherTextWrapper.empty(); - expect(wrapper.isSingle, isFalse); - }); - - test('makes List the list value', () { - final wrapper = CipherTextWrapper.empty(); - expect(wrapper.list, []); - }); - - test('throws when trying to get single', () { - final wrapper = CipherTextWrapper.empty(); - expect( - () => wrapper.single, - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_data', - ) - .having( - (e) => e.message, - 'message', - contains('is not single'), - ), - ), - ); - }); - - test('makes wrapper returns empty bytes', () { - final wrapper = CipherTextWrapper.empty(); - expect(wrapper.bytes, Uint8List.fromList([])); - }); - - test('makes chunkCount = 0', () { - final wrapper = CipherTextWrapper.empty(); - expect(wrapper.chunkCount, 0); - }); - - test('makes unwrap() returns empty List', () { - final wrapper = CipherTextWrapper.empty(); - expect(wrapper.unwrap>(), []); - }); - - test('makes unwrap() throws when trying to unwrap single', () { - final wrapper = CipherTextWrapper.empty(); - expect( - () => wrapper.unwrap(), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_data', - ) - .having( - (e) => e.message, - 'message', - contains('you should use unwrap'), - ), - ), - ); - }); - - test('makes adding is supported', () { - final wrapper = CipherTextWrapper.empty()..add(single); - expect(wrapper.list, [single]); - }); - }); - - group('fromBytes', () { - test('creates single from bytes when no too big', () { - final wrapper = CipherTextWrapper.fromBytes( - Uint8List.fromList([1, 2, 3]), - ivLength: 1, - tagLength: 1, - ); - expect(wrapper.isSingle, isTrue); - expect(wrapper.single, single); - }); - - test('creates list from bytes when too big', () { - Cipher.bytesCountPerChunk = 1; - final wrapper = CipherTextWrapper.fromBytes( - Uint8List.fromList([1, 2, 3, 4, 5, 6]), - ivLength: 1, - tagLength: 1, - ); - expect(wrapper.isList, isTrue); - expect(wrapper.list, list); - }); - - test('modifies Cipher.bytesCountPerChunk', () { - expect(Cipher.bytesCountPerChunk, Cipher.defaultBytesCountPerChunk); - CipherTextWrapper.fromBytes( - Uint8List.fromList([1, 2, 3]), - ivLength: 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 #'), - ), - ), - ); - }); - }); -} diff --git a/packages/native_crypto/test/src/digest_test.dart b/packages/native_crypto/test/src/digest_test.dart new file mode 100644 index 0000000..1927ea5 --- /dev/null +++ b/packages/native_crypto/test/src/digest_test.dart @@ -0,0 +1,116 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +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_api.dart'; + +void main() { + setUp(() { + // Mock the platform interface API + NativeCryptoPlatform.instance = BasicMessageChannelNativeCrypto( + api: MockNativeCryptoAPI(), + ); + }); + + group('Hash', () { + test('$Sha256 digest correctly', () async { + final hash = await Sha256().digest('abc'.toBytes()); + expect(hash, isNotNull); + expect( + hash, + Uint8List.fromList([1, 2, 3]), + ); + }); + + test('$Sha256 digest throws if platform returns null', () async { + MockNativeCryptoAPI.hashFn = (input, algorithm) => null; + expect( + () async => Sha256().digest('abc'.toBytes()), + throwsA(isA()), + ); + }); + + test('$Sha256 digest throws if platform returns invalid data', () async { + MockNativeCryptoAPI.hashFn = (input, algorithm) => Uint8List(0); + expect( + () async => Sha256().digest('abcd'.toBytes()), + throwsA(isA()), + ); + }); + + test('$Sha256 returns correct $HashAlgorithm', () async { + final hash = Sha256(); + + expect(hash.algorithm, HashAlgorithm.sha256); + }); + + test('$Sha384 returns correct $HashAlgorithm', () async { + final hash = Sha384(); + + expect(hash.algorithm, HashAlgorithm.sha384); + }); + + test('$Sha512 returns correct $HashAlgorithm', () async { + final hash = Sha512(); + + expect(hash.algorithm, HashAlgorithm.sha512); + }); + }); + + group('Hmac', () { + test('$HmacSha256 digest correctly', () async { + final hash = await HmacSha256() + .digest('abc'.toBytes(), SecretKey.fromUtf16('key')); + expect(hash, isNotNull); + expect( + hash, + Uint8List.fromList([1, 2, 3]), + ); + }); + + test('$HmacSha256 digest throws if platform returns null', () async { + MockNativeCryptoAPI.hmacFn = (input, key, algorithm) => null; + expect( + () async => + HmacSha256().digest('abc'.toBytes(), SecretKey.fromUtf16('key')), + throwsA(isA()), + ); + }); + + test('$HmacSha256 digest throws if platform returns invalid data', + () async { + MockNativeCryptoAPI.hmacFn = (input, key, algorithm) => Uint8List(0); + expect( + () async => + HmacSha256().digest('abc'.toBytes(), SecretKey.fromUtf16('key')), + throwsA(isA()), + ); + }); + + test('$HmacSha256 returns correct $HashAlgorithm', () async { + final hash = HmacSha256(); + + expect(hash.algorithm, HashAlgorithm.sha256); + }); + + test('$HmacSha384 returns correct $HashAlgorithm', () async { + final hash = HmacSha384(); + + expect(hash.algorithm, HashAlgorithm.sha384); + }); + + test('$HmacSha512 returns correct $HashAlgorithm', () async { + final hash = HmacSha512(); + + expect(hash.algorithm, HashAlgorithm.sha512); + }); + }); +} diff --git a/packages/native_crypto/test/src/hash_algorithm_test.dart b/packages/native_crypto/test/src/hash_algorithm_test.dart deleted file mode 100644 index b7911b1..0000000 --- a/packages/native_crypto/test/src/hash_algorithm_test.dart +++ /dev/null @@ -1,128 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: hash_algorithm_test.dart -// Created Date: 26/05/2022 22:28:53 -// Last Modified: 26/05/2022 23:03:03 -// ----- -// Copyright (c) 2022 - -import 'dart:typed_data'; - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:native_crypto/src/utils/hash_algorithm.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('name', () { - test('is sha256 for HashAlgorithm.sha256', () { - expect(HashAlgorithm.sha256.name, 'sha256'); - }); - test('is sha384 for HashAlgorithm.sha384', () { - expect(HashAlgorithm.sha384.name, 'sha384'); - }); - test('is sha512 for HashAlgorithm.sha512', () { - expect(HashAlgorithm.sha512.name, 'sha512'); - }); - }); - - group('digest', () { - test('handles returning empty list', () async { - mock - ..setDigestExpectations( - data: Uint8List.fromList([1, 2, 3]), - algorithm: 'sha256', - ) - ..setResponse(() => Uint8List(0)); - - await expectLater( - () => HashAlgorithm.sha256.digest(Uint8List.fromList([1, 2, 3])), - throwsA( - isA().having( - (e) => e.code, - 'code', - 'platform_returned_empty_data', - ), - ), - ); - }); - - test('handles returning null', () async { - mock - ..setDigestExpectations( - data: Uint8List.fromList([1, 2, 3]), - algorithm: 'sha256', - ) - ..setResponse(() => null); - - await expectLater( - () => HashAlgorithm.sha256.digest(Uint8List.fromList([1, 2, 3])), - throwsA( - isA().having( - (e) => e.code, - 'code', - 'platform_returned_null', - ), - ), - ); - }); - - test('handles throwing PlatformException', () async { - mock - ..setDigestExpectations( - data: Uint8List.fromList([1, 2, 3]), - algorithm: 'sha256', - ) - ..setResponse( - () => throw PlatformException( - code: 'native_crypto', - message: 'dummy error', - ), - ); - - await expectLater( - () => HashAlgorithm.sha256.digest(Uint8List.fromList([1, 2, 3])), - throwsA( - isA() - .having( - (e) => e.message, - 'message', - 'PlatformException(native_crypto, dummy error, null, null)', - ) - .having( - (e) => e.code, - 'code', - 'platform_throws', - ), - ), - ); - }); - - test('returns data on success', () async { - final hash = Uint8List.fromList([4, 5, 6]); - mock - ..setDigestExpectations( - data: Uint8List.fromList([1, 2, 3]), - algorithm: 'sha256', - ) - ..setResponse(() => hash); - - final result = await HashAlgorithm.sha256.digest( - Uint8List.fromList( - [1, 2, 3], - ), - ); - - expect( - result, - hash, - ); - }); - }); -} diff --git a/packages/native_crypto/test/src/pbkdf2_test.dart b/packages/native_crypto/test/src/pbkdf2_test.dart index f0ae084..9732018 100644 --- a/packages/native_crypto/test/src/pbkdf2_test.dart +++ b/packages/native_crypto/test/src/pbkdf2_test.dart @@ -1,280 +1,155 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: pbkdf2_test.dart -// Created Date: 26/05/2022 22:37:27 -// Last Modified: 26/05/2022 23:20:11 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. 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'; +import '../mocks/mock_native_crypto_api.dart'; void main() { - final MockNativeCryptoPlatform mock = MockNativeCryptoPlatform(); - NativeCryptoPlatform.instance = mock; + setUp(() { + // Mock the platform interface API + NativeCryptoPlatform.instance = BasicMessageChannelNativeCrypto( + api: MockNativeCryptoAPI(), + ); - group('Constructor', () { - test('throws if keyBytesCount is negative', () { - expect( - () => Pbkdf2(keyBytesCount: -1, iterations: 10000), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_argument', - ) - .having( - (e) => e.message, - 'message', - contains('must be positive'), - ), - ), - ); - }); - - test('throws if iterations is negative or 0', () { - expect( - () => Pbkdf2(keyBytesCount: 32, iterations: -1), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_argument', - ) - .having( - (e) => e.message, - 'message', - contains('must be strictly positive'), - ), - ), - ); - }); + MockNativeCryptoAPI.pbkdf2Fn = null; }); - group('derive', () { - test('throws if password is null', () async { - final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); - await expectLater( - () => pbkdf2.derive( - salt: 'salt', - ), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_argument', - ) - .having( - (e) => e.message, - 'message', - contains('cannot be null'), - ), - ), - ); - }); - - test('throws if salt is null', () async { - final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); - await expectLater( - () => pbkdf2.derive( - password: 'password', - ), - throwsA( - isA() - .having( - (e) => e.code, - 'code', - 'invalid_argument', - ) - .having( - (e) => e.message, - 'message', - contains('cannot be null'), - ), - ), - ); - }); - - test('handles returning empty list', () async { - mock - ..setPbkdf2Expectations( - password: 'password', - salt: 'salt', - keyBytesCount: 32, - iterations: 10000, - algorithm: 'sha256', - ) - ..setResponse(() => Uint8List(0)); - - final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); - - await expectLater( - () => pbkdf2.derive( - password: 'password', - salt: 'salt', - ), - throwsA( - isA().having( - (e) => e.code, - 'code', - 'platform_returned_empty_data', - ), - ), - ); - }); - - test('handles returning null', () async { - mock - ..setPbkdf2Expectations( - password: 'password', - salt: 'salt', - keyBytesCount: 32, - iterations: 10000, - algorithm: 'sha256', - ) - ..setResponse(() => null); - - final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); - - await expectLater( - () => pbkdf2.derive( - password: 'password', - salt: 'salt', - ), - throwsA( - isA().having( - (e) => e.code, - 'code', - 'platform_returned_null', - ), - ), - ); - }); - - test('handles returning data with wrong length', () async { - mock - ..setPbkdf2Expectations( - password: 'password', - salt: 'salt', - keyBytesCount: 32, - iterations: 10000, - algorithm: 'sha256', - ) - ..setResponse(() => Uint8List(33)); - - final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); - - await expectLater( - () => pbkdf2.derive( - password: 'password', - salt: 'salt', - ), - throwsA( - isA().having( - (e) => e.code, - 'code', - 'platform_returned_invalid_data', - ), - ), - ); - }); - - test('handles throwing PlatformException', () async { - mock - ..setPbkdf2Expectations( - password: 'password', - salt: 'salt', - keyBytesCount: 32, - iterations: 10000, - algorithm: 'sha256', - ) - ..setResponse( - () => throw PlatformException( - code: 'native_crypto', - message: 'dummy error', - ), - ); - - final pbkdf2 = Pbkdf2(keyBytesCount: 32, iterations: 10000); - - await expectLater( - () => pbkdf2.derive( - password: 'password', - salt: 'salt', - ), - throwsA( - isA() - .having( - (e) => e.message, - 'message', - 'PlatformException(native_crypto, dummy error, null, null)', - ) - .having( - (e) => e.code, - 'code', - 'platform_throws', - ), - ), - ); - }); - - test('returns SecretKey on success', () async { - final data = Uint8List.fromList([1, 2, 3, 4, 5, 6]); - final sk = SecretKey(data); - mock - ..setPbkdf2Expectations( - password: 'password', - salt: 'salt', - keyBytesCount: 6, - iterations: 10000, - algorithm: 'sha256', - ) - ..setResponse(() => data); - - final pbkdf = Pbkdf2(keyBytesCount: 6, iterations: 10000); - final result = await pbkdf.derive( - password: 'password', - salt: 'salt', + group('$Pbkdf2', () { + test('derive key correctly', () async { + final key = await Pbkdf2( + salt: Uint8List.fromList([1, 2, 3]), + iterations: 1, + length: 3, + hashAlgorithm: HashAlgorithm.sha256, + ).derive( + Uint8List.fromList([1, 2, 3]), ); + expect(key, isNotNull); + expect(key.length, 3); expect( - result, - sk, + key, + Uint8List.fromList([1, 2, 3]), ); }); - test('return empty SecretKey when keyBytesCount is set to 0', () async { - final sk = SecretKey(Uint8List(0)); - mock - ..setPbkdf2Expectations( - password: 'password', - salt: 'salt', - keyBytesCount: 0, - iterations: 10000, - algorithm: 'sha256', - ) - ..setResponse(() => Uint8List(0)); - - final pbkdf = Pbkdf2(keyBytesCount: 0, iterations: 10000); - final result = await pbkdf.derive( - password: 'password', - salt: 'salt', - ); - + test('derive key with invalid length throws', () async { expect( - result, - sk, + () => Pbkdf2( + salt: Uint8List.fromList([1, 2, 3]), + iterations: 1, + length: -1, + hashAlgorithm: HashAlgorithm.sha256, + ).derive( + Uint8List.fromList([1, 2, 3]), + ), + throwsA(isA()), ); }); + + test('derive key with invalid iterations throws', () async { + expect( + () => Pbkdf2( + salt: Uint8List.fromList([1, 2, 3]), + iterations: 0, + length: 3, + hashAlgorithm: HashAlgorithm.sha256, + ).derive( + Uint8List.fromList([1, 2, 3]), + ), + throwsA(isA()), + ); + }); + + test('derive key with 0 length returns empty list', () async { + final key = await Pbkdf2( + salt: Uint8List.fromList([1, 2, 3]), + iterations: 1, + length: 0, + hashAlgorithm: HashAlgorithm.sha256, + ).derive( + Uint8List.fromList([1, 2, 3]), + ); + expect(key, isNotNull); + expect(key.length, 0); + }); + + test('derive key throws if platform returns null', () async { + MockNativeCryptoAPI.pbkdf2Fn = + (password, salt, iterations, length, hashAlgorithm) => null; + expect( + () => Pbkdf2( + salt: Uint8List.fromList([1, 2, 3]), + iterations: 1, + length: 3, + hashAlgorithm: HashAlgorithm.sha256, + ).derive( + Uint8List.fromList([1, 2, 3]), + ), + throwsA(isA()), + ); + }); + + test('derive key throws if platform returns invalid data', () async { + expect( + () async => Pbkdf2( + salt: Uint8List.fromList([1, 2, 3]), + iterations: 1, + length: 4, + hashAlgorithm: HashAlgorithm.sha256, + ).derive( + Uint8List.fromList([1, 2, 3]), + ), + throwsA(isA()), + ); + }); + + test('call returns $SecretKey', () async { + final pbkdf = Pbkdf2( + salt: Uint8List.fromList([1, 2, 3]), + iterations: 1, + length: 3, + hashAlgorithm: HashAlgorithm.sha256, + ); + + final key = await pbkdf(password: 'password'); + + expect(key, isNotNull); + expect(key, isA()); + }); + + test('verify key returns true on the same password', () async { + final pbkdf = Pbkdf2( + salt: Uint8List.fromList([1, 2, 3]), + iterations: 1, + length: 3, + hashAlgorithm: HashAlgorithm.sha256, + ); + final pwd = Uint8List.fromList([1, 2, 3]); + final key = await pbkdf.derive(pwd); + final sucess = await pbkdf.verify(pwd, key); + expect(sucess, true); + }); + + test('verify key returns true on the same password', () async { + final pbkdf = Pbkdf2( + salt: Uint8List.fromList([1, 2, 3]), + iterations: 1, + length: 3, + hashAlgorithm: HashAlgorithm.sha256, + ); + final pwd = Uint8List.fromList([1, 2, 3]); + final key = Uint8List.fromList([1, 2, 3, 4, 5, 6]); + final sucess = await pbkdf.verify(pwd, key); + expect(sucess, false); + }); }); } diff --git a/packages/native_crypto/test/src/random_test.dart b/packages/native_crypto/test/src/random_test.dart new file mode 100644 index 0000000..71fb794 --- /dev/null +++ b/packages/native_crypto/test/src/random_test.dart @@ -0,0 +1,63 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +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_api.dart'; + +void main() { + setUp(() { + // Mock the platform interface API + NativeCryptoPlatform.instance = BasicMessageChannelNativeCrypto( + api: MockNativeCryptoAPI(), + ); + }); + + group('$SecureRandom', () { + test('generate random bytes correctly', () async { + final random = await const SecureRandom().generate(3); + expect(random, isNotNull); + expect(random.length, 3); + expect( + random, + Uint8List.fromList([1, 2, 3]), + ); + }); + + test('generate random bytes with invalid length throws', () async { + expect( + () => const SecureRandom().generate(-1), + throwsA(isA()), + ); + }); + + test('generate random bytes with 0 length returns empty list', () async { + final random = await const SecureRandom().generate(0); + expect(random, isNotNull); + expect(random.length, 0); + }); + + test('generate random bytes throws if platform returns null', () async { + MockNativeCryptoAPI.generateSecureRandomFn = (length) => null; + expect( + () async => const SecureRandom().generate(3), + throwsA(isA()), + ); + }); + + test('generate random bytes throws if platform returns invalid data', + () async { + expect( + () async => const SecureRandom().generate(4), + throwsA(isA()), + ); + }); + }); +} diff --git a/packages/native_crypto/test/src/secret_key_test.dart b/packages/native_crypto/test/src/secret_key_test.dart index df2f159..29716fe 100644 --- a/packages/native_crypto/test/src/secret_key_test.dart +++ b/packages/native_crypto/test/src/secret_key_test.dart @@ -1,125 +1,56 @@ -// 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 22:38:07 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. 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/native_crypto.dart'; import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; -import '../mocks/mock_native_crypto_platform.dart'; +import '../mocks/mock_native_crypto_api.dart'; void main() { - final MockNativeCryptoPlatform mock = MockNativeCryptoPlatform(); - NativeCryptoPlatform.instance = mock; + setUp(() { + // Mock the platform interface API + NativeCryptoPlatform.instance = BasicMessageChannelNativeCrypto( + api: MockNativeCryptoAPI(), + ); + }); - group('Constructors', () { - test('handles Uint8List', () { + group('$SecretKey', () { + test('can be create from 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', () { + test('can be create from base16', () { final SecretKey key = SecretKey.fromBase16('0102030405'); expect(key.bytes, Uint8List.fromList([1, 2, 3, 4, 5])); }); - test('handles base64', () { + test('can be create from base64', () { final SecretKey key = SecretKey.fromBase64('AQIDBAU='); expect(key.bytes, Uint8List.fromList([1, 2, 3, 4, 5])); }); - test('handles utf8', () { + test('can be create from utf8', () { final SecretKey key = SecretKey.fromUtf8('ABCDE'); expect(key.bytes, Uint8List.fromList([65, 66, 67, 68, 69])); }); - }); - group('fromSecureRandom', () { - test('handles returning random bytes', () async { - mock - ..setGenerateKeyExpectations(bitsCount: 5) - ..setResponse(() => Uint8List.fromList([1, 2, 3, 4, 5])); + test('can be create from secure random', () async { + MockNativeCryptoAPI.generateSecureRandomFn = + (length) => Uint8List.fromList([1, 2, 3, 4, 5]); + final SecretKey key = await SecretKey.fromSecureRandom(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_empty_data', - ), - ), - ); - }); - - 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', - 'platform_throws', - ), - ), - ); + expect(key.bytes, Uint8List.fromList([1, 2, 3, 4, 5])); }); }); } -- 2.47.2 From f570ed076a8036ba8f15874f4bcfca940be9ffa6 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Tue, 4 Apr 2023 22:36:10 +0200 Subject: [PATCH 09/21] feat(interface)!: set pigeon as default implementation --- .../native_crypto_platform_interface/LICENSE | 4 +- .../README.md | 10 +- .../analysis_options.yaml | 5 - .../lib/native_crypto_platform_interface.dart | 4 +- .../native_crypto_platform_interface_gen.dart | 11 + .../lib/src/core/utils/enum_parser.dart | 27 + .../lib/src/gen/messages.g.dart | 256 ++++++ .../lib/src/gen/test.g.dart | 307 +++++++ .../basic_message_channel_native_crypto.dart | 227 +++-- .../src/interface/native_crypto_platform.dart | 43 +- .../lib/src/pigeon/messages.pigeon.dart | 829 ------------------ .../lib/src/pigeon/test_api.dart | 316 ------- .../pigeons/messages.dart | 248 ++---- .../pubspec.yaml | 8 +- .../native_crypto_platform_test.dart | 12 +- 15 files changed, 812 insertions(+), 1495 deletions(-) create mode 100644 packages/native_crypto_platform_interface/lib/native_crypto_platform_interface_gen.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/core/utils/enum_parser.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/gen/messages.g.dart create mode 100644 packages/native_crypto_platform_interface/lib/src/gen/test.g.dart delete mode 100644 packages/native_crypto_platform_interface/lib/src/pigeon/messages.pigeon.dart delete mode 100644 packages/native_crypto_platform_interface/lib/src/pigeon/test_api.dart diff --git a/packages/native_crypto_platform_interface/LICENSE b/packages/native_crypto_platform_interface/LICENSE index 68bb0c6..5d49a94 100644 --- a/packages/native_crypto_platform_interface/LICENSE +++ b/packages/native_crypto_platform_interface/LICENSE @@ -2,7 +2,7 @@ NativeCrypto - Platform Interface MIT License -Copyright (c) 2019 - 2022 Hugo Pointcheval +Copyright (c) 2019 - 2023 Hugo Pointcheval Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/packages/native_crypto_platform_interface/README.md b/packages/native_crypto_platform_interface/README.md index d1cde17..4aec31b 100644 --- a/packages/native_crypto_platform_interface/README.md +++ b/packages/native_crypto_platform_interface/README.md @@ -8,5 +8,13 @@ This interface allows platform-specific implementations of the `native_crypto` p To implement a new platform-specific implementation of `native_crypto`, extend [`NativeCryptoPlatform`][2] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `NativeCryptoPlatform` by calling `NativeCryptoPlatform.instance = MyNativeCryptoPlatform()`. +## Pigeon + +This package uses [Pigeon](https://pub.dev/packages/pigeon) to generate the platform interface code. + +Run generator with `flutter pub run pigeon --input pigeons/messages.dart`. + +> Note: Make sure the `lib/src/gen` folder exists before running the generator. + [1]: ../native_crypto -[2]: lib/native_crypto_platform_interface.dart \ No newline at end of file +[2]: lib/native_crypto_platform_interface.dart diff --git a/packages/native_crypto_platform_interface/analysis_options.yaml b/packages/native_crypto_platform_interface/analysis_options.yaml index 224f249..8c9daa4 100644 --- a/packages/native_crypto_platform_interface/analysis_options.yaml +++ b/packages/native_crypto_platform_interface/analysis_options.yaml @@ -1,6 +1 @@ include: package:wyatt_analysis/analysis_options.flutter.yaml - -analyzer: - exclude: - - "**/*.pigeon.dart" - - "lib/src/pigeon/test_api.dart" \ No newline at end of file diff --git a/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart index dbfa27c..cc6c0ce 100644 --- a/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart +++ b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart @@ -10,8 +10,6 @@ library native_crypto_platform_interface; export 'src/core/enums/exception_code.dart'; export 'src/core/enums/methods.dart'; export 'src/core/exceptions/exception.dart'; +export 'src/gen/test.g.dart'; export 'src/implementations/basic_message_channel_native_crypto.dart'; -export 'src/implementations/method_channel_native_crypto.dart'; export 'src/interface/native_crypto_platform.dart'; -export 'src/pigeon/messages.pigeon.dart'; -export 'src/pigeon/test_api.dart'; diff --git a/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface_gen.dart b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface_gen.dart new file mode 100644 index 0000000..f3f4a34 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/native_crypto_platform_interface_gen.dart @@ -0,0 +1,11 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +/// The interface that implementations of native_crypto must implement. +library native_crypto_platform_interface; + +export 'src/gen/messages.g.dart'; +export 'src/gen/test.g.dart'; diff --git a/packages/native_crypto_platform_interface/lib/src/core/utils/enum_parser.dart b/packages/native_crypto_platform_interface/lib/src/core/utils/enum_parser.dart new file mode 100644 index 0000000..e565f2d --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/core/utils/enum_parser.dart @@ -0,0 +1,27 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:native_crypto_platform_interface/src/gen/messages.g.dart'; + +abstract class EnumParser { + static HashAlgorithm hashAlgorithmFromString(String value) { + for (final algorithm in HashAlgorithm.values) { + if (algorithm.name == value) { + return algorithm; + } + } + throw ArgumentError('Invalid algorithm: $value'); + } + + static CipherAlgorithm cipherAlgorithmFromString(String value) { + for (final algorithm in CipherAlgorithm.values) { + if (algorithm.name == value) { + return algorithm; + } + } + throw ArgumentError('Invalid algorithm: $value'); + } +} diff --git a/packages/native_crypto_platform_interface/lib/src/gen/messages.g.dart b/packages/native_crypto_platform_interface/lib/src/gen/messages.g.dart new file mode 100644 index 0000000..b2061ff --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/gen/messages.g.dart @@ -0,0 +1,256 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.2.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +enum HashAlgorithm { + sha256, + sha384, + sha512, +} + +enum CipherAlgorithm { + aes, +} + +class NativeCryptoAPI { + /// Constructor for [NativeCryptoAPI]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + NativeCryptoAPI({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + Future hash(Uint8List arg_data, HashAlgorithm arg_algorithm) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.hash', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_data, arg_algorithm.index]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as Uint8List?); + } + } + + Future hmac(Uint8List arg_data, Uint8List arg_key, HashAlgorithm arg_algorithm) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.hmac', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_data, arg_key, arg_algorithm.index]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as Uint8List?); + } + } + + Future generateSecureRandom(int arg_length) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_length]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as Uint8List?); + } + } + + Future pbkdf2(Uint8List arg_password, Uint8List arg_salt, int arg_length, int arg_iterations, HashAlgorithm arg_algorithm) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.pbkdf2', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_password, arg_salt, arg_length, arg_iterations, arg_algorithm.index]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as Uint8List?); + } + } + + Future encrypt(Uint8List arg_plainText, Uint8List arg_key, CipherAlgorithm arg_algorithm) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encrypt', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_plainText, arg_key, arg_algorithm.index]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as Uint8List?); + } + } + + Future encryptWithIV(Uint8List arg_plainText, Uint8List arg_iv, Uint8List arg_key, CipherAlgorithm arg_algorithm) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_plainText, arg_iv, arg_key, arg_algorithm.index]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as Uint8List?); + } + } + + Future decrypt(Uint8List arg_cipherText, Uint8List arg_key, CipherAlgorithm arg_algorithm) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.decrypt', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_cipherText, arg_key, arg_algorithm.index]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as Uint8List?); + } + } + + Future encryptFile(String arg_plainTextPath, String arg_cipherTextPath, Uint8List arg_key, CipherAlgorithm arg_algorithm) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptFile', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_plainTextPath, arg_cipherTextPath, arg_key, arg_algorithm.index]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as bool?); + } + } + + Future encryptFileWithIV(String arg_plainTextPath, String arg_cipherTextPath, Uint8List arg_iv, Uint8List arg_key, CipherAlgorithm arg_algorithm) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_plainTextPath, arg_cipherTextPath, arg_iv, arg_key, arg_algorithm.index]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as bool?); + } + } + + Future decryptFile(String arg_cipherTextPath, String arg_plainTextPath, Uint8List arg_key, CipherAlgorithm arg_algorithm) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.decryptFile', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_cipherTextPath, arg_plainTextPath, arg_key, arg_algorithm.index]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return (replyList[0] as bool?); + } + } +} diff --git a/packages/native_crypto_platform_interface/lib/src/gen/test.g.dart b/packages/native_crypto_platform_interface/lib/src/gen/test.g.dart new file mode 100644 index 0000000..14c7da7 --- /dev/null +++ b/packages/native_crypto_platform_interface/lib/src/gen/test.g.dart @@ -0,0 +1,307 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.2.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import +// ignore_for_file: avoid_relative_lib_imports +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'messages.g.dart'; + +abstract class TestNativeCryptoAPI { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec codec = StandardMessageCodec(); + + Uint8List? hash(Uint8List data, HashAlgorithm algorithm); + + Uint8List? hmac(Uint8List data, Uint8List key, HashAlgorithm algorithm); + + Uint8List? generateSecureRandom(int length); + + Uint8List? pbkdf2(Uint8List password, Uint8List salt, int length, int iterations, HashAlgorithm algorithm); + + Uint8List? encrypt(Uint8List plainText, Uint8List key, CipherAlgorithm algorithm); + + Uint8List? encryptWithIV(Uint8List plainText, Uint8List iv, Uint8List key, CipherAlgorithm algorithm); + + Uint8List? decrypt(Uint8List cipherText, Uint8List key, CipherAlgorithm algorithm); + + bool? encryptFile(String plainTextPath, String cipherTextPath, Uint8List key, CipherAlgorithm algorithm); + + bool? encryptFileWithIV(String plainTextPath, String cipherTextPath, Uint8List iv, Uint8List key, CipherAlgorithm algorithm); + + bool? decryptFile(String cipherTextPath, String plainTextPath, Uint8List key, CipherAlgorithm algorithm); + + static void setup(TestNativeCryptoAPI? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.hash', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hash was null.'); + final List args = (message as List?)!; + final Uint8List? arg_data = (args[0] as Uint8List?); + assert(arg_data != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hash was null, expected non-null Uint8List.'); + final HashAlgorithm? arg_algorithm = args[1] == null ? null : HashAlgorithm.values[args[1] as int]; + assert(arg_algorithm != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hash was null, expected non-null HashAlgorithm.'); + final Uint8List? output = api.hash(arg_data!, arg_algorithm!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.hmac', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hmac was null.'); + final List args = (message as List?)!; + final Uint8List? arg_data = (args[0] as Uint8List?); + assert(arg_data != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hmac was null, expected non-null Uint8List.'); + final Uint8List? arg_key = (args[1] as Uint8List?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hmac was null, expected non-null Uint8List.'); + final HashAlgorithm? arg_algorithm = args[2] == null ? null : HashAlgorithm.values[args[2] as int]; + assert(arg_algorithm != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hmac was null, expected non-null HashAlgorithm.'); + final Uint8List? output = api.hmac(arg_data!, arg_key!, arg_algorithm!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom was null.'); + final List args = (message as List?)!; + final int? arg_length = (args[0] as int?); + assert(arg_length != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom was null, expected non-null int.'); + final Uint8List? output = api.generateSecureRandom(arg_length!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.pbkdf2', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null.'); + final List args = (message as List?)!; + final Uint8List? arg_password = (args[0] as Uint8List?); + assert(arg_password != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null, expected non-null Uint8List.'); + final Uint8List? arg_salt = (args[1] as Uint8List?); + assert(arg_salt != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null, expected non-null Uint8List.'); + final int? arg_length = (args[2] as int?); + assert(arg_length != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null, expected non-null int.'); + final int? arg_iterations = (args[3] as int?); + assert(arg_iterations != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null, expected non-null int.'); + final HashAlgorithm? arg_algorithm = args[4] == null ? null : HashAlgorithm.values[args[4] as int]; + assert(arg_algorithm != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null, expected non-null HashAlgorithm.'); + final Uint8List? output = api.pbkdf2(arg_password!, arg_salt!, arg_length!, arg_iterations!, arg_algorithm!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encrypt', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encrypt was null.'); + final List args = (message as List?)!; + final Uint8List? arg_plainText = (args[0] as Uint8List?); + assert(arg_plainText != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encrypt was null, expected non-null Uint8List.'); + final Uint8List? arg_key = (args[1] as Uint8List?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encrypt was null, expected non-null Uint8List.'); + final CipherAlgorithm? arg_algorithm = args[2] == null ? null : CipherAlgorithm.values[args[2] as int]; + assert(arg_algorithm != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encrypt was null, expected non-null CipherAlgorithm.'); + final Uint8List? output = api.encrypt(arg_plainText!, arg_key!, arg_algorithm!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV was null.'); + final List args = (message as List?)!; + final Uint8List? arg_plainText = (args[0] as Uint8List?); + assert(arg_plainText != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV was null, expected non-null Uint8List.'); + final Uint8List? arg_iv = (args[1] as Uint8List?); + assert(arg_iv != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV was null, expected non-null Uint8List.'); + final Uint8List? arg_key = (args[2] as Uint8List?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV was null, expected non-null Uint8List.'); + final CipherAlgorithm? arg_algorithm = args[3] == null ? null : CipherAlgorithm.values[args[3] as int]; + assert(arg_algorithm != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV was null, expected non-null CipherAlgorithm.'); + final Uint8List? output = api.encryptWithIV(arg_plainText!, arg_iv!, arg_key!, arg_algorithm!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.decrypt', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decrypt was null.'); + final List args = (message as List?)!; + final Uint8List? arg_cipherText = (args[0] as Uint8List?); + assert(arg_cipherText != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decrypt was null, expected non-null Uint8List.'); + final Uint8List? arg_key = (args[1] as Uint8List?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decrypt was null, expected non-null Uint8List.'); + final CipherAlgorithm? arg_algorithm = args[2] == null ? null : CipherAlgorithm.values[args[2] as int]; + assert(arg_algorithm != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decrypt was null, expected non-null CipherAlgorithm.'); + final Uint8List? output = api.decrypt(arg_cipherText!, arg_key!, arg_algorithm!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptFile', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFile was null.'); + final List args = (message as List?)!; + final String? arg_plainTextPath = (args[0] as String?); + assert(arg_plainTextPath != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFile was null, expected non-null String.'); + final String? arg_cipherTextPath = (args[1] as String?); + assert(arg_cipherTextPath != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFile was null, expected non-null String.'); + final Uint8List? arg_key = (args[2] as Uint8List?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFile was null, expected non-null Uint8List.'); + final CipherAlgorithm? arg_algorithm = args[3] == null ? null : CipherAlgorithm.values[args[3] as int]; + assert(arg_algorithm != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFile was null, expected non-null CipherAlgorithm.'); + final bool? output = api.encryptFile(arg_plainTextPath!, arg_cipherTextPath!, arg_key!, arg_algorithm!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV was null.'); + final List args = (message as List?)!; + final String? arg_plainTextPath = (args[0] as String?); + assert(arg_plainTextPath != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV was null, expected non-null String.'); + final String? arg_cipherTextPath = (args[1] as String?); + assert(arg_cipherTextPath != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV was null, expected non-null String.'); + final Uint8List? arg_iv = (args[2] as Uint8List?); + assert(arg_iv != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV was null, expected non-null Uint8List.'); + final Uint8List? arg_key = (args[3] as Uint8List?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV was null, expected non-null Uint8List.'); + final CipherAlgorithm? arg_algorithm = args[4] == null ? null : CipherAlgorithm.values[args[4] as int]; + assert(arg_algorithm != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV was null, expected non-null CipherAlgorithm.'); + final bool? output = api.encryptFileWithIV(arg_plainTextPath!, arg_cipherTextPath!, arg_iv!, arg_key!, arg_algorithm!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.NativeCryptoAPI.decryptFile', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger.setMockDecodedMessageHandler(channel, (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decryptFile was null.'); + final List args = (message as List?)!; + final String? arg_cipherTextPath = (args[0] as String?); + assert(arg_cipherTextPath != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decryptFile was null, expected non-null String.'); + final String? arg_plainTextPath = (args[1] as String?); + assert(arg_plainTextPath != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decryptFile was null, expected non-null String.'); + final Uint8List? arg_key = (args[2] as Uint8List?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decryptFile was null, expected non-null Uint8List.'); + final CipherAlgorithm? arg_algorithm = args[3] == null ? null : CipherAlgorithm.values[args[3] as int]; + assert(arg_algorithm != null, + 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decryptFile was null, expected non-null CipherAlgorithm.'); + final bool? output = api.decryptFile(arg_cipherTextPath!, arg_plainTextPath!, arg_key!, arg_algorithm!); + return [output]; + }); + } + } + } +} diff --git a/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart b/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart index a1df83b..2ecc4ff 100644 --- a/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart +++ b/packages/native_crypto_platform_interface/lib/src/implementations/basic_message_channel_native_crypto.dart @@ -6,6 +6,8 @@ import 'package:flutter/foundation.dart'; import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; +import 'package:native_crypto_platform_interface/src/core/utils/enum_parser.dart'; +import 'package:native_crypto_platform_interface/src/gen/messages.g.dart'; /// An implementation of [NativeCryptoPlatform] that uses Pigeon generated code. class BasicMessageChannelNativeCrypto extends NativeCryptoPlatform { @@ -22,14 +24,10 @@ class BasicMessageChannelNativeCrypto extends NativeCryptoPlatform { @override Future hash(Uint8List data, {required String algorithm}) async { try { - return api - .hash( - HashRequest( - data: data, - algorithm: algorithm, - ), - ) - .then((value) => value.hash); + return api.hash( + data, + EnumParser.hashAlgorithmFromString(algorithm), + ); } catch (e, s) { NativeCryptoException.convertPlatformException(e, s); } @@ -42,15 +40,11 @@ class BasicMessageChannelNativeCrypto extends NativeCryptoPlatform { required String algorithm, }) async { try { - return api - .hmac( - HmacRequest( - data: data, - key: key, - algorithm: algorithm, - ), - ) - .then((value) => value.hmac); + return api.hmac( + data, + key, + EnumParser.hashAlgorithmFromString(algorithm), + ); } catch (e, s) { NativeCryptoException.convertPlatformException(e, s); } @@ -59,13 +53,7 @@ class BasicMessageChannelNativeCrypto extends NativeCryptoPlatform { @override Future generateSecureRandom(int length) async { try { - return api - .generateSecureRandom( - GenerateSecureRandomRequest( - length: length, - ), - ) - .then((value) => value.random); + return api.generateSecureRandom(length); } catch (e, s) { NativeCryptoException.convertPlatformException(e, s); } @@ -80,17 +68,13 @@ class BasicMessageChannelNativeCrypto extends NativeCryptoPlatform { required String hashAlgorithm, }) async { try { - return api - .pbkdf2( - Pbkdf2Request( - password: password, - salt: salt, - length: length, - iterations: iterations, - hashAlgorithm: hashAlgorithm, - ), - ) - .then((value) => value.key); + return api.pbkdf2( + password, + salt, + length, + iterations, + EnumParser.hashAlgorithmFromString(hashAlgorithm), + ); } catch (e, s) { NativeCryptoException.convertPlatformException(e, s); } @@ -103,82 +87,11 @@ class BasicMessageChannelNativeCrypto extends NativeCryptoPlatform { required String algorithm, }) async { try { - return api - .encrypt( - EncryptRequest( - plainText: plainText, - key: key, - algorithm: algorithm, - ), - ) - .then((value) => value.cipherText); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } - - @override - Future decrypt( - Uint8List cipherText, { - required Uint8List key, - required String algorithm, - }) async { - try { - return api - .decrypt( - DecryptRequest( - cipherText: cipherText, - key: key, - algorithm: algorithm, - ), - ) - .then((value) => value.plainText); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } - - @override - Future encryptFile({ - required String plainTextPath, - required String cipherTextPath, - required Uint8List key, - required String algorithm, - }) async { - try { - return api - .encryptFile( - EncryptFileRequest( - plainTextPath: plainTextPath, - cipherTextPath: cipherTextPath, - key: key, - algorithm: algorithm, - ), - ) - .then((value) => value.success); - } catch (e, s) { - NativeCryptoException.convertPlatformException(e, s); - } - } - - @override - Future decryptFile({ - required String cipherTextPath, - required String plainTextPath, - required Uint8List key, - required String algorithm, - }) async { - try { - return api - .decryptFile( - DecryptFileRequest( - cipherTextPath: cipherTextPath, - plainTextPath: plainTextPath, - key: key, - algorithm: algorithm, - ), - ) - .then((value) => value.success); + return api.encrypt( + plainText, + key, + EnumParser.cipherAlgorithmFromString(algorithm), + ); } catch (e, s) { NativeCryptoException.convertPlatformException(e, s); } @@ -192,16 +105,88 @@ class BasicMessageChannelNativeCrypto extends NativeCryptoPlatform { required String algorithm, }) async { try { - return api - .encryptWithIV( - EncryptWithIVRequest( - plainText: plainText, - iv: iv, - key: key, - algorithm: algorithm, - ), - ) - .then((value) => value.cipherText); + return api.encryptWithIV( + plainText, + iv, + key, + EnumParser.cipherAlgorithmFromString(algorithm), + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future decrypt( + Uint8List cipherText, { + required Uint8List key, + required String algorithm, + }) async { + try { + return api.decrypt( + cipherText, + key, + EnumParser.cipherAlgorithmFromString(algorithm), + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future encryptFile({ + required String plainTextPath, + required String cipherTextPath, + required Uint8List key, + required String algorithm, + }) async { + try { + return api.encryptFile( + plainTextPath, + cipherTextPath, + key, + EnumParser.cipherAlgorithmFromString(algorithm), + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future encryptFileWithIV({ + required String plainTextPath, + required String cipherTextPath, + required Uint8List iv, + required Uint8List key, + required String algorithm, + }) async { + try { + return api.encryptFileWithIV( + plainTextPath, + cipherTextPath, + iv, + key, + EnumParser.cipherAlgorithmFromString(algorithm), + ); + } catch (e, s) { + NativeCryptoException.convertPlatformException(e, s); + } + } + + @override + Future decryptFile({ + required String cipherTextPath, + required String plainTextPath, + required Uint8List key, + required String algorithm, + }) async { + try { + return api.decryptFile( + cipherTextPath, + plainTextPath, + key, + EnumParser.cipherAlgorithmFromString(algorithm), + ); } catch (e, s) { NativeCryptoException.convertPlatformException(e, s); } diff --git a/packages/native_crypto_platform_interface/lib/src/interface/native_crypto_platform.dart b/packages/native_crypto_platform_interface/lib/src/interface/native_crypto_platform.dart index 6a953ad..684cf39 100644 --- a/packages/native_crypto_platform_interface/lib/src/interface/native_crypto_platform.dart +++ b/packages/native_crypto_platform_interface/lib/src/interface/native_crypto_platform.dart @@ -7,7 +7,6 @@ import 'dart:typed_data'; import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; -import 'package:native_crypto_platform_interface/src/implementations/method_channel_native_crypto.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; /// The interface that implementations of native_crypto must implement. @@ -24,11 +23,11 @@ abstract class NativeCryptoPlatform extends PlatformInterface { static final Object _token = Object(); - static NativeCryptoPlatform _instance = MethodChannelNativeCrypto(); + static NativeCryptoPlatform _instance = BasicMessageChannelNativeCrypto(); /// The default instance of [NativeCryptoPlatform] to use. /// - /// Defaults to [MethodChannelNativeCrypto]. + /// Defaults to [BasicMessageChannelNativeCrypto]. static NativeCryptoPlatform get instance => _instance; /// Platform-specific plugins should set this with their own platform-specific @@ -77,6 +76,18 @@ abstract class NativeCryptoPlatform extends PlatformInterface { throw UnimplementedError('encrypt is not implemented'); } + /// Encrypts the given data using the given key, algorithm and iv. + /// + /// Users should use [encrypt] instead if they don't need to specify the iv. + Future encryptWithIV({ + required Uint8List plainText, + required Uint8List iv, + required Uint8List key, + required String algorithm, + }) { + throw UnimplementedError('encryptWithIV is not implemented'); + } + /// Decrypts the given data using the given key and algorithm. Future decrypt( Uint8List cipherText, { @@ -96,6 +107,20 @@ abstract class NativeCryptoPlatform extends PlatformInterface { throw UnimplementedError('encryptFile is not implemented'); } + /// Encrypts the given file using the given key, algorithm and iv. + /// + /// Users should use [encryptFile] instead if they don't need to specify + /// the iv. + Future encryptFileWithIV({ + required String plainTextPath, + required String cipherTextPath, + required Uint8List iv, + required Uint8List key, + required String algorithm, + }) { + throw UnimplementedError('encryptFileWithIV is not implemented'); + } + /// Decrypts the given file using the given key and algorithm. Future decryptFile({ required String cipherTextPath, @@ -105,16 +130,4 @@ abstract class NativeCryptoPlatform extends PlatformInterface { }) { throw UnimplementedError('decryptFile is not implemented'); } - - /// Encrypts the given data using the given key, algorithm and iv. - /// - /// Users should use [encrypt] instead if they don't need to specify the iv. - Future encryptWithIV({ - required Uint8List plainText, - required Uint8List iv, - required Uint8List key, - required String algorithm, - }) { - throw UnimplementedError('encryptWithIV is not implemented'); - } } diff --git a/packages/native_crypto_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/native_crypto_platform_interface/lib/src/pigeon/messages.pigeon.dart deleted file mode 100644 index 7f8a4a4..0000000 --- a/packages/native_crypto_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ /dev/null @@ -1,829 +0,0 @@ -// Copyright 2019-2023 Hugo Pointcheval -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. -// -- -// Autogenerated from Pigeon (v9.0.0), do not edit directly. -// See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import - -import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; - -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; -import 'package:flutter/services.dart'; - -class HashRequest { - HashRequest({ - this.data, - this.algorithm, - }); - - Uint8List? data; - - String? algorithm; - - Object encode() { - return [ - data, - algorithm, - ]; - } - - static HashRequest decode(Object result) { - result as List; - return HashRequest( - data: result[0] as Uint8List?, - algorithm: result[1] as String?, - ); - } -} - -class HashResponse { - HashResponse({ - this.hash, - }); - - Uint8List? hash; - - Object encode() { - return [ - hash, - ]; - } - - static HashResponse decode(Object result) { - result as List; - return HashResponse( - hash: result[0] as Uint8List?, - ); - } -} - -class HmacRequest { - HmacRequest({ - this.data, - this.key, - this.algorithm, - }); - - Uint8List? data; - - Uint8List? key; - - String? algorithm; - - Object encode() { - return [ - data, - key, - algorithm, - ]; - } - - static HmacRequest decode(Object result) { - result as List; - return HmacRequest( - data: result[0] as Uint8List?, - key: result[1] as Uint8List?, - algorithm: result[2] as String?, - ); - } -} - -class HmacResponse { - HmacResponse({ - this.hmac, - }); - - Uint8List? hmac; - - Object encode() { - return [ - hmac, - ]; - } - - static HmacResponse decode(Object result) { - result as List; - return HmacResponse( - hmac: result[0] as Uint8List?, - ); - } -} - -class GenerateSecureRandomRequest { - GenerateSecureRandomRequest({ - this.length, - }); - - int? length; - - Object encode() { - return [ - length, - ]; - } - - static GenerateSecureRandomRequest decode(Object result) { - result as List; - return GenerateSecureRandomRequest( - length: result[0] as int?, - ); - } -} - -class GenerateSecureRandomResponse { - GenerateSecureRandomResponse({ - this.random, - }); - - Uint8List? random; - - Object encode() { - return [ - random, - ]; - } - - static GenerateSecureRandomResponse decode(Object result) { - result as List; - return GenerateSecureRandomResponse( - random: result[0] as Uint8List?, - ); - } -} - -class Pbkdf2Request { - Pbkdf2Request({ - this.password, - this.salt, - this.length, - this.iterations, - this.hashAlgorithm, - }); - - Uint8List? password; - - Uint8List? salt; - - int? length; - - int? iterations; - - String? hashAlgorithm; - - Object encode() { - return [ - password, - salt, - length, - iterations, - hashAlgorithm, - ]; - } - - static Pbkdf2Request decode(Object result) { - result as List; - return Pbkdf2Request( - password: result[0] as Uint8List?, - salt: result[1] as Uint8List?, - length: result[2] as int?, - iterations: result[3] as int?, - hashAlgorithm: result[4] as String?, - ); - } -} - -class Pbkdf2Response { - Pbkdf2Response({ - this.key, - }); - - Uint8List? key; - - Object encode() { - return [ - key, - ]; - } - - static Pbkdf2Response decode(Object result) { - result as List; - return Pbkdf2Response( - key: result[0] as Uint8List?, - ); - } -} - -class EncryptRequest { - EncryptRequest({ - this.plainText, - this.key, - this.algorithm, - }); - - Uint8List? plainText; - - Uint8List? key; - - String? algorithm; - - Object encode() { - return [ - plainText, - key, - algorithm, - ]; - } - - static EncryptRequest decode(Object result) { - result as List; - return EncryptRequest( - plainText: result[0] as Uint8List?, - key: result[1] as Uint8List?, - algorithm: result[2] as String?, - ); - } -} - -class EncryptResponse { - EncryptResponse({ - this.cipherText, - }); - - Uint8List? cipherText; - - Object encode() { - return [ - cipherText, - ]; - } - - static EncryptResponse decode(Object result) { - result as List; - return EncryptResponse( - cipherText: result[0] as Uint8List?, - ); - } -} - -class DecryptRequest { - DecryptRequest({ - this.cipherText, - this.key, - this.algorithm, - }); - - Uint8List? cipherText; - - Uint8List? key; - - String? algorithm; - - Object encode() { - return [ - cipherText, - key, - algorithm, - ]; - } - - static DecryptRequest decode(Object result) { - result as List; - return DecryptRequest( - cipherText: result[0] as Uint8List?, - key: result[1] as Uint8List?, - algorithm: result[2] as String?, - ); - } -} - -class DecryptResponse { - DecryptResponse({ - this.plainText, - }); - - Uint8List? plainText; - - Object encode() { - return [ - plainText, - ]; - } - - static DecryptResponse decode(Object result) { - result as List; - return DecryptResponse( - plainText: result[0] as Uint8List?, - ); - } -} - -class EncryptFileRequest { - EncryptFileRequest({ - this.plainTextPath, - this.cipherTextPath, - this.key, - this.algorithm, - }); - - String? plainTextPath; - - String? cipherTextPath; - - Uint8List? key; - - String? algorithm; - - Object encode() { - return [ - plainTextPath, - cipherTextPath, - key, - algorithm, - ]; - } - - static EncryptFileRequest decode(Object result) { - result as List; - return EncryptFileRequest( - plainTextPath: result[0] as String?, - cipherTextPath: result[1] as String?, - key: result[2] as Uint8List?, - algorithm: result[3] as String?, - ); - } -} - -class EncryptFileResponse { - EncryptFileResponse({ - this.success, - }); - - bool? success; - - Object encode() { - return [ - success, - ]; - } - - static EncryptFileResponse decode(Object result) { - result as List; - return EncryptFileResponse( - success: result[0] as bool?, - ); - } -} - -class DecryptFileRequest { - DecryptFileRequest({ - this.cipherTextPath, - this.plainTextPath, - this.key, - this.algorithm, - }); - - String? cipherTextPath; - - String? plainTextPath; - - Uint8List? key; - - String? algorithm; - - Object encode() { - return [ - cipherTextPath, - plainTextPath, - key, - algorithm, - ]; - } - - static DecryptFileRequest decode(Object result) { - result as List; - return DecryptFileRequest( - cipherTextPath: result[0] as String?, - plainTextPath: result[1] as String?, - key: result[2] as Uint8List?, - algorithm: result[3] as String?, - ); - } -} - -class DecryptFileResponse { - DecryptFileResponse({ - this.success, - }); - - bool? success; - - Object encode() { - return [ - success, - ]; - } - - static DecryptFileResponse decode(Object result) { - result as List; - return DecryptFileResponse( - success: result[0] as bool?, - ); - } -} - -class EncryptWithIVRequest { - EncryptWithIVRequest({ - this.plainText, - this.iv, - this.key, - this.algorithm, - }); - - Uint8List? plainText; - - Uint8List? iv; - - Uint8List? key; - - String? algorithm; - - Object encode() { - return [ - plainText, - iv, - key, - algorithm, - ]; - } - - static EncryptWithIVRequest decode(Object result) { - result as List; - return EncryptWithIVRequest( - plainText: result[0] as Uint8List?, - iv: result[1] as Uint8List?, - key: result[2] as Uint8List?, - algorithm: result[3] as String?, - ); - } -} - -class _NativeCryptoAPICodec extends StandardMessageCodec { - const _NativeCryptoAPICodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is DecryptFileRequest) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is DecryptFileResponse) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is DecryptRequest) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is DecryptResponse) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is EncryptFileRequest) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is EncryptFileResponse) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); - } else if (value is EncryptRequest) { - buffer.putUint8(134); - writeValue(buffer, value.encode()); - } else if (value is EncryptResponse) { - buffer.putUint8(135); - writeValue(buffer, value.encode()); - } else if (value is EncryptWithIVRequest) { - buffer.putUint8(136); - writeValue(buffer, value.encode()); - } else if (value is GenerateSecureRandomRequest) { - buffer.putUint8(137); - writeValue(buffer, value.encode()); - } else if (value is GenerateSecureRandomResponse) { - buffer.putUint8(138); - writeValue(buffer, value.encode()); - } else if (value is HashRequest) { - buffer.putUint8(139); - writeValue(buffer, value.encode()); - } else if (value is HashResponse) { - buffer.putUint8(140); - writeValue(buffer, value.encode()); - } else if (value is HmacRequest) { - buffer.putUint8(141); - writeValue(buffer, value.encode()); - } else if (value is HmacResponse) { - buffer.putUint8(142); - writeValue(buffer, value.encode()); - } else if (value is Pbkdf2Request) { - buffer.putUint8(143); - writeValue(buffer, value.encode()); - } else if (value is Pbkdf2Response) { - buffer.putUint8(144); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return DecryptFileRequest.decode(readValue(buffer)!); - case 129: - return DecryptFileResponse.decode(readValue(buffer)!); - case 130: - return DecryptRequest.decode(readValue(buffer)!); - case 131: - return DecryptResponse.decode(readValue(buffer)!); - case 132: - return EncryptFileRequest.decode(readValue(buffer)!); - case 133: - return EncryptFileResponse.decode(readValue(buffer)!); - case 134: - return EncryptRequest.decode(readValue(buffer)!); - case 135: - return EncryptResponse.decode(readValue(buffer)!); - case 136: - return EncryptWithIVRequest.decode(readValue(buffer)!); - case 137: - return GenerateSecureRandomRequest.decode(readValue(buffer)!); - case 138: - return GenerateSecureRandomResponse.decode(readValue(buffer)!); - case 139: - return HashRequest.decode(readValue(buffer)!); - case 140: - return HashResponse.decode(readValue(buffer)!); - case 141: - return HmacRequest.decode(readValue(buffer)!); - case 142: - return HmacResponse.decode(readValue(buffer)!); - case 143: - return Pbkdf2Request.decode(readValue(buffer)!); - case 144: - return Pbkdf2Response.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - -class NativeCryptoAPI { - /// Constructor for [NativeCryptoAPI]. The [binaryMessenger] named argument is - /// available for dependency injection. If it is left null, the default - /// BinaryMessenger will be used which routes to the host platform. - NativeCryptoAPI({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; - - static const MessageCodec codec = _NativeCryptoAPICodec(); - - Future hash(HashRequest arg_request) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.hash', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as HashResponse?)!; - } - } - - Future hmac(HmacRequest arg_request) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.hmac', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as HmacResponse?)!; - } - } - - Future generateSecureRandom(GenerateSecureRandomRequest arg_request) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as GenerateSecureRandomResponse?)!; - } - } - - Future pbkdf2(Pbkdf2Request arg_request) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.pbkdf2', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as Pbkdf2Response?)!; - } - } - - Future encrypt(EncryptRequest arg_request) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.encrypt', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as EncryptResponse?)!; - } - } - - Future decrypt(DecryptRequest arg_request) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.decrypt', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as DecryptResponse?)!; - } - } - - Future encryptFile(EncryptFileRequest arg_request) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.encryptFile', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as EncryptFileResponse?)!; - } - } - - Future decryptFile(DecryptFileRequest arg_request) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.decryptFile', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as DecryptFileResponse?)!; - } - } - - Future encryptWithIV(EncryptWithIVRequest arg_request) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_request]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (replyList[0] as EncryptResponse?)!; - } - } -} diff --git a/packages/native_crypto_platform_interface/lib/src/pigeon/test_api.dart b/packages/native_crypto_platform_interface/lib/src/pigeon/test_api.dart deleted file mode 100644 index 6687736..0000000 --- a/packages/native_crypto_platform_interface/lib/src/pigeon/test_api.dart +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2019-2023 Hugo Pointcheval -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. -// -- -// Autogenerated from Pigeon (v9.0.0), do not edit directly. -// See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import -// ignore_for_file: avoid_relative_lib_imports -import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'messages.pigeon.dart'; - -class _TestNativeCryptoAPICodec extends StandardMessageCodec { - const _TestNativeCryptoAPICodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is DecryptFileRequest) { - buffer.putUint8(128); - writeValue(buffer, value.encode()); - } else if (value is DecryptFileResponse) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is DecryptRequest) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is DecryptResponse) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is EncryptFileRequest) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else if (value is EncryptFileResponse) { - buffer.putUint8(133); - writeValue(buffer, value.encode()); - } else if (value is EncryptRequest) { - buffer.putUint8(134); - writeValue(buffer, value.encode()); - } else if (value is EncryptResponse) { - buffer.putUint8(135); - writeValue(buffer, value.encode()); - } else if (value is EncryptWithIVRequest) { - buffer.putUint8(136); - writeValue(buffer, value.encode()); - } else if (value is GenerateSecureRandomRequest) { - buffer.putUint8(137); - writeValue(buffer, value.encode()); - } else if (value is GenerateSecureRandomResponse) { - buffer.putUint8(138); - writeValue(buffer, value.encode()); - } else if (value is HashRequest) { - buffer.putUint8(139); - writeValue(buffer, value.encode()); - } else if (value is HashResponse) { - buffer.putUint8(140); - writeValue(buffer, value.encode()); - } else if (value is HmacRequest) { - buffer.putUint8(141); - writeValue(buffer, value.encode()); - } else if (value is HmacResponse) { - buffer.putUint8(142); - writeValue(buffer, value.encode()); - } else if (value is Pbkdf2Request) { - buffer.putUint8(143); - writeValue(buffer, value.encode()); - } else if (value is Pbkdf2Response) { - buffer.putUint8(144); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 128: - return DecryptFileRequest.decode(readValue(buffer)!); - case 129: - return DecryptFileResponse.decode(readValue(buffer)!); - case 130: - return DecryptRequest.decode(readValue(buffer)!); - case 131: - return DecryptResponse.decode(readValue(buffer)!); - case 132: - return EncryptFileRequest.decode(readValue(buffer)!); - case 133: - return EncryptFileResponse.decode(readValue(buffer)!); - case 134: - return EncryptRequest.decode(readValue(buffer)!); - case 135: - return EncryptResponse.decode(readValue(buffer)!); - case 136: - return EncryptWithIVRequest.decode(readValue(buffer)!); - case 137: - return GenerateSecureRandomRequest.decode(readValue(buffer)!); - case 138: - return GenerateSecureRandomResponse.decode(readValue(buffer)!); - case 139: - return HashRequest.decode(readValue(buffer)!); - case 140: - return HashResponse.decode(readValue(buffer)!); - case 141: - return HmacRequest.decode(readValue(buffer)!); - case 142: - return HmacResponse.decode(readValue(buffer)!); - case 143: - return Pbkdf2Request.decode(readValue(buffer)!); - case 144: - return Pbkdf2Response.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - -abstract class TestNativeCryptoAPI { - static const MessageCodec codec = _TestNativeCryptoAPICodec(); - - HashResponse hash(HashRequest request); - - HmacResponse hmac(HmacRequest request); - - GenerateSecureRandomResponse generateSecureRandom(GenerateSecureRandomRequest request); - - Pbkdf2Response pbkdf2(Pbkdf2Request request); - - EncryptResponse encrypt(EncryptRequest request); - - DecryptResponse decrypt(DecryptRequest request); - - EncryptFileResponse encryptFile(EncryptFileRequest request); - - DecryptFileResponse decryptFile(DecryptFileRequest request); - - EncryptResponse encryptWithIV(EncryptWithIVRequest request); - - static void setup(TestNativeCryptoAPI? api, {BinaryMessenger? binaryMessenger}) { - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.hash', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hash was null.'); - final List args = (message as List?)!; - final HashRequest? arg_request = (args[0] as HashRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hash was null, expected non-null HashRequest.'); - final HashResponse output = api.hash(arg_request!); - return [output]; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.hmac', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hmac was null.'); - final List args = (message as List?)!; - final HmacRequest? arg_request = (args[0] as HmacRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.hmac was null, expected non-null HmacRequest.'); - final HmacResponse output = api.hmac(arg_request!); - return [output]; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom was null.'); - final List args = (message as List?)!; - final GenerateSecureRandomRequest? arg_request = (args[0] as GenerateSecureRandomRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom was null, expected non-null GenerateSecureRandomRequest.'); - final GenerateSecureRandomResponse output = api.generateSecureRandom(arg_request!); - return [output]; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.pbkdf2', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null.'); - final List args = (message as List?)!; - final Pbkdf2Request? arg_request = (args[0] as Pbkdf2Request?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.pbkdf2 was null, expected non-null Pbkdf2Request.'); - final Pbkdf2Response output = api.pbkdf2(arg_request!); - return [output]; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.encrypt', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encrypt was null.'); - final List args = (message as List?)!; - final EncryptRequest? arg_request = (args[0] as EncryptRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encrypt was null, expected non-null EncryptRequest.'); - final EncryptResponse output = api.encrypt(arg_request!); - return [output]; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.decrypt', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decrypt was null.'); - final List args = (message as List?)!; - final DecryptRequest? arg_request = (args[0] as DecryptRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decrypt was null, expected non-null DecryptRequest.'); - final DecryptResponse output = api.decrypt(arg_request!); - return [output]; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.encryptFile', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFile was null.'); - final List args = (message as List?)!; - final EncryptFileRequest? arg_request = (args[0] as EncryptFileRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptFile was null, expected non-null EncryptFileRequest.'); - final EncryptFileResponse output = api.encryptFile(arg_request!); - return [output]; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.decryptFile', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decryptFile was null.'); - final List args = (message as List?)!; - final DecryptFileRequest? arg_request = (args[0] as DecryptFileRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.decryptFile was null, expected non-null DecryptFileRequest.'); - final DecryptFileResponse output = api.decryptFile(arg_request!); - return [output]; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV was null.'); - final List args = (message as List?)!; - final EncryptWithIVRequest? arg_request = (args[0] as EncryptWithIVRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV was null, expected non-null EncryptWithIVRequest.'); - final EncryptResponse output = api.encryptWithIV(arg_request!); - return [output]; - }); - } - } - } -} diff --git a/packages/native_crypto_platform_interface/pigeons/messages.dart b/packages/native_crypto_platform_interface/pigeons/messages.dart index e2802cd..3f00fe2 100644 --- a/packages/native_crypto_platform_interface/pigeons/messages.dart +++ b/packages/native_crypto_platform_interface/pigeons/messages.dart @@ -1,4 +1,3 @@ -// ignore_for_file: public_member_api_docs, sort_constructors_first // Copyright 2019-2023 Hugo Pointcheval // // Use of this source code is governed by an MIT-style @@ -10,213 +9,80 @@ import 'package:pigeon/pigeon.dart'; @ConfigurePigeon( PigeonOptions( copyrightHeader: 'pigeons/copyright_header.txt', - dartOut: 'lib/src/pigeon/messages.pigeon.dart', + dartOut: 'lib/src/gen/messages.g.dart', // We export in the lib folder to expose the class to other packages. - dartTestOut: 'lib/src/pigeon/test_api.dart', - javaOut: - '../native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java', - javaOptions: JavaOptions( + dartTestOut: 'lib/src/gen/test.g.dart', + kotlinOut: + '../native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Pigeon.kt', + kotlinOptions: KotlinOptions( package: 'fr.pointcheval.native_crypto_android', - className: 'GeneratedAndroidNativeCrypto', ), - objcHeaderOut: '../native_crypto_ios/ios/Classes/Public/messages.g.h', - objcSourceOut: '../native_crypto_ios/ios/Classes/messages.g.m', + swiftOut: '../native_crypto_ios/ios/Classes/messages.g.swift', ), ) -class HashRequest { - const HashRequest({ - this.data, - this.algorithm, - }); - - final Uint8List? data; - final String? algorithm; +enum HashAlgorithm { + sha256, + sha384, + sha512; } -class HashResponse { - const HashResponse({ - this.hash, - }); - - final Uint8List? hash; -} - -class HmacRequest { - const HmacRequest({ - this.data, - this.key, - this.algorithm, - }); - - final Uint8List? data; - final Uint8List? key; - final String? algorithm; -} - -class HmacResponse { - const HmacResponse({ - this.hmac, - }); - - final Uint8List? hmac; -} - -class GenerateSecureRandomRequest { - const GenerateSecureRandomRequest({ - this.length, - }); - - final int? length; -} - -class GenerateSecureRandomResponse { - const GenerateSecureRandomResponse({ - this.random, - }); - - final Uint8List? random; -} - -class Pbkdf2Request { - const Pbkdf2Request({ - this.password, - this.salt, - this.length, - this.iterations, - this.hashAlgorithm, - }); - - final Uint8List? password; - final Uint8List? salt; - final int? length; - final int? iterations; - final String? hashAlgorithm; -} - -class Pbkdf2Response { - const Pbkdf2Response({ - this.key, - }); - - final Uint8List? key; -} - -class EncryptRequest { - const EncryptRequest({ - this.plainText, - this.key, - this.algorithm, - }); - - final Uint8List? plainText; - final Uint8List? key; - final String? algorithm; -} - -class EncryptResponse { - const EncryptResponse({ - this.cipherText, - }); - - final Uint8List? cipherText; -} - -class DecryptRequest { - const DecryptRequest({ - this.cipherText, - this.key, - this.algorithm, - }); - - final Uint8List? cipherText; - final Uint8List? key; - final String? algorithm; -} - -class DecryptResponse { - const DecryptResponse({ - this.plainText, - }); - - final Uint8List? plainText; -} - -class EncryptFileRequest { - const EncryptFileRequest({ - this.plainTextPath, - this.cipherTextPath, - this.key, - this.algorithm, - }); - - final String? plainTextPath; - final String? cipherTextPath; - final Uint8List? key; - final String? algorithm; -} - -class EncryptFileResponse { - const EncryptFileResponse({ - this.success, - }); - - final bool? success; -} - -class DecryptFileRequest { - const DecryptFileRequest({ - this.cipherTextPath, - this.plainTextPath, - this.key, - this.algorithm, - }); - - final String? cipherTextPath; - final String? plainTextPath; - final Uint8List? key; - final String? algorithm; -} - -class DecryptFileResponse { - const DecryptFileResponse({ - this.success, - }); - - final bool? success; -} - -class EncryptWithIVRequest { - const EncryptWithIVRequest({ - this.plainText, - this.iv, - this.key, - this.algorithm, - }); - - final Uint8List? plainText; - final Uint8List? iv; - final Uint8List? key; - final String? algorithm; +enum CipherAlgorithm { + aes; } @HostApi(dartHostTestHandler: 'TestNativeCryptoAPI') abstract class NativeCryptoAPI { - HashResponse hash(HashRequest request); - HmacResponse hmac(HmacRequest request); + Uint8List? hash(Uint8List data, HashAlgorithm algorithm); + Uint8List? hmac(Uint8List data, Uint8List key, HashAlgorithm algorithm); + Uint8List? generateSecureRandom(int length); - GenerateSecureRandomResponse generateSecureRandom( - GenerateSecureRandomRequest request, + Uint8List? pbkdf2( + Uint8List password, + Uint8List salt, + int length, + int iterations, + HashAlgorithm algorithm, ); - Pbkdf2Response pbkdf2(Pbkdf2Request request); + Uint8List? encrypt( + Uint8List plainText, + Uint8List key, + CipherAlgorithm algorithm, + ); - EncryptResponse encrypt(EncryptRequest request); + Uint8List? encryptWithIV( + Uint8List plainText, + Uint8List iv, + Uint8List key, + CipherAlgorithm algorithm, + ); - DecryptResponse decrypt(DecryptRequest request); + Uint8List? decrypt( + Uint8List cipherText, + Uint8List key, + CipherAlgorithm algorithm, + ); - EncryptFileResponse encryptFile(EncryptFileRequest request); + bool? encryptFile( + String plainTextPath, + String cipherTextPath, + Uint8List key, + CipherAlgorithm algorithm, + ); - DecryptFileResponse decryptFile(DecryptFileRequest request); + bool? encryptFileWithIV( + String plainTextPath, + String cipherTextPath, + Uint8List iv, + Uint8List key, + CipherAlgorithm algorithm, + ); + + bool? decryptFile( + String cipherTextPath, + String plainTextPath, + Uint8List key, + CipherAlgorithm algorithm, + ); - EncryptResponse encryptWithIV(EncryptWithIVRequest request); } diff --git a/packages/native_crypto_platform_interface/pubspec.yaml b/packages/native_crypto_platform_interface/pubspec.yaml index c3f9532..320e4ba 100644 --- a/packages/native_crypto_platform_interface/pubspec.yaml +++ b/packages/native_crypto_platform_interface/pubspec.yaml @@ -10,16 +10,16 @@ dependencies: equatable: ^2.0.5 flutter: { sdk: flutter } - plugin_platform_interface: ^2.1.3 + plugin_platform_interface: ^2.1.4 dev_dependencies: flutter_test: { sdk: flutter } - mockito: ^5.3.2 - pigeon: ^9.0.0 + mockito: ^5.4.0 + pigeon: ^9.2.0 wyatt_analysis: hosted: url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ name: wyatt_analysis - version: 2.4.0 + version: 2.4.1 diff --git a/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart b/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart index 8c1c890..8d06d88 100644 --- a/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart +++ b/packages/native_crypto_platform_interface/test/platform_interface/native_crypto_platform_test.dart @@ -4,18 +4,18 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. - import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:native_crypto_platform_interface/src/implementations/basic_message_channel_native_crypto.dart'; -import 'package:native_crypto_platform_interface/src/implementations/method_channel_native_crypto.dart'; import 'package:native_crypto_platform_interface/src/interface/native_crypto_platform.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class ImplementsNativeCryptoPlatform // ignore: prefer_mixin - with Mock - implements NativeCryptoPlatform {} + with + Mock + implements + NativeCryptoPlatform {} class ExtendsNativeCryptoPlatform extends NativeCryptoPlatform {} @@ -31,10 +31,6 @@ void main() { group('$NativeCryptoPlatform', () { // should allow read of default app from native - test('$MethodChannelNativeCrypto is the default instance', () { - expect(NativeCryptoPlatform.instance, isA()); - }); - test('Can be extended', () { NativeCryptoPlatform.instance = ExtendsNativeCryptoPlatform(); }); -- 2.47.2 From 560f5b4942bb31e93540a93af9c63ef538b01246 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Tue, 4 Apr 2023 22:36:50 +0200 Subject: [PATCH 10/21] feat(android): use kotlin pigeon generator --- .../android/build.gradle | 1 + .../GeneratedAndroidNativeCrypto.java | 136 ++++---- .../native_crypto_android/NativeCrypto.kt | 118 +++++++ .../NativeCryptoAndroidPlugin.kt | 159 +--------- .../native_crypto_android/Pigeon.kt | 300 ++++++++++++++++++ .../native_crypto_android/ciphers/AES.kt | 143 +++++++-- .../interfaces/Cipher.kt | 10 +- .../native_crypto_android/interfaces/Key.kt | 5 - .../interfaces/KeyDerivation.kt | 7 +- .../native_crypto_android/kdf/Pbkdf2.kt | 40 +-- .../native_crypto_android/keys/SecretKey.kt | 14 - .../utils/CipherAlgorithm.kt | 14 - .../native_crypto_android/utils/Constants.kt | 14 - .../utils/FileParameters.kt | 71 +++++ .../utils/HashAlgorithm.kt | 50 --- .../utils/HashAlgorithmParser.kt | 31 ++ .../utils/KdfAlgorithm.kt | 5 - .../native_crypto_android/utils/Task.kt | 44 --- 18 files changed, 741 insertions(+), 421 deletions(-) create mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt create mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Pigeon.kt delete mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt delete mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt delete mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt delete mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt create mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt delete mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt create mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithmParser.kt delete mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt delete mode 100644 packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt diff --git a/packages/native_crypto_android/android/build.gradle b/packages/native_crypto_android/android/build.gradle index 301735f..a30b841 100644 --- a/packages/native_crypto_android/android/build.gradle +++ b/packages/native_crypto_android/android/build.gradle @@ -47,4 +47,5 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'androidx.documentfile:documentfile:1.0.1' } diff --git a/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java b/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java index d48520e..8b10a22 100644 --- a/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java +++ b/packages/native_crypto_android/android/src/main/java/fr/pointcheval/native_crypto_android/GeneratedAndroidNativeCrypto.java @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. // -- -// Autogenerated from Pigeon (v9.0.0), do not edit directly. +// Autogenerated from Pigeon (v9.2.0), do not edit directly. // See also: https://pub.dev/packages/pigeon package fr.pointcheval.native_crypto_android; @@ -26,15 +26,40 @@ import java.util.List; import java.util.Map; /** Generated class from Pigeon. */ -@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"}) +@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) public class GeneratedAndroidNativeCrypto { + + /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ + public static class FlutterError extends RuntimeException { + + /** The error code. */ + public final String code; + + /** The error details. Must be a datatype supported by the api codec. */ + public final Object details; + + public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) + { + super(message); + this.code = code; + this.details = details; + } + } + @NonNull private static ArrayList wrapError(@NonNull Throwable exception) { ArrayList errorList = new ArrayList(3); - errorList.add(exception.toString()); - errorList.add(exception.getClass().getSimpleName()); - errorList.add( - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + if (exception instanceof FlutterError) { + FlutterError error = (FlutterError) exception; + errorList.add(error.code); + errorList.add(error.getMessage()); + errorList.add(error.details); + } else { + errorList.add(exception.toString()); + errorList.add(exception.getClass().getSimpleName()); + errorList.add( + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + } return errorList; } @@ -1339,16 +1364,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + HashRequest requestArg = (HashRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - HashRequest requestArg = (HashRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } HashResponse output = api.hash(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1366,16 +1388,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + HmacRequest requestArg = (HmacRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - HmacRequest requestArg = (HmacRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } HmacResponse output = api.hmac(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1393,16 +1412,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + GenerateSecureRandomRequest requestArg = (GenerateSecureRandomRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - GenerateSecureRandomRequest requestArg = (GenerateSecureRandomRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } GenerateSecureRandomResponse output = api.generateSecureRandom(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1420,16 +1436,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + Pbkdf2Request requestArg = (Pbkdf2Request) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - Pbkdf2Request requestArg = (Pbkdf2Request) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } Pbkdf2Response output = api.pbkdf2(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1447,16 +1460,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + EncryptRequest requestArg = (EncryptRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - EncryptRequest requestArg = (EncryptRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } EncryptResponse output = api.encrypt(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1474,16 +1484,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + DecryptRequest requestArg = (DecryptRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - DecryptRequest requestArg = (DecryptRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } DecryptResponse output = api.decrypt(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1501,16 +1508,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + EncryptFileRequest requestArg = (EncryptFileRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - EncryptFileRequest requestArg = (EncryptFileRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } EncryptFileResponse output = api.encryptFile(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1528,16 +1532,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + DecryptFileRequest requestArg = (DecryptFileRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - DecryptFileRequest requestArg = (DecryptFileRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } DecryptFileResponse output = api.decryptFile(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } @@ -1555,16 +1556,13 @@ public class GeneratedAndroidNativeCrypto { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList(); + ArrayList args = (ArrayList) message; + EncryptWithIVRequest requestArg = (EncryptWithIVRequest) args.get(0); try { - ArrayList args = (ArrayList) message; - assert args != null; - EncryptWithIVRequest requestArg = (EncryptWithIVRequest) args.get(0); - if (requestArg == null) { - throw new NullPointerException("requestArg unexpectedly null."); - } EncryptResponse output = api.encryptWithIV(requestArg); wrapped.add(0, output); - } catch (Error | RuntimeException exception) { + } + catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; } diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt new file mode 100644 index 0000000..3d1e12f --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt @@ -0,0 +1,118 @@ +package fr.pointcheval.native_crypto_android + +import android.content.Context +import fr.pointcheval.native_crypto_android.ciphers.AES +import fr.pointcheval.native_crypto_android.kdf.Pbkdf2 +import fr.pointcheval.native_crypto_android.utils.FileParameters +import fr.pointcheval.native_crypto_android.utils.HashAlgorithmParser +import java.security.SecureRandom + +class NativeCrypto(private val context: Context) : NativeCryptoAPI { + override fun hash(data: ByteArray, algorithm: HashAlgorithm): ByteArray? { + val md = HashAlgorithmParser.getMessageDigest(algorithm) + + return md.digest(data) + } + + override fun hmac(data: ByteArray, key: ByteArray, algorithm: HashAlgorithm): ByteArray? { + val mac = HashAlgorithmParser.getMac(algorithm) + val secretKey = javax.crypto.spec.SecretKeySpec(key, mac.algorithm) + mac.init(secretKey) + + return mac.doFinal(data) + } + + override fun generateSecureRandom(length: Long): ByteArray { + val bytes = ByteArray(length.toInt()) + SecureRandom.getInstanceStrong().nextBytes(bytes) + + return bytes + } + + override fun pbkdf2( + password: ByteArray, + salt: ByteArray, + length: Long, + iterations: Long, + algorithm: HashAlgorithm + ): ByteArray? { + val pbkdf2 = Pbkdf2(length.toInt(), iterations.toInt(), algorithm) + pbkdf2.init(password, salt) + + return pbkdf2.derive() + } + + override fun encrypt( + plainText: ByteArray, + key: ByteArray, + algorithm: CipherAlgorithm + ): ByteArray { + // For now, only AES is supported + val aes = AES() + + return aes.encrypt(plainText, key, null) + } + + override fun encryptWithIV( + plainText: ByteArray, + iv: ByteArray, + key: ByteArray, + algorithm: CipherAlgorithm + ): ByteArray { + // For now, only AES is supported + val aes = AES() + + return aes.encrypt(plainText, key, iv) + } + + override fun decrypt( + cipherText: ByteArray, + key: ByteArray, + algorithm: CipherAlgorithm + ): ByteArray { + // For now, only AES is supported + val aes = AES() + + return aes.decrypt(cipherText, key) + } + + override fun encryptFile( + plainTextPath: String, + cipherTextPath: String, + key: ByteArray, + algorithm: CipherAlgorithm + ): Boolean { + // For now, only AES is supported + val aes = AES() + val params = FileParameters(context, plainTextPath, cipherTextPath) + + return aes.encryptFile(params, key, null) + } + + override fun encryptFileWithIV( + plainTextPath: String, + cipherTextPath: String, + iv: ByteArray, + key: ByteArray, + algorithm: CipherAlgorithm + ): Boolean { + // For now, only AES is supported + val aes = AES() + val params = FileParameters(context, plainTextPath, cipherTextPath) + + return aes.encryptFile(params, key, iv) + } + + override fun decryptFile( + cipherTextPath: String, + plainTextPath: String, + key: ByteArray, + algorithm: CipherAlgorithm + ): Boolean { + // For now, only AES is supported + val aes = AES() + val params = FileParameters(context, plainTextPath, cipherTextPath) + + return aes.decryptFile(params, key) + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt index 36cd412..4c8b235 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCryptoAndroidPlugin.kt @@ -1,160 +1,19 @@ package fr.pointcheval.native_crypto_android -import androidx.annotation.NonNull -import fr.pointcheval.native_crypto_android.interfaces.Cipher -import fr.pointcheval.native_crypto_android.kdf.Pbkdf2 -import fr.pointcheval.native_crypto_android.keys.SecretKey -import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm -import fr.pointcheval.native_crypto_android.utils.Constants -import fr.pointcheval.native_crypto_android.utils.HashAlgorithm -import fr.pointcheval.native_crypto_android.utils.Task import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result -import java.util.* /** NativeCryptoAndroidPlugin */ -class NativeCryptoAndroidPlugin : FlutterPlugin, MethodCallHandler { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private lateinit var channel: MethodChannel - private val name = "plugins.hugop.cl/native_crypto" +class NativeCryptoAndroidPlugin : FlutterPlugin { + private var nativeCrypto: NativeCrypto? = null - private var cipherInstance: Cipher? = null - - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.binaryMessenger, name) - channel.setMethodCallHandler(this) + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + val context = flutterPluginBinding.applicationContext + nativeCrypto = NativeCrypto(context) + NativeCryptoAPI.setUp(flutterPluginBinding.binaryMessenger, nativeCrypto) } - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - } - - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - lateinit var methodCallTask: Task<*> - - when (call.method) { - "digest" -> methodCallTask = handleDigest(call.arguments()) - "generateSecretKey" -> methodCallTask = handleGenerateSecretKey(call.arguments()) - "pbkdf2" -> methodCallTask = handlePbkdf2(call.arguments()) - "encryptAsList" -> methodCallTask = handleEncryptAsList(call.arguments()) - "decryptAsList" -> methodCallTask = handleDecryptAsList(call.arguments()) - "encrypt" -> methodCallTask = handleCrypt(call.arguments(), true) - "decrypt" -> methodCallTask = handleCrypt(call.arguments(), false) - else -> result.notImplemented() - } - - methodCallTask.call() - - methodCallTask.finalize { task -> - if (task.isSuccessful()) { - result.success(task.getResult()) - } else { - val exception: Exception = task.getException() - val message = exception.message - result.error("native_crypto", message, null) - } - } - } - - private fun handleDigest(arguments: Map?): Task { - return Task { - val data: ByteArray = - Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - HashAlgorithm.digest(data, algorithm) - } - } - - private fun handleGenerateSecretKey(arguments: Map?): Task { - return Task { - val bitsCount: Int = Objects.requireNonNull(arguments?.get(Constants.BITS_COUNT)) as Int - SecretKey.fromSecureRandom(bitsCount).bytes - } - } - - private fun handlePbkdf2(arguments: Map?): Task { - return Task { - val password: String = - Objects.requireNonNull(arguments?.get(Constants.PASSWORD)) as String - val salt: String = Objects.requireNonNull(arguments?.get(Constants.SALT)) as String - val keyBytesCount: Int = - Objects.requireNonNull(arguments?.get(Constants.KEY_BYTES_COUNT)) as Int - val iterations: Int = - Objects.requireNonNull(arguments?.get(Constants.ITERATIONS)) as Int - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - - val pbkdf2: Pbkdf2 = Pbkdf2(keyBytesCount, iterations, HashAlgorithm.valueOf(algorithm)) - pbkdf2.init(password, salt) - - pbkdf2.derive().bytes - } - } - - private fun lazyLoadCipher(cipherAlgorithm: CipherAlgorithm) { - if (cipherInstance == null) { - cipherInstance = cipherAlgorithm.getCipher() - } else { - if (cipherInstance!!.algorithm != cipherAlgorithm) { - cipherInstance = cipherAlgorithm.getCipher() - } - } - } - - private fun handleEncryptAsList(arguments: Map?): Task> { - return Task { - val data: ByteArray = - Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray - val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - - val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) - lazyLoadCipher(cipherAlgorithm) - - cipherInstance!!.encryptAsList(data, key) - } - } - - private fun handleDecryptAsList(arguments: Map?): Task { - return Task { - val data: List = - Objects.requireNonNull(arguments?.get(Constants.DATA)) as List - val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - - val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) - lazyLoadCipher(cipherAlgorithm) - - cipherInstance!!.decryptAsList(data, key) - } - } - - // **EN**Crypt and **DE**Crypt - private fun handleCrypt(arguments: Map?, forEncryption: Boolean): Task { - return Task { - val data: ByteArray = - Objects.requireNonNull(arguments?.get(Constants.DATA)) as ByteArray - val key: ByteArray = Objects.requireNonNull(arguments?.get(Constants.KEY)) as ByteArray - val algorithm: String = - Objects.requireNonNull(arguments?.get(Constants.ALGORITHM)) as String - - val cipherAlgorithm: CipherAlgorithm = CipherAlgorithm.valueOf(algorithm) - lazyLoadCipher(cipherAlgorithm) - - if (forEncryption) { - cipherInstance!!.encrypt(data, key) - } else { - cipherInstance!!.decrypt(data, key) - } - } + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + NativeCryptoAPI.setUp(binding.binaryMessenger, null) + nativeCrypto = null } } diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Pigeon.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Pigeon.kt new file mode 100644 index 0000000..0383075 --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/Pigeon.kt @@ -0,0 +1,300 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.2.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package fr.pointcheval.native_crypto_android + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +private fun wrapResult(result: Any?): List { + return listOf(result) +} + +private fun wrapError(exception: Throwable): List { + if (exception is FlutterError) { + return listOf( + exception.code, + exception.message, + exception.details + ) + } else { + return listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +enum class HashAlgorithm(val raw: Int) { + SHA256(0), + SHA384(1), + SHA512(2); + + companion object { + fun ofRaw(raw: Int): HashAlgorithm? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class CipherAlgorithm(val raw: Int) { + AES(0); + + companion object { + fun ofRaw(raw: Int): CipherAlgorithm? { + return values().firstOrNull { it.raw == raw } + } + } +} +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface NativeCryptoAPI { + fun hash(data: ByteArray, algorithm: HashAlgorithm): ByteArray? + fun hmac(data: ByteArray, key: ByteArray, algorithm: HashAlgorithm): ByteArray? + fun generateSecureRandom(length: Long): ByteArray? + fun pbkdf2(password: ByteArray, salt: ByteArray, length: Long, iterations: Long, algorithm: HashAlgorithm): ByteArray? + fun encrypt(plainText: ByteArray, key: ByteArray, algorithm: CipherAlgorithm): ByteArray? + fun encryptWithIV(plainText: ByteArray, iv: ByteArray, key: ByteArray, algorithm: CipherAlgorithm): ByteArray? + fun decrypt(cipherText: ByteArray, key: ByteArray, algorithm: CipherAlgorithm): ByteArray? + fun encryptFile(plainTextPath: String, cipherTextPath: String, key: ByteArray, algorithm: CipherAlgorithm): Boolean? + fun encryptFileWithIV(plainTextPath: String, cipherTextPath: String, iv: ByteArray, key: ByteArray, algorithm: CipherAlgorithm): Boolean? + fun decryptFile(cipherTextPath: String, plainTextPath: String, key: ByteArray, algorithm: CipherAlgorithm): Boolean? + + companion object { + /** The codec used by NativeCryptoAPI. */ + val codec: MessageCodec by lazy { + StandardMessageCodec() + } + /** Sets up an instance of `NativeCryptoAPI` to handle messages through the `binaryMessenger`. */ + @Suppress("UNCHECKED_CAST") + fun setUp(binaryMessenger: BinaryMessenger, api: NativeCryptoAPI?) { + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.hash", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val dataArg = args[0] as ByteArray + val algorithmArg = HashAlgorithm.ofRaw(args[1] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.hash(dataArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.hmac", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val dataArg = args[0] as ByteArray + val keyArg = args[1] as ByteArray + val algorithmArg = HashAlgorithm.ofRaw(args[2] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.hmac(dataArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val lengthArg = args[0].let { if (it is Int) it.toLong() else it as Long } + var wrapped: List + try { + wrapped = listOf(api.generateSecureRandom(lengthArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.pbkdf2", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val passwordArg = args[0] as ByteArray + val saltArg = args[1] as ByteArray + val lengthArg = args[2].let { if (it is Int) it.toLong() else it as Long } + val iterationsArg = args[3].let { if (it is Int) it.toLong() else it as Long } + val algorithmArg = HashAlgorithm.ofRaw(args[4] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.pbkdf2(passwordArg, saltArg, lengthArg, iterationsArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encrypt", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val plainTextArg = args[0] as ByteArray + val keyArg = args[1] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[2] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.encrypt(plainTextArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val plainTextArg = args[0] as ByteArray + val ivArg = args[1] as ByteArray + val keyArg = args[2] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[3] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.encryptWithIV(plainTextArg, ivArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.decrypt", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val cipherTextArg = args[0] as ByteArray + val keyArg = args[1] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[2] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.decrypt(cipherTextArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptFile", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val plainTextPathArg = args[0] as String + val cipherTextPathArg = args[1] as String + val keyArg = args[2] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[3] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.encryptFile(plainTextPathArg, cipherTextPathArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val plainTextPathArg = args[0] as String + val cipherTextPathArg = args[1] as String + val ivArg = args[2] as ByteArray + val keyArg = args[3] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[4] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.encryptFileWithIV(plainTextPathArg, cipherTextPathArg, ivArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.NativeCryptoAPI.decryptFile", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val cipherTextPathArg = args[0] as String + val plainTextPathArg = args[1] as String + val keyArg = args[2] as ByteArray + val algorithmArg = CipherAlgorithm.ofRaw(args[3] as Int)!! + var wrapped: List + try { + wrapped = listOf(api.decryptFile(cipherTextPathArg, plainTextPathArg, keyArg, algorithmArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt index 0b4c86c..94e0ce8 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt @@ -1,55 +1,146 @@ package fr.pointcheval.native_crypto_android.ciphers import fr.pointcheval.native_crypto_android.interfaces.Cipher -import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm -import javax.crypto.SecretKey +import fr.pointcheval.native_crypto_android.utils.FileParameters +import javax.crypto.CipherOutputStream import javax.crypto.spec.GCMParameterSpec -import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec class AES : Cipher { - override val algorithm: CipherAlgorithm - get() = CipherAlgorithm.aes + private var cipherInstance: javax.crypto.Cipher? = null - var cipherInstance: javax.crypto.Cipher? = null; - - fun lazyLoadCipher() { + private fun lazyLoadCipher() { if (cipherInstance == null) { cipherInstance = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding") } } - // native.crypto cipherText representation = [NONCE(12) || CIPHERTEXT(n-28) || TAG(16)] - // javax.crypto cipherText representation = [NONCE(12)] + [CIPHERTEXT(n-16) || TAG(16)] - override fun encrypt(data: ByteArray, key: ByteArray): ByteArray { - val list : List = encryptAsList(data, key) - return list.first().plus(list.last()) - } - // native.crypto cipherText representation = [NONCE(12)] + [CIPHERTEXT(n-16) || TAG(16)] - // javax.crypto cipherText representation = [NONCE(12)] + [CIPHERTEXT(n-16) || TAG(16)] - override fun encryptAsList(data: ByteArray, key: ByteArray): List { + // native.crypto cipherText representation = [NONCE(12) || CIPHERTEXT(n-28) || TAG(16)] + override fun encrypt(data: ByteArray, key: ByteArray, predefinedIV: ByteArray?): ByteArray { + // Initialize secret key spec val sk = SecretKeySpec(key, "AES") + + // Initialize cipher (if not already done) lazyLoadCipher() - cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) + + // If predefinedIV is not null, use it + if (predefinedIV != null && predefinedIV.isNotEmpty()) { + // Here we use the predefinedIV as the nonce (12 bytes) + // And we set the tag length to 16 bytes (128 bits) + val gcmParameterSpec = GCMParameterSpec(16*8, predefinedIV) + cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk, gcmParameterSpec) + } else { + // If predefinedIV is null, we generate a new one + cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) + } + + // Encrypt data val bytes: ByteArray = cipherInstance!!.doFinal(data) val iv: ByteArray = cipherInstance!!.iv - return listOf(iv, bytes) + + return iv.plus(bytes) + } + + override fun encryptFile(fileParameters: FileParameters, key: ByteArray, predefinedIV: ByteArray?): Boolean { + // Initialize secret key spec + val sk = SecretKeySpec(key, "AES") + + // Initialize cipher (if not already done) + lazyLoadCipher() + + // If predefinedIV is not null, use it + if (predefinedIV != null && predefinedIV.isNotEmpty()) { + // Here we use the predefinedIV as the nonce (12 bytes) + // And we set the tag length to 16 bytes (128 bits) + val gcmParameterSpec = GCMParameterSpec(16*8, predefinedIV) + cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk, gcmParameterSpec) + } else { + // If predefinedIV is null, we generate a new one + cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) + } + + var len: Int? + val buffer = ByteArray(8192) + val inputFile = fileParameters.getFileInputStream() + val outputFile = fileParameters.getFileOutputStream() + val iv: ByteArray? = cipherInstance!!.iv + + outputFile?.write(iv) + outputFile?.flush() + + val encryptedStream = CipherOutputStream(outputFile!!, cipherInstance) + while(true) { + len = inputFile?.read(buffer) + if (len != null && len > 0) { + encryptedStream.write(buffer,0,len) + } else { + break + } + } + encryptedStream.flush() + encryptedStream.close() + inputFile?.close() + outputFile.close() + + return fileParameters.outputExists() } override fun decrypt(data: ByteArray, key: ByteArray): ByteArray { + // Extract the IV from the cipherText val iv: ByteArray = data.take(12).toByteArray() val payload: ByteArray = data.drop(12).toByteArray() - return decryptAsList(listOf(iv, payload), key) - } - override fun decryptAsList(data: List, key: ByteArray): ByteArray { + // Initialize secret key spec val sk = SecretKeySpec(key, "AES") - val payload: ByteArray = data.last() - val iv: ByteArray = data.first() - val gcmSpec = GCMParameterSpec(16 * 8, iv) + + // Initialize GCMParameterSpec + val gcmParameterSpec = GCMParameterSpec(16 * 8, iv) + + // Initialize cipher (if not already done) lazyLoadCipher() - cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmSpec) + cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmParameterSpec) + + // Decrypt data return cipherInstance!!.doFinal(payload) } + + override fun decryptFile(fileParameters: FileParameters, key: ByteArray): Boolean { + val iv = ByteArray(12) + val inputFile = fileParameters.getFileInputStream() ?: throw Exception("Error while reading IV") + + // Read the first 12 bytes from the file + for (i in 0 until 12) { + iv[i] = inputFile.read().toByte() + } + + // Initialize secret key spec + val sk = SecretKeySpec(key, "AES") + + // Initialize GCMParameterSpec + val gcmParameterSpec = GCMParameterSpec(16 * 8, iv) + + // Initialize cipher (if not already done) + lazyLoadCipher() + + cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmParameterSpec) + + var len: Int? + val buffer = ByteArray(8192) + val outputFile = fileParameters.getFileOutputStream() + val decryptedStream = CipherOutputStream(outputFile!!, cipherInstance) + while (true) { + len = inputFile.read(buffer) + if(len > 0){ + decryptedStream.write(buffer,0, len) + } else { + break + } + } + decryptedStream.flush() + decryptedStream.close() + inputFile.close() + + return fileParameters.outputExists() + } } \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt index 5893d25..6d8e63f 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Cipher.kt @@ -1,12 +1,10 @@ package fr.pointcheval.native_crypto_android.interfaces -import fr.pointcheval.native_crypto_android.utils.CipherAlgorithm +import fr.pointcheval.native_crypto_android.utils.FileParameters interface Cipher { - val algorithm: CipherAlgorithm - - fun encrypt(data: ByteArray, key: ByteArray): ByteArray + fun encrypt(data: ByteArray, key: ByteArray, predefinedIV: ByteArray?): ByteArray fun decrypt(data: ByteArray, key: ByteArray): ByteArray - fun encryptAsList(data: ByteArray, key: ByteArray): List - fun decryptAsList(data: List, key: ByteArray): ByteArray + fun encryptFile(fileParameters: FileParameters, key: ByteArray, predefinedIV: ByteArray?): Boolean + fun decryptFile(fileParameters: FileParameters, key: ByteArray): Boolean } \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt deleted file mode 100644 index 4df212b..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/Key.kt +++ /dev/null @@ -1,5 +0,0 @@ -package fr.pointcheval.native_crypto_android.interfaces - -interface Key { - val bytes: ByteArray -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt index 6bd9216..17db04e 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/interfaces/KeyDerivation.kt @@ -1,10 +1,5 @@ package fr.pointcheval.native_crypto_android.interfaces -import fr.pointcheval.native_crypto_android.keys.SecretKey -import fr.pointcheval.native_crypto_android.utils.KdfAlgorithm - interface KeyDerivation { - val algorithm: KdfAlgorithm - - fun derive(): SecretKey + fun derive(): ByteArray? } \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt index 17d8c5e..0daa8ae 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/kdf/Pbkdf2.kt @@ -1,39 +1,43 @@ package fr.pointcheval.native_crypto_android.kdf +import fr.pointcheval.native_crypto_android.HashAlgorithm import fr.pointcheval.native_crypto_android.interfaces.KeyDerivation -import fr.pointcheval.native_crypto_android.keys.SecretKey -import fr.pointcheval.native_crypto_android.utils.HashAlgorithm -import fr.pointcheval.native_crypto_android.utils.KdfAlgorithm +import fr.pointcheval.native_crypto_android.utils.HashAlgorithmParser import javax.crypto.SecretKeyFactory import javax.crypto.spec.PBEKeySpec class Pbkdf2( - private val keyBytesCount: Int, private val iterations: Int, - private val hash: HashAlgorithm = HashAlgorithm.sha256 + private val length: Int, private val iterations: Int, + private val hashAlgorithm: HashAlgorithm ) : KeyDerivation { - private var password: String? = null - private var salt: String? = null + private var password: CharArray? = null + private var salt: ByteArray? = null - fun init(password: String, salt: String) { - this.password = password + fun init(password: ByteArray, salt: ByteArray) { + // Transform the password to a char array + val passwordCharArray = CharArray(password.size) + for (i in password.indices) { + passwordCharArray[i] = password[i].toInt().toChar() + } + + this.password = passwordCharArray this.salt = salt } - override val algorithm: KdfAlgorithm - get() = KdfAlgorithm.pbkdf2 - - override fun derive(): SecretKey { + override fun derive(): ByteArray? { if (password == null || salt == null) { throw Exception("Password and Salt must be initialized.") } val spec = PBEKeySpec( - password!!.toCharArray(), - salt!!.toByteArray(), + password!!, + salt!!, iterations, - keyBytesCount * 8 + length * 8 ) - val skf: SecretKeyFactory = SecretKeyFactory.getInstance(hash.pbkdf2String()) - return SecretKey(skf.generateSecret(spec).encoded) + val skf: SecretKeyFactory = + SecretKeyFactory.getInstance(HashAlgorithmParser.getPbkdf2String(hashAlgorithm)) + + return skf.generateSecret(spec).encoded } } \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt deleted file mode 100644 index 07e9833..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/keys/SecretKey.kt +++ /dev/null @@ -1,14 +0,0 @@ -package fr.pointcheval.native_crypto_android.keys - -import fr.pointcheval.native_crypto_android.interfaces.Key -import java.security.SecureRandom - -class SecretKey(override val bytes: ByteArray) : Key { - companion object { - fun fromSecureRandom(bitsCount: Int): SecretKey { - val bytes = ByteArray(bitsCount / 8) - SecureRandom.getInstanceStrong().nextBytes(bytes) - return SecretKey(bytes) - } - } -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt deleted file mode 100644 index a95185f..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/CipherAlgorithm.kt +++ /dev/null @@ -1,14 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -import fr.pointcheval.native_crypto_android.ciphers.AES -import fr.pointcheval.native_crypto_android.interfaces.Cipher - -enum class CipherAlgorithm { - aes; - - fun getCipher(): Cipher { - return when (this) { - aes -> AES() - } - } -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt deleted file mode 100644 index 1cb359a..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Constants.kt +++ /dev/null @@ -1,14 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -object Constants { - const val ALGORITHM = "algorithm" - const val BITS_COUNT = "bitsCount" - const val DATA = "data" - const val PASSWORD = "password" - const val SALT = "salt" - const val KEY = "key" - const val KEY_BYTES_COUNT = "keyBytesCount" - const val ITERATIONS = "iterations" - const val EPHEMERAL_PRIVATE_KEY = "ephemeralPrivateKey" - const val OTHER_PUBLIC_KEY = "otherPublicKey" -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt new file mode 100644 index 0000000..44207dc --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt @@ -0,0 +1,71 @@ +package fr.pointcheval.native_crypto_android.utils + +import android.content.Context +import android.content.res.Resources +import android.net.Uri +import androidx.documentfile.provider.DocumentFile +import java.io.* + +class FileParameters(ctx: Context, input: String, output: String) { + private var context: Context + + private var inputPath: String + private var outputPath: String + + init { + this.context = ctx + this.inputPath = input + this.outputPath = output + } + + private fun getUri(): Uri? { + val persistedUriPermissions = context.contentResolver.persistedUriPermissions + if (persistedUriPermissions.size > 0) { + val uriPermission = persistedUriPermissions[0] + return uriPermission.uri + } + return null + } + + private fun getDocumentFileByPath(path: String): DocumentFile { + var doc = DocumentFile.fromTreeUri(context, getUri()!!) + val parts = path.split("/") + for (i in parts.indices) { + val nextFile = doc?.findFile(parts[i]) + if(nextFile != null){ + doc = nextFile + } + } + if (doc != null){ + return doc + } else { + throw Resources.NotFoundException("File not found") + } + } + + fun getFileOutputStream(): OutputStream? { + val path = outputPath + return try{ + FileOutputStream(path) + } catch(e: IOException){ + val documentFile: DocumentFile = this.getDocumentFileByPath(path) + val documentUri = documentFile.uri + context.contentResolver.openOutputStream(documentUri) + } + } + + fun getFileInputStream(): InputStream? { + val path = inputPath + return try{ + FileInputStream(path) + } catch(e: IOException){ + val documentFile: DocumentFile = this.getDocumentFileByPath(path) + val documentUri = documentFile.uri + context.contentResolver.openInputStream(documentUri) + } + } + + fun outputExists(): Boolean { + return File(outputPath).exists() + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt deleted file mode 100644 index 904f7ce..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithm.kt +++ /dev/null @@ -1,50 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -import java.security.MessageDigest - -@Suppress("EnumEntryName") -enum class HashAlgorithm(val bitsCount: Int) { - sha256(256), - sha384(384), - sha512(512); - - fun messageDigestString(): String { - return when (this) { - sha256 -> "SHA-256" - sha384 -> "SHA-384" - sha512 -> "SHA-512" - } - } - - fun hmacString(): String { - return when (this) { - sha256 -> "HmacSHA256" - sha384 -> "HmacSHA384" - sha512 -> "HmacSHA512" - } - } - - fun pbkdf2String(): String { - return when (this) { - sha256 -> "PBKDF2WithHmacSHA256" - sha384 -> "PBKDF2WithHmacSHA384" - sha512 -> "PBKDF2WithHmacSHA512" - } - } - - fun digest(data: ByteArray): ByteArray { - val md = MessageDigest.getInstance(messageDigestString()) - return md.digest(data) - } - - companion object { - fun digest(data: ByteArray, algorithm: String): ByteArray { - for (h in values()) { - if (h.name == algorithm) { - return h.digest(data) - } - } - throw Exception("Unknown HashAlgorithm: $algorithm") - } - } -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithmParser.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithmParser.kt new file mode 100644 index 0000000..0114acb --- /dev/null +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/HashAlgorithmParser.kt @@ -0,0 +1,31 @@ +package fr.pointcheval.native_crypto_android.utils + +import fr.pointcheval.native_crypto_android.HashAlgorithm +import java.security.MessageDigest +import javax.crypto.Mac + +object HashAlgorithmParser { + fun getMessageDigest(algorithm: HashAlgorithm): MessageDigest { + return when (algorithm) { + HashAlgorithm.SHA256 -> MessageDigest.getInstance("SHA-256") + HashAlgorithm.SHA384 -> MessageDigest.getInstance("SHA-384") + HashAlgorithm.SHA512 -> MessageDigest.getInstance("SHA-512") + } + } + + fun getMac(algorithm: HashAlgorithm): Mac { + return when (algorithm) { + HashAlgorithm.SHA256 -> Mac.getInstance("HmacSHA256") + HashAlgorithm.SHA384 -> Mac.getInstance("HmacSHA384") + HashAlgorithm.SHA512 -> Mac.getInstance("HmacSHA512") + } + } + + fun getPbkdf2String(algorithm: HashAlgorithm): String { + return when (algorithm) { + HashAlgorithm.SHA256 -> "PBKDF2WithHmacSHA256" + HashAlgorithm.SHA384 -> "PBKDF2WithHmacSHA384" + HashAlgorithm.SHA512 -> "PBKDF2WithHmacSHA512" + } + } +} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt deleted file mode 100644 index 4ef9f69..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/KdfAlgorithm.kt +++ /dev/null @@ -1,5 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -enum class KdfAlgorithm { - pbkdf2 -} \ No newline at end of file diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt deleted file mode 100644 index d4832de..0000000 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/Task.kt +++ /dev/null @@ -1,44 +0,0 @@ -package fr.pointcheval.native_crypto_android.utils - -class Task(private var task: () -> T) { - - private var successful = false - private var result: T? = null - private var exception: Exception? = null - - fun isSuccessful(): Boolean { - return successful - } - - fun getResult(): T { - if (successful && result != null) { - return result!! - } else { - throw Exception("No result found!") - } - } - - fun getException(): Exception { - if (exception != null) { - return exception!! - } else { - throw Exception("No exception found!") - } - } - - fun call() { - try { - result = task() - exception = null - successful = true - } catch (e: Exception) { - exception = e - result = null - successful = false - } - } - - fun finalize(callback: (task: Task) -> Unit) { - callback(this) - } -} \ No newline at end of file -- 2.47.2 From 68217ac4b96ce56c29887cbd4c57b92ae2b88fa0 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Tue, 4 Apr 2023 22:37:12 +0200 Subject: [PATCH 11/21] feat(ios): use swift pigeon generator --- .../ios/Classes/NativeCrypto.swift | 113 +++ .../ios/Classes/Public/messages.g.h | 188 ---- .../Classes/SwiftNativeCryptoIosPlugin.swift | 141 +-- .../ios/Classes/ciphers/AESCipher.swift | 105 ++- .../ios/Classes/kdf/Pbkdf2.swift | 41 +- .../ios/Classes/keys/SecretKey.swift | 25 - .../ios/Classes/messages.g.m | 868 ------------------ .../ios/Classes/messages.g.swift | 243 +++++ .../ios/Classes/protocols/Cipher.swift | 7 +- .../ios/Classes/protocols/Key.swift | 12 - .../ios/Classes/protocols/KeyDerivation.swift | 3 +- .../ios/Classes/utils/CipherAlgorithm.swift | 18 - .../ios/Classes/utils/FileParameters.swift | 12 + .../ios/Classes/utils/HashAlgorithm.swift | 45 - .../Classes/utils/HashAlgorithmParser.swift | 31 + .../ios/Classes/utils/KdfAlgorithm.swift | 12 - .../ios/Classes/utils/NativeCryptoError.swift | 55 +- .../ios/Classes/utils/Task.swift | 9 +- 18 files changed, 551 insertions(+), 1377 deletions(-) create mode 100644 packages/native_crypto_ios/ios/Classes/NativeCrypto.swift delete mode 100644 packages/native_crypto_ios/ios/Classes/Public/messages.g.h delete mode 100644 packages/native_crypto_ios/ios/Classes/keys/SecretKey.swift delete mode 100644 packages/native_crypto_ios/ios/Classes/messages.g.m create mode 100644 packages/native_crypto_ios/ios/Classes/messages.g.swift delete mode 100644 packages/native_crypto_ios/ios/Classes/protocols/Key.swift delete mode 100644 packages/native_crypto_ios/ios/Classes/utils/CipherAlgorithm.swift delete mode 100644 packages/native_crypto_ios/ios/Classes/utils/HashAlgorithm.swift create mode 100644 packages/native_crypto_ios/ios/Classes/utils/HashAlgorithmParser.swift delete mode 100644 packages/native_crypto_ios/ios/Classes/utils/KdfAlgorithm.swift diff --git a/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift b/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift new file mode 100644 index 0000000..ca8fa2c --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift @@ -0,0 +1,113 @@ +// +// NativeCrypto.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 04/04/2023. +// + +import Foundation +import CryptoKit + +public class NativeCrypto: NSObject, NativeCryptoAPI { + func hash(data: FlutterStandardTypedData, algorithm: HashAlgorithm) throws -> FlutterStandardTypedData? { + var md = HashAlgorithmParser.getMessageDigest(algorithm: algorithm) + md.update(data: data.data) + + let bytes = Data(md.finalize()) + + return FlutterStandardTypedData(bytes: bytes) + } + + func hmac(data: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: HashAlgorithm) throws -> FlutterStandardTypedData? { + let symmetricKey : SymmetricKey = SymmetricKey.init(data: key.data) + + switch algorithm { + case .sha256: return FlutterStandardTypedData(bytes: Data(HMAC(key: symmetricKey).finalize())) + case .sha384: return FlutterStandardTypedData(bytes: Data(HMAC(key: symmetricKey).finalize())) + case .sha512: return FlutterStandardTypedData(bytes: Data(HMAC(key: symmetricKey).finalize())) + @unknown default: fatalError("Unknown algorithm") + } + } + + func generateSecureRandom(length: Int64) throws -> FlutterStandardTypedData? { + let lengthInt = Int(truncatingIfNeeded: length) + let symmetricKey = SymmetricKey.init(size: SymmetricKeySize(bitCount: lengthInt / 8)) + let bytes = symmetricKey.withUnsafeBytes + { + return Data(Array($0)) + } + + return FlutterStandardTypedData(bytes: bytes) + } + + func pbkdf2(password: FlutterStandardTypedData, salt: FlutterStandardTypedData, length: Int64, iterations: Int64, algorithm: HashAlgorithm) throws -> FlutterStandardTypedData? { + let lengthInt = Int(truncatingIfNeeded: length) + let iterationsInt = Int(truncatingIfNeeded: iterations) + let pbkdf2 = Pbkdf2(length: lengthInt, iterations: iterationsInt, hashAlgorithm: algorithm) + pbkdf2.initialize(password: password.data, salt: salt.data) + let data = try? pbkdf2.derive() + + if (data == nil) { + return nil + } + + return FlutterStandardTypedData(bytes: data!) + } + + func encrypt(plainText: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> FlutterStandardTypedData? { + let aes = AESCipher() + let bytes = try? aes.encrypt(data: plainText.data, key: key.data, predefinedIV: nil) + + if (bytes == nil) { + return nil + } + + return FlutterStandardTypedData(bytes: bytes!) + } + + func encryptWithIV(plainText: FlutterStandardTypedData, iv: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> FlutterStandardTypedData? { + let aes = AESCipher() + let bytes = try? aes.encrypt(data: plainText.data, key: key.data, predefinedIV: iv.data) + + if (bytes == nil) { + return nil + } + + return FlutterStandardTypedData(bytes: bytes!) + } + + func decrypt(cipherText: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> FlutterStandardTypedData? { + let aes = AESCipher() + let bytes = try? aes.decrypt(data: cipherText.data, key: key.data) + + if (bytes == nil) { + return nil + } + + return FlutterStandardTypedData(bytes: bytes!) + } + + func encryptFile(plainTextPath: String, cipherTextPath: String, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> Bool? { + let aes = AESCipher() + let params = FileParameters(input: plainTextPath, output: cipherTextPath) + let success = try? aes.encryptFile(fileParameters: params, key: key.data, predefinedIV: nil) + + return success + } + + func encryptFileWithIV(plainTextPath: String, cipherTextPath: String, iv: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> Bool? { + let aes = AESCipher() + let params = FileParameters(input: plainTextPath, output: cipherTextPath) + let success = try? aes.encryptFile(fileParameters: params, key: key.data, predefinedIV: iv.data) + + return success + } + + func decryptFile(cipherTextPath: String, plainTextPath: String, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> Bool? { + let aes = AESCipher() + let params = FileParameters(input: cipherTextPath, output: plainTextPath) + let success = try? aes.decryptFile(fileParameters: params, key: key.data) + + return success + } +} diff --git a/packages/native_crypto_ios/ios/Classes/Public/messages.g.h b/packages/native_crypto_ios/ios/Classes/Public/messages.g.h deleted file mode 100644 index 6caeca0..0000000 --- a/packages/native_crypto_ios/ios/Classes/Public/messages.g.h +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2019-2023 Hugo Pointcheval -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. -// -- -// Autogenerated from Pigeon (v9.0.0), do not edit directly. -// See also: https://pub.dev/packages/pigeon - -#import - -@protocol FlutterBinaryMessenger; -@protocol FlutterMessageCodec; -@class FlutterError; -@class FlutterStandardTypedData; - -NS_ASSUME_NONNULL_BEGIN - -@class HashRequest; -@class HashResponse; -@class HmacRequest; -@class HmacResponse; -@class GenerateSecureRandomRequest; -@class GenerateSecureRandomResponse; -@class Pbkdf2Request; -@class Pbkdf2Response; -@class EncryptRequest; -@class EncryptResponse; -@class DecryptRequest; -@class DecryptResponse; -@class EncryptFileRequest; -@class EncryptFileResponse; -@class DecryptFileRequest; -@class DecryptFileResponse; -@class EncryptWithIVRequest; - -@interface HashRequest : NSObject -+ (instancetype)makeWithData:(nullable FlutterStandardTypedData *)data - algorithm:(nullable NSString *)algorithm; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * data; -@property(nonatomic, copy, nullable) NSString * algorithm; -@end - -@interface HashResponse : NSObject -+ (instancetype)makeWithHash:(nullable FlutterStandardTypedData *)hash; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * hash; -@end - -@interface HmacRequest : NSObject -+ (instancetype)makeWithData:(nullable FlutterStandardTypedData *)data - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * data; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; -@property(nonatomic, copy, nullable) NSString * algorithm; -@end - -@interface HmacResponse : NSObject -+ (instancetype)makeWithHmac:(nullable FlutterStandardTypedData *)hmac; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * hmac; -@end - -@interface GenerateSecureRandomRequest : NSObject -+ (instancetype)makeWithLength:(nullable NSNumber *)length; -@property(nonatomic, strong, nullable) NSNumber * length; -@end - -@interface GenerateSecureRandomResponse : NSObject -+ (instancetype)makeWithRandom:(nullable FlutterStandardTypedData *)random; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * random; -@end - -@interface Pbkdf2Request : NSObject -+ (instancetype)makeWithPassword:(nullable FlutterStandardTypedData *)password - salt:(nullable FlutterStandardTypedData *)salt - length:(nullable NSNumber *)length - iterations:(nullable NSNumber *)iterations - hashAlgorithm:(nullable NSString *)hashAlgorithm; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * password; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * salt; -@property(nonatomic, strong, nullable) NSNumber * length; -@property(nonatomic, strong, nullable) NSNumber * iterations; -@property(nonatomic, copy, nullable) NSString * hashAlgorithm; -@end - -@interface Pbkdf2Response : NSObject -+ (instancetype)makeWithKey:(nullable FlutterStandardTypedData *)key; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; -@end - -@interface EncryptRequest : NSObject -+ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * plainText; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; -@property(nonatomic, copy, nullable) NSString * algorithm; -@end - -@interface EncryptResponse : NSObject -+ (instancetype)makeWithCipherText:(nullable FlutterStandardTypedData *)cipherText; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * cipherText; -@end - -@interface DecryptRequest : NSObject -+ (instancetype)makeWithCipherText:(nullable FlutterStandardTypedData *)cipherText - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * cipherText; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; -@property(nonatomic, copy, nullable) NSString * algorithm; -@end - -@interface DecryptResponse : NSObject -+ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * plainText; -@end - -@interface EncryptFileRequest : NSObject -+ (instancetype)makeWithPlainTextPath:(nullable NSString *)plainTextPath - cipherTextPath:(nullable NSString *)cipherTextPath - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm; -@property(nonatomic, copy, nullable) NSString * plainTextPath; -@property(nonatomic, copy, nullable) NSString * cipherTextPath; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; -@property(nonatomic, copy, nullable) NSString * algorithm; -@end - -@interface EncryptFileResponse : NSObject -+ (instancetype)makeWithSuccess:(nullable NSNumber *)success; -@property(nonatomic, strong, nullable) NSNumber * success; -@end - -@interface DecryptFileRequest : NSObject -+ (instancetype)makeWithCipherTextPath:(nullable NSString *)cipherTextPath - plainTextPath:(nullable NSString *)plainTextPath - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm; -@property(nonatomic, copy, nullable) NSString * cipherTextPath; -@property(nonatomic, copy, nullable) NSString * plainTextPath; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; -@property(nonatomic, copy, nullable) NSString * algorithm; -@end - -@interface DecryptFileResponse : NSObject -+ (instancetype)makeWithSuccess:(nullable NSNumber *)success; -@property(nonatomic, strong, nullable) NSNumber * success; -@end - -@interface EncryptWithIVRequest : NSObject -+ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText - iv:(nullable FlutterStandardTypedData *)iv - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * plainText; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * iv; -@property(nonatomic, strong, nullable) FlutterStandardTypedData * key; -@property(nonatomic, copy, nullable) NSString * algorithm; -@end - -/// The codec used by NativeCryptoAPI. -NSObject *NativeCryptoAPIGetCodec(void); - -@protocol NativeCryptoAPI -/// @return `nil` only when `error != nil`. -- (nullable HashResponse *)hashRequest:(HashRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; -/// @return `nil` only when `error != nil`. -- (nullable HmacResponse *)hmacRequest:(HmacRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; -/// @return `nil` only when `error != nil`. -- (nullable GenerateSecureRandomResponse *)generateSecureRandomRequest:(GenerateSecureRandomRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; -/// @return `nil` only when `error != nil`. -- (nullable Pbkdf2Response *)pbkdf2Request:(Pbkdf2Request *)request error:(FlutterError *_Nullable *_Nonnull)error; -/// @return `nil` only when `error != nil`. -- (nullable EncryptResponse *)encryptRequest:(EncryptRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; -/// @return `nil` only when `error != nil`. -- (nullable DecryptResponse *)decryptRequest:(DecryptRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; -/// @return `nil` only when `error != nil`. -- (nullable EncryptFileResponse *)encryptFileRequest:(EncryptFileRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; -/// @return `nil` only when `error != nil`. -- (nullable DecryptFileResponse *)decryptFileRequest:(DecryptFileRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; -/// @return `nil` only when `error != nil`. -- (nullable EncryptResponse *)encryptWithIVRequest:(EncryptWithIVRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; -@end - -extern void NativeCryptoAPISetup(id binaryMessenger, NSObject *_Nullable api); - -NS_ASSUME_NONNULL_END diff --git a/packages/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift b/packages/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift index c9b1a84..8a6a342 100644 --- a/packages/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift +++ b/packages/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift @@ -3,144 +3,9 @@ import UIKit @available(iOS 13.0, *) public class SwiftNativeCryptoIosPlugin: NSObject, FlutterPlugin { - static let name: String = "plugins.hugop.cl/native_crypto" - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: name, binaryMessenger: registrar.messenger()) - let instance = SwiftNativeCryptoIosPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) + let messenger : FlutterBinaryMessenger = registrar.messenger() + let api : NativeCryptoAPI & NSObjectProtocol = NativeCrypto.init() + NativeCryptoAPISetup.setUp(binaryMessenger: messenger, api: api); } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "digest": _call(task: handleDigest(call: call), result: result) - case "generateSecretKey": _call(task: handleGenerateSecretKey(call: call), result: result) - case "pbkdf2": _call(task: handlePbkdf2(call: call), result: result) - case "encryptAsList": _call(task: handleEncryptAsList(call: call), result: result) - case "decryptAsList": _call(task: handleDecryptAsList(call: call), result: result) - case "encrypt": _call(task: handleCrypt(call: call, forEncryption: true), result: result) - case "decrypt": _call(task: handleCrypt(call: call, forEncryption: false), result: result) - default: result(FlutterMethodNotImplemented) - } - } - - private func _call(task: Task, result: @escaping FlutterResult) { - task.call() - task.finalize(callback: {(task: Task) in - if (task.isSuccessful()) { - result(task.getResult()!) - } else { - let exception: Error = task.getException() - let message = exception.localizedDescription - result(FlutterError(code: "native_crypto", message: message, details: nil)) - } - }) - } - - private func handleDigest(call: FlutterMethodCall) -> Task { - return Task(task: { - let args : NSDictionary = call.arguments as! NSDictionary - - let data : Data = (args["data"] as! FlutterStandardTypedData).data - let algorithm : String = args["algorithm"] as! String - - return FlutterStandardTypedData.init(bytes: try HashAlgorithm.digest(data: data, algorithm: algorithm)) - }) - } - - private func handleGenerateSecretKey(call: FlutterMethodCall) -> Task { - return Task(task: { - let args : NSDictionary = call.arguments as! NSDictionary - - let bitsCount : NSNumber = args["bitsCount"] as! NSNumber - - return FlutterStandardTypedData.init(bytes: SecretKey(fromSecureRandom: bitsCount.intValue).bytes) - }) - } - - private func handlePbkdf2(call: FlutterMethodCall) -> Task { - return Task(task: { - let args : NSDictionary = call.arguments as! NSDictionary - - let password : String = args["password"] as! String - let salt : String = args["salt"] as! String - let keyBytesCount : NSNumber = args["keyBytesCount"] as! NSNumber - let iterations : NSNumber = args["iterations"] as! NSNumber - let algorithm : String = args["algorithm"] as! String - - let pbkdf2 : Pbkdf2 = Pbkdf2(keyBytesCount: keyBytesCount.intValue, iterations: iterations.intValue) - pbkdf2.hash = HashAlgorithm.init(rawValue: algorithm) ?? pbkdf2.hash - pbkdf2.initialize(password: password, salt: salt) - - return FlutterStandardTypedData.init(bytes: try pbkdf2.derive().bytes) - }) - } - - private func handleEncryptAsList(call: FlutterMethodCall) -> Task> { - return Task(task: { - let args : NSDictionary = call.arguments as! NSDictionary - - let data : Data = (args["data"] as! FlutterStandardTypedData).data - let key : Data = (args["key"] as! FlutterStandardTypedData).data - let algorithm : String = args["algorithm"] as! String - - let cipherAlgorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algorithm) - var cipher : Cipher - if (cipherAlgorithm != nil) { - cipher = cipherAlgorithm!.getCipher - } else { - throw NativeCryptoError.cipherError - } - - return try cipher.encryptAsList(data: data, key: key) - }) - } - - private func handleDecryptAsList(call: FlutterMethodCall) -> Task { - return Task(task: { - let args : NSDictionary = call.arguments as! NSDictionary - - let data = args["data"] as! NSArray - let key : Data = (args["key"] as! FlutterStandardTypedData).data - let algorithm : String = args["algorithm"] as! String - - let iv = (data[0] as! FlutterStandardTypedData).data - let encrypted = (data[1] as! FlutterStandardTypedData).data - - let cipherAlgorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algorithm) - var cipher : Cipher - if (cipherAlgorithm != nil) { - cipher = cipherAlgorithm!.getCipher - } else { - throw NativeCryptoError.cipherError - } - - return FlutterStandardTypedData.init(bytes: try cipher.decryptAsList(data: [iv, encrypted], key: key)) - }) - } - - private func handleCrypt(call: FlutterMethodCall, forEncryption: Bool) -> Task { - return Task(task: { - let args : NSDictionary = call.arguments as! NSDictionary - - let data : Data = (args["data"] as! FlutterStandardTypedData).data - let key : Data = (args["key"] as! FlutterStandardTypedData).data - let algorithm : String = args["algorithm"] as! String - - let cipherAlgorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algorithm) - var cipher : Cipher - if (cipherAlgorithm != nil) { - cipher = cipherAlgorithm!.getCipher - } else { - throw NativeCryptoError.cipherError - } - - if (forEncryption) { - return FlutterStandardTypedData.init(bytes: try cipher.encrypt(data: data, key: key)) - } else { - return FlutterStandardTypedData.init(bytes: try cipher.decrypt(data: data, key: key)) - } - }) - } - } diff --git a/packages/native_crypto_ios/ios/Classes/ciphers/AESCipher.swift b/packages/native_crypto_ios/ios/Classes/ciphers/AESCipher.swift index 2110ff5..20c48ff 100644 --- a/packages/native_crypto_ios/ios/Classes/ciphers/AESCipher.swift +++ b/packages/native_crypto_ios/ios/Classes/ciphers/AESCipher.swift @@ -9,17 +9,33 @@ import Foundation import CryptoKit class AESCipher : Cipher { - var algorithm: CipherAlgorithm = CipherAlgorithm.aes - /// Encrypts plaintext with key using AES GCM @available(iOS 13.0, *) - func encrypt(data: Data, key: Data) throws -> Data { + func encrypt(data: Data, key: Data, predefinedIV: Data?) throws -> Data { let symmetricKey : SymmetricKey = SymmetricKey.init(data: key) - let encrypted : AES.GCM.SealedBox? = try? AES.GCM.seal(data, using: symmetricKey) - let encryptedData : Data? = encrypted?.combined + // Encryption + var encrypted : AES.GCM.SealedBox + do { + // If predefinedIV is not null use it + if (predefinedIV != nil) { + let nonce = try AES.GCM.Nonce(data: predefinedIV!) + encrypted = try AES.GCM.seal(data, using: symmetricKey, nonce: nonce) + } else { + encrypted = try AES.GCM.seal(data, using: symmetricKey) + } + } catch CryptoKitError.incorrectKeySize { + throw NativeCryptoError.invalidKeySize() + } catch CryptoKitError.invalidParameter, CryptoKitError.incorrectParameterSize { + throw NativeCryptoError.invalidParameter() + } catch { + throw NativeCryptoError.unknownError(reason: "An error occured during encryption.") + } + + // NONCE[12] || CIPHERTEXT[n] || TAG[16] + let encryptedData : Data? = encrypted.combined if (encryptedData == nil) { - throw NativeCryptoError.encryptionError + throw NativeCryptoError.unknownError(reason: "An error occured during ciphertext combination.") } return encryptedData! } @@ -28,30 +44,65 @@ class AESCipher : Cipher { @available(iOS 13.0, *) func decrypt(data: Data, key: Data) throws -> Data { let symmetricKey = SymmetricKey.init(data: key) - let sealedBox = try? AES.GCM.SealedBox(combined: data) - if (sealedBox == nil) { return Data.init() } - let decryptedData = try? AES.GCM.open(sealedBox!, using: symmetricKey) - if (decryptedData == nil) { - throw NativeCryptoError.decryptionError + + // SealedBox initialization + var encrypted : AES.GCM.SealedBox + do { + encrypted = try AES.GCM.SealedBox(combined: data) + } catch { + throw NativeCryptoError.unknownError(reason: "An error occured during sealedbox initialization.") } - return decryptedData! - } - - func encryptAsList(data: Data, key: Data) throws -> [Data] { - let encryptedData = try encrypt(data: data, key: key) - let iv = encryptedData.prefix(12) - let data = encryptedData.suffix(from: 12) + // Decryption + var decryptedData : Data + do { + decryptedData = try AES.GCM.open(encrypted, using: symmetricKey) + } catch CryptoKitError.incorrectKeySize { + throw NativeCryptoError.invalidKeySize() + } catch CryptoKitError.invalidParameter, CryptoKitError.incorrectParameterSize { + throw NativeCryptoError.invalidParameter() + } catch CryptoKitError.authenticationFailure { + throw NativeCryptoError.authenticationError() + } catch { + throw NativeCryptoError.unknownError(reason: "An error occured during decryption.") + } - return [iv, data] - } - - func decryptAsList(data: [Data], key: Data) throws -> Data { - var encryptedData = data.first! - let data = data.last! - encryptedData.append(data) - - let decryptedData = try decrypt(data: encryptedData, key: key) return decryptedData } + + /// Encrypts plaintext file with key using AES GCM + func encryptFile(fileParameters: FileParameters, key: Data, predefinedIV: Data?) throws -> Bool { + let fileManager = FileManager.default + let inputFile = URL(fileURLWithPath: fileParameters.inputPath) + + guard let data = fileManager.contents(atPath: inputFile.path) else { + throw NativeCryptoError.ioError(reason: "Error while reading input file.") + } + + let encryptedData = try encrypt(data: data, key: key, predefinedIV: predefinedIV) + + guard fileManager.createFile(atPath: fileParameters.outputPath, contents: encryptedData, attributes: nil) else { + throw NativeCryptoError.ioError(reason: "Error while writing output file.") + } + + return true + } + + /// Decrypts ciphertext file with key using AES GCM + func decryptFile(fileParameters: FileParameters, key: Data) throws -> Bool { + let fileManager = FileManager.default + let inputFile = URL(fileURLWithPath: fileParameters.inputPath) + + guard let data = fileManager.contents(atPath: inputFile.path) else { + throw NativeCryptoError.ioError(reason: "Error while reading input file.") + } + + let decryptedData = try decrypt(data: data, key: key) + + guard fileManager.createFile(atPath: fileParameters.outputPath, contents: decryptedData, attributes: nil) else { + throw NativeCryptoError.ioError(reason: "Error while writing output file.") + } + + return true + } } diff --git a/packages/native_crypto_ios/ios/Classes/kdf/Pbkdf2.swift b/packages/native_crypto_ios/ios/Classes/kdf/Pbkdf2.swift index 8a34a1e..0377904 100644 --- a/packages/native_crypto_ios/ios/Classes/kdf/Pbkdf2.swift +++ b/packages/native_crypto_ios/ios/Classes/kdf/Pbkdf2.swift @@ -9,45 +9,42 @@ import Foundation import CommonCrypto class Pbkdf2 : KeyDerivation { - var algorithm: KdfAlgorithm = KdfAlgorithm.pbkdf2 - - var keyBytesCount: Int + var length: Int var iterations: Int - var hash: HashAlgorithm = HashAlgorithm.HashSHA256 + var hashAlgorithm: HashAlgorithm - var password: String? = nil - var salt: String? = nil + var password: Data? = nil + var salt: Data? = nil - init(keyBytesCount: Int, iterations: Int) { - self.keyBytesCount = keyBytesCount + init(length: Int, iterations: Int, hashAlgorithm: HashAlgorithm) { + self.length = length self.iterations = iterations + self.hashAlgorithm = hashAlgorithm } - func initialize(password: String, salt: String) { + func initialize(password: Data, salt: Data) { self.password = password self.salt = salt } - func derive() throws -> SecretKey { + func derive() throws -> Data? { if (password == nil || salt == nil) { - throw NativeCryptoError.pbkdf2Error + throw NativeCryptoError.kdfError(reason: "Password and salt cannot be null.") } - let passwordData = password!.data(using: .utf8)! - let saltData = salt!.data(using: .utf8)! - - var derivedKeyData = Data(repeating: 0, count: keyBytesCount) + var derivedKeyData = Data(repeating: 0, count: length) let localDerivedKeyData = derivedKeyData + let identifier = HashAlgorithmParser.getPbkdf2Identifier(algorithm: hashAlgorithm) let status = derivedKeyData.withUnsafeMutableBytes { (derivedKeyBytes: UnsafeMutableRawBufferPointer) in - saltData.withUnsafeBytes { (saltBytes: UnsafeRawBufferPointer) in + salt!.withUnsafeBytes { (saltBytes: UnsafeRawBufferPointer) in CCKeyDerivationPBKDF( CCPBKDFAlgorithm(kCCPBKDF2), - password, - passwordData.count, + (password! as NSData).bytes, + password!.count, saltBytes.bindMemory(to: UInt8.self).baseAddress, - saltData.count, - hash.pbkdf2identifier, + salt!.count, + identifier, UInt32(iterations), derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress, localDerivedKeyData.count) @@ -55,9 +52,9 @@ class Pbkdf2 : KeyDerivation { } if (status != kCCSuccess) { - throw NativeCryptoError.pbkdf2Error + throw NativeCryptoError.kdfError() } - return SecretKey(derivedKeyData) + return derivedKeyData } } diff --git a/packages/native_crypto_ios/ios/Classes/keys/SecretKey.swift b/packages/native_crypto_ios/ios/Classes/keys/SecretKey.swift deleted file mode 100644 index 6325143..0000000 --- a/packages/native_crypto_ios/ios/Classes/keys/SecretKey.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// SecretKey.swift -// native_crypto_ios -// -// Created by Hugo Pointcheval on 25/05/2022. -// - -import Foundation -import CryptoKit - -class SecretKey : Key { - var bytes: Data - - init(_ bytes: Data) { - self.bytes = bytes - } - - init(fromSecureRandom bitsCount: Int) { - let symmetricKey = SymmetricKey.init(size: SymmetricKeySize(bitCount: bitsCount)) - bytes = symmetricKey.withUnsafeBytes - { - return Data(Array($0)) - } - } -} diff --git a/packages/native_crypto_ios/ios/Classes/messages.g.m b/packages/native_crypto_ios/ios/Classes/messages.g.m deleted file mode 100644 index 75ff6c3..0000000 --- a/packages/native_crypto_ios/ios/Classes/messages.g.m +++ /dev/null @@ -1,868 +0,0 @@ -// Copyright 2019-2023 Hugo Pointcheval -// -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file or at -// https://opensource.org/licenses/MIT. -// -- -// Autogenerated from Pigeon (v9.0.0), do not edit directly. -// See also: https://pub.dev/packages/pigeon - -#import "messages.g.h" -#import - -#if !__has_feature(objc_arc) -#error File requires ARC to be enabled. -#endif - -static NSArray *wrapResult(id result, FlutterError *error) { - if (error) { - return @[ - error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] - ]; - } - return @[ result ?: [NSNull null] ]; -} -static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { - id result = array[key]; - return (result == [NSNull null]) ? nil : result; -} - -@interface HashRequest () -+ (HashRequest *)fromList:(NSArray *)list; -+ (nullable HashRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface HashResponse () -+ (HashResponse *)fromList:(NSArray *)list; -+ (nullable HashResponse *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface HmacRequest () -+ (HmacRequest *)fromList:(NSArray *)list; -+ (nullable HmacRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface HmacResponse () -+ (HmacResponse *)fromList:(NSArray *)list; -+ (nullable HmacResponse *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface GenerateSecureRandomRequest () -+ (GenerateSecureRandomRequest *)fromList:(NSArray *)list; -+ (nullable GenerateSecureRandomRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface GenerateSecureRandomResponse () -+ (GenerateSecureRandomResponse *)fromList:(NSArray *)list; -+ (nullable GenerateSecureRandomResponse *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface Pbkdf2Request () -+ (Pbkdf2Request *)fromList:(NSArray *)list; -+ (nullable Pbkdf2Request *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface Pbkdf2Response () -+ (Pbkdf2Response *)fromList:(NSArray *)list; -+ (nullable Pbkdf2Response *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface EncryptRequest () -+ (EncryptRequest *)fromList:(NSArray *)list; -+ (nullable EncryptRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface EncryptResponse () -+ (EncryptResponse *)fromList:(NSArray *)list; -+ (nullable EncryptResponse *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface DecryptRequest () -+ (DecryptRequest *)fromList:(NSArray *)list; -+ (nullable DecryptRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface DecryptResponse () -+ (DecryptResponse *)fromList:(NSArray *)list; -+ (nullable DecryptResponse *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface EncryptFileRequest () -+ (EncryptFileRequest *)fromList:(NSArray *)list; -+ (nullable EncryptFileRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface EncryptFileResponse () -+ (EncryptFileResponse *)fromList:(NSArray *)list; -+ (nullable EncryptFileResponse *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface DecryptFileRequest () -+ (DecryptFileRequest *)fromList:(NSArray *)list; -+ (nullable DecryptFileRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface DecryptFileResponse () -+ (DecryptFileResponse *)fromList:(NSArray *)list; -+ (nullable DecryptFileResponse *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface EncryptWithIVRequest () -+ (EncryptWithIVRequest *)fromList:(NSArray *)list; -+ (nullable EncryptWithIVRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@implementation HashRequest -+ (instancetype)makeWithData:(nullable FlutterStandardTypedData *)data - algorithm:(nullable NSString *)algorithm { - HashRequest* pigeonResult = [[HashRequest alloc] init]; - pigeonResult.data = data; - pigeonResult.algorithm = algorithm; - return pigeonResult; -} -+ (HashRequest *)fromList:(NSArray *)list { - HashRequest *pigeonResult = [[HashRequest alloc] init]; - pigeonResult.data = GetNullableObjectAtIndex(list, 0); - pigeonResult.algorithm = GetNullableObjectAtIndex(list, 1); - return pigeonResult; -} -+ (nullable HashRequest *)nullableFromList:(NSArray *)list { - return (list) ? [HashRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.data ?: [NSNull null]), - (self.algorithm ?: [NSNull null]), - ]; -} -@end - -@implementation HashResponse -+ (instancetype)makeWithHash:(nullable FlutterStandardTypedData *)hash { - HashResponse* pigeonResult = [[HashResponse alloc] init]; - pigeonResult.hash = hash; - return pigeonResult; -} -+ (HashResponse *)fromList:(NSArray *)list { - HashResponse *pigeonResult = [[HashResponse alloc] init]; - pigeonResult.hash = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable HashResponse *)nullableFromList:(NSArray *)list { - return (list) ? [HashResponse fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.hash ?: [NSNull null]), - ]; -} -@end - -@implementation HmacRequest -+ (instancetype)makeWithData:(nullable FlutterStandardTypedData *)data - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm { - HmacRequest* pigeonResult = [[HmacRequest alloc] init]; - pigeonResult.data = data; - pigeonResult.key = key; - pigeonResult.algorithm = algorithm; - return pigeonResult; -} -+ (HmacRequest *)fromList:(NSArray *)list { - HmacRequest *pigeonResult = [[HmacRequest alloc] init]; - pigeonResult.data = GetNullableObjectAtIndex(list, 0); - pigeonResult.key = GetNullableObjectAtIndex(list, 1); - pigeonResult.algorithm = GetNullableObjectAtIndex(list, 2); - return pigeonResult; -} -+ (nullable HmacRequest *)nullableFromList:(NSArray *)list { - return (list) ? [HmacRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.data ?: [NSNull null]), - (self.key ?: [NSNull null]), - (self.algorithm ?: [NSNull null]), - ]; -} -@end - -@implementation HmacResponse -+ (instancetype)makeWithHmac:(nullable FlutterStandardTypedData *)hmac { - HmacResponse* pigeonResult = [[HmacResponse alloc] init]; - pigeonResult.hmac = hmac; - return pigeonResult; -} -+ (HmacResponse *)fromList:(NSArray *)list { - HmacResponse *pigeonResult = [[HmacResponse alloc] init]; - pigeonResult.hmac = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable HmacResponse *)nullableFromList:(NSArray *)list { - return (list) ? [HmacResponse fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.hmac ?: [NSNull null]), - ]; -} -@end - -@implementation GenerateSecureRandomRequest -+ (instancetype)makeWithLength:(nullable NSNumber *)length { - GenerateSecureRandomRequest* pigeonResult = [[GenerateSecureRandomRequest alloc] init]; - pigeonResult.length = length; - return pigeonResult; -} -+ (GenerateSecureRandomRequest *)fromList:(NSArray *)list { - GenerateSecureRandomRequest *pigeonResult = [[GenerateSecureRandomRequest alloc] init]; - pigeonResult.length = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable GenerateSecureRandomRequest *)nullableFromList:(NSArray *)list { - return (list) ? [GenerateSecureRandomRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.length ?: [NSNull null]), - ]; -} -@end - -@implementation GenerateSecureRandomResponse -+ (instancetype)makeWithRandom:(nullable FlutterStandardTypedData *)random { - GenerateSecureRandomResponse* pigeonResult = [[GenerateSecureRandomResponse alloc] init]; - pigeonResult.random = random; - return pigeonResult; -} -+ (GenerateSecureRandomResponse *)fromList:(NSArray *)list { - GenerateSecureRandomResponse *pigeonResult = [[GenerateSecureRandomResponse alloc] init]; - pigeonResult.random = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable GenerateSecureRandomResponse *)nullableFromList:(NSArray *)list { - return (list) ? [GenerateSecureRandomResponse fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.random ?: [NSNull null]), - ]; -} -@end - -@implementation Pbkdf2Request -+ (instancetype)makeWithPassword:(nullable FlutterStandardTypedData *)password - salt:(nullable FlutterStandardTypedData *)salt - length:(nullable NSNumber *)length - iterations:(nullable NSNumber *)iterations - hashAlgorithm:(nullable NSString *)hashAlgorithm { - Pbkdf2Request* pigeonResult = [[Pbkdf2Request alloc] init]; - pigeonResult.password = password; - pigeonResult.salt = salt; - pigeonResult.length = length; - pigeonResult.iterations = iterations; - pigeonResult.hashAlgorithm = hashAlgorithm; - return pigeonResult; -} -+ (Pbkdf2Request *)fromList:(NSArray *)list { - Pbkdf2Request *pigeonResult = [[Pbkdf2Request alloc] init]; - pigeonResult.password = GetNullableObjectAtIndex(list, 0); - pigeonResult.salt = GetNullableObjectAtIndex(list, 1); - pigeonResult.length = GetNullableObjectAtIndex(list, 2); - pigeonResult.iterations = GetNullableObjectAtIndex(list, 3); - pigeonResult.hashAlgorithm = GetNullableObjectAtIndex(list, 4); - return pigeonResult; -} -+ (nullable Pbkdf2Request *)nullableFromList:(NSArray *)list { - return (list) ? [Pbkdf2Request fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.password ?: [NSNull null]), - (self.salt ?: [NSNull null]), - (self.length ?: [NSNull null]), - (self.iterations ?: [NSNull null]), - (self.hashAlgorithm ?: [NSNull null]), - ]; -} -@end - -@implementation Pbkdf2Response -+ (instancetype)makeWithKey:(nullable FlutterStandardTypedData *)key { - Pbkdf2Response* pigeonResult = [[Pbkdf2Response alloc] init]; - pigeonResult.key = key; - return pigeonResult; -} -+ (Pbkdf2Response *)fromList:(NSArray *)list { - Pbkdf2Response *pigeonResult = [[Pbkdf2Response alloc] init]; - pigeonResult.key = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable Pbkdf2Response *)nullableFromList:(NSArray *)list { - return (list) ? [Pbkdf2Response fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.key ?: [NSNull null]), - ]; -} -@end - -@implementation EncryptRequest -+ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm { - EncryptRequest* pigeonResult = [[EncryptRequest alloc] init]; - pigeonResult.plainText = plainText; - pigeonResult.key = key; - pigeonResult.algorithm = algorithm; - return pigeonResult; -} -+ (EncryptRequest *)fromList:(NSArray *)list { - EncryptRequest *pigeonResult = [[EncryptRequest alloc] init]; - pigeonResult.plainText = GetNullableObjectAtIndex(list, 0); - pigeonResult.key = GetNullableObjectAtIndex(list, 1); - pigeonResult.algorithm = GetNullableObjectAtIndex(list, 2); - return pigeonResult; -} -+ (nullable EncryptRequest *)nullableFromList:(NSArray *)list { - return (list) ? [EncryptRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.plainText ?: [NSNull null]), - (self.key ?: [NSNull null]), - (self.algorithm ?: [NSNull null]), - ]; -} -@end - -@implementation EncryptResponse -+ (instancetype)makeWithCipherText:(nullable FlutterStandardTypedData *)cipherText { - EncryptResponse* pigeonResult = [[EncryptResponse alloc] init]; - pigeonResult.cipherText = cipherText; - return pigeonResult; -} -+ (EncryptResponse *)fromList:(NSArray *)list { - EncryptResponse *pigeonResult = [[EncryptResponse alloc] init]; - pigeonResult.cipherText = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable EncryptResponse *)nullableFromList:(NSArray *)list { - return (list) ? [EncryptResponse fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.cipherText ?: [NSNull null]), - ]; -} -@end - -@implementation DecryptRequest -+ (instancetype)makeWithCipherText:(nullable FlutterStandardTypedData *)cipherText - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm { - DecryptRequest* pigeonResult = [[DecryptRequest alloc] init]; - pigeonResult.cipherText = cipherText; - pigeonResult.key = key; - pigeonResult.algorithm = algorithm; - return pigeonResult; -} -+ (DecryptRequest *)fromList:(NSArray *)list { - DecryptRequest *pigeonResult = [[DecryptRequest alloc] init]; - pigeonResult.cipherText = GetNullableObjectAtIndex(list, 0); - pigeonResult.key = GetNullableObjectAtIndex(list, 1); - pigeonResult.algorithm = GetNullableObjectAtIndex(list, 2); - return pigeonResult; -} -+ (nullable DecryptRequest *)nullableFromList:(NSArray *)list { - return (list) ? [DecryptRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.cipherText ?: [NSNull null]), - (self.key ?: [NSNull null]), - (self.algorithm ?: [NSNull null]), - ]; -} -@end - -@implementation DecryptResponse -+ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText { - DecryptResponse* pigeonResult = [[DecryptResponse alloc] init]; - pigeonResult.plainText = plainText; - return pigeonResult; -} -+ (DecryptResponse *)fromList:(NSArray *)list { - DecryptResponse *pigeonResult = [[DecryptResponse alloc] init]; - pigeonResult.plainText = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable DecryptResponse *)nullableFromList:(NSArray *)list { - return (list) ? [DecryptResponse fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.plainText ?: [NSNull null]), - ]; -} -@end - -@implementation EncryptFileRequest -+ (instancetype)makeWithPlainTextPath:(nullable NSString *)plainTextPath - cipherTextPath:(nullable NSString *)cipherTextPath - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm { - EncryptFileRequest* pigeonResult = [[EncryptFileRequest alloc] init]; - pigeonResult.plainTextPath = plainTextPath; - pigeonResult.cipherTextPath = cipherTextPath; - pigeonResult.key = key; - pigeonResult.algorithm = algorithm; - return pigeonResult; -} -+ (EncryptFileRequest *)fromList:(NSArray *)list { - EncryptFileRequest *pigeonResult = [[EncryptFileRequest alloc] init]; - pigeonResult.plainTextPath = GetNullableObjectAtIndex(list, 0); - pigeonResult.cipherTextPath = GetNullableObjectAtIndex(list, 1); - pigeonResult.key = GetNullableObjectAtIndex(list, 2); - pigeonResult.algorithm = GetNullableObjectAtIndex(list, 3); - return pigeonResult; -} -+ (nullable EncryptFileRequest *)nullableFromList:(NSArray *)list { - return (list) ? [EncryptFileRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.plainTextPath ?: [NSNull null]), - (self.cipherTextPath ?: [NSNull null]), - (self.key ?: [NSNull null]), - (self.algorithm ?: [NSNull null]), - ]; -} -@end - -@implementation EncryptFileResponse -+ (instancetype)makeWithSuccess:(nullable NSNumber *)success { - EncryptFileResponse* pigeonResult = [[EncryptFileResponse alloc] init]; - pigeonResult.success = success; - return pigeonResult; -} -+ (EncryptFileResponse *)fromList:(NSArray *)list { - EncryptFileResponse *pigeonResult = [[EncryptFileResponse alloc] init]; - pigeonResult.success = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable EncryptFileResponse *)nullableFromList:(NSArray *)list { - return (list) ? [EncryptFileResponse fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.success ?: [NSNull null]), - ]; -} -@end - -@implementation DecryptFileRequest -+ (instancetype)makeWithCipherTextPath:(nullable NSString *)cipherTextPath - plainTextPath:(nullable NSString *)plainTextPath - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm { - DecryptFileRequest* pigeonResult = [[DecryptFileRequest alloc] init]; - pigeonResult.cipherTextPath = cipherTextPath; - pigeonResult.plainTextPath = plainTextPath; - pigeonResult.key = key; - pigeonResult.algorithm = algorithm; - return pigeonResult; -} -+ (DecryptFileRequest *)fromList:(NSArray *)list { - DecryptFileRequest *pigeonResult = [[DecryptFileRequest alloc] init]; - pigeonResult.cipherTextPath = GetNullableObjectAtIndex(list, 0); - pigeonResult.plainTextPath = GetNullableObjectAtIndex(list, 1); - pigeonResult.key = GetNullableObjectAtIndex(list, 2); - pigeonResult.algorithm = GetNullableObjectAtIndex(list, 3); - return pigeonResult; -} -+ (nullable DecryptFileRequest *)nullableFromList:(NSArray *)list { - return (list) ? [DecryptFileRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.cipherTextPath ?: [NSNull null]), - (self.plainTextPath ?: [NSNull null]), - (self.key ?: [NSNull null]), - (self.algorithm ?: [NSNull null]), - ]; -} -@end - -@implementation DecryptFileResponse -+ (instancetype)makeWithSuccess:(nullable NSNumber *)success { - DecryptFileResponse* pigeonResult = [[DecryptFileResponse alloc] init]; - pigeonResult.success = success; - return pigeonResult; -} -+ (DecryptFileResponse *)fromList:(NSArray *)list { - DecryptFileResponse *pigeonResult = [[DecryptFileResponse alloc] init]; - pigeonResult.success = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable DecryptFileResponse *)nullableFromList:(NSArray *)list { - return (list) ? [DecryptFileResponse fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.success ?: [NSNull null]), - ]; -} -@end - -@implementation EncryptWithIVRequest -+ (instancetype)makeWithPlainText:(nullable FlutterStandardTypedData *)plainText - iv:(nullable FlutterStandardTypedData *)iv - key:(nullable FlutterStandardTypedData *)key - algorithm:(nullable NSString *)algorithm { - EncryptWithIVRequest* pigeonResult = [[EncryptWithIVRequest alloc] init]; - pigeonResult.plainText = plainText; - pigeonResult.iv = iv; - pigeonResult.key = key; - pigeonResult.algorithm = algorithm; - return pigeonResult; -} -+ (EncryptWithIVRequest *)fromList:(NSArray *)list { - EncryptWithIVRequest *pigeonResult = [[EncryptWithIVRequest alloc] init]; - pigeonResult.plainText = GetNullableObjectAtIndex(list, 0); - pigeonResult.iv = GetNullableObjectAtIndex(list, 1); - pigeonResult.key = GetNullableObjectAtIndex(list, 2); - pigeonResult.algorithm = GetNullableObjectAtIndex(list, 3); - return pigeonResult; -} -+ (nullable EncryptWithIVRequest *)nullableFromList:(NSArray *)list { - return (list) ? [EncryptWithIVRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - (self.plainText ?: [NSNull null]), - (self.iv ?: [NSNull null]), - (self.key ?: [NSNull null]), - (self.algorithm ?: [NSNull null]), - ]; -} -@end - -@interface NativeCryptoAPICodecReader : FlutterStandardReader -@end -@implementation NativeCryptoAPICodecReader -- (nullable id)readValueOfType:(UInt8)type { - switch (type) { - case 128: - return [DecryptFileRequest fromList:[self readValue]]; - case 129: - return [DecryptFileResponse fromList:[self readValue]]; - case 130: - return [DecryptRequest fromList:[self readValue]]; - case 131: - return [DecryptResponse fromList:[self readValue]]; - case 132: - return [EncryptFileRequest fromList:[self readValue]]; - case 133: - return [EncryptFileResponse fromList:[self readValue]]; - case 134: - return [EncryptRequest fromList:[self readValue]]; - case 135: - return [EncryptResponse fromList:[self readValue]]; - case 136: - return [EncryptWithIVRequest fromList:[self readValue]]; - case 137: - return [GenerateSecureRandomRequest fromList:[self readValue]]; - case 138: - return [GenerateSecureRandomResponse fromList:[self readValue]]; - case 139: - return [HashRequest fromList:[self readValue]]; - case 140: - return [HashResponse fromList:[self readValue]]; - case 141: - return [HmacRequest fromList:[self readValue]]; - case 142: - return [HmacResponse fromList:[self readValue]]; - case 143: - return [Pbkdf2Request fromList:[self readValue]]; - case 144: - return [Pbkdf2Response fromList:[self readValue]]; - default: - return [super readValueOfType:type]; - } -} -@end - -@interface NativeCryptoAPICodecWriter : FlutterStandardWriter -@end -@implementation NativeCryptoAPICodecWriter -- (void)writeValue:(id)value { - if ([value isKindOfClass:[DecryptFileRequest class]]) { - [self writeByte:128]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[DecryptFileResponse class]]) { - [self writeByte:129]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[DecryptRequest class]]) { - [self writeByte:130]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[DecryptResponse class]]) { - [self writeByte:131]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[EncryptFileRequest class]]) { - [self writeByte:132]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[EncryptFileResponse class]]) { - [self writeByte:133]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[EncryptRequest class]]) { - [self writeByte:134]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[EncryptResponse class]]) { - [self writeByte:135]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[EncryptWithIVRequest class]]) { - [self writeByte:136]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[GenerateSecureRandomRequest class]]) { - [self writeByte:137]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[GenerateSecureRandomResponse class]]) { - [self writeByte:138]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[HashRequest class]]) { - [self writeByte:139]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[HashResponse class]]) { - [self writeByte:140]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[HmacRequest class]]) { - [self writeByte:141]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[HmacResponse class]]) { - [self writeByte:142]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[Pbkdf2Request class]]) { - [self writeByte:143]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[Pbkdf2Response class]]) { - [self writeByte:144]; - [self writeValue:[value toList]]; - } else { - [super writeValue:value]; - } -} -@end - -@interface NativeCryptoAPICodecReaderWriter : FlutterStandardReaderWriter -@end -@implementation NativeCryptoAPICodecReaderWriter -- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[NativeCryptoAPICodecWriter alloc] initWithData:data]; -} -- (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[NativeCryptoAPICodecReader alloc] initWithData:data]; -} -@end - -NSObject *NativeCryptoAPIGetCodec() { - static FlutterStandardMessageCodec *sSharedObject = nil; - static dispatch_once_t sPred = 0; - dispatch_once(&sPred, ^{ - NativeCryptoAPICodecReaderWriter *readerWriter = [[NativeCryptoAPICodecReaderWriter alloc] init]; - sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; - }); - return sSharedObject; -} - -void NativeCryptoAPISetup(id binaryMessenger, NSObject *api) { - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.hash" - binaryMessenger:binaryMessenger - codec:NativeCryptoAPIGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(hashRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(hashRequest:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - HashRequest *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - HashResponse *output = [api hashRequest:arg_request error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.hmac" - binaryMessenger:binaryMessenger - codec:NativeCryptoAPIGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(hmacRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(hmacRequest:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - HmacRequest *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - HmacResponse *output = [api hmacRequest:arg_request error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom" - binaryMessenger:binaryMessenger - codec:NativeCryptoAPIGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(generateSecureRandomRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(generateSecureRandomRequest:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - GenerateSecureRandomRequest *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - GenerateSecureRandomResponse *output = [api generateSecureRandomRequest:arg_request error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.pbkdf2" - binaryMessenger:binaryMessenger - codec:NativeCryptoAPIGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(pbkdf2Request:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(pbkdf2Request:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - Pbkdf2Request *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - Pbkdf2Response *output = [api pbkdf2Request:arg_request error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.encrypt" - binaryMessenger:binaryMessenger - codec:NativeCryptoAPIGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(encryptRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(encryptRequest:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - EncryptRequest *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - EncryptResponse *output = [api encryptRequest:arg_request error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.decrypt" - binaryMessenger:binaryMessenger - codec:NativeCryptoAPIGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(decryptRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(decryptRequest:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - DecryptRequest *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - DecryptResponse *output = [api decryptRequest:arg_request error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.encryptFile" - binaryMessenger:binaryMessenger - codec:NativeCryptoAPIGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(encryptFileRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(encryptFileRequest:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - EncryptFileRequest *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - EncryptFileResponse *output = [api encryptFileRequest:arg_request error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.decryptFile" - binaryMessenger:binaryMessenger - codec:NativeCryptoAPIGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(decryptFileRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(decryptFileRequest:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - DecryptFileRequest *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - DecryptFileResponse *output = [api decryptFileRequest:arg_request error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:@"dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV" - binaryMessenger:binaryMessenger - codec:NativeCryptoAPIGetCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(encryptWithIVRequest:error:)], @"NativeCryptoAPI api (%@) doesn't respond to @selector(encryptWithIVRequest:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - EncryptWithIVRequest *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - EncryptResponse *output = [api encryptWithIVRequest:arg_request error:&error]; - callback(wrapResult(output, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } -} diff --git a/packages/native_crypto_ios/ios/Classes/messages.g.swift b/packages/native_crypto_ios/ios/Classes/messages.g.swift new file mode 100644 index 0000000..f9c5a5d --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/messages.g.swift @@ -0,0 +1,243 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. +// -- +// Autogenerated from Pigeon (v9.2.0), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation +#if os(iOS) +import Flutter +#elseif os(macOS) +import FlutterMacOS +#else +#error("Unsupported platform.") +#endif + + + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)" + ] +} + +enum HashAlgorithm: Int { + case sha256 = 0 + case sha384 = 1 + case sha512 = 2 +} + +enum CipherAlgorithm: Int { + case aes = 0 +} +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol NativeCryptoAPI { + func hash(data: FlutterStandardTypedData, algorithm: HashAlgorithm) throws -> FlutterStandardTypedData? + func hmac(data: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: HashAlgorithm) throws -> FlutterStandardTypedData? + func generateSecureRandom(length: Int64) throws -> FlutterStandardTypedData? + func pbkdf2(password: FlutterStandardTypedData, salt: FlutterStandardTypedData, length: Int64, iterations: Int64, algorithm: HashAlgorithm) throws -> FlutterStandardTypedData? + func encrypt(plainText: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> FlutterStandardTypedData? + func encryptWithIV(plainText: FlutterStandardTypedData, iv: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> FlutterStandardTypedData? + func decrypt(cipherText: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> FlutterStandardTypedData? + func encryptFile(plainTextPath: String, cipherTextPath: String, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> Bool? + func encryptFileWithIV(plainTextPath: String, cipherTextPath: String, iv: FlutterStandardTypedData, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> Bool? + func decryptFile(cipherTextPath: String, plainTextPath: String, key: FlutterStandardTypedData, algorithm: CipherAlgorithm) throws -> Bool? +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class NativeCryptoAPISetup { + /// The codec used by NativeCryptoAPI. + /// Sets up an instance of `NativeCryptoAPI` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: NativeCryptoAPI?) { + let hashChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.hash", binaryMessenger: binaryMessenger) + if let api = api { + hashChannel.setMessageHandler { message, reply in + let args = message as! [Any] + let dataArg = args[0] as! FlutterStandardTypedData + let algorithmArg = HashAlgorithm(rawValue: args[1] as! Int)! + do { + let result = try api.hash(data: dataArg, algorithm: algorithmArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + hashChannel.setMessageHandler(nil) + } + let hmacChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.hmac", binaryMessenger: binaryMessenger) + if let api = api { + hmacChannel.setMessageHandler { message, reply in + let args = message as! [Any] + let dataArg = args[0] as! FlutterStandardTypedData + let keyArg = args[1] as! FlutterStandardTypedData + let algorithmArg = HashAlgorithm(rawValue: args[2] as! Int)! + do { + let result = try api.hmac(data: dataArg, key: keyArg, algorithm: algorithmArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + hmacChannel.setMessageHandler(nil) + } + let generateSecureRandomChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.generateSecureRandom", binaryMessenger: binaryMessenger) + if let api = api { + generateSecureRandomChannel.setMessageHandler { message, reply in + let args = message as! [Any] + let lengthArg = (args[0] is Int) ? Int64(args[0] as! Int) : args[0] as! Int64 + do { + let result = try api.generateSecureRandom(length: lengthArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + generateSecureRandomChannel.setMessageHandler(nil) + } + let pbkdf2Channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.pbkdf2", binaryMessenger: binaryMessenger) + if let api = api { + pbkdf2Channel.setMessageHandler { message, reply in + let args = message as! [Any] + let passwordArg = args[0] as! FlutterStandardTypedData + let saltArg = args[1] as! FlutterStandardTypedData + let lengthArg = (args[2] is Int) ? Int64(args[2] as! Int) : args[2] as! Int64 + let iterationsArg = (args[3] is Int) ? Int64(args[3] as! Int) : args[3] as! Int64 + let algorithmArg = HashAlgorithm(rawValue: args[4] as! Int)! + do { + let result = try api.pbkdf2(password: passwordArg, salt: saltArg, length: lengthArg, iterations: iterationsArg, algorithm: algorithmArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + pbkdf2Channel.setMessageHandler(nil) + } + let encryptChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.encrypt", binaryMessenger: binaryMessenger) + if let api = api { + encryptChannel.setMessageHandler { message, reply in + let args = message as! [Any] + let plainTextArg = args[0] as! FlutterStandardTypedData + let keyArg = args[1] as! FlutterStandardTypedData + let algorithmArg = CipherAlgorithm(rawValue: args[2] as! Int)! + do { + let result = try api.encrypt(plainText: plainTextArg, key: keyArg, algorithm: algorithmArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + encryptChannel.setMessageHandler(nil) + } + let encryptWithIVChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.encryptWithIV", binaryMessenger: binaryMessenger) + if let api = api { + encryptWithIVChannel.setMessageHandler { message, reply in + let args = message as! [Any] + let plainTextArg = args[0] as! FlutterStandardTypedData + let ivArg = args[1] as! FlutterStandardTypedData + let keyArg = args[2] as! FlutterStandardTypedData + let algorithmArg = CipherAlgorithm(rawValue: args[3] as! Int)! + do { + let result = try api.encryptWithIV(plainText: plainTextArg, iv: ivArg, key: keyArg, algorithm: algorithmArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + encryptWithIVChannel.setMessageHandler(nil) + } + let decryptChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.decrypt", binaryMessenger: binaryMessenger) + if let api = api { + decryptChannel.setMessageHandler { message, reply in + let args = message as! [Any] + let cipherTextArg = args[0] as! FlutterStandardTypedData + let keyArg = args[1] as! FlutterStandardTypedData + let algorithmArg = CipherAlgorithm(rawValue: args[2] as! Int)! + do { + let result = try api.decrypt(cipherText: cipherTextArg, key: keyArg, algorithm: algorithmArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + decryptChannel.setMessageHandler(nil) + } + let encryptFileChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.encryptFile", binaryMessenger: binaryMessenger) + if let api = api { + encryptFileChannel.setMessageHandler { message, reply in + let args = message as! [Any] + let plainTextPathArg = args[0] as! String + let cipherTextPathArg = args[1] as! String + let keyArg = args[2] as! FlutterStandardTypedData + let algorithmArg = CipherAlgorithm(rawValue: args[3] as! Int)! + do { + let result = try api.encryptFile(plainTextPath: plainTextPathArg, cipherTextPath: cipherTextPathArg, key: keyArg, algorithm: algorithmArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + encryptFileChannel.setMessageHandler(nil) + } + let encryptFileWithIVChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.encryptFileWithIV", binaryMessenger: binaryMessenger) + if let api = api { + encryptFileWithIVChannel.setMessageHandler { message, reply in + let args = message as! [Any] + let plainTextPathArg = args[0] as! String + let cipherTextPathArg = args[1] as! String + let ivArg = args[2] as! FlutterStandardTypedData + let keyArg = args[3] as! FlutterStandardTypedData + let algorithmArg = CipherAlgorithm(rawValue: args[4] as! Int)! + do { + let result = try api.encryptFileWithIV(plainTextPath: plainTextPathArg, cipherTextPath: cipherTextPathArg, iv: ivArg, key: keyArg, algorithm: algorithmArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + encryptFileWithIVChannel.setMessageHandler(nil) + } + let decryptFileChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.NativeCryptoAPI.decryptFile", binaryMessenger: binaryMessenger) + if let api = api { + decryptFileChannel.setMessageHandler { message, reply in + let args = message as! [Any] + let cipherTextPathArg = args[0] as! String + let plainTextPathArg = args[1] as! String + let keyArg = args[2] as! FlutterStandardTypedData + let algorithmArg = CipherAlgorithm(rawValue: args[3] as! Int)! + do { + let result = try api.decryptFile(cipherTextPath: cipherTextPathArg, plainTextPath: plainTextPathArg, key: keyArg, algorithm: algorithmArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + decryptFileChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/native_crypto_ios/ios/Classes/protocols/Cipher.swift b/packages/native_crypto_ios/ios/Classes/protocols/Cipher.swift index f1291f9..024c5f8 100644 --- a/packages/native_crypto_ios/ios/Classes/protocols/Cipher.swift +++ b/packages/native_crypto_ios/ios/Classes/protocols/Cipher.swift @@ -8,9 +8,8 @@ import Foundation protocol Cipher { - var algorithm: CipherAlgorithm { get } - func encrypt(data: Data, key: Data) throws -> Data + func encrypt(data: Data, key: Data, predefinedIV: Data?) throws -> Data func decrypt(data: Data, key: Data) throws -> Data - func encryptAsList(data: Data, key: Data) throws -> [Data] - func decryptAsList(data: [Data], key: Data) throws-> Data + func encryptFile(fileParameters: FileParameters, key: Data, predefinedIV: Data?) throws -> Bool + func decryptFile(fileParameters: FileParameters, key: Data) throws -> Bool } diff --git a/packages/native_crypto_ios/ios/Classes/protocols/Key.swift b/packages/native_crypto_ios/ios/Classes/protocols/Key.swift deleted file mode 100644 index 9fc9199..0000000 --- a/packages/native_crypto_ios/ios/Classes/protocols/Key.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Key.swift -// native_crypto_ios -// -// Created by Hugo Pointcheval on 25/05/2022. -// - -import Foundation - -protocol Key { - var bytes: Data { get set } -} diff --git a/packages/native_crypto_ios/ios/Classes/protocols/KeyDerivation.swift b/packages/native_crypto_ios/ios/Classes/protocols/KeyDerivation.swift index df3a0d5..f5ff3fb 100644 --- a/packages/native_crypto_ios/ios/Classes/protocols/KeyDerivation.swift +++ b/packages/native_crypto_ios/ios/Classes/protocols/KeyDerivation.swift @@ -8,6 +8,5 @@ import Foundation protocol KeyDerivation { - var algorithm : KdfAlgorithm { get } - func derive() throws -> SecretKey + func derive() throws -> Data? } diff --git a/packages/native_crypto_ios/ios/Classes/utils/CipherAlgorithm.swift b/packages/native_crypto_ios/ios/Classes/utils/CipherAlgorithm.swift deleted file mode 100644 index afb9094..0000000 --- a/packages/native_crypto_ios/ios/Classes/utils/CipherAlgorithm.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// CipherAlgorithm.swift -// native_crypto_ios -// -// Created by Hugo Pointcheval on 25/05/2022. -// - -import Foundation - -enum CipherAlgorithm : String { - case aes = "aes" - - var getCipher: Cipher { - switch self { - case .aes: return AESCipher() - } - } -} diff --git a/packages/native_crypto_ios/ios/Classes/utils/FileParameters.swift b/packages/native_crypto_ios/ios/Classes/utils/FileParameters.swift index efbc0ae..dbdbf29 100644 --- a/packages/native_crypto_ios/ios/Classes/utils/FileParameters.swift +++ b/packages/native_crypto_ios/ios/Classes/utils/FileParameters.swift @@ -6,3 +6,15 @@ // import Foundation + +class FileParameters { + var inputPath: String + var outputPath: String + + init(input: String, output: String) { + self.inputPath = input + self.outputPath = output + } + + +} diff --git a/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithm.swift b/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithm.swift deleted file mode 100644 index 38b7740..0000000 --- a/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithm.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// HashAlgorithm.swift -// native_crypto_ios -// -// Created by Hugo Pointcheval on 25/05/2022. -// - -import Foundation -import CommonCrypto -import CryptoKit - -enum HashAlgorithm: String { - case HashSHA256 = "sha256" - case HashSHA384 = "sha384" - case HashSHA512 = "sha512" - - var pbkdf2identifier: UInt32 { - switch self { - case .HashSHA256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) - case .HashSHA384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384) - case .HashSHA512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) - } - } - - @available(iOS 13.0, *) - func digest(data: Data) -> Data { - switch self { - case .HashSHA256: - return Data(SHA256.hash(data: data)) - case .HashSHA384: - return Data(SHA384.hash(data: data)) - case .HashSHA512: - return Data(SHA512.hash(data: data)) - } - } - - @available(iOS 13.0, *) - static func digest(data: Data, algorithm: String) throws -> Data { - let algo = HashAlgorithm.init(rawValue: algorithm) - if (algo == nil) { - throw NativeCryptoError.messageDigestError - } - return algo!.digest(data: data) - } -} diff --git a/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithmParser.swift b/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithmParser.swift new file mode 100644 index 0000000..52e160e --- /dev/null +++ b/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithmParser.swift @@ -0,0 +1,31 @@ +// +// HashAlgorithmParser.swift +// native_crypto_ios +// +// Created by Hugo Pointcheval on 04/04/2023. +// + +import Foundation +import CommonCrypto +import CryptoKit + +public class HashAlgorithmParser { + static func getMessageDigest(algorithm: HashAlgorithm) -> any HashFunction { + switch algorithm { + case .sha256: return SHA256.init() + case .sha384: return SHA384.init() + case .sha512: return SHA512.init() + @unknown default: fatalError("Unknown algorithm") + } + } + + static func getPbkdf2Identifier(algorithm: HashAlgorithm) -> UInt32 { + switch algorithm { + case .sha256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) + case .sha384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384) + case .sha512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) + @unknown default: fatalError("Unknown algorithm") + } + } + +} diff --git a/packages/native_crypto_ios/ios/Classes/utils/KdfAlgorithm.swift b/packages/native_crypto_ios/ios/Classes/utils/KdfAlgorithm.swift deleted file mode 100644 index d6af3c1..0000000 --- a/packages/native_crypto_ios/ios/Classes/utils/KdfAlgorithm.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// KdfAlgorithm.swift -// native_crypto_ios -// -// Created by Hugo Pointcheval on 25/05/2022. -// - -import Foundation - -enum KdfAlgorithm { - case pbkdf2 -} diff --git a/packages/native_crypto_ios/ios/Classes/utils/NativeCryptoError.swift b/packages/native_crypto_ios/ios/Classes/utils/NativeCryptoError.swift index a2ac159..0a10b00 100644 --- a/packages/native_crypto_ios/ios/Classes/utils/NativeCryptoError.swift +++ b/packages/native_crypto_ios/ios/Classes/utils/NativeCryptoError.swift @@ -7,12 +7,51 @@ import Foundation -enum NativeCryptoError : Error { -case decryptionError -case encryptionError -case messageDigestError -case pbkdf2Error -case cipherError -case resultError -case exceptionError +enum NativeCryptoError : LocalizedError { + /// When an operation is not supported + case notSupported(reason: String? = nil) + /// When the key is invalid + case invalidKey(reason: String? = nil) + /// When the key length is invalid + case invalidKeySize(reason: String? = nil) + /// When operation parameter is invalid + case invalidParameter(reason: String? = nil) + /// When an authentication process fails + case authenticationError(reason: String? = nil) + /// When a input/output process (like file manipulation) fails + case ioError(reason: String? = nil) + /// When cipher initialization fails + case cipherError(reason: String? = nil) + /// When a digest process fails + case digestError(reason: String? = nil) + /// When a key derivation process fails + case kdfError(reason: String? = nil) + /// When any other expection is thrown + case unknownError(reason: String? = nil) + + var errorDescription: String? { + switch self { + case .notSupported(reason: let reason): + return reason ?? "This operation is not supported." + case .invalidKey(reason: let reason): + return reason ?? "Invalid key." + case .invalidKeySize(reason: let reason): + return reason ?? "Invalid key size." + case .invalidParameter(reason: let reason): + return reason ?? "Invalid parameter." + case .authenticationError(reason: let reason): + return reason ?? "Authentication fails." + case .ioError(reason: let reason): + return reason ?? "IO operation fails." + case .cipherError(reason: let reason): + return reason ?? "Cipher initialization fails." + case .digestError(reason: let reason): + return reason ?? "Digest fails." + case .kdfError(reason: let reason): + return reason ?? "Key derivation fails." + case .unknownError(reason: let reason): + return reason ?? "An unknown error occurred during the operation." + } + } + } diff --git a/packages/native_crypto_ios/ios/Classes/utils/Task.swift b/packages/native_crypto_ios/ios/Classes/utils/Task.swift index 4d11070..4a9d049 100644 --- a/packages/native_crypto_ios/ios/Classes/utils/Task.swift +++ b/packages/native_crypto_ios/ios/Classes/utils/Task.swift @@ -1,10 +1,3 @@ -// -// Task.swift -// native_crypto_ios -// -// Created by Hugo Pointcheval on 25/05/2022. -// - import Foundation class Task { @@ -30,7 +23,7 @@ class Task { if (exception != nil) { return exception! } else { - return NativeCryptoError.exceptionError + return NativeCryptoError.unknownError() } } -- 2.47.2 From 5be6296829b3052fab182d79b5c87eb3fb79be67 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Tue, 4 Apr 2023 23:21:39 +0200 Subject: [PATCH 12/21] fix(ios): key length in bits --- packages/native_crypto_ios/ios/Classes/NativeCrypto.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift b/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift index ca8fa2c..d9e8dc1 100644 --- a/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift +++ b/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift @@ -31,7 +31,8 @@ public class NativeCrypto: NSObject, NativeCryptoAPI { func generateSecureRandom(length: Int64) throws -> FlutterStandardTypedData? { let lengthInt = Int(truncatingIfNeeded: length) - let symmetricKey = SymmetricKey.init(size: SymmetricKeySize(bitCount: lengthInt / 8)) + let bitCount = lengthInt * 8 + let symmetricKey = SymmetricKey.init(size: SymmetricKeySize(bitCount: bitCount)) let bytes = symmetricKey.withUnsafeBytes { return Data(Array($0)) -- 2.47.2 From 2f22cc549d9b6756ceae4f0ba80938b231c0931b Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Tue, 4 Apr 2023 23:22:43 +0200 Subject: [PATCH 13/21] fix(api): accept empty decrypted plaintext --- packages/native_crypto/lib/src/ciphers/aes/aes.dart | 7 ------- .../native_crypto/lib/src/core/constants/constants.dart | 5 +++++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes.dart b/packages/native_crypto/lib/src/ciphers/aes/aes.dart index 8eda12e..9d995fe 100644 --- a/packages/native_crypto/lib/src/ciphers/aes/aes.dart +++ b/packages/native_crypto/lib/src/ciphers/aes/aes.dart @@ -288,13 +288,6 @@ class AES implements Cipher { ); } - if (bytes.isEmpty) { - throw NativeCryptoException( - code: NativeCryptoExceptionCode.invalidData, - message: 'Platform returned no data on chunk #$count', - ); - } - return bytes; } } diff --git a/packages/native_crypto/lib/src/core/constants/constants.dart b/packages/native_crypto/lib/src/core/constants/constants.dart index c9b13ae..97b111c 100644 --- a/packages/native_crypto/lib/src/core/constants/constants.dart +++ b/packages/native_crypto/lib/src/core/constants/constants.dart @@ -6,8 +6,13 @@ abstract class Constants { /// The default chunk size in bytes used for encryption and decryption. + /// + /// ~32MB static const int defaultChunkSize = 33554432; + /// The length of the initialization vector in bytes used for AES GCM. static const int aesGcmNonceLength = 12; + + /// The length of the tag in bytes used for AES GCM. static const int aesGcmTagLength = 16; } -- 2.47.2 From e47004e2d067ef1bca00e6755e47f917521dd107 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Tue, 4 Apr 2023 23:23:47 +0200 Subject: [PATCH 14/21] feat(api): update example --- packages/native_crypto/example/.metadata | 24 +- .../example/analysis_options.yaml | 30 +-- .../example/android/app/build.gradle | 1 + .../android/app/src/debug/AndroidManifest.xml | 3 +- .../android/app/src/main/AndroidManifest.xml | 3 +- .../app/src/profile/AndroidManifest.xml | 3 +- .../example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- .../ios/Flutter/AppFrameworkInfo.plist | 2 +- .../native_crypto/example/ios/Podfile.lock | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 10 +- .../example/ios/Runner/Info.plist | 2 + .../example/lib/core/get_it.dart | 35 +++ .../example/lib/core/typography.dart | 18 ++ .../data_sources/logger_data_source_impl.dart | 34 +++ .../native_crypto_data_source_impl.dart | 104 ++++++++ .../pointy_castle_data_source_impl.dart | 160 ++++++++++++ .../session_data_source_impl.dart | 45 ++++ .../repositories/crypto_repository_impl.dart | 111 +++++++++ .../repositories/logger_repository_impl.dart | 41 +++ .../repositories/session_repository_impl.dart | 49 ++++ .../data_sources/crypto_data_source.dart | 26 ++ .../data_sources/logger_data_source.dart | 15 ++ .../data_sources/session_data_source.dart | 17 ++ .../lib/domain/entities/log_message.dart | 27 ++ .../example/lib/domain/entities/mode.dart | 34 +++ .../example/lib/domain/entities/states.dart | 7 + .../lib/domain/entities/test_vector.dart | 54 ++++ .../repositories/crypto_repository.dart | 27 ++ .../repositories/logger_repository.dart | 18 ++ .../repositories/session_repository.dart | 20 ++ packages/native_crypto/example/lib/home.dart | 64 ----- packages/native_crypto/example/lib/main.dart | 21 +- .../example/lib/pages/benchmark_page.dart | 189 -------------- .../example/lib/pages/cipher_page.dart | 157 ------------ .../example/lib/pages/kdf_page.dart | 136 ---------- .../example/lib/pointycastle/aes_gcm.dart | 16 +- .../example/lib/presentation/app/app.dart | 58 +++++ .../cipher/blocs/aes/aes_cubit.dart | 235 ++++++++++++++++++ .../cipher/blocs/aes/aes_state.dart | 51 ++++ .../aes_state_management.dart | 108 ++++++++ .../hash/blocs/hash/hash_cubit.dart | 53 ++++ .../hash/blocs/hash/hash_state.dart | 32 +++ .../hash_state_management.dart | 88 +++++++ .../mode_switcher/mode_switcher_cubit.dart | 41 +++ .../mode_switcher/mode_switcher_state.dart | 26 ++ .../navigation_bar/navigation_bar_cubit.dart | 20 ++ .../navigation_bar/navigation_bar_state.dart | 14 ++ .../home_state_management.dart | 67 +++++ .../widgets/app_bar_state_management.dart | 33 +++ ...ottom_navigation_bar_state_management.dart | 35 +++ .../widgets/button_state_management.dart | 38 +++ .../lib/presentation/home/widgets/blank.dart | 15 ++ .../key_derivation/key_derivation_cubit.dart | 112 +++++++++ .../key_derivation/key_derivation_state.dart | 32 +++ .../key_derivation_state_management.dart | 102 ++++++++ .../output/blocs/output/output_cubit.dart | 49 ++++ .../output/blocs/output/output_state.dart | 32 +++ .../output_state_management.dart | 76 ++++++ .../clear_button_state_management.dart | 22 ++ .../lib/presentation/output/widgets/logs.dart | 44 ++++ .../test_vectors/test_vectors_cubit.dart | 204 +++++++++++++++ .../test_vectors/test_vectors_state.dart | 27 ++ .../test_vectors_state_management.dart | 74 ++++++ .../native_crypto/example/lib/session.dart | 26 -- .../example/lib/widgets/button.dart | 31 --- .../example/lib/widgets/output.dart | 61 ----- packages/native_crypto/example/pubspec.yaml | 48 ++-- .../example/pubspec_overrides.yaml | 12 + .../native_crypto/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes packages/native_crypto/example/web/index.html | 58 +++++ .../native_crypto/example/web/manifest.json | 35 +++ packages/native_crypto/example/web/pkg | 1 + 77 files changed, 2711 insertions(+), 758 deletions(-) create mode 100644 packages/native_crypto/example/lib/core/get_it.dart create mode 100644 packages/native_crypto/example/lib/core/typography.dart create mode 100644 packages/native_crypto/example/lib/data/data_sources/logger_data_source_impl.dart create mode 100644 packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart create mode 100644 packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart create mode 100644 packages/native_crypto/example/lib/data/data_sources/session_data_source_impl.dart create mode 100644 packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart create mode 100644 packages/native_crypto/example/lib/data/repositories/logger_repository_impl.dart create mode 100644 packages/native_crypto/example/lib/data/repositories/session_repository_impl.dart create mode 100644 packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart create mode 100644 packages/native_crypto/example/lib/domain/data_sources/logger_data_source.dart create mode 100644 packages/native_crypto/example/lib/domain/data_sources/session_data_source.dart create mode 100644 packages/native_crypto/example/lib/domain/entities/log_message.dart create mode 100644 packages/native_crypto/example/lib/domain/entities/mode.dart create mode 100644 packages/native_crypto/example/lib/domain/entities/states.dart create mode 100644 packages/native_crypto/example/lib/domain/entities/test_vector.dart create mode 100644 packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart create mode 100644 packages/native_crypto/example/lib/domain/repositories/logger_repository.dart create mode 100644 packages/native_crypto/example/lib/domain/repositories/session_repository.dart delete mode 100644 packages/native_crypto/example/lib/home.dart delete mode 100644 packages/native_crypto/example/lib/pages/benchmark_page.dart delete mode 100644 packages/native_crypto/example/lib/pages/cipher_page.dart delete mode 100644 packages/native_crypto/example/lib/pages/kdf_page.dart create mode 100644 packages/native_crypto/example/lib/presentation/app/app.dart create mode 100644 packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart create mode 100644 packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_state.dart create mode 100644 packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart create mode 100644 packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_cubit.dart create mode 100644 packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_state.dart create mode 100644 packages/native_crypto/example/lib/presentation/hash/state_management/hash_state_management.dart create mode 100644 packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart create mode 100644 packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_state.dart create mode 100644 packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart create mode 100644 packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_state.dart create mode 100644 packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart create mode 100644 packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart create mode 100644 packages/native_crypto/example/lib/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart create mode 100644 packages/native_crypto/example/lib/presentation/home/state_management/widgets/button_state_management.dart create mode 100644 packages/native_crypto/example/lib/presentation/home/widgets/blank.dart create mode 100644 packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart create mode 100644 packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_state.dart create mode 100644 packages/native_crypto/example/lib/presentation/kdf/state_management/key_derivation_state_management.dart create mode 100644 packages/native_crypto/example/lib/presentation/output/blocs/output/output_cubit.dart create mode 100644 packages/native_crypto/example/lib/presentation/output/blocs/output/output_state.dart create mode 100644 packages/native_crypto/example/lib/presentation/output/state_management/output_state_management.dart create mode 100644 packages/native_crypto/example/lib/presentation/output/state_management/widgets/clear_button_state_management.dart create mode 100644 packages/native_crypto/example/lib/presentation/output/widgets/logs.dart create mode 100644 packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart create mode 100644 packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_state.dart create mode 100644 packages/native_crypto/example/lib/presentation/test_vectors/state_management/test_vectors_state_management.dart delete mode 100644 packages/native_crypto/example/lib/session.dart delete mode 100644 packages/native_crypto/example/lib/widgets/button.dart delete mode 100644 packages/native_crypto/example/lib/widgets/output.dart create mode 100644 packages/native_crypto/example/pubspec_overrides.yaml create mode 100644 packages/native_crypto/example/web/favicon.png create mode 100644 packages/native_crypto/example/web/icons/Icon-192.png create mode 100644 packages/native_crypto/example/web/icons/Icon-512.png create mode 100644 packages/native_crypto/example/web/icons/Icon-maskable-192.png create mode 100644 packages/native_crypto/example/web/icons/Icon-maskable-512.png create mode 100644 packages/native_crypto/example/web/index.html create mode 100644 packages/native_crypto/example/web/manifest.json create mode 120000 packages/native_crypto/example/web/pkg diff --git a/packages/native_crypto/example/.metadata b/packages/native_crypto/example/.metadata index ee7f61d..92fd7de 100644 --- a/packages/native_crypto/example/.metadata +++ b/packages/native_crypto/example/.metadata @@ -1,10 +1,30 @@ # This file tracks properties of this Flutter project. # Used by Flutter tool to assess capabilities and perform upgrades etc. # -# This file should be version controlled and should not be manually edited. +# This file should be version controlled. version: - revision: cf4400006550b70f28e4b4af815151d1e74846c6 + revision: cd41fdd495f6944ecd3506c21e94c6567b073278 channel: stable project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + - platform: web + create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/native_crypto/example/analysis_options.yaml b/packages/native_crypto/example/analysis_options.yaml index 61b6c4d..82177cd 100644 --- a/packages/native_crypto/example/analysis_options.yaml +++ b/packages/native_crypto/example/analysis_options.yaml @@ -1,29 +1 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options +include: package:wyatt_analysis/analysis_options.flutter.yaml \ No newline at end of file diff --git a/packages/native_crypto/example/android/app/build.gradle b/packages/native_crypto/example/android/app/build.gradle index 15632c2..237aa05 100644 --- a/packages/native_crypto/example/android/app/build.gradle +++ b/packages/native_crypto/example/android/app/build.gradle @@ -57,6 +57,7 @@ android { signingConfig signingConfigs.debug } } + namespace 'fr.pointcheval.native_crypto_example' } flutter { diff --git a/packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml b/packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml index 94e68f6..f880684 100644 --- a/packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml +++ b/packages/native_crypto/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/packages/native_crypto/example/android/app/src/main/AndroidManifest.xml b/packages/native_crypto/example/android/app/src/main/AndroidManifest.xml index c55f004..7f62b9c 100644 --- a/packages/native_crypto/example/android/app/src/main/AndroidManifest.xml +++ b/packages/native_crypto/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + + diff --git a/packages/native_crypto/example/android/build.gradle b/packages/native_crypto/example/android/build.gradle index 3245887..4bcbcdd 100644 --- a/packages/native_crypto/example/android/build.gradle +++ b/packages/native_crypto/example/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:7.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties index 562c5e4..cc5527d 100644 --- a/packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip diff --git a/packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist b/packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f..9625e10 100644 --- a/packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/packages/native_crypto/example/ios/Podfile.lock b/packages/native_crypto/example/ios/Podfile.lock index 3257d42..ab8fa0d 100644 --- a/packages/native_crypto/example/ios/Podfile.lock +++ b/packages/native_crypto/example/ios/Podfile.lock @@ -14,7 +14,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/native_crypto_ios/ios" SPEC CHECKSUMS: - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 native_crypto_ios: de03ec2f594e8d41bcba2341b7ad57fd926ada5d PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b diff --git a/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj b/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj index 8c503eb..47f5593 100644 --- a/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -200,6 +200,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -214,6 +215,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -340,7 +342,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -418,7 +420,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -467,7 +469,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/packages/native_crypto/example/ios/Runner/Info.plist b/packages/native_crypto/example/ios/Runner/Info.plist index 8293c48..ff3e5aa 100644 --- a/packages/native_crypto/example/ios/Runner/Info.plist +++ b/packages/native_crypto/example/ios/Runner/Info.plist @@ -45,5 +45,7 @@ CADisableMinimumFrameDurationOnPhone + UIApplicationSupportsIndirectInputEvents + diff --git a/packages/native_crypto/example/lib/core/get_it.dart b/packages/native_crypto/example/lib/core/get_it.dart new file mode 100644 index 0000000..1de732b --- /dev/null +++ b/packages/native_crypto/example/lib/core/get_it.dart @@ -0,0 +1,35 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:get_it/get_it.dart'; +import 'package:native_crypto_example/data/data_sources/logger_data_source_impl.dart'; +import 'package:native_crypto_example/data/data_sources/native_crypto_data_source_impl.dart'; +import 'package:native_crypto_example/data/data_sources/pointy_castle_data_source_impl.dart'; +import 'package:native_crypto_example/data/data_sources/session_data_source_impl.dart'; +import 'package:native_crypto_example/domain/data_sources/logger_data_source.dart'; +import 'package:native_crypto_example/domain/data_sources/session_data_source.dart'; + +final getIt = GetIt.I; + +abstract class GetItInitializer { + static Future init() async { + getIt + ..registerLazySingleton( + SessionDataSourceImpl.new, + ) + ..registerLazySingleton( + LoggerDataSourceImpl.new, + ) + ..registerLazySingleton( + NativeCryptoDataSourceImpl.new, + ) + ..registerLazySingleton( + PointyCastleDataSourceImpl.new, + ); + + await getIt.allReady(); + } +} diff --git a/packages/native_crypto/example/lib/core/typography.dart b/packages/native_crypto/example/lib/core/typography.dart new file mode 100644 index 0000000..3e55d46 --- /dev/null +++ b/packages/native_crypto/example/lib/core/typography.dart @@ -0,0 +1,18 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; + +abstract class AppTypography { + static const title = TextStyle( + color: Colors.black, + fontSize: 24, + ); + + static const body = TextStyle( + fontSize: 16, + ); +} diff --git a/packages/native_crypto/example/lib/data/data_sources/logger_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/logger_data_source_impl.dart new file mode 100644 index 0000000..022d9e6 --- /dev/null +++ b/packages/native_crypto/example/lib/data/data_sources/logger_data_source_impl.dart @@ -0,0 +1,34 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:native_crypto_example/domain/data_sources/logger_data_source.dart'; +import 'package:native_crypto_example/domain/entities/log_message.dart'; + +class LoggerDataSourceImpl extends LoggerDataSource { + final Map _logs = {}; + final StreamController> _streamController = + StreamController.broadcast(); + + @override + Future addLog(LogMessage message) async { + _logs[DateTime.now()] = message; + _streamController.add(Map.from(_logs)); + } + + @override + Future clearLog() async { + _logs.clear(); + _streamController.add(Map.from(_logs)); + } + + @override + Future> getLogs() async => _logs; + + @override + Stream> streamLogs() => _streamController.stream; +} diff --git a/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart new file mode 100644 index 0000000..957619e --- /dev/null +++ b/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart @@ -0,0 +1,104 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/foundation.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart'; + +class NativeCryptoDataSourceImpl extends CryptoDataSource { + @override + Future decrypt(Uint8List data, SecretKey key) async { + final AES cipher = AES( + key: key, + mode: AESMode.gcm, + padding: AESPadding.none, + ); + final Uint8List plainText = await cipher.decrypt( + CipherText( + data, + chunkFactory: (bytes) => AESCipherChunk( + bytes, + ivLength: cipher.mode.ivLength, + tagLength: cipher.mode.tagLength, + ), + ), + ); + + return plainText; + } + + @override + Future deriveKeyFromPassword( + String password, { + required String salt, + }) async { + final Pbkdf2 pbkdf2 = Pbkdf2( + length: 32, + iterations: 1000, + salt: salt.toBytes(), + hashAlgorithm: HashAlgorithm.sha256, + ); + return pbkdf2(password: password); + } + + @override + Future hash(Hash hasher, Uint8List data) async { + final Uint8List digestMessage = await hasher.digest(data); + + return digestMessage; + } + + @override + Future hmac(Hmac hmac, Uint8List data, SecretKey key) async { + final Uint8List digestMessage = await hmac.digest(data, key); + + return digestMessage; + } + + @override + Future encrypt(Uint8List data, SecretKey key) async { + final AES cipher = AES( + key: key, + mode: AESMode.gcm, + padding: AESPadding.none, + ); + final CipherText cipherText = await cipher.encrypt(data); + + return cipherText.bytes; + } + + @override + Future encryptWithIV( + Uint8List data, + SecretKey key, + Uint8List iv, + ) async { + final AES cipher = AES( + key: key, + mode: AESMode.gcm, + padding: AESPadding.none, + ); + + final AESCipherChunk chunk = await cipher.encryptWithIV(data, iv); + final CipherText cipherText = CipherText.fromChunks( + [chunk], + chunkFactory: (bytes) => AESCipherChunk( + bytes, + ivLength: cipher.mode.ivLength, + tagLength: cipher.mode.tagLength, + ), + ); + + return cipherText.bytes; + } + + @override + Future generateSecureRandom(int length) async { + final SecretKey sk = await SecretKey.fromSecureRandom(length); + + return sk; + } +} diff --git a/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart new file mode 100644 index 0000000..d4921eb --- /dev/null +++ b/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart @@ -0,0 +1,160 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +// ignore_for_file: implementation_imports + +import 'package:flutter/foundation.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart'; +import 'package:pointycastle/export.dart'; +import 'package:pointycastle/src/platform_check/platform_check.dart'; + +class PointyCastleDataSourceImpl extends CryptoDataSource { + FortunaRandom? _secureRandom; + + @override + Future decrypt(Uint8List data, SecretKey key) async { + final iv = Uint8List.fromList(data.sublist(0, 12)); + final cipherTextWithoutIv = Uint8List.fromList( + data.sublist(12), + ); + + final gcm = GCMBlockCipher(AESEngine()) + ..init( + false, + AEADParameters( + KeyParameter(key.bytes), + 16 * 8, + iv, + Uint8List(0), + ), + ); + final paddedPlainText = gcm.process(cipherTextWithoutIv); + + return paddedPlainText; + } + + @override + Future deriveKeyFromPassword( + String password, { + required String salt, + }) async { + final derivator = PBKDF2KeyDerivator(HMac(SHA256Digest(), 64)) + ..init( + Pbkdf2Parameters(salt.toBytes(), 1000, 32), + ); + + final Uint8List sk = derivator.process(password.toBytes()); + + return SecretKey(sk); + } + + @override + Future hash(Hash hasher, Uint8List data) async { + final Digest? digest; + + switch (hasher.runtimeType) { + case Sha256: + digest = SHA256Digest(); + break; + case Sha384: + digest = SHA384Digest(); + break; + case Sha512: + digest = SHA512Digest(); + break; + default: + throw UnsupportedError( + 'Unsupported hash algorithm: ${hasher.runtimeType}', + ); + } + + return digest.process(data); + } + + @override + Future hmac(Hmac hmac, Uint8List data, SecretKey key) async { + final HMac? digest; + + switch (hmac.runtimeType) { + case HmacSha256: + digest = HMac.withDigest(SHA256Digest()); + digest.init(KeyParameter(key.bytes)); + break; + case Sha384: + digest = HMac.withDigest(SHA384Digest()); + digest.init(KeyParameter(key.bytes)); + break; + case Sha512: + digest = HMac.withDigest(SHA512Digest()); + digest.init(KeyParameter(key.bytes)); + break; + default: + throw UnsupportedError( + 'Unsupported hmac algorithm: ${hmac.runtimeType}', + ); + } + + return digest.process(data); + } + + @override + Future encrypt(Uint8List data, SecretKey key) async { + final iv = (await generateSecureRandom(12 * 8)).bytes; + + final gcm = GCMBlockCipher(AESEngine()) + ..init( + true, + AEADParameters( + KeyParameter(key.bytes), + 16 * 8, + iv, + Uint8List(0), + ), + ); + + final cipherText = gcm.process(data); + + return Uint8List.fromList( + iv + cipherText, + ); + } + + @override + Future encryptWithIV( + Uint8List data, SecretKey key, Uint8List iv,) async { + final gcm = GCMBlockCipher(AESEngine()) + ..init( + true, + AEADParameters( + KeyParameter(key.bytes), + 16 * 8, + iv, + Uint8List(0), + ), + ); + + final cipherText = gcm.process(data); + + return Uint8List.fromList( + iv + cipherText, + ); + } + + @override + Future generateSecureRandom(int length) async { + if (_secureRandom == null) { + _secureRandom = FortunaRandom(); + _secureRandom!.seed( + KeyParameter(Platform.instance.platformEntropySource().getBytes(32)), + ); + } + + final sk = _secureRandom!.nextBytes(length); + + return SecretKey(sk); + } +} diff --git a/packages/native_crypto/example/lib/data/data_sources/session_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/session_data_source_impl.dart new file mode 100644 index 0000000..d7ee01e --- /dev/null +++ b/packages/native_crypto/example/lib/data/data_sources/session_data_source_impl.dart @@ -0,0 +1,45 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/data_sources/session_data_source.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; + +class SessionDataSourceImpl extends SessionDataSource { + SecretKey? _sk; + Mode? _mode = const NativeCryptoMode(); + + @override + Future getSessionKey() async { + if (_sk == null) { + throw Exception('Session key is not ready'); + } + + return _sk!; + } + + @override + Future isSessionKeyReady() async => _sk != null; + + @override + Future setSessionKey(SecretKey key) async { + _sk = key; + } + + @override + Future getCurrentMode() async { + if (_mode == null) { + throw Exception('Mode is not set'); + } + + return _mode!; + } + + @override + Future setCurrentMode(Mode mode) async { + _mode = mode; + } +} diff --git a/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart b/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart new file mode 100644 index 0000000..818aa21 --- /dev/null +++ b/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart @@ -0,0 +1,111 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:typed_data'; + +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +class CryptoRepositoryImpl extends CryptoRepository { + CryptoRepositoryImpl({ + required this.cryptoDataSource, + }); + CryptoDataSource cryptoDataSource; + + @override + FutureOrResult decrypt(Uint8List data, SecretKey key) => + Result.tryCatchAsync( + () async => cryptoDataSource.decrypt(data, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult deriveKeyFromPassword( + String password, { + required String salt, + }) => + Result.tryCatchAsync( + () async => cryptoDataSource.deriveKeyFromPassword( + password, + salt: salt, + ), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult hash(Hash hasher, Uint8List data) => + Result.tryCatchAsync( + () async => cryptoDataSource.hash(hasher, data), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult hmac(Hmac hmac, Uint8List data, SecretKey key) => + Result.tryCatchAsync( + () async => cryptoDataSource.hmac(hmac, data, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult encrypt(Uint8List data, SecretKey key) => + Result.tryCatchAsync( + () async => cryptoDataSource.encrypt(data, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult encryptWithIV( + Uint8List data, SecretKey key, Uint8List iv,) => + Result.tryCatchAsync( + () async => cryptoDataSource.encryptWithIV(data, key, iv), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult generateSecureRandom(int length) => + Result.tryCatchAsync( + () async => cryptoDataSource.generateSecureRandom(length), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); +} diff --git a/packages/native_crypto/example/lib/data/repositories/logger_repository_impl.dart b/packages/native_crypto/example/lib/data/repositories/logger_repository_impl.dart new file mode 100644 index 0000000..54ba9be --- /dev/null +++ b/packages/native_crypto/example/lib/data/repositories/logger_repository_impl.dart @@ -0,0 +1,41 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:native_crypto_example/domain/data_sources/logger_data_source.dart'; +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +class LoggerRepositoryImpl extends LoggerRepository { + LoggerRepositoryImpl({ + required this.loggerDataSource, + }); + + final LoggerDataSource loggerDataSource; + + @override + FutureOrResult addLog(LogMessage message) => Result.tryCatchAsync( + () async => loggerDataSource.addLog(message), + (error) => ClientException(error.toString()), + ); + + @override + FutureOrResult clearLog() => Result.tryCatchAsync( + () async => loggerDataSource.clearLog(), + (error) => ClientException(error.toString()), + ); + + @override + FutureOrResult> getLogs() => Result.tryCatchAsync( + () async => loggerDataSource.getLogs(), + (error) => ClientException(error.toString()), + ); + + @override + StreamResult> streamLogs() => + loggerDataSource.streamLogs().map(Ok.new); +} diff --git a/packages/native_crypto/example/lib/data/repositories/session_repository_impl.dart b/packages/native_crypto/example/lib/data/repositories/session_repository_impl.dart new file mode 100644 index 0000000..5d2d592 --- /dev/null +++ b/packages/native_crypto/example/lib/data/repositories/session_repository_impl.dart @@ -0,0 +1,49 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/data_sources/session_data_source.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +class SessionRepositoryImpl extends SessionRepository { + SessionRepositoryImpl({ + required this.sessionDataSource, + }); + SessionDataSource sessionDataSource; + + @override + FutureOrResult getSessionKey() => Result.tryCatchAsync( + () async => sessionDataSource.getSessionKey(), + (error) => ClientException(error.toString()), + ); + + @override + FutureOrResult isSessionKeyReady() => Result.tryCatchAsync( + () async => sessionDataSource.isSessionKeyReady(), + (error) => ClientException(error.toString()), + ); + + @override + FutureOrResult setSessionKey(SecretKey key) => Result.tryCatchAsync( + () async => sessionDataSource.setSessionKey(key), + (error) => ClientException(error.toString()), + ); + + @override + FutureOrResult getCurrentMode() => Result.tryCatchAsync( + () async => sessionDataSource.getCurrentMode(), + (error) => ClientException(error.toString()), + ); + + @override + FutureOrResult setCurrentMode(Mode mode) => Result.tryCatchAsync( + () async => sessionDataSource.setCurrentMode(mode), + (error) => ClientException(error.toString()), + ); +} diff --git a/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart b/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart new file mode 100644 index 0000000..69db521 --- /dev/null +++ b/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart @@ -0,0 +1,26 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/foundation.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; + +abstract class CryptoDataSource extends BaseDataSource { + Future generateSecureRandom(int length); + Future deriveKeyFromPassword( + String password, { + required String salt, + }); + Future encrypt(Uint8List data, SecretKey key); + Future encryptWithIV( + Uint8List data, + SecretKey key, + Uint8List iv, + ); + Future decrypt(Uint8List data, SecretKey key); + Future hash(Hash hasher, Uint8List data); + Future hmac(Hmac hmac, Uint8List data, SecretKey key); +} diff --git a/packages/native_crypto/example/lib/domain/data_sources/logger_data_source.dart b/packages/native_crypto/example/lib/domain/data_sources/logger_data_source.dart new file mode 100644 index 0000000..fbff97a --- /dev/null +++ b/packages/native_crypto/example/lib/domain/data_sources/logger_data_source.dart @@ -0,0 +1,15 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; + +abstract class LoggerDataSource extends BaseDataSource { + Future addLog(LogMessage message); + Future clearLog(); + Future> getLogs(); + Stream> streamLogs(); +} diff --git a/packages/native_crypto/example/lib/domain/data_sources/session_data_source.dart b/packages/native_crypto/example/lib/domain/data_sources/session_data_source.dart new file mode 100644 index 0000000..8a31feb --- /dev/null +++ b/packages/native_crypto/example/lib/domain/data_sources/session_data_source.dart @@ -0,0 +1,17 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; + +abstract class SessionDataSource extends BaseDataSource { + Future setSessionKey(SecretKey key); + Future isSessionKeyReady(); + Future getSessionKey(); + Future getCurrentMode(); + Future setCurrentMode(Mode mode); +} diff --git a/packages/native_crypto/example/lib/domain/entities/log_message.dart b/packages/native_crypto/example/lib/domain/entities/log_message.dart new file mode 100644 index 0000000..05edf62 --- /dev/null +++ b/packages/native_crypto/example/lib/domain/entities/log_message.dart @@ -0,0 +1,27 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; + +class LogMessage { + const LogMessage(this.prefix, this.color, this.message); + + final String prefix; + final Color color; + final String message; +} + +class LogInfo extends LogMessage { + const LogInfo(String message) : super('info', Colors.black, message); +} + +class LogWarning extends LogMessage { + const LogWarning(String message) : super('warn', Colors.orange, message); +} + +class LogError extends LogMessage { + const LogError(String message) : super('fail', Colors.red, message); +} diff --git a/packages/native_crypto/example/lib/domain/entities/mode.dart b/packages/native_crypto/example/lib/domain/entities/mode.dart new file mode 100644 index 0000000..d35e901 --- /dev/null +++ b/packages/native_crypto/example/lib/domain/entities/mode.dart @@ -0,0 +1,34 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; + +class Mode extends Entity { + const Mode( + this.primaryColor, + this.secondaryColor, + ); + + final Color primaryColor; + final Color secondaryColor; +} + +class NativeCryptoMode extends Mode { + const NativeCryptoMode() + : super( + Colors.blue, + Colors.black, + ); +} + +class PointyCastleMode extends Mode { + const PointyCastleMode() + : super( + Colors.red, + Colors.white, + ); +} diff --git a/packages/native_crypto/example/lib/domain/entities/states.dart b/packages/native_crypto/example/lib/domain/entities/states.dart new file mode 100644 index 0000000..0e1482c --- /dev/null +++ b/packages/native_crypto/example/lib/domain/entities/states.dart @@ -0,0 +1,7 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +enum State { initial, loading, success, failure } diff --git a/packages/native_crypto/example/lib/domain/entities/test_vector.dart b/packages/native_crypto/example/lib/domain/entities/test_vector.dart new file mode 100644 index 0000000..5682213 --- /dev/null +++ b/packages/native_crypto/example/lib/domain/entities/test_vector.dart @@ -0,0 +1,54 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/foundation.dart'; +import 'package:native_crypto/native_crypto.dart'; + +class TestVector { + TestVector({ + required this.key, + required this.nonce, + required this.plainText, + required this.cipherText, + required this.tag, + }); + + final String key; + final String nonce; + final String plainText; + final String cipherText; + final String tag; + + Uint8List get keyBytes => key.toBytes(from: Encoding.base16); + Uint8List get nonceBytes => nonce.toBytes(from: Encoding.base16); + Uint8List get tagBytes => tag.toBytes(from: Encoding.base16); + Uint8List get plainTextBytes => plainText.toBytes(from: Encoding.base16); + Uint8List get cipherTextBytes { + final iv = nonceBytes; + final data = cipherText.toBytes(from: Encoding.base16); + final tag = tagBytes; + return Uint8List.fromList(iv + data + tag); + } + + bool validateCipherText(Uint8List? testCipherText) { + final result = listEquals(testCipherText, cipherTextBytes); + if (!result) { + debugPrint('Cipher texts differs:\n$cipherTextBytes\n$testCipherText'); + } + return result; + } + + bool validatePlainText(Uint8List? testPlainText) { + final result = listEquals(testPlainText, plainTextBytes); + if (!result) { + debugPrint('Plain texts differs:\n$plainTextBytes\n$testPlainText'); + } + return result; + } + + bool validate(Uint8List? testPlainText, Uint8List? testCipherText) => + validateCipherText(testCipherText) && validatePlainText(testPlainText); +} diff --git a/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart b/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart new file mode 100644 index 0000000..6bf54ea --- /dev/null +++ b/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart @@ -0,0 +1,27 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/foundation.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; + +abstract class CryptoRepository extends BaseRepository { + FutureOrResult generateSecureRandom(int length); + FutureOrResult deriveKeyFromPassword( + String password, { + required String salt, + }); + FutureOrResult encrypt(Uint8List data, SecretKey key); + FutureOrResult encryptWithIV( + Uint8List data, + SecretKey key, + Uint8List iv, + ); + FutureOrResult decrypt(Uint8List data, SecretKey key); + + FutureOrResult hash(Hash hasher, Uint8List data); + FutureOrResult hmac(Hmac hmac, Uint8List data, SecretKey key); +} diff --git a/packages/native_crypto/example/lib/domain/repositories/logger_repository.dart b/packages/native_crypto/example/lib/domain/repositories/logger_repository.dart new file mode 100644 index 0000000..ed29af4 --- /dev/null +++ b/packages/native_crypto/example/lib/domain/repositories/logger_repository.dart @@ -0,0 +1,18 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: logger_repository.dart +// Created Date: 09/01/2023 22:12:56 +// Last Modified: 09/01/2023 23:03:51 +// ----- +// Copyright (c) 2023 + +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; + +abstract class LoggerRepository extends BaseRepository { + FutureOrResult addLog(LogMessage message); + FutureOrResult clearLog(); + FutureOrResult> getLogs(); + StreamResult> streamLogs(); +} diff --git a/packages/native_crypto/example/lib/domain/repositories/session_repository.dart b/packages/native_crypto/example/lib/domain/repositories/session_repository.dart new file mode 100644 index 0000000..2ecce6f --- /dev/null +++ b/packages/native_crypto/example/lib/domain/repositories/session_repository.dart @@ -0,0 +1,20 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: session_repository.dart +// Created Date: 08/01/2023 17:35:46 +// Last Modified: 09/01/2023 22:13:21 +// ----- +// Copyright (c) 2023 + +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; + +abstract class SessionRepository extends BaseRepository { + FutureOrResult setSessionKey(SecretKey key); + FutureOrResult isSessionKeyReady(); + FutureOrResult getSessionKey(); + FutureOrResult getCurrentMode(); + FutureOrResult setCurrentMode(Mode mode); +} diff --git a/packages/native_crypto/example/lib/home.dart b/packages/native_crypto/example/lib/home.dart deleted file mode 100644 index 8357b7e..0000000 --- a/packages/native_crypto/example/lib/home.dart +++ /dev/null @@ -1,64 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: home.dart -// Created Date: 28/12/2021 13:48:36 -// Last Modified: 28/12/2021 15:18:03 -// ----- -// Copyright (c) 2021 - -import 'package:flutter/material.dart'; -import 'package:native_crypto_example/pages/benchmark_page.dart'; - -import 'pages/cipher_page.dart'; -import 'pages/kdf_page.dart'; - -class Home extends StatefulWidget { - const Home({Key? key}) : super(key: key); - - @override - _HomeState createState() => _HomeState(); -} - -class _HomeState extends State { - int _currentIndex = 0; - final List _children = [KdfPage(), CipherPage(), BenchmarkPage()]; - - void onTabTapped(int index) { - setState(() { - _currentIndex = index; - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - centerTitle: true, - title: const Text('Native Crypto'), - ), - body: _children[_currentIndex], - bottomNavigationBar: BottomNavigationBar( - selectedItemColor: Colors.blue, - unselectedItemColor: Colors.black, - showUnselectedLabels: true, - onTap: onTabTapped, // new - currentIndex: _currentIndex, // new - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.vpn_key), - label: 'Key', - ), - BottomNavigationBarItem( - icon: Icon(Icons.lock), - label: 'Encryption', - ), - BottomNavigationBarItem( - icon: Icon(Icons.timer), - label: 'Benchmark', - ), - ], - ), - ); - } -} diff --git a/packages/native_crypto/example/lib/main.dart b/packages/native_crypto/example/lib/main.dart index 65b60e0..07da36e 100644 --- a/packages/native_crypto/example/lib/main.dart +++ b/packages/native_crypto/example/lib/main.dart @@ -3,23 +3,16 @@ // ----- // File: main.dart // Created Date: 27/12/2021 21:15:12 -// Last Modified: 28/12/2021 13:51:36 +// Last Modified: 10/01/2023 14:59:05 // ----- // Copyright (c) 2021 import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:native_crypto_example/home.dart'; +import 'package:native_crypto_example/core/get_it.dart'; +import 'package:native_crypto_example/presentation/app/app.dart'; -void main() { - runApp(const ProviderScope(child: MyApp())); -} - -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const MaterialApp(home: Home()); - } +Future main() async { + await GetItInitializer.init(); + + runApp(App()); } diff --git a/packages/native_crypto/example/lib/pages/benchmark_page.dart b/packages/native_crypto/example/lib/pages/benchmark_page.dart deleted file mode 100644 index 6396152..0000000 --- a/packages/native_crypto/example/lib/pages/benchmark_page.dart +++ /dev/null @@ -1,189 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: benchmark_page.dart -// Created Date: 28/12/2021 15:12:39 -// Last Modified: 26/05/2022 20:19:28 -// ----- -// Copyright (c) 2021 - -import 'dart:math'; -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:native_crypto/native_crypto.dart'; -import 'package:native_crypto_example/pointycastle/aes_gcm.dart'; -import 'package:native_crypto_example/widgets/button.dart'; - -import '../session.dart'; -import '../widgets/output.dart'; - -class BenchmarkPage extends ConsumerWidget { - BenchmarkPage({Key? key}) : super(key: key); - - final Output keyContent = Output(); - final Output benchmarkStatus = Output(large: true); - - Future _benchmark( - WidgetRef ref, - Cipher cipher, { - bool usePc = false, - bool encryptionOnly = false, - }) async { - Session state = ref.read(sessionProvider.state).state; - AesGcm pc = AesGcm(); - - if (state.secretKey.bytes.isEmpty) { - benchmarkStatus - .print('No SecretKey!\nGo in Key tab and generate or derive one.'); - return; - } - - List testedSizes = [2, 4, 8, 16, 32, 64, 128, 256]; - int multiplier = pow(2, 20).toInt(); // MiB - - benchmarkStatus.print("[Benchmark] Sizes: ${testedSizes.join('/')}MiB\n"); - benchmarkStatus.appendln( - "[Benchmark] Engine: " + (usePc ? " PointyCastle" : " NativeCrypto")); - benchmarkStatus.appendln("[Benchmark] Test: " + - (encryptionOnly ? " Encryption Only" : " Encryption & Decryption")); - benchmarkStatus.appendln( - '[Benchmark] bytesCountPerChunk: ${Cipher.bytesCountPerChunk} bytes/chunk'); - - String csv = encryptionOnly - ? "Run;Size (B);Encryption Time (ms)\n" - : "Run;Size (B);Encryption Time (ms);Decryption Time (ms)\n"; - - int run = 0; - var beforeBench = DateTime.now(); - - for (int size in testedSizes) { - run++; - final StringBuffer csvLine = StringBuffer(); - final dummyBytes = Uint8List(size * multiplier); - csvLine.write('$run;${size * multiplier};'); - - // Encryption - Object encryptedBigFile; - var before = DateTime.now(); - if (usePc) { - encryptedBigFile = pc.encrypt(dummyBytes, state.secretKey.bytes); - } else { - encryptedBigFile = await cipher.encrypt(dummyBytes); - } - var after = DateTime.now(); - - var benchmark = - after.millisecondsSinceEpoch - before.millisecondsSinceEpoch; - benchmarkStatus - .appendln('[Benchmark] ${size}MiB => Encryption took $benchmark ms'); - csvLine.write('$benchmark'); - - if (!encryptionOnly) { - // Decryption - before = DateTime.now(); - if (usePc) { - pc.decrypt(encryptedBigFile as Uint8List, state.secretKey.bytes); - } else { - await cipher.decrypt(encryptedBigFile as CipherTextWrapper); - } - after = DateTime.now(); - benchmark = - after.millisecondsSinceEpoch - before.millisecondsSinceEpoch; - benchmarkStatus.appendln( - '[Benchmark] ${size}MiB => Decryption took $benchmark ms'); - csvLine.write(';$benchmark'); - } - csv += csvLine.toString() + '\n'; - } - var afterBench = DateTime.now(); - var benchmark = - afterBench.millisecondsSinceEpoch - beforeBench.millisecondsSinceEpoch; - var sum = testedSizes.reduce((a, b) => a + b); - benchmarkStatus - .appendln('[Benchmark] Finished: ${sum}MiB in $benchmark ms'); - benchmarkStatus.appendln('[Benchmark] Check the console for csv data'); - benchmarkStatus.appendln(csv); - debugPrint(csv); - } - - void _clear() { - benchmarkStatus.clear(); - } - - @override - Widget build(BuildContext context, WidgetRef ref) { - Session state = ref.read(sessionProvider.state).state; - if (state.secretKey.bytes.isEmpty) { - keyContent - .print('No SecretKey!\nGo in Key tab and generate or derive one.'); - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - const Align( - child: Text("Secret Key"), - alignment: Alignment.centerLeft, - ), - keyContent, - ], - ), - ), - ); - } - keyContent.print(state.secretKey.bytes.toString()); - - AES cipher = AES(state.secretKey); - - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - const Align( - child: Text("Secret Key"), - alignment: Alignment.centerLeft, - ), - keyContent, - Wrap( - children: [ - Button( - () => _benchmark(ref, cipher), - "NativeCrypto", - ), - const SizedBox(width: 8), - Button( - () => _benchmark(ref, cipher, usePc: true), - "PointyCastle", - ), - const SizedBox(width: 8), - Button( - () => _benchmark(ref, cipher, encryptionOnly: true), - "NC Encryption Only", - ), - const SizedBox(width: 8), - Button( - () => _benchmark( - ref, - cipher, - usePc: true, - encryptionOnly: true, - ), - "PC Encryption Only", - ), - const SizedBox(width: 8), - Button( - _clear, - "Clear", - ), - ], - ), - benchmarkStatus, - ], - ), - ), - ); - } -} diff --git a/packages/native_crypto/example/lib/pages/cipher_page.dart b/packages/native_crypto/example/lib/pages/cipher_page.dart deleted file mode 100644 index b0843c7..0000000 --- a/packages/native_crypto/example/lib/pages/cipher_page.dart +++ /dev/null @@ -1,157 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: cipher_page.dart -// Created Date: 28/12/2021 13:33:15 -// Last Modified: 27/05/2022 16:42:10 -// ----- -// Copyright (c) 2021 - -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:native_crypto/native_crypto.dart'; -import 'package:native_crypto/native_crypto_ext.dart'; -import 'package:native_crypto_example/widgets/button.dart'; - -import '../session.dart'; -import '../widgets/output.dart'; - -// ignore: must_be_immutable -class CipherPage extends ConsumerWidget { - CipherPage({Key? key}) : super(key: key); - - final Output keyContent = Output(); - final Output encryptionStatus = Output(); - final Output decryptionStatus = Output(); - - final TextEditingController _plainTextController = TextEditingController() - ..text = 'PlainText'; - CipherTextWrapper? cipherText; - - Future _encrypt(WidgetRef ref, Cipher cipher) async { - Session state = ref.read(sessionProvider.state).state; - final plainText = _plainTextController.text.trim(); - - if (state.secretKey.bytes.isEmpty) { - encryptionStatus - .print('No SecretKey!\nGo in Key tab and generate or derive one.'); - } else if (plainText.isEmpty) { - encryptionStatus.print('Entry is empty'); - } else { - var stringToBytes = plainText.toBytes(); - cipherText = await cipher.encrypt(stringToBytes); - encryptionStatus.print('String successfully encrypted:\n'); - - CipherText unwrap = cipherText!.unwrap(); - encryptionStatus.append(unwrap.base16); - } - } - - Future _alter() async { - if (cipherText == null) { - decryptionStatus.print('Encrypt before altering CipherText!'); - } else { - // Add 1 to the first byte - Uint8List _altered = cipherText!.unwrap().bytes; - _altered[0] += 1; - // Recreate cipher text with altered data - cipherText = CipherTextWrapper.fromBytes( - _altered, - ivLength: AESMode.gcm.ivLength, - tagLength: AESMode.gcm.tagLength, - ); - encryptionStatus.print('String successfully encrypted:\n'); - - CipherText unwrap = cipherText!.unwrap(); - encryptionStatus.appendln(unwrap.base16); - decryptionStatus.print('CipherText altered!\nDecryption will fail.'); - } - } - - void _decrypt(WidgetRef ref, Cipher cipher) async { - Session state = ref.read(sessionProvider.state).state; - - if (state.secretKey.bytes.isEmpty) { - decryptionStatus - .print('No SecretKey!\nGo in Key tab and generate or derive one.'); - } else if (cipherText == null) { - decryptionStatus.print('Encrypt before decrypting!'); - } else { - try { - Uint8List plainText = await cipher.decrypt(cipherText!); - var bytesToString = plainText.toStr(); - decryptionStatus - .print('String successfully decrypted:\n\n$bytesToString'); - } on NativeCryptoException catch (e) { - decryptionStatus.print(e.message ?? 'Decryption failed!'); - } - } - } - - @override - Widget build(BuildContext context, WidgetRef ref) { - Session state = ref.read(sessionProvider.state).state; - if (state.secretKey.bytes.isEmpty) { - keyContent - .print('No SecretKey!\nGo in Key tab and generate or derive one.'); - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - const Align( - child: Text("Secret Key"), - alignment: Alignment.centerLeft, - ), - keyContent, - ], - ), - ), - ); - } - keyContent.print(state.secretKey.bytes.toString()); - - AES cipher = AES(state.secretKey); - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - const Align( - child: Text("Secret Key"), - alignment: Alignment.centerLeft, - ), - keyContent, - TextField( - controller: _plainTextController, - decoration: const InputDecoration( - hintText: 'Plain text', - ), - ), - Button( - () => _encrypt(ref, cipher), - "Encrypt", - ), - encryptionStatus, - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Button( - _alter, - "Alter cipher", - ), - Button( - () => _decrypt(ref, cipher), - "Decrypt", - ), - ], - ), - decryptionStatus, - ], - ), - ), - ); - } -} diff --git a/packages/native_crypto/example/lib/pages/kdf_page.dart b/packages/native_crypto/example/lib/pages/kdf_page.dart deleted file mode 100644 index 371d883..0000000 --- a/packages/native_crypto/example/lib/pages/kdf_page.dart +++ /dev/null @@ -1,136 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: kdf_page.dart -// Created Date: 28/12/2021 13:40:34 -// Last Modified: 26/05/2022 21:09:47 -// ----- -// Copyright (c) 2021 - -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:native_crypto/native_crypto.dart'; -import 'package:native_crypto/native_crypto_ext.dart'; -import 'package:native_crypto_example/widgets/button.dart'; - -import '../session.dart'; -import '../widgets/output.dart'; - -class KdfPage extends ConsumerWidget { - KdfPage({Key? key}) : super(key: key); - - final Output keyContent = Output(); - final Output keyStatus = Output(); - final Output pbkdf2Status = Output(); - final Output hashStatus = Output(large: true); - - final TextEditingController _pwdTextController = TextEditingController() - ..text = 'Password'; - final TextEditingController _messageTextController = TextEditingController() - ..text = 'Message'; - - Future _generate(WidgetRef ref) async { - Session state = ref.read(sessionProvider.state).state; - try { - SecretKey sk = await SecretKey.fromSecureRandom(256); - state.setKey(sk); - keyStatus.print( - "SecretKey successfully generated.\nLength: ${state.secretKey.bytes.length} bytes"); - keyContent.print(state.secretKey.bytes.toString()); - debugPrint("As hex :\n${sk.base16}"); - } catch (e) { - keyStatus.print(e.toString()); - } - } - - Future _pbkdf2(WidgetRef ref) async { - Session state = ref.read(sessionProvider.state).state; - final password = _pwdTextController.text.trim(); - - if (password.isEmpty) { - pbkdf2Status.print('Password is empty'); - } else { - Pbkdf2 _pbkdf2 = Pbkdf2( - keyBytesCount: 32, - iterations: 1000, - algorithm: HashAlgorithm.sha512, - ); - SecretKey sk = await _pbkdf2.derive(password: password, salt: 'salt'); - state.setKey(sk); - pbkdf2Status.print('Key successfully derived.'); - keyContent.print(state.secretKey.bytes.toString()); - debugPrint("As hex :\n${sk.base16}"); - } - } - - Future _hash(HashAlgorithm hasher) async { - final message = _messageTextController.text.trim(); - if (message.isEmpty) { - hashStatus.print('Message is empty'); - } else { - Uint8List hash = await hasher.digest(message.toBytes()); - hashStatus.print( - 'Message successfully hashed with $hasher :${hash.toStr(to: Encoding.base16)}'); - } - } - - @override - Widget build(BuildContext context, WidgetRef ref) { - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - const Align( - child: Text("SecretKey"), - alignment: Alignment.centerLeft, - ), - keyContent, - Button( - () => _generate(ref), - "Generate SecretKey", - ), - keyStatus, - TextField( - controller: _pwdTextController, - decoration: const InputDecoration( - hintText: 'Password', - ), - ), - Button( - () => _pbkdf2(ref), - "Apply PBKDF2", - ), - pbkdf2Status, - TextField( - controller: _messageTextController, - decoration: const InputDecoration( - hintText: 'Message', - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Button( - () => _hash(HashAlgorithm.sha256), - "SHA256", - ), - Button( - () => _hash(HashAlgorithm.sha384), - "SHA384", - ), - Button( - () => _hash(HashAlgorithm.sha512), - "SHA512", - ), - ], - ), - hashStatus, - ], - ), - ), - ); - } -} diff --git a/packages/native_crypto/example/lib/pointycastle/aes_gcm.dart b/packages/native_crypto/example/lib/pointycastle/aes_gcm.dart index 967c384..67d0483 100644 --- a/packages/native_crypto/example/lib/pointycastle/aes_gcm.dart +++ b/packages/native_crypto/example/lib/pointycastle/aes_gcm.dart @@ -1,11 +1,8 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: aes_gcm.dart -// Created Date: 24/05/2022 16:34:54 -// Last Modified: 27/05/2022 17:36:31 -// ----- -// Copyright (c) 2022 +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. // ignore_for_file: implementation_imports @@ -69,7 +66,8 @@ class AesGcm { _secureRandom = FortunaRandom(); _secureRandom!.seed( - KeyParameter(Platform.instance.platformEntropySource().getBytes(32))); + KeyParameter(Platform.instance.platformEntropySource().getBytes(32)), + ); } // Use it to generate the random bytes diff --git a/packages/native_crypto/example/lib/presentation/app/app.dart b/packages/native_crypto/example/lib/presentation/app/app.dart new file mode 100644 index 0000000..0769515 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/app/app.dart @@ -0,0 +1,58 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto_example/core/get_it.dart'; +import 'package:native_crypto_example/data/data_sources/native_crypto_data_source_impl.dart'; +import 'package:native_crypto_example/data/repositories/crypto_repository_impl.dart'; +import 'package:native_crypto_example/data/repositories/logger_repository_impl.dart'; +import 'package:native_crypto_example/data/repositories/session_repository_impl.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; +import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart'; +import 'package:native_crypto_example/presentation/home/state_management/home_state_management.dart'; +import 'package:native_crypto_example/presentation/output/blocs/output/output_cubit.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class App extends StatelessWidget { + App({super.key}); + + final LoggerRepository _loggerRepository = + LoggerRepositoryImpl(loggerDataSource: getIt()); + + final SessionRepository _sessionRepository = + SessionRepositoryImpl(sessionDataSource: getIt()); + + @override + Widget build(BuildContext context) => MultiProvider( + repositoryProviders: [ + RepositoryProvider.value(value: _loggerRepository), + RepositoryProvider.value( + value: _sessionRepository, + ), + RepositoryProvider( + create: (_) => CryptoRepositoryImpl( + cryptoDataSource: getIt(), + ), + ), + ], + blocProviders: [ + BlocProvider( + create: (_) => OutputCubit(_loggerRepository), + ), + BlocProvider( + create: (_) => ModeSwitcherCubit(_sessionRepository), + ) + ], + child: MaterialApp( + title: 'NativeCrypto', + debugShowCheckedModeBanner: false, + home: HomeStateManagement(), + ), + ); +} diff --git a/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart new file mode 100644 index 0000000..bb004cd --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart @@ -0,0 +1,235 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:native_crypto_example/domain/entities/states.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; + +part 'aes_state.dart'; + +class AESCubit extends Cubit { + AESCubit({ + required this.sessionRepository, + required this.loggerRepository, + required this.cryptoRepository, + }) : super(const AESState.initial()); + final SessionRepository sessionRepository; + final LoggerRepository loggerRepository; + final CryptoRepository cryptoRepository; + + FutureOr encrypt(String message) async { + emit(state.copyWith(state: State.loading)); + + final sk = await sessionRepository.getSessionKey(); + + if (sk.isErr) { + await loggerRepository.addLog( + const LogError('No SecretKey!\n' + 'Go in Key tab and generate or derive one.'), + ); + emit( + state.copyWith( + state: State.failure, + plainText: message.toBytes(), + error: sk.err?.message, + ), + ); + + return; + } + + final result = await cryptoRepository.encrypt(message.toBytes(), sk.ok!); + + emit( + await result.foldAsync( + (cipherText) async { + await loggerRepository.addLog( + LogInfo('String successfully encrypted.\n' + 'Length: ${cipherText.length} bytes.\n' + 'Hex:\n${cipherText.toStr(to: Encoding.base16)}'), + ); + return state.copyWith( + state: State.success, + plainText: message.toBytes(), + cipherText: cipherText, + plainTextFile: '', + cipherTextFile: '', + ); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during encryption.'), + ); + return state.copyWith( + state: State.failure, + plainText: message.toBytes(), + error: error.message, + ); + }, + ), + ); + + return; + } + + FutureOr alterMemory() async { + emit(state.copyWith(state: State.loading)); + + if (state.cipherText?.isEmpty ?? true) { + const error = 'Encrypt before decrypting!'; + await loggerRepository.addLog( + const LogError(error), + ); + emit( + state.copyWith( + state: State.failure, + error: error, + ), + ); + + return; + } + + final altered = state.cipherText!; + altered[0] += 1; + + await loggerRepository.addLog( + const LogWarning('In memory cipher text altered.'), + ); + + emit(state.copyWith(cipherText: altered)); + + return; + } + + FutureOr decryptFromMemory() async { + emit(state.copyWith(state: State.loading)); + + final sk = await sessionRepository.getSessionKey(); + + if (sk.isErr) { + await loggerRepository.addLog( + const LogError('No SecretKey!\n' + 'Go in Key tab and generate or derive one.'), + ); + emit( + state.copyWith( + state: State.failure, + error: sk.err?.message, + ), + ); + + return; + } + + if (state.cipherText?.isEmpty ?? true) { + const error = 'Encrypt before decrypting!'; + await loggerRepository.addLog( + const LogError(error), + ); + emit( + state.copyWith( + state: State.failure, + error: error, + ), + ); + + return; + } + + final result = await cryptoRepository.decrypt(state.cipherText!, sk.ok!); + + emit( + await result.foldAsync( + (plainText) async { + await loggerRepository.addLog( + LogInfo('String successfully decrypted.\n' + 'Text:\n${plainText.toStr()}'), + ); + return state.copyWith( + state: State.success, + plainText: plainText, + plainTextFile: '', + cipherTextFile: '', + ); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during decryption.'), + ); + return state.copyWith( + state: State.failure, + error: error.message, + ); + }, + ), + ); + + return; + } + + FutureOr decryptFromBase16(String message) async { + emit(state.copyWith(state: State.loading)); + + final sk = await sessionRepository.getSessionKey(); + final cipherText = message.toBytes(from: Encoding.base16); + + if (sk.isErr) { + await loggerRepository.addLog( + const LogError('No SecretKey!\n' + 'Go in Key tab and generate or derive one.'), + ); + emit( + state.copyWith( + state: State.failure, + cipherText: cipherText, + error: sk.err?.message, + ), + ); + + return; + } + + final result = await cryptoRepository.decrypt(cipherText, sk.ok!); + + emit( + await result.foldAsync( + (plainText) async { + await loggerRepository.addLog( + LogInfo('String successfully decrypted.\n' + 'Text:\n${plainText.toStr()}'), + ); + return state.copyWith( + state: State.success, + plainText: plainText, + cipherText: cipherText, + plainTextFile: '', + cipherTextFile: '', + ); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during decryption.'), + ); + return state.copyWith( + state: State.failure, + cipherText: cipherText, + error: error.message, + ); + }, + ), + ); + + return; + } +} diff --git a/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_state.dart b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_state.dart new file mode 100644 index 0000000..7f4411d --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_state.dart @@ -0,0 +1,51 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +part of 'aes_cubit.dart'; + +@immutable +class AESState { + const AESState( + this.state, + this.plainText, + this.cipherText, + this.plainTextFile, + this.cipherTextFile, + this.error, + ); + + const AESState.initial() + : state = State.initial, + plainText = null, + plainTextFile = null, + cipherText = null, + cipherTextFile = null, + error = null; + + final State state; + final Uint8List? plainText; + final Uint8List? cipherText; + final String? plainTextFile; + final String? cipherTextFile; + final String? error; + + AESState copyWith({ + State? state, + Uint8List? plainText, + Uint8List? cipherText, + String? plainTextFile, + String? cipherTextFile, + String? error, + }) => + AESState( + state ?? this.state, + plainText ?? this.plainText, + cipherText ?? this.cipherText, + plainTextFile ?? this.plainTextFile, + cipherTextFile ?? this.cipherTextFile, + error ?? this.error, + ); +} diff --git a/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart b/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart new file mode 100644 index 0000000..f518477 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart @@ -0,0 +1,108 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/core/typography.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; +import 'package:native_crypto_example/presentation/cipher/blocs/aes/aes_cubit.dart'; +import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart'; +import 'package:native_crypto_example/presentation/output/widgets/logs.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class AESStateManagement extends CubitScreen { + AESStateManagement({super.key}); + + final TextEditingController _plainTextTextController = TextEditingController() + ..text = 'abc'; + + final TextEditingController _cipherTextTextController = + TextEditingController(); + + @override + AESCubit create(BuildContext context) => AESCubit( + sessionRepository: repo(context), + loggerRepository: repo(context), + cryptoRepository: repo(context), + ); + + @override + Widget onBuild(BuildContext context, AESState state) => ListView( + children: [ + const Logs(), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'AES256-GCM', + style: AppTypography.title, + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + '''AES is a symmetric-key encryption algorithm that is widely used to encrypt sensitive data. It works by encrypting plaintext (the original data) using a secret key and a set of predefined mathematical operations (known as a cipher). The result is ciphertext (the encrypted data) that can only be decrypted and read by someone who has access to the secret key.\nGCM is a mode of operation for AES that provides both confidentiality and authenticity. It works by combining a block cipher (such as AES) with a Galois Field (a mathematical structure used to define a specific type of encryption) and a Counter (a value that is incremented for each block of data that is processed).''', + style: AppTypography.body, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _plainTextTextController, + decoration: const InputDecoration( + hintText: 'PlainText', + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Encrypt', + onPressed: () => + bloc(context).encrypt(_plainTextTextController.text), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Alter', + onPressed: () => bloc(context).alterMemory(), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Decrypt', + onPressed: () => bloc(context).decryptFromMemory(), + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'External CipherText', + style: AppTypography.title, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _cipherTextTextController, + decoration: const InputDecoration( + hintText: 'Base16 CipherText', + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Decrypt', + onPressed: () => bloc(context) + .decryptFromBase16(_cipherTextTextController.text), + ), + ), + ], + ); +} diff --git a/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_cubit.dart b/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_cubit.dart new file mode 100644 index 0000000..f57f58c --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_cubit.dart @@ -0,0 +1,53 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:native_crypto_example/domain/entities/states.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; + +part 'hash_state.dart'; + +class HashCubit extends Cubit { + HashCubit({ + required this.loggerRepository, + required this.cryptoRepository, + }) : super(const HashState.initial()); + + final LoggerRepository loggerRepository; + final CryptoRepository cryptoRepository; + + FutureOr hash(Hash hasher, String message) async { + emit(const HashState.loading()); + final result = await cryptoRepository.hash(hasher, message.toBytes()); + + emit( + await result.foldAsync( + (digest) async { + await loggerRepository.addLog( + LogInfo('Hash $message using ${hasher.algorithm.name}.\n' + 'Length: ${digest.length} bytes.\n' + 'Hex:\n${digest.toStr(to: Encoding.base16)}'), + ); + return HashState.success(digest); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during digest.'), + ); + return HashState.failure(error.message); + }, + ), + ); + + return; + } +} diff --git a/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_state.dart b/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_state.dart new file mode 100644 index 0000000..7eb49e3 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/hash/blocs/hash/hash_state.dart @@ -0,0 +1,32 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +part of 'hash_cubit.dart'; + +@immutable +class HashState { + const HashState.initial() + : state = State.initial, + result = null, + error = null; + + const HashState.loading() + : state = State.loading, + result = null, + error = null; + + const HashState.failure(this.error) + : state = State.failure, + result = null; + + const HashState.success(this.result) + : state = State.success, + error = null; + + final State state; + final Uint8List? result; + final String? error; +} diff --git a/packages/native_crypto/example/lib/presentation/hash/state_management/hash_state_management.dart b/packages/native_crypto/example/lib/presentation/hash/state_management/hash_state_management.dart new file mode 100644 index 0000000..c2c5c9a --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/hash/state_management/hash_state_management.dart @@ -0,0 +1,88 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/core/typography.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/presentation/hash/blocs/hash/hash_cubit.dart'; +import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart'; +import 'package:native_crypto_example/presentation/output/widgets/logs.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class HashStateManagement extends CubitScreen { + HashStateManagement({super.key}); + + final TextEditingController _hashTextController = TextEditingController() + ..text = 'abc'; + + @override + HashCubit create(BuildContext context) => HashCubit( + loggerRepository: repo(context), + cryptoRepository: repo(context), + ); + + @override + Widget onBuild(BuildContext context, HashState state) => ListView( + children: [ + const Logs(), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'Hash', + style: AppTypography.title, + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + '''SHA-256, SHA-384, and SHA-512 are cryptographic hash functions. They are used to create a fixed-size output (known as a hash or digest) from an input of any size. The output of a hash function is deterministic, which means that the same input will always produce the same output.''', + style: AppTypography.body, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _hashTextController, + decoration: const InputDecoration( + hintText: 'Message', + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'SHA256', + onPressed: () => bloc(context).hash( + Sha256(), + _hashTextController.text, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'SHA384', + onPressed: () => bloc(context).hash( + Sha384(), + _hashTextController.text, + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'SHA512', + onPressed: () => bloc(context).hash( + Sha512(), + _hashTextController.text, + ), + ), + ), + ], + ); +} diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart new file mode 100644 index 0000000..d4a0c5e --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart @@ -0,0 +1,41 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; + +part 'mode_switcher_state.dart'; + +class ModeSwitcherCubit extends Cubit { + ModeSwitcherCubit( + this.sessionRepository, + ) : super(const ModeSwitcherState(NativeCryptoMode())); + SessionRepository sessionRepository; + + FutureOr switchMode() async { + final currentMode = await sessionRepository.getCurrentMode(); + Mode? newMode; + + if (currentMode.isOk) { + if (currentMode.ok == const NativeCryptoMode()) { + newMode = const PointyCastleMode(); + } else { + newMode = const NativeCryptoMode(); + } + + sessionRepository.setCurrentMode(newMode); + } else { + newMode = const NativeCryptoMode(); + sessionRepository.setCurrentMode(newMode); + } + + emit(ModeSwitcherState(newMode)); + } +} diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_state.dart b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_state.dart new file mode 100644 index 0000000..9eae7c3 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_state.dart @@ -0,0 +1,26 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +part of 'mode_switcher_cubit.dart'; + +@immutable +class ModeSwitcherState { + const ModeSwitcherState(this.currentMode); + + final Mode currentMode; + + @override + bool operator ==(covariant ModeSwitcherState other) { + if (identical(this, other)) { + return true; + } + + return other.currentMode == currentMode; + } + + @override + int get hashCode => currentMode.hashCode; +} diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart b/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart new file mode 100644 index 0000000..ee5263a --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart @@ -0,0 +1,20 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +part 'navigation_bar_state.dart'; + +class NavigationBarCubit extends Cubit { + NavigationBarCubit() : super(const NavigationBarState(0)); + + FutureOr changePage(int page) { + emit(NavigationBarState(page)); + } +} diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_state.dart b/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_state.dart new file mode 100644 index 0000000..abec21c --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/home/blocs/navigation_bar/navigation_bar_state.dart @@ -0,0 +1,14 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +part of 'navigation_bar_cubit.dart'; + +@immutable +class NavigationBarState { + const NavigationBarState(this.index); + + final int index; +} diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart new file mode 100644 index 0000000..991df1f --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart @@ -0,0 +1,67 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/presentation/cipher/state_management/aes_state_management.dart'; +import 'package:native_crypto_example/presentation/hash/state_management/hash_state_management.dart'; +import 'package:native_crypto_example/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart'; +import 'package:native_crypto_example/presentation/home/state_management/widgets/app_bar_state_management.dart'; +import 'package:native_crypto_example/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart'; +import 'package:native_crypto_example/presentation/home/widgets/blank.dart'; +import 'package:native_crypto_example/presentation/kdf/state_management/key_derivation_state_management.dart'; +import 'package:native_crypto_example/presentation/test_vectors/state_management/test_vectors_state_management.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class HomeStateManagement + extends CubitScreen { + HomeStateManagement({super.key}); + + final List _children = [ + KeyDerivationStateManagement(), + HashStateManagement(), + AESStateManagement(), + TestVectorsStateManagement(), + const Blank() + ]; + + @override + NavigationBarCubit create(BuildContext context) => NavigationBarCubit(); + + @override + Widget onBuild(BuildContext context, NavigationBarState state) => Scaffold( + appBar: const PreferredSize( + preferredSize: Size(double.infinity, 60), + child: AppBarStateManagement(), + ), + body: _children[state.index], + bottomNavigationBar: BottomNavigationBarStateManagement( + onTap: (page) => bloc(context).changePage(page), // new + currentIndex: state.index, // new + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.vpn_key), + label: 'Key', + ), + BottomNavigationBarItem( + icon: Icon(Icons.tag), + label: 'Hash', + ), + BottomNavigationBarItem( + icon: Icon(Icons.lock), + label: 'Encryption', + ), + BottomNavigationBarItem( + icon: Icon(Icons.rule_rounded), + label: 'Tests', + ), + BottomNavigationBarItem( + icon: Icon(Icons.timer), + label: 'Benchmark', + ), + ], + ), + ); +} diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart new file mode 100644 index 0000000..1f51bbe --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart @@ -0,0 +1,33 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; +import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class AppBarStateManagement + extends CubitConsumerScreen { + const AppBarStateManagement({super.key}); + + @override + Widget onBuild(BuildContext context, ModeSwitcherState state) => AppBar( + centerTitle: true, + title: Text( + state.currentMode == const NativeCryptoMode() + ? 'NativeCrypto' + : 'PointyCastle', + ), + backgroundColor: state.currentMode.primaryColor, + // TODO(hpcl): enable mode switcher + // actions: [ + // Switch( + // value: state.currentMode == const NativeCryptoMode(), + // onChanged: (_) => bloc(context).switchMode(), + // ) + // ], + ); +} diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart new file mode 100644 index 0000000..5483ad1 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart @@ -0,0 +1,35 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class BottomNavigationBarStateManagement + extends CubitConsumerScreen { + const BottomNavigationBarStateManagement({ + required this.items, + this.currentIndex = 0, + this.onTap, + super.key, + }); + + final void Function(int)? onTap; + final int currentIndex; + final List items; + + @override + Widget onBuild(BuildContext context, ModeSwitcherState state) => + BottomNavigationBar( + selectedItemColor: state.currentMode.primaryColor, + unselectedItemColor: Colors.black, + showUnselectedLabels: true, + onTap: onTap, + currentIndex: currentIndex, + items: items, + type: BottomNavigationBarType.shifting, + ); +} diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/button_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/button_state_management.dart new file mode 100644 index 0000000..97b0f8a --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/button_state_management.dart @@ -0,0 +1,38 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto_example/core/typography.dart'; +import 'package:native_crypto_example/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class ButtonStateManagement + extends CubitConsumerScreen { + const ButtonStateManagement({ + required this.label, + this.onPressed, + super.key, + }); + + final void Function()? onPressed; + final String label; + + @override + Widget onBuild(BuildContext context, ModeSwitcherState state) { + context.watch(); + return ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: state.currentMode.primaryColor, + ), + child: Text( + label, + style: AppTypography.body, + ), + ); + } +} diff --git a/packages/native_crypto/example/lib/presentation/home/widgets/blank.dart b/packages/native_crypto/example/lib/presentation/home/widgets/blank.dart new file mode 100644 index 0000000..3a5835e --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/home/widgets/blank.dart @@ -0,0 +1,15 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; + +class Blank extends StatelessWidget { + const Blank({super.key}); + + @override + Widget build(BuildContext context) => + const Center(child: Text('Nothing to show')); +} diff --git a/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart b/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart new file mode 100644 index 0000000..41f9c48 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart @@ -0,0 +1,112 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:native_crypto_example/domain/entities/states.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; + +part 'key_derivation_state.dart'; + +class KeyDerivationCubit extends Cubit { + KeyDerivationCubit({ + required this.sessionRepository, + required this.loggerRepository, + required this.cryptoRepository, + }) : super(const KeyDerivationState.initial()); + final SessionRepository sessionRepository; + final LoggerRepository loggerRepository; + final CryptoRepository cryptoRepository; + + FutureOr generate() async { + emit(const KeyDerivationState.loading()); + final result = await cryptoRepository.generateSecureRandom(32); + + emit( + await result.foldAsync( + (key) async { + await sessionRepository.setSessionKey(key); + await loggerRepository.addLog( + LogInfo('SecretKey successfully generated.\n' + 'Length: ${key.bytes.length} bytes.\n' + 'Hex:\n${key.base16}'), + ); + return KeyDerivationState.success(key.bytes); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during key generation.'), + ); + return KeyDerivationState.failure(error.message); + }, + ), + ); + + return; + } + + FutureOr pbkdf2(String password) async { + emit(const KeyDerivationState.loading()); + if (password.isEmpty) { + const error = 'Password is empty'; + await loggerRepository.addLog( + const LogError(error), + ); + emit(const KeyDerivationState.failure(error)); + } + final result = + await cryptoRepository.deriveKeyFromPassword(password, salt: 'salt'); + + emit( + await result.foldAsync( + (key) async { + await sessionRepository.setSessionKey(key); + await loggerRepository.addLog( + LogInfo('SecretKey successfully derivated from $password.\n' + 'Length: ${key.bytes.length} bytes.\n' + 'Hex:\n${key.base16}'), + ); + return KeyDerivationState.success(key.bytes); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during key derivation.'), + ); + return KeyDerivationState.failure(error.message); + }, + ), + ); + + return; + } + + FutureOr getSessionKey() async { + emit(const KeyDerivationState.loading()); + final sk = await sessionRepository.getSessionKey(); + emit( + await sk.foldAsync((key) async { + await loggerRepository.addLog( + LogInfo('Session key successfully retreived.\n' + 'Length: ${key.bytes.length} bytes.\n' + 'Hex:\n${key.base16}'), + ); + return KeyDerivationState.success(key.bytes); + }, (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during key retrieving.'), + ); + return KeyDerivationState.failure(error.message); + }), + ); + + return; + } +} diff --git a/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_state.dart b/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_state.dart new file mode 100644 index 0000000..c8b7e6d --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/kdf/blocs/key_derivation/key_derivation_state.dart @@ -0,0 +1,32 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +part of 'key_derivation_cubit.dart'; + +@immutable +class KeyDerivationState { + const KeyDerivationState.initial() + : state = State.initial, + result = null, + error = null; + + const KeyDerivationState.loading() + : state = State.loading, + result = null, + error = null; + + const KeyDerivationState.failure(this.error) + : state = State.failure, + result = null; + + const KeyDerivationState.success(this.result) + : state = State.success, + error = null; + + final State state; + final Uint8List? result; + final String? error; +} diff --git a/packages/native_crypto/example/lib/presentation/kdf/state_management/key_derivation_state_management.dart b/packages/native_crypto/example/lib/presentation/kdf/state_management/key_derivation_state_management.dart new file mode 100644 index 0000000..fff27cc --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/kdf/state_management/key_derivation_state_management.dart @@ -0,0 +1,102 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/core/typography.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; +import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart'; +import 'package:native_crypto_example/presentation/kdf/blocs/key_derivation/key_derivation_cubit.dart'; +import 'package:native_crypto_example/presentation/output/widgets/logs.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class KeyDerivationStateManagement + extends CubitScreen { + KeyDerivationStateManagement({super.key}); + + final TextEditingController _pwdTextController = TextEditingController() + ..text = 'password'; + + @override + KeyDerivationCubit create(BuildContext context) => KeyDerivationCubit( + sessionRepository: repo(context), + loggerRepository: repo(context), + cryptoRepository: repo(context), + ); + + @override + Widget onBuild(BuildContext context, KeyDerivationState state) => ListView( + children: [ + const Logs(), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'Random secret key', + style: AppTypography.title, + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + '''A secret key is a piece of information that is used to encrypt and decrypt messages. In symmetric-key encryption, the same secret key is used for both encrypting the original data and for decrypting the encrypted data. The key is kept secret so that only authorized parties can read the encrypted messages.''', + style: AppTypography.body, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Generate secret key', + onPressed: () => bloc(context).generate(), + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'PBKDF2', + style: AppTypography.title, + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + '''PBKDF2 (Password-Based Key Derivation Function 2) is a key derivation function that is designed to be computationally expensive and memory-intensive, in order to slow down the process of guessing a password. This makes it more difficult for an attacker to guess a password by using a brute-force attack, which involves trying every possible combination of characters until the correct password is found.''', + style: AppTypography.body, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _pwdTextController, + decoration: const InputDecoration( + hintText: 'Password', + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Compute PBKDF2', + onPressed: () => bloc(context).pbkdf2(_pwdTextController.text), + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'Session', + style: AppTypography.title, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Retrieve session key', + onPressed: () => bloc(context).getSessionKey(), + ), + ), + ], + ); +} diff --git a/packages/native_crypto/example/lib/presentation/output/blocs/output/output_cubit.dart b/packages/native_crypto/example/lib/presentation/output/blocs/output/output_cubit.dart new file mode 100644 index 0000000..fd5cd43 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/output/blocs/output/output_cubit.dart @@ -0,0 +1,49 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +part 'output_state.dart'; + +class OutputCubit extends Cubit { + OutputCubit(this.loggerRepository) : super(const OutputState.empty()) { + logSubscription = loggerRepository.streamLogs().listen((message) { + if (message.isOk) { + onMessage(message.ok!); + } + }); + } + + final LoggerRepository loggerRepository; + late StreamSubscription, AppException>> + logSubscription; + + FutureOr add(LogMessage message) { + loggerRepository.addLog(message); + } + + FutureOr clear() { + loggerRepository.clearLog(); + emit(const OutputState.empty()); + } + + FutureOr onMessage(Map entries) { + emit(OutputState(entries: entries)); + } + + @override + Future close() { + logSubscription.cancel(); + return super.close(); + } +} diff --git a/packages/native_crypto/example/lib/presentation/output/blocs/output/output_state.dart b/packages/native_crypto/example/lib/presentation/output/blocs/output/output_state.dart new file mode 100644 index 0000000..6cb82c3 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/output/blocs/output/output_state.dart @@ -0,0 +1,32 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +part of 'output_cubit.dart'; + +@immutable +class OutputState { + const OutputState({ + required this.entries, + }); + + const OutputState.empty() : entries = const {}; + + final Map entries; + + @override + String toString() { + final StringBuffer buffer = StringBuffer(); + entries.forEach((key, value) { + buffer + ..write(value.prefix) + ..write(' at ') + ..write(key.toIso8601String()) + ..write(':\t') + ..writeln(value.message); + }); + return buffer.toString(); + } +} diff --git a/packages/native_crypto/example/lib/presentation/output/state_management/output_state_management.dart b/packages/native_crypto/example/lib/presentation/output/state_management/output_state_management.dart new file mode 100644 index 0000000..1512987 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/output/state_management/output_state_management.dart @@ -0,0 +1,76 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/presentation/output/blocs/output/output_cubit.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class OutputStateManagement + extends CubitConsumerScreen { + OutputStateManagement({super.key}); + + final ScrollController _scrollController = ScrollController(); + + @override + Widget onBuild(BuildContext context, OutputState state) { + // Auto scroll + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_scrollController.hasClients) { + _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 100), + curve: Curves.easeOut, + ); + } + }); + + return SizedBox( + height: 250, + child: ListView.separated( + controller: _scrollController, + itemCount: state.entries.length, + separatorBuilder: (context, index) => const Padding( + padding: EdgeInsets.all(8), + child: SizedBox( + height: 1, + child: ColoredBox(color: Colors.black), + ), + ), + itemBuilder: (context, index) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + state.entries.values.elementAt(index).prefix, + style: TextStyle( + color: state.entries.values.elementAt(index).color, + ), + ), + Text( + state.entries.keys.elementAt(index).toIso8601String(), + style: TextStyle( + color: state.entries.values.elementAt(index).color, + ), + ) + ], + ), + Align( + alignment: Alignment.centerLeft, + child: SelectableText( + state.entries.values.elementAt(index).message, + style: TextStyle( + color: state.entries.values.elementAt(index).color, + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/packages/native_crypto/example/lib/presentation/output/state_management/widgets/clear_button_state_management.dart b/packages/native_crypto/example/lib/presentation/output/state_management/widgets/clear_button_state_management.dart new file mode 100644 index 0000000..ecbc128 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/output/state_management/widgets/clear_button_state_management.dart @@ -0,0 +1,22 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart'; +import 'package:native_crypto_example/presentation/output/blocs/output/output_cubit.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class ClearButtonStateManagement + extends CubitConsumerScreen { + const ClearButtonStateManagement({super.key}); + + @override + Widget onBuild(BuildContext context, OutputState state) => + ButtonStateManagement( + label: 'Clear', + onPressed: () => bloc(context).clear(), + ); +} diff --git a/packages/native_crypto/example/lib/presentation/output/widgets/logs.dart b/packages/native_crypto/example/lib/presentation/output/widgets/logs.dart new file mode 100644 index 0000000..a623e1a --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/output/widgets/logs.dart @@ -0,0 +1,44 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/core/typography.dart'; +import 'package:native_crypto_example/presentation/output/state_management/output_state_management.dart'; +import 'package:native_crypto_example/presentation/output/state_management/widgets/clear_button_state_management.dart'; + +class Logs extends StatelessWidget { + const Logs({super.key}); + + @override + Widget build(BuildContext context) => Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: const [ + Text( + 'Logs', + style: AppTypography.title, + ), + ClearButtonStateManagement(), + ], + ), + ), + const SizedBox( + height: 2, + width: double.infinity, + child: ColoredBox(color: Colors.black), + ), + OutputStateManagement(), + const SizedBox( + height: 2, + width: double.infinity, + child: ColoredBox(color: Colors.black), + ), + ], + ); +} diff --git a/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart b/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart new file mode 100644 index 0000000..2d84265 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart @@ -0,0 +1,204 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:native_crypto_example/domain/entities/states.dart'; +import 'package:native_crypto_example/domain/entities/test_vector.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; + +part 'test_vectors_state.dart'; + +class TestVectorsCubit extends Cubit { + TestVectorsCubit({ + required this.loggerRepository, + required this.cryptoRepository, + }) : super(const TestVectorsState.initial()); + final LoggerRepository loggerRepository; + final CryptoRepository cryptoRepository; + + final tests = [ + TestVector( + key: 'b52c505a37d78eda5dd34f20c22540ea1b58963cf8e5bf8ffa85f9f2492505b4', + nonce: '516c33929df5a3284ff463d7', + plainText: '', + cipherText: '', + tag: 'bdc1ac884d332457a1d2664f168c76f0', + ), + TestVector( + key: '5fe0861cdc2690ce69b3658c7f26f8458eec1c9243c5ba0845305d897e96ca0f', + nonce: '770ac1a5a3d476d5d96944a1', + plainText: '', + cipherText: '', + tag: '196d691e1047093ca4b3d2ef4baba216', + ), + TestVector( + key: '7620b79b17b21b06d97019aa70e1ca105e1c03d2a0cf8b20b5a0ce5c3903e548', + nonce: '60f56eb7a4b38d4f03395511', + plainText: '', + cipherText: '', + tag: 'f570c38202d94564bab39f75617bc87a', + ), + TestVector( + key: '31bdadd96698c204aa9ce1448ea94ae1fb4a9a0b3c9d773b51bb1822666b8f22', + nonce: '0d18e06c7c725ac9e362e1ce', + plainText: '2db5168e932556f8089a0622981d017d', + cipherText: 'fa4362189661d163fcd6a56d8bf0405a', + tag: 'd636ac1bbedd5cc3ee727dc2ab4a9489', + ), + TestVector( + key: '460fc864972261c2560e1eb88761ff1c992b982497bd2ac36c04071cbb8e5d99', + nonce: '8a4a16b9e210eb68bcb6f58d', + plainText: '99e4e926ffe927f691893fb79a96b067', + cipherText: '133fc15751621b5f325c7ff71ce08324', + tag: 'ec4e87e0cf74a13618d0b68636ba9fa7', + ), + TestVector( + key: 'f78a2ba3c5bd164de134a030ca09e99463ea7e967b92c4b0a0870796480297e5', + nonce: '2bb92fcb726c278a2fa35a88', + plainText: 'f562509ed139a6bbe7ab545ac616250c', + cipherText: 'e2f787996e37d3b47294bf7ebba5ee25', + tag: '00f613eee9bdad6c9ee7765db1cb45c0', + ), + ]; + + FutureOr launchAllTests() async { + emit(const TestVectorsState.loading()); + int i = 0; + for (final test in tests) { + final sk = SecretKey(test.keyBytes); + final iv = test.nonceBytes; + + // Encryption + final encryption = + await cryptoRepository.encryptWithIV(test.plainTextBytes, sk, iv); + final testCipherText = await encryption + .foldAsync((cipherText) async => cipherText, (error) async { + await loggerRepository.addLog( + LogError( + 'TEST $i :: ${error.message ?? 'Error during encryption.'} ❌', + ), + ); + }); + + final encryptionSuccess = test.validateCipherText(testCipherText); + + if (encryptionSuccess) { + await loggerRepository.addLog( + LogInfo('TEST $i :: Encryption test passed ✅'), + ); + } else { + await loggerRepository.addLog( + LogError('TEST $i :: Encryption test failed ❌'), + ); + } + + // Decryption + final decryption = + await cryptoRepository.decrypt(test.cipherTextBytes, sk); + final testPlainText = await decryption + .foldAsync((plainText) async => plainText, (error) async { + await loggerRepository.addLog( + LogError( + 'TEST $i :: ${error.message ?? 'Error during decryption. ❌'}', + ), + ); + }); + + final decryptionSuccess = test.validatePlainText(testPlainText); + + if (decryptionSuccess) { + await loggerRepository.addLog( + LogInfo('TEST $i :: Decryption test passed ✅'), + ); + } else { + await loggerRepository.addLog( + LogError('TEST $i :: Decryption test failed ❌'), + ); + } + i++; + } + + if (i == tests.length) { + emit(const TestVectorsState.success()); + } else { + emit(const TestVectorsState.failure('Error during the test suite')); + } + + return; + } + + FutureOr launchTest(String number) async { + try { + final id = int.parse(number); + + final test = tests.elementAt(id); + + final sk = SecretKey(test.keyBytes); + final iv = test.nonceBytes; + + // Encryption + final encryption = + await cryptoRepository.encryptWithIV(test.plainTextBytes, sk, iv); + final testCipherText = await encryption + .foldAsync((cipherText) async => cipherText, (error) async { + await loggerRepository.addLog( + LogError( + 'TEST $id :: ${error.message ?? 'Error during encryption.'} ❌', + ), + ); + }); + + final encryptionSuccess = test.validateCipherText(testCipherText); + + if (encryptionSuccess) { + await loggerRepository.addLog( + LogInfo('TEST $id :: Encryption test passed ✅'), + ); + } else { + await loggerRepository.addLog( + LogError('TEST $id :: Encryption test failed ❌'), + ); + } + + // Decryption + final decryption = + await cryptoRepository.decrypt(test.cipherTextBytes, sk); + final testPlainText = await decryption + .foldAsync((plainText) async => plainText, (error) async { + await loggerRepository.addLog( + LogError( + 'TEST $id :: ${error.message ?? 'Error during decryption. ❌'}', + ), + ); + }); + + final decryptionSuccess = test.validatePlainText(testPlainText); + + if (decryptionSuccess) { + await loggerRepository.addLog( + LogInfo('TEST $id :: Decryption test passed ✅'), + ); + } else { + await loggerRepository.addLog( + LogError('TEST $id :: Decryption test failed ❌'), + ); + } + emit(const TestVectorsState.success()); + } catch (e) { + final error = 'Invalid vector number: $number'; + await loggerRepository.addLog(LogError(error)); + emit(TestVectorsState.failure(error)); + } + + return; + } +} diff --git a/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_state.dart b/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_state.dart new file mode 100644 index 0000000..7b06271 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/test_vectors/blocs/test_vectors/test_vectors_state.dart @@ -0,0 +1,27 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +part of 'test_vectors_cubit.dart'; + +@immutable +class TestVectorsState { + const TestVectorsState.initial() + : state = State.initial, + error = null; + + const TestVectorsState.loading() + : state = State.loading, + error = null; + + const TestVectorsState.failure(this.error) : state = State.failure; + + const TestVectorsState.success() + : state = State.success, + error = null; + + final State state; + final String? error; +} diff --git a/packages/native_crypto/example/lib/presentation/test_vectors/state_management/test_vectors_state_management.dart b/packages/native_crypto/example/lib/presentation/test_vectors/state_management/test_vectors_state_management.dart new file mode 100644 index 0000000..282b52e --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/test_vectors/state_management/test_vectors_state_management.dart @@ -0,0 +1,74 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/core/typography.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart'; +import 'package:native_crypto_example/presentation/output/widgets/logs.dart'; +import 'package:native_crypto_example/presentation/test_vectors/blocs/test_vectors/test_vectors_cubit.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class TestVectorsStateManagement + extends CubitScreen { + TestVectorsStateManagement({super.key}); + + final TextEditingController _vectorNumberTextController = + TextEditingController(); + + @override + TestVectorsCubit create(BuildContext context) => TestVectorsCubit( + loggerRepository: repo(context), + cryptoRepository: repo(context), + ); + + @override + Widget onBuild(BuildContext context, TestVectorsState state) => ListView( + children: [ + const Logs(), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'Test vectors', + style: AppTypography.title, + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + '''In cryptography, a test vector is a set of input values that is used to test the correct implementation and operation of a cryptographic algorithm or system. Test vectors are usually provided by the creators of the algorithm, or by national standardization organizations, and can be used to verify that an implementation of the algorithm is correct and produces the expected output for a given set of inputs.''', + style: AppTypography.body, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'All Tests', + onPressed: () => bloc(context).launchAllTests(), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: TextField( + controller: _vectorNumberTextController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + hintText: 'Vector number', + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Specific Test', + onPressed: () => + bloc(context).launchTest(_vectorNumberTextController.text), + ), + ), + ], + ); +} diff --git a/packages/native_crypto/example/lib/session.dart b/packages/native_crypto/example/lib/session.dart deleted file mode 100644 index 7636866..0000000 --- a/packages/native_crypto/example/lib/session.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: session.dart -// Created Date: 28/12/2021 13:54:29 -// Last Modified: 28/12/2021 13:58:49 -// ----- -// Copyright (c) 2021 - -import 'dart:typed_data'; - -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:native_crypto/native_crypto.dart'; - -class Session { - SecretKey secretKey; - Session() : secretKey = SecretKey(Uint8List(0)); - - void setKey(SecretKey sk) { - secretKey = sk; - } -} - -// Providers - -final sessionProvider = StateProvider((ref) => Session()); diff --git a/packages/native_crypto/example/lib/widgets/button.dart b/packages/native_crypto/example/lib/widgets/button.dart deleted file mode 100644 index 2244bb4..0000000 --- a/packages/native_crypto/example/lib/widgets/button.dart +++ /dev/null @@ -1,31 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: button.dart -// Created Date: 28/12/2021 13:31:17 -// Last Modified: 28/12/2021 13:31:34 -// ----- -// Copyright (c) 2021 - -import 'package:flutter/material.dart'; - -class Button extends StatelessWidget { - final void Function() onPressed; - final String label; - - const Button(this.onPressed, this.label, {Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ElevatedButton( - onPressed: onPressed, - style: TextButton.styleFrom( - primary: Colors.blue, - ), - child: Text( - label, - style: const TextStyle(color: Colors.white), - ), - ); - } -} diff --git a/packages/native_crypto/example/lib/widgets/output.dart b/packages/native_crypto/example/lib/widgets/output.dart deleted file mode 100644 index 1dc9b70..0000000 --- a/packages/native_crypto/example/lib/widgets/output.dart +++ /dev/null @@ -1,61 +0,0 @@ -// Author: Hugo Pointcheval -// Email: git@pcl.ovh -// ----- -// File: output.dart -// Created Date: 28/12/2021 13:31:39 -// Last Modified: 25/05/2022 16:39:39 -// ----- -// Copyright (c) 2021 - -import 'package:flutter/material.dart'; - -// ignore: must_be_immutable -class Output extends StatelessWidget { - late TextEditingController controller; - final bool large; - final bool editable; - - Output({ - Key? key, - TextEditingController? controller, - this.large = false, - this.editable = false, - }) : super(key: key) { - this.controller = controller ?? TextEditingController(); - } - - void print(String message) { - debugPrint(message); - controller.text = message; - } - - void append(String message) { - debugPrint(message); - controller.text += message; - } - - void appendln(String message) { - debugPrint(message); - controller.text += message + "\n"; - } - - void clear() { - controller.clear(); - } - - String read() { - return controller.text; - } - - @override - Widget build(BuildContext context) { - return TextField( - enableInteractiveSelection: true, - readOnly: editable ? false : true, - minLines: large ? 3 : 1, - maxLines: large ? 500 : 5, - decoration: const InputDecoration(border: OutlineInputBorder()), - controller: controller, - ); - } -} diff --git a/packages/native_crypto/example/pubspec.yaml b/packages/native_crypto/example/pubspec.yaml index 1e16cd6..73185a7 100644 --- a/packages/native_crypto/example/pubspec.yaml +++ b/packages/native_crypto/example/pubspec.yaml @@ -3,10 +3,10 @@ description: Demonstrates how to use the native_crypto plugin. # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.15.0 <3.0.0" + sdk: ">=2.17.0 <3.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -15,40 +15,36 @@ environment: # the latest version available on pub.dev. To see which dependencies have newer # versions available, run `flutter pub outdated`. dependencies: - flutter: - sdk: flutter + flutter: { sdk: flutter } + native_crypto: { path: ../ } + cupertino_icons: ^1.0.5 + flutter_riverpod: ^2.1.3 + pointycastle: ^3.6.2 + flutter_bloc: ^8.1.1 - native_crypto: - # When depending on this package from a real application you should use: - # native_crypto: ^x.y.z - # See https://dart.dev/tools/pub/dependencies#version-constraints - # The example app is bundled with the plugin so we use a path dependency on - # the parent directory to use the current plugin's version. - path: ../ - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - flutter_riverpod: ^1.0.3 - pointycastle: ^3.6.0 + wyatt_architecture: + hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ + version: 0.1.0+1 + wyatt_type_utils: + hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ + version: 0.0.4 + wyatt_bloc_helper: + hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ + version: 2.0.0 + get_it: ^7.2.0 dev_dependencies: - flutter_test: - sdk: flutter + flutter_test: { sdk: flutter } - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^2.0.0 + wyatt_analysis: + hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ + version: 2.4.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. diff --git a/packages/native_crypto/example/pubspec_overrides.yaml b/packages/native_crypto/example/pubspec_overrides.yaml new file mode 100644 index 0000000..f4eef6c --- /dev/null +++ b/packages/native_crypto/example/pubspec_overrides.yaml @@ -0,0 +1,12 @@ +# melos_managed_dependency_overrides: native_crypto,native_crypto_android,native_crypto_ios,native_crypto_platform_interface,native_crypto_web +dependency_overrides: + native_crypto: + path: .. + native_crypto_android: + path: ../../native_crypto_android + native_crypto_ios: + path: ../../native_crypto_ios + native_crypto_platform_interface: + path: ../../native_crypto_platform_interface + native_crypto_web: + path: ../../native_crypto_web diff --git a/packages/native_crypto/example/web/favicon.png b/packages/native_crypto/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/native_crypto/example/web/icons/Icon-192.png b/packages/native_crypto/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 zcmZ`-2T+sGz6~)*FVZ`aW+(v>MIm&M-g^@e2u-B-DoB?qO+b1Tq<5uCCv>ESfRum& zp%X;f!~1{tzL__3=gjVJ=j=J>+nMj%ncXj1Q(b|Ckbw{Y0FWpt%4y%$uD=Z*c-x~o zE;IoE;xa#7Ll5nj-e4CuXB&G*IM~D21rCP$*xLXAK8rIMCSHuSu%bL&S3)8YI~vyp@KBu9Ph7R_pvKQ@xv>NQ`dZp(u{Z8K3yOB zn7-AR+d2JkW)KiGx0hosml;+eCXp6+w%@STjFY*CJ?udJ64&{BCbuebcuH;}(($@@ znNlgBA@ZXB)mcl9nbX#F!f_5Z=W>0kh|UVWnf!At4V*LQP%*gPdCXd6P@J4Td;!Ur z<2ZLmwr(NG`u#gDEMP19UcSzRTL@HsK+PnIXbVBT@oHm53DZr?~V(0{rsalAfwgo zEh=GviaqkF;}F_5-yA!1u3!gxaR&Mj)hLuj5Q-N-@Lra{%<4ONja8pycD90&>yMB` zchhd>0CsH`^|&TstH-8+R`CfoWqmTTF_0?zDOY`E`b)cVi!$4xA@oO;SyOjJyP^_j zx^@Gdf+w|FW@DMdOi8=4+LJl$#@R&&=UM`)G!y%6ZzQLoSL%*KE8IO0~&5XYR9 z&N)?goEiWA(YoRfT{06&D6Yuu@Qt&XVbuW@COb;>SP9~aRc+z`m`80pB2o%`#{xD@ zI3RAlukL5L>px6b?QW1Ac_0>ew%NM!XB2(H+1Y3AJC?C?O`GGs`331Nd4ZvG~bMo{lh~GeL zSL|tT*fF-HXxXYtfu5z+T5Mx9OdP7J4g%@oeC2FaWO1D{=NvL|DNZ}GO?O3`+H*SI z=grGv=7dL{+oY0eJFGO!Qe(e2F?CHW(i!!XkGo2tUvsQ)I9ev`H&=;`N%Z{L zO?vV%rDv$y(@1Yj@xfr7Kzr<~0{^T8wM80xf7IGQF_S-2c0)0D6b0~yD7BsCy+(zL z#N~%&e4iAwi4F$&dI7x6cE|B{f@lY5epaDh=2-(4N05VO~A zQT3hanGy_&p+7Fb^I#ewGsjyCEUmSCaP6JDB*=_()FgQ(-pZ28-{qx~2foO4%pM9e z*_63RT8XjgiaWY|*xydf;8MKLd{HnfZ2kM%iq}fstImB-K6A79B~YoPVa@tYN@T_$ zea+9)<%?=Fl!kd(Y!G(-o}ko28hg2!MR-o5BEa_72uj7Mrc&{lRh3u2%Y=Xk9^-qa zBPWaD=2qcuJ&@Tf6ue&)4_V*45=zWk@Z}Q?f5)*z)-+E|-yC4fs5CE6L_PH3=zI8p z*Z3!it{1e5_^(sF*v=0{`U9C741&lub89gdhKp|Y8CeC{_{wYK-LSbp{h)b~9^j!s z7e?Y{Z3pZv0J)(VL=g>l;<}xk=T*O5YR|hg0eg4u98f2IrA-MY+StQIuK-(*J6TRR z|IM(%uI~?`wsfyO6Tgmsy1b3a)j6M&-jgUjVg+mP*oTKdHg?5E`!r`7AE_#?Fc)&a z08KCq>Gc=ne{PCbRvs6gVW|tKdcE1#7C4e`M|j$C5EYZ~Y=jUtc zj`+?p4ba3uy7><7wIokM79jPza``{Lx0)zGWg;FW1^NKY+GpEi=rHJ+fVRGfXO zPHV52k?jxei_!YYAw1HIz}y8ZMwdZqU%ESwMn7~t zdI5%B;U7RF=jzRz^NuY9nM)&<%M>x>0(e$GpU9th%rHiZsIT>_qp%V~ILlyt^V`=d z!1+DX@ah?RnB$X!0xpTA0}lN@9V-ePx>wQ?-xrJr^qDlw?#O(RsXeAvM%}rg0NT#t z!CsT;-vB=B87ShG`GwO;OEbeL;a}LIu=&@9cb~Rsx(ZPNQ!NT7H{@j0e(DiLea>QD zPmpe90gEKHEZ8oQ@6%E7k-Ptn#z)b9NbD@_GTxEhbS+}Bb74WUaRy{w;E|MgDAvHw zL)ycgM7mB?XVh^OzbC?LKFMotw3r@i&VdUV%^Efdib)3@soX%vWCbnOyt@Y4swW925@bt45y0HY3YI~BnnzZYrinFy;L?2D3BAL`UQ zEj))+f>H7~g8*VuWQ83EtGcx`hun$QvuurSMg3l4IP8Fe`#C|N6mbYJ=n;+}EQm;< z!!N=5j1aAr_uEnnzrEV%_E|JpTb#1p1*}5!Ce!R@d$EtMR~%9# zd;h8=QGT)KMW2IKu_fA_>p_und#-;Q)p%%l0XZOXQicfX8M~7?8}@U^ihu;mizj)t zgV7wk%n-UOb z#!P5q?Ex+*Kx@*p`o$q8FWL*E^$&1*!gpv?Za$YO~{BHeGY*5%4HXUKa_A~~^d z=E*gf6&+LFF^`j4$T~dR)%{I)T?>@Ma?D!gi9I^HqvjPc3-v~=qpX1Mne@*rzT&Xw zQ9DXsSV@PqpEJO-g4A&L{F&;K6W60D!_vs?Vx!?w27XbEuJJP&);)^+VF1nHqHBWu z^>kI$M9yfOY8~|hZ9WB!q-9u&mKhEcRjlf2nm_@s;0D#c|@ED7NZE% zzR;>P5B{o4fzlfsn3CkBK&`OSb-YNrqx@N#4CK!>bQ(V(D#9|l!e9(%sz~PYk@8zt zPN9oK78&-IL_F zhsk1$6p;GqFbtB^ZHHP+cjMvA0(LqlskbdYE_rda>gvQLTiqOQ1~*7lg%z*&p`Ry& zRcG^DbbPj_jOKHTr8uk^15Boj6>hA2S-QY(W-6!FIq8h$<>MI>PYYRenQDBamO#Fv zAH5&ImqKBDn0v5kb|8i0wFhUBJTpT!rB-`zK)^SNnRmLraZcPYK7b{I@+}wXVdW-{Ps17qdRA3JatEd?rPV z4@}(DAMf5EqXCr4-B+~H1P#;t@O}B)tIJ(W6$LrK&0plTmnPpb1TKn3?f?Kk``?D+ zQ!MFqOX7JbsXfQrz`-M@hq7xlfNz;_B{^wbpG8des56x(Q)H)5eLeDwCrVR}hzr~= zM{yXR6IM?kXxauLza#@#u?Y|o;904HCqF<8yT~~c-xyRc0-vxofnxG^(x%>bj5r}N zyFT+xnn-?B`ohA>{+ZZQem=*Xpqz{=j8i2TAC#x-m;;mo{{sLB_z(UoAqD=A#*juZ zCv=J~i*O8;F}A^Wf#+zx;~3B{57xtoxC&j^ie^?**T`WT2OPRtC`xj~+3Kprn=rVM zVJ|h5ux%S{dO}!mq93}P+h36mZ5aZg1-?vhL$ke1d52qIiXSE(llCr5i=QUS?LIjc zV$4q=-)aaR4wsrQv}^shL5u%6;`uiSEs<1nG^?$kl$^6DL z43CjY`M*p}ew}}3rXc7Xck@k41jx}c;NgEIhKZ*jsBRZUP-x2cm;F1<5$jefl|ppO zmZd%%?gMJ^g9=RZ^#8Mf5aWNVhjAS^|DQO+q$)oeob_&ZLFL(zur$)); zU19yRm)z<4&4-M}7!9+^Wl}Uk?`S$#V2%pQ*SIH5KI-mn%i;Z7-)m$mN9CnI$G7?# zo`zVrUwoSL&_dJ92YhX5TKqaRkfPgC4=Q&=K+;_aDs&OU0&{WFH}kKX6uNQC6%oUH z2DZa1s3%Vtk|bglbxep-w)PbFG!J17`<$g8lVhqD2w;Z0zGsh-r zxZ13G$G<48leNqR!DCVt9)@}(zMI5w6Wo=N zpP1*3DI;~h2WDWgcKn*f!+ORD)f$DZFwgKBafEZmeXQMAsq9sxP9A)7zOYnkHT9JU zRA`umgmP9d6=PHmFIgx=0$(sjb>+0CHG)K@cPG{IxaJ&Ueo8)0RWgV9+gO7+Bl1(F z7!BslJ2MP*PWJ;x)QXbR$6jEr5q3 z(3}F@YO_P1NyTdEXRLU6fp?9V2-S=E+YaeLL{Y)W%6`k7$(EW8EZSA*(+;e5@jgD^I zaJQ2|oCM1n!A&-8`;#RDcZyk*+RPkn_r8?Ak@agHiSp*qFNX)&i21HE?yuZ;-C<3C zwJGd1lx5UzViP7sZJ&|LqH*mryb}y|%AOw+v)yc`qM)03qyyrqhX?ub`Cjwx2PrR! z)_z>5*!*$x1=Qa-0uE7jy0z`>|Ni#X+uV|%_81F7)b+nf%iz=`fF4g5UfHS_?PHbr zB;0$bK@=di?f`dS(j{l3-tSCfp~zUuva+=EWxJcRfp(<$@vd(GigM&~vaYZ0c#BTs z3ijkxMl=vw5AS&DcXQ%eeKt!uKvh2l3W?&3=dBHU=Gz?O!40S&&~ei2vg**c$o;i89~6DVns zG>9a*`k5)NI9|?W!@9>rzJ;9EJ=YlJTx1r1BA?H`LWijk(rTax9(OAu;q4_wTj-yj z1%W4GW&K4T=uEGb+E!>W0SD_C0RR91 literal 0 HcmV?d00001 diff --git a/packages/native_crypto/example/web/icons/Icon-512.png b/packages/native_crypto/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 zcmd5=2T+s!lYZ%-(h(2@5fr2dC?F^$C=i-}R6$UX8af(!je;W5yC_|HmujSgN*6?W z3knF*TL1$|?oD*=zPbBVex*RUIKsL<(&Rj9%^UD2IK3W?2j>D?eWQgvS-HLymHo9%~|N2Q{~j za?*X-{b9JRowv_*Mh|;*-kPFn>PI;r<#kFaxFqbn?aq|PduQg=2Q;~Qc}#z)_T%x9 zE|0!a70`58wjREmAH38H1)#gof)U3g9FZ^ zF7&-0^Hy{4XHWLoC*hOG(dg~2g6&?-wqcpf{ z&3=o8vw7lMi22jCG9RQbv8H}`+}9^zSk`nlR8?Z&G2dlDy$4#+WOlg;VHqzuE=fM@ z?OI6HEJH4&tA?FVG}9>jAnq_^tlw8NbjNhfqk2rQr?h(F&WiKy03Sn=-;ZJRh~JrD zbt)zLbnabttEZ>zUiu`N*u4sfQaLE8-WDn@tHp50uD(^r-}UsUUu)`!Rl1PozAc!a z?uj|2QDQ%oV-jxUJmJycySBINSKdX{kDYRS=+`HgR2GO19fg&lZKyBFbbXhQV~v~L za^U944F1_GtuFXtvDdDNDvp<`fqy);>Vw=ncy!NB85Tw{&sT5&Ox%-p%8fTS;OzlRBwErvO+ROe?{%q-Zge=%Up|D4L#>4K@Ke=x%?*^_^P*KD zgXueMiS63!sEw@fNLB-i^F|@Oib+S4bcy{eu&e}Xvb^(mA!=U=Xr3||IpV~3K zQWzEsUeX_qBe6fky#M zzOJm5b+l;~>=sdp%i}}0h zO?B?i*W;Ndn02Y0GUUPxERG`3Bjtj!NroLoYtyVdLtl?SE*CYpf4|_${ku2s`*_)k zN=a}V8_2R5QANlxsq!1BkT6$4>9=-Ix4As@FSS;1q^#TXPrBsw>hJ}$jZ{kUHoP+H zvoYiR39gX}2OHIBYCa~6ERRPJ#V}RIIZakUmuIoLF*{sO8rAUEB9|+A#C|@kw5>u0 zBd=F!4I)Be8ycH*)X1-VPiZ+Ts8_GB;YW&ZFFUo|Sw|x~ZajLsp+_3gv((Q#N>?Jz zFBf`~p_#^${zhPIIJY~yo!7$-xi2LK%3&RkFg}Ax)3+dFCjGgKv^1;lUzQlPo^E{K zmCnrwJ)NuSaJEmueEPO@(_6h3f5mFffhkU9r8A8(JC5eOkux{gPmx_$Uv&|hyj)gN zd>JP8l2U&81@1Hc>#*su2xd{)T`Yw< zN$dSLUN}dfx)Fu`NcY}TuZ)SdviT{JHaiYgP4~@`x{&h*Hd>c3K_To9BnQi@;tuoL z%PYQo&{|IsM)_>BrF1oB~+`2_uZQ48z9!)mtUR zdfKE+b*w8cPu;F6RYJiYyV;PRBbThqHBEu_(U{(gGtjM}Zi$pL8Whx}<JwE3RM0F8x7%!!s)UJVq|TVd#hf1zVLya$;mYp(^oZQ2>=ZXU1c$}f zm|7kfk>=4KoQoQ!2&SOW5|JP1)%#55C$M(u4%SP~tHa&M+=;YsW=v(Old9L3(j)`u z2?#fK&1vtS?G6aOt@E`gZ9*qCmyvc>Ma@Q8^I4y~f3gs7*d=ATlP>1S zyF=k&6p2;7dn^8?+!wZO5r~B+;@KXFEn^&C=6ma1J7Au6y29iMIxd7#iW%=iUzq&C=$aPLa^Q zncia$@TIy6UT@69=nbty5epP>*fVW@5qbUcb2~Gg75dNd{COFLdiz3}kODn^U*=@E z0*$7u7Rl2u)=%fk4m8EK1ctR!6%Ve`e!O20L$0LkM#f+)n9h^dn{n`T*^~d+l*Qlx z$;JC0P9+en2Wlxjwq#z^a6pdnD6fJM!GV7_%8%c)kc5LZs_G^qvw)&J#6WSp< zmsd~1-(GrgjC56Pdf6#!dt^y8Rg}!#UXf)W%~PeU+kU`FeSZHk)%sFv++#Dujk-~m zFHvVJC}UBn2jN& zs!@nZ?e(iyZPNo`p1i#~wsv9l@#Z|ag3JR>0#u1iW9M1RK1iF6-RbJ4KYg?B`dET9 zyR~DjZ>%_vWYm*Z9_+^~hJ_|SNTzBKx=U0l9 z9x(J96b{`R)UVQ$I`wTJ@$_}`)_DyUNOso6=WOmQKI1e`oyYy1C&%AQU<0-`(ow)1 zT}gYdwWdm4wW6|K)LcfMe&psE0XGhMy&xS`@vLi|1#Za{D6l@#D!?nW87wcscUZgELT{Cz**^;Zb~7 z(~WFRO`~!WvyZAW-8v!6n&j*PLm9NlN}BuUN}@E^TX*4Or#dMMF?V9KBeLSiLO4?B zcE3WNIa-H{ThrlCoN=XjOGk1dT=xwwrmt<1a)mrRzg{35`@C!T?&_;Q4Ce=5=>z^*zE_c(0*vWo2_#TD<2)pLXV$FlwP}Ik74IdDQU@yhkCr5h zn5aa>B7PWy5NQ!vf7@p_qtC*{dZ8zLS;JetPkHi>IvPjtJ#ThGQD|Lq#@vE2xdl%`x4A8xOln}BiQ92Po zW;0%A?I5CQ_O`@Ad=`2BLPPbBuPUp@Hb%a_OOI}y{Rwa<#h z5^6M}s7VzE)2&I*33pA>e71d78QpF>sNK;?lj^Kl#wU7G++`N_oL4QPd-iPqBhhs| z(uVM}$ItF-onXuuXO}o$t)emBO3Hjfyil@*+GF;9j?`&67GBM;TGkLHi>@)rkS4Nj zAEk;u)`jc4C$qN6WV2dVd#q}2X6nKt&X*}I@jP%Srs%%DS92lpDY^K*Sx4`l;aql$ zt*-V{U&$DM>pdO?%jt$t=vg5|p+Rw?SPaLW zB6nvZ69$ne4Z(s$3=Rf&RX8L9PWMV*S0@R zuIk&ba#s6sxVZ51^4Kon46X^9`?DC9mEhWB3f+o4#2EXFqy0(UTc>GU| zGCJmI|Dn-dX#7|_6(fT)>&YQ0H&&JX3cTvAq(a@ydM4>5Njnuere{J8p;3?1az60* z$1E7Yyxt^ytULeokgDnRVKQw9vzHg1>X@@jM$n$HBlveIrKP5-GJq%iWH#odVwV6cF^kKX(@#%%uQVb>#T6L^mC@)%SMd4DF? zVky!~ge27>cpUP1Vi}Z32lbLV+CQy+T5Wdmva6Fg^lKb!zrg|HPU=5Qu}k;4GVH+x z%;&pN1LOce0w@9i1Mo-Y|7|z}fbch@BPp2{&R-5{GLoeu8@limQmFF zaJRR|^;kW_nw~0V^ zfTnR!Ni*;-%oSHG1yItARs~uxra|O?YJxBzLjpeE-=~TO3Dn`JL5Gz;F~O1u3|FE- zvK2Vve`ylc`a}G`gpHg58Cqc9fMoy1L}7x7T>%~b&irrNMo?np3`q;d3d;zTK>nrK zOjPS{@&74-fA7j)8uT9~*g23uGnxwIVj9HorzUX#s0pcp2?GH6i}~+kv9fWChtPa_ z@T3m+$0pbjdQw7jcnHn;Pi85hk_u2-1^}c)LNvjdam8K-XJ+KgKQ%!?2n_!#{$H|| zLO=%;hRo6EDmnOBKCL9Cg~ETU##@u^W_5joZ%Et%X_n##%JDOcsO=0VL|Lkk!VdRJ z^|~2pB@PUspT?NOeO?=0Vb+fAGc!j%Ufn-cB`s2A~W{Zj{`wqWq_-w0wr@6VrM zbzni@8c>WS!7c&|ZR$cQ;`niRw{4kG#e z70e!uX8VmP23SuJ*)#(&R=;SxGAvq|&>geL&!5Z7@0Z(No*W561n#u$Uc`f9pD70# z=sKOSK|bF~#khTTn)B28h^a1{;>EaRnHj~>i=Fnr3+Fa4 z`^+O5_itS#7kPd20rq66_wH`%?HNzWk@XFK0n;Z@Cx{kx==2L22zWH$Yg?7 zvDj|u{{+NR3JvUH({;b*$b(U5U z7(lF!1bz2%06+|-v(D?2KgwNw7( zJB#Tz+ZRi&U$i?f34m7>uTzO#+E5cbaiQ&L}UxyOQq~afbNB4EI{E04ZWg53w0A{O%qo=lF8d zf~ktGvIgf-a~zQoWf>loF7pOodrd0a2|BzwwPDV}ShauTK8*fmF6NRbO>Iw9zZU}u zw8Ya}?seBnEGQDmH#XpUUkj}N49tP<2jYwTFp!P+&Fd(%Z#yo80|5@zN(D{_pNow*&4%ql zW~&yp@scb-+Qj-EmErY+Tu=dUmf@*BoXY2&oKT8U?8?s1d}4a`Aq>7SV800m$FE~? zjmz(LY+Xx9sDX$;vU`xgw*jLw7dWOnWWCO8o|;}f>cu0Q&`0I{YudMn;P;L3R-uz# zfns_mZED_IakFBPP2r_S8XM$X)@O-xVKi4`7373Jkd5{2$M#%cRhWer3M(vr{S6>h zj{givZJ3(`yFL@``(afn&~iNx@B1|-qfYiZu?-_&Z8+R~v`d6R-}EX9IVXWO-!hL5 z*k6T#^2zAXdardU3Ao~I)4DGdAv2bx{4nOK`20rJo>rmk3S2ZDu}))8Z1m}CKigf0 z3L`3Y`{huj`xj9@`$xTZzZc3je?n^yG<8sw$`Y%}9mUsjUR%T!?k^(q)6FH6Af^b6 zlPg~IEwg0y;`t9y;#D+uz!oE4VP&Je!<#q*F?m5L5?J3i@!0J6q#eu z!RRU`-)HeqGi_UJZ(n~|PSNsv+Wgl{P-TvaUQ9j?ZCtvb^37U$sFpBrkT{7Jpd?HpIvj2!}RIq zH{9~+gErN2+}J`>Jvng2hwM`=PLNkc7pkjblKW|+Fk9rc)G1R>Ww>RC=r-|!m-u7( zc(a$9NG}w#PjWNMS~)o=i~WA&4L(YIW25@AL9+H9!?3Y}sv#MOdY{bb9j>p`{?O(P zIvb`n?_(gP2w3P#&91JX*md+bBEr%xUHMVqfB;(f?OPtMnAZ#rm5q5mh;a2f_si2_ z3oXWB?{NF(JtkAn6F(O{z@b76OIqMC$&oJ_&S|YbFJ*)3qVX_uNf5b8(!vGX19hsG z(OP>RmZp29KH9Ge2kKjKigUmOe^K_!UXP`von)PR8Qz$%=EmOB9xS(ZxE_tnyzo}7 z=6~$~9k0M~v}`w={AeqF?_)9q{m8K#6M{a&(;u;O41j)I$^T?lx5(zlebpY@NT&#N zR+1bB)-1-xj}R8uwqwf=iP1GbxBjneCC%UrSdSxK1vM^i9;bUkS#iRZw2H>rS<2<$ zNT3|sDH>{tXb=zq7XZi*K?#Zsa1h1{h5!Tq_YbKFm_*=A5-<~j63he;4`77!|LBlo zR^~tR3yxcU=gDFbshyF6>o0bdp$qmHS7D}m3;^QZq9kBBU|9$N-~oU?G5;jyFR7>z hN`IR97YZXIo@y!QgFWddJ3|0`sjFx!m))><{BI=FK%f8s literal 0 HcmV?d00001 diff --git a/packages/native_crypto/example/web/icons/Icon-maskable-192.png b/packages/native_crypto/example/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 zcmdT|`#%%j|KDb2V@0DPm$^(Lx5}lO%Yv(=e*7hl@QqKS50#~#^IQPxBmuh|i9sXnt4ch@VT0F7% zMtrs@KWIOo+QV@lSs66A>2pz6-`9Jk=0vv&u?)^F@HZ)-6HT=B7LF;rdj zskUyBfbojcX#CS>WrIWo9D=DIwcXM8=I5D{SGf$~=gh-$LwY?*)cD%38%sCc?5OsX z-XfkyL-1`VavZ?>(pI-xp-kYq=1hsnyP^TLb%0vKRSo^~r{x?ISLY1i7KjSp z*0h&jG(Rkkq2+G_6eS>n&6>&Xk+ngOMcYrk<8KrukQHzfx675^^s$~<@d$9X{VBbg z2Fd4Z%g`!-P}d#`?B4#S-9x*eNlOVRnDrn#jY@~$jfQ-~3Od;A;x-BI1BEDdvr`pI z#D)d)!2_`GiZOUu1crb!hqH=ezs0qk<_xDm_Kkw?r*?0C3|Io6>$!kyDl;eH=aqg$B zsH_|ZD?jP2dc=)|L>DZmGyYKa06~5?C2Lc0#D%62p(YS;%_DRCB1k(+eLGXVMe+=4 zkKiJ%!N6^mxqM=wq`0+yoE#VHF%R<{mMamR9o_1JH8jfnJ?NPLs$9U!9!dq8 z0B{dI2!M|sYGH&9TAY34OlpIsQ4i5bnbG>?cWwat1I13|r|_inLE?FS@Hxdxn_YZN z3jfUO*X9Q@?HZ>Q{W0z60!bbGh557XIKu1?)u|cf%go`pwo}CD=0tau-}t@R2OrSH zQzZr%JfYa`>2!g??76=GJ$%ECbQh7Q2wLRp9QoyiRHP7VE^>JHm>9EqR3<$Y=Z1K^SHuwxCy-5@z3 zVM{XNNm}yM*pRdLKp??+_2&!bp#`=(Lh1vR{~j%n;cJv~9lXeMv)@}Odta)RnK|6* zC+IVSWumLo%{6bLDpn)Gz>6r&;Qs0^+Sz_yx_KNz9Dlt^ax`4>;EWrIT#(lJ_40<= z750fHZ7hI{}%%5`;lwkI4<_FJw@!U^vW;igL0k+mK)-j zYuCK#mCDK3F|SC}tC2>m$ZCqNB7ac-0UFBJ|8RxmG@4a4qdjvMzzS&h9pQmu^x&*= zGvapd1#K%Da&)8f?<9WN`2H^qpd@{7In6DNM&916TRqtF4;3`R|Nhwbw=(4|^Io@T zIjoR?tB8d*sO>PX4vaIHF|W;WVl6L1JvSmStgnRQq zTX4(>1f^5QOAH{=18Q2Vc1JI{V=yOr7yZJf4Vpfo zeHXdhBe{PyY;)yF;=ycMW@Kb>t;yE>;f79~AlJ8k`xWucCxJfsXf2P72bAavWL1G#W z;o%kdH(mYCM{$~yw4({KatNGim49O2HY6O07$B`*K7}MvgI=4x=SKdKVb8C$eJseA$tmSFOztFd*3W`J`yIB_~}k%Sd_bPBK8LxH)?8#jM{^%J_0|L z!gFI|68)G}ex5`Xh{5pB%GtlJ{Z5em*e0sH+sU1UVl7<5%Bq+YrHWL7?X?3LBi1R@_)F-_OqI1Zv`L zb6^Lq#H^2@d_(Z4E6xA9Z4o3kvf78ZDz!5W1#Mp|E;rvJz&4qj2pXVxKB8Vg0}ek%4erou@QM&2t7Cn5GwYqy%{>jI z)4;3SAgqVi#b{kqX#$Mt6L8NhZYgonb7>+r#BHje)bvaZ2c0nAvrN3gez+dNXaV;A zmyR0z@9h4@6~rJik-=2M-T+d`t&@YWhsoP_XP-NsVO}wmo!nR~QVWU?nVlQjNfgcTzE-PkfIX5G z1?&MwaeuzhF=u)X%Vpg_e@>d2yZwxl6-r3OMqDn8_6m^4z3zG##cK0Fsgq8fcvmhu z{73jseR%X%$85H^jRAcrhd&k!i^xL9FrS7qw2$&gwAS8AfAk#g_E_tP;x66fS`Mn@SNVrcn_N;EQm z`Mt3Z%rw%hDqTH-s~6SrIL$hIPKL5^7ejkLTBr46;pHTQDdoErS(B>``t;+1+M zvU&Se9@T_BeK;A^p|n^krIR+6rH~BjvRIugf`&EuX9u69`9C?9ANVL8l(rY6#mu^i z=*5Q)-%o*tWl`#b8p*ZH0I}hn#gV%|jt6V_JanDGuekR*-wF`u;amTCpGG|1;4A5$ zYbHF{?G1vv5;8Ph5%kEW)t|am2_4ik!`7q{ymfHoe^Z99c|$;FAL+NbxE-_zheYbV z3hb0`uZGTsgA5TG(X|GVDSJyJxsyR7V5PS_WSnYgwc_D60m7u*x4b2D79r5UgtL18 zcCHWk+K6N1Pg2c;0#r-)XpwGX?|Iv)^CLWqwF=a}fXUSM?n6E;cCeW5ER^om#{)Jr zJR81pkK?VoFm@N-s%hd7@hBS0xuCD0-UDVLDDkl7Ck=BAj*^ps`393}AJ+Ruq@fl9 z%R(&?5Nc3lnEKGaYMLmRzKXow1+Gh|O-LG7XiNxkG^uyv zpAtLINwMK}IWK65hOw&O>~EJ}x@lDBtB`yKeV1%GtY4PzT%@~wa1VgZn7QRwc7C)_ zpEF~upeDRg_<#w=dLQ)E?AzXUQpbKXYxkp>;c@aOr6A|dHA?KaZkL0svwB^U#zmx0 zzW4^&G!w7YeRxt<9;d@8H=u(j{6+Uj5AuTluvZZD4b+#+6Rp?(yJ`BC9EW9!b&KdPvzJYe5l7 zMJ9aC@S;sA0{F0XyVY{}FzW0Vh)0mPf_BX82E+CD&)wf2!x@{RO~XBYu80TONl3e+ zA7W$ra6LcDW_j4s-`3tI^VhG*sa5lLc+V6ONf=hO@q4|p`CinYqk1Ko*MbZ6_M05k zSwSwkvu;`|I*_Vl=zPd|dVD0lh&Ha)CSJJvV{AEdF{^Kn_Yfsd!{Pc1GNgw}(^~%)jk5~0L~ms|Rez1fiK~s5t(p1ci5Gq$JC#^JrXf?8 z-Y-Zi_Hvi>oBzV8DSRG!7dm|%IlZg3^0{5~;>)8-+Nk&EhAd(}s^7%MuU}lphNW9Q zT)DPo(ob{tB7_?u;4-qGDo!sh&7gHaJfkh43QwL|bbFVi@+oy;i;M zM&CP^v~lx1U`pi9PmSr&Mc<%HAq0DGH?Ft95)WY`P?~7O z`O^Nr{Py9M#Ls4Y7OM?e%Y*Mvrme%=DwQaye^Qut_1pOMrg^!5u(f9p(D%MR%1K>% zRGw%=dYvw@)o}Fw@tOtPjz`45mfpn;OT&V(;z75J*<$52{sB65$gDjwX3Xa!x_wE- z!#RpwHM#WrO*|~f7z}(}o7US(+0FYLM}6de>gQdtPazXz?OcNv4R^oYLJ_BQOd_l172oSK$6!1r@g+B@0ofJ4*{>_AIxfe-#xp>(1 z@Y3Nfd>fmqvjL;?+DmZk*KsfXJf<%~(gcLwEez%>1c6XSboURUh&k=B)MS>6kw9bY z{7vdev7;A}5fy*ZE23DS{J?8at~xwVk`pEwP5^k?XMQ7u64;KmFJ#POzdG#np~F&H ze-BUh@g54)dsS%nkBb}+GuUEKU~pHcYIg4vSo$J(J|U36bs0Use+3A&IMcR%6@jv$ z=+QI+@wW@?iu}Hpyzlvj-EYeop{f65GX0O%>w#0t|V z1-svWk`hU~m`|O$kw5?Yn5UhI%9P-<45A(v0ld1n+%Ziq&TVpBcV9n}L9Tus-TI)f zd_(g+nYCDR@+wYNQm1GwxhUN4tGMLCzDzPqY$~`l<47{+l<{FZ$L6(>J)|}!bi<)| zE35dl{a2)&leQ@LlDxLQOfUDS`;+ZQ4ozrleQwaR-K|@9T{#hB5Z^t#8 zC-d_G;B4;F#8A2EBL58s$zF-=SCr`P#z zNCTnHF&|X@q>SkAoYu>&s9v@zCpv9lLSH-UZzfhJh`EZA{X#%nqw@@aW^vPcfQrlPs(qQxmC|4tp^&sHy!H!2FH5eC{M@g;ElWNzlb-+ zxpfc0m4<}L){4|RZ>KReag2j%Ot_UKkgpJN!7Y_y3;Ssz{9 z!K3isRtaFtQII5^6}cm9RZd5nTp9psk&u1C(BY`(_tolBwzV_@0F*m%3G%Y?2utyS zY`xM0iDRT)yTyYukFeGQ&W@ReM+ADG1xu@ruq&^GK35`+2r}b^V!m1(VgH|QhIPDE X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/packages/native_crypto/example/web/icons/Icon-maskable-512.png b/packages/native_crypto/example/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 zcmeFZ_gj-)&^4Nb2tlbLMU<{!p(#yjqEe+=0IA_oih%ScH9@5#MNp&}Y#;;(h=A0@ zh7{>lT2MkSQ344eAvrhici!td|HJuyvJm#Y_w1Q9Yu3!26dNlO-oxUDK_C#XnW^Co z5C{VN6#{~B0)K2j7}*1Xq(Nqemv23A-6&=ZpEijkVnSwVGqLv40?n0=p;k3-U5e5+ z+z3>aS`u9DS=!wg8ROu?X4TFoW6CFLL&{GzoVT)ldhLekLM|+j3tIxRd|*5=c{=s&*vfPdBr(Fyj(v@%eQj1Soy7m4^@VRl1~@-PV7y+c!xz$8436WBn$t{=}mEdK#k`aystimGgI{(IBx$!pAwFoE9Y`^t^;> zKAD)C(Dl^s%`?q5$P|fZf8Xymrtu^Pv(7D`rn>Z-w$Ahs!z9!94WNVxrJuXfHAaxg zC6s@|Z1$7R$(!#t%Jb{{s6(Y?NoQXDYq)!}X@jKPhe`{9KQ@sAU8y-5`xt?S9$jKH zoi}6m5PcG*^{kjvt+kwPpyQzVg4o)a>;LK`aaN2x4@itBD3Aq?yWTM20VRn1rrd+2 zKO=P0rMjEGq_UqpMa`~7B|p?xAN1SCoCp}QxAv8O`jLJ5CVh@umR%c%i^)6!o+~`F zaalSTQcl5iwOLC&H)efzd{8(88mo`GI(56T<(&p7>Qd^;R1hn1Y~jN~tApaL8>##U zd65bo8)79CplWxr#z4!6HvLz&N7_5AN#x;kLG?zQ(#p|lj<8VUlKY=Aw!ATqeL-VG z42gA!^cMNPj>(`ZMEbCrnkg*QTsn*u(nQPWI9pA{MQ=IsPTzd7q5E#7+z>Ch=fx$~ z;J|?(5jTo5UWGvsJa(Sx0?S#56+8SD!I^tftyeh_{5_31l6&Hywtn`bbqYDqGZXI( zCG7hBgvksX2ak8+)hB4jnxlO@A32C_RM&g&qDSb~3kM&)@A_j1*oTO@nicGUyv+%^ z=vB)4(q!ykzT==Z)3*3{atJ5}2PV*?Uw+HhN&+RvKvZL3p9E?gHjv{6zM!A|z|UHK z-r6jeLxbGn0D@q5aBzlco|nG2tr}N@m;CJX(4#Cn&p&sLKwzLFx1A5izu?X_X4x8r@K*d~7>t1~ zDW1Mv5O&WOxbzFC`DQ6yNJ(^u9vJdj$fl2dq`!Yba_0^vQHXV)vqv1gssZYzBct!j zHr9>ydtM8wIs}HI4=E}qAkv|BPWzh3^_yLH(|kdb?x56^BlDC)diWyPd*|f!`^12_U>TD^^94OCN0lVv~Sgvs94ecpE^}VY$w`qr_>Ue zTfH~;C<3H<0dS5Rkf_f@1x$Gms}gK#&k()IC0zb^QbR!YLoll)c$Agfi6MKI0dP_L z=Uou&u~~^2onea2%XZ@>`0x^L8CK6=I{ge;|HXMj)-@o~h&O{CuuwBX8pVqjJ*o}5 z#8&oF_p=uSo~8vn?R0!AMWvcbZmsrj{ZswRt(aEdbi~;HeVqIe)-6*1L%5u$Gbs}| zjFh?KL&U(rC2izSGtwP5FnsR@6$-1toz?RvLD^k~h9NfZgzHE7m!!7s6(;)RKo2z} zB$Ci@h({l?arO+vF;s35h=|WpefaOtKVx>l399}EsX@Oe3>>4MPy%h&^3N_`UTAHJ zI$u(|TYC~E4)|JwkWW3F!Tib=NzjHs5ii2uj0^m|Qlh-2VnB#+X~RZ|`SA*}}&8j9IDv?F;(Y^1=Z0?wWz;ikB zewU>MAXDi~O7a~?jx1x=&8GcR-fTp>{2Q`7#BE#N6D@FCp`?ht-<1|y(NArxE_WIu zP+GuG=Qq>SHWtS2M>34xwEw^uvo4|9)4s|Ac=ud?nHQ>ax@LvBqusFcjH0}{T3ZPQ zLO1l<@B_d-(IS682}5KA&qT1+{3jxKolW+1zL4inqBS-D>BohA!K5++41tM@ z@xe<-qz27}LnV#5lk&iC40M||JRmZ*A##K3+!j93eouU8@q-`W0r%7N`V$cR&JV;iX(@cS{#*5Q>~4BEDA)EikLSP@>Oo&Bt1Z~&0d5)COI%3$cLB_M?dK# z{yv2OqW!al-#AEs&QFd;WL5zCcp)JmCKJEdNsJlL9K@MnPegK23?G|O%v`@N{rIRa zi^7a}WBCD77@VQ-z_v{ZdRsWYrYgC$<^gRQwMCi6);%R~uIi31OMS}=gUTE(GKmCI z$zM>mytL{uNN+a&S38^ez(UT=iSw=l2f+a4)DyCA1Cs_N-r?Q@$3KTYosY!;pzQ0k zzh1G|kWCJjc(oZVBji@kN%)UBw(s{KaYGy=i{g3{)Z+&H8t2`^IuLLKWT6lL<-C(! zSF9K4xd-|VO;4}$s?Z7J_dYqD#Mt)WCDnsR{Kpjq275uUq6`v0y*!PHyS(}Zmv)_{>Vose9-$h8P0|y;YG)Bo}$(3Z%+Gs0RBmFiW!^5tBmDK-g zfe5%B*27ib+7|A*Fx5e)2%kIxh7xWoc3pZcXS2zik!63lAG1;sC1ja>BqH7D zODdi5lKW$$AFvxgC-l-)!c+9@YMC7a`w?G(P#MeEQ5xID#<}W$3bSmJ`8V*x2^3qz zVe<^^_8GHqYGF$nIQm0Xq2kAgYtm#UC1A(=&85w;rmg#v906 zT;RyMgbMpYOmS&S9c38^40oUp?!}#_84`aEVw;T;r%gTZkWeU;;FwM@0y0adt{-OK z(vGnPSlR=Nv2OUN!2=xazlnHPM9EWxXg2EKf0kI{iQb#FoP>xCB<)QY>OAM$Dcdbm zU6dU|%Mo(~avBYSjRc13@|s>axhrPl@Sr81{RSZUdz4(=|82XEbV*JAX6Lfbgqgz584lYgi0 z2-E{0XCVON$wHfvaLs;=dqhQJ&6aLn$D#0i(FkAVrXG9LGm3pSTf&f~RQb6|1_;W> z?n-;&hrq*~L=(;u#jS`*Yvh@3hU-33y_Kv1nxqrsf>pHVF&|OKkoC)4DWK%I!yq?P z=vXo8*_1iEWo8xCa{HJ4tzxOmqS0&$q+>LroMKI*V-rxhOc%3Y!)Y|N6p4PLE>Yek>Y(^KRECg8<|%g*nQib_Yc#A5q8Io z6Ig&V>k|~>B6KE%h4reAo*DfOH)_01tE0nWOxX0*YTJgyw7moaI^7gW*WBAeiLbD?FV9GSB zPv3`SX*^GRBM;zledO`!EbdBO_J@fEy)B{-XUTVQv}Qf~PSDpK9+@I`7G7|>Dgbbu z_7sX9%spVo$%qwRwgzq7!_N;#Td08m5HV#?^dF-EV1o)Q=Oa+rs2xH#g;ykLbwtCh znUnA^dW!XjspJ;otq$yV@I^s9Up(5k7rqhQd@OLMyyxVLj_+$#Vc*}Usevp^I(^vH zmDgHc0VMme|K&X?9&lkN{yq_(If)O`oUPW8X}1R5pSVBpfJe0t{sPA(F#`eONTh_) zxeLqHMfJX#?P(@6w4CqRE@Eiza; z;^5)Kk=^5)KDvd9Q<`=sJU8rjjxPmtWMTmzcH={o$U)j=QBuHarp?=}c??!`3d=H$nrJMyr3L-& zA#m?t(NqLM?I3mGgWA_C+0}BWy3-Gj7bR+d+U?n*mN$%5P`ugrB{PeV>jDUn;eVc- zzeMB1mI4?fVJatrNyq|+zn=!AiN~<}eoM#4uSx^K?Iw>P2*r=k`$<3kT00BE_1c(02MRz4(Hq`L^M&xt!pV2 zn+#U3@j~PUR>xIy+P>51iPayk-mqIK_5rlQMSe5&tDkKJk_$i(X&;K(11YGpEc-K= zq4Ln%^j>Zi_+Ae9eYEq_<`D+ddb8_aY!N;)(&EHFAk@Ekg&41ABmOXfWTo)Z&KotA zh*jgDGFYQ^y=m)<_LCWB+v48DTJw*5dwMm_YP0*_{@HANValf?kV-Ic3xsC}#x2h8 z`q5}d8IRmqWk%gR)s~M}(Qas5+`np^jW^oEd-pzERRPMXj$kS17g?H#4^trtKtq;C?;c ztd|%|WP2w2Nzg@)^V}!Gv++QF2!@FP9~DFVISRW6S?eP{H;;8EH;{>X_}NGj^0cg@ z!2@A>-CTcoN02^r6@c~^QUa={0xwK0v4i-tQ9wQq^=q*-{;zJ{Qe%7Qd!&X2>rV@4 z&wznCz*63_vw4>ZF8~%QCM?=vfzW0r_4O^>UA@otm_!N%mH)!ERy&b!n3*E*@?9d^ zu}s^By@FAhG(%?xgJMuMzuJw2&@$-oK>n z=UF}rt%vuaP9fzIFCYN-1&b#r^Cl6RDFIWsEsM|ROf`E?O(cy{BPO2Ie~kT+^kI^i zp>Kbc@C?}3vy-$ZFVX#-cx)Xj&G^ibX{pWggtr(%^?HeQL@Z( zM-430g<{>vT*)jK4aY9(a{lSy{8vxLbP~n1MXwM527ne#SHCC^F_2@o`>c>>KCq9c(4c$VSyMl*y3Nq1s+!DF| z^?d9PipQN(mw^j~{wJ^VOXDCaL$UtwwTpyv8IAwGOg<|NSghkAR1GSNLZ1JwdGJYm zP}t<=5=sNNUEjc=g(y)1n5)ynX(_$1-uGuDR*6Y^Wgg(LT)Jp><5X|}bt z_qMa&QP?l_n+iVS>v%s2Li_;AIeC=Ca^v1jX4*gvB$?H?2%ndnqOaK5-J%7a} zIF{qYa&NfVY}(fmS0OmXA70{znljBOiv5Yod!vFU{D~*3B3Ka{P8?^ zfhlF6o7aNT$qi8(w<}OPw5fqA7HUje*r*Oa(YV%*l0|9FP9KW@U&{VSW{&b0?@y)M zs%4k1Ax;TGYuZ9l;vP5@?3oQsp3)rjBeBvQQ>^B;z5pc=(yHhHtq6|0m(h4envn_j787fizY@V`o(!SSyE7vlMT zbo=Z1c=atz*G!kwzGB;*uPL$Ei|EbZLh8o+1BUMOpnU(uX&OG1MV@|!&HOOeU#t^x zr9=w2ow!SsTuJWT7%Wmt14U_M*3XiWBWHxqCVZI0_g0`}*^&yEG9RK9fHK8e+S^m? zfCNn$JTswUVbiC#>|=wS{t>-MI1aYPLtzO5y|LJ9nm>L6*wpr_m!)A2Fb1RceX&*|5|MwrvOk4+!0p99B9AgP*9D{Yt|x=X}O% zgIG$MrTB=n-!q%ROT|SzH#A$Xm;|ym)0>1KR}Yl0hr-KO&qMrV+0Ej3d@?FcgZ+B3 ztEk16g#2)@x=(ko8k7^Tq$*5pfZHC@O@}`SmzT1(V@x&NkZNM2F#Q-Go7-uf_zKC( zB(lHZ=3@dHaCOf6C!6i8rDL%~XM@rVTJbZL09?ht@r^Z_6x}}atLjvH^4Vk#Ibf(^LiBJFqorm?A=lE zzFmwvp4bT@Nv2V>YQT92X;t9<2s|Ru5#w?wCvlhcHLcsq0TaFLKy(?nzezJ>CECqj zggrI~Hd4LudM(m{L@ezfnpELsRFVFw>fx;CqZtie`$BXRn#Ns%AdoE$-Pf~{9A8rV zf7FbgpKmVzmvn-z(g+&+-ID=v`;6=)itq8oM*+Uz**SMm_{%eP_c0{<%1JGiZS19o z@Gj7$Se~0lsu}w!%;L%~mIAO;AY-2i`9A*ZfFs=X!LTd6nWOZ7BZH2M{l2*I>Xu)0 z`<=;ObglnXcVk!T>e$H?El}ra0WmPZ$YAN0#$?|1v26^(quQre8;k20*dpd4N{i=b zuN=y}_ew9SlE~R{2+Rh^7%PA1H5X(p8%0TpJ=cqa$65XL)$#ign-y!qij3;2>j}I; ziO@O|aYfn&up5F`YtjGw68rD3{OSGNYmBnl?zdwY$=RFsegTZ=kkzRQ`r7ZjQP!H( zp4>)&zf<*N!tI00xzm-ME_a{_I!TbDCr;8E;kCH4LlL-tqLxDuBn-+xgPk37S&S2^ z2QZumkIimwz!c@!r0)j3*(jPIs*V!iLTRl0Cpt_UVNUgGZzdvs0(-yUghJfKr7;=h zD~y?OJ-bWJg;VdZ^r@vlDoeGV&8^--!t1AsIMZ5S440HCVr%uk- z2wV>!W1WCvFB~p$P$$_}|H5>uBeAe>`N1FI8AxM|pq%oNs;ED8x+tb44E) zTj{^fbh@eLi%5AqT?;d>Es5D*Fi{Bpk)q$^iF!!U`r2hHAO_?#!aYmf>G+jHsES4W zgpTKY59d?hsb~F0WE&dUp6lPt;Pm zcbTUqRryw^%{ViNW%Z(o8}dd00H(H-MmQmOiTq{}_rnwOr*Ybo7*}3W-qBT!#s0Ie z-s<1rvvJx_W;ViUD`04%1pra*Yw0BcGe)fDKUK8aF#BwBwMPU;9`!6E(~!043?SZx z13K%z@$$#2%2ovVlgFIPp7Q6(vO)ud)=*%ZSucL2Dh~K4B|%q4KnSpj#n@(0B})!9 z8p*hY@5)NDn^&Pmo;|!>erSYg`LkO?0FB@PLqRvc>4IsUM5O&>rRv|IBRxi(RX(gJ ztQ2;??L~&Mv;aVr5Q@(?y^DGo%pO^~zijld41aA0KKsy_6FeHIn?fNHP-z>$OoWer zjZ5hFQTy*-f7KENRiCE$ZOp4|+Wah|2=n@|W=o}bFM}Y@0e62+_|#fND5cwa3;P{^pEzlJbF1Yq^}>=wy8^^^$I2M_MH(4Dw{F6hm+vrWV5!q;oX z;tTNhz5`-V={ew|bD$?qcF^WPR{L(E%~XG8eJx(DoGzt2G{l8r!QPJ>kpHeOvCv#w zr=SSwMDaUX^*~v%6K%O~i)<^6`{go>a3IdfZ8hFmz&;Y@P%ZygShQZ2DSHd`m5AR= zx$wWU06;GYwXOf(%MFyj{8rPFXD};JCe85Bdp4$YJ2$TzZ7Gr#+SwCvBI1o$QP0(c zy`P51FEBV2HTisM3bHqpmECT@H!Y2-bv2*SoSPoO?wLe{M#zDTy@ujAZ!Izzky~3k zRA1RQIIoC*Mej1PH!sUgtkR0VCNMX(_!b65mo66iM*KQ7xT8t2eev$v#&YdUXKwGm z7okYAqYF&bveHeu6M5p9xheRCTiU8PFeb1_Rht0VVSbm%|1cOVobc8mvqcw!RjrMRM#~=7xibH&Fa5Imc|lZ{eC|R__)OrFg4@X_ ze+kk*_sDNG5^ELmHnZ7Ue?)#6!O)#Nv*Dl2mr#2)w{#i-;}0*_h4A%HidnmclH#;Q zmQbq+P4DS%3}PpPm7K_K3d2s#k~x+PlTul7+kIKol0@`YN1NG=+&PYTS->AdzPv!> zQvzT=)9se*Jr1Yq+C{wbK82gAX`NkbXFZ)4==j4t51{|-v!!$H8@WKA={d>CWRW+g z*`L>9rRucS`vbXu0rzA1#AQ(W?6)}1+oJSF=80Kf_2r~Qm-EJ6bbB3k`80rCv(0d` zvCf3;L2ovYG_TES%6vSuoKfIHC6w;V31!oqHM8-I8AFzcd^+_86!EcCOX|Ta9k1!s z_Vh(EGIIsI3fb&dF$9V8v(sTBC%!#<&KIGF;R+;MyC0~}$gC}}= zR`DbUVc&Bx`lYykFZ4{R{xRaUQkWCGCQlEc;!mf=+nOk$RUg*7 z;kP7CVLEc$CA7@6VFpsp3_t~m)W0aPxjsA3e5U%SfY{tp5BV5jH-5n?YX7*+U+Zs%LGR>U- z!x4Y_|4{gx?ZPJobISy991O znrmrC3otC;#4^&Rg_iK}XH(XX+eUHN0@Oe06hJk}F?`$)KmH^eWz@@N%wEc)%>?Ft z#9QAroDeyfztQ5Qe{m*#R#T%-h*&XvSEn@N$hYRTCMXS|EPwzF3IIysD2waj`vQD{ zv_#^Pgr?s~I*NE=acf@dWVRNWTr(GN0wrL)Z2=`Dr>}&ZDNX|+^Anl{Di%v1Id$_p zK5_H5`RDjJx`BW7hc85|> zHMMsWJ4KTMRHGu+vy*kBEMjz*^K8VtU=bXJYdhdZ-?jTXa$&n)C?QQIZ7ln$qbGlr zS*TYE+ppOrI@AoPP=VI-OXm}FzgXRL)OPvR$a_=SsC<3Jb+>5makX|U!}3lx4tX&L z^C<{9TggZNoeX!P1jX_K5HkEVnQ#s2&c#umzV6s2U-Q;({l+j^?hi7JnQ7&&*oOy9 z(|0asVTWUCiCnjcOnB2pN0DpuTglKq;&SFOQ3pUdye*eT<2()7WKbXp1qq9=bhMWlF-7BHT|i3TEIT77AcjD(v=I207wi-=vyiw5mxgPdTVUC z&h^FEUrXwWs9en2C{ywZp;nvS(Mb$8sBEh-*_d-OEm%~p1b2EpcwUdf<~zmJmaSTO zSX&&GGCEz-M^)G$fBvLC2q@wM$;n4jp+mt0MJFLuJ%c`tSp8$xuP|G81GEd2ci$|M z4XmH{5$j?rqDWoL4vs!}W&!?!rtj=6WKJcE>)?NVske(p;|#>vL|M_$as=mi-n-()a*OU3Okmk0wC<9y7t^D(er-&jEEak2!NnDiOQ99Wx8{S8}=Ng!e0tzj*#T)+%7;aM$ z&H}|o|J1p{IK0Q7JggAwipvHvko6>Epmh4RFRUr}$*2K4dz85o7|3#Bec9SQ4Y*;> zXWjT~f+d)dp_J`sV*!w>B%)#GI_;USp7?0810&3S=WntGZ)+tzhZ+!|=XlQ&@G@~3 z-dw@I1>9n1{+!x^Hz|xC+P#Ab`E@=vY?3%Bc!Po~e&&&)Qp85!I|U<-fCXy*wMa&t zgDk!l;gk;$taOCV$&60z+}_$ykz=Ea*)wJQ3-M|p*EK(cvtIre0Pta~(95J7zoxBN zS(yE^3?>88AL0Wfuou$BM{lR1hkrRibz=+I9ccwd`ZC*{NNqL)3pCcw^ygMmrG^Yp zn5f}Xf>%gncC=Yq96;rnfp4FQL#{!Y*->e82rHgY4Zwy{`JH}b9*qr^VA{%~Z}jtp z_t$PlS6}5{NtTqXHN?uI8ut8rOaD#F1C^ls73S=b_yI#iZDOGz3#^L@YheGd>L;<( z)U=iYj;`{>VDNzIxcjbTk-X3keXR8Xbc`A$o5# zKGSk-7YcoBYuAFFSCjGi;7b<;n-*`USs)IX z=0q6WZ=L!)PkYtZE-6)azhXV|+?IVGTOmMCHjhkBjfy@k1>?yFO3u!)@cl{fFAXnRYsWk)kpT?X{_$J=|?g@Q}+kFw|%n!;Zo}|HE@j=SFMvT8v`6Y zNO;tXN^036nOB2%=KzxB?n~NQ1K8IO*UE{;Xy;N^ZNI#P+hRZOaHATz9(=)w=QwV# z`z3+P>9b?l-@$@P3<;w@O1BdKh+H;jo#_%rr!ute{|YX4g5}n?O7Mq^01S5;+lABE+7`&_?mR_z7k|Ja#8h{!~j)| zbBX;*fsbUak_!kXU%HfJ2J+G7;inu#uRjMb|8a){=^))y236LDZ$$q3LRlat1D)%7K0!q5hT5V1j3qHc7MG9 z_)Q=yQ>rs>3%l=vu$#VVd$&IgO}Za#?aN!xY>-<3PhzS&q!N<=1Q7VJBfHjug^4|) z*fW^;%3}P7X#W3d;tUs3;`O&>;NKZBMR8au6>7?QriJ@gBaorz-+`pUWOP73DJL=M z(33uT6Gz@Sv40F6bN|H=lpcO z^AJl}&=TIjdevuDQ!w0K*6oZ2JBOhb31q!XDArFyKpz!I$p4|;c}@^bX{>AXdt7Bm zaLTk?c%h@%xq02reu~;t@$bv`b3i(P=g}~ywgSFpM;}b$zAD+=I!7`V~}ARB(Wx0C(EAq@?GuxOL9X+ffbkn3+Op0*80TqmpAq~EXmv%cq36celXmRz z%0(!oMp&2?`W)ALA&#|fu)MFp{V~~zIIixOxY^YtO5^FSox8v$#d0*{qk0Z)pNTt0QVZ^$`4vImEB>;Lo2!7K05TpY-sl#sWBz_W-aDIV`Ksabi zvpa#93Svo!70W*Ydh)Qzm{0?CU`y;T^ITg-J9nfWeZ-sbw)G@W?$Eomf%Bg2frfh5 zRm1{|E0+(4zXy){$}uC3%Y-mSA2-^I>Tw|gQx|7TDli_hB>``)Q^aZ`LJC2V3U$SABP}T)%}9g2pF9dT}aC~!rFFgkl1J$ z`^z{Arn3On-m%}r}TGF8KQe*OjSJ=T|caa_E;v89A{t@$yT^(G9=N9F?^kT*#s3qhJq!IH5|AhnqFd z0B&^gm3w;YbMNUKU>naBAO@fbz zqw=n!@--}o5;k6DvTW9pw)IJVz;X}ncbPVrmH>4x);8cx;q3UyiML1PWp%bxSiS|^ zC5!kc4qw%NSOGQ*Kcd#&$30=lDvs#*4W4q0u8E02U)7d=!W7+NouEyuF1dyH$D@G& zaFaxo9Ex|ZXA5y{eZT*i*dP~INSMAi@mvEX@q5i<&o&#sM}Df?Og8n8Ku4vOux=T% zeuw~z1hR}ZNwTn8KsQHKLwe2>p^K`YWUJEdVEl|mO21Bov!D0D$qPoOv=vJJ`)|%_ z>l%`eexY7t{BlVKP!`a^U@nM?#9OC*t76My_E_<16vCz1x_#82qj2PkWiMWgF8bM9 z(1t4VdHcJ;B~;Q%x01k_gQ0>u2*OjuEWNOGX#4}+N?Gb5;+NQMqp}Puqw2HnkYuKA zzKFWGHc&K>gwVgI1Sc9OT1s6fq=>$gZU!!xsilA$fF`kLdGoX*^t}ao@+^WBpk>`8 z4v_~gK|c2rCq#DZ+H)$3v~Hoi=)=1D==e3P zpKrRQ+>O^cyTuWJ%2}__0Z9SM_z9rptd*;-9uC1tDw4+A!=+K%8~M&+Zk#13hY$Y$ zo-8$*8dD5@}XDi19RjK6T^J~DIXbF5w&l?JLHMrf0 zLv0{7*G!==o|B%$V!a=EtVHdMwXLtmO~vl}P6;S(R2Q>*kTJK~!}gloxj)m|_LYK{ zl(f1cB=EON&wVFwK?MGn^nWuh@f95SHatPs(jcwSY#Dnl1@_gkOJ5=f`%s$ZHljRH0 z+c%lrb=Gi&N&1>^L_}#m>=U=(oT^vTA&3!xXNyqi$pdW1BDJ#^{h|2tZc{t^vag3& zAD7*8C`chNF|27itjBUo^CCDyEpJLX3&u+(L;YeeMwnXEoyN(ytoEabcl$lSgx~Ltatn}b$@j_yyMrBb03)shJE*$;Mw=;mZd&8e>IzE+4WIoH zCSZE7WthNUL$|Y#m!Hn?x7V1CK}V`KwW2D$-7&ODy5Cj;!_tTOOo1Mm%(RUt)#$@3 zhurA)t<7qik%%1Et+N1?R#hdBB#LdQ7{%-C zn$(`5e0eFh(#c*hvF>WT*07fk$N_631?W>kfjySN8^XC9diiOd#s?4tybICF;wBjp zIPzilX3{j%4u7blhq)tnaOBZ_`h_JqHXuI7SuIlNTgBk9{HIS&3|SEPfrvcE<@}E` zKk$y*nzsqZ{J{uWW9;#n=de&&h>m#A#q)#zRonr(?mDOYU&h&aQWD;?Z(22wY?t$U3qo`?{+amA$^TkxL+Ex2dh`q7iR&TPd0Ymwzo#b? zP$#t=elB5?k$#uE$K>C$YZbYUX_JgnXA`oF_Ifz4H7LEOW~{Gww&3s=wH4+j8*TU| zSX%LtJWqhr-xGNSe{;(16kxnak6RnZ{0qZ^kJI5X*It_YuynSpi(^-}Lolr{)#z_~ zw!(J-8%7Ybo^c3(mED`Xz8xecP35a6M8HarxRn%+NJBE;dw>>Y2T&;jzRd4FSDO3T zt*y+zXCtZQ0bP0yf6HRpD|WmzP;DR^-g^}{z~0x~z4j8m zucTe%k&S9Nt-?Jb^gYW1w6!Y3AUZ0Jcq;pJ)Exz%7k+mUOm6%ApjjSmflfKwBo6`B zhNb@$NHTJ>guaj9S{@DX)!6)b-Shav=DNKWy(V00k(D!v?PAR0f0vDNq*#mYmUp6> z76KxbFDw5U{{qx{BRj(>?|C`82ICKbfLxoldov-M?4Xl+3;I4GzLHyPOzYw7{WQST zPNYcx5onA%MAO9??41Po*1zW(Y%Zzn06-lUp{s<3!_9vv9HBjT02On0Hf$}NP;wF) zP<`2p3}A^~1YbvOh{ePMx$!JGUPX-tbBzp3mDZMY;}h;sQ->!p97GA)9a|tF(Gh{1$xk7 zUw?ELkT({Xw!KIr);kTRb1b|UL`r2_`a+&UFVCdJ)1T#fdh;71EQl9790Br0m_`$x z9|ZANuchFci8GNZ{XbP=+uXSJRe(;V5laQz$u18#?X*9}x7cIEbnr%<=1cX3EIu7$ zhHW6pe5M(&qEtsqRa>?)*{O;OJT+YUhG5{km|YI7I@JL_3Hwao9aXneiSA~a* z|Lp@c-oMNyeAEuUz{F?kuou3x#C*gU?lon!RC1s37gW^0Frc`lqQWH&(J4NoZg3m8 z;Lin#8Q+cFPD7MCzj}#|ws7b@?D9Q4dVjS4dpco=4yX5SSH=A@U@yqPdp@?g?qeia zH=Tt_9)G=6C2QIPsi-QipnK(mc0xXIN;j$WLf@n8eYvMk;*H-Q4tK%(3$CN}NGgO8n}fD~+>?<3UzvsrMf*J~%i;VKQHbF%TPalFi=#sgj)(P#SM^0Q=Tr>4kJVw8X3iWsP|e8tj}NjlMdWp z@2+M4HQu~3!=bZpjh;;DIDk&X}=c8~kn)FWWH z2KL1w^rA5&1@@^X%MjZ7;u(kH=YhH2pJPFQe=hn>tZd5RC5cfGYis8s9PKaxi*}-s6*W zRA^PwR=y^5Z){!(4D9-KC;0~;b*ploznFOaU`bJ_7U?qAi#mTo!&rIECRL$_y@yI27x2?W+zqDBD5~KCVYKFZLK+>ABC(Kj zeAll)KMgIlAG`r^rS{loBrGLtzhHY8$)<_S<(Dpkr(Ym@@vnQ&rS@FC*>2@XCH}M+an74WcRDcoQ+a3@A z9tYhl5$z7bMdTvD2r&jztBuo37?*k~wcU9GK2-)MTFS-lux-mIRYUuGUCI~V$?s#< z?1qAWb(?ZLm(N>%S%y10COdaq_Tm5c^%ooIxpR=`3e4C|@O5wY+eLik&XVi5oT7oe zmxH)Jd*5eo@!7t`x8!K=-+zJ-Sz)B_V$)s1pW~CDU$=q^&ABvf6S|?TOMB-RIm@CoFg>mjIQE)?+A1_3s6zmFU_oW&BqyMz1mY*IcP_2knjq5 zqw~JK(cVsmzc7*EvTT2rvpeqhg)W=%TOZ^>f`rD4|7Z5fq*2D^lpCttIg#ictgqZ$P@ru6P#f$x#KfnfTZj~LG6U_d-kE~`;kU_X)`H5so@?C zWmb!7x|xk@0L~0JFall*@ltyiL^)@3m4MqC7(7H0sH!WidId1#f#6R{Q&A!XzO1IAcIx;$k66dumt6lpUw@nL2MvqJ5^kbOVZ<^2jt5-njy|2@`07}0w z;M%I1$FCoLy`8xp8Tk)bFr;7aJeQ9KK6p=O$U0-&JYYy8woV*>b+FB?xLX`=pirYM z5K$BA(u)+jR{?O2r$c_Qvl?M{=Ar{yQ!UVsVn4k@0!b?_lA;dVz9uaQUgBH8Oz(Sb zrEs;&Ey>_ex8&!N{PmQjp+-Hlh|OA&wvDai#GpU=^-B70V0*LF=^bi+Nhe_o|azZ%~ZZ1$}LTmWt4aoB1 zPgccm$EwYU+jrdBaQFxQfn5gd(gM`Y*Ro1n&Zi?j=(>T3kmf94vdhf?AuS8>$Va#P zGL5F+VHpxdsCUa}+RqavXCobI-@B;WJbMphpK2%6t=XvKWWE|ruvREgM+|V=i6;;O zx$g=7^`$XWn0fu!gF=Xe9cMB8Z_SelD>&o&{1XFS`|nInK3BXlaeD*rc;R-#osyIS zWv&>~^TLIyBB6oDX+#>3<_0+2C4u2zK^wmHXXDD9_)kmLYJ!0SzM|%G9{pi)`X$uf zW}|%%#LgyK7m(4{V&?x_0KEDq56tk|0YNY~B(Sr|>WVz-pO3A##}$JCT}5P7DY+@W z#gJv>pA5>$|E3WO2tV7G^SuymB?tY`ooKcN3!vaQMnBNk-WATF{-$#}FyzgtJ8M^; zUK6KWSG)}6**+rZ&?o@PK3??uN{Q)#+bDP9i1W&j)oaU5d0bIWJ_9T5ac!qc?x66Q z$KUSZ`nYY94qfN_dpTFr8OW~A?}LD;Yty-BA)-be5Z3S#t2Io%q+cAbnGj1t$|qFR z9o?8B7OA^KjCYL=-!p}w(dkC^G6Nd%_I=1))PC0w5}ZZGJxfK)jP4Fwa@b-SYBw?% zdz9B-<`*B2dOn(N;mcTm%Do)rIvfXRNFX&1h`?>Rzuj~Wx)$p13nrDlS8-jwq@e@n zNIj_|8or==8~1h*Ih?w*8K7rYkGlwlTWAwLKc5}~dfz3y`kM&^Q|@C%1VAp_$wnw6zG~W4O+^ z>i?NY?oXf^Puc~+fDM$VgRNBpOZj{2cMP~gCqWAX4 z7>%$ux8@a&_B(pt``KSt;r+sR-$N;jdpY>|pyvPiN)9ohd*>mVST3wMo)){`B(&eX z1?zZJ-4u9NZ|~j1rdZYq4R$?swf}<6(#ex%7r{kh%U@kT)&kWuAszS%oJts=*OcL9 zaZwK<5DZw%1IFHXgFplP6JiL^dk8+SgM$D?8X+gE4172hXh!WeqIO>}$I9?Nry$*S zQ#f)RuH{P7RwA3v9f<-w>{PSzom;>(i&^l{E0(&Xp4A-*q-@{W1oE3K;1zb{&n28dSC2$N+6auXe0}e4b z)KLJ?5c*>@9K#I^)W;uU_Z`enquTUxr>mNq z1{0_puF-M7j${rs!dxxo3EelGodF1TvjV;Zpo;s{5f1pyCuRp=HDZ?s#IA4f?h|-p zGd|Mq^4hDa@Bh!c4ZE?O&x&XZ_ptZGYK4$9F4~{%R!}G1leCBx`dtNUS|K zL-7J5s4W@%mhXg1!}a4PD%!t&Qn%f_oquRajn3@C*)`o&K9o7V6DwzVMEhjVdDJ1fjhr#@=lp#@4EBqi=CCQ>73>R(>QKPNM&_Jpe5G`n4wegeC`FYEPJ{|vwS>$-`fuRSp3927qOv|NC3T3G-0 zA{K`|+tQy1yqE$ShWt8ny&5~)%ITb@^+x$w0)f&om;P8B)@}=Wzy59BwUfZ1vqw87 za2lB8J(&*l#(V}Id8SyQ0C(2amzkz3EqG&Ed0Jq1)$|&>4_|NIe=5|n=3?siFV0fI z{As5DLW^gs|B-b4C;Hd(SM-S~GQhzb>HgF2|2Usww0nL^;x@1eaB)=+Clj+$fF@H( z-fqP??~QMT$KI-#m;QC*&6vkp&8699G3)Bq0*kFZXINw=b9OVaed(3(3kS|IZ)CM? zJdnW&%t8MveBuK21uiYj)_a{Fnw0OErMzMN?d$QoPwkhOwcP&p+t>P)4tHlYw-pPN z^oJ=uc$Sl>pv@fZH~ZqxSvdhF@F1s=oZawpr^-#l{IIOGG=T%QXjtwPhIg-F@k@uIlr?J->Ia zpEUQ*=4g|XYn4Gez&aHr*;t$u3oODPmc2Ku)2Og|xjc%w;q!Zz+zY)*3{7V8bK4;& zYV82FZ+8?v)`J|G1w4I0fWdKg|2b#iaazCv;|?(W-q}$o&Y}Q5d@BRk^jL7#{kbCK zSgkyu;=DV+or2)AxCBgq-nj5=@n^`%T#V+xBGEkW4lCqrE)LMv#f;AvD__cQ@Eg3`~x| zW+h9mofSXCq5|M)9|ez(#X?-sxB%Go8};sJ?2abp(Y!lyi>k)|{M*Z$c{e1-K4ky` MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/packages/native_crypto/example/web/index.html b/packages/native_crypto/example/web/index.html new file mode 100644 index 0000000..41b3bc3 --- /dev/null +++ b/packages/native_crypto/example/web/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + + + diff --git a/packages/native_crypto/example/web/manifest.json b/packages/native_crypto/example/web/manifest.json new file mode 100644 index 0000000..096edf8 --- /dev/null +++ b/packages/native_crypto/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/packages/native_crypto/example/web/pkg b/packages/native_crypto/example/web/pkg new file mode 120000 index 0000000..033f93b --- /dev/null +++ b/packages/native_crypto/example/web/pkg @@ -0,0 +1 @@ +../../../native-crypto-rust/pkg/ \ No newline at end of file -- 2.47.2 From 108c394a25ec13fc624a515bc55f7aa7b88cd768 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Tue, 4 Apr 2023 23:24:20 +0200 Subject: [PATCH 15/21] test(api): update mocks with new interface --- packages/native_crypto/pubspec.yaml | 6 +- .../test/mocks/mock_native_crypto_api.dart | 264 ++++++++++-------- 2 files changed, 153 insertions(+), 117 deletions(-) diff --git a/packages/native_crypto/pubspec.yaml b/packages/native_crypto/pubspec.yaml index e78c351..f5b311c 100644 --- a/packages/native_crypto/pubspec.yaml +++ b/packages/native_crypto/pubspec.yaml @@ -33,14 +33,14 @@ dependencies: dev_dependencies: flutter_test: { sdk: flutter } - mockito: ^5.3.2 - plugin_platform_interface: ^2.1.3 + mockito: ^5.4.0 + plugin_platform_interface: ^2.1.4 wyatt_analysis: hosted: url: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ name: wyatt_analysis - version: 2.4.0 + version: 2.4.1 flutter: plugin: diff --git a/packages/native_crypto/test/mocks/mock_native_crypto_api.dart b/packages/native_crypto/test/mocks/mock_native_crypto_api.dart index e8b4afa..d2e1831 100644 --- a/packages/native_crypto/test/mocks/mock_native_crypto_api.dart +++ b/packages/native_crypto/test/mocks/mock_native_crypto_api.dart @@ -6,7 +6,7 @@ import 'dart:typed_data'; -import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface_gen.dart'; class MockNativeCryptoAPI implements NativeCryptoAPI { static Uint8List? Function(int length)? generateSecureRandomFn; @@ -41,6 +41,13 @@ class MockNativeCryptoAPI implements NativeCryptoAPI { String algorithm, )? encryptFn; + static Uint8List? Function( + Uint8List plainText, + Uint8List key, + Uint8List iv, + String algorithm, + )? encryptWithIVFn; + static bool? Function( String plainTextPath, String cipherTextPath, @@ -48,12 +55,13 @@ class MockNativeCryptoAPI implements NativeCryptoAPI { String algorithm, )? encryptFileFn; - static Uint8List? Function( - Uint8List plainText, + static bool? Function( + String plainTextPath, + String cipherTextPath, Uint8List key, Uint8List iv, String algorithm, - )? encryptWithIVFn; + )? encryptFileWithIVFn; static Uint8List? Function( Uint8List password, @@ -64,130 +72,158 @@ class MockNativeCryptoAPI implements NativeCryptoAPI { )? pbkdf2Fn; @override - Future decrypt(DecryptRequest argRequest) async => - decryptFn != null - ? DecryptResponse( - plainText: decryptFn!( - argRequest.cipherText!, - argRequest.key!, - argRequest.algorithm!, - ), - ) - : DecryptResponse( - plainText: Uint8List.fromList([1, 2, 3]), - ); + Future decrypt( + Uint8List argCiphertext, + Uint8List argKey, + CipherAlgorithm argAlgorithm, + ) async { + if (decryptFn != null) { + return decryptFn!(argCiphertext, argKey, argAlgorithm.toString()); + } else { + return Uint8List.fromList([1, 2, 3]); + } + } @override - Future decryptFile( - DecryptFileRequest argRequest, - ) async => - decryptFileFn != null - ? DecryptFileResponse( - success: decryptFileFn!( - argRequest.cipherTextPath!, - argRequest.plainTextPath!, - argRequest.key!, - argRequest.algorithm!, - ), - ) - : DecryptFileResponse(success: true); + Future decryptFile( + String argCiphertextpath, + String argPlaintextpath, + Uint8List argKey, + CipherAlgorithm argAlgorithm, + ) async { + if (decryptFileFn != null) { + return decryptFileFn!( + argCiphertextpath, + argPlaintextpath, + argKey, + argAlgorithm.toString(), + ); + } else { + return Future.value(true); + } + } @override - Future encrypt(EncryptRequest argRequest) async => - encryptFn != null - ? EncryptResponse( - cipherText: encryptFn!( - argRequest.plainText!, - argRequest.key!, - argRequest.algorithm!, - ), - ) - : EncryptResponse( - cipherText: Uint8List.fromList([1, 2, 3]), - ); + Future encrypt( + Uint8List argPlaintext, + Uint8List argKey, + CipherAlgorithm argAlgorithm, + ) async { + if (encryptFn != null) { + return encryptFn!(argPlaintext, argKey, argAlgorithm.toString()); + } else { + return Uint8List.fromList([1, 2, 3]); + } + } @override - Future encryptFile( - EncryptFileRequest argRequest, - ) async => - encryptFileFn != null - ? EncryptFileResponse( - success: encryptFileFn!( - argRequest.plainTextPath!, - argRequest.cipherTextPath!, - argRequest.key!, - argRequest.algorithm!, - ), - ) - : EncryptFileResponse(success: true); + Future encryptFile( + String argPlaintextpath, + String argCiphertextpath, + Uint8List argKey, + CipherAlgorithm argAlgorithm, + ) async { + if (encryptFileFn != null) { + return encryptFileFn!( + argPlaintextpath, + argCiphertextpath, + argKey, + argAlgorithm.toString(), + ); + } else { + return Future.value(true); + } + } @override - Future encryptWithIV( - EncryptWithIVRequest argRequest, - ) async => - encryptWithIVFn != null - ? EncryptResponse( - cipherText: encryptWithIVFn!( - argRequest.plainText!, - argRequest.key!, - argRequest.iv!, - argRequest.algorithm!, - ), - ) - : EncryptResponse( - cipherText: Uint8List.fromList([1, 2, 3]), - ); + Future encryptFileWithIV( + String argPlaintextpath, + String argCiphertextpath, + Uint8List argIv, + Uint8List argKey, + CipherAlgorithm argAlgorithm, + ) async { + if (encryptFileWithIVFn != null) { + return encryptFileWithIVFn!( + argPlaintextpath, + argCiphertextpath, + argKey, + argIv, + argAlgorithm.toString(), + ); + } else { + return Future.value(true); + } + } @override - Future generateSecureRandom( - GenerateSecureRandomRequest argRequest, - ) async => - generateSecureRandomFn != null - ? GenerateSecureRandomResponse( - random: generateSecureRandomFn!(argRequest.length!), - ) - : GenerateSecureRandomResponse( - random: Uint8List.fromList([1, 2, 3]), - ); + Future encryptWithIV( + Uint8List argPlaintext, + Uint8List argIv, + Uint8List argKey, + CipherAlgorithm argAlgorithm, + ) async { + if (encryptWithIVFn != null) { + return encryptWithIVFn!( + argPlaintext, + argKey, + argIv, + argAlgorithm.toString(), + ); + } else { + return Future.value(Uint8List.fromList([1, 2, 3])); + } + } @override - Future hash(HashRequest argRequest) async => hashFn != null - ? HashResponse( - hash: hashFn!( - argRequest.data!, - argRequest.algorithm!, - ), - ) - : HashResponse( - hash: Uint8List.fromList([1, 2, 3]), - ); + Future generateSecureRandom(int argLength) { + if (generateSecureRandomFn != null) { + return Future.value(generateSecureRandomFn!(argLength)); + } else { + return Future.value(Uint8List.fromList([1, 2, 3])); + } + } @override - Future hmac(HmacRequest argRequest) async => hmacFn != null - ? HmacResponse( - hmac: hmacFn!( - argRequest.data!, - argRequest.key!, - argRequest.algorithm!, - ), - ) - : HmacResponse( - hmac: Uint8List.fromList([1, 2, 3]), - ); + Future hash(Uint8List argData, HashAlgorithm argAlgorithm) { + if (hashFn != null) { + return Future.value(hashFn!(argData, argAlgorithm.toString())); + } else { + return Future.value(Uint8List.fromList([1, 2, 3])); + } + } @override - Future pbkdf2(Pbkdf2Request argRequest) async => - pbkdf2Fn != null - ? Pbkdf2Response( - key: pbkdf2Fn!( - argRequest.password!, - argRequest.salt!, - argRequest.iterations!, - argRequest.length!, - argRequest.hashAlgorithm!, - ), - ) - : Pbkdf2Response( - key: Uint8List.fromList([1, 2, 3]), - ); + Future hmac( + Uint8List argData, + Uint8List argKey, + HashAlgorithm argAlgorithm, + ) { + if (hmacFn != null) { + return Future.value(hmacFn!(argData, argKey, argAlgorithm.toString())); + } else { + return Future.value(Uint8List.fromList([1, 2, 3])); + } + } + + @override + Future pbkdf2( + Uint8List argPassword, + Uint8List argSalt, + int argLength, + int argIterations, + HashAlgorithm argAlgorithm, + ) { + if (pbkdf2Fn != null) { + return Future.value(pbkdf2Fn!( + argPassword, + argSalt, + argIterations, + argLength, + argAlgorithm.toString(), + ),); + } else { + return Future.value(Uint8List.fromList([1, 2, 3])); + } + } } -- 2.47.2 From 0bf72447a0671c2649ab1ccfae72b7612efa32e1 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 5 Apr 2023 15:16:45 +0200 Subject: [PATCH 16/21] fix(android): file encryption --- .../native_crypto_android/NativeCrypto.kt | 2 +- .../native_crypto_android/ciphers/AES.kt | 79 ++++++++++--------- .../utils/FileParameters.kt | 7 +- 3 files changed, 46 insertions(+), 42 deletions(-) diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt index 3d1e12f..6e29361 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/NativeCrypto.kt @@ -111,7 +111,7 @@ class NativeCrypto(private val context: Context) : NativeCryptoAPI { ): Boolean { // For now, only AES is supported val aes = AES() - val params = FileParameters(context, plainTextPath, cipherTextPath) + val params = FileParameters(context, cipherTextPath, plainTextPath) return aes.decryptFile(params, key) } diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt index 94e0ce8..e8a5145 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/ciphers/AES.kt @@ -2,6 +2,9 @@ package fr.pointcheval.native_crypto_android.ciphers import fr.pointcheval.native_crypto_android.interfaces.Cipher import fr.pointcheval.native_crypto_android.utils.FileParameters +import java.io.BufferedInputStream +import java.io.BufferedOutputStream +import javax.crypto.CipherInputStream import javax.crypto.CipherOutputStream import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.SecretKeySpec @@ -60,28 +63,32 @@ class AES : Cipher { cipherInstance!!.init(javax.crypto.Cipher.ENCRYPT_MODE, sk) } - var len: Int? - val buffer = ByteArray(8192) - val inputFile = fileParameters.getFileInputStream() - val outputFile = fileParameters.getFileOutputStream() - val iv: ByteArray? = cipherInstance!!.iv + val input = BufferedInputStream(fileParameters.getFileInputStream()) - outputFile?.write(iv) - outputFile?.flush() + var outputBuffered = BufferedOutputStream(fileParameters.getFileOutputStream(false)) + val iv: ByteArray = cipherInstance!!.iv + + // Prepend the IV to the cipherText file + outputBuffered.write(iv) + outputBuffered.flush() + outputBuffered.close() + + // Reopen the file and append the cipherText + outputBuffered = BufferedOutputStream(fileParameters.getFileOutputStream(true)) + val output = CipherOutputStream(outputBuffered, cipherInstance) + + var i: Int + do { + i = input.read() + if (i != -1) output.write(i) + } while (i != -1) + + output.flush() + output.close() + + input.close() + outputBuffered.close() - val encryptedStream = CipherOutputStream(outputFile!!, cipherInstance) - while(true) { - len = inputFile?.read(buffer) - if (len != null && len > 0) { - encryptedStream.write(buffer,0,len) - } else { - break - } - } - encryptedStream.flush() - encryptedStream.close() - inputFile?.close() - outputFile.close() return fileParameters.outputExists() } @@ -110,9 +117,7 @@ class AES : Cipher { val inputFile = fileParameters.getFileInputStream() ?: throw Exception("Error while reading IV") // Read the first 12 bytes from the file - for (i in 0 until 12) { - iv[i] = inputFile.read().toByte() - } + inputFile.read(iv) // Initialize secret key spec val sk = SecretKeySpec(key, "AES") @@ -125,21 +130,19 @@ class AES : Cipher { cipherInstance!!.init(javax.crypto.Cipher.DECRYPT_MODE, sk, gcmParameterSpec) - var len: Int? - val buffer = ByteArray(8192) - val outputFile = fileParameters.getFileOutputStream() - val decryptedStream = CipherOutputStream(outputFile!!, cipherInstance) - while (true) { - len = inputFile.read(buffer) - if(len > 0){ - decryptedStream.write(buffer,0, len) - } else { - break - } - } - decryptedStream.flush() - decryptedStream.close() - inputFile.close() + val input = CipherInputStream(BufferedInputStream(inputFile), cipherInstance) + val output = BufferedOutputStream(fileParameters.getFileOutputStream(false)) + + var i: Int + do { + i = input.read() + if (i != -1) output.write(i) + } while (i != -1) + + output.flush() + output.close() + + input.close() return fileParameters.outputExists() } diff --git a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt index 44207dc..8a5651e 100644 --- a/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt +++ b/packages/native_crypto_android/android/src/main/kotlin/fr/pointcheval/native_crypto_android/utils/FileParameters.kt @@ -43,14 +43,15 @@ class FileParameters(ctx: Context, input: String, output: String) { } } - fun getFileOutputStream(): OutputStream? { + fun getFileOutputStream(append: Boolean): OutputStream? { val path = outputPath return try{ - FileOutputStream(path) + FileOutputStream(path, append) } catch(e: IOException){ val documentFile: DocumentFile = this.getDocumentFileByPath(path) val documentUri = documentFile.uri - context.contentResolver.openOutputStream(documentUri) + val mode = if(append) "wa" else "w" + context.contentResolver.openOutputStream(documentUri, mode) } } -- 2.47.2 From 7c8f7206f094c55315a6e673801be1eca86ce5b6 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 5 Apr 2023 15:17:56 +0200 Subject: [PATCH 17/21] feat(api): update example with benchmark + file encryption --- .../native_crypto_data_source_impl.dart | 46 +++++ .../pointy_castle_data_source_impl.dart | 25 ++- .../repositories/crypto_repository_impl.dart | 38 +++- .../crypto_repository_switchable_impl.dart | 167 ++++++++++++++++ .../data_sources/crypto_data_source.dart | 12 ++ .../repositories/crypto_repository.dart | 12 ++ .../example/lib/presentation/app/app.dart | 21 +- .../benchmark/blocs/benchmark_cubit.dart | 132 +++++++++++++ .../benchmark/blocs/benchmark_state.dart | 27 +++ .../benchmark_state_management.dart | 55 ++++++ .../cipher/blocs/aes/aes_cubit.dart | 182 ++++++++++++++++++ .../aes_state_management.dart | 21 ++ .../mode_switcher/mode_switcher_cubit.dart | 12 ++ .../home_state_management.dart | 4 +- .../widgets/app_bar_state_management.dart | 14 +- packages/native_crypto/example/pubspec.yaml | 1 + 16 files changed, 751 insertions(+), 18 deletions(-) create mode 100644 packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart create mode 100644 packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart create mode 100644 packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart create mode 100644 packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart diff --git a/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart index 957619e..dd61735 100644 --- a/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart +++ b/packages/native_crypto/example/lib/data/data_sources/native_crypto_data_source_impl.dart @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:native_crypto/native_crypto.dart'; import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart'; @@ -30,6 +32,29 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource { return plainText; } + @override + Future decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ) async { + final AES cipher = AES( + key: key, + mode: AESMode.gcm, + padding: AESPadding.none, + ); + + final plainText = File.fromUri( + Uri.parse( + '${folderResult.path}/${cipherText.path.split('/').last.replaceAll('.enc', '')}', + ), + ); + await cipher.decryptFile( + cipherText, + plainText, + ); + } + @override Future deriveKeyFromPassword( String password, { @@ -95,6 +120,27 @@ class NativeCryptoDataSourceImpl extends CryptoDataSource { return cipherText.bytes; } + @override + Future encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ) async { + final AES cipher = AES( + key: key, + mode: AESMode.gcm, + padding: AESPadding.none, + ); + + final cipherText = File.fromUri( + Uri.parse( + '${folderResult.path}/${plainText.path.split('/').last}.enc', + ), + ); + + await cipher.encryptFile(plainText, cipherText); + } + @override Future generateSecureRandom(int length) async { final SecretKey sk = await SecretKey.fromSecureRandom(length); diff --git a/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart b/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart index d4921eb..e14d14f 100644 --- a/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart +++ b/packages/native_crypto/example/lib/data/data_sources/pointy_castle_data_source_impl.dart @@ -6,6 +6,8 @@ // ignore_for_file: implementation_imports +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:native_crypto/native_crypto.dart'; import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart'; @@ -37,6 +39,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource { return paddedPlainText; } + @override + Future decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ) async { + throw UnimplementedError(); + } + @override Future deriveKeyFromPassword( String password, { @@ -125,7 +136,10 @@ class PointyCastleDataSourceImpl extends CryptoDataSource { @override Future encryptWithIV( - Uint8List data, SecretKey key, Uint8List iv,) async { + Uint8List data, + SecretKey key, + Uint8List iv, + ) async { final gcm = GCMBlockCipher(AESEngine()) ..init( true, @@ -144,6 +158,15 @@ class PointyCastleDataSourceImpl extends CryptoDataSource { ); } + @override + Future encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ) async { + throw UnimplementedError(); + } + @override Future generateSecureRandom(int length) async { if (_secureRandom == null) { diff --git a/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart b/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart index 818aa21..2781fb3 100644 --- a/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart +++ b/packages/native_crypto/example/lib/data/repositories/crypto_repository_impl.dart @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'dart:io'; import 'dart:typed_data'; import 'package:native_crypto/native_crypto.dart'; @@ -30,6 +31,22 @@ class CryptoRepositoryImpl extends CryptoRepository { }, ); + @override + FutureOrResult decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.decryptFile(cipherText, folderResult, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + @override FutureOrResult deriveKeyFromPassword( String password, { @@ -86,7 +103,10 @@ class CryptoRepositoryImpl extends CryptoRepository { @override FutureOrResult encryptWithIV( - Uint8List data, SecretKey key, Uint8List iv,) => + Uint8List data, + SecretKey key, + Uint8List iv, + ) => Result.tryCatchAsync( () async => cryptoDataSource.encryptWithIV(data, key, iv), (error) { @@ -97,6 +117,22 @@ class CryptoRepositoryImpl extends CryptoRepository { }, ); + @override + FutureOrResult encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.encryptFile(plainText, folderResult, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + @override FutureOrResult generateSecureRandom(int length) => Result.tryCatchAsync( diff --git a/packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart b/packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart new file mode 100644 index 0000000..972ac7b --- /dev/null +++ b/packages/native_crypto/example/lib/data/repositories/crypto_repository_switchable_impl.dart @@ -0,0 +1,167 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:native_crypto/native_crypto.dart'; +import 'package:native_crypto_example/domain/data_sources/crypto_data_source.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +class CryptoRepositorySwitchableImpl extends CryptoRepository { + CryptoRepositorySwitchableImpl({ + required this.nativeCryptoDataSource, + required this.pointyCastleDataSource, + required this.currentMode, + }); + + CryptoDataSource nativeCryptoDataSource; + CryptoDataSource pointyCastleDataSource; + Mode currentMode; + + set mode(Mode mode) { + currentMode = mode; + } + + CryptoDataSource get cryptoDataSource { + if (currentMode is NativeCryptoMode) { + return nativeCryptoDataSource; + } else if (currentMode is PointyCastleMode) { + return pointyCastleDataSource; + } else { + throw Exception('Unknown mode'); + } + } + + @override + FutureOrResult decrypt(Uint8List data, SecretKey key) => + Result.tryCatchAsync( + () async => cryptoDataSource.decrypt(data, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.decryptFile(cipherText, folderResult, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult deriveKeyFromPassword( + String password, { + required String salt, + }) => + Result.tryCatchAsync( + () async => cryptoDataSource.deriveKeyFromPassword( + password, + salt: salt, + ), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult hash(Hash hasher, Uint8List data) => + Result.tryCatchAsync( + () async => cryptoDataSource.hash(hasher, data), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult hmac(Hmac hmac, Uint8List data, SecretKey key) => + Result.tryCatchAsync( + () async => cryptoDataSource.hmac(hmac, data, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult encrypt(Uint8List data, SecretKey key) => + Result.tryCatchAsync( + () async => cryptoDataSource.encrypt(data, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult encryptWithIV( + Uint8List data, + SecretKey key, + Uint8List iv, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.encryptWithIV(data, key, iv), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ) => + Result.tryCatchAsync( + () async => cryptoDataSource.encryptFile(plainText, folderResult, key), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); + + @override + FutureOrResult generateSecureRandom(int length) => + Result.tryCatchAsync( + () async => cryptoDataSource.generateSecureRandom(length), + (error) { + if (error is NativeCryptoException) { + return ClientException('${error.message}'); + } + return ClientException(error.toString()); + }, + ); +} diff --git a/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart b/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart index 69db521..df7f304 100644 --- a/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart +++ b/packages/native_crypto/example/lib/domain/data_sources/crypto_data_source.dart @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:native_crypto/native_crypto.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart'; @@ -15,12 +17,22 @@ abstract class CryptoDataSource extends BaseDataSource { required String salt, }); Future encrypt(Uint8List data, SecretKey key); + Future encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ); Future encryptWithIV( Uint8List data, SecretKey key, Uint8List iv, ); Future decrypt(Uint8List data, SecretKey key); + Future decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ); Future hash(Hash hasher, Uint8List data); Future hmac(Hmac hmac, Uint8List data, SecretKey key); } diff --git a/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart b/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart index 6bf54ea..6045be6 100644 --- a/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart +++ b/packages/native_crypto/example/lib/domain/repositories/crypto_repository.dart @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:native_crypto/native_crypto.dart'; import 'package:wyatt_architecture/wyatt_architecture.dart'; @@ -15,12 +17,22 @@ abstract class CryptoRepository extends BaseRepository { required String salt, }); FutureOrResult encrypt(Uint8List data, SecretKey key); + FutureOrResult encryptFile( + File plainText, + Uri folderResult, + SecretKey key, + ); FutureOrResult encryptWithIV( Uint8List data, SecretKey key, Uint8List iv, ); FutureOrResult decrypt(Uint8List data, SecretKey key); + FutureOrResult decryptFile( + File cipherText, + Uri folderResult, + SecretKey key, + ); FutureOrResult hash(Hash hasher, Uint8List data); FutureOrResult hmac(Hmac hmac, Uint8List data, SecretKey key); diff --git a/packages/native_crypto/example/lib/presentation/app/app.dart b/packages/native_crypto/example/lib/presentation/app/app.dart index 0769515..f9253a4 100644 --- a/packages/native_crypto/example/lib/presentation/app/app.dart +++ b/packages/native_crypto/example/lib/presentation/app/app.dart @@ -8,9 +8,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:native_crypto_example/core/get_it.dart'; import 'package:native_crypto_example/data/data_sources/native_crypto_data_source_impl.dart'; -import 'package:native_crypto_example/data/repositories/crypto_repository_impl.dart'; +import 'package:native_crypto_example/data/data_sources/pointy_castle_data_source_impl.dart'; +import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart'; import 'package:native_crypto_example/data/repositories/logger_repository_impl.dart'; import 'package:native_crypto_example/data/repositories/session_repository_impl.dart'; +import 'package:native_crypto_example/domain/entities/mode.dart'; import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; import 'package:native_crypto_example/domain/repositories/session_repository.dart'; @@ -28,6 +30,12 @@ class App extends StatelessWidget { final SessionRepository _sessionRepository = SessionRepositoryImpl(sessionDataSource: getIt()); + final CryptoRepository _cryptoRepository = CryptoRepositorySwitchableImpl( + nativeCryptoDataSource: getIt(), + pointyCastleDataSource: getIt(), + currentMode: const NativeCryptoMode(), + ); + @override Widget build(BuildContext context) => MultiProvider( repositoryProviders: [ @@ -35,18 +43,17 @@ class App extends StatelessWidget { RepositoryProvider.value( value: _sessionRepository, ), - RepositoryProvider( - create: (_) => CryptoRepositoryImpl( - cryptoDataSource: getIt(), - ), - ), + RepositoryProvider.value(value: _cryptoRepository), ], blocProviders: [ BlocProvider( create: (_) => OutputCubit(_loggerRepository), ), BlocProvider( - create: (_) => ModeSwitcherCubit(_sessionRepository), + create: (_) => ModeSwitcherCubit( + _sessionRepository, + _cryptoRepository, + ), ) ], child: MaterialApp( diff --git a/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart new file mode 100644 index 0000000..3c766fe --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_cubit.dart @@ -0,0 +1,132 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto_example/domain/entities/log_message.dart'; +import 'package:native_crypto_example/domain/entities/states.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; + +part 'benchmark_state.dart'; + +class BenchmarkCubit extends Cubit { + BenchmarkCubit({ + required this.sessionRepository, + required this.loggerRepository, + required this.cryptoRepository, + }) : super(const BenchmarkState.initial()); + final SessionRepository sessionRepository; + final LoggerRepository loggerRepository; + final CryptoRepository cryptoRepository; + + List testedSizes = [ + 2097152, + 6291456, + 10485760, + 14680064, + 18874368, + 23068672, + 27262976, + 31457280, + 35651584, + 39845888, + 44040192, + 48234496, + 52428800, + ]; + + FutureOr launchBenchmark() async { + emit(const BenchmarkState.loading()); + + final sk = await sessionRepository.getSessionKey(); + + if (sk.isErr) { + await loggerRepository.addLog( + const LogError('No SecretKey!\n' + 'Go in Key tab and generate or derive one.'), + ); + emit( + BenchmarkState.failure( + sk.err?.message, + ), + ); + } + + int run = 0; + final csv = StringBuffer( + 'Run;Size (B);Encryption Time (ms);Decryption Time (ms)\n', + ); + for (final size in testedSizes) { + run++; + final StringBuffer csvLine = StringBuffer(); + final dummyBytes = Uint8List(size); + csvLine.write('$run;$size;'); + + // Encryption + final beforeEncryption = DateTime.now(); + + final encryptedBigFileResult = await cryptoRepository.encrypt( + dummyBytes, + sk.ok!, + ); + + final afterEncryption = DateTime.now(); + + final benchmarkEncryption = afterEncryption.millisecondsSinceEpoch - + beforeEncryption.millisecondsSinceEpoch; + + await loggerRepository.addLog( + LogInfo( + '[Benchmark] ${size ~/ 1000000}MB => Encryption took $benchmarkEncryption ms', + ), + ); + + csvLine.write('$benchmarkEncryption'); + + if (encryptedBigFileResult.isErr) { + await loggerRepository.addLog( + LogError( + 'Encryption failed: ${encryptedBigFileResult.err?.message}', + ), + ); + emit( + BenchmarkState.failure( + encryptedBigFileResult.err?.message, + ), + ); + return; + } + + // Decryption + final beforeDecryption = DateTime.now(); + await cryptoRepository.decrypt( + encryptedBigFileResult.ok!, + sk.ok!, + ); + final afterDecryption = DateTime.now(); + final benchmarkDecryption = afterDecryption.millisecondsSinceEpoch - + beforeDecryption.millisecondsSinceEpoch; + await loggerRepository.addLog( + LogInfo( + '[Benchmark] ${size ~/ 1000000}MB => Decryption took $benchmarkDecryption ms', + ), + ); + + csvLine.write(';$benchmarkDecryption'); + csv.writeln(csvLine); + } + debugPrint(csv.toString()); + emit( + const BenchmarkState.success(), + ); + + return; + } +} diff --git a/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart new file mode 100644 index 0000000..8cf7978 --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/benchmark/blocs/benchmark_state.dart @@ -0,0 +1,27 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +part of 'benchmark_cubit.dart'; + +@immutable +class BenchmarkState { + const BenchmarkState.initial() + : state = State.initial, + error = null; + + const BenchmarkState.loading() + : state = State.loading, + error = null; + + const BenchmarkState.failure(this.error) : state = State.failure; + + const BenchmarkState.success() + : state = State.success, + error = null; + + final State state; + final String? error; +} diff --git a/packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart b/packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart new file mode 100644 index 0000000..dc2627e --- /dev/null +++ b/packages/native_crypto/example/lib/presentation/benchmark/state_management/benchmark_state_management.dart @@ -0,0 +1,55 @@ +// Copyright 2019-2023 Hugo Pointcheval +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter/material.dart'; +import 'package:native_crypto_example/core/typography.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; +import 'package:native_crypto_example/domain/repositories/logger_repository.dart'; +import 'package:native_crypto_example/domain/repositories/session_repository.dart'; +import 'package:native_crypto_example/presentation/benchmark/blocs/benchmark_cubit.dart'; +import 'package:native_crypto_example/presentation/home/state_management/widgets/button_state_management.dart'; +import 'package:native_crypto_example/presentation/output/widgets/logs.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class BenchmarkStateManagement + extends CubitScreen { + const BenchmarkStateManagement({super.key}); + + @override + BenchmarkCubit create(BuildContext context) => BenchmarkCubit( + sessionRepository: repo(context), + loggerRepository: repo(context), + cryptoRepository: repo(context), + ); + + @override + Widget onBuild(BuildContext context, BenchmarkState state) => ListView( + children: [ + const Logs(), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'Benchmark', + style: AppTypography.title, + ), + ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + '''In computer science, a benchmark is a standardized way to measure the performance of a software program or hardware device. A benchmark is typically a set of tests or tasks designed to measure how quickly a program can complete a given set of operations or how efficiently a hardware device can perform a specific task.''', + style: AppTypography.body, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Launch', + onPressed: () => bloc(context).launchBenchmark(), + ), + ), + ], + ); +} diff --git a/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart index bb004cd..13b3435 100644 --- a/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart +++ b/packages/native_crypto/example/lib/presentation/cipher/blocs/aes/aes_cubit.dart @@ -5,7 +5,9 @@ // https://opensource.org/licenses/MIT. import 'dart:async'; +import 'dart:io'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:native_crypto/native_crypto.dart'; @@ -232,4 +234,184 @@ class AESCubit extends Cubit { return; } + + FutureOr encryptFile() async { + emit(state.copyWith(state: State.loading)); + + final sk = await sessionRepository.getSessionKey(); + + if (sk.isErr) { + await loggerRepository.addLog( + const LogError('No SecretKey!\n' + 'Go in Key tab and generate or derive one.'), + ); + emit( + state.copyWith( + state: State.failure, + error: sk.err?.message, + ), + ); + + return; + } + + // Pick file to encrypt + final pickFileResult = await FilePicker.platform.pickFiles(); + + if (pickFileResult == null) { + await loggerRepository.addLog( + const LogError('No file selected.'), + ); + emit( + state.copyWith( + state: State.failure, + error: 'No file selected.', + ), + ); + + return; + } + + final file = File(pickFileResult.files.single.path!); + + // Pick folder to store the encrypted file + final resultFolder = await FilePicker.platform.getDirectoryPath(); + + if (resultFolder == null) { + await loggerRepository.addLog( + const LogError('No folder selected.'), + ); + emit( + state.copyWith( + state: State.failure, + error: 'No folder selected.', + ), + ); + + return; + } + + final folder = Directory(resultFolder); + + final encryption = await cryptoRepository.encryptFile( + file, + folder.uri, + sk.ok!, + ); + + emit( + await encryption.foldAsync( + (_) async { + await loggerRepository.addLog( + const LogInfo('File successfully encrypted.\n'), + ); + return state.copyWith( + state: State.success, + plainTextFile: '', + cipherTextFile: '', + ); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during encryption.'), + ); + return state.copyWith( + state: State.failure, + error: error.message, + ); + }, + ), + ); + + return; + } + + FutureOr decryptFile() async { + emit(state.copyWith(state: State.loading)); + + final sk = await sessionRepository.getSessionKey(); + + if (sk.isErr) { + await loggerRepository.addLog( + const LogError('No SecretKey!\n' + 'Go in Key tab and generate or derive one.'), + ); + emit( + state.copyWith( + state: State.failure, + error: sk.err?.message, + ), + ); + + return; + } + + await FilePicker.platform.clearTemporaryFiles(); + + final resultPickFile = await FilePicker.platform.pickFiles(); + + if (resultPickFile == null) { + await loggerRepository.addLog( + const LogError('No file selected.'), + ); + emit( + state.copyWith( + state: State.failure, + error: 'No file selected.', + ), + ); + + return; + } + + final file = File(resultPickFile.files.single.path!); + + // Pick folder to store the encrypted file + final resultFolder = await FilePicker.platform.getDirectoryPath(); + + if (resultFolder == null) { + await loggerRepository.addLog( + const LogError('No folder selected.'), + ); + emit( + state.copyWith( + state: State.failure, + error: 'No folder selected.', + ), + ); + + return; + } + + final folder = Directory(resultFolder); + + final decryption = + await cryptoRepository.decryptFile(file, folder.uri, sk.ok!); + + emit( + await decryption.foldAsync( + (_) async { + await loggerRepository.addLog( + const LogInfo('File successfully decrypted.\n'), + ); + return state.copyWith( + state: State.success, + plainTextFile: '', + cipherTextFile: '', + ); + }, + (error) async { + await loggerRepository.addLog( + LogError(error.message ?? 'Error during decryption.'), + ); + return state.copyWith( + state: State.failure, + error: error.message, + ); + }, + ), + ); + + return; + } } diff --git a/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart b/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart index f518477..4b96dca 100644 --- a/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart +++ b/packages/native_crypto/example/lib/presentation/cipher/state_management/aes_state_management.dart @@ -79,6 +79,27 @@ class AESStateManagement extends CubitScreen { onPressed: () => bloc(context).decryptFromMemory(), ), ), + const Padding( + padding: EdgeInsets.all(8), + child: Text( + 'File', + style: AppTypography.title, + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Encrypt file', + onPressed: () => bloc(context).encryptFile(), + ), + ), + Padding( + padding: const EdgeInsets.all(8), + child: ButtonStateManagement( + label: 'Decrypt file', + onPressed: () => bloc(context).decryptFile(), + ), + ), const Padding( padding: EdgeInsets.all(8), child: Text( diff --git a/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart index d4a0c5e..b007ce4 100644 --- a/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart +++ b/packages/native_crypto/example/lib/presentation/home/blocs/mode_switcher/mode_switcher_cubit.dart @@ -8,7 +8,9 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:native_crypto_example/data/repositories/crypto_repository_switchable_impl.dart'; import 'package:native_crypto_example/domain/entities/mode.dart'; +import 'package:native_crypto_example/domain/repositories/crypto_repository.dart'; import 'package:native_crypto_example/domain/repositories/session_repository.dart'; part 'mode_switcher_state.dart'; @@ -16,8 +18,11 @@ part 'mode_switcher_state.dart'; class ModeSwitcherCubit extends Cubit { ModeSwitcherCubit( this.sessionRepository, + this.cryptoRepository, ) : super(const ModeSwitcherState(NativeCryptoMode())); + SessionRepository sessionRepository; + CryptoRepository cryptoRepository; FutureOr switchMode() async { final currentMode = await sessionRepository.getCurrentMode(); @@ -31,9 +36,16 @@ class ModeSwitcherCubit extends Cubit { } sessionRepository.setCurrentMode(newMode); + if (cryptoRepository is CryptoRepositorySwitchableImpl) { + (cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode; + } } else { newMode = const NativeCryptoMode(); sessionRepository.setCurrentMode(newMode); + + if (cryptoRepository is CryptoRepositorySwitchableImpl) { + (cryptoRepository as CryptoRepositorySwitchableImpl).mode = newMode; + } } emit(ModeSwitcherState(newMode)); diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart index 991df1f..fbd378d 100644 --- a/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart +++ b/packages/native_crypto/example/lib/presentation/home/state_management/home_state_management.dart @@ -5,12 +5,12 @@ // https://opensource.org/licenses/MIT. import 'package:flutter/material.dart'; +import 'package:native_crypto_example/presentation/benchmark/state_management/benchmark_state_management.dart'; import 'package:native_crypto_example/presentation/cipher/state_management/aes_state_management.dart'; import 'package:native_crypto_example/presentation/hash/state_management/hash_state_management.dart'; import 'package:native_crypto_example/presentation/home/blocs/navigation_bar/navigation_bar_cubit.dart'; import 'package:native_crypto_example/presentation/home/state_management/widgets/app_bar_state_management.dart'; import 'package:native_crypto_example/presentation/home/state_management/widgets/bottom_navigation_bar_state_management.dart'; -import 'package:native_crypto_example/presentation/home/widgets/blank.dart'; import 'package:native_crypto_example/presentation/kdf/state_management/key_derivation_state_management.dart'; import 'package:native_crypto_example/presentation/test_vectors/state_management/test_vectors_state_management.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; @@ -24,7 +24,7 @@ class HomeStateManagement HashStateManagement(), AESStateManagement(), TestVectorsStateManagement(), - const Blank() + const BenchmarkStateManagement(), ]; @override diff --git a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart index 1f51bbe..6b522a4 100644 --- a/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart +++ b/packages/native_crypto/example/lib/presentation/home/state_management/widgets/app_bar_state_management.dart @@ -22,12 +22,12 @@ class AppBarStateManagement : 'PointyCastle', ), backgroundColor: state.currentMode.primaryColor, - // TODO(hpcl): enable mode switcher - // actions: [ - // Switch( - // value: state.currentMode == const NativeCryptoMode(), - // onChanged: (_) => bloc(context).switchMode(), - // ) - // ], + actions: [ + Switch( + activeColor: Colors.white, + value: state.currentMode == const NativeCryptoMode(), + onChanged: (_) => bloc(context).switchMode(), + ) + ], ); } diff --git a/packages/native_crypto/example/pubspec.yaml b/packages/native_crypto/example/pubspec.yaml index 73185a7..33e895d 100644 --- a/packages/native_crypto/example/pubspec.yaml +++ b/packages/native_crypto/example/pubspec.yaml @@ -32,6 +32,7 @@ dependencies: hosted: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub/ version: 2.0.0 get_it: ^7.2.0 + file_picker: ^5.2.7 dev_dependencies: flutter_test: { sdk: flutter } -- 2.47.2 From 550fe8b73e178f3e6e008c6482921c51c567dec3 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 5 Apr 2023 16:41:54 +0200 Subject: [PATCH 18/21] refactor(ios): remove useless lines/classes --- .../ios/Classes/NativeCrypto.swift | 1 - .../Classes/utils/HashAlgorithmParser.swift | 2 - .../ios/Classes/utils/Task.swift | 45 ------------------- 3 files changed, 48 deletions(-) delete mode 100644 packages/native_crypto_ios/ios/Classes/utils/Task.swift diff --git a/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift b/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift index d9e8dc1..ac94c52 100644 --- a/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift +++ b/packages/native_crypto_ios/ios/Classes/NativeCrypto.swift @@ -25,7 +25,6 @@ public class NativeCrypto: NSObject, NativeCryptoAPI { case .sha256: return FlutterStandardTypedData(bytes: Data(HMAC(key: symmetricKey).finalize())) case .sha384: return FlutterStandardTypedData(bytes: Data(HMAC(key: symmetricKey).finalize())) case .sha512: return FlutterStandardTypedData(bytes: Data(HMAC(key: symmetricKey).finalize())) - @unknown default: fatalError("Unknown algorithm") } } diff --git a/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithmParser.swift b/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithmParser.swift index 52e160e..d097a56 100644 --- a/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithmParser.swift +++ b/packages/native_crypto_ios/ios/Classes/utils/HashAlgorithmParser.swift @@ -15,7 +15,6 @@ public class HashAlgorithmParser { case .sha256: return SHA256.init() case .sha384: return SHA384.init() case .sha512: return SHA512.init() - @unknown default: fatalError("Unknown algorithm") } } @@ -24,7 +23,6 @@ public class HashAlgorithmParser { case .sha256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) case .sha384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384) case .sha512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) - @unknown default: fatalError("Unknown algorithm") } } diff --git a/packages/native_crypto_ios/ios/Classes/utils/Task.swift b/packages/native_crypto_ios/ios/Classes/utils/Task.swift deleted file mode 100644 index 4a9d049..0000000 --- a/packages/native_crypto_ios/ios/Classes/utils/Task.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Foundation - -class Task { - - var task: () throws -> T - private var successful: Bool = false - private var result: T? = nil - private var exception: Error? = nil - - init(task: @escaping () throws -> T) { - self.task = task - } - - func isSuccessful() -> Bool { - return successful - } - - func getResult() -> T? { - return result - } - - func getException() -> Error { - if (exception != nil) { - return exception! - } else { - return NativeCryptoError.unknownError() - } - } - - func call() { - do { - result = try task() - exception = nil - successful = true - } catch { - exception = error - result = nil - successful = false - } - } - - func finalize(callback: (_ task: Task) -> Void) { - callback(self) - } -} -- 2.47.2 From 7dc07c693a0e07a1121be9978f01b2e1436688c0 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 5 Apr 2023 16:42:16 +0200 Subject: [PATCH 19/21] feat(api): update example for ios file encryption --- .../native_crypto/example/ios/Podfile.lock | 53 +++++++++++++++++++ .../example/ios/Runner/Info.plist | 4 ++ 2 files changed, 57 insertions(+) diff --git a/packages/native_crypto/example/ios/Podfile.lock b/packages/native_crypto/example/ios/Podfile.lock index ab8fa0d..cad8cae 100644 --- a/packages/native_crypto/example/ios/Podfile.lock +++ b/packages/native_crypto/example/ios/Podfile.lock @@ -1,21 +1,74 @@ PODS: + - DKImagePickerController/Core (4.3.4): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.4) + - DKImagePickerController/PhotoGallery (4.3.4): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.4) + - DKPhotoGallery (0.0.17): + - DKPhotoGallery/Core (= 0.0.17) + - DKPhotoGallery/Model (= 0.0.17) + - DKPhotoGallery/Preview (= 0.0.17) + - DKPhotoGallery/Resource (= 0.0.17) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.17): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.17): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter - Flutter (1.0.0) - native_crypto_ios (0.0.1): - Flutter + - SDWebImage (5.15.5): + - SDWebImage/Core (= 5.15.5) + - SDWebImage/Core (5.15.5) + - SwiftyGif (5.4.4) DEPENDENCIES: + - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - native_crypto_ios (from `.symlinks/plugins/native_crypto_ios/ios`) +SPEC REPOS: + trunk: + - DKImagePickerController + - DKPhotoGallery + - SDWebImage + - SwiftyGif + EXTERNAL SOURCES: + file_picker: + :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter native_crypto_ios: :path: ".symlinks/plugins/native_crypto_ios/ios" SPEC CHECKSUMS: + DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac + DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 + file_picker: ce3938a0df3cc1ef404671531facef740d03f920 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 native_crypto_ios: de03ec2f594e8d41bcba2341b7ad57fd926ada5d + SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe + SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b diff --git a/packages/native_crypto/example/ios/Runner/Info.plist b/packages/native_crypto/example/ios/Runner/Info.plist index ff3e5aa..c8a011c 100644 --- a/packages/native_crypto/example/ios/Runner/Info.plist +++ b/packages/native_crypto/example/ios/Runner/Info.plist @@ -47,5 +47,9 @@ UIApplicationSupportsIndirectInputEvents + UISupportsDocumentBrowser + + LSSupportsOpeningDocumentsInPlace + -- 2.47.2 From 01832a3b033fcd3eb816dd0532a5ff576e9c01ad Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 5 Apr 2023 16:46:35 +0200 Subject: [PATCH 20/21] chore(api): file format + update readme file --- README.md | 190 ++++++++++++++---- .../lib/src/ciphers/aes/aes_key_size.dart | 2 +- .../lib/src/core/constants/constants.dart | 2 +- .../lib/src/core/enums/encoding.dart | 3 + .../lib/src/core/enums/hash_algorithm.dart | 2 + .../lib/src/core/extensions/extensions.dart | 2 +- .../lib/src/core/utils/utils.dart | 2 +- .../lib/src/domain/base_key.dart | 2 +- .../native_crypto/lib/src/domain/cipher.dart | 1 + .../native_crypto/lib/src/domain/domain.dart | 2 +- .../test/mocks/mock_native_crypto_api.dart | 16 +- 11 files changed, 171 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 6ec789e..ed6621c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,3 @@ -/* - * Copyright 2019-2023 Hugo Pointcheval - * - * Use of this source code is governed by an MIT-style - * license that can be found in the LICENSE file or at - * https://opensource.org/licenses/MIT. - */

Fast and powerful cryptographic functions for Flutter.
@@ -42,6 +35,71 @@ For comparison, on a *iPhone 13*, you can encrypt/decrypt a message of **2MiB** In short, NativeCrypto is incomparable with PointyCastle. +## Features + +* Hash functions + - SHA-256 + - SHA-384 + - SHA-512 +* HMAC functions + - HMAC-SHA-256 + - HMAC-SHA-384 + - HMAC-SHA-512 +* Secure random +* PBKDF2 +* AES + - Uint8List encryption/decryption + - File encryption/decryption + +## Quick start + +```dart +import 'package:native_crypto/native_crypto.dart'; + +Future main() async { + // Message to encrypt + final Uint8List message = 'Hello World!'.toBytes(); + + // Ask user for a password + final String password = await getPassword(); + + // Initialize a PBKDF2 object + final Pbkdf2 pbkdf2 = Pbkdf2( + length: 32, // 32 bytes + iterations: 1000, + salt: 'salt'.toBytes(), + hashAlgorithm: HashAlgorithm.sha256, + ); + + // Derive a secret key from the password + final SecretKey secretKey = await pbkdf2(password: password); + + // Initialize an AES cipher + final AES cipher = AES( + key: secretKey, + mode: AESMode.gcm, + padding: AESPadding.none, + ); + + // Encrypt the message + final CipherText cipherText = await cipher.encrypt(message); + + // Decrypt the message + final Uint8List decryptedMessage = await cipher.decrypt(cipherText); + + // Verify and print the decrypted message + assert(listEquals(message, decryptedMessage)); + + print(decryptedMessage.toStr()); +} +``` + +Check the [example](./native_crypto/example) for a complete example. + +Please take a look a the compatibility table below to check if your target is supported. + +> Note: This **Flutter** example must run on a real device or a simulator. + ## Usage First, check compatibility with your targets. @@ -50,26 +108,42 @@ First, check compatibility with your targets. | --- | ------- | ----- | ----- | ------- | --- | | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +> Warning: NativeCrypto 0.2.0+ is not compatible with lower NativeCrypto versions. Especially, with NativeCrypto 0.0. X because the cipher mode is not the same. Now, NativeCrypto uses AES-GCM mode instead of AES-CBC mode. (See [Changelog](./CHANGELOG.md)) + #### Hash -To digest a message, you can use the following function: +To digest a message, you'll need to initialize a Hasher object implementing `Hash` . Then, you can digest your message. ```dart -Uint8List hash = await HashAlgorithm.sha256.digest(message); +Hash hasher = Sha256(); +Uint8List digest = await hasher.digest(message); ``` > In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512 -#### Keys +#### HMAC -You can build a `SecretKey` from a utf8, base64, base16 (hex) strings or raw bytes. You can also generate a SecretKey from secure random. +To generate a HMAC, you'll need to initialize a `Hmac` object. Then, you can generate a HMAC from a message and a secret key. ```dart -SecretKey secretKey = SecretKey(Uint8List.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74])); +Hmac hmac = HmacSha256(); +Uint8List hmac = await hmac.digest(message, secretKey); +``` + +> In NativeCrypto, you can use the following HMAC functions: HMAC-SHA-256, HMAC-SHA-384, HMAC-SHA-512 + +#### Keys + +You can build a `SecretKey` from utf8, utf16, base64, base16 (hex) strings, int list or raw bytes. You can also generate a SecretKey from secure random. + +```dart +SecretKey secretKey = SecretKey(bytes); // bytes is a Uint8List SecretKey secretKey = SecretKey.fromUtf8('secret'); +SecretKet secretKey = SecretKey.fromUtf16('secret'); SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0'); SecretKey secretKey = SecretKey.fromBase16('63657274'); -SecretKey secretKey = await SecretKey.fromSecureRandom(256); +SecretKey secretKey = SecretKey.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74]); +SecretKey secretKey = await SecretKey.fromSecureRandom(32); // 32 bytes ``` #### Key derivation @@ -79,20 +153,21 @@ You can derive a `SecretKey` using **PBKDF2**. First, you need to initialize a `Pbkdf2` object. ```dart -Pbkdf2 pbkdf2 = Pbkdf2( - keyBytesCount: 32, +final Pbkdf2 pbkdf2 = Pbkdf2( + length: 32, // 32 bytes iterations: 1000, - algorithm: HashAlgorithm.sha512, + salt: salt.toBytes(), + hashAlgorithm: HashAlgorithm.sha256, ); ``` -Then, you can derive a `SecretKey` from a password and salt. +Then, you can derive a `SecretKey` from a password. ```dart -SecretKey secretKey = await pbkdf2.derive(password: password, salt: 'salt'); +SecretKey secretKey = await pbkdf2(password: password); ``` -> In NativeCrypto, you can use the following key derivation function: PBKDF2 +> Note: Pbkdf2 is a callable class. You can use it like a function. #### Cipher @@ -101,44 +176,79 @@ And now, you can use the `SecretKey` to encrypt/decrypt a message. First, you need to initialize a `Cipher` object. ```dart -AES cipher = AES(secretKey); +final AES cipher = AES( + key: key, + mode: AESMode.gcm, + padding: AESPadding.none, +); ``` Then, you can encrypt your message. ```dart -CipherTextWrapper wrapper = await cipher.encrypt(message); - -CipherText cipherText = wrapper.unwrap(); -// same as -CipherText cipherText = wrapper.single; - -// or - -List cipherTexts = wrapper.unwrap>(); -// same as -List cipherTexts = wrapper.list; +final CipherText cipherText = await cipher.encrypt(message); ``` -After an encryption you obtain a `CipherTextWrapper` which contains `CipherText` or `List` depending on the message size. It's up to you to know how to unwrap the `CipherTextWrapper` depending the chunk size you configured. +After an encryption you obtain a `CipherText` which contains chunks. You can get the underlying bytes with `cipherText.bytes` . -Uppon receiving encrypted message, you can decrypt it. -You have to reconstruct the wrapper before decrypting. +Uppon receiving encrypted message `receivedData` , you can decrypt it. +You have to reconstruct the ciphertext and the setup the chunk factory. ```dart -CipherTextWrapper wrapper = CipherTextWrapper.fromBytes( - data, - ivLength: AESMode.gcm.ivLength, - tagLength: AESMode.gcm.tagLength, -); +final CipherText receivedCipherText CipherText( + receivedData, + chunkFactory: (bytes) => AESCipherChunk( + bytes, + ivLength: cipher.mode.ivLength, + tagLength: cipher.mode.tagLength, + ), +), ``` Then, you can decrypt your message. ```dart -Uint8List message = await cipher.decrypt(wrapper); +Uint8List message = await cipher.decrypt(receivedCipherText); ``` +#### Files + +You can encrypt/decrypt files. + +First, you need to initialize a `Cipher` object. + +```dart +final AES cipher = AES( + key: key, + mode: AESMode.gcm, + padding: AESPadding.none, +); +``` + +Then, you can encrypt your file. + +```dart +await cipher.encryptFile(plainText, cipherText); +``` + +> Note: `plainText` and `cipherText` are `File` objects. + +You can decrypt your file. + +```dart +await cipher.decryptFile(cipherText, plainText); +``` + +#### Advanced + +You can force the use of a specific IV. Please note that the IV must be unique for each encryption. + +```dart +final CipherText cipherText = await cipher.encryptWithIV(message, iv); +``` + +⚠️ Use `encrypt(...)` instead of `encryptWithIV(...)` if you don't know what you are doing. + ## Development ### Android diff --git a/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart b/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart index 7bc8f26..72e81d7 100644 --- a/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart +++ b/packages/native_crypto/lib/src/ciphers/aes/aes_key_size.dart @@ -1,5 +1,5 @@ // Copyright 2019-2023 Hugo Pointcheval -// +// // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. diff --git a/packages/native_crypto/lib/src/core/constants/constants.dart b/packages/native_crypto/lib/src/core/constants/constants.dart index 97b111c..37fe0b5 100644 --- a/packages/native_crypto/lib/src/core/constants/constants.dart +++ b/packages/native_crypto/lib/src/core/constants/constants.dart @@ -6,7 +6,7 @@ abstract class Constants { /// The default chunk size in bytes used for encryption and decryption. - /// + /// /// ~32MB static const int defaultChunkSize = 33554432; diff --git a/packages/native_crypto/lib/src/core/enums/encoding.dart b/packages/native_crypto/lib/src/core/enums/encoding.dart index 2b69508..fb459dd 100644 --- a/packages/native_crypto/lib/src/core/enums/encoding.dart +++ b/packages/native_crypto/lib/src/core/enums/encoding.dart @@ -8,10 +8,13 @@ enum Encoding { /// UTF-8 encoding, as defined by the Unicode standard. utf8, + /// UTF-16 encoding, as defined by the Unicode standard. utf16, + /// Base64 encoding, as defined by RFC 4648. base64, + /// Hexadecimal encoding. base16, } diff --git a/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart b/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart index 7748efb..860f609 100644 --- a/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart +++ b/packages/native_crypto/lib/src/core/enums/hash_algorithm.dart @@ -8,8 +8,10 @@ enum HashAlgorithm { /// The SHA-256 hash algorithm. sha256, + /// The SHA-384 hash algorithm. sha384, + /// The SHA-512 hash algorithm. sha512, } diff --git a/packages/native_crypto/lib/src/core/extensions/extensions.dart b/packages/native_crypto/lib/src/core/extensions/extensions.dart index f10b73b..811436f 100644 --- a/packages/native_crypto/lib/src/core/extensions/extensions.dart +++ b/packages/native_crypto/lib/src/core/extensions/extensions.dart @@ -1,5 +1,5 @@ // Copyright 2019-2023 Hugo Pointcheval -// +// // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. diff --git a/packages/native_crypto/lib/src/core/utils/utils.dart b/packages/native_crypto/lib/src/core/utils/utils.dart index a673ed2..40b6a15 100644 --- a/packages/native_crypto/lib/src/core/utils/utils.dart +++ b/packages/native_crypto/lib/src/core/utils/utils.dart @@ -1,5 +1,5 @@ // Copyright 2019-2023 Hugo Pointcheval -// +// // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. diff --git a/packages/native_crypto/lib/src/domain/base_key.dart b/packages/native_crypto/lib/src/domain/base_key.dart index 2dc92cb..be6e135 100644 --- a/packages/native_crypto/lib/src/domain/base_key.dart +++ b/packages/native_crypto/lib/src/domain/base_key.dart @@ -12,7 +12,7 @@ import 'package:native_crypto/src/domain/byte_array.dart'; /// [BaseKey] is a [ByteArray] that can be used to store keys. /// /// This interface is implemented by all the key classes. -/// +/// /// Note: [BaseKey] is named [BaseKey] instead of Key to avoid conflicts with /// the Key class from Flutter. /// {@endtemplate} diff --git a/packages/native_crypto/lib/src/domain/cipher.dart b/packages/native_crypto/lib/src/domain/cipher.dart index 6ad3ae1..ee9a475 100644 --- a/packages/native_crypto/lib/src/domain/cipher.dart +++ b/packages/native_crypto/lib/src/domain/cipher.dart @@ -16,6 +16,7 @@ import 'package:native_crypto/src/domain/cipher_chunk.dart'; abstract class Cipher { /// {@macro cipher} const Cipher(); + /// Encrypts a [Uint8List] and returns a [CipherText]. Future> encrypt(Uint8List plainText); diff --git a/packages/native_crypto/lib/src/domain/domain.dart b/packages/native_crypto/lib/src/domain/domain.dart index 58cc8b7..a22b2cc 100644 --- a/packages/native_crypto/lib/src/domain/domain.dart +++ b/packages/native_crypto/lib/src/domain/domain.dart @@ -1,5 +1,5 @@ // Copyright 2019-2023 Hugo Pointcheval -// +// // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. diff --git a/packages/native_crypto/test/mocks/mock_native_crypto_api.dart b/packages/native_crypto/test/mocks/mock_native_crypto_api.dart index d2e1831..a1b2e1b 100644 --- a/packages/native_crypto/test/mocks/mock_native_crypto_api.dart +++ b/packages/native_crypto/test/mocks/mock_native_crypto_api.dart @@ -215,13 +215,15 @@ class MockNativeCryptoAPI implements NativeCryptoAPI { HashAlgorithm argAlgorithm, ) { if (pbkdf2Fn != null) { - return Future.value(pbkdf2Fn!( - argPassword, - argSalt, - argIterations, - argLength, - argAlgorithm.toString(), - ),); + return Future.value( + pbkdf2Fn!( + argPassword, + argSalt, + argIterations, + argLength, + argAlgorithm.toString(), + ), + ); } else { return Future.value(Uint8List.fromList([1, 2, 3])); } -- 2.47.2 From c98b9947b4f22ca35a22d1be09ee4405edd4d314 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 5 Apr 2023 17:02:38 +0200 Subject: [PATCH 21/21] docs: update readmes/licences --- README.md | 14 +- packages/native_crypto/LICENSE | 4 +- packages/native_crypto/README.md | 134 +----------------- packages/native_crypto_android/LICENSE | 4 +- packages/native_crypto_android/README.md | 16 +-- packages/native_crypto_ios/LICENSE | 4 +- packages/native_crypto_ios/README.md | 14 +- .../README.md | 6 +- 8 files changed, 37 insertions(+), 159 deletions(-) diff --git a/README.md b/README.md index ed6621c..c01bf49 100644 --- a/README.md +++ b/README.md @@ -102,13 +102,25 @@ Please take a look a the compatibility table below to check if your target is su ## Usage +#### Compatibility + First, check compatibility with your targets. | iOS | Android | MacOS | Linux | Windows | Web | | --- | ------- | ----- | ----- | ------- | --- | | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | -> Warning: NativeCrypto 0.2.0+ is not compatible with lower NativeCrypto versions. Especially, with NativeCrypto 0.0. X because the cipher mode is not the same. Now, NativeCrypto uses AES-GCM mode instead of AES-CBC mode. (See [Changelog](./CHANGELOG.md)) +> Warning: NativeCrypto 0.2.0+ is not compatible with lower NativeCrypto versions. Especially, with NativeCrypto 0.0. X because the cipher mode is not the same. Now, NativeCrypto uses AES-GCM mode instead of AES-CBC mode. (See [Changelog](./CHANGELOG.md)) + +NativeCrypto ciphertexts are formatted as follow: + +``` ++------------------+--------------------+------------------+ +| Nonce (12 bytes) | Cipher text (n-28) | Tag (16 bytes) | ++------------------+--------------------+------------------+ +``` + +> Warning: If your data comes from another source, make sur to use the same format. #### Hash diff --git a/packages/native_crypto/LICENSE b/packages/native_crypto/LICENSE index 082d930..67b1d53 100644 --- a/packages/native_crypto/LICENSE +++ b/packages/native_crypto/LICENSE @@ -2,7 +2,7 @@ NativeCrypto MIT License -Copyright (c) 2019 - 2022 Hugo Pointcheval +Copyright (c) 2019 - 2023 Hugo Pointcheval Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/packages/native_crypto/README.md b/packages/native_crypto/README.md index 507b7ec..f412888 100644 --- a/packages/native_crypto/README.md +++ b/packages/native_crypto/README.md @@ -1,133 +1,3 @@ -

- -

Fast and powerful cryptographic functions for Flutter.
-

+# NativeCrypto -
- ---- - -[[Changelog]](./CHANGELOG.md) | [[License]](./LICENSE) - ---- - -## About - -The goal of this plugin is to provide a fast and powerful cryptographic functions by calling native libraries. On Android, it uses [javax.cypto](https://developer.android.com/reference/javax/crypto/package-summary), and on iOS, it uses [CommonCrypto](https://opensource.apple.com/source/CommonCrypto/) and [CryptoKit](https://developer.apple.com/documentation/cryptokit/) - -I started this projet because I wanted to add cryptographic functions on a Flutter app. But I faced a problem with the well-known [Pointy Castle](https://pub.dev/packages/pointycastle) library: the performance was very poor. Here some benchmarks and comparison: - -![](resources/benchmarks.png) - -For comparison, on a *iPhone 13*, you can encrypt/decrypt a message of **2MiB** in **~5.6s** with PointyCastle and in **~40ms** with NativeCrypto. And on an *OnePlus 5*, you can encrypt/decrypt a message of **50MiB** in **~6min30** with PointyCastle and in less than **~1s** with NativeCrypto. - -In short, NativeCrypto is incomparable with PointyCastle. - -## Usage - -First, check compatibility with your targets. - -| iOS | Android | MacOS | Linux | Windows | Web | -| --- | ------- | ----- | ----- | ------- | --- | -| ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | - -#### Hash - -To digest a message, you can use the following function: - -```dart -Uint8List hash = await HashAlgorithm.sha256.digest(message); -``` - -> In NativeCrypto, you can use the following hash functions: SHA-256, SHA-384, SHA-512 - -#### Keys - -You can build a `SecretKey` from a utf8, base64, base16 (hex) strings or raw bytes. You can also generate a SecretKey from secure random. - -```dart -SecretKey secretKey = SecretKey(Uint8List.fromList([0x73, 0x65, 0x63, 0x72, 0x65, 0x74])); -SecretKey secretKey = SecretKey.fromUtf8('secret'); -SecretKey secretKey = SecretKey.fromBase64('c2VjcmV0'); -SecretKey secretKey = SecretKey.fromBase16('63657274'); -SecretKey secretKey = await SecretKey.fromSecureRandom(256); -``` - -#### Key derivation - -You can derive a `SecretKey` using **PBKDF2**. - -First, you need to initialize a `Pbkdf2` object. - -```dart -Pbkdf2 pbkdf2 = Pbkdf2( - keyBytesCount: 32, - iterations: 1000, - algorithm: HashAlgorithm.sha512, -); -``` - -Then, you can derive a `SecretKey` from a password and salt. - -```dart -SecretKey secretKey = await pbkdf2.derive(password: password, salt: 'salt'); -``` - -> In NativeCrypto, you can use the following key derivation function: PBKDF2 - -#### Cipher - -And now, you can use the `SecretKey` to encrypt/decrypt a message. - -First, you need to initialize a `Cipher` object. - -```dart -AES cipher = AES(secretKey); -``` - -Then, you can encrypt your message. - -```dart -CipherTextWrapper wrapper = await cipher.encrypt(message); - -CipherText cipherText = wrapper.unwrap(); -// same as -CipherText cipherText = wrapper.single; - -// or - -List cipherTexts = wrapper.unwrap>(); -// same as -List cipherTexts = wrapper.list; -``` - -After an encryption you obtain a `CipherTextWrapper` which contains `CipherText` or `List` depending on the message size. It's up to you to know how to unwrap the `CipherTextWrapper` depending the chunk size you configured. - -Uppon receiving encrypted message, you can decrypt it. -You have to reconstruct the wrapper before decrypting. - -```dart -CipherTextWrapper wrapper = CipherTextWrapper.fromBytes( - data, - ivLength: AESMode.gcm.ivLength, - tagLength: AESMode.gcm.tagLength, -); -``` - -Then, you can decrypt your message. - -```dart -Uint8List message = await cipher.decrypt(wrapper); -``` \ No newline at end of file +Readme available at [project root](../../README.md). diff --git a/packages/native_crypto_android/LICENSE b/packages/native_crypto_android/LICENSE index dd5d33b..84320e5 100644 --- a/packages/native_crypto_android/LICENSE +++ b/packages/native_crypto_android/LICENSE @@ -2,7 +2,7 @@ NativeCrypto - Android Implementation MIT License -Copyright (c) 2019 - 2022 Hugo Pointcheval +Copyright (c) 2019 - 2023 Hugo Pointcheval Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/packages/native_crypto_android/README.md b/packages/native_crypto_android/README.md index 48b93a5..0c65720 100644 --- a/packages/native_crypto_android/README.md +++ b/packages/native_crypto_android/README.md @@ -1,15 +1,13 @@ -# native_crypto_android +# NativeCrypto - Android Implementation -A new flutter plugin project. +Android Implementation of [NativeCrypto][1] Plugin. ## Getting Started -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/developing-packages/), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. +This project is a starting point for a Flutter [plug-in package][2], a specialized package that includes platform-specific implementation code for Android and/or iOS. -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +For help getting started with Flutter, view our [online documentation][3], which offers tutorials, samples, guidance on mobile development, and a full API reference. +[1]: ../../README.md +[2]: https://flutter.dev/developing-packages/ +[3]: https://flutter.dev/docs diff --git a/packages/native_crypto_ios/LICENSE b/packages/native_crypto_ios/LICENSE index 8c5c1f7..d243b55 100644 --- a/packages/native_crypto_ios/LICENSE +++ b/packages/native_crypto_ios/LICENSE @@ -2,7 +2,7 @@ NativeCrypto - iOS Implementation MIT License -Copyright (c) 2019 - 2022 Hugo Pointcheval +Copyright (c) 2019 - 2023 Hugo Pointcheval Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/packages/native_crypto_ios/README.md b/packages/native_crypto_ios/README.md index a60dd49..beb7c0e 100644 --- a/packages/native_crypto_ios/README.md +++ b/packages/native_crypto_ios/README.md @@ -1,15 +1,13 @@ # NativeCrypto - iOS Implementation -iOS Implementation of NativeCrypto Plugin. +iOS Implementation of [NativeCrypto][1] Plugin. ## Getting Started -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/developing-packages/), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. +This project is a starting point for a Flutter [plug-in package][2], a specialized package that includes platform-specific implementation code for Android and/or iOS. -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +For help getting started with Flutter, view our [online documentation][3], which offers tutorials, samples, guidance on mobile development, and a full API reference. +[1]: ../../README.md +[2]: https://flutter.dev/developing-packages/ +[3]: https://flutter.dev/docs diff --git a/packages/native_crypto_platform_interface/README.md b/packages/native_crypto_platform_interface/README.md index 4aec31b..07bd2a9 100644 --- a/packages/native_crypto_platform_interface/README.md +++ b/packages/native_crypto_platform_interface/README.md @@ -1,12 +1,12 @@ # NativeCrypto - Platform Interface -A common platform interface for the [`native_crypto`][1] plugin. +A common platform interface for the [NativeCrypto][1] plugin. This interface allows platform-specific implementations of the `native_crypto` plugin, as well as the plugin itself, to ensure they are supporting the same interface. ## Usage -To implement a new platform-specific implementation of `native_crypto`, extend [`NativeCryptoPlatform`][2] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `NativeCryptoPlatform` by calling `NativeCryptoPlatform.instance = MyNativeCryptoPlatform()`. +To implement a new platform-specific implementation of `native_crypto`, extend [`NativeCryptoPlatform`][1] with an implementation that performs the platform-specific behavior, and when you register your plugin, set the default `NativeCryptoPlatform` by calling `NativeCryptoPlatform.instance = MyNativeCryptoPlatform()`. ## Pigeon @@ -16,5 +16,5 @@ Run generator with `flutter pub run pigeon --input pigeons/messages.dart`. > Note: Make sure the `lib/src/gen` folder exists before running the generator. -[1]: ../native_crypto +[1]: ../../README.md [2]: lib/native_crypto_platform_interface.dart -- 2.47.2

- -Style: Wyatt Analysis - - - -Maintained with Melos - - - -Build Status - -