2026年1月15日 星期四

RFID 系統的Stream Cipher(串流加密)

  RFID 系統的Stream Cipher(串流加密)

在 RFID 系統中,由於標籤(Tag)的運算資源有限,通常會採用 Stream Cipher(串流加密)。其原理是利用一個「金鑰(Key)」與「初始向量(IV)」生成一段無限長的偽隨機位元流(Key Stream),再與原始資料(Plaintext)進行 XOR(互斥或) 運算。

以下是一個使用 tkinter 實作的 RFID Stream Cipher 模擬器。我選擇模擬經典的 LSFR(線性回饋移位暫存器) 邏輯,這是許多 RFID 加密(如 Mifare Crypto1)的基礎。


Stream Cipher 在 RFID 中的關鍵原理

  1. 為什麼用 Stream Cipher?

    RFID 標籤的電路非常小,無法執行像 RSA 或複雜 AES 那樣的區塊加密(Block Cipher)。Stream Cipher 只需要簡單的暫存器移位(Shift)和 XOR 閘,非常省電且快速。

  2. LSFR (線性回饋移位暫存器):

    這是程式中 generate_keystream 的核心。它透過少量的邏輯閘產生看起來很亂的數列。只要 Reader 和 Tag 擁有相同的 Seed (金鑰),他們就能生成一模一樣的數列。

  3. 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)」進行即時解密,否則辨識就會失敗。



    import tkinter as tk
    from tkinter import ttk
    import random
    import time
    from threading import Thread, Event

    # --- 加密核心類別 ---
    class RFIDCipher:
        @staticmethod
        def process(hex_data, hex_key):
            try:
                # Hex to Bin
                clean_hex = hex_data.replace(" ", "")
                plain_bin = bin(int(clean_hex, 16))[2:].zfill(len(clean_hex) * 4)
                key_bin = bin(int(hex_key, 16))[2:].zfill(len(hex_key.replace(" ","")) * 4)
                
                # 簡易 LSFR 生成 KeyStream
                state = [int(b) for b in key_bin]
                keystream = []
                for _ in range(len(plain_bin)):
                    out = state[-1]
                    feedback = state[0] ^ state[2]
                    state = [feedback] + state[:-1]
                    keystream.append(out)
                
                # XOR 運算
                cipher_bits = [int(plain_bin[i]) ^ keystream[i] for i in range(len(plain_bin))]
                cipher_bin = "".join(map(str, cipher_bits))
                
                # Bin to Hex
                hex_out = hex(int(cipher_bin, 2))[2:].upper().zfill(len(clean_hex))
                return " ".join(hex_out[i:i+2] for i in range(0, len(hex_out), 2))
            except:
                return "ERR"

    # --- 主程式 ---
    class EncryptedSuperTagApp:
        def __init__(self, root):
            self.root = root
            self.root.title("Encrypted RFID Super Tag 系統 (安全傳輸模擬)")
            
            self.system_key = "AC55"  # Reader 持有的金鑰
            self.tags = [
                {"id": "53 71 37 A4 F6", "label": "Tag-A", "identified": False},
                {"id": "12 AB 34 CD EF", "label": "Tag-B", "identified": False},
                {"id": "FF 00 FF 00 FF", "label": "Tag-C", "identified": False}
            ]
            
            self.is_running = False
            self.pause_event = Event()
            self.pause_event.set()
            
            self.setup_ui()

        def setup_ui(self):
            # 頂部:金鑰設定
            key_frame = tk.Frame(self.root, bg="#34495e", pady=10)
            key_frame.pack(fill="x")
            tk.Label(key_frame, text="Reader Key (Hex):", fg="white", bg="#34495e").pack(side=tk.LEFT, padx=10)
            self.ent_key = tk.Entry(key_frame, width=10, font=("Courier", 10))
            self.ent_key.insert(0, self.system_key)
            self.ent_key.pack(side=tk.LEFT)

            # 視覺化區域
            self.canvas = tk.Canvas(self.root, width=700, height=180, bg="#fdfdfd")
            self.canvas.pack(pady=10)
            self.tag_shapes = {}
            for i, tag in enumerate(self.tags):
                x = 150 + (i * 200)
                rect = self.canvas.create_rectangle(x-60, 40, x+60, 100, fill="#ecf0f1", width=2)
                name = self.canvas.create_text(x, 70, text=tag["label"], font=("Arial", 10, "bold"))
                data = self.canvas.create_text(x, 120, text=tag["id"], fill="gray", font=("Courier", 8))
                self.tag_shapes[tag["label"]] = {"rect": rect, "data": data}

            # 控制鈕
            btn_frame = tk.Frame(self.root)
            btn_frame.pack(pady=5)
            self.btn_run = tk.Button(btn_frame, text="▶ 啟動安全掃描", command=self.start_sim, bg="#2ecc71", fg="white", width=15)
            self.btn_run.pack(side=tk.LEFT, padx=5)
            tk.Button(btn_frame, text="⏸ 暫停/恢復", command=self.toggle_pause, width=12).pack(side=tk.LEFT, padx=5)
            tk.Button(btn_frame, text="⏹ 結束", command=self.stop_sim, bg="#e74c3c", fg="white", width=12).pack(side=tk.LEFT, padx=5)

            # 日誌
            self.tree = ttk.Treeview(self.root, columns=("Raw", "Encrypted", "Decrypted", "Status"), show="headings", height=6)
            for col in ("Raw", "Encrypted", "Decrypted", "Status"): self.tree.heading(col, text=col)
            self.tree.column("Status", width=100)
            self.tree.pack(fill="both", padx=10, pady=10)

        def toggle_pause(self):
            if self.pause_event.is_set(): self.pause_event.clear()
            else: self.pause_event.set()

        def stop_sim(self):
            self.is_running = False
            self.pause_event.set()

        def start_sim(self):
            if self.is_running: return
            self.is_running = True
            self.system_key = self.ent_key.get()
            for t in self.tags: t["identified"] = False
            for item in self.tree.get_children(): self.tree.delete(item)
            Thread(target=self.logic, daemon=True).start()

        def logic(self):
            while self.is_running and any(not t["identified"] for t in self.tags):
                self.pause_event.wait()
                
                # 模擬標籤隨機決定發送
                active_list = [t for t in self.tags if not t["identified"] and random.random() < 0.4]
                
                if len(active_list) == 1:
                    tag = active_list[0]
                    # 1. 標籤端加密
                    encrypted_data = RFIDCipher.process(tag["id"], self.system_key)
                    
                    # 2. Reader 端解密
                    decrypted_data = RFIDCipher.process(encrypted_data, self.system_key)
                    
                    # 更新 UI
                    self.update_tag_ui(tag["label"], "#2ecc71")
                    self.log_res(tag["id"], encrypted_data, decrypted_data, "SUCCESS")
                    tag["identified"] = True
                    time.sleep(1.5)
                elif len(active_list) > 1:
                    for t in active_list: self.update_tag_ui(t["label"], "#e74c3c")
                    self.log_res("---", "CONFLICT", "---", "COLLISION")
                    time.sleep(1.5)
                    for t in active_list: self.update_tag_ui(t["label"], "#ecf0f1")
                
                time.sleep(0.5)
            self.is_running = False
            self.root.after(0, lambda: self.btn_run.config(state=tk.NORMAL))

        def update_tag_ui(self, label, color):
            self.root.after(0, lambda: self.canvas.itemconfig(self.tag_shapes[label]["rect"], fill=color))

        def log_res(self, r, e, d, s):
            self.root.after(0, lambda: self.tree.insert("", 0, values=(r, e, d, s)))

    if __name__ == "__main__":
        root = tk.Tk()
        app = EncryptedSuperTagApp(root)
        root.mainloop()

    沒有留言:

    張貼留言

    RFID 系統的Stream Cipher(串流加密)

      RFID 系統的Stream Cipher(串流加密) 在 RFID 系統中,由於標籤(Tag)的運算資源有限,通常會採用 Stream Cipher(串流加密) 。其原理是利用一個「金鑰(Key)」與「初始向量(IV)」生成一段無限長的偽隨機位元流(Key Stream),...