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}