Skip to content

Instantly share code, notes, and snippets.

@baileysh9
Last active February 9, 2017 16:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baileysh9/eddcba49d544635b3cf5 to your computer and use it in GitHub Desktop.
Save baileysh9/eddcba49d544635b3cf5 to your computer and use it in GitHub Desktop.
Parsing Receipt in Swift
//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