package com.example.otp_kommunikator import org.bouncycastle.crypto.generators.Argon2BytesGenerator import org.bouncycastle.crypto.params.Argon2Parameters import org.bouncycastle.crypto.generators.HKDFBytesGenerator import org.bouncycastle.crypto.params.HKDFParameters import org.bouncycastle.crypto.digests.SHA256Digest import java.security.MessageDigest import java.nio.ByteBuffer import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec object CryptoManager { fun deriveKeyArgon2(password: CharArray, salt: ByteArray): ByteArray { val charBuffer = java.nio.CharBuffer.wrap(password) val byteBuffer = Charsets.UTF_8.encode(charBuffer) val passwordBytes = ByteArray(byteBuffer.remaining()) byteBuffer.get(passwordBytes) val parameters = Argon2Parameters.Builder(Argon2Parameters.ARGON2_id) .withVersion(Argon2Parameters.ARGON2_VERSION_13) .withIterations(5) .withMemoryAsKB(98304) .withParallelism(4) .withSalt(salt) .build() val generator = Argon2BytesGenerator() generator.init(parameters) val result = ByteArray(32) try { generator.generateBytes(passwordBytes, result) return result } finally { passwordBytes.fill(0) } } // NEU: Saubere Schlüsseltrennung (HKDF) fun deriveHKDF(masterKey: ByteArray, info: String, outputLength: Int = 32): ByteArray { val hkdf = HKDFBytesGenerator(SHA256Digest()) hkdf.init(HKDFParameters(masterKey, null, info.toByteArray(Charsets.UTF_8))) val result = ByteArray(outputLength) hkdf.generateBytes(result, 0, outputLength) return result } // NEU: Kryptografische Checksumme (erste 8 Bytes von SHA-256) statt CRC32 fun calculateChecksum(data: ByteArray): Long { val hash = getSHA256(data) val checksum = ByteBuffer.wrap(hash, 0, 8).long hash.fill(0) return checksum } fun encryptWithHMAC(messageBytes: ByteArray, otpKey: ByteArray): ByteArray { val hmacKey = otpKey.copyOfRange(0, 32) val xorKey = otpKey.copyOfRange(32, 32 + messageBytes.size) val cipherBytes = ByteArray(messageBytes.size) try { for (i in messageBytes.indices) { cipherBytes[i] = (messageBytes[i].toInt() xor xorKey[i].toInt()).toByte() } val mac = Mac.getInstance("HmacSHA256") mac.init(SecretKeySpec(hmacKey, "HmacSHA256")) return mac.doFinal(cipherBytes) + cipherBytes } finally { hmacKey.fill(0); xorKey.fill(0) } } fun decryptWithHMAC(cipherBytes: ByteArray, receivedHmac: ByteArray, otpKey: ByteArray): ByteArray { val hmacKey = otpKey.copyOfRange(0, 32) val xorKey = otpKey.copyOfRange(32, 32 + cipherBytes.size) val plainBytes = ByteArray(cipherBytes.size) try { val mac = Mac.getInstance("HmacSHA256") mac.init(SecretKeySpec(hmacKey, "HmacSHA256")) val calculatedHmac = mac.doFinal(cipherBytes) if (!MessageDigest.isEqual(calculatedHmac, receivedHmac)) { throw SecurityException("Integritätsprüfung fehlgeschlagen!") } for (i in cipherBytes.indices) { plainBytes[i] = (cipherBytes[i].toInt() xor xorKey[i].toInt()).toByte() } return plainBytes } finally { hmacKey.fill(0); xorKey.fill(0) } } fun removePadding(data: ByteArray): ByteArray { if (data.isEmpty()) return data val padLen = data.last().toInt() and 0xFF if (padLen == 0 || padLen > data.size) { throw SecurityException("Ungültiges Padding!") } var mismatch = 0 val expectedPadByte = data.last() for (i in data.size - padLen until data.size) { mismatch = mismatch or (data[i].toInt() xor expectedPadByte.toInt()) } if (mismatch != 0) { throw SecurityException("Ungültiges Padding!") } return data.copyOfRange(0, data.size - padLen) } fun getSHA256(input: ByteArray): ByteArray = MessageDigest.getInstance("SHA-256").digest(input) fun byteArrayToHex(a: ByteArray): String { val sb = StringBuilder(a.size * 2) for (b in a) sb.append(String.format("%02x", b)) return sb.toString() } fun hexToByteArray(s: String): ByteArray { val data = ByteArray(s.length / 2) for (i in 0 until s.length step 2) { data[i / 2] = ((Character.digit(s[i], 16) shl 4) + Character.digit(s[i + 1], 16)).toByte() } return data } fun padToTargetLength(data: ByteArray, targetLength: Int): ByteArray { val padLen = targetLength - data.size val padded = ByteArray(targetLength) data.copyInto(padded) for (i in data.size until targetLength) padded[i] = padLen.toByte() return padded } }