ocspdUtils.cpp
1 /* 2 * Copyright (c) 2000,2002,2011-2012,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 * ocspUtils.cpp - common utilities for OCSPD 26 */ 27 28 #include "ocspdUtils.h" 29 #include "ocspdDebug.h" 30 #include <Security/cssmerr.h> 31 #include <Security/keyTemplates.h> 32 #include <CoreFoundation/CoreFoundation.h> 33 34 /* 35 * Compare two CSSM_DATAs, return CSSM_TRUE if identical. 36 */ 37 CSSM_BOOL ocspdCompareCssmData( 38 const CSSM_DATA *data1, 39 const CSSM_DATA *data2) 40 { 41 if((data1 == NULL) || (data1->Data == NULL) || 42 (data2 == NULL) || (data2->Data == NULL) || 43 (data1->Length != data2->Length)) { 44 return CSSM_FALSE; 45 } 46 if(data1->Length != data2->Length) { 47 return CSSM_FALSE; 48 } 49 if(memcmp(data1->Data, data2->Data, data1->Length) == 0) { 50 return CSSM_TRUE; 51 } 52 else { 53 return CSSM_FALSE; 54 } 55 } 56 57 /* 58 * Convert a generalized time string, with a 4-digit year and no trailing 59 * fractional seconds or time zone info, to a CFAbsoluteTime. Returns 60 * NULL_TIME (0.0) on error. 61 */ 62 static CFAbsoluteTime parseGenTime( 63 const uint8 *str, 64 uint32 len) 65 { 66 if((str == NULL) || (len == 0)) { 67 return NULL_TIME; 68 } 69 70 /* tolerate NULL terminated or not */ 71 if(str[len - 1] == '\0') { 72 len--; 73 } 74 if(len < 4) { 75 return NULL_TIME; 76 } 77 char szTemp[5]; 78 CFGregorianDate greg; 79 memset(&greg, 0, sizeof(greg)); 80 const uint8 *cp = str; 81 82 /* YEAR */ 83 szTemp[0] = *cp++; 84 szTemp[1] = *cp++; 85 szTemp[2] = *cp++; 86 szTemp[3] = *cp++; 87 szTemp[4] = '\0'; 88 len -= 4; 89 greg.year = atoi(szTemp); 90 91 /* MONTH - CFGregorianDate ranges 1..12, just like the string */ 92 if(len < 2) { 93 return NULL_TIME; 94 } 95 szTemp[0] = *cp++; 96 szTemp[1] = *cp++; 97 szTemp[2] = '\0'; 98 len -= 2; 99 greg.month = atoi( szTemp ); 100 101 /* DAY - 1..31 */ 102 if(len < 2) { 103 return NULL_TIME; 104 } 105 szTemp[0] = *cp++; 106 szTemp[1] = *cp++; 107 szTemp[2] = '\0'; 108 greg.day = atoi( szTemp ); 109 len -= 2; 110 111 if(len >= 2) { 112 /* HOUR 0..23 */ 113 szTemp[0] = *cp++; 114 szTemp[1] = *cp++; 115 szTemp[2] = '\0'; 116 greg.hour = atoi( szTemp ); 117 len -= 2; 118 } 119 if(len >= 2) { 120 /* MINUTE 0..59 */ 121 szTemp[0] = *cp++; 122 szTemp[1] = *cp++; 123 szTemp[2] = '\0'; 124 greg.minute = atoi( szTemp ); 125 len -= 2; 126 } 127 if(len >= 2) { 128 /* SECOND 0..59 */ 129 szTemp[0] = *cp++; 130 szTemp[1] = *cp++; 131 szTemp[2] = '\0'; 132 greg.second = atoi( szTemp ); 133 len -= 2; 134 } 135 return CFGregorianDateGetAbsoluteTime(greg, NULL); 136 } 137 138 /* 139 * Parse a GeneralizedTime string into a CFAbsoluteTime. Returns NULL on parse error. 140 * Fractional parts of a second are discarded. 141 */ 142 CFAbsoluteTime genTimeToCFAbsTime( 143 const CSSM_DATA *strData) 144 { 145 if((strData == NULL) || (strData->Data == NULL) || (strData->Length == 0)) { 146 return NULL_TIME; 147 } 148 149 uint8 *timeStr = strData->Data; 150 size_t timeStrLen = strData->Length; 151 152 /* tolerate NULL terminated or not */ 153 if(timeStr[timeStrLen - 1] == '\0') { 154 timeStrLen--; 155 } 156 157 /* start with a fresh editable copy */ 158 uint8 *str = (uint8 *)malloc(timeStrLen); 159 uint32 strLen = 0; 160 161 /* 162 * If there is a decimal point, strip it and all trailing digits off 163 */ 164 const uint8 *inCp = timeStr; 165 uint8 *outCp = str; 166 int foundDecimal = 0; 167 int minutesOffset = 0; 168 int hoursOffset = 0; 169 bool minusOffset = false; 170 bool isGMT = false; 171 size_t toGo = timeStrLen; 172 173 do { 174 if(*inCp == '.') { 175 if(foundDecimal) { 176 /* only legal once */ { 177 free(str); 178 return NULL_TIME; 179 } 180 } 181 foundDecimal++; 182 183 /* skip the decimal point... */ 184 inCp++; 185 toGo--; 186 if(toGo == 0) { 187 /* all done */ 188 break; 189 } 190 /* then all subsequent contiguous digits */ 191 while(isdigit(*inCp) && (toGo != 0)) { 192 inCp++; 193 toGo--; 194 } 195 } /* decimal point processing */ 196 else if((*inCp == '+') || (*inCp == '-')) { 197 /* Time zone offset - handle 2 or 4 chars */ 198 if((toGo != 2) & (toGo != 4)) { 199 free(str); 200 return NULL_TIME; 201 } 202 if(*inCp == '-') { 203 minusOffset = true; 204 } 205 inCp++; 206 hoursOffset = (10 * (inCp[0] - '0')) + (inCp[1] - '0'); 207 toGo -= 2; 208 if(toGo) { 209 minutesOffset = (10 * (inCp[0] - '0')) + (inCp[1] - '0'); 210 toGo -= 2; 211 } 212 } 213 else { 214 *outCp++ = *inCp++; 215 strLen++; 216 toGo--; 217 } 218 } while(toGo != 0); 219 220 if(strLen >= 1 && str[strLen - 1] == 'Z') { 221 isGMT = true; 222 strLen--; 223 } 224 225 CFAbsoluteTime absTime; 226 absTime = parseGenTime(str, strLen); 227 free(str); 228 if(absTime == NULL_TIME) { 229 return NULL_TIME; 230 } 231 232 /* post processing needed? */ 233 if(isGMT) { 234 /* Nope, string was in GMT */ 235 return absTime; 236 } 237 if((minutesOffset != 0) || (hoursOffset != 0)) { 238 /* string contained explicit offset from GMT */ 239 if(minusOffset) { 240 absTime -= (minutesOffset * 60); 241 absTime -= (hoursOffset * 3600); 242 } 243 else { 244 absTime += (minutesOffset * 60); 245 absTime += (hoursOffset * 3600); 246 } 247 } 248 else { 249 /* implciit offset = local */ 250 CFTimeInterval tzDelta; 251 CFTimeZoneRef localZone = CFTimeZoneCopySystem(); 252 tzDelta = CFTimeZoneGetSecondsFromGMT (localZone, CFAbsoluteTimeGetCurrent()); 253 CFRelease(localZone); 254 absTime += tzDelta; 255 } 256 return absTime; 257 } 258 259 /* 260 * Convert CFAbsoluteTime to generalized time string, GMT format (4 digit year, 261 * trailing 'Z'). Caller allocated the output which is GENERAL_TIME_STRLEN+1 bytes. 262 */ 263 void cfAbsTimeToGgenTime( 264 CFAbsoluteTime absTime, 265 char *genTime) 266 { 267 /* time zone = GMT */ 268 CFTimeZoneRef tz = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0); 269 CFGregorianDate greg = CFAbsoluteTimeGetGregorianDate(absTime, tz); 270 CFRelease(tz); 271 272 int seconds = (int)greg.second; 273 sprintf(genTime, "%04d%02d%02d%02d%02d%02dZ", 274 (int)greg.year, greg.month, greg.day, greg.hour, 275 greg.minute, seconds); 276 } 277 278 void ocspdSha1( 279 const void *data, 280 CC_LONG len, 281 unsigned char *md) // allocd by caller, CC_SHA1_DIGEST_LENGTH bytes 282 { 283 CC_SHA1_CTX ctx; 284 CC_SHA1_Init(&ctx); 285 CC_SHA1_Update(&ctx, data, len); 286 CC_SHA1_Final(md, &ctx); 287 } 288 289 void ocspdMD5( 290 const void *data, 291 CC_LONG len, 292 unsigned char *md) // allocd by caller, CC_MD5_DIGEST_LENGTH bytes 293 { 294 CC_MD5_CTX ctx; 295 CC_MD5_Init(&ctx); 296 CC_MD5_Update(&ctx, data, len); 297 CC_MD5_Final(md, &ctx); 298 } 299 300 void ocspdMD4( 301 const void *data, 302 CC_LONG len, 303 unsigned char *md) // allocd by caller, CC_MD4_DIGEST_LENGTH bytes 304 { 305 CC_MD4_CTX ctx; 306 CC_MD4_Init(&ctx); 307 CC_MD4_Update(&ctx, data, len); 308 CC_MD4_Final(md, &ctx); 309 } 310 311 void ocspdSHA256( 312 const void *data, 313 CC_LONG len, 314 unsigned char *md) // allocd by caller, CC_SHA256_DIGEST_LENGTH bytes 315 { 316 CC_SHA256_CTX ctx; 317 CC_SHA256_Init(&ctx); 318 CC_SHA256_Update(&ctx, data, len); 319 CC_SHA256_Final(md, &ctx); 320 } 321 322 /* 323 * How many items in a NULL-terminated array of pointers? 324 */ 325 unsigned ocspdArraySize( 326 const void **array) 327 { 328 unsigned count = 0; 329 if (array) { 330 while (*array++) { 331 count++; 332 } 333 } 334 return count; 335 } 336 337 /* Fill out a CSSM_DATA with the subset of public key bytes from the given 338 * CSSM_KEY_PTR which should be hashed to produce the issuerKeyHash field 339 * of a CertID in an OCSP request. 340 * 341 * For RSA keys, this simply copies the input key pointer and length. 342 * For EC keys, we need to further deconstruct the SubjectPublicKeyInfo 343 * to obtain the key bytes (i.e. curve point) for hashing. 344 * 345 * Returns CSSM_OK on success, or non-zero error if the bytes could not 346 * be retrieved. 347 */ 348 CSSM_RETURN ocspdGetPublicKeyBytes( 349 SecAsn1CoderRef coder, // optional 350 CSSM_KEY_PTR publicKey, // input public key 351 CSSM_DATA &publicKeyBytes) // filled in by this function 352 { 353 CSSM_RETURN crtn = CSSM_OK; 354 SecAsn1CoderRef _coder = NULL; 355 356 if(publicKey == NULL) { 357 crtn = CSSMERR_CSP_INVALID_KEY_POINTER; 358 goto exit; 359 } 360 361 if(coder == NULL) { 362 crtn = SecAsn1CoderCreate(&_coder); 363 if(crtn) { 364 goto exit; 365 } 366 coder = _coder; 367 } 368 369 publicKeyBytes.Length = publicKey->KeyData.Length; 370 publicKeyBytes.Data = publicKey->KeyData.Data; 371 372 if(publicKey->KeyHeader.AlgorithmId == CSSM_ALGID_ECDSA) { 373 /* 374 * For an EC key, publicKey->KeyData is a SubjectPublicKeyInfo 375 * ASN.1 sequence that includes the algorithm identifier. 376 * We only want to return the bit string portion of the key here. 377 */ 378 SecAsn1PubKeyInfo pkinfo; 379 memset(&pkinfo, 0, sizeof(pkinfo)); 380 if(SecAsn1Decode(coder, 381 publicKey->KeyData.Data, 382 publicKey->KeyData.Length, 383 kSecAsn1SubjectPublicKeyInfoTemplate, 384 &pkinfo) == 0) { 385 if(pkinfo.subjectPublicKey.Length && 386 pkinfo.subjectPublicKey.Data) { 387 publicKeyBytes.Length = pkinfo.subjectPublicKey.Length >> 3; 388 publicKeyBytes.Data = pkinfo.subjectPublicKey.Data; 389 /* 390 * Important: if we allocated the SecAsn1Coder, the memory 391 * being pointed to by pkinfo.subjectPublicKey.Data will be 392 * deallocated when the coder is released below. We want to 393 * point to the identical data inside the caller's public key, 394 * now that the decoder has identified it for us. 395 */ 396 if(publicKeyBytes.Length <= publicKey->KeyData.Length) { 397 publicKeyBytes.Data = (uint8*)((uintptr_t)publicKey->KeyData.Data + 398 (publicKey->KeyData.Length - publicKeyBytes.Length)); 399 goto exit; 400 } 401 /* intentional fallthrough to error exit */ 402 } 403 ocspdErrorLog("ocspdGetPublicKeyBytes: invalid SecAsn1PubKeyInfo\n"); 404 crtn = CSSMERR_CSP_INVALID_KEY_POINTER; 405 } 406 else { 407 /* Unable to decode using kSecAsn1SubjectPublicKeyInfoTemplate. 408 * This may or may not be an error; just return the unchanged key. 409 */ 410 ocspdErrorLog("ocspdGetPublicKeyBytes: unable to decode SubjectPublicKeyInfo\n"); 411 } 412 } 413 414 exit: 415 if(_coder) { 416 SecAsn1CoderRelease(_coder); 417 } 418 return crtn; 419 }