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.

1
  • 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? Commented Jun 6, 2014 at 21:15

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

7 Comments

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 = <no summary available>). 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.
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.
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 Unmanagedand 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.
Updated post regarding the CCCryptor/CCCryptorRef issue.
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.
|
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.