diff --git a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt index e8aaca567..2b1c8becb 100644 --- a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt +++ b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ImageUploadTesting.kt @@ -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) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 5907b79d1..712ff187f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Chatroom.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Chatroom.kt index dc4e63648..dbf77b216 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Chatroom.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Chatroom.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 51047d5fc..c7aee6dff 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/Base64Image.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/Base64Image.kt index 91b3ad9dc..41e5204c8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/Base64Image.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/Base64Image.kt @@ -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 } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomDataSource.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomDataSource.kt index 2fd76043a..130c17a37 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomDataSource.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomDataSource.kt @@ -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") { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomListDataSource.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomListDataSource.kt index 8ddd310fa..37fc97c84 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomListDataSource.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrChatroomListDataSource.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrSearchEventOrUserDataSource.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrSearchEventOrUserDataSource.kt index 29b45f65a..f41c8c740 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrSearchEventOrUserDataSource.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/NostrSearchEventOrUserDataSource.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/OnlineCheck.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/OnlineCheck.kt index e00932f21..1fd857049 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/OnlineCheck.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/OnlineCheck.kt @@ -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() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt index 991915cf5..0d662a63d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/EventNotificationConsumer.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/EncryptFiles.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/EncryptFiles.kt index 353e5d3cd..28e40e032 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/EncryptFiles.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/EncryptFiles.kt @@ -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) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/FileHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/FileHeader.kt index 092bcdc22..e22655d38 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/FileHeader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/FileHeader.kt @@ -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 = try { - val hash = CryptoUtils.sha256(data).toHexKey() + val hash = sha256(data).toHexKey() val size = data.size val (blurHash, dim) = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/blossom/BlossomUploader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/blossom/BlossomUploader.kt index 49cc0fc79..a6dabb114 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/blossom/BlossomUploader.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/uploads/blossom/BlossomUploader.kt @@ -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) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt index ed4f4bee3..8f363caa3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt index 41816ec2a..beb7bab1a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt @@ -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)) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index 3f5be7875..e4bc3bfa3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt index 11eb81bd4..dc3757935 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt index 0cd971f28..721a8ca2f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ZoomableContentView.kt @@ -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}") diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListNewFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListNewFeedFilter.kt index 4ebefbe73..faee792da 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListNewFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListNewFeedFilter.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt index c72513d8e..6061f6b34 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt index d8db70276..1881067b1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 49d88fbac..1f099290e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PrivateMessage.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PrivateMessage.kt index b217cce08..6a0dd6bdb 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PrivateMessage.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/PrivateMessage.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt index ee0ba65c8..b8f85b4f5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountStateViewModel.kt @@ -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) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountBackupDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountBackupDialog.kt index a632bea32..5bfff3424 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountBackupDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountBackupDialog.kt @@ -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) } }, ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index 0c12b1f17..f14583fe4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -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, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChatroomMessageCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChatroomMessageCompose.kt index 30737d867..4421d1649 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChatroomMessageCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chatrooms/ChatroomMessageCompose.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt index 0f9e94663..7bb7a6da6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt @@ -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 diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt index c16820c48..5d4218e43 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/threadview/ThreadFeedView.kt @@ -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 diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/EventBenchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/EventBenchmark.kt index c3921abce..ef2aeca89 100644 --- a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/EventBenchmark.kt +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/EventBenchmark.kt @@ -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") + } + } } diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapBenchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapBenchmark.kt index 024436e10..33493df01 100644 --- a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapBenchmark.kt +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapBenchmark.kt @@ -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 diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapReceivingBenchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapReceivingBenchmark.kt index d1fc02a31..00900de95 100644 --- a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapReceivingBenchmark.kt +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapReceivingBenchmark.kt @@ -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(), diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/Sha256Benchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/Sha256Benchmark.kt index 3fbd26471..5885c2236 100644 --- a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/Sha256Benchmark.kt +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/Sha256Benchmark.kt @@ -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)) } } } diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/CryptoBenchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/SharedKeyBenchmark.kt similarity index 63% rename from benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/CryptoBenchmark.kt rename to benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/SharedKeyBenchmark.kt index a29c3ace4..2a2a9d322 100644 --- a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/CryptoBenchmark.kt +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/SharedKeyBenchmark.kt @@ -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)) } } } diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/SignVerifyBenchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/SignVerifyBenchmark.kt new file mode 100644 index 000000000..01dbf62a4 --- /dev/null +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/SignVerifyBenchmark.kt @@ -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)) + } + } +} diff --git a/commons/src/main/java/com/vitorpamplona/amethyst/commons/robohash/RobohashAssembler.kt b/commons/src/main/java/com/vitorpamplona/amethyst/commons/robohash/RobohashAssembler.kt index a07bb1f8d..eab71fe67 100644 --- a/commons/src/main/java/com/vitorpamplona/amethyst/commons/robohash/RobohashAssembler.kt +++ b/commons/src/main/java/com/vitorpamplona/amethyst/commons/robohash/RobohashAssembler.kt @@ -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)) diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/CryptoUtilsTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/CryptoUtilsTest.kt deleted file mode 100644 index 2b5b4862b..000000000 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/CryptoUtilsTest.kt +++ /dev/null @@ -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) - } -} diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/bloom/BloomFilter.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/bloom/BloomFilter.kt index d41e4d628..1a9b1bacd 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/bloom/BloomFilter.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/bloom/BloomFilter.kt @@ -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++ } } diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip01Core/Nip01Test.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip01Core/Nip01Test.kt index ce34592c7..7566c29ea 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip01Core/Nip01Test.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip01Core/Nip01Test.kt @@ -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), ), ) } diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip01Core/crypto/Nip01CryptoTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip01Core/crypto/Nip01CryptoTest.kt new file mode 100644 index 000000000..9835f3b0f --- /dev/null +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip01Core/crypto/Nip01CryptoTest.kt @@ -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) + } +} diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip04Dm/Nip04EncryptionTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip04Dm/EncryptionTest.kt similarity index 77% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/nip04Dm/Nip04EncryptionTest.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/nip04Dm/EncryptionTest.kt index d024ca17a..e25755e29 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip04Dm/Nip04EncryptionTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip04Dm/EncryptionTest.kt @@ -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" + diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip04Dm/Nip04Test.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip04Dm/Nip04Test.kt new file mode 100644 index 000000000..29e0acfc8 --- /dev/null +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip04Dm/Nip04Test.kt @@ -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) + } +} diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip32SeedDerivationTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip32SeedDerivationTest.kt index a1fef7a1d..722372b3c 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip32SeedDerivationTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip32SeedDerivationTest.kt @@ -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( diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip39MnemonicsTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip39MnemonicsTest.kt index 1aaa0eaa5..2e1d95729 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip39MnemonicsTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip39MnemonicsTest.kt @@ -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) } } diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Nip06Test.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Nip06Test.kt index 2466e84da..040cfa758 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Nip06Test.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip06KeyDerivation/Nip06Test.kt @@ -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 diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v1Test.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v1Test.kt new file mode 100644 index 000000000..a3bc8b645 --- /dev/null +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v1Test.kt @@ -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) + } +} diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/NIP44v2Test.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v2Test.kt similarity index 90% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/NIP44v2Test.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v2Test.kt index f603c98d3..104371cdd 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/NIP44v2Test.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v2Test.kt @@ -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() } diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/NIP49Test.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/NIP49Test.kt index 3985842c0..f7876a08e 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/NIP49Test.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/NIP49Test.kt @@ -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() { diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip55AndroidSigner/SignStringTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip55AndroidSigner/SignStringTest.kt new file mode 100644 index 000000000..bef7a3f60 --- /dev/null +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/nip55AndroidSigner/SignStringTest.kt @@ -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) + } +} diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/utils/HexEncodingTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/utils/HexEncodingTest.kt index 2b6d50ce0..42686132d 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/utils/HexEncodingTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/utils/HexEncodingTest.kt @@ -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(), diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/CryptoUtils.kt b/quartz/src/main/java/com/vitorpamplona/quartz/CryptoUtils.kt deleted file mode 100644 index 968ba94fa..000000000 --- a/quartz/src/main/java/com/vitorpamplona/quartz/CryptoUtils.kt +++ /dev/null @@ -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) -} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/EventFactory.kt b/quartz/src/main/java/com/vitorpamplona/quartz/EventFactory.kt index 08b301031..3072c450a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/EventFactory.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/EventFactory.kt @@ -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 diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/EventExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/EventExt.kt index e2e85884f..3afaf3bc7 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/EventExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/EventExt.kt @@ -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. */ diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/EventHasher.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/EventHasher.kt index 13a682950..f694cd627 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/EventHasher.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/EventHasher.kt @@ -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>, content: String, - ): ByteArray = sha256Hash(makeJsonForId(pubKey, createdAt, kind, tags, content).toByteArray()) + ): ByteArray = sha256(makeJsonForId(pubKey, createdAt, kind, tags, content).toByteArray()) fun hashId( pubKey: HexKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/KeyPair.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/KeyPair.kt index a5ac5c4ac..4dc020aaa 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/KeyPair.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/KeyPair.kt @@ -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) } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/Nip01.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/Nip01.kt index 6466d3c75..d0cb44340 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/Nip01.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/crypto/Nip01.kt @@ -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) } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/NostrSigner.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/NostrSigner.kt index bfda070b0..ad0f8998b 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/NostrSigner.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/NostrSigner.kt @@ -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) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/NostrSignerSync.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/NostrSignerSync.kt index aabdafd1c..b45160e43 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/NostrSignerSync.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip01Core/signers/NostrSignerSync.kt @@ -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) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/Nip04Encryption.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/Nip04Encryption.kt deleted file mode 100644 index f02b44eeb..000000000 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/Nip04Encryption.kt +++ /dev/null @@ -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) - } -} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/AESCBC.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/AESCBC.kt new file mode 100644 index 000000000..66e6b1bdb --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/AESCBC.kt @@ -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" + } +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/Nip04Encoder.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/EncryptedInfo.kt similarity index 70% rename from quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/Nip04Encoder.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/EncryptedInfo.kt index 96fbfe922..067185f20 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/Nip04Encoder.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/EncryptedInfo.kt @@ -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" - } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/Encryption.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/Encryption.kt new file mode 100644 index 000000000..3303c9f2a --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/Encryption.kt @@ -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) +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/Nip04.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/Nip04.kt new file mode 100644 index 000000000..a4d86f7f7 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/crypto/Nip04.kt @@ -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)) +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/PrivateDmEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/messages/PrivateDmEvent.kt similarity index 99% rename from quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/PrivateDmEvent.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/messages/PrivateDmEvent.kt index 6f73e3195..8b2e09b6f 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/PrivateDmEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/messages/PrivateDmEvent.kt @@ -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 diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/TagArrayBuilderExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/messages/TagArrayBuilderExt.kt similarity index 97% rename from quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/TagArrayBuilderExt.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/messages/TagArrayBuilderExt.kt index c5a018155..2b6e87cda 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/TagArrayBuilderExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip04Dm/messages/TagArrayBuilderExt.kt @@ -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 diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip32SeedDerivation.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip32SeedDerivation.kt index 88fc1dcdc..c093a2c45 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip32SeedDerivation.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip32SeedDerivation.kt @@ -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) } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip39Mnemonics.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip39Mnemonics.kt index 423a6e00a..afbbaa9b1 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip39Mnemonics.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Bip39Mnemonics.kt @@ -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, ): List { 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] } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Nip06.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Nip06.kt index f1b405901..6e3fe0d6e 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Nip06.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip06KeyDerivation/Nip06.kt @@ -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'/'/0/0 private val nip6Base: KeyPath = diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/encryption/AESGCM.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/encryption/AESGCM.kt index 8f3c7541f..47c3295b8 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/encryption/AESGCM.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip17Dm/files/encryption/AESGCM.kt @@ -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") diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip19Bech32/Nip19Parser.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip19Bech32/Nip19Parser.kt index 1222d013f..697269977 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip19Bech32/Nip19Parser.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip19Bech32/Nip19Parser.kt @@ -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 diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/RandomExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/EncryptedInfoString.kt similarity index 87% rename from quartz/src/main/java/com/vitorpamplona/quartz/utils/RandomExt.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/EncryptedInfoString.kt index 333b62a67..c17755d03 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/RandomExt.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/EncryptedInfoString.kt @@ -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?, +) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44.kt index 03bb5fff3..2ecd2b31f 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44.kt @@ -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 diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v1.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v1.kt index 7cf47f42d..f4d30df87 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v1.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v1.kt @@ -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( diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v2.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v2.kt index 7b3f9d49d..5544888a1 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v2.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/Nip44v2.kt @@ -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) } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/crypto/SodiumUtils.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/crypto/SodiumUtils.kt deleted file mode 100644 index 2cc0c902f..000000000 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip44Encryption/crypto/SodiumUtils.kt +++ /dev/null @@ -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 -} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/ByteArrayExt.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/ByteArrayExt.kt new file mode 100644 index 000000000..ae6a33928 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/ByteArrayExt.kt @@ -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) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/Nip49.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/Nip49.kt index e8e7e59df..04fc99659 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/Nip49.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip49PrivKeyEnc/Nip49.kt @@ -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() } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip55AndroidSigner/NostrSignerExternal.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip55AndroidSigner/NostrSignerExternal.kt index b06db3df3..7ee5a51ad 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip55AndroidSigner/NostrSignerExternal.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip55AndroidSigner/NostrSignerExternal.kt @@ -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, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip55AndroidSigner/SignString.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip55AndroidSigner/SignString.kt new file mode 100644 index 000000000..99f6d213b --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip55AndroidSigner/SignString.kt @@ -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) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip57Zaps/PrivateZapEncryption.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip57Zaps/PrivateZapEncryption.kt index 9f89384e1..61228c675 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip57Zaps/PrivateZapEncryption.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip57Zaps/PrivateZapEncryption.kt @@ -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") } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip57Zaps/PrivateZapRequestBuilder.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip57Zaps/PrivateZapRequestBuilder.kt index 8ee6fbf4c..0b0a8b9f9 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip57Zaps/PrivateZapRequestBuilder.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip57Zaps/PrivateZapRequestBuilder.kt @@ -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) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/nip98HttpAuth/tags/PayloadHashTag.kt b/quartz/src/main/java/com/vitorpamplona/quartz/nip98HttpAuth/tags/PayloadHashTag.kt index 85904bb3b..e607ee950 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/nip98HttpAuth/tags/PayloadHashTag.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/nip98HttpAuth/tags/PayloadHashTag.kt @@ -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()) } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/ArrayUtils.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/ArrayUtils.kt index 9f1725f88..e33916bfd 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/ArrayUtils.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/ArrayUtils.kt @@ -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 { diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/Hmac512.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Hmac512.kt new file mode 100644 index 000000000..c19f7cc4b --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Hmac512.kt @@ -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) +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/LibSodiumInstance.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LibSodiumInstance.kt new file mode 100644 index 000000000..cdffe0a24 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/LibSodiumInstance.kt @@ -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 + } +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/RandomInstance.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/RandomInstance.kt new file mode 100644 index 000000000..90b0c95f3 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/RandomInstance.kt @@ -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) } +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/Secp256k1Instance.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Secp256k1Instance.kt new file mode 100644 index 000000000..814514cb4 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Secp256k1Instance.kt @@ -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) +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/Sha256.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Sha256.kt index 5eda7da51..c33dc0b7d 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/Sha256.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/Sha256.kt @@ -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) } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/utils/TimeUtils.kt b/quartz/src/main/java/com/vitorpamplona/quartz/utils/TimeUtils.kt index 8e6341c09..819339779 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/utils/TimeUtils.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/utils/TimeUtils.kt @@ -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()) }