Add cert pinning during pairing

This commit is contained in:
Cameron Gutman 2018-12-21 21:00:53 -08:00
parent 16b845ab84
commit db49077b9b
8 changed files with 89 additions and 23 deletions

View File

@ -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() {

View File

@ -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);

View File

@ -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);

View File

@ -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<ComputerDetails> 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;

View File

@ -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) {

View File

@ -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) {}

View File

@ -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 {

@ -1 +1 @@
Subproject commit 1af5c24e56e3d0b4cae477346306075129b8ddf8
Subproject commit 364f34a5646dcfd89cf02270906b01892cd1f76e