Constant time base64
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
f8d8e5e23e
commit
f6b864d4e7
@ -6,6 +6,7 @@ import android.databinding.Observable;
|
|||||||
|
|
||||||
import com.wireguard.android.BR;
|
import com.wireguard.android.BR;
|
||||||
import com.wireguard.crypto.Keypair;
|
import com.wireguard.crypto.Keypair;
|
||||||
|
import com.wireguard.crypto.KeyEncoding;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the configuration for a WireGuard interface (an [Interface] block).
|
* Represents the configuration for a WireGuard interface (an [Interface] block).
|
||||||
@ -90,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() != Keypair.KEY_STRING_LENGTH)
|
if (privateKey.length() != KeyEncoding.WG_KEY_LEN_BASE64)
|
||||||
return;
|
return;
|
||||||
keypair = new Keypair(privateKey);
|
keypair = new Keypair(privateKey);
|
||||||
notifyPropertyChanged(BR.privateKey);
|
notifyPropertyChanged(BR.privateKey);
|
||||||
|
75
app/src/main/java/com/wireguard/crypto/KeyEncoding.java
Normal file
75
app/src/main/java/com/wireguard/crypto/KeyEncoding.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/* 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;
|
||||||
|
|
||||||
|
public class KeyEncoding {
|
||||||
|
public static final int WG_KEY_LEN = 32;
|
||||||
|
public static final int WG_KEY_LEN_BASE64 = 44;
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 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;
|
||||||
|
}
|
||||||
|
}
|
@ -9,12 +9,9 @@ import java.security.SecureRandom;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public class Keypair {
|
public class Keypair {
|
||||||
private static final int KEY_LENGTH = 32;
|
|
||||||
public static final int KEY_STRING_LENGTH = 44;
|
|
||||||
|
|
||||||
private static byte[] generatePrivateKey() {
|
private static byte[] generatePrivateKey() {
|
||||||
final SecureRandom secureRandom = new SecureRandom();
|
final SecureRandom secureRandom = new SecureRandom();
|
||||||
final byte privateKey[] = new byte[KEY_LENGTH];
|
final byte privateKey[] = new byte[KeyEncoding.WG_KEY_LEN];
|
||||||
secureRandom.nextBytes(privateKey);
|
secureRandom.nextBytes(privateKey);
|
||||||
privateKey[0] &= 248;
|
privateKey[0] &= 248;
|
||||||
privateKey[31] &= 127;
|
privateKey[31] &= 127;
|
||||||
@ -23,22 +20,11 @@ public class Keypair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] generatePublicKey(byte privateKey[]) {
|
private static byte[] generatePublicKey(byte privateKey[]) {
|
||||||
final byte publicKey[] = new byte[KEY_LENGTH];
|
final byte publicKey[] = new byte[KeyEncoding.WG_KEY_LEN];
|
||||||
Curve25519.eval(publicKey, 0, privateKey, null);
|
Curve25519.eval(publicKey, 0, privateKey, null);
|
||||||
return publicKey;
|
return publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] parseKey(String key) {
|
|
||||||
final byte keyBytes[] = Base64.decode(key, Base64.NO_WRAP);
|
|
||||||
if (keyBytes.length != KEY_LENGTH)
|
|
||||||
throw new IndexOutOfBoundsException("Key is not the correct length");
|
|
||||||
return keyBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String unParseKey(byte keyBytes[]) {
|
|
||||||
return Base64.encodeToString(keyBytes, Base64.NO_WRAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final byte privateKey[];
|
private final byte privateKey[];
|
||||||
private final byte publicKey[];
|
private final byte publicKey[];
|
||||||
|
|
||||||
@ -52,14 +38,14 @@ public class Keypair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Keypair(String privateKey) {
|
public Keypair(String privateKey) {
|
||||||
this(parseKey(privateKey));
|
this(KeyEncoding.keyFromBase64(privateKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPrivateKey() {
|
public String getPrivateKey() {
|
||||||
return unParseKey(privateKey);
|
return KeyEncoding.keyToBase64(privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPublicKey() {
|
public String getPublicKey() {
|
||||||
return unParseKey(publicKey);
|
return KeyEncoding.keyToBase64(publicKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user