aboutsummaryrefslogtreecommitdiffstats
path: root/WireGuard/Shared/Model
diff options
context:
space:
mode:
authorEric Kuck <eric@bluelinelabs.com>2018-12-20 22:52:45 -0600
committerJason A. Donenfeld <Jason@zx2c4.com>2018-12-21 16:42:16 +0100
commit8553723e04c4d63b99e669df2e43fc4a914b7f9d (patch)
treee3b7b25777e47455a787fa590cf00d1c25cbdc5d /WireGuard/Shared/Model
parentNE: simplify logic (diff)
downloadwireguard-apple-8553723e04c4d63b99e669df2e43fc4a914b7f9d.tar.xz
wireguard-apple-8553723e04c4d63b99e669df2e43fc4a914b7f9d.zip
Updated NETunnelProvider save format
Signed-off-by: Eric Kuck <eric@bluelinelabs.com>
Diffstat (limited to 'WireGuard/Shared/Model')
-rw-r--r--WireGuard/Shared/Model/Configuration.swift68
-rw-r--r--WireGuard/Shared/Model/DNSServer.swift62
-rw-r--r--WireGuard/Shared/Model/Endpoint.swift68
-rw-r--r--WireGuard/Shared/Model/IPAddressRange.swift63
-rw-r--r--WireGuard/Shared/Model/InterfaceConfiguration.swift59
-rw-r--r--WireGuard/Shared/Model/Legacy/LegacyDNSServer.swift46
-rw-r--r--WireGuard/Shared/Model/Legacy/LegacyEndpoint.swift64
-rw-r--r--WireGuard/Shared/Model/Legacy/LegacyIPAddressRange.swift57
-rw-r--r--WireGuard/Shared/Model/Legacy/LegacyInterfaceConfiguration.swift24
-rw-r--r--WireGuard/Shared/Model/Legacy/LegacyPeerConfiguration.swift29
-rw-r--r--WireGuard/Shared/Model/Legacy/LegacyTunnelConfiguration.swift15
-rw-r--r--WireGuard/Shared/Model/PeerConfiguration.swift66
-rw-r--r--WireGuard/Shared/Model/TunnelConfiguration.swift42
13 files changed, 494 insertions, 169 deletions
diff --git a/WireGuard/Shared/Model/Configuration.swift b/WireGuard/Shared/Model/Configuration.swift
deleted file mode 100644
index 77dfe974..00000000
--- a/WireGuard/Shared/Model/Configuration.swift
+++ /dev/null
@@ -1,68 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright © 2018 WireGuard LLC. All Rights Reserved.
-
-import Foundation
-
-@available(OSX 10.14, iOS 12.0, *)
-final class TunnelConfiguration: Codable {
- var interface: InterfaceConfiguration
- let peers: [PeerConfiguration]
-
- static let keyLength: Int = 32
-
- init(interface: InterfaceConfiguration, peers: [PeerConfiguration]) {
- self.interface = interface
- self.peers = peers
-
- let peerPublicKeysArray = peers.map { $0.publicKey }
- let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
- if peerPublicKeysArray.count != peerPublicKeysSet.count {
- fatalError("Two or more peers cannot have the same public key")
- }
- }
-}
-
-@available(OSX 10.14, iOS 12.0, *)
-struct InterfaceConfiguration: Codable {
- var name: String
- var privateKey: Data
- var addresses = [IPAddressRange]()
- var listenPort: UInt16?
- var mtu: UInt16?
- var dns = [DNSServer]()
-
- init(name: String, privateKey: Data) {
- self.name = name
- self.privateKey = privateKey
- if name.isEmpty {
- fatalError("Empty name")
- }
- if privateKey.count != TunnelConfiguration.keyLength {
- fatalError("Invalid private key")
- }
- }
-}
-
-@available(OSX 10.14, iOS 12.0, *)
-struct PeerConfiguration: Codable {
- var publicKey: Data
- var preSharedKey: Data? {
- didSet(value) {
- if let value = value {
- if value.count != TunnelConfiguration.keyLength {
- fatalError("Invalid preshared key")
- }
- }
- }
- }
- var allowedIPs = [IPAddressRange]()
- var endpoint: Endpoint?
- var persistentKeepAlive: UInt16?
-
- init(publicKey: Data) {
- self.publicKey = publicKey
- if publicKey.count != TunnelConfiguration.keyLength {
- fatalError("Invalid public key")
- }
- }
-}
diff --git a/WireGuard/Shared/Model/DNSServer.swift b/WireGuard/Shared/Model/DNSServer.swift
index d1c75fc0..8703fbba 100644
--- a/WireGuard/Shared/Model/DNSServer.swift
+++ b/WireGuard/Shared/Model/DNSServer.swift
@@ -4,52 +4,50 @@
import Foundation
import Network
-@available(OSX 10.14, iOS 12.0, *)
struct DNSServer {
let address: IPAddress
-}
-
-// MARK: Converting to and from String
-
-extension DNSServer {
- init?(from addressString: String) {
- if let addr = IPv4Address(addressString) {
- address = addr
- } else if let addr = IPv6Address(addressString) {
- address = addr
- } else {
- return nil
- }
- }
- func stringRepresentation() -> String {
- return "\(address)"
+
+ init(address: IPAddress) {
+ self.address = address
}
}
-// MARK: Codable
-
-@available(OSX 10.14, iOS 12.0, *)
extension DNSServer: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
- try container.encode(address.rawValue)
+ try container.encode(stringRepresentation)
}
+
public init(from decoder: Decoder) throws {
- let container = try decoder.singleValueContainer()
- var data = try container.decode(Data.self)
- let ipAddressFromData: IPAddress? = {
- switch data.count {
- case 4: return IPv4Address(data)
- case 16: return IPv6Address(data)
- default: return nil
- }
- }()
- guard let ipAddress = ipAddressFromData else {
+ let values = try decoder.singleValueContainer()
+ let addressString = try values.decode(String.self)
+
+ if let address = IPv4Address(addressString) {
+ self.address = address
+ } else if let address = IPv6Address(addressString) {
+ self.address = address
+ } else {
throw DecodingError.invalidData
}
- address = ipAddress
}
+
enum DecodingError: Error {
case invalidData
}
}
+
+extension DNSServer {
+ var stringRepresentation: String {
+ return "\(address)"
+ }
+
+ init?(from addressString: String) {
+ if let addr = IPv4Address(addressString) {
+ address = addr
+ } else if let addr = IPv6Address(addressString) {
+ address = addr
+ } else {
+ return nil
+ }
+ }
+}
diff --git a/WireGuard/Shared/Model/Endpoint.swift b/WireGuard/Shared/Model/Endpoint.swift
index 3a4beee7..891c5642 100644
--- a/WireGuard/Shared/Model/Endpoint.swift
+++ b/WireGuard/Shared/Model/Endpoint.swift
@@ -4,15 +4,48 @@
import Foundation
import Network
-@available(OSX 10.14, iOS 12.0, *)
struct Endpoint {
let host: NWEndpoint.Host
let port: NWEndpoint.Port
+
+ init(host: NWEndpoint.Host, port: NWEndpoint.Port) {
+ self.host = host
+ self.port = port
+ }
}
-// MARK: Converting to and from String
+extension Endpoint: Codable {
+ init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ let endpointString = try container.decode(String.self)
+ guard let endpoint = Endpoint(from: endpointString) else {
+ throw DecodingError.invalidData
+ }
+ self = endpoint
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ try container.encode(stringRepresentation)
+ }
+
+ enum DecodingError: Error {
+ case invalidData
+ }
+}
extension Endpoint {
+ 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)"
+ }
+ }
+
init?(from string: String) {
// Separation of host and port is based on 'parse_endpoint' function in
// https://git.zx2c4.com/WireGuard/tree/src/tools/config.c
@@ -41,37 +74,6 @@ extension Endpoint {
host = NWEndpoint.Host(hostString)
port = endpointPort
}
- func 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)"
- }
- }
-}
-
-// MARK: Codable
-
-@available(OSX 10.14, iOS 12.0, *)
-extension Endpoint: Codable {
- public func encode(to encoder: Encoder) throws {
- var container = encoder.singleValueContainer()
- try container.encode(stringRepresentation())
- }
- public init(from decoder: Decoder) throws {
- let container = try decoder.singleValueContainer()
- let endpointString = try container.decode(String.self)
- guard let endpoint = Endpoint(from: endpointString) else {
- throw DecodingError.invalidData
- }
- self = endpoint
- }
- enum DecodingError: Error {
- case invalidData
- }
}
extension Endpoint {
diff --git a/WireGuard/Shared/Model/IPAddressRange.swift b/WireGuard/Shared/Model/IPAddressRange.swift
index 0098c32c..da4cbd5e 100644
--- a/WireGuard/Shared/Model/IPAddressRange.swift
+++ b/WireGuard/Shared/Model/IPAddressRange.swift
@@ -4,16 +4,28 @@
import Foundation
import Network
-@available(OSX 10.14, iOS 12.0, *)
struct IPAddressRange {
let address: IPAddress
var networkPrefixLength: UInt8
+
+ init(address: IPAddress, networkPrefixLength: UInt8) {
+ self.address = address
+ self.networkPrefixLength = networkPrefixLength
+ }
}
-// MARK: Converting to and from String
-
extension IPAddressRange {
+ var stringRepresentation: String {
+ return "\(address)/\(networkPrefixLength)"
+ }
+
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
@@ -24,7 +36,8 @@ extension IPAddressRange {
} else {
return nil
}
- let maxNetworkPrefixLength: UInt8 = (address is IPv4Address) ? 32 : 128
+
+ let maxNetworkPrefixLength: UInt8 = address is IPv4Address ? 32 : 128
var networkPrefixLength: UInt8
if endOfIPAddress < string.endIndex { // "/" was located
let indexOfNetworkPrefixLength = string.index(after: endOfIPAddress)
@@ -35,47 +48,25 @@ extension IPAddressRange {
} else {
networkPrefixLength = maxNetworkPrefixLength
}
- self.address = address
- self.networkPrefixLength = networkPrefixLength
- }
- func stringRepresentation() -> String {
- return "\(address)/\(networkPrefixLength)"
+
+ return (address, networkPrefixLength)
}
}
-// MARK: Codable
-
-@available(OSX 10.14, iOS 12.0, *)
extension IPAddressRange: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
- let addressDataLength: Int
- if address is IPv4Address {
- addressDataLength = 4
- } else if address is IPv6Address {
- addressDataLength = 16
- } else {
- fatalError()
- }
- var data = Data(capacity: addressDataLength + 1)
- data.append(address.rawValue)
- data.append(networkPrefixLength)
- try container.encode(data)
+ try container.encode(stringRepresentation)
}
+
public init(from decoder: Decoder) throws {
- let container = try decoder.singleValueContainer()
- var data = try container.decode(Data.self)
- networkPrefixLength = data.removeLast()
- let ipAddressFromData: IPAddress? = {
- switch data.count {
- case 4: return IPv4Address(data)
- case 16: return IPv6Address(data)
- default: return nil
- }
- }()
- guard let ipAddress = ipAddressFromData else { throw DecodingError.invalidData }
- address = ipAddress
+ let values = try decoder.singleValueContainer()
+ let addressString = try values.decode(String.self)
+ guard let parsed = IPAddressRange.parseAddressString(addressString) else { throw DecodingError.invalidData }
+ address = parsed.0
+ networkPrefixLength = parsed.1
}
+
enum DecodingError: Error {
case invalidData
}
diff --git a/WireGuard/Shared/Model/InterfaceConfiguration.swift b/WireGuard/Shared/Model/InterfaceConfiguration.swift
new file mode 100644
index 00000000..dfcb1fc7
--- /dev/null
+++ b/WireGuard/Shared/Model/InterfaceConfiguration.swift
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+
+struct InterfaceConfiguration {
+ var name: String
+ var privateKey: Data
+ var addresses = [IPAddressRange]()
+ var listenPort: UInt16?
+ var mtu: UInt16?
+ var dns = [DNSServer]()
+
+ init(name: String, privateKey: Data) {
+ self.name = name
+ self.privateKey = privateKey
+ if name.isEmpty {
+ fatalError("Empty name")
+ }
+ if privateKey.count != TunnelConfiguration.keyLength {
+ fatalError("Invalid private key")
+ }
+ }
+}
+
+extension InterfaceConfiguration: Codable {
+ enum CodingKeys: String, CodingKey {
+ case name = "Name"
+ case privateKey = "PrivateKey"
+ case addresses = "Address"
+ case listenPort = "ListenPort"
+ case mtu = "MTU"
+ case dns = "DNS"
+ }
+
+ init(from decoder: Decoder) throws {
+ let values = try decoder.container(keyedBy: CodingKeys.self)
+ name = try values.decode(String.self, forKey: .name)
+ privateKey = try Data(base64Encoded: values.decode(String.self, forKey: .privateKey))!
+ addresses = try values.decode([IPAddressRange].self, forKey: .addresses)
+ listenPort = try? values.decode(UInt16.self, forKey: .listenPort)
+ mtu = try? values.decode(UInt16.self, forKey: .mtu)
+ dns = try values.decode([DNSServer].self, forKey: .dns)
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(name, forKey: .name)
+ try container.encode(privateKey.base64EncodedString(), forKey: .privateKey)
+ try container.encode(addresses, forKey: .addresses)
+ if let listenPort = listenPort {
+ try container.encode(listenPort, forKey: .listenPort)
+ }
+ if let mtu = mtu {
+ try container.encode(mtu, forKey: .mtu)
+ }
+ try container.encode(dns, forKey: .dns)
+ }
+}
diff --git a/WireGuard/Shared/Model/Legacy/LegacyDNSServer.swift b/WireGuard/Shared/Model/Legacy/LegacyDNSServer.swift
new file mode 100644
index 00000000..f2afcd7a
--- /dev/null
+++ b/WireGuard/Shared/Model/Legacy/LegacyDNSServer.swift
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+import Network
+
+struct LegacyDNSServer: Codable {
+ let address: IPAddress
+
+ init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ var data = try container.decode(Data.self)
+ let ipAddressFromData: IPAddress? = {
+ switch data.count {
+ case 4: return IPv4Address(data)
+ case 16: return IPv6Address(data)
+ default: return nil
+ }
+ }()
+ guard let ipAddress = ipAddressFromData else {
+ throw DecodingError.invalidData
+ }
+ address = ipAddress
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ try container.encode(address.rawValue)
+ }
+
+ enum DecodingError: Error {
+ case invalidData
+ }
+}
+
+extension LegacyDNSServer {
+ var migrated: DNSServer {
+ return DNSServer(address: address)
+ }
+}
+
+extension Array where Element == LegacyDNSServer {
+ var migrated: [DNSServer] {
+ return map { $0.migrated }
+ }
+}
diff --git a/WireGuard/Shared/Model/Legacy/LegacyEndpoint.swift b/WireGuard/Shared/Model/Legacy/LegacyEndpoint.swift
new file mode 100644
index 00000000..7a80be47
--- /dev/null
+++ b/WireGuard/Shared/Model/Legacy/LegacyEndpoint.swift
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+import Network
+
+struct LegacyEndpoint: Codable {
+ let host: NWEndpoint.Host
+ let port: NWEndpoint.Port
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ let endpointString = try container.decode(String.self)
+ guard !endpointString.isEmpty else { throw DecodingError.invalidData }
+ let startOfPort: String.Index
+ let hostString: String
+ if endpointString.first! == "[" {
+ // Look for IPv6-style endpoint, like [::1]:80
+ let startOfHost = endpointString.index(after: endpointString.startIndex)
+ guard let endOfHost = endpointString.dropFirst().firstIndex(of: "]") else { throw DecodingError.invalidData }
+ let afterEndOfHost = endpointString.index(after: endOfHost)
+ guard endpointString[afterEndOfHost] == ":" else { throw DecodingError.invalidData }
+ startOfPort = endpointString.index(after: afterEndOfHost)
+ hostString = String(endpointString[startOfHost ..< endOfHost])
+ } else {
+ // Look for an IPv4-style endpoint, like 127.0.0.1:80
+ guard let endOfHost = endpointString.firstIndex(of: ":") else { throw DecodingError.invalidData }
+ startOfPort = endpointString.index(after: endOfHost)
+ hostString = String(endpointString[endpointString.startIndex ..< endOfHost])
+ }
+ guard let endpointPort = NWEndpoint.Port(String(endpointString[startOfPort ..< endpointString.endIndex])) else { throw DecodingError.invalidData }
+ let invalidCharacterIndex = hostString.unicodeScalars.firstIndex { char in
+ return !CharacterSet.urlHostAllowed.contains(char)
+ }
+ guard invalidCharacterIndex == nil else { throw DecodingError.invalidData }
+ host = NWEndpoint.Host(hostString)
+ port = endpointPort
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ let stringRepresentation: String
+ switch host {
+ case .name(let hostname, _):
+ stringRepresentation = "\(hostname):\(port)"
+ case .ipv4(let address):
+ stringRepresentation = "\(address):\(port)"
+ case .ipv6(let address):
+ stringRepresentation = "[\(address)]:\(port)"
+ }
+
+ var container = encoder.singleValueContainer()
+ try container.encode(stringRepresentation)
+ }
+
+ enum DecodingError: Error {
+ case invalidData
+ }
+}
+
+extension LegacyEndpoint {
+ var migrated: Endpoint {
+ return Endpoint(host: host, port: port)
+ }
+}
diff --git a/WireGuard/Shared/Model/Legacy/LegacyIPAddressRange.swift b/WireGuard/Shared/Model/Legacy/LegacyIPAddressRange.swift
new file mode 100644
index 00000000..ade87f21
--- /dev/null
+++ b/WireGuard/Shared/Model/Legacy/LegacyIPAddressRange.swift
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+import Network
+
+struct LegacyIPAddressRange: Codable {
+ let address: IPAddress
+ let networkPrefixLength: UInt8
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.singleValueContainer()
+ var data = try container.decode(Data.self)
+ networkPrefixLength = data.removeLast()
+ let ipAddressFromData: IPAddress? = {
+ switch data.count {
+ case 4: return IPv4Address(data)
+ case 16: return IPv6Address(data)
+ default: return nil
+ }
+ }()
+ guard let ipAddress = ipAddressFromData else { throw DecodingError.invalidData }
+ address = ipAddress
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.singleValueContainer()
+ let addressDataLength: Int
+ if address is IPv4Address {
+ addressDataLength = 4
+ } else if address is IPv6Address {
+ addressDataLength = 16
+ } else {
+ fatalError()
+ }
+ var data = Data(capacity: addressDataLength + 1)
+ data.append(address.rawValue)
+ data.append(networkPrefixLength)
+ try container.encode(data)
+ }
+
+ enum DecodingError: Error {
+ case invalidData
+ }
+}
+
+extension LegacyIPAddressRange {
+ var migrated: IPAddressRange {
+ return IPAddressRange(address: address, networkPrefixLength: networkPrefixLength)
+ }
+}
+
+extension Array where Element == LegacyIPAddressRange {
+ var migrated: [IPAddressRange] {
+ return map { $0.migrated }
+ }
+}
diff --git a/WireGuard/Shared/Model/Legacy/LegacyInterfaceConfiguration.swift b/WireGuard/Shared/Model/Legacy/LegacyInterfaceConfiguration.swift
new file mode 100644
index 00000000..680c8d74
--- /dev/null
+++ b/WireGuard/Shared/Model/Legacy/LegacyInterfaceConfiguration.swift
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+
+struct LegacyInterfaceConfiguration: Codable {
+ let name: String
+ let privateKey: Data
+ let addresses: [LegacyIPAddressRange]
+ let listenPort: UInt16?
+ let mtu: UInt16?
+ let dns: [LegacyDNSServer]
+}
+
+extension LegacyInterfaceConfiguration {
+ var migrated: InterfaceConfiguration {
+ var interface = InterfaceConfiguration(name: name, privateKey: privateKey)
+ interface.addresses = addresses.migrated
+ interface.listenPort = listenPort
+ interface.mtu = mtu
+ interface.dns = dns.migrated
+ return interface
+ }
+}
diff --git a/WireGuard/Shared/Model/Legacy/LegacyPeerConfiguration.swift b/WireGuard/Shared/Model/Legacy/LegacyPeerConfiguration.swift
new file mode 100644
index 00000000..5a61b4ad
--- /dev/null
+++ b/WireGuard/Shared/Model/Legacy/LegacyPeerConfiguration.swift
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+
+struct LegacyPeerConfiguration: Codable {
+ let publicKey: Data
+ let preSharedKey: Data?
+ let allowedIPs: [LegacyIPAddressRange]
+ let endpoint: LegacyEndpoint?
+ let persistentKeepAlive: UInt16?
+}
+
+extension LegacyPeerConfiguration {
+ var migrated: PeerConfiguration {
+ var configuration = PeerConfiguration(publicKey: publicKey)
+ configuration.preSharedKey = preSharedKey
+ configuration.allowedIPs = allowedIPs.migrated
+ configuration.endpoint = endpoint?.migrated
+ configuration.persistentKeepAlive = persistentKeepAlive
+ return configuration
+ }
+}
+
+extension Array where Element == LegacyPeerConfiguration {
+ var migrated: [PeerConfiguration] {
+ return map { $0.migrated }
+ }
+}
diff --git a/WireGuard/Shared/Model/Legacy/LegacyTunnelConfiguration.swift b/WireGuard/Shared/Model/Legacy/LegacyTunnelConfiguration.swift
new file mode 100644
index 00000000..ac369b94
--- /dev/null
+++ b/WireGuard/Shared/Model/Legacy/LegacyTunnelConfiguration.swift
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+
+final class LegacyTunnelConfiguration: Codable {
+ let interface: LegacyInterfaceConfiguration
+ let peers: [LegacyPeerConfiguration]
+}
+
+extension LegacyTunnelConfiguration {
+ var migrated: TunnelConfiguration {
+ return TunnelConfiguration(interface: interface.migrated, peers: peers.migrated)
+ }
+}
diff --git a/WireGuard/Shared/Model/PeerConfiguration.swift b/WireGuard/Shared/Model/PeerConfiguration.swift
new file mode 100644
index 00000000..0fad842a
--- /dev/null
+++ b/WireGuard/Shared/Model/PeerConfiguration.swift
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+
+struct PeerConfiguration {
+ var publicKey: Data
+ var preSharedKey: Data? {
+ didSet(value) {
+ if let value = value {
+ if value.count != TunnelConfiguration.keyLength {
+ fatalError("Invalid preshared key")
+ }
+ }
+ }
+ }
+ var allowedIPs = [IPAddressRange]()
+ var endpoint: Endpoint?
+ var persistentKeepAlive: UInt16?
+
+ init(publicKey: Data) {
+ self.publicKey = publicKey
+ if publicKey.count != TunnelConfiguration.keyLength {
+ fatalError("Invalid public key")
+ }
+ }
+}
+
+extension PeerConfiguration: Codable {
+ enum CodingKeys: String, CodingKey {
+ case publicKey = "PublicKey"
+ case preSharedKey = "PreSharedKey"
+ case allowedIPs = "AllowedIPs"
+ case endpoint = "Endpoint"
+ case persistentKeepAlive = "PersistentKeepAlive"
+ }
+
+ init(from decoder: Decoder) throws {
+ let values = try decoder.container(keyedBy: CodingKeys.self)
+ publicKey = try Data(base64Encoded: values.decode(String.self, forKey: .publicKey))!
+ if let base64PreSharedKey = try? values.decode(Data.self, forKey: .preSharedKey) {
+ preSharedKey = Data(base64Encoded: base64PreSharedKey)
+ } else {
+ preSharedKey = nil
+ }
+ allowedIPs = try values.decode([IPAddressRange].self, forKey: .allowedIPs)
+ endpoint = try? values.decode(Endpoint.self, forKey: .endpoint)
+ persistentKeepAlive = try? values.decode(UInt16.self, forKey: .persistentKeepAlive)
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(publicKey.base64EncodedString(), forKey: .publicKey)
+ if let preSharedKey = preSharedKey {
+ try container.encode(preSharedKey.base64EncodedString(), forKey: .preSharedKey)
+ }
+
+ try container.encode(allowedIPs, forKey: .allowedIPs)
+ if let endpoint = endpoint {
+ try container.encode(endpoint, forKey: .endpoint)
+ }
+ if let persistentKeepAlive = persistentKeepAlive {
+ try container.encode(persistentKeepAlive, forKey: .persistentKeepAlive)
+ }
+ }
+}
diff --git a/WireGuard/Shared/Model/TunnelConfiguration.swift b/WireGuard/Shared/Model/TunnelConfiguration.swift
new file mode 100644
index 00000000..87812cde
--- /dev/null
+++ b/WireGuard/Shared/Model/TunnelConfiguration.swift
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: MIT
+// Copyright © 2018 WireGuard LLC. All Rights Reserved.
+
+import Foundation
+
+final class TunnelConfiguration {
+ var interface: InterfaceConfiguration
+ let peers: [PeerConfiguration]
+
+ static let keyLength = 32
+
+ init(interface: InterfaceConfiguration, peers: [PeerConfiguration]) {
+ self.interface = interface
+ self.peers = peers
+
+ let peerPublicKeysArray = peers.map { $0.publicKey }
+ let peerPublicKeysSet = Set<Data>(peerPublicKeysArray)
+ if peerPublicKeysArray.count != peerPublicKeysSet.count {
+ fatalError("Two or more peers cannot have the same public key")
+ }
+ }
+}
+
+extension TunnelConfiguration: Codable {
+ enum CodingKeys: String, CodingKey {
+ case interface = "Interface"
+ case peers = "Peer"
+ }
+
+ convenience init(from decoder: Decoder) throws {
+ let values = try decoder.container(keyedBy: CodingKeys.self)
+ let interface = try values.decode(InterfaceConfiguration.self, forKey: .interface)
+ let peers = try values.decode([PeerConfiguration].self, forKey: .peers)
+ self.init(interface: interface, peers: peers)
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(interface, forKey: .interface)
+ try container.encode(peers, forKey: .peers)
+ }
+}