From 9636f4742b4c00dea3c70834dec75f14a8231d36 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 31 Mar 2016 11:20:18 -0400 Subject: [PATCH] Implement pairing for Gen 7 servers --- Limelight/Crypto/CryptoManager.h | 4 ++- Limelight/Crypto/CryptoManager.m | 20 +++++++++++--- Limelight/Network/PairManager.h | 2 +- Limelight/Network/PairManager.m | 46 +++++++++++++++++++++++++++----- 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/Limelight/Crypto/CryptoManager.h b/Limelight/Crypto/CryptoManager.h index 493ae59..f716083 100644 --- a/Limelight/Crypto/CryptoManager.h +++ b/Limelight/Crypto/CryptoManager.h @@ -17,8 +17,10 @@ + (NSData*) getSignatureFromCert:(NSData*)cert; + (NSData*) nullTerminateString:(NSData*)data; -- (NSData*) createAESKeyFromSalt:(NSData*)saltedPIN; +- (NSData*) createAESKeyFromSaltSHA1:(NSData*)saltedPIN; +- (NSData*) createAESKeyFromSaltSHA256:(NSData*)saltedPIN; - (NSData*) SHA1HashData:(NSData*)data; +- (NSData*) SHA256HashData:(NSData*)data; - (NSData*) aesEncrypt:(NSData*)data withKey:(NSData*)key; - (NSData*) aesDecrypt:(NSData*)data withKey:(NSData*)key; - (bool) verifySignature:(NSData *)data withSignature:(NSData*)signature andCert:(NSData*)cert; diff --git a/Limelight/Crypto/CryptoManager.m b/Limelight/Crypto/CryptoManager.m index 7e2aa4d..38b4e8b 100644 --- a/Limelight/Crypto/CryptoManager.m +++ b/Limelight/Crypto/CryptoManager.m @@ -16,19 +16,31 @@ #include @implementation CryptoManager -static const int SHA1_DIGEST_LENGTH = 20; +static const int SHA1_HASH_LENGTH = 20; +static const int SHA256_HASH_LENGTH = 32; static NSData* key = nil; static NSData* cert = nil; static NSData* p12 = nil; -- (NSData*) createAESKeyFromSalt:(NSData*)saltedPIN { +- (NSData*) createAESKeyFromSaltSHA1:(NSData*)saltedPIN { return [[self SHA1HashData:saltedPIN] subdataWithRange:NSMakeRange(0, 16)]; } +- (NSData*) createAESKeyFromSaltSHA256:(NSData*)saltedPIN { + return [[self SHA256HashData:saltedPIN] subdataWithRange:NSMakeRange(0, 16)]; +} + - (NSData*) SHA1HashData:(NSData*)data { - unsigned char sha1[SHA1_DIGEST_LENGTH]; + unsigned char sha1[SHA1_HASH_LENGTH]; SHA1([data bytes], [data length], sha1); - NSData* bytes = [NSData dataWithBytes:sha1 length:20]; + NSData* bytes = [NSData dataWithBytes:sha1 length:sizeof(sha1)]; + return bytes; +} + +- (NSData*) SHA256HashData:(NSData*)data { + unsigned char sha256[SHA256_HASH_LENGTH]; + SHA256([data bytes], [data length], sha256); + NSData* bytes = [NSData dataWithBytes:sha256 length:sizeof(sha256)]; return bytes; } diff --git a/Limelight/Network/PairManager.h b/Limelight/Network/PairManager.h index 41fee24..144eb7d 100644 --- a/Limelight/Network/PairManager.h +++ b/Limelight/Network/PairManager.h @@ -22,6 +22,6 @@ - (id) initWithManager:(HttpManager*)httpManager andCert:(NSData*)cert callback:(id)callback; - (NSString*) generatePIN; - (NSData*) saltPIN:(NSString*)PIN; -- (void) initiatePair; +- (void) initiatePair:(int)serverMajorVersion; @end diff --git a/Limelight/Network/PairManager.m b/Limelight/Network/PairManager.m index 7a0e2ad..ac22cbf 100644 --- a/Limelight/Network/PairManager.m +++ b/Limelight/Network/PairManager.m @@ -41,14 +41,21 @@ if (![[serverInfoResp getStringTag:@"state"] hasSuffix:@"_SERVER_AVAILABLE"]) { [_callback pairFailed:@"You must stop streaming before attempting to pair."]; } else if (![[serverInfoResp getStringTag:@"PairStatus"] isEqual:@"1"]) { - [self initiatePair]; + NSString* appversion = [serverInfoResp getStringTag:@"appversion"]; + if (appversion == nil) { + [_callback pairFailed:@"Missing XML element"]; + return; + } + [self initiatePair: [[appversion substringToIndex:1] intValue]]; } else { [_callback alreadyPaired]; } } } -- (void) initiatePair { +- (void) initiatePair:(int)serverMajorVersion { + Log(LOG_I, @"Pairing with generation %d server", serverMajorVersion); + NSString* PIN = [self generatePIN]; NSData* salt = [self saltPIN:PIN]; Log(LOG_I, @"PIN: %@, saltedPIN: %@", PIN, salt); @@ -69,7 +76,18 @@ NSString* plainCert = [pairResp getStringTag:@"plaincert"]; CryptoManager* cryptoMan = [[CryptoManager alloc] init]; - NSData* aesKey = [cryptoMan createAESKeyFromSalt:salt]; + NSData* aesKey; + + // Gen 7 servers use SHA256 to get the key + int hashLength; + if (serverMajorVersion >= 7) { + aesKey = [cryptoMan createAESKeyFromSaltSHA256:salt]; + hashLength = 32; + } + else { + aesKey = [cryptoMan createAESKeyFromSaltSHA1:salt]; + hashLength = 20; + } NSData* randomChallenge = [Utils randomBytes:16]; NSData* encryptedChallenge = [cryptoMan aesEncrypt:randomChallenge withKey:aesKey]; @@ -88,11 +106,18 @@ NSData* encServerChallengeResp = [Utils hexToBytes:[challengeResp getStringTag:@"challengeresponse"]]; NSData* decServerChallengeResp = [cryptoMan aesDecrypt:encServerChallengeResp withKey:aesKey]; - NSData* serverResponse = [decServerChallengeResp subdataWithRange:NSMakeRange(0, 20)]; - NSData* serverChallenge = [decServerChallengeResp subdataWithRange:NSMakeRange(20, 16)]; + NSData* serverResponse = [decServerChallengeResp subdataWithRange:NSMakeRange(0, hashLength)]; + NSData* serverChallenge = [decServerChallengeResp subdataWithRange:NSMakeRange(hashLength, 16)]; NSData* clientSecret = [Utils randomBytes:16]; - NSData* challengeRespHash = [cryptoMan SHA1HashData:[self concatData:[self concatData:serverChallenge with:[CryptoManager getSignatureFromCert:_cert]] with:clientSecret]]; + NSData* challengeRespHashInput = [self concatData:[self concatData:serverChallenge with:[CryptoManager getSignatureFromCert:_cert]] with:clientSecret]; + NSData* challengeRespHash; + if (serverMajorVersion >= 7) { + challengeRespHash = [cryptoMan SHA256HashData: challengeRespHashInput]; + } + else { + challengeRespHash = [cryptoMan SHA1HashData: challengeRespHashInput]; + } NSData* challengeRespEncrypted = [cryptoMan aesEncrypt:challengeRespHash withKey:aesKey]; HttpResponse* secretResp = [[HttpResponse alloc] init]; @@ -116,7 +141,14 @@ return; } - NSData* serverChallengeRespHash = [cryptoMan SHA1HashData:[self concatData:[self concatData:randomChallenge with:[CryptoManager getSignatureFromCert:[Utils hexToBytes:plainCert]]] with:serverSecret]]; + NSData* serverChallengeRespHashInput = [self concatData:[self concatData:randomChallenge with:[CryptoManager getSignatureFromCert:[Utils hexToBytes:plainCert]]] with:serverSecret]; + NSData* serverChallengeRespHash; + if (serverMajorVersion >= 7) { + serverChallengeRespHash = [cryptoMan SHA256HashData: serverChallengeRespHashInput]; + } + else { + serverChallengeRespHash = [cryptoMan SHA1HashData: serverChallengeRespHashInput]; + } if (![serverChallengeRespHash isEqual:serverResponse]) { [_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]]; [_callback pairFailed:@"Incorrect PIN"];