mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 08:47:33 +00:00
Decoupling NIP01 methods from CryptoUtils
Decoupling Encryption and Decryptions from CryptoUtils Decoupling live instances of JNI bindings for Secp and LibSodium from CryptoUtils Decoupling key cache from CryptoUtils Reorganizes NIP-04 to match new package structure Adjusts test structures to match
This commit is contained in:
@@ -34,9 +34,9 @@ import com.vitorpamplona.amethyst.service.uploads.nip96.ServerInfoRetriever
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.DEFAULT_MEDIA_SERVERS
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerName
|
||||
import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.fail
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -81,7 +81,7 @@ class ImageUploadTesting {
|
||||
|
||||
private suspend fun testBlossom(server: ServerName) {
|
||||
val paylod = getBitmap()
|
||||
val initialHash = CryptoUtils.sha256(paylod).toHexKey()
|
||||
val initialHash = sha256(paylod).toHexKey()
|
||||
val inputStream = paylod.inputStream()
|
||||
val result =
|
||||
BlossomUploader()
|
||||
@@ -111,7 +111,7 @@ class ImageUploadTesting {
|
||||
return
|
||||
}
|
||||
|
||||
val downloadedHash = CryptoUtils.sha256(imageData).toHexKey()
|
||||
val downloadedHash = sha256(imageData).toHexKey()
|
||||
assertEquals(initialHash, downloadedHash)
|
||||
}
|
||||
|
||||
|
||||
@@ -102,8 +102,8 @@ import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactTag
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ReadWrite
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.reply
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.reply
|
||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
|
||||
|
||||
@@ -24,7 +24,7 @@ import androidx.compose.runtime.Stable
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip14Subject.subject
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ import com.vitorpamplona.quartz.nip01Core.verify
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.VerificationState
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
|
||||
@@ -34,8 +34,8 @@ import coil3.key.Keyer
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.Options
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser.Companion.base64contentPattern
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import java.util.Base64
|
||||
|
||||
@Stable
|
||||
@@ -83,7 +83,7 @@ class Base64Fetcher(
|
||||
options: Options,
|
||||
): String? =
|
||||
if (data.scheme == "data") {
|
||||
CryptoUtils.sha256(data.toString().toByteArray()).toHexKey()
|
||||
sha256(data.toString().toByteArray()).toHexKey()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ import com.vitorpamplona.amethyst.service.relays.EOSEAccount
|
||||
import com.vitorpamplona.ammolite.relays.FeedType
|
||||
import com.vitorpamplona.ammolite.relays.TypedFilter
|
||||
import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
|
||||
|
||||
object NostrChatroomDataSource : AmethystNostrDataSource("ChatroomFeed") {
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import com.vitorpamplona.ammolite.relays.EVENT_FINDER_TYPES
|
||||
import com.vitorpamplona.ammolite.relays.FeedType
|
||||
import com.vitorpamplona.ammolite.relays.TypedFilter
|
||||
import com.vitorpamplona.ammolite.relays.filters.SincePerRelayFilter
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||
|
||||
+3
-3
@@ -30,13 +30,13 @@ import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStory
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStorySceneEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEmbed
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||
@@ -80,7 +80,7 @@ object NostrSearchEventOrUserDataSource : AmethystNostrDataSource("SearchEventFe
|
||||
}
|
||||
|
||||
when (val parsed = Nip19Parser.uriToRoute(mySearchString)?.entity) {
|
||||
is NSec -> KeyPair(privKey = parsed.hex.bechToBytes()).pubKey.toHexKey()
|
||||
is NSec -> Nip01.pubKeyCreate(parsed.hex.hexToByteArray()).toHexKey()
|
||||
is NPub -> parsed.hex
|
||||
is NProfile -> parsed.hex
|
||||
is com.vitorpamplona.quartz.nip19Bech32.entities.Note -> parsed.hex
|
||||
|
||||
@@ -25,7 +25,7 @@ import android.util.LruCache
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.amethyst.BuildConfig
|
||||
import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import okhttp3.EventListener
|
||||
import okhttp3.Protocol
|
||||
import okhttp3.Request
|
||||
@@ -70,7 +70,7 @@ object OnlineChecker {
|
||||
.url(url.replace("wss+livekit://", "wss://"))
|
||||
.header("Upgrade", "websocket")
|
||||
.header("Connection", "Upgrade")
|
||||
.header("Sec-WebSocket-Key", CryptoUtils.random(16).toByteString().base64())
|
||||
.header("Sec-WebSocket-Key", RandomInstance.bytes(16).toByteString().base64())
|
||||
.header("Sec-WebSocket-Version", "13")
|
||||
.header("Sec-WebSocket-Extensions", "permessage-deflate")
|
||||
.build()
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.isTaggedUser
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||
|
||||
@@ -23,9 +23,9 @@ package com.vitorpamplona.amethyst.service.uploads
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.encryption.NostrCipher
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import java.io.File
|
||||
|
||||
class EncryptFilesResult(
|
||||
@@ -48,9 +48,9 @@ class EncryptFiles {
|
||||
|
||||
resolver.openInputStream(inputFile)!!.use { inputStream ->
|
||||
val bytes = inputStream.readBytes()
|
||||
val originalHash = CryptoUtils.sha256(bytes).toHexKey()
|
||||
val originalHash = sha256(bytes).toHexKey()
|
||||
val encrypted = cipher.encrypt(bytes)
|
||||
val encryptedHash = CryptoUtils.sha256(encrypted).toHexKey()
|
||||
val encryptedHash = sha256(encrypted).toHexKey()
|
||||
|
||||
encryptedFile.outputStream().use { outputStream ->
|
||||
outputStream.write(encrypted)
|
||||
|
||||
@@ -30,9 +30,9 @@ import android.os.Build
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.amethyst.commons.blurhash.toBlurhash
|
||||
import com.vitorpamplona.amethyst.service.Blurhash
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import java.io.IOException
|
||||
|
||||
@@ -74,7 +74,7 @@ class FileHeader(
|
||||
dimPrecomputed: DimensionTag?,
|
||||
): Result<FileHeader> =
|
||||
try {
|
||||
val hash = CryptoUtils.sha256(data).toHexKey()
|
||||
val hash = sha256(data).toHexKey()
|
||||
val size = data.size
|
||||
|
||||
val (blurHash, dim) =
|
||||
|
||||
+2
-2
@@ -35,10 +35,10 @@ import com.vitorpamplona.amethyst.service.okhttp.HttpClientManager
|
||||
import com.vitorpamplona.amethyst.service.uploads.MediaUploadResult
|
||||
import com.vitorpamplona.amethyst.service.uploads.nip96.randomChars
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.blossom.BlossomAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
@@ -88,7 +88,7 @@ class BlossomUploader {
|
||||
|
||||
checkNotNull(payload) { "Can't open the image input stream" }
|
||||
|
||||
val hash = CryptoUtils.sha256(payload).toHexKey()
|
||||
val hash = sha256(payload).toHexKey()
|
||||
|
||||
val imageInputStream = contentResolver.openInputStream(uri)
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ import com.vitorpamplona.amethyst.ui.screen.AccountScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
|
||||
import com.vitorpamplona.amethyst.ui.tor.TorManager
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEmbed
|
||||
|
||||
@@ -24,7 +24,7 @@ import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.Bech32
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
|
||||
@@ -180,7 +180,7 @@ class NewMessageTagger(
|
||||
val restOfWord = key.substring(63)
|
||||
// Converts to npub
|
||||
val pubkey =
|
||||
Nip19Parser.uriToRoute(KeyPair(privKey = keyB32.bechToBytes()).pubKey.toNpub()) ?: return null
|
||||
Nip19Parser.uriToRoute(Nip01.pubKeyCreate(keyB32.bechToBytes()).toNpub()) ?: return null
|
||||
|
||||
return DirtyKeyInfo(pubkey, restOfWord.ifEmpty { null })
|
||||
} else if (key.startsWith("npub1", true)) {
|
||||
|
||||
@@ -77,7 +77,7 @@ import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hashtags
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.people.pTags
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.references.references
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.content.findHashtags
|
||||
|
||||
@@ -67,7 +67,7 @@ import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ImmutableListOfLists
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEmbed
|
||||
|
||||
+2
-2
@@ -93,11 +93,11 @@ import com.vitorpamplona.amethyst.ui.theme.Size30dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size75dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.hashVerifierMark
|
||||
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -695,7 +695,7 @@ private suspend fun verifyHash(content: MediaUrlContent): Boolean? {
|
||||
if (content.hash == null) return null
|
||||
|
||||
Amethyst.instance.coilCache.openSnapshot(content.url)?.use { snapshot ->
|
||||
val hash = CryptoUtils.sha256(snapshot.data.toFile().readBytes()).toHexKey()
|
||||
val hash = sha256(snapshot.data.toFile().readBytes()).toHexKey()
|
||||
|
||||
Log.d("Image Hash Verification", "$hash == ${content.hash}")
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.ui.dal
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.relays.updated
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.isTaggedGeoHash
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||
|
||||
@@ -26,7 +26,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.isTaggedHash
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
|
||||
|
||||
@@ -160,7 +160,7 @@ import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.isTaggedAddressableKind
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.BaseThreadedEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull
|
||||
|
||||
@@ -51,7 +51,7 @@ import com.vitorpamplona.amethyst.ui.theme.replyModifier
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
|
||||
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
|
||||
import com.vitorpamplona.quartz.nip02FollowList.toImmutableListOfLists
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||
|
||||
|
||||
+9
-4
@@ -36,7 +36,6 @@ import com.vitorpamplona.amethyst.service.Nip05NostrAddressVerifier
|
||||
import com.vitorpamplona.amethyst.ui.tor.TorSettings
|
||||
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||
import com.vitorpamplona.ammolite.relays.Constants
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
@@ -45,6 +44,7 @@ import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactTag
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ReadWrite
|
||||
import com.vitorpamplona.quartz.nip06KeyDerivation.Nip06
|
||||
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip19Bech32.Nip19Parser
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
|
||||
@@ -56,6 +56,7 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
|
||||
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||
import com.vitorpamplona.quartz.nip49PrivKeyEnc.Nip49
|
||||
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
@@ -142,9 +143,9 @@ class AccountStateViewModel : ViewModel() {
|
||||
transientAccount = transientAccount,
|
||||
torSettings = TorSettingsFlow.build(torSettings),
|
||||
)
|
||||
} else if (key.contains(" ") && CryptoUtils.isValidMnemonic(key)) {
|
||||
} else if (key.contains(" ") && Nip06().isValidMnemonic(key)) {
|
||||
AccountSettings(
|
||||
keyPair = KeyPair(privKey = CryptoUtils.privateKeyFromMnemonic(key)),
|
||||
keyPair = KeyPair(privKey = Nip06().privateKeyFromMnemonic(key)),
|
||||
transientAccount = transientAccount,
|
||||
torSettings = TorSettingsFlow.build(torSettings),
|
||||
)
|
||||
@@ -206,7 +207,11 @@ class AccountStateViewModel : ViewModel() {
|
||||
if (key.startsWith("ncryptsec")) {
|
||||
val newKey =
|
||||
try {
|
||||
CryptoUtils.decryptNIP49(key, password)
|
||||
if (key.isEmpty() || password.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
Nip49().decrypt(key, password)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
onError(e.message)
|
||||
|
||||
+3
-3
@@ -102,9 +102,9 @@ import com.vitorpamplona.amethyst.ui.theme.ButtonPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow
|
||||
import com.vitorpamplona.amethyst.ui.theme.grayText
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip19Bech32.toNsec
|
||||
import com.vitorpamplona.quartz.nip49PrivKeyEnc.Nip49
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -444,7 +444,7 @@ private fun encryptCopyNSec(
|
||||
}
|
||||
} else {
|
||||
accountViewModel.account.settings.keyPair.privKey?.let {
|
||||
val key = CryptoUtils.encryptNIP49(it.toHexKey(), password.value.text)
|
||||
val key = runCatching { Nip49().encrypt(it.toHexKey(), password.value.text) }.getOrNull()
|
||||
if (key != null) {
|
||||
clipboardManager.setText(AnnotatedString(key))
|
||||
scope.launch {
|
||||
@@ -540,7 +540,7 @@ private fun QrCodeButtonEncrypted(
|
||||
onDialogShow = {
|
||||
accountViewModel.account.settings.keyPair.privKey
|
||||
?.toHexKey()
|
||||
?.let { CryptoUtils.encryptNIP49(it, password.value.text) }
|
||||
?.let { Nip49().encrypt(it, password.value.text) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
+1
-3
@@ -111,8 +111,8 @@ import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip65RelayList.RelayUrlFormatter
|
||||
import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||
import fr.acinq.secp256k1.Hex
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.ImmutableSet
|
||||
import kotlinx.collections.immutable.persistentSetOf
|
||||
@@ -1753,7 +1753,6 @@ fun mockAccountViewModel(): AccountViewModel {
|
||||
KeyPair(
|
||||
privKey = Hex.decode("0f761f8a5a481e26f06605a1d9b3e9eba7a107d351f43c43a57469b788274499"),
|
||||
pubKey = Hex.decode("989c3734c46abac7ce3ce229971581a5a6ee39cdd6aa7261a55823fa7f8c4799"),
|
||||
forcePubKeyCheck = false,
|
||||
),
|
||||
),
|
||||
sharedPreferencesViewModel.sharedPrefs,
|
||||
@@ -1771,7 +1770,6 @@ fun mockVitorAccountViewModel(): AccountViewModel {
|
||||
keyPair =
|
||||
KeyPair(
|
||||
pubKey = Hex.decode("460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c"),
|
||||
forcePubKeyCheck = false,
|
||||
),
|
||||
),
|
||||
sharedPreferencesViewModel.sharedPrefs,
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ImmutableListOfLists
|
||||
import com.vitorpamplona.quartz.nip02FollowList.toImmutableListOfLists
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.NIP17Group
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ import com.vitorpamplona.amethyst.ui.feeds.InvalidatableContent
|
||||
import com.vitorpamplona.amethyst.ui.feeds.LoadedFeedState
|
||||
import com.vitorpamplona.ammolite.relays.BundledInsert
|
||||
import com.vitorpamplona.ammolite.relays.BundledUpdate
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.base.NIP17Group
|
||||
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
|
||||
|
||||
+1
-1
@@ -175,7 +175,7 @@ import com.vitorpamplona.quartz.experimental.zapPolls.PollNoteEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.isTaggedAddressableKind
|
||||
import com.vitorpamplona.quartz.nip01Core.tags.geohash.getGeoHash
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip13Pow.strongPoWOrNull
|
||||
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
|
||||
|
||||
+10
-1
@@ -65,11 +65,20 @@ class EventBenchmark {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun eventFactoryPerformanceTest() {
|
||||
fun eventFactoryKind1PerformanceTest() {
|
||||
val now = TimeUtils.now()
|
||||
val tags = arrayOf(arrayOf(""))
|
||||
benchmarkRule.measureRepeated {
|
||||
EventFactory.create("id", "pubkey", now, 1, tags, "content", "sig")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun eventFactoryKind30818PerformanceTest() {
|
||||
val now = TimeUtils.now()
|
||||
val tags = arrayOf(arrayOf(""))
|
||||
benchmarkRule.measureRepeated {
|
||||
EventFactory.create("id", "pubkey", now, 30818, tags, "content", "sig")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ package com.vitorpamplona.quartz.benchmark
|
||||
import androidx.benchmark.junit4.BenchmarkRule
|
||||
import androidx.benchmark.junit4.measureRepeated
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.checkSignature
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
@@ -132,7 +131,6 @@ class GiftWrapBenchmark {
|
||||
|
||||
// Simulate Receiver
|
||||
benchmarkRule.measureRepeated {
|
||||
CryptoUtils.clearCache()
|
||||
val counter = CountDownLatch(1)
|
||||
|
||||
val wrap = Event.fromJson(giftWrapJson) as GiftWrapEvent
|
||||
|
||||
+5
-5
@@ -23,7 +23,6 @@ package com.vitorpamplona.quartz.benchmark
|
||||
import androidx.benchmark.junit4.BenchmarkRule
|
||||
import androidx.benchmark.junit4.measureRepeated
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
@@ -35,6 +34,7 @@ import com.vitorpamplona.quartz.nip01Core.verifySignature
|
||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.messages.changeSubject
|
||||
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarning
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44
|
||||
import com.vitorpamplona.quartz.nip57Zaps.zapraiser.zapraiser
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.rumors.Rumor
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
@@ -171,7 +171,7 @@ class GiftWrapReceivingBenchmark {
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
assertNotNull(
|
||||
CryptoUtils.decryptNIP44(
|
||||
Nip44.decrypt(
|
||||
wrap.content,
|
||||
receiver.keyPair.privKey!!,
|
||||
wrap.pubKey.hexToByteArray(),
|
||||
@@ -188,7 +188,7 @@ class GiftWrapReceivingBenchmark {
|
||||
val wrap = createWrap(sender, receiver)
|
||||
|
||||
val innerJson =
|
||||
CryptoUtils.decryptNIP44(
|
||||
Nip44.decrypt(
|
||||
wrap.content,
|
||||
receiver.keyPair.privKey!!,
|
||||
wrap.pubKey.hexToByteArray(),
|
||||
@@ -206,7 +206,7 @@ class GiftWrapReceivingBenchmark {
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
assertNotNull(
|
||||
CryptoUtils.decryptNIP44(
|
||||
Nip44.decrypt(
|
||||
seal.content,
|
||||
receiver.keyPair.privKey!!,
|
||||
seal.pubKey.hexToByteArray(),
|
||||
@@ -223,7 +223,7 @@ class GiftWrapReceivingBenchmark {
|
||||
val seal = createSeal(sender, receiver)
|
||||
|
||||
val innerJson =
|
||||
CryptoUtils.decryptNIP44(
|
||||
Nip44.decrypt(
|
||||
seal.content,
|
||||
receiver.keyPair.privKey!!,
|
||||
seal.pubKey.hexToByteArray(),
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ import androidx.benchmark.junit4.measureRepeated
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.EventHasher
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -49,7 +49,7 @@ class Sha256Benchmark {
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
// Should pass
|
||||
assertNotNull(sha256Hash(byteArray))
|
||||
assertNotNull(sha256(byteArray))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-37
@@ -23,15 +23,17 @@ package com.vitorpamplona.quartz.benchmark
|
||||
import androidx.benchmark.junit4.BenchmarkRule
|
||||
import androidx.benchmark.junit4.measureRepeated
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.Encryption
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.Nip04
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44v2
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CryptoBenchmark {
|
||||
class SharedKeyBenchmark {
|
||||
@get:Rule val benchmarkRule = BenchmarkRule()
|
||||
|
||||
@Test
|
||||
@@ -40,7 +42,7 @@ class CryptoBenchmark {
|
||||
val keyPair2 = KeyPair()
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
assertNotNull(CryptoUtils.getSharedSecretNIP04(keyPair1.privKey!!, keyPair2.pubKey))
|
||||
assertNotNull(Nip04.getSharedSecret(keyPair1.privKey!!, keyPair2.pubKey))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +50,10 @@ class CryptoBenchmark {
|
||||
fun getSharedKeyNip44() {
|
||||
val keyPair1 = KeyPair()
|
||||
val keyPair2 = KeyPair()
|
||||
val nip44v2 = Nip44v2()
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
assertNotNull(CryptoUtils.nip44.v1.getSharedSecret(keyPair1.privKey!!, keyPair2.pubKey))
|
||||
assertNotNull(nip44v2.getConversationKey(keyPair1.privKey!!, keyPair2.pubKey))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,9 +61,10 @@ class CryptoBenchmark {
|
||||
fun computeSharedKeyNip04() {
|
||||
val keyPair1 = KeyPair()
|
||||
val keyPair2 = KeyPair()
|
||||
val nip04 = Encryption()
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
assertNotNull(CryptoUtils.computeSharedSecretNIP04(keyPair1.privKey!!, keyPair2.pubKey))
|
||||
assertNotNull(nip04.computeSharedSecret(keyPair1.privKey!!, keyPair2.pubKey))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,40 +72,10 @@ class CryptoBenchmark {
|
||||
fun computeSharedKeyNip44() {
|
||||
val keyPair1 = KeyPair()
|
||||
val keyPair2 = KeyPair()
|
||||
val nip44v2 = Nip44v2()
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
assertNotNull(CryptoUtils.nip44.v1.computeSharedSecret(keyPair1.privKey!!, keyPair2.pubKey))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun random() {
|
||||
benchmarkRule.measureRepeated { assertNotNull(CryptoUtils.random(1000)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sha256() {
|
||||
val keyPair = KeyPair()
|
||||
|
||||
benchmarkRule.measureRepeated { assertNotNull(CryptoUtils.sha256(keyPair.pubKey)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sign() {
|
||||
val keyPair = KeyPair()
|
||||
val msg = CryptoUtils.sha256(CryptoUtils.random(1000))
|
||||
|
||||
benchmarkRule.measureRepeated { assertNotNull(CryptoUtils.sign(msg, keyPair.privKey!!)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verify() {
|
||||
val keyPair = KeyPair()
|
||||
val msg = CryptoUtils.sha256(CryptoUtils.random(1000))
|
||||
val signature = CryptoUtils.sign(msg, keyPair.privKey!!)
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
assertNotNull(CryptoUtils.verifySignature(signature, msg, keyPair.pubKey))
|
||||
assertNotNull(nip44v2.computeConversationKey(keyPair1.privKey!!, keyPair2.pubKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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.benchmark
|
||||
|
||||
import androidx.benchmark.junit4.BenchmarkRule
|
||||
import androidx.benchmark.junit4.measureRepeated
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SignVerifyBenchmark {
|
||||
@get:Rule val benchmarkRule = BenchmarkRule()
|
||||
|
||||
@Test
|
||||
fun random() {
|
||||
benchmarkRule.measureRepeated { assertNotNull(RandomInstance.bytes(1000)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sign() {
|
||||
val keyPair = KeyPair()
|
||||
val msg = sha256(RandomInstance.bytes(1000))
|
||||
|
||||
benchmarkRule.measureRepeated { assertNotNull(Nip01.sign(msg, keyPair.privKey!!)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verify() {
|
||||
val keyPair = KeyPair()
|
||||
val msg = sha256(RandomInstance.bytes(1000))
|
||||
val signature = Nip01.sign(msg, keyPair.privKey!!)
|
||||
|
||||
benchmarkRule.measureRepeated {
|
||||
assertNotNull(Nip01.verify(signature, msg, keyPair.pubKey))
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -85,8 +85,8 @@ import com.vitorpamplona.amethyst.commons.robohash.parts.mouth6Cell
|
||||
import com.vitorpamplona.amethyst.commons.robohash.parts.mouth7Happy
|
||||
import com.vitorpamplona.amethyst.commons.robohash.parts.mouth8Buttons
|
||||
import com.vitorpamplona.amethyst.commons.robohash.parts.mouth9Closed
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
|
||||
val Black = SolidColor(Color.Black)
|
||||
val Gray = SolidColor(Color(0xFF6d6e70))
|
||||
@@ -168,7 +168,7 @@ class RobohashAssembler {
|
||||
Hex.decode(msg)
|
||||
} else {
|
||||
Log.w("Robohash", "$msg is not a hex")
|
||||
CryptoUtils.sha256(msg.toByteArray())
|
||||
sha256(msg.toByteArray())
|
||||
}
|
||||
|
||||
val bgColor = SolidColor(bytesToColor(hash[0], hash[1], hash[2], isLightTheme))
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CryptoUtilsTest {
|
||||
@Test
|
||||
fun testGetPublicFromPrivateKey() {
|
||||
val privateKey =
|
||||
"f410f88bcec6cbfda04d6a273c7b1dd8bba144cd45b71e87109cfa11dd7ed561".hexToByteArray()
|
||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey).toHexKey()
|
||||
assertEquals("7d4b8806f1fd713c287235411bf95aa81b7242ead892733ec84b3f2719845be6", publicKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSharedSecretCompatibilityWithCoracle() {
|
||||
val privateKey = "f410f88bcec6cbfda04d6a273c7b1dd8bba144cd45b71e87109cfa11dd7ed561"
|
||||
val publicKey = "765cd7cf91d3ad07423d114d5a39c61d52b2cdbc18ba055ddbbeec71fbe2aa2f"
|
||||
|
||||
val key =
|
||||
CryptoUtils.nip44.v1.getSharedSecret(
|
||||
privateKey = privateKey.hexToByteArray(),
|
||||
pubKey = publicKey.hexToByteArray(),
|
||||
)
|
||||
|
||||
assertEquals("577c966f499dddd8e8dcc34e8f352e283cc177e53ae372794947e0b8ede7cfd8", key.toHexKey())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSharedSecret() {
|
||||
val sender = KeyPair()
|
||||
val receiver = KeyPair()
|
||||
|
||||
val sharedSecret1 = CryptoUtils.nip44.v1.getSharedSecret(sender.privKey!!, receiver.pubKey)
|
||||
val sharedSecret2 = CryptoUtils.nip44.v1.getSharedSecret(receiver.privKey!!, sender.pubKey)
|
||||
|
||||
assertEquals(sharedSecret1.toHexKey(), sharedSecret2.toHexKey())
|
||||
|
||||
val secretKey1 = KeyPair(privKey = sharedSecret1)
|
||||
val secretKey2 = KeyPair(privKey = sharedSecret2)
|
||||
|
||||
assertEquals(secretKey1.pubKey.toHexKey(), secretKey2.pubKey.toHexKey())
|
||||
assertEquals(secretKey1.privKey?.toHexKey(), secretKey2.privKey?.toHexKey())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun encryptDecryptNIP4Test() {
|
||||
val msg = "Hi"
|
||||
|
||||
val privateKey = CryptoUtils.privkeyCreate()
|
||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||
|
||||
val encrypted = CryptoUtils.encryptNIP04(msg, privateKey, publicKey)
|
||||
val decrypted = CryptoUtils.decryptNIP04(encrypted, privateKey, publicKey)
|
||||
|
||||
assertEquals(msg, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun encryptDecryptNIP44v1Test() {
|
||||
val msg = "Hi"
|
||||
|
||||
val privateKey = CryptoUtils.privkeyCreate()
|
||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||
|
||||
val encrypted = CryptoUtils.nip44.v1.encrypt(msg, privateKey, publicKey)
|
||||
val decrypted = CryptoUtils.nip44.v1.decrypt(encrypted, privateKey, publicKey)
|
||||
|
||||
assertEquals(msg, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun encryptSharedSecretDecryptNIP4Test() {
|
||||
val msg = "Hi"
|
||||
|
||||
val privateKey = CryptoUtils.privkeyCreate()
|
||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||
|
||||
val encrypted = CryptoUtils.encryptNIP04(msg, privateKey, publicKey)
|
||||
val decrypted = CryptoUtils.decryptNIP04(encrypted, privateKey, publicKey)
|
||||
|
||||
assertEquals(msg, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun encryptSharedSecretDecryptNIP44v1Test() {
|
||||
val msg = "Hi"
|
||||
|
||||
val privateKey = CryptoUtils.privkeyCreate()
|
||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||
val sharedSecret = CryptoUtils.nip44.v1.getSharedSecret(privateKey, publicKey)
|
||||
|
||||
val encrypted = CryptoUtils.nip44.v1.encrypt(msg, sharedSecret)
|
||||
val decrypted = CryptoUtils.nip44.v1.decrypt(encrypted, sharedSecret)
|
||||
|
||||
assertEquals(msg, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun signString() {
|
||||
val random = "319cc5596fdd6cd767e5a59d976e8e059c61306af90dff1e6ee1067b3a1fdbc0".hexToByteArray()
|
||||
val message = "8e58c8251bb406b6ded69e9eb14f55282a9a53bdab16fc49a3218c2ad3abc887".hexToByteArray()
|
||||
val keyPair = KeyPair("a5ab474552c8f9c46c2eda5a0b68f27430ad81f96cb405e0cb4e34bf0c6494a2".hexToByteArray())
|
||||
|
||||
val signedMessage = CryptoUtils.sign(message, keyPair.privKey!!, random).toHexKey()
|
||||
val expectedValue = "0f9be7e01ba53d5ee6874b9180c7956269fda7a5be424634c3d17b5cfcea6da001be89183876415ba08b7dafa6cff4555e393dc228fb8769b384344e9a27b77c"
|
||||
assertEquals(expectedValue, signedMessage)
|
||||
|
||||
val message2 = "Hello"
|
||||
val signedMessage2 = CryptoUtils.signString(message2, keyPair.privKey!!, random).toHexKey()
|
||||
val expectedValue2 = "7ec8194a585bfb513564113b6b7bfeaafa0254c99d24eaf92280657c2291bab908b1b7bc553c83276a0254aef5041bbe6a50e93381edc4de3d859efa1c3a5a1e"
|
||||
assertEquals(expectedValue2, signedMessage2)
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,10 @@
|
||||
package com.vitorpamplona.quartz.bloom
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertFalse
|
||||
import junit.framework.TestCase.assertTrue
|
||||
@@ -41,7 +42,7 @@ class BloomFilter(
|
||||
private val size: Int,
|
||||
private val rounds: Int,
|
||||
private val bits: BitSet = BitSet(size),
|
||||
private val salt: ByteArray = CryptoUtils.random(8),
|
||||
private val salt: ByteArray = RandomInstance.bytes(8),
|
||||
) {
|
||||
private val hash = MessageDigest.getInstance("SHA-256")
|
||||
private val lock = ReentrantReadWriteLock()
|
||||
@@ -135,7 +136,7 @@ class BloomFilterTest {
|
||||
|
||||
var failureCounter = 0
|
||||
for (seed in 0..1000000) {
|
||||
if (bloomFilter.mightContains(CryptoUtils.pubkeyCreate(CryptoUtils.privkeyCreate()))) {
|
||||
if (bloomFilter.mightContains(Nip01.pubKeyCreate(Nip01.privKeyCreate()))) {
|
||||
failureCounter++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,25 +22,23 @@ package com.vitorpamplona.quartz.nip01Core
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import com.vitorpamplona.quartz.utils.Secp256k1Instance
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.security.SecureRandom
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class Nip01Test {
|
||||
private val nip01 = Nip01(Secp256k1.get(), SecureRandom())
|
||||
private val privateKey = "f410f88bcec6cbfda04d6a273c7b1dd8bba144cd45b71e87109cfa11dd7ed561".hexToByteArray()
|
||||
|
||||
@Test
|
||||
fun testGetPublicFromPrivateKey() {
|
||||
assertEquals(
|
||||
"7d4b8806f1fd713c287235411bf95aa81b7242ead892733ec84b3f2719845be6",
|
||||
nip01.pubkeyCreate(privateKey).toHexKey(),
|
||||
Nip01.pubKeyCreate(privateKey).toHexKey(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,7 +47,7 @@ class Nip01Test {
|
||||
val key = "e6159851715b4aa6190c22b899b0c792847de0a4435ac5b678f35738351c43b0".hexToByteArray()
|
||||
assertEquals(
|
||||
"029fa4ce8c87ca546b196e6518db80a6780e1bd5552b61f9f17bafee5d4e34e09b",
|
||||
nip01.compressedPubkeyCreate(key).toHexKey(),
|
||||
Secp256k1Instance.compressedPubKeyFor(key).toHexKey(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -58,7 +56,7 @@ class Nip01Test {
|
||||
val key = "65f039136f8da8d3e87b4818746b53318d5481e24b2673f162815144223a0b5a".hexToByteArray()
|
||||
assertEquals(
|
||||
"033dcef7585efbdb68747d919152bd481e21f5e952aaaef5a19604fbd096a93dd5",
|
||||
nip01.compressedPubkeyCreate(key).toHexKey(),
|
||||
Secp256k1Instance.compressedPubKeyFor(key).toHexKey(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -66,7 +64,7 @@ class Nip01Test {
|
||||
fun testDeterministicSign() {
|
||||
assertEquals(
|
||||
"1484d0e0bd62165e822e31f1f4cc8e1ce8e20c30a060e24fb0ecd7baf7c624f661fb7a3e4f0ddb43018e5f0b4892c929af64d8b7a86021aa081ec8231e3dfa37",
|
||||
nip01.signDeterministic(sha256Hash("Test".toByteArray()), privateKey).toHexKey(),
|
||||
Nip01.sign(sha256("Test".toByteArray()), privateKey, null).toHexKey(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,17 +72,17 @@ class Nip01Test {
|
||||
fun testSha256() {
|
||||
assertEquals(
|
||||
"532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25",
|
||||
sha256Hash("Test".toByteArray()).toHexKey(),
|
||||
sha256("Test".toByteArray()).toHexKey(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeterministicVerify() {
|
||||
assertTrue(
|
||||
nip01.verify(
|
||||
Nip01.verify(
|
||||
"1484d0e0bd62165e822e31f1f4cc8e1ce8e20c30a060e24fb0ecd7baf7c624f661fb7a3e4f0ddb43018e5f0b4892c929af64d8b7a86021aa081ec8231e3dfa37".hexToByteArray(),
|
||||
sha256Hash("Test".toByteArray()),
|
||||
nip01.pubkeyCreate(privateKey),
|
||||
sha256("Test".toByteArray()),
|
||||
Nip01.pubKeyCreate(privateKey),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -93,18 +91,18 @@ class Nip01Test {
|
||||
fun testNonDeterministicSign() {
|
||||
assertNotEquals(
|
||||
"1484d0e0bd62165e822e31f1f4cc8e1ce8e20c30a060e24fb0ecd7baf7c624f661fb7a3e4f0ddb43018e5f0b4892c929af64d8b7a86021aa081ec8231e3dfa37",
|
||||
nip01.sign(sha256Hash("Test".toByteArray()), privateKey).toHexKey(),
|
||||
Nip01.sign(sha256("Test".toByteArray()), privateKey).toHexKey(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNonDeterministicSignVerify() {
|
||||
val signature = nip01.sign(sha256Hash("Test".toByteArray()), privateKey)
|
||||
val signature = Nip01.sign(sha256("Test".toByteArray()), privateKey)
|
||||
assertTrue(
|
||||
nip01.verify(
|
||||
Nip01.verify(
|
||||
signature,
|
||||
sha256Hash("Test".toByteArray()),
|
||||
nip01.pubkeyCreate(privateKey),
|
||||
sha256("Test".toByteArray()),
|
||||
Nip01.pubKeyCreate(privateKey),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.nip01Core.crypto
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class Nip01CryptoTest {
|
||||
@Test
|
||||
fun testGetPublicFromPrivateKey() {
|
||||
val privateKey =
|
||||
"f410f88bcec6cbfda04d6a273c7b1dd8bba144cd45b71e87109cfa11dd7ed561".hexToByteArray()
|
||||
val publicKey = Nip01.pubKeyCreate(privateKey).toHexKey()
|
||||
assertEquals("7d4b8806f1fd713c287235411bf95aa81b7242ead892733ec84b3f2719845be6", publicKey)
|
||||
}
|
||||
}
|
||||
+10
-12
@@ -23,21 +23,19 @@ package com.vitorpamplona.quartz.nip04Dm
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.EncryptedInfo
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.Encryption
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import org.junit.Test
|
||||
import java.security.SecureRandom
|
||||
|
||||
class Nip04EncryptionTest {
|
||||
private val random = SecureRandom()
|
||||
private val nip01 = Nip01(Secp256k1.get(), random)
|
||||
private val nip04 = Nip04Encryption(Secp256k1.get(), random)
|
||||
class EncryptionTest {
|
||||
private val nip04 = Encryption()
|
||||
|
||||
val sk1 = "91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe".hexToByteArray()
|
||||
val sk2 = "96f6fa197aa07477ab88f6981118466ae3a982faab8ad5db9d5426870c73d220".hexToByteArray()
|
||||
val pk1 = nip01.pubkeyCreate(sk1)
|
||||
val pk2 = nip01.pubkeyCreate(sk2)
|
||||
val pk1 = Nip01.pubKeyCreate(sk1)
|
||||
val pk2 = Nip01.pubKeyCreate(sk2)
|
||||
|
||||
val expectedShared = "7ce22696eb0e303ddaa491bdf2a56b79d249f2d861b8e012a933e01dc4beba81"
|
||||
|
||||
@@ -89,17 +87,17 @@ class Nip04EncryptionTest {
|
||||
|
||||
@Test
|
||||
fun isNIP04Encode() {
|
||||
assertTrue(Nip04Encryption.isNIP04("Xj/oZZolaItdyQ5v7xYFpA==?iv=+a6zagBp+mr5m1aFbHQ8lA=="))
|
||||
assertTrue(Nip04Encryption.isNIP04("zJxfaJ32rN5Dg1ODjOlEew==?iv=EV5bUjcc4OX2Km/zPp4ndQ=="))
|
||||
assertTrue(EncryptedInfo.isNIP04("Xj/oZZolaItdyQ5v7xYFpA==?iv=+a6zagBp+mr5m1aFbHQ8lA=="))
|
||||
assertTrue(EncryptedInfo.isNIP04("zJxfaJ32rN5Dg1ODjOlEew==?iv=EV5bUjcc4OX2Km/zPp4ndQ=="))
|
||||
assertTrue(
|
||||
Nip04Encryption.isNIP04("6f8dMstm+udOu7yipSn33orTmwQpWbtfuY95NH+eTU1kArysWJIDkYgI2D25EAGIDJsNd45jOJ2NbVOhFiL3ZP/NWsTwXokk34iyHyA/lkjzugQ1bHXoMD1fP/Ay4hB4al1NHb8HXHKZaxPrErwdRDb8qa/I6dXb/1xxyVvNQBHHvmsM5yIFaPwnCN1DZqXf2KbTA/Ekz7Hy+7R+Sy3TXLQDFpWYqykppkXc7Fs0qSuPRyxz5+anuN0dxZa9GTwTEnBrZPbthKkNRrvZMdTGJ6WumOh9aUq8OJJWy9aOgsXvs7qjN1UqcCqQqYaVnEOhCaqWNDsVtsFrVDj+SaLIBvCiomwF4C4nIgngJ5I69tx0UNI0q+ZnvOGQZ7m1PpW2NYP7Yw43HJNdeUEQAmdCPnh/PJwzLTnIxHmQU7n7SPlMdV0SFa6H8y2HHvex697GAkyE5t8c2uO24OnqIwF1tR3blIqXzTSRl0GA6QvrSj2p4UtnWjvF7xT7RiIEyTtgU/AsihTrXyXzWWZaIBJogpgw6erlZqWjCH7sZy/WoGYEiblobOAqMYxax6vRbeuGtoYksr/myX+x9rfLrYuoDRTw4woXOLmMrrj+Mf0TbAgc3SjdkqdsPU1553rlSqIEZXuFgoWmxvVQDtekgTYyS97G81TDSK9nTJT5ilku8NVq2LgtBXGwsNIw/xekcOUzJke3kpnFPutNaexR1VF3ohIuqRKYRGcd8ADJP2lfwMcaGRiplAmFoaVS1YUhQwYFNq9rMLf7YauRGV4BJg/t9srdGxf5RoKCvRo+XM/nLxxysTR9MVaEP/3lDqjwChMxs+eWfLHE5vRWV8hUEqdrWNZV29gsx5nQpzJ4PARGZVu310pQzc6JAlc2XAhhFk6RamkYJnmCSMnb/RblzIATBi2kNrCVAlaXIon188inB62rEpZGPkRIP7PUfu27S/elLQHBHeGDsxOXsBRo1gl3te+raoBHsxo6zvRnYbwdAQa5taDE63eh+fT6kFI+xYmXNAQkU8Dp0MVhEh4JQI06Ni/AKrvYpC95TXXIphZcF+/Pv/vaGkhG2X9S3uhugwWK?iv=2vWkOQQi0WynNJz/aZ4k2g=="),
|
||||
EncryptedInfo.isNIP04("6f8dMstm+udOu7yipSn33orTmwQpWbtfuY95NH+eTU1kArysWJIDkYgI2D25EAGIDJsNd45jOJ2NbVOhFiL3ZP/NWsTwXokk34iyHyA/lkjzugQ1bHXoMD1fP/Ay4hB4al1NHb8HXHKZaxPrErwdRDb8qa/I6dXb/1xxyVvNQBHHvmsM5yIFaPwnCN1DZqXf2KbTA/Ekz7Hy+7R+Sy3TXLQDFpWYqykppkXc7Fs0qSuPRyxz5+anuN0dxZa9GTwTEnBrZPbthKkNRrvZMdTGJ6WumOh9aUq8OJJWy9aOgsXvs7qjN1UqcCqQqYaVnEOhCaqWNDsVtsFrVDj+SaLIBvCiomwF4C4nIgngJ5I69tx0UNI0q+ZnvOGQZ7m1PpW2NYP7Yw43HJNdeUEQAmdCPnh/PJwzLTnIxHmQU7n7SPlMdV0SFa6H8y2HHvex697GAkyE5t8c2uO24OnqIwF1tR3blIqXzTSRl0GA6QvrSj2p4UtnWjvF7xT7RiIEyTtgU/AsihTrXyXzWWZaIBJogpgw6erlZqWjCH7sZy/WoGYEiblobOAqMYxax6vRbeuGtoYksr/myX+x9rfLrYuoDRTw4woXOLmMrrj+Mf0TbAgc3SjdkqdsPU1553rlSqIEZXuFgoWmxvVQDtekgTYyS97G81TDSK9nTJT5ilku8NVq2LgtBXGwsNIw/xekcOUzJke3kpnFPutNaexR1VF3ohIuqRKYRGcd8ADJP2lfwMcaGRiplAmFoaVS1YUhQwYFNq9rMLf7YauRGV4BJg/t9srdGxf5RoKCvRo+XM/nLxxysTR9MVaEP/3lDqjwChMxs+eWfLHE5vRWV8hUEqdrWNZV29gsx5nQpzJ4PARGZVu310pQzc6JAlc2XAhhFk6RamkYJnmCSMnb/RblzIATBi2kNrCVAlaXIon188inB62rEpZGPkRIP7PUfu27S/elLQHBHeGDsxOXsBRo1gl3te+raoBHsxo6zvRnYbwdAQa5taDE63eh+fT6kFI+xYmXNAQkU8Dp0MVhEh4JQI06Ni/AKrvYpC95TXXIphZcF+/Pv/vaGkhG2X9S3uhugwWK?iv=2vWkOQQi0WynNJz/aZ4k2g=="),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isNIP04EncodeWithBug() {
|
||||
assertTrue(
|
||||
Nip04Encryption.isNIP04(
|
||||
EncryptedInfo.isNIP04(
|
||||
"QOAYBWa88ConWs2C4kSvNqAcowCtg0ZRtAl7FyLSv9VMaJH4oCiDx0h8VLBnV97HdE4lv" +
|
||||
"TW7AYC1eEw8/t1dbe0qRc3XrOt7MrPAO8yqpy1/3lFB1+10kip0+KdgT8Quvv02wTP8Dqi" +
|
||||
"xpr2fliAIG2ONvDn+O5V0q9aVUN9HitgL/myTyR0T42edmxWeZoMBEOKvJyO80FekSsgVL" +
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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.nip04Dm
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.Nip04
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class Nip04Test {
|
||||
@Test
|
||||
fun encryptDecryptNIP4Test() {
|
||||
val msg = "Hi"
|
||||
|
||||
val privateKey = Nip01.privKeyCreate()
|
||||
val publicKey = Nip01.pubKeyCreate(privateKey)
|
||||
|
||||
val encrypted = Nip04.encrypt(msg, privateKey, publicKey)
|
||||
val decrypted = Nip04.decrypt(encrypted, privateKey, publicKey)
|
||||
|
||||
assertEquals(msg, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun encryptSharedSecretDecryptNIP4Test() {
|
||||
val msg = "Hi"
|
||||
|
||||
val privateKey = Nip01.privKeyCreate()
|
||||
val publicKey = Nip01.pubKeyCreate(privateKey)
|
||||
|
||||
val encrypted = Nip04.encrypt(msg, privateKey, publicKey)
|
||||
val decrypted = Nip04.decrypt(encrypted, privateKey, publicKey)
|
||||
|
||||
assertEquals(msg, decrypted)
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -22,14 +22,13 @@ package com.vitorpamplona.quartz.nip06KeyDerivation
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class Bip32SeedDerivationTest {
|
||||
val seedDerivation = Bip32SeedDerivation(Secp256k1.get())
|
||||
val seedDerivation = Bip32SeedDerivation()
|
||||
|
||||
val masterBitcoin =
|
||||
seedDerivation.generate(
|
||||
|
||||
+2
-2
@@ -23,9 +23,9 @@ package com.vitorpamplona.quartz.nip06KeyDerivation
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import junit.framework.TestCase.fail
|
||||
@@ -69,7 +69,7 @@ class Bip39MnemonicsTest {
|
||||
fun validateMnemonicsValid() {
|
||||
for (i in 0..99) {
|
||||
for (length in listOf(16, 20, 24, 28, 32, 36, 40)) {
|
||||
val mnemonics = Bip39Mnemonics.toMnemonics(CryptoUtils.random(length))
|
||||
val mnemonics = Bip39Mnemonics.toMnemonics(RandomInstance.bytes(length))
|
||||
Bip39Mnemonics.validate(mnemonics)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -22,7 +22,6 @@ package com.vitorpamplona.quartz.nip06KeyDerivation
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
@@ -30,7 +29,7 @@ import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class Nip06Test {
|
||||
val nip06 = Nip06(Secp256k1.get())
|
||||
val nip06 = Nip06()
|
||||
|
||||
// private key (hex): 7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a
|
||||
// nsec: nsec10allq0gjx7fddtzef0ax00mdps9t2kmtrldkyjfs8l5xruwvh2dq0lhhkp
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* 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.nip44Encryption
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class Nip44v1Test {
|
||||
private val nip44v1 = Nip44v1()
|
||||
|
||||
@Test
|
||||
fun testSharedSecretCompatibilityWithCoracle() {
|
||||
val privateKey = "f410f88bcec6cbfda04d6a273c7b1dd8bba144cd45b71e87109cfa11dd7ed561"
|
||||
val publicKey = "765cd7cf91d3ad07423d114d5a39c61d52b2cdbc18ba055ddbbeec71fbe2aa2f"
|
||||
|
||||
val key =
|
||||
nip44v1.getSharedSecret(
|
||||
privateKey = privateKey.hexToByteArray(),
|
||||
pubKey = publicKey.hexToByteArray(),
|
||||
)
|
||||
|
||||
assertEquals("577c966f499dddd8e8dcc34e8f352e283cc177e53ae372794947e0b8ede7cfd8", key.toHexKey())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSharedSecret() {
|
||||
val sender = KeyPair()
|
||||
val receiver = KeyPair()
|
||||
|
||||
val sharedSecret1 = nip44v1.getSharedSecret(sender.privKey!!, receiver.pubKey)
|
||||
val sharedSecret2 = nip44v1.getSharedSecret(receiver.privKey!!, sender.pubKey)
|
||||
|
||||
assertEquals(sharedSecret1.toHexKey(), sharedSecret2.toHexKey())
|
||||
|
||||
val secretKey1 = KeyPair(privKey = sharedSecret1)
|
||||
val secretKey2 = KeyPair(privKey = sharedSecret2)
|
||||
|
||||
assertEquals(secretKey1.pubKey.toHexKey(), secretKey2.pubKey.toHexKey())
|
||||
assertEquals(secretKey1.privKey?.toHexKey(), secretKey2.privKey?.toHexKey())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun encryptDecrypt() {
|
||||
val msg = "Hi"
|
||||
|
||||
val privateKey = Nip01.privKeyCreate()
|
||||
val publicKey = Nip01.pubKeyCreate(privateKey)
|
||||
|
||||
val encrypted = nip44v1.encrypt(msg, privateKey, publicKey)
|
||||
val decrypted = nip44v1.decrypt(encrypted, privateKey, publicKey)
|
||||
|
||||
assertEquals(msg, decrypted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun encryptDecryptSharedSecret() {
|
||||
val msg = "Hi"
|
||||
|
||||
val privateKey = Nip01.privKeyCreate()
|
||||
val publicKey = Nip01.pubKeyCreate(privateKey)
|
||||
|
||||
val sharedSecret = nip44v1.getSharedSecret(privateKey, publicKey)
|
||||
|
||||
val encrypted = nip44v1.encrypt(msg, sharedSecret)
|
||||
val decrypted = nip44v1.decrypt(encrypted, sharedSecret)
|
||||
|
||||
assertEquals(msg, decrypted)
|
||||
}
|
||||
}
|
||||
+10
-14
@@ -27,18 +27,17 @@ import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertNull
|
||||
import junit.framework.TestCase.fail
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.security.SecureRandom
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class NIP44v2Test {
|
||||
class Nip44v2Test {
|
||||
private val vectors: VectorFile =
|
||||
jacksonObjectMapper()
|
||||
.readValue(
|
||||
@@ -46,9 +45,7 @@ class NIP44v2Test {
|
||||
VectorFile::class.java,
|
||||
)
|
||||
|
||||
private val random = SecureRandom()
|
||||
private val nip44v2 = Nip44v2(Secp256k1.get(), random)
|
||||
private val nip01 = Nip01(Secp256k1.get(), random)
|
||||
private val nip44v2 = Nip44v2()
|
||||
|
||||
@Test
|
||||
fun conversationKeyTest() {
|
||||
@@ -73,8 +70,8 @@ class NIP44v2Test {
|
||||
val privateKeyA = "f410f88bcec6cbfda04d6a273c7b1dd8bba144cd45b71e87109cfa11dd7ed561".hexToByteArray()
|
||||
val privateKeyB = "65f039136f8da8d3e87b4818746b53318d5481e24b2673f162815144223a0b5a".hexToByteArray()
|
||||
|
||||
val publicKeyA = nip01.pubkeyCreate(privateKeyA)
|
||||
val publicKeyB = nip01.pubkeyCreate(privateKeyB)
|
||||
val publicKeyA = Nip01.pubKeyCreate(privateKeyA)
|
||||
val publicKeyB = Nip01.pubKeyCreate(privateKeyB)
|
||||
|
||||
assertEquals(
|
||||
nip44v2.getConversationKey(privateKeyA, publicKeyB).toHexKey(),
|
||||
@@ -87,8 +84,8 @@ class NIP44v2Test {
|
||||
val privateKeyA = "f410f88bcec6cbfda04d6a273c7b1dd8bba144cd45b71e87109cfa11dd7ed561".hexToByteArray()
|
||||
val privateKeyB = "e6159851715b4aa6190c22b899b0c792847de0a4435ac5b678f35738351c43b0".hexToByteArray()
|
||||
|
||||
val publicKeyA = nip01.pubkeyCreate(privateKeyA)
|
||||
val publicKeyB = nip01.pubkeyCreate(privateKeyB)
|
||||
val publicKeyA = Nip01.pubKeyCreate(privateKeyA)
|
||||
val publicKeyB = Nip01.pubKeyCreate(privateKeyB)
|
||||
|
||||
assertEquals(
|
||||
nip44v2.getConversationKey(privateKeyA, publicKeyB).toHexKey(),
|
||||
@@ -149,8 +146,7 @@ class NIP44v2Test {
|
||||
@Test
|
||||
fun invalidMessageLengths() {
|
||||
for (v in vectors.v2?.invalid?.encryptMsgLengths!!) {
|
||||
val key = ByteArray(32)
|
||||
random.nextBytes(key)
|
||||
val key = RandomInstance.bytes(32)
|
||||
try {
|
||||
nip44v2.encrypt("a".repeat(v), key)
|
||||
fail("Should Throw for $v")
|
||||
@@ -185,5 +181,5 @@ class NIP44v2Test {
|
||||
}
|
||||
}
|
||||
|
||||
private fun sha256Hex(data: ByteArray) = sha256Hash(data).toHexKey()
|
||||
private fun sha256Hex(data: ByteArray) = sha256(data).toHexKey()
|
||||
}
|
||||
@@ -22,13 +22,11 @@ package com.vitorpamplona.quartz.nip49PrivKeyEnc
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.fail
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.security.SecureRandom
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
public class NIP49Test {
|
||||
@@ -53,8 +51,7 @@ public class NIP49Test {
|
||||
)
|
||||
}
|
||||
|
||||
val random = SecureRandom()
|
||||
val nip49 = Nip49(Secp256k1.get(), random)
|
||||
val nip49 = Nip49()
|
||||
|
||||
@Test
|
||||
fun decodeBech32() {
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.nip55AndroidSigner
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SignStringTest {
|
||||
@Test
|
||||
fun signString() {
|
||||
val random = "319cc5596fdd6cd767e5a59d976e8e059c61306af90dff1e6ee1067b3a1fdbc0".hexToByteArray()
|
||||
val message = "8e58c8251bb406b6ded69e9eb14f55282a9a53bdab16fc49a3218c2ad3abc887".hexToByteArray()
|
||||
val keyPair = KeyPair("a5ab474552c8f9c46c2eda5a0b68f27430ad81f96cb405e0cb4e34bf0c6494a2".hexToByteArray())
|
||||
|
||||
val signedMessage = Nip01.sign(message, keyPair.privKey!!, random).toHexKey()
|
||||
val expectedValue = "0f9be7e01ba53d5ee6874b9180c7956269fda7a5be424634c3d17b5cfcea6da001be89183876415ba08b7dafa6cff4555e393dc228fb8769b384344e9a27b77c"
|
||||
assertEquals(expectedValue, signedMessage)
|
||||
|
||||
val message2 = "Hello"
|
||||
val signedMessage2 = signString(message2, keyPair.privKey!!, random).toHexKey()
|
||||
val expectedValue2 = "7ec8194a585bfb513564113b6b7bfeaafa0254c99d24eaf92280657c2291bab908b1b7bc553c83276a0254aef5041bbe6a50e93381edc4de3d859efa1c3a5a1e"
|
||||
assertEquals(expectedValue2, signedMessage2)
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@
|
||||
package com.vitorpamplona.quartz.utils
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
@@ -56,7 +55,7 @@ class HexEncodingTest {
|
||||
@Test
|
||||
fun testRandoms() {
|
||||
for (i in 0..1000) {
|
||||
val bytes = CryptoUtils.privkeyCreate()
|
||||
val bytes = RandomInstance.bytes(32)
|
||||
val hex =
|
||||
fr.acinq.secp256k1.Hex
|
||||
.encode(bytes)
|
||||
@@ -90,7 +89,7 @@ class HexEncodingTest {
|
||||
@Test
|
||||
fun testRandomsIsHex() {
|
||||
for (i in 0..10000) {
|
||||
val bytes = CryptoUtils.privkeyCreate()
|
||||
val bytes = RandomInstance.bytes(32)
|
||||
val hex = bytes.toHexString(HexFormat.Default)
|
||||
assertTrue(hex, Hex.isHex(hex))
|
||||
val hexUpper = bytes.toHexString(HexFormat.UpperCase)
|
||||
@@ -102,7 +101,7 @@ class HexEncodingTest {
|
||||
@Test
|
||||
fun testRandomsUppercase() {
|
||||
for (i in 0..1000) {
|
||||
val bytes = CryptoUtils.privkeyCreate()
|
||||
val bytes = RandomInstance.bytes(32)
|
||||
val hex = bytes.toHexString(HexFormat.UpperCase)
|
||||
assertEquals(
|
||||
bytes.toList(),
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip04Dm.Nip04Encoder
|
||||
import com.vitorpamplona.quartz.nip04Dm.Nip04Encryption
|
||||
import com.vitorpamplona.quartz.nip06KeyDerivation.Nip06
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44v2
|
||||
import com.vitorpamplona.quartz.nip49PrivKeyEnc.Nip49
|
||||
import com.vitorpamplona.quartz.utils.nextBytes
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.SecureRandom
|
||||
|
||||
object CryptoUtils {
|
||||
private val secp256k1 = Secp256k1.get()
|
||||
private val random = SecureRandom()
|
||||
|
||||
val nip01 = Nip01(secp256k1, random)
|
||||
val nip06 = Nip06(secp256k1)
|
||||
val nip04 = Nip04Encryption(secp256k1, random)
|
||||
val nip44 = Nip44(secp256k1, random, nip04)
|
||||
val nip49 = Nip49(secp256k1, random)
|
||||
|
||||
fun clearCache() {
|
||||
nip04.clearCache()
|
||||
nip44.clearCache()
|
||||
}
|
||||
|
||||
/** Provides a 32B "private key" aka random number */
|
||||
fun privkeyCreate() = nip01.privkeyCreate()
|
||||
|
||||
fun pubkeyCreate(privKey: ByteArray) = nip01.pubkeyCreate(privKey)
|
||||
|
||||
fun randomInt(bound: Int) = random.nextInt(bound)
|
||||
|
||||
fun random(size: Int) = random.nextBytes(size)
|
||||
|
||||
fun signString(
|
||||
message: String,
|
||||
privKey: ByteArray,
|
||||
auxrand32: ByteArray = random(32),
|
||||
): ByteArray = nip01.signString(message, privKey, auxrand32)
|
||||
|
||||
fun sign(
|
||||
data: ByteArray,
|
||||
privKey: ByteArray,
|
||||
auxrand32: ByteArray? = null,
|
||||
): ByteArray = nip01.sign(data, privKey, auxrand32)
|
||||
|
||||
fun verifySignature(
|
||||
signature: ByteArray,
|
||||
hash: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): Boolean = nip01.verify(signature, hash, pubKey)
|
||||
|
||||
fun sha256(data: ByteArray): ByteArray {
|
||||
// Creates a new buffer every time
|
||||
return sha256Hash(data)
|
||||
}
|
||||
|
||||
fun decrypt(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? =
|
||||
if (Nip04Encryption.isNIP04(msg)) {
|
||||
decryptNIP04(msg, privateKey, pubKey)
|
||||
} else {
|
||||
decryptNIP44(msg, privateKey, pubKey)
|
||||
}
|
||||
|
||||
/** NIP 04 Utils */
|
||||
fun encryptNIP04(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = nip04.encrypt(msg, privateKey, pubKey)
|
||||
|
||||
fun encryptNIP04(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): Nip04Encoder = nip04.encrypt(msg, sharedSecret)
|
||||
|
||||
fun decryptNIP04(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = nip04.decrypt(msg, privateKey, pubKey)
|
||||
|
||||
fun decryptNIP04(
|
||||
encryptedInfo: Nip04Encoder,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
|
||||
fun decryptNIP04(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): String = nip04.decrypt(msg, sharedSecret)
|
||||
|
||||
private fun decryptNIP04(
|
||||
cipher: String,
|
||||
nonce: String,
|
||||
sharedSecret: ByteArray,
|
||||
): String = nip04.decrypt(cipher, nonce, sharedSecret)
|
||||
|
||||
private fun decryptNIP04(
|
||||
encryptedMsg: ByteArray,
|
||||
iv: ByteArray,
|
||||
sharedSecret: ByteArray,
|
||||
): String = nip04.decrypt(encryptedMsg, iv, sharedSecret)
|
||||
|
||||
fun getSharedSecretNIP04(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray = nip04.getSharedSecret(privateKey, pubKey)
|
||||
|
||||
fun computeSharedSecretNIP04(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray = nip04.computeSharedSecret(privateKey, pubKey)
|
||||
|
||||
/** 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,
|
||||
): Nip44v2.EncryptedInfo = nip44.encrypt(msg, privateKey, pubKey)
|
||||
|
||||
fun decryptNIP44(
|
||||
payload: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String? = nip44.decrypt(payload, privateKey, pubKey)
|
||||
|
||||
/** NIP 49 Utils */
|
||||
fun decryptNIP49(
|
||||
payload: String,
|
||||
password: String,
|
||||
): String? {
|
||||
if (payload.isEmpty() || password.isEmpty()) return null
|
||||
return nip49.decrypt(payload, password)
|
||||
}
|
||||
|
||||
fun encryptNIP49(
|
||||
key: HexKey,
|
||||
password: String,
|
||||
): String = nip49.encrypt(key, password)
|
||||
}
|
||||
@@ -42,7 +42,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
package com.vitorpamplona.quartz.nip01Core
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
|
||||
fun Event.generateId(): String = EventHasher.hashId(pubKey, createdAt, kind, tags, content)
|
||||
@@ -34,7 +34,7 @@ fun Event.verifyId(): Boolean {
|
||||
|
||||
fun Event.verifySignature(): Boolean {
|
||||
if (id.isEmpty() || sig.isEmpty()) return false
|
||||
return CryptoUtils.verifySignature(Hex.decode(sig), Hex.decode(id), Hex.decode(pubKey))
|
||||
return Nip01.verify(Hex.decode(sig), Hex.decode(id), Hex.decode(pubKey))
|
||||
}
|
||||
|
||||
/** Checks if the ID is correct and then if the pubKey's secret key signed the event. */
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip01Core
|
||||
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
|
||||
class EventHasher {
|
||||
companion object {
|
||||
@@ -61,7 +61,7 @@ class EventHasher {
|
||||
kind: Int,
|
||||
tags: Array<Array<String>>,
|
||||
content: String,
|
||||
): ByteArray = sha256Hash(makeJsonForId(pubKey, createdAt, kind, tags, content).toByteArray())
|
||||
): ByteArray = sha256(makeJsonForId(pubKey, createdAt, kind, tags, content).toByteArray())
|
||||
|
||||
fun hashId(
|
||||
pubKey: HexKey,
|
||||
|
||||
@@ -20,13 +20,11 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.crypto
|
||||
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
|
||||
class KeyPair(
|
||||
privKey: ByteArray? = null,
|
||||
pubKey: ByteArray? = null,
|
||||
forcePubKeyCheck: Boolean = true,
|
||||
) {
|
||||
val privKey: ByteArray?
|
||||
val pubKey: ByteArray
|
||||
@@ -35,8 +33,8 @@ class KeyPair(
|
||||
if (privKey == null) {
|
||||
if (pubKey == null) {
|
||||
// create new, random keys
|
||||
this.privKey = CryptoUtils.privkeyCreate()
|
||||
this.pubKey = CryptoUtils.pubkeyCreate(this.privKey)
|
||||
this.privKey = Nip01.privKeyCreate()
|
||||
this.pubKey = Nip01.pubKeyCreate(this.privKey)
|
||||
} else {
|
||||
// this is a read-only account
|
||||
check(pubKey.size == 32)
|
||||
@@ -46,12 +44,7 @@ class KeyPair(
|
||||
} else {
|
||||
// as private key is provided, ignore the public key and set keys according to private key
|
||||
this.privKey = privKey
|
||||
|
||||
if (pubKey == null || forcePubKeyCheck) {
|
||||
this.pubKey = CryptoUtils.pubkeyCreate(privKey)
|
||||
} else {
|
||||
this.pubKey = pubKey
|
||||
}
|
||||
this.pubKey = Nip01.pubKeyCreate(privKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,45 +20,23 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip01Core.crypto
|
||||
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.utils.nextBytes
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.SecureRandom
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import com.vitorpamplona.quartz.utils.Secp256k1Instance
|
||||
|
||||
class Nip01(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
/** Provides a 32B "private key" aka random number */
|
||||
fun privkeyCreate() = random.nextBytes(32)
|
||||
object Nip01 {
|
||||
fun privKeyCreate() = RandomInstance.bytes(32)
|
||||
|
||||
fun compressedPubkeyCreate(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey))
|
||||
|
||||
fun pubkeyCreate(privKey: ByteArray) = compressedPubkeyCreate(privKey).copyOfRange(1, 33)
|
||||
fun pubKeyCreate(privKey: ByteArray) = Secp256k1Instance.compressedPubKeyFor(privKey).copyOfRange(1, 33)
|
||||
|
||||
fun sign(
|
||||
data: ByteArray,
|
||||
privKey: ByteArray,
|
||||
nonce: ByteArray? = random.nextBytes(32),
|
||||
): ByteArray = secp256k1.signSchnorr(data, privKey, nonce)
|
||||
|
||||
fun signDeterministic(
|
||||
data: ByteArray,
|
||||
privKey: ByteArray,
|
||||
): ByteArray = secp256k1.signSchnorr(data, privKey, null)
|
||||
nonce: ByteArray? = RandomInstance.bytes(32),
|
||||
): ByteArray = Secp256k1Instance.signSchnorr(data, privKey, nonce)
|
||||
|
||||
fun verify(
|
||||
signature: ByteArray,
|
||||
hash: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): Boolean = secp256k1.verifySchnorr(signature, hash, pubKey)
|
||||
|
||||
fun sha256(data: ByteArray) = sha256Hash(data)
|
||||
|
||||
fun signString(
|
||||
message: String,
|
||||
privKey: ByteArray,
|
||||
nonce: ByteArray = random.nextBytes(32),
|
||||
): ByteArray = sign(CryptoUtils.sha256(message.toByteArray()), privKey, nonce)
|
||||
): Boolean = Secp256k1Instance.verifySchnorr(signature, hash, pubKey)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import com.vitorpamplona.quartz.EventFactory
|
||||
import com.vitorpamplona.quartz.nip01Core.EventHasher
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip04Dm.Nip04Encryption
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.EncryptedInfo
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||
|
||||
@@ -78,7 +78,7 @@ abstract class NostrSigner(
|
||||
fromPublicKey: HexKey,
|
||||
onReady: (String) -> Unit,
|
||||
) {
|
||||
if (Nip04Encryption.isNIP04(encryptedContent)) {
|
||||
if (EncryptedInfo.isNIP04(encryptedContent)) {
|
||||
nip04Decrypt(encryptedContent, fromPublicKey, onReady)
|
||||
} else {
|
||||
nip44Decrypt(encryptedContent, fromPublicKey, onReady)
|
||||
|
||||
+13
-15
@@ -21,14 +21,16 @@
|
||||
package com.vitorpamplona.quartz.nip01Core.signers
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.EventFactory
|
||||
import com.vitorpamplona.quartz.nip01Core.EventHasher
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.Nip04
|
||||
import com.vitorpamplona.quartz.nip44Encryption.Nip44
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.PrivateZapRequestBuilder
|
||||
@@ -69,7 +71,7 @@ class NostrSignerSync(
|
||||
if (keyPair.privKey == null) return null
|
||||
|
||||
val id = EventHasher.hashIdBytes(pubKey, createdAt, kind, tags, content)
|
||||
val sig = CryptoUtils.sign(id, keyPair.privKey).toHexKey()
|
||||
val sig = Nip01.sign(id, keyPair.privKey).toHexKey()
|
||||
|
||||
return EventFactory.create(
|
||||
id.toHexKey(),
|
||||
@@ -88,7 +90,7 @@ class NostrSignerSync(
|
||||
): String? {
|
||||
if (keyPair.privKey == null) return null
|
||||
|
||||
return CryptoUtils.encryptNIP04(
|
||||
return Nip04.encrypt(
|
||||
decryptedContent,
|
||||
keyPair.privKey,
|
||||
toPublicKey.hexToByteArray(),
|
||||
@@ -102,10 +104,7 @@ class NostrSignerSync(
|
||||
if (keyPair.privKey == null) return null
|
||||
|
||||
return try {
|
||||
val sharedSecret =
|
||||
CryptoUtils.getSharedSecretNIP04(keyPair.privKey, fromPublicKey.hexToByteArray())
|
||||
|
||||
CryptoUtils.decryptNIP04(encryptedContent, sharedSecret)
|
||||
Nip04.decrypt(encryptedContent, keyPair.privKey, fromPublicKey.hexToByteArray())
|
||||
} catch (e: Exception) {
|
||||
Log.w("NIP04Decrypt", "Error decrypting the message ${e.message} on $encryptedContent")
|
||||
null
|
||||
@@ -118,8 +117,8 @@ class NostrSignerSync(
|
||||
): String? {
|
||||
if (keyPair.privKey == null) return null
|
||||
|
||||
return CryptoUtils
|
||||
.encryptNIP44(
|
||||
return Nip44
|
||||
.encrypt(
|
||||
decryptedContent,
|
||||
keyPair.privKey,
|
||||
toPublicKey.hexToByteArray(),
|
||||
@@ -132,12 +131,11 @@ class NostrSignerSync(
|
||||
): String? {
|
||||
if (keyPair.privKey == null) return null
|
||||
|
||||
return CryptoUtils
|
||||
.decryptNIP44(
|
||||
payload = encryptedContent,
|
||||
privateKey = keyPair.privKey,
|
||||
pubKey = fromPublicKey.hexToByteArray(),
|
||||
)
|
||||
return Nip44.decrypt(
|
||||
payload = encryptedContent,
|
||||
privateKey = keyPair.privKey,
|
||||
pubKey = fromPublicKey.hexToByteArray(),
|
||||
)
|
||||
}
|
||||
|
||||
fun decryptZapEvent(event: LnZapRequestEvent): LnZapPrivateEvent? = PrivateZapRequestBuilder().decryptZapEvent(event, this)
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/**
|
||||
* 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.nip04Dm
|
||||
|
||||
import com.vitorpamplona.quartz.nip44Encryption.SharedKeyCache
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.SecureRandom
|
||||
import java.util.Base64
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class Nip04Encryption(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
private val sharedKeyCache = SharedKeyCache()
|
||||
private val h02 = Hex.decode("02")
|
||||
|
||||
fun clearCache() {
|
||||
sharedKeyCache.clearCache()
|
||||
}
|
||||
|
||||
fun encrypt(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = encrypt(msg, getSharedSecret(privateKey, pubKey)).encodeToNIP04()
|
||||
|
||||
fun encrypt(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): Nip04Encoder {
|
||||
val iv = ByteArray(16)
|
||||
random.nextBytes(iv)
|
||||
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec(iv))
|
||||
val encryptedMsg = cipher.doFinal(msg.toByteArray())
|
||||
return Nip04Encoder(encryptedMsg, iv)
|
||||
}
|
||||
|
||||
fun decrypt(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String {
|
||||
val sharedSecret = getSharedSecret(privateKey, pubKey)
|
||||
return decrypt(msg, sharedSecret)
|
||||
}
|
||||
|
||||
fun decrypt(
|
||||
encryptedInfo: Nip04Encoder,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String {
|
||||
val sharedSecret = getSharedSecret(privateKey, pubKey)
|
||||
return decrypt(encryptedInfo.ciphertext, encryptedInfo.nonce, sharedSecret)
|
||||
}
|
||||
|
||||
fun decrypt(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): String {
|
||||
val decoded = Nip04Encoder.decodeFromNIP04(msg)
|
||||
check(decoded != null) { "Unable to decode msg $msg as NIP04" }
|
||||
return decrypt(decoded.ciphertext, decoded.nonce, sharedSecret)
|
||||
}
|
||||
|
||||
fun decrypt(
|
||||
cipher: String,
|
||||
nonce: String,
|
||||
sharedSecret: ByteArray,
|
||||
): String {
|
||||
val iv = Base64.getDecoder().decode(nonce)
|
||||
val encryptedMsg = Base64.getDecoder().decode(cipher)
|
||||
return decrypt(encryptedMsg, iv, sharedSecret)
|
||||
}
|
||||
|
||||
fun decrypt(
|
||||
encryptedMsg: ByteArray,
|
||||
iv: ByteArray,
|
||||
sharedSecret: ByteArray,
|
||||
): String {
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sharedSecret, "AES"), IvParameterSpec(iv))
|
||||
return String(cipher.doFinal(encryptedMsg))
|
||||
}
|
||||
|
||||
fun getSharedSecret(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray {
|
||||
val preComputed = sharedKeyCache.get(privateKey, pubKey)
|
||||
if (preComputed != null) return preComputed
|
||||
|
||||
val computed = computeSharedSecret(privateKey, pubKey)
|
||||
sharedKeyCache.add(privateKey, pubKey, computed)
|
||||
return computed
|
||||
}
|
||||
|
||||
/** @return 32B shared secret */
|
||||
fun computeSharedSecret(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray = secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33)
|
||||
|
||||
companion object {
|
||||
fun isNIP04(encoded: String) = Nip04Encoder.isNIP04(encoded)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.nip04Dm.crypto
|
||||
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.encryption.NostrCipher
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class AESCBC(
|
||||
val keyBytes: ByteArray = RandomInstance.bytes(32),
|
||||
val iv: ByteArray = RandomInstance.bytes(16),
|
||||
) : NostrCipher {
|
||||
private fun newCipher() = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
|
||||
private fun keySpec() = SecretKeySpec(keyBytes, "AES")
|
||||
|
||||
private fun param() = IvParameterSpec(iv)
|
||||
|
||||
override fun name() = NAME
|
||||
|
||||
override fun encrypt(bytesToEncrypt: ByteArray): ByteArray =
|
||||
with(newCipher()) {
|
||||
init(Cipher.ENCRYPT_MODE, keySpec(), param())
|
||||
doFinal(bytesToEncrypt)
|
||||
}
|
||||
|
||||
override fun decrypt(bytesToDecrypt: ByteArray): ByteArray =
|
||||
with(newCipher()) {
|
||||
init(Cipher.DECRYPT_MODE, keySpec(), param())
|
||||
doFinal(bytesToDecrypt)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "aes-cbc"
|
||||
}
|
||||
}
|
||||
+15
-24
@@ -18,32 +18,20 @@
|
||||
* 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.nip04Dm
|
||||
package com.vitorpamplona.quartz.nip04Dm.crypto
|
||||
|
||||
import android.util.Log
|
||||
import java.util.Base64
|
||||
|
||||
class Nip04Encoder(
|
||||
class EncryptedInfo(
|
||||
val ciphertext: ByteArray,
|
||||
val nonce: ByteArray,
|
||||
) {
|
||||
fun encodeToNIP04() = encode(ciphertext, nonce)
|
||||
|
||||
companion object {
|
||||
const val V: Int = 0
|
||||
|
||||
fun decodePayload(payload: String): Nip04Encoder? {
|
||||
return try {
|
||||
val byteArray = Base64.getDecoder().decode(payload)
|
||||
check(byteArray[0].toInt() == V)
|
||||
return Nip04Encoder(
|
||||
nonce = byteArray.copyOfRange(1, 25),
|
||||
ciphertext = byteArray.copyOfRange(25, byteArray.size),
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.w("NIP04", "Unable to Parse encrypted payload: $payload")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun isNIP04(encoded: String): Boolean {
|
||||
// cleaning up some bug from some client.
|
||||
val cleanedUp = encoded.removeSuffix("-null")
|
||||
@@ -56,11 +44,20 @@ class Nip04Encoder(
|
||||
cleanedUp[l - 25] == '='
|
||||
}
|
||||
|
||||
fun decodeFromNIP04(payload: String): Nip04Encoder? =
|
||||
fun encode(
|
||||
ciphertext: ByteArray,
|
||||
nonce: ByteArray,
|
||||
): String {
|
||||
val nonceB64 = Base64.getEncoder().encodeToString(nonce)
|
||||
val ciphertextB64 = Base64.getEncoder().encodeToString(ciphertext)
|
||||
return "$ciphertextB64?iv=$nonceB64"
|
||||
}
|
||||
|
||||
fun decode(payload: String): EncryptedInfo? =
|
||||
try {
|
||||
// cleaning up some bug from some client.
|
||||
val parts = payload.removeSuffix("-null").split("?iv=")
|
||||
Nip04Encoder(
|
||||
EncryptedInfo(
|
||||
ciphertext = Base64.getDecoder().decode(parts[0]),
|
||||
nonce = Base64.getDecoder().decode(parts[1]),
|
||||
)
|
||||
@@ -69,10 +66,4 @@ class Nip04Encoder(
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun encodeToNIP04(): String {
|
||||
val nonce = Base64.getEncoder().encodeToString(nonce)
|
||||
val ciphertext = Base64.getEncoder().encodeToString(ciphertext)
|
||||
return "$ciphertext?iv=$nonce"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* 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.nip04Dm.crypto
|
||||
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import com.vitorpamplona.quartz.utils.Secp256k1Instance
|
||||
|
||||
class Encryption {
|
||||
fun encrypt(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = encrypt(msg, computeSharedSecret(privateKey, pubKey))
|
||||
|
||||
fun decrypt(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = decrypt(msg, computeSharedSecret(privateKey, pubKey))
|
||||
|
||||
fun encrypt(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): String = encryptToEncoder(msg, sharedSecret).encodeToNIP04()
|
||||
|
||||
fun encryptToEncoder(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): EncryptedInfo {
|
||||
val iv = RandomInstance.bytes(16)
|
||||
return EncryptedInfo(AESCBC(sharedSecret, iv).encrypt(msg.toByteArray()), iv)
|
||||
}
|
||||
|
||||
fun decrypt(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): String {
|
||||
val decoded = EncryptedInfo.decode(msg)
|
||||
check(decoded != null) { "Unable to decode msg $msg as NIP04" }
|
||||
return decrypt(decoded, sharedSecret)
|
||||
}
|
||||
|
||||
fun decrypt(
|
||||
msg: EncryptedInfo,
|
||||
sharedSecret: ByteArray,
|
||||
): String = decrypt(msg.ciphertext, msg.nonce, sharedSecret)
|
||||
|
||||
fun decrypt(
|
||||
encryptedMsg: ByteArray,
|
||||
iv: ByteArray,
|
||||
sharedSecret: ByteArray,
|
||||
): String = String(AESCBC(sharedSecret, iv).decrypt(encryptedMsg))
|
||||
|
||||
fun computeSharedSecret(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray = Secp256k1Instance.pubKeyTweakMulCompact(pubKey, privateKey)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.nip04Dm.crypto
|
||||
|
||||
import com.vitorpamplona.quartz.nip44Encryption.SharedKeyCache
|
||||
|
||||
object Nip04 {
|
||||
private val sharedKeyCache = SharedKeyCache()
|
||||
|
||||
private val nip04 = Encryption()
|
||||
|
||||
fun clearCache() {
|
||||
sharedKeyCache.clearCache()
|
||||
}
|
||||
|
||||
fun getSharedSecret(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray {
|
||||
val preComputed = sharedKeyCache.get(privateKey, pubKey)
|
||||
if (preComputed != null) return preComputed
|
||||
|
||||
val computed = nip04.computeSharedSecret(privateKey, pubKey)
|
||||
sharedKeyCache.add(privateKey, pubKey, computed)
|
||||
return computed
|
||||
}
|
||||
|
||||
fun encrypt(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = nip04.encrypt(msg, getSharedSecret(privateKey, pubKey))
|
||||
|
||||
fun decrypt(
|
||||
msg: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = nip04.decrypt(msg, getSharedSecret(privateKey, pubKey))
|
||||
|
||||
fun decrypt(
|
||||
msg: EncryptedInfo,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): String = nip04.decrypt(msg, getSharedSecret(privateKey, pubKey))
|
||||
}
|
||||
+1
-1
@@ -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.nip04Dm
|
||||
package com.vitorpamplona.quartz.nip04Dm.messages
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.experimental.inlineMetadata.Nip54InlineMetadata
|
||||
+1
-1
@@ -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.nip04Dm
|
||||
package com.vitorpamplona.quartz.nip04Dm.messages
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.EventHintBundle
|
||||
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
|
||||
+7
-28
@@ -20,16 +20,13 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip06KeyDerivation
|
||||
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import com.vitorpamplona.quartz.utils.Secp256k1Instance
|
||||
import com.vitorpamplona.quartz.utils.hmac512
|
||||
|
||||
/*
|
||||
Simplified from: https://github.com/ACINQ/bitcoin-kmp/
|
||||
*/
|
||||
class Bip32SeedDerivation(
|
||||
val secp256k1: Secp256k1,
|
||||
) {
|
||||
class Bip32SeedDerivation {
|
||||
class ExtendedPrivateKey(
|
||||
val secretkeybytes: ByteArray,
|
||||
val chaincode: ByteArray,
|
||||
@@ -46,24 +43,6 @@ class Bip32SeedDerivation(
|
||||
return ExtendedPrivateKey(il, ir)
|
||||
}
|
||||
|
||||
fun hmac512(
|
||||
key: ByteArray,
|
||||
data: ByteArray,
|
||||
): ByteArray {
|
||||
val mac = Mac.getInstance("HmacSHA512")
|
||||
mac.init(SecretKeySpec(key, "HmacSHA512"))
|
||||
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,
|
||||
@@ -73,17 +52,17 @@ class Bip32SeedDerivation(
|
||||
val data = arrayOf(0.toByte()).toByteArray() + parent.secretkeybytes + writeInt32BE(index.toInt())
|
||||
hmac512(parent.chaincode, data)
|
||||
} else {
|
||||
val data = pubkeyCreateBitcoin(parent.secretkeybytes) + writeInt32BE(index.toInt())
|
||||
val data = Secp256k1Instance.compressedPubKeyFor(parent.secretkeybytes) + writeInt32BE(index.toInt())
|
||||
hmac512(parent.chaincode, data)
|
||||
}
|
||||
val il = i.take(32).toByteArray()
|
||||
val ir = i.takeLast(32).toByteArray()
|
||||
|
||||
require(isPrivKeyValid(il)) { "cannot generate child private key: IL is invalid" }
|
||||
require(Secp256k1Instance.isPrivateKeyValid(il)) { "cannot generate child private key: IL is invalid" }
|
||||
|
||||
val key = sum(il, parent.secretkeybytes)
|
||||
val key = Secp256k1Instance.privateKeyAdd(il, parent.secretkeybytes)
|
||||
|
||||
require(isPrivKeyValid(key)) { "cannot generate child private key: resulting private key is invalid" }
|
||||
require(Secp256k1Instance.isPrivateKeyValid(key)) { "cannot generate child private key: resulting private key is invalid" }
|
||||
|
||||
return ExtendedPrivateKey(key, ir)
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
package com.vitorpamplona.quartz.nip06KeyDerivation
|
||||
|
||||
import com.vitorpamplona.quartz.nip49PrivKeyEnc.PBKDF
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
|
||||
// CODE FROM: https://github.com/ACINQ/bitcoin-kmp/
|
||||
|
||||
@@ -79,7 +79,7 @@ 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(sha256Hash(data)).take(data.size / 4)
|
||||
val check = toBinary(sha256(data)).take(data.size / 4)
|
||||
require(check == checksumbits) { "invalid checksum" }
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ object Bip39Mnemonics {
|
||||
wordlist: Array<String>,
|
||||
): List<String> {
|
||||
require(wordlist.size == 2048) { "invalid word list (size should be 2048)" }
|
||||
val digits = toBinary(entropy) + toBinary(sha256Hash(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,12 +20,8 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip06KeyDerivation
|
||||
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
|
||||
class Nip06(
|
||||
val secp256k1: Secp256k1,
|
||||
) {
|
||||
val derivation = Bip32SeedDerivation(secp256k1)
|
||||
class Nip06 {
|
||||
val derivation = Bip32SeedDerivation()
|
||||
|
||||
// m/44'/1237'/<account>'/0/0
|
||||
private val nip6Base: KeyPath =
|
||||
|
||||
@@ -20,15 +20,15 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip17Dm.files.encryption
|
||||
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.GCMParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
class AESGCM(
|
||||
val keyBytes: ByteArray = CryptoUtils.random(32),
|
||||
val nonce: ByteArray = CryptoUtils.random(16),
|
||||
val keyBytes: ByteArray = RandomInstance.bytes(32),
|
||||
val nonce: ByteArray = RandomInstance.bytes(16),
|
||||
) : NostrCipher {
|
||||
private fun newCipher() = Cipher.getInstance("AES/GCM/NoPadding")
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip19Bech32
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
|
||||
@@ -152,7 +152,7 @@ object Nip19Parser {
|
||||
|
||||
fun decodePublicKey(key: String): ByteArray =
|
||||
when (val parsed = Nip19Parser.uriToRoute(key)?.entity) {
|
||||
is NSec -> KeyPair(privKey = key.bechToBytes()).pubKey
|
||||
is NSec -> Nip01.pubKeyCreate(parsed.hex.hexToByteArray())
|
||||
is NPub -> parsed.hex.hexToByteArray()
|
||||
is NProfile -> parsed.hex.hexToByteArray()
|
||||
else -> Hex.decode(key) // crashes on purpose
|
||||
@@ -179,7 +179,7 @@ fun decodePrivateKeyAsHexOrNull(key: String): HexKey? =
|
||||
fun decodePublicKeyAsHexOrNull(key: String): HexKey? =
|
||||
try {
|
||||
when (val parsed = Nip19Parser.uriToRoute(key)?.entity) {
|
||||
is NSec -> KeyPair(privKey = key.bechToBytes()).pubKey.toHexKey()
|
||||
is NSec -> Nip01.pubKeyCreate(parsed.hex.hexToByteArray()).toHexKey()
|
||||
is NPub -> parsed.hex
|
||||
is NProfile -> parsed.hex
|
||||
is Note -> null
|
||||
|
||||
+7
-4
@@ -18,8 +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.utils
|
||||
package com.vitorpamplona.quartz.nip44Encryption
|
||||
|
||||
import java.security.SecureRandom
|
||||
|
||||
fun SecureRandom.nextBytes(size: Int) = ByteArray(size).also { nextBytes(it) }
|
||||
class EncryptedInfoString(
|
||||
val ciphertext: String,
|
||||
val nonce: String,
|
||||
val v: Int,
|
||||
val mac: String?,
|
||||
)
|
||||
@@ -22,36 +22,19 @@ package com.vitorpamplona.quartz.nip44Encryption
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
|
||||
import com.vitorpamplona.quartz.nip04Dm.Nip04Encoder
|
||||
import com.vitorpamplona.quartz.nip04Dm.Nip04Encryption
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.SecureRandom
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.EncryptedInfo
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.Nip04
|
||||
import java.util.Base64
|
||||
|
||||
class Nip44(
|
||||
secp256k1: Secp256k1,
|
||||
random: SecureRandom,
|
||||
val nip04: Nip04Encryption,
|
||||
) {
|
||||
public val v1 = Nip44v1(secp256k1, random)
|
||||
public val v2 = Nip44v2(secp256k1, random)
|
||||
object Nip44 {
|
||||
val v1 = Nip44v1()
|
||||
val v2 = Nip44v2()
|
||||
|
||||
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,
|
||||
@@ -75,14 +58,7 @@ class Nip44(
|
||||
}
|
||||
}
|
||||
|
||||
class EncryptedInfoString(
|
||||
val ciphertext: String,
|
||||
val nonce: String,
|
||||
val v: Int,
|
||||
val mac: String?,
|
||||
)
|
||||
|
||||
fun decryptNIP44FromJackson(
|
||||
private fun decryptNIP44FromJackson(
|
||||
json: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
@@ -97,13 +73,13 @@ class Nip44(
|
||||
}
|
||||
|
||||
return when (info.v) {
|
||||
Nip04Encoder.V -> {
|
||||
EncryptedInfo.V -> {
|
||||
val encryptedInfo =
|
||||
Nip04Encoder(
|
||||
EncryptedInfo(
|
||||
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||
nonce = Base64.getDecoder().decode(info.nonce),
|
||||
)
|
||||
nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
Nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
||||
}
|
||||
|
||||
Nip44v1.EncryptedInfo.V -> {
|
||||
@@ -129,7 +105,7 @@ class Nip44(
|
||||
}
|
||||
}
|
||||
|
||||
fun decryptNIP44FromBase64(
|
||||
private fun decryptNIP44FromBase64(
|
||||
payload: String,
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
@@ -146,7 +122,7 @@ class Nip44(
|
||||
}
|
||||
|
||||
return when (byteArray[0].toInt()) {
|
||||
Nip04Encoder.V -> nip04.decrypt(payload, privateKey, pubKey)
|
||||
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
|
||||
|
||||
@@ -21,22 +21,14 @@
|
||||
package com.vitorpamplona.quartz.nip44Encryption
|
||||
|
||||
import android.util.Log
|
||||
import com.goterl.lazysodium.SodiumAndroid
|
||||
import com.goterl.lazysodium.utils.Key
|
||||
import com.vitorpamplona.quartz.nip44Encryption.crypto.cryptoStreamXChaCha20Xor
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.SecureRandom
|
||||
import com.vitorpamplona.quartz.utils.LibSodiumInstance
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import com.vitorpamplona.quartz.utils.Secp256k1Instance
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import java.util.Base64
|
||||
|
||||
class Nip44v1(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
class Nip44v1 {
|
||||
private val sharedKeyCache = SharedKeyCache()
|
||||
private val h02 = Hex.decode("02")
|
||||
private val libSodium = SodiumAndroid()
|
||||
|
||||
fun clearCache() {
|
||||
sharedKeyCache.clearCache()
|
||||
@@ -55,15 +47,13 @@ class Nip44v1(
|
||||
msg: String,
|
||||
sharedSecret: ByteArray,
|
||||
): EncryptedInfo {
|
||||
val nonce = ByteArray(24)
|
||||
random.nextBytes(nonce)
|
||||
val nonce = RandomInstance.bytes(24)
|
||||
|
||||
val cipher =
|
||||
cryptoStreamXChaCha20Xor(
|
||||
libSodium = libSodium,
|
||||
LibSodiumInstance.cryptoStreamXChaCha20Xor(
|
||||
messageBytes = msg.toByteArray(),
|
||||
nonce = nonce,
|
||||
key = Key.fromBytes(sharedSecret),
|
||||
key = sharedSecret,
|
||||
)
|
||||
|
||||
return EncryptedInfo(
|
||||
@@ -102,12 +92,12 @@ class Nip44v1(
|
||||
encryptedInfo: EncryptedInfo,
|
||||
sharedSecret: ByteArray,
|
||||
): String? =
|
||||
cryptoStreamXChaCha20Xor(
|
||||
libSodium = libSodium,
|
||||
messageBytes = encryptedInfo.ciphertext,
|
||||
nonce = encryptedInfo.nonce,
|
||||
key = Key.fromBytes(sharedSecret),
|
||||
)?.decodeToString()
|
||||
LibSodiumInstance
|
||||
.cryptoStreamXChaCha20Xor(
|
||||
messageBytes = encryptedInfo.ciphertext,
|
||||
nonce = encryptedInfo.nonce,
|
||||
key = sharedSecret,
|
||||
)?.decodeToString()
|
||||
|
||||
fun getSharedSecret(
|
||||
privateKey: ByteArray,
|
||||
@@ -126,8 +116,8 @@ class Nip44v1(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray =
|
||||
sha256Hash(
|
||||
secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33),
|
||||
sha256(
|
||||
Secp256k1Instance.pubKeyTweakMulCompact(pubKey, privateKey),
|
||||
)
|
||||
|
||||
class EncryptedInfo(
|
||||
|
||||
@@ -21,30 +21,21 @@
|
||||
package com.vitorpamplona.quartz.nip44Encryption
|
||||
|
||||
import android.util.Log
|
||||
import com.goterl.lazysodium.LazySodiumAndroid
|
||||
import com.goterl.lazysodium.SodiumAndroid
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip44Encryption.crypto.Hkdf
|
||||
import com.vitorpamplona.quartz.utils.Hex
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import com.vitorpamplona.quartz.utils.LibSodiumInstance
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import com.vitorpamplona.quartz.utils.Secp256k1Instance
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.security.SecureRandom
|
||||
import java.util.Base64
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.log2
|
||||
|
||||
class Nip44v2(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
class Nip44v2 {
|
||||
private val sharedKeyCache = SharedKeyCache()
|
||||
|
||||
private val libSodium = SodiumAndroid()
|
||||
private val lazySodium = LazySodiumAndroid(libSodium)
|
||||
private val hkdf = Hkdf()
|
||||
|
||||
private val h02 = Hex.decode("02")
|
||||
private val saltPrefix = "nip44-v2".toByteArray(Charsets.UTF_8)
|
||||
private val hashLength = 32
|
||||
|
||||
@@ -65,8 +56,7 @@ class Nip44v2(
|
||||
plaintext: String,
|
||||
conversationKey: ByteArray,
|
||||
): EncryptedInfo {
|
||||
val nonce = ByteArray(hashLength)
|
||||
random.nextBytes(nonce)
|
||||
val nonce = RandomInstance.bytes(hashLength)
|
||||
return encryptWithNonce(plaintext, conversationKey, nonce)
|
||||
}
|
||||
|
||||
@@ -78,15 +68,12 @@ class Nip44v2(
|
||||
val messageKeys = getMessageKeys(conversationKey, nonce)
|
||||
val padded = pad(plaintext)
|
||||
|
||||
val ciphertext = ByteArray(padded.size)
|
||||
|
||||
lazySodium.cryptoStreamChaCha20IetfXor(
|
||||
ciphertext,
|
||||
padded,
|
||||
padded.size.toLong(),
|
||||
messageKeys.chachaNonce,
|
||||
messageKeys.chachaKey,
|
||||
)
|
||||
val ciphertext =
|
||||
LibSodiumInstance.cryptoStreamChaCha20IetfXor(
|
||||
padded,
|
||||
messageKeys.chachaNonce,
|
||||
messageKeys.chachaKey,
|
||||
)
|
||||
|
||||
val mac = hmacAad(messageKeys.hmacKey, ciphertext, nonce)
|
||||
|
||||
@@ -128,16 +115,12 @@ class Nip44v2(
|
||||
"Invalid Mac: Calculated ${calculatedMac.toHexKey()}, decoded: ${decoded.mac.toHexKey()}"
|
||||
}
|
||||
|
||||
val mLen = decoded.ciphertext.size.toLong()
|
||||
val padded = ByteArray(decoded.ciphertext.size)
|
||||
|
||||
lazySodium.cryptoStreamChaCha20IetfXor(
|
||||
padded,
|
||||
decoded.ciphertext,
|
||||
mLen,
|
||||
messageKey.chachaNonce,
|
||||
messageKey.chachaKey,
|
||||
)
|
||||
val padded =
|
||||
LibSodiumInstance.cryptoStreamChaCha20IetfXor(
|
||||
decoded.ciphertext,
|
||||
messageKey.chachaNonce,
|
||||
messageKey.chachaKey,
|
||||
)
|
||||
|
||||
return unpad(padded)
|
||||
}
|
||||
@@ -241,7 +224,7 @@ class Nip44v2(
|
||||
privateKey: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): ByteArray {
|
||||
val sharedX = secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33)
|
||||
val sharedX = Secp256k1Instance.pubKeyTweakMulCompact(pubKey, privateKey)
|
||||
return hkdf.extract(sharedX, saltPrefix)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
/**
|
||||
* 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.nip44Encryption.crypto
|
||||
|
||||
import com.goterl.lazysodium.SodiumAndroid
|
||||
import com.goterl.lazysodium.utils.Key
|
||||
|
||||
/**
|
||||
* I initially extended these methods from the Sodium and SodiumAndroid classes But JNI doesn't like
|
||||
* it. There is some native method overriding bug when using Kotlin extensions
|
||||
*/
|
||||
fun cryptoStreamXchacha20XorIc(
|
||||
libSodium: SodiumAndroid,
|
||||
cipher: ByteArray,
|
||||
message: ByteArray,
|
||||
messageLen: Long,
|
||||
nonce: ByteArray,
|
||||
ic: Long,
|
||||
key: ByteArray,
|
||||
): Int {
|
||||
/**
|
||||
* C++ Code:
|
||||
*
|
||||
* unsigned char k2[crypto_core_hchacha20_OUTPUTBYTES]; crypto_core_hchacha20(k2, n, k, NULL);
|
||||
* return crypto_stream_chacha20_xor_ic( c, m, mlen, n + crypto_core_hchacha20_INPUTBYTES, ic,
|
||||
* k2);
|
||||
*/
|
||||
val k2 = ByteArray(32)
|
||||
|
||||
val nonceChaCha = nonce.drop(16).toByteArray()
|
||||
assert(nonceChaCha.size == 8)
|
||||
|
||||
libSodium.crypto_core_hchacha20(k2, nonce, key, null)
|
||||
return libSodium.crypto_stream_chacha20_xor_ic(
|
||||
cipher,
|
||||
message,
|
||||
messageLen,
|
||||
nonceChaCha,
|
||||
ic,
|
||||
k2,
|
||||
)
|
||||
}
|
||||
|
||||
fun cryptoStreamXchacha20Xor(
|
||||
libSodium: SodiumAndroid,
|
||||
cipher: ByteArray,
|
||||
message: ByteArray,
|
||||
messageLen: Long,
|
||||
nonce: ByteArray,
|
||||
key: ByteArray,
|
||||
): Int = cryptoStreamXchacha20XorIc(libSodium, cipher, message, messageLen, nonce, 0, key)
|
||||
|
||||
fun cryptoStreamXChaCha20Xor(
|
||||
libSodium: SodiumAndroid,
|
||||
cipher: ByteArray,
|
||||
message: ByteArray,
|
||||
messageLen: Long,
|
||||
nonce: ByteArray,
|
||||
key: ByteArray,
|
||||
): Boolean {
|
||||
require(!(messageLen < 0 || messageLen > message.size)) {
|
||||
"messageLen out of bounds: $messageLen"
|
||||
}
|
||||
return cryptoStreamXchacha20Xor(
|
||||
libSodium,
|
||||
cipher,
|
||||
message,
|
||||
messageLen,
|
||||
nonce,
|
||||
key,
|
||||
) == 0
|
||||
}
|
||||
|
||||
fun cryptoStreamXChaCha20Xor(
|
||||
libSodium: SodiumAndroid,
|
||||
messageBytes: ByteArray,
|
||||
nonce: ByteArray,
|
||||
key: Key,
|
||||
): ByteArray? {
|
||||
val mLen = messageBytes.size
|
||||
val cipher = ByteArray(mLen)
|
||||
val successful =
|
||||
cryptoStreamXChaCha20Xor(libSodium, cipher, messageBytes, mLen.toLong(), nonce, key.asBytes)
|
||||
return if (successful) cipher else null
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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.nip49PrivKeyEnc
|
||||
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.Bech32
|
||||
|
||||
fun ByteArray.toNCryptSec() = Bech32.encodeBytes(hrp = "ncryptsec", this, Bech32.Encoding.Bech32)
|
||||
@@ -21,24 +21,15 @@
|
||||
package com.vitorpamplona.quartz.nip49PrivKeyEnc
|
||||
|
||||
import android.util.Log
|
||||
import com.goterl.lazysodium.LazySodiumAndroid
|
||||
import com.goterl.lazysodium.SodiumAndroid
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.Bech32
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.bechToBytes
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
import java.security.SecureRandom
|
||||
import com.vitorpamplona.quartz.utils.LibSodiumInstance
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import java.text.Normalizer
|
||||
|
||||
class Nip49(
|
||||
val secp256k1: Secp256k1,
|
||||
val random: SecureRandom,
|
||||
) {
|
||||
private val libSodium = SodiumAndroid()
|
||||
private val lazySodium = LazySodiumAndroid(libSodium)
|
||||
|
||||
class Nip49 {
|
||||
fun decrypt(
|
||||
nCryptSec: String,
|
||||
password: String,
|
||||
@@ -56,14 +47,11 @@ class Nip49(
|
||||
val key = SCrypt.scrypt(normalizedPassword, encryptedInfo.salt, n, 8, 1, 32)
|
||||
val m = ByteArray(32)
|
||||
|
||||
lazySodium.cryptoAeadXChaCha20Poly1305IetfDecrypt(
|
||||
LibSodiumInstance.cryptoAeadXChaCha20Poly1305IetfDecrypt(
|
||||
m,
|
||||
longArrayOf(32L),
|
||||
key,
|
||||
encryptedInfo.encryptedKey,
|
||||
encryptedInfo.encryptedKey.size.toLong(),
|
||||
byteArrayOf(encryptedInfo.keySecurity),
|
||||
1,
|
||||
encryptedInfo.nonce,
|
||||
key,
|
||||
)
|
||||
@@ -87,11 +75,8 @@ class Nip49(
|
||||
ksb: Byte,
|
||||
): String {
|
||||
check(secretKey.size == 32) { "invalid secret key" }
|
||||
val salt = ByteArray(16)
|
||||
random.nextBytes(salt)
|
||||
|
||||
val nonce = ByteArray(24)
|
||||
random.nextBytes(nonce)
|
||||
val salt = RandomInstance.bytes(16)
|
||||
val nonce = RandomInstance.bytes(24)
|
||||
|
||||
val normalizedPassword = Normalizer.normalize(password, Normalizer.Form.NFKC).toByteArray(Charsets.UTF_8)
|
||||
val n = Math.pow(2.0, logn.toDouble()).toInt()
|
||||
@@ -102,13 +87,10 @@ class Nip49(
|
||||
// byte[] m, long mLen,
|
||||
// byte[] ad, long adLen,
|
||||
// byte[] nSec, byte[] nPub, byte[] k
|
||||
lazySodium.cryptoAeadXChaCha20Poly1305IetfEncrypt(
|
||||
LibSodiumInstance.cryptoAeadXChaCha20Poly1305IetfEncrypt(
|
||||
ciphertext,
|
||||
longArrayOf(48),
|
||||
secretKey,
|
||||
secretKey.size.toLong(),
|
||||
byteArrayOf(ksb),
|
||||
1,
|
||||
key,
|
||||
nonce,
|
||||
key,
|
||||
@@ -166,13 +148,8 @@ class Nip49(
|
||||
|
||||
// ln(n.toDouble()).toInt().toByte(),
|
||||
fun encodePayload(): String =
|
||||
Bech32.encodeBytes(
|
||||
hrp = "ncryptsec",
|
||||
byteArrayOf(
|
||||
version,
|
||||
logn,
|
||||
) + salt + nonce + keySecurity + encryptedKey,
|
||||
Bech32.Encoding.Bech32,
|
||||
)
|
||||
(
|
||||
byteArrayOf(version, logn) + salt + nonce + keySecurity + encryptedKey
|
||||
).toNCryptSec()
|
||||
}
|
||||
}
|
||||
|
||||
-17
@@ -21,7 +21,6 @@
|
||||
package com.vitorpamplona.quartz.nip55AndroidSigner
|
||||
|
||||
import android.util.Log
|
||||
import com.goterl.lazysodium.BuildConfig
|
||||
import com.vitorpamplona.quartz.EventFactory
|
||||
import com.vitorpamplona.quartz.nip01Core.EventHasher
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
@@ -87,10 +86,6 @@ class NostrSignerExternal(
|
||||
toPublicKey: HexKey,
|
||||
onReady: (String) -> Unit,
|
||||
) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d("NostrExternalSigner", "Encrypt NIP04 Event: $decryptedContent")
|
||||
}
|
||||
|
||||
launcher.encrypt(
|
||||
decryptedContent,
|
||||
toPublicKey,
|
||||
@@ -104,10 +99,6 @@ class NostrSignerExternal(
|
||||
fromPublicKey: HexKey,
|
||||
onReady: (String) -> Unit,
|
||||
) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d("NostrExternalSigner", "Decrypt NIP04 Event: $encryptedContent")
|
||||
}
|
||||
|
||||
launcher.decrypt(
|
||||
encryptedContent,
|
||||
fromPublicKey,
|
||||
@@ -121,10 +112,6 @@ class NostrSignerExternal(
|
||||
toPublicKey: HexKey,
|
||||
onReady: (String) -> Unit,
|
||||
) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d("NostrExternalSigner", "Encrypt NIP44 Event: $decryptedContent")
|
||||
}
|
||||
|
||||
launcher.encrypt(
|
||||
decryptedContent,
|
||||
toPublicKey,
|
||||
@@ -138,10 +125,6 @@ class NostrSignerExternal(
|
||||
fromPublicKey: HexKey,
|
||||
onReady: (String) -> Unit,
|
||||
) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d("NostrExternalSigner", "Decrypt NIP44 Event: $encryptedContent")
|
||||
}
|
||||
|
||||
launcher.decrypt(
|
||||
encryptedContent,
|
||||
fromPublicKey,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.nip55AndroidSigner
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.utils.RandomInstance
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
|
||||
fun signString(
|
||||
message: String,
|
||||
privKey: ByteArray,
|
||||
nonce: ByteArray = RandomInstance.bytes(32),
|
||||
): ByteArray = Nip01.sign(sha256(message.toByteArray()), privKey, nonce)
|
||||
@@ -20,8 +20,9 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.nip57Zaps
|
||||
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip04Dm.crypto.Nip04
|
||||
import com.vitorpamplona.quartz.nip19Bech32.bech32.Bech32
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
import java.nio.charset.Charset
|
||||
import java.security.SecureRandom
|
||||
import javax.crypto.BadPaddingException
|
||||
@@ -38,7 +39,7 @@ class PrivateZapEncryption {
|
||||
): ByteArray {
|
||||
val str = privkey + id + createdAt.toString()
|
||||
val strbyte = str.toByteArray(Charset.forName("utf-8"))
|
||||
return CryptoUtils.sha256(strbyte)
|
||||
return sha256(strbyte)
|
||||
}
|
||||
|
||||
fun encryptPrivateZapMessage(
|
||||
@@ -46,7 +47,7 @@ class PrivateZapEncryption {
|
||||
privkey: ByteArray,
|
||||
pubkey: ByteArray,
|
||||
): String {
|
||||
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
|
||||
val sharedSecret = Nip04.getSharedSecret(privkey, pubkey)
|
||||
val iv = ByteArray(16)
|
||||
SecureRandom().nextBytes(iv)
|
||||
|
||||
@@ -70,7 +71,7 @@ class PrivateZapEncryption {
|
||||
privkey: ByteArray,
|
||||
pubkey: ByteArray,
|
||||
): String {
|
||||
val sharedSecret = CryptoUtils.getSharedSecretNIP04(privkey, pubkey)
|
||||
val sharedSecret = Nip04.getSharedSecret(privkey, pubkey)
|
||||
if (sharedSecret.size != 16 && sharedSecret.size != 32) {
|
||||
throw IllegalArgumentException("Invalid shared secret size")
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
package com.vitorpamplona.quartz.nip57Zaps
|
||||
|
||||
import android.util.Log
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.nip01Core.crypto.Nip01
|
||||
import com.vitorpamplona.quartz.nip01Core.hexToByteArray
|
||||
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
@@ -105,7 +105,7 @@ class PrivateZapRequestBuilder {
|
||||
|
||||
try {
|
||||
if (altPrivateKeyToUse != null && altPubkeyToUse != null) {
|
||||
val altPubKeyFromPrivate = CryptoUtils.pubkeyCreate(altPrivateKeyToUse).toHexKey()
|
||||
val altPubKeyFromPrivate = Nip01.pubKeyCreate(altPrivateKeyToUse).toHexKey()
|
||||
|
||||
if (altPubKeyFromPrivate == event.pubKey) {
|
||||
val result = event.getPrivateZapEvent(altPrivateKeyToUse, altPubkeyToUse)
|
||||
|
||||
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.nip98HttpAuth.tags
|
||||
|
||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||
import com.vitorpamplona.quartz.nip01Core.toHexKey
|
||||
import com.vitorpamplona.quartz.utils.sha256Hash
|
||||
import com.vitorpamplona.quartz.utils.sha256
|
||||
|
||||
class PayloadHashTag {
|
||||
companion object {
|
||||
@@ -39,6 +39,6 @@ class PayloadHashTag {
|
||||
fun assemble(hash: HexKey) = arrayOf(TAG_NAME, hash)
|
||||
|
||||
@JvmStatic
|
||||
fun assemble(payload: ByteArray) = assemble(sha256Hash(payload).toHexKey())
|
||||
fun assemble(payload: ByteArray) = assemble(sha256(payload).toHexKey())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.utils
|
||||
|
||||
import com.vitorpamplona.quartz.R
|
||||
|
||||
public fun arrayOfNotNull(vararg elements: String?) = removeTrailingNullsAndEmptyOthers(*elements)
|
||||
|
||||
public fun removeTrailingNullsAndEmptyOthers(vararg elements: String?): Array<String> {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.utils
|
||||
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
fun hmac512(
|
||||
key: ByteArray,
|
||||
data: ByteArray,
|
||||
): ByteArray {
|
||||
val mac = Mac.getInstance("HmacSHA512")
|
||||
mac.init(SecretKeySpec(key, "HmacSHA512"))
|
||||
return mac.doFinal(data)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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.utils
|
||||
|
||||
import com.goterl.lazysodium.LazySodiumAndroid
|
||||
import com.goterl.lazysodium.SodiumAndroid
|
||||
|
||||
object LibSodiumInstance {
|
||||
private val libSodium = SodiumAndroid()
|
||||
private val lazySodium = LazySodiumAndroid(libSodium)
|
||||
|
||||
fun cryptoAeadXChaCha20Poly1305IetfDecrypt(
|
||||
message: ByteArray,
|
||||
nSec: ByteArray,
|
||||
ciphertext: ByteArray,
|
||||
ad: ByteArray,
|
||||
nPub: ByteArray,
|
||||
k: ByteArray,
|
||||
): Boolean =
|
||||
lazySodium.cryptoAeadXChaCha20Poly1305IetfDecrypt(
|
||||
message,
|
||||
longArrayOf(message.size.toLong()),
|
||||
nSec,
|
||||
ciphertext,
|
||||
ciphertext.size.toLong(),
|
||||
ad,
|
||||
ad.size.toLong(),
|
||||
nPub,
|
||||
k,
|
||||
)
|
||||
|
||||
fun cryptoAeadXChaCha20Poly1305IetfEncrypt(
|
||||
ciphertext: ByteArray,
|
||||
message: ByteArray,
|
||||
ad: ByteArray,
|
||||
nSec: ByteArray,
|
||||
nPub: ByteArray,
|
||||
k: ByteArray,
|
||||
): Boolean =
|
||||
lazySodium.cryptoAeadXChaCha20Poly1305IetfEncrypt(
|
||||
ciphertext,
|
||||
longArrayOf(ciphertext.size.toLong()),
|
||||
message,
|
||||
message.size.toLong(),
|
||||
ad,
|
||||
ad.size.toLong(),
|
||||
nSec,
|
||||
nPub,
|
||||
k,
|
||||
)
|
||||
|
||||
fun cryptoStreamChaCha20IetfXor(
|
||||
message: ByteArray,
|
||||
nonce: ByteArray?,
|
||||
key: ByteArray?,
|
||||
): ByteArray {
|
||||
val ciphertext = ByteArray(message.size)
|
||||
lazySodium.cryptoStreamChaCha20IetfXor(ciphertext, message, message.size.toLong(), nonce, key)
|
||||
return ciphertext
|
||||
}
|
||||
|
||||
// This function wasn't available in the bindings library. I had to move them here from C
|
||||
fun cryptoStreamXChaCha20Xor(
|
||||
messageBytes: ByteArray,
|
||||
nonce: ByteArray,
|
||||
key: ByteArray,
|
||||
): ByteArray? {
|
||||
val cipher = ByteArray(messageBytes.size)
|
||||
val k2 = ByteArray(32)
|
||||
|
||||
val nonceChaCha = nonce.drop(16).toByteArray()
|
||||
assert(nonceChaCha.size == 8)
|
||||
|
||||
libSodium.crypto_core_hchacha20(k2, nonce, key, null)
|
||||
val resultCode =
|
||||
libSodium.crypto_stream_chacha20_xor_ic(
|
||||
cipher,
|
||||
messageBytes,
|
||||
messageBytes.size.toLong(),
|
||||
nonceChaCha,
|
||||
0,
|
||||
k2,
|
||||
)
|
||||
|
||||
return if (resultCode == 0) cipher else null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.utils
|
||||
|
||||
import java.security.SecureRandom
|
||||
|
||||
object RandomInstance {
|
||||
private val randomizer = SecureRandom()
|
||||
|
||||
fun int(bound: Int) = randomizer.nextInt(bound)
|
||||
|
||||
fun bytes(size: Int) = ByteArray(size).also { randomizer.nextBytes(it) }
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.utils
|
||||
|
||||
import fr.acinq.secp256k1.Secp256k1
|
||||
|
||||
object Secp256k1Instance {
|
||||
private val h02 = Hex.decode("02")
|
||||
private val secp256k1 = Secp256k1.get()
|
||||
|
||||
fun compressedPubKeyFor(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey))
|
||||
|
||||
fun isPrivateKeyValid(il: ByteArray): Boolean = secp256k1.secKeyVerify(il)
|
||||
|
||||
fun signSchnorr(
|
||||
data: ByteArray,
|
||||
privKey: ByteArray,
|
||||
nonce: ByteArray? = RandomInstance.bytes(32),
|
||||
): ByteArray = secp256k1.signSchnorr(data, privKey, nonce)
|
||||
|
||||
fun signSchnorr(
|
||||
data: ByteArray,
|
||||
privKey: ByteArray,
|
||||
): ByteArray = secp256k1.signSchnorr(data, privKey, null)
|
||||
|
||||
fun verifySchnorr(
|
||||
signature: ByteArray,
|
||||
hash: ByteArray,
|
||||
pubKey: ByteArray,
|
||||
): Boolean = secp256k1.verifySchnorr(signature, hash, pubKey)
|
||||
|
||||
fun privateKeyAdd(
|
||||
first: ByteArray,
|
||||
second: ByteArray,
|
||||
): ByteArray = secp256k1.privKeyTweakAdd(first, second)
|
||||
|
||||
fun pubKeyTweakMulCompact(
|
||||
pubKey: ByteArray,
|
||||
privateKey: ByteArray,
|
||||
): ByteArray = secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33)
|
||||
}
|
||||
@@ -22,7 +22,7 @@ package com.vitorpamplona.quartz.utils
|
||||
|
||||
import java.security.MessageDigest
|
||||
|
||||
fun sha256Hash(data: ByteArray): ByteArray {
|
||||
fun sha256(data: ByteArray): ByteArray {
|
||||
// Creates a new buffer every time
|
||||
return MessageDigest.getInstance("SHA-256").digest(data)
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
*/
|
||||
package com.vitorpamplona.quartz.utils
|
||||
|
||||
import com.vitorpamplona.quartz.CryptoUtils
|
||||
|
||||
object TimeUtils {
|
||||
const val ONE_MINUTE = 60
|
||||
const val FIVE_MINUTES = 5 * ONE_MINUTE
|
||||
@@ -57,5 +55,5 @@ object TimeUtils {
|
||||
|
||||
fun oneMonthAgo() = now() - ONE_MONTH
|
||||
|
||||
fun randomWithTwoDays() = now() - CryptoUtils.randomInt(twoDays())
|
||||
fun randomWithTwoDays() = now() - RandomInstance.int(twoDays())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user