Factor out onion address encoder to CryptoComponent

This commit is contained in:
Daniel Lublin
2021-11-11 14:04:12 +01:00
parent 6d1a0a5792
commit 1809be4656
6 changed files with 54 additions and 25 deletions

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.plugin.tor;
import android.app.Application; import android.app.Application;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
@@ -60,6 +61,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
private final File torDirectory; private final File torDirectory;
private int torSocksPort; private int torSocksPort;
private int torControlPort; private int torControlPort;
private final CryptoComponent crypto;
@Inject @Inject
AndroidTorPluginFactory(@IoExecutor Executor ioExecutor, AndroidTorPluginFactory(@IoExecutor Executor ioExecutor,
@@ -77,7 +79,8 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
Clock clock, Clock clock,
@TorDirectory File torDirectory, @TorDirectory File torDirectory,
@TorSocksPort int torSocksPort, @TorSocksPort int torSocksPort,
@TorControlPort int torControlPort) { @TorControlPort int torControlPort,
CryptoComponent crypto) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor; this.wakefulIoExecutor = wakefulIoExecutor;
this.app = app; this.app = app;
@@ -94,6 +97,7 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
this.torDirectory = torDirectory; this.torDirectory = torDirectory;
this.torSocksPort = torSocksPort; this.torSocksPort = torSocksPort;
this.torControlPort = torControlPort; this.torControlPort = torControlPort;
this.crypto = crypto;
} }
@Override @Override
@@ -135,7 +139,8 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl(); TorRendezvousCrypto torRendezvousCrypto =
new TorRendezvousCryptoImpl(crypto);
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor,
wakefulIoExecutor, app, networkManager, locationUtils, wakefulIoExecutor, app, networkManager, locationUtils,
torSocketFactory, clock, resourceProvider, torSocketFactory, clock, resourceProvider,

View File

@@ -170,4 +170,11 @@ public interface CryptoComponent {
* length. The line terminator is CRLF. * length. The line terminator is CRLF.
*/ */
String asciiArmour(byte[] b, int lineLength); String asciiArmour(byte[] b, int lineLength);
/**
* Encode the onion/hidden service address given its public key. As
* specified here: https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt?id=29245fd5#n2135
*/
String encodeOnionAddress(byte[] publicKey);
} }

View File

@@ -7,6 +7,7 @@ import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.Blake2bDigest; import org.bouncycastle.crypto.digests.Blake2bDigest;
import org.bouncycastle.crypto.digests.SHA3Digest;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey; import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey; import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
@@ -21,11 +22,13 @@ import org.briarproject.bramble.api.crypto.SignaturePrivateKey;
import org.briarproject.bramble.api.crypto.SignaturePublicKey; import org.briarproject.bramble.api.crypto.SignaturePublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.util.Base32;
import org.briarproject.bramble.util.ByteUtils; import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.whispersystems.curve25519.Curve25519; import org.whispersystems.curve25519.Curve25519;
import org.whispersystems.curve25519.Curve25519KeyPair; import org.whispersystems.curve25519.Curve25519KeyPair;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.Provider; import java.security.Provider;
@@ -58,6 +61,8 @@ class CryptoComponentImpl implements CryptoComponent {
private static final int PBKDF_SALT_BYTES = 32; // 256 bits private static final int PBKDF_SALT_BYTES = 32; // 256 bits
private static final byte PBKDF_FORMAT_SCRYPT = 0; private static final byte PBKDF_FORMAT_SCRYPT = 0;
private static final byte PBKDF_FORMAT_SCRYPT_STRENGTHENED = 1; private static final byte PBKDF_FORMAT_SCRYPT_STRENGTHENED = 1;
private static final byte ONION_HS_PROTOCOL_VERSION = 3;
private static final int ONION_CHECKSUM_BYTES = 2;
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final PasswordBasedKdf passwordBasedKdf; private final PasswordBasedKdf passwordBasedKdf;
@@ -442,4 +447,21 @@ class CryptoComponentImpl implements CryptoComponent {
public String asciiArmour(byte[] b, int lineLength) { public String asciiArmour(byte[] b, int lineLength) {
return AsciiArmour.wrap(b, lineLength); return AsciiArmour.wrap(b, lineLength);
} }
@Override
public String encodeOnionAddress(byte[] publicKey) {
Digest digest = new SHA3Digest(256);
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
digest.update(label, 0, label.length);
digest.update(publicKey, 0, publicKey.length);
digest.update(ONION_HS_PROTOCOL_VERSION);
byte[] checksum = new byte[digest.getDigestSize()];
digest.doFinal(checksum, 0);
byte[] address = new byte[publicKey.length + ONION_CHECKSUM_BYTES + 1];
arraycopy(publicKey, 0, address, 0, publicKey.length);
arraycopy(checksum, 0, address, publicKey.length, ONION_CHECKSUM_BYTES);
address[address.length - 1] = ONION_HS_PROTOCOL_VERSION;
return Base32.encode(address).toLowerCase();
}
} }

View File

@@ -4,39 +4,26 @@ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA3Digest;
import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Base64;
import org.briarproject.bramble.util.Base32; import org.briarproject.bramble.api.crypto.CryptoComponent;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import static java.lang.System.arraycopy;
public class TorRendezvousCryptoImpl implements TorRendezvousCrypto { public class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
private static final EdDSANamedCurveSpec CURVE_SPEC = private static final EdDSANamedCurveSpec CURVE_SPEC =
EdDSANamedCurveTable.getByName("Ed25519"); EdDSANamedCurveTable.getByName("Ed25519");
private static final byte HS_PROTOCOL_VERSION = 3; private final CryptoComponent crypto;
private static final int CHECKSUM_BYTES = 2;
TorRendezvousCryptoImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
@Override @Override
public String getOnionAddress(byte[] seed) { public String getOnionAddress(byte[] seed) {
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC); EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
byte[] publicKey = spec.getA().toByteArray(); return crypto.encodeOnionAddress(spec.getA().toByteArray());
Digest digest = new SHA3Digest(256);
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
digest.update(label, 0, label.length);
digest.update(publicKey, 0, publicKey.length);
digest.update(HS_PROTOCOL_VERSION);
byte[] checksum = new byte[digest.getDigestSize()];
digest.doFinal(checksum, 0);
byte[] address = new byte[publicKey.length + CHECKSUM_BYTES + 1];
arraycopy(publicKey, 0, address, 0, publicKey.length);
arraycopy(checksum, 0, address, publicKey.length, CHECKSUM_BYTES);
address[address.length - 1] = HS_PROTOCOL_VERSION;
return Base32.encode(address).toLowerCase();
} }
@Override @Override

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.plugin.tor; package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
@@ -58,6 +59,7 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
private final File torDirectory; private final File torDirectory;
private int torSocksPort; private int torSocksPort;
private int torControlPort; private int torControlPort;
private final CryptoComponent crypto;
@Inject @Inject
UnixTorPluginFactory(@IoExecutor Executor ioExecutor, UnixTorPluginFactory(@IoExecutor Executor ioExecutor,
@@ -73,7 +75,8 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
Clock clock, Clock clock,
@TorDirectory File torDirectory, @TorDirectory File torDirectory,
@TorSocksPort int torSocksPort, @TorSocksPort int torSocksPort,
@TorControlPort int torControlPort) { @TorControlPort int torControlPort,
CryptoComponent crypto) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.wakefulIoExecutor = wakefulIoExecutor; this.wakefulIoExecutor = wakefulIoExecutor;
this.networkManager = networkManager; this.networkManager = networkManager;
@@ -88,6 +91,7 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
this.torDirectory = torDirectory; this.torDirectory = torDirectory;
this.torSocksPort = torSocksPort; this.torSocksPort = torSocksPort;
this.torControlPort = torControlPort; this.torControlPort = torControlPort;
this.crypto = crypto;
} }
@Override @Override
@@ -128,7 +132,8 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl(); TorRendezvousCrypto torRendezvousCrypto =
new TorRendezvousCryptoImpl(crypto);
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, wakefulIoExecutor, UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, wakefulIoExecutor,
networkManager, locationUtils, torSocketFactory, clock, networkManager, locationUtils, torSocketFactory, clock,
resourceProvider, circumventionProvider, batteryManager, resourceProvider, circumventionProvider, batteryManager,

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin.tor;
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons; import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
import org.briarproject.bramble.api.battery.BatteryManager; import org.briarproject.bramble.api.battery.BatteryManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager; import org.briarproject.bramble.api.network.NetworkManager;
@@ -88,6 +89,8 @@ public class BridgeTest extends BrambleTestCase {
BackoffFactory backoffFactory; BackoffFactory backoffFactory;
@Inject @Inject
Clock clock; Clock clock;
@Inject
CryptoComponent crypto;
private final File torDir = getTestDirectory(); private final File torDir = getTestDirectory();
private final String bridge; private final String bridge;
@@ -142,7 +145,7 @@ public class BridgeTest extends BrambleTestCase {
networkManager, locationUtils, eventBus, torSocketFactory, networkManager, locationUtils, eventBus, torSocketFactory,
backoffFactory, resourceProvider, bridgeProvider, backoffFactory, resourceProvider, bridgeProvider,
batteryManager, clock, torDir, DEFAULT_SOCKS_PORT, batteryManager, clock, torDir, DEFAULT_SOCKS_PORT,
DEFAULT_CONTROL_PORT); DEFAULT_CONTROL_PORT, crypto);
} }
@After @After