package com.example.otp_kommunikator import android.content.Context import net.sqlcipher.database.SQLiteDatabase import androidx.core.content.ContextCompat import java.io.File import java.io.InputStream fun generateKeyDatabases( context: Context, keyCount: Int, safePinA: CharArray, safePinB: CharArray, entropyFileUri: android.net.Uri ): String? { val sdCardDir = ContextCompat.getExternalFilesDirs(context, null).getOrNull(1) ?: return null val entropyStream = context.contentResolver.openInputStream(entropyFileUri) ?: return null fun readBytes(stream: InputStream, size: Int): ByteArray { val buf = ByteArray(size) var total = 0 while (total < size) { val r = stream.read(buf, total, size - total) if (r == -1) throw Exception("Zu wenig Entropie in der Datei!") total += r } return buf } try { val saltA = readBytes(entropyStream, 16) val saltB = readBytes(entropyStream, 16) val masterKeyA = CryptoManager.deriveKeyArgon2(safePinA, saltA) val sqlCipherKeyA = CryptoManager.deriveHKDF(masterKeyA, "SQLCIPHER_KEY") masterKeyA.fill(0) val masterKeyB = CryptoManager.deriveKeyArgon2(safePinB, saltB) val sqlCipherKeyB = CryptoManager.deriveHKDF(masterKeyB, "SQLCIPHER_KEY") masterKeyB.fill(0) val fileA = File(sdCardDir, "Geraet_A_${CryptoManager.byteArrayToHex(saltA)}.db") val fileB = File(sdCardDir, "Geraet_B_${CryptoManager.byteArrayToHex(saltB)}.db") val dbA = SQLiteDatabase.openOrCreateDatabase(fileA.absolutePath, sqlCipherKeyA, null) val dbB = SQLiteDatabase.openOrCreateDatabase(fileB.absolutePath, sqlCipherKeyB, null) sqlCipherKeyA.fill(0); sqlCipherKeyB.fill(0) val sql = "CREATE TABLE otp_keys (msg_index TEXT, direction TEXT, key_material BLOB, checksum INTEGER)" dbA.execSQL(sql) dbB.execSQL(sql) dbA.execSQL("CREATE TABLE config (config_key TEXT PRIMARY KEY, config_value TEXT)") dbB.execSQL("CREATE TABLE config (config_key TEXT PRIMARY KEY, config_value TEXT)") val keyLength = 150 + 32 dbA.execSQL("INSERT INTO config VALUES ('key_length', ?)", arrayOf(keyLength.toString())) dbB.execSQL("INSERT INTO config VALUES ('key_length', ?)", arrayOf(keyLength.toString())) dbA.beginTransaction() dbB.beginTransaction() val stmtA = dbA.compileStatement("INSERT INTO otp_keys VALUES (?, ?, ?, ?)") val stmtB = dbB.compileStatement("INSERT INTO otp_keys VALUES (?, ?, ?, ?)") for (i in 0 until keyCount) { // Richtung A -> B val idAtoB = CryptoManager.byteArrayToHex(readBytes(entropyStream, 8)) val materialAtoB = readBytes(entropyStream, keyLength) val crcAtoB = CryptoManager.calculateChecksum(materialAtoB) stmtA.bindString(1, idAtoB); stmtA.bindString(2, "SEND"); stmtA.bindBlob(3, materialAtoB); stmtA.bindLong(4, crcAtoB); stmtA.executeInsert(); stmtA.clearBindings() stmtB.bindString(1, idAtoB); stmtB.bindString(2, "RECEIVE"); stmtB.bindBlob(3, materialAtoB); stmtB.bindLong(4, crcAtoB); stmtB.executeInsert(); stmtB.clearBindings() // Richtung B -> A val idBtoA = CryptoManager.byteArrayToHex(readBytes(entropyStream, 8)) val materialBtoA = readBytes(entropyStream, keyLength) val crcBtoA = CryptoManager.calculateChecksum(materialBtoA) stmtA.bindString(1, idBtoA); stmtA.bindString(2, "RECEIVE"); stmtA.bindBlob(3, materialBtoA); stmtA.bindLong(4, crcBtoA); stmtA.executeInsert(); stmtA.clearBindings() stmtB.bindString(1, idBtoA); stmtB.bindString(2, "SEND"); stmtB.bindBlob(3, materialBtoA); stmtB.bindLong(4, crcBtoA); stmtB.executeInsert(); stmtB.clearBindings() } dbA.setTransactionSuccessful() dbB.setTransactionSuccessful() dbA.endTransaction() dbB.endTransaction() dbA.close() dbB.close() return sdCardDir.absolutePath } finally { entropyStream.close() } }