diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/core/AndroidEagerSingletons.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/core/AndroidEagerSingletons.kt index 1269bb461bb447eb2be2d10f035fcd14a55ac9d4..d477e73e75133cacd5fa2e7373d7a6ccec4ab22e 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/core/AndroidEagerSingletons.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/core/AndroidEagerSingletons.kt @@ -2,12 +2,12 @@ package org.briarproject.mailbox.core import org.briarproject.mailbox.core.system.AndroidTaskScheduler import org.briarproject.mailbox.core.tor.AndroidNetworkManager -import org.briarproject.mailbox.core.tor.AndroidTorPlugin +import org.briarproject.mailbox.core.tor.TorPlugin import javax.inject.Inject @Suppress("unused") internal class AndroidEagerSingletons @Inject constructor( val androidTaskScheduler: AndroidTaskScheduler, val androidNetworkManager: AndroidNetworkManager, - val androidTorPlugin: AndroidTorPlugin, + val androidTorPlugin: TorPlugin, ) diff --git a/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt b/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt index 369f926cd02ee4b8f49ac56261dd23e163afbcb8..b750cf91afd42a1c830a51205b9d0c329d00135a 100644 --- a/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt +++ b/mailbox-android/src/main/java/org/briarproject/mailbox/core/tor/AndroidTorModule.kt @@ -54,7 +54,7 @@ internal class AndroidTorModule { backoff: Backoff, lifecycleManager: LifecycleManager, eventBus: EventBus, - ) = AndroidTorPlugin( + ): TorPlugin = AndroidTorPlugin( ioExecutor, app, settingsManager, diff --git a/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/Main.kt b/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/Main.kt index 24784e8ba8a1a0c2b205a4ed2282a68950c36e82..cb2abda0e79e9e6fb4f6ea9125b93a18f0bb2d9f 100644 --- a/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/Main.kt +++ b/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/Main.kt @@ -8,7 +8,10 @@ import com.github.ajalt.clikt.parameters.options.flag import com.github.ajalt.clikt.parameters.options.option import org.briarproject.mailbox.core.CoreEagerSingletons import org.briarproject.mailbox.core.JavaCliEagerSingletons +import org.briarproject.mailbox.core.db.Database import org.briarproject.mailbox.core.lifecycle.LifecycleManager +import org.briarproject.mailbox.core.setup.QrCodeEncoder +import org.briarproject.mailbox.core.setup.SetupManager import org.slf4j.LoggerFactory.getLogger import java.util.logging.Level.ALL import java.util.logging.Level.INFO @@ -38,6 +41,15 @@ class Main : CliktCommand( @Inject internal lateinit var lifecycleManager: LifecycleManager + @Inject + internal lateinit var db: Database + + @Inject + internal lateinit var setupManager: SetupManager + + @Inject + internal lateinit var qrCodeEncoder: QrCodeEncoder + override fun run() { // logging val levelSlf4j = if (debug) Level.DEBUG else when (verbosity) { @@ -68,6 +80,18 @@ class Main : CliktCommand( lifecycleManager.startServices() lifecycleManager.waitForStartup() + + // TODO this is obviously not the final code, just a stub to get us started + val setupTokenExists = db.transactionWithResult(true) { txn -> + setupManager.getSetupToken(txn) != null + } + val ownerTokenExists = db.transactionWithResult(true) { txn -> + setupManager.getOwnerToken(txn) != null + } + if (!setupTokenExists && !ownerTokenExists) setupManager.restartSetup() + qrCodeEncoder.getQrCodeBitMatrix()?.let { + println(QrCodeRenderer.getQrString(it)) + } } } diff --git a/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/QrCodeRenderer.kt b/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/QrCodeRenderer.kt new file mode 100644 index 0000000000000000000000000000000000000000..c96f6123b021fb36a270a23d9d587e5f22bf7e5e --- /dev/null +++ b/mailbox-cli/src/main/java/org/briarproject/mailbox/cli/QrCodeRenderer.kt @@ -0,0 +1,20 @@ +package org.briarproject.mailbox.cli + +import com.google.zxing.common.BitMatrix + +object QrCodeRenderer { + + private const val SET = "██" + private const val UNSET = " " + + internal fun getQrString(bitMatrix: BitMatrix): String = StringBuilder().apply { + append(System.lineSeparator()) + for (y in 0 until bitMatrix.height) { + for (x in 0 until bitMatrix.width) { + append(if (bitMatrix[x, y]) SET else UNSET) + } + append(System.lineSeparator()) + } + }.toString() + +} diff --git a/mailbox-cli/src/main/java/org/briarproject/mailbox/core/JavaCliEagerSingletons.kt b/mailbox-cli/src/main/java/org/briarproject/mailbox/core/JavaCliEagerSingletons.kt index 446a1cc524401b85bd76e56e518c5c7f84d652bf..8e1830fdd027270ab403c91053973ef59ccc5933 100644 --- a/mailbox-cli/src/main/java/org/briarproject/mailbox/core/JavaCliEagerSingletons.kt +++ b/mailbox-cli/src/main/java/org/briarproject/mailbox/core/JavaCliEagerSingletons.kt @@ -1,11 +1,11 @@ package org.briarproject.mailbox.core import org.briarproject.mailbox.core.system.TaskScheduler -import org.briarproject.mailbox.core.tor.JavaTorPlugin +import org.briarproject.mailbox.core.tor.TorPlugin import javax.inject.Inject @Suppress("unused") internal class JavaCliEagerSingletons @Inject constructor( val taskScheduler: TaskScheduler, - val javaTorPlugin: JavaTorPlugin, + val javaTorPlugin: TorPlugin, ) diff --git a/mailbox-cli/src/main/java/org/briarproject/mailbox/core/tor/JavaTorModule.kt b/mailbox-cli/src/main/java/org/briarproject/mailbox/core/tor/JavaTorModule.kt index 051edfe6a889f91e80d11fef66db36ae1ff9f97e..d281a492bc79c740e49180ecfe8124376722ab36 100644 --- a/mailbox-cli/src/main/java/org/briarproject/mailbox/core/tor/JavaTorModule.kt +++ b/mailbox-cli/src/main/java/org/briarproject/mailbox/core/tor/JavaTorModule.kt @@ -47,7 +47,7 @@ internal class JavaTorModule { backoff: Backoff, lifecycleManager: LifecycleManager, eventBus: EventBus, - ): JavaTorPlugin { + ): TorPlugin { val configDir = File(System.getProperty("user.home") + File.separator + ".config") val mailboxDir = File(configDir, ".briar-mailbox") val torDir = File(mailboxDir, "tor") diff --git a/mailbox-core/build.gradle b/mailbox-core/build.gradle index 198bff74dfbca8d44375f04a8b78fe24d8b7a4b1..7d4568cd69a806bef811c46100ca126db6a57f0c 100644 --- a/mailbox-core/build.gradle +++ b/mailbox-core/build.gradle @@ -28,6 +28,11 @@ dependencies { implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6 + // Base 32 + implementation 'dev.keiji.rfc4648:rfc4648:1.0.0' + // QrCode + implementation 'com.google.zxing:core:3.4.1' + testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_version" testImplementation "org.junit.jupiter:junit-jupiter-params:$junit_version" diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/setup/QrCodeEncoder.kt b/mailbox-core/src/main/java/org/briarproject/mailbox/core/setup/QrCodeEncoder.kt new file mode 100644 index 0000000000000000000000000000000000000000..334482d378562efaa5e64ce376ae791b75c8933c --- /dev/null +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/setup/QrCodeEncoder.kt @@ -0,0 +1,80 @@ +package org.briarproject.mailbox.core.setup + +import com.google.zxing.BarcodeFormat.QR_CODE +import com.google.zxing.common.BitMatrix +import com.google.zxing.qrcode.QRCodeWriter +import dev.keiji.util.Base32 +import org.briarproject.mailbox.core.db.Database +import org.briarproject.mailbox.core.db.DbException +import org.briarproject.mailbox.core.tor.TorPlugin +import org.briarproject.mailbox.core.util.LogUtils.logException +import org.briarproject.mailbox.core.util.StringUtils.fromHexString +import org.slf4j.LoggerFactory.getLogger +import java.nio.ByteBuffer +import java.nio.charset.Charset +import javax.inject.Inject + +private const val VERSION = 32 +private val LOG = getLogger(QrCodeEncoder::class.java) + +class QrCodeEncoder @Inject constructor( + private val db: Database, + private val setupManager: SetupManager, + private val torPlugin: TorPlugin, +) { + + fun getQrCodeBitMatrix(edgeLen: Int = 0): BitMatrix? { + val bytes = getQrCodeBytes() ?: return null + // Use ISO 8859-1 to encode bytes directly as a string + val content = String(bytes, Charset.forName("ISO-8859-1")) + return QRCodeWriter().encode(content, QR_CODE, edgeLen, edgeLen) + } + + private fun getQrCodeBytes(): ByteArray? { + val hiddenServiceBytes = getHiddenServiceBytes() ?: return null + val setupTokenBytes = getSetupTokenBytes() ?: return null + return ByteBuffer.allocate(65) + .put(VERSION.toByte()) // 1 + .put(hiddenServiceBytes) // 32 + .put(setupTokenBytes) // 32 + .array() + } + + /** + * https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt?id=29245fd5#n2135 + */ + private fun getHiddenServiceBytes(): ByteArray? { + val addressString = try { + torPlugin.hiddenServiceAddress + } catch (e: DbException) { + logException(LOG, e) + return null + } + if (addressString == null) { + LOG.error("Hidden service address not yet available") + return null + } + LOG.error(addressString) + val addressBytes = Base32.decode(addressString.uppercase()) + check(addressBytes.size == 35) { "$addressString not 35 bytes long" } + return addressBytes.copyOfRange(0, 32) + } + + private fun getSetupTokenBytes(): ByteArray? { + val tokenString = try { + db.transactionWithResult(true) { txn -> + setupManager.getSetupToken(txn) + } + } catch (e: DbException) { + logException(LOG, e) + return null + } + if (tokenString == null) { + LOG.error("Setup token not available") + return null + } + val tokenBytes = fromHexString(tokenString) + check(tokenBytes.size == 32) { "$tokenString not 32 bytes long" } + return tokenBytes + } +} diff --git a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java index 05929a6ef0dd4fdd7bcbf6fb2eaab759ddc7c1bc..276a592e30e1569c757afc60e696b5c14128ca83 100644 --- a/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java +++ b/mailbox-core/src/main/java/org/briarproject/mailbox/core/tor/TorPlugin.java @@ -63,7 +63,8 @@ import static org.briarproject.mailbox.core.util.LogUtils.warn; import static org.briarproject.mailbox.core.util.PrivacyUtils.scrubOnion; import static org.slf4j.LoggerFactory.getLogger; -abstract class TorPlugin implements Service, EventHandler, EventListener { +public abstract class TorPlugin + implements Service, EventHandler, EventListener { private static final Logger LOG = getLogger(TorPlugin.class); @@ -376,12 +377,18 @@ abstract class TorPlugin implements Service, EventHandler, EventListener { if (privKey == null) { s.put(HS_PRIVATE_KEY_V3, response.get(HS_PRIVKEY)); + try { + settingsManager.mergeSettings(s, SETTINGS_NAMESPACE); + } catch (DbException e) { + logException(LOG, e); + } } - try { - settingsManager.mergeSettings(s, SETTINGS_NAMESPACE); - } catch (DbException e) { - logException(LOG, e); - } + } + + @Nullable + public String getHiddenServiceAddress() throws DbException { + Settings s = settingsManager.getSettings(SETTINGS_NAMESPACE); + return s.get(HS_ADDRESS_V3); } protected void enableNetwork(boolean enable) throws IOException {