--- /dev/null
+//
+// SecMaskGenerationFunctionTransform.c
+// libsecurity_transform
+//
+// Created by Josh Osborne on 10/6/11.
+// Copyright 2011 Apple. All rights reserved.
+//
+
+#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;
+ });
+
+ // XXX: be a good citizen, put a validator in for the types.
+
+ 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;
+ }
+ }
+
+ // NOTE: we shuld be able to do this without the copy, make a transform that takes an
+ // array and outputs each item in the array followed by a NULL ought to be quicker.
+ 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;
+}
\ No newline at end of file