rearranges the crypto package into separate nips and reduces the amount of circular dependencies.
This commit is contained in:
@@ -20,35 +20,32 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.crypto.nip01.Nip01
|
||||
import com.vitorpamplona.quartz.crypto.nip04.Nip04
|
||||
import com.vitorpamplona.quartz.crypto.nip06.Nip06
|
||||
import com.vitorpamplona.quartz.crypto.nip44.Nip44
|
||||
import com.vitorpamplona.quartz.crypto.nip44.Nip44v2
|
||||
import com.vitorpamplona.quartz.crypto.nip49.Nip49
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.MessageDigest
|
||||
import java.security.SecureRandom
|
||||
import java.util.Base64
|
||||
|
||||
object CryptoUtils {
|
||||
private val secp256k1 = Secp256k1.get()
|
||||
private val random = SecureRandom()
|
||||
|
||||
private val nip04 = Nip04(secp256k1, random)
|
||||
private val nip44v1 = Nip44v1(secp256k1, random)
|
||||
private val nip44v2 = Nip44v2(secp256k1, random)
|
||||
private val nip49 = Nip49(secp256k1, random)
|
||||
public val nip01 = Nip01(secp256k1, random)
|
||||
public val nip06 = Nip06(secp256k1)
|
||||
public val nip04 = Nip04(secp256k1, random)
|
||||
public val nip44 = Nip44(secp256k1, random, nip04)
|
||||
public val nip49 = Nip49(secp256k1, random)
|
||||
|
||||
fun clearCache() {
|
||||
nip04.clearCache()
|
||||
nip44v1.clearCache()
|
||||
nip44v2.clearCache()
|
||||
nip44.clearCache()
|
||||
}
|
||||
|
||||
fun randomInt(bound: Int): Int {
|
||||
return random.nextInt(bound)
|
||||
}
|
||||
|
||||
/** Provides a 32B "private key" aka random number */
|
||||
fun privkeyCreate() = random(32)
|
||||
fun randomInt(bound: Int): Int = random.nextInt(bound)
|
||||
|
||||
fun random(size: Int): ByteArray {
|
||||
val bytes = ByteArray(size)
|
||||
@@ -56,39 +53,32 @@ object CryptoUtils {
|
||||
return bytes
|
||||
}
|
||||
|
||||
fun pubkeyCreateBitcoin(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey))
|
||||
/** Provides a 32B "private key" aka random number */
|
||||
fun privkeyCreate() = nip01.privkeyCreate()
|
||||
|
||||
fun pubkeyCreate(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey)).copyOfRange(1, 33)
|
||||
|
||||
fun isPrivKeyValid(il: ByteArray): Boolean {
|
||||
return secp256k1.secKeyVerify(il)
|
||||
}
|
||||
fun pubkeyCreate(privKey: ByteArray) = nip01.pubkeyCreate(privKey)
|
||||
|
||||
fun signString(
|
||||
message: String,
|
||||
privKey: ByteArray,
|
||||
auxrand32: ByteArray = random(32),
|
||||
): ByteArray {
|
||||
return sign(sha256(message.toByteArray()), privKey, auxrand32)
|
||||
}
|
||||
): ByteArray = nip01.signString(message, privKey, auxrand32)
|
||||
|
||||
fun sign(
|
||||
data: ByteArray,
|
||||
privKey: ByteArray,
|
||||
auxrand32: ByteArray? = null,
|
||||
): ByteArray = secp256k1.signSchnorr(data, privKey, auxrand32)
|
||||
): ByteArray = nip01.sign(data, privKey, auxrand32)
|
||||
|
||||
fun verifySignature(
|
||||
signature: ByteArray,
|
||||
hash: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): Boolean {
|
||||
return secp256k1.verifySchnorr(signature, hash, pubKey)
|
||||
}
|
||||
): Boolean = nip01.verify(signature, hash, pubKey)
|
||||
|
||||
fun sha256(data: ByteArray): ByteArray {
|
||||
// Creates a new buffer every time
|
||||
return MessageDigest.getInstance("SHA-256").digest(data)
|
||||
return nip01.sha256(data)
|
||||
}
|
||||
|
||||
/** NIP 04 Utils */
|
||||
@@ -96,189 +86,84 @@ object CryptoUtils {
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String {
|
||||
return nip04.encrypt(msg, privateKey, pubKey)
|
||||
}
|
||||
): String = nip04.encrypt(msg, privateKey, pubKey)
|
||||
|
||||
fun encryptNIP04(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): Nip04.EncryptedInfo {
|
||||
return nip04.encrypt(msg, sharedSecret)
|
||||
}
|
||||
): Nip04.EncryptedInfo = nip04.encrypt(msg, sharedSecret)
|
||||
|
||||
fun decryptNIP04(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String {
|
||||
return nip04.decrypt(msg, privateKey, pubKey)
|
||||
}
|
||||
): String = nip04.decrypt(msg, privateKey, pubKey)
|
||||
|
||||
fun decryptNIP04(
|
||||
encryptedInfo: Nip04.EncryptedInfo,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String {
|
||||
return nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
): String = nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
|
||||
fun decryptNIP04(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): String {
|
||||
return nip04.decrypt(msg, sharedSecret)
|
||||
}
|
||||
): String = nip04.decrypt(msg, sharedSecret)
|
||||
|
||||
private fun decryptNIP04(
|
||||
cipher: String,
|
||||
nonce: String,
|
||||
sharedSecret: ByteArray,
|
||||
): String {
|
||||
return nip04.decrypt(cipher, nonce, sharedSecret)
|
||||
}
|
||||
): String = nip04.decrypt(cipher, nonce, sharedSecret)
|
||||
|
||||
private fun decryptNIP04(
|
||||
encryptedMsg: ByteArray,
|
||||
iv: ByteArray,
|
||||
sharedSecret: ByteArray,
|
||||
): String {
|
||||
return nip04.decrypt(encryptedMsg, iv, sharedSecret)
|
||||
}
|
||||
): String = nip04.decrypt(encryptedMsg, iv, sharedSecret)
|
||||
|
||||
fun getSharedSecretNIP04(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray {
|
||||
return nip04.getSharedSecret(privateKey, pubKey)
|
||||
}
|
||||
): ByteArray = nip04.getSharedSecret(privateKey, pubKey)
|
||||
|
||||
fun computeSharedSecretNIP04(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray {
|
||||
return nip04.computeSharedSecret(privateKey, pubKey)
|
||||
}
|
||||
): ByteArray = nip04.computeSharedSecret(privateKey, pubKey)
|
||||
|
||||
/** NIP 44v1 Utils */
|
||||
fun encryptNIP44v1(
|
||||
/** NIP 06 Utils */
|
||||
fun isValidMnemonic(mnemonic: String): Boolean = nip06.isValidMnemonic(mnemonic)
|
||||
|
||||
fun privateKeyFromMnemonic(
|
||||
mnemonic: String,
|
||||
account: Long = 0,
|
||||
) = nip06.privateKeyFromMnemonic(mnemonic, account)
|
||||
|
||||
/** NIP 44 Utils */
|
||||
fun getSharedSecretNIP44(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray = nip44.getSharedSecret(privateKey, pubKey)
|
||||
|
||||
fun computeSharedSecretNIP44(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray = nip44.computeSharedSecret(privateKey, pubKey)
|
||||
|
||||
fun encryptNIP44(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): Nip44v1.EncryptedInfo {
|
||||
return nip44v1.encrypt(msg, privateKey, pubKey)
|
||||
}
|
||||
|
||||
fun encryptNIP44v1(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): Nip44v1.EncryptedInfo {
|
||||
return nip44v1.encrypt(msg, sharedSecret)
|
||||
}
|
||||
|
||||
fun decryptNIP44v1(
|
||||
encryptedInfo: Nip44v1.EncryptedInfo,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
return nip44v1.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
|
||||
fun decryptNIP44v1(
|
||||
encryptedInfo: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
return nip44v1.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
|
||||
fun decryptNIP44v1(
|
||||
encryptedInfo: Nip44v1.EncryptedInfo,
|
||||
sharedSecret: ByteArray,
|
||||
): String? {
|
||||
return nip44v1.decrypt(encryptedInfo, sharedSecret)
|
||||
}
|
||||
|
||||
fun getSharedSecretNIP44v1(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray {
|
||||
return nip44v1.getSharedSecret(privateKey, pubKey)
|
||||
}
|
||||
|
||||
fun computeSharedSecretNIP44v1(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray {
|
||||
return nip44v1.computeSharedSecret(privateKey, pubKey)
|
||||
}
|
||||
|
||||
/** NIP 44v2 Utils */
|
||||
fun encryptNIP44v2(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): Nip44v2.EncryptedInfo {
|
||||
return nip44v2.encrypt(msg, privateKey, pubKey)
|
||||
}
|
||||
|
||||
fun encryptNIP44v2(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): Nip44v2.EncryptedInfo {
|
||||
return nip44v2.encrypt(msg, sharedSecret)
|
||||
}
|
||||
|
||||
fun decryptNIP44v2(
|
||||
encryptedInfo: Nip44v2.EncryptedInfo,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
return nip44v2.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
|
||||
fun decryptNIP44v2(
|
||||
encryptedInfo: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
return nip44v2.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
|
||||
fun decryptNIP44v2(
|
||||
encryptedInfo: Nip44v2.EncryptedInfo,
|
||||
sharedSecret: ByteArray,
|
||||
): String? {
|
||||
return nip44v2.decrypt(encryptedInfo, sharedSecret)
|
||||
}
|
||||
|
||||
fun getSharedSecretNIP44v2(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray {
|
||||
return nip44v2.getConversationKey(privateKey, pubKey)
|
||||
}
|
||||
|
||||
fun computeSharedSecretNIP44v2(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray {
|
||||
return nip44v2.computeConversationKey(privateKey, pubKey)
|
||||
}
|
||||
): Nip44v2.EncryptedInfo = nip44.encrypt(msg, privateKey, pubKey)
|
||||
|
||||
fun decryptNIP44(
|
||||
payload: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
if (payload.isEmpty()) return null
|
||||
return if (payload[0] == '{') {
|
||||
decryptNIP44FromJackson(payload, privateKey, pubKey)
|
||||
} else {
|
||||
decryptNIP44FromBase64(payload, privateKey, pubKey)
|
||||
}
|
||||
}
|
||||
): String? = nip44.decrypt(payload, privateKey, pubKey)
|
||||
|
||||
/** NIP 49 Utils */
|
||||
fun decryptNIP49(
|
||||
payload: String,
|
||||
password: String,
|
||||
@@ -290,87 +175,5 @@ object CryptoUtils {
|
||||
fun encryptNIP49(
|
||||
key: HexKey,
|
||||
password: String,
|
||||
): String? {
|
||||
return nip49.encrypt(key, password, 16, Nip49.EncryptedInfo.CLIENT_DOES_NOT_TRACK)
|
||||
}
|
||||
|
||||
class EncryptedInfoString(
|
||||
val ciphertext: String,
|
||||
val nonce: String,
|
||||
val v: Int,
|
||||
val mac: String?,
|
||||
)
|
||||
|
||||
fun decryptNIP44FromJackson(
|
||||
json: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
return try {
|
||||
val info = Event.mapper.readValue(json, EncryptedInfoString::class.java)
|
||||
|
||||
when (info.v) {
|
||||
Nip04.EncryptedInfo.V -> {
|
||||
val encryptedInfo =
|
||||
Nip04.EncryptedInfo(
|
||||
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||
nonce = Base64.getDecoder().decode(info.nonce),
|
||||
)
|
||||
decryptNIP04(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
Nip44v1.EncryptedInfo.V -> {
|
||||
val encryptedInfo =
|
||||
Nip44v1.EncryptedInfo(
|
||||
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||
nonce = Base64.getDecoder().decode(info.nonce),
|
||||
)
|
||||
decryptNIP44v1(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
Nip44v2.EncryptedInfo.V -> {
|
||||
val encryptedInfo =
|
||||
Nip44v2.EncryptedInfo(
|
||||
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||
nonce = Base64.getDecoder().decode(info.nonce),
|
||||
mac = Base64.getDecoder().decode(info.mac),
|
||||
)
|
||||
decryptNIP44v2(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $json")
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun decryptNIP44FromBase64(
|
||||
payload: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
if (payload.isEmpty()) return null
|
||||
|
||||
return try {
|
||||
val byteArray = Base64.getDecoder().decode(payload)
|
||||
|
||||
when (byteArray[0].toInt()) {
|
||||
Nip04.EncryptedInfo.V -> decryptNIP04(payload, privateKey, pubKey)
|
||||
Nip44v1.EncryptedInfo.V -> decryptNIP44v1(payload, privateKey, pubKey)
|
||||
Nip44v2.EncryptedInfo.V -> decryptNIP44v2(payload, privateKey, pubKey)
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $payload")
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun sum(
|
||||
first: ByteArray,
|
||||
second: ByteArray,
|
||||
): ByteArray {
|
||||
return secp256k1.privKeyTweakAdd(first, second)
|
||||
}
|
||||
): String = nip49.encrypt(key, password)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto.nip01
|
||||
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils.random
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.MessageDigest
|
||||
import java.security.SecureRandom
|
||||
|
||||
class Nip01(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
/** Provides a 32B "private key" aka random number */
|
||||
fun privkeyCreate() = random(32)
|
||||
|
||||
fun pubkeyCreate(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey)).copyOfRange(1, 33)
|
||||
|
||||
fun sign(
|
||||
data: ByteArray,
|
||||
privKey: ByteArray,
|
||||
auxrand32: ByteArray? = null,
|
||||
): ByteArray = secp256k1.signSchnorr(data, privKey, auxrand32)
|
||||
|
||||
fun verify(
|
||||
signature: ByteArray,
|
||||
hash: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): Boolean = secp256k1.verifySchnorr(signature, hash, pubKey)
|
||||
|
||||
fun sha256(data: ByteArray): ByteArray {
|
||||
// Creates a new buffer every time
|
||||
return MessageDigest.getInstance("SHA-256").digest(data)
|
||||
}
|
||||
|
||||
fun signString(
|
||||
message: String,
|
||||
privKey: ByteArray,
|
||||
auxrand32: ByteArray = random(32),
|
||||
): ByteArray = sign(CryptoUtils.sha256(message.toByteArray()), privKey, auxrand32)
|
||||
}
|
||||
+13
-11
@@ -18,9 +18,11 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto
|
||||
package com.vitorpamplona.quartz.crypto.nip04
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.crypto.SharedKeyCache
|
||||
import com.vitorpamplona.quartz.crypto.nip44.Nip44v1
|
||||
import com.vitorpamplona.quartz.encoders.Hex
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.SecureRandom
|
||||
@@ -29,7 +31,10 @@ import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
class Nip04(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
private val sharedKeyCache = SharedKeyCache()
|
||||
private val h02 = Hex.decode("02")
|
||||
|
||||
@@ -41,9 +46,7 @@ class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String {
|
||||
return encrypt(msg, getSharedSecret(privateKey, pubKey)).encodeToNIP04()
|
||||
}
|
||||
): String = encrypt(msg, getSharedSecret(privateKey, pubKey)).encodeToNIP04()
|
||||
|
||||
fun encrypt(
|
||||
msg: String,
|
||||
@@ -146,8 +149,8 @@ class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
}
|
||||
}
|
||||
|
||||
fun decodeFromNIP04(payload: String): EncryptedInfo? {
|
||||
return try {
|
||||
fun decodeFromNIP04(payload: String): EncryptedInfo? =
|
||||
try {
|
||||
val parts = payload.split("?iv=")
|
||||
EncryptedInfo(
|
||||
ciphertext = Base64.getDecoder().decode(parts[0]),
|
||||
@@ -157,15 +160,14 @@ class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
Log.w("NIP04", "Unable to Parse encrypted payload: $payload")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun encodePayload(): String {
|
||||
return Base64.getEncoder()
|
||||
fun encodePayload(): String =
|
||||
Base64
|
||||
.getEncoder()
|
||||
.encodeToString(
|
||||
byteArrayOf(V.toByte()) + nonce + ciphertext,
|
||||
)
|
||||
}
|
||||
|
||||
fun encodeToNIP04(): String {
|
||||
val nonce = Base64.getEncoder().encodeToString(nonce)
|
||||
@@ -20,14 +20,16 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto.nip06
|
||||
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
/*
|
||||
Simplified from: https://github.com/ACINQ/bitcoin-kmp/
|
||||
*/
|
||||
object Bip32SeedDerivation {
|
||||
class Bip32SeedDerivation(
|
||||
val secp256k1: Secp256k1,
|
||||
) {
|
||||
class ExtendedPrivateKey(
|
||||
val secretkeybytes: ByteArray,
|
||||
val chaincode: ByteArray,
|
||||
@@ -53,6 +55,15 @@ object Bip32SeedDerivation {
|
||||
return mac.doFinal(data)
|
||||
}
|
||||
|
||||
fun isPrivKeyValid(il: ByteArray): Boolean = secp256k1.secKeyVerify(il)
|
||||
|
||||
fun pubkeyCreateBitcoin(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey))
|
||||
|
||||
fun sum(
|
||||
first: ByteArray,
|
||||
second: ByteArray,
|
||||
): ByteArray = secp256k1.privKeyTweakAdd(first, second)
|
||||
|
||||
fun derivePrivateKey(
|
||||
parent: ExtendedPrivateKey,
|
||||
index: Long,
|
||||
@@ -62,17 +73,17 @@ object Bip32SeedDerivation {
|
||||
val data = arrayOf(0.toByte()).toByteArray() + parent.secretkeybytes + writeInt32BE(index.toInt())
|
||||
hmac512(parent.chaincode, data)
|
||||
} else {
|
||||
val data = CryptoUtils.pubkeyCreateBitcoin(parent.secretkeybytes) + writeInt32BE(index.toInt())
|
||||
val data = pubkeyCreateBitcoin(parent.secretkeybytes) + writeInt32BE(index.toInt())
|
||||
hmac512(parent.chaincode, data)
|
||||
}
|
||||
val il = i.take(32).toByteArray()
|
||||
val ir = i.takeLast(32).toByteArray()
|
||||
|
||||
require(CryptoUtils.isPrivKeyValid(il)) { "cannot generate child private key: IL is invalid" }
|
||||
require(isPrivKeyValid(il)) { "cannot generate child private key: IL is invalid" }
|
||||
|
||||
val key = CryptoUtils.sum(il, parent.secretkeybytes)
|
||||
val key = sum(il, parent.secretkeybytes)
|
||||
|
||||
require(CryptoUtils.isPrivKeyValid(key)) { "cannot generate child private key: resulting private key is invalid" }
|
||||
require(isPrivKeyValid(key)) { "cannot generate child private key: resulting private key is invalid" }
|
||||
|
||||
return ExtendedPrivateKey(key, ir)
|
||||
}
|
||||
@@ -94,7 +105,7 @@ object Bip32SeedDerivation {
|
||||
fun derivePrivateKey(
|
||||
parent: ExtendedPrivateKey,
|
||||
chain: List<Long>,
|
||||
): ExtendedPrivateKey = chain.fold(parent, Bip32SeedDerivation::derivePrivateKey)
|
||||
): ExtendedPrivateKey = chain.fold(parent, this::derivePrivateKey)
|
||||
|
||||
fun derivePrivateKey(
|
||||
parent: ExtendedPrivateKey,
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto.nip06
|
||||
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import com.vitorpamplona.quartz.crypto.PBKDF
|
||||
import com.vitorpamplona.quartz.crypto.nip49.PBKDF
|
||||
import java.security.MessageDigest
|
||||
|
||||
// CODE FROM: https://github.com/ACINQ/bitcoin-kmp/
|
||||
|
||||
@@ -37,6 +37,11 @@ object Bip39Mnemonics {
|
||||
return zeroes + digits
|
||||
}
|
||||
|
||||
fun sha256(data: ByteArray): ByteArray {
|
||||
// Creates a new buffer every time
|
||||
return MessageDigest.getInstance("SHA-256").digest(data)
|
||||
}
|
||||
|
||||
private fun toBinary(x: ByteArray): List<Boolean> = x.map(Bip39Mnemonics::toBinary).flatten()
|
||||
|
||||
private fun fromBinary(bin: List<Boolean>): Int = bin.fold(0) { acc, flag -> if (flag) 2 * acc + 1 else 2 * acc }
|
||||
@@ -45,13 +50,12 @@ object Bip39Mnemonics {
|
||||
items: List<Boolean>,
|
||||
size: Int,
|
||||
acc: List<List<Boolean>> = emptyList(),
|
||||
): List<List<Boolean>> {
|
||||
return when {
|
||||
): List<List<Boolean>> =
|
||||
when {
|
||||
items.isEmpty() -> acc
|
||||
items.size < size -> acc + listOf(items)
|
||||
else -> group(items.drop(size), size, acc + listOf(items.take(size)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mnemonics list of mnemonic words
|
||||
@@ -80,20 +84,19 @@ object Bip39Mnemonics {
|
||||
val databits = bits.subList(0, bitlength)
|
||||
val checksumbits = bits.subList(bitlength, bits.size)
|
||||
val data = group(databits, 8).map { fromBinary(it) }.map { it.toByte() }.toByteArray()
|
||||
val check = toBinary(CryptoUtils.sha256(data)).take(data.size / 4)
|
||||
val check = toBinary(sha256(data)).take(data.size / 4)
|
||||
require(check == checksumbits) { "invalid checksum" }
|
||||
}
|
||||
|
||||
fun validate(mnemonics: String): Unit = validate(mnemonics.split(" "))
|
||||
|
||||
fun isValid(mnemonics: String): Boolean {
|
||||
return try {
|
||||
fun isValid(mnemonics: String): Boolean =
|
||||
try {
|
||||
validate(mnemonics)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BIP39 entropy encoding
|
||||
@@ -108,7 +111,7 @@ object Bip39Mnemonics {
|
||||
wordlist: Array<String>,
|
||||
): List<String> {
|
||||
require(wordlist.size == 2048) { "invalid word list (size should be 2048)" }
|
||||
val digits = toBinary(entropy) + toBinary(CryptoUtils.sha256(entropy)).take(entropy.size / 4)
|
||||
val digits = toBinary(entropy) + toBinary(sha256(entropy)).take(entropy.size / 4)
|
||||
|
||||
return group(digits, 11).map(Bip39Mnemonics::fromBinary).map { wordlist[it] }
|
||||
}
|
||||
|
||||
@@ -20,29 +20,31 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto.nip06
|
||||
|
||||
class Nip06 {
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
|
||||
class Nip06(
|
||||
val secp256k1: Secp256k1,
|
||||
) {
|
||||
val derivation = Bip32SeedDerivation(secp256k1)
|
||||
|
||||
// m/44'/1237'/<account>'/0/0
|
||||
private val nip6Base: KeyPath =
|
||||
KeyPath("")
|
||||
.derive(Hardener.hardened(44L))
|
||||
.derive(Hardener.hardened(1237L))
|
||||
|
||||
private fun nip6Path(account: Long): KeyPath {
|
||||
return nip6Base.derive(Hardener.hardened(account))
|
||||
private fun nip6Path(account: Long): KeyPath =
|
||||
nip6Base
|
||||
.derive(Hardener.hardened(account))
|
||||
.derive(0L)
|
||||
.derive(0L)
|
||||
}
|
||||
|
||||
fun isValidMnemonic(mnemonic: String): Boolean {
|
||||
return Bip39Mnemonics.isValid(mnemonic)
|
||||
}
|
||||
fun isValidMnemonic(mnemonic: String): Boolean = Bip39Mnemonics.isValid(mnemonic)
|
||||
|
||||
fun privateKeyFromSeed(
|
||||
seed: ByteArray,
|
||||
account: Long = 0,
|
||||
): ByteArray {
|
||||
return Bip32SeedDerivation.derivePrivateKey(Bip32SeedDerivation.generate(seed), nip6Path(account))
|
||||
}
|
||||
): ByteArray = derivation.derivePrivateKey(derivation.generate(seed), nip6Path(account))
|
||||
|
||||
fun privateKeyFromMnemonic(
|
||||
mnemonic: String,
|
||||
|
||||
+5
-2
@@ -18,13 +18,16 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto
|
||||
package com.vitorpamplona.quartz.crypto.nip44
|
||||
|
||||
import java.nio.ByteBuffer
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class Hkdf(val algorithm: String = "HmacSHA256", val hashLen: Int = 32) {
|
||||
class Hkdf(
|
||||
val algorithm: String = "HmacSHA256",
|
||||
val hashLen: Int = 32,
|
||||
) {
|
||||
fun extract(
|
||||
key: ByteArray,
|
||||
salt: ByteArray,
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto.nip44
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.crypto.nip04.Nip04
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.SecureRandom
|
||||
import java.util.Base64
|
||||
|
||||
class Nip44(
|
||||
secp256k1: Secp256k1,
|
||||
random: SecureRandom,
|
||||
val nip04: Nip04,
|
||||
) {
|
||||
public val v1 = Nip44v1(secp256k1, random)
|
||||
public val v2 = Nip44v2(secp256k1, random)
|
||||
|
||||
fun clearCache() {
|
||||
v1.clearCache()
|
||||
v2.clearCache()
|
||||
}
|
||||
|
||||
/** NIP 44v2 Utils */
|
||||
fun getSharedSecret(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray = v2.getConversationKey(privateKey, pubKey)
|
||||
|
||||
fun computeSharedSecret(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray = v2.computeConversationKey(privateKey, pubKey)
|
||||
|
||||
fun encrypt(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): Nip44v2.EncryptedInfo {
|
||||
// current version should be used.
|
||||
return v2.encrypt(msg, privateKey, pubKey)
|
||||
}
|
||||
|
||||
// Always decrypt from any version/any encoding
|
||||
fun decrypt(
|
||||
payload: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
if (payload.isEmpty()) return null
|
||||
return if (payload[0] == '{') {
|
||||
decryptNIP44FromJackson(payload, privateKey, pubKey)
|
||||
} else {
|
||||
decryptNIP44FromBase64(payload, privateKey, pubKey)
|
||||
}
|
||||
}
|
||||
|
||||
class EncryptedInfoString(
|
||||
val ciphertext: String,
|
||||
val nonce: String,
|
||||
val v: Int,
|
||||
val mac: String?,
|
||||
)
|
||||
|
||||
fun decryptNIP44FromJackson(
|
||||
json: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? =
|
||||
try {
|
||||
val info = Event.mapper.readValue(json, EncryptedInfoString::class.java)
|
||||
|
||||
when (info.v) {
|
||||
Nip04.EncryptedInfo.V -> {
|
||||
val encryptedInfo =
|
||||
Nip04.EncryptedInfo(
|
||||
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||
nonce = Base64.getDecoder().decode(info.nonce),
|
||||
)
|
||||
nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
Nip44v1.EncryptedInfo.V -> {
|
||||
val encryptedInfo =
|
||||
Nip44v1.EncryptedInfo(
|
||||
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||
nonce = Base64.getDecoder().decode(info.nonce),
|
||||
)
|
||||
v1.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
Nip44v2.EncryptedInfo.V -> {
|
||||
val encryptedInfo =
|
||||
Nip44v2.EncryptedInfo(
|
||||
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||
nonce = Base64.getDecoder().decode(info.nonce),
|
||||
mac = Base64.getDecoder().decode(info.mac),
|
||||
)
|
||||
v2.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $json")
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
|
||||
fun decryptNIP44FromBase64(
|
||||
payload: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
if (payload.isEmpty()) return null
|
||||
|
||||
return try {
|
||||
val byteArray = Base64.getDecoder().decode(payload)
|
||||
|
||||
when (byteArray[0].toInt()) {
|
||||
Nip04.EncryptedInfo.V -> nip04.decrypt(payload, privateKey, pubKey)
|
||||
Nip44v1.EncryptedInfo.V -> v1.decrypt(payload, privateKey, pubKey)
|
||||
Nip44v2.EncryptedInfo.V -> v2.decrypt(payload, privateKey, pubKey)
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $payload")
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-10
@@ -18,18 +18,22 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto
|
||||
package com.vitorpamplona.quartz.crypto.nip44
|
||||
|
||||
import android.util.Log
|
||||
import com.goterl.lazysodium.SodiumAndroid
|
||||
import com.goterl.lazysodium.utils.Key
|
||||
import com.vitorpamplona.quartz.crypto.SharedKeyCache
|
||||
import com.vitorpamplona.quartz.encoders.Hex
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.MessageDigest
|
||||
import java.security.SecureRandom
|
||||
import java.util.Base64
|
||||
|
||||
class Nip44v1(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
class Nip44v1(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
private val sharedKeyCache = SharedKeyCache()
|
||||
private val h02 = Hex.decode("02")
|
||||
private val libSodium = SodiumAndroid()
|
||||
@@ -97,15 +101,13 @@ class Nip44v1(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
fun decrypt(
|
||||
encryptedInfo: EncryptedInfo,
|
||||
sharedSecret: ByteArray,
|
||||
): String? {
|
||||
return cryptoStreamXChaCha20Xor(
|
||||
): String? =
|
||||
cryptoStreamXChaCha20Xor(
|
||||
libSodium = libSodium,
|
||||
messageBytes = encryptedInfo.ciphertext,
|
||||
nonce = encryptedInfo.nonce,
|
||||
key = Key.fromBytes(sharedSecret),
|
||||
)
|
||||
?.decodeToString()
|
||||
}
|
||||
)?.decodeToString()
|
||||
|
||||
fun getSharedSecret(
|
||||
privateKey: ByteArray,
|
||||
@@ -155,11 +157,11 @@ class Nip44v1(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
}
|
||||
}
|
||||
|
||||
fun encodePayload(): String {
|
||||
return Base64.getEncoder()
|
||||
fun encodePayload(): String =
|
||||
Base64
|
||||
.getEncoder()
|
||||
.encodeToString(
|
||||
byteArrayOf(V.toByte()) + nonce + ciphertext,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-18
@@ -18,11 +18,12 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto
|
||||
package com.vitorpamplona.quartz.crypto.nip44
|
||||
|
||||
import android.util.Log
|
||||
import com.goterl.lazysodium.LazySodiumAndroid
|
||||
import com.goterl.lazysodium.SodiumAndroid
|
||||
import com.vitorpamplona.quartz.crypto.SharedKeyCache
|
||||
import com.vitorpamplona.quartz.encoders.Hex
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
@@ -33,7 +34,10 @@ import java.util.Base64
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.log2
|
||||
|
||||
class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
class Nip44v2(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
private val sharedKeyCache = SharedKeyCache()
|
||||
|
||||
private val libSodium = SodiumAndroid()
|
||||
@@ -55,9 +59,7 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): EncryptedInfo {
|
||||
return encrypt(msg, getConversationKey(privateKey, pubKey))
|
||||
}
|
||||
): EncryptedInfo = encrypt(msg, getConversationKey(privateKey, pubKey))
|
||||
|
||||
fun encrypt(
|
||||
plaintext: String,
|
||||
@@ -99,17 +101,13 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
payload: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
return decrypt(payload, getConversationKey(privateKey, pubKey))
|
||||
}
|
||||
): String? = decrypt(payload, getConversationKey(privateKey, pubKey))
|
||||
|
||||
fun decrypt(
|
||||
decoded: EncryptedInfo,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? {
|
||||
return decrypt(decoded, getConversationKey(privateKey, pubKey))
|
||||
}
|
||||
): String? = decrypt(decoded, getConversationKey(privateKey, pubKey))
|
||||
|
||||
fun decrypt(
|
||||
payload: String,
|
||||
@@ -173,7 +171,11 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
check(unpaddedLen <= maxPlaintextSize) { "Message is too long ($unpaddedLen): $plaintext" }
|
||||
|
||||
val prefix =
|
||||
ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putShort(unpaddedLen.toShort()).array()
|
||||
ByteBuffer
|
||||
.allocate(2)
|
||||
.order(ByteOrder.BIG_ENDIAN)
|
||||
.putShort(unpaddedLen.toShort())
|
||||
.array()
|
||||
val suffix = ByteArray(calcPaddedLen(unpaddedLen) - unpaddedLen)
|
||||
return ByteBuffer.wrap(prefix + unpadded + suffix).array()
|
||||
}
|
||||
@@ -182,13 +184,12 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
byte1: Byte,
|
||||
byte2: Byte,
|
||||
bigEndian: Boolean,
|
||||
): Int {
|
||||
return if (bigEndian) {
|
||||
): Int =
|
||||
if (bigEndian) {
|
||||
(byte1.toInt() and 0xFF shl 8 or (byte2.toInt() and 0xFF))
|
||||
} else {
|
||||
(byte2.toInt() and 0xFF shl 8 or (byte1.toInt() and 0xFF))
|
||||
}
|
||||
}
|
||||
|
||||
fun unpad(padded: ByteArray): String {
|
||||
val unpaddedLen: Int = bytesToInt(padded[0], padded[1], true)
|
||||
@@ -273,11 +274,11 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
}
|
||||
}
|
||||
|
||||
fun encodePayload(): String {
|
||||
return Base64.getEncoder()
|
||||
fun encodePayload(): String =
|
||||
Base64
|
||||
.getEncoder()
|
||||
.encodeToString(
|
||||
byteArrayOf(V.toByte()) + nonce + ciphertext + mac,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto
|
||||
package com.vitorpamplona.quartz.crypto.nip44
|
||||
|
||||
import com.goterl.lazysodium.SodiumAndroid
|
||||
import com.goterl.lazysodium.utils.Key
|
||||
@@ -66,9 +66,7 @@ fun cryptoStreamXchacha20Xor(
|
||||
messageLen: Long,
|
||||
nonce: ByteArray,
|
||||
key: ByteArray,
|
||||
): Int {
|
||||
return cryptoStreamXchacha20XorIc(libSodium, cipher, message, messageLen, nonce, 0, key)
|
||||
}
|
||||
): Int = cryptoStreamXchacha20XorIc(libSodium, cipher, message, messageLen, nonce, 0, key)
|
||||
|
||||
fun cryptoStreamXChaCha20Xor(
|
||||
libSodium: SodiumAndroid,
|
||||
+17
-16
@@ -18,7 +18,7 @@
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.crypto
|
||||
package com.vitorpamplona.quartz.crypto.nip49
|
||||
|
||||
import android.util.Log
|
||||
import com.goterl.lazysodium.LazySodiumAndroid
|
||||
@@ -32,16 +32,17 @@ import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.SecureRandom
|
||||
import java.text.Normalizer
|
||||
|
||||
class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
class Nip49(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
private val libSodium = SodiumAndroid()
|
||||
private val lazySodium = LazySodiumAndroid(libSodium)
|
||||
|
||||
fun decrypt(
|
||||
nCryptSec: String,
|
||||
password: String,
|
||||
): HexKey {
|
||||
return decrypt(EncryptedInfo.decodePayload(nCryptSec), password)
|
||||
}
|
||||
): HexKey = decrypt(EncryptedInfo.decodePayload(nCryptSec), password)
|
||||
|
||||
fun decrypt(
|
||||
encryptedInfo: EncryptedInfo?,
|
||||
@@ -75,11 +76,9 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
fun encrypt(
|
||||
secretKeyHex: String,
|
||||
password: String,
|
||||
logn: Int,
|
||||
ksb: Byte,
|
||||
): String? {
|
||||
return encrypt(secretKeyHex.hexToByteArray(), password, logn, ksb)
|
||||
}
|
||||
logn: Int = 16,
|
||||
ksb: Byte = EncryptedInfo.CLIENT_DOES_NOT_TRACK,
|
||||
): String = encrypt(secretKeyHex.hexToByteArray(), password, logn, ksb)
|
||||
|
||||
fun encrypt(
|
||||
secretKey: ByteArray,
|
||||
@@ -104,9 +103,12 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
// byte[] ad, long adLen,
|
||||
// byte[] nSec, byte[] nPub, byte[] k
|
||||
lazySodium.cryptoAeadXChaCha20Poly1305IetfEncrypt(
|
||||
ciphertext, longArrayOf(48),
|
||||
secretKey, secretKey.size.toLong(),
|
||||
byteArrayOf(ksb), 1,
|
||||
ciphertext,
|
||||
longArrayOf(48),
|
||||
secretKey,
|
||||
secretKey.size.toLong(),
|
||||
byteArrayOf(ksb),
|
||||
1,
|
||||
key,
|
||||
nonce,
|
||||
key,
|
||||
@@ -163,8 +165,8 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
}
|
||||
|
||||
// ln(n.toDouble()).toInt().toByte(),
|
||||
fun encodePayload(): String {
|
||||
return Bech32.encodeBytes(
|
||||
fun encodePayload(): String =
|
||||
Bech32.encodeBytes(
|
||||
hrp = "ncryptsec",
|
||||
byteArrayOf(
|
||||
version,
|
||||
@@ -172,6 +174,5 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) {
|
||||
) + salt + nonce + keySecurity + encryptedKey,
|
||||
Bech32.Encoding.Bech32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.vitorpamplona.quartz.crypto;
|
||||
package com.vitorpamplona.quartz.crypto.nip49;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
||||
|
||||
package com.vitorpamplona.quartz.crypto;
|
||||
package com.vitorpamplona.quartz.crypto.nip49;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import java.security.GeneralSecurityException;
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.vitorpamplona.quartz.crypto;
|
||||
package com.vitorpamplona.quartz.crypto.nip49;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.spec.KeySpec;
|
||||
@@ -31,7 +31,9 @@ import com.vitorpamplona.quartz.events.EventFactory
|
||||
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
|
||||
import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
||||
|
||||
class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toHexKey()) {
|
||||
class NostrSignerInternal(
|
||||
val keyPair: KeyPair,
|
||||
) : NostrSigner(keyPair.pubKey.toHexKey()) {
|
||||
override fun <T : Event> sign(
|
||||
createdAt: Long,
|
||||
kind: Int,
|
||||
@@ -52,10 +54,9 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH
|
||||
fun isUnsignedPrivateEvent(
|
||||
kind: Int,
|
||||
tags: Array<Array<String>>,
|
||||
): Boolean {
|
||||
return kind == LnZapRequestEvent.KIND &&
|
||||
): Boolean =
|
||||
kind == LnZapRequestEvent.KIND &&
|
||||
tags.any { t -> t.size > 1 && t[0] == "anon" && t[1].isBlank() }
|
||||
}
|
||||
|
||||
fun <T : Event> signNormal(
|
||||
createdAt: Long,
|
||||
@@ -123,12 +124,12 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH
|
||||
if (keyPair.privKey == null) return
|
||||
|
||||
onReady(
|
||||
CryptoUtils.encryptNIP44v2(
|
||||
decryptedContent,
|
||||
keyPair.privKey,
|
||||
toPublicKey.hexToByteArray(),
|
||||
)
|
||||
.encodePayload(),
|
||||
CryptoUtils
|
||||
.encryptNIP44(
|
||||
decryptedContent,
|
||||
keyPair.privKey,
|
||||
toPublicKey.hexToByteArray(),
|
||||
).encodePayload(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -139,12 +140,12 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH
|
||||
) {
|
||||
if (keyPair.privKey == null) return
|
||||
|
||||
CryptoUtils.decryptNIP44(
|
||||
payload = encryptedContent,
|
||||
privateKey = keyPair.privKey,
|
||||
pubKey = fromPublicKey.hexToByteArray(),
|
||||
)
|
||||
?.let { onReady(it) }
|
||||
CryptoUtils
|
||||
.decryptNIP44(
|
||||
payload = encryptedContent,
|
||||
privateKey = keyPair.privKey,
|
||||
pubKey = fromPublicKey.hexToByteArray(),
|
||||
)?.let { onReady(it) }
|
||||
}
|
||||
|
||||
private fun <T> signPrivateZap(
|
||||
|
||||
Reference in New Issue
Block a user