diff --git a/BDBOAuth1Manager.podspec b/BDBOAuth1Manager.podspec index a427e3b..6cd08df 100644 --- a/BDBOAuth1Manager.podspec +++ b/BDBOAuth1Manager.podspec @@ -9,11 +9,11 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/bdbergeron/BDBOAuth1Manager.git', :tag => s.version.to_s } s.requires_arc = true - s.ios.deployment_target = '6.0' + s.ios.deployment_target = '7.0' s.osx.deployment_target = '10.8' s.source_files = 'BDBOAuth1Manager/**/*.{h,m}' - s.dependency 'AFNetworking/NSURLConnection', '~> 2.5.0' - s.dependency 'AFNetworking/NSURLSession', '~> 2.5.0' + s.dependency 'AFNetworking/NSURLConnection', '~> 2.6.0' + s.dependency 'AFNetworking/NSURLSession', '~> 2.6.0' end diff --git a/BDBOAuth1Manager/BDBOAuth1RequestSerializer.h b/BDBOAuth1Manager/BDBOAuth1RequestSerializer.h index 604587a..828c35c 100644 --- a/BDBOAuth1Manager/BDBOAuth1RequestSerializer.h +++ b/BDBOAuth1Manager/BDBOAuth1RequestSerializer.h @@ -164,6 +164,34 @@ FOUNDATION_EXPORT NSString * const BDBOAuth1OAuthCallbackParameter; consumerKey:(NSString *)consumerKey consumerSecret:(NSString *)consumerSecret; +#if TARGET_OS_IPHONE +/** + * Create a new BDBOAuth1RequestSerializer instance for the given service with its consumerKey and RSAPrivateKey + * + * @param service Service (base URL) this request serializer is used for. + * @param consumerKey OAuth consumer key + * @param RSAPrivateKey RSA private key + * + * @return New BDBOAuth1RequestSerializer for the specified service + */ ++ (instancetype)serializerForService:(NSString *)service + withConsumerKey:(NSString *)consumerKey + RSAPrivateKey:(id)RSAPrivateKey; + +/** + * Instantiate a new BDBOAuth1RequestSerializer instance for the given service with its consumerKey and RSAPrivateKey + * + * @param service Service (base URL) this request serializer is used for. + * @param consumerKey OAuth consumer key + * @param RSAPrivateKey RSA private key + * + * @return New BDBOAuth1RequestSerializer for the specified service + */ +- (instancetype)initWithService:(NSString *)service + consumerKey:(NSString *)consumerKey + RSAPrivateKey:(id)RSAPrivateKey; +#endif + /** * --------------------------------------------------------------------------------------- diff --git a/BDBOAuth1Manager/BDBOAuth1RequestSerializer.m b/BDBOAuth1Manager/BDBOAuth1RequestSerializer.m index 18323fa..4a47ce2 100644 --- a/BDBOAuth1Manager/BDBOAuth1RequestSerializer.m +++ b/BDBOAuth1Manager/BDBOAuth1RequestSerializer.m @@ -173,6 +173,7 @@ @interface BDBOAuth1RequestSerializer () @property (nonatomic, copy) NSString *service; @property (nonatomic, copy) NSString *consumerKey; @property (nonatomic, copy) NSString *consumerSecret; +@property (nonatomic, copy) id RSAPrivateKey; - (NSString *)OAuthSignatureForMethod:(NSString *)method URLString:(NSString *)URLString @@ -214,6 +215,25 @@ - (instancetype)initWithService:(NSString *)service return self; } ++ (instancetype)serializerForService:(NSString *)service + withConsumerKey:(NSString *)consumerKey + RSAPrivateKey:(id)RSAPrivateKey { + return [[[self class] alloc] initWithService:service consumerKey:consumerKey RSAPrivateKey:RSAPrivateKey]; +} + +- (instancetype)initWithService:(NSString *)service + consumerKey:(NSString *)consumerKey + RSAPrivateKey:(id)RSAPrivateKey { + self = [super init]; + + if (self) { + _service = service; + _consumerKey = consumerKey; + _RSAPrivateKey = RSAPrivateKey; + } + return self; +} + #pragma mark Storing the Access Token static NSDictionary *OAuthKeychainDictionaryForService(NSString *service) { return @{(__bridge id)kSecClass:(__bridge id)kSecClassGenericPassword, @@ -287,7 +307,12 @@ - (NSDictionary *)OAuthParameters { parameters[BDBOAuth1SignatureVersionParameter] = @"1.0"; parameters[BDBOAuth1SignatureConsumerKeyParameter] = self.consumerKey; parameters[BDBOAuth1SignatureTimestampParameter] = [@(floor([[NSDate date] timeIntervalSince1970])) stringValue]; - parameters[BDBOAuth1SignatureMethodParameter] = @"HMAC-SHA1"; + + if (self.RSAPrivateKey) { + parameters[BDBOAuth1SignatureMethodParameter] = @"RSA-SHA1"; + } else { + parameters[BDBOAuth1SignatureMethodParameter] = @"HMAC-SHA1"; + } CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); CFUUIDBytes uuidBytes = CFUUIDGetUUIDBytes(uuid); @@ -302,14 +327,7 @@ - (NSDictionary *)OAuthParameters { return parameters; } -- (NSString *)OAuthSignatureForMethod:(NSString *)method - URLString:(NSString *)URLString - parameters:(NSDictionary *)parameters - error:(NSError *__autoreleasing *)error { - NSMutableURLRequest *request = [super requestWithMethod:@"GET" URLString:URLString parameters:parameters error:error]; - - [request setHTTPMethod:method]; - +- (NSString *)OAuthHMACSignatureForRequestData:(NSData *)requestData { NSString *secret = @""; if (self.accessToken) { @@ -321,6 +339,43 @@ - (NSString *)OAuthSignatureForMethod:(NSString *)method NSString *secretString = [[self.consumerSecret bdb_URLEncode] stringByAppendingFormat:@"&%@", [secret bdb_URLEncode]]; NSData *secretData = [secretString dataUsingEncoding:NSUTF8StringEncoding]; + uint8_t digest[CC_SHA1_DIGEST_LENGTH]; + CCHmacContext context; + CCHmacInit(&context, kCCHmacAlgSHA1, [secretData bytes], [secretData length]); + CCHmacUpdate(&context, [requestData bytes], [requestData length]); + CCHmacFinal(&context, digest); + +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) + return [[NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH] base64EncodedStringWithOptions:0]; +#else + return [[NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH] base64Encoding]; +#endif +} + +#if TARGET_OS_IPHONE +- (NSString *)OAuthRSASignatureForRequestData:(NSData*)requestData { + uint8_t digest[CC_SHA1_DIGEST_LENGTH]; + CC_SHA1([requestData bytes], (unsigned int)[requestData length], digest); + + size_t signatureSize = SecKeyGetBlockSize((__bridge SecKeyRef)self.RSAPrivateKey); + uint8_t signature[signatureSize]; + SecKeyRawSign((__bridge SecKeyRef)self.RSAPrivateKey, kSecPaddingPKCS1SHA1, digest, sizeof(digest), signature, &signatureSize); +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) + return [[NSData dataWithBytes:signature length:signatureSize] base64EncodedStringWithOptions:0]; +#else + return [[NSData dataWithBytes:signature length:signatureSize] base64Encoding]; +#endif +} +#endif + +- (NSString *)OAuthSignatureForMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + error:(NSError *__autoreleasing *)error { + NSMutableURLRequest *request = [super requestWithMethod:@"GET" URLString:URLString parameters:parameters error:error]; + + [request setHTTPMethod:method]; + /** * Create signature from request data * @@ -334,21 +389,19 @@ - (NSString *)OAuthSignatureForMethod:(NSString *)method NSString *requestURL = [[[[request URL] absoluteString] componentsSeparatedByString:@"?"][0] bdb_URLEncode]; NSArray *sortedQueryString = [[[[request URL] query] componentsSeparatedByString:@"&"] sortedArrayUsingSelector:@selector(compare:)]; - NSString *queryString = [[sortedQueryString componentsJoinedByString:@"&"] bdb_URLEncode]; + NSString *queryString = [[[sortedQueryString componentsJoinedByString:@"&"] bdb_URLEncodeSlashesAndQuestionMarks] bdb_URLEncode]; NSString *requestString = [NSString stringWithFormat:@"%@&%@&%@", requestMethod, requestURL, queryString]; NSData *requestData = [requestString dataUsingEncoding:NSUTF8StringEncoding]; - uint8_t digest[CC_SHA1_DIGEST_LENGTH]; - CCHmacContext context; - CCHmacInit(&context, kCCHmacAlgSHA1, [secretData bytes], [secretData length]); - CCHmacUpdate(&context, [requestData bytes], [requestData length]); - CCHmacFinal(&context, digest); - -#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) - return [[NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH] base64EncodedStringWithOptions:0]; +#if TARGET_OS_IPHONE + if (self.RSAPrivateKey) { + return [self OAuthRSASignatureForRequestData:requestData]; + } else { + return [self OAuthHMACSignatureForRequestData:requestData]; + } #else - return [[NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH] base64Encoding]; + return [self OAuthHMACSignatureForRequestData:requestData]; #endif } @@ -369,7 +422,7 @@ - (NSString *)OAuthAuthorizationHeaderForMethod:(NSString *)method NSMutableDictionary *mutableAuthorizationParameters = [NSMutableDictionary dictionary]; - if (self.consumerKey && self.consumerSecret) { + if (self.consumerKey && (self.consumerSecret || self.RSAPrivateKey)) { [mutableAuthorizationParameters addEntriesFromDictionary:[self OAuthParameters]]; NSString *token = self.accessToken.token; diff --git a/BDBOAuth1Manager/Categories/NSString+BDBOAuth1Manager.h b/BDBOAuth1Manager/Categories/NSString+BDBOAuth1Manager.h index a24b43d..c5c39fa 100644 --- a/BDBOAuth1Manager/Categories/NSString+BDBOAuth1Manager.h +++ b/BDBOAuth1Manager/Categories/NSString+BDBOAuth1Manager.h @@ -55,4 +55,14 @@ - (NSString *)bdb_URLEncode; + +/** + * Returns the given string with the '/' and '?' characters URL-encoded. + * + * AFNetworking 2.6 no longer encodes '/' and '?' characters. See https://github.com/AFNetworking/AFNetworking/pull/2908 + * + * @return '?' and '/' URL-encoded string + */ +- (NSString *)bdb_URLEncodeSlashesAndQuestionMarks; + @end diff --git a/BDBOAuth1Manager/Categories/NSString+BDBOAuth1Manager.m b/BDBOAuth1Manager/Categories/NSString+BDBOAuth1Manager.m index b240e36..160c190 100644 --- a/BDBOAuth1Manager/Categories/NSString+BDBOAuth1Manager.m +++ b/BDBOAuth1Manager/Categories/NSString+BDBOAuth1Manager.m @@ -46,4 +46,10 @@ - (NSString *)bdb_URLDecode { kCFStringEncodingUTF8); } +- (NSString *)bdb_URLEncodeSlashesAndQuestionMarks { + NSString *selfWithSlashesEscaped = [self stringByReplacingOccurrencesOfString:@"/" withString:@"%2F"]; + NSString *selfWithQuestionMarksEscaped = [selfWithSlashesEscaped stringByReplacingOccurrencesOfString:@"?" withString:@"%3F"]; + return selfWithQuestionMarksEscaped; +} + @end diff --git a/BDBOAuth1ManagerDemo/BDBOAuth1ManagerDemo.xcodeproj/project.pbxproj b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerDemo.xcodeproj/project.pbxproj index effed68..b9755bb 100644 --- a/BDBOAuth1ManagerDemo/BDBOAuth1ManagerDemo.xcodeproj/project.pbxproj +++ b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerDemo.xcodeproj/project.pbxproj @@ -7,6 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + 05022751EC3D08B7DCB1D614 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8241A861A961B246A8EEBF16 /* libPods.a */; }; + 1F1F009767F0F6E090D7BECA /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8241A861A961B246A8EEBF16 /* libPods.a */; }; + 398963E796E95A7C01D298CB /* libPods-osx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 577F1C9F5AB778827D69A425 /* libPods-osx.a */; }; + 96CDCC59BDDC25F2334E832A /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8241A861A961B246A8EEBF16 /* libPods.a */; }; + D91E288F1B2648A7005F9455 /* BDBOAuth1ManagerTests_iOS.m in Sources */ = {isa = PBXBuildFile; fileRef = D91E288E1B2648A7005F9455 /* BDBOAuth1ManagerTests_iOS.m */; }; + D91E28A91B278AE1005F9455 /* BDBOAuth1ManagerTests_OSX.m in Sources */ = {isa = PBXBuildFile; fileRef = D91E28A81B278AE1005F9455 /* BDBOAuth1ManagerTests_OSX.m */; }; E816CAD519565291009877A6 /* BDBTwitterClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E816CAD419565291009877A6 /* BDBTwitterClient.m */; }; E8284C8518464EF900B3C6AA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E8284C6918464ED900B3C6AA /* AppDelegate.m */; }; E8284C8F18464EF900B3C6AA /* PhotosViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E8284C7418464ED900B3C6AA /* PhotosViewController.m */; }; @@ -14,8 +20,6 @@ E8284CA618464EF900B3C6AA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E8284C6D18464ED900B3C6AA /* InfoPlist.strings */; }; E8284CB01846500000B3C6AA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E8F376351808769B00C40148 /* InfoPlist.strings */; }; E847027019D4DD2E00C10210 /* Flickr.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E847026F19D4DD2E00C10210 /* Flickr.xcassets */; }; - E86A44DF19244C4400700A9C /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E86A44DE19244C4400700A9C /* libPods.a */; }; - E86A44E219244C9900700A9C /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E86A44DE19244C4400700A9C /* libPods.a */; }; E88595A8184A751500BD7AEC /* BDBFlickrClient.m in Sources */ = {isa = PBXBuildFile; fileRef = E88595A7184A751500BD7AEC /* BDBFlickrClient.m */; }; E8C6BAA119D4DDC300A2102A /* Launch Screen.xib in Resources */ = {isa = PBXBuildFile; fileRef = E8C6BAA019D4DDC300A2102A /* Launch Screen.xib */; }; E8C6BAA319D4E02E00A2102A /* Twitter.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E8C6BAA219D4E02E00A2102A /* Twitter.xcassets */; }; @@ -35,8 +39,18 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 33E03F5490FE80216781BCDE /* Pods-osx.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-osx.release.xcconfig"; path = "Pods/Target Support Files/Pods-osx/Pods-osx.release.xcconfig"; sourceTree = ""; }; 3C23EA492A8642587406840B /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + 577F1C9F5AB778827D69A425 /* libPods-osx.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-osx.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 8241A861A961B246A8EEBF16 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + AA2776B2923D583815CE75A5 /* Pods-osx.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-osx.debug.xcconfig"; path = "Pods/Target Support Files/Pods-osx/Pods-osx.debug.xcconfig"; sourceTree = ""; }; D4E42A9BCC3D91597BD0D127 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + D91E288A1B2648A7005F9455 /* BDBOAuth1ManagerTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "BDBOAuth1ManagerTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + D91E288D1B2648A7005F9455 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D91E288E1B2648A7005F9455 /* BDBOAuth1ManagerTests_iOS.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BDBOAuth1ManagerTests_iOS.m; sourceTree = ""; }; + D91E28A41B278AE1005F9455 /* BDBOAuth1ManagerTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "BDBOAuth1ManagerTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + D91E28A71B278AE1005F9455 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D91E28A81B278AE1005F9455 /* BDBOAuth1ManagerTests_OSX.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BDBOAuth1ManagerTests_OSX.m; sourceTree = ""; }; E816CAD319565291009877A6 /* BDBTwitterClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BDBTwitterClient.h; path = Twitter/BDBTwitterClient.h; sourceTree = ""; }; E816CAD419565291009877A6 /* BDBTwitterClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BDBTwitterClient.m; path = Twitter/BDBTwitterClient.m; sourceTree = ""; }; E8284C6818464ED900B3C6AA /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -49,7 +63,6 @@ E8284C7418464ED900B3C6AA /* PhotosViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhotosViewController.m; sourceTree = ""; }; E8284CAE18464EF900B3C6AA /* Flickr Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Flickr Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; E847026F19D4DD2E00C10210 /* Flickr.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Flickr.xcassets; sourceTree = ""; }; - E86A44DE19244C4400700A9C /* libPods.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libPods.a; path = "Pods/build/Debug-iphoneos/libPods.a"; sourceTree = ""; }; E88595A6184A751500BD7AEC /* BDBFlickrClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BDBFlickrClient.h; sourceTree = ""; }; E88595A7184A751500BD7AEC /* BDBFlickrClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BDBFlickrClient.m; sourceTree = ""; }; E8C6BAA019D4DDC300A2102A /* Launch Screen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = "Launch Screen.xib"; sourceTree = ""; }; @@ -83,11 +96,27 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + D91E28871B2648A7005F9455 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1F1F009767F0F6E090D7BECA /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D91E28A11B278AE1005F9455 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 398963E796E95A7C01D298CB /* libPods-osx.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; E8284CA118464EF900B3C6AA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E86A44E219244C9900700A9C /* libPods.a in Frameworks */, + 05022751EC3D08B7DCB1D614 /* libPods.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -95,13 +124,56 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E86A44DF19244C4400700A9C /* libPods.a in Frameworks */, + 96CDCC59BDDC25F2334E832A /* libPods.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 9E5F78392547B0A56FE1BFB6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8241A861A961B246A8EEBF16 /* libPods.a */, + 577F1C9F5AB778827D69A425 /* libPods-osx.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + D91E288B1B2648A7005F9455 /* BDBOAuth1ManagerTests-iOS */ = { + isa = PBXGroup; + children = ( + D91E288E1B2648A7005F9455 /* BDBOAuth1ManagerTests_iOS.m */, + D91E288C1B2648A7005F9455 /* Supporting Files */, + ); + path = "BDBOAuth1ManagerTests-iOS"; + sourceTree = ""; + }; + D91E288C1B2648A7005F9455 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D91E288D1B2648A7005F9455 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D91E28A51B278AE1005F9455 /* BDBOAuth1ManagerTests-OSX */ = { + isa = PBXGroup; + children = ( + D91E28A81B278AE1005F9455 /* BDBOAuth1ManagerTests_OSX.m */, + D91E28A61B278AE1005F9455 /* Supporting Files */, + ); + path = "BDBOAuth1ManagerTests-OSX"; + sourceTree = ""; + }; + D91E28A61B278AE1005F9455 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + D91E28A71B278AE1005F9455 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; E816CAD21956525D009877A6 /* Twitter */ = { isa = PBXGroup; children = ( @@ -203,9 +275,11 @@ children = ( E8284C6718464ED900B3C6AA /* Flickr Demo */, E8F375CE1808743300C40148 /* Twitter Demo */, + D91E288B1B2648A7005F9455 /* BDBOAuth1ManagerTests-iOS */, + D91E28A51B278AE1005F9455 /* BDBOAuth1ManagerTests-OSX */, E8F375C61808743300C40148 /* Products */, - E86A44DE19244C4400700A9C /* libPods.a */, FC3EB9D2479E3EF337045269 /* Pods */, + 9E5F78392547B0A56FE1BFB6 /* Frameworks */, ); sourceTree = ""; }; @@ -214,6 +288,8 @@ children = ( E8F375C51808743300C40148 /* Twitter Demo.app */, E8284CAE18464EF900B3C6AA /* Flickr Demo.app */, + D91E288A1B2648A7005F9455 /* BDBOAuth1ManagerTests-iOS.xctest */, + D91E28A41B278AE1005F9455 /* BDBOAuth1ManagerTests-OSX.xctest */, ); name = Products; sourceTree = ""; @@ -261,6 +337,8 @@ children = ( D4E42A9BCC3D91597BD0D127 /* Pods.debug.xcconfig */, 3C23EA492A8642587406840B /* Pods.release.xcconfig */, + AA2776B2923D583815CE75A5 /* Pods-osx.debug.xcconfig */, + 33E03F5490FE80216781BCDE /* Pods-osx.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -268,6 +346,44 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + D91E28891B2648A7005F9455 /* BDBOAuth1ManagerTests-iOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = D91E28901B2648A7005F9455 /* Build configuration list for PBXNativeTarget "BDBOAuth1ManagerTests-iOS" */; + buildPhases = ( + CE050617FF0206A97C3315F3 /* Check Pods Manifest.lock */, + D91E28861B2648A7005F9455 /* Sources */, + D91E28871B2648A7005F9455 /* Frameworks */, + D91E28881B2648A7005F9455 /* Resources */, + 71E1881C9EEC7FDA60581922 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "BDBOAuth1ManagerTests-iOS"; + productName = BDBOAuth1ManagerTests; + productReference = D91E288A1B2648A7005F9455 /* BDBOAuth1ManagerTests-iOS.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + D91E28A31B278AE1005F9455 /* BDBOAuth1ManagerTests-OSX */ = { + isa = PBXNativeTarget; + buildConfigurationList = D91E28AC1B278AE1005F9455 /* Build configuration list for PBXNativeTarget "BDBOAuth1ManagerTests-OSX" */; + buildPhases = ( + 48181B1AA5EA993CC1DD39BE /* Check Pods Manifest.lock */, + D91E28A01B278AE1005F9455 /* Sources */, + D91E28A11B278AE1005F9455 /* Frameworks */, + D91E28A21B278AE1005F9455 /* Resources */, + 6724F647A8C0E150E87A646B /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "BDBOAuth1ManagerTests-OSX"; + productName = "BDBOAuth1ManagerTests-OSX"; + productReference = D91E28A41B278AE1005F9455 /* BDBOAuth1ManagerTests-OSX.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; E8284C8118464EF900B3C6AA /* Flickr Demo */ = { isa = PBXNativeTarget; buildConfigurationList = E8284CAB18464EF900B3C6AA /* Build configuration list for PBXNativeTarget "Flickr Demo" */; @@ -314,6 +430,14 @@ attributes = { LastUpgradeCheck = 0510; ORGANIZATIONNAME = "Bradley David Bergeron"; + TargetAttributes = { + D91E28891B2648A7005F9455 = { + CreatedOnToolsVersion = 6.3.2; + }; + D91E28A31B278AE1005F9455 = { + CreatedOnToolsVersion = 6.3.2; + }; + }; }; buildConfigurationList = E8F375C01808743300C40148 /* Build configuration list for PBXProject "BDBOAuth1ManagerDemo" */; compatibilityVersion = "Xcode 3.2"; @@ -330,11 +454,27 @@ targets = ( E8F375C41808743300C40148 /* Twitter Demo */, E8284C8118464EF900B3C6AA /* Flickr Demo */, + D91E28891B2648A7005F9455 /* BDBOAuth1ManagerTests-iOS */, + D91E28A31B278AE1005F9455 /* BDBOAuth1ManagerTests-OSX */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + D91E28881B2648A7005F9455 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D91E28A21B278AE1005F9455 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; E8284CA418464EF900B3C6AA /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -390,6 +530,21 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; showEnvVarsInLog = 0; }; + 48181B1AA5EA993CC1DD39BE /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; 5DD86A06D72F47E49726F4A5 /* Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -405,6 +560,36 @@ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; + 6724F647A8C0E150E87A646B /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-osx/Pods-osx-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 71E1881C9EEC7FDA60581922 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; B7F71007CEE3472886B6578E /* Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -420,9 +605,40 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; showEnvVarsInLog = 0; }; + CE050617FF0206A97C3315F3 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + D91E28861B2648A7005F9455 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D91E288F1B2648A7005F9455 /* BDBOAuth1ManagerTests_iOS.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D91E28A01B278AE1005F9455 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D91E28A91B278AE1005F9455 /* BDBOAuth1ManagerTests_OSX.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; E8284C8218464EF900B3C6AA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -474,6 +690,104 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + D91E28911B2648A7005F9455 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D4E42A9BCC3D91597BD0D127 /* Pods.debug.xcconfig */; + buildSettings = { + CLANG_WARN_UNREACHABLE_CODE = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "BDBOAuth1ManagerTests-iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + D91E28921B2648A7005F9455 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3C23EA492A8642587406840B /* Pods.release.xcconfig */; + buildSettings = { + CLANG_WARN_UNREACHABLE_CODE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "BDBOAuth1ManagerTests-iOS/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + D91E28AD1B278AE1005F9455 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AA2776B2923D583815CE75A5 /* Pods-osx.debug.xcconfig */; + buildSettings = { + CLANG_WARN_UNREACHABLE_CODE = YES; + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "BDBOAuth1ManagerTests-OSX/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + D91E28AE1B278AE1005F9455 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E03F5490FE80216781BCDE /* Pods-osx.release.xcconfig */; + buildSettings = { + CLANG_WARN_UNREACHABLE_CODE = YES; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INFOPLIST_FILE = "BDBOAuth1ManagerTests-OSX/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; E8284CAC18464EF900B3C6AA /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = D4E42A9BCC3D91597BD0D127 /* Pods.debug.xcconfig */; @@ -599,6 +913,10 @@ GCC_PREFIX_HEADER = "Twitter Demo/Supporting Files/Twitter Demo-Prefix.pch"; INFOPLIST_FILE = "$(SRCROOT)/Twitter Demo/Supporting Files/Twitter Demo-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); PRODUCT_NAME = "Twitter Demo"; TARGETED_DEVICE_FAMILY = 1; WARNING_CFLAGS = "-Wall"; @@ -617,6 +935,10 @@ GCC_PREFIX_HEADER = "Twitter Demo/Supporting Files/Twitter Demo-Prefix.pch"; INFOPLIST_FILE = "$(SRCROOT)/Twitter Demo/Supporting Files/Twitter Demo-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug-iphoneos", + ); PRODUCT_NAME = "Twitter Demo"; TARGETED_DEVICE_FAMILY = 1; WARNING_CFLAGS = "-Wall"; @@ -627,6 +949,24 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + D91E28901B2648A7005F9455 /* Build configuration list for PBXNativeTarget "BDBOAuth1ManagerTests-iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D91E28911B2648A7005F9455 /* Debug */, + D91E28921B2648A7005F9455 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D91E28AC1B278AE1005F9455 /* Build configuration list for PBXNativeTarget "BDBOAuth1ManagerTests-OSX" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D91E28AD1B278AE1005F9455 /* Debug */, + D91E28AE1B278AE1005F9455 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; E8284CAB18464EF900B3C6AA /* Build configuration list for PBXNativeTarget "Flickr Demo" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-OSX/BDBOAuth1ManagerTests_OSX.m b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-OSX/BDBOAuth1ManagerTests_OSX.m new file mode 100644 index 0000000..c6a1aa1 --- /dev/null +++ b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-OSX/BDBOAuth1ManagerTests_OSX.m @@ -0,0 +1,63 @@ +// +// BDBOAuth1ManagerTests_OSX.m +// BDBOAuth1ManagerTests-OSX +// +// Created by Shawn Kim on 6/9/15. +// Copyright (c) 2015 Bradley David Bergeron. All rights reserved. +// + +#import +#import +#import "BDBOAuth1RequestSerializer.h" + +@interface BDBOAuth1RequestSerializer () + +- (NSString *)OAuthSignatureForMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + error:(NSError *__autoreleasing *)error; + +@end + +@interface BDBOAuth1ManagerTests_OSX : XCTestCase + +@end + +@implementation BDBOAuth1ManagerTests_OSX + +// Test cases from http://wiki.oauth.net/w/page/12238556/TestCases + +- (void) testOAuth1HMACSHA1Signatures { + NSString *expectedSignature = @"tR3+Ty81lMeYAr/Fid0kMTYa/WM="; + + NSString *service = @"http://photos.example.net"; + NSString *path = @"photos"; + + NSString *consumerKey = @"dpf43f3p2l4k3l03"; + NSString *consumerSecret = @"kd94hf93k423kf44"; + + NSString *tokenKey = @"nnch734d00sl2jdk"; + NSString *tokenSecret = @"pfkkdhi9sl3r4s00"; + + /*Expected base string: + + GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal + */ + NSDictionary *parameters = @{ @"oauth_signature_method": @"HMAC-SHA1", + @"oauth_version": @"1.0", + @"oauth_consumer_key": consumerKey, + @"oauth_token": tokenKey, + @"oauth_timestamp": @"1191242096", + @"oauth_nonce": @"kllo9940pd9333jh", + @"file": @"vacation.jpg", + @"size": @"original" }; + + BDBOAuth1RequestSerializer *serializer = [BDBOAuth1RequestSerializer serializerForService:service withConsumerKey:consumerKey consumerSecret:consumerSecret]; + [serializer setValue:[BDBOAuth1Credential credentialWithToken:tokenKey secret:tokenSecret expiration:[NSDate dateWithTimeIntervalSinceNow:3600]] forKey:NSStringFromSelector(@selector(accessToken))]; + + NSError *error; + NSString *signature = [serializer OAuthSignatureForMethod:@"GET" URLString:[NSString stringWithFormat:@"%@/%@", service, path] parameters:parameters error:&error]; + XCTAssertEqualObjects(signature, expectedSignature, @"Computed OAuth signature does not match expected signature"); +} + +@end diff --git a/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-OSX/Info.plist b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-OSX/Info.plist new file mode 100644 index 0000000..632b7a9 --- /dev/null +++ b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-OSX/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.bradbergeron.bdboauth1test.osx + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-iOS/BDBOAuth1ManagerTests_iOS.m b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-iOS/BDBOAuth1ManagerTests_iOS.m new file mode 100644 index 0000000..ccc1112 --- /dev/null +++ b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-iOS/BDBOAuth1ManagerTests_iOS.m @@ -0,0 +1,152 @@ +// +// BDBOAuth1ManagerTests.h +// +// Copyright (c) 2014 Bradley David Bergeron +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#import +#import +#import "BDBOAuth1RequestSerializer.h" + +@interface BDBOAuth1RequestSerializer () + +- (NSString *)OAuthSignatureForMethod:(NSString *)method + URLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + error:(NSError *__autoreleasing *)error; + +@end + +@interface BDBOAuth1ManagerTests_iOS : XCTestCase + +@end + +@implementation BDBOAuth1ManagerTests_iOS + +// Test cases from http://wiki.oauth.net/w/page/12238556/TestCases + +- (void) testOAuth1HMACSHA1Signatures { + NSString *expectedSignature = @"tR3+Ty81lMeYAr/Fid0kMTYa/WM="; + + NSString *service = @"http://photos.example.net"; + NSString *path = @"photos"; + + NSString *consumerKey = @"dpf43f3p2l4k3l03"; + NSString *consumerSecret = @"kd94hf93k423kf44"; + + NSString *tokenKey = @"nnch734d00sl2jdk"; + NSString *tokenSecret = @"pfkkdhi9sl3r4s00"; + + /*Expected base string: + + GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal + */ + NSDictionary *parameters = @{ @"oauth_signature_method": @"HMAC-SHA1", + @"oauth_version": @"1.0", + @"oauth_consumer_key": consumerKey, + @"oauth_token": tokenKey, + @"oauth_timestamp": @"1191242096", + @"oauth_nonce": @"kllo9940pd9333jh", + @"file": @"vacation.jpg", + @"size": @"original" }; + + BDBOAuth1RequestSerializer *serializer = [BDBOAuth1RequestSerializer serializerForService:service withConsumerKey:consumerKey consumerSecret:consumerSecret]; + [serializer setValue:[BDBOAuth1Credential credentialWithToken:tokenKey secret:tokenSecret expiration:[NSDate dateWithTimeIntervalSinceNow:3600]] forKey:NSStringFromSelector(@selector(accessToken))]; + + NSError *error; + NSString *signature = [serializer OAuthSignatureForMethod:@"GET" URLString:[NSString stringWithFormat:@"%@/%@", service, path] parameters:parameters error:&error]; + XCTAssertEqualObjects(signature, expectedSignature, @"Computed OAuth signature does not match expected signature"); +} + +- (void) testOAuth1RSASHA1Signatures { + NSString *expectedSignature = @"jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE="; + + NSString *service = @"http://photos.example.net"; + NSString *path = @"photos"; + + /* Base64 encoded PKCS12 identity generated from: + + PKCS8 Private Key: + + -----BEGIN PRIVATE KEY----- + MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V + A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d + 7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ + hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H + X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm + uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw + rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z + zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn + qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG + WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno + cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+ + 3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8 + AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54 + Lw03eHTNQghS0A== + -----END PRIVATE KEY----- + + Certificate: + + -----BEGIN CERTIFICATE----- + MIIBpjCCAQ+gAwIBAgIBATANBgkqhkiG9w0BAQUFADAZMRcwFQYDVQQDDA5UZXN0 + IFByaW5jaXBhbDAeFw03MDAxMDEwODAwMDBaFw0zODEyMzEwODAwMDBaMBkxFzAV + BgNVBAMMDlRlc3QgUHJpbmNpcGFsMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB + gQC0YjCwIfYoprq/FQO6lb3asXrxLlJFuCvtinTF5p0GxvQGu5O3gYytUvtC2JlY + zypSRjVxwxrsuRcP3e641SdASwfrmzyvIgP08N4S0IFzEURkV1wp/IpH7kH41Etb + mUmrXSwfNZsnQRE5SYSOhh+LcK2wyQkdgcMv11l4KoBkcwIDAQABMA0GCSqGSIb3 + DQEBBQUAA4GBAGZLPEuJ5SiJ2ryq+CmEGOXfvlTtEL2nuGtr9PewxkgnOjZpUy+d + 4TvuXJbNQc8f4AMWL/tO9w0Fk80rWKp9ea8/df4qMq5qlFWlx6yOLQxumNOmECKb + WpkUQDIDJEoFUzKMVuJf4KO/FJ345+BNLGgbJ6WujreoM1X/gYfdnJ/J + -----END CERTIFICATE----- + + openssl pkcs12 -export -inkey priv.key -in cert.cer -out id.p12 | base64 + */ + NSString *pkcs12IdentityString = @"MIIFoQIBAzCCBWcGCSqGSIb3DQEHAaCCBVgEggVUMIIFUDCCAk8GCSqGSIb3DQEHBqCCAkAwggI8AgEAMIICNQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIWarzGOduQfkCAggAgIICCP3hmzrKg7zlCEKiMyW7GukMNf5uzvfCe387u565Q3CXWBvt3SYUOV9/EDPe8LVlckhyWe13UxyV1mA7wu0J6GXx2GDG1ejUIXVNNnN7BuOsHaSdvbtcEmIDehw5ztTPsyI9CIrqcWBYuT3zbiooY4IMjQIM/sxU/xT09aG4YU3Mp0EBiXFYIlt/p3HdWMbFP6rPXmxzV3zuu8IxTyNvSxdjn7y7YRrOsYq2E1Ir0EQ6J4cGolpE7z/YFG/ClCv4WMhKddCe2lFepBZ/ZXslw4baDNQTOAsQvorbwBQFH2M5X1Esm/99aoeO54kf0I79M0agtxQblluv0AgCJXDt/amT0Gw2kIx42bV9SAPnHSTUZj5TK/NNtN1ioDBkDmlO6E/LocxjQuJhEzyDDOEug9/GeKTZKk8z0AHhyRS7YtYU6yWyMAXTiXCBtH499SR/DktV4qgoTxUVWVX0Ww2JEbVjYGF7D1bhG6+/SqC1PXPDW56g9EdxMsHQ6UiLVypIeH2PgHlKowW7S6rDP5XCX0WP7gynA5QUJu+vR21ofzBnmMqouBPefHNIafChLpZ3Yf1hKPzTM2O54C5hIXT4ZZPEIH0pkN/c5WxsCY7C+Twlg1p+iCJjmLjPHrfWKF6ZVXfopyM0q0YZnaEe/a2dwWca4dFaYR1Ye7WmEpikUdIrcs1aBEPlvA0wggL5BgkqhkiG9w0BBwGgggLqBIIC5jCCAuIwggLeBgsqhkiG9w0BDAoBAqCCAqYwggKiMBwGCiqGSIb3DQEMAQMwDgQIEmugo5LoJ4UCAggABIICgOaYelT606qdQ73fYCzCGJd/zRsIIsV1lxAUXLt5eGaMO8cpQfL9l2kMmUCxpGtBVGvuvejcDJUlkjxxqmJMY5519Nz2Y6ESNGU3aX7sqCnZV7SXBpIlVrdpf+VxT0e94Pf2vsan7qNCrKpcWsWDP5ZKiBaeDi2ZjYjZIyQ7biMIyBjOitAf+w4u3hOV48d7JaCeKVAGPi4faE6EcQ6J/sPWy5asRXDhnRFdzDWz/ziXE2IgLZrrNs3Yp+qSWIg5gsOn9oCwLKIIfyxC59V7mzYUJ0q+5NsvoduX8ou02v2DCwn3aRDd2Qz++J/kk1ad6tn6EjmOk+sLKx66ztw2bGNWcBWfbAOG2QjRrp+b8A7w1pZ4Zt+qVTWGuRKnz80Za2BZx94Ml7K2GLYBCcZRBCaQtmgHQYBXx6nRfRwfqQpfNflColMZqccTcgE+/8NV8idMW6F20L51xp70uNimNTAoIVpgmGlRhNK/0DGaAVIcPuw/hXdDHlXnyW4TzhoRrnZzaPO53cmQQvvb3BpdH5Klo6I26ZXDQVt6nVZ3Yk1PUO9lVdOyFo0aWP7wnympELWu/7L9z8lwGQaa+m+IqlKsvP2q4iBQs6HNS0ssTHpXFprmeaRyfwZNoC2Uiq3gAjbn4VujgBpKpFoR7Yxh792iwq2NCtiyNhuHRpX3Hs/vMxRGuEqGLqN4bExwlsnmfk8rUW9pNLzucPACTCVf10t2oqi+Sv0dMuxQa2e5OlMiKp1D+GAeF9usNAnTOE3vmK4bCaLUQ9769xztAeKkQJ3zufBdRYB9jEIi2B5X89x8l4csGdHr2A8KAD0vJlLEXisUQdN0XkBMMYWCp/NtFeAxJTAjBgkqhkiG9w0BCRUxFgQUyxy3KzcjoJDSIjCFZZpzt9s8OvEwMTAhMAkGBSsOAwIaBQAEFO8/JDF+DcwTHzx7kp45U3udWoI/BAgQAMdvRAUJmwICCAA="; + + NSString *consumerKey = @"dpf43f3p2l4k3l03"; + + /* Expected base string: + + GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacaction.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3D13917289812797014437%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1196666512%26oauth_version%3D1.0%26size%3Doriginal + */ + NSDictionary *parameters = @{ @"oauth_signature_method": @"RSA-SHA1", + @"oauth_version": @"1.0", + @"oauth_consumer_key": consumerKey, + @"oauth_timestamp": @"1196666512", + @"oauth_nonce": @"13917289812797014437", + @"file": @"vacaction.jpg", + @"size": @"original" }; + + // Extract private key from PKCS12 identity + CFArrayRef importResults = NULL; + NSDictionary *options = @{ (__bridge id)kSecImportExportPassphrase: @"" }; + NSData *pkcs12IdentityData = [[NSData alloc] initWithBase64EncodedString:pkcs12IdentityString options:0]; + SecPKCS12Import((__bridge CFDataRef)pkcs12IdentityData, (__bridge CFDictionaryRef)options, &importResults); + SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(CFArrayGetValueAtIndex(importResults, 0), + kSecImportItemIdentity); + SecKeyRef RSAPrivateKey = NULL; + SecIdentityCopyPrivateKey(identity, &RSAPrivateKey); + + BDBOAuth1RequestSerializer *serializer = [BDBOAuth1RequestSerializer serializerForService:service withConsumerKey:consumerKey RSAPrivateKey:(__bridge_transfer id)RSAPrivateKey]; + CFRelease(RSAPrivateKey); + NSString *signature = [serializer OAuthSignatureForMethod:@"GET" URLString:[NSString stringWithFormat:@"%@/%@", service, path] parameters:parameters error:nil]; + XCTAssertEqualObjects(signature, expectedSignature, @"Computed OAuth signature does not match expected signature"); +} + +@end diff --git a/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-iOS/Info.plist b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-iOS/Info.plist new file mode 100644 index 0000000..b44c1db --- /dev/null +++ b/BDBOAuth1ManagerDemo/BDBOAuth1ManagerTests-iOS/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.bradbergeron.bdboauth1test.ios + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/BDBOAuth1ManagerDemo/Podfile b/BDBOAuth1ManagerDemo/Podfile index be61f52..b2c791f 100644 --- a/BDBOAuth1ManagerDemo/Podfile +++ b/BDBOAuth1ManagerDemo/Podfile @@ -2,8 +2,15 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '7.0' -link_with 'Twitter Demo', 'Flickr Demo' +link_with 'Twitter Demo', 'Flickr Demo', 'BDBOAuth1ManagerTests-iOS' -pod 'AFNetworking/UIKit', '~> 2.5.0' +pod 'AFNetworking/UIKit', '~> 2.6.0' pod 'BBlock/UIKit', '~> 1.2.0' pod 'BDBOAuth1Manager', :path => '../' + +target :osx, :exclusive => true do +platform :osx, '10.9' +link_with 'BDBOAuth1ManagerTests-OSX' + +pod 'BDBOAuth1Manager', :path => '../' +end diff --git a/BDBOAuth1ManagerDemo/Podfile.lock b/BDBOAuth1ManagerDemo/Podfile.lock index 3115c38..2015e05 100644 --- a/BDBOAuth1ManagerDemo/Podfile.lock +++ b/BDBOAuth1ManagerDemo/Podfile.lock @@ -1,25 +1,25 @@ PODS: - - AFNetworking/NSURLConnection (2.5.0): + - AFNetworking/NSURLConnection (2.6.0): - AFNetworking/Reachability - AFNetworking/Security - AFNetworking/Serialization - - AFNetworking/NSURLSession (2.5.0): + - AFNetworking/NSURLSession (2.6.0): - AFNetworking/Reachability - AFNetworking/Security - AFNetworking/Serialization - - AFNetworking/Reachability (2.5.0) - - AFNetworking/Security (2.5.0) - - AFNetworking/Serialization (2.5.0) - - AFNetworking/UIKit (2.5.0): + - AFNetworking/Reachability (2.6.0) + - AFNetworking/Security (2.6.0) + - AFNetworking/Serialization (2.6.0) + - AFNetworking/UIKit (2.6.0): - AFNetworking/NSURLConnection - AFNetworking/NSURLSession - BBlock/UIKit (1.2.0) - BDBOAuth1Manager (1.5.0): - - AFNetworking/NSURLConnection (~> 2.5.0) - - AFNetworking/NSURLSession (~> 2.5.0) + - AFNetworking/NSURLConnection (~> 2.6.0) + - AFNetworking/NSURLSession (~> 2.6.0) DEPENDENCIES: - - AFNetworking/UIKit (~> 2.5.0) + - AFNetworking/UIKit (~> 2.6.0) - BBlock/UIKit (~> 1.2.0) - BDBOAuth1Manager (from `../`) @@ -28,8 +28,8 @@ EXTERNAL SOURCES: :path: ../ SPEC CHECKSUMS: - AFNetworking: 0f54cb5d16ce38c1b76948faffb8d5fb705021c7 - BBlock: 90f0ae0e3ba94fc88be6b9a85b4adbb73b8a7c9a - BDBOAuth1Manager: 164b717a1123fbcfe6f8209a23823261e0ac11ee + AFNetworking: 79f7eb1a0fcfa7beb409332b2ca49afe9ce53b05 + BBlock: 11c666b6898c074eceb40fa56a901b96415ebf68 + BDBOAuth1Manager: 0c2504af494803d48ae45b6622ddd448a740ff54 -COCOAPODS: 0.34.4 +COCOAPODS: 0.38.2