Thursday, January 7, 2016

SecTransformExecuteAsync Considered Confusing

Jeff Johnson:

We have a group transform that first generates a digest from the message and then signs the message digest. Intuitively, you would think that the intermediate result would be the digest, and the final result would be the signature, right? Right?

I don’t know how it’s intended to work, but my interpretation of the vague documentation is that you don’t get results from the intermediate transforms. You get intermediate results for the final transform. This is the same result as with SecTransformExecute, only it’s possibly split into chunks. isFinal means that you’ve received the last chunk.

The documentation in Dash says that the SecTransform functions aren’t available in Swift, but that seems to be incorrect.

3 Comments RSS · Twitter

Exactly as you say, it’s chunk-wise async returning of the same data that the sync version of the API would return. Exactly the same situation as async I/O and honestly, no other interpretation even occurred to me - certainly not getting intermediate data that are an internal detail of the chained transform!

VS, do you have knowledge of this, or are you just speculating? CFDataRef is only one possible return type for transforms. If we're getting chunks of data and are supposed to concatenate them to get the final result, that definitely needs to be documented by Apple. The existing documentation is maddeningly vague: "There may be multple[sic] results depending on the transform." Regardless of whether we get CFDataRef chunks, the notion of chunks doesn't even apply to a number of transform types, so it's a bit too facile to claim that this is totally obvious and analogous to data I/O. My sample code uses CFDataRef, but it is merely a simplified example. When I discovered this problem, I was using a transform that returns a boolean, which of course would never be sent in chunks.

I didn't design the API (or to be accurate the API was designed already, and while I got to add a few things, I didn't get to significantly alter the foundational design), but I did implement most of the transforms.

Some transforms need to stream, so API has to be flexible to let any of them stream. As a general rule none generate more chunks then "needed" (i.e. if you feed one chunk in you will rarely see >1 chunk out, and never >2, for example if you have a block cypher in some padding mode and the incoming chunk isn't a multiple of the block size you will get an extra chunk out at the end of the stream). You ought no rely on that because someone else may revisit that choice.

I don't recall any transforms that are part of the final API that stream anything other then CFDataRefs, I don't think it even has any that stream CFStrings. I had implemented some that produced a stream of CFNumber (proof of concept, or maybe useful in the unit tests...it has been like half a decade since I've seen that code -- I know it went 3 or so years without any real changes, but I haven't paid much attention to it since then).

Hopefully despite the lack of entirely clear documentation it still ends up being useful to you.

Leave a Comment