Skip to content
Closed
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
2 changes: 2 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ disabled_rules:

opt_in_rules:
- empty_count
- vertical_parameter_alignment_on_call

# configurable rules can be customized from this configuration file
force_cast: warning
Expand Down Expand Up @@ -48,3 +49,4 @@ statement_position:
todo: warning
trailing_semicolon: error
vertical_parameter_alignment: error
vertical_parameter_alignment_on_call: true
25 changes: 19 additions & 6 deletions Source/AwsCommonRuntimeKit/crt/Utilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,29 @@ func withOptionalCString<Result>(

func withOptionalByteCursorPointerFromString<Result>(
_ arg1: String?,
_ body: (UnsafePointer<aws_byte_cursor>?) -> Result
) -> Result {
_ body: (UnsafePointer<aws_byte_cursor>?) throws -> Result
) rethrows -> Result {
guard let arg1 = arg1 else {
return body(nil)
return try body(nil)
}
return try arg1.withCString { arg1C in
try withUnsafePointer(to: aws_byte_cursor_from_c_str(arg1C)) { byteCursorPointer in
try body(byteCursorPointer)
}
}
return arg1.withCString { arg1C in
withUnsafePointer(to: aws_byte_cursor_from_c_str(arg1C)) { byteCursorPointer in
body(byteCursorPointer)
}

func withOptionalByteCursorPointerFromStrings<Result>(
_ arg1: String?,
_ arg2: String?,
_ body: (UnsafePointer<aws_byte_cursor>?, UnsafePointer<aws_byte_cursor>?) throws -> Result
) rethrows -> Result {
return try withOptionalByteCursorPointerFromString(arg1) { arg1C in
return try withOptionalByteCursorPointerFromString(arg2) { arg2C in
return try body(arg1C, arg2C)
}
}

}

func withByteCursorFromStrings<Result>(
Expand Down
88 changes: 79 additions & 9 deletions Source/AwsCommonRuntimeKit/io/TLSContextOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,55 @@ public class TLSContextOptions: CStruct {
TLSContextOptions()
}

public static func makeMtlsPkcs12FromPath(
path: String,
/// Initializes TLSContextOptions for mutual TLS (mTLS), with client certificate and private key in the PKCS#12 format.
///
/// NOTE: This only works on Apple devices.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@available directive?
Pros:

  • Compile time safety instead of runtime error

Cons:

  • Consumers of this API will have to do IF/ELSE at compile time, although they should probably do that anyhow if it is a cross-platform application.

///
/// - Parameters:
/// - pkcs12Path: Path to PKCS #12 file. The file is loaded from disk and stored internally. It must remain in
/// memory for the lifetime of the returned object.
/// - password: Password to PKCS #12 file. It must remain in memory for the lifetime of the returned object.
/// - Throws: CommonRuntimeError.crtError
public static func makeMTLS(
pkcs12Path: String,
password: String) throws -> TLSContextOptions {
try TLSContextOptions(mtlsPkcs12FromPath: path, password: password)
try TLSContextOptions(mtlsPkcs12FromPath: pkcs12Path, password: password)
}

/// Initializes TLSContextOptions for mutual TLS (mTLS), with client certificate and private key. These are in memory
/// buffers. These buffers must be in the PEM format.
///
/// NOTE: This is unsupported on iOS, tvOS, watchOS.
///
/// - Parameters:
/// - certificateData: Certificate contents in memory.
/// - privateKeyData: Private key contents in memory.
/// - Throws: CommonRuntimeError.crtError
@available(iOS, unavailable)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
public static func makeMTLS(
certificateData: String,
privateKeyData: String) throws -> TLSContextOptions {
try TLSContextOptions(certificateData: certificateData, privateKeyData: privateKeyData)
}

/// Initializes TLSContextOptions for mutual TLS (mTLS), with client certificate and private key. These are paths to a
/// file on disk. These files must be in the PEM format.
///
/// NOTE: This is unsupported on iOS, tvOS, watchOS.
///
/// - Parameters:
/// - certificatePath: Path to certificate file.
/// - privateKeyPath: Path to private key file.
/// - Throws: CommonRuntimeError.crtError
@available(iOS, unavailable)
@available(tvOS, unavailable)
@available(watchOS, unavailable)
public static func makeMTLS(
certificatePath: String,
privateKeyPath: String) throws -> TLSContextOptions {
try TLSContextOptions(certificatePath: certificatePath, privateKeyPath: privateKeyPath)
}

init() {
Expand All @@ -23,12 +68,37 @@ public class TLSContextOptions: CStruct {
init(mtlsPkcs12FromPath path: String,
password: String) throws {
self.rawValue = allocator.allocate(capacity: 1)
if (password.withByteCursorPointer { passwordCursorPointer in
aws_tls_ctx_options_init_client_mtls_pkcs12_from_path(rawValue,
allocator.rawValue,
path,
passwordCursorPointer)
}) != AWS_OP_SUCCESS {
guard password.withByteCursorPointer({ passwordCursorPointer in
return aws_tls_ctx_options_init_client_mtls_pkcs12_from_path(rawValue,
allocator.rawValue,
path,
passwordCursorPointer)
}) == AWS_OP_SUCCESS else {
throw CommonRunTimeError.crtError(CRTError.makeFromLastError())
}
}

init(certificateData cert_data: String,
privateKeyData private_key_data: String) throws {
self.rawValue = allocator.allocate(capacity: 1)
guard withOptionalByteCursorPointerFromStrings(
cert_data, private_key_data, {certificateByteCursor, privatekeyByteCursor in
return aws_tls_ctx_options_init_client_mtls(self.rawValue,
allocator.rawValue,
certificateByteCursor,
privatekeyByteCursor)
}) == AWS_OP_SUCCESS else {
throw CommonRunTimeError.crtError(CRTError.makeFromLastError())
}
}

init(certificatePath cert_path: String,
privateKeyPath private_path: String) throws {
self.rawValue = allocator.allocate(capacity: 1)
guard aws_tls_ctx_options_init_client_mtls_from_path(self.rawValue,
allocator.rawValue,
cert_path,
private_path) == AWS_OP_SUCCESS else {
throw CommonRunTimeError.crtError(CRTError.makeFromLastError())
}
}
Expand Down
15 changes: 15 additions & 0 deletions Test/AwsCommonRuntimeKitTests/io/TLSContextTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,19 @@ class TLSContextTests: XCBaseTestCase {
let context = try TLSContext(options: options, mode: .client)
_ = TLSConnectionOptions(context: context)
}

// TODO: The test is disabled as the github CI failed on access default keychain.
// TODO: Add test for testCreateTlsContextWithRawData()
func testCreateTlsContextWithFilePath() throws{
try skipIfiOS()
try skipIftvOS()
try skipIfwatchOS()

let cert_path = try getEnvironmentVarOrSkipTest(environmentVarName: "AWS_TEST_MQTT311_IOT_CORE_X509_CERT")
let private_key_path = try getEnvironmentVarOrSkipTest(environmentVarName: "AWS_TEST_MQTT311_IOT_CORE_X509_KEY")
let options = try TLSContextOptions.makeMTLS(certificatePath: cert_path, privateKeyPath: private_key_path)

let context = try TLSContext(options: options, mode: .client)
_ = TLSConnectionOptions(context: context)
}
}