mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2025-07-01 23:35:59 +00:00
Add server cert pinning after pairing
This commit is contained in:
parent
791a5b1ea1
commit
fbae7f88b5
@ -13,7 +13,7 @@
|
||||
+ (NSData*) readKeyFromFile;
|
||||
+ (NSData*) readP12FromFile;
|
||||
+ (NSData*) getSignatureFromCert:(NSData*)cert;
|
||||
+ (NSData*) nullTerminateString:(NSData*)data;
|
||||
+ (NSData*) pemToDer:(NSData*)pemCertBytes;
|
||||
|
||||
- (NSData*) createAESKeyFromSaltSHA1:(NSData*)saltedPIN;
|
||||
- (NSData*) createAESKeyFromSaltSHA256:(NSData*)saltedPIN;
|
||||
|
@ -87,17 +87,28 @@ static NSData* p12 = nil;
|
||||
return (((int)[data length] + 15) / 16) * 16;
|
||||
}
|
||||
|
||||
+ (NSData*) nullTerminateString:(NSData*)data {
|
||||
NSMutableData* mutData = [NSMutableData dataWithData:data];
|
||||
[mutData appendBytes:"" length:1];
|
||||
return mutData;
|
||||
+ (NSData*) pemToDer:(NSData*)pemCertBytes {
|
||||
X509* x509;
|
||||
|
||||
BIO* bio = BIO_new_mem_buf([pemCertBytes bytes], (int)[pemCertBytes length]);
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
BIO_free(bio);
|
||||
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
i2d_X509_bio(bio, x509);
|
||||
|
||||
BUF_MEM* mem;
|
||||
BIO_get_mem_ptr(bio, &mem);
|
||||
|
||||
NSData* ret = [[NSData alloc] initWithBytes:mem->data length:mem->length];
|
||||
BIO_free(bio);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (bool) verifySignature:(NSData *)data withSignature:(NSData*)signature andCert:(NSData*)cert {
|
||||
const char* buffer = [[CryptoManager nullTerminateString:cert] bytes];
|
||||
X509* x509;
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_puts(bio, buffer);
|
||||
BIO* bio = BIO_new_mem_buf([cert bytes], (int)[cert length]);
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
|
||||
BIO_free(bio);
|
||||
@ -122,9 +133,7 @@ static NSData* p12 = nil;
|
||||
}
|
||||
|
||||
- (NSData *)signData:(NSData *)data withKey:(NSData *)key {
|
||||
const char* buffer = [[CryptoManager nullTerminateString:key] bytes];
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_puts(bio, buffer);
|
||||
BIO* bio = BIO_new_mem_buf([key bytes], (int)[key length]);
|
||||
|
||||
EVP_PKEY* pkey;
|
||||
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
@ -211,11 +220,8 @@ static NSData* p12 = nil;
|
||||
}
|
||||
|
||||
+ (NSData *)getSignatureFromCert:(NSData *)cert {
|
||||
const char* buffer = [[CryptoManager nullTerminateString:cert] bytes];
|
||||
X509* x509;
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_puts(bio, buffer);
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
BIO* bio = BIO_new_mem_buf([cert bytes], (int)[cert length]);
|
||||
X509* x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
|
||||
if (!x509) {
|
||||
Log(LOG_E, @"Unable to parse certificate in memory!");
|
||||
|
@ -16,15 +16,17 @@
|
||||
@property (nonatomic, nullable) NSString * activeAddress;
|
||||
@property (nonatomic, nullable) NSString * currentGame;
|
||||
|
||||
@property (nonatomic, nullable, retain) NSData *serverCert;
|
||||
@property (nonatomic, nullable, retain) NSString *address;
|
||||
@property (nonatomic, nullable, retain) NSString *externalAddress;
|
||||
@property (nonatomic, nullable, retain) NSString *localAddress;
|
||||
@property (nonatomic, nullable, retain) NSString *mac;
|
||||
@property (nonatomic) int serverCodecModeSupport;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nonatomic, retain) NSString *address;
|
||||
@property (nonatomic, retain) NSString *externalAddress;
|
||||
@property (nonatomic, retain) NSString *localAddress;
|
||||
@property (nonatomic, retain) NSString *mac;
|
||||
@property (nonatomic, retain) NSString *name;
|
||||
@property (nonatomic, retain) NSString *uuid;
|
||||
@property (nonatomic) int serverCodecModeSupport;
|
||||
@property (nonatomic, retain) NSMutableSet *appList;
|
||||
|
||||
- (id) initFromHost:(Host*)host;
|
||||
|
@ -31,6 +31,7 @@
|
||||
self.uuid = host.uuid;
|
||||
self.pairState = [host.pairState intValue];
|
||||
self.serverCodecModeSupport = host.serverCodecModeSupport;
|
||||
self.serverCert = host.serverCert;
|
||||
|
||||
NSMutableSet *appList = [[NSMutableSet alloc] init];
|
||||
|
||||
@ -60,6 +61,9 @@
|
||||
if (self.mac != nil) {
|
||||
parentHost.mac = self.mac;
|
||||
}
|
||||
if (self.serverCert != nil) {
|
||||
parentHost.serverCert = self.serverCert;
|
||||
}
|
||||
parentHost.name = self.name;
|
||||
parentHost.uuid = self.uuid;
|
||||
parentHost.serverCodecModeSupport = self.serverCodecModeSupport;
|
||||
|
@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>Moonlight v1.2.xcdatamodel</string>
|
||||
<string>Moonlight v1.3.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14460.32" systemVersion="18C54" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="id" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="Host" representedClassName="Host" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="externalAddress" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="serverCert" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||
<attribute name="serverCodecModeSupport" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="streamingRemotely" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
|
||||
<attribute name="useHevc" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
|
||||
<element name="Host" positionX="0" positionY="0" width="128" height="195"/>
|
||||
<element name="Settings" positionX="0" positionY="0" width="128" height="225"/>
|
||||
</elements>
|
||||
</model>
|
@ -20,7 +20,7 @@ static const int MAX_ATTEMPTS = 5;
|
||||
- (void) main {
|
||||
int attempts = 0;
|
||||
while (![self isCancelled] && attempts++ < MAX_ATTEMPTS) {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.activeAddress uniqueId:[IdManager getUniqueId] deviceName:deviceName cert:[CryptoManager readCertFromFile]];
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.activeAddress uniqueId:[IdManager getUniqueId] serverCert:_host.serverCert];
|
||||
AppAssetResponse* appAssetResp = [[AppAssetResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:appAssetResp withUrlRequest:[hMan newAppAssetRequestWithAppId:self.app.id]]];
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
@interface ConnectionHelper : NSObject
|
||||
|
||||
+(AppListResponse*) getAppListForHostWithHostIP:(NSString*) hostIP deviceName:(NSString*)deviceName cert:(NSData*) cert uniqueID:(NSString*) uniqueId;
|
||||
+(AppListResponse*) getAppListForHostWithHostIP:(NSString*) hostIP serverCert:(NSData*) serverCert uniqueID:(NSString*) uniqueId;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -14,8 +14,8 @@
|
||||
|
||||
@implementation ConnectionHelper
|
||||
|
||||
+(AppListResponse*) getAppListForHostWithHostIP:(NSString*) hostIP deviceName:(NSString*)deviceName cert:(NSData*) cert uniqueID:(NSString*) uniqueId {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:hostIP uniqueId:uniqueId deviceName:deviceName cert:cert];
|
||||
+(AppListResponse*) getAppListForHostWithHostIP:(NSString*) hostIP serverCert:(NSData*) cert uniqueID:(NSString*) uniqueId {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:hostIP uniqueId:uniqueId serverCert:cert];
|
||||
|
||||
// Try up to 5 times to get the app list
|
||||
AppListResponse* appListResp;
|
||||
|
@ -48,7 +48,7 @@
|
||||
}
|
||||
|
||||
- (void) discoverHost:(NSString *)hostAddress withCallback:(void (^)(TemporaryHost *, NSString*))callback {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:hostAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:hostAddress uniqueId:_uniqueId serverCert:nil];
|
||||
ServerInfoResponse* serverInfoResponse = [[ServerInfoResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResponse withUrlRequest:[hMan newServerInfoRequest:false] fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
|
||||
|
||||
@ -183,7 +183,7 @@
|
||||
}
|
||||
|
||||
- (NSOperation*) createWorkerForHost:(TemporaryHost*)host {
|
||||
DiscoveryWorker* worker = [[DiscoveryWorker alloc] initWithHost:host uniqueId:_uniqueId cert:_cert];
|
||||
DiscoveryWorker* worker = [[DiscoveryWorker alloc] initWithHost:host uniqueId:_uniqueId];
|
||||
return worker;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
@interface DiscoveryWorker : NSOperation
|
||||
|
||||
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId cert:(NSData*)cert;
|
||||
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId;
|
||||
- (void) discoverHost;
|
||||
- (TemporaryHost*) getHost;
|
||||
|
||||
|
@ -16,16 +16,14 @@
|
||||
@implementation DiscoveryWorker {
|
||||
TemporaryHost* _host;
|
||||
NSString* _uniqueId;
|
||||
NSData* _cert;
|
||||
}
|
||||
|
||||
static const float POLL_RATE = 2.0f; // Poll every 2 seconds
|
||||
|
||||
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId cert:(NSData*)cert {
|
||||
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId {
|
||||
self = [super init];
|
||||
_host = host;
|
||||
_uniqueId = uniqueId;
|
||||
_cert = cert;
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -98,7 +96,7 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
|
||||
return;
|
||||
}
|
||||
|
||||
ServerInfoResponse* serverInfoResp = [self requestInfoAtAddress:address];
|
||||
ServerInfoResponse* serverInfoResp = [self requestInfoAtAddress:address cert:_host.serverCert];
|
||||
receivedResponse = [self checkResponse:serverInfoResp];
|
||||
if (receivedResponse) {
|
||||
[serverInfoResp populateHost:_host];
|
||||
@ -123,11 +121,10 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
|
||||
}
|
||||
}
|
||||
|
||||
- (ServerInfoResponse*) requestInfoAtAddress:(NSString*)address {
|
||||
- (ServerInfoResponse*) requestInfoAtAddress:(NSString*)address cert:(NSData*)cert {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:address
|
||||
uniqueId:_uniqueId
|
||||
deviceName:deviceName
|
||||
cert:_cert];
|
||||
serverCert:cert];
|
||||
ServerInfoResponse* response = [[ServerInfoResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:response
|
||||
withUrlRequest:[hMan newServerInfoRequest:true]
|
||||
|
@ -12,8 +12,8 @@
|
||||
|
||||
@interface HttpManager : NSObject <NSURLSessionDelegate>
|
||||
|
||||
- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId deviceName:(NSString*) deviceName cert:(NSData*) cert;
|
||||
- (NSURLRequest*) newPairRequest:(NSData*)salt;
|
||||
- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId serverCert:(NSData*) serverCert;
|
||||
- (NSURLRequest*) newPairRequest:(NSData*)salt clientCert:(NSData*)clientCert;
|
||||
- (NSURLRequest*) newUnpairRequest;
|
||||
- (NSURLRequest*) newChallengeRequest:(NSData*)challenge;
|
||||
- (NSURLRequest*) newChallengeRespRequest:(NSData*)challengeResp;
|
||||
|
@ -25,12 +25,12 @@
|
||||
NSString* _baseHTTPSURL;
|
||||
NSString* _uniqueId;
|
||||
NSString* _deviceName;
|
||||
NSData* _cert;
|
||||
NSData* _serverCert;
|
||||
NSMutableData* _respData;
|
||||
NSData* _requestResp;
|
||||
dispatch_semaphore_t _requestLock;
|
||||
|
||||
BOOL _errorOccurred;
|
||||
NSError* _error;
|
||||
}
|
||||
|
||||
static const NSString* HTTP_PORT = @"47989";
|
||||
@ -43,11 +43,11 @@ static const NSString* HTTPS_PORT = @"47984";
|
||||
return [xmlString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId deviceName:(NSString*) deviceName cert:(NSData*) cert {
|
||||
- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId serverCert:(NSData*) serverCert {
|
||||
self = [super init];
|
||||
_uniqueId = uniqueId;
|
||||
_deviceName = deviceName;
|
||||
_cert = cert;
|
||||
_serverCert = serverCert;
|
||||
_requestLock = dispatch_semaphore_create(0);
|
||||
_respData = [[NSMutableData alloc] init];
|
||||
NSURLSessionConfiguration* config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
@ -68,13 +68,15 @@ static const NSString* HTTPS_PORT = @"47984";
|
||||
}
|
||||
|
||||
- (void) executeRequestSynchronously:(HttpRequest*)request {
|
||||
Log(LOG_D, @"Making Request: %@", request);
|
||||
[_respData setLength:0];
|
||||
_error = nil;
|
||||
|
||||
Log(LOG_D, @"Making Request: %@", request);
|
||||
[[_urlSession dataTaskWithRequest:request.request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
|
||||
|
||||
if (error != NULL) {
|
||||
Log(LOG_D, @"Connection error: %@", error);
|
||||
self->_errorOccurred = true;
|
||||
self->_error = error;
|
||||
}
|
||||
else {
|
||||
Log(LOG_D, @"Received response: %@", response);
|
||||
@ -94,7 +96,7 @@ static const NSString* HTTPS_PORT = @"47984";
|
||||
}] resume];
|
||||
dispatch_semaphore_wait(_requestLock, DISPATCH_TIME_FOREVER);
|
||||
|
||||
if (!_errorOccurred && request.response) {
|
||||
if (!_error && request.response) {
|
||||
[request.response populateWithData:_requestResp];
|
||||
|
||||
// If the fallback error code was detected, issue the fallback request
|
||||
@ -106,7 +108,15 @@ static const NSString* HTTPS_PORT = @"47984";
|
||||
[self executeRequestSynchronously:request];
|
||||
}
|
||||
}
|
||||
_errorOccurred = false;
|
||||
else if (_error && [_error code] == NSURLErrorServerCertificateUntrusted && request.fallbackRequest) {
|
||||
// This will fall back to HTTP on serverinfo queries to allow us to pair again
|
||||
// and get the server cert updated.
|
||||
Log(LOG_D, @"Attempting fallback request after certificate trust failure");
|
||||
request.request = request.fallbackRequest;
|
||||
request.fallbackError = 0;
|
||||
request.fallbackRequest = NULL;
|
||||
[self executeRequestSynchronously:request];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSURLRequest*) createRequestFromString:(NSString*) urlString timeout:(int)timeout {
|
||||
@ -116,9 +126,9 @@ static const NSString* HTTPS_PORT = @"47984";
|
||||
return request;
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newPairRequest:(NSData*)salt {
|
||||
- (NSURLRequest*) newPairRequest:(NSData*)salt clientCert:(NSData*)clientCert {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=getservercert&salt=%@&clientcert=%@",
|
||||
_baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:salt], [self bytesToHex:_cert]];
|
||||
_baseHTTPURL, _uniqueId, _deviceName, [self bytesToHex:salt], [self bytesToHex:clientCert]];
|
||||
// This call blocks while waiting for the user to input the PIN on the PC
|
||||
return [self createRequestFromString:urlString timeout:EXTRA_LONG_TIMEOUT_SEC];
|
||||
}
|
||||
@ -130,18 +140,18 @@ static const NSString* HTTPS_PORT = @"47984";
|
||||
|
||||
- (NSURLRequest*) newChallengeRequest:(NSData*)challenge {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientchallenge=%@",
|
||||
_baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:challenge]];
|
||||
_baseHTTPURL, _uniqueId, _deviceName, [self bytesToHex:challenge]];
|
||||
return [self createRequestFromString:urlString timeout:NORMAL_TIMEOUT_SEC];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newChallengeRespRequest:(NSData*)challengeResp {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&serverchallengeresp=%@",
|
||||
_baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:challengeResp]];
|
||||
_baseHTTPURL, _uniqueId, _deviceName, [self bytesToHex:challengeResp]];
|
||||
return [self createRequestFromString:urlString timeout:NORMAL_TIMEOUT_SEC];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newClientSecretRespRequest:(NSString*)clientPairSecret {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientpairingsecret=%@", _baseHTTPSURL, _uniqueId, _deviceName, clientPairSecret];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientpairingsecret=%@", _baseHTTPURL, _uniqueId, _deviceName, clientPairSecret];
|
||||
return [self createRequestFromString:urlString timeout:NORMAL_TIMEOUT_SEC];
|
||||
}
|
||||
|
||||
@ -249,6 +259,26 @@ static const NSString* HTTPS_PORT = @"47984";
|
||||
// Allow untrusted server certificates
|
||||
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
|
||||
{
|
||||
if (_serverCert) {
|
||||
SecCertificateRef actualCert = SecTrustGetCertificateAtIndex(challenge.protectionSpace.serverTrust, 0);
|
||||
|
||||
CFDataRef actualCertData;
|
||||
|
||||
actualCertData = SecCertificateCopyData(actualCert);
|
||||
|
||||
if (!CFEqual(actualCertData, (__bridge CFDataRef)_serverCert)) {
|
||||
Log(LOG_E, @"Server certificate mismatch");
|
||||
CFRelease(actualCertData);
|
||||
completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
CFRelease(actualCertData);
|
||||
|
||||
// Fall-through to TLS success
|
||||
}
|
||||
|
||||
// Allow TLS handshake to proceed
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential,
|
||||
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]);
|
||||
}
|
||||
|
@ -11,14 +11,14 @@
|
||||
@protocol PairCallback <NSObject>
|
||||
|
||||
- (void) showPIN:(NSString*)PIN;
|
||||
- (void) pairSuccessful;
|
||||
- (void) pairSuccessful:(NSData*)serverCert;
|
||||
- (void) pairFailed:(NSString*)message;
|
||||
- (void) alreadyPaired;
|
||||
|
||||
@end
|
||||
|
||||
@interface PairManager : NSOperation
|
||||
- (id) initWithManager:(HttpManager*)httpManager andCert:(NSData*)cert callback:(id<PairCallback>)callback;
|
||||
- (id) initWithManager:(HttpManager*)httpManager clientCert:(NSData*)clientCert callback:(id<PairCallback>)callback;
|
||||
- (NSString*) generatePIN;
|
||||
- (NSData*) saltPIN:(NSString*)PIN;
|
||||
- (void) initiatePair:(int)serverMajorVersion;
|
||||
|
@ -17,14 +17,14 @@
|
||||
|
||||
@implementation PairManager {
|
||||
HttpManager* _httpManager;
|
||||
NSData* _cert;
|
||||
NSData* _clientCert;
|
||||
id<PairCallback> _callback;
|
||||
}
|
||||
|
||||
- (id) initWithManager:(HttpManager*)httpManager andCert:(NSData*)cert callback:(id<PairCallback>)callback {
|
||||
- (id) initWithManager:(HttpManager*)httpManager clientCert:(NSData*)clientCert callback:(id<PairCallback>)callback {
|
||||
self = [super init];
|
||||
_httpManager = httpManager;
|
||||
_cert = cert;
|
||||
_clientCert = clientCert;
|
||||
_callback = callback;
|
||||
return self;
|
||||
}
|
||||
@ -62,7 +62,7 @@
|
||||
[_callback showPIN:PIN];
|
||||
|
||||
HttpResponse* pairResp = [[HttpResponse alloc] init];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:pairResp withUrlRequest:[_httpManager newPairRequest:salt]]];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:pairResp withUrlRequest:[_httpManager newPairRequest:salt clientCert:_clientCert]]];
|
||||
if (![self verifyResponseStatus:pairResp]) {
|
||||
return;
|
||||
}
|
||||
@ -113,7 +113,7 @@
|
||||
NSData* serverChallenge = [decServerChallengeResp subdataWithRange:NSMakeRange(hashLength, 16)];
|
||||
|
||||
NSData* clientSecret = [Utils randomBytes:16];
|
||||
NSData* challengeRespHashInput = [self concatData:[self concatData:serverChallenge with:[CryptoManager getSignatureFromCert:_cert]] with:clientSecret];
|
||||
NSData* challengeRespHashInput = [self concatData:[self concatData:serverChallenge with:[CryptoManager getSignatureFromCert:_clientCert]] with:clientSecret];
|
||||
NSData* challengeRespHash;
|
||||
if (serverMajorVersion >= 7) {
|
||||
challengeRespHash = [cryptoMan SHA256HashData: challengeRespHashInput];
|
||||
@ -180,7 +180,8 @@
|
||||
[_callback pairFailed:@"Pairing stage #5 failed"];
|
||||
return;
|
||||
}
|
||||
[_callback pairSuccessful];
|
||||
|
||||
[_callback pairSuccessful: [CryptoManager pemToDer:[Utils hexToBytes:plainCert]]];
|
||||
}
|
||||
|
||||
- (BOOL) verifyResponseStatus:(HttpResponse*)resp {
|
||||
|
@ -28,5 +28,6 @@
|
||||
@property BOOL enableHdr;
|
||||
@property BOOL multiController;
|
||||
@property BOOL allowHevc;
|
||||
@property NSData* serverCert;
|
||||
|
||||
@end
|
||||
|
@ -9,5 +9,5 @@
|
||||
#import "StreamConfiguration.h"
|
||||
|
||||
@implementation StreamConfiguration
|
||||
@synthesize host, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, streamingRemotely, appName, optimizeGameSettings, playAudioOnPC, audioChannelMask, audioChannelCount, enableHdr, multiController, allowHevc;
|
||||
@synthesize host, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, streamingRemotely, appName, optimizeGameSettings, playAudioOnPC, audioChannelMask, audioChannelCount, enableHdr, multiController, allowHevc, serverCert;
|
||||
@end
|
||||
|
@ -38,12 +38,10 @@
|
||||
- (void)main {
|
||||
[CryptoManager generateKeyPairUsingSSL];
|
||||
NSString* uniqueId = [IdManager getUniqueId];
|
||||
NSData* cert = [CryptoManager readCertFromFile];
|
||||
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_config.host
|
||||
uniqueId:uniqueId
|
||||
deviceName:deviceName
|
||||
cert:cert];
|
||||
serverCert:_config.serverCert];
|
||||
|
||||
ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest:false]
|
||||
|
@ -37,7 +37,7 @@
|
||||
NSOperationQueue* _opQueue;
|
||||
TemporaryHost* _selectedHost;
|
||||
NSString* _uniqueId;
|
||||
NSData* _cert;
|
||||
NSData* _clientCert;
|
||||
DiscoveryManager* _discMan;
|
||||
AppAssetManager* _appManager;
|
||||
StreamConfiguration* _streamConfig;
|
||||
@ -98,11 +98,14 @@ static NSMutableSet* hostList;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)pairSuccessful {
|
||||
- (void)pairSuccessful:(NSData*)serverCert {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Store the cert from pairing with the host
|
||||
self->_selectedHost.serverCert = serverCert;
|
||||
|
||||
[self->_pairAlert dismissViewControllerAnimated:YES completion:nil];
|
||||
self->_pairAlert = nil;
|
||||
|
||||
|
||||
[self->_discMan startDiscovery];
|
||||
[self alreadyPaired];
|
||||
});
|
||||
@ -139,7 +142,7 @@ static NSMutableSet* hostList;
|
||||
// Exempt this host from discovery while handling the applist query
|
||||
[self->_discMan removeHostFromDiscovery:host];
|
||||
|
||||
AppListResponse* appListResp = [ConnectionHelper getAppListForHostWithHostIP:host.activeAddress deviceName:deviceName cert:self->_cert uniqueID:self->_uniqueId];
|
||||
AppListResponse* appListResp = [ConnectionHelper getAppListForHostWithHostIP:host.activeAddress serverCert:host.serverCert uniqueID:self->_uniqueId];
|
||||
|
||||
[self->_discMan addHostToDiscovery:host];
|
||||
|
||||
@ -307,7 +310,7 @@ static NSMutableSet* hostList;
|
||||
|
||||
[self showLoadingFrame: ^{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:host.activeAddress uniqueId:self->_uniqueId deviceName:deviceName cert:self->_cert];
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:host.activeAddress uniqueId:self->_uniqueId serverCert:host.serverCert];
|
||||
ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init];
|
||||
|
||||
// Exempt this host from discovery while handling the serverinfo request
|
||||
@ -352,7 +355,7 @@ static NSMutableSet* hostList;
|
||||
Log(LOG_I, @"Trying to pair");
|
||||
// Polling the server while pairing causes the server to screw up
|
||||
[self->_discMan stopDiscoveryBlocking];
|
||||
PairManager* pMan = [[PairManager alloc] initWithManager:hMan andCert:self->_cert callback:self];
|
||||
PairManager* pMan = [[PairManager alloc] initWithManager:hMan clientCert:self->_clientCert callback:self];
|
||||
[self->_opQueue addOperation:pMan];
|
||||
}
|
||||
else {
|
||||
@ -457,6 +460,7 @@ static NSMutableSet* hostList;
|
||||
_streamConfig.host = app.host.activeAddress;
|
||||
_streamConfig.appID = app.id;
|
||||
_streamConfig.appName = app.name;
|
||||
_streamConfig.serverCert = app.host.serverCert;
|
||||
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
TemporarySettings* streamSettings = [dataMan getSettings];
|
||||
@ -524,7 +528,7 @@ static NSMutableSet* hostList;
|
||||
Log(LOG_I, @"Quitting application: %@", currentApp.name);
|
||||
[self showLoadingFrame: ^{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:app.host.activeAddress uniqueId:self->_uniqueId deviceName:deviceName cert:self->_cert];
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:app.host.activeAddress uniqueId:self->_uniqueId serverCert:app.host.serverCert];
|
||||
HttpResponse* quitResponse = [[HttpResponse alloc] init];
|
||||
HttpRequest* quitRequest = [HttpRequest requestForResponse: quitResponse withUrlRequest:[hMan newQuitAppRequest]];
|
||||
|
||||
@ -700,7 +704,7 @@ static NSMutableSet* hostList;
|
||||
// Set up crypto
|
||||
[CryptoManager generateKeyPairUsingSSL];
|
||||
_uniqueId = [IdManager getUniqueId];
|
||||
_cert = [CryptoManager readCertFromFile];
|
||||
_clientCert = [CryptoManager readCertFromFile];
|
||||
|
||||
_appManager = [[AppAssetManager alloc] initWithCallback:self];
|
||||
_opQueue = [[NSOperationQueue alloc] init];
|
||||
|
@ -165,6 +165,7 @@
|
||||
98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
9832D1341BBCD5C50036EF48 /* TemporaryApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryApp.h; path = Database/TemporaryApp.h; sourceTree = "<group>"; };
|
||||
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = "<group>"; };
|
||||
98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
9865DC3B2132922E0005B9B9 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
|
||||
986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
98878AE0206A226D00586E90 /* OSPortabilityDefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSPortabilityDefs.h; sourceTree = "<group>"; };
|
||||
@ -1504,6 +1505,7 @@
|
||||
FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */,
|
||||
986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */,
|
||||
98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */,
|
||||
FB53E1441BE5DCBC00CD6ECE /* Moonlight v1.0-2.xcdatamodel */,
|
||||
@ -1512,7 +1514,7 @@
|
||||
FB4678F21A51BDCB00377732 /* Limelight 0.3.0.xcdatamodel */,
|
||||
FB290D0619B2C406004C83CF /* Limelight.xcdatamodel */,
|
||||
);
|
||||
currentVersion = 986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */;
|
||||
currentVersion = 98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */;
|
||||
path = Limelight.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
Loading…
x
Reference in New Issue
Block a user