add chacha20
This commit is contained in:
parent
cb33d81428
commit
8538e89f92
@ -31,10 +31,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
|
||||
// 加密算法
|
||||
let rsaCipher = try! CCRSACipher(keySize: 1024)
|
||||
let aesChiper = CCAESChiper()
|
||||
|
||||
self.rootTask = Task {
|
||||
self.contextActor = SDLContextActor(provider: self, config: config, rsaCipher: rsaCipher, aesCipher: aesChiper)
|
||||
self.contextActor = SDLContextActor(provider: self, config: config, rsaCipher: rsaCipher)
|
||||
await self.contextActor?.start()
|
||||
completionHandler(nil)
|
||||
}
|
||||
@ -79,52 +77,4 @@ extension PacketTunnelProvider {
|
||||
|
||||
return interfaces.first {$0.name == "en0"}
|
||||
}()
|
||||
|
||||
struct CCRSACipher: RSACipher {
|
||||
var pubKey: String
|
||||
let privateKeyDER: Data
|
||||
|
||||
init(keySize: Int) throws {
|
||||
let (privateKey, publicKey) = try Self.loadKeys(keySize: keySize)
|
||||
let privKeyStr = SwKeyConvert.PrivateKey.derToPKCS1PEM(privateKey)
|
||||
|
||||
self.pubKey = SwKeyConvert.PublicKey.derToPKCS8PEM(publicKey)
|
||||
self.privateKeyDER = try SwKeyConvert.PrivateKey.pemToPKCS1DER(privKeyStr)
|
||||
}
|
||||
|
||||
public func decode(data: Data) throws -> Data {
|
||||
let tag = Data()
|
||||
let (decryptedData, _) = try CC.RSA.decrypt(data, derKey: self.privateKeyDER, tag: tag, padding: .pkcs1, digest: .none)
|
||||
|
||||
return decryptedData
|
||||
}
|
||||
|
||||
private static func loadKeys(keySize: Int) throws -> (Data, Data) {
|
||||
if let privateKey = UserDefaults.standard.data(forKey: "privateKey"),
|
||||
let publicKey = UserDefaults.standard.data(forKey: "publicKey") {
|
||||
|
||||
return (privateKey, publicKey)
|
||||
} else {
|
||||
let (privateKey, publicKey) = try CC.RSA.generateKeyPair(keySize)
|
||||
UserDefaults.standard.setValue(privateKey, forKey: "privateKey")
|
||||
UserDefaults.standard.setValue(publicKey, forKey: "publicKey")
|
||||
|
||||
return (privateKey, publicKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CCAESChiper: AESCipher {
|
||||
func decypt(aesKey: Data, data: Data) throws -> Data {
|
||||
let ivData = Data(aesKey.prefix(16))
|
||||
return try CC.crypt(.decrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: data, key: aesKey, iv: ivData)
|
||||
}
|
||||
|
||||
func encrypt(aesKey: Data, data: Data) throws -> Data {
|
||||
let ivData = Data(aesKey.prefix(16))
|
||||
|
||||
return try CC.crypt(.encrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: data, key: aesKey, iv: ivData)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
//
|
||||
// AESCipher.swift
|
||||
// sdlan
|
||||
//
|
||||
// Created by 安礼成 on 2025/7/14.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
public protocol AESCipher {
|
||||
func decypt(aesKey: Data, data: Data) throws -> Data
|
||||
|
||||
func encrypt(aesKey: Data, data: Data) throws -> Data
|
||||
}
|
||||
@ -26,15 +26,13 @@ actor SDLContextActor {
|
||||
var natType: SDLNATProberActor.NatType = .blocked
|
||||
|
||||
// AES加密,授权通过后,对象才会被创建
|
||||
nonisolated let aesCipher: AESCipher
|
||||
|
||||
// aes
|
||||
private var aesKey: Data?
|
||||
private var dataCipher: CCDataCipher?
|
||||
|
||||
// session token
|
||||
private var sessionToken: Data?
|
||||
|
||||
// rsa的相关配置, public_key是本地生成的
|
||||
// 加密算法相关
|
||||
nonisolated let rsaCipher: RSACipher
|
||||
|
||||
// 依赖的变量
|
||||
@ -84,11 +82,10 @@ actor SDLContextActor {
|
||||
// 注册任务
|
||||
private var registerTask: Task<Void, Never>?
|
||||
|
||||
public init(provider: NEPacketTunnelProvider, config: SDLConfiguration, rsaCipher: RSACipher, aesCipher: AESCipher) {
|
||||
public init(provider: NEPacketTunnelProvider, config: SDLConfiguration, rsaCipher: RSACipher) {
|
||||
self.provider = provider
|
||||
self.config = config
|
||||
self.rsaCipher = rsaCipher
|
||||
self.aesCipher = aesCipher
|
||||
|
||||
self.puncherActor = SDLPuncherActor()
|
||||
self.proberActor = SDLNATProberActor(addressArray: config.stunProbeSocketAddressArray)
|
||||
@ -392,10 +389,28 @@ actor SDLContextActor {
|
||||
|
||||
private func handleRegisterSuperAck(registerSuperAck: SDLRegisterSuperAck) async {
|
||||
// 需要对数据通过rsa的私钥解码
|
||||
self.aesKey = try! self.rsaCipher.decode(data: Data(registerSuperAck.aesKey))
|
||||
guard let key = try? self.rsaCipher.decode(data: Data(registerSuperAck.key)) else {
|
||||
SDLLogger.shared.log("[SDLContext] registerSuperAck invalid key", level: .error)
|
||||
self.provider.cancelTunnelWithError(SDLError.invalidKey)
|
||||
return
|
||||
}
|
||||
|
||||
let algorithm = registerSuperAck.algorithm.lowercased()
|
||||
let regionId = registerSuperAck.regionID
|
||||
self.sessionToken = registerSuperAck.sessionToken
|
||||
|
||||
SDLLogger.shared.log("[SDLContext] get registerSuperAck, aes_key len: \(self.aesKey!.count)", level: .info)
|
||||
switch algorithm {
|
||||
case "aes":
|
||||
self.dataCipher = CCAESChiper(key: key)
|
||||
case "chacha20":
|
||||
self.dataCipher = CCChaCha20Cipher(regionId: regionId, keyData: key)
|
||||
default:
|
||||
SDLLogger.shared.log("[SDLContext] registerSuperAck invalid algorithm \(algorithm)", level: .error)
|
||||
self.provider.cancelTunnelWithError(SDLError.unsupportedAlgorithm(algorithm: algorithm))
|
||||
return
|
||||
}
|
||||
|
||||
SDLLogger.shared.log("[SDLContext] get registerSuperAck, aes_key len: \(key.count)", level: .info)
|
||||
// 服务器分配的tun网卡信息
|
||||
do {
|
||||
try await self.setNetworkSettings(networkAddress: self.config.networkAddress, dnsServer: SDLDNSClient.Helper.dnsServer)
|
||||
@ -511,7 +526,7 @@ actor SDLContextActor {
|
||||
}
|
||||
|
||||
private func handleHoleData(data: SDLData) async throws {
|
||||
guard let aesKey = self.aesKey else {
|
||||
guard let dataCipher = self.dataCipher else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -521,7 +536,7 @@ actor SDLContextActor {
|
||||
return
|
||||
}
|
||||
|
||||
guard let decyptedData = try? self.aesCipher.decypt(aesKey: aesKey, data: Data(data.data)) else {
|
||||
guard let decyptedData = try? dataCipher.decrypt(cipherText: Data(data.data)) else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -683,7 +698,7 @@ actor SDLContextActor {
|
||||
let networkAddr = self.config.networkAddress
|
||||
// 将数据封装层2层的数据包
|
||||
let layerPacket = LayerPacket(dstMac: dstMac, srcMac: networkAddr.mac, type: type, data: data)
|
||||
guard let udpHole = self.udpHole, let aesKey = self.aesKey, let encodedPacket = try? self.aesCipher.encrypt(aesKey: aesKey, data: layerPacket.marshal()) else {
|
||||
guard let udpHole = self.udpHole, let dataCipher = self.dataCipher, let encodedPacket = try? dataCipher.encrypt(plainText: layerPacket.marshal()) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
27
Tun/Punchnet/Cipher/CCAESChiper.swift
Normal file
27
Tun/Punchnet/Cipher/CCAESChiper.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// CCAESChiper.swift
|
||||
// punchnet
|
||||
//
|
||||
// Created by 安礼成 on 2026/3/17.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
struct CCAESChiper: CCDataCipher {
|
||||
private let aesKey: Data
|
||||
|
||||
init(key: Data) {
|
||||
self.aesKey = key
|
||||
}
|
||||
|
||||
func decrypt(cipherText: Data) throws -> Data {
|
||||
let ivData = Data(aesKey.prefix(16))
|
||||
return try CC.crypt(.decrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: cipherText, key: aesKey, iv: ivData)
|
||||
}
|
||||
|
||||
func encrypt(plainText: Data) throws -> Data {
|
||||
let ivData = Data(aesKey.prefix(16))
|
||||
|
||||
return try CC.crypt(.encrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding, data: plainText, key: aesKey, iv: ivData)
|
||||
}
|
||||
|
||||
}
|
||||
77
Tun/Punchnet/Cipher/CCChaCha20Cipher.swift
Normal file
77
Tun/Punchnet/Cipher/CCChaCha20Cipher.swift
Normal file
@ -0,0 +1,77 @@
|
||||
//
|
||||
// NonceGenerator.swift
|
||||
// punchnet
|
||||
//
|
||||
// Created by 安礼成 on 2026/3/17.
|
||||
//
|
||||
import Foundation
|
||||
import CryptoKit
|
||||
|
||||
/// ChaCha20-Poly1305 加解密示例
|
||||
struct CCChaCha20Cipher: CCDataCipher {
|
||||
private let key: SymmetricKey
|
||||
private let nonceGenerator: NonceGenerator
|
||||
|
||||
init(regionId: UInt32, keyData: Data) {
|
||||
self.key = SymmetricKey(data: keyData)
|
||||
self.nonceGenerator = NonceGenerator(regionId: regionId)
|
||||
}
|
||||
|
||||
/// 加密
|
||||
func encrypt(plainText: Data) throws -> Data {
|
||||
let nonce = nonceGenerator.nextNonceData()
|
||||
let sealedBox = try ChaChaPoly.seal(plainText, using: key, nonce: .init(data: nonce))
|
||||
|
||||
return sealedBox.combined
|
||||
}
|
||||
|
||||
/// 解密
|
||||
func decrypt(cipherText: Data) throws -> Data {
|
||||
let sealedBox = try ChaChaPoly.SealedBox(combined: cipherText)
|
||||
return try ChaChaPoly.open(sealedBox, using: key)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension CCChaCha20Cipher {
|
||||
|
||||
/// Nonce生成器(基于ServerRange + 毫秒时间低位 + 本地自增counter)
|
||||
final class NonceGenerator {
|
||||
private let locker = NSLock()
|
||||
|
||||
private let regionId: UInt32 // 32-bit 全局前缀
|
||||
private var counter: UInt64 = 0 // 自增counter
|
||||
|
||||
init(regionId: UInt32) {
|
||||
self.regionId = regionId
|
||||
}
|
||||
|
||||
/// 生成64-bit Nonce
|
||||
func nextNonceData() -> Data {
|
||||
locker.lock()
|
||||
defer {
|
||||
locker.unlock()
|
||||
}
|
||||
|
||||
let nowMillis = UInt64(Date().timeIntervalSince1970 * 1000)
|
||||
// 时间占用40个bit位, 自增id占用24位
|
||||
let timeMask: UInt64 = (1 << 40) - 1
|
||||
let timeLow = nowMillis & timeMask
|
||||
|
||||
// 生成 Nonce
|
||||
let counterMask: UInt64 = (1 << 24) - 1
|
||||
let nonce = (timeLow << 24) | (counter & counterMask)
|
||||
// 自增counter
|
||||
self.counter = (self.counter + 1) & counterMask // 超过最大值回到0
|
||||
|
||||
var data = Data()
|
||||
// region: UInt32 -> 4字节大端
|
||||
data.append(contentsOf: withUnsafeBytes(of: regionId.bigEndian, Array.init))
|
||||
// nonce: UInt64 -> 8字节大端
|
||||
data.append(contentsOf: withUnsafeBytes(of: nonce.bigEndian, Array.init))
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
13
Tun/Punchnet/Cipher/CCDataCipher.swift
Normal file
13
Tun/Punchnet/Cipher/CCDataCipher.swift
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// AESCipher.swift
|
||||
// sdlan
|
||||
//
|
||||
// Created by 安礼成 on 2025/7/14.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
public protocol CCDataCipher {
|
||||
func decrypt(cipherText: Data) throws -> Data
|
||||
|
||||
func encrypt(plainText: Data) throws -> Data
|
||||
}
|
||||
41
Tun/Punchnet/Cipher/CCRSACipher.swift
Normal file
41
Tun/Punchnet/Cipher/CCRSACipher.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// CCRSACipher.swift
|
||||
// punchnet
|
||||
//
|
||||
// Created by 安礼成 on 2026/3/17.
|
||||
//
|
||||
import Foundation
|
||||
|
||||
struct CCRSACipher: RSACipher {
|
||||
var pubKey: String
|
||||
let privateKeyDER: Data
|
||||
|
||||
init(keySize: Int) throws {
|
||||
let (privateKey, publicKey) = try Self.loadKeys(keySize: keySize)
|
||||
let privKeyStr = SwKeyConvert.PrivateKey.derToPKCS1PEM(privateKey)
|
||||
|
||||
self.pubKey = SwKeyConvert.PublicKey.derToPKCS8PEM(publicKey)
|
||||
self.privateKeyDER = try SwKeyConvert.PrivateKey.pemToPKCS1DER(privKeyStr)
|
||||
}
|
||||
|
||||
public func decode(data: Data) throws -> Data {
|
||||
let tag = Data()
|
||||
let (decryptedData, _) = try CC.RSA.decrypt(data, derKey: self.privateKeyDER, tag: tag, padding: .pkcs1, digest: .none)
|
||||
|
||||
return decryptedData
|
||||
}
|
||||
|
||||
private static func loadKeys(keySize: Int) throws -> (Data, Data) {
|
||||
if let privateKey = UserDefaults.standard.data(forKey: "privateKey"),
|
||||
let publicKey = UserDefaults.standard.data(forKey: "publicKey") {
|
||||
|
||||
return (privateKey, publicKey)
|
||||
} else {
|
||||
let (privateKey, publicKey) = try CC.RSA.generateKeyPair(keySize)
|
||||
UserDefaults.standard.setValue(privateKey, forKey: "privateKey")
|
||||
UserDefaults.standard.setValue(publicKey, forKey: "publicKey")
|
||||
|
||||
return (privateKey, publicKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,4 +8,7 @@
|
||||
enum SDLError: Error {
|
||||
case socketClosed
|
||||
case socketError
|
||||
|
||||
case invalidKey
|
||||
case unsupportedAlgorithm(algorithm: String)
|
||||
}
|
||||
|
||||
@ -127,7 +127,13 @@ struct SDLRegisterSuperAck: @unchecked Sendable {
|
||||
|
||||
var pktID: UInt32 = 0
|
||||
|
||||
var aesKey: Data = Data()
|
||||
/// 目前支持aes, chacha20
|
||||
var algorithm: String = String()
|
||||
|
||||
var key: Data = Data()
|
||||
|
||||
/// 逻辑分段,chacha20加密算法需要使用该字段
|
||||
var regionID: UInt32 = 0
|
||||
|
||||
var sessionToken: Data = Data()
|
||||
|
||||
@ -268,7 +274,7 @@ struct SDLPolicyResponse: @unchecked Sendable {
|
||||
/// 版本号,客户端需要比较版本号确定是否覆盖; 请求端自己去管理版本号,服务端只是原样回写
|
||||
var version: UInt32 = 0
|
||||
|
||||
/// 4+1+2 的稀疏序列化规则
|
||||
/// 1 + 2稀疏序列化规则, 按照: <<Proto:8, Port:16>> 这个格式序列号所有的规则信息; 下发的数据默认都是allow,deny规则的服务器端已经屏蔽
|
||||
var rules: Data = Data()
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
@ -725,8 +731,10 @@ extension SDLRegisterSuperAck: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
||||
static let protoMessageName: String = "SDLRegisterSuperAck"
|
||||
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "pkt_id"),
|
||||
2: .standard(proto: "aes_key"),
|
||||
3: .standard(proto: "session_token"),
|
||||
2: .same(proto: "algorithm"),
|
||||
3: .same(proto: "key"),
|
||||
4: .standard(proto: "region_id"),
|
||||
5: .standard(proto: "session_token"),
|
||||
]
|
||||
|
||||
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
@ -736,8 +744,10 @@ extension SDLRegisterSuperAck: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.pktID) }()
|
||||
case 2: try { try decoder.decodeSingularBytesField(value: &self.aesKey) }()
|
||||
case 3: try { try decoder.decodeSingularBytesField(value: &self.sessionToken) }()
|
||||
case 2: try { try decoder.decodeSingularStringField(value: &self.algorithm) }()
|
||||
case 3: try { try decoder.decodeSingularBytesField(value: &self.key) }()
|
||||
case 4: try { try decoder.decodeSingularUInt32Field(value: &self.regionID) }()
|
||||
case 5: try { try decoder.decodeSingularBytesField(value: &self.sessionToken) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
@ -747,18 +757,26 @@ extension SDLRegisterSuperAck: SwiftProtobuf.Message, SwiftProtobuf._MessageImpl
|
||||
if self.pktID != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.pktID, fieldNumber: 1)
|
||||
}
|
||||
if !self.aesKey.isEmpty {
|
||||
try visitor.visitSingularBytesField(value: self.aesKey, fieldNumber: 2)
|
||||
if !self.algorithm.isEmpty {
|
||||
try visitor.visitSingularStringField(value: self.algorithm, fieldNumber: 2)
|
||||
}
|
||||
if !self.key.isEmpty {
|
||||
try visitor.visitSingularBytesField(value: self.key, fieldNumber: 3)
|
||||
}
|
||||
if self.regionID != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.regionID, fieldNumber: 4)
|
||||
}
|
||||
if !self.sessionToken.isEmpty {
|
||||
try visitor.visitSingularBytesField(value: self.sessionToken, fieldNumber: 3)
|
||||
try visitor.visitSingularBytesField(value: self.sessionToken, fieldNumber: 5)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: SDLRegisterSuperAck, rhs: SDLRegisterSuperAck) -> Bool {
|
||||
if lhs.pktID != rhs.pktID {return false}
|
||||
if lhs.aesKey != rhs.aesKey {return false}
|
||||
if lhs.algorithm != rhs.algorithm {return false}
|
||||
if lhs.key != rhs.key {return false}
|
||||
if lhs.regionID != rhs.regionID {return false}
|
||||
if lhs.sessionToken != rhs.sessionToken {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user