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