bin()は2進数に、hex()は16進数に変換する
num = 13 num_bin = bin(num) num_hex = hex(num) print(num_bin) print(num_hex)
$ python3 test.py
0b1101
0xd
format()を用いると、bで2進数、xで16進数に変換する
随机应变 ABCD: Always Be Coding and … : хороший
bin()は2進数に、hex()は16進数に変換する
num = 13 num_bin = bin(num) num_hex = hex(num) print(num_bin) print(num_hex)
$ python3 test.py
0b1101
0xd
format()を用いると、bで2進数、xで16進数に変換する
署名をシリアライズする場合、署名のr, sをエンコードする必要がある。しかし、pointのように圧縮はできない
署名をシリアライズする標準をDERフォーマット(Distinguished Encoding Rules)と呼ぶ。
### DER署名
1. 0x30バイトで開始
2. 署名の残りの長さをエンコード(0x44または0x45)して追加
3. マーカーバイト0x02を追加
4. rをビッグエンディアン整数としてエンコード ただしrの先頭バイトが0x80以上の時は0x00を付与、長さをrの先頭に追加
5. マーカーバイト0x02を追加
6. sをビッグエンディアン整数としてエンコード ただしsの先頭バイトが0x80以上の時は0x00を付与、長さをsの先頭に追加
先頭ビットが1の場合は負の数。DERは負のエンコードを許容する。
def der(self):
rbin = self.r.to_bytes(32, byteorder='big')
rbin = rbin.lstrip(b'\x00')
if rbin[0] & 0x80:
rbin = b'\x00' + rbin
result = bytes([2, len(rbin)]) + rbin
sbin = self.s.to_bytes(32, byteorder='big')
sbin = sbin.lstrip(b'\x00')
if sbin[0] & 0x80:
sbin = b'\x00' + sbin
result += bytes([2, len(sbin)]) + sbin
return bytes([0x30, len(result)]) + result
r, sが定まれば、DERフォーマットが求められる。
from ecc import Signature
r = 0x37206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c6
s = 0x8ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec
sig = Signature(r, s)
print(sig.der().hex())
$ python3 main.py
3045022037206a0610995c58074999cb9767b87af4c4978db68c06e8e6e81d282047a7c60221008ca63759c1157ebeaec0d03cecca119fc9a75bf8e6d0fa65c841c8e2738cdaec
bytesはバイナリデータを扱うバイト型
str1 = "高島屋"
enc_str1 = str1.encode()
print(f"{str1}:{enc_str1}")
print(f"type: {type(enc_str1)}")
print(f"高: {hex(ord('高'))}")
print(f"島: {hex(ord('島'))}")
print(f"屋: {hex(ord('屋'))}")
$ python3 test.py
高島屋:b’\xe9\xab\x98\xe5\xb3\xb6\xe5\xb1\x8b’
type:
高: 0x9ad8
島: 0x5cf6
屋: 0x5c4b
バイト型はb’で囲まれる
\xは続く文字列
左端から指定した文字列などを抜き取る
str = '\npython\n' print(str.lstrip())
str = 'TOMATO'
print(str.lstrip('T'))
@classmethod
def parse(self, sec_bin):
if sec_bin[0] == 4:
x = int.from_bytes(sec_bin[1:33], 'big')
y = int.from_bytes(sec_bin[33:65], 'big')
return S256Point(x=x, y=y)
is_even = sec_bin[0] == 2
x = S256Field(int.from_bytes(sec_bin[1:], 'big'))
alpha = x**3 + S256Field(B)
beta = alpha.sqrt()
if beta.num % 2 == 0:
even_beta = beta
odd_beta = S256Field(P - beta.num)
else:
even_beta = S256Field(P - beta.num)
odd_beta = beta
if is_even:
return S256Point(x, even_beta)
else:
return S256Point(x, odd_beta)
公開鍵が04, 03, 02かで秘密鍵の点を戻す
SEC圧縮フォーマットは以下の通り
from ecc import S256Point, PrivateKey p = PrivateKey(5001) print(p.point.sec(compressed=True).hex())
クラスにくっついている関数のようなもので、インスタンス化していないクラスのものから呼び出せる。
メソッドに@classmethodと付けることでクラスメソッドにできる。
class A:
def test_method():
print("test")
@classmethod
def my_cls_method(cls):
print("hello")
A.my_cls_method()
A.test_method()
– 第一引数でクラスが取得できる(インスタンスメソッドは第一引数が必ずselfになる)
– クラスの中にあるので、クラスをインポートすれば使える
– クラスメソッドを使わずに関数として書くこともできるが、クラスメソッドの場合は、インポートできて、まとめて管理できる
class Item:
def __init__(self, id, name):
self.id = id
self.name = name
@classmethod
def retrieve_from_api(cls, id):
res = requests.get(f"https://api.example.com/items/{id}")
data = res.json()
return cls(id, data["name"])
楕円曲線暗号の公開鍵は(x, y)形式の一つの座標
ECDSA公開鍵をシリアライズする方法はSECフォーマット(Standards for Efficient Cryptography)と呼ばれる
1. プレフィックスバイトは0x04
2. 32バイトのビッグエンディアン整数としてx座標を追加
3. 32バイトのビッグエンディアン整数としてy座標を追加
圧縮SECフォーマットの場合、yが偶数の場合は0x02とし、奇数の場合は0x03とする。
def sec(self, compressed=True):
if compressed:
if self.y.num % 2 == 0:
return b'\x02' + self.x.num.to_bytes(32, 'big')
else:
return b'\x03' + self.x.num.to_bytes(32, 'big')
else:
return b'\x04' + self.x.num.to_bytes(32, 'big') + self.y.num.to_bytes(32, 'big')
from ecc import S256Point, PrivateKey pkey = PrivateKey(5000) p = pkey.point.sec(compressed=False).hex() print(p)
$ python3 main.py
04ffe558e388852f0120e46af2d1b370f85854a8eb0841811ece0e3e03d282d57c315dc72890a4f10a1481c031b03b351b0dc79901ca18a00cf009dbdb157a1d10
import binascii hex_b = 'f0148c' bytes_be = binascii.unhexlify(hex_b) bytes_le = bytes_be[::-1] hex_le = binascii.hexlify(bytes_le).decode() print(hex_le)
import sys
def dump(data):
print(data)
a = int.from_bytes(data, byteorder='big')
b = int.from_bytes(data, byteorder='little')
c = int.from_bytes(data, byteorder=sys.byteorder)
print(a, hex(a))
print(b, hex(b))
print(c, hex(c))
dump(b'\x01\x02')
dump(b'\x11\x12\x13\x14\x15\x16\x17\x18\x19')
$ python3 test.py
b’\x01\x02′
258 0x102
513 0x201
513 0x201
b’\x11\x12\x13\x14\x15\x16\x17\x18\x19′
314897056051100063769 0x111213141516171819
462904482303900324369 0x191817161514131211
462904482303900324369 0x191817161514131211
シリアライズとは、複数の要素を一列に並べる操作や処理のこと。単にシリアライズといった場合には、プログラムの実行状態や複雑なデータ構造などを一つの文字列やバイト列で表現する「直列化」を指すことが多い。
class PrivateKey:
def __init__(self, secret):
self.secret = secret
self.point = secret * G
def hex(self):
return '{:x}'.format(self.secret).zifll(64)
class PrivateKey:
def __init__(self, secret):
self.secret = secret
self.point = secret * G
def hex(self):
return '{:x}'.format(self.secret).zifll(64)
def sign(self, z):
k = randint(0, N-1)
r = (k*G).x.num
k_inv = pow(k, N-1, N)
s = (z + r*self.secret) * k_inv % N
if s > N/2
s = N - s
return(r, s)
kがrandintではなく、一意であるようにする。
def sign(self, z):
k = self.deterministic_k(z)
r = (k*G).x.num
k_inv = pow(k, N-1, N)
s = (z + r*self.secret) * k_inv % N
if s > N/2
s = N - s
return(r, s)
def deterministic_k(self, k):
k = b'\x00' * 32
v = b'\x01' * 32
if z > N:
z -= N
z_bytes = z.to_bytes(32, 'big')
secret_bytes = self.secret.to_bytes(32, 'big')
s256 = hashlib.sha256
k = hmac.new(k, v + b'\x00' + secret_bytes + z_bytes, s256).giest()
v = hmac.new(k, v, s256).digest()
k = hmac.new(k, v + b'\x01' + secret_bytes + z_bytes, s256).giest()
v = hmac.new(k, v, s256).digest()
while True:
v = hmac.new(k, v, s256).digest()
candidate = int.from_bytes(v, 'big')
if candidate >= 1 and candidate < N:
return candidate
k = hmac.new(k, v + b'\x00', s256).giest()
v = hmac.new(k, v, s256).digest()