Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSX ECC key support #524

Merged
merged 18 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from 5 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 include/aws/io/private/pki_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ AWS_IO_API int aws_read_and_decode_pem_file_to_buffer_list(
const char *filename,
struct aws_array_list *cert_chain_or_key);

/**
* Decodes a PEM file to DER format and adds the results to 'der_buffer' if successful.
* Otherwise, 'der_buffer' will be empty. This code is slow, and it allocates, so please try
* not to call this in the middle of something that needs to be fast or resource sensitive.
*/
int aws_decode_pem_to_der_buf(
struct aws_allocator *alloc,
const struct aws_byte_cursor *pem_cursor,
struct aws_byte_buf *der_buffer);

#ifdef AWS_OS_APPLE
# if !defined(AWS_OS_IOS)
/**
Expand Down
39 changes: 35 additions & 4 deletions source/darwin/darwin_pki_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,40 @@ int aws_import_public_and_private_keys_to_identity(
}

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 (key_status == errSecUnknownFormat) { // If the format is unknow, try ecc
AWS_LOGF_TRACE(AWS_LS_IO_PKI, "static: error reading private key format, try ECC key format.");
struct aws_byte_buf der_buffer;
AWS_ZERO_STRUCT(der_buffer);

/* Decode PEM format file to DER format */
if (aws_decode_pem_to_der_buf(alloc, private_key, &der_buffer) == AWS_OP_ERR) {
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Failed to decode PEM private key to DER format.");
result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
goto done;
}
AWS_ASSERT(der_buffer);

key_data = CFDataCreate(cf_alloc, der_buffer.buffer, der_buffer.len);

format = kSecFormatOpenSSL;
item_type = kSecItemTypePrivateKey;
key_status = SecItemImport(
key_data, NULL, &format, &item_type, 0, &import_params, import_keychain, &key_import_output);

/* Clean up key buffer */
aws_byte_buf_clean_up(&der_buffer);
CFRelease(key_data);

if (key_status != errSecSuccess && key_status != errSecDuplicateItem) {
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: error importing ECC key with OSStatus %d", (int)key_status);
result = aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
goto done;
}
} else {
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. */
Expand Down Expand Up @@ -180,7 +211,7 @@ int aws_import_public_and_private_keys_to_identity(
return result;
}

#endif /* AWS_OS_IOS */
#endif /* !AWS_OS_IOS */

int aws_import_pkcs12_to_identity(
CFAllocatorRef cf_alloc,
Expand Down
73 changes: 73 additions & 0 deletions source/pki_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,76 @@ int aws_read_and_decode_pem_file_to_buffer_list(

return AWS_OP_SUCCESS;
}

int aws_decode_pem_to_der_buf(
struct aws_allocator *alloc,
const struct aws_byte_cursor *pem_cursor,
struct aws_byte_buf *der_buf) {
aws_byte_buf_clean_up(der_buf);
struct aws_array_list base_64_buffer_list;

if (aws_array_list_init_dynamic(&base_64_buffer_list, alloc, 2, sizeof(struct aws_byte_buf))) {
return AWS_OP_ERR;
}

int err_code = AWS_OP_ERR;

if (s_convert_pem_to_raw_base64(alloc, pem_cursor, &base_64_buffer_list)) {
goto cleanup_base64_buffer_list;
}

for (size_t i = 0; i < aws_array_list_length(&base_64_buffer_list); ++i) {
size_t decoded_len = 0;
struct aws_byte_buf *byte_buf_ptr = NULL;
aws_array_list_get_at_ptr(&base_64_buffer_list, (void **)&byte_buf_ptr, i);
struct aws_byte_cursor byte_cur = aws_byte_cursor_from_buf(byte_buf_ptr);

if (aws_base64_compute_decoded_len(&byte_cur, &decoded_len)) {
aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
goto cleanup_output_due_to_error;
}

struct aws_byte_buf decoded_buffer;

if (aws_byte_buf_init(&decoded_buffer, alloc, decoded_len)) {
goto cleanup_output_due_to_error;
}

if (aws_base64_decode(&byte_cur, &decoded_buffer)) {
aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
aws_byte_buf_clean_up(&decoded_buffer);
goto cleanup_output_due_to_error;
}

struct aws_byte_cursor decoded_cursor = aws_byte_cursor_from_buf(&decoded_buffer);

if (der_buf->len == 0) {
if (aws_byte_buf_init_copy(der_buf, alloc, &decoded_buffer) == AWS_OP_ERR) {
aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
aws_byte_buf_clean_up(&decoded_buffer);
goto cleanup_output_due_to_error;
}
} else if (aws_byte_buf_append_dynamic_secure(der_buf, &decoded_cursor) == AWS_OP_ERR) {
aws_raise_error(AWS_IO_FILE_VALIDATION_FAILURE);
aws_byte_buf_clean_up(&decoded_buffer);
goto cleanup_output_due_to_error;
}
aws_byte_buf_clean_up(&decoded_buffer);
}

err_code = AWS_OP_SUCCESS;

cleanup_base64_buffer_list:
aws_cert_chain_clean_up(&base_64_buffer_list);
aws_array_list_clean_up(&base_64_buffer_list);

return err_code;

cleanup_output_due_to_error:
AWS_LOGF_ERROR(AWS_LS_IO_PKI, "static: Invalid PEM buffer.");
aws_cert_chain_clean_up(&base_64_buffer_list);
aws_array_list_clean_up(&base_64_buffer_list);
aws_byte_buf_clean_up(der_buf);

return AWS_OP_ERR;
}