Skip to content

Commit

Permalink
Merge pull request #3 from BinaryParadise/BlueSocket
Browse files Browse the repository at this point in the history
为了支持Linux平台,用BlueSocket替换CocoaAsyncSocket
  • Loading branch information
rakeyang authored Oct 15, 2021
2 parents 9a23f1f + 64007a3 commit 20700bb
Show file tree
Hide file tree
Showing 15 changed files with 149 additions and 247 deletions.
17 changes: 13 additions & 4 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
"object": {
"pins": [
{
"package": "CocoaAsyncSocket",
"repositoryURL": "https://github.com/robbiehanson/CocoaAsyncSocket",
"package": "Socket",
"repositoryURL": "https://github.com/Kitura/BlueSocket.git",
"state": {
"branch": null,
"revision": "dbdc00669c1ced63b27c3c5f052ee4d28f10150c",
"version": "7.6.5"
"revision": "dd924c3bc2c1c144c42b8dda3896f1a03115ded4",
"version": "2.0.2"
}
},
{
Expand All @@ -28,6 +28,15 @@
"version": "4.0.0"
}
},
{
"package": "swift-argument-parser",
"repositoryURL": "https://github.com/apple/swift-argument-parser",
"state": {
"branch": null,
"revision": "6b2aa2748a7881eebb9f84fb10c01293e15b52ca",
"version": "0.5.0"
}
},
{
"package": "SwiftHpack",
"repositoryURL": "https://github.com/BinaryParadise/swift-hpack.git",
Expand Down
10 changes: 4 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ let package = Package(
// Products define the executables and libraries a package produces, and make them visible to other packages.
.executable(name: "server", targets: ["SimpleHTTPServer"]),
.library(name: "PracticeTLS", targets: ["PracticeTLS"]),
.library(name: "quic", targets: ["QUIC"])
],
dependencies: [
.package(url: "https://github.com/robbiehanson/CocoaAsyncSocket", from: "7.6.4"),
.package(url: "https://github.com/Kitura/BlueSocket.git", .upToNextMinor(from: "2.0.0")),
.package(url: "https://github.com/onevcat/Rainbow", .upToNextMajor(from: "4.0.0")),
.package(url: "https://github.com/BinaryParadise/CryptoSwift.git", .upToNextMajor(from: "1.4.1")),
.package(name: "SwiftHpack", url: "https://github.com/BinaryParadise/swift-hpack.git", from: "0.1.0"),
Expand All @@ -22,15 +21,14 @@ let package = Package(
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(name: "SimpleHTTPServer",
dependencies:
["PracticeTLS", "SwiftHpack", "QUIC"],
["PracticeTLS", "SwiftHpack"],
resources: [.copy("Cert")]),
.target(
name: "PracticeTLS",
dependencies: ["CocoaAsyncSocket", "Rainbow", "CryptoSwift"]),
.target(name: "QUIC", dependencies: ["PracticeTLS"]),
dependencies: ["Rainbow", "CryptoSwift", .product(name: "Socket", package: "BlueSocket")]),
.testTarget(
name: "PracticeTLSTests",
dependencies: ["PracticeTLS", "QUIC"]),
dependencies: ["PracticeTLS"]),
],
swiftLanguageVersions: [.v5]
)
6 changes: 3 additions & 3 deletions Sources/PracticeTLS/TLS/TLS1_2.RecordLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ extension TLS1_2 {
case .finished:
let clientFinished = TLSFinished(context.verifyDataForFinishedMessage(isClient: true))
s.clientVerifyData = clientFinished.dataWithBytes()
let clientVerifyData = try decrypt(handshake.dataWithBytes(), contentType: handshake.contentType) ?? []
let clientVerifyData = try decrypt(handshake.dataWithBytes(), contentType: handshake.contentType)
if clientVerifyData == s.clientVerifyData {
context.sock.writeData(data: TLSChangeCipherSpec().dataWithBytes(), tag: .changeCipherSpec)
context.asyncWrite(data: TLSChangeCipherSpec().dataWithBytes(), tag: .changeCipherSpec)
//踩坑:发送给客户端的finish也需要包含在摘要的握手消息中⚠️⚠️⚠️⚠️⚠️
context.handshakeMessages.append(clientFinished)
} else {
Expand All @@ -93,7 +93,7 @@ extension TLS1_2 {
context.disconnect()
} else {
if alert.alertType == .closeNotify {
context.sock.disconnectAfterReadingAndWriting()
context.disconnect()
}
}
LogError("alert: \(alert.level) -> \(alert.alertType)")
Expand Down
8 changes: 4 additions & 4 deletions Sources/PracticeTLS/TLS/TLS1_3.RecordLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extension TLS1_3 {
var serverCipherChanged: Bool = false
var handshakeState: HandshakeState
var s: TLSSecurityParameters!
let sema = DispatchSemaphore(value: 0)
let sema = DispatchSemaphore(value: 10)

required init(_ context: TLSConnection) {
self.context = context
Expand Down Expand Up @@ -61,7 +61,7 @@ extension TLS1_3 {
handshaked = true

//线程步调不一致导致解密失败⚠️⚠️⚠️⚠️
sema.wait()
//sema.wait()
changeReadKey(with: handshakeState.clientTrafficSecret!)
if TLSSessionManager.shared.isDebug {
try? description.write(toFile: "\(NSHomeDirectory())/MasterSecretKey.log", atomically: true, encoding: .utf8)
Expand All @@ -77,7 +77,7 @@ extension TLS1_3 {
case .alert:
if let alert = msg as? TLSAlert {
if alert.alertType == .closeNotify {
context.sock.disconnectAfterReadingAndWriting()
context.disconnect()
}
LogError("alert: \(alert.level) -> \(alert.alertType)")
} else {
Expand Down Expand Up @@ -169,7 +169,7 @@ extension TLS1_3 {

handshakeState.deriveApplicationTrafficSecrets(transcriptHash: context.transcriptHash)
changeWriteKey(with: handshakeState.serverTrafficSecret!)
sema.signal()
//sema.signal()
}

func finishedData(forClient isClient: Bool) -> [UInt8] {
Expand Down
77 changes: 45 additions & 32 deletions Sources/PracticeTLS/TLS/TLSConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@
//

import Foundation
import CocoaAsyncSocket
import Socket
import CryptoSwift

public let TLSClientFinishedLabel = [UInt8]("client finished".utf8)
public let TLSServerFinishedLabel = [UInt8]("server finished".utf8)
public let TLSKeyExpansionLabel = [UInt8]("key expansion".utf8)

public class TLSConnection: NSObject {
protocol TLSSocketStream {
func readData(_ tag: RWTags) -> [UInt8]

func writeData(_ data: [UInt8]?, tag: RWTags)

func disconnect()
}

public class TLSConnection {
var sessionId: String
public var sock: GCDAsyncSocket
var sock: TLSSocketStream
var nextMessage: TLSMessage?
var preMasterKey: [UInt8] = []
var handshakeMessages: [TLSHandshakeMessage] = []
Expand All @@ -35,6 +43,11 @@ public class TLSConnection: NSObject {
private var _record: TLSRecordProtocol?
var record: TLSRecordProtocol!
public var negotiatedProtocolVersion: TLSVersion = .V1_2
var socketQueue: DispatchQueue = DispatchQueue(label: "SocketQueue", attributes: .init(rawValue: 0))

public var connectedHost: String {
return (sock as? Socket)?.remoteHostname ?? "known"
}

var transcriptHash: [UInt8] {
var handshakeData: [UInt8] = []
Expand All @@ -61,16 +74,14 @@ public class TLSConnection: NSObject {
return record.s.hashAlgorithm.hashFunction(handshakeData)
}

init(_ sock: GCDAsyncSocket) {
init(_ sock: TLSSocketStream) {
self.sock = sock
sessionId = AES.randomIV(16).toHexString()
super.init()
self.sock.delegate = self
}

func handshake() {
LogInfo("handshake start")
sock.readData(tag: .handshake(.clientHello))
asyncRead(tag: .handshake(.clientHello))
}

func verifyDataForFinishedMessage(isClient: Bool) -> [UInt8] {
Expand All @@ -80,14 +91,15 @@ public class TLSConnection: NSObject {
}

public func disconnect() {
sock.disconnectAfterWriting()
sock.disconnect()
TLSSessionManager.shared.clearConnection(self)
}

public func readApplication(tag: Int) {
readWriteTag = tag
if lastPacket {
//处理粘包,若为最后一个包开始新读取
sock.readData(tag: .applicationData)
asyncRead(tag: .applicationData)
}
}

Expand All @@ -104,7 +116,11 @@ public class TLSConnection: NSObject {
guard let msg = msg else { return }
var prepareData: [UInt8] = []
if record.serverCipherChanged {
prepareData.write(ContentType.applicationData.rawValue)
if negotiatedProtocolVersion == .V1_3 {
prepareData.write(ContentType.applicationData.rawValue)
} else {
prepareData.write(msg.contentType.rawValue)
}
} else {
prepareData.write(msg.contentType.rawValue)
}
Expand All @@ -119,24 +135,27 @@ public class TLSConnection: NSObject {
handshakeMessages.append(handshake)
}

sendData(prepareData, tag: msg.rwtag)
asyncWrite(data: prepareData, tag: msg.rwtag)
}

func sendData(_ sendData: [UInt8], tag: RWTags) {
if maximumRecordSize < sendData.count {
let page = (sendData.count/maximumRecordSize+(sendData.count%maximumRecordSize > 0 ? 1:0))
for i in 0..<page {
let cur = sendData[i*maximumRecordSize..<min(sendData.count, (i+1)*maximumRecordSize)]
sock.writeData(data: Array(cur), tag: i < page-1 ? .fragment : tag)
}
} else {
sock.writeData(data: sendData, tag: tag)
func asyncRead(tag: RWTags) {
socketQueue.async { [weak self] in
guard let self = self else { return }
self.socket(didRead: self.sock.readData(tag), withTag: tag)
}
}

func asyncWrite(data: [UInt8]?, tag: RWTags) -> Void {
socketQueue.async { [weak self] in
guard let self = self else { return }
self.sock.writeData(data, tag: tag)
self.socket(didWriteDataWithTag: tag)
}
}
}

extension TLSConnection: GCDAsyncSocketDelegate {
public func socket(_ sock: GCDAsyncSocket, didRead data: Data, withTag tag: Int) {
extension TLSConnection {
func socket(didRead data: [UInt8], withTag tag: RWTags) {
//处理粘包
let stream = DataStream(data)
while !stream.endOfStream {
Expand Down Expand Up @@ -164,16 +183,10 @@ extension TLSConnection: GCDAsyncSocketDelegate {
}
}

public func socket(_ sock: GCDAsyncSocket, didWriteDataWithTag tag: Int) {
let wtag = RWTags(rawValue: UInt8(tag))
LogInfo("\(wtag)")
if let nextRead = record.didWriteMessage(wtag) {
sock.readData(tag: nextRead)
func socket(didWriteDataWithTag tag: RWTags) {
LogInfo("\(tag)")
if let nextRead = record.didWriteMessage(tag) {
asyncRead(tag: nextRead)
}
}

public func socketDidDisconnect(_ sock: GCDAsyncSocket, withError err: Error?) {
LogInfo("\(err)")
TLSSessionManager.shared.clearConnection(self)
}
}
4 changes: 2 additions & 2 deletions Sources/PracticeTLS/TLS/TLSSessionManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//

import Foundation
import CocoaAsyncSocket
import Socket

public protocol TLSConnectionDelegate {
/// TLS握手完成
Expand All @@ -31,7 +31,7 @@ public class TLSSessionManager: NSObject {
public var delegate: TLSConnectionDelegate?
let sema = DispatchSemaphore(value: 1)

public func acceptConnection(_ sock: GCDAsyncSocket) {
public func acceptConnection(_ sock: Socket) {
let newConnection = TLSConnection(sock)
newConnection.handshake()
sema.wait()
Expand Down
26 changes: 26 additions & 0 deletions Sources/PracticeTLS/TLS/TLSUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,32 @@ public extension String {
}
}

var uint8Array: [UInt8] {
var result: [UInt8] = []
var even: Bool = true
var byte: UInt8 = 0
func decodeByte(c: UInt8) -> UInt8? {
switch c {
case 0x30...0x39: return c - 0x30
case 0x41...0x46: return c - 0x41 + 10
case 0x61...0x66: return c - 0x61 + 10
default:
return nil
}
}
for c in utf8 {
guard let val = decodeByte(c: c) else { continue }
if even {
byte = val << 4
} else {
byte += val
result.append(byte)
}
even = !even
}
return result
}

var rsaCleanKey: String {
if contains("BEGIN") {
var arr = self.split(separator: "\n")
Expand Down
34 changes: 28 additions & 6 deletions Sources/PracticeTLS/Utility/SocketExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// Created by Rake Yang on 2021/8/5.
//

import CocoaAsyncSocket
import Socket
import Foundation

/// 数据读写标记
enum RWTags {
Expand Down Expand Up @@ -45,13 +46,34 @@ enum RWTags {
}
}

extension GCDAsyncSocket {
func readData(tag: RWTags) -> Void {
readData(withTimeout: -1, tag: tag.rawValue)
extension Socket: TLSSocketStream {
func readData(_ tag: RWTags) -> [UInt8] {
LogDebug("读取前: \(Thread.current)")
var buffer: Data = Data()
do {
try read(into: &buffer)
//LogDebug("读取后: \(Thread.current)")
return buffer.bytes
} catch {
//LogError("\(error)")
}
return []
}

func writeData(_ data: [UInt8]?, tag: RWTags) {
guard let data = data else { return }
//LogDebug("写入前: \(Thread.current)")
var buffer: Data = Data()
do {
try write(from: Data(data))
//LogDebug("写入后: \(Thread.current)")
} catch {
LogError("\(error)")
}
}

func writeData(data: [UInt8]?, tag: RWTags) -> Void {
write(Data(data ?? []), withTimeout: -1, tag: tag.rawValue)
func disconnect() {
close()
}
}

Expand Down
Loading

0 comments on commit 20700bb

Please sign in to comment.