From 307d07e80d189c98b2a73a29ee2a5249225a7d02 Mon Sep 17 00:00:00 2001 From: Kamil Sobonski <39964811+SucharMistrz@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:11:25 +0200 Subject: [PATCH] Bugfix: Inconsistent EDPoint representation This branch fixes 2 issues related to EDPoint representation. First and foremost, it addresses an issue with the modulo P operations missing in some cases and, as a result, the parameter T being inconsistently calculated. Secondly, deriving an ED25519PublicKey from an ED25519PrivateKey now ensures the underlying EDPoint is scaled to affine coordinates, allowing us to easily compare any actually derived ED25519PublicKey with the expected results of EDPoint.fromBytes(). List of changes: - updated ed25519_private_key.dart, so that publicKey getter always constructs the public key from an EDPoint scaled to affine coordinates, ensuring consistency with the EDPoint form produced by EDPoint.fromBytes() - updated ed_point.dart, adding modulo P operations where they were missing, which also ensures consistency of the fromBytes() default constructor with the EDPoint form produced by scaleToAffineCoordinates(), as well as fixing an incorrect value for the parameter T in * operator - unrelated with the domain: fixed solana-specific ed25519_derivator_test.dart using Ethereum derivation path in tests --- .../eddsa/ed25519/ed25519_private_key.dart | 2 +- lib/src/cdsa/eddsa/ed_point.dart | 22 ++--- pubspec.yaml | 2 +- .../ed25519_derivator_test.dart | 93 +++++++------------ .../hd_wallet/legacy_hd_wallet_test.dart | 8 +- .../ed25519/ed25519_private_key_test.dart | 8 +- test/cdsa/eddsa/ed_point_test.dart | 2 +- 7 files changed, 58 insertions(+), 79 deletions(-) diff --git a/lib/src/cdsa/eddsa/ed25519/ed25519_private_key.dart b/lib/src/cdsa/eddsa/ed25519/ed25519_private_key.dart index 66077630..a10bc893 100644 --- a/lib/src/cdsa/eddsa/ed25519/ed25519_private_key.dart +++ b/lib/src/cdsa/eddsa/ed25519/ed25519_private_key.dart @@ -27,7 +27,7 @@ class ED25519PrivateKey extends ABip32PrivateKey { @override ED25519PublicKey get publicKey { return ED25519PublicKey( - edPublicKey: edPrivateKey.edPublicKey, + edPublicKey: EDPublicKey(edPrivateKey.edPublicKey.A.scaleToAffineCoordinates()), metadata: metadata, ); } diff --git a/lib/src/cdsa/eddsa/ed_point.dart b/lib/src/cdsa/eddsa/ed_point.dart index e3b2914b..44c1e3da 100644 --- a/lib/src/cdsa/eddsa/ed_point.dart +++ b/lib/src/cdsa/eddsa/ed_point.dart @@ -75,7 +75,7 @@ class EDPoint extends Equatable { }) : x = x ?? BigInt.zero, y = y ?? BigInt.zero, z = z ?? BigInt.zero, - t = t ?? ((x ?? BigInt.zero) * (y ?? BigInt.zero)); + t = t ?? ((x ?? BigInt.zero) * (y ?? BigInt.zero)) % curve.p; /// Constructs an instance of EDPoint from a byte array. factory EDPoint.fromBytes(EDPoint generator, Uint8List bytes) { @@ -91,15 +91,15 @@ class EDPoint extends Equatable { int x0 = (editableBytes[expLen - 1] & 0x80) >> 7; editableBytes[expLen - 1] &= 0x80 - 1; - BigInt y = BigIntUtils.decode(editableBytes, order: Endian.little); + BigInt y = BigIntUtils.decode(editableBytes, order: Endian.little) % p; - BigInt x2 = (y * y - BigInt.from(1)) * (curve.d * y * y - curve.a).modInverse(p) % p; + BigInt x2 = (((y * y) % p - BigInt.one) % p) * (((curve.d * ((y * y) % p)) % p - curve.a) % p).modInverse(p) % p; BigInt x = ED25519Utils.findModularSquareRoot(a: x2, p: p); if (x.isOdd != (x0 == 1)) { x = (-x) % p; } - return EDPoint(curve: curve, n: generator.n, x: x, y: y, z: BigInt.one, t: x * y); + return EDPoint(curve: curve, n: generator.n, x: x, y: y, z: BigInt.one, t: (x * y) % p); } /// Constructs an instance of [EDPoint] representing the point at infinity. @@ -143,10 +143,10 @@ class EDPoint extends Equatable { BigInt b = (y * other.y) % curve.p; BigInt c = (z * other.t) % curve.p; BigInt d = (t * other.z) % curve.p; - BigInt e = d + c; + BigInt e = (d + c) % curve.p; BigInt f = (((x - y) * (other.x + other.y)) + b - A) % curve.p; - BigInt g = b + (curve.a * A); - BigInt h = d - c; + BigInt g = (b + (curve.a * A)) % curve.p; + BigInt h = (d - c) % curve.p; if (h == BigInt.zero) { return _double(); @@ -188,7 +188,7 @@ class EDPoint extends Equatable { x: BigInt.zero, y: BigInt.one, z: BigInt.one, - t: BigInt.one, + t: BigInt.zero, ); List nafList = BigIntUtils.computeNAF(modScalar).reversed.toList(); @@ -245,9 +245,9 @@ class EDPoint extends Equatable { BigInt C = (z * z * BigInt.two) % curve.p; BigInt D = (curve.a * A) % curve.p; BigInt E = (((x + y) * (x + y)) - A - B) % curve.p; - BigInt G = D + B; - BigInt F = G - C; - BigInt H = D - B; + BigInt G = (D + B) % curve.p; + BigInt F = (G - C) % curve.p; + BigInt H = (D - B) % curve.p; BigInt x3 = (E * F) % curve.p; BigInt y3 = (G * H) % curve.p; BigInt t3 = (E * H) % curve.p; diff --git a/pubspec.yaml b/pubspec.yaml index cf6d0119..4288430e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: cryptography_utils description: "Dart package containing utility methods for common cryptographic and blockchain-specific operations" publish_to: none -version: 0.0.27 +version: 0.0.28 environment: sdk: ">=3.2.6" diff --git a/test/bip/bip32/derivators/legacy_derivators/ed25519_derivator_test.dart b/test/bip/bip32/derivators/legacy_derivators/ed25519_derivator_test.dart index 0a82204d..099381a1 100644 --- a/test/bip/bip32/derivators/legacy_derivators/ed25519_derivator_test.dart +++ b/test/bip/bip32/derivators/legacy_derivators/ed25519_derivator_test.dart @@ -54,30 +54,30 @@ void main() { expect(actualED25519PrivateKey, expectedED25519PrivateKey); }); - test("Should [return ED25519PrivateKey] constructed from mnemonic and derivation path (m/44'/60'/)", () async { + test("Should [return ED25519PrivateKey] constructed from mnemonic and derivation path (m/44'/501'/)", () async { // Act - LegacyDerivationPath actualLegacyDerivationPath = LegacyDerivationPath.parse("m/44'/60'/"); + LegacyDerivationPath actualLegacyDerivationPath = LegacyDerivationPath.parse("m/44'/501'/"); ED25519PrivateKey actualED25519PrivateKey = await actualED25519Derivator.derivePath(actualMnemonic, actualLegacyDerivationPath); // Assert ED25519PrivateKey expectedED25519PrivateKey = ED25519PrivateKey( metadata: Bip32KeyMetadata( depth: 2, - shiftedIndex: 2147483708, - chainCode: base64Decode('AlYCSjYOCo//7XisF+s9f+4uREPjJlQ3lZVRypceTI0='), - fingerprint: BigInt.parse('4278372777'), + shiftedIndex: 2147484149, + chainCode: base64Decode('N8hqS/GQMbS0QaVEk75FmKA6yPFRa2XC5x+MWTMYSsg='), + fingerprint: BigInt.parse('4036075356'), parentFingerprint: BigInt.parse('2330465125'), masterFingerprint: BigInt.parse('3578578273'), ), - edPrivateKey: EDPrivateKey.fromBytes(base64Decode('dZiZCf9yd0YSUxbInwyamtkndKTTRj6j+G6xmj928vY=')), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('/TWsTCOLvsuX0xrFMzD3yXZSjCQViIGf6hWaevc1vOY=')), ); expect(actualED25519PrivateKey, expectedED25519PrivateKey); }); - test("Should [return ED25519PrivateKey] constructed from mnemonic and derivation path (m/44'/60'/0'/)", () async { + test("Should [return ED25519PrivateKey] constructed from mnemonic and derivation path (m/44'/501'/0'/)", () async { // Act - LegacyDerivationPath actualLegacyDerivationPath = LegacyDerivationPath.parse("m/44'/60'/0'/"); + LegacyDerivationPath actualLegacyDerivationPath = LegacyDerivationPath.parse("m/44'/501'/0'/"); ED25519PrivateKey actualED25519PrivateKey = await actualED25519Derivator.derivePath(actualMnemonic, actualLegacyDerivationPath); // Assert @@ -85,20 +85,20 @@ void main() { metadata: Bip32KeyMetadata( depth: 3, shiftedIndex: 2147483648, - chainCode: base64Decode('XJIq3dw+4wLO363ghHmYr8iBf0sSpDC1SsJGbG6BMxM='), - fingerprint: BigInt.parse('4237045580'), - parentFingerprint: BigInt.parse('4278372777'), + chainCode: base64Decode('LxPIxnNe9YXF9o6sstwDQavHXPdE7WUF2cKUqxS+MTc='), + fingerprint: BigInt.parse('753771646'), + parentFingerprint: BigInt.parse('4036075356'), masterFingerprint: BigInt.parse('3578578273'), ), - edPrivateKey: EDPrivateKey.fromBytes(base64Decode('dnfNSsX9wTWBvMXva/SUqWt3iRaK+oH68fM9Feg/SIs=')), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('T3WAkMPu1mLHuRW+Dra0RFDDclbr6L6zJw6wPkpPJYA=')), ); expect(actualED25519PrivateKey, expectedED25519PrivateKey); }); - test("Should [return ED25519PrivateKey] constructed from mnemonic and derivation path (m/44'/60'/0'/0'/)", () async { + test("Should [return ED25519PrivateKey] constructed from mnemonic and derivation path (m/44'/501'/0'/0'/)", () async { // Act - LegacyDerivationPath actualLegacyDerivationPath = LegacyDerivationPath.parse("m/44'/60'/0'/0'/"); + LegacyDerivationPath actualLegacyDerivationPath = LegacyDerivationPath.parse("m/44'/501'/0'/0'/"); ED25519PrivateKey actualED25519PrivateKey = await actualED25519Derivator.derivePath(actualMnemonic, actualLegacyDerivationPath); // Assert @@ -106,54 +106,33 @@ void main() { metadata: Bip32KeyMetadata( depth: 4, shiftedIndex: 2147483648, - chainCode: base64Decode('7VKKNJkj9ZEp7SX+GveAvvcZhi+8NJNrSG98+BNgjxY='), - fingerprint: BigInt.parse('2130523803'), - parentFingerprint: BigInt.parse('4237045580'), + chainCode: base64Decode('bJz0NEJLEZUTTobbklJ8hYb9gCS+4J7UGPMtDyO+IDY='), + fingerprint: BigInt.parse('635615273'), + parentFingerprint: BigInt.parse('753771646'), masterFingerprint: BigInt.parse('3578578273'), ), - edPrivateKey: EDPrivateKey.fromBytes(base64Decode('Dy1S7AO7gPWC+vO7mV/cwTi9sjJ56abeYtD8s3qRTAk=')), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('iGjr84MoC1+WAY4cnAFqETc5KjMEtZ7CfAYvxkd877M=')), ); expect(actualED25519PrivateKey, expectedED25519PrivateKey); }); - test("Should [return ED25519PrivateKey] constructed from mnemonic and derivation path (m/44'/60'/0'/0'/0')", () async { + test("Should [return ED25519PrivateKey] constructed from mnemonic and derivation path (m/44'/501'/1'/0'/)", () async { // Act - LegacyDerivationPath actualLegacyDerivationPath = LegacyDerivationPath.parse("m/44'/60'/0'/0'/0'"); + LegacyDerivationPath actualLegacyDerivationPath = LegacyDerivationPath.parse("m/44'/501'/1'/0'/"); ED25519PrivateKey actualED25519PrivateKey = await actualED25519Derivator.derivePath(actualMnemonic, actualLegacyDerivationPath); // Assert ED25519PrivateKey expectedED25519PrivateKey = ED25519PrivateKey( metadata: Bip32KeyMetadata( - depth: 5, + depth: 4, shiftedIndex: 2147483648, - chainCode: base64Decode('hz6ve3vISMyVDK7ZD0zQoV2v4K1ota1QJ9kY1xakFR4='), - fingerprint: BigInt.parse('3808761756'), - parentFingerprint: BigInt.parse('2130523803'), + chainCode: base64Decode('pdvuHGZNYfgdrW/BAMpsBlNcM2xCYGKsT7j4ZymApJA='), + fingerprint: BigInt.parse('1931527399'), + parentFingerprint: BigInt.parse('429089605'), masterFingerprint: BigInt.parse('3578578273'), ), - edPrivateKey: EDPrivateKey.fromBytes(base64Decode('fbrqtRb364ZjYonX89pDhtlKE+4jFrxmpHGU2kmQaGs=')), - ); - - expect(actualED25519PrivateKey, expectedED25519PrivateKey); - }); - - test("Should [return ED25519PrivateKey] constructed from mnemonic and derivation path (m/44'/60'/0'/0'/1')", () async { - // Act - LegacyDerivationPath actualLegacyDerivationPath = LegacyDerivationPath.parse("m/44'/60'/0'/0'/1'"); - ED25519PrivateKey actualED25519PrivateKey = await actualED25519Derivator.derivePath(actualMnemonic, actualLegacyDerivationPath); - - // Assert - ED25519PrivateKey expectedED25519PrivateKey = ED25519PrivateKey( - metadata: Bip32KeyMetadata( - depth: 5, - shiftedIndex: 2147483649, - chainCode: base64Decode('l1JlMYKoERbLwsNQIZbHugLvqyxktb1J+O+zU5jue+k='), - fingerprint: BigInt.parse('2573384385'), - parentFingerprint: BigInt.parse('2130523803'), - masterFingerprint: BigInt.parse('3578578273'), - ), - edPrivateKey: EDPrivateKey.fromBytes(base64Decode('AVPH3U7LPhJ6E3PFI9S+Ek+vUM59I5RBk5uRG1nMIBw=')), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('2fcDuvPOTai9vU+A3d9xto81C5nkpEtawFntHXhyrts=')), ); expect(actualED25519PrivateKey, expectedED25519PrivateKey); @@ -215,19 +194,19 @@ void main() { expect(actualDerivedED25519PrivateKey, expectedDerivedED25519PrivateKey); }); - test("Should [return ED25519PrivateKey] derived from ED25519PrivateKey (m/44'/ -> m/44'/60'/)", () async { + test("Should [return ED25519PrivateKey] derived from ED25519PrivateKey (m/44'/ -> m/44'/501'/)", () async { // Arrange - LegacyDerivationPathElement actualDerivationPathElement = LegacyDerivationPathElement.parse("60'"); + LegacyDerivationPathElement actualDerivationPathElement = LegacyDerivationPathElement.parse("501'"); ED25519PrivateKey actualED25519PrivateKey = ED25519PrivateKey( metadata: Bip32KeyMetadata( depth: 1, shiftedIndex: 2147483692, - chainCode: base64Decode('oVTP3c7E2KeoquJttMLqsSV7zyzEbvACvVcFTjW2Cz4='), + chainCode: base64Decode('N8hqS/GQMbS0QaVEk75FmKA6yPFRa2XC5x+MWTMYSsg='), fingerprint: BigInt.parse('2330465125'), parentFingerprint: BigInt.parse('3578578273'), masterFingerprint: BigInt.parse('3578578273'), ), - edPrivateKey: EDPrivateKey.fromBytes(base64Decode('MinQRVP+LSjLX9pmmkDLcm01pJP8IVaKrlVAGlNXUbs=')), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('/TWsTCOLvsuX0xrFMzD3yXZSjCQViIGf6hWaevc1vOY=')), ); // Act @@ -237,19 +216,19 @@ void main() { ED25519PrivateKey expectedDerivedED25519PrivateKey = ED25519PrivateKey( metadata: Bip32KeyMetadata( depth: 2, - shiftedIndex: 2147483708, - chainCode: base64Decode('AlYCSjYOCo//7XisF+s9f+4uREPjJlQ3lZVRypceTI0='), - fingerprint: BigInt.parse('4278372777'), + shiftedIndex: 2147484149, + chainCode: base64Decode('nVpoaXRJzBmqw/3/DHi9EjRrEWOUiEilp5fbkw8aaTk='), + fingerprint: BigInt.parse('3862087306'), parentFingerprint: BigInt.parse('2330465125'), masterFingerprint: BigInt.parse('3578578273'), ), - edPrivateKey: EDPrivateKey.fromBytes(base64Decode('dZiZCf9yd0YSUxbInwyamtkndKTTRj6j+G6xmj928vY=')), + edPrivateKey: EDPrivateKey.fromBytes(base64Decode('/Fcp11uAxg7VyMEvkW5IPYuGDrk2bcxS8tZ8QvZgqaQ=')), ); expect(actualDerivedED25519PrivateKey, expectedDerivedED25519PrivateKey); }); - test("Should [return ED25519PrivateKey] derived from ED25519PrivateKey (m/44'/60'/ -> m/44'/60'/0'/)", () async { + test("Should [return ED25519PrivateKey] derived from ED25519PrivateKey (m/44'/501'/ -> m/44'/501'/0'/)", () async { // Arrange LegacyDerivationPathElement actualDerivationPathElement = LegacyDerivationPathElement.parse("0'"); ED25519PrivateKey actualED25519PrivateKey = ED25519PrivateKey( @@ -283,7 +262,7 @@ void main() { expect(actualDerivedED25519PrivateKey, expectedDerivedED25519PrivateKey); }); - test("Should [return ED25519PrivateKey] derived from ED25519PrivateKey (m/44'/60'/0'/ -> m/44'/60'/0'/0'/)", () async { + test("Should [return ED25519PrivateKey] derived from ED25519PrivateKey (m/44'/501'/0'/ -> m/44'/501'/0'/0'/)", () async { // Arrange LegacyDerivationPathElement actualDerivationPathElement = LegacyDerivationPathElement.parse("0'"); ED25519PrivateKey actualED25519PrivateKey = ED25519PrivateKey( @@ -317,7 +296,7 @@ void main() { expect(actualDerivedED25519PrivateKey, expectedDerivedED25519PrivateKey); }); - test("Should [return ED25519PrivateKey] derived from ED25519PrivateKey (m/44'/60'/0'/0'/ -> m/44'/60'/0'/0'/0'/)", () async { + test("Should [return ED25519PrivateKey] derived from ED25519PrivateKey (m/44'/501'/0'/0'/ -> m/44'/501'/0'/0'/0'/)", () async { // Arrange LegacyDerivationPathElement actualDerivationPathElement = LegacyDerivationPathElement.parse("0'"); ED25519PrivateKey actualED25519PrivateKey = ED25519PrivateKey( diff --git a/test/bip/bip32/hd_wallet/legacy_hd_wallet_test.dart b/test/bip/bip32/hd_wallet/legacy_hd_wallet_test.dart index 74214476..21c496f7 100644 --- a/test/bip/bip32/hd_wallet/legacy_hd_wallet_test.dart +++ b/test/bip/bip32/hd_wallet/legacy_hd_wallet_test.dart @@ -321,10 +321,10 @@ void main() { EDPoint( curve: Curves.ed25519, n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), - x: BigInt.parse('44770412612249132879508693710199042699271465674024261288602292914024751467771'), - y: BigInt.parse('9071192539003680880021133333155815940615935403600658841594844653366853384648'), - z: BigInt.parse('215655415489707601689906281986627164961686488479408878249048443374796070278'), - t: BigInt.parse('55926202586167076010672118601058589559146741453778469146658296279768153697900'), + x: BigInt.parse('2897232476953985954279425303677103468775948810819257722110548651944397154000'), + y: BigInt.parse('55864704347926684278745305761095583792533035912294806089133322461286120966785'), + z: BigInt.parse('1'), + t: BigInt.parse('22729427263460374807531776853364223631806892420209994431342720698762552415126'), ), ), ), diff --git a/test/cdsa/eddsa/ed25519/ed25519_private_key_test.dart b/test/cdsa/eddsa/ed25519/ed25519_private_key_test.dart index 71a181e7..94779479 100644 --- a/test/cdsa/eddsa/ed25519/ed25519_private_key_test.dart +++ b/test/cdsa/eddsa/ed25519/ed25519_private_key_test.dart @@ -119,10 +119,10 @@ void main() { EDPoint( curve: Curves.ed25519, n: BigInt.parse('7237005577332262213973186563042994240857116359379907606001950938285454250989'), - x: BigInt.parse('19793122580953396643657614893737675412715271732516190437477497872396293476517'), - y: BigInt.parse('11043535616153659670420426046812415327322207260394876224747641236324749531277'), - z: BigInt.parse('52307325976996590356746334614254516249809358280279038039749391123110162041889'), - t: BigInt.parse('18663140037355883143136513717086037971900193259991003799674694468591719114115'), + x: BigInt.parse('54065565874754690464541746979170565753169877194888053393545030920551520745988'), + y: BigInt.parse('3341297077934518673457142734077350879420471371059900657313532910747302736079'), + z: BigInt.parse('1'), + t: BigInt.parse('17889545404307743854678190178496636156195764849150499252736625982701909100111'), ), ), ); diff --git a/test/cdsa/eddsa/ed_point_test.dart b/test/cdsa/eddsa/ed_point_test.dart index 1680e841..6fec3120 100644 --- a/test/cdsa/eddsa/ed_point_test.dart +++ b/test/cdsa/eddsa/ed_point_test.dart @@ -21,7 +21,7 @@ void main() { y: BigInt.parse('21722763853247575927517928451216332729805032358530396811928860500080504648297'), z: BigInt.one, t: BigInt.parse( - '788462136826487035517522181628529984945326118901391734773631962347443138612680071187232142979650154875261951270767071733362165107888869070753013137283114'), + '35199835903149059728846105492536262642237786856927316283699659281752607508541'), ); expect(actualEDPoint, expectedEDPoint);