diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 00000000..a342187c --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,100 @@ +name: Build and Test + +on: + push: + branches: + - main + - master + - develop + - dev + - "release/**" + - "hotfix/**" + - "feature/**" + pull_request: + branches: + - main + - master + - develop + - dev + workflow_dispatch: + workflow_call: + secrets: + ENV_JSON_PASSPHRASE: + required: false + +jobs: + build-and-test: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: "17" + cache: "gradle" + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Install Android SDK packages + run: | + yes | sdkmanager --licenses >/dev/null + yes | sdkmanager \ + "platform-tools" \ + "platforms;android-34" \ + "build-tools;34.0.0" + + - name: Decrypt env.json for integration tests + if: hashFiles('env.json.enc') != '' + env: + ENV_JSON_PASSPHRASE: ${{ secrets.ENV_JSON_PASSPHRASE }} + run: | + if [ -z "${ENV_JSON_PASSPHRASE:-}" ]; then + echo "ENV_JSON_PASSPHRASE is not set. Integration tests that need env.json will be skipped." + exit 0 + fi + ./scripts/decrypt-env.sh + + - name: Build JVM and Android modules + run: | + ./gradlew \ + :api:assemble \ + :common:assemble \ + :crypto:assemble \ + :test-common:assemble \ + :sdk:assemble \ + :crypto-android:assembleRelease \ + :android-utils:assembleRelease \ + :sdk-android:assembleRelease \ + --no-daemon \ + --stacktrace + + - name: Run JVM tests + run: | + ./gradlew \ + :api:test \ + :common:test \ + :crypto:test \ + :test-common:test \ + :sdk:test \ + --no-daemon \ + --stacktrace + + - name: Cleanup decrypted env.json + if: always() + run: rm -f env.json + + - name: Upload test reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: jvm-test-reports + path: | + **/build/reports/tests/**/* + **/build/test-results/**/* + if-no-files-found: warn diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 00000000..c636440e --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,212 @@ +name: Publish Release + +on: + push: + tags: + - "v*.*.*" + - "v*.*.*-rc*" + workflow_dispatch: + inputs: + tag: + description: "Tag to release (for example v7.3.2)" + required: true + type: string + +jobs: + verify-version: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} + + - name: Verify SDK version matches tag + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + TAG="${{ inputs.tag }}" + else + TAG="${GITHUB_REF#refs/tags/}" + fi + + TAG_VERSION="${TAG#v}" + SDK_VERSION=$(grep -E "final String SDK_VERSION = '[^']+'" build.gradle | sed -E "s/.*'([^']+)'.*/\1/" | tr -d '[:space:]') + SDK_VERSION_BASE="${SDK_VERSION%-SNAPSHOT}" + + echo "Tag version: ${TAG_VERSION}" + echo "SDK version: ${SDK_VERSION_BASE}" + + if [ -z "${SDK_VERSION_BASE}" ]; then + echo "ERROR: Could not extract SDK_VERSION from build.gradle" + exit 1 + fi + + if [ "${TAG_VERSION}" != "${SDK_VERSION_BASE}" ]; then + echo "ERROR: Tag version (${TAG_VERSION}) doesn't match SDK version (${SDK_VERSION_BASE})" + exit 1 + fi + + build: + name: Build and Test + needs: verify-version + uses: ./.github/workflows/build-and-test.yml + secrets: inherit + + publish: + name: Publish Artifacts + needs: build + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: "temurin" + java-version: "17" + cache: "gradle" + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - name: Install Android SDK packages + run: | + yes | sdkmanager --licenses >/dev/null + yes | sdkmanager \ + "platform-tools" \ + "platforms;android-34" \ + "build-tools;34.0.0" + + - name: Verify Central bundle publish tasks exist + run: | + ./gradlew -q help --task :api:publishMavenJavaPublicationToCentralBundleRepository + ./gradlew -q help --task :common:publishMavenJavaPublicationToCentralBundleRepository + ./gradlew -q help --task :crypto:publishMavenJavaPublicationToCentralBundleRepository + ./gradlew -q help --task :test-common:publishMavenJavaPublicationToCentralBundleRepository + ./gradlew -q help --task :sdk:publishMavenJavaPublicationToCentralBundleRepository + ./gradlew -q help --task :crypto-android:publishMavenJavaPublicationToCentralBundleRepository + ./gradlew -q help --task :android-utils:publishMavenJavaPublicationToCentralBundleRepository + ./gradlew -q help --task :sdk-android:publishMavenJavaPublicationToCentralBundleRepository + + - name: Publish artifacts to local bundle repo + env: + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.CENTRAL_SONATYPE_SIGNING_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.CENTRAL_SONATYPE_SIGNING_PASSWORD }} + run: | + rm -rf build/central-bundle-repo build/central-bundle.zip + ./gradlew \ + :api:publishMavenJavaPublicationToCentralBundleRepository \ + :common:publishMavenJavaPublicationToCentralBundleRepository \ + :crypto:publishMavenJavaPublicationToCentralBundleRepository \ + :test-common:publishMavenJavaPublicationToCentralBundleRepository \ + :sdk:publishMavenJavaPublicationToCentralBundleRepository \ + :crypto-android:publishMavenJavaPublicationToCentralBundleRepository \ + :android-utils:publishMavenJavaPublicationToCentralBundleRepository \ + :sdk-android:publishMavenJavaPublicationToCentralBundleRepository \ + --no-daemon \ + --stacktrace + + - name: Validate bundle repo contains POMs + run: | + test -d build/central-bundle-repo + POM_COUNT=$(find build/central-bundle-repo -type f -name '*.pom' | wc -l | tr -d '[:space:]') + echo "POM count: ${POM_COUNT}" + if [ "${POM_COUNT}" = "0" ]; then + echo "ERROR: No .pom files found in build/central-bundle-repo" + find build/central-bundle-repo -maxdepth 6 -type f | head -n 200 + exit 1 + fi + + - name: Build Central bundle zip + run: | + test -d build/central-bundle-repo + + # Central bundle should not include repository metadata/module metadata. + find build/central-bundle-repo -type f -name 'maven-metadata.xml*' -delete + find build/central-bundle-repo -type f -name '*.module*' -delete + + while IFS= read -r -d '' file; do + md5sum "$file" | awk '{print $1}' > "$file.md5" + sha1sum "$file" | awk '{print $1}' > "$file.sha1" + done < <(find build/central-bundle-repo -type f \ + ! -name '*.asc' \ + ! -name '*.md5' \ + ! -name '*.sha1' \ + -print0) + + (cd build/central-bundle-repo && zip -q -r ../central-bundle.zip .) + ls -la build/central-bundle.zip + + - name: Upload Central bundle (debug) + if: always() + uses: actions/upload-artifact@v4 + with: + name: central-portal-bundle + path: build/central-bundle.zip + + - name: Upload Central bundle repo (debug) + if: always() + uses: actions/upload-artifact@v4 + with: + name: central-portal-bundle-repo + path: build/central-bundle-repo/ + + - name: Upload bundle to Sonatype Central Portal + env: + CENTRAL_USERNAME: ${{ secrets.CENTRAL_SONATYPE_TOKEN_USERNAME }} + CENTRAL_PASSWORD: ${{ secrets.CENTRAL_SONATYPE_TOKEN_PASSWORD }} + run: | + if [ -z "${CENTRAL_USERNAME}" ] || [ -z "${CENTRAL_PASSWORD}" ]; then + echo "ERROR: Missing Central token credentials" + exit 1 + fi + + CENTRAL_BEARER=$(printf "%s:%s" "${CENTRAL_USERNAME}" "${CENTRAL_PASSWORD}" | base64 | tr -d '\n') + DEPLOYMENT_ID=$(curl -sS \ + --fail \ + --header "Authorization: Bearer ${CENTRAL_BEARER}" \ + --form "bundle=@build/central-bundle.zip" \ + "https://central.sonatype.com/api/v1/publisher/upload?publishingType=AUTOMATIC&name=${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}") + + if [ -z "${DEPLOYMENT_ID}" ]; then + echo "ERROR: Central Portal did not return deployment ID" + exit 1 + fi + echo "Central deployment id: ${DEPLOYMENT_ID}" + echo "DEPLOYMENT_ID=${DEPLOYMENT_ID}" >> "$GITHUB_ENV" + + - name: Wait for Central Portal publish + env: + CENTRAL_USERNAME: ${{ secrets.CENTRAL_SONATYPE_TOKEN_USERNAME }} + CENTRAL_PASSWORD: ${{ secrets.CENTRAL_SONATYPE_TOKEN_PASSWORD }} + run: | + CENTRAL_BEARER=$(printf "%s:%s" "${CENTRAL_USERNAME}" "${CENTRAL_PASSWORD}" | base64 | tr -d '\n') + + for i in $(seq 1 60); do + STATUS_JSON=$(curl -sS --fail --request POST \ + --header "Authorization: Bearer ${CENTRAL_BEARER}" \ + "https://central.sonatype.com/api/v1/publisher/status?id=${DEPLOYMENT_ID}") + STATE=$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("deploymentState",""))' <<<"${STATUS_JSON}") + echo "Central state: ${STATE}" + + if [ "${STATE}" = "PUBLISHED" ]; then + exit 0 + fi + if [ "${STATE}" = "FAILED" ]; then + echo "Central deployment FAILED" + echo "${STATUS_JSON}" + exit 1 + fi + + sleep 30 + done + + echo "Timed out waiting for Central Portal publish" + exit 1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 26008f9f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,65 +0,0 @@ -language: android -sudo: required -jdk: oraclejdk8 - -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ - -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - - $HOME/.android/build-cache - -env: - global: - - ANDROID_BUILD_API=28 - - ANDROID_BUILD_TOOLS=28.0.3 - - ANDROID_EMULATOR_API=24 - - ADB_INSTALL_TIMEOUT=5 # minutes - - ANDROID_ABI=default/armeabi-v7a - -android: - components: - - tools # to get the new `repository-11.xml` - - tools # to install Android SDK tools - - platform-tools - - build-tools-$ANDROID_BUILD_TOOLS - - android-$ANDROID_BUILD_API - - android-$ANDROID_EMULATOR_API - - extra-android-m2repository # for design library - - extra-google-m2repository - - extra-google-google_play_services - - addon-google_apis-google-28 # google play services - - sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_BUILD_API - - sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API - - licenses: - - 'android-sdk-preview-license-52d11cd2' - - 'android-sdk-license-.+' - - 'google-gdk-license-.+' - -before_install: - - yes | sdkmanager "platforms;android-28" - - mkdir -p "$ANDROID_HOME/licenses" - - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" - - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd" > "$ANDROID_HOME/licenses/android-sdk-preview-license" - - chmod +x gradlew - - ./gradlew dependencies || true - - openssl aes-256-cbc -K $encrypted_c870b79a80ef_key -iv $encrypted_c870b79a80ef_iv -in env.json.enc -out env.json -d - -before_script: - - android list targets - - android list sdk --extended --no-ui --all - - echo "y" | android update sdk -a --no-ui --filter sys-img-armeabi-v7a-android-$ANDROID_EMULATOR_API,sys-img-x86_64-android-$ANDROID_EMULATOR_API - - echo no | android create avd --force -n test -t android-$ANDROID_EMULATOR_API --abi $ANDROID_ABI - - QEMU_AUDIO_DRV=none emulator -avd test -no-window & - - android-wait-for-emulator - - adb shell input keyevent 82 & - -script: - - "./gradlew :crypto:clean :crypto:test" - - "./gradlew :sdk:clean :sdk:test" - - "./gradlew :crypto-android:clean :crypto-android:connectedCheck -PdisablePreDex --stacktrace" - - "./gradlew :android-utils:clean :android-utils:connectedCheck -PdisablePreDex --stacktrace" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..84ff4924 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,41 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## 7.4.0 - 2026-02-12 + +### Added +- Added GitHub Actions CI workflow `.github/workflows/build-and-test.yml` for JVM and Android build/test coverage. +- Added GitHub Actions release workflow `.github/workflows/publish-release.yml` with tag/version validation and automated publishing. +- Added `scripts/encrypt-env.sh` and `scripts/decrypt-env.sh` for `env.json` management in local/CI environments. +- Added constructor-focused tests for `VirgilCardClient` URL handling in `sdk/src/test/java/com/virgilsecurity/sdk/client/VirgilCardClientConstructorTest.java`. + +### Changed +- Migrated publishing pipeline to Sonatype Central Portal bundle upload flow: + - publish all artifacts to local `build/central-bundle-repo`, + - build and upload `build/central-bundle.zip`, + - poll deployment status until `PUBLISHED`. +- Upgraded build toolchain and dependencies: + - Android Gradle Plugin to `8.5.2`, + - Gradle wrapper to `8.7`, + - Kotlin to `1.9.24`, + - `com.virgilsecurity.crypto` dependency to `0.17.2`. +- Migrated Android modules (`crypto-android`, `android-utils`, `sdk-android`) to modern AGP configuration: + - `namespace`, + - `compileSdk 34`, + - `targetSdk 34`, + - `singleVariant("release")` publishing. +- Updated `settings.gradle` to include Android modules only when Android SDK is configured. +- Updated `sdk` generated source wiring (`generateVirgilInfo`) to use a dedicated directory and explicit task dependencies for Gradle 8 task validation. +- Unified `test-common` published version with `SDK_VERSION` to avoid manual version drift. + +### Fixed +- Fixed `sourcesJar`/`compileJava` task ordering issues under Gradle 8 by declaring explicit dependencies and isolating generated sources. +- Updated `VirgilCardClient` service URL normalization to always resolve to `/card/v5/` on the same host/protocol/port. + +### Documentation +- Updated README build and runtime guidance (JDK/Android SDK requirements, local setup, test commands, encrypted env usage). +- Removed Travis CI in favor of GitHub Actions. + +### Notes +- `common` artifact remains published under `com.virgilsecurity:common` due to explicit group override in `common/build.gradle`. diff --git a/README.md b/README.md index f462187c..bc4eb8b7 100644 --- a/README.md +++ b/README.md @@ -26,15 +26,20 @@ In case you need additional security functionality for multi-device support, gro The Virgil Core SDK is provided as set of packages named *com.virgilsecurity.sdk*. Packages are distributed via Maven repository. In this guide you'll also find one more package - Virgil Crypto Library, that is used by the SDK to perform cryptographic operations. -### Target +### Runtime targets -* Java 7+. -* Android API 16+. +* JVM artifacts target Java 8 bytecode. +* Android artifacts target: + * `sdk-android`: minSdk 21, targetSdk 34. + * `crypto-android`: minSdk 21, targetSdk 34. + * `android-utils`: minSdk 23, targetSdk 34. -### Prerequisites +### Build prerequisites -* Java Development Kit (JDK) 7+ -* Maven 3+ +* JDK 17 (required by Android Gradle Plugin 8.5.x). +* Android SDK Platform 34 (for Android modules/tests). +* Android emulator or device (for instrumentation tests). +* Gradle Wrapper (already included in this repository). ### Installing the package @@ -56,11 +61,11 @@ Use this packages for Java projects: #### Gradle -Add jcenter() repository if missing: +Add Maven Central if missing: ``` repositories { - jcenter() + mavenCentral() } ``` @@ -72,6 +77,62 @@ Use this packages for Android projects: The **\** of the SDK can be found in the [Maven Central Repository](https://mvnrepository.com/artifact/com.virgilsecurity.sdk/sdk) or in the header of current readme. +## Build and Test + +### Local setup + +Create `local.properties` in the project root if Android SDK is not auto-detected: + +``` +sdk.dir=/Users//Library/Android/sdk +``` + +For integration tests that hit Virgil services, keep `env.json` in the project root: + +* `env.json` is ignored by git. +* `env.json.enc` can be stored in git and decrypted in CI. + +Encrypt/decrypt commands: + +``` +ENV_JSON_PASSPHRASE='' ./scripts/encrypt-env.sh +ENV_JSON_PASSPHRASE='' ./scripts/decrypt-env.sh +``` + +### Build all modules + +``` +./gradlew clean build --no-daemon +``` + +### Run JVM tests + +Default environment is `pro`: + +``` +./gradlew :sdk:test --no-daemon --stacktrace +``` + +Use staging environment explicitly: + +``` +./gradlew :sdk:test -Denvironment=stg --no-daemon --stacktrace +``` + +### Run Android unit tests + +``` +./gradlew :crypto-android:testReleaseUnitTest :sdk-android:testReleaseUnitTest --no-daemon +``` + +### Run Android instrumentation tests + +Start an emulator (or connect a device), then run: + +``` +./gradlew :crypto-android:connectedReleaseAndroidTest :sdk-android:connectedReleaseAndroidTest --no-daemon --stacktrace +``` + ## Configure SDK This section contains guides on how to set up Virgil Core SDK modules for authenticating users, managing Virgil Cards and storing private keys. @@ -352,4 +413,3 @@ Our developer support team is here to help you. Find out more information on our You can find us on [Twitter](https://twitter.com/VirgilSecurity) or send us email support@VirgilSecurity.com. Also, get extra help from our support team on [Slack](https://virgilsecurity.com/join-community). - diff --git a/android-utils/build.gradle b/android-utils/build.gradle index fcb9317e..0740bcae 100644 --- a/android-utils/build.gradle +++ b/android-utils/build.gradle @@ -32,27 +32,35 @@ */ apply plugin: 'com.android.library' -apply plugin: 'digital.wup.android-maven-publish' android { - compileSdkVersion 28 + namespace "com.virgilsecurity.sdk.androidutils" + compileSdk 34 defaultConfig { - minSdkVersion 23 - targetSdkVersion 28 + minSdk 23 + targetSdk 34 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } + consumerProguardFiles 'proguard-rules.txt' + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } - packagingOptions { - exclude "META-INF/LICENSE*" + packaging { + resources { + excludes += ["META-INF/LICENSE*"] } + } - consumerProguardFiles 'proguard-rules.txt' + publishing { + singleVariant("release") { + withSourcesJar() + } } } @@ -73,31 +81,12 @@ dependencies { androidTestImplementation "com.android.support.test:runner:$versions.testsRunner" } -task androidJavadocs(type: Javadoc, dependsOn: 'assembleRelease') { - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - android.libraryVariants.all { variant -> - owner.classpath += variant.javaCompileProvider.get().classpath - } - source = android.sourceSets.main.java.srcDirs - exclude '**/R.html', '**/R.*.html', '**/index.html' -} - -task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { - archiveClassifier = 'javadoc' - from androidJavadocs.destinationDir -} - -task androidSourcesJar(type: Jar) { - archiveClassifier = 'sources' - from android.sourceSets.main.java.srcDirs -} - publishing { publications { mavenJava(MavenPublication) { - from components.android - artifact androidJavadocsJar - artifact androidSourcesJar + afterEvaluate { + from components.release + } pom { name = 'Virgil Android Utils' diff --git a/android-utils/src/main/AndroidManifest.xml b/android-utils/src/main/AndroidManifest.xml index aa0d6e60..5c26bcca 100644 --- a/android-utils/src/main/AndroidManifest.xml +++ b/android-utils/src/main/AndroidManifest.xml @@ -30,8 +30,7 @@ ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> - + diff --git a/build.gradle b/build.gradle index a3150fdd..2d4a42d6 100644 --- a/build.gradle +++ b/build.gradle @@ -31,12 +31,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -buildscript { +buildscript { ext.versions = [ // Virgil - virgilCrypto : '0.15.2', + virgilCrypto : '0.17.2', // Common commonsLang : '3.9', @@ -44,14 +44,14 @@ buildscript { gson : '2.8.5', // Gradle - gradle : '3.4.2', + gradle : '8.5.2', // Android appCompat : '28.0.0', androidx : '1.1.1', // Kotlin - kotlinVersion : '1.3.61', + kotlinVersion : '1.9.24', coroutines : '1.3.0-M1', @@ -63,29 +63,24 @@ buildscript { jsonsimple : '1.5.1', mockito : '3.0.0', mockitoJunit : '3.0.0', - mannodermaus : '1.5.0.0', + mannodermaus : '1.7.1.1', mannodermausJunit5: '1.1.0', // Android Tests junitAndroid : '4.12', // Publish - mavenPublishPlugin: '3.6.2', - dokka : '0.10.0' + mavenPublishPlugin: '3.6.2' ] repositories { google() - jcenter() mavenCentral() } dependencies { classpath "com.android.tools.build:gradle:$versions.gradle" - classpath "de.mannodermaus.gradle.plugins:android-junit5:$versions.mannodermaus" - classpath "digital.wup:android-maven-publish:$versions.mavenPublishPlugin" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlinVersion" - classpath "org.jetbrains.dokka:dokka-gradle-plugin:$versions.dokka" } } @@ -98,9 +93,8 @@ allprojects { repositories { mavenCentral() google() - jcenter() maven { - url "https://oss.sonatype.org/service/local/repositories/snapshots/content/" + url "https://central.sonatype.com/repository/maven-snapshots/" } } } @@ -145,20 +139,23 @@ final String BASE_VIRGIL_PACKAGE = 'com.virgilsecurity' final String SDK_PACKAGE = BASE_VIRGIL_PACKAGE + '.sdk' // Packages versions -final String SDK_VERSION = '7.3.2' -final String TEST_COMMON_VERSION = '0.1.3' +final String SDK_VERSION = '7.4.0' + +group SDK_PACKAGE +version SDK_VERSION subprojects { group SDK_PACKAGE version SDK_VERSION - apply plugin: 'org.jetbrains.dokka' apply plugin: 'maven-publish' apply plugin: 'checkstyle' apply from: '../tests-verbal-output.gradle' - def authentication_username = getGradleOrSystemProperty('authentication_username', project) - def authentication_password = getGradleOrSystemProperty('authentication_password', project) + // Central bundle upload uses Maven repository layout; Gradle module metadata is unnecessary. + tasks.withType(org.gradle.api.publish.tasks.GenerateModuleMetadata).configureEach { + enabled = false + } def isAndroid = (it.name == 'crypto-android' || it.name == 'android-utils' @@ -166,24 +163,35 @@ subprojects { if (it.name == 'test-common') { group BASE_VIRGIL_PACKAGE - version TEST_COMMON_VERSION + version SDK_VERSION } if (!isAndroid) { apply plugin: 'java-library' - sourceCompatibility = JavaVersion.VERSION_1_8 - dependencies { testImplementation "org.junit.jupiter:junit-jupiter:$versions.junit" } test { useJUnitPlatform() + if (System.getProperty('environment') != null) { + systemProperty 'environment', System.getProperty('environment') + } testLogging { events "passed", "skipped", "failed" } } + + tasks.withType(JavaCompile).configureEach { + options.release.set(8) + } + } + + tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { + kotlinOptions { + jvmTarget = "1.8" + } } // Don't publish examples subproject @@ -191,19 +199,14 @@ subprojects { apply plugin: 'signing' - dokka { - outputFormat = 'html' - outputDirectory = "$buildDir/javadoc" - } - if (!isAndroid) { task sourcesJar(type: Jar) { from sourceSets.main.allSource archiveClassifier = 'sources' } - task javadocJar(type: Jar, dependsOn: 'dokka') { - from "$buildDir/javadoc" + task javadocJar(type: Jar, dependsOn: 'javadoc') { + from javadoc.destinationDir archiveClassifier = 'javadoc' } } @@ -227,16 +230,10 @@ subprojects { } developers { developer { - id = 'BuddahLD' - name = 'Danylo Oliinyk' - email = 'doliinyk@virgilsecurity.com' - organizationUrl = 'https://github.com/BuddahLD' - } - developer { - id = 'andrii-iakovenko' - name = 'Andrii Iakovenko' - email = 'andrii-iakovenko@gmail.com' - organizationUrl = 'https://github.com/andrii-iakovenko' + id = 'virgil-security-inc' + name = 'Virgil Security, Inc.' + email = 'support@virgilsecurity.com' + organizationUrl = 'https://www.virgilsecurity.com/' } } scm { @@ -247,21 +244,23 @@ subprojects { } } } - repositories { maven { - def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" - def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots" - url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl - credentials { - username "${authentication_username}" - password "${authentication_password}" - } + name = 'centralBundle' + url = uri("${rootProject.buildDir}/central-bundle-repo") } } + } signing { + def signingKey = getGradleOrSystemProperty('signingKey', project) + def signingPassword = getGradleOrSystemProperty('signingPassword', project) + + if (signingKey != null && signingPassword != null) { + useInMemoryPgpKeys(signingKey, signingPassword) + } + sign publishing.publications.mavenJava } } diff --git a/crypto-android/build.gradle b/crypto-android/build.gradle index be59c374..3b129613 100644 --- a/crypto-android/build.gradle +++ b/crypto-android/build.gradle @@ -32,25 +32,33 @@ */ apply plugin: 'com.android.library' -apply plugin: 'digital.wup.android-maven-publish' android { - compileSdkVersion 28 + namespace "com.virgilsecurity.sdk.crypto" + compileSdk 34 defaultConfig { - minSdkVersion 21 - targetSdkVersion 28 + minSdk 21 + targetSdk 34 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + packaging { + resources { + excludes += ["META-INF/LICENSE*"] } + } - packagingOptions { - exclude "META-INF/LICENSE*" + publishing { + singleVariant("release") { + withSourcesJar() } } } @@ -71,28 +79,12 @@ dependencies { androidTestImplementation "org.apache.commons:commons-lang3:$versions.commonsLang" } -task androidJavadocs(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - exclude '**/R.html', '**/R.*.html', '**/index.html' -} - -task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { - archiveClassifier = 'javadoc' - from androidJavadocs.destinationDir -} - -task androidSourcesJar(type: Jar) { - archiveClassifier = 'sources' - from android.sourceSets.main.java.srcDirs -} - publishing { publications { mavenJava(MavenPublication) { - from components.android - artifact androidJavadocsJar - artifact androidSourcesJar + afterEvaluate { + from components.release + } pom { name = 'Virgil Crypto for Android' diff --git a/crypto-android/src/main/AndroidManifest.xml b/crypto-android/src/main/AndroidManifest.xml index 0af5c1c2..b7f5faf8 100644 --- a/crypto-android/src/main/AndroidManifest.xml +++ b/crypto-android/src/main/AndroidManifest.xml @@ -31,5 +31,4 @@ ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> - + diff --git a/env.json.enc b/env.json.enc index c6b8da5a..1fb3d9a6 100644 Binary files a/env.json.enc and b/env.json.enc differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 97f2726e..22032f18 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -36,4 +36,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip diff --git a/scripts/decrypt-env.sh b/scripts/decrypt-env.sh new file mode 100755 index 00000000..00530e36 --- /dev/null +++ b/scripts/decrypt-env.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +INPUT_FILE="${1:-$ROOT_DIR/env.json.enc}" +OUTPUT_FILE="${2:-$ROOT_DIR/env.json}" +PASSPHRASE="${ENV_JSON_PASSPHRASE:-}" + +if [[ -z "${PASSPHRASE}" ]]; then + echo "ENV_JSON_PASSPHRASE is required." + echo "Usage: ENV_JSON_PASSPHRASE=... $0 [input_enc] [output_json]" + exit 1 +fi + +if [[ ! -f "${INPUT_FILE}" ]]; then + echo "Input file not found: ${INPUT_FILE}" + exit 1 +fi + +openssl enc -d -aes-256-cbc -pbkdf2 -a \ + -in "${INPUT_FILE}" \ + -out "${OUTPUT_FILE}" \ + -pass "pass:${PASSPHRASE}" + +echo "Decrypted ${INPUT_FILE} -> ${OUTPUT_FILE}" diff --git a/scripts/encrypt-env.sh b/scripts/encrypt-env.sh new file mode 100755 index 00000000..2d6661af --- /dev/null +++ b/scripts/encrypt-env.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +INPUT_FILE="${1:-$ROOT_DIR/env.json}" +OUTPUT_FILE="${2:-$ROOT_DIR/env.json.enc}" +PASSPHRASE="${ENV_JSON_PASSPHRASE:-}" + +if [[ -z "${PASSPHRASE}" ]]; then + echo "ENV_JSON_PASSPHRASE is required." + echo "Usage: ENV_JSON_PASSPHRASE=... $0 [input_json] [output_enc]" + exit 1 +fi + +if [[ ! -f "${INPUT_FILE}" ]]; then + echo "Input file not found: ${INPUT_FILE}" + exit 1 +fi + +openssl enc -aes-256-cbc -pbkdf2 -salt -a \ + -in "${INPUT_FILE}" \ + -out "${OUTPUT_FILE}" \ + -pass "pass:${PASSPHRASE}" + +echo "Encrypted ${INPUT_FILE} -> ${OUTPUT_FILE}" diff --git a/sdk-android/build.gradle b/sdk-android/build.gradle index da52a918..37b10dc3 100644 --- a/sdk-android/build.gradle +++ b/sdk-android/build.gradle @@ -32,25 +32,33 @@ */ apply plugin: 'com.android.library' -apply plugin: 'digital.wup.android-maven-publish' android { - compileSdkVersion 28 + namespace "com.virgilsecurity.sdk.sdk" + compileSdk 34 defaultConfig { - minSdkVersion 21 - targetSdkVersion 28 + minSdk 21 + targetSdk 34 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + packaging { + resources { + excludes += ["META-INF/LICENSE*"] } + } - packagingOptions { - exclude "META-INF/LICENSE*" + publishing { + singleVariant("release") { + withSourcesJar() } } } @@ -72,28 +80,12 @@ dependencies { androidTestImplementation "org.apache.commons:commons-lang3:$versions.commonsLang" } -task androidJavadocs(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - exclude '**/R.html', '**/R.*.html', '**/index.html' -} - -task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { - archiveClassifier = 'javadoc' - from androidJavadocs.destinationDir -} - -task androidSourcesJar(type: Jar) { - archiveClassifier = 'sources' - from android.sourceSets.main.java.srcDirs -} - publishing { publications { mavenJava(MavenPublication) { - from components.android - artifact androidJavadocsJar - artifact androidSourcesJar + afterEvaluate { + from components.release + } pom { name = 'Virgil SDK for Android' diff --git a/sdk-android/src/main/AndroidManifest.xml b/sdk-android/src/main/AndroidManifest.xml index 64d26233..b7f5faf8 100644 --- a/sdk-android/src/main/AndroidManifest.xml +++ b/sdk-android/src/main/AndroidManifest.xml @@ -31,4 +31,4 @@ ~ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ~ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> - + diff --git a/sdk/build.gradle b/sdk/build.gradle index 9fcdfbfe..2035798a 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -57,19 +57,24 @@ dependencies { testImplementation "com.virgilsecurity.crypto:foundation:$versions.virgilCrypto" } -sourceSets.main.java.srcDirs = ["${buildDir}/generated/", "src/main/java"] +def virgilGeneratedSourcesDir = "${buildDir}/generated/virgil-info" + +sourceSets.main.java.srcDirs = [virgilGeneratedSourcesDir, "src/main/java"] task generateVirgilInfo(type: Copy) { from('src/main/templates') { include '**/*.java' } - into "$buildDir/generated" + into virgilGeneratedSourcesDir expand(version: "$project.version") includeEmptyDirs = false } compileKotlin.dependsOn(generateVirgilInfo) compileJava.dependsOn(generateVirgilInfo) +tasks.matching { it.name == "sourcesJar" }.configureEach { + dependsOn(generateVirgilInfo) +} publishing { publications { diff --git a/sdk/src/main/java/com/virgilsecurity/sdk/client/VirgilCardClient.java b/sdk/src/main/java/com/virgilsecurity/sdk/client/VirgilCardClient.java index 30281685..86ff5c37 100644 --- a/sdk/src/main/java/com/virgilsecurity/sdk/client/VirgilCardClient.java +++ b/sdk/src/main/java/com/virgilsecurity/sdk/client/VirgilCardClient.java @@ -61,8 +61,8 @@ public class VirgilCardClient implements CardClient { private static final Logger LOGGER = Logger.getLogger(VirgilCardClient.class.getName()); - private static final String BASE_URL = "https://api.virgilsecurity.com/card"; - private static final String SERVICE_VERSION = "/v5/"; + private static final String BASE_SERVICE_URL = "https://api.virgilsecurity.com"; + private static final String CARDS_V5_PATH = "/card/v5/"; private URL serviceUrl; private HttpClient httpClient; @@ -71,7 +71,7 @@ public class VirgilCardClient implements CardClient { * Create a new instance of {@code CardClient} with default HttpClient. */ public VirgilCardClient() { - this(BASE_URL + SERVICE_VERSION); + this(BASE_SERVICE_URL); } /** @@ -80,7 +80,7 @@ public VirgilCardClient() { * @param httpClient http client that will be used for firing requests */ public VirgilCardClient(HttpClient httpClient) { - this(BASE_URL + SERVICE_VERSION, httpClient); + this(BASE_SERVICE_URL, httpClient); } /** @@ -90,11 +90,12 @@ public VirgilCardClient(HttpClient httpClient) { */ public VirgilCardClient(String serviceUrl) { try { - this.serviceUrl = new URL(serviceUrl); + this.serviceUrl = resolveServiceUrl(new URL(serviceUrl)); + httpClient = new HttpClient(); } catch (MalformedURLException e) { - throw new IllegalArgumentException("CardClient -> 'serviceUrl' has wrong format"); + LOGGER.log(Level.SEVERE, "Some issue occurred during service URL creating", e); + throw new IllegalArgumentException("Incorrect service URL", e); } - httpClient = new HttpClient(); } /** @@ -105,9 +106,10 @@ public VirgilCardClient(String serviceUrl) { */ public VirgilCardClient(String serviceUrl, HttpClient httpClient) { try { - this.serviceUrl = new URL(serviceUrl); + this.serviceUrl = resolveServiceUrl(new URL(serviceUrl)); } catch (MalformedURLException e) { - throw new IllegalArgumentException("CardClient -> 'serviceUrl' has wrong format"); + LOGGER.log(Level.SEVERE, "Some issue occurred during service URL creating", e); + throw new IllegalArgumentException("Incorrect service URL", e); } this.httpClient = httpClient; } @@ -118,7 +120,12 @@ public VirgilCardClient(String serviceUrl, HttpClient httpClient) { * @param serviceUrl the service url to fire requests to */ public VirgilCardClient(URL serviceUrl) { - this.serviceUrl = serviceUrl; + try { + this.serviceUrl = resolveServiceUrl(serviceUrl); + } catch (MalformedURLException e) { + LOGGER.log(Level.SEVERE, "Some issue occurred during service URL creating", e); + throw new IllegalArgumentException("Incorrect service URL", e); + } httpClient = new HttpClient(); } @@ -129,10 +136,19 @@ public VirgilCardClient(URL serviceUrl) { * @param httpClient http client that will be used for firing requests */ public VirgilCardClient(URL serviceUrl, HttpClient httpClient) { - this.serviceUrl = serviceUrl; + try { + this.serviceUrl = resolveServiceUrl(serviceUrl); + } catch (MalformedURLException e) { + LOGGER.log(Level.SEVERE, "Some issue occurred during service URL creating", e); + throw new IllegalArgumentException("Incorrect service URL", e); + } this.httpClient = httpClient; } + private static URL resolveServiceUrl(URL serviceUrl) throws MalformedURLException { + return new URL(serviceUrl.getProtocol(), serviceUrl.getHost(), serviceUrl.getPort(), CARDS_V5_PATH); + } + /** * Get card from Virgil Services by specified identifier. * diff --git a/sdk/src/main/java/com/virgilsecurity/sdk/utils/ConvertionUtils.java b/sdk/src/main/java/com/virgilsecurity/sdk/utils/ConvertionUtils.java index 5173cf87..1454194e 100644 --- a/sdk/src/main/java/com/virgilsecurity/sdk/utils/ConvertionUtils.java +++ b/sdk/src/main/java/com/virgilsecurity/sdk/utils/ConvertionUtils.java @@ -181,7 +181,8 @@ public static byte[] concatenate(byte[] first, byte[] second) { * * @param serializedObject the string presentation of object serialized to JSON * @param objectType the type of object - * @return the {@code objectType} object deserialized from {@code serializedObject} + * @return the {@code objectType} object deserialized from + * {@code serializedObject} */ public static T deserializeFromJson(String serializedObject, Class objectType) { return getGson().fromJson(serializedObject, objectType); @@ -200,7 +201,8 @@ public static Map deserializeMapFromJson(String serializedObject } /** - * Get {@link Gson} which supports Virgil rules for date and byte array conversions. + * Get {@link Gson} which supports Virgil rules for date and byte array + * conversions. * * @return the Gson instance. */ @@ -243,9 +245,11 @@ public static byte[] hexToBytes(String value) { } /** - * Deserialize object of objectType type from binary presentation of JSON String. + * Deserialize object of objectType type from binary presentation + * of JSON String. * - * @param snapshot the object snapshot which is a binary presentation of JSON string + * @param snapshot the object snapshot which is a binary presentation of JSON + * string * @param objectType the type of object * @return the {@code objectType} object deserialized from {@code snapshot} */ @@ -274,6 +278,17 @@ public static byte[] toBase64Bytes(String value) { return toBytes(str); } + /** + * Convert byte array to Base64 byte array. + * + * @param value the byte array to be converted. + * @return the byte array. + */ + public static byte[] toBase64Bytes(byte[] value) { + String str = toBase64String(value); + return toBytes(str); + } + /** * Encode byte array as Base64 string. * @@ -340,7 +355,8 @@ public static String toString(byte[] bytes) { } /** - * Decodes the current bytes to a string according to the specified character encoding. + * Decodes the current bytes to a string according to the specified character + * encoding. * * @param inputBytes bytes to decode * @param encoding The character encoding to decode to. @@ -369,7 +385,8 @@ public static String toString(byte[] inputBytes, StringEncoding encoding) { } /** - * Get the contents of an InputStream as a String using UTF-8 character encoding. + * Get the contents of an InputStream as a String using UTF-8 + * character encoding. * * @param is the input stream. * @return the input stream data as string. diff --git a/sdk/src/test/java/com/virgilsecurity/sdk/cards/CardsManagerTest.java b/sdk/src/test/java/com/virgilsecurity/sdk/cards/CardsManagerTest.java index 4ba5c68a..a056339f 100644 --- a/sdk/src/test/java/com/virgilsecurity/sdk/cards/CardsManagerTest.java +++ b/sdk/src/test/java/com/virgilsecurity/sdk/cards/CardsManagerTest.java @@ -60,7 +60,6 @@ import com.virgilsecurity.sdk.utils.StringUtils; import com.virgilsecurity.sdk.utils.TestUtils; import com.virgilsecurity.sdk.utils.Tuple; -import com.virgilsecurity.testcommon.utils.PropertyUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -90,7 +89,6 @@ public class CardsManagerTest extends PropertyManager { private static final String SIGNER_TYPE_EXTRA = "bestsignerever"; - private static final String CARDS_SERVICE_PUBLIC_KEY = "bestsignerever"; private Mocker mocker; private VirgilCrypto crypto; @@ -105,16 +103,13 @@ public void setUp() { mocker = new Mocker(); crypto = new VirgilCrypto(); cardCrypto = new VirgilCardCrypto(); - String url = getCardsServiceUrl(); + String url = getServiceBaseUrl(); if (StringUtils.isBlank(url)) { cardClient = new VirgilCardClient(); } else { cardClient = new VirgilCardClient(url); } cardVerifier = new VirgilCardVerifier(cardCrypto); - if (!StringUtils.isBlank(PropertyUtils.getSystemProperty(CARDS_SERVICE_PUBLIC_KEY))) { - cardVerifier.setServiceKey(PropertyUtils.getSystemProperty(CARDS_SERVICE_PUBLIC_KEY)); - } dataProvider = new CompatibilityDataProvider(); } @@ -250,7 +245,8 @@ public void stc_19() throws CryptoException, VirgilServiceException, Interrupted // Call publishCard(privateKey2, publicKey2, Card1.identifier) Card publishedCard2 = cardManager.publishCard(cardModel2); - // Card2 is successfully created and matches card that was generated on the client side + // Card2 is successfully created and matches card that was generated on the + // client side assertNotNull(publishedCard2); assertFalse(publishedCard2.isOutdated()); assertCardsEquals(generatedCard2, publishedCard2); @@ -258,7 +254,8 @@ public void stc_19() throws CryptoException, VirgilServiceException, Interrupted // Get Card2 using getCard Card cardFromService2 = cardManager.getCard(generatedCard2.getIdentifier()); - // Card2 is successfully retrieved and matches card that was generated on the client side + // Card2 is successfully retrieved and matches card that was generated on the + // client side // (including // previousCardId and isOutdated=false). assertNotNull(cardFromService2); @@ -268,7 +265,8 @@ public void stc_19() throws CryptoException, VirgilServiceException, Interrupted // Get Card1 using getCard Card outdatedCard = cardManager.getCard(generatedCard1.getIdentifier()); - // Card1 is successfully retrieved and matches card that was generated on the client side. + // Card1 is successfully retrieved and matches card that was generated on the + // client side. // (isOutdated=true) assertNotNull(outdatedCard); assertTrue(outdatedCard.isOutdated()); @@ -402,8 +400,8 @@ public void stc_35_1() throws VirgilServiceException, CryptoException { RawCardContent cardContent = new RawCardContent(testIdentity, dataProvider.getJsonByKey(34, "public_key_base64"), new Date()); - RawSignedModel rawSignedModelTwo = - new RawSignedModel(ConvertionUtils.base64ToBytes(cardContent.exportAsBase64String())); + RawSignedModel rawSignedModelTwo = new RawSignedModel( + ConvertionUtils.base64ToBytes(cardContent.exportAsBase64String())); ModelSigner signer = new ModelSigner(cardCrypto); VirgilPrivateKey privateKey = crypto.importPrivateKey( diff --git a/sdk/src/test/java/com/virgilsecurity/sdk/cards/SignerAndVerifierTest.java b/sdk/src/test/java/com/virgilsecurity/sdk/cards/SignerAndVerifierTest.java index b4000e2e..50faa35e 100644 --- a/sdk/src/test/java/com/virgilsecurity/sdk/cards/SignerAndVerifierTest.java +++ b/sdk/src/test/java/com/virgilsecurity/sdk/cards/SignerAndVerifierTest.java @@ -83,586 +83,586 @@ public class SignerAndVerifierTest extends PropertyManager { - private static final String TEST_SIGNER_TYPE = "test_custom_type"; - - private static final String TEST_KEY_ONE = "TEST_KEY_ONE"; - private static final String TEST_VALUE_ONE = "TEST_VALUE_ONE"; - private static final String TEST_KEY_TWO = "TEST_KEY_TWO"; - private static final String TEST_VALUE_TWO = "TEST_VALUE_TWO"; - - private VirgilCrypto virgilCrypto; - private VirgilCardCrypto cardCrypto; - private ModelSigner modelSigner; - private Mocker mocker; - private CompatibilityDataProvider dataProvider; - private CtrDrbg random; - - @BeforeEach - public void setUp() { - virgilCrypto = new VirgilCrypto(); - this.random = new CtrDrbg(); - this.random.setupDefaults(); - cardCrypto = new VirgilCardCrypto(); - modelSigner = new ModelSigner(cardCrypto); - mocker = new Mocker(); - dataProvider = new CompatibilityDataProvider(); - } - - @Test - public void stc_10_emptyVerifier_should_verifyCard() throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, - new ArrayList()); - - assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); - } - - @SuppressWarnings("unused") - @Test - public void stc_10_verifier_should_verifyCard_ifCardHasAtLeastOneSignatureFromWhiteList() - throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - final Card card = Card.parse(cardCrypto, rawSignedModel); - - VirgilPrivateKey privateKey1 = virgilCrypto - .importPrivateKey( - ConvertionUtils.base64ToBytes(dataProvider.getJsonByKey(10, "private_key1_base64"))) - .getPrivateKey(); - VirgilPublicKey publicKey1 = virgilCrypto.extractPublicKey(privateKey1); - - VirgilKeyPair keyPair2 = this.virgilCrypto.generateKeyPair(); - VirgilPrivateKey privateKey2 = keyPair2.getPrivateKey(); - VirgilPublicKey publicKey2 = keyPair2.getPublicKey(); - - VirgilKeyPair keyPair3 = this.virgilCrypto.generateKeyPair(); - VirgilPrivateKey privateKey3 = keyPair3.getPrivateKey(); - VirgilPublicKey publicKey3 = keyPair3.getPublicKey(); - - List verifierCredentialsList1 = new ArrayList<>(); - verifierCredentialsList1 - .add(new VerifierCredentials("extra", virgilCrypto.exportPublicKey(publicKey1))); - verifierCredentialsList1 - .add(new VerifierCredentials("extra2", virgilCrypto.exportPublicKey(publicKey2))); - Whitelist whitelist1 = new Whitelist(verifierCredentialsList1); - - List verifierCredentialsList2 = new ArrayList<>(); - verifierCredentialsList2 - .add(new VerifierCredentials("extra3", virgilCrypto.exportPublicKey(publicKey3))); - Whitelist whitelist2 = new Whitelist(verifierCredentialsList2); - - List whitelists = new ArrayList<>(); - whitelists.add(whitelist1); - whitelists.add(whitelist2); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, - whitelists); - - assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); - } - - @Test - public void stc_10_verifier_shouldNot_verifyCard_ifCardDoesntHaveSignatureOfAtLeastOneWhiteList() - throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - final Card card = Card.parse(cardCrypto, rawSignedModel); - - VirgilPrivateKey privateKey1 = virgilCrypto - .importPrivateKey( - ConvertionUtils.base64ToBytes(dataProvider.getJsonByKey(10, "private_key1_base64"))) - .getPrivateKey(); - VirgilPublicKey publicKey1 = virgilCrypto.extractPublicKey(privateKey1); - VirgilPublicKey publicKey2 = this.virgilCrypto.generateKeyPair().getPublicKey(); - - List verifierCredentialsList1 = new ArrayList<>(); - verifierCredentialsList1.add( - new VerifierCredentials("extra1", TestUtils.exportPublicKey(publicKey1.getPublicKey()))); - verifierCredentialsList1.add( - new VerifierCredentials("extra2", TestUtils.exportPublicKey(publicKey2.getPublicKey()))); - Whitelist whitelist1 = new Whitelist(verifierCredentialsList1); - List whitelists = new ArrayList<>(); - whitelists.add(whitelist1); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, - whitelists); - - assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); - } - - @Test - public void stc_10_verifier_shouldNot_verifyCard_ifMissedRequiredSelfSignature() - throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - for (RawSignature signature : rawSignedModel.getSignatures()) { - if (SignerType.SELF.getRawValue().equals(signature.getSigner())) { - rawSignedModel.getSignatures().remove(signature); - break; - } + private static final String TEST_SIGNER_TYPE = "test_custom_type"; + + private static final String TEST_KEY_ONE = "TEST_KEY_ONE"; + private static final String TEST_VALUE_ONE = "TEST_VALUE_ONE"; + private static final String TEST_KEY_TWO = "TEST_KEY_TWO"; + private static final String TEST_VALUE_TWO = "TEST_VALUE_TWO"; + + private VirgilCrypto virgilCrypto; + private VirgilCardCrypto cardCrypto; + private ModelSigner modelSigner; + private Mocker mocker; + private CompatibilityDataProvider dataProvider; + private CtrDrbg random; + + @BeforeEach + public void setUp() { + virgilCrypto = new VirgilCrypto(); + this.random = new CtrDrbg(); + this.random.setupDefaults(); + cardCrypto = new VirgilCardCrypto(); + modelSigner = new ModelSigner(cardCrypto); + mocker = new Mocker(); + dataProvider = new CompatibilityDataProvider(); } - Card card = Card.parse(cardCrypto, rawSignedModel); - - List whitelists = new ArrayList<>(); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, false, - whitelists); - - assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); - } - - @Test - public void stc_10_verifier_shouldNot_verifyCard_ifVerifierHasEmptyWhiteList() - throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - - List whitelists = new ArrayList<>(); - whitelists.add(new Whitelist(new ArrayList())); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, - whitelists); - - assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); - } - - @Test - public void stc_10_verifier_shouldNot_verifyCard_ifWrongSelfSignature() throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - for (RawSignature signature : rawSignedModel.getSignatures()) { - if (SignerType.SELF.getRawValue().equals(signature.getSigner())) { - String sign = ConvertionUtils - .toBase64String(this.virgilCrypto.generateSignature(rawSignedModel.getContentSnapshot(), - this.virgilCrypto.generateKeyPair().getPrivateKey())); - signature.setSignature(sign); - break; - } + + @Test + public void stc_10_emptyVerifier_should_verifyCard() throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, + new ArrayList()); + + assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); + } + + @SuppressWarnings("unused") + @Test + public void stc_10_verifier_should_verifyCard_ifCardHasAtLeastOneSignatureFromWhiteList() + throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + final Card card = Card.parse(cardCrypto, rawSignedModel); + + VirgilPrivateKey privateKey1 = virgilCrypto + .importPrivateKey( + ConvertionUtils.base64ToBytes(dataProvider.getJsonByKey(10, "private_key1_base64"))) + .getPrivateKey(); + VirgilPublicKey publicKey1 = virgilCrypto.extractPublicKey(privateKey1); + + VirgilKeyPair keyPair2 = this.virgilCrypto.generateKeyPair(); + VirgilPrivateKey privateKey2 = keyPair2.getPrivateKey(); + VirgilPublicKey publicKey2 = keyPair2.getPublicKey(); + + VirgilKeyPair keyPair3 = this.virgilCrypto.generateKeyPair(); + VirgilPrivateKey privateKey3 = keyPair3.getPrivateKey(); + VirgilPublicKey publicKey3 = keyPair3.getPublicKey(); + + List verifierCredentialsList1 = new ArrayList<>(); + verifierCredentialsList1 + .add(new VerifierCredentials("extra", virgilCrypto.exportPublicKey(publicKey1))); + verifierCredentialsList1 + .add(new VerifierCredentials("extra2", virgilCrypto.exportPublicKey(publicKey2))); + Whitelist whitelist1 = new Whitelist(verifierCredentialsList1); + + List verifierCredentialsList2 = new ArrayList<>(); + verifierCredentialsList2 + .add(new VerifierCredentials("extra3", virgilCrypto.exportPublicKey(publicKey3))); + Whitelist whitelist2 = new Whitelist(verifierCredentialsList2); + + List whitelists = new ArrayList<>(); + whitelists.add(whitelist1); + whitelists.add(whitelist2); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, + whitelists); + + assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); + } + + @Test + public void stc_10_verifier_shouldNot_verifyCard_ifCardDoesntHaveSignatureOfAtLeastOneWhiteList() + throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + final Card card = Card.parse(cardCrypto, rawSignedModel); + + VirgilPrivateKey privateKey1 = virgilCrypto + .importPrivateKey( + ConvertionUtils.base64ToBytes(dataProvider.getJsonByKey(10, "private_key1_base64"))) + .getPrivateKey(); + VirgilPublicKey publicKey1 = virgilCrypto.extractPublicKey(privateKey1); + VirgilPublicKey publicKey2 = this.virgilCrypto.generateKeyPair().getPublicKey(); + + List verifierCredentialsList1 = new ArrayList<>(); + verifierCredentialsList1.add( + new VerifierCredentials("extra1", TestUtils.exportPublicKey(publicKey1.getPublicKey()))); + verifierCredentialsList1.add( + new VerifierCredentials("extra2", TestUtils.exportPublicKey(publicKey2.getPublicKey()))); + Whitelist whitelist1 = new Whitelist(verifierCredentialsList1); + List whitelists = new ArrayList<>(); + whitelists.add(whitelist1); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, + whitelists); + + assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); + } + + @Test + public void stc_10_verifier_shouldNot_verifyCard_ifMissedRequiredSelfSignature() + throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + for (RawSignature signature : rawSignedModel.getSignatures()) { + if (SignerType.SELF.getRawValue().equals(signature.getSigner())) { + rawSignedModel.getSignatures().remove(signature); + break; + } + } + Card card = Card.parse(cardCrypto, rawSignedModel); + + List whitelists = new ArrayList<>(); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, false, + whitelists); + + assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); + } + + @Test + public void stc_10_verifier_shouldNot_verifyCard_ifVerifierHasEmptyWhiteList() + throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + + List whitelists = new ArrayList<>(); + whitelists.add(new Whitelist(new ArrayList())); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, + whitelists); + + assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); + } + + @Test + public void stc_10_verifier_shouldNot_verifyCard_ifWrongSelfSignature() throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + for (RawSignature signature : rawSignedModel.getSignatures()) { + if (SignerType.SELF.getRawValue().equals(signature.getSigner())) { + String sign = ConvertionUtils + .toBase64String(this.virgilCrypto.generateSignature(rawSignedModel.getContentSnapshot(), + this.virgilCrypto.generateKeyPair().getPrivateKey())); + signature.setSignature(sign); + break; + } + } + Card card = Card.parse(cardCrypto, rawSignedModel); + + List whitelists = new ArrayList<>(); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, false, + whitelists); + + assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); + } + + @Test + public void stc_10_verifier_shouldNot_verifyCard_ifWrongVirgilSignature() throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + for (RawSignature signature : rawSignedModel.getSignatures()) { + if (SignerType.VIRGIL.getRawValue().equals(signature.getSigner())) { + String sign = ConvertionUtils + .toBase64String(this.virgilCrypto.generateSignature(rawSignedModel.getContentSnapshot(), + this.virgilCrypto.generateKeyPair().getPrivateKey())); + signature.setSignature(sign); + break; + } + } + Card card = Card.parse(cardCrypto, rawSignedModel); + + List whitelists = new ArrayList<>(); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, true, + whitelists); + + assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); + } + + @Test + public void stc_10_verifySelfAndVirgilSignWithEmptyWhiteList() throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, true, + new ArrayList()); + + assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); + } + + @Test + public void stc_10_verifySelfSignWithEmptyWhiteList() throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, false, + new ArrayList()); + + assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); + } + + @Test + public void stc_10_verifyVirgilSignWithEmptyWhiteList() throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, true, + new ArrayList()); + + assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); + } + + @Test + public void stc_10_whiteList1Key() throws CryptoException { + // STC-10 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(10, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + + VirgilPrivateKey privateKey1 = virgilCrypto + .importPrivateKey( + ConvertionUtils.base64ToBytes(dataProvider.getJsonByKey(10, "private_key1_base64"))) + .getPrivateKey(); + VirgilPublicKey publicKey1 = virgilCrypto.extractPublicKey(privateKey1); + + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, + new ArrayList()); + + List verifierCredentialsList = new ArrayList<>(); + verifierCredentialsList + .add(new VerifierCredentials("extra", virgilCrypto.exportPublicKey(publicKey1))); + Whitelist whitelist1 = new Whitelist(verifierCredentialsList); + virgilCardVerifier.addWhiteList(whitelist1); + + assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); + } + + @Test + public void stc_11() throws CryptoException { + // STC-11 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(11, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, + Collections.emptyList()); + assertTrue(virgilCardVerifier.verifyCard(card)); + + virgilCardVerifier.setVerifySelfSignature(true); + assertFalse(virgilCardVerifier.verifyCard(card)); + } + + @Test + public void stc_12() throws CryptoException { + // STC-12 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(12, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false); + assertTrue(virgilCardVerifier.verifyCard(card)); + + virgilCardVerifier.setVerifyVirgilSignature(true); + assertFalse(virgilCardVerifier.verifyCard(card)); + } + + @Test + public void stc_14() throws CryptoException { + // STC-14 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(14, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, true); + assertFalse(virgilCardVerifier.verifyCard(card)); } - Card card = Card.parse(cardCrypto, rawSignedModel); - - List whitelists = new ArrayList<>(); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, false, - whitelists); - - assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); - } - - @Test - public void stc_10_verifier_shouldNot_verifyCard_ifWrongVirgilSignature() throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - for (RawSignature signature : rawSignedModel.getSignatures()) { - if (SignerType.VIRGIL.getRawValue().equals(signature.getSigner())) { - String sign = ConvertionUtils - .toBase64String(this.virgilCrypto.generateSignature(rawSignedModel.getContentSnapshot(), - this.virgilCrypto.generateKeyPair().getPrivateKey())); - signature.setSignature(sign); - break; - } + + @Test + public void stc_15() throws CryptoException { + // STC-15 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(15, STRING)); + Card card = Card.parse(cardCrypto, rawSignedModel); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, false); + assertFalse(virgilCardVerifier.verifyCard(card)); + } + + @Test + public void stc_16() throws CryptoException { + // STC-16 + RawSignedModel rawSignedModel = RawSignedModel + .fromString(dataProvider.getTestDataAs(16, STRING)); + final VirgilPublicKey publicKey = (VirgilPublicKey) cardCrypto.importPublicKey( + ConvertionUtils.base64ToBytes(dataProvider.getJsonByKey(16, "public_key1_base64"))); + + VirgilPublicKey publicKeyTwo = mocker.generatePublicKey(); + List verifierCredentialsList = new ArrayList<>(); + verifierCredentialsList.add(new VerifierCredentials(TEST_SIGNER_TYPE, + TestUtils.exportPublicKey(publicKeyTwo.getPublicKey()))); + Whitelist whitelistOne = new Whitelist(verifierCredentialsList); + List whitelists = new ArrayList<>(); + whitelists.add(whitelistOne); + + Card card = Card.parse(cardCrypto, rawSignedModel); + VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, + whitelists); + assertFalse(virgilCardVerifier.verifyCard(card)); + + verifierCredentialsList + .add(new VerifierCredentials("extra", virgilCrypto.exportPublicKey(publicKey))); + + assertTrue(virgilCardVerifier.verifyCard(card)); + } + + @Test + public void stc_26() throws CryptoException, VirgilServiceException, InterruptedException { + // STC-26 + AccessTokenProvider accessTokenProvider = Mockito.mock(AccessTokenProvider.class); + final CardVerifier cardVerifier = new VirgilCardVerifier(this.cardCrypto, false, false); + String cardsServiceUrl = getServiceBaseUrl(); + + VirgilCardClient cardClient; + if (StringUtils.isBlank(cardsServiceUrl)) { + cardClient = new VirgilCardClient(); + } else { + cardClient = new VirgilCardClient(cardsServiceUrl); + } + SignCallback signCallback = Mockito.mock(SignCallback.class); + + String identity = Generator.identity(); + RawSignedModel rawSignedModel = mocker.generateCardModel(identity); + AccessToken expiredToken = mocker.generateExpiredAccessToken(identity); + AccessToken token = mocker.generateAccessToken(identity); + when(accessTokenProvider.getToken(Mockito.any(TokenContext.class))).thenReturn(expiredToken, + token); + when(signCallback.onSign(Mockito.any(RawSignedModel.class))).thenReturn(rawSignedModel); + + CardManager cardManager = new CardManager(this.cardCrypto, accessTokenProvider, cardVerifier, + cardClient, signCallback, true); + + // Let expiredToken to expire + Thread.sleep(2000); + + Card card = cardManager.publishCard(rawSignedModel); + assertNotNull(card); + + Card loadedCard = cardManager.getCard(card.getIdentifier()); + assertNotNull(loadedCard); + + List foundCards = cardManager.searchCards(card.getIdentity()); + assertNotNull(foundCards); + assertEquals(1, foundCards.size()); + } + + @Test + public void stc_8_extraSign_should_addValidSignature() throws CryptoException { + // STC-8 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); + + VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); + modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey()); + + assertEquals(2, cardModel.getSignatures().size()); + RawSignature extraSignature = cardModel.getSignatures().get(1); + assertEquals("test_id", extraSignature.getSigner()); + assertNull(extraSignature.getSnapshot()); + assertTrue( + virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(extraSignature.getSignature()), + cardModel.getContentSnapshot(), keyPair2.getPublicKey())); + } + + @Test + public void stc_8_secondExtraSign_should_throwException() throws CryptoException { + // STC-8 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + + VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); + modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey()); + assertThrows(SignatureNotUniqueException.class, () -> { + modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey()); + }); + } + + @Test + public void stc_8_secondSelfSign_should_throwException() throws CryptoException { + // STC-8 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + assertTrue(cardModel.getSignatures().isEmpty()); + + modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); + assertThrows(SignatureNotUniqueException.class, () -> { + modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); + }); + } + + @Test + public void stc_8_selfSign_should_addValidSignature() throws CryptoException { + // STC-8 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + assertTrue(cardModel.getSignatures().isEmpty()); + + modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); + assertEquals(1, cardModel.getSignatures().size()); + + RawSignature selfSignature = cardModel.getSignatures().get(0); + assertEquals(SignerType.SELF.getRawValue(), selfSignature.getSigner()); + assertNull(selfSignature.getSnapshot()); + assertTrue( + virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(selfSignature.getSignature()), + cardModel.getContentSnapshot(), keyPair.getPublicKey())); } - Card card = Card.parse(cardCrypto, rawSignedModel); - - List whitelists = new ArrayList<>(); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, true, - whitelists); - - assertFalse(virgilCardVerifier.verifyCard(card), "Card should NOT be verified"); - } - - @Test - public void stc_10_verifySelfAndVirgilSignWithEmptyWhiteList() throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, true, - new ArrayList()); - - assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); - } - - @Test - public void stc_10_verifySelfSignWithEmptyWhiteList() throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, false, - new ArrayList()); - - assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); - } - - @Test - public void stc_10_verifyVirgilSignWithEmptyWhiteList() throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, true, - new ArrayList()); - - assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); - } - - @Test - public void stc_10_whiteList1Key() throws CryptoException { - // STC-10 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(10, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - - VirgilPrivateKey privateKey1 = virgilCrypto - .importPrivateKey( - ConvertionUtils.base64ToBytes(dataProvider.getJsonByKey(10, "private_key1_base64"))) - .getPrivateKey(); - VirgilPublicKey publicKey1 = virgilCrypto.extractPublicKey(privateKey1); - - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, - new ArrayList()); - - List verifierCredentialsList = new ArrayList<>(); - verifierCredentialsList - .add(new VerifierCredentials("extra", virgilCrypto.exportPublicKey(publicKey1))); - Whitelist whitelist1 = new Whitelist(verifierCredentialsList); - virgilCardVerifier.addWhiteList(whitelist1); - - assertTrue(virgilCardVerifier.verifyCard(card), "Card should be verified"); - } - - @Test - public void stc_11() throws CryptoException { - // STC-11 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(11, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, - Collections.emptyList()); - assertTrue(virgilCardVerifier.verifyCard(card)); - - virgilCardVerifier.setVerifySelfSignature(true); - assertFalse(virgilCardVerifier.verifyCard(card)); - } - - @Test - public void stc_12() throws CryptoException { - // STC-12 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(12, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false); - assertTrue(virgilCardVerifier.verifyCard(card)); - - virgilCardVerifier.setVerifyVirgilSignature(true); - assertFalse(virgilCardVerifier.verifyCard(card)); - } - - @Test - public void stc_14() throws CryptoException { - // STC-14 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(14, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, true); - assertFalse(virgilCardVerifier.verifyCard(card)); - } - - @Test - public void stc_15() throws CryptoException { - // STC-15 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(15, STRING)); - Card card = Card.parse(cardCrypto, rawSignedModel); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, true, false); - assertFalse(virgilCardVerifier.verifyCard(card)); - } - - @Test - public void stc_16() throws CryptoException { - // STC-16 - RawSignedModel rawSignedModel = RawSignedModel - .fromString(dataProvider.getTestDataAs(16, STRING)); - final VirgilPublicKey publicKey = (VirgilPublicKey) cardCrypto.importPublicKey( - ConvertionUtils.base64ToBytes(dataProvider.getJsonByKey(16, "public_key1_base64"))); - - VirgilPublicKey publicKeyTwo = mocker.generatePublicKey(); - List verifierCredentialsList = new ArrayList<>(); - verifierCredentialsList.add(new VerifierCredentials(TEST_SIGNER_TYPE, - TestUtils.exportPublicKey(publicKeyTwo.getPublicKey()))); - Whitelist whitelistOne = new Whitelist(verifierCredentialsList); - List whitelists = new ArrayList<>(); - whitelists.add(whitelistOne); - - Card card = Card.parse(cardCrypto, rawSignedModel); - VirgilCardVerifier virgilCardVerifier = new VirgilCardVerifier(cardCrypto, false, false, - whitelists); - assertFalse(virgilCardVerifier.verifyCard(card)); - - verifierCredentialsList - .add(new VerifierCredentials("extra", virgilCrypto.exportPublicKey(publicKey))); - - assertTrue(virgilCardVerifier.verifyCard(card)); - } - - @Test - public void stc_26() throws CryptoException, VirgilServiceException, InterruptedException { - // STC-26 - AccessTokenProvider accessTokenProvider = Mockito.mock(AccessTokenProvider.class); - final CardVerifier cardVerifier = new VirgilCardVerifier(this.cardCrypto, false, false); - String cardsServiceUrl = getCardsServiceUrl(); - - VirgilCardClient cardClient; - if (StringUtils.isBlank(cardsServiceUrl)) { - cardClient = new VirgilCardClient(); - } else { - cardClient = new VirgilCardClient(cardsServiceUrl); + + @Test + public void stc_8_selfSignWithSignatureSnapshot_should_addValidSignature() + throws CryptoException { + // STC-8 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + assertTrue(cardModel.getSignatures().isEmpty()); + + byte[] signatureSnapshot = new byte[32]; + new Random().nextBytes(signatureSnapshot); + + modelSigner.selfSign(cardModel, keyPair.getPrivateKey(), signatureSnapshot); + assertEquals(1, cardModel.getSignatures().size()); + + RawSignature selfSignature = cardModel.getSignatures().get(0); + assertEquals(SignerType.SELF.getRawValue(), selfSignature.getSigner()); + assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), selfSignature.getSnapshot()); + + byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), + signatureSnapshot); + + assertTrue( + virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(selfSignature.getSignature()), + extendedSnapshot, keyPair.getPublicKey())); } - SignCallback signCallback = Mockito.mock(SignCallback.class); - - String identity = Generator.identity(); - RawSignedModel rawSignedModel = mocker.generateCardModel(identity); - AccessToken expiredToken = mocker.generateExpiredAccessToken(identity); - AccessToken token = mocker.generateAccessToken(identity); - when(accessTokenProvider.getToken(Mockito.any(TokenContext.class))).thenReturn(expiredToken, - token); - when(signCallback.onSign(Mockito.any(RawSignedModel.class))).thenReturn(rawSignedModel); - - CardManager cardManager = new CardManager(this.cardCrypto, accessTokenProvider, cardVerifier, - cardClient, signCallback, true); - - // Let expiredToken to expire - Thread.sleep(2000); - - Card card = cardManager.publishCard(rawSignedModel); - assertNotNull(card); - - Card loadedCard = cardManager.getCard(card.getIdentifier()); - assertNotNull(loadedCard); - - List foundCards = cardManager.searchCards(card.getIdentity()); - assertNotNull(foundCards); - assertEquals(1, foundCards.size()); - } - - @Test - public void stc_8_extraSign_should_addValidSignature() throws CryptoException { - // STC-8 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); - - VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); - modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey()); - - assertEquals(2, cardModel.getSignatures().size()); - RawSignature extraSignature = cardModel.getSignatures().get(1); - assertEquals("test_id", extraSignature.getSigner()); - assertNull(extraSignature.getSnapshot()); - assertTrue( - virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(extraSignature.getSignature()), - cardModel.getContentSnapshot(), keyPair2.getPublicKey())); - } - - @Test - public void stc_8_secondExtraSign_should_throwException() throws CryptoException { - // STC-8 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - - VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); - modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey()); - assertThrows(SignatureNotUniqueException.class, () -> { - modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey()); - }); - } - - @Test - public void stc_8_secondSelfSign_should_throwException() throws CryptoException { - // STC-8 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - assertTrue(cardModel.getSignatures().isEmpty()); - - modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); - assertThrows(SignatureNotUniqueException.class, () -> { - modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); - }); - } - - @Test - public void stc_8_selfSign_should_addValidSignature() throws CryptoException { - // STC-8 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - assertTrue(cardModel.getSignatures().isEmpty()); - - modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); - assertEquals(1, cardModel.getSignatures().size()); - - RawSignature selfSignature = cardModel.getSignatures().get(0); - assertEquals(SignerType.SELF.getRawValue(), selfSignature.getSigner()); - assertNull(selfSignature.getSnapshot()); - assertTrue( - virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(selfSignature.getSignature()), - cardModel.getContentSnapshot(), keyPair.getPublicKey())); - } - - @Test - public void stc_8_selfSignWithSignatureSnapshot_should_addValidSignature() - throws CryptoException { - // STC-8 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - assertTrue(cardModel.getSignatures().isEmpty()); - - byte[] signatureSnapshot = new byte[32]; - new Random().nextBytes(signatureSnapshot); - - modelSigner.selfSign(cardModel, keyPair.getPrivateKey(), signatureSnapshot); - assertEquals(1, cardModel.getSignatures().size()); - - RawSignature selfSignature = cardModel.getSignatures().get(0); - assertEquals(SignerType.SELF.getRawValue(), selfSignature.getSigner()); - assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), selfSignature.getSnapshot()); - - byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), - signatureSnapshot); - - assertTrue( - virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(selfSignature.getSignature()), - extendedSnapshot, keyPair.getPublicKey())); - } - - @Test - public void stc_9_extraSignWithExtraFields_should_addValidSignature() throws CryptoException { - // STC-9 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); - - Map additionalData = new HashMap<>(); - additionalData.put(TEST_KEY_ONE, TEST_VALUE_ONE); - additionalData.put(TEST_KEY_TWO, TEST_VALUE_TWO); - - final byte[] signatureSnapshot = ConvertionUtils.captureSnapshot(additionalData); - - VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); - modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey(), additionalData); - - assertEquals(2, cardModel.getSignatures().size()); - RawSignature extraSignature = cardModel.getSignatures().get(1); - assertEquals("test_id", extraSignature.getSigner()); - assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), extraSignature.getSnapshot()); - - byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), - signatureSnapshot); - assertTrue( - virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(extraSignature.getSignature()), - extendedSnapshot, keyPair2.getPublicKey())); - } - - @Test - public void stc_9_extraSignWithSignatureSnapshot_should_addValidSignature() - throws CryptoException { - // STC-9 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); - - byte[] signatureSnapshot = new byte[32]; - new Random().nextBytes(signatureSnapshot); - - VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); - modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey(), signatureSnapshot); - - assertEquals(2, cardModel.getSignatures().size()); - RawSignature extraSignature = cardModel.getSignatures().get(1); - assertEquals("test_id", extraSignature.getSigner()); - assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), extraSignature.getSnapshot()); - - byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), - signatureSnapshot); - assertTrue( - virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(extraSignature.getSignature()), - extendedSnapshot, keyPair2.getPublicKey())); - } - - @Test - public void stc_9_secondExtraSignWithSignatureSnapshot_should_throwException() - throws CryptoException { - // STC-9 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - - byte[] signatureSnapshot = new byte[32]; - new Random().nextBytes(signatureSnapshot); - - VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); - modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey(), signatureSnapshot); - assertThrows(SignatureNotUniqueException.class, () -> { - modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey(), signatureSnapshot); - }); - } - - @Test - public void stc_9_selfSignWithExtraFields_should_addValidSignature() throws CryptoException { - // STC-9 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - - Map additionalData = new HashMap<>(); - additionalData.put(TEST_KEY_ONE, TEST_VALUE_ONE); - additionalData.put(TEST_KEY_TWO, TEST_VALUE_TWO); - - final byte[] signatureSnapshot = ConvertionUtils.captureSnapshot(additionalData); - modelSigner.selfSign(cardModel, keyPair.getPrivateKey(), additionalData); - assertEquals(1, cardModel.getSignatures().size()); - - RawSignature selfSignature = cardModel.getSignatures().get(0); - assertEquals(SignerType.SELF.getRawValue(), selfSignature.getSigner()); - assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), selfSignature.getSnapshot()); - - byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), - signatureSnapshot); - assertTrue( - virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(selfSignature.getSignature()), - extendedSnapshot, keyPair.getPublicKey())); - } - - @Test - public void stc_9_selfSignWithSignatureSnapshot_should_addValidSignature() - throws CryptoException { - // STC-9 - VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); - RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); - - byte[] signatureSnapshot = new byte[32]; - new Random().nextBytes(signatureSnapshot); - - modelSigner.selfSign(cardModel, keyPair.getPrivateKey(), signatureSnapshot); - assertEquals(1, cardModel.getSignatures().size()); - - RawSignature selfSignature = cardModel.getSignatures().get(0); - assertEquals(SignerType.SELF.getRawValue(), selfSignature.getSigner()); - assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), selfSignature.getSnapshot()); - - byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), - signatureSnapshot); - assertTrue( - virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(selfSignature.getSignature()), - extendedSnapshot, keyPair.getPublicKey())); - } - -} \ No newline at end of file + + @Test + public void stc_9_extraSignWithExtraFields_should_addValidSignature() throws CryptoException { + // STC-9 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); + + Map additionalData = new HashMap<>(); + additionalData.put(TEST_KEY_ONE, TEST_VALUE_ONE); + additionalData.put(TEST_KEY_TWO, TEST_VALUE_TWO); + + final byte[] signatureSnapshot = ConvertionUtils.captureSnapshot(additionalData); + + VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); + modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey(), additionalData); + + assertEquals(2, cardModel.getSignatures().size()); + RawSignature extraSignature = cardModel.getSignatures().get(1); + assertEquals("test_id", extraSignature.getSigner()); + assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), extraSignature.getSnapshot()); + + byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), + signatureSnapshot); + assertTrue( + virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(extraSignature.getSignature()), + extendedSnapshot, keyPair2.getPublicKey())); + } + + @Test + public void stc_9_extraSignWithSignatureSnapshot_should_addValidSignature() + throws CryptoException { + // STC-9 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + modelSigner.selfSign(cardModel, keyPair.getPrivateKey()); + + byte[] signatureSnapshot = new byte[32]; + new Random().nextBytes(signatureSnapshot); + + VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); + modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey(), signatureSnapshot); + + assertEquals(2, cardModel.getSignatures().size()); + RawSignature extraSignature = cardModel.getSignatures().get(1); + assertEquals("test_id", extraSignature.getSigner()); + assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), extraSignature.getSnapshot()); + + byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), + signatureSnapshot); + assertTrue( + virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(extraSignature.getSignature()), + extendedSnapshot, keyPair2.getPublicKey())); + } + + @Test + public void stc_9_secondExtraSignWithSignatureSnapshot_should_throwException() + throws CryptoException { + // STC-9 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + + byte[] signatureSnapshot = new byte[32]; + new Random().nextBytes(signatureSnapshot); + + VirgilKeyPair keyPair2 = virgilCrypto.generateKeyPair(); + modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey(), signatureSnapshot); + assertThrows(SignatureNotUniqueException.class, () -> { + modelSigner.sign(cardModel, "test_id", keyPair2.getPrivateKey(), signatureSnapshot); + }); + } + + @Test + public void stc_9_selfSignWithExtraFields_should_addValidSignature() throws CryptoException { + // STC-9 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + + Map additionalData = new HashMap<>(); + additionalData.put(TEST_KEY_ONE, TEST_VALUE_ONE); + additionalData.put(TEST_KEY_TWO, TEST_VALUE_TWO); + + final byte[] signatureSnapshot = ConvertionUtils.captureSnapshot(additionalData); + modelSigner.selfSign(cardModel, keyPair.getPrivateKey(), additionalData); + assertEquals(1, cardModel.getSignatures().size()); + + RawSignature selfSignature = cardModel.getSignatures().get(0); + assertEquals(SignerType.SELF.getRawValue(), selfSignature.getSigner()); + assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), selfSignature.getSnapshot()); + + byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), + signatureSnapshot); + assertTrue( + virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(selfSignature.getSignature()), + extendedSnapshot, keyPair.getPublicKey())); + } + + @Test + public void stc_9_selfSignWithSignatureSnapshot_should_addValidSignature() + throws CryptoException { + // STC-9 + VirgilKeyPair keyPair = virgilCrypto.generateKeyPair(); + RawSignedModel cardModel = mocker.generateCardModelUnsigned(keyPair.getPublicKey()); + + byte[] signatureSnapshot = new byte[32]; + new Random().nextBytes(signatureSnapshot); + + modelSigner.selfSign(cardModel, keyPair.getPrivateKey(), signatureSnapshot); + assertEquals(1, cardModel.getSignatures().size()); + + RawSignature selfSignature = cardModel.getSignatures().get(0); + assertEquals(SignerType.SELF.getRawValue(), selfSignature.getSigner()); + assertEquals(ConvertionUtils.toBase64String(signatureSnapshot), selfSignature.getSnapshot()); + + byte[] extendedSnapshot = ConvertionUtils.concatenate(cardModel.getContentSnapshot(), + signatureSnapshot); + assertTrue( + virgilCrypto.verifySignature(ConvertionUtils.base64ToBytes(selfSignature.getSignature()), + extendedSnapshot, keyPair.getPublicKey())); + } + +} diff --git a/sdk/src/test/java/com/virgilsecurity/sdk/client/VirgilCardClientConstructorTest.java b/sdk/src/test/java/com/virgilsecurity/sdk/client/VirgilCardClientConstructorTest.java new file mode 100644 index 00000000..5e38f087 --- /dev/null +++ b/sdk/src/test/java/com/virgilsecurity/sdk/client/VirgilCardClientConstructorTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015-2020, Virgil Security, Inc. + * + * Lead Maintainer: Virgil Security Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * (1) Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * (2) Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * (3) Neither the name of virgil nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.virgilsecurity.sdk.client; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class VirgilCardClientConstructorTest { + + @Test + public void shouldAppendCardsPathForBaseUrl() { + VirgilCardClient cardClient = new VirgilCardClient("https://api.virgilsecurity.com"); + + assertEquals("https://api.virgilsecurity.com/card/v5/", cardClient.getServiceUrl().toString()); + } + + @Test + public void shouldNormalizeCardsPathWithoutTrailingSlash() { + VirgilCardClient cardClient = new VirgilCardClient("https://api.virgilsecurity.com/card/v5"); + + assertEquals("https://api.virgilsecurity.com/card/v5/", cardClient.getServiceUrl().toString()); + } + + @Test + public void shouldKeepCardsPathWithTrailingSlash() { + VirgilCardClient cardClient = new VirgilCardClient("https://api.virgilsecurity.com/card/v5/"); + + assertEquals("https://api.virgilsecurity.com/card/v5/", cardClient.getServiceUrl().toString()); + } + + @Test + public void shouldKeepCustomPathUntouched() { + VirgilCardClient cardClient = new VirgilCardClient("https://localhost:9443/custom/cards/"); + + assertEquals("https://localhost:9443/card/v5/", cardClient.getServiceUrl().toString()); + } +} diff --git a/sdk/src/test/java/com/virgilsecurity/sdk/client/VirgilCardClientTest.java b/sdk/src/test/java/com/virgilsecurity/sdk/client/VirgilCardClientTest.java index 20a91f40..5235577f 100644 --- a/sdk/src/test/java/com/virgilsecurity/sdk/client/VirgilCardClientTest.java +++ b/sdk/src/test/java/com/virgilsecurity/sdk/client/VirgilCardClientTest.java @@ -67,7 +67,7 @@ public class VirgilCardClientTest extends PropertyManager { @BeforeEach public void setUp() { - String url = getCardsServiceUrl(); + String url = getServiceBaseUrl(); if (StringUtils.isBlank(url)) { cardClient = new VirgilCardClient(); } else { @@ -184,9 +184,8 @@ public void revoke_card() throws CryptoException, VirgilServiceException { RawSignedModel cardModelBeforePublish = mocker.generateCardModel(identity); assertNotNull(cardModelBeforePublish); - RawSignedModel cardModelAfterPublish = - cardClient.publishCard(cardModelBeforePublish, - mocker.generateAccessToken(identity).stringRepresentation()); + RawSignedModel cardModelAfterPublish = cardClient.publishCard(cardModelBeforePublish, + mocker.generateAccessToken(identity).stringRepresentation()); assertNotNull(cardModelAfterPublish); TestUtils.assertCardModelsEquals(cardModelBeforePublish, cardModelAfterPublish); @@ -196,15 +195,13 @@ public void revoke_card() throws CryptoException, VirgilServiceException { cardClient.revokeCard(publishedCard.getIdentifier(), mocker.generateAccessToken(identity).stringRepresentation()); - Tuple revokedTuple = - cardClient.getCard(publishedCard.getIdentifier(), - mocker.generateAccessToken(identity).stringRepresentation()); + Tuple revokedTuple = cardClient.getCard(publishedCard.getIdentifier(), + mocker.generateAccessToken(identity).stringRepresentation()); assertTrue(revokedTuple.getRight()); - List searchedModels = - cardClient.searchCards(identity, - mocker.generateAccessToken(identity).stringRepresentation()); + List searchedModels = cardClient.searchCards(identity, + mocker.generateAccessToken(identity).stringRepresentation()); assertEquals(searchedModels.size(), 0); } } diff --git a/sdk/src/test/java/com/virgilsecurity/sdk/common/PropertyManager.java b/sdk/src/test/java/com/virgilsecurity/sdk/common/PropertyManager.java index 61c38b8a..d6d7db16 100644 --- a/sdk/src/test/java/com/virgilsecurity/sdk/common/PropertyManager.java +++ b/sdk/src/test/java/com/virgilsecurity/sdk/common/PropertyManager.java @@ -33,22 +33,27 @@ package com.virgilsecurity.sdk.common; +import com.virgilsecurity.sdk.crypto.HashAlgorithm; import com.virgilsecurity.sdk.crypto.VirgilCrypto; import com.virgilsecurity.sdk.crypto.VirgilPrivateKey; import com.virgilsecurity.sdk.crypto.VirgilPublicKey; import com.virgilsecurity.sdk.crypto.exceptions.CryptoException; import com.virgilsecurity.sdk.utils.ConvertionUtils; +import com.virgilsecurity.common.util.HexUtils; import com.virgilsecurity.testcommon.property.EnvPropertyReader; import com.virgilsecurity.testcommon.utils.PropertyUtils; +import org.opentest4j.TestAbortedException; + +import java.io.File; +import java.security.PublicKey; + public class PropertyManager { private static final String ENVIRONMENT_SYS_VAR = "environment"; private static final String APP_ID = "APP_ID"; - private static final String APP_PRIVATE_KEY = "APP_PRIVATE_KEY"; - private static final String APP_PUBLIC_KEY = "APP_PUBLIC_KEY"; - private static final String APP_PUBLIC_KEY_ID = "APP_PUBLIC_KEY_ID"; - private static final String CARDS_SERVICE_ADDRESS = "CARDS_SERVICE_ADDRESS"; + private static final String APP_PRIVATE_KEY = "APP_KEY"; + private static final String BASE_SERVICE_URL = "BASE_SERVICE_URL"; private final EnvPropertyReader propertyReader; private final VirgilCrypto crypto; @@ -57,17 +62,24 @@ public class PropertyManager { * Create new instance of {@link PropertyManager}. */ public PropertyManager() { + File expectedEnvFile = new File(new File(System.getProperty("user.dir")).getParentFile(), "env.json"); + if (!expectedEnvFile.exists()) { + throw new TestAbortedException( + "Missing " + expectedEnvFile.getAbsolutePath() + ". " + + "Cards-service integration tests are skipped."); + } + String environment = PropertyUtils.getSystemProperty(ENVIRONMENT_SYS_VAR); if (environment != null) { this.propertyReader = new EnvPropertyReader.Builder() - .environment(EnvPropertyReader.Environment.fromType(environment)) - .isDefaultSubmodule(true) - .build(); + .environment(EnvPropertyReader.Environment.fromType(environment)) + .isDefaultSubmodule(true) + .build(); } else { this.propertyReader = new EnvPropertyReader.Builder() - .isDefaultSubmodule(true) - .build(); + .isDefaultSubmodule(true) + .build(); } this.crypto = new VirgilCrypto(); @@ -87,20 +99,41 @@ public VirgilPrivateKey getAppPrivateKey() { } public VirgilPublicKey getAppPublicKey() { - String publicKeyString = propertyReader.getProperty(APP_PUBLIC_KEY); - + String privateKeyString = propertyReader.getProperty(APP_PRIVATE_KEY); try { - return crypto.importPublicKey(ConvertionUtils.base64ToBytes(publicKeyString)); + return crypto.importPrivateKey(ConvertionUtils.base64ToBytes(privateKeyString)).getPublicKey(); } catch (CryptoException e) { throw new IllegalStateException(e); } } public String getAppPublicKeyId() { - return propertyReader.getProperty(APP_PUBLIC_KEY_ID); + String privateKeyString = propertyReader.getProperty(APP_PRIVATE_KEY); + try { + + VirgilPublicKey publicKey = crypto.importPrivateKey(ConvertionUtils.base64ToBytes(privateKeyString)) + .getPublicKey(); + byte[] publicKeyBase64Bytes = ConvertionUtils.toBase64Bytes(crypto.exportPublicKey(publicKey)); + byte[] publicKeyHash = crypto.computeHash(publicKeyBase64Bytes, HashAlgorithm.SHA512); + String keyId = ConvertionUtils.toHex(publicKeyHash).substring(0, 32).toLowerCase(); + return keyId; + + } catch (CryptoException e) { + throw new IllegalStateException(e); + } } - public String getCardsServiceUrl() { - return propertyReader.getProperty(CARDS_SERVICE_ADDRESS); + public String getServiceBaseUrl() { + return getOptionalProperty(BASE_SERVICE_URL); } + + private String getOptionalProperty(String name) { + try { + return propertyReader.getProperty(name); + } catch (RuntimeException e) { + // EnvPropertyReader throws when property is absent. + return null; + } + } + } diff --git a/sdk/src/test/java/com/virgilsecurity/sdk/jwt/JwtCrossCompatibilityTest.java b/sdk/src/test/java/com/virgilsecurity/sdk/jwt/JwtCrossCompatibilityTest.java index 2050c4ee..95c86c38 100644 --- a/sdk/src/test/java/com/virgilsecurity/sdk/jwt/JwtCrossCompatibilityTest.java +++ b/sdk/src/test/java/com/virgilsecurity/sdk/jwt/JwtCrossCompatibilityTest.java @@ -53,6 +53,7 @@ import org.mockito.stubbing.Answer; import java.io.InputStreamReader; +import java.util.Date; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -202,8 +203,11 @@ public void stc_29() { assertEquals(sampleJson.get("STC-29.jwt_signature_base64").getAsString(), ConvertionUtils.toBase64String(jwt.getSignatureData())); - // Call isExpired() - assertFalse(jwt.isExpired()); + // Call isExpired(Date) + // (avoid relying on wall-clock time because STC-29.jwt eventually becomes expired) + long expiresAt = jwt.getBodyContent().getExpiresAt(); + assertFalse(jwt.isExpired(new Date((expiresAt - 1) * 1000)), "Token should not be expired just before exp"); + assertTrue(jwt.isExpired(new Date(expiresAt * 1000)), "Token should be expired at exp"); // Call stringRepresentation() assertEquals(token, jwt.stringRepresentation()); diff --git a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/KeyknoxManagerTest.kt b/sdk/src/test/kotlin/com/virgilsecurity/keyknox/KeyknoxManagerTest.kt index eb06893c..53cc0d0b 100644 --- a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/KeyknoxManagerTest.kt +++ b/sdk/src/test/kotlin/com/virgilsecurity/keyknox/KeyknoxManagerTest.kt @@ -42,12 +42,14 @@ import com.virgilsecurity.keyknox.exception.SignerNotFoundException import com.virgilsecurity.keyknox.utils.base64Encode import com.virgilsecurity.keyknox.utils.random import com.virgilsecurity.sdk.common.TimeSpan +import com.virgilsecurity.sdk.common.PropertyManager import com.virgilsecurity.sdk.crypto.* import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import java.net.URL import java.util.* import java.util.concurrent.TimeUnit @@ -66,6 +68,7 @@ class KeyknoxManagerTest { private lateinit var keyknoxClient: KeyknoxClient private lateinit var keyknoxManager: KeyknoxManager private lateinit var provider: CachingJwtProvider + private lateinit var propertyManager: PropertyManager @BeforeEach fun setup() { @@ -75,13 +78,19 @@ class KeyknoxManagerTest { this.privateKey = keyPair.privateKey this.publicKey = keyPair.publicKey this.publicKeys = arrayListOf(this.publicKey) + this.propertyManager = PropertyManager() - val jwtGenerator = JwtGenerator(TestConfig.appId, TestConfig.appPrivateKey, TestConfig.appPublicKeyId, TimeSpan.fromTime(600, TimeUnit.SECONDS), + val jwtGenerator = JwtGenerator(this.propertyManager.getAppId(), this.propertyManager.getAppPrivateKey(), this.propertyManager.getAppPublicKeyId(), TimeSpan.fromTime(600, TimeUnit.SECONDS), VirgilAccessTokenSigner(this.virgilCrypto)) this.provider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { jwtGenerator.generateToken(identity) }) - this.keyknoxClient = KeyknoxClient(this.provider) + val serviceBaseUrl = this.propertyManager.getServiceBaseUrl() + this.keyknoxClient = if (serviceBaseUrl.isNullOrBlank()) { + KeyknoxClient(this.provider) + } else { + KeyknoxClient(this.provider, URL(serviceBaseUrl)) + } this.keyknoxManager = KeyknoxManager(this.keyknoxClient, this.keyknoxCrypto) } diff --git a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/TestConfig.kt b/sdk/src/test/kotlin/com/virgilsecurity/keyknox/TestConfig.kt deleted file mode 100644 index 45bea919..00000000 --- a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/TestConfig.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2015-2020, Virgil Security, Inc. - * - * Lead Maintainer: Virgil Security Inc. - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * (1) Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * (2) Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * (3) Neither the name of virgil nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.virgilsecurity.keyknox - -import com.virgilsecurity.keyknox.utils.base64Decode -import com.virgilsecurity.sdk.crypto.VirgilCrypto -import com.virgilsecurity.sdk.crypto.VirgilPrivateKey -import com.virgilsecurity.testcommon.property.EnvPropertyReader -import com.virgilsecurity.testcommon.utils.PropertyUtils - -class TestConfig { - companion object { - private const val APP_ID = "APP_ID" - private const val APP_PRIVATE_KEY = "APP_PRIVATE_KEY" - private const val APP_PUBLIC_KEY_ID = "APP_PUBLIC_KEY_ID" - private const val KEYKNOX_SERVICE_ADDRESS = "KEYKNOX_SERVICE_ADDRESS" - - private const val ENVIRONMENT_PARAMETER = "environment" - - private val propertyReader: EnvPropertyReader by lazy { - val environment = PropertyUtils.getSystemProperty(ENVIRONMENT_PARAMETER) - - if (environment != null) - EnvPropertyReader.Builder() - .environment(EnvPropertyReader.Environment.fromType(environment)) - .isDefaultSubmodule(true) - .build() - else - EnvPropertyReader.Builder() - .isDefaultSubmodule(true) - .build() - } - - val virgilCrypto = VirgilCrypto(false) - val appId: String by lazy { propertyReader.getProperty(APP_ID) } - val appPrivateKey: VirgilPrivateKey by lazy { - with(propertyReader.getProperty(APP_PRIVATE_KEY)) { - virgilCrypto.importPrivateKey(base64Decode(this)).privateKey - } - } - val appPublicKeyId: String by lazy { propertyReader.getProperty(APP_PUBLIC_KEY_ID) } - val keyknoxServiceAddress: String by lazy { propertyReader.getProperty(KEYKNOX_SERVICE_ADDRESS) } - } -} diff --git a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/client/KeyknoxClientTest.kt b/sdk/src/test/kotlin/com/virgilsecurity/keyknox/client/KeyknoxClientTest.kt index 1ccb0f15..9bf4f56d 100644 --- a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/client/KeyknoxClientTest.kt +++ b/sdk/src/test/kotlin/com/virgilsecurity/keyknox/client/KeyknoxClientTest.kt @@ -37,9 +37,9 @@ import com.virgilsecurity.crypto.foundation.Aes256Gcm import com.virgilsecurity.crypto.foundation.RecipientCipher import com.virgilsecurity.crypto.foundation.Sha512 import com.virgilsecurity.crypto.foundation.Signer -import com.virgilsecurity.keyknox.TestConfig import com.virgilsecurity.keyknox.utils.base64Encode import com.virgilsecurity.sdk.common.TimeSpan +import com.virgilsecurity.sdk.common.PropertyManager import com.virgilsecurity.sdk.crypto.* import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider @@ -60,6 +60,7 @@ class KeyknoxClientTest { private lateinit var keyknoxClient: KeyknoxClientProtocol private lateinit var publicKey: VirgilPublicKey private lateinit var privateKey: VirgilPrivateKey + private lateinit var propertyManager: PropertyManager @BeforeEach fun setup() { @@ -70,12 +71,12 @@ class KeyknoxClientTest { val keyPair = this.virgilCrypto.generateKeyPair(KeyPairType.ED25519) this.privateKey = keyPair.privateKey this.publicKey = keyPair.publicKey + this.propertyManager = PropertyManager() - - val jwtGenerator = JwtGenerator(TestConfig.appId, TestConfig.appPrivateKey, TestConfig.appPublicKeyId, TimeSpan.fromTime(600, TimeUnit.SECONDS), + val jwtGenerator = JwtGenerator(this.propertyManager.getAppId(), this.propertyManager.getAppPrivateKey(), this.propertyManager.getAppPublicKeyId(), TimeSpan.fromTime(600, TimeUnit.SECONDS), VirgilAccessTokenSigner(this.virgilCrypto)) val tokenProvider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { jwtGenerator.generateToken(identity) }) - this.keyknoxClient = KeyknoxClient(tokenProvider, URL(TestConfig.keyknoxServiceAddress)) + this.keyknoxClient = KeyknoxClient(tokenProvider, URL(this.propertyManager.getServiceBaseUrl())) } @Test diff --git a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/cloud/CloudKeyStorageTest.kt b/sdk/src/test/kotlin/com/virgilsecurity/keyknox/cloud/CloudKeyStorageTest.kt index 5a0690ca..d36cd8a1 100644 --- a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/cloud/CloudKeyStorageTest.kt +++ b/sdk/src/test/kotlin/com/virgilsecurity/keyknox/cloud/CloudKeyStorageTest.kt @@ -34,7 +34,6 @@ package com.virgilsecurity.keyknox.cloud import com.virgilsecurity.keyknox.KeyknoxManager -import com.virgilsecurity.keyknox.TestConfig import com.virgilsecurity.keyknox.client.KeyknoxClient import com.virgilsecurity.keyknox.crypto.KeyknoxCrypto import com.virgilsecurity.keyknox.crypto.KeyknoxCryptoProtocol @@ -42,6 +41,7 @@ import com.virgilsecurity.keyknox.exception.CloudKeyStorageException import com.virgilsecurity.keyknox.exception.CloudStorageOutOfSyncException import com.virgilsecurity.keyknox.exception.EntryNotFoundException import com.virgilsecurity.sdk.common.TimeSpan +import com.virgilsecurity.sdk.common.PropertyManager import com.virgilsecurity.sdk.crypto.* import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider @@ -50,6 +50,7 @@ import com.virgilsecurity.sdk.storage.KeyEntry import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import java.net.URL import java.util.* import java.util.concurrent.TimeUnit @@ -65,22 +66,30 @@ class CloudKeyStorageTest { private lateinit var keyknoxManager: KeyknoxManager private lateinit var provider: CachingJwtProvider private lateinit var keyStorage: CloudKeyStorageProtocol + private lateinit var propertyManager: PropertyManager @BeforeEach fun setup() { this.virgilCrypto = VirgilCrypto(false) this.keyknoxCrypto = KeyknoxCrypto() + this.propertyManager = PropertyManager() val keyPair = this.virgilCrypto.generateKeyPair(KeyPairType.ED25519) this.privateKey = keyPair.privateKey this.publicKey = keyPair.publicKey this.publicKeys = arrayListOf(this.publicKey) - val jwtGenerator = JwtGenerator(TestConfig.appId, TestConfig.appPrivateKey, TestConfig.appPublicKeyId, TimeSpan.fromTime(600, TimeUnit.SECONDS), + + val jwtGenerator = JwtGenerator(this.propertyManager.getAppId(), this.propertyManager.getAppPrivateKey(), this.propertyManager.getAppPublicKeyId(), TimeSpan.fromTime(600, TimeUnit.SECONDS), VirgilAccessTokenSigner(this.virgilCrypto)) this.provider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { jwtGenerator.generateToken(identity) }) - this.keyknoxClient = KeyknoxClient(this.provider) + val serviceBaseUrl = this.propertyManager.getServiceBaseUrl() + this.keyknoxClient = if (serviceBaseUrl.isNullOrBlank()) { + KeyknoxClient(this.provider) + } else { + KeyknoxClient(this.provider, URL(serviceBaseUrl)) + } this.keyknoxManager = KeyknoxManager(this.keyknoxClient, this.keyknoxCrypto) this.keyStorage = CloudKeyStorage(this.keyknoxManager, this.publicKeys, this.privateKey) diff --git a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/storage/SyncKeyStorageTests.kt b/sdk/src/test/kotlin/com/virgilsecurity/keyknox/storage/SyncKeyStorageTests.kt index 7470fdb7..a2bfabe3 100644 --- a/sdk/src/test/kotlin/com/virgilsecurity/keyknox/storage/SyncKeyStorageTests.kt +++ b/sdk/src/test/kotlin/com/virgilsecurity/keyknox/storage/SyncKeyStorageTests.kt @@ -34,7 +34,6 @@ package com.virgilsecurity.keyknox.storage import com.virgilsecurity.keyknox.KeyknoxManager -import com.virgilsecurity.keyknox.TestConfig import com.virgilsecurity.keyknox.client.KeyknoxClient import com.virgilsecurity.keyknox.cloud.CloudKeyStorage import com.virgilsecurity.keyknox.cloud.CloudKeyStorageProtocol @@ -44,6 +43,7 @@ import com.virgilsecurity.keyknox.exception.DecryptionFailedException import com.virgilsecurity.keyknox.exception.SignerNotFoundException import com.virgilsecurity.keyknox.model.CloudEntry import com.virgilsecurity.sdk.common.TimeSpan +import com.virgilsecurity.sdk.common.PropertyManager import com.virgilsecurity.sdk.crypto.* import com.virgilsecurity.sdk.jwt.JwtGenerator import com.virgilsecurity.sdk.jwt.accessProviders.CachingJwtProvider @@ -54,6 +54,7 @@ import com.virgilsecurity.sdk.utils.ConvertionUtils import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import java.net.URL import java.util.* import java.util.concurrent.TimeUnit @@ -68,6 +69,8 @@ class SyncKeyStorageTests { private lateinit var keyStorage: KeyStorage private lateinit var keychainStorageWrapper: KeyStorageWrapper private lateinit var syncKeyStorage: SyncKeyStorage + private lateinit var propertyManager: PropertyManager + @BeforeEach fun setup() { @@ -76,18 +79,24 @@ class SyncKeyStorageTests { val keyPair = this.virgilCrypto.generateKeyPair(KeyPairType.ED25519) this.privateKey = keyPair.privateKey this.publicKey = keyPair.publicKey + this.propertyManager = PropertyManager() val jwtGenerator = JwtGenerator( - TestConfig.appId, - TestConfig.appPrivateKey, - TestConfig.appPublicKeyId, + this.propertyManager.getAppId(), + this.propertyManager.getAppPrivateKey(), + this.propertyManager.getAppPublicKeyId(), TimeSpan.fromTime(100, TimeUnit.SECONDS), VirgilAccessTokenSigner(this.virgilCrypto) ) val provider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { jwtGenerator.generateToken(identity) }) - val keyknoxClient = KeyknoxClient(provider) + val serviceBaseUrl = this.propertyManager.getServiceBaseUrl() + val keyknoxClient = if (serviceBaseUrl.isNullOrBlank()) { + KeyknoxClient(provider) + } else { + KeyknoxClient(provider, URL(serviceBaseUrl)) + } this.keyknoxManager = KeyknoxManager(keyknoxClient) this.cloudKeyStorage = @@ -109,9 +118,9 @@ class SyncKeyStorageTests { @Test fun init() { val jwtGenerator = JwtGenerator( - TestConfig.appId, - TestConfig.appPrivateKey, - TestConfig.appPublicKeyId, + this.propertyManager.getAppId(), + this.propertyManager.getAppPrivateKey(), + this.propertyManager.getAppPublicKeyId(), TimeSpan.fromTime(100, TimeUnit.SECONDS), VirgilAccessTokenSigner(this.virgilCrypto) ) @@ -129,13 +138,18 @@ class SyncKeyStorageTests { // Load private key from Keychain val privateKey = this.privateKey - val syncKeyStorage = SyncKeyStorage( - identity = "Alice", - accessTokenProvider = accessTokenProvider, - crypto = this.virgilCrypto, + val serviceBaseUrl = this.propertyManager.getServiceBaseUrl() + val keyknoxClient = if (serviceBaseUrl.isNullOrBlank()) { + KeyknoxClient(accessTokenProvider) + } else { + KeyknoxClient(accessTokenProvider, URL(serviceBaseUrl)) + } + val cloudKeyStorage = CloudKeyStorage( + keyknoxManager = KeyknoxManager(keyknoxClient), publicKeys = publicKeys, privateKey = privateKey ) + val syncKeyStorage = SyncKeyStorage(identity = "Alice", cloudKeyStorage = cloudKeyStorage) syncKeyStorage.sync() assertTrue(syncKeyStorage.retrieveAll().isEmpty()) @@ -331,7 +345,7 @@ class SyncKeyStorageTests { assertEquals(newPublicKeys.map { it.identifier }, pubIds) assertEquals( newPrivateKey.identifier, - (cloudKeyStorage.privateKey as VirgilPrivateKey).identifier + cloudKeyStorage.privateKey.identifier ) val keychainEntry2 = this.syncKeyStorage.retrieve(name) @@ -566,16 +580,21 @@ class SyncKeyStorageTests { val publicKey2 = keyPair2.publicKey val jwtGenerator = JwtGenerator( - TestConfig.appId, - TestConfig.appPrivateKey, - TestConfig.appPublicKeyId, + this.propertyManager.getAppId(), + this.propertyManager.getAppPrivateKey(), + this.propertyManager.getAppPublicKeyId(), TimeSpan.fromTime(100, TimeUnit.SECONDS), VirgilAccessTokenSigner(this.virgilCrypto) ) val provider = CachingJwtProvider(CachingJwtProvider.RenewJwtCallback { jwtGenerator.generateToken(identity) }) - var keyknoxManager = KeyknoxManager(KeyknoxClient(provider)) + val serviceBaseUrl = this.propertyManager.getServiceBaseUrl() + var keyknoxManager = if (serviceBaseUrl.isNullOrBlank()) { + KeyknoxManager(KeyknoxClient(provider)) + } else { + KeyknoxManager(KeyknoxClient(provider, URL(serviceBaseUrl))) + } val keyStorage = DefaultKeyStorage(System.getProperty("java.io.tmpdir"), UUID.randomUUID().toString()) @@ -606,7 +625,11 @@ class SyncKeyStorageTests { } // Reinit syncKeyStorage2 - keyknoxManager = KeyknoxManager(KeyknoxClient(provider)) + keyknoxManager = if (serviceBaseUrl.isNullOrBlank()) { + KeyknoxManager(KeyknoxClient(provider)) + } else { + KeyknoxManager(KeyknoxClient(provider, URL(serviceBaseUrl))) + } syncKeyStorage2 = SyncKeyStorage( identity = this.identity, keyStorage = keyStorage, diff --git a/settings.gradle b/settings.gradle index a22d0f57..2896e934 100644 --- a/settings.gradle +++ b/settings.gradle @@ -32,4 +32,28 @@ */ rootProject.name = 'Virgil SDK' -include ':api', ':sdk', ':crypto', ':crypto-android', ':android-utils', 'examples', 'common', 'test-common', 'sdk-android' + +include ':api', ':sdk', ':crypto', 'examples', 'common', 'test-common' + +def hasAndroidSdk = { + def envSdkRoot = System.getenv('ANDROID_SDK_ROOT') ?: System.getenv('ANDROID_HOME') + if (envSdkRoot != null && !envSdkRoot.trim().isEmpty()) { + return true + } + + def localProperties = file('local.properties') + if (!localProperties.exists()) { + return false + } + + def props = new Properties() + localProperties.withInputStream { props.load(it) } + def sdkDir = props.getProperty('sdk.dir') + return sdkDir != null && !sdkDir.trim().isEmpty() +} + +if (hasAndroidSdk()) { + include ':crypto-android', ':android-utils', ':sdk-android' +} else { + println 'Android SDK not configured (set ANDROID_SDK_ROOT/ANDROID_HOME or local.properties sdk.dir); skipping Android subprojects.' +} diff --git a/test-common/build.gradle b/test-common/build.gradle index 2aaf107a..e7153b05 100644 --- a/test-common/build.gradle +++ b/test-common/build.gradle @@ -43,10 +43,12 @@ dependencies { // Tests testImplementation "org.junit.jupiter:junit-jupiter-api:$versions.junit" - testRuntime "org.junit.jupiter:junit-jupiter-engine:$versions.junit" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$versions.junit" } -sourceCompatibility = 1.8 +tasks.withType(JavaCompile).configureEach { + options.release.set(8) +} compileKotlin { kotlinOptions.jvmTarget = "1.8"