From 59c715397b6ba26cdd1c8b3e429ef3fc964fb506 Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:19:34 -0300 Subject: [PATCH 01/42] Fix overlapping buttons in new wallet generation dialog in vertical mode --- .../Wallet/WalletGUIFolder/WalletGUI.cs | 76 +++++++++++-------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index e946158..20c2634 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -2028,6 +2028,8 @@ private void TrySeedVerification(int[] wordsOrder, Action callback) }); } } + + private string[] backupScreenOptions = new string[] { "Copy to clipboard", "Continue", "Cancel" }; private void DoBackupScreen() { int curY; @@ -2050,42 +2052,52 @@ private void DoBackupScreen() rect.y += Border*2; GUI.Label(rect, "For your own safety, write down these words on a piece of paper and store it safely and hidden.\nThese words serve as a back-up of your wallet.\nWithout a backup, it is impossible to recover your private key,\nand any funds in the account will be lost if something happens to this device."); - var btnWidth = Units(10); - var btnHeight = Units(3); - curY = (int)(windowRect.height - Units(VerticalLayout ? 6: 7)); - DoButton(true, new Rect(windowRect.width / 3 - btnWidth / 2, curY, btnWidth, Units(2)), "Copy to clipboard", () => + DoButtonGrid(true, backupScreenOptions.Length, Units(2), 0, out _, (index) => { - GUIUtility.systemCopyBuffer = newWalletSeedPhrase; - MessageBox(MessageKind.Default, "Seed phrase copied to the clipboard."); - }); - - DoButton(true, new Rect((windowRect.width / 2) - btnWidth / 2, curY, btnWidth, Units(2)), "Continue", () => + return new MenuEntry(index, backupScreenOptions[index], true); + }, + (selected) => { - int[] wordsOrder; - if(AccountManager.Instance.Settings.mnemonicPhraseLength == MnemonicPhraseLength.Twelve_Words) - wordsOrder = Enumerable.Range(1, 12).ToArray(); - else - wordsOrder = Enumerable.Range(1, 24).ToArray(); + switch (selected) + { + case 0: + { + GUIUtility.systemCopyBuffer = newWalletSeedPhrase; + MessageBox(MessageKind.Default, "Seed phrase copied to the clipboard."); + break; + } - var rnd = new System.Security.Cryptography.RNGCryptoServiceProvider(); - wordsOrder = wordsOrder.OrderBy(x => GetNextInt32(rnd)).ToArray(); + case 1: + { + int[] wordsOrder; + if (AccountManager.Instance.Settings.mnemonicPhraseLength == MnemonicPhraseLength.Twelve_Words) + wordsOrder = Enumerable.Range(1, 12).ToArray(); + else + wordsOrder = Enumerable.Range(1, 24).ToArray(); - TrySeedVerification(wordsOrder, (success) => - { - if (success) - { - newWalletCallback(); - } - else - { - PopState(); - } - }); - }); - - DoButton(true, new Rect((windowRect.width / 3) * 2 - btnWidth / 2, curY, btnWidth, Units(2)), "Cancel", () => - { - PopState(); + var rnd = new System.Security.Cryptography.RNGCryptoServiceProvider(); + wordsOrder = wordsOrder.OrderBy(x => GetNextInt32(rnd)).ToArray(); + + TrySeedVerification(wordsOrder, (success) => + { + if (success) + { + newWalletCallback(); + } + else + { + PopState(); + } + }); + break; + } + + case 2: + { + PopState(); + break; + } + } }); } From 90da76a7f5bfb8306788d1536269983410b30beb Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Mon, 12 Aug 2024 18:20:06 -0300 Subject: [PATCH 02/42] Upgrade to Unity 2022.3.41f1 --- ProjectSettings/ProjectVersion.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt index b12da39..44ca791 100644 --- a/ProjectSettings/ProjectVersion.txt +++ b/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2022.3.37f1 -m_EditorVersionWithRevision: 2022.3.37f1 (340ba89e4c23) +m_EditorVersion: 2022.3.41f1 +m_EditorVersionWithRevision: 2022.3.41f1 (0f988161febf) From ccb85c4fca6fcafc958fd5fde18bcae7dc6cb480 Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Fri, 16 Aug 2024 20:59:39 -0300 Subject: [PATCH 03/42] Add misc cryptography utils --- Assets/Neo2Legacy/Core/NeoKeys.cs | 17 +++++++++++++++++ .../Phantasma.Core/src/Cryptography/Address.cs | 15 +++++++++++++++ .../src/Cryptography/ECDsa/ECDsa.cs | 16 +++++++++++++++- .../Poltergeist.Extensions/Ethereum/KeyPair.cs | 9 +++++++-- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/Assets/Neo2Legacy/Core/NeoKeys.cs b/Assets/Neo2Legacy/Core/NeoKeys.cs index 283c10d..d5e59e5 100644 --- a/Assets/Neo2Legacy/Core/NeoKeys.cs +++ b/Assets/Neo2Legacy/Core/NeoKeys.cs @@ -49,6 +49,23 @@ public NeoKeys(byte[] privateKey) this.WIF = GetWIF(); } + public static string PublicKeyToN2Address(byte[] publicKey) + { + byte[] compressedPublicKey; + if(publicKey.Length == 33) // Compressed + { + compressedPublicKey = publicKey; + } + else + { + compressedPublicKey = ECDsa.CompressPublicKey(publicKey); + } + + var signatureScriptN2 = CreateSignatureScript(compressedPublicKey); + var signatureHashN2 = NeoUtils.ToScriptHash(signatureScriptN2); + return NeoUtils.ToAddress(signatureHashN2); + } + public static NeoKeys FromWIF(string wif) { if (wif == null) throw new ArgumentNullException(); diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/Address.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/Address.cs index 014379a..9f763fb 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/Address.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/Address.cs @@ -388,6 +388,21 @@ public byte[] ToByteArray() return bytes; } + public byte[] GetPublicKey() + { + var bytes = new byte[32]; + if (_bytes != null) + { + if (_bytes.Length != LengthInBytes) + { + throw new Exception("invalid address byte length"); + } + ByteArrayUtils.CopyBytes(_bytes, 2, bytes, 0, _bytes.Length - 2); + } + + return bytes; + } + public int CompareTo(Address other) { byte[] x = ToByteArray(); diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs index b2c97e6..409b303 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs @@ -40,7 +40,21 @@ public static byte[] GetPublicKey(byte[] privateKey, bool compressed, ECDsaCurve var publicParams = new ECPublicKeyParameters(q, dom); return publicParams.Q.GetEncoded(compressed); } - + + public static byte[] CompressPublicKey(byte[] uncompressedPublicKey) + { + var x = new BigInteger(1, uncompressedPublicKey.Take(32).ToArray()); + var y = new BigInteger(1, uncompressedPublicKey.Skip(32).ToArray()); + + byte prefix = 0x02; + if(y.Mod(BigInteger.Two) != BigInteger.Zero) + { + prefix = 0x03; + } + + return new byte[] { prefix }.Concat(x.ToByteArrayUnsigned()).ToArray(); + } + public static ECDomainParameters GetDomain(ECDsaCurve curve) { X9ECParameters ecCurve; diff --git a/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs b/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs index 445e93f..dc1ca28 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs @@ -26,8 +26,7 @@ public EthereumKey(byte[] privateKey) this.PublicKey = ECDsa.GetPublicKey(privateKey, true, ECDsaCurve.Secp256k1); this.UncompressedPublicKey = ECDsa.GetPublicKey(privateKey, false, ECDsaCurve.Secp256k1).Skip(1).ToArray(); - var kak = new Sha3Keccack().CalculateHash(this.UncompressedPublicKey); - this.Address = "0x"+Base16.Encode( kak.Skip(12).ToArray()); + this.Address = PublicKeyToAddress(this.UncompressedPublicKey); } public static EthereumKey FromPrivateKey(string prv) @@ -77,6 +76,12 @@ private static byte[] XOR(byte[] x, byte[] y) return x.Zip(y, (a, b) => (byte)(a ^ b)).ToArray(); } + public static string PublicKeyToAddress(byte[] uncompressedPublicKey) + { + var kak = new Sha3Keccack().CalculateHash(uncompressedPublicKey); + return "0x" + Base16.Encode(kak.Skip(12).ToArray()); + } + public override string ToString() { return this.Address; From 9a7daa11e71545232bdec48f9a25ae7de6257376 Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:08:35 -0300 Subject: [PATCH 04/42] Add "Prove addresses" dialog and validation dialog --- .../WalletGUIFolder/WalletGUI.ModalPrompts.cs | 1 + .../WalletGUIFolder/WalletGUI.Settings.cs | 82 +++++++++++++++++++ .../Wallet/WalletGUIFolder/WalletGUI.cs | 50 ++++++++++- 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.ModalPrompts.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.ModalPrompts.cs index 830a4ab..bbc97ee 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.ModalPrompts.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.ModalPrompts.cs @@ -17,6 +17,7 @@ public partial class WalletGUI : MonoBehaviour private string[] ModalOkCopy_NoAutoCopy = new string[] { "Ok", "Copy to clipboard" }; private string[] ModalOkView = new string[] { "Ok", "View" }; private string[] ModalConfirmCancel = new string[] { "Confirm", "Cancel" }; + private string[] ModalSignCancel = new string[] { "Sign", "Cancel" }; private string[] ModalSendCancel = new string[] { "Send", "Cancel" }; private string[] ModalYesNo = new string[] { "Yes" , "No" }; private string[] ModalHexWifCancel = new string[] { "HEX format", "WIF format", "Cancel" }; diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs index 79381af..340c14d 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs @@ -7,6 +7,11 @@ using Phantasma.Business.VM.Utils; using Phantasma.Core.Domain; using Phantasma.Core.Numerics; +using System.Text; +using Phantasma.Core.Cryptography.ECDsa; +using Phantasma.Core.Cryptography.EdDSA; +using Phantasma.Core.Cryptography; +using Poltergeist.PhantasmaLegacy.Ethereum; namespace Poltergeist { @@ -495,6 +500,83 @@ private void DoSettingsScreen() }); curY += Units(3); + DoButton(true, new Rect(posX, curY, Units(16), Units(2)), "Verify proof of addresses", () => + { + ShowModal("Verify proof of addresses", "Enter proof of addresses messages", ModalState.Input, 2, -1, ModalConfirmCancel, 4, (result, input) => + { + if (result == PromptResult.Success) + { + input = input.Replace("\r", string.Empty); + var split = input.Split('\n'); + var signedMessage = string.Join('\n', split.Take(7)); + + if (settings.devMode) + { + Log.Write("signedMessage: '" + signedMessage + "'"); + } + + var phaAddress = split[1].Substring(19); + var ethAddress = split[2].Substring(18); + var ethPublicKey = split[3].Substring(21); + var neoAddress = split[4].Substring(20); + var neoPublicKey = split[5].Substring(23); + + var phaSignatureSerialized = split[7].Substring(21); + var ethSignatureSerialized = split[8].Substring(20); + var neoSignatureSerialized = split[9].Substring(22); + + MessageBox(MessageKind.Default, $"phaAddress: '{phaAddress}'\n ethAddress: '{ethAddress}'\n ethPublicKey: '{ethPublicKey}'\n neoAddress: '{neoAddress}'\n neoPublicKey: '{neoPublicKey}'"); + + var signedBytes = Encoding.ASCII.GetBytes(signedMessage); + + if (settings.devMode) + { + Log.Write("phaAddress: '" + phaAddress + "'"); + Log.Write("ethAddress: '" + ethAddress + "'"); + Log.Write("ethPublicKey: '" + ethPublicKey + "'"); + Log.Write("neoAddress: '" + neoAddress + "'"); + Log.Write("neoPublicKey: '" + neoPublicKey + "'"); + Log.Write("phaSignatureSerialized: '" + phaSignatureSerialized + "'"); + Log.Write("ethSignatureSerialized: '" + ethSignatureSerialized + "'"); + Log.Write("neoSignatureSerialized: '" + neoSignatureSerialized + "'"); + } + + if(!Ed25519.Verify(System.Convert.FromBase64String(phaSignatureSerialized), signedBytes, Address.FromText(phaAddress).GetPublicKey())) + { + MessageBox(MessageKind.Default, "Phantasma signature is incorrect!"); + return; + } + if(!ECDsa.Verify(signedBytes, System.Convert.FromBase64String(ethSignatureSerialized), System.Convert.FromBase64String(ethPublicKey), ECDsaCurve.Secp256k1)) + { + MessageBox(MessageKind.Default, "Ethereum signature is incorrect!"); + return; + } + if(!ECDsa.Verify(signedBytes, System.Convert.FromBase64String(neoSignatureSerialized), System.Convert.FromBase64String(neoPublicKey), ECDsaCurve.Secp256r1)) + { + MessageBox(MessageKind.Default, "Neo Legacy signature is incorrect!"); + return; + } + + var ethAddressFromPublicKey = new PhantasmaLegacy.Ethereum.Util.AddressUtil().ConvertToChecksumAddress(EthereumKey.PublicKeyToAddress(System.Convert.FromBase64String(ethPublicKey))); + if(ethAddress != ethAddressFromPublicKey) + { + MessageBox(MessageKind.Default, "Ethereum address is incorrect: " + ethAddressFromPublicKey); + return; + } + + var neo2AddressFromPublicKey = Poltergeist.Neo2.Core.NeoKeys.PublicKeyToN2Address(System.Convert.FromBase64String(neoPublicKey)); + if (neoAddress != neo2AddressFromPublicKey) + { + MessageBox(MessageKind.Default, "Neo Legacy address is incorrect: " + neo2AddressFromPublicKey); + return; + } + + MessageBox(MessageKind.Default, "Proof of addresses message was validated successfully"); + } + }); + }); + curY += Units(3); + curY += Units(1); DoButton(true, new Rect(posX, curY, Units(16), Units(2)), "Clear cache", () => { diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index 20c2634..fbc5e07 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -21,6 +21,10 @@ using Phantasma.Business.Blockchain; using Phantasma.Core.Types; using NBitcoin; +using Phantasma.Core.Utils; +using Phantasma.Core.Cryptography.ECDsa; +using Poltergeist.Neo2.Core; +using Phantasma.Core.Cryptography.EdDSA; namespace Poltergeist { @@ -3217,7 +3221,7 @@ private void DoAccountManagementMenu(int btnOffset) }, ignoreStoredPassword: true); } - else if(result == PromptResult.Custom_2) + else if (result == PromptResult.Custom_2) { RequestPassword("Export private key (WIF)", accountManager.CurrentPlatform, true, false, (auth) => { @@ -3379,6 +3383,48 @@ private void DoAccountManagementMenu(int btnOffset) } break; } + case 3: + { + var account = AccountManager.Instance.CurrentAccount; + + var wif = account.GetWif(AccountManager.Instance.CurrentPasswordHash); + var phantasmaKeys = PhantasmaKeys.FromWIF(wif); + var ethKeys = EthereumKey.FromWIF(wif); + var neoKeys = NeoKeys.FromWIF(wif); + + var message = "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + + "Phantasma address: " + account.phaAddress + "\n" + + "Ethereum address: " + account.ethAddress + "\n" + + "Ethereum public key: " + System.Convert.ToBase64String(ethKeys.UncompressedPublicKey) + "\n" + + "Neo Legacy address: " + account.neoAddress + "\n" + + "Neo Legacy public key: " + System.Convert.ToBase64String(neoKeys.PublicKey) + "\n"; + + var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); + + ShowModal("Proof of addresses", message, + ModalState.Message, AccountManager.MinAccountNameLength, AccountManager.MaxAccountNameLength, ModalSignCancel, 1, (result, name) => + { + if (result == PromptResult.Success) + { + var phaSignature = phantasmaKeys.Sign(messageBytes); + message += "\nPhantasma signature: " + System.Convert.ToBase64String(((Ed25519Signature)phaSignature).Bytes); + + { + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(messageBytes, ethKeys.PrivateKey, ethKeys.PublicKey, ECDsaCurve.Secp256k1); + var ethSignature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); + message += "\nEthereum signature: " + System.Convert.ToBase64String(signatureBytes); + } + { + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(messageBytes, neoKeys.PrivateKey, neoKeys.CompressedPublicKey, ECDsaCurve.Secp256r1); + var neoLegacySignature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256r1); + message += "\nNeo Legacy signature: " + System.Convert.ToBase64String(signatureBytes); + } + + ShowModal("Signed proof of addresses", message, ModalState.Message, 0, 0, ModalOkCopy, 0, (_, input) => { }); + } + }); + break; + } } }); } @@ -3424,7 +3470,7 @@ private void StakeSOUL(decimal selectedAmount, string msg, Action }); } - private string[] managerMenu = new string[] { "Export Private Key", "Migrate", "Set Name" }; + private string[] managerMenu = new string[] { "Export Private Key", "Migrate", "Set Name", "Prove addresses" }; private GUIState[] bottomMenu = new GUIState[] { GUIState.Balances, GUIState.History, GUIState.Account, GUIState.Exit }; From 96219ff5c332e2eb5936c0b9c27c712959eda47f Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Mon, 19 Aug 2024 19:15:23 -0300 Subject: [PATCH 05/42] Refactor previous commit --- .../Scripts/Wallet/ProofOfAddressesSigner.cs | 62 ++++++++++++ .../Wallet/ProofOfAddressesSigner.cs.meta | 11 +++ .../Wallet/ProofOfAddressesVerifier.cs | 96 +++++++++++++++++++ .../Wallet/ProofOfAddressesVerifier.cs.meta | 11 +++ .../WalletGUIFolder/WalletGUI.Settings.cs | 71 +++----------- .../Wallet/WalletGUIFolder/WalletGUI.cs | 38 +------- 6 files changed, 197 insertions(+), 92 deletions(-) create mode 100644 Assets/Scripts/Wallet/ProofOfAddressesSigner.cs create mode 100644 Assets/Scripts/Wallet/ProofOfAddressesSigner.cs.meta create mode 100644 Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs create mode 100644 Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs.meta diff --git a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs new file mode 100644 index 0000000..3d48700 --- /dev/null +++ b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs @@ -0,0 +1,62 @@ +using Phantasma.Core.Cryptography; +using Phantasma.Core.Cryptography.ECDsa; +using Poltergeist.PhantasmaLegacy.Ethereum; +using Poltergeist.Neo2.Core; +using Phantasma.Core.Cryptography.EdDSA; + +public class ProofOfAddressesSigner +{ + private readonly PhantasmaKeys PhantasmaKeys; + private readonly EthereumKey EthKeys; + private readonly NeoKeys NeoKeys; + + public ProofOfAddressesSigner(string wif) + { + PhantasmaKeys = PhantasmaKeys.FromWIF(wif); + EthKeys = EthereumKey.FromWIF(wif); + NeoKeys = NeoKeys.FromWIF(wif); + } + + public string GenerateMessage() + { + var phaAddress = PhantasmaKeys.Address.ToString(); + var ethAddress = new Poltergeist.PhantasmaLegacy.Ethereum.Util.AddressUtil().ConvertToChecksumAddress(EthereumKey.PublicKeyToAddress(EthKeys.UncompressedPublicKey)); + var ethPubKey = System.Convert.ToBase64String(EthKeys.UncompressedPublicKey); + + var neo2Address = Poltergeist.Neo2.Core.NeoKeys.PublicKeyToN2Address(NeoKeys.PublicKey); + var neo2PubKey = System.Convert.ToBase64String(NeoKeys.PublicKey); + + + var message = "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + + "Phantasma address: " + phaAddress + "\n" + + "Ethereum address: " + ethAddress + "\n" + + "Ethereum public key: " + ethPubKey + "\n" + + "Neo Legacy address: " + neo2Address + "\n" + + "Neo Legacy public key: " + neo2PubKey + "\n"; + + return message; + } + + public string GenerateSignedMessage() + { + var message = GenerateMessage(); + + var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); + + var phaSignature = PhantasmaKeys.Sign(messageBytes); + message += "\nPhantasma signature: " + System.Convert.ToBase64String(((Ed25519Signature)phaSignature).Bytes); + + { + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(messageBytes, EthKeys.PrivateKey, EthKeys.PublicKey, ECDsaCurve.Secp256k1); + message += "\nEthereum signature: " + System.Convert.ToBase64String(signatureBytes); + } + + { + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(messageBytes, NeoKeys.PrivateKey, NeoKeys.CompressedPublicKey, ECDsaCurve.Secp256r1); + message += "\nNeo Legacy signature: " + System.Convert.ToBase64String(signatureBytes); + } + + return message; + } +} + diff --git a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs.meta b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs.meta new file mode 100644 index 0000000..0b8f958 --- /dev/null +++ b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 280aa052a47ef6749bde6ae00fb0c54e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs new file mode 100644 index 0000000..6da3047 --- /dev/null +++ b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs @@ -0,0 +1,96 @@ +using System.Linq; +using Phantasma.Core.Cryptography; +using Phantasma.Core.Cryptography.ECDsa; +using Poltergeist.PhantasmaLegacy.Ethereum; +using Phantasma.Core.Cryptography.EdDSA; +using System.Text; + +public class ProofOfAddressesVerifier +{ + public readonly string Message; + public readonly string SignedMessage; + private readonly byte[] SignedMessageBytes; + + public readonly string PhaAddress; + private readonly byte[] PhaPublicKeyBytes; + + public readonly string EthAddress; + public readonly string EthPublicKey; + private readonly byte[] EthPublicKeyBytes; + + public readonly string Neo2Address; + public readonly string Neo2PublicKey; + private readonly byte[] Neo2PublicKeyBytes; + + public readonly string PhaSignature; + private readonly byte[] PhaSignatureBytes; + public readonly string EthSignature; + private readonly byte[] EthSignatureBytes; + public readonly string Neo2Signature; + private readonly byte[] Neo2SignatureBytes; + + public ProofOfAddressesVerifier(string message) + { + Message = message; + + var split = Message.Replace("\r", string.Empty).Split('\n'); + + SignedMessage = string.Join('\n', split.Take(7)); + SignedMessageBytes = Encoding.ASCII.GetBytes(SignedMessage); + + PhaAddress = split[1].Substring(19); + PhaPublicKeyBytes = Address.FromText(PhaAddress).GetPublicKey(); + EthAddress = split[2].Substring(18); + EthPublicKey = split[3].Substring(21); + EthPublicKeyBytes = System.Convert.FromBase64String(EthPublicKey); + Neo2Address = split[4].Substring(20); + Neo2PublicKey = split[5].Substring(23); + Neo2PublicKeyBytes = System.Convert.FromBase64String(Neo2PublicKey); + + PhaSignature = split[7].Substring(21); + PhaSignatureBytes = System.Convert.FromBase64String(PhaSignature); + EthSignature = split[8].Substring(20); + EthSignatureBytes = System.Convert.FromBase64String(EthSignature); + Neo2Signature = split[9].Substring(22); + Neo2SignatureBytes = System.Convert.FromBase64String(Neo2Signature); + } + + public (bool, string) VerifyMessage() + { + bool success = true; + string errorMessage = ""; + + if (!Ed25519.Verify(PhaSignatureBytes, SignedMessageBytes, PhaPublicKeyBytes)) + { + success = false; + errorMessage += "Phantasma signature is incorrect!\n"; + } + if (!ECDsa.Verify(SignedMessageBytes, EthSignatureBytes, EthPublicKeyBytes, ECDsaCurve.Secp256k1)) + { + success = false; + errorMessage += "Ethereum signature is incorrect!\n"; + } + if (!ECDsa.Verify(SignedMessageBytes, Neo2SignatureBytes, Neo2PublicKeyBytes, ECDsaCurve.Secp256r1)) + { + success = false; + errorMessage += "Neo Legacy signature is incorrect!\n"; + } + + var ethAddressFromPublicKey = new Poltergeist.PhantasmaLegacy.Ethereum.Util.AddressUtil().ConvertToChecksumAddress(EthereumKey.PublicKeyToAddress(EthPublicKeyBytes)); + if (EthAddress != ethAddressFromPublicKey) + { + success = false; + errorMessage += "Ethereum address is incorrect: " + ethAddressFromPublicKey + "\n"; + } + + var neo2AddressFromPublicKey = Poltergeist.Neo2.Core.NeoKeys.PublicKeyToN2Address(Neo2PublicKeyBytes); + if (Neo2Address != neo2AddressFromPublicKey) + { + success = false; + errorMessage += "Neo Legacy address is incorrect: " + neo2AddressFromPublicKey + "\n"; + } + + return (success, errorMessage); + } +} + diff --git a/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs.meta b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs.meta new file mode 100644 index 0000000..630671b --- /dev/null +++ b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 261d8f8f56182d6429a58ba2beab68f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs index 340c14d..a8fc005 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs @@ -3,15 +3,9 @@ using System.Numerics; using UnityEngine; using Phantasma.SDK; -using System.Numerics; using Phantasma.Business.VM.Utils; using Phantasma.Core.Domain; using Phantasma.Core.Numerics; -using System.Text; -using Phantasma.Core.Cryptography.ECDsa; -using Phantasma.Core.Cryptography.EdDSA; -using Phantasma.Core.Cryptography; -using Poltergeist.PhantasmaLegacy.Ethereum; namespace Poltergeist { @@ -506,68 +500,31 @@ private void DoSettingsScreen() { if (result == PromptResult.Success) { - input = input.Replace("\r", string.Empty); - var split = input.Split('\n'); - var signedMessage = string.Join('\n', split.Take(7)); + var verifier = new ProofOfAddressesVerifier(input); + if (settings.devMode) { - Log.Write("signedMessage: '" + signedMessage + "'"); + Log.Write("signedMessage: '" + verifier.SignedMessage + "'"); } - var phaAddress = split[1].Substring(19); - var ethAddress = split[2].Substring(18); - var ethPublicKey = split[3].Substring(21); - var neoAddress = split[4].Substring(20); - var neoPublicKey = split[5].Substring(23); - - var phaSignatureSerialized = split[7].Substring(21); - var ethSignatureSerialized = split[8].Substring(20); - var neoSignatureSerialized = split[9].Substring(22); - - MessageBox(MessageKind.Default, $"phaAddress: '{phaAddress}'\n ethAddress: '{ethAddress}'\n ethPublicKey: '{ethPublicKey}'\n neoAddress: '{neoAddress}'\n neoPublicKey: '{neoPublicKey}'"); - - var signedBytes = Encoding.ASCII.GetBytes(signedMessage); - if (settings.devMode) { - Log.Write("phaAddress: '" + phaAddress + "'"); - Log.Write("ethAddress: '" + ethAddress + "'"); - Log.Write("ethPublicKey: '" + ethPublicKey + "'"); - Log.Write("neoAddress: '" + neoAddress + "'"); - Log.Write("neoPublicKey: '" + neoPublicKey + "'"); - Log.Write("phaSignatureSerialized: '" + phaSignatureSerialized + "'"); - Log.Write("ethSignatureSerialized: '" + ethSignatureSerialized + "'"); - Log.Write("neoSignatureSerialized: '" + neoSignatureSerialized + "'"); + Log.Write("phaAddress: '" + verifier.PhaAddress + "'"); + Log.Write("ethAddress: '" + verifier.EthAddress + "'"); + Log.Write("ethPublicKey: '" + verifier.EthPublicKey + "'"); + Log.Write("neo2Address: '" + verifier.Neo2Address + "'"); + Log.Write("neo2PublicKey: '" + verifier.Neo2PublicKey + "'"); + Log.Write("phaSignature: '" + verifier.PhaSignature + "'"); + Log.Write("ethSignature: '" + verifier.EthSignature + "'"); + Log.Write("neo2Signature: '" + verifier.Neo2Signature + "'"); } - if(!Ed25519.Verify(System.Convert.FromBase64String(phaSignatureSerialized), signedBytes, Address.FromText(phaAddress).GetPublicKey())) - { - MessageBox(MessageKind.Default, "Phantasma signature is incorrect!"); - return; - } - if(!ECDsa.Verify(signedBytes, System.Convert.FromBase64String(ethSignatureSerialized), System.Convert.FromBase64String(ethPublicKey), ECDsaCurve.Secp256k1)) - { - MessageBox(MessageKind.Default, "Ethereum signature is incorrect!"); - return; - } - if(!ECDsa.Verify(signedBytes, System.Convert.FromBase64String(neoSignatureSerialized), System.Convert.FromBase64String(neoPublicKey), ECDsaCurve.Secp256r1)) - { - MessageBox(MessageKind.Default, "Neo Legacy signature is incorrect!"); - return; - } - - var ethAddressFromPublicKey = new PhantasmaLegacy.Ethereum.Util.AddressUtil().ConvertToChecksumAddress(EthereumKey.PublicKeyToAddress(System.Convert.FromBase64String(ethPublicKey))); - if(ethAddress != ethAddressFromPublicKey) - { - MessageBox(MessageKind.Default, "Ethereum address is incorrect: " + ethAddressFromPublicKey); - return; - } + var (success, errorMessage) = verifier.VerifyMessage(); - var neo2AddressFromPublicKey = Poltergeist.Neo2.Core.NeoKeys.PublicKeyToN2Address(System.Convert.FromBase64String(neoPublicKey)); - if (neoAddress != neo2AddressFromPublicKey) + if (!success) { - MessageBox(MessageKind.Default, "Neo Legacy address is incorrect: " + neo2AddressFromPublicKey); + MessageBox(MessageKind.Error, errorMessage); return; } diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index fbc5e07..194bc95 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -21,10 +21,6 @@ using Phantasma.Business.Blockchain; using Phantasma.Core.Types; using NBitcoin; -using Phantasma.Core.Utils; -using Phantasma.Core.Cryptography.ECDsa; -using Poltergeist.Neo2.Core; -using Phantasma.Core.Cryptography.EdDSA; namespace Poltergeist { @@ -3385,42 +3381,14 @@ private void DoAccountManagementMenu(int btnOffset) } case 3: { - var account = AccountManager.Instance.CurrentAccount; + var signer = new ProofOfAddressesSigner(AccountManager.Instance.CurrentAccount.GetWif(AccountManager.Instance.CurrentPasswordHash)); - var wif = account.GetWif(AccountManager.Instance.CurrentPasswordHash); - var phantasmaKeys = PhantasmaKeys.FromWIF(wif); - var ethKeys = EthereumKey.FromWIF(wif); - var neoKeys = NeoKeys.FromWIF(wif); - - var message = "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + - "Phantasma address: " + account.phaAddress + "\n" + - "Ethereum address: " + account.ethAddress + "\n" + - "Ethereum public key: " + System.Convert.ToBase64String(ethKeys.UncompressedPublicKey) + "\n" + - "Neo Legacy address: " + account.neoAddress + "\n" + - "Neo Legacy public key: " + System.Convert.ToBase64String(neoKeys.PublicKey) + "\n"; - - var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); - - ShowModal("Proof of addresses", message, + ShowModal("Proof of addresses", signer.GenerateMessage(), ModalState.Message, AccountManager.MinAccountNameLength, AccountManager.MaxAccountNameLength, ModalSignCancel, 1, (result, name) => { if (result == PromptResult.Success) { - var phaSignature = phantasmaKeys.Sign(messageBytes); - message += "\nPhantasma signature: " + System.Convert.ToBase64String(((Ed25519Signature)phaSignature).Bytes); - - { - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(messageBytes, ethKeys.PrivateKey, ethKeys.PublicKey, ECDsaCurve.Secp256k1); - var ethSignature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); - message += "\nEthereum signature: " + System.Convert.ToBase64String(signatureBytes); - } - { - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(messageBytes, neoKeys.PrivateKey, neoKeys.CompressedPublicKey, ECDsaCurve.Secp256r1); - var neoLegacySignature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256r1); - message += "\nNeo Legacy signature: " + System.Convert.ToBase64String(signatureBytes); - } - - ShowModal("Signed proof of addresses", message, ModalState.Message, 0, 0, ModalOkCopy, 0, (_, input) => { }); + ShowModal("Signed proof of addresses", signer.GenerateSignedMessage(), ModalState.Message, 0, 0, ModalOkCopy, 0, (_, input) => { }); } }); break; From facbe8e35e2db98dae6bf362386e7b129f9f31b5 Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Mon, 19 Aug 2024 19:20:51 -0300 Subject: [PATCH 06/42] ProofOfAddresses: Tweak message format --- Assets/Scripts/Wallet/ProofOfAddressesSigner.cs | 4 ++-- Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs index 3d48700..e9b528c 100644 --- a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs +++ b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs @@ -32,7 +32,7 @@ public string GenerateMessage() "Ethereum address: " + ethAddress + "\n" + "Ethereum public key: " + ethPubKey + "\n" + "Neo Legacy address: " + neo2Address + "\n" + - "Neo Legacy public key: " + neo2PubKey + "\n"; + "Neo Legacy public key: " + neo2PubKey; return message; } @@ -44,7 +44,7 @@ public string GenerateSignedMessage() var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); var phaSignature = PhantasmaKeys.Sign(messageBytes); - message += "\nPhantasma signature: " + System.Convert.ToBase64String(((Ed25519Signature)phaSignature).Bytes); + message += "\n\nPhantasma signature: " + System.Convert.ToBase64String(((Ed25519Signature)phaSignature).Bytes); { var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(messageBytes, EthKeys.PrivateKey, EthKeys.PublicKey, ECDsaCurve.Secp256k1); diff --git a/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs index 6da3047..ec4d152 100644 --- a/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs +++ b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs @@ -35,7 +35,7 @@ public ProofOfAddressesVerifier(string message) var split = Message.Replace("\r", string.Empty).Split('\n'); - SignedMessage = string.Join('\n', split.Take(7)); + SignedMessage = string.Join('\n', split.Take(6)); SignedMessageBytes = Encoding.ASCII.GetBytes(SignedMessage); PhaAddress = split[1].Substring(19); From 00f41a70fcd27a308b0b8517ce5e16e138f6bfce Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Mon, 19 Aug 2024 19:22:34 -0300 Subject: [PATCH 07/42] Bump version --- Assets/Scripts/Updater/UpdateChecker.cs | 2 +- ProjectSettings/ProjectSettings.asset | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Updater/UpdateChecker.cs b/Assets/Scripts/Updater/UpdateChecker.cs index 6739fb5..5ddd9b4 100644 --- a/Assets/Scripts/Updater/UpdateChecker.cs +++ b/Assets/Scripts/Updater/UpdateChecker.cs @@ -8,7 +8,7 @@ public class UpdateChecker : MonoBehaviour { public string githubOwner = "phantasma-io"; // Your GitHub username public string githubRepo = "PoltergeistLite"; // Your repository name - public string currentVersion = "1.1.1"; // Current version of your game + public string currentVersion = "1.2.0"; // Current version of your game private const string GITHUB_RELEASES_URL = "https://github.com/"; private static string URL = ""; diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 15f427f..9ac2a1c 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -139,7 +139,7 @@ PlayerSettings: loadStoreDebugModeEnabled: 0 visionOSBundleVersion: 1.0 tvOSBundleVersion: 1.0 - bundleVersion: 1.1.1 + bundleVersion: 1.2.0 preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 From 32a8e378c6cd0b35a546b04201c20cf7347d6184 Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Mon, 19 Aug 2024 22:55:36 -0300 Subject: [PATCH 08/42] Upgrade to Unity 2022.3.42f1 --- ProjectSettings/ProjectVersion.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt index 44ca791..094355e 100644 --- a/ProjectSettings/ProjectVersion.txt +++ b/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2022.3.41f1 -m_EditorVersionWithRevision: 2022.3.41f1 (0f988161febf) +m_EditorVersion: 2022.3.42f1 +m_EditorVersionWithRevision: 2022.3.42f1 (2dcb6a0abc42) From 611f9b72e345a529213adc08f8bc5245ec5363c7 Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Wed, 21 Aug 2024 10:58:32 -0300 Subject: [PATCH 09/42] More detailed exceptions for ExtractContractNames() --- .../src/VM/Utils/DisasmUtils.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Assets/Phantasma/Phantasma.Business/src/VM/Utils/DisasmUtils.cs b/Assets/Phantasma/Phantasma.Business/src/VM/Utils/DisasmUtils.cs index c2adf4c..69cdac2 100644 --- a/Assets/Phantasma/Phantasma.Business/src/VM/Utils/DisasmUtils.cs +++ b/Assets/Phantasma/Phantasma.Business/src/VM/Utils/DisasmUtils.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Phantasma.Core; using Phantasma.Core.Domain; namespace Phantasma.Business.VM.Utils @@ -133,7 +134,8 @@ public static IEnumerable ExtractContractNames(Disassembler disassembler var result = new List(); int index = 0; - var regs = new VMObject[16]; + var regsLength = 16; + var regs = new VMObject[regsLength]; while (index < instructions.Length) { var instruction = instructions[index]; @@ -142,10 +144,14 @@ public static IEnumerable ExtractContractNames(Disassembler disassembler { case Opcode.LOAD: { + Throw.If(instruction.Args.Length < 3, $"Opcode.LOAD: instruction.Args length {instruction.Args.Length} is less than {3}"); + var dst = (byte)instruction.Args[0]; var type = (VMType)instruction.Args[1]; var bytes = (byte[])instruction.Args[2]; + Throw.If(dst >= regsLength, $"Opcode.LOAD: Destination {dst} is out of range 0-{regsLength - 1}"); + regs[dst] = new VMObject(); regs[dst].SetValue(bytes, type); @@ -154,9 +160,14 @@ public static IEnumerable ExtractContractNames(Disassembler disassembler case Opcode.CTX: { + Throw.If(instruction.Args.Length < 2, $"Opcode.CTX: instruction.Args length {instruction.Args.Length} is less than {2}"); + var src = (byte)instruction.Args[0]; var dst = (byte)instruction.Args[1]; + Throw.If(src >= regsLength, $"Opcode.CTX: Source {src} is out of range 0-{regsLength - 1}"); + Throw.If(dst >= regsLength, $"Opcode.CTX: Destination {dst} is out of range 0-{regsLength - 1}"); + regs[dst] = new VMObject(); regs[dst].Copy(regs[src]); break; @@ -164,8 +175,12 @@ public static IEnumerable ExtractContractNames(Disassembler disassembler case Opcode.SWITCH: { + Throw.If(instruction.Args.Length < 1, $"Opcode.SWITCH: instruction.Args length {instruction.Args.Length} is less than {1}"); + var src = (byte)instruction.Args[0]; + Throw.If(src >= regsLength, $"Opcode.SWITCH: Source {src} is out of range 0-{regsLength - 1}"); + var contractName = regs[src].AsString(); result.Add(contractName); break; From 3eeb8ab77f860427e6e5a82fa2314d3ad53762f7 Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Wed, 21 Aug 2024 10:59:32 -0300 Subject: [PATCH 10/42] Dev mode: Log transaction script in wallet connector --- Assets/Scripts/Wallet/WalletLink/WalletConnector.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs b/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs index b1e20c8..3f61cbc 100644 --- a/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs +++ b/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs @@ -408,6 +408,16 @@ protected override void SignTransaction(string platform, SignatureKind kind, str { var accountManager = AccountManager.Instance; + if(accountManager.Settings.devMode) + { + Log.Write($"WalletConnector: SignTransaction(): Script description: Platform: {platform}\n"+ + $"SignatureKind: {kind}\n"+ + $"Chain: {chain}\n" + + $"Script: {Base16.Encode(script)}\n" + + $"Payload: '{(payload == null ? "" : Encoding.UTF8.GetString(payload))}'\n" + + $"ProofOfWork: {pow}"); + } + var targetPlatform = RequestPlatform(platform); if (targetPlatform == PlatformKind.None) { From 98869d9edd6322e7a0eb626bd6cc36fc6dae3b9e Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Wed, 28 Aug 2024 16:14:04 -0300 Subject: [PATCH 11/42] POA: Switch to deterministic ECDsa --- .../Poltergeist.Extensions/CryptoUtils.cs | 36 +++++++++++++++++++ .../Scripts/Wallet/ProofOfAddressesSigner.cs | 4 +-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index ecab9fd..0483d5c 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -1,7 +1,10 @@ using System; using System.Linq; using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Phantasma.Core.Cryptography; @@ -96,6 +99,39 @@ public static byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey, ECDsaCur } } + public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve curve = ECDsaCurve.Secp256r1) + { + var sha256 = new Sha256Digest(); + sha256.BlockUpdate(message, 0, message.Length); + var messageHash = new byte[sha256.GetDigestSize()]; + sha256.DoFinal(messageHash, 0); + + X9ECParameters ecParams; + switch (curve) + { + case ECDsaCurve.Secp256k1: + ecParams = ECNamedCurveTable.GetByName("secp256k1"); + break; + default: + ecParams = ECNamedCurveTable.GetByName("secp256r1"); + break; + } + + var dom = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed()); + + ECKeyParameters privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, prikey), dom); + + var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest())); + + signer.Init(true, privateKeyParameters); + + var RS = signer.GenerateSignature(messageHash); + var R = RS[0].ToByteArrayUnsigned(); + var S = RS[1].ToByteArrayUnsigned(); + + return R.Concat(S).ToArray(); + } + public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve phaCurve = ECDsaCurve.Secp256r1, SignatureFormat signatureFormat = SignatureFormat.Concatenated) { var signer = SignerUtilities.GetSigner("SHA256withECDSA"); diff --git a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs index e9b528c..34229ef 100644 --- a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs +++ b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs @@ -47,12 +47,12 @@ public string GenerateSignedMessage() message += "\n\nPhantasma signature: " + System.Convert.ToBase64String(((Ed25519Signature)phaSignature).Bytes); { - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(messageBytes, EthKeys.PrivateKey, EthKeys.PublicKey, ECDsaCurve.Secp256k1); + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, EthKeys.PrivateKey, ECDsaCurve.Secp256k1); message += "\nEthereum signature: " + System.Convert.ToBase64String(signatureBytes); } { - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(messageBytes, NeoKeys.PrivateKey, NeoKeys.CompressedPublicKey, ECDsaCurve.Secp256r1); + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, NeoKeys.PrivateKey, ECDsaCurve.Secp256r1); message += "\nNeo Legacy signature: " + System.Convert.ToBase64String(signatureBytes); } From 6054a5cbc34d23f7ab579a2986967bcf1f46728e Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Wed, 28 Aug 2024 16:41:48 -0300 Subject: [PATCH 12/42] Cryptography code small cleanup --- .../Poltergeist.Extensions/CryptoUtils.cs | 78 ++++++++----------- .../Wallet/WalletLink/WalletConnector.cs | 8 +- Assets/Tests/CryptoTests.cs | 4 +- 3 files changed, 39 insertions(+), 51 deletions(-) diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index 0483d5c..f8864a2 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -1,15 +1,12 @@ using System; using System.Linq; -using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; -using Phantasma.Core.Cryptography; using Phantasma.Core.Cryptography.ECDsa; -using Phantasma.Core.Numerics; namespace Poltergeist.PhantasmaLegacy.Cryptography { @@ -66,21 +63,38 @@ private static byte[] TranscodeSignatureToConcat(byte[] derSignature, int output return concatSignature; } - public static byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey, ECDsaCurve phaCurve = ECDsaCurve.Secp256r1, SignatureFormat signatureFormat = SignatureFormat.Concatenated) + private static X9ECParameters GetECParameters(ECDsaCurve curve) { - var signer = SignerUtilities.GetSigner("SHA256withECDSA"); - Org.BouncyCastle.Asn1.X9.X9ECParameters curve; - switch (phaCurve) + return curve switch { - case ECDsaCurve.Secp256k1: - curve = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); - break; - default: - curve = NistNamedCurves.GetByName("P-256"); - break; - } - var dom = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H); - ECKeyParameters privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, prikey), dom); + ECDsaCurve.Secp256k1 => ECNamedCurveTable.GetByName("secp256k1"), + ECDsaCurve.Secp256r1 => ECNamedCurveTable.GetByName("secp256r1"), + _ => ECNamedCurveTable.GetByName("secp256r1"), + }; + } + + private static ECDomainParameters GetECDomainParameters(ECDsaCurve curve) + { + var ecParams = GetECParameters(curve); + return new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H); + } + + private static ECPrivateKeyParameters GetECPrivateKeyParameters(ECDsaCurve curve, byte[] privateKey) + { + return new ECPrivateKeyParameters(new BigInteger(1, privateKey), GetECDomainParameters(curve)); + } + + private static ECPublicKeyParameters GetECPublicKeyParameters(ECDsaCurve curve, byte[] publicKey) + { + var ecDomainParameters = GetECDomainParameters(curve); + var point = ecDomainParameters.Curve.DecodePoint(publicKey); + return new ECPublicKeyParameters(point, ecDomainParameters); + } + + public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve = ECDsaCurve.Secp256r1, SignatureFormat signatureFormat = SignatureFormat.Concatenated) + { + var signer = SignerUtilities.GetSigner("SHA256withECDSA"); + var privateKeyParameters = GetECPrivateKeyParameters(curve, prikey); signer.Init(true, privateKeyParameters); signer.BlockUpdate(message, 0, message.Length); @@ -106,20 +120,7 @@ public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve var messageHash = new byte[sha256.GetDigestSize()]; sha256.DoFinal(messageHash, 0); - X9ECParameters ecParams; - switch (curve) - { - case ECDsaCurve.Secp256k1: - ecParams = ECNamedCurveTable.GetByName("secp256k1"); - break; - default: - ecParams = ECNamedCurveTable.GetByName("secp256r1"); - break; - } - - var dom = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed()); - - ECKeyParameters privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, prikey), dom); + var privateKeyParameters = GetECPrivateKeyParameters(curve, prikey); var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest())); @@ -132,23 +133,10 @@ public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve return R.Concat(S).ToArray(); } - public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve phaCurve = ECDsaCurve.Secp256r1, SignatureFormat signatureFormat = SignatureFormat.Concatenated) + public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve curve = ECDsaCurve.Secp256r1, SignatureFormat signatureFormat = SignatureFormat.Concatenated) { var signer = SignerUtilities.GetSigner("SHA256withECDSA"); - Org.BouncyCastle.Asn1.X9.X9ECParameters curve; - switch (phaCurve) - { - case ECDsaCurve.Secp256k1: - curve = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); - break; - default: - curve = NistNamedCurves.GetByName("P-256"); - break; - } - var dom = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H); - - var point = dom.Curve.DecodePoint(pubkey); - var publicKeyParameters = new ECPublicKeyParameters(point, dom); + var publicKeyParameters = GetECPublicKeyParameters(curve, pubkey); signer.Init(false, publicKeyParameters); signer.BlockUpdate(message, 0, message.Length); diff --git a/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs b/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs index 3f61cbc..17cf620 100644 --- a/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs +++ b/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs @@ -299,7 +299,7 @@ protected override void FetchAndMultiSignature(string subject, string platform, case SignatureKind.ECDSA: var ethKeys = EthereumKey.FromWIF(wif); - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, ethKeys.PrivateKey, ethKeys.PublicKey, ECDsaCurve.Secp256k1); + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, ethKeys.PrivateKey, ECDsaCurve.Secp256k1); signature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); break; @@ -372,7 +372,7 @@ protected override void SignTransactionSignature(Phantasma.Core.Domain.Transacti case SignatureKind.ECDSA: var ethKeys = EthereumKey.FromWIF(wif); - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, ethKeys.PrivateKey, ethKeys.PublicKey, ECDsaCurve.Secp256k1); + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, ethKeys.PrivateKey, ECDsaCurve.Secp256k1); signature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); break; @@ -531,13 +531,13 @@ protected override void SignData(string platform, SignatureKind kind, byte[] dat { var ethKeys = EthereumKey.FromWIF(wif); - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, ethKeys.PrivateKey, ethKeys.PublicKey, ECDsaCurve.Secp256k1); + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, ethKeys.PrivateKey, ECDsaCurve.Secp256k1); signature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); } else { var neoKeys = NeoKeys.FromWIF(wif); - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, neoKeys.PrivateKey, neoKeys.CompressedPublicKey, ECDsaCurve.Secp256k1); + var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, neoKeys.PrivateKey, ECDsaCurve.Secp256k1); signature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); } break; diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index f17ab54..4725bcc 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -36,7 +36,7 @@ public IEnumerator ECDsaSecP256k1() var msgBytes = Encoding.ASCII.GetBytes("Phantasma"); var signature = ethKeys.Sign(msgBytes, (message, prikey, pubkey) => { - return Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(message, prikey, pubkey, ECDsaCurve.Secp256k1); + return Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(message, prikey, ECDsaCurve.Secp256k1); }); var ecdsaSignature = (ECDsaSignature)signature; @@ -49,7 +49,7 @@ public IEnumerator ECDsaSecP256k1() var signatureDEREncoded = ethKeys.Sign(msgBytes, (message, prikey, pubkey) => { - return Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(message, prikey, pubkey, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded); + return Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(message, prikey, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded); }); var ecdsaSignatureDEREncoded = (ECDsaSignature)signatureDEREncoded; From 8a50794b10dc9df40b113ea99feeec74788be975 Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Wed, 28 Aug 2024 16:58:59 -0300 Subject: [PATCH 13/42] POA: Add message box that message was copied --- Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index 194bc95..30ef7fd 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -3388,7 +3388,14 @@ private void DoAccountManagementMenu(int btnOffset) { if (result == PromptResult.Success) { - ShowModal("Signed proof of addresses", signer.GenerateSignedMessage(), ModalState.Message, 0, 0, ModalOkCopy, 0, (_, input) => { }); + var message = signer.GenerateSignedMessage(); + ShowModal("Signed proof of addresses", message, ModalState.Message, 0, 0, ModalOkCopy_NoAutoCopy, 0, (result2, input) => { + if (result2 != PromptResult.Success) + { + GUIUtility.systemCopyBuffer = message; + MessageBox(MessageKind.Default, "Message copied to the clipboard."); + } + }); } }); break; From cf38734e534f25fd8c916b2f3244208ee5fc9d95 Mon Sep 17 00:00:00 2001 From: alexmpa <62516783+alexmpa@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:16:43 -0300 Subject: [PATCH 14/42] POA: Switch to base16 --- Assets/Scripts/Wallet/ProofOfAddressesSigner.cs | 11 ++++++----- Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs index 34229ef..6742311 100644 --- a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs +++ b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs @@ -3,6 +3,7 @@ using Poltergeist.PhantasmaLegacy.Ethereum; using Poltergeist.Neo2.Core; using Phantasma.Core.Cryptography.EdDSA; +using Phantasma.Core.Numerics; public class ProofOfAddressesSigner { @@ -21,10 +22,10 @@ public string GenerateMessage() { var phaAddress = PhantasmaKeys.Address.ToString(); var ethAddress = new Poltergeist.PhantasmaLegacy.Ethereum.Util.AddressUtil().ConvertToChecksumAddress(EthereumKey.PublicKeyToAddress(EthKeys.UncompressedPublicKey)); - var ethPubKey = System.Convert.ToBase64String(EthKeys.UncompressedPublicKey); + var ethPubKey = Base16.Encode(EthKeys.UncompressedPublicKey); var neo2Address = Poltergeist.Neo2.Core.NeoKeys.PublicKeyToN2Address(NeoKeys.PublicKey); - var neo2PubKey = System.Convert.ToBase64String(NeoKeys.PublicKey); + var neo2PubKey = Base16.Encode(NeoKeys.PublicKey); var message = "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + @@ -44,16 +45,16 @@ public string GenerateSignedMessage() var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); var phaSignature = PhantasmaKeys.Sign(messageBytes); - message += "\n\nPhantasma signature: " + System.Convert.ToBase64String(((Ed25519Signature)phaSignature).Bytes); + message += "\n\nPhantasma signature: " + Base16.Encode(((Ed25519Signature)phaSignature).Bytes); { var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, EthKeys.PrivateKey, ECDsaCurve.Secp256k1); - message += "\nEthereum signature: " + System.Convert.ToBase64String(signatureBytes); + message += "\nEthereum signature: " + Base16.Encode(signatureBytes); } { var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, NeoKeys.PrivateKey, ECDsaCurve.Secp256r1); - message += "\nNeo Legacy signature: " + System.Convert.ToBase64String(signatureBytes); + message += "\nNeo Legacy signature: " + Base16.Encode(signatureBytes); } return message; diff --git a/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs index ec4d152..e91b4f7 100644 --- a/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs +++ b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs @@ -4,6 +4,7 @@ using Poltergeist.PhantasmaLegacy.Ethereum; using Phantasma.Core.Cryptography.EdDSA; using System.Text; +using Phantasma.Core.Numerics; public class ProofOfAddressesVerifier { @@ -42,17 +43,17 @@ public ProofOfAddressesVerifier(string message) PhaPublicKeyBytes = Address.FromText(PhaAddress).GetPublicKey(); EthAddress = split[2].Substring(18); EthPublicKey = split[3].Substring(21); - EthPublicKeyBytes = System.Convert.FromBase64String(EthPublicKey); + EthPublicKeyBytes = Base16.Decode(EthPublicKey); Neo2Address = split[4].Substring(20); Neo2PublicKey = split[5].Substring(23); - Neo2PublicKeyBytes = System.Convert.FromBase64String(Neo2PublicKey); + Neo2PublicKeyBytes = Base16.Decode(Neo2PublicKey); PhaSignature = split[7].Substring(21); - PhaSignatureBytes = System.Convert.FromBase64String(PhaSignature); + PhaSignatureBytes = Base16.Decode(PhaSignature); EthSignature = split[8].Substring(20); - EthSignatureBytes = System.Convert.FromBase64String(EthSignature); + EthSignatureBytes = Base16.Decode(EthSignature); Neo2Signature = split[9].Substring(22); - Neo2SignatureBytes = System.Convert.FromBase64String(Neo2Signature); + Neo2SignatureBytes = Base16.Decode(Neo2Signature); } public (bool, string) VerifyMessage() From 79711499a7cf122e63f8f8c5cb82f233a0d1bb16 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Sun, 15 Sep 2024 23:26:56 -0300 Subject: [PATCH 15/42] POA: Switch to compressed public keys --- .../src/Cryptography/ECDsa/ECDsa.cs | 29 +++++++++++++++++++ .../Ethereum/KeyPair.cs | 12 ++++++-- .../Scripts/Wallet/ProofOfAddressesSigner.cs | 6 ++-- .../Wallet/ProofOfAddressesVerifier.cs | 2 +- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs index 409b303..00e0404 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs @@ -55,6 +55,35 @@ public static byte[] CompressPublicKey(byte[] uncompressedPublicKey) return new byte[] { prefix }.Concat(x.ToByteArrayUnsigned()).ToArray(); } + public static byte[] DecompressPublicKey(byte[] compressedPublicKey, ECDsaCurve curve, bool dropUncompressedKeyPrefixByte = false) + { + if (compressedPublicKey.Length != 33) + { + throw new ArgumentException("Incorrect compressed key length: " + compressedPublicKey.Length); + } + + X9ECParameters ecCurve; + switch (curve) + { + case ECDsaCurve.Secp256k1: + ecCurve = SecNamedCurves.GetByName("secp256k1"); + break; + default: + ecCurve = SecNamedCurves.GetByName("secp256r1"); + break; + } + var dom = new ECDomainParameters(ecCurve.Curve, ecCurve.G, ecCurve.N, ecCurve.H); + + ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(dom.Curve.DecodePoint(compressedPublicKey), dom); + var uncompressedPublicKey = publicKeyParameters.Q.GetEncoded(false); + + if (dropUncompressedKeyPrefixByte) + { + uncompressedPublicKey = uncompressedPublicKey.Skip(1).ToArray(); + } + + return uncompressedPublicKey; + } public static ECDomainParameters GetDomain(ECDsaCurve curve) { X9ECParameters ecCurve; diff --git a/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs b/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs index dc1ca28..d755903 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs @@ -15,6 +15,7 @@ public class EthereumKey : IKeyPair public byte[] PublicKey { get; private set; } public readonly string Address; public readonly byte[] UncompressedPublicKey; + public readonly byte[] CompressedPublicKey; public EthereumKey(byte[] privateKey) { @@ -25,8 +26,9 @@ public EthereumKey(byte[] privateKey) this.PublicKey = ECDsa.GetPublicKey(privateKey, true, ECDsaCurve.Secp256k1); this.UncompressedPublicKey = ECDsa.GetPublicKey(privateKey, false, ECDsaCurve.Secp256k1).Skip(1).ToArray(); + this.CompressedPublicKey = ECDsa.GetPublicKey(privateKey, true, ECDsaCurve.Secp256k1).ToArray(); - this.Address = PublicKeyToAddress(this.UncompressedPublicKey); + this.Address = PublicKeyToAddress(this.UncompressedPublicKey, ECDsaCurve.Secp256k1); } public static EthereumKey FromPrivateKey(string prv) @@ -76,9 +78,13 @@ private static byte[] XOR(byte[] x, byte[] y) return x.Zip(y, (a, b) => (byte)(a ^ b)).ToArray(); } - public static string PublicKeyToAddress(byte[] uncompressedPublicKey) + public static string PublicKeyToAddress(byte[] publicKey, ECDsaCurve curve) { - var kak = new Sha3Keccack().CalculateHash(uncompressedPublicKey); + if (publicKey.Length == 33) + { + publicKey = ECDsa.DecompressPublicKey(publicKey, curve, true); + } + var kak = new Sha3Keccack().CalculateHash(publicKey); return "0x" + Base16.Encode(kak.Skip(12).ToArray()); } diff --git a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs index 6742311..6063208 100644 --- a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs +++ b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs @@ -21,11 +21,11 @@ public ProofOfAddressesSigner(string wif) public string GenerateMessage() { var phaAddress = PhantasmaKeys.Address.ToString(); - var ethAddress = new Poltergeist.PhantasmaLegacy.Ethereum.Util.AddressUtil().ConvertToChecksumAddress(EthereumKey.PublicKeyToAddress(EthKeys.UncompressedPublicKey)); - var ethPubKey = Base16.Encode(EthKeys.UncompressedPublicKey); + var ethAddress = new Poltergeist.PhantasmaLegacy.Ethereum.Util.AddressUtil().ConvertToChecksumAddress(EthereumKey.PublicKeyToAddress(EthKeys.UncompressedPublicKey, ECDsaCurve.Secp256k1)); + var ethPubKey = Base16.Encode(EthKeys.CompressedPublicKey); var neo2Address = Poltergeist.Neo2.Core.NeoKeys.PublicKeyToN2Address(NeoKeys.PublicKey); - var neo2PubKey = Base16.Encode(NeoKeys.PublicKey); + var neo2PubKey = Base16.Encode(NeoKeys.CompressedPublicKey); var message = "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + diff --git a/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs index e91b4f7..f6d3927 100644 --- a/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs +++ b/Assets/Scripts/Wallet/ProofOfAddressesVerifier.cs @@ -77,7 +77,7 @@ public ProofOfAddressesVerifier(string message) errorMessage += "Neo Legacy signature is incorrect!\n"; } - var ethAddressFromPublicKey = new Poltergeist.PhantasmaLegacy.Ethereum.Util.AddressUtil().ConvertToChecksumAddress(EthereumKey.PublicKeyToAddress(EthPublicKeyBytes)); + var ethAddressFromPublicKey = new Poltergeist.PhantasmaLegacy.Ethereum.Util.AddressUtil().ConvertToChecksumAddress(EthereumKey.PublicKeyToAddress(EthPublicKeyBytes, ECDsaCurve.Secp256k1)); if (EthAddress != ethAddressFromPublicKey) { success = false; From feffeb8b51b7fcf9d4d59cf2c4925bd790da10e5 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Sun, 15 Sep 2024 23:27:18 -0300 Subject: [PATCH 16/42] Upgrade to Unity 2022.3.46f1 --- ProjectSettings/ProjectVersion.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt index 094355e..1e3d799 100644 --- a/ProjectSettings/ProjectVersion.txt +++ b/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2022.3.42f1 -m_EditorVersionWithRevision: 2022.3.42f1 (2dcb6a0abc42) +m_EditorVersion: 2022.3.46f1 +m_EditorVersionWithRevision: 2022.3.46f1 (8e9b8558c41a) From 794101a6364969bd13ee105ce965cb0dd590c0d3 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Mon, 16 Sep 2024 20:25:09 -0300 Subject: [PATCH 17/42] Add "Sign message" dialog --- .../Wallet/WalletGUIFolder/WalletGUI.cs | 100 +++++++++++++++--- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index 30ef7fd..e5895ca 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -21,6 +21,9 @@ using Phantasma.Business.Blockchain; using Phantasma.Core.Types; using NBitcoin; +using Phantasma.Core.Cryptography.EdDSA; +using Poltergeist.Neo2.Core; +using Phantasma.Core.Cryptography.ECDsa; namespace Poltergeist { @@ -3157,7 +3160,14 @@ private void DoAccountManagementMenu(int btnOffset) var accountManager = AccountManager.Instance; int posY; - DoButtonGrid(false, managerMenu.Length, 0, -btnOffset, out posY, (index) => + var menu = managerMenu; + if (Input.GetKey(KeyCode.LeftShift)) + { + menu = (string[])managerMenu.Clone(); + menu[3] = "Sign message"; + } + + DoButtonGrid(false, menu.Length, 0, -btnOffset, out posY, (index) => { var enabled = true; @@ -3179,7 +3189,7 @@ private void DoAccountManagementMenu(int btnOffset) enabled = false; } - return new MenuEntry(index, managerMenu[index], enabled); + return new MenuEntry(index, menu[index], enabled); }, (selected) => { @@ -3381,23 +3391,85 @@ private void DoAccountManagementMenu(int btnOffset) } case 3: { - var signer = new ProofOfAddressesSigner(AccountManager.Instance.CurrentAccount.GetWif(AccountManager.Instance.CurrentPasswordHash)); - - ShowModal("Proof of addresses", signer.GenerateMessage(), - ModalState.Message, AccountManager.MinAccountNameLength, AccountManager.MaxAccountNameLength, ModalSignCancel, 1, (result, name) => + if (Input.GetKey(KeyCode.LeftShift)) { - if (result == PromptResult.Success) + ShowModal("", "Select chain", ModalState.Input, 1, 10, ModalConfirmCancel, 1, (result, chain) => { - var message = signer.GenerateSignedMessage(); - ShowModal("Signed proof of addresses", message, ModalState.Message, 0, 0, ModalOkCopy_NoAutoCopy, 0, (result2, input) => { - if (result2 != PromptResult.Success) + if (result == PromptResult.Failure) + { + return; // user cancelled + } + + ShowModal("", "Enter message", ModalState.Input, 1, -1, ModalConfirmCancel, 4, (result2, message) => + { + if (result2 == PromptResult.Failure) + { + return; // user cancelled + } + + var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); + + var wif = AccountManager.Instance.CurrentAccount.GetWif(AccountManager.Instance.CurrentPasswordHash); + byte[] signatureBytes; + + if (chain == "Phantasma") { - GUIUtility.systemCopyBuffer = message; - MessageBox(MessageKind.Default, "Message copied to the clipboard."); + var keys = PhantasmaKeys.FromWIF(wif); + var phaSignature = keys.Sign(messageBytes); + signatureBytes = ((Ed25519Signature)phaSignature).Bytes; } + else if (chain == "Ethereum") + { + var keys = EthereumKey.FromWIF(wif); + signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, keys.PrivateKey, ECDsaCurve.Secp256k1); + } + else if (chain == "Neo Legacy") + { + var keys = NeoKeys.FromWIF(wif); + signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, keys.PrivateKey, ECDsaCurve.Secp256r1); + } + else + { + MessageBox(MessageKind.Error, "Unsupported chain"); + return; + } + + var signature = Base16.Encode(signatureBytes); + + ShowModal("Signature", signature, ModalState.Message, 0, 0, ModalOkCopy_NoAutoCopy, 0, (result3, input) => + { + if (result3 != PromptResult.Success) + { + GUIUtility.systemCopyBuffer = signature; + MessageBox(MessageKind.Default, "Signature copied to the clipboard."); + } + }); }); - } - }); + }); + + modalHints = new Dictionary() { { "Phantasma", "Phantasma" }, { "Ethereum", "Ethereum" }, { "Neo Legacy", "Neo Legacy" } }; + } + else + { + var signer = new ProofOfAddressesSigner(AccountManager.Instance.CurrentAccount.GetWif(AccountManager.Instance.CurrentPasswordHash)); + + ShowModal("Proof of addresses", signer.GenerateMessage(), + ModalState.Message, AccountManager.MinAccountNameLength, AccountManager.MaxAccountNameLength, ModalSignCancel, 1, (result, name) => + { + if (result == PromptResult.Success) + { + var message = signer.GenerateSignedMessage(); + ShowModal("Signed proof of addresses", message, ModalState.Message, 0, 0, ModalOkCopy_NoAutoCopy, 0, (result2, input) => + { + if (result2 != PromptResult.Success) + { + GUIUtility.systemCopyBuffer = message; + MessageBox(MessageKind.Default, "Message copied to the clipboard."); + } + }); + } + }); + } break; } } From 14f0e2845c44ba682a9cb489db463790e3e2d55b Mon Sep 17 00:00:00 2001 From: alexmpa Date: Tue, 17 Sep 2024 20:14:58 -0300 Subject: [PATCH 18/42] Add "Verify signature" dialog --- .../Wallet/WalletGUIFolder/WalletGUI.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index e5895ca..301876e 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -3166,6 +3166,11 @@ private void DoAccountManagementMenu(int btnOffset) menu = (string[])managerMenu.Clone(); menu[3] = "Sign message"; } + else if (Input.GetKey(KeyCode.LeftControl)) + { + menu = (string[])managerMenu.Clone(); + menu[3] = "Verify signature"; + } DoButtonGrid(false, menu.Length, 0, -btnOffset, out posY, (index) => { @@ -3449,6 +3454,70 @@ private void DoAccountManagementMenu(int btnOffset) modalHints = new Dictionary() { { "Phantasma", "Phantasma" }, { "Ethereum", "Ethereum" }, { "Neo Legacy", "Neo Legacy" } }; } + else if (Input.GetKey(KeyCode.LeftControl)) + { + ShowModal("", "Select chain", ModalState.Input, 1, 10, ModalConfirmCancel, 1, (result, chain) => + { + if (result == PromptResult.Failure) + { + return; // user cancelled + } + + ShowModal("", "Enter message", ModalState.Input, 1, -1, ModalConfirmCancel, 4, (result2, message) => + { + if (result2 == PromptResult.Failure) + { + return; // user cancelled + } + + ShowModal("", "Enter signature", ModalState.Input, 1, -1, ModalConfirmCancel, 4, (result3, signature) => + { + if (result3 == PromptResult.Failure) + { + return; // user cancelled + } + + var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); + var signatureBytes = Base16.Decode(signature); + + var wif = AccountManager.Instance.CurrentAccount.GetWif(AccountManager.Instance.CurrentPasswordHash); + var verificationResult = false; + + if (chain == "Phantasma") + { + var keys = PhantasmaKeys.FromWIF(wif); + verificationResult = Ed25519.Verify(signatureBytes, messageBytes, keys.PublicKey); + } + else if (chain == "Ethereum") + { + var keys = EthereumKey.FromWIF(wif); + verificationResult = ECDsa.Verify(signatureBytes, messageBytes, keys.PublicKey, ECDsaCurve.Secp256k1); + } + else if (chain == "Neo Legacy") + { + var keys = NeoKeys.FromWIF(wif); + verificationResult = ECDsa.Verify(signatureBytes, messageBytes, keys.PublicKey, ECDsaCurve.Secp256r1); + } + else + { + MessageBox(MessageKind.Error, "Unsupported chain"); + return; + } + + if (verificationResult) + { + MessageBox(MessageKind.Success, "Signature is correct"); + } + else + { + MessageBox(MessageKind.Error, "Signature is incorrect"); + } + }); + }); + }); + + modalHints = new Dictionary() { { "Phantasma", "Phantasma" }, { "Ethereum", "Ethereum" }, { "Neo Legacy", "Neo Legacy" } }; + } else { var signer = new ProofOfAddressesSigner(AccountManager.Instance.CurrentAccount.GetWif(AccountManager.Instance.CurrentPasswordHash)); From 05e7b2ab5055cfb581bbe08cf5b18844a56bba15 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Tue, 17 Sep 2024 22:23:37 -0300 Subject: [PATCH 19/42] Small code refactoring --- .../Poltergeist.Extensions/CryptoUtils.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index f8864a2..789c5c7 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -20,7 +20,7 @@ public enum SignatureFormat } // Transcodes the JCA ASN.1/DER-encoded signature into the concatenated R + S format expected by ECDSA JWS. - private static byte[] TranscodeSignatureToConcat(byte[] derSignature, int outputLength) + public static byte[] TranscodeSignatureToConcat(byte[] derSignature, int outputLength) { if (derSignature.Length < 8 || derSignature[0] != 48) throw new Exception("Invalid ECDSA signature format"); @@ -63,6 +63,16 @@ private static byte[] TranscodeSignatureToConcat(byte[] derSignature, int output return concatSignature; } + public static byte[] RSBytesToDER(byte[] RSBytes) + { + return new Org.BouncyCastle.Asn1.DerSequence( + // first 32 bytes is "r" number + new Org.BouncyCastle.Asn1.DerInteger(new BigInteger(1, RSBytes.Take(32).ToArray())), + // last 32 bytes is "s" number + new Org.BouncyCastle.Asn1.DerInteger(new BigInteger(1, RSBytes.Skip(32).ToArray()))) + .GetDerEncoded(); + } + private static X9ECParameters GetECParameters(ECDsaCurve curve) { return curve switch @@ -145,12 +155,7 @@ public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsa { case SignatureFormat.Concatenated: // We convert from concatenated "raw" R + S format to DER format that Bouncy Castle uses. - signature = new Org.BouncyCastle.Asn1.DerSequence( - // first 32 bytes is "r" number - new Org.BouncyCastle.Asn1.DerInteger(new BigInteger(1, signature.Take(32).ToArray())), - // last 32 bytes is "s" number - new Org.BouncyCastle.Asn1.DerInteger(new BigInteger(1, signature.Skip(32).ToArray()))) - .GetDerEncoded(); + signature = RSBytesToDER(signature); break; case SignatureFormat.DEREncoded: // Do nothing, signature already DER-encoded. From d3582347314ab09dba0068e0a0efc053d4c56fb0 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Tue, 17 Sep 2024 22:24:27 -0300 Subject: [PATCH 20/42] Add more unit tests --- Assets/Tests/CryptoTests.cs | 108 ++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 4725bcc..9310e0b 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -78,6 +78,114 @@ public IEnumerator ECDsaSecP256k1() yield return null; } + + [UnityTest] + public IEnumerator ECDsaSecP256k1_Deterministic() + { + // Eth address: "0x66571c32d77c4852be4c282eb952ba94efbeac20"; + var key = "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1"; + Assert.IsTrue(key.Length == 64); + + var privBytes = Base16.Decode(key); + var phantasmaKeys = new PhantasmaKeys(privBytes); + + var wif = phantasmaKeys.ToWIF(); + var ethKeys = EthereumKey.FromWIF(wif); + Debug.Log("Eth address: " + ethKeys); + + var ethPublicKeyCompressed = ECDsa.GetPublicKey(privBytes, true, ECDsaCurve.Secp256k1); + Debug.Log("Eth compressed public key: " + Base16.Encode(ethPublicKeyCompressed)); + var ethPublicKeyUncompressed = ECDsa.GetPublicKey(privBytes, false, ECDsaCurve.Secp256k1).Skip(1).ToArray(); + Debug.Log("Eth uncompressed public key: " + Base16.Encode(ethPublicKeyUncompressed)); + + var msgBytes = Encoding.ASCII.GetBytes("Phantasma"); + var signature = ethKeys.Sign(msgBytes, (message, prikey, pubkey) => + { + return Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(message, prikey, ECDsaCurve.Secp256k1); + }); + + var ecdsaSignature = (ECDsaSignature)signature; + var signatureSerialized = signature.Serialize(); // signature.ToByteArray() gives same result + + Debug.Log("\nSignature (RAW concatenated r & s, hex):\n" + Base16.Encode(ecdsaSignature.Bytes)); + // Curve byte: ECDsaCurve enum: Secp256r1 = 0, Secp256k1 = 1. + // Following is the format we use for signature: + Debug.Log("\nSignature (curve byte + signature length + concatenated r & s, hex):\n" + Base16.Encode(signatureSerialized)); + + var signatureDEREncoded = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.RSBytesToDER(ecdsaSignature.Bytes); + + Debug.Log("\nSignature (RAW DER-encoded, hex):\n" + Base16.Encode(signatureDEREncoded)); + Debug.Log("\nSignature (curve byte + signature length + DER-encoded, hex):\n" + Base16.Encode(signatureDEREncoded.Serialize())); + + // Since ECDsaSignature class not working for us, + // we use signature .Bytes directly to verify it with Bouncy Castle. + // Verifying concatenated signature / compressed Eth public key. + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, ecdsaSignature.Bytes, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + + // Verifying DER signature. + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureDEREncoded, ethPublicKeyCompressed, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded)); + + yield return null; + } + + private void ECDsaTest(string pkHex, string message, string signatureReference = null) + { + Assert.IsTrue(pkHex.Length == 64); + + var privBytes = Base16.Decode(pkHex); + var phantasmaKeys = new PhantasmaKeys(privBytes); + + var wif = phantasmaKeys.ToWIF(); + var ethKeys = EthereumKey.FromWIF(wif); + Debug.Log("Eth address: " + ethKeys); + + var ethPublicKeyCompressed = ECDsa.GetPublicKey(privBytes, true, ECDsaCurve.Secp256k1); + Debug.Log("Eth compressed public key: " + Base16.Encode(ethPublicKeyCompressed)); + + var msgBytes = Encoding.ASCII.GetBytes(message); + var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, privBytes, ECDsaCurve.Secp256k1); + var signatureHex = Base16.Encode(signature); + + if (signatureReference != null) + { + Assert.AreEqual(signatureHex, signatureReference); + } + + // Curve byte: ECDsaCurve enum: Secp256r1 = 0, Secp256k1 = 1. + // Following is the format we use for signature: + Debug.Log("\nSignature (concatenated r & s, hex):\n" + signatureHex); + + var signatureDER = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.RSBytesToDER(signature); + + Debug.Log("\nSignature (RAW DER-encoded, hex):\n" + Base16.Encode(signatureDER)); + + // Verifying concatenated signature / compressed Eth public key. + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + + // Verifying DER signature. + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded)); + + var signatureConvertedBack = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.TranscodeSignatureToConcat(signatureDER, 64); + var signatureConvertedBackHex = Base16.Encode(signatureConvertedBack); + Debug.Log("\nSignature (converted back from DER):\n" + signatureConvertedBackHex); + Assert.AreEqual(signatureHex, signatureConvertedBackHex); + + // Verifying signature, converted back from DER. + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureConvertedBack, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + } + + [UnityTest] + public IEnumerator ECDsaSecP256k1_DeterministicRaw() + { + // Eth address: 0x66571c32d77c4852be4c282eb952ba94efbeac20 + ECDsaTest("6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "Phantasma"); + ECDsaTest("6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "test message"); + // Eth address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a + ECDsaTest("4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "Phantasma"); + ECDsaTest("4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "test message", "55DEB9E4D985834192AB8298C3DDA18EB7082C2A744EBDF7233D0A93FB00A4A9F4750F4A6F3FB3928C28690BE3A2BE52DEB95E1935E960FACBF7CC4AC4FDADCB"); + + yield return null; + } } } From 4c2fdd0c9be116886957e24751136c7be1542f93 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Wed, 18 Sep 2024 12:22:20 -0300 Subject: [PATCH 21/42] Small refactoring and more logging --- .../Poltergeist.Extensions/CryptoUtils.cs | 11 +++++++---- .../Scripts/Wallet/WalletGUIFolder/WalletGUI.cs | 17 +++++++++++++++++ Assets/Tests/CryptoTests.cs | 4 ++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index 789c5c7..572588f 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -125,10 +125,7 @@ public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve = ECDs public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve curve = ECDsaCurve.Secp256r1) { - var sha256 = new Sha256Digest(); - sha256.BlockUpdate(message, 0, message.Length); - var messageHash = new byte[sha256.GetDigestSize()]; - sha256.DoFinal(messageHash, 0); + var messageHash = Sha256Hash(message); var privateKeyParameters = GetECPrivateKeyParameters(curve, prikey); @@ -166,5 +163,11 @@ public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsa return signer.VerifySignature(signature); } + + public static byte[] Sha256Hash(byte[] message) + { + var sha256 = new Phantasma.Core.Cryptography.Hashing.SHA256(); + return sha256.ComputeHash(message); + } } } diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index 301876e..249c6d4 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -3414,6 +3414,12 @@ private void DoAccountManagementMenu(int btnOffset) var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); + var hash = Base16.Encode(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(messageBytes)); + if (accountManager.Settings.devMode) + { + Log.Write($"Signed message: '{message}', hash: '{hash}'"); + } + var wif = AccountManager.Instance.CurrentAccount.GetWif(AccountManager.Instance.CurrentPasswordHash); byte[] signatureBytes; @@ -3445,6 +3451,11 @@ private void DoAccountManagementMenu(int btnOffset) { if (result3 != PromptResult.Success) { + if (accountManager.Settings.devMode) + { + Log.Write($"Signature: '{signature}'"); + } + GUIUtility.systemCopyBuffer = signature; MessageBox(MessageKind.Default, "Signature copied to the clipboard."); } @@ -3480,6 +3491,12 @@ private void DoAccountManagementMenu(int btnOffset) var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); var signatureBytes = Base16.Decode(signature); + var hash = Base16.Encode(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(messageBytes)); + if (accountManager.Settings.devMode) + { + Log.Write($"Verified message: '{message}', hash: '{hash}', signature: '{signature}'"); + } + var wif = AccountManager.Instance.CurrentAccount.GetWif(AccountManager.Instance.CurrentPasswordHash); var verificationResult = false; diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 9310e0b..4ef0e2f 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -143,6 +143,10 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = Debug.Log("Eth compressed public key: " + Base16.Encode(ethPublicKeyCompressed)); var msgBytes = Encoding.ASCII.GetBytes(message); + + var hash = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(msgBytes); + Debug.Log("Message hash: " + Base16.Encode(hash)); + var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, privBytes, ECDsaCurve.Secp256k1); var signatureHex = Base16.Encode(signature); From c9d094a9033e1a719ffb2c86c9102b7be7b13500 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Wed, 18 Sep 2024 17:48:28 -0300 Subject: [PATCH 22/42] Add missing support for compressed keys, more tests, some cleanup --- .../Poltergeist.Extensions/CryptoUtils.cs | 19 +++++--- .../Ethereum/KeyPair.cs | 8 +++- Assets/Tests/CryptoTests.cs | 46 +++++++++++++++++++ Assets/Tests/Tests.asmdef | 2 +- 4 files changed, 65 insertions(+), 10 deletions(-) diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index 572588f..82cd014 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -79,7 +79,7 @@ private static X9ECParameters GetECParameters(ECDsaCurve curve) { ECDsaCurve.Secp256k1 => ECNamedCurveTable.GetByName("secp256k1"), ECDsaCurve.Secp256r1 => ECNamedCurveTable.GetByName("secp256r1"), - _ => ECNamedCurveTable.GetByName("secp256r1"), + _ => throw new Exception("Unsupported curve"), }; } @@ -97,11 +97,16 @@ private static ECPrivateKeyParameters GetECPrivateKeyParameters(ECDsaCurve curve private static ECPublicKeyParameters GetECPublicKeyParameters(ECDsaCurve curve, byte[] publicKey) { var ecDomainParameters = GetECDomainParameters(curve); - var point = ecDomainParameters.Curve.DecodePoint(publicKey); - return new ECPublicKeyParameters(point, ecDomainParameters); - } - public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve = ECDsaCurve.Secp256r1, SignatureFormat signatureFormat = SignatureFormat.Concatenated) + ECPublicKeyParameters publicKeyParameters; + if (publicKey.Length == 33) + publicKeyParameters = new ECPublicKeyParameters(ecDomainParameters.Curve.DecodePoint(publicKey), ecDomainParameters); + else + publicKeyParameters = new ECPublicKeyParameters(ecDomainParameters.Curve.CreatePoint(new BigInteger(1, publicKey.Take(publicKey.Length / 2).ToArray()), new BigInteger(1, publicKey.Skip(publicKey.Length / 2).ToArray())), ecDomainParameters); + + return publicKeyParameters; + } + public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve, SignatureFormat signatureFormat = SignatureFormat.Concatenated) { var signer = SignerUtilities.GetSigner("SHA256withECDSA"); var privateKeyParameters = GetECPrivateKeyParameters(curve, prikey); @@ -123,7 +128,7 @@ public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve = ECDs } } - public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve curve = ECDsaCurve.Secp256r1) + public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve curve) { var messageHash = Sha256Hash(message); @@ -140,7 +145,7 @@ public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve return R.Concat(S).ToArray(); } - public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve curve = ECDsaCurve.Secp256r1, SignatureFormat signatureFormat = SignatureFormat.Concatenated) + public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve curve, SignatureFormat signatureFormat = SignatureFormat.Concatenated) { var signer = SignerUtilities.GetSigner("SHA256withECDSA"); var publicKeyParameters = GetECPublicKeyParameters(curve, pubkey); diff --git a/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs b/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs index d755903..0e97698 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs @@ -37,7 +37,7 @@ public static EthereumKey FromPrivateKey(string prv) return new EthereumKey(prv.HexToByteArray()); } - public static EthereumKey FromWIF(string wif) + public static byte[] FromWIFToBytes(string wif) { if (wif == null) throw new ArgumentNullException(); byte[] data = wif.Base58CheckDecode(); @@ -46,7 +46,11 @@ public static EthereumKey FromWIF(string wif) byte[] privateKey = new byte[32]; Buffer.BlockCopy(data, 1, privateKey, 0, privateKey.Length); Array.Clear(data, 0, data.Length); - return new EthereumKey(privateKey); + return privateKey; + } + public static EthereumKey FromWIF(string wif) + { + return new EthereumKey(FromWIFToBytes(wif)); } private static System.Security.Cryptography.RNGCryptoServiceProvider rnd = new System.Security.Cryptography.RNGCryptoServiceProvider(); diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 4ef0e2f..9000799 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -140,6 +140,7 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = Debug.Log("Eth address: " + ethKeys); var ethPublicKeyCompressed = ECDsa.GetPublicKey(privBytes, true, ECDsaCurve.Secp256k1); + var ethPublicKeyUncompressed = ECDsa.GetPublicKey(privBytes, false, ECDsaCurve.Secp256k1).Skip(1).ToArray(); Debug.Log("Eth compressed public key: " + Base16.Encode(ethPublicKeyCompressed)); var msgBytes = Encoding.ASCII.GetBytes(message); @@ -166,6 +167,9 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = // Verifying concatenated signature / compressed Eth public key. Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + // Verifying concatenated signature / uncompressed Eth public key. + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); + // Verifying DER signature. Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded)); @@ -178,6 +182,39 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureConvertedBack, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); } + + [UnityTest] + public IEnumerator WifPkTest() + { + var wif = "Kyry8sMHFzx5DfubcqyGMQByaHQBtdyALBjAqcx9Lx1YtSZjy2vZ"; + var pkHex = "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49"; + + var keyFromWif = EthereumKey.FromWIF(wif); + + Assert.AreEqual(keyFromWif.PublicKey, keyFromWif.CompressedPublicKey); + + var pkBytesFromHex = Base16.Decode(pkHex); + Assert.AreEqual(pkBytesFromHex.Length, 32); + + var keyFromHex = new EthereumKey(pkBytesFromHex); + + var pkBytesFromWif = EthereumKey.FromWIFToBytes(wif); + Assert.AreEqual(pkBytesFromWif, pkBytesFromHex); + Assert.AreEqual(pkBytesFromWif, keyFromWif.PrivateKey); + Assert.AreEqual(pkBytesFromWif, keyFromHex.PrivateKey); + Assert.AreEqual(pkBytesFromWif.Length, 32); + + Assert.AreEqual(keyFromWif.PrivateKey.Length, 32); + Assert.AreEqual(keyFromHex.PrivateKey.Length, 32); + Assert.AreEqual(keyFromWif.PublicKey.Length, 33); + Assert.AreEqual(keyFromHex.PublicKey.Length, 33); + + Assert.AreEqual(keyFromWif.PublicKey, keyFromHex.PublicKey); + Assert.AreEqual(keyFromWif.PrivateKey, keyFromHex.PrivateKey); + + yield return null; + } + [UnityTest] public IEnumerator ECDsaSecP256k1_DeterministicRaw() { @@ -188,6 +225,15 @@ public IEnumerator ECDsaSecP256k1_DeterministicRaw() ECDsaTest("4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "Phantasma"); ECDsaTest("4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "test message", "55DEB9E4D985834192AB8298C3DDA18EB7082C2A744EBDF7233D0A93FB00A4A9F4750F4A6F3FB3928C28690BE3A2BE52DEB95E1935E960FACBF7CC4AC4FDADCB"); + + ECDsaTest("4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + +"Phantasma address: P2KHhbVZWDv1ZLLoJccN3PUAb9x9BqRnUyH3ZEhu5YwBeJQ\n" + +"Ethereum address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a\n" + +"Ethereum public key: 5D3F7F469803C68C12B8F731576C74A9B5308484FD3B425D87C35CAED0A2E398C7AC626D916A1D65E23F673A55E6B16FFC1ABD673F3EF6AE8D5E6A0F99784A56\n" + +"Neo Legacy address: Ae3aEA6CpvckvypAUShj2CLsy7sfynKUzj\n" + +"Neo Legacy public key: 183A301779007BF42DD7B5247587585B0524E13989F964C2A8E289A0CDC91F001765FCC3B4CEE5ED274C4A8B6D80978BDFED678210458CE264D4A4DAB3923EE6", +"E3E1FCD85385675F9E3508630570C545DECCD1241C7A8FFF523D2AC500D6F68745E43975DBF871C99504100B8DD6715F036FA51EFF9EB8B79D1E31FD555E78FC"); + yield return null; } } diff --git a/Assets/Tests/Tests.asmdef b/Assets/Tests/Tests.asmdef index 6674757..ebdd9c7 100644 --- a/Assets/Tests/Tests.asmdef +++ b/Assets/Tests/Tests.asmdef @@ -1,7 +1,7 @@ { "name": "Tests", "references": [ - "Neo", + "Neo2Legacy", "PhantasmaLegacy" ], "precompiledReferences": [ From 709ae6b262835b36177ebc71f95810ee7e8bdb53 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 11:08:16 -0300 Subject: [PATCH 23/42] Extend unit test --- Assets/Tests/CryptoTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 9000799..43a86ab 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -170,9 +170,18 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = // Verifying concatenated signature / uncompressed Eth public key. Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); + // Verifying concatenated signature / compressed Eth public key. + Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + + // Verifying concatenated signature / uncompressed Eth public key. + Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); + // Verifying DER signature. Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded)); + // Verifying DER signature (unsupported). + Assert.IsFalse(ECDsa.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + var signatureConvertedBack = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.TranscodeSignatureToConcat(signatureDER, 64); var signatureConvertedBackHex = Base16.Encode(signatureConvertedBack); Debug.Log("\nSignature (converted back from DER):\n" + signatureConvertedBackHex); From 60190bf69e866a960013bec2286c781d432c6883 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 11:09:04 -0300 Subject: [PATCH 24/42] Fix "Verify signature" dialog --- Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index 249c6d4..d855e42 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -3508,12 +3508,12 @@ private void DoAccountManagementMenu(int btnOffset) else if (chain == "Ethereum") { var keys = EthereumKey.FromWIF(wif); - verificationResult = ECDsa.Verify(signatureBytes, messageBytes, keys.PublicKey, ECDsaCurve.Secp256k1); + verificationResult = ECDsa.Verify(messageBytes, signatureBytes, keys.PublicKey, ECDsaCurve.Secp256k1); } else if (chain == "Neo Legacy") { var keys = NeoKeys.FromWIF(wif); - verificationResult = ECDsa.Verify(signatureBytes, messageBytes, keys.PublicKey, ECDsaCurve.Secp256r1); + verificationResult = ECDsa.Verify(messageBytes, signatureBytes, keys.PublicKey, ECDsaCurve.Secp256r1); } else { From 9e7bd3903c3459147815bf27abed9e1146df2ca0 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 14:17:15 -0300 Subject: [PATCH 25/42] Tweak logging --- Assets/Phantasma.SDK/Log.cs | 29 +++++++++++++++++++ .../Wallet/WalletGUIFolder/WalletGUI.cs | 7 +++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Assets/Phantasma.SDK/Log.cs b/Assets/Phantasma.SDK/Log.cs index 658a630..55e772d 100644 --- a/Assets/Phantasma.SDK/Log.cs +++ b/Assets/Phantasma.SDK/Log.cs @@ -204,6 +204,35 @@ public static void Write(string message, Level level = Level.Logic, UnityDebugLo } } +#if UNITY_5_3_OR_NEWER + switch (unityDebugLogMode) + { + case UnityDebugLogMode.Normal: + Debug.Log(message); + break; + case UnityDebugLogMode.Warning: + Debug.LogWarning(message); + break; + case UnityDebugLogMode.Error: + Debug.LogError(message); + break; + } +#endif + } + + public static void WriteRaw(string message, Level level = Level.Logic, UnityDebugLogMode unityDebugLogMode = UnityDebugLogMode.Normal) + { + if (LogStreamWriter != null && MaxLevel != Level.Disabled && level <= MaxLevel) + { + lock (Locker) + { + if (ConsoleOutput) + Console.Write(message); + LogStreamWriter.Write(message); + LogStreamWriter.Flush(); + } + } + #if UNITY_5_3_OR_NEWER switch (unityDebugLogMode) { diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index d855e42..ce8ab5b 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -3491,10 +3491,13 @@ private void DoAccountManagementMenu(int btnOffset) var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); var signatureBytes = Base16.Decode(signature); - var hash = Base16.Encode(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(messageBytes)); if (accountManager.Settings.devMode) { - Log.Write($"Verified message: '{message}', hash: '{hash}', signature: '{signature}'"); + var hash = Base16.Encode(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(messageBytes)); + Log.WriteRaw($"Verified message: '{message}'"); + Log.WriteRaw("\n\n"); + Log.Write($"Hash: '{hash}'"); + Log.Write($"Signature: '{signature}'"); } var wif = AccountManager.Instance.CurrentAccount.GetWif(AccountManager.Instance.CurrentPasswordHash); From b1298a5f543a03e4732d43fa3bfed1c664c2eb1d Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 14:51:19 -0300 Subject: [PATCH 26/42] Code restructuring --- .../src/Cryptography/ECDsa/ECDsa.cs | 53 ++---------------- .../src/Cryptography/ECDsa/ECDsaHelpers.cs | 56 +++++++++++++++++++ .../Cryptography/ECDsa/ECDsaHelpers.cs.meta | 11 ++++ 3 files changed, 73 insertions(+), 47 deletions(-) create mode 100644 Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs create mode 100644 Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs.meta diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs index 00e0404..415d80d 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs @@ -35,7 +35,7 @@ public static byte[] GetPublicKey(byte[] privateKey, bool compressed, ECDsaCurve var d = new BigInteger(1, privateKey); var q = dom.G.Multiply(d); - + var publicParams = new ECPublicKeyParameters(q, dom); return publicParams.Q.GetEncoded(compressed); @@ -47,7 +47,7 @@ public static byte[] CompressPublicKey(byte[] uncompressedPublicKey) var y = new BigInteger(1, uncompressedPublicKey.Skip(32).ToArray()); byte prefix = 0x02; - if(y.Mod(BigInteger.Two) != BigInteger.Zero) + if (y.Mod(BigInteger.Two) != BigInteger.Zero) { prefix = 0x03; } @@ -76,28 +76,13 @@ public static byte[] DecompressPublicKey(byte[] compressedPublicKey, ECDsaCurve ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(dom.Curve.DecodePoint(compressedPublicKey), dom); var uncompressedPublicKey = publicKeyParameters.Q.GetEncoded(false); - + if (dropUncompressedKeyPrefixByte) { uncompressedPublicKey = uncompressedPublicKey.Skip(1).ToArray(); } - - return uncompressedPublicKey; - } - public static ECDomainParameters GetDomain(ECDsaCurve curve) - { - X9ECParameters ecCurve; - switch (curve) - { - case ECDsaCurve.Secp256k1: - ecCurve = SecNamedCurves.GetByName("secp256k1"); - break; - default: - ecCurve = SecNamedCurves.GetByName("secp256r1"); - break; - } - return new ECDomainParameters(ecCurve.Curve, ecCurve.G, ecCurve.N, ecCurve.H); + return uncompressedPublicKey; } public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve) @@ -120,7 +105,7 @@ public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve) signer.BlockUpdate(message, 0, message.Length); var signature = signer.GenerateSignature(); - return FromDER(signature); + return ECDsaHelpers.FromDER(signature); } public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve curve) @@ -147,33 +132,7 @@ public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsa signer.Init(false, publicKeyParameters); signer.BlockUpdate(message, 0, message.Length); - return signer.VerifySignature(ToDER(signature)); - } - - public static byte[] FromDER(byte[] signature) - { - using var decoder = new Asn1InputStream(signature); - var seq = decoder.ReadObject() as DerSequence; - if (seq == null || seq.Count != 2) - throw new FormatException("Invalid DER Signature"); - var R = ((DerInteger)seq[0]).Value.ToByteArrayUnsigned(); - var S = ((DerInteger)seq[1]).Value.ToByteArrayUnsigned(); - - byte[] concatenated = new byte[R.Length + S.Length]; - Buffer.BlockCopy(R, 0, concatenated, 0, R.Length); - Buffer.BlockCopy(S, 0, concatenated, R.Length, S.Length); - - return concatenated; - } - public static byte[] ToDER(byte[] signature) - { - // We convert from concatenated "raw" R + S format to DER format that Bouncy Castle uses. - return new DerSequence( - // first 32 bytes is "R" number - new DerInteger(new BigInteger(1, signature.Take(32).ToArray())), - // last 32 bytes is "S" number - new DerInteger(new BigInteger(1, signature.Skip(32).ToArray()))) - .GetDerEncoded(); + return signer.VerifySignature(ECDsaHelpers.ToDER(signature)); } } } diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs new file mode 100644 index 0000000..108afb3 --- /dev/null +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs @@ -0,0 +1,56 @@ +using System; +using System.Linq; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; + +namespace Phantasma.Core.Cryptography.ECDsa +{ + public static class ECDsaHelpers + { + public static ECDomainParameters GetDomain(ECDsaCurve curve) + { + X9ECParameters ecCurve; + switch (curve) + { + case ECDsaCurve.Secp256k1: + ecCurve = SecNamedCurves.GetByName("secp256k1"); + break; + default: + ecCurve = SecNamedCurves.GetByName("secp256r1"); + break; + } + + return new ECDomainParameters(ecCurve.Curve, ecCurve.G, ecCurve.N, ecCurve.H); + } + + public static byte[] FromDER(byte[] signature) + { + using var decoder = new Asn1InputStream(signature); + var seq = decoder.ReadObject() as DerSequence; + if (seq == null || seq.Count != 2) + throw new FormatException("Invalid DER Signature"); + var R = ((DerInteger)seq[0]).Value.ToByteArrayUnsigned(); + var S = ((DerInteger)seq[1]).Value.ToByteArrayUnsigned(); + + byte[] concatenated = new byte[R.Length + S.Length]; + Buffer.BlockCopy(R, 0, concatenated, 0, R.Length); + Buffer.BlockCopy(S, 0, concatenated, R.Length, S.Length); + + return concatenated; + } + + public static byte[] ToDER(byte[] signature) + { + // We convert from concatenated "raw" R + S format to DER format that Bouncy Castle uses. + return new DerSequence( + // first 32 bytes is "R" number + new DerInteger(new BigInteger(1, signature.Take(32).ToArray())), + // last 32 bytes is "S" number + new DerInteger(new BigInteger(1, signature.Skip(32).ToArray()))) + .GetDerEncoded(); + } + } +} diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs.meta b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs.meta new file mode 100644 index 0000000..c7b82ba --- /dev/null +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 34afa39b27f934047b751cd9c81156dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 93a2d6cfcadbf8f8742dcf9f71bc0234f03bdf4c Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 15:06:09 -0300 Subject: [PATCH 27/42] Crypto code refactoring --- .../src/Cryptography/ECDsa/ECDsaHelpers.cs | 50 +++++++--- .../Poltergeist.Extensions/CryptoUtils.cs | 97 +------------------ Assets/Tests/CryptoTests.cs | 6 +- 3 files changed, 42 insertions(+), 111 deletions(-) diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs index 108afb3..0840b44 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs @@ -10,22 +10,6 @@ namespace Phantasma.Core.Cryptography.ECDsa { public static class ECDsaHelpers { - public static ECDomainParameters GetDomain(ECDsaCurve curve) - { - X9ECParameters ecCurve; - switch (curve) - { - case ECDsaCurve.Secp256k1: - ecCurve = SecNamedCurves.GetByName("secp256k1"); - break; - default: - ecCurve = SecNamedCurves.GetByName("secp256r1"); - break; - } - - return new ECDomainParameters(ecCurve.Curve, ecCurve.G, ecCurve.N, ecCurve.H); - } - public static byte[] FromDER(byte[] signature) { using var decoder = new Asn1InputStream(signature); @@ -52,5 +36,39 @@ public static byte[] ToDER(byte[] signature) new DerInteger(new BigInteger(1, signature.Skip(32).ToArray()))) .GetDerEncoded(); } + + public static X9ECParameters GetECParameters(ECDsaCurve curve) + { + return curve switch + { + ECDsaCurve.Secp256k1 => ECNamedCurveTable.GetByName("secp256k1"), + ECDsaCurve.Secp256r1 => ECNamedCurveTable.GetByName("secp256r1"), + _ => throw new Exception("Unsupported curve"), + }; + } + + public static ECDomainParameters GetECDomainParameters(ECDsaCurve curve) + { + var ecParams = GetECParameters(curve); + return new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H); + } + + public static ECPrivateKeyParameters GetECPrivateKeyParameters(ECDsaCurve curve, byte[] privateKey) + { + return new ECPrivateKeyParameters(new BigInteger(1, privateKey), GetECDomainParameters(curve)); + } + + public static ECPublicKeyParameters GetECPublicKeyParameters(ECDsaCurve curve, byte[] publicKey) + { + var ecDomainParameters = GetECDomainParameters(curve); + + ECPublicKeyParameters publicKeyParameters; + if (publicKey.Length == 33) + publicKeyParameters = new ECPublicKeyParameters(ecDomainParameters.Curve.DecodePoint(publicKey), ecDomainParameters); + else + publicKeyParameters = new ECPublicKeyParameters(ecDomainParameters.Curve.CreatePoint(new BigInteger(1, publicKey.Take(publicKey.Length / 2).ToArray()), new BigInteger(1, publicKey.Skip(publicKey.Length / 2).ToArray())), ecDomainParameters); + + return publicKeyParameters; + } } } diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index 82cd014..2c547d3 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -19,97 +19,10 @@ public enum SignatureFormat DEREncoded } - // Transcodes the JCA ASN.1/DER-encoded signature into the concatenated R + S format expected by ECDSA JWS. - public static byte[] TranscodeSignatureToConcat(byte[] derSignature, int outputLength) - { - if (derSignature.Length < 8 || derSignature[0] != 48) throw new Exception("Invalid ECDSA signature format"); - - int offset; - if (derSignature[1] > 0) - offset = 2; - else if (derSignature[1] == 0x81) - offset = 3; - else - throw new Exception("Invalid ECDSA signature format"); - - var rLength = derSignature[offset + 1]; - - int i = rLength; - while (i > 0 - && derSignature[offset + 2 + rLength - i] == 0) - i--; - - var sLength = derSignature[offset + 2 + rLength + 1]; - - int j = sLength; - while (j > 0 - && derSignature[offset + 2 + rLength + 2 + sLength - j] == 0) - j--; - - var rawLen = Math.Max(i, j); - rawLen = Math.Max(rawLen, outputLength / 2); - - if ((derSignature[offset - 1] & 0xff) != derSignature.Length - offset - || (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength - || derSignature[offset] != 2 - || derSignature[offset + 2 + rLength] != 2) - throw new Exception("Invalid ECDSA signature format"); - - var concatSignature = new byte[2 * rawLen]; - - Array.Copy(derSignature, offset + 2 + rLength - i, concatSignature, rawLen - i, i); - Array.Copy(derSignature, offset + 2 + rLength + 2 + sLength - j, concatSignature, 2 * rawLen - j, j); - - return concatSignature; - } - - public static byte[] RSBytesToDER(byte[] RSBytes) - { - return new Org.BouncyCastle.Asn1.DerSequence( - // first 32 bytes is "r" number - new Org.BouncyCastle.Asn1.DerInteger(new BigInteger(1, RSBytes.Take(32).ToArray())), - // last 32 bytes is "s" number - new Org.BouncyCastle.Asn1.DerInteger(new BigInteger(1, RSBytes.Skip(32).ToArray()))) - .GetDerEncoded(); - } - - private static X9ECParameters GetECParameters(ECDsaCurve curve) - { - return curve switch - { - ECDsaCurve.Secp256k1 => ECNamedCurveTable.GetByName("secp256k1"), - ECDsaCurve.Secp256r1 => ECNamedCurveTable.GetByName("secp256r1"), - _ => throw new Exception("Unsupported curve"), - }; - } - - private static ECDomainParameters GetECDomainParameters(ECDsaCurve curve) - { - var ecParams = GetECParameters(curve); - return new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H); - } - - private static ECPrivateKeyParameters GetECPrivateKeyParameters(ECDsaCurve curve, byte[] privateKey) - { - return new ECPrivateKeyParameters(new BigInteger(1, privateKey), GetECDomainParameters(curve)); - } - - private static ECPublicKeyParameters GetECPublicKeyParameters(ECDsaCurve curve, byte[] publicKey) - { - var ecDomainParameters = GetECDomainParameters(curve); - - ECPublicKeyParameters publicKeyParameters; - if (publicKey.Length == 33) - publicKeyParameters = new ECPublicKeyParameters(ecDomainParameters.Curve.DecodePoint(publicKey), ecDomainParameters); - else - publicKeyParameters = new ECPublicKeyParameters(ecDomainParameters.Curve.CreatePoint(new BigInteger(1, publicKey.Take(publicKey.Length / 2).ToArray()), new BigInteger(1, publicKey.Skip(publicKey.Length / 2).ToArray())), ecDomainParameters); - - return publicKeyParameters; - } public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve, SignatureFormat signatureFormat = SignatureFormat.Concatenated) { var signer = SignerUtilities.GetSigner("SHA256withECDSA"); - var privateKeyParameters = GetECPrivateKeyParameters(curve, prikey); + var privateKeyParameters = ECDsaHelpers.GetECPrivateKeyParameters(curve, prikey); signer.Init(true, privateKeyParameters); signer.BlockUpdate(message, 0, message.Length); @@ -119,7 +32,7 @@ public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve, Signa { case SignatureFormat.Concatenated: // We convert from default DER format that Bouncy Castle uses to concatenated "raw" R + S format. - return TranscodeSignatureToConcat(sig, 64); + return ECDsaHelpers.FromDER(sig); case SignatureFormat.DEREncoded: // Return DER-encoded signature unchanged. return sig; @@ -132,7 +45,7 @@ public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve { var messageHash = Sha256Hash(message); - var privateKeyParameters = GetECPrivateKeyParameters(curve, prikey); + var privateKeyParameters = ECDsaHelpers.GetECPrivateKeyParameters(curve, prikey); var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest())); @@ -148,7 +61,7 @@ public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve curve, SignatureFormat signatureFormat = SignatureFormat.Concatenated) { var signer = SignerUtilities.GetSigner("SHA256withECDSA"); - var publicKeyParameters = GetECPublicKeyParameters(curve, pubkey); + var publicKeyParameters = ECDsaHelpers.GetECPublicKeyParameters(curve, pubkey); signer.Init(false, publicKeyParameters); signer.BlockUpdate(message, 0, message.Length); @@ -157,7 +70,7 @@ public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsa { case SignatureFormat.Concatenated: // We convert from concatenated "raw" R + S format to DER format that Bouncy Castle uses. - signature = RSBytesToDER(signature); + signature = ECDsaHelpers.ToDER(signature); break; case SignatureFormat.DEREncoded: // Do nothing, signature already DER-encoded. diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 43a86ab..1341be4 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -112,7 +112,7 @@ public IEnumerator ECDsaSecP256k1_Deterministic() // Following is the format we use for signature: Debug.Log("\nSignature (curve byte + signature length + concatenated r & s, hex):\n" + Base16.Encode(signatureSerialized)); - var signatureDEREncoded = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.RSBytesToDER(ecdsaSignature.Bytes); + var signatureDEREncoded = ECDsaHelpers.ToDER(ecdsaSignature.Bytes); Debug.Log("\nSignature (RAW DER-encoded, hex):\n" + Base16.Encode(signatureDEREncoded)); Debug.Log("\nSignature (curve byte + signature length + DER-encoded, hex):\n" + Base16.Encode(signatureDEREncoded.Serialize())); @@ -160,7 +160,7 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = // Following is the format we use for signature: Debug.Log("\nSignature (concatenated r & s, hex):\n" + signatureHex); - var signatureDER = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.RSBytesToDER(signature); + var signatureDER = ECDsaHelpers.ToDER(signature); Debug.Log("\nSignature (RAW DER-encoded, hex):\n" + Base16.Encode(signatureDER)); @@ -182,7 +182,7 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = // Verifying DER signature (unsupported). Assert.IsFalse(ECDsa.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); - var signatureConvertedBack = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.TranscodeSignatureToConcat(signatureDER, 64); + var signatureConvertedBack = ECDsaHelpers.FromDER(signatureDER); var signatureConvertedBackHex = Base16.Encode(signatureConvertedBack); Debug.Log("\nSignature (converted back from DER):\n" + signatureConvertedBackHex); Assert.AreEqual(signatureHex, signatureConvertedBackHex); From 53934fa1d314625a8b2d478e129c56a4cacb0814 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 15:14:34 -0300 Subject: [PATCH 28/42] Extend unit tests --- Assets/Tests/CryptoTests.cs | 72 ++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 1341be4..4c32df2 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -14,6 +14,38 @@ namespace Phantasma.Tests { public class CryptoTests { + [UnityTest] + public IEnumerator WifPkTest() + { + var wif = "Kyry8sMHFzx5DfubcqyGMQByaHQBtdyALBjAqcx9Lx1YtSZjy2vZ"; + var pkHex = "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49"; + + var keyFromWif = EthereumKey.FromWIF(wif); + + Assert.AreEqual(keyFromWif.PublicKey, keyFromWif.CompressedPublicKey); + + var pkBytesFromHex = Base16.Decode(pkHex); + Assert.AreEqual(pkBytesFromHex.Length, 32); + + var keyFromHex = new EthereumKey(pkBytesFromHex); + + var pkBytesFromWif = EthereumKey.FromWIFToBytes(wif); + Assert.AreEqual(pkBytesFromWif, pkBytesFromHex); + Assert.AreEqual(pkBytesFromWif, keyFromWif.PrivateKey); + Assert.AreEqual(pkBytesFromWif, keyFromHex.PrivateKey); + Assert.AreEqual(pkBytesFromWif.Length, 32); + + Assert.AreEqual(keyFromWif.PrivateKey.Length, 32); + Assert.AreEqual(keyFromHex.PrivateKey.Length, 32); + Assert.AreEqual(keyFromWif.PublicKey.Length, 33); + Assert.AreEqual(keyFromHex.PublicKey.Length, 33); + + Assert.AreEqual(keyFromWif.PublicKey, keyFromHex.PublicKey); + Assert.AreEqual(keyFromWif.PrivateKey, keyFromHex.PrivateKey); + + yield return null; + } + [UnityTest] public IEnumerator ECDsaSecP256k1() { @@ -151,6 +183,13 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, privBytes, ECDsaCurve.Secp256k1); var signatureHex = Base16.Encode(signature); + var signature2 = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msgBytes, privBytes, ECDsaCurve.Secp256k1); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); + var signature3 = ECDsa.Sign(msgBytes, privBytes, ECDsaCurve.Secp256k1); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature3, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature3, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); + if (signatureReference != null) { Assert.AreEqual(signatureHex, signatureReference); @@ -191,39 +230,6 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureConvertedBack, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); } - - [UnityTest] - public IEnumerator WifPkTest() - { - var wif = "Kyry8sMHFzx5DfubcqyGMQByaHQBtdyALBjAqcx9Lx1YtSZjy2vZ"; - var pkHex = "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49"; - - var keyFromWif = EthereumKey.FromWIF(wif); - - Assert.AreEqual(keyFromWif.PublicKey, keyFromWif.CompressedPublicKey); - - var pkBytesFromHex = Base16.Decode(pkHex); - Assert.AreEqual(pkBytesFromHex.Length, 32); - - var keyFromHex = new EthereumKey(pkBytesFromHex); - - var pkBytesFromWif = EthereumKey.FromWIFToBytes(wif); - Assert.AreEqual(pkBytesFromWif, pkBytesFromHex); - Assert.AreEqual(pkBytesFromWif, keyFromWif.PrivateKey); - Assert.AreEqual(pkBytesFromWif, keyFromHex.PrivateKey); - Assert.AreEqual(pkBytesFromWif.Length, 32); - - Assert.AreEqual(keyFromWif.PrivateKey.Length, 32); - Assert.AreEqual(keyFromHex.PrivateKey.Length, 32); - Assert.AreEqual(keyFromWif.PublicKey.Length, 33); - Assert.AreEqual(keyFromHex.PublicKey.Length, 33); - - Assert.AreEqual(keyFromWif.PublicKey, keyFromHex.PublicKey); - Assert.AreEqual(keyFromWif.PrivateKey, keyFromHex.PrivateKey); - - yield return null; - } - [UnityTest] public IEnumerator ECDsaSecP256k1_DeterministicRaw() { From 923401d62facfa79612583a29fcd2510d4fae3d5 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 15:21:11 -0300 Subject: [PATCH 29/42] Extend unit tests --- Assets/Tests/CryptoTests.cs | 50 ++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 4c32df2..d28b24f 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -160,8 +160,9 @@ public IEnumerator ECDsaSecP256k1_Deterministic() yield return null; } - private void ECDsaTest(string pkHex, string message, string signatureReference = null) + private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string signatureReference = null) { + Debug.Log("\n\n\nCurve: " + curve); Assert.IsTrue(pkHex.Length == 64); var privBytes = Base16.Decode(pkHex); @@ -171,8 +172,8 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = var ethKeys = EthereumKey.FromWIF(wif); Debug.Log("Eth address: " + ethKeys); - var ethPublicKeyCompressed = ECDsa.GetPublicKey(privBytes, true, ECDsaCurve.Secp256k1); - var ethPublicKeyUncompressed = ECDsa.GetPublicKey(privBytes, false, ECDsaCurve.Secp256k1).Skip(1).ToArray(); + var ethPublicKeyCompressed = ECDsa.GetPublicKey(privBytes, true, curve); + var ethPublicKeyUncompressed = ECDsa.GetPublicKey(privBytes, false, curve).Skip(1).ToArray(); Debug.Log("Eth compressed public key: " + Base16.Encode(ethPublicKeyCompressed)); var msgBytes = Encoding.ASCII.GetBytes(message); @@ -180,15 +181,15 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = var hash = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(msgBytes); Debug.Log("Message hash: " + Base16.Encode(hash)); - var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, privBytes, ECDsaCurve.Secp256k1); + var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, privBytes, curve); var signatureHex = Base16.Encode(signature); - var signature2 = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msgBytes, privBytes, ECDsaCurve.Secp256k1); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); - var signature3 = ECDsa.Sign(msgBytes, privBytes, ECDsaCurve.Secp256k1); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature3, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature3, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); + var signature2 = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msgBytes, privBytes, curve); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyCompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyUncompressed, curve)); + var signature3 = ECDsa.Sign(msgBytes, privBytes, curve); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature3, ethPublicKeyCompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature3, ethPublicKeyUncompressed, curve)); if (signatureReference != null) { @@ -204,22 +205,22 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = Debug.Log("\nSignature (RAW DER-encoded, hex):\n" + Base16.Encode(signatureDER)); // Verifying concatenated signature / compressed Eth public key. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyCompressed, curve)); // Verifying concatenated signature / uncompressed Eth public key. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyUncompressed, curve)); // Verifying concatenated signature / compressed Eth public key. - Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyCompressed, curve)); // Verifying concatenated signature / uncompressed Eth public key. - Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyUncompressed, curve)); // Verifying DER signature. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded)); + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, curve, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded)); // Verifying DER signature (unsupported). - Assert.IsFalse(ECDsa.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + Assert.IsFalse(ECDsa.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, curve)); var signatureConvertedBack = ECDsaHelpers.FromDER(signatureDER); var signatureConvertedBackHex = Base16.Encode(signatureConvertedBack); @@ -227,21 +228,24 @@ private void ECDsaTest(string pkHex, string message, string signatureReference = Assert.AreEqual(signatureHex, signatureConvertedBackHex); // Verifying signature, converted back from DER. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureConvertedBack, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureConvertedBack, ethPublicKeyCompressed, curve)); } [UnityTest] - public IEnumerator ECDsaSecP256k1_DeterministicRaw() + public IEnumerator ECDsaSecP256k1_Mixed() { // Eth address: 0x66571c32d77c4852be4c282eb952ba94efbeac20 - ECDsaTest("6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "Phantasma"); - ECDsaTest("6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "test message"); + ECDsaTest(ECDsaCurve.Secp256k1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "Phantasma"); + ECDsaTest(ECDsaCurve.Secp256r1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "Phantasma"); + ECDsaTest(ECDsaCurve.Secp256k1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "test message"); + ECDsaTest(ECDsaCurve.Secp256r1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "test message"); // Eth address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a - ECDsaTest("4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "Phantasma"); - ECDsaTest("4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "test message", "55DEB9E4D985834192AB8298C3DDA18EB7082C2A744EBDF7233D0A93FB00A4A9F4750F4A6F3FB3928C28690BE3A2BE52DEB95E1935E960FACBF7CC4AC4FDADCB"); + ECDsaTest(ECDsaCurve.Secp256k1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "Phantasma"); + ECDsaTest(ECDsaCurve.Secp256r1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "Phantasma"); + ECDsaTest(ECDsaCurve.Secp256k1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "test message", "55DEB9E4D985834192AB8298C3DDA18EB7082C2A744EBDF7233D0A93FB00A4A9F4750F4A6F3FB3928C28690BE3A2BE52DEB95E1935E960FACBF7CC4AC4FDADCB"); - ECDsaTest("4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + + ECDsaTest(ECDsaCurve.Secp256k1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + "Phantasma address: P2KHhbVZWDv1ZLLoJccN3PUAb9x9BqRnUyH3ZEhu5YwBeJQ\n" + "Ethereum address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a\n" + "Ethereum public key: 5D3F7F469803C68C12B8F731576C74A9B5308484FD3B425D87C35CAED0A2E398C7AC626D916A1D65E23F673A55E6B16FFC1ABD673F3EF6AE8D5E6A0F99784A56\n" + From cdd18acf2a6fe05daf81d9c103800ef129a97a8b Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 15:32:16 -0300 Subject: [PATCH 30/42] Crypto code refactoring --- .../src/Cryptography/ECDsa/ECDsa.cs | 61 ++----------------- .../Poltergeist.Extensions/CryptoUtils.cs | 3 - 2 files changed, 4 insertions(+), 60 deletions(-) diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs index 415d80d..258f99c 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs @@ -1,12 +1,8 @@ using System; using System.Linq; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Sec; -using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; -using Poltergeist.PhantasmaLegacy.Ethereum.Hex.HexConvertors.Extensions; namespace Phantasma.Core.Cryptography.ECDsa { @@ -20,18 +16,7 @@ public static class ECDsa { public static byte[] GetPublicKey(byte[] privateKey, bool compressed, ECDsaCurve curve) { - X9ECParameters ecCurve; - switch (curve) - { - case ECDsaCurve.Secp256k1: - ecCurve = SecNamedCurves.GetByName("secp256k1"); - break; - default: - ecCurve = SecNamedCurves.GetByName("secp256r1"); - break; - } - - var dom = new ECDomainParameters(ecCurve.Curve, ecCurve.G, ecCurve.N, ecCurve.H); + var dom = ECDsaHelpers.GetECDomainParameters(curve); var d = new BigInteger(1, privateKey); var q = dom.G.Multiply(d); @@ -62,19 +47,8 @@ public static byte[] DecompressPublicKey(byte[] compressedPublicKey, ECDsaCurve throw new ArgumentException("Incorrect compressed key length: " + compressedPublicKey.Length); } - X9ECParameters ecCurve; - switch (curve) - { - case ECDsaCurve.Secp256k1: - ecCurve = SecNamedCurves.GetByName("secp256k1"); - break; - default: - ecCurve = SecNamedCurves.GetByName("secp256r1"); - break; - } - var dom = new ECDomainParameters(ecCurve.Curve, ecCurve.G, ecCurve.N, ecCurve.H); + var publicKeyParameters = ECDsaHelpers.GetECPublicKeyParameters(curve, compressedPublicKey); - ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(dom.Curve.DecodePoint(compressedPublicKey), dom); var uncompressedPublicKey = publicKeyParameters.Q.GetEncoded(false); if (dropUncompressedKeyPrefixByte) @@ -88,18 +62,7 @@ public static byte[] DecompressPublicKey(byte[] compressedPublicKey, ECDsaCurve public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve) { var signer = SignerUtilities.GetSigner("SHA256withECDSA"); - X9ECParameters ecCurve; - switch (curve) - { - case ECDsaCurve.Secp256k1: - ecCurve = SecNamedCurves.GetByName("secp256k1"); - break; - default: - ecCurve = SecNamedCurves.GetByName("secp256r1"); - break; - } - var dom = new ECDomainParameters(ecCurve.Curve, ecCurve.G, ecCurve.N, ecCurve.H); - var privateKeyParameters = new ECPrivateKeyParameters(new BigInteger(1, prikey), dom); + var privateKeyParameters = ECDsaHelpers.GetECPrivateKeyParameters(curve, prikey); signer.Init(true, privateKeyParameters); signer.BlockUpdate(message, 0, message.Length); @@ -111,23 +74,7 @@ public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve) public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve curve) { var signer = SignerUtilities.GetSigner("SHA256withECDSA"); - X9ECParameters ecCurve; - switch (curve) - { - case ECDsaCurve.Secp256k1: - ecCurve = SecNamedCurves.GetByName("secp256k1"); - break; - default: - ecCurve = SecNamedCurves.GetByName("secp256r1"); - break; - } - var dom = new ECDomainParameters(ecCurve.Curve, ecCurve.G, ecCurve.N, ecCurve.H); - - ECPublicKeyParameters publicKeyParameters; - if (pubkey.Length == 33) - publicKeyParameters = new ECPublicKeyParameters(dom.Curve.DecodePoint(pubkey), dom); - else - publicKeyParameters = new ECPublicKeyParameters(dom.Curve.CreatePoint(new BigInteger(1, pubkey.Take(pubkey.Length / 2).ToArray()), new BigInteger(1, pubkey.Skip(pubkey.Length / 2).ToArray())), dom); + var publicKeyParameters = ECDsaHelpers.GetECPublicKeyParameters(curve, pubkey); signer.Init(false, publicKeyParameters); signer.BlockUpdate(message, 0, message.Length); diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index 2c547d3..aca5b89 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -1,10 +1,7 @@ using System; using System.Linq; -using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Signers; -using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Phantasma.Core.Cryptography.ECDsa; From e576a69dcda0e4c27c3db7b93d5d9eaf577bca76 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 15:42:58 -0300 Subject: [PATCH 31/42] Crypto code refactoring --- .../Poltergeist.Extensions/CryptoUtils.cs | 24 ------------------- Assets/Tests/CryptoTests.cs | 21 +++++----------- 2 files changed, 6 insertions(+), 39 deletions(-) diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index aca5b89..6e99d9d 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -55,30 +55,6 @@ public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve return R.Concat(S).ToArray(); } - public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve curve, SignatureFormat signatureFormat = SignatureFormat.Concatenated) - { - var signer = SignerUtilities.GetSigner("SHA256withECDSA"); - var publicKeyParameters = ECDsaHelpers.GetECPublicKeyParameters(curve, pubkey); - - signer.Init(false, publicKeyParameters); - signer.BlockUpdate(message, 0, message.Length); - - switch (signatureFormat) - { - case SignatureFormat.Concatenated: - // We convert from concatenated "raw" R + S format to DER format that Bouncy Castle uses. - signature = ECDsaHelpers.ToDER(signature); - break; - case SignatureFormat.DEREncoded: - // Do nothing, signature already DER-encoded. - break; - default: - throw new Exception("Unknown signature format"); - } - - return signer.VerifySignature(signature); - } - public static byte[] Sha256Hash(byte[] message) { var sha256 = new Phantasma.Core.Cryptography.Hashing.SHA256(); diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index d28b24f..0062f95 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -92,14 +92,14 @@ public IEnumerator ECDsaSecP256k1() // Since ECDsaSignature class not working for us, // we use signature .Bytes directly to verify it with Bouncy Castle. // Verifying concatenated signature / compressed Eth public key. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, ecdsaSignature.Bytes, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + Assert.IsTrue(ECDsa.Verify(msgBytes, ecdsaSignature.Bytes, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); // Verifying concatenated signature / uncompressed Eth public key. // Not working with Bouncy Castle. // Assert.IsTrue(Phantasma.Neo.Utils.CryptoUtils.Verify(msgBytes, ecdsaSignature.Bytes, ethPublicKeyUncompressed, ECDsaCurve.Secp256k1)); // Verifying DER signature. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, ecdsaSignatureDEREncoded.Bytes, ethPublicKeyCompressed, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded)); + Assert.IsTrue(ECDsa.Verify(msgBytes, ECDsaHelpers.FromDER(ecdsaSignatureDEREncoded.Bytes), ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); // This method we cannot use, it gives "System.NotImplementedException : The method or operation is not implemented." // exception in Unity, because Unity does not fully support .NET cryptography. @@ -152,10 +152,10 @@ public IEnumerator ECDsaSecP256k1_Deterministic() // Since ECDsaSignature class not working for us, // we use signature .Bytes directly to verify it with Bouncy Castle. // Verifying concatenated signature / compressed Eth public key. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, ecdsaSignature.Bytes, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); + Assert.IsTrue(ECDsa.Verify(msgBytes, ecdsaSignature.Bytes, ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); // Verifying DER signature. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureDEREncoded, ethPublicKeyCompressed, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded)); + Assert.IsTrue(ECDsa.Verify(msgBytes, ECDsaHelpers.FromDER(signatureDEREncoded), ethPublicKeyCompressed, ECDsaCurve.Secp256k1)); yield return null; } @@ -204,21 +204,12 @@ private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string si Debug.Log("\nSignature (RAW DER-encoded, hex):\n" + Base16.Encode(signatureDER)); - // Verifying concatenated signature / compressed Eth public key. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyCompressed, curve)); - - // Verifying concatenated signature / uncompressed Eth public key. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signature, ethPublicKeyUncompressed, curve)); - // Verifying concatenated signature / compressed Eth public key. Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyCompressed, curve)); // Verifying concatenated signature / uncompressed Eth public key. Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyUncompressed, curve)); - // Verifying DER signature. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, curve, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded)); - // Verifying DER signature (unsupported). Assert.IsFalse(ECDsa.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, curve)); @@ -227,8 +218,8 @@ private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string si Debug.Log("\nSignature (converted back from DER):\n" + signatureConvertedBackHex); Assert.AreEqual(signatureHex, signatureConvertedBackHex); - // Verifying signature, converted back from DER. - Assert.IsTrue(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Verify(msgBytes, signatureConvertedBack, ethPublicKeyCompressed, curve)); + // Verifying DER signature. + Assert.IsTrue(ECDsa.Verify(msgBytes, signatureConvertedBack, ethPublicKeyCompressed, curve)); } [UnityTest] From 13c9ae39ea4f5e78bc9d82719c53f9caf7f52e79 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 15:49:45 -0300 Subject: [PATCH 32/42] Crypto code refactoring --- .../Poltergeist.Extensions/CryptoUtils.cs | 22 ------------------- .../Wallet/WalletLink/WalletConnector.cs | 8 +++---- Assets/Tests/CryptoTests.cs | 9 +++----- 3 files changed, 7 insertions(+), 32 deletions(-) diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index 6e99d9d..82f752f 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -16,28 +16,6 @@ public enum SignatureFormat DEREncoded } - public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve, SignatureFormat signatureFormat = SignatureFormat.Concatenated) - { - var signer = SignerUtilities.GetSigner("SHA256withECDSA"); - var privateKeyParameters = ECDsaHelpers.GetECPrivateKeyParameters(curve, prikey); - - signer.Init(true, privateKeyParameters); - signer.BlockUpdate(message, 0, message.Length); - var sig = signer.GenerateSignature(); - - switch (signatureFormat) - { - case SignatureFormat.Concatenated: - // We convert from default DER format that Bouncy Castle uses to concatenated "raw" R + S format. - return ECDsaHelpers.FromDER(sig); - case SignatureFormat.DEREncoded: - // Return DER-encoded signature unchanged. - return sig; - default: - throw new Exception("Unknown signature format"); - } - } - public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve curve) { var messageHash = Sha256Hash(message); diff --git a/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs b/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs index 17cf620..19b0c7e 100644 --- a/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs +++ b/Assets/Scripts/Wallet/WalletLink/WalletConnector.cs @@ -299,7 +299,7 @@ protected override void FetchAndMultiSignature(string subject, string platform, case SignatureKind.ECDSA: var ethKeys = EthereumKey.FromWIF(wif); - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, ethKeys.PrivateKey, ECDsaCurve.Secp256k1); + var signatureBytes = ECDsa.Sign(msg, ethKeys.PrivateKey, ECDsaCurve.Secp256k1); signature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); break; @@ -372,7 +372,7 @@ protected override void SignTransactionSignature(Phantasma.Core.Domain.Transacti case SignatureKind.ECDSA: var ethKeys = EthereumKey.FromWIF(wif); - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, ethKeys.PrivateKey, ECDsaCurve.Secp256k1); + var signatureBytes = ECDsa.Sign(msg, ethKeys.PrivateKey, ECDsaCurve.Secp256k1); signature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); break; @@ -531,13 +531,13 @@ protected override void SignData(string platform, SignatureKind kind, byte[] dat { var ethKeys = EthereumKey.FromWIF(wif); - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, ethKeys.PrivateKey, ECDsaCurve.Secp256k1); + var signatureBytes = ECDsa.Sign(msg, ethKeys.PrivateKey, ECDsaCurve.Secp256k1); signature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); } else { var neoKeys = NeoKeys.FromWIF(wif); - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msg, neoKeys.PrivateKey, ECDsaCurve.Secp256k1); + var signatureBytes = ECDsa.Sign(msg, neoKeys.PrivateKey, ECDsaCurve.Secp256k1); signature = new ECDsaSignature(signatureBytes, ECDsaCurve.Secp256k1); } break; diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 0062f95..0825c37 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -68,7 +68,7 @@ public IEnumerator ECDsaSecP256k1() var msgBytes = Encoding.ASCII.GetBytes("Phantasma"); var signature = ethKeys.Sign(msgBytes, (message, prikey, pubkey) => { - return Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(message, prikey, ECDsaCurve.Secp256k1); + return ECDsa.Sign(message, prikey, ECDsaCurve.Secp256k1); }); var ecdsaSignature = (ECDsaSignature)signature; @@ -81,7 +81,7 @@ public IEnumerator ECDsaSecP256k1() var signatureDEREncoded = ethKeys.Sign(msgBytes, (message, prikey, pubkey) => { - return Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(message, prikey, ECDsaCurve.Secp256k1, Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignatureFormat.DEREncoded); + return ECDsaHelpers.ToDER(ECDsa.Sign(message, prikey, ECDsaCurve.Secp256k1)); }); var ecdsaSignatureDEREncoded = (ECDsaSignature)signatureDEREncoded; @@ -184,12 +184,9 @@ private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string si var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, privBytes, curve); var signatureHex = Base16.Encode(signature); - var signature2 = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sign(msgBytes, privBytes, curve); + var signature2 = ECDsa.Sign(msgBytes, privBytes, curve); Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyCompressed, curve)); Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyUncompressed, curve)); - var signature3 = ECDsa.Sign(msgBytes, privBytes, curve); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature3, ethPublicKeyCompressed, curve)); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature3, ethPublicKeyUncompressed, curve)); if (signatureReference != null) { From ee966851eefe31dda599e0f38d9071735442a439 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 15:57:03 -0300 Subject: [PATCH 33/42] Clean unit test code --- Assets/Tests/CryptoTests.cs | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 0825c37..01a6676 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -165,28 +165,23 @@ private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string si Debug.Log("\n\n\nCurve: " + curve); Assert.IsTrue(pkHex.Length == 64); - var privBytes = Base16.Decode(pkHex); - var phantasmaKeys = new PhantasmaKeys(privBytes); - - var wif = phantasmaKeys.ToWIF(); - var ethKeys = EthereumKey.FromWIF(wif); - Debug.Log("Eth address: " + ethKeys); + var privKey = Base16.Decode(pkHex); - var ethPublicKeyCompressed = ECDsa.GetPublicKey(privBytes, true, curve); - var ethPublicKeyUncompressed = ECDsa.GetPublicKey(privBytes, false, curve).Skip(1).ToArray(); - Debug.Log("Eth compressed public key: " + Base16.Encode(ethPublicKeyCompressed)); + var pubKeyCompressed = ECDsa.GetPublicKey(privKey, true, curve); + var pubKeyUncompressed = ECDsa.GetPublicKey(privKey, false, curve).Skip(1).ToArray(); + Debug.Log("Eth compressed public key: " + Base16.Encode(pubKeyCompressed)); var msgBytes = Encoding.ASCII.GetBytes(message); var hash = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(msgBytes); Debug.Log("Message hash: " + Base16.Encode(hash)); - var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, privBytes, curve); + var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, privKey, curve); var signatureHex = Base16.Encode(signature); - var signature2 = ECDsa.Sign(msgBytes, privBytes, curve); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyCompressed, curve)); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, ethPublicKeyUncompressed, curve)); + var signature2 = ECDsa.Sign(msgBytes, privKey, curve); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, pubKeyCompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, pubKeyUncompressed, curve)); if (signatureReference != null) { @@ -202,13 +197,13 @@ private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string si Debug.Log("\nSignature (RAW DER-encoded, hex):\n" + Base16.Encode(signatureDER)); // Verifying concatenated signature / compressed Eth public key. - Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyCompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature, pubKeyCompressed, curve)); // Verifying concatenated signature / uncompressed Eth public key. - Assert.IsTrue(ECDsa.Verify(msgBytes, signature, ethPublicKeyUncompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature, pubKeyUncompressed, curve)); // Verifying DER signature (unsupported). - Assert.IsFalse(ECDsa.Verify(msgBytes, signatureDER, ethPublicKeyCompressed, curve)); + Assert.IsFalse(ECDsa.Verify(msgBytes, signatureDER, pubKeyCompressed, curve)); var signatureConvertedBack = ECDsaHelpers.FromDER(signatureDER); var signatureConvertedBackHex = Base16.Encode(signatureConvertedBack); @@ -216,7 +211,7 @@ private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string si Assert.AreEqual(signatureHex, signatureConvertedBackHex); // Verifying DER signature. - Assert.IsTrue(ECDsa.Verify(msgBytes, signatureConvertedBack, ethPublicKeyCompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signatureConvertedBack, pubKeyCompressed, curve)); } [UnityTest] From c038be49b1c2e6dd09aec29e95c117a260d12eb2 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 16:27:04 -0300 Subject: [PATCH 34/42] Extend unit tests --- Assets/Tests/CryptoTests.cs | 131 +++++++++++++++++++++++++++++------- 1 file changed, 105 insertions(+), 26 deletions(-) diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 01a6676..86d72dc 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -9,6 +9,8 @@ using Phantasma.Core.Cryptography; using Phantasma.Core.Cryptography.ECDsa; using Phantasma.Core.Domain; +using Poltergeist.Neo2.Core; +using System; namespace Phantasma.Tests { @@ -160,28 +162,74 @@ public IEnumerator ECDsaSecP256k1_Deterministic() yield return null; } - private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string signatureReference = null) + private class Keys { - Debug.Log("\n\n\nCurve: " + curve); - Assert.IsTrue(pkHex.Length == 64); + public ECDsaCurve Curve; + public byte[] PrivKey; + public byte[] PubKeyCompressed; + public byte[] PubKeyUncompressed; - var privKey = Base16.Decode(pkHex); + public Keys(ECDsaCurve curve, bool useKeyClasses, string pkHex) + { + Assert.IsTrue(pkHex.Length == 64); + + PrivKey = Base16.Decode(pkHex); + + if (useKeyClasses) + { + switch(curve) + { + case ECDsaCurve.Secp256k1: + var phantasmaKeys = new PhantasmaKeys(PrivKey); + + var wif = phantasmaKeys.ToWIF(); + var ethKeys = EthereumKey.FromWIF(wif); + + PubKeyCompressed = ethKeys.CompressedPublicKey; + PubKeyUncompressed = ethKeys.UncompressedPublicKey; + break; + case ECDsaCurve.Secp256r1: + var neoKeys = new NeoKeys(PrivKey); + + PubKeyCompressed = neoKeys.CompressedPublicKey; + PubKeyUncompressed = neoKeys.PublicKey; + break; + default: + throw new Exception("Unsupported curve"); + } + } + else + { + PubKeyCompressed = ECDsa.GetPublicKey(PrivKey, true, curve); + PubKeyUncompressed = ECDsa.GetPublicKey(PrivKey, false, curve).Skip(1).ToArray(); + } + } - var pubKeyCompressed = ECDsa.GetPublicKey(privKey, true, curve); - var pubKeyUncompressed = ECDsa.GetPublicKey(privKey, false, curve).Skip(1).ToArray(); - Debug.Log("Eth compressed public key: " + Base16.Encode(pubKeyCompressed)); + public static Keys NewKeys(ECDsaCurve curve, bool useKeyClasses, string pkHex) + { + return new Keys(curve, useKeyClasses, pkHex); + } + } + + private void ECDsaTest(ECDsaCurve curve, + Keys keys, + string message, string signatureReference = null) + { + Debug.Log("\n\n\nCurve: " + curve); + + Debug.Log("Compressed public key: " + Base16.Encode(keys.PubKeyCompressed)); var msgBytes = Encoding.ASCII.GetBytes(message); var hash = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(msgBytes); Debug.Log("Message hash: " + Base16.Encode(hash)); - var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, privKey, curve); + var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, keys.PrivKey, curve); var signatureHex = Base16.Encode(signature); - var signature2 = ECDsa.Sign(msgBytes, privKey, curve); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, pubKeyCompressed, curve)); - Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, pubKeyUncompressed, curve)); + var signature2 = ECDsa.Sign(msgBytes, keys.PrivKey, curve); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, keys.PubKeyCompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature2, keys.PubKeyUncompressed, curve)); if (signatureReference != null) { @@ -197,13 +245,13 @@ private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string si Debug.Log("\nSignature (RAW DER-encoded, hex):\n" + Base16.Encode(signatureDER)); // Verifying concatenated signature / compressed Eth public key. - Assert.IsTrue(ECDsa.Verify(msgBytes, signature, pubKeyCompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature, keys.PubKeyCompressed, curve)); // Verifying concatenated signature / uncompressed Eth public key. - Assert.IsTrue(ECDsa.Verify(msgBytes, signature, pubKeyUncompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signature, keys.PubKeyUncompressed, curve)); // Verifying DER signature (unsupported). - Assert.IsFalse(ECDsa.Verify(msgBytes, signatureDER, pubKeyCompressed, curve)); + Assert.IsFalse(ECDsa.Verify(msgBytes, signatureDER, keys.PubKeyCompressed, curve)); var signatureConvertedBack = ECDsaHelpers.FromDER(signatureDER); var signatureConvertedBackHex = Base16.Encode(signatureConvertedBack); @@ -211,24 +259,55 @@ private void ECDsaTest(ECDsaCurve curve, string pkHex, string message, string si Assert.AreEqual(signatureHex, signatureConvertedBackHex); // Verifying DER signature. - Assert.IsTrue(ECDsa.Verify(msgBytes, signatureConvertedBack, pubKeyCompressed, curve)); + Assert.IsTrue(ECDsa.Verify(msgBytes, signatureConvertedBack, keys.PubKeyCompressed, curve)); } - [UnityTest] + private void ECDsaTest(ECDsaCurve curve, + string pkHex, + string message, string signatureReference = null) + { + ECDsaTest(curve, + Keys.NewKeys(curve, false, pkHex), + message, + signatureReference); + + ECDsaTest(curve, + Keys.NewKeys(curve, true, pkHex), + message, + signatureReference); + } + + [UnityTest] public IEnumerator ECDsaSecP256k1_Mixed() { // Eth address: 0x66571c32d77c4852be4c282eb952ba94efbeac20 - ECDsaTest(ECDsaCurve.Secp256k1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "Phantasma"); - ECDsaTest(ECDsaCurve.Secp256r1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "Phantasma"); - ECDsaTest(ECDsaCurve.Secp256k1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "test message"); - ECDsaTest(ECDsaCurve.Secp256r1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "test message"); - // Eth address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a - ECDsaTest(ECDsaCurve.Secp256k1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "Phantasma"); - ECDsaTest(ECDsaCurve.Secp256r1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "Phantasma"); - ECDsaTest(ECDsaCurve.Secp256k1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "test message", "55DEB9E4D985834192AB8298C3DDA18EB7082C2A744EBDF7233D0A93FB00A4A9F4750F4A6F3FB3928C28690BE3A2BE52DEB95E1935E960FACBF7CC4AC4FDADCB"); + ECDsaTest(ECDsaCurve.Secp256k1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", + "Phantasma"); + ECDsaTest(ECDsaCurve.Secp256r1, + "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", + "Phantasma"); + ECDsaTest(ECDsaCurve.Secp256k1, + "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", + "test message"); + ECDsaTest(ECDsaCurve.Secp256r1, + "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", + "test message"); - - ECDsaTest(ECDsaCurve.Secp256k1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + + // Eth address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a + ECDsaTest(ECDsaCurve.Secp256k1, + "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", + "Phantasma"); + ECDsaTest(ECDsaCurve.Secp256r1, + "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", + "Phantasma"); + ECDsaTest(ECDsaCurve.Secp256k1, + "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", + "test message", + "55DEB9E4D985834192AB8298C3DDA18EB7082C2A744EBDF7233D0A93FB00A4A9F4750F4A6F3FB3928C28690BE3A2BE52DEB95E1935E960FACBF7CC4AC4FDADCB"); + + + ECDsaTest(ECDsaCurve.Secp256k1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", +"I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + "Phantasma address: P2KHhbVZWDv1ZLLoJccN3PUAb9x9BqRnUyH3ZEhu5YwBeJQ\n" + "Ethereum address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a\n" + "Ethereum public key: 5D3F7F469803C68C12B8F731576C74A9B5308484FD3B425D87C35CAED0A2E398C7AC626D916A1D65E23F673A55E6B16FFC1ABD673F3EF6AE8D5E6A0F99784A56\n" + From 5d1cd365fb8785f7af2d20b3bd6e98016cc38375 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 16:47:15 -0300 Subject: [PATCH 35/42] Crypto code refactoring --- .../src/Cryptography/CryptoExtensions.cs | 19 ++----------------- .../src/Cryptography/Hashing/SHA256.cs | 6 +++--- .../Poltergeist.Extensions/CryptoUtils.cs | 8 +------- .../Wallet/WalletGUIFolder/WalletGUI.cs | 4 ++-- Assets/Tests/CryptoTests.cs | 2 +- 5 files changed, 9 insertions(+), 30 deletions(-) diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/CryptoExtensions.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/CryptoExtensions.cs index 7f35396..6409690 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/CryptoExtensions.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/CryptoExtensions.cs @@ -2,18 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading; -using Phantasma.Core.Cryptography.Hashing; using Phantasma.Core.Numerics; using Phantasma.Core.Utils; -using SHA256 = System.Security.Cryptography.SHA256; namespace Phantasma.Core.Cryptography { public static class CryptoExtensions { - private static ThreadLocal _sha256 = new ThreadLocal(() => SHA256.Create()); - public static byte[] AESGenerateIV(int vectorSize) { var ivBytes = new byte[vectorSize]; @@ -69,16 +64,6 @@ public static string Base58CheckEncode(this byte[] data) return Base58.Encode(buffer); } - public static byte[] Sha256(this IEnumerable value) - { - return _sha256.Value.ComputeHash(value.ToArray()); - } - - public static byte[] Sha256(this byte[] value, int offset, int count) - { - return _sha256.Value.ComputeHash(value, offset, count); - } - public static byte[] Sha256(this string value) { var bytes = Encoding.UTF8.GetBytes(value); @@ -87,12 +72,12 @@ public static byte[] Sha256(this string value) public static byte[] Sha256(this byte[] value) { - return new Hashing.SHA256().ComputeHash(value, 0, (uint)value.Length); + return Hashing.SHA256.ComputeHash(value, 0, (uint)value.Length); } public static byte[] Sha256(this byte[] value, uint offset, uint count) { - return new Hashing.SHA256().ComputeHash(value, offset, count); + return Hashing.SHA256.ComputeHash(value, offset, count); } } } diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/Hashing/SHA256.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/Hashing/SHA256.cs index da04604..f35b511 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/Hashing/SHA256.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/Hashing/SHA256.cs @@ -2,14 +2,14 @@ namespace Phantasma.Core.Cryptography.Hashing { - public class SHA256 + public static class SHA256 { - public byte[] ComputeHash(byte[] data) + public static byte[] ComputeHash(byte[] data) { return ComputeHash(data, 0, (uint)data.Length); } - public byte[] ComputeHash(byte[] data, uint offset, uint length) + public static byte[] ComputeHash(byte[] data, uint offset, uint length) { var digest = new Sha256Digest(); var output = new byte[digest.GetDigestSize()]; diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs index 82f752f..e4f3dc0 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs @@ -18,7 +18,7 @@ public enum SignatureFormat public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve curve) { - var messageHash = Sha256Hash(message); + var messageHash = Phantasma.Core.Cryptography.Hashing.SHA256.ComputeHash(message); var privateKeyParameters = ECDsaHelpers.GetECPrivateKeyParameters(curve, prikey); @@ -32,11 +32,5 @@ public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve return R.Concat(S).ToArray(); } - - public static byte[] Sha256Hash(byte[] message) - { - var sha256 = new Phantasma.Core.Cryptography.Hashing.SHA256(); - return sha256.ComputeHash(message); - } } } diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index ce8ab5b..be2238a 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -3414,7 +3414,7 @@ private void DoAccountManagementMenu(int btnOffset) var messageBytes = System.Text.Encoding.ASCII.GetBytes(message); - var hash = Base16.Encode(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(messageBytes)); + var hash = Base16.Encode(Phantasma.Core.Cryptography.Hashing.SHA256.ComputeHash(messageBytes)); if (accountManager.Settings.devMode) { Log.Write($"Signed message: '{message}', hash: '{hash}'"); @@ -3493,7 +3493,7 @@ private void DoAccountManagementMenu(int btnOffset) if (accountManager.Settings.devMode) { - var hash = Base16.Encode(Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(messageBytes)); + var hash = Base16.Encode(Phantasma.Core.Cryptography.Hashing.SHA256.ComputeHash(messageBytes)); Log.WriteRaw($"Verified message: '{message}'"); Log.WriteRaw("\n\n"); Log.Write($"Hash: '{hash}'"); diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index 86d72dc..cb4bfc7 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -221,7 +221,7 @@ private void ECDsaTest(ECDsaCurve curve, var msgBytes = Encoding.ASCII.GetBytes(message); - var hash = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.Sha256Hash(msgBytes); + var hash = Phantasma.Core.Cryptography.Hashing.SHA256.ComputeHash(msgBytes); Debug.Log("Message hash: " + Base16.Encode(hash)); var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, keys.PrivKey, curve); From 1b6764b4af0f9cb577d5732672b852ae8f2ad9c8 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Thu, 19 Sep 2024 16:53:10 -0300 Subject: [PATCH 36/42] Crypto code refactoring --- .../src/Cryptography/ECDsa/ECDsa.cs | 19 ++++++++++ .../Poltergeist.Extensions/CryptoUtils.cs | 36 ------------------- .../CryptoUtils.cs.meta | 11 ------ .../Ethereum/KeyPair.cs | 3 +- .../Scripts/Wallet/ProofOfAddressesSigner.cs | 4 +-- .../Wallet/WalletGUIFolder/WalletGUI.cs | 4 +-- Assets/Tests/CryptoTests.cs | 4 +-- 7 files changed, 26 insertions(+), 55 deletions(-) delete mode 100644 Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs delete mode 100644 Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs.meta diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs index 258f99c..0cb806b 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs @@ -1,6 +1,8 @@ using System; using System.Linq; +using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; @@ -71,6 +73,23 @@ public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve) return ECDsaHelpers.FromDER(signature); } + public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve curve) + { + var messageHash = Hashing.SHA256.ComputeHash(message); + + var privateKeyParameters = ECDsaHelpers.GetECPrivateKeyParameters(curve, prikey); + + var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest())); + + signer.Init(true, privateKeyParameters); + + var RS = signer.GenerateSignature(messageHash); + var R = RS[0].ToByteArrayUnsigned(); + var S = RS[1].ToByteArrayUnsigned(); + + return R.Concat(S).ToArray(); + } + public static bool Verify(byte[] message, byte[] signature, byte[] pubkey, ECDsaCurve curve) { var signer = SignerUtilities.GetSigner("SHA256withECDSA"); diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs deleted file mode 100644 index e4f3dc0..0000000 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Linq; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Signers; -using Org.BouncyCastle.Security; -using Phantasma.Core.Cryptography.ECDsa; - -namespace Poltergeist.PhantasmaLegacy.Cryptography -{ - public static class CryptoUtils - { - public enum SignatureFormat - { - None, - Concatenated, - DEREncoded - } - - public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve curve) - { - var messageHash = Phantasma.Core.Cryptography.Hashing.SHA256.ComputeHash(message); - - var privateKeyParameters = ECDsaHelpers.GetECPrivateKeyParameters(curve, prikey); - - var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest())); - - signer.Init(true, privateKeyParameters); - - var RS = signer.GenerateSignature(messageHash); - var R = RS[0].ToByteArrayUnsigned(); - var S = RS[1].ToByteArrayUnsigned(); - - return R.Concat(S).ToArray(); - } - } -} diff --git a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs.meta b/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs.meta deleted file mode 100644 index a362fd4..0000000 --- a/Assets/Phantasma/Poltergeist.Extensions/CryptoUtils.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 1b4a7a8657ff1db42a9f361d312a3eff -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs b/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs index 0e97698..b3ed710 100644 --- a/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs +++ b/Assets/Phantasma/Poltergeist.Extensions/Ethereum/KeyPair.cs @@ -1,7 +1,6 @@ -using Phantasma.Core.Cryptography; +using Phantasma.Core.Cryptography; using Phantasma.Core.Cryptography.ECDsa; using Phantasma.Core.Numerics; -using Poltergeist.PhantasmaLegacy.Cryptography; using Poltergeist.PhantasmaLegacy.Ethereum.Hex.HexConvertors.Extensions; using Poltergeist.PhantasmaLegacy.Ethereum.Util; using System; diff --git a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs index 6063208..7f8659f 100644 --- a/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs +++ b/Assets/Scripts/Wallet/ProofOfAddressesSigner.cs @@ -48,12 +48,12 @@ public string GenerateSignedMessage() message += "\n\nPhantasma signature: " + Base16.Encode(((Ed25519Signature)phaSignature).Bytes); { - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, EthKeys.PrivateKey, ECDsaCurve.Secp256k1); + var signatureBytes = ECDsa.SignDeterministic(messageBytes, EthKeys.PrivateKey, ECDsaCurve.Secp256k1); message += "\nEthereum signature: " + Base16.Encode(signatureBytes); } { - var signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, NeoKeys.PrivateKey, ECDsaCurve.Secp256r1); + var signatureBytes = ECDsa.SignDeterministic(messageBytes, NeoKeys.PrivateKey, ECDsaCurve.Secp256r1); message += "\nNeo Legacy signature: " + Base16.Encode(signatureBytes); } diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index be2238a..d6904fb 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -3432,12 +3432,12 @@ private void DoAccountManagementMenu(int btnOffset) else if (chain == "Ethereum") { var keys = EthereumKey.FromWIF(wif); - signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, keys.PrivateKey, ECDsaCurve.Secp256k1); + signatureBytes = ECDsa.SignDeterministic(messageBytes, keys.PrivateKey, ECDsaCurve.Secp256k1); } else if (chain == "Neo Legacy") { var keys = NeoKeys.FromWIF(wif); - signatureBytes = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(messageBytes, keys.PrivateKey, ECDsaCurve.Secp256r1); + signatureBytes = ECDsa.SignDeterministic(messageBytes, keys.PrivateKey, ECDsaCurve.Secp256r1); } else { diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index cb4bfc7..b446d23 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -135,7 +135,7 @@ public IEnumerator ECDsaSecP256k1_Deterministic() var msgBytes = Encoding.ASCII.GetBytes("Phantasma"); var signature = ethKeys.Sign(msgBytes, (message, prikey, pubkey) => { - return Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(message, prikey, ECDsaCurve.Secp256k1); + return ECDsa.SignDeterministic(message, prikey, ECDsaCurve.Secp256k1); }); var ecdsaSignature = (ECDsaSignature)signature; @@ -224,7 +224,7 @@ private void ECDsaTest(ECDsaCurve curve, var hash = Phantasma.Core.Cryptography.Hashing.SHA256.ComputeHash(msgBytes); Debug.Log("Message hash: " + Base16.Encode(hash)); - var signature = Poltergeist.PhantasmaLegacy.Cryptography.CryptoUtils.SignDeterministic(msgBytes, keys.PrivKey, curve); + var signature = ECDsa.SignDeterministic(msgBytes, keys.PrivKey, curve); var signatureHex = Base16.Encode(signature); var signature2 = ECDsa.Sign(msgBytes, keys.PrivKey, curve); From ddb214277099a01675fa866422dcd32b4f33a6d4 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Fri, 20 Sep 2024 01:18:51 -0300 Subject: [PATCH 37/42] Replace faulty FromDER() implementation with alternative --- .../src/Cryptography/ECDsa/ECDsaHelpers.cs | 49 ++++++++++++++----- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs index 0840b44..ba0fd82 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsaHelpers.cs @@ -10,18 +10,45 @@ namespace Phantasma.Core.Cryptography.ECDsa { public static class ECDsaHelpers { - public static byte[] FromDER(byte[] signature) + public static byte[] FromDER(byte[] derSignature, int outputLength = 64) { - using var decoder = new Asn1InputStream(signature); - var seq = decoder.ReadObject() as DerSequence; - if (seq == null || seq.Count != 2) - throw new FormatException("Invalid DER Signature"); - var R = ((DerInteger)seq[0]).Value.ToByteArrayUnsigned(); - var S = ((DerInteger)seq[1]).Value.ToByteArrayUnsigned(); - - byte[] concatenated = new byte[R.Length + S.Length]; - Buffer.BlockCopy(R, 0, concatenated, 0, R.Length); - Buffer.BlockCopy(S, 0, concatenated, R.Length, S.Length); + if (derSignature.Length < 8 || derSignature[0] != 48) throw new Exception("Invalid ECDSA signature format"); + + int offset; + if (derSignature[1] > 0) + offset = 2; + else if (derSignature[1] == 0x81) + offset = 3; + else + throw new Exception("Invalid ECDSA signature format"); + + var rLength = derSignature[offset + 1]; + + int i = rLength; + while (i > 0 + && derSignature[offset + 2 + rLength - i] == 0) + i--; + + var sLength = derSignature[offset + 2 + rLength + 1]; + + int j = sLength; + while (j > 0 + && derSignature[offset + 2 + rLength + 2 + sLength - j] == 0) + j--; + + var rawLen = Math.Max(i, j); + rawLen = Math.Max(rawLen, outputLength / 2); + + if ((derSignature[offset - 1] & 0xff) != derSignature.Length - offset + || (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength + || derSignature[offset] != 2 + || derSignature[offset + 2 + rLength] != 2) + throw new Exception("Invalid ECDSA signature format"); + + var concatenated = new byte[2 * rawLen]; + + Array.Copy(derSignature, offset + 2 + rLength - i, concatenated, rawLen - i, i); + Array.Copy(derSignature, offset + 2 + rLength + 2 + sLength - j, concatenated, 2 * rawLen - j, j); return concatenated; } From 7ecf1e095ae59f7d806c0f560f7a47c462a4405a Mon Sep 17 00:00:00 2001 From: alexmpa Date: Fri, 20 Sep 2024 01:20:14 -0300 Subject: [PATCH 38/42] Improve unit tests --- Assets/Tests/CryptoTests.cs | 87 ++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/Assets/Tests/CryptoTests.cs b/Assets/Tests/CryptoTests.cs index b446d23..e1f5a67 100644 --- a/Assets/Tests/CryptoTests.cs +++ b/Assets/Tests/CryptoTests.cs @@ -16,6 +16,13 @@ namespace Phantasma.Tests { public class CryptoTests { + private string SignatureIncorrect1 = "45deb9e4d985834192ab8298c3dda18eb7082c2a744ebdf7233d0a93fb00a4a90b8af0b590c04c6d73d796f41c5d41abdbf57ecd795f3f40f3da92420b389376"; + private string SignatureIncorrect2 = "55deb9e4d985834192ab8298c3dda18eb7082c2a744ebcf7233d0a93fb00a4a90b8af0b590c04c6d73d796f41c5d41abdbf57ecd795f3f40f3da92420b389376"; + private string SignatureIncorrect3 = "55deb9e4d985834192ab8298c3dda18eb7082c2a744ebdf7233d0a93fb00a4a90b8af0b590c04c6d73d796f41c5d41abdbf57ecd000000000000000000000000"; + private string SignatureIncorrect4 = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + private string SignatureIncorrect5 = "7D375DEEB56530A8E09BB3F4AF9217F922FD3D33EBF02874239A2910E9DEF1BD25119CA641F13C6EBED1BFF4FEB7834F56723F9A9DCFC80B3128F1028B2C3A6B"; + + [UnityTest] public IEnumerator WifPkTest() { @@ -260,6 +267,13 @@ private void ECDsaTest(ECDsaCurve curve, // Verifying DER signature. Assert.IsTrue(ECDsa.Verify(msgBytes, signatureConvertedBack, keys.PubKeyCompressed, curve)); + + // Incorrect signatures + Assert.IsFalse(ECDsa.Verify(msgBytes, Base16.Decode(SignatureIncorrect1), keys.PubKeyCompressed, curve)); + Assert.IsFalse(ECDsa.Verify(msgBytes, Base16.Decode(SignatureIncorrect2), keys.PubKeyCompressed, curve)); + Assert.IsFalse(ECDsa.Verify(msgBytes, Base16.Decode(SignatureIncorrect3), keys.PubKeyCompressed, curve)); + Assert.IsFalse(ECDsa.Verify(msgBytes, Base16.Decode(SignatureIncorrect4), keys.PubKeyCompressed, curve)); + Assert.IsFalse(ECDsa.Verify(msgBytes, Base16.Decode(SignatureIncorrect5), keys.PubKeyCompressed, curve)); } private void ECDsaTest(ECDsaCurve curve, @@ -280,41 +294,46 @@ private void ECDsaTest(ECDsaCurve curve, [UnityTest] public IEnumerator ECDsaSecP256k1_Mixed() { - // Eth address: 0x66571c32d77c4852be4c282eb952ba94efbeac20 - ECDsaTest(ECDsaCurve.Secp256k1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", - "Phantasma"); - ECDsaTest(ECDsaCurve.Secp256r1, - "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", - "Phantasma"); - ECDsaTest(ECDsaCurve.Secp256k1, - "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", - "test message"); - ECDsaTest(ECDsaCurve.Secp256r1, - "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", - "test message"); - - // Eth address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a - ECDsaTest(ECDsaCurve.Secp256k1, - "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", - "Phantasma"); - ECDsaTest(ECDsaCurve.Secp256r1, - "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", + for (int i = 0; i < 10; i++) + { + // Eth address: 0x66571c32d77c4852be4c282eb952ba94efbeac20 + ECDsaTest(ECDsaCurve.Secp256k1, "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", "Phantasma"); - ECDsaTest(ECDsaCurve.Secp256k1, - "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", - "test message", - "55DEB9E4D985834192AB8298C3DDA18EB7082C2A744EBDF7233D0A93FB00A4A9F4750F4A6F3FB3928C28690BE3A2BE52DEB95E1935E960FACBF7CC4AC4FDADCB"); - - - ECDsaTest(ECDsaCurve.Secp256k1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", -"I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + -"Phantasma address: P2KHhbVZWDv1ZLLoJccN3PUAb9x9BqRnUyH3ZEhu5YwBeJQ\n" + -"Ethereum address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a\n" + -"Ethereum public key: 5D3F7F469803C68C12B8F731576C74A9B5308484FD3B425D87C35CAED0A2E398C7AC626D916A1D65E23F673A55E6B16FFC1ABD673F3EF6AE8D5E6A0F99784A56\n" + -"Neo Legacy address: Ae3aEA6CpvckvypAUShj2CLsy7sfynKUzj\n" + -"Neo Legacy public key: 183A301779007BF42DD7B5247587585B0524E13989F964C2A8E289A0CDC91F001765FCC3B4CEE5ED274C4A8B6D80978BDFED678210458CE264D4A4DAB3923EE6", -"E3E1FCD85385675F9E3508630570C545DECCD1241C7A8FFF523D2AC500D6F68745E43975DBF871C99504100B8DD6715F036FA51EFF9EB8B79D1E31FD555E78FC"); - + ECDsaTest(ECDsaCurve.Secp256r1, + "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", + "Phantasma"); + ECDsaTest(ECDsaCurve.Secp256k1, + "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", + "test message"); + ECDsaTest(ECDsaCurve.Secp256r1, + "6f6784731c4e526c97fa6a97b6f22e96f307588c5868bc2c545248bc31207eb1", + "test message"); + + // Eth address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a + ECDsaTest(ECDsaCurve.Secp256k1, + "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", + "Phantasma"); + + + ECDsaTest(ECDsaCurve.Secp256r1, + "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", + "Phantasma"); + + ECDsaTest(ECDsaCurve.Secp256k1, + "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", + "test message", + "55DEB9E4D985834192AB8298C3DDA18EB7082C2A744EBDF7233D0A93FB00A4A9F4750F4A6F3FB3928C28690BE3A2BE52DEB95E1935E960FACBF7CC4AC4FDADCB"); + + + ECDsaTest(ECDsaCurve.Secp256k1, "4ed773e5c8edc0487acef0011bc9ae8228287d4843f9d8477ff77c401ac59a49", + "I have signed this message with my Phantasma, Ethereum and Neo Legacy signatures to prove that following addresses belong to me and were derived from private key that belongs to me and to confirm my willingness to swap funds across these addresses upon my request. My public addresses are:\n" + + "Phantasma address: P2KHhbVZWDv1ZLLoJccN3PUAb9x9BqRnUyH3ZEhu5YwBeJQ\n" + + "Ethereum address: 0xDf738B927DA923fe0A5Fd3aD2192990C68913e6a\n" + + "Ethereum public key: 5D3F7F469803C68C12B8F731576C74A9B5308484FD3B425D87C35CAED0A2E398C7AC626D916A1D65E23F673A55E6B16FFC1ABD673F3EF6AE8D5E6A0F99784A56\n" + + "Neo Legacy address: Ae3aEA6CpvckvypAUShj2CLsy7sfynKUzj\n" + + "Neo Legacy public key: 183A301779007BF42DD7B5247587585B0524E13989F964C2A8E289A0CDC91F001765FCC3B4CEE5ED274C4A8B6D80978BDFED678210458CE264D4A4DAB3923EE6", + "E3E1FCD85385675F9E3508630570C545DECCD1241C7A8FFF523D2AC500D6F68745E43975DBF871C99504100B8DD6715F036FA51EFF9EB8B79D1E31FD555E78FC"); + } yield return null; } } From a2d26e18c8ccefa9e95f3114bb4357e135f52fc5 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Sat, 26 Oct 2024 22:07:10 -0300 Subject: [PATCH 39/42] Add logging --- Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index d6904fb..bc79eb3 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -3552,6 +3552,11 @@ private void DoAccountManagementMenu(int btnOffset) { if (result2 != PromptResult.Success) { + if (accountManager.Settings.devMode) + { + Log.Write($"Signed POA message (Base64): '{Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(message))}'"); + } + GUIUtility.systemCopyBuffer = message; MessageBox(MessageKind.Default, "Message copied to the clipboard."); } From 5f7da7e08dcb24143d8471c281e7a7528e2dcfe2 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Sat, 2 Nov 2024 18:48:58 -0300 Subject: [PATCH 40/42] Add button to send POA message to the server, add url configuration --- Assets/Scripts/Wallet/Settings.cs | 21 +++++++++++++++ .../WalletGUIFolder/WalletGUI.Settings.cs | 4 +++ .../Wallet/WalletGUIFolder/WalletGUI.cs | 27 +++++++++++++++---- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/Assets/Scripts/Wallet/Settings.cs b/Assets/Scripts/Wallet/Settings.cs index 1ceb12d..9605962 100644 --- a/Assets/Scripts/Wallet/Settings.cs +++ b/Assets/Scripts/Wallet/Settings.cs @@ -78,6 +78,7 @@ public class Settings public const string PhantasmaRPCTag = "settings.phantasma.rpc.url"; public const string PhantasmaExplorerTag = "settings.phantasma.explorer.url"; public const string PhantasmaNftExplorerTag = "settings.phantasma.nft.explorer.url"; + public const string PhantasmaPoaUrlTag = "settings.phantasma.poa.url"; public const string NexusNameTag = "settings.nexus.name"; public const string NexusKindTag = "settings.nexus.kind"; @@ -107,6 +108,7 @@ public class Settings public string phantasmaRPCURL; public string phantasmaExplorer; public string phantasmaNftExplorer; + public string phantasmaPoaUrl; public string nexusName; public string currency; public BigInteger feePrice; @@ -131,6 +133,7 @@ public override string ToString() "Phantasma RPC: " + this.phantasmaRPCURL + "\n" + "Phantasma Explorer: " + this.phantasmaExplorer + "\n" + "Phantasma NFT Explorer: " + this.phantasmaNftExplorer + "\n" + + "Phantasma POA URL: " + this.phantasmaPoaUrl + "\n" + "Fee price: " + this.feePrice + "\n" + "Fee limit: " + this.feeLimit + "\n" + "Nexus name: " + this.nexusName + "\n" + @@ -176,12 +179,14 @@ public void Load() // to avoid dealing with "stuck" values from old PG version that had different defaults. this.phantasmaExplorer = GetDefaultValue(PhantasmaExplorerTag); this.phantasmaNftExplorer = GetDefaultValue(PhantasmaNftExplorerTag); + this.phantasmaPoaUrl = GetDefaultValue(PhantasmaPoaUrlTag); this.nexusName = GetDefaultValue(NexusNameTag); } else { this.phantasmaExplorer = PlayerPrefs.GetString(PhantasmaExplorerTag, GetDefaultValue(PhantasmaExplorerTag)); this.phantasmaNftExplorer = PlayerPrefs.GetString(PhantasmaNftExplorerTag, GetDefaultValue(PhantasmaNftExplorerTag)); + this.phantasmaPoaUrl = PlayerPrefs.GetString(PhantasmaPoaUrlTag, GetDefaultValue(PhantasmaPoaUrlTag)); this.nexusName = PlayerPrefs.GetString(NexusNameTag, GetDefaultValue(NexusNameTag)); } @@ -320,6 +325,19 @@ public string GetDefaultValue(string tag) } break; + case PhantasmaPoaUrlTag: + switch (nexusKind) + { + case NexusKind.Main_Net: + _return_value = "https://poa.phantasma.info"; + break; + + default: + _return_value = "https://poa.phantasma.info"; + break; + } + break; + case NexusNameTag: switch (nexusKind) { @@ -351,6 +369,7 @@ public void Save() PlayerPrefs.SetString(PhantasmaRPCTag, this.phantasmaRPCURL); PlayerPrefs.SetString(PhantasmaExplorerTag, this.phantasmaExplorer); PlayerPrefs.SetString(PhantasmaNftExplorerTag, this.phantasmaNftExplorer); + PlayerPrefs.SetString(PhantasmaPoaUrlTag, this.phantasmaPoaUrl); PlayerPrefs.SetString(GasPriceTag, this.feePrice.ToString()); PlayerPrefs.SetString(GasLimitTag, this.feeLimit.ToString()); @@ -391,6 +410,7 @@ public void RestoreEndpoints(bool restoreName) this.phantasmaRPCURL = this.GetDefaultValue(PhantasmaRPCTag); this.phantasmaExplorer = this.GetDefaultValue(PhantasmaExplorerTag); this.phantasmaNftExplorer = this.GetDefaultValue(PhantasmaNftExplorerTag); + this.phantasmaPoaUrl = this.GetDefaultValue(PhantasmaPoaUrlTag); if (restoreName) { @@ -401,6 +421,7 @@ public void RestoreEndpoints(bool restoreName) " Phantasma RPC: " + this.phantasmaRPCURL + "\n" + " Phantasma Explorer: " + this.phantasmaExplorer + "\n" + " Phantasma NFT Explorer: " + this.phantasmaNftExplorer + "\n" + + " Phantasma POA URL: " + this.phantasmaPoaUrl + "\n" + " Nexus name: " + this.nexusName, Log.Level.Debug1); } diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs index a8fc005..1d98524 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.Settings.cs @@ -204,6 +204,10 @@ private void DoSettingsScreen() GUI.Label(new Rect(posX, curY, labelWidth, labelHeight), "Phantasma NFT URL"); settings.phantasmaNftExplorer = GUI.TextField(new Rect(fieldX, curY, fieldWidth, Units(2)), settings.phantasmaNftExplorer); curY += Units(3); + + GUI.Label(new Rect(posX, curY, labelWidth, labelHeight), "Phantasma POA URL"); + settings.phantasmaPoaUrl = GUI.TextField(new Rect(fieldX, curY, fieldWidth, Units(2)), settings.phantasmaPoaUrl); + curY += Units(3); } else { diff --git a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs index bc79eb3..07b964f 100644 --- a/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs +++ b/Assets/Scripts/Wallet/WalletGUIFolder/WalletGUI.cs @@ -3548,17 +3548,34 @@ private void DoAccountManagementMenu(int btnOffset) if (result == PromptResult.Success) { var message = signer.GenerateSignedMessage(); - ShowModal("Signed proof of addresses", message, ModalState.Message, 0, 0, ModalOkCopy_NoAutoCopy, 0, (result2, input) => + ShowModal("Signed proof of addresses", message, ModalState.Message, 0, 0, ModalSendCancel, 0, (result2, input) => { - if (result2 != PromptResult.Success) + if (result2 == PromptResult.Success) { + var signedPoaBase64 = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(message)); if (accountManager.Settings.devMode) { - Log.Write($"Signed POA message (Base64): '{Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(message))}'"); + Log.Write($"Signed POA message (Base64): '{signedPoaBase64}'"); } - GUIUtility.systemCopyBuffer = message; - MessageBox(MessageKind.Default, "Message copied to the clipboard."); + var url = string.Format("{0}/{1}", accountManager.Settings.phantasmaPoaUrl.TrimEnd('/'), "api/v1/poa/register"); + + var jsonMessage = "{\"message\": \"" + signedPoaBase64 + "\"}"; + + StartCoroutine(Phantasma.SDK.WebClient.RESTRequest(url, jsonMessage, (error, msg) => + { + MessageBox(MessageKind.Error, "Error occured. Please try later."); + }, + (response) => + { + MessageBox(MessageKind.Default, "Message sent."); + })); + + if (accountManager.Settings.devMode) + { + GUIUtility.systemCopyBuffer = message; + MessageBox(MessageKind.Default, "Message copied to the clipboard."); + } } }); } From 928113bc6164dbb740e6f68ce7215f345875910a Mon Sep 17 00:00:00 2001 From: alexmpa Date: Sat, 2 Nov 2024 18:49:28 -0300 Subject: [PATCH 41/42] Upgrade to Unity 2022.3.51f1 --- ProjectSettings/ProjectVersion.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt index 1e3d799..de82503 100644 --- a/ProjectSettings/ProjectVersion.txt +++ b/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2022.3.46f1 -m_EditorVersionWithRevision: 2022.3.46f1 (8e9b8558c41a) +m_EditorVersion: 2022.3.51f1 +m_EditorVersionWithRevision: 2022.3.51f1 (9f9d16c45e54) From 52f21d0a26c3ccec01e5c99d17ffac50bc570432 Mon Sep 17 00:00:00 2001 From: alexmpa Date: Sat, 2 Nov 2024 20:50:55 -0300 Subject: [PATCH 42/42] Fix ECDSA deterministic signature compatibility with eth implementations --- .../Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs index 0cb806b..31f52dd 100644 --- a/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs +++ b/Assets/Phantasma/Phantasma.Core/src/Cryptography/ECDsa/ECDsa.cs @@ -73,6 +73,15 @@ public static byte[] Sign(byte[] message, byte[] prikey, ECDsaCurve curve) return ECDsaHelpers.FromDER(signature); } + private static byte[] ByteArrayLeftPad(byte[] sourceArray, byte padValue, int len) + { + if(sourceArray.Length == len) + return sourceArray; + + var prefixArray = Enumerable.Repeat(padValue, len - sourceArray.Length).ToArray(); + return prefixArray.Concat(sourceArray).ToArray(); + } + public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve curve) { var messageHash = Hashing.SHA256.ComputeHash(message); @@ -84,8 +93,8 @@ public static byte[] SignDeterministic(byte[] message, byte[] prikey, ECDsaCurve signer.Init(true, privateKeyParameters); var RS = signer.GenerateSignature(messageHash); - var R = RS[0].ToByteArrayUnsigned(); - var S = RS[1].ToByteArrayUnsigned(); + var R = ByteArrayLeftPad(RS[0].ToByteArrayUnsigned(), 0, 32); + var S = ByteArrayLeftPad(RS[1].ToByteArrayUnsigned(), 0, 32); return R.Concat(S).ToArray(); }