Skip to content
This repository has been archived by the owner on Dec 18, 2022. It is now read-only.

Commit

Permalink
Progress on getting login to work again
Browse files Browse the repository at this point in the history
  • Loading branch information
NSExceptional committed Sep 10, 2015
1 parent e2276f4 commit faf9fcf
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 97 deletions.
4 changes: 2 additions & 2 deletions Example/SnapchatKit-OSX/SnapchatKit-OSX/main.m
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ int main(int argc, const char * argv[]) {
id foo = [dict[@"updates_response"] allKeys];

SKStoryCollection *friend = [[SKClient sharedClient].currentSession.stories
filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"%K BEGINSWITH %@", @"username", @"luke_"]].firstObject;
filteredOrderedSetUsingPredicate:[NSPredicate predicateWithFormat:@"%K BEGINSWITH %@", @"displayName", @""]].firstObject;
for (SKStory *s in friend.stories)
[s load:^(NSError *error1) {
if (!error1) {
Expand Down Expand Up @@ -297,7 +297,7 @@ int main(int argc, const char * argv[]) {
// testGetConversations();

// Download and save unread snaps
// saveUnreadSnapsToDirectory(unread, directory);
saveUnreadSnapsToDirectory(unread, directory);

// Mark snaps read
// markSnapsRead(unread);
Expand Down
3 changes: 1 addition & 2 deletions Pod/Classes/Model/SKRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,7 @@ - (id)initWithPOSTEndpoint:(NSString *)endpoint token:(NSString *)token query:(N

self.HTTPBody = body;
} else {
NSData *queryData = [[NSString queryStringWithParams:json] dataUsingEncoding:NSUTF8StringEncoding];
self.HTTPBody = queryData;
self.HTTPBody = [[NSString queryStringWithParams:json] dataUsingEncoding:NSUTF8StringEncoding];
}

if ([endpoint isEqualToString:SKEPSnaps.loadBlob] || [endpoint isEqualToString:SKEPChat.media])
Expand Down
2 changes: 2 additions & 0 deletions Pod/Classes/Networking/SKClient+Stories.m
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ - (void)loadStoryBlob:(SKStory *)story completion:(ResponseBlock)completion {
} else {
[self handleError:error data:data response:response completion:completion];
}
} else {
[self handleError:error data:data response:response completion:completion];
}
}];
} else {
Expand Down
9 changes: 0 additions & 9 deletions Pod/Classes/Networking/SKClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,6 @@
/** The default Snapchat session manager. To use more than one account, simply create and manage your own instances of \c SKClient instead of using the singleton. */
+ (instancetype)sharedClient;

/** You must explicitly set this property to YES in order to sign in with this library.
Leaving it set to NO will likely cause login to fail.
As of 9.14.0.0, Snapchat's servers require a new header value to login. No one knows
how this value is generated, so we use their code to generate it on a remote server.
The way this server is set up, the data is sent unencrypted. DO NOT use this library
to publish an app without explicitly warning users just as I have warned you. */
@property (nonatomic) BOOL useInsecureLogin;

/** See the \c SKMiddleMan protocol. */
@property (nonatomic) id<SKMiddleMan> middleMan;

Expand Down
147 changes: 82 additions & 65 deletions Pod/Classes/Networking/SKClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ BOOL SKHasActiveConnection() {
}

NSDictionary *SKMakeSignInParams(NSString *gauth, NSString *attest, NSString *ptoken, NSString *clientAuthToken, NSDictionary *deviceTokens, NSString *timestamp) {
return @{@"googleAuthToken": gauth, @"attestation": attest, @"pushToken": ptoken, @"clientAuthToken": clientAuthToken, @"dt": deviceTokens, @"ts": timestamp};
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";
Expand Down Expand Up @@ -314,70 +314,90 @@ - (void)getGoogleCloudMessagingIdentifier:(StringBlock)callback {

/** Google attestation. */
- (void)getAttestation:(NSString *)username password:(NSString *)password ts:(NSString *)timestamp callback:(StringBlock)callback {
NSString *hashString = [NSString stringWithFormat:@"%@|%@|%@|%@", username, password, timestamp, SKEPAccount.login];

AttestationBuilder *attestBuilder = [Attestation builder];

UnknownDataBuilder *unknownDataBuilder = [UnknownData builder];
unknownDataBuilder.unknown1 = YES;
unknownDataBuilder.unknown2 = YES;

DataContainerBuilder *dataContainerBuilder = [DataContainer builder];
dataContainerBuilder.nonce = hashString.sha256HashRaw;
dataContainerBuilder.apkPackageName = @"com.snapchat.android";
dataContainerBuilder.apkDigestSha256 = SKAttestation.digest9_14.base64DecodedData;
dataContainerBuilder.apkCertificateDigestSha256 = SKAttestation.certificateDigest.base64DecodedData;
dataContainerBuilder.gmsVersion = (int)SKAttestation.GMSVersion;
dataContainerBuilder.timestamp = timestamp.integerValue;
dataContainerBuilder.unknowndata = [unknownDataBuilder build];

attestBuilder.datacontainer = [dataContainerBuilder build];
attestBuilder.droidGuard = SKAttestation.droidGuard;

NSData *data = [attestBuilder build].data;

NSURL *url = [NSURL URLWithString:@"https://www.googleapis.com/androidcheck/v1/attestations/attest?alt=JSON&key=AIzaSyDqVnJBjE5ymo--oBJt3On7HQx9xNm1RHA"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = data;
[request setValue:@(data.length).stringValue forHTTPHeaderField:@"Content-Length"];
[request setValue:@"application/x-protobuf" forHTTPHeaderField:SKHeaders.contentType];
[request setValue:SKAttestation.userAgent forHTTPHeaderField:SKHeaders.userAgent];

NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
callback(nil, error);
} else if (data) {
NSError *jsonError;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];

if (jsonError)
callback(nil, jsonError);
else if ([(NSHTTPURLResponse *)response statusCode] == 200)
callback(json[@"signedAttestation"], nil);
else
callback(nil, [SKRequest unknownError]);
} else {
callback(nil, [SKRequest unknownError]);
}
}] resume];
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"]];
__block NSError *jsonError = nil;
__block NSDictionary *json = [NSJSONSerialization JSONObjectWithData:binaryData options:0 error:&jsonError];

if (json) {
// Get bytecode from Google
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:SKAttestation.protobufBytecodeURL]];
request.HTTPMethod = @"POST";
request.HTTPBody = [[json[@"binary"] base64Decode] dataUsingEncoding:NSUTF8StringEncoding];
[request setValue:SKHeaders.values.protobuf forHTTPHeaderField:SKHeaders.contentType];
[request setValue:SKHeaders.values.droidGuardUA forHTTPHeaderField:SKHeaders.userAgent];

NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error && [(NSHTTPURLResponse *)response statusCode] == 200) {

// Send it to Casper.io to build the protobuf
NSString *bytecodeProtobuf = [data base64EncodedStringWithOptions:0];
NSDictionary *query = @{@"bytecode_proto": bytecodeProtobuf,
@"nonce": hashString,
@"apk_digest": SKAttestation.digest9_14_2};

request.URL = [NSURL URLWithString:SKAttestation.protobufPOSTURL];
request.HTTPBody = [[NSString queryStringWithParams:query] dataUsingEncoding:NSUTF8StringEncoding];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data2, NSURLResponse *response2, NSError *error2) {
/////////////////////////////////
// This is where I get the 400 //
/////////////////////////////////
if (!error2 && [(NSHTTPURLResponse *)response2 statusCode] == 200) {
jsonError = nil;
json = [NSJSONSerialization JSONObjectWithData:data2 options:0 error:&jsonError];

if (json) {

// Get the actual attestation from Google
[request setValue:SKAttestation.userAgent forHTTPHeaderField:SKHeaders.userAgent];
request.URL = [NSURL URLWithString:SKAttestation.attestationURL];
request.HTTPBody = [json[@"binary"] base64DecodedData];
[request setValue:SKAttestation.userAgent forHTTPHeaderField:SKHeaders.userAgent];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data3, NSURLResponse *response3, NSError *error3) {
if (!error3 && [(NSHTTPURLResponse *)response3 statusCode] == 200) {
jsonError = nil;
json = [NSJSONSerialization JSONObjectWithData:data3 options:0 error:&jsonError];
callback(json[@"signedAttestation"], nil);
} else {
callback(nil, error3);
}
}] resume];

} else {
callback(nil, jsonError);
}
} else {
// Error: 400, bad request (missing parameter according to Liam)
callback(nil, error2);
}
}] resume];

} else {
callback(nil, error);
}
}] resume];
} else {
callback(nil, jsonError);
}
}

- (void)getClientAuthToken:(NSString *)username password:(NSString *)password timestamp:(NSString *)ts callback:(StringBlock)callback {
NSParameterAssert(username); NSParameterAssert(password); NSParameterAssert(ts);

NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://client-auth.casper.io/?username=%@&password=%@&timestamp=%@", username, password, ts]];
NSURL *url = [NSURL URLWithString:@"https://api.casper.io/security/login/signrequest/"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [[NSString queryStringWithParams:@{@"username": username, @"password": password, @"timestamp": ts}] dataUsingEncoding:NSUTF8StringEncoding];

NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSError *jsonError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];

if ([json[@"status"] integerValue] == 200)
if ([json[@"code"] integerValue] == 200)
callback(json[@"signature"], nil);
else
callback(nil, [SKRequest errorWithMessage:json[@"message"] code:[json[@"status"] integerValue]]);
Expand Down Expand Up @@ -411,20 +431,17 @@ - (void)signInWithUsername:(NSString *)username password:(NSString *)password gm
completion(nil, [SKRequest errorWithMessage:@"Could not retrieve Snapchat device token." code:error4.code?:1]);
} else {

if (self.useInsecureLogin) {
// X-Snapchat-Client-Auth
[self getClientAuthToken:username password:password timestamp:timestamp callback:^(NSString *clientAuthToken, NSError *error5) {
if (!error5 || !clientAuthToken) {

// Sign in
[self signInWithData:SKMakeSignInParams(gauth, attestation, ptoken, clientAuthToken, dict, timestamp) username:username password:password completion:completion];
} else {
completion(nil, error5 ?: [SKRequest errorWithMessage:@"Could not retrieve client auth token." code:1]);
}
}];
} else {
[self signInWithData:SKMakeSignInParams(gauth, attestation, ptoken, @"", dict, timestamp) username:username password:password completion:completion];
}
// X-Snapchat-Client-Auth
[self getClientAuthToken:username password:password timestamp:timestamp callback:^(NSString *clientAuthToken, NSError *error5) {
if (error5 || !clientAuthToken) {
completion(nil, error5 ?: [SKRequest errorWithMessage:@"Could not retrieve client auth token." code:1]);
} else {

// Sign in
NSParameterAssert(gauth); NSParameterAssert(attestation); NSParameterAssert(clientAuthToken); NSParameterAssert(dict); NSParameterAssert(timestamp);
[self signInWithData:SKMakeSignInParams(gauth, attestation, ptoken, clientAuthToken, dict, timestamp) username:username password:password completion:completion];
}
}];
}
}];
}];
Expand Down
10 changes: 8 additions & 2 deletions Pod/Classes/SnapchatKit-Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define SKTempDirectory() [NSTemporaryDirectory() stringByAppendingPathComponent:@"SnapchatKit-tmp"]

#define NSNSString __unsafe_unretained NSString
#define NSNSURL __unsafe_unretained NSURL
#define SK_NAMESPACE(name, vals) extern const struct name vals name
#define SK_NAMESPACE_IMP(name) const struct name name =

Expand Down Expand Up @@ -101,8 +102,10 @@ SK_NAMESPACE(SKAttestation, {
NSNSString *certificateDigest;
/** Google Play Services version used to make the attestation request. */
NSInteger GMSVersion;
/** Authentication token sent to verify requests with the server to prevent abuse. */
NSNSString *droidGuard;
/** The URL used to get the bytecode needed to generate droidguard and attestation. */
NSNSString *protobufBytecodeURL;
NSNSString *protobufPOSTURL;
NSNSString *attestationURL;
NSNSString *digest9_8;
NSNSString *digest9_9;
NSNSString *digest9_10;
Expand All @@ -112,6 +115,7 @@ SK_NAMESPACE(SKAttestation, {
NSNSString *digest9_12_2;
NSNSString *digest9_13;
NSNSString *digest9_14;
NSNSString *digest9_14_2;

});

Expand Down Expand Up @@ -150,6 +154,8 @@ SK_NAMESPACE(SKHeaders, {
struct {
NSNSString *language;
NSNSString *locale;
NSNSString *droidGuardUA;
NSNSString *protobuf;
} values;
});

Expand Down

0 comments on commit faf9fcf

Please sign in to comment.