mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 07:25:20 +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