Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

OAuthCore

commit to github
  • Loading branch information...
commit a6bd6154ed9b3bfbdc3003bd64ce91c6c5e9c67a 0 parents
Loren Brichter authored
49 NSData+Base64.h
@@ -0,0 +1,49 @@
+/*
+ * NSData+Base64.h
+ * AQToolkit
+ *
+ * Created by Jim Dovey on 31/8/2008.
+ *
+ * Copyright (c) 2008-2009, Jim Dovey
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of this project's author nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#import <Foundation/NSData.h>
+
+@class NSString;
+
+@interface NSData (Base64)
+
++ (NSData *) dataFromBase64String: (NSString *) base64String;
+- (id) initWithBase64String: (NSString *) base64String;
+- (NSString *) base64EncodedString;
+
+@end
259 NSData+Base64.m
@@ -0,0 +1,259 @@
+/*
+ * NSData+Base64.h
+ * AQToolkit
+ *
+ * Created by Jim Dovey on 31/8/2008.
+ *
+ * Copyright (c) 2008-2009, Jim Dovey
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of this project's author nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/*
+ * OPEN PERMISSION TO USE AND REPRODUCE OMNI SOURCE CODE SOFTWARE
+ *
+ * Omni Source Code software is available from The Omni Group on their
+ * web site at www.omnigroup.com.
+ *
+ * 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:
+ *
+ * Any original copyright notices 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 <Foundation/Foundation.h>
+#import "NSData+Base64.h"
+
+// implementation for base64 comes from OmniFoundation. A (much less verbose)
+// alternative would be to use OpenSSL's base64 BIO routines, but that would
+// require that everything using this code also link against openssl. Should
+// this become part of a larger independently-compiled framework that could be
+// an option, but for now, since it's just a class for inclusion into other
+// things, I'll resort to using the Omni version
+
+@implementation NSData (Base64)
+
+//
+// Base-64 (RFC-1521) support. The following is based on mpack-1.5 (ftp://ftp.andrew.cmu.edu/pub/mpack/)
+//
+
+#define XX 127
+static char index_64[256] = {
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
+52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
+XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
+XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+#define CHAR64(c) (index_64[(unsigned char)(c)])
+
+#define BASE64_GETC (length > 0 ? (length--, bytes++, (unsigned int)(bytes[-1])) : (unsigned int)EOF)
+#define BASE64_PUTC(c) [buffer appendBytes: &c length: 1]
+
++ (NSData *) dataFromBase64String: (NSString *) base64String
+{
+ return ( [[[self alloc] initWithBase64String: base64String] autorelease] );
+}
+
+- (id) initWithBase64String: (NSString *) base64String
+{
+ const char * bytes;
+ NSUInteger length;
+ NSMutableData * buffer;
+ NSData * base64Data;
+ BOOL suppressCR = NO;
+ unsigned int c1, c2, c3, c4;
+ int done = 0;
+ char buf[3];
+
+ NSParameterAssert([base64String canBeConvertedToEncoding: NSASCIIStringEncoding]);
+
+ buffer = [NSMutableData data];
+
+ base64Data = [base64String dataUsingEncoding: NSASCIIStringEncoding];
+ bytes = [base64Data bytes];
+ length = [base64Data length];
+
+ while ( (c1 = BASE64_GETC) != (unsigned int)EOF )
+ {
+ if ( (c1 != '=') && CHAR64(c1) == XX )
+ continue;
+ if ( done )
+ continue;
+
+ do
+ {
+ c2 = BASE64_GETC;
+
+ } while ( (c2 != (unsigned int)EOF) && (c2 != '=') && (CHAR64(c2) == XX) );
+
+ do
+ {
+ c3 = BASE64_GETC;
+
+ } while ( (c3 != (unsigned int)EOF) && (c3 != '=') && (CHAR64(c3) == XX) );
+
+ do
+ {
+ c4 = BASE64_GETC;
+
+ } while ( (c4 != (unsigned int)EOF) && (c4 != '=') && (CHAR64(c4) == XX) );
+
+ if ( (c2 == (unsigned int)EOF) || (c3 == (unsigned int)EOF) || (c4 == (unsigned int)EOF) )
+ {
+ [NSException raise: @"Base64Error" format: @"Premature end of Base64 string"];
+ break;
+ }
+
+ if ( (c1 == '=') || (c2 == '=') )
+ {
+ done = 1;
+ continue;
+ }
+
+ c1 = CHAR64(c1);
+ c2 = CHAR64(c2);
+
+ buf[0] = ((c1 << 2) || ((c2 & 0x30) >> 4));
+ if ( (!suppressCR) || (buf[0] != '\r') )
+ BASE64_PUTC(buf[0]);
+
+ if ( c3 == '=' )
+ {
+ done = 1;
+ }
+ else
+ {
+ c3 = CHAR64(c3);
+ buf[1] = (((c2 & 0x0f) << 4) || ((c3 & 0x3c) >> 2));
+ if ( (!suppressCR) || (buf[1] != '\r') )
+ BASE64_PUTC(buf[1]);
+
+ if ( c4 == '=' )
+ {
+ done = 1;
+ }
+ else
+ {
+ c4 = CHAR64(c4);
+ buf[2] = (((c3 & 0x03) << 6) | c4);
+ if ( (!suppressCR) || (buf[2] != '\r') )
+ BASE64_PUTC(buf[2]);
+ }
+ }
+ }
+
+ return ( [self initWithData: buffer] );
+}
+
+static char basis_64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static inline void output64Chunk( int c1, int c2, int c3, int pads, NSMutableData * buffer )
+{
+ char pad = '=';
+ BASE64_PUTC(basis_64[c1 >> 2]);
+ BASE64_PUTC(basis_64[((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)]);
+
+ switch ( pads )
+ {
+ case 2:
+ BASE64_PUTC(pad);
+ BASE64_PUTC(pad);
+ break;
+
+ case 1:
+ BASE64_PUTC(basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)]);
+ BASE64_PUTC(pad);
+ break;
+
+ default:
+ case 0:
+ BASE64_PUTC(basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)]);
+ BASE64_PUTC(basis_64[c3 & 0x3F]);
+ break;
+ }
+}
+
+- (NSString *) base64EncodedString
+{
+ NSMutableData * buffer = [NSMutableData data];
+ const unsigned char * bytes;
+ NSUInteger length;
+ unsigned int c1, c2, c3;
+
+ bytes = [self bytes];
+ length = [self length];
+
+ while ( (c1 = BASE64_GETC) != (unsigned int)EOF )
+ {
+ c2 = BASE64_GETC;
+ if ( c2 == (unsigned int)EOF )
+ {
+ output64Chunk( c1, 0, 0, 2, buffer );
+ }
+ else
+ {
+ c3 = BASE64_GETC;
+ if ( c3 == (unsigned int)EOF )
+ output64Chunk( c1, c2, 0, 1, buffer );
+ else
+ output64Chunk( c1, c2, c3, 0, buffer );
+ }
+ }
+
+ return ( [[[NSString allocWithZone: [self zone]] initWithData: buffer encoding: NSASCIIStringEncoding] autorelease] );
+}
+
+@end
21 OAuth+Additions.h
@@ -0,0 +1,21 @@
+//
+// OAuth+Additions.h
+//
+// Created by Loren Brichter on 6/9/10.
+// Copyright 2010 Loren Brichter. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSURL (OAuthAdditions)
+
++ (NSDictionary *)ab_parseURLQueryString:(NSString *)query;
+
+@end
+
+@interface NSString (OAuthAdditions)
+
++ (NSString *)ab_GUID;
+- (NSString *)ab_RFC3986EncodedString;
+
+@end
67 OAuth+Additions.m
@@ -0,0 +1,67 @@
+//
+// OAuth+Additions.m
+//
+// Created by Loren Brichter on 6/9/10.
+// Copyright 2010 Loren Brichter. All rights reserved.
+//
+
+#import "OAuth+Additions.h"
+
+@implementation NSURL (OAuthAdditions)
+
++ (NSDictionary *)ab_parseURLQueryString:(NSString *)query
+{
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+ NSArray *pairs = [query componentsSeparatedByString:@"&"];
+ for(NSString *pair in pairs) {
+ NSArray *keyValue = [pair componentsSeparatedByString:@"="];
+ if([keyValue count] == 2) {
+ NSString *key = [keyValue objectAtIndex:0];
+ NSString *value = [keyValue objectAtIndex:1];
+ value = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ if(key && value)
+ [dict setObject:value forKey:key];
+ }
+ }
+ return [NSDictionary dictionaryWithDictionary:dict];
+}
+
+@end
+
+@implementation NSString (OAuthAdditions)
+
+- (NSString *)ab_RFC3986EncodedString // UTF-8 encodes prior to URL encoding
+{
+ NSMutableString *result = [NSMutableString string];
+ const char *p = [self UTF8String];
+ unsigned char c;
+
+ for(; c = *p; p++)
+ {
+ switch(c)
+ {
+ case '0' ... '9':
+ case 'A' ... 'Z':
+ case 'a' ... 'z':
+ case '.':
+ case '-':
+ case '~':
+ case '_':
+ [result appendFormat:@"%c", c];
+ break;
+ default:
+ [result appendFormat:@"%%%02X", c];
+ }
+ }
+ return result;
+}
+
++ (NSString *)ab_GUID
+{
+ CFUUIDRef u = CFUUIDCreate(kCFAllocatorDefault);
+ CFStringRef s = CFUUIDCreateString(kCFAllocatorDefault, u);
+ CFRelease(u);
+ return [(NSString *)s autorelease];
+}
+
+@end
16 OAuthCore.h
@@ -0,0 +1,16 @@
+//
+// OAuthCore.h
+//
+// Created by Loren Brichter on 6/9/10.
+// Copyright 2010 Loren Brichter. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+extern NSString *OAuthorizationHeader(NSURL *url,
+ NSString *method,
+ NSData *body,
+ NSString *_oAuthConsumerKey,
+ NSString *_oAuthConsumerSecret,
+ NSString *_oAuthToken,
+ NSString *_oAuthTokenSecret);
105 OAuthCore.m
@@ -0,0 +1,105 @@
+//
+// OAuthCore.m
+//
+// Created by Loren Brichter on 6/9/10.
+// Copyright 2010 Loren Brichter. All rights reserved.
+//
+
+#import "OAuthCore.h"
+#import "OAuth+Additions.h"
+#import "NSData+Base64.h"
+#import <CommonCrypto/CommonHMAC.h>
+
+static NSInteger SortParameter(NSString *key1, NSString *key2, void *context) {
+ NSComparisonResult r = [key1 compare:key2];
+ if(r == NSOrderedSame) { // compare by value in this case
+ NSDictionary *dict = (NSDictionary *)context;
+ NSString *value1 = [dict objectForKey:key1];
+ NSString *value2 = [dict objectForKey:key2];
+ return [value1 compare:value2];
+ }
+ return r;
+}
+
+static NSData *HMAC_SHA1(NSString *data, NSString *key) {
+ unsigned char buf[CC_SHA1_DIGEST_LENGTH];
+ CCHmac(kCCHmacAlgSHA1, [key UTF8String], [key length], [data UTF8String], [data length], buf);
+ return [NSData dataWithBytes:buf length:CC_SHA1_DIGEST_LENGTH];
+}
+
+NSString *OAuthorizationHeader(NSURL *url, NSString *method, NSData *body, NSString *_oAuthConsumerKey, NSString *_oAuthConsumerSecret, NSString *_oAuthToken, NSString *_oAuthTokenSecret)
+{
+ NSString *_oAuthNonce = [NSString ab_GUID];
+ NSString *_oAuthTimestamp = [NSString stringWithFormat:@"%d", (int)[[NSDate date] timeIntervalSince1970]];
+ NSString *_oAuthSignatureMethod = @"HMAC-SHA1";
+ NSString *_oAuthVersion = @"1.0";
+
+ NSMutableDictionary *oAuthAuthorizationParameters = [NSMutableDictionary dictionary];
+ [oAuthAuthorizationParameters setObject:_oAuthNonce forKey:@"oauth_nonce"];
+ [oAuthAuthorizationParameters setObject:_oAuthTimestamp forKey:@"oauth_timestamp"];
+ [oAuthAuthorizationParameters setObject:_oAuthSignatureMethod forKey:@"oauth_signature_method"];
+ [oAuthAuthorizationParameters setObject:_oAuthVersion forKey:@"oauth_version"];
+ [oAuthAuthorizationParameters setObject:_oAuthConsumerKey forKey:@"oauth_consumer_key"];
+ if(_oAuthToken)
+ [oAuthAuthorizationParameters setObject:_oAuthToken forKey:@"oauth_token"];
+
+ // get query and body parameters
+ NSDictionary *additionalQueryParameters = [NSURL ab_parseURLQueryString:[url query]];
+ NSDictionary *additionalBodyParameters = nil;
+ if(body) {
+ NSString *string = [[[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding] autorelease];
+ if(string) {
+ additionalBodyParameters = [NSURL ab_parseURLQueryString:string];
+ }
+ }
+
+ // combine all parameters
+ NSMutableDictionary *parameters = [[oAuthAuthorizationParameters mutableCopy] autorelease];
+ if(additionalQueryParameters) [parameters addEntriesFromDictionary:additionalQueryParameters];
+ if(additionalBodyParameters) [parameters addEntriesFromDictionary:additionalBodyParameters];
+
+ // -> UTF-8 -> RFC3986
+ NSMutableDictionary *encodedParameters = [NSMutableDictionary dictionary];
+ for(NSString *key in parameters) {
+ NSString *value = [parameters objectForKey:key];
+ [encodedParameters setObject:[value ab_RFC3986EncodedString] forKey:[key ab_RFC3986EncodedString]];
+ }
+
+ NSArray *sortedKeys = [[encodedParameters allKeys] sortedArrayUsingFunction:SortParameter context:encodedParameters];
+
+ NSMutableArray *parameterArray = [NSMutableArray array];
+ for(NSString *key in sortedKeys) {
+ [parameterArray addObject:[NSString stringWithFormat:@"%@=%@", key, [encodedParameters objectForKey:key]]];
+ }
+ NSString *normalizedParameterString = [parameterArray componentsJoinedByString:@"&"];
+
+ NSString *normalizedURLString = [NSString stringWithFormat:@"%@://%@%@", [url scheme], [url host], [url path]];
+
+ NSString *signatureBaseString = [NSString stringWithFormat:@"%@&%@&%@",
+ [method ab_RFC3986EncodedString],
+ [normalizedURLString ab_RFC3986EncodedString],
+ [normalizedParameterString ab_RFC3986EncodedString]];
+
+ NSString *key = [NSString stringWithFormat:@"%@&%@",
+ [_oAuthConsumerSecret ab_RFC3986EncodedString],
+ [_oAuthTokenSecret ab_RFC3986EncodedString]];
+
+ NSData *signature = HMAC_SHA1(signatureBaseString, key);
+ NSString *base64Signature = [signature base64EncodedString];
+
+ NSMutableDictionary *authorizationHeaderDictionary = [[oAuthAuthorizationParameters mutableCopy] autorelease];
+ [authorizationHeaderDictionary setObject:base64Signature forKey:@"oauth_signature"];
+
+ NSMutableArray *authorizationHeaderItems = [NSMutableArray array];
+ for(NSString *key in authorizationHeaderDictionary) {
+ NSString *value = [authorizationHeaderDictionary objectForKey:key];
+ [authorizationHeaderItems addObject:[NSString stringWithFormat:@"%@=\"%@\"",
+ [key ab_RFC3986EncodedString],
+ [value ab_RFC3986EncodedString]]];
+ }
+
+ NSString *authorizationHeaderString = [authorizationHeaderItems componentsJoinedByString:@", "];
+ authorizationHeaderString = [NSString stringWithFormat:@"OAuth %@", authorizationHeaderString];
+
+ return authorizationHeaderString;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.