merge development

This commit is contained in:
Erik 2016-06-09 09:37:26 -04:00
commit 523ced2f9a
6 changed files with 321 additions and 268 deletions

View File

@ -101,11 +101,6 @@
74171EC51C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171EC71C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E601C10CD240062D398 /* SocketTypes.swift */; };
74171ECF1C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
74171ED01C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
74171ED11C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
74171ED31C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
74171ED41C10CD240062D398 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74171E621C10CD240062D398 /* WebSocket.swift */; };
741F39EE1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; };
741F39EF1BD025D80026C9CC /* SocketEngineTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */; };
7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */; };
@ -121,6 +116,12 @@
7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; };
7472C6601BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */; };
74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */; };
74B4AD1D1D09A5D80062A523 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */; };
74B4AD211D09A6190062A523 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD201D09A6190062A523 /* SSLSecurity.swift */; };
74B4AD221D09A6190062A523 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD201D09A6190062A523 /* SSLSecurity.swift */; };
74B4AD231D09A6190062A523 /* SSLSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD201D09A6190062A523 /* SSLSecurity.swift */; };
74B4AD241D09A6450062A523 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */; };
74B4AD251D09A6490062A523 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74B4AD1C1D09A5D80062A523 /* WebSocket.swift */; };
74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; };
74F124F11BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; };
CEBA56961CDA0B7700BA0389 /* NSCharacterSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBA56951CDA0B7700BA0389 /* NSCharacterSet.swift */; };
@ -184,7 +185,6 @@
74171E5E1C10CD240062D398 /* SocketParsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketParsable.swift; path = Source/SocketParsable.swift; sourceTree = "<group>"; };
74171E5F1C10CD240062D398 /* SocketStringReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketStringReader.swift; path = Source/SocketStringReader.swift; sourceTree = "<group>"; };
74171E601C10CD240062D398 /* SocketTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketTypes.swift; path = Source/SocketTypes.swift; sourceTree = "<group>"; };
74171E621C10CD240062D398 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = "<group>"; };
741F39ED1BD025D80026C9CC /* SocketEngineTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketEngineTest.swift; sourceTree = "<group>"; };
7420CB781C49629E00956AA4 /* SocketEnginePollable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketEnginePollable.swift; path = Source/SocketEnginePollable.swift; sourceTree = "<group>"; };
742D150B1CA5794B00BD987D /* SocketObjectiveCTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SocketObjectiveCTest.m; sourceTree = "<group>"; };
@ -193,6 +193,8 @@
7472C65B1BCAB53E003CA70D /* SocketNamespacePacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketNamespacePacketTest.swift; sourceTree = "<group>"; };
7472C65E1BCAC46E003CA70D /* SocketSideEffectTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketSideEffectTest.swift; sourceTree = "<group>"; };
74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientSpec.swift; path = Source/SocketIOClientSpec.swift; sourceTree = "<group>"; };
74B4AD1C1D09A5D80062A523 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket/WebSocket.swift; sourceTree = "<group>"; };
74B4AD201D09A6190062A523 /* SSLSecurity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SSLSecurity.swift; path = Source/WebSocket/SSLSecurity.swift; sourceTree = "<group>"; };
74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketBasicPacketTest.swift; sourceTree = "<group>"; };
CEBA56951CDA0B7700BA0389 /* NSCharacterSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSCharacterSet.swift; path = Source/NSCharacterSet.swift; sourceTree = "<group>"; };
CEBA56991CDA0B8200BA0389 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = String.swift; path = Source/String.swift; sourceTree = "<group>"; };
@ -331,7 +333,6 @@
5764DF7B1B51F24A004FF46E /* Source */ = {
isa = PBXGroup;
children = (
CEBA569E1CDA0C0C00BA0389 /* utils */,
74171E501C10CD240062D398 /* SocketAckEmitter.swift */,
74171E511C10CD240062D398 /* SocketAckManager.swift */,
74171E521C10CD240062D398 /* SocketAnyEvent.swift */,
@ -351,11 +352,21 @@
74171E5E1C10CD240062D398 /* SocketParsable.swift */,
74171E5F1C10CD240062D398 /* SocketStringReader.swift */,
74171E601C10CD240062D398 /* SocketTypes.swift */,
74171E621C10CD240062D398 /* WebSocket.swift */,
CEBA569E1CDA0C0C00BA0389 /* utils */,
74B4AD1B1D09A5C30062A523 /* Websocket */,
);
name = Source;
sourceTree = "<group>";
};
74B4AD1B1D09A5C30062A523 /* Websocket */ = {
isa = PBXGroup;
children = (
74B4AD1C1D09A5D80062A523 /* WebSocket.swift */,
74B4AD201D09A6190062A523 /* SSLSecurity.swift */,
);
name = Websocket;
sourceTree = "<group>";
};
CEBA569E1CDA0C0C00BA0389 /* utils */ = {
isa = PBXGroup;
children = (
@ -601,6 +612,7 @@
740CA1221C496EF700CB98F4 /* SocketEngineWebsocket.swift in Sources */,
74171EA51C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
CEBA569A1CDA0B8200BA0389 /* String.swift in Sources */,
74B4AD1D1D09A5D80062A523 /* WebSocket.swift in Sources */,
74171E751C10CD240062D398 /* SocketEngine.swift in Sources */,
74171E691C10CD240062D398 /* SocketAckManager.swift in Sources */,
7420CB791C49629E00956AA4 /* SocketEnginePollable.swift in Sources */,
@ -608,13 +620,13 @@
74ABF7771C3991C10078C657 /* SocketIOClientSpec.swift in Sources */,
74171E871C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171E631C10CD240062D398 /* SocketAckEmitter.swift in Sources */,
74B4AD211D09A6190062A523 /* SSLSecurity.swift in Sources */,
74171EBD1C10CD240062D398 /* SocketStringReader.swift in Sources */,
74171EC31C10CD240062D398 /* SocketTypes.swift in Sources */,
74171EAB1C10CD240062D398 /* SocketLogger.swift in Sources */,
74171E991C10CD240062D398 /* SocketIOClient.swift in Sources */,
74171E8D1C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7B1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171ECF1C10CD240062D398 /* WebSocket.swift in Sources */,
74171EB11C10CD240062D398 /* SocketPacket.swift in Sources */,
74171EB71C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E811C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
@ -630,7 +642,6 @@
7472C65F1BCAC46E003CA70D /* SocketSideEffectTest.swift in Sources */,
74171EA61C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
74171E881C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171ED01C10CD240062D398 /* WebSocket.swift in Sources */,
74171EA01C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74171E701C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EC41C10CD240062D398 /* SocketTypes.swift in Sources */,
@ -655,6 +666,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74B4AD241D09A6450062A523 /* WebSocket.swift in Sources */,
740CA1211C496EF200CB98F4 /* SocketEngineWebsocket.swift in Sources */,
7471CCEA1C39926300364B59 /* SocketIOClientSpec.swift in Sources */,
CEBA569B1CDA0B8200BA0389 /* String.swift in Sources */,
@ -671,7 +683,7 @@
74171E9B1C10CD240062D398 /* SocketIOClient.swift in Sources */,
74171E8F1C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7D1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171ED11C10CD240062D398 /* WebSocket.swift in Sources */,
74B4AD221D09A6190062A523 /* SSLSecurity.swift in Sources */,
74171EB31C10CD240062D398 /* SocketPacket.swift in Sources */,
74171EB91C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E831C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
@ -699,6 +711,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74B4AD251D09A6490062A523 /* WebSocket.swift in Sources */,
740CA1201C496EEB00CB98F4 /* SocketEngineWebsocket.swift in Sources */,
7471CCEB1C39926C00364B59 /* SocketIOClientSpec.swift in Sources */,
CEBA569C1CDA0B8200BA0389 /* String.swift in Sources */,
@ -715,7 +728,7 @@
74171E9D1C10CD240062D398 /* SocketIOClient.swift in Sources */,
74171E911C10CD240062D398 /* SocketEventHandler.swift in Sources */,
74171E7F1C10CD240062D398 /* SocketEngineClient.swift in Sources */,
74171ED31C10CD240062D398 /* WebSocket.swift in Sources */,
74B4AD231D09A6190062A523 /* SSLSecurity.swift in Sources */,
74171EB51C10CD240062D398 /* SocketPacket.swift in Sources */,
74171EBB1C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E851C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
@ -731,7 +744,6 @@
57634A231BD9B46D00E19CD7 /* SocketSideEffectTest.swift in Sources */,
74171EAA1C10CD240062D398 /* SocketIOClientStatus.swift in Sources */,
74171E8C1C10CD240062D398 /* SocketEngineSpec.swift in Sources */,
74171ED41C10CD240062D398 /* WebSocket.swift in Sources */,
74171EA41C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
74171E741C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
74171EC81C10CD240062D398 /* SocketTypes.swift in Sources */,

View File

@ -13,7 +13,7 @@ class SocketParserTest: XCTestCase {
let testSocket = SocketIOClient(socketURL: NSURL())
//Format key: message; namespace-data-binary-id
static let packetTypes: Dictionary<String, (String, [AnyObject], [NSData], Int)> = [
static let packetTypes: [String: (String, [AnyObject], [NSData], Int)] = [
"0": ("/", [], [], -1), "1": ("/", [], [], -1),
"25[\"test\"]": ("/", ["test"], [], 5),
"2[\"test\",\"~~0\"]": ("/", ["test", "~~0"], [], -1),

View File

@ -163,6 +163,16 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
}
}
private func closeOutEngine() {
sid = ""
closed = true
invalidated = true
connected = false
ws?.disconnect()
stopPolling()
}
/// Starts the connection to the server
public func connect() {
if connected {
@ -266,35 +276,32 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
}
public func disconnect(reason: String) {
func postSendClose(_ data: NSData?, _ res: NSURLResponse?, _ err: NSError?) {
sid = ""
closed = true
invalidated = true
connected = false
ws?.disconnect()
stopPolling()
client?.engineDidClose(reason: reason)
}
guard connected else { return closeOutEngine() }
DefaultSocketLogger.Logger.log("Engine is being closed.", type: logType)
if closed {
closeOutEngine()
client?.engineDidClose(reason: reason)
return
}
if websocket {
sendWebSocketMessage("", withType: .close, withData: [])
postSendClose(nil, nil, nil)
closeOutEngine()
} else {
// We need to take special care when we're polling that we send it ASAP
// Also make sure we're on the emitQueue since we're touching postWait
dispatch_sync(emitQueue) {
self.postWait.append(String(SocketEnginePacketType.close.rawValue))
let req = self.createRequestForPostWithPostWait()
self.doRequest(for: req, callbackWith: postSendClose)
}
disconnectPolling()
}
}
// We need to take special care when we're polling that we send it ASAP
// Also make sure we're on the emitQueue since we're touching postWait
private func disconnectPolling() {
dispatch_sync(emitQueue) {
self.postWait.append(String(SocketEnginePacketType.close.rawValue))
let req = self.createRequestForPostWithPostWait()
self.doRequest(for: req) {_, _, _ in }
self.closeOutEngine()
}
}
@ -334,7 +341,7 @@ public final class SocketEngine : NSObject, SocketEnginePollable, SocketEngineWe
guard let ws = self.ws else { return }
for msg in postWait {
ws.writeString(str: fixDoubleUTF8(string: msg))
ws.writeString(str: msg)
}
postWait.removeAll(keepingCapacity: true)

View File

@ -0,0 +1,258 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// SSLSecurity.swift
// Starscream
//
// Created by Dalton Cherry on 5/16/15.
// Copyright (c) 2014-2015 Dalton Cherry.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation
import Security
public class SSLCert {
var certData: NSData?
var key: SecKey?
/**
Designated init for certificates
- parameter data: is the binary data of the certificate
- returns: a representation security object to be used with
*/
public init(data: NSData) {
self.certData = data
}
/**
Designated init for public keys
- parameter key: is the public key to be used
- returns: a representation security object to be used with
*/
public init(key: SecKey) {
self.key = key
}
}
public class SSLSecurity {
public var validatedDN = true //should the domain name be validated?
var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates
var pubKeys: [SecKey]? //the public keys
var usePublicKeys = false //use public keys or certificate validation?
/**
Use certs from main app bundle
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
public convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.main().pathsForResources(ofType: "cer", inDirectory: ".")
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
var certs = certs
if let data = NSData(contentsOfFile: path) {
certs.append(SSLCert(data: data))
}
return certs
}
self.init(certs: certs, usePublicKeys: usePublicKeys)
}
/**
Designated init
- parameter keys: is the certificates or public keys to use
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
public init(certs: [SSLCert], usePublicKeys: Bool) {
self.usePublicKeys = usePublicKeys
if self.usePublicKeys {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in
var pubKeys = pubKeys
if let data = cert.certData where cert.key == nil {
cert.key = self.extractPublicKey(data: data)
}
if let key = cert.key {
pubKeys.append(key)
}
return pubKeys
}
self.pubKeys = pubKeys
self.isReady = true
}
} else {
let certificates = certs.reduce([NSData]()) { (certificates: [NSData], cert: SSLCert) -> [NSData] in
var certificates = certificates
if let data = cert.certData {
certificates.append(data)
}
return certificates
}
self.certificates = certificates
self.isReady = true
}
}
/**
Valid the trust and domain name.
- parameter trust: is the serverTrust to validate
- parameter domain: is the CN domain to validate
- returns: if the key was successfully validated
*/
public func isValid(trust: SecTrust, domain: String?) -> Bool {
var tries = 0
while(!self.isReady) {
usleep(1000)
tries += 1
if tries > 5 {
return false //doesn't appear it is going to ever be ready...
}
}
var policy: SecPolicy
if self.validatedDN {
policy = SecPolicyCreateSSL(true, domain)
} else {
policy = SecPolicyCreateBasicX509()
}
SecTrustSetPolicies(trust,policy)
if self.usePublicKeys {
if let keys = self.pubKeys {
let serverPubKeys = publicKeyChainForTrust(trust: trust)
for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] {
if serverKey.isEqual(key) {
return true
}
}
}
}
} else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust: trust)
var collect = [SecCertificate]()
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!)
}
SecTrustSetAnchorCertificates(trust,collect)
var result: SecTrustResultType = 0
SecTrustEvaluate(trust,&result)
let r = Int(result)
if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
trustedCount += 1
break
}
}
}
if trustedCount == serverCerts.count {
return true
}
}
}
return false
}
/**
Get the public key from a certificate data
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKey? {
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
return extractPublicKeyFromCert(cert: cert, policy: SecPolicyCreateBasicX509())
}
/**
Get the public key from a certificate
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKey? {
var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
guard let trust = possibleTrust else { return nil }
var result: SecTrustResultType = 0
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
/**
Get the certificate chain for the trust
- parameter trust: is the trust to lookup the certificate chain for
- returns: the certificate chain for the trust
*/
func certificateChainForTrust(trust: SecTrust) -> [NSData] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
var certificates = certificates
let cert = SecTrustGetCertificateAtIndex(trust, index)
certificates.append(SecCertificateCopyData(cert!))
return certificates
}
return certificates
}
/**
Get the public key chain for the trust
- parameter trust: is the trust to lookup the certificate chain and extract the public keys
- returns: the public keys from the certifcate chain for the trust
*/
func publicKeyChainForTrust(trust: SecTrust) -> [SecKey] {
let policy = SecPolicyCreateBasicX509()
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKey]()) { (keys: [SecKey], index: Int) -> [SecKey] in
var keys = keys
let cert = SecTrustGetCertificateAtIndex(trust, index)
if let key = extractPublicKeyFromCert(cert: cert!, policy: policy) {
keys.append(key)
}
return keys
}
return keys
}
}

View File

@ -23,6 +23,10 @@ import Foundation
import CoreFoundation
import Security
public let WebsocketDidConnectNotification = "WebsocketDidConnectNotification"
public let WebsocketDidDisconnectNotification = "WebsocketDidDisconnectNotification"
public let WebsocketDisconnectionErrorKeyName = "WebsocketDisconnectionErrorKeyName"
public protocol WebSocketDelegate: class {
func websocketDidConnect(socket: WebSocket)
func websocketDidDisconnect(socket: WebSocket, error: NSError?)
@ -133,6 +137,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
private var didDisconnect = false
private var readyToWrite = false
private let mutex = NSLock()
private let notificationCenter = NSNotificationCenter.default()
private var canDispatch: Bool {
mutex.lock()
let canWork = readyToWrite
@ -188,6 +193,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
Write a string to the websocket. This sends it as a text frame.
If you supply a non-nil completion block, I will perform it when the write completes.
- parameter str: The string to write.
- parameter completion: The (optional) completion handler.
*/
@ -200,6 +206,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
Write binary data to the websocket. This sends it as a binary frame.
If you supply a non-nil completion block, I will perform it when the write completes.
- parameter data: The data to write.
- parameter completion: The (optional) completion handler.
*/
@ -440,6 +447,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard let s = self else { return }
s.onConnect?()
s.delegate?.websocketDidConnect(socket: s)
s.notificationCenter.post(name: WebsocketDidConnectNotification, object: self)
}
case -1:
fragBuffer = NSData(bytes: buffer, length: bufferLen)
@ -823,6 +831,8 @@ public class WebSocket : NSObject, NSStreamDelegate {
guard let s = self else { return }
s.onDisconnect?(error)
s.delegate?.websocketDidDisconnect(socket: s, error: error)
let userInfo = error.map({ [WebsocketDisconnectionErrorKeyName: $0] })
s.notificationCenter.post(name: WebsocketDidDisconnectNotification, object: self, userInfo: userInfo)
}
}
@ -852,237 +862,3 @@ private extension UnsafeBufferPointer {
}
private let emptyBuffer = UnsafeBufferPointer<UInt8>(start: nil, count: 0)
public class SSLCert {
var certData: NSData?
var key: SecKey?
/**
Designated init for certificates
- parameter data: is the binary data of the certificate
- returns: a representation security object to be used with
*/
public init(data: NSData) {
self.certData = data
}
/**
Designated init for public keys
- parameter key: is the public key to be used
- returns: a representation security object to be used with
*/
public init(key: SecKey) {
self.key = key
}
}
public class SSLSecurity {
public var validatedDN = true //should the domain name be validated?
var isReady = false //is the key processing done?
var certificates: [NSData]? //the certificates
var pubKeys: [SecKey]? //the public keys
var usePublicKeys = false //use public keys or certificate validation?
/**
Use certs from main app bundle
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
public convenience init(usePublicKeys: Bool = false) {
let paths = NSBundle.main().pathsForResources(ofType: "cer", inDirectory: ".")
let certs = paths.reduce([SSLCert]()) { (certs: [SSLCert], path: String) -> [SSLCert] in
var certs = certs
if let data = NSData(contentsOfFile: path) {
certs.append(SSLCert(data: data))
}
return certs
}
self.init(certs: certs, usePublicKeys: usePublicKeys)
}
/**
Designated init
- parameter keys: is the certificates or public keys to use
- parameter usePublicKeys: is to specific if the publicKeys or certificates should be used for SSL pinning validation
- returns: a representation security object to be used with
*/
public init(certs: [SSLCert], usePublicKeys: Bool) {
self.usePublicKeys = usePublicKeys
if self.usePublicKeys {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)) {
let pubKeys = certs.reduce([SecKey]()) { (pubKeys: [SecKey], cert: SSLCert) -> [SecKey] in
var pubKeys = pubKeys
if let data = cert.certData where cert.key == nil {
cert.key = self.extractPublicKey(data: data)
}
if let key = cert.key {
pubKeys.append(key)
}
return pubKeys
}
self.pubKeys = pubKeys
self.isReady = true
}
} else {
let certificates = certs.reduce([NSData]()) { (certificates: [NSData], cert: SSLCert) -> [NSData] in
var certificates = certificates
if let data = cert.certData {
certificates.append(data)
}
return certificates
}
self.certificates = certificates
self.isReady = true
}
}
/**
Valid the trust and domain name.
- parameter trust: is the serverTrust to validate
- parameter domain: is the CN domain to validate
- returns: if the key was successfully validated
*/
public func isValid(trust: SecTrust, domain: String?) -> Bool {
var tries = 0
while(!self.isReady) {
usleep(1000)
tries += 1
if tries > 5 {
return false //doesn't appear it is going to ever be ready...
}
}
var policy: SecPolicy
if self.validatedDN {
policy = SecPolicyCreateSSL(true, (domain as! CFString))
} else {
policy = SecPolicyCreateBasicX509()
}
SecTrustSetPolicies(trust,policy)
if self.usePublicKeys {
if let keys = self.pubKeys {
let serverPubKeys = publicKeyChainForTrust(trust: trust)
for serverKey in serverPubKeys as [AnyObject] {
for key in keys as [AnyObject] {
if serverKey.isEqual(key) {
return true
}
}
}
}
} else if let certs = self.certificates {
let serverCerts = certificateChainForTrust(trust: trust)
var collect = [SecCertificate]()
for cert in certs {
collect.append(SecCertificateCreateWithData(nil,cert)!)
}
SecTrustSetAnchorCertificates(trust,collect as CFArray)
var result: SecTrustResultType = 0
SecTrustEvaluate(trust,&result)
let r = Int(result)
if r == kSecTrustResultUnspecified || r == kSecTrustResultProceed {
var trustedCount = 0
for serverCert in serverCerts {
for cert in certs {
if cert == serverCert {
trustedCount += 1
break
}
}
}
if trustedCount == serverCerts.count {
return true
}
}
}
return false
}
/**
Get the public key from a certificate data
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKey(data: NSData) -> SecKey? {
guard let cert = SecCertificateCreateWithData(nil, data) else { return nil }
return extractPublicKeyFromCert(cert: cert, policy: SecPolicyCreateBasicX509())
}
/**
Get the public key from a certificate
- parameter data: is the certificate to pull the public key from
- returns: a public key
*/
func extractPublicKeyFromCert(cert: SecCertificate, policy: SecPolicy) -> SecKey? {
var possibleTrust: SecTrust?
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
guard let trust = possibleTrust else { return nil }
var result: SecTrustResultType = 0
SecTrustEvaluate(trust, &result)
return SecTrustCopyPublicKey(trust)
}
/**
Get the certificate chain for the trust
- parameter trust: is the trust to lookup the certificate chain for
- returns: the certificate chain for the trust
*/
func certificateChainForTrust(trust: SecTrust) -> [NSData] {
let certificates = (0..<SecTrustGetCertificateCount(trust)).reduce([NSData]()) { (certificates: [NSData], index: Int) -> [NSData] in
var certificates = certificates
let cert = SecTrustGetCertificateAtIndex(trust, index)
certificates.append(SecCertificateCopyData(cert!))
return certificates
}
return certificates
}
/**
Get the public key chain for the trust
- parameter trust: is the trust to lookup the certificate chain and extract the public keys
- returns: the public keys from the certifcate chain for the trust
*/
func publicKeyChainForTrust(trust: SecTrust) -> [SecKey] {
let policy = SecPolicyCreateBasicX509()
let keys = (0..<SecTrustGetCertificateCount(trust)).reduce([SecKey]()) { (keys: [SecKey], index: Int) -> [SecKey] in
var keys = keys
let cert = SecTrustGetCertificateAtIndex(trust, index)
if let key = extractPublicKeyFromCert(cert: cert!, policy: policy) {
keys.append(key)
}
return keys
}
return keys
}
}