1/*
2 * Copyright (c) 1999-2001,2005-2007,2010-2014 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 * sslBER.c - BER routines
26 */
27
28#if USE_CDSA_CRYPTO
29
30#include "ssl.h"
31#include "sslMemory.h"
32#include "sslDebug.h"
33#include "sslBER.h"
34#include "sslCrypto.h"
35
36#include <string.h>
37
38#include "appleCdsa.h"
39#include "SecureTransportPriv.h"
40
41#include <string.h>
42#include <Security/SecAsn1Coder.h>
43#include <Security/keyTemplates.h>
44#include <security_asn1/nssUtils.h>
45#include <Security/oidsattr.h>
46#include <Security/oidsalg.h>
47
48/* we should get rid of this low level stuff and use SecAsn1Coder throughout... */
49#include <security_asn1/secasn1.h>
50
51#define SSLBUF_TO_SECITEM(sb, cd)  {		\
52	(cd)->Length = (sb)->length; 		\
53	(cd)->Data   = (sb)->data;			\
54}
55
56/*
57 * Given a PKCS-1 encoded RSA public key, extract the
58 * modulus and public exponent.
59 *
60 * RSAPublicKey ::= SEQUENCE {
61 *		modulus INTEGER, -- n
62 *		publicExponent INTEGER -- e }
63 */
64
65/*
66 * Default chunk size for new arena pool.
67 * FIXME: analyze & measure different defaults here. I'm pretty sure
68 * that only performance - not correct behavior - is affected by
69 * an arena pool's chunk size.
70 */
71#define CHUNKSIZE_DEF		1024
72
73OSStatus sslDecodeRsaBlob(
74	const SSLBuffer	*blob,			/* PKCS-1 encoded */
75	SSLBuffer		*modulus,		/* data mallocd and RETURNED */
76	SSLBuffer		*exponent)		/* data mallocd and RETURNED */
77{
78    SECStatus rv;
79	OSStatus srtn;
80	NSS_RSAPublicKeyPKCS1 nssPubKey = {};
81    PLArenaPool *pool;
82
83	assert(blob != NULL);
84	assert(modulus != NULL);
85	assert(exponent != NULL);
86
87	/* DER-decode the blob */
88    pool = PORT_NewArena(CHUNKSIZE_DEF);
89    rv = SEC_ASN1Decode(pool, &nssPubKey,
90        kSecAsn1RSAPublicKeyPKCS1Template, (const char *)blob->data, blob->length);
91    if (rv != SECSuccess)
92		srtn = errSSLBadCert;
93    else {
94        /* malloc & copy components */
95        srtn = SSLCopyBufferFromData(nssPubKey.modulus.Data,
96            nssPubKey.modulus.Length, modulus);
97        if(!srtn) {
98            srtn = SSLCopyBufferFromData(nssPubKey.publicExponent.Data,
99                nssPubKey.publicExponent.Length, exponent);
100        }
101    }
102    PORT_FreeArena(pool, PR_TRUE);
103    return srtn;
104}
105
106/*
107 * Given a raw modulus and exponent, cook up a
108 * BER-encoded RSA public key blob.
109 */
110OSStatus sslEncodeRsaBlob(
111	const SSLBuffer	*modulus,
112	const SSLBuffer	*exponent,
113	SSLBuffer		*blob)			/* data mallocd and RETURNED */
114{
115    PLArenaPool *pool;
116	OSStatus srtn;
117    SECItem *encBlob, dest = {};
118	NSS_RSAPublicKeyPKCS1 nssPubKey;
119
120	assert((modulus != NULL) && (exponent != NULL));
121
122	/* convert to NSS_RSAPublicKeyPKCS1 */
123	SSLBUF_TO_SECITEM(modulus, &nssPubKey.modulus);
124	SSLBUF_TO_SECITEM(exponent, &nssPubKey.publicExponent);
125
126	/* DER encode */
127    pool = PORT_NewArena(CHUNKSIZE_DEF);
128    encBlob = SEC_ASN1EncodeItem(pool, &dest, &nssPubKey,
129        kSecAsn1RSAPublicKeyPKCS1Template);
130	if (!encBlob)
131		srtn = errSecAllocate;
132    else {
133        /* copy out to caller */
134        srtn = SSLCopyBufferFromData(encBlob->Data, encBlob->Length, blob);
135    }
136
137    PORT_FreeArena(pool, PR_TRUE);
138    return srtn;
139}
140
141#if APPLE_DH
142/*
143 * Given a DER encoded DHParameterBlock, extract the prime and generator.
144 * modulus and public exponent.
145 * This will work with either PKCS-1 encoded DHParameterBlock or
146 * openssl-style DHParameter.
147 */
148OSStatus sslDecodeDhParams(
149	const SSLBuffer	*blob,			/* PKCS-1 encoded */
150	SSLBuffer		*prime,			/* data mallocd and RETURNED */
151	SSLBuffer		*generator)		/* data mallocd and RETURNED */
152{
153    SECStatus rv;
154	OSStatus srtn;
155	NSS_DHParameterBlock paramBlock = {};
156    PLArenaPool *pool;
157
158	assert(blob != NULL);
159	assert(prime != NULL);
160	assert(generator != NULL);
161
162    pool = PORT_NewArena(CHUNKSIZE_DEF);
163	/*
164	 * Since the common case here is to decode a parameter block coming
165	 * over the wire, which is in openssl format, let's try that format first.
166	 */
167    rv = SEC_ASN1Decode(pool, &paramBlock.params,
168        kSecAsn1DHParameterTemplate, (const char *)blob->data, blob->length);
169    if (rv != SECSuccess) {
170		/*
171		 * OK, that failed when trying as a CDSA_formatted parameter
172		 * block DHParameterBlock). Openssl uses a subset of that,
173		 * a DHParameter. Try that instead.
174		 */
175		memset(&paramBlock, 0, sizeof(paramBlock));
176        rv = SEC_ASN1Decode(pool, &paramBlock,
177            kSecAsn1DHParameterBlockTemplate,
178            (const char *)blob->data, blob->length);
179	}
180
181    if (rv != SECSuccess) {
182        /* Ah well, we tried. */
183        sslErrorLog("sslDecodeDhParams: both CDSA and openssl format"
184            "failed\n");
185        srtn = errSSLCrypto;
186    }
187    else {
188        /* copy out components */
189        srtn = SSLCopyBufferFromData(paramBlock.params.prime.Data,
190            paramBlock.params.prime.Length, prime);
191        if(!srtn) {
192            srtn = SSLCopyBufferFromData(paramBlock.params.base.Data,
193                paramBlock.params.base.Length, generator);
194        }
195    }
196
197    PORT_FreeArena(pool, PR_TRUE);
198    return srtn;
199}
200
201/*
202 * Given a prime and generator, cook up a BER-encoded DHParameter blob.
203 */
204OSStatus sslEncodeDhParams(
205	const SSLBuffer	*prime,
206	const SSLBuffer	*generator,
207	SSLBuffer		*blob)			/* data mallocd and RETURNED */
208{
209    PLArenaPool *pool;
210	OSStatus srtn;
211    SECItem *encBlob, dest = {};
212	NSS_DHParameter dhParams;
213
214	assert((prime != NULL) && (generator != NULL));
215
216	/* convert to NSS_DHParameter */
217	SSLBUF_TO_SECITEM(prime, &dhParams.prime);
218	SSLBUF_TO_SECITEM(generator, &dhParams.base);
219	dhParams.privateValueLength.Data = NULL;
220	dhParams.privateValueLength.Length = 0;
221
222	/* DER encode */
223    pool = PORT_NewArena(CHUNKSIZE_DEF);
224    encBlob = SEC_ASN1EncodeItem(pool, &dest, &dhParams,
225        kSecAsn1DHParameterTemplate);
226	if (!encBlob)
227		srtn = errSecAllocate;
228    else {
229        /* copy out to caller */
230        srtn = SSLCopyBufferFromData(encBlob->Data, encBlob->Length, blob);
231    }
232
233    PORT_FreeArena(pool, PR_TRUE);
234    return srtn;
235}
236#endif /* APPLE_DH */
237
238/*
239 * Given an ECDSA key in CSSM format, extract the SSL_ECDSA_NamedCurve
240 * from its algorithm parameters.
241 */
242OSStatus sslEcdsaPeerCurve(
243	CSSM_KEY_PTR pubKey,
244	SSL_ECDSA_NamedCurve *namedCurve)
245{
246	SecAsn1CoderRef coder = NULL;
247	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO subjPubKeyInfo;
248	CSSM_X509_ALGORITHM_IDENTIFIER *algId = &subjPubKeyInfo.algorithm;
249	CSSM_OID curveOid;
250	OSStatus ortn;
251
252	CSSM_KEYHEADER *hdr = &pubKey->KeyHeader;
253	if(hdr->AlgorithmId != CSSM_ALGID_ECDSA) {
254	   sslErrorLog("sslEcdsaPeerCurve: bad peer key algorithm\n");
255	   return errSSLProtocol;
256	}
257	if(hdr->BlobType != CSSM_KEYBLOB_RAW) {
258		/* No can do - this must be raw format, it came from the CL */
259	   sslErrorLog("sslEcdsaPeerCurve: bad peer key algorithm\n");
260	   return errSSLProtocol;
261	}
262	if(hdr->Format != CSSM_KEYBLOB_RAW_FORMAT_X509) {
263	   sslErrorLog("sslEcdsaPeerCurve: bad peer key format\n");
264	   return errSSLProtocol;
265	}
266
267	/* KeyData is an encoded CSSM_X509_SUBJECT_PUBLIC_KEY_INFO */
268	ortn = SecAsn1CoderCreate(&coder);
269	if(ortn) {
270		return errSSLInternal;
271	}
272	/* subsequent errors to errOut: */
273
274	memset(&subjPubKeyInfo, 0, sizeof(subjPubKeyInfo));
275	ortn = SecAsn1DecodeData(coder, &pubKey->KeyData, kSecAsn1SubjectPublicKeyInfoTemplate,
276		&subjPubKeyInfo);
277	if(ortn) {
278		printf("sslEcdsaPeerCurve: error decoding public key\n");
279		goto errOut;
280	}
281
282	if(!nssCompareCssmData(&algId->algorithm, &CSSMOID_ecPublicKey)) {
283		printf("sslEcdsaPeerCurve: unexpected algorithm ID in public key\n");
284		ortn = errSSLProtocol;
285		goto errOut;
286	}
287	if((algId->parameters.Data[0] != BER_TAG_OID) ||
288	   (algId->parameters.Length < 2)) {
289		printf("sslEcdsaPeerCurve: missing algorithm parameters in public key\n");
290		ortn = errSSLProtocol;
291		goto errOut;
292	}
293
294	/*
295	 * The curve OID is DER-encoded since the parameters are ASN_ANY.
296	 * Quickie decode for further processing...
297	 */
298	curveOid.Data = algId->parameters.Data + 2;
299	curveOid.Length = algId->parameters.Length - 2;
300
301	/* algId->parameters is the curve OID */
302	if(nssCompareCssmData(&curveOid, &CSSMOID_secp256r1)) {
303		*namedCurve = SSL_Curve_secp256r1;
304	}
305	else if(nssCompareCssmData(&curveOid, &CSSMOID_secp384r1)) {
306		*namedCurve = SSL_Curve_secp384r1;
307	}
308	else if(nssCompareCssmData(&curveOid, &CSSMOID_secp521r1)) {
309		*namedCurve = SSL_Curve_secp521r1;
310	}
311	/* Others? Later. That's all we support for now. */
312	else {
313		printf("sslEcdsaPeerCurve: missing algorithm parameters in public key\n");
314		ortn = errSSLProtocol;
315	}
316
317errOut:
318	SecAsn1CoderRelease(coder);
319	return ortn;
320}
321
322/*
323 * Given an ECDSA public key in X509 format, extract the raw public key
324 * bits in ECPOint format.
325 */
326OSStatus sslEcdsaPubKeyBits(
327	CSSM_KEY_PTR	pubKey,
328	SSLBuffer		*pubBits)		/* data mallocd and RETURNED */
329{
330	SecAsn1CoderRef coder = NULL;
331	CSSM_X509_SUBJECT_PUBLIC_KEY_INFO subjPubKeyInfo;
332	OSStatus ortn = errSecSuccess;
333
334	CSSM_KEYHEADER *hdr = &pubKey->KeyHeader;
335	if(hdr->AlgorithmId != CSSM_ALGID_ECDSA) {
336	   sslErrorLog("sslEcdsaPubKeyBits: bad peer key algorithm\n");
337	   return errSSLProtocol;
338	}
339	if(hdr->BlobType != CSSM_KEYBLOB_RAW) {
340		/* No can do - this must be raw format, it came from the CL */
341	   sslErrorLog("sslEcdsaPubKeyBits: bad peer key algorithm\n");
342	   return errSSLProtocol;
343	}
344	if(hdr->Format != CSSM_KEYBLOB_RAW_FORMAT_X509) {
345	   sslErrorLog("sslEcdsaPubKeyBits: bad peer key format\n");
346	   return errSSLProtocol;
347	}
348
349	/* KeyData is an encoded CSSM_X509_SUBJECT_PUBLIC_KEY_INFO */
350	ortn = SecAsn1CoderCreate(&coder);
351	if(ortn) {
352		return errSSLInternal;
353	}
354	/* subsequent errors to errOut: */
355
356	memset(&subjPubKeyInfo, 0, sizeof(subjPubKeyInfo));
357	ortn = SecAsn1DecodeData(coder, &pubKey->KeyData, kSecAsn1SubjectPublicKeyInfoTemplate,
358		&subjPubKeyInfo);
359	if(ortn) {
360		printf("sslEcdsaPubKeyBits: error decoding public key\n");
361		goto errOut;
362	}
363	/* that key data is a BITSTRING */
364	ortn = SSLCopyBufferFromData(subjPubKeyInfo.subjectPublicKey.Data,
365		subjPubKeyInfo.subjectPublicKey.Length >> 3, pubBits);
366errOut:
367	SecAsn1CoderRelease(coder);
368	return ortn;
369}
370
371#endif /* USE_CDSA_CRYPTO */
372