1/*
2 *  Digest_block.cpp
3 *  libsecurity_transform
4 *
5 *  Created by JOsborne on 2/20/10.
6 *  Copyright 2010 Apple. All rights reserved.
7 *
8 */
9
10#include "Digest_block.h"
11#include "SecCustomTransform.h"
12#include <CommonCrypto/CommonDigest.h>
13#include "Utilities.h"
14
15extern const CFStringRef kSecDigestMD2, kSecDigestMD4, kSecDigestMD5, kSecDigestSHA1, kSecDigestSHA2;
16extern const CFStringRef kSecDigestTypeAttribute, kSecDigestLengthAttribute;
17
18
19static CFStringRef kCustomDigestTransformName = CFSTR("com.apple.security.digest");
20
21SecTransformRef SecDigestTransformCreate_block(SecProviderRef device, CFTypeRef digestType, CFIndex digestLength, CFErrorRef* error) {
22	static dispatch_once_t inited;
23	__block CFStringRef current_algo;
24	__block CFIndex current_length;
25
26	dispatch_once(&inited, ^{
27		SecTransformRegister(kCustomDigestTransformName, ^(CFStringRef name, SecTransformRebindActionBlock rebind) {
28			rebind(kSecTransformActionProcessData, NULL, ^(SecTransformRef tr, CFDataRef d, SecTransformSendAttribute set) {
29				set(kSecDigestTypeAttribute, kSecDigestSHA2);
30			});
31			rebind(SecTransformSetAttributeAction, NULL, ^(SecTransformRef tr, CFStringRef name, CFTypeRef value, SecTransformSendAttribute set) {
32				Boolean algo_changed = FALSE;
33				if (name == kSecDigestTypeAttribute || CFStringCompare(kSecDigestTypeAttribute, name, 0) == kCFCompareEqualTo) {
34					algo_changed = TRUE;
35					current_algo = CFStringCreateCopy(NULL, (CFStringRef)value);
36
37				} else if (name == kSecDigestLengthAttribute || CFStringCompare(kSecDigestLengthAttribute, name, 0) == kCFCompareEqualTo) {
38					algo_changed = TRUE;
39					CFNumberGetValue(value, kCFNumberCFIndexType, &current_length);
40				} else if (CFStringCompare(kSecTransformInputAttributeName, name, 0) == kCFCompareEqualTo) {
41					return (CFTypeRef)CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "The type %@ is invalid.", name);
42				} else {
43					return (CFTypeRef)CreateSecTransformErrorRef(kSecTransformErrorInvalidType, "The type %@ is invalid.", name);
44				}
45				if (!algo_changed) {
46					return value;
47				}
48
49				if (current_algo == kSecDigestSHA2 || CFStringCompare(current_algo, kSecDigestSHA2, 0) == kCFCompareEqualTo) {
50					switch (current_length) {
51						case 0:
52						case 512: {
53							__block CC_SHA512_CTX cc_context;
54							CC_SHA512_Init(&cc_context);
55							rebind(kSecTransformActionProcessData, NULL, ^(SecTransformRef tr, CFDataRef d, SecTransformSendAttribute set) {
56								if (d) {
57									CC_SHA512_Update(&cc_context, CFDataGetBytePtr(d), CFDataGetLength(d));
58									return SecTransformNoData();
59								} else {
60									u_int8_t digest_buffer[CC_SHA512_DIGEST_LENGTH];
61
62									CC_SHA512_Final(digest_buffer, &cc_context);
63									set(kSecTransformOutputAttributeName, CFDataCreate(NULL, digest_buffer, CC_SHA512_DIGEST_LENGTH));
64									return NULL;
65								}
66							});
67							return value;
68						}
69					}
70					return (CFTypeRef)CreateSecTransformErrorRef(kSecTransformErrorInvalidLength, "Invalid length.");
71				} else {
72					return (CFTypeRef)CreateSecTransformErrorRef(kSecTransformErrorInvalidAlgorithm, "Invalid algorithm.");
73				}
74			});
75		}, NULL);
76	});
77
78	SecTransformRef dt = SecTransformCreate(kCustomDigestTransformName, NULL);
79	SecTransformSetAttribute(dt, kSecDigestTypeAttribute, digestType, error);
80	CFNumberRef dlen = CFNumberCreate(NULL, kCFNumberCFIndexType, &digestLength);
81	SecTransformSetAttribute(dt, kSecDigestLengthAttribute, dlen, error);
82	CFRelease(dlen);
83
84	return dt;
85}
86