CertTools - MacOSLib for 64Bit

Is there anybody who can fix this problem:
For MAS receipt check we need the CertTolls from MacOSLib. We need only these definition, parameters, declares and methods:

Private Const SampleReceipt As String = "MIIOBQYJKoZIhvcNAQcCoIIN9jCCDfICAQExCzAJBgUrDgMCGgUAMIIBogYJKoZIhvcNAQcBoIIB …

Protected Enum Keys
kReceiptBundleIdentifer
kReceiptBundleIdentiferData
kReceiptVersion
kReceiptOpaqueValue
kReceiptHash

Private Enum ATTRS
zero
ATTR_START
BUNDLE_ID
VERSION
OPAQUE_VALUE
HASH
ATTR_END

length as Int32
type as Int32
data as Ptr
flags as Int32

asn1 as CString
length as Int32
state as Int32
detached as Int32
type as Ptr
d as Ptr

version as Ptr
md_algs as Ptr
cert as Ptr
crl as Ptr
signer_info as Ptr
contents as Ptr

Protected Sub SelfTest()
// Call this function just to test whether the verification code works in general

#if TargetMacOS
dim f as FolderItem
f = SpecialFolder.Temporary.Child(“selftest-data”)
BinaryStream.Create(f,true).Write(DecodeBase64(SampleReceipt))
dim d as Dictionary
d = ReadReceipt(f)
f.Delete

if d = nil then raise new RuntimeException // selftest failed

dim guid as String

guid = DeviceGUID
if guid = "" then
  MsgBox "Oops - no DeviceGUID on this computer?"
end if

guid = HexBytesToData (Array("00", "17", "f2", "c4", "bc", "c0"))

if not IsValid (guid, d, "com.example.sampleApp") then
  raise new RuntimeException // selftest failed
end

#endif
End Sub

Protected Function DeviceGUID() As String
// The Mac’s GUID is the MAC address from network interface “en0”, see https://developer.apple.com/devcenter/mac/documents/validating.html

dim mac as String = SystemInformationMBS.MACAddressString
dim bytes() as String = mac.Split(":")
if bytes.Ubound = 5 then
return HexBytesToData (bytes)
end if

End Function

Protected Function HexBytesToData(bytes() as String) As MemoryBlock
dim mb as new MemoryBlock(6)
for i as Integer = 0 to 5
mb.Byte(i) = Val("&h"+bytes(i))
next
return mb
End Function

Protected Function IsValid(guid as String, receipt as Dictionary, bundleID as String) As Boolean
// Returns true if the given receipt (which comes from ‘ReadReceipt’) is valid
// for the given GUID (which is a unique code for a particular machine)

#if TargetMacOS
declare sub SHA1 lib “/usr/lib/libcrypto.dylib” (d as Ptr, n as Int32, md as Ptr)

if receipt <> nil then
  dim input as MemoryBlock
  input = guid + receipt.Value(Keys.kReceiptOpaqueValue) + receipt.Value(Keys.kReceiptBundleIdentiferData)
  dim hash as new MemoryBlock(20) ' SHA_DIGEST_LENGTH
  SHA1 (input, input.Size, hash)
  dim hashFromReceipt as String = receipt.Value(Keys.kReceiptHash)
  if StrComp (hash, hashFromReceipt, 0) = 0 then
    return receipt.Value(Keys.kReceiptBundleIdentifer) = bundleID
  end if
end if

#endif
End Function

Protected Function ReadReceipt(certFile as FolderItem) As Dictionary
// This function reads certain entries from the App’s certification receipt file

#if TargetMacOS
declare function d2i_PKCS7_fp lib “/usr/lib/libcrypto.dylib” (fp as Int32, p7 as Ptr) as Ptr
declare sub PKCS7_free lib “/usr/lib/libcrypto.dylib” (p7 as Ptr)
declare function OBJ_obj2nid lib “/usr/lib/libcrypto.dylib” (ASN1_OBJECT as Ptr) as Int32
declare function ASN1_get_object lib “/usr/lib/libcrypto.dylib” (ByRef pp as Ptr, ByRef plength as Int32, ByRef ptag as Int32, ByRef pclass as Int32, omax as Int32) as Int32

dim result as Dictionary

if certFile = nil then return nil
dim bs as BinaryStream
try
  bs = BinaryStream.Open(certFile)
catch exc as RuntimeException
  return nil
end
dim fp as Int32 = bs.Handle(BinaryStream.HandleTypeFilePointer)
if fp = 0 then return nil
dim p7 as Ptr = d2i_PKCS7_fp (fp, nil)
bs.Close
bs = nil
if p7 = nil then return nil

// is it signed?
dim nid as Int32 = OBJ_obj2nid (p7.PKCS7.type)
if nid <> 22 then goto bail1

// is data?
nid = OBJ_obj2nid (p7.PKCS7.d.PKCS7_SIGNED.contents.PKCS7.type)
if nid <> 21 then goto bail1

dim octets as Ptr = p7.PKCS7.d.PKCS7_SIGNED.contents.PKCS7.d
dim p, e as Ptr
p = octets.ASN1_STRING.data
dim l as Integer = octets.ASN1_STRING.length
e = p + Ptr(l)

dim res, type, xclass, length as Integer

res = ASN1_get_object(p, length, type, xclass, e - p)
if type <> 17 then goto bail1 ' V_ASN1_SET

result = new Dictionary
while p < e
  call ASN1_get_object (p, length, type, xclass, e - p)
  if type <> 16 then
    exit ' V_ASN1_SEQUENCE
  end
  
  dim seq_end as Ptr = p + Ptr(length)
  
  dim attr_type, attr_version as Integer
  
  // Attribute type
  call ASN1_get_object (p, length, type, xclass, seq_end - p)
  if type = 2 and length = 1 then ' V_ASN1_INTEGER
    attr_type = p.Byte(0)
  end
  p = p + Ptr(length)
  
  // Attribute version
  call ASN1_get_object (p, length, type, xclass, seq_end - p)
  if type = 2 and length = 1 then ' V_ASN1_INTEGER
    attr_version = p.Byte(0)
  end
  p = p + Ptr(length)
  
  // Only parse attributes we're interested in
  if ATTRS(attr_type) > ATTRS.ATTR_START and ATTRS(attr_type) < ATTRS.ATTR_END then
    dim key as Keys
    
    call ASN1_get_object (p, length, type, xclass, seq_end - p)
    if type = 4 then ' V_ASN1_OCTET_STRING
      // Bytes
      if ATTRS(attr_type) = ATTRS.BUNDLE_ID or ATTRS(attr_type) = ATTRS.OPAQUE_VALUE or ATTRS(attr_type) = ATTRS.HASH then
        select case ATTRS(attr_type)
        case ATTRS.BUNDLE_ID
          // This is included for hash generation
          key = Keys.kReceiptBundleIdentiferData
        case ATTRS.OPAQUE_VALUE
          key = Keys.kReceiptOpaqueValue
        case ATTRS.HASH
          key = Keys.kReceiptHash
        end select
        dim mb as MemoryBlock = p
        result.Value(key) = mb.StringValue(0, length)
      end
      
      // Strings
      if ATTRS(attr_type) = ATTRS.BUNDLE_ID or ATTRS(attr_type) = ATTRS.VERSION then
        dim str_type, str_length as Integer
        dim str_p as Ptr = p
        call ASN1_get_object (str_p, str_length, str_type, xclass, seq_end - str_p)
        if str_type = 12 then ' V_ASN1_UTF8STRING
          dim mb as MemoryBlock = str_p
          dim str as String = mb.StringValue(0,str_length).DefineEncoding(Encodings.UTF8)
          select case ATTRS(attr_type)
          case ATTRS.BUNDLE_ID
            key = Keys.kReceiptBundleIdentifer
          case ATTRS.VERSION
            key = Keys.kReceiptVersion
          end select
          result.Value(key) = str
        end
      end
    end
    p = p + Ptr(length)
    
  end if
  
  // Skip any remaining fields in this SEQUENCE
  while p < seq_end
    call ASN1_get_object (p, length, type, xclass, seq_end - p)
    p = p + Ptr(length)
  wend
  
wend

bail1:
PKCS7_free (p7)

return result

#endif
End Function

In the app open event we check the receipt with:

if not CertTools.IsValid (CertTools.DeviceGUID, CertTools.ReadReceipt(f), “de.pps4me.MASTestApp”) then
declare sub exit_ lib “System” alias “exit” (code as Integer)
exit_ (173)
end if

If I try to build my app in 64 Bit, I got these errors:

CertTools.ReadReceipt, line 41
Expected (Ptr, Int32, Int32, Int32, Int32), but these arguments are (Ptr, Integer, Integer, Integer, Int64)
res = ASN1_get_object (p, length, type, xclass, e-p)

CertTools.ReadReceipt, line 46
Expected (Ptr, Int32, Int32, Int32, Int32), but these arguments are (Ptr, Integer, Integer, Integer, Int64)
call ASN1_get_object (p, length, type, xclass, seq_end - p)

CertTools.ReadReceipt, line 56
Expected (Ptr, Int32, Int32, Int32, Int32), but these arguments are (Ptr, Integer, Integer, Integer, Int64)
res = ASN1_get_object (p, length, type, xclass, e-p)

CertTools.ReadReceipt, line 63
Expected (Ptr, Int32, Int32, Int32, Int32), but these arguments are (Ptr, Integer, Integer, Integer, Int64)
call ASN1_get_object (p, length, type, xclass, seq_end - p)

CertTools.ReadReceipt, line 73
Expected (Ptr, Int32, Int32, Int32, Int32), but these arguments are (Ptr, Integer, Integer, Integer, Int64)
call ASN1_get_object (p, length, type, xclass, seq_end - p)

CertTools.ReadReceipt, line 94
Expected (Ptr, Int32, Int32, Int32, Int32), but these arguments are (Ptr, Integer, Integer, Integer, Int64)
call ASN1_get_object (str_p, str_length, str_type, class, sequined - str_p)

CertTools.ReadReceipt, line 114
Expected (Ptr, Int32, Int32, Int32, Int32), but these arguments are (Ptr, Integer, Integer, Integer, Int64)
call ASN1_get_object (str_p, str_length, str_type, class, sequined - str_p)

You can if you spend some time on it
Its telling you quite literally what is expected vs whats declared

CertTools.ReadReceipt, line 114
Expected (Ptr, Int32, Int32, Int32, Int32), but these arguments are (Ptr, Integer, Integer, Integer, Int64)

So look in CertTools.ReadReciept
And change the declaration of the Declare to match what is expected

Now what I CANNOT tell you is whether that is the correct change for a 64 bit version of CertTools

Any news about this?

Yes, I got a new example which is working in 64Bit. For more information read this “Mac App Store

Here’s a direct link to the code (and a disclaimer).

[quote]I ended up creating a 64-Bit adaptation of @Thomas Tempelmann’s Receipt validation code, which I then modified to suit my needs. It will have to be completely re-written as all of the API has been depreciated, so we don’t know when it won’t work.

The code is available from the following link.
http://www.ohanaware.com/xojo/ReceiptTester.zip[/quote]

Thanks Sam - This helped me out.