Kit: move types from WireGuardKit to WireGuardKitTypes.

This prevents linking against wg-go when 3rd-party application needs to
use the WireGuardKit types from within the main bundle, therefore
prevents wg-go from spinning off Golang threads where not applicable.

Signed-off-by: Andrej Mihajlov <and@mullvad.net>
This commit is contained in:
Andrej Mihajlov
2022-03-11 13:25:29 +01:00
parent 23618f994f
commit a27dc674f1
12 changed files with 91 additions and 73 deletions
+35
View File
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
import Foundation
import Network
public struct DNSServer {
public let address: IPAddress
public init(address: IPAddress) {
self.address = address
}
}
extension DNSServer: Equatable {
public static func == (lhs: DNSServer, rhs: DNSServer) -> Bool {
return lhs.address.rawValue == rhs.address.rawValue
}
}
extension DNSServer {
public var stringRepresentation: String {
return "\(address)"
}
public init?(from addressString: String) {
if let addr = IPv4Address(addressString) {
address = addr
} else if let addr = IPv6Address(addressString) {
address = addr
} else {
return nil
}
}
}
+101
View File
@@ -0,0 +1,101 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
import Foundation
import Network
public struct Endpoint {
public let host: NWEndpoint.Host
public let port: NWEndpoint.Port
public init(host: NWEndpoint.Host, port: NWEndpoint.Port) {
self.host = host
self.port = port
}
}
extension Endpoint: Equatable {
public static func == (lhs: Endpoint, rhs: Endpoint) -> Bool {
return lhs.host == rhs.host && lhs.port == rhs.port
}
}
extension Endpoint: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(host)
hasher.combine(port)
}
}
extension Endpoint {
public var stringRepresentation: String {
switch host {
case .name(let hostname, _):
return "\(hostname):\(port)"
case .ipv4(let address):
return "\(address):\(port)"
case .ipv6(let address):
return "[\(address)]:\(port)"
@unknown default:
fatalError()
}
}
public init?(from string: String) {
// Separation of host and port is based on 'parse_endpoint' function in
// https://git.zx2c4.com/wireguard-tools/tree/src/config.c
guard !string.isEmpty else { return nil }
let startOfPort: String.Index
let hostString: String
if string.first! == "[" {
// Look for IPv6-style endpoint, like [::1]:80
let startOfHost = string.index(after: string.startIndex)
guard let endOfHost = string.dropFirst().firstIndex(of: "]") else { return nil }
let afterEndOfHost = string.index(after: endOfHost)
if afterEndOfHost == string.endIndex { return nil }
guard string[afterEndOfHost] == ":" else { return nil }
startOfPort = string.index(after: afterEndOfHost)
hostString = String(string[startOfHost ..< endOfHost])
} else {
// Look for an IPv4-style endpoint, like 127.0.0.1:80
guard let endOfHost = string.firstIndex(of: ":") else { return nil }
startOfPort = string.index(after: endOfHost)
hostString = String(string[string.startIndex ..< endOfHost])
}
guard let endpointPort = NWEndpoint.Port(String(string[startOfPort ..< string.endIndex])) else { return nil }
let invalidCharacterIndex = hostString.unicodeScalars.firstIndex { char in
return !CharacterSet.urlHostAllowed.contains(char)
}
guard invalidCharacterIndex == nil else { return nil }
host = NWEndpoint.Host(hostString)
port = endpointPort
}
}
extension Endpoint {
public func hasHostAsIPAddress() -> Bool {
switch host {
case .name:
return false
case .ipv4:
return true
case .ipv6:
return true
@unknown default:
fatalError()
}
}
public func hostname() -> String? {
switch host {
case .name(let hostname, _):
return hostname
case .ipv4:
return nil
case .ipv6:
return nil
@unknown default:
fatalError()
}
}
}
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
import Foundation
import Network
public struct IPAddressRange {
public let address: IPAddress
public let networkPrefixLength: UInt8
init(address: IPAddress, networkPrefixLength: UInt8) {
self.address = address
self.networkPrefixLength = networkPrefixLength
}
}
extension IPAddressRange: Equatable {
public static func == (lhs: IPAddressRange, rhs: IPAddressRange) -> Bool {
return lhs.address.rawValue == rhs.address.rawValue && lhs.networkPrefixLength == rhs.networkPrefixLength
}
}
extension IPAddressRange: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(address.rawValue)
hasher.combine(networkPrefixLength)
}
}
extension IPAddressRange {
public var stringRepresentation: String {
return "\(address)/\(networkPrefixLength)"
}
public init?(from string: String) {
guard let parsed = IPAddressRange.parseAddressString(string) else { return nil }
address = parsed.0
networkPrefixLength = parsed.1
}
private static func parseAddressString(_ string: String) -> (IPAddress, UInt8)? {
let endOfIPAddress = string.lastIndex(of: "/") ?? string.endIndex
let addressString = String(string[string.startIndex ..< endOfIPAddress])
let address: IPAddress
if let addr = IPv4Address(addressString) {
address = addr
} else if let addr = IPv6Address(addressString) {
address = addr
} else {
return nil
}
let maxNetworkPrefixLength: UInt8 = address is IPv4Address ? 32 : 128
var networkPrefixLength: UInt8
if endOfIPAddress < string.endIndex { // "/" was located
let indexOfNetworkPrefixLength = string.index(after: endOfIPAddress)
guard indexOfNetworkPrefixLength < string.endIndex else { return nil }
let networkPrefixLengthSubstring = string[indexOfNetworkPrefixLength ..< string.endIndex]
guard let npl = UInt8(networkPrefixLengthSubstring) else { return nil }
networkPrefixLength = min(npl, maxNetworkPrefixLength)
} else {
networkPrefixLength = maxNetworkPrefixLength
}
return (address, networkPrefixLength)
}
public func subnetMask() -> IPAddress {
if address is IPv4Address {
let mask = networkPrefixLength > 0 ? ~UInt32(0) << (32 - networkPrefixLength) : UInt32(0)
let bytes = Data([
UInt8(truncatingIfNeeded: mask >> 24),
UInt8(truncatingIfNeeded: mask >> 16),
UInt8(truncatingIfNeeded: mask >> 8),
UInt8(truncatingIfNeeded: mask >> 0)
])
return IPv4Address(bytes)!
}
if address is IPv6Address {
var bytes = Data(repeating: 0, count: 16)
for i in 0..<Int(networkPrefixLength/8) {
bytes[i] = 0xff
}
let nibble = networkPrefixLength % 32
if nibble != 0 {
let mask = ~UInt32(0) << (32 - nibble)
let i = Int(networkPrefixLength / 32 * 4)
bytes[i + 0] = UInt8(truncatingIfNeeded: mask >> 24)
bytes[i + 1] = UInt8(truncatingIfNeeded: mask >> 16)
bytes[i + 2] = UInt8(truncatingIfNeeded: mask >> 8)
bytes[i + 3] = UInt8(truncatingIfNeeded: mask >> 0)
}
return IPv6Address(bytes)!
}
fatalError()
}
public func maskedAddress() -> IPAddress {
let subnet = subnetMask().rawValue
var masked = Data(address.rawValue)
if subnet.count != masked.count {
fatalError()
}
for i in 0..<subnet.count {
masked[i] &= subnet[i]
}
if subnet.count == 4 {
return IPv4Address(masked)!
}
if subnet.count == 16 {
return IPv6Address(masked)!
}
fatalError()
}
}
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
import Foundation
import Network
public struct InterfaceConfiguration {
public var privateKey: PrivateKey
public var addresses = [IPAddressRange]()
public var listenPort: UInt16?
public var mtu: UInt16?
public var dns = [DNSServer]()
public var dnsSearch = [String]()
public init(privateKey: PrivateKey) {
self.privateKey = privateKey
}
}
extension InterfaceConfiguration: Equatable {
public static func == (lhs: InterfaceConfiguration, rhs: InterfaceConfiguration) -> Bool {
let lhsAddresses = lhs.addresses.filter { $0.address is IPv4Address } + lhs.addresses.filter { $0.address is IPv6Address }
let rhsAddresses = rhs.addresses.filter { $0.address is IPv4Address } + rhs.addresses.filter { $0.address is IPv6Address }
return lhs.privateKey == rhs.privateKey &&
lhsAddresses == rhsAddresses &&
lhs.listenPort == rhs.listenPort &&
lhs.mtu == rhs.mtu &&
lhs.dns == rhs.dns &&
lhs.dnsSearch == rhs.dnsSearch
}
}
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
import Foundation
public struct PeerConfiguration {
public var publicKey: PublicKey
public var preSharedKey: PreSharedKey?
public var allowedIPs = [IPAddressRange]()
public var endpoint: Endpoint?
public var persistentKeepAlive: UInt16?
public var rxBytes: UInt64?
public var txBytes: UInt64?
public var lastHandshakeTime: Date?
public init(publicKey: PublicKey) {
self.publicKey = publicKey
}
}
extension PeerConfiguration: Equatable {
public static func == (lhs: PeerConfiguration, rhs: PeerConfiguration) -> Bool {
return lhs.publicKey == rhs.publicKey &&
lhs.preSharedKey == rhs.preSharedKey &&
Set(lhs.allowedIPs) == Set(rhs.allowedIPs) &&
lhs.endpoint == rhs.endpoint &&
lhs.persistentKeepAlive == rhs.persistentKeepAlive
}
}
extension PeerConfiguration: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(publicKey)
hasher.combine(preSharedKey)
hasher.combine(Set(allowedIPs))
hasher.combine(endpoint)
hasher.combine(persistentKeepAlive)
}
}
+114
View File
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
import Foundation
#if SWIFT_PACKAGE
import WireGuardKitC
#endif
/// The class describing a private key used by WireGuard.
public class PrivateKey: BaseKey {
/// Derived public key
public var publicKey: PublicKey {
return rawValue.withUnsafeBytes { (privateKeyBufferPointer: UnsafeRawBufferPointer) -> PublicKey in
var publicKeyData = Data(repeating: 0, count: Int(WG_KEY_LEN))
let privateKeyBytes = privateKeyBufferPointer.baseAddress!.assumingMemoryBound(to: UInt8.self)
publicKeyData.withUnsafeMutableBytes { (publicKeyBufferPointer: UnsafeMutableRawBufferPointer) in
let publicKeyBytes = publicKeyBufferPointer.baseAddress!.assumingMemoryBound(to: UInt8.self)
curve25519_derive_public_key(publicKeyBytes, privateKeyBytes)
}
return PublicKey(rawValue: publicKeyData)!
}
}
/// Initialize new private key
convenience public init() {
var privateKeyData = Data(repeating: 0, count: Int(WG_KEY_LEN))
privateKeyData.withUnsafeMutableBytes { (rawBufferPointer: UnsafeMutableRawBufferPointer) in
let privateKeyBytes = rawBufferPointer.baseAddress!.assumingMemoryBound(to: UInt8.self)
curve25519_generate_private_key(privateKeyBytes)
}
self.init(rawValue: privateKeyData)!
}
}
/// The class describing a public key used by WireGuard.
public class PublicKey: BaseKey {}
/// The class describing a pre-shared key used by WireGuard.
public class PreSharedKey: BaseKey {}
/// The base key implementation. Should not be used directly.
public class BaseKey: RawRepresentable, Equatable, Hashable {
/// Raw key representation
public let rawValue: Data
/// Hex encoded representation
public var hexKey: String {
return rawValue.withUnsafeBytes { (rawBufferPointer: UnsafeRawBufferPointer) -> String in
let inBytes = rawBufferPointer.baseAddress!.assumingMemoryBound(to: UInt8.self)
var outBytes = [CChar](repeating: 0, count: Int(WG_KEY_LEN_HEX))
key_to_hex(&outBytes, inBytes)
return String(cString: outBytes, encoding: .ascii)!
}
}
/// Base64 encoded representation
public var base64Key: String {
return rawValue.withUnsafeBytes { (rawBufferPointer: UnsafeRawBufferPointer) -> String in
let inBytes = rawBufferPointer.baseAddress!.assumingMemoryBound(to: UInt8.self)
var outBytes = [CChar](repeating: 0, count: Int(WG_KEY_LEN_BASE64))
key_to_base64(&outBytes, inBytes)
return String(cString: outBytes, encoding: .ascii)!
}
}
/// Initialize the key with existing raw representation
required public init?(rawValue: Data) {
if rawValue.count == WG_KEY_LEN {
self.rawValue = rawValue
} else {
return nil
}
}
/// Initialize the key with hex representation
public convenience init?(hexKey: String) {
var bytes = Data(repeating: 0, count: Int(WG_KEY_LEN))
let success = bytes.withUnsafeMutableBytes { (bufferPointer: UnsafeMutableRawBufferPointer) -> Bool in
return key_from_hex(bufferPointer.baseAddress!.assumingMemoryBound(to: UInt8.self), hexKey)
}
if success {
self.init(rawValue: bytes)
} else {
return nil
}
}
/// Initialize the key with base64 representation
public convenience init?(base64Key: String) {
var bytes = Data(repeating: 0, count: Int(WG_KEY_LEN))
let success = bytes.withUnsafeMutableBytes { (bufferPointer: UnsafeMutableRawBufferPointer) -> Bool in
return key_from_base64(bufferPointer.baseAddress!.assumingMemoryBound(to: UInt8.self), base64Key)
}
if success {
self.init(rawValue: bytes)
} else {
return nil
}
}
public static func == (lhs: BaseKey, rhs: BaseKey) -> Bool {
return lhs.rawValue.withUnsafeBytes { (lhsBytes: UnsafeRawBufferPointer) -> Bool in
return rhs.rawValue.withUnsafeBytes { (rhsBytes: UnsafeRawBufferPointer) -> Bool in
return key_eq(
lhsBytes.baseAddress!.assumingMemoryBound(to: UInt8.self),
rhsBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
)
}
}
}
}
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
import Foundation
public final class TunnelConfiguration {
public var name: String?
public var interface: InterfaceConfiguration
public let peers: [PeerConfiguration]
public init(name: String?, interface: InterfaceConfiguration, peers: [PeerConfiguration]) {
self.interface = interface
self.peers = peers
self.name = name
let peerPublicKeysArray = peers.map { $0.publicKey }
let peerPublicKeysSet = Set<PublicKey>(peerPublicKeysArray)
if peerPublicKeysArray.count != peerPublicKeysSet.count {
fatalError("Two or more peers cannot have the same public key")
}
}
}
extension TunnelConfiguration: Equatable {
public static func == (lhs: TunnelConfiguration, rhs: TunnelConfiguration) -> Bool {
return lhs.name == rhs.name &&
lhs.interface == rhs.interface &&
Set(lhs.peers) == Set(rhs.peers)
}
}