diff --git a/app/src/main/assets/pq-core-mldsa.sig b/app/src/main/assets/pq-core-mldsa.sig deleted file mode 100644 index 2511d9b..0000000 Binary files a/app/src/main/assets/pq-core-mldsa.sig and /dev/null differ diff --git a/app/src/main/assets/pq-core-sphincs.sig b/app/src/main/assets/pq-core-sphincs.sig deleted file mode 100644 index 1531759..0000000 Binary files a/app/src/main/assets/pq-core-sphincs.sig and /dev/null differ diff --git a/app/src/main/java/org/vextaproject/wallet/VextaTransactionSender.kt b/app/src/main/java/org/vextaproject/wallet/VextaTransactionSender.kt index 24edd58..5c6f857 100644 --- a/app/src/main/java/org/vextaproject/wallet/VextaTransactionSender.kt +++ b/app/src/main/java/org/vextaproject/wallet/VextaTransactionSender.kt @@ -548,76 +548,6 @@ object VextaTransactionSender { return doubleSha256(preimage) } - fun runPqrSignatureHashSelfTest(): Pair { - val selected = listOf( - BlockScanner.SpendableUtxo( - txid = - "0000000000000000000000000000000000000000000000000000000000000001", - outputIndex = 3L, - value = 987654L, - height = 1, - addressIndex = 0, - isCoinbase = false, - addressType = BlockScanner.AddressType.MLDSA - ) - ) - - val outputs = listOf( - TransactionOutputData( - value = 123456L, - script = byteArrayOf(0x51) - ) - ) - - val program = - ByteArray(32) { index -> - index.toByte() - } - - val v2 = calculatePqrSignatureHash( - selected = selected, - outputs = outputs, - inputIndex = 0, - witnessVersion = 2, - witnessProgram = program, - amount = 987654L, - version = 2, - lockTime = 500L, - inputSequence = 0xfffffffeL - ) - - val v3 = calculatePqrSignatureHash( - selected = selected, - outputs = outputs, - inputIndex = 0, - witnessVersion = 3, - witnessProgram = program, - amount = 987654L, - version = 2, - lockTime = 500L, - inputSequence = 0xfffffffeL - ) - - val v2CoreHex = v2.reversedArray().toHex() - val v3CoreHex = v3.reversedArray().toHex() - - check( - v2CoreHex == - "ba39c447b4577db974473a1e7ed2182686672f2900045a40aee6b4dc5811995b" - ) { - "PQR V2 signature hash self-test failed: $v2CoreHex" - } - - check( - v3CoreHex == - "dab3188530f1094ef87087451da77bfba77dfc6e63d1730bb9d0475cebe76618" - ) { - "PQR V3 signature hash self-test failed: $v3CoreHex" - } - - return v2CoreHex to v3CoreHex - } - private fun createPqWitness( signature: ByteArray, publicKey: ByteArray @@ -636,290 +566,6 @@ object VextaTransactionSender { } } - fun runPqWitnessSelfTest(): List { - val seed = - ByteArray(64) { index -> - index.toByte() - } - - val mldsa = - requireNotNull( - VextaPQ.mldsaKeypairFromSeed(seed) - ) - - val sphincs = - requireNotNull( - VextaPQ.sphincsKeypairFromSeed(seed) - ) - - val message = - "Vexta PQ witness self-test" - .toByteArray(Charsets.UTF_8) - - val mldsaSignature = - requireNotNull( - VextaPQ.mldsaSign( - message, - mldsa[1] - ) - ) - - val sphincsSignature = - requireNotNull( - VextaPQ.sphincsSign( - message, - sphincs[1] - ) - ) - - val mldsaWitness = - createPqWitness( - mldsaSignature, - mldsa[0] - ) - - val sphincsWitness = - createPqWitness( - sphincsSignature, - sphincs[0] - ) - - check(mldsaWitness.pushCount == 2) - check(mldsaWitness.getPush(0).size == 3309) - check(mldsaWitness.getPush(1).size == 1952) - - check(sphincsWitness.pushCount == 2) - check(sphincsWitness.getPush(0).size == 7856) - check(sphincsWitness.getPush(1).size == 32) - - mldsa[1].fill(0) - sphincs[1].fill(0) - - return listOf( - "MLDSA_WITNESS_PUSHES=${mldsaWitness.pushCount}", - "MLDSA_SIG_BYTES=${mldsaWitness.getPush(0).size}", - "MLDSA_PUBKEY_BYTES=${mldsaWitness.getPush(1).size}", - "SLHDSA_WITNESS_PUSHES=${sphincsWitness.pushCount}", - "SLHDSA_SIG_BYTES=${sphincsWitness.getPush(0).size}", - "SLHDSA_PUBKEY_BYTES=${sphincsWitness.getPush(1).size}", - "PQ_WITNESS_RESULT=PASS" - ) - } - - fun runPqSignedTransactionSelfTest(): List { - val recipientAddress = - "vtx1z6engqms3emse5lycna6yt7gd6svu7nfd28dccr7mfs8s54pz8rysd6lu5u" - - val recipientScript = - addressToScript(recipientAddress) - - val changePubKeyHash = - ByteArray(20) { index -> - (0x40 + index).toByte() - } - - val changeScript = - byteArrayOf(0x00, 0x14) + changePubKeyHash - - val amountSatoshis = 100_000L - val utxoValue = 10_000_000L - - fun testType( - addressType: BlockScanner.AddressType, - witnessVersion: Int, - expectedSignatureBytes: Int, - expectedPublicKeyBytes: Int, - txidSuffix: String - ): List { - val seed = - ByteArray(64) { index -> - index.toByte() - } - - val keyPair = - when (addressType) { - BlockScanner.AddressType.MLDSA -> - requireNotNull( - VextaPQ.mldsaKeypairFromSeed(seed) - ) - - BlockScanner.AddressType.SLHDSA -> - requireNotNull( - VextaPQ.sphincsKeypairFromSeed(seed) - ) - - BlockScanner.AddressType.STANDARD -> - error("Standard is not a PQ self-test type") - } - - require(keyPair.size == 2) - - val publicKey = keyPair[0] - val secretKey = keyPair[1] - - val syntheticTxid = - txidSuffix.padStart(64, '0') - - val utxo = - BlockScanner.SpendableUtxo( - txid = syntheticTxid, - outputIndex = 0L, - value = utxoValue, - height = 1, - addressIndex = 0, - isCoinbase = false, - addressType = addressType - ) - - val pqSigningKeys = - mapOf( - (addressType to 0) to - PqSigningKey( - addressType = addressType, - publicKey = publicKey, - secretKey = secretKey - ) - ) - - val created = - createAndSign( - spendableUtxos = listOf(utxo), - recipientAddress = recipientAddress, - amountSatoshis = amountSatoshis, - privateKeysByIndex = emptyMap(), - pqSigningKeys = pqSigningKeys, - changePubKeyHash = changePubKeyHash, - chainHeight = 10_000 - ) - - val parsed = - Transaction( - MainNetParams.get(), - created.rawTransaction - ) - - check(parsed.inputs.size == 1) - - val witness = - parsed.getInput(0L).getWitness() - - check(witness.pushCount == 2) - - val signature = - witness.getPush(0) - - val parsedPublicKey = - witness.getPush(1) - - check(signature.size == expectedSignatureBytes) - check(parsedPublicKey.size == expectedPublicKeyBytes) - check(parsedPublicKey.contentEquals(publicKey)) - - check( - parsed.txId.toString() == - created.txid - ) - - val witnessProgram = - MessageDigest - .getInstance("SHA-256") - .digest(publicKey) - - val outputs = - mutableListOf( - TransactionOutputData( - value = created.amount, - script = recipientScript - ) - ) - - if (created.change > 0L) { - outputs.add( - TransactionOutputData( - value = created.change, - script = changeScript - ) - ) - } - - val signatureHash = - calculatePqrSignatureHash( - selected = listOf(utxo), - outputs = outputs, - inputIndex = 0, - witnessVersion = witnessVersion, - witnessProgram = witnessProgram, - amount = utxo.value, - version = 2, - lockTime = 0L, - inputSequence = sequence - ) - - val verified = - when (addressType) { - BlockScanner.AddressType.MLDSA -> - VextaPQ.mldsaVerify( - signature, - signatureHash, - publicKey - ) - - BlockScanner.AddressType.SLHDSA -> - VextaPQ.sphincsVerify( - signature, - signatureHash, - publicKey - ) - - BlockScanner.AddressType.STANDARD -> - false - } - - check(verified) - - seed.fill(0) - signatureHash.fill(0) - - return listOf( - "${addressType.name}_SIGNED_TX_BYTES=" + - created.rawTransaction.size, - "${addressType.name}_SIGNED_TX_WITNESS_PUSHES=" + - witness.pushCount, - "${addressType.name}_SIGNED_TX_SIG_BYTES=" + - signature.size, - "${addressType.name}_SIGNED_TX_PUBKEY_BYTES=" + - parsedPublicKey.size, - "${addressType.name}_SIGNED_TX_VERIFY=$verified", - "${addressType.name}_SIGNED_TX_TXID_MATCH=true" - ) - } - - val results = - mutableListOf() - - results += - testType( - addressType = BlockScanner.AddressType.MLDSA, - witnessVersion = 2, - expectedSignatureBytes = 3309, - expectedPublicKeyBytes = 1952, - txidSuffix = "01" - ) - - results += - testType( - addressType = BlockScanner.AddressType.SLHDSA, - witnessVersion = 3, - expectedSignatureBytes = 7856, - expectedPublicKeyBytes = 32, - txidSuffix = "02" - ) - - results += "PQ_SIGNED_TX_RESULT=PASS" - - return results - } - private fun serializeTransaction( selected: List, outputs: List, diff --git a/app/src/main/java/org/vextaproject/wallet/WalletActivity.kt b/app/src/main/java/org/vextaproject/wallet/WalletActivity.kt index 254622a..97e0ad1 100644 --- a/app/src/main/java/org/vextaproject/wallet/WalletActivity.kt +++ b/app/src/main/java/org/vextaproject/wallet/WalletActivity.kt @@ -352,13 +352,6 @@ class WalletActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if ( - applicationInfo.flags and - android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE != 0 - ) { - runPqCompatibilitySelfTest() - } - onBackPressedDispatcher.addCallback( this, object : OnBackPressedCallback(true) { @@ -445,504 +438,6 @@ class WalletActivity : FragmentActivity() { } } - private fun runPqCompatibilitySelfTest() { - Thread { - try { - val seed = ByteArray(64) { index -> - index.toByte() - } - - val mldsa = - requireNotNull( - VextaPQ.mldsaKeypairFromSeed(seed) - ) - - val sphincs = - requireNotNull( - VextaPQ.sphincsKeypairFromSeed(seed) - ) - - require(mldsa.size == 2) - require(sphincs.size == 2) - - fun sha256Hex(data: ByteArray): String = - MessageDigest - .getInstance("SHA-256") - .digest(data) - .joinToString("") { byte -> - "%02x".format(byte.toInt() and 0xff) - } - - val results = listOf( - "MLDSA_PK" to sha256Hex(mldsa[0]), - "MLDSA_SK" to sha256Hex(mldsa[1]), - "SPHINCS_PK" to sha256Hex(sphincs[0]), - "SPHINCS_SK" to sha256Hex(sphincs[1]) - ) - - results.forEach { (name, hash) -> - android.util.Log.i( - "VextaPQTest", - "$name=$hash" - ) - } - - val passed = - results[0].second == - "d666806e11cee19a7c989f7445f90dd419cf4d2d51db8c0fdb4c0f0a542238c9" && - results[1].second == - "9f1e24f47795fe50040384e3d6183988047170fa2d866406b70fe0a3f8216063" && - results[2].second == - "9d5b69aa74c4939468bae5108568a4d0926f2532c96b0742eda0a7eff4c99c3e" && - results[3].second == - "c6222658caa895a591af74d9f8c85d160962d253a782edf95875bbb3acee0a07" - - android.util.Log.i( - "VextaPQTest", - if (passed) { - "KEYGEN_RESULT=PASS" - } else { - "KEYGEN_RESULT=FAIL" - } - ) - - - val mldsaKeyId = - MessageDigest - .getInstance("SHA-256") - .digest(mldsa[0]) - - val sphincsKeyId = - MessageDigest - .getInstance("SHA-256") - .digest(sphincs[0]) - - val mldsaAddress = - encodeSegwitAddress( - "vtx", - 2, - mldsaKeyId - ) - - val sphincsAddress = - encodeSegwitAddress( - "vtx", - 3, - sphincsKeyId - ) - - android.util.Log.i( - "VextaPQTest", - "MLDSA_ADDRESS=$mldsaAddress" - ) - - android.util.Log.i( - "VextaPQTest", - "SLHDSA_ADDRESS=$sphincsAddress" - ) - - val addressPassed = - mldsaAddress == - "vtx1z6engqms3emse5lycna6yt7gd6svu7nfd28dccr7mfs8s54pz8rysd6lu5u" && - sphincsAddress == - "vtx1rn4dkn2n5cjfeg696u5gg269y6zfx7ffje94swshd5zn7laxfnslqfc63ay" - - android.util.Log.i( - "VextaPQTest", - "ADDRESS_RESULT=" + - if (addressPassed) "PASS" - else "FAIL" - ) - - fun derivePqSeedMaterial( - walletSeed: ByteArray, - algorithm: Int, - counter: Int - ): ByteArray { - val domain = - "Vexta-PQ-HD-v1" - .toByteArray(Charsets.US_ASCII) - - val data = ByteArray( - domain.size + 1 + 4 - ) - - System.arraycopy( - domain, - 0, - data, - 0, - domain.size - ) - - var offset = domain.size - - data[offset++] = - algorithm.toByte() - - data[offset++] = - ((counter ushr 24) and 0xff).toByte() - data[offset++] = - ((counter ushr 16) and 0xff).toByte() - data[offset++] = - ((counter ushr 8) and 0xff).toByte() - data[offset] = - (counter and 0xff).toByte() - - val mac = - javax.crypto.Mac.getInstance( - "HmacSHA512" - ) - - mac.init( - javax.crypto.spec.SecretKeySpec( - walletSeed, - "HmacSHA512" - ) - ) - - return mac.doFinal(data) - } - - val pqHdWalletSeed = - ByteArray(32) { index -> - index.toByte() - } - - val pqHdVectors = listOf( - "MLDSA_COUNTER_0" to - derivePqSeedMaterial( - pqHdWalletSeed, - 2, - 0 - ), - "MLDSA_COUNTER_1" to - derivePqSeedMaterial( - pqHdWalletSeed, - 2, - 1 - ), - "SLHDSA_COUNTER_0" to - derivePqSeedMaterial( - pqHdWalletSeed, - 3, - 0 - ), - "SLHDSA_COUNTER_1" to - derivePqSeedMaterial( - pqHdWalletSeed, - 3, - 1 - ) - ) - - pqHdVectors.forEach { (name, value) -> - android.util.Log.i( - "VextaPQTest", - "$name=${value.joinToString("") { - "%02x".format( - it.toInt() and 0xff - ) - }}" - ) - } - - val pqHdPassed = - pqHdVectors[0].second.contentEquals( - hexToBytes( - "0b5d81161cee1dd59cba10551b48069b2d38deabaec25ce2553777f2ee1574d8a9b5dd1cc72b84f5a6f97a943a060484b048f7ae06433ef73519c9c47136a8e6" - ) - ) && - pqHdVectors[1].second.contentEquals( - hexToBytes( - "96afee1e425cdc5cef6f2a5e565669e059f77538d432b00598c2ce4dae777de58a9ca26ea8c8f63d54a3b9b25554c9db4f4b2f3ff8a83954db44f94c46c7919b" - ) - ) && - pqHdVectors[2].second.contentEquals( - hexToBytes( - "62789e270793f5ca638ceba587a63c8931e1fa8f6ae300d79219b56351d71899905391fd9739652b11db87176ecba328cbac551b6b685961afe9c35346ec0d34" - ) - ) && - pqHdVectors[3].second.contentEquals( - hexToBytes( - "8e445ae4bb057ce26190c032115adeebbda64f6768904dd56147f4832981fb762e7d683b639c2bff6034c93223f71d3ca3d12bad887cb139d3c62da1f8487fa2" - ) - ) - - android.util.Log.i( - "VextaPQTest", - "PQ_HD_RESULT=" + - if (pqHdPassed) "PASS" - else "FAIL" - ) - - val message = - "Vexta PQ Android signature test" - .toByteArray(Charsets.UTF_8) - - val mldsaSignature = - requireNotNull( - VextaPQ.mldsaSign( - message, - mldsa[1] - ) - ) - - val mldsaVerified = - VextaPQ.mldsaVerify( - mldsaSignature, - message, - mldsa[0] - ) - - android.util.Log.i( - "VextaPQTest", - "MLDSA_SIG_BYTES=${mldsaSignature.size}" - ) - - android.util.Log.i( - "VextaPQTest", - "MLDSA_VERIFY=$mldsaVerified" - ) - - val sphincsSignature = - requireNotNull( - VextaPQ.sphincsSign( - message, - sphincs[1] - ) - ) - - val sphincsVerified = - VextaPQ.sphincsVerify( - sphincsSignature, - message, - sphincs[0] - ) - - android.util.Log.i( - "VextaPQTest", - "SPHINCS_SIG_BYTES=${sphincsSignature.size}" - ) - - android.util.Log.i( - "VextaPQTest", - "SPHINCS_VERIFY=$sphincsVerified" - ) - - java.io.File( - filesDir, - "pq-test-message.bin" - ).writeBytes(message) - - java.io.File( - filesDir, - "pq-mldsa.sig" - ).writeBytes(mldsaSignature) - - java.io.File( - filesDir, - "pq-sphincs.sig" - ).writeBytes(sphincsSignature) - - android.util.Log.i( - "VextaPQTest", - "SIGNATURE_FILES_WRITTEN=true" - ) - - val signatureTestPassed = - passed && - mldsaSignature.size == 3309 && - mldsaVerified && - sphincsSignature.size == 7856 && - sphincsVerified - - val coreMldsaSignature = - assets.open("pq-core-mldsa.sig").use { - it.readBytes() - } - - val coreSphincsSignature = - assets.open("pq-core-sphincs.sig").use { - it.readBytes() - } - - val coreMldsaVerified = - VextaPQ.mldsaVerify( - coreMldsaSignature, - message, - mldsa[0] - ) - - val coreSphincsVerified = - VextaPQ.sphincsVerify( - coreSphincsSignature, - message, - sphincs[0] - ) - - android.util.Log.i( - "VextaPQTest", - "CORE_TO_ANDROID_MLDSA=$coreMldsaVerified" - ) - - android.util.Log.i( - "VextaPQTest", - "CORE_TO_ANDROID_SPHINCS=$coreSphincsVerified" - ) - - val (pqrV2, pqrV3) = - VextaTransactionSender - .runPqrSignatureHashSelfTest() - - android.util.Log.i( - "VextaPQTest", - "PQR_V2=$pqrV2" - ) - - android.util.Log.i( - "VextaPQTest", - "PQR_V3=$pqrV3" - ) - - val pqrPassed = - pqrV2 == - "ba39c447b4577db974473a1e7ed2182686672f2900045a40aee6b4dc5811995b" && - pqrV3 == - "dab3188530f1094ef87087451da77bfba77dfc6e63d1730bb9d0475cebe76618" - - android.util.Log.i( - "VextaPQTest", - "PQR_HASH_RESULT=" + - if (pqrPassed) "PASS" - else "FAIL" - ) - - val pqWitnessResults = - VextaTransactionSender - .runPqWitnessSelfTest() - - pqWitnessResults.forEach { result -> - android.util.Log.i( - "VextaPQTest", - result - ) - } - - val pqWitnessPassed = - pqWitnessResults.contains( - "PQ_WITNESS_RESULT=PASS" - ) - - val pqSignedTxResults = - VextaTransactionSender - .runPqSignedTransactionSelfTest() - - pqSignedTxResults.forEach { result -> - android.util.Log.i( - "VextaPQTest", - result - ) - } - - val pqSignedTxPassed = - pqSignedTxResults.contains( - "PQ_SIGNED_TX_RESULT=PASS" - ) - - val finalPassed = - signatureTestPassed && - coreMldsaVerified && - coreSphincsVerified && - pqrPassed && - pqWitnessPassed && - pqSignedTxPassed - - android.util.Log.i( - "VextaPQTest", - if (finalPassed) { - "RESULT=PASS" - } else { - "RESULT=FAIL" - } - ) - } catch (error: Throwable) { - android.util.Log.e( - "VextaPQTest", - "RESULT=ERROR", - error - ) - } - }.start() - } - - override fun onResume() { - super.onResume() - - if ( - Build.VERSION.SDK_INT < Build.VERSION_CODES.O || - packageManager.canRequestPackageInstalls() - ) { - if (pendingUpdateUri != null) { - openDownloadedUpdate() - } - } - - if ( - walletExists() && - mainWalletVisible && - !blockchainScanRunning - ) { - window.decorView.postDelayed({ - if ( - !isFinishing && - !isDestroyed && - mainWalletVisible && - !blockchainScanRunning - ) { - startBackgroundHeaderSyncAndScan() - } - }, 400L) - } - } - - @Deprecated("Deprecated in Java") - override fun onBackPressed() { - if (!walletExists()) { - super.onBackPressed() - return - } - - if (!mainWalletVisible) { - showMainWallet() - return - } - - val now = System.currentTimeMillis() - - if (now - lastExitSwipeAt <= 2_000L) { - finishAndRemoveTask() - } else { - lastExitSwipeAt = now - Toast.makeText( - this, - "Swipe again to exit", - Toast.LENGTH_SHORT - ).show() - } - } - - override fun onDestroy() { - refreshHandler.removeCallbacks(refreshRunnable) - - try { - unregisterReceiver(updateReceiver) - } catch (_: IllegalArgumentException) { - } - - super.onDestroy() - } - private fun createPaymentNotificationChannel() { val manager = getSystemService(