github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/clients/ios-objc/photobackup/LACamliClient/LACamliUtil.m (about)

     1  //
     2  //  LACamliUtil.m
     3  //  photobackup
     4  //
     5  //  Created by Nick O'Neill on 11/29/13.
     6  //  Copyright (c) 2013 The Camlistore Authors. All rights reserved.
     7  //
     8  
     9  #import "LACamliUtil.h"
    10  #import <CommonCrypto/CommonDigest.h>
    11  
    12  @implementation LACamliUtil
    13  
    14  static NSString *const serviceName = @"org.camlistore.credentials";
    15  
    16  // h/t AFNetworking
    17  + (NSString *)base64EncodedStringFromString:(NSString *)string
    18  {
    19      NSData *data = [NSData dataWithBytes:[string UTF8String] length:[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
    20      NSUInteger length = [data length];
    21      NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
    22  
    23      uint8_t *input = (uint8_t *)[data bytes];
    24      uint8_t *output = (uint8_t *)[mutableData mutableBytes];
    25  
    26      for (NSUInteger i = 0; i < length; i += 3) {
    27          NSUInteger value = 0;
    28          for (NSUInteger j = i; j < (i + 3); j++) {
    29              value <<= 8;
    30              if (j < length) {
    31                  value |= (0xFF & input[j]);
    32              }
    33          }
    34  
    35          static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    36  
    37          NSUInteger idx = (i / 3) * 4;
    38          output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F];
    39          output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F];
    40          output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6)  & 0x3F] : '=';
    41          output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0)  & 0x3F] : '=';
    42      }
    43  
    44      return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding];
    45  }
    46  
    47  #pragma mark - keychain stuff
    48  
    49  + (NSString *)passwordForUsername:(NSString *)username
    50  {
    51      if (!username) {
    52          LALog(@"no username");
    53          return nil;
    54      }
    55  
    56      // construct query
    57      NSDictionary *passwordQuery = @{(__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
    58                                      (__bridge NSString *)kSecAttrAccount: username,
    59                                      (__bridge NSString *)kSecAttrService: serviceName,
    60                                      (__bridge NSString *)kSecReturnData: (id)kCFBooleanTrue};
    61  
    62  
    63  	NSData *results = nil;
    64      CFTypeRef resultRef = (__bridge CFTypeRef)results;
    65  
    66      // actually request password
    67      OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)passwordQuery, &resultRef);
    68  
    69      results = (__bridge NSData *)resultRef;
    70  
    71      if (status == noErr) {
    72          LALog(@"returning valid password");
    73          return [[NSString alloc] initWithData:results encoding:NSUTF8StringEncoding];
    74      }
    75  
    76      return nil;
    77  }
    78  
    79  + (BOOL)savePassword:(NSString *)password forUsername:(NSString *)username
    80  {
    81      if (!password || !username) {
    82          LALog(@"no password or username");
    83          return NO;
    84      }
    85  
    86      OSStatus status = noErr;
    87  
    88      if ([self passwordForUsername:username]) {
    89          LALog(@"update");
    90          // update keychain item
    91  
    92          NSDictionary *updateQuery = @{(__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
    93                                        (__bridge NSString *)kSecAttrAccount: username,
    94                                        (__bridge NSString *)kSecAttrService: serviceName
    95                                        };
    96  
    97          NSDictionary *updateAttribute = @{(__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding]};
    98  
    99          status = SecItemUpdate((__bridge CFDictionaryRef)updateQuery, (__bridge CFDictionaryRef)updateAttribute);
   100      } else {
   101          LALog(@"new");
   102          // add a new keychain item
   103  
   104          NSDictionary *addQuery = @{(__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassGenericPassword,
   105                                     (__bridge NSString *)kSecAttrAccount: username,
   106                                     (__bridge NSString *)kSecAttrService: serviceName,
   107                                     (__bridge NSString *)kSecValueData: [password dataUsingEncoding:NSUTF8StringEncoding]
   108                                     };
   109  
   110          status =  SecItemAdd((__bridge CFDictionaryRef)addQuery, nil);
   111      }
   112  
   113      if (status != noErr) {
   114          return NO;
   115      }
   116  
   117      return YES;
   118  }
   119  
   120  #pragma mark - hashes
   121  
   122  + (NSString *)blobRef:(NSData *)data
   123  {
   124      uint8_t digest[CC_SHA1_DIGEST_LENGTH];
   125  
   126      CC_SHA1(data.bytes, data.length, digest);
   127  
   128      NSMutableString* output = [NSMutableString stringWithCapacity:(CC_SHA1_DIGEST_LENGTH * 2) + 5];
   129      [output appendString:@"sha1-"];
   130  
   131      for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
   132          [output appendFormat:@"%02x", digest[i]];
   133      }
   134  
   135      return output;
   136  }
   137  
   138  #pragma mark - dates
   139  
   140  + (NSString *)rfc3339StringFromDate:(NSDate *)date
   141  {
   142      NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init];
   143  
   144      NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
   145  
   146      [rfc3339DateFormatter setLocale:enUSPOSIXLocale];
   147      [rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
   148      [rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
   149  
   150      return [rfc3339DateFormatter stringFromDate:date];
   151  }
   152  
   153  #pragma mark - yucky logging hack
   154  
   155  + (void)logText:(NSArray *)logs
   156  {
   157      NSMutableString *logString = [NSMutableString string];
   158  
   159      for (NSString *log in logs) {
   160          [logString appendString:log];
   161      }
   162  
   163      [[NSNotificationCenter defaultCenter] postNotificationName:@"logtext" object:@{@"text": logString}];
   164  }
   165  
   166  + (void)statusText:(NSArray *)statuses
   167  {
   168      NSMutableString *statusString = [NSMutableString string];
   169  
   170      for (NSString *status in statuses) {
   171          [statusString appendString:status];
   172      }
   173  
   174      [[NSNotificationCenter defaultCenter] postNotificationName:@"statusText" object:@{@"text": statusString}];
   175  }
   176  
   177  + (void)errorText:(NSArray *)errors
   178  {
   179      NSMutableString *errorString = [NSMutableString string];
   180  
   181      for (NSString *error in errors) {
   182          [errorString appendString:error];
   183      }
   184  
   185      [[NSNotificationCenter defaultCenter] postNotificationName:@"errorText" object:@{@"text": errorString}];
   186  }
   187  
   188  @end