From 9e0d921564b41f6a209e9beedace6c9f7d210595 Mon Sep 17 00:00:00 2001 From: Pointcheval Hugo Date: Mon, 27 Dec 2021 21:13:26 +0100 Subject: [PATCH] Switch to Federated Plugin layout --- CHANGELOG.md | 25 - README.md | 167 ------ android/.gitignore | 8 - android/build.gradle | 44 -- android/settings.gradle | 1 - android/src/main/AndroidManifest.xml | 3 - .../fr/pointcheval/native_crypto/Cipher.kt | 93 ---- .../native_crypto/HashAlgorithm.kt | 42 -- .../native_crypto/KeyDerivation.kt | 35 -- .../native_crypto/KeyGeneration.kt | 32 -- .../native_crypto/NativeCryptoPlugin.kt | 142 ----- .../native_crypto/PlatformVersionException.kt | 5 - assets/benchmark_pointycastle.png | Bin 123423 -> 0 bytes assets/native_crypto.png | Bin 30364 -> 0 bytes example/android/.gitignore | 7 - .../native_crypto_example/MainActivity.kt | 12 - .../app/src/main/res/values/styles.xml | 8 - example/android/gradle.properties | 4 - .../gradle/wrapper/gradle-wrapper.properties | 6 - example/android/settings.gradle | 15 - example/ios/Flutter/.last_build_id | 1 - example/ios/Podfile | 90 ---- example/ios/Podfile.lock | 22 - example/ios/Runner/Runner-Bridging-Header.h | 1 - example/lib/main.dart | 69 --- example/lib/pages/benchmarkPage.dart | 144 ------ example/lib/pages/cipherPage.dart | 205 -------- example/lib/pages/hashKeyDerivationPage.dart | 180 ------- example/lib/pages/kemPage.dart | 36 -- example/lib/session.dart | 9 - example/lib/utils.dart | 30 -- example/lib/widgets/button.dart | 24 - example/lib/widgets/output.dart | 55 -- ios/Classes/Cipher.swift | 135 ----- ios/Classes/Hash.swift | 74 --- ios/Classes/KeyDerivation.swift | 40 -- ios/Classes/KeyGeneration.swift | 59 --- ios/Classes/NativeCryptoPlugin.h | 4 - ios/Classes/SwiftNativeCryptoPlugin.swift | 158 ------ lib/native_crypto.dart | 19 - lib/src/asym/RSA.dart | 77 --- lib/src/cipher.dart | 101 ---- lib/src/digest.dart | 41 -- lib/src/exceptions.dart | 55 -- lib/src/kem.dart | 52 -- lib/src/key.dart | 118 ----- lib/src/keyderivation.dart | 81 --- lib/src/keyspec.dart | 21 - lib/src/platform.dart | 161 ------ lib/src/sym/AES.dart | 215 -------- lib/src/utils.dart | 141 ----- native_crypto/.gitignore | 389 ++++++++++++++ .metadata => native_crypto/.metadata | 2 +- native_crypto/CHANGELOG.md | 3 + native_crypto/LICENSE | 1 + native_crypto/README.md | 18 + native_crypto/analysis_options.yaml | 4 + {example => native_crypto/example}/.gitignore | 13 +- {example => native_crypto/example}/.metadata | 2 +- {example => native_crypto/example}/README.md | 0 native_crypto/example/analysis_options.yaml | 29 ++ native_crypto/example/android/.gitignore | 13 + .../example}/android/app/build.gradle | 23 +- .../android/app/src/debug/AndroidManifest.xml | 0 .../android/app/src/main/AndroidManifest.xml | 18 +- .../native_crypto_example/MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 0 .../example}/android/build.gradle | 6 +- .../example/android}/gradle.properties | 1 - .../gradle/wrapper/gradle-wrapper.properties | 3 +- native_crypto/example/android/settings.gradle | 11 + .../example}/ios/.gitignore | 2 + .../ios/Flutter/AppFrameworkInfo.plist | 4 +- .../example}/ios/Flutter/Debug.xcconfig | 1 - .../example}/ios/Flutter/Release.xcconfig | 1 - native_crypto/example/ios/Podfile | 41 ++ .../ios/Runner.xcodeproj/project.pbxproj | 484 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 10 +- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example}/ios/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../Icon-App-1024x1024@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../Icon-App-83.5x83.5@2x.png | Bin .../LaunchImage.imageset/Contents.json | 0 .../LaunchImage.imageset/LaunchImage.png | Bin .../LaunchImage.imageset/LaunchImage@2x.png | Bin .../LaunchImage.imageset/LaunchImage@3x.png | Bin .../LaunchImage.imageset/README.md | 0 .../Runner/Base.lproj/LaunchScreen.storyboard | 0 .../ios/Runner/Base.lproj/Main.storyboard | 0 .../example}/ios/Runner/Info.plist | 4 +- .../ios/Runner/Runner-Bridging-Header.h | 1 + native_crypto/example/lib/main.dart | 62 +++ .../example}/pubspec.yaml | 36 +- .../example}/test/widget_test.dart | 4 +- native_crypto/lib/native_crypto.dart | 17 + native_crypto/pubspec.yaml | 28 + .../test}/native_crypto_test.dart | 5 + .gitignore => native_crypto_ios/.gitignore | 318 +++--------- native_crypto_ios/.metadata | 10 + native_crypto_ios/CHANGELOG.md | 3 + LICENSE => native_crypto_ios/LICENSE | 2 +- native_crypto_ios/README.md | 15 + native_crypto_ios/example/.gitignore | 46 ++ native_crypto_ios/example/.metadata | 10 + native_crypto_ios/example/README.md | 16 + .../example/analysis_options.yaml | 29 ++ native_crypto_ios/example/ios/.gitignore | 34 ++ .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + native_crypto_ios/example/ios/Podfile | 41 ++ native_crypto_ios/example/ios/Podfile.lock | 22 + .../ios/Runner.xcodeproj/project.pbxproj | 109 ++-- .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 87 ++++ .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 +++++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../example/ios/Runner/Info.plist | 47 ++ .../ios/Runner/Runner-Bridging-Header.h | 1 + native_crypto_ios/example/lib/main.dart | 79 +++ native_crypto_ios/example/pubspec.yaml | 87 ++++ .../example/test/widget_test.dart | 27 + {ios => native_crypto_ios/ios}/.gitignore | 1 + .../ios}/Assets/.gitkeep | 0 native_crypto_ios/ios/Classes/Cipher.swift | 53 ++ native_crypto_ios/ios/Classes/Hash.swift | 43 ++ native_crypto_ios/ios/Classes/KEM.swift | 78 +++ native_crypto_ios/ios/Classes/Key.swift | 62 +++ .../ios/Classes/NativeCryptoIosPlugin.h | 4 + .../ios/Classes/NativeCryptoIosPlugin.m | 12 +- .../Classes/SwiftNativeCryptoIosPlugin.swift | 95 ++++ .../ios/native_crypto_ios.podspec | 10 +- native_crypto_ios/pubspec.yaml | 22 + native_crypto_platform_interface/.gitignore | 390 ++++++++++++++ native_crypto_platform_interface/.metadata | 10 + native_crypto_platform_interface/CHANGELOG.md | 3 + native_crypto_platform_interface/LICENSE | 1 + native_crypto_platform_interface/README.md | 18 + .../analysis_options.yaml | 4 + .../lib/native_crypto_platform_interface.dart | 75 +++ .../lib/src/method_channel_native_crypto.dart | 107 ++++ .../lib/src/platform_interface.dart | 97 ++++ native_crypto_platform_interface/pubspec.yaml | 17 + ...native_crypto_platform_interface_test.dart | 0 pubspec.yaml | 21 - 200 files changed, 3330 insertions(+), 3451 deletions(-) delete mode 100644 CHANGELOG.md delete mode 100644 README.md delete mode 100644 android/.gitignore delete mode 100644 android/build.gradle delete mode 100644 android/settings.gradle delete mode 100644 android/src/main/AndroidManifest.xml delete mode 100644 android/src/main/kotlin/fr/pointcheval/native_crypto/Cipher.kt delete mode 100644 android/src/main/kotlin/fr/pointcheval/native_crypto/HashAlgorithm.kt delete mode 100644 android/src/main/kotlin/fr/pointcheval/native_crypto/KeyDerivation.kt delete mode 100644 android/src/main/kotlin/fr/pointcheval/native_crypto/KeyGeneration.kt delete mode 100644 android/src/main/kotlin/fr/pointcheval/native_crypto/NativeCryptoPlugin.kt delete mode 100644 android/src/main/kotlin/fr/pointcheval/native_crypto/PlatformVersionException.kt delete mode 100644 assets/benchmark_pointycastle.png delete mode 100644 assets/native_crypto.png delete mode 100644 example/android/.gitignore delete mode 100644 example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt delete mode 100644 example/android/app/src/main/res/values/styles.xml delete mode 100644 example/android/gradle.properties delete mode 100644 example/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 example/android/settings.gradle delete mode 100644 example/ios/Flutter/.last_build_id delete mode 100644 example/ios/Podfile delete mode 100644 example/ios/Podfile.lock delete mode 100644 example/ios/Runner/Runner-Bridging-Header.h delete mode 100644 example/lib/main.dart delete mode 100644 example/lib/pages/benchmarkPage.dart delete mode 100644 example/lib/pages/cipherPage.dart delete mode 100644 example/lib/pages/hashKeyDerivationPage.dart delete mode 100644 example/lib/pages/kemPage.dart delete mode 100644 example/lib/session.dart delete mode 100644 example/lib/utils.dart delete mode 100644 example/lib/widgets/button.dart delete mode 100644 example/lib/widgets/output.dart delete mode 100644 ios/Classes/Cipher.swift delete mode 100644 ios/Classes/Hash.swift delete mode 100644 ios/Classes/KeyDerivation.swift delete mode 100644 ios/Classes/KeyGeneration.swift delete mode 100644 ios/Classes/NativeCryptoPlugin.h delete mode 100644 ios/Classes/SwiftNativeCryptoPlugin.swift delete mode 100644 lib/native_crypto.dart delete mode 100644 lib/src/asym/RSA.dart delete mode 100644 lib/src/cipher.dart delete mode 100644 lib/src/digest.dart delete mode 100644 lib/src/exceptions.dart delete mode 100644 lib/src/kem.dart delete mode 100644 lib/src/key.dart delete mode 100644 lib/src/keyderivation.dart delete mode 100644 lib/src/keyspec.dart delete mode 100644 lib/src/platform.dart delete mode 100644 lib/src/sym/AES.dart delete mode 100644 lib/src/utils.dart create mode 100644 native_crypto/.gitignore rename .metadata => native_crypto/.metadata (82%) create mode 100644 native_crypto/CHANGELOG.md create mode 100644 native_crypto/LICENSE create mode 100644 native_crypto/README.md create mode 100644 native_crypto/analysis_options.yaml rename {example => native_crypto/example}/.gitignore (70%) rename {example => native_crypto/example}/.metadata (82%) rename {example => native_crypto/example}/README.md (100%) create mode 100644 native_crypto/example/analysis_options.yaml create mode 100644 native_crypto/example/android/.gitignore rename {example => native_crypto/example}/android/app/build.gradle (81%) rename {example => native_crypto/example}/android/app/src/debug/AndroidManifest.xml (100%) rename {example => native_crypto/example}/android/app/src/main/AndroidManifest.xml (65%) create mode 100644 native_crypto/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt create mode 100644 native_crypto/example/android/app/src/main/res/drawable-v21/launch_background.xml rename {example => native_crypto/example}/android/app/src/main/res/drawable/launch_background.xml (100%) rename {example => native_crypto/example}/android/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {example => native_crypto/example}/android/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {example => native_crypto/example}/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {example => native_crypto/example}/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {example => native_crypto/example}/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) create mode 100644 native_crypto/example/android/app/src/main/res/values-night/styles.xml create mode 100644 native_crypto/example/android/app/src/main/res/values/styles.xml rename {example => native_crypto/example}/android/app/src/profile/AndroidManifest.xml (100%) rename {example => native_crypto/example}/android/build.gradle (82%) rename {android => native_crypto/example/android}/gradle.properties (78%) rename {android => native_crypto/example/android}/gradle/wrapper/gradle-wrapper.properties (80%) create mode 100644 native_crypto/example/android/settings.gradle rename {example => native_crypto/example}/ios/.gitignore (95%) rename {example => native_crypto/example}/ios/Flutter/AppFrameworkInfo.plist (91%) rename {example => native_crypto/example}/ios/Flutter/Debug.xcconfig (58%) rename {example => native_crypto/example}/ios/Flutter/Release.xcconfig (58%) create mode 100644 native_crypto/example/ios/Podfile create mode 100644 native_crypto/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename {example => native_crypto/example}/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) create mode 100644 native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename {example => native_crypto/example}/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (95%) rename {example/ios/Runner.xcodeproj/project.xcworkspace => native_crypto/example/ios/Runner.xcworkspace}/contents.xcworkspacedata (100%) rename {example => native_crypto/example}/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) create mode 100644 native_crypto/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename {example => native_crypto/example}/ios/Runner/AppDelegate.swift (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (100%) rename {example => native_crypto/example}/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md (100%) rename {example => native_crypto/example}/ios/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename {example => native_crypto/example}/ios/Runner/Base.lproj/Main.storyboard (100%) rename {example => native_crypto/example}/ios/Runner/Info.plist (95%) create mode 100644 native_crypto/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 native_crypto/example/lib/main.dart rename {example => native_crypto/example}/pubspec.yaml (55%) rename {example => native_crypto/example}/test/widget_test.dart (87%) create mode 100644 native_crypto/lib/native_crypto.dart create mode 100644 native_crypto/pubspec.yaml rename {test => native_crypto/test}/native_crypto_test.dart (72%) rename .gitignore => native_crypto_ios/.gitignore (61%) create mode 100644 native_crypto_ios/.metadata create mode 100644 native_crypto_ios/CHANGELOG.md rename LICENSE => native_crypto_ios/LICENSE (96%) create mode 100644 native_crypto_ios/README.md create mode 100644 native_crypto_ios/example/.gitignore create mode 100644 native_crypto_ios/example/.metadata create mode 100644 native_crypto_ios/example/README.md create mode 100644 native_crypto_ios/example/analysis_options.yaml create mode 100644 native_crypto_ios/example/ios/.gitignore create mode 100644 native_crypto_ios/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 native_crypto_ios/example/ios/Flutter/Debug.xcconfig create mode 100644 native_crypto_ios/example/ios/Flutter/Release.xcconfig create mode 100644 native_crypto_ios/example/ios/Podfile create mode 100644 native_crypto_ios/example/ios/Podfile.lock rename {example => native_crypto_ios/example}/ios/Runner.xcodeproj/project.pbxproj (88%) create mode 100644 native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 native_crypto_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename {example => native_crypto_ios/example}/ios/Runner.xcworkspace/contents.xcworkspacedata (100%) create mode 100644 native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 native_crypto_ios/example/ios/Runner/AppDelegate.swift create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 native_crypto_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 native_crypto_ios/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 native_crypto_ios/example/ios/Runner/Info.plist create mode 100644 native_crypto_ios/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 native_crypto_ios/example/lib/main.dart create mode 100644 native_crypto_ios/example/pubspec.yaml create mode 100644 native_crypto_ios/example/test/widget_test.dart rename {ios => native_crypto_ios/ios}/.gitignore (95%) rename {ios => native_crypto_ios/ios}/Assets/.gitkeep (100%) create mode 100644 native_crypto_ios/ios/Classes/Cipher.swift create mode 100644 native_crypto_ios/ios/Classes/Hash.swift create mode 100644 native_crypto_ios/ios/Classes/KEM.swift create mode 100644 native_crypto_ios/ios/Classes/Key.swift create mode 100644 native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.h rename ios/Classes/NativeCryptoPlugin.m => native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.m (52%) create mode 100644 native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift rename ios/native_crypto.podspec => native_crypto_ios/ios/native_crypto_ios.podspec (63%) create mode 100644 native_crypto_ios/pubspec.yaml create mode 100644 native_crypto_platform_interface/.gitignore create mode 100644 native_crypto_platform_interface/.metadata create mode 100644 native_crypto_platform_interface/CHANGELOG.md create mode 100644 native_crypto_platform_interface/LICENSE create mode 100644 native_crypto_platform_interface/README.md create mode 100644 native_crypto_platform_interface/analysis_options.yaml create mode 100644 native_crypto_platform_interface/lib/native_crypto_platform_interface.dart create mode 100644 native_crypto_platform_interface/lib/src/method_channel_native_crypto.dart create mode 100644 native_crypto_platform_interface/lib/src/platform_interface.dart create mode 100644 native_crypto_platform_interface/pubspec.yaml create mode 100644 native_crypto_platform_interface/test/native_crypto_platform_interface_test.dart delete mode 100644 pubspec.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 050c69a..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,25 +0,0 @@ -## 0.0.6 - -**WIP...** - -## 0.0.5 - -* New API -* Add digest support -* Clean platform specific code base -## 0.0.4 - -* Improve AES - -## 0.0.3 - -* Add PBKDF2 support -* Add exceptions -* Improve documentation -## 0.0.2 - -* Add different key size support -* Improve performances -## 0.0.1 - -* First AES cross-platform encryption & decryption implementation. diff --git a/README.md b/README.md deleted file mode 100644 index 998e20b..0000000 --- a/README.md +++ /dev/null @@ -1,167 +0,0 @@ -# NativeCrypto for Flutter - -![NativeCrypto Logo](/assets/native_crypto.png) ---- - -Fast and powerful cryptographic functions thanks to **javax.crypto** and **CommonCrypto**. - -## πŸ“ Table of Contents - -- [About](#about) -- [Getting Started](#getting_started) -- [Example](#example) -- [Usage](#usage) -- [Built Using](#built_using) -- [TODOS](#todos) -- [Authors](#authors) - -## 🧐 About - -The goal of this plugin is to provide simple access to fast and powerful cryptographic functions by calling native libraries. So on **Android** the plugin uses *javax.crypto* and on **iOS** it uses *CommonCrypto*. - -I started this project because using **Pointy Castle** I faced big performance issues on smartphone. It's quite simple, an encryption of 1MB of data in AES256 on an Android device takes **20s** with Pointy Castle against **27ms** using NativeCrypto. - -![Pointy Castle Benchmark](/assets/benchmark_pointycastle.png) - -> We also notice on this benchmark that the AES encryption time does not even increase linearly with size. - -As for NativeCrypto, here is a benchmark realized on an iPhone 11. - -| Size (kB) | NativeCrypto **encryption** time (ms) | -|-----------|---------------------------------------| -| 2 mB | 13 ms -| 4 mB | 17 ms -| 8 mB | 56 ms -| 16 mB | 73 ms -| 32 mB | 120 ms -| 64 mB | 243 ms -| 128 mB | 509 ms -| 256 mB | 1057 ms - -> ~1s for 256 mB ! - -In short, **NativeCrypto** is incomparable to **Pointy Castle** in terms of performance. - -## 🏁 Getting Started - -### Prerequisites - -You'll need: - -- Flutter - -### Installing - -Add these lines in your **pubspec.yaml**: - -```yaml -native_crypto: - git: - url: https://github.com/hugo-pcl/native-crypto-flutter.git - ref: v0.0.x -``` - -> Replace "x" with the current version! - -Then in your code: - -```dart -import 'package:native_crypto/native_crypto.dart'; -``` - -## πŸ” Example - -Look in **example/lib/** for an example app. - -## 🎈 Usage - -To derive a key with **PBKDF2**. - -```dart -PBKDF2 _pbkdf2 = PBKDF2(keyLength: 32, iteration: 1000, hash: HashAlgorithm.SHA512); -await _pbkdf2.derive(password: "password123", salt: 'salty'); -SecretKey key = _pbkdf2.key; -``` - -To generate a key, and create an **AES Cipher** instance. - -```dart -AESCipher aes = await AESCipher.generate( - AESKeySize.bits256, - CipherParameters( - BlockCipherMode.CBC, - PlainTextPadding.PKCS5, - ), -); -``` - -You can also generate key, then create **AES Cipher**. - -```dart -SecretKey _key = await SecretKey.generate(256, CipherAlgorithm.AES); -AESCipher aes = AESCipher( - _key, - CipherParameters( - BlockCipherMode.CBC, - PlainTextPadding.PKCS5, - ), -); -``` - -Then you can encrypt/decrypt data with this cipher. - -```dart -CipherText cipherText = await aes.encrypt(data); -Uint8List plainText = await aes.decrypt(cipherText); -``` - -You can easely get encrypted bytes and IV from a CipherText - -```dart -Uint8List bytes = cipherText.bytes; -Uint8List iv = cipherText.iv; -``` - -To create a cipher text with custom data. - -```dart -CipherText cipherText = AESCipherText(bytes, iv); -``` - -To create a hashed message - -```dart -MessageDigest md = MessageDigest.getInstance("sha256"); -Uint8List hash = await md.digest(message); -``` - -## ⛏️ Built Using - -- [Dart](https://dart.dev) -- [Flutter](https://flutter.dev) - Framework -- [Kotlin](https://kotlinlang.org) - Android Specific code -- [Swift](https://www.apple.com/fr/swift/) - iOS Specific code - -## πŸš€ TODOS - -Here you can check major changes, roadmap and todos. - -I plan to deal with asymmetric cryptography with the implementation of a Key Encapsulation Mechanism. - -- [x] Add PBKDF2 support. -- [x] Implement working cross platform AES encryption/decryption. -- [x] Add Different key sizes support. -- [x] Add exceptions. -- [x] Clean platform specific code. -- [x] Add digest. -- [x] Rework exposed API. -- [x] Add KeyPair generation. -- [ ] Add KEM. -- [ ] Porting NativeCrypto to other platforms... - -You can contribute to this project. - -## ✍️ Authors - -- [Hugo Pointcheval](https://github.com/hugo-pcl) - Idea & Initial work -- [Chisom Maxwell](https://github.com/maxcotech) - For the chunks idea [#2](https://github.com/hugo-pcl/native-crypto-flutter/issues/2) diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index c6cbe56..0000000 --- a/android/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.iml -.gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index 599777e..0000000 --- a/android/build.gradle +++ /dev/null @@ -1,44 +0,0 @@ -group 'fr.pointcheval.native_crypto' -version '1.0-SNAPSHOT' - -buildscript { - ext.kotlin_version = '1.3.50' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -rootProject.allprojects { - repositories { - google() - jcenter() - } -} - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion 29 - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - defaultConfig { - minSdkVersion 16 - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - lintOptions { - disable 'InvalidPackage' - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index 430c95a..0000000 --- a/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'native_crypto' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml deleted file mode 100644 index fcd47dc..0000000 --- a/android/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/android/src/main/kotlin/fr/pointcheval/native_crypto/Cipher.kt b/android/src/main/kotlin/fr/pointcheval/native_crypto/Cipher.kt deleted file mode 100644 index 5302993..0000000 --- a/android/src/main/kotlin/fr/pointcheval/native_crypto/Cipher.kt +++ /dev/null @@ -1,93 +0,0 @@ -package fr.pointcheval.native_crypto - -import java.lang.Exception -import javax.crypto.Cipher -import javax.crypto.SecretKey -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -enum class CipherAlgorithm(val spec: String) { - AES("AES"), -} - -enum class BlockCipherMode(val instance: String) { - CBC("CBC"), - GCM("GCM"), -} - -enum class Padding(val instance: String) { - PKCS5("PKCS5Padding"), - None("NoPadding") -} - -class CipherParameters(private val mode: BlockCipherMode, private val padding: Padding) { - override fun toString(): String { - return mode.instance + "/" + padding.instance - } -} - -class Cipher { - - fun getCipherAlgorithm(dartAlgorithm: String) : CipherAlgorithm { - return when (dartAlgorithm) { - "aes" -> CipherAlgorithm.AES - else -> CipherAlgorithm.AES - } - } - - fun getInstance(mode : String, padding : String) : CipherParameters { - val m = when (mode) { - "cbc" -> BlockCipherMode.CBC - "gcm" -> BlockCipherMode.GCM - else -> throw Exception() - } - val p = when (padding) { - "pkcs5" -> Padding.PKCS5 - else -> Padding.None - } - return CipherParameters(m,p) - } - - fun encrypt(data: ByteArray, key: ByteArray, algorithm: String, mode: String, padding: String) : List { - val algo = getCipherAlgorithm(algorithm) - val params = getInstance(mode, padding) - - val keySpecification = algo.spec + "/" + params.toString() - - val mac = Hash().digest(key + data) - val sk: SecretKey = SecretKeySpec(key, algo.spec) - - val cipher = Cipher.getInstance(keySpecification) - cipher.init(Cipher.ENCRYPT_MODE, sk) - - val encryptedBytes = cipher.doFinal(mac + data) - val iv = cipher.iv - - return listOf(encryptedBytes, iv); - } - - fun decrypt(payload: Collection, key: ByteArray, algorithm: String, mode: String, padding: String) : ByteArray? { - val algo = getCipherAlgorithm(algorithm) - val params = getInstance(mode, padding) - - val keySpecification = algo.spec + "/" + params.toString() - - val sk: SecretKey = SecretKeySpec(key, algo.spec) - val cipher = Cipher.getInstance(keySpecification); - val iv = payload.last(); - val ivSpec = IvParameterSpec(iv) - cipher.init(Cipher.DECRYPT_MODE, sk, ivSpec); - - val decryptedBytes = cipher.doFinal(payload.first()); - - val mac = decryptedBytes.copyOfRange(0, 32) - val decryptedContent : ByteArray = decryptedBytes.copyOfRange(32, decryptedBytes.size) - val verificationMac = Hash().digest(key + decryptedContent) - - return if (mac.contentEquals(verificationMac)) { - decryptedContent - } else { - null; - } - } -} \ No newline at end of file diff --git a/android/src/main/kotlin/fr/pointcheval/native_crypto/HashAlgorithm.kt b/android/src/main/kotlin/fr/pointcheval/native_crypto/HashAlgorithm.kt deleted file mode 100644 index 434f752..0000000 --- a/android/src/main/kotlin/fr/pointcheval/native_crypto/HashAlgorithm.kt +++ /dev/null @@ -1,42 +0,0 @@ -package fr.pointcheval.native_crypto - -import java.security.MessageDigest - -enum class HashAlgorithm(val length : Int) { - SHA1(160), - SHA224(224), - SHA256(256), - SHA384(384), - SHA512(512); -} - - -class Hash() { - fun digest(data: ByteArray?, algorithm: HashAlgorithm): ByteArray { - val func : String = when (algorithm) { - HashAlgorithm.SHA1 -> "SHA-1" - HashAlgorithm.SHA224 -> "SHA-224" - HashAlgorithm.SHA256 -> "SHA-256" - HashAlgorithm.SHA384 -> "SHA-384" - HashAlgorithm.SHA512 -> "SHA-512" - } - val md = MessageDigest.getInstance(func) - return md.digest(data) - } - - fun digest(data: ByteArray?, algorithm: String): ByteArray { - val func : HashAlgorithm = when (algorithm) { - "sha1" -> HashAlgorithm.SHA1 - "sha224" -> HashAlgorithm.SHA224 - "sha256" -> HashAlgorithm.SHA256 - "sha384" -> HashAlgorithm.SHA384 - "sha512" -> HashAlgorithm.SHA512 - else -> HashAlgorithm.SHA256 - } - return digest(data, func) - } - - fun digest(data: ByteArray?): ByteArray { - return digest(data, HashAlgorithm.SHA256) - } -} diff --git a/android/src/main/kotlin/fr/pointcheval/native_crypto/KeyDerivation.kt b/android/src/main/kotlin/fr/pointcheval/native_crypto/KeyDerivation.kt deleted file mode 100644 index 8931164..0000000 --- a/android/src/main/kotlin/fr/pointcheval/native_crypto/KeyDerivation.kt +++ /dev/null @@ -1,35 +0,0 @@ -package fr.pointcheval.native_crypto - -import android.os.Build -import java.lang.IllegalArgumentException -import javax.crypto.SecretKeyFactory -import javax.crypto.spec.PBEKeySpec - -class KeyDerivation { - fun pbkdf2(password: String, salt: String, keyLength: Int, iteration: Int, algorithm: String): ByteArray { - val chars: CharArray = password.toCharArray() - val availableHashAlgorithm: Map = mapOf( - "sha1" to "PBKDF2withHmacSHA1", - "sha224" to "PBKDF2withHmacSHA224", - "sha256" to "PBKDF2WithHmacSHA256", - "sha384" to "PBKDF2withHmacSHA384", - "sha512" to "PBKDF2withHmacSHA512" - ) - - if (Build.VERSION.SDK_INT >= 26) { - // SHA-1 and SHA-2 implemented - val spec = PBEKeySpec(chars, salt.toByteArray(), iteration, keyLength * 8) - val skf: SecretKeyFactory = SecretKeyFactory.getInstance(availableHashAlgorithm[algorithm]); - return skf.generateSecret(spec).encoded - } else if (Build.VERSION.SDK_INT >= 10) { - // Only SHA-1 is implemented - if (!algorithm.equals("sha1")) { - throw PlatformVersionException("Only SHA1 is implemented on this SDK version!") - } - val spec = PBEKeySpec(chars, salt.toByteArray(), iteration, keyLength * 8) - val skf: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2withHmacSHA1"); - return skf.generateSecret(spec).encoded - } - throw PlatformVersionException("Invalid SDK version!") - } -} \ No newline at end of file diff --git a/android/src/main/kotlin/fr/pointcheval/native_crypto/KeyGeneration.kt b/android/src/main/kotlin/fr/pointcheval/native_crypto/KeyGeneration.kt deleted file mode 100644 index b937732..0000000 --- a/android/src/main/kotlin/fr/pointcheval/native_crypto/KeyGeneration.kt +++ /dev/null @@ -1,32 +0,0 @@ -package fr.pointcheval.native_crypto - -import java.security.KeyPairGenerator -import java.security.SecureRandom -import javax.crypto.KeyGenerator - -class KeyGeneration { - fun keygen(size : Int) : ByteArray { - val secureRandom = SecureRandom() - val keyGenerator = if (size in listOf(128,192,256)) { - KeyGenerator.getInstance("AES") - } else { - KeyGenerator.getInstance("BLOWFISH") - } - - keyGenerator.init(size, secureRandom) - val sk = keyGenerator.generateKey() - - return sk!!.encoded - } - - fun rsaKeypairGen(size : Int) : List { - val secureRandom = SecureRandom() - val keyGenerator = KeyPairGenerator.getInstance("RSA") - - keyGenerator.initialize(size, secureRandom) - val keypair = keyGenerator.genKeyPair() - val res : List = listOf(keypair.public.encoded, keypair.private.encoded) - - return res - } -} \ No newline at end of file diff --git a/android/src/main/kotlin/fr/pointcheval/native_crypto/NativeCryptoPlugin.kt b/android/src/main/kotlin/fr/pointcheval/native_crypto/NativeCryptoPlugin.kt deleted file mode 100644 index 78218bd..0000000 --- a/android/src/main/kotlin/fr/pointcheval/native_crypto/NativeCryptoPlugin.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2020 - * Author: Hugo Pointcheval - */ -package fr.pointcheval.native_crypto - -import androidx.annotation.NonNull -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 io.flutter.plugin.common.PluginRegistry.Registrar - - -/** NativeCryptoPlugin */ -class NativeCryptoPlugin : FlutterPlugin, MethodCallHandler { - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "native.crypto") - channel.setMethodCallHandler(NativeCryptoPlugin()); - } - - companion object { - @JvmStatic - fun registerWith(registrar: Registrar) { - val channel = MethodChannel(registrar.messenger(), "native.crypto") - channel.setMethodCallHandler(NativeCryptoPlugin()) - } - } - - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - - when (call.method) { - "digest" -> { - val message = call.argument("message") - val algorithm = call.argument("algorithm") - - try { - val d = Hash().digest(message, algorithm!!) - if (d.isNotEmpty()) { - result.success(d) - } else { - result.error("DIGESTERROR", "DIGEST IS NULL.", null) - } - } catch (e : Exception) { - result.error("DIGESTEXCEPTION", e.message, null) - } - } - "pbkdf2" -> { - val password = call.argument("password") - val salt = call.argument("salt") - val keyLength = call.argument("keyLength") - val iteration = call.argument("iteration") - val algorithm = call.argument("algorithm") - - try { - val key = KeyDerivation().pbkdf2(password!!, salt!!, keyLength!!, iteration!!, algorithm!!) - if (key.isNotEmpty()) { - result.success(key) - } else { - result.error("PBKDF2ERROR", "PBKDF2 KEY IS NULL.", null) - } - } catch (e : Exception) { - result.error("PBKDF2EXCEPTION", e.message, null) - } - } - "keygen" -> { - val size = call.argument("size") // 128, 192, 256 - try { - val key = KeyGeneration().keygen(size!!) - - if (key.isNotEmpty()) { - result.success(key) - } else { - result.error("KEYGENERROR", "GENERATED KEY IS NULL.", null) - } - } catch (e : Exception) { - result.error("KEYGENEXCEPTION", e.message, null) - } - } - "rsaKeypairGen" -> { - val size = call.argument("size") - try { - val keypair = KeyGeneration().rsaKeypairGen(size!!) - - if (keypair.isNotEmpty()) { - result.success(keypair) - } else { - result.error("KEYPAIRGENERROR", "GENERATED KEYPAIR IS EMPTY.", null) - } - } catch (e : Exception) { - result.error("KEYPAIRGENEXCEPTION", e.message, null) - } - } - "encrypt" -> { - val data = call.argument("data") - val key = call.argument("key") - val algorithm = call.argument("algorithm") - val mode = call.argument("mode") - val padding = call.argument("padding") - - try { - val payload = Cipher().encrypt(data!!, key!!, algorithm!!, mode!!, padding!!) - - if (payload.isNotEmpty()) { - result.success(payload) - } else { - result.error("ENCRYPTIONERROR", "ENCRYPTED PAYLOAD IS EMPTY.", null) - } - } catch (e: Exception) { - result.error("ENCRYPTIONEXCEPTION", e.message, null) - } - - } - "decrypt" -> { - val payload = call.argument>("payload") // Collection - - val key = call.argument("key") - val algorithm = call.argument("algorithm") - val mode = call.argument("mode") - val padding = call.argument("padding") - - var decryptedPayload : ByteArray? = null - - try { - decryptedPayload = Cipher().decrypt(payload!!, key!!, algorithm!!, mode!!, padding!!) - if (decryptedPayload != null && decryptedPayload.isNotEmpty()) { - result.success(decryptedPayload) - } else { - result.error("DECRYPTIONERROR", "DECRYPTED PAYLOAD IS NULL. MAYBE VERIFICATION MAC IS UNVALID.", null) - } - } catch (e : Exception) { - result.error("DECRYPTIONEXCEPTION", e.message, null) - } - } - else -> result.notImplemented() - } - } - - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - } -} diff --git a/android/src/main/kotlin/fr/pointcheval/native_crypto/PlatformVersionException.kt b/android/src/main/kotlin/fr/pointcheval/native_crypto/PlatformVersionException.kt deleted file mode 100644 index 4549060..0000000 --- a/android/src/main/kotlin/fr/pointcheval/native_crypto/PlatformVersionException.kt +++ /dev/null @@ -1,5 +0,0 @@ -package fr.pointcheval.native_crypto - -import java.lang.Exception - -class PlatformVersionException(message : String) : Exception(message) \ No newline at end of file diff --git a/assets/benchmark_pointycastle.png b/assets/benchmark_pointycastle.png deleted file mode 100644 index 0f357353cae33376c1ee99e346f06bd57e1a07d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 123423 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%0AEm!wrv$ZlK@zT#e)HU`0CSuGUrPE zsy9F*2Y}m}yPNstQD?qcmF*Y`jK8LD2NgJl_u4AxCA&pxXk=6JAy{7@%nKhq}o zu_{w#Cm@V*06wO@Nu1Bk-I23>s*Rs@OcH|I<#>mJdZc@i07~nQvIU*l&tX(rL1r0+ zH`7a28>%gL3PGy>*^@}5U@!pf1Rok;B`rY-RS*;n@lYMermeT3{8Ms zGK=of#m>V?pV>=U|5SFD{UG7rGCrMi_%_8)cl4(0I^;s7KpL$!2w*&u!l{tGk&1?H zP$z%H^cwh<7y{J)Aw=|=GJZ(1RO4G8mzMSI+$=y(Ko5QaAfvf0B1jiNO`NR>COWRX}fL>qbYRP)Rh$Mb=S?@n5IyBtj$$$`ByZuF^UH+(kY+NX&7nR`kG{y0vEI z9ldlS=<4PPp(*gCr;-)p+wT&+9=YO`iO`yOtT=*QE~D2gUu*B$owG(Eq+QK<`Lj#`v{|{vll4U^xW(<0sCXMAA~kmh=99ORMNHK zGI7cb#5AYM;EiGc@Y)qk_zSK!%n|f{YsOazoA>{P5e zFK>6D4(MEUK%CDWUa#t@`d~jR)LO(WD0f@TPyn_|1m)_T5lbn-CYQvAj#>tsHSFD& zh5^e_D^65E{LfOySj{8sX)>2QnBf^LXaRET*fw!bXcq!#FHxN|Hd`CqY*uSzS5;Sd z;w@)ls2-|tC8!7o442hyj6jcZo=*L3Xa)?(mFeyzV=axl>n@=DDn)m6)d03~7${>) z_7%`pze4!qa_=0rN4F){f^K~-|U zNfJz}41n(Kpsy6Q?7sRTMivT5&S|NB;I2Y?M)`c3V0HQox&-`gvod-QA=b67Tc@IQ zLX<=Q7rq2+i_?x~$xB!R=%?!+m;{X<9gpmA%bjQ9q7SCLQ%-|P~Lr48Nhky)y1;3 zcY6@kTi$I>*(AG~RK|U8U}V+drk&p)mE#}>4&c!ZnHE2-r9=X@P9ttfaKY;@QV0U8 z-TOFqN{C5!@FFKnjuHWFMc^H>Q!aBGvhPU%Y&`yvXt2Yf9WXmXZ$_BsxR$3L4g%m5 zgl$!3BFO>k0|4NCIN0qe|Goi%qypQco><$Wes#;jsdiZY;Cq_gz7|vnA^&9HZ`~Bl zZ7Fo0weeOODLqS9f3s2>G4Gc2$5?hs}JYQ<~w#O~VU&0(?sgcbV%scT_i-vt(NErng$?59YmY4q_4 z&)In64Z)61poRtD5+2FBIa$C$Z&3l~7LJT+Uq)>eLyhF`(<*wVq@2&-4_?JpVO!%C zml{;HEFPDInFC&rJXqP(P;&q&d`Bp{|HE|;@HSlEY&aLQKx@ari@fn|h%w!*4)=4- z|7oUg4$uurwE2&v?us6@h7_Qj6sgKh@iSCGn?sHxB@UpMA`cchL?!t~F=$Pj?Sd|C zxEdQsduAc_qw}}p1gwG3_7qpqZ^KUFnra9@>SSt$-R;M>m**RMx!(zGuYgF+E%|UC zFZcZk8k3B|?{g{Pc2Lm$Nw~=-p_lp$coAji9U{6OaYlc^HWNKbpmoGw8Tti${o9v5 zX#tDP=VE$%q6J^V3ao8;lka65oFyBd>wuW9>?*Ybm?t0og;wiU2j^IO@t=TmXJ8zPEB54Pr4DAWi^s{qO^NY5&H`fn+_9 zX|C>FndVB3G89s7OH$prJc((zKXc~a;pV3##U6_VYIPa+q5%He4i@!LU6ASS-%oo7 zVd2vr78{*m!j!ps^pUUzkS#=%fi5~(@od@Nk__0bqMp)a>a6oJ)%p=@g+h?=tvNuC zX)0mvt_3Dx)G_%%UN`c+fyJNA?;^~m(oWkCyvcZS8j5rG!p3DTy zrzw;0`1ko8;?P!N#YHHw1^p~RJR|9v;%OQ2fD^`EJ1=IxAH#sXJpl}kN@c#DL9G- z3-Kj|xnka<+I0lJvGkN;PTG$mI8z4+xIBqqlT`AOahh{+;~gxND)Lp!G`lc^-a43B zOpI1ql2#P_=uAYHfMH~-xO_*Y1}x(NjSM6`7l2(U#TFbeKqR@fBOOPO zZ%q(C22$wb^(5$M>!>a|@cu<=K%S05VuX$%&>%Gf9Y@gLeyEAUMxPMl2@P`DkoPyt z|F7AGi2>1{e8KPL1Rl$KYuiQO$O9LHDrLZaavdoKWOL$MisGMV;CZ0HDSKyUR>v5? zeRL;gP5yO zWuSpQrVER>9Pzjr6`@?BDFvo@MCZF7us}Il z4#sxojF4@|E=3>p;t7!T_{-O=_yZSm=zNA$KQhPw)F&`ZwzInp2JwttpguatC1%>X zC1+R;HOpg}MNd+0o1bqU_m(?>jm z3QG8TW4A_ifSnX8DISp;?d?ur%dynQwyc&dg6x||A|c)r8st1;>wlJn|2YU#0M_*K4ZpiO z6reVKxbXccWU&D-?*WGyJ6BpLK*grW{doqS#{oEHk7Z`}2^64ID`%e^Q9~t|ilDhy zyC;_fnA3U5zaS7?THqUV#YHq4JWsl}{PYortpq|5H23cI+<>AS8F$Z7A)g1*b^P=S zJ)#N}f}voGIvSwfg5lv_{hphUbSN37|1tX%>?y~?7 z(0X!t-Q!4*`vgHqj0WZLV5(HY`_xC0&(<8sG^r@4D9-^^D=FO5#Sx3eUk8phpeNo#1X8Z0EQ&`&5`Ur}SD~pq;S8ewOb!kV8BLEOlb`ya zZ+uNN)1ib%*j3;+z2tv2eH8-@BA7>=#tsh3k_(S2NBCK&qI)2`Uf? z7L&&|vyKdcfo!@-VRl9z401-IPxwfzIC)bASWv7%Gfg*`(G%ekYX3h4+Y0FqLnS~( zgAeRPEZOwKpJ#}D0?svfaIOfY4*bu%>zT{dKSlrb2T3y1N&KMWP)%q7(EOBYqkZS~ zHfb#U47HQKhCAR|g>Hb>3G;mUYH()IMs_x6NCej&047psEd)^KpunpanCA}x5)iau zeF)XnLYVoC;*eT@(Sz-r1F%=$OryMkITgAVKbv( z4l0}mcw5W(#~F9UjH0=eoP&Xp0|&(@cQ0vcIle1Xuj&06=ZW}6pT`w}k}x26!&(Pz z4l0?cY*26%2(1HzHal$u;M~|TG77kG}ofiOWoqawZ;95Bcpde+LpE!VCFyU-oOXbKK0QDXaI6vND_3lit85V4K zbzL;mV+j}n8yu&y;r{%IX@>y+QSAocU))-0HrRu165hTRLi zgJ4d6)ns}lLYsB;0YSBdBHN=m2cSlE);JvXBnsJ(fuU72Xec29jPn8nvj#vIA_51H zKaFlsb`K!jgs`BTRihpNr7RW>Z7FQn1=CJX(*s;W=%D}vfQ19hRj&blE859Gqa4g2 zbc4NY+dzY6%N^kN4RC-$*gG#k`?56LA?oVJ$ZmIj1GpBvQlJ42!H>=hWaZnE8sKdO zd!ZYL+qI$%y9I{;t<V|q>AV0`51WTxJ&O{bJRA;oQL+XIgSS&mWlInV=cS2)%`*+V0M0B(gRmCx;Jx~u7aHL1 z0E2FTg9ebL#$h}SE;%irtd{e#I{@dxS9?J0b@hp9tke0bl3J?+465`iB>%8JC4j_Tq)J+tlhFhS8cgkvmUT9KHR5L?5rK3l5_@=!|w7?yH8(cIACMW@F|af zbJ5-L%EpSLCc0|GxjK8hIE!(`@u?z>yCdlokgb{qP3^?;U|iKmbv5^jqn%;~Xi4Q9 zK$J7!el;UI;M4L`lKg;1+pz0ND3F|-fx|7kv(M+cE3nVQ=@0UgjpqTA85V#WBXVb) z=N&N5%{c(?ZeX9EDB2$R7PkPrYAPGFN{f;+0OqQLzya%4@2;)k1tz@O3tU5iT5zLN z8M_OS=F=dv!~&CpUK6m|3wkPg)L^$qM+0ajmY1dJ0el!TSMZ{j1~@P_N9P5#^4-T8 zvn6S-qzv&+%p1QT%)KayQ zYiTXFe_k6_x)aWa>SlX5m}$%+f?VUY-cQrhzH~_QNwa7#obNzX5}fAl7x&UpktU|= z;)KdI-xZdJZ_N~GCUk;oVtgj%KQpduCcG+ZPhLG7rc9$7KMo}^|L$Z^-U3O7c$ljE_WoLh$XYc!c-#1>L_viZu ze1CcI(siB3aUSP!Jjdhlcs|d#l{NTXghlSz9=ZAI-{0Zy3;*?Zr?|^5mn<^j+paM_ z+5XK0J1_?kc%J>Y%v1S9$!eOJVg0S;^KI5)KId=il8MrvWpc5&GCh*J34bY7KkpM$ z^_o;P1|G4sPJVT>Ej(feC?&MMr$R4EPm!1m*Pdt`e)mTp@3QcY!gEIA9#rczaLJNRTFs)oSdhFK;$7Ockc;=ARZ&L?B)PLn7?&v zJrqR#CEEqfDupeY!5_YNK7S}6CO$_Em;X^zV(J5-h>p!Y8=r&Y{P+dmJ+p`N`_C%; z;z;xlji%BOd7UrjhYtnf-)qGtj~@(|o{IrgRp?ixlnkkc;0|l$@}WD{R)gT=-AJ6K zBsA=yz3{Z~@MQl|ri}g`(U%~=$Rk-VJrrQ9A-cpG{;4q(IQ|m^z2^TAV93JSi|Z=U zA?=ZViF|o~sD0Uw_wX-k=lRO$P_Q^hKARj0iN!wLg^#!_VxJZv0Y;y04b4k=(*Cuw&*d=b!coN30a-gbsx)<~KR4&L!moMV6u z(B9JbEsTT}9*|aWJ+$4T;qlD9!K?+BAr~R7H$Hq359A`ZE%;1rVMC_+2kah-MA69O zi5QtrDS>BfcP#!eV{lIv;$6x5h4FNFJm&c%E%XO(=ZG7u$tL>$k5vB$rv3jAsb;}~ zpNWiJK{dka@k*-vR|WU~KBtY4Z}RHgb}~mYjhSC<4hL7hG7GlanZDT7d(LpRHK|6P z!+YQue3|y~{kg&r*huLw<&6$)Bw9q(9Ui;(p$xf*be+KAi+CUxF~h8z8pB4CC-QGP zypfPZFk5ora|wcoCB_|!@5SzYL^hHz&is%noU%~rGtq~^)jyfg5-$wL4M7~;@Bc#v zU;xIEjf))JdJ?XoaSqSd@rJT$OKtBY})z2T|^ikJhVU7f6t#`8! z++gO{>XU;D>sElIUvodE{)i!&lZ1NY!A~5;*@2I&Y5rKPf=p2`?PXil|D?RgExX-k zfP{l10Sb>e2B2(o3Yhnsh`MF-3uSr0UWDuJMw>SYT$hh^itF^lA3? za(X*rd0O@!d-JovG)*Cm;?~DOim?I|o}I0>zpYUeM*6S73<~W_ggM@eN(_f+Xs7g9 ze>!oYXuvGDu@%%Q>ggGMNb9~fD1MLRg?PAlGj@>VKSDP)36&5-hp&MwWCe6JSH8}+ z37^C7$GHtO1|di}1ysDrI3hJVYL~ccok3O8<`3OWY=Eb=xA!UD z3=ez;IcM@*;ZS)-Ic5UVBL&f|o@UiMD@kdfa+`u0FC{fa9(7A4`rp<7Nhr-8pF{fD zCAYL{?my#D3sDYXV=^QHMp0gocD=@JaFd}Nc)uZz^tavyd7r`-T#kQLCQi);GX5`zu95nDAwqKMT@ATSyi83drObeW9vz@k#MyoKGvLZv>T` zZzCE3y{6u1o7S61A@gz~#UL<#n2f*Dp@64R$0Tnt)Y9VE6q@P5p_uXWeZSubxG51A zA%Zhf`Ku~?WcDx4YRDqQ-R3Z5MR=wcO|A{3ZNt!pK``--Pr+r_JD6f<6VV3UEp%>= z#T;uf93aez#=nOorR4XQfIio=e=6bLKOJ;hiV>uHI=g8x8{dtVKK-p4g6E`2l2Z;T z-B;rcbRkfQk2hD6MfEzrN_#fkUx%KCA*e z3M!pvX|nBR;rvA*y~fU}PazE~(2zJc>~x>==8Ye~H)lNW3?jK!Q2B)xziXkMSmmwt zCAN!wK_N%)yrQBT8aA{&&Swm@lq_~LA|rTD7R@@gzLB0H`K{4#lQ}xLpw}cd#{>>p z-LZx6jlMf^>4-e&Z?YbXND1{IQy$yBWe)L*+@P}#Laqc=#i{MLBOe%XAJjdE1i$cE zC!(ZsZPB_;mM?Ux#~Yf12~jY)4{R+|RCG6nF+8@5lo~+}yq>piJD&@5XYl=D2dmG@ zaraT6l&WXGa0pVD1pM|_0YphU@TT{p|Fu%;>TjoqkiIoVkD{9K6Du}5GDBD2DvLNc z0-OKxS-P&cg}_B#{Dh0OtLp#bCI5v3o(>TkL1)mI=c%B9^>&(V0z(ufvQeiz-f*J& z6;cNMI`PD7s#4e?EwP3tuuz9@fQH^xugBXU5e?)WuxW$7RKAcdaX5okPSLV-QQF}T zn8CU(Qoq+A3}RLUC}WKD^si+^^!5JBViCK0KfaAmYujeYSAk3#>Id}votYpb_SQN3G+V|Y>>UZXOA1MC3Q&Vs`+vPWUChhIQH&OxwFTq@1H4L?k zDus=meC!PWhf)iW-zj*}IX=dUlG6aGam&8>-9^o;aX8&%dz~Vm-E@fl0Oa%i*FB#F+jAe(f(wL6>Wmc=6&b}zAf;0q8gb$T; z(*`Mv&j)0c7^fn+hwY=41G&d(SGPNTyrB}8<-UK5zWQzT0bnPx1U0}E9P@^CfBs?b zJaB_yHAux)5{OW+{R0o7=tMKCP(RSiH(l{^yzPo z(C>vWEWmSS`O+7+4O-oFo$|PkmfLT5?DrP-b~DougfWH=+KO4ZUgrH5tH=gCmU&NX z%k}*xU+jbUPXJ})0SGhK5^T9amUxG(pq8)x7+F?J8Zr??zlg|ES)FKd?^%NO715CA zWMJYkgg^Pq0h3Hh%_zPiY6~5VltCeA=FkIOu-~u$Jnh@Pp;idV1E2@f@OZp-Z zXA^$?;Zjw$i{*%OF*L@R#={f5l?!wyo@US!dgNzy2oLf?Axc&2j5u=+`zdj?*5q682a zH8cQJK9Wr+VmX0h#4 zpUuzP;2>>9knwG5-4Eip{yOxFB3-im+VM#Xok(3J3IfhPhY`D}7 zd1?t61$|23rR^k>s`h*7d))-kYWc**DJ*;0N^7x^&eLBnmZ=gEDnrlN((ZU%kqowbUBeCSf+9P6}0D%*jLIfFb{}%ZP+^>5gZ`U+foLj1u&T5xU0VSL4%x7%$2a z$tCc0AdUIIt`LcLn5`ZV3S-BVL9xyfirCAKn zHT%;1n{(GMz-;A_qUrDJpphQDJu0jM1r*i^h`f3u`~7Y%$g`8edvt>Tt19mgcvAiXfMYS^Q84Av!Y5NSg{aWvSNsJ{W6K1 z39KF_nJq?<)PCzN=rABDE;dSwEkPQlc$KdtawOtg`Ya*ZMAHEZ4kaJEkv~udq8-KA zWTcx53QpT@GyP2Vz5b;L=_9;qB#Sj^-Ya z^DRX*7)TY6e17WLBehYlSssM&N93^WwsVT7T{gY$T{#5RVcKzdR; z80iL1^jUN{qM2NM!|0p^@K3h3*@Nb3PJMUYL+bcf^Hv5sd@L5*}kC8#-hyjQ=e5thCn|K@8k#wlELE1DWhH8JZ zsC$8k?&e^x86$ntkyfCuBN{-vr`QW+4)s(m81pE5=!ugI)eYa~11KOISt@ZzMM!(| z8!HDWT^qkjq){K`g|)FdFixE-kWGb#ow*TdFd}RQZBl)I@VHMR99C54Lt~HZhc)A$ z%|<@IpwPZN03Di)p@p)UQ1F5%B+eVEgb>oV@YjeU!7JC3__=RZ-S@o`A(bi1c9dPi z!%BXcPf7<&&b%YAh2y}&mEs{D=b3Z&u809oErgR!eHG=$u`Roh>9#>67u@VQ*SItM z)c~QZ-T_bND6|74h2r1->(Kw9f5ZXvWcU@c5{QfC(ZC8V%`NI*!)FFu;b;$AuMC(R zk%x)N2TTq>LABLUq=uq`J_Q>3+=wijy9e#lie$d~`Pv#I^dus3v~%dgw?Jr+XWz^$ z@7|zbz~JVQYy%}2Ig&>+cNNU&1}WiUkDOFMpIm$_ha>=>v`Y*>_PcICE5xG3zJK^z z#e#ImeL&@ThtD2dr#!SEG+WPy>V;ENfiiS;eeW^fsM-(>FqrE1+;tbs zf{th89lbS+%<>DZPGqObYNhbR9rN9+NSr>q>mL=Ds$|EHsYk>uikGaHWXVvqzIk-N9Ert_9K}8GpWDw-Ve3b6 zMBsuV=h80x!*y=Ff$LP;;8Btiz=r~VoYFnEZ!-V-ikJk<#Pfn#O&)w&!CsWlfB%$` zPu)mEu4G3@848Fvb}F_>faD)$6hw-uRg}9N=-7~Rk)54AP}n{7q`KheXtu>N30t;U z6XwNKLT5H{i%CUel0&ycEQVTzYu8=VEq{hp2)`z-k#^_Twt(ZxN2DRMYW4#uEbO;2 z2tdyI*Svk?lVWbv6eR!^Q85=C=xMc z;O!OlPyDVc@29&mU0bdboU)tmXMrNV&@}zBUSp$$!7|nIXAUXa`JZ&*47R50@YO|d z8GWg`U=fMp8!q-dC2@H9iL>zVtvsI6JT23w;(zj?6nvo7+HB8rncJEAdeXksW4K|v zc5wT`jw=DNl3tsFFNSgq8ba$JeYaJKewi7Kwhe+o^lwj4OnQ%iFZzPrJ1<~W^mS2R z#_v1}w84IJUeNBUK6XL+#mTdyz%~XQC#3#EA=8hBM`24q>3R4uq3)d`9zDLu`$HuK zf_tq};Zqb>FFP!chs^aA`W(-jS51&5(ZX6Kl5^I{9+rW4hQ{*pSCIg+NB)=!p>3*q>iBp663ZcxsgBez zuHN>6n;xB>@w$>7e(xo#hIh8sx@$Ne~9WnpM2rK@7QC%hj24nwW)bETi82hJpHVo8E+%S@)XE`^Wg>Ew5XsQ$FF z>OU}&WEvSA3j0=<=s;k5OnxJip>}oQfgBBw$wj~>-sYxT|JsE-{H;8D_K+<>DD*SB zrXTJ!`GcV@Mzo@b_4eHM#zc%^o@Kgr{*8$~#rZh#biZ?U$GTq<($uGE8M?bD)ruppo42bKQq^e$M?p&!g*ub~(z{$10P^)-DE}8y^ zF;0I9naK8UNeAeSwM`d$-{{wu7tt+g#U97w6(f0V*9g@Gd&B)!%v<7Kv}Ne~=&M7jD0g>MR zBB#?7A~>7q(9WNsPH7Ie#O+K=mKD4#Mrk$cepP}_p+%G9$KIB2FHcilK+5E#CZ3#h z-)^wienHA4S35sYa*PgZ&_IC&EsVmBJKm{LNd2w9m`5+KwWp{lMT`8Ng}{?{i%A#2 zAt$6$yAtN2awZI&HPJ17*qH2+blbECIgLLxXny5duUgXi!==4_i%=+t_0)Zf2cfvw zSCf<6Cs(ICtk-0({BBn%K|fHZCr@gC#BdCX`)oSl2=LlLGbv< z$1dH+@y@9kC>dnbE3vbEedYE?R3x9}TDo7jt$V-s)Jb;SLudMe!o8jQ^62kN^?_Wm z6HOBsy8v)E6>NoxWWn2b=&YsZ4eNrwvhil3c|5kR6}%UAs;_iA zrWjojQfxa@A1CD#^$F}z{c^J?$0GaBr7IIJ( zqEr4b(3T#B;v1M0xPHhX-pHyPd>h(-t&!D<%pqLMgrg!*m~;K@H@YM({3AC5XpUSG z_==`pN4oFDxbF4{C`jIQ4hXLx=E=5uYa%nz+ZZia7$xG2&Nftt7P)1%{bx}%-$Y$4 ziPd(xJup!|3~k_XoG=J7`AhoFUwonE*-cn}!Q>)n3e>c6MlD$>gvPS8g{2{aE17HV z#+Q+2g<)(umn>vIjiD_w{OM+uDhjN29>}JDacoO5rqNf+HZCumsCLz?JV)- zmWIc`=IRWBXI!D-HG3l6RNN*w>0rAz)(}>6D#Wz?BLn)u9m+Bb+Oo)QE0C4(yJgKU z17N}5c2VKIkQSsg8WUL;q+auXwtZl3)?cJ@MI-I8U5i)J*{hHJBmA>op!mL^JsZRi zo&+}=%5dkjfKu0=ty#J<8iG~PwjMI|69(n|o`dHU z>aZNdV=k68_lAbA>3c~kGYFTM4SxUp+|*w*k7w3bpoq4eMhqmzXB3Jf1EEiDi`fi0 zG5V}0JCv>_*(?kWv~TFNU|m-Gi=pm~aS6)(8z9SLgcsBJs3lo1(Fr@YYZ0whv=Q~Z#YCZf^ zDlsZpf@}gSve&rS9Z22lkUaS*qe>*V@da>Pm?TmB9IBg2f@s?+ct_Bjg8YZ&&Ou1S zU6ifYX0?pU2YF08`B%eqtzJzYYw4GgnVO!|*Bf-%G6%=zqcgUi@4U6vWNVV>$K6R% zgSP(-vLj2QJsiA~;a;&D=9TR2rYRcf?@|T}En~>pb-T@E>Q9PWB&^_exg8290#Vm2 zay1><3%)qApefcTbc9sVeDPM!zu>JjJnnb8j;K~C5Z)VLVrOq+q2YYw{iq?dkxglZ z=k^>fx(>pH%hXp(8Zz`s5+2nBH5tzJ=0{Ia*f3(PN2f3UgqWxvBygs5yP-h>R{rM& zE^I6%?v!4c-#M`*7eJFnzQu4bq!qi0-_JWzdbNjsSJ3&BW0a7VuL`z&30}TNeKT#w zqsqpw+v5b$JK=^nlj@d(0+@%O@X{!w5zats@(KN1*y@`8Qv z&e8dv&V#K;+Nx5C02;$5N?wQ7j)e8e${VJROO8yXBN&b+?RoU+E9R*RB&KiaD524H z!X~^p=n9mX2KDI342l5r5%<~9@4SG5gDNXT$|=i_og+969)Xi=$^Zh;H97@lQ?>rf z02cA>&9|2Xws1F7Z^cSnDz&5c3;q4e0HU{`>5kMBCK4;#^W_O58Kza>^}!e>?7}Kdd$MT3)pV{o#(RqU4ivKX|R85MeMWpBW_lsrFmC`WR`Cz&kiXJzb4z8kQm_0XUDig>_GIW4}G z9)V~k^?Q?Z#Wqu_$pvtNw`--1SC5A{5+$1&EHwGLG%{{g`)c0x~qkg9!)E5 zM#ZDeF?Y|L=SRWRt!BHgv*;FOO@7qVgLa*Uuzg+9b)~c6;Zy}zEyihjZnXVH#C##Lg9GY8}E$01)*qsMP&U(ZJ;Q1tQ8JO1Whcdb8Y63QM zz3MA>?5WT;ABCWc%zRSXooopRZrG@A&%}u@e2?H_vK%Zmq^Et6ItEVEw%*32pH|S$ zyXpG*SEs}+F0P&II-Dv^G5oIQ%zT!`u;bpZOyiJ%x#_N3xIpmtIUGBF`U#z3k~(@g z&tjn1Wo_wh!g5bB(H*HE!#ikkb!njZJ8GZfb1gqeQoxMd9|wRGbqtn9>s(BN*x&J4zUO}Yqq67v?YYMfOYG)?AUmxKWzz{N#V33U z^|(x5U!Pqr$y6e1Nl{}Kzzlm1ITmKwOtr=l34m33r}IePb`vl9(*51-jijqTUWWMeD(f)bgMmN;uhJT+Rhygj1FuZVVwyC2m zAck367~(j~s$oN?6v`wY`l!%xzF)v7IP-W{Pp(l|eF&?MU$x@A6~rf6g%;7!{*hia zRswJ0-Lr|`iWW}424hfs{lig2hht~04;$^vIlpB8Wd076!z_JKfB!p%-@RED-un<; zmH6WBa}?k9c+_FUT;P2sc7CHPg*o7i@QMf?s~d}gyH`S2XeuExicNYkXpreWqpQ%E=4cA zrHG)!9mN-m-*=r&EP$qMpti0`lR@q=g-|(B{Ud*^n(Wz#K+2Dp{k3Ey-A=#uLcm< z^^Sr5tB0b=iwyU)LTz*&*NvKo3TX%^ZMGNxM&)p=;SZ?LT&2qH40>qv_HngxzTcOQ z0`1UCm8(@Fh2E$xj#DqPufY1G3O73+TA$~lZj`Qz^)iuHu_K(c0!|Fos{;#vwB*aX zO-__m=;SPYux$J~(=%H|Lryt$>+_i>@~@r_<6}59PU=F~)BW<@Ida%#pOq#;55a+z znUyrDrAwXE@-~|uB&#Dqr}c?q%8ADWM3+A7`vPj7p&x|dl(;!nvDitrNim2)c%#iP)q2~ zWmJcPC8SD-FH$%Nm=eo!xnUy9TCKC0Y)|^UF(1yB=^!0WqO#+pzErDtoT5a!0v_ec z`gMEnLyyu8ue{HqZsb&{sz608WuwsFhjuUhcPH`a%dW^=sUrBMr#*4l;W*Iv(lvX3 zCZgTZkXG!COZmTLKhJs=8NZBne@S4a_WT<|3$lC5oExGKhr9RO?yhNBO`Ojpuf4?C za|R<_{cZ2 z&it}E{LMyOyFK%pf`Zk;&$J)us`+oHUSqI$ci-#7efNH1RO^iud~jsScdeKY86@I6 zKPyvCF=Fg@*p^fn{kg9ydDGlsEN$;C#REX^yS!b|6m`NPLal=!3pF7$lps!VYchuK(WOVK18d=jF zPEUA{FDEatb3(ra?6074p~KU)aO0X_bNJ1ixxM05E1y3y8vEL_=-0eQdz3$lkH_&m zjV*jtr7N+Y_|PHl#+fJ!6)LY}9aTlNy;+%6-@l zPNCrR3zgLdaTI7TRfb@erYrCcy$sFV zi6}kliSIr%lr>e2oWtNJ_!!ryyP^zXw#L%(RfoP9a0bUi(_w6mCp3?RjWe!XhSTBi zm=abnX27*p**xTt$}3yZANuO=L3Czm!IvQEuTs){E3%ymo<&p}dXS6+I{BLtVPTDH zZyFOHa)qtp_r6=|PnA^%nZEXZ*4fP{z)3coB+2Kw5{!#w7)!{0gP?aUP60DAhs;7< zg;Qdmg#4$o#8gAyk&t6px(abk)Q*8p_z}p6myPlNX~b{r@^ldsAtxQA%FX5jw|oBH zW9ul4X#7Pko=P3IQ2g5YuEQ?k)NbBrIenR`P;#v%n~*B^2?h)9GFEtHSqdLE>^30r zN$&!VAKtXIxS?}}qkcX5bz$m{f@p_Bn-skx&kO`4C10#nRMV$!evBBf^qM05Lk0{v z)Ms)_gL)qNSLY|I6};qZ=ZY(}AvMFID@F+vAB_ zaIdqkhnfL(^yn?-kh#Q9@BkEzQhIn7BQZK6O=o;7ikOeKm>~`Bk&u;-*~bRHD7(Or zaX1`06H_oc#8sBF5nc-bXlo8PgT>WSxW$LDVc41v2@o2nF)I#@egZtwBZaUM$fFS zh;6OED6FRLc`5;7OfHPWrYC7Tt1EjEIoT2%PY4g4Y_KM5tMy(qY}ou~QKwUBm@ybk zWpx(Z=xKWqaNtM8=f7SGJ{WU87yE;EJY%KawM)CTIIp&3RXC_l(wk;i-#d*9a(oTf zCp=SE;b=F}R!$w-zG5#kSL4ho(tv z=SIZmILC7KDz?D8)<{t(NSKTfQ#D9H;s}~z{JC@Ntj@UjR&6!Wxres=^V&ab`VNMH zO!+5YzcHN-)YZbrINaT46bsn$$1}yp7iYJ`IlQPuZk7cOW3OV^QiAV}`WVvHg@q9F z32!n4H3Mi|S5}q(LJuC}>}b?Mv*#908IElT@|ti+d`1_@5$Tk0&rYI0+*R$*QB>`! zhlI-PN#fCO7DLt(2k$b$t8!cPD%Lea^Ys|{M*SI#&)CIkMSquC{E3UTjre8NJTDbt zOtbc=gN6^r{9_QqWZ$${&!i=VL?u%Y(q7+7lEsBDkevVYKpB3nZAJ+mBr8zDxEi#1 z2_t5ikD6`nUxbJlwoW`fXR82#6_=_dCfyh@(c{%ohYWN$FuCw>e*XjU zwsiyapcc9X4qpR&^p3vk&g#XRe)OX3kUGm*RV3KehOmYOv+JQ-2Cg;)!8@OC0&c?W zn0|tYE*L?Pj#~Ass%BO6GqK%W~x0>xXLaIC!_BOZGpG2RDC0 z0d_DDALznofK&ki4q5#Uz}nK#%rErfy1a=OfS?V~$rfjx*QEV2@;2s2ZApqp*NuB} z5>L~jZ^KN;pU@Xdc=Q43cq{a()-Z+MiuT7_49Gju6CT$1QFa)Mf1DS->uad`vuBF4 zI97%_3{}1Aqj*0K{M4(ZTU4g-GrM!7pJQLRL#%@X_x%U(-6#}mH-^#(RF%&kKvzXfYT>?LO*np@Dq@6tZ0*Ndjj=<$TDrN4ae!z z##--)ySl#gWE>j7gglj?Tc1+a^i!+f3dnb&RHX00f&Vpi583imw|roj2~yo01yS%6SwB zo1rzr#g5&Df%-w68(k{mlV9HBCgk`ZX`-B6PfVa zKdr}a2WeGhaC`vaa@@J zR9^_*0E}?=M)c7WYapu+1WKazSCv=eBGk9XJOix3P)CzHD+ACPvw`A{;F-&PpptC> zt=d%qt5M?2uD|5I@GkHPJX;PzbXr;W+}hK1x`Go|24m(rbnNE(@Aod+FC+z>#dU`@ z{SO&foR*lR4k73ITpu&=Y~-%-%B#B`gjQ+~cayc>k*X0*i6j!v{KWG77e0N|aG>tdDpq z?zhZtr&(?l+Zgxcn`#Diz1I+*2b^XM;cBI8rls0bHJb_4Y>RP+q$CXon)Oz;tY}Fh z*>rddC*w2;wUd=&Eb3Q3KSsK(_LTp6Ni&R>E$p-w>~`$LyG>74f=uydXW*H9#_JWI z>VBAGYT)SK!m2Sx>ER4GeHdYpK?T6!MN!p1RD~&CDv6HVe|Y1RP{JPlKzVz+xX$_I zu&Km(Uezi1zMxwG+DlImTJ-;!i7G_1Nj&fo3HVOiLqkc)3|$q2fjmWfZ9_S_Dx6XQ z+v(ukbm;1dwN4(g-U1oKBSM=2gVuzbtr>cP_FR))!cOZAXL+V;hnzUxTvM7I^soq~ zCHdoe)LnP*B>o@p^PBgw4jA2l`*?`ed)~(KhMK3I3iFANa+(B-oMyzB^kc1Q1!>&` zEeD05RL3;Re5k@^D2PcO-6s0j;c|vlvTPB&b+FOcq%)0$RU_?MpPPoh%Z35esc0(X zH_i1`xVq-jey>mAG8$_&eb+$G%JH2ks=Mq&8{HNv;1(>^{~b)a&TtEBWuR5zQf8eQ zi?8h50*w`Er+V{KSsbAT3nBds`|PFY-*5!ji#^`LM&USrzh? z@;_Lw9?zZh=-TBP`A|BvE4N5TG(YHJ9Og_cdl@$UEzh}d2#0*9<|oGYTbyuW*;EwY zuV;m59ycWw;>S176=!JWN{@e!VBCDdY44}wS_8kUfjPSbJw|>@bs(yv$4_V2L3WYd z52a-DhLv5g=VSr;RV)(`QlyUyZI5HH$YctA^EB4ye2l=C+4t>bk+fRkC@Sy*b(Igg zCK2FqS7iLm{;Axdl=VT`!^u{SmnIYd_PI)@^eLvN)FJorB=zH#loOz|c<>3)Pku-e ze?n9Q0Csd>$)*^WeTy;evt*-iU^9?j-xju&nuL$I4C*g#ev$`emjRgeCdUV^Wq*7$ zrJJ-^w>H~J8)&x6m^&}o;^TrKjqswRg8~vkJu0+r+y4j^2ogZC7$e?QTQ3+IX%Nf4 z1Pj5L)y=aB4lTaIA1k@fdZ9Y;BxpyKVnp<`3oXp=*lN22=k#hpR!eOvH_m#XDg1g> z^5_>6gf9b`K^)P8WjzqhF#s81KR#gFV2j3$9xAx^* zvcBoZSl8@d!ZZR;bgg+;Ml@1Y@lqvO9(<%%&zZbdIDs2e$uYy~nwk0oRL-gK4|mu$ zQ9RC)dP+a{t=~@8qF~8nIl0DC9+72D0m3xWK@_!zAG{>2N$lb{+k`Jf$lgMe$y zt!5+;!a7;t0pDI!I(KB|C|X!D*RYXP*bb2j0FfT8oo~Vfp^-H&$d4|e2i!|QP@sgi zo1L^c{!G|m=~3y%vtx~cf)CO)kEi7&xLglp=pd5cP-RlHSvbn!dGi0|{qKIId-Ow4q7A}-%BqN{6VAd=Qp)-G z2g-UDN?=hSD~j$>GLxF9?l7k4AA=S*LEr-#;Qa(F(4Hh%=W3bbYS7VR$HV8uSb6(uYmR}4I=tf8T#6f z+8;eGdL&!(HmMaSy}++$jjk(xDMTUcgS!Z9sWX^xUFO>C*QL@+RFL z&ue&=W$6T~73az#$+F$WIAK4%b^Dsp!U5~HxMJpjuZq%xScn;fotREU&NIKhrhO|z zS6~C&cyw{~t|Mqx#?lK1IZF@`^b%CI)h7&fe}{;2-swhJishQ!1^0iM5-FwBq@X*)bpL6)Ej!%*2C!366HdkE-xd7sp+;E zyYjYK-58)}Su(>EwJr?R9lWYe3%eN}oVfoI1bc6WgB|uwnIAXY?-Rnw@zn!TiO#p3 z@W=Wzh0M6zoeO1J-Gp-KT6hz|cZ)Ko_0Zj&tw?iMR>%=TK*ACJ2@2X2q~DyoUx0`r z8Xz^oxp9Pp5D|se&+n-_r#gbDczD04cY)^3WHE9hZ~oA87zLCU-T#ZT2?YK za@Or0l0Aexs0b&1vR8HghJ}I+AK!r^ksx;2T{rqv;f+_M-;^a)Y4IXiHQ7P^P!A;_ z9Gd}L_I2-tOAw_K7>zNCuTF}8u5m?&AtwCKrZ&lBxTqk;+_g4-?04aB} zs=*&jj{((N^7Zub=92$GcAzbM)sVslbXPGkmRD@b?I@F1C1X^R;rPzq1Y57%7N%Y zz}VD5(lam+)dd=j2B1!9G8OvTH|!7rM$7X*pe>~wr~pS{HQmkOzNhJO^-eYS+#Dn;VV@@UX1=#~+XHmXyLUg> z&Gm-v#5_;T+%y7%{%suOTs&iIHzt$%cURA-S?r;`S-L_X1Bxs*ZcBBw_o+4Q!l3<0YI4%$)v37q2vrm~FLPkf{}Y4c$?@qn1&# z!M?UF4Fho0-Btuma4_gWc9QyCra!WD6%?5DJebyh`U_9Zs5$dd{Twz>(E)Bm5u4Eu zZM~A3c3)+tY(dv`Gp~2Z@2C}8 z44~D(+>5G-+^amIwhJPQcUD`P?-Z~ZHj_U-w<|`YJKB4e??hN_c?7Z)x?ZuYU)2W{ zzwtD5w9ryphfY{sut3?c`k77P#<79Jxi3DWC2k@(l$t!c*mg9Hj<@lxvxUQaC%mt zTwx&L=352q&bTbhd}C{G9bhH%%=0l;+MOX$Puhk$W`CAKde8gytJ`ZQOyKjs`D{VfQ_ivn0h5A5B(FK#HMeQ!_jWLG zdtA0q5$VUW0E%~(q3nlVFqIg7%$IiI?cY^N^PqR-8$%p43(`;zMuV3@@g!M*?jfnk zh2M+C6;x~r8=(1B+Ea%esu;)GZsz}he@W2ExGOkd05a$#-I-O>jFQQ& z2kYl{sOQ+%95SWN^8XoV3z<8!D~w-^!s_r*02wU!Un)N=Jil)&aRfhno(B%a*He5<@l$R9ytGOAl9>>@8+&2``V)6&1nkNbpGe1AO)Y-f_e|=N<`2y)Ad|DZ7#gJ){k7LQ$ z=5)zpQLJsR>wMhKbf~FoiTs!5>P-<0rSo1<&E!_S9_f}s+Jf5pIp@7%`79V==vB#N z1$|-qJyEim3i$iZ`V9MLmrI?QWb>pmF|X#l9JK8CN4K*lu(n!hqsYia7BrhC%IWtK z*SNBGT`m1H@;L!s%QPQa?pC{ar+kQvc{QR4L`j|S<}gDzI?a}mYusU_WCd2bi+q3)oI!Y0-sZAD1(oR-r#m z%07;a=fUMHc5dGQdCWJmGTXo0j3Lf)$h54Vycf>QrytV%XoHg?<6-|6NX!NCjGR^Z(h4w zcl;C%em9OndS%}spuL;;4yH0laN;Rbs?4J}Aji*c;3y{((_H8`NIDk6L5n1BHFHdFXY@2(Xmv=S!{GPB=&B14)KC~Ix$jFqP|wAR5s}YLFD?DW zUNT=l%ke-HU?*FNqPPtpxL~m!`FI&->)Yx#R&an#eV~7DB=8i&yjEuI#F}jXJ3Oja zG|QH+Pag+VH1HWK%zO~_03^e))t~D zt~^-Q*wuvm(+E}bSumjC0GsaZ;1*3i+{=g|iXz|m0Dyl8s@ivhPb&Dxf6Xu!fNnxx z=&}F5cueG>2GHS@?21+otnV>cJVb8qcvFNl3}%cgpG$>M7!TKIG!GK#mGc!G3dvcT zlKnHntwk3ZF%FtH!XUb|S!K3=D+h~n8^?-&UvvqhvXJvz4+i16zTPhF3bOTL>%r)b zX68~-H$+M$C|Cqu*%(Q9zw;w!^XrYAr@I6u>)i|%Kp}0kox#ZTFj%Nb^piuk6sa5- z=HmL9E($vsChM*B0j|{T9Oa)8PbS7L9ZpOtlT0(N7v`o$px@cy(O+_y2NM-a-di;U zfq}qMr?rb7wS1DEO^HJ#iQMK2nI_dGxaKu+YwG=b512Rpnfyn8dQZ73MJrRutSMaB zoG22uvQH2uEfip|s7DE0k%XOK=xs54p^6EfEW2@&$Nm#T+<=2VxgO}CujO!!`()UJ z&s&V*NW$YY(jzaWgEcOwzzj&8UYN@9!3=ETC#nx_s0&#eADQgGmtTu9zif$xI_)Z5 z|KJA6a2h$y-q+{hOSMy{GA=@WvK8Y8vLf6AH}LW753itsCCN!iT}p+j{!AUma}hJ{ zzp2&XL928`_bZq{ex=4*yLez&{6CcZ4@E;}JE`=wY-?SeKg!LJQ*6XV$Z$HHi=erp z*82)UTx%y!WL%IyhE8BCI(=|xvU550tW}+N?qf6cc}7M>)rIa0P%?|7{w>y_l7M@c zv>~J1pUvsnY^+O8D!DrEkY`%MjRe89H+ z@adPVg?p5gzA1U&7V-IU<_x3461Un;!~?R0QJiCr$wm?5A&>OOtA2?|bpQ!DAZwJxV|gss43ZpAP{D+uJ~ET*7*+p6SIY%1}UNzz=xXJ9RPeO$AzwFw)(~h-a4}4)x6-d*tQ1Q5UJqs60-RXDk zx&*v4kPJ{dr`Ow76HRPT)lvfTX(Hm4lWG%Av;7 zWWiBN>Jm3&Enx`rTtcSUyW?1RqiO1CD$X0gf-P?`7^ zSVEt!8DANdyvHh7=o}O!3dD3E)HBvIh3P^;&e&TBPU_%O(J2&XXwesEx-!EcKofL@ z)Qc*UcHnVal5Z(g52_|9MEFc!7DF+?8nUR2oZEkJkK*E-dR90PR6|kS?1i&H+xyO+ z+A%>ZrRWE*oGE_7TQOM=U0^mL?Fa=#r-$I-fYT>kY)(IL&`1>kf{W@zrFB6$B7$QW z%ywtzSdGb9rzv570Gnh~kEaIM2@7aZ6&cQZ9n?`0i~X#IGw3oE@^<3bY=HDvhg|Qg z6^)ENoFt=3B-a~XYM~Ou=cn3|nShOKVtXn?hlM*zgngq50CB)zCuC1!kgwW|_X`7W zft02ssDUKyf$>#Ih!PAx&JRC0u)jII#hSnBQxyu7wy_F5F^DSN429Ut~=txrC&K82&sr=-&Zc!1@sxd)AWl`9K^5y-UH>cD$y-PEUP0$wdD8? z0W{yuccDmb37%Iyq)k_#){4_>QmAP22p8L5(xeWs)>(x_V#*q26SJT!RCw(;VCYw-D2o}>u@vgIqop=ppU-|rKE0knSNZX1G0FZ#5 zf#ZNldeEI?;B(g#N*8rs64B2W`bpqQAh!I<&EA7bDX~Y(Ef6POce*&w>?@>tAxyS} zN?+i~6?2={J>rX9Y{IIXlpaUhq{B7Z)KF>S^B=@LMxh-$&-x6_DAHuG6>DvzHeqQn zRv&`!SFY;848){hjz9&8e=s!3S@dS2HF6rWB*f4-C0Mka`ZZl#7240kymCLoZ*8YE zMJA(G#=D`91C95EViZUY4Y@FOHrQ^q7lBc~3jI&KN`d`qT7!fgd1bIqu0E8DH~^J6 zhO}n`V)5?ZiqUOAFM^?p4n4E|LQES%hNKr)%=hDk_|@+!9R<@mh}Tyfkf3vY`odzB zy7Ql&s^wBBIC%W%Qx9;wW;H$Z_YS1`fBVM^iAss^mYI=;QQ7+_WQ0m(kBqF4V-q?wl(JK{WF#vi z9J?Zvy~i<%>`_@6$N62)?)(0{)9=2&f4+xvUg!CGKCkC>J+8;&alL=J7n)utX;{Qu zRtAxb5bOjpGB2k-0x9~(;}b#@?dTd7xMD-UJ>qdjQMlA(-!HM6dv5iZ?Q6H-th}MD z>rlA3j=>q*3f_sVNNUTxM5`6uZ%D5lEa{}JTz`pKh4ZGu{Qsztt&P2=cs;>gpQZy* zM<5V3yS=sTvH<2rnx@Wco!ohu9oZNh8K~gjdAQ!>q(uqjMca19?QaQ_MZM`&A1*yd z+tX}q3FM&86Jzo<%dTy0cwEe3P{2Er z14FuY2s{sNLYwe66q~*{KD~)Al>z6+GalbfwbM@m0uuqc5NXTxi-r7p`8AnV&`-8@57)`4qF*cv9m|v+?-K6{1=lv@Cc4!x8U89 z=-K)kam4Dp*SqQ+%wBEayG~Kx6{zB;{YRGuhB|%2$1Ut?i_g$()CD zP4>-@7)ym6xYoV!m0e*)&_Z^e$9%|#dt6GJ2U$2Az3_Y_(ga3%<^zQ7;hxdKkECYD z?Fc1#Kr!MomSv&3ixRBJn8JI1CDLn|xA8^AZ7|0D z3A!bT|Hr9Rd|BP%r;jBW=8olYSf(6-0-eg~WnLmaz5XDTL1rNUO@Ms=Iyb$zivMG8pDkj76)1u zKk=@dt3TR%Pvug5F|I5bv5%^d#S5kL%g)R%!@OYpCk^*cB~5;m7=EXXjcL5{|aJ?LDhw9zghFV z*uEnp>^!pXWE79AwnL z{EHmy&+=l zyJHy|6ar(h8o`R3mSootU|Zs)`4%Ty+}Upy*$;@dg%!UO?8?+}AMd4c^IJK`iH8Sj zU@-Ul>w&D9er<~9F6o;!Jbl)CBu6&>EE0ya4NVYCey<%gd_!P&w}DFJeH$38M9X+$ z)%Jh;hr%C`8Xi52|2v8(j*LDr_Pn0Kdv!7Bw!?#xwF1BiRC=6a4<4iK<}3y<{0yA> zL&9)T07*OmvLzb2M}vQTO0u!(m{;zSk%MZJKbLuo2an0MvxHX*E)J)X(QO59>&kRz zhWRP%c;j(#!mR)Z!piwpEe@aP4Kn^aV|zGvmW?a}SphL{?EZ_Bsraz^`&(mApH!s4 zK|Du%0r2!yd$26bx+u(pcPU?p?8@_VSE0A12aSY70{SB49ZpkiKy+bwQt?UluRYFl zex#|(n0Y;u*LALU9GK<}(b2TxlD@M+7=09@<{+yohw#_zWBuy46JwWbn;_Y7I4~Ng z*>?YRTPotJ?=ToM=QW4V1U7H>b;LTPK+LyZkvZu3z$Ag)MJ|cVI zhY}&i^7w6SVz)Luq1W`}LcEmdVOf0o|Aq0WL{)p{@@fg>k7W)E*cb?E-tT@A(RjC{ z;f#e9#d}^z0(2a~8Ie64l7Hz{F^hWIsiBIpn%W=q3AbFuCRFv|tk`g`{8f7d?#C6l z1rB_(Qcs(IlVW~Pg4shO&ZFKY`K2>H=+z+W_$32)18hp>&@zz7`@L!D{a<4&BR z-31hNfUJ=MUOgt@VDXG=7S80Sv4#k37yjKxXpte-DYek7W0VH?u!c4`g{^RBL%;;NN5VC*WZxFNwl%cvcJ%#{q@yM9-&}1S}G9hJCxK)a{QH=>F=3c8wekO51Sa~ z`Y>IqI)DXH*ObNpRZ1#+5&_ivDqx`!=j{nUIK{3f$Hvp7@CoJ-6xrG}9~J(3DF=_e znG;f%YF_zN8NtOp?VWOUs)VXsO%PB7e+D?!-jD?~JQGR;ZLx9;IB-D87hWP!d52=) z(kv4@B#+-EoEiVxfrSje=3dFh`?Gyzm*qb5(tf)jnfkN*tnm5fC$4$&o*3bVT<2m3A=9wKY_ zeQJ8r4oD)04d-ZR{!}0xJaUz3fTs?BmH^y@t3Yc?f)iBAD4_Yr=?PZ z9dzud+uz9{2Di&k|K|u~F@<%Xe1bvv1rrPf9eu`qQfXe&s-{`7GLF$ZfoE z?+fubejd5z04nk<{G22uE(u@;Rkp5wb6f8ybIbD^!(Xo{ee6FVb}}@cR-B!dg-S6R!AHcizF_##(4i$S z0DbHO(|i{*estO#hG}`i6FuTfIxM=hD(J~=cS&x;;gXF;Y97)jzxxV~Kjsdik}v}_ zn9cJ2UFvaN_f!DCG=Je;XRZNY6KF77KD|VCcoR5ZM~(4iFFH;{9=r5@@IvmKRHg0<>}E8TlM#pU!9X`$C?)<;>h+v07qy8TNl12nbO@zLNf!II zbqkfQpP%TUE}Sf+M!sE@D$`7A#0;bNiNBtnxT>}k^ma=h8pkh?#N!^9{hv7orjQD0 znv6sLnXL4u#)@*+gOAzd>PD&csnY#Z+vPbeQGQ!OXk*rQ!saPdb5H$ zUGmnddUIy*$F58G`Jc$O3)q^|U^eOnbS%E%TPGhKI?jbKJaJ2itnGz1+6CEF6Em_!^n={W^*}=7!TIj` zFesj(L(GWT5}5}rjmC1W3XiMc^#85`Bas0H>s7t*P1Hix<9m{`=_zikD3b^1h?*X{ zOxFN%)^T&gpluO~`hhD`cTNvq%GY;7PGTv!($BKl7s2m1#{6<=a>qYNNHl)Ss0WD7 z0xaSVNNEx?4W?3`A!Tb^0S?;X1&_h*uo_hXooiVbfSGz*2|fpL-0@c?&S2b9uxLl!6f4d|d|4Hhr1ko7q|*7rGr4ysMLlH_ zTq^i0qAvC->Pvr7=B=kGmYDdn0LMK(aq)jdA8YR@@KRNCaC194tU^i}EZ%h|(5<(=OEvLwMwM@ppUeDn8PA_QG9qS0 z5D8~D`63}zn_$O|)rXC}b)D-M9nlh|XE)5eCg2GKV0y;bIq6cindCkoKIu36z|LCjLa8V3&|bF2r$1gZ4zcmBBt$Uz{_gc zQfIE&cDJUe(oc9wuM^^H7~_BDA+yYY4g%3d7E!bd)Hh8wuG7B( zGtIze+{++MmKTfNJVldo>l|8dK5K{%@(hF4L#fQpx*xP~6Y{A+CrDKoFmEF$Knk-@ zf<_5Er6jQ_!X|lkUlwAPdgPXRtl=gb$(1tuL?BtX&L90dX|E=;IXGtP@fw#dr+X9X zknu4ova40;OX3XYz^W{6!DleuHT3Dihn_*~unwC@cd?_%#KsLD3GbwD(9LkEB&}pu zA-U6WI+yS5puMw%)UWw1{(w`*aBtT_t!s^w+B2g%0layUPE=*9nkG@|vk3j`H9w62 zbT;lIJVe1gUe2d~Em`*ZdkSZlQwsCYXbWesSl@U{%nWb_}L2Z1^dz86ZXza7V>jpE$%Khm$CeswK51~caQ+2BJo>sZ&oqNW+v@U`0FB6{Tnzws<~(p6H3A9iX?N3Kdb`mfCYfp z909JBr^71fa{F&DoeeCT8#ss^8O^z2MH2`fy{wLrxfvEfi8VV{ok0JTS7oR7p`2xq zrs}Tvor)sP2d>ub$41<&-TLYn>^Ys0o+V&V#Is(PAhMM#HfM6+wEd)L;myAGexNrN zBoaI1?cnB!k`)i@t7DJ0Yf6-t*XWmYnGChmx_7EkZYwuQs^wx>x3zD;V966h^BpFpr;GLR!pR{SQx!u`% zP?x`*Ran;B+_7UvzpA3bm9VrQm4WNN3N%OSLH%^T>BoAa3YLk+)+e9?z-IlGu{kDD z^A;^T27cYvxXp2;7KF6Q<%=aOD;D!VR@A5ERaZ-vJb3Fh4!AaN zV?Z!pf?Y5V7z6XdhVt-2@t~{sYyY6V(awkm&SD4~ke4odDb9|$N z?YAxRjO!-^L^Y_M;)yED9SHpa|Gf?? z;L;;U$gOjy1`t63S($-sH@+0|9c6Z}$s^7(31VBo*_HmF2ciranxMQtGprqCPJ9=k zleY%WVDU~cQ-u@`j);=8r>Ctfh>eqrYe<+4EetZGlpF&A&`zJ}2?8<(@0_ywfi4E$ zd6`=ycML(M1Nu&SXPN9LAV)B9fSFWx&Oy2t3gefglN^>N+pq)n#k)E^D~$0lv9tF-l|%8^$LsXpT8h0I9{S*Z?5;?J-Cu8$ z9v?No#){d2i#sxMBs6v{pmHo+-2xaIJ87051W_suhU;69S9Ou3oxi796ItF9Ko^nWO%2=WB+Vxv)14-Jmh$r!Pmay6%dmf?;f)UHYen# zCwSE&Rc0cSW+1?GLnSFP2j0!vd{nd7z)Ssth{PNTZy;~31@7{|^j;7X&Xro5fvG93 zWI1W=L#;cqH2puldc5YFj+h5n>mPjSA~T>aeatG^j$hE;e;FK7CABc*_{i2>vpdq7 zEyPH(7>E1uy;YY)?C1J2H3#VVG<89(TriuI_SR`s`6Mqd?*}<3Y8=*KfZWM5H#2vG znY2rXFH|RKK0ND4y)k9Lb}h%yW?jo}6oT)Wxw&Eev@RaO>BC|-t*7Mo`~{AEBonpm zJe9aOn`HIz$p@L&iL&ZZ{5Cl+6Xi`{TFfc$jfq0Y$_0~J6apxT&aFd-<$Z5g{{Wan z4;bkOtRuW80L{Aw9aFOA8F(AhH+F8g(|}JDfd&M2fvez zn2#@}_-++;uvtX6BphdHoq)uSqcxq_^JZrq&@`BD$yCI{JVCCJa>!59`<9?&z4lVQ zjH0m1yFgo97g2EDLOL$e@{(>R1-57hj&Lf`?-kBa+G_6jaME(Di#`dcI`&fso^;l3 z75H@+`*}at%hNvld<9Z1;U@0379=fNhXOfiO1gqZdLqjvclG9kkXo)yQ8#^k(=}hg zZ6;0BJhW<@J}EOYv#@gZrn}{pG|f?q_UtwzO;^dDY%u`!>J>d!0km z-GKbfR^p=T`)kw5KkgpR7Tjx{K7lUf_Lk;DfMs#Y?!=Du4-0%a4yKk2-pbjMq6MsY z7<2IU0;o6&duTSqoi&H_HHIoJ8tB@_rni*n-waeSh+JI}ch!4q`?Mfeb$E{B_PDM- zi`P8x)<;-Np~(LRk_NXT?zRvjA3GM}j1=)HRDB(_BQP$LZ^C!bY5J?3+w^XCaR+13 z^YhT~AA>sQQS9l{wA zKwt(xOjX)uC4t^m%BI=oWl#tjqMlaKZ9K5y)K@f?=xPpd-8cTq8|ey%yv?53e5NF8 z;0*2i8qjbnB(MCzO`5UkN8z9*4!aF=U;uZjlGrIiBW9a)X$E=jgFQ!Z>iO25J))s@ z6AfViFG>>=J;$Se0_Q^&u;s?(rPxhq^&ExxTGi|68s{a=7mUV9%-+T{ZvozN(oK`y z%KO*%FYS;fI{WIgte@*@o*8AG2K8%4)x08*&jI7%@fXrBoIIZCoW>q9RCc(;5`GxM z$k6Iik9}-;jsL=MFg?%#fRSU}$w3-hily`I6;(J-K^MY|pT-g&RK zaMH?JdO1-tEfII}>T8RYjsXuC2KC#n_&fBI1P@!g1qs^TnE2}I9zQeXDtvOCd8i5v zjpXB2etb()`nu;BPOInB`xk>3#1EIC0j8;NSkr=OD8g0mU>xoy-tf|PeW^!+&(dEJ z7t6f$<^B5SS9ko7jkaL+Ue~_i-fX}h_4?(8)LvyUprRS9+>N8Q*6N|V6E#2ATg9;N z`y0P9>wcsfWbLT^aONZ$G zS?_;i{{a$^3^;e0B|z2h*;$=P5?3VIlQ203CZJQF8E~wk`59%_!QY8-IQG&OCK>!_ zyHjj18(o+t*U5@sNqkWsPf; zTIbO>)~&Z2Za3&sIfjEy>mwplVe|7j=FFd-<|F&b3?^_N+1?s_Y<7yISJ0lWyg-= z_EB-)(FmFq-p^I;75oWO?_5?9{pC2Xh7v7{mk>grlX*2Dh#%kOzh_lBDXe9pV3svw zyjTm+k~g!CjhZbh;yT(3&GlX6>l45FoA0}C^{v@(oeHmaIoQOEFFl;*-QwnJC~Qi) zDx6#Lb&q!9(EYUZTN`;y=Y^ZN+FAgPyfExVBdy{HiNgnyc*M$AYa7vrK~guyKGvF> z(*7A&LU)k8fG;x0@2OK2Zc6seDP$t%&sN)3TWPEcXII+x`xib+n=fF}QeTvbUye0S zjA?Eo6=0_;MT4DS%v@?;uC(CX@ZgGHm+r@(O@8_lT2j}q`YzmEZb&=y*_ixTQ_o9hnDH-l;k(^-FPVBxv{>61*Ix}8zNF+#6ew|Nni&zD6 zzBE#yn21)`z>wxj!nKGJlt?gz;1vGMwJ~H=cj@c92J)Hs3TBm>o2(R93(UN7506v; z4T|6UsD0^)b$l6gLKId#yY;N}3anZ*i{)3Vp&sDZ$VgHcUi$c~v=R{7?z-WeH`5vt z*W+ck?ut6cw&?L4buGKKLOoWhbzg@9n}%s5!@~dt@!=(})p@q0_)CCH7mRP+SNHqC zD8w6y$r02Y_YEl0=C$zJ3kC!tb42HHG#on3SYuY+K($!N`>IyQHNTNwzZkQA<<}e= zE!K^q$iBP7Z~Dn-U5EjISEBZF^3ke;JPz=+|J$wcg%Z151oK|5PnkG?0YuyFYcBHQ zWw~xKyJlcoVcwF^*?{Bl4}9`Dls1EMnk*h4qtZtf`P>Fe|{dO zu$^=})w4CkW44>FcI!!0K{l!Fc5aNha!@RHPv07e1WrRbvm$%V{LBwEWykPov6DXZ z<+G^;6KaE)J_EwO@VH}twbbe>Puq?YUZfbiIssdRzF6rj{<7Ym02Wk|8!L$(f$v)nJgBDEi^}3fX|! z?mO{vqEV(T-3(1rnUWp_muDZUH6NCbIiTCKejIu|i)#Z3kSTQc$&N2OH93)Vk$%*nUsQee3S)VAxpFwL~jOB(oA1q#O=pXFK@P#~x& zQGFeM%-3SXMJtVaz66SBg z$8d&h2ymRwikYI6T^9FWNwlm-In_$yjX+8z*IgPkHKj@|ZC3?}L0ccSbOt+zbxW13 z#-|^Hnlk`+Emt?KBD!-;OrYPzwUQWG(iGpLviwrPe52+1h2ixqk$NiclXixrx*u`{j)G76{Btrw>;6m%Ck zZ$QD&lTA<1ehJ(^$qc$#g5Z8VWgG!g!*12uBpT7$JFyAxBu}%D=xsT59!P=q+Ui=q z-y5-P1Bto9jxi|c!l74vJp02>$U9V^K&>&p#osjQkh%A-%|?g=$OK(?J5{f1&RcNFA>jrbbl^f=2|HHsg_qx;Y2^y zcKQMZohEwZr)|uyq|C1bjaPK_#jM9%{B_VIgy9nP_4#~ z&COq!xVZl4iaeB#PCwUe)gFWGU0{W+6()H2;>Sofd+M)k~XxG`u(B8qD+_%X5}Z_Isj+80*Obw2PRWp;qAU!x+(p zXHb2}f7s}_LXIY{!JHamWNqmgnXmw=yT>t{;uj<||Ll`MnR}-?=R=fB^i%}L>Z2uK zh)LUdI#(Mx2SJxfh^&vU-qjI4wdxsU`e;yFoP6CdH0z-yp$TA!5WogGF7pIE)P)zD zV8FfFOI!a$>NL}Kh8NxCk!R$50VMGkQvrXeHF5k@Swqfe0=*~i)}L%DZ!aRtIFw)AkO2>uhRX9V5(1Lw4=0?zGFYexFfKiUapen}C`Tmsw zkRYkSdGmMBI!AGq0p7&xf9AF^jd5cP&&#`D=GIz@hg_IbcEx3)h<{1LMj>99;;RXI zw?<>L$SIBt|E1NsnE)H^p7V(bPTEL#+N9^!+rtP?MRo78oTtS@-lsu<<&L|ZP98cM z7`t>Jo<)(fd6(JYdH~_Ffyuyqla363y~D{Yl#Bu!a5tHG8@>aX+IemMJ|D#~N9khw zN<%K*+32_Tep;}HvQ>WK6WTOYt)SyF7hF<%xEY-ov4eFV32~I{8z1uj57B@XZPv z1cpc|qX&T+wE^&tFV$#}9*oK0vHTR-aJrRXZAL{~ukm=I=GeBku+tA0P$$4#(Ht&5 z=jR==#?jy*ov1SwbAo;9>cD7Ych~I_ZC?g_R`_{i>#t$urSF;1K*2zIbm0I7D+G=j zS(0@N-@eqWxC3WRRl^@DZCbs<6RP5P@8>W`(h!E3x0P_LEB+s=*GM5k^FGNk zs{DG(`zKd2E_ZU96s@X!;ruzNJRP%y$c7XrX$vs(g*F{2Kn-Xv`ET1gyEo=3Y-FeD zVRs7K3U1#}cLkCZ2;Sp*Kl`HUcR3Bd>&#=4U>W|%5-Qyz`|%{~n$UQ%(|gC*C2nDm zk>c^=$B|MBk%Zlb#Cw!EHFlEWZSpT}>jF^KJgw#q@@e+BXgl2LnW`xFCICNH|0L=Z zk?Rgd0)5={2!NHp$6^ULgKG>DC^bmZH*1f^y`qM2a9DtCJE8aB!CxK`TI>9Qn->V3 z0Ud^Ta*}=SePtBPzABS?5ww^{OcyasTpWM6YSrZTBikI)@{9LSqGf86Mh6@fejyhike_bD}b|)73z;(8LL10%HoQv5%i4Oj)-@A!t?0 zZ&^0fYMaHet|or|-1?CnpR;F*VN<0S&gD(oKs`=)v3Ml7nP5B*IR?mZw8RL4&lEY$ zVYf4H+=6fXWAWb;i_@z3_4g;RMz3wQd@vJ%DykR>{-sOEb1Me4j`|771K`&0KE86Kx~4}=kc zZ_%&yGKVUnaNj?ts_}K;IH?FjY7Wh8eDk8R0C}YNIrjbAN#}iBr#v%xWvf0{GUMGl zm+C*zz$WC7=Qz)loB!!@QHI&nn>#AETQ zC--yHR>I5vJ}0qVa}^j>O&AuyZ^h2;9b5UG5*)05(!H1pZ8|EHED~U#jS94Pf%wT+ zarV}Sy*Il+>J}|xbJ>*Fn@aH_ym5-tQ`=?DfuYqa=b}WmL^jCqC~ctwhC4(U1pZ|Ethr!B2)?d;J}FXBij%5 zL0s9y`dEQLp2_zt?7^@F!Osj~-bwTbQ9p@3@P=h*To5J;?Lf7dneA`)1$ATZjRpbd z_lc~G{asr&gT=w;*SmP-=jFL>9=U^YgBaK(=$XUv3@*{ppLwT_{kh#Rn9H}LMDiEM z=A~GrAIrFHx($6ZKCTm5xF=70LJb0T0zB`M#jBPu_-p4e^NF-SZ$i$U6d!X|8@Sa$ z%!0iao9Q){vH1K+><$z5eFt%@M`(Xw=7I5Q3Cp@a0lW;sK2#O1cS~TNoPp_A{4b(F zh|w&&O5k69*Fa^#16z%oEqHsN%42H1*L**#&|>Vc`g9cSkNyaAo!Q_m3AJ&iXrB!5 z5VsU_SbB>mgX@YLeIF7*ww>n??J>@`cxZhK*szxEZ?2!y=4e(1`5*F_{Sx;{l8S;C zzU&9bU`-zwDhPt0LMr_*t!43>t@ROnarc$7=_?E`X^*qXvnrn$#7f z$ITwIh9QJ&qn$IBF8k|Xi{o^u6lFqLf)t!-D3a0Y=;tp5ZBw36@V&Va!fRx;5qD_M zITr7+*M6xSSGtO1C)O<%aU<)^udcSqaD9mNNF9t$(XOw5Xb#)Q2mKh}_|~CXQo_9x z%(9?Ur@Au8rlZWM2m$bjf7>?DoHj72I%|%PyZ}F|JIx|c-d=cfT%oHz~!>>*$RBX{164fbifIn=;4D#U1OJ3jcrRZC4Z zN3`BMn~VmU5*~WP-GL20w!GnJuYKc6Kp zb(ZrjyuD7Y+3g8j82zuC1zPI@Z&Iv{!;1C2QWaGFOTs!*flaUChI>Vc<$luh-xoqk zS*kq6;~hA|bMGj({S4YmI8ydXL+I0}IHM~TjrWWt3r;Bz0?9g{xZ$aQZ%y&5A($S6 zSADK6Szz0rhT^NK6L}8^6 zQ9<+kd@W{M7!TfcZMJCansi{<>hpS9D3!WMT!AcgAwe(Tf&9^*gzm0AWzG$+SghG_ z+jR7B9n(+O)V*3(RksYea*MyKMVv{wtTL3|gnkP@B!?d_$J*7R1ugMY+_!CN!B^dp zr=oJ6H}0YG^Rdmhrv;DbY5+d2ZG}Vj4(__6z2xR4ZKw`5GV$7W$i@yy%mWs~r7lOX zP#taOv---|avmgfiDK@cmhD5~t85JKU+NFN&jBq;a5z7>+o|dLQBeB=k=B2 zo-if!L#Z|l9|n!eQ;oL!lc(K=lG?vLW&-hVD}s zV4A8R%!v1cV)al6-x4M@*cFJh`9Oqh+dweIl~)%PTpssZ0U~sk-O7uPRo3eYV9&;q zw|~9qNCDNwWf;$}6!XBSYV{SbY~^Bue>|5?{5L1;*YPHXQjK2n5k(Ym9>5!DI&vUp zV4o(O$Xl9)f8@rTbYw_-C<9)BnmVNQo~kiGDfi7uCvA4%1Kr2ZZf?_a#Nyfv`i_XPm$XE26gUB{|~2%-v!i7b;Uxj~S@yrnmiC<^5EUSH!n|EHDDn~%98K1;Wm-;o_cIQi`WGu z^pJF$)xCHIEa*y+Qnog93BVMgRcCY){MTnb^aBN0NI16bKuEB|yi6x%9@t!|VDmbD+npN* z0U%WXU)OtJYP;{tXnJ=+G(mdvRyS3iw!`SO@+{e zdi~Gcq@01ahDWR}?Q|C8GY)%hzzIqE)na@oxcrPHrEz;v-R?&-d}`jpkhTIbGQl({>F6zc`*T7;3eC%w+!fZ z&RKwbfav>BK`slEvB!(X1lfU37>TZmqxS3}y2_mr*|`4=gMnH&;T9QT&XMS$W9O*H zY(u-`X*WQ`k4$6RXZoqZ(5=6dwFP8?vqKdFW1j}!To1UCt>4AkQ@9(VSHr1n9HkMF zX@5ucJm78@I7(mO>_{`y6EyKOSv^|0)}eajISy_2+M1!S=`Q|3C!RO!I0M0S4QbjC zNkx+HB#s~-qTAlo{Ge=BD~yyd8ipgSs0a!v7zO~f1u#-_$OTSg#_1lSo5LxAf%{(x zp~^m-%5E%bj3Hqu6txmNN>BAdXEo+}>s1z8%vJJC2M`Quje!8;mYep(Ko@g1bk$sA z)7E0b!@rIdcZnR(cVXSVpOxzQJ8zI{eTME)(^|~pHSMz&S6r0mz>GQWXiW1L;9xxi zx|Nm7Bj*d+s@Bb>?$`2~Pj&aTZcVYjry1KS>vmLq zeVQ@;6re%oVg z$BC?(VuZyDk~97h;SDfAjH$N1T>S*!bsHdbuJ(c&E3Pkl-d8_Yid%;~4Q>Rt{WFgZ z;{~#8m?zbY@iYeTp)Z8AJ5?K|rP0$TEoHLUx;Oa6nGR+DoTT0#-X(v(mSyb2+> z7IOQv9JyWD4K>~-t=Yb{b}-P$9bt@rVgR?hyLgjM{Eye3PJ@?0ub>A;&1EOixN9_& z;RXp9RxV@>QtB%Z&LVRw*FQC|Z9-&M1C%cnP`PILrn1b9cZ?dRodElcwsS|e9a;&q zaqJG9t0l6;l`xnnF>Kv{=T#cIIrTJrjruF)#szCMO;b@FHcp|%l=uGzfrJ=ed-@rCC3b2#d4kVTF1WH(w z@ARDlN>5SUtf>Qp&C0aAVdZ!JyS|}mN&^mMO}S#kCNx!P?(MyyMB{YePc~D3p}>-) zY*j-Xee!QTt7mvBd!{h&{LgP(fw5ckBZ!_#D~;-lf^ju!PB^1G+3C*x+fY(DjJrjK zM}QUPkw1Xe7X-^@^Dk(?04d<^KdiLPxRMbLv!VL>_Akqq-ic0Ay9K^n3{8VGz6q`C zz}wS;XgV$y1~equSBGflsZq~K*jYzgEJa6T*ImlULGSh}2V>5>7XZ4Z_`Ed!RbNy9 z*MaWE`oXt)osDnDeuJ93;rV`iKlINz0#%7)IS}s-i<_JM*$d~%#J~{%5(FiMqUe)5 zY4lWSAe}$&a`21*U^-7hPABlZgRFnj-)6mqCSm$r;m;*oulsOFcg4-jBQOdS)L&CB zOZaYJda(MZhvEZa)* z4QC(hd%iR8jXSeGL^r;tskYb+iFY6heNNwL|2ew}X~dI6t9L4Y=CafWIplFPeM7m4Jb13J;Jiqn3`{7xvkr}+PvBFO0@m(3mt$sD z;L_gzs)Mb`D$~5Xc5o9#nr}cA3x`xB+E(Lf65a<`fp+MU+9A=Mlp#K1!PDfx%2(pt0{0o59A0dM+ zwNNs;pIOv4^5FV^N9a=47{mqs}at%~(PiN`s!i8FGM+edO0A`p~86 zO~gz6r~bk;(^2t-4nSts7D?`aK)K1;&c;umj8S5ub5=hkiG^t|yzWDu>DHX$u4qV) zb&jD%MCi4)k)KFPc1wlneA#4j5ZUzQg?+#f)uV07hBUqc8vI>@PeCA}0;#?QG$1tN zDP8IwGn)r*I9eR4AG8y)b=z>4i*~F_6UrS6n~3$`kq?SlTg*e=i8b-4I`Zb(cT5_A z>FIJk|8`&NZP0pic#Q09Gfg)b0*W0T)Te`RW4K0TrfJ^_YQ9YbO&MWO+gKEnwPr)K zsgNAWY7uZM?=i5<5h$0Oy8!Xqs9ovjhmMH_{xT ztugk1!&k*w`Aw0Q5LgmcA*{z@ac7wKj8}>lTdr98?@GT0505M1huJ4o1R>gdrm5@S zosBI*XAze3GcauIMv%S$7^+r{a0`M>m+vYFkB4$v}cnLi1A) z`@aDaVsf(VoliF1*l&MV@SMj_T+je|#ux-@kqu^hYfVFqf{`c?Q>lD;f6o#~&nV$8 z*8dcFoSVt;7OqdLA`i@LVEEa?jP4@*qo+{}ZsU|Eb>yUV7Cfe|gk9M~8Tem21YL`3 zyiVjtdy6n@^JH23BC5I3?shk(M%bWp~1 z>PgIja=|%PGB@TE#;ONQ8&4(4y=0^LYI~|jz^K$kl2>AuU7ZM^8<9!2L#fY2%xN0q zFui!Tuf-%OiFIq^;<5BrMpUA6#-g{rNDl6jsBW!H*!>bAK>xLr2>cmQPGX)ipW z8sD7+@eVmnHEKoxKT8^a+x5nqDBy?u)`HBbP+~<*zP~s}fo0R(LajfZKzPR#o)`BC zi;>NO zj-k(T*A00nkdj@W8M#Gg?6o|%*atFWFvBm@p)XKJ)=)q*)_ipL@jBAIK!>SQL=r6I zEq;8hZQg>)Nf42mC8PT8H;)m)h2=s4omE%38_tB|aH+5sG%B{P)HAASjJNb2 zt3Q}=>E(TNyV-<;I|(qF&Zg~+!4p$g`(K_vfJPEFegW~Jw4|Gc(qR_hri1}IG=}@P z5gy3PZ^%bJSxswtD##WSt9^NQZqrV&-!GI7wWH3;9HnGFiSFI-ajAb31FFEg5SY!S^R zPP7O?#T5?Y8_KTk%*|lJcIdqWIY~sEYq#FcIsd+Ba2+bHBF1m7d#HE7E%d^a9QK-u z3Oo(TGUXUJa_4b6?iG2FZKpwxka#oQ=?+NusBqH~LXE}Q= zzClPEPYP_)ySH!dlhQ|Z60dsIAFCO6g4Tgk#~XzIPYWia1@}jlf-h5NG%nNAR{I%% z1J@wNyt`u?|6@AsDwPE7`(|*3I}TbA*>C&eFJ&eQZ(325J#qP%O=&!MuL)G#U2&Dd zHyT610|&t*KELkr`Y`F9fj*eV=RiW79nQsC!}i|3HVHI`ohKBXA1~c~qU-3v92*(_ zjghpscI5+IP7b*|;DG_q8U!rQaRMYS1KM%-s=w{hyFpD4bcirj$6s? zH7p)vOmVf%{`hK;zw$vmiUNJ`t`34t0Wp-fxcNszf7R>`hpQnnL#^CFjE&EL>mDul zJk0J`FS@_!E~A|%Cq~I80Pngj9Cg$FbFXVM)B_jHn*jD*^hrdx)X^Q5Zm7s4V%PiT z1;UP+?WQRqgh#?XoRYKOQ~o79)Cv%Dq^IabFqwO^Q?I(bdkaIz>QpmAyMHWAGcM$( z)D_G1IcjOLyCj@C*cQaG7xWl>aSAR75v!{=QFmqF}9Ryo?`Q_vxCcLSq79lP}v7LN`W!dqd-Dpt~3QPs_tgA5~ys9w*`V{U=L+7igh6 za9p%8BzI@(kkT>u37d3x=PGywb`JPmvz%UDA1HPdubZzT`8v-6tL^kMu+@c^5n~~v z=LsNq(dKS){LU9z+!xx6zrTY&dURuY`4@z)rqgWc-oe4c0xSO1Xj&P! zQuuFBV+k*7$*rXr*!TIF!5_$w+Edk;oNof!Gv0can*IV2%w=y*hpifEzmh2RtI>bfc9{vj*zzFgskwg7ZIcr(dItu zOQ-9QUQ=7%uA5)mQRx2lY1fyKk&O2O{?(WV{sPa}AuZ|c9LN-m^{Uu{ z5muIg#?NRV1-&DzQu$)DaNVrDjz}Feuq~!R9mA|mLRi&0v>{`?21}6cF0UbiFV-Bw zY(mBY(DE{hbv(qDy@Xa0kHna-Jn9(lC2g(BYs-TlrS>pb*lFjout~X`s;eZf7&zYq z6N$7fa5oTLMiW*tdS(_t(!pA`*euUeBjM$R5$?Nt&cSp7(S9K&C(0c*URQhKX;N~1=5UjtDGk^odLV`9q$FY=Z)!! z#%S|tu(z~^#Q}NZnL0*(UDMz`Nm>o!xdXBm({oR2G%AcqaKVbzIh4(bInTmuFY#q0 zwa%ylYbE%)IKQp6@m@esn)09{8SCY(z@}XVgMqb&(Y(rBmX&0&@m|<&*MpyM$Paab zVc1~jOz$!~!4E*D({<~s3y#w!;C~8l{f@{oB5^L4wt+t0`6g)5$9v)EI!ptWVK=?p zhwtguk(N)DbOSgi)*LiBuoAfkS6PYuKZFGI3*j9AU(;EoigD0+$*xmi{q0QmOlE~y zg?IflWa^S>B%u-|5{FYu8_AuJOwz(Hub27~y;eh0NkXM{OQcK1lcX|-iY@s$lO>Yp z>T_SATS2}dw`P{T9~jKuPDnEjnOS88`w%YpM)q%v#G!B9Gpiz`m&X6c*LQ$p-M{Teii|R{Wo2el zWbYMHNj7D#&@I`rvuE}!60-NqmQAS;GBZj-Rs-+#73%%H|NrkedU`qz?)(1UpK*=z zIdKf{Q;8UHu9Qt*`Z;>9!EPVllMPP3Y?Zb2NDHCtsBElGNM{!IO z;7UM8t_5tWYHu;<0S&%=2ue0tyCvP6e6Z5F>TLGb(3w6rjKfuT`!gmk+3n8&3>t&s zfEK+=uVCadiTzSWxESvS(m|8c0ALpEde!DniZ5HOKdImD{cNgZe!o?-EclapxB=sP zs^)}Mt!&}^CQN8Se()21K;GDpxs|FZu2wa_V1`$jT?q}^yRvC&^}&TD7j|q*)$fd* z`r@a~8VemI>(w>$3*R3oeTH{;m?qiz3Hw#ZR%yJRs@ZTAHulR9N7GB@_j{_ag?m-* zCw`@B?uk3Vo<1BWTyizs5t}0UNW}ae{`F%?%+(!lL$vs%xLxo|+m&x@@$jCzt)lW1 zmD_RaABn#FqTQl$e>h9xJ~0a?WzW^nz2Ot};MN_R*IQ;b^)GF|bNzhTCz|zbYwYQ_ zE^8o_|{T1S9ShK)cZQ#Q1V1x-P2DA}MFmL50e$VVl^y)=S6xXuAn9%q9om(e< zO%M_7of?Kxp$+P|m+n?AYFfnx`Sq=|!jH-IpI(ou7Adykl6qyIiqNLBEW3}moufMJ z9qffyn^P~?o$r-1#$X4BKnj0LJoF~^9TbWJel4O1Tq)7!Yn~Nz1b*P~zt_oJTOl=(OMd*n#-h?Hd!=pZ zytx~u>hY25O{u>;b?&!FU48GW!_QtpK6{q{bvzOw6Dz9x{HI)X42ZF&)}@(4ZfVIY zfg%&b=i@Xs{YQ!C_sc;Hfe;EXI7f`h86#n6d!+u~5Cy!d2wLwW;pRR+;hw%gohK0~ zXDaoK4vj0!^v`< zH@8|NDEf?mwdz#_^NLb|IG{xI`ZDto-E{6TQQ4suz|qnJW|q~+g~`5XGvA`upqi<3 zDO|*b^M_qa=IlizowIaq`n70wxmrvxk)C(fE&TVq z!E8%!5c52;L7f*MWzkAw-y%k0qN3r;|7&bO^I5X$c4p~Ynl~yYJKd@rRGDdP;vXj? zx$A$eSa-5cmXcEGRH@C5PH<>dk471s4pOqbll59Ba8{QDDR%GPFL2{!NqXJ$ z0nqQoOB2kGR0D8f(u~!Mh%*9i_xg}6MIp7Q(YbBo?~;+JmqgaCxrfiv)7ab}SVUk`1r%Ii#a++p`T_Xn{&TWc|9o>o^+_*T zG4ci;-o>+T_*mj~TIY#YKS+*Gsnos6F}z*lx89fWM{n-GZFPhX#{H(^y}pX`c|0s9 z04N!0Vp9(W9s`;{9FkZ>9de>LZ^E-Y33~mHgq>F_p~9-xo6o_~Ij0RTt-aoCHvPIQ zZeWvF(CM(h+fj%0I~E*RnNrVJK)Htt^5-uG&qn5k?IpigPm^xYU6M{Aq*oIfRzS;`p(}T-+I&MDZgmk=nV+tN!yoxf^Z;H{5&w zQ?D#K5yGut8XLkyIDBYNY~_-{3u8nxW!MF7qQdpfahV!+f=>J8Ff3_!UNZCKs5v8p$qcw)w`Na!fr1qzV)b0uuI*Gz-RSsT@*erAXva5&N{a5PRi;835 zyKtKF$3#&tT|WaiKaDP8ptxo7Yd^;|FTF<`7TbYbI^4^7i;=r{bt9;|e~1AQ zRLBR;2%<;{fHy@+r%>}3ypy24dZHjTl^(~j&BJa`$m!XwQ>sf`J-}E-u7?~f#T$_; zHVlNwGConD`F9x$8=pUL@zdwIS}39Ix4q+X+Zb6#dW6`O`MgatgA+FGbE_`A&*97{sT+U8Myh_B-)5<`nh;r-WaK%&SEDrUAoL`nu))I@B8yGyCD@?# z#>CVb^{L_l7w@JNiJ($C*BO6=`_P&XSE9=OA}+fy?8JYx9MNu~Z*i@6Vv6frueV!u zqc?C^zf$ep(;j-^VhTdq5=3}xr=17JOzq(FeeD03`GboQ$X>L5Mrk?uOZ3EU*hjtD zji3;#uQ5y}SkrQ<38}D3Udad|Y9?yC%ufWzA>r6PvVXoX1}Rzjd*=RvqD%$q$oaml zF3K-%C9~ljA8&dHM>oId?FgpSQg_*JH=>Q`9y*5Z0gtf54ZQ55H=zy0*rYDcoIp=g zp5S~_l-a@dAidm;E*!|=oj$?r31x!379?FH_~h@#*zlBihy(&(x>X@6?X;Z8sceI$ z2LAQd@j{DH=f)UPZ1Hx^6Ux ztSwYiv!-$teOqyzRsOS=yWn7cg`d#D!t&C6iS_R;ae4`SeS1okt1=UqiD0Zz_x#p< z@*aih@S6HCek-UW;JNB-3IXekEe?{#!9af|eIue5p8d-cfAC<=SklB{lg# zBXHkNkhX^QU@e^3-xjY91i-Ch6=f=dbBE#KgnRC2yNa90q^M-*3u=8f8_j_f)8oSVLfoJaW0 z0Hp5BGX2J38o6V2X6*t0cwo)D&u>&26{zx$R$JQ&y*eK*u&y9>gLTDkzo{U?7`a+Y zUMOKLWMwaj_x-KkNYK7Vx80kKmh5I1#w_TFkfW~7D-n1vJ?Zl2wznepLpi=$;VJU? zkD1#p|NU50P267U7nVj zNa}!nsa#1v6iihBK8~9b&2+f-Ed!c9EY1bJsQI>6G{EKXcj1Bb8 ze9j^}K+Y22EA(Onv?80V~4a!hPXy3t8)S)z&X}77n;h}0IhgF(*Ku+X2D;? zwjufnDd&2AE;_6u_M*blj z-#vj|HL}KCg?Io@i5_so?a9eF?WM3PP98!bN2LX_*@9V>ShR zfrUp((#Vzd@`ac!X(c>@G62D&6)fM7BF}+bI(!{3g%c={QDg^D9-Hj<0T0YWB4rtL zc%?vbnFTZ}&#HWlsM(kD0xh+-@A^D0{(KvDDPIaCA_nVBO=A$;6_!T4)RaeDY|Bw; zx!9-bAvHmDZLIuq{+IpSi{~M}?moGaRtHIa*u+W01;ApiZpx6IIeM77M%Y(G>a!_r zWsk!l>+Ab`onB`INJj9q9cah90#lLDO{Xg?-(5laVS(y&Z*A%`yB^*yyiy>&5Nf-E ztfvzNY;ArHhMKQ0>Q90Q@P4tOmiOMY%px`(v-v^rf4;YCc75k{}cp|$h6Ot1|TocCwDXCNRsqTXt1 zAixPN*$E(jmhxHzQKzs2gfXp5&FL~o|+L`vx^(GCCEdtpg6TZ8tLHJ#`T3xZ_ z=Yto|>apYl%sH2R^1+6-O`u$w&BRX^7_=)`?d(tsbU6gr9#_uhbx&g4A=#d7ychZF zi}P$>BDL9^z0^2{&mX=NZ8I#-dE6_Gfyjj~PNT^CP8Pe^Bt5hTQ-9;a!NtmP5OhsjOLZflg)L#lg0L+?zSmOY#}2 zTT-vWY2twN-Cb<-DmnL{I3u}br`urj;URBK^XCSc59;{NFN-y(Pc zz_Y}o?2Da{wCS5_!0HjLREyLJ(S}TQUFJE5%x-?_Y%+M^u*-=m5i|%JarNvQZKcg- zNmy^loTd?5diyzK43OHuq!KzfJ#l+ek7zRCW-J#dC-L_xeosgHh=?xjA+3v`$r1}y zti%T+^{Z3;V$Syi88<(e@=iieg>G-sxb*bT1N;8}ukAnON)L`qvF{UnTBr>KJSGsW zi2`ZSOy_&OcVFb-k3B8D2a_c}Eoc46wZOYr@1+BWixC8i9yq4BRYxule$@pbRHvEH z<$|6h0o9XdsM!afs|u9fANPTa91B-lSaJ!Qc!{-WFkrXShwP~sN6Q0T{AB8 zTN#&2?MkPv+bQlJc?_f&a_meXT|2@sUdr1K8TTz%#5r_Wyh=n(PFLF}8K}1czaodL zwx@$r8goW97}nmN9GwR&nL*kg_Z}C=3GB1nd_F2Bxp*m}zu&5<27Z`@GRC{t3tu@$ zOcV=$h>du3=%;_B3Ky~LZ{>(cf<~HFq>%<8=@{`E;Hvv+VzkXHds8M|S%59iT z)u*hb;-3BtOwiM6D;$zCjdwxUUegwXp5OF_iwWo^_BC7D=w%l^Ssy`~N`)LOv*YYO zh_AL1twB51BL^E6=4Zn{i1f9MI)lYEZ(Qu}gg~i}!ILtG?(V+RMIkNS$v?)bzubr5 zF6+4Tr9;aZfq|Z5Rvt!i>52`U>45Q(XjL)RpZz6EGKG5v2w@I5q=~hZYgXCrmO;KC zq=TtD4m6Z{(wF1vpg1O`jZ;st}3Zfl_Cc5=4o66V)X zP#OuC`|a@k#Ft{Ik#qwwwrwWR&CX%%mJ+q@Y_!iS+2t2skf!`rO204L6X!?&oNIp6}yf;YrncnY2ASJT(a z2Cw2|pD|?~6WjVa%8bZ`!2#>`I*vIDGzZdi@^XwD)8-!@Km!>Hc=~4dwH02SPAi&S zeHI(CHxd=(r^;#mKHYfscpDvFQ2W7?rNfh9xS8V$x`5TpFjMM3@Wn_g957*Zy5hLF zAP%zkPj|LL)E9wdDK{()T!lD9K=G;M&|8G!+~;ixgqSPtEG;|c>j_gQ_V#YW1vLiz zJuByA-6brlz!GSNavaGy3n(Z`)+8Lko|>H@b!mACvI?&ye~0P0$=uE*pbiUx6|Ug7jfpE!wpcqyrTbb$6;}-y7ZyLwb9oLeAzgo zp)hjb0y3DuLJZ_OoIy6N$Kx_(BJ(y_X=l@FM&~+u)wKD@z2Ds*|Fq*k7&HuDza?v? z)fiCNq!85@*8?-{mCQMSXwU#l<_?x)WGU31IzTV)0)$1E+!y`(R_ec2j2gozI=cB5 zh=$O7a1700d=k>BJ-7yUIuj>Dk3PEP%#U9vDAiIF%;F^{P(<)_O*^abVjr`y@J^!_USyR%wsn@}!k?Y*@P;G7K6%e_4SAxMW?knF z+?7NhiTTI6>YfzgA=(gXQEjLMn{N7y#2;{HvU2)9zpLrytnGR0S3?jgriSV7z>h*| z>E}MEYv>UdnI~n1EkK1fYrY!q)^bH=Tz&co7p4s7i~A_aQbXrc(=}pse}F7}R)}dL zqcYPn5tDy5JTmDhZw-+MBxIZgvMKMcM7AOKezOa(DKJ9p!_}JoxpsGI;KIU$%KfEeue28XR410?&ppZXE^2HPYPsIxfZH8ki`0 zjnVr+*i|4Be4_Wwi5Vn7hBKjsoKIPatE!x6TPi#7C8EEyS3z6u!Ck?`Hs>4zeq)mG zmXio-vJs8-W_CnQx3v)nv7(Sdp((&6$Z8w9f+70;byvNC^?4`l2~3 z_rXY|Szy$Xq*Q;2g&3$iQ!yGLUOyBySN!%vOl$3tLdFe9dvCJzu#equy8P$FN?^d% zl{*&686^*XaZN;N8ROvS-~)x*zwGYldx@L5H#>XsF2y8RFQA(t#CVGhkX2KJNV>;^ zn-Uyi1jNosS+5t=gjE$4BcUYUiHz+ict%N){7!dY-R)dAxa*X*o-!NAOS1A$R$a<= z1=?Id4rgLO{3c!UIHFFaW1?TLUr5v;3c}Sn z_5$}SO8U;egn1@oxoY}b=b+=tQ0e8cVP`{7K;76|HmE>&x?-Ot{_~kKhzR53+l%}> zh$Ek~H+p;YEZXRzF6$8jADvpLK(!?emy*9{$*kguR-YHUuvlm$#uBH~|;*|Vnqs}55_5bX1 z$t0;l^nbw%XMwAJ{U%>Wpfqiy9iq)=h#t?S?)9pNv;q|FS(lEb_3!&2@IPH(X;4>) z^vd!f>be>>hD@-*CgpUyLYaR#*06IRCN|{f>!`u+NC^vYHd@alCX$$gbuMQu>_`lzafOLDoEKOv$o)M}rV``%_zq`LO22kEJ z7y1LX(JF)0xzl`48->(&`sTazj^cEzOs84A1H#>lfu{&_{l>q1IbW{JpAFEu`-DIVLp$!ht>_ywrKldt~JB~#}I zf6>e2#QC=&8(-Z^y_RE9bb^_3D_B=|${4>AIV#s>pWFZ0E_!zO#OCz-aY!6M^y>$Y z%b`g<<|W}pB&MXVZ`2t;!_FF^GaKknU0o3*P(IuM?XQRfTHp&a5Jhe(qL-TYh zuYv<$1_5Z)?hXin#{jeKnQMXTB+{~p3?P9~AITojjK*q~;t-2SO;CVdZoTUk35*oy zg6-Qm#Uu9j+xKAHSkkI0+%fMgQjc}|TJ_au1DG9&%=AS3ytVwV4gfgXKjEX5%SVU| zAG3acxRiqFbhB-!eL~OWTfM@JzmE{j?Rd1{H%LlK-si<1`Ob!U#aUQ68qZD3`W;2L_EC z#Qb#L<+V4{CD33T_c>s{*$*+$E}cakpC+rqBBVt-_=Ba0UP)tKMU2MqzU|#bMbFH@ zx^L%Pw`=?hYDky+x+SW6uIpF*7<74<8X-b!6NI7QiTpuNKIW%OLp1>CLU5keMnA8p z+?6DLjsk;*|`VlzRlFcj%gyG}R&OMC~^xiX}uLlsE=Qi%Z&(MDvh;=Rn zv&eGcIj*0sxYvBy!Q&xMD1{N5@`l?(^`^D9Y9(V|HJ8{O$>&idXRne9uWk&7vT%(x zaH5&;p=Dgqxy}-6%_trn7)xToa6ixAGu)xjsb!#}1F$2kJ8q-jWKmJXcD^_1 zxwgWR{_5LHgOJu=hX}7UI~7Lp@`(MIB~m9T)D3K+3<#96p-sgelG$J9{nc)goZMEy z6BQUO`Y}CIsDUy5UY27a$Mr2!INiE;U18b-iI$=iiuIzZ1??{HJ>p8*NQ`z9`or&n zC5R3Oc|idXSup@V3>5pcR>Jkxkzf&6d<;WJ67*$H4!_I#?-(pey2MJ@OT)t zs0&#eu)fowmDV)omFlk|YI&B#cbd^%gs{c#r7e~DQ z91B^r+U~AaoUJ4u3jd{n6^VwhY4>rQt$IQdhAh&8`P(B=2VcPZ@iD)#W0x;imlwGWY_ds|2_W{Zpj~hEHX*f)@Wm#u(RifJE3jZx({j`>cz=OuyCF<4g|Hn4 z$2_$P190-i3O2|yokL*cfQD-O`CB`>df8bQHhKUZ8Gu%y7Q~KGiMb8G6uP|zTR+EA zh5yesV54_CsE$=oTI-YE>+S9KGY|f(8Oh;y;<=5*LV@el?h}LSbkwy*>zY+a8AegpanWg7j^;Ss{D({X_)J62JC>%h8tupRKP(T1T#z&YVP@U(<-{ofr^5>F306dp_;byic6ko_Iz-6XGKmO^BK0ipI z31AolPtSObgO9J{HYn}Gl)mFFvAiZ%#$LLQRwqeoP46Vq=iF4lIu%$=Y2K-u(aAjU z{?*2jO@@D9oc0>JCELDeSUS9%Y5p@?vx<@L4URy?(s1g*@saWZ7b2eaRQIQeORK-1 z-yOJ)pLu!apDAu*m|edm;t1qJi`o9Pi3PUQ(&=+nOco8G1u*P72NFRip{t)i(GAdC z|E+?NkMBw$DDbCccR-Yhn;F=3tMDOMFL|w77r>=EAU;8?B4jstEQo|7SG(hx1!&UV z_$1L85W)6zeMXm_l*M&Y=#N+X^Y?UGtOy2-;4y~)T1+iZl=#sg)0b|MGtxv0;evIokCLwsLz~|7yfS*p1 zC>&(8TY5>|`2JCjR{NlN@^0Q4v)P-6K2HD)=}dcnwSb?l8NC?0lAAVxAtO8u4zp@% z2v7x}eh>y5(TSx*D~^RE&##Amj6&lHt>IVbuMEkBf}J#{3}3Xzmwe5YixY6!EW zYwj%$Uw-|&r0UZ~9!nEhwa1-)MaBIn=)i<8Vt{04 zAmnY?BY+-$wj+ma-Y`0V|rrzzTzoWkhY@4J_Z~5F}@yL$7M+9hcxJ^ z8=#$zxBZ;BhX|0^MbFzlbk+i&QVhRgJ`YZ{6g%a%e%PFw^T_ZNJX=E`o|ojp)+~}fO^ftQ)%DkHhz=1-Aa%u=x?%@I*d7w!KvG^!vYBv z8Mq?%=KIbWZNe!bgZ|E-2aNnlc=S&{);M&V4?%}Prb1-%U7FPnm6!{}^z=_{%46ay zkcqgjUxNK4gXV%ruAABTv-G@pg7y0;pLPU8y#^Ej0a+W6g;e4`@k(dQ+)K~HI+N5tSS0$xT)7;j?j>o)_?cmG#+%Zx=P`<{+5~qQVOroB zmBfoYYlI|{bza}+`)wbmG`Isw7-T%-dUAjm9;?C9X3EY-B-v2m8* zc=o$0O`0;}Acaz6{l)gYpYFr7WAn_1K@hE1Ba_{7n12w?4)bhmFUD`(I4PUk4Ogm! z4wgB92D#jitjaPx04iS?DSiH0Zo2j3OLt0m$FPd{XbD1_% z>v=2QCP-ylW}3B!ko&+K@;$W8}aM8%u_5+|Wmw(!iZS%`eAtircxa-=iB7;(_a`~xD#J~#S z6&!_dU|_==$FcDI=6|YoxN|bEe6@$*|`nQHlx> z-K|Le0^BpyB8{Nd;R6Tc#b;2b$pC!;EF7E`*qb6CWI)z%^PQ52wuP@{0ILN{kZ3q54c5#4VAA2ao}ju^n@O-*3P zYk`8iCCTw+MLL1WFkhUbAez?8^Zd0tk_>$l8~r8P#KpG<_Et;y_6Fwj4z_910M3tl zkAdNMmSS`})^l35t#1=F$4}y}I1xWrO_Kve_a5v7*{^^`VdpjD8N*8&q0nC90}0d% zr6JvN0-aP5XWF=An5_Cal8MkmvYFxc=BRD>TS3ZMZ~jxI_#H@Ikal`V?jL(AIhnE% zsp-%zZbhy>;ytvc*0FKMz3qeVXHI7r+|0we0$>{t?t?bK|Q`)86K&Wq?*I8MMmMi6uRle;zQ1 z3=&4tm1OaUAD?I&Zg;sbyp;wF?%ZeeVhK6{d{sZIe^#xLp8KmO-cF^3ht}KJ_FJ)#X9f~e6@R7*z2cj{9u;2sRB zsZ^jYARuzjNO%B-`4l;R(@z$5JdKo0-y74k2vp4cs1fzkH6WUI`+Ifp-7sdGOR;h8 z7X8rd&W}G4Z;-Tl&*vL)3-LiL=;?@ctz+Y~d>A1;WElP(k|@f5UfqxJVb8RMi`o6f z?A2^b$rd-<7x?}1-BSm?g7)^QYaMvQ8B@)_CBbKiXWL)>$Fhr||0=M|uI+Kuul9R` z{(@5T&O7Nio`b`F>=c7CoEW0HYWpD@cm#*z{-%eo^^(ULt93jBiWaZnzt9@65KoOe zqr%IA`)D2UkjXG|=DE?#s?z}Lz{p+!h+(aLYc8l|Dc>Kb$q5b#X$AIDvFn!2(uZ;! zn2MurBxHmsG%Xx&A4h7oMpd*axOqh07nh zoFI7M=L~lkYA*vd!E*p%dyZ*Ma(23&BhW`i9v3KvY?CS2-ilD)q~d#7#4dj#z5Jq&5G*2Q?ZIq(}VH93E!9iR{C> z=ua;XV$;T23>ezy1I??FB9^_gIT38BbpVoan)W4IJP6%{rfNfW?J_Kg4jo69VUAe4 zQwfu5lO<#uDF0FpCr&EJpDQrZJC6bn;e~Tv3 zt;f8meW#^i>U4J9t?UGx__Yw?!23<{frmjQ?Hw>_Nk#o>wT_4{JcSi}bhyItBkWFq zD`^Lfl5?L$h*3$Gto#M-=Ps{<2V|8w3)v6{uqu8BXyp5HN7$W$$klPd5klt2Tn5yL zrhSnT9WNcK#Dj3Nud1FGA?v@dgZx8|97sU6angt{?Nd@DrLoQ zoU?C@-i2iw%lhT*oqBO&Z;pbpPhNK9n%i_D0l`-H^L&NK?S|@UA*Npk_XDyME;s{C zt`BsbMIeTxJabKu;V>a2L(`DTvwlrP=Loa+%zrkJr;8I+~g7#tubE3~u%iuEM1&*Br=l@SLr_hMsXIx_*QVQR6mC+@l5C(d&%)= z?v_uj-%U?k#@exvWov#Ta$)kr_kbdc_!@&2bG_g0V@d<9Gh2h$w{CV)@91uR=T$v) z0TJAAD^=|6cK-Cy0(<^EL-?Q{B~h zq8LUMB93|_nGY$1iny!%0;6#qxQ-btPxGC=C*}ad5xD&#u=jD zrq0jjyf*91aNk$UY!6qKPD8}%mu=~t5ms6yRJwsP4#lV7aakjcZx-<$5-n%sFYVA@ zbTwT5z881GTx-~y^!po}AyLNn2Ul34nF*Z6oiuUBZ!+)=i10~{!f;%4LZb>Gd1*Fy z)L$p*zqP8Je=9$c{%mx_DAdxSkSbXo7heg&+BZxz$XAc0UbKk1&KURD9lBXrz=K@3 z8Li*|y`j7s6|g=?Hg=)5YpmXt6z*6n__v$oPFPAJdcPjy`;94#Cq4KniFe-7qKXo& zY=DdJ%dYDv4>Wv5f5&( zP2)f^H$13H{CuC@Auw_u1L~`O0J}(W?nE(2&DDN;84T(f<3@B_P%MyzlM=eSrc!7B zDwOhhS9o+}cMqp-ReH_Gs(UT?2{BD7SrA!mgP_XIub}>J?L^8Zjhist7HkCV@NzG1 zlcEYIhPd6H`zJiQn5m4r&TiB+>f+=hgDNE_I!si--jKo5p*^704SNWWx54$W@9M#M z_mZO(5ut)0}t@xnm6owkK=BOv!OCGJt=2!Jn@bYkl%7}XeS2jrzIHvBlg)NSol!R2JyWS zJ1ua#{L|TZOc~qcI?nMJVgT9FWIfuJ9=A}h_C3}RtzmKPV*redcKPpq5P35Tt>LW@ zZWk`W_$5!LQMot>fO?_=PC1UygsDA65cMdC!7^)8nzhYIir#2Gu=jBM*x z{k7GteaFB~O#)+9!ySS00aCdVW7LJH7yy`v z`b%2yjiiqSo(ufsD8yBCz=+bQ3*ICY;&{geE{(7|M z=*XpRyo^~uF9FsUFT!V%_b}?{$4KPAYw6V3#am+98w7wmCgiIRG+{|~S6%d~QO~3w zvMMBxf(4S`#4A;W&T>y*5rnwIt7QHNsFQRM!gVp-djR=lZdcUe$9iEPE(I|tMzQ>Y z8Xc`+$<8Np(`NxqR`2t8f_cPz6gx!Sm0B7btTjC-I^uD+R}^s)&la^;$@3qTtZ2QC zj^-LATr3ai{=lQ?6)RvqFCf(vh6vC>$IO0~kmA^eoKLOWWQ73GUm=-Eg#v(s@hXMhy#82@B%kzO zKp8QFAjH%mr2jTPiZP@)v!0^;%agOqqn;#w>rF9=(n@>J4pZJ$CVB(ntUIS-GSAGj76g%NDGIx&wm78J5}$ zdHlZo(c{-w`8toUqk)cvx|U2+-@t2+(b?o1AiB`hP1%K^#|&2hzJ2}E0xuzvMl*XnRIwQE zd)pJzY5UIW)jl9MfJjslihCj3wcu0b0F%01uFd2A^FC-v2q6W!01Q>Zl3_$e)c!1^mXmDgS<0lAdmAV0^dS|CZ=SWlQW!4H7kFh^ zIDw9o-RHNg8G!&^f#hlAC97MGdep_3-Vv_TrjI>q<<4Y8FjiB`O)28&ciOre=U@B> zU}Hg6;>z>R`ql)nIhW7R9y`1VWf(F85Hdf1^@X&57)bTmn;_jH18V)DsF!X(xSdvR zt25s@gPQ9Jgm8hZ0x8_VuX9Q>WM6eL1p+=&2IE!FzJ>-N22Ypx-fuo+_Vlc5L`?8& zkUT5+P}8OGdje!DUgUyk7E(;VVWP^rdlvOg&U||^6n@JM5(vtKwSGa z=&t52T~Gt`78$y$4wa+57HD~~cnsQm#!hd7;_?}sX^dz}hq#5@T_MI>1#Fnb3hE#{ zw;PYCZ!*kq(K)uU?(p)7hBRA<8p<+`du>N!-#V+JiZ&F@XGD3luLIz11TOYP)~*o3 z@zO`{ymMf_Y0>Zl@*~)a%|Au)@i7A2fJ9nQ*ingYu?G_0Y3d5aD266rH{p|zWPJ$4 zEYh_x%>p}!fy_?Sy6?2PJWdJ30uBD^h_8?dbfk*bmN-YPmPfeG%E3|gY{ydMK2~sG4|aBSct05l#xLSTmv!` z1bD94k&%)9+`^_kWKa`)t@I>g24IuD&JR;n~T5H_8K6ydHVG~ z3BK)gKNmRE0?@Kxqpk-#U2JPJi!}BG_I|OfgSs+hTz;plWwFXC$LxpuC+$yLk-b!E ziE)+KNN)68yLDEfn;W}Wl&#_j)WzaJQn@c8)k&8Tmm%-DbQmd~3TT|cs`fU6?`b}e z?sab@fw>A?a}e9n{su=W#oY{ zot6%9Kn8p(^r@*Sxhg*xn@moJZ}YJ(5RMGen#Q7AWtaCV`ZOu0ZX?vi?A8~qXR0KT z5DGv_UKe499d#!2zJOFqYyEYDEqbr46YSgRc_$L0G2aUv43~~V?eP;RVq`|8? zUPhfgysl`3cJhgwSoK@4W2G<^FXH)zp>;s0N6 zb02O7oL_wgJ67G6fE0>3s@D8ltfUU^zgK# zqocaQ>-FjRMbXm*NMy;#E7R7L|2PJ{AdbiW^5Hl2!jOV1hAwDsGsp+eB0AH0>*)3| z!0al$xJ$S{yZt#_sK(eky{Jvt&9xiRU-;4g$1}V}e8#zR?-?({n-Gmf+P56N3#`Ou zh5sFR1HV_77=~shD>zIgF-1viav9Fr)dwU<|HUS+AunENvL7vgoptE-yLI@KXoLY_ zjro`qOusYk1X1G%h!8q4_IT@?aCAgQ{pWHN(}AVsFB|5cMS`Xu=jToyCHHj-v^N?$ ztTzep(!Ra7W2JJsz&21=NBj)ptqpq9)3Sfp{v;S_H@XPVbMXHM-0=rK7;KN1IaZ$V zi>c_Z{8V+PeV?I;xxEPza%P#7^9fNu`pOLW`_9`pJViPbT&uT0&2Hl;#-?5< zp`Ac7<{Rb{HL(?w`BEed6*pQp_^rbJ+0{+h)enQW#hQ>^1)Lw_hdMzNBqcS2Ukqv z*G1(qsKR0l&=#I>Pc5UhwZyLaXxh8_&rK47P07e;slkwkgW@fWvWY+VD&F3n0XemW z$K-9ytMUvkkWin>RKPG+L_4c&CD|DwQ6lq^_}1M-f0}ShvK-f`)CL@pgi{NJ2FAC< zf*t2i1s$KHKiU`t+GEIF^pu_?M1O=NI)XR_z6T#eDCG4k60t*fc6soUxX{m7F9$LV z$&Tlacnz)TTo(|CUNIj{A}xpLtyBL0fnhxC^Qlt}dr+~_x(zicW|h>hE{#6I-G z?ey$(!PTwqV9$xGcQ>kK{_7PW^*P2eyQVII{s7syrrUpUk9mx~@z(>3j+DD^MeSx1 zQ~OVa=Fu&Nw_ZWY75$fs z)zOi|?L~F5Qt{xdqAA=SGQqRWFP-WV{l>u=M@@3){r6jE2WA#(u8=OvIqwEx7L~!B z^)7hE(GlD1;HqIX@J}&^VUiNI*S#g76yp>ebxG7UU8cjRiOg%fCScUK9Ix_xT|;q* zOMtImd47j2^W4X(!aLLYcxszg?QIqtYYJ4lulPS2oRUBA&HElY00-Xg-ji1^>5ya7 zagJ5skXnYOD$&gPR>!i%gv(`G$X1(^UFX(>rLW5NX1af|>qXt#Ga@m>3%@dnRZX)n z3;6hA|2ZWC$SEl!nsV8%mK&_vqYf;Df*r=JA>V52_>y0I6;BgeQm6OBmP^UicxP2; zK6&Y!Cp|G1UsZerPe4`B4~`i^#W+ukyk~&XB>iDP)`SnVD}U;wOKD#PqNz=%S|H&e zH1&EXMKEMGDM)abF&sPPgz#gyBxdoSfd8;rNM>~0rzLnPAW<(+doY1Z2>|ZZ$w1~* z3FvhTEd*4mg)p?}!I$?O$ztw8eWL&}5U=8BQm<5pCM*7oNV-x%Yb6Pdf~Y6UtTxU0i9*G zgpA@bM?+!)If z4YmPKp_t>}Dz(7?tw*>}5%C2smiaOcT_r&B5#=t4(2D8}%`0@kmP&oV^FdZd1|gwd zwx0{Rel=UlCb&FO^C9Q0g}OJI%@;y)rni5tbv05Q+7)EGY4yQEhHBJNi3}qTQb9E1 zf;=2;(WyEZqOlEn?=)JOPG!(5ObVL${=Uj~S0UMK2AFZ__GWCvD70h&m1*FPH7g|A z#8B&5KEDYFHu=*pfDGAz)@}fnRhn@)y@+cfU}?t*UYLlRUI~!0amw_M5+AnfH@&ek zt~Ncp>~<;g8`G@vk#&ZD=)+APVj~k&$VLLI3(TGFNW|f}cglLS0uOSp`zx*a`t?FD zx2KV?ybDW_R=N;rbRJaS(yWAxUxn_ppa8Z-4v*#JS z-5&;yQ<9LJT&3k#gWu-1e9HnP?9q!;07Wj0*KVDkUWY>8b&ybh*QYMm9vg9*{)X*% z&ATUxQfE<>J1?HBUJ=i(I@4)uZZ4}y;jdxmL|1a;q9oAKuELe@q`{Ay*Fg+ScTy?h z<|SB&oIoJD2o(Ox+h8~c8ZybCTdBEqv>f;ur{fZUE{+X}qS_7(GoRzKE;6;!;qFku zIYhy<%hVKL3-dRbfm$+QRDwmoH8bu{)f?#v3FwC@+^vW}Ezwvg+taVFGvx2BPRpL3 zTZUSjI?XR-c;tEN;wP_6`f$TNFlxFPjaE*AT+bi~Y> zhXvsvj^E3Z$!=kQ?kuOSWOeEwVc7BmR>y18UQLgQ>RQgQ;4svDa?LaeQ$;sAB!)6-?2Cm$;Y zNG-?h4RharzUMpEpH`Bth}Pce;KEv>@HZwwQ-LLwpgBW2Z-p$WO7i#pB{e71tbM>? z`8N9r{}5D>5YYulX^F_m1A!)SVd~Po>DDUe?^#M`^fh+A-bF|wzmpz7$9GFIpV`&l zAnL#=YW>X1X(mXcAYraQEgYEv!7jE|cW>q-WJk}JfVkJ@heLXEq~}`4B}I{+ROO@o zuOI$W?bs>vK4D9o74d7KbKqY-mJ&SXWGbG<940*G&ao6V1Xq>7BHTVeYRmapK~G!? z&?tcX{h@dCg=IBvKI8?tONw%w=U>45ChRY?)K$=jYPXW^KrP>V847sP35>Q49_p*y z_RvlSqQEy30AmjYG$^Meft=r(Y|Kv$VRdsFytKZk5$!b>BbHsZ2r-Y#Yd;W^MuqY>9bBPyJ3DLHHPg zWk7=EuP%ZD?f9>}XT9|*wce%){pSpD6wHl`i z5UKEISrwaUI!uhnp`*WI4?v;_nC5Z*vCoJd0xwJ#SWG~cK1*M@vSK&r3y2)W6RO8e zKFU(0C`Y~b1GlStkKb4wG`2s_hI;lif>Qjx;Dx^)u=NbA;%ZAw#z}+=BAjW9xMewBSCf+lqMw8#N2NR*7U#`9e|0bkC;$T5_~lg zb`9WV#UNFRv_$ahJU(Ut$e!D_UBHm7wHiFrd25O*J*87uWvn~f>fOU0H^#OHPK)8o z|An3*TKYpw>g_~NL7coeT|qb!OG-t11{BgaCn{pRL7=Bw`URjv8$R`#jgErhQ%B@+ zLL&i_D|+SMxjTgzjGKX3Y`7E&4e2mHh!SMd1B&xsoIIu1LCkd5BeZpGuwbFJFyEOD z$N!eG+t~KAp4Wf9TH`P9vMy*QmQvEao&FJF@Ki$>#l!wYKD=8Ga zJ6uw-)&yb?e#tuIvBV|ZA*QCJ6+WW3M0g%DCn)2QSa+`4Za+K055iywyNh5pwjV_v zeFd><3cyZ+c?$B~pML_#6X;-F4EIoQmT;=Mdj}bwS~qy?h^)1j)2KCb2@Fn<8^y1Y~in4f0NeO3g(z8#(eoC2fYFpDfVA)o~CFg0L$K%qhs zq7|w{MJLSaOeoB1vs_C*#;IQy0BphAzqePbJa%fNM*)my ziVGUf`&6`#Y{UmQoo=V+vnQ34D`4<8Pqd*U**vsjHZuDWQpp2N`Q;_I90lJze zR&}2uCvh}cdCrYzsDhvhKyFNCdlHDCA|dc0@EAfvLzELfc$hw0?@kjJ?@NA7-1aC3 zh?@S-?EofKiFOIvW}iAMV^^ViqhbrYqR>7@6-%msbuw&F)J|1oy^$ELQqcn^is`4S z^4(fG!tSfpPiOTgjb>cpnB|W^7g{s}ND9ALO(%1uBRTHE%oIR)5VmBW>S((S%ujEu z!cA7N3)|rKLx;;YAEIdoIv%I;4Bwmja4SKyb&Y$OGKe=@-QPTBppQsU||R zt;K;%h70Cf5I}6FJ~yn7+Jr7|3M+2Rh^l%-X*t*?4ZcxRFxSiM&p>hv0zPgI0Q}bg z9G11*o(k~i+OoXtE_~sn%`3v|bZ3`D?evNTJ=9v**zQ)5orz2Pq=eT~cvtrQ**#~p zTJ7U^{~k{XbT7oS-cCECF>i!;R@~&lea{*|ROR@M8fokY+D^xv=O7r26l(DbZ#@dq zB$2sHEcsR$0cWA?8^Nl^GG7ARHO!=G=q5aw>If~$bh%FUT&)VEb;{U5sC1D?zE{~wPik~BmmGm^cD$jZvf z$|l9z-q~9eDSKp(ME1-UvUicav$8kY|JU8A&#CkO{vMArIw!r~_kCa2{kmS`IbgUA z4N9*RL(y(v#0%7E$|~H#D!q77rvVKq@k&x&++qwR`2N1J+GP$K=b|cU=l<5rNnDp= zgCBGD_{y>lWk!8TftZIa>Q`iHE?*z~RYoEE5ewO!pM)oaCjo^&{NqV$(9i5T@xl+F zFRu+6=cJkX6=(Lt5a7LuWxHa=LtGG6Qmlnk5EBlfhtT^+hN+OUD0YMPIgwBSYe_St z*FRIJ4^R`-!F&>#Ni*Y=M$~dh_0|uWpED}iI&nUyPlhV%eZ=Wp6%yHMo0$(2G0!LN znD#G~a%Mk&AA0$_Lhs9U)rtZF4raDL_kk;dUCjBx?T~=9vka&|{Ul5XSX?}B-UEaE z2?Vos>LRtwD?lgmZF{4ZZ|SzZxqx8ZdM?uS9TZDlebVG}p58Mz5-c4+X5E&|RBfb^ zJ&Kaz16DT*<{E?mX-5$}aX$4n&i(a5vC*|zrVrTcFD7$6KV zd&){xe!hzyfP_S5&tQVZFb_Ib2-HY%fat2PcQhn_9HI7}kWpVslb?I3MGrK_Vltfz zQ}Uc_zT$U)l~g=I+7Lh*0NAR?_SdO(X#TH@Et#=PsW<(arqBx<#5j0!(R%X59L67Z z%&6CF7Lo|>_>k}`^Lo~9P2P{Y(twG=eA><@c7H6peqvSiuLBzCHQcRflkh~U@JHM< z*&)aQjUMf;VS)I1y%#~I8c^gY!WOV{P=WA2!l{X%20*52KHmNt_B2}ND-smq#sHoc z-HA3}sl@`^JjEO>VgMSlFtQF+I&p!#&MUyW@f3!sR6{*$n_I7lb&7mv+y-}Y$3W$& z&@*W~RrNb|q3-IOmqfbXbVU4dW(lB=Qp0B0m*ReW9%;?iy=Z)gbk+n;T!19S$waPa zSQN0c77+3ael%@BSgHmFNzNk7v7PQcQVL_yo%)myC%Fh+10aa{P0H@1E3^P=5(+d- z?2Yr$_)S8~%dKVZ`vJJEGH6!%1AYz+4OA$RZiVXC?N`xh-R+Pz9;2*3!4z_q z@1j}lBAfR72@D|CYU{SAk8xgZL;y-4d@mKptJ;nbQjzIn`t_k#b%Rho#a`HX=%p#! z1@t;^gsTD%Wjnc4I9dST%B`E}%Dz<&E>Y)6Uz_&ro&$oXw{!$muAM14_o5N~&#TZz z@?;!anQQNntC(-L(ENE7yclqRyn<7&(V>O~yA(2|YGC#x%>akzj9Rf-R!cy1kYC5$ zz3(?5e?Sik+Iy`fBk_0*45Irc*BiiP9RSRIFHOPc3U5j+Ws2x}z`| z43yhEW72MoJGv&B{O)-D{8ogR*TALYPpXJqOpm2y-aqg0nmA0FPv5ruh5;4gV_qmF zjj>9gdhy@#Hd-rbYb4n-^LoxL#_n7xTRS%-kfK{XBtnlM#LhO~8o$e_V*6JQ>nHUF zm*Mm|Q6!IB`&#lFEV~+09cF>ccFqVvn|;`?zLe%sRJLOpf=)mwdk?M8I{2@v9sgag zVY0)%M|0uT_&u5A<@5+CPO31Uo!2UvaIEv`+8Z%BrZW>ctguSFiaFePs@4CW`}*gf zA8#aRqiUc~Sq&5bow*O-4`sL>ngb;i9Vhf9XjqJsSvR>C12|C~gDZN^<}hPg#q>za7+L(q^> zvTa7#qUi`8B;5?~oh_rlkJGDi-p$W%JpdG=qRk$|M!HPZd+`APS)?vHE=uY}v;k}= zXa4A5LsltuOf6>-as#DpuuP&WZ*$vO)>KMu{H$mLO(MB%z;e?$0sT2tlNAtM4{V@B zSF(wBUiZxhS4+hKnBbf1^)U&{+ctk^;ZTYt8B^$i0!DIROEM<(5IS8gj85D(^P%Yr zp!}0g1Xg7E=7WV21HfZ_2M-STsp&)D6_Fkdu)usEzi1A0#@uIh!sab zM&#|HqluiJ8*t;oG+7aKv-IvKLFr{M3ZvbJvpe(oR0?VGR7%BAZ7Oj=@YOqpY(4A{sHAe+P}$R#>%#Ce zeclxqz)DcFSGx64UE@vP0_xR}I(BRI%!rt`1ph z`aUGDVU@5YTNw5AI~Y1ajq80G08`S-0HF)r6VbN2>w|f+oxB^b()Zy`6vHYA+V8Gk z;|Qs+l`iMqVm^-wL*$y2sQGcqnT86F4++wyBXWLDa!ICD-SwYLJk;N_nTI3 zu{EzSb~9FWFNPd+u))tJ!p|nd&&Hy7T=oio{wxRlEGPV|0UP*>DGq2mEpc?4@J`!( z91uHVVD1J$e5eaR(Nk36)r`+yaC@L^+uT$oGNS_PE^lK!+o7}##&%)@wY9ZjntRPv z-l#p$Sy37Rz_?UFkzOl-o*VSjkmm;86Y1*Zwpr=jlX7%UkTZp9R(*dkpmzjqlC&}e z4pRm%Htjw*WrtSk)IM)vj6{GIWpL`JmqEjfuAHP(Z6JVzFCCT~WEd1RVFxLW;PD?M zEF!um`Bl&nZDG6`QpxLb7^gD&Y_M~s+3B%Ej`4QTPW5(k$WGn5){qoyJa;%RpMxy@ z$`&k!+ll?DWxl+F_)m*Bj{BHHRymi}3uUcjP_Z%bky9GdGJ2`C zs&i(bm8}T=-ytlP%d>tm~LlEskE09__!7_ z$w%+9&LJ7!OVlI_~)#c@;m~hICXT@()%tTjrv~Xa$o*+$lLo?e{j0?k^j1l$G~nm9E}1Q6a#(d(i5q8^l&tuT;MIa_N(}VL2Vww|VxK zod&)?zWIpd+uty?pC$BDWDXEpFeOtV0KH&BGOOQ09IF%_bQ9M;@+G?we_b`d9Z1E! z8cwpm4Z~YqDH*grzxUku|302T0e2*696wz*y&DcsYHWSRtV&?$n)!&%-UL+2tn z!HFv#g3%Omtts|Q?%rHd?hqbSAbjSjq@OK*oC>74@HyF?W6C8Et5$HT;=7`o55;?Pvcw8R0Q^U z?(HeJ)U7ckkW?n>JSW+=9hJi^eyYPQc7NQI?`Vk#lR-S5{%F=t+Vc+-0MbQ62uvq8 zG)idsp-5YxUPTb*<9XOIybc!;qS2myb1)_-&^ayUwfT=;*?@Ggzu27Ac4ZW47Ei!A z3HiJ5(ZQ}wEu9;INT;$IGf~I8_W5#nH?6HB$EY2tbz&LC-xAp$I17wxaCF@LPBT4t*Q|it~ql zNU5eIWFfd{WiT!@8@jFh#~dyO3Sf|61eWb>$hL9);y+=$um8bfy;}CXqiSfiv!{eX z=o7=MbQ6(j6H7wnoY8LPS)b=CPw|EoiW8j<&~v<0Kg;h_GX&pVo@_KtU6lTJ-T)Ww zf$X~R_zTM45u}R?RN0S;IDzZnfXPynSM#&w1X%9)_SzKv*`s&)lh(nsje?ReZ%n^|~&3j_JEY5zt|IZ( zL1-?ZzpjS0{0tu3^jZ}v3=G*qW*UtqgM~BzL(AxWf?A(xH^xebZhbvuWKsod_&KT< zTp@{Cc~s2wYlN3w+1#Yx8s2_BAC!|lp#dRy&tQKO)B+xZj^uVqU?w3dDqSBxnbcMqRAAt6zQ3x3H2|_H_8lWry z>MC@x8fysBF-w~=ch!g}CWY%=uuSbm4k_}5mg|Rc?6yiOUa+@)tvbRzP1$RvFtSnP zO>&pq+~{R-;@;roZ@wF}cwlCRV5iJ)TZfY&Gc)mXFIy=+Fhm9Jsjs0b?bnhnkZuEN zni0EBv}tSm4NOHFV3^udhSY`9R zdB}~#winCPQxz^ImO$!qE|D0z8a#h(xzi@=R$Pr- z=;5R%$^Jt^^N2aEk_%;l8Whj$$JAE4E{#^vGq~C{{n4;TdQ}LR40jk{6bDG)Crz*h zXtT!^Y;dJM4MD*_PZI#&=Kc;)P9)33UIWmz%3Gf-3SJOl7+#7he>&$0td8_Ou&FCQ z8&4LI;=7X-32NLPAi)q-FBS)YS3w{Rg=%8Vd}kC>ziI_RMuZ`)BcLjlP^qbSSSRu&{Qf{~Dgc08xK z(fDgm0`_Ke!Ek(h3m2PHF@H=^#mOchGDoj=#N6?J4qMz{JQqZ%s?9N=`E|+;y5Hw3 zp`6Pr>bIYAv~GiBei9fka?YShGQi_|EK%3 z$_giy)~S{WPo!ryewRNx4&x$)$1ko>L+_ItQ=*NuMNjTK4bhB;1_vGz%WXMDl!4pVTjjoHlrRP`-VG?(2hX0tU5lkc?28e$c8>m6JH^96fP$ zVWBeX{jou&hIWp}&#~?AL@H1K(JSC0kLBFCb_1@Ch2rr^*mx&yoLBz>)Y8VrH`^P) zw8TeB+7yuTAY!p$)BSATWOW}A#elcO61W9kTROpSO1EK_7tz{#kL(`KrTL|Kp1Ov!|-5;W*mEV57z+K%a5jrW(ER6F7x& z(&NW@&B-TH7j%KyiCvlr%~!oMrr;+@o_IA0r=A(a-U`h|)V6|NX&BrjC;0T~lf~A& z5;Pebt~6FyfJDaQAFC7RSH>U692N~SA118S(qbuKr!i-UCOv3qt^6|&3=|MRny!f0 z!w*=U-$DvL49F!^a-#r%k_Y7LCMw+y8ZrOtNC#OC#Ia`6#ackYgjwh&5cFq*c=Uxq z_Lc>zO?HFN)upskEz-(j-|YBDro(Sg<-TL_9u9PpvlRX_AfqAk>S_{P!WldW%!h)p z)-Xn^^LFPFQMM)?y$+avk~_9-*9RwdKjAPXfKB6jmAs^9BT5r>zLyNZCU>G|A$@c- zoY%n$>A5vvngrubZyt08-QIjJx*Wow&QR^kg*;qN>OYevGBlV?YJ5B^UhNP6zH_Aw zz7zu!&HQEd%yYO~I4CV)&TVZFA&c!s77hgUAT>HscCJc2Q{k~?`l zj6n38Nt;Z|TG4E+be4FyxboA;ePF^qMRVegnW*^~%3BA1>^kX5|%{JsN&FYTv*{pHWy%=~1|3*}D$c>}V z%f>p{!gk13XU{}Z%?Idf@EVrg5C)8~K*B{Iye}Mg8=lt7exd;yvbd52mNzgW)WU#PD-4JJy|Diggv;9i z`?3nGr*tFV?s5PZD4l!P)+T&y&m?bGPtx?I8lYTzn5?bk{}|8#$_g3F-mFuYjT=D< z0|Hz+cpm7X0?t0!c~>`}*@bNMB!x;xn;WOHS?`5d5@-N?-WSMsX(ZoX(`l8WqIhi5 zFYwgRmpf?zvK>)v>Y)La&z6W`y0trMTjLA!@tfzEdwpR}N2*h8vsw0>rza=(UflgT zxB3Yags1%fx(t(h%Cg1hhghhxmjDv*dV(rt2|_Gy)ct!yaQbHyeG@6SUwf=%1`=|p zwl|3_!H9V+ZF#HLix)RwQun#=ec;Kfth(+#XeLt4_hVJ2nA)atZ{B6R zB!G#xHR^hQSO3d<51|G~d?&*N6KVQR?Ex}lF?Y2b%ixfyEF+-MKaxQIe>|ap@Ty+TAa!^|+k)Da{ zf0-S($X)aV#&JrT8mK%KvIzfX)rL@nd(Mxp}K1I2C6j%QJ3;DXCkdc<*PV6 z;kqi`WcJvWzJEqPav(sPVG-XfzGw&0Z?YrshSxbXUnN?K_s&m8H7|Nd)fK~6l^9=q ztLaq^wG`3PBY7ruiSLTRIm5%r-D~#mnoMw%8r!Y36vf{#V&_Tu~YYV&*@3 z4ru{>tr+-Lu`o>JE)rV)McC(f(_>T*Y9?QDb%gZ_I(=R|g`2&u^#m}V4Zyl0emvAd zdV!MOvqGkgk^(XK2x3pmHYpQ_&a@aXM6Vk`rZ8p8a1QrS5z zOlsA${Pr^EJyDSmpT1bBpQ0d}4X>4(uE+cl#N07704unF zenuKQ0=BM~)AsPL8$u3!2}k3RzWtllXf7sJ{f<`vtNaG<(7CO3OUGa3Pe8T>XgoFP zJ=|uItE8H`U(pNTuSrNa&Pl>Yy4RYL1Ks8%4Phal|r1tw6Zqw#;w=W4Wa9|=Op!m!GV^0El*3-&rb-?{vAG-u;qzVP7c z3Gag9t&-x#w;031TuB6qj0HZ$1VDJeyc%3eXdWBEZ<&KDY+IKTJXGk8c_Ti_XQ3@l z@2_CCiV17`F`v;{mFim6T7GhphaKzcUWCjCwoUX;W#{` zP&S*zIBxdw%7=Vv%H=G^QO@3~PhIwmqdY4aees;mZns_QhK&Y1uJ&Y!Uwf^eyWyyE z^LK##mHU#w0~&qO93_13^K@TUsz(;v9L-R-Tr@K*(1}~W6izq}MuVk{pT>KA; z1ri6xqagk2(=d=ufEeGfQq#yml5T%cxxZW<*q<6lxm$Alk2cG!L3FEF86c-{*6_C| zD`7@;K93nlU2*JqQJfcx*@24OQosUBXC_z{M zI=XUDT-jFoGBcgTwma$Rr61@J6LQejYHx)WA5&S?4ay`Lo2@Mb3oPb+v6#yXGPP+T zCv2?LSA!YHAGV@V7BvJg?;YwylYeg!F|?G(uVAENu&)Zmd>G60Cqn{?68B?d%uDLj zaortxAY+Mj@IZVrAo~ueWt#6Zo;w~qz(}S zBhbdJvj<>$?+=HtRqCUoldXJ*MR{9g26W%Dd`j$W$T=J+5>Sk#WNnW%v@db zDOD*Hv!5L`t7UDpsLKZBex)|_vj_Zi_fg623m4D-YLxyuF2{dkgD^RfH;JDVBFqQ8 z=11Aen@h!wc77X} z@ql?!1pNXhF42|+HpS@uA70UT?v!ORnwmQ$m)DAlmOY}o0?4tqHG7yP1ETi}K7ORz zNgA_HuVe1rN0H;u>b-L(;$!Ij7hQ<#6LV-mY%!R35RAh`{LDJA9d-A*~`XmvWkTP@!M%*LzpupbeU^bRj-&5HQ(z-5`>PHl!=0p8Gk! zRm#!gqlZyh*fYKxYo^}5k(B51>~D)kcooc{(JducRlWf0g!Zx$=T~Fbh3jlV$>(_^ zvoP-gK=tvhq{W>b!`$nV4i&22OVtf&%NUiU<0M<1R_QU3`(|3nOOPymO-1N^9pR`Q zE+|*l`l|1dR&5%vT8YGXIW@f%dr1>sh;>=51+iv_WQn&=ZW@wZD0fkYW^A5C7tx;GzJtbT+UKL2Tnb!21vlcqrO;7e7TwTA%?`$AectWxBukg2tD4lC0whmL|LfKelsF#dK*#kF2<37Qx zG8PouFv1!3kGGdkO1-2o9shAhGWa^>X$e}ndm^B_j)wi>R*Uc2)G9BxB6G~d1MKfon5j1jCN;%%!~Q?d2Lzl z)^ZMWw<#?(z)_2=NXn#L89vF2BSzHJ^Yom|$H%x7#sbPKl?3ReBnAWyQ=uns@B6-sgRWEq6jDk3WAq0&g7hjXxQ3A`5{!W26Ax z5)8SjFyZ=(nU|=;PIwF-RB1Y356;mK01cu%w);`iOhJ3vCXDt+-Hu&~2l;k}Z$woo zx87N)xB>koo1M8#ZF5%@JFz;?nYy0Pvoc?bCzYxe)el~57UXG`>p5{O|*ES^kP(6P?q4~10 zXYo>d0lw3lYE$J6;XyBZ&&7WY3BZu?fPgG8dhAnSydu~bY#iH8QyHZ0$^!Ca$yVm% zODG4d`<^g7UFemD`OLRv;0IM9syo|dDW^&hvlEv9-gTd0$Av6dA8$x4pYxrNQed#x zKWyM(94LljLxq4z>-rAs=7a&vv@M2tzSz!8^Cd26IL^IWhusek7YZ>1v7g$y8u#qy zZHRj;?K4E8Jd2_L>&ii~-xgAJU)Lu_Y4UU1P{#)$`9v?@8To%~AAT9~c$@$~P26hf z1e_Yga{PFQf=RHPF1ti%)P54T*xGmMT~E#+EW0{rseFm7eFp)Z6)5pe;NE0*33rrd z8k#TRpawap`g%8=`*9`S2G4}oC6M8xVgU`}bL~u-eQ{x7dix>zGfOWJxXTXqlH(OMGIgfXVm1MF&Q@YO_BT@S*#tkiH)Mj4yC8C z%Elhtet)B@xgwlVbT!-#=eKtn;*)`axOuNGWAoA814oP9fcS`n*c9QFp^pj zM0|k&!nkwEN$6P%$nWL@(W(9Ji&IaxT*c_X z6f7-ytmxg)IZX4^s1wV0L?Tj)4_gQ>eYsi;i(|revN(B0?zfHBFOwoP*il5|(ZYRa zwBS}+Dr(k$YXKZZ&vF{1@Aii+Y>5WxG>G4r(T8#_ztE=af5k;Q1je8jTeOxm11x-+O$!3mvw8~fLWOozu5Av zuRk$k0N?hT;iuiw9Y+2+r4F*+Up;bRLOO)Dgcl}Y zlsT2a>QdoG(L21aU8i3=_{|g&LlSB5sD}ngyAH#gR%*Y*xLiANFcl)Z*aO=~mBj79 zQa_?%t5;nIkKfwvo+_eXri(%iI?*a;(^nmvQHUC9JS{5RIM1zDvOQ=#T{;^lsY1!M zf-U{z3sB#Rm*60ifVCgqYVwA*P&1eg&YJa>Ilc-9PsAqpin-+{Kn+8X-C`CWx8LDp8K^FObY87Fy}ev@xMaQ# z1_?Y$R6!NxYO4BPMn;&akU!m^$IB_;8_6AK37fmOR9VK zr>)~u+vWJoHzq={GZ<9uzc0~Ve(4vZ(48#B>@noeA=0v%n~(0wWx?}53l{Jrx`hRi z05h?T`=<~3(OCL0#!}LjC@DQ8Dg0bwMuj}oarQ}v4+S6h!Mj}!j-t~5 z=C{RjoA4$~hMqDKqM-`(O;NCT8D|U!d54jF)x7Z3$D=*t{cD>GvpmtIFzV*k3P~xx z!m8_|ZeHheffXbH?f6o?hbyR|H%VPQVyiAAUAE7$R1e!=cnijgL)D z$53alTE1&%B3mz=wQjGe@dhVDu;u3Lx)x6BpX=x@@ENj%Q%nAz>9u6nutFvGeWS7odb6Be<9EsDi8?n)uqaG%X^M-<| z*)TPhfYWIQs_|g3J9RLs*WR4uyRdMUcac6^>f6wczS{bryky*AJe9E>wQGz_N9B80 zaWfN=U`IV`LS4<=-3M{%?Kbdn z!sF2yIND}h5|zD?$a6fg!3NuRzY6RYSb;ohS8$g;3*eO7du~S?h=^uDx$LtlM5Lyi z#CT$NHolm2W_qz|K0RX!3{@;2hU{$t_b`Gkr=qL!6z=l?QhqH^sGBz1U^IyGm1Hcv z$1awwuqkN4W4zt}q`j~WL(=slHF#LrUwW((RMj3FcIUC8_@jap&5{}n>(d-K z^)Vdf()uIWm-or_;v>hGo>#uO)_e6Mar@$^wbW{nuuYIvJ@)8L0h!Bx?rL#4*wr+S zPf9Z@5XYuybu<5#&V<-v(kMxVjlLtu4YpCzmlMz$(v{j30;P7L)Tl2VE7O8XMHST5 z7#kS4MxKUzZ_`SJvX#$jrE-TSL*jT4bVVaMP1X8s00<$lnF?jHJm}KwPnbnzBlPX; z9BcPe9pW1#o{2pRmp1X#r8P9p`4qB z)IwxTf_GwR7TV7AxM$83ZKa6#-1e^AgO!m4!I}|Gkd;a&uCPHgP{%rK7I<$<5A}>M z)1=0HQ*7Q&Z$$V);SB0C%h53DQX$c@6ANZEIQK0?hDQQu?vYDpz}bkR@w)XZBt8z^ z9Z6t|f!-0JjdsOr2M5_Y2S#M8|(EeElo?_!%+&k3)L>TA?x2iYkxRU?WA8FPG8H z&$0KX3o54?@SKP>RI4nDT%M%~;?}W=03LruaS-CmXlsPFBBg|j;q)6^Owql0vt_DK_F*W&vTp3A$#Viso8iVle%RtpiK4G$HNeG8l&4 zq0YRUYYd%3R-1XntGxEP#Aj`@3)^e$vT`8M(jM7nx<10DlSu?%X3f#x4~MG-ZI(pIg$tI>{Qi1oOs z8%U!rYUB;t>hAVc9Z}g0TZ&DgdM}8pc{I-IHNP+2wpa7h%PkA(7L; z{%tn-vn23A+`o%(ow^=C3VJlQ2^aA}Kbp%t9A+j1Y&w_b((aZlR_=_3K=7;2x(j5T z(&e%RT>xcUF3icapWX-SG|i*kPPf5UZV~}Zj9>u){mCMdcq;WBLKFRX1H$0Zt;inD z-1!*XKfgCv3BE3WlzT6h33+9_IWoVz#hx+}=DG85qst#O_F(jY-&<6WeLVbUosE$Q8?pGb|65cjQ#%leHimgvQ|B;_~O=m4`RVZOcN8pCh zr6ycuoxdL0`6ej&xyrHCyAb2MYh%ug#2)zg{e?RZ#ntwD7^w=DZQF1B>iR`XLLpptO3X?5SCtX03z#AhX@ z^2;yz*HcQu0qf1>Prd9&nwG*9*D9KD9+SOz=_T>wE=ra45Q%^UZeyFGrH{3FJ;5UP zWH{MOWVt-laYy6SRFU0w+Uy3CO@PD~i*G~TgiVz>O5%GQ6oEtz+A3ndkEWmHKLwmm zqAzpfdSMy5_qMIqu<(7VuZ2(uS`QgjKXjWhB^asmW20(d9Eot%mWDmbRc4-t6D^vn~ni;M&|}CXcL4%aCa5 z1*}Zi=+`p&*oS)KERDmtmif9yjHskP_qG-B?e>43APrit$GQ8S+u1g}BKL>Ir;!^Y ztvm0m>hH~LHwRMeUVq=QgEN8gcY2EkO$>$;x-vn$$csFy^`(1?pxHjW)1*6ul1 ztRy2dT6;Z@>$~0Ef#87|G%XYdD(#VHT&%J8RPb|hLv@BLH8SS<%F&xxI8r1-nCfi{ zGs3MIw!~?cubPdWf{6pb6_MAQKib=!)XM)S0 zd7%Iqk{J5lmt$gpbU&rubcO;)W&BY%6%&2_RGRk-)Y#1SW|ZTj?`c=99D6+Rei)8L zm~x>F)z+xt4$nVpDq7~Zx*SC!u+h)Sfz~{8(|%~UBF+GXPpw|&K9|9;+pUkuMOW-Z z95ma(dcpj+JMgcQSO760z4SOqz6{nv`s1C-A*qFfc4rrtR0oyNlmU8UPa~VQ+NAsV zG(Qrw*XO3)0wT-`bjKr9^9>fh#4pTW61M70OLQ(Tj#?IcRw$Ee`w@GPk@K*(5Kkse zN1mUZO;8=Tmop1LKO%rGglFYLm2(`8!b5K5Xtx|H>4WX=xU2GaycgGH{lfn)0)*kR z9H`00p}VzEgZ2G!N+db=lh~$YZH8UPN~$V*t3V#j-rPZKl&J3hJo%gw7iCs@vP?pI zX@jnP?8k=Sg^x|ae33_65A|n{%AY(}LHo`<0O;N3yMqD?aOp zI6;RScaY>vFXx@>g;^>TE3;d&}`-n@6O_LbFIzlLV3S`O@9wvvZajMyDq( z=G}VF6aOY>?s$45v&6w-V~|ie4 zGF+DQIkfm7dby2Woo(phv*OonPBU-VV|(~IlY@RtHmc??BuGvzd>V^-uG>U|Vd*0B zj^)oiCXNGJ%+NBrwCNs%kejqhxm!p2AHTVFbE$6kjRwrLEoJ!f4sr?`h2KnYV)y4J zF8@57gOj4$tVOE-O*F1RS}CZQM^9WXL{|9O0a#NHd>BmP!_knb`nZbFLlSQx3 zRn0qw1y_(#xac37N$`g-*+o+mTIj}BWa1fI3@(@Y_~gG^ZyD(!kG{Ggj~s-nX3Iaq zRIT=l!_5V2AksJ3HU-+=TtcsR^WeK_m9E{cU0QowbVL2?*V86Q9_Q_qOF`$-Q(UF}z~#Qg0h_?a6cg-PZvu+?Ha6XPT!QFV(%)@A%i~6$#vA zagcS|^DIihfi-yyV%AmOz;gSc|*&<~cW3W9S_oe>~o4 z;}fhCx^yYQ)JUHEdmFrgM)$o2--!s3FPma0#Xa1Kbp908F%{|UaQx){i+PHj_&9Rh z7E9UnrOHL&yXJb7qUf`@Kq@slso#Fq`2Nuu6ZW0$dXex~>N?G37$jaPZuE1Cl2cI)SG!VZ zpU}IloXfiLk4^c{R6*e9>m6~Y-R34zRFkJT=M-AKpc-lK+t^spk?g?yN>9c5POJm3 z-D-a0n)djbgDR_$nXC8c+jw0dEUt@P5j2Jc3Tpv-c0=Qx9G${Czu zjK+(S9DZmV=085RoH`p4P-eoi>|3>0`25c-We&-DM#j)I+}^-bXZ9CTFMX<(;{4&` zBN?{=2+r?u*?6oovPvzKmoD!1@air`#D*WBw8m6W&CAb9P3OKZjJaAtW1)kO*HJ?4 zR~!8Iv&71W&mZzE0&?+|usRz6q;>gx@~#S`6~i`1D_Ztt|FR2Yg>4L9kh z%WuFi>U(oF;)Rw-Z`AaMa*>D8OI6W_f;6eh7aUf2L+GA){(156!V8>%b3%F|TK!D~ zDl%v6=49=2tSN$-@|n-2PO|Nx16713{Ci*Vnx8CnJrPaym&W~+&4M=YK_;P>-Z=N+ z$q!{NmCYVTk4p#c?|!4;km5NbEg{#K%z8Wf)th+X9Db9+Dbj+Z>!DefkGb&Kip?a( z&eBBHDDNKjy%YKKMTW9F0criW#EP%GA|6Vtj++LNckHt1RdNNOJ;nr`y$Gn6Bw^^O z2TeWs^iba;j0!kshlvWU%Bl2SRB{U}w_a}5=<{pFkiQGLojUtbN|H0`#L}rtUOfU! zD^BOy(+@TK@R%0v;&zev2x*jkPds7T-nDlv_}a|YgU_70r_F4f(vtE{zQa2TrIKa5 z6(f*6TFcbY8Mrt7ct>{O!C#XKSR!#0Z1HC29rUi9**GT|v+^ZQ@RN6HBjGUVf1u?! zM>PjT)HWW$enGNgiq5IA+zvwwWi++SiVm};N)wqDt6 z8CwV@PgUkCGxlL3iwE{% z_nM8)rakd2aAJL;9?D+elMz*&c}hRy4Vh_AF!{bxUk1q~s~siI{g;;oT08``^_qXg z<1hv8HV`@f}gIc#~|s%PH)#~?~04v4Tt_A`U80ifA}!&E_rOv-?D zqydEIn|OSifxN5LC(W3^e3TyP(wCDIVxpINJBba-Hic^25y+78C=xonRoFa@TH<1R zVn=d*z|3{i=EX+A*vowB9d$EeYVq>prZM&;tw%H=g9*Wr+!+P>bWs|GVN%Qwwdvm2 zjA&8OKcMrFJZZUKj1pbWhjiJWpGjCv2?h^a?e(I6KknbHqdW`7Z;|?OL4da?qmBoZ z=hL~adQCe5;F^+@0!cbn$?2gP(5Q#E$QY0fRez3u(3I!=jF*dixAKP$ zN(9x}p4n=j6I3xjW+zkcD15gR)-8~EN>B1l!r}zkQsSefL>s60NR|w=p>HC=cd&Mw zV(72(Rjs0zYHzm$mg$8Iecr$O`0DR)@_Wc84*NEUB9of_bET7AB(FohW)<*sSV5Zb zeWTIxFfwL>7V**{Sn!6zaFfkwh;`l{ZAEm<5bIdFB?RLuI% zYy%1DzndnGnCv@QGw>08l%5vE@Jswsl-o@~~Gg!2-P`AT;f4DTdbENc%LQLH?j8Ks(2e_{6L!GV0=Er{HL zx6-|FK(cBD*PDx}eEKDO-p)>XdK6zHZyAPk?GZ$xmS7h$+p$yYcC4niegeg4)!Qj3xeG<)XU) zD^mD0loPn$m5sSAhsf&fBm=fCA9&&=m3v5Q7CYET1e&--CIcmn_zg0t0bsPi_^Lbg zko!ePM{ooIp$XY%K~hUK@jb4afeZFi(>6n{k{$N^lC~sGtzR7O%zT4lwSstu!QAn> z_uq$2pcI^9A#-M!?{fgt7vaZP*0d}KDrb5~yT+*);K+u-(hVOmKfKHE?3 zs@lK*Wyu(4$~gw(@5fJOhYl#YAZ2YA4!hHGcTynMykTGTp@xnoX%o@{RHjkRjxiY` z;AfeEkdqAJ3T7dI+71GzjgkdH)z1f&jttOG#JL~Ql}`Fl^uq38PDmZCYN9|+`S(2$ zt3@JZ!tdM)Te^;TT$WSSO3Z9J(U)E^Djs#imAloY`J{htem{y@aM3_y+gu*Z%X?HIu_jz`d+6 z?zjshru#Wsm5Z*^RAS5U{=WlxE{}3X9>pK%>f5STy{A?4ZQN=$-+4&%97uG+F)k?Z zS#~XQfuYpMF?_Lov!F-V^aH!9>gY0@zb~yi5B`rM9%(Sm$70)80n+RyptAFvB7=g+ z!HyybNJNc`TQHHQ1pzLrot2?tCNq~}3LHb4)rNff(#avu$gur~sb>wfW8ELQR(wGn zn>EMtZ*bijODtu2K(+^Y!6{JjAX*_& zy4FCnbm+@_odG4hC{E-7#Z{<8)%42rQI?_j^BAG~-Uo_W1+BqERV~4)Riq=TFW;0b zy>1t>>Xsm<`LsCqwsy+kKL1@Hd|J5cx;w zrwizJ?ZcX!=nAGXwl41I7;LrpJWb2OOT>`~c{!YlUM>;ycL^XsmJ2WXNhf2^u{=0G z6iA#`1C zy;EI(w`=q<^6S-Ne6CKG1t@=HsZmXlJ=o(7^^d9lzC8%A7;2E=C78>u-T}S<;~i;) z>u0sN2tqz0-SiL6uL=un%(|8fPPwVm@0zC0sDD^|iaq_(?<>cyQgmcwaaM*;DE<8t zeR$@B|EWG8C;tSEKlPF$8cSR0tW}UFX$9&I6FfSc@{|Wnyzk#jT)N8VSO}ATZWB_v z|Adxb{~3tXMRhUn!xE|?f7hG%%#P%?NZS*!R z_wPRQzgLMB2p@f7>}L6-J5{lBlwUBDfgLw`qo{%fB3@8=7UMZp&s z(3k7WW7lqUo{rTXQ z;t%4)(^!;)>;UKV*9PlJ={@|niq#?)F$_4GZy8t=Xk2Tkw zx44y0s|IN>L}h#df0v)1PkH0U4dupr8H(X)qDQQL?f7=o+H~Wa z)wj4?8yT^l9|=qONIiy!U&`^n!vG^AE@U~uzgHQ6Tx7sGi2(AkB#^`20p)}h81^&( z*}kvIM)<7y3)TTBsr(#Ufv>f}F`$j4{rQjVkrRA#zDM$CGnJoe)2|u`DK`;lQ{zaI zh+i@g1iBzB?TZcFhVk2_`4sIX%BQTfYJIuo}p(Q-Gfd0Rhw|5XhncvTZ1EDO!>77Esp1)y*q88pkdlUSo6p5F%9kLQ+D` z17>?lB(6VnBKe$(ka-y7G!$?nAC6!5rbrW&&ZR^r18ad>s}de*KxfDGJCc|fXH*`6 z03a*K>L5m12rgD02x?xA=L;>v!Y8}$Wzt|JzXPGTaHVVw?$(1b+8J3;OUMHK7zzfL z5(A71bX0HrR`p3d6(AY(E{?d3L7^ro$T2KN8Z*(kNwJdR6iW z?n+tHMybE#vs)VpC_{ut*2I~%DQ2nls1=}I?^rnPR(qU$2t>w$4{wjLwofI;xZQ#Q zAhSAASxt^@5@&}f4Bo1paXVPq&}tjxD+IEC-W8?2^=FYWj5c}!N&e25PD&RJHp?uS zh&LlYRzwQp!th9zH%r5=udh}OHunm0lEFSJr0`8B zWpDiP!E7$imxfmz-?xsQl|cF&enh3@R|Gmx^!oucGQ_cwcfiuyscMJc^~0oTh#p3; z#CTpK2@a7Y59%AH)!`L3XWN_Icf3B5WVs))*ZQ6i^LJU1-xtG#fMr^LAsvwy!2E=@QV(T`RZ5d0M!_Lc|INLnnM<;~ z6TRL1dn^5UH!|ZOMCNpM zN+RMEvW2N+$lk~Agkd5ES-F?*?auIo!<}(z|TKg9=kH z?!X`|E|=0?$nfn6>AVFUQpZu&FNdvHqB2w;d=hNfG~5Qg;i}$@zMOJMMOu5eUOor( z2^Z0>c5o^mF`dd3BOVmu*V~Tkb}4BP6P^EVpV&epRKeryMIPs=@_H)`XB-TfuMsnw zKW=H1%%3A`^Qxz%anF>1D=~icHtGq_gUghCd3mW^&4zCQS7o4p<0Wtjnk5(1rr_vHT=fiWM5<}u09@W>!AcfF8j3f2jfV68>L2U z>8GX9@FCq~bfgOkqIrS3gFAP{XlQ8=J4M(I)V#%YjM(zUcfEZjKCV6iS?sRJn-H!v+JWt-wc71zNv`2#5vz$rT zZ=$f@;9tVJ)5+J^Ky8(oyK-*4uS}i(5bQ0++Nryeos+_@W7O6=o?HZSHXm~R)sMWFzV6$pzs#mJL z=SHz?sy0F^I}U_R>Z6ryD{s*Ui4_RVb2h{V%R6++0a)hkRCF5%Q#{meEFm}Ka#S(| zRCqMo=vmrxxUAe~HKv;mtBNqda5Ou(ZV$1DvE*EJITGPy^oPk1u2h9^xZ>~Wp{Wsj zE3s)i$QRYP94D&3S0)GTcw0ihH2ccAZxjSmH=5^G@4GCIEHu9@%M#HH3dWwj<}kX8 zcF#aAPcbkuNW-%As_H-vILmf!^&Em%JCLtXQSZsavJeqTHgBQ&D!7O2C;SGnPxy_K zAxY_(pVv|ejN8PH@TPdT9&qG=o9t570Y;n2xd#?69Y4~kX^BIp zZ|y|K2AQ7!Y|>S%T|iuly_X<#oS_#A6vKcQDvtz-5#~6^xDp7?|14dK^?nYneN440`s}IpS zvMGm)?F3qxx&9E9)L|1%?qRb^ybZ@Ri;(IAuxKn{FpA(J8ua8^_*G8^2UO8BeQ9x{ zD$Nf`QBHc(7oU?m0;ZdFJrutM6)U zRpuCUKMEAB1&|fa<>r09Q&~*~WYGeCvg;814)1Vo>=)W^GF)tiXk}&uxW==qIQjz$ z5Fn4>z0U!e3QvQpI`OPcu-~7Hx}M=Y$60U!LP)wYZi(x&$zsSnhk>ls-jqIJ<9Ytq z3AGT($HF!6<1~TtF1M}$L0l4q_~)<=x=K?gf7H&kC(`q4H#w68$^d#%YaTCD#m?NSVaM|yPvM<5$A@Cs+=O<~(b4IM8|@+GGpct)n9r~41s|O=p!ekN&RId+ zX&wgH|2Qta#t;hEvAY97rmN`u)s59W{gF=%zDpwrQ<3K&;mpw_iK>C%$*a zJw?Hb_N+?ecrg9(XkIPcw%OoZQ^e4{D`IcPSWOzTmh;7M==Hu@Ilv@xxGS!ud3IJ2 z{{{vYkAnmo5EWp-=bK2*BY1Qe{z>Sj=Z5%$DenLf@Zy%1j~GrAVk zJcs+k%RrmOJn<+g#OEp#s)3o`q#(^MIRB2@fK?|-3t(uks$0v4+dIplmYvO5MAD?< z1leae3{)Y#ZWQp|*^)B{q9IS3jYor$ci`%|8T9@26ecN>9bBqt;X9UkzX6sN-nnDx zJ>v4xJ3o(6Ohbak+?eP5TN%_rfxiW_KkdRaKPO`bE9-!Tw`ukgn1Q(k>+21EbaWH7 z+lA7XLoR=x1HK%9!( z(}iVfUQGAnOhR$(co}kpjANs3D)Os>-p;Mc4 zuW=-liqFTpN?RlBx1M=`(fms@#tx%X*y*KH#y+yczqLqA!L)#5bMaq`26$)ZHW{mI zqq{-c>O~tRDL((ac3RNfbHddOyZVVv;kQ2P18e130xMFdO3ni~;&Gq>B-aqJLBf?sGZ5mri`CCd zWF7agxZ!GzPy%wgs>oN4~ZoLwS ztN?wN?TD-vRw$YyY&w{u?*#z!nE(d6h&4oHuH>&KV~`J~tMg889{Uiqu&0r&kMqs& zTq|dq-)(Yg5ll&u2<&(zw- zT1i6NAsS>BwUFxU9tw7$2Pq63syEa|z_^wp6^rh&Fk~f9o?36aIa>brj|R$o($T>A zEMkD|@aDb8ST`%s+It-whK|A}8Jl!)*jEEH%X$Oh#Rw?aSsSkF>`mPeRIsUCN`_0~ z$+em(Im!MDvzA&^ortYsjNP!_tKK@F?d`a^FZC9mCack8AHppc;5Gjp>%ejprV+ne z3^AQns9R)d>2sCjss@S1Gcclc3H*B@Xo~_*-}`VCpY~i-{S2gBp(*}@=_iu^h%GZeA=JgS_&-)drY2Mf3FHRb2}vjV8cgK>7@ZUJ_Zg}3x~4H9(E zaS3(&7e8jN1@X{wmw1a_>q=?IaeO?(FUI#y3E`0oHl=f!}T^g~Rl$+m~XK z^RE~HjA!=V&~*lWL!I<2BMn+l?#(!`+DVic9VvL=Um3E$K00!`|FR1epDN4QZ0j0<*f6bA`6 zKo{@Iq9Ma~K+T`V14+9j1BR~@+6XB!u1iiMNN=m*&AKCKOGIDa`95?ucG$Nr@7pn2 z2pN(Pp%?|Kldib=6F#9H=)n2vKF#`0F$P}P4a`X{U-6mq4*G)~*wtD=b)U($n{3ms zL!eqBodyr~OOJ1Y_hF%2@$hy7+FO~`$Z?BmHnWYN4F`5+79gy7lv2J)tz6vSh7qv@ zH(JBwL!KYWK}i2masCPJ!JUzrRh*YCQ)qJ%1=qiyN;AWHY5 z-HG}!E$I_aZ!9rfjjA4WBbP@ezGvp~JeR<;Yqx}J;0%oA5nX!K7JS}$&&x zXsv=LB>DkPH2-lN8o&WjNRV08q=tq53w+rRy#_A{-y{Fva{gpVpb*|}lJXxee<#JR zL9++|8omx^se9e*pDh$7{EJRPnh5@O|NHp=h%W!{%m3fj`+s%taQA-&2d_G&d z0@5cXoDqPe281)?fq;|7K&;crj{QZH==wnp3Sq&ra8O~@A}J215d~8RCCV3`jt6Fu%a^%4>F)38kppqu@e@s25LUrk+I4f-8S4q~~3)$S^giL*d>EqD^^y zsn-`+E?xPy$%o!{KJSBhcLx(h@_Y)w9P*_+Oj@`i18|xly=>MxzzRE@2#s61rqEiX z#d(5I;t~DpI)eDxn)Fz3;38;B)|j>IEDIn8D#h=A0uB@IpoFY*s}jRDl9!-ZA5R(p z6O~HBT+Ob>0z95NF{joK0>*6uVlQmktzod`FnqMX;Z@rr*I*(5(@}Q{%g9{I08m{3 zsQ=tT_e-Z8U0P|58u$k#POXxvpYNMom>KI-O|7^R{#lT@6ffv;r3ZzQm?Tv+4_h$^ z-i@ynLIH@BYM@}ZdTRY0kh`5wT@lOQONxsAanOf;|YyNvtHYA6OgOeHrm;-+|Spv5vlMv>>Z~*|PA& z%h<|#jSY_T{12bl4>Kx|e2!9f4;TV%yz7cZ&&xX6ny}(6psbTam}BFg-^@p(ZZ(JT zAu#W$!I<*|I3*?1!^jdr!%srTyr*9~051!W61zp2c3kKp)OWtS7;3GHwW z#3}6W{X5RUdX7vL52cpE#1?nTX){tN=#Y|i?%qRCjU{g%e<4NJ9L$=lW#XUTK^;^Pngv^}#$K*KmSZPBJSpo`ZBzVn?HI`bsWMP10 zm?#4W^mg~NRQ)SqWWo|)GtawzY_+t7uT1n?U_ z567{1#Tu8xtP{?s0f!|TSv5VZ0PR{VbY{D{V`a~5-<5FXkhz%Y&yWS3`A0532{dN% zc=9;LMBMS37p%OKVC?bHd${Q}@BKp857x$NBhM~*J-z&=VD(*7YRNbRpu&&`kjJx3t)E<(heDQq=X-$NbQqNr4PWBsPF zflwY%$;wbZpKj_+b6gMHb{$S)smA!xcB);$-Re&~gQJGSNJsfaSRfwg4*6J$^X}Vl za5}v_3n!715#m&%_Z^p}fEjQc=~{q3j!y(hNTJ*cNJucNomls^3R(#7@*A-l34V3{%~$aXLGohL3t`S^UK-ZR6mcE zsmCk6J|%S`2_RN45S>_ps41bG?@nt=-+?rYyIVc9hlcM8Ra1oX)dFT=oK|i`YbPPHU1%#HOQq8AUy7M=N-#neR|f({I^LNF zh(fkiw(sO8Z%s?1JpRl*JY7vM%4pqjSgIFD-3q6svVh(0{|MpTNT_zOa!9+tID zli0VR9c+}&6e3ZQmoI=4@MJ{L^Zkp&sgY9;$C16+H5+73cg2B21Q%4vJ4 zm$}Yb;@P5)EWP(KfqC6yDukLUo^Wi!E24@|u-=&$3`&y_sYa|+z`*jYrlCh|_+M59 zikoKqsa8HUVDZFXxTpESaj6SwM<-I9|Mbct_b@o_DUBZnre$S@?uJ3cS_sCZ$s%oT z+Nfa%JnPMr(24!`x-kTgEQ^c%ROv>I*#ygg@d9$jQbxG3wGB^r0PELtf`7Z-v|*|7 zJ&tlekK<`%R%{KxAY*l{p>`vc#T%ug@=d{){!tk?7>!*e^?5jIDVBu@HSEax zenXlW?9~vB#+Q=D0w#r78lA-V+aut$1nwmExZl1bqO|*CX3I3INE78L%Ne7R{`=4f z2gp1c5~mtK7RL`GQAfG#daKT0rsg#x@!ZrDXkpMv<2;(nx~+PSD~!{(hd7aSb_kc^ zdZ3X|2bZRU)-}VBEDLIle&)Di*v;xKZ@SE3-Tr`Dc^xs)bVT6hG0wJ5nb4JFk_Wt= zOF4M&E+@LiO#8W?X~Ztb07pEDBva;TWoL(mhrb^Lj&YgRs|1m2h8Nm1IAp;eMgs1W z+=!(*o8|5CmF?2dUIkyj5^zcH3cO+lq3!~V-gK|uTcr}J8%P;Y48#eICv!xBPc=42 zcOfD*$QLYv#;pK|S!D!S6g=}>8 z4={mSXIx-|^QS$25c?}T+WOWj!ilheurPSp0?E5!Ms{2oV$iV<^wzmcSkit_T=Ixy z()UuSwcQ2fmPB?1R7F`xip(?bWIGXmk&tH!vt$|Z`xF$iVQ5<2zI<%29gy|xFT*g9 ztRNtA6lQ;E62DwoAJ3yMm-fJ^HI-C#QPe>vuK?m|Ze_ipd0qWUo=mKRNGPSo=`--3 zd?H|WU2x&3D!aRq?d7d;LxtxY23FgF=ISI6*9Sm>QUDAvT46f%1I%JH_^N+lZ~B%V ze+bFWgDy;;HlQn^0W}+s`pNo>>_I__UpQ4fYBqy^n!bUo{`Ej}=+%+R7lyfQwPRKc9gLZ!j)c`NcLKOS3HVW?oxbfg`#A+Mg~; zUkkK{s=Z_$vMc19A#6FHR%nUXgJaN4v2rBMRPV~``lm4nZNVKV2^Z84u~w5SJ4F;7 z(RH!%ji#YIYx@XOj4*A9>SV_DHHDtEY?6X1*oN36_79&m|CH_e+@5jTK+!i{3)+@< zOy4D1jUM1K!cI7AxK1dH9^AWMhr=E3tY%;)K3xD8uFp(*{xNJ4FSxZETChnYMkA_b zIpBMCOFf$UESf*My+UKTSy$XkJW)tKvIGPZ%@2zpYbuDKB<1R#-3CByOb?s8!oGcj zfCz6cXySZ_H1lP1$Gq!hhrOV%Z0YlyYEZsC^MJXTo*@Dx&tT3gOUwu3=0=<@oCLtB z*ojk2$qE@oGA6Jg-r?i3Xyc7WxB2hC2wn;L{$XW#7fkny0$y1d`49!;BqgAhAs6(Q z%%kKkI2nbdKH&0iR{RA~v%&ZB?p2Ud^Sa-GY49U4*7HFkn!gkUEV{BGd)JiC^< zyXg!GT2FYq^`WWm7@*+2ut2we0iM^FCQ93HRwdpKz_ge1sf{OU?YX$F!*`1AVV_^y z81U6%G`Z`4U=6!O+9;g2Mhtpa;PrNc2VtMtp5u zKUcH*C!7k@YXkkc8~*k8Yd`*{@8jpYf;KjF}b*z=8565qc*ckRbDFO+{Nh?vV@I|$hvKOGhL zH@5aaR`3Zv;BH8z+<*ImxZn+iT-iMQFS!8oi{?j6QMPRH{%^gD{|~mnvbZmPr|+A% S<0|XmAN?c7+Jr;4*Z&VLp)522 diff --git a/assets/native_crypto.png b/assets/native_crypto.png deleted file mode 100644 index e3fdadf556b1027af53e8ef8dbb391490a7bf9ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30364 zcmdQ~^;?u}(+25g$)&p+M7kTKyGuf9X;@OaOF&8*q(QnHNu?yEyKCw6TYTPs;`;$O zc8}d_>YQ`tnz@NmQ<1|!B}RpTfx%Fam)3xRfg^)~f#pC(guX-BP)Q2?1M8+CCkazM zPI>@+0dFIrECB;kn}GIYjsSg);w-Q41_OhJ^ZEljy%O*QeUr*vM$cW!)ymxcBglzT z>!YI;3^yk?ry%EBL2gb;E`C8S9zkC2E}A0f@t*Js(h^$UCMTU}essDXyX{_N9?d*d zNx~T@8{DmITm1AuYJqTmanccNOHR(uuv;)0^1u4y!9}wn3P?-R)9}YYbT+zAL&DQI zL7aohdc?@^=(TULT{CDcKYC*%XSJlh4|nE4saoS8y4{*t_402ocRx%~5gC7!-+S}| z&DzgzY0Z|^cNy59R3TG^VoUyi`c>qy?<|jefPJ%lT#6nZ%#?96Q|brs`i53G8xEO0 za7K05r@6fN_-a#P&ZmNqL;~tyi{&_PX5b&^QvA@tJd!h*xpL)uSE)~5Kl>y7NTdEu z{hpKd)e8qeJhIT4C}cEmI@yi<#EtJw+{fQbZHX)9c0iP}u?GG8KmK{@4}uI56_NBC zxEwSz7pg6Tihie!7fZu|;c#L6IZ3KMXSP@A4e-&Jk`|G}Kpi?>cG=q&4ERC$mQpf! z*?>2TXKGouhQQE|5~0Hvl}x7Lc>kjC5hfaQdY)E#(3_fR0nrIf!AeHZbSOylAdUeHTGz`Q#j;PRZ-tjCh zz&QJ8QzPo2XkR2HXB&a+4A+$TY+Xu*_kP;fewXhr%2h_s#Y(_AO+_nN|wU zvHD>i0f5SlF!fy1wlIf#`w#Y4EFFuzeoaHm0ji8hw^-$g)#l zx^AVeOK7kJj0Zhm;cVUnX4Rb5WC7u;m#`&?p>Db&tzI;&AV+7g$Gu{i;;uoI#>@;+ zj*iU)dU&YQ2;-56c&TCVDUn_>Ot`_<8MHj{9BNIcY?%^~hJV7^#Te7b#b zk8EbMl8AjNwvJn!%mR3jM^2V)t|Aw*gHjh@J;9zTh$2kF4sRQJ^{fmW4O%t2AUa(U zZ(b9ON&xDkoWgJbc@&2CJ7UcIU>m9{>-T)qe`WASXXAIRc6j7&{8SUE-UXX;I*(cT zX}+-PC;a!-Mrim%A}%Gp?PSpxT`FdyF@;GmqTaaVg$78(u;zkq-c+=nxYyk1O?fcMrS3rL&OJ_<%D_KH# zG8u!a{M#14rD5<@ioW- zavfQylSEAb8IXn>`}GZ-01?!d+?j(5wbvgNilEEL1#uujUr99ZR3ea^`t23R&zjIn zLCkpl)R{!?>i+)tYb_HaMrq4?k3&83?Ojq*J+u8+Oii1k*^99KGTVg;ZT(~&aG;r2SP|!uu{AT+tIfc$D z%mYP6l3g{^X6y;aBeiIb_=UtPdhMZd#(J{?<`1hKWF^fq3y%I|rlvpIq0 zN>R3iGrI|kX0x9~{Xc0KP-!@nn5>c+)1O=8+S*pFZDY0_B5VKDVLop2pjUh*29>5k z3?lQe4IdgD?E5j|TyxTVCg6H%={n`|EE6(=b%PrCK}>u~A`_83Xp(Z34s+ns^7L?bY5(MxlYCxg z^Me+Tv87`d?)d8`v4?5 zJmvV-Z_a6Z?}|Ok1!)ajCtQPd(c#0U7`&xql=0I#v)lu|x!`iUOFOmJY=>sfFnL5l z$XgkpiU;@CO3B#4Sv6-o?w~+b z?89r7>Xnd_Q5d!y#-1{KBy)}-tPm0FkTT-8Oz)&^(W53miAz@KhKUec+*0epn|y8j z4%b^9{&=U{1_Js=vB3BtC*U zpTZrAP@>~y$U<`!U0xo&;Y!>xy!yG=8WYdPy&Vn3)uJU-nKlu2{8t`})r?)fc}kMc_R=Lm-P+pnnu*Cs2rF}nG> za27Oteu~G7WFT2SU#1C3(ckA_tNCp|G8N)YDCmIbhx%QPSm@|`*ElcPx2x|CN5GBL zl9b|*CQz&~er?@(KSrpGh|eZlvaBy!9k183#iL&L_JR<}D9tN_%HKlEc33S%hRkK{ zIsdWKtU?8iOeY|t4`GJNWV44eZA*Mi-U4s0Nw|?M++oJWOzx8VlaP)(J`T7)Yy;P3Z;5D@=fZ~p1DfegM-y^ZUC-m2}Ah^Z; zL=oM{`&}k#zH3f;-Ze{PY`70o0tv)v|HVR(6fvYNEyTK6-M!WRMW+m3poB&Bskl9J zH0VB1by?6c^(csRl?@58Ynt$Rp>|M^;I)+>UWK~sn0mHfT@FpVN)le&T8vy$X&~G_ zW2{u+K32w{>rWVPItya;C(%+yb%(^NIJ}{br>>K-RnXeD-8?#fZE)uZ;`K|9Z3v+z zZUvwj)k}G&B(63-LJTjfGd_Hu+}%bm`=}D5ZH!Foqod-a=Pvo_m}F#;%MIgF$^5MC1y6(S2( z(s%TcVM8@%@!mkWP=eH=&^&~&Ko*R8LWHe`H8Ew(}Q))wxmKmi$5?;^MbJGMPM@{;~L4eopjLWC9yFo%LzB z*2`0PE#kGZcVfvPeau*o#O7%@{cuTohUkNpYwz)<-8d(V^Uq@L!i0Jtxd)i1?(TGC zF&{1V-swY8h)N^|ANtE%TER{6PmB{P#1%Hd(&q~|TmrH zNMffPj6 zN-yeH?~BJ0*xt=%0$;uQ+Kc+XXX6MfB6F7kSR*e)QndTlUD3G+g@7EAFB*X z`wJI@Y0wMlpu1|li1S^oqrM%IzUI=S-Qq}L5`{WAu`9Ho5NqU|^XE|+IMlHCGd=&n z_ez$8e)b938|Sa#ZhYG-M`?kDnH|5vQ|!$SiK8?xwOmUe}V zcdqea+j-kZ?lY}S1hB;R8g(@;9K!rrVMDyBwOE8#_pEkyvo^VT%kibi!L1|>LMA2H zP|5NeO8ndGe1p{yg_!zX`3)kDeIEe`m}O$Dml2NqaY1uFm5a|vW7!Onp%KPRWtS|3 zyc==e+XGYi)Aw*_t~uYzZvjgs>PMr$dD8wuMA#+@UxO`wCNVW-GjoNk*T^1hDg3ou zK?y-_{iBP&F2D4-{KuALF|#k$x&y-=Bl{L>@7Uf^gAjd~nhjJ(FHRIQJ*95vPk5k& zKI&@UEKT&aZZJA=vSdTs8I2vd(DFTMB5QjI+JLjC&3>=Nrnl2G6tSyHx_IKOF&L|= zs~Bx&K~joCOO!hm6s=U+p#u-c&i;uYF{s}s<90)DrGAAX9~6qA`Z>mlXaR4(x<>-i zmuh5L5adXKH{9Y8!EE3qGAEE$)4X%(WJa>rd%MDKGPcrh2GzMf|0`(jTb`P+*BAmr zB(g_wudVZ!Fjn6FLWcN+iqE*T`~H4f)^x(WP`@6(#168P6zj6LH`ZdpP_!QXLYBy? z&tFh>T8reojLiW*oSc~>g+MhL77Z&XRK*VQ(*qw)IdfYW64?}$ypvmckEZL3a7FAC zCkB@g?ni~_7I$tdz35Sxm|iv4RU-#43z&px>64MB#qhIJqx2_j+rW=p!novB8QV3` zm5X&At*O{M5>}#S{aINIXaN=KVCC@qPMi3B^sh+qF$zAw1IXG9E4U92S!={OkTJcM z6|+ZiWaC_Kq`cl6supTjZ~{mJt$^sBQ5crT-~4WPp`GqAT~g8zpDHQE++Z#q*C#hJ zZ|`%Y{sDmn0XZGEm<0oGyKDiX;af(r-Cs;q38#^9wpmWgP9#<(x&DvTo#g-Q4K655 z;8d!Yr}RSf%yGuH>=rzuE|3a_%OR@3hrBJ=a#TRms$0o-8~i!kMD-2_wA^q0bMniD z@_oJ54OXR*x6)CVY7MNqb5Nk!w63QHi-@`a1JvaM6H{$p$6CkMAg6883%aWT?pp9` z!A1+5+oQj}oP;ekdfqRCdEI#b)1vEBLE(h6g=E?93@U92MuJr{UA{6yIWf{-W@6ki z1CGd9qc3Q*?KT(be)Nuh9p*JFDfSd-ac>imMb(0lq~Q$~{^&mmx0>@?Y(IEMj3!@) z8~fj%3`sxfU!is@!9>F>eny(vR$Z$dCa8WT2oX#fq_Q_TFfrH#!~NLmMG7lDLfi-R zu?E=Ynzx3B1|z5Oh%$0y7M6zeU&hmr2p;rpi!O%SSB0yqQjcm%rpNPZ9}aCg8buS3 z?zY!y($NXP0Wy ziXRWJMCMd_d7Lvmu1)JGM@N<&#ocCLbz^|`Rf~v5#0TeP5?Mt~8$oM?3Ku84008nJ zN&4LA`0z*c4qT@wjey1Or&0P298+e#@OJW3X0n{ zuUJiIygf)WdrsTtUHjgZM4;aITE|yqcjn0R@*Qh-zO+De=DWD)zrxjZlZM!r1C5cBo{@@)+KMYI`!5>Nu(?IO+ z)kbsnXObu9?ZqBbrm>yNVu5hAM$Aw-5zPU4{zV+}m&U@a@=6tBk+nZ3|L!6}YVO#; zmqy$nH-lJ3TvCgzJx_jQJKhlz)DTC@neCw9?57R>aUca7m2#_x2engjb8P9hdvvMs ze^ldCeC-Y-+b~4P{9`d5j!Owhu`%B{+`L2BE4Ed0=C8$ps)V5XVOg(T*m-+x$49gfOw#Mu==2Gk7A@zD`ZMq!$Lj^D!C;djB|I7At04VF`L%A~`(|tE z0h<`cUoag4(_i{eM;~+_!5i2)&HbrGqDxzd&Nnv<@5*F6Qj2zuUF3MGR!h$v@UYL2 zE5gu7Gn@P`$}`E)c-_8C8X0(`zOvVdu<8RyFRoW6m9s2EYJ7+I5&q3SU&~ePkVGUN zgr-j+;=Ro-Pmf`eksSJ+GRRJphZTiQdn8YAW4U~%!`g8o_iJ{8K?Ieyd6G-v&m<{1 z$m6hh%_iIqB(rkMr$2tq>qn+CfISUs)!n9F`RElsp@}z=Q?>IFdfGT!28HDcK%GW= z0m-_)smzw`GXv!}ptkxl zm4kmykoV2sU+7;*_^A@TUwZh%5ZyIYzBmF1wzJkHN6H-fbqeF0W+UyaycNEc0oC+d6tizsC{-On6!YFJF7`Xer7QllqKW^apZ}Rp42Pm;M4g&Eh+@@h$BvzK6Qxy}_59}?001k^1}yM% z`WW@|@BR_Sj*nd_G+N^Z_4Iu6$cHf+dR^_q9<}m`25U*k!~=0x94>@nTSq#mo$kO>%RP zuE?+@QE!J?U=US&`8tX0#^h(;tfXk;!%F_hnK?LXsp7!jyAht%tMM8+?CAtG_hT-j z_%KCe^oqG_j-O}a?UALa!qrwLvMS*cXu5NlZ3CbT%z=>XWDkwxnZHnw<&3PzEgI6O8zD_X{Q$WMS?BqGjViS0B_N>w2VVb zUBJ1#&l`T6p~ks!p3P82MjdJI_oD2D3Wf}C$Fof}Wknn&QdRwL;hL!yvtQ2(J*Ht4 zyumIriH|iINc9XZB2VHjYA|+QMP&jK7dMRwm}?K-P8oVn^Kk$5ukn?8s12&$!H*H; z|A2QyVyT(%Z{{0%to(Y&oEzEQ?B32~xnR^%`3`H6hK{xDA!$4cl9ddypQ?`DED|U; zxy`baA_{-)j|YG>rh)cSD z8nF4ds-p5GkB%34li9J7if%GFR%^LHlDufx+Qcr+yz8M>Ua6Ep-NV?4M;G5tee^J@ zVttMG;&r9 z3|%2&|FUv8C$zv4%S?4VettiVhIbpq1QUR+iBAOsFej8QD)OO7xh9~5H|j02jdn9> zOWSI{9miBECSu_K@CV!Pn1{RNvd}=I*s7MRLhb--*Zme+^CynsoYyX{QAV)KSCXI?SL?PtM$g}Y} zWQT&$pHa|}pd?Kb{Mxs^2E$H>w6A|}WY;i0@e*g=p%rBT31ptgqZ zu(sfM77TZgug72=(GVwyi5?ScZ%@x5_yqv@pXmsAWFql8)gVkHbPoVL%SRJ5Usumz zW)Y%WgJEZZiUf?ZcZ7s*xR;4L4o=Fbcc6&$A7}Jfh{9mn;QD11rkyR|u5*)bmF-Ny zIQUa+L)nYR<)5-eSb6*V=9BNMf&1(Sf)E35B9llrM6$<4N(B;`7rmMkgTO~PFY!q& zFZj~;K6?1lakV_!OMG^hNTz`{b}jSGftPnJ$nk}x-CN*V&&w!Ko6u6$Kh}`E!GP)< z%Y~Y+nh$gi7tI_|zBRDrB|}T<^Qq7`!K|7wdXH?@!t1T+!=kZMzqj|HSi)fq|L2Tg zI|Y_%dzY9+uEe=#2j4%!`0!V1YO1gW-qk83fJbdR0i;|mw~;Hl{kgrOgP^Oi?7CUZ zd8uLelb%0Qluq5|>tx-Q`nD98hZ`fwqEdh2&~OVBaLF&(Tz+eQ^3-jC^y%NR{s=O1 z2^u=zM(xd|8A!E?Q=KoI8pz{12pH=y4e}K6;dMTSI)zF0L`7Di@pO>VX`^Ot#N7nB zIBJRzU^CSvWQ^Xd*epDmhim_<`&VR8i$~)b#ekGM^*`!Z^4>`)$k}%s&U&Ty`@m*Zs8@k=!cHf+onE*Mn8aI#XllGP=X>A5HeXV?hqROGIvX= zfR!5Ea{lFBbIt2_Hd;bElZee5Hnov(sFxvpyti=v4)WP~^%5DLKQiBQM4V-AR5`2> zH#$Cl?fM@BbU`n1NVNu3Ov_+=$$k-$v_h3poAFk9(3VL|9XFJq|GIIOo}q?@;?<smP#M&Q~&gsm^kI=B>w!eC=z>rLUeAlt4Mq zW|>Khoa#@^DCP{8Axebn=hz2Kv3-ZhDB_H_a}K_{@5>p&{>#KbsD0=hs8y&K{#>ig zb$!K$8)v7T{zgn>$eGR*b4s_ar2Nj-(*>rOL}}&K-X=`i;=(?gcP%qmounH#RoSik z3l?^7gkO6@GssPTVyVx`o_Dt$FS>C|*1xuO^0#Ltp6DCl--LbNq8M3eUl`xY_wXDtJJOjwjz-1_us}0mAhhZ9L9( zv1~A*%~CJrfOrEIh;k){`ueLbEyQ5h?5M!hN7v53>Zgj_r&3VLN>_@>>lGywn;NPI zPW5a#vsAa$vw@ytcA6!F=|KAR6+EO+Y7o&RY1n>)C6$@73*2`tD!LI*guwE_Tm2u& z%LhW|8d8z9r@~}O`5cn@botu0VTw&6wKUd6ckhg)p#x!T(peKgvPpN*&*k`Z33boM zzutR{g8#l98B$OvcXx{9B&FG^9o_|b)>0i>>pH7SUIYI%)cw#B>KEtiscgie#qEx; zlxFdSHjNOcE|IXqsIf>~pE)-nyN-|2!6@N>Z#gr%<52s$v7rhLg<69u`@^8hRbk1H z@LQ;u|LbPWpaQA*G<4eTur_3m+l>Lei7ho7N#>hB8i31G6Yn+5-z6klgu;7xG{$Wu zj$#ANG?>UDi=7|KwGJcTAq)z=L%*1HpDs0y?h0J3!xGOVP)j7&a<(W-!X@j`(YMNUkb!xc2Gk> zK-ZaC!RVm$k{5Zm@P2Fp*)ZSL-!0TaR!8-WVSwY2r`A&Z`J`lZ)Y=tS-2E*MalL6! z-mnmb%wgi`g^l@0L1an3G2TT39zf-PhH$J`OfgHX5ntiuWKK{Kylh10W9YKSCg~)M%~?%c(S^tdS~=SW4n4SUj@F5bAsEm9}v3qZK>! z9_0>8hN(b!zjXUI8W~muQHlasyqkL$-fq_fbFJHb!Gh*G8*d?u$9&Y^?e6tU8X_ik z@kK$^zQJ{;=Y~VzW_-pT+VHIT7!=Z#I~z!kCN(&h{qpJrt9XjWxah9^shZk4BO0{L z{nXqN2#6)v`|X5X%)s?I8O#;{s{^5{ z`f4whI75euRbQlBzKYbs38hwgpd+%;7%LpbJm&oOWW3fP;?^2S*ICQE>?bGlKs;6G zv<6xrvCdC!J9(!`ROBOT1Fx~V4^A`5qcpo+{FK@M00(R1wQ{@n_g8okq9fx4s;(Ux zTmU>rY2>~(%0ZAu+~#;6S+QUjQn+));+ScJfD&yJ>Y(|fcN=WEwmVh7cw(({wI9x)0N z(<~IRq?hsZHOa$E!=m>Fz_+qpBV+b_bMwujW)q+F{gnlKtG9X^(+bV0X*x#1w%B)` z{M=XBwV-1ipCSL|f_doBm_nwEr%Y1vOS{U8XVck%mbqD??q>MLjj~ULcDa_~+eP~( z4zU+<`Xs_a)5lX=Y4PN1vJr z5GHD~b2Et%qMp@_CFOIWouAc!lPYa^$?X)??z7r{qcdVR#IdcUpWv)l-vB?w# zRORRf6ien(<4$P93mLOcThB!dQ-4-B6s(tG#O`-|$9=*+yQNs`$SEsqp3s-2LMJ3< z>O?#Bh#X@5!$cYrdNwsS1-x6835M;rer)Y+?DKq)K<%J?#7s-wLK8)yGvSz&;$z?g_o-py4#7;3u+fFVu-dLs&a~_7{3_U9 z+f{IM2L$#i=TH10fdl5Oe!2{R;2qlZRK{@oQ4aB!p++!_?h@|-HF z_%YIB;k@M;oGMWIgBEfbbY@qvx~G%?SaS~<6#LI*2mk+Z9Gr;Un* zWPJcx6_a7COMz%ObL2BA$X7{L@wa&39IK$~DH>sEc|V~A3ZIFI_Af=vz1Ja;v8Ytq z(B#c1H$qKbbxoD9*TP0n0d8aS_QyCXjVPke%fh2d!@uNr)n;uJ7^9=Zo5b&@bNN{- zvgj5?cW|!!s=EX=D9U4W9K@Gw5N@s1ge8JBz&fjgA}LLbBh)pNNS`Rsw%y~6FkQ$QJAj3^YDeCiOr^&j)MSxCk`$;rji9D7rR7i9SEa#>{j1~4H{HYA_oB9+O8tT2H78in1Aq_X ziC%+#;fZQe)$x_<1Ja!B^mQwGFA-U(@kbMI9{qttP)ZK7u$C+OzEi%rK7Rf&7wHP5o`1dwiJ``lge_a3eJH&3Y$l8QUFyrbLdreIUh`kyt$@7_DUch;6jT^kbK6ow`sqrDKjOkZ=xDAiky%on0=oA##?-~>?-yW_ z&jx6@$k0y?$q`FT?3f$>94>u2HZ?YCJ#>tb67wGle#tO3ChhX+@?4|n0Sb{4^3>_? zy3b(6=66td0^j)F#|6yQ_9q8@mdTmQ+>xPLL7L}K(%ajyz@s{Q?6r2ijM_`w@X4p? z?v%W`4FBxKA>%KbSJ-cxy-yVEhW$%Qvp4%qut0#Yeifmthg!5B=zJP!NotMZ!lN+Q zS&$X9D-f+SZs#bJk)~InQZ*gzMqzB@P%$U!Ec)HAJCyTG0qQe(Bj7elRL^7rPJewb zME+-91L~rdHpgC(Ii(6*S-cjQuf8wE9y;r(6V)KbqU$L3}l&@=U|K1;6` zFSpMhZG(H^o_nQm?URzPsKOy;Mq4GQ6VnZQAD_hjc6~@Wb_fW`!7GLU?BV%uE2`a!0mg1L3YHY+_ zvB(rwE={a!Qn(Sivnp!xaNmm|$_XGVG32w=+s)<4rKzO`O?R^uRyfjIJslG@p~%=S z&o7k|MjS3%#CvvP;BYgo%_`3%U-Vp&fSPx1rgKtHPkg?o7edf-9~qO{dBu&?4U0`8 zQ{%?w+_jFc;A10Ck`L7+X`ULQX!P>MtArsU!h@RYBDR^`n)dTpclLv#@paQx+x4dt zgdx(%4Ex=+p}7iTBr#8LmHtM4`VQ~DOkDs>bf}KZb`)-O{3`v#`H=t0erLAL2lqc? zwez3yiT=JE6ZQs8Ake&JDn0)yV@kQNF2a1Vr}lKY)zR>{82rfR(~o(kQMgBuOSp;} z8P2~zF3Kb55gWK$YNpRNff@^PsEKTmL9=OW%J$39frdFao;oJ@Qdh}gJ#(|Zn2h8U zmQ~f~%DH>yFf6wL7Mg}Qgoup#(&3_qdpm*r09*{W!?}=Tl8twUEsFt&STC&o=*Pv) zoOiY3uDbJ<=({K>omV>5rXxJxAOlitpAp?YQ|B8$t3cZ8O~(n*cZZz^-0)?sXO6%S zotFhDg~=-}_-_4?w@?|rTD=? zUw3{M>&5D|4&_fwbh$iS)`K#8z4_belhR%2eCvz&!R9)5fE5;VovHx1Y;6iL!8l-5 zk=wTMnQS%1+=vhEQYm0wbzp73?|jtvqL#2{>1E$-gZ$G%=^hxjCdRkYSe7rIiwTdI ziv7DY#s%2E^Tz}Yci&dHps25Uf*eE8x_nn?h_`K#DUlHBH1GBsLP&Oz?ktDJ8c zd8FD%kOGAqWqV+_sLuOhTprF_+TO5Sxarl@&czIv{_JT-lxi^fZt`?L!w!<09-E3> zk3nNK5(7^BLY70u2cBN6<;#0tq26`4^afcD?m68&#!tjfkR#=!nEdTrsei3+=_g54 zxi$ksZcJ)oXsihldR7J6lO5F+Ai2I)UX*ysg}_y=#I~Lgjw3lZM1O9l=J`)l$=BAE zfGv8C0p_zRz5`LPtb|Abjq1W~)K||pG{_+3C4Ga9-d=&&K%2TQQ{OOo=k@xxzmGw} zq}ZEzvl9fsI+v*=!T>!qKhuJV7+;Z(oP2PnwJqn7F|AI)M=(^_fT$v!mG+U#^ks^V z(7Bv3?b|Ca^m|rnDT-OOI>~&a?Cghh#S%x0@ENHO-#@@wvW%@Cz5@@<4P&tma~oW2 zsU=J)3aaMpm+r;6nykp4)#`jY<3w-r{Gt;XzfH1iy_$H7I%W75y5;S8#?7`AHau+Q zi@^?*?VEIzrj(Z@BRG)pWZ3zp&}7bt;T@6yy^GpGt?D1}b&yp@dp5iO&^UUj9n+GoK<-si=CS(cS|sATDk}Sbp!={d7w$)( zM}Dig?M15x;L$g0Whu?lT%3)18R7RmuoVD{(cbkQ{6I2aO>mRt3p z`=kleyHyhu;j4)A9AhnP7BdZxwxwTAyR!g*(PPk#unL9HsE?g1r_3E6=`;=|DE<{e5E1SNU2 z-hRZEna7 zXI+0G*0$#%yJg;d*=y-;@J#Wkxma`U;x6EP`QS^MjVZKMkje3X+#FTyc68Za^kUtw zSZ(4b9IuW7-j|pi%b7&Ir^^3%;2oK&BPepPe2Bq7TO_}7;tHlTmM0}m|58P+ZV=6s zU=n!ik4JW5&T+kxWP@%2c8}pTm^5@CxS;Tu&e|qI*S}StzLO7j)Z-+hH{tV=SWq z<`72rrE4hD@Bh?52n>(g-QDiAI+-Dvd=}>xwh{wsonU`;0m0|E#N{vm$1fh67bi<~ z&?;FsAN%&s8pPvr-AI*6Ojv0rT=+VK9|@rAILYnCPl-L8Xk|wo)IoRO&;3G`?08)E zeADPp{np+{%kZYf@@}H1px%Fc zdw4R8)5IywC>Sj@rH;O=dEqv@KbCsugG~yaxDw7Z?k4{{=*1@^#~{YHfA#W!2o)b3 z84+hkl@zajlTtyG8sc%~+)~O!$v;|tRjT2m^U-Z{Ilo?MgL;}A(pPzo_$@0Z+=Tmz z>Qfuwjd*nO?U&}c=M)BqEPuCUP>*;5$-(1>(C=!#bp?=Ir!3#$Q0_z(_Rz^gWuyFQ z#qT$~0bN4(trAA=$I#T|f$oFJRHWov0#)P}u0*+|li)XLS?8Lh-BVJD2VYq3sWYG5 z8wdx!>^54@{^}BHWAe6>;PVE-^;3m&fO@ky*O>F^3Ef**z%!Sc2Wz?5sHQ{Zw_h<-@#r-mfx zUFr!a>FrJo3|YYu`goOzOx-?$i!x%L?x75LNkWIEx*wR(=LnO(A?9q`;_yH8?;5}z zzAfNCZQK)Dgg(K045+jT*7;_3rNkxJDd&pa=o*%<;X|9gM9R^<_pCki=_K&!7vJxt zDU&)uj`2yu6f9awa85_+{CNo+8AV!uVb@I{|U zE7(Jw*xq0Lp;V|72J5MTOnW^}FKE8> zzO$sDBri5YtKn;syMRF{sF@*as{s?ENh z9FnTjG}i&=YoEMDylhg2E59AVolII7#vxmfEs4u}mVWXLwL$9UI~bV2KOGU~irq_;haNNFnwfo$$aB;>tx0M>E!AwiTvZn|ur47@e0YSR)C;YVU<3WwrP0kCSh1q-#A3*RKbD{_85O1itd$cjh;25=Yt z?smQZS?!mUEN7|&lH2nJ^B)s{v0hYwA>hXzE=ZLlC>h^1*q$)Z@M}@(2JU?c>i^V!f-~+-B8ZcvjpIa1z5|dnmcZ;Vgh$Y2uVivRN7B9jdDFj*UT1% z!Z1YTnpbb6T7KNFoOb-UySeCGM~f$bUh(1l4&(V$lxlN3w&F^3LB3n={Jw;PDQVPF zYbYqiI?zJRsnc#U!g39#D*N4?Ps4`TLroXRnmYczt(I7?xI(J(y{ueV5!w1s4H;+> z@`)tTI81wac+@y*6Xlhx1w^4(c3g2XdaD~fpXVy8V&67)6Oe*h9Dfj0 zwuuXqw`*>T91Gne3Z9*i#5c~zr3aKG@s(zG3_T!3wixskYUbX3e-Cc>dOW>kg*gN6sTnvkx3`*KHZ!Mv(5c@5D+mH#astJ+=y6!P?{@{t-o;$x zHLZLNm_V|NM%i}0`{N70HO1BKbOHD5$+40Dm$27X`8=_MALvNpeK&`@7DJI}y}AnB zd$AV6P5!AU8ItF9)vv7T|A&av04{eO)VJ0*+pxBb_W5kE3~ zg0bJZ#h9nccpvY-jv&`Q4u(ARFZlX!W-8o`ZS{yOtJFx63jaMPjs2w8FA$yEt|RuC zoQuCn50P$!2%#4Wv44#=iQyl){9SItDLt5rSctitaJO7_?n9rOyzoxciy?6A0nta$ z2Zn38xv-0G`S%Z@Nt}rw2YZ@xgBE{zgx?vOI#a;ydu!&N+7uc}nxNr_;wGuj?_3g)- zS~_$m$tpPqY`(!>`uVWJh#Fw}xvFZz&8E|x`As~>y#;+hfIA(>4ewA7 zu$PXQk^tfju8?MDz7N=5x&0yeEjX}CFeS6XMLe7AI<=K}XLs$+%(Al8K9BAEVoec!R;FeaQ70u+Lt`&`y@n+gu z`?VkE&$4RNoYhj=!-JU1gnHY1S-Nt|bOYPZ?{j0mhV9%Udu>8ODpYO%gn-eN&_WPL zym~Q_u+`f*F2oMK$1i>sKc1pcmPUgpV1b>Qgh*@s{v82d*iTm^_vM$fXvP?$)e*d(2PLh@@5+&&MPj43D_ z#`3EN^~pan;RwaqD_iFA^elv)%XdxoxKZLzaJrg*-;KidhEYQrqzd@-J%F2>;_>6) z@X1^L@w-kSpAnTjHP@sH<@o1KP8@8`)%t3*FB|EFM*s$#>|aJ`ez{wFNWqkCrk{sG zeW?Ub_f1CLEXAX-e>#!!7MC`AehVAC7Hn(ULa}vg?mlrz^xGQxgaM3c#&_`2I#Zs( zjPG62j~~Bf7r_jCv46hwCONAsOS@}D=rJ`ZHp=xRcnSsXpMM|cW5qAbjWHo>h1Ig$W)1!t4{}HgWi;B{T`g@V#sz2NTj%(T*5o~6>NiWlFi~eT z%-F4$*Ym}BueG}U-ji_x_ybEF@{9R^&H3-{id6R7U3mn* zX0zCWd~EbA-k;wC8bm@z-H)tf>SuhW-fT6JWZg9U@DS!{l#Sk@o{}J^`*bz) z2Si3uuM0~p%=}+-U-=g07p?sxq*DZkE>S{S>5xWJx^w6T0Rai=8cG>DM7q067#isg z>28MZ_ZiPQ|HJ!*FT-{1{j9xeuXV3`H^=Z@x9~Z(X2{f(1JyO8QB^4Dc43&_-_P_o z@m{sJ6{lQJ(lB%`8W8Dnem&Un8@iR?(&S3%r;PfaW7N%>aaY+AjxxtT+;Ahqo{#&U zK>Srem;~`H$0B96>M!?A9^Bh#c3<_6EDdebw9#{9tk1kw{7-5I6-{t&-Kz!`8D=jI zT1f%>)%s?hwDwf>U6)_+Rft@wYW7xQ$c8;|LF*B0?4g!(W4s>>Q zcL*Wl8nl}5b`~l$LP3_SS&R%n@Tkk~rlj=%yU;&CLu zCz4j85}tPkKPl`A2~movHow5ZzeeBLMZnpR*DyzLp6GLE0JuRX3vimczZ!KlnA7(g z>epx2;MPIvCQoI3NTfjE2NsB>8DV7;*x97{_y0j*(HFl*@gs+o*QCo=sfE1GJams& zql}?tc$KO*w3=6o@V)*o%ibH4!tK{KUq^;yxb*t}jgl}DP^eTF%X&N1T)Yy$#RKYx zznHt^dbp)}`?Jd_-|vkwl^(*{aP+2H4sXb4HMzJK7EBJmPu)5(s^=};%oiq{zek`r z&M^qcPes_~r{F}ich3Qk%de6LXwjApl!`x@_SJx#vTiAwiqRu}Lj3ag=`sRSa|O3B zD3{GVs{$3Gh>p??$@Cpj!t%kL9t{@m5Lz zuXH%jK*aP~uxmh&mxKPIC*W{_(~V8|>Q~O6G){{_OuXC#{IvJUrK&e_fD>RUMMp>5 zIRrFqTnC`%hkv5ui_PWUe99666aIvy05@O8d| zmnf-pauESb@l@Yeqf9DO^BhS-LM@}*$N=yaYv%1H{#)Gk&9)!6iN}KcK6ak`Qxix> z*Q4+dim$T=jurUHdH-a{?tfB{FJJhEkixAJ55TSC0%j2yHfBg{YF;BpOzG%x>{tpi zhi#nw9=(DpI$Fymf@#$ly-qSxjne11rZ5R{zx;jI>OQldcbHzDF7R-9u;sK}B*ZR< zKVTs32;etj4Us>t@XFt8tNKdGl|J&&C{-MoG8GJVIg6(NO;PkQS%O3p8l0avlPh;i z*Lx5Gmf9D*=jQKfk1DU&9ks#r`ohV0)7;6w)+@Kmg8 zrJ`{EwDI5Sq18ufgzd;;`^SDz<%7R-{q?)r-~8{nLs!i9S&~?vAp%F|jJY-~-T1=f zR5duL{QNsNT!llT81}}YpMXhjM9MJEc){zS46@}U#%O_j96Swfzf38dME$Z~Oj`ff zikeOxV!0iV8&s1SXW8!Ly^(Lo#|{7U@=w(Lq#L&A z^NWU0|2OF@9Ferg;w)j5$KCQ0$c+RvO0|wy88ZXj>kA;{tX!W@= zbp3oiJz@3kq!-yEH0FDkg|1oRvfL1%{id-&DP!3=kk7Q6rruq}_+=2|hEDO6N+H+l zt5O#DUq0qDizv9#0R72ozWxpJU3{i@$NUdJ_zicWp(m@b3qnNXKpar1;jLJKc)zf$ zE$&$E!>Cl9eyhHzsx1tireJ&CSvuuk5NBPV!TG})bD54V_Cp&%MAS*rH64Ml2f9QCI>Qc2`aM~ZCw8>nGH?Y18K9C^RdR?=H^5e!PnVuYNlcg zZz0>|`d-h35nff61xUhF9CTWa<3We18a>}Xj!eY{PnBwa57wsDS~r$H ztJ|#7J)xz`ae1W~pGn2VAifzN9yBm4GTB#tpXvw8aT$L{KjPi7u~TjM?d!p-An6U2 z;^*V4YvMv}i_i)sbAh*VirUJe-u=WQdd*2|2(-2}Cg8sy7=gJqU?$jC|KXc>UFdV} zJi8+QV}Y{;>m{B=8R@ge6ZgJHlw)-K8nZ@` zJt&?71y$qg;zBD#EHFc?OQEMPVjpD?MJ@qY2l9%^6S zThG348LW^_62}#KcpOG-F5{4Td!wG^as}_5hHz?RB9)rAZfca9+G^%-(D9AhozbhJ z*?KW7*8E9SJ%$&3B^vD9>~O8or`F6c6cmEDoD;|(*3SVP%f3|&1u;n6q2OZm%ipvy z{nFE|A_zli@6mwlHA&~Km`c^d9FAjeIqTD9z$A9cnN){D@yjYNN>M$sq|4Hx0d!8e zp{3gaE%*>;zxjsT8$PAAHfIL+<7X2l)=r)&ppOy6H=A>j5|YORTkT0|p4E|*N(Z~CWcI7b?HDm3cckDQhBRLB=Re0ZwPJXoC}8HaqxrHeD=TP3_RIMcYX& z3{o>{#FVKl8&F^is;66B`02U83}#|6+l!ynQv%5+FGW|0_o-Vc^uLQZPE>29#A8AJ zRpnD(=BQy2aJ|+m+D4aYPIUoBTm2dK>pfP?<5m@mQH2>a1LG*S=Rjiuv2%JJ$Q?L2Z7Zbh``_+_r zX0*I=kvQ3%p+qeiPd+taP)uP0k@p0dNdS{u*Hg>7hLzI|POo2p4-)iZky|Kv&KVol za2N!g@6^4nCrC#ED=<@ca3iNb>tsX{QQv`3yDXq$1s7eZ4u6yg{GJ}DGwY=7n|%6Q zE0hJVXZ@>f)m~8fbpOaay?@qttp>fLc`G>4(W(I~&X@jL8r~&0@1j-lR-Z}%-Hmy| zWX!9=wXI7D1wr9~1C-W4n^jaVC4A-*O{0e2y9zPTb*jH{ptoLREkU}Sj4cvNf+}Q( znZMU&-hO9qd;hU;{}w%*6kP4tz-h4ZEpQtz`mPTHG-&;7~vWFl;9l68*w#nD;%FMj9Qh2m?= zfXrE3J~zyI5rA}WZ@Xjs-qj|L^0uuJ|CrxHXW4J4Jgnl%zWVF;#Kc>Tc49;EB91nj zJ_+xSVExDtP=&mP@9kOh1|A5et$ezD)kAcpXoq0xm;`7glmrtU{m$@jgctyQQ~jBr zjo)BwD{8K&`B?&2YOMGiQB5YX$w_D5}ZP%@O@Z0{(juMggqcx>C`gBb!{%-HB+?crk z>^)>&<8xOf?nR^3l6fAi%>B7o!5qsVy;3CjMNAr0RVuc*3uygAcZ|q`?jpVny@cpc zEPh^db&Da8d?Z{k0@u3l$N=}iNm-2Vi=j!)_l+ze<70Y8$xD)l;W0iqH#GZzva5m} z`#utgI&F>SsMHNq(~HTQpK8Hhud!qhZ}Bu{zDDb z*PM}qHp}GXkitDfd;uNn6o3L@(z;QB13er#>5u-uvNJ2(Ut7N=&E*RICg5tUT7+hz z$+Zh%$+f7yozT`d5INLi$4QJJ4KlTA+sfs&z)IMhLgy*r7u*w|?DFEmmjX@d3SSq6 zC+6}j%X_85=!ql}^-o*gH)i10t7^Drdqkj zbd3J#aXY+ph}*#;;ORE1{PB=j*>~QrUiU%YI78}AZ9Va6R;`}uW^0CbQ5~13{i2jR zJJAqM4PkBluUD(J&G%6)^FBVZ?XuJ{=XK4GrS)-^a!Spjdvv*d7T6t4OHo7F>V=yh z3ViQ;%cWW4@vhX4P#JIz=S;wiFsX&HaR;cD*sq!eTrC55)mR#bSQ@tVN?ur?C0_c_ zyL-`-uhd4B^HgF!T4sjYN^Q3cyh_b-Oh3DsmD9-+*Yy<3%1bi5y4!u{dZLJSKOW|M zY1km0f!wwQ^idQx;hS|r(|3R7cv6~P8D?#k1tA=QHOz(n-yjOWWw*nvFG`V-qC}?F zGy8|PhE0ru@}}Rz%&xyr+c%@+;)NOT>_mGe z`#7UDjG4ssQPHb->W50~_EXWe8*~SxT}-?f2=2pgBK8M-7oTWTGPEZ0ruN~AWgPKs zObZ06wJSFAqx5YA4WExus!PB!h z*YKUDEl>!v7Dii&zJ%+zGMc7&%Ls9*_-tS%`N;k>MW4Cg7L~~duRika^vHcY zy7(gfHU;+WVucOKIx%>l zWclj%Lvlu>R{MrDA%2mZ5XiP3I+i_HTn6q?H{?I9V&GITs4g_N^dY2rxpT(dw~4Wp zb2k36?FHpav|h?G>Xt}%$41`T%X}@-))c01visk@z_jVn>P1@in<8kS;mPew-i%W; zew{H!^fQZirb4>Z>SgwPIF{-N_PL!sm-WKIsm$`T40>R{Hb4s!-*bY+uri1wgM%&P ze(QHu;JQxd`b$TT?hw!cJ!qLT50d`0a=ete%bDccA7!grJ-=>zm{+&ma~1?+AyBQQ zcMpKV?Cmm6hhxe~2&jnIy>}aUurhwYl9b58=mJROL;vW<*~j(p3Ar3rXmMe~8I!|? zzupV<)&4z)@HW=o%qh^`22uMRrD!zOT`~VJf7^*mtXBT%rp3bJ=F2&Lnt$H@v%n=i z9iK4{e_&rx+?nNYxJpv=#h;wX(qZVR&E*t({}IySt>x<<>DS?_e5R8fwT0o~3MdEb z#rS_ni=$CFua4{+^o&C2(I5QPq~P3|eotR!4m#ik7&+ZV;NN`G@dA69Q~71h?sU9% zk(21+g=|gtDg2u{ge6{`2BJ)3Zmv>6`$nShQLBuV|E6tb`aWE^b+r=8jvEWS#br~b z0>>Y(hTy*a!caorJA�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 Ws#CNmE8y|i|2|47NtTHl2mC))zcQcz diff --git a/example/android/.gitignore b/example/android/.gitignore deleted file mode 100644 index bc2100d..0000000 --- a/example/android/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java diff --git a/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt b/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt deleted file mode 100644 index 09d279f..0000000 --- a/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt +++ /dev/null @@ -1,12 +0,0 @@ -package fr.pointcheval.native_crypto_example - -import androidx.annotation.NonNull; -import io.flutter.embedding.android.FlutterActivity -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.plugins.GeneratedPluginRegistrant - -class MainActivity: FlutterActivity() { - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine); - } -} diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 00fa441..0000000 --- a/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - diff --git a/example/android/gradle.properties b/example/android/gradle.properties deleted file mode 100644 index 38c8d45..0000000 --- a/example/android/gradle.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true -android.useAndroidX=true -android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index dab3e5b..0000000 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Dec 18 22:37:36 CET 2020 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index 5a2f14f..0000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id deleted file mode 100644 index 8f1ef8b..0000000 --- a/example/ios/Flutter/.last_build_id +++ /dev/null @@ -1 +0,0 @@ -a1d04c54a9dc0dec55298921eadf7972 \ No newline at end of file diff --git a/example/ios/Podfile b/example/ios/Podfile deleted file mode 100644 index b30a428..0000000 --- a/example/ios/Podfile +++ /dev/null @@ -1,90 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def parse_KV_file(file, separator='=') - file_abs_path = File.expand_path(file) - if !File.exists? file_abs_path - return []; - end - generated_key_values = {} - skip_line_start_symbols = ["#", "/"] - File.foreach(file_abs_path) do |line| - next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } - plugin = line.split(pattern=separator) - if plugin.length == 2 - podname = plugin[0].strip() - path = plugin[1].strip() - podpath = File.expand_path("#{path}", file_abs_path) - generated_key_values[podname] = podpath - else - puts "Invalid plugin specification: #{line}" - end - end - generated_key_values -end - -target 'Runner' do - use_frameworks! - use_modular_headers! - - # Flutter Pod - - copied_flutter_dir = File.join(__dir__, 'Flutter') - copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') - copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') - unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) - # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. - # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. - # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. - - generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') - unless File.exist?(generated_xcode_build_settings_path) - raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) - cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; - - unless File.exist?(copied_framework_path) - FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) - end - unless File.exist?(copied_podspec_path) - FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) - end - end - - # Keep pod path relative so it can be checked into Podfile.lock. - pod 'Flutter', :path => 'Flutter' - - # Plugin Pods - - # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock - # referring to absolute paths on developers' machines. - system('rm -rf .symlinks') - system('mkdir -p .symlinks/plugins') - plugin_pods = parse_KV_file('../.flutter-plugins') - plugin_pods.each do |name, path| - symlink = File.join('.symlinks', 'plugins', name) - File.symlink(path, symlink) - pod name, :path => File.join(symlink, 'ios') - end -end - -# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. -install! 'cocoapods', :disable_input_output_paths => true - -post_install do |installer| - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['ENABLE_BITCODE'] = 'NO' - end - end -end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock deleted file mode 100644 index 2d6d76f..0000000 --- a/example/ios/Podfile.lock +++ /dev/null @@ -1,22 +0,0 @@ -PODS: - - Flutter (1.0.0) - - native_crypto (0.0.1): - - Flutter - -DEPENDENCIES: - - Flutter (from `Flutter`) - - native_crypto (from `.symlinks/plugins/native_crypto/ios`) - -EXTERNAL SOURCES: - Flutter: - :path: Flutter - native_crypto: - :path: ".symlinks/plugins/native_crypto/ios" - -SPEC CHECKSUMS: - Flutter: 0e3d915762c693b495b44d77113d4970485de6ec - native_crypto: 33b8108e3fcc10052862b69863efc2304c59cb2f - -PODFILE CHECKSUM: 1b66dae606f75376c5f2135a8290850eeb09ae83 - -COCOAPODS: 1.10.1 diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 7335fdf..0000000 --- a/example/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart deleted file mode 100644 index 0cca731..0000000 --- a/example/lib/main.dart +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval -import 'package:flutter/material.dart'; -import 'package:native_crypto_example/pages/kemPage.dart'; - -import 'pages/benchmarkPage.dart'; -import 'pages/cipherPage.dart'; -import 'pages/hashKeyDerivationPage.dart'; - -void main() => runApp(MyApp()); - -class MyApp extends StatefulWidget { - @override - _MyAppState createState() => _MyAppState(); -} - -class _MyAppState extends State { - int _currentIndex = 0; - final List _children = [ - HashKeyDerivationPage(), - CipherPage(), - KemPage(), - BenchmarkPage(), - ]; - - void onTabTapped(int index) { - setState(() { - _currentIndex = index; - }); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: 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: [ - BottomNavigationBarItem( - icon: Icon(Icons.vpn_key), - label: 'Key', - ), - BottomNavigationBarItem( - icon: Icon(Icons.lock), - label: 'Encryption', - ), - BottomNavigationBarItem( - icon: Icon(Icons.connect_without_contact), - label: 'KEM', - ), - BottomNavigationBarItem( - icon: Icon(Icons.timer), - label: 'Benchmark', - ), - ], - ), - ), - ); - } -} diff --git a/example/lib/pages/benchmarkPage.dart b/example/lib/pages/benchmarkPage.dart deleted file mode 100644 index 52024c7..0000000 --- a/example/lib/pages/benchmarkPage.dart +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2021 -// Author: Hugo Pointcheval -import 'dart:developer'; -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:native_crypto/native_crypto.dart'; - -import '../session.dart'; -import '../widgets/button.dart'; -import '../widgets/output.dart'; - -class BenchmarkPage extends StatefulWidget { - const BenchmarkPage({key}) : super(key: key); - - @override - _BenchmarkPageState createState() => _BenchmarkPageState(); -} - -class _BenchmarkPageState extends State { - final Output keyContent = Output( - textEditingController: TextEditingController(), - ); - final Output benchmarkStatus = Output( - textEditingController: TextEditingController(), - large: true, - ); - - Future _benchmark() async { - if (Session.secretKey == null || Session.secretKey.isEmpty) { - benchmarkStatus - .print('No SecretKey!\nGo in Key tab and generate or derive one.'); - return; - } else if (!Session.aesCipher.isInitialized) { - benchmarkStatus.print( - 'Cipher not initialized!\nGo in Key tab and generate or derive one.'); - return; - } - - benchmarkStatus.print("Benchmark 2/4/8/16/32/64/128/256MB\n"); - List testedSizes = [2, 4, 8, 16, 32, 64, 128, 256]; - String csv = - "size;encryption time;encode time;decryption time;crypto time\n"; - - var beforeBench = DateTime.now(); - for (int size in testedSizes) { - var bigFile = Uint8List(size * 1000000); - csv += "${size * 1000000};"; - var cryptoTime = 0; - - // Encryption - var before = DateTime.now(); - var encryptedBigFile = await Session.aesCipher.encrypt(bigFile); - var after = DateTime.now(); - - var benchmark = - after.millisecondsSinceEpoch - before.millisecondsSinceEpoch; - benchmarkStatus.append('[$size MB] Encryption took $benchmark ms\n'); - - csv += "$benchmark;"; - cryptoTime += benchmark; - - // Encoding - before = DateTime.now(); - encryptedBigFile.encode(); - after = DateTime.now(); - - benchmark = after.millisecondsSinceEpoch - before.millisecondsSinceEpoch; - benchmarkStatus.append('[$size MB] Encoding took $benchmark ms\n'); - - csv += "$benchmark;"; - - // Decryption - before = DateTime.now(); - await Session.aesCipher.decrypt(encryptedBigFile); - after = DateTime.now(); - - benchmark = after.millisecondsSinceEpoch - before.millisecondsSinceEpoch; - benchmarkStatus.append('[$size MB] Decryption took $benchmark ms\n'); - - csv += "$benchmark;"; - cryptoTime += benchmark; - csv += "$cryptoTime\n"; - } - var afterBench = DateTime.now(); - var benchmark = - afterBench.millisecondsSinceEpoch - beforeBench.millisecondsSinceEpoch; - var sum = testedSizes.reduce((a, b) => a + b); - benchmarkStatus.append( - 'Benchmark finished.\nGenerated, encrypted and decrypted $sum MB in $benchmark ms'); - log(csv, name: "Benchmark"); - } - - void _clear() { - benchmarkStatus.clear(); - } - - @override - void initState() { - super.initState(); - if (Session.secretKey != null) { - keyContent.print(Session.secretKey.encoded.toString()); - Session.aesCipher = AESCipher( - Session.secretKey, - CipherParameters( - BlockCipherMode.CBC, - PlainTextPadding.PKCS5, - ), - ); - } - } - - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - Align( - child: Text("Secret Key"), - alignment: Alignment.centerLeft, - ), - keyContent, - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Button( - onPressed: _benchmark, - label: "Launch benchmark", - ), - Button( - onPressed: _clear, - label: "Clear", - ), - ], - ), - benchmarkStatus, - ], - ), - ), - ); - } -} diff --git a/example/lib/pages/cipherPage.dart b/example/lib/pages/cipherPage.dart deleted file mode 100644 index 2813831..0000000 --- a/example/lib/pages/cipherPage.dart +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) 2021 -// Author: Hugo Pointcheval -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:native_crypto/native_crypto.dart'; - -import '../session.dart'; -import '../utils.dart'; -import '../widgets/button.dart'; -import '../widgets/output.dart'; - -class CipherPage extends StatefulWidget { - const CipherPage({key}) : super(key: key); - - @override - _CipherPageState createState() => _CipherPageState(); -} - -class _CipherPageState extends State { - final Output keyContent = Output( - textEditingController: TextEditingController(), - ); - final Output encryptionStatus = Output( - textEditingController: TextEditingController(), - ); - final Output decryptionStatus = Output( - textEditingController: TextEditingController(), - ); - final Output cipherExport = Output( - textEditingController: TextEditingController(), - large: true, - editable: true, - ); - - final TextEditingController _plainTextController = TextEditingController(); - CipherText cipherText; - - void _encrypt() async { - final plainText = _plainTextController.text.trim(); - - if (Session.secretKey == null || Session.secretKey.isEmpty) { - encryptionStatus - .print('No SecretKey!\nGo in Key tab and generate or derive one.'); - } else if (!Session.aesCipher.isInitialized) { - encryptionStatus.print( - 'Cipher not initialized!\nGo in Key tab and generate or derive one.'); - } else if (plainText.isEmpty) { - encryptionStatus.print('Entry is empty'); - } else { - var stringToBytes = TypeHelper.stringToBytes(plainText); - cipherText = await Session.aesCipher.encrypt(stringToBytes); - encryptionStatus.print('String successfully encrypted.\n'); - encryptionStatus.append("IV: " + - cipherText.iv.toString() + - "\nCipherText: " + - cipherText.bytes.toString()); - } - } - - void _alter() async { - if (cipherText == null || cipherText.bytes.isEmpty) { - decryptionStatus.print('Encrypt before altering CipherText!'); - } else { - // Add 1 to the first byte - List _altered = cipherText.bytes; - _altered[0][0] += 1; - // Recreate cipher text with altered data - cipherText = AESCipherText.from(_altered, cipherText.iv); - encryptionStatus.print('String successfully encrypted.\n'); - encryptionStatus.append("IV: " + - cipherText.iv.toString() + - "\nCipherText: " + - cipherText.bytes.toString()); - decryptionStatus.print('CipherText altered!\nDecryption will fail.'); - } - } - - void _decrypt() async { - if (Session.secretKey == null || Session.secretKey.isEmpty) { - decryptionStatus - .print('No SecretKey!\nGo in Key tab and generate or derive one.'); - } else if (!Session.aesCipher.isInitialized) { - decryptionStatus.print( - 'Cipher not initialized!\nGo in Key tab and generate or derive one.'); - } else if (cipherText == null || cipherText.bytes.isEmpty) { - decryptionStatus.print('Encrypt before decrypting!'); - } else { - try { - Uint8List plainText = await Session.aesCipher.decrypt(cipherText); - var bytesToString = TypeHelper.bytesToString(plainText); - decryptionStatus - .print('String successfully decrypted:\n\n$bytesToString'); - } on DecryptionException catch (e) { - decryptionStatus.print(e.message); - } - } - } - - void _export() async { - if (cipherText == null) { - decryptionStatus.print('Encrypt data before export!'); - } else { - // TODO: fix export format to support chunks ! - Uint8List payload = cipherText.encode(); - String data = TypeHelper.bytesToBase64(payload); - decryptionStatus.print('CipherText successfully exported'); - cipherExport.print(data); - } - } - - void _import() async { - final String data = cipherExport.read(); - if (data.isEmpty) { - encryptionStatus.print('CipherText import failed'); - } else { - Uint8List payload = TypeHelper.base64ToBytes(data); - cipherText = AESCipherText.empty(); - cipherText.decode(payload); - encryptionStatus.print('CipherText successfully imported\n'); - encryptionStatus.append("IV: " + - cipherText.iv.toString() + - "\nCipherText: " + - cipherText.bytes.toString()); - } - } - - @override - void initState() { - super.initState(); - if (Session.secretKey != null) { - keyContent.print(Session.secretKey.encoded.toString()); - Session.aesCipher = AESCipher( - Session.secretKey, - CipherParameters( - BlockCipherMode.CBC, - PlainTextPadding.PKCS5, - ), - ); - } - } - - @override - void dispose() { - super.dispose(); - _plainTextController.dispose(); - } - - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - Align( - child: Text("Secret Key"), - alignment: Alignment.centerLeft, - ), - keyContent, - TextField( - controller: _plainTextController, - decoration: InputDecoration( - hintText: 'Plain text', - ), - ), - Button( - onPressed: _encrypt, - label: "Encrypt", - ), - encryptionStatus, - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Button( - onPressed: _alter, - label: "Alter cipher", - ), - Button( - onPressed: _decrypt, - label: "Decrypt", - ), - ], - ), - decryptionStatus, - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Button( - onPressed: _export, - label: "Export cipher", - ), - Button( - onPressed: _import, - label: "Import cipher", - ), - ], - ), - cipherExport - ], - ), - ), - ); - } -} diff --git a/example/lib/pages/hashKeyDerivationPage.dart b/example/lib/pages/hashKeyDerivationPage.dart deleted file mode 100644 index 5219dc0..0000000 --- a/example/lib/pages/hashKeyDerivationPage.dart +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:native_crypto/native_crypto.dart'; - -import '../session.dart'; -import '../utils.dart'; -import '../widgets/button.dart'; -import '../widgets/output.dart'; - -class HashKeyDerivationPage extends StatefulWidget { - const HashKeyDerivationPage({key}) : super(key: key); - - @override - _HashKeyDerivationPageState createState() => _HashKeyDerivationPageState(); -} - -class _HashKeyDerivationPageState extends State { - final Output keyContent = Output( - textEditingController: TextEditingController(), - ); - final Output keyStatus = Output( - textEditingController: TextEditingController(), - ); - final Output keyExport = Output( - textEditingController: TextEditingController(), - large: true, - editable: true, - ); - final Output pbkdf2Status = Output( - textEditingController: TextEditingController(), - ); - final Output hashStatus = Output( - textEditingController: TextEditingController(), - ); - - final TextEditingController _pwdTextController = TextEditingController(); - final TextEditingController _messageTextController = TextEditingController(); - final TextEditingController _keyTextController = TextEditingController(); - - void _generate() async { - try { - Session.secretKey = await SecretKey.generate(256, CipherAlgorithm.AES); - keyContent.print(Session.secretKey.encoded.toString()); - keyStatus.print( - "Secret Key successfully generated.\nLength: ${Session.secretKey.encoded.length} bytes"); - } catch (e) { - keyStatus.print(e.message); - } - } - - void _pbkdf2() async { - final password = _pwdTextController.text.trim(); - - if (password.isEmpty) { - pbkdf2Status.print('Password is empty'); - } else { - PBKDF2 _pbkdf2 = - PBKDF2(keyLength: 32, iteration: 1000, hash: HashAlgorithm.SHA512); - await _pbkdf2.derive(password: password, salt: 'salty'); - SecretKey key = _pbkdf2.key; - pbkdf2Status.print('Key successfully derived.'); - Session.secretKey = key; - keyContent.print(Session.secretKey.encoded.toString()); - } - } - - void _export() async { - if (Session.secretKey == null || Session.secretKey.isEmpty) { - keyStatus - .print('No SecretKey!\nGenerate or derive one before exporting!'); - } else { - String key = TypeHelper.bytesToBase64(Session.secretKey.encoded); - keyStatus.print('Key successfully exported'); - keyExport.print(key); - } - } - - void _import() async { - final String key = keyExport.read(); - if (key.isEmpty) { - keyStatus.print('Key import failed'); - } else { - Uint8List keyBytes = TypeHelper.base64ToBytes(key); - Session.secretKey = - SecretKey.fromBytes(keyBytes, algorithm: CipherAlgorithm.AES); - keyStatus.print('Key successfully imported'); - keyContent.print(Session.secretKey.encoded.toString()); - } - } - - void _hash() async { - final message = _messageTextController.text.trim(); - - if (message.isEmpty) { - hashStatus.print('Message is empty'); - } else { - MessageDigest md = MessageDigest.getInstance("sha256"); - Uint8List hash = await md.digest(TypeHelper.stringToBytes(message)); - hashStatus.print('Message successfully hashed.\n' + hash.toString()); - } - } - - @override - void initState() { - super.initState(); - if (Session.secretKey != null) { - keyContent.print(Session.secretKey.encoded.toString()); - } - } - - @override - void dispose() { - super.dispose(); - _pwdTextController.dispose(); - _messageTextController.dispose(); - _keyTextController.dispose(); - } - - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - Align( - child: Text("Secret Key"), - alignment: Alignment.centerLeft, - ), - keyContent, - Button( - onPressed: _generate, - label: "Generate key", - ), - keyStatus, - TextField( - controller: _pwdTextController, - decoration: InputDecoration( - hintText: 'Password', - ), - ), - Button( - onPressed: _pbkdf2, - label: "Apply PBKDF2", - ), - pbkdf2Status, - TextField( - controller: _messageTextController, - decoration: InputDecoration( - hintText: 'Message', - ), - ), - Button( - onPressed: _hash, - label: "Hash", - ), - hashStatus, - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Button( - onPressed: _export, - label: "Export key", - ), - Button( - onPressed: _import, - label: "Import key", - ), - ], - ), - keyExport - ], - ), - ), - ); - } -} diff --git a/example/lib/pages/kemPage.dart b/example/lib/pages/kemPage.dart deleted file mode 100644 index 30e2fc6..0000000 --- a/example/lib/pages/kemPage.dart +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval -import 'package:flutter/material.dart'; -import 'package:native_crypto/native_crypto.dart'; - -class KemPage extends StatefulWidget { - KemPage({key}) : super(key: key); - - @override - _KemPageState createState() => _KemPageState(); -} - -class _KemPageState extends State { - void test() async { - KeySpec specs = RSAKeySpec(2048); - KeyPair kp = await KeyPair.generate(specs); - print(kp.isComplete); - print(kp.privateKey); - print(kp.privateKey.encoded); - } - - @override - void initState() { - super.initState(); - test(); - } - - @override - Widget build(BuildContext context) { - return Container( - child: Center( - child: Text("Not implemented."), - ), - ); - } -} diff --git a/example/lib/session.dart b/example/lib/session.dart deleted file mode 100644 index 4f68f87..0000000 --- a/example/lib/session.dart +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval - -import 'package:native_crypto/native_crypto.dart'; - -class Session { - static SecretKey secretKey; - static AESCipher aesCipher; -} diff --git a/example/lib/utils.dart b/example/lib/utils.dart deleted file mode 100644 index 3f03f32..0000000 --- a/example/lib/utils.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval -import 'dart:typed_data'; -import 'dart:convert'; - -/// Contains some useful functions. -class TypeHelper { - /// Returns bytes [Uint8List] from a [String]. - static Uint8List stringToBytes(String source) { - var list = source.runes.toList(); - var bytes = Uint8List.fromList(list); - return bytes; - } - - /// Returns a [String] from bytes [Uint8List]. - static String bytesToString(Uint8List bytes) { - var string = String.fromCharCodes(bytes); - return string; - } - - /// Returns a `base64` [String] from bytes [Uint8List]. - static String bytesToBase64(Uint8List bytes) { - return base64.encode(bytes); - } - - /// Returns a [Uint8List] from a `base64` [String]. - static Uint8List base64ToBytes(String encoded) { - return base64.decode(encoded); - } -} diff --git a/example/lib/widgets/button.dart b/example/lib/widgets/button.dart deleted file mode 100644 index 27c6db7..0000000 --- a/example/lib/widgets/button.dart +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval -import 'package:flutter/material.dart'; - -class Button extends StatelessWidget { - const Button({Key key, this.onPressed, this.label}) : super(key: key); - - final void Function() onPressed; - final String label; - - @override - Widget build(BuildContext context) { - return Container( - child: FlatButton( - onPressed: onPressed, - color: Colors.blue, - child: Text( - label, - style: TextStyle(color: Colors.white), - ), - ), - ); - } -} diff --git a/example/lib/widgets/output.dart b/example/lib/widgets/output.dart deleted file mode 100644 index 45bdccb..0000000 --- a/example/lib/widgets/output.dart +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval -import 'dart:developer'; - -import 'package:flutter/material.dart'; - -class Output extends StatelessWidget { - const Output( - {Key key, - this.textEditingController, - this.large: false, - this.editable: false}) - : super(key: key); - - final TextEditingController textEditingController; - final bool large; - final bool editable; - - void print(String message) { - log(message, name: "NativeCrypto Example"); - textEditingController.text = message; - } - - void append(String message) { - log(message, name: "NativeCrypto Example"); - textEditingController.text += message; - } - - void appendln(String message) { - log(message, name: "NativeCrypto Example"); - textEditingController.text += message + "\n"; - } - - void clear() { - textEditingController.clear(); - } - - String read() { - return textEditingController.text; - } - - @override - Widget build(BuildContext context) { - return Container( - child: TextField( - enableInteractiveSelection: true, - readOnly: editable ? false : true, - minLines: large ? 3 : 1, - maxLines: large ? 500 : 5, - decoration: InputDecoration(border: OutlineInputBorder()), - controller: textEditingController, - ), - ); - } -} diff --git a/ios/Classes/Cipher.swift b/ios/Classes/Cipher.swift deleted file mode 100644 index 5d92895..0000000 --- a/ios/Classes/Cipher.swift +++ /dev/null @@ -1,135 +0,0 @@ -// -// Cipher.swift -// -// NativeCryptoPlugin -// -// Copyright (c) 2020 -// Author: Hugo Pointcheval -// - -import Foundation -import CommonCrypto - -enum CipherAlgorithm: String { - case AES = "aes" - case BlowFish = "blowfish" - - var instance: Int { - switch self { - case .AES: return kCCAlgorithmAES - case .BlowFish: return kCCAlgorithmBlowfish - } - } -} - -enum BlockCipherMode: String { - case ECB = "ecb" - case CBC = "cbc" - - var instance: Int { - switch self { - case .CBC: return 0 - case .ECB: return kCCOptionECBMode - } - } -} - -enum Padding: String { - case PKCS5 = "pkcs5" - case None = "none" - - var instance: Int { - switch self { - case .PKCS5: return kCCOptionPKCS7Padding - case .None: return 0 - } - } -} - -class Cipher { - func encrypt(data : Data, key : Data, algorithm : CipherAlgorithm, mode : BlockCipherMode, padding : Padding) -> [Data]? { - // Calculate Mac - let mac = Hash().digest(data: key + data, algorithm: .SHA256) - let payload = mac! + data - - // Generate IV - let ivBytes = UnsafeMutableRawPointer.allocate(byteCount: kCCBlockSizeAES128, alignment: 1) - defer { ivBytes.deallocate() } - let ivStatus = CCRandomGenerateBytes(ivBytes, kCCBlockSizeAES128) - if (ivStatus != kCCSuccess) { - return nil - } - let ivData = Data(bytes: ivBytes, count: kCCBlockSizeAES128) - - let algo = algorithm.instance - let options: CCOptions = UInt32(mode.instance + padding.instance) - - - guard var ciphertext = crypt(operation: kCCEncrypt, - algorithm: algo, - options: options, - key: key, - initializationVector: ivData, - dataIn: payload) else { return nil } - - return [ciphertext, ivData] - } - - func decrypt(payload : [Data], key : Data, algorithm : CipherAlgorithm, mode : BlockCipherMode, padding : Padding) -> Data? { - let encrypted = payload[1] + payload[0] - - guard encrypted.count > kCCBlockSizeAES128 else { return nil } - let iv = encrypted.prefix(kCCBlockSizeAES128) - let ciphertext = encrypted.suffix(from: kCCBlockSizeAES128) - - let algo = algorithm.instance - let options: CCOptions = UInt32(mode.instance + padding.instance) - - guard var decrypted = crypt(operation: kCCDecrypt, - algorithm: algo, - options: options, - key: key, - initializationVector: iv, - dataIn: ciphertext) else {return nil} - - - // Create a range based on the length of data to return - let range = 0..<32 - - // Get a new copy of data - let mac = decrypted.subdata(in: range) - decrypted.removeSubrange(range) - - let vmac = Hash().digest(data: key + decrypted, algorithm: .SHA256) - - if (mac.base64EncodedData() == vmac!.base64EncodedData()) { - return decrypted - } else { - return nil - } - } - - private func crypt(operation: Int, algorithm: Int, options: UInt32, key: Data, - initializationVector: Data, dataIn: Data) -> Data? { - - return key.withUnsafeBytes { keyUnsafeRawBufferPointer in - return dataIn.withUnsafeBytes { dataInUnsafeRawBufferPointer in - return initializationVector.withUnsafeBytes { ivUnsafeRawBufferPointer in - let dataOutSize: Int = dataIn.count + kCCBlockSizeAES128*2 - let dataOut = UnsafeMutableRawPointer.allocate(byteCount: dataOutSize, - alignment: 1) - defer { dataOut.deallocate() } - var dataOutMoved: Int = 0 - let status = CCCrypt(CCOperation(operation), CCAlgorithm(algorithm), - CCOptions(options), - keyUnsafeRawBufferPointer.baseAddress, key.count, - ivUnsafeRawBufferPointer.baseAddress, - dataInUnsafeRawBufferPointer.baseAddress, dataIn.count, - dataOut, dataOutSize, &dataOutMoved) - guard status == kCCSuccess else { return nil } - return Data(bytes: dataOut, count: dataOutMoved) - } - } - } - } -} diff --git a/ios/Classes/Hash.swift b/ios/Classes/Hash.swift deleted file mode 100644 index 430e2a7..0000000 --- a/ios/Classes/Hash.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// Hash.swift -// -// NativeCryptoPlugin -// -// Copyright (c) 2020 -// Author: Hugo Pointcheval -// -import Foundation -import CommonCrypto - -enum HashAlgorithm: String { - case SHA1 = "sha1" - case SHA224 = "sha224" - case SHA256 = "sha256" - case SHA384 = "sha384" - case SHA512 = "sha512" - - var digestLength: Int { - switch self { - case .SHA1: return Int(CC_SHA1_DIGEST_LENGTH) - case .SHA224: return Int(CC_SHA224_DIGEST_LENGTH) - case .SHA256: return Int(CC_SHA256_DIGEST_LENGTH) - case .SHA384: return Int(CC_SHA384_DIGEST_LENGTH) - case .SHA512: return Int(CC_SHA512_DIGEST_LENGTH) - } - } - - var pbkdf2: UInt32 { - switch self { - case .SHA1: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA1) - case .SHA224: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA224) - case .SHA256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) - case .SHA384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384) - case .SHA512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) - } - } -} - -class Hash { - func digest(data: Data?, algorithm: HashAlgorithm) -> Data? { - if (data == nil) { - return nil - } - - let hashBytes = UnsafeMutablePointer.allocate(capacity: algorithm.digestLength) - defer { hashBytes.deallocate() } - - switch algorithm { - case .SHA1: - data!.withUnsafeBytes { (buffer) -> Void in - CC_SHA1(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes) - } - case .SHA224: - data!.withUnsafeBytes { (buffer) -> Void in - CC_SHA224(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes) - } - case .SHA256: - data!.withUnsafeBytes { (buffer) -> Void in - CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes) - } - case .SHA384: - data!.withUnsafeBytes { (buffer) -> Void in - CC_SHA384(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes) - } - case .SHA512: - data!.withUnsafeBytes { (buffer) -> Void in - CC_SHA512(buffer.baseAddress!, CC_LONG(buffer.count), hashBytes) - } - } - - return Data(bytes: hashBytes, count: algorithm.digestLength) - } -} diff --git a/ios/Classes/KeyDerivation.swift b/ios/Classes/KeyDerivation.swift deleted file mode 100644 index b1a5954..0000000 --- a/ios/Classes/KeyDerivation.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// KeyDerivation.swift -// -// NativeCryptoPlugin -// -// Copyright (c) 2020 -// Author: Hugo Pointcheval -// - -import Foundation -import CommonCrypto - -class KeyDerivation { - func pbkdf2(password: String, salt: String, keyLength: Int, iteration: Int, algorithm: HashAlgorithm) -> Data? { - - let passwordData = password.data(using: .utf8)! - let saltData = salt.data(using: .utf8)! - - var derivedKeyData = Data(repeating: 0, count: keyLength) - var localDerivedKeyData = derivedKeyData - - let derivationStatus = derivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in - saltData.withUnsafeBytes { saltBytes in - CCKeyDerivationPBKDF( - CCPBKDFAlgorithm(kCCPBKDF2), - password, passwordData.count, - saltBytes, saltData.count, - algorithm.pbkdf2, - UInt32(iteration), - derivedKeyBytes, localDerivedKeyData.count) - } - } - if (derivationStatus != kCCSuccess) { - print("Error: \(derivationStatus)") - return nil; - } - - return derivedKeyData - } -} diff --git a/ios/Classes/KeyGeneration.swift b/ios/Classes/KeyGeneration.swift deleted file mode 100644 index 76e0325..0000000 --- a/ios/Classes/KeyGeneration.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// KeyGeneration.swift -// -// NativeCryptoPlugin -// -// Copyright (c) 2020 -// Author: Hugo Pointcheval -// -import Foundation -import CommonCrypto - -class KeyGeneration { - func keygen(size : NSNumber) -> Data? { - var bytes = [Int8](repeating: 0, count: size.intValue / 8) - let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes) - - if status == errSecSuccess { - let keyBytes = bytes.withUnsafeBytes { - return Data(Array($0)) - - } - return keyBytes - } - return nil - } - - @available(iOS 10.0, watchOS 3.0, tvOS 10.0, *) - func rsaKeypairGen(size : NSNumber) throws -> [Data]? { - - let tagData = UUID().uuidString.data(using: .utf8) - - let isPermanent = true - - let attributes: [CFString: Any] = [ - kSecAttrKeyType: kSecAttrKeyTypeRSA, - kSecAttrKeySizeInBits: (size.intValue * 8), - kSecPrivateKeyAttrs: [ - kSecAttrIsPermanent: isPermanent, - kSecAttrApplicationTag: tagData! - ] - ] - - var error: Unmanaged? - guard let privKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { - throw error!.takeRetainedValue() as Error - } - let pubKey = SecKeyCopyPublicKey(privKey) - - - var errorExport: Unmanaged? - let data1 = SecKeyCopyExternalRepresentation(pubKey!, &errorExport) - let unwrappedData1 = data1 as Data? - - let data2 = SecKeyCopyExternalRepresentation(privKey, &errorExport) - let unwrappedData2 = data2 as Data? - - return [unwrappedData1!, unwrappedData2!] - } -} diff --git a/ios/Classes/NativeCryptoPlugin.h b/ios/Classes/NativeCryptoPlugin.h deleted file mode 100644 index 0f584e1..0000000 --- a/ios/Classes/NativeCryptoPlugin.h +++ /dev/null @@ -1,4 +0,0 @@ -#import - -@interface NativeCryptoPlugin : NSObject -@end diff --git a/ios/Classes/SwiftNativeCryptoPlugin.swift b/ios/Classes/SwiftNativeCryptoPlugin.swift deleted file mode 100644 index f33cb88..0000000 --- a/ios/Classes/SwiftNativeCryptoPlugin.swift +++ /dev/null @@ -1,158 +0,0 @@ -// -// NativeCryptoPlugin -// -// Copyright (c) 2020 -// Author: Hugo Pointcheval -// -import Flutter - -extension FlutterStandardTypedData { - var uint8Array: Array { - return Array(data) - } - var int8Array: Array { - return data.withUnsafeBytes { raw in - [Int8](raw.bindMemory(to: Int8.self)) - } - } -} - -public class SwiftNativeCryptoPlugin: NSObject, FlutterPlugin { - public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "native.crypto", binaryMessenger: registrar.messenger()) - let instance = SwiftNativeCryptoPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) - } - - public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "digest": - let args = call.arguments as! NSDictionary - - let message = (args["message"] as! FlutterStandardTypedData).data - let algo = args["algorithm"] as! String - - let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) - - let hash = Hash().digest(data: message, algorithm: algorithm!) - - if hash != nil { - result(FlutterStandardTypedData.init(bytes: hash!)) - } else { - result(FlutterError(code: "DIGESTERROR", - message: "DIGEST IS NIL.", - details: nil) - ) - } - case "pbkdf2": - let args = call.arguments as! NSDictionary - - let password = args["password"] as! String - let salt = args["salt"] as! String - let keyLength = args["keyLength"] as! NSNumber - let iteration = args["iteration"] as! NSNumber - let algo = args["algorithm"] as! String - - let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) - - let key = KeyDerivation().pbkdf2(password: password, salt: salt, keyLength: keyLength.intValue, iteration: iteration.intValue, algorithm: algorithm!) - - if key != nil { - result(FlutterStandardTypedData.init(bytes: key!)) - } else { - result(FlutterError(code: "PBKDF2ERROR", - message: "PBKDF2 KEY IS NIL.", - details: nil) - ) - } - case "keygen": - let args = call.arguments as! NSDictionary - - let size = args["size"] as! NSNumber - - let key = KeyGeneration().keygen(size: size) - - if key != nil { - result(FlutterStandardTypedData.init(bytes: key!)) - } else { - result(FlutterError(code: "KEYGENERROR", - message: "GENERATED KEY IS NIL.", - details: nil)) - } - case "rsaKeypairGen": - let args = call.arguments as! NSDictionary - - let size = args["size"] as! NSNumber - - let keypair : [Data]? - - if #available(iOS 10.0, *) { - do { - keypair = try KeyGeneration().rsaKeypairGen(size: size) - } catch { - keypair = nil - } - } else { - // Fallback on earlier versions - keypair = nil - } - - if keypair != nil { - result(keypair) - } else { - result(FlutterError(code: "KEYPAIRGENERROR", - message: "GENERATED KEYPAIR IS EMPTY.", - details: nil)) - } - case "encrypt": - let args = call.arguments as! NSDictionary - - let data = (args["data"] as! FlutterStandardTypedData).data - let key = (args["key"] as! FlutterStandardTypedData).data - let algo = args["algorithm"] as! String - let mode = args["mode"] as! String - let padding = args["padding"] as! String - - let algorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algo) - let modeEnum : BlockCipherMode? = BlockCipherMode.init(rawValue: mode) - let paddingEnum : Padding? = Padding.init(rawValue: padding) - - let ciphertext = Cipher().encrypt(data: data, key: key, algorithm: algorithm!, mode: modeEnum!, padding: paddingEnum!) - - if ciphertext != nil { - result(ciphertext) - } else { - result(FlutterError(code: "ENCRYPTIONERROR", - message: "ENCRYPTED PAYLOAD IS EMPTY.", - details: nil)) - } - case "decrypt": - let args = call.arguments as! NSDictionary - - let payload = args["payload"] as! NSArray - let key = (args["key"] as! FlutterStandardTypedData).data - let algo = args["algorithm"] as! String - let mode = args["mode"] as! String - let padding = args["padding"] as! String - - let encrypted = (payload[0] as! FlutterStandardTypedData).data - let iv = (payload[1] as! FlutterStandardTypedData).data - let encryptedPayload = [encrypted, iv] - - let algorithm : CipherAlgorithm? = CipherAlgorithm.init(rawValue: algo) - let modeEnum : BlockCipherMode? = BlockCipherMode.init(rawValue: mode) - let paddingEnum : Padding? = Padding.init(rawValue: padding) - - let decrypted = Cipher().decrypt(payload: encryptedPayload, key: key, algorithm: algorithm!, mode: modeEnum!, padding: paddingEnum!) - - if decrypted != nil { - result(FlutterStandardTypedData.init(bytes: decrypted!)) - } else { - result(FlutterError(code: "DECRYPTIONERROR", - message: "DECRYPTED PAYLOAD IS NIL. MAYBE VERIFICATION MAC IS UNVALID.", - details: nil)) - } - default: result(FlutterMethodNotImplemented) - } - } -} diff --git a/lib/native_crypto.dart b/lib/native_crypto.dart deleted file mode 100644 index 7f00d94..0000000 --- a/lib/native_crypto.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2021 -// Author: Hugo Pointcheval -export './src/exceptions.dart'; -export './src/key.dart'; -export './src/keyspec.dart'; -export './src/keyderivation.dart'; -export './src/digest.dart'; -export './src/cipher.dart'; -export './src/kem.dart'; -export './src/platform.dart'; -export './src/utils.dart'; - -export './src/sym/AES.dart'; -export './src/asym/RSA.dart'; - -const String version = "0.0.6"; -const String author = "Hugo Pointcheval"; -const String website = "https://hugo.pointcheval.fr/"; -const String repository = "https://github.com/hugo-pcl/native-crypto-flutter"; diff --git a/lib/src/asym/RSA.dart b/lib/src/asym/RSA.dart deleted file mode 100644 index 96a1db3..0000000 --- a/lib/src/asym/RSA.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval - -import 'package:native_crypto/src/exceptions.dart'; -import 'package:native_crypto/src/kem.dart'; -import 'package:native_crypto/src/key.dart'; - -/// This represents RSA Key Encapsulation Mechanism -class RSAKeyEncapsulationMechanism implements KeyEncapsulationMechanism { - bool _isInit = false; - KemMode _mode; - - PublicKey _publicKey; - PrivateKey _privateKey; - - @override - KemAlgorithm get algorithm => KemAlgorithm.RSA; - - @override - Object get options => null; - - @override - bool get isInitialized => _isInit; - - @override - KemMode get mode => _mode; - - @override - KeyPair get keypair => KeyPair.from(_publicKey, _privateKey); - - RSAKeyEncapsulationMechanism( - KemMode mode, { - KeyPair keyPair, - PublicKey publicKey, - }) { - _mode = mode; - if (_mode == KemMode.ENCAPSULATION) { - if (publicKey != null) { - _isInit = true; - _publicKey = publicKey; - } else if (keyPair != null) { - if (keyPair.publicKey != null) { - _isInit = true; - _publicKey = keyPair.publicKey; - } - } - } else if (_mode == KemMode.DECAPSULATION) { - if (keyPair != null) { - if (keyPair.isComplete) { - _isInit = true; - _publicKey = keyPair.publicKey; - _privateKey = keyPair.privateKey; - } else { - throw KemInitException("Keypair must be complete for decapsulation."); - } - } else { - throw KemInitException("You must provide a keypair for decapsulation."); - } - } - } - - @override - Future encapsulate() { - if (!_isInit || _mode == KemMode.DECAPSULATION) { - throw KemInitException("KEM not properly initialized."); - } - throw UnimplementedError(); - } - - @override - Future decapsulate(Encapsulation encapsulation) { - if (!_isInit || _mode == KemMode.ENCAPSULATION) { - throw KemInitException("KEM not properly initialized."); - } - throw UnimplementedError(); - } -} diff --git a/lib/src/cipher.dart b/lib/src/cipher.dart deleted file mode 100644 index b1e34bf..0000000 --- a/lib/src/cipher.dart +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2021 -// Author: Hugo Pointcheval - -import 'dart:typed_data'; - -import 'key.dart'; - -/// Represents different cipher algorithms -enum CipherAlgorithm { AES, None } - -/// Represents different block cipher modes -enum BlockCipherMode { CBC, GCM } - -/// Represents different padding -enum PlainTextPadding { PKCS5, None } - -/// Represents a cipher. -/// -/// 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. -abstract class Cipher { - /// Returns the standard algorithm name for this cipher - CipherAlgorithm get algorithm; - - /// Returns the secret key used for this cipher - SecretKey get secretKey; - - /// Returns the parameters used for this cipher - CipherParameters get parameters; - - /// Returns true if cipher is initialized - bool get isInitialized; - - /// Returnes list of supported [CipherParameters] for this cipher - List get supportedParameters; - - /// Encrypts data. - /// - /// Takes [Uint8List] data as parameter. - /// Returns a [CipherText]. - Future encrypt(Uint8List data); - - /// Decrypts cipher text. - /// - /// Takes [CipherText] as parameter. - /// And returns plain text data as [Uint8List]. - Future decrypt(CipherText cipherText); -} - -/// Represents a cipher text. -/// -/// It's the result of an encryption. -abstract class CipherText { - /// Returns the standard algorithm name used for this ciphertext. - CipherAlgorithm get algorithm; - - /// Returns the data of this ciphertext (in chunks). - List get bytes; - - /// Returns the IV of this cipertext (in chunks). - List get iv; - - /// Returns the chunk number of this cipherText. - int get size; - - /// Returns this ciphertext in simple Byte Array format. - Uint8List encode(); - - /// Transforms a simple Byte Array to a NativeCrypto cipherText. - void decode(Uint8List src); -} - -/// Represents a pair of [BlockCipherMode] and [Padding] -class CipherParameters { - BlockCipherMode _mode; - PlainTextPadding _padding; - - /// Returns mode used in the cipher - BlockCipherMode get mode => _mode; - - /// Returns padding used in the cipher - PlainTextPadding get padding => _padding; - - CipherParameters(BlockCipherMode mode, PlainTextPadding padding) { - _mode = mode; - _padding = padding; - } - - @override - bool operator ==(Object o) { - if (identical(this, o)) return true; - - return o is CipherParameters && - o._mode.index == _mode.index && - o._padding.index == _padding.index; - } - - @override - int get hashCode => _mode.hashCode ^ _padding.hashCode; -} diff --git a/lib/src/digest.dart b/lib/src/digest.dart deleted file mode 100644 index 344d5c5..0000000 --- a/lib/src/digest.dart +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval - -import 'dart:typed_data'; - -import 'exceptions.dart'; -import 'platform.dart'; -import 'utils.dart'; - -enum HashAlgorithm { SHA1, SHA224, SHA256, SHA384, SHA512 } - -/// Represents message digest, or hash function. -class MessageDigest { - HashAlgorithm _algo; - - /// Returns the standard algorithm name for this digest - HashAlgorithm get algorithm => _algo; - - /// Returns true if digest is initialized - bool get isInitialized => (_algo != null); - - /// Creates [MessageDigest] with a specific algorithm - MessageDigest(HashAlgorithm algorithm) { - _algo = algorithm; - } - - /// Creates [MessageDigest] from the name of an algorithm - MessageDigest.getInstance(String algorithm) { - _algo = Utils.getHashAlgorithm(algorithm); - } - - /// Hashes a message - Future digest(Uint8List data) async { - if (!isInitialized) { - throw DigestInitException('Digest not properly initialized.'); - } - - Uint8List hash = await Platform().digest(data, _algo); - return hash; - } -} diff --git a/lib/src/exceptions.dart b/lib/src/exceptions.dart deleted file mode 100644 index 690d137..0000000 --- a/lib/src/exceptions.dart +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval - -class NativeCryptoException implements Exception { - String message; - NativeCryptoException(this.message); -} - -class UtilsException extends NativeCryptoException { - UtilsException(message) : super(message); -} - -class KeyException extends NativeCryptoException { - KeyException(message) : super(message); -} - -class KeyGenerationException extends NativeCryptoException { - KeyGenerationException(message) : super(message); -} - -class KeyPairGenerationException extends NativeCryptoException { - KeyPairGenerationException(message) : super(message); -} - -class KeyDerivationException extends NativeCryptoException { - KeyDerivationException(message) : super(message); -} - -class CipherInitException extends NativeCryptoException { - CipherInitException(message) : super(message); -} - -class KemInitException extends NativeCryptoException { - KemInitException(message) : super(message); -} - -class DigestInitException extends NativeCryptoException { - DigestInitException(message) : super(message); -} - -class DigestException extends NativeCryptoException { - DigestException(message) : super(message); -} - -class EncryptionException extends NativeCryptoException { - EncryptionException(message) : super(message); -} - -class DecryptionException extends NativeCryptoException { - DecryptionException(message) : super(message); -} - -class NotImplementedException extends NativeCryptoException { - NotImplementedException(message) : super(message); -} diff --git a/lib/src/kem.dart b/lib/src/kem.dart deleted file mode 100644 index e6a23dd..0000000 --- a/lib/src/kem.dart +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval - -import 'dart:typed_data'; - -import 'key.dart'; - -enum KemAlgorithm { RSA, ECDH } - -enum KemMode { ENCAPSULATION, DECAPSULATION } - -abstract class KeyEncapsulationMechanism { - /// Returns the standard algorithm name for this kem - KemAlgorithm get algorithm; - - /// Returns the parameters used for this kem - Object get options; - - /// Returns true if kem is initialized - bool get isInitialized; - - /// Returns mode used in this kem. - KemMode get mode; - - /// Returns key pair used in this kem. - /// - /// Can be an incomplete if just have public key - /// for example. - KeyPair get keypair; - - /// Encapsulate key. - /// - /// Returns an [Encapsulation]. - Future encapsulate(); - - /// Decapsulate key. - /// - /// Takes [Encapsulation] as parameter. - /// And returns plain text key as [SecretKey]. - Future decapsulate(Encapsulation encapsulation); -} - -abstract class Encapsulation { - /// Returns the standard algorithm name used for this encapsulation - KemAlgorithm get algorithm; - - /// Returns the secret key used in this encapsulation - SecretKey get secretKey; - - /// Returns the encapsulated key - Uint8List get key; -} diff --git a/lib/src/key.dart b/lib/src/key.dart deleted file mode 100644 index 16ba29f..0000000 --- a/lib/src/key.dart +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2021 -// Author: Hugo Pointcheval - -import 'dart:typed_data'; - -import 'package:flutter/services.dart'; -import 'platform.dart'; - -import 'exceptions.dart'; -import 'keyspec.dart'; -import 'cipher.dart'; -import 'utils.dart'; - -/// This is the base class of all key types. -abstract class Key { - Uint8List _bytes; - String _algo; - - /// Returns key as raw byte array. - Uint8List get encoded => _bytes; - - /// Returns the standard algorithm name for this key - String get algorithm => _algo; - - /// Returns the true if this key is null or empty. - bool get isEmpty => (_bytes == null || _bytes.length == 0); - - Key({Uint8List bytes, String algorithm}) { - _bytes = bytes; - _algo = algorithm; - } -} - -/// This represents a secret key, usefull in -/// algorithms like AES or BlowFish. -class SecretKey extends Key { - /// Creates a key from raw byte array - SecretKey.fromBytes(Uint8List bytes, - {CipherAlgorithm algorithm: CipherAlgorithm.None}) - : super(bytes: bytes, algorithm: algorithm.name); - - /// Creates a key from a specific size. - static Future generate(int size, CipherAlgorithm algorithm) async { - if (algorithm == null) { - throw KeyException("Algorithm can't be null"); - } else if (algorithm == CipherAlgorithm.AES) { - List _supportedSizes = [128, 192, 256]; - if (!_supportedSizes.contains(size)) { - throw KeyException("AES must be 128, 192 or 256 bits long."); - } - } - - try { - Uint8List _key = await Platform().keygen(size); - return SecretKey.fromBytes(_key, algorithm: algorithm); - } on PlatformException catch (e) { - throw KeyException(e); - } - } -} - -/// This represents a keypair, usefull in -/// algorithms like RSA. -class KeyPair extends Key { - PublicKey _publicKey; - PrivateKey _privateKey; - - /// Returns public key of this key pair. - PublicKey get publicKey => _publicKey; - - /// Returns private key of this key pair. - PrivateKey get privateKey => _privateKey; - - /// Returns true if key pair contains public AND private keys - bool get isComplete => (_publicKey != null && _privateKey != null); - - /// Creates a key pair from public and private keys. - KeyPair.from(PublicKey publicKey, PrivateKey privateKey, {String algorithm}) { - _publicKey = publicKey; - _privateKey = privateKey; - _algo = algorithm; - } - - /// Creates a key pair from a specific size. - static Future generate(KeySpec keySpec) async { - List _supportedAlgorithms = ["RSA"]; - if (!_supportedAlgorithms.contains(keySpec.algorithm)) { - throw KeyException(keySpec.algorithm + " not supported!"); - } - if (keySpec.algorithm == "RSA") { - RSAKeySpec spec = keySpec; - try { - List kp = await Platform().rsaKeypairGen(spec.size); - PublicKey _publicKey = PublicKey.fromBytes(kp.first, "RSA"); - PrivateKey _privateKey = PrivateKey.fromBytes(kp.last, "RSA"); - return KeyPair.from(_publicKey, _privateKey, algorithm: "RSA"); - } on PlatformException catch (e) { - throw KeyException(e); - } - } else { - throw NotImplementedException("KeyPair generation not yet implemented."); - } - } -} - -/// This represents a public key -class PublicKey extends Key { - /// Creates a public key from raw byte array - PublicKey.fromBytes(Uint8List bytes, String algorithm) - : super(bytes: bytes, algorithm: algorithm); -} - -/// This represents a private key -class PrivateKey extends Key { - /// Creates a private key from raw byte array - PrivateKey.fromBytes(Uint8List bytes, String algorithm) - : super(bytes: bytes, algorithm: algorithm); -} diff --git a/lib/src/keyderivation.dart b/lib/src/keyderivation.dart deleted file mode 100644 index 37b67dc..0000000 --- a/lib/src/keyderivation.dart +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval - -import 'dart:typed_data'; - -import 'package:native_crypto/src/digest.dart'; -import 'package:native_crypto/src/exceptions.dart'; -import 'package:native_crypto/src/key.dart'; - -import 'platform.dart'; - -enum KdfAlgorithm { PBKDF2, SCrypt } - -/// Represents a Key Derivation Function -abstract class KeyDerivation { - /// Returns the standard algorithm name for this key derivation function - KdfAlgorithm get algorithm; - - /// Returns the derivated key - SecretKey get key; - - /// Derive key - Future derive(); -} - -class PBKDF2 implements KeyDerivation { - SecretKey _sk; - - int _length; - int _iteration; - HashAlgorithm _hash; - - @override - KdfAlgorithm get algorithm => KdfAlgorithm.PBKDF2; - - @override - SecretKey get key => _sk; - - PBKDF2({ - int keyLength: 32, - int iteration: 10000, - HashAlgorithm hash: HashAlgorithm.SHA256, - }) { - _length = keyLength; - _iteration = iteration; - _hash = hash; - } - - @override - Future derive({String password, String salt}) async { - if (password == null || salt == null) { - throw KeyDerivationException("Password or Salt can't be null!"); - } - if (_hash == null) { - throw KeyDerivationException("Hash Algorithm can't be null!"); - } - - Uint8List derivation = await Platform().pbkdf2( - password, - salt, - keyLength: _length, - iteration: _iteration, - algorithm: _hash, - ); - - _sk = SecretKey.fromBytes(derivation); - } -} - -class Scrypt implements KeyDerivation { - @override - KdfAlgorithm get algorithm => KdfAlgorithm.SCrypt; - - @override - SecretKey get key => throw UnimplementedError(); - - @override - Future derive() { - throw UnimplementedError(); - } -} diff --git a/lib/src/keyspec.dart b/lib/src/keyspec.dart deleted file mode 100644 index 20b43df..0000000 --- a/lib/src/keyspec.dart +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval - -/// This represents security parameters. -abstract class KeySpec { - /// Returns the standard algorithm name for this key - String get algorithm; -} - -class RSAKeySpec implements KeySpec { - int _size; - - String get algorithm => "RSA"; - - /// Returns the size of RSA keys - int get size => _size; - - RSAKeySpec(int size) { - _size = size; - } -} diff --git a/lib/src/platform.dart b/lib/src/platform.dart deleted file mode 100644 index ff3a75f..0000000 --- a/lib/src/platform.dart +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval - -import 'dart:typed_data'; - -import 'package:flutter/services.dart'; - -import 'cipher.dart'; -import 'digest.dart'; -import 'exceptions.dart'; -import 'utils.dart'; - -/// Represents a platform, and is usefull to calling -/// methods from a specific platform. -class Platform { - /// Contains the channel for platform specific code. - static const MethodChannel _channel = const MethodChannel('native.crypto'); - - /// Calls native code. - static Future call(String method, [Map arguments]) { - return _channel.invokeMethod(method, arguments); - } - - /// Calls native code that return list. - static Future> callList(String method, - [Map arguments]) { - return _channel.invokeListMethod(method, arguments); - } - - /// Digests a message with a specific algorithm - /// - /// Takes message and algorithm as parameters. - /// - /// Returns a hash as [Uint8List]. - Future digest( - Uint8List message, - HashAlgorithm algorithm, - ) async { - try { - final Uint8List hash = await call('digest', { - 'message': message, - 'algorithm': algorithm.name, - }); - return hash; - } on PlatformException catch (e) { - throw DigestException(e.code + " : " + e.message); - } - } - - /// Calls native PBKDF2. - /// - /// Takes password and salt as parameters. - /// And optionnally keyLength in bytes, number of iterations and hash algorithm. - /// - /// Returns a key as [Uint8List]. - Future pbkdf2( - String password, - String salt, { - int keyLength: 32, - int iteration: 10000, - HashAlgorithm algorithm: HashAlgorithm.SHA256, - }) async { - try { - final Uint8List key = await call('pbkdf2', { - 'password': password, - 'salt': salt, - 'keyLength': keyLength, - 'iteration': iteration, - 'algorithm': algorithm.name, - }); - return key; - } on PlatformException catch (e) { - throw KeyDerivationException(e.code + " : " + e.message); - } - } - - /// Generates a random key. - /// - /// Takes size in bits. - /// - /// Returns a key as [Uint8List]. - Future keygen(int size) async { - try { - final Uint8List key = await call('keygen', { - 'size': size, - }); - return key; - } on PlatformException catch (e) { - throw KeyGenerationException(e.code + " : " + e.message); - } - } - - /// Generates an RSA key pair. - /// - /// Takes size in bits. - /// - /// Returns a key pair as list of [Uint8List], the public key is the - /// first element, and the private is the last. - Future> rsaKeypairGen(int size) async { - try { - final List keypair = - await callList('rsaKeypairGen', { - 'size': size, - }); - return keypair; - } on PlatformException catch (e) { - throw KeyPairGenerationException(e.code + " : " + e.message); - } - } - - /// Encrypts data with a secret key and algorithm. - /// - /// Takes data, key, algorithm, mode and padding as parameters. - /// - /// Encrypts data and returns cipher text - /// and IV as a list of [Uint8List]. - Future> encrypt( - Uint8List data, - Uint8List key, - CipherAlgorithm algorithm, - CipherParameters parameters, - ) async { - try { - final List payload = - await callList('encrypt', { - 'data': data, - 'key': key, - 'algorithm': algorithm.name, - 'mode': parameters.mode.name, - 'padding': parameters.padding.name, - }); - return payload; - } on PlatformException catch (e) { - throw EncryptionException(e.code + " : " + e.message); - } - } - - /// Decrypts a payload with a secret key and algorithm. - /// - /// The payload must be a list of `Uint8List` - /// with encrypted cipher as first and IV as second member. - Future decrypt( - List payload, - Uint8List key, - CipherAlgorithm algorithm, - CipherParameters parameters, - ) async { - try { - final Uint8List data = await call('decrypt', { - 'payload': payload, - 'key': key, - 'algorithm': algorithm.name, - 'mode': parameters.mode.name, - 'padding': parameters.padding.name, - }); - return data; - } on PlatformException catch (e) { - throw DecryptionException(e.code + " : " + e.message); - } - } -} diff --git a/lib/src/sym/AES.dart b/lib/src/sym/AES.dart deleted file mode 100644 index c352a89..0000000 --- a/lib/src/sym/AES.dart +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2021 -// Author: Hugo Pointcheval - -import 'dart:typed_data'; - -import 'package:native_crypto/native_crypto.dart'; - -import '../cipher.dart'; -import '../exceptions.dart'; -import '../key.dart'; -import '../platform.dart'; -import '../utils.dart'; - -/// Defines all available key sizes. -enum AESKeySize { bits128, bits192, bits256 } - -extension AESKeySizeExtension on AESKeySize { - int get length { - Map table = { - AESKeySize.bits128: 128, - AESKeySize.bits192: 192, - AESKeySize.bits256: 256, - }; - return table[this]; - } -} - -class AESCipher implements Cipher { - SecretKey _sk; - CipherParameters _params; - bool _isInit; - - @override - CipherAlgorithm get algorithm => CipherAlgorithm.AES; - - @override - SecretKey get secretKey => _sk; - - @override - CipherParameters get parameters => _params; - - @override - bool get isInitialized => _isInit; - - @override - List get supportedParameters => [ - CipherParameters(BlockCipherMode.CBC, PlainTextPadding.PKCS5), - ]; - - /// Creates an AES cipher with specified secretKey and mode/padding - AESCipher(SecretKey secretKey, CipherParameters parameters) { - if (secretKey.algorithm != CipherAlgorithm.AES.name) { - List _supportedSizes = [128, 192, 256]; - if (!_supportedSizes.contains(secretKey.encoded.length)) { - throw CipherInitException("Invalid key length!"); - } - } else if (!supportedParameters.contains(parameters)) { - throw CipherInitException("Invalid cipher parameters."); - } - _sk = secretKey; - _params = parameters; - _isInit = true; - } - - /// Generates a secret key of specified size, then creates an AES cipher. - static Future generate( - AESKeySize size, CipherParameters parameters) async { - SecretKey _sk = await SecretKey.generate(size.length, CipherAlgorithm.AES); - return AESCipher(_sk, parameters); - } - - @override - Future encrypt(Uint8List data) async { - if (!_isInit) { - throw CipherInitException('Cipher not properly initialized.'); - } else if (_sk == null || _sk.isEmpty) { - throw CipherInitException('Invalid key size.'); - } - Uint8List dataToEncrypt; - int maxSize = 33554432; - AESCipherText cipherText = AESCipherText.empty(); - // If data is bigger than 32mB -> split in chunks - if (data.length > maxSize) { - int chunkNb = (data.length / maxSize).ceil(); - for (var i = 0; i < chunkNb; i++) { - if (i < (chunkNb - 1)) { - dataToEncrypt = data.sublist(i * maxSize, (i + 1) * maxSize); - } else { - dataToEncrypt = data.sublist(i * maxSize); - } - List c = await Platform() - .encrypt(dataToEncrypt, _sk.encoded, algorithm, _params); - cipherText.append(c[0], c[1]); - } - } else { - List c = - await Platform().encrypt(data, _sk.encoded, algorithm, _params); - cipherText.append(c[0], c[1]); - } - return cipherText; - } - - @override - Future decrypt(CipherText cipherText) async { - if (cipherText.algorithm != CipherAlgorithm.AES) { - throw DecryptionException("This cipher text's algorithm is not AES: " + - cipherText.algorithm.name + - "\nYou must use an AESCipherText."); - } else if (!_isInit) { - throw CipherInitException('Cipher not properly initialized.'); - } else if (_sk == null || _sk.isEmpty) { - throw CipherInitException('Invalid key size.'); - } else if (cipherText.bytes.length != cipherText.iv.length) { - throw DecryptionException( - "This cipher text's bytes chunks length is not the same as iv chunks length"); - } - - BytesBuilder decryptedData = BytesBuilder(); - if (cipherText.size > 1) { - for (var i = 0; i < cipherText.size; i++) { - List payload = [cipherText.bytes[i], cipherText.iv[i]]; - Uint8List d = - await Platform().decrypt(payload, _sk.encoded, algorithm, _params); - decryptedData.add(d); - } - } else { - List payload = [cipherText.bytes[0], cipherText.iv[0]]; - Uint8List d = - await Platform().decrypt(payload, _sk.encoded, algorithm, _params); - decryptedData.add(d); - } - return decryptedData.toBytes(); - } -} - -class AESCipherText implements CipherText { - List _bytes; - List _iv; - - @override - CipherAlgorithm get algorithm => CipherAlgorithm.AES; - - @override - List get bytes => _bytes; - - @override - List get iv => _iv; - - @override - int get size => _bytes.length; - - AESCipherText(Uint8List bytes, Uint8List iv) { - _bytes = List.from([bytes]); - _iv = List.from([iv]); - } - - AESCipherText.from(List bytes, List iv) { - _bytes = bytes; - _iv = iv; - } - - AESCipherText.empty() { - _bytes = []; - _iv = []; - } - - void append(Uint8List bytes, Uint8List iv) { - _bytes.add(bytes); - _iv.add(iv); - } - - /// Returns this ciphertext in [Uint8List] format. - /// - /// Encoding - /// -------- - /// Uint8List encoding is : IV_1 + M_1 + IV_2 + M_2 + ... + IV_n + M_n - /// - /// Where **IV_k** is the IV of the cipher text **M_k** - /// - /// IV is **always** 16 bytes long, And the **M** are all max - /// size (of 33 554 480 bytes) except the last one which is shorter than the others. - Uint8List encode() { - BytesBuilder builder = BytesBuilder(); - for (var i = 0; i < size; i++) { - builder.add(_iv[i]); - builder.add(_bytes[i]); - } - - return builder.toBytes(); - } - - /// Transforms a [Uint8List] to a *NativeCrypto* cipherText. - /// - /// Decoding - /// -------- - /// See the list as a chain of chunks (IV and Messages) - /// `[IV][MESSAGE][IV][MESSAGE] ... [IV][MESSA...]` - /// - /// Chunk length is IV length + Message length = 16 + 33 554 480 bytes - void decode(Uint8List src) { - ByteBuffer buffer = src.buffer; - - int chunkSize = 16 + 33554480; - int chunkNb = (buffer.lengthInBytes / chunkSize).ceil(); - - for (var i = 0; i < chunkNb; i++) { - _iv.add(buffer.asUint8List(i * chunkSize, 16)); - if (i < (chunkNb - 1)) { - _bytes.add(buffer.asUint8List(16 + i * chunkSize, 33554480)); - } else { - _bytes.add(buffer.asUint8List(16 + i * chunkSize)); - } - } - } -} diff --git a/lib/src/utils.dart b/lib/src/utils.dart deleted file mode 100644 index 3bce678..0000000 --- a/lib/src/utils.dart +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2020 -// Author: Hugo Pointcheval - -import 'package:flutter/foundation.dart'; - -import 'cipher.dart'; -import 'digest.dart'; -import 'exceptions.dart'; -import 'kem.dart'; -import 'keyderivation.dart'; - -extension HashAlgorithmExtension on HashAlgorithm { - String get name => describeEnum(this).toLowerCase(); -} - -extension KdfAlgorithmExtension on KdfAlgorithm { - String get name => describeEnum(this).toLowerCase(); -} - -extension CipherAlgorithmExtension on CipherAlgorithm { - String get name => describeEnum(this).toLowerCase(); -} - -extension KemAlgorithmExtension on KemAlgorithm { - String get name => describeEnum(this).toLowerCase(); -} - -extension BlockCipherModeExtension on BlockCipherMode { - String get name => describeEnum(this).toLowerCase(); -} - -extension PlainTextPaddingExtension on PlainTextPadding { - String get name => describeEnum(this).toLowerCase(); -} - -class Utils { - /// Returns [HashAlgorithm] from his name. - static HashAlgorithm getHashAlgorithm(String algorithm) { - String _query = algorithm.toLowerCase(); - for (HashAlgorithm h in HashAlgorithm.values) { - if (_query == h.name) { - return h; - } - } - throw UtilsException("Unknown hash algorithm!"); - } - - /// Returns all available [HashAlgorithm] as String list - static List getAvailableHashAlgorithms() { - List _res = []; - for (HashAlgorithm h in HashAlgorithm.values) { - _res.add(h.name); - } - return _res; - } - - /// Returns [KdfAlgorithm] from his name. - static KdfAlgorithm getKdfAlgorithm(String algorithm) { - String _query = algorithm.toLowerCase(); - for (KdfAlgorithm h in KdfAlgorithm.values) { - if (_query == h.name) { - return h; - } - } - throw UtilsException("Unknown key derivation algorithm!"); - } - - /// Returns all available [KdfAlgorithm] as String list - static List getAvailableKdfAlgorithms() { - List _res = []; - for (KdfAlgorithm h in KdfAlgorithm.values) { - _res.add(h.name); - } - return _res; - } - - /// Returns [CipherAlgorithm] from his name. - static CipherAlgorithm getCipherAlgorithm(String algorithm) { - String _query = algorithm.toLowerCase(); - for (CipherAlgorithm c in CipherAlgorithm.values) { - if (_query == c.name) { - return c; - } - } - throw UtilsException("Unknown cipher algorithm!"); - } - - /// Returns all available [CipherAlgorithm] as String list - static List getAvailableCipherAlgorithms() { - List _res = []; - for (CipherAlgorithm c in CipherAlgorithm.values) { - _res.add(c.name); - } - return _res; - } - - /// Returns [KemAlgorithm] from his name. - static KemAlgorithm getKemAlgorithm(String algorithm) { - String _query = algorithm.toLowerCase(); - for (KemAlgorithm k in KemAlgorithm.values) { - if (_query == k.name) { - return k; - } - } - throw UtilsException("Unknown KEM algorithm!"); - } - - /// Returns all available [KemAlgorithm] as String list - static List getAvailableKemAlgorithms() { - List _res = []; - for (KemAlgorithm k in KemAlgorithm.values) { - _res.add(k.name); - } - return _res; - } - - /// Returns [CipherParameters] from string. - /// - /// For example, `CBC/PKCS5` gives a CipherParameters with - /// CBC mode and PKCS5 as padding. - static CipherParameters getCipherParameters(String parameters) { - List _query = parameters.toLowerCase().split("/"); - BlockCipherMode _mode; - PlainTextPadding _padding; - for (BlockCipherMode b in BlockCipherMode.values) { - if (_query[0] == b.name) { - _mode = b; - } - } - for (PlainTextPadding p in PlainTextPadding.values) { - if (_query[1] == p.name) { - _padding = p; - } - } - if (_mode == null || _padding == null) { - throw UtilsException("Unknown parameters!"); - } else { - return CipherParameters(_mode, _padding); - } - } -} diff --git a/native_crypto/.gitignore b/native_crypto/.gitignore new file mode 100644 index 0000000..9a04984 --- /dev/null +++ b/native_crypto/.gitignore @@ -0,0 +1,389 @@ +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig + +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows + +### Dart ### +# See https://www.dartlang.org/guides/libraries/private-files + +# Files and directories created by pub +.dart_tool/ +.packages +build/ +# If you're building an application, you may want to check-in your pubspec.lock +pubspec.lock + +# Directory created by dartdoc +# If you don't generate documentation locally you can remove this line. +doc/api/ + +# dotenv environment variables file +.env* + +# Avoid committing generated Javascript files: +*.dart.js +*.info.json # Produced by the --dump-info flag. +*.js # When generated by dart2js. Don't specify *.js if your + # project includes source files written in JavaScript. +*.js_ +*.js.deps +*.js.map + +.flutter-plugins +.flutter-plugins-dependencies + +### Dart Patch ### +# dotenv environment variables file +.env + +### Flutter ### +# Flutter/Dart/Pub related +**/doc/api/ +.fvm/ +.pub-cache/ +.pub/ +coverage/ +lib/generated_plugin_registrant.dart +# For library packages, don’t commit the pubspec.lock file. +# Regenerating the pubspec.lock file lets you test your package against the latest compatible versions of its dependencies. +# See https://dart.dev/guides/libraries/private-files#pubspeclock +#pubspec.lock + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/key.properties +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/.last_build_id +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Kotlin ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Swift ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +!.vscode/*.code-snippets + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + diff --git a/.metadata b/native_crypto/.metadata similarity index 82% rename from .metadata rename to native_crypto/.metadata index 0b60be7..50d6e33 100644 --- a/.metadata +++ b/native_crypto/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: f139b11009aeb8ed2a3a3aa8b0066e482709dde3 + revision: cf4400006550b70f28e4b4af815151d1e74846c6 channel: stable project_type: plugin diff --git a/native_crypto/CHANGELOG.md b/native_crypto/CHANGELOG.md new file mode 100644 index 0000000..41cc7d8 --- /dev/null +++ b/native_crypto/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/native_crypto/LICENSE b/native_crypto/LICENSE new file mode 100644 index 0000000..ba75c69 --- /dev/null +++ b/native_crypto/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/native_crypto/README.md b/native_crypto/README.md new file mode 100644 index 0000000..1bf39c0 --- /dev/null +++ b/native_crypto/README.md @@ -0,0 +1,18 @@ +# native_crypto + +A new flutter plugin project. + +## 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. + +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. + +The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported. +To add platforms, run `flutter create -t plugin --platforms .` under the same +directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. diff --git a/native_crypto/analysis_options.yaml b/native_crypto/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/native_crypto/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/.gitignore b/native_crypto/example/.gitignore similarity index 70% rename from example/.gitignore rename to native_crypto/example/.gitignore index ae1f183..0fa6b67 100644 --- a/example/.gitignore +++ b/native_crypto/example/.gitignore @@ -22,6 +22,7 @@ # Flutter/Dart/Pub related **/doc/api/ +**/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins .flutter-plugins-dependencies @@ -33,5 +34,13 @@ # Web related lib/generated_plugin_registrant.dart -# Exceptions to above rules. -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/.metadata b/native_crypto/example/.metadata similarity index 82% rename from example/.metadata rename to native_crypto/example/.metadata index 4adf4bf..ee7f61d 100644 --- a/example/.metadata +++ b/native_crypto/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: f139b11009aeb8ed2a3a3aa8b0066e482709dde3 + revision: cf4400006550b70f28e4b4af815151d1e74846c6 channel: stable project_type: app diff --git a/example/README.md b/native_crypto/example/README.md similarity index 100% rename from example/README.md rename to native_crypto/example/README.md diff --git a/native_crypto/example/analysis_options.yaml b/native_crypto/example/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/native_crypto/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# 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 diff --git a/native_crypto/example/android/.gitignore b/native_crypto/example/android/.gitignore new file mode 100644 index 0000000..6f56801 --- /dev/null +++ b/native_crypto/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/native_crypto/example/android/app/build.gradle similarity index 81% rename from example/android/app/build.gradle rename to native_crypto/example/android/app/build.gradle index f6c24d7..803881a 100644 --- a/example/android/app/build.gradle +++ b/native_crypto/example/android/app/build.gradle @@ -26,24 +26,28 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion flutter.compileSdkVersion - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' } - lintOptions { - disable 'InvalidPackage' + sourceSets { + main.java.srcDirs += 'src/main/kotlin' } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "fr.pointcheval.native_crypto_example" - minSdkVersion 16 - targetSdkVersion 28 + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -61,7 +65,4 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/example/android/app/src/debug/AndroidManifest.xml b/native_crypto/example/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from example/android/app/src/debug/AndroidManifest.xml rename to native_crypto/example/android/app/src/debug/AndroidManifest.xml diff --git a/example/android/app/src/main/AndroidManifest.xml b/native_crypto/example/android/app/src/main/AndroidManifest.xml similarity index 65% rename from example/android/app/src/main/AndroidManifest.xml rename to native_crypto/example/android/app/src/main/AndroidManifest.xml index 6e6213c..c55f004 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/native_crypto/example/android/app/src/main/AndroidManifest.xml @@ -1,21 +1,25 @@ - - + + diff --git a/native_crypto/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt b/native_crypto/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt new file mode 100644 index 0000000..de245bb --- /dev/null +++ b/native_crypto/example/android/app/src/main/kotlin/fr/pointcheval/native_crypto_example/MainActivity.kt @@ -0,0 +1,6 @@ +package fr.pointcheval.native_crypto_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/native_crypto/example/android/app/src/main/res/drawable-v21/launch_background.xml b/native_crypto/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/native_crypto/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/native_crypto/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable/launch_background.xml rename to native_crypto/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/native_crypto/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to native_crypto/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/native_crypto/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to native_crypto/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/native_crypto/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to native_crypto/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/native_crypto/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to native_crypto/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/native_crypto/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to native_crypto/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/native_crypto/example/android/app/src/main/res/values-night/styles.xml b/native_crypto/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..3db14bb --- /dev/null +++ b/native_crypto/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/native_crypto/example/android/app/src/main/res/values/styles.xml b/native_crypto/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..d460d1e --- /dev/null +++ b/native_crypto/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/native_crypto/example/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from example/android/app/src/profile/AndroidManifest.xml rename to native_crypto/example/android/app/src/profile/AndroidManifest.xml diff --git a/example/android/build.gradle b/native_crypto/example/android/build.gradle similarity index 82% rename from example/android/build.gradle rename to native_crypto/example/android/build.gradle index 59baba0..24047dc 100644 --- a/example/android/build.gradle +++ b/native_crypto/example/android/build.gradle @@ -2,11 +2,11 @@ buildscript { ext.kotlin_version = '1.3.50' repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -14,7 +14,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/android/gradle.properties b/native_crypto/example/android/gradle.properties similarity index 78% rename from android/gradle.properties rename to native_crypto/example/android/gradle.properties index 38c8d45..94adc3a 100644 --- a/android/gradle.properties +++ b/native_crypto/example/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true android.useAndroidX=true android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 80% rename from android/gradle/wrapper/gradle-wrapper.properties rename to native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties index 01a286e..bc6a58a 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/native_crypto/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/native_crypto/example/android/settings.gradle b/native_crypto/example/android/settings.gradle new file mode 100644 index 0000000..44e62bc --- /dev/null +++ b/native_crypto/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/ios/.gitignore b/native_crypto/example/ios/.gitignore similarity index 95% rename from example/ios/.gitignore rename to native_crypto/example/ios/.gitignore index e96ef60..7a7f987 100644 --- a/example/ios/.gitignore +++ b/native_crypto/example/ios/.gitignore @@ -1,3 +1,4 @@ +**/dgph *.mode1v3 *.mode2v3 *.moved-aside @@ -18,6 +19,7 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist similarity index 91% rename from example/ios/Flutter/AppFrameworkInfo.plist rename to native_crypto/example/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f7..8d4492f 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/native_crypto/example/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/example/ios/Flutter/Debug.xcconfig b/native_crypto/example/ios/Flutter/Debug.xcconfig similarity index 58% rename from example/ios/Flutter/Debug.xcconfig rename to native_crypto/example/ios/Flutter/Debug.xcconfig index b2f5fae..ec97fc6 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/native_crypto/example/ios/Flutter/Debug.xcconfig @@ -1,3 +1,2 @@ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/native_crypto/example/ios/Flutter/Release.xcconfig similarity index 58% rename from example/ios/Flutter/Release.xcconfig rename to native_crypto/example/ios/Flutter/Release.xcconfig index 88c2914..c4855bf 100644 --- a/example/ios/Flutter/Release.xcconfig +++ b/native_crypto/example/ios/Flutter/Release.xcconfig @@ -1,3 +1,2 @@ #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/native_crypto/example/ios/Podfile b/native_crypto/example/ios/Podfile new file mode 100644 index 0000000..1e8c3c9 --- /dev/null +++ b/native_crypto/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj b/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6571d99 --- /dev/null +++ b/native_crypto/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,484 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 6Z5P8GG96U; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 6Z5P8GG96U; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 6Z5P8GG96U; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/native_crypto/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/native_crypto/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 95% rename from example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to native_crypto/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..c87d15a 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/native_crypto/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ - - - - + + - - + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.swift b/native_crypto/example/ios/Runner/AppDelegate.swift similarity index 100% rename from example/ios/Runner/AppDelegate.swift rename to native_crypto/example/ios/Runner/AppDelegate.swift diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to native_crypto/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/native_crypto/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to native_crypto/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/native_crypto/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/Main.storyboard rename to native_crypto/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/example/ios/Runner/Info.plist b/native_crypto/example/ios/Runner/Info.plist similarity index 95% rename from example/ios/Runner/Info.plist rename to native_crypto/example/ios/Runner/Info.plist index 24b0528..58d9657 100644 --- a/example/ios/Runner/Info.plist +++ b/native_crypto/example/ios/Runner/Info.plist @@ -4,6 +4,8 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Native Crypto CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -40,6 +42,6 @@ UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance - + diff --git a/native_crypto/example/ios/Runner/Runner-Bridging-Header.h b/native_crypto/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/native_crypto/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/native_crypto/example/lib/main.dart b/native_crypto/example/lib/main.dart new file mode 100644 index 0000000..322f70f --- /dev/null +++ b/native_crypto/example/lib/main.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:native_crypto/native_crypto.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _platformVersion = 'Unknown'; + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. + try { + platformVersion = + await NativeCrypto.platformVersion ?? 'Unknown platform version'; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on: $_platformVersion\n'), + ), + ), + ); + } +} diff --git a/example/pubspec.yaml b/native_crypto/example/pubspec.yaml similarity index 55% rename from example/pubspec.yaml rename to native_crypto/example/pubspec.yaml index 21602e9..e6e3ec3 100644 --- a/example/pubspec.yaml +++ b/native_crypto/example/pubspec.yaml @@ -1,25 +1,45 @@ name: native_crypto_example description: Demonstrates how to use the native_crypto plugin. -version: 1.0.0+1 -publish_to: 'none' + +# 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 environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.15.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 +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter + 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: ^0.1.2 + cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter - native_crypto: - path: ../ + # 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: ^1.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -34,8 +54,8 @@ flutter: # To add assets to your application, add an assets section, like this: # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. diff --git a/example/test/widget_test.dart b/native_crypto/example/test/widget_test.dart similarity index 87% rename from example/test/widget_test.dart rename to native_crypto/example/test/widget_test.dart index 8e47dd6..ffdf51c 100644 --- a/example/test/widget_test.dart +++ b/native_crypto/example/test/widget_test.dart @@ -13,13 +13,13 @@ import 'package:native_crypto_example/main.dart'; void main() { testWidgets('Verify Platform version', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + await tester.pumpWidget(const MyApp()); // Verify that platform version is retrieved. expect( find.byWidgetPredicate( (Widget widget) => widget is Text && - widget.data.startsWith('Running on:'), + widget.data!.startsWith('Running on:'), ), findsOneWidget, ); diff --git a/native_crypto/lib/native_crypto.dart b/native_crypto/lib/native_crypto.dart new file mode 100644 index 0000000..1036e3a --- /dev/null +++ b/native_crypto/lib/native_crypto.dart @@ -0,0 +1,17 @@ +// You have generated a new plugin project without +// specifying the `--platforms` flag. A plugin project supports no platforms is generated. +// To add platforms, run `flutter create -t plugin --platforms .` under the same +// directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. + +import 'dart:async'; + +import 'package:flutter/services.dart'; + +class NativeCrypto { + static const MethodChannel _channel = MethodChannel('native_crypto'); + + static Future get platformVersion async { + final String? version = await _channel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/native_crypto/pubspec.yaml b/native_crypto/pubspec.yaml new file mode 100644 index 0000000..3baf2be --- /dev/null +++ b/native_crypto/pubspec.yaml @@ -0,0 +1,28 @@ +name: native_crypto +description: Fast and secure cryptography for Flutter. +version: 0.0.7 +publish_to: 'none' + +environment: + sdk: ">=2.15.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + + native_crypto_ios: + path: ../native_crypto_ios + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.4 + +flutter: + plugin: + platforms: + # android: + # default_package: native_crypto_android + ios: + default_package: native_crypto_ios \ No newline at end of file diff --git a/test/native_crypto_test.dart b/native_crypto/test/native_crypto_test.dart similarity index 72% rename from test/native_crypto_test.dart rename to native_crypto/test/native_crypto_test.dart index 1787622..5caceab 100644 --- a/test/native_crypto_test.dart +++ b/native_crypto/test/native_crypto_test.dart @@ -1,5 +1,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:native_crypto/native_crypto.dart'; void main() { const MethodChannel channel = MethodChannel('native_crypto'); @@ -15,4 +16,8 @@ void main() { tearDown(() { channel.setMockMethodCallHandler(null); }); + + test('getPlatformVersion', () async { + expect(await NativeCrypto.platformVersion, '42'); + }); } diff --git a/.gitignore b/native_crypto_ios/.gitignore similarity index 61% rename from .gitignore rename to native_crypto_ios/.gitignore index cea0a9c..23f216c 100644 --- a/.gitignore +++ b/native_crypto_ios/.gitignore @@ -1,107 +1,7 @@ # File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig -# Created by https://www.gitignore.io/api/windows,visualstudiocode,android,androidstudio,cocoapods,dart,flutter,intellij+all,kotlin,macos,swift,xcode,xcodeinjection -# Edit at https://www.gitignore.io/?templates=windows,visualstudiocode,android,androidstudio,cocoapods,dart,flutter,intellij+all,kotlin,macos,swift,xcode,xcodeinjection - -### Android ### -# Built application files -*.apk -*.ap_ -*.aab - -# Files for the ART/Dalvik VM -*.dex - -# Java class files -*.class - -# Generated files -bin/ -gen/ -out/ -release/ - -# Gradle files -.gradle/ -build/ - -# Local configuration file (sdk path, etc) -local.properties - -# Proguard folder generated by Eclipse -proguard/ - -# Log Files -*.log - -# Android Studio Navigation editor temp files -.navigation/ - -# Android Studio captures folder -captures/ - -# IntelliJ -*.iml -.idea/workspace.xml -.idea/tasks.xml -.idea/gradle.xml -.idea/assetWizardSettings.xml -.idea/dictionaries -.idea/libraries -# Android Studio 3 in .gitignore file. -.idea/caches -.idea/modules.xml -# Comment next line if keeping position of elements in Navigation Editor is relevant for you -.idea/navEditor.xml - -# Keystore files -# Uncomment the following lines if you do not want to check your keystore files in. -#*.jks -#*.keystore - -# External native build folder generated in Android Studio 2.2 and later -.externalNativeBuild - -# Google Services (e.g. APIs or Firebase) -# google-services.json - -# Freeline -freeline.py -freeline/ -freeline_project_description.json - -# fastlane -fastlane/report.xml -fastlane/Preview.html -fastlane/screenshots -fastlane/test_output -fastlane/readme.md - -# Version control -vcs.xml - -# lint -lint/intermediates/ -lint/generated/ -lint/outputs/ -lint/tmp/ -# lint/reports/ - -### Android Patch ### -gen-external-apklibs -output.json - -# Replacement of .externalNativeBuild directories introduced -# with Android Studio 3.5. -.cxx/ - -### CocoaPods ### -## CocoaPods GitIgnore Template - -# CocoaPods - Only use to conserve bandwidth / Save time on Pushing -# - Also handy if you have a large number of dependant pods -# - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE -Pods/ +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows ### Dart ### # See https://www.dartlang.org/guides/libraries/private-files @@ -109,6 +9,7 @@ Pods/ # Files and directories created by pub .dart_tool/ .packages +build/ # If you're building an application, you may want to check-in your pubspec.lock pubspec.lock @@ -116,6 +17,9 @@ pubspec.lock # If you don't generate documentation locally you can remove this line. doc/api/ +# dotenv environment variables file +.env* + # Avoid committing generated Javascript files: *.dart.js *.info.json # Produced by the --dump-info flag. @@ -125,13 +29,25 @@ doc/api/ *.js.deps *.js.map +.flutter-plugins +.flutter-plugins-dependencies + +### Dart Patch ### +# dotenv environment variables file +.env + ### Flutter ### # Flutter/Dart/Pub related **/doc/api/ -.flutter-plugins -.flutter-plugins-dependencies +.fvm/ .pub-cache/ .pub/ +coverage/ +lib/generated_plugin_registrant.dart +# For library packages, don’t commit the pubspec.lock file. +# Regenerating the pubspec.lock file lets you test your package against the latest compatible versions of its dependencies. +# See https://dart.dev/guides/libraries/private-files#pubspeclock +#pubspec.lock # Android related **/android/**/gradle-wrapper.jar @@ -139,6 +55,7 @@ doc/api/ **/android/captures/ **/android/gradlew **/android/gradlew.bat +**/android/key.properties **/android/local.properties **/android/**/GeneratedPluginRegistrant.java @@ -159,12 +76,15 @@ doc/api/ **/ios/**/profile **/ios/**/xcuserdata **/ios/.generated/ +**/ios/Flutter/.last_build_id **/ios/Flutter/App.framework **/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec **/ios/Flutter/Generated.xcconfig **/ios/Flutter/app.flx **/ios/Flutter/app.zip **/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh **/ios/ServiceDefinitions.json **/ios/Runner/GeneratedPluginRegistrant.* @@ -176,7 +96,7 @@ doc/api/ !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages ### Intellij+all ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff @@ -186,6 +106,9 @@ doc/api/ .idea/**/dictionaries .idea/**/shelf +# AWS User-specific +.idea/**/aws.xml + # Generated files .idea/**/contentModel.xml @@ -206,6 +129,9 @@ doc/api/ # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml # .idea/modules.xml # .idea/*.iml # .idea/modules @@ -222,6 +148,7 @@ cmake-build-*/ *.iws # IntelliJ +out/ # mpeltonen/sbt-idea plugin .idea_modules/ @@ -252,6 +179,7 @@ fabric.properties # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 +*.iml modules.xml .idea/misc.xml *.ipr @@ -261,8 +189,10 @@ modules.xml ### Kotlin ### # Compiled class file +*.class # Log file +*.log # BlueJ files *.ctxt @@ -282,6 +212,21 @@ modules.xml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + ### macOS ### # General .DS_Store @@ -291,6 +236,7 @@ hs_err_pid* # Icon must end with two \r Icon + # Thumbnails ._* @@ -315,10 +261,16 @@ Temporary Items # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore -## Build generated -DerivedData/ +## User settings +xcuserdata/ -## Various settings +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +DerivedData/ +*.moved-aside *.pbxuser !default.pbxuser *.mode1v3 @@ -327,15 +279,11 @@ DerivedData/ !default.mode2v3 *.perspectivev3 !default.perspectivev3 -xcuserdata/ - -## Other -*.moved-aside -*.xccheckout -*.xcscmblueprint ## Obj-C/Swift specific *.hmap + +## App packaging *.ipa *.dSYM.zip *.dSYM @@ -349,9 +297,12 @@ playground.xcworkspace # Packages/ # Package.pins # Package.resolved +# *.xcodeproj +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + .build/ -# Add this line if you want to avoid checking in Xcode SPM integration. -# .swiftpm/xcode # CocoaPods # We recommend against adding the Pods directory to your .gitignore. However @@ -365,19 +316,22 @@ playground.xcworkspace # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts -Carthage/Build +Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control +fastlane/report.xml +fastlane/Preview.html fastlane/screenshots/**/*.png +fastlane/test_output # Code Injection # After new code Injection tools there's a generated folder /iOSInjectionProject @@ -386,16 +340,23 @@ fastlane/screenshots/**/*.png iOSInjectionProject/ ### VisualStudioCode ### -.vscode .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ ### VisualStudioCode Patch ### # Ignore all local history of files .history +.ionide + +# Support for Project snippet scope +!.vscode/*.code-snippets ### Windows ### # Windows thumbnail cache files @@ -423,122 +384,7 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -### Xcode ### -# Xcode -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - -## User settings - -## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) - -## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) - -## Xcode Patch -*.xcodeproj/* -!*.xcodeproj/project.pbxproj -!*.xcodeproj/xcshareddata/ -!*.xcworkspace/contents.xcworkspacedata -/*.gcno - -### Xcode Patch ### -**/xcshareddata/WorkspaceSettings.xcsettings - -### XcodeInjection ### -# Code Injection -# After new code Injection tools there's a generated folder /iOSInjectionProject -# https://github.com/johnno1962/injectionforxcode - - -### AndroidStudio ### -# Covers files to be ignored for android development using Android Studio. - -# Built application files - -# Files for the ART/Dalvik VM - -# Java class files - -# Generated files - -# Gradle files -.gradle - -# Signing files -.signing/ - -# Local configuration file (sdk path, etc) - -# Proguard folder generated by Eclipse - -# Log Files - -# Android Studio -/*/build/ -/*/local.properties -/*/out -/*/*/build -/*/*/production -*~ -*.swp - -# Android Patch - -# External native build folder generated in Android Studio 2.2 and later - -# NDK -obj/ - -# IntelliJ IDEA -/out/ - -# User-specific configurations -.idea/caches/ -.idea/libraries/ -.idea/shelf/ -.idea/.name -.idea/compiler.xml -.idea/copyright/profiles_settings.xml -.idea/encodings.xml -.idea/scopes/scope_settings.xml -.idea/vcs.xml -.idea/jsLibraryMappings.xml -.idea/datasources.xml -.idea/dataSources.ids -.idea/sqlDataSources.xml -.idea/dynamic.xml -.idea/uiDesigner.xml - -# OS-specific files -.DS_Store? - -# Legacy Eclipse project files -.classpath -.project -.cproject -.settings/ - -# Mobile Tools for Java (J2ME) - -# Package Files # - -# virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) - -## Plugin-specific files: - -# mpeltonen/sbt-idea plugin - -# JIRA plugin - -# Mongo Explorer plugin -.idea/mongoSettings.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) - -### AndroidStudio Patch ### - -!/gradle/wrapper/gradle-wrapper.jar - -# End of https://www.gitignore.io/api/windows,visualstudiocode,android,androidstudio,cocoapods,dart,flutter,intellij+all,kotlin,macos,swift,xcode,xcodeinjection +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows # Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) diff --git a/native_crypto_ios/.metadata b/native_crypto_ios/.metadata new file mode 100644 index 0000000..50d6e33 --- /dev/null +++ b/native_crypto_ios/.metadata @@ -0,0 +1,10 @@ +# 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. + +version: + revision: cf4400006550b70f28e4b4af815151d1e74846c6 + channel: stable + +project_type: plugin diff --git a/native_crypto_ios/CHANGELOG.md b/native_crypto_ios/CHANGELOG.md new file mode 100644 index 0000000..41cc7d8 --- /dev/null +++ b/native_crypto_ios/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/LICENSE b/native_crypto_ios/LICENSE similarity index 96% rename from LICENSE rename to native_crypto_ios/LICENSE index 3a5f8fe..a9f90ec 100644 --- a/LICENSE +++ b/native_crypto_ios/LICENSE @@ -1,4 +1,4 @@ -native_crypto +NativeCrypto - iOS Implementation MIT License diff --git a/native_crypto_ios/README.md b/native_crypto_ios/README.md new file mode 100644 index 0000000..302c828 --- /dev/null +++ b/native_crypto_ios/README.md @@ -0,0 +1,15 @@ +# native_crypto_ios + +A new flutter plugin project. + +## 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. + +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. + diff --git a/native_crypto_ios/example/.gitignore b/native_crypto_ios/example/.gitignore new file mode 100644 index 0000000..0fa6b67 --- /dev/null +++ b/native_crypto_ios/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/native_crypto_ios/example/.metadata b/native_crypto_ios/example/.metadata new file mode 100644 index 0000000..ee7f61d --- /dev/null +++ b/native_crypto_ios/example/.metadata @@ -0,0 +1,10 @@ +# 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. + +version: + revision: cf4400006550b70f28e4b4af815151d1e74846c6 + channel: stable + +project_type: app diff --git a/native_crypto_ios/example/README.md b/native_crypto_ios/example/README.md new file mode 100644 index 0000000..340d8c1 --- /dev/null +++ b/native_crypto_ios/example/README.md @@ -0,0 +1,16 @@ +# native_crypto_ios_example + +Demonstrates how to use the native_crypto_ios plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +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. diff --git a/native_crypto_ios/example/analysis_options.yaml b/native_crypto_ios/example/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/native_crypto_ios/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# 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 diff --git a/native_crypto_ios/example/ios/.gitignore b/native_crypto_ios/example/ios/.gitignore new file mode 100644 index 0000000..7a7f987 --- /dev/null +++ b/native_crypto_ios/example/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/native_crypto_ios/example/ios/Flutter/AppFrameworkInfo.plist b/native_crypto_ios/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 0000000..8d4492f --- /dev/null +++ b/native_crypto_ios/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/native_crypto_ios/example/ios/Flutter/Debug.xcconfig b/native_crypto_ios/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000..ec97fc6 --- /dev/null +++ b/native_crypto_ios/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/native_crypto_ios/example/ios/Flutter/Release.xcconfig b/native_crypto_ios/example/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000..c4855bf --- /dev/null +++ b/native_crypto_ios/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/native_crypto_ios/example/ios/Podfile b/native_crypto_ios/example/ios/Podfile new file mode 100644 index 0000000..1e8c3c9 --- /dev/null +++ b/native_crypto_ios/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/native_crypto_ios/example/ios/Podfile.lock b/native_crypto_ios/example/ios/Podfile.lock new file mode 100644 index 0000000..d6ba796 --- /dev/null +++ b/native_crypto_ios/example/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - native_crypto_ios (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - native_crypto_ios (from `.symlinks/plugins/native_crypto_ios/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + native_crypto_ios: + :path: ".symlinks/plugins/native_crypto_ios/ios" + +SPEC CHECKSUMS: + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + native_crypto_ios: 01f5aa926eb715d08259fd20bb951ba0f69c4e74 + +PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c + +COCOAPODS: 1.10.1 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/native_crypto_ios/example/ios/Runner.xcodeproj/project.pbxproj similarity index 88% rename from example/ios/Runner.xcodeproj/project.pbxproj rename to native_crypto_ios/example/ios/Runner.xcodeproj/project.pbxproj index 76a945e..7156cef 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/native_crypto_ios/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,17 +3,17 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 20AC864B3037BB896BD381B1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 056766980834B5FAC1C4D331 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - D294241BEFD2D3EF500C0577 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6CB4DB73769DFCBF23FCC12 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,9 +30,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 056766980834B5FAC1C4D331 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1180301722C337B6C625E46F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2DBF05A146610778D425B9D9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 2A0F0FD6D80A80663D317892 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -44,9 +46,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C6CB4DB73769DFCBF23FCC12 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D3EC88B33A6F35C67BAC8349 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - FB0023B75BB4BD74C9F8D280 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + EC40AF9C4AC2A38A0B8CC0E6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,19 +54,22 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D294241BEFD2D3EF500C0577 /* Pods_Runner.framework in Frameworks */, + 20AC864B3037BB896BD381B1 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 8C27A9C4EB5E71366C317BF8 /* Frameworks */ = { + 3024D600AF0E0DED3BB44E03 /* Pods */ = { isa = PBXGroup; children = ( - C6CB4DB73769DFCBF23FCC12 /* Pods_Runner.framework */, + EC40AF9C4AC2A38A0B8CC0E6 /* Pods-Runner.debug.xcconfig */, + 2A0F0FD6D80A80663D317892 /* Pods-Runner.release.xcconfig */, + 1180301722C337B6C625E46F /* Pods-Runner.profile.xcconfig */, ); - name = Frameworks; + name = Pods; + path = Pods; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { @@ -86,8 +89,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 9F5A6E791DB57C1B2E0BF33E /* Pods */, - 8C27A9C4EB5E71366C317BF8 /* Frameworks */, + 3024D600AF0E0DED3BB44E03 /* Pods */, + F589250F1A900F4BC6E4BB56 /* Frameworks */, ); sourceTree = ""; }; @@ -106,7 +109,6 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, @@ -115,21 +117,12 @@ path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 9F5A6E791DB57C1B2E0BF33E /* Pods */ = { + F589250F1A900F4BC6E4BB56 /* Frameworks */ = { isa = PBXGroup; children = ( - FB0023B75BB4BD74C9F8D280 /* Pods-Runner.debug.xcconfig */, - 2DBF05A146610778D425B9D9 /* Pods-Runner.release.xcconfig */, - D3EC88B33A6F35C67BAC8349 /* Pods-Runner.profile.xcconfig */, + 056766980834B5FAC1C4D331 /* Pods_Runner.framework */, ); - path = Pods; + name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ @@ -139,14 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - D72ACB84F79B4AD23991D136 /* [CP] Check Pods Manifest.lock */, + CE2C3E978245C5FF80E431AC /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - FF811A27CCC4B4D5EBD756CE /* [CP] Embed Pods Frameworks */, + ED05A5A0A8AF0FA8F71C2C2B /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -163,18 +156,17 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; - ORGANIZATIONNAME = "The Chromium Authors"; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = 6Z5P8GG96U; LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; + compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -234,7 +226,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - D72ACB84F79B4AD23991D136 /* [CP] Check Pods Manifest.lock */ = { + CE2C3E978245C5FF80E431AC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -256,15 +248,17 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - FF811A27CCC4B4D5EBD756CE /* [CP] Embed Pods Frameworks */ = { + ED05A5A0A8AF0FA8F71C2C2B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; - outputPaths = ( + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -346,7 +340,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -364,18 +358,12 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 6Z5P8GG96U; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoFlutterExample; + PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoIosExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -430,7 +418,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -479,11 +467,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -498,18 +487,12 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 6Z5P8GG96U; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoFlutterExample; + PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoIosExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -527,18 +510,12 @@ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 6Z5P8GG96U; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Flutter", - ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoFlutterExample; + PRODUCT_BUNDLE_IDENTIFIER = fr.pointcheval.nativeCryptoIosExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/native_crypto_ios/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/native_crypto_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/native_crypto_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..c87d15a --- /dev/null +++ b/native_crypto_ios/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/native_crypto_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to native_crypto_ios/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/native_crypto_ios/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/native_crypto_ios/example/ios/Runner/AppDelegate.swift b/native_crypto_ios/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/native_crypto_ios/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d36b1fa --- /dev/null +++ b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 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 literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000..89c2725 --- /dev/null +++ b/native_crypto_ios/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/native_crypto_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/native_crypto_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f2e259c --- /dev/null +++ b/native_crypto_ios/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/native_crypto_ios/example/ios/Runner/Base.lproj/Main.storyboard b/native_crypto_ios/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000..f3c2851 --- /dev/null +++ b/native_crypto_ios/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/native_crypto_ios/example/ios/Runner/Info.plist b/native_crypto_ios/example/ios/Runner/Info.plist new file mode 100644 index 0000000..292a791 --- /dev/null +++ b/native_crypto_ios/example/ios/Runner/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Native Crypto Ios + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + native_crypto_ios_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/native_crypto_ios/example/ios/Runner/Runner-Bridging-Header.h b/native_crypto_ios/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..308a2a5 --- /dev/null +++ b/native_crypto_ios/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/native_crypto_ios/example/lib/main.dart b/native_crypto_ios/example/lib/main.dart new file mode 100644 index 0000000..6636074 --- /dev/null +++ b/native_crypto_ios/example/lib/main.dart @@ -0,0 +1,79 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _platformVersion = 'Unknown'; + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + NativeCryptoPlatform _nativeCryptoPlatform = NativeCryptoPlatform.instance; + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. + Uint8List? sk = await _nativeCryptoPlatform.generateSecretKey(256); + print(sk ?? 'null'); + + Uint8List? ciphertext = await _nativeCryptoPlatform.encrypt(Uint8List.fromList("abc".codeUnits), sk!, "aes"); + print(ciphertext ?? 'null'); + + Uint8List? plaintext = await _nativeCryptoPlatform.decrypt(ciphertext!, sk, "aes"); + print(plaintext ?? 'null'); + + Uint8List? kp = await _nativeCryptoPlatform.generateKeyPair(); + print(kp!.sublist(0, 31)); + print(kp.sublist(32).length); + + Uint8List? sharedSecret = await _nativeCryptoPlatform.generateSharedSecretKey(Uint8List.fromList("salt".codeUnits), 32, kp.sublist(0, 31), kp.sublist(32), "sha256"); + + try { + platformVersion = 'Unknown platform version'; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Center( + child: Text('Running on: $_platformVersion\n'), + ), + ), + ); + } +} diff --git a/native_crypto_ios/example/pubspec.yaml b/native_crypto_ios/example/pubspec.yaml new file mode 100644 index 0000000..ab0098f --- /dev/null +++ b/native_crypto_ios/example/pubspec.yaml @@ -0,0 +1,87 @@ +name: native_crypto_ios_example +description: Demonstrates how to use the native_crypto_ios 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 + +environment: + sdk: ">=2.15.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 +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + native_crypto_ios: + # When depending on this package from a real application you should use: + # native_crypto_ios: ^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: ../ + + native_crypto_platform_interface: + path: ../../native_crypto_platform_interface + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + 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: ^1.0.0 + +# 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. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/native_crypto_ios/example/test/widget_test.dart b/native_crypto_ios/example/test/widget_test.dart new file mode 100644 index 0000000..75f369c --- /dev/null +++ b/native_crypto_ios/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:native_crypto_ios_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => widget is Text && + widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/ios/.gitignore b/native_crypto_ios/ios/.gitignore similarity index 95% rename from ios/.gitignore rename to native_crypto_ios/ios/.gitignore index aa479fd..0c88507 100644 --- a/ios/.gitignore +++ b/native_crypto_ios/ios/.gitignore @@ -34,4 +34,5 @@ Icon? .tags* /Flutter/Generated.xcconfig +/Flutter/ephemeral/ /Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Assets/.gitkeep b/native_crypto_ios/ios/Assets/.gitkeep similarity index 100% rename from ios/Assets/.gitkeep rename to native_crypto_ios/ios/Assets/.gitkeep diff --git a/native_crypto_ios/ios/Classes/Cipher.swift b/native_crypto_ios/ios/Classes/Cipher.swift new file mode 100644 index 0000000..7204680 --- /dev/null +++ b/native_crypto_ios/ios/Classes/Cipher.swift @@ -0,0 +1,53 @@ +/** + * Author: Hugo Pointcheval + * Email: git@pcl.ovh + * ----- + * File: Cipher.swift + * Created Date: 25/12/2021 18:31:28 + * Last Modified: 25/12/2021 18:38:53 + * ----- + * Copyright (c) 2021 + */ + +import Foundation +import CryptoKit + +class AESCipher { + /// Encrypts plaintext with key using AES GCM + @available(iOS 13.0, *) + static func encrypt(plaintext: Data, key: Data) -> Data? { + let symmetricKey = SymmetricKey.init(data: key) + let encrypted = try? AES.GCM.seal(plaintext, using: symmetricKey) + return encrypted?.combined + } + + /// Decrypts ciphertext with key using AES GCM + @available(iOS 13.0, *) + static func decrypt(ciphertext: Data, key: Data) -> Data? { + let symmetricKey = SymmetricKey.init(data: key) + let sealedBox = try? AES.GCM.SealedBox(combined: ciphertext) + if (sealedBox == nil) { return nil } + let decryptedData = try? AES.GCM.open(sealedBox!, using: symmetricKey) + return decryptedData + } +} + +class CHACHACipher { + /// Encrypts plaintext with key using CHACHAPOLY + @available(iOS 13.0, *) + static func encrypt(plaintext: Data, key: Data) -> Data? { + let symmetricKey = SymmetricKey.init(data: key) + let encrypted = try? ChaChaPoly.seal(plaintext, using: symmetricKey) + return encrypted?.combined + } + + /// Decrypts ciphertext with key using CHACHAPOLY + @available(iOS 13.0, *) + static func decrypt(ciphertext: Data, key: Data) -> Data? { + let symmetricKey = SymmetricKey.init(data: key) + let sealedBox = try? ChaChaPoly.SealedBox(combined: ciphertext) + if (sealedBox == nil) { return nil } + let decryptedData = try? ChaChaPoly.open(sealedBox!, using: symmetricKey) + return decryptedData + } +} diff --git a/native_crypto_ios/ios/Classes/Hash.swift b/native_crypto_ios/ios/Classes/Hash.swift new file mode 100644 index 0000000..94990ec --- /dev/null +++ b/native_crypto_ios/ios/Classes/Hash.swift @@ -0,0 +1,43 @@ +/** + * Author: Hugo Pointcheval + * Email: git@pcl.ovh + * ----- + * File: Hash.swift + * Created Date: 25/12/2021 18:31:11 + * Last Modified: 25/12/2021 18:38:20 + * ----- + * Copyright (c) 2021 + */ + +import Foundation +import CommonCrypto +import CryptoKit + +enum HashAlgorithm: String { + case HashSHA256 = "sha256" + case HashSHA384 = "sha384" + case HashSHA512 = "sha512" + + var commonCrypto: UInt32 { + switch self { + case .HashSHA256: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256) + case .HashSHA384: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA384) + case .HashSHA512: return CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512) + } + } +} + +@available(iOS 13.0, *) +class Hash { + /// Hash a message with a specified HashAlgorithm + static func digest(data: Data, algorithm: HashAlgorithm) -> Data { + switch algorithm { + case .HashSHA256: + return Data(SHA256.hash(data: data)) + case .HashSHA384: + return Data(SHA384.hash(data: data)) + case .HashSHA512: + return Data(SHA512.hash(data: data)) + } + } +} diff --git a/native_crypto_ios/ios/Classes/KEM.swift b/native_crypto_ios/ios/Classes/KEM.swift new file mode 100644 index 0000000..cf3a27b --- /dev/null +++ b/native_crypto_ios/ios/Classes/KEM.swift @@ -0,0 +1,78 @@ +/** + * Author: Hugo Pointcheval + * Email: git@pcl.ovh + * ----- + * File: KEM.swift + * Created Date: 25/12/2021 18:31:48 + * Last Modified: 25/12/2021 18:40:00 + * ----- + * Copyright (c) 2021 + */ + +import Foundation +import CryptoKit + +class KeyPair { + /// Generate a keypair. + @available(iOS 13.0, *) + static func fromCurve() -> Data { + let sk = P256.KeyAgreement.PrivateKey() + var kp = sk.rawRepresentation + kp.append(contentsOf: sk.publicKey.rawRepresentation) + return kp; + } + + /// Import private key from Data + @available(iOS 13.0, *) + static func importPrivateKey(privateKey: Data) throws -> P256.KeyAgreement.PrivateKey { + let sk = try P256.KeyAgreement.PrivateKey(rawRepresentation: privateKey) + + return sk; + } + + /// Import public key from Data + @available(iOS 13.0, *) + static func importPublicKey(publicKey: Data) throws -> P256.KeyAgreement.PublicKey { + let pk = try P256.KeyAgreement.PublicKey(rawRepresentation: publicKey) + + return pk; + } +} + +class ECDH { + /// Generate a shared secret with your private key and other party public key. + @available(iOS 13.0, *) + static func generateSharedSecretKey(salt: Data, hash: HashAlgorithm, keyBytesCount: Int ,privateKey: Data, publicKey: Data) -> Data? { + let sk = try? KeyPair.importPrivateKey(privateKey: privateKey) + if (sk == nil) {return nil} + + let pk = try? KeyPair.importPublicKey(publicKey: publicKey) + if (pk == nil) {return nil} + + let secret = try? sk!.sharedSecretFromKeyAgreement(with: pk!) + + switch hash { + case .HashSHA256: + let key = secret?.hkdfDerivedSymmetricKey(using: SHA256.self, salt: salt, sharedInfo: Data(), outputByteCount: keyBytesCount) + if (key == nil) { + return nil + } else { + return Key.toBytes(key: key!) + } + case .HashSHA384: + let key = secret?.hkdfDerivedSymmetricKey(using: SHA384.self, salt: salt, sharedInfo: Data(), outputByteCount: keyBytesCount) + if (key == nil) { + return nil + } else { + return Key.toBytes(key: key!) + } + case .HashSHA512: + let key = secret?.hkdfDerivedSymmetricKey(using: SHA512.self, salt: salt, sharedInfo: Data(), outputByteCount: keyBytesCount) + if (key == nil) { + return nil + } else { + return Key.toBytes(key: key!) + } + } + } +} diff --git a/native_crypto_ios/ios/Classes/Key.swift b/native_crypto_ios/ios/Classes/Key.swift new file mode 100644 index 0000000..b113df5 --- /dev/null +++ b/native_crypto_ios/ios/Classes/Key.swift @@ -0,0 +1,62 @@ +/** + * Author: Hugo Pointcheval + * Email: git@pcl.ovh + * ----- + * File: KDF.swift + * Created Date: 25/12/2021 17:45:28 + * Last Modified: 25/12/2021 17:45:38 + * ----- + * Copyright (c) 2021 + */ + +import Foundation +import CryptoKit +import CommonCrypto + +class Key { + /// Generate secret key of a specified length + @available(iOS 13.0, *) + static func fromSecureRandom(bitsCount : Int) -> Data { + let symmetricKey = SymmetricKey.init(size: SymmetricKeySize(bitCount: bitsCount)) + return toBytes(key: symmetricKey) + } + + /// Encode key as Data + @available(iOS 13.0, *) + static func toBytes(key: SymmetricKey) -> Data { + let keyBytes = key.withUnsafeBytes + { + return Data(Array($0)) + } + return keyBytes + } + + /// Derive a new secret key with PBKDF2 algorithm + static func fromPBKDF2(password: String, salt: String, keyBytesCount: Int, iterations: Int, algorithm: HashAlgorithm) -> Data? { + let passwordData = password.data(using: .utf8)! + let saltData = salt.data(using: .utf8)! + + var derivedKeyData = Data(repeating: 0, count: keyBytesCount) + let localDerivedKeyData = derivedKeyData + + let status = derivedKeyData.withUnsafeMutableBytes { (derivedKeyBytes: UnsafeMutableRawBufferPointer) in + saltData.withUnsafeBytes { (saltBytes: UnsafeRawBufferPointer) in + CCKeyDerivationPBKDF( + CCPBKDFAlgorithm(kCCPBKDF2), + password, + passwordData.count, + saltBytes.bindMemory(to: UInt8.self).baseAddress, + saltData.count, + algorithm.commonCrypto, + UInt32(iterations), + derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress, + localDerivedKeyData.count) + } + } + if (status != kCCSuccess) { + return nil; + } + + return derivedKeyData + } +} diff --git a/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.h b/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.h new file mode 100644 index 0000000..4fa1376 --- /dev/null +++ b/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface NativeCryptoIosPlugin : NSObject +@end diff --git a/ios/Classes/NativeCryptoPlugin.m b/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.m similarity index 52% rename from ios/Classes/NativeCryptoPlugin.m rename to native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.m index c674cf7..e252c0b 100644 --- a/ios/Classes/NativeCryptoPlugin.m +++ b/native_crypto_ios/ios/Classes/NativeCryptoIosPlugin.m @@ -1,15 +1,15 @@ -#import "NativeCryptoPlugin.h" -#if __has_include() -#import +#import "NativeCryptoIosPlugin.h" +#if __has_include() +#import #else // Support project import fallback if the generated compatibility header // is not copied when this plugin is created as a library. // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "native_crypto-Swift.h" +#import "native_crypto_ios-Swift.h" #endif -@implementation NativeCryptoPlugin +@implementation NativeCryptoIosPlugin + (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftNativeCryptoPlugin registerWithRegistrar:registrar]; + [SwiftNativeCryptoIosPlugin registerWithRegistrar:registrar]; } @end diff --git a/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift b/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift new file mode 100644 index 0000000..00fdbb7 --- /dev/null +++ b/native_crypto_ios/ios/Classes/SwiftNativeCryptoIosPlugin.swift @@ -0,0 +1,95 @@ +import Flutter +import UIKit + +@available(iOS 13.0, *) +public class SwiftNativeCryptoIosPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let channel = FlutterMethodChannel(name: "plugins.hugop.cl/native_crypto", binaryMessenger: registrar.messenger()) + let instance = SwiftNativeCryptoIosPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + switch call.method { + case "digest": + let args : NSDictionary = call.arguments as! NSDictionary + + let data : Data = (args["data"] as! FlutterStandardTypedData).data + let algo : String = args["algorithm"] as! String + let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) + // TODO(hpcl): check if algorithm is null + // TODO(hpcl): check if digest is null + result(FlutterStandardTypedData.init(bytes: Hash.digest(data: data, algorithm: algorithm!))) + case "generateSecretKey": + let args : NSDictionary = call.arguments as! NSDictionary + + let bitsCount : NSNumber = args["bitsCount"] as! NSNumber + // TODO(hpcl): check if secure random is null + result(FlutterStandardTypedData.init(bytes: Key.fromSecureRandom(bitsCount: bitsCount.intValue))) + case "generateKeyPair": + result(FlutterStandardTypedData.init(bytes: KeyPair.fromCurve())) + case "pbkdf2": + 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 algo : String = args["algorithm"] as! String + let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: algo) + // TODO(hpcl): check if algorithm is null + // TODO(hpcl): check if derivation is null + result(FlutterStandardTypedData.init(bytes: Key.fromPBKDF2(password: password, salt: salt, keyBytesCount: keyBytesCount.intValue, iterations: iterations.intValue, algorithm: algorithm!)!)) + case "encrypt": + let args : NSDictionary = call.arguments as! NSDictionary + + let data : Data = (args["data"] as! FlutterStandardTypedData).data + let key : Data = (args["key"] as! FlutterStandardTypedData).data + let algo : String = args["algorithm"] as! String + // TODO(hpcl): check if algorithm is null + // TODO(hpcl): check if ciphertext is null + var ciphertext : Data + switch algo { + case "aes": + ciphertext = AESCipher.encrypt(plaintext: data, key: key)! + case "chachapoly": + ciphertext = CHACHACipher.encrypt(plaintext: data, key: key)! + default: + ciphertext = Data.init(); + } + result(FlutterStandardTypedData.init(bytes: ciphertext)) + case "decrypt": + let args : NSDictionary = call.arguments as! NSDictionary + + let data : Data = (args["data"] as! FlutterStandardTypedData).data + let key : Data = (args["key"] as! FlutterStandardTypedData).data + let algo : String = args["algorithm"] as! String + // TODO(hpcl): check if algorithm is null + // TODO(hpcl): check if ciphertext is null + var ciphertext : Data + switch algo { + case "aes": + ciphertext = AESCipher.decrypt(ciphertext: data, key: key)! + case "chachapoly": + ciphertext = CHACHACipher.decrypt(ciphertext: data, key: key)! + default: + ciphertext = Data.init(); + } + result(FlutterStandardTypedData.init(bytes: ciphertext)) + case "generateSharedSecretKey": + let args : NSDictionary = call.arguments as! NSDictionary + + let salt : Data = (args["salt"] as! FlutterStandardTypedData).data + let keyBytesCount : NSNumber = args["keyBytesCount"] as! NSNumber + let ephemeralPrivateKey : Data = (args["ephemeralPrivateKey"] as! FlutterStandardTypedData).data + let otherPublicKey : Data = (args["otherPublicKey"] as! FlutterStandardTypedData).data + let hkdfAlgorithm : String = args["hkdfAlgorithm"] as! String + let algorithm : HashAlgorithm? = HashAlgorithm.init(rawValue: hkdfAlgorithm) + // TODO(hpcl): check if algorithm is null + // TODO(hpcl): check if generated key is null + result(FlutterStandardTypedData.init(bytes: ECDH.generateSharedSecretKey(salt: salt, hash: algorithm!, keyBytesCount: keyBytesCount.intValue, privateKey: ephemeralPrivateKey, publicKey: otherPublicKey)!)) + + default: result(FlutterMethodNotImplemented) + } + } +} diff --git a/ios/native_crypto.podspec b/native_crypto_ios/ios/native_crypto_ios.podspec similarity index 63% rename from ios/native_crypto.podspec rename to native_crypto_ios/ios/native_crypto_ios.podspec index 0ff9ea5..3701f76 100644 --- a/ios/native_crypto.podspec +++ b/native_crypto_ios/ios/native_crypto_ios.podspec @@ -1,9 +1,9 @@ # # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint native_crypto.podspec' to validate before publishing. +# Run `pod lib lint native_crypto_ios.podspec` to validate before publishing. # Pod::Spec.new do |s| - s.name = 'native_crypto' + s.name = 'native_crypto_ios' s.version = '0.0.1' s.summary = 'A new flutter plugin project.' s.description = <<-DESC @@ -15,9 +15,9 @@ A new flutter plugin project. s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' - s.platform = :ios, '8.0' + s.platform = :ios, '13.0' - # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } s.swift_version = '5.0' end diff --git a/native_crypto_ios/pubspec.yaml b/native_crypto_ios/pubspec.yaml new file mode 100644 index 0000000..211e4de --- /dev/null +++ b/native_crypto_ios/pubspec.yaml @@ -0,0 +1,22 @@ +name: native_crypto_ios +description: iOS implementation of NativeCrypto +version: 0.0.7 + +environment: + sdk: ">=2.15.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + plugin: + implements: native_crypto + platforms: + ios: + pluginClass: NativeCryptoIosPlugin \ No newline at end of file diff --git a/native_crypto_platform_interface/.gitignore b/native_crypto_platform_interface/.gitignore new file mode 100644 index 0000000..23f216c --- /dev/null +++ b/native_crypto_platform_interface/.gitignore @@ -0,0 +1,390 @@ +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig + +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows + +### Dart ### +# See https://www.dartlang.org/guides/libraries/private-files + +# Files and directories created by pub +.dart_tool/ +.packages +build/ +# If you're building an application, you may want to check-in your pubspec.lock +pubspec.lock + +# Directory created by dartdoc +# If you don't generate documentation locally you can remove this line. +doc/api/ + +# dotenv environment variables file +.env* + +# Avoid committing generated Javascript files: +*.dart.js +*.info.json # Produced by the --dump-info flag. +*.js # When generated by dart2js. Don't specify *.js if your + # project includes source files written in JavaScript. +*.js_ +*.js.deps +*.js.map + +.flutter-plugins +.flutter-plugins-dependencies + +### Dart Patch ### +# dotenv environment variables file +.env + +### Flutter ### +# Flutter/Dart/Pub related +**/doc/api/ +.fvm/ +.pub-cache/ +.pub/ +coverage/ +lib/generated_plugin_registrant.dart +# For library packages, don’t commit the pubspec.lock file. +# Regenerating the pubspec.lock file lets you test your package against the latest compatible versions of its dependencies. +# See https://dart.dev/guides/libraries/private-files#pubspeclock +#pubspec.lock + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/key.properties +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/.last_build_id +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignores the whole .idea folder and all .iml files +# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 + +.idea/ + +# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 + +*.iml +modules.xml +.idea/misc.xml +*.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Kotlin ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Swift ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +# .swiftpm + +.build/ + +# CocoaPods +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +!.vscode/*.code-snippets + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,dart,flutter,intellij+all,kotlin,linux,swift,windows + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + diff --git a/native_crypto_platform_interface/.metadata b/native_crypto_platform_interface/.metadata new file mode 100644 index 0000000..50d6e33 --- /dev/null +++ b/native_crypto_platform_interface/.metadata @@ -0,0 +1,10 @@ +# 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. + +version: + revision: cf4400006550b70f28e4b4af815151d1e74846c6 + channel: stable + +project_type: plugin diff --git a/native_crypto_platform_interface/CHANGELOG.md b/native_crypto_platform_interface/CHANGELOG.md new file mode 100644 index 0000000..41cc7d8 --- /dev/null +++ b/native_crypto_platform_interface/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/native_crypto_platform_interface/LICENSE b/native_crypto_platform_interface/LICENSE new file mode 100644 index 0000000..ba75c69 --- /dev/null +++ b/native_crypto_platform_interface/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/native_crypto_platform_interface/README.md b/native_crypto_platform_interface/README.md new file mode 100644 index 0000000..7190aa2 --- /dev/null +++ b/native_crypto_platform_interface/README.md @@ -0,0 +1,18 @@ +# native_crypto_platform_interface + +A new flutter plugin project. + +## 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. + +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. + +The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported. +To add platforms, run `flutter create -t plugin --platforms .` under the same +directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. diff --git a/native_crypto_platform_interface/analysis_options.yaml b/native_crypto_platform_interface/analysis_options.yaml new file mode 100644 index 0000000..a5744c1 --- /dev/null +++ b/native_crypto_platform_interface/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart b/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart new file mode 100644 index 0000000..1d6fbd5 --- /dev/null +++ b/native_crypto_platform_interface/lib/native_crypto_platform_interface.dart @@ -0,0 +1,75 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: native_crypto_platform_interface.dart +// Created Date: 25/12/2021 16:43:49 +// Last Modified: 25/12/2021 17:39:39 +// ----- +// Copyright (c) 2021 + +import 'dart:typed_data'; + +import './src/method_channel_native_crypto.dart'; +import './src/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 [MethodChannelPathProvider]. + 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.verifyToken(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 generateKeyPair() { + throw UnimplementedError('generateKeyPair is not implemented'); + } + + Future pbkdf2(String password, String salt, int keyBytesCount, + int iterations, String algorithm) { + throw UnimplementedError('pbkdf2 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'); + } + + Future generateSharedSecretKey( + Uint8List salt, + int keyBytesCount, + Uint8List ephemeralPrivateKey, + Uint8List otherPublicKey, + String hkdfAlgorithm) { + throw UnimplementedError('generateSharedSecretKey is not implemented'); + } +} diff --git a/native_crypto_platform_interface/lib/src/method_channel_native_crypto.dart b/native_crypto_platform_interface/lib/src/method_channel_native_crypto.dart new file mode 100644 index 0000000..3cab922 --- /dev/null +++ b/native_crypto_platform_interface/lib/src/method_channel_native_crypto.dart @@ -0,0 +1,107 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: native_crypto_method_channel.dart +// Created Date: 25/12/2021 16:58:04 +// Last Modified: 25/12/2021 18:58:53 +// ----- +// Copyright (c) 2021 + +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import '../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 methodChannel = + const MethodChannel('plugins.hugop.cl/native_crypto'); + + @override + Future digest(Uint8List data, String algorithm) { + return methodChannel.invokeMethod( + 'digest', + { + 'data': data, + 'algorithm': algorithm, + }, + ); + } + + @override + Future generateSecretKey(int bitsCount) { + return methodChannel.invokeMethod( + 'generateSecretKey', + { + 'bitsCount': bitsCount, + }, + ); + } + + @override + Future generateKeyPair() { + return methodChannel.invokeMethod('generateKeyPair'); + } + + @override + Future pbkdf2(String password, String salt, int keyBytesCount, + int iterations, String algorithm) { + return methodChannel.invokeMethod( + 'pbkdf2', + { + 'password': password, + 'salt': salt, + 'keyBytesCount': keyBytesCount, + 'iterations': iterations, + 'algorithm': algorithm, + }, + ); + } + + @override + Future encrypt(Uint8List data, Uint8List key, String algorithm) { + return methodChannel.invokeMethod( + 'encrypt', + { + 'data': data, + 'key': key, + 'algorithm': algorithm, + }, + ); + } + + @override + Future decrypt(Uint8List data, Uint8List key, String algorithm) { + return methodChannel.invokeMethod( + 'decrypt', + { + 'data': data, + 'key': key, + 'algorithm': algorithm, + }, + ); + } + + @override + Future generateSharedSecretKey( + Uint8List salt, + int keyBytesCount, + Uint8List ephemeralPrivateKey, + Uint8List otherPublicKey, + String hkdfAlgorithm) { + return methodChannel.invokeMethod( + 'generateSharedSecretKey', + { + 'salt': salt, + 'keyBytesCount': keyBytesCount, + 'ephemeralPrivateKey': ephemeralPrivateKey, + 'otherPublicKey': otherPublicKey, + 'hkdfAlgorithm': hkdfAlgorithm, + }, + ); + } +} diff --git a/native_crypto_platform_interface/lib/src/platform_interface.dart b/native_crypto_platform_interface/lib/src/platform_interface.dart new file mode 100644 index 0000000..d0585c9 --- /dev/null +++ b/native_crypto_platform_interface/lib/src/platform_interface.dart @@ -0,0 +1,97 @@ +// Author: Hugo Pointcheval +// Email: git@pcl.ovh +// ----- +// File: platform_interface.dart +// Created Date: 25/12/2021 16:52:56 +// Last Modified: 25/12/2021 16:53:36 +// ----- +// Copyright (c) 2021 + +import 'package:meta/meta.dart'; + +/// Base class for platform interfaces. +/// +/// Provides a static helper method for ensuring that platform interfaces are +/// implemented using `extends` instead of `implements`. +/// +/// Platform interface classes are expected to have a private static token object which will be +/// be passed to [verifyToken] along with a platform interface object for verification. +/// +/// Sample usage: +/// +/// ```dart +/// abstract class UrlLauncherPlatform extends PlatformInterface { +/// UrlLauncherPlatform() : super(token: _token); +/// +/// static UrlLauncherPlatform _instance = MethodChannelUrlLauncher(); +/// +/// static const Object _token = Object(); +/// +/// static UrlLauncherPlatform get instance => _instance; +/// +/// /// Platform-specific plugins should set this with their own platform-specific +/// /// class that extends [UrlLauncherPlatform] when they register themselves. +/// static set instance(UrlLauncherPlatform instance) { +/// PlatformInterface.verifyToken(instance, _token); +/// _instance = instance; +/// } +/// +/// } +/// ``` +/// +/// Mockito mocks of platform interfaces will fail the verification, in test code only it is possible +/// to include the [MockPlatformInterfaceMixin] for the verification to be temporarily disabled. See +/// [MockPlatformInterfaceMixin] for a sample of using Mockito to mock a platform interface. +abstract class PlatformInterface { + /// Pass a private, class-specific `const Object()` as the `token`. + PlatformInterface({required Object token}) : _instanceToken = token; + + final Object? _instanceToken; + + /// Ensures that the platform instance has a token that matches the + /// provided token and throws [AssertionError] if not. + /// + /// This is used to ensure that implementers are using `extends` rather than + /// `implements`. + /// + /// Subclasses of [MockPlatformInterfaceMixin] are assumed to be valid in debug + /// builds. + /// + /// This is implemented as a static method so that it cannot be overridden + /// with `noSuchMethod`. + static void verifyToken(PlatformInterface instance, Object token) { + if (instance is MockPlatformInterfaceMixin) { + bool assertionsEnabled = false; + assert(() { + assertionsEnabled = true; + return true; + }()); + if (!assertionsEnabled) { + throw AssertionError( + '`MockPlatformInterfaceMixin` is not intended for use in release builds.'); + } + return; + } + if (!identical(token, instance._instanceToken)) { + throw AssertionError( + 'Platform interfaces must not be implemented with `implements`'); + } + } +} + +/// A [PlatformInterface] mixin that can be combined with mockito's `Mock`. +/// +/// It passes the [PlatformInterface.verifyToken] check even though it isn't +/// using `extends`. +/// +/// This class is intended for use in tests only. +/// +/// Sample usage (assuming UrlLauncherPlatform extends [PlatformInterface]: +/// +/// ```dart +/// class UrlLauncherPlatformMock extends Mock +/// with MockPlatformInterfaceMixin +/// implements UrlLauncherPlatform {} +/// ``` +@visibleForTesting +abstract class MockPlatformInterfaceMixin implements PlatformInterface {} \ No newline at end of file diff --git a/native_crypto_platform_interface/pubspec.yaml b/native_crypto_platform_interface/pubspec.yaml new file mode 100644 index 0000000..017ea35 --- /dev/null +++ b/native_crypto_platform_interface/pubspec.yaml @@ -0,0 +1,17 @@ +name: native_crypto_platform_interface +description: A common interface for NativeCrypto plugin. +version: 0.0.7 + +environment: + sdk: ">=2.15.0 <3.0.0" + flutter: ">=2.5.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^1.0.4 \ No newline at end of file diff --git a/native_crypto_platform_interface/test/native_crypto_platform_interface_test.dart b/native_crypto_platform_interface/test/native_crypto_platform_interface_test.dart new file mode 100644 index 0000000..e69de29 diff --git a/pubspec.yaml b/pubspec.yaml deleted file mode 100644 index 431d835..0000000 --- a/pubspec.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: native_crypto -description: Fast crypto functions for Flutter. -version: 0.0.6 -author: Hugo Pointcheval -homepage: https://hugo.pointcheval.fr - -environment: - sdk: ">=2.6.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - -dev_dependencies: - flutter_test: - sdk: flutter - -flutter: - plugin: - androidPackage: fr.pointcheval.native_crypto - pluginClass: NativeCryptoPlugin \ No newline at end of file