/* * Phusion Passenger - https://www.phusionpassenger.com/ * Copyright (c) 2010-2018 Phusion Holding B.V. * * "Passenger", "Phusion Passenger" and "Union Station" are registered * trademarks of Phusion Holding B.V. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #if BOOST_OS_MACOS #else #include #endif #define AES_KEY_BYTESIZE (256/8) #define AES_CBC_IV_BYTESIZE (128/8) namespace Passenger { using namespace std; using namespace boost; using namespace oxt; #if BOOST_OS_MACOS Crypto::Crypto() :id(NULL) { } Crypto::~Crypto() { } CFDictionaryRef Crypto::createQueryDict(const char *label) { if (kSecClassIdentity != NULL) { const size_t size = 5L; CFStringRef cfLabel = CFStringCreateWithCString(NULL, label, kCFStringEncodingUTF8); SecPolicyRef policy = SecPolicyCreateSSL(false, NULL); CFTypeRef keys[] = {kSecClass, kSecReturnRef, kSecMatchLimit, kSecMatchPolicy, kSecMatchSubjectWholeString}; CFTypeRef values[] = {kSecClassIdentity, kCFBooleanTrue, kSecMatchLimitOne, policy, cfLabel}; CFDictionaryRef queryDict = CFDictionaryCreate(NULL, keys, values, size, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(policy); CFRelease(cfLabel); return queryDict; } return NULL; } SecAccessRef Crypto::createAccess(const char *cLabel) { SecAccessRef access = NULL; CFStringRef label = CFStringCreateWithCString(NULL, cLabel, kCFStringEncodingUTF8); if (SecAccessCreate(label, NULL, &access)) { logError("SecAccessCreate failed."); CFRelease(label); return NULL; } CFRelease(label); return access; } OSStatus Crypto::copyIdentityFromPKCS12File(const char *cPath, const char *cPassword, const char *cLabel) { OSStatus status = errSecItemNotFound; if (strlen(cPath) == 0) { return errSecMissingValue; } CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *) cPath, strlen(cPath), false); CFStringRef password = cPassword ? CFStringCreateWithCString(NULL, cPassword, kCFStringEncodingUTF8) : NULL; CFReadStreamRef cfrs = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); SecTransformRef readTransform = SecTransformCreateReadTransformWithReadStream(cfrs); CFErrorRef error = NULL; CFDataRef pkcsData = (CFDataRef) SecTransformExecute(readTransform, &error); if (error != NULL) { logFreeErrorExtended("ReadTransform", error); return status; } SecAccessRef access = createAccess(cLabel); if (access == NULL) { return status; } CFTypeRef cKeys[] = {kSecImportExportPassphrase, kSecImportExportAccess}; CFTypeRef cValues[] = {password, access}; CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, 2L, NULL, NULL); CFArrayRef items = NULL; /* Here we go: */ status = SecPKCS12Import(pkcsData, options, &items); if (!(status == noErr && items && CFArrayGetCount(items))) { string suffix = string("Please check for a certificate labeled: ") + cLabel + " in your keychain, and remove the associated private key. For more help please read: https://www.phusionpassenger.com/library/admin/standalone/mac_keychain_popups.html"; string prefix = "Loading Passenger Cert failed"; if (status == noErr) { status = errSecAuthFailed; logError( prefix + ". " + suffix ); }else{ CFStringRef str = SecCopyErrorMessageString(status, NULL); logError( prefix + ": " + CFStringGetCStringPtr(str, kCFStringEncodingUTF8) + "\n" + suffix ); CFRelease(str); } }else{ id = (CFDataRef)CFDictionaryGetValue((CFDictionaryRef)CFArrayGetValueAtIndex(items, 0),kSecImportItemKeyID); CFRetain(id); } if (items) { CFRelease(items); } CFRelease(options); CFRelease(access); if (pkcsData) { CFRelease(pkcsData); } CFRelease(readTransform); CFRelease(cfrs); if (password) { CFRelease(password); } CFRelease(url); return status; } bool Crypto::generateRandomChars(unsigned char *rndChars, int rndLen) { FILE *fPtr = fopen("/dev/random", "r"); if (fPtr == NULL) { CFIndex errNum = errno; char* errMsg = strerror(errno); const UInt8 numKeys = 4; CFTypeRef userInfoKeys[numKeys] = { kCFErrorFilePathKey, kCFErrorLocalizedDescriptionKey, kCFErrorLocalizedFailureReasonKey, kCFErrorLocalizedRecoverySuggestionKey }; CFTypeRef userInfoValues[numKeys] = { CFSTR("/dev/random"), CFSTR("Couldn't open file for reading."), CFStringCreateWithCStringNoCopy(NULL, errMsg, kCFStringEncodingUTF8, NULL), CFSTR("Have you tried turning it off and on again?") }; CFErrorRef error = CFErrorCreateWithUserInfoKeysAndValues(NULL, kCFErrorDomainOSStatus, errNum, userInfoKeys, userInfoValues, numKeys); logFreeErrorExtended("generateRandomChars failed", error); return false; } for (int i = 0; i < rndLen; i++) { rndChars[i] = fgetc(fPtr); } fclose(fPtr); return true; } bool Crypto::generateAndAppendNonce(string &nonce) { nonce.append(toString(SystemTime::getUsec())); int rndLen = 16; unsigned char rndChars[rndLen]; if (generateRandomChars(rndChars, rndLen)) { char rndChars64[modp_b64_encode_len(rndLen)]; modp_b64_encode(rndChars64, (const char *) rndChars, rndLen); nonce.append(rndChars64); return true; } else { return false; } } CFDataRef Crypto::genIV(size_t ivSize) { UInt8 *ivBytesPtr = (UInt8*) malloc(ivSize);//freed when iv is freed if (generateRandomChars(ivBytesPtr, ivSize)) { return CFDataCreateWithBytesNoCopy(NULL, ivBytesPtr, ivSize, kCFAllocatorMalloc); } else { return NULL; } } bool Crypto::getKeyBytes(SecKeyRef cryptokey, void **target, size_t &len) { const CSSM_KEY *cssmKey; CSSM_WRAP_KEY wrappedKey; CSSM_CSP_HANDLE cspHandle = 0; CSSM_CC_HANDLE ccHandle = 0; const CSSM_ACCESS_CREDENTIALS *creds; CSSM_RETURN error = SecKeyGetCredentials(cryptokey, CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault, &creds); if (error != CSSM_OK) { cssmPerror("SecKeyGetCredentials", error); } error = SecKeyGetCSSMKey(cryptokey, &cssmKey); if (error != CSSM_OK) { cssmPerror("SecKeyGetCSSMKey", error); } error = SecKeyGetCSPHandle(cryptokey, &cspHandle); if (error != CSSM_OK) { cssmPerror("SecKeyGetCSPHandle", error); } error = CSSM_CSP_CreateSymmetricContext(cspHandle, CSSM_ALGID_NONE, CSSM_ALGMODE_NONE, creds, NULL, NULL, CSSM_PADDING_NONE, 0, &ccHandle); if (error != CSSM_OK) { cssmPerror("CSSM_CSP_CreateSymmetricContext",error); } memset(&wrappedKey, 0, sizeof(wrappedKey)); error = CSSM_WrapKey(ccHandle, creds, cssmKey, NULL, &wrappedKey); if (error != CSSM_OK) { cssmPerror("CSSM_WrapKey", error); } CSSM_DeleteContext(ccHandle); len = wrappedKey.KeyData.Length; return innerMemoryBridge(wrappedKey.KeyData.Data,target,wrappedKey.KeyData.Length); } bool Crypto::encryptAES256(char *dataChars, size_t dataLen, AESEncResult &aesEnc) { CFErrorRef error = NULL; bool retVal = false; CFNumberRef cfSize = NULL; CFDictionaryRef parameters = NULL; SecKeyRef cryptokey = NULL; CFDataRef iv = NULL; SecTransformRef encrypt = NULL; CFDataRef message = NULL; CFDataRef enc = NULL; do { UInt32 size = kSecAES256; // c++ is dumb CFNumberRef cfSize = CFNumberCreate(NULL, kCFNumberSInt32Type, &size); CFTypeRef cKeys[] = {kSecAttrKeyType, kSecAttrKeySizeInBits}; CFTypeRef cValues[] = {kSecAttrKeyTypeAES, cfSize}; CFDictionaryRef parameters = CFDictionaryCreate(NULL, cKeys, cValues, 2L, NULL, NULL); if (parameters == NULL) { logError("CFDictionaryCreate failed."); retVal = false; break; } SecKeyRef cryptokey = SecKeyGenerateSymmetric(parameters, &error); if (error != NULL) { logFreeErrorExtended("SecKeyGenerateSymmetric", error); retVal = false; break; } if (!getKeyBytes(cryptokey, (void **) &aesEnc.key, aesEnc.keyLen)) { retVal = false; break; } CFDataRef iv = genIV(AES_CBC_IV_BYTESIZE); if (iv == NULL) { logError("genIV failed."); retVal = false; break; } else if (!memoryBridge(iv, (void **) &aesEnc.iv, aesEnc.ivLen)) { retVal = false; break; } SecTransformRef encrypt = SecEncryptTransformCreate(cryptokey, &error); if (error != NULL) { logFreeErrorExtended("SecEncryptTransformCreate", error); retVal = false; break; } SecTransformSetAttribute(encrypt, kSecIVKey, iv, &error); if (error != NULL) { logFreeErrorExtended("SecTransformSetAttribute", error); retVal = false; break; } CFDataRef message = CFDataCreateWithBytesNoCopy(NULL, (UInt8*) dataChars, dataLen, kCFAllocatorNull); SecTransformSetAttribute(encrypt, kSecTransformInputAttributeName, message, &error); if (error != NULL) { logFreeErrorExtended("SecTransformSetAttribute", error); retVal = false; break; } CFDataRef enc = (CFDataRef) SecTransformExecute(encrypt, &error); if (error != NULL) { logFreeErrorExtended("SecTransformExecute", error); retVal = false; break; } if (!memoryBridge(enc, (void **) &aesEnc.encrypted, aesEnc.encryptedLen)) { retVal = false; break; } retVal = true; } while (false); if (enc) { CFRelease(enc); } if (message) { CFRelease(message); } if (encrypt) { CFRelease(encrypt); } if (iv) { CFRelease(iv); } if (cryptokey) { CFRelease(cryptokey); } if (parameters) { CFRelease(parameters); } if (cfSize) { CFRelease(cfSize); } return retVal; } void Crypto::freeAESEncrypted(AESEncResult &aesEnc) { if (aesEnc.encrypted != NULL) { free(aesEnc.encrypted); aesEnc.encrypted = NULL; } if (aesEnc.iv != NULL) { free(aesEnc.iv); aesEnc.iv = NULL; } if (aesEnc.key != NULL) { memset(aesEnc.key, 0, aesEnc.keyLen); free(aesEnc.key); aesEnc.key = NULL; } } bool Crypto::encryptRSA(unsigned char *dataChars, size_t dataLen, string encryptPubKeyPath, unsigned char **encryptedCharsPtr, size_t &encryptedLen) { bool retVal = false; CFErrorRef error = NULL; SecKeyRef rsaPubKey = loadPubKey(encryptPubKeyPath.c_str()); CFDataRef aesKeyData = NULL; SecTransformRef rsaEncryptContext = NULL; CFDataRef encryptedKey = NULL; do { if (rsaPubKey == NULL) { logError("loadPubKey failed"); retVal = false; break; } aesKeyData = CFDataCreateWithBytesNoCopy(NULL, (UInt8*) dataChars, dataLen, kCFAllocatorNull); if (aesKeyData == NULL) { logError("CFDataCreateWithBytesNoCopy failed"); retVal = false; break; } rsaEncryptContext = SecEncryptTransformCreate(rsaPubKey, &error); if (error) { logFreeErrorExtended("SecEncryptTransformCreate", error); retVal = false; break; } SecTransformSetAttribute(rsaEncryptContext, kSecPaddingKey, kSecPaddingOAEPKey, &error); if (error) { logFreeErrorExtended("SecTransformSetAttribute", error); retVal = false; break; } SecTransformSetAttribute(rsaEncryptContext, kSecTransformInputAttributeName, aesKeyData, &error); if (error) { logFreeErrorExtended("SecTransformSetAttribute", error); retVal = false; break; } encryptedKey = (CFDataRef) SecTransformExecute(rsaEncryptContext, &error); if (error) { logFreeErrorExtended("SecTransformExecute", error); retVal = false; break; } if (!memoryBridge(encryptedKey, (void **) encryptedCharsPtr, encryptedLen)) { retVal = false; break; } retVal = true; } while (false); if (encryptedKey) { CFRelease(encryptedKey); } if (rsaEncryptContext) { CFRelease(rsaEncryptContext); } if (aesKeyData) { CFRelease(aesKeyData); } if (rsaPubKey) { CFRelease(rsaPubKey); } return retVal; } bool Crypto::memoryBridge(CFDataRef input, void **target, size_t &len) { len = CFDataGetLength(input); return innerMemoryBridge((void *) CFDataGetBytePtr(input), target, len); } bool Crypto::innerMemoryBridge(void *input, void **target, size_t len){ *target = malloc(len); if (*target == NULL) { logError("malloc failed: " + toString(len)); return false; } memcpy(*target, input, len); return true; } bool Crypto::verifySignature(string signaturePubKeyPath, char *signatureChars, int signatureLen, string data) { SecKeyRef rsaPubKey = NULL; bool result = false; SecTransformRef verifier = NULL; CFNumberRef cfSize = NULL; do { rsaPubKey = loadPubKey(signaturePubKeyPath.c_str()); if (rsaPubKey == NULL) { logError("Failed to load public key at " + signaturePubKeyPath); break; } CFDataRef signatureRef = CFDataCreateWithBytesNoCopy(NULL, (UInt8*) signatureChars, signatureLen, kCFAllocatorNull); CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL, reinterpret_cast(data.c_str()), data.length(), kCFAllocatorNull); CFErrorRef error = NULL; verifier = SecVerifyTransformCreate(rsaPubKey, signatureRef, &error); if (error) { logFreeErrorExtended("SecVerifyTransformCreate", error); result = -20; break; } SecTransformSetAttribute(verifier, kSecTransformInputAttributeName, dataRef, &error); if (error) { logFreeErrorExtended("SecTransformSetAttribute InputName", error); result = -21; break; } SecTransformSetAttribute(verifier, kSecDigestTypeAttribute, kSecDigestSHA2, &error); if (error) { logFreeErrorExtended("SecTransformSetAttribute DigestType", error); result = -22; break; } UInt32 size = kSecAES256; // c++ is dumb cfSize = CFNumberCreate(NULL, kCFNumberSInt32Type, &size); SecTransformSetAttribute(verifier, kSecDigestLengthAttribute, cfSize, &error); if (error) { logFreeErrorExtended("SecTransformSetAttribute DigestLength", error); result = -23; break; } CFTypeRef verifyResult = SecTransformExecute(verifier, &error); if (error) { logFreeErrorExtended("SecTransformExecute", error); result = -24; break; } result = (verifyResult == kCFBooleanTrue); } while(0); if (cfSize) { CFRelease(cfSize); } if (verifier) { CFRelease(verifier); } freePubKey(rsaPubKey); return result; } PUBKEY_TYPE Crypto::loadPubKey(const char *filename) { SecKeyRef pubKey = NULL; CFDataRef keyData = NULL; CFURLRef url = NULL; CFReadStreamRef cfrs = NULL; SecTransformRef readTransform = NULL; CFArrayRef temparray = NULL; do { url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, reinterpret_cast(filename), strlen(filename), false); if (url == NULL) { logError("CFURLCreateFromFileSystemRepresentation failed."); break; } cfrs = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); if (cfrs == NULL) { logError("CFReadStreamCreateWithFile failed"); break; } readTransform = SecTransformCreateReadTransformWithReadStream(cfrs); if (readTransform == NULL) { logError("SecTransformCreateReadTransformWithReadStream failed"); break; } CFErrorRef error = NULL; keyData = (CFDataRef) SecTransformExecute(readTransform, &error); if (keyData == NULL) { logError("SecTransformExecute failed to get keyData"); break; } if (error) { logFreeErrorExtended("SecTransformExecute", error); break; } SecExternalItemType itemType = kSecItemTypePublicKey; SecExternalFormat externalFormat = kSecFormatPEMSequence; OSStatus oserr = SecItemImport(keyData, NULL, // filename or extension &externalFormat, // See SecExternalFormat for details &itemType, 0, // See SecItemImportExportFlags for details, Note that PEM formatting // is determined internally via inspection of the incoming data, so // the kSecItemPemArmour flag is ignored. NULL, NULL, // Don't import into a keychain &temparray); if (oserr) { CFStringRef str = SecCopyErrorMessageString(oserr, NULL); logError(string("SecItemImport: ") + CFStringGetCStringPtr(str, kCFStringEncodingUTF8)); CFRelease(str); break; } pubKey = reinterpret_cast(const_cast(CFArrayGetValueAtIndex(temparray, 0))); CFRetain(pubKey); //bump ref count, now we own this and need to release it eventually } while (0); if (keyData) { CFRelease(keyData); //might be wrong to release here, not sure if SecItemImport makes a copy of the bytes, it looks like it does //https://opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-55035/lib/SecImport.cpp //http://opensource.apple.com//source/libsecurity_keychain/libsecurity_keychain-14/lib/SecImportExportPem.cpp } if (temparray) { CFRelease(temparray); } if (readTransform) { CFRelease(readTransform); } if (cfrs) { CFRelease(cfrs); } if (url) { CFRelease(url); } return pubKey; } void Crypto::freePubKey(PUBKEY_TYPE pubKey) { if (pubKey) { CFRelease(pubKey); } } void Crypto::logFreeErrorExtended(const StaticString &prefix, CFErrorRef &error) { if (error) { CFStringRef description = CFErrorCopyDescription((CFErrorRef) error); CFStringRef failureReason = CFErrorCopyFailureReason((CFErrorRef) error); CFStringRef recoverySuggestion = CFErrorCopyRecoverySuggestion((CFErrorRef) error); logError(prefix + ": " + CFStringGetCStringPtr(description, kCFStringEncodingUTF8) + "; " + CFStringGetCStringPtr(failureReason, kCFStringEncodingUTF8) + "; " + CFStringGetCStringPtr(recoverySuggestion, kCFStringEncodingUTF8) ); CFRelease(recoverySuggestion); CFRelease(failureReason); CFRelease(description); CFRelease(error); error = NULL; } } #else Crypto::Crypto() { OpenSSL_add_all_algorithms(); } Crypto::~Crypto() { EVP_cleanup(); } bool Crypto::generateAndAppendNonce(string &nonce) { nonce.append(toString(SystemTime::getUsec())); int rndLen = 16; unsigned char rndChars[rndLen]; if (1 != RAND_bytes(rndChars, rndLen)) { logErrorExtended("RAND_bytes failed for nonce"); return false; } char rndChars64[modp_b64_encode_len(rndLen)]; modp_b64_encode(rndChars64, (const char *) rndChars, rndLen); nonce.append(rndChars64); return true; } bool Crypto::encryptAES256(char *dataChars, size_t dataLen, AESEncResult &aesEnc) { assert(dataLen > 0 && dataChars != NULL); aesEnc.encrypted = NULL; aesEnc.key = NULL; aesEnc.iv = NULL; bool result = false; EVP_CIPHER_CTX *aesEncryptContext = NULL; do { // 1. Generate random key (secret) and init vector to be used for the encryption aesEnc.keyLen = AES_KEY_BYTESIZE; aesEnc.key = (unsigned char*) OPENSSL_malloc(aesEnc.keyLen); if (aesEnc.key == NULL) { logErrorExtended("OPENSSL_malloc failed"); break; } aesEnc.ivLen = AES_CBC_IV_BYTESIZE; aesEnc.iv = (unsigned char*) malloc(aesEnc.ivLen); // not secret if (aesEnc.iv == NULL) { logError("malloc for IV failed"); break; } if (1 != RAND_bytes(aesEnc.key, aesEnc.keyLen)) { logErrorExtended("RAND_bytes failed for AES key"); break; } if (1 != RAND_bytes(aesEnc.iv, aesEnc.ivLen)) { logErrorExtended("RAND_bytes failed for IV"); break; } // 2. Get ready to encrypt aesEncryptContext = EVP_CIPHER_CTX_new(); if (aesEncryptContext == NULL) { logErrorExtended("EVP_CIPHER_CTX_new failed"); break; } aesEnc.encrypted = (unsigned char*) malloc(dataLen + AES_BLOCK_SIZE); // not secret if (aesEnc.encrypted == NULL) { logError("malloc for encryptedChars failed " + toString(dataLen + AES_BLOCK_SIZE)); break; } // 3. Let's go if (1 != EVP_EncryptInit_ex(aesEncryptContext, EVP_aes_256_cbc(), NULL, aesEnc.key, aesEnc.iv)) { logErrorExtended("EVP_EncryptInit_ex failed"); break; } size_t blockLength = 0; size_t writeIdx = 0; if (1 != EVP_EncryptUpdate(aesEncryptContext, aesEnc.encrypted + writeIdx, (int *) &blockLength, (unsigned char*) dataChars, dataLen)) { logErrorExtended("EVP_EncryptUpdate failed"); break; } writeIdx += blockLength; if (1 != EVP_EncryptFinal_ex(aesEncryptContext, aesEnc.encrypted + writeIdx, (int *) &blockLength)) { logErrorExtended("EVP_EncryptFinal_ex failed"); break; } writeIdx += blockLength; aesEnc.encryptedLen = writeIdx; result = true; } while(0); if (!result) { // convenience: free the result if it's not valid anyway freeAESEncrypted(aesEnc); } if (aesEncryptContext != NULL) { EVP_CIPHER_CTX_free(aesEncryptContext); } return result; } void Crypto::freeAESEncrypted(AESEncResult &aesEnc) { if (aesEnc.encrypted != NULL) { free(aesEnc.encrypted); aesEnc.encrypted = NULL; } if (aesEnc.iv != NULL) { free(aesEnc.iv); aesEnc.iv = NULL; } // Secret parts were allocated differently if (aesEnc.key != NULL) { OPENSSL_free(aesEnc.key); aesEnc.key = NULL; } } bool Crypto::encryptRSA(unsigned char *dataChars, size_t dataLen, string encryptPubKeyPath, unsigned char **encryptedCharsPtr, size_t &encryptedLen) { RSA *rsaPubKey = NULL; EVP_PKEY *rsaPubKeyEVP = NULL; EVP_PKEY_CTX *ctx = NULL; bool result = false; do { // 1. Get the RSA public key to encrypt with rsaPubKey = loadPubKey(encryptPubKeyPath.c_str()); if (rsaPubKey == NULL) { logError("Failed to load public key at " + encryptPubKeyPath); break; } rsaPubKeyEVP = EVP_PKEY_new(); if (1 != EVP_PKEY_assign_RSA(rsaPubKeyEVP, rsaPubKey)) { logErrorExtended("EVP_PKEY_assign_RSA"); freePubKey(rsaPubKey); // since it's not tied to EVP key yet break; } // 2. Prepare for encryption ctx = EVP_PKEY_CTX_new(rsaPubKeyEVP, NULL); if (ctx == NULL) { logErrorExtended("EVP_PKEY_CTX_new"); break; } if (1 != EVP_PKEY_encrypt_init(ctx)) { logErrorExtended("EVP_PKEY_encrypt_init"); break; } if (1 != EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING)) { logErrorExtended("EVP_PKEY_CTX_set_rsa_padding"); break; } // 3. First encrypt to get encryptedLen if (1 != EVP_PKEY_encrypt(ctx, NULL, &encryptedLen, (unsigned char *) dataChars, dataLen)) { logErrorExtended("EVP_PKEY_encrypt (first)"); break; } *encryptedCharsPtr = (unsigned char *) malloc(encryptedLen); // not secret if (*encryptedCharsPtr == NULL) { logError("malloc for encryptedChars failed" + toString(encryptedLen)); break; } if (1 != EVP_PKEY_encrypt(ctx, (unsigned char *) *encryptedCharsPtr, &encryptedLen, (unsigned char *) dataChars, AES_KEY_BYTESIZE)) { logErrorExtended("EVP_PKEY_encrypt (second)"); break; } result = true; } while (0); if (ctx != NULL) { EVP_PKEY_CTX_free(ctx); } if (rsaPubKeyEVP != NULL) { EVP_PKEY_free(rsaPubKeyEVP); // also frees the rsaPubKey } return result; } bool Crypto::verifySignature(string signaturePubKeyPath, char *signatureChars, int signatureLen, string data) { RSA *rsaPubKey = NULL; EVP_PKEY *rsaPubKeyEVP = NULL; EVP_MD_CTX *mdctx = NULL; bool result = false; do { rsaPubKey = loadPubKey(signaturePubKeyPath.c_str()); if (rsaPubKey == NULL) { logError("Failed to load public key at " + signaturePubKeyPath); break; } rsaPubKeyEVP = EVP_PKEY_new(); if (!EVP_PKEY_assign_RSA(rsaPubKeyEVP, rsaPubKey)) { freePubKey(rsaPubKey); logErrorExtended("EVP_PKEY_assign_RSA"); break; } if (!(mdctx = EVP_MD_CTX_create())) { logErrorExtended("EVP_MD_CTX_create"); break; } if (1 != EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, rsaPubKeyEVP)) { logErrorExtended("EVP_DigestVerifyInit"); break; } if (1 != EVP_DigestVerifyUpdate(mdctx, data.c_str(), data.length())) { logErrorExtended("EVP_DigestVerifyUpdate"); break; } if (1 != EVP_DigestVerifyFinal(mdctx, (unsigned char *) signatureChars, signatureLen)) { logErrorExtended("EVP_DigestVerifyFinal"); break; } result = true; } while(0); if (mdctx) { EVP_MD_CTX_destroy(mdctx); } if (rsaPubKeyEVP) { EVP_PKEY_free(rsaPubKeyEVP); // freePubKey not needed, already free by EVP_PKEY_free. } return result; } PUBKEY_TYPE Crypto::loadPubKey(const char *filename) { FILE *fp = fopen(filename, "rb"); if (fp == NULL) { return NULL; } RSA *rsa = RSA_new(); rsa = PEM_read_RSA_PUBKEY(fp, &rsa, NULL, NULL); fclose(fp); return rsa; } void Crypto::freePubKey(PUBKEY_TYPE pubKey) { if (pubKey) { RSA_free(pubKey); } } void Crypto::logErrorExtended(const StaticString &prefix) { char err[500]; ERR_load_crypto_strings(); ERR_error_string(ERR_get_error(), err); P_ERROR(prefix << ": " << err); ERR_free_strings(); } #endif void Crypto::logError(const StaticString &error) { P_ERROR(error); } } // namespace Passenger