1// 2// SecMaskGenerationFunctionTransform.c 3// libsecurity_transform 4// 5// Created by Josh Osborne on 10/6/11. 6// Copyright 2011 Apple. All rights reserved. 7// 8 9#include <stdio.h> 10#include <CoreFoundation/CoreFoundation.h> 11#include "SecMaskGenerationFunctionTransform.h" 12#include "SecCustomTransform.h" 13#include "SecDigestTransform.h" 14#include "misc.h" 15#include "Utilities.h" 16 17static const CFStringRef kMaskGenerationFunctionTransformName = CFSTR("com.apple.security.MGF1"); 18static const CFStringRef kLengthName = CFSTR("Length"); 19 20static SecTransformInstanceBlock MaskGenerationFunctionTransform(CFStringRef name, 21 SecTransformRef newTransform, 22 SecTransformImplementationRef ref) 23{ 24 __block CFMutableDataRef accumulator = CFDataCreateMutable(NULL, 0); 25 __block int32_t outputLength = 0; 26 27 SecTransformInstanceBlock instanceBlock = ^{ 28 SecTransformSetTransformAction(ref, kSecTransformActionFinalize, ^{ 29 CFRelease(accumulator); 30 31 return (CFTypeRef)NULL; 32 }); 33 34 // XXX: be a good citizen, put a validator in for the types. 35 36 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kLengthName, ^CFTypeRef(SecTransformAttributeRef attribute, CFTypeRef value) { 37 CFNumberGetValue((CFNumberRef)value, kCFNumberSInt32Type, &outputLength); 38 if (outputLength <= 0) { 39 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateSecTransformErrorRef(kSecTransformErrorInvalidLength, "MaskGenerationFunction Length must be one or more (not %@)", value)); 40 } 41 42 return (CFTypeRef)NULL; 43 }); 44 45 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, ^CFTypeRef(SecTransformAttributeRef attribute, CFTypeRef value) { 46 if (value) { 47 CFDataRef d = value; 48 CFDataAppendBytes(accumulator, CFDataGetBytePtr(d), CFDataGetLength(d)); 49 } else { 50 int32_t i = 0, l = 0; 51 (void)transforms_assume(outputLength > 0); 52 CFStringRef digestType = SecTranformCustomGetAttribute(ref, kSecDigestTypeAttribute, kSecTransformMetaAttributeValue); 53 SecTransformRef digest0 = transforms_assume(SecDigestTransformCreate(digestType, 0, NULL)); 54 int32_t digestLength = 0; 55 { 56 CFNumberRef digestLengthAsCFNumber = SecTransformGetAttribute(digest0, kSecDigestLengthAttribute); 57 CFNumberGetValue(transforms_assume(digestLengthAsCFNumber), kCFNumberSInt32Type, &digestLength); 58 } 59 (void)transforms_assume(digestLength >= 0); 60 61 UInt8 *buffer = malloc(outputLength + digestLength); 62 if (!buffer) { 63 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, GetNoMemoryErrorAndRetain()); 64 return (CFErrorRef)NULL; 65 } 66 67 dispatch_group_t all_hashed = dispatch_group_create(); 68 dispatch_group_enter(all_hashed); 69 for(; l < outputLength; l += digestLength, i++) { 70 dispatch_group_enter(all_hashed); 71 CFErrorRef err = NULL; 72 SecTransformRef digest = NULL; 73 if (l == 0) { 74 digest = digest0; 75 } else { 76 digest = SecDigestTransformCreate(digestType, 0, &err); 77 if (digest == NULL) { 78 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, err); 79 return (CFErrorRef)NULL; 80 } 81 } 82 83 // NOTE: we shuld be able to do this without the copy, make a transform that takes an 84 // array and outputs each item in the array followed by a NULL ought to be quicker. 85 CFMutableDataRef accumulatorPlusCounter = CFDataCreateMutableCopy(NULL, CFDataGetLength(accumulator) + sizeof(uint32_t), accumulator); 86 int32_t bigendian_i = htonl(i); 87 CFDataAppendBytes(accumulatorPlusCounter, (UInt8*)&bigendian_i, sizeof(bigendian_i)); 88 SecTransformSetAttribute(digest, kSecTransformInputAttributeName, accumulatorPlusCounter, &err); 89 CFRelease(accumulatorPlusCounter); 90 91 UInt8 *buf = buffer + l; 92 SecTransformExecuteAsync(digest, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) { 93 if (message) { 94 CFIndex messageLen = CFDataGetLength(message); 95 CFDataGetBytes(message, CFRangeMake(0, messageLen), buf); 96 } 97 if (error) { 98 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, error); 99 } 100 if (isFinal) { 101 dispatch_group_leave(all_hashed); 102 } 103 }); 104 CFRelease(digest); 105 } 106 107 dispatch_group_leave(all_hashed); 108 dispatch_group_wait(all_hashed, DISPATCH_TIME_FOREVER); 109 CFDataRef out = CFDataCreateWithBytesNoCopy(NULL, buffer, outputLength, kCFAllocatorMalloc); 110 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, out); 111 CFRelease(out); 112 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, NULL); 113 } 114 return (CFErrorRef)NULL; 115 }); 116 117 return (CFErrorRef)NULL; 118 }; 119 120 return Block_copy(instanceBlock); 121} 122 123SecTransformRef SecCreateMaskGenerationFunctionTransform(CFStringRef hashType, int length, CFErrorRef *error) 124{ 125 static dispatch_once_t once; 126 __block Boolean ok = TRUE; 127 128 if (length <= 0) { 129 if (error) { 130 *error = CreateSecTransformErrorRef(kSecTransformErrorInvalidLength, "MaskGenerationFunction Length must be one or more (not %d)", length); 131 } 132 return NULL; 133 } 134 135 dispatch_once(&once, ^(void) { 136 ok = SecTransformRegister(kMaskGenerationFunctionTransformName, MaskGenerationFunctionTransform, error); 137 }); 138 139 if (!ok) { 140 return NULL; 141 } 142 143 SecTransformRef ret = SecTransformCreate(kMaskGenerationFunctionTransformName, error); 144 if (!ret) { 145 return NULL; 146 } 147 148 if (!SecTransformSetAttribute(ret, kSecDigestTypeAttribute, hashType ? hashType : kSecDigestSHA1, error)) { 149 CFRelease(ret); 150 return NULL; 151 } 152 153 CFNumberRef len = CFNumberCreate(NULL, kCFNumberIntType, &length); 154 ok = SecTransformSetAttribute(ret, kLengthName, len, error); 155 CFRelease(len); 156 if (!ok) { 157 CFRelease(ret); 158 return NULL; 159 } 160 161 return ret; 162}