【Python】kecchak256

kecchak256は、文字列をランダムな256ビットの16進数にマッピングする機能
ethereumで使用されている

$ pip3 install pysha3

import sha3

data = b'Hello, keccak256!'

keccak = sha3.keccak_256()
keccak.update(data)
hash_hex = keccak.hexdigest()

print("keccak256 hash:", hash_hex)

$ python3 keccak256.py
keccak256 hash: 0fa5c2f72a50589fff02f2f5105ba40d97c01e114df6fd00cc4b1fc0afbec43a

【Python】準同型暗号(Homomorphic Encryption)

号化されたままのデータに対して演算(加算や乗算)を行える暗号方式

from phe import paillier

# 鍵生成
public_key, private_key = paillier.generate_paillier_keypair()

# 平文データ
a = 10
b = 20

# 暗号化
enc_a = public_key.encrypt(a)
enc_b = public_key.encrypt(b)

# 暗号上での加算(a + b)
enc_sum = enc_a + enc_b

# 暗号上でのスカラー乗算(a * 5)
enc_mul = enc_a * 5

# 復号
dec_sum = private_key.decrypt(enc_sum)
dec_mul = private_key.decrypt(enc_mul)

print("平文 a =", a, ", b =", b)
print("復号後 (a + b) =", dec_sum)
print("復号後 (a * 5) =", dec_mul)

$ python3 paillier.py
平文 a = 10 , b = 20
復号後 (a + b) = 30
復号後 (a * 5) = 50

【Python】シュノア証明

シュノア署名(Schnorr signature)は、楕円曲線暗号や整数群の上で定義される、シンプルかつ安全なデジタル署名方式

import hashlib
import random

p = 23
q = 11
g = 2

x = random.randint(1, q - 1)
y = pow(g, x, p)
print("秘密鍵 x = ", x)
print("公開鍵 y = ", y)

def H(r, m):
    data = str(r) + m
    h = hashlib.sha256(data.encode()).hexdigest()
    return int(h, 16) % q

def schnorr_sign(m):
    k = random.randint(1, q - 1)
    r = pow(g, k, p)
    e = H(r, m)
    s = (k + x * e) % q
    return (e, s)

def schnorr_verify(m, signature):
    e, s = signature
    g_s = pow(g, s, p)
    y_e = pow(y, e, p)
    r_prime = (g_s * pow(y_e, -1, p)) % p
    e_prime = H(r_prime, m)
    return e == e_prime

message = "hello"
signature = schnorr_sign(message)
print("署名:", signature)

valid = schnorr_verify(message, signature)
print("署名の検証結果:", valid)

$ python3 schnorr.py
秘密鍵 x = 10
公開鍵 y = 12
署名: (5, 1)
署名の検証結果: True

【Python】ゼロ知識証明

ゼロ知識証明とは、証明者がその事実を提示するだけで、その事実の内容を隠すことができる証明方法
Zero-knowledge Proofとしてよく使われるのが、ハミルトングラフの知識を持っていることや秘密のパスワードを証明せず知っていることを示す。

xを知っていおり、 y = x^2 mod p を示す。

import random

p = 23
x = 5
y = pow(x, 2, p)

print(" y =", y)

def prover_step():
    r = random.randint(1, p - 1)
    a = pow(r, 2, p)
    return r, a

def verifier_challenge():
    return random.randint(0, 1)

def prover_response(r, c):
    if c == 0:
        return r
    else:
        return (r * x) % p

def verifier_check(a, z, c):
    if c == 0:
        return pow(z, 2, p) == a
    else:
        return pow(z, 2, p) == (a * y) % p

r, a = prover_step()
print("Prover: 提出 a=", a)

c = verifier_challenge()
print("Verifier: チャレンジ c=", c)

z = prover_response(r, c)
print("Prover: 応答 z =", z)

ok = verifier_check(a, z, c)
print("Verifier: 検証結果 =", ok)

$ python3 zkp.py
y = 2
Prover: 提出 a= 4
Verifier: チャレンジ c= 1
Prover: 応答 z = 10
Verifier: 検証結果 = True

—– 実際の計算
r = ?,
a = 4,
c = 1
z = 10
10 * 10 % 23 = 5 * 2 / 23

x の値を知らせずに、y = x^2 mod p を証明している。証明の際には、c != 0 の時にのみyの値を利用する
zkpの概念は理解できたが、なぜこれが証明になるのかのロジックはよくわからない..

【Rust】base64エンコード/デコード(2回目)

これ、何回やっても忘れてしまうな…

base64= “0.22.1”

use base64::prelude::*;

fn main() {
    let ip = "192.168.33.10".to_string();
    println!("{}", base64_str(&ip));
    let str = "MTkyLjE2OC4zMy4xMA==".to_string();
    println!("{}", base64_decode(&str));
}

fn base64_str(str: &String) -> String {
    BASE64_STANDARD.encode(&str)
}

fn base64_decode_bytes(b64str: &str) -> Vec<u8> {
    let t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    let mut table: [u8; 256] = [0; 256]; 
    for (i, v) in t.as_bytes().iter().enumerate() {
        table[*v as usize] = i as u8; 
    }
    let b64 = String::from(b64str).replace("\r", "").replace("\n", "");
    let b64bytes = b64.as_bytes();
    let mut result: Vec<u8> = vec![];
    let cnt = b64bytes.len() / 4;
    for i in 0..cnt {
        let i0 = b64bytes[i*4+0];
        let i1 = b64bytes[i*4+1];
        let i2 = b64bytes[i*4+2];
        let i3 = b64bytes[i*4+3];
        let c0 = table[i0 as usize] as usize;
        let c1 = table[i1 as usize] as usize;
        let c2 = table[i2 as usize] as usize;
        let c3 = table[i3 as usize] as usize;
        let b24 = (c0 << 18) | (c1 << 12) | (c2 <<  6) | (c3 <<  0);
        let b0 = ((b24 >> 16) & 0xFF) as u8;
        let b1 = ((b24 >>  8) & 0xFF) as u8;
        let b2 = ((b24 >>  0) & 0xFF) as u8;
        result.push(b0);
        if i2 as char != '=' { result.push(b1); }
        if i3 as char != '=' { result.push(b2); }
    }
    result
}

fn base64_decode(b64str: &str) -> String {
    String::from_utf8(base64_decode_bytes(b64str)).unwrap()
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.51s
Running `target/debug/app`
MTkyLjE2OC4zMy4xMA==
192.168.33.10

[bitcoin基礎技術] sha256, ripemd160, HMAC-SHA512, PBKDF2

### SHA-256
常に32bytesで出力される
$ echo -n abcdefghijklmn | openssl dgst -sha256
SHA2-256(stdin)= 0653c7e992d7aad40cb2635738b870e4c154afb346340d02c797d490dd52d5f9
-n は改行出力を抑止
$ echo -n a | openssl dgst -sha256
SHA2-256(stdin)= ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb

### RIPEMD-160
160bit 20bytes
$ echo -n a | openssl dgst -rmd160
Error setting digest
2070E0ADFFFF0000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:../crypto/evp/evp_fetch.c:349:Global default library context, Algorithm (RIPEMD160 : 99), Properties ()
2070E0ADFFFF0000:error:03000086:digital envelope routines:evp_md_init_internal:initialization error:../crypto/evp/digest.c:254:

$ apt list openssl
Listing… Done
openssl/jammy-updates,jammy-security,now 3.0.2-0ubuntu1.12 arm64 [installed,automatic]

ubuntu22.04でopenssl3.0.2だと ripemd160が使えないらしい

### ダブルハッシュ
$ echo -n a | xxd -r -p | openssl dgst -sha256 | openssl dgst -rmd160
xxd -r -pでバイナリデータに変換する
$ echo -n a | xxd -r -p | openssl dgst -sha256 | openssl dgst -sha256
SHA2-256(stdin)= e877d81a8f216a6aebe39b3ca7b350ad38db6d8ea6ad7e3fe581a8a1ab09eeee

### HMAC-SHA512
HMAC-SHA512とはキーとデータのペアを入力してハッシュ化する処理のこと
SHA-512を使用する
$ echo -n “what do ya want for nothing?” | openssl dgst -sha512 -hmac “Jefe”
SHA2-512(stdin)= 164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737
キーを16進数で指定する場合は以下の通り
$ echo -n “what do ya want for nothing?” | openssl dgst -sha512 -mac HMAC -macopt hexkey:4a656665
SHA2-512(stdin)= 164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737

ワンライナーで表示
$ echo -n “what do ya want for nothing?” | openssl dgst -sha512 -mac HMAC -macopt hexkey:`echo -n Jefe | xxd -p`
$ echo -n “what do ya want for nothing?” | openssl dgst -sha512 -mac HMAC -macopt hexkey:$(echo -n Jefe | xxd -p)
hmac=`echo -n “what do ya want for nothing?” | openssl dgst -sha512 -mac HMAC -macopt hexkey:$(echo -n Jefe | xxd -p)`; echo $hmac

### PBKDF2
一般的にパスワードのハッシュ化に利用される
ビットコインではニモニックコードからシードを生成する際に利用される。
PBKDF2ではキーとデータを入力し、使用するハッシュ関数とストレッチングの回数を決めておく

import hashlib, binascii
hashFunc="sha512"
password=b"passw0rd"
salt=b"bitcoin"
iteCnt=100
encPass=hashlib.pbkdf2_hmac(hashFunc, password, salt, iteCnt)
print(binascii.hexlify(encPass))

$ python3 main.py
b’65252733b1f1d6e5abd4474d696989641125bedb34be2c7789eef454abac6e718180666c73e0ec44ed529291e3b6e5b22d4222b263c259aa0128ad9f5e1cd2c2′

【Python】RC4の実装

def KSA(key):
	S = list(range(256))
	j = 0
	for i in range(256):
		j = (j + S[i] + ord(key[i % len(key)])) % 256
		S[i], S[j] = S[j], S[i]
	return S

def PRGA(S):
	i,j = 0, 0
	while True:
		i = (i + 1) % 256
		j = (j + S[i]) % 256
		S[i], S[j] = S[j], S[i]
		K = S[(S[i] + S[j]) % 256]
		yield K

def RC4(data, key):
	S = KSA(key)
	gen = PRGA(S)
	data = bytearray(data, encoding='utf-8')
	result = bytearray(c ^ n for c, n in zip(data, gen))
	return str(result)

key = 'this_is_a_key'
message = 'this_is_a_message'

chiphertext = RC4(message, key)
print(chiphertext)

これでもいける

from Crypto.Cipher import ARC4
key = b'Very long and confidential key'
cipher = ARC4.new(key)
msg = cipher.encrypt(b'Open the pod bay doors, HAL')
print(msg)

【Python】DESを実装する

$ pip3 install base32hex

import base32hex
import hashlib
from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes

password = "Password"
salt = '\x28\xAB\xBC\xCD\xDE\xEF\x00\x33'
key = password + salt
m = hashlib.md5(key)
key = m.digest()
(dk, iv) = (key[:8], key[8:])
crypter = DES.new(dk, DES.MODE_CBC, iv)

plain_text = "hello world"

print("The plain text is:", plain_text)
plain_text += '\x00' * (8 - len(plainn_text) % 8)
ciphertext = crypter.encrypt(plain_text)
encode_string = base32hex.b32encode(ciphertext)
print("the encoded string is : ", encode_string)

Traceback (most recent call last):
File “aes.py”, line 9, in
m = hashlib.md5(key)
TypeError: Unicode-objects must be encoded before hashing

pyDesを使用します。
$ pip3 install pyDes
https://github.com/twhiteman/pyDes

from pyDes import *

data = "Please encrypt my data"
k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
d = k.encrypt(data)
print(d)
print(k.decrypt(d))

b’o\xceSef\xe6\xa6\x8f\x82\x98\xc7V\xd0I\xdc\x03\x1e\x97\xe4\x99&\x07\x9cI’
b’Please encrypt my data’

【Python】AESを実装しよう

pycryptodomeをインストールします
$ pip3 install pycryptodome

from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util import Padding
from hashlib import pbkdf2_hmac

targetText = "hello world"

salt = get_random_bytes(16)
iv = get_random_bytes(16)

passPhrase = "password123"

key = pbkdf2_hmac('sha256', bytes(passPhrase, encoding='utf-8'), salt, 50000, int(128/8))

aes = AES.new(key, AES.MODE_CBC, iv)
data = Padding.pad(targetText.encode('utf-8'), AES.block_size, 'pkcs7')
encrypted = aes.encrypt(data)
print(encrypted)

$ python3 aes.py
b’\x86\xb4V\x7f\xfaN\x83\xc9\x87\x02\xd1\x15z\x19bU’

パスワードマネージャーの機能

パスワードマネージャーの便利機能
– パスワード保存機能: パスワード管理
– パスワード自動生成: パスワードの強化
– ワンタイムパスワードの発行
– ログイン操作の入力補助
– エクスポートとインポート
– 端末間の同期: PC、スマホで利用できる

e.g.
RoboForm: https://www.roboform.com/jp
L Chrome Extensionで管理 
Bitwarden: https://bitwarden.com/ja-JP/
True Key: https://www.truekey.com/ja
L McAfeeが運営している
1Password: https://1password.com/jp
Keeper: https://www.keepersecurity.com/ja_JP
パスメモ(日本システムウエア株式会社が運営)

データ漏洩絵事件で公開されたパスワードを管理するデータベース
Have I Been Pwned (HIBP)
https://haveibeenpwned.com/

パスワード生成よりもパスワード保存、一括管理が機能的に必要そう