6

I am attempting to implement some AES256 crypto routines in Apple Swift to kinda toy around with the interoperability between ObjC, C, and Swift code and data types, and have run into a bit of an issue and am hoping someone has some suggestions on something I've overlooked.

As most are familiar, common C-style pattern is to declare an uninitialized pointer and then pass that into a function, wherein the function call malloc()s an object and points the pointer to it; after the function call completes the pointer points to the newly-created object. The Common Crypto libraries use this in some places; most notably when creating a new CCCryptor object (really a struct behind the scenes, it looks like, typedef'ed to CCCryptorRef for an opaque reference) - the final argument to the call to CCCryptorCreate() is such a pointer and should, at the conclusion of the function call, contain a pointer to the CCCryptorRef.

Swift puts a wrinkle into this - variables cannot be uninitialized, they always either have a value or are nil(/optional), and thus I've run into a bit of a bump here. The following doesn't work because CCCryptorCreate (rightfully) acts as though I'm just passing nil as the last argument, which I am:

var myCryptorRef: CMutablePointer<Unmanaged<CCCryptorRef>?> = nil
CCCryptorCreate(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES128),
  CCOptions(kCCOptionECBMode), seedData.bytes(), UInt(kCCKeySizeAES256), 
  nil, myCryptorRef)

But neither can you declare it as an optional, like so:

var myCryptorRef: CMutablePointer<Unmanaged<CCCryptorRef>?>?

Or as a non-pointer type:

var myCryptoRef: Unmanaged<CCCryptorRef>?
CCCryptorCreate(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES128),
  CCOptions(kCCOptionECBMode), seedData.bytes(), UInt(kCCKeySizeAES256), 
  nil, &myCryptorRef)

Since what CCCryptorCreate expects is either an uninitialized pointer to a CCCryptorRef or an already-malloc()'ed CCCryptorRef, attempting to pass it the address of a not-yet-initialized object doesn't go very well as you might expect.

So, it comes down to this: can anyone come up with a way to initialize that CCCryptor struct before the call to CCCryptorCreate (the Swift-standard method of struct init by naming all variables inside the struct does not seem to work), or some alternate construction that would allow me to preserve the C concept of the uninitialized pointer for use in this way? Thank you for any suggestions you may have.

Adding for clarity from the comments: Swift interprets the call to CCCryptorCreate() as the following:

CCCryptorCreate(op: CCOperation, alg: CCAlgorithm, options: CCOptions, 
  key: CConstVoidPointer, keyLength: UInt, iv: CConstVoidPointer, 
  cryptorRef: CMutablePointer<Unmanaged<CCCryptor>?>)

An additional edit for some other things I've tried: Just to be truly absurd, I tried the following, but it also did not work (EXC_BAD_ACCESS on the call to fromOpaque):

var someMem = malloc(UInt(sizeof(CCCryptor)))
var crashTime = Unmanaged<CCCryptor>.fromOpaque(someMem)

Also, every place CCCryptor or CCCryptorRef is mentioned, I have tried either one - in CommonCrypto/CommonCryptor.h, CCCryptorRef is defined thusly:

typedef struct _CCCryptor *CCCryptorRef

So while existing Objective-C code examples use CCCryptorRef, I've been trying either.

Khaibit
  • 95
  • 1
  • 5
  • It would help if you added what the Swift type (aka function signature) is for `CCCryptorCreate`, I'm not familiar with that API. In particular, what is the type of its last argument? – Analog File Jun 06 '14 at 21:15

2 Answers2

3

Try this:

var myCryptor: Unmanaged<CCCryptor>?

CCCryptorCreate( .... , &myCryptor )

From Using Swift with Cocoa and Objective-C

If you have declared a function like this one:

func takesAMutablePointer(x: CMutablePointer<Float>) { /*...*/ }

You can call it in any of the following ways:

var x: Float = 0.0 
var p: CMutablePointer<Float> = nil 
var a: Float[] = [1.0, 2.0, 3.0] 
takesAMutablePointer(nil) 
takesAMutablePointer(p) 
takesAMutablePointer(&x) 
takesAMutablePointer(&a)

You should also check out this and convert the Unmanaged reference into something that is managed.

Analog File
  • 5,280
  • 20
  • 23
  • 1
    CCCryptorCreate returns without any error code, but myCryptor!.takeUnretainedValue() afterwards causes an EXC_BAD_ACCESS(code=1, address=0x20), attempting to retain a non-existent object. Debugging immediately after the call to CCCryptorCreate, myCryptor's value cannot be displayed by lldb - it shows as "(_value = ). I had this issue earlier in the code with a different function (and the CC_SHA256_CTX struct) and was only able to resolve it by manually creating the struct to allocate the memory - I cannot seem to manually allocate a CCCryptor struct however. – Khaibit Jun 06 '14 at 22:01
  • Hmm. Use an if to check if it is nil. That's the first thing to do if you get a crash when using ! I'm sorry I cannot help you much beyond this. I do not personally have the compiler, the compiler is still beta and sometimes does weird things (from what I hear many people say), and I'm ignorant of that specific API. – Analog File Jun 06 '14 at 22:10
  • But I suspect that there is an intrinsic problem. In other words the way Swift imported that function may be wrong. Unfortunately I do not have the compiler and I do not find any docs about the actual implementation of `Unmanaged`and `CMutablePointer`. I think they are structures, but I do not have access to the headers therefore there's much I cannot check. If everything is not EXACTLY as large (in the sizeof sense) as a plain pointer, then the way the API is being translated is just wrong. – Analog File Jun 06 '14 at 22:38
  • Updated post regarding the CCCryptor/CCCryptorRef issue. – Khaibit Jun 06 '14 at 22:50
  • Hmm. Thanks for accepting the answer even if it's not really a solution. I do believe that, unless there's some problem with the compiler, that is the correct way to do it. But I do not have the compiler and you say it does not work. So ... you may want to post this to Apple forums and maybe file a bug report if you do not find a solution on those forums. – Analog File Jun 06 '14 at 22:57
  • I suspect you are correct that it is an intrinsic beta issue, and that's why I accepted the answer. That method of passing an uninitialized reference works elsewhere, the Common Crypto API may just not be fully compatible yet. Thanks again! – Khaibit Jun 06 '14 at 23:02
  • Still true. I just tried MIDIClientCreate() passing in var client : Unmanaged? The status is OK, the reference is not nil, but takeRetainedValue yields the bad access problem just like your example. – Gene De Lisa Dec 30 '14 at 15:26
0

Here's an example that works on beta 3, using a type from librabbitmq:

let envelopePtr = UnsafePointer<amqp_envelope_t>.alloc(1)
reply = amqp_consume_message(connection, envelopePtr, nil, 0)
let envelope = envelopePtr.memory
jbg
  • 4,903
  • 1
  • 27
  • 30