diff --git a/app/src/main/java/com/limelight/AppView.java b/app/src/main/java/com/limelight/AppView.java index 8f993069..ba5a2787 100644 --- a/app/src/main/java/com/limelight/AppView.java +++ b/app/src/main/java/com/limelight/AppView.java @@ -390,8 +390,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks { @Override public void run() { suspendGridUpdates = true; - ServerHelper.doQuit(AppView.this, - ServerHelper.getCurrentAddressFromComputer(computer), + ServerHelper.doQuit(AppView.this, computer, app.app, managerBinder, new Runnable() { @Override public void run() { diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index e0f3a62c..7139427c 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -68,6 +68,11 @@ import android.widget.FrameLayout; import android.view.inputmethod.InputMethodManager; import android.widget.Toast; +import java.io.ByteArrayInputStream; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + public class Game extends Activity implements SurfaceHolder.Callback, OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener, @@ -135,6 +140,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, public static final String EXTRA_PC_UUID = "UUID"; public static final String EXTRA_PC_NAME = "PcName"; public static final String EXTRA_APP_HDR = "HDR"; + public static final String EXTRA_SERVER_CERT = "ServerCert"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -228,6 +234,17 @@ public class Game extends Activity implements SurfaceHolder.Callback, String uuid = Game.this.getIntent().getStringExtra(EXTRA_PC_UUID); String pcName = Game.this.getIntent().getStringExtra(EXTRA_PC_NAME); boolean willStreamHdr = Game.this.getIntent().getBooleanExtra(EXTRA_APP_HDR, false); + byte[] derCertData = Game.this.getIntent().getByteArrayExtra(EXTRA_SERVER_CERT); + + X509Certificate serverCert = null; + try { + if (derCertData != null) { + serverCert = (X509Certificate) CertificateFactory.getInstance("X.509") + .generateCertificate(new ByteArrayInputStream(derCertData)); + } + } catch (CertificateException e) { + e.printStackTrace(); + } if (appId == StreamConfiguration.INVALID_APP_ID) { finish(); @@ -386,7 +403,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, .build(); // Initialize the connection - conn = new NvConnection(host, uniqueId, config, PlatformBinding.getCryptoProvider(this)); + conn = new NvConnection(host, uniqueId, config, PlatformBinding.getCryptoProvider(this), serverCert); controllerHandler = new ControllerHandler(this, conn, this, prefConfig); InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE); diff --git a/app/src/main/java/com/limelight/PcView.java b/app/src/main/java/com/limelight/PcView.java index b7e073c2..13c4b06e 100644 --- a/app/src/main/java/com/limelight/PcView.java +++ b/app/src/main/java/com/limelight/PcView.java @@ -373,9 +373,9 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), managerBinder.getUniqueId(), - PlatformBinding.getDeviceName(), + computer.serverCert, PlatformBinding.getCryptoProvider(PcView.this)); - if (httpConn.getPairState() == PairingManager.PairState.PAIRED) { + if (httpConn.getPairState() == PairState.PAIRED) { // Don't display any toast, but open the app list message = null; success = true; @@ -387,21 +387,26 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { Dialog.displayDialog(PcView.this, getResources().getString(R.string.pair_pairing_title), getResources().getString(R.string.pair_pairing_msg)+" "+pinStr, false); - PairingManager.PairState pairState = httpConn.pair(httpConn.getServerInfo(), pinStr); - if (pairState == PairingManager.PairState.PIN_WRONG) { + PairingManager pm = httpConn.getPairingManager(); + + PairState pairState = pm.pair(httpConn.getServerInfo(), pinStr); + if (pairState == PairState.PIN_WRONG) { message = getResources().getString(R.string.pair_incorrect_pin); } - else if (pairState == PairingManager.PairState.FAILED) { + else if (pairState == PairState.FAILED) { message = getResources().getString(R.string.pair_fail); } - else if (pairState == PairingManager.PairState.ALREADY_IN_PROGRESS) { + else if (pairState == PairState.ALREADY_IN_PROGRESS) { message = getResources().getString(R.string.pair_already_in_progress); } - else if (pairState == PairingManager.PairState.PAIRED) { + else if (pairState == PairState.PAIRED) { // Just navigate to the app view without displaying a toast message = null; success = true; + // Pin this certificate for later HTTPS use + managerBinder.getComputer(computer.uuid).serverCert = pm.getPairedCert(); + // Invalidate reachability information after pairing to force // a refresh before reading pair state again managerBinder.invalidateStateForComputer(computer.uuid); @@ -498,7 +503,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { try { httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), managerBinder.getUniqueId(), - PlatformBinding.getDeviceName(), + computer.serverCert, PlatformBinding.getCryptoProvider(PcView.this)); if (httpConn.getPairState() == PairingManager.PairState.PAIRED) { httpConn.unpair(); @@ -605,8 +610,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks { UiHelper.displayQuitConfirmationDialog(this, new Runnable() { @Override public void run() { - ServerHelper.doQuit(PcView.this, - ServerHelper.getCurrentAddressFromComputer(computer.details), + ServerHelper.doQuit(PcView.this, computer.details, new NvApp("app", 0, false), managerBinder, null); } }, null); diff --git a/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java b/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java index 4c523ce4..8488ad0a 100644 --- a/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java +++ b/app/src/main/java/com/limelight/computers/ComputerDatabaseManager.java @@ -1,5 +1,11 @@ package com.limelight.computers; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -23,6 +29,7 @@ public class ComputerDatabaseManager { private static final String REMOTE_ADDRESS_COLUMN_NAME = "RemoteAddress"; private static final String MANUAL_ADDRESS_COLUMN_NAME = "ManualAddress"; private static final String MAC_ADDRESS_COLUMN_NAME = "MacAddress"; + private static final String SERVER_CERT_COLUMN_NAME = "ServerCert"; private SQLiteDatabase computerDb; @@ -43,13 +50,21 @@ public class ComputerDatabaseManager { } private void initializeDb(Context c) { + // Add cert column to the table if not present + try { + computerDb.execSQL(String.format((Locale)null, + "ALTER TABLE %s ADD COLUMN %s TEXT", + COMPUTER_TABLE_NAME, SERVER_CERT_COLUMN_NAME)); + } catch (SQLiteException e) {} + + // Create tables if they aren't already there computerDb.execSQL(String.format((Locale)null, - "CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY, %s TEXT NOT NULL, %s TEXT, %s TEXT, %s TEXT, %s TEXT)", + "CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY, %s TEXT NOT NULL, %s TEXT, %s TEXT, %s TEXT, %s TEXT, %s TEXT)", COMPUTER_TABLE_NAME, COMPUTER_UUID_COLUMN_NAME, COMPUTER_NAME_COLUMN_NAME, LOCAL_ADDRESS_COLUMN_NAME, REMOTE_ADDRESS_COLUMN_NAME, MANUAL_ADDRESS_COLUMN_NAME, - MAC_ADDRESS_COLUMN_NAME)); + MAC_ADDRESS_COLUMN_NAME, SERVER_CERT_COLUMN_NAME)); // Move all computers from the old DB (if any) to the new one List oldComputers = LegacyDatabaseReader.migrateAllComputers(c); @@ -70,6 +85,17 @@ public class ComputerDatabaseManager { values.put(REMOTE_ADDRESS_COLUMN_NAME, details.remoteAddress); values.put(MANUAL_ADDRESS_COLUMN_NAME, details.manualAddress); values.put(MAC_ADDRESS_COLUMN_NAME, details.macAddress); + try { + if (details.serverCert != null) { + values.put(SERVER_CERT_COLUMN_NAME, details.serverCert.getEncoded()); + } + else { + values.put(SERVER_CERT_COLUMN_NAME, (byte[])null); + } + } catch (CertificateEncodingException e) { + values.put(SERVER_CERT_COLUMN_NAME, (byte[])null); + e.printStackTrace(); + } return -1 != computerDb.insertWithOnConflict(COMPUTER_TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); } @@ -90,6 +116,17 @@ public class ComputerDatabaseManager { details.manualAddress = c.getString(4); details.macAddress = c.getString(5); + try { + byte[] derCertData = c.getBlob(6); + + if (derCertData != null) { + details.serverCert = (X509Certificate) CertificateFactory.getInstance("X.509") + .generateCertificate(new ByteArrayInputStream(derCertData)); + } + } catch (CertificateException e) { + e.printStackTrace(); + } + // This signifies we don't have dynamic state (like pair state) details.state = ComputerDetails.State.UNKNOWN; diff --git a/app/src/main/java/com/limelight/computers/ComputerManagerService.java b/app/src/main/java/com/limelight/computers/ComputerManagerService.java index 0b4a0794..237cf642 100644 --- a/app/src/main/java/com/limelight/computers/ComputerManagerService.java +++ b/app/src/main/java/com/limelight/computers/ComputerManagerService.java @@ -435,8 +435,8 @@ public class ComputerManagerService extends Service { } try { - NvHTTP http = new NvHTTP(address, idManager.getUniqueId(), - null, PlatformBinding.getCryptoProvider(ComputerManagerService.this)); + NvHTTP http = new NvHTTP(address, idManager.getUniqueId(), details.serverCert, + PlatformBinding.getCryptoProvider(ComputerManagerService.this)); ComputerDetails newDetails = http.getComputerDetails(); @@ -698,7 +698,7 @@ public class ComputerManagerService extends Service { try { NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), idManager.getUniqueId(), - null, PlatformBinding.getCryptoProvider(ComputerManagerService.this)); + computer.serverCert, PlatformBinding.getCryptoProvider(ComputerManagerService.this)); String appList; if (tuple != null) { diff --git a/app/src/main/java/com/limelight/grid/assets/NetworkAssetLoader.java b/app/src/main/java/com/limelight/grid/assets/NetworkAssetLoader.java index 9042d099..fa31f67c 100644 --- a/app/src/main/java/com/limelight/grid/assets/NetworkAssetLoader.java +++ b/app/src/main/java/com/limelight/grid/assets/NetworkAssetLoader.java @@ -22,7 +22,8 @@ public class NetworkAssetLoader { public InputStream getBitmapStream(CachedAppAssetLoader.LoaderTuple tuple) { InputStream in = null; try { - NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(tuple.computer), uniqueId, null, PlatformBinding.getCryptoProvider(context)); + NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(tuple.computer), uniqueId, + tuple.computer.serverCert, PlatformBinding.getCryptoProvider(context)); in = http.getBoxArt(tuple.app); } catch (IOException ignored) {} diff --git a/app/src/main/java/com/limelight/utils/ServerHelper.java b/app/src/main/java/com/limelight/utils/ServerHelper.java index 7f6c19d8..f1b976d8 100644 --- a/app/src/main/java/com/limelight/utils/ServerHelper.java +++ b/app/src/main/java/com/limelight/utils/ServerHelper.java @@ -15,6 +15,7 @@ import com.limelight.nvstream.http.NvHTTP; import java.io.FileNotFoundException; import java.net.UnknownHostException; +import java.security.cert.CertificateEncodingException; public class ServerHelper { public static String getCurrentAddressFromComputer(ComputerDetails computer) { @@ -31,6 +32,13 @@ public class ServerHelper { intent.putExtra(Game.EXTRA_UNIQUEID, managerBinder.getUniqueId()); intent.putExtra(Game.EXTRA_PC_UUID, computer.uuid.toString()); intent.putExtra(Game.EXTRA_PC_NAME, computer.name); + try { + if (computer.serverCert != null) { + intent.putExtra(Game.EXTRA_SERVER_CERT, computer.serverCert.getEncoded()); + } + } catch (CertificateEncodingException e) { + e.printStackTrace(); + } return intent; } @@ -45,7 +53,7 @@ public class ServerHelper { } public static void doQuit(final Activity parent, - final String address, + final ComputerDetails computer, final NvApp app, final ComputerManagerService.ComputerManagerBinder managerBinder, final Runnable onComplete) { @@ -56,8 +64,8 @@ public class ServerHelper { NvHTTP httpConn; String message; try { - httpConn = new NvHTTP(address, - managerBinder.getUniqueId(), null, PlatformBinding.getCryptoProvider(parent)); + httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), + managerBinder.getUniqueId(), computer.serverCert, PlatformBinding.getCryptoProvider(parent)); if (httpConn.quitApp()) { message = parent.getResources().getString(R.string.applist_quit_success) + " " + app.getAppName(); } else { diff --git a/moonlight-common b/moonlight-common index 1af5c24e..364f34a5 160000 --- a/moonlight-common +++ b/moonlight-common @@ -1 +1 @@ -Subproject commit 1af5c24e56e3d0b4cae477346306075129b8ddf8 +Subproject commit 364f34a5646dcfd89cf02270906b01892cd1f76e