Initial commit: rosetta-android-prime

This commit is contained in:
k1ngsterr1
2026-01-08 19:06:37 +05:00
commit 42ddfe5b18
54 changed files with 68604 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
package com.rosetta.messenger.crypto
import org.bitcoinj.crypto.MnemonicCode
import org.bitcoinj.crypto.MnemonicException
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.ECPrivateKeySpec
import org.bouncycastle.jce.spec.ECPublicKeySpec
import java.math.BigInteger
import java.security.*
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec
import android.util.Base64
import java.security.spec.PKCS8EncodedKeySpec
import java.io.ByteArrayOutputStream
import java.util.zip.Deflater
import java.util.zip.Inflater
/**
* Cryptography module for Rosetta Messenger
* Implements BIP39 seed phrase generation and secp256k1 key derivation
*/
object CryptoManager {
private const val PBKDF2_ITERATIONS = 1000
private const val KEY_SIZE = 256
private const val SALT = "rosetta"
init {
// Add BouncyCastle provider for secp256k1 support
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(BouncyCastleProvider())
}
}
/**
* Generate a new 12-word BIP39 seed phrase
*/
fun generateSeedPhrase(): List<String> {
val secureRandom = SecureRandom()
val entropy = ByteArray(16) // 128 bits = 12 words
secureRandom.nextBytes(entropy)
val mnemonicCode = MnemonicCode.INSTANCE
return mnemonicCode.toMnemonic(entropy)
}
/**
* Validate a seed phrase
*/
fun validateSeedPhrase(words: List<String>): Boolean {
return try {
val mnemonicCode = MnemonicCode.INSTANCE
mnemonicCode.check(words)
true
} catch (e: MnemonicException) {
false
}
}
/**
* Convert seed phrase to private key (64 bytes hex string)
*/
fun seedPhraseToPrivateKey(seedPhrase: List<String>): String {
val mnemonicCode = MnemonicCode.INSTANCE
val seed = MnemonicCode.toSeed(seedPhrase, "")
// Convert to hex string (128 characters for 64 bytes)
return seed.joinToString("") { "%02x".format(it) }
}
/**
* Generate key pair from private key using secp256k1 curve
*/
fun generateKeyPairFromSeed(privateKeyHex: String): KeyPairData {
val ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1")
// Use first 32 bytes of private key for secp256k1
val privateKeyBytes = privateKeyHex.take(64).chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
val privateKeyBigInt = BigInteger(1, privateKeyBytes)
// Generate public key from private key
val publicKeyPoint = ecSpec.g.multiply(privateKeyBigInt)
val publicKeyHex = publicKeyPoint.getEncoded(false)
.joinToString("") { "%02x".format(it) }
return KeyPairData(
privateKey = privateKeyHex.take(64),
publicKey = publicKeyHex
)
}
/**
* Generate private key hash for protocol (SHA256(privateKey + "rosetta"))
*/
fun generatePrivateKeyHash(privateKey: String): String {
val data = (privateKey + SALT).toByteArray()
val digest = MessageDigest.getInstance("SHA-256")
val hash = digest.digest(data)
return hash.joinToString("") { "%02x".format(it) }
}
/**
* Encrypt data with password using PBKDF2 + AES
*/
fun encryptWithPassword(password: String, data: String): String {
// Compress data
val compressed = compress(data.toByteArray())
// Derive key using PBKDF2
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val spec = PBEKeySpec(password.toCharArray(), SALT.toByteArray(), PBKDF2_ITERATIONS, KEY_SIZE)
val secretKey = factory.generateSecret(spec)
val key = SecretKeySpec(secretKey.encoded, "AES")
// Generate random IV
val iv = ByteArray(16)
SecureRandom().nextBytes(iv)
val ivSpec = IvParameterSpec(iv)
// Encrypt with AES
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec)
val encrypted = cipher.doFinal(compressed)
// Return iv:ciphertext in Base64
val ivBase64 = Base64.encodeToString(iv, Base64.NO_WRAP)
val ctBase64 = Base64.encodeToString(encrypted, Base64.NO_WRAP)
return "$ivBase64:$ctBase64"
}
/**
* Decrypt data with password
*/
fun decryptWithPassword(password: String, encryptedData: String): String? {
return try {
val parts = encryptedData.split(":")
if (parts.size != 2) return null
val iv = Base64.decode(parts[0], Base64.NO_WRAP)
val ciphertext = Base64.decode(parts[1], Base64.NO_WRAP)
// Derive key using PBKDF2
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val spec = PBEKeySpec(password.toCharArray(), SALT.toByteArray(), PBKDF2_ITERATIONS, KEY_SIZE)
val secretKey = factory.generateSecret(spec)
val key = SecretKeySpec(secretKey.encoded, "AES")
// Decrypt
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv))
val decrypted = cipher.doFinal(ciphertext)
// Decompress
String(decompress(decrypted))
} catch (e: Exception) {
null
}
}
private fun compress(data: ByteArray): ByteArray {
val deflater = Deflater()
deflater.setInput(data)
deflater.finish()
val outputStream = ByteArrayOutputStream()
val buffer = ByteArray(1024)
while (!deflater.finished()) {
val count = deflater.deflate(buffer)
outputStream.write(buffer, 0, count)
}
outputStream.close()
return outputStream.toByteArray()
}
private fun decompress(data: ByteArray): ByteArray {
val inflater = Inflater()
inflater.setInput(data)
val outputStream = ByteArrayOutputStream()
val buffer = ByteArray(1024)
while (!inflater.finished()) {
val count = inflater.inflate(buffer)
outputStream.write(buffer, 0, count)
}
outputStream.close()
return outputStream.toByteArray()
}
}
data class KeyPairData(
val privateKey: String,
val publicKey: String
)