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
113 changes: 113 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: CI

on:
push:
branches:
- master
pull_request:
branches:
- master

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Format, lint, and build
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up JDK 17
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6

- name: Check formatting
run: ./gradlew spotlessCheck

- name: Run Android lint
run: ./gradlew lint

- name: Build TrustKit library
run: ./gradlew :trustkit:assembleRelease

- name: Build Java demo app
run: ./gradlew :app:assembleDebug

- name: Build Kotlin demo app
run: ./gradlew :demoappkotlin:assembleDebug

- name: Upload build reports
if: failure()
uses: actions/upload-artifact@v7
with:
name: build-reports
path: |
**/build/reports/**
**/build/outputs/logs/**
if-no-files-found: ignore
retention-days: 7

instrumentation-tests:
name: Instrumentation tests (API ${{ matrix.api-level }})
needs: build
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
api-level:
- 27
- 36

steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Set up JDK 17
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6

- 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

- name: Run instrumentation tests
uses: reactivecircus/android-emulator-runner@v2.37.0
with:
api-level: ${{ matrix.api-level }}
arch: x86_64
target: default
script: ./gradlew :trustkit:connectedDebugAndroidTest

- name: Upload instrumentation test reports
if: failure()
uses: actions/upload-artifact@v7
with:
name: instrumentation-reports-api-${{ matrix.api-level }}
path: |
trustkit/build/reports/androidTests/**
trustkit/build/outputs/androidTest-results/**
trustkit/build/outputs/managed_device_android_test_additional_output/**
if-no-files-found: ignore
retention-days: 7
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.HashSet;
import java.util.List;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import org.junit.Before;
import org.junit.BeforeClass;
Expand Down Expand Up @@ -113,6 +114,22 @@ public void setUp() {
TestableTrustKit.reset();
}

private static Socket createSocketAndStartHandshake(
SSLSocketFactory socketFactory, String serverHostname) throws IOException {
SSLSocket socket = (SSLSocket) socketFactory.createSocket(serverHostname, 443);
try {
socket.startHandshake();
return socket;
} catch (IOException handshakeException) {
try {
socket.close();
} catch (IOException closeException) {
handshakeException.addSuppressed(closeException);
}
throw handshakeException;
}
}

// region Tests for when the domain is pinned
@Test
public void testPinnedDomainExpiredChain() throws IOException {
Expand All @@ -125,7 +142,7 @@ public void testPinnedDomainExpiredChain() throws IOException {
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
boolean didReceiveHandshakeError = false;
try {
test.createSocket(serverHostname, 443).getInputStream();
createSocketAndStartHandshake(test, serverHostname).close();
} catch (SSLHandshakeException e) {
if ((e.getCause() instanceof CertificateException
&& !(e.getCause().getMessage().startsWith("Pin verification failed")))) {
Expand Down Expand Up @@ -164,7 +181,7 @@ public void testPinnedDomainWrongHostnameChain() throws IOException {
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
boolean didReceiveHandshakeError = false;
try {
test.createSocket(serverHostname, 443).getInputStream();
createSocketAndStartHandshake(test, serverHostname).close();
} catch (SSLHandshakeException e) {
if ((e.getCause() instanceof CertificateException
&& !(e.getCause().getMessage().startsWith("Pin verification failed")))) {
Expand Down Expand Up @@ -200,8 +217,7 @@ public void testPinnedDomainSuccessAnchor() throws IOException {

// Create a TrustKit SocketFactory and ensure the connection succeeds
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
Socket socket = test.createSocket(serverHostname, 443);
socket.getInputStream();
Socket socket = createSocketAndStartHandshake(test, serverHostname);

assertTrue(socket.isConnected());
socket.close();
Expand All @@ -228,8 +244,7 @@ public void testPinnedDomainSuccessLeaf() throws IOException {

// Create a TrustKit SocketFactory and ensure the connection succeeds
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
Socket socket = test.createSocket(serverHostname, 443);
socket.getInputStream();
Socket socket = createSocketAndStartHandshake(test, serverHostname);

assertTrue(socket.isConnected());
socket.close();
Expand Down Expand Up @@ -263,7 +278,7 @@ public void testPinnedDomainInvalidPin() throws IOException {
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
boolean didReceivePinningError = false;
try {
test.createSocket(serverHostname, 443).getInputStream();
createSocketAndStartHandshake(test, serverHostname).close();
} catch (SSLHandshakeException e) {
if ((e.getCause() instanceof CertificateException
&& (e.getCause().getMessage().startsWith("Pin verification failed")))) {
Expand Down Expand Up @@ -294,8 +309,7 @@ public void testPinnedDomainInvalidPinAndPinningNotEnforced() throws IOException

// Create a TrustKit SocketFactory and ensure the connection succeeds
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
Socket socket = test.createSocket(serverHostname, 443);
socket.getInputStream();
Socket socket = createSocketAndStartHandshake(test, serverHostname);

assertTrue(socket.isConnected());
socket.close();
Expand Down Expand Up @@ -327,8 +341,7 @@ public void testPinnedDomainInvalidPinAndPolicyExpired() throws IOException {

// Create a TrustKit SocketFactory and ensure the connection succeeds
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
Socket socket = test.createSocket(serverHostname, 443);
socket.getInputStream();
Socket socket = createSocketAndStartHandshake(test, serverHostname);

assertTrue(socket.isConnected());
socket.close();
Expand All @@ -354,7 +367,7 @@ public void testPinnedDomainUntrustedChainAndPinningNotEnforced() throws IOExcep
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
boolean didReceiveHandshakeError = false;
try {
test.createSocket(serverHostname, 443).getInputStream();
createSocketAndStartHandshake(test, serverHostname).close();
} catch (SSLHandshakeException e) {
if ((e.getCause() instanceof CertificateException
&& !(e.getCause().getMessage().startsWith("Pin verification failed")))) {
Expand Down Expand Up @@ -434,8 +447,7 @@ public void testDebugOverridesInvalidPinButOverridePins()
// This means that debug-overrides properly enables the supplied debug CA cert and
// disables pinning when overridePins is true
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
Socket socket = test.createSocket(serverHostname, 443);
socket.getInputStream();
Socket socket = createSocketAndStartHandshake(test, serverHostname);

assertTrue(socket.isConnected());
socket.close();
Expand Down Expand Up @@ -505,7 +517,7 @@ public void testDebugOverridesButAppNotDebuggable() throws IOException, Certific
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
boolean didReceiveHandshakeError = false;
try {
test.createSocket(serverHostname, 443).getInputStream();
createSocketAndStartHandshake(test, serverHostname).close();
} catch (SSLHandshakeException e) {
didReceiveHandshakeError = true;
}
Expand Down Expand Up @@ -575,7 +587,7 @@ public void testDebugOverridesInvalidPin() throws IOException, CertificateExcept
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
boolean didReceivePinningError = false;
try {
test.createSocket(serverHostname, 443).getInputStream();
createSocketAndStartHandshake(test, serverHostname).close();
} catch (SSLHandshakeException e) {
if ((e.getCause() instanceof CertificateException
&& (e.getCause().getMessage().startsWith("Pin verification failed")))) {
Expand Down Expand Up @@ -631,7 +643,7 @@ public void testNonPinnedDomainUntrustedRootChain() throws IOException {
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
boolean didReceiveHandshakeError = false;
try {
test.createSocket(serverHostname, 443).getInputStream();
createSocketAndStartHandshake(test, serverHostname).close();
} catch (SSLHandshakeException e) {
if ((e.getCause() instanceof CertificateException
&& !(e.getCause().getMessage().startsWith("Pin verification failed")))) {
Expand Down Expand Up @@ -663,8 +675,7 @@ public void testNonPinnedDomainSuccess() throws IOException {

// Create a TrustKit SocketFactory and ensure the connection succeeds
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
Socket socket = test.createSocket(serverHostname, 443);
socket.getInputStream();
Socket socket = createSocketAndStartHandshake(test, serverHostname);

assertTrue(socket.isConnected());
socket.close();
Expand Down Expand Up @@ -728,8 +739,7 @@ public void testDebugOverrides() throws IOException, CertificateException {
// Create a TrustKit SocketFactory and ensure the connection succeeds
// This means that debug-overrides properly enables the supplied debug CA cert
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
Socket socket = test.createSocket(serverHostname, 443);
socket.getInputStream();
Socket socket = createSocketAndStartHandshake(test, serverHostname);

assertTrue(socket.isConnected());
socket.close();
Expand Down Expand Up @@ -789,8 +799,7 @@ public void testDebugOverridesSystemCa() throws IOException, CertificateExceptio
// Create a TrustKit SocketFactory and ensure the connection succeeds
// This means that debug-overrides does not disable the System CAs
SSLSocketFactory test = TestableTrustKit.getInstance().getSSLSocketFactory(serverHostname);
Socket socket = test.createSocket(serverHostname, 443);
socket.getInputStream();
Socket socket = createSocketAndStartHandshake(test, serverHostname);

assertTrue(socket.isConnected());
socket.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public void testExecuteSucceedHttp() throws MalformedURLException {

// Run the task synchronously and ensure it succeeded
Integer lastResponseCode = testTask.doInBackground(taskParameters.toArray());
assertEquals(Integer.valueOf(302), lastResponseCode);
assertEquals(Integer.valueOf(200), lastResponseCode);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<pin-set>
<!-- Pin the CA/Anchor key -->
<pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin>
<pin digest="SHA-256">F6jTih9VkkYZS8yuYqeU/4DUGehJ+niBGkkQ1yg8H3U=</pin>
<pin digest="SHA-256">u91x/rv9ivMrXO0du6N2ABI6BiiVfD0T99DTEzoqSv4=</pin>
</pin-set>
<trustkit-config enforcePinning="true">
</trustkit-config>
Expand Down