Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 641 lines (521 sloc) 19.045 kb
bddbcd1 @roddi initial commit
authored
1 //
2 // validatereceipt.m
3 //
4 // Created by Ruotger Skupin on 23.10.10.
4ccfe17 @roddi updated copyright years
authored
5 // Copyright 2010-2011 Matthew Stevens, Ruotger Skupin, Apple, Dave Carlton, Fraser Hess, anlumo, David Keegan. All rights reserved.
bddbcd1 @roddi initial commit
authored
6 //
7
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
8 /*
eb16b8f @roddi added BSD-style license; tweaked build settings; corrected pointer const...
authored
9 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
10
eb16b8f @roddi added BSD-style license; tweaked build settings; corrected pointer const...
authored
11 Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
12
eb16b8f @roddi added BSD-style license; tweaked build settings; corrected pointer const...
authored
13 Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
15
16 Neither the name of the copyright holders nor the names of its contributors may be used to endorse or promote products derived
eb16b8f @roddi added BSD-style license; tweaked build settings; corrected pointer const...
authored
17 from this software without specific prior written permission.
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
20 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
21 SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
eb16b8f @roddi added BSD-style license; tweaked build settings; corrected pointer const...
authored
24 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
bddbcd1 @roddi initial commit
authored
27 #import "validatereceipt.h"
28
fcd6d2c @roddi cleaned up, added anlumo to copyright notice and README
authored
29 // link with Foundation.framework, IOKit.framework, Security.framework and libCrypto (via -lcrypto in Other Linker Flags)
bddbcd1 @roddi initial commit
authored
30
31 #import <IOKit/IOKitLib.h>
32 #import <Foundation/Foundation.h>
33
9fef320 @roddi working certificate check
authored
34 #import <Security/Security.h>
35
bddbcd1 @roddi initial commit
authored
36 #include <openssl/pkcs7.h>
37 #include <openssl/objects.h>
38 #include <openssl/sha.h>
9fef320 @roddi working certificate check
authored
39 #include <openssl/x509.h>
40 #include <openssl/err.h>
bddbcd1 @roddi initial commit
authored
41
f22472b cosmetic changes
yene authored
42 //#define USE_SAMPLE_RECEIPT // also defined in the debug build settings
9d599b3 @roddi added proto for copy_mac_address(); added warnings for when it is compil...
authored
43
44 #ifdef USE_SAMPLE_RECEIPT
45 #warning ************************************
46 #warning ******* USES SAMPLE RECEIPT! *******
47 #warning ************************************
48 #endif
49
bddbcd1 @roddi initial commit
authored
50
eb16b8f @roddi added BSD-style license; tweaked build settings; corrected pointer const...
authored
51 #ifndef YES_I_HAVE_READ_THE_WARNING_AND_I_ACCEPT_THE_RISK
52
53 #warning --- DON'T USE THIS CODE AS IS! IF EVERYONE USES THE SAME CODE
54 #warning --- IT IS PRETTY EASY TO BUILD AN AUTOMATIC CRACKING TOOL
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
55 #warning --- FOR APPS USING THIS CODE!
56 #warning --- BY USING THIS CODE YOU ACCEPT TAKING THE RESPONSIBILITY FOR
57 #warning --- ANY DAMAGE!
58 #warning ---
eb16b8f @roddi added BSD-style license; tweaked build settings; corrected pointer const...
authored
59 #warning --- YOU HAVE BEEN WARNED!
60
61 // if you want to take that risk, add "-DYES_I_HAVE_READ_THE_WARNING_AND_I_ACCEPT_THE_RISK" in the build settings at "Other C Flags"
62
63 #endif // YES_I_HAVE_READ_THE_WARNING_AND_I_ACCEPT_THE_RISK
ec42914 @kgn Moving VRCFRelease to a better place
kgn authored
64
1051075 @kgn Adding CFRelease macro
kgn authored
65 #define VRCFRelease(object) if(object) CFRelease(object)
eb16b8f @roddi added BSD-style license; tweaked build settings; corrected pointer const...
authored
66
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
67 NSString *kReceiptBundleIdentifier = @"BundleIdentifier";
68 NSString *kReceiptBundleIdentifierData = @"BundleIdentifierData";
69 NSString *kReceiptVersion = @"Version";
70 NSString *kReceiptOpaqueValue = @"OpaqueValue";
71 NSString *kReceiptHash = @"Hash";
72 NSString *kReceiptInApp = @"InApp";
73
74 NSString *kReceiptInAppQuantity = @"Quantity";
75 NSString *kReceiptInAppProductIdentifier = @"ProductIdentifier";
76 NSString *kReceiptInAppTransactionIdentifier = @"TransactionIdentifier";
77 NSString *kReceiptInAppPurchaseDate = @"PurchaseDate";
78 NSString *kReceiptInAppOriginalTransactionIdentifier = @"OriginalTransactionIdentifier";
79 NSString *kReceiptInAppOriginalPurchaseDate = @"OriginalPurchaseDate";
bddbcd1 @roddi initial commit
authored
80
9fef320 @roddi working certificate check
authored
81
f22472b cosmetic changes
yene authored
82 NSData * appleRootCert(void)
9fef320 @roddi working certificate check
authored
83 {
84 OSStatus status;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
85
9fef320 @roddi working certificate check
authored
86 SecKeychainRef keychain = nil;
87 status = SecKeychainOpen("/System/Library/Keychains/SystemRootCertificates.keychain", &keychain);
1051075 @kgn Adding CFRelease macro
kgn authored
88 if(status){
89 VRCFRelease(keychain);
9fef320 @roddi working certificate check
authored
90 return nil;
91 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
92
9fef320 @roddi working certificate check
authored
93 CFArrayRef searchList = CFArrayCreate(kCFAllocatorDefault, (const void**)&keychain, 1, &kCFTypeArrayCallBacks);
94
864f78c @philippm Fixed reference count underflow warning
philippm authored
95 // For some reason we get a malloc reference underflow warning message when garbage collection
96 // is on. Perhaps a bug in SecKeychainOpen where the keychain reference isn't actually retained
97 // in GC?
98 #ifndef __OBJC_GC__
1051075 @kgn Adding CFRelease macro
kgn authored
99 VRCFRelease(keychain);
864f78c @philippm Fixed reference count underflow warning
philippm authored
100 #endif
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
101
9fef320 @roddi working certificate check
authored
102 SecKeychainSearchRef searchRef = nil;
103 status = SecKeychainSearchCreateFromAttributes(searchList, kSecCertificateItemClass, NULL, &searchRef);
1051075 @kgn Adding CFRelease macro
kgn authored
104 if(status){
9e9f7d6 @kgn Spaces to tabs
kgn authored
105 VRCFRelease(searchRef);
106 VRCFRelease(searchList);
9fef320 @roddi working certificate check
authored
107 return nil;
108 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
109
9fef320 @roddi working certificate check
authored
110 SecKeychainItemRef itemRef = nil;
111 NSData * resultData = nil;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
112
9fef320 @roddi working certificate check
authored
113 while(SecKeychainSearchCopyNext(searchRef, &itemRef) == noErr && resultData == nil) {
114 // Grab the name of the certificate
115 SecKeychainAttributeList list;
116 SecKeychainAttribute attributes[1];
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
117
9fef320 @roddi working certificate check
authored
118 attributes[0].tag = kSecLabelItemAttr;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
119
9fef320 @roddi working certificate check
authored
120 list.count = 1;
121 list.attr = attributes;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
122
9fef320 @roddi working certificate check
authored
123 SecKeychainItemCopyContent(itemRef, nil, &list, nil, nil);
124 NSData *nameData = [NSData dataWithBytesNoCopy:attributes[0].data length:attributes[0].length freeWhenDone:NO];
125 NSString *name = [[NSString alloc] initWithData:nameData encoding:NSUTF8StringEncoding];
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
126
9fef320 @roddi working certificate check
authored
127 if([name isEqualToString:@"Apple Root CA"]) {
128 CSSM_DATA certData;
e615ec8 @kgn Removing un-needed release and status variable.
kgn authored
129 SecCertificateGetData((SecCertificateRef)itemRef, &certData);
9fef320 @roddi working certificate check
authored
130 resultData = [NSData dataWithBytes:certData.Data length:certData.Length];
131 }
467df26 @roddi plugged a leak.
authored
132
133 SecKeychainItemFreeContent(&list, NULL);
134
135 if (itemRef)
136 VRCFRelease(itemRef);
e615ec8 @kgn Removing un-needed release and status variable.
kgn authored
137
9e9f7d6 @kgn Spaces to tabs
kgn authored
138 [name release];
9fef320 @roddi working certificate check
authored
139 }
e615ec8 @kgn Removing un-needed release and status variable.
kgn authored
140
9e9f7d6 @kgn Spaces to tabs
kgn authored
141 VRCFRelease(searchList);
142 VRCFRelease(searchRef);
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
143
9fef320 @roddi working certificate check
authored
144 return resultData;
145 }
146
147
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
148 NSArray * parseInAppPurchasesData(NSData * inappData)
149 {
150 #define INAPP_ATTR_START 1700
151 #define INAPP_QUANTITY 1701
152 #define INAPP_PRODID 1702
153 #define INAPP_TRANSID 1703
154 #define INAPP_PURCHDATE 1704
155 #define INAPP_ORIGTRANSID 1705
156 #define INAPP_ORIGPURCHDATE 1706
157 #define INAPP_ATTR_END 1707
158
159 int type = 0;
160 int xclass = 0;
161 long length = 0;
162
163 NSUInteger dataLenght = [inappData length];
164 const uint8_t *p = [inappData bytes];
165
166 const uint8_t *end = p + dataLenght;
167
168 NSMutableArray *resultArray = [NSMutableArray array];
169
170 while (p < end)
171 {
172 ASN1_get_object(&p, &length, &type, &xclass, end - p);
173
174 const uint8_t *set_end = p + length;
175
176 if(type != V_ASN1_SET) {
177 break;
178 }
179
180 NSMutableDictionary *item = [NSMutableDictionary dictionaryWithCapacity:6];
181
182 while (p < set_end) {
183 ASN1_get_object(&p, &length, &type, &xclass, set_end - p);
184 if (type != V_ASN1_SEQUENCE)
185 break;
186
187 const uint8_t *seq_end = p + length;
188
189 int attr_type = 0;
190 int attr_version = 0;
191
192 // Attribute type
193 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
194 if (type == V_ASN1_INTEGER) {
195 if(length == 1) {
196 attr_type = p[0];
197 }
198 else if(length == 2) {
199 attr_type = p[0] * 0x100 + p[1]
200 ;
201 }
202 }
203 p += length;
204
205 // Attribute version
206 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
207 if (type == V_ASN1_INTEGER && length == 1) {
208 attr_version = p[0];
209 attr_version = attr_version;
210 }
211 p += length;
212
213 // Only parse attributes we're interested in
214 if (attr_type > INAPP_ATTR_START && attr_type < INAPP_ATTR_END) {
215 NSString *key = nil;
216
217 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
218 if (type == V_ASN1_OCTET_STRING) {
219 //NSData *data = [NSData dataWithBytes:p length:(NSUInteger)length];
220
221 // Integers
222 if(attr_type == INAPP_QUANTITY) {
223 int num_type = 0;
224 long num_length = 0;
225 const uint8_t *num_p = p;
226 ASN1_get_object(&num_p, &num_length, &num_type, &xclass, seq_end - num_p);
227 if(num_type == V_ASN1_INTEGER) {
228 NSUInteger quantity = 0;
229 if(num_length) {
230 quantity += num_p[0];
231 if(num_length > 1) {
232 quantity += num_p[1] * 0x100;
233 if(num_length > 2) {
234 quantity += num_p[2] * 0x10000;
235 if(num_length > 3) {
236 quantity += num_p[3] * 0x1000000;
237 }
238 }
239 }
240 }
241
242 NSNumber *num = [[NSNumber alloc] initWithUnsignedInteger:quantity];
243 [item setObject:num forKey:kReceiptInAppQuantity];
244 [num release];
245 }
246 }
247
248 // Strings
249 else if (attr_type == INAPP_PRODID ||
250 attr_type == INAPP_TRANSID ||
251 attr_type == INAPP_ORIGTRANSID ||
252 attr_type == INAPP_PURCHDATE ||
253 attr_type == INAPP_ORIGPURCHDATE) {
254
255 int str_type = 0;
256 long str_length = 0;
257 const uint8_t *str_p = p;
258 ASN1_get_object(&str_p, &str_length, &str_type, &xclass, seq_end - str_p);
259 if (str_type == V_ASN1_UTF8STRING) {
260 switch (attr_type) {
261 case INAPP_PRODID:
262 key = kReceiptInAppProductIdentifier;
263 break;
264 case INAPP_TRANSID:
265 key = kReceiptInAppTransactionIdentifier;
266 break;
267 case INAPP_ORIGTRANSID:
268 key = kReceiptInAppOriginalTransactionIdentifier;
269 break;
270 }
271
272 if (key) {
273 NSString *string = [[NSString alloc] initWithBytes:str_p
274 length:(NSUInteger)str_length
275 encoding:NSUTF8StringEncoding];
276 [item setObject:string forKey:key];
277 [string release];
278 }
279 }
280 else if (str_type == V_ASN1_IA5STRING) {
281 switch (attr_type) {
282 case INAPP_PURCHDATE:
283 key = kReceiptInAppPurchaseDate;
284 break;
285 case INAPP_ORIGPURCHDATE:
286 key = kReceiptInAppOriginalPurchaseDate;
287 break;
288 }
289
290 if (key) {
291 NSString *string = [[NSString alloc] initWithBytes:str_p
292 length:(NSUInteger)str_length
293 encoding:NSASCIIStringEncoding];
294 [item setObject:string forKey:key];
295 [string release];
296 }
297 }
298 }
299 }
300
301 p += length;
302 }
303
304 // Skip any remaining fields in this SEQUENCE
305 while (p < seq_end) {
306 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
307 p += length;
308 }
309 }
310
311 [resultArray addObject:item];
312 }
313
314 return resultArray;
315 }
316
317
bddbcd1 @roddi initial commit
authored
318 NSDictionary * dictionaryWithAppStoreReceipt(NSString * path)
319 {
9fef320 @roddi working certificate check
authored
320 NSData * rootCertData = appleRootCert();
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
321
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
322 #define ATTR_START 1
323 #define BUNDLE_ID 2
324 #define VERSION 3
325 #define OPAQUE_VALUE 4
326 #define HASH 5
327 #define ATTR_END 6
328 #define INAPP_PURCHASE 17
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
329
9fef320 @roddi working certificate check
authored
330 ERR_load_PKCS7_strings();
331 ERR_load_X509_strings();
332 OpenSSL_add_all_digests();
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
333
9e9f7d6 @kgn Spaces to tabs
kgn authored
334 // Expected input is a PKCS7 container with signed data containing
335 // an ASN.1 SET of SEQUENCE structures. Each SEQUENCE contains
336 // two INTEGERS and an OCTET STRING.
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
337
bddbcd1 @roddi initial commit
authored
338 const char * receiptPath = [[path stringByStandardizingPath] fileSystemRepresentation];
9e9f7d6 @kgn Spaces to tabs
kgn authored
339 FILE *fp = fopen(receiptPath, "rb");
340 if (fp == NULL)
341 return nil;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
342
9e9f7d6 @kgn Spaces to tabs
kgn authored
343 PKCS7 *p7 = d2i_PKCS7_fp(fp, NULL);
344 fclose(fp);
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
345
8d5c75d Fixed crash when the receipt file is invalid.
philippm authored
346 // Check if the receipt file was invalid (otherwise we go crashing and burning)
347 if (p7 == NULL) {
348 return nil;
349 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
350
9e9f7d6 @kgn Spaces to tabs
kgn authored
351 if (!PKCS7_type_is_signed(p7)) {
352 PKCS7_free(p7);
353 return nil;
354 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
355
9e9f7d6 @kgn Spaces to tabs
kgn authored
356 if (!PKCS7_type_is_data(p7->d.sign->contents)) {
357 PKCS7_free(p7);
358 return nil;
359 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
360
91a8697 Proper allocation checks and cleanup for bio and X509.
Philippe Casgrain authored
361 int verifyReturnValue = 0;
9fef320 @roddi working certificate check
authored
362 X509_STORE *store = X509_STORE_new();
91a8697 Proper allocation checks and cleanup for bio and X509.
Philippe Casgrain authored
363 if (store)
364 {
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
365 const uint8_t *data = (uint8_t *)(rootCertData.bytes);
eb16b8f @roddi added BSD-style license; tweaked build settings; corrected pointer const...
authored
366 X509 *appleCA = d2i_X509(NULL, &data, (long)rootCertData.length);
91a8697 Proper allocation checks and cleanup for bio and X509.
Philippe Casgrain authored
367 if (appleCA)
368 {
369 BIO *payload = BIO_new(BIO_s_mem());
370 X509_STORE_add_cert(store, appleCA);
371
372 if (payload)
373 {
374 verifyReturnValue = PKCS7_verify(p7,NULL,store,NULL,payload,0);
375 BIO_free(payload);
376 }
9fef320 @roddi working certificate check
authored
377
91a8697 Proper allocation checks and cleanup for bio and X509.
Philippe Casgrain authored
378 // this code will come handy when the first real receipts arrive
9fef320 @roddi working certificate check
authored
379 #if 0
91a8697 Proper allocation checks and cleanup for bio and X509.
Philippe Casgrain authored
380 unsigned long err = ERR_get_error();
381 if(err)
382 printf("%lu: %s\n",err,ERR_error_string(err,NULL));
383 else {
384 STACK_OF(X509) *stack = PKCS7_get0_signers(p7, NULL, 0);
385 for(NSUInteger i = 0; i < sk_num(stack); i++) {
386 const X509 *signer = (X509*)sk_value(stack, i);
387 NSLog(@"name = %s", signer->name);
388 }
389 }
390 #endif
391
392 X509_free(appleCA);
9fef320 @roddi working certificate check
authored
393 }
91a8697 Proper allocation checks and cleanup for bio and X509.
Philippe Casgrain authored
394 X509_STORE_free(store);
9fef320 @roddi working certificate check
authored
395 }
396 EVP_cleanup();
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
397
9fef320 @roddi working certificate check
authored
398 if (verifyReturnValue != 1)
fcd6d2c @roddi cleaned up, added anlumo to copyright notice and README
authored
399 {
9e9f7d6 @kgn Spaces to tabs
kgn authored
400 PKCS7_free(p7);
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
401 return nil;
fcd6d2c @roddi cleaned up, added anlumo to copyright notice and README
authored
402 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
403
404 ASN1_OCTET_STRING *octets = p7->d.sign->contents->d.data;
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
405 const uint8_t *p = octets->data;
406 const uint8_t *end = p + octets->length;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
407
9e9f7d6 @kgn Spaces to tabs
kgn authored
408 int type = 0;
409 int xclass = 0;
410 long length = 0;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
411
9e9f7d6 @kgn Spaces to tabs
kgn authored
412 ASN1_get_object(&p, &length, &type, &xclass, end - p);
413 if (type != V_ASN1_SET) {
414 PKCS7_free(p7);
415 return nil;
416 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
417
9e9f7d6 @kgn Spaces to tabs
kgn authored
418 NSMutableDictionary *info = [NSMutableDictionary dictionary];
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
419
9e9f7d6 @kgn Spaces to tabs
kgn authored
420 while (p < end) {
421 ASN1_get_object(&p, &length, &type, &xclass, end - p);
422 if (type != V_ASN1_SEQUENCE)
423 break;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
424
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
425 const uint8_t *seq_end = p + length;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
426
9e9f7d6 @kgn Spaces to tabs
kgn authored
427 int attr_type = 0;
428 int attr_version = 0;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
429
9e9f7d6 @kgn Spaces to tabs
kgn authored
430 // Attribute type
431 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
432 if (type == V_ASN1_INTEGER && length == 1) {
433 attr_type = p[0];
434 }
435 p += length;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
436
9e9f7d6 @kgn Spaces to tabs
kgn authored
437 // Attribute version
438 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
439 if (type == V_ASN1_INTEGER && length == 1) {
440 attr_version = p[0];
8cce890 @tgunr Added new static library target validateAS.
tgunr authored
441 attr_version = attr_version;
9e9f7d6 @kgn Spaces to tabs
kgn authored
442 }
443 p += length;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
444
9e9f7d6 @kgn Spaces to tabs
kgn authored
445 // Only parse attributes we're interested in
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
446 if ((attr_type > ATTR_START && attr_type < ATTR_END) || attr_type == INAPP_PURCHASE) {
c8dfd4a @kgn Fixing problems found with the static analyzer.
kgn authored
447 NSString *key = nil;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
448
9e9f7d6 @kgn Spaces to tabs
kgn authored
449 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
450 if (type == V_ASN1_OCTET_STRING) {
c8dfd4a @kgn Fixing problems found with the static analyzer.
kgn authored
451 NSData *data = [NSData dataWithBytes:p length:(NSUInteger)length];
452
9e9f7d6 @kgn Spaces to tabs
kgn authored
453 // Bytes
454 if (attr_type == BUNDLE_ID || attr_type == OPAQUE_VALUE || attr_type == HASH) {
455 switch (attr_type) {
456 case BUNDLE_ID:
457 // This is included for hash generation
d4c0c29 @kgn Fixing bundleIdentifier spelling mistake.
kgn authored
458 key = kReceiptBundleIdentifierData;
9e9f7d6 @kgn Spaces to tabs
kgn authored
459 break;
460 case OPAQUE_VALUE:
461 key = kReceiptOpaqueValue;
462 break;
463 case HASH:
464 key = kReceiptHash;
465 break;
466 }
c8dfd4a @kgn Fixing problems found with the static analyzer.
kgn authored
467 if (key) {
468 [info setObject:data forKey:key];
469 }
9e9f7d6 @kgn Spaces to tabs
kgn authored
470 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
471
9e9f7d6 @kgn Spaces to tabs
kgn authored
472 // Strings
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
473 else if (attr_type == BUNDLE_ID || attr_type == VERSION) {
9e9f7d6 @kgn Spaces to tabs
kgn authored
474 int str_type = 0;
475 long str_length = 0;
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
476 const uint8_t *str_p = p;
9e9f7d6 @kgn Spaces to tabs
kgn authored
477 ASN1_get_object(&str_p, &str_length, &str_type, &xclass, seq_end - str_p);
478 if (str_type == V_ASN1_UTF8STRING) {
479 switch (attr_type) {
480 case BUNDLE_ID:
d4c0c29 @kgn Fixing bundleIdentifier spelling mistake.
kgn authored
481 key = kReceiptBundleIdentifier;
9e9f7d6 @kgn Spaces to tabs
kgn authored
482 break;
483 case VERSION:
484 key = kReceiptVersion;
485 break;
486 }
c8dfd4a @kgn Fixing problems found with the static analyzer.
kgn authored
487
488 if (key) {
489 NSString *string = [[NSString alloc] initWithBytes:str_p
490 length:(NSUInteger)str_length
491 encoding:NSUTF8StringEncoding];
492 [info setObject:string forKey:key];
493 [string release];
494 }
9e9f7d6 @kgn Spaces to tabs
kgn authored
495 }
496 }
fddf344 Adding in-app purchases support (fixes issue #17)
EgoAleSum authored
497
498 // In-App purchases
499 else if (attr_type == INAPP_PURCHASE)
500 {
501 NSArray *inApp = parseInAppPurchasesData(data);
502 [info setObject:inApp forKey:kReceiptInApp];
503 }
9e9f7d6 @kgn Spaces to tabs
kgn authored
504 }
505 p += length;
506 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
507
9e9f7d6 @kgn Spaces to tabs
kgn authored
508 // Skip any remaining fields in this SEQUENCE
509 while (p < seq_end) {
510 ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
511 p += length;
512 }
513 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
514
9e9f7d6 @kgn Spaces to tabs
kgn authored
515 PKCS7_free(p7);
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
516
9e9f7d6 @kgn Spaces to tabs
kgn authored
517 return info;
bddbcd1 @roddi initial commit
authored
518 }
519
520
521
522 // Returns a CFData object, containing the machine's GUID.
523 CFDataRef copy_mac_address(void)
524 {
9e9f7d6 @kgn Spaces to tabs
kgn authored
525 kern_return_t kernResult;
526 mach_port_t master_port;
527 CFMutableDictionaryRef matchingDict;
528 io_iterator_t iterator;
529 io_object_t service;
530 CFDataRef macAddress = nil;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
531
9e9f7d6 @kgn Spaces to tabs
kgn authored
532 kernResult = IOMasterPort(MACH_PORT_NULL, &master_port);
533 if (kernResult != KERN_SUCCESS) {
534 printf("IOMasterPort returned %d\n", kernResult);
535 return nil;
536 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
537
9e9f7d6 @kgn Spaces to tabs
kgn authored
538 matchingDict = IOBSDNameMatching(master_port, 0, "en0");
539 if(!matchingDict) {
540 printf("IOBSDNameMatching returned empty dictionary\n");
541 return nil;
542 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
543
9e9f7d6 @kgn Spaces to tabs
kgn authored
544 kernResult = IOServiceGetMatchingServices(master_port, matchingDict, &iterator);
545 if (kernResult != KERN_SUCCESS) {
546 printf("IOServiceGetMatchingServices returned %d\n", kernResult);
547 return nil;
548 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
549
9e9f7d6 @kgn Spaces to tabs
kgn authored
550 while((service = IOIteratorNext(iterator)) != 0)
551 {
552 io_object_t parentService;
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
553
9e9f7d6 @kgn Spaces to tabs
kgn authored
554 kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parentService);
555 if(kernResult == KERN_SUCCESS)
556 {
557 VRCFRelease(macAddress);
558 macAddress = IORegistryEntryCreateCFProperty(parentService, CFSTR("IOMACAddress"), kCFAllocatorDefault, 0);
559 IOObjectRelease(parentService);
560 }
561 else {
562 printf("IORegistryEntryGetParentEntry returned %d\n", kernResult);
563 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
564
9e9f7d6 @kgn Spaces to tabs
kgn authored
565 IOObjectRelease(service);
566 }
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
567
9e9f7d6 @kgn Spaces to tabs
kgn authored
568 return macAddress;
bddbcd1 @roddi initial commit
authored
569 }
570
3f1fb71 @roddi now uses global NSString variables (declared extern) for the bundle stuf...
authored
571 extern const NSString * global_bundleVersion;
572 extern const NSString * global_bundleIdentifier;
573
574 // in your project define those two somewhere as such:
575 // const NSString * global_bundleVersion = @"1.0.2";
576 // const NSString * global_bundleIdentifier = @"com.example.SampleApp";
577
bddbcd1 @roddi initial commit
authored
578 BOOL validateReceiptAtPath(NSString * path)
579 {
c7b6179 @roddi receipt coping vulnerability fix (thanks to Sean Christmann for bringing...
authored
580 // it turns out, it's a bad idea, to use these two NSBundle methods in your app:
581 //
582 // bundleVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
d4c0c29 @kgn Fixing bundleIdentifier spelling mistake.
kgn authored
583 // bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
c7b6179 @roddi receipt coping vulnerability fix (thanks to Sean Christmann for bringing...
authored
584 //
585 // http://www.craftymind.com/2011/01/06/mac-app-store-hacked-how-developers-can-better-protect-themselves/
3f1fb71 @roddi now uses global NSString variables (declared extern) for the bundle stuf...
authored
586 //
c7b6179 @roddi receipt coping vulnerability fix (thanks to Sean Christmann for bringing...
authored
587 // so use hard coded values instead (probably even somehow obfuscated)
588
3f1fb71 @roddi now uses global NSString variables (declared extern) for the bundle stuf...
authored
589 // analyser warning when USE_SAMPLE_RECEIPT is defined (wontfix)
590 NSString *bundleVersion = (NSString*)global_bundleVersion;
591 NSString *bundleIdentifier = (NSString*)global_bundleIdentifier;
592 #ifndef USE_SAMPLE_RECEIPT
c7b6179 @roddi receipt coping vulnerability fix (thanks to Sean Christmann for bringing...
authored
593 // avoid making stupid mistakes --> check again
119a2c1 @kgn updating NSAssert so it doesn't error on build, fixes issue 11
kgn authored
594 NSCAssert([bundleVersion isEqualToString:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]],
c7b6179 @roddi receipt coping vulnerability fix (thanks to Sean Christmann for bringing...
authored
595 @"whoops! check the hard-coded CFBundleShortVersionString!");
119a2c1 @kgn updating NSAssert so it doesn't error on build, fixes issue 11
kgn authored
596 NSCAssert([bundleIdentifier isEqualToString:[[NSBundle mainBundle] bundleIdentifier]],
c7b6179 @roddi receipt coping vulnerability fix (thanks to Sean Christmann for bringing...
authored
597 @"whoops! check the hard-coded bundle identifier!");
6f35a9b @kgn Moving bundleIdentifier and bundleVersion code above any returns so the ...
kgn authored
598 #else
599 bundleVersion = @"1.0.2";
d4c0c29 @kgn Fixing bundleIdentifier spelling mistake.
kgn authored
600 bundleIdentifier = @"com.example.SampleApp";
6f35a9b @kgn Moving bundleIdentifier and bundleVersion code above any returns so the ...
kgn authored
601 #endif
602 NSDictionary * receipt = dictionaryWithAppStoreReceipt(path);
603
604 if (!receipt)
605 return NO;
606
607 NSData * guidData = nil;
608 #ifndef USE_SAMPLE_RECEIPT
609 guidData = (NSData*)copy_mac_address();
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
610
6f35a9b @kgn Moving bundleIdentifier and bundleVersion code above any returns so the ...
kgn authored
611 if ([NSGarbageCollector defaultCollector])
612 [[NSGarbageCollector defaultCollector] enableCollectorForPointer:guidData];
613 else
614 [guidData autorelease];
615
616 if (!guidData)
617 return NO;
adf2ab5 @fraserhess Validate bundle identifier and bundle version
fraserhess authored
618 #else
619 // Overwrite with example GUID for use with example receipt
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
620 unsigned char guid[] = { 0x00, 0x17, 0xf2, 0xc4, 0xbc, 0xc0 };
adf2ab5 @fraserhess Validate bundle identifier and bundle version
fraserhess authored
621 guidData = [NSData dataWithBytes:guid length:sizeof(guid)];
8cce890 @tgunr Added new static library target validateAS.
tgunr authored
622 #endif
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
623
bddbcd1 @roddi initial commit
authored
624 NSMutableData *input = [NSMutableData data];
625 [input appendData:guidData];
626 [input appendData:[receipt objectForKey:kReceiptOpaqueValue]];
d4c0c29 @kgn Fixing bundleIdentifier spelling mistake.
kgn authored
627 [input appendData:[receipt objectForKey:kReceiptBundleIdentifierData]];
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
628
bddbcd1 @roddi initial commit
authored
629 NSMutableData *hash = [NSMutableData dataWithLength:SHA_DIGEST_LENGTH];
630 SHA1([input bytes], [input length], [hash mutableBytes]);
adf2ab5 @fraserhess Validate bundle identifier and bundle version
fraserhess authored
631
d4c0c29 @kgn Fixing bundleIdentifier spelling mistake.
kgn authored
632 if ([bundleIdentifier isEqualToString:[receipt objectForKey:kReceiptBundleIdentifier]] &&
adf2ab5 @fraserhess Validate bundle identifier and bundle version
fraserhess authored
633 [bundleVersion isEqualToString:[receipt objectForKey:kReceiptVersion]] &&
0d24338 @kgn cleaning code with spaces to tabs and removeing needless whitespace
kgn authored
634 [hash isEqualToData:[receipt objectForKey:kReceiptHash]])
bddbcd1 @roddi initial commit
authored
635 {
636 return YES;
637 }
638
639 return NO;
640 }
Something went wrong with that request. Please try again.