From d8baf4ddc40041d2c367919c7a3ddeb19a7cb9fb Mon Sep 17 00:00:00 2001
From: bitcookies
Date: Fri, 16 May 2025 15:47:02 +0800
Subject: [PATCH] =?UTF-8?q?=E2=9C=94=20Add=20an=20example=20of=20verifying?=
=?UTF-8?q?=20the=20point=20G?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.HOW_DOES_IT_WORK.md | 22 +----
README.HOW_DOES_IT_WORK.zh-CN.md | 2 +
README.VERIFY_Point_G.md | 155 +++++++++++++++++++++++++++++++
README.VERIFY_Point_G.zh-CN.md | 155 +++++++++++++++++++++++++++++++
4 files changed, 314 insertions(+), 20 deletions(-)
create mode 100644 README.VERIFY_Point_G.md
create mode 100644 README.VERIFY_Point_G.zh-CN.md
diff --git a/README.HOW_DOES_IT_WORK.md b/README.HOW_DOES_IT_WORK.md
index 02cfe7f..937d809 100644
--- a/README.HOW_DOES_IT_WORK.md
+++ b/README.HOW_DOES_IT_WORK.md
@@ -11,7 +11,6 @@ Elements in ground field . If we use
@@ -19,7 +18,6 @@ where each coefficients is in  in  can be denoted as
@@ -27,7 +25,6 @@ as the standard basis of the ground field, an element   is
@@ -37,7 +34,6 @@ The irreducible polynomial of composite field . If we use
@@ -45,7 +41,6 @@ where each coefficients is in  in   can be denoted as
@@ -53,7 +48,6 @@ as the standard basis of the composite field, an element  , which is a 255-bits-long integer to denote an element  in  . The map between them is
@@ -63,7 +57,6 @@ For clarity, we use 
-
## 2. Elliptic curve over  
The equation of the elliptic curve that WinRAR uses is
@@ -73,7 +66,6 @@ The equation of the elliptic curve that WinRAR uses is
-
The base point  is
@@ -81,6 +73,7 @@ The base point  of the base point  on the curve.
whose order  is
@@ -89,7 +82,6 @@ whose order . So the SHA1 value of  should be
@@ -107,7 +98,6 @@ to denote a message whose length is  are 5 state values when SHA1 outputs. Generally speaking, the final SHA1 value should be the join of these 5 state values while each of state values is serialized in big-endian.
However, WinRAR doesn't serialize the 5 state values. Instead, it use a big integer  as the hash of the input message.
@@ -117,7 +107,6 @@ However, WinRAR doesn't serialize the 5 state values. Instead, it use a big inte
-
## 4. ECC digital signature algorithm
We use  to denote private key,  to denote public key. So there must be
@@ -127,7 +116,6 @@ We use  to denote the hash of input data, WinRAR use the following algorithm to perform signing:
1. Generate a random big integer  which satisfies .
@@ -165,7 +153,6 @@ We use
-
to denote input data whose length is . WinRAR use it to generate private key .
1. We use  to denote 6 32-bits-long integer. So there is
@@ -184,7 +171,6 @@ to denote input data whose length is , we let
@@ -201,7 +187,6 @@ to denote input data whose length is  and donote it as .
5. Repeat step 4 again with 14 times.
@@ -222,7 +207,6 @@ Private key  is zero.
Public key  is
@@ -297,7 +281,6 @@ The following is the algorithm to generate `rarreg.key`.
-
Use the algorithm describled in section 4, with argument  and private key  describled section 6, to get signature .
The bit length of  and  shall not be more than 240. Otherwise, repeat this step.
@@ -320,9 +303,8 @@ The following is the algorithm to generate `rarreg.key`.
-
The final checksum the complement of CRC32 value.
-
+
Then convert the checksum to decimal string . If the length is less than 10, pad character `'0'` until the length is 10.
12. Let  be
diff --git a/README.HOW_DOES_IT_WORK.zh-CN.md b/README.HOW_DOES_IT_WORK.zh-CN.md
index 64ba641..c450908 100644
--- a/README.HOW_DOES_IT_WORK.zh-CN.md
+++ b/README.HOW_DOES_IT_WORK.zh-CN.md
@@ -73,6 +73,8 @@ WinRAR 使用了基于 ECC 的签名算法来生成 `rarreg.key` 文件,其使
+基点  在曲线上的[验证示例](README.VERIFY_Point_G.zh-CN.md)。
+
基点  的阶  为:
diff --git a/README.VERIFY_Point_G.md b/README.VERIFY_Point_G.md
new file mode 100644
index 0000000..a724fe2
--- /dev/null
+++ b/README.VERIFY_Point_G.md
@@ -0,0 +1,155 @@
+# Verifying point G lies on the curve
+
+## 1. Composite field, curves and point G
+
+Composite field is  .
+
+And the base field is  , i.e. each element in the domain is a `15-bit` number. The number of extensions is 17, so the entire composite domain element can be viewed as a 17-term polynomial, each term being .
+
+The equation of the elliptic curve that WinRAR uses is
+
+
+
+
+
+
+The base point  is
+
+
+
+
+
+
+## 2. Conversion point G
+
+Convert the point (in large integer format) into the required little-endian representation of `15-bit` segments for the field . A Python for converting the point  is as follows.
+
+```python
+def to_field_repr(val, bits=15, count=17):
+ mask = (1 << bits) - 1
+ result = []
+ for _ in range(count):
+ result.append(val & mask)
+ val >>= bits
+ return result
+
+def print_field(label, field_data):
+ print(f"{label} = to_field([")
+ for i in range(len(field_data)):
+ print(f" 0x{field_data[i]:04X},", end="\n" if (i + 1) % 4 == 0 else " ")
+ print("])")
+
+Gx_int = 0x56fdcbc6a27acee0cc2996e0096ae74feb1acf220a2341b898b549440297b8cc
+Gy_int = 0x20da32e8afc90b7cf0e76bde44496b4d0794054e6ea60f388682463132f931a7
+
+Gx_field = to_field_repr(Gx_int)
+Gy_field = to_field_repr(Gy_int)
+
+print_field("Gx", Gx_field)
+print()
+print_field("Gy", Gy_field)
+```
+
+The output can be seen in lines 435 to 456 of the WinRarConfig.hpp file.
+
+```
+Gx = to_field([
+ 0x38CC, 0x052F, 0x2510, 0x45AA,
+ 0x1B89, 0x4468, 0x4882, 0x0D67,
+ 0x4FEB, 0x55CE, 0x0025, 0x4CB7,
+ 0x0CC2, 0x59DC, 0x289E, 0x65E3,
+ 0x56FD
+])
+
+Gy = to_field([
+ 0x31A7, 0x65F2, 0x18C4, 0x3412,
+ 0x7388, 0x54C1, 0x539B, 0x4A02,
+ 0x4D07, 0x12D6, 0x7911, 0x3B5E,
+ 0x4F0E, 0x216F, 0x2BF2, 0x1974,
+ 0x20DA
+])
+```
+
+## 3. Verify whether the Point G and PK are on the curve
+
+A Python code to verify whether the base point  and PK are on the curve is as follows.
+
+```python
+# GF(((2^15)^17))
+def gf2_15_add(a, b):
+ return a ^ b
+
+def gf2_15_mul(a, b):
+ # Modulus: x^15 + x + 1
+ res = 0
+ for i in range(15):
+ if (b >> i) & 1:
+ res ^= a << i
+ # Irreducible polynomial: x^15 + x + 1
+ for i in range(29, 14, -1):
+ if (res >> i) & 1:
+ res ^= 0b1000000000000011 << (i - 15)
+ return res & 0x7FFF # 15-bit mask
+
+def gf2_15_poly_add(a, b):
+ return [gf2_15_add(x, y) for x, y in zip(a, b)]
+
+def gf2_15_poly_mul(a, b):
+ res = [0] * 33
+ for i in range(17):
+ for j in range(17):
+ res[i + j] ^= gf2_15_mul(a[i], b[j])
+ return res
+
+def gf2_15_17_mod(poly):
+ # Modulus: y^17 + y^3 + 1
+ for i in range(len(poly) - 1, 16, -1):
+ if poly[i]:
+ poly[i - 17] ^= poly[i]
+ poly[i - 14] ^= poly[i]
+ poly[i] = 0
+ return poly[:17]
+
+def gf2_15_17_mul(a, b):
+ return gf2_15_17_mod(gf2_15_poly_mul(a, b))
+
+def gf2_15_17_square(a):
+ return gf2_15_17_mul(a, a)
+
+def gf2_15_17_add(a, b):
+ return gf2_15_poly_add(a, b)
+
+def gf2_15_17_eq(a, b):
+ return all(x == y for x, y in zip(a, b))
+
+def is_on_curve(x, y, b):
+ y2 = gf2_15_17_square(y)
+ xy = gf2_15_17_mul(x, y)
+ lhs = gf2_15_17_add(y2, xy)
+ x2 = gf2_15_17_square(x)
+ x3 = gf2_15_17_mul(x2, x)
+ rhs = gf2_15_17_add(x3, b)
+ return gf2_15_17_eq(lhs, rhs)
+
+def to_field(arr):
+ assert len(arr) == 17
+ return arr[:]
+
+# Parameter definition
+b = [0x00] * 17
+b = [161] + [0]*16 # Constant element in GF((2^15)^17), equivalent to b[0] = 0xA1
+
+Gx = to_field([0x38CC, 0x052F, 0x2510, 0x45AA, 0x1B89, 0x4468, 0x4882, 0x0D67,
+ 0x4FEB, 0x55CE, 0x0025, 0x4CB7, 0x0CC2, 0x59DC, 0x289E, 0x65E3, 0x56FD])
+Gy = to_field([0x31A7, 0x65F2, 0x18C4, 0x3412, 0x7388, 0x54C1, 0x539B, 0x4A02,
+ 0x4D07, 0x12D6, 0x7911, 0x3B5E, 0x4F0E, 0x216F, 0x2BF2, 0x1974, 0x20DA])
+
+PKx = to_field([0x3A1A, 0x1109, 0x268A, 0x12F7, 0x3734, 0x75F0, 0x576C, 0x2EA4,
+ 0x4813, 0x3F62, 0x0567, 0x784D, 0x753D, 0x6D92, 0x366C, 0x1107, 0x3861])
+PKy = to_field([0x6C20, 0x6027, 0x1B22, 0x7A87, 0x43C4, 0x1908, 0x2449, 0x4675,
+ 0x7933, 0x2E66, 0x32F5, 0x2A58, 0x1145, 0x74AC, 0x36D0, 0x2731, 0x12B6])
+
+# Verification
+print("Verify whether the point G is on the curve:", is_on_curve(Gx, Gy, b))
+print("Verify whether the PK is on the curve:", is_on_curve(PKx, PKy, b))
+```
diff --git a/README.VERIFY_Point_G.zh-CN.md b/README.VERIFY_Point_G.zh-CN.md
new file mode 100644
index 0000000..66c48f6
--- /dev/null
+++ b/README.VERIFY_Point_G.zh-CN.md
@@ -0,0 +1,155 @@
+# 验证基点G在曲线上
+
+## 1. 复合域、曲线和基点G
+
+复合域为  。
+
+基域为  ,即域中每个元素是一个 `15-bit` 的数字。扩展次数是17,所以整个复合域元素可以看作是一个17项的多项式,每项都是  中的数。
+
+曲线方程为:
+
+
+
+
+
+
+基点  为:
+
+
+
+
+
+
+## 2. 转换基点G
+
+将基点 (以大整数形式)转换为  所需的 `15-bit` 小端表示形式。一个转换基点  的 Python 脚本如下:
+
+```python
+def to_field_repr(val, bits=15, count=17):
+ mask = (1 << bits) - 1
+ result = []
+ for _ in range(count):
+ result.append(val & mask)
+ val >>= bits
+ return result
+
+def print_field(label, field_data):
+ print(f"{label} = to_field([")
+ for i in range(len(field_data)):
+ print(f" 0x{field_data[i]:04X},", end="\n" if (i + 1) % 4 == 0 else " ")
+ print("])")
+
+Gx_int = 0x56fdcbc6a27acee0cc2996e0096ae74feb1acf220a2341b898b549440297b8cc
+Gy_int = 0x20da32e8afc90b7cf0e76bde44496b4d0794054e6ea60f388682463132f931a7
+
+Gx_field = to_field_repr(Gx_int)
+Gy_field = to_field_repr(Gy_int)
+
+print_field("Gx", Gx_field)
+print()
+print_field("Gy", Gy_field)
+```
+
+输出结果可以看到正是 WinRarConfig.hpp 文件中 435~456 行的内容:
+
+```
+Gx = to_field([
+ 0x38CC, 0x052F, 0x2510, 0x45AA,
+ 0x1B89, 0x4468, 0x4882, 0x0D67,
+ 0x4FEB, 0x55CE, 0x0025, 0x4CB7,
+ 0x0CC2, 0x59DC, 0x289E, 0x65E3,
+ 0x56FD
+])
+
+Gy = to_field([
+ 0x31A7, 0x65F2, 0x18C4, 0x3412,
+ 0x7388, 0x54C1, 0x539B, 0x4A02,
+ 0x4D07, 0x12D6, 0x7911, 0x3B5E,
+ 0x4F0E, 0x216F, 0x2BF2, 0x1974,
+ 0x20DA
+])
+```
+
+## 3. 验证基点G和PK是否在曲线上
+
+一个验证基点  和PK是否在曲线上的 Python 脚本如下:
+
+```python
+# GF(((2^15)^17))
+def gf2_15_add(a, b):
+ return a ^ b
+
+def gf2_15_mul(a, b):
+ # 模 x^15 + x + 1
+ res = 0
+ for i in range(15):
+ if (b >> i) & 1:
+ res ^= a << i
+ # 模多项式 x^15 + x + 1
+ for i in range(29, 14, -1):
+ if (res >> i) & 1:
+ res ^= 0b1000000000000011 << (i - 15)
+ return res & 0x7FFF # 15-bit mask
+
+def gf2_15_poly_add(a, b):
+ return [gf2_15_add(x, y) for x, y in zip(a, b)]
+
+def gf2_15_poly_mul(a, b):
+ res = [0] * 33
+ for i in range(17):
+ for j in range(17):
+ res[i + j] ^= gf2_15_mul(a[i], b[j])
+ return res
+
+def gf2_15_17_mod(poly):
+ # 模 y^17 + y^3 + 1
+ for i in range(len(poly) - 1, 16, -1):
+ if poly[i]:
+ poly[i - 17] ^= poly[i]
+ poly[i - 14] ^= poly[i]
+ poly[i] = 0
+ return poly[:17]
+
+def gf2_15_17_mul(a, b):
+ return gf2_15_17_mod(gf2_15_poly_mul(a, b))
+
+def gf2_15_17_square(a):
+ return gf2_15_17_mul(a, a)
+
+def gf2_15_17_add(a, b):
+ return gf2_15_poly_add(a, b)
+
+def gf2_15_17_eq(a, b):
+ return all(x == y for x, y in zip(a, b))
+
+def is_on_curve(x, y, b):
+ y2 = gf2_15_17_square(y)
+ xy = gf2_15_17_mul(x, y)
+ lhs = gf2_15_17_add(y2, xy)
+ x2 = gf2_15_17_square(x)
+ x3 = gf2_15_17_mul(x2, x)
+ rhs = gf2_15_17_add(x3, b)
+ return gf2_15_17_eq(lhs, rhs)
+
+def to_field(arr):
+ assert len(arr) == 17
+ return arr[:]
+
+# 参数定义
+b = [0x00] * 17
+b = [161] + [0]*16 # GF((2^15)^17) 中的常数元素,等价于 b[0] = 0xA1
+
+Gx = to_field([0x38CC, 0x052F, 0x2510, 0x45AA, 0x1B89, 0x4468, 0x4882, 0x0D67,
+ 0x4FEB, 0x55CE, 0x0025, 0x4CB7, 0x0CC2, 0x59DC, 0x289E, 0x65E3, 0x56FD])
+Gy = to_field([0x31A7, 0x65F2, 0x18C4, 0x3412, 0x7388, 0x54C1, 0x539B, 0x4A02,
+ 0x4D07, 0x12D6, 0x7911, 0x3B5E, 0x4F0E, 0x216F, 0x2BF2, 0x1974, 0x20DA])
+
+PKx = to_field([0x3A1A, 0x1109, 0x268A, 0x12F7, 0x3734, 0x75F0, 0x576C, 0x2EA4,
+ 0x4813, 0x3F62, 0x0567, 0x784D, 0x753D, 0x6D92, 0x366C, 0x1107, 0x3861])
+PKy = to_field([0x6C20, 0x6027, 0x1B22, 0x7A87, 0x43C4, 0x1908, 0x2449, 0x4675,
+ 0x7933, 0x2E66, 0x32F5, 0x2A58, 0x1145, 0x74AC, 0x36D0, 0x2731, 0x12B6])
+
+# 验证
+print("验证基点 G 是否在曲线上:", is_on_curve(Gx, Gy, b))
+print("验证 PK 是否在曲线上:", is_on_curve(PKx, PKy, b))
+```