1/* 2 * clearPubKeyTest.cpp 3 * 4 * Test CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT. This cannot be run on a handsoff environment; 5 * it forces Keychain unlock dialogs. 6 */ 7 8#include <stdlib.h> 9#include <strings.h> 10#include <stdio.h> 11#include <unistd.h> 12#include <Security/Security.h> 13#include "cspwrap.h" 14#include "common.h" 15 16#define KEYCHAIN_NAME "/tmp/clearPubKey.keychain" 17#define KEYCHAIN_PWD "pwd" 18#define KEY_ALG CSSM_ALGID_RSA 19#define KEYSIZE 1024 20#define ENCRALG CSSM_ALGID_RSA 21 22static void usage(char **argv) 23{ 24 printf("usage: %s -v(erbose)\n", argv[0]); 25 exit(1); 26} 27 28static void printNoDialog() 29{ 30 printf("*** If you get a keychain unlock dialog here the test is failing ***\n"); 31} 32 33static void printExpectDialog() 34{ 35 printf("*** You MUST get a keychain unlock dialog here (password = '%s') ***\n", 36 KEYCHAIN_PWD); 37} 38 39static bool didGetDialog() 40{ 41 fpurge(stdin); 42 printf("Enter 'y' if you just got a keychain unlock dialog: "); 43 if(getchar() == 'y') { 44 return 1; 45 } 46 printf("***Well, you really should have. Test failed.\n"); 47 return 0; 48} 49 50static void verboseDisp(bool verbose, const char *str) 51{ 52 if(verbose) { 53 printf("...%s\n", str); 54 } 55} 56 57/* generate key pair, optionally storing the public key in encrypted form */ 58static int genKeyPair( 59 bool pubKeyIsEncrypted, 60 SecKeychainRef kcRef, 61 SecKeyRef *pubKeyRef, 62 SecKeyRef *privKeyRef) 63{ 64 /* gather keygen args */ 65 CSSM_ALGORITHMS keyAlg = KEY_ALG; 66 uint32 keySizeInBits = KEYSIZE; 67 CSSM_KEYUSE pubKeyUsage = CSSM_KEYUSE_ANY; 68 uint32 pubKeyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT; 69 if(pubKeyIsEncrypted) { 70 pubKeyAttr |= CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT; 71 } 72 CSSM_KEYUSE privKeyUsage = CSSM_KEYUSE_ANY; 73 uint32 privKeyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | 74 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE; 75 76 OSStatus ortn = SecKeyCreatePair(kcRef, keyAlg, keySizeInBits, 0, 77 pubKeyUsage, pubKeyAttr, 78 privKeyUsage, privKeyAttr, 79 NULL, // default initial access for now 80 pubKeyRef, privKeyRef); 81 if(ortn) { 82 cssmPerror("SecKeyCreatePair", ortn); 83 return 1; 84 } 85 return 0; 86} 87 88/* encrypt something with a public key */ 89static int pubKeyEncrypt( 90 SecKeyRef pubKeyRef) 91{ 92 const CSSM_KEY *cssmKey; 93 OSStatus ortn; 94 CSSM_CSP_HANDLE cspHand; 95 96 ortn = SecKeyGetCSSMKey(pubKeyRef, &cssmKey); 97 if(ortn) { 98 cssmPerror("SecKeyGetCSSMKey", ortn); 99 return -1; 100 } 101 ortn = SecKeyGetCSPHandle(pubKeyRef, &cspHand); 102 if(ortn) { 103 cssmPerror("SecKeyGetCSPHandle", ortn); 104 return -1; 105 } 106 107 char *ptext = "something to encrypt"; 108 CSSM_DATA ptextData = {strlen(ptext), (uint8 *)ptext}; 109 CSSM_DATA ctextData = { 0, NULL }; 110 CSSM_RETURN crtn; 111 112 crtn = cspEncrypt(cspHand, CSSM_ALGID_RSA, 113 0, CSSM_PADDING_PKCS1, // mode/pad 114 cssmKey, NULL, 115 0, 0, // effect/rounds 116 NULL, // IV 117 &ptextData, &ctextData, CSSM_FALSE); 118 if(crtn) { 119 return -1; 120 } 121 /* slighyly hazardous, allocated by CSPDL's allocator */ 122 free(ctextData.Data); 123 return 0; 124} 125 126int main(int argc, char **argv) 127{ 128 bool verbose = false; 129 130 int arg; 131 while ((arg = getopt(argc, argv, "vh")) != -1) { 132 switch (arg) { 133 case 'v': 134 verbose = true; 135 break; 136 case 'h': 137 usage(argv); 138 } 139 } 140 if(optind != argc) { 141 usage(argv); 142 } 143 144 printNoDialog(); 145 146 /* initial setup */ 147 verboseDisp(verbose, "deleting keychain"); 148 unlink(KEYCHAIN_NAME); 149 150 verboseDisp(verbose, "creating keychain"); 151 SecKeychainRef kcRef = NULL; 152 OSStatus ortn = SecKeychainCreate(KEYCHAIN_NAME, 153 strlen(KEYCHAIN_PWD), KEYCHAIN_PWD, 154 false, NULL, &kcRef); 155 if(ortn) { 156 cssmPerror("SecKeychainCreate", ortn); 157 exit(1); 158 } 159 160 /* 161 * 1. Generate key pair with cleartext public key. 162 * Ensure we can use the public key when keychain is locked with no 163 * user interaction. 164 */ 165 166 /* generate key pair, cleartext public key */ 167 verboseDisp(verbose, "creating key pair, cleartext public key"); 168 SecKeyRef pubKeyRef = NULL; 169 SecKeyRef privKeyRef = NULL; 170 if(genKeyPair(false, kcRef, &pubKeyRef, &privKeyRef)) { 171 exit(1); 172 } 173 174 /* Use generated cleartext public key with locked keychain */ 175 verboseDisp(verbose, "locking keychain, exporting public key"); 176 SecKeychainLock(kcRef); 177 CFDataRef exportData = NULL; 178 ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); 179 if(ortn) { 180 cssmPerror("SecKeychainCreate", ortn); 181 exit(1); 182 } 183 CFRelease(exportData); 184 185 verboseDisp(verbose, "locking keychain, encrypting with public key"); 186 SecKeychainLock(kcRef); 187 if(pubKeyEncrypt(pubKeyRef)) { 188 exit(1); 189 } 190 191 /* reset */ 192 verboseDisp(verbose, "deleting keys"); 193 ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef); 194 if(ortn) { 195 cssmPerror("SecKeychainItemDelete", ortn); 196 exit(1); 197 } 198 ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef); 199 if(ortn) { 200 cssmPerror("SecKeychainItemDelete", ortn); 201 exit(1); 202 } 203 CFRelease(pubKeyRef); 204 CFRelease(privKeyRef); 205 206 /* 207 * 2. Generate key pair with encrypted public key. 208 * Ensure that user interaction is required when we use the public key 209 * when keychain is locked. 210 */ 211 212 verboseDisp(verbose, "programmatically unlocking keychain"); 213 ortn = SecKeychainUnlock(kcRef, strlen(KEYCHAIN_PWD), KEYCHAIN_PWD, TRUE); 214 if(ortn) { 215 cssmPerror("SecKeychainItemDelete", ortn); 216 exit(1); 217 } 218 219 /* generate key pair, encrypted public key */ 220 verboseDisp(verbose, "creating key pair, encrypted public key"); 221 if(genKeyPair(true, kcRef, &pubKeyRef, &privKeyRef)) { 222 exit(1); 223 } 224 225 /* Use generated encrypted public key with locked keychain */ 226 verboseDisp(verbose, "locking keychain, exporting public key"); 227 SecKeychainLock(kcRef); 228 printExpectDialog(); 229 ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); 230 if(ortn) { 231 cssmPerror("SecKeychainCreate", ortn); 232 exit(1); 233 } 234 /* we'll use that exported blob later to test import */ 235 if(!didGetDialog()) { 236 exit(1); 237 } 238 239 verboseDisp(verbose, "locking keychain, encrypting with public key"); 240 SecKeychainLock(kcRef); 241 printExpectDialog(); 242 if(pubKeyEncrypt(pubKeyRef)) { 243 exit(1); 244 } 245 if(!didGetDialog()) { 246 exit(1); 247 } 248 249 /* reset */ 250 printNoDialog(); 251 verboseDisp(verbose, "locking keychain"); 252 SecKeychainLock(kcRef); 253 verboseDisp(verbose, "deleting keys"); 254 ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef); 255 if(ortn) { 256 cssmPerror("SecKeychainItemDelete", ortn); 257 exit(1); 258 } 259 ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef); 260 if(ortn) { 261 cssmPerror("SecKeychainItemDelete", ortn); 262 exit(1); 263 } 264 CFRelease(pubKeyRef); 265 CFRelease(privKeyRef); 266 267 /* 268 * 3. Import public key, storing in cleartext. Ensure that the import 269 * doesn't require unlock, and ensure we can use the public key 270 * when keychain is locked with no user interaction. 271 */ 272 273 printNoDialog(); 274 verboseDisp(verbose, "locking keychain"); 275 SecKeychainLock(kcRef); 276 277 /* import public key - default is in the clear */ 278 verboseDisp(verbose, "importing public key, store in the clear (default)"); 279 CFArrayRef outArray = NULL; 280 SecExternalFormat format = kSecFormatOpenSSL; 281 SecExternalItemType type = kSecItemTypePublicKey; 282 ortn = SecKeychainItemImport(exportData, 283 NULL, &format, &type, 284 0, NULL, 285 kcRef, &outArray); 286 if(ortn) { 287 cssmPerror("SecKeychainItemImport", ortn); 288 exit(1); 289 } 290 CFRelease(exportData); 291 if(CFArrayGetCount(outArray) != 1) { 292 printf("***Unexpected outArray size (%ld) after import\n", 293 (long)CFArrayGetCount(outArray)); 294 exit(1); 295 } 296 pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); 297 if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) { 298 printf("***Unexpected item type after import\n"); 299 exit(1); 300 } 301 302 /* Use imported cleartext public key with locked keychain */ 303 verboseDisp(verbose, "locking keychain, exporting public key"); 304 SecKeychainLock(kcRef); 305 exportData = NULL; 306 ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); 307 if(ortn) { 308 cssmPerror("SecKeychainItemExport", ortn); 309 exit(1); 310 } 311 /* we'll use exportData again */ 312 313 verboseDisp(verbose, "locking keychain, encrypting with public key"); 314 SecKeychainLock(kcRef); 315 if(pubKeyEncrypt(pubKeyRef)) { 316 exit(1); 317 } 318 319 /* reset */ 320 verboseDisp(verbose, "deleting key"); 321 ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef); 322 if(ortn) { 323 cssmPerror("SecKeychainItemDelete", ortn); 324 exit(1); 325 } 326 CFRelease(pubKeyRef); 327 328 /* 329 * Import public key, storing in encrypted form. 330 * Ensure that user interaction is required when we use the public key 331 * when keychain is locked. 332 */ 333 334 /* import public key, encrypted in the keychain */ 335 SecKeyImportExportParameters impExpParams; 336 memset(&impExpParams, 0, sizeof(impExpParams)); 337 impExpParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; 338 impExpParams.keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | 339 CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT; 340 verboseDisp(verbose, "importing public key, store encrypted"); 341 printExpectDialog(); 342 outArray = NULL; 343 format = kSecFormatOpenSSL; 344 type = kSecItemTypePublicKey; 345 ortn = SecKeychainItemImport(exportData, 346 NULL, &format, &type, 347 0, &impExpParams, 348 kcRef, &outArray); 349 if(ortn) { 350 cssmPerror("SecKeychainItemImport", ortn); 351 exit(1); 352 } 353 if(!didGetDialog()) { 354 exit(1); 355 } 356 CFRelease(exportData); 357 if(CFArrayGetCount(outArray) != 1) { 358 printf("***Unexpected outArray size (%ld) after import\n", 359 (long)CFArrayGetCount(outArray)); 360 exit(1); 361 } 362 pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); 363 if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) { 364 printf("***Unexpected item type after import\n"); 365 exit(1); 366 } 367 368 /* Use imported encrypted public key with locked keychain */ 369 verboseDisp(verbose, "locking keychain, exporting public key"); 370 SecKeychainLock(kcRef); 371 printExpectDialog(); 372 ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); 373 if(ortn) { 374 cssmPerror("SecKeychainItemExport", ortn); 375 exit(1); 376 } 377 if(!didGetDialog()) { 378 exit(1); 379 } 380 CFRelease(exportData); 381 382 verboseDisp(verbose, "locking keychain, encrypting with public key"); 383 SecKeychainLock(kcRef); 384 printExpectDialog(); 385 if(pubKeyEncrypt(pubKeyRef)) { 386 exit(1); 387 } 388 if(!didGetDialog()) { 389 exit(1); 390 } 391 392 SecKeychainDelete(kcRef); 393 printf("...test succeeded.\n"); 394 return 0; 395} 396