1/*
2 * Copyright (c) 2003 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please
7 * obtain a copy of the License at http://www.apple.com/publicsource and
8 * read it before using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
12 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
13 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
15 * Please see the License for the specific language governing rights and
16 * limitations under the License.
17 */
18/*
19 * p12pbe.h - PKCS12 PBE routine. App space reference version.
20 *
21 * Created 2/28/03 by Doug Mitchell.
22*/
23
24#include "p12pbe.h"
25#include <string.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include <Security/cssm.h>
29#include <openssl/bn.h>
30/*
31 * For development outside the CSP, malloc using stdlib.
32 * Inside the CSP we'll use CssmAllocator.
33 */
34#define PBE_MALLOC	malloc
35#define PBE_FREE	free
36
37/*
38 * implementation dependent hash object
39 * for now just a Digest context handle
40 */
41typedef CSSM_CC_HANDLE HashHand;
42static HashHand hashCreate(CSSM_CSP_HANDLE cspHand,
43	CSSM_ALGORITHMS alg)
44{
45	CSSM_CC_HANDLE hashHand;
46	CSSM_RETURN crtn = CSSM_CSP_CreateDigestContext(cspHand,
47		alg,
48		&hashHand);
49	if(crtn) {
50		printf("CSSM_CSP_CreateDigestContext error\n");
51		return 0;
52	}
53	return hashHand;
54}
55
56static CSSM_RETURN hashInit(HashHand hand)
57{
58	return CSSM_DigestDataInit(hand);
59}
60
61static CSSM_RETURN hashUpdate(HashHand hand,
62	const unsigned char *buf,
63	unsigned bufLen)
64{
65	const CSSM_DATA cdata = {bufLen, (uint8 *)buf};
66	return CSSM_DigestDataUpdate(hand, &cdata, 1);
67}
68
69static CSSM_RETURN hashFinal(HashHand hand,
70	unsigned char *digest,		// mallocd by caller
71	unsigned *digestLen)		// IN/OUT
72{
73	CSSM_DATA cdata = {(uint32)digestLen, digest};
74	return CSSM_DigestDataFinal(hand, &cdata);
75}
76
77static CSSM_RETURN hashDone(HashHand hand)
78{
79	return CSSM_DeleteContext(hand);
80}
81
82/*
83 * Create a "string" (in the loose p12 notation) of specified length
84 * from the concatention of copies of the specified input string.
85 */
86static unsigned char *p12StrCat(
87	const unsigned char *inStr,
88	unsigned inStrLen,
89	unsigned outLen,
90	unsigned char *outStr = NULL)	// if not present, we malloc
91{
92	if(outStr == NULL) {
93		outStr = (unsigned char *)PBE_MALLOC(outLen);
94	}
95	unsigned toMove = outLen;
96	unsigned char *outp = outStr;
97	while(toMove) {
98		unsigned thisMove = inStrLen;
99		if(thisMove > toMove) {
100			thisMove = toMove;
101		}
102		memmove(outp, inStr, thisMove);
103		toMove -= thisMove;
104		outp   += thisMove;
105	}
106	return outStr;
107}
108
109/*
110 * PBE generator per PKCS12 v.1 section B.2.
111 */
112CSSM_RETURN p12PbeGen_app(
113	const CSSM_DATA	&pwd,		// unicode, double null terminated
114	const unsigned char *salt,
115	unsigned saltLen,
116	unsigned iterCount,
117	P12_PBE_ID pbeId,
118	CSSM_ALGORITHMS hashAlg,	// MS5 or SHA1 only
119	CSSM_CSP_HANDLE cspHand,
120	/* result goes here, mallocd by caller */
121	unsigned char *outbuf,
122	unsigned outbufLen)
123{
124	CSSM_RETURN ourRtn;
125	unsigned unipassLen = pwd.Length;
126	unsigned char *unipass = pwd.Data;
127
128	/*
129	 * all variables of the form p12_<XXX> represent <XXX> from the
130	 * PKCS12 spec. E.g., p12_u is u, the length of the digest output.
131	 * Only difference here is: all of our sizes are in BYTES, not
132	 * bits.
133	 */
134	unsigned p12_r = iterCount;
135	unsigned p12_n = outbufLen;
136
137	unsigned p12_u;					// hash output size
138	unsigned p12_v;					// hash block size
139	unsigned char *p12_P = NULL;	// catted passwords
140	unsigned char *p12_S = NULL;	// catted salts
141
142	switch(hashAlg) {
143		case CSSM_ALGID_MD5:
144			p12_u = 16;
145			p12_v = 64;
146			break;
147		case CSSM_ALGID_SHA1:
148			p12_u = 20;
149			p12_v = 64;
150			break;
151		default:
152			return CSSMERR_CSP_INVALID_ALGORITHM;
153	}
154
155	/*
156	 * 1. Construct a string, D (the “diversifier”), by
157	 *    concatenating v/8 copies of ID.
158	 */
159	unsigned char *p12_D = NULL;		// diversifier
160	p12_D = (unsigned char *)PBE_MALLOC(p12_v);
161	/* subsequent errors to errOut: */
162	for(unsigned dex=0; dex<p12_v; dex++) {
163		p12_D[dex] = (unsigned char)pbeId;
164	}
165
166	/*
167	 * 2. Concatenate copies of the salt together to create
168	 *    a string S of length v * ceil(s/v) bits (the final copy
169	 *    of the salt may be truncated to create S). Note that if
170	 *    the salt is the empty string, then so is S.
171	 */
172	unsigned p12_Slen = p12_v * ((saltLen + p12_v - 1) / p12_v);
173	if(p12_Slen) {
174		p12_S = p12StrCat(salt, saltLen, p12_Slen);
175	}
176
177
178	/*
179	 * 3. Concatenate copies of the password together to create
180	 *    a string P of length v * ceil(p/v) bits (the final copy of
181	 *    the password may be truncated to create P). Note that
182	 *    if the password is the empty string, then so is P.
183	 */
184	unsigned p12_Plen = p12_v * ((unipassLen + p12_v - 1) / p12_v);
185	if(p12_Plen) {
186		p12_P = p12StrCat(unipass, unipassLen, p12_Plen);
187	}
188
189	/*
190	 * 4. Set I= S||P to be the concatenation of S and P.
191	 */
192	unsigned char *p12_I =
193		(unsigned char *)PBE_MALLOC(p12_Slen + p12_Plen);
194	memmove(p12_I, p12_S, p12_Slen);
195	if(p12_Plen) {
196		memmove(p12_I + p12_Slen, p12_P, p12_Plen);
197	}
198
199	/*
200	 * 5. Set c = ceil(n/u).
201	 */
202	unsigned p12_c = (p12_n + p12_u - 1) / p12_u;
203
204	/* allocate c hash-output-size bufs */
205	unsigned char *p12_A = (unsigned char *)PBE_MALLOC(p12_c * p12_u);
206
207	/* one reusable hash object */
208	HashHand hashHand = hashCreate(cspHand, hashAlg);
209	if(!hashHand) {
210		return CSSMERR_CSP_INVALID_CONTEXT_HANDLE;	// XXX
211	}
212
213	/* reused inside the loop */
214	unsigned char *p12_B = (unsigned char *)PBE_MALLOC(p12_v + sizeof(int));
215	BIGNUM *Ij = BN_new();
216	BIGNUM *Bpl1 = BN_new();
217
218	/*
219	 * 6. For i=1, 2, ..., p12_c, do the following:
220	 */
221	for(unsigned p12_i=0; p12_i<p12_c; p12_i++) {
222		unsigned char *p12_AsubI = p12_A + (p12_i * p12_u);
223
224		/*
225		 * a) Set A[i] = H**r(D||I). (i.e. the rth hash of D||I,
226		 *    H(H(H(...H(D||I))))
227		 */
228		ourRtn = hashInit(hashHand);
229		if(ourRtn) break;
230		ourRtn = hashUpdate(hashHand, p12_D, p12_v);
231		if(ourRtn) break;
232		ourRtn = hashUpdate(hashHand, p12_I, p12_Slen + p12_Plen);
233		if(ourRtn) break;
234		unsigned outLen = p12_u;
235		ourRtn = hashFinal(hashHand, p12_AsubI, &outLen);
236		if(ourRtn) break;
237
238		for(unsigned iter=1; iter<p12_r; iter++) {
239			ourRtn = hashInit(hashHand);
240			if(ourRtn) break;
241			ourRtn = hashUpdate(hashHand, p12_AsubI, p12_u);
242			if(ourRtn) break;
243			ourRtn = hashFinal(hashHand, p12_AsubI, &outLen);
244			if(ourRtn) break;
245		}
246
247		/*
248		 * b) Concatenate copies of A[i] to create a string B of
249		 *    length v bits (the final copy of A[i]i may be truncated
250		 *    to create B).
251		 */
252		p12StrCat(p12_AsubI, p12_u, p12_v, p12_B);
253
254		/*
255		 * c) Treating I as a concatenation I[0], I[1], ...,
256		 *    I[k-1] of v-bit blocks, where k = ceil(s/v) + ceil(p/v),
257		 *    modify I by setting I[j]=(I[j]+B+1) mod (2 ** v)
258		 *    for each j.
259		 *
260		 * Copied from PKCS12_key_gen_uni() from openssl...
261		 */
262		/* Work out B + 1 first then can use B as tmp space */
263		BN_bin2bn (p12_B, p12_v, Bpl1);
264		BN_add_word (Bpl1, 1);
265		unsigned Ilen = p12_Slen + p12_Plen;
266
267		for (unsigned j = 0; j < Ilen; j+=p12_v) {
268			BN_bin2bn (p12_I + j, p12_v, Ij);
269			BN_add (Ij, Ij, Bpl1);
270			BN_bn2bin (Ij, p12_B);
271			unsigned Ijlen = BN_num_bytes (Ij);
272			/* If more than 2^(v*8) - 1 cut off MSB */
273			if (Ijlen > p12_v) {
274				BN_bn2bin (Ij, p12_B);
275				memcpy (p12_I + j, p12_B + 1, p12_v);
276			/* If less than v bytes pad with zeroes */
277			} else if (Ijlen < p12_v) {
278				memset(p12_I + j, 0, p12_v - Ijlen);
279				BN_bn2bin(Ij, p12_I + j + p12_v - Ijlen);
280			} else BN_bn2bin (Ij, p12_I + j);
281		}
282	}
283
284	if(ourRtn) {
285		goto errOut;
286	}
287
288	/*
289	 * 7. Concatenate A[1], A[2], ..., A[c] together to form a
290	 *    pseudo-random bit string, A.
291	 *
292	 * 8. Use the first n bits of A as the output of this entire
293	 *    process.
294	 */
295	memmove(outbuf, p12_A, outbufLen);
296	ourRtn = CSSM_OK;
297
298errOut:
299	/* FIXME clear all these strings */
300	if(p12_D) {
301		PBE_FREE(p12_D);
302	}
303	if(p12_S) {
304		PBE_FREE(p12_S);
305	}
306	if(p12_P) {
307		PBE_FREE(p12_P);
308	}
309	if(p12_I) {
310		PBE_FREE(p12_I);
311	}
312	if(p12_A) {
313		PBE_FREE(p12_A);
314	}
315	if(p12_B) {
316		PBE_FREE(p12_B);
317	}
318	if(hashHand) {
319		hashDone(hashHand);
320	}
321	BN_free(Bpl1);
322	BN_free(Ij);
323	return ourRtn;
324}
325
326