SecMaskGenerationFunctionTransform.c [plain text]
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include "SecMaskGenerationFunctionTransform.h"
#include "SecCustomTransform.h"
#include "SecDigestTransform.h"
#include "misc.h"
#include "Utilities.h"
static const CFStringRef kMaskGenerationFunctionTransformName = CFSTR("com.apple.security.MGF1");
static const CFStringRef kLengthName = CFSTR("Length");
static SecTransformInstanceBlock MaskGenerationFunctionTransform(CFStringRef name,
SecTransformRef newTransform,
SecTransformImplementationRef ref)
{
__block CFMutableDataRef accumulator = CFDataCreateMutable(NULL, 0);
__block int32_t outputLength = 0;
SecTransformInstanceBlock instanceBlock = ^{
SecTransformSetTransformAction(ref, kSecTransformActionFinalize, ^{
CFRelease(accumulator);
return (CFTypeRef)NULL;
});
SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kLengthName, ^CFTypeRef(SecTransformAttributeRef attribute, CFTypeRef value) {
CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &outputLength);
if (outputLength <= 0) {
SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateSecTransformErrorRef(kSecTransformErrorInvalidLength, "MaskGenerationFunction Length must be one or more (not %@)", value));
}
return (CFTypeRef)NULL;
});
SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, ^CFTypeRef(SecTransformAttributeRef attribute, CFTypeRef value) {
if (value) {
CFDataRef d = value;
CFDataAppendBytes(accumulator, CFDataGetBytePtr(d), CFDataGetLength(d));
} else {
int32_t i = 0, l = 0;
(void)transforms_assume(outputLength > 0);
CFStringRef digestType = SecTranformCustomGetAttribute(ref, kSecDigestTypeAttribute, kSecTransformMetaAttributeValue);
SecTransformRef digest0 = transforms_assume(SecDigestTransformCreate(digestType, 0, NULL));
int32_t digestLength = 0;
{
CFNumberRef digestLengthAsCFNumber = SecTransformGetAttribute(digest0, kSecDigestLengthAttribute);
CFNumberGetValue(transforms_assume(digestLengthAsCFNumber), kCFNumberSInt32Type, &digestLength);
}
(void)transforms_assume(digestLength >= 0);
UInt8 *buffer = malloc(outputLength + digestLength);
if (!buffer) {
SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, GetNoMemoryErrorAndRetain());
return (CFErrorRef)NULL;
}
dispatch_group_t all_hashed = dispatch_group_create();
dispatch_group_enter(all_hashed);
for(; l < outputLength; l += digestLength, i++) {
dispatch_group_enter(all_hashed);
CFErrorRef err = NULL;
SecTransformRef digest = NULL;
if (l == 0) {
digest = digest0;
} else {
digest = SecDigestTransformCreate(digestType, 0, &err);
if (digest == NULL) {
SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, err);
return (CFErrorRef)NULL;
}
}
CFMutableDataRef accumulatorPlusCounter = CFDataCreateMutableCopy(NULL, CFDataGetLength(accumulator) + sizeof(uint32_t), accumulator);
int32_t bigendian_i = htonl(i);
CFDataAppendBytes(accumulatorPlusCounter, (UInt8*)&bigendian_i, sizeof(bigendian_i));
SecTransformSetAttribute(digest, kSecTransformInputAttributeName, accumulatorPlusCounter, &err);
CFRelease(accumulatorPlusCounter);
UInt8 *buf = buffer + l;
SecTransformExecuteAsync(digest, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) {
if (message) {
CFIndex messageLen = CFDataGetLength(message);
CFDataGetBytes(message, CFRangeMake(0, messageLen), buf);
}
if (error) {
SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, error);
}
if (isFinal) {
dispatch_group_leave(all_hashed);
}
});
CFRelease(digest);
}
dispatch_group_leave(all_hashed);
dispatch_group_wait(all_hashed, DISPATCH_TIME_FOREVER);
CFDataRef out = CFDataCreateWithBytesNoCopy(NULL, buffer, outputLength, kCFAllocatorMalloc);
SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, out);
CFRelease(out);
SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, NULL);
}
return (CFErrorRef)NULL;
});
return (CFErrorRef)NULL;
};
return Block_copy(instanceBlock);
}
SecTransformRef SecCreateMaskGenerationFunctionTransform(CFStringRef hashType, int length, CFErrorRef *error)
{
static dispatch_once_t once;
__block Boolean ok = TRUE;
if (length <= 0) {
if (error) {
*error = CreateSecTransformErrorRef(kSecTransformErrorInvalidLength, "MaskGenerationFunction Length must be one or more (not %d)", length);
}
return NULL;
}
dispatch_once(&once, ^(void) {
ok = SecTransformRegister(kMaskGenerationFunctionTransformName, MaskGenerationFunctionTransform, error);
});
if (!ok) {
return NULL;
}
SecTransformRef ret = SecTransformCreate(kMaskGenerationFunctionTransformName, error);
if (!ret) {
return NULL;
}
if (!SecTransformSetAttribute(ret, kSecDigestTypeAttribute, hashType ? hashType : kSecDigestSHA1, error)) {
CFRelease(ret);
return NULL;
}
CFNumberRef len = CFNumberCreate(NULL, kCFNumberIntType, &length);
ok = SecTransformSetAttribute(ret, kLengthName, len, error);
CFRelease(len);
if (!ok) {
CFRelease(ret);
return NULL;
}
return ret;
}