CoolFace
Apppublic

study-1/KeyStroke_Dynamics

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
app.py181 linesDownload Raw Back to root
1import gradio as ui2import numpy as np3import joblib4 5# 1. 기존 model.pt 파일 로드 (파이프라인)6try:7    pipeline = joblib.load('model.pt')8    scaler = pipeline['scaler']9    pca = pipeline['pca']10    svm = pipeline['svm']11    minmax = pipeline['minmax']12    gold_threshold = pipeline['best_threshold']13    target_subject = pipeline['target_subject']14except Exception as e:15    scaler, pca, svm, minmax = None, None, None, None16    gold_threshold = 0.5517    target_subject = "JunSeo"18 19# 2. 파이썬 백엔드 판정 로직 (초고속 모드)20def verify_real_keystroke(password, timing_data_str):21    if not password:22        return "<div style='background-color: #f5f5f5; padding: 15px; border-radius: 5px; color: #666;'>🔒 비밀번호를 입력해주세요.</div>", "인증 대기 중..."23    24    # 텍스트 검증25    if password != "sincos7475" and password != "signcos7475":26        return (27            "<div style='background-color: #fce8e6; padding: 15px; border-radius: 5px;'><h3 style='color: #c5221f; margin: 0;'>❌ [액세스 거부] 비밀번호 불일치</h3></div>",28            "비밀번호 텍스트가 틀렸습니다."29        )30 31    if not timing_data_str or timing_data_str == "WAIT":32        return "<div style='background-color: #feefe3; padding: 15px; border-radius: 5px; color: #b06000;'>⚠️ 입력 데이터 동기화 중입니다. 다시 엔터키를 눌러주세요.</div>", "데이터 추출 오류"33 34    try:35        # JS에서 넘겨준 H, DD, UD 값들을 실수형 배열로 파싱36        raw_features = [float(x) for x in timing_data_str.split(",") if x.strip() != ""]37        38        # 모델 호환을 위해 45개 피처 규격으로 자동 패딩/슬라이싱39        if len(raw_features) < 45:40            padding_val = raw_features[-1] if len(raw_features) > 0 else 0.1541            raw_features += [padding_val] * (45 - len(raw_features))42        elif len(raw_features) > 45:43            raw_features = raw_features[:45]44            45        features_np = np.array(raw_features).reshape(1, -1)46        47        # 스케일러 -> PCA -> SVM 파이프라인 즉시 통과48        scaled_data = scaler.transform(features_np)49        pca_data = pca.transform(scaled_data)50        raw_score = svm.decision_function(pca_data)51        scaled_score = minmax.transform(raw_score.reshape(-1, 1)).flatten()[0]52        53        # 최종 보안 스크리닝54        if scaled_score >= gold_threshold:55            status = f"✅ [액세스 승인] '{target_subject}' 본인 행동 패턴 일치"56            color_md = f"<div style='background-color: #e6f4ea; padding: 15px; border-radius: 5px; border-left: 5px solid green;'><h3 style='color: green; margin: 0;'>{status}</h3><p style='margin: 5px 0 0 0; color: #333; font-size: 0.95em;'>H, DD, UD 타이핑 동역학이 완벽합니다.</p></div>"57        else:58            status = f"⚠️ [액세스 거부] 도용 의심 (타이핑 리듬 불일치)"59            color_md = f"<div style='background-color: #feefe3; padding: 15px; border-radius: 5px; border-left: 5px solid #b06000;'><h3 style='color: #b06000; margin: 0;'>{status}</h3><p style='margin: 5px 0 0 0; color: #333; font-size: 0.95em;'>비밀번호는 일치하나 타건 속도와 간격이 본인과 다릅니다.</p></div>"60            61        score_info = f"■ H/DD/UD 추출 피처: {len(timing_data_str.split(','))}개\n■ 패턴 매칭 점수: {scaled_score:.4f}\n■ 요구 커트라인: {gold_threshold:.4f}"62        return color_md, score_info63 64    except Exception as e:65        return f"<div style='background-color: #feefe3; padding: 15px; border-radius: 5px; color: orange;'>연산 오류: {str(e)}</div>", "다시 시도해주세요."66 67# 3. 사용자 제공 HTML 로직을 완벽히 이식한 Gradio용 커스텀 JS68js_enter_trigger = """69function() {70    let currentEvents = [];71 72    setTimeout(function() {73        const pwd_input = document.querySelector("#pwd_input input");74        const hidden_output = document.querySelector("#hidden_timing_input textarea, #hidden_timing_input input");75        const hidden_submit_btn = document.querySelector("#hidden_submit_btn");76 77        if (!pwd_input) return;78 79        pwd_input.addEventListener('keydown', function(e) {80            // 엔터키 입력 시 H, DD, UD 즉시 계산 후 백엔드 전송 트리거81            if (e.key === "Enter") {82                e.preventDefault();83                84                if (currentEvents.length > 0) {85                    const downs = currentEvents.map(ev => ev.down);86                    const ups = currentEvents.map(ev => ev.up !== null ? ev.up : performance.now());87 88                    // H, DD, UD 계산 (초 단위 변환)89                    const H = downs.map((d, i) => (ups[i] - d) / 1000.0);90                    const DD = downs.slice(1).map((d, i) => (d - downs[i]) / 1000.0);91                    const UD = downs.slice(1).map((d, i) => (d - ups[i]) / 1000.0);92 93                    const features = [...H, ...DD, ...UD];94 95                    if (hidden_output) {96                        hidden_output.value = features.join(",");97                        hidden_output.dispatchEvent(new Event('input', { bubbles: true }));98                    }99 100                    // 0.05초 대기 후 보이지 않는 버튼을 JS가 강제 클릭하여 딜레이 0 구현101                    if (hidden_submit_btn) {102                        setTimeout(() => { hidden_submit_btn.click(); }, 50);103                    }104                }105                return;106            }107 108            // 백스페이스나 딜리트 시 초기화 로직 (HTML 코드 반영)109            if (e.key === "Backspace" || e.key === "Delete") {110                currentEvents = [];111                return;112            }113 114            if (e.ctrlKey || e.metaKey || e.altKey) return;115 116            if (e.key.length === 1) {117                currentEvents.push({118                    key: e.key,119                    down: performance.now(),120                    up: null121                });122            }123        });124 125        pwd_input.addEventListener('keyup', function(e) {126            if (e.ctrlKey || e.metaKey || e.altKey) return;127 128            if (e.key.length === 1) {129                const t = performance.now();130                for (let i = currentEvents.length - 1; i >= 0; i--) {131                    if (currentEvents[i].key === e.key && currentEvents[i].up === null) {132                        currentEvents[i].up = t;133                        break;134                    }135                }136            }137        });138 139        pwd_input.addEventListener('paste', function(e) {140            e.preventDefault();141            currentEvents = [];142        });143 144    }, 1000);145}146"""147 148with ui.Blocks(theme="soft") as demo:149    ui.Markdown("# ⚡ 무지연 생체 인증 시스템 (H, DD, UD 정밀 추출)")150    ui.Markdown("제공된 정밀 타이밍 수집 로직 적용 완료. 비밀번호를 타이핑한 후 **`Enter(엔터)` 키를 누르면 즉시 결과를 판정**합니다.")151    152    with ui.Row():153        with ui.Column():154            pwd_box = ui.Textbox(155                label="비밀번호 입력 후 [Enter] (테스트: sincos7475)", 156                placeholder="입력 후 엔터키를 치면 바로 넘어갑니다.",157                type="password",158                elem_id="pwd_input"159            )160            161            # JS 데이터를 주고받기 위한 히든 필드와 히든 버튼162            hidden_timing = ui.Textbox(visible=False, value="", elem_id="hidden_timing_input")163            hidden_btn = ui.Button("숨겨진 제출 버튼", visible=False, elem_id="hidden_submit_btn")164            165        with ui.Column():166            result_html = ui.HTML(label="보안 판정 결과", value="<div style='background-color: #f5f5f5; padding: 15px; border-radius: 5px; color: #666;'>대기 중...</div>")167            score_box = ui.Textbox(label="패턴 데이터 분석 지표", interactive=False)168 169    # 버튼 클릭 이벤트를 큐잉 없이 즉각 처리 (queue=False)170    hidden_btn.click(171        fn=verify_real_keystroke,172        inputs=[pwd_box, hidden_timing],173        outputs=[result_html, score_box],174        queue=False 175    )176    177    demo.load(fn=None, inputs=None, outputs=None, js=js_enter_trigger)178 179if __name__ == "__main__":180    demo.launch()181