Merge branch 'main' into amber
This commit is contained in:
@@ -17,7 +17,7 @@ import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
object CryptoUtils {
|
||||
private val sharedKeyCache04 = LruCache<Int, ByteArray>(200)
|
||||
private val sharedKeyCache24 = LruCache<Int, ByteArray>(200)
|
||||
private val sharedKeyCache44 = LruCache<Int, ByteArray>(200)
|
||||
|
||||
private val secp256k1 = Secp256k1.get()
|
||||
private val libSodium = SodiumAndroid()
|
||||
@@ -26,7 +26,7 @@ object CryptoUtils {
|
||||
|
||||
fun clearCache() {
|
||||
sharedKeyCache04.evictAll()
|
||||
sharedKeyCache24.evictAll()
|
||||
sharedKeyCache44.evictAll()
|
||||
}
|
||||
|
||||
fun randomInt(bound: Int): Int {
|
||||
@@ -115,12 +115,12 @@ object CryptoUtils {
|
||||
return String(cipher.doFinal(encryptedMsg))
|
||||
}
|
||||
|
||||
fun encryptNIP24(msg: String, privateKey: ByteArray, pubKey: ByteArray): EncryptedInfo {
|
||||
val sharedSecret = getSharedSecretNIP24(privateKey, pubKey)
|
||||
return encryptNIP24(msg, sharedSecret)
|
||||
fun encryptNIP44(msg: String, privateKey: ByteArray, pubKey: ByteArray): EncryptedInfo {
|
||||
val sharedSecret = getSharedSecretNIP44(privateKey, pubKey)
|
||||
return encryptNIP44(msg, sharedSecret)
|
||||
}
|
||||
|
||||
fun encryptNIP24(msg: String, sharedSecret: ByteArray): EncryptedInfo {
|
||||
fun encryptNIP44(msg: String, sharedSecret: ByteArray): EncryptedInfo {
|
||||
val nonce = ByteArray(24)
|
||||
random.nextBytes(nonce)
|
||||
|
||||
@@ -134,16 +134,16 @@ object CryptoUtils {
|
||||
return EncryptedInfo(
|
||||
ciphertext = cipher ?: ByteArray(0),
|
||||
nonce = nonce,
|
||||
v = Nip44Version.NIP24.versionCode
|
||||
v = Nip44Version.NIP44.versionCode
|
||||
)
|
||||
}
|
||||
|
||||
fun decryptNIP24(encryptedInfo: EncryptedInfo, privateKey: ByteArray, pubKey: ByteArray): String? {
|
||||
val sharedSecret = getSharedSecretNIP24(privateKey, pubKey)
|
||||
return decryptNIP24(encryptedInfo, sharedSecret)
|
||||
fun decryptNIP44(encryptedInfo: EncryptedInfo, privateKey: ByteArray, pubKey: ByteArray): String? {
|
||||
val sharedSecret = getSharedSecretNIP44(privateKey, pubKey)
|
||||
return decryptNIP44(encryptedInfo, sharedSecret)
|
||||
}
|
||||
|
||||
fun decryptNIP24(encryptedInfo: EncryptedInfo, sharedSecret: ByteArray): String? {
|
||||
fun decryptNIP44(encryptedInfo: EncryptedInfo, sharedSecret: ByteArray): String? {
|
||||
return cryptoStreamXChaCha20Xor(
|
||||
libSodium = libSodium,
|
||||
messageBytes = encryptedInfo.ciphertext,
|
||||
@@ -174,20 +174,20 @@ object CryptoUtils {
|
||||
/**
|
||||
* @return 32B shared secret
|
||||
*/
|
||||
fun getSharedSecretNIP24(privateKey: ByteArray, pubKey: ByteArray): ByteArray {
|
||||
fun getSharedSecretNIP44(privateKey: ByteArray, pubKey: ByteArray): ByteArray {
|
||||
val hash = combinedHashCode(privateKey, pubKey)
|
||||
val preComputed = sharedKeyCache24[hash]
|
||||
val preComputed = sharedKeyCache44[hash]
|
||||
if (preComputed != null) return preComputed
|
||||
|
||||
val computed = computeSharedSecretNIP24(privateKey, pubKey)
|
||||
sharedKeyCache24.put(hash, computed)
|
||||
val computed = computeSharedSecretNIP44(privateKey, pubKey)
|
||||
sharedKeyCache44.put(hash, computed)
|
||||
return computed
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 32B shared secret
|
||||
*/
|
||||
fun computeSharedSecretNIP24(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
|
||||
fun computeSharedSecretNIP44(privateKey: ByteArray, pubKey: ByteArray): ByteArray =
|
||||
sha256(secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33))
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ data class EncryptedInfoString(val ciphertext: String, val nonce: String, val v:
|
||||
|
||||
enum class Nip44Version(val versionCode: Int) {
|
||||
NIP04(0),
|
||||
NIP24(1)
|
||||
NIP44(1)
|
||||
}
|
||||
|
||||
|
||||
@@ -234,10 +234,10 @@ fun decodeByteArray(base64: String): EncryptedInfo? {
|
||||
fun encodeJackson(info: EncryptedInfo): String {
|
||||
return Event.mapper.writeValueAsString(
|
||||
EncryptedInfoString(
|
||||
v = info.v,
|
||||
nonce = Base64.getEncoder().encodeToString(info.nonce),
|
||||
ciphertext = Base64.getEncoder().encodeToString(info.ciphertext)
|
||||
)
|
||||
v = info.v,
|
||||
nonce = Base64.getEncoder().encodeToString(info.nonce),
|
||||
ciphertext = Base64.getEncoder().encodeToString(info.ciphertext)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,8 @@ class AdvertisedRelayListEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
override fun dTag() = fixedDTag
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
fun relays(): List<AdvertisedRelayInfo> {
|
||||
return tags.mapNotNull {
|
||||
|
||||
@@ -14,10 +14,7 @@ class AppDefinitionEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun appMetaData() = try {
|
||||
mapper.readValue(
|
||||
ByteArrayInputStream(content.toByteArray(Charsets.UTF_8)),
|
||||
|
||||
@@ -12,16 +12,11 @@ class AppRecommendationEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun recommendations() = tags.filter { it.size > 1 && it[0] == "a" }.mapNotNull {
|
||||
ATag.parse(it[1], it.getOrNull(2))
|
||||
}
|
||||
|
||||
fun forKind() = runCatching { dTag().toInt() }.getOrNull()
|
||||
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
companion object {
|
||||
const val kind = 31989
|
||||
}
|
||||
|
||||
@@ -15,10 +15,7 @@ class AudioTrackEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
fun participants() = tags.filter { it.size > 1 && it[0] == "p" }.map { Participant(it[1], it.getOrNull(2)) }
|
||||
fun type() = tags.firstOrNull { it.size > 1 && it[0] == TYPE }?.get(1)
|
||||
|
||||
@@ -12,10 +12,7 @@ class BadgeDefinitionEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun name() = tags.firstOrNull { it.size > 1 && it[0] == "name" }?.get(1)
|
||||
fun thumb() = tags.firstOrNull { it.size > 1 && it[0] == "thumb" }?.get(1)
|
||||
fun image() = tags.firstOrNull { it.size > 1 && it[0] == "image" }?.get(1)
|
||||
|
||||
@@ -12,7 +12,7 @@ class BadgeProfilesEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun badgeAwardEvents() = tags.filter { it.firstOrNull() == "e" }.mapNotNull { it.getOrNull(1) }
|
||||
fun badgeAwardDefinitions() = tags.filter { it.firstOrNull() == "a" }.mapNotNull {
|
||||
val aTagValue = it.getOrNull(1)
|
||||
@@ -21,9 +21,6 @@ class BadgeProfilesEvent(
|
||||
if (aTagValue != null) ATag.parse(aTagValue, relay) else null
|
||||
}
|
||||
|
||||
override fun dTag() = tags.filter { it.firstOrNull() == "d" }.mapNotNull { it.getOrNull(1) }.firstOrNull() ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
companion object {
|
||||
const val kind = 30008
|
||||
const val standardDTAg = "profile_badges"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
|
||||
@Immutable
|
||||
class CalendarDateSlotEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
fun location() = tags.firstOrNull { it.size > 1 && it[0] == "location" }?.get(1)
|
||||
fun start() = tags.firstOrNull { it.size > 1 && it[0] == "start" }?.get(1)
|
||||
fun end() = tags.firstOrNull { it.size > 1 && it[0] == "end" }?.get(1)
|
||||
|
||||
// ["start", "<YYYY-MM-DD>"],
|
||||
// ["end", "<YYYY-MM-DD>"],
|
||||
|
||||
companion object {
|
||||
const val kind = 31922
|
||||
|
||||
fun create(
|
||||
privateKey: ByteArray,
|
||||
createdAt: Long = TimeUtils.now()
|
||||
): CalendarDateSlotEvent {
|
||||
val tags = mutableListOf<List<String>>()
|
||||
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
|
||||
val id = generateId(pubKey, createdAt, kind, tags, "")
|
||||
val sig = CryptoUtils.sign(id, privateKey)
|
||||
return CalendarDateSlotEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
|
||||
@Immutable
|
||||
class CalendarEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
companion object {
|
||||
const val kind = 31924
|
||||
|
||||
fun create(
|
||||
privateKey: ByteArray,
|
||||
createdAt: Long = TimeUtils.now()
|
||||
): CalendarEvent {
|
||||
val tags = mutableListOf<List<String>>()
|
||||
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
|
||||
val id = generateId(pubKey, createdAt, kind, tags, "")
|
||||
val sig = CryptoUtils.sign(id, privateKey)
|
||||
return CalendarEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
|
||||
@Immutable
|
||||
class CalendarRSVPEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
fun status() = tags.firstOrNull { it.size > 1 && it[0] == "location" }?.get(1)
|
||||
fun start() = tags.firstOrNull { it.size > 1 && it[0] == "start" }?.get(1)
|
||||
fun end() = tags.firstOrNull { it.size > 1 && it[0] == "end" }?.get(1)
|
||||
|
||||
// ["L", "status"],
|
||||
// ["l", "<accepted/declined/tentative>", "status"],
|
||||
// ["L", "freebusy"],
|
||||
// ["l", "<free/busy>", "freebusy"]
|
||||
|
||||
companion object {
|
||||
const val kind = 31925
|
||||
|
||||
fun create(
|
||||
privateKey: ByteArray,
|
||||
createdAt: Long = TimeUtils.now()
|
||||
): CalendarRSVPEvent {
|
||||
val tags = mutableListOf<List<String>>()
|
||||
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
|
||||
val id = generateId(pubKey, createdAt, kind, tags, "")
|
||||
val sig = CryptoUtils.sign(id, privateKey)
|
||||
return CalendarRSVPEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
|
||||
@Immutable
|
||||
class CalendarTimeSlotEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun location() = tags.firstOrNull { it.size > 1 && it[0] == "location" }?.get(1)
|
||||
fun start() = tags.firstOrNull { it.size > 1 && it[0] == "start" }?.get(1)?.toLongOrNull()
|
||||
fun end() = tags.firstOrNull { it.size > 1 && it[0] == "end" }?.get(1)?.toLongOrNull()
|
||||
|
||||
fun startTmz() = tags.firstOrNull { it.size > 1 && it[0] == "start_tzid" }?.get(1)?.toLongOrNull()
|
||||
fun endTmz() = tags.firstOrNull { it.size > 1 && it[0] == "end_tzid" }?.get(1)?.toLongOrNull()
|
||||
|
||||
// ["start", "<Unix timestamp in seconds>"],
|
||||
// ["end", "<Unix timestamp in seconds>"],
|
||||
// ["start_tzid", "<IANA Time Zone Database identifier>"],
|
||||
// ["end_tzid", "<IANA Time Zone Database identifier>"],
|
||||
|
||||
companion object {
|
||||
const val kind = 31923
|
||||
|
||||
fun create(
|
||||
privateKey: ByteArray,
|
||||
createdAt: Long = TimeUtils.now()
|
||||
): CalendarTimeSlotEvent {
|
||||
val tags = mutableListOf<List<String>>()
|
||||
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
|
||||
val id = generateId(pubKey, createdAt, kind, tags, "")
|
||||
val sig = CryptoUtils.sign(id, privateKey)
|
||||
return CalendarTimeSlotEvent(id.toHexKey(), pubKey, createdAt, tags, "", sig.toHexKey())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,5 +71,5 @@ class ChannelMessageEvent(
|
||||
}
|
||||
|
||||
interface IsInPublicChatChannel {
|
||||
open fun channel(): String?
|
||||
fun channel(): String?
|
||||
}
|
||||
|
||||
@@ -15,11 +15,7 @@ class ClassifiedsEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun title() = tags.firstOrNull { it.size > 1 && it[0] == "title" }?.get(1)
|
||||
fun image() = tags.firstOrNull { it.size > 1 && it[0] == "image" }?.get(1)
|
||||
fun summary() = tags.firstOrNull { it.size > 1 && it[0] == "summary" }?.get(1)
|
||||
|
||||
@@ -15,11 +15,7 @@ class CommunityDefinitionEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun description() = tags.firstOrNull { it.size > 1 && it[0] == "description" }?.get(1)
|
||||
fun image() = tags.firstOrNull { it.size > 1 && it[0] == "image" }?.get(1)
|
||||
fun rules() = tags.firstOrNull { it.size > 1 && it[0] == "rules" }?.get(1)
|
||||
|
||||
@@ -15,11 +15,7 @@ class EmojiPackSelectionEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
|
||||
override fun dTag() = ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
companion object {
|
||||
const val kind = 10030
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ open class Event(
|
||||
override fun matchTag1With(text: String) = tags.any { it.size > 1 && it[1].contains(text, true) }
|
||||
|
||||
override fun isTaggedUser(idHex: String) = tags.any { it.size > 1 && it[0] == "p" && it[1] == idHex }
|
||||
override fun isTaggedUsers(idHexes: Set<String>) = tags.any { it.size > 1 && it[0] == "p" && it[1] in idHexes }
|
||||
|
||||
override fun isTaggedEvent(idHex: String) = tags.any { it.size > 1 && it[0] == "e" && it[1] == idHex }
|
||||
|
||||
@@ -199,7 +200,7 @@ open class Event(
|
||||
return try {
|
||||
hasCorrectIDHash() && hasVerifedSignature()
|
||||
} catch (e: Exception) {
|
||||
Log.e("Event", "Fail checking if event $id has a valid signature", e)
|
||||
Log.e("Event", "Event $id does not have a valid signature: ${toJson()}", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -384,6 +385,20 @@ interface AddressableEvent {
|
||||
fun address(): ATag
|
||||
}
|
||||
|
||||
@Immutable
|
||||
open class BaseAddressableEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
kind: Int,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
): Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
}
|
||||
|
||||
fun String.bytesUsedInMemory(): Int {
|
||||
return (8 * ((((this.length) * 2) + 45) / 8))
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ class EventFactory {
|
||||
BadgeDefinitionEvent.kind -> BadgeDefinitionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
BadgeProfilesEvent.kind -> BadgeProfilesEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
BookmarkListEvent.kind -> BookmarkListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
CalendarEvent.kind -> CalendarEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
CalendarDateSlotEvent.kind -> CalendarDateSlotEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
CalendarTimeSlotEvent.kind -> CalendarTimeSlotEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
CalendarRSVPEvent.kind -> CalendarRSVPEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
ChannelCreateEvent.kind -> ChannelCreateEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
ChannelHideMessageEvent.kind -> ChannelHideMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
ChannelMessageEvent.kind -> ChannelMessageEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
@@ -41,9 +45,6 @@ class EventFactory {
|
||||
ContactListEvent.kind -> ContactListEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
DeletionEvent.kind -> DeletionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
|
||||
// Will never happen.
|
||||
// DirectMessageEvent.kind -> DirectMessageEvent(createdAt, tags, content)
|
||||
|
||||
EmojiPackEvent.kind -> EmojiPackEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
EmojiPackSelectionEvent.kind -> EmojiPackSelectionEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
SealedGossipEvent.kind -> SealedGossipEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
@@ -59,6 +60,7 @@ class EventFactory {
|
||||
LnZapEvent.kind -> LnZapEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
LnZapPaymentRequestEvent.kind -> LnZapPaymentRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
LnZapPaymentResponseEvent.kind -> LnZapPaymentResponseEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
LnZapPrivateEvent.kind -> LnZapPrivateEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
LnZapRequestEvent.kind -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
LongTextNoteEvent.kind -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
MetadataEvent.kind -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
|
||||
|
||||
@@ -30,6 +30,7 @@ interface EventInterface {
|
||||
fun hasValidSignature(): Boolean
|
||||
|
||||
fun isTaggedUser(idHex: String): Boolean
|
||||
fun isTaggedUsers(idHex: Set<String>): Boolean
|
||||
|
||||
fun isTaggedEvent(idHex: String): Boolean
|
||||
|
||||
|
||||
@@ -17,10 +17,7 @@ abstract class GeneralListEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
override fun dTag() = tags.filter { it.firstOrNull() == "d" }.mapNotNull { it.getOrNull(1) }.firstOrNull() ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun category() = dTag()
|
||||
fun bookmarkedPosts() = taggedEvents()
|
||||
fun bookmarkedPeople() = taggedUsers()
|
||||
|
||||
@@ -51,7 +51,7 @@ class GiftWrapEvent(
|
||||
|
||||
return when (toDecrypt.v) {
|
||||
Nip44Version.NIP04.versionCode -> CryptoUtils.decryptNIP04(toDecrypt, privKey, pubKey.hexToByteArray())
|
||||
Nip44Version.NIP24.versionCode -> CryptoUtils.decryptNIP24(toDecrypt, privKey, pubKey.hexToByteArray())
|
||||
Nip44Version.NIP44.versionCode -> CryptoUtils.decryptNIP44(toDecrypt, privKey, pubKey.hexToByteArray())
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -71,10 +71,10 @@ class GiftWrapEvent(
|
||||
createdAt: Long = TimeUtils.randomWithinAWeek()
|
||||
): GiftWrapEvent {
|
||||
val privateKey = CryptoUtils.privkeyCreate() // GiftWrap is always a random key
|
||||
val sharedSecret = CryptoUtils.getSharedSecretNIP24(privateKey, recipientPubKey.hexToByteArray())
|
||||
val sharedSecret = CryptoUtils.getSharedSecretNIP44(privateKey, recipientPubKey.hexToByteArray())
|
||||
|
||||
val content = encodeNIP44(
|
||||
CryptoUtils.encryptNIP24(
|
||||
CryptoUtils.encryptNIP44(
|
||||
toJson(event),
|
||||
sharedSecret
|
||||
)
|
||||
|
||||
@@ -15,11 +15,7 @@ class LiveActivitiesEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun title() = tags.firstOrNull { it.size > 1 && it[0] == "title" }?.get(1)
|
||||
fun summary() = tags.firstOrNull { it.size > 1 && it[0] == "summary" }?.get(1)
|
||||
fun image() = tags.firstOrNull { it.size > 1 && it[0] == "image" }?.get(1)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.vitorpamplona.quartz.events
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||
import com.vitorpamplona.quartz.encoders.Bech32
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.encoders.LnInvoiceUtil
|
||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import java.nio.charset.Charset
|
||||
import java.security.SecureRandom
|
||||
import javax.crypto.BadPaddingException
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
@Immutable
|
||||
class LnZapPrivateEvent(
|
||||
id: HexKey,
|
||||
pubKey: HexKey,
|
||||
createdAt: Long,
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
companion object {
|
||||
const val kind = 9733
|
||||
|
||||
fun create(
|
||||
privateKey: ByteArray,
|
||||
tags: List<List<String>> = emptyList(),
|
||||
content: String = "",
|
||||
createdAt: Long = TimeUtils.now()
|
||||
): Event {
|
||||
val pubKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
|
||||
val id = generateId(pubKey, createdAt, kind, tags, content)
|
||||
val sig = CryptoUtils.sign(id, privateKey).toHexKey()
|
||||
return Event(id.toHexKey(), pubKey, createdAt, kind, tags, content, sig)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,9 +86,9 @@ class LnZapRequestEvent(
|
||||
privkey = CryptoUtils.privkeyCreate()
|
||||
pubKey = CryptoUtils.pubkeyCreate(privkey).toHexKey()
|
||||
} else if (zapType == LnZapEvent.ZapType.PRIVATE) {
|
||||
var encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), originalNote.id(), createdAt)
|
||||
var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson()
|
||||
var encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, originalNote.pubKey().hexToByteArray())
|
||||
val encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), originalNote.id(), createdAt)
|
||||
val noteJson = (LnZapPrivateEvent.create(privkey, listOf(tags[0], tags[1]), message)).toJson()
|
||||
val encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, originalNote.pubKey().hexToByteArray())
|
||||
tags = tags + listOf(listOf("anon", encryptedContent))
|
||||
content = "" // make sure public content is empty, as the content is encrypted
|
||||
privkey = encryptionPrivateKey // sign event with generated privkey
|
||||
@@ -119,9 +119,9 @@ class LnZapRequestEvent(
|
||||
pubKey = CryptoUtils.pubkeyCreate(privkey).toHexKey()
|
||||
tags = tags + listOf(listOf("anon", ""))
|
||||
} else if (zapType == LnZapEvent.ZapType.PRIVATE) {
|
||||
var encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), userHex, createdAt)
|
||||
var noteJson = (create(privkey, 9733, listOf(tags[0], tags[1]), message)).toJson()
|
||||
var encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, userHex.hexToByteArray())
|
||||
val encryptionPrivateKey = createEncryptionPrivateKey(privateKey.toHexKey(), userHex, createdAt)
|
||||
val noteJson = LnZapPrivateEvent.create(privkey, listOf(tags[0], tags[1]), message).toJson()
|
||||
val encryptedContent = encryptPrivateZapMessage(noteJson, encryptionPrivateKey, userHex.hexToByteArray())
|
||||
tags = tags + listOf(listOf("anon", encryptedContent))
|
||||
content = ""
|
||||
privkey = encryptionPrivateKey
|
||||
@@ -132,21 +132,22 @@ class LnZapRequestEvent(
|
||||
return LnZapRequestEvent(id.toHexKey(), pubKey, createdAt, tags, content, sig.toHexKey())
|
||||
}
|
||||
|
||||
|
||||
fun createEncryptionPrivateKey(privkey: String, id: String, createdAt: Long): ByteArray {
|
||||
var str = privkey + id + createdAt.toString()
|
||||
var strbyte = str.toByteArray(Charset.forName("utf-8"))
|
||||
val str = privkey + id + createdAt.toString()
|
||||
val strbyte = str.toByteArray(Charset.forName("utf-8"))
|
||||
return CryptoUtils.sha256(strbyte)
|
||||
}
|
||||
|
||||
private fun encryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
||||
var sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
|
||||
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
|
||||
val iv = ByteArray(16)
|
||||
SecureRandom().nextBytes(iv)
|
||||
|
||||
val keySpec = SecretKeySpec(sharedSecret, "AES")
|
||||
val ivSpec = IvParameterSpec(iv)
|
||||
|
||||
var utf8message = msg.toByteArray(Charset.forName("utf-8"))
|
||||
val utf8message = msg.toByteArray(Charset.forName("utf-8"))
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec)
|
||||
val encryptedMsg = cipher.doFinal(utf8message)
|
||||
@@ -158,7 +159,7 @@ class LnZapRequestEvent(
|
||||
}
|
||||
|
||||
private fun decryptPrivateZapMessage(msg: String, privkey: ByteArray, pubkey: ByteArray): String {
|
||||
var sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
|
||||
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
|
||||
if (sharedSecret.size != 16 && sharedSecret.size != 32) {
|
||||
throw IllegalArgumentException("Invalid shared secret size")
|
||||
}
|
||||
@@ -170,8 +171,10 @@ class LnZapRequestEvent(
|
||||
val encryptedMsg = parts.first().run { Bech32.decode(this).second }
|
||||
val encryptedBytes = Bech32.five2eight(encryptedMsg, 0)
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec(
|
||||
Bech32.five2eight(iv, 0)))
|
||||
cipher.init(
|
||||
Cipher.DECRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec(
|
||||
Bech32.five2eight(iv, 0))
|
||||
)
|
||||
|
||||
try {
|
||||
val decryptedMsgBytes = cipher.doFinal(encryptedBytes)
|
||||
|
||||
@@ -18,10 +18,7 @@ class MuteListEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
override fun dTag() = tags.filter { it.firstOrNull() == "d" }.mapNotNull { it.getOrNull(1) }.firstOrNull() ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun plainContent(privKey: ByteArray): String? {
|
||||
return try {
|
||||
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privKey, pubKey.hexToByteArray())
|
||||
|
||||
@@ -15,11 +15,7 @@ class NNSEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun ip4() = tags.firstOrNull { it.size > 1 && it[0] == "ip4" }?.get(1)
|
||||
fun ip6() = tags.firstOrNull { it.size > 1 && it[0] == "ip6" }?.get(1)
|
||||
fun version() = tags.firstOrNull { it.size > 1 && it[0] == "version" }?.get(1)
|
||||
|
||||
@@ -15,10 +15,7 @@ class PinListEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
|
||||
fun pins() = tags.filter { it.size > 1 && it[0] == "pin" }.map { it[1] }
|
||||
|
||||
|
||||
@@ -15,11 +15,7 @@ class RelaySetEvent(
|
||||
tags: List<List<String>>,
|
||||
content: String,
|
||||
sig: HexKey
|
||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig), AddressableEvent {
|
||||
|
||||
override fun dTag() = tags.firstOrNull { it.size > 1 && it[0] == "d" }?.get(1) ?: ""
|
||||
override fun address() = ATag(kind, pubKey, dTag(), null)
|
||||
|
||||
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||
fun relays() = tags.filter { it.size > 1 && it[0] == "r" }.map { it[1] }
|
||||
fun description() = tags.firstOrNull() { it.size > 1 && it[0] == "description" }?.get(1)
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ class SealedGossipEvent(
|
||||
|
||||
return when (toDecrypt.v) {
|
||||
Nip44Version.NIP04.versionCode -> CryptoUtils.decryptNIP04(toDecrypt, privKey, pubKey.hexToByteArray())
|
||||
Nip44Version.NIP24.versionCode -> CryptoUtils.decryptNIP24(toDecrypt, privKey, pubKey.hexToByteArray())
|
||||
Nip44Version.NIP44.versionCode -> CryptoUtils.decryptNIP44(toDecrypt, privKey, pubKey.hexToByteArray())
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -81,10 +81,10 @@ class SealedGossipEvent(
|
||||
privateKey: ByteArray,
|
||||
createdAt: Long = TimeUtils.randomWithinAWeek()
|
||||
): SealedGossipEvent {
|
||||
val sharedSecret = CryptoUtils.getSharedSecretNIP24(privateKey, encryptTo.hexToByteArray())
|
||||
val sharedSecret = CryptoUtils.getSharedSecretNIP44(privateKey, encryptTo.hexToByteArray())
|
||||
|
||||
val content = encodeNIP44(
|
||||
CryptoUtils.encryptNIP24(
|
||||
CryptoUtils.encryptNIP44(
|
||||
Gossip.toJson(gossip),
|
||||
sharedSecret
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user