mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-18 18:42:46 +00:00
Add cert pinning during pairing
This commit is contained in:
parent
16b845ab84
commit
db49077b9b
@ -390,8 +390,7 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
suspendGridUpdates = true;
|
suspendGridUpdates = true;
|
||||||
ServerHelper.doQuit(AppView.this,
|
ServerHelper.doQuit(AppView.this, computer,
|
||||||
ServerHelper.getCurrentAddressFromComputer(computer),
|
|
||||||
app.app, managerBinder, new Runnable() {
|
app.app, managerBinder, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -68,6 +68,11 @@ import android.widget.FrameLayout;
|
|||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.Toast;
|
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,
|
public class Game extends Activity implements SurfaceHolder.Callback,
|
||||||
OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener,
|
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_UUID = "UUID";
|
||||||
public static final String EXTRA_PC_NAME = "PcName";
|
public static final String EXTRA_PC_NAME = "PcName";
|
||||||
public static final String EXTRA_APP_HDR = "HDR";
|
public static final String EXTRA_APP_HDR = "HDR";
|
||||||
|
public static final String EXTRA_SERVER_CERT = "ServerCert";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
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 uuid = Game.this.getIntent().getStringExtra(EXTRA_PC_UUID);
|
||||||
String pcName = Game.this.getIntent().getStringExtra(EXTRA_PC_NAME);
|
String pcName = Game.this.getIntent().getStringExtra(EXTRA_PC_NAME);
|
||||||
boolean willStreamHdr = Game.this.getIntent().getBooleanExtra(EXTRA_APP_HDR, false);
|
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) {
|
if (appId == StreamConfiguration.INVALID_APP_ID) {
|
||||||
finish();
|
finish();
|
||||||
@ -386,7 +403,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Initialize the connection
|
// 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);
|
controllerHandler = new ControllerHandler(this, conn, this, prefConfig);
|
||||||
|
|
||||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||||
|
@ -373,9 +373,9 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
|
|
||||||
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
||||||
managerBinder.getUniqueId(),
|
managerBinder.getUniqueId(),
|
||||||
PlatformBinding.getDeviceName(),
|
computer.serverCert,
|
||||||
PlatformBinding.getCryptoProvider(PcView.this));
|
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
|
// Don't display any toast, but open the app list
|
||||||
message = null;
|
message = null;
|
||||||
success = true;
|
success = true;
|
||||||
@ -387,21 +387,26 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
Dialog.displayDialog(PcView.this, getResources().getString(R.string.pair_pairing_title),
|
Dialog.displayDialog(PcView.this, getResources().getString(R.string.pair_pairing_title),
|
||||||
getResources().getString(R.string.pair_pairing_msg)+" "+pinStr, false);
|
getResources().getString(R.string.pair_pairing_msg)+" "+pinStr, false);
|
||||||
|
|
||||||
PairingManager.PairState pairState = httpConn.pair(httpConn.getServerInfo(), pinStr);
|
PairingManager pm = httpConn.getPairingManager();
|
||||||
if (pairState == PairingManager.PairState.PIN_WRONG) {
|
|
||||||
|
PairState pairState = pm.pair(httpConn.getServerInfo(), pinStr);
|
||||||
|
if (pairState == PairState.PIN_WRONG) {
|
||||||
message = getResources().getString(R.string.pair_incorrect_pin);
|
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);
|
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);
|
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
|
// Just navigate to the app view without displaying a toast
|
||||||
message = null;
|
message = null;
|
||||||
success = true;
|
success = true;
|
||||||
|
|
||||||
|
// Pin this certificate for later HTTPS use
|
||||||
|
managerBinder.getComputer(computer.uuid).serverCert = pm.getPairedCert();
|
||||||
|
|
||||||
// Invalidate reachability information after pairing to force
|
// Invalidate reachability information after pairing to force
|
||||||
// a refresh before reading pair state again
|
// a refresh before reading pair state again
|
||||||
managerBinder.invalidateStateForComputer(computer.uuid);
|
managerBinder.invalidateStateForComputer(computer.uuid);
|
||||||
@ -498,7 +503,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
try {
|
try {
|
||||||
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
||||||
managerBinder.getUniqueId(),
|
managerBinder.getUniqueId(),
|
||||||
PlatformBinding.getDeviceName(),
|
computer.serverCert,
|
||||||
PlatformBinding.getCryptoProvider(PcView.this));
|
PlatformBinding.getCryptoProvider(PcView.this));
|
||||||
if (httpConn.getPairState() == PairingManager.PairState.PAIRED) {
|
if (httpConn.getPairState() == PairingManager.PairState.PAIRED) {
|
||||||
httpConn.unpair();
|
httpConn.unpair();
|
||||||
@ -605,8 +610,7 @@ public class PcView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
UiHelper.displayQuitConfirmationDialog(this, new Runnable() {
|
UiHelper.displayQuitConfirmationDialog(this, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
ServerHelper.doQuit(PcView.this,
|
ServerHelper.doQuit(PcView.this, computer.details,
|
||||||
ServerHelper.getCurrentAddressFromComputer(computer.details),
|
|
||||||
new NvApp("app", 0, false), managerBinder, null);
|
new NvApp("app", 0, false), managerBinder, null);
|
||||||
}
|
}
|
||||||
}, null);
|
}, null);
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
package com.limelight.computers;
|
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.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -23,6 +29,7 @@ public class ComputerDatabaseManager {
|
|||||||
private static final String REMOTE_ADDRESS_COLUMN_NAME = "RemoteAddress";
|
private static final String REMOTE_ADDRESS_COLUMN_NAME = "RemoteAddress";
|
||||||
private static final String MANUAL_ADDRESS_COLUMN_NAME = "ManualAddress";
|
private static final String MANUAL_ADDRESS_COLUMN_NAME = "ManualAddress";
|
||||||
private static final String MAC_ADDRESS_COLUMN_NAME = "MacAddress";
|
private static final String MAC_ADDRESS_COLUMN_NAME = "MacAddress";
|
||||||
|
private static final String SERVER_CERT_COLUMN_NAME = "ServerCert";
|
||||||
|
|
||||||
private SQLiteDatabase computerDb;
|
private SQLiteDatabase computerDb;
|
||||||
|
|
||||||
@ -43,13 +50,21 @@ public class ComputerDatabaseManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeDb(Context c) {
|
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
|
// Create tables if they aren't already there
|
||||||
computerDb.execSQL(String.format((Locale)null,
|
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_TABLE_NAME,
|
||||||
COMPUTER_UUID_COLUMN_NAME, COMPUTER_NAME_COLUMN_NAME,
|
COMPUTER_UUID_COLUMN_NAME, COMPUTER_NAME_COLUMN_NAME,
|
||||||
LOCAL_ADDRESS_COLUMN_NAME, REMOTE_ADDRESS_COLUMN_NAME, MANUAL_ADDRESS_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
|
// Move all computers from the old DB (if any) to the new one
|
||||||
List<ComputerDetails> oldComputers = LegacyDatabaseReader.migrateAllComputers(c);
|
List<ComputerDetails> oldComputers = LegacyDatabaseReader.migrateAllComputers(c);
|
||||||
@ -70,6 +85,17 @@ public class ComputerDatabaseManager {
|
|||||||
values.put(REMOTE_ADDRESS_COLUMN_NAME, details.remoteAddress);
|
values.put(REMOTE_ADDRESS_COLUMN_NAME, details.remoteAddress);
|
||||||
values.put(MANUAL_ADDRESS_COLUMN_NAME, details.manualAddress);
|
values.put(MANUAL_ADDRESS_COLUMN_NAME, details.manualAddress);
|
||||||
values.put(MAC_ADDRESS_COLUMN_NAME, details.macAddress);
|
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);
|
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.manualAddress = c.getString(4);
|
||||||
details.macAddress = c.getString(5);
|
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)
|
// This signifies we don't have dynamic state (like pair state)
|
||||||
details.state = ComputerDetails.State.UNKNOWN;
|
details.state = ComputerDetails.State.UNKNOWN;
|
||||||
|
|
||||||
|
@ -435,8 +435,8 @@ public class ComputerManagerService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
NvHTTP http = new NvHTTP(address, idManager.getUniqueId(),
|
NvHTTP http = new NvHTTP(address, idManager.getUniqueId(), details.serverCert,
|
||||||
null, PlatformBinding.getCryptoProvider(ComputerManagerService.this));
|
PlatformBinding.getCryptoProvider(ComputerManagerService.this));
|
||||||
|
|
||||||
ComputerDetails newDetails = http.getComputerDetails();
|
ComputerDetails newDetails = http.getComputerDetails();
|
||||||
|
|
||||||
@ -698,7 +698,7 @@ public class ComputerManagerService extends Service {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), idManager.getUniqueId(),
|
NvHTTP http = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer), idManager.getUniqueId(),
|
||||||
null, PlatformBinding.getCryptoProvider(ComputerManagerService.this));
|
computer.serverCert, PlatformBinding.getCryptoProvider(ComputerManagerService.this));
|
||||||
|
|
||||||
String appList;
|
String appList;
|
||||||
if (tuple != null) {
|
if (tuple != null) {
|
||||||
|
@ -22,7 +22,8 @@ public class NetworkAssetLoader {
|
|||||||
public InputStream getBitmapStream(CachedAppAssetLoader.LoaderTuple tuple) {
|
public InputStream getBitmapStream(CachedAppAssetLoader.LoaderTuple tuple) {
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
try {
|
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);
|
in = http.getBoxArt(tuple.app);
|
||||||
} catch (IOException ignored) {}
|
} catch (IOException ignored) {}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import com.limelight.nvstream.http.NvHTTP;
|
|||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
|
|
||||||
public class ServerHelper {
|
public class ServerHelper {
|
||||||
public static String getCurrentAddressFromComputer(ComputerDetails computer) {
|
public static String getCurrentAddressFromComputer(ComputerDetails computer) {
|
||||||
@ -31,6 +32,13 @@ public class ServerHelper {
|
|||||||
intent.putExtra(Game.EXTRA_UNIQUEID, managerBinder.getUniqueId());
|
intent.putExtra(Game.EXTRA_UNIQUEID, managerBinder.getUniqueId());
|
||||||
intent.putExtra(Game.EXTRA_PC_UUID, computer.uuid.toString());
|
intent.putExtra(Game.EXTRA_PC_UUID, computer.uuid.toString());
|
||||||
intent.putExtra(Game.EXTRA_PC_NAME, computer.name);
|
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;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +53,7 @@ public class ServerHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void doQuit(final Activity parent,
|
public static void doQuit(final Activity parent,
|
||||||
final String address,
|
final ComputerDetails computer,
|
||||||
final NvApp app,
|
final NvApp app,
|
||||||
final ComputerManagerService.ComputerManagerBinder managerBinder,
|
final ComputerManagerService.ComputerManagerBinder managerBinder,
|
||||||
final Runnable onComplete) {
|
final Runnable onComplete) {
|
||||||
@ -56,8 +64,8 @@ public class ServerHelper {
|
|||||||
NvHTTP httpConn;
|
NvHTTP httpConn;
|
||||||
String message;
|
String message;
|
||||||
try {
|
try {
|
||||||
httpConn = new NvHTTP(address,
|
httpConn = new NvHTTP(ServerHelper.getCurrentAddressFromComputer(computer),
|
||||||
managerBinder.getUniqueId(), null, PlatformBinding.getCryptoProvider(parent));
|
managerBinder.getUniqueId(), computer.serverCert, PlatformBinding.getCryptoProvider(parent));
|
||||||
if (httpConn.quitApp()) {
|
if (httpConn.quitApp()) {
|
||||||
message = parent.getResources().getString(R.string.applist_quit_success) + " " + app.getAppName();
|
message = parent.getResources().getString(R.string.applist_quit_success) + " " + app.getAppName();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 1af5c24e56e3d0b4cae477346306075129b8ddf8
|
Subproject commit 364f34a5646dcfd89cf02270906b01892cd1f76e
|
Loading…
x
Reference in New Issue
Block a user