RFID 門禁與支付系統整合模擬器
這是一個非常完整的系統整合。在現實世界的 RFID 應用(如門禁系統或小額支付)中,Reader 只是一個「傳聲筒」,真正的決策(如扣款、權限比對)是在遠端的後端資料庫 (Backend Database) 完成的。
這個模擬器將整合:
Nonce 挑戰應答:確保通訊安全。
後端資料庫:模擬儲存標籤的 UID、持有人姓名及餘額。
交易邏輯:如果驗證成功且餘額充足,則進行扣款。
RFID 門禁與支付系統整合模擬器
系統運作流程解析:
多層防護機制:
物理層:模擬標籤靠近 Reader。
通訊層:使用
Nonce確保每次傳輸的密文都不同,防止駭客監聽。應用層:後端資料庫檢查 UID 是否合法,以及是否有足夠的餘額完成動作。
後端連動 (Backend Binding):
當您點擊不同人的按鈕時,系統會自動查找該標籤對應的密鑰進行「虛擬解密」。
如果張小明的錢被扣光了,系統會自動從「交易成功」轉為「餘額不足」。
實務觀察點:
安全性:嘗試修改程式碼中的
tag_key,模擬駭客使用錯誤密鑰,您會看到系統立即噴出「驗證失敗」。資料完整性:所有變動(餘額扣除)都會即時反映在最上方的資料庫表格中。
這已經是一個具備 安全傳輸、身份認證、資料庫互動 的完整 RFID 應用雛形。
import tkinter as tk
from tkinter import ttk, messagebox
import random
import time
from threading import Thread
# --- 安全加密核心 (Nonce-based Stream Cipher) ---
class SecureCipher:
@staticmethod
def process(hex_data, hex_key, hex_nonce):
try:
val_data = int(hex_data.replace(" ", ""), 16)
val_nonce = int(hex_nonce.replace(" ", ""), 16)
# 結合 Nonce 進行混淆
mixed_hex = hex(val_data ^ val_nonce)[2:].upper()
# 使用金鑰生成串流 (LSFR 簡化版)
key_bin = bin(int(hex_key, 16))[2:].zfill(len(hex_key)*4)
plain_bin = bin(int(mixed_hex, 16))[2:].zfill(len(mixed_hex)*4)
state = [int(b) for b in key_bin[:8]]
keystream = []
for _ in range(len(plain_bin)):
out = state[-1]
feedback = state[0] ^ state[2]
state = [feedback] + state[:-1]
keystream.append(out)
res_bits = [int(plain_bin[i]) ^ keystream[i] for i in range(len(plain_bin))]
hex_out = hex(int("".join(map(str, res_bits)), 2))[2:].upper().zfill(len(mixed_hex))
return " ".join(hex_out[i:i+2] for i in range(0, len(hex_out), 2))
except: return "ERR"
# --- 後端資料庫 ---
class BackendDatabase:
def __init__(self):
# 模擬儲存:使用者金鑰與餘額
self.records = {
"53 71 37 A4 F6": {"name": "張小明", "balance": 1200, "key": "AC55"},
"12 AB 34 CD EF": {"name": "李華", "balance": 80, "key": "B88B"}
}
# 模擬商品標籤 (無金鑰,僅供識別價格)
self.products = {
"AA BB CC 01": {"item": "蘋果", "price": 30},
"AA BB CC 02": {"item": "牛奶", "price": 85},
"AA BB CC 03": {"item": "麵包", "price": 45}
}
# --- 主程式介面 ---
class RFIDIntegratedSystem:
def __init__(self, root):
self.root = root
self.root.title("RFID 智慧結帳與安全支付系統")
self.db = BackendDatabase()
self.cart = [] # 購物籃
self.is_processing = False
self.setup_ui()
def setup_ui(self):
# 1. 資料庫與帳戶資訊
db_frame = tk.LabelFrame(self.root, text="後端帳戶資料庫", padx=10, pady=5)
db_frame.pack(fill="x", padx=10, pady=5)
self.db_tree = ttk.Treeview(db_frame, columns=("UID", "Name", "Balance"), show="headings", height=2)
for col in ("UID", "Name", "Balance"): self.db_tree.heading(col, text=col)
self.db_tree.pack(fill="x")
self.refresh_db_view()
# 2. Reader 顯示幕
display_frame = tk.Frame(self.root, bg="#2c3e50", pady=15)
display_frame.pack(fill="x", padx=10)
self.lbl_status = tk.Label(display_frame, text="系統待機中", font=("Arial", 16, "bold"), fg="#ecf0f1", bg="#2c3e50")
self.lbl_status.pack()
self.lbl_price = tk.Label(display_frame, text="總計: $0", font=("Arial", 12), fg="#f1c40f", bg="#2c3e50")
self.lbl_price.pack()
# 3. 標籤模擬按鈕
action_frame = tk.Frame(self.root, pady=10)
action_frame.pack()
# 商品區
tk.Label(action_frame, text="商品感應:").grid(row=0, column=0, padx=5)
for i, (uid, info) in enumerate(self.db.products.items()):
btn = tk.Button(action_frame, text=f"{info['item']} (${info['price']})",
command=lambda u=uid: self.add_to_cart(u))
btn.grid(row=0, column=i+1, padx=2)
# 支付區
tk.Label(action_frame, text="支付感應:").grid(row=1, column=0, padx=5, pady=10)
for i, (uid, info) in enumerate(self.db.records.items()):
btn = tk.Button(action_frame, text=f"感應 {info['name']} 的卡",
command=lambda u=uid: self.start_checkout(u), bg="#d4efdf")
btn.grid(row=1, column=i+1, padx=2)
# 4. 交易日誌
log_frame = tk.LabelFrame(self.root, text="交易日誌與傳輸監控")
log_frame.pack(fill="both", padx=10, pady=5, expand=True)
self.log = tk.Text(log_frame, height=10, bg="#fdfefe", font=("Consolas", 9))
self.log.pack(fill="both", padx=5, pady=5)
def refresh_db_view(self):
# 修正:指向 self.db.records.items()
for i in self.db_tree.get_children(): self.db_tree.delete(i)
for uid, info in self.db.records.items():
self.db_tree.insert("", tk.END, values=(uid, info["name"], f"${info['balance']}"))
def add_to_cart(self, product_uid):
if self.is_processing: return
product = self.db.products[product_uid]
self.cart.append(product)
total = sum(item['price'] for item in self.cart)
self.lbl_price.config(text=f"總計: ${total} (已感應 {len(self.cart)} 件商品)")
self.write_log(f"[感應] 商品: {product['item']} - 價格: ${product['price']}")
def start_checkout(self, user_uid):
if not self.cart:
messagebox.showwarning("提示", "購物籃是空的!")
return
if self.is_processing: return
Thread(target=self.checkout_logic, args=(user_uid,), daemon=True).start()
def checkout_logic(self, user_uid):
self.is_processing = True
total_amount = sum(item['price'] for item in self.cart)
self.write_log(f"\n--- 開始安全支付程序 (總金額: ${total_amount}) ---")
# 1. Reader 發送 Nonce 挑戰
nonce = hex(random.getrandbits(16))[2:].upper().zfill(4)
self.root.after(0, lambda: self.lbl_status.config(text=f"安全挑戰 Nonce: {nonce}", fg="#f1c40f"))
time.sleep(1)
# 2. 標籤加密應答 (模擬卡片內部的加密)
user_key = self.db.records[user_uid]["key"]
encrypted_resp = SecureCipher.process(user_uid, user_key, nonce)
self.write_log(f"[傳輸] 密文應答: {encrypted_resp}")
time.sleep(1)
# 3. 後端驗證與扣款
# 模擬後端解密
decrypted_uid = SecureCipher.process(encrypted_resp, user_key, nonce)
if decrypted_uid.replace(" ","") == user_uid.replace(" ",""):
# 驗證通過,檢查餘額
user = self.db.records[user_uid]
if user["balance"] >= total_amount:
user["balance"] -= total_amount
self.root.after(0, lambda: self.lbl_status.config(text="支付成功!請取走商品", fg="#2ecc71"))
self.write_log(f"[資料庫] 扣款成功!{user['name']} 剩餘餘額: ${user['balance']}")
self.cart = [] # 清空購物籃
self.root.after(0, lambda: self.lbl_price.config(text="總計: $0"))
else:
self.root.after(0, lambda: self.lbl_status.config(text="餘額不足,支付失敗", fg="#e67e22"))
self.write_log(f"[拒絕] {user['name']} 餘額不足 (${user['balance']})")
else:
self.root.after(0, lambda: self.lbl_status.config(text="安全性錯誤: 驗證失敗", fg="#e74c3c"))
self.write_log("[警告] 偵測到非法卡片或重播攻擊!")
self.root.after(0, self.refresh_db_view)
time.sleep(3)
self.root.after(0, lambda: self.lbl_status.config(text="系統待機中", fg="#ecf0f1"))
self.is_processing = False
def write_log(self, msg):
self.root.after(0, lambda: self.log.insert(tk.END, msg + "\n"))
self.root.after(0, lambda: self.log.see(tk.END))
if __name__ == "__main__":
root = tk.Tk()
app = RFIDIntegratedSystem(root)
root.mainloop()

沒有留言:
張貼留言