From 7516bb7982150229e140d5ed78a628cc776b0324 Mon Sep 17 00:00:00 2001 From: "Charles E. Lehner" Date: Fri, 16 Oct 2020 12:13:22 -0400 Subject: [PATCH 1/3] Scaffold library, CLI program, and HTTP server --- .github/workflows/build.yml | 49 +++++++++++++++++++++++++++++++++++++ .gitignore | 2 ++ Cargo.toml | 6 +++++ cli/Cargo.toml | 13 ++++++++++ cli/src/main.rs | 46 ++++++++++++++++++++++++++++++++++ http/Cargo.toml | 8 ++++++ http/src/main.rs | 3 +++ lib/Cargo.toml | 8 ++++++ lib/src/lib.rs | 0 9 files changed, 135 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 cli/Cargo.toml create mode 100644 cli/src/main.rs create mode 100644 http/Cargo.toml create mode 100644 http/src/main.rs create mode 100644 lib/Cargo.toml create mode 100644 lib/src/lib.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..eef4cf7f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,49 @@ +name: ci + +on: + pull_request: + branches: + - main + push: + branches: + - main + +env: + CARGO_TERM_COLOR: always + +defaults: + run: + working-directory: didkit + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout DIDKit repository + uses: actions/checkout@v2 + with: + path: didkit + + - name: Checkout SSI library + uses: actions/checkout@v2 + with: + repository: spruceid/ssi + token: ${{ secrets.GH_ACCESS_TOKEN_CEL }} + path: ssi + + - name: Cache Cargo registry and build artifacts + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', '**.rs') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Build + run: cargo build --verbose --workspace + + - name: Test + run: cargo test --verbose --workspace diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..96ef6c0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..c89ed57d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = [ + "http", + "cli", + "lib", +] diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 00000000..6eb9efe6 --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "didkit-cli" +version = "0.0.1" +authors = ["Charles E. Lehner "] +edition = "2018" + +[dependencies] +didkit = { path = "../lib" } +structopt = "0.3" + +[[bin]] +path = "src/main.rs" +name = "didkit" diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 00000000..968b748d --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,46 @@ +use structopt::StructOpt; + +#[derive(StructOpt, Debug)] +enum DIDKit { + // DID Functionality + /// Create new DID Document. + DIDCreate {}, + /// Resolve a DID to a DID Document. + DIDResolve {}, + /// Dereference a DID URL to a resource. + DIDDereference {}, + /// Update a DID Document’s authentication. + DIDUpdateAuthentication {}, + /// Update a DID Document’s service endpoint(s). + DIDUpdateServiceEndpoints {}, + /// Deactivate a DID. + DIDDeactivate {}, + /// Create a Signed IETF JSON Patch to update a DID document. + DIDPatch {}, + + // VC Functionality + /// Issue Credential + VCIssueCredential {}, + /// Verify Credential + VCVerifyCredential {}, + /// Issue Presentation + VCIssuePresentation {}, + /// Verify Presentation + VCVerifyPresentation {}, + /// Revoke Credential + VCRevokeCredential {}, + + // DIDComm Functionality (???) + /// Discover a messaging endpoint from a DID which supports DIDComm. + DIDCommDiscover {}, + /// Send a DIDComm message. + DIDCommSend {}, + /// Receive a DIDComm message. + DIDCommReceive {}, +} + +fn main() { + let opt = DIDKit::from_args(); + println!("{:?}", opt); + // TODO +} diff --git a/http/Cargo.toml b/http/Cargo.toml new file mode 100644 index 00000000..4f3c0af7 --- /dev/null +++ b/http/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "didkit-http" +version = "0.0.1" +authors = ["Charles E. Lehner "] +edition = "2018" + +[dependencies] +didkit = { path = "../lib" } diff --git a/http/src/main.rs b/http/src/main.rs new file mode 100644 index 00000000..171cfe28 --- /dev/null +++ b/http/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + // TODO +} diff --git a/lib/Cargo.toml b/lib/Cargo.toml new file mode 100644 index 00000000..bbccfa07 --- /dev/null +++ b/lib/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "didkit" +version = "0.0.1" +authors = ["Charles E. Lehner "] +edition = "2018" + +[dependencies] +ssi = { path = "../../ssi" } diff --git a/lib/src/lib.rs b/lib/src/lib.rs new file mode 100644 index 00000000..e69de29b From d5228347a595a8799077b214fad4dd9a7c63e917 Mon Sep 17 00:00:00 2001 From: "Charles E. Lehner" Date: Fri, 16 Oct 2020 12:18:59 -0400 Subject: [PATCH 2/3] Add DIDKit FFI Close #1 --- .cargo/config | 11 + .github/workflows/build.yml | 44 +- Cargo.toml | 1 + lib/.gitignore | 1 + lib/Cargo.toml | 4 + lib/FFI.md | 8 + lib/Makefile | 102 ++++ lib/android/AndroidManifest.xml | 3 + lib/android/R.txt | 0 lib/android/README.md | 17 + lib/android/classes.jar | 1 + lib/android/jni/arm64-v8a/libdidkit.so | 1 + lib/android/jni/armeabi-v7a/libdidkit.so | 1 + lib/android/jni/x86/libdidkit.so | 1 + lib/android/jni/x86_64/libdidkit.so | 1 + lib/c/README.md | 14 + lib/c/test.c | 15 + lib/cbindgen.toml | 3 + lib/cbindings/Cargo.toml | 10 + lib/cbindings/README.md | 5 + lib/cbindings/build.rs | 23 + lib/cbindings/src/lib.rs | 1 + lib/flutter/.gitignore | 7 + lib/flutter/.metadata | 10 + lib/flutter/CHANGELOG.md | 3 + lib/flutter/LICENSE | 1 + lib/flutter/README.md | 16 + lib/flutter/android/.gitignore | 8 + lib/flutter/android/build.gradle | 33 ++ lib/flutter/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + lib/flutter/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../spruceid/DIDKit/DIDKitFlutterPlugin.java | 18 + lib/flutter/android/src/main/jniLibs | 1 + lib/flutter/didkit.iml | 17 + lib/flutter/example/.gitignore | 41 ++ lib/flutter/example/.metadata | 10 + lib/flutter/example/README.md | 8 + lib/flutter/example/android/.gitignore | 11 + lib/flutter/example/android/app/build.gradle | 54 ++ .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 41 ++ .../spruceid/didkit_example/MainActivity.java | 6 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + lib/flutter/example/android/build.gradle | 29 + lib/flutter/example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 6 + lib/flutter/example/android/settings.gradle | 11 + lib/flutter/example/ios/.gitignore | 32 ++ .../ios/Flutter/AppFrameworkInfo.plist | 26 + .../example/ios/Flutter/Debug.xcconfig | 1 + .../example/ios/Flutter/Release.xcconfig | 1 + .../ios/Runner.xcodeproj/project.pbxproj | 495 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 91 ++++ .../contents.xcworkspacedata | 7 + .../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 + lib/flutter/example/ios/Runner/Info.plist | 45 ++ .../ios/Runner/Runner-Bridging-Header.h | 1 + lib/flutter/example/lib/main.dart | 58 ++ lib/flutter/example/pubspec.lock | 167 ++++++ lib/flutter/example/pubspec.yaml | 71 +++ lib/flutter/example/test/widget_test.dart | 27 + lib/flutter/ios/.gitignore | 37 ++ lib/flutter/ios/Assets/.gitkeep | 0 lib/flutter/ios/Classes/DIDKitFlutterPlugin.h | 4 + lib/flutter/ios/Classes/DIDKitFlutterPlugin.m | 8 + lib/flutter/ios/Classes/didkit.h | 1 + lib/flutter/ios/didkit.podspec | 23 + lib/flutter/ios/libdidkit.a | 1 + lib/flutter/lib/didkit.dart | 23 + lib/flutter/libdidkit.so | 1 + lib/flutter/pubspec.lock | 153 ++++++ lib/flutter/pubspec.yaml | 24 + lib/flutter/test/didkit_test.dart | 11 + lib/java/README.md | 22 + lib/java/com/spruceid/DIDKit.java | 14 + lib/src/lib.rs | 22 + lib/tests/vc.rs | 16 + 115 files changed, 2321 insertions(+), 1 deletion(-) create mode 100644 .cargo/config create mode 100644 lib/.gitignore create mode 100644 lib/FFI.md create mode 100644 lib/Makefile create mode 100644 lib/android/AndroidManifest.xml create mode 100644 lib/android/R.txt create mode 100644 lib/android/README.md create mode 120000 lib/android/classes.jar create mode 120000 lib/android/jni/arm64-v8a/libdidkit.so create mode 120000 lib/android/jni/armeabi-v7a/libdidkit.so create mode 120000 lib/android/jni/x86/libdidkit.so create mode 120000 lib/android/jni/x86_64/libdidkit.so create mode 100644 lib/c/README.md create mode 100644 lib/c/test.c create mode 100644 lib/cbindgen.toml create mode 100644 lib/cbindings/Cargo.toml create mode 100644 lib/cbindings/README.md create mode 100644 lib/cbindings/build.rs create mode 100644 lib/cbindings/src/lib.rs create mode 100644 lib/flutter/.gitignore create mode 100644 lib/flutter/.metadata create mode 100644 lib/flutter/CHANGELOG.md create mode 100644 lib/flutter/LICENSE create mode 100644 lib/flutter/README.md create mode 100644 lib/flutter/android/.gitignore create mode 100644 lib/flutter/android/build.gradle create mode 100644 lib/flutter/android/gradle.properties create mode 100644 lib/flutter/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 lib/flutter/android/settings.gradle create mode 100644 lib/flutter/android/src/main/AndroidManifest.xml create mode 100644 lib/flutter/android/src/main/java/com/spruceid/DIDKit/DIDKitFlutterPlugin.java create mode 120000 lib/flutter/android/src/main/jniLibs create mode 100644 lib/flutter/didkit.iml create mode 100644 lib/flutter/example/.gitignore create mode 100644 lib/flutter/example/.metadata create mode 100644 lib/flutter/example/README.md create mode 100644 lib/flutter/example/android/.gitignore create mode 100644 lib/flutter/example/android/app/build.gradle create mode 100644 lib/flutter/example/android/app/src/debug/AndroidManifest.xml create mode 100644 lib/flutter/example/android/app/src/main/AndroidManifest.xml create mode 100644 lib/flutter/example/android/app/src/main/java/com/spruceid/didkit_example/MainActivity.java create mode 100644 lib/flutter/example/android/app/src/main/res/drawable/launch_background.xml create mode 100644 lib/flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 lib/flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 lib/flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 lib/flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 lib/flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 lib/flutter/example/android/app/src/main/res/values-night/styles.xml create mode 100644 lib/flutter/example/android/app/src/main/res/values/styles.xml create mode 100644 lib/flutter/example/android/app/src/profile/AndroidManifest.xml create mode 100644 lib/flutter/example/android/build.gradle create mode 100644 lib/flutter/example/android/gradle.properties create mode 100644 lib/flutter/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 lib/flutter/example/android/settings.gradle create mode 100644 lib/flutter/example/ios/.gitignore create mode 100644 lib/flutter/example/ios/Flutter/AppFrameworkInfo.plist create mode 100644 lib/flutter/example/ios/Flutter/Debug.xcconfig create mode 100644 lib/flutter/example/ios/Flutter/Release.xcconfig create mode 100644 lib/flutter/example/ios/Runner.xcodeproj/project.pbxproj create mode 100644 lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 lib/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 lib/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 lib/flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 lib/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 lib/flutter/example/ios/Runner/AppDelegate.swift create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 lib/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 lib/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 lib/flutter/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 lib/flutter/example/ios/Runner/Info.plist create mode 100644 lib/flutter/example/ios/Runner/Runner-Bridging-Header.h create mode 100644 lib/flutter/example/lib/main.dart create mode 100644 lib/flutter/example/pubspec.lock create mode 100644 lib/flutter/example/pubspec.yaml create mode 100644 lib/flutter/example/test/widget_test.dart create mode 100644 lib/flutter/ios/.gitignore create mode 100644 lib/flutter/ios/Assets/.gitkeep create mode 100644 lib/flutter/ios/Classes/DIDKitFlutterPlugin.h create mode 100644 lib/flutter/ios/Classes/DIDKitFlutterPlugin.m create mode 120000 lib/flutter/ios/Classes/didkit.h create mode 100644 lib/flutter/ios/didkit.podspec create mode 120000 lib/flutter/ios/libdidkit.a create mode 100644 lib/flutter/lib/didkit.dart create mode 120000 lib/flutter/libdidkit.so create mode 100644 lib/flutter/pubspec.lock create mode 100644 lib/flutter/pubspec.yaml create mode 100644 lib/flutter/test/didkit_test.dart create mode 100644 lib/java/README.md create mode 100644 lib/java/com/spruceid/DIDKit.java create mode 100644 lib/tests/vc.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 00000000..8f2b0338 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,11 @@ +[target.i686-linux-android] +linker = "i686-linux-android16-clang" + +[target.armv7-linux-androideabi] +linker = "armv7a-linux-androideabi16-clang" + +[target.aarch64-linux-android] +linker = "aarch64-linux-android21-clang" + +[target.x86_64-linux-android] +linker = "x86_64-linux-android21-clang" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eef4cf7f..5d01f315 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', '**.rs') }} + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.toml', 'lib/Makefile', '**.rs') }} restore-keys: | ${{ runner.os }}-cargo- @@ -47,3 +47,45 @@ jobs: - name: Test run: cargo test --verbose --workspace + + - name: Install Rust Android targets + run: make -C lib install-rustup-android + + - name: Install JDK + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Install Flutter + uses: subosito/flutter-action@v1 + + - name: Opt out of Dart/Flutter analytics + run: | + dart --disable-analytics + flutter --suppress-analytics config --no-analytics + + - name: Install Android SDK + uses: android-actions/setup-android@v2 + + - name: Cache Android NDK + id: ndk-cache + uses: actions/cache@v2 + with: + path: ${{ env.ANDROID_SDK_ROOT }}/ndk-bundle + key: ${{ runner.os }}-ndk-bundle + + - name: Install Android NDK + if: steps.ndk-cache.outputs.cache-hit != 'true' + run: $ANDROID_SDK_ROOT/tools/bin/sdkmanager ndk-bundle + + - name: Test C FFI + run: make -C lib ../target/test/c.stamp + + - name: Test JNI + run: make -C lib ../target/test/java.stamp + + - name: Test Dart/Flutter plugin + run: make -C lib ../target/test/flutter.stamp + + - name: Build Android Archive + run: make -C lib ../target/test/aar.stamp diff --git a/Cargo.toml b/Cargo.toml index c89ed57d..a550c79d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,5 @@ members = [ "http", "cli", "lib", + "lib/cbindings" ] diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 00000000..ceeb05b4 --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1 @@ +/tmp diff --git a/lib/Cargo.toml b/lib/Cargo.toml index bbccfa07..ca504b40 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -6,3 +6,7 @@ edition = "2018" [dependencies] ssi = { path = "../../ssi" } +jni = "0.17" + +[lib] +crate-type = ["lib", "cdylib"] diff --git a/lib/FFI.md b/lib/FFI.md new file mode 100644 index 00000000..2dc442e1 --- /dev/null +++ b/lib/FFI.md @@ -0,0 +1,8 @@ +## FFI + +The DIDKit library is written in Rust, but has bindings for other languages and environments: + +- [C](c/) +- [Java](java/) +- [Android](android/) +- [Flutter](flutter/) diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..c3158c3b --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,102 @@ +# didkit/lib/Makefile + +TARGET=../target + +.PHONY: test +test: $(TARGET)/test/c.stamp \ + $(TARGET)/test/java.stamp \ + $(TARGET)/test/aar.stamp \ + $(TARGET)/test/flutter.stamp + +## Setup + +android/res $(TARGET)/test $(TARGET)/jvm: + mkdir -p $@ + +## Rust + +RUST_SRC=Cargo.toml $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs) + +$(TARGET)/didkit.h: cbindgen.toml cbindings/build.rs cbindings/Cargo.toml $(RUST_SRC) + cargo build -p didkit-cbindings + test -s $@ && touch $@ + +$(TARGET)/release/libdidkit.so: $(RUST_SRC) + cargo build --lib --release + strip $@ + +## C + +$(TARGET)/test/c.stamp: $(TARGET)/cabi-test $(TARGET)/release/libdidkit.so | $(TARGET)/test + $(TARGET)/cabi-test + touch $@ + +$(TARGET)/cabi-test: c/test.c $(TARGET)/didkit.h + $(CC) -I$(TARGET) $< -ldl -o $@ + +## Java + +JAVA_SRC=$(wildcard java/*/*.java java/*/*/*.java java/*/*/*/*.java) + +$(TARGET)/test/java.stamp: $(TARGET)/jvm/com/spruceid/DIDKit.class $(TARGET)/release/libdidkit.so | $(TARGET)/test + java -Djava.class.path=$(TARGET)/jvm \ + -Djava.library.path=$(TARGET)/release \ + com.spruceid.DIDKit + touch $@ + +$(TARGET)/jvm/com/spruceid/DIDKit.class: java/com/spruceid/DIDKit.java | $(TARGET)/jvm + javac $^ -d $(TARGET)/jvm -source 1.7 -target 1.7 + +$(TARGET)/com_spruceid_DIDKit.h: java/com/spruceid/DIDkit.java + javac -h $(TARGET) $< + +$(TARGET)/didkit.jar: $(TARGET)/jvm/com/spruceid/DIDKit.class + jar -cf $@ -C $(TARGET)/jvm $(patsubst $(TARGET)/jvm/%,%,$^) + +## Android + +.PHONY: install-rustup-android +install-rustup-android: + rustup target add i686-linux-android armv7-linux-androideabi aarch64-linux-android x86_64-linux-android + +ANDROID_SDK_ROOT ?= ~/Android/Sdk +ANDROID_TOOLS ?= $(lastword $(wildcard $(ANDROID_SDK_ROOT)/build-tools/*)) +ANDROID_NDK_HOME ?= $(lastword $(wildcard \ + $(ANDROID_SDK_ROOT)/ndk/* \ + $(ANDROID_SDK_ROOT)/ndk-bundle)) +OS_NAME=$(shell uname | tr '[:upper:]' '[:lower:]') +TOOLCHAIN=$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/$(OS_NAME)-x86_64 +ANDROID_LIBS=\ + $(TARGET)/i686-linux-android/release/libdidkit.so\ + $(TARGET)/armv7-linux-androideabi/release/libdidkit.so\ + $(TARGET)/aarch64-linux-android/release/libdidkit.so\ + $(TARGET)/x86_64-linux-android/release/libdidkit.so + +$(TARGET)/test/aar.stamp: $(TARGET)/didkit.aar | $(TARGET)/test + rm -rf tmp/test-aar + mkdir -p tmp/test-aar + cd tmp/test-aar && unzip -q ../../$< || unzip -l ../../$< + cd tmp/test-aar && unzip -qo classes.jar com/spruceid/DIDKit.class || unzip -l classes.jar + javap tmp/test-aar/com/spruceid/DIDKit.class | grep -q 'public class com.spruceid.DIDKit' + touch $@ + +$(TARGET)/didkit.aar: $(TARGET)/didkit.jar android/AndroidManifest.xml android/R.txt $(ANDROID_LIBS) | android/res + $(ANDROID_TOOLS)/aapt package -f -S android/res -F $@ --ignore-assets '.*:*~:README.md' android + +$(TARGET)/%/release/libdidkit.so: $(RUST_SRC) + PATH=$(TOOLCHAIN)/bin:$(PATH) \ + cargo build --lib --release --target $* + $(TOOLCHAIN)/bin/llvm-strip $@ + +## Flutter + +$(TARGET)/test/flutter.stamp: flutter/lib/didkit.dart $(TARGET)/release/libdidkit.so | $(TARGET)/test + cd flutter && LD_LIBRARY_PATH=$$PWD \ + flutter --suppress-analytics test + touch $@ + +## Cleanup + +.PHONY: clean +clean: + cargo clean diff --git a/lib/android/AndroidManifest.xml b/lib/android/AndroidManifest.xml new file mode 100644 index 00000000..ff4665ec --- /dev/null +++ b/lib/android/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/lib/android/R.txt b/lib/android/R.txt new file mode 100644 index 00000000..e69de29b diff --git a/lib/android/README.md b/lib/android/README.md new file mode 100644 index 00000000..972b9226 --- /dev/null +++ b/lib/android/README.md @@ -0,0 +1,17 @@ +# DIDKit - Android + +[Android Library (AAR file)][AAR] for DIDKit. The AAR file includes Java class files using [JNI][], and binary shared libraries for Android's supported architectures (x86, armeabi-v7a, arm64-v8a, x86\_64). It can be added to existing Android projects using Android Studio or Gradle. + +## Requires + +Android SDK and NDK, for Linux x86\_64 + +## Build + +In the parent directory, run: +``` +make target/didkit.aar +``` + +[AAR]: https://developer.android.com/studio/projects/android-library.html#aar-contents +[JNI]: https://en.wikipedia.org/wiki/Java_Native_Interface diff --git a/lib/android/classes.jar b/lib/android/classes.jar new file mode 120000 index 00000000..ed750ac6 --- /dev/null +++ b/lib/android/classes.jar @@ -0,0 +1 @@ +../../target/didkit.jar \ No newline at end of file diff --git a/lib/android/jni/arm64-v8a/libdidkit.so b/lib/android/jni/arm64-v8a/libdidkit.so new file mode 120000 index 00000000..8a1285c6 --- /dev/null +++ b/lib/android/jni/arm64-v8a/libdidkit.so @@ -0,0 +1 @@ +../../../../target/aarch64-linux-android/release/libdidkit.so \ No newline at end of file diff --git a/lib/android/jni/armeabi-v7a/libdidkit.so b/lib/android/jni/armeabi-v7a/libdidkit.so new file mode 120000 index 00000000..274b819b --- /dev/null +++ b/lib/android/jni/armeabi-v7a/libdidkit.so @@ -0,0 +1 @@ +../../../../target/armv7-linux-androideabi/release/libdidkit.so \ No newline at end of file diff --git a/lib/android/jni/x86/libdidkit.so b/lib/android/jni/x86/libdidkit.so new file mode 120000 index 00000000..dc0974bd --- /dev/null +++ b/lib/android/jni/x86/libdidkit.so @@ -0,0 +1 @@ +../../../../target/i686-linux-android/release/libdidkit.so \ No newline at end of file diff --git a/lib/android/jni/x86_64/libdidkit.so b/lib/android/jni/x86_64/libdidkit.so new file mode 120000 index 00000000..a250510b --- /dev/null +++ b/lib/android/jni/x86_64/libdidkit.so @@ -0,0 +1 @@ +../../../../target/x86_64-linux-android/release/libdidkit.so \ No newline at end of file diff --git a/lib/c/README.md b/lib/c/README.md new file mode 100644 index 00000000..aafa2dbe --- /dev/null +++ b/lib/c/README.md @@ -0,0 +1,14 @@ +# DIDKit - C + +Shared Library for DIDKit. Intended to be compatible with C. + +## Build + +In the parent directory, run: +``` +make ../target/release/libdidkit.so +``` + +## Android + +To build the C shared library for Android targets, build the [DIDKit Android library](../android/). diff --git a/lib/c/test.c b/lib/c/test.c new file mode 100644 index 00000000..09143e7d --- /dev/null +++ b/lib/c/test.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include "didkit.h" + +int main() { + void *lib = dlopen("../target/release/libdidkit.so", RTLD_NOW); + if (lib == NULL) errx(1, "dlopen: %s", dlerror()); + const char *(*didkit_get_version)() = dlsym(lib, "didkit_get_version"); + if (didkit_get_version == NULL) errx(1, "unable to find version function"); + const char *version = didkit_get_version(); + printf("C libdidkit version: %s\n", version); + int rc = dlclose(lib); + if (rc < 0) errx(1, "dlclose: %s", dlerror()); +} diff --git a/lib/cbindgen.toml b/lib/cbindgen.toml new file mode 100644 index 00000000..34b61af7 --- /dev/null +++ b/lib/cbindgen.toml @@ -0,0 +1,3 @@ +language = "C" +autogen_warning = "// didkit.h - autogenerated by cbindgen" +pragma_once = true diff --git a/lib/cbindings/Cargo.toml b/lib/cbindings/Cargo.toml new file mode 100644 index 00000000..412f6db3 --- /dev/null +++ b/lib/cbindings/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "didkit-cbindings" +version = "0.1.0" +authors = ["Charles E. Lehner "] +edition = "2018" +publish = false + +[build-dependencies] +#didkit = { path = "../" } +cbindgen = "0.14" diff --git a/lib/cbindings/README.md b/lib/cbindings/README.md new file mode 100644 index 00000000..bb756cd2 --- /dev/null +++ b/lib/cbindings/README.md @@ -0,0 +1,5 @@ +# cbindings for didkit + +This crate builds a C header file for the didkit crate. It is a separate crate so that it runs after the didkit crate is built, rather than blocking compilation of the didkit crate. If there are syntax errors, the Rust compiler gives more useful output than would the failing build script using cbindgen. + +Related issue: [Cargo post-build script execution](https://github.com/rust-lang/cargo/issues/545) diff --git a/lib/cbindings/build.rs b/lib/cbindings/build.rs new file mode 100644 index 00000000..b7240410 --- /dev/null +++ b/lib/cbindings/build.rs @@ -0,0 +1,23 @@ +extern crate cbindgen; + +use std::env; +use std::path::Path; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let lib_dir = Path::new(&crate_dir).parent().unwrap(); + let workspace_dir = lib_dir.parent().unwrap(); + + // Docs say to output into OUT_DIR, but then how do we use that? + // https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script + // https://stackoverflow.com/questions/63928113/is-there-a-way-to-change-out-dir-for-a-build-rs + // let out_dir = env::var("OUT_DIR").unwrap(); + let out_dir = workspace_dir.join("target"); + let out_file = out_dir.join("didkit.h"); + + cbindgen::generate(lib_dir) + .expect("Unable to generate bindings") + .write_to_file(&out_file); + + println!("cargo:rerun-if-changed={:?}", &out_file); +} diff --git a/lib/cbindings/src/lib.rs b/lib/cbindings/src/lib.rs new file mode 100644 index 00000000..269e1e9f --- /dev/null +++ b/lib/cbindings/src/lib.rs @@ -0,0 +1 @@ +// placeholder for build-only crate diff --git a/lib/flutter/.gitignore b/lib/flutter/.gitignore new file mode 100644 index 00000000..e9dc58d3 --- /dev/null +++ b/lib/flutter/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ diff --git a/lib/flutter/.metadata b/lib/flutter/.metadata new file mode 100644 index 00000000..28776579 --- /dev/null +++ b/lib/flutter/.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: 6b39acdc53bad8b3d51d1618e3df4e299b344d7c + channel: master + +project_type: plugin diff --git a/lib/flutter/CHANGELOG.md b/lib/flutter/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/lib/flutter/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/lib/flutter/LICENSE b/lib/flutter/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/lib/flutter/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/lib/flutter/README.md b/lib/flutter/README.md new file mode 100644 index 00000000..d8e79e30 --- /dev/null +++ b/lib/flutter/README.md @@ -0,0 +1,16 @@ +# DIDKit - Flutter + +[Flutter plugin][packages-plugins] for the DIDKit library. Includes Dart bindings, and functionality for Android and iOS. + +## Usage + +You can depend on this plugin as a [path dependency][path-packages]. + +You will also need to build the DIDKit library for your target platforms. +To do that for Android, trigger building the AAR file - in the parent directory: +``` +make ../target/didkit.aar +``` + +[path-packages]: https://dart.dev/tools/pub/dependencies#path-packages +[packages-plugins]: https://flutter.dev/developing-packages/ diff --git a/lib/flutter/android/.gitignore b/lib/flutter/android/.gitignore new file mode 100644 index 00000000..c6cbe562 --- /dev/null +++ b/lib/flutter/android/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/lib/flutter/android/build.gradle b/lib/flutter/android/build.gradle new file mode 100644 index 00000000..28dd6dae --- /dev/null +++ b/lib/flutter/android/build.gradle @@ -0,0 +1,33 @@ +group 'com.spruceid.DIDKit' +version '1.0' + +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + } +} + +rootProject.allprojects { + repositories { + google() + jcenter() + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 29 + + defaultConfig { + minSdkVersion 16 + } + lintOptions { + disable 'InvalidPackage' + } +} diff --git a/lib/flutter/android/gradle.properties b/lib/flutter/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/lib/flutter/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/lib/flutter/android/gradle/wrapper/gradle-wrapper.properties b/lib/flutter/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..01a286e9 --- /dev/null +++ b/lib/flutter/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +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 diff --git a/lib/flutter/android/settings.gradle b/lib/flutter/android/settings.gradle new file mode 100644 index 00000000..7fcf27dd --- /dev/null +++ b/lib/flutter/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'DIDKit' diff --git a/lib/flutter/android/src/main/AndroidManifest.xml b/lib/flutter/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..ff4665ec --- /dev/null +++ b/lib/flutter/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/lib/flutter/android/src/main/java/com/spruceid/DIDKit/DIDKitFlutterPlugin.java b/lib/flutter/android/src/main/java/com/spruceid/DIDKit/DIDKitFlutterPlugin.java new file mode 100644 index 00000000..fe05213f --- /dev/null +++ b/lib/flutter/android/src/main/java/com/spruceid/DIDKit/DIDKitFlutterPlugin.java @@ -0,0 +1,18 @@ +package com.spruceid.DIDKit; + +import androidx.annotation.NonNull; + +import io.flutter.embedding.engine.plugins.FlutterPlugin; + +/** DIDKitFlutterPlugin */ +public class DIDKitFlutterPlugin implements FlutterPlugin { + /// This class is required by Flutter, but does not do anything. + /// All functionality is in the FFI between Dart and the C shared library. + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + } +} diff --git a/lib/flutter/android/src/main/jniLibs b/lib/flutter/android/src/main/jniLibs new file mode 120000 index 00000000..48882acb --- /dev/null +++ b/lib/flutter/android/src/main/jniLibs @@ -0,0 +1 @@ +../../../../android/jni \ No newline at end of file diff --git a/lib/flutter/didkit.iml b/lib/flutter/didkit.iml new file mode 100644 index 00000000..ebe8b325 --- /dev/null +++ b/lib/flutter/didkit.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/flutter/example/.gitignore b/lib/flutter/example/.gitignore new file mode 100644 index 00000000..9d532b18 --- /dev/null +++ b/lib/flutter/example/.gitignore @@ -0,0 +1,41 @@ +# 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 diff --git a/lib/flutter/example/.metadata b/lib/flutter/example/.metadata new file mode 100644 index 00000000..fee8bd7e --- /dev/null +++ b/lib/flutter/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: 6b39acdc53bad8b3d51d1618e3df4e299b344d7c + channel: master + +project_type: app diff --git a/lib/flutter/example/README.md b/lib/flutter/example/README.md new file mode 100644 index 00000000..056084c8 --- /dev/null +++ b/lib/flutter/example/README.md @@ -0,0 +1,8 @@ +# didkit_example + +Demonstrates how to use the didkit plugin. + +This example app is not used but is required by Flutter. + +More info: +https://flutter.dev/docs/development/packages-and-plugins/developing-packages diff --git a/lib/flutter/example/android/.gitignore b/lib/flutter/example/android/.gitignore new file mode 100644 index 00000000..0a741cb4 --- /dev/null +++ b/lib/flutter/example/android/.gitignore @@ -0,0 +1,11 @@ +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 diff --git a/lib/flutter/example/android/app/build.gradle b/lib/flutter/example/android/app/build.gradle new file mode 100644 index 00000000..3dfc452a --- /dev/null +++ b/lib/flutter/example/android/app/build.gradle @@ -0,0 +1,54 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 29 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.spruceid.didkit_example" + minSdkVersion 16 + targetSdkVersion 29 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} diff --git a/lib/flutter/example/android/app/src/debug/AndroidManifest.xml b/lib/flutter/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..d4c22505 --- /dev/null +++ b/lib/flutter/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/lib/flutter/example/android/app/src/main/AndroidManifest.xml b/lib/flutter/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..28572a87 --- /dev/null +++ b/lib/flutter/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + diff --git a/lib/flutter/example/android/app/src/main/java/com/spruceid/didkit_example/MainActivity.java b/lib/flutter/example/android/app/src/main/java/com/spruceid/didkit_example/MainActivity.java new file mode 100644 index 00000000..0006c4b5 --- /dev/null +++ b/lib/flutter/example/android/app/src/main/java/com/spruceid/didkit_example/MainActivity.java @@ -0,0 +1,6 @@ +package com.spruceid.didkit_example; + +import io.flutter.embedding.android.FlutterActivity; + +public class MainActivity extends FlutterActivity { +} diff --git a/lib/flutter/example/android/app/src/main/res/drawable/launch_background.xml b/lib/flutter/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/lib/flutter/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/lib/flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/lib/flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/lib/flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/lib/flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/lib/flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/lib/flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/lib/flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/lib/flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/lib/flutter/example/android/app/src/main/res/values-night/styles.xml b/lib/flutter/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..449a9f93 --- /dev/null +++ b/lib/flutter/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/lib/flutter/example/android/app/src/main/res/values/styles.xml b/lib/flutter/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..d74aa35c --- /dev/null +++ b/lib/flutter/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/lib/flutter/example/android/app/src/profile/AndroidManifest.xml b/lib/flutter/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..d4c22505 --- /dev/null +++ b/lib/flutter/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/lib/flutter/example/android/build.gradle b/lib/flutter/example/android/build.gradle new file mode 100644 index 00000000..e0d7ae2c --- /dev/null +++ b/lib/flutter/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.5.0' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/lib/flutter/example/android/gradle.properties b/lib/flutter/example/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/lib/flutter/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/lib/flutter/example/android/gradle/wrapper/gradle-wrapper.properties b/lib/flutter/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..296b146b --- /dev/null +++ b/lib/flutter/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +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 diff --git a/lib/flutter/example/android/settings.gradle b/lib/flutter/example/android/settings.gradle new file mode 100644 index 00000000..44e62bcf --- /dev/null +++ b/lib/flutter/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/lib/flutter/example/ios/.gitignore b/lib/flutter/example/ios/.gitignore new file mode 100644 index 00000000..e96ef602 --- /dev/null +++ b/lib/flutter/example/ios/.gitignore @@ -0,0 +1,32 @@ +*.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/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/lib/flutter/example/ios/Flutter/AppFrameworkInfo.plist b/lib/flutter/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..f2872cf4 --- /dev/null +++ b/lib/flutter/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + 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/lib/flutter/example/ios/Flutter/Debug.xcconfig b/lib/flutter/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/lib/flutter/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/lib/flutter/example/ios/Flutter/Release.xcconfig b/lib/flutter/example/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/lib/flutter/example/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/lib/flutter/example/ios/Runner.xcodeproj/project.pbxproj b/lib/flutter/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..43314e09 --- /dev/null +++ b/lib/flutter/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,495 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + 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 = 1020; + 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)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.spruceid.DIDKitExample; + 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_OPTIMIZATION_LEVEL = "-Owholemodule"; + 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)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.spruceid.DIDKitExample; + 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)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.spruceid.DIDKitExample; + 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/lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/lib/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/lib/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/lib/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..a28140cf --- /dev/null +++ b/lib/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/lib/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/lib/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/lib/flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/lib/flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/lib/flutter/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/lib/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/lib/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/lib/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/lib/flutter/example/ios/Runner/AppDelegate.swift b/lib/flutter/example/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/lib/flutter/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/lib/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/lib/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/lib/flutter/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/lib/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/lib/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/lib/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/flutter/example/ios/Runner/Base.lproj/Main.storyboard b/lib/flutter/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/lib/flutter/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/flutter/example/ios/Runner/Info.plist b/lib/flutter/example/ios/Runner/Info.plist new file mode 100644 index 00000000..9fc0ad41 --- /dev/null +++ b/lib/flutter/example/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + didkit_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/lib/flutter/example/ios/Runner/Runner-Bridging-Header.h b/lib/flutter/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/lib/flutter/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/lib/flutter/example/lib/main.dart b/lib/flutter/example/lib/main.dart new file mode 100644 index 00000000..6c2baa82 --- /dev/null +++ b/lib/flutter/example/lib/main.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:didkit/didkit.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + _MyAppState 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. + try { + platformVersion = await DIDKit.platformVersion; + } 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/lib/flutter/example/pubspec.lock b/lib/flutter/example/pubspec.lock new file mode 100644 index 00000000..eb427d5a --- /dev/null +++ b/lib/flutter/example/pubspec.lock @@ -0,0 +1,167 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.2" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.2" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + didkit: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.2" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.2" +sdks: + dart: ">=2.10.0-0.0.dev <2.10.0" diff --git a/lib/flutter/example/pubspec.yaml b/lib/flutter/example/pubspec.yaml new file mode 100644 index 00000000..42c1ff42 --- /dev/null +++ b/lib/flutter/example/pubspec.yaml @@ -0,0 +1,71 @@ +name: didkit_example +description: Demonstrates how to use the DIDKit plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `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.7.0 <3.0.0" + +dependencies: + flutter: + sdk: flutter + + didkit: + # When depending on this package from a real application you should use: + # didkit: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + +# 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/lib/flutter/example/test/widget_test.dart b/lib/flutter/example/test/widget_test.dart new file mode 100644 index 00000000..ce906d17 --- /dev/null +++ b/lib/flutter/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:didkit_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => widget is Text && + widget.data.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/lib/flutter/ios/.gitignore b/lib/flutter/ios/.gitignore new file mode 100644 index 00000000..aa479fd3 --- /dev/null +++ b/lib/flutter/ios/.gitignore @@ -0,0 +1,37 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/lib/flutter/ios/Assets/.gitkeep b/lib/flutter/ios/Assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/lib/flutter/ios/Classes/DIDKitFlutterPlugin.h b/lib/flutter/ios/Classes/DIDKitFlutterPlugin.h new file mode 100644 index 00000000..2625da56 --- /dev/null +++ b/lib/flutter/ios/Classes/DIDKitFlutterPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface DIDKitFlutterPlugin : NSObject +@end diff --git a/lib/flutter/ios/Classes/DIDKitFlutterPlugin.m b/lib/flutter/ios/Classes/DIDKitFlutterPlugin.m new file mode 100644 index 00000000..9e58545a --- /dev/null +++ b/lib/flutter/ios/Classes/DIDKitFlutterPlugin.m @@ -0,0 +1,8 @@ +#import "DIDKitFlutterPlugin.h" + +#import "didkit.h" + +@implementation DIDKitFlutterPlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { +} +@end diff --git a/lib/flutter/ios/Classes/didkit.h b/lib/flutter/ios/Classes/didkit.h new file mode 120000 index 00000000..5db79f9a --- /dev/null +++ b/lib/flutter/ios/Classes/didkit.h @@ -0,0 +1 @@ +../../../../target/didkit.h \ No newline at end of file diff --git a/lib/flutter/ios/didkit.podspec b/lib/flutter/ios/didkit.podspec new file mode 100644 index 00000000..eed38da5 --- /dev/null +++ b/lib/flutter/ios/didkit.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint didkit.podspec' to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'didkit' + s.version = '0.0.1' + s.summary = 'DIDKit Flutter plugin - iOS implementation' + s.description = <<-DESC +DIDKit Flutter plugin - iOS implementation + DESC + s.homepage = 'https://github.com/spruceid/didkit/tree/main/lib/flutter' + s.license = { :file => '../LICENSE' } + s.author = { 'Spruce Systems, Inc.' => 'hello@spruceid.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + s.platform = :ios, '8.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } +end diff --git a/lib/flutter/ios/libdidkit.a b/lib/flutter/ios/libdidkit.a new file mode 120000 index 00000000..7eedeff4 --- /dev/null +++ b/lib/flutter/ios/libdidkit.a @@ -0,0 +1 @@ +../../../target/universal/release/libdidkit.a \ No newline at end of file diff --git a/lib/flutter/lib/didkit.dart b/lib/flutter/lib/didkit.dart new file mode 100644 index 00000000..d0b10337 --- /dev/null +++ b/lib/flutter/lib/didkit.dart @@ -0,0 +1,23 @@ +library DIDKit; + +import 'dart:ffi'; +import 'dart:io'; +import 'package:ffi/ffi.dart'; + +typedef get_version_func = Pointer Function(); +typedef GetVersion = Pointer Function(); + +// TODO: support macOS +final DynamicLibrary lib = Platform.isAndroid || Platform.isLinux + ? DynamicLibrary.open("libdidkit.so") + : DynamicLibrary.process(); + +final GetVersion get_version = lib + .lookup>('didkit_get_version') + .asFunction(); + +class DIDKit { + static String getVersion() { + return Utf8.fromUtf8(get_version()); + } +} diff --git a/lib/flutter/libdidkit.so b/lib/flutter/libdidkit.so new file mode 120000 index 00000000..3e79ab96 --- /dev/null +++ b/lib/flutter/libdidkit.so @@ -0,0 +1 @@ +../../target/release/libdidkit.so \ No newline at end of file diff --git a/lib/flutter/pubspec.lock b/lib/flutter/pubspec.lock new file mode 100644 index 00000000..d8b633d8 --- /dev/null +++ b/lib/flutter/pubspec.lock @@ -0,0 +1,153 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + ffi: + dependency: "direct main" + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + flutter: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.2" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" +sdks: + dart: ">=2.10.0-110 <=2.11.0-185.0.dev" diff --git a/lib/flutter/pubspec.yaml b/lib/flutter/pubspec.yaml new file mode 100644 index 00000000..843eaa1c --- /dev/null +++ b/lib/flutter/pubspec.yaml @@ -0,0 +1,24 @@ +name: didkit +description: A new flutter plugin project. +version: 0.0.1 +author: Charles E. Lehner +homepage: https://github.com/spruceid/didkit/tree/main/lib/flutter + +environment: + sdk: ">=2.7.0 <3.0.0" + +dependencies: + ffi: ^0.1.3 + +dev_dependencies: + flutter_test: + sdk: flutter + +flutter: + plugin: + platforms: + android: + package: com.spruceid.DIDKit + pluginClass: DIDKitFlutterPlugin + ios: + pluginClass: DIDKitFlutterPlugin diff --git a/lib/flutter/test/didkit_test.dart b/lib/flutter/test/didkit_test.dart new file mode 100644 index 00000000..bb347b0d --- /dev/null +++ b/lib/flutter/test/didkit_test.dart @@ -0,0 +1,11 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:didkit/didkit.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + test('getVersion', () async { + expect(DIDKit.getVersion(), isInstanceOf()); + }); +} diff --git a/lib/java/README.md b/lib/java/README.md new file mode 100644 index 00000000..3a7f4f07 --- /dev/null +++ b/lib/java/README.md @@ -0,0 +1,22 @@ +# DIDKit - Java + +Java bindings for DIDKit, using [JNI][]. The [JAR][] file includes Java class files. To use this in an application, you must also include the shared library (`libdidkit.so`) in your application in your Java Library Path. + +## Build + +In the parent directory, run: +``` +make ../target/didkit.jar +``` + +To build the shared library: +``` +make ../target/release/libdidkit.so +``` + +## Android + +For Android, you can use the separate [Android library (AAR file)](../android/) which includes the Java class files and compiled shared libraries. + +[JAR]: https://en.wikipedia.org/wiki/JAR_(file_format) +[JNI]: https://en.wikipedia.org/wiki/Java_Native_Interface diff --git a/lib/java/com/spruceid/DIDKit.java b/lib/java/com/spruceid/DIDKit.java new file mode 100644 index 00000000..f65942a3 --- /dev/null +++ b/lib/java/com/spruceid/DIDKit.java @@ -0,0 +1,14 @@ +package com.spruceid; + +public class DIDKit { + public static native String getVersion(); + + static { + System.loadLibrary("didkit"); + } + + public static void main(String[] args) { + String version = DIDKit.getVersion(); + System.out.println("Java libdidkit version: " + version); + } +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index e69de29b..1efafd47 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -0,0 +1,22 @@ +// pub use ssi::vc::Credential as VerifiableCredential; + +use std::os::raw::c_char; + +pub static VERSION: &str = env!("CARGO_PKG_VERSION"); +pub static VERSION_C: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); + +#[no_mangle] +pub extern "C" fn didkit_get_version() -> *const c_char { + VERSION_C.as_ptr() as *const c_char +} + +use jni::objects::JClass; +use jni::sys::jstring; +use jni::JNIEnv; + +#[no_mangle] +pub extern "system" fn Java_com_spruceid_DIDKit_getVersion(env: JNIEnv, _class: JClass) -> jstring { + env.new_string(VERSION) + .expect("Unable to create Java string") + .into_inner() +} diff --git a/lib/tests/vc.rs b/lib/tests/vc.rs new file mode 100644 index 00000000..f8ebd1f6 --- /dev/null +++ b/lib/tests/vc.rs @@ -0,0 +1,16 @@ +use didkit::VerifiableCredential; + +#[test] +fn verify_credential() { + let vc_str = r###"{ + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "http://example.org/credentials/3731", + "type": ["VerifiableCredential"], + "issuer": "did:example:30e07a529f32d234f6181736bd3", + "issuanceDate": "2020-08-19T21:41:50Z", + "credentialSubject": { + "id": "did:example:d23dd687a7dc6787646f2eb98d0" + } + }"###; + let cred = VerifiableCredential::from_json_unsigned(vc_str).unwrap(); +} From ef70465280dda099dde7e91b16bbe8424cfb10a8 Mon Sep 17 00:00:00 2001 From: "Charles E. Lehner" Date: Mon, 19 Oct 2020 14:19:45 -0400 Subject: [PATCH 3/3] Implement JSON-based credential functionality Implemented in CLI, C library, and Flutter plugin: - Generate key and did:key for Ed25519 - VC and VP issue and verify --- cli/Cargo.toml | 4 +- cli/src/main.rs | 156 +++++++++++++++++++-- cli/tests/cli.rs | 98 +++++++++++++ cli/tests/credential-unsigned.jsonld | 10 ++ cli/tests/ed25519-key.jwk | 1 + cli/tests/presentation-unsigned.jsonld | 7 + http/Cargo.toml | 2 +- lib/Cargo.toml | 2 + lib/Makefile | 10 +- lib/c/test.c | 90 ++++++++++-- lib/cbindings/Cargo.toml | 2 +- lib/flutter/lib/didkit.dart | 106 +++++++++++++- lib/flutter/pubspec.lock | 6 +- lib/flutter/test/didkit_test.dart | 77 +++++++++++ lib/src/c.rs | 183 +++++++++++++++++++++++++ lib/src/error.rs | 134 ++++++++++++++++++ lib/src/jni.rs | 12 ++ lib/src/lib.rs | 32 ++--- lib/tests/vc.rs | 2 +- 19 files changed, 876 insertions(+), 58 deletions(-) create mode 100644 cli/tests/cli.rs create mode 100644 cli/tests/credential-unsigned.jsonld create mode 100644 cli/tests/ed25519-key.jwk create mode 100644 cli/tests/presentation-unsigned.jsonld create mode 100644 lib/src/c.rs create mode 100644 lib/src/error.rs create mode 100644 lib/src/jni.rs diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 6eb9efe6..5bc56971 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,11 +1,13 @@ [package] -name = "didkit-cli" +name = "didkit_cli" version = "0.0.1" authors = ["Charles E. Lehner "] edition = "2018" [dependencies] +chrono = { version = "0.4", features = ["serde"] } didkit = { path = "../lib" } +serde_json = "1.0" structopt = "0.3" [[bin]] diff --git a/cli/src/main.rs b/cli/src/main.rs index 968b748d..b677633b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,7 +1,25 @@ +use std::fs::File; +use std::io::{stdin, stdout, BufReader, BufWriter}; +use std::path::PathBuf; + +use chrono::prelude::*; use structopt::StructOpt; +use didkit::{ + LinkedDataProofOptions, ProofPurpose, VerifiableCredential, VerifiablePresentation, JWK, +}; + #[derive(StructOpt, Debug)] -enum DIDKit { +pub enum DIDKit { + /// Generate and output a Ed25519 keypair in JWK format + GenerateEd25519Key, + /// Output a did:key DID for a JWK + KeyToDIDKey { + #[structopt(short, long, parse(from_os_str))] + key: PathBuf, + }, + + /* // DID Functionality /// Create new DID Document. DIDCreate {}, @@ -17,19 +35,38 @@ enum DIDKit { DIDDeactivate {}, /// Create a Signed IETF JSON Patch to update a DID document. DIDPatch {}, - + */ // VC Functionality /// Issue Credential - VCIssueCredential {}, + VCIssueCredential { + #[structopt(short, long, parse(from_os_str))] + key: PathBuf, + #[structopt(flatten)] + proof_options: ProofOptions, + }, /// Verify Credential - VCVerifyCredential {}, + VCVerifyCredential { + #[structopt(flatten)] + proof_options: ProofOptions, + }, /// Issue Presentation - VCIssuePresentation {}, + VCIssuePresentation { + #[structopt(short, long, parse(from_os_str))] + key: PathBuf, + #[structopt(flatten)] + proof_options: ProofOptions, + }, /// Verify Presentation - VCVerifyPresentation {}, + VCVerifyPresentation { + #[structopt(flatten)] + proof_options: ProofOptions, + }, + /* /// Revoke Credential VCRevokeCredential {}, + */ + /* // DIDComm Functionality (???) /// Discover a messaging endpoint from a DID which supports DIDComm. DIDCommDiscover {}, @@ -37,10 +74,113 @@ enum DIDKit { DIDCommSend {}, /// Receive a DIDComm message. DIDCommReceive {}, + */ +} + +#[derive(StructOpt, Debug)] +pub struct ProofOptions { + #[structopt(short, long)] + pub verification_method: Option, + #[structopt(short, long)] + pub proof_purpose: Option, + #[structopt(short, long)] + pub created: Option>, + #[structopt(short = "C", long)] + pub challenge: Option, + #[structopt(short, long)] + pub domain: Option, +} + +impl From for LinkedDataProofOptions { + fn from(options: ProofOptions) -> LinkedDataProofOptions { + LinkedDataProofOptions { + verification_method: options.verification_method, + proof_purpose: options.proof_purpose, + created: options.created, + challenge: options.challenge, + domain: options.domain, + } + } +} + +fn read_key(key_path: PathBuf) -> JWK { + let key_file = File::open(key_path).unwrap(); + let key_reader = BufReader::new(key_file); + let key: JWK = serde_json::from_reader(key_reader).unwrap(); + key } fn main() { let opt = DIDKit::from_args(); - println!("{:?}", opt); - // TODO + match opt { + DIDKit::GenerateEd25519Key => { + let jwk = JWK::generate_ed25519().unwrap(); + let jwk_str = serde_json::to_string(&jwk).unwrap(); + println!("{}", jwk_str); + } + + DIDKit::KeyToDIDKey { key: key_path } => { + let key = read_key(key_path); + let did = key.to_did().unwrap(); + println!("{}", did); + } + + DIDKit::VCIssueCredential { + key: key_path, + proof_options, + } => { + let key: JWK = read_key(key_path); + let credential_reader = BufReader::new(stdin()); + let mut credential: VerifiableCredential = + serde_json::from_reader(credential_reader).unwrap(); + let options = LinkedDataProofOptions::from(proof_options); + let proof = credential.generate_proof(&key, &options).unwrap(); + credential.add_proof(proof); + let stdout_writer = BufWriter::new(stdout()); + serde_json::to_writer(stdout_writer, &credential).unwrap(); + } + + DIDKit::VCVerifyCredential { proof_options } => { + let credential_reader = BufReader::new(stdin()); + let credential: VerifiableCredential = + serde_json::from_reader(credential_reader).unwrap(); + let options = LinkedDataProofOptions::from(proof_options); + credential.validate_unsigned().unwrap(); + let result = credential.verify(Some(options)); + let stdout_writer = BufWriter::new(stdout()); + serde_json::to_writer(stdout_writer, &result).unwrap(); + if result.errors.len() > 0 { + std::process::exit(2); + } + } + + DIDKit::VCIssuePresentation { + key: key_path, + proof_options, + } => { + let key: JWK = read_key(key_path); + let presentation_reader = BufReader::new(stdin()); + let mut presentation: VerifiablePresentation = + serde_json::from_reader(presentation_reader).unwrap(); + let options = LinkedDataProofOptions::from(proof_options); + let proof = presentation.generate_proof(&key, &options).unwrap(); + presentation.add_proof(proof); + let stdout_writer = BufWriter::new(stdout()); + serde_json::to_writer(stdout_writer, &presentation).unwrap(); + } + + DIDKit::VCVerifyPresentation { proof_options } => { + let presentation_reader = BufReader::new(stdin()); + let presentation: VerifiablePresentation = + serde_json::from_reader(presentation_reader).unwrap(); + let options = LinkedDataProofOptions::from(proof_options); + presentation.validate_unsigned().unwrap(); + let result = presentation.verify(Some(options)); + let stdout_writer = BufWriter::new(stdout()); + serde_json::to_writer(stdout_writer, &result).unwrap(); + if result.errors.len() > 0 { + std::process::exit(2); + } + } + } } diff --git a/cli/tests/cli.rs b/cli/tests/cli.rs new file mode 100644 index 00000000..266c6912 --- /dev/null +++ b/cli/tests/cli.rs @@ -0,0 +1,98 @@ +use std::fs::File; +use std::io::{BufReader, Write}; +use std::process::{Command, Stdio}; + +static BIN: &str = env!("CARGO_BIN_EXE_didkit"); + +#[test] +fn generate_key() { + Command::new(BIN) + .arg("generate-ed25519-key") + .output() + .unwrap(); +} + +#[test] +fn issue_verify_credential_presentation() { + // Get DID for key + let did_output = Command::new(BIN) + .args(&["key-to-did-key", "-k", "tests/ed25519-key.jwk"]) + .stderr(Stdio::inherit()) + .output() + .unwrap(); + assert!(did_output.status.success()); + let did = String::from_utf8(did_output.stdout).unwrap(); + + // Issue credential + let credential_file = File::open("tests/credential-unsigned.jsonld").unwrap(); + let issue_credential = Command::new(BIN) + .args(&[ + "vc-issue-credential", + "-k", + "tests/ed25519-key.jwk", + "-v", + &did.trim(), + "-p", + "assertionMethod", + ]) + .stdin(Stdio::from(credential_file)) + .stderr(Stdio::inherit()) + .output() + .unwrap(); + assert!(issue_credential.status.success()); + let vc = issue_credential.stdout; + + // Verify credential + let mut verify_credential = Command::new(BIN) + .args(&["vc-verify-credential", "-p", "assertionMethod"]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .unwrap(); + let verify_stdin = verify_credential.stdin.as_mut().unwrap(); + verify_stdin.write_all(&vc).unwrap(); + let verify_output = verify_credential.wait_with_output().unwrap(); + assert!(verify_output.status.success()); + + // Issue presentation with credential + let presentation_file = File::open("tests/presentation-unsigned.jsonld").unwrap(); + let mut presentation: serde_json::Value = + serde_json::from_reader(BufReader::new(presentation_file)).unwrap(); + let vc_value = serde_json::from_slice(&vc).unwrap(); + presentation["verifiableCredential"] = vc_value; + let mut issue_presentation = Command::new(BIN) + .args(&[ + "vc-issue-presentation", + "-k", + "tests/ed25519-key.jwk", + "-v", + &did.trim(), + "-p", + "authentication", + ]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .unwrap(); + let issue_stdin = issue_presentation.stdin.as_mut().unwrap(); + serde_json::to_writer(issue_stdin, &presentation).unwrap(); + let issue_output = issue_presentation.wait_with_output().unwrap(); + assert!(issue_output.status.success()); + let vp = issue_output.stdout; + // io::stdout().write_all(&vp).unwrap(); + + // Verify presentation + let mut verify_presentation = Command::new(BIN) + .args(&["vc-verify-presentation", "-p", "authentication"]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .unwrap(); + let verify_stdin = verify_presentation.stdin.as_mut().unwrap(); + verify_stdin.write_all(&vp).unwrap(); + let verify_output = verify_presentation.wait_with_output().unwrap(); + assert!(verify_output.status.success()); +} diff --git a/cli/tests/credential-unsigned.jsonld b/cli/tests/credential-unsigned.jsonld new file mode 100644 index 00000000..ece7680d --- /dev/null +++ b/cli/tests/credential-unsigned.jsonld @@ -0,0 +1,10 @@ +{ + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "http://example.org/credentials/3731", + "type": ["VerifiableCredential"], + "issuer": "did:example:30e07a529f32d234f6181736bd3", + "issuanceDate": "2020-08-19T21:41:50Z", + "credentialSubject": { + "id": "did:example:d23dd687a7dc6787646f2eb98d0" + } +} diff --git a/cli/tests/ed25519-key.jwk b/cli/tests/ed25519-key.jwk new file mode 100644 index 00000000..8de62d70 --- /dev/null +++ b/cli/tests/ed25519-key.jwk @@ -0,0 +1 @@ +{"kty":"OKP","crv":"Ed25519","x":"PBcY2yJ4h_cLUnQNcYhplu9KQQBNpGxP4sYcMPdlu6I","d":"n5WUFIghmRYZi0rEYo2lz-Zg2B9B1KW4MYfJXwOXfyI"} diff --git a/cli/tests/presentation-unsigned.jsonld b/cli/tests/presentation-unsigned.jsonld new file mode 100644 index 00000000..be738c54 --- /dev/null +++ b/cli/tests/presentation-unsigned.jsonld @@ -0,0 +1,7 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "http://example.org/presentations/3731", + "type": ["VerifiablePresentation"], + "issuer": "did:example:30e07a529f32d234f6181736bd3", + "issuanceDate": "2020-10-19T11:41:50Z" +} diff --git a/http/Cargo.toml b/http/Cargo.toml index 4f3c0af7..d00574e3 100644 --- a/http/Cargo.toml +++ b/http/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "didkit-http" +name = "didkit_http" version = "0.0.1" authors = ["Charles E. Lehner "] edition = "2018" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index ca504b40..bad2a7a2 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -5,7 +5,9 @@ authors = ["Charles E. Lehner "] edition = "2018" [dependencies] +#didkit_cbindings = { path = "cbindings/" } ssi = { path = "../../ssi" } +serde_json = "1.0" jni = "0.17" [lib] diff --git a/lib/Makefile b/lib/Makefile index c3158c3b..58f22415 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -18,7 +18,7 @@ android/res $(TARGET)/test $(TARGET)/jvm: RUST_SRC=Cargo.toml $(wildcard src/*.rs src/*/*.rs src/*/*/*.rs) $(TARGET)/didkit.h: cbindgen.toml cbindings/build.rs cbindings/Cargo.toml $(RUST_SRC) - cargo build -p didkit-cbindings + cargo build -p didkit_cbindings test -s $@ && touch $@ $(TARGET)/release/libdidkit.so: $(RUST_SRC) @@ -28,11 +28,11 @@ $(TARGET)/release/libdidkit.so: $(RUST_SRC) ## C $(TARGET)/test/c.stamp: $(TARGET)/cabi-test $(TARGET)/release/libdidkit.so | $(TARGET)/test - $(TARGET)/cabi-test + LD_LIBRARY_PATH=$(TARGET)/release $(TARGET)/cabi-test touch $@ -$(TARGET)/cabi-test: c/test.c $(TARGET)/didkit.h - $(CC) -I$(TARGET) $< -ldl -o $@ +$(TARGET)/cabi-test: c/test.c $(TARGET)/release/libdidkit.so $(TARGET)/didkit.h + $(CC) -I$(TARGET) -L$(TARGET)/release $< -ldl -ldidkit -o $@ ## Java @@ -90,7 +90,7 @@ $(TARGET)/%/release/libdidkit.so: $(RUST_SRC) ## Flutter -$(TARGET)/test/flutter.stamp: flutter/lib/didkit.dart $(TARGET)/release/libdidkit.so | $(TARGET)/test +$(TARGET)/test/flutter.stamp: flutter/lib/didkit.dart flutter/test/didkit_test.dart $(TARGET)/release/libdidkit.so | $(TARGET)/test cd flutter && LD_LIBRARY_PATH=$$PWD \ flutter --suppress-analytics test touch $@ diff --git a/lib/c/test.c b/lib/c/test.c index 09143e7d..821c2adf 100644 --- a/lib/c/test.c +++ b/lib/c/test.c @@ -1,15 +1,87 @@ -#include +#include #include #include +#include + #include "didkit.h" int main() { - void *lib = dlopen("../target/release/libdidkit.so", RTLD_NOW); - if (lib == NULL) errx(1, "dlopen: %s", dlerror()); - const char *(*didkit_get_version)() = dlsym(lib, "didkit_get_version"); - if (didkit_get_version == NULL) errx(1, "unable to find version function"); - const char *version = didkit_get_version(); - printf("C libdidkit version: %s\n", version); - int rc = dlclose(lib); - if (rc < 0) errx(1, "dlclose: %s", dlerror()); + const char *version = didkit_get_version(); + assert(version != NULL); + assert(strlen(version) > 0); + + // Trigger error + const char *vp = didkit_vc_issue_presentation("{}", "{}", "{}"); + assert(vp == NULL); + const char *error_msg = didkit_error_message(); + assert(error_msg != NULL); + assert(strlen(error_msg) > 0); + int error_code = didkit_error_code(); + assert(error_code != 0); + + // Generate key + const char *key = didkit_vc_generate_ed25519_key(); + if (key == NULL) errx(1, "generate key: %s", didkit_error_message()); + + // Get did:key for key + const char *did = didkit_key_to_did(key); + if (key == NULL) errx(1, "key to did: %s", didkit_error_message()); + + // Issue Credential + const char *credential = "{" + " \"@context\": \"https://www.w3.org/2018/credentials/v1\"," + " \"id\": \"http://example.org/credentials/3731\"," + " \"type\": [\"VerifiableCredential\"]," + " \"issuer\": \"did:example:30e07a529f32d234f6181736bd3\"," + " \"issuanceDate\": \"2020-08-19T21:41:50Z\"," + " \"credentialSubject\": {" + " \"id\": \"did:example:d23dd687a7dc6787646f2eb98d0\"" + " }" + "}"; + char vc_options[0x1000]; + snprintf(vc_options, sizeof vc_options, "{" + " \"type\":\"Ed25519VerificationKey2018\"," + " \"proofPurpose\": \"assertionMethod\"," + " \"verificationMethod\": \"%s\"" + "}", did); + const char *vc = didkit_vc_issue_credential(credential, vc_options, key); + if (vc == NULL) errx(1, "issue credential: %s", didkit_error_message()); + + // Verify Credential + const char *vc_verify_options = "{\"proofPurpose\": \"assertionMethod\"}"; + const char *res = didkit_vc_verify_credential(vc, vc_verify_options); + if (res == NULL) errx(1, "verify credential: %s", didkit_error_message()); + if (strstr(res, "\"errors\":[]") == NULL) errx(1, "verify credential result: %s", res); + didkit_free_string(res); + + // Issue Presentation + char presentation[0x1000]; + snprintf(presentation, sizeof presentation, "{" + " \"@context\": [\"https://www.w3.org/2018/credentials/v1\"]," + " \"id\": \"http://example.org/presentations/3731\"," + " \"type\": [\"VerifiablePresentation\"]," + " \"issuer\": \"did:example:30e07a529f32d234f6181736bd3\"," + " \"issuanceDate\": \"2020-10-19T11:41:50Z\"," + " \"verifiableCredential\": %s" + "}", vc); + char vp_options[0x1000]; + snprintf(vp_options, sizeof vp_options, "{" + " \"type\":\"Ed25519VerificationKey2018\"," + " \"proofPurpose\": \"authentication\"," + " \"verificationMethod\": \"%s\"" + "}", did); + vp = didkit_vc_issue_presentation(presentation, vp_options, key); + if (vp == NULL) errx(1, "issue presentation: %s", didkit_error_message()); + + // Verify Presentation + const char *vp_verify_options = "{\"proofPurpose\": \"authentication\"}"; + res = didkit_vc_verify_presentation(vp, vp_verify_options); + if (res == NULL) errx(1, "verify presentation: %s", didkit_error_message()); + if (strstr(res, "\"errors\":[]") == NULL) errx(1, "verify presentation result: %s", res); + didkit_free_string(res); + + didkit_free_string(vp); + didkit_free_string(vc); + didkit_free_string(did); + didkit_free_string(key); } diff --git a/lib/cbindings/Cargo.toml b/lib/cbindings/Cargo.toml index 412f6db3..e4f753f9 100644 --- a/lib/cbindings/Cargo.toml +++ b/lib/cbindings/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "didkit-cbindings" +name = "didkit_cbindings" version = "0.1.0" authors = ["Charles E. Lehner "] edition = "2018" diff --git a/lib/flutter/lib/didkit.dart b/lib/flutter/lib/didkit.dart index d0b10337..ceea23e7 100644 --- a/lib/flutter/lib/didkit.dart +++ b/lib/flutter/lib/didkit.dart @@ -4,20 +4,112 @@ import 'dart:ffi'; import 'dart:io'; import 'package:ffi/ffi.dart'; -typedef get_version_func = Pointer Function(); -typedef GetVersion = Pointer Function(); - -// TODO: support macOS +// TODO: support macOS, Windows final DynamicLibrary lib = Platform.isAndroid || Platform.isLinux ? DynamicLibrary.open("libdidkit.so") : DynamicLibrary.process(); -final GetVersion get_version = lib - .lookup>('didkit_get_version') - .asFunction(); +final get_version = lib + .lookupFunction Function(), Pointer Function()>('didkit_get_version'); + +final get_error_message = lib + .lookupFunction Function(), Pointer Function()>('didkit_error_message'); + +final get_error_code = lib + .lookupFunction('didkit_error_code'); + +final generate_ed25519_key = lib + .lookupFunction Function(), Pointer Function()>('didkit_vc_generate_ed25519_key'); + +final key_to_did_key = lib + .lookupFunction Function(Pointer), Pointer Function(Pointer)>('didkit_key_to_did'); + +final issue_credential = lib + .lookupFunction Function(Pointer, Pointer, Pointer), Pointer Function(Pointer, Pointer, Pointer)>('didkit_vc_issue_credential'); + +final verify_credential = lib + .lookupFunction Function(Pointer, Pointer), Pointer Function(Pointer, Pointer)>('didkit_vc_verify_credential'); + +final issue_presentation = lib + .lookupFunction Function(Pointer, Pointer, Pointer), Pointer Function(Pointer, Pointer, Pointer)>('didkit_vc_issue_presentation'); + +final verify_presentation = lib + .lookupFunction Function(Pointer, Pointer), Pointer Function(Pointer, Pointer)>('didkit_vc_verify_presentation'); + +final free_string = lib + .lookupFunction), void Function(Pointer)>('didkit_free_string'); + +class DIDKitException implements Exception { + int code; + String message; + DIDKitException(this.code, this.message); + String toString() { + return "DIDKitException ($code): $message"; + } +} + +DIDKitException lastError() { + final code = get_error_code(); + final message_utf8 = get_error_message(); + final message_string = message_utf8.address == nullptr.address + ? "Unable to get error message" + : Utf8.fromUtf8(message_utf8); + + return new DIDKitException(code, message_string); +} class DIDKit { + static String getVersion() { return Utf8.fromUtf8(get_version()); } + + static String generateEd25519Key() { + final key = generate_ed25519_key(); + if (key.address == nullptr.address) throw lastError(); + final key_string = Utf8.fromUtf8(key); + free_string(key); + return key_string; + } + + static String keyToDIDKey(String key) { + final did_key = key_to_did_key(Utf8.toUtf8(key)); + if (did_key.address == nullptr.address) throw lastError(); + final did_key_string = Utf8.fromUtf8(did_key); + free_string(did_key); + return did_key_string; + } + + static String issueCredential(String credential, String options, String key) { + final vc = issue_credential(Utf8.toUtf8(credential), Utf8.toUtf8(options), Utf8.toUtf8(key)); + if (vc.address == nullptr.address) throw lastError(); + final vc_string = Utf8.fromUtf8(vc); + free_string(vc); + return vc_string; + } + + static String verifyCredential(String credential, String options) { + final result = verify_credential(Utf8.toUtf8(credential), Utf8.toUtf8(options)); + if (result.address == nullptr.address) throw lastError(); + final result_string = Utf8.fromUtf8(result); + free_string(result); + return result_string; + } + + static String issuePresentation(String presentation, String options, String key) { + final vp = issue_presentation(Utf8.toUtf8(presentation), Utf8.toUtf8(options), Utf8.toUtf8(key)); + if (vp.address == nullptr.address) throw lastError(); + final vp_string = Utf8.fromUtf8(vp); + free_string(vp); + return vp_string; + } + + static String verifyPresentation(String presentation, String options) { + final result = verify_presentation(Utf8.toUtf8(presentation), Utf8.toUtf8(options)); + if (result.address == nullptr.address) throw lastError(); + final result_string = Utf8.fromUtf8(result); + free_string(result); + return result_string; + } + } diff --git a/lib/flutter/pubspec.lock b/lib/flutter/pubspec.lock index d8b633d8..80b31217 100644 --- a/lib/flutter/pubspec.lock +++ b/lib/flutter/pubspec.lock @@ -80,7 +80,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0-nullsafety.4" path: dependency: transitive description: @@ -106,7 +106,7 @@ packages: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.2" + version: "1.10.0-nullsafety.4" stream_channel: dependency: transitive description: @@ -150,4 +150,4 @@ packages: source: hosted version: "2.1.0-nullsafety.3" sdks: - dart: ">=2.10.0-110 <=2.11.0-185.0.dev" + dart: ">=2.10.0-110 <=2.11.0-228.0.dev" diff --git a/lib/flutter/test/didkit_test.dart b/lib/flutter/test/didkit_test.dart index bb347b0d..8bfc55be 100644 --- a/lib/flutter/test/didkit_test.dart +++ b/lib/flutter/test/didkit_test.dart @@ -1,6 +1,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:didkit/didkit.dart'; +import 'dart:convert'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -8,4 +9,80 @@ void main() { test('getVersion', () async { expect(DIDKit.getVersion(), isInstanceOf()); }); + + test('Exception', () async { + expect(() => DIDKit.issuePresentation("", "", ""), throwsA(isInstanceOf())); + }); + + test('generateEd25519Key', () async { + expect(DIDKit.generateEd25519Key(), isInstanceOf()); + }); + + test('keyToDIDKey', () async { + final key = DIDKit.generateEd25519Key(); + final did = DIDKit.keyToDIDKey(key); + expect(did, isInstanceOf()); + }); + + test('issueCredential, verifyCredential', () async { + final key = DIDKit.generateEd25519Key(); + final did = DIDKit.keyToDIDKey(key); + final options = { + "type": "Ed25519VerificationKey2018", + "proofPurpose": "assertionMethod", + "verificationMethod": did + }; + final credential = { + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "http://example.org/credentials/3731", + "type": ["VerifiableCredential"], + "issuer": "did:example:30e07a529f32d234f6181736bd3", + "issuanceDate": "2020-08-19T21:41:50Z", + "credentialSubject": { + "id": "did:example:d23dd687a7dc6787646f2eb98d0" + } + }; + final vc = DIDKit.issueCredential(jsonEncode(credential), jsonEncode(options), key); + + final verifyOptions = { + "proofPurpose": "assertionMethod" + }; + final verifyResult = jsonDecode(DIDKit.verifyCredential(vc, jsonEncode(verifyOptions))); + expect(verifyResult['errors'], isEmpty); + }); + + test('issuePresentation, verifyPresentation', () async { + final key = DIDKit.generateEd25519Key(); + final did = DIDKit.keyToDIDKey(key); + final options = { + "type": "Ed25519VerificationKey2018", + "proofPurpose": "authentication", + "verificationMethod": did + }; + final presentation = { + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "http://example.org/presentations/3731", + "type": ["VerifiablePresentation"], + "issuer": "did:example:30e07a529f32d234f6181736bd3", + "issuanceDate": "2020-08-19T21:41:50Z", + "verifiableCredential": { + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "http://example.org/credentials/3731", + "type": ["VerifiableCredential"], + "issuer": "did:example:30e07a529f32d234f6181736bd3", + "issuanceDate": "2020-08-19T21:41:50Z", + "credentialSubject": { + "id": "did:example:d23dd687a7dc6787646f2eb98d0" + } + } + }; + final vc = DIDKit.issuePresentation(jsonEncode(presentation), jsonEncode(options), key); + + final verifyOptions = { + "proofPurpose": "authentication" + }; + final verifyResult = jsonDecode(DIDKit.verifyPresentation(vc, jsonEncode(verifyOptions))); + expect(verifyResult['errors'], isEmpty); + }); + } diff --git a/lib/src/c.rs b/lib/src/c.rs new file mode 100644 index 00000000..7bb3caaa --- /dev/null +++ b/lib/src/c.rs @@ -0,0 +1,183 @@ +use std::ffi::CStr; +use std::ffi::CString; +use std::os::raw::c_char; +use std::ptr; + +use crate::error::Error; +use crate::LinkedDataProofOptions; +use crate::VerifiableCredential; +use crate::VerifiablePresentation; +use crate::JWK; + +pub static VERSION_C: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); + +#[no_mangle] +pub extern "C" fn didkit_get_version() -> *const c_char { + VERSION_C.as_ptr() as *const c_char +} + +fn ccchar_or_error(result: Result<*const c_char, Error>) -> *const c_char { + match result { + Ok(ccchar) => ccchar, + Err(error) => { + error.stash(); + ptr::null() + } + } +} + +// TODO: instead of having two of each function, make a procedural macro to wrap each function. Or +// implement std::ops::Try (nightly). + +// Generate Ed25519 key +fn generate_ed25519_key() -> Result<*const c_char, Error> { + let jwk = JWK::generate_ed25519()?; + Ok(CString::new(serde_json::to_string(&jwk)?)?.into_raw()) +} +#[no_mangle] +pub extern "C" fn didkit_vc_generate_ed25519_key() -> *const c_char { + ccchar_or_error(generate_ed25519_key()) +} + +// Convert JWK to did:key DID +fn key_to_did(key_json_ptr: *const c_char) -> Result<*const c_char, Error> { + let key_json = unsafe { CStr::from_ptr(key_json_ptr) }.to_str()?; + let key: JWK = serde_json::from_str(key_json)?; + let did = key.to_did()?; + Ok(CString::new(did)?.into_raw()) +} +#[no_mangle] +pub extern "C" fn didkit_key_to_did(jwk: *const c_char) -> *const c_char { + ccchar_or_error(key_to_did(jwk)) +} + +// Issue Credential +fn issue_credential( + credential_json_ptr: *const c_char, + linked_data_proof_options_json_ptr: *const c_char, + key_json_ptr: *const c_char, +) -> Result<*const c_char, Error> { + let credential_json = unsafe { CStr::from_ptr(credential_json_ptr) }.to_str()?; + let linked_data_proof_options_json = + unsafe { CStr::from_ptr(linked_data_proof_options_json_ptr) }.to_str()?; + let key_json = unsafe { CStr::from_ptr(key_json_ptr) }.to_str()?; + let mut credential = VerifiableCredential::from_json_unsigned(credential_json)?; + let key: JWK = serde_json::from_str(key_json)?; + let options: LinkedDataProofOptions = serde_json::from_str(linked_data_proof_options_json)?; + let proof = credential.generate_proof(&key, &options)?; + credential.add_proof(proof); + Ok(CString::new(serde_json::to_string(&credential)?)?.into_raw()) +} +#[no_mangle] +pub extern "C" fn didkit_vc_issue_credential( + credential_json: *const c_char, + linked_data_proof_options_json: *const c_char, + key_json: *const c_char, +) -> *const c_char { + ccchar_or_error(issue_credential( + credential_json, + linked_data_proof_options_json, + key_json, + )) +} + +// Verify Credential +fn verify_credential( + credential_json_ptr: *const c_char, + linked_data_proof_options_json_ptr: *const c_char, +) -> Result<*const c_char, Error> { + let credential_json = unsafe { CStr::from_ptr(credential_json_ptr) }.to_str()?; + let linked_data_proof_options_json = + unsafe { CStr::from_ptr(linked_data_proof_options_json_ptr) }.to_str()?; + let credential = VerifiableCredential::from_json_unsigned(credential_json)?; + let options: LinkedDataProofOptions = serde_json::from_str(linked_data_proof_options_json)?; + let result = credential.verify(Some(options)); + Ok(CString::new(serde_json::to_string(&result)?)?.into_raw()) +} +#[no_mangle] +pub extern "C" fn didkit_vc_verify_credential( + credential_json: *const c_char, + linked_data_proof_options_json: *const c_char, +) -> *const c_char { + ccchar_or_error(verify_credential( + credential_json, + linked_data_proof_options_json, + )) +} + +// Issue Presentation +fn issue_presentation( + presentation_json_ptr: *const c_char, + linked_data_proof_options_json_ptr: *const c_char, + key_json_ptr: *const c_char, +) -> Result<*const c_char, Error> { + let presentation_json = unsafe { CStr::from_ptr(presentation_json_ptr) }.to_str()?; + let linked_data_proof_options_json = + unsafe { CStr::from_ptr(linked_data_proof_options_json_ptr) }.to_str()?; + let key_json = unsafe { CStr::from_ptr(key_json_ptr) }.to_str()?; + let mut presentation = VerifiablePresentation::from_json_unsigned(presentation_json)?; + let key: JWK = serde_json::from_str(key_json)?; + let options: LinkedDataProofOptions = serde_json::from_str(linked_data_proof_options_json)?; + let proof = presentation.generate_proof(&key, &options)?; + presentation.add_proof(proof); + Ok(CString::new(serde_json::to_string(&presentation)?)?.into_raw()) +} +#[no_mangle] +pub extern "C" fn didkit_vc_issue_presentation( + presentation_json: *const c_char, + linked_data_proof_options_json: *const c_char, + key_json: *const c_char, +) -> *const c_char { + ccchar_or_error(issue_presentation( + presentation_json, + linked_data_proof_options_json, + key_json, + )) +} + +// Verify Presentation +fn verify_presentation( + presentation_json_ptr: *const c_char, + linked_data_proof_options_json_ptr: *const c_char, +) -> Result<*const c_char, Error> { + let presentation_json = unsafe { CStr::from_ptr(presentation_json_ptr) }.to_str()?; + let linked_data_proof_options_json = + unsafe { CStr::from_ptr(linked_data_proof_options_json_ptr) }.to_str()?; + let presentation = VerifiablePresentation::from_json_unsigned(presentation_json)?; + let options: LinkedDataProofOptions = serde_json::from_str(linked_data_proof_options_json)?; + let result = presentation.verify(Some(options)); + Ok(CString::new(serde_json::to_string(&result)?)?.into_raw()) +} +#[no_mangle] +pub extern "C" fn didkit_vc_verify_presentation( + presentation_json: *const c_char, + linked_data_proof_options_json: *const c_char, +) -> *const c_char { + ccchar_or_error(verify_presentation( + presentation_json, + linked_data_proof_options_json, + )) +} + +#[no_mangle] +pub extern "C" fn didkit_free_string(string: *const c_char) { + if string.is_null() { + return; + } + unsafe { + CString::from_raw(string as *mut c_char); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_version() { + let version = didkit_get_version(); + let version_cstr = unsafe { CStr::from_ptr(version) }; + let version_str = version_cstr.to_str().unwrap(); + assert!(version_str.len() > 0); + } +} diff --git a/lib/src/error.rs b/lib/src/error.rs new file mode 100644 index 00000000..605664f7 --- /dev/null +++ b/lib/src/error.rs @@ -0,0 +1,134 @@ +use std::cell::BorrowError; +use std::ffi::CString; +use std::ffi::NulError; +use std::fmt; +use std::os::raw::{c_char, c_int}; +use std::ptr; + +use serde_json::Error as JSONError; +use ssi::error::Error as SSIError; +use std::str::Utf8Error; + +static UNKNOWN_ERROR: &str = "Unable to create error string\0"; + +use std::cell::RefCell; +thread_local! { + pub static LAST_ERROR: RefCell> = RefCell::new(None); +} + +#[derive(Debug)] +pub enum Error { + SSI(ssi::error::Error), + Null(NulError), + Utf8(Utf8Error), + Borrow(BorrowError), + + #[doc(hidden)] + __Nonexhaustive, +} + +impl Error { + pub fn stash(self) { + LAST_ERROR.with(|stash| { + stash.replace(Some(( + self.get_code(), + CString::new(self.to_string()).unwrap(), + ))) + }); + } + + fn get_code(&self) -> c_int { + // TODO: try to give each individual error its own number + match self { + Error::SSI(_) => 1, + Error::Null(_) => 2, + Error::Utf8(_) => 3, + _ => -1, + } + } +} + +#[no_mangle] +pub extern "C" fn didkit_error_message() -> *const c_char { + LAST_ERROR.with(|error| match error.try_borrow() { + Ok(maybe_err_ref) => match &*maybe_err_ref { + Some(err) => err.1.as_ptr() as *const c_char, + None => ptr::null(), + }, + Err(_) => UNKNOWN_ERROR.as_ptr() as *const c_char, + }) +} + +#[no_mangle] +pub extern "C" fn didkit_error_code() -> c_int { + LAST_ERROR.with(|error| match error.try_borrow() { + Ok(maybe_err_ref) => match &*maybe_err_ref { + Some(err) => err.0, + None => 0, + }, + Err(err) => Error::from(err).get_code(), + }) +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::SSI(e) => e.fmt(f), + Error::Null(e) => e.fmt(f), + Error::Utf8(e) => e.fmt(f), + _ => unreachable!(), + } + } +} + +impl From for Error { + fn from(err: SSIError) -> Error { + Error::SSI(err) + } +} + +impl From for Error { + fn from(err: JSONError) -> Error { + Error::SSI(SSIError::from(err)) + } +} + +impl From for Error { + fn from(err: NulError) -> Error { + Error::Null(err) + } +} + +impl From for Error { + fn from(err: Utf8Error) -> Error { + Error::Utf8(err) + } +} + +impl From for Error { + fn from(err: BorrowError) -> Error { + Error::Borrow(err) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn errors() { + use crate::c::didkit_vc_issue_presentation; + use std::ffi::CStr; + let presentation = CString::new("{}").unwrap().as_ptr(); + let options = CString::new("{}").unwrap().as_ptr(); + let key = CString::new("{}").unwrap().as_ptr(); + let vp = didkit_vc_issue_presentation(presentation, options, key); + assert_eq!(vp, ptr::null()); + let msg = unsafe { CStr::from_ptr(didkit_error_message()) } + .to_str() + .unwrap(); + let code = didkit_error_code(); + assert_ne!(code, 0); + println!("code: {:?} msg: {:?}", code, msg); + } +} diff --git a/lib/src/jni.rs b/lib/src/jni.rs new file mode 100644 index 00000000..1335324f --- /dev/null +++ b/lib/src/jni.rs @@ -0,0 +1,12 @@ +use jni::objects::JClass; +use jni::sys::jstring; +use jni::JNIEnv; + +pub static VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[no_mangle] +pub extern "system" fn Java_com_spruceid_DIDKit_getVersion(env: JNIEnv, _class: JClass) -> jstring { + env.new_string(VERSION) + .expect("Unable to create Java string") + .into_inner() +} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 1efafd47..a2ae3337 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,22 +1,10 @@ -// pub use ssi::vc::Credential as VerifiableCredential; - -use std::os::raw::c_char; - -pub static VERSION: &str = env!("CARGO_PKG_VERSION"); -pub static VERSION_C: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); - -#[no_mangle] -pub extern "C" fn didkit_get_version() -> *const c_char { - VERSION_C.as_ptr() as *const c_char -} - -use jni::objects::JClass; -use jni::sys::jstring; -use jni::JNIEnv; - -#[no_mangle] -pub extern "system" fn Java_com_spruceid_DIDKit_getVersion(env: JNIEnv, _class: JClass) -> jstring { - env.new_string(VERSION) - .expect("Unable to create Java string") - .into_inner() -} +pub mod c; +pub mod error; +pub mod jni; + +pub use ssi::jwk::JWK; +pub use ssi::vc::Credential as VerifiableCredential; +pub use ssi::vc::LinkedDataProofOptions; +pub use ssi::vc::Presentation as VerifiablePresentation; +pub use ssi::vc::ProofPurpose; +pub use ssi::vc::VerificationResult; diff --git a/lib/tests/vc.rs b/lib/tests/vc.rs index f8ebd1f6..c08d785e 100644 --- a/lib/tests/vc.rs +++ b/lib/tests/vc.rs @@ -12,5 +12,5 @@ fn verify_credential() { "id": "did:example:d23dd687a7dc6787646f2eb98d0" } }"###; - let cred = VerifiableCredential::from_json_unsigned(vc_str).unwrap(); + let _cred = VerifiableCredential::from_json_unsigned(vc_str).unwrap(); }