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 ![GF2p15-inlined](assets/formula/GF2p15-inlined-light.s

- where each coefficients is in ![GF2-inlined](assets/formula/GF2-inlined-light.svg#gh-light-mode-only)![GF2-inlined](assets/formula/GF2-inlined-dark.svg#gh-dark-mode-only). If we use

@@ -19,7 +18,6 @@ where each coefficients is in ![GF2-inlined](assets/formula/GF2-inlined-light.sv

- as the standard basis of the ground field, an element ![A](assets/formula/AA-inlined-light.svg#gh-light-mode-only)![A](assets/formula/AA-inlined-dark.svg#gh-dark-mode-only) in ![GF2p15-inlined](assets/formula/GF2p15-inlined-light.svg#gh-light-mode-only)![GF2p15-inlined](assets/formula/GF2p15-inlined-dark.svg#gh-dark-mode-only) can be denoted as

@@ -27,7 +25,6 @@ as the standard basis of the ground field, an element ![A](assets/formula/AA-inl

- --- The irreducible polynomial of composite field ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-light.svg#gh-light-mode-only) ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-dark.svg#gh-dark-mode-only) is @@ -37,7 +34,6 @@ The irreducible polynomial of composite field ![GF2p15p17-inlined](assets/formul

- where each coefficients is in ![GF2p15-inlined](assets/formula/GF2p15-inlined-light.svg#gh-light-mode-only)![GF2p15-inlined](assets/formula/GF2p15-inlined-dark.svg#gh-dark-mode-only). If we use

@@ -45,7 +41,6 @@ where each coefficients is in ![GF2p15-inlined](assets/formula/GF2p15-inlined-li

- as the standard basis of the composite field, an element ![B](assets/formula/BB-inlined-light.svg#gh-light-mode-only)![B](assets/formula/BB-inlined-dark.svg#gh-dark-mode-only) in ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-light.svg#gh-light-mode-only) ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-dark.svg#gh-dark-mode-only) can be denoted as

@@ -53,7 +48,6 @@ as the standard basis of the composite field, an element ![B](assets/formula/BB-

- --- For clarity, we use ![D](assets/formula/DD-inlined-light.svg#gh-light-mode-only)![D](assets/formula/DD-inlined-dark.svg#gh-dark-mode-only) , which is a 255-bits-long integer to denote an element ![B](assets/formula/BB-inlined-light.svg#gh-light-mode-only)![B](assets/formula/BB-inlined-dark.svg#gh-dark-mode-only) in ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-light.svg#gh-light-mode-only) ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-dark.svg#gh-dark-mode-only). The map between them is @@ -63,7 +57,6 @@ For clarity, we use ![D](assets/formula/DD-inlined-light.svg#gh-light-mode-only)

- ## 2. Elliptic curve over ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-light.svg#gh-light-mode-only) ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-dark.svg#gh-dark-mode-only) 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 ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) is

@@ -81,6 +73,7 @@ The base point ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](

+A [verification example](README.VERIFY_Point_G.md) of the base point ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) on the curve. whose order ![n](assets/formula/n-inlined-light.svg#gh-light-mode-only)![n](assets/formula/n-inlined-dark.svg#gh-dark-mode-only) is @@ -89,7 +82,6 @@ whose order ![n](assets/formula/n-inlined-light.svg#gh-light-mode-only)![n](asse

- ## 3. Message hash algorithm We use @@ -99,7 +91,6 @@ We use

- to denote a message whose length is ![l](assets/formula/l-inlined-light.svg#gh-light-mode-only)![l](assets/formula/l-inlined-dark.svg#gh-dark-mode-only). So the SHA1 value of ![M](assets/formula/MM-inlined-light.svg#gh-light-mode-only)![M](assets/formula/MM-inlined-dark.svg#gh-dark-mode-only) should be

@@ -107,7 +98,6 @@ to denote a message whose length is ![l](assets/formula/l-inlined-light.svg#gh-l

- where ![s0,4](assets/formula/13-light.svg#gh-light-mode-only)![s0,4](assets/formula/13-dark.svg#gh-dark-mode-only) 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 ![h](assets/formula/h-inlined-light.svg#gh-light-mode-only)![h](assets/formula/h-inlined-dark.svg#gh-dark-mode-only) 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 ![k](assets/formula/k-inlined-light.svg#gh-light-mode-only)![k](assets/formula/k-inlined-dark.svg#gh-dark-mode-only) to denote private key, ![P](assets/formula/PP-inlined-light.svg#gh-light-mode-only)![P](assets/formula/PP-inlined-dark.svg#gh-dark-mode-only) to denote public key. So there must be @@ -127,7 +116,6 @@ We use ![k](assets/formula/k-inlined-light.svg#gh-light-mode-only)![k](assets/fo

- If we use ![h](assets/formula/h-inlined-light.svg#gh-light-mode-only)![h](assets/formula/h-inlined-dark.svg#gh-dark-mode-only) to denote the hash of input data, WinRAR use the following algorithm to perform signing: 1. Generate a random big integer ![Rnd](assets/formula/Rnd-inlined-light.svg#gh-light-mode-only)![Rnd](assets/formula/Rnd-inlined-dark.svg#gh-dark-mode-only) which satisfies ![RND](assets/formula/18-light.svg#gh-light-mode-only)![RND](assets/formula/18-dark.svg#gh-dark-mode-only). @@ -165,7 +153,6 @@ We use

- to denote input data whose length is ![l](assets/formula/l-inlined-light.svg#gh-light-mode-only)![l](assets/formula/l-inlined-dark.svg#gh-dark-mode-only). WinRAR use it to generate private key ![k](assets/formula/k-inlined-light.svg#gh-light-mode-only)![k](assets/formula/k-inlined-dark.svg#gh-dark-mode-only). 1. We use ![g0-5](assets/formula/27-light.svg#gh-light-mode-only)![g0-5](assets/formula/27-dark.svg#gh-dark-mode-only) to denote 6 32-bits-long integer. So there is @@ -184,7 +171,6 @@ to denote input data whose length is ![l](assets/formula/l-inlined-light.svg#gh-

- Otherwise, when ![l=0](assets/formula/34-light.svg#gh-light-mode-only)![l=0](assets/formula/34-dark.svg#gh-dark-mode-only), we let

@@ -201,7 +187,6 @@ to denote input data whose length is ![l](assets/formula/l-inlined-light.svg#gh-

- We takes the lowest 16 bits of ![S0](assets/formula/38-light.svg#gh-light-mode-only)![S0](assets/formula/38-dark.svg#gh-dark-mode-only) and donote it as ![Kg0](assets/formula/39-light.svg#gh-light-mode-only)![Kg0](assets/formula/39-dark.svg#gh-dark-mode-only). 5. Repeat step 4 again with 14 times. @@ -222,7 +207,6 @@ Private key ![k](assets/formula/k-inlined-light.svg#gh-light-mode-only)![k](asse

- This private key is generated by the algorithm describled in section 5 where the length of data ![T](assets/formula/TT-inlined-light.svg#gh-light-mode-only)![T](assets/formula/TT-inlined-dark.svg#gh-dark-mode-only) is zero. Public key ![P](assets/formula/PP-inlined-light.svg#gh-light-mode-only)![P](assets/formula/PP-inlined-dark.svg#gh-dark-mode-only) is @@ -297,7 +281,6 @@ The following is the algorithm to generate `rarreg.key`.

- Use the algorithm describled in section 4, with argument ![Temp](assets/formula/Temp-inlined-light.svg#gh-light-mode-only)![Temp](assets/formula/Temp-inlined-dark.svg#gh-dark-mode-only) and private key ![k](assets/formula/k-inlined-light.svg#gh-light-mode-only)![k](assets/formula/k-inlined-dark.svg#gh-dark-mode-only) describled section 6, to get signature ![(rTemp,sTemp)](assets/formula/59-light.svg#gh-light-mode-only)![(rTemp,sTemp)](assets/formula/59-dark.svg#gh-dark-mode-only). The bit length of ![rTemp](assets/formula/60-light.svg#gh-light-mode-only)![rTemp](assets/formula/60-dark.svg#gh-dark-mode-only) and ![sTemp](assets/formula/61-light.svg#gh-light-mode-only)![sTemp](assets/formula/61-dark.svg#gh-dark-mode-only) 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 ![SZchecksum](assets/formula/66-light.svg#gh-light-mode-only)![SZchecksum](assets/formula/66-dark.svg#gh-dark-mode-only). If the length is less than 10, pad character `'0'` until the length is 10. 12. Let ![Data](assets/formula/Data-inlined-light.svg#gh-light-mode-only)![Data](assets/formula/Data-inlined-dark.svg#gh-dark-mode-only) 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` 文件,其使

+基点 ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) 在曲线上的[验证示例](README.VERIFY_Point_G.zh-CN.md)。 + 基点 ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) 的阶 ![n](assets/formula/n-inlined-light.svg#gh-light-mode-only)![n](assets/formula/n-inlined-dark.svg#gh-dark-mode-only) 为:

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 ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-light.svg#gh-light-mode-only) ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-dark.svg#gh-dark-mode-only). + +And the base field is ![GF2p15-inlined](assets/formula/GF2p15-inlined-light.svg#gh-light-mode-only) ![GF2p15-inlined](assets/formula/GF2p15-inlined-dark.svg#gh-dark-mode-only), 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 ![GF2p15-inlined](assets/formula/GF2p15-inlined-light.svg#gh-light-mode-only)![GF2p15-inlined](assets/formula/GF2p15-inlined-dark.svg#gh-dark-mode-only). + +The equation of the elliptic curve that WinRAR uses is + +

+ + +

+ +The base point ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) is + +

+ + +

+ +## 2. Conversion point G + +Convert the point ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only)(in large integer format) into the required little-endian representation of `15-bit` segments for the field ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-dark.svg#gh-dark-mode-only). A Python for converting the point ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) 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 ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) 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 + +复合域为 ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-light.svg#gh-light-mode-only) ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-dark.svg#gh-dark-mode-only)。 + +基域为 ![GF2p15-inlined](assets/formula/GF2p15-inlined-light.svg#gh-light-mode-only)![GF2p15-inlined](assets/formula/GF2p15-inlined-dark.svg#gh-dark-mode-only) ,即域中每个元素是一个 `15-bit` 的数字。扩展次数是17,所以整个复合域元素可以看作是一个17项的多项式,每项都是 ![GF2p15-inlined](assets/formula/GF2p15-inlined-light.svg#gh-light-mode-only)![GF2p15-inlined](assets/formula/GF2p15-inlined-dark.svg#gh-dark-mode-only) 中的数。 + +曲线方程为: + +

+ + +

+ +基点 ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) 为: + +

+ + +

+ +## 2. 转换基点G + +将基点 ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only)(以大整数形式)转换为 ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-light.svg#gh-light-mode-only) ![GF2p15p17-inlined](assets/formula/GF2p15p17-inlined-dark.svg#gh-dark-mode-only)所需的 `15-bit` 小端表示形式。一个转换基点 ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) 的 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是否在曲线上 + +一个验证基点 ![G](assets/formula/GG-inlined-light.svg#gh-light-mode-only)![G](assets/formula/GG-inlined-dark.svg#gh-dark-mode-only) 和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)) +```