diff --git a/app/src/main/java/com/wireguard/config/Interface.java b/app/src/main/java/com/wireguard/config/Interface.java index 9f74bab0..d583f37b 100644 --- a/app/src/main/java/com/wireguard/config/Interface.java +++ b/app/src/main/java/com/wireguard/config/Interface.java @@ -91,7 +91,7 @@ public class Interface extends BaseObservable implements Observable { public void setPrivateKey(String privateKey) { // Avoid exceptions from Keypair while the user is typing. - if (privateKey.length() != KeyEncoding.WG_KEY_LEN_BASE64) + if (privateKey.length() != KeyEncoding.KEY_LENGTH_BASE64) return; keypair = new Keypair(privateKey); notifyPropertyChanged(BR.privateKey); diff --git a/app/src/main/java/com/wireguard/crypto/KeyEncoding.java b/app/src/main/java/com/wireguard/crypto/KeyEncoding.java index c62657cb..070a1a99 100644 --- a/app/src/main/java/com/wireguard/crypto/KeyEncoding.java +++ b/app/src/main/java/com/wireguard/crypto/KeyEncoding.java @@ -1,75 +1,91 @@ -/* Copyright (C) 2015-2017 Jason A. Donenfeld . All Rights Reserved. - * - * This is a specialized constant-time base64 implementation that resists side-channel attacks. - */ +/* Copyright (C) 2015-2017 Jason A. Donenfeld . All Rights Reserved. */ package com.wireguard.crypto; +/** + * This is a specialized constant-time base64 implementation that resists side-channel attacks. + */ + public class KeyEncoding { - public static final int WG_KEY_LEN = 32; - public static final int WG_KEY_LEN_BASE64 = 44; + public static final int KEY_LENGTH = 32; + public static final int KEY_LENGTH_BASE64 = 44; + private static final String KEY_LENGTH_BASE64_EXCEPTION_MESSAGE = + "WireGuard base64 keys must be 44 characters encoding 32 bytes"; - private static void encodeBase64(char dest[], final int dest_offset, final byte src[], final int src_offset) { - final byte input[] = { (byte)((src[0 + src_offset] >>> 2) & 63), - (byte)(((src[0 + src_offset] << 4) | ((src[1 + src_offset] & 0xff) >>> 4)) & 63), - (byte)(((src[1 + src_offset] << 2) | ((src[2 + src_offset] & 0xff) >>> 6)) & 63), - (byte)(src[2 + src_offset] & 63) }; - for (int i = 0; i < 4; ++i) - dest[i + dest_offset] = (char)(input[i] + 'A' - + (((25 - input[i]) >>> 8) & 6) - - (((51 - input[i]) >>> 8) & 75) - - (((61 - input[i]) >>> 8) & 15) - + (((62 - input[i]) >>> 8) & 3)); - } + private static int decodeBase64(final char[] src, final int src_offset) { + int val = 0; + for (int i = 0; i < 4; ++i) { + final char c = src[i + src_offset]; + 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; + } - public static String keyToBase64(final byte key[]) { - if (key.length != WG_KEY_LEN) - throw new IllegalArgumentException("WireGuard keys must be 32 bytes"); - final char base64[] = new char[WG_KEY_LEN_BASE64]; - int i; - for (i = 0; i < WG_KEY_LEN / 3; ++i) - encodeBase64(base64, i * 4, key, i * 3); - final byte endSegment[] = { key[i * 3 + 0], key[i * 3 + 1], 0 }; - encodeBase64(base64, i * 4, endSegment, 0); - base64[WG_KEY_LEN_BASE64 - 1] = '='; - return new String(base64); - } + private static void encodeBase64(final byte[] src, final int src_offset, + char[] dest, final int dest_offset) { + final byte[] input = { + (byte) ((src[0 + src_offset] >>> 2) & 63), + (byte) ((src[0 + src_offset] << 4 | ((src[1 + src_offset] & 0xff) >>> 4)) & 63), + (byte) ((src[1 + src_offset] << 2 | ((src[2 + src_offset] & 0xff) >>> 6)) & 63), + (byte) ((src[2 + src_offset]) & 63), + }; + for (int i = 0; i < 4; ++i) { + dest[i + dest_offset] = (char) (input[i] + 'A' + + (((25 - input[i]) >>> 8) & 6) + - (((51 - input[i]) >>> 8) & 75) + - (((61 - input[i]) >>> 8) & 15) + + (((62 - input[i]) >>> 8) & 3)); + } + } - private static int decodeBase64(final char src[], int src_offset) { - int val = 0; - for (int i = 0; i < 4; ++i) - val |= (-1 - + ((((('A' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('Z' + 1))) >>> 8) & (src[i + src_offset] - 64)) - + ((((('a' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('z' + 1))) >>> 8) & (src[i + src_offset] - 70)) - + ((((('0' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('9' + 1))) >>> 8) & (src[i + src_offset] + 5)) - + ((((('+' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('+' + 1))) >>> 8) & 63) - + ((((('/' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('/' + 1))) >>> 8) & 64) - ) << (18 - 6 * i); - return val; - } + public static byte[] keyFromBase64(final String str) { + final char[] input = str.toCharArray(); + final byte[] key = new byte[KEY_LENGTH]; + if (input.length != KEY_LENGTH_BASE64 || input[KEY_LENGTH_BASE64 - 1] != '=') + throw new IllegalArgumentException(KEY_LENGTH_BASE64_EXCEPTION_MESSAGE); + int i; + for (i = 0; i < KEY_LENGTH / 3; ++i) { + final int val = decodeBase64(input, i * 4); + if (val < 0) + throw new IllegalArgumentException(KEY_LENGTH_BASE64_EXCEPTION_MESSAGE); + key[i * 3 + 0] = (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 + 0], + input[i * 4 + 1], + input[i * 4 + 2], + 'A', + }; + final int val = decodeBase64(endSegment, 0); + if (val < 0 || (val & 0xff) != 0) + throw new IllegalArgumentException(KEY_LENGTH_BASE64_EXCEPTION_MESSAGE); + key[i * 3 + 0] = (byte) ((val >>> 16) & 0xff); + key[i * 3 + 1] = (byte) ((val >>> 8) & 0xff); + return key; + } - public static byte[] keyFromBase64(final String input) { - final char base64[] = input.toCharArray(); - if (base64.length != WG_KEY_LEN_BASE64 || base64[WG_KEY_LEN_BASE64 - 1] != '=') - throw new IllegalArgumentException("WireGuard base64 keys must be 44 characters encoding 32 bytes"); - final byte key[] = new byte[WG_KEY_LEN]; - int i; - int val; - - for (i = 0; i < WG_KEY_LEN / 3; ++i) { - val = decodeBase64(base64, i * 4); - if (val < 0) - throw new IllegalArgumentException("WireGuard base64 keys must be 44 characters encoding 32 bytes"); - key[i * 3 + 0] = (byte)((val >>> 16) & 0xff); - key[i * 3 + 1] = (byte)((val >>> 8) & 0xff); - key[i * 3 + 2] = (byte)(val & 0xff); - } - final char endSegment[] = { base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' }; - val = decodeBase64(endSegment, 0); - if (val < 0 || (val & 0xff) != 0) - throw new IllegalArgumentException("WireGuard base64 keys must be 44 characters encoding 32 bytes"); - key[i * 3 + 0] = (byte)((val >>> 16) & 0xff); - key[i * 3 + 1] = (byte)((val >>> 8) & 0xff); - 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("WireGuard keys must be 32 bytes"); + int i; + for (i = 0; i < KEY_LENGTH / 3; ++i) + encodeBase64(key, i * 3, output, i * 4); + final byte[] endSegment = { + key[i * 3 + 0], + key[i * 3 + 1], + 0, + }; + encodeBase64(endSegment, 0, output, i * 4); + output[KEY_LENGTH_BASE64 - 1] = '='; + return new String(output); + } } diff --git a/app/src/main/java/com/wireguard/crypto/Keypair.java b/app/src/main/java/com/wireguard/crypto/Keypair.java index 71c7066c..bd6fd90f 100644 --- a/app/src/main/java/com/wireguard/crypto/Keypair.java +++ b/app/src/main/java/com/wireguard/crypto/Keypair.java @@ -1,7 +1,5 @@ package com.wireguard.crypto; -import android.util.Base64; - import java.security.SecureRandom; /** @@ -11,7 +9,7 @@ import java.security.SecureRandom; public class Keypair { private static byte[] generatePrivateKey() { final SecureRandom secureRandom = new SecureRandom(); - final byte[] privateKey = new byte[KeyEncoding.WG_KEY_LEN]; + final byte[] privateKey = new byte[KeyEncoding.KEY_LENGTH]; secureRandom.nextBytes(privateKey); privateKey[0] &= 248; privateKey[31] &= 127; @@ -20,7 +18,7 @@ public class Keypair { } private static byte[] generatePublicKey(byte[] privateKey) { - final byte[] publicKey = new byte[KeyEncoding.WG_KEY_LEN]; + final byte[] publicKey = new byte[KeyEncoding.KEY_LENGTH]; Curve25519.eval(publicKey, 0, privateKey, null); return publicKey; }