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 e7ba98d21..0c9c3ec1b 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 @@ -33,7 +33,6 @@ import com.vitorpamplona.ammolite.relays.Client import com.vitorpamplona.ammolite.service.HttpClientManager import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.crypto.KeyPair -import com.vitorpamplona.quartz.crypto.nip06.Nip06 import com.vitorpamplona.quartz.encoders.Hex import com.vitorpamplona.quartz.encoders.Nip19Bech32 import com.vitorpamplona.quartz.encoders.bechToBytes @@ -129,8 +128,8 @@ class AccountStateViewModel : ViewModel() { proxyPort = proxyPort, signer = NostrSignerInternal(keyPair), ) - } else if (key.contains(" ") && Nip06().isValidMnemonic(key)) { - val keyPair = KeyPair(privKey = Nip06().privateKeyFromMnemonic(key)) + } else if (key.contains(" ") && CryptoUtils.isValidMnemonic(key)) { + val keyPair = KeyPair(privKey = CryptoUtils.privateKeyFromMnemonic(key)) Account( keyPair, proxy = proxy, diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/CryptoBenchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/CryptoBenchmark.kt index 67332f084..111fc6ad5 100644 --- a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/CryptoBenchmark.kt +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/CryptoBenchmark.kt @@ -50,7 +50,7 @@ class CryptoBenchmark { val keyPair2 = KeyPair() benchmarkRule.measureRepeated { - assertNotNull(CryptoUtils.getSharedSecretNIP44v1(keyPair1.privKey!!, keyPair2.pubKey)) + assertNotNull(CryptoUtils.nip44.v1.getSharedSecret(keyPair1.privKey!!, keyPair2.pubKey)) } } @@ -70,7 +70,7 @@ class CryptoBenchmark { val keyPair2 = KeyPair() benchmarkRule.measureRepeated { - assertNotNull(CryptoUtils.computeSharedSecretNIP44v1(keyPair1.privKey!!, keyPair2.pubKey)) + assertNotNull(CryptoUtils.nip44.v1.computeSharedSecret(keyPair1.privKey!!, keyPair2.pubKey)) } } 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 f16b43892..21a27914a 100644 --- a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapReceivingBenchmark.kt +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/GiftWrapReceivingBenchmark.kt @@ -165,7 +165,7 @@ class GiftWrapReceivingBenchmark { benchmarkRule.measureRepeated { assertNotNull( - CryptoUtils.decryptNIP44v2( + CryptoUtils.decryptNIP44( wrap.content, receiver.keyPair.privKey!!, wrap.pubKey.hexToByteArray(), @@ -182,7 +182,7 @@ class GiftWrapReceivingBenchmark { val wrap = createWrap(sender, receiver) val innerJson = - CryptoUtils.decryptNIP44v2( + CryptoUtils.decryptNIP44( wrap.content, receiver.keyPair.privKey!!, wrap.pubKey.hexToByteArray(), @@ -200,7 +200,7 @@ class GiftWrapReceivingBenchmark { benchmarkRule.measureRepeated { assertNotNull( - CryptoUtils.decryptNIP44v2( + CryptoUtils.decryptNIP44( seal.content, receiver.keyPair.privKey!!, seal.pubKey.hexToByteArray(), @@ -217,7 +217,7 @@ class GiftWrapReceivingBenchmark { val seal = createSeal(sender, receiver) val innerJson = - CryptoUtils.decryptNIP44v2( + CryptoUtils.decryptNIP44( seal.content, receiver.keyPair.privKey!!, seal.pubKey.hexToByteArray(), diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/CryptoUtilsTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/CryptoUtilsTest.kt index ad5bcff7b..840a7068a 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/CryptoUtilsTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/CryptoUtilsTest.kt @@ -43,7 +43,7 @@ class CryptoUtilsTest { val publicKey = "765cd7cf91d3ad07423d114d5a39c61d52b2cdbc18ba055ddbbeec71fbe2aa2f" val key = - CryptoUtils.getSharedSecretNIP44v1( + CryptoUtils.nip44.v1.getSharedSecret( privateKey = privateKey.hexToByteArray(), pubKey = publicKey.hexToByteArray(), ) @@ -56,8 +56,8 @@ class CryptoUtilsTest { val sender = KeyPair() val receiver = KeyPair() - val sharedSecret1 = CryptoUtils.getSharedSecretNIP44v1(sender.privKey!!, receiver.pubKey) - val sharedSecret2 = CryptoUtils.getSharedSecretNIP44v1(receiver.privKey!!, sender.pubKey) + 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()) @@ -88,8 +88,8 @@ class CryptoUtilsTest { val privateKey = CryptoUtils.privkeyCreate() val publicKey = CryptoUtils.pubkeyCreate(privateKey) - val encrypted = CryptoUtils.encryptNIP44v1(msg, privateKey, publicKey) - val decrypted = CryptoUtils.decryptNIP44v1(encrypted, privateKey, publicKey) + val encrypted = CryptoUtils.nip44.v1.encrypt(msg, privateKey, publicKey) + val decrypted = CryptoUtils.nip44.v1.decrypt(encrypted, privateKey, publicKey) assertEquals(msg, decrypted) } @@ -113,10 +113,10 @@ class CryptoUtilsTest { val privateKey = CryptoUtils.privkeyCreate() val publicKey = CryptoUtils.pubkeyCreate(privateKey) - val sharedSecret = CryptoUtils.getSharedSecretNIP44v1(privateKey, publicKey) + val sharedSecret = CryptoUtils.nip44.v1.getSharedSecret(privateKey, publicKey) - val encrypted = CryptoUtils.encryptNIP44v1(msg, sharedSecret) - val decrypted = CryptoUtils.decryptNIP44v1(encrypted, sharedSecret) + val encrypted = CryptoUtils.nip44.v1.encrypt(msg, sharedSecret) + val decrypted = CryptoUtils.nip44.v1.decrypt(encrypted, sharedSecret) assertEquals(msg, decrypted) } diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip06/Bip32SeedDerivationTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip06/Bip32SeedDerivationTest.kt index 42fd549f1..e3e14f256 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip06/Bip32SeedDerivationTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip06/Bip32SeedDerivationTest.kt @@ -22,42 +22,45 @@ package com.vitorpamplona.quartz.crypto.nip06 import androidx.test.ext.junit.runners.AndroidJUnit4 import com.vitorpamplona.quartz.encoders.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 masterBitcoin = - Bip32SeedDerivation.generate( + seedDerivation.generate( Bip39Mnemonics.toSeed("gun please vital unable phone catalog explain raise erosion zoo truly exist", ""), ) val nostrMnemonic0 = - Bip32SeedDerivation.generate( + seedDerivation.generate( Bip39Mnemonics.toSeed("leader monkey parrot ring guide accident before fence cannon height naive bean", ""), ) val nostrMnemonic1 = - Bip32SeedDerivation.generate( + seedDerivation.generate( Bip39Mnemonics.toSeed("what bleak badge arrange retreat wolf trade produce cricket blur garlic valid proud rude strong choose busy staff weather area salt hollow arm fade", ""), ) @Test fun restoreBIP44Wallet() { - val privateKey = Bip32SeedDerivation.derivePrivateKey(masterBitcoin, KeyPath("m/44'/1'/0'")) + val privateKey = seedDerivation.derivePrivateKey(masterBitcoin, KeyPath("m/44'/1'/0'")) assertEquals("50b3e7905c642309c8a8b73df5a49757a10f2bebb5804571b9db9004cce8a190", privateKey.toHexKey()) } @Test fun restoreBIP49Wallet() { - val privateKey = Bip32SeedDerivation.derivePrivateKey(masterBitcoin, KeyPath("m/49'/1'/0'")) + val privateKey = seedDerivation.derivePrivateKey(masterBitcoin, KeyPath("m/49'/1'/0'")) assertEquals("154c02c0b66899291a19012207642ba096a2d3ebf51baf153c9495976feb1b30", privateKey.toHexKey()) } @Test fun restoreBIP84Wallet() { - val privateKey = Bip32SeedDerivation.derivePrivateKey(masterBitcoin, KeyPath("m/84'/1'/0'")) + val privateKey = seedDerivation.derivePrivateKey(masterBitcoin, KeyPath("m/84'/1'/0'")) assertEquals("53e8c09a0e3ddcd8d68821c1e99e823966e99df91fb253e1f453a443ba543cb2", privateKey.toHexKey()) } diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip06/Nip06Test.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip06/Nip06Test.kt index 687fc5bfe..eaa39101e 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip06/Nip06Test.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip06/Nip06Test.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.crypto.nip06 import androidx.test.ext.junit.runners.AndroidJUnit4 import com.vitorpamplona.quartz.encoders.toHexKey +import fr.acinq.secp256k1.Secp256k1 import junit.framework.TestCase.assertEquals import org.junit.Ignore import org.junit.Test @@ -29,6 +30,8 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class Nip06Test { + val nip06 = Nip06(Secp256k1.get()) + // private key (hex): 7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a // nsec: nsec10allq0gjx7fddtzef0ax00mdps9t2kmtrldkyjfs8l5xruwvh2dq0lhhkp private val menemonic0 = "leader monkey parrot ring guide accident before fence cannon height naive bean" @@ -43,26 +46,26 @@ class Nip06Test { @Test fun fromSeedNip06TestVector0() { - val privateKeyHex = Nip06().privateKeyFromMnemonic(menemonic0).toHexKey() + val privateKeyHex = nip06.privateKeyFromMnemonic(menemonic0).toHexKey() assertEquals("7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a", privateKeyHex) - val privateKeyHex21 = Nip06().privateKeyFromMnemonic(menemonic0, 21).toHexKey() + val privateKeyHex21 = nip06.privateKeyFromMnemonic(menemonic0, 21).toHexKey() assertEquals("576390ec69951fcfbf159f2aac0965bb2e6d7a07da2334992af3225c57eaefca", privateKeyHex21) } @Test fun fromSeedNip06TestVector1() { - val privateKeyHex = Nip06().privateKeyFromMnemonic(menemonic1).toHexKey() + val privateKeyHex = nip06.privateKeyFromMnemonic(menemonic1).toHexKey() assertEquals("c15d739894c81a2fcfd3a2df85a0d2c0dbc47a280d092799f144d73d7ae78add", privateKeyHex) - val privateKeyHex21 = Nip06().privateKeyFromMnemonic(menemonic1, 42).toHexKey() + val privateKeyHex21 = nip06.privateKeyFromMnemonic(menemonic1, 42).toHexKey() assertEquals("ad993054383da74e955f8b86346365b5ffd6575992e1de3738dda9f94407052b", privateKeyHex21) } @Test @Ignore("Snort is not correctly implemented") fun fromSeedNip06FromSnort() { - val privateKeyNsec = Nip06().privateKeyFromMnemonic(snortTest).toHexKey() + val privateKeyNsec = nip06.privateKeyFromMnemonic(snortTest).toHexKey() assertEquals("nsec1ppw9ltr2x9qwg9a2qnmgv98tfruy2ejnja7me76mwmsreu3s8u2sscj5nt", privateKeyNsec) } } diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/NIP44v2Test.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip44/NIP44v2Test.kt similarity index 97% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/NIP44v2Test.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip44/NIP44v2Test.kt index bdd53082e..c2bffa2ee 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/NIP44v2Test.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip44/NIP44v2Test.kt @@ -18,12 +18,13 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.quartz.crypto +package com.vitorpamplona.quartz.crypto.nip44 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.vitorpamplona.quartz.crypto.KeyPair import com.vitorpamplona.quartz.encoders.hexToByteArray import com.vitorpamplona.quartz.encoders.toHexKey import fr.acinq.secp256k1.Secp256k1 @@ -79,8 +80,7 @@ class NIP44v2Test { v.plaintext!!, conversationKey1, v.nonce!!.hexToByteArray(), - ) - .encodePayload() + ).encodePayload() assertEquals(v.payload, ciphertext) @@ -107,8 +107,7 @@ class NIP44v2Test { plaintext, conversationKey, v.nonce!!.hexToByteArray(), - ) - .encodePayload() + ).encodePayload() assertEquals(v.payloadSha256, sha256Hex(ciphertext.toByteArray(Charsets.UTF_8))) diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/NIP49Test.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip49/NIP49Test.kt similarity index 99% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/NIP49Test.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip49/NIP49Test.kt index ef750ca52..ca6a2700b 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/NIP49Test.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/crypto/nip49/NIP49Test.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.crypto +package com.vitorpamplona.quartz.crypto.nip49 import androidx.test.ext.junit.runners.AndroidJUnit4 import com.vitorpamplona.quartz.encoders.toHexKey diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/HexEncodingTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/encoders/HexEncodingTest.kt similarity index 79% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/HexEncodingTest.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/encoders/HexEncodingTest.kt index 2bb95b09a..5d6451b54 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/HexEncodingTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/encoders/HexEncodingTest.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 +package com.vitorpamplona.quartz.encoders import com.vitorpamplona.quartz.crypto.CryptoUtils import org.junit.Assert.assertEquals @@ -31,8 +31,8 @@ class HexEncodingTest { fun testHexEncodeDecodeOurs() { assertEquals( testHex, - com.vitorpamplona.quartz.encoders.Hex.encode( - com.vitorpamplona.quartz.encoders.Hex.decode(testHex), + Hex.encode( + Hex.decode(testHex), ), ) } @@ -42,7 +42,8 @@ class HexEncodingTest { assertEquals( testHex, fr.acinq.secp256k1.Hex.encode( - fr.acinq.secp256k1.Hex.decode(testHex), + fr.acinq.secp256k1.Hex + .decode(testHex), ), ) } @@ -51,14 +52,17 @@ class HexEncodingTest { fun testRandoms() { for (i in 0..1000) { val bytes = CryptoUtils.privkeyCreate() - val hex = fr.acinq.secp256k1.Hex.encode(bytes) + val hex = + fr.acinq.secp256k1.Hex + .encode(bytes) assertEquals( - fr.acinq.secp256k1.Hex.encode(bytes), - com.vitorpamplona.quartz.encoders.Hex.encode(bytes), + fr.acinq.secp256k1.Hex + .encode(bytes), + Hex.encode(bytes), ) assertEquals( bytes.toList(), - com.vitorpamplona.quartz.encoders.Hex.decode(hex).toList(), + Hex.decode(hex).toList(), ) } } diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/LnInvoiceUtilTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/encoders/LnInvoiceUtilTest.kt similarity index 96% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/LnInvoiceUtilTest.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/encoders/LnInvoiceUtilTest.kt index 9c570927e..0c3f8e6da 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/LnInvoiceUtilTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/encoders/LnInvoiceUtilTest.kt @@ -18,10 +18,9 @@ * 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 +package com.vitorpamplona.quartz.encoders import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.vitorpamplona.quartz.encoders.LnInvoiceUtil import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/NIP19EmbedTests.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/encoders/NIP19EmbedTests.kt similarity index 97% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/NIP19EmbedTests.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/encoders/NIP19EmbedTests.kt index 57a3beb72..aafe64b5b 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/NIP19EmbedTests.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/encoders/NIP19EmbedTests.kt @@ -18,14 +18,10 @@ * 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 +package com.vitorpamplona.quartz.encoders import androidx.test.ext.junit.runners.AndroidJUnit4 import com.vitorpamplona.quartz.crypto.KeyPair -import com.vitorpamplona.quartz.encoders.Hex -import com.vitorpamplona.quartz.encoders.Nip19Bech32 -import com.vitorpamplona.quartz.encoders.decodePrivateKeyAsHexOrNull -import com.vitorpamplona.quartz.encoders.hexToByteArray import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.FhirResourceEvent import com.vitorpamplona.quartz.events.TextNoteEvent diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/ChatroomKeyTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/ChatroomKeyTest.kt similarity index 95% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/ChatroomKeyTest.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/events/ChatroomKeyTest.kt index 2e3758c28..58f679a98 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/ChatroomKeyTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/ChatroomKeyTest.kt @@ -18,10 +18,9 @@ * 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 +package com.vitorpamplona.quartz.events import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.vitorpamplona.quartz.events.ChatroomKey import kotlinx.collections.immutable.persistentSetOf import org.junit.Assert.assertEquals import org.junit.Test diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/CitationTests.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/CitationTests.kt similarity index 96% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/CitationTests.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/events/CitationTests.kt index 2a080e954..5a5b7bf7c 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/CitationTests.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/CitationTests.kt @@ -18,11 +18,9 @@ * 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 +package com.vitorpamplona.quartz.events import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.vitorpamplona.quartz.events.Event -import com.vitorpamplona.quartz.events.TextNoteEvent import junit.framework.TestCase.assertEquals import org.junit.Test import org.junit.runner.RunWith diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/EventSigCheck.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/EventSigCheck.kt similarity index 97% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/EventSigCheck.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/events/EventSigCheck.kt index 74dbdfc54..55b733111 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/EventSigCheck.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/EventSigCheck.kt @@ -18,10 +18,9 @@ * 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 +package com.vitorpamplona.quartz.events import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.vitorpamplona.quartz.events.Event import org.junit.Test import org.junit.runner.RunWith diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/GiftWrapEventTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/GiftWrapEventTest.kt similarity index 96% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/GiftWrapEventTest.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/events/GiftWrapEventTest.kt index c2aab0d1a..4fc96e6e2 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/GiftWrapEventTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/GiftWrapEventTest.kt @@ -18,18 +18,13 @@ * 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 +package com.vitorpamplona.quartz.events import androidx.test.ext.junit.runners.AndroidJUnit4 import com.vitorpamplona.quartz.crypto.KeyPair import com.vitorpamplona.quartz.encoders.Hex import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.hexToByteArray -import com.vitorpamplona.quartz.events.ChatMessageEvent -import com.vitorpamplona.quartz.events.Event -import com.vitorpamplona.quartz.events.GiftWrapEvent -import com.vitorpamplona.quartz.events.NIP17Factory -import com.vitorpamplona.quartz.events.SealedGossipEvent import com.vitorpamplona.quartz.signers.NostrSignerInternal import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals @@ -276,14 +271,12 @@ class GiftWrapEventTest { assertTrue(unwrappedMsgForReceiverByReceiver is SealedGossipEvent) if (unwrappedMsgForReceiverByReceiver is SealedGossipEvent) { - unwrappedMsgForReceiverByReceiver.cachedGossip(receiver) { - unwrappedGossipToReceiverByReceiver -> + unwrappedMsgForReceiverByReceiver.cachedGossip(receiver) { unwrappedGossipToReceiverByReceiver -> assertEquals("Hi There!", unwrappedGossipToReceiverByReceiver?.content) countDownDecryptLatch.countDown() } - unwrappedMsgForReceiverByReceiver.cachedGossip(sender) { unwrappedGossipToReceiverBySender, - -> + unwrappedMsgForReceiverByReceiver.cachedGossip(sender) { unwrappedGossipToReceiverBySender -> fail( "Should not be able to decrypt msg for the receiver by the receiver but decrypted with the sender", ) @@ -422,13 +415,11 @@ class GiftWrapEventTest { assertEquals(SealedGossipEvent.KIND, unwrappedMsgForSenderBySender.kind) if (unwrappedMsgForSenderBySender is SealedGossipEvent) { - unwrappedMsgForSenderBySender.cachedGossip(receiverA) { unwrappedGossipToSenderByReceiverA, - -> + unwrappedMsgForSenderBySender.cachedGossip(receiverA) { unwrappedGossipToSenderByReceiverA -> fail() } - unwrappedMsgForSenderBySender.cachedGossip(receiverB) { unwrappedGossipToSenderByReceiverB, - -> + unwrappedMsgForSenderBySender.cachedGossip(receiverB) { unwrappedGossipToSenderByReceiverB -> fail() } @@ -459,21 +450,18 @@ class GiftWrapEventTest { assertEquals(SealedGossipEvent.KIND, unwrappedMsgForReceiverAByReceiverA.kind) if (unwrappedMsgForReceiverAByReceiverA is SealedGossipEvent) { - unwrappedMsgForReceiverAByReceiverA.cachedGossip(receiverA) { - unwrappedGossipToReceiverAByReceiverA -> + unwrappedMsgForReceiverAByReceiverA.cachedGossip(receiverA) { unwrappedGossipToReceiverAByReceiverA -> assertEquals( "Who is going to the party tonight?", unwrappedGossipToReceiverAByReceiverA.content, ) } - unwrappedMsgForReceiverAByReceiverA.cachedGossip(sender) { - unwrappedGossipToReceiverABySender -> + unwrappedMsgForReceiverAByReceiverA.cachedGossip(sender) { unwrappedGossipToReceiverABySender -> fail() } - unwrappedMsgForReceiverAByReceiverA.cachedGossip(receiverB) { - unwrappedGossipToReceiverAByReceiverB -> + unwrappedMsgForReceiverAByReceiverA.cachedGossip(receiverB) { unwrappedGossipToReceiverAByReceiverB -> fail() } } @@ -495,13 +483,11 @@ class GiftWrapEventTest { assertEquals(SealedGossipEvent.KIND, unwrappedMsgForReceiverBByReceiverB.kind) if (unwrappedMsgForReceiverBByReceiverB is SealedGossipEvent) { - unwrappedMsgForReceiverBByReceiverB.cachedGossip(receiverA) { - unwrappedGossipToReceiverBByReceiverA -> + unwrappedMsgForReceiverBByReceiverB.cachedGossip(receiverA) { unwrappedGossipToReceiverBByReceiverA -> fail() } - unwrappedMsgForReceiverBByReceiverB.cachedGossip(receiverB) { - unwrappedGossipToReceiverBByReceiverB -> + unwrappedMsgForReceiverBByReceiverB.cachedGossip(receiverB) { unwrappedGossipToReceiverBByReceiverB -> assertEquals( "Who is going to the party tonight?", unwrappedGossipToReceiverBByReceiverB.content, @@ -510,8 +496,7 @@ class GiftWrapEventTest { countDownDecryptLatch.countDown() } - unwrappedMsgForReceiverBByReceiverB.cachedGossip(sender) { - unwrappedGossipToReceiverBBySender -> + unwrappedMsgForReceiverBByReceiverB.cachedGossip(sender) { unwrappedGossipToReceiverBBySender -> fail() } } @@ -538,8 +523,7 @@ class GiftWrapEventTest { ] ] } - """ - .trimIndent() + """.trimIndent() var gossip: Event? = null @@ -573,8 +557,7 @@ class GiftWrapEventTest { ] ] } - """ - .trimIndent() + """.trimIndent() val privateKey = "409ff7654141eaa16cd2161fe5bd127aeaef71f270c67587474b78998a8e3533" @@ -612,8 +595,7 @@ class GiftWrapEventTest { "wss://relay.damus.io/" ] } - """ - .trimIndent() + """.trimIndent() val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771" var gossip: Event? = null @@ -647,8 +629,7 @@ class GiftWrapEventTest { ] ] } - """ - .trimIndent() + """.trimIndent() val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771" @@ -687,8 +668,7 @@ class GiftWrapEventTest { "id": "d9fc85ece892ce45ffa737b3ddc0f8b752623181d75363b966191f8c03d2debe", "sig": "1b20416b83f4b5b8eead11e29c185f46b5e76d1960e4505210ddd00f7a6973cc11268f52a8989e3799b774d5f3a55db95bed4d66a1b6e88ab54becec5c771c17" } - """ - .trimIndent() + """.trimIndent() val privateKey = "7dd22cafc512c0bc363a259f6dcda515b13ae3351066d7976fd0bb79cbd0d700" @@ -753,8 +733,7 @@ class GiftWrapEventTest { "id": "ae625fd43612127d63bfd1967ba32ae915100842a205fc2c3b3fc02ab3827f08", "sig": "2807a7ab5728984144676fd34686267cbe6fe38bc2f65a3640ba9243c13e8a1ae5a9a051e8852aa0c997a3623d7fa066cf2073a233c6d7db46fb1a0d4c01e5a3" } - """ - .trimIndent() + """.trimIndent() val wrap = Event.fromJson(msg) as GiftWrapEvent wrap.checkSignature() diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/PrivateZapTests.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/PrivateZapTests.kt similarity index 97% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/PrivateZapTests.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/events/PrivateZapTests.kt index a13c8768d..5b5493850 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/PrivateZapTests.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/events/PrivateZapTests.kt @@ -18,15 +18,12 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.quartz +package com.vitorpamplona.quartz.events import androidx.test.ext.junit.runners.AndroidJUnit4 import com.vitorpamplona.quartz.crypto.KeyPair import com.vitorpamplona.quartz.encoders.Hex import com.vitorpamplona.quartz.encoders.toHexKey -import com.vitorpamplona.quartz.events.Event -import com.vitorpamplona.quartz.events.LnZapEvent -import com.vitorpamplona.quartz.events.LnZapRequestEvent import com.vitorpamplona.quartz.events.LnZapRequestEvent.Companion.createEncryptionPrivateKey import com.vitorpamplona.quartz.signers.NostrSignerInternal import junit.framework.TestCase.assertNotNull diff --git a/quartz/src/androidTest/java/com/vitorpamplona/quartz/OtsTest.kt b/quartz/src/androidTest/java/com/vitorpamplona/quartz/ots/OtsTest.kt similarity index 99% rename from quartz/src/androidTest/java/com/vitorpamplona/quartz/OtsTest.kt rename to quartz/src/androidTest/java/com/vitorpamplona/quartz/ots/OtsTest.kt index 838c279ca..6471fa8f7 100644 --- a/quartz/src/androidTest/java/com/vitorpamplona/quartz/OtsTest.kt +++ b/quartz/src/androidTest/java/com/vitorpamplona/quartz/ots/OtsTest.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 +package com.vitorpamplona.quartz.ots import androidx.test.ext.junit.runners.AndroidJUnit4 import com.vitorpamplona.quartz.crypto.KeyPair diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/CryptoUtils.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/CryptoUtils.kt index 64d0efd82..da9450d6a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/CryptoUtils.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/CryptoUtils.kt @@ -20,35 +20,32 @@ */ package com.vitorpamplona.quartz.crypto -import android.util.Log +import com.vitorpamplona.quartz.crypto.nip01.Nip01 +import com.vitorpamplona.quartz.crypto.nip04.Nip04 +import com.vitorpamplona.quartz.crypto.nip06.Nip06 +import com.vitorpamplona.quartz.crypto.nip44.Nip44 +import com.vitorpamplona.quartz.crypto.nip44.Nip44v2 +import com.vitorpamplona.quartz.crypto.nip49.Nip49 import com.vitorpamplona.quartz.encoders.HexKey -import com.vitorpamplona.quartz.events.Event import fr.acinq.secp256k1.Secp256k1 -import java.security.MessageDigest import java.security.SecureRandom -import java.util.Base64 object CryptoUtils { private val secp256k1 = Secp256k1.get() private val random = SecureRandom() - private val nip04 = Nip04(secp256k1, random) - private val nip44v1 = Nip44v1(secp256k1, random) - private val nip44v2 = Nip44v2(secp256k1, random) - private val nip49 = Nip49(secp256k1, random) + public val nip01 = Nip01(secp256k1, random) + public val nip06 = Nip06(secp256k1) + public val nip04 = Nip04(secp256k1, random) + public val nip44 = Nip44(secp256k1, random, nip04) + public val nip49 = Nip49(secp256k1, random) fun clearCache() { nip04.clearCache() - nip44v1.clearCache() - nip44v2.clearCache() + nip44.clearCache() } - fun randomInt(bound: Int): Int { - return random.nextInt(bound) - } - - /** Provides a 32B "private key" aka random number */ - fun privkeyCreate() = random(32) + fun randomInt(bound: Int): Int = random.nextInt(bound) fun random(size: Int): ByteArray { val bytes = ByteArray(size) @@ -56,39 +53,32 @@ object CryptoUtils { return bytes } - fun pubkeyCreateBitcoin(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey)) + /** Provides a 32B "private key" aka random number */ + fun privkeyCreate() = nip01.privkeyCreate() - fun pubkeyCreate(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey)).copyOfRange(1, 33) - - fun isPrivKeyValid(il: ByteArray): Boolean { - return secp256k1.secKeyVerify(il) - } + fun pubkeyCreate(privKey: ByteArray) = nip01.pubkeyCreate(privKey) fun signString( message: String, privKey: ByteArray, auxrand32: ByteArray = random(32), - ): ByteArray { - return sign(sha256(message.toByteArray()), privKey, auxrand32) - } + ): ByteArray = nip01.signString(message, privKey, auxrand32) fun sign( data: ByteArray, privKey: ByteArray, auxrand32: ByteArray? = null, - ): ByteArray = secp256k1.signSchnorr(data, privKey, auxrand32) + ): ByteArray = nip01.sign(data, privKey, auxrand32) fun verifySignature( signature: ByteArray, hash: ByteArray, pubKey: ByteArray, - ): Boolean { - return secp256k1.verifySchnorr(signature, hash, pubKey) - } + ): Boolean = nip01.verify(signature, hash, pubKey) fun sha256(data: ByteArray): ByteArray { // Creates a new buffer every time - return MessageDigest.getInstance("SHA-256").digest(data) + return nip01.sha256(data) } /** NIP 04 Utils */ @@ -96,189 +86,84 @@ object CryptoUtils { msg: String, privateKey: ByteArray, pubKey: ByteArray, - ): String { - return nip04.encrypt(msg, privateKey, pubKey) - } + ): String = nip04.encrypt(msg, privateKey, pubKey) fun encryptNIP04( msg: String, sharedSecret: ByteArray, - ): Nip04.EncryptedInfo { - return nip04.encrypt(msg, sharedSecret) - } + ): Nip04.EncryptedInfo = nip04.encrypt(msg, sharedSecret) fun decryptNIP04( msg: String, privateKey: ByteArray, pubKey: ByteArray, - ): String { - return nip04.decrypt(msg, privateKey, pubKey) - } + ): String = nip04.decrypt(msg, privateKey, pubKey) fun decryptNIP04( encryptedInfo: Nip04.EncryptedInfo, privateKey: ByteArray, pubKey: ByteArray, - ): String { - return nip04.decrypt(encryptedInfo, privateKey, pubKey) - } + ): String = nip04.decrypt(encryptedInfo, privateKey, pubKey) fun decryptNIP04( msg: String, sharedSecret: ByteArray, - ): String { - return nip04.decrypt(msg, sharedSecret) - } + ): String = nip04.decrypt(msg, sharedSecret) private fun decryptNIP04( cipher: String, nonce: String, sharedSecret: ByteArray, - ): String { - return nip04.decrypt(cipher, nonce, sharedSecret) - } + ): String = nip04.decrypt(cipher, nonce, sharedSecret) private fun decryptNIP04( encryptedMsg: ByteArray, iv: ByteArray, sharedSecret: ByteArray, - ): String { - return nip04.decrypt(encryptedMsg, iv, sharedSecret) - } + ): String = nip04.decrypt(encryptedMsg, iv, sharedSecret) fun getSharedSecretNIP04( privateKey: ByteArray, pubKey: ByteArray, - ): ByteArray { - return nip04.getSharedSecret(privateKey, pubKey) - } + ): ByteArray = nip04.getSharedSecret(privateKey, pubKey) fun computeSharedSecretNIP04( privateKey: ByteArray, pubKey: ByteArray, - ): ByteArray { - return nip04.computeSharedSecret(privateKey, pubKey) - } + ): ByteArray = nip04.computeSharedSecret(privateKey, pubKey) - /** NIP 44v1 Utils */ - fun encryptNIP44v1( + /** NIP 06 Utils */ + fun isValidMnemonic(mnemonic: String): Boolean = nip06.isValidMnemonic(mnemonic) + + fun privateKeyFromMnemonic( + mnemonic: String, + account: Long = 0, + ) = nip06.privateKeyFromMnemonic(mnemonic, account) + + /** NIP 44 Utils */ + fun getSharedSecretNIP44( + privateKey: ByteArray, + pubKey: ByteArray, + ): ByteArray = nip44.getSharedSecret(privateKey, pubKey) + + fun computeSharedSecretNIP44( + privateKey: ByteArray, + pubKey: ByteArray, + ): ByteArray = nip44.computeSharedSecret(privateKey, pubKey) + + fun encryptNIP44( msg: String, privateKey: ByteArray, pubKey: ByteArray, - ): Nip44v1.EncryptedInfo { - return nip44v1.encrypt(msg, privateKey, pubKey) - } - - fun encryptNIP44v1( - msg: String, - sharedSecret: ByteArray, - ): Nip44v1.EncryptedInfo { - return nip44v1.encrypt(msg, sharedSecret) - } - - fun decryptNIP44v1( - encryptedInfo: Nip44v1.EncryptedInfo, - privateKey: ByteArray, - pubKey: ByteArray, - ): String? { - return nip44v1.decrypt(encryptedInfo, privateKey, pubKey) - } - - fun decryptNIP44v1( - encryptedInfo: String, - privateKey: ByteArray, - pubKey: ByteArray, - ): String? { - return nip44v1.decrypt(encryptedInfo, privateKey, pubKey) - } - - fun decryptNIP44v1( - encryptedInfo: Nip44v1.EncryptedInfo, - sharedSecret: ByteArray, - ): String? { - return nip44v1.decrypt(encryptedInfo, sharedSecret) - } - - fun getSharedSecretNIP44v1( - privateKey: ByteArray, - pubKey: ByteArray, - ): ByteArray { - return nip44v1.getSharedSecret(privateKey, pubKey) - } - - fun computeSharedSecretNIP44v1( - privateKey: ByteArray, - pubKey: ByteArray, - ): ByteArray { - return nip44v1.computeSharedSecret(privateKey, pubKey) - } - - /** NIP 44v2 Utils */ - fun encryptNIP44v2( - msg: String, - privateKey: ByteArray, - pubKey: ByteArray, - ): Nip44v2.EncryptedInfo { - return nip44v2.encrypt(msg, privateKey, pubKey) - } - - fun encryptNIP44v2( - msg: String, - sharedSecret: ByteArray, - ): Nip44v2.EncryptedInfo { - return nip44v2.encrypt(msg, sharedSecret) - } - - fun decryptNIP44v2( - encryptedInfo: Nip44v2.EncryptedInfo, - privateKey: ByteArray, - pubKey: ByteArray, - ): String? { - return nip44v2.decrypt(encryptedInfo, privateKey, pubKey) - } - - fun decryptNIP44v2( - encryptedInfo: String, - privateKey: ByteArray, - pubKey: ByteArray, - ): String? { - return nip44v2.decrypt(encryptedInfo, privateKey, pubKey) - } - - fun decryptNIP44v2( - encryptedInfo: Nip44v2.EncryptedInfo, - sharedSecret: ByteArray, - ): String? { - return nip44v2.decrypt(encryptedInfo, sharedSecret) - } - - fun getSharedSecretNIP44v2( - privateKey: ByteArray, - pubKey: ByteArray, - ): ByteArray { - return nip44v2.getConversationKey(privateKey, pubKey) - } - - fun computeSharedSecretNIP44v2( - privateKey: ByteArray, - pubKey: ByteArray, - ): ByteArray { - return nip44v2.computeConversationKey(privateKey, pubKey) - } + ): Nip44v2.EncryptedInfo = nip44.encrypt(msg, privateKey, pubKey) fun decryptNIP44( payload: String, privateKey: ByteArray, pubKey: ByteArray, - ): String? { - if (payload.isEmpty()) return null - return if (payload[0] == '{') { - decryptNIP44FromJackson(payload, privateKey, pubKey) - } else { - decryptNIP44FromBase64(payload, privateKey, pubKey) - } - } + ): String? = nip44.decrypt(payload, privateKey, pubKey) + /** NIP 49 Utils */ fun decryptNIP49( payload: String, password: String, @@ -290,87 +175,5 @@ object CryptoUtils { fun encryptNIP49( key: HexKey, password: String, - ): String? { - return nip49.encrypt(key, password, 16, Nip49.EncryptedInfo.CLIENT_DOES_NOT_TRACK) - } - - class EncryptedInfoString( - val ciphertext: String, - val nonce: String, - val v: Int, - val mac: String?, - ) - - fun decryptNIP44FromJackson( - json: String, - privateKey: ByteArray, - pubKey: ByteArray, - ): String? { - return try { - val info = Event.mapper.readValue(json, EncryptedInfoString::class.java) - - when (info.v) { - Nip04.EncryptedInfo.V -> { - val encryptedInfo = - Nip04.EncryptedInfo( - ciphertext = Base64.getDecoder().decode(info.ciphertext), - nonce = Base64.getDecoder().decode(info.nonce), - ) - decryptNIP04(encryptedInfo, privateKey, pubKey) - } - Nip44v1.EncryptedInfo.V -> { - val encryptedInfo = - Nip44v1.EncryptedInfo( - ciphertext = Base64.getDecoder().decode(info.ciphertext), - nonce = Base64.getDecoder().decode(info.nonce), - ) - decryptNIP44v1(encryptedInfo, privateKey, pubKey) - } - Nip44v2.EncryptedInfo.V -> { - val encryptedInfo = - Nip44v2.EncryptedInfo( - ciphertext = Base64.getDecoder().decode(info.ciphertext), - nonce = Base64.getDecoder().decode(info.nonce), - mac = Base64.getDecoder().decode(info.mac), - ) - decryptNIP44v2(encryptedInfo, privateKey, pubKey) - } - else -> null - } - } catch (e: Exception) { - Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $json") - e.printStackTrace() - null - } - } - - fun decryptNIP44FromBase64( - payload: String, - privateKey: ByteArray, - pubKey: ByteArray, - ): String? { - if (payload.isEmpty()) return null - - return try { - val byteArray = Base64.getDecoder().decode(payload) - - when (byteArray[0].toInt()) { - Nip04.EncryptedInfo.V -> decryptNIP04(payload, privateKey, pubKey) - Nip44v1.EncryptedInfo.V -> decryptNIP44v1(payload, privateKey, pubKey) - Nip44v2.EncryptedInfo.V -> decryptNIP44v2(payload, privateKey, pubKey) - else -> null - } - } catch (e: Exception) { - Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $payload") - e.printStackTrace() - null - } - } - - fun sum( - first: ByteArray, - second: ByteArray, - ): ByteArray { - return secp256k1.privKeyTweakAdd(first, second) - } + ): String = nip49.encrypt(key, password) } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip01/Nip01.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip01/Nip01.kt new file mode 100644 index 000000000..3bbf58ea3 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip01/Nip01.kt @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.crypto.nip01 + +import com.vitorpamplona.quartz.crypto.CryptoUtils +import com.vitorpamplona.quartz.crypto.CryptoUtils.random +import fr.acinq.secp256k1.Secp256k1 +import java.security.MessageDigest +import java.security.SecureRandom + +class Nip01( + val secp256k1: Secp256k1, + val random: SecureRandom, +) { + /** Provides a 32B "private key" aka random number */ + fun privkeyCreate() = random(32) + + fun pubkeyCreate(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey)).copyOfRange(1, 33) + + fun sign( + data: ByteArray, + privKey: ByteArray, + auxrand32: ByteArray? = null, + ): ByteArray = secp256k1.signSchnorr(data, privKey, auxrand32) + + fun verify( + signature: ByteArray, + hash: ByteArray, + pubKey: ByteArray, + ): Boolean = secp256k1.verifySchnorr(signature, hash, pubKey) + + fun sha256(data: ByteArray): ByteArray { + // Creates a new buffer every time + return MessageDigest.getInstance("SHA-256").digest(data) + } + + fun signString( + message: String, + privKey: ByteArray, + auxrand32: ByteArray = random(32), + ): ByteArray = sign(CryptoUtils.sha256(message.toByteArray()), privKey, auxrand32) +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip04.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip04/Nip04.kt similarity index 93% rename from quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip04.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip04/Nip04.kt index 857432663..5d242dd66 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip04.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip04/Nip04.kt @@ -18,9 +18,11 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.quartz.crypto +package com.vitorpamplona.quartz.crypto.nip04 import android.util.Log +import com.vitorpamplona.quartz.crypto.SharedKeyCache +import com.vitorpamplona.quartz.crypto.nip44.Nip44v1 import com.vitorpamplona.quartz.encoders.Hex import fr.acinq.secp256k1.Secp256k1 import java.security.SecureRandom @@ -29,7 +31,10 @@ import javax.crypto.Cipher import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec -class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) { +class Nip04( + val secp256k1: Secp256k1, + val random: SecureRandom, +) { private val sharedKeyCache = SharedKeyCache() private val h02 = Hex.decode("02") @@ -41,9 +46,7 @@ class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) { msg: String, privateKey: ByteArray, pubKey: ByteArray, - ): String { - return encrypt(msg, getSharedSecret(privateKey, pubKey)).encodeToNIP04() - } + ): String = encrypt(msg, getSharedSecret(privateKey, pubKey)).encodeToNIP04() fun encrypt( msg: String, @@ -146,8 +149,8 @@ class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) { } } - fun decodeFromNIP04(payload: String): EncryptedInfo? { - return try { + fun decodeFromNIP04(payload: String): EncryptedInfo? = + try { val parts = payload.split("?iv=") EncryptedInfo( ciphertext = Base64.getDecoder().decode(parts[0]), @@ -157,15 +160,14 @@ class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) { Log.w("NIP04", "Unable to Parse encrypted payload: $payload") null } - } } - fun encodePayload(): String { - return Base64.getEncoder() + fun encodePayload(): String = + Base64 + .getEncoder() .encodeToString( byteArrayOf(V.toByte()) + nonce + ciphertext, ) - } fun encodeToNIP04(): String { val nonce = Base64.getEncoder().encodeToString(nonce) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Bip32SeedDerivation.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Bip32SeedDerivation.kt index d82899894..8c7bbeed5 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Bip32SeedDerivation.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Bip32SeedDerivation.kt @@ -20,14 +20,16 @@ */ package com.vitorpamplona.quartz.crypto.nip06 -import com.vitorpamplona.quartz.crypto.CryptoUtils +import fr.acinq.secp256k1.Secp256k1 import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec /* Simplified from: https://github.com/ACINQ/bitcoin-kmp/ */ -object Bip32SeedDerivation { +class Bip32SeedDerivation( + val secp256k1: Secp256k1, +) { class ExtendedPrivateKey( val secretkeybytes: ByteArray, val chaincode: ByteArray, @@ -53,6 +55,15 @@ object Bip32SeedDerivation { return mac.doFinal(data) } + fun isPrivKeyValid(il: ByteArray): Boolean = secp256k1.secKeyVerify(il) + + fun pubkeyCreateBitcoin(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey)) + + fun sum( + first: ByteArray, + second: ByteArray, + ): ByteArray = secp256k1.privKeyTweakAdd(first, second) + fun derivePrivateKey( parent: ExtendedPrivateKey, index: Long, @@ -62,17 +73,17 @@ object Bip32SeedDerivation { val data = arrayOf(0.toByte()).toByteArray() + parent.secretkeybytes + writeInt32BE(index.toInt()) hmac512(parent.chaincode, data) } else { - val data = CryptoUtils.pubkeyCreateBitcoin(parent.secretkeybytes) + writeInt32BE(index.toInt()) + val data = pubkeyCreateBitcoin(parent.secretkeybytes) + writeInt32BE(index.toInt()) hmac512(parent.chaincode, data) } val il = i.take(32).toByteArray() val ir = i.takeLast(32).toByteArray() - require(CryptoUtils.isPrivKeyValid(il)) { "cannot generate child private key: IL is invalid" } + require(isPrivKeyValid(il)) { "cannot generate child private key: IL is invalid" } - val key = CryptoUtils.sum(il, parent.secretkeybytes) + val key = sum(il, parent.secretkeybytes) - require(CryptoUtils.isPrivKeyValid(key)) { "cannot generate child private key: resulting private key is invalid" } + require(isPrivKeyValid(key)) { "cannot generate child private key: resulting private key is invalid" } return ExtendedPrivateKey(key, ir) } @@ -94,7 +105,7 @@ object Bip32SeedDerivation { fun derivePrivateKey( parent: ExtendedPrivateKey, chain: List, - ): ExtendedPrivateKey = chain.fold(parent, Bip32SeedDerivation::derivePrivateKey) + ): ExtendedPrivateKey = chain.fold(parent, this::derivePrivateKey) fun derivePrivateKey( parent: ExtendedPrivateKey, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Bip39Mnemonics.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Bip39Mnemonics.kt index a52e50175..d92d790ba 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Bip39Mnemonics.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Bip39Mnemonics.kt @@ -20,8 +20,8 @@ */ package com.vitorpamplona.quartz.crypto.nip06 -import com.vitorpamplona.quartz.crypto.CryptoUtils -import com.vitorpamplona.quartz.crypto.PBKDF +import com.vitorpamplona.quartz.crypto.nip49.PBKDF +import java.security.MessageDigest // CODE FROM: https://github.com/ACINQ/bitcoin-kmp/ @@ -37,6 +37,11 @@ object Bip39Mnemonics { return zeroes + digits } + fun sha256(data: ByteArray): ByteArray { + // Creates a new buffer every time + return MessageDigest.getInstance("SHA-256").digest(data) + } + private fun toBinary(x: ByteArray): List = x.map(Bip39Mnemonics::toBinary).flatten() private fun fromBinary(bin: List): Int = bin.fold(0) { acc, flag -> if (flag) 2 * acc + 1 else 2 * acc } @@ -45,13 +50,12 @@ object Bip39Mnemonics { items: List, size: Int, acc: List> = emptyList(), - ): List> { - return when { + ): List> = + when { items.isEmpty() -> acc items.size < size -> acc + listOf(items) else -> group(items.drop(size), size, acc + listOf(items.take(size))) } - } /** * @param mnemonics list of mnemonic words @@ -80,20 +84,19 @@ object Bip39Mnemonics { val databits = bits.subList(0, bitlength) val checksumbits = bits.subList(bitlength, bits.size) val data = group(databits, 8).map { fromBinary(it) }.map { it.toByte() }.toByteArray() - val check = toBinary(CryptoUtils.sha256(data)).take(data.size / 4) + val check = toBinary(sha256(data)).take(data.size / 4) require(check == checksumbits) { "invalid checksum" } } fun validate(mnemonics: String): Unit = validate(mnemonics.split(" ")) - fun isValid(mnemonics: String): Boolean { - return try { + fun isValid(mnemonics: String): Boolean = + try { validate(mnemonics) true } catch (e: Exception) { false } - } /** * BIP39 entropy encoding @@ -108,7 +111,7 @@ object Bip39Mnemonics { wordlist: Array, ): List { require(wordlist.size == 2048) { "invalid word list (size should be 2048)" } - val digits = toBinary(entropy) + toBinary(CryptoUtils.sha256(entropy)).take(entropy.size / 4) + val digits = toBinary(entropy) + toBinary(sha256(entropy)).take(entropy.size / 4) return group(digits, 11).map(Bip39Mnemonics::fromBinary).map { wordlist[it] } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Nip06.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Nip06.kt index d5628e4bc..8bb79e827 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Nip06.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip06/Nip06.kt @@ -20,29 +20,31 @@ */ package com.vitorpamplona.quartz.crypto.nip06 -class Nip06 { +import fr.acinq.secp256k1.Secp256k1 + +class Nip06( + val secp256k1: Secp256k1, +) { + val derivation = Bip32SeedDerivation(secp256k1) + // m/44'/1237'/'/0/0 private val nip6Base: KeyPath = KeyPath("") .derive(Hardener.hardened(44L)) .derive(Hardener.hardened(1237L)) - private fun nip6Path(account: Long): KeyPath { - return nip6Base.derive(Hardener.hardened(account)) + private fun nip6Path(account: Long): KeyPath = + nip6Base + .derive(Hardener.hardened(account)) .derive(0L) .derive(0L) - } - fun isValidMnemonic(mnemonic: String): Boolean { - return Bip39Mnemonics.isValid(mnemonic) - } + fun isValidMnemonic(mnemonic: String): Boolean = Bip39Mnemonics.isValid(mnemonic) fun privateKeyFromSeed( seed: ByteArray, account: Long = 0, - ): ByteArray { - return Bip32SeedDerivation.derivePrivateKey(Bip32SeedDerivation.generate(seed), nip6Path(account)) - } + ): ByteArray = derivation.derivePrivateKey(derivation.generate(seed), nip6Path(account)) fun privateKeyFromMnemonic( mnemonic: String, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Hkdf.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Hkdf.kt similarity index 94% rename from quartz/src/main/java/com/vitorpamplona/quartz/crypto/Hkdf.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Hkdf.kt index 55db69dde..258670df1 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Hkdf.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Hkdf.kt @@ -18,13 +18,16 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.quartz.crypto +package com.vitorpamplona.quartz.crypto.nip44 import java.nio.ByteBuffer import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec -class Hkdf(val algorithm: String = "HmacSHA256", val hashLen: Int = 32) { +class Hkdf( + val algorithm: String = "HmacSHA256", + val hashLen: Int = 32, +) { fun extract( key: ByteArray, salt: ByteArray, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Nip44.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Nip44.kt new file mode 100644 index 000000000..793549be3 --- /dev/null +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Nip44.kt @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.crypto.nip44 + +import android.util.Log +import com.vitorpamplona.quartz.crypto.nip04.Nip04 +import com.vitorpamplona.quartz.events.Event +import fr.acinq.secp256k1.Secp256k1 +import java.security.SecureRandom +import java.util.Base64 + +class Nip44( + secp256k1: Secp256k1, + random: SecureRandom, + val nip04: Nip04, +) { + public val v1 = Nip44v1(secp256k1, random) + public val v2 = Nip44v2(secp256k1, random) + + fun clearCache() { + v1.clearCache() + v2.clearCache() + } + + /** NIP 44v2 Utils */ + fun getSharedSecret( + privateKey: ByteArray, + pubKey: ByteArray, + ): ByteArray = v2.getConversationKey(privateKey, pubKey) + + fun computeSharedSecret( + privateKey: ByteArray, + pubKey: ByteArray, + ): ByteArray = v2.computeConversationKey(privateKey, pubKey) + + fun encrypt( + msg: String, + privateKey: ByteArray, + pubKey: ByteArray, + ): Nip44v2.EncryptedInfo { + // current version should be used. + return v2.encrypt(msg, privateKey, pubKey) + } + + // Always decrypt from any version/any encoding + fun decrypt( + payload: String, + privateKey: ByteArray, + pubKey: ByteArray, + ): String? { + if (payload.isEmpty()) return null + return if (payload[0] == '{') { + decryptNIP44FromJackson(payload, privateKey, pubKey) + } else { + decryptNIP44FromBase64(payload, privateKey, pubKey) + } + } + + class EncryptedInfoString( + val ciphertext: String, + val nonce: String, + val v: Int, + val mac: String?, + ) + + fun decryptNIP44FromJackson( + json: String, + privateKey: ByteArray, + pubKey: ByteArray, + ): String? = + try { + val info = Event.mapper.readValue(json, EncryptedInfoString::class.java) + + when (info.v) { + Nip04.EncryptedInfo.V -> { + val encryptedInfo = + Nip04.EncryptedInfo( + ciphertext = Base64.getDecoder().decode(info.ciphertext), + nonce = Base64.getDecoder().decode(info.nonce), + ) + nip04.decrypt(encryptedInfo, privateKey, pubKey) + } + Nip44v1.EncryptedInfo.V -> { + val encryptedInfo = + Nip44v1.EncryptedInfo( + ciphertext = Base64.getDecoder().decode(info.ciphertext), + nonce = Base64.getDecoder().decode(info.nonce), + ) + v1.decrypt(encryptedInfo, privateKey, pubKey) + } + Nip44v2.EncryptedInfo.V -> { + val encryptedInfo = + Nip44v2.EncryptedInfo( + ciphertext = Base64.getDecoder().decode(info.ciphertext), + nonce = Base64.getDecoder().decode(info.nonce), + mac = Base64.getDecoder().decode(info.mac), + ) + v2.decrypt(encryptedInfo, privateKey, pubKey) + } + else -> null + } + } catch (e: Exception) { + Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $json") + e.printStackTrace() + null + } + + fun decryptNIP44FromBase64( + payload: String, + privateKey: ByteArray, + pubKey: ByteArray, + ): String? { + if (payload.isEmpty()) return null + + return try { + val byteArray = Base64.getDecoder().decode(payload) + + when (byteArray[0].toInt()) { + Nip04.EncryptedInfo.V -> nip04.decrypt(payload, privateKey, pubKey) + Nip44v1.EncryptedInfo.V -> v1.decrypt(payload, privateKey, pubKey) + Nip44v2.EncryptedInfo.V -> v2.decrypt(payload, privateKey, pubKey) + else -> null + } + } catch (e: Exception) { + Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $payload") + e.printStackTrace() + null + } + } +} diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip44v1.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Nip44v1.kt similarity index 93% rename from quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip44v1.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Nip44v1.kt index 5872c5d43..926402447 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip44v1.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Nip44v1.kt @@ -18,18 +18,22 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.quartz.crypto +package com.vitorpamplona.quartz.crypto.nip44 import android.util.Log import com.goterl.lazysodium.SodiumAndroid import com.goterl.lazysodium.utils.Key +import com.vitorpamplona.quartz.crypto.SharedKeyCache import com.vitorpamplona.quartz.encoders.Hex import fr.acinq.secp256k1.Secp256k1 import java.security.MessageDigest import java.security.SecureRandom import java.util.Base64 -class Nip44v1(val secp256k1: Secp256k1, val random: SecureRandom) { +class Nip44v1( + val secp256k1: Secp256k1, + val random: SecureRandom, +) { private val sharedKeyCache = SharedKeyCache() private val h02 = Hex.decode("02") private val libSodium = SodiumAndroid() @@ -97,15 +101,13 @@ class Nip44v1(val secp256k1: Secp256k1, val random: SecureRandom) { fun decrypt( encryptedInfo: EncryptedInfo, sharedSecret: ByteArray, - ): String? { - return cryptoStreamXChaCha20Xor( + ): String? = + cryptoStreamXChaCha20Xor( libSodium = libSodium, messageBytes = encryptedInfo.ciphertext, nonce = encryptedInfo.nonce, key = Key.fromBytes(sharedSecret), - ) - ?.decodeToString() - } + )?.decodeToString() fun getSharedSecret( privateKey: ByteArray, @@ -155,11 +157,11 @@ class Nip44v1(val secp256k1: Secp256k1, val random: SecureRandom) { } } - fun encodePayload(): String { - return Base64.getEncoder() + fun encodePayload(): String = + Base64 + .getEncoder() .encodeToString( byteArrayOf(V.toByte()) + nonce + ciphertext, ) - } } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip44v2.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Nip44v2.kt similarity index 92% rename from quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip44v2.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Nip44v2.kt index 88fd5ba96..54cbf1c40 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip44v2.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/Nip44v2.kt @@ -18,11 +18,12 @@ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package com.vitorpamplona.quartz.crypto +package com.vitorpamplona.quartz.crypto.nip44 import android.util.Log import com.goterl.lazysodium.LazySodiumAndroid import com.goterl.lazysodium.SodiumAndroid +import com.vitorpamplona.quartz.crypto.SharedKeyCache import com.vitorpamplona.quartz.encoders.Hex import com.vitorpamplona.quartz.encoders.toHexKey import fr.acinq.secp256k1.Secp256k1 @@ -33,7 +34,10 @@ import java.util.Base64 import kotlin.math.floor import kotlin.math.log2 -class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) { +class Nip44v2( + val secp256k1: Secp256k1, + val random: SecureRandom, +) { private val sharedKeyCache = SharedKeyCache() private val libSodium = SodiumAndroid() @@ -55,9 +59,7 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) { msg: String, privateKey: ByteArray, pubKey: ByteArray, - ): EncryptedInfo { - return encrypt(msg, getConversationKey(privateKey, pubKey)) - } + ): EncryptedInfo = encrypt(msg, getConversationKey(privateKey, pubKey)) fun encrypt( plaintext: String, @@ -99,17 +101,13 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) { payload: String, privateKey: ByteArray, pubKey: ByteArray, - ): String? { - return decrypt(payload, getConversationKey(privateKey, pubKey)) - } + ): String? = decrypt(payload, getConversationKey(privateKey, pubKey)) fun decrypt( decoded: EncryptedInfo, privateKey: ByteArray, pubKey: ByteArray, - ): String? { - return decrypt(decoded, getConversationKey(privateKey, pubKey)) - } + ): String? = decrypt(decoded, getConversationKey(privateKey, pubKey)) fun decrypt( payload: String, @@ -173,7 +171,11 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) { check(unpaddedLen <= maxPlaintextSize) { "Message is too long ($unpaddedLen): $plaintext" } val prefix = - ByteBuffer.allocate(2).order(ByteOrder.BIG_ENDIAN).putShort(unpaddedLen.toShort()).array() + ByteBuffer + .allocate(2) + .order(ByteOrder.BIG_ENDIAN) + .putShort(unpaddedLen.toShort()) + .array() val suffix = ByteArray(calcPaddedLen(unpaddedLen) - unpaddedLen) return ByteBuffer.wrap(prefix + unpadded + suffix).array() } @@ -182,13 +184,12 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) { byte1: Byte, byte2: Byte, bigEndian: Boolean, - ): Int { - return if (bigEndian) { + ): Int = + if (bigEndian) { (byte1.toInt() and 0xFF shl 8 or (byte2.toInt() and 0xFF)) } else { (byte2.toInt() and 0xFF shl 8 or (byte1.toInt() and 0xFF)) } - } fun unpad(padded: ByteArray): String { val unpaddedLen: Int = bytesToInt(padded[0], padded[1], true) @@ -273,11 +274,11 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) { } } - fun encodePayload(): String { - return Base64.getEncoder() + fun encodePayload(): String = + Base64 + .getEncoder() .encodeToString( byteArrayOf(V.toByte()) + nonce + ciphertext + mac, ) - } } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/SodiumUtils.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/SodiumUtils.kt similarity index 95% rename from quartz/src/main/java/com/vitorpamplona/quartz/crypto/SodiumUtils.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/SodiumUtils.kt index 8d5c748a0..9961e4b1f 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/SodiumUtils.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip44/SodiumUtils.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.crypto +package com.vitorpamplona.quartz.crypto.nip44 import com.goterl.lazysodium.SodiumAndroid import com.goterl.lazysodium.utils.Key @@ -66,9 +66,7 @@ fun cryptoStreamXchacha20Xor( messageLen: Long, nonce: ByteArray, key: ByteArray, -): Int { - return cryptoStreamXchacha20XorIc(libSodium, cipher, message, messageLen, nonce, 0, key) -} +): Int = cryptoStreamXchacha20XorIc(libSodium, cipher, message, messageLen, nonce, 0, key) fun cryptoStreamXChaCha20Xor( libSodium: SodiumAndroid, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip49.kt b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/Nip49.kt similarity index 90% rename from quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip49.kt rename to quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/Nip49.kt index f537ef836..70a19252b 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/Nip49.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/Nip49.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.crypto +package com.vitorpamplona.quartz.crypto.nip49 import android.util.Log import com.goterl.lazysodium.LazySodiumAndroid @@ -32,16 +32,17 @@ import fr.acinq.secp256k1.Secp256k1 import java.security.SecureRandom import java.text.Normalizer -class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) { +class Nip49( + val secp256k1: Secp256k1, + val random: SecureRandom, +) { private val libSodium = SodiumAndroid() private val lazySodium = LazySodiumAndroid(libSodium) fun decrypt( nCryptSec: String, password: String, - ): HexKey { - return decrypt(EncryptedInfo.decodePayload(nCryptSec), password) - } + ): HexKey = decrypt(EncryptedInfo.decodePayload(nCryptSec), password) fun decrypt( encryptedInfo: EncryptedInfo?, @@ -75,11 +76,9 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) { fun encrypt( secretKeyHex: String, password: String, - logn: Int, - ksb: Byte, - ): String? { - return encrypt(secretKeyHex.hexToByteArray(), password, logn, ksb) - } + logn: Int = 16, + ksb: Byte = EncryptedInfo.CLIENT_DOES_NOT_TRACK, + ): String = encrypt(secretKeyHex.hexToByteArray(), password, logn, ksb) fun encrypt( secretKey: ByteArray, @@ -104,9 +103,12 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) { // byte[] ad, long adLen, // byte[] nSec, byte[] nPub, byte[] k lazySodium.cryptoAeadXChaCha20Poly1305IetfEncrypt( - ciphertext, longArrayOf(48), - secretKey, secretKey.size.toLong(), - byteArrayOf(ksb), 1, + ciphertext, + longArrayOf(48), + secretKey, + secretKey.size.toLong(), + byteArrayOf(ksb), + 1, key, nonce, key, @@ -163,8 +165,8 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) { } // ln(n.toDouble()).toInt().toByte(), - fun encodePayload(): String { - return Bech32.encodeBytes( + fun encodePayload(): String = + Bech32.encodeBytes( hrp = "ncryptsec", byteArrayOf( version, @@ -172,6 +174,5 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) { ) + salt + nonce + keySecurity + encryptedKey, Bech32.Encoding.Bech32, ) - } } } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/PBKDF.java b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/PBKDF.java similarity index 98% rename from quartz/src/main/java/com/vitorpamplona/quartz/crypto/PBKDF.java rename to quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/PBKDF.java index 638a8b315..6b7adfacd 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/PBKDF.java +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/PBKDF.java @@ -1,6 +1,6 @@ // Copyright (C) 2011 - Will Glozer. All rights reserved. -package com.vitorpamplona.quartz.crypto; +package com.vitorpamplona.quartz.crypto.nip49; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/SCrypt.java b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/SCrypt.java similarity index 99% rename from quartz/src/main/java/com/vitorpamplona/quartz/crypto/SCrypt.java rename to quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/SCrypt.java index c464013a2..bd5c89610 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/SCrypt.java +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/SCrypt.java @@ -1,6 +1,6 @@ // Copyright (C) 2011 - Will Glozer. All rights reserved. -package com.vitorpamplona.quartz.crypto; +package com.vitorpamplona.quartz.crypto.nip49; import javax.crypto.Mac; import java.security.GeneralSecurityException; diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/SecretKeyOrEmptySpec.java b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/SecretKeyOrEmptySpec.java similarity index 99% rename from quartz/src/main/java/com/vitorpamplona/quartz/crypto/SecretKeyOrEmptySpec.java rename to quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/SecretKeyOrEmptySpec.java index 7e3766bdc..d4e347c28 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/crypto/SecretKeyOrEmptySpec.java +++ b/quartz/src/main/java/com/vitorpamplona/quartz/crypto/nip49/SecretKeyOrEmptySpec.java @@ -23,7 +23,7 @@ * questions. */ -package com.vitorpamplona.quartz.crypto; +package com.vitorpamplona.quartz.crypto.nip49; import java.security.MessageDigest; import java.security.spec.KeySpec; diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/signers/NostrSignerInternal.kt b/quartz/src/main/java/com/vitorpamplona/quartz/signers/NostrSignerInternal.kt index 2537287b5..5728c3190 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/signers/NostrSignerInternal.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/signers/NostrSignerInternal.kt @@ -31,7 +31,9 @@ import com.vitorpamplona.quartz.events.EventFactory import com.vitorpamplona.quartz.events.LnZapPrivateEvent import com.vitorpamplona.quartz.events.LnZapRequestEvent -class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toHexKey()) { +class NostrSignerInternal( + val keyPair: KeyPair, +) : NostrSigner(keyPair.pubKey.toHexKey()) { override fun sign( createdAt: Long, kind: Int, @@ -52,10 +54,9 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH fun isUnsignedPrivateEvent( kind: Int, tags: Array>, - ): Boolean { - return kind == LnZapRequestEvent.KIND && + ): Boolean = + kind == LnZapRequestEvent.KIND && tags.any { t -> t.size > 1 && t[0] == "anon" && t[1].isBlank() } - } fun signNormal( createdAt: Long, @@ -123,12 +124,12 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH if (keyPair.privKey == null) return onReady( - CryptoUtils.encryptNIP44v2( - decryptedContent, - keyPair.privKey, - toPublicKey.hexToByteArray(), - ) - .encodePayload(), + CryptoUtils + .encryptNIP44( + decryptedContent, + keyPair.privKey, + toPublicKey.hexToByteArray(), + ).encodePayload(), ) } @@ -139,12 +140,12 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH ) { if (keyPair.privKey == null) return - CryptoUtils.decryptNIP44( - payload = encryptedContent, - privateKey = keyPair.privKey, - pubKey = fromPublicKey.hexToByteArray(), - ) - ?.let { onReady(it) } + CryptoUtils + .decryptNIP44( + payload = encryptedContent, + privateKey = keyPair.privKey, + pubKey = fromPublicKey.hexToByteArray(), + )?.let { onReady(it) } } private fun signPrivateZap( diff --git a/quartz/src/test/java/com/vitorpamplona/quartz/encoders/TimeUtilsTest.kt b/quartz/src/test/java/com/vitorpamplona/quartz/utils/TimeUtilsTest.kt similarity index 95% rename from quartz/src/test/java/com/vitorpamplona/quartz/encoders/TimeUtilsTest.kt rename to quartz/src/test/java/com/vitorpamplona/quartz/utils/TimeUtilsTest.kt index 34528d91b..19b296238 100644 --- a/quartz/src/test/java/com/vitorpamplona/quartz/encoders/TimeUtilsTest.kt +++ b/quartz/src/test/java/com/vitorpamplona/quartz/utils/TimeUtilsTest.kt @@ -18,10 +18,8 @@ * 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.encoders +package com.vitorpamplona.quartz.utils -import com.vitorpamplona.quartz.utils.DualCase -import com.vitorpamplona.quartz.utils.containsAny import junit.framework.TestCase import org.junit.Test @@ -30,8 +28,7 @@ class TimeUtilsTest { """Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. -""" - .intern() +""".intern() val atTheMiddle = DualCase("Lorem Ipsum".lowercase(), "Lorem Ipsum".uppercase()) val atTheBeginning = DualCase("contrAry".lowercase(), "contrAry".uppercase())