ブロックチェーン システム探索への道: 楕円曲線に基づく秘密鍵と公開鍵の生成

最初の 2 つのセクションでは、抽象代数の重要な概念である有限体について説明し、次に楕円曲線上の点に基づく奇妙な「+」演算について学びました。この 2 つは無関係に見えますが、実際には論理的に密接に関連しています。要するに、楕円曲線上の点 G を取り、それ自体に対して「+」演算を実行させると、その結果によって形成される集合は有限体を形成します。

まず、最初に有限体 F(103)={0, 1, ... 102} を与え、有限体に作用する 2 つの演算 "+" と "*" が実際には剰余 通常の加算と乗算に基づいて、有限体の点が与えられた楕円曲線 y ^ 2 = x ^ 3 + 7 上にあるかどうかを判断する場合、まず、点の x 座標と y 座標を楕円曲線方程式に代入します。 、同時に余りに基づいて左辺と右辺が等しいかどうかを判断します。たとえば、点(17,64)(x、y座標の値を有限体から取得します)であるかどうかを判断します。 F(103)) が曲線上にあるので、次の計算を行います:
y ^ 2 = (64 ^ 2) % 103 = 79, x ^ 3 + 7 = (17 ^ 3 + 7 ) % 103 = 79 左
から計算後、右辺が同じ値をとる場合、点 (17, 64) が曲線上にあります。

暗号化効果を実現するために、有限体の「+」および「*」演算と、前のセクションで説明した楕円曲線上の点の「+」演算を組み合わせます。ここでは、この 2 つを組み合わせないように注意する必要があります。操作 対応するシンボルは同じように見えますが、実際の対応する操作は異なるため、混乱が生じます。

まず、上記の楕円曲線上の有限体点の判定ロジックをコードで実装して見てみましょう。

"""
将有限域的点输入到椭圆曲线,需要注意的是在椭圆曲线里执行+和*两种运算时,它会自动转换为
有限域定义的__add__ 和 __mul__运算,注意到即使运算操作的逻辑变了,但是判断点是否在椭圆曲线上
依然是判断椭圆曲线对应公式的两边是否相等
"""

a = LimitFieldElement(0, 223)
b = LimitFieldElement(7, 223)
x = LimitFieldElement(192, 223)
y = LimitFieldElement(105, 223)
p1 = EllipticPoint(x, y, a, b)
print(f"Elliptc Point over F_223 is {
      
      p1}")

上記のコードを実行した結果は次のようになります。
F_223 上の Elliptc Point is EllipticPoint(x:LimitFieldElement_223(192),y:LimitFieldElement_223(105),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))

ここで、ステートメント EllipticPoint(x, y, a, b) が実行されると、対応する __init__ 関数が実行され、入力パラメーターに対して次の操作が実行されることに注意してください。

if y ** 2 != x ** 3 + a * x + b:

ここで、「**」、「*」、「+」、「!=」などの対応する演算は、有限体オブジェクトの _ pow_、 _ mul_、 _ add_、 _ ne_、つまりこれらのオーバーロードされた関数に対応します。

さて、少し頭を働かせましょう。前のセクションでは、楕円曲線上の指定された 2 点に対して「+」演算を実行した後、3 番目の点を取得する方法を推測しました。アルゴリズムでは、一連の通常の加算、減算が行われます。 , 乗算と除算の演算が実行されます。ここで、これらすべての演算を有限体上の対応する演算に変換します。得られた結果は依然として有効です。たとえば、2 つの点 P1(x1,y1)、P2(x2, y2) が与えられたとします。 P3 = P1 + P2 を取得するには、上記の A セクションで次の操作を実行します。

s = (y2 - y1)/(x2 - x1)
x3 = s ^ 2 - x1 - x3
y3 = s * (x1 - x3) - y1

上記の減算は、LimitFiniteField クラスの __sub__、「_truediv_ 」、およびその他のロジック実装に対応する必要があります。ここで、通常の加算、減算、乗算の演算を剰余演算を伴う演算に置き換えても、元の結論が依然として有効であることがわかります。これが抽象代数の強力な役割です。前の章では、LimitFinitField クラスに __ sub __ メソッドと __ rmul __ メソッドを実装するのを忘れていました。

 def __sub__(self, other):
        if self.order != other.order:
            raise TypeError("不能对两个不同有限域的元素执行减法操作")
        num = (self.num - other.num) % self.order
        return __class__(num, self.order)
   
def __rmul__(self, scalar):
        #实现与常量相乘
        num = (self.num * scalar) % self.order
        return __class__(num, self.order)

上記の基礎を踏まえて、有限体に基づく楕円曲線上の点の「+」演算をテストしてみましょう。コードは次のとおりです。

```a = LimitFieldElement(0, 223)
b = LimitFieldElement(7, 223)
x = LimitFieldElement(192, 223)
y = LimitFieldElement(105, 223)
p1 = EllipticPoint(x, y, a, b)
#print(f"Elliptc Point over F_223 is {p1}")

#基于有限域上椭圆曲线点对应的"+"操作
x2 = LimitFieldElement(17, 223)
y2 = LimitFieldElement(56, 223)
p2 = EllipticPoint(x2, y2, a, b)

print(f"Elliptic point P1 + P2 is {
      
      p1 + p2}")

上記のコードを実行した結果は次のようになります。

Elliptic point P1 + P2 is EllipticPoint(x:LimitFieldElement_223(170),y:LimitFieldElement_223(142),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))

次に、楕円曲線の点と定数の乗算を実現する必要があります。この演算は、楕円曲線暗号化で重要な役割を果たします。後で、楕円曲線上の点 G を選択し、定数 k を選択して k G を計算します。ここで、k は秘密鍵. に対応し、k G は公開鍵に対応します。ここでの原理は、点 G がわかっていて、同時に k*G の結果が得られたとしても、数学では k を逆に計算する方法はないので、これが楕円曲線暗号の原理になります。

次に、コードを使用して楕円曲線上の特定の点 G を実装し、k*G、k = 1、2、... を計算します。得られた結果は、定数の乗算が本質的に、G をそれ自体に加算することであることに注意してください。与えられた回数:

#计算点G(47, 71)的常量乘法
x = LimitFieldElement(47, 223)
y = LimitFieldElement(71, 223)
G = EllipticPoint(x, y, a, b)
result = G
for k in range(1, 22):
    if k != 1:
        result = result + G
    print(f"{
      
      k} * (47, 71) is {
      
      result}")

上記のコードを実行した結果は次のようになります。

1 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(47),y:LimitFieldElement_223(71),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
2 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(36),y:LimitFieldElement_223(111),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
3 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(15),y:LimitFieldElement_223(137),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
4 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(194),y:LimitFieldElement_223(51),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
5 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(126),y:LimitFieldElement_223(96),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
6 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(139),y:LimitFieldElement_223(137),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
7 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(92),y:LimitFieldElement_223(47),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
8 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(116),y:LimitFieldElement_223(55),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
9 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(69),y:LimitFieldElement_223(86),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
10 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(154),y:LimitFieldElement_223(150),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
11 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(154),y:LimitFieldElement_223(73),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
12 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(69),y:LimitFieldElement_223(137),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
13 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(116),y:LimitFieldElement_223(168),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
14 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(92),y:LimitFieldElement_223(176),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
15 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(139),y:LimitFieldElement_223(86),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
16 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(126),y:LimitFieldElement_223(127),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
17 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(194),y:LimitFieldElement_223(172),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
18 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(15),y:LimitFieldElement_223(86),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
19 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(36),y:LimitFieldElement_223(112),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
20 * (47, 71) is EllipticPoint(x:LimitFieldElement_223(47),y:LimitFieldElement_223(152),a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))
21 * (47, 71) is EllipticPoint(x:None,y:None,a:LimitFieldElement_223(0), b:LimitFieldElement_223(7))

上記の演算結果には特徴があり、k が 21 まで増加すると、k*G で得られる結果が楕円曲線の 0 点となり、楕円曲線上の任意の点を選択して上記の演算を行うと、 k=1 のときは常に k=n で k * G がゼロになるとき、集合 {0, G, 2*G, … ,(n-1)*G} によって形成される集合は「」と呼ばれます。数学における「群」。

前に述べた有限体と比較すると、グループ内の要素は 1 つの演算「+」にのみ対応します (有限体には 2 種類あります)。グループのいくつかのプロパティを見てみましょう: 1、ユニット 0、つまり、グループには
特別な要素「0」が含まれている必要があります。任意の要素はそれに対して「+」演算を実行し、結果は要素自体になります。つまり、A がグループ内のいずれかの要素である場合、A "+" 0 = A, 2 、
クロージャ、A と B がグループ内の 2 つの要素である場合、A "+" B の結果は依然としてグループ内の要素です。
3. 可逆性、A が群内の要素の場合、群内に別の要素 B が存在します。つまり、A "+" B = 0 4、可換性、A、B が群内の 2 つの要素の場合
、 A "+" B = B "+" A
5、結合性、(A "+" B) "+" C = A "+" (B "+" C)

「群」は抽象代数において重要な概念であり、暗号の柱概念でもあります。以前に k * G を実装したとき、コードは点 G に対して k 回の「+」演算を実行することでした。今度は、楕円曲線の点に定数乗算を追加し、EllipitcPoint にコードを追加して次のように実現します。

 def __rmul__(self, scalar):
        #如果常量是0,那么就返回椭圆曲线"0"点
        result = self.__class__(None, None, self.a, self.b)
        #自加给定次数
        for _ in range(scalar):
            result += self

        return result

次のコードを使用して上記の実装をテストすると、出力が前と同じであることがわかり、実装が正しいことを確認できます。

#测试常量乘法:
for k in range(1, 22):
    print(f"{
      
      k} * (47, 71) is {
      
      k * G}")

次に, ビットコインの対応する楕円曲線の実現を見てみましょう. ビットコインの場合、その対応する楕円曲線は a = 0, b = 7 を設定するため、対応する曲線の式は y ^ 2 = x ^ 3 + 7 となります。定義される有限体の要素の数は p = 2256 – 232 – 977 で、点 G の x 成分は G(x) = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 G(y) = 0x483ada7726 a3c です。 4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8、k = 0xfffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 の場合、k *
G
= 0.

ビットコインで使用される楕円曲線には、いくつかの特徴があります。第一に、パラメータ a と b は非常に単純です。第二に、それは有限体の要素の数に対応しており、これは 2^256 に近いため、楕円曲線の 2 つの成分 G 点 これらはすべて 256 ビット、つまり 32 バイトに近く、この値は宇宙の原子の総数にほぼ近い値です。

コードを使用して、点 G がビットコイン曲線上にあるかどうかを確認してみましょう。

#测试G点是否在曲线上
gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798
gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
p = 2 ** 256 - 2 ** 32 - 977
print(gy ** 2 % p == (gx ** 3 + 7) % p) #True

上記のコードは実行後に True を返します。次に、点 G の 2 つの成分を有限体上の点に変換し、同時に k * G の結果が楕円曲線上の 0 点であるかどうかをテストします。対応するコードは次のとおりです。

k = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
x = LimitFieldElement(gx, p)
y = LimitFieldElement(gy, p)
a = LimitFieldElement(0, p)
b = LimitFieldElement(7, p)
G = EllipticPoint(x, y, a, b)
print(f'result of n * G is {
      
      n * G}')

上記のコードを直接実行すると、k の値が大きすぎるためにプログラムがスタックしてしまうことがわかりますが、EllipticPoint クラスの定数乗算 (__ rmul __) を実装するときは、k 加算ループを使用します。しかし、k の値が大きすぎるため、ループは短時間で完了できません。

解決策の 1 つは、バイナリ展開を使用して最適化することです。具体的な例を見てみましょう。定数値 k の値が 36 であると仮定します。バイナリに変換すると 100100 になるため、k * G = (2 ^5 + 2 ^ 2) ) * G = 2 ^ 5 * G + 2 ^ 2 * G, ここでは、元の 36 回の加算を 2 回の乗算と 1 回の加算に変換し、必要な計算量が lg(k) を超えないことがわかります。計算中 (0b100100) * G の場合、右端のビットからトラバースします。ビットが 0 の場合、計算する必要があるのは 2 ^ k (k は現在トラバースされているビットの位置をバイナリで表します)、トラバースされたビットの場合のみです。が 1 の場合は加算が実行されるため、次のように定数乗算を最適化します。

    def __rmul__(self, scalar):
        #二进制扩展
        coef = scalar
        current = self
        result = self.__class__(None, None, self.a, self.b)
        while coef:
            if coef & 1: #如果当前比特位是1,那么执行加法
                result += current
            current += current  #如果上次比特位的位置在k,那么下次比特位的位置变成k+1,2^(k+1)*G 等价于2^k*G + 2^k * G
            coef >>= 1

        return result

上記の最適化後に前のコードを実行すると、結果は次のようになります。

result of k * G is EllipticPoint(x:None,y:None,a:LimitFieldElement_115792089237316195423570985008687907853269984665640564039457584007908834671663(0), b:LimitFieldElement_115792089237316195423570985008687907853269984665640564039457584007908834671663(7))

k * G の結果は確かに楕円曲線上のゼロ点であることがわかります。ビットコインに対応する楕円曲線は secp256k1 というので、曲線の a パラメータと b パラメータが決まり、有限体の要素の数も決まりました。そこで、LimitFinitField と EllipticPoint に基づいて対応するサブクラスを作成し、次のように書きます。 death ,code に対応するパラメータは次のようになります。


P = 2**256 - 2**32 - 977


class S256Field(LimitFieldElement):
    def __init__(self, num, order=None):
        # 参数写死即可
        super().__init__(num, P)

    def __repr__(self):
        return '{:x}'.format(self.num).zfill(64)


N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141


class S256Point(EllipticPoint):
    def __init__(self, x, y, a=None, b=None):
        a, b = S256Field(0), S256Field(7)
        if type(x) == int:
            super().__init__(S256Field(x), S256Field(y), a, b)
        else:
            # 如果x,y 是None,那么直接初始化椭圆曲线的0点
            super().__init__(x, y, a, b)

    def __repr__(self):
        if self.x is None:
            return 'S256Point(infinity)'

        return f'S256Point({
      
      self.x}, {
      
      self.y})'

    def __rmul__(self, k):
        k = k % N
        return super().__rmul__(k)


G = S256Point(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
              0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)


print(N * G)

上記のコードを実行した後の出力は次のとおりです。

S256Point(infinity)

上記の基礎により、楕円曲線を通じて公開鍵と秘密鍵を生成できます。秘密鍵は非常に単純です。必要なのは [1, N] の範囲の値 e を取るだけであり、公開鍵は P = e * G、公開キーを使用して、ビットコイン ウォレットのアドレスを構築できます。

さらに詳しいコンテンツについては、Station B で「コーディング ディズニー」を検索してください。このセクションのコードのダウンロード アドレスは次のとおりです。

リンク: https://pan.baidu.com/s/1SIVPmmVXYnA0pfKh4cfuEQ 抽出コード: b1fe

おすすめ

転載: blog.csdn.net/tyler_download/article/details/130444233