2025年11月9日 星期日

握手與認證過程(三步認證)

握手與認證過程(三步認證)

在讀寫器和 MIFARE Classic 卡片開始交換加密數據之前,它們必須先進行三步認證 (Three-Pass Authentication) 過程,這個過程同時也是密鑰狀態初始化的過程:

  1. 讀卡器挑戰 (Reader Challenge): 讀卡器(如 MFRC522)發送一個隨機數(Nonce)給卡片。

  2. 卡片回應 (Card Response): 卡片使用其內部狀態和密鑰,對讀卡器的挑戰進行加密運算並返回一個回應。

  3. 讀卡器驗證並最終確定狀態: 讀卡器和卡片分別進行一系列的加密和狀態更新,最終兩者都必須達到相同的內部 LFSR 狀態。

  • 只有當雙方都擁有相同的 48 位元密鑰,且認證過程成功,LFSR 才會初始化到相同的、用於通訊的狀態。

  • 認證成功後,所有的數據交換都會使用 CRYPTO1 算法產生的密鑰串流進行 XOR 運算來加密和解密。

4. 🔒 安全性與現狀


應用程式旨在視覺化地解釋三步認證的流程和核心目的:

核心目標:密鑰驗證 與 串流加密狀態同步

**「三步認證」**並不是為了加密數據,而是為了達成兩個目的:

  1. 密鑰驗證 (Mutual Authentication): 驗證讀卡器和卡片是否都擁有相同的 48 位元密鑰。

  2. 狀態同步 (LFSR Synchronization): 如果密鑰匹配,則讀卡器和卡片內部的 48 位元 LFSR 狀態會被初始化到一個相同的、隨機的新狀態。從此時起,這個同步的 LFSR 才能開始產生密鑰串流,用於後續的數據加密。

步驟誰發送 / 誰接收內容核心目的
步驟 1讀卡器 $\to$ 卡片NonceA (隨機數)讀卡器挑戰卡片。卡片接收後,將其與自己的密鑰和卡片 ID 等資訊,初始化卡片的 LFSR
步驟 2卡片 $\to$ 讀卡器ResponseB (加密後的數據) + NonceB (卡片隨機數)卡片證明自己擁有密鑰(透過 ResponseB)。讀卡器接收 NonceB 後,將其與自己的密鑰和 NonceA 等資訊,初始化讀卡器的 LFSR
步驟 3讀卡器 $\to$ 卡片ResponseA (讀卡器最終回應)讀卡器生成 ResponseA,並用它來驗證卡片在步驟 2 中發送的 ResponseB 是否正確。如果驗證成功,代表:雙方密鑰相同,且 LFSR 狀態已同步。

模擬中的「驗證點」:

在程式碼中,驗證點發生在 步驟 3。如果用戶在密鑰輸入框中將「讀卡器密鑰」和「卡片密鑰」設定成不同的值,則模擬器會判斷「驗證失敗」,因為這兩方無法在複雜的 CRYPTO1 運算中得出預期的響應。



 

import tkinter as tk

from tkinter import messagebox

import random

import time


# 模擬的 CRYPTO1 Key (48 bits / 6 bytes HEX)

# 認證的基礎是雙方擁有相同的密鑰

SHARED_SECRET_KEY = "AABBCCDDEEFF" 


class ThreePassAuthApp:

    def __init__(self, master):

        self.master = master

        master.title("MIFARE Classic 三步認證模擬")

        

        # 狀態變數

        self.reader_key = tk.StringVar(value=SHARED_SECRET_KEY)

        self.card_key = tk.StringVar(value=SHARED_SECRET_KEY)

        self.current_step = 0

        self.log_text = None # 初始化為 None

        self.lfsr_state_r = None # 模擬讀卡器 LFSR 狀態

        self.lfsr_state_c = None # 模擬卡片 LFSR 狀態


        self.setup_ui()

        self.log_message("--- 認證流程開始 ---", color='blue')

        self.log_message(f"讀卡器/卡片共享密鑰: {SHARED_SECRET_KEY}")

        

    def setup_ui(self):

        # 密鑰設定區

        key_frame = tk.LabelFrame(self.master, text="🔐 共享密鑰設置 (48 bits HEX)", padx=5, pady=5)

        key_frame.pack(pady=10, padx=10, fill='x')

        

        tk.Label(key_frame, text="讀卡器密鑰:").grid(row=0, column=0, padx=5, pady=2, sticky='w')

        tk.Entry(key_frame, textvariable=self.reader_key, width=20).grid(row=0, column=1, padx=5, pady=2, sticky='w')

        

        tk.Label(key_frame, text="卡片密鑰:").grid(row=1, column=0, padx=5, pady=2, sticky='w')

        tk.Entry(key_frame, textvariable=self.card_key, width=20).grid(row=1, column=1, padx=5, pady=2, sticky='w')

        

        tk.Button(key_frame, text="重設密鑰", command=self.reset_keys).grid(row=0, column=2, rowspan=2, padx=10)


        # 步驟按鈕區

        btn_frame = tk.Frame(self.master)

        btn_frame.pack(pady=10)

        

        self.step_btn = tk.Button(btn_frame, text="▶️ 執行下一步 (步驟 0)", command=self.next_step, bg='lightgreen', width=20)

        self.step_btn.pack(side=tk.LEFT, padx=10)

        

        tk.Button(btn_frame, text="🔄 重啟流程", command=self.reset_flow).pack(side=tk.LEFT, padx=10)


        # 狀態與 LFSR 模擬

        status_frame = tk.LabelFrame(self.master, text="✨ 模擬狀態", padx=5, pady=5)

        status_frame.pack(pady=5, padx=10, fill='x')

        

        self.lfsr_state_r = tk.StringVar(value="讀卡器 LFSR: (未初始化)")

        self.lfsr_state_c = tk.StringVar(value="卡片 LFSR: (未初始化)")

        

        tk.Label(status_frame, textvariable=self.lfsr_state_r, fg='darkgreen').pack(anchor='w')

        tk.Label(status_frame, textvariable=self.lfsr_state_c, fg='darkred').pack(anchor='w')

        

        # 日誌區

        tk.Label(self.master, text="認證日誌 (Authentication Log):").pack(pady=5)

        self.log_text = tk.Text(self.master, height=15, width=60, state=tk.DISABLED)

        self.log_text.pack(padx=10, pady=5)

        

        tk.Button(self.master, text="❌ 結束", command=self.master.quit).pack(pady=10)


    def log_message(self, message, color='black'):

        """向日誌區添加帶顏色的訊息。"""

        self.log_text.config(state=tk.NORMAL)

        self.log_text.insert(tk.END, message + "\n", color)

        self.log_text.tag_config(color, foreground=color)

        self.log_text.see(tk.END)

        self.log_text.config(state=tk.DISABLED)


    def generate_random_hex(self, length=8):

        """生成指定長度的隨機 HEX 字串 (模擬 Nonce)。"""

        return format(random.getrandbits(length * 4), f'0{length}X')


    def reset_keys(self):

        """將密鑰重設為預設值。"""

        self.reader_key.set(SHARED_SECRET_KEY)

        self.card_key.set(SHARED_SECRET_KEY)

        self.reset_flow(message="密鑰已重設為相同值。")


    def reset_flow(self, message="認證流程已重啟。"):

        """重設整個認證流程。"""

        self.current_step = 0

        self.lfsr_state_r.set("讀卡器 LFSR: (未初始化)")

        self.lfsr_state_c.set("卡片 LFSR: (未初始化)")

        self.step_btn.config(text="▶️ 執行下一步 (步驟 0)", state=tk.NORMAL, bg='lightgreen')

        

        self.log_text.config(state=tk.NORMAL)

        self.log_text.delete("1.0", tk.END)

        self.log_text.config(state=tk.DISABLED)

        

        self.log_message("--- 認證流程重啟 ---", color='blue')

        self.log_message(message, color='black')


    def next_step(self):

        """處理三步認證的每一步。"""

        self.current_step += 1

        reader_key = self.reader_key.get()

        card_key = self.card_key.get()


        if self.current_step == 1:

            # 步驟 1: 讀卡器挑戰 (Reader Challenge)

            nonce_a = self.generate_random_hex(8) # 讀卡器 Nonce (8 bytes / 32 bits)

            self.log_message(f"[步驟 1/3] 讀卡器 -> 卡片", color='green')

            self.log_message(f"  發送隨機數 (NonceA): {nonce_a}", color='green')

            

            # 模擬:卡片接收 NonceA,並使用 KeyC 開始初始化 LFSR

            sim_lfsr_state_c = self.generate_random_hex(6) # 模擬 LFSR 狀態

            self.lfsr_state_c.set(f"卡片 LFSR: 初始化中 ({sim_lfsr_state_c}... )")

            

            self.step_btn.config(text="▶️ 執行下一步 (步驟 1)")


        elif self.current_step == 2:

            # 步驟 2: 卡片回應與挑戰 (Card Response & Challenge)

            

            # 模擬:卡片根據 NonceA 和 KeyC 進行 CRYPTO1 加密運算,得出回應和自己的挑戰 NonceB

            nonce_b = self.generate_random_hex(4) # 卡片 Nonce

            response_b = self.generate_random_hex(6) # 模擬加密後的響應


            self.log_message(f"[步驟 2/3] 卡片 -> 讀卡器", color='red')

            self.log_message(f"  卡片回應 (ResponseB): {response_b}", color='red')

            self.log_message(f"  發送卡片隨機數 (NonceB): {nonce_b}", color='red')

            

            # 模擬:讀卡器接收 NonceB 和 ResponseB,並使用 KeyR 開始初始化 LFSR

            sim_lfsr_state_r = self.generate_random_hex(6)

            self.lfsr_state_r.set(f"讀卡器 LFSR: 初始化中 ({sim_lfsr_state_r}... )")

            

            self.step_btn.config(text="▶️ 執行下一步 (步驟 2)")


        elif self.current_step == 3:

            # 步驟 3: 讀卡器驗證與最終回應 (Reader Verification & Final Response)


            # 模擬:讀卡器使用 NonceB 和 KeyR 進行 CRYPTO1 加密運算,生成 ResponseA

            response_a = self.generate_random_hex(6)

            

            # 驗證環節 (核心!)

            if reader_key == card_key:

                # 密鑰匹配,認證成功

                auth_result = "SUCCESS"

                result_color = "purple"

                self.log_message(f"[步驟 3/3] 讀卡器驗證成功!", color='green')

                self.log_message(f"  最終 LFSR 狀態同步,讀卡器發送最終響應。", color='green')

                

                # 模擬最終同步狀態

                final_state = self.generate_random_hex(6)

                self.lfsr_state_r.set(f"讀卡器 LFSR: 已同步 ({final_state})")

                self.lfsr_state_c.set(f"卡片 LFSR: 已同步 ({final_state})")

                

                messagebox.showinfo("認證結果", "🎉 三步認證完成!密鑰匹配,連線已加密!")

                self.step_btn.config(text="✅ 認證完成", state=tk.DISABLED, bg='gray')


            else:

                # 密鑰不匹配,認證失敗

                auth_result = "FAILED"

                result_color = "red"

                self.log_message(f"[步驟 3/3] 讀卡器驗證失敗!", color='red')

                self.log_message(f"  **錯誤:** 讀卡器 {reader_key} 與卡片 {card_key} 密鑰不匹配,無法得出正確的 ResponseB。", color='red')

                

                self.lfsr_state_r.set("讀卡器 LFSR: (錯誤狀態)")

                self.lfsr_state_c.set("卡片 LFSR: (錯誤狀態)")

                

                messagebox.showerror("認證結果", "❌ 三步認證失敗!密鑰不匹配或數據被篡改。")

                self.step_btn.config(text="❌ 認證失敗", state=tk.DISABLED, bg='darkred')

                

            self.log_message(f"--- 認證結束: {auth_result} ---", color=result_color)


        else:

            self.reset_flow(message="認證已結束,請重啟流程。")



if __name__ == "__main__":

    root = tk.Tk()

    app = ThreePassAuthApp(root)

    root.mainloop()


沒有留言:

張貼留言

ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite

 ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite  ESP32 VS Code 程式 ; PlatformIO Project Configuration File ; ;   Build op...