]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 1999-2001,2005-2007,2010-2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
d8f41ccd | 5 | * |
b1ab9ed8 A |
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. | |
d8f41ccd | 12 | * |
b1ab9ed8 A |
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. | |
d8f41ccd | 20 | * |
b1ab9ed8 A |
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 | ||
73 | OSStatus 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 | */ | |
110 | OSStatus 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) | |
427c49bc | 131 | srtn = errSecAllocate; |
b1ab9ed8 A |
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 | */ | |
148 | OSStatus 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, ¶mBlock.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(¶mBlock, 0, sizeof(paramBlock)); | |
176 | rv = SEC_ASN1Decode(pool, ¶mBlock, | |
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 | */ | |
204 | OSStatus 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) | |
427c49bc | 227 | srtn = errSecAllocate; |
b1ab9ed8 A |
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 | */ | |
242 | OSStatus 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 | ||
317 | errOut: | |
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 | */ | |
326 | OSStatus 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; | |
427c49bc | 332 | OSStatus ortn = errSecSuccess; |
b1ab9ed8 A |
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); | |
366 | errOut: | |
367 | SecAsn1CoderRelease(coder); | |
368 | return ortn; | |
369 | } | |
370 | ||
371 | #endif /* USE_CDSA_CRYPTO */ |