diff --git a/.github/workflows/ci-slow.yml b/.github/workflows/ci-slow.yml new file mode 100644 index 000000000..ef33a487a --- /dev/null +++ b/.github/workflows/ci-slow.yml @@ -0,0 +1,35 @@ +name: CI Slow + +on: + push: + branches-ignore: + - 'main' + +env: + BUILDER_VERSION: v0.9.72 + BUILDER_SOURCE: releases + BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net + PACKAGE_NAME: aws-c-io + LINUX_BASE_IMAGE: ubuntu-18-x64 + RUN: ${{ github.run_id }}-${{ github.run_number }} + CRT_CI_ROLE: ${{ secrets.CRT_CI_ROLE_ARN }} + AWS_DEFAULT_REGION: us-east-1 + +permissions: + id-token: write # This is required for requesting the JWT + +jobs: + # Test downstream repos. + # This should not be required because we can run into a chicken and egg problem if there is a change that needs some fix in a downstream repo. + downstream: + runs-on: ubuntu-24.04 # latest + steps: + - uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ env.CRT_CI_ROLE }} + aws-region: ${{ env.AWS_DEFAULT_REGION }} + # We can't use the `uses: docker://image` version yet, GitHub lacks authentication for actions -> packages + - name: Build ${{ env.PACKAGE_NAME }} + run: | + aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh + ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ env.LINUX_BASE_IMAGE }} build downstream -p ${{ env.PACKAGE_NAME }} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6bb8bfda7..6291905f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,6 +59,7 @@ jobs: linux-compiler-compat: runs-on: ubuntu-24.04 # latest strategy: + fail-fast: false matrix: compiler: - name: clang-6 @@ -145,21 +146,6 @@ jobs: aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ env.LINUX_BASE_IMAGE }} build -p ${{ env.PACKAGE_NAME }} --cmake-extra=-DBUILD_SHARED_LIBS=ON - # Test downstream repos. - # This should not be required because we can run into a chicken and egg problem if there is a change that needs some fix in a downstream repo. - downstream: - runs-on: ubuntu-24.04 # latest - steps: - - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ env.CRT_CI_ROLE }} - aws-region: ${{ env.AWS_DEFAULT_REGION }} - # We can't use the `uses: docker://image` version yet, GitHub lacks authentication for actions -> packages - - name: Build ${{ env.PACKAGE_NAME }} - run: | - aws s3 cp s3://aws-crt-test-stuff/ci/${{ env.BUILDER_VERSION }}/linux-container-ci.sh ./linux-container-ci.sh && chmod a+x ./linux-container-ci.sh - ./linux-container-ci.sh ${{ env.BUILDER_VERSION }} aws-crt-${{ env.LINUX_BASE_IMAGE }} build downstream -p ${{ env.PACKAGE_NAME }} - windows: runs-on: windows-2025 # latest steps: @@ -201,6 +187,7 @@ jobs: windows-vc17: runs-on: windows-2025 # latest strategy: + fail-fast: false matrix: arch: [x86, x64] steps: diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e381352b..9c4b992bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,11 +92,9 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Androi elseif (APPLE) - file(GLOB AWS_IO_OS_HEADERS - ) + file(GLOB AWS_IO_OS_HEADERS) file(GLOB AWS_IO_OS_SRC - "source/bsd/*.c" "source/posix/*.c" "source/darwin/*.c" ) @@ -192,7 +190,6 @@ if (USE_VSOCK) endif() if (APPLE) - target_compile_definitions(${PROJECT_NAME} PUBLIC "-DAWS_USE_SECITEM") if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Target file-based keychain on macOS. target_compile_definitions(${PROJECT_NAME} PUBLIC "-DAWS_SECITEM_FILEBASED_KEYCHAIN") diff --git a/include/aws/io/private/pki_utils.h b/include/aws/io/private/pki_utils.h index 5c0dec219..31cf54c1c 100644 --- a/include/aws/io/private/pki_utils.h +++ b/include/aws/io/private/pki_utils.h @@ -40,27 +40,6 @@ AWS_IO_API const char *aws_determine_default_pki_dir(void); AWS_IO_API const char *aws_determine_default_pki_ca_file(void); #ifdef AWS_OS_APPLE -/** - * Imports a PEM armored PKCS#7 public/private key pair - * into identity for use with SecurityFramework. - */ -int aws_import_public_and_private_keys_to_identity( - struct aws_allocator *alloc, - CFAllocatorRef cf_alloc, - const struct aws_byte_cursor *public_cert_chain, - const struct aws_byte_cursor *private_key, - CFArrayRef *identity, - const struct aws_string *keychain_path); - -/** - * Imports a PKCS#12 file into identity for use with - * SecurityFramework - */ -int aws_import_pkcs12_to_identity( - CFAllocatorRef cf_alloc, - const struct aws_byte_cursor *pkcs12_cursor, - const struct aws_byte_cursor *password, - CFArrayRef *identity); /** * Loads PRM armored PKCS#7 certificates into certs @@ -83,7 +62,8 @@ int aws_secitem_import_cert_and_key( const struct aws_byte_cursor *public_cert_chain, const struct aws_byte_cursor *private_key, sec_identity_t *secitem_identity, - const struct aws_secitem_options *secitem_options); + const struct aws_secitem_options *secitem_options, + const struct aws_string *keychain_path); /** * Imports a PKCS#12 file into protected data keychain for use with diff --git a/include/aws/io/tls_channel_handler.h b/include/aws/io/tls_channel_handler.h index eb309ccd2..26cce17c6 100644 --- a/include/aws/io/tls_channel_handler.h +++ b/include/aws/io/tls_channel_handler.h @@ -255,8 +255,8 @@ struct aws_tls_ctx_options { struct aws_secitem_options secitem_options; /** - * On MacOS you can also use a custom keychain instead of - * the default keychain of the account. This is NOT supported on iOS. + * On macOS, you can also use a custom keychain instead of + * the default keychain of the account. This is NOT supported on iOS or tvOS. */ struct aws_string *keychain_path; diff --git a/source/darwin/darwin_pki_utils.c b/source/darwin/darwin_pki_utils.c index 86cbe7222..83ae20601 100644 --- a/source/darwin/darwin_pki_utils.c +++ b/source/darwin/darwin_pki_utils.c @@ -14,54 +14,81 @@ #include #include +#include "darwin_shared_private.h" + /* SecureTransport is not thread-safe during identity import */ /* https://developer.apple.com/documentation/security/certificate_key_and_trust_services/working_with_concurrency */ static struct aws_mutex s_sec_mutex = AWS_MUTEX_INIT; -#if !defined(AWS_OS_IOS) +#if defined(AWS_SECITEM_FILEBASED_KEYCHAIN) -/* - * Helper function to import ECC private key in PEM format into `import_keychain`. Return - * AWS_OP_SUCCESS if successfully imported a private key or find a duplicate key in the - * `import_keychain`, otherwise return AWS_OP_ERR. - * `private_key`: UTF-8 key data in PEM format. If the key file contains multiple key sections, - * the function will only import the first valid key. - * `import_keychain`: The keychain to be imported to. `import_keychain` should not be NULL. - */ -int aws_import_ecc_key_into_keychain( +# define AwsSecKeychainRef SecKeychainRef +static bool s_is_filebased_keychain = true; + +#else /* AWS_SECITEM_FILEBASED_KEYCHAIN */ + +/* Among Apple platforms only macOS supports file-based keychain represented by SecKeychainRef type. On iOS, tvOS, and + * watchOS this type is unavailable. To keep code consistent on all platforms we use void* type when file-based keychain + * is not available. */ +# define AwsSecKeychainRef void * +static bool s_is_filebased_keychain = false; + +#endif /* AWS_SECITEM_FILEBASED_KEYCHAIN */ + +void aws_cf_release(CFTypeRef obj) { + if (obj != NULL) { + CFRelease(obj); + } +} + +static int s_import_key_into_keychain_with_seckeychain( struct aws_allocator *alloc, CFAllocatorRef cf_alloc, const struct aws_byte_cursor *private_key, - SecKeychainRef import_keychain) { - // Ensure imported_keychain is not NULL - AWS_PRECONDITION(import_keychain != NULL); + AwsSecKeychainRef import_keychain) { + + (void)alloc; + (void)cf_alloc; + (void)private_key; + (void)import_keychain; + +#ifdef AWS_SECITEM_FILEBASED_KEYCHAIN + AWS_PRECONDITION(private_key != NULL); + /* SecItemImport used here for importing private key into keychain requires SecKeychainRef in order to actually put + * a private key into keychain. */ + AWS_PRECONDITION(import_keychain != NULL); int result = AWS_OP_ERR; + struct aws_array_list decoded_key_buffer_list; + AWS_ZERO_STRUCT(decoded_key_buffer_list); /* Decode PEM format file to DER format */ if (aws_pem_objects_init_from_file_contents(&decoded_key_buffer_list, alloc, *private_key)) { AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed to decode PEM private key to DER format."); - goto ecc_import_cleanup; + goto done; } - AWS_ASSERT(aws_array_list_is_valid(&decoded_key_buffer_list)); + AWS_FATAL_ASSERT(aws_array_list_is_valid(&decoded_key_buffer_list)); - // A PEM file could contains multiple PEM data section. Try importing each PEM section until find the first - // succeed key. + /* A PEM file may contain multiple PEM data sections. Try importing each PEM section until we successfully find + * a key. */ for (size_t index = 0; index < aws_array_list_length(&decoded_key_buffer_list); index++) { struct aws_pem_object *pem_object_ptr = NULL; - /* We only check the first pem section. Currently, we dont support key with multiple pem section. */ + /* We only check individual PEM sections and do not currently support keys with multiple PEM sections. */ aws_array_list_get_at_ptr(&decoded_key_buffer_list, (void **)&pem_object_ptr, index); - AWS_ASSERT(pem_object_ptr); + if (!pem_object_ptr) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed to get PEM object at index %zu", index); + continue; + } CFDataRef key_data = CFDataCreate(cf_alloc, pem_object_ptr->data.buffer, pem_object_ptr->data.len); if (!key_data) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error in creating ECC key data system call."); + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Error in creating private key data system call at index %zu", index); continue; } - /* Import ECC key data into keychain. */ - SecExternalFormat format = kSecFormatOpenSSL; + /* Import private key data into keychain. */ + SecExternalFormat format = kSecFormatUnknown; SecExternalItemType item_type = kSecItemTypePrivateKey; SecItemImportExportKeyParameters import_params; AWS_ZERO_STRUCT(import_params); @@ -76,298 +103,36 @@ int aws_import_ecc_key_into_keychain( // As long as we found an imported key, ignore the rest of keys if (key_status == errSecSuccess || key_status == errSecDuplicateItem) { + AWS_LOGF_INFO(AWS_LS_IO_PKI, "static: Successfully imported private key into keychain with SecKeychain."); result = AWS_OP_SUCCESS; break; } else { // Log the error code for key importing - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error importing ECC private key with OSStatus %d", (int)key_status); + AWS_LOGF_WARN(AWS_LS_IO_PKI, "static: Failed to import private key with OSStatus %d", (int)key_status); } } -ecc_import_cleanup: +done: // Zero out the array list and release it aws_pem_objects_clean_up(&decoded_key_buffer_list); return result; -} - -int aws_import_public_and_private_keys_to_identity( - struct aws_allocator *alloc, - CFAllocatorRef cf_alloc, - const struct aws_byte_cursor *public_cert_chain, - const struct aws_byte_cursor *private_key, - CFArrayRef *identity, - const struct aws_string *keychain_path) { - AWS_PRECONDITION(public_cert_chain != NULL); - AWS_PRECONDITION(private_key != NULL); - - int result = AWS_OP_ERR; - - CFDataRef cert_data = NULL; - CFDataRef key_data = NULL; - - CFArrayRef cert_import_output = NULL; - CFArrayRef key_import_output = NULL; - SecExternalFormat format = kSecFormatUnknown; - SecExternalItemType item_type = kSecItemTypeCertificate; - - SecItemImportExportKeyParameters import_params; - AWS_ZERO_STRUCT(import_params); - import_params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; - import_params.passphrase = CFSTR(""); - - struct aws_array_list cert_chain_list; - AWS_ZERO_STRUCT(cert_chain_list); - CFDataRef root_cert_data = NULL; - SecCertificateRef certificate_ref = NULL; - SecKeychainRef import_keychain = NULL; - - cert_data = CFDataCreate(cf_alloc, public_cert_chain->ptr, public_cert_chain->len); - if (!cert_data) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed creating public cert chain data."); - result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); - goto done; - } - - key_data = CFDataCreate(cf_alloc, private_key->ptr, private_key->len); - if (!key_data) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed creating private key data."); - result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); - goto done; - } - -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdeprecated-declarations" - /* SecKeychain functions are marked as deprecated. - * Disable compiler warnings for now, but consider removing support for keychain altogether */ - - if (keychain_path) { - OSStatus keychain_status = SecKeychainOpen(aws_string_c_str(keychain_path), &import_keychain); - if (keychain_status != errSecSuccess) { - AWS_LOGF_ERROR( - AWS_LS_IO_PKI, - "static: error opening keychain \"%s\" with OSStatus %d", - aws_string_c_str(keychain_path), - keychain_status); - result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); - goto done; - } - keychain_status = SecKeychainUnlock(import_keychain, 0, "", true); - if (keychain_status != errSecSuccess) { - AWS_LOGF_ERROR( - AWS_LS_IO_PKI, - "static: error unlocking keychain \"%s\" with OSStatus %d", - aws_string_c_str(keychain_path), - keychain_status); - result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); - goto done; - } - } else { - OSStatus keychain_status = SecKeychainCopyDefault(&import_keychain); - if (keychain_status != errSecSuccess) { - AWS_LOGF_ERROR( - AWS_LS_IO_PKI, "static: error opening the default keychain with OSStatus %d", keychain_status); - result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); - goto done; - } - } - -# pragma clang diagnostic pop - - aws_mutex_lock(&s_sec_mutex); - - /* import certificate */ - OSStatus cert_status = - SecItemImport(cert_data, NULL, &format, &item_type, 0, &import_params, import_keychain, &cert_import_output); - - /* import private key */ - format = kSecFormatUnknown; - item_type = kSecItemTypePrivateKey; - OSStatus key_status = - SecItemImport(key_data, NULL, &format, &item_type, 0, &import_params, import_keychain, &key_import_output); - - if (cert_status != errSecSuccess && cert_status != errSecDuplicateItem) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error importing certificate with OSStatus %d", (int)cert_status); - result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE); - goto done; - } - - /* - * If the key format is unknown, we tried to decode the key into DER format import it. - * The PEM file might contains multiple key sections, we will only add the first succeed key into the keychain. - */ - if (key_status == errSecUnknownFormat) { - AWS_LOGF_TRACE(AWS_LS_IO_PKI, "static: error reading private key format, try ECC key format."); - if (aws_import_ecc_key_into_keychain(alloc, cf_alloc, private_key, import_keychain)) { - result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE); - goto done; - } - } else if (key_status != errSecSuccess && key_status != errSecDuplicateItem) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error importing private key with OSStatus %d", (int)key_status); - result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE); - goto done; - } - - /* if it's already there, just convert this over to a cert and then let the keychain give it back to us. */ - if (cert_status == errSecDuplicateItem) { - /* The text for this log is also in the README for each CRT and v2 IoT SDK. If changed, please also change - * where it is referenced. */ - AWS_LOGF_INFO( - AWS_LS_IO_PKI, - "static: certificate has an existing certificate-key pair that was previously imported into the Keychain. " - "Using key from Keychain instead of the one provided."); - if (aws_pem_objects_init_from_file_contents(&cert_chain_list, alloc, *public_cert_chain)) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: decoding certificate PEM failed."); - result = AWS_OP_ERR; - goto done; - } - - struct aws_pem_object *root_cert_ptr = NULL; - aws_array_list_get_at_ptr(&cert_chain_list, (void **)&root_cert_ptr, 0); - AWS_ASSERT(root_cert_ptr); - root_cert_data = CFDataCreate(cf_alloc, root_cert_ptr->data.buffer, root_cert_ptr->data.len); - if (!root_cert_data) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed creating root cert data."); - result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); - goto done; - } - - certificate_ref = SecCertificateCreateWithData(cf_alloc, root_cert_data); - if (!certificate_ref) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: failed to create certificate."); - result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE); - goto done; - } - } else { - certificate_ref = (SecCertificateRef)CFArrayGetValueAtIndex(cert_import_output, 0); - /* SecCertificateCreateWithData returns an object with +1 retain, so we need to match that behavior here */ - CFRetain(certificate_ref); - } - - /* we got a cert one way or the other, create the identity and return it */ - AWS_ASSERT(certificate_ref); - SecIdentityRef identity_output; - OSStatus status = SecIdentityCreateWithCertificate(import_keychain, certificate_ref, &identity_output); - if (status != errSecSuccess) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error creating identity with OSStatus %d", key_status); - result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); - goto done; - } - - CFTypeRef certs[] = {identity_output}; - *identity = CFArrayCreate(cf_alloc, (const void **)certs, 1L, &kCFTypeArrayCallBacks); - result = AWS_OP_SUCCESS; - -done: - aws_mutex_unlock(&s_sec_mutex); - if (certificate_ref) { - CFRelease(certificate_ref); - } - if (root_cert_data) { - CFRelease(root_cert_data); - } - if (cert_import_output) { - CFRelease(cert_import_output); - } - if (key_import_output) { - CFRelease(key_import_output); - } - if (import_keychain) { - CFRelease(import_keychain); - } - if (cert_data) { - CFRelease(cert_data); - } - if (key_data) { - CFRelease(key_data); - } - aws_pem_objects_clean_up(&cert_chain_list); - - return result; -} - -#else /* !AWS_OS_IOS */ - -int aws_import_public_and_private_keys_to_identity( - struct aws_allocator *alloc, - CFAllocatorRef cf_alloc, - const struct aws_byte_cursor *public_cert_chain, - const struct aws_byte_cursor *private_key, - CFArrayRef *identity, - const struct aws_string *keychain_path) { - (void)alloc; - (void)cf_alloc; - (void)public_cert_chain; - (void)private_key; - (void)identity; - (void)keychain_path; - /* This should not be reached when using iOS */ - AWS_FATAL_ASSERT(false); -} -#endif - -int aws_import_pkcs12_to_identity( - CFAllocatorRef cf_alloc, - const struct aws_byte_cursor *pkcs12_cursor, - const struct aws_byte_cursor *password, - CFArrayRef *identity) { - CFDataRef pkcs12_data = CFDataCreate(cf_alloc, pkcs12_cursor->ptr, pkcs12_cursor->len); - CFArrayRef items = NULL; - - CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(cf_alloc, 0, NULL, NULL); - - CFStringRef password_ref = CFSTR(""); - - if (password->len) { - password_ref = CFStringCreateWithBytes(cf_alloc, password->ptr, password->len, kCFStringEncodingUTF8, false); - } - - CFDictionaryAddValue(dictionary, kSecImportExportPassphrase, password_ref); - - aws_mutex_lock(&s_sec_mutex); - OSStatus status = SecPKCS12Import(pkcs12_data, dictionary, &items); - aws_mutex_unlock(&s_sec_mutex); - CFRelease(pkcs12_data); - - if (password_ref) { - CFRelease(password_ref); - } - - CFRelease(dictionary); - - if (status == errSecSuccess) { - CFTypeRef item = (CFTypeRef)CFArrayGetValueAtIndex(items, 0); - - CFTypeRef identity_ref = (CFTypeRef)CFDictionaryGetValue((CFDictionaryRef)item, kSecImportItemIdentity); - if (identity_ref) { - *identity = CFArrayCreate(cf_alloc, &identity_ref, 1L, &kCFTypeArrayCallBacks); - } - - CFRelease(items); - return AWS_OP_SUCCESS; - } - - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error importing pkcs#12 certificate OSStatus %d", (int)status); +#else /* AWS_SECITEM_FILEBASED_KEYCHAIN */ + aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION); return AWS_OP_ERR; -} - -/* - * Apple Network framework and SecItem API's use of the data protection keychain is currently only implemented - * for iOS and tvOS. We may add support for MacOS at a later date. - */ -void aws_cf_release(CFTypeRef obj) { - if (obj != NULL) { - CFRelease(obj); - } +#endif /* AWS_SECITEM_FILEBASED_KEYCHAIN */ } static int s_aws_secitem_add_certificate_to_keychain( CFAllocatorRef cf_alloc, SecCertificateRef cert_ref, CFDataRef serial_data, - CFStringRef label) { + CFStringRef label, + AwsSecKeychainRef import_keychain) { + + (void)import_keychain; int result = AWS_OP_ERR; OSStatus status; @@ -384,6 +149,9 @@ static int s_aws_secitem_add_certificate_to_keychain( #ifdef AWS_SECITEM_FILEBASED_KEYCHAIN /* Target file-based keychain instead of data protection keychain. */ CFDictionaryAddValue(add_attributes, kSecUseDataProtectionKeychain, kCFBooleanFalse); + if (import_keychain != NULL) { + CFDictionaryAddValue(add_attributes, kSecUseKeychain, import_keychain); + } #endif // Initial attempt to add certificate to keychain. @@ -476,7 +244,10 @@ static int s_aws_secitem_add_private_key_to_keychain( CFAllocatorRef cf_alloc, SecKeyRef key_ref, CFStringRef label, - CFDataRef application_label) { + CFDataRef application_label, + AwsSecKeychainRef import_keychain) { + + (void)import_keychain; int result = AWS_OP_ERR; OSStatus status; @@ -494,6 +265,9 @@ static int s_aws_secitem_add_private_key_to_keychain( #ifdef AWS_SECITEM_FILEBASED_KEYCHAIN /* Target file-based keychain instead of data protection keychain. */ CFDictionaryAddValue(add_attributes, kSecUseDataProtectionKeychain, kCFBooleanFalse); + if (import_keychain != NULL) { + CFDictionaryAddValue(add_attributes, kSecUseKeychain, import_keychain); + } #endif // Initial attempt to add private key to keychain. @@ -568,7 +342,7 @@ static int s_aws_secitem_add_private_key_to_keychain( } } - AWS_LOGF_INFO(AWS_LS_IO_PKI, "static: Successfully imported private key into SecItem keychain."); + AWS_LOGF_INFO(AWS_LS_IO_PKI, "static: Successfully imported private key into keychain with SecItem."); result = AWS_OP_SUCCESS; @@ -584,15 +358,20 @@ static int s_aws_secitem_get_identity( CFAllocatorRef cf_alloc, CFDataRef serial_data, SecCertificateRef cert_ref, - sec_identity_t *out_identity) { + sec_identity_t *out_identity, + AwsSecKeychainRef import_keychain) { (void)cert_ref; + (void)import_keychain; int result = AWS_OP_ERR; OSStatus status; CFMutableDictionaryRef search_query = NULL; CFArrayRef sec_identity_array = NULL; + CFArrayRef cert_filter = NULL; + CFArrayRef keychain_filter = NULL; + /* * SecItem identity is created when a certificate matches a private key in the keychain. * Since a private key may be associated with multiple certificates, searching for the @@ -610,8 +389,13 @@ static int s_aws_secitem_get_identity( CFDictionaryAddValue(search_query, kSecUseDataProtectionKeychain, kCFBooleanFalse); /* The kSecAttrSerialNumber filter attribute does not work for kSecClassIdentity when SecItem targets file-based * keychain. So, use additional filtering by a certificate provided by a user. */ - CFArrayRef cert_filter = CFArrayCreate(cf_alloc, (const void **)&cert_ref, 1L, &kCFTypeArrayCallBacks); + cert_filter = CFArrayCreate(cf_alloc, (const void **)&cert_ref, 1L, &kCFTypeArrayCallBacks); CFDictionaryAddValue(search_query, kSecMatchItemList, cert_filter); + + if (import_keychain) { + keychain_filter = CFArrayCreate(cf_alloc, (const void **)&import_keychain, 1L, &kCFTypeArrayCallBacks); + CFDictionaryAddValue(search_query, kSecMatchSearchList, keychain_filter); + } #endif /* Though the kSecAttrSerialNumber and kSecMatchItemList attributes filter out unmatching identities, request @@ -691,92 +475,84 @@ static int s_aws_secitem_get_identity( // cleanup aws_cf_release(search_query); aws_cf_release(cert_filter); - // TODO Release elements. + aws_cf_release(keychain_filter); aws_cf_release(sec_identity_array); return result; } -int aws_secitem_import_cert_and_key( +/* + * Look for a private key in PEM data sections. + * Returns the first private key found. + */ +struct aws_pem_object *s_find_private_key(const struct aws_array_list *pem_objects_list) { + struct aws_pem_object *pem_object_ptr = NULL; + for (size_t index = 0; index < aws_array_list_length(pem_objects_list); index++) { + aws_array_list_get_at_ptr(pem_objects_list, (void **)&pem_object_ptr, index); + switch (pem_object_ptr->type) { + case AWS_PEM_TYPE_PRIVATE_RSA_PKCS1: + case AWS_PEM_TYPE_EVP_PKEY: + case AWS_PEM_TYPE_EC_PRIVATE: + case AWS_PEM_TYPE_PRIVATE_PKCS8: + AWS_LOGF_DEBUG(AWS_LS_IO_PKI, "static: Found a private key in PEM file."); + return pem_object_ptr; + default: + break; + } + } + return NULL; +} + +/* + * Import private key into Apple keychain. + * + * macOS: + * - imports into file-based keychain. + * - supports RSA with a key size of at least 2048 bits. + * - supports RSA-PSS with a key size of at least 2048 bits. + * - supports ECC NIST P-256/P-384/P-521 keys. + * iOS/tvOS: + * - imports into data protection keychain. + * - supports RSA with a key size of at least 2048 bits. + */ +int s_import_private_key_into_keychain( struct aws_allocator *alloc, CFAllocatorRef cf_alloc, - const struct aws_byte_cursor *public_cert_chain, const struct aws_byte_cursor *private_key, - sec_identity_t *secitem_identity, - const struct aws_secitem_options *secitem_options) { - - AWS_PRECONDITION(public_cert_chain != NULL); - AWS_PRECONDITION(private_key != NULL); + const struct aws_secitem_options *secitem_options, + AwsSecKeychainRef import_keychain) { int result = AWS_OP_ERR; - CFErrorRef error = NULL; + struct aws_array_list decoded_key_buffer_list; + AWS_ZERO_STRUCT(decoded_key_buffer_list); - CFDataRef cert_data = NULL; - SecCertificateRef cert_ref = NULL; - CFDataRef cert_serial_data = NULL; - CFStringRef cert_label_ref = NULL; + struct aws_pem_object *pem_key_ptr = NULL; - CFMutableDictionaryRef key_attributes = NULL; CFDictionaryRef key_copied_attributes = NULL; - CFDataRef key_data = NULL; + CFMutableDictionaryRef key_attributes = NULL; SecKeyRef key_ref = NULL; + CFDataRef key_data = NULL; + CFDataRef application_label_ref = NULL; CFStringRef key_type = NULL; CFStringRef key_label_ref = NULL; - CFDataRef application_label_ref = NULL; - - struct aws_array_list decoded_cert_buffer_list; - AWS_ZERO_STRUCT(decoded_cert_buffer_list); - struct aws_array_list decoded_key_buffer_list; - AWS_ZERO_STRUCT(decoded_key_buffer_list); + CFErrorRef error = NULL; /* - * iOS SecItem requires DER encoded files so we first convert the provided PEM encoded + * SecItem requires DER encoded files so we first convert the provided PEM encoded * cert and key into a list of aws_pem_object that strips headers/footers and Base64 decodes * the data into a byte buf. */ - if (aws_pem_objects_init_from_file_contents(&decoded_cert_buffer_list, alloc, *public_cert_chain)) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed to decode PEM certificate to DER format."); - goto done; - } - AWS_ASSERT(aws_array_list_is_valid(&decoded_cert_buffer_list)); - if (aws_pem_objects_init_from_file_contents(&decoded_key_buffer_list, alloc, *private_key)) { AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed to decode PEM private key to DER format."); goto done; } AWS_ASSERT(aws_array_list_is_valid(&decoded_key_buffer_list)); - /* - * A PEM certificate file could contains multiple PEM data sections. We currently decode and - * use the first certificate data only. Certificate chaining support could be added in the future. - */ - if (aws_array_list_length(&decoded_cert_buffer_list) > 1) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "Certificate chains not currently supported on iOS."); - result = aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - goto done; - } - - /* Convert the DER encoded files to the CFDataRef type required for import into keychain */ - struct aws_pem_object *pem_cert_ptr = NULL; - aws_array_list_get_at_ptr(&decoded_cert_buffer_list, (void **)&pem_cert_ptr, 0); - AWS_ASSERT(pem_cert_ptr); - - struct aws_pem_object *pem_key_ptr = NULL; - aws_array_list_get_at_ptr(&decoded_key_buffer_list, (void **)&pem_key_ptr, 0); - AWS_ASSERT(pem_key_ptr); - - cert_data = CFDataCreate(cf_alloc, pem_cert_ptr->data.buffer, pem_cert_ptr->data.len); - if (!cert_data) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "Error creating certificate data system call."); - aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); - goto done; - } - - key_data = CFDataCreate(cf_alloc, pem_key_ptr->data.buffer, pem_key_ptr->data.len); - if (!key_data) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "Error creating private key data system call."); - aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + pem_key_ptr = s_find_private_key(&decoded_key_buffer_list); + if (!pem_key_ptr) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed to find a private key in PEM file."); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); goto done; } @@ -791,6 +567,13 @@ int aws_secitem_import_cert_and_key( case AWS_PEM_TYPE_EC_PRIVATE: key_type = kSecAttrKeyTypeEC; + if (!s_is_filebased_keychain) { + AWS_LOGF_ERROR( + AWS_LS_IO_PKI, + "static: The ECC private key format is currently unsupported for use on iOS or tvOS"); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + goto done; + } break; case AWS_PEM_TYPE_PRIVATE_PKCS8: @@ -800,19 +583,220 @@ int aws_secitem_import_cert_and_key( * different import strategy than the currently shared one. */ key_type = kSecAttrKeyTypeRSA; - AWS_LOGF_ERROR( - AWS_LS_IO_PKI, "The PKCS8 private key format is currently unsupported for use with SecItem."); - aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - goto done; + if (!s_is_filebased_keychain) { + AWS_LOGF_ERROR( + AWS_LS_IO_PKI, + "static: The PKCS8 private key format is currently unsupported for use with SecItem"); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + goto done; + } break; - case AWS_PEM_TYPE_UNKNOWN: default: - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "Unsupported private key format."); + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Unsupported private key format %d", (int)pem_key_ptr->type); aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); goto done; } + key_data = CFDataCreate(cf_alloc, pem_key_ptr->data.buffer, pem_key_ptr->data.len); + if (!key_data) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "Error creating private key data system call."); + aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + + /* + * We create a SecKeyRef (key_ref) here using the key_data for the purpose of extracting the public key hash from + * the private key. We need the public key hash (application_label_ref) to use as a unique identifier when importing + * the private key into the keychain. + */ + key_attributes = + CFDictionaryCreateMutable(cf_alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(key_attributes, kSecAttrKeyClass, kSecAttrKeyClassPrivate); + CFDictionaryAddValue(key_attributes, kSecAttrKeyType, key_type); + + /* + * Try to parse a user-provided private key into a SecKeyRef. On this step, the private key won't be added + * to keychain yet. + */ + key_ref = SecKeyCreateWithData(key_data, key_attributes, &error); + if (error) { + char description_buffer[256]; + aws_get_core_foundation_error_description(error, description_buffer, sizeof(description_buffer)); + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed importing private key using SecItem: %s", description_buffer); + + if (!s_is_filebased_keychain) { + aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + + /* + * If parsing with SecItem fails, we fall back to trying to add the private key via SecKeychain API. + * This API is available on macOS only. + */ + AWS_LOGF_DEBUG(AWS_LS_IO_PKI, "static: Falling back to SecKeychain API for private key import"); + if (s_import_key_into_keychain_with_seckeychain(alloc, cf_alloc, private_key, import_keychain)) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed importing private key into keychain with SecKeychain API"); + aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + result = AWS_OP_SUCCESS; + } else { + /* + * Get the hash of the public key stored within the private key by extracting it from the key_ref's attributes + */ + key_copied_attributes = SecKeyCopyAttributes(key_ref); + + /* + * application_label_ref does not need to be released. It gets released when key_copied_attributes is released. + */ + application_label_ref = (CFDataRef)CFDictionaryGetValue(key_copied_attributes, kSecAttrApplicationLabel); + if (!application_label_ref) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed creating private key application label."); + aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + + key_label_ref = CFStringCreateWithBytes( + cf_alloc, + (const UInt8 *)aws_string_bytes(secitem_options->key_label), + secitem_options->key_label->len, + kCFStringEncodingUTF8, + false); + if (!key_label_ref) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed creating private key label."); + aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + + if (s_aws_secitem_add_private_key_to_keychain( + cf_alloc, key_ref, key_label_ref, application_label_ref, import_keychain)) { + aws_mutex_unlock(&s_sec_mutex); + goto done; + } + + result = AWS_OP_SUCCESS; + } + +done: + aws_cf_release(key_attributes); + aws_cf_release(key_copied_attributes); + aws_cf_release(key_type); + aws_cf_release(key_label_ref); + aws_cf_release(key_data); + aws_cf_release(key_ref); + aws_pem_objects_clean_up(&decoded_key_buffer_list); + + return result; +} + +int aws_secitem_import_cert_and_key( + struct aws_allocator *alloc, + CFAllocatorRef cf_alloc, + const struct aws_byte_cursor *public_cert_chain, + const struct aws_byte_cursor *private_key, + sec_identity_t *secitem_identity, + const struct aws_secitem_options *secitem_options, + const struct aws_string *keychain_path) { + + AWS_PRECONDITION(public_cert_chain != NULL); + AWS_PRECONDITION(private_key != NULL); + + int result = AWS_OP_ERR; + + CFErrorRef error = NULL; + CFDataRef cert_data = NULL; + SecCertificateRef cert_ref = NULL; + CFDataRef cert_serial_data = NULL; + CFStringRef cert_label_ref = NULL; + AwsSecKeychainRef import_keychain = NULL; + + struct aws_array_list decoded_cert_buffer_list; + AWS_ZERO_STRUCT(decoded_cert_buffer_list); + +#ifdef AWS_SECITEM_FILEBASED_KEYCHAIN +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" + + /* + * SecKeychain functions are marked as deprecated. There are no non-deprecated functions for specifying specific + * file-based keychains. + * Disable compiler warnings for now. This will be removed when we stop supporting file-based keychain altogether. + */ + if (keychain_path) { + OSStatus keychain_status = SecKeychainOpen(aws_string_c_str(keychain_path), &import_keychain); + if (keychain_status != errSecSuccess) { + AWS_LOGF_ERROR( + AWS_LS_IO_PKI, + "static: error opening keychain \"%s\" with OSStatus %d", + aws_string_c_str(keychain_path), + keychain_status); + result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + keychain_status = SecKeychainUnlock(import_keychain, 0, "", true); + if (keychain_status != errSecSuccess) { + AWS_LOGF_ERROR( + AWS_LS_IO_PKI, + "static: error unlocking keychain \"%s\" with OSStatus %d", + aws_string_c_str(keychain_path), + keychain_status); + result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + } else { + OSStatus keychain_status = SecKeychainCopyDefault(&import_keychain); + if (keychain_status != errSecSuccess) { + AWS_LOGF_ERROR( + AWS_LS_IO_PKI, "static: error opening the default keychain with OSStatus %d", keychain_status); + result = aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + } + +# pragma clang diagnostic pop + +#else /* AWS_SECITEM_FILEBASED_KEYCHAIN */ + if (keychain_path) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Keychain path is supported only on macOS"); + result = aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + goto done; + } +#endif /* AWS_SECITEM_FILEBASED_KEYCHAIN */ + + /* + * SecItem requires DER encoded files so we first convert the provided PEM encoded + * cert and key into a list of aws_pem_object that strips headers/footers and Base64 decodes + * the data into a byte buf. + */ + if (aws_pem_objects_init_from_file_contents(&decoded_cert_buffer_list, alloc, *public_cert_chain)) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed to decode PEM certificate to DER format."); + goto done; + } + AWS_ASSERT(aws_array_list_is_valid(&decoded_cert_buffer_list)); + + /* + * A PEM certificate file could contain multiple PEM data sections. We currently decode and + * use the first certificate data only. Certificate chaining support could be added in the future. + */ + if (aws_array_list_length(&decoded_cert_buffer_list) > 1) { + AWS_LOGF_ERROR(AWS_LS_IO_PKI, "Certificate chains not currently supported on iOS."); + result = aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + goto done; + } + + /* Convert the DER encoded files to the CFDataRef type required for import into keychain */ + struct aws_pem_object *pem_cert_ptr = NULL; + aws_array_list_get_at_ptr(&decoded_cert_buffer_list, (void **)&pem_cert_ptr, 0); + AWS_ASSERT(pem_cert_ptr); + + cert_data = CFDataCreate(cf_alloc, pem_cert_ptr->data.buffer, pem_cert_ptr->data.len); + if (!cert_data) { + AWS_LOGF_WARN(AWS_LS_IO_PKI, "Error creating certificate data system call."); + aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + goto done; + } + /* Attributes used for query and adding of cert/key SecItems */ /* @@ -846,76 +830,40 @@ int aws_secitem_import_cert_and_key( goto done; } - /* - * We create a SecKeyRef (key_ref) here using the key_data for the purpose of extracting the public key hash from - * the private key. We need the public key hash (application_label_ref) to use as a unique identifier when importing - * the private key into the keychain. - */ - key_attributes = - CFDictionaryCreateMutable(cf_alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(key_attributes, kSecAttrKeyClass, kSecAttrKeyClassPrivate); - CFDictionaryAddValue(key_attributes, kSecAttrKeyType, key_type); -#ifdef AWS_SECITEM_FILEBASED_KEYCHAIN - /* Target file-based keychain instead of data protection keychain. */ - CFDictionaryAddValue(key_attributes, kSecUseDataProtectionKeychain, kCFBooleanFalse); -#endif - - key_ref = SecKeyCreateWithData(key_data, key_attributes, &error); - - // Get the hash of the public key stored within the private key by extracting it from the key_ref's attributes - key_copied_attributes = SecKeyCopyAttributes(key_ref); - // application_label_ref does not need to be released. It gets released when key_copied_attributes is released. - application_label_ref = (CFDataRef)CFDictionaryGetValue(key_copied_attributes, kSecAttrApplicationLabel); - if (!application_label_ref) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "Failed creating private key application label."); - aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); - goto done; - } + /* Add the certificate and private key to keychain then retrieve identity. + * Protect the entire SecItem operation with mutex to prevent race conditions. */ + aws_mutex_lock(&s_sec_mutex); - key_label_ref = CFStringCreateWithBytes( - cf_alloc, - (const UInt8 *)aws_string_bytes(secitem_options->key_label), - secitem_options->key_label->len, - kCFStringEncodingUTF8, - false); - if (!key_label_ref) { - AWS_LOGF_ERROR(AWS_LS_IO_PKI, "Failed creating private key label."); - aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE); + if (s_import_private_key_into_keychain(alloc, cf_alloc, private_key, secitem_options, import_keychain)) { + aws_mutex_unlock(&s_sec_mutex); goto done; } - // Add the certificate and private key to keychain then retrieve identity - if (s_aws_secitem_add_certificate_to_keychain(cf_alloc, cert_ref, cert_serial_data, cert_label_ref)) { + if (s_aws_secitem_add_certificate_to_keychain( + cf_alloc, cert_ref, cert_serial_data, cert_label_ref, import_keychain)) { + aws_mutex_unlock(&s_sec_mutex); goto done; } - if (s_aws_secitem_add_private_key_to_keychain(cf_alloc, key_ref, key_label_ref, application_label_ref)) { + if (s_aws_secitem_get_identity(cf_alloc, cert_serial_data, cert_ref, secitem_identity, import_keychain)) { + aws_mutex_unlock(&s_sec_mutex); goto done; } - if (s_aws_secitem_get_identity(cf_alloc, cert_serial_data, cert_ref, secitem_identity)) { - goto done; - } + aws_mutex_unlock(&s_sec_mutex); result = AWS_OP_SUCCESS; done: - // cleanup + /* cleanup */ aws_cf_release(error); aws_cf_release(cert_data); aws_cf_release(cert_ref); aws_cf_release(cert_serial_data); aws_cf_release(cert_label_ref); - aws_cf_release(key_attributes); - aws_cf_release(key_copied_attributes); - aws_cf_release(key_data); - aws_cf_release(key_ref); - aws_cf_release(key_type); - aws_cf_release(key_label_ref); - // Zero out the array list and release it + /* Zero out the array list and release it */ aws_pem_objects_clean_up(&decoded_cert_buffer_list); - aws_pem_objects_clean_up(&decoded_key_buffer_list); return result; } diff --git a/source/darwin/darwin_shared.c b/source/darwin/darwin_shared.c new file mode 100644 index 000000000..f994b2d02 --- /dev/null +++ b/source/darwin/darwin_shared.c @@ -0,0 +1,23 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include "darwin_shared_private.h" + +/* + * Helper function that gets the available human-readable error description from Core Foundation. + */ +void aws_get_core_foundation_error_description(CFErrorRef error, char *description_buffer, size_t buffer_size) { + if (error == NULL) { + snprintf(description_buffer, buffer_size, "No error provided"); + return; + } + + CFStringRef error_description = CFErrorCopyDescription(error); + if (error_description) { + CFStringGetCString(error_description, description_buffer, buffer_size, kCFStringEncodingUTF8); + CFRelease(error_description); + } else { + snprintf(description_buffer, buffer_size, "Unable to retrieve error description"); + } +} diff --git a/source/darwin/darwin_shared_private.h b/source/darwin/darwin_shared_private.h new file mode 100644 index 000000000..76c08a567 --- /dev/null +++ b/source/darwin/darwin_shared_private.h @@ -0,0 +1,13 @@ +#ifndef AWS_IO_DARWIN_SHARED_PRIVATE_H +#define AWS_IO_DARWIN_SHARED_PRIVATE_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +void aws_get_core_foundation_error_description(CFErrorRef error, char *description_buffer, size_t buffer_size); + +#endif // AWS_IO_DARWIN_SHARED_PRIVATE_H diff --git a/source/darwin/nw_socket.c b/source/darwin/nw_socket.c index dc1b48b64..bd63e9d11 100644 --- a/source/darwin/nw_socket.c +++ b/source/darwin/nw_socket.c @@ -12,6 +12,7 @@ #include #include +#include "./darwin_shared_private.h" // private header #include "./dispatch_queue_event_loop_private.h" // private header #include #include @@ -128,24 +129,6 @@ static inline int s_convert_pton_error(int pton_code) { return s_determine_socket_error(errno); } -/* - * Helper function that gets the available human readable error description from Core Foundation. - */ -static void s_get_error_description(CFErrorRef error, char *description_buffer, size_t buffer_size) { - if (error == NULL) { - snprintf(description_buffer, buffer_size, "No error provided"); - return; - } - - CFStringRef error_description = CFErrorCopyDescription(error); - if (error_description) { - CFStringGetCString(error_description, description_buffer, buffer_size, kCFStringEncodingUTF8); - CFRelease(error_description); - } else { - snprintf(description_buffer, buffer_size, "Unable to retrieve error description"); - } -} - /* * A socket is only in one of these states at a time, except for CONNECTED_READ | CONNECTED_WRITE. * @@ -624,7 +607,7 @@ static void s_tls_verification_block( } } else { char description_buffer[256]; - s_get_error_description(error, description_buffer, sizeof(description_buffer)); + aws_get_core_foundation_error_description(error, description_buffer, sizeof(description_buffer)); int crt_error_code = s_determine_socket_error((int)CFErrorGetCode(error)); AWS_LOGF_DEBUG( AWS_LS_IO_TLS, @@ -745,11 +728,9 @@ static int s_setup_socket_params(struct nw_socket *nw_socket, const struct aws_s } bool setup_tls = false; - if (aws_is_using_secitem()) { - /* If SecItem isn't being used then the nw_parameters should not be setup to handle the TLS Negotiation. */ - if (nw_socket->tls_ctx) { - setup_tls = true; - } + /* If SecItem isn't being used then the nw_parameters should not be setup to handle the TLS Negotiation. */ + if (nw_socket->tls_ctx) { + setup_tls = true; } if (options->type == AWS_SOCKET_STREAM) { diff --git a/source/darwin/secure_transport_tls_channel_handler.c b/source/darwin/secure_transport_tls_channel_handler.c index a3cebf739..2f9ac9add 100644 --- a/source/darwin/secure_transport_tls_channel_handler.c +++ b/source/darwin/secure_transport_tls_channel_handler.c @@ -78,11 +78,7 @@ void aws_tls_init_static_state(struct aws_allocator *alloc) { s_SSLSetALPNProtocols = (OSStatus(*)(SSLContextRef, CFArrayRef))dlsym(RTLD_DEFAULT, "SSLSetALPNProtocols"); s_SSLCopyALPNProtocols = (OSStatus(*)(SSLContextRef, CFArrayRef *))dlsym(RTLD_DEFAULT, "SSLCopyALPNProtocols"); - if (aws_is_using_secitem()) { - AWS_LOGF_INFO(AWS_LS_IO_TLS, "static: initializing TLS implementation as Apple SecItem."); - } else { - AWS_LOGF_INFO(AWS_LS_IO_TLS, "static: initializing TLS implementation as Apple SecureTransport."); - } + AWS_LOGF_INFO(AWS_LS_IO_TLS, "static: initializing TLS implementation as Apple SecItem."); if (s_SSLSetALPNProtocols) { AWS_LOGF_INFO(AWS_LS_IO_TLS, "static: ALPN support detected."); @@ -829,29 +825,20 @@ static void s_gather_statistics(struct aws_channel_handler *handler, struct aws_ } struct aws_byte_buf aws_tls_handler_protocol(struct aws_channel_handler *handler) { - if (aws_is_using_secitem()) { - /* Apple Network Framework's SecItem API handles both TCP and TLS aspects of a connection and an aws_channel - * using it does not have a TLS. The negotiated protocol is stored in the nw_socket and must be retrieved from - * the socket rather than a secure_transport_handler. */ - const struct aws_socket *socket = aws_socket_handler_get_socket(handler); - return socket->vtable->socket_get_protocol_fn(socket); - } - struct secure_transport_handler *secure_transport_handler = handler->impl; - return secure_transport_handler->protocol; + /* Apple Network Framework's SecItem API handles both TCP and TLS aspects of a connection and an aws_channel + * using it does not have a TLS. The negotiated protocol is stored in the nw_socket and must be retrieved from + * the socket rather than a secure_transport_handler. */ + const struct aws_socket *socket = aws_socket_handler_get_socket(handler); + return socket->vtable->socket_get_protocol_fn(socket); } struct aws_byte_buf aws_tls_handler_server_name(struct aws_channel_handler *handler) { struct aws_string *server_name = NULL; - if (aws_is_using_secitem()) { - /* Apple Network Framework's SecItem API handles both TCP and TLS aspects of a connection and an aws_channel - * using it does not have a TLS slot. The server_name is stored in the nw_socket and must be retrieved from the - * socket rather than a secure_transport_handler. */ - const struct aws_socket *socket = aws_socket_handler_get_socket(handler); - server_name = socket->vtable->socket_get_server_name_fn(socket); - } else { - struct secure_transport_handler *secure_transport_handler = handler->impl; - server_name = secure_transport_handler->server_name; - } + /* Apple Network Framework's SecItem API handles both TCP and TLS aspects of a connection and an aws_channel + * using it does not have a TLS slot. The server_name is stored in the nw_socket and must be retrieved from the + * socket rather than a secure_transport_handler. */ + const struct aws_socket *socket = aws_socket_handler_get_socket(handler); + server_name = socket->vtable->socket_get_server_name_fn(socket); const uint8_t *bytes = NULL; size_t len = 0; @@ -967,10 +954,6 @@ static struct aws_channel_handler *s_tls_handler_new( SSLSetSessionOption(secure_transport_handler->ctx, kSSLSessionOptionBreakOnServerAuth, true); } - if (secure_transport_ctx->certs) { - status = SSLSetCertificate(secure_transport_handler->ctx, secure_transport_ctx->certs); - } - secure_transport_handler->ca_certs = NULL; if (secure_transport_ctx->ca_cert) { secure_transport_handler->ca_certs = secure_transport_ctx->ca_cert; @@ -1037,25 +1020,11 @@ struct aws_channel_handler *aws_tls_server_handler_new( return s_tls_handler_new(allocator, options, slot, kSSLServerSide); } -void s_aws_release_cert(const void *val, void *context) { - (void)context; - CFTypeRef cert = (CFTypeRef)val; - CFRelease(cert); -} - static void s_aws_secure_transport_ctx_destroy(struct secure_transport_ctx *secure_transport_ctx) { if (secure_transport_ctx == NULL) { return; } - if (secure_transport_ctx->certs) { - if (secure_transport_ctx->cleanup_cert) { - CFRange range = CFRangeMake(0, CFArrayGetCount(secure_transport_ctx->certs)); - CFArrayApplyFunction(secure_transport_ctx->certs, range, s_aws_release_cert, NULL); - } - CFRelease(secure_transport_ctx->certs); - } - if (secure_transport_ctx->secitem_identity) { CFRelease(secure_transport_ctx->secitem_identity); } @@ -1096,8 +1065,6 @@ static struct aws_tls_ctx *s_tls_ctx_new(struct aws_allocator *alloc, const stru secure_transport_ctx->verify_peer = options->verify_peer; secure_transport_ctx->ca_cert = NULL; - secure_transport_ctx->certs = NULL; - secure_transport_ctx->cleanup_cert = false; secure_transport_ctx->secitem_identity = NULL; secure_transport_ctx->ctx.alloc = alloc; secure_transport_ctx->ctx.impl = secure_transport_ctx; @@ -1123,64 +1090,34 @@ static struct aws_tls_ctx *s_tls_ctx_new(struct aws_allocator *alloc, const stru struct aws_byte_cursor cert_chain_cur = aws_byte_cursor_from_buf(&options->certificate); struct aws_byte_cursor private_key_cur = aws_byte_cursor_from_buf(&options->private_key); - if (aws_is_using_secitem()) { - if (aws_secitem_import_cert_and_key( - alloc, - secure_transport_ctx->wrapped_allocator, - &cert_chain_cur, - &private_key_cur, - &secure_transport_ctx->secitem_identity, - &options->secitem_options)) { - AWS_LOGF_ERROR( - AWS_LS_IO_TLS, - "static: failed to import certificate and private key with error %d.", - aws_last_error()); - goto cleanup_wrapped_allocator; - } - } else { - if (aws_import_public_and_private_keys_to_identity( - alloc, - secure_transport_ctx->wrapped_allocator, - &cert_chain_cur, - &private_key_cur, - &secure_transport_ctx->certs, - options->keychain_path)) { - AWS_LOGF_ERROR( - AWS_LS_IO_TLS, - "static: failed to import certificate and private key with error %d.", - aws_last_error()); - goto cleanup_wrapped_allocator; - } - secure_transport_ctx->cleanup_cert = true; + + if (aws_secitem_import_cert_and_key( + alloc, + secure_transport_ctx->wrapped_allocator, + &cert_chain_cur, + &private_key_cur, + &secure_transport_ctx->secitem_identity, + &options->secitem_options, + options->keychain_path)) { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, "static: failed to import certificate and private key with error %d.", aws_last_error()); + goto cleanup_wrapped_allocator; } + } else if (aws_tls_options_buf_is_set(&options->pkcs12)) { struct aws_byte_cursor pkcs12_blob_cur = aws_byte_cursor_from_buf(&options->pkcs12); struct aws_byte_cursor password_cur = aws_byte_cursor_from_buf(&options->pkcs12_password); - if (aws_is_using_secitem()) { - AWS_LOGF_DEBUG( - AWS_LS_IO_TLS, "static: a pkcs#12 certificate and key has been set, setting up for secitem now."); - if (aws_secitem_import_pkcs12( - secure_transport_ctx->wrapped_allocator, - &pkcs12_blob_cur, - &password_cur, - &secure_transport_ctx->secitem_identity)) { - AWS_LOGF_ERROR( - AWS_LS_IO_TLS, "static: failed to import pkcs#12 certificate with error %d.", aws_last_error()); - goto cleanup_wrapped_allocator; - } - } else { - AWS_LOGF_DEBUG( - AWS_LS_IO_TLS, "static: a pkcs#12 certificate and key has been set, setting up for secKeychain now."); - if (aws_import_pkcs12_to_identity( - secure_transport_ctx->wrapped_allocator, - &pkcs12_blob_cur, - &password_cur, - &secure_transport_ctx->certs)) { - AWS_LOGF_ERROR( - AWS_LS_IO_TLS, "static: failed to import pkcs#12 certificate with error %d.", aws_last_error()); - goto cleanup_wrapped_allocator; - } + AWS_LOGF_DEBUG( + AWS_LS_IO_TLS, "static: a pkcs#12 certificate and key has been set, setting up for secitem now."); + if (aws_secitem_import_pkcs12( + secure_transport_ctx->wrapped_allocator, + &pkcs12_blob_cur, + &password_cur, + &secure_transport_ctx->secitem_identity)) { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, "static: failed to import pkcs#12 certificate with error %d.", aws_last_error()); + goto cleanup_wrapped_allocator; } } diff --git a/source/tls_channel_handler.c b/source/tls_channel_handler.c index 6e9539f48..59bf2708e 100644 --- a/source/tls_channel_handler.c +++ b/source/tls_channel_handler.c @@ -27,10 +27,8 @@ void aws_tls_ctx_options_init_default_client(struct aws_tls_ctx_options *options #ifdef __APPLE__ - if (aws_is_using_secitem()) { - options->secitem_options.cert_label = aws_string_new_from_c_str(allocator, "aws-crt-default-certificate-label"); - options->secitem_options.key_label = aws_string_new_from_c_str(allocator, "aws-crt-default-key-label"); - } + options->secitem_options.cert_label = aws_string_new_from_c_str(allocator, "aws-crt-default-certificate-label"); + options->secitem_options.key_label = aws_string_new_from_c_str(allocator, "aws-crt-default-key-label"); #endif /* __APPLE__ */ } @@ -278,36 +276,32 @@ int aws_tls_ctx_options_set_keychain_path( int aws_tls_ctx_options_set_secitem_options( struct aws_tls_ctx_options *tls_ctx_options, const struct aws_secitem_options *secitem_options) { - if (aws_is_using_secitem()) { - if (secitem_options->cert_label != NULL) { - aws_string_destroy(tls_ctx_options->secitem_options.cert_label); - tls_ctx_options->secitem_options.cert_label = NULL; - tls_ctx_options->secitem_options.cert_label = - aws_string_new_from_string(tls_ctx_options->allocator, secitem_options->cert_label); - if (tls_ctx_options->secitem_options.cert_label == NULL) { - AWS_LOGF_ERROR(AWS_LS_IO_TLS, "static: Secitem option certificate label is invalid."); - return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - } - AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "static: Secitem option certificate label set."); + if (secitem_options->cert_label != NULL) { + aws_string_destroy(tls_ctx_options->secitem_options.cert_label); + tls_ctx_options->secitem_options.cert_label = NULL; + tls_ctx_options->secitem_options.cert_label = + aws_string_new_from_string(tls_ctx_options->allocator, secitem_options->cert_label); + if (tls_ctx_options->secitem_options.cert_label == NULL) { + AWS_LOGF_ERROR(AWS_LS_IO_TLS, "static: Secitem option certificate label is invalid."); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } + AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "static: Secitem option certificate label set."); + } - if (secitem_options->key_label != NULL) { - aws_string_destroy(tls_ctx_options->secitem_options.key_label); - tls_ctx_options->secitem_options.key_label = NULL; - tls_ctx_options->secitem_options.key_label = - aws_string_new_from_string(tls_ctx_options->allocator, secitem_options->key_label); - if (tls_ctx_options->secitem_options.key_label == NULL) { - AWS_LOGF_ERROR(AWS_LS_IO_TLS, "static: Secitem option key label is invalid."); - return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); - } - AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "static: Secitem option key label set."); + if (secitem_options->key_label != NULL) { + aws_string_destroy(tls_ctx_options->secitem_options.key_label); + tls_ctx_options->secitem_options.key_label = NULL; + tls_ctx_options->secitem_options.key_label = + aws_string_new_from_string(tls_ctx_options->allocator, secitem_options->key_label); + if (tls_ctx_options->secitem_options.key_label == NULL) { + AWS_LOGF_ERROR(AWS_LS_IO_TLS, "static: Secitem option key label is invalid."); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); } - return AWS_OP_SUCCESS; + AWS_LOGF_DEBUG(AWS_LS_IO_TLS, "static: Secitem option key label set."); } - AWS_LOGF_ERROR(AWS_LS_IO_TLS, "Secitem options can only be set when using Secitem."); - return AWS_OP_ERR; + return AWS_OP_SUCCESS; } #else /* __APPLE__ */ @@ -451,7 +445,7 @@ int aws_tls_ctx_options_init_default_server_from_path( const char *cert_path, const char *pkey_path) { -#if !defined(AWS_OS_IOS) +#if !defined(__APPLE__) if (aws_tls_ctx_options_init_client_mtls_from_path(options, allocator, cert_path, pkey_path)) { return AWS_OP_ERR; } @@ -474,7 +468,7 @@ int aws_tls_ctx_options_init_default_server( struct aws_byte_cursor *cert, struct aws_byte_cursor *pkey) { -#if !defined(AWS_OS_IOS) +#if !defined(__APPLE__) if (aws_tls_ctx_options_init_client_mtls(options, allocator, cert, pkey)) { return AWS_OP_ERR; } diff --git a/source/tls_channel_handler_shared.c b/source/tls_channel_handler_shared.c index ca9e82db5..9865425c4 100644 --- a/source/tls_channel_handler_shared.c +++ b/source/tls_channel_handler_shared.c @@ -8,7 +8,7 @@ #include #include -#if defined(AWS_USE_SECITEM) +#ifdef __APPLE__ static bool s_is_use_secitem = true; #else static bool s_is_use_secitem = false; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c8f91d724..b12c0bea4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -295,17 +295,25 @@ if(NOT BYO_CRYPTO) add_net_test_case(test_tls_negotiation_timeout) add_net_test_case(alpn_successfully_negotiates) add_net_test_case(alpn_no_protocol_message) - add_net_test_case(test_ecc_cert_import) + add_net_test_case(test_ecc_p256_cert_import) + add_net_test_case(test_ecc_p384_cert_import) + add_net_test_case(test_ecc_p521_cert_import) add_test_case(test_tls_cipher_preference) add_net_test_case(test_concurrent_cert_import) - if(NOT APPLE) - # PKCS8 is not supported on Apple platforms. PKCS8 support for SecItem can be added in the future but it will - # require macOS specific branching of logic and import of the key into the keychain. + if(NOT APPLE OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + # PKCS8 is not supported on iOS and tvOS. add_net_test_case(test_pkcs8_import) + endif() + if(APPLE) + # PKCS12 is primarily supported on APPLE platforms. + add_test_case(test_pkcs12_import) + endif() + + if(NOT APPLE) # This test shuts down the channel after a socket is established but while the TLS handshake is taking place # further up the channel. Apple Network Framework's connection handles both the socket connection as well # as the TLS handshake within the same create connection call without external notification that the socket diff --git a/tests/resources/ecc-cert.pem b/tests/resources/ecc-p256-cert.pem similarity index 100% rename from tests/resources/ecc-cert.pem rename to tests/resources/ecc-p256-cert.pem diff --git a/tests/resources/ecc-key.pem b/tests/resources/ecc-p256-key.pem similarity index 100% rename from tests/resources/ecc-key.pem rename to tests/resources/ecc-p256-key.pem diff --git a/tests/resources/ecc-p384-cert.pem b/tests/resources/ecc-p384-cert.pem new file mode 100644 index 000000000..ccff07eab --- /dev/null +++ b/tests/resources/ecc-p384-cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICvTCCAkKgAwIBAgIUeP9ui00vOr6GvctifJ929rRIKB8wCgYIKoZIzj0EAwIw +gZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdT +ZWF0dGxlMQ8wDQYDVQQKDAZBbWF6b24xDTALBgNVBAsMBFNES3MxDDAKBgNVBAMM +A0NSVDEwMC4GCSqGSIb3DQEJARYhYXdzLXNkay1jb21tb24tcnVudGltZUBhbWF6 +b24uY29tMB4XDTI1MTEwNjE3NTAzN1oXDTM1MDkxNTE3NTAzN1owgZQxCzAJBgNV +BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMQ8w +DQYDVQQKDAZBbWF6b24xDTALBgNVBAsMBFNES3MxDDAKBgNVBAMMA0NSVDEwMC4G +CSqGSIb3DQEJARYhYXdzLXNkay1jb21tb24tcnVudGltZUBhbWF6b24uY29tMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAELW5XvrGkolo2Rqm3XbTZ83dZYMjfLQK59HTg +9ExL2lVD2MHK0iFh2SB9PVCprghSHZW9uE8ymaiUyujt2WtVFRhTAlRt85vBM8Xw +SJ4ybYhzJwaRD8F+koIlbgRRJu+So1MwUTAdBgNVHQ4EFgQU+LZ0DF0pMGTw4F8A +jpaXUnQLT8cwHwYDVR0jBBgwFoAU+LZ0DF0pMGTw4F8AjpaXUnQLT8cwDwYDVR0T +AQH/BAUwAwEB/zAKBggqhkjOPQQDAgNpADBmAjEA/7upMHjcbIkZjiuLcdQH2+Sd +xGRQrAxd5pqcqQDKkV9VMdXHFkxH54GgbG7QrsPhAjEA3yLCiWbt7meDZZS4p0Ji +Pl6pg6lwzylTVVnQA9028rREcTebdc9ZmZ3SOIWKyydO +-----END CERTIFICATE----- diff --git a/tests/resources/ecc-p384-key.pem b/tests/resources/ecc-p384-key.pem new file mode 100644 index 000000000..85fb4b927 --- /dev/null +++ b/tests/resources/ecc-p384-key.pem @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDDeuS2ZwnCWqjeDJJThQ3TYukh9HxEo/VoOOf2vDS+p+Ml5sxF60H69 +jy6AmYwA1bKgBwYFK4EEACKhZANiAAQtble+saSiWjZGqbddtNnzd1lgyN8tArn0 +dOD0TEvaVUPYwcrSIWHZIH09UKmuCFIdlb24TzKZqJTK6O3Za1UVGFMCVG3zm8Ez +xfBInjJtiHMnBpEPwX6SgiVuBFEm75I= +-----END EC PRIVATE KEY----- diff --git a/tests/resources/ecc-p521-cert.pem b/tests/resources/ecc-p521-cert.pem new file mode 100644 index 000000000..8c6473a54 --- /dev/null +++ b/tests/resources/ecc-p521-cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBjCCAmigAwIBAgIUOerLXxyrMNqIjYkQfCFX5UX08+EwCgYIKoZIzj0EAwIw +gZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdT +ZWF0dGxlMQ8wDQYDVQQKDAZBbWF6b24xDTALBgNVBAsMBFNES3MxDDAKBgNVBAMM +A0NSVDEwMC4GCSqGSIb3DQEJARYhYXdzLXNkay1jb21tb24tcnVudGltZUBhbWF6 +b24uY29tMB4XDTI1MTExMjE4NTIyN1oXDTM1MDkyMTE4NTIyN1owgZQxCzAJBgNV +BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMQ8w +DQYDVQQKDAZBbWF6b24xDTALBgNVBAsMBFNES3MxDDAKBgNVBAMMA0NSVDEwMC4G +CSqGSIb3DQEJARYhYXdzLXNkay1jb21tb24tcnVudGltZUBhbWF6b24uY29tMIGb +MBAGByqGSM49AgEGBSuBBAAjA4GGAAQAz2hjkTaOXn1523TyFy1PpOy2t92fxqUk +TfjKkJ+58GC6Sem4iwzuiDPiZqi6EBUGLVPhtwaj3Sr4s+w6gq0LTxUBuvbN6FXe +W4w7rWZ1D1I621bB7Y//CAinK3zJ7NErsP+26Me93DEI4PCd4Iv6cf+17Y3walhb +HQokDrVx0IYFyS+jUzBRMB0GA1UdDgQWBBTdAsbmGqUnqsC7pvWcHD9jHLnnIDAf +BgNVHSMEGDAWgBTdAsbmGqUnqsC7pvWcHD9jHLnnIDAPBgNVHRMBAf8EBTADAQH/ +MAoGCCqGSM49BAMCA4GLADCBhwJBUmQ2LYPbBoOUh4FoE7taH9XDcVLsVu48TcbA +1kG2Ks7mmxS2YX/FssU/GePKlFhmCorVMh+cnpBH98u/41NfeFwCQgCODTwoUGCm +IE2bBFmQ+tlvJf1HEDdnP/t2HbwGJ3iKEHyipGQLC73wnSKPZ1f+qPNfzQSDCH2y +lmH4czz5ySQUiw== +-----END CERTIFICATE----- diff --git a/tests/resources/ecc-p521-key.pem b/tests/resources/ecc-p521-key.pem new file mode 100644 index 000000000..ea375e9bc --- /dev/null +++ b/tests/resources/ecc-p521-key.pem @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIANAx1H+GrgLyOkHixNpSwN5AgBUiEUQjcI0IKkdihiQKNo7w9WfNV +1U0I8/UV7gRTrFRHjw4lyinI4DIbXjE/jJOgBwYFK4EEACOhgYkDgYYABADPaGOR +No5efXnbdPIXLU+k7La33Z/GpSRN+MqQn7nwYLpJ6biLDO6IM+JmqLoQFQYtU+G3 +BqPdKviz7DqCrQtPFQG69s3oVd5bjDutZnUPUjrbVsHtj/8ICKcrfMns0Suw/7bo +x73cMQjg8J3gi/px/7XtjfBqWFsdCiQOtXHQhgXJLw== +-----END EC PRIVATE KEY----- diff --git a/tests/socket_test.c b/tests/socket_test.c index e42701ff0..f7e500a7e 100644 --- a/tests/socket_test.c +++ b/tests/socket_test.c @@ -814,7 +814,7 @@ static int s_test_socket_with_bind_to_interface(struct aws_allocator *allocator, options.keep_alive_timeout_sec = 60000; options.type = AWS_SOCKET_STREAM; options.domain = AWS_SOCKET_IPV4; -#if defined(AWS_OS_APPLE) +#if defined(__APPLE__) strncpy(options.network_interface_name, "lo0", AWS_NETWORK_INTERFACE_NAME_MAX); #else strncpy(options.network_interface_name, "lo", AWS_NETWORK_INTERFACE_NAME_MAX); @@ -860,7 +860,7 @@ static int s_test_socket_with_bind_to_invalid_interface(struct aws_allocator *al options.domain = AWS_SOCKET_IPV4; strncpy(options.network_interface_name, "invalid", AWS_NETWORK_INTERFACE_NAME_MAX); struct aws_socket outgoing; -#if (defined(AWS_OS_APPLE) && !defined(AWS_USE_APPLE_NETWORK_FRAMEWORK)) || defined(AWS_OS_LINUX) +#if (defined(AWS_OS_LINUX)) ASSERT_ERROR(AWS_IO_SOCKET_INVALID_OPTIONS, aws_socket_init(&outgoing, allocator, &options)); #else ASSERT_ERROR(AWS_ERROR_PLATFORM_NOT_SUPPORTED, aws_socket_init(&outgoing, allocator, &options)); @@ -1942,7 +1942,7 @@ static int s_cleanup_in_accept_doesnt_explode(struct aws_allocator *allocator, v ASSERT_SUCCESS(aws_socket_bind(&listener, &socket_bind_options)); ASSERT_SUCCESS(aws_socket_listen(&listener, 1024)); -#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK +#ifdef __APPLE__ aws_socket_set_cleanup_complete_callback(&listener, s_local_listener_shutdown_complete, &listener_args); #endif struct aws_socket_listener_options listener_options = { @@ -2000,7 +2000,7 @@ static int s_cleanup_in_accept_doesnt_explode(struct aws_allocator *allocator, v io_args.socket = listener_args.incoming; io_args.close_completed = false; -#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK +#ifdef __APPLE__ aws_socket_set_cleanup_complete_callback(io_args.socket, s_socket_shutdown_complete_fn, &io_args); io_args.shutdown_complete = false; #endif @@ -2011,7 +2011,7 @@ static int s_cleanup_in_accept_doesnt_explode(struct aws_allocator *allocator, v ASSERT_SUCCESS(aws_mutex_unlock(&mutex)); aws_socket_clean_up(io_args.socket); -#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK +#ifdef __APPLE__ ASSERT_SUCCESS(aws_mutex_lock(&mutex)); aws_condition_variable_wait_pred(&io_args.condition_variable, &mutex, s_shutdown_completed_predicate, &io_args); ASSERT_SUCCESS(aws_mutex_unlock(&mutex)); @@ -2168,7 +2168,7 @@ static int s_cleanup_in_write_cb_doesnt_explode(struct aws_allocator *allocator, .shutdown_complete = false, }; -#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK +#ifdef __APPLE__ aws_socket_set_cleanup_complete_callback(io_args.socket, s_socket_shutdown_complete_fn, &io_args); #endif @@ -2191,24 +2191,24 @@ static int s_cleanup_in_write_cb_doesnt_explode(struct aws_allocator *allocator, io_args.amount_written = 0; io_args.socket = server_sock; io_args.shutdown_complete = false; -#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK +#ifdef __APPLE__ aws_socket_set_cleanup_complete_callback(io_args.socket, s_socket_shutdown_complete_fn, &io_args); #endif aws_event_loop_schedule_task_now(event_loop, &write_task); ASSERT_SUCCESS(aws_mutex_lock(&mutex)); aws_condition_variable_wait_pred(&io_args.condition_variable, &mutex, s_write_completed_predicate, &io_args); -#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK +#ifdef __APPLE__ aws_condition_variable_wait_pred(&io_args.condition_variable, &mutex, s_shutdown_completed_predicate, &io_args); #endif ASSERT_SUCCESS(aws_mutex_unlock(&mutex)); ASSERT_INT_EQUALS(AWS_OP_SUCCESS, io_args.error_code); aws_mem_release(allocator, server_sock); -#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK +#ifdef __APPLE__ aws_socket_set_cleanup_complete_callback(&listener, s_local_listener_shutdown_complete, &listener_args); #endif aws_socket_clean_up(&listener); -#ifdef AWS_USE_APPLE_NETWORK_FRAMEWORK +#ifdef __APPLE__ ASSERT_SUCCESS(aws_mutex_lock(&mutex)); aws_condition_variable_wait_pred( listener_args.condition_variable, &mutex, s_local_listener_shutdown_completed_predicate, &listener_args); diff --git a/tests/tls_handler_test.c b/tests/tls_handler_test.c index 9f6096b2b..21b33b183 100644 --- a/tests/tls_handler_test.c +++ b/tests/tls_handler_test.c @@ -1444,7 +1444,7 @@ static int s_verify_good_host( return AWS_OP_SUCCESS; } -static int s_verify_good_host_mqtt_connect( +static int s_verify_good_host_mtls_connect( struct aws_allocator *allocator, const struct aws_string *host_name, uint32_t port, @@ -1621,16 +1621,16 @@ static int s_tls_client_channel_negotiation_success_ecc384_SCHANNEL_CREDS_fn( AWS_TEST_CASE( tls_client_channel_negotiation_success_ecc384_deprecated, s_tls_client_channel_negotiation_success_ecc384_SCHANNEL_CREDS_fn) -# endif +# endif /* _WIN32 */ static void s_raise_tls_version_to_13(struct aws_tls_ctx_options *options) { aws_tls_ctx_options_set_minimum_tls_version(options, AWS_IO_TLSv1_3); } -AWS_STATIC_STRING_FROM_LITERAL(s_aws_ecc384_host_name, "127.0.0.1"); +AWS_STATIC_STRING_FROM_LITERAL(s_aws_mtls_host_name, "127.0.0.1"); static int s_tls_client_channel_negotiation_success_mtls_tls1_3_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; - return s_verify_good_host_mqtt_connect(allocator, s_aws_ecc384_host_name, 59443, s_raise_tls_version_to_13); + return s_verify_good_host_mtls_connect(allocator, s_aws_mtls_host_name, 59443, s_raise_tls_version_to_13); } AWS_TEST_CASE( @@ -2641,18 +2641,14 @@ static int s_tls_destroy_null_context(struct aws_allocator *allocator, void *ctx } AWS_TEST_CASE(tls_destroy_null_context, s_tls_destroy_null_context); -static int s_test_ecc_cert_import(struct aws_allocator *allocator, void *ctx) { - (void)ctx; - (void)allocator; - -# ifndef AWS_OS_APPLE +static int s_test_cert_key_import(struct aws_allocator *allocator, const char *cert_path, const char *key_path) { aws_io_library_init(allocator); struct aws_byte_buf cert_buf; struct aws_byte_buf key_buf; - ASSERT_SUCCESS(aws_byte_buf_init_from_file(&cert_buf, allocator, "ecc-cert.pem")); - ASSERT_SUCCESS(aws_byte_buf_init_from_file(&key_buf, allocator, "ecc-key.pem")); + ASSERT_SUCCESS(aws_byte_buf_init_from_file(&cert_buf, allocator, cert_path)); + ASSERT_SUCCESS(aws_byte_buf_init_from_file(&key_buf, allocator, key_path)); struct aws_byte_cursor cert_cur = aws_byte_cursor_from_buf(&cert_buf); struct aws_byte_cursor key_cur = aws_byte_cursor_from_buf(&key_buf); @@ -2672,30 +2668,49 @@ static int s_test_ecc_cert_import(struct aws_allocator *allocator, void *ctx) { aws_byte_buf_clean_up(&key_buf); aws_io_library_clean_up(); -# endif /* AWS_OS_APPLE */ return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_ecc_cert_import, s_test_ecc_cert_import) +static int s_test_ecc_p256_cert_import(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + return s_test_cert_key_import(allocator, "ecc-p256-cert.pem", "ecc-p256-key.pem"); +} + +AWS_TEST_CASE(test_ecc_p256_cert_import, s_test_ecc_p256_cert_import) + +static int s_test_ecc_p384_cert_import(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + return s_test_cert_key_import(allocator, "ecc-p384-cert.pem", "ecc-p384-key.pem"); +} + +AWS_TEST_CASE(test_ecc_p384_cert_import, s_test_ecc_p384_cert_import) + +static int s_test_ecc_p521_cert_import(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + return s_test_cert_key_import(allocator, "ecc-p521-cert.pem", "ecc-p521-key.pem"); +} + +AWS_TEST_CASE(test_ecc_p521_cert_import, s_test_ecc_p521_cert_import) static int s_test_pkcs8_import(struct aws_allocator *allocator, void *ctx) { (void)ctx; - (void)allocator; + return s_test_cert_key_import(allocator, "unittests.crt", "unittests.p8"); +} - aws_io_library_init(allocator); +AWS_TEST_CASE(test_pkcs8_import, s_test_pkcs8_import) - struct aws_byte_buf cert_buf; - struct aws_byte_buf key_buf; +static int s_test_pkcs12_import(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + (void)allocator; - ASSERT_SUCCESS(aws_byte_buf_init_from_file(&cert_buf, allocator, "unittests.crt")); - ASSERT_SUCCESS(aws_byte_buf_init_from_file(&key_buf, allocator, "unittests.p8")); + aws_io_library_init(allocator); - struct aws_byte_cursor cert_cur = aws_byte_cursor_from_buf(&cert_buf); - struct aws_byte_cursor key_cur = aws_byte_cursor_from_buf(&key_buf); + struct aws_byte_cursor pwd_cur = aws_byte_cursor_from_c_str("1234"); struct aws_tls_ctx_options tls_options = {0}; AWS_FATAL_ASSERT( - AWS_OP_SUCCESS == aws_tls_ctx_options_init_client_mtls(&tls_options, allocator, &cert_cur, &key_cur)); + AWS_OP_SUCCESS == + aws_tls_ctx_options_init_client_mtls_pkcs12_from_path(&tls_options, allocator, "unittests.p12", &pwd_cur)); /* import happens in here */ struct aws_tls_ctx *tls_context = aws_tls_client_ctx_new(allocator, &tls_options); @@ -2705,15 +2720,12 @@ static int s_test_pkcs8_import(struct aws_allocator *allocator, void *ctx) { aws_tls_ctx_options_clean_up(&tls_options); - aws_byte_buf_clean_up(&cert_buf); - aws_byte_buf_clean_up(&key_buf); - aws_io_library_clean_up(); return AWS_OP_SUCCESS; } -AWS_TEST_CASE(test_pkcs8_import, s_test_pkcs8_import) +AWS_TEST_CASE(test_pkcs12_import, s_test_pkcs12_import) static int s_test_tls_cipher_preference_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx;