1/*
2 * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * SecFramework.c - generic non API class specific functions
26 */
27
28#ifdef STANDALONE
29/* Allows us to build genanchors against the BaseSDK. */
30#undef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
31#undef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
32#endif
33
34#include "SecFramework.h"
35#include <pthread.h>
36#include <CoreFoundation/CFBundle.h>
37#include <CoreFoundation/CFURLAccess.h>
38#include <Security/SecRandom.h>
39#include <CommonCrypto/CommonDigest.h>
40#include <CommonCrypto/CommonDigestSPI.h>
41#include <Security/SecAsn1Coder.h>
42#include <Security/oidsalg.h>
43#include <fcntl.h>
44#include <sys/types.h>
45#include <unistd.h>
46#include <utilities/debugging.h>
47#include <Security/SecBase.h>
48#include <errno.h>
49#include <inttypes.h>
50
51/* Security.framework's bundle id. */
52static CFStringRef kSecFrameworkBundleID = CFSTR("com.apple.Security");
53
54/* Security framework's own bundle used for localized string lookups. */
55static CFBundleRef kSecFrameworkBundle;
56static pthread_once_t kSecFrameworkBundleLookup = PTHREAD_ONCE_INIT;
57
58static void SecFrameworkBundleLookup(void) {
59    kSecFrameworkBundle = CFBundleGetBundleWithIdentifier(kSecFrameworkBundleID);
60    if (kSecFrameworkBundle)
61        CFRetain(kSecFrameworkBundle);
62}
63
64CFStringRef SecFrameworkCopyLocalizedString(CFStringRef key,
65    CFStringRef tableName) {
66    pthread_once(&kSecFrameworkBundleLookup, SecFrameworkBundleLookup);
67    if (kSecFrameworkBundle) {
68        return CFBundleCopyLocalizedString(kSecFrameworkBundle, key, key,
69            tableName);
70    }
71
72    CFRetain(key);
73    return key;
74}
75
76CFURLRef SecFrameworkCopyResourceURL(CFStringRef resourceName,
77	CFStringRef resourceType, CFStringRef subDirName) {
78    CFURLRef url = NULL;
79    pthread_once(&kSecFrameworkBundleLookup, SecFrameworkBundleLookup);
80    if (kSecFrameworkBundle) {
81        url = CFBundleCopyResourceURL(kSecFrameworkBundle, resourceName,
82			resourceType, subDirName);
83		if (!url) {
84            secwarning("resource: %@.%@ in %@ not found", resourceName,
85                resourceType, subDirName);
86		}
87    }
88
89	return url;
90}
91
92
93CFDataRef SecFrameworkCopyResourceContents(CFStringRef resourceName,
94	CFStringRef resourceType, CFStringRef subDirName) {
95    CFURLRef url = SecFrameworkCopyResourceURL(resourceName, resourceType,
96        subDirName);
97	CFDataRef data = NULL;
98    if (url) {
99        SInt32 error;
100        if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
101            url, &data, NULL, NULL, &error)) {
102            secwarning("read: %ld", (long) error);
103        }
104        CFRelease(url);
105    }
106
107	return data;
108}
109
110/* Return the SHA1 digest of a chunk of data as newly allocated CFDataRef. */
111CFDataRef SecSHA1DigestCreate(CFAllocatorRef allocator,
112	const UInt8 *data, CFIndex length) {
113	CFMutableDataRef digest = CFDataCreateMutable(allocator,
114		CC_SHA1_DIGEST_LENGTH);
115	CFDataSetLength(digest, CC_SHA1_DIGEST_LENGTH);
116	//FIXME: Cast from CFIndex to CC_LONG
117	CCDigest(kCCDigestSHA1, data, (CC_LONG)length, CFDataGetMutableBytePtr(digest));
118	return digest;
119}
120
121CFDataRef SecDigestCreate(CFAllocatorRef allocator,
122    const SecAsn1Oid *algorithm, const SecAsn1Item *params,
123	const UInt8 *data, CFIndex length) {
124    unsigned char *(*digestFcn)(const void *data, CC_LONG len, unsigned char *md);
125    CFIndex digestLen;
126
127    if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA1)) {
128        digestFcn = CC_SHA1;
129        digestLen = CC_SHA1_DIGEST_LENGTH;
130    } else if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA224)) {
131        digestFcn = CC_SHA224;
132        digestLen = CC_SHA224_DIGEST_LENGTH;
133    } else if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA256)) {
134        digestFcn = CC_SHA256;
135        digestLen = CC_SHA256_DIGEST_LENGTH;
136    } else if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA384)) {
137        digestFcn = CC_SHA384;
138        digestLen = CC_SHA384_DIGEST_LENGTH;
139    } else if (SecAsn1OidCompare(algorithm, &CSSMOID_SHA512)) {
140        digestFcn = CC_SHA512;
141        digestLen = CC_SHA512_DIGEST_LENGTH;
142    } else {
143        return NULL;
144    }
145
146	CFMutableDataRef digest = CFDataCreateMutable(allocator, digestLen);
147	CFDataSetLength(digest, digestLen);
148	//FIXME: Cast from CFIndex to CC_LONG
149	digestFcn(data, (CC_LONG)length, CFDataGetMutableBytePtr(digest));
150	return digest;
151}
152
153/* Default random ref for /dev/random. */
154const SecRandomRef kSecRandomDefault = NULL;
155
156/* File descriptor for "/dev/random". */
157static int kSecRandomFD;
158static pthread_once_t kSecDevRandomOpen = PTHREAD_ONCE_INIT;
159
160static void SecDevRandomOpen(void) {
161    kSecRandomFD = open("/dev/random", O_RDONLY);
162}
163
164int SecRandomCopyBytes(SecRandomRef rnd, size_t count, uint8_t *bytes) {
165    if (rnd != kSecRandomDefault)
166        return errSecParam;
167    pthread_once(&kSecDevRandomOpen, SecDevRandomOpen);
168    if (kSecRandomFD < 0)
169        return -1;
170    while (count) {
171        ssize_t bytes_read = read(kSecRandomFD, bytes, count);
172        if (bytes_read == -1) {
173            if (errno == EINTR)
174                continue;
175            return -1;
176        }
177        if (bytes_read == 0) {
178            return -1;
179        }
180        bytes += bytes_read;
181        count -= bytes_read;
182    }
183
184	return 0;
185}
186
187#if 0
188#include <CommonCrypto/CommonDigest.h>
189#include <stdlib.h>
190
191/* FIPS rng declarations. */
192typedef struct __SecRandom *SecRandomRef;
193SecRandomRef SecRandomCreate(CFIndex randomAlg, CFIndex seedLength,
194	const UInt8 *seed);
195void SecRandomCopyBytes(SecRandomRef randomref, CFIndex numBytes, UInt8 *outBytes);
196
197/* FIPS Rng implementation. */
198struct __SecRandom {
199	CC_SHA1_CTX sha1;
200	CFIndex bytesLeft;
201	UInt8 block[64];
202};
203
204SecRandomRef SecRandomCreate(CFIndex randomAlg, CFIndex seedLength,
205	const UInt8 *seed) {
206	SecRandomRef result = (SecRandomRef)malloc(sizeof(struct __SecRandom));
207	CC_SHA1_Init(&result->sha1);
208	memset(result->block + 20, 0, 44);
209	result->bytesLeft = 0;
210
211	if (seedLength) {
212		/* Digest the seed and put it into output. */
213		CCDigest(kCCDigestSHA1, seed, seedLength, result->block);
214	} else {
215		/* Seed 20 bytes from "/dev/srandom". */
216		int fd = open("/dev/srandom", O_RDONLY);
217		if (fd < 0)
218			goto errOut;
219
220		if (read(fd, result->block, 20) != 20)
221			goto errOut;
222
223		close(fd);
224	}
225
226	CC_SHA1_Update(&result->sha1, result->block, 64);
227
228	return result;
229
230errOut:
231	free(result);
232	return NULL;
233}
234
235void SecRandomCopyBytes(SecRandomRef randomref, CFIndex numBytes,
236	UInt8 *outBytes) {
237	while (numBytes > 0) {
238		if (!randomref->bytesLeft) {
239			CC_SHA1_Update(&randomref->sha1, randomref->block, 64);
240			OSWriteBigInt32(randomref->block, 0, randomref->sha1.h0);
241			OSWriteBigInt32(randomref->block, 4, randomref->sha1.h1);
242			OSWriteBigInt32(randomref->block, 8, randomref->sha1.h2);
243			OSWriteBigInt32(randomref->block, 12, randomref->sha1.h3);
244			OSWriteBigInt32(randomref->block, 16, randomref->sha1.h4);
245			randomref->bytesLeft = 20;
246		}
247		CFIndex outLength = (numBytes > randomref->bytesLeft ?
248							 randomref->bytesLeft : numBytes);
249		memcpy(outBytes, randomref->block + 20 - randomref->bytesLeft,
250			outLength);
251		randomref->bytesLeft -= outLength;
252		outBytes += outLength;
253		numBytes -= outLength;
254	}
255}
256#endif
257