RFID 系統的Stream Cipher(串流加密)
在 RFID 系統中,由於標籤(Tag)的運算資源有限,通常會採用 Stream Cipher(串流加密)。其原理是利用一個「金鑰(Key)」與「初始向量(IV)」生成一段無限長的偽隨機位元流(Key Stream),再與原始資料(Plaintext)進行 XOR(互斥或) 運算。
以下是一個使用 tkinter 實作的 RFID Stream Cipher 模擬器。我選擇模擬經典的 LSFR(線性回饋移位暫存器) 邏輯,這是許多 RFID 加密(如 Mifare Crypto1)的基礎。
Stream Cipher 在 RFID 中的關鍵原理
為什麼用 Stream Cipher?
RFID 標籤的電路非常小,無法執行像 RSA 或複雜 AES 那樣的區塊加密(Block Cipher)。Stream Cipher 只需要簡單的暫存器移位(Shift)和 XOR 閘,非常省電且快速。
LSFR (線性回饋移位暫存器):
這是程式中 generate_keystream 的核心。它透過少量的邏輯閘產生看起來很亂的數列。只要 Reader 和 Tag 擁有相同的 Seed (金鑰),他們就能生成一模一樣的數列。
XOR 的對稱性:
這是 Stream Cipher 的靈魂公式:
加密:Plaintext ⊕ Keystream = Ciphertext
解密:Ciphertext ⊕ Keystream = Plaintext
你會發現加密與解密使用的是同一個函數,這大幅減少了硬體設計的複雜度。
import tkinter as tk
from tkinter import ttk, messagebox
import random
class StreamCipherSimulator:
def __init__(self, root):
self.root = root
self.root.title("RFID Stream Cipher (LSFR) 模擬器")
self.root.geometry("600x550")
# 核心邏輯變數
self.key = "10110101" # 預設 8-bit 金鑰
self.iv = "1100" # 預設 4-bit IV
self.setup_ui()
def setup_ui(self):
# 標題
tk.Label(self.root, text="RFID 資料傳輸加密模擬", font=("Arial", 14, "bold")).pack(pady=10)
# 設定區
config_frame = tk.LabelFrame(self.root, text="加密設定 (Binary Only)", padx=10, pady=10)
config_frame.pack(fill="x", padx=20)
tk.Label(config_frame, text="金鑰 (Key):").grid(row=0, column=0, sticky="w")
self.ent_key = tk.Entry(config_frame, width=15)
self.ent_key.insert(0, self.key)
self.ent_key.grid(row=0, column=1, padx=5)
tk.Label(config_frame, text="初始向量 (IV):").grid(row=0, column=2, sticky="w")
self.ent_iv = tk.Entry(config_frame, width=10)
self.ent_iv.insert(0, self.iv)
self.ent_iv.grid(row=0, column=3, padx=5)
# 傳輸資料區
data_frame = tk.Frame(self.root, pady=10)
data_frame.pack(fill="x", padx=20)
tk.Label(data_frame, text="原始資料 (Plaintext):").pack(anchor="w")
self.ent_plain = tk.Entry(data_frame, font=("Courier", 12))
self.ent_plain.insert(0, "1010101011110000")
self.ent_plain.pack(fill="x", pady=5)
# 操作按鈕
btn_frame = tk.Frame(self.root)
btn_frame.pack(pady=10)
tk.Button(btn_frame, text="執行加密 (Encrypt)", command=self.process_cipher, bg="#2ecc71", fg="white", width=15).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="清除 (Reset)", command=self.reset_fields, width=10).pack(side=tk.LEFT, padx=5)
# 視覺化展示區
result_frame = tk.LabelFrame(self.root, text="串流加密過程展示", padx=10, pady=10)
result_frame.pack(fill="both", expand=True, padx=20, pady=10)
self.txt_log = tk.Text(result_frame, font=("Courier", 9), height=15, bg="#f4f4f4")
self.txt_log.pack(fill="both", expand=True)
def generate_keystream(self, key_str, length):
"""簡單的 LSFR 邏輯生成金鑰流"""
state = [int(b) for b in key_str]
keystream = []
for _ in range(length):
# 取出最後一位作為金鑰位元
out = state[-1]
# 簡單的回饋邏輯: bit0 XOR bit2
feedback = state[0] ^ state[2]
state = [feedback] + state[:-1]
keystream.append(out)
return keystream
def xor_logic(self, bits1, bits2):
return [b1 ^ b2 for b1, b2 in zip(bits1, bits2)]
def process_cipher(self):
plain_str = self.ent_plain.get()
key_str = self.ent_key.get()
# 檢查輸入是否為二進位
if not all(b in '01' for b in plain_str + key_str):
messagebox.showerror("錯誤", "請輸入二進位字串 (0 或 1)")
return
plain_bits = [int(b) for b in plain_str]
length = len(plain_bits)
# 1. 生成金鑰流
keystream = self.generate_keystream(key_str, length)
# 2. XOR 運算 (加密)
cipher_bits = self.xor_logic(plain_bits, keystream)
# 3. 再 XOR 一次 (解密測試)
decrypted_bits = self.xor_logic(cipher_bits, keystream)
# 顯示結果
self.txt_log.delete(1.0, tk.END)
self.txt_log.insert(tk.END, f"Step 1: 生成 Key Stream\n")
self.txt_log.insert(tk.END, f"Key: {key_str} -> Stream: {''.join(map(str, keystream))}\n\n")
self.txt_log.insert(tk.END, f"Step 2: 資料加密 (Plain XOR Stream)\n")
self.txt_log.insert(tk.END, f"Plain: {plain_str}\n")
self.txt_log.insert(tk.END, f"Stream: {''.join(map(str, keystream))}\n")
self.txt_log.insert(tk.END, f"Cipher: {''.join(map(str, cipher_bits))}\n\n")
self.txt_log.insert(tk.END, f"Step 3: 資料解密 (Cipher XOR Stream)\n")
self.txt_log.insert(tk.END, f"Cipher: {''.join(map(str, cipher_bits))}\n")
self.txt_log.insert(tk.END, f"Stream: {''.join(map(str, keystream))}\n")
self.txt_log.insert(tk.END, f"Result: {''.join(map(str, decrypted_bits))} (成功還原)\n")
if plain_bits == decrypted_bits:
self.txt_log.insert(tk.END, "\n[驗證成功] Stream Cipher 對稱性確認。")
def reset_fields(self):
self.txt_log.delete(1.0, tk.END)
self.ent_plain.delete(0, tk.END)
if __name__ == "__main__":
root = tk.Tk()
app = StreamCipherSimulator(root)
root.mainloop()
Hex to Binary 映射:
程式會將您的輸入 53 71 37 A4 F6 每一位 Hex 轉為 4 位 Binary(例如 5 $\rightarrow$ 0101, 3 $\rightarrow$ 0011),這樣才能進行位元等級的 XOR 運算。
LSFR 狀態初始化:
金鑰 AC55 會被當作 LSFR 的初始種子(Seed)。如果金鑰不同,生成的 Keystream 會完全不同。
XOR 操作視覺化:
在日誌視窗中,您可以看到:
Input Bin: 原始資料的二進位形式。
Cipher Hex: 加密後產生的全新 16 進位字串。
Decrypted: 將加密結果再次與同樣的 Key Stream 做 XOR,會精確回到
53 71 37 A4 F6。
import tkinter as tk
from tkinter import ttk, messagebox
class HexStreamCipherSimulator:
def __init__(self, root):
self.root = root
self.root.title("RFID Hex-based Stream Cipher 模擬器")
self.root.geometry("700x600")
# 初始金鑰 (16進位)
self.default_key = "AC55"
self.default_data = "53 71 37 A4 F6"
self.setup_ui()
def setup_ui(self):
tk.Label(self.root, text="RFID 16進位資料串流加密 (LSFR)", font=("Arial", 14, "bold")).pack(pady=10)
# 設定區
config_frame = tk.LabelFrame(self.root, text="加密金鑰設定 (Hex)", padx=10, pady=10)
config_frame.pack(fill="x", padx=20)
tk.Label(config_frame, text="Key (Hex):").pack(side=tk.LEFT)
self.ent_key = tk.Entry(config_frame, width=20, font=("Courier", 10))
self.ent_key.insert(0, self.default_key)
self.ent_key.pack(side=tk.LEFT, padx=5)
tk.Label(config_frame, text="(例如: AC55)", fg="gray").pack(side=tk.LEFT)
# 資料輸入區
data_frame = tk.Frame(self.root, pady=10)
data_frame.pack(fill="x", padx=20)
tk.Label(data_frame, text="原始資料 (Plaintext Hex):").pack(anchor="w")
self.ent_plain = tk.Entry(data_frame, font=("Courier", 12), fg="blue")
self.ent_plain.insert(0, self.default_data)
self.ent_plain.pack(fill="x", pady=5)
# 按鈕
btn_frame = tk.Frame(self.root)
btn_frame.pack(pady=5)
tk.Button(btn_frame, text="執行加密與轉換", command=self.process_cipher, bg="#2980b9", fg="white", width=20).pack()
# 詳細過程 Log
log_frame = tk.LabelFrame(self.root, text="加密轉換分析 (Binary Operations)", padx=10, pady=10)
log_frame.pack(fill="both", expand=True, padx=20, pady=10)
self.txt_log = tk.Text(log_frame, font=("Courier", 9), bg="#2c3e50", fg="#ecf0f1")
self.txt_log.pack(fill="both", expand=True)
def hex_to_bin(self, hex_str):
# 移除空格並轉為 2 進位字串
clean_hex = hex_str.replace(" ", "")
return bin(int(clean_hex, 16))[2:].zfill(len(clean_hex) * 4)
def bin_to_hex(self, bin_str):
# 2 進位轉回 16 進位並格式化
hex_val = hex(int(bin_str, 2))[2:].upper()
# 補齊長度
hex_val = hex_val.zfill(len(bin_str) // 4)
# 每兩個字元加一個空格
return " ".join(hex_val[i:i+2] for i in range(0, len(hex_val), 2))
def generate_keystream(self, key_bin, length):
state = [int(b) for b in key_bin]
keystream = []
for _ in range(length):
out = state[-1]
feedback = state[0] ^ state[2] # LSFR 回饋邏輯
state = [feedback] + state[:-1]
keystream.append(out)
return keystream
def process_cipher(self):
try:
raw_plain = self.ent_plain.get()
raw_key = self.ent_key.get()
# 1. 轉換為 Binary
plain_bin = self.hex_to_bin(raw_plain)
key_bin = self.hex_to_bin(raw_key)
# 2. 生成相同長度的 Key Stream
keystream_bits = self.generate_keystream(key_bin, len(plain_bin))
keystream_str = "".join(map(str, keystream_bits))
# 3. XOR 加密
cipher_bits = [int(plain_bin[i]) ^ keystream_bits[i] for i in range(len(plain_bin))]
cipher_bin_str = "".join(map(str, cipher_bits))
cipher_hex = self.bin_to_hex(cipher_bin_str)
# 4. 解密驗證
de_bits = [cipher_bits[i] ^ keystream_bits[i] for i in range(len(cipher_bits))]
de_hex = self.bin_to_hex("".join(map(str, de_bits)))
# 顯示結果
self.txt_log.delete(1.0, tk.END)
self.txt_log.insert(tk.END, f"[Input Hex]: {raw_plain}\n")
self.txt_log.insert(tk.END, f"[Input Bin]: {plain_bin}\n")
self.txt_log.insert(tk.END, "-"*50 + "\n")
self.txt_log.insert(tk.END, f"Key Stream (Bin): {keystream_str[:40]}...\n")
self.txt_log.insert(tk.END, "-"*50 + "\n")
self.txt_log.insert(tk.END, f"[Cipher Bin]: {cipher_bin_str}\n")
self.txt_log.insert(tk.END, f"[Cipher Hex]: {cipher_hex} (加密後結果)\n")
self.txt_log.insert(tk.END, "-"*50 + "\n")
self.txt_log.insert(tk.END, f"[Decrypted]: {de_hex} (解密後還原)\n")
if de_hex.replace(" ", "") == raw_plain.replace(" ", "").upper():
self.txt_log.insert(tk.END, "\n>>> 驗證成功:16進位資料一致。")
except Exception as e:
messagebox.showerror("格式錯誤", "請確保輸入為有效的 16 進位字元 (0-9, A-F)")
if __name__ == "__main__":
root = tk.Tk()
app = HexStreamCipherSimulator(root)
root.mainloop()
Encrypted Super Tag 模擬器。
在這個版本中,每個標籤都內建一個 StreamCipher 引擎。當標籤在 Slot 中發送資料時,它傳輸的是加密後的 16 進制碼;Reader 接收後,必須使用正確的「金鑰(Key)」進行即時解密,否則辨識就會失敗。



沒有留言:
張貼留言