X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/cspxutils/clearPubKeyTest/clearPubKeyTest.cpp diff --git a/SecurityTests/cspxutils/clearPubKeyTest/clearPubKeyTest.cpp b/SecurityTests/cspxutils/clearPubKeyTest/clearPubKeyTest.cpp new file mode 100644 index 00000000..81c73e4a --- /dev/null +++ b/SecurityTests/cspxutils/clearPubKeyTest/clearPubKeyTest.cpp @@ -0,0 +1,395 @@ +/* + * clearPubKeyTest.cpp + * + * Test CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT. This cannot be run on a handsoff environment; + * it forces Keychain unlock dialogs. + */ + +#include +#include +#include +#include +#include +#include "cspwrap.h" +#include "common.h" + +#define KEYCHAIN_NAME "/tmp/clearPubKey.keychain" +#define KEYCHAIN_PWD "pwd" +#define KEY_ALG CSSM_ALGID_RSA +#define KEYSIZE 1024 +#define ENCRALG CSSM_ALGID_RSA + +static void usage(char **argv) +{ + printf("usage: %s -v(erbose)\n", argv[0]); + exit(1); +} + +static void printNoDialog() +{ + printf("*** If you get a keychain unlock dialog here the test is failing ***\n"); +} + +static void printExpectDialog() +{ + printf("*** You MUST get a keychain unlock dialog here (password = '%s') ***\n", + KEYCHAIN_PWD); +} + +static bool didGetDialog() +{ + fpurge(stdin); + printf("Enter 'y' if you just got a keychain unlock dialog: "); + if(getchar() == 'y') { + return 1; + } + printf("***Well, you really should have. Test failed.\n"); + return 0; +} + +static void verboseDisp(bool verbose, const char *str) +{ + if(verbose) { + printf("...%s\n", str); + } +} + +/* generate key pair, optionally storing the public key in encrypted form */ +static int genKeyPair( + bool pubKeyIsEncrypted, + SecKeychainRef kcRef, + SecKeyRef *pubKeyRef, + SecKeyRef *privKeyRef) +{ + /* gather keygen args */ + CSSM_ALGORITHMS keyAlg = KEY_ALG; + uint32 keySizeInBits = KEYSIZE; + CSSM_KEYUSE pubKeyUsage = CSSM_KEYUSE_ANY; + uint32 pubKeyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT; + if(pubKeyIsEncrypted) { + pubKeyAttr |= CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT; + } + CSSM_KEYUSE privKeyUsage = CSSM_KEYUSE_ANY; + uint32 privKeyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | + CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE; + + OSStatus ortn = SecKeyCreatePair(kcRef, keyAlg, keySizeInBits, 0, + pubKeyUsage, pubKeyAttr, + privKeyUsage, privKeyAttr, + NULL, // default initial access for now + pubKeyRef, privKeyRef); + if(ortn) { + cssmPerror("SecKeyCreatePair", ortn); + return 1; + } + return 0; +} + +/* encrypt something with a public key */ +static int pubKeyEncrypt( + SecKeyRef pubKeyRef) +{ + const CSSM_KEY *cssmKey; + OSStatus ortn; + CSSM_CSP_HANDLE cspHand; + + ortn = SecKeyGetCSSMKey(pubKeyRef, &cssmKey); + if(ortn) { + cssmPerror("SecKeyGetCSSMKey", ortn); + return -1; + } + ortn = SecKeyGetCSPHandle(pubKeyRef, &cspHand); + if(ortn) { + cssmPerror("SecKeyGetCSPHandle", ortn); + return -1; + } + + char *ptext = "something to encrypt"; + CSSM_DATA ptextData = {strlen(ptext), (uint8 *)ptext}; + CSSM_DATA ctextData = { 0, NULL }; + CSSM_RETURN crtn; + + crtn = cspEncrypt(cspHand, CSSM_ALGID_RSA, + 0, CSSM_PADDING_PKCS1, // mode/pad + cssmKey, NULL, + 0, 0, // effect/rounds + NULL, // IV + &ptextData, &ctextData, CSSM_FALSE); + if(crtn) { + return -1; + } + /* slighyly hazardous, allocated by CSPDL's allocator */ + free(ctextData.Data); + return 0; +} + +int main(int argc, char **argv) +{ + bool verbose = false; + + int arg; + while ((arg = getopt(argc, argv, "vh")) != -1) { + switch (arg) { + case 'v': + verbose = true; + break; + case 'h': + usage(argv); + } + } + if(optind != argc) { + usage(argv); + } + + printNoDialog(); + + /* initial setup */ + verboseDisp(verbose, "deleting keychain"); + unlink(KEYCHAIN_NAME); + + verboseDisp(verbose, "creating keychain"); + SecKeychainRef kcRef = NULL; + OSStatus ortn = SecKeychainCreate(KEYCHAIN_NAME, + strlen(KEYCHAIN_PWD), KEYCHAIN_PWD, + false, NULL, &kcRef); + if(ortn) { + cssmPerror("SecKeychainCreate", ortn); + exit(1); + } + + /* + * 1. Generate key pair with cleartext public key. + * Ensure we can use the public key when keychain is locked with no + * user interaction. + */ + + /* generate key pair, cleartext public key */ + verboseDisp(verbose, "creating key pair, cleartext public key"); + SecKeyRef pubKeyRef = NULL; + SecKeyRef privKeyRef = NULL; + if(genKeyPair(false, kcRef, &pubKeyRef, &privKeyRef)) { + exit(1); + } + + /* Use generated cleartext public key with locked keychain */ + verboseDisp(verbose, "locking keychain, exporting public key"); + SecKeychainLock(kcRef); + CFDataRef exportData = NULL; + ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); + if(ortn) { + cssmPerror("SecKeychainCreate", ortn); + exit(1); + } + CFRelease(exportData); + + verboseDisp(verbose, "locking keychain, encrypting with public key"); + SecKeychainLock(kcRef); + if(pubKeyEncrypt(pubKeyRef)) { + exit(1); + } + + /* reset */ + verboseDisp(verbose, "deleting keys"); + ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef); + if(ortn) { + cssmPerror("SecKeychainItemDelete", ortn); + exit(1); + } + ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef); + if(ortn) { + cssmPerror("SecKeychainItemDelete", ortn); + exit(1); + } + CFRelease(pubKeyRef); + CFRelease(privKeyRef); + + /* + * 2. Generate key pair with encrypted public key. + * Ensure that user interaction is required when we use the public key + * when keychain is locked. + */ + + verboseDisp(verbose, "programmatically unlocking keychain"); + ortn = SecKeychainUnlock(kcRef, strlen(KEYCHAIN_PWD), KEYCHAIN_PWD, TRUE); + if(ortn) { + cssmPerror("SecKeychainItemDelete", ortn); + exit(1); + } + + /* generate key pair, encrypted public key */ + verboseDisp(verbose, "creating key pair, encrypted public key"); + if(genKeyPair(true, kcRef, &pubKeyRef, &privKeyRef)) { + exit(1); + } + + /* Use generated encrypted public key with locked keychain */ + verboseDisp(verbose, "locking keychain, exporting public key"); + SecKeychainLock(kcRef); + printExpectDialog(); + ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); + if(ortn) { + cssmPerror("SecKeychainCreate", ortn); + exit(1); + } + /* we'll use that exported blob later to test import */ + if(!didGetDialog()) { + exit(1); + } + + verboseDisp(verbose, "locking keychain, encrypting with public key"); + SecKeychainLock(kcRef); + printExpectDialog(); + if(pubKeyEncrypt(pubKeyRef)) { + exit(1); + } + if(!didGetDialog()) { + exit(1); + } + + /* reset */ + printNoDialog(); + verboseDisp(verbose, "locking keychain"); + SecKeychainLock(kcRef); + verboseDisp(verbose, "deleting keys"); + ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef); + if(ortn) { + cssmPerror("SecKeychainItemDelete", ortn); + exit(1); + } + ortn = SecKeychainItemDelete((SecKeychainItemRef)privKeyRef); + if(ortn) { + cssmPerror("SecKeychainItemDelete", ortn); + exit(1); + } + CFRelease(pubKeyRef); + CFRelease(privKeyRef); + + /* + * 3. Import public key, storing in cleartext. Ensure that the import + * doesn't require unlock, and ensure we can use the public key + * when keychain is locked with no user interaction. + */ + + printNoDialog(); + verboseDisp(verbose, "locking keychain"); + SecKeychainLock(kcRef); + + /* import public key - default is in the clear */ + verboseDisp(verbose, "importing public key, store in the clear (default)"); + CFArrayRef outArray = NULL; + SecExternalFormat format = kSecFormatOpenSSL; + SecExternalItemType type = kSecItemTypePublicKey; + ortn = SecKeychainItemImport(exportData, + NULL, &format, &type, + 0, NULL, + kcRef, &outArray); + if(ortn) { + cssmPerror("SecKeychainItemImport", ortn); + exit(1); + } + CFRelease(exportData); + if(CFArrayGetCount(outArray) != 1) { + printf("***Unexpected outArray size (%ld) after import\n", + (long)CFArrayGetCount(outArray)); + exit(1); + } + pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); + if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) { + printf("***Unexpected item type after import\n"); + exit(1); + } + + /* Use imported cleartext public key with locked keychain */ + verboseDisp(verbose, "locking keychain, exporting public key"); + SecKeychainLock(kcRef); + exportData = NULL; + ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); + if(ortn) { + cssmPerror("SecKeychainItemExport", ortn); + exit(1); + } + /* we'll use exportData again */ + + verboseDisp(verbose, "locking keychain, encrypting with public key"); + SecKeychainLock(kcRef); + if(pubKeyEncrypt(pubKeyRef)) { + exit(1); + } + + /* reset */ + verboseDisp(verbose, "deleting key"); + ortn = SecKeychainItemDelete((SecKeychainItemRef)pubKeyRef); + if(ortn) { + cssmPerror("SecKeychainItemDelete", ortn); + exit(1); + } + CFRelease(pubKeyRef); + + /* + * Import public key, storing in encrypted form. + * Ensure that user interaction is required when we use the public key + * when keychain is locked. + */ + + /* import public key, encrypted in the keychain */ + SecKeyImportExportParameters impExpParams; + memset(&impExpParams, 0, sizeof(impExpParams)); + impExpParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; + impExpParams.keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE | + CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT; + verboseDisp(verbose, "importing public key, store encrypted"); + printExpectDialog(); + outArray = NULL; + format = kSecFormatOpenSSL; + type = kSecItemTypePublicKey; + ortn = SecKeychainItemImport(exportData, + NULL, &format, &type, + 0, &impExpParams, + kcRef, &outArray); + if(ortn) { + cssmPerror("SecKeychainItemImport", ortn); + exit(1); + } + if(!didGetDialog()) { + exit(1); + } + CFRelease(exportData); + if(CFArrayGetCount(outArray) != 1) { + printf("***Unexpected outArray size (%ld) after import\n", + (long)CFArrayGetCount(outArray)); + exit(1); + } + pubKeyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); + if(CFGetTypeID(pubKeyRef) != SecKeyGetTypeID()) { + printf("***Unexpected item type after import\n"); + exit(1); + } + + /* Use imported encrypted public key with locked keychain */ + verboseDisp(verbose, "locking keychain, exporting public key"); + SecKeychainLock(kcRef); + printExpectDialog(); + ortn = SecKeychainItemExport(pubKeyRef, kSecFormatOpenSSL, 0, NULL, &exportData); + if(ortn) { + cssmPerror("SecKeychainItemExport", ortn); + exit(1); + } + if(!didGetDialog()) { + exit(1); + } + CFRelease(exportData); + + verboseDisp(verbose, "locking keychain, encrypting with public key"); + SecKeychainLock(kcRef); + printExpectDialog(); + if(pubKeyEncrypt(pubKeyRef)) { + exit(1); + } + if(!didGetDialog()) { + exit(1); + } + + SecKeychainDelete(kcRef); + printf("...test succeeded.\n"); + return 0; +}