Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c52e8395 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Update libraries, Kotlin tests, minor refactoring

parent 0d12bf44
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line

buildscript {
    ext.kotlin_version = '1.2.21'
    ext.kotlin_version = '1.2.30'
    ext.dokka_version = '0.9.15'

    repositories {
@@ -46,8 +46,8 @@ android {
dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

    compile 'com.android.support:appcompat-v7:27.0.2'
    compile 'com.android.support:cardview-v7:27.0.2'
    compile 'com.android.support:appcompat-v7:27.1.0'
    compile 'com.android.support:cardview-v7:27.1.0'

    androidTestCompile 'com.android.support.test:runner:1.0.1'
    androidTestCompile 'com.android.support.test:rules:1.0.1'
+0 −160
Original line number Diff line number Diff line
/*
 * Copyright © Ricki Hirner (bitfire web engineering).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 */

package at.bitfire.cert4android;

import android.content.Intent;
import android.os.IBinder;
import android.os.Messenger;
import android.support.test.rule.ServiceTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.IOException;
import java.net.URL;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeoutException;

import javax.net.ssl.HttpsURLConnection;

import static android.support.test.InstrumentationRegistry.getContext;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static org.junit.Assert.assertNotNull;

@RunWith(AndroidJUnit4.class)
public class CustomCertManagerTest {

    CustomCertManager certManager, paranoidCertManager;
    static {
        CustomCertManager.SERVICE_TIMEOUT = 1000;
    }

    @Rule
    public ServiceTestRule serviceTestRule = new ServiceTestRule();

    Messenger service;

    private static X509Certificate[] siteCerts;
    static {
        try {
            siteCerts = getSiteCertificates(new URL("https://www.davdroid.com"));
        } catch(IOException ignored) {
        }
        assertNotNull(siteCerts);
    }


    @Before
    public void initCertManager() throws TimeoutException, InterruptedException {
        // prepare a bound and ready service for testing
        // loop required because of https://code.google.com/p/android/issues/detail?id=180396
        IBinder binder = bindService(CustomCertService.class);
        assertNotNull(binder);

        CustomCertManager.resetCertificates(getContext());

        certManager = new CustomCertManager(getContext(), false);
        assertNotNull(certManager);

        paranoidCertManager = new CustomCertManager(getContext(), false, false);
        assertNotNull(paranoidCertManager);
    }

    @After
    public void closeCertManager() {
        paranoidCertManager.close();
        certManager.close();
    }


    @Test(expected = CertificateException.class)
    public void testCheckClientCertificate() throws CertificateException {
        certManager.checkClientTrusted(null, null);
    }

    @Test
    public void testTrustedCertificate() throws CertificateException, TimeoutException {
        certManager.checkServerTrusted(siteCerts, "RSA");
    }

    @Test(expected = CertificateException.class)
    public void testParanoidCertificate() throws CertificateException {
        paranoidCertManager.checkServerTrusted(siteCerts, "RSA");
    }

    @Test
    public void testAddCustomCertificate() throws CertificateException, TimeoutException, InterruptedException {
        addCustomCertificate();
        paranoidCertManager.checkServerTrusted(siteCerts, "RSA");
    }

    // fails randomly for unknown reason:
    @Test(expected = CertificateException.class)
    public void testRemoveCustomCertificate() throws CertificateException, TimeoutException, InterruptedException {
        addCustomCertificate();

        // remove certificate and check again
        // should now be rejected for the whole session, i.e. no timeout anymore
        Intent intent = new Intent(getContext(), CustomCertService.class);
        intent.setAction(CustomCertService.CMD_CERTIFICATION_DECISION);
        intent.putExtra(CustomCertService.EXTRA_CERTIFICATE, siteCerts[0].getEncoded());
        intent.putExtra(CustomCertService.EXTRA_TRUSTED, false);
        startService(intent, CustomCertService.class);
        paranoidCertManager.checkServerTrusted(siteCerts, "RSA");
    }

    private void addCustomCertificate() throws CertificateException, TimeoutException, InterruptedException {
        // add certificate and check again
        Intent intent = new Intent(getContext(), CustomCertService.class);
        intent.setAction(CustomCertService.CMD_CERTIFICATION_DECISION);
        intent.putExtra(CustomCertService.EXTRA_CERTIFICATE, siteCerts[0].getEncoded());
        intent.putExtra(CustomCertService.EXTRA_TRUSTED, true);
        startService(intent, CustomCertService.class);
    }


    private IBinder bindService(Class clazz) throws TimeoutException, InterruptedException {
        IBinder binder = null;
        int it = 0;
        while ((binder = serviceTestRule.bindService(new Intent(getTargetContext(), clazz))) == null && it++ <100) {
            System.err.println("Waiting for ServiceTestRule.bindService");
            Thread.sleep(50);
        }
        if (binder == null)
            throw new IllegalStateException("Couldn't bind to service");
        return binder;
    }

    private void startService(Intent intent, Class clazz) throws TimeoutException, InterruptedException {
        serviceTestRule.startService(intent);
        bindService(clazz);
    }

    private static X509Certificate[] getSiteCertificates(URL url) throws IOException {
        HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
        try {
            conn.getInputStream().read();

            Certificate[] certs = conn.getServerCertificates();
            X509Certificate[] x509 = new X509Certificate[certs.length];
            System.arraycopy(certs, 0, x509, 0, certs.length);
            return x509;

        } finally {
            conn.disconnect();
        }
    }

}
+153 −0
Original line number Diff line number Diff line
/*
 * Copyright © Ricki Hirner (bitfire web engineering).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 */

package at.bitfire.cert4android

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.support.test.InstrumentationRegistry.getContext
import android.support.test.InstrumentationRegistry.getTargetContext
import android.support.test.rule.ServiceTestRule
import org.junit.After
import org.junit.Assert.assertNotNull
import org.junit.Assume.assumeNotNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.io.IOException
import java.net.URL
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
import javax.net.ssl.HttpsURLConnection

class CustomCertManagerTest {

    companion object {
        private fun getSiteCertificates(url: URL): List<X509Certificate> {
            val conn = url.openConnection() as HttpsURLConnection
            try {
                conn.inputStream.read()
                val certs = mutableListOf<X509Certificate>()
                conn.serverCertificates.forEach { certs += it as X509Certificate }
                return certs
            } finally {
                conn.disconnect()
            }
        }
    }

    lateinit var certManager: CustomCertManager
    lateinit var paranoidCertManager: CustomCertManager

    init {
        CustomCertManager.SERVICE_TIMEOUT = 1000
    }

    @JvmField
    @Rule
    val serviceTestRule = ServiceTestRule()

    var siteCerts: List<X509Certificate>? = null
    init {
        try {
            siteCerts = getSiteCertificates(URL("https://www.davdroid.com"))
        } catch(e: IOException) {
        }
        assumeNotNull(siteCerts)
    }


    @Before
    fun initCertManager() {
        // prepare a bound and ready service for testing
        // loop required because of https://code.google.com/p/android/issues/detail?id=180396
        val binder = bindService(CustomCertService::class.java)
        assertNotNull(binder)

        CustomCertManager.resetCertificates(getContext())

        certManager = CustomCertManager(getContext(), false)
        assertNotNull(certManager)

        paranoidCertManager = CustomCertManager(getContext(), false, false)
        assertNotNull(paranoidCertManager)
    }

    @After
    fun closeCertManager() {
        paranoidCertManager.close()
        certManager.close()
    }


    @Test(expected = CertificateException::class)
    fun testCheckClientCertificate() {
        certManager.checkClientTrusted(null, null)
    }

    @Test
    fun testTrustedCertificate() {
        certManager.checkServerTrusted(siteCerts!!.toTypedArray(), "RSA")
    }

    @Test(expected = CertificateException::class)
    fun testParanoidCertificate() {
        paranoidCertManager.checkServerTrusted(siteCerts!!.toTypedArray(), "RSA")
    }

    @Test
    fun testAddCustomCertificate() {
        addCustomCertificate()
        paranoidCertManager.checkServerTrusted(siteCerts!!.toTypedArray(), "RSA")
    }

    // fails randomly for unknown reason:
    @Test(expected = CertificateException::class)
    fun testRemoveCustomCertificate() {
        addCustomCertificate()

        // remove certificate and check again
        // should now be rejected for the whole session, i.e. no timeout anymore
        val intent = Intent(getContext(), CustomCertService::class.java)
        intent.action = CustomCertService.CMD_CERTIFICATION_DECISION
        intent.putExtra(CustomCertService.EXTRA_CERTIFICATE, siteCerts!!.first().encoded)
        intent.putExtra(CustomCertService.EXTRA_TRUSTED, false)
        startService(intent, CustomCertService::class.java)
        paranoidCertManager.checkServerTrusted(siteCerts!!.toTypedArray(), "RSA")
    }

    private fun addCustomCertificate() {
        // add certificate and check again
        val intent = Intent(getContext(), CustomCertService::class.java)
        intent.action = CustomCertService.CMD_CERTIFICATION_DECISION
        intent.putExtra(CustomCertService.EXTRA_CERTIFICATE, siteCerts!!.first().encoded)
        intent.putExtra(CustomCertService.EXTRA_TRUSTED, true)
        startService(intent, CustomCertService::class.java)
    }


    private fun bindService(clazz: Class<out Service>): IBinder {
        var binder = serviceTestRule.bindService(Intent(getTargetContext(), clazz))
        var it = 0
        while (binder == null && it++ <100) {
            binder = serviceTestRule.bindService(Intent(getTargetContext(), clazz))
            System.err.println("Waiting for ServiceTestRule.bindService")
            Thread.sleep(50)
        }
        if (binder == null)
            throw IllegalStateException("Couldn't bind to service")
        return binder
    }

    private fun startService(intent: Intent, clazz: Class<out Service>) {
        serviceTestRule.startService(intent)
        bindService(clazz)
    }

}
+0 −2
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@ import javax.net.ssl.X509TrustManager

object CertUtils {

    @JvmStatic
    fun getTrustManager(keyStore: KeyStore?): X509TrustManager? {
        try {
            val tmf = TrustManagerFactory.getInstance("X509")
@@ -31,7 +30,6 @@ object CertUtils {
        return null
    }

    @JvmStatic
    fun getTag(cert: X509Certificate): String {
        val str = StringBuilder()
        for (b in cert.signature)
+2 −4
Original line number Diff line number Diff line
@@ -14,9 +14,8 @@ import java.util.logging.Logger

object Constants {

    val TAG = "cert4android"
    const val TAG = "cert4android"

    @JvmField
    var log: Logger = Logger.getLogger(TAG)
    init {
        log.level = if (Log.isLoggable(TAG, Log.VERBOSE))
@@ -25,7 +24,6 @@ object Constants {
            Level.INFO
    }

    @JvmField
    val NOTIFICATION_CERT_DECISION = 88809
    const val NOTIFICATION_CERT_DECISION = 88809

}
Loading