mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2024-09-30 00:40:49 +00:00
rearranges the crypto package into separate nips and reduces the amount of circular dependencies.
This commit is contained in:
parent
79ace7f18c
commit
a8a2bda9af
@ -33,7 +33,6 @@ import com.vitorpamplona.ammolite.relays.Client
|
|||||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
import com.vitorpamplona.ammolite.service.HttpClientManager
|
||||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||||
import com.vitorpamplona.quartz.crypto.nip06.Nip06
|
|
||||||
import com.vitorpamplona.quartz.encoders.Hex
|
import com.vitorpamplona.quartz.encoders.Hex
|
||||||
import com.vitorpamplona.quartz.encoders.Nip19Bech32
|
import com.vitorpamplona.quartz.encoders.Nip19Bech32
|
||||||
import com.vitorpamplona.quartz.encoders.bechToBytes
|
import com.vitorpamplona.quartz.encoders.bechToBytes
|
||||||
@ -129,8 +128,8 @@ class AccountStateViewModel : ViewModel() {
|
|||||||
proxyPort = proxyPort,
|
proxyPort = proxyPort,
|
||||||
signer = NostrSignerInternal(keyPair),
|
signer = NostrSignerInternal(keyPair),
|
||||||
)
|
)
|
||||||
} else if (key.contains(" ") && Nip06().isValidMnemonic(key)) {
|
} else if (key.contains(" ") && CryptoUtils.isValidMnemonic(key)) {
|
||||||
val keyPair = KeyPair(privKey = Nip06().privateKeyFromMnemonic(key))
|
val keyPair = KeyPair(privKey = CryptoUtils.privateKeyFromMnemonic(key))
|
||||||
Account(
|
Account(
|
||||||
keyPair,
|
keyPair,
|
||||||
proxy = proxy,
|
proxy = proxy,
|
||||||
|
@ -50,7 +50,7 @@ class CryptoBenchmark {
|
|||||||
val keyPair2 = KeyPair()
|
val keyPair2 = KeyPair()
|
||||||
|
|
||||||
benchmarkRule.measureRepeated {
|
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()
|
val keyPair2 = KeyPair()
|
||||||
|
|
||||||
benchmarkRule.measureRepeated {
|
benchmarkRule.measureRepeated {
|
||||||
assertNotNull(CryptoUtils.computeSharedSecretNIP44v1(keyPair1.privKey!!, keyPair2.pubKey))
|
assertNotNull(CryptoUtils.nip44.v1.computeSharedSecret(keyPair1.privKey!!, keyPair2.pubKey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ class GiftWrapReceivingBenchmark {
|
|||||||
|
|
||||||
benchmarkRule.measureRepeated {
|
benchmarkRule.measureRepeated {
|
||||||
assertNotNull(
|
assertNotNull(
|
||||||
CryptoUtils.decryptNIP44v2(
|
CryptoUtils.decryptNIP44(
|
||||||
wrap.content,
|
wrap.content,
|
||||||
receiver.keyPair.privKey!!,
|
receiver.keyPair.privKey!!,
|
||||||
wrap.pubKey.hexToByteArray(),
|
wrap.pubKey.hexToByteArray(),
|
||||||
@ -182,7 +182,7 @@ class GiftWrapReceivingBenchmark {
|
|||||||
val wrap = createWrap(sender, receiver)
|
val wrap = createWrap(sender, receiver)
|
||||||
|
|
||||||
val innerJson =
|
val innerJson =
|
||||||
CryptoUtils.decryptNIP44v2(
|
CryptoUtils.decryptNIP44(
|
||||||
wrap.content,
|
wrap.content,
|
||||||
receiver.keyPair.privKey!!,
|
receiver.keyPair.privKey!!,
|
||||||
wrap.pubKey.hexToByteArray(),
|
wrap.pubKey.hexToByteArray(),
|
||||||
@ -200,7 +200,7 @@ class GiftWrapReceivingBenchmark {
|
|||||||
|
|
||||||
benchmarkRule.measureRepeated {
|
benchmarkRule.measureRepeated {
|
||||||
assertNotNull(
|
assertNotNull(
|
||||||
CryptoUtils.decryptNIP44v2(
|
CryptoUtils.decryptNIP44(
|
||||||
seal.content,
|
seal.content,
|
||||||
receiver.keyPair.privKey!!,
|
receiver.keyPair.privKey!!,
|
||||||
seal.pubKey.hexToByteArray(),
|
seal.pubKey.hexToByteArray(),
|
||||||
@ -217,7 +217,7 @@ class GiftWrapReceivingBenchmark {
|
|||||||
val seal = createSeal(sender, receiver)
|
val seal = createSeal(sender, receiver)
|
||||||
|
|
||||||
val innerJson =
|
val innerJson =
|
||||||
CryptoUtils.decryptNIP44v2(
|
CryptoUtils.decryptNIP44(
|
||||||
seal.content,
|
seal.content,
|
||||||
receiver.keyPair.privKey!!,
|
receiver.keyPair.privKey!!,
|
||||||
seal.pubKey.hexToByteArray(),
|
seal.pubKey.hexToByteArray(),
|
||||||
|
@ -43,7 +43,7 @@ class CryptoUtilsTest {
|
|||||||
val publicKey = "765cd7cf91d3ad07423d114d5a39c61d52b2cdbc18ba055ddbbeec71fbe2aa2f"
|
val publicKey = "765cd7cf91d3ad07423d114d5a39c61d52b2cdbc18ba055ddbbeec71fbe2aa2f"
|
||||||
|
|
||||||
val key =
|
val key =
|
||||||
CryptoUtils.getSharedSecretNIP44v1(
|
CryptoUtils.nip44.v1.getSharedSecret(
|
||||||
privateKey = privateKey.hexToByteArray(),
|
privateKey = privateKey.hexToByteArray(),
|
||||||
pubKey = publicKey.hexToByteArray(),
|
pubKey = publicKey.hexToByteArray(),
|
||||||
)
|
)
|
||||||
@ -56,8 +56,8 @@ class CryptoUtilsTest {
|
|||||||
val sender = KeyPair()
|
val sender = KeyPair()
|
||||||
val receiver = KeyPair()
|
val receiver = KeyPair()
|
||||||
|
|
||||||
val sharedSecret1 = CryptoUtils.getSharedSecretNIP44v1(sender.privKey!!, receiver.pubKey)
|
val sharedSecret1 = CryptoUtils.nip44.v1.getSharedSecret(sender.privKey!!, receiver.pubKey)
|
||||||
val sharedSecret2 = CryptoUtils.getSharedSecretNIP44v1(receiver.privKey!!, sender.pubKey)
|
val sharedSecret2 = CryptoUtils.nip44.v1.getSharedSecret(receiver.privKey!!, sender.pubKey)
|
||||||
|
|
||||||
assertEquals(sharedSecret1.toHexKey(), sharedSecret2.toHexKey())
|
assertEquals(sharedSecret1.toHexKey(), sharedSecret2.toHexKey())
|
||||||
|
|
||||||
@ -88,8 +88,8 @@ class CryptoUtilsTest {
|
|||||||
val privateKey = CryptoUtils.privkeyCreate()
|
val privateKey = CryptoUtils.privkeyCreate()
|
||||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
||||||
|
|
||||||
val encrypted = CryptoUtils.encryptNIP44v1(msg, privateKey, publicKey)
|
val encrypted = CryptoUtils.nip44.v1.encrypt(msg, privateKey, publicKey)
|
||||||
val decrypted = CryptoUtils.decryptNIP44v1(encrypted, privateKey, publicKey)
|
val decrypted = CryptoUtils.nip44.v1.decrypt(encrypted, privateKey, publicKey)
|
||||||
|
|
||||||
assertEquals(msg, decrypted)
|
assertEquals(msg, decrypted)
|
||||||
}
|
}
|
||||||
@ -113,10 +113,10 @@ class CryptoUtilsTest {
|
|||||||
|
|
||||||
val privateKey = CryptoUtils.privkeyCreate()
|
val privateKey = CryptoUtils.privkeyCreate()
|
||||||
val publicKey = CryptoUtils.pubkeyCreate(privateKey)
|
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 encrypted = CryptoUtils.nip44.v1.encrypt(msg, sharedSecret)
|
||||||
val decrypted = CryptoUtils.decryptNIP44v1(encrypted, sharedSecret)
|
val decrypted = CryptoUtils.nip44.v1.decrypt(encrypted, sharedSecret)
|
||||||
|
|
||||||
assertEquals(msg, decrypted)
|
assertEquals(msg, decrypted)
|
||||||
}
|
}
|
||||||
|
@ -22,42 +22,45 @@ package com.vitorpamplona.quartz.crypto.nip06
|
|||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
import junit.framework.TestCase.assertEquals
|
import junit.framework.TestCase.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class Bip32SeedDerivationTest {
|
class Bip32SeedDerivationTest {
|
||||||
|
val seedDerivation = Bip32SeedDerivation(Secp256k1.get())
|
||||||
|
|
||||||
val masterBitcoin =
|
val masterBitcoin =
|
||||||
Bip32SeedDerivation.generate(
|
seedDerivation.generate(
|
||||||
Bip39Mnemonics.toSeed("gun please vital unable phone catalog explain raise erosion zoo truly exist", ""),
|
Bip39Mnemonics.toSeed("gun please vital unable phone catalog explain raise erosion zoo truly exist", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
val nostrMnemonic0 =
|
val nostrMnemonic0 =
|
||||||
Bip32SeedDerivation.generate(
|
seedDerivation.generate(
|
||||||
Bip39Mnemonics.toSeed("leader monkey parrot ring guide accident before fence cannon height naive bean", ""),
|
Bip39Mnemonics.toSeed("leader monkey parrot ring guide accident before fence cannon height naive bean", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
val nostrMnemonic1 =
|
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", ""),
|
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
|
@Test
|
||||||
fun restoreBIP44Wallet() {
|
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())
|
assertEquals("50b3e7905c642309c8a8b73df5a49757a10f2bebb5804571b9db9004cce8a190", privateKey.toHexKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun restoreBIP49Wallet() {
|
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())
|
assertEquals("154c02c0b66899291a19012207642ba096a2d3ebf51baf153c9495976feb1b30", privateKey.toHexKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun restoreBIP84Wallet() {
|
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())
|
assertEquals("53e8c09a0e3ddcd8d68821c1e99e823966e99df91fb253e1f453a443ba543cb2", privateKey.toHexKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.crypto.nip06
|
|||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
import junit.framework.TestCase.assertEquals
|
import junit.framework.TestCase.assertEquals
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -29,6 +30,8 @@ import org.junit.runner.RunWith
|
|||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class Nip06Test {
|
class Nip06Test {
|
||||||
|
val nip06 = Nip06(Secp256k1.get())
|
||||||
|
|
||||||
// private key (hex): 7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a
|
// private key (hex): 7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a
|
||||||
// nsec: nsec10allq0gjx7fddtzef0ax00mdps9t2kmtrldkyjfs8l5xruwvh2dq0lhhkp
|
// nsec: nsec10allq0gjx7fddtzef0ax00mdps9t2kmtrldkyjfs8l5xruwvh2dq0lhhkp
|
||||||
private val menemonic0 = "leader monkey parrot ring guide accident before fence cannon height naive bean"
|
private val menemonic0 = "leader monkey parrot ring guide accident before fence cannon height naive bean"
|
||||||
@ -43,26 +46,26 @@ class Nip06Test {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun fromSeedNip06TestVector0() {
|
fun fromSeedNip06TestVector0() {
|
||||||
val privateKeyHex = Nip06().privateKeyFromMnemonic(menemonic0).toHexKey()
|
val privateKeyHex = nip06.privateKeyFromMnemonic(menemonic0).toHexKey()
|
||||||
assertEquals("7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a", privateKeyHex)
|
assertEquals("7f7ff03d123792d6ac594bfa67bf6d0c0ab55b6b1fdb6249303fe861f1ccba9a", privateKeyHex)
|
||||||
|
|
||||||
val privateKeyHex21 = Nip06().privateKeyFromMnemonic(menemonic0, 21).toHexKey()
|
val privateKeyHex21 = nip06.privateKeyFromMnemonic(menemonic0, 21).toHexKey()
|
||||||
assertEquals("576390ec69951fcfbf159f2aac0965bb2e6d7a07da2334992af3225c57eaefca", privateKeyHex21)
|
assertEquals("576390ec69951fcfbf159f2aac0965bb2e6d7a07da2334992af3225c57eaefca", privateKeyHex21)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun fromSeedNip06TestVector1() {
|
fun fromSeedNip06TestVector1() {
|
||||||
val privateKeyHex = Nip06().privateKeyFromMnemonic(menemonic1).toHexKey()
|
val privateKeyHex = nip06.privateKeyFromMnemonic(menemonic1).toHexKey()
|
||||||
assertEquals("c15d739894c81a2fcfd3a2df85a0d2c0dbc47a280d092799f144d73d7ae78add", privateKeyHex)
|
assertEquals("c15d739894c81a2fcfd3a2df85a0d2c0dbc47a280d092799f144d73d7ae78add", privateKeyHex)
|
||||||
|
|
||||||
val privateKeyHex21 = Nip06().privateKeyFromMnemonic(menemonic1, 42).toHexKey()
|
val privateKeyHex21 = nip06.privateKeyFromMnemonic(menemonic1, 42).toHexKey()
|
||||||
assertEquals("ad993054383da74e955f8b86346365b5ffd6575992e1de3738dda9f94407052b", privateKeyHex21)
|
assertEquals("ad993054383da74e955f8b86346365b5ffd6575992e1de3738dda9f94407052b", privateKeyHex21)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("Snort is not correctly implemented")
|
@Ignore("Snort is not correctly implemented")
|
||||||
fun fromSeedNip06FromSnort() {
|
fun fromSeedNip06FromSnort() {
|
||||||
val privateKeyNsec = Nip06().privateKeyFromMnemonic(snortTest).toHexKey()
|
val privateKeyNsec = nip06.privateKeyFromMnemonic(snortTest).toHexKey()
|
||||||
assertEquals("nsec1ppw9ltr2x9qwg9a2qnmgv98tfruy2ejnja7me76mwmsreu3s8u2sscj5nt", privateKeyNsec)
|
assertEquals("nsec1ppw9ltr2x9qwg9a2qnmgv98tfruy2ejnja7me76mwmsreu3s8u2sscj5nt", privateKeyNsec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,13 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
|
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||||
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
import fr.acinq.secp256k1.Secp256k1
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
@ -79,8 +80,7 @@ class NIP44v2Test {
|
|||||||
v.plaintext!!,
|
v.plaintext!!,
|
||||||
conversationKey1,
|
conversationKey1,
|
||||||
v.nonce!!.hexToByteArray(),
|
v.nonce!!.hexToByteArray(),
|
||||||
)
|
).encodePayload()
|
||||||
.encodePayload()
|
|
||||||
|
|
||||||
assertEquals(v.payload, ciphertext)
|
assertEquals(v.payload, ciphertext)
|
||||||
|
|
||||||
@ -107,8 +107,7 @@ class NIP44v2Test {
|
|||||||
plaintext,
|
plaintext,
|
||||||
conversationKey,
|
conversationKey,
|
||||||
v.nonce!!.hexToByteArray(),
|
v.nonce!!.hexToByteArray(),
|
||||||
)
|
).encodePayload()
|
||||||
.encodePayload()
|
|
||||||
|
|
||||||
assertEquals(v.payloadSha256, sha256Hex(ciphertext.toByteArray(Charsets.UTF_8)))
|
assertEquals(v.payloadSha256, sha256Hex(ciphertext.toByteArray(Charsets.UTF_8)))
|
||||||
|
|
@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 com.vitorpamplona.quartz.crypto.CryptoUtils
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
@ -31,8 +31,8 @@ class HexEncodingTest {
|
|||||||
fun testHexEncodeDecodeOurs() {
|
fun testHexEncodeDecodeOurs() {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
testHex,
|
testHex,
|
||||||
com.vitorpamplona.quartz.encoders.Hex.encode(
|
Hex.encode(
|
||||||
com.vitorpamplona.quartz.encoders.Hex.decode(testHex),
|
Hex.decode(testHex),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -42,7 +42,8 @@ class HexEncodingTest {
|
|||||||
assertEquals(
|
assertEquals(
|
||||||
testHex,
|
testHex,
|
||||||
fr.acinq.secp256k1.Hex.encode(
|
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() {
|
fun testRandoms() {
|
||||||
for (i in 0..1000) {
|
for (i in 0..1000) {
|
||||||
val bytes = CryptoUtils.privkeyCreate()
|
val bytes = CryptoUtils.privkeyCreate()
|
||||||
val hex = fr.acinq.secp256k1.Hex.encode(bytes)
|
val hex =
|
||||||
|
fr.acinq.secp256k1.Hex
|
||||||
|
.encode(bytes)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
fr.acinq.secp256k1.Hex.encode(bytes),
|
fr.acinq.secp256k1.Hex
|
||||||
com.vitorpamplona.quartz.encoders.Hex.encode(bytes),
|
.encode(bytes),
|
||||||
|
Hex.encode(bytes),
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
bytes.toList(),
|
bytes.toList(),
|
||||||
com.vitorpamplona.quartz.encoders.Hex.decode(hex).toList(),
|
Hex.decode(hex).toList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,10 +18,9 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.encoders.LnInvoiceUtil
|
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
@ -18,14 +18,10 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
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.Event
|
||||||
import com.vitorpamplona.quartz.events.FhirResourceEvent
|
import com.vitorpamplona.quartz.events.FhirResourceEvent
|
||||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
import com.vitorpamplona.quartz.events.TextNoteEvent
|
@ -18,10 +18,9 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.events.ChatroomKey
|
|
||||||
import kotlinx.collections.immutable.persistentSetOf
|
import kotlinx.collections.immutable.persistentSetOf
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
@ -18,11 +18,9 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.events.Event
|
|
||||||
import com.vitorpamplona.quartz.events.TextNoteEvent
|
|
||||||
import junit.framework.TestCase.assertEquals
|
import junit.framework.TestCase.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
@ -18,10 +18,9 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.events.Event
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
@ -18,18 +18,13 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||||
import com.vitorpamplona.quartz.encoders.Hex
|
import com.vitorpamplona.quartz.encoders.Hex
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.encoders.hexToByteArray
|
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 com.vitorpamplona.quartz.signers.NostrSignerInternal
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertNotEquals
|
import org.junit.Assert.assertNotEquals
|
||||||
@ -276,14 +271,12 @@ class GiftWrapEventTest {
|
|||||||
assertTrue(unwrappedMsgForReceiverByReceiver is SealedGossipEvent)
|
assertTrue(unwrappedMsgForReceiverByReceiver is SealedGossipEvent)
|
||||||
|
|
||||||
if (unwrappedMsgForReceiverByReceiver is SealedGossipEvent) {
|
if (unwrappedMsgForReceiverByReceiver is SealedGossipEvent) {
|
||||||
unwrappedMsgForReceiverByReceiver.cachedGossip(receiver) {
|
unwrappedMsgForReceiverByReceiver.cachedGossip(receiver) { unwrappedGossipToReceiverByReceiver ->
|
||||||
unwrappedGossipToReceiverByReceiver ->
|
|
||||||
assertEquals("Hi There!", unwrappedGossipToReceiverByReceiver?.content)
|
assertEquals("Hi There!", unwrappedGossipToReceiverByReceiver?.content)
|
||||||
countDownDecryptLatch.countDown()
|
countDownDecryptLatch.countDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
unwrappedMsgForReceiverByReceiver.cachedGossip(sender) { unwrappedGossipToReceiverBySender,
|
unwrappedMsgForReceiverByReceiver.cachedGossip(sender) { unwrappedGossipToReceiverBySender ->
|
||||||
->
|
|
||||||
fail(
|
fail(
|
||||||
"Should not be able to decrypt msg for the receiver by the receiver but decrypted with the sender",
|
"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)
|
assertEquals(SealedGossipEvent.KIND, unwrappedMsgForSenderBySender.kind)
|
||||||
|
|
||||||
if (unwrappedMsgForSenderBySender is SealedGossipEvent) {
|
if (unwrappedMsgForSenderBySender is SealedGossipEvent) {
|
||||||
unwrappedMsgForSenderBySender.cachedGossip(receiverA) { unwrappedGossipToSenderByReceiverA,
|
unwrappedMsgForSenderBySender.cachedGossip(receiverA) { unwrappedGossipToSenderByReceiverA ->
|
||||||
->
|
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
unwrappedMsgForSenderBySender.cachedGossip(receiverB) { unwrappedGossipToSenderByReceiverB,
|
unwrappedMsgForSenderBySender.cachedGossip(receiverB) { unwrappedGossipToSenderByReceiverB ->
|
||||||
->
|
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,21 +450,18 @@ class GiftWrapEventTest {
|
|||||||
assertEquals(SealedGossipEvent.KIND, unwrappedMsgForReceiverAByReceiverA.kind)
|
assertEquals(SealedGossipEvent.KIND, unwrappedMsgForReceiverAByReceiverA.kind)
|
||||||
|
|
||||||
if (unwrappedMsgForReceiverAByReceiverA is SealedGossipEvent) {
|
if (unwrappedMsgForReceiverAByReceiverA is SealedGossipEvent) {
|
||||||
unwrappedMsgForReceiverAByReceiverA.cachedGossip(receiverA) {
|
unwrappedMsgForReceiverAByReceiverA.cachedGossip(receiverA) { unwrappedGossipToReceiverAByReceiverA ->
|
||||||
unwrappedGossipToReceiverAByReceiverA ->
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Who is going to the party tonight?",
|
"Who is going to the party tonight?",
|
||||||
unwrappedGossipToReceiverAByReceiverA.content,
|
unwrappedGossipToReceiverAByReceiverA.content,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
unwrappedMsgForReceiverAByReceiverA.cachedGossip(sender) {
|
unwrappedMsgForReceiverAByReceiverA.cachedGossip(sender) { unwrappedGossipToReceiverABySender ->
|
||||||
unwrappedGossipToReceiverABySender ->
|
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
unwrappedMsgForReceiverAByReceiverA.cachedGossip(receiverB) {
|
unwrappedMsgForReceiverAByReceiverA.cachedGossip(receiverB) { unwrappedGossipToReceiverAByReceiverB ->
|
||||||
unwrappedGossipToReceiverAByReceiverB ->
|
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -495,13 +483,11 @@ class GiftWrapEventTest {
|
|||||||
assertEquals(SealedGossipEvent.KIND, unwrappedMsgForReceiverBByReceiverB.kind)
|
assertEquals(SealedGossipEvent.KIND, unwrappedMsgForReceiverBByReceiverB.kind)
|
||||||
|
|
||||||
if (unwrappedMsgForReceiverBByReceiverB is SealedGossipEvent) {
|
if (unwrappedMsgForReceiverBByReceiverB is SealedGossipEvent) {
|
||||||
unwrappedMsgForReceiverBByReceiverB.cachedGossip(receiverA) {
|
unwrappedMsgForReceiverBByReceiverB.cachedGossip(receiverA) { unwrappedGossipToReceiverBByReceiverA ->
|
||||||
unwrappedGossipToReceiverBByReceiverA ->
|
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
unwrappedMsgForReceiverBByReceiverB.cachedGossip(receiverB) {
|
unwrappedMsgForReceiverBByReceiverB.cachedGossip(receiverB) { unwrappedGossipToReceiverBByReceiverB ->
|
||||||
unwrappedGossipToReceiverBByReceiverB ->
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Who is going to the party tonight?",
|
"Who is going to the party tonight?",
|
||||||
unwrappedGossipToReceiverBByReceiverB.content,
|
unwrappedGossipToReceiverBByReceiverB.content,
|
||||||
@ -510,8 +496,7 @@ class GiftWrapEventTest {
|
|||||||
countDownDecryptLatch.countDown()
|
countDownDecryptLatch.countDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
unwrappedMsgForReceiverBByReceiverB.cachedGossip(sender) {
|
unwrappedMsgForReceiverBByReceiverB.cachedGossip(sender) { unwrappedGossipToReceiverBBySender ->
|
||||||
unwrappedGossipToReceiverBBySender ->
|
|
||||||
fail()
|
fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -538,8 +523,7 @@ class GiftWrapEventTest {
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
""".trimIndent()
|
||||||
.trimIndent()
|
|
||||||
|
|
||||||
var gossip: Event? = null
|
var gossip: Event? = null
|
||||||
|
|
||||||
@ -573,8 +557,7 @@ class GiftWrapEventTest {
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
""".trimIndent()
|
||||||
.trimIndent()
|
|
||||||
|
|
||||||
val privateKey = "409ff7654141eaa16cd2161fe5bd127aeaef71f270c67587474b78998a8e3533"
|
val privateKey = "409ff7654141eaa16cd2161fe5bd127aeaef71f270c67587474b78998a8e3533"
|
||||||
|
|
||||||
@ -612,8 +595,7 @@ class GiftWrapEventTest {
|
|||||||
"wss://relay.damus.io/"
|
"wss://relay.damus.io/"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
""".trimIndent()
|
||||||
.trimIndent()
|
|
||||||
|
|
||||||
val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771"
|
val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771"
|
||||||
var gossip: Event? = null
|
var gossip: Event? = null
|
||||||
@ -647,8 +629,7 @@ class GiftWrapEventTest {
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
"""
|
""".trimIndent()
|
||||||
.trimIndent()
|
|
||||||
|
|
||||||
val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771"
|
val privateKey = "09e0051fdf5fdd9dd7a54713583006442cbdbf87bdcdab1a402f26e527d56771"
|
||||||
|
|
||||||
@ -687,8 +668,7 @@ class GiftWrapEventTest {
|
|||||||
"id": "d9fc85ece892ce45ffa737b3ddc0f8b752623181d75363b966191f8c03d2debe",
|
"id": "d9fc85ece892ce45ffa737b3ddc0f8b752623181d75363b966191f8c03d2debe",
|
||||||
"sig": "1b20416b83f4b5b8eead11e29c185f46b5e76d1960e4505210ddd00f7a6973cc11268f52a8989e3799b774d5f3a55db95bed4d66a1b6e88ab54becec5c771c17"
|
"sig": "1b20416b83f4b5b8eead11e29c185f46b5e76d1960e4505210ddd00f7a6973cc11268f52a8989e3799b774d5f3a55db95bed4d66a1b6e88ab54becec5c771c17"
|
||||||
}
|
}
|
||||||
"""
|
""".trimIndent()
|
||||||
.trimIndent()
|
|
||||||
|
|
||||||
val privateKey = "7dd22cafc512c0bc363a259f6dcda515b13ae3351066d7976fd0bb79cbd0d700"
|
val privateKey = "7dd22cafc512c0bc363a259f6dcda515b13ae3351066d7976fd0bb79cbd0d700"
|
||||||
|
|
||||||
@ -753,8 +733,7 @@ class GiftWrapEventTest {
|
|||||||
"id": "ae625fd43612127d63bfd1967ba32ae915100842a205fc2c3b3fc02ab3827f08",
|
"id": "ae625fd43612127d63bfd1967ba32ae915100842a205fc2c3b3fc02ab3827f08",
|
||||||
"sig": "2807a7ab5728984144676fd34686267cbe6fe38bc2f65a3640ba9243c13e8a1ae5a9a051e8852aa0c997a3623d7fa066cf2073a233c6d7db46fb1a0d4c01e5a3"
|
"sig": "2807a7ab5728984144676fd34686267cbe6fe38bc2f65a3640ba9243c13e8a1ae5a9a051e8852aa0c997a3623d7fa066cf2073a233c6d7db46fb1a0d4c01e5a3"
|
||||||
}
|
}
|
||||||
"""
|
""".trimIndent()
|
||||||
.trimIndent()
|
|
||||||
|
|
||||||
val wrap = Event.fromJson(msg) as GiftWrapEvent
|
val wrap = Event.fromJson(msg) as GiftWrapEvent
|
||||||
wrap.checkSignature()
|
wrap.checkSignature()
|
@ -18,15 +18,12 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||||
import com.vitorpamplona.quartz.encoders.Hex
|
import com.vitorpamplona.quartz.encoders.Hex
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
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.events.LnZapRequestEvent.Companion.createEncryptionPrivateKey
|
||||||
import com.vitorpamplona.quartz.signers.NostrSignerInternal
|
import com.vitorpamplona.quartz.signers.NostrSignerInternal
|
||||||
import junit.framework.TestCase.assertNotNull
|
import junit.framework.TestCase.assertNotNull
|
@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
import com.vitorpamplona.quartz.crypto.KeyPair
|
@ -20,35 +20,32 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.crypto
|
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.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.events.Event
|
|
||||||
import fr.acinq.secp256k1.Secp256k1
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
import java.security.MessageDigest
|
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.Base64
|
|
||||||
|
|
||||||
object CryptoUtils {
|
object CryptoUtils {
|
||||||
private val secp256k1 = Secp256k1.get()
|
private val secp256k1 = Secp256k1.get()
|
||||||
private val random = SecureRandom()
|
private val random = SecureRandom()
|
||||||
|
|
||||||
private val nip04 = Nip04(secp256k1, random)
|
public val nip01 = Nip01(secp256k1, random)
|
||||||
private val nip44v1 = Nip44v1(secp256k1, random)
|
public val nip06 = Nip06(secp256k1)
|
||||||
private val nip44v2 = Nip44v2(secp256k1, random)
|
public val nip04 = Nip04(secp256k1, random)
|
||||||
private val nip49 = Nip49(secp256k1, random)
|
public val nip44 = Nip44(secp256k1, random, nip04)
|
||||||
|
public val nip49 = Nip49(secp256k1, random)
|
||||||
|
|
||||||
fun clearCache() {
|
fun clearCache() {
|
||||||
nip04.clearCache()
|
nip04.clearCache()
|
||||||
nip44v1.clearCache()
|
nip44.clearCache()
|
||||||
nip44v2.clearCache()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun randomInt(bound: Int): Int {
|
fun randomInt(bound: Int): Int = random.nextInt(bound)
|
||||||
return random.nextInt(bound)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Provides a 32B "private key" aka random number */
|
|
||||||
fun privkeyCreate() = random(32)
|
|
||||||
|
|
||||||
fun random(size: Int): ByteArray {
|
fun random(size: Int): ByteArray {
|
||||||
val bytes = ByteArray(size)
|
val bytes = ByteArray(size)
|
||||||
@ -56,39 +53,32 @@ object CryptoUtils {
|
|||||||
return bytes
|
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 pubkeyCreate(privKey: ByteArray) = nip01.pubkeyCreate(privKey)
|
||||||
|
|
||||||
fun isPrivKeyValid(il: ByteArray): Boolean {
|
|
||||||
return secp256k1.secKeyVerify(il)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun signString(
|
fun signString(
|
||||||
message: String,
|
message: String,
|
||||||
privKey: ByteArray,
|
privKey: ByteArray,
|
||||||
auxrand32: ByteArray = random(32),
|
auxrand32: ByteArray = random(32),
|
||||||
): ByteArray {
|
): ByteArray = nip01.signString(message, privKey, auxrand32)
|
||||||
return sign(sha256(message.toByteArray()), privKey, auxrand32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sign(
|
fun sign(
|
||||||
data: ByteArray,
|
data: ByteArray,
|
||||||
privKey: ByteArray,
|
privKey: ByteArray,
|
||||||
auxrand32: ByteArray? = null,
|
auxrand32: ByteArray? = null,
|
||||||
): ByteArray = secp256k1.signSchnorr(data, privKey, auxrand32)
|
): ByteArray = nip01.sign(data, privKey, auxrand32)
|
||||||
|
|
||||||
fun verifySignature(
|
fun verifySignature(
|
||||||
signature: ByteArray,
|
signature: ByteArray,
|
||||||
hash: ByteArray,
|
hash: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): Boolean {
|
): Boolean = nip01.verify(signature, hash, pubKey)
|
||||||
return secp256k1.verifySchnorr(signature, hash, pubKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sha256(data: ByteArray): ByteArray {
|
fun sha256(data: ByteArray): ByteArray {
|
||||||
// Creates a new buffer every time
|
// Creates a new buffer every time
|
||||||
return MessageDigest.getInstance("SHA-256").digest(data)
|
return nip01.sha256(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** NIP 04 Utils */
|
/** NIP 04 Utils */
|
||||||
@ -96,189 +86,84 @@ object CryptoUtils {
|
|||||||
msg: String,
|
msg: String,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): String {
|
): String = nip04.encrypt(msg, privateKey, pubKey)
|
||||||
return nip04.encrypt(msg, privateKey, pubKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encryptNIP04(
|
fun encryptNIP04(
|
||||||
msg: String,
|
msg: String,
|
||||||
sharedSecret: ByteArray,
|
sharedSecret: ByteArray,
|
||||||
): Nip04.EncryptedInfo {
|
): Nip04.EncryptedInfo = nip04.encrypt(msg, sharedSecret)
|
||||||
return nip04.encrypt(msg, sharedSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decryptNIP04(
|
fun decryptNIP04(
|
||||||
msg: String,
|
msg: String,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): String {
|
): String = nip04.decrypt(msg, privateKey, pubKey)
|
||||||
return nip04.decrypt(msg, privateKey, pubKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decryptNIP04(
|
fun decryptNIP04(
|
||||||
encryptedInfo: Nip04.EncryptedInfo,
|
encryptedInfo: Nip04.EncryptedInfo,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): String {
|
): String = nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
||||||
return nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decryptNIP04(
|
fun decryptNIP04(
|
||||||
msg: String,
|
msg: String,
|
||||||
sharedSecret: ByteArray,
|
sharedSecret: ByteArray,
|
||||||
): String {
|
): String = nip04.decrypt(msg, sharedSecret)
|
||||||
return nip04.decrypt(msg, sharedSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun decryptNIP04(
|
private fun decryptNIP04(
|
||||||
cipher: String,
|
cipher: String,
|
||||||
nonce: String,
|
nonce: String,
|
||||||
sharedSecret: ByteArray,
|
sharedSecret: ByteArray,
|
||||||
): String {
|
): String = nip04.decrypt(cipher, nonce, sharedSecret)
|
||||||
return nip04.decrypt(cipher, nonce, sharedSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun decryptNIP04(
|
private fun decryptNIP04(
|
||||||
encryptedMsg: ByteArray,
|
encryptedMsg: ByteArray,
|
||||||
iv: ByteArray,
|
iv: ByteArray,
|
||||||
sharedSecret: ByteArray,
|
sharedSecret: ByteArray,
|
||||||
): String {
|
): String = nip04.decrypt(encryptedMsg, iv, sharedSecret)
|
||||||
return nip04.decrypt(encryptedMsg, iv, sharedSecret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSharedSecretNIP04(
|
fun getSharedSecretNIP04(
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): ByteArray {
|
): ByteArray = nip04.getSharedSecret(privateKey, pubKey)
|
||||||
return nip04.getSharedSecret(privateKey, pubKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun computeSharedSecretNIP04(
|
fun computeSharedSecretNIP04(
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): ByteArray {
|
): ByteArray = nip04.computeSharedSecret(privateKey, pubKey)
|
||||||
return nip04.computeSharedSecret(privateKey, pubKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** NIP 44v1 Utils */
|
/** NIP 06 Utils */
|
||||||
fun encryptNIP44v1(
|
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,
|
msg: String,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): Nip44v1.EncryptedInfo {
|
): Nip44v2.EncryptedInfo = nip44.encrypt(msg, privateKey, pubKey)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decryptNIP44(
|
fun decryptNIP44(
|
||||||
payload: String,
|
payload: String,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): String? {
|
): String? = nip44.decrypt(payload, privateKey, pubKey)
|
||||||
if (payload.isEmpty()) return null
|
|
||||||
return if (payload[0] == '{') {
|
|
||||||
decryptNIP44FromJackson(payload, privateKey, pubKey)
|
|
||||||
} else {
|
|
||||||
decryptNIP44FromBase64(payload, privateKey, pubKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/** NIP 49 Utils */
|
||||||
fun decryptNIP49(
|
fun decryptNIP49(
|
||||||
payload: String,
|
payload: String,
|
||||||
password: String,
|
password: String,
|
||||||
@ -290,87 +175,5 @@ object CryptoUtils {
|
|||||||
fun encryptNIP49(
|
fun encryptNIP49(
|
||||||
key: HexKey,
|
key: HexKey,
|
||||||
password: String,
|
password: String,
|
||||||
): String? {
|
): String = nip49.encrypt(key, password)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
@ -18,9 +18,11 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 android.util.Log
|
||||||
|
import com.vitorpamplona.quartz.crypto.SharedKeyCache
|
||||||
|
import com.vitorpamplona.quartz.crypto.nip44.Nip44v1
|
||||||
import com.vitorpamplona.quartz.encoders.Hex
|
import com.vitorpamplona.quartz.encoders.Hex
|
||||||
import fr.acinq.secp256k1.Secp256k1
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
@ -29,7 +31,10 @@ import javax.crypto.Cipher
|
|||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
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 sharedKeyCache = SharedKeyCache()
|
||||||
private val h02 = Hex.decode("02")
|
private val h02 = Hex.decode("02")
|
||||||
|
|
||||||
@ -41,9 +46,7 @@ class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
msg: String,
|
msg: String,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): String {
|
): String = encrypt(msg, getSharedSecret(privateKey, pubKey)).encodeToNIP04()
|
||||||
return encrypt(msg, getSharedSecret(privateKey, pubKey)).encodeToNIP04()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encrypt(
|
fun encrypt(
|
||||||
msg: String,
|
msg: String,
|
||||||
@ -146,8 +149,8 @@ class Nip04(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun decodeFromNIP04(payload: String): EncryptedInfo? {
|
fun decodeFromNIP04(payload: String): EncryptedInfo? =
|
||||||
return try {
|
try {
|
||||||
val parts = payload.split("?iv=")
|
val parts = payload.split("?iv=")
|
||||||
EncryptedInfo(
|
EncryptedInfo(
|
||||||
ciphertext = Base64.getDecoder().decode(parts[0]),
|
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")
|
Log.w("NIP04", "Unable to Parse encrypted payload: $payload")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodePayload(): String {
|
fun encodePayload(): String =
|
||||||
return Base64.getEncoder()
|
Base64
|
||||||
|
.getEncoder()
|
||||||
.encodeToString(
|
.encodeToString(
|
||||||
byteArrayOf(V.toByte()) + nonce + ciphertext,
|
byteArrayOf(V.toByte()) + nonce + ciphertext,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
fun encodeToNIP04(): String {
|
fun encodeToNIP04(): String {
|
||||||
val nonce = Base64.getEncoder().encodeToString(nonce)
|
val nonce = Base64.getEncoder().encodeToString(nonce)
|
@ -20,14 +20,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.crypto.nip06
|
package com.vitorpamplona.quartz.crypto.nip06
|
||||||
|
|
||||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
import javax.crypto.Mac
|
import javax.crypto.Mac
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Simplified from: https://github.com/ACINQ/bitcoin-kmp/
|
Simplified from: https://github.com/ACINQ/bitcoin-kmp/
|
||||||
*/
|
*/
|
||||||
object Bip32SeedDerivation {
|
class Bip32SeedDerivation(
|
||||||
|
val secp256k1: Secp256k1,
|
||||||
|
) {
|
||||||
class ExtendedPrivateKey(
|
class ExtendedPrivateKey(
|
||||||
val secretkeybytes: ByteArray,
|
val secretkeybytes: ByteArray,
|
||||||
val chaincode: ByteArray,
|
val chaincode: ByteArray,
|
||||||
@ -53,6 +55,15 @@ object Bip32SeedDerivation {
|
|||||||
return mac.doFinal(data)
|
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(
|
fun derivePrivateKey(
|
||||||
parent: ExtendedPrivateKey,
|
parent: ExtendedPrivateKey,
|
||||||
index: Long,
|
index: Long,
|
||||||
@ -62,17 +73,17 @@ object Bip32SeedDerivation {
|
|||||||
val data = arrayOf(0.toByte()).toByteArray() + parent.secretkeybytes + writeInt32BE(index.toInt())
|
val data = arrayOf(0.toByte()).toByteArray() + parent.secretkeybytes + writeInt32BE(index.toInt())
|
||||||
hmac512(parent.chaincode, data)
|
hmac512(parent.chaincode, data)
|
||||||
} else {
|
} else {
|
||||||
val data = CryptoUtils.pubkeyCreateBitcoin(parent.secretkeybytes) + writeInt32BE(index.toInt())
|
val data = pubkeyCreateBitcoin(parent.secretkeybytes) + writeInt32BE(index.toInt())
|
||||||
hmac512(parent.chaincode, data)
|
hmac512(parent.chaincode, data)
|
||||||
}
|
}
|
||||||
val il = i.take(32).toByteArray()
|
val il = i.take(32).toByteArray()
|
||||||
val ir = i.takeLast(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)
|
return ExtendedPrivateKey(key, ir)
|
||||||
}
|
}
|
||||||
@ -94,7 +105,7 @@ object Bip32SeedDerivation {
|
|||||||
fun derivePrivateKey(
|
fun derivePrivateKey(
|
||||||
parent: ExtendedPrivateKey,
|
parent: ExtendedPrivateKey,
|
||||||
chain: List<Long>,
|
chain: List<Long>,
|
||||||
): ExtendedPrivateKey = chain.fold(parent, Bip32SeedDerivation::derivePrivateKey)
|
): ExtendedPrivateKey = chain.fold(parent, this::derivePrivateKey)
|
||||||
|
|
||||||
fun derivePrivateKey(
|
fun derivePrivateKey(
|
||||||
parent: ExtendedPrivateKey,
|
parent: ExtendedPrivateKey,
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.crypto.nip06
|
package com.vitorpamplona.quartz.crypto.nip06
|
||||||
|
|
||||||
import com.vitorpamplona.quartz.crypto.CryptoUtils
|
import com.vitorpamplona.quartz.crypto.nip49.PBKDF
|
||||||
import com.vitorpamplona.quartz.crypto.PBKDF
|
import java.security.MessageDigest
|
||||||
|
|
||||||
// CODE FROM: https://github.com/ACINQ/bitcoin-kmp/
|
// CODE FROM: https://github.com/ACINQ/bitcoin-kmp/
|
||||||
|
|
||||||
@ -37,6 +37,11 @@ object Bip39Mnemonics {
|
|||||||
return zeroes + digits
|
return zeroes + digits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sha256(data: ByteArray): ByteArray {
|
||||||
|
// Creates a new buffer every time
|
||||||
|
return MessageDigest.getInstance("SHA-256").digest(data)
|
||||||
|
}
|
||||||
|
|
||||||
private fun toBinary(x: ByteArray): List<Boolean> = x.map(Bip39Mnemonics::toBinary).flatten()
|
private fun toBinary(x: ByteArray): List<Boolean> = x.map(Bip39Mnemonics::toBinary).flatten()
|
||||||
|
|
||||||
private fun fromBinary(bin: List<Boolean>): Int = bin.fold(0) { acc, flag -> if (flag) 2 * acc + 1 else 2 * acc }
|
private fun fromBinary(bin: List<Boolean>): Int = bin.fold(0) { acc, flag -> if (flag) 2 * acc + 1 else 2 * acc }
|
||||||
@ -45,13 +50,12 @@ object Bip39Mnemonics {
|
|||||||
items: List<Boolean>,
|
items: List<Boolean>,
|
||||||
size: Int,
|
size: Int,
|
||||||
acc: List<List<Boolean>> = emptyList(),
|
acc: List<List<Boolean>> = emptyList(),
|
||||||
): List<List<Boolean>> {
|
): List<List<Boolean>> =
|
||||||
return when {
|
when {
|
||||||
items.isEmpty() -> acc
|
items.isEmpty() -> acc
|
||||||
items.size < size -> acc + listOf(items)
|
items.size < size -> acc + listOf(items)
|
||||||
else -> group(items.drop(size), size, acc + listOf(items.take(size)))
|
else -> group(items.drop(size), size, acc + listOf(items.take(size)))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mnemonics list of mnemonic words
|
* @param mnemonics list of mnemonic words
|
||||||
@ -80,20 +84,19 @@ object Bip39Mnemonics {
|
|||||||
val databits = bits.subList(0, bitlength)
|
val databits = bits.subList(0, bitlength)
|
||||||
val checksumbits = bits.subList(bitlength, bits.size)
|
val checksumbits = bits.subList(bitlength, bits.size)
|
||||||
val data = group(databits, 8).map { fromBinary(it) }.map { it.toByte() }.toByteArray()
|
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" }
|
require(check == checksumbits) { "invalid checksum" }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun validate(mnemonics: String): Unit = validate(mnemonics.split(" "))
|
fun validate(mnemonics: String): Unit = validate(mnemonics.split(" "))
|
||||||
|
|
||||||
fun isValid(mnemonics: String): Boolean {
|
fun isValid(mnemonics: String): Boolean =
|
||||||
return try {
|
try {
|
||||||
validate(mnemonics)
|
validate(mnemonics)
|
||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BIP39 entropy encoding
|
* BIP39 entropy encoding
|
||||||
@ -108,7 +111,7 @@ object Bip39Mnemonics {
|
|||||||
wordlist: Array<String>,
|
wordlist: Array<String>,
|
||||||
): List<String> {
|
): List<String> {
|
||||||
require(wordlist.size == 2048) { "invalid word list (size should be 2048)" }
|
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] }
|
return group(digits, 11).map(Bip39Mnemonics::fromBinary).map { wordlist[it] }
|
||||||
}
|
}
|
||||||
|
@ -20,29 +20,31 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.crypto.nip06
|
package com.vitorpamplona.quartz.crypto.nip06
|
||||||
|
|
||||||
class Nip06 {
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
|
|
||||||
|
class Nip06(
|
||||||
|
val secp256k1: Secp256k1,
|
||||||
|
) {
|
||||||
|
val derivation = Bip32SeedDerivation(secp256k1)
|
||||||
|
|
||||||
// m/44'/1237'/<account>'/0/0
|
// m/44'/1237'/<account>'/0/0
|
||||||
private val nip6Base: KeyPath =
|
private val nip6Base: KeyPath =
|
||||||
KeyPath("")
|
KeyPath("")
|
||||||
.derive(Hardener.hardened(44L))
|
.derive(Hardener.hardened(44L))
|
||||||
.derive(Hardener.hardened(1237L))
|
.derive(Hardener.hardened(1237L))
|
||||||
|
|
||||||
private fun nip6Path(account: Long): KeyPath {
|
private fun nip6Path(account: Long): KeyPath =
|
||||||
return nip6Base.derive(Hardener.hardened(account))
|
nip6Base
|
||||||
|
.derive(Hardener.hardened(account))
|
||||||
.derive(0L)
|
.derive(0L)
|
||||||
.derive(0L)
|
.derive(0L)
|
||||||
}
|
|
||||||
|
|
||||||
fun isValidMnemonic(mnemonic: String): Boolean {
|
fun isValidMnemonic(mnemonic: String): Boolean = Bip39Mnemonics.isValid(mnemonic)
|
||||||
return Bip39Mnemonics.isValid(mnemonic)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun privateKeyFromSeed(
|
fun privateKeyFromSeed(
|
||||||
seed: ByteArray,
|
seed: ByteArray,
|
||||||
account: Long = 0,
|
account: Long = 0,
|
||||||
): ByteArray {
|
): ByteArray = derivation.derivePrivateKey(derivation.generate(seed), nip6Path(account))
|
||||||
return Bip32SeedDerivation.derivePrivateKey(Bip32SeedDerivation.generate(seed), nip6Path(account))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun privateKeyFromMnemonic(
|
fun privateKeyFromMnemonic(
|
||||||
mnemonic: String,
|
mnemonic: String,
|
||||||
|
@ -18,13 +18,16 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 java.nio.ByteBuffer
|
||||||
import javax.crypto.Mac
|
import javax.crypto.Mac
|
||||||
import javax.crypto.spec.SecretKeySpec
|
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(
|
fun extract(
|
||||||
key: ByteArray,
|
key: ByteArray,
|
||||||
salt: ByteArray,
|
salt: ByteArray,
|
@ -0,0 +1,148 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2024 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.crypto.nip44
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.vitorpamplona.quartz.crypto.nip04.Nip04
|
||||||
|
import com.vitorpamplona.quartz.events.Event
|
||||||
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import java.util.Base64
|
||||||
|
|
||||||
|
class Nip44(
|
||||||
|
secp256k1: Secp256k1,
|
||||||
|
random: SecureRandom,
|
||||||
|
val nip04: Nip04,
|
||||||
|
) {
|
||||||
|
public val v1 = Nip44v1(secp256k1, random)
|
||||||
|
public val v2 = Nip44v2(secp256k1, random)
|
||||||
|
|
||||||
|
fun clearCache() {
|
||||||
|
v1.clearCache()
|
||||||
|
v2.clearCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** NIP 44v2 Utils */
|
||||||
|
fun getSharedSecret(
|
||||||
|
privateKey: ByteArray,
|
||||||
|
pubKey: ByteArray,
|
||||||
|
): ByteArray = v2.getConversationKey(privateKey, pubKey)
|
||||||
|
|
||||||
|
fun computeSharedSecret(
|
||||||
|
privateKey: ByteArray,
|
||||||
|
pubKey: ByteArray,
|
||||||
|
): ByteArray = v2.computeConversationKey(privateKey, pubKey)
|
||||||
|
|
||||||
|
fun encrypt(
|
||||||
|
msg: String,
|
||||||
|
privateKey: ByteArray,
|
||||||
|
pubKey: ByteArray,
|
||||||
|
): Nip44v2.EncryptedInfo {
|
||||||
|
// current version should be used.
|
||||||
|
return v2.encrypt(msg, privateKey, pubKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always decrypt from any version/any encoding
|
||||||
|
fun decrypt(
|
||||||
|
payload: String,
|
||||||
|
privateKey: ByteArray,
|
||||||
|
pubKey: ByteArray,
|
||||||
|
): String? {
|
||||||
|
if (payload.isEmpty()) return null
|
||||||
|
return if (payload[0] == '{') {
|
||||||
|
decryptNIP44FromJackson(payload, privateKey, pubKey)
|
||||||
|
} else {
|
||||||
|
decryptNIP44FromBase64(payload, privateKey, pubKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EncryptedInfoString(
|
||||||
|
val ciphertext: String,
|
||||||
|
val nonce: String,
|
||||||
|
val v: Int,
|
||||||
|
val mac: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun decryptNIP44FromJackson(
|
||||||
|
json: String,
|
||||||
|
privateKey: ByteArray,
|
||||||
|
pubKey: ByteArray,
|
||||||
|
): String? =
|
||||||
|
try {
|
||||||
|
val info = Event.mapper.readValue(json, EncryptedInfoString::class.java)
|
||||||
|
|
||||||
|
when (info.v) {
|
||||||
|
Nip04.EncryptedInfo.V -> {
|
||||||
|
val encryptedInfo =
|
||||||
|
Nip04.EncryptedInfo(
|
||||||
|
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||||
|
nonce = Base64.getDecoder().decode(info.nonce),
|
||||||
|
)
|
||||||
|
nip04.decrypt(encryptedInfo, privateKey, pubKey)
|
||||||
|
}
|
||||||
|
Nip44v1.EncryptedInfo.V -> {
|
||||||
|
val encryptedInfo =
|
||||||
|
Nip44v1.EncryptedInfo(
|
||||||
|
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||||
|
nonce = Base64.getDecoder().decode(info.nonce),
|
||||||
|
)
|
||||||
|
v1.decrypt(encryptedInfo, privateKey, pubKey)
|
||||||
|
}
|
||||||
|
Nip44v2.EncryptedInfo.V -> {
|
||||||
|
val encryptedInfo =
|
||||||
|
Nip44v2.EncryptedInfo(
|
||||||
|
ciphertext = Base64.getDecoder().decode(info.ciphertext),
|
||||||
|
nonce = Base64.getDecoder().decode(info.nonce),
|
||||||
|
mac = Base64.getDecoder().decode(info.mac),
|
||||||
|
)
|
||||||
|
v2.decrypt(encryptedInfo, privateKey, pubKey)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $json")
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decryptNIP44FromBase64(
|
||||||
|
payload: String,
|
||||||
|
privateKey: ByteArray,
|
||||||
|
pubKey: ByteArray,
|
||||||
|
): String? {
|
||||||
|
if (payload.isEmpty()) return null
|
||||||
|
|
||||||
|
return try {
|
||||||
|
val byteArray = Base64.getDecoder().decode(payload)
|
||||||
|
|
||||||
|
when (byteArray[0].toInt()) {
|
||||||
|
Nip04.EncryptedInfo.V -> nip04.decrypt(payload, privateKey, pubKey)
|
||||||
|
Nip44v1.EncryptedInfo.V -> v1.decrypt(payload, privateKey, pubKey)
|
||||||
|
Nip44v2.EncryptedInfo.V -> v2.decrypt(payload, privateKey, pubKey)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("CryptoUtils", "Could not identify the version for NIP44 payload $payload")
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,18 +18,22 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 android.util.Log
|
||||||
import com.goterl.lazysodium.SodiumAndroid
|
import com.goterl.lazysodium.SodiumAndroid
|
||||||
import com.goterl.lazysodium.utils.Key
|
import com.goterl.lazysodium.utils.Key
|
||||||
|
import com.vitorpamplona.quartz.crypto.SharedKeyCache
|
||||||
import com.vitorpamplona.quartz.encoders.Hex
|
import com.vitorpamplona.quartz.encoders.Hex
|
||||||
import fr.acinq.secp256k1.Secp256k1
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.Base64
|
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 sharedKeyCache = SharedKeyCache()
|
||||||
private val h02 = Hex.decode("02")
|
private val h02 = Hex.decode("02")
|
||||||
private val libSodium = SodiumAndroid()
|
private val libSodium = SodiumAndroid()
|
||||||
@ -97,15 +101,13 @@ class Nip44v1(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
fun decrypt(
|
fun decrypt(
|
||||||
encryptedInfo: EncryptedInfo,
|
encryptedInfo: EncryptedInfo,
|
||||||
sharedSecret: ByteArray,
|
sharedSecret: ByteArray,
|
||||||
): String? {
|
): String? =
|
||||||
return cryptoStreamXChaCha20Xor(
|
cryptoStreamXChaCha20Xor(
|
||||||
libSodium = libSodium,
|
libSodium = libSodium,
|
||||||
messageBytes = encryptedInfo.ciphertext,
|
messageBytes = encryptedInfo.ciphertext,
|
||||||
nonce = encryptedInfo.nonce,
|
nonce = encryptedInfo.nonce,
|
||||||
key = Key.fromBytes(sharedSecret),
|
key = Key.fromBytes(sharedSecret),
|
||||||
)
|
)?.decodeToString()
|
||||||
?.decodeToString()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSharedSecret(
|
fun getSharedSecret(
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
@ -155,11 +157,11 @@ class Nip44v1(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodePayload(): String {
|
fun encodePayload(): String =
|
||||||
return Base64.getEncoder()
|
Base64
|
||||||
|
.getEncoder()
|
||||||
.encodeToString(
|
.encodeToString(
|
||||||
byteArrayOf(V.toByte()) + nonce + ciphertext,
|
byteArrayOf(V.toByte()) + nonce + ciphertext,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,11 +18,12 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 android.util.Log
|
||||||
import com.goterl.lazysodium.LazySodiumAndroid
|
import com.goterl.lazysodium.LazySodiumAndroid
|
||||||
import com.goterl.lazysodium.SodiumAndroid
|
import com.goterl.lazysodium.SodiumAndroid
|
||||||
|
import com.vitorpamplona.quartz.crypto.SharedKeyCache
|
||||||
import com.vitorpamplona.quartz.encoders.Hex
|
import com.vitorpamplona.quartz.encoders.Hex
|
||||||
import com.vitorpamplona.quartz.encoders.toHexKey
|
import com.vitorpamplona.quartz.encoders.toHexKey
|
||||||
import fr.acinq.secp256k1.Secp256k1
|
import fr.acinq.secp256k1.Secp256k1
|
||||||
@ -33,7 +34,10 @@ import java.util.Base64
|
|||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
import kotlin.math.log2
|
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 sharedKeyCache = SharedKeyCache()
|
||||||
|
|
||||||
private val libSodium = SodiumAndroid()
|
private val libSodium = SodiumAndroid()
|
||||||
@ -55,9 +59,7 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
msg: String,
|
msg: String,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): EncryptedInfo {
|
): EncryptedInfo = encrypt(msg, getConversationKey(privateKey, pubKey))
|
||||||
return encrypt(msg, getConversationKey(privateKey, pubKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encrypt(
|
fun encrypt(
|
||||||
plaintext: String,
|
plaintext: String,
|
||||||
@ -99,17 +101,13 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
payload: String,
|
payload: String,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): String? {
|
): String? = decrypt(payload, getConversationKey(privateKey, pubKey))
|
||||||
return decrypt(payload, getConversationKey(privateKey, pubKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decrypt(
|
fun decrypt(
|
||||||
decoded: EncryptedInfo,
|
decoded: EncryptedInfo,
|
||||||
privateKey: ByteArray,
|
privateKey: ByteArray,
|
||||||
pubKey: ByteArray,
|
pubKey: ByteArray,
|
||||||
): String? {
|
): String? = decrypt(decoded, getConversationKey(privateKey, pubKey))
|
||||||
return decrypt(decoded, getConversationKey(privateKey, pubKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decrypt(
|
fun decrypt(
|
||||||
payload: String,
|
payload: String,
|
||||||
@ -173,7 +171,11 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
check(unpaddedLen <= maxPlaintextSize) { "Message is too long ($unpaddedLen): $plaintext" }
|
check(unpaddedLen <= maxPlaintextSize) { "Message is too long ($unpaddedLen): $plaintext" }
|
||||||
|
|
||||||
val prefix =
|
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)
|
val suffix = ByteArray(calcPaddedLen(unpaddedLen) - unpaddedLen)
|
||||||
return ByteBuffer.wrap(prefix + unpadded + suffix).array()
|
return ByteBuffer.wrap(prefix + unpadded + suffix).array()
|
||||||
}
|
}
|
||||||
@ -182,13 +184,12 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
byte1: Byte,
|
byte1: Byte,
|
||||||
byte2: Byte,
|
byte2: Byte,
|
||||||
bigEndian: Boolean,
|
bigEndian: Boolean,
|
||||||
): Int {
|
): Int =
|
||||||
return if (bigEndian) {
|
if (bigEndian) {
|
||||||
(byte1.toInt() and 0xFF shl 8 or (byte2.toInt() and 0xFF))
|
(byte1.toInt() and 0xFF shl 8 or (byte2.toInt() and 0xFF))
|
||||||
} else {
|
} else {
|
||||||
(byte2.toInt() and 0xFF shl 8 or (byte1.toInt() and 0xFF))
|
(byte2.toInt() and 0xFF shl 8 or (byte1.toInt() and 0xFF))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun unpad(padded: ByteArray): String {
|
fun unpad(padded: ByteArray): String {
|
||||||
val unpaddedLen: Int = bytesToInt(padded[0], padded[1], true)
|
val unpaddedLen: Int = bytesToInt(padded[0], padded[1], true)
|
||||||
@ -273,11 +274,11 @@ class Nip44v2(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encodePayload(): String {
|
fun encodePayload(): String =
|
||||||
return Base64.getEncoder()
|
Base64
|
||||||
|
.getEncoder()
|
||||||
.encodeToString(
|
.encodeToString(
|
||||||
byteArrayOf(V.toByte()) + nonce + ciphertext + mac,
|
byteArrayOf(V.toByte()) + nonce + ciphertext + mac,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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.SodiumAndroid
|
||||||
import com.goterl.lazysodium.utils.Key
|
import com.goterl.lazysodium.utils.Key
|
||||||
@ -66,9 +66,7 @@ fun cryptoStreamXchacha20Xor(
|
|||||||
messageLen: Long,
|
messageLen: Long,
|
||||||
nonce: ByteArray,
|
nonce: ByteArray,
|
||||||
key: ByteArray,
|
key: ByteArray,
|
||||||
): Int {
|
): Int = cryptoStreamXchacha20XorIc(libSodium, cipher, message, messageLen, nonce, 0, key)
|
||||||
return cryptoStreamXchacha20XorIc(libSodium, cipher, message, messageLen, nonce, 0, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cryptoStreamXChaCha20Xor(
|
fun cryptoStreamXChaCha20Xor(
|
||||||
libSodium: SodiumAndroid,
|
libSodium: SodiumAndroid,
|
@ -18,7 +18,7 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 android.util.Log
|
||||||
import com.goterl.lazysodium.LazySodiumAndroid
|
import com.goterl.lazysodium.LazySodiumAndroid
|
||||||
@ -32,16 +32,17 @@ import fr.acinq.secp256k1.Secp256k1
|
|||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.text.Normalizer
|
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 libSodium = SodiumAndroid()
|
||||||
private val lazySodium = LazySodiumAndroid(libSodium)
|
private val lazySodium = LazySodiumAndroid(libSodium)
|
||||||
|
|
||||||
fun decrypt(
|
fun decrypt(
|
||||||
nCryptSec: String,
|
nCryptSec: String,
|
||||||
password: String,
|
password: String,
|
||||||
): HexKey {
|
): HexKey = decrypt(EncryptedInfo.decodePayload(nCryptSec), password)
|
||||||
return decrypt(EncryptedInfo.decodePayload(nCryptSec), password)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun decrypt(
|
fun decrypt(
|
||||||
encryptedInfo: EncryptedInfo?,
|
encryptedInfo: EncryptedInfo?,
|
||||||
@ -75,11 +76,9 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
fun encrypt(
|
fun encrypt(
|
||||||
secretKeyHex: String,
|
secretKeyHex: String,
|
||||||
password: String,
|
password: String,
|
||||||
logn: Int,
|
logn: Int = 16,
|
||||||
ksb: Byte,
|
ksb: Byte = EncryptedInfo.CLIENT_DOES_NOT_TRACK,
|
||||||
): String? {
|
): String = encrypt(secretKeyHex.hexToByteArray(), password, logn, ksb)
|
||||||
return encrypt(secretKeyHex.hexToByteArray(), password, logn, ksb)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun encrypt(
|
fun encrypt(
|
||||||
secretKey: ByteArray,
|
secretKey: ByteArray,
|
||||||
@ -104,9 +103,12 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
// byte[] ad, long adLen,
|
// byte[] ad, long adLen,
|
||||||
// byte[] nSec, byte[] nPub, byte[] k
|
// byte[] nSec, byte[] nPub, byte[] k
|
||||||
lazySodium.cryptoAeadXChaCha20Poly1305IetfEncrypt(
|
lazySodium.cryptoAeadXChaCha20Poly1305IetfEncrypt(
|
||||||
ciphertext, longArrayOf(48),
|
ciphertext,
|
||||||
secretKey, secretKey.size.toLong(),
|
longArrayOf(48),
|
||||||
byteArrayOf(ksb), 1,
|
secretKey,
|
||||||
|
secretKey.size.toLong(),
|
||||||
|
byteArrayOf(ksb),
|
||||||
|
1,
|
||||||
key,
|
key,
|
||||||
nonce,
|
nonce,
|
||||||
key,
|
key,
|
||||||
@ -163,8 +165,8 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ln(n.toDouble()).toInt().toByte(),
|
// ln(n.toDouble()).toInt().toByte(),
|
||||||
fun encodePayload(): String {
|
fun encodePayload(): String =
|
||||||
return Bech32.encodeBytes(
|
Bech32.encodeBytes(
|
||||||
hrp = "ncryptsec",
|
hrp = "ncryptsec",
|
||||||
byteArrayOf(
|
byteArrayOf(
|
||||||
version,
|
version,
|
||||||
@ -172,6 +174,5 @@ class Nip49(val secp256k1: Secp256k1, val random: SecureRandom) {
|
|||||||
) + salt + nonce + keySecurity + encryptedKey,
|
) + salt + nonce + keySecurity + encryptedKey,
|
||||||
Bech32.Encoding.Bech32,
|
Bech32.Encoding.Bech32,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
// 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.Mac;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
@ -1,6 +1,6 @@
|
|||||||
// Copyright (C) 2011 - Will Glozer. All rights reserved.
|
// 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.Mac;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
@ -23,7 +23,7 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.vitorpamplona.quartz.crypto;
|
package com.vitorpamplona.quartz.crypto.nip49;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.spec.KeySpec;
|
import java.security.spec.KeySpec;
|
@ -31,7 +31,9 @@ import com.vitorpamplona.quartz.events.EventFactory
|
|||||||
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
|
import com.vitorpamplona.quartz.events.LnZapPrivateEvent
|
||||||
import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
import com.vitorpamplona.quartz.events.LnZapRequestEvent
|
||||||
|
|
||||||
class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toHexKey()) {
|
class NostrSignerInternal(
|
||||||
|
val keyPair: KeyPair,
|
||||||
|
) : NostrSigner(keyPair.pubKey.toHexKey()) {
|
||||||
override fun <T : Event> sign(
|
override fun <T : Event> sign(
|
||||||
createdAt: Long,
|
createdAt: Long,
|
||||||
kind: Int,
|
kind: Int,
|
||||||
@ -52,10 +54,9 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH
|
|||||||
fun isUnsignedPrivateEvent(
|
fun isUnsignedPrivateEvent(
|
||||||
kind: Int,
|
kind: Int,
|
||||||
tags: Array<Array<String>>,
|
tags: Array<Array<String>>,
|
||||||
): Boolean {
|
): Boolean =
|
||||||
return kind == LnZapRequestEvent.KIND &&
|
kind == LnZapRequestEvent.KIND &&
|
||||||
tags.any { t -> t.size > 1 && t[0] == "anon" && t[1].isBlank() }
|
tags.any { t -> t.size > 1 && t[0] == "anon" && t[1].isBlank() }
|
||||||
}
|
|
||||||
|
|
||||||
fun <T : Event> signNormal(
|
fun <T : Event> signNormal(
|
||||||
createdAt: Long,
|
createdAt: Long,
|
||||||
@ -123,12 +124,12 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH
|
|||||||
if (keyPair.privKey == null) return
|
if (keyPair.privKey == null) return
|
||||||
|
|
||||||
onReady(
|
onReady(
|
||||||
CryptoUtils.encryptNIP44v2(
|
CryptoUtils
|
||||||
decryptedContent,
|
.encryptNIP44(
|
||||||
keyPair.privKey,
|
decryptedContent,
|
||||||
toPublicKey.hexToByteArray(),
|
keyPair.privKey,
|
||||||
)
|
toPublicKey.hexToByteArray(),
|
||||||
.encodePayload(),
|
).encodePayload(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,12 +140,12 @@ class NostrSignerInternal(val keyPair: KeyPair) : NostrSigner(keyPair.pubKey.toH
|
|||||||
) {
|
) {
|
||||||
if (keyPair.privKey == null) return
|
if (keyPair.privKey == null) return
|
||||||
|
|
||||||
CryptoUtils.decryptNIP44(
|
CryptoUtils
|
||||||
payload = encryptedContent,
|
.decryptNIP44(
|
||||||
privateKey = keyPair.privKey,
|
payload = encryptedContent,
|
||||||
pubKey = fromPublicKey.hexToByteArray(),
|
privateKey = keyPair.privKey,
|
||||||
)
|
pubKey = fromPublicKey.hexToByteArray(),
|
||||||
?.let { onReady(it) }
|
)?.let { onReady(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> signPrivateZap(
|
private fun <T> signPrivateZap(
|
||||||
|
@ -18,10 +18,8 @@
|
|||||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* 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.
|
* 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 junit.framework.TestCase
|
||||||
import org.junit.Test
|
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.
|
"""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.
|
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 atTheMiddle = DualCase("Lorem Ipsum".lowercase(), "Lorem Ipsum".uppercase())
|
||||||
val atTheBeginning = DualCase("contrAry".lowercase(), "contrAry".uppercase())
|
val atTheBeginning = DualCase("contrAry".lowercase(), "contrAry".uppercase())
|
Loading…
Reference in New Issue
Block a user