# XenLook CA · DigiCert 벤치마크 parity 산식 (공개)

> **SSOT**: `data/registry/c2pa-ca-benchmark-digicert.json` → `status_scores` · `practice_areas[]`  
> **자동 산출**: `services/c2pa-ca-service/src/operational-tests.ts` → `loadBenchmark()`  
> **포털 직렬화**: `apps/c2pa/src/lib/portal-public-api-whitelist.ts` → `parityFromStatus()`  
> **수동 입력 금지**: 공개 `parity_pct`는 위 경로만 따릅니다.

## 1. parity_pct 정의

각 practice의 `pilot_status`(또는 리포트의 `xenlook_status`)에 대해:

```
parity_pct = round(status_scores[status] × 1000) / 10
```

예: `ocsp_stub` → `0.55` → **55%** · `planned` → `0.25` → **25%** · `crl_http` → `1.0` → **100%**

55%는 50% 단계가 아니라 **`ocsp_stub` 티어 점수(0.55)** 에서 온 값입니다. 하위 항목 가중 합이 아닙니다.

## 2. practice별 만점 조건 · 현재 상태 (2026-08-19T17:00:07.375Z)

| id | weight | pilot_status | parity_pct | 만점 조건 (status 키) | 현재 충족 |
|----|--------|--------------|------------|------------------------|-----------|
| cps | 8 | cps_public | 100 | cps_public | 예 |
| ra_identity | 10 | ra_api_enforced | 100 | ra_api_enforced | 예 |
| cpl_gate | 10 | enforced_api | 100 | enforced_api | 예 |
| hsm_keys | 12 | kms_dry_run | **50** | kms_dry_run (dry-run · kms_signer_wire 미배선) | 부분 |
| key_ceremony | 8 | ceremony_recorded | 100 | ceremony_recorded (M-of-N 완료) | 예 |
| crl_distribution | 0 | crl_http | 100 | crl_http | 예 |
| ocsp | 7 | ocsp_responder | 100 | ocsp_responder (외부 시맨틱 검증) | 예 |
| revocation_sla | 8 | api_automated | 100 | api_automated | 예 |
| incident_48h | 10 | incident_drilled | 100 | incident_drilled | 예 |
| audit_log | 9 | audit_hash_chain | 100 | audit_hash_chain | 예 |
| transparency | 9 | live | 100 | live | 예 |
| webtrust | 10 | readiness_partial | **75** | readiness_pass / 제3자 감사 | 부분 |
### crl_distribution 100% 근거 (R1)

| 조건 | 검증 |
|------|------|
| `pilot_status = crl_http` | registry SSOT |
| `status_scores.crl_http = 1.0` | → parity **100%** |
| 운영 테스트 `crl_pem` | CRL PEM 바이트 존재 |
| 운영 테스트 `crl_http` | `GET /api/ca/v1/crl` HTTP &lt; 500 |
| `metrics.crl_available` | PEM ∧ HTTP ∧ practice `crl_http` (`operational-tests.ts`) |

CP §7.1.2상 CRL DP는 **optional** — weight **0** (engineering parity 합산 제외).

### ocsp 이력 (R2 · 2026-08 이전 스냅샷)

> **현재 SSOT**: §2 본표 `ocsp_responder` = **100%**. 아래는 과거 `ocsp_stub`(55%) 티어 기록 — **역사적 근거만** 보존.

| 항목 | 내용 (과거) |
|------|------|
| status 키 | `ocsp_stub` |
| score | `0.55` (registry `status_scores`) |
| 의미 | POST `/api/ca/v1/ocsp` 전송 확인 · openssl/Globalping 외부 시맨틱 미확정 |


## 3. operational vs benchmark (별도 축)

| 축 | 측정 | 공개 API |
|----|------|----------|
| **A · 운영 테스트** | `operational.passed` / `total` / `failed` — 분모 **고정(total=테스트 슬롯 수, 통상 60)** · `informational_failed` = weight 0 실패 | `/api/transparency` · `/api/ca/public/transparency` |
| **B · benchmark parity 정보성** | practice `parity_pct` &lt; 100% (hsm_keys·webtrust 등) | `/api/transparency` → `benchmark.practices[]` |

parity 상향(축 B)이 운영 테스트 실패(축 A)를 줄이지 않습니다. 반대도 마찬가지입니다.

### 3a. 운영 프로브 외부 재현성

<!-- benchmark-parity-internal-probes:v1 -->
| test id | 외부 재현 | 비고 |
|---------|-----------|------|
| ca_health | **아니오** (내부망 프로브) | 127.0.0.1 `/health` — CA 서비스 내부망 |
| ca_status | **아니오** (내부망 프로브) | 127.0.0.1 `/api/ca/status` — 공개 `/api/ca/v1/health`·`/status` 404 |
<!-- /benchmark-parity-internal-probes -->


### 3b. test_certs 카운터 (migration)

`test_certs_issued` 3→0: **카운터 재정의 + ephemeral purge** (데이터 손실 아님). 근거 SSOT: `data/compliance/c2pa-ca-test-cert-counter-note.json` · audit `data/audit/ca-issuance.jsonl` (281 events). `internal_pilot_certs_issued` / `external_subscriber_certs_issued` 분리는 유지.

## 4. 운영 실측 스냅샷 (v5.4 · 자동 동기화)

아래 JSON은 `scripts/ops/sync-benchmark-parity-operational-snapshot.py`가 **`/api/ca/public/transparency`** 의 `operational` 블록에서 생성합니다. 수동 숫자 기재 금지.

<!-- benchmark-parity-operational-snapshot:v5.4 -->
```json
{
  "source": "/api/ca/public/transparency",
  "generated_at": "2026-08-19T17:00:07.375Z",
  "operational": {
    "total": 60,
    "passed": 59,
    "failed": 1,
    "informational_failed": 1,
    "weighted_failed": 0,
    "weighted_pass_rate_pct": 100
  },
  "weighted_operational": {
    "total": 60,
    "passed": 59,
    "failed": 1,
    "informational_failed": 1
  },
  "failed_tests": [
    {
      "id": "benchmark_engineering_100",
      "name": "Non-WebTrust benchmark practices at 100% parity (informational)",
      "weight": 0,
      "evidence": null,
      "error": null
    }
  ]
}
```
<!-- /benchmark-parity-operational-snapshot -->

### 4a. benchmark parity 정보성 미달 (축 B · parity)

`parity_pct` &lt; 100% 인 practice — **운영 테스트 failed와 무관**.

<!-- benchmark-parity-shortfall-table:v1 -->
| id | weight | parity_pct | 사유 |
|----|--------|------------|------|
| hsm_keys | 12 | **50** | `kms_dry_run` — kms_signer_wire 미배선 |
| webtrust | 10 | **75** | `readiness_partial` — CA-2 법무·제3자 감사 전 |
<!-- /benchmark-parity-shortfall-table -->

### 4b. 운영 테스트 실패 (축 A · operational)

위 스냅샷 `failed_tests[]`와 동일합니다.

| 구분 | 의미 |
|------|------|
| `operational.total` | **고정** — 전체 테스트 슬롯 수 (통상 60) |
| `operational.failed` | weight 0 포함 전체 실패 건수 |
| `operational.informational_failed` | weight 0 실패만 (벤치마크 parity 알림 프로브) |
| `weighted_operational` | 스냅샷 JSON — total=operational.total 동일 · informational_failed 별도 |
| `operational.score` | `round(1000×passed/total)/10` — readiness operational 항 |
| `weighted_pass_rate_pct` | `sum(passed.weight)/sum(all.weight)` — 엔지니어링 표시용 |

실패 항목명·가중치·사유는 스냅샷 `failed_tests`를 참조하세요.

## 5. 코드 위치 (R4)

| 산출 | 경로 | 수동? |
|------|------|-------|
| benchmark practices | `operational-tests.ts` `loadBenchmark()` | 자동 |
| transparency JSON | `saveTransparencyReport` → `transparency-latest.json` | 자동 (CA run) |
| `/api/transparency` | `serializeBenchmarkPractices()` | 자동 |
| parity sync (캐시) | `scripts/ops/sync-transparency-benchmark-from-registry.py` | registry→JSON |

## 6. readiness_index · gating cap (공개)

```
raw = round(0.70 × operational_score + 0.30 × benchmark_parity_score)
operational_score = round(1000 × operational.passed / operational.total) / 10
  (denominator = all test slots, typically 60; weight-0 informational failures reduce passed count)
weighted_pass_rate_pct = sum(passed.weight) / sum(all.weight)  [display only]
readiness_index = min(raw, 74)  when gating_blockers.length > 0
readiness_index = raw           when gating_blockers.length = 0
cap_value = 74
```

`gating_blockers` 후보: `webtrust`, `hsm_keys`, `key_ceremony`, `ocsp` — 각 practice가 만점 tier 미달 시 포함.  
cap 적용 시 operational 70% 가중이 반영되지 않은 것처럼 보일 수 있음(예: raw≈94 → 출력 74).

## 7. status_scores 참조표 (일부)

| status | score | parity_pct |
|--------|-------|------------|
| live, crl_http, cps_public, … | 1.0 | 100 |
| ceremony_partial, incident_partial | 0.5 | 50 |
| ocsp_stub | 0.55 | 55 |
| planned | 0.25 | 25 |
| not_started | 0 | 0 |

전체: `data/registry/c2pa-ca-benchmark-digicert.json` → `status_scores`.

## 8. Parity 95% upgrade target (2026-08-19)

> **SSOT**: `data/registry/c2pa-ca-parity-95-upgrade-ssot.json`  
> **Plan**: `docs/ops/C2PA-CA-PARITY-95-UPGRADE-PLAN.md`

| Phase | Deadline | `parity_score` min | Work packages |
|-------|----------|-------------------|---------------|
| P95-A | 2026-09-30 | **85%** | P95-1 KMS · P95-2 ceremony |
| P95-B | 2026-10-31 | **92.6%** | P95-3 OCSP · P95-4 incident |
| P95-C | 2026-12-31 | **≥95%** | P95-5 WebTrust · P95-6 Trust List |

### 8a. 일정 충돌 (P95-C · §31②) — 의장 확인 대기

P95-C(2026-12-31, WebTrust + Trust List)와 §31② 전면 배포(on/Companion, 2026-Q4)가 겹칩니다. §31②는 법정 의무입니다. **P95-C 시한 이동(예: 2027-Q1)은 의장 확인 후 문서만 수정** — 임의 변경 금지. P95-A(09-30) · P95-B(10-31)는 유지.

**95% 수학**: 엔지니어링 11 practice 100% (= 93.5/101) + WebTrust `partial` 이상 필수.  
감사: `python3 scripts/audit-c2pa-parity-95-upgrade-ssot.py`
