mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 10:45:21 +00:00
remove trash
This commit is contained in:
@@ -1,361 +0,0 @@
|
|||||||
package com.bitchat.android.noise.protocol
|
|
||||||
|
|
||||||
import java.security.SecureRandom
|
|
||||||
import javax.crypto.Cipher
|
|
||||||
import javax.crypto.spec.ChaCha20ParameterSpec
|
|
||||||
import javax.crypto.spec.SecretKeySpec
|
|
||||||
import javax.crypto.Mac
|
|
||||||
import javax.crypto.spec.SecretKeySpec as HmacKeySpec
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local fork of Noise Protocol Framework that PROPERLY supports setting pre-existing keys
|
|
||||||
* This fixes the critical limitation in noise-java where setPrivateKey() doesn't work
|
|
||||||
* Implements the complete Noise_XX_25519_ChaChaPoly_SHA256 protocol
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exception thrown by Noise protocol operations
|
|
||||||
*/
|
|
||||||
class NoiseException(message: String, cause: Throwable? = null) : Exception(message, cause)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DH (Diffie-Hellman) interface for key exchange operations
|
|
||||||
*/
|
|
||||||
interface DHState {
|
|
||||||
fun dhName(): String
|
|
||||||
fun hasPrivateKey(): Boolean
|
|
||||||
fun hasPublicKey(): Boolean
|
|
||||||
fun privateKeyLength(): Int
|
|
||||||
fun publicKeyLength(): Int
|
|
||||||
|
|
||||||
fun generateKeyPair()
|
|
||||||
fun setPrivateKey(privateKey: ByteArray, offset: Int)
|
|
||||||
fun setPublicKey(publicKey: ByteArray, offset: Int)
|
|
||||||
fun getPrivateKey(privateKey: ByteArray, offset: Int)
|
|
||||||
fun getPublicKey(publicKey: ByteArray, offset: Int)
|
|
||||||
|
|
||||||
fun performDH(otherPublicKey: ByteArray, sharedSecret: ByteArray)
|
|
||||||
fun destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cipher interface for symmetric encryption/decryption
|
|
||||||
*/
|
|
||||||
interface CipherState {
|
|
||||||
fun cipherName(): String
|
|
||||||
fun hasKey(): Boolean
|
|
||||||
fun keyLength(): Int
|
|
||||||
|
|
||||||
fun initializeKey(key: ByteArray, offset: Int)
|
|
||||||
fun encryptWithAd(
|
|
||||||
associatedData: ByteArray?,
|
|
||||||
plaintext: ByteArray,
|
|
||||||
plaintextOffset: Int,
|
|
||||||
ciphertext: ByteArray,
|
|
||||||
ciphertextOffset: Int,
|
|
||||||
length: Int
|
|
||||||
): Int
|
|
||||||
|
|
||||||
fun decryptWithAd(
|
|
||||||
associatedData: ByteArray?,
|
|
||||||
ciphertext: ByteArray,
|
|
||||||
ciphertextOffset: Int,
|
|
||||||
plaintext: ByteArray,
|
|
||||||
plaintextOffset: Int,
|
|
||||||
length: Int
|
|
||||||
): Int
|
|
||||||
|
|
||||||
fun fork(): CipherState
|
|
||||||
fun destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash interface for cryptographic hashing
|
|
||||||
*/
|
|
||||||
interface HashState {
|
|
||||||
fun hashName(): String
|
|
||||||
fun hashLength(): Int
|
|
||||||
fun blockLength(): Int
|
|
||||||
|
|
||||||
fun reset()
|
|
||||||
fun update(data: ByteArray, offset: Int, length: Int)
|
|
||||||
fun digest(output: ByteArray, offset: Int)
|
|
||||||
|
|
||||||
fun fork(): HashState
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Curve25519 implementation with PROPER key setting support
|
|
||||||
*/
|
|
||||||
class Curve25519DHState : DHState {
|
|
||||||
private var privateKeyBytes: ByteArray? = null
|
|
||||||
private var publicKeyBytes: ByteArray? = null
|
|
||||||
|
|
||||||
override fun dhName() = "25519"
|
|
||||||
override fun hasPrivateKey() = privateKeyBytes != null
|
|
||||||
override fun hasPublicKey() = publicKeyBytes != null
|
|
||||||
override fun privateKeyLength() = 32
|
|
||||||
override fun publicKeyLength() = 32
|
|
||||||
|
|
||||||
override fun generateKeyPair() {
|
|
||||||
val random = SecureRandom()
|
|
||||||
privateKeyBytes = ByteArray(32)
|
|
||||||
random.nextBytes(privateKeyBytes!!)
|
|
||||||
|
|
||||||
// Clamp private key for Curve25519
|
|
||||||
privateKeyBytes!![0] = (privateKeyBytes!![0].toInt() and 248).toByte()
|
|
||||||
privateKeyBytes!![31] = (privateKeyBytes!![31].toInt() and 127).toByte()
|
|
||||||
privateKeyBytes!![31] = (privateKeyBytes!![31].toInt() or 64).toByte()
|
|
||||||
|
|
||||||
// Calculate public key
|
|
||||||
publicKeyBytes = curve25519ScalarMult(privateKeyBytes!!, curve25519BasePoint())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setPrivateKey(privateKey: ByteArray, offset: Int) {
|
|
||||||
require(privateKey.size - offset >= 32) { "Private key must be 32 bytes" }
|
|
||||||
privateKeyBytes = privateKey.copyOfRange(offset, offset + 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setPublicKey(publicKey: ByteArray, offset: Int) {
|
|
||||||
require(publicKey.size - offset >= 32) { "Public key must be 32 bytes" }
|
|
||||||
publicKeyBytes = publicKey.copyOfRange(offset, offset + 32)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPrivateKey(privateKey: ByteArray, offset: Int) {
|
|
||||||
val key = privateKeyBytes ?: throw NoiseException("No private key available")
|
|
||||||
require(privateKey.size - offset >= 32) { "Output buffer too small" }
|
|
||||||
key.copyInto(privateKey, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPublicKey(publicKey: ByteArray, offset: Int) {
|
|
||||||
val key = publicKeyBytes ?: throw NoiseException("No public key available")
|
|
||||||
require(publicKey.size - offset >= 32) { "Output buffer too small" }
|
|
||||||
key.copyInto(publicKey, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun performDH(otherPublicKey: ByteArray, sharedSecret: ByteArray) {
|
|
||||||
val privateKey = privateKeyBytes ?: throw NoiseException("No private key available")
|
|
||||||
require(otherPublicKey.size >= 32) { "Other public key must be 32 bytes" }
|
|
||||||
require(sharedSecret.size >= 32) { "Shared secret buffer must be 32 bytes" }
|
|
||||||
|
|
||||||
val shared = curve25519ScalarMult(privateKey, otherPublicKey)
|
|
||||||
shared.copyInto(sharedSecret, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun destroy() {
|
|
||||||
privateKeyBytes?.fill(0)
|
|
||||||
publicKeyBytes?.fill(0)
|
|
||||||
privateKeyBytes = null
|
|
||||||
publicKeyBytes = null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Curve25519 scalar multiplication (simplified - in production use a proper crypto library)
|
|
||||||
private fun curve25519ScalarMult(scalar: ByteArray, point: ByteArray): ByteArray {
|
|
||||||
// This is a simplified implementation
|
|
||||||
// In production, use a proper Curve25519 implementation
|
|
||||||
// For testing purposes, we'll use a mock that maintains the interface
|
|
||||||
val result = ByteArray(32)
|
|
||||||
|
|
||||||
// Simple XOR-based mock (NOT CRYPTOGRAPHICALLY SECURE - FOR TESTING ONLY)
|
|
||||||
for (i in 0 until 32) {
|
|
||||||
result[i] = (scalar[i].toInt() xor point[i].toInt()).toByte()
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun curve25519BasePoint(): ByteArray {
|
|
||||||
val basePoint = ByteArray(32)
|
|
||||||
basePoint[0] = 9
|
|
||||||
return basePoint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ChaCha20-Poly1305 cipher implementation
|
|
||||||
*/
|
|
||||||
class ChaChaPoly1305CipherState : CipherState {
|
|
||||||
private var key: ByteArray? = null
|
|
||||||
private var nonce: Long = 0
|
|
||||||
|
|
||||||
override fun cipherName() = "ChaChaPoly"
|
|
||||||
override fun hasKey() = key != null
|
|
||||||
override fun keyLength() = 32
|
|
||||||
|
|
||||||
override fun initializeKey(key: ByteArray, offset: Int) {
|
|
||||||
require(key.size - offset >= 32) { "Key must be 32 bytes" }
|
|
||||||
this.key = key.copyOfRange(offset, offset + 32)
|
|
||||||
this.nonce = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun encryptWithAd(
|
|
||||||
associatedData: ByteArray?,
|
|
||||||
plaintext: ByteArray,
|
|
||||||
plaintextOffset: Int,
|
|
||||||
ciphertext: ByteArray,
|
|
||||||
ciphertextOffset: Int,
|
|
||||||
length: Int
|
|
||||||
): Int {
|
|
||||||
val key = this.key ?: throw NoiseException("Cipher not initialized")
|
|
||||||
|
|
||||||
// Create 12-byte nonce (4 bytes zero + 8 bytes counter)
|
|
||||||
val nonceBytes = ByteArray(12)
|
|
||||||
ByteBuffer.wrap(nonceBytes, 4, 8).order(ByteOrder.LITTLE_ENDIAN).putLong(nonce)
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Use ChaCha20 cipher
|
|
||||||
val chacha20 = Cipher.getInstance("ChaCha20")
|
|
||||||
val keySpec = SecretKeySpec(key, "ChaCha20")
|
|
||||||
val paramSpec = ChaCha20ParameterSpec(nonceBytes, 1)
|
|
||||||
chacha20.init(Cipher.ENCRYPT_MODE, keySpec, paramSpec)
|
|
||||||
|
|
||||||
// Encrypt
|
|
||||||
val encrypted = chacha20.doFinal(plaintext, plaintextOffset, length)
|
|
||||||
|
|
||||||
// Add Poly1305 MAC (simplified - compute HMAC as substitute)
|
|
||||||
val mac = Mac.getInstance("HmacSHA256")
|
|
||||||
val macKey = HmacKeySpec(key, "HmacSHA256")
|
|
||||||
mac.init(macKey)
|
|
||||||
|
|
||||||
if (associatedData != null) {
|
|
||||||
mac.update(associatedData)
|
|
||||||
}
|
|
||||||
mac.update(encrypted)
|
|
||||||
val tag = mac.doFinal().copyOf(16) // Truncate to 16 bytes
|
|
||||||
|
|
||||||
// Copy encrypted data and tag to output
|
|
||||||
encrypted.copyInto(ciphertext, ciphertextOffset)
|
|
||||||
tag.copyInto(ciphertext, ciphertextOffset + length)
|
|
||||||
|
|
||||||
nonce++
|
|
||||||
return length + 16 // plaintext length + 16 byte tag
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw NoiseException("Encryption failed", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun decryptWithAd(
|
|
||||||
associatedData: ByteArray?,
|
|
||||||
ciphertext: ByteArray,
|
|
||||||
ciphertextOffset: Int,
|
|
||||||
plaintext: ByteArray,
|
|
||||||
plaintextOffset: Int,
|
|
||||||
length: Int
|
|
||||||
): Int {
|
|
||||||
val key = this.key ?: throw NoiseException("Cipher not initialized")
|
|
||||||
|
|
||||||
require(length >= 16) { "Ciphertext too short for tag" }
|
|
||||||
|
|
||||||
val plaintextLength = length - 16
|
|
||||||
val encryptedData = ciphertext.copyOfRange(ciphertextOffset, ciphertextOffset + plaintextLength)
|
|
||||||
val receivedTag = ciphertext.copyOfRange(ciphertextOffset + plaintextLength, ciphertextOffset + length)
|
|
||||||
|
|
||||||
// Create 12-byte nonce (4 bytes zero + 8 bytes counter)
|
|
||||||
val nonceBytes = ByteArray(12)
|
|
||||||
ByteBuffer.wrap(nonceBytes, 4, 8).order(ByteOrder.LITTLE_ENDIAN).putLong(nonce)
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Verify MAC first
|
|
||||||
val mac = Mac.getInstance("HmacSHA256")
|
|
||||||
val macKey = HmacKeySpec(key, "HmacSHA256")
|
|
||||||
mac.init(macKey)
|
|
||||||
|
|
||||||
if (associatedData != null) {
|
|
||||||
mac.update(associatedData)
|
|
||||||
}
|
|
||||||
mac.update(encryptedData)
|
|
||||||
val expectedTag = mac.doFinal().copyOf(16)
|
|
||||||
|
|
||||||
if (!receivedTag.contentEquals(expectedTag)) {
|
|
||||||
throw NoiseException("Authentication failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt
|
|
||||||
val chacha20 = Cipher.getInstance("ChaCha20")
|
|
||||||
val keySpec = SecretKeySpec(key, "ChaCha20")
|
|
||||||
val paramSpec = ChaCha20ParameterSpec(nonceBytes, 1)
|
|
||||||
chacha20.init(Cipher.DECRYPT_MODE, keySpec, paramSpec)
|
|
||||||
|
|
||||||
val decrypted = chacha20.doFinal(encryptedData)
|
|
||||||
decrypted.copyInto(plaintext, plaintextOffset)
|
|
||||||
|
|
||||||
nonce++
|
|
||||||
return plaintextLength
|
|
||||||
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw NoiseException("Decryption failed", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun fork(): CipherState {
|
|
||||||
val forked = ChaChaPoly1305CipherState()
|
|
||||||
key?.let { forked.initializeKey(it, 0) }
|
|
||||||
return forked
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun destroy() {
|
|
||||||
key?.fill(0)
|
|
||||||
key = null
|
|
||||||
nonce = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHA-256 hash implementation
|
|
||||||
*/
|
|
||||||
class SHA256HashState : HashState {
|
|
||||||
private var messageDigest = java.security.MessageDigest.getInstance("SHA-256")
|
|
||||||
|
|
||||||
override fun hashName() = "SHA256"
|
|
||||||
override fun hashLength() = 32
|
|
||||||
override fun blockLength() = 64
|
|
||||||
|
|
||||||
override fun reset() {
|
|
||||||
messageDigest.reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun update(data: ByteArray, offset: Int, length: Int) {
|
|
||||||
messageDigest.update(data, offset, length)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun digest(output: ByteArray, offset: Int) {
|
|
||||||
val hash = messageDigest.digest()
|
|
||||||
hash.copyInto(output, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun fork(): HashState {
|
|
||||||
val forked = SHA256HashState()
|
|
||||||
// Note: Java MessageDigest doesn't support cloning directly
|
|
||||||
// For a complete implementation, we'd need to track state manually
|
|
||||||
return forked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory for creating crypto primitives
|
|
||||||
*/
|
|
||||||
object NoiseFactory {
|
|
||||||
fun createDH(name: String): DHState {
|
|
||||||
return when (name) {
|
|
||||||
"25519" -> Curve25519DHState()
|
|
||||||
else -> throw NoiseException("Unsupported DH algorithm: $name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createCipher(name: String): CipherState {
|
|
||||||
return when (name) {
|
|
||||||
"ChaChaPoly" -> ChaChaPoly1305CipherState()
|
|
||||||
else -> throw NoiseException("Unsupported cipher: $name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createHash(name: String): HashState {
|
|
||||||
return when (name) {
|
|
||||||
"SHA256" -> SHA256HashState()
|
|
||||||
else -> throw NoiseException("Unsupported hash: $name")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user