握手與認證過程(三步認證)
在讀寫器和 MIFARE Classic 卡片開始交換加密數據之前,它們必須先進行三步認證 (Three-Pass Authentication) 過程,這個過程同時也是密鑰狀態初始化的過程:
讀卡器挑戰 (Reader Challenge): 讀卡器(如 MFRC522)發送一個隨機數(Nonce)給卡片。
卡片回應 (Card Response): 卡片使用其內部狀態和密鑰,對讀卡器的挑戰進行加密運算並返回一個回應。
讀卡器驗證並最終確定狀態: 讀卡器和卡片分別進行一系列的加密和狀態更新,最終兩者都必須達到相同的內部 LFSR 狀態。
只有當雙方都擁有相同的 48 位元密鑰,且認證過程成功,LFSR 才會初始化到相同的、用於通訊的狀態。
認證成功後,所有的數據交換都會使用 CRYPTO1 算法產生的密鑰串流進行 XOR 運算來加密和解密。
4. 🔒 安全性與現狀
應用程式旨在視覺化地解釋三步認證的流程和核心目的:
核心目標:密鑰驗證 與 串流加密狀態同步
**「三步認證」**並不是為了加密數據,而是為了達成兩個目的:
密鑰驗證 (Mutual Authentication): 驗證讀卡器和卡片是否都擁有相同的 48 位元密鑰。
狀態同步 (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()

沒有留言:
張貼留言