Skip to content
This repository was archived by the owner on Sep 29, 2024. It is now read-only.

Add Complete XOR Patch Functionality #255

Merged
merged 15 commits into from
Nov 6, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"repositoryURL": "https://github.com/passepartoutvpn/wireguard-apple",
"state": {
"branch": null,
"revision": "b4f74b7bcba9004a1852e615298f9cbc68fb7f67",
"revision": "d3b8f1ac6f3361d69bd3daf8aee3c43012c6ec0b",
"version": "1.0.16"
}
}
Expand Down
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,20 @@ TunnelKit can parse .ovpn configuration files. Below are a few details worth men

#### Non-standard

- Single-byte XOR masking
- Via `--scramble xormask <character>`
- XOR all incoming and outgoing bytes by the ASCII value of the character argument
- See [Tunnelblick website][about-tunnelblick-xor] for more details
- XOR-patch functionality:
- Multi-byte XOR Masking
- Via `--scramble xormask <passphrase>`
- XOR all incoming and outgoing bytes by the passphrase given
- XOR Position Masking
- Via `--scramble xorptrpos`
- XOR all bytes by their position in the array
- Packet Reverse Scramble
- Via `--scramble reverse`
- Keeps the first byte and reverses the rest of the array
- XOR Scramble Obfuscate
- Via `--scramble obfuscate <passphrase>`
- Performs a combination of the three above (specifically `xormask <passphrase>` -> `xorptrpos` -> `reverse` -> `xorptrpos` for reading, and the opposite for writing)
- See [Tunnelblick website][about-tunnelblick-xor] for more details (Patch was written in accordance with Tunnelblick's patch for compatibility)

#### Unsupported

Expand Down Expand Up @@ -217,6 +227,7 @@ For more details please see [CONTRIBUTING][contrib-readme].
- [SURFnet][ppl-surfnet]
- [SwiftyBeaver][dep-swiftybeaver-repo] - Copyright (c) 2015 Sebastian Kreutzberger
- [XMB5][ppl-xmb5] for the [XOR patch][ppl-xmb5-xor] - Copyright (c) 2020 Sam Foxman
- [tmthecoder][ppl-tmthecoder] for the complete [XOR patch][ppl-tmthecoder-xor] - Copyright (c) 2022 Tejas Mehta
- [eduVPN][ppl-eduvpn] for the convenient WireGuardKitGo script

### OpenVPN
Expand Down Expand Up @@ -259,6 +270,8 @@ Website: [passepartoutvpn.app][about-website]
[ppl-surfnet]: https://www.surf.nl/en/about-surf/subsidiaries/surfnet
[ppl-xmb5]: https://github.com/XMB5
[ppl-xmb5-xor]: https://github.com/passepartoutvpn/tunnelkit/pull/170
[ppl-tmthecoder]: https://github.com/tmthecoder
[ppl-tmthecoder-xor]: https://github.com/passepartoutvpn/tunnelkit/pull/255
[ppl-eduvpn]: https://github.com/eduvpn/apple
[about-tunnelblick-xor]: https://tunnelblick.net/cOpenvpn_xorpatch.html
[about-pr-bitcode]: https://github.com/passepartoutvpn/tunnelkit/issues/51
Expand Down
85 changes: 70 additions & 15 deletions Sources/CTunnelKitOpenVPNProtocol/PacketStream.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,77 @@

@implementation PacketStream

+ (void)memcpyXor:(uint8_t *)dst src:(NSData *)src xorMask:(uint8_t)xorMask
+ (void)xormask:(uint8_t *)dst src:(uint8_t *)src xorMask:(NSData *)xorMask length:(int)length
{
if (xorMask != 0) {
for (int i = 0; i < src.length; ++i) {
dst[i] = ((uint8_t *)(src.bytes))[i] ^ xorMask;
if (xorMask.length > 0) {
for (int i = 0; i < length; ++i) {
dst[i] = src[i] ^ ((uint8_t *)(xorMask.bytes))[i % xorMask.length];
}
return;
}
memcpy(dst, src.bytes, src.length);
memcpy(dst, src, length);
}

+ (NSArray<NSData *> *)packetsFromStream:(NSData *)stream until:(NSInteger *)until xorMask:(uint8_t)xorMask
+ (void)xorptrpos:(uint8_t *)dst src:(uint8_t *)src length:(int)length
{
for (int i = 0; i < length; ++i) {
dst[i] = src[i] ^ (i + 1);
}
}

+ (void)reverse:(uint8_t *)dst src:(uint8_t *)src length:(int)length
{
int start = 1;
int end = length - 1;
uint8_t temp = 0;
dst[0] = src[0];
while (start < end) {
temp = src[start];
dst[start] = src[end];
dst[end] = temp;
start++;
end--;
}
if (start == end) {
dst[start] = src[start];
}
}

+ (void)memcpyXor:(uint8_t *)dst src:(NSData *)src xorMask:(NSData *)xorMask xorMethod:(int)xorMethod mode:(int)mode
{
uint8_t *source = (uint8_t *) src.bytes;
switch (xorMethod) {
case 0:
memcpy(dst, source, src.length);
break;
case 1:
[PacketStream xormask:dst src:source xorMask:xorMask length:src.length];
break;
case 2:
[PacketStream xorptrpos:dst src:source length:src.length];
break;
case 3:
[PacketStream reverse:dst src:source length:src.length];
break;
case 4:
// 0 = read; 1 = write
if (mode == 0) {
[PacketStream xormask:dst src:source xorMask:xorMask length:src.length];
[PacketStream xorptrpos:dst src:dst length:src.length];
[PacketStream reverse:dst src:dst length:src.length];
[PacketStream xorptrpos:dst src:dst length:src.length];
} else {
[PacketStream xorptrpos:dst src:source length:src.length];
[PacketStream reverse:dst src:dst length:src.length];
[PacketStream xorptrpos:dst src:dst length:src.length];
[PacketStream xormask:dst src:dst xorMask:xorMask length:src.length];
}
default:
break;
}
}

+ (NSArray<NSData *> *)packetsFromStream:(NSData *)stream until:(NSInteger *)until xorMask:(NSData *)xorMask xorMethod:(int)xorMethod mode:(int)mode
{
NSInteger ni = 0;
NSMutableArray<NSData *> *parsed = [[NSMutableArray alloc] init];
Expand All @@ -54,11 +113,7 @@ + (void)memcpyXor:(uint8_t *)dst src:(NSData *)src xorMask:(uint8_t)xorMask
}
NSData *packet = [stream subdataWithRange:NSMakeRange(start, packlen)];
uint8_t* packetBytes = (uint8_t*) packet.bytes;
if (xorMask != 0) {
for (int i = 0; i < packet.length; i++) {
packetBytes[i] ^= xorMask;
}
}
[PacketStream memcpyXor:packetBytes src:packet xorMask:xorMask xorMethod:xorMethod mode:mode];
[parsed addObject:packet];
ni = end;
}
Expand All @@ -68,19 +123,19 @@ + (void)memcpyXor:(uint8_t *)dst src:(NSData *)src xorMask:(uint8_t)xorMask
return parsed;
}

+ (NSData *)streamFromPacket:(NSData *)packet xorMask:(uint8_t)xorMask
+ (NSData *)streamFromPacket:(NSData *)packet xorMask:(NSData *)xorMask xorMethod:(int)xorMethod mode:(int)mode
{
NSMutableData *raw = [[NSMutableData alloc] initWithLength:(PacketStreamHeaderLength + packet.length)];

uint8_t *ptr = raw.mutableBytes;
*(uint16_t *)ptr = CFSwapInt16HostToBig(packet.length);
ptr += PacketStreamHeaderLength;
[PacketStream memcpyXor:ptr src:packet xorMask:xorMask];
[PacketStream memcpyXor:ptr src:packet xorMask:xorMask xorMethod:xorMethod mode:mode];

return raw;
}

+ (NSData *)streamFromPackets:(NSArray<NSData *> *)packets xorMask:(uint8_t)xorMask
+ (NSData *)streamFromPackets:(NSArray<NSData *> *)packets xorMask:(NSData *)xorMask xorMethod:(int)xorMethod mode:(int)mode
{
NSInteger streamLength = 0;
for (NSData *p in packets) {
Expand All @@ -92,7 +147,7 @@ + (NSData *)streamFromPackets:(NSArray<NSData *> *)packets xorMask:(uint8_t)xorM
for (NSData *packet in packets) {
*(uint16_t *)ptr = CFSwapInt16HostToBig(packet.length);
ptr += PacketStreamHeaderLength;
[PacketStream memcpyXor:ptr src:packet xorMask:xorMask];
[PacketStream memcpyXor:ptr src:packet xorMask:xorMask xorMethod:xorMethod mode:mode];
ptr += packet.length;
}
return raw;
Expand Down
6 changes: 3 additions & 3 deletions Sources/CTunnelKitOpenVPNProtocol/include/PacketStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ NS_ASSUME_NONNULL_BEGIN

@interface PacketStream : NSObject

+ (NSArray<NSData *> *)packetsFromStream:(NSData *)stream until:(NSInteger *)until xorMask:(uint8_t)xorMask;
+ (NSData *)streamFromPacket:(NSData *)packet xorMask:(uint8_t)xorMask;
+ (NSData *)streamFromPackets:(NSArray<NSData *> *)packets xorMask:(uint8_t)xorMask;
+ (NSArray<NSData *> *)packetsFromStream:(NSData *)stream until:(NSInteger *)until xorMask:(NSData *)xorMask xorMethod:(int)xorMethod mode:(int)mode;
+ (NSData *)streamFromPacket:(NSData *)packet xorMask:(NSData *)xorMask xorMethod:(int)xorMethod mode:(int)mode;
+ (NSData *)streamFromPackets:(NSArray<NSData *> *)packets xorMask:(NSData *)xorMask xorMethod:(int)xorMethod mode:(int)mode;

@end

Expand Down
2 changes: 1 addition & 1 deletion Sources/TunnelKitAppExtension/LinkProducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ public protocol LinkProducer {

- Parameter xorMask: The XOR mask.
**/
func link(xorMask: UInt8?) -> LinkInterface
func link(xorMask: Data?, xorMethod: Int?) -> LinkInterface
}
2 changes: 1 addition & 1 deletion Sources/TunnelKitCore/LinkInterface.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ public protocol LinkInterface: IOInterface {
var packetBufferSize: Int { get }

/// A byte to xor all packet payloads with.
var xorMask: UInt8 { get }
var xorMask: Data { get }
}
32 changes: 32 additions & 0 deletions Sources/TunnelKitOpenVPNAppExtension/Mode.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// NEUDPLink.swift
// TunnelKit
//
// Created by Tejas Mehta on 5/24/22.
// Copyright (c) 2022 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//

import Foundation

/// Represents whether an operation is reading or writing
enum Mode: Int {
case read = 0
case write = 1
}
18 changes: 10 additions & 8 deletions Sources/TunnelKitOpenVPNAppExtension/NETCPLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ class NETCPLink: LinkInterface {

private let maxPacketSize: Int

let xorMask: UInt8
let xorMask: Data
let xorMethod: Int

init(impl: NWTCPConnection, maxPacketSize: Int? = nil, xorMask: UInt8?) {
init(impl: NWTCPConnection, maxPacketSize: Int? = nil, xorMask: Data?, xorMethod: Int?) {
self.impl = impl
self.maxPacketSize = maxPacketSize ?? (512 * 1024)
self.xorMask = xorMask ?? 0
self.xorMask = xorMask ?? Data(repeating: 0, count: 1)
self.xorMethod = xorMethod ?? 0
}

// MARK: LinkInterface
Expand Down Expand Up @@ -74,7 +76,7 @@ class NETCPLink: LinkInterface {
var newBuffer = buffer
newBuffer.append(contentsOf: data)
var until = 0
let packets = PacketStream.packets(fromStream: newBuffer, until: &until, xorMask: self.xorMask)
let packets = PacketStream.packets(fromStream: newBuffer, until: &until, xorMask: self.xorMask, xorMethod: Int32(self.xorMethod), mode: Int32(Mode.read.rawValue))
newBuffer = newBuffer.subdata(in: until..<newBuffer.count)
self.loopReadPackets(queue, newBuffer, handler)

Expand All @@ -84,22 +86,22 @@ class NETCPLink: LinkInterface {
}

func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
let stream = PacketStream.stream(fromPacket: packet, xorMask: xorMask)
let stream = PacketStream.stream(fromPacket: packet, xorMask: xorMask, xorMethod: Int32(self.xorMethod), mode: Int32(Mode.write.rawValue))
impl.write(stream) { (error) in
completionHandler?(error)
}
}

func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
let stream = PacketStream.stream(fromPackets: packets, xorMask: xorMask)
let stream = PacketStream.stream(fromPackets: packets, xorMask: xorMask, xorMethod: Int32(self.xorMethod), mode: Int32(Mode.write.rawValue))
impl.write(stream) { (error) in
completionHandler?(error)
}
}
}

extension NETCPSocket: LinkProducer {
public func link(xorMask: UInt8?) -> LinkInterface {
return NETCPLink(impl: impl, maxPacketSize: nil, xorMask: xorMask)
public func link(xorMask: Data?, xorMethod: Int?) -> LinkInterface {
return NETCPLink(impl: impl, maxPacketSize: nil, xorMask: xorMask, xorMethod: xorMethod)
}
}
Loading