【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/

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

一方向性ハッシュ関数 MD5, SHA, RIPEMD160

一方向性ハッシュ関数は任意の長さのデータを固定長のビット文字列に変換することのアルゴリズム
入力を受け取ったら、不可逆的に変換して出力する一方通行のプロセス
出力から入力を復元することは不可能

アルゴリズムのうちの2つがMD5とSHA
MD5は暗号的に壊れており、衝突の原因になると考えられている
一方SHAはNIST(National Institute of Standards and Technology)が開発した暗号ハッシュ関数のファミリーをさす。SHAにはSHA256やSHA512などのバージョンがあり、全体としてMD5よりも安全性が高いとされている。

### MD5とは
MD5とはMessage-Digest Algorithmの略。128ビットのハッシュ値を生成することができる。
多くの脆弱性を引き起こしたが、データの完全性を検証するためのチェックサムとして使用することは可能
また、パーティション分割されたデータベースから特定の鍵のパーティションを見つけるなど、非暗号的なタスクにも適している

$text = "It's sunny today.";
echo md5($text);

### SHAとは
SHAとは、Secure Hash Algorithmの略。
SHA-0: 160ビットのハッシュ関数オリジナルバージョン
SHA-1: 160ビットのハッシュ関数 ※2010年以降は使用されていない
SHA-2: ブロックサイズの異なる2つの等価ハッシュ関数から構成される
SHA-256: 32ビットワード
SHA-523: 64ビットワード

$text = "It's sunny today.";
echo hash('sha256', $text);

### RIPEMD
入力データのサイズに関わらず、160ビットのハッシュ値を生成する。SHA-256より小さいためデータサイズを節約できる
データとkeyを渡す

$text = "It's sunny today.";
echo hash_hmac('ripemd160', $text, 'secret');

OpenSSLを用いたデジタル署名

デジタル署名には2つの方法がある
– メッセージに署名するケース
– メッセージのハッシュ値に署名するケース

1. テスト用ファイルの作成
$ echo foobar > foo.txt

2. dsaパラメータの作成(Digital signature algorithm)
DSAパラメータを1セット作成し、PEMファイルに出力する。1024ビット長の素数を利用
$ openssl dsaparam 1024 -in /dev/random -out dsa_param.pem

3. プライベート鍵の作成
$ openssl gendsa dsa_param.pem -out dsa_private.pem

4. 公開鍵の作成
DSAプライベート鍵からDSA公開鍵を作成
$ openssl dsa -in dsa_private.pem -pubout -out dsa_public.pem

5. ダイジェストの作成
テキスト用ファイル(foo.txt)からダイジェストを作成する
$ openssl dgst -sha1 foo.txt | awk ‘{print $2}’ > foo.txt.sha1.sum

6. ダイジェストの署名
DSA秘密鍵を用いて、ダイジェストファイル(foo.txt.sha1sum)からデジタル署名を作成する
$ openssl dgst -dss1 -sign dsa_private.pem -out foo.txt.sig foo.txt.sha1sum

7.ダイジェストの検証
$ openssl dgst -dss1 -sign dsa_private.pem -out foo.txt.sig foo.txt.sha1sum

OpenSSLで秘密鍵、公開鍵の作成

RSAを用いて公開鍵暗号方式で暗号化、複合化を行う

1. 秘密鍵の作成
$ openssl genrsa -out private.pem 2048

※暗号化した秘密鍵の作成
$ openssl genrsa -out private_secure.pem -aes256 2048

2. 公開鍵の作成
$ openssl rsa -in private.pem -pubout -out public.pem

3. ファイルの準備
$ echo ‘hoge’ > file.txt

4. 公開鍵で暗号化
$ openssl rsautl -encrypt -inkey public.pem -pubin -in file.txt -out file.encrypted
$ hexdump file.encrypted

5. 秘密鍵で復号化
$ openssl rsautl -decrypt -inkey private.pem -in file.encrypted -out file.decrypted
$ cat file.decrypted
hoge

OpenSSLで共通鍵作成

1. 暗号化するデータの用意
$ echo hoge > file.txt

2. 共通鍵の作成
$ openssl rand -base64 32 > rand.txt
※-out rand.txtだとExtra arguments given.でエラーになる

3. データの暗号化
$ openssl enc -e -aes128 -kfile rand.txt -in file.txt -out file.aes128_64 -base64
$ cat file.aes128_64
U2FsdGVkX19QJLESvS5Z/08k+CWpOjXqe9GA08NuCzE=

4. データの複合化
$ openssl enc -d -aes128 -kfile rand.txt -in file.aes128_64 -base64
*** WARNING : deprecated key derivation used.
Using -iter or -pbkdf2 would be better.
hoge

### 初期ベクトルを指定する方法
-iv: 初期ベクトル
-K: 秘密鍵
$ mkdir src
$ echo hello > src/file.txt
$ openssl enc -aes-128-cbc -in src/file.txt -out src/file.txt.enc -K 1234567890abcdef0123456789abcdef -iv 00000000000000000000000000000000
$ openssl enc -d -aes-128-cbc -in src/file.txt.enc -out src/file.txt.decrypted -K 1234567890abcdef0123456789abcdef -iv 00000000000000000000000000000000
※AES-128の秘密鍵と初期ベクトルは32桁の128bit