Skip to content
Browse files

Merge branch 'crypto'

  • Loading branch information...
2 parents d511097 + fc53a1f commit 545c7c0f275ee4d7156311ff726aaca5e89811b1 @followben committed Oct 8, 2012
View
4 Example/TentStatusExample.xcodeproj/project.pbxproj
@@ -34,6 +34,7 @@
9F24FEF3161AB0C500A93048 /* UIImageView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F24FEEA161AB0C500A93048 /* UIImageView+AFNetworking.m */; };
9F24FEF6161AB1DA00A93048 /* TentStatusClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F24FEF5161AB1D900A93048 /* TentStatusClient.m */; };
9F24FF01161AC6CF00A93048 /* TPTentHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F24FF00161AB80E00A93048 /* TPTentHTTPClient.m */; };
+ 9F6BE96716228E69000B45A7 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F6BE96616228E69000B45A7 /* Security.framework */; };
9F83DF20161BC0AC0078F22C /* AccountViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F83DF1F161BC0AC0078F22C /* AccountViewController.m */; };
9F859A84161BD4D300CE3388 /* NSURL+TPEquivalence.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F859A83161BD4D300CE3388 /* NSURL+TPEquivalence.m */; };
9F859A8C161D0C0700CE3388 /* NewStatusPostViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F859A8B161D0C0700CE3388 /* NewStatusPostViewController.m */; };
@@ -91,6 +92,7 @@
9F24FEF5161AB1D900A93048 /* TentStatusClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TentStatusClient.m; sourceTree = "<group>"; };
9F24FEFF161AB80E00A93048 /* TPTentHTTPClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TPTentHTTPClient.h; sourceTree = "<group>"; };
9F24FF00161AB80E00A93048 /* TPTentHTTPClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPTentHTTPClient.m; sourceTree = "<group>"; };
+ 9F6BE96616228E69000B45A7 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
9F83DF1E161BC0AC0078F22C /* AccountViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AccountViewController.h; sourceTree = "<group>"; };
9F83DF1F161BC0AC0078F22C /* AccountViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AccountViewController.m; sourceTree = "<group>"; };
9F859A82161BD4D300CE3388 /* NSURL+TPEquivalence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+TPEquivalence.h"; sourceTree = "<group>"; };
@@ -108,6 +110,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 9F6BE96716228E69000B45A7 /* Security.framework in Frameworks */,
9F24FC86161AAC0100A93048 /* UIKit.framework in Frameworks */,
9F24FC88161AAC0100A93048 /* Foundation.framework in Frameworks */,
9F24FC8A161AAC0100A93048 /* CoreGraphics.framework in Frameworks */,
@@ -138,6 +141,7 @@
9F24FC84161AAC0100A93048 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 9F6BE96616228E69000B45A7 /* Security.framework */,
9F24FC85161AAC0100A93048 /* UIKit.framework */,
9F24FC87161AAC0100A93048 /* Foundation.framework */,
9F24FC89161AAC0100A93048 /* CoreGraphics.framework */,
View
16 Example/TentStatusExample/AccountViewController.m
@@ -66,22 +66,18 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField
[textField resignFirstResponder];
- if ([self.entityURL isEquivalent:entityURL] &&
- [[TentStatusClient sharedClient] isAuthorizedForTentServer:self.tentServerURL]) {
+ if ([self.entityURL isEquivalent:entityURL] && [[TentStatusClient sharedClient] isAuthorizedForTentServer:self.tentServerURL]) {
[self showTimeline];
return YES;
}
-
- self.entityURL = entityURL;
-
- [[TentStatusClient sharedClient] discoverTentServerForEntityURL:self.entityURL success:^(NSURL *canonicalServerURL, NSURL *canonicalEntityURL) {
+
+ [[TentStatusClient sharedClient] discoverCanonicalURLsForEntityURL:entityURL success:^(NSURL *canonicalServerURL, NSURL *canonicalEntityURL) {
self.entityURL = canonicalEntityURL;
- if ([self.tentServerURL isEquivalent:canonicalServerURL] &&
- [[TentStatusClient sharedClient] isAuthorizedForTentServer:self.tentServerURL]) {
+ self.tentServerURL = canonicalServerURL;
+ if ([[TentStatusClient sharedClient] isAuthorizedForTentServer:canonicalServerURL]) {
[self showTimeline];
} else {
- self.tentServerURL = canonicalServerURL;
- [[TentStatusClient sharedClient] authorizeForTentServerURL:self.tentServerURL];
+ [[TentStatusClient sharedClient] authorizeForTentServerURL:canonicalServerURL];
}
} failure:nil];
View
6 TPTentClient/TPTentClient.h
@@ -36,9 +36,9 @@
@property (nonatomic, strong) NSDictionary *scopes;
// Discovery
-- (void)discoverTentServerForEntityURL:(NSURL *)url
- success:(void (^)(NSURL *canonicalServerURL, NSURL *canonicalEntityURL))success
- failure:(void (^)(NSError *error))failure;
+- (void)discoverCanonicalURLsForEntityURL:(NSURL *)url
+ success:(void (^)(NSURL *canonicalServerURL, NSURL *canonicalEntityURL))success
+ failure:(void (^)(NSError *error))failure;
// OAuth
- (BOOL)isAuthorizedForTentServer:(NSURL *)url;
View
17 TPTentClient/TPTentClient.m
@@ -70,9 +70,9 @@ @implementation TPTentClient
#pragma mark Discovery
-- (void)discoverTentServerForEntityURL:(NSURL *)url
- success:(void (^)(NSURL *canonicalServerURL, NSURL *canonicalEntityURL))success
- failure:(void (^)(NSError *error))failure
+- (void)discoverCanonicalURLsForEntityURL:(NSURL *)url
+ success:(void (^)(NSURL *canonicalServerURL, NSURL *canonicalEntityURL))success
+ failure:(void (^)(NSError *error))failure
{
AFHTTPClient *discoveryHTTPClient = [[AFHTTPClient alloc] initWithBaseURL:url];
[self headTentServerWithDiscoveryHTTPClient:discoveryHTTPClient success:success failure:failure];
@@ -82,11 +82,12 @@ - (void)discoverTentServerForEntityURL:(NSURL *)url
- (BOOL)isAuthorizedForTentServer:(NSURL *)url
{
- if (self.httpClient && [self.httpClient.baseURL isEquivalent:url] && [self.httpClient isRegisteredWithBaseURL]) {
- return YES;
+ if (!self.httpClient) {
+ self.httpClient = [[TPTentHTTPClient alloc] initWithBaseURL:url];
+ self.httpClient.delegate = self;
}
- return NO;
+ return [self.httpClient hasAuthorizedWithBaseURL];
}
- (void)authorizeForTentServerURL:(NSURL *)url
@@ -98,11 +99,11 @@ - (void)authorizeForTentServerURL:(NSURL *)url
success:(void (^)())success
failure:(void (^)(NSError *error))failure
{
- if (self.httpClient.isRegisteredWithBaseURL && [self.httpClient.baseURL isEqual:url]) {
+ if ([self.httpClient.baseURL isEquivalent:url] && [self.httpClient hasAuthorizedWithBaseURL] ) {
return;
}
- if (![self.httpClient.baseURL isEqual:url]) {
+ if (![self.httpClient.baseURL isEquivalent:url]) {
self.httpClient = [[TPTentHTTPClient alloc] initWithBaseURL:url];
self.httpClient.delegate = self;
}
View
2 TPTentClient/TPTentHTTPClient.h
@@ -29,7 +29,7 @@
@property (nonatomic, weak) id<TPTentHTTPDelegate> delegate;
-- (BOOL)isRegisteredWithBaseURL;
+- (BOOL)hasAuthorizedWithBaseURL;
- (void)registerForBaseURL;
- (void)registerForBaseURLWithSuccess:(void (^)())success
failure:(void (^)(NSError *error))failure;
View
128 TPTentClient/TPTentHTTPClient.m
@@ -25,6 +25,7 @@
#import "AFJSONRequestOperation.h"
#import "NSURL+SSToolkitAdditions.h"
#import <CommonCrypto/CommonHMAC.h>
+#import <Security/Security.h>
#pragma mark - Functions and constants
@@ -56,8 +57,14 @@
return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding];
}
-static NSString *const kTPTentContentType = @"application/vnd.tent.v0+json";
+static NSString * const kTPTentContentType = @"application/vnd.tent.v0+json";
+typedef enum TPTentHTTPClientKeychainKey : NSUInteger {
+ TPTentHTTPClientClientId,
+ TPTentHTTPClientMacKeyId,
+ TPTentHTTPClientMacKey,
+ TPTentHTTPClientAccessToken
+} TPTentHTTPClientKeychainKey;
#pragma mark - NSString extensions for HMACSHA256
@@ -85,12 +92,7 @@ + (NSString *)HMACSHA256EncodedStringWithString:(NSString *)string key:(NSString
@interface TPTentHTTPClient ()
-@property (nonatomic, strong) NSString *tentClientId;
@property (nonatomic, strong) NSString *tentState;
-@property (nonatomic, strong) NSString *tentMacAlgorithm;
-@property (nonatomic, strong) NSString *tentMacKeyId;
-@property (nonatomic, strong) NSString *tentMacKey;
-@property (nonatomic, strong) NSString *tentAccessToken;
@property (nonatomic, copy) void (^registrationSuccessBlock) ();
@property (nonatomic, copy) void (^registrationFailureBlock) (NSError *);
@@ -136,9 +138,9 @@ - (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)p
#pragma mark - Public methods
-- (BOOL)isRegisteredWithBaseURL
+- (BOOL)hasAuthorizedWithBaseURL
{
- return self.tentAccessToken.length > 0;
+ return [self keychainKeyExistsForCurrentBaseURL:TPTentHTTPClientAccessToken];
}
- (void)registerForBaseURL
@@ -151,7 +153,7 @@ - (void)registerForBaseURLWithSuccess:(void (^)())success failure:(void (^)(NSEr
self.registrationSuccessBlock = success;
self.registrationFailureBlock = failure;
- if ([self isRegisteredWithBaseURL]) {
+ if ([self hasAuthorizedWithBaseURL]) {
NSLog(@"Token found - skipping App registration");
return;
}
@@ -178,12 +180,11 @@ - (void)registerForBaseURLWithSuccess:(void (^)())success failure:(void (^)(NSEr
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
- self.tentMacAlgorithm = JSON[@"mac_algorithm"];
- self.tentMacKey = JSON[@"mac_key"];
- self.tentMacKeyId = JSON[@"mac_key_id"];
- self.tentClientId = JSON[@"id"];
+ [self setKeychainKey:TPTentHTTPClientMacKey value:JSON[@"mac_key"]];
+ [self setKeychainKey:TPTentHTTPClientMacKeyId value:JSON[@"mac_key_id"]];
+ [self setKeychainKey:TPTentHTTPClientClientId value:JSON[@"id"]];
- NSDictionary *authRequestParams = @{@"client_id": self.tentClientId,
+ NSDictionary *authRequestParams = @{@"client_id":JSON[@"id"],
@"tent_profile_info_types": @"all",
@"tent_post_types": @"all",
@"redirect_uri": [NSString stringWithFormat:@"%@://oauth", self.delegate.customURLScheme],
@@ -209,34 +210,32 @@ - (BOOL)handleOpenURL:(NSURL *)url
{
NSDictionary *queryDictionary = [url queryDictionary];
- if (![self.tentState isEqualToString:queryDictionary[@"state"]]) {
+ if (![self.tentState isEqualToString:queryDictionary[@"state"]] || ![self keychainKeyExistsForCurrentBaseURL:TPTentHTTPClientClientId]) {
return NO;
}
NSMutableURLRequest *request = [self requestWithMethod:@"POST"
- path:[NSString stringWithFormat:@"apps/%@/authorizations", self.tentClientId]
+ path:[NSString stringWithFormat:@"apps/%@/authorizations", [self valueForKeychainKey:TPTentHTTPClientClientId]]
parameters:@{@"code": queryDictionary[@"code"], @"token_type": @"mac"}];
-
- __weak TPTentHTTPClient *weakSelf = self;
-
+
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
if (JSON[@"access_token"]) {
- weakSelf.tentAccessToken = JSON[@"access_token"];
- weakSelf.tentMacKey = JSON[@"mac_key"];
+ [self setKeychainKey:TPTentHTTPClientAccessToken value:JSON[@"access_token"]];
+ [self setKeychainKey:TPTentHTTPClientMacKey value:JSON[@"mac_key"]];
- if ([weakSelf.delegate respondsToSelector:@selector(httpClientDidRegisterWithBaseURL:)]) {
- [weakSelf.delegate httpClientDidRegisterWithBaseURL:self];
+ if ([self.delegate respondsToSelector:@selector(httpClientDidRegisterWithBaseURL:)]) {
+ [self.delegate httpClientDidRegisterWithBaseURL:self];
}
- if (weakSelf.registrationSuccessBlock) {
- weakSelf.registrationSuccessBlock();
+ if (self.registrationSuccessBlock) {
+ self.registrationSuccessBlock();
}
}
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
- if (weakSelf.registrationFailureBlock) {
- weakSelf.registrationFailureBlock(error);
+ if (self.registrationFailureBlock) {
+ self.registrationFailureBlock(error);
}
}];
@@ -249,7 +248,7 @@ - (BOOL)handleOpenURL:(NSURL *)url
- (NSString *)authorizationHeaderForRequest:(NSURLRequest *)request
{
- if (self.tentAccessToken.length == 0 && self.tentMacKeyId.length == 0) {
+ if (![self keychainKeyExistsForCurrentBaseURL:TPTentHTTPClientAccessToken] && ![self keychainKeyExistsForCurrentBaseURL:TPTentHTTPClientMacKeyId]) {
return nil;
}
@@ -270,8 +269,12 @@ - (NSString *)authorizationHeaderForRequest:(NSURLRequest *)request
}
[normalisedRequestString appendFormat:@"%@\n\n", port];
- NSString *mac = [NSString HMACSHA256EncodedStringWithString:normalisedRequestString key:self.tentMacKey];
- NSString *authorizationId = (self.tentAccessToken.length > 0) ? self.tentAccessToken : self.tentMacKeyId;
+ NSString *mac = [NSString HMACSHA256EncodedStringWithString:normalisedRequestString key:[self valueForKeychainKey:TPTentHTTPClientMacKey]];
+ BOOL foundAccessToken;
+ NSString *authorizationId = [self valueForKeychainKey:TPTentHTTPClientAccessToken found:&foundAccessToken];
+ if (!foundAccessToken) {
+ [self valueForKeychainKey:TPTentHTTPClientMacKeyId];
+ }
return [NSString stringWithFormat:@"MAC id=\"%@\", ts=\"%@\", nonce=\"%@\", mac=\"%@\"", authorizationId, timeStamp, nonce, mac];
}
@@ -297,7 +300,72 @@ - (BOOL)mainBundleContainsURLScheme:(NSString *)urlScheme
return NO;
}
+#pragma mark Keychain
+
+- (NSMutableDictionary *)queryAttributesForKeychainKey:(TPTentHTTPClientKeychainKey)key
+{
+ NSMutableDictionary *queryDictionary = [NSMutableDictionary dictionary];
+ [queryDictionary setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
+
+ // Incorporate baseURL into keyname to support storing creds for multiple tent servers
+ NSString *tag = [NSString stringWithFormat:@"com.thoughtfulpixel.tptenthttpclient.keychainkey.%d.%@", key, [self.baseURL absoluteString]];
+
+ CFDataRef tagRef = (__bridge CFDataRef)[tag dataUsingEncoding:NSUTF8StringEncoding];
+ [queryDictionary setObject:(__bridge id)tagRef forKey:(__bridge id)kSecAttrApplicationTag];
+
+ return queryDictionary;
+}
+
+- (NSString *)valueForKeychainKey:(TPTentHTTPClientKeychainKey)key
+{
+ BOOL found;
+ NSString *value = [self valueForKeychainKey:key found:&found];
+ if (!found) {
+ return nil;
+ }
+ return value;
+}
+
+- (NSString *)valueForKeychainKey:(TPTentHTTPClientKeychainKey)key found:(BOOL *)found
+{
+ NSMutableDictionary *attributesToQuery = [self queryAttributesForKeychainKey:key];
+ [attributesToQuery setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id<NSCopying>)(kSecReturnData)];
+
+ CFTypeRef resultRef = NULL;
+ OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)attributesToQuery, &resultRef);
+ *found = status != errSecItemNotFound;
+
+ return [[NSString alloc] initWithData:(__bridge_transfer NSData *)resultRef encoding:NSUTF8StringEncoding];
+}
+- (BOOL)keychainKeyExistsForCurrentBaseURL:(TPTentHTTPClientKeychainKey)key
+{
+ BOOL found;
+ [self valueForKeychainKey:key found:&found];
+ return found;
+}
+
+- (BOOL)setKeychainKey:(TPTentHTTPClientKeychainKey)key value:(NSString *)value
+{
+ [self deleteKeychainKey:key];
+
+ NSMutableDictionary *attributes = [self queryAttributesForKeychainKey:key];
+
+ CFDataRef dataRef = (__bridge CFDataRef)[value dataUsingEncoding:NSUTF8StringEncoding];
+ [attributes setObject:(__bridge id)dataRef forKey:(__bridge id)kSecValueData];
+
+ [attributes setObject:(__bridge id)kSecAttrAccessibleWhenUnlocked forKey:(__bridge id)kSecAttrAccessible];
+
+ OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
+ return status == noErr;
+}
+- (BOOL)deleteKeychainKey:(TPTentHTTPClientKeychainKey)key
+{
+ NSMutableDictionary *attributesToQuery = [self queryAttributesForKeychainKey:key];
+
+ OSStatus status = SecItemDelete((__bridge CFDictionaryRef)attributesToQuery);
+ return status == noErr;
+}
@end

0 comments on commit 545c7c0

Please sign in to comment.
Something went wrong with that request. Please try again.