Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Introduce SSL .p12 certificate import API
  • Loading branch information
viktorasl committed Aug 2, 2019
commit c3923e340a64114ca4fc308973005826d34b94f7
9 changes: 9 additions & 0 deletions XCode/Sources/Errno.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,13 @@ public class Errno {
// https://forums.developer.apple.com/thread/113919
return String(cString: strerror(errno))
}

#if !os(Linux)
public class func sslError(from status: OSStatus) -> Error {
guard let msg = SecCopyErrorMessageString(status, nil) else {
return SocketError.tlsSessionFailed("<\(status): message is not provided>")
}
return SocketError.tlsSessionFailed(msg as NSString as String)
}
#endif
Comment on lines +17 to +35
Copy link
Copy Markdown
Member

@Vkt0r Vkt0r Nov 5, 2019

Choose a reason for hiding this comment

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

SecCopyErrorMessageString is only available for iOS 11.3+ and we're supporting iOS 8+, not sure which is the counterpart here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch! Very weird that Xcode didn't notify me.
Could not find any counterpart so formed custom message.

}
3 changes: 3 additions & 0 deletions XCode/Sources/Socket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import Foundation
public enum SocketError: Error {
case socketCreationFailed(String)
case socketSettingReUseAddrFailed(String)
#if !os(Linux)
case tlsSessionFailed(String)
#endif
case bindFailed(String)
case listenFailed(String)
case writeFailed(String)
Expand Down
30 changes: 30 additions & 0 deletions XCode/Sources/TlsSession.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// HttpRouter.swift
// Swifter
//
// Copyright (c) 2014-2016 Damian Kołakowski. All rights reserved.
//

import Foundation

#if !os(Linux)
private func ensureNoErr(_ status: OSStatus) throws {
guard status == noErr else {
throw Errno.sslError(from: status)
}
}

public enum TLS {
public static func loadP12Certificate(_ _data: Data, _ password: String) throws -> CFArray {
let data = _data as NSData
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SecPKCS12Import(_:_:_:) is expecting a CFData here, what would be the difference between passing an NSData object instead of a CFData here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Most of Core Foundation types are toll-free bridged to their Cocoa Foundation counterparts and can be used interchangeably. But I agree, it's better to use actually expected type names here. Will fix that.

/cc https://developer.apple.com/documentation/corefoundation/cfdata-rv9
/cc https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Toll-FreeBridgin/Toll-FreeBridgin.html

let options = [kSecImportExportPassphrase: password]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the way Apple does it in the page you referenced they make a cast to String like this:

 let options = [ kSecImportExportPassphrase as String: password ]

Do we know what difference could generate?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Related to same discussion of String, NSString and CFString I believe they're all the same.

var items: CFArray!
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we please make this CFArray optional in order to avoid the force-unwrapping in the next lines?

try ensureNoErr(SecPKCS12Import(data, options as NSDictionary, &items))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

SecPKCS12Import(_:_:_:) is expecting a UnsafeMutablePointer<CFArray?>What are differences here between pass a UnsafeMutablePointer<CFArray?> and raw CFArray?. Wouldn't be more type-safe to pass a type like this:

try ensureNoErr(withUnsafeMutablePointer(to: &items) { SecPKCS12Import(data, options as CFDictionary, $0) })

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not fully sure what &items generates but I assume it's something like a pointer to CFArray. Used https://developer.apple.com/documentation/security/certificate_key_and_trust_services/identities/importing_an_identity as an example.

let dictionary = (items! as [AnyObject])[0]
Copy link
Copy Markdown
Member

@Vkt0r Vkt0r Aug 22, 2019

Choose a reason for hiding this comment

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

The items would be of [[String: Any]], can we please cast it as optional and use firsts instead of the first index directly? Something like this maybe:

guard let dictionary = (items as? [[String: Any]]).first else { throw SomeError }

let secIdentity = dictionary[kSecImportItemIdentity] as! SecIdentity
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here we can please avoid the force-unwrapping even when Apple does in his examples?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Well, this cannot quite be avoided, because when trying to make it optional compiler produces
Conditional downcast to CoreFoundation type 'SecIdentity' will always succeed error

let chain = dictionary[kSecImportItemCertChain] as! [SecCertificate]
let certs = [secIdentity] + chain.dropFirst().map { $0 as Any }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Any reason we are dropping the first SecCertificate ?

Copy link
Copy Markdown
Author

@viktorasl viktorasl Aug 23, 2019

Choose a reason for hiding this comment

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

from SSLSetCertificate:

You must place in certRefs[0] a SecIdentityRef object that identifies the leaf certificate and its corresponding private key

so secIdentity already identifies leaf certificate so that leaf certificate should be dropped from chain otherwise chain becomes invalid due to duplicate certs.

return certs as CFArray
}
}
#endif
8 changes: 8 additions & 0 deletions XCode/Swifter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
objects = {

/* Begin PBXBuildFile section */
039CE04222F32BA600C9788F /* TlsSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE04122F32BA600C9788F /* TlsSession.swift */; };
039CE04322F32BA600C9788F /* TlsSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE04122F32BA600C9788F /* TlsSession.swift */; };
039CE04422F32BA600C9788F /* TlsSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 039CE04122F32BA600C9788F /* TlsSession.swift */; };
043660C721FED34100497989 /* Swifter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AE893FB1C0512C400A29F63 /* Swifter.framework */; };
043660CD21FED35200497989 /* SwifterTestsHttpRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCB8C5F1D97B8CC008B9712 /* SwifterTestsHttpRouter.swift */; };
043660CE21FED35500497989 /* SwifterTestsHttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CCD876D1C660B250068099B /* SwifterTestsHttpParser.swift */; };
Expand Down Expand Up @@ -164,6 +167,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
039CE04122F32BA600C9788F /* TlsSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TlsSession.swift; sourceTree = "<group>"; };
043660C221FED34100497989 /* SwiftermacOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftermacOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
043660C621FED34100497989 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
043660DA21FED3A300497989 /* SwiftertvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftertvOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -353,6 +357,7 @@
7C76B2A11D369C9D00D35BFB /* Errno.swift */,
7C377E161D964B6A009C6148 /* String+File.swift */,
6AE2FF702048011A00302EC4 /* MimeTypes.swift */,
039CE04122F32BA600C9788F /* TlsSession.swift */,
);
path = Sources;
sourceTree = "<group>";
Expand Down Expand Up @@ -818,6 +823,7 @@
269B47901D3AAAE20042D137 /* DemoServer.swift in Sources */,
269B47921D3AAAE20042D137 /* Socket+File.swift in Sources */,
269B47931D3AAAE20042D137 /* Socket.swift in Sources */,
039CE04422F32BA600C9788F /* TlsSession.swift in Sources */,
269B47941D3AAAE20042D137 /* HttpServerIO.swift in Sources */,
269B47951D3AAAE20042D137 /* Files.swift in Sources */,
2659FC1A1DADC077003F3930 /* String+File.swift in Sources */,
Expand Down Expand Up @@ -845,6 +851,7 @@
7C76B7151D2C45760030FC98 /* HttpRequest.swift in Sources */,
7C76B70D1D2C456A0030FC98 /* DemoServer.swift in Sources */,
7C76B29F1D369BEC00D35BFB /* Socket+File.swift in Sources */,
039CE04222F32BA600C9788F /* TlsSession.swift in Sources */,
7C76B7231D2C45890030FC98 /* Socket.swift in Sources */,
7C76B71D1D2C45820030FC98 /* HttpServerIO.swift in Sources */,
7C76B7111D2C45710030FC98 /* Files.swift in Sources */,
Expand Down Expand Up @@ -872,6 +879,7 @@
7C76B7161D2C45760030FC98 /* HttpRequest.swift in Sources */,
7C76B70E1D2C456B0030FC98 /* DemoServer.swift in Sources */,
7C76B2A01D369BEC00D35BFB /* Socket+File.swift in Sources */,
039CE04322F32BA600C9788F /* TlsSession.swift in Sources */,
7C76B7241D2C458A0030FC98 /* Socket.swift in Sources */,
7C76B71E1D2C45820030FC98 /* HttpServerIO.swift in Sources */,
7C76B7121D2C45710030FC98 /* Files.swift in Sources */,
Expand Down