X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_transform/lib/SecMaskGenerationFunctionTransform.c diff --git a/Security/libsecurity_transform/lib/SecMaskGenerationFunctionTransform.c b/Security/libsecurity_transform/lib/SecMaskGenerationFunctionTransform.c new file mode 100644 index 00000000..8cbb1197 --- /dev/null +++ b/Security/libsecurity_transform/lib/SecMaskGenerationFunctionTransform.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2011-2012,2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#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; +}