/* * Copyright 2015 Vineet Garg <grg.vineet@gmail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kde.kdeconnect; import android.content.Context; import android.content.SharedPreferences; import android.test.AndroidTestCase; import android.util.Base64; import android.util.Log; import org.kde.kdeconnect.Backends.BasePairingHandler; import org.kde.kdeconnect.Backends.LanBackend.LanLink; import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; import org.kde.kdeconnect.Backends.LanBackend.LanPairingHandler; import org.mockito.Mockito; import org.spongycastle.asn1.x500.X500NameBuilder; import org.spongycastle.asn1.x500.style.BCStyle; import org.spongycastle.cert.X509v3CertificateBuilder; import org.spongycastle.cert.jcajce.JcaX509CertificateConverter; import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.operator.ContentSigner; import org.spongycastle.operator.jcajce.JcaContentSignerBuilder; import java.lang.reflect.Method; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.cert.X509Certificate; import java.util.Date; public class DeviceTest extends AndroidTestCase { // Creating a paired device before each test case @Override protected void setUp() throws Exception { super.setUp(); // Dexmaker has problems guessing cache directory, setting manually System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath()); // Save new test device in settings Context context = getContext(); String deviceId = "testDevice"; String name = "Test Device"; KeyPair keyPair; try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); keyPair = keyGen.genKeyPair(); } catch(Exception e) { e.printStackTrace(); Log.e("KDE/initializeRsaKeys", "Exception"); return; } SharedPreferences settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); //Store device information needed to create a Device object in a future SharedPreferences.Editor editor = settings.edit(); editor.putString("deviceName", name); editor.putString("deviceType", Device.DeviceType.Phone.toString()); editor.putString("publicKey", Base64.encodeToString(keyPair.getPublic().getEncoded(), 0).trim() + "\n"); editor.apply(); SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); preferences.edit().putBoolean(deviceId,true).apply(); } // Removing paired device info after each test case @Override protected void tearDown() throws Exception { super.tearDown(); // Remove saved test device Context context = getContext(); String deviceId = "testDevice"; SharedPreferences settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); //Store device information needed to create a Device object in a future SharedPreferences.Editor editor = settings.edit(); editor.clear(); editor.apply(); SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); preferences.edit().remove(deviceId).apply(); } // Basic paired device testing public void testDevice(){ Device device = new Device(getContext(), "testDevice"); assertEquals(device.getDeviceId(), "testDevice"); assertEquals(device.getDeviceType(), Device.DeviceType.Phone); assertEquals(device.getName(), "Test Device"); assertEquals(device.isPaired(), true); } // Testing pairing done using reflection since it is private // Created an unpaired device inside this test public void testPairingDone(){ NetworkPackage fakeNetworkPackage = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY); fakeNetworkPackage.set("deviceId", "unpairedTestDevice"); fakeNetworkPackage.set("deviceName", "Unpaired Test Device"); fakeNetworkPackage.set("protocolVersion", NetworkPackage.ProtocolVersion); fakeNetworkPackage.set("deviceType", Device.DeviceType.Phone.toString()); LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class); Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); LanLink link = Mockito.mock(LanLink.class); Mockito.when(link.getLinkProvider()).thenReturn(linkProvider); Mockito.when(link.getPairingHandler(Mockito.any(Device.class), Mockito.any(BasePairingHandler.PairingHandlerCallback.class))).thenReturn(Mockito.mock(LanPairingHandler.class)); Device device = new Device(getContext(), fakeNetworkPackage, link); KeyPair keyPair; try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); keyPair = keyGen.genKeyPair(); } catch(Exception e) { e.printStackTrace(); Log.e("KDE/initializeRsaKeys", "Exception"); return; } device.publicKey = keyPair.getPublic(); assertNotNull(device); assertEquals(device.getDeviceId(), "unpairedTestDevice"); assertEquals(device.getName(), "Unpaired Test Device"); assertEquals(device.getDeviceType(), Device.DeviceType.Phone); assertNotNull(device.publicKey); assertNull(device.certificate); Method method; try { method = Device.class.getDeclaredMethod("pairingDone"); method.setAccessible(true); method.invoke(device); }catch (Exception e){ e.printStackTrace(); } assertEquals(device.isPaired(), true); SharedPreferences preferences = getContext().getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); assertEquals(preferences.getBoolean(device.getDeviceId(), false), true); SharedPreferences settings = getContext().getSharedPreferences(device.getDeviceId(),Context.MODE_PRIVATE); assertEquals(settings.getString("deviceName", "Unknown device"), "Unpaired Test Device"); assertEquals(settings.getString("deviceType", "tablet"), "phone"); // Cleanup for unpaired test device preferences.edit().remove(device.getDeviceId()).apply(); settings.edit().clear().apply(); } public void testPairingDoneWithCertificate() throws Exception{ KeyPair keyPair = null; try { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); keyPair = keyGen.genKeyPair(); } catch(Exception e) { e.printStackTrace(); Log.e("KDE/initializeRsaKeys", "Exception"); } X509Certificate certificate = null; try { BouncyCastleProvider BC = new BouncyCastleProvider(); X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); nameBuilder.addRDN(BCStyle.CN, "testDevice"); nameBuilder.addRDN(BCStyle.OU, "KDE Connect"); nameBuilder.addRDN(BCStyle.O, "KDE"); Date notBefore = new Date(System.currentTimeMillis()); Date notAfter = new Date(System.currentTimeMillis() + System.currentTimeMillis()); X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder( nameBuilder.build(), BigInteger.ONE, notBefore, notAfter, nameBuilder.build(), keyPair.getPublic() ); ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(keyPair.getPrivate()); certificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateBuilder.build(contentSigner)); } catch(Exception e) { e.printStackTrace(); Log.e("KDE/initialiseCert", "Exception"); } NetworkPackage fakeNetworkPackage = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY); fakeNetworkPackage.set("deviceId", "unpairedTestDevice"); fakeNetworkPackage.set("deviceName", "Unpaired Test Device"); fakeNetworkPackage.set("protocolVersion", NetworkPackage.ProtocolVersion); fakeNetworkPackage.set("deviceType", Device.DeviceType.Phone.toString()); fakeNetworkPackage.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0)); LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class); Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); LanLink link = Mockito.mock(LanLink.class); Mockito.when(link.getPairingHandler(Mockito.any(Device.class), Mockito.any(BasePairingHandler.PairingHandlerCallback.class))).thenReturn(Mockito.mock(LanPairingHandler.class)); Mockito.when(link.getLinkProvider()).thenReturn(linkProvider); Device device = new Device(getContext(), fakeNetworkPackage, link); device.publicKey = keyPair.getPublic(); assertNotNull(device); assertEquals(device.getDeviceId(), "unpairedTestDevice"); assertEquals(device.getName(), "Unpaired Test Device"); assertEquals(device.getDeviceType(), Device.DeviceType.Phone); assertNotNull(device.publicKey); assertNotNull(device.certificate); Method method; try { method = Device.class.getDeclaredMethod("pairingDone"); method.setAccessible(true); method.invoke(device); }catch (Exception e){ e.printStackTrace(); } assertEquals(device.isPaired(), true); SharedPreferences preferences = getContext().getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); assertEquals(preferences.getBoolean(device.getDeviceId(), false), true); SharedPreferences settings = getContext().getSharedPreferences(device.getDeviceId(),Context.MODE_PRIVATE); assertEquals(settings.getString("deviceName", "Unknown device"), "Unpaired Test Device"); assertEquals(settings.getString("deviceType", "tablet"), "phone"); // Cleanup for unpaired test device preferences.edit().remove(device.getDeviceId()).apply(); settings.edit().clear().apply(); } public void testUnpair(){ Device device = new Device(getContext(), "testDevice"); device.unpair(); assertEquals(device.isPaired(), false); SharedPreferences preferences = getContext().getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); assertEquals(preferences.getBoolean(device.getDeviceId(), false), false); } }