According to the API for SecTransformExecuteAsync
(which I won't link to because Apple will inevitably break the URL):
There may be multple[sic] results depending on the transform. The block knows that the processing is complete when the isFinal parameter is set to true.
And for the isFinal
parameter of SecMessageBlock
:
If set the message returned is the final result otherwise it is an intermediate result.
With that in mind, consider the following code:
@import Security;
@implementation AppDelegate
-(void)signMessage:(NSData *)message privateKey:(SecKeyRef)privateKey isAsync:(BOOL)isAsync
{
SecTransformRef digest = SecDigestTransformCreate( kSecDigestSHA2, 256, NULL );
SecTransformSetAttribute( digest, kSecTransformInputAttributeName, (CFDataRef)message, NULL );
SecTransformRef sign = SecSignTransformCreate( privateKey, NULL );
SecTransformSetAttribute( sign, kSecInputIsAttributeName, kSecInputIsDigest, NULL );
SecTransformSetAttribute( sign, kSecDigestTypeAttribute, kSecDigestSHA2, NULL );
SecTransformSetAttribute( sign, kSecDigestLengthAttribute, @256, NULL );
SecGroupTransformRef group = SecTransformCreateGroupTransform();
SecTransformConnectTransforms( digest, kSecTransformOutputAttributeName, sign, kSecTransformInputAttributeName, group, NULL );
if ( isAsync )
{
SecTransformExecuteAsync( group, dispatch_get_main_queue(), ^( CFTypeRef result, CFErrorRef error, Boolean isFinal ) {
NSLog( @"isFinal:%i\n%@", isFinal, (NSData *)result );
} );
}
else
{
CFTypeRef result = SecTransformExecute( group, NULL );
NSLog( @"Sync\n%@", (NSData *)result );
}
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSDictionary *parameters = @{ (NSString *)kSecAttrKeyType: (NSString *)kSecAttrKeyTypeRSA, (NSString *)kSecAttrKeySizeInBits : @2048 };
SecKeyRef publicKey;
SecKeyRef privateKey;
SecKeyGeneratePair( (CFDictionaryRef)parameters, &publicKey, &privateKey );
NSData *message = [[NSFileHandle fileHandleForReadingAtPath:@"/dev/random"] readDataOfLength:500];
[self signMessage:message privateKey:privateKey isAsync:NO];
[self signMessage:message privateKey:privateKey isAsync:YES];
}
@end
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?
Nope.
In reality, the "intermediate" (isFinal false) result is the signature, and the "final" (isFinal true) result is NULL
.
I'm not even sure how you're supposed to use SecTransformExecuteAsync
. In the example above, there are only 2 transforms in the group, but what if there are more than 2? In your block, which may get called more than once with isFinal false, how are you supposed to determine the real "final" result?
I spent an entire day puzzling over why SecTransformExecuteAsync
would not give the proper result, because I enclosed my SecMessageBlock
result checking code with if (isFinal)
. I was only interested in the final result, and like a logical person, I read the documentation and assumed that isFinal
would give me the final result. Live and learn — to not trust Apple.