Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ foreach(EVENT_LOOP_DEFINE IN LISTS EVENT_LOOP_DEFINES)
target_compile_definitions(${PROJECT_NAME} PUBLIC "-DAWS_ENABLE_${EVENT_LOOP_DEFINE}")
endforeach()

if (AWS_USE_SECITEM)
target_compile_definitions(${PROJECT_NAME} PUBLIC "-DAWS_USE_SECITEM")
endif()

# Darwin means Apple stationary operating systems, e.g. macOS
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# Target file-based keychain on macOS.
target_compile_definitions(${PROJECT_NAME} PUBLIC "-DAWS_SECITEM_FILEBASED_KEYCHAIN")
endif()

if (BYO_CRYPTO)
target_compile_definitions(${PROJECT_NAME} PUBLIC "-DBYO_CRYPTO")
endif()
Expand Down
112 changes: 97 additions & 15 deletions source/darwin/darwin_pki_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ static int s_aws_secitem_add_certificate_to_keychain(
CFDictionaryAddValue(add_attributes, kSecAttrSerialNumber, serial_data);
CFDictionaryAddValue(add_attributes, kSecAttrLabel, label);
CFDictionaryAddValue(add_attributes, kSecValueRef, cert_ref);
#ifdef AWS_SECITEM_FILEBASED_KEYCHAIN
/* Target file-based keychain instead of data protection keychain. */
CFDictionaryAddValue(add_attributes, kSecUseDataProtectionKeychain, kCFBooleanFalse);
#endif

// Initial attempt to add certificate to keychain.
status = SecItemAdd(add_attributes, NULL);
Expand All @@ -391,8 +395,9 @@ static int s_aws_secitem_add_certificate_to_keychain(
case errSecMissingEntitlement:
AWS_LOGF_ERROR(
AWS_LS_IO_PKI,
"SecItemAdd certificate failed with OSStatus %d : errSecMissingEntitlement. The process
attempting " "to access the keychain is missing the necessary entitlements.", (int)status);
"SecItemAdd certificate failed with OSStatus %d : errSecMissingEntitlement. The process "
"attempting to access the keychain is missing the necessary entitlements.",
(int)status);
break;
default:
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "SecItemAdd certificate failed with OSStatus %d", (int)status);
Expand Down Expand Up @@ -435,6 +440,10 @@ static int s_aws_secitem_add_certificate_to_keychain(
CFDictionaryCreateMutable(cf_alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(delete_query, kSecClass, kSecClassCertificate);
CFDictionaryAddValue(delete_query, kSecAttrSerialNumber, serial_data);
#ifdef AWS_SECITEM_FILEBASED_KEYCHAIN
/* Target file-based keychain instead of data protection keychain. */
CFDictionaryAddValue(delete_query, kSecUseDataProtectionKeychain, kCFBooleanFalse);
#endif

// delete the existing certificate from keychain
status = SecItemDelete(delete_query);
Expand Down Expand Up @@ -483,6 +492,10 @@ static int s_aws_secitem_add_private_key_to_keychain(
CFDictionaryAddValue(add_attributes, kSecAttrApplicationLabel, application_label);
CFDictionaryAddValue(add_attributes, kSecAttrLabel, label);
CFDictionaryAddValue(add_attributes, kSecValueRef, key_ref);
#ifdef AWS_SECITEM_FILEBASED_KEYCHAIN
/* Target file-based keychain instead of data protection keychain. */
CFDictionaryAddValue(add_attributes, kSecUseDataProtectionKeychain, kCFBooleanFalse);
#endif

// Initial attempt to add private key to keychain.
status = SecItemAdd(add_attributes, NULL);
Expand Down Expand Up @@ -535,7 +548,10 @@ static int s_aws_secitem_add_private_key_to_keychain(
CFDictionaryAddValue(delete_query, kSecClass, kSecClassKey);
CFDictionaryAddValue(delete_query, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
CFDictionaryAddValue(delete_query, kSecAttrApplicationLabel, application_label);

#ifdef AWS_SECITEM_FILEBASED_KEYCHAIN
/* Target file-based keychain instead of data protection keychain. */
CFDictionaryAddValue(delete_query, kSecUseDataProtectionKeychain, kCFBooleanFalse);
#endif
// delete the existing private key from keychain
status = SecItemDelete(delete_query);
if (status != errSecSuccess) {
Expand Down Expand Up @@ -565,12 +581,23 @@ static int s_aws_secitem_add_private_key_to_keychain(
return result;
}

static int s_aws_secitem_get_identity(CFAllocatorRef cf_alloc, CFDataRef serial_data, sec_identity_t *out_identity) {
static bool s_compare_serial_numbers(CFDataRef a, CFDataRef b) {
return CFDataGetLength(a) == CFDataGetLength(b) &&
memcmp(CFDataGetBytePtr(a), CFDataGetBytePtr(b), CFDataGetLength(a)) == 0;
}

static int s_aws_secitem_get_identity(
CFAllocatorRef cf_alloc,
CFDataRef serial_data,
SecCertificateRef cert_ref,
sec_identity_t *out_identity) {

(void)cert_ref;

int result = AWS_OP_ERR;
OSStatus status;
CFMutableDictionaryRef search_query = NULL;
SecIdentityRef sec_identity_ref = NULL;
CFArrayRef sec_identity_array = NULL;

/*
* SecItem identity is created when a certificate matches a private key in the keychain.
Expand All @@ -584,34 +611,84 @@ static int s_aws_secitem_get_identity(CFAllocatorRef cf_alloc, CFDataRef serial_
CFDictionaryAddValue(search_query, kSecAttrSerialNumber, serial_data);
CFDictionaryAddValue(search_query, kSecReturnRef, kCFBooleanTrue);

#ifdef AWS_SECITEM_FILEBASED_KEYCHAIN
/* Target file-based keychain instead of data protection keychain. */
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);
CFDictionaryAddValue(search_query, kSecMatchItemList, cert_filter);
#endif

/* Though the kSecAttrSerialNumber and kSecMatchItemList attributes filter out unmatching identities, request
* SecItemCopyMatching to return all results. */
CFDictionaryAddValue(search_query, kSecMatchLimit, kSecMatchLimitAll);

/*
* Copied or created CF items must have CFRelease called on them or you leak memory. This identity needs to
* have CFRelease called on it at some point or it will leak.
*/
status = SecItemCopyMatching(search_query, (CFTypeRef *)&sec_identity_ref);
status = SecItemCopyMatching(search_query, (CFTypeRef *)&sec_identity_array);

if (status != errSecSuccess) {
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "SecItemCopyMatching identity failed with OSStatus %d", (int)status);
aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
goto done;
}

*out_identity = sec_identity_create(sec_identity_ref);
if (*out_identity == NULL) {
AWS_LOGF_ERROR(
AWS_LS_IO_PKI, "sec_identity_create failed to create a sec_identity_t from provided SecIdentityRef.");
aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
goto done;
CFIndex identity_num = CFArrayGetCount(sec_identity_array);
AWS_LOGF_DEBUG(AWS_LS_IO_PKI, "Found %d identities", (int)identity_num);

/* With the kSecAttrSerialNumber and kSecMatchItemList filters in place, the following additional serial number
* verification is NOT needed. However, I'll keep it for now to be on the safe side.
* NOTE: Only public data is processed here, i.e. any logged user already has access to it without any additional
* verification. */
for (CFIndex i = 0; i < identity_num; i++) {
/* The identity object here is basically two references: one to a cert and one to a private key. No actual
* data is retrieved from keychain yet. We can use special functions provided by Security framework to get them:
* SecIdentityCopyCertificate and SecIdentityCopyPrivateKey.
* Certificate data can be obtained with no additional verification since it's public. Private key data can be
* obtained only if the user has access to the keychain (i.e. either provide password into keychain's popup or
* add the app to a list of allowed in the Keychain Access app).
* Since we process only certificate here, no user verification will be requested in this function. Later,
* during establishing a TLS connection, user verification will be done when Network framework will try to
* retrieve private key data from the provided identity. That part remains the same. */
const SecIdentityRef sec_identity_ref = (const SecIdentityRef)CFArrayGetValueAtIndex(sec_identity_array, i);

SecCertificateRef found_cert = NULL;
OSStatus copy_cert_status = SecIdentityCopyCertificate(sec_identity_ref, &found_cert);
if (copy_cert_status != errSecSuccess) {
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "SecIdentityCopyCertificate failed with OSStatus %d", (int)copy_cert_status);
aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
goto done;
}

CFDataRef found_cert_serial_data = SecCertificateCopySerialNumberData(found_cert, NULL);

if (s_compare_serial_numbers(serial_data, found_cert_serial_data)) {
AWS_LOGF_TRACE(AWS_LS_IO_PKI, "Found a matching identity");
*out_identity = sec_identity_create(sec_identity_ref);
if (*out_identity == NULL) {
AWS_LOGF_ERROR(
AWS_LS_IO_PKI,
"sec_identity_create failed to create a sec_identity_t from provided SecIdentityRef.");
aws_raise_error(AWS_ERROR_SYS_CALL_FAILURE);
goto done;
}

break;
}
}

AWS_LOGF_INFO(AWS_LS_IO_PKI, "static: Successfully retrieved identity from keychain.");

result = AWS_OP_SUCCESS;

done:
// cleanup
aws_cf_release(search_query);
aws_cf_release(sec_identity_ref);
aws_cf_release(cert_filter);
// TODO Release elements.
aws_cf_release(sec_identity_array);

return result;
}
Expand Down Expand Up @@ -774,6 +851,11 @@ int aws_secitem_import_cert_and_key(
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
Expand Down Expand Up @@ -807,7 +889,7 @@ int aws_secitem_import_cert_and_key(
goto done;
}

if (s_aws_secitem_get_identity(cf_alloc, cert_serial_data, secitem_identity)) {
if (s_aws_secitem_get_identity(cf_alloc, cert_serial_data, cert_ref, secitem_identity)) {
goto done;
}

Expand Down
2 changes: 1 addition & 1 deletion source/s2n/s2n_tls_channel_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -1539,7 +1539,7 @@ static struct aws_tls_ctx *s_tls_ctx_new(
security_policy = "AWS-CRT-SDK-TLSv1.2-2025";
break;
case AWS_IO_TLS_CIPHER_PREF_TLSV1_0_2023_06:
security_policy = "AWS-CRT-SDK-TLSv1.2-2025";
security_policy = "AWS-CRT-SDK-TLSv1.0-2023";
break;
default:
AWS_LOGF_ERROR(AWS_LS_IO_TLS, "Unrecognized TLS Cipher Preference: %d", options->cipher_pref);
Expand Down
Loading