diff --git a/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/SnapchatKit.xcscheme b/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/SnapchatKit.xcscheme
index 230b6c3..1804021 100644
--- a/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/SnapchatKit.xcscheme
+++ b/Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/SnapchatKit.xcscheme
@@ -23,33 +23,42 @@
-
-
+ shouldUseLaunchSchemeArgsEnv = "YES">
+
+
+
+
+
+
+ shouldUseLaunchSchemeArgsEnv = "YES">
@@ -48,15 +48,18 @@
ReferencedContainer = "container:SnapchatKit.xcodeproj">
+
+
@@ -72,10 +75,10 @@
diff --git a/Pod/Classes/Categories/NSString+SnapchatKit.h b/Pod/Classes/Categories/NSString+SnapchatKit.h
index c67da4a..ca75a54 100755
--- a/Pod/Classes/Categories/NSString+SnapchatKit.h
+++ b/Pod/Classes/Categories/NSString+SnapchatKit.h
@@ -21,7 +21,8 @@
+ (NSString *)hashSC:(NSData *)first and:(NSData *)second;
+ (NSString *)hashSCString:(NSString *)first and:(NSString *)second;
-+ (NSString *)hashHMac:(NSString *)data key:(NSString *)key;
++ (NSData *)hashHMac:(NSString *)data key:(NSString *)key;
++ (NSString *)hashHMacToString:(NSString *)data key:(NSString *)key;
- (NSString *)MD5Hash;
diff --git a/Pod/Classes/Categories/NSString+SnapchatKit.m b/Pod/Classes/Categories/NSString+SnapchatKit.m
index 7115020..5ab7554 100755
--- a/Pod/Classes/Categories/NSString+SnapchatKit.m
+++ b/Pod/Classes/Categories/NSString+SnapchatKit.m
@@ -71,15 +71,17 @@ + (NSString *)hashSC:(NSData *)a and:(NSData *)b {
return hash;
}
-+ (NSString *)hashHMac:(NSString *)data key:(NSString *)key {
++ (NSString *)hashHMacToString:(NSString *)data key:(NSString *)key {
+ return [[self hashHMac:data key:key] base64EncodedStringWithOptions:0];
+}
+
++ (NSData *)hashHMac:(NSString *)data key:(NSString *)key {
const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
- NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
-
- return [HMAC base64EncodedStringWithOptions:0];
+ return [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
}
- (NSString *)MD5Hash {
diff --git a/Pod/Classes/Networking/SKClient.h b/Pod/Classes/Networking/SKClient.h
index 85359a2..93a8470 100755
--- a/Pod/Classes/Networking/SKClient.h
+++ b/Pod/Classes/Networking/SKClient.h
@@ -12,6 +12,8 @@
#import "SKSession.h"
+extern NSString *SKMakeCapserSignature(NSDictionary *params, NSString *secret);
+
/** Used to restructure JSON or modify an error returned from nearly any API call before being passed back to the application code. */
@protocol SKMiddleMan
/** @discussion This method is passed the JSON and any error generated by \c -[SKClient handleError:data:response:completion:].
@@ -36,6 +38,31 @@
/** The maxium sized to load videos in. */
@property (nonatomic) CGSize maxVideoSize;
+/** The username of the currently signed in (or not yet singed in) user. @note Always lowercase. */
+@property (nonatomic, readonly) NSString *username;
+/** The \c SKSession object representing the current Snapchat session.
+ @discussion Many of the categories on \c SKClient will automatically update this property; rarely is it necessary to attempt to update it yourself. */
+@property (nonatomic) SKSession *currentSession;
+
+// Data used to sign in
+
+/** Used internally to sign in. Passed in either of the \c -restoreSessionWithUsername:snapchatAuthToken: methods as the \c snapchatAuthToken: parameter. */
+@property (nonatomic, readonly) NSString *authToken;
+/** Used internally to sign in. Passed as the \c googleAuthToken: parameter to \c -restoreSessionWithUsername:snapchatAuthToken:googleAuthToken: method. */
+@property (nonatomic, readonly) NSString *googleAuthToken;
+/** Used internally. */
+@property (nonatomic, readonly) NSString *deviceToken1i;
+/** Used internally. */
+@property (nonatomic, readonly) NSString *deviceToken1v;
+/** Used internally to sign in and trick Snapchat into thinking we're using the first party client. */
+@property (nonatomic, readonly) NSString *googleAttestation;
+
+/** Required to sign in properly. See https://clients.casper.io to get your own. */
+@property (nonatomic) NSString *casperAPIKey;
+/** Required to sign in properly. See https://clients.casper.io to get your own. */
+@property (nonatomic) NSString *casperAPISecret;
+
+
#pragma mark Signing in
/** Signs into Snapchat.
@discussion A valid GMail account is necessary to trick Snapchat into thinking we're using the first party client. Your data is only ever sent to Google, Scout's honor.
@@ -123,24 +150,6 @@ The first step in creating a new Snapchat account. Registers an email, password,
/** Completion is GUARANTEED to have one and only one non-nil parameter. */
- (void)handleError:(NSError *)error data:(NSData *)data response:(NSURLResponse *)response completion:(ResponseBlock)completion;
-/** The username of the currently signed in (or not yet singed in) user. @note Always lowercase. */
-@property (nonatomic, readonly) NSString *username;
-/** The \c SKSession object representing the current Snapchat session.
- @discussion Many of the categories on \c SKClient will automatically update this property; rarely is it necessary to attempt to update it yourself. */
-@property (nonatomic) SKSession *currentSession;
-
-// Data used to sign in
-/** Used internally to sign in. Passed in either of the \c -restoreSessionWithUsername:snapchatAuthToken: methods as the \c snapchatAuthToken: parameter. */
-@property (nonatomic, readonly) NSString *authToken;
-/** Used internally to sign in. Passed as the \c googleAuthToken: parameter to \c -restoreSessionWithUsername:snapchatAuthToken:googleAuthToken: method. */
-@property (nonatomic, readonly) NSString *googleAuthToken;
-/** Used internally. */
-@property (nonatomic, readonly) NSString *deviceToken1i;
-/** Used internally. */
-@property (nonatomic, readonly) NSString *deviceToken1v;
-/** Used internally to sign in and trick Snapchat into thinking we're using the first party client. */
-@property (nonatomic, readonly) NSString *googleAttestation;
-
@end
/** Used to assert that we are signed in before making certain requests. */
diff --git a/Pod/Classes/Networking/SKClient.m b/Pod/Classes/Networking/SKClient.m
index 35208ef..8275d55 100755
--- a/Pod/Classes/Networking/SKClient.m
+++ b/Pod/Classes/Networking/SKClient.m
@@ -40,8 +40,19 @@ BOOL SKHasActiveConnection() {
return @{@"googleAuthToken": gauth, @"attestation": attest, @"pushToken": ptoken?:@"e", @"clientAuthToken": clientAuthToken, @"dt": deviceTokens, @"ts": timestamp};
}
-NSString * const kAttestationURLString = @"https://www.googleapis.com/androidcheck/v1/attestations/attest?alt=JSON&key=AIzaSyDqVnJBjE5ymo--oBJt3On7HQx9xNm1RHA";
-NSString * const kAttestationBase64Request = @"ClMKABIUY29tLnNuYXBjaGF0LmFuZHJvaWQaIC8cqvyh7TDQtOOIY+76vqDoFXEfpM95uCJRmoJZ2VpYIgAojKq/AzIECgASADoECAEQAUD4kP+pBRIA";
+NSString *SKMakeCapserSignature(NSDictionary *params, NSString *secret) {
+ assert(params.allKeys.count); assert(secret);
+ NSArray *sortedKeys = [params.allKeys sortedArrayUsingSelector:@selector(compare:options:)];
+ NSMutableString *signature = [NSMutableString string];
+
+ for (NSString *key in sortedKeys) {
+ [signature appendString:key];
+ [signature appendString:params[key]];
+ }
+
+ return [@"v1:" stringByAppendingString:[NSString hashHMac:signature key:secret].hexadecimalString];
+}
+
@implementation SKClient
@@ -314,9 +325,12 @@ - (void)getGoogleCloudMessagingIdentifier:(StringBlock)callback {
/** Google attestation. */
- (void)getAttestation:(NSString *)username password:(NSString *)password ts:(NSString *)timestamp callback:(StringBlock)callback {
+ NSAssert(self.casperAPIKey, @"You must have a valid API key from https://clients.casper.io to sign in.");
+ NSAssert(self.casperAPISecret, @"You must have a valid API secret from https://clients.casper.io to sign in.");
+
NSString *hashString = [[NSString stringWithFormat:@"%@|%@|%@|%@", username, password, timestamp, SKEPAccount.login].sha256HashRaw base64EncodedStringWithOptions:0];
// Get binary data
- NSData *binaryData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://api.casper.io/droidguard/create/binary"]];
+ NSData *binaryData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://api.casper.io/snapchat/attestation/create"]];
__block NSError *jsonError = nil;
__block NSDictionary *json = [NSJSONSerialization JSONObjectWithData:binaryData options:0 error:&jsonError];
@@ -334,11 +348,15 @@ - (void)getAttestation:(NSString *)username password:(NSString *)password ts:(NS
// Send it to Casper.io to build the protobuf
NSString *bytecodeProtobuf = [data base64EncodedStringWithOptions:0];
- NSDictionary *query = @{@"bytecode_proto": bytecodeProtobuf,
+ NSDictionary *query = @{@"protobuf": bytecodeProtobuf,
@"nonce": hashString,
- @"apk_digest": SKAttestation.digest9_14_2};
+ @"snapchat_version": SKConsts.snapchatVersion};
request.URL = [NSURL URLWithString:SKAttestation.protobufPOSTURL];
request.HTTPBody = [[NSString queryStringWithParams:query URLEscapeValues:YES] dataUsingEncoding:NSUTF8StringEncoding];
+
+ // Set headers
+ [request setValue:self.casperAPIKey forHTTPHeaderField:SKHeaders.casperAPIKey];
+ [request setValue:SKMakeCapserSignature(query, self.casperAPISecret) forHTTPHeaderField:SKHeaders.casperSignature];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:SKHeaders.contentType];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data2, NSURLResponse *response2, NSError *error2) {
@@ -387,11 +405,21 @@ - (void)getAttestation:(NSString *)username password:(NSString *)password ts:(NS
- (void)getClientAuthToken:(NSString *)username password:(NSString *)password timestamp:(NSString *)ts callback:(StringBlock)callback {
NSParameterAssert(username); NSParameterAssert(password); NSParameterAssert(ts);
+ NSAssert(self.casperAPIKey, @"You must have a valid API key from https://clients.casper.io to sign in.");
+ NSAssert(self.casperAPISecret, @"You must have a valid API secret from https://clients.casper.io to sign in.");
- NSURL *url = [NSURL URLWithString:@"https://api.casper.io/security/login/signrequest/"];
+ // Params
+ NSDictionary *query = @{@"username": username, @"password": password, @"timestamp": ts, @"snapchat_version": SKConsts.snapchatVersion};
+
+ // Build request
+ NSURL *url = [NSURL URLWithString:@"https://api.casper.io/snapchat/clientauth/signrequest"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
request.HTTPMethod = @"POST";
- request.HTTPBody = [[NSString queryStringWithParams:@{@"username": username, @"password": password, @"timestamp": ts}] dataUsingEncoding:NSUTF8StringEncoding];
+ request.HTTPBody = [[NSString queryStringWithParams:query] dataUsingEncoding:NSUTF8StringEncoding];
+
+ // Set headers
+ [request setValue:self.casperAPIKey forHTTPHeaderField:SKHeaders.casperAPIKey];
+ [request setValue:SKMakeCapserSignature(query, self.casperAPISecret) forHTTPHeaderField:SKHeaders.casperSignature];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
@@ -468,7 +496,7 @@ - (void)signInWithData:(NSDictionary *)loginData username:(NSString *)username p
NSString *req_token = [NSString hashSCString:SKConsts.staticToken and:timestamp];
NSString *string = [NSString stringWithFormat:@"%@|%@|%@|%@", username, password, timestamp, req_token];
- NSString *deviceSig = [[NSString hashHMac:string key:self.deviceToken1v] substringWithRange:NSMakeRange(0, 20)];
+ NSString *deviceSig = [[[NSString hashHMac:string key:self.deviceToken1v] base64EncodedStringWithOptions:0] substringWithRange:NSMakeRange(0, 20)];
NSDictionary *post = @{@"username": username,
@"password": password,
diff --git a/Pod/Classes/SnapchatKit-Constants.h b/Pod/Classes/SnapchatKit-Constants.h
index 9b9e5bd..7fc8f62 100755
--- a/Pod/Classes/SnapchatKit-Constants.h
+++ b/Pod/Classes/SnapchatKit-Constants.h
@@ -140,6 +140,7 @@ SK_NAMESPACE(SKConsts, {
NSNSString *boundary;
NSNSString *deviceToken1i;
NSNSString *deviceToken1v;
+ NSNSString *snapchatVersion;
});
#pragma mark Header fields / values
@@ -151,6 +152,8 @@ SK_NAMESPACE(SKHeaders, {
NSNSString *acceptLocale;
NSNSString *clientAuth;
NSNSString *clientAuthToken;
+ NSNSString *casperAPIKey;
+ NSNSString *casperSignature;
struct {
NSNSString *language;
NSNSString *locale;
diff --git a/Pod/Classes/SnapchatKit-Constants.m b/Pod/Classes/SnapchatKit-Constants.m
index 0665348..7e41cd2 100755
--- a/Pod/Classes/SnapchatKit-Constants.m
+++ b/Pod/Classes/SnapchatKit-Constants.m
@@ -101,7 +101,7 @@ BOOL SKMediaKindIsVideo(SKMediaKind mediaKind) {
.certificateDigest = @"Lxyq/KHtMNC044hj7vq+oOgVcR+kz3m4IlGaglnZWlg=",
.GMSVersion = 7329038,
.protobufBytecodeURL = @"https://www.googleapis.com/androidantiabuse/v1/x/create?alt=PROTO&key=AIzaSyBofcZsgLSS7BOnBjZPEkk4rYwzOIz-lTI",
- .protobufPOSTURL = @"https://api.casper.io/droidguard/attest/binary",
+ .protobufPOSTURL = @"https://api.casper.io/snapchat/attestation/attest",
.attestationURL = @"https://www.googleapis.com/androidcheck/v1/attestations/attest?alt=JSON&key=AIzaSyDqVnJBjE5ymo--oBJt3On7HQx9xNm1RHA",
.digest9_8 = @"vXCcGhQ7RfL1LUiE3F6vcNORNo7IFSOvuDBunK87mEI=",
.digest9_9 = @"Yk9Wqmx7TrTatldWI+5PWbQjGA8Gi8ZoO8X9OUAw1hg=",
@@ -118,7 +118,7 @@ BOOL SKMediaKindIsVideo(SKMediaKind mediaKind) {
#pragma mark Misc
SK_NAMESPACE_IMP(SKConsts) {
.baseURL = @"https://feelinsonice-hrd.appspot.com",
- .userAgent = @"Snapchat/9.14.2.0 (SM-N9005; Android 5.0.2; gzip)", //Snapchat/9.10.0.0 (HTC One; Android 4.4.2#302626.7#19; gzip)";
+ .userAgent = @"Snapchat/9.16.1.0 (SM-N9005; Android 5.0.2; gzip)", //Snapchat/9.16.1.0 (HTC One; Android 4.4.2#302626.7#19; gzip)";
.eventsURL = @"https://sc-analytics.appspot.com/post_events",
.analyticsURL = @"https://sc-analytics.appspot.com/analytics/bz",
.secret = @"iEk21fuwZApXlz93750dmW22pw389dPwOk",
@@ -128,6 +128,7 @@ BOOL SKMediaKindIsVideo(SKMediaKind mediaKind) {
.boundary = @"Boundary+0xAbCdEfGbOuNdArY",
.deviceToken1i = @"dtoken1i",
.deviceToken1v = @"dtoken1v",
+ .snapchatVersion = @"9.16.1.0"
};
#pragma mark Header fields / values
@@ -139,6 +140,8 @@ BOOL SKMediaKindIsVideo(SKMediaKind mediaKind) {
.acceptLocale = @"Accept-Locale",
.clientAuth = @"X-Snapchat-Client-Auth",
.clientAuthToken = @"X-Snapchat-Client-Auth-Token",
+ .casperAPIKey = @"X-Casper-API-Key",
+ .casperSignature = @"X-Casper-Signature",
.values = {
.language = @"en",
.locale = @"en_US",