KeyEncoding: Clean up and reorganize to match style

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Samuel Holland 2017-08-09 02:51:08 -05:00
parent c3afe5be2c
commit f0f9192aed
3 changed files with 85 additions and 71 deletions

View File

@ -91,7 +91,7 @@ public class Interface extends BaseObservable implements Observable {
public void setPrivateKey(String privateKey) { public void setPrivateKey(String privateKey) {
// Avoid exceptions from Keypair while the user is typing. // 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; return;
keypair = new Keypair(privateKey); keypair = new Keypair(privateKey);
notifyPropertyChanged(BR.privateKey); notifyPropertyChanged(BR.privateKey);

View File

@ -1,75 +1,91 @@
/* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. /* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
*
* This is a specialized constant-time base64 implementation that resists side-channel attacks.
*/
package com.wireguard.crypto; package com.wireguard.crypto;
/**
* This is a specialized constant-time base64 implementation that resists side-channel attacks.
*/
public class KeyEncoding { public class KeyEncoding {
public static final int WG_KEY_LEN = 32; public static final int KEY_LENGTH = 32;
public static final int WG_KEY_LEN_BASE64 = 44; 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) { private static int decodeBase64(final char[] src, final int src_offset) {
final byte input[] = { (byte)((src[0 + src_offset] >>> 2) & 63), int val = 0;
(byte)(((src[0 + src_offset] << 4) | ((src[1 + src_offset] & 0xff) >>> 4)) & 63), for (int i = 0; i < 4; ++i) {
(byte)(((src[1 + src_offset] << 2) | ((src[2 + src_offset] & 0xff) >>> 6)) & 63), final char c = src[i + src_offset];
(byte)(src[2 + src_offset] & 63) }; val |= (-1
for (int i = 0; i < 4; ++i) + ((((('A' - 1) - c) & (c - ('Z' + 1))) >>> 8) & (c - 64))
dest[i + dest_offset] = (char)(input[i] + 'A' + ((((('a' - 1) - c) & (c - ('z' + 1))) >>> 8) & (c - 70))
+ (((25 - input[i]) >>> 8) & 6) + ((((('0' - 1) - c) & (c - ('9' + 1))) >>> 8) & (c + 5))
- (((51 - input[i]) >>> 8) & 75) + ((((('+' - 1) - c) & (c - ('+' + 1))) >>> 8) & 63)
- (((61 - input[i]) >>> 8) & 15) + ((((('/' - 1) - c) & (c - ('/' + 1))) >>> 8) & 64)
+ (((62 - input[i]) >>> 8) & 3)); ) << (18 - 6 * i);
} }
return val;
}
public static String keyToBase64(final byte key[]) { private static void encodeBase64(final byte[] src, final int src_offset,
if (key.length != WG_KEY_LEN) char[] dest, final int dest_offset) {
throw new IllegalArgumentException("WireGuard keys must be 32 bytes"); final byte[] input = {
final char base64[] = new char[WG_KEY_LEN_BASE64]; (byte) ((src[0 + src_offset] >>> 2) & 63),
int i; (byte) ((src[0 + src_offset] << 4 | ((src[1 + src_offset] & 0xff) >>> 4)) & 63),
for (i = 0; i < WG_KEY_LEN / 3; ++i) (byte) ((src[1 + src_offset] << 2 | ((src[2 + src_offset] & 0xff) >>> 6)) & 63),
encodeBase64(base64, i * 4, key, i * 3); (byte) ((src[2 + src_offset]) & 63),
final byte endSegment[] = { key[i * 3 + 0], key[i * 3 + 1], 0 }; };
encodeBase64(base64, i * 4, endSegment, 0); for (int i = 0; i < 4; ++i) {
base64[WG_KEY_LEN_BASE64 - 1] = '='; dest[i + dest_offset] = (char) (input[i] + 'A'
return new String(base64); + (((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) { public static byte[] keyFromBase64(final String str) {
int val = 0; final char[] input = str.toCharArray();
for (int i = 0; i < 4; ++i) final byte[] key = new byte[KEY_LENGTH];
val |= (-1 if (input.length != KEY_LENGTH_BASE64 || input[KEY_LENGTH_BASE64 - 1] != '=')
+ ((((('A' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('Z' + 1))) >>> 8) & (src[i + src_offset] - 64)) throw new IllegalArgumentException(KEY_LENGTH_BASE64_EXCEPTION_MESSAGE);
+ ((((('a' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('z' + 1))) >>> 8) & (src[i + src_offset] - 70)) int i;
+ ((((('0' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('9' + 1))) >>> 8) & (src[i + src_offset] + 5)) for (i = 0; i < KEY_LENGTH / 3; ++i) {
+ ((((('+' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('+' + 1))) >>> 8) & 63) final int val = decodeBase64(input, i * 4);
+ ((((('/' - 1) - src[i + src_offset]) & (src[i + src_offset] - ('/' + 1))) >>> 8) & 64) if (val < 0)
) << (18 - 6 * i); throw new IllegalArgumentException(KEY_LENGTH_BASE64_EXCEPTION_MESSAGE);
return val; 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) { public static String keyToBase64(final byte[] key) {
final char base64[] = input.toCharArray(); final char[] output = new char[KEY_LENGTH_BASE64];
if (base64.length != WG_KEY_LEN_BASE64 || base64[WG_KEY_LEN_BASE64 - 1] != '=') if (key.length != KEY_LENGTH)
throw new IllegalArgumentException("WireGuard base64 keys must be 44 characters encoding 32 bytes"); throw new IllegalArgumentException("WireGuard keys must be 32 bytes");
final byte key[] = new byte[WG_KEY_LEN]; int i;
int i; for (i = 0; i < KEY_LENGTH / 3; ++i)
int val; encodeBase64(key, i * 3, output, i * 4);
final byte[] endSegment = {
for (i = 0; i < WG_KEY_LEN / 3; ++i) { key[i * 3 + 0],
val = decodeBase64(base64, i * 4); key[i * 3 + 1],
if (val < 0) 0,
throw new IllegalArgumentException("WireGuard base64 keys must be 44 characters encoding 32 bytes"); };
key[i * 3 + 0] = (byte)((val >>> 16) & 0xff); encodeBase64(endSegment, 0, output, i * 4);
key[i * 3 + 1] = (byte)((val >>> 8) & 0xff); output[KEY_LENGTH_BASE64 - 1] = '=';
key[i * 3 + 2] = (byte)(val & 0xff); return new String(output);
} }
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;
}
} }

View File

@ -1,7 +1,5 @@
package com.wireguard.crypto; package com.wireguard.crypto;
import android.util.Base64;
import java.security.SecureRandom; import java.security.SecureRandom;
/** /**
@ -11,7 +9,7 @@ import java.security.SecureRandom;
public class Keypair { public class Keypair {
private static byte[] generatePrivateKey() { private static byte[] generatePrivateKey() {
final SecureRandom secureRandom = new SecureRandom(); 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); secureRandom.nextBytes(privateKey);
privateKey[0] &= 248; privateKey[0] &= 248;
privateKey[31] &= 127; privateKey[31] &= 127;
@ -20,7 +18,7 @@ public class Keypair {
} }
private static byte[] generatePublicKey(byte[] privateKey) { 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); Curve25519.eval(publicKey, 0, privateKey, null);
return publicKey; return publicKey;
} }