Last active
February 9, 2017 16:44
-
-
Save baileysh9/eddcba49d544635b3cf5 to your computer and use it in GitHub Desktop.
Parsing Receipt in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Swift 2 | |
//let octets = pkcs7_d_data(pkcs7_d_sign(receiptPKCS7).memory.contents) | |
//var ptr = UnsafePointer<UInt8>(octets.memory.data) | |
//let end = ptr.advancedBy(Int(octets.memory.length)) | |
//Swift 3 | |
let octets = pkcs7_d_data(pkcs7_d_sign(receiptPKCS7).pointee.contents) | |
var ptr = UnsafePointer<UInt8>(octets?.pointee.data) | |
let end = ptr?.advanced(by: Int((octets?.pointee.length)!)) | |
var type: Int32 = 0 | |
var xclass: Int32 = 0 | |
var length = 0 | |
ASN1_get_object(&ptr, &length, &type, &xclass, end - ptr!) | |
if (type != V_ASN1_SET) { | |
print("failed to read ASN1 from receipt") | |
return | |
} | |
var bundleIdString1: NSString? | |
var bundleVersionString1: NSString? | |
//Swift 3 | |
var bundleIdData1: Data? //Swift 2: NSData? | |
var hashData1: Data? //NSData? | |
var opaqueData1: Data? //NSData? | |
while (ptr! < end!) | |
{ | |
var integer: UnsafeMutablePointer<ASN1_INTEGER> | |
// Expecting an attribute sequence | |
ASN1_get_object(&ptr, &length, &type, &xclass, end! - ptr!) | |
if type != V_ASN1_SEQUENCE { | |
print("ASN1 error: expected an attribute sequence") | |
return | |
} | |
//Swift 2 | |
//let seq_end = ptr.advancedBy(length) | |
//Swift 3 | |
let seq_end = ptr?.advanced(by: length) | |
var attr_type = 0 | |
// The attribute is an integer | |
ASN1_get_object(&ptr, &length, &type, &xclass, end! - ptr!) | |
if type != V_ASN1_INTEGER { | |
print("ASN1 error: attribute not an integer") | |
return | |
} | |
integer = c2i_ASN1_INTEGER(nil, &ptr, length) | |
attr_type = ASN1_INTEGER_get(integer) | |
ASN1_INTEGER_free(integer) | |
// The version is an integer | |
ASN1_get_object(&ptr, &length, &type, &xclass, end! - ptr!) | |
if type != V_ASN1_INTEGER { | |
print("ASN1 error: version not an integer") | |
return | |
} | |
integer = c2i_ASN1_INTEGER(nil, &ptr, length); | |
ASN1_INTEGER_free(integer); | |
// The attribute value is an octet string | |
ASN1_get_object(&ptr, &length, &type, &xclass, end! - ptr!) | |
if type != V_ASN1_OCTET_STRING { | |
print("ASN1 error: value not an octet string") | |
return | |
} | |
if attr_type == 2 { | |
// Bundle identifier | |
var str_ptr = ptr | |
var str_type: Int32 = 0 | |
var str_length = 0 | |
var str_xclass: Int32 = 0 | |
ASN1_get_object(&str_ptr, &str_length, &str_type, &str_xclass, seq_end! - str_ptr!) | |
if str_type == V_ASN1_UTF8STRING { | |
//Swift 2 | |
//bundleIdString1 = NSString(bytes: str_ptr, length: str_length, encoding: NSUTF8StringEncoding) | |
//bundleIdData1 = NSData(bytes: ptr, length: length) | |
//Swift 3 | |
bundleIdString1 = NSString(bytes: str_ptr!, length: str_length, encoding: String.Encoding.utf8.rawValue) | |
bundleIdData1 = Data(bytes: UnsafePointer<UInt8>(ptr!), count: length) | |
} | |
} | |
else if attr_type == 3 { | |
// Bundle version | |
var str_ptr = ptr | |
var str_type: Int32 = 0 | |
var str_length = 0 | |
var str_xclass: Int32 = 0 | |
ASN1_get_object(&str_ptr, &str_length, &str_type, &str_xclass, seq_end! - str_ptr!) | |
if str_type == V_ASN1_UTF8STRING { | |
//Swift 2 | |
//bundleVersionString1 = NSString(bytes: str_ptr, length: str_length, encoding: NSUTF8StringEncoding) | |
//Swift 3 | |
bundleVersionString1 = NSString(bytes: str_ptr!, length: str_length, encoding: String.Encoding.utf8.rawValue) | |
} | |
} | |
else if attr_type == 4 { | |
// Opaque value | |
//Swift 2 | |
//opaqueData1 = NSData(bytes: ptr, length: length) | |
//Swift 3 | |
opaqueData1 = Data(bytes: UnsafePointer<UInt8>(ptr!), count: length) | |
} | |
else if attr_type == 5 { | |
// Computed GUID (SHA-1 Hash) | |
//Swift 2 | |
//hashData1 = NSData(bytes: ptr, length: length) | |
//Swift 3 | |
hashData1 = Data(bytes: UnsafePointer<UInt8>(ptr!), count: length) | |
} | |
else if attr_type == 17 { | |
//In app receipt | |
//Swift 2 | |
//let r = NSData(bytes: ptr, length: length) | |
//Swift 3 | |
let r = Data(bytes: UnsafePointer<UInt8>(ptr!), count: length) | |
let id = self.getProductIdFromReceipt(r) | |
if id != nil { | |
pIds.addObject(id!) | |
} | |
} | |
// Move past the value | |
//Swift 2 | |
//ptr = ptr.advancedBy(length) | |
//Swift 3 | |
ptr = ptr?.advanced(by: length) | |
} | |
//Make sure that expected values from the receipt are actually there | |
if bundleIdString1 == nil { | |
print("No Bundle Id Found") | |
return | |
} | |
if bundleVersionString1 == nil { | |
print("No Bundle Version String Found") | |
return | |
} | |
if opaqueData1 == nil { | |
print("No Opaque Data Found") | |
return | |
} | |
if hashData1 == nil { | |
print("No Hash Value Found") | |
return | |
} | |
//Verify the bundle id in the receipt matches the app, use hard coded value instead of pulling | |
//info.plist since the plist can be changed by anyone that knows anything | |
if bundleIdString1 != "your.app.id" { | |
print("Receipt verification error: Wrong bundle identifier") | |
return | |
} | |
// Retrieve the Device GUID | |
let device = UIDevice.current //Swift 2: UIDevice.currentDevice() | |
let uuid = device.identifierForVendor | |
let mutableData = NSMutableData(length: 16) | |
//Swift 2 | |
//uuid!.getUUIDBytes(UnsafeMutablePointer(mutableData!.mutableBytes)) | |
//Swift 3 | |
(uuid! as NSUUID).getBytes(mutableData!.mutableBytes.assumingMemoryBound(to: UInt8.self)) | |
// Verify the hash | |
//Swift 2 | |
//var hash = Array<UInt8>(count: 20, repeatedValue: 0) | |
//Swift 3 | |
var hash = Array<UInt8>(repeating: 0, count: 20) | |
var ctx = SHA_CTX() | |
SHA1_Init(&ctx) | |
SHA1_Update(&ctx, mutableData!.bytes, mutableData!.length) | |
SHA1_Update(&ctx, opaqueData1!.bytes, opaqueData1!.length) | |
SHA1_Update(&ctx, bundleIdData1!.bytes, bundleIdData1!.length) | |
SHA1_Final(&hash, &ctx) | |
//Swift 2 | |
// let computedHashData1 = NSData(bytes: &hash, length: 20) | |
//Swift 3 | |
let computedHashData1 = Data(bytes: &hash, count: 20) | |
if !computedHashData1.isEqualToData(hashData1!) | |
{ | |
print("Receipt Hash Did Not Match!") | |
return | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment