1/*
2 * Copyright (c) 2003,2005 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/*
20 * p12Crypto.cpp - PKCS12 Crypto routines. App space reference version.
21 *
22 * Created 2/28/03 by Doug Mitchell.
23 */
24
25#include "p12Crypto.h"
26#include "pkcs12Utils.h"
27#include <security_cdsa_utils/cuCdsaUtils.h>
28#include <Security/cssmapple.h>
29
30/*
31 * Free memory via specified plugin's app-level allocator
32 */
33static void appFreeCssmMemory(
34	CSSM_HANDLE	hand,
35	void 			*p)
36{
37	CSSM_API_MEMORY_FUNCS memFuncs;
38	CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
39	if(crtn) {
40		cssmPerror("CSSM_GetAPIMemoryFunctions", crtn);
41		/* oh well, leak and continue */
42		return;
43	}
44	memFuncs.free_func(p, memFuncs.AllocRef);
45}
46
47#define KEY_LABEL	"p12 key"
48
49/*
50 * Given appropriate P12-style parameters, cook up a CSSM_KEY.
51 */
52CSSM_RETURN p12KeyGen_app(
53	CSSM_CSP_HANDLE		cspHand,
54	CSSM_KEY			&key,
55	bool				isForEncr,	// true: en/decrypt   false: MAC
56	CSSM_ALGORITHMS		keyAlg,
57	CSSM_ALGORITHMS		pbeHashAlg,	// SHA1, MD5 only
58	uint32				keySizeInBits,
59	uint32				iterCount,
60	const CSSM_DATA		&salt,
61	const CSSM_DATA		&pwd,		// unicode, double null terminated
62	CSSM_DATA			&iv)		// referent is optional
63{
64	CSSM_RETURN					crtn;
65	CSSM_CC_HANDLE 				ccHand;
66	CSSM_DATA					dummyLabel;
67	CSSM_ACCESS_CREDENTIALS		creds;
68
69	memset(&key, 0, sizeof(CSSM_KEY));
70	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
71
72	/* infer key derivation algorithm */
73	CSSM_ALGORITHMS deriveAlg = CSSM_ALGID_NONE;
74	if(pbeHashAlg != CSSM_ALGID_SHA1) {
75		return CSSMERR_CSP_INVALID_ALGORITHM;
76	}
77	if(isForEncr) {
78		/*
79		 * FIXME - if this key is going to be used to wrap/unwrap a
80		 * shrouded key bag, its usage will change accordingly...
81		 */
82		deriveAlg = CSSM_ALGID_PKCS12_PBE_ENCR;
83	}
84	else {
85		deriveAlg = CSSM_ALGID_PKCS12_PBE_MAC;
86	}
87	CSSM_CRYPTO_DATA seed;
88	seed.Param = pwd;
89	seed.Callback = NULL;
90	seed.CallerCtx = NULL;
91
92	crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
93		deriveAlg,
94		keyAlg,
95		keySizeInBits,
96		&creds,
97		NULL,			// BaseKey
98		iterCount,
99		&salt,
100		&seed,			// seed
101		&ccHand);
102	if(crtn) {
103		cuPrintError("CSSM_CSP_CreateDeriveKeyContext", crtn);
104		return crtn;
105	}
106
107	dummyLabel.Length = strlen(KEY_LABEL);
108	dummyLabel.Data = (uint8 *)KEY_LABEL;
109
110	/* KEYUSE_ANY - this is just an ephemeral session key */
111	crtn = CSSM_DeriveKey(ccHand,
112		&iv,
113		CSSM_KEYUSE_ANY,
114		//CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
115		CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
116		&dummyLabel,
117		NULL,			// cred and acl
118		&key);
119	CSSM_DeleteContext(ccHand);
120	if(crtn) {
121		cuPrintError("CSSM_DeriveKey", crtn);
122	}
123	return crtn;
124}
125
126/*
127 * Decrypt (typically, an encrypted P7 ContentInfo contents or
128 * a P12 ShroudedKeyBag).
129 */
130CSSM_RETURN p12Decrypt_app(
131	CSSM_CSP_HANDLE		cspHand,
132	const CSSM_DATA		&cipherText,
133	CSSM_ALGORITHMS		keyAlg,
134	CSSM_ALGORITHMS		encrAlg,
135	CSSM_ALGORITHMS		pbeHashAlg,			// SHA1, MD5 only
136	uint32				keySizeInBits,
137	uint32				blockSizeInBytes,	// for IV
138	CSSM_PADDING		padding,			// CSSM_PADDING_PKCS7, etc.
139	CSSM_ENCRYPT_MODE	mode,				// CSSM_ALGMODE_CBCPadIV8, etc.
140	uint32				iterCount,
141	const CSSM_DATA		&salt,
142	const CSSM_DATA		&pwd,		// unicode, double null terminated
143	SecNssCoder			&coder,		// for mallocing KeyData and plainText
144	CSSM_DATA			&plainText)
145{
146	CSSM_RETURN crtn;
147	CSSM_KEY ckey;
148	CSSM_CC_HANDLE ccHand = 0;
149
150	/* P12 style IV derivation, optional */
151	CSSM_DATA iv = {0, NULL};
152	CSSM_DATA_PTR ivPtr = NULL;
153	if(blockSizeInBytes) {
154		coder.allocItem(iv, blockSizeInBytes);
155		ivPtr = &iv;
156	}
157
158	/* P12 style key derivation */
159	crtn = p12KeyGen_app(cspHand, ckey, true, keyAlg, pbeHashAlg,
160		keySizeInBits, iterCount, salt, pwd, iv);
161	if(crtn) {
162		return crtn;
163	}
164
165	/* CSSM context */
166	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
167		encrAlg,
168		mode,
169		NULL,			// access cred
170		&ckey,
171		ivPtr,			// InitVector, optional
172		padding,
173		NULL,			// Params
174		&ccHand);
175	if(crtn) {
176		cuPrintError("CSSM_CSP_CreateSymmetricContext", crtn);
177		return crtn;
178	}
179
180	/* go - CSP mallocs ptext and rem data */
181	CSSM_DATA ourPtext = {0, NULL};
182	CSSM_DATA remData = {0, NULL};
183	uint32 bytesDecrypted;
184	crtn = CSSM_DecryptData(ccHand,
185		&cipherText,
186		1,
187		&ourPtext,
188		1,
189		&bytesDecrypted,
190		&remData);
191	if(crtn) {
192		cuPrintError("CSSM_EncryptData", crtn);
193	}
194	else {
195		coder.allocCopyItem(ourPtext, plainText);
196		plainText.Length = bytesDecrypted;
197
198		/* plaintext copied into coder space; free the memory allocated
199		 * by the CSP */
200		appFreeCssmMemory(cspHand, ourPtext.Data);
201	}
202	/* an artifact of CSPFUllPLuginSession - this never contains
203	 * valid data but sometimes gets mallocds */
204	if(remData.Data) {
205		appFreeCssmMemory(cspHand, remData.Data);
206	}
207	CSSM_DeleteContext(ccHand);
208	CSSM_FreeKey(cspHand, NULL, &ckey, CSSM_FALSE);
209	return crtn;
210}
211
212/*
213 * Calculate the MAC for a PFX. Caller is either going compare
214 * the result against an existing PFX's MAC or drop the result into
215 * a newly created PFX.
216 */
217CSSM_RETURN p12GenMac_app(
218	CSSM_CSP_HANDLE		cspHand,
219	const CSSM_DATA		&ptext,	// e.g., NSS_P12_DecodedPFX.derAuthSaafe
220	CSSM_ALGORITHMS		alg,	// better be SHA1!
221	unsigned			iterCount,
222	const CSSM_DATA		&salt,
223	const CSSM_DATA		&pwd,		// unicode, double null terminated
224	SecNssCoder			&coder,		// for mallocing macData
225	CSSM_DATA			&macData)	// RETURNED
226{
227	CSSM_RETURN crtn;
228
229	/* P12 style key derivation */
230	unsigned keySizeInBits;
231	CSSM_ALGORITHMS hmacAlg;
232	switch(alg) {
233		case CSSM_ALGID_SHA1:
234			keySizeInBits = 160;
235			hmacAlg = CSSM_ALGID_SHA1HMAC;
236			break;
237		case CSSM_ALGID_MD5:
238			/* not even sure if this is legal in p12 world... */
239			keySizeInBits = 128;
240			hmacAlg = CSSM_ALGID_MD5HMAC;
241			break;
242		default:
243			return CSSMERR_CSP_INVALID_ALGORITHM;
244	}
245	CSSM_KEY macKey;
246	CSSM_DATA iv = {0, NULL};
247	crtn = p12KeyGen_app(cspHand, macKey, false, hmacAlg, alg,
248		keySizeInBits, iterCount, salt, pwd, iv);
249	if(crtn) {
250		return crtn;
251	}
252
253	/* prealloc the mac data */
254	coder.allocItem(macData, keySizeInBits / 8);
255	CSSM_CC_HANDLE ccHand = 0;
256	crtn = CSSM_CSP_CreateMacContext(cspHand, hmacAlg, &macKey, &ccHand);
257	if(crtn) {
258		cuPrintError("CSSM_CSP_CreateMacContext", crtn);
259		return crtn;
260	}
261
262	crtn = CSSM_GenerateMac (ccHand, &ptext, 1, &macData);
263	if(crtn) {
264		cuPrintError("CSSM_GenerateMac", crtn);
265	}
266	CSSM_DeleteContext(ccHand);
267	CSSM_FreeKey(cspHand, NULL, &macKey, CSSM_FALSE);
268	return crtn;
269}
270
271/*
272 * Verify MAC on an existing PFX.
273 */
274CSSM_RETURN p12VerifyMac_app(
275	const NSS_P12_DecodedPFX 	&pfx,
276	CSSM_CSP_HANDLE				cspHand,
277	const CSSM_DATA				&pwd,	// unicode, double null terminated
278	SecNssCoder					&coder)	// for temp mallocs
279{
280	if(pfx.macData == NULL) {
281		return CSSMERR_CSP_INVALID_SIGNATURE;
282	}
283	NSS_P12_MacData &macData = *pfx.macData;
284	NSS_P7_DigestInfo &digestInfo  = macData.mac;
285	CSSM_OID &algOid = digestInfo.digestAlgorithm.algorithm;
286	CSSM_ALGORITHMS macAlg;
287	if(!cssmOidToAlg(&algOid, &macAlg)) {
288		return CSSMERR_CSP_INVALID_ALGORITHM;
289	}
290	uint32 iterCount = 0;
291	CSSM_DATA &citer = macData.iterations;
292	if(!p12DataToInt(citer, iterCount)) {
293		return CSSMERR_CSP_INVALID_ATTR_ROUNDS;
294	}
295	if(iterCount == 0) {
296		/* optional, default 1 */
297		iterCount = 1;
298	}
299
300	/*
301	 * In classic fashion, the PKCS12 spec now says:
302	 *
303	 *      When password integrity mode is used to secure a PFX PDU,
304	 *      an SHA-1 HMAC is computed on the BER-encoding of the contents
305	 *      of the content field of the authSafe field in the PFX PDU.
306	 *
307	 * So here we go.
308	 */
309	CSSM_DATA genMac;
310	CSSM_RETURN crtn = p12GenMac_app(cspHand, *pfx.authSafe.content.data,
311		macAlg, iterCount, macData.macSalt, pwd, coder, genMac);
312	if(crtn) {
313		return crtn;
314	}
315	if(nssCompareCssmData(&genMac, &digestInfo.digest)) {
316		return CSSM_OK;
317	}
318	else {
319		return CSSMERR_CSP_VERIFY_FAILED;
320	}
321}
322
323