CoCalc Shared FilesCrypytography / 00_Basics.ipynbOpen in CoCalc with one click!
Author: phonchi chung
Views : 7
Description: Jupyter notebook Crypytography/00_Basics.ipynb

Overview

學習的最佳途徑之一是實作。 在密碼學中,我們必須處理big number 和 high precision floating number,這兩者都不一定是程式語言native 支援的。這方面最早相關且完整的library可以回溯到支援橢圓曲線運算的函式庫,這個函式庫為 Implementing Elliptic Curve Cryptography這本書作者 Michael Rossing 開發的。

在 C 語言當中這類 library 並不少,像是 Miracl, PBC, OpenssL, Nacl, libsodium 等等都有非常豐富的支援。 另一方面, low level routine 也有很多library可以選擇像是 gmp, ntl, flint, gnugp 等等都很受歡迎。 這邊我們推薦從 Miracl 入門,因為他有豐富的document 且實作了許多學術論文的方法。

另一個相當不錯的專案為 LibTom 他的初衷便是開發給初學者,所以有搭配講解的書籍 12, 更進一步的你還可以從他github上的專案找到 tex 檔案來編譯成向 Tommath.pdf 等說明文件。

在這份教學文檔中,我們將會以python為主,主要原因是因為他的易讀和容易上手的特性可以幫助我們快速了解並實際應用密碼學在各領域中。雖然python有native支援 long integer。但是他在密碼學這方面的相關模組並不像其他領域例如 machine learning 或 network secruity 這麼豐富(這邊有相關探討)。 就我們所知, sympy 有提供一些 cryptography tools

如果你想進一步體驗更多相關 crypto 模組, sage 會是一個不錯的選擇, 有很多相關文件可以參閱

在這份教學中,我們將會使用 pycrtpocryptography 這兩個library為主,因為他們涵蓋的範圍最廣。 請參考pycrypto說明文件Cryptography說明文件

其他的 python binding 像是 pynacl 也很推薦,可以保持關注 !!

Pycrypto

Pycrypto是一個完整的密碼學library,不過只支援到2.7版本,我們來看一下如何在python中使用他來做加解密 (only pairing x)

python 3 可以用 http://pycryptodome.readthedocs.io/en/latest/src/introduction.html

In [159]:
from Crypto.Cipher import AES key = 'starpasswordhere' plaintext = 'Martinet is god!' print len(key), len(plaintext) encobj = AES.new(key, AES.MODE_ECB) # 128-bit 安全度的AES~ ciphertext = encobj.encrypt(plaintext) print len(ciphertext), ciphertext.encode('hex') # 印出好看用!
16 16 16 292138362368e0cdd76508781b3f3b82

你會發現 hex-encoded 的 ciphertext 長度為 32 characters。 注意當我們將他encode為 hex時, 我們實際上加倍了長度(因為兩個hex digit才能表示一個character),因此真正的ciphertext長度是 16 characters

通常我們I/O進來後習慣操作 raw bit,也就是會將要加密的東西轉換或hash到 raw bit,不過許多cryptography的探討也會先將其encode成base64格式(同樣被用於其他網路上許多應用), 見討論!

注意在此我們無法任意改變key和 plaintext的長度,這項限制在接下來的教學會慢慢放寬,我們將使用 KDF (key derivation function) 來解這個限制!!

In [160]:
import binascii key = 'starpasswordhere' ciphertext = binascii.unhexlify('292138362368e0cdd76508781b3f3b82') decobj = AES.new(key, AES.MODE_ECB) plaintext = decobj.decrypt(ciphertext) # Resulting plaintext print plaintext
Martinet is god!

我們可以用binascii這個library轉回ascii樣子~~

Cryptography

另外一個較新的library是cryptography (exactly the name!!)

我們做跟上述同樣的事,不過注意我們這邊使用hazmat下的function,作者不建議我們這樣使用,真正應用時必須小心使用。

事實上cryptography的邏輯是會先當我們配好一些密碼學處方,例如當我們要加密時他就有一種標準的處方可以使用,要完整性時也有標準的處方,這個設計邏輯跟Nacl十分類似!

In [166]:
from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes ) from cryptography.hazmat.backends import default_backend key = 'starpasswordhere' plaintext = 'Martinet is god!' encryptor = Cipher(algorithms.AES(key),modes.ECB(),backend=default_backend()).encryptor() ciphertext = encryptor.update(plaintext) + encryptor.finalize() print ciphertext.encode('hex')
292138362368e0cdd76508781b3f3b82
In [167]:
decryptor = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()).decryptor() plaintext = decryptor.update(ciphertext) #+ decryptor.finalize() finalized後不可再call~ print decryptor.update(ciphertext), plaintext
Martinet is god! Martinet is god!

BitVector

Python pip 中還有很多好用的library來幫助處理bit level的運算,像是BitVector

In [156]:
import sys import BitVector if BitVector.__version__ < '3.2': sys.exit("You need BitVector module of version 3.2 or higher" ) from BitVector import * message = "" # Initialize hashcode for the first block. Subsequetnly, the # output for each 512-bit block of the input message becomes # the hashcode for the next block of the message. h0 = BitVector(hexstring='67452301') h1 = BitVector(hexstring='efcdab89') h2 = BitVector(hexstring='98badcfe') h3 = BitVector(hexstring='10325476') h4 = BitVector(hexstring='c3d2e1f0') bv = BitVector(textstring = message) length = bv.length() bv1 = bv + BitVector(bitstring="1") length1 = bv1.length() howmanyzeros = (448 - length1) % 512 zerolist = [0] * howmanyzeros bv2 = bv1 + BitVector(bitlist = zerolist) bv3 = BitVector(intVal = length, size = 64) bv4 = bv2 + bv3 words = [None] * 80 for n in range(0,bv4.length(),512): block = bv4[n:n+512] words[0:16] = [block[i:i+32] for i in range(0,512,32)] for i in range(16, 80): words[i] = words[i-3] ^ words[i-8] ^ words[i-14] ^ words[i-16] words[i] << 1 a,b,c,d,e = h0,h1,h2,h3,h4 for i in range(80): if (0 <= i <= 19): f = (b & c) ^ ((~b) & d) k = 0x5a827999 elif (20 <= i <= 39): f = b ^ c ^ d k = 0x6ed9eba1 elif (40 <= i <= 59): f = (b & c) ^ (b & d) ^ (c & d) k = 0x8f1bbcdc elif (60 <= i <= 79): f = b ^ c ^ d k = 0xca62c1d6 a_copy = a.deep_copy() T = BitVector( intVal = (int(a_copy << 5) + int(f) + int(e) + int(k) + \ int(words[i])) & 0xFFFFFFFF, size=32 ) e = d d = c b_copy = b.deep_copy() b_copy << 30 c = b_copy b = a a = T h0 = BitVector( intVal = (int(h0) + int(a)) & 0xFFFFFFFF, size=32 ) h1 = BitVector( intVal = (int(h1) + int(b)) & 0xFFFFFFFF, size=32 ) h2 = BitVector( intVal = (int(h2) + int(c)) & 0xFFFFFFFF, size=32 ) h3 = BitVector( intVal = (int(h3) + int(d)) & 0xFFFFFFFF, size=32 ) h4 = BitVector( intVal = (int(h4) + int(e)) & 0xFFFFFFFF, size=32 ) message_hash = h0 + h1 + h2 + h3 + h4 hash_hex_string = message_hash.getHexStringFromBitVector() sys.stdout.writelines((hash_hex_string, "\n"))
da39a3ee5e6b4b0d3255bfef95601890afd80709
In [ ]: