1/* 2 * Copyright (c) 1999-2001,2005-2007,2010-2012 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, ¶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 */ 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