Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions .github/workflows/android_gradle_fipsready.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
name: Android FIPS Ready Gradle Build and Test

on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ 'master' ]

concurrency:
group: android-fips-${{ github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
build_wolfcryptjni_fipsready:
runs-on: ubuntu-latest
steps:
- name: Clone wolfcrypt-jni
uses: actions/checkout@v4

# Free up disk space to prevent emulator from failing
- name: Free up disk space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android/sdk/ndk
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force
df -h

# Get latest stable wolfSSL version for FIPS Ready download
- name: Get latest wolfSSL stable version
id: wolfssl-version
run: |
LATEST=$(curl -s -H "Authorization: token ${{ github.token }}" \
"https://api.github.com/repos/wolfSSL/wolfssl/tags?per_page=100" | \
jq -r '.[].name | select(endswith("-stable"))' | \
sort -V | tail -1 | sed 's/^v//;s/-stable$//')
if [ -z "$LATEST" ]; then
echo "Failed to determine latest wolfSSL stable version" >&2
exit 1
fi
echo "version=$LATEST" >> $GITHUB_OUTPUT
echo "wolfSSL stable version: $LATEST"

# Cache wolfSSL FIPS Ready archive
- name: Cache wolfSSL FIPS Ready archive
uses: actions/cache@v4
id: fips-cache
with:
path: wolfssl-fips-ready.zip
key: wolfssl-fips-ready-${{ steps.wolfssl-version.outputs.version }}

# Download wolfSSL FIPS Ready if not cached
- name: Download wolfSSL FIPS Ready
if: steps.fips-cache.outputs.cache-hit != 'true'
run: |
VERSION="${{ steps.wolfssl-version.outputs.version }}"
URL="https://www.wolfssl.com/wolfssl-${VERSION}-gplv3-fips-ready.zip"
echo "Downloading: $URL"
wget -q "$URL" -O wolfssl-fips-ready.zip

Comment thread
cconlon marked this conversation as resolved.
# Extract wolfSSL FIPS Ready to expected location
- name: Extract wolfSSL FIPS Ready
run: |
unzip -q wolfssl-fips-ready.zip -d /tmp/wolfssl-fips-extract
EXTRACTED_DIR=$(find /tmp/wolfssl-fips-extract -mindepth 1 -maxdepth 1 -type d | head -1)
echo "Extracted directory: $EXTRACTED_DIR"
ls "$EXTRACTED_DIR/wolfcrypt/src/" | head -5
mv "$EXTRACTED_DIR" IDE/Android/app/src/main/cpp/wolfssl

# Configure CMakeLists.txt for FIPS Ready build
- name: Configure for FIPS Ready
run: |
sed -i 's/set(WOLFSSL_PKG_TYPE "normal")/set(WOLFSSL_PKG_TYPE "fipsready")/' \
IDE/Android/app/src/main/cpp/CMakeLists.txt
grep 'WOLFSSL_PKG_TYPE' IDE/Android/app/src/main/cpp/CMakeLists.txt

# Patch MainActivity to auto-trigger WolfCryptProvider on launch,
# so FIPS error callback fires and prints the expected hash to logcat
# without needing a button press.
- name: Patch MainActivity for auto FIPS hash detection
run: |
sed -i 's/button.setOnClickListener(buttonListener);/button.setOnClickListener(buttonListener);\n\n try { testFindProvider(null); } catch (Exception e) { e.printStackTrace(); }/' \
IDE/Android/app/src/main/java/com/example/wolfssl/MainActivity.java

# Setup Java with Gradle caching
- name: Setup java
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '21'
cache: 'gradle'

# Build all targets
- name: Gradle Build (pass 1 - placeholder hash)
run: cd IDE/Android && ./gradlew --build-cache assembleDebug assembleDebugUnitTest assembleDebugAndroidTest

# Enable KVM for hardware acceleration
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

# Cache AVD snapshot for faster emulator boot
- name: AVD cache
uses: actions/cache@v4
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-wolfcryptjni-fips-30-x86_64-google_apis-v1

# Create AVD and generate snapshot for caching
- name: Create AVD and generate snapshot
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2.37.0
with:
api-level: 30
arch: x86_64
target: google_apis
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: echo "Generated AVD snapshot for caching"

# Launch app briefly to capture FIPS in-core hash from logcat.
# The FIPS error callback prints the expected verifyCore hash on
# startup if there is a mismatch.
- name: Capture FIPS in-core hash
id: fips-hash
uses: reactivecircus/android-emulator-runner@v2.37.0
timeout-minutes: 5
with:
api-level: 30
arch: x86_64
target: google_apis
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: |
adb wait-for-device
adb logcat -c
cd IDE/Android && ./gradlew installDebug --no-daemon --no-watch-fs
adb shell am start -W -n com.example.wolfssl/.MainActivity
for i in 1 2 3 4 5 6; do sleep 5; adb logcat -d | grep -q 'hash = [A-Fa-f0-9]\{64\}' && break; echo "Waiting for FIPS hash ($i/6)..."; done
adb logcat -d > /tmp/logcat_hash.txt 2>&1
HASH=$(grep -o 'hash = [A-Fa-f0-9]\{64\}' /tmp/logcat_hash.txt | head -1 | sed 's/hash = //'); if [ -n "$HASH" ]; then echo "Captured FIPS hash: $HASH"; echo "hash=$HASH" >> $GITHUB_OUTPUT; else echo "No FIPS hash found in logcat, assuming existing hash is correct"; echo "hash=" >> $GITHUB_OUTPUT; fi

# Update FIPS hash in CMakeLists.txt and rebuild if needed
- name: Rebuild with correct FIPS hash
if: steps.fips-hash.outputs.hash != ''
run: |
HASH="${{ steps.fips-hash.outputs.hash }}"
echo "Updating FIPS hash to: $HASH"
sed -i "s/WOLFCRYPT_FIPS_CORE_HASH_VALUE=[A-Fa-f0-9]*/WOLFCRYPT_FIPS_CORE_HASH_VALUE=$HASH/g" \
IDE/Android/app/src/main/cpp/CMakeLists.txt
cd IDE/Android && ./gradlew --build-cache assembleDebug assembleDebugUnitTest assembleDebugAndroidTest

# Generate BKS KeyStore files for PKIX tests
- name: Generate BKS KeyStore files
run: |
# Bouncy Castle version may need periodic updates
BCPROV_JAR="bcprov-jdk18on-1.78.1.jar"
BCPROV_URL="https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk18on/1.78.1/${BCPROV_JAR}"
wget -q "$BCPROV_URL" -O "/tmp/${BCPROV_JAR}"
wget -q "${BCPROV_URL}.sha256" -O "/tmp/${BCPROV_JAR}.sha256"
(cd /tmp && echo "$(cat ${BCPROV_JAR}.sha256) ${BCPROV_JAR}" | sha256sum -c -)
cd examples/certs && ./convert-to-bks.sh "/tmp/${BCPROV_JAR}"

# Run instrumented tests on Android emulator
- name: Run Android Instrumented Tests
uses: reactivecircus/android-emulator-runner@v2.37.0
timeout-minutes: 15
with:
api-level: 30
arch: x86_64
target: google_apis
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
script: |
adb wait-for-device
adb shell mkdir -p /data/local/tmp/examples/certs/intermediate
adb shell mkdir -p /data/local/tmp/examples/certs/rsapss
adb shell mkdir -p /data/local/tmp/examples/certs/crl
adb push ./examples/certs/ /data/local/tmp/examples/
adb logcat -c
cd IDE/Android && ./gradlew connectedDebugAndroidTest --no-daemon --no-watch-fs || { adb logcat -d > /tmp/logcat.txt 2>&1; echo "=== LOGCAT (errors) ==="; grep -i "exception\|error\|fatal" /tmp/logcat.txt || true; exit 1; }
adb logcat -d > /tmp/logcat.txt 2>&1 || true
# Clean up emulator processes. Safe to kill -9 since
# -no-snapshot-save is used (no snapshot to corrupt).
pgrep -f '[q]emu-system' | xargs -r kill -9 2>/dev/null || true
pgrep -f '[c]rashpad' | xargs -r kill -9 2>/dev/null || true
sleep 2

# Upload test reports even on failure
- name: Upload Test Reports
uses: actions/upload-artifact@v4
if: always()
timeout-minutes: 5
with:
name: android-fips-ready-test-reports
path: |
IDE/Android/app/build/reports/androidTests/
/tmp/logcat.txt
/tmp/logcat_hash.txt
retention-days: 14
2 changes: 1 addition & 1 deletion .github/workflows/windows-vs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
ant_version:
description: 'Apache Ant version to use'
required: false
default: '1.10.15'
default: '1.10.16'
type: string
platform_toolset:
description: 'Visual Studio platform toolset (auto-detect if not specified)'
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ wolfcrypt*.tar.gz

# Android
IDE/Android/.idea/deploymentTargetDropDown.xml
IDE/Android/.idea/vcs.xml
IDE/Android/app/.cxx/
IDE/Android/app/src/main/cpp/wolfssl

Expand Down
43 changes: 36 additions & 7 deletions IDE/Android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,34 @@ del wolfssl
mklink /D wolfssl ..\..\..\..\..\..\..\src\java\com\wolfssl\
```

## 3. Push Certificate and KeyStore Files to Android Device
## 3. Convert JKS KeyStore Files to BKS for Android Use

Android does not support JKS format KeyStores. Several JUnit tests
require BKS format KeyStore files which must be converted from the existing
JKS files.

To convert, you will need to download a Bouncy Castle provider JAR from the
[Bouncy Castle website](https://www.bouncycastle.org/download/bouncy-castle-java/).
Then run the conversion script from the `examples/certs` directory:

```
cd examples/certs
./convert-to-bks.sh <path/to/bcprov.jar>
```

For example, when using `bcprov-jdk18on-1.78.1.jar`:

```
cd examples/certs
./convert-to-bks.sh ~/Downloads/bcprov-jdk18on-1.78.1.jar
```

This will create the following BKS files needed by the Android tests:

- `ca-server-rsa-2048.bks`
- `ca-server-ecc-256.bks`

## 4. Push Certificate and KeyStore Files to Android Device

Several JUnit tests require access to certificate and KeyStore files. These
files are located in the `examples/certs` directory and must be pushed to
Expand All @@ -92,18 +119,20 @@ adb shell mkdir -p /data/local/tmp/examples/certs/crl
adb push ./examples/certs/ /data/local/tmp/examples/
```

This will push all certificate files, KeyStore files (.jks, .wks, .p12),
and subdirectories (intermediate, rsapss, crl) needed by the JUnit tests.
This will push all certificate files, KeyStore files (.jks, .wks, .bks,
.p12), and subdirectories (intermediate, rsapss, crl) needed by the JUnit
tests.

If this step is skipped, tests in the following classes will be skipped due
to missing certificate files:
If step 3 (BKS conversion) or this step is skipped, tests in the following
classes will be skipped due to missing files:

- `WolfSSLKeyStoreTest`
- `WolfCryptPKIXCertPathBuilderTest`
- `WolfCryptPKIXCertPathValidatorTest`
- `WolfCryptPKIXRevocationCheckerTest`
- `WolfSSLKeyStoreTest`
- `WolfSSLCertManagerOCSPTest`

## 4. Import and Build the Example Project with Android Studio
## 5. Import and Build the Example Project with Android Studio

1) Open the Android Studio project by double clicking on the `Android` folder
in wolfcrypt-jni/IDE/. Or, from inside Android Studio, open the `Android`
Expand Down
10 changes: 5 additions & 5 deletions IDE/Android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ android {
targetSdkVersion 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments notClass: 'com.wolfssl.wolfcrypt.test.WolfCryptTestSuite,com.wolfssl.provider.jce.test.WolfJCETestSuite,com.wolfssl.wolfcrypt.test.fips.WolfCryptFipsTestSuite'
externalNativeBuild {
cmake {
Expand Down Expand Up @@ -42,10 +42,10 @@ android {

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:2.0.4'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'junit:junit:4.13.2'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

package com.example.wolfssl;

import android.support.v7.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
Expand Down
4 changes: 2 additions & 2 deletions IDE/Android/app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
Expand Down Expand Up @@ -31,4 +31,4 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.067" />

</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
4 changes: 2 additions & 2 deletions IDE/Android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
buildscript {
repositories {
google()
jcenter()
mavenCentral()

}
dependencies {
Expand All @@ -17,7 +17,7 @@ buildscript {
allprojects {
repositories {
google()
jcenter()
mavenCentral()
}
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
Expand Down
1 change: 1 addition & 0 deletions IDE/Android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
android.useAndroidX=true
android.nonFinalResIds=false
android.nonTransitiveRClass=false
org.gradle.jvmargs=-Xmx1536m
Expand Down
Loading
Loading