From d23c0699033a62e993cf2b723cc541222c313996 Mon Sep 17 00:00:00 2001 From: Stephane Peter Date: Thu, 21 Nov 2013 01:37:34 -0800 Subject: [PATCH 1/2] Ignore Xcode files. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f4be871 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +xcuserdata +*.xcworkspace From e606605c306d13ca6eea74470afe8323bab9a766 Mon Sep 17 00:00:00 2001 From: Stephane Peter Date: Thu, 21 Nov 2013 01:38:18 -0800 Subject: [PATCH 2/2] - Handle receipt expiration date properly. - Convert date strings to NSDate objects where possible. - Ignore empty fields altogether. - Check if the app receipt is past an expiration date in validation. --- VerifyStoreReceipt/VerifyStoreReceipt.m | 75 +++++++++++++++++-------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/VerifyStoreReceipt/VerifyStoreReceipt.m b/VerifyStoreReceipt/VerifyStoreReceipt.m index 26f1d91..4483cae 100644 --- a/VerifyStoreReceipt/VerifyStoreReceipt.m +++ b/VerifyStoreReceipt/VerifyStoreReceipt.m @@ -104,7 +104,14 @@ int type = 0; int xclass = 0; long length = 0; + + NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init]; + NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + [rfc3339DateFormatter setLocale:enUSPOSIXLocale]; + [rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"]; + [rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; + NSUInteger dataLenght = [inappData length]; const uint8_t *p = [inappData bytes]; @@ -243,11 +250,12 @@ break; } - if (key) { + if (key && str_length>0) { NSString *string = [[NSString alloc] initWithBytes:str_p length:(NSUInteger)str_length encoding:NSASCIIStringEncoding]; - [item setObject:string forKey:key]; + NSDate *date = [rfc3339DateFormatter dateFromString:string]; + [item setObject:(date ? date : string) forKey:key]; } } } @@ -418,31 +426,48 @@ } // Strings - if (attr_type == BUNDLE_ID || attr_type == VERSION || attr_type == ORIG_VERSION) { + if (attr_type == BUNDLE_ID || attr_type == VERSION || attr_type == ORIG_VERSION || attr_type == EXPIRE_DATE) { int str_type = 0; long str_length = 0; const uint8_t *str_p = p; + + switch (attr_type) { + case BUNDLE_ID: + key = kReceiptBundleIdentifier; + break; + case VERSION: + key = kReceiptVersion; + break; + case ORIG_VERSION: + key = kReceiptOriginalVersion; + break; + case EXPIRE_DATE: + key = kReceiptExpirationDate; + break; + } + ASN1_get_object(&str_p, &str_length, &str_type, &xclass, seq_end - str_p); - if (str_type == V_ASN1_UTF8STRING) { - switch (attr_type) { - case BUNDLE_ID: - key = kReceiptBundleIdentifier; - break; - case VERSION: - key = kReceiptVersion; - break; - case ORIG_VERSION: - key = kReceiptOriginalVersion; - break; - } - - if (key) { + if (key && str_length>0) { + if (str_type == V_ASN1_UTF8STRING) { NSString *string = [[NSString alloc] initWithBytes:str_p length:(NSUInteger)str_length encoding:NSUTF8StringEncoding]; [info setObject:string forKey:key]; - } - } + } else if (str_type == V_ASN1_IA5STRING) { + NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init]; + NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; + + [rfc3339DateFormatter setLocale:enUSPOSIXLocale]; + [rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"]; + [rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; + + NSString *string = [[NSString alloc] initWithBytes:str_p + length:(NSUInteger)str_length + encoding:NSASCIIStringEncoding]; + NSDate *date = [rfc3339DateFormatter dateFromString:string]; + [info setObject:(date ? date : string) forKey:key]; + } + } } // In-App purchases @@ -521,6 +546,12 @@ BOOL verifyReceiptAtPath(NSString *receiptPath) { return NO; } + // See if we are past any expiration date + NSDate *expired = receipt[kReceiptExpirationDate]; + if ([expired timeIntervalSinceNow] < 0.0) { + return NO; + } + unsigned char uuidBytes[16]; NSUUID *vendorUUID = [[UIDevice currentDevice] identifierForVendor]; [vendorUUID getUUIDBytes:uuidBytes]; @@ -533,9 +564,9 @@ BOOL verifyReceiptAtPath(NSString *receiptPath) { NSMutableData *hash = [NSMutableData dataWithLength:SHA_DIGEST_LENGTH]; SHA1([input bytes], [input length], [hash mutableBytes]); - if ([bundleIdentifier isEqualToString:[receipt objectForKey:kReceiptBundleIdentifier]] && - [bundleVersion isEqualToString:[receipt objectForKey:kReceiptVersion]] && - [hash isEqualToData:[receipt objectForKey:kReceiptHash]]) { + if ([bundleIdentifier isEqualToString:receipt[kReceiptBundleIdentifier]] && + [bundleVersion isEqualToString:receipt[kReceiptVersion]] && + [hash isEqualToData:receipt[kReceiptHash]]) { return YES; }