법정 증거급 음성 진위 제품. 무결성 본체 = 암호 봉인(해시+서명+시각+블록체인), 핑거프린트는 보조.
| seal_id | 모드/버전 | 번들 | 타임스탬프 | 원본 | 지문 |
|---|---|---|---|---|---|
02de8d6f9c9f4a70… | client-hybrid / vseal-manifest/0.2 | ✅ | 2 | ✅ | ✅ |
23aeffc3cb2a4e29… | client-hybrid / vseal-manifest/0.2 | ✅ | 1 | ✅ | ✅ |
40701a9afffc4389… | server-excerpt / vseal-manifest/0.2 ↳ 발췌 ← 23aeffc3… [0–10000ms] | ✅ | — | — | — |
848bf5ca730c4e6b… | client-hybrid / vseal-manifest/0.2 | ✅ | 1 | ✅ | ✅ |
한 녹음 = 같은 seal_id로 번들·타임스탬프·원본·지문이 묶임. 원본/지문은 best-effort(없을 수 있음).
| 기능 | 엔드포인트 | 구현 위치 | 상태 |
|---|---|---|---|
| 봉인 생성 (서버 모드) | POST /seal | app/seal.py · sealcore.py · tsa.py | ✅ 완료 |
| 검증 (C1~C4 재현 + 키신원 C5 + 발췌 C6) | POST /verify | app/seal.py:verify_seal · cert.py (C5) · excerpt_service.py (C6) | ✅ 완료 |
| 단일구조 봉인 수신 (sealed WAV 1개) | POST /seal/intake | main.py · containers · sealcore · original_store · fingerprint | ✅ 완료 |
| 시각 도장 (temporary→confirmed) | POST /seal/{id}/timestamp | app/seal.py · tsa.py | ✅ 완료 |
| 원본 아카이브 + 지문 색인 (backfill) | POST /seal/{id}/original | app/original_store.py · fingerprint.py | ✅ 완료 |
| 변형본/짜깁기 식별 | POST /identify | app/identify.py · wire.py (AudioDfs) | ✅ 완료 |
| 세그먼트 대조 검증 (지각적) | POST /verify/segment | app/segcompare.py (numpy/scipy FFT) | ✅ 완료 |
| 출처 찾기 (일반 클립 → 어느 봉인 어디) | POST /locate | app/locate.py (지문 + 세그먼트 대조) | ✅ 완료 |
| 발췌본 생성 (Merkle 출처 증명, C6) | POST /seal/{id}/excerpt | main.py · excerpt_service · merkle.py · seal.create_derived_seal | ✅ 완료 |
| 발췌본 역방향 조회 (원본→발췌들) | GET /seal/{id}/excerpts | seal.find_excerpts | ✅ 완료 |
| 봉인 파일 다운로드 (자기완결 WAV) | GET /seal/{id}/download | app/original_store.py | ✅ 완료 |
| 디바이스 cert 발급 (§13.15 키 신원) | POST /device/cert · GET /cert/root | app/cert.py · signer.py (KMS root) | ✅ 완료 |
| 서버 키 custody (KMS Ed25519, HSM) | (SEAL_SIGNING_BACKEND=kms) | app/signer.py · seal.py · cert.py | ✅ 완료 |
| S3 원본 아카이브 (라이브) | (SEAL_ORIGINAL_BACKEND=s3) | app/original_store.py:S3OriginalStore | ✅ 완료 |
| 번들 조회 | GET /seal/{id} | app/main.py · sealstore.py | ✅ 완료 |
| 블록체인 앵커링 (OTS) | (opt-in SEAL_OTS_ENABLED) | app/anchor.py | ⚪ 구현됨·꺼짐 |
| 다중 TSA (K-of-N) | (SEAL_TSAS 설정) | app/tsa.py | ✅ 완료 |
| 실 WORM (MinIO/S3 Object Lock) | (SEAL_STORE_BACKEND=minio) | app/sealstore.py | ✅ 완료 |
| 감사 해시체인 | (SEAL_AUDIT_DIR) | app/audit.py | ✅ 완료 |
| 키 회전·폐기·keyring 자체서명 | CLI | app/keyring*.py | ✅ 완료 |
| 레포/모듈 | 역할 | 언어 | GitLab | 설명 |
|---|---|---|---|---|
sound-fingerprint | 🖥️ 봉인 서버 (이 repo) | Python / FastAPI | proofcam/sound-fingerprint | 권위 보관·시각 도장·지문 색인·검증 본체. 데모 https://proofcam.deshow.co.kr. |
proofcam-android :seal | 📱 봉인 라이브러리 (순수 JVM) | Kotlin | proofcam/proofcam-android | canonical/manifest/signer/WAV/번들/SealClient/SealVerifier. 서버와 같은 SPEC. |
proofcam-android :app | 📱 봉인 dev 앱 (폰) | Kotlin / Android | proofcam/proofcam-android | 녹음→봉인→업로드. AudioRecorder·MainActivity·SealUploader. |
proofcam-android :verifier | 📱 검증 전용 앱 | Kotlin / Android | proofcam/proofcam-android | 서명키·네트워크 권한 없음 — 트러스트 분리. 오프라인 C1~C3. |
audiodfs-linux | 🔊 핑거프린트 엔진 (리눅스 포팅) | C++ 바이너리 | (git 아님, 로컬) | 서버가 shell out: extract/filtering_server/client. 모바일 포팅 안 함. |
own_skills/ · benchmark/ | 🔒 보조 자산 | — | (gitignore, 커밋 금지) | AudioDfs 원본 소스(ETRI 매입) · 엔진 비교 산출물. |
[📱 :app 폰 앱] ──녹음→Keystore키 서명(:seal)──HTTPS──▶ [🖥️ 서버 proofcam.deshow.co.kr]
│ TEE Ed25519 + §13.15 cert │ intake→timestamp(KMS서명)→S3 원본
│ 로컬 저장(sealed WAV) ├─▶ [🔊 audiodfs-linux] 지문 추출/검색
▼ ▼
[📱 :verifier 앱] ◀──오프라인 검증(C1~C5)── /state/ (번들·원본→S3·지문·감사)
proofcam 관련은 위 3곳뿐. ~/IdeaProjects의 deshow*·exocobio*·its-* 등은 무관.
| 항목 | 지금 | 되어야 할 것 / 비고 | 상태 |
|---|---|---|---|
| 시각 증인 (TSA) | ✅ freeTSA RFC3161 (실연동, 라이브) | (완료) 인증서 핀 검증 포함 | ✅ 완료 |
| 블록체인 영구 증인 | ✅ OpenTimestamps tentative (제출됨) | upgrade 자동화 → Bitcoin block confirmed (~1h+) | ✅ 완료 |
| 전송 보안 | ✅ HTTPS (proofcam.deshow.co.kr, Let's Encrypt·자동갱신) | (완료) prod 도메인/인증서는 별도로 파면 됨 | ✅ 완료 |
| 서명 키 custody | ✅ AWS KMS Ed25519 (HSM) — §13.15 root + 서버 봉인키 | (완료) 개인키가 HSM 밖으로 안 나옴. '오프라인 루트'는 prod 옵션 | ✅ 완료 |
| 디바이스 키 신원 (C5) | ✅ cert 발급 라이브(KMS root) + 안드 Keystore Ed25519(작성) | 안드 빌드하면 봉인이 identity-bound (현 봉인=self-signed) | ✅ 완료 |
| 기기/앱 증명 (attestation) | deferred (약한 모드) | Play Integrity / App Attest — Google 프로젝트 필요, prod 단계 | 🔴 dev 대체물 |
✅ 시각(TSA·OTS)·전송(HTTPS)·키 custody(KMS HSM)·디바이스 신원(§13.15)까지 실제로 동작. 남은 건 Play Integrity(기기 attestation) — Google 프로젝트가 필요해 prod 단계에. 봉인 자체는 self-signed(현)→안드 빌드 시 identity-bound.
| 항목 | 내용 | 분류 |
|---|---|---|
| 장시간 녹음 스트리밍 (Inc-1~4) | 44.1kHz·3-4시간(1.27GB) — 녹음기 파일스트리밍(Inc-1 작성됨, 빌드대기)·봉인 파이프라인 스트림화·서버 인테이크 스트리밍·재개 청크 업로드+오프라인 큐(간헐 인터넷에도 안 잃음) | 🔴 안드+서버 |
| 디바이스 §13.15 빌드 검증 | Keystore Ed25519 영속키 + cert 발급/embed 작성됨(미빌드). 빌드해 봉인이 C5 identity-bound 뜨는지 확인. Keystore Ed25519 KeyGenSpec digest는 기기/KeyMint별 조정 가능성 | 🟡 안드 빌드 |
| 검증기 시간 정밀도 표시 | /verify의 time 객체(신뢰상한 vs 자가신고 캡처 + 정밀도 tier)를 검증기 '언제'에 3층 표시 — 오해 방지 | 🟡 안드 |
| 타임스탬프 파일 백필 | RFC3161 토큰을 WAV에 박아 C4까지 자기완결(서버 없이도 confirmed 유지, §13.14) | 안드+서버 |
| OTS upgrade 자동화 | tentative→Bitcoin block confirmed 자동 업그레이드 스케줄러(~1h+) | 서버 |
| 서버 키 prod 강화 (옵션) | 오프라인 루트(KMS root는 평소 미사용) · CloudHSM · 키 회전·CRL | 🔵 prod |
| Play Integrity (외부) | 기기/앱 attestation 실호출 — Google Cloud 프로젝트 + Play Console 등록 + 실기기. prod/전용서버 이식 단계에 | ⚫ 외부 |
| 운영 잡일 | filtering_server 핫리로드 · H4/H5(verify CLI·temporary 만료) · 데모 후 IAM 키 rotate | 운영/병행 |
| 데이터 | 경로 | 파일 |
|---|---|---|
| 번들 (WORM) | /state/seal_store | {id}.seal.json + .anchors.jsonl |
| 원본 음성 (삭제가능) | /state/originals | {id}.wav |
| 지문 색인 | /state/fpidx | {id}.dfs |
| 감사로그 | (꺼짐) | YYYY-MM.jsonl |
| 봉인 (Seal) | 녹음을 '이 순간 이 내용 그대로'라고 잠그는 행위/결과. 해시+서명+시각도장을 한 묶음으로 만든 것. |
| 번들 (Bundle) | 봉인의 증거 묶음(JSON). 매니페스트 + 서명 + 공개키가 들어있음. 오디오 자체는 없고 '해시'만. = store/{id}.seal.json |
| 매니페스트 (Manifest) | 봉인 대상의 명세. 오디오 해시(payload_sha256)·포맷·기기·당사자 진술·캡처시각 등. 이걸 서명한다. |
| 페이로드 (Payload) | 오디오의 '실제 소리' 부분. WAV의 fmt+data. 헤더 메타는 제외 — 재포장해도 안 변하는 본질. |
| 해시 (Hash, SHA-256) | 데이터의 지문 같은 고정길이 값. 한 바이트만 바뀌어도 완전히 달라짐 → 변조 탐지. |
| 서명 (Signature, Ed25519) | 개인키로 매니페스트에 도장. 공개키로 누구나 '이 키 주인이 서명했고 안 바뀜'을 검증. = 위조 불가 날인. |
| 키 (Key pair) | 개인키(서명용, 비밀) + 공개키(검증용, 공개) 한 쌍. 서버키=AWS KMS(HSM 안, §13.15 root+봉인키), 디바이스키=Android Keystore Ed25519(TEE/StrongBox). 둘 다 개인키가 보안하드웨어 밖으로 안 나옴. |
| TSA (시각 인증기관) | Time Stamping Authority. '이 해시가 이 시각 이전에 존재했다'를 서명해주는 독립 제3자. 표준=RFC3161. |
| RFC3161 | TSA 타임스탬프 토큰의 국제 표준 포맷. 봉인에 '신뢰된 시각 증인'을 붙이는 방법. |
| 앵커 (Anchor) | 봉인 밖에 따로 붙는 시각/블록체인 증거. RFC3161 토큰이나 OTS 증명. = store/{id}.anchors.jsonl |
| OTS (OpenTimestamps) | 비트코인 블록체인에 해시를 박아 '영구 시각 증인'을 만드는 방식. TSA보다 더 분산·영구적. |
| attestation (기기 증명) | '이 봉인이 진짜 안드로이드 기기·미변조 앱에서 나왔다'는 OS 차원 증명. Play Integrity / App Attest. 지금은 deferred(약한 모드). |
| nonce | 재사용·위조 막으려 매니페스트에서 뽑은 1회성 값(해시). attestation 토큰을 이 봉인에 묶는 데 씀(§13.4). |
| hybrid 봉인 | 폰이 오프라인에서 먼저 서명(temporary) → 나중에 서버가 TSA 도장(confirmed). 완전 오프라인은 시각증인이 없어 불가. |
| seal_status | 봉인의 시각 확정 여부. temporary(시각증인 0) / confirmed(RFC3161 ≥1). 저장값 믿지 않고 증거 카운트로 도출. |
| verdict / C1~C6 | 검증 검사. C1=이 오디오 맞나 · C2=매니페스트 일관 · C3=서명 유효 · C4=시각 증인 · C5=키 신원(§13.15 cert 체인이 root까지 결속=identity-bound, 없으면 self-signed) · C6=발췌 출처(Merkle). PASS / PASS-PENDING(시각만 빠짐) / FAIL. |
| 발췌본 / C6 (Merkle) | 원본의 일부를 잘라 다시 봉인한 것(POST /seal/{id}/excerpt). 원본 봉인 시 PCM을 Merkle 커밋(container.merkle), 발췌본은 range proof를 들고 다님 → C6=이 발췌가 정말 원본 [t1,t2]임을 서버 신뢰 없이 증명. 10초 경계 스냅. 핑거프린트와 상보. |
| 인라인 봉인 (Inline) | 번들을 별도 파일이 아니라 WAV의 'seal' 청크에 박는 것. 파일 하나가 곧 자기완결 증거. = sealed WAV. |
| WORM | Write Once Read Many. 한 번 쓰면 못 고침. 봉인 번들 저장 방식(0o444 / Object Lock) — 사후 조작 차단. |
| 핑거프린트 (.dfs) | 오디오의 잡음에 강한 음향 지문(AudioDfs). 재인코딩 사본·짜깁기 매칭용 보조 자료. 봉인 본체 아님. |
| identify / splice | 변형본·짜깁기 식별. splice=이어붙인 경계 / gap=공백 이상. 확률적 보조 라벨(법정 본체는 verify). |
| keyring | 공개키 배포·회전·폐기 관리 아티팩트. key_id로 봉인↔검증 매칭. root key가 keyring 자체를 서명. |
| custody (보관 사슬) | 증거를 누가 언제부터 보관했나의 사슬. 원본을 중립 보관소에 두면 '녹음자≠보관자'로 신뢰↑. |
| payload_sha256 | 매니페스트 안의 오디오 해시 필드. 검증 C1이 '제출된 오디오의 해시 == 이 값'인지 대조. |