aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/app/src
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2020-03-09 19:00:14 +0530
committerHarsh Shandilya <me@msfjarvis.dev>2020-03-09 19:24:26 +0530
commitadc613d8011af7c508050badb1272e8326554c39 (patch)
tree4eadedc0767e1f4f482b7c22ec905329acab62a6 /app/src
parentToolsInstaller: restrict to only main app usage (diff)
downloadwireguard-android-adc613d8011af7c508050badb1272e8326554c39.tar.xz
wireguard-android-adc613d8011af7c508050badb1272e8326554c39.zip
Migrate tunnel related classes to tunnel/ Gradle module
Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/AndroidManifest.xml8
-rw-r--r--app/src/main/java/com/wireguard/android/backend/Backend.java63
-rw-r--r--app/src/main/java/com/wireguard/android/backend/BackendException.java30
-rw-r--r--app/src/main/java/com/wireguard/android/backend/GoBackend.java292
-rw-r--r--app/src/main/java/com/wireguard/android/backend/Statistics.java62
-rw-r--r--app/src/main/java/com/wireguard/android/backend/Tunnel.java46
-rw-r--r--app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java158
-rw-r--r--app/src/main/java/com/wireguard/android/util/AsyncWorker.java63
-rw-r--r--app/src/main/java/com/wireguard/android/util/RootShell.java211
-rw-r--r--app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java94
-rw-r--r--app/src/main/java/com/wireguard/android/util/ToolsInstaller.java196
-rw-r--r--app/src/main/java/com/wireguard/config/Attribute.java58
-rw-r--r--app/src/main/java/com/wireguard/config/BadConfigException.java118
-rw-r--r--app/src/main/java/com/wireguard/config/Config.java221
-rw-r--r--app/src/main/java/com/wireguard/config/InetAddresses.java71
-rw-r--r--app/src/main/java/com/wireguard/config/InetEndpoint.java125
-rw-r--r--app/src/main/java/com/wireguard/config/InetNetwork.java76
-rw-r--r--app/src/main/java/com/wireguard/config/Interface.java355
-rw-r--r--app/src/main/java/com/wireguard/config/ParseException.java44
-rw-r--r--app/src/main/java/com/wireguard/config/Peer.java306
-rw-r--r--app/src/main/java/com/wireguard/crypto/Curve25519.java497
-rw-r--r--app/src/main/java/com/wireguard/crypto/Key.java288
-rw-r--r--app/src/main/java/com/wireguard/crypto/KeyFormatException.java34
-rw-r--r--app/src/main/java/com/wireguard/crypto/KeyPair.java51
-rw-r--r--app/src/main/java/com/wireguard/util/NonNullForAll.java26
25 files changed, 0 insertions, 3493 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4bcb7588..5e993ae2 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -74,14 +74,6 @@
</receiver>
<service
- android:name=".backend.GoBackend$VpnService"
- android:permission="android.permission.BIND_VPN_SERVICE">
- <intent-filter>
- <action android:name="android.net.VpnService" />
- </intent-filter>
- </service>
-
- <service
android:name=".QuickTileService"
android:icon="@drawable/ic_tile"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
diff --git a/app/src/main/java/com/wireguard/android/backend/Backend.java b/app/src/main/java/com/wireguard/android/backend/Backend.java
deleted file mode 100644
index ed3a5ebd..00000000
--- a/app/src/main/java/com/wireguard/android/backend/Backend.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.backend;
-
-import com.wireguard.config.Config;
-
-import java.util.Collection;
-import java.util.Set;
-
-import androidx.annotation.Nullable;
-
-/**
- * Interface for implementations of the WireGuard secure network tunnel.
- */
-
-public interface Backend {
- /**
- * Enumerate names of currently-running tunnels.
- *
- * @return The set of running tunnel names.
- */
- Set<String> getRunningTunnelNames();
-
- /**
- * Get the state of a tunnel.
- *
- * @param tunnel The tunnel to examine the state of.
- * @return The state of the tunnel.
- */
- Tunnel.State getState(Tunnel tunnel) throws Exception;
-
- /**
- * Get statistics about traffic and errors on this tunnel. If the tunnel is not running, the
- * statistics object will be filled with zero values.
- *
- * @param tunnel The tunnel to retrieve statistics for.
- * @return The statistics for the tunnel.
- */
- Statistics getStatistics(Tunnel tunnel) throws Exception;
-
- /**
- * Determine version of underlying backend.
- *
- * @return The version of the backend.
- * @throws Exception
- */
- String getVersion() throws Exception;
-
- /**
- * Set the state of a tunnel, updating it's configuration. If the tunnel is already up, config
- * may update the running configuration; config may be null when setting the tunnel down.
- *
- * @param tunnel The tunnel to control the state of.
- * @param state The new state for this tunnel. Must be {@code UP}, {@code DOWN}, or
- * {@code TOGGLE}.
- * @param config The configuration for this tunnel, may be null if state is {@code DOWN}.
- * @return The updated state of the tunnel.
- */
- Tunnel.State setState(Tunnel tunnel, Tunnel.State state, @Nullable Config config) throws Exception;
-}
diff --git a/app/src/main/java/com/wireguard/android/backend/BackendException.java b/app/src/main/java/com/wireguard/android/backend/BackendException.java
deleted file mode 100644
index e1e8eaa9..00000000
--- a/app/src/main/java/com/wireguard/android/backend/BackendException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright © 2020 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.backend;
-
-public final class BackendException extends Exception {
- public enum Reason {
- UNKNOWN_KERNEL_MODULE_NAME,
- WG_QUICK_CONFIG_ERROR_CODE,
- TUNNEL_MISSING_CONFIG,
- VPN_NOT_AUTHORIZED,
- UNABLE_TO_START_VPN,
- TUN_CREATION_ERROR,
- GO_ACTIVATION_ERROR_CODE
- }
- private final Reason reason;
- private final Object[] format;
- public BackendException(final Reason reason, final Object ...format) {
- this.reason = reason;
- this.format = format;
- }
- public Reason getReason() {
- return reason;
- }
- public Object[] getFormat() {
- return format;
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/backend/GoBackend.java b/app/src/main/java/com/wireguard/android/backend/GoBackend.java
deleted file mode 100644
index 6ad5afa4..00000000
--- a/app/src/main/java/com/wireguard/android/backend/GoBackend.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.backend;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.ParcelFileDescriptor;
-import androidx.annotation.Nullable;
-import androidx.collection.ArraySet;
-import android.util.Log;
-
-import com.wireguard.android.backend.BackendException.Reason;
-import com.wireguard.android.backend.Tunnel.State;
-import com.wireguard.android.util.SharedLibraryLoader;
-import com.wireguard.config.Config;
-import com.wireguard.config.InetNetwork;
-import com.wireguard.config.Peer;
-import com.wireguard.crypto.Key;
-import com.wireguard.crypto.KeyFormatException;
-
-import java.net.InetAddress;
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import java9.util.concurrent.CompletableFuture;
-
-public final class GoBackend implements Backend {
- private static final String TAG = "WireGuard/" + GoBackend.class.getSimpleName();
- private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>();
- public interface AlwaysOnCallback {
- void alwaysOnTriggered();
- }
- @Nullable private static AlwaysOnCallback alwaysOnCallback;
- public static void setAlwaysOnCallback(AlwaysOnCallback cb) {
- alwaysOnCallback = cb;
- }
-
- private final Context context;
- @Nullable private Tunnel currentTunnel;
- @Nullable private Config currentConfig;
- private int currentTunnelHandle = -1;
-
- public GoBackend(final Context context) {
- SharedLibraryLoader.loadSharedLibrary(context, "wg-go");
- this.context = context;
- }
-
- private static native String wgGetConfig(int handle);
-
- private static native int wgGetSocketV4(int handle);
-
- private static native int wgGetSocketV6(int handle);
-
- private static native void wgTurnOff(int handle);
-
- private static native int wgTurnOn(String ifName, int tunFd, String settings);
-
- private static native String wgVersion();
-
- @Override
- public Set<String> getRunningTunnelNames() {
- if (currentTunnel != null) {
- final Set<String> runningTunnels = new ArraySet<>();
- runningTunnels.add(currentTunnel.getName());
- return runningTunnels;
- }
- return Collections.emptySet();
- }
-
- @Override
- public State getState(final Tunnel tunnel) {
- return currentTunnel == tunnel ? State.UP : State.DOWN;
- }
-
- @Override
- public Statistics getStatistics(final Tunnel tunnel) {
- final Statistics stats = new Statistics();
- if (tunnel != currentTunnel) {
- return stats;
- }
- final String config = wgGetConfig(currentTunnelHandle);
- Key key = null;
- long rx = 0, tx = 0;
- for (final String line : config.split("\\n")) {
- if (line.startsWith("public_key=")) {
- if (key != null)
- stats.add(key, rx, tx);
- rx = 0;
- tx = 0;
- try {
- key = Key.fromHex(line.substring(11));
- } catch (final KeyFormatException ignored) {
- key = null;
- }
- } else if (line.startsWith("rx_bytes=")) {
- if (key == null)
- continue;
- try {
- rx = Long.parseLong(line.substring(9));
- } catch (final NumberFormatException ignored) {
- rx = 0;
- }
- } else if (line.startsWith("tx_bytes=")) {
- if (key == null)
- continue;
- try {
- tx = Long.parseLong(line.substring(9));
- } catch (final NumberFormatException ignored) {
- tx = 0;
- }
- }
- }
- if (key != null)
- stats.add(key, rx, tx);
- return stats;
- }
-
- @Override
- public String getVersion() {
- return wgVersion();
- }
-
- @Override
- public State setState(final Tunnel tunnel, State state, @Nullable final Config config) throws Exception {
- final State originalState = getState(tunnel);
-
- if (state == State.TOGGLE)
- state = originalState == State.UP ? State.DOWN : State.UP;
- if (state == originalState && tunnel == currentTunnel && config == currentConfig)
- return originalState;
- if (state == State.UP) {
- final Config originalConfig = currentConfig;
- final Tunnel originalTunnel = currentTunnel;
- if (currentTunnel != null)
- setStateInternal(currentTunnel, null, State.DOWN);
- try {
- setStateInternal(tunnel, config, state);
- } catch(final Exception e) {
- if (originalTunnel != null)
- setStateInternal(originalTunnel, originalConfig, State.UP);
- throw e;
- }
- } else if (state == State.DOWN && tunnel == currentTunnel) {
- setStateInternal(tunnel, null, State.DOWN);
- }
- return getState(tunnel);
- }
-
- private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state)
- throws Exception {
- Log.i(TAG, "Bringing tunnel " + tunnel.getName() + " " + state);
-
- if (state == State.UP) {
- if (config == null)
- throw new BackendException(Reason.TUNNEL_MISSING_CONFIG);
-
- if (VpnService.prepare(context) != null)
- throw new BackendException(Reason.VPN_NOT_AUTHORIZED);
-
- final VpnService service;
- if (!vpnService.isDone())
- startVpnService();
-
- try {
- service = vpnService.get(2, TimeUnit.SECONDS);
- } catch (final TimeoutException e) {
- final Exception be = new BackendException(Reason.UNABLE_TO_START_VPN);
- be.initCause(e);
- throw be;
- }
- service.setOwner(this);
-
- if (currentTunnelHandle != -1) {
- Log.w(TAG, "Tunnel already up");
- return;
- }
-
- // Build config
- final String goConfig = config.toWgUserspaceString();
-
- // Create the vpn tunnel with android API
- final VpnService.Builder builder = service.getBuilder();
- builder.setSession(tunnel.getName());
-
- for (final String excludedApplication : config.getInterface().getExcludedApplications())
- builder.addDisallowedApplication(excludedApplication);
-
- for (final InetNetwork addr : config.getInterface().getAddresses())
- builder.addAddress(addr.getAddress(), addr.getMask());
-
- for (final InetAddress addr : config.getInterface().getDnsServers())
- builder.addDnsServer(addr.getHostAddress());
-
- for (final Peer peer : config.getPeers()) {
- for (final InetNetwork addr : peer.getAllowedIps())
- builder.addRoute(addr.getAddress(), addr.getMask());
- }
-
- builder.setMtu(config.getInterface().getMtu().orElse(1280));
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
- builder.setMetered(false);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
- service.setUnderlyingNetworks(null);
-
- builder.setBlocking(true);
- try (final ParcelFileDescriptor tun = builder.establish()) {
- if (tun == null)
- throw new BackendException(Reason.TUN_CREATION_ERROR);
- Log.d(TAG, "Go backend v" + wgVersion());
- currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig);
- }
- if (currentTunnelHandle < 0)
- throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle);
-
- currentTunnel = tunnel;
- currentConfig = config;
-
- service.protect(wgGetSocketV4(currentTunnelHandle));
- service.protect(wgGetSocketV6(currentTunnelHandle));
- } else {
- if (currentTunnelHandle == -1) {
- Log.w(TAG, "Tunnel already down");
- return;
- }
-
- wgTurnOff(currentTunnelHandle);
- currentTunnel = null;
- currentTunnelHandle = -1;
- currentConfig = null;
- }
-
- tunnel.onStateChange(state);
- }
-
- private void startVpnService() {
- Log.d(TAG, "Requesting to start VpnService");
- context.startService(new Intent(context, VpnService.class));
- }
-
- public static class VpnService extends android.net.VpnService {
- @Nullable private GoBackend owner;
-
- public void setOwner(final GoBackend owner) {
- this.owner = owner;
- }
-
- public Builder getBuilder() {
- return new Builder();
- }
-
- @Override
- public void onCreate() {
- vpnService.complete(this);
- super.onCreate();
- }
-
- @Override
- public void onDestroy() {
- if (owner != null) {
- final Tunnel tunnel = owner.currentTunnel;
- if (tunnel != null) {
- if (owner.currentTunnelHandle != -1)
- wgTurnOff(owner.currentTunnelHandle);
- owner.currentTunnel = null;
- owner.currentTunnelHandle = -1;
- owner.currentConfig = null;
- tunnel.onStateChange(State.DOWN);
- }
- }
- vpnService = vpnService.newIncompleteFuture();
- super.onDestroy();
- }
-
- @Override
- public int onStartCommand(@Nullable final Intent intent, final int flags, final int startId) {
- vpnService.complete(this);
- if (intent == null || intent.getComponent() == null || !intent.getComponent().getPackageName().equals(getPackageName())) {
- Log.d(TAG, "Service started by Always-on VPN feature");
- if (alwaysOnCallback != null)
- alwaysOnCallback.alwaysOnTriggered();
- }
- return super.onStartCommand(intent, flags, startId);
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/backend/Statistics.java b/app/src/main/java/com/wireguard/android/backend/Statistics.java
deleted file mode 100644
index 2ca87d23..00000000
--- a/app/src/main/java/com/wireguard/android/backend/Statistics.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright © 2020 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.backend;
-
-import android.os.SystemClock;
-import android.util.Pair;
-
-import com.wireguard.crypto.Key;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class Statistics {
- private long lastTouched = SystemClock.elapsedRealtime();
- private final Map<Key, Pair<Long, Long>> peerBytes = new HashMap<>();
-
- Statistics() { }
-
- void add(final Key key, final long rx, final long tx) {
- peerBytes.put(key, Pair.create(rx, tx));
- lastTouched = SystemClock.elapsedRealtime();
- }
-
- public boolean isStale() {
- return SystemClock.elapsedRealtime() - lastTouched > 900;
- }
-
- public Key[] peers() {
- return peerBytes.keySet().toArray(new Key[0]);
- }
-
- public long peerRx(final Key peer) {
- if (!peerBytes.containsKey(peer))
- return 0;
- return peerBytes.get(peer).first;
- }
-
- public long peerTx(final Key peer) {
- if (!peerBytes.containsKey(peer))
- return 0;
- return peerBytes.get(peer).second;
- }
-
- public long totalRx() {
- long rx = 0;
- for (final Pair<Long, Long> val : peerBytes.values()) {
- rx += val.first;
- }
- return rx;
- }
-
- public long totalTx() {
- long tx = 0;
- for (final Pair<Long, Long> val : peerBytes.values()) {
- tx += val.second;
- }
- return tx;
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/backend/Tunnel.java b/app/src/main/java/com/wireguard/android/backend/Tunnel.java
deleted file mode 100644
index af2f59f7..00000000
--- a/app/src/main/java/com/wireguard/android/backend/Tunnel.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright © 2020 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.backend;
-
-import java.util.regex.Pattern;
-
-/**
- * Represents a WireGuard tunnel.
- */
-
-public interface Tunnel {
- enum State {
- DOWN,
- TOGGLE,
- UP;
-
- public static State of(final boolean running) {
- return running ? UP : DOWN;
- }
- }
-
- int NAME_MAX_LENGTH = 15;
- Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_=+.-]{1,15}");
-
- static boolean isNameInvalid(final CharSequence name) {
- return !NAME_PATTERN.matcher(name).matches();
- }
-
- /**
- * Get the name of the tunnel, which should always pass the !isNameInvalid test.
- *
- * @return The name of the tunnel.
- */
- String getName();
-
- /**
- * React to a change in state of the tunnel. Should only be directly called by Backend.
- *
- * @param newState The new state of the tunnel.
- * @return The new state of the tunnel.
- */
- void onStateChange(State newState);
-}
diff --git a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java b/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java
deleted file mode 100644
index 9695aab7..00000000
--- a/app/src/main/java/com/wireguard/android/backend/WgQuickBackend.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.backend;
-
-import androidx.annotation.Nullable;
-
-import android.content.Context;
-import android.util.Log;
-
-import com.wireguard.android.backend.BackendException.Reason;
-import com.wireguard.android.backend.Tunnel.State;
-import com.wireguard.android.util.RootShell;
-import com.wireguard.android.util.ToolsInstaller;
-import com.wireguard.config.Config;
-import com.wireguard.crypto.Key;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.HashMap;
-
-import java9.util.stream.Collectors;
-import java9.util.stream.Stream;
-
-/**
- * WireGuard backend that uses {@code wg-quick} to implement tunnel configuration.
- */
-
-public final class WgQuickBackend implements Backend {
- private static final String TAG = "WireGuard/" + WgQuickBackend.class.getSimpleName();
-
- private final RootShell rootShell;
- private final ToolsInstaller toolsInstaller;
- private final File localTemporaryDir;
- private final Map<Tunnel, Config> runningConfigs = new HashMap<>();
-
- public WgQuickBackend(final Context context, final RootShell rootShell, final ToolsInstaller toolsInstaller) {
- localTemporaryDir = new File(context.getCacheDir(), "tmp");
- this.rootShell = rootShell;
- this.toolsInstaller = toolsInstaller;
- }
-
- @Override
- public Set<String> getRunningTunnelNames() {
- final List<String> output = new ArrayList<>();
- // Don't throw an exception here or nothing will show up in the UI.
- try {
- toolsInstaller.ensureToolsAvailable();
- if (rootShell.run(output, "wg show interfaces") != 0 || output.isEmpty())
- return Collections.emptySet();
- } catch (final Exception e) {
- Log.w(TAG, "Unable to enumerate running tunnels", e);
- return Collections.emptySet();
- }
- // wg puts all interface names on the same line. Split them into separate elements.
- return Stream.of(output.get(0).split(" ")).collect(Collectors.toUnmodifiableSet());
- }
-
- @Override
- public State getState(final Tunnel tunnel) {
- return getRunningTunnelNames().contains(tunnel.getName()) ? State.UP : State.DOWN;
- }
-
- @Override
- public Statistics getStatistics(final Tunnel tunnel) {
- final Statistics stats = new Statistics();
- final Collection<String> output = new ArrayList<>();
- try {
- if (rootShell.run(output, String.format("wg show '%s' transfer", tunnel.getName())) != 0)
- return stats;
- } catch (final Exception ignored) {
- return stats;
- }
- for (final String line : output) {
- final String[] parts = line.split("\\t");
- if (parts.length != 3)
- continue;
- try {
- stats.add(Key.fromBase64(parts[0]), Long.parseLong(parts[1]), Long.parseLong(parts[2]));
- } catch (final Exception ignored) {
- }
- }
- return stats;
- }
-
- @Override
- public String getVersion() throws Exception {
- final List<String> output = new ArrayList<>();
- if (rootShell.run(output, "cat /sys/module/wireguard/version") != 0 || output.isEmpty())
- throw new BackendException(Reason.UNKNOWN_KERNEL_MODULE_NAME);
- return output.get(0);
- }
-
- @Override
- public State setState(final Tunnel tunnel, State state, @Nullable final Config config) throws Exception {
- final State originalState = getState(tunnel);
- final Config originalConfig = runningConfigs.get(tunnel);
-
- if (state == State.TOGGLE)
- state = originalState == State.UP ? State.DOWN : State.UP;
- if ((state == State.UP && originalState == State.UP && originalConfig != null && originalConfig == config) ||
- (state == State.DOWN && originalState == State.DOWN))
- return originalState;
- if (state == State.UP) {
- toolsInstaller.ensureToolsAvailable();
- if (originalState == State.UP)
- setStateInternal(tunnel, originalConfig == null ? config : originalConfig, State.DOWN);
- try {
- setStateInternal(tunnel, config, State.UP);
- } catch(final Exception e) {
- if (originalState == State.UP && originalConfig != null)
- setStateInternal(tunnel, originalConfig, State.UP);
- throw e;
- }
- } else if (state == State.DOWN) {
- setStateInternal(tunnel, originalConfig == null ? config : originalConfig, State.DOWN);
- }
- return state;
- }
-
- private void setStateInternal(final Tunnel tunnel, @Nullable final Config config, final State state) throws Exception {
- Log.i(TAG, "Bringing tunnel " + tunnel.getName() + " " + state);
-
- Objects.requireNonNull(config, "Trying to set state up with a null config");
-
- final File tempFile = new File(localTemporaryDir, tunnel.getName() + ".conf");
- try (final FileOutputStream stream = new FileOutputStream(tempFile, false)) {
- stream.write(config.toWgQuickString().getBytes(StandardCharsets.UTF_8));
- }
- String command = String.format("wg-quick %s '%s'",
- state.toString().toLowerCase(Locale.ENGLISH), tempFile.getAbsolutePath());
- if (state == State.UP)
- command = "cat /sys/module/wireguard/version && " + command;
- final int result = rootShell.run(null, command);
- // noinspection ResultOfMethodCallIgnored
- tempFile.delete();
- if (result != 0)
- throw new BackendException(Reason.WG_QUICK_CONFIG_ERROR_CODE, result);
-
- if (state == State.UP)
- runningConfigs.put(tunnel, config);
- else
- runningConfigs.remove(tunnel);
-
- tunnel.onStateChange(state);
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/AsyncWorker.java b/app/src/main/java/com/wireguard/android/util/AsyncWorker.java
deleted file mode 100644
index 1d041851..00000000
--- a/app/src/main/java/com/wireguard/android/util/AsyncWorker.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.os.Handler;
-
-import java.util.concurrent.Executor;
-
-import java9.util.concurrent.CompletableFuture;
-import java9.util.concurrent.CompletionStage;
-
-/**
- * Helper class for running asynchronous tasks and ensuring they are completed on the main thread.
- */
-
-public class AsyncWorker {
- private final Executor executor;
- private final Handler handler;
-
- public AsyncWorker(final Executor executor, final Handler handler) {
- this.executor = executor;
- this.handler = handler;
- }
-
- public CompletionStage<Void> runAsync(final AsyncRunnable<?> runnable) {
- final CompletableFuture<Void> future = new CompletableFuture<>();
- executor.execute(() -> {
- try {
- runnable.run();
- handler.post(() -> future.complete(null));
- } catch (final Throwable t) {
- handler.post(() -> future.completeExceptionally(t));
- }
- });
- return future;
- }
-
- public <T> CompletionStage<T> supplyAsync(final AsyncSupplier<T, ?> supplier) {
- final CompletableFuture<T> future = new CompletableFuture<>();
- executor.execute(() -> {
- try {
- final T result = supplier.get();
- handler.post(() -> future.complete(result));
- } catch (final Throwable t) {
- handler.post(() -> future.completeExceptionally(t));
- }
- });
- return future;
- }
-
- @FunctionalInterface
- public interface AsyncRunnable<E extends Throwable> {
- void run() throws E;
- }
-
- @FunctionalInterface
- public interface AsyncSupplier<T, E extends Throwable> {
- T get() throws E;
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/RootShell.java b/app/src/main/java/com/wireguard/android/util/RootShell.java
deleted file mode 100644
index 1fc2c9f2..00000000
--- a/app/src/main/java/com/wireguard/android/util/RootShell.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.Context;
-import androidx.annotation.Nullable;
-import android.util.Log;
-
-import com.wireguard.android.util.RootShell.RootShellException.Reason;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Collection;
-import java.util.UUID;
-
-/**
- * Helper class for running commands as root.
- */
-
-public class RootShell {
- private static final String SU = "su";
- private static final String TAG = "WireGuard/" + RootShell.class.getSimpleName();
-
- private final File localBinaryDir;
- private final File localTemporaryDir;
- private final Object lock = new Object();
- private final String preamble;
- @Nullable private Process process;
- @Nullable private BufferedReader stderr;
- @Nullable private OutputStreamWriter stdin;
- @Nullable private BufferedReader stdout;
-
- public RootShell(final Context context) {
- localBinaryDir = new File(context.getCodeCacheDir(), "bin");
- localTemporaryDir = new File(context.getCacheDir(), "tmp");
- preamble = String.format("export CALLING_PACKAGE=%s PATH=\"%s:$PATH\" TMPDIR='%s'; id -u\n",
- context.getPackageName(), localBinaryDir, localTemporaryDir);
- }
-
- private static boolean isExecutableInPath(final String name) {
- final String path = System.getenv("PATH");
- if (path == null)
- return false;
- for (final String dir : path.split(":"))
- if (new File(dir, name).canExecute())
- return true;
- return false;
- }
-
- private boolean isRunning() {
- synchronized (lock) {
- try {
- // Throws an exception if the process hasn't finished yet.
- if (process != null)
- process.exitValue();
- return false;
- } catch (final IllegalThreadStateException ignored) {
- // The existing process is still running.
- return true;
- }
- }
- }
-
- /**
- * Run a command in a root shell.
- *
- * @param output Lines read from stdout are appended to this list. Pass null if the
- * output from the shell is not important.
- * @param command Command to run as root.
- * @return The exit value of the command.
- */
- public int run(@Nullable final Collection<String> output, final String command)
- throws IOException, RootShellException {
- synchronized (lock) {
- /* Start inside synchronized block to prevent a concurrent call to stop(). */
- start();
- final String marker = UUID.randomUUID().toString();
- final String script = "echo " + marker + "; echo " + marker + " >&2; (" + command +
- "); ret=$?; echo " + marker + " $ret; echo " + marker + " $ret >&2\n";
- Log.v(TAG, "executing: " + command);
- stdin.write(script);
- stdin.flush();
- String line;
- int errnoStdout = Integer.MIN_VALUE;
- int errnoStderr = Integer.MAX_VALUE;
- int markersSeen = 0;
- while ((line = stdout.readLine()) != null) {
- if (line.startsWith(marker)) {
- ++markersSeen;
- if (line.length() > marker.length() + 1) {
- errnoStdout = Integer.valueOf(line.substring(marker.length() + 1));
- break;
- }
- } else if (markersSeen > 0) {
- if (output != null)
- output.add(line);
- Log.v(TAG, "stdout: " + line);
- }
- }
- while ((line = stderr.readLine()) != null) {
- if (line.startsWith(marker)) {
- ++markersSeen;
- if (line.length() > marker.length() + 1) {
- errnoStderr = Integer.valueOf(line.substring(marker.length() + 1));
- break;
- }
- } else if (markersSeen > 2) {
- Log.v(TAG, "stderr: " + line);
- }
- }
- if (markersSeen != 4)
- throw new RootShellException(Reason.SHELL_MARKER_COUNT_ERROR, markersSeen);
- if (errnoStdout != errnoStderr)
- throw new RootShellException(Reason.SHELL_EXIT_STATUS_READ_ERROR);
- Log.v(TAG, "exit: " + errnoStdout);
- return errnoStdout;
- }
- }
-
- public void start() throws IOException, RootShellException {
- if (!isExecutableInPath(SU))
- throw new RootShellException(Reason.NO_ROOT_ACCESS);
- synchronized (lock) {
- if (isRunning())
- return;
- if (!localBinaryDir.isDirectory() && !localBinaryDir.mkdirs())
- throw new RootShellException(Reason.CREATE_BIN_DIR_ERROR);
- if (!localTemporaryDir.isDirectory() && !localTemporaryDir.mkdirs())
- throw new RootShellException(Reason.CREATE_TEMP_DIR_ERROR);
- try {
- final ProcessBuilder builder = new ProcessBuilder().command(SU);
- builder.environment().put("LC_ALL", "C");
- try {
- process = builder.start();
- } catch (final IOException e) {
- // A failure at this stage means the device isn't rooted.
- final RootShellException rse = new RootShellException(Reason.NO_ROOT_ACCESS);
- rse.initCause(e);
- throw rse;
- }
- stdin = new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8);
- stdout = new BufferedReader(new InputStreamReader(process.getInputStream(),
- StandardCharsets.UTF_8));
- stderr = new BufferedReader(new InputStreamReader(process.getErrorStream(),
- StandardCharsets.UTF_8));
- stdin.write(preamble);
- stdin.flush();
- // Check that the shell started successfully.
- final String uid = stdout.readLine();
- if (!"0".equals(uid)) {
- Log.w(TAG, "Root check did not return correct UID: " + uid);
- throw new RootShellException(Reason.NO_ROOT_ACCESS);
- }
- if (!isRunning()) {
- String line;
- while ((line = stderr.readLine()) != null) {
- Log.w(TAG, "Root check returned an error: " + line);
- if (line.contains("Permission denied"))
- throw new RootShellException(Reason.NO_ROOT_ACCESS);
- }
- throw new RootShellException(Reason.SHELL_START_ERROR, process.exitValue());
- }
- } catch (final IOException | RootShellException e) {
- stop();
- throw e;
- }
- }
- }
-
- public void stop() {
- synchronized (lock) {
- if (process != null) {
- process.destroy();
- process = null;
- }
- }
- }
-
- public static class RootShellException extends Exception {
- public enum Reason {
- NO_ROOT_ACCESS,
- SHELL_MARKER_COUNT_ERROR,
- SHELL_EXIT_STATUS_READ_ERROR,
- SHELL_START_ERROR,
- CREATE_BIN_DIR_ERROR,
- CREATE_TEMP_DIR_ERROR
- }
- private final Reason reason;
- private final Object[] format;
- public RootShellException(final Reason reason, final Object ...format) {
- this.reason = reason;
- this.format = format;
- }
- public boolean isIORelated() {
- return reason != Reason.NO_ROOT_ACCESS;
- }
- public Reason getReason() {
- return reason;
- }
- public Object[] getFormat() {
- return format;
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java b/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java
deleted file mode 100644
index 93e44b64..00000000
--- a/app/src/main/java/com/wireguard/android/util/SharedLibraryLoader.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.Context;
-import android.os.Build;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-public final class SharedLibraryLoader {
- private static final String TAG = "WireGuard/" + SharedLibraryLoader.class.getSimpleName();
-
- private SharedLibraryLoader() {
- }
-
- public static boolean extractLibrary(final Context context, final String libName, final File destination) throws IOException {
- final Collection<String> apks = new HashSet<>();
- if (context.getApplicationInfo().sourceDir != null)
- apks.add(context.getApplicationInfo().sourceDir);
- if (context.getApplicationInfo().splitSourceDirs != null)
- apks.addAll(Arrays.asList(context.getApplicationInfo().splitSourceDirs));
-
- for (final String abi : Build.SUPPORTED_ABIS) {
- for (final String apk : apks) {
- final ZipFile zipFile;
- try {
- zipFile = new ZipFile(new File(apk), ZipFile.OPEN_READ);
- } catch (final IOException e) {
- throw new RuntimeException(e);
- }
-
- final String mappedLibName = System.mapLibraryName(libName);
- final byte[] buffer = new byte[1024 * 32];
- final String libZipPath = "lib" + File.separatorChar + abi + File.separatorChar + mappedLibName;
- final ZipEntry zipEntry = zipFile.getEntry(libZipPath);
- if (zipEntry == null)
- continue;
- Log.d(TAG, "Extracting apk:/" + libZipPath + " to " + destination.getAbsolutePath());
- try (final FileOutputStream out = new FileOutputStream(destination);
- final InputStream in = zipFile.getInputStream(zipEntry)) {
- int len;
- while ((len = in.read(buffer)) != -1) {
- out.write(buffer, 0, len);
- }
- out.getFD().sync();
- }
- zipFile.close();
- return true;
- }
- }
- return false;
- }
-
- public static void loadSharedLibrary(final Context context, final String libName) {
- Throwable noAbiException;
- try {
- System.loadLibrary(libName);
- return;
- } catch (final UnsatisfiedLinkError e) {
- Log.d(TAG, "Failed to load library normally, so attempting to extract from apk", e);
- noAbiException = e;
- }
- File f = null;
- try {
- f = File.createTempFile("lib", ".so", context.getCodeCacheDir());
- if (extractLibrary(context, libName, f)) {
- System.load(f.getAbsolutePath());
- return;
- }
- } catch (final Exception e) {
- Log.d(TAG, "Failed to load library apk:/" + libName, e);
- noAbiException = e;
- } finally {
- if (f != null)
- // noinspection ResultOfMethodCallIgnored
- f.delete();
- }
- if (noAbiException instanceof RuntimeException)
- throw (RuntimeException) noAbiException;
- throw new RuntimeException(noAbiException);
- }
-}
diff --git a/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java b/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java
deleted file mode 100644
index ac18cabf..00000000
--- a/app/src/main/java/com/wireguard/android/util/ToolsInstaller.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.android.util;
-
-import android.content.Context;
-import androidx.annotation.Nullable;
-import android.system.OsConstants;
-import android.util.Log;
-
-import com.wireguard.android.util.RootShell.RootShellException;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Helper to install WireGuard tools to the system partition.
- */
-
-public final class ToolsInstaller {
- public static final int ERROR = 0x0;
- public static final int MAGISK = 0x4;
- public static final int NO = 0x2;
- public static final int SYSTEM = 0x8;
- public static final int YES = 0x1;
- private static final String[] EXECUTABLES = {"wg", "wg-quick"};
- private static final File[] INSTALL_DIRS = {
- new File("/system/xbin"),
- new File("/system/bin"),
- };
- @Nullable private static final File INSTALL_DIR = getInstallDir();
- private static final String TAG = "WireGuard/" + ToolsInstaller.class.getSimpleName();
-
- private final Context context;
- private final RootShell rootShell;
- private final File localBinaryDir;
- private final Object lock = new Object();
- @Nullable private Boolean areToolsAvailable;
- @Nullable private Boolean installAsMagiskModule;
-
- public ToolsInstaller(final Context context, final RootShell rootShell) {
- localBinaryDir = new File(context.getCodeCacheDir(), "bin");
- this.context = context;
- this.rootShell = rootShell;
- }
-
- @Nullable
- private static File getInstallDir() {
- final String path = System.getenv("PATH");
- if (path == null)
- return INSTALL_DIRS[0];
- final List<String> paths = Arrays.asList(path.split(":"));
- for (final File dir : INSTALL_DIRS) {
- if (paths.contains(dir.getPath()) && dir.isDirectory())
- return dir;
- }
- return null;
- }
-
- public int areInstalled() throws RootShellException {
- if (INSTALL_DIR == null)
- return ERROR;
- final StringBuilder script = new StringBuilder();
- for (final String name : EXECUTABLES) {
- script.append(String.format("cmp -s '%s' '%s' && ",
- new File(localBinaryDir, name).getAbsolutePath(),
- new File(INSTALL_DIR, name).getAbsolutePath()));
- }
- script.append("exit ").append(OsConstants.EALREADY).append(';');
- try {
- final int ret = rootShell.run(null, script.toString());
- if (ret == OsConstants.EALREADY)
- return willInstallAsMagiskModule() ? YES | MAGISK : YES | SYSTEM;
- else
- return willInstallAsMagiskModule() ? NO | MAGISK : NO | SYSTEM;
- } catch (final IOException ignored) {
- return ERROR;
- } catch (final RootShellException e) {
- if (e.isIORelated())
- return ERROR;
- throw e;
- }
- }
-
- public void ensureToolsAvailable() throws FileNotFoundException {
- synchronized (lock) {
- if (areToolsAvailable == null) {
- try {
- Log.d(TAG, extract() ? "Tools are now extracted into our private binary dir" :
- "Tools were already extracted into our private binary dir");
- areToolsAvailable = true;
- } catch (final IOException e) {
- Log.e(TAG, "The wg and wg-quick tools are not available", e);
- areToolsAvailable = false;
- }
- }
- if (!areToolsAvailable)
- throw new FileNotFoundException("Required tools unavailable");
- }
- }
-
- public int install() throws RootShellException, IOException {
- if (!context.getPackageName().startsWith("com.wireguard."))
- throw new SecurityException("The tools may only be installed system-wide from the main WireGuard app.");
- return willInstallAsMagiskModule() ? installMagisk() : installSystem();
- }
-
- private int installMagisk() throws RootShellException, IOException {
- extract();
- final StringBuilder script = new StringBuilder("set -ex; ");
-
- script.append("trap 'rm -rf /sbin/.magisk/img/wireguard' INT TERM EXIT; ");
- script.append(String.format("rm -rf /sbin/.magisk/img/wireguard/; mkdir -p /sbin/.magisk/img/wireguard%s; ", INSTALL_DIR));
- script.append("printf 'name=WireGuard Command Line Tools\nversion=1.0\nversionCode=1\nauthor=zx2c4\ndescription=Command line tools for WireGuard\nminMagisk=1500\n' > /sbin/.magisk/img/wireguard/module.prop; ");
- script.append("touch /sbin/.magisk/img/wireguard/auto_mount; ");
- for (final String name : EXECUTABLES) {
- final File destination = new File("/sbin/.magisk/img/wireguard" + INSTALL_DIR, name);
- script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; chcon 'u:object_r:system_file:s0' '%s' || true; ",
- new File(localBinaryDir, name), destination, destination, destination));
- }
- script.append("trap - INT TERM EXIT;");
-
- try {
- return rootShell.run(null, script.toString()) == 0 ? YES | MAGISK : ERROR;
- } catch (final IOException ignored) {
- return ERROR;
- } catch (final RootShellException e) {
- if (e.isIORelated())
- return ERROR;
- throw e;
- }
- }
-
- private int installSystem() throws RootShellException, IOException {
- if (INSTALL_DIR == null)
- return OsConstants.ENOENT;
- extract();
- final StringBuilder script = new StringBuilder("set -ex; ");
- script.append("trap 'mount -o ro,remount /system' EXIT; mount -o rw,remount /system; ");
- for (final String name : EXECUTABLES) {
- final File destination = new File(INSTALL_DIR, name);
- script.append(String.format("cp '%s' '%s'; chmod 755 '%s'; restorecon '%s' || true; ",
- new File(localBinaryDir, name), destination, destination, destination));
- }
- try {
- return rootShell.run(null, script.toString()) == 0 ? YES | SYSTEM : ERROR;
- } catch (final IOException ignored) {
- return ERROR;
- } catch (final RootShellException e) {
- if (e.isIORelated())
- return ERROR;
- throw e;
- }
- }
-
- public boolean extract() throws IOException {
- localBinaryDir.mkdirs();
- final File files[] = new File[EXECUTABLES.length];
- final File tempFiles[] = new File[EXECUTABLES.length];
- boolean allExist = true;
- for (int i = 0; i < files.length; ++i) {
- files[i] = new File(localBinaryDir, EXECUTABLES[i]);
- tempFiles[i] = new File(localBinaryDir, EXECUTABLES[i] + ".tmp");
- allExist &= files[i].exists();
- }
- if (allExist)
- return false;
- for (int i = 0; i < files.length; ++i) {
- if (!SharedLibraryLoader.extractLibrary(context, EXECUTABLES[i], tempFiles[i]))
- throw new FileNotFoundException("Unable to find " + EXECUTABLES[i]);
- if (!tempFiles[i].setExecutable(true, false))
- throw new IOException("Unable to mark " + tempFiles[i].getAbsolutePath() + " as executable");
- if (!tempFiles[i].renameTo(files[i]))
- throw new IOException("Unable to rename " + tempFiles[i].getAbsolutePath() + " to " + files[i].getAbsolutePath());
- }
- return true;
- }
-
- private boolean willInstallAsMagiskModule() {
- synchronized (lock) {
- if (installAsMagiskModule == null) {
- try {
- installAsMagiskModule = rootShell.run(null, "[ -d /sbin/.magisk/mirror -a -d /sbin/.magisk/img -a ! -f /cache/.disable_magisk ]") == OsConstants.EXIT_SUCCESS;
- } catch (final Exception ignored) {
- installAsMagiskModule = false;
- }
- }
- return installAsMagiskModule;
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/config/Attribute.java b/app/src/main/java/com/wireguard/config/Attribute.java
deleted file mode 100644
index 1e9e25f0..00000000
--- a/app/src/main/java/com/wireguard/config/Attribute.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.config;
-
-import java.util.Iterator;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import java9.util.Optional;
-
-public final class Attribute {
- private static final Pattern LINE_PATTERN = Pattern.compile("(\\w+)\\s*=\\s*([^\\s#][^#]*)");
- private static final Pattern LIST_SEPARATOR = Pattern.compile("\\s*,\\s*");
-
- private final String key;
- private final String value;
-
- private Attribute(final String key, final String value) {
- this.key = key;
- this.value = value;
- }
-
- public static String join(final Iterable<?> values) {
- final Iterator<?> it = values.iterator();
- if (!it.hasNext()) {
- return "";
- }
- final StringBuilder sb = new StringBuilder();
- sb.append(it.next());
- while (it.hasNext()) {
- sb.append(", ");
- sb.append(it.next());
- }
- return sb.toString();
- }
-
- public static Optional<Attribute> parse(final CharSequence line) {
- final Matcher matcher = LINE_PATTERN.matcher(line);
- if (!matcher.matches())
- return Optional.empty();
- return Optional.of(new Attribute(matcher.group(1), matcher.group(2)));
- }
-
- public static String[] split(final CharSequence value) {
- return LIST_SEPARATOR.split(value);
- }
-
- public String getKey() {
- return key;
- }
-
- public String getValue() {
- return value;
- }
-}
diff --git a/app/src/main/java/com/wireguard/config/BadConfigException.java b/app/src/main/java/com/wireguard/config/BadConfigException.java
deleted file mode 100644
index 6d41b065..00000000
--- a/app/src/main/java/com/wireguard/config/BadConfigException.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.config;
-
-import androidx.annotation.Nullable;
-
-import com.wireguard.crypto.KeyFormatException;
-
-public class BadConfigException extends Exception {
- private final Location location;
- private final Reason reason;
- private final Section section;
- @Nullable private final CharSequence text;
-
- private BadConfigException(final Section section, final Location location,
- final Reason reason, @Nullable final CharSequence text,
- @Nullable final Throwable cause) {
- super(cause);
- this.section = section;
- this.location = location;
- this.reason = reason;
- this.text = text;
- }
-
- public BadConfigException(final Section section, final Location location,
- final Reason reason, @Nullable final CharSequence text) {
- this(section, location, reason, text, null);
- }
-
- public BadConfigException(final Section section, final Location location,
- final KeyFormatException cause) {
- this(section, location, Reason.INVALID_KEY, null, cause);
- }
-
- public BadConfigException(final Section section, final Location location,
- @Nullable final CharSequence text,
- final NumberFormatException cause) {
- this(section, location, Reason.INVALID_NUMBER, text, cause);
- }
-
- public BadConfigException(final Section section, final Location location,
- final ParseException cause) {
- this(section, location, Reason.INVALID_VALUE, cause.getText(), cause);
- }
-
- public Location getLocation() {
- return location;
- }
-
- public Reason getReason() {
- return reason;
- }
-
- public Section getSection() {
- return section;
- }
-
- @Nullable
- public CharSequence getText() {
- return text;
- }
-
- public enum Location {
- TOP_LEVEL(""),
- ADDRESS("Address"),
- ALLOWED_IPS("AllowedIPs"),
- DNS("DNS"),
- ENDPOINT("Endpoint"),
- EXCLUDED_APPLICATIONS("ExcludedApplications"),
- LISTEN_PORT("ListenPort"),
- MTU("MTU"),
- PERSISTENT_KEEPALIVE("PersistentKeepalive"),
- PRE_SHARED_KEY("PresharedKey"),
- PRIVATE_KEY("PrivateKey"),
- PUBLIC_KEY("PublicKey");
-
- private final String name;
-
- Location(final String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
- }
-
- public enum Reason {
- INVALID_KEY,
- INVALID_NUMBER,
- INVALID_VALUE,
- MISSING_ATTRIBUTE,
- MISSING_SECTION,
- MISSING_VALUE,
- SYNTAX_ERROR,
- UNKNOWN_ATTRIBUTE,
- UNKNOWN_SECTION
- }
-
- public enum Section {
- CONFIG("Config"),
- INTERFACE("Interface"),
- PEER("Peer");
-
- private final String name;
-
- Section(final String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/config/Config.java b/app/src/main/java/com/wireguard/config/Config.java
deleted file mode 100644
index 62651b08..00000000
--- a/app/src/main/java/com/wireguard/config/Config.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.config;
-
-import androidx.annotation.Nullable;
-
-import com.wireguard.config.BadConfigException.Location;
-import com.wireguard.config.BadConfigException.Reason;
-import com.wireguard.config.BadConfigException.Section;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Represents the contents of a wg-quick configuration file, made up of one or more "Interface"
- * sections (combined together), and zero or more "Peer" sections (treated individually).
- * <p>
- * Instances of this class are immutable.
- */
-public final class Config {
- private final Interface interfaze;
- private final List<Peer> peers;
-
- private Config(final Builder builder) {
- interfaze = Objects.requireNonNull(builder.interfaze, "An [Interface] section is required");
- // Defensively copy to ensure immutability even if the Builder is reused.
- peers = Collections.unmodifiableList(new ArrayList<>(builder.peers));
- }
-
- /**
- * Parses an series of "Interface" and "Peer" sections into a {@code Config}. Throws
- * {@link BadConfigException} if the input is not well-formed or contains data that cannot
- * be parsed.
- *
- * @param stream a stream of UTF-8 text that is interpreted as a WireGuard configuration
- * @return a {@code Config} instance representing the supplied configuration
- */
- public static Config parse(final InputStream stream)
- throws IOException, BadConfigException {
- return parse(new BufferedReader(new InputStreamReader(stream)));
- }
-
- /**
- * Parses an series of "Interface" and "Peer" sections into a {@code Config}. Throws
- * {@link BadConfigException} if the input is not well-formed or contains data that cannot
- * be parsed.
- *
- * @param reader a BufferedReader of UTF-8 text that is interpreted as a WireGuard configuration
- * @return a {@code Config} instance representing the supplied configuration
- */
- public static Config parse(final BufferedReader reader)
- throws IOException, BadConfigException {
- final Builder builder = new Builder();
- final Collection<String> interfaceLines = new ArrayList<>();
- final Collection<String> peerLines = new ArrayList<>();
- boolean inInterfaceSection = false;
- boolean inPeerSection = false;
- @Nullable String line;
- while ((line = reader.readLine()) != null) {
- final int commentIndex = line.indexOf('#');
- if (commentIndex != -1)
- line = line.substring(0, commentIndex);
- line = line.trim();
- if (line.isEmpty())
- continue;
- if (line.startsWith("[")) {
- // Consume all [Peer] lines read so far.
- if (inPeerSection) {
- builder.parsePeer(peerLines);
- peerLines.clear();
- }
- if ("[Interface]".equalsIgnoreCase(line)) {
- inInterfaceSection = true;
- inPeerSection = false;
- } else if ("[Peer]".equalsIgnoreCase(line)) {
- inInterfaceSection = false;
- inPeerSection = true;
- } else {
- throw new BadConfigException(Section.CONFIG, Location.TOP_LEVEL,
- Reason.UNKNOWN_SECTION, line);
- }
- } else if (inInterfaceSection) {
- interfaceLines.add(line);
- } else if (inPeerSection) {
- peerLines.add(line);
- } else {
- throw new BadConfigException(Section.CONFIG, Location.TOP_LEVEL,
- Reason.UNKNOWN_SECTION, line);
- }
- }
- if (inPeerSection)
- builder.parsePeer(peerLines);
- else if (!inInterfaceSection)
- throw new BadConfigException(Section.CONFIG, Location.TOP_LEVEL,
- Reason.MISSING_SECTION, null);
- // Combine all [Interface] sections in the file.
- builder.parseInterface(interfaceLines);
- return builder.build();
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (!(obj instanceof Config))
- return false;
- final Config other = (Config) obj;
- return interfaze.equals(other.interfaze) && peers.equals(other.peers);
- }
-
- /**
- * Returns the interface section of the configuration.
- *
- * @return the interface configuration
- */
- public Interface getInterface() {
- return interfaze;
- }
-
- /**
- * Returns a list of the configuration's peer sections.
- *
- * @return a list of {@link Peer}s
- */
- public List<Peer> getPeers() {
- return peers;
- }
-
- @Override
- public int hashCode() {
- return 31 * interfaze.hashCode() + peers.hashCode();
- }
-
- /**
- * Converts the {@code Config} into a string suitable for debugging purposes. The {@code Config}
- * is identified by its interface's public key and the number of peers it has.
- *
- * @return a concise single-line identifier for the {@code Config}
- */
- @Override
- public String toString() {
- return "(Config " + interfaze + " (" + peers.size() + " peers))";
- }
-
- /**
- * Converts the {@code Config} into a string suitable for use as a {@code wg-quick}
- * configuration file.
- *
- * @return the {@code Config} represented as one [Interface] and zero or more [Peer] sections
- */
- public String toWgQuickString() {
- final StringBuilder sb = new StringBuilder();
- sb.append("[Interface]\n").append(interfaze.toWgQuickString());
- for (final Peer peer : peers)
- sb.append("\n[Peer]\n").append(peer.toWgQuickString());
- return sb.toString();
- }
-
- /**
- * Serializes the {@code Config} for use with the WireGuard cross-platform userspace API.
- *
- * @return the {@code Config} represented as a series of "key=value" lines
- */
- public String toWgUserspaceString() {
- final StringBuilder sb = new StringBuilder();
- sb.append(interfaze.toWgUserspaceString());
- sb.append("replace_peers=true\n");
- for (final Peer peer : peers)
- sb.append(peer.toWgUserspaceString());
- return sb.toString();
- }
-
- @SuppressWarnings("UnusedReturnValue")
- public static final class Builder {
- // Defaults to an empty set.
- private final Set<Peer> peers = new LinkedHashSet<>();
- // No default; must be provided before building.
- @Nullable private Interface interfaze;
-
- public Builder addPeer(final Peer peer) {
- peers.add(peer);
- return this;
- }
-
- public Builder addPeers(final Collection<Peer> peers) {
- this.peers.addAll(peers);
- return this;
- }
-
- public Config build() {
- if (interfaze == null)
- throw new IllegalArgumentException("An [Interface] section is required");
- return new Config(this);
- }
-
- public Builder parseInterface(final Iterable<? extends CharSequence> lines)
- throws BadConfigException {
- return setInterface(Interface.parse(lines));
- }
-
- public Builder parsePeer(final Iterable<? extends CharSequence> lines)
- throws BadConfigException {
- return addPeer(Peer.parse(lines));
- }
-
- public Builder setInterface(final Interface interfaze) {
- this.interfaze = interfaze;
- return this;
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/config/InetAddresses.java b/app/src/main/java/com/wireguard/config/InetAddresses.java
deleted file mode 100644
index 5303e27f..00000000
--- a/app/src/main/java/com/wireguard/config/InetAddresses.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.config;
-
-import java.lang.reflect.Method;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.regex.Pattern;
-
-import javax.annotation.Nullable;
-
-/**
- * Utility methods for creating instances of {@link InetAddress}.
- */
-public final class InetAddresses {
- @Nullable private static final Method PARSER_METHOD;
- private static final Pattern WONT_TOUCH_RESOLVER = Pattern.compile("^(((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?)|((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$");
-
- static {
- Method m = null;
- try {
- if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.Q)
- // noinspection JavaReflectionMemberAccess
- m = InetAddress.class.getMethod("parseNumericAddress", String.class);
- } catch (final Exception ignored) {
- }
- PARSER_METHOD = m;
- }
-
- private InetAddresses() { }
-
- /**
- * Parses a numeric IPv4 or IPv6 address without performing any DNS lookups.
- *
- * @param address a string representing the IP address
- * @return an instance of {@link Inet4Address} or {@link Inet6Address}, as appropriate
- */
- public static InetAddress parse(final String address) throws ParseException {
- if (address.isEmpty())
- throw new ParseException(InetAddress.class, address, "Empty address");
- try {
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q)
- return android.net.InetAddresses.parseNumericAddress(address);
- else if (PARSER_METHOD != null)
- return (InetAddress) PARSER_METHOD.invoke(null, address);
- else
- throw new NoSuchMethodException("parseNumericAddress");
- } catch (final IllegalArgumentException e) {
- throw new ParseException(InetAddress.class, address, e);
- } catch (final Exception e) {
- final Throwable cause = e.getCause();
- // Re-throw parsing exceptions with the original type, as callers might try to catch
- // them. On the other hand, callers cannot be expected to handle reflection failures.
- if (cause instanceof IllegalArgumentException)
- throw new ParseException(InetAddress.class, address, cause);
- try {
- if (WONT_TOUCH_RESOLVER.matcher(address).matches())
- return InetAddress.getByName(address);
- else
- throw new ParseException(InetAddress.class, address, "Not an IP address");
- } catch (final UnknownHostException f) {
- throw new ParseException(InetAddress.class, address, f);
- }
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/config/InetEndpoint.java b/app/src/main/java/com/wireguard/config/InetEndpoint.java
deleted file mode 100644
index a442258e..00000000
--- a/app/src/main/java/com/wireguard/config/InetEndpoint.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.config;
-
-import androidx.annotation.Nullable;
-
-import org.threeten.bp.Duration;
-import org.threeten.bp.Instant;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.UnknownHostException;
-import java.util.regex.Pattern;
-
-import java9.util.Optional;
-
-
-/**
- * An external endpoint (host and port) used to connect to a WireGuard {@link Peer}.
- * <p>
- * Instances of this class are externally immutable.
- */
-public final class InetEndpoint {
- private static final Pattern BARE_IPV6 = Pattern.compile("^[^\\[\\]]*:[^\\[\\]]*");
- private static final Pattern FORBIDDEN_CHARACTERS = Pattern.compile("[/?#]");
-
- private final String host;
- private final boolean isResolved;
- private final Object lock = new Object();
- private final int port;
- private Instant lastResolution = Instant.EPOCH;
- @Nullable private InetEndpoint resolved;
-
- private InetEndpoint(final String host, final boolean isResolved, final int port) {
- this.host = host;
- this.isResolved = isResolved;
- this.port = port;
- }
-
- public static InetEndpoint parse(final String endpoint) throws ParseException {
- if (FORBIDDEN_CHARACTERS.matcher(endpoint).find())
- throw new ParseException(InetEndpoint.class, endpoint, "Forbidden characters");
- final URI uri;
- try {
- uri = new URI("wg://" + endpoint);
- } catch (final URISyntaxException e) {
- throw new IllegalArgumentException(e);
- }
- if (uri.getPort() < 0 || uri.getPort() > 65535)
- throw new ParseException(InetEndpoint.class, endpoint, "Missing/invalid port number");
- try {
- InetAddresses.parse(uri.getHost());
- // Parsing ths host as a numeric address worked, so we don't need to do DNS lookups.
- return new InetEndpoint(uri.getHost(), true, uri.getPort());
- } catch (final ParseException ignored) {
- // Failed to parse the host as a numeric address, so it must be a DNS hostname/FQDN.
- return new InetEndpoint(uri.getHost(), false, uri.getPort());
- }
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (!(obj instanceof InetEndpoint))
- return false;
- final InetEndpoint other = (InetEndpoint) obj;
- return host.equals(other.host) && port == other.port;
- }
-
- public String getHost() {
- return host;
- }
-
- public int getPort() {
- return port;
- }
-
- /**
- * Generate an {@code InetEndpoint} instance with the same port and the host resolved using DNS
- * to a numeric address. If the host is already numeric, the existing instance may be returned.
- * Because this function may perform network I/O, it must not be called from the main thread.
- *
- * @return the resolved endpoint, or {@link Optional#empty()}
- */
- public Optional<InetEndpoint> getResolved() {
- if (isResolved)
- return Optional.of(this);
- synchronized (lock) {
- //TODO(zx2c4): Implement a real timeout mechanism using DNS TTL
- if (Duration.between(lastResolution, Instant.now()).toMinutes() > 1) {
- try {
- // Prefer v4 endpoints over v6 to work around DNS64 and IPv6 NAT issues.
- final InetAddress[] candidates = InetAddress.getAllByName(host);
- InetAddress address = candidates[0];
- for (final InetAddress candidate : candidates) {
- if (candidate instanceof Inet4Address) {
- address = candidate;
- break;
- }
- }
- resolved = new InetEndpoint(address.getHostAddress(), true, port);
- lastResolution = Instant.now();
- } catch (final UnknownHostException e) {
- resolved = null;
- }
- }
- return Optional.ofNullable(resolved);
- }
- }
-
- @Override
- public int hashCode() {
- return host.hashCode() ^ port;
- }
-
- @Override
- public String toString() {
- final boolean isBareIpv6 = isResolved && BARE_IPV6.matcher(host).matches();
- return (isBareIpv6 ? '[' + host + ']' : host) + ':' + port;
- }
-}
diff --git a/app/src/main/java/com/wireguard/config/InetNetwork.java b/app/src/main/java/com/wireguard/config/InetNetwork.java
deleted file mode 100644
index f89322fd..00000000
--- a/app/src/main/java/com/wireguard/config/InetNetwork.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.config;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-
-/**
- * An Internet network, denoted by its address and netmask
- * <p>
- * Instances of this class are immutable.
- */
-public final class InetNetwork {
- private final InetAddress address;
- private final int mask;
-
- private InetNetwork(final InetAddress address, final int mask) {
- this.address = address;
- this.mask = mask;
- }
-
- public static InetNetwork parse(final String network) throws ParseException {
- final int slash = network.lastIndexOf('/');
- final String maskString;
- final int rawMask;
- final String rawAddress;
- if (slash >= 0) {
- maskString = network.substring(slash + 1);
- try {
- rawMask = Integer.parseInt(maskString, 10);
- } catch (final NumberFormatException ignored) {
- throw new ParseException(Integer.class, maskString);
- }
- rawAddress = network.substring(0, slash);
- } else {
- maskString = "";
- rawMask = -1;
- rawAddress = network;
- }
- final InetAddress address = InetAddresses.parse(rawAddress);
- final int maxMask = (address instanceof Inet4Address) ? 32 : 128;
- if (rawMask > maxMask)
- throw new ParseException(InetNetwork.class, maskString, "Invalid network mask");
- final int mask = rawMask >= 0 && rawMask <= maxMask ? rawMask : maxMask;
- return new InetNetwork(address, mask);
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (!(obj instanceof InetNetwork))
- return false;
- final InetNetwork other = (InetNetwork) obj;
- return address.equals(other.address) && mask == other.mask;
- }
-
- public InetAddress getAddress() {
- return address;
- }
-
- public int getMask() {
- return mask;
- }
-
- @Override
- public int hashCode() {
- return address.hashCode() ^ mask;
- }
-
- @Override
- public String toString() {
- return address.getHostAddress() + '/' + mask;
- }
-}
diff --git a/app/src/main/java/com/wireguard/config/Interface.java b/app/src/main/java/com/wireguard/config/Interface.java
deleted file mode 100644
index 54944424..00000000
--- a/app/src/main/java/com/wireguard/config/Interface.java
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.config;
-
-import androidx.annotation.Nullable;
-
-import com.wireguard.config.BadConfigException.Location;
-import com.wireguard.config.BadConfigException.Reason;
-import com.wireguard.config.BadConfigException.Section;
-import com.wireguard.crypto.Key;
-import com.wireguard.crypto.KeyFormatException;
-import com.wireguard.crypto.KeyPair;
-
-import java.net.InetAddress;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.Set;
-
-import java9.util.Lists;
-import java9.util.Optional;
-import java9.util.stream.Collectors;
-import java9.util.stream.StreamSupport;
-
-/**
- * Represents the configuration for a WireGuard interface (an [Interface] block). Interfaces must
- * have a private key (used to initialize a {@code KeyPair}), and may optionally have several other
- * attributes.
- * <p>
- * Instances of this class are immutable.
- */
-public final class Interface {
- private static final int MAX_UDP_PORT = 65535;
- private static final int MIN_UDP_PORT = 0;
-
- private final Set<InetNetwork> addresses;
- private final Set<InetAddress> dnsServers;
- private final Set<String> excludedApplications;
- private final KeyPair keyPair;
- private final Optional<Integer> listenPort;
- private final Optional<Integer> mtu;
-
- private Interface(final Builder builder) {
- // Defensively copy to ensure immutability even if the Builder is reused.
- addresses = Collections.unmodifiableSet(new LinkedHashSet<>(builder.addresses));
- dnsServers = Collections.unmodifiableSet(new LinkedHashSet<>(builder.dnsServers));
- excludedApplications = Collections.unmodifiableSet(new LinkedHashSet<>(builder.excludedApplications));
- keyPair = Objects.requireNonNull(builder.keyPair, "Interfaces must have a private key");
- listenPort = builder.listenPort;
- mtu = builder.mtu;
- }
-
- /**
- * Parses an series of "KEY = VALUE" lines into an {@code Interface}. Throws
- * {@link ParseException} if the input is not well-formed or contains unknown attributes.
- *
- * @param lines An iterable sequence of lines, containing at least a private key attribute
- * @return An {@code Interface} with all of the attributes from {@code lines} set
- */
- public static Interface parse(final Iterable<? extends CharSequence> lines)
- throws BadConfigException {
- final Builder builder = new Builder();
- for (final CharSequence line : lines) {
- final Attribute attribute = Attribute.parse(line).orElseThrow(() ->
- new BadConfigException(Section.INTERFACE, Location.TOP_LEVEL,
- Reason.SYNTAX_ERROR, line));
- switch (attribute.getKey().toLowerCase(Locale.ENGLISH)) {
- case "address":
- builder.parseAddresses(attribute.getValue());
- break;
- case "dns":
- builder.parseDnsServers(attribute.getValue());
- break;
- case "excludedapplications":
- builder.parseExcludedApplications(attribute.getValue());
- break;
- case "listenport":
- builder.parseListenPort(attribute.getValue());
- break;
- case "mtu":
- builder.parseMtu(attribute.getValue());
- break;
- case "privatekey":
- builder.parsePrivateKey(attribute.getValue());
- break;
- default:
- throw new BadConfigException(Section.INTERFACE, Location.TOP_LEVEL,
- Reason.UNKNOWN_ATTRIBUTE, attribute.getKey());
- }
- }
- return builder.build();
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (!(obj instanceof Interface))
- return false;
- final Interface other = (Interface) obj;
- return addresses.equals(other.addresses)
- && dnsServers.equals(other.dnsServers)
- && excludedApplications.equals(other.excludedApplications)
- && keyPair.equals(other.keyPair)
- && listenPort.equals(other.listenPort)
- && mtu.equals(other.mtu);
- }
-
- /**
- * Returns the set of IP addresses assigned to the interface.
- *
- * @return a set of {@link InetNetwork}s
- */
- public Set<InetNetwork> getAddresses() {
- // The collection is already immutable.
- return addresses;
- }
-
- /**
- * Returns the set of DNS servers associated with the interface.
- *
- * @return a set of {@link InetAddress}es
- */
- public Set<InetAddress> getDnsServers() {
- // The collection is already immutable.
- return dnsServers;
- }
-
- /**
- * Returns the set of applications excluded from using the interface.
- *
- * @return a set of package names
- */
- public Set<String> getExcludedApplications() {
- // The collection is already immutable.
- return excludedApplications;
- }
-
- /**
- * Returns the public/private key pair used by the interface.
- *
- * @return a key pair
- */
- public KeyPair getKeyPair() {
- return keyPair;
- }
-
- /**
- * Returns the UDP port number that the WireGuard interface will listen on.
- *
- * @return a UDP port number, or {@code Optional.empty()} if none is configured
- */
- public Optional<Integer> getListenPort() {
- return listenPort;
- }
-
- /**
- * Returns the MTU used for the WireGuard interface.
- *
- * @return the MTU, or {@code Optional.empty()} if none is configured
- */
- public Optional<Integer> getMtu() {
- return mtu;
- }
-
- @Override
- public int hashCode() {
- int hash = 1;
- hash = 31 * hash + addresses.hashCode();
- hash = 31 * hash + dnsServers.hashCode();
- hash = 31 * hash + excludedApplications.hashCode();
- hash = 31 * hash + keyPair.hashCode();
- hash = 31 * hash + listenPort.hashCode();
- hash = 31 * hash + mtu.hashCode();
- return hash;
- }
-
- /**
- * Converts the {@code Interface} into a string suitable for debugging purposes. The {@code
- * Interface} is identified by its public key and (if set) the port used for its UDP socket.
- *
- * @return A concise single-line identifier for the {@code Interface}
- */
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("(Interface ");
- sb.append(keyPair.getPublicKey().toBase64());
- listenPort.ifPresent(lp -> sb.append(" @").append(lp));
- sb.append(')');
- return sb.toString();
- }
-
- /**
- * Converts the {@code Interface} into a string suitable for inclusion in a {@code wg-quick}
- * configuration file.
- *
- * @return The {@code Interface} represented as a series of "Key = Value" lines
- */
- public String toWgQuickString() {
- final StringBuilder sb = new StringBuilder();
- if (!addresses.isEmpty())
- sb.append("Address = ").append(Attribute.join(addresses)).append('\n');
- if (!dnsServers.isEmpty()) {
- final List<String> dnsServerStrings = StreamSupport.stream(dnsServers)
- .map(InetAddress::getHostAddress)
- .collect(Collectors.toUnmodifiableList());
- sb.append("DNS = ").append(Attribute.join(dnsServerStrings)).append('\n');
- }
- if (!excludedApplications.isEmpty())
- sb.append("ExcludedApplications = ").append(Attribute.join(excludedApplications)).append('\n');
- listenPort.ifPresent(lp -> sb.append("ListenPort = ").append(lp).append('\n'));
- mtu.ifPresent(m -> sb.append("MTU = ").append(m).append('\n'));
- sb.append("PrivateKey = ").append(keyPair.getPrivateKey().toBase64()).append('\n');
- return sb.toString();
- }
-
- /**
- * Serializes the {@code Interface} for use with the WireGuard cross-platform userspace API.
- * Note that not all attributes are included in this representation.
- *
- * @return the {@code Interface} represented as a series of "KEY=VALUE" lines
- */
- public String toWgUserspaceString() {
- final StringBuilder sb = new StringBuilder();
- sb.append("private_key=").append(keyPair.getPrivateKey().toHex()).append('\n');
- listenPort.ifPresent(lp -> sb.append("listen_port=").append(lp).append('\n'));
- return sb.toString();
- }
-
- @SuppressWarnings("UnusedReturnValue")
- public static final class Builder {
- // Defaults to an empty set.
- private final Set<InetNetwork> addresses = new LinkedHashSet<>();
- // Defaults to an empty set.
- private final Set<InetAddress> dnsServers = new LinkedHashSet<>();
- // Defaults to an empty set.
- private final Set<String> excludedApplications = new LinkedHashSet<>();
- // No default; must be provided before building.
- @Nullable private KeyPair keyPair;
- // Defaults to not present.
- private Optional<Integer> listenPort = Optional.empty();
- // Defaults to not present.
- private Optional<Integer> mtu = Optional.empty();
-
- public Builder addAddress(final InetNetwork address) {
- addresses.add(address);
- return this;
- }
-
- public Builder addAddresses(final Collection<InetNetwork> addresses) {
- this.addresses.addAll(addresses);
- return this;
- }
-
- public Builder addDnsServer(final InetAddress dnsServer) {
- dnsServers.add(dnsServer);
- return this;
- }
-
- public Builder addDnsServers(final Collection<? extends InetAddress> dnsServers) {
- this.dnsServers.addAll(dnsServers);
- return this;
- }
-
- public Interface build() throws BadConfigException {
- if (keyPair == null)
- throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY,
- Reason.MISSING_ATTRIBUTE, null);
- return new Interface(this);
- }
-
- public Builder excludeApplication(final String application) {
- excludedApplications.add(application);
- return this;
- }
-
- public Builder excludeApplications(final Collection<String> applications) {
- excludedApplications.addAll(applications);
- return this;
- }
-
- public Builder parseAddresses(final CharSequence addresses) throws BadConfigException {
- try {
- for (final String address : Attribute.split(addresses))
- addAddress(InetNetwork.parse(address));
- return this;
- } catch (final ParseException e) {
- throw new BadConfigException(Section.INTERFACE, Location.ADDRESS, e);
- }
- }
-
- public Builder parseDnsServers(final CharSequence dnsServers) throws BadConfigException {
- try {
- for (final String dnsServer : Attribute.split(dnsServers))
- addDnsServer(InetAddresses.parse(dnsServer));
- return this;
- } catch (final ParseException e) {
- throw new BadConfigException(Section.INTERFACE, Location.DNS, e);
- }
- }
-
- public Builder parseExcludedApplications(final CharSequence apps) {
- return excludeApplications(Lists.of(Attribute.split(apps)));
- }
-
- public Builder parseListenPort(final String listenPort) throws BadConfigException {
- try {
- return setListenPort(Integer.parseInt(listenPort));
- } catch (final NumberFormatException e) {
- throw new BadConfigException(Section.INTERFACE, Location.LISTEN_PORT, listenPort, e);
- }
- }
-
- public Builder parseMtu(final String mtu) throws BadConfigException {
- try {
- return setMtu(Integer.parseInt(mtu));
- } catch (final NumberFormatException e) {
- throw new BadConfigException(Section.INTERFACE, Location.MTU, mtu, e);
- }
- }
-
- public Builder parsePrivateKey(final String privateKey) throws BadConfigException {
- try {
- return setKeyPair(new KeyPair(Key.fromBase64(privateKey)));
- } catch (final KeyFormatException e) {
- throw new BadConfigException(Section.INTERFACE, Location.PRIVATE_KEY, e);
- }
- }
-
- public Builder setKeyPair(final KeyPair keyPair) {
- this.keyPair = keyPair;
- return this;
- }
-
- public Builder setListenPort(final int listenPort) throws BadConfigException {
- if (listenPort < MIN_UDP_PORT || listenPort > MAX_UDP_PORT)
- throw new BadConfigException(Section.INTERFACE, Location.LISTEN_PORT,
- Reason.INVALID_VALUE, String.valueOf(listenPort));
- this.listenPort = listenPort == 0 ? Optional.empty() : Optional.of(listenPort);
- return this;
- }
-
- public Builder setMtu(final int mtu) throws BadConfigException {
- if (mtu < 0)
- throw new BadConfigException(Section.INTERFACE, Location.LISTEN_PORT,
- Reason.INVALID_VALUE, String.valueOf(mtu));
- this.mtu = mtu == 0 ? Optional.empty() : Optional.of(mtu);
- return this;
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/config/ParseException.java b/app/src/main/java/com/wireguard/config/ParseException.java
deleted file mode 100644
index c79d1fa1..00000000
--- a/app/src/main/java/com/wireguard/config/ParseException.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.config;
-
-import androidx.annotation.Nullable;
-
-/**
- */
-public class ParseException extends Exception {
- private final Class<?> parsingClass;
- private final CharSequence text;
-
- public ParseException(final Class<?> parsingClass, final CharSequence text,
- @Nullable final String message, @Nullable final Throwable cause) {
- super(message, cause);
- this.parsingClass = parsingClass;
- this.text = text;
- }
-
- public ParseException(final Class<?> parsingClass, final CharSequence text,
- @Nullable final String message) {
- this(parsingClass, text, message, null);
- }
-
- public ParseException(final Class<?> parsingClass, final CharSequence text,
- @Nullable final Throwable cause) {
- this(parsingClass, text, null, cause);
- }
-
- public ParseException(final Class<?> parsingClass, final CharSequence text) {
- this(parsingClass, text, null, null);
- }
-
- public Class<?> getParsingClass() {
- return parsingClass;
- }
-
- public CharSequence getText() {
- return text;
- }
-}
diff --git a/app/src/main/java/com/wireguard/config/Peer.java b/app/src/main/java/com/wireguard/config/Peer.java
deleted file mode 100644
index 37fcfa69..00000000
--- a/app/src/main/java/com/wireguard/config/Peer.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.config;
-
-import androidx.annotation.Nullable;
-
-import com.wireguard.config.BadConfigException.Location;
-import com.wireguard.config.BadConfigException.Reason;
-import com.wireguard.config.BadConfigException.Section;
-import com.wireguard.crypto.Key;
-import com.wireguard.crypto.KeyFormatException;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.Set;
-
-import java9.util.Optional;
-
-/**
- * Represents the configuration for a WireGuard peer (a [Peer] block). Peers must have a public key,
- * and may optionally have several other attributes.
- * <p>
- * Instances of this class are immutable.
- */
-public final class Peer {
- private final Set<InetNetwork> allowedIps;
- private final Optional<InetEndpoint> endpoint;
- private final Optional<Integer> persistentKeepalive;
- private final Optional<Key> preSharedKey;
- private final Key publicKey;
-
- private Peer(final Builder builder) {
- // Defensively copy to ensure immutability even if the Builder is reused.
- allowedIps = Collections.unmodifiableSet(new LinkedHashSet<>(builder.allowedIps));
- endpoint = builder.endpoint;
- persistentKeepalive = builder.persistentKeepalive;
- preSharedKey = builder.preSharedKey;
- publicKey = Objects.requireNonNull(builder.publicKey, "Peers must have a public key");
- }
-
- /**
- * Parses an series of "KEY = VALUE" lines into a {@code Peer}. Throws {@link ParseException} if
- * the input is not well-formed or contains unknown attributes.
- *
- * @param lines an iterable sequence of lines, containing at least a public key attribute
- * @return a {@code Peer} with all of its attributes set from {@code lines}
- */
- public static Peer parse(final Iterable<? extends CharSequence> lines)
- throws BadConfigException {
- final Builder builder = new Builder();
- for (final CharSequence line : lines) {
- final Attribute attribute = Attribute.parse(line).orElseThrow(() ->
- new BadConfigException(Section.PEER, Location.TOP_LEVEL,
- Reason.SYNTAX_ERROR, line));
- switch (attribute.getKey().toLowerCase(Locale.ENGLISH)) {
- case "allowedips":
- builder.parseAllowedIPs(attribute.getValue());
- break;
- case "endpoint":
- builder.parseEndpoint(attribute.getValue());
- break;
- case "persistentkeepalive":
- builder.parsePersistentKeepalive(attribute.getValue());
- break;
- case "presharedkey":
- builder.parsePreSharedKey(attribute.getValue());
- break;
- case "publickey":
- builder.parsePublicKey(attribute.getValue());
- break;
- default:
- throw new BadConfigException(Section.PEER, Location.TOP_LEVEL,
- Reason.UNKNOWN_ATTRIBUTE, attribute.getKey());
- }
- }
- return builder.build();
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (!(obj instanceof Peer))
- return false;
- final Peer other = (Peer) obj;
- return allowedIps.equals(other.allowedIps)
- && endpoint.equals(other.endpoint)
- && persistentKeepalive.equals(other.persistentKeepalive)
- && preSharedKey.equals(other.preSharedKey)
- && publicKey.equals(other.publicKey);
- }
-
- /**
- * Returns the peer's set of allowed IPs.
- *
- * @return the set of allowed IPs
- */
- public Set<InetNetwork> getAllowedIps() {
- // The collection is already immutable.
- return allowedIps;
- }
-
- /**
- * Returns the peer's endpoint.
- *
- * @return the endpoint, or {@code Optional.empty()} if none is configured
- */
- public Optional<InetEndpoint> getEndpoint() {
- return endpoint;
- }
-
- /**
- * Returns the peer's persistent keepalive.
- *
- * @return the persistent keepalive, or {@code Optional.empty()} if none is configured
- */
- public Optional<Integer> getPersistentKeepalive() {
- return persistentKeepalive;
- }
-
- /**
- * Returns the peer's pre-shared key.
- *
- * @return the pre-shared key, or {@code Optional.empty()} if none is configured
- */
- public Optional<Key> getPreSharedKey() {
- return preSharedKey;
- }
-
- /**
- * Returns the peer's public key.
- *
- * @return the public key
- */
- public Key getPublicKey() {
- return publicKey;
- }
-
- @Override
- public int hashCode() {
- int hash = 1;
- hash = 31 * hash + allowedIps.hashCode();
- hash = 31 * hash + endpoint.hashCode();
- hash = 31 * hash + persistentKeepalive.hashCode();
- hash = 31 * hash + preSharedKey.hashCode();
- hash = 31 * hash + publicKey.hashCode();
- return hash;
- }
-
- /**
- * Converts the {@code Peer} into a string suitable for debugging purposes. The {@code Peer} is
- * identified by its public key and (if known) its endpoint.
- *
- * @return a concise single-line identifier for the {@code Peer}
- */
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("(Peer ");
- sb.append(publicKey.toBase64());
- endpoint.ifPresent(ep -> sb.append(" @").append(ep));
- sb.append(')');
- return sb.toString();
- }
-
- /**
- * Converts the {@code Peer} into a string suitable for inclusion in a {@code wg-quick}
- * configuration file.
- *
- * @return the {@code Peer} represented as a series of "Key = Value" lines
- */
- public String toWgQuickString() {
- final StringBuilder sb = new StringBuilder();
- if (!allowedIps.isEmpty())
- sb.append("AllowedIPs = ").append(Attribute.join(allowedIps)).append('\n');
- endpoint.ifPresent(ep -> sb.append("Endpoint = ").append(ep).append('\n'));
- persistentKeepalive.ifPresent(pk -> sb.append("PersistentKeepalive = ").append(pk).append('\n'));
- preSharedKey.ifPresent(psk -> sb.append("PreSharedKey = ").append(psk.toBase64()).append('\n'));
- sb.append("PublicKey = ").append(publicKey.toBase64()).append('\n');
- return sb.toString();
- }
-
- /**
- * Serializes the {@code Peer} for use with the WireGuard cross-platform userspace API. Note
- * that not all attributes are included in this representation.
- *
- * @return the {@code Peer} represented as a series of "key=value" lines
- */
- public String toWgUserspaceString() {
- final StringBuilder sb = new StringBuilder();
- // The order here is important: public_key signifies the beginning of a new peer.
- sb.append("public_key=").append(publicKey.toHex()).append('\n');
- for (final InetNetwork allowedIp : allowedIps)
- sb.append("allowed_ip=").append(allowedIp).append('\n');
- endpoint.flatMap(InetEndpoint::getResolved).ifPresent(ep -> sb.append("endpoint=").append(ep).append('\n'));
- persistentKeepalive.ifPresent(pk -> sb.append("persistent_keepalive_interval=").append(pk).append('\n'));
- preSharedKey.ifPresent(psk -> sb.append("preshared_key=").append(psk.toHex()).append('\n'));
- return sb.toString();
- }
-
- @SuppressWarnings("UnusedReturnValue")
- public static final class Builder {
- // See wg(8)
- private static final int MAX_PERSISTENT_KEEPALIVE = 65535;
-
- // Defaults to an empty set.
- private final Set<InetNetwork> allowedIps = new LinkedHashSet<>();
- // Defaults to not present.
- private Optional<InetEndpoint> endpoint = Optional.empty();
- // Defaults to not present.
- private Optional<Integer> persistentKeepalive = Optional.empty();
- // Defaults to not present.
- private Optional<Key> preSharedKey = Optional.empty();
- // No default; must be provided before building.
- @Nullable private Key publicKey;
-
- public Builder addAllowedIp(final InetNetwork allowedIp) {
- allowedIps.add(allowedIp);
- return this;
- }
-
- public Builder addAllowedIps(final Collection<InetNetwork> allowedIps) {
- this.allowedIps.addAll(allowedIps);
- return this;
- }
-
- public Peer build() throws BadConfigException {
- if (publicKey == null)
- throw new BadConfigException(Section.PEER, Location.PUBLIC_KEY,
- Reason.MISSING_ATTRIBUTE, null);
- return new Peer(this);
- }
-
- public Builder parseAllowedIPs(final CharSequence allowedIps) throws BadConfigException {
- try {
- for (final String allowedIp : Attribute.split(allowedIps))
- addAllowedIp(InetNetwork.parse(allowedIp));
- return this;
- } catch (final ParseException e) {
- throw new BadConfigException(Section.PEER, Location.ALLOWED_IPS, e);
- }
- }
-
- public Builder parseEndpoint(final String endpoint) throws BadConfigException {
- try {
- return setEndpoint(InetEndpoint.parse(endpoint));
- } catch (final ParseException e) {
- throw new BadConfigException(Section.PEER, Location.ENDPOINT, e);
- }
- }
-
- public Builder parsePersistentKeepalive(final String persistentKeepalive)
- throws BadConfigException {
- try {
- return setPersistentKeepalive(Integer.parseInt(persistentKeepalive));
- } catch (final NumberFormatException e) {
- throw new BadConfigException(Section.PEER, Location.PERSISTENT_KEEPALIVE,
- persistentKeepalive, e);
- }
- }
-
- public Builder parsePreSharedKey(final String preSharedKey) throws BadConfigException {
- try {
- return setPreSharedKey(Key.fromBase64(preSharedKey));
- } catch (final KeyFormatException e) {
- throw new BadConfigException(Section.PEER, Location.PRE_SHARED_KEY, e);
- }
- }
-
- public Builder parsePublicKey(final String publicKey) throws BadConfigException {
- try {
- return setPublicKey(Key.fromBase64(publicKey));
- } catch (final KeyFormatException e) {
- throw new BadConfigException(Section.PEER, Location.PUBLIC_KEY, e);
- }
- }
-
- public Builder setEndpoint(final InetEndpoint endpoint) {
- this.endpoint = Optional.of(endpoint);
- return this;
- }
-
- public Builder setPersistentKeepalive(final int persistentKeepalive)
- throws BadConfigException {
- if (persistentKeepalive < 0 || persistentKeepalive > MAX_PERSISTENT_KEEPALIVE)
- throw new BadConfigException(Section.PEER, Location.PERSISTENT_KEEPALIVE,
- Reason.INVALID_VALUE, String.valueOf(persistentKeepalive));
- this.persistentKeepalive = persistentKeepalive == 0 ?
- Optional.empty() : Optional.of(persistentKeepalive);
- return this;
- }
-
- public Builder setPreSharedKey(final Key preSharedKey) {
- this.preSharedKey = Optional.of(preSharedKey);
- return this;
- }
-
- public Builder setPublicKey(final Key publicKey) {
- this.publicKey = publicKey;
- return this;
- }
- }
-}
diff --git a/app/src/main/java/com/wireguard/crypto/Curve25519.java b/app/src/main/java/com/wireguard/crypto/Curve25519.java
deleted file mode 100644
index 5622fc5f..00000000
--- a/app/src/main/java/com/wireguard/crypto/Curve25519.java
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * Copyright © 2016 Southern Storm Software, Pty Ltd.
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.crypto;
-
-import androidx.annotation.Nullable;
-
-import java.util.Arrays;
-
-/**
- * Implementation of the Curve25519 elliptic curve algorithm.
- * <p>
- * This implementation was imported to WireGuard from noise-java:
- * https://github.com/rweather/noise-java
- * <p>
- * This implementation is based on that from arduinolibs:
- * https://github.com/rweather/arduinolibs
- * <p>
- * Differences in this version are due to using 26-bit limbs for the
- * representation instead of the 8/16/32-bit limbs in the original.
- * <p>
- * References: http://cr.yp.to/ecdh.html, RFC 7748
- */
-@SuppressWarnings({"MagicNumber", "NonConstantFieldWithUpperCaseName", "SuspiciousNameCombination"})
-public final class Curve25519 {
- // Numbers modulo 2^255 - 19 are broken up into ten 26-bit words.
- private static final int NUM_LIMBS_255BIT = 10;
- private static final int NUM_LIMBS_510BIT = 20;
-
- private final int[] A;
- private final int[] AA;
- private final int[] B;
- private final int[] BB;
- private final int[] C;
- private final int[] CB;
- private final int[] D;
- private final int[] DA;
- private final int[] E;
- private final long[] t1;
- private final int[] t2;
- private final int[] x_1;
- private final int[] x_2;
- private final int[] x_3;
- private final int[] z_2;
- private final int[] z_3;
-
- /**
- * Constructs the temporary state holder for Curve25519 evaluation.
- */
- private Curve25519() {
- // Allocate memory for all of the temporary variables we will need.
- x_1 = new int[NUM_LIMBS_255BIT];
- x_2 = new int[NUM_LIMBS_255BIT];
- x_3 = new int[NUM_LIMBS_255BIT];
- z_2 = new int[NUM_LIMBS_255BIT];
- z_3 = new int[NUM_LIMBS_255BIT];
- A = new int[NUM_LIMBS_255BIT];
- B = new int[NUM_LIMBS_255BIT];
- C = new int[NUM_LIMBS_255BIT];
- D = new int[NUM_LIMBS_255BIT];
- E = new int[NUM_LIMBS_255BIT];
- AA = new int[NUM_LIMBS_255BIT];
- BB = new int[NUM_LIMBS_255BIT];
- DA = new int[NUM_LIMBS_255BIT];
- CB = new int[NUM_LIMBS_255BIT];
- t1 = new long[NUM_LIMBS_510BIT];
- t2 = new int[NUM_LIMBS_510BIT];
- }
-
- /**
- * Conditional swap of two values.
- *
- * @param select Set to 1 to swap, 0 to leave as-is.
- * @param x The first value.
- * @param y The second value.
- */
- private static void cswap(int select, final int[] x, final int[] y) {
- select = -select;
- for (int index = 0; index < NUM_LIMBS_255BIT; ++index) {
- final int dummy = select & (x[index] ^ y[index]);
- x[index] ^= dummy;
- y[index] ^= dummy;
- }
- }
-
- /**
- * Evaluates the Curve25519 curve.
- *
- * @param result Buffer to place the result of the evaluation into.
- * @param offset Offset into the result buffer.
- * @param privateKey The private key to use in the evaluation.
- * @param publicKey The public key to use in the evaluation, or null
- * if the base point of the curve should be used.
- */
- public static void eval(final byte[] result, final int offset,
- final byte[] privateKey, @Nullable final byte[] publicKey) {
- final Curve25519 state = new Curve25519();
- try {
- // Unpack the public key value. If null, use 9 as the base point.
- Arrays.fill(state.x_1, 0);
- if (publicKey != null) {
- // Convert the input value from little-endian into 26-bit limbs.
- for (int index = 0; index < 32; ++index) {
- final int bit = (index * 8) % 26;
- final int word = (index * 8) / 26;
- final int value = publicKey[index] & 0xFF;
- if (bit <= (26 - 8)) {
- state.x_1[word] |= value << bit;
- } else {
- state.x_1[word] |= value << bit;
- state.x_1[word] &= 0x03FFFFFF;
- state.x_1[word + 1] |= value >> (26 - bit);
- }
- }
-
- // Just in case, we reduce the number modulo 2^255 - 19 to
- // make sure that it is in range of the field before we start.
- // This eliminates values between 2^255 - 19 and 2^256 - 1.
- state.reduceQuick(state.x_1);
- state.reduceQuick(state.x_1);
- } else {
- state.x_1[0] = 9;
- }
-
- // Initialize the other temporary variables.
- Arrays.fill(state.x_2, 0); // x_2 = 1
- state.x_2[0] = 1;
- Arrays.fill(state.z_2, 0); // z_2 = 0
- System.arraycopy(state.x_1, 0, state.x_3, 0, state.x_1.length); // x_3 = x_1
- Arrays.fill(state.z_3, 0); // z_3 = 1
- state.z_3[0] = 1;
-
- // Evaluate the curve for every bit of the private key.
- state.evalCurve(privateKey);
-
- // Compute x_2 * (z_2 ^ (p - 2)) where p = 2^255 - 19.
- state.recip(state.z_3, state.z_2);
- state.mul(state.x_2, state.x_2, state.z_3);
-
- // Convert x_2 into little-endian in the result buffer.
- for (int index = 0; index < 32; ++index) {
- final int bit = (index * 8) % 26;
- final int word = (index * 8) / 26;
- if (bit <= (26 - 8))
- result[offset + index] = (byte) (state.x_2[word] >> bit);
- else
- result[offset + index] = (byte) ((state.x_2[word] >> bit) | (state.x_2[word + 1] << (26 - bit)));
- }
- } finally {
- // Clean up all temporary state before we exit.
- state.destroy();
- }
- }
-
- /**
- * Subtracts two numbers modulo 2^255 - 19.
- *
- * @param result The result.
- * @param x The first number to subtract.
- * @param y The second number to subtract.
- */
- private static void sub(final int[] result, final int[] x, final int[] y) {
- int index;
- int borrow;
-
- // Subtract y from x to generate the intermediate result.
- borrow = 0;
- for (index = 0; index < NUM_LIMBS_255BIT; ++index) {
- borrow = x[index] - y[index] - ((borrow >> 26) & 0x01);
- result[index] = borrow & 0x03FFFFFF;
- }
-
- // If we had a borrow, then the result has gone negative and we
- // have to add 2^255 - 19 to the result to make it positive again.
- // The top bits of "borrow" will be all 1's if there is a borrow
- // or it will be all 0's if there was no borrow. Easiest is to
- // conditionally subtract 19 and then mask off the high bits.
- borrow = result[0] - ((-((borrow >> 26) & 0x01)) & 19);
- result[0] = borrow & 0x03FFFFFF;
- for (index = 1; index < NUM_LIMBS_255BIT; ++index) {
- borrow = result[index] - ((borrow >> 26) & 0x01);
- result[index] = borrow & 0x03FFFFFF;
- }
- result[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
- }
-
- /**
- * Adds two numbers modulo 2^255 - 19.
- *
- * @param result The result.
- * @param x The first number to add.
- * @param y The second number to add.
- */
- private void add(final int[] result, final int[] x, final int[] y) {
- int carry = x[0] + y[0];
- result[0] = carry & 0x03FFFFFF;
- for (int index = 1; index < NUM_LIMBS_255BIT; ++index) {
- carry = (carry >> 26) + x[index] + y[index];
- result[index] = carry & 0x03FFFFFF;
- }
- reduceQuick(result);
- }
-
- /**
- * Destroy all sensitive data in this object.
- */
- private void destroy() {
- // Destroy all temporary variables.
- Arrays.fill(x_1, 0);
- Arrays.fill(x_2, 0);
- Arrays.fill(x_3, 0);
- Arrays.fill(z_2, 0);
- Arrays.fill(z_3, 0);
- Arrays.fill(A, 0);
- Arrays.fill(B, 0);
- Arrays.fill(C, 0);
- Arrays.fill(D, 0);
- Arrays.fill(E, 0);
- Arrays.fill(AA, 0);
- Arrays.fill(BB, 0);
- Arrays.fill(DA, 0);
- Arrays.fill(CB, 0);
- Arrays.fill(t1, 0L);
- Arrays.fill(t2, 0);
- }
-
- /**
- * Evaluates the curve for every bit in a secret key.
- *
- * @param s The 32-byte secret key.
- */
- private void evalCurve(final byte[] s) {
- int sposn = 31;
- int sbit = 6;
- int svalue = s[sposn] | 0x40;
- int swap = 0;
-
- // Iterate over all 255 bits of "s" from the highest to the lowest.
- // We ignore the high bit of the 256-bit representation of "s".
- while (true) {
- // Conditional swaps on entry to this bit but only if we
- // didn't swap on the previous bit.
- final int select = (svalue >> sbit) & 0x01;
- swap ^= select;
- cswap(swap, x_2, x_3);
- cswap(swap, z_2, z_3);
- swap = select;
-
- // Evaluate the curve.
- add(A, x_2, z_2); // A = x_2 + z_2
- square(AA, A); // AA = A^2
- sub(B, x_2, z_2); // B = x_2 - z_2
- square(BB, B); // BB = B^2
- sub(E, AA, BB); // E = AA - BB
- add(C, x_3, z_3); // C = x_3 + z_3
- sub(D, x_3, z_3); // D = x_3 - z_3
- mul(DA, D, A); // DA = D * A
- mul(CB, C, B); // CB = C * B
- add(x_3, DA, CB); // x_3 = (DA + CB)^2
- square(x_3, x_3);
- sub(z_3, DA, CB); // z_3 = x_1 * (DA - CB)^2
- square(z_3, z_3);
- mul(z_3, z_3, x_1);
- mul(x_2, AA, BB); // x_2 = AA * BB
- mulA24(z_2, E); // z_2 = E * (AA + a24 * E)
- add(z_2, z_2, AA);
- mul(z_2, z_2, E);
-
- // Move onto the next lower bit of "s".
- if (sbit > 0) {
- --sbit;
- } else if (sposn == 0) {
- break;
- } else if (sposn == 1) {
- --sposn;
- svalue = s[sposn] & 0xF8;
- sbit = 7;
- } else {
- --sposn;
- svalue = s[sposn];
- sbit = 7;
- }
- }
-
- // Final conditional swaps.
- cswap(swap, x_2, x_3);
- cswap(swap, z_2, z_3);
- }
-
- /**
- * Multiplies two numbers modulo 2^255 - 19.
- *
- * @param result The result.
- * @param x The first number to multiply.
- * @param y The second number to multiply.
- */
- private void mul(final int[] result, final int[] x, final int[] y) {
- // Multiply the two numbers to create the intermediate result.
- long v = x[0];
- for (int i = 0; i < NUM_LIMBS_255BIT; ++i) {
- t1[i] = v * y[i];
- }
- for (int i = 1; i < NUM_LIMBS_255BIT; ++i) {
- v = x[i];
- for (int j = 0; j < (NUM_LIMBS_255BIT - 1); ++j) {
- t1[i + j] += v * y[j];
- }
- t1[i + NUM_LIMBS_255BIT - 1] = v * y[NUM_LIMBS_255BIT - 1];
- }
-
- // Propagate carries and convert back into 26-bit words.
- v = t1[0];
- t2[0] = ((int) v) & 0x03FFFFFF;
- for (int i = 1; i < NUM_LIMBS_510BIT; ++i) {
- v = (v >> 26) + t1[i];
- t2[i] = ((int) v) & 0x03FFFFFF;
- }
-
- // Reduce the result modulo 2^255 - 19.
- reduce(result, t2, NUM_LIMBS_255BIT);
- }
-
- /**
- * Multiplies a number by the a24 constant, modulo 2^255 - 19.
- *
- * @param result The result.
- * @param x The number to multiply by a24.
- */
- private void mulA24(final int[] result, final int[] x) {
- final long a24 = 121665;
- long carry = 0;
- for (int index = 0; index < NUM_LIMBS_255BIT; ++index) {
- carry += a24 * x[index];
- t2[index] = ((int) carry) & 0x03FFFFFF;
- carry >>= 26;
- }
- t2[NUM_LIMBS_255BIT] = ((int) carry) & 0x03FFFFFF;
- reduce(result, t2, 1);
- }
-
- /**
- * Raise x to the power of (2^250 - 1).
- *
- * @param result The result. Must not overlap with x.
- * @param x The argument.
- */
- private void pow250(final int[] result, final int[] x) {
- // The big-endian hexadecimal expansion of (2^250 - 1) is:
- // 03FFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
- //
- // The naive implementation needs to do 2 multiplications per 1 bit and
- // 1 multiplication per 0 bit. We can improve upon this by creating a
- // pattern 0000000001 ... 0000000001. If we square and multiply the
- // pattern by itself we can turn the pattern into the partial results
- // 0000000011 ... 0000000011, 0000000111 ... 0000000111, etc.
- // This averages out to about 1.1 multiplications per 1 bit instead of 2.
-
- // Build a pattern of 250 bits in length of repeated copies of 0000000001.
- square(A, x);
- for (int j = 0; j < 9; ++j)
- square(A, A);
- mul(result, A, x);
- for (int i = 0; i < 23; ++i) {
- for (int j = 0; j < 10; ++j)
- square(A, A);
- mul(result, result, A);
- }
-
- // Multiply bit-shifted versions of the 0000000001 pattern into
- // the result to "fill in" the gaps in the pattern.
- square(A, result);
- mul(result, result, A);
- for (int j = 0; j < 8; ++j) {
- square(A, A);
- mul(result, result, A);
- }
- }
-
- /**
- * Computes the reciprocal of a number modulo 2^255 - 19.
- *
- * @param result The result. Must not overlap with x.
- * @param x The argument.
- */
- private void recip(final int[] result, final int[] x) {
- // The reciprocal is the same as x ^ (p - 2) where p = 2^255 - 19.
- // The big-endian hexadecimal expansion of (p - 2) is:
- // 7FFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFEB
- // Start with the 250 upper bits of the expansion of (p - 2).
- pow250(result, x);
-
- // Deal with the 5 lowest bits of (p - 2), 01011, from highest to lowest.
- square(result, result);
- square(result, result);
- mul(result, result, x);
- square(result, result);
- square(result, result);
- mul(result, result, x);
- square(result, result);
- mul(result, result, x);
- }
-
- /**
- * Reduce a number modulo 2^255 - 19.
- *
- * @param result The result.
- * @param x The value to be reduced. This array will be
- * modified during the reduction.
- * @param size The number of limbs in the high order half of x.
- */
- private void reduce(final int[] result, final int[] x, final int size) {
- // Calculate (x mod 2^255) + ((x / 2^255) * 19) which will
- // either produce the answer we want or it will produce a
- // value of the form "answer + j * (2^255 - 19)". There are
- // 5 left-over bits in the top-most limb of the bottom half.
- int carry = 0;
- int limb = x[NUM_LIMBS_255BIT - 1] >> 21;
- x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
- for (int index = 0; index < size; ++index) {
- limb += x[NUM_LIMBS_255BIT + index] << 5;
- carry += (limb & 0x03FFFFFF) * 19 + x[index];
- x[index] = carry & 0x03FFFFFF;
- limb >>= 26;
- carry >>= 26;
- }
- if (size < NUM_LIMBS_255BIT) {
- // The high order half of the number is short; e.g. for mulA24().
- // Propagate the carry through the rest of the low order part.
- for (int index = size; index < NUM_LIMBS_255BIT; ++index) {
- carry += x[index];
- x[index] = carry & 0x03FFFFFF;
- carry >>= 26;
- }
- }
-
- // The "j" value may still be too large due to the final carry-out.
- // We must repeat the reduction. If we already have the answer,
- // then this won't do any harm but we must still do the calculation
- // to preserve the overall timing. The "j" value will be between
- // 0 and 19, which means that the carry we care about is in the
- // top 5 bits of the highest limb of the bottom half.
- carry = (x[NUM_LIMBS_255BIT - 1] >> 21) * 19;
- x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
- for (int index = 0; index < NUM_LIMBS_255BIT; ++index) {
- carry += x[index];
- result[index] = carry & 0x03FFFFFF;
- carry >>= 26;
- }
-
- // At this point "x" will either be the answer or it will be the
- // answer plus (2^255 - 19). Perform a trial subtraction to
- // complete the reduction process.
- reduceQuick(result);
- }
-
- /**
- * Reduces a number modulo 2^255 - 19 where it is known that the
- * number can be reduced with only 1 trial subtraction.
- *
- * @param x The number to reduce, and the result.
- */
- private void reduceQuick(final int[] x) {
- // Perform a trial subtraction of (2^255 - 19) from "x" which is
- // equivalent to adding 19 and subtracting 2^255. We add 19 here;
- // the subtraction of 2^255 occurs in the next step.
- int carry = 19;
- for (int index = 0; index < NUM_LIMBS_255BIT; ++index) {
- carry += x[index];
- t2[index] = carry & 0x03FFFFFF;
- carry >>= 26;
- }
-
- // If there was a borrow, then the original "x" is the correct answer.
- // If there was no borrow, then "t2" is the correct answer. Select the
- // correct answer but do it in a way that instruction timing will not
- // reveal which value was selected. Borrow will occur if bit 21 of
- // "t2" is zero. Turn the bit into a selection mask.
- final int mask = -((t2[NUM_LIMBS_255BIT - 1] >> 21) & 0x01);
- final int nmask = ~mask;
- t2[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
- for (int index = 0; index < NUM_LIMBS_255BIT; ++index)
- x[index] = (x[index] & nmask) | (t2[index] & mask);
- }
-
- /**
- * Squares a number modulo 2^255 - 19.
- *
- * @param result The result.
- * @param x The number to square.
- */
- private void square(final int[] result, final int[] x) {
- mul(result, x, x);
- }
-}
diff --git a/app/src/main/java/com/wireguard/crypto/Key.java b/app/src/main/java/com/wireguard/crypto/Key.java
deleted file mode 100644
index 6648a5f3..00000000
--- a/app/src/main/java/com/wireguard/crypto/Key.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.crypto;
-
-import com.wireguard.crypto.KeyFormatException.Type;
-
-import java.security.MessageDigest;
-import java.security.SecureRandom;
-import java.util.Arrays;
-
-/**
- * Represents a WireGuard public or private key. This class uses specialized constant-time base64
- * and hexadecimal codec implementations that resist side-channel attacks.
- * <p>
- * Instances of this class are immutable.
- */
-@SuppressWarnings("MagicNumber")
-public final class Key {
- private final byte[] key;
-
- /**
- * Constructs an object encapsulating the supplied key.
- *
- * @param key an array of bytes containing a binary key. Callers of this constructor are
- * responsible for ensuring that the array is of the correct length.
- */
- private Key(final byte[] key) {
- // Defensively copy to ensure immutability.
- this.key = Arrays.copyOf(key, key.length);
- }
-
- /**
- * Decodes a single 4-character base64 chunk to an integer in constant time.
- *
- * @param src an array of at least 4 characters in base64 format
- * @param srcOffset the offset of the beginning of the chunk in {@code src}
- * @return the decoded 3-byte integer, or some arbitrary integer value if the input was not
- * valid base64
- */
- private static int decodeBase64(final char[] src, final int srcOffset) {
- int val = 0;
- for (int i = 0; i < 4; ++i) {
- final char c = src[i + srcOffset];
- val |= (-1
- + ((((('A' - 1) - c) & (c - ('Z' + 1))) >>> 8) & (c - 64))
- + ((((('a' - 1) - c) & (c - ('z' + 1))) >>> 8) & (c - 70))
- + ((((('0' - 1) - c) & (c - ('9' + 1))) >>> 8) & (c + 5))
- + ((((('+' - 1) - c) & (c - ('+' + 1))) >>> 8) & 63)
- + ((((('/' - 1) - c) & (c - ('/' + 1))) >>> 8) & 64)
- ) << (18 - 6 * i);
- }
- return val;
- }
-
- /**
- * Encodes a single 4-character base64 chunk from 3 consecutive bytes in constant time.
- *
- * @param src an array of at least 3 bytes
- * @param srcOffset the offset of the beginning of the chunk in {@code src}
- * @param dest an array of at least 4 characters
- * @param destOffset the offset of the beginning of the chunk in {@code dest}
- */
- private static void encodeBase64(final byte[] src, final int srcOffset,
- final char[] dest, final int destOffset) {
- final byte[] input = {
- (byte) ((src[srcOffset] >>> 2) & 63),
- (byte) ((src[srcOffset] << 4 | ((src[1 + srcOffset] & 0xff) >>> 4)) & 63),
- (byte) ((src[1 + srcOffset] << 2 | ((src[2 + srcOffset] & 0xff) >>> 6)) & 63),
- (byte) ((src[2 + srcOffset]) & 63),
- };
- for (int i = 0; i < 4; ++i) {
- dest[i + destOffset] = (char) (input[i] + 'A'
- + (((25 - input[i]) >>> 8) & 6)
- - (((51 - input[i]) >>> 8) & 75)
- - (((61 - input[i]) >>> 8) & 15)
- + (((62 - input[i]) >>> 8) & 3));
- }
- }
-
- /**
- * Decodes a WireGuard public or private key from its base64 string representation. This
- * function throws a {@link KeyFormatException} if the source string is not well-formed.
- *
- * @param str the base64 string representation of a WireGuard key
- * @return the decoded key encapsulated in an immutable container
- */
- public static Key fromBase64(final String str) throws KeyFormatException {
- final char[] input = str.toCharArray();
- if (input.length != Format.BASE64.length || input[Format.BASE64.length - 1] != '=')
- throw new KeyFormatException(Format.BASE64, Type.LENGTH);
- final byte[] key = new byte[Format.BINARY.length];
- int i;
- int ret = 0;
- for (i = 0; i < key.length / 3; ++i) {
- final int val = decodeBase64(input, i * 4);
- ret |= val >>> 31;
- key[i * 3] = (byte) ((val >>> 16) & 0xff);
- key[i * 3 + 1] = (byte) ((val >>> 8) & 0xff);
- key[i * 3 + 2] = (byte) (val & 0xff);
- }
- final char[] endSegment = {
- input[i * 4],
- input[i * 4 + 1],
- input[i * 4 + 2],
- 'A',
- };
- final int val = decodeBase64(endSegment, 0);
- ret |= (val >>> 31) | (val & 0xff);
- key[i * 3] = (byte) ((val >>> 16) & 0xff);
- key[i * 3 + 1] = (byte) ((val >>> 8) & 0xff);
-
- if (ret != 0)
- throw new KeyFormatException(Format.BASE64, Type.CONTENTS);
- return new Key(key);
- }
-
- /**
- * Wraps a WireGuard public or private key in an immutable container. This function throws a
- * {@link KeyFormatException} if the source data is not the correct length.
- *
- * @param bytes an array of bytes containing a WireGuard key in binary format
- * @return the key encapsulated in an immutable container
- */
- public static Key fromBytes(final byte[] bytes) throws KeyFormatException {
- if (bytes.length != Format.BINARY.length)
- throw new KeyFormatException(Format.BINARY, Type.LENGTH);
- return new Key(bytes);
- }
-
- /**
- * Decodes a WireGuard public or private key from its hexadecimal string representation. This
- * function throws a {@link KeyFormatException} if the source string is not well-formed.
- *
- * @param str the hexadecimal string representation of a WireGuard key
- * @return the decoded key encapsulated in an immutable container
- */
- public static Key fromHex(final String str) throws KeyFormatException {
- final char[] input = str.toCharArray();
- if (input.length != Format.HEX.length)
- throw new KeyFormatException(Format.HEX, Type.LENGTH);
- final byte[] key = new byte[Format.BINARY.length];
- int ret = 0;
- for (int i = 0; i < key.length; ++i) {
- int c;
- int cNum;
- int cNum0;
- int cAlpha;
- int cAlpha0;
- int cVal;
- final int cAcc;
-
- c = input[i * 2];
- cNum = c ^ 48;
- cNum0 = ((cNum - 10) >>> 8) & 0xff;
- cAlpha = (c & ~32) - 55;
- cAlpha0 = (((cAlpha - 10) ^ (cAlpha - 16)) >>> 8) & 0xff;
- ret |= ((cNum0 | cAlpha0) - 1) >>> 8;
- cVal = (cNum0 & cNum) | (cAlpha0 & cAlpha);
- cAcc = cVal * 16;
-
- c = input[i * 2 + 1];
- cNum = c ^ 48;
- cNum0 = ((cNum - 10) >>> 8) & 0xff;
- cAlpha = (c & ~32) - 55;
- cAlpha0 = (((cAlpha - 10) ^ (cAlpha - 16)) >>> 8) & 0xff;
- ret |= ((cNum0 | cAlpha0) - 1) >>> 8;
- cVal = (cNum0 & cNum) | (cAlpha0 & cAlpha);
- key[i] = (byte) (cAcc | cVal);
- }
- if (ret != 0)
- throw new KeyFormatException(Format.HEX, Type.CONTENTS);
- return new Key(key);
- }
-
- /**
- * Generates a private key using the system's {@link SecureRandom} number generator.
- *
- * @return a well-formed random private key
- */
- static Key generatePrivateKey() {
- final SecureRandom secureRandom = new SecureRandom();
- final byte[] privateKey = new byte[Format.BINARY.getLength()];
- secureRandom.nextBytes(privateKey);
- privateKey[0] &= 248;
- privateKey[31] &= 127;
- privateKey[31] |= 64;
- return new Key(privateKey);
- }
-
- /**
- * Generates a public key from an existing private key.
- *
- * @param privateKey a private key
- * @return a well-formed public key that corresponds to the supplied private key
- */
- static Key generatePublicKey(final Key privateKey) {
- final byte[] publicKey = new byte[Format.BINARY.getLength()];
- Curve25519.eval(publicKey, 0, privateKey.getBytes(), null);
- return new Key(publicKey);
- }
-
- /**
- * Returns the key as an array of bytes.
- *
- * @return an array of bytes containing the raw binary key
- */
- public byte[] getBytes() {
- // Defensively copy to ensure immutability.
- return Arrays.copyOf(key, key.length);
- }
-
- /**
- * Encodes the key to base64.
- *
- * @return a string containing the encoded key
- */
- public String toBase64() {
- final char[] output = new char[Format.BASE64.length];
- int i;
- for (i = 0; i < key.length / 3; ++i)
- encodeBase64(key, i * 3, output, i * 4);
- final byte[] endSegment = {
- key[i * 3],
- key[i * 3 + 1],
- 0,
- };
- encodeBase64(endSegment, 0, output, i * 4);
- output[Format.BASE64.length - 1] = '=';
- return new String(output);
- }
-
- /**
- * Encodes the key to hexadecimal ASCII characters.
- *
- * @return a string containing the encoded key
- */
- public String toHex() {
- final char[] output = new char[Format.HEX.length];
- for (int i = 0; i < key.length; ++i) {
- output[i * 2] = (char) (87 + (key[i] >> 4 & 0xf)
- + ((((key[i] >> 4 & 0xf) - 10) >> 8) & ~38));
- output[i * 2 + 1] = (char) (87 + (key[i] & 0xf)
- + ((((key[i] & 0xf) - 10) >> 8) & ~38));
- }
- return new String(output);
- }
-
- @Override
- public int hashCode() {
- int ret = 0;
- for (int i = 0; i < key.length / 4; ++i)
- ret ^= (key[i * 4 + 0] >> 0) + (key[i * 4 + 1] >> 8) + (key[i * 4 + 2] >> 16) + (key[i * 4 + 3] >> 24);
- return ret;
- }
-
- @Override
- public boolean equals(final Object obj) {
- if (obj == this)
- return true;
- if (obj == null || obj.getClass() != getClass())
- return false;
- final Key other = (Key) obj;
- return MessageDigest.isEqual(key, other.key);
- }
-
- /**
- * The supported formats for encoding a WireGuard key.
- */
- public enum Format {
- BASE64(44),
- BINARY(32),
- HEX(64);
-
- private final int length;
-
- Format(final int length) {
- this.length = length;
- }
-
- public int getLength() {
- return length;
- }
- }
-
-}
diff --git a/app/src/main/java/com/wireguard/crypto/KeyFormatException.java b/app/src/main/java/com/wireguard/crypto/KeyFormatException.java
deleted file mode 100644
index 5818b4d4..00000000
--- a/app/src/main/java/com/wireguard/crypto/KeyFormatException.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.crypto;
-
-/**
- * An exception thrown when attempting to parse an invalid key (too short, too long, or byte
- * data inappropriate for the format). The format being parsed can be accessed with the
- * {@link #getFormat} method.
- */
-public final class KeyFormatException extends Exception {
- private final Key.Format format;
- private final Type type;
-
- KeyFormatException(final Key.Format format, final Type type) {
- this.format = format;
- this.type = type;
- }
-
- public Key.Format getFormat() {
- return format;
- }
-
- public Type getType() {
- return type;
- }
-
- public enum Type {
- CONTENTS,
- LENGTH
- }
-}
diff --git a/app/src/main/java/com/wireguard/crypto/KeyPair.java b/app/src/main/java/com/wireguard/crypto/KeyPair.java
deleted file mode 100644
index f8238e91..00000000
--- a/app/src/main/java/com/wireguard/crypto/KeyPair.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.crypto;
-
-/**
- * Represents a Curve25519 key pair as used by WireGuard.
- * <p>
- * Instances of this class are immutable.
- */
-public class KeyPair {
- private final Key privateKey;
- private final Key publicKey;
-
- /**
- * Creates a key pair using a newly-generated private key.
- */
- public KeyPair() {
- this(Key.generatePrivateKey());
- }
-
- /**
- * Creates a key pair using an existing private key.
- *
- * @param privateKey a private key, used to derive the public key
- */
- public KeyPair(final Key privateKey) {
- this.privateKey = privateKey;
- publicKey = Key.generatePublicKey(privateKey);
- }
-
- /**
- * Returns the private key from the key pair.
- *
- * @return the private key
- */
- public Key getPrivateKey() {
- return privateKey;
- }
-
- /**
- * Returns the public key from the key pair.
- *
- * @return the public key
- */
- public Key getPublicKey() {
- return publicKey;
- }
-}
diff --git a/app/src/main/java/com/wireguard/util/NonNullForAll.java b/app/src/main/java/com/wireguard/util/NonNullForAll.java
deleted file mode 100644
index f179fa49..00000000
--- a/app/src/main/java/com/wireguard/util/NonNullForAll.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package com.wireguard.util;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import javax.annotation.Nonnull;
-import javax.annotation.meta.TypeQualifierDefault;
-
-/**
- * This annotation can be applied to a package, class or method to indicate that all
- * class fields and method parameters and return values in that element are nonnull
- * by default unless overridden.
- */
-@Documented
-@Nonnull
-@TypeQualifierDefault({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface NonNullForAll {
-}