global: Automatic code formatting

Signed-off-by: Samuel Holland <samuel@sholland.org>
This commit is contained in:
Samuel Holland 2018-04-30 11:39:12 -05:00
parent e2636320b7
commit 7eedf08d4b
22 changed files with 964 additions and 990 deletions

View File

@ -35,8 +35,8 @@
<activity <activity
android:name=".activity.SettingsActivity" android:name=".activity.SettingsActivity"
android:label="@string/settings" android:label="@string/settings"
android:theme="@style/SettingsTheme" android:parentActivityName=".activity.MainActivity"
android:parentActivityName=".activity.MainActivity" /> android:theme="@style/SettingsTheme" />
<activity <activity
android:name=".activity.TunnelCreatorActivity" android:name=".activity.TunnelCreatorActivity"
@ -62,7 +62,8 @@
<intent-filter> <intent-filter>
<action android:name="android.net.VpnService" /> <action android:name="android.net.VpnService" />
</intent-filter> </intent-filter>
<meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON" <meta-data
android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
android:value="false" /> android:value="false" />
</service> </service>

View File

@ -55,11 +55,11 @@ public class Application extends android.app.Application {
public interface ApplicationComponent { public interface ApplicationComponent {
AsyncWorker getAsyncWorker(); AsyncWorker getAsyncWorker();
Class getBackendType();
ToolsInstaller getToolsInstaller(); ToolsInstaller getToolsInstaller();
TunnelManager getTunnelManager(); TunnelManager getTunnelManager();
Class getBackendType();
} }
@Qualifier @Qualifier

View File

@ -23,11 +23,6 @@ import java.util.List;
*/ */
public class SettingsActivity extends AppCompatActivity { public class SettingsActivity extends AppCompatActivity {
@FunctionalInterface
public interface PermissionRequestCallback {
void done(String[] permissions, int[] grantResults);
}
private HashMap<Integer, PermissionRequestCallback> permissionRequestCallbacks = new HashMap<>(); private HashMap<Integer, PermissionRequestCallback> permissionRequestCallbacks = new HashMap<>();
private int permissionRequestCounter = 0; private int permissionRequestCounter = 0;
@ -48,15 +43,6 @@ public class SettingsActivity extends AppCompatActivity {
ActivityCompat.requestPermissions(this, needPermissions.toArray(new String[needPermissions.size()]), idx); ActivityCompat.requestPermissions(this, needPermissions.toArray(new String[needPermissions.size()]), idx);
} }
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
final PermissionRequestCallback f = permissionRequestCallbacks.get(requestCode);
if (f != null) {
permissionRequestCallbacks.remove(requestCode);
f.done(permissions, grantResults);
}
}
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -78,6 +64,20 @@ public class SettingsActivity extends AppCompatActivity {
} }
} }
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
final PermissionRequestCallback f = permissionRequestCallbacks.get(requestCode);
if (f != null) {
permissionRequestCallbacks.remove(requestCode);
f.done(permissions, grantResults);
}
}
@FunctionalInterface
public interface PermissionRequestCallback {
void done(String[] permissions, int[] grantResults);
}
public static class SettingsFragment extends PreferenceFragmentCompat { public static class SettingsFragment extends PreferenceFragmentCompat {
@Override @Override
public void onCreatePreferences(final Bundle savedInstanceState, final String key) { public void onCreatePreferences(final Bundle savedInstanceState, final String key) {

View File

@ -27,48 +27,20 @@ import java9.util.concurrent.CompletableFuture;
public final class GoBackend implements Backend { public final class GoBackend implements Backend {
private static final String TAG = "WireGuard/" + GoBackend.class.getSimpleName(); private static final String TAG = "WireGuard/" + GoBackend.class.getSimpleName();
private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>();
static { static {
System.loadLibrary("wg-go"); System.loadLibrary("wg-go");
} }
private Context context;
private Tunnel currentTunnel; private Tunnel currentTunnel;
private int currentTunnelHandle = -1; private int currentTunnelHandle = -1;
private Context context;
public GoBackend(Context context) { public GoBackend(Context context) {
this.context = context; this.context = context;
} }
private void startVpnService() {
context.startService(new Intent(context, VpnService.class));
}
public static class VpnService extends android.net.VpnService {
@Override
public void onCreate() {
vpnService.complete(this);
super.onCreate();
}
@Override
public void onDestroy() {
for (final Tunnel tunnel : Application.getComponent().getTunnelManager().getTunnels()) {
if (tunnel != null && tunnel.getState() != State.DOWN)
tunnel.setState(State.DOWN);
}
vpnService = vpnService.newIncompleteFuture();
super.onDestroy();
}
public Builder getBuilder() {
return new Builder();
}
}
private static CompletableFuture<VpnService> vpnService = new CompletableFuture<>();
private static native int wgGetSocketV4(int handle); private static native int wgGetSocketV4(int handle);
private static native int wgGetSocketV6(int handle); private static native int wgGetSocketV6(int handle);
@ -219,4 +191,30 @@ public final class GoBackend implements Backend {
currentTunnelHandle = -1; currentTunnelHandle = -1;
} }
} }
private void startVpnService() {
context.startService(new Intent(context, VpnService.class));
}
public static class VpnService extends android.net.VpnService {
public Builder getBuilder() {
return new Builder();
}
@Override
public void onCreate() {
vpnService.complete(this);
super.onCreate();
}
@Override
public void onDestroy() {
for (final Tunnel tunnel : Application.getComponent().getTunnelManager().getTunnels()) {
if (tunnel != null && tunnel.getState() != State.DOWN)
tunnel.setState(State.DOWN);
}
vpnService = vpnService.newIncompleteFuture();
super.onDestroy();
}
}
} }

View File

@ -20,6 +20,10 @@ import com.wireguard.config.Config;
public class TunnelDetailFragment extends BaseFragment { public class TunnelDetailFragment extends BaseFragment {
private TunnelDetailFragmentBinding binding; private TunnelDetailFragmentBinding binding;
private void onConfigLoaded(final String name, final Config config) {
binding.setConfig(new Config.Observable(config, name));
}
@Override @Override
public void onCreate(final Bundle savedInstanceState) { public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -46,10 +50,6 @@ public class TunnelDetailFragment extends BaseFragment {
super.onDestroyView(); super.onDestroyView();
} }
private void onConfigLoaded(final String name, final Config config) {
binding.setConfig(new Config.Observable(config, name));
}
@Override @Override
public void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) { public void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) {
if (binding == null) if (binding == null)

View File

@ -3,8 +3,6 @@ package com.wireguard.android.fragment;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
@ -41,46 +39,30 @@ public class TunnelEditorFragment extends BaseFragment {
binding.setConfig(new Config.Observable(config, name)); binding.setConfig(new Config.Observable(config, name));
} }
private void onConfigSaved(final Tunnel savedTunnel, final Config config,
final Throwable throwable) {
final String message;
if (throwable == null) {
message = getString(R.string.config_save_success, savedTunnel.getName());
Log.d(TAG, message);
onFinished();
} else {
final String error = ExceptionLoggers.unwrap(throwable).getMessage();
message = getString(R.string.config_save_error, savedTunnel.getName(), error);
Log.e(TAG, message, throwable);
if (binding != null) {
final CoordinatorLayout container = binding.mainContainer;
Snackbar.make(container, message, Snackbar.LENGTH_LONG).show();
}
}
}
@Override @Override
public void onCreate(final Bundle savedInstanceState) { public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@Override
public void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) {
tunnel = newTunnel;
if (binding == null)
return;
binding.setConfig(new Config.Observable(null, null));
if (tunnel != null)
tunnel.getConfigAsync().thenAccept(a -> onConfigLoaded(tunnel.getName(), a));
}
@Override
public void onSaveInstanceState(@NonNull final Bundle outState) {
outState.putParcelable(KEY_LOCAL_CONFIG, binding.getConfig());
outState.putString(KEY_ORIGINAL_NAME, tunnel == null ? null : tunnel.getName());
super.onSaveInstanceState(outState);
}
@Override
public void onViewStateRestored(final Bundle savedInstanceState) {
if (savedInstanceState == null) {
onSelectedTunnelChanged(null, getSelectedTunnel());
} else {
tunnel = getSelectedTunnel();
Config.Observable config = savedInstanceState.getParcelable(KEY_LOCAL_CONFIG);
String originalName = savedInstanceState.getString(KEY_ORIGINAL_NAME);
if (tunnel != null && !tunnel.getName().equals(originalName))
onSelectedTunnelChanged(null, tunnel);
else
binding.setConfig(config);
}
super.onViewStateRestored(savedInstanceState);
}
@Override @Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.config_editor, menu); inflater.inflate(R.menu.config_editor, menu);
@ -159,22 +141,21 @@ public class TunnelEditorFragment extends BaseFragment {
} }
} }
private void onConfigSaved(final Tunnel savedTunnel, final Config config, @Override
final Throwable throwable) { public void onSaveInstanceState(@NonNull final Bundle outState) {
final String message; outState.putParcelable(KEY_LOCAL_CONFIG, binding.getConfig());
if (throwable == null) { outState.putString(KEY_ORIGINAL_NAME, tunnel == null ? null : tunnel.getName());
message = getString(R.string.config_save_success, savedTunnel.getName()); super.onSaveInstanceState(outState);
Log.d(TAG, message);
onFinished();
} else {
final String error = ExceptionLoggers.unwrap(throwable).getMessage();
message = getString(R.string.config_save_error, savedTunnel.getName(), error);
Log.e(TAG, message, throwable);
if (binding != null) {
final CoordinatorLayout container = binding.mainContainer;
Snackbar.make(container, message, Snackbar.LENGTH_LONG).show();
}
} }
@Override
public void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) {
tunnel = newTunnel;
if (binding == null)
return;
binding.setConfig(new Config.Observable(null, null));
if (tunnel != null)
tunnel.getConfigAsync().thenAccept(a -> onConfigLoaded(tunnel.getName(), a));
} }
private void onTunnelCreated(final Tunnel newTunnel, final Throwable throwable) { private void onTunnelCreated(final Tunnel newTunnel, final Throwable throwable) {
@ -214,4 +195,21 @@ public class TunnelEditorFragment extends BaseFragment {
} }
} }
} }
@Override
public void onViewStateRestored(final Bundle savedInstanceState) {
if (savedInstanceState == null) {
onSelectedTunnelChanged(null, getSelectedTunnel());
} else {
tunnel = getSelectedTunnel();
Config.Observable config = savedInstanceState.getParcelable(KEY_LOCAL_CONFIG);
String originalName = savedInstanceState.getString(KEY_ORIGINAL_NAME);
if (tunnel != null && !tunnel.getName().equals(originalName))
onSelectedTunnelChanged(null, tunnel);
else
binding.setConfig(config);
}
super.onViewStateRestored(savedInstanceState);
}
} }

View File

@ -27,7 +27,6 @@ import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener; import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.TextView;
import com.wireguard.android.Application; import com.wireguard.android.Application;
import com.wireguard.android.Application.ApplicationComponent; import com.wireguard.android.Application.ApplicationComponent;
@ -44,7 +43,6 @@ import java.io.BufferedReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@ -70,6 +68,14 @@ public class TunnelListFragment extends BaseFragment {
private TunnelListFragmentBinding binding; private TunnelListFragmentBinding binding;
private TunnelManager tunnelManager; private TunnelManager tunnelManager;
public boolean collapseActionMenu() {
if (binding.createMenu.isExpanded()) {
binding.createMenu.collapse();
return true;
}
return false;
}
private void importTunnel(final Uri uri) { private void importTunnel(final Uri uri) {
final Activity activity = getActivity(); final Activity activity = getActivity();
if (activity == null) if (activity == null)
@ -199,14 +205,6 @@ public class TunnelListFragment extends BaseFragment {
super.onDestroyView(); super.onDestroyView();
} }
public boolean collapseActionMenu() {
if (binding.createMenu.isExpanded()) {
binding.createMenu.collapse();
return true;
}
return false;
}
public void onRequestCreateConfig(@SuppressWarnings("unused") final View view) { public void onRequestCreateConfig(@SuppressWarnings("unused") final View view) {
startActivity(new Intent(getActivity(), TunnelCreatorActivity.class)); startActivity(new Intent(getActivity(), TunnelCreatorActivity.class));
if (binding != null) if (binding != null)

View File

@ -128,15 +128,6 @@ public final class TunnelManager extends BaseObservable {
return tunnels; return tunnels;
} }
public void refreshTunnelStates() {
asyncWorker.supplyAsync(backend::enumerate)
.thenAccept(running -> {
for (final Tunnel tunnel : tunnels)
tunnel.onStateChanged(running.contains(tunnel.getName()) ? State.UP : State.DOWN);
})
.whenComplete(ExceptionLoggers.E);
}
public void onCreate() { public void onCreate() {
asyncWorker.supplyAsync(configStore::enumerate) asyncWorker.supplyAsync(configStore::enumerate)
.thenAcceptBoth(asyncWorker.supplyAsync(backend::enumerate), this::onTunnelsLoaded) .thenAcceptBoth(asyncWorker.supplyAsync(backend::enumerate), this::onTunnelsLoaded)
@ -151,6 +142,15 @@ public final class TunnelManager extends BaseObservable {
setLastUsedTunnel(tunnels.get(lastUsedName)); setLastUsedTunnel(tunnels.get(lastUsedName));
} }
public void refreshTunnelStates() {
asyncWorker.supplyAsync(backend::enumerate)
.thenAccept(running -> {
for (final Tunnel tunnel : tunnels)
tunnel.onStateChanged(running.contains(tunnel.getName()) ? State.UP : State.DOWN);
})
.whenComplete(ExceptionLoggers.E);
}
public CompletionStage<Void> restoreState() { public CompletionStage<Void> restoreState() {
if (!preferences.getBoolean(KEY_RESTORE_ON_BOOT, false)) if (!preferences.getBoolean(KEY_RESTORE_ON_BOOT, false))
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);

View File

@ -6,9 +6,9 @@ import android.content.pm.PackageManager;
import android.os.Environment; import android.os.Environment;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
import android.view.ContextThemeWrapper;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.ContextThemeWrapper;
import com.wireguard.android.Application; import com.wireguard.android.Application;
import com.wireguard.android.Application.ApplicationComponent; import com.wireguard.android.Application.ApplicationComponent;
@ -25,7 +25,6 @@ import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
@ -50,19 +49,6 @@ public class ZipExporterPreference extends Preference {
tunnelManager = applicationComponent.getTunnelManager(); tunnelManager = applicationComponent.getTunnelManager();
} }
@Override
public CharSequence getSummary() {
if (exportedFilePath == null)
return getContext().getString(R.string.export_summary);
else
return getContext().getString(R.string.export_success, exportedFilePath);
}
@Override
public CharSequence getTitle() {
return getContext().getString(R.string.zip_exporter_title);
}
private void exportZip() { private void exportZip() {
List<Tunnel> tunnels = new ArrayList<>(tunnelManager.getTunnels()); List<Tunnel> tunnels = new ArrayList<>(tunnelManager.getTunnels());
List<CompletableFuture<Config>> futureConfigs = new ArrayList<>(tunnels.size()); List<CompletableFuture<Config>> futureConfigs = new ArrayList<>(tunnels.size());
@ -123,6 +109,19 @@ public class ZipExporterPreference extends Preference {
return null; return null;
} }
@Override
public CharSequence getSummary() {
if (exportedFilePath == null)
return getContext().getString(R.string.export_summary);
else
return getContext().getString(R.string.export_success, exportedFilePath);
}
@Override
public CharSequence getTitle() {
return getContext().getString(R.string.zip_exporter_title);
}
@Override @Override
protected void onClick() { protected void onClick() {
getPrefActivity(this).ensurePermissions( getPrefActivity(this).ensurePermissions(

View File

@ -30,6 +30,7 @@ enum Attribute {
private static final Map<String, Attribute> KEY_MAP; private static final Map<String, Attribute> KEY_MAP;
private static final Pattern SEPARATOR_PATTERN = Pattern.compile("\\s|="); private static final Pattern SEPARATOR_PATTERN = Pattern.compile("\\s|=");
private static Method parseNumericAddressMethod;
static { static {
KEY_MAP = new HashMap<>(Attribute.values().length); KEY_MAP = new HashMap<>(Attribute.values().length);
@ -38,6 +39,14 @@ enum Attribute {
} }
} }
static {
try {
parseNumericAddressMethod = InetAddress.class.getMethod("parseNumericAddress", new Class[]{String.class});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private final Pattern pattern; private final Pattern pattern;
private final String token; private final String token;
@ -46,27 +55,12 @@ enum Attribute {
this.token = token; this.token = token;
} }
public static Attribute match(final CharSequence line) {
return KEY_MAP.get(SEPARATOR_PATTERN.split(line)[0]);
}
public static <T> String listToString(final List<T> list) { public static <T> String listToString(final List<T> list) {
return TextUtils.join(", ", list); return TextUtils.join(", ", list);
} }
public static String[] stringToList(final String string) { public static Attribute match(final CharSequence line) {
if (string == null) return KEY_MAP.get(SEPARATOR_PATTERN.split(line)[0]);
return new String[0];
return string.trim().split("\\s*,\\s*");
}
private static Method parseNumericAddressMethod;
static {
try {
parseNumericAddressMethod = InetAddress.class.getMethod("parseNumericAddress", new Class[]{String.class});
} catch (Exception e) {
throw new RuntimeException(e);
}
} }
public static InetAddress parseIPString(final String address) { public static InetAddress parseIPString(final String address) {
@ -84,6 +78,12 @@ enum Attribute {
} }
} }
public static String[] stringToList(final String string) {
if (string == null)
return new String[0];
return string.trim().split("\\s*,\\s*");
}
public String composeWith(final Object value) { public String composeWith(final Object value) {
return String.format("%s = %s%n", token, value); return String.format("%s = %s%n", token, value);
} }

View File

@ -1,7 +1,5 @@
package com.wireguard.config; package com.wireguard.config;
import com.android.databinding.library.baseAdapters.BR;
import android.databinding.BaseObservable; import android.databinding.BaseObservable;
import android.databinding.Bindable; import android.databinding.Bindable;
import android.databinding.ObservableArrayList; import android.databinding.ObservableArrayList;
@ -9,6 +7,8 @@ import android.databinding.ObservableList;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.android.databinding.library.baseAdapters.BR;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -22,90 +22,6 @@ import java.util.List;
*/ */
public class Config { public class Config {
public static class Observable extends BaseObservable implements Parcelable {
private String name;
private Interface.Observable observableInterface;
private ObservableList<Peer.Observable> observablePeers;
public Observable(Config parent, String name) {
this.name = name;
loadData(parent);
}
public void loadData(Config parent) {
this.observableInterface = new Interface.Observable(parent == null ? null : parent.interfaceSection);
this.observablePeers = new ObservableArrayList<>();
if (parent != null) {
for (Peer peer : parent.getPeers())
this.observablePeers.add(new Peer.Observable(peer));
}
}
public void commitData(Config parent) {
this.observableInterface.commitData(parent.interfaceSection);
List<Peer> newPeers = new ArrayList<>(this.observablePeers.size());
for (Peer.Observable observablePeer : this.observablePeers) {
Peer peer = new Peer();
observablePeer.commitData(peer);
newPeers.add(peer);
}
parent.peers = newPeers;
notifyChange();
}
@Bindable
public String getName() {
return name == null ? "" : name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public Interface.Observable getInterfaceSection() {
return observableInterface;
}
@Bindable
public ObservableList<Peer.Observable> getPeers() {
return observablePeers;
}
public static final Creator<Observable> CREATOR = new Creator<Observable>() {
@Override
public Observable createFromParcel(final Parcel in) {
return new Observable(in);
}
@Override
public Observable[] newArray(final int size) {
return new Observable[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeString(name);
dest.writeParcelable(observableInterface, flags);
dest.writeTypedList(observablePeers);
}
private Observable(final Parcel in) {
name = in.readString();
observableInterface = in.readParcelable(Interface.Observable.class.getClassLoader());
observablePeers = new ObservableArrayList<>();
in.readTypedList(observablePeers, Peer.Observable.CREATOR);
}
}
private final Interface interfaceSection = new Interface(); private final Interface interfaceSection = new Interface();
private List<Peer> peers = new ArrayList<>(); private List<Peer> peers = new ArrayList<>();
@ -157,4 +73,86 @@ public class Config {
sb.append('\n').append(peer); sb.append('\n').append(peer);
return sb.toString(); return sb.toString();
} }
public static class Observable extends BaseObservable implements Parcelable {
public static final Creator<Observable> CREATOR = new Creator<Observable>() {
@Override
public Observable createFromParcel(final Parcel in) {
return new Observable(in);
}
@Override
public Observable[] newArray(final int size) {
return new Observable[size];
}
};
private String name;
private Interface.Observable observableInterface;
private ObservableList<Peer.Observable> observablePeers;
public Observable(Config parent, String name) {
this.name = name;
loadData(parent);
}
private Observable(final Parcel in) {
name = in.readString();
observableInterface = in.readParcelable(Interface.Observable.class.getClassLoader());
observablePeers = new ObservableArrayList<>();
in.readTypedList(observablePeers, Peer.Observable.CREATOR);
}
public void commitData(Config parent) {
this.observableInterface.commitData(parent.interfaceSection);
List<Peer> newPeers = new ArrayList<>(this.observablePeers.size());
for (Peer.Observable observablePeer : this.observablePeers) {
Peer peer = new Peer();
observablePeer.commitData(peer);
newPeers.add(peer);
}
parent.peers = newPeers;
notifyChange();
}
@Override
public int describeContents() {
return 0;
}
@Bindable
public Interface.Observable getInterfaceSection() {
return observableInterface;
}
@Bindable
public String getName() {
return name == null ? "" : name;
}
@Bindable
public ObservableList<Peer.Observable> getPeers() {
return observablePeers;
}
public void loadData(Config parent) {
this.observableInterface = new Interface.Observable(parent == null ? null : parent.interfaceSection);
this.observablePeers = new ObservableArrayList<>();
if (parent != null) {
for (Peer peer : parent.getPeers())
this.observablePeers.add(new Peer.Observable(peer));
}
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeString(name);
dest.writeParcelable(observableInterface, flags);
dest.writeTypedList(observablePeers);
}
}
} }

View File

@ -17,158 +17,34 @@ import java.util.List;
*/ */
public class Interface { public class Interface {
public static class Observable extends BaseObservable implements Parcelable {
private String addresses;
private String dnses;
private String publicKey;
private String privateKey;
private String listenPort;
private String mtu;
public Observable(Interface parent) {
if (parent != null)
loadData(parent);
}
public void loadData(Interface parent) {
this.addresses = parent.getAddressString();
this.dnses = parent.getDnsString();
this.publicKey = parent.getPublicKey();
this.privateKey = parent.getPrivateKey();
this.listenPort = parent.getListenPortString();
this.mtu = parent.getMtuString();
}
public void commitData(Interface parent) {
parent.setAddressString(this.addresses);
parent.setDnsString(this.dnses);
parent.setPrivateKey(this.privateKey);
parent.setListenPortString(this.listenPort);
parent.setMtuString(this.mtu);
loadData(parent);
notifyChange();
}
@Bindable
public String getAddresses() {
return addresses;
}
public void setAddresses(String addresses) {
this.addresses = addresses;
notifyPropertyChanged(BR.addresses);
}
@Bindable
public String getDnses() {
return dnses;
}
public void setDnses(String dnses) {
this.dnses = dnses;
notifyPropertyChanged(BR.dnses);
}
@Bindable
public String getPublicKey() {
return publicKey;
}
@Bindable
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
try {
this.publicKey = new Keypair(privateKey).getPublicKey();
} catch (IllegalArgumentException ignored) {
this.publicKey = "";
}
notifyPropertyChanged(BR.privateKey);
notifyPropertyChanged(BR.publicKey);
}
public void generateKeypair() {
Keypair keypair = new Keypair();
privateKey = keypair.getPrivateKey();
publicKey = keypair.getPublicKey();
notifyPropertyChanged(BR.privateKey);
notifyPropertyChanged(BR.publicKey);
}
@Bindable
public String getListenPort() {
return listenPort;
}
public void setListenPort(String listenPort) {
this.listenPort = listenPort;
notifyPropertyChanged(BR.listenPort);
}
@Bindable
public String getMtu() {
return mtu;
}
public void setMtu(String mtu) {
this.mtu = mtu;
notifyPropertyChanged(BR.mtu);
}
public static final Creator<Observable> CREATOR = new Creator<Observable>() {
@Override
public Observable createFromParcel(final Parcel in) {
return new Observable(in);
}
@Override
public Observable[] newArray(final int size) {
return new Observable[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeString(addresses);
dest.writeString(dnses);
dest.writeString(publicKey);
dest.writeString(privateKey);
dest.writeString(listenPort);
dest.writeString(mtu);
}
private Observable(final Parcel in) {
addresses = in.readString();
dnses = in.readString();
publicKey = in.readString();
privateKey = in.readString();
listenPort = in.readString();
mtu = in.readString();
}
}
private List<IPCidr> addressList; private List<IPCidr> addressList;
private List<InetAddress> dnsList; private List<InetAddress> dnsList;
private Keypair keypair; private Keypair keypair;
private int listenPort; private int listenPort;
private int mtu; private int mtu;
public Interface() { public Interface() {
addressList = new LinkedList<>(); addressList = new LinkedList<>();
dnsList = new LinkedList<>(); dnsList = new LinkedList<>();
} }
private void addAddresses(String[] addresses) {
if (addresses != null && addresses.length > 0) {
for (final String addr : addresses) {
if (addr.isEmpty())
throw new IllegalArgumentException("Address is empty");
this.addressList.add(new IPCidr(addr));
}
}
}
private void addDnses(String[] dnses) {
if (dnses != null && dnses.length > 0) {
for (final String dns : dnses) {
this.dnsList.add(Attribute.parseIPString(dns));
}
}
}
private String getAddressString() { private String getAddressString() {
if (addressList.isEmpty()) if (addressList.isEmpty())
return null; return null;
@ -179,6 +55,12 @@ public class Interface {
return addressList.toArray(new IPCidr[addressList.size()]); return addressList.toArray(new IPCidr[addressList.size()]);
} }
private String getDnsString() {
if (dnsList.isEmpty())
return null;
return Attribute.listToString(getDnsStrings());
}
private List<String> getDnsStrings() { private List<String> getDnsStrings() {
List<String> strings = new LinkedList<>(); List<String> strings = new LinkedList<>();
for (final InetAddress addr : dnsList) for (final InetAddress addr : dnsList)
@ -186,12 +68,6 @@ public class Interface {
return strings; return strings;
} }
private String getDnsString() {
if (dnsList.isEmpty())
return null;
return Attribute.listToString(getDnsStrings());
}
public InetAddress[] getDnses() { public InetAddress[] getDnses() {
return dnsList.toArray(new InetAddress[dnsList.size()]); return dnsList.toArray(new InetAddress[dnsList.size()]);
} }
@ -244,29 +120,11 @@ public class Interface {
throw new IllegalArgumentException(line); throw new IllegalArgumentException(line);
} }
private void addAddresses(String[] addresses) {
if (addresses != null && addresses.length > 0) {
for (final String addr : addresses) {
if (addr.isEmpty())
throw new IllegalArgumentException("Address is empty");
this.addressList.add(new IPCidr(addr));
}
}
}
private void setAddressString(final String addressString) { private void setAddressString(final String addressString) {
this.addressList.clear(); this.addressList.clear();
addAddresses(Attribute.stringToList(addressString)); addAddresses(Attribute.stringToList(addressString));
} }
private void addDnses(String[] dnses) {
if (dnses != null && dnses.length > 0) {
for (final String dns : dnses) {
this.dnsList.add(Attribute.parseIPString(dns));
}
}
}
private void setDnsString(final String dnsString) { private void setDnsString(final String dnsString) {
this.dnsList.clear(); this.dnsList.clear();
addDnses(Attribute.stringToList(dnsString)); addDnses(Attribute.stringToList(dnsString));
@ -318,4 +176,143 @@ public class Interface {
sb.append(Attribute.PRIVATE_KEY.composeWith(keypair.getPrivateKey())); sb.append(Attribute.PRIVATE_KEY.composeWith(keypair.getPrivateKey()));
return sb.toString(); return sb.toString();
} }
public static class Observable extends BaseObservable implements Parcelable {
public static final Creator<Observable> CREATOR = new Creator<Observable>() {
@Override
public Observable createFromParcel(final Parcel in) {
return new Observable(in);
}
@Override
public Observable[] newArray(final int size) {
return new Observable[size];
}
};
private String addresses;
private String dnses;
private String listenPort;
private String mtu;
private String privateKey;
private String publicKey;
public Observable(Interface parent) {
if (parent != null)
loadData(parent);
}
private Observable(final Parcel in) {
addresses = in.readString();
dnses = in.readString();
publicKey = in.readString();
privateKey = in.readString();
listenPort = in.readString();
mtu = in.readString();
}
public void commitData(Interface parent) {
parent.setAddressString(this.addresses);
parent.setDnsString(this.dnses);
parent.setPrivateKey(this.privateKey);
parent.setListenPortString(this.listenPort);
parent.setMtuString(this.mtu);
loadData(parent);
notifyChange();
}
@Override
public int describeContents() {
return 0;
}
public void generateKeypair() {
Keypair keypair = new Keypair();
privateKey = keypair.getPrivateKey();
publicKey = keypair.getPublicKey();
notifyPropertyChanged(BR.privateKey);
notifyPropertyChanged(BR.publicKey);
}
@Bindable
public String getAddresses() {
return addresses;
}
@Bindable
public String getDnses() {
return dnses;
}
@Bindable
public String getListenPort() {
return listenPort;
}
@Bindable
public String getMtu() {
return mtu;
}
@Bindable
public String getPrivateKey() {
return privateKey;
}
@Bindable
public String getPublicKey() {
return publicKey;
}
public void loadData(Interface parent) {
this.addresses = parent.getAddressString();
this.dnses = parent.getDnsString();
this.publicKey = parent.getPublicKey();
this.privateKey = parent.getPrivateKey();
this.listenPort = parent.getListenPortString();
this.mtu = parent.getMtuString();
}
public void setAddresses(String addresses) {
this.addresses = addresses;
notifyPropertyChanged(BR.addresses);
}
public void setDnses(String dnses) {
this.dnses = dnses;
notifyPropertyChanged(BR.dnses);
}
public void setListenPort(String listenPort) {
this.listenPort = listenPort;
notifyPropertyChanged(BR.listenPort);
}
public void setMtu(String mtu) {
this.mtu = mtu;
notifyPropertyChanged(BR.mtu);
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
try {
this.publicKey = new Keypair(privateKey).getPublicKey();
} catch (IllegalArgumentException ignored) {
this.publicKey = "";
}
notifyPropertyChanged(BR.privateKey);
notifyPropertyChanged(BR.publicKey);
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeString(addresses);
dest.writeString(dnses);
dest.writeString(publicKey);
dest.writeString(privateKey);
dest.writeString(listenPort);
dest.writeString(mtu);
}
}
} }

View File

@ -22,136 +22,26 @@ import java.util.Locale;
*/ */
public class Peer { public class Peer {
public static class Observable extends BaseObservable implements Parcelable {
private String allowedIPs;
private String endpoint;
private String persistentKeepalive;
private String preSharedKey;
private String publicKey;
public Observable(Peer parent) {
loadData(parent);
}
public static Observable newInstance() {
return new Observable(new Peer());
}
public void loadData(Peer parent) {
this.allowedIPs = parent.getAllowedIPsString();
this.endpoint = parent.getEndpointString();
this.persistentKeepalive = parent.getPersistentKeepaliveString();
this.preSharedKey = parent.getPreSharedKey();
this.publicKey = parent.getPublicKey();
}
public void commitData(Peer parent) {
parent.setAllowedIPsString(this.allowedIPs);
parent.setEndpointString(this.endpoint);
parent.setPersistentKeepaliveString(this.persistentKeepalive);
parent.setPreSharedKey(this.preSharedKey);
parent.setPublicKey(this.publicKey);
if (parent.getPublicKey() == null)
throw new IllegalArgumentException("Peer public key may not be empty");
loadData(parent);
notifyChange();
}
@Bindable
public String getAllowedIPs() {
return allowedIPs;
}
public void setAllowedIPs(String allowedIPs) {
this.allowedIPs = allowedIPs;
notifyPropertyChanged(BR.allowedIPs);
}
@Bindable
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
notifyPropertyChanged(BR.endpoint);
}
@Bindable
public String getPersistentKeepalive() {
return persistentKeepalive;
}
public void setPersistentKeepalive(String persistentKeepalive) {
this.persistentKeepalive = persistentKeepalive;
notifyPropertyChanged(BR.persistentKeepalive);
}
@Bindable
public String getPreSharedKey() {
return preSharedKey;
}
public void setPreSharedKey(String preSharedKey) {
this.preSharedKey = preSharedKey;
notifyPropertyChanged(BR.preSharedKey);
}
@Bindable
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
notifyPropertyChanged(BR.publicKey);
}
public static final Creator<Observable> CREATOR = new Creator<Observable>() {
@Override
public Observable createFromParcel(final Parcel in) {
return new Observable(in);
}
@Override
public Observable[] newArray(final int size) {
return new Observable[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeString(allowedIPs);
dest.writeString(endpoint);
dest.writeString(persistentKeepalive);
dest.writeString(preSharedKey);
dest.writeString(publicKey);
}
private Observable(final Parcel in) {
allowedIPs = in.readString();
endpoint = in.readString();
persistentKeepalive = in.readString();
preSharedKey = in.readString();
publicKey = in.readString();
}
}
private List<IPCidr> allowedIPsList; private List<IPCidr> allowedIPsList;
private InetSocketAddress endpoint; private InetSocketAddress endpoint;
private int persistentKeepalive; private int persistentKeepalive;
private String preSharedKey; private String preSharedKey;
private String publicKey; private String publicKey;
public Peer() { public Peer() {
allowedIPsList = new LinkedList<>(); allowedIPsList = new LinkedList<>();
} }
private void addAllowedIPs(String[] allowedIPs) {
if (allowedIPs != null && allowedIPs.length > 0) {
for (final String allowedIP : allowedIPs) {
this.allowedIPsList.add(new IPCidr(allowedIP));
}
}
}
public IPCidr[] getAllowedIPs() {
return allowedIPsList.toArray(new IPCidr[allowedIPsList.size()]);
}
private String getAllowedIPsString() { private String getAllowedIPsString() {
if (allowedIPsList.isEmpty()) if (allowedIPsList.isEmpty())
@ -159,10 +49,6 @@ public class Peer {
return Attribute.listToString(allowedIPsList); return Attribute.listToString(allowedIPsList);
} }
public IPCidr[] getAllowedIPs() {
return allowedIPsList.toArray(new IPCidr[allowedIPsList.size()]);
}
public InetSocketAddress getEndpoint() { public InetSocketAddress getEndpoint() {
return endpoint; return endpoint;
} }
@ -173,24 +59,6 @@ public class Peer {
return String.format(Locale.getDefault(), "%s:%d", endpoint.getHostString(), endpoint.getPort()); return String.format(Locale.getDefault(), "%s:%d", endpoint.getHostString(), endpoint.getPort());
} }
public String getResolvedEndpointString() throws UnknownHostException {
if (endpoint == null)
throw new UnknownHostException("{empty}");
if (endpoint.isUnresolved())
endpoint = new InetSocketAddress(endpoint.getHostString(), endpoint.getPort());
if (endpoint.isUnresolved())
throw new UnknownHostException(endpoint.getHostString());
if (endpoint.getAddress() instanceof Inet6Address)
return String.format(Locale.getDefault(),
"[%s]:%d",
endpoint.getAddress().getHostAddress(),
endpoint.getPort());
return String.format(Locale.getDefault(),
"%s:%d",
endpoint.getAddress().getHostAddress(),
endpoint.getPort());
}
public int getPersistentKeepalive() { public int getPersistentKeepalive() {
return persistentKeepalive; return persistentKeepalive;
} }
@ -209,6 +77,24 @@ public class Peer {
return publicKey; return publicKey;
} }
public String getResolvedEndpointString() throws UnknownHostException {
if (endpoint == null)
throw new UnknownHostException("{empty}");
if (endpoint.isUnresolved())
endpoint = new InetSocketAddress(endpoint.getHostString(), endpoint.getPort());
if (endpoint.isUnresolved())
throw new UnknownHostException(endpoint.getHostString());
if (endpoint.getAddress() instanceof Inet6Address)
return String.format(Locale.getDefault(),
"[%s]:%d",
endpoint.getAddress().getHostAddress(),
endpoint.getPort());
return String.format(Locale.getDefault(),
"%s:%d",
endpoint.getAddress().getHostAddress(),
endpoint.getPort());
}
public void parse(final String line) { public void parse(final String line) {
final Attribute key = Attribute.match(line); final Attribute key = Attribute.match(line);
if (key == Attribute.ALLOWED_IPS) if (key == Attribute.ALLOWED_IPS)
@ -225,14 +111,6 @@ public class Peer {
throw new IllegalArgumentException(line); throw new IllegalArgumentException(line);
} }
private void addAllowedIPs(String[] allowedIPs) {
if (allowedIPs != null && allowedIPs.length > 0) {
for (final String allowedIP : allowedIPs) {
this.allowedIPsList.add(new IPCidr(allowedIP));
}
}
}
private void setAllowedIPsString(final String allowedIPsString) { private void setAllowedIPsString(final String allowedIPsString) {
this.allowedIPsList.clear(); this.allowedIPsList.clear();
addAllowedIPs(Attribute.stringToList(allowedIPsString)); addAllowedIPs(Attribute.stringToList(allowedIPsString));
@ -301,4 +179,123 @@ public class Peer {
sb.append(Attribute.PUBLIC_KEY.composeWith(publicKey)); sb.append(Attribute.PUBLIC_KEY.composeWith(publicKey));
return sb.toString(); return sb.toString();
} }
public static class Observable extends BaseObservable implements Parcelable {
public static final Creator<Observable> CREATOR = new Creator<Observable>() {
@Override
public Observable createFromParcel(final Parcel in) {
return new Observable(in);
}
@Override
public Observable[] newArray(final int size) {
return new Observable[size];
}
};
private String allowedIPs;
private String endpoint;
private String persistentKeepalive;
private String preSharedKey;
private String publicKey;
public Observable(Peer parent) {
loadData(parent);
}
private Observable(final Parcel in) {
allowedIPs = in.readString();
endpoint = in.readString();
persistentKeepalive = in.readString();
preSharedKey = in.readString();
publicKey = in.readString();
}
public static Observable newInstance() {
return new Observable(new Peer());
}
public void commitData(Peer parent) {
parent.setAllowedIPsString(this.allowedIPs);
parent.setEndpointString(this.endpoint);
parent.setPersistentKeepaliveString(this.persistentKeepalive);
parent.setPreSharedKey(this.preSharedKey);
parent.setPublicKey(this.publicKey);
if (parent.getPublicKey() == null)
throw new IllegalArgumentException("Peer public key may not be empty");
loadData(parent);
notifyChange();
}
@Override
public int describeContents() {
return 0;
}
@Bindable
public String getAllowedIPs() {
return allowedIPs;
}
@Bindable
public String getEndpoint() {
return endpoint;
}
@Bindable
public String getPersistentKeepalive() {
return persistentKeepalive;
}
@Bindable
public String getPreSharedKey() {
return preSharedKey;
}
@Bindable
public String getPublicKey() {
return publicKey;
}
public void loadData(Peer parent) {
this.allowedIPs = parent.getAllowedIPsString();
this.endpoint = parent.getEndpointString();
this.persistentKeepalive = parent.getPersistentKeepaliveString();
this.preSharedKey = parent.getPreSharedKey();
this.publicKey = parent.getPublicKey();
}
public void setAllowedIPs(String allowedIPs) {
this.allowedIPs = allowedIPs;
notifyPropertyChanged(BR.allowedIPs);
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
notifyPropertyChanged(BR.endpoint);
}
public void setPersistentKeepalive(String persistentKeepalive) {
this.persistentKeepalive = persistentKeepalive;
notifyPropertyChanged(BR.persistentKeepalive);
}
public void setPreSharedKey(String preSharedKey) {
this.preSharedKey = preSharedKey;
notifyPropertyChanged(BR.preSharedKey);
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
notifyPropertyChanged(BR.publicKey);
}
@Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeString(allowedIPs);
dest.writeString(endpoint);
dest.writeString(persistentKeepalive);
dest.writeString(preSharedKey);
dest.writeString(publicKey);
}
}
} }

View File

@ -26,16 +26,16 @@ import java.util.Arrays;
/** /**
* Implementation of the Curve25519 elliptic curve algorithm. * Implementation of the Curve25519 elliptic curve algorithm.
* * <p>
* This implementation is based on that from arduinolibs: * This implementation is based on that from arduinolibs:
* https://github.com/rweather/arduinolibs * https://github.com/rweather/arduinolibs
* * <p>
* This implementation is copied verbatim from noise-java: * This implementation is copied verbatim from noise-java:
* https://github.com/rweather/noise-java * https://github.com/rweather/noise-java
* * <p>
* Differences in this version are due to using 26-bit limbs for the * 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. * representation instead of the 8/16/32-bit limbs in the original.
* * <p>
* References: http://cr.yp.to/ecdh.html, RFC 7748 * References: http://cr.yp.to/ecdh.html, RFC 7748
*/ */
@SuppressWarnings("ALL") @SuppressWarnings("ALL")
@ -44,28 +44,27 @@ public final class Curve25519 {
// Numbers modulo 2^255 - 19 are broken up into ten 26-bit words. // 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_255BIT = 10;
private static final int NUM_LIMBS_510BIT = 20; private static final int NUM_LIMBS_510BIT = 20;
private int[] A;
private int[] AA;
private int[] B;
private int[] BB;
private int[] C;
private int[] CB;
private int[] D;
private int[] DA;
private int[] E;
private long[] t1;
private int[] t2;
private int[] x_1; private int[] x_1;
private int[] x_2; private int[] x_2;
private int[] x_3; private int[] x_3;
private int[] z_2; private int[] z_2;
private int[] z_3; private int[] z_3;
private int[] A;
private int[] B;
private int[] C;
private int[] D;
private int[] E;
private int[] AA;
private int[] BB;
private int[] DA;
private int[] CB;
private long[] t1;
private int[] t2;
/** /**
* Constructs the temporary state holder for Curve25519 evaluation. * Constructs the temporary state holder for Curve25519 evaluation.
*/ */
private Curve25519() private Curve25519() {
{
// Allocate memory for all of the temporary variables we will need. // Allocate memory for all of the temporary variables we will need.
x_1 = new int[NUM_LIMBS_255BIT]; x_1 = new int[NUM_LIMBS_255BIT];
x_2 = new int[NUM_LIMBS_255BIT]; x_2 = new int[NUM_LIMBS_255BIT];
@ -85,6 +84,108 @@ public final class Curve25519 {
t2 = new int[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, int[] x, int[] y) {
int dummy;
select = -select;
for (int index = 0; index < NUM_LIMBS_255BIT; ++index) {
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(byte[] result, int offset, byte[] privateKey, byte[] publicKey) {
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) {
int bit = (index * 8) % 26;
int word = (index * 8) / 26;
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) {
int bit = (index * 8) % 26;
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();
}
}
/**
* 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(int[] result, int[] x, int[] y) {
int index, carry;
carry = x[0] + y[0];
result[0] = carry & 0x03FFFFFF;
for (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. * Destroy all sensitive data in this object.
@ -109,303 +210,12 @@ public final class Curve25519 {
Arrays.fill(t2, 0); Arrays.fill(t2, 0);
} }
/**
* 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(int[] x)
{
int index, carry;
// 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.
carry = 19;
for (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.
int mask = -((t2[NUM_LIMBS_255BIT - 1] >> 21) & 0x01);
int nmask = ~mask;
t2[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
for (index = 0; index < NUM_LIMBS_255BIT; ++index)
x[index] = (x[index] & nmask) | (t2[index] & mask);
}
/**
* 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(int[] result, int[] x, int size)
{
int index, limb, carry;
// 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.
carry = 0;
limb = x[NUM_LIMBS_255BIT - 1] >> 21;
x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
for (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 (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 (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);
}
/**
* 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(int[] result, int[] x, int[] y)
{
int i, j;
// Multiply the two numbers to create the intermediate result.
long v = x[0];
for (i = 0; i < NUM_LIMBS_255BIT; ++i) {
t1[i] = v * y[i];
}
for (i = 1; i < NUM_LIMBS_255BIT; ++i) {
v = x[i];
for (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 (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);
}
/**
* Squares a number modulo 2^255 - 19.
*
* @param result The result.
* @param x The number to square.
*/
private void square(int[] result, int[] x)
{
mul(result, x, x);
}
/**
* 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(int[] result, int[] x)
{
long a24 = 121665;
long carry = 0;
int index;
for (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);
}
/**
* 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(int[] result, int[] x, int[] y)
{
int index, carry;
carry = x[0] + y[0];
result[0] = carry & 0x03FFFFFF;
for (index = 1; index < NUM_LIMBS_255BIT; ++index) {
carry = (carry >> 26) + x[index] + y[index];
result[index] = carry & 0x03FFFFFF;
}
reduceQuick(result);
}
/**
* 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 void sub(int[] result, int[] x, int[] y)
{
int index, 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;
}
/**
* 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, int[] x, int[] y)
{
int dummy;
select = -select;
for (int index = 0; index < NUM_LIMBS_255BIT; ++index) {
dummy = select & (x[index] ^ y[index]);
x[index] ^= dummy;
y[index] ^= dummy;
}
}
/**
* 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(int[] result, int[] x)
{
int i, j;
// 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 (j = 0; j < 9; ++j)
square(A, A);
mul(result, A, x);
for (i = 0; i < 23; ++i) {
for (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 (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(int[] result, 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);
}
/** /**
* Evaluates the curve for every bit in a secret key. * Evaluates the curve for every bit in a secret key.
* *
* @param s The 32-byte secret key. * @param s The 32-byte secret key.
*/ */
private void evalCurve(byte[] s) private void evalCurve(byte[] s) {
{
int sposn = 31; int sposn = 31;
int sbit = 6; int sbit = 6;
int svalue = s[sposn] | 0x40; int svalue = s[sposn] | 0x40;
@ -465,71 +275,247 @@ public final class Curve25519 {
} }
/** /**
* Evaluates the Curve25519 curve. * Multiplies two numbers modulo 2^255 - 19.
* *
* @param result Buffer to place the result of the evaluation into. * @param result The result.
* @param offset Offset into the result buffer. * @param x The first number to multiply.
* @param privateKey The private key to use in the evaluation. * @param y The second number to multiply.
* @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(byte[] result, int offset, byte[] privateKey, byte[] publicKey) private void mul(int[] result, int[] x, int[] y) {
{ int i, j;
Curve25519 state = new Curve25519();
try { // Multiply the two numbers to create the intermediate result.
// Unpack the public key value. If null, use 9 as the base point. long v = x[0];
Arrays.fill(state.x_1, 0); for (i = 0; i < NUM_LIMBS_255BIT; ++i) {
if (publicKey != null) { t1[i] = v * y[i];
// Convert the input value from little-endian into 26-bit limbs. }
for (int index = 0; index < 32; ++index) { for (i = 1; i < NUM_LIMBS_255BIT; ++i) {
int bit = (index * 8) % 26; v = x[i];
int word = (index * 8) / 26; for (j = 0; j < (NUM_LIMBS_255BIT - 1); ++j) {
int value = publicKey[index] & 0xFF; t1[i + j] += v * y[j];
if (bit <= (26 - 8)) { }
state.x_1[word] |= value << bit; t1[i + NUM_LIMBS_255BIT - 1] = v * y[NUM_LIMBS_255BIT - 1];
} else { }
state.x_1[word] |= value << bit;
state.x_1[word] &= 0x03FFFFFF; // Propagate carries and convert back into 26-bit words.
state.x_1[word + 1] |= value >> (26 - bit); v = t1[0];
t2[0] = ((int) v) & 0x03FFFFFF;
for (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(int[] result, int[] x) {
long a24 = 121665;
long carry = 0;
int index;
for (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(int[] result, int[] x) {
int i, j;
// 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 (j = 0; j < 9; ++j)
square(A, A);
mul(result, A, x);
for (i = 0; i < 23; ++i) {
for (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 (j = 0; j < 8; ++j) {
square(A, A);
mul(result, result, A);
} }
} }
// 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. * Computes the reciprocal of a number modulo 2^255 - 19.
// This eliminates values between 2^255 - 19 and 2^256 - 1. *
state.reduceQuick(state.x_1); * @param result The result. Must not overlap with x.
state.reduceQuick(state.x_1); * @param x The argument.
} else { */
state.x_1[0] = 9; private void recip(int[] result, 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);
} }
// Initialize the other temporary variables. /**
Arrays.fill(state.x_2, 0); // x_2 = 1 * Reduce a number modulo 2^255 - 19.
state.x_2[0] = 1; *
Arrays.fill(state.z_2, 0); // z_2 = 0 * @param result The result.
System.arraycopy(state.x_1, 0, state.x_3, 0, state.x_1.length); // x_3 = x_1 * @param x The value to be reduced. This array will be
Arrays.fill(state.z_3, 0); // z_3 = 1 * modified during the reduction.
state.z_3[0] = 1; * @param size The number of limbs in the high order half of x.
*/
private void reduce(int[] result, int[] x, int size) {
int index, limb, carry;
// Evaluate the curve for every bit of the private key. // Calculate (x mod 2^255) + ((x / 2^255) * 19) which will
state.evalCurve(privateKey); // 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.
carry = 0;
limb = x[NUM_LIMBS_255BIT - 1] >> 21;
x[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
for (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 (index = size; index < NUM_LIMBS_255BIT; ++index) {
carry += x[index];
x[index] = carry & 0x03FFFFFF;
carry >>= 26;
}
}
// Compute x_2 * (z_2 ^ (p - 2)) where p = 2^255 - 19. // The "j" value may still be too large due to the final carry-out.
state.recip(state.z_3, state.z_2); // We must repeat the reduction. If we already have the answer,
state.mul(state.x_2, state.x_2, state.z_3); // 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 (index = 0; index < NUM_LIMBS_255BIT; ++index) {
carry += x[index];
result[index] = carry & 0x03FFFFFF;
carry >>= 26;
}
// Convert x_2 into little-endian in the result buffer. // At this point "x" will either be the answer or it will be the
for (int index = 0; index < 32; ++index) { // answer plus (2^255 - 19). Perform a trial subtraction to
int bit = (index * 8) % 26; // complete the reduction process.
int word = (index * 8) / 26; reduceQuick(result);
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();
} }
/**
* 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(int[] x) {
int index, carry;
// 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.
carry = 19;
for (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.
int mask = -((t2[NUM_LIMBS_255BIT - 1] >> 21) & 0x01);
int nmask = ~mask;
t2[NUM_LIMBS_255BIT - 1] &= 0x001FFFFF;
for (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(int[] result, int[] x) {
mul(result, x, x);
}
/**
* 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 void sub(int[] result, int[] x, int[] y) {
int index, 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;
} }
} }

View File

@ -13,10 +13,10 @@ public final class KeyEncoding {
public static final int KEY_LENGTH_HEX = 64; public static final int KEY_LENGTH_HEX = 64;
private static final String KEY_LENGTH_BASE64_EXCEPTION_MESSAGE = private static final String KEY_LENGTH_BASE64_EXCEPTION_MESSAGE =
"WireGuard base64 keys must be 44 characters encoding 32 bytes"; "WireGuard base64 keys must be 44 characters encoding 32 bytes";
private static final String KEY_LENGTH_HEX_EXCEPTION_MESSAGE =
"WireGuard hex keys must be 64 characters encoding 32 bytes";
private static final String KEY_LENGTH_EXCEPTION_MESSAGE = private static final String KEY_LENGTH_EXCEPTION_MESSAGE =
"WireGuard keys must be 32 bytes"; "WireGuard keys must be 32 bytes";
private static final String KEY_LENGTH_HEX_EXCEPTION_MESSAGE =
"WireGuard hex keys must be 64 characters encoding 32 bytes";
private KeyEncoding() { private KeyEncoding() {
// Prevent instantiation. // Prevent instantiation.
@ -82,23 +82,6 @@ public final class KeyEncoding {
return key; return key;
} }
public static String keyToBase64(final byte[] key) {
final char[] output = new char[KEY_LENGTH_BASE64];
if (key.length != KEY_LENGTH)
throw new IllegalArgumentException(KEY_LENGTH_EXCEPTION_MESSAGE);
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[KEY_LENGTH_BASE64 - 1] = '=';
return new String(output);
}
public static byte[] keyFromHex(final String str) { public static byte[] keyFromHex(final String str) {
final char[] input = str.toCharArray(); final char[] input = str.toCharArray();
final byte[] key = new byte[KEY_LENGTH]; final byte[] key = new byte[KEY_LENGTH];
@ -125,6 +108,23 @@ public final class KeyEncoding {
return key; return key;
} }
public static String keyToBase64(final byte[] key) {
final char[] output = new char[KEY_LENGTH_BASE64];
if (key.length != KEY_LENGTH)
throw new IllegalArgumentException(KEY_LENGTH_EXCEPTION_MESSAGE);
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[KEY_LENGTH_BASE64 - 1] = '=';
return new String(output);
}
public static String keyToHex(final byte[] key) { public static String keyToHex(final byte[] key) {
final char[] output = new char[KEY_LENGTH_HEX]; final char[] output = new char[KEY_LENGTH_HEX];
if (key.length != KEY_LENGTH) if (key.length != KEY_LENGTH)

View File

@ -114,11 +114,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/public_key_label" android:layout_below="@+id/public_key_label"
android:contentDescription="@string/public_key_description"
android:ellipsize="end" android:ellipsize="end"
android:focusable="false" android:focusable="false"
android:hint="@string/hint_generated" android:hint="@string/hint_generated"
android:maxLines="1" android:maxLines="1"
android:contentDescription="@string/public_key_description"
android:onClick="@{ClipboardUtils::copyTextView}" android:onClick="@{ClipboardUtils::copyTextView}"
android:text="@{config.interfaceSection.publicKey}" /> android:text="@{config.interfaceSection.publicKey}" />

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/menu_action_save" android:id="@+id/menu_action_save"
android:alphabeticShortcut="s" android:alphabeticShortcut="s"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/menu_settings" android:id="@+id/menu_settings"
android:alphabeticShortcut="s" android:alphabeticShortcut="s"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/menu_action_edit" android:id="@+id/menu_action_edit"
android:alphabeticShortcut="e" android:alphabeticShortcut="e"

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/menu_action_delete" android:id="@+id/menu_action_delete"
android:alphabeticShortcut="d" android:alphabeticShortcut="d"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<plurals name="delete_error"> <plurals name="delete_error">
<item quantity="one">Unable to delete %d tunnel: %s</item> <item quantity="one">Unable to delete %d tunnel: %s</item>
<item quantity="other">Unable to delete %d tunnels: %s</item> <item quantity="other">Unable to delete %d tunnels: %s</item>

View File

@ -2,9 +2,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar" /> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar" />
<style name="SettingsTheme" parent="AppTheme"> <style name="SettingsTheme" parent="AppTheme">
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
</style> </style>
<style name="fab_label" parent="android:TextAppearance.DeviceDefault.Inverse"> <style name="fab_label" parent="android:TextAppearance.DeviceDefault.Inverse">
<item name="android:background">@drawable/fab_label_background</item> <item name="android:background">@drawable/fab_label_background</item>
</style> </style>