You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
결정 필요: 캡슐 암호화 모델 — 설계 문서(E2E)와 현재 구현(서버 봉투 암호화) 불일치 #32
A(설계대로 E2E 전환)로 진행하기로 하고 착수했습니다. 그런데 구현에 들어가기 전에 설계 문서를 다시 읽어보니 문서대로 만들면 E2E가 성립하지 않습니다. 먼저 정리해두고 나머지 구현 방향을 정해야 할 것 같습니다.
문제
설계 문서의 흐름은 이렇습니다.
5. 서버는 blob → tbl_capsule.content_encrypted 저장
키 조각들 → tbl_key_share에 각각 저장
...
3. 서버가 tbl_key_share에서 키 조각들 반환
즉 N개 조각이 전부 서버의 tbl_key_share 에 저장됩니다. 그러면 서버는 언제든 자기 테이블에서 조각을 다 읽어 합칠 수 있고, CEK 를 복원해 모든 캡슐을 열어볼 수 있습니다.
Shamir 를 붙였지만 "서버는 캡슐 내용을 절대 볼 수 없다" 는 성질은 전혀 얻어지지 않습니다. CEK 를 그냥 평문으로 한 컬럼에 저장한 것과 보안상 동등합니다. 조각으로 나눠 저장하는 것 자체는 같은 DB 안에서는 아무것도 보호하지 못합니다.
문서 안에서도 이 부분이 서로 어긋납니다.
서버가 조각 일부를 갖고 있어도 단독으로 복원 불가
"일부" 를 갖고 있을 때만 성립하는 문장인데, 저장 구조는 전부를 갖도록 되어 있습니다.
이대로 구현하면 겉보기에는 E2E 인데 실제로는 아닌 코드가 됩니다. 포트폴리오 관점에서는 정직한 at-rest 암호화보다 오히려 불리합니다. 리뷰어가 tbl_key_share 스키마만 봐도 바로 드러납니다.
성립하려면
서버가 정족수(threshold)를 못 채우게, 최소 한 조각은 서버에 없어야 합니다. 후보는 셋인데 실제로 쓸만한 건 하나뿐입니다.
방식
성립 여부
위치에서 조각 유도
❌ 서버가 ST_DWithin 때문에 좌표를 반드시 갖고 있으므로 서버도 유도 가능
수신자 기기에 조각 보관
△ 성립하지만 기기 분실 = 캡슐 영구 소실, PUBLIC 캡슐에는 적용 불가
비밀번호·질문 답에서 조각 유도
✅ 서버는 bcrypt 해시만 갖고 있어 유도 불가
세 번째가 이 서비스의 잠금 모델과 그대로 맞물립니다.
서버는 threshold - 1 개 조각만 저장
마지막 조각은 클라이언트가 KDF(비밀번호 또는 정답) 로 유도
서버는 위치·비밀번호 검증 후 자기 조각들을 반환하고, 클라이언트가 마지막 조각을 만들어 정족수를 채움
서버는 정답을 모르므로 혼자서는 영원히 복원 불가
다만 여기에 걸리는 제약
lockType = NONE 인 캡슐은 E2E 가 원리적으로 불가능합니다.
잠금이 없는 캡슐의 열람 조건은 "그 좌표에 간다" 뿐이고, 그 조건은 서버가 전부 알고 있습니다. 서버가 스스로 만족시킬 수 있는 조건만으로 열리는 캡슐은, 정의상 서버도 열 수 있습니다. 이건 구현으로 우회할 수 있는 게 아니라 제품 규칙에서 따라 나오는 결론입니다.
그래서 선택이 필요합니다.
(A-1) 잠금 있는 캡슐만 E2E — PASSWORD/QUESTION 캡슐은 서버도 못 열고, NONE 캡슐은 지금처럼 서버 봉투 암호화. 캡슐 종류에 따라 보장 수준이 다르다는 걸 문서와 API 에 명시.
(A-2) 모든 캡슐에 잠금 필수 — lockType = NONE 을 제거해서 전 캡슐 E2E. 보장은 깔끔해지지만 "지나가다 발견해서 열어보는" 경험이 사라집니다.
(A-3) 수신자 기기 보관 병행 — 지정 수신자가 있는 캡슐은 기기에 조각을 두고, 그 외에는 A-1 규칙. 구현·UX 복잡도가 가장 큽니다.
지금까지 한 것
어느 방식으로 가든 필요한 Shamir GF(256) 구현을 먼저 넣었습니다 (6083e31).
GaloisField256 — AES 기약다항식(0x11B) 기반 log/exp 테이블 연산
ShamirSecretSharing — 바이트 단위 분할, Lagrange 보간 복원, 2-of-3 / 3-of-5 지원
테스트 24개: 체 공리(결합·분배·역원), 임계값 미만 조각으로는 복원 불가, 1개 조각이 256개 후보 전부와 모순 없음(정보이론적 안전성), 0x00·0xFF 경계, 최대 255 조각
tbl_key_share 스키마와 API 계약 변경은 위 A-1/A-2/A-3 결정에 따라 달라져서 아직 손대지 않았습니다.
추가로 확인이 필요한 것
기존에 저장된 캡슐 처리 방안입니다. 현재 캡슐은 서버 KEK 로 암호화되어 있어 E2E 로 옮기려면 평문을 거쳐야 하고, 그건 클라이언트가 해야 합니다. 운영 데이터가 아직 없다면 마이그레이션 없이 전환 가능하지만, 있다면 별도 계획이 필요합니다.
Activity
cfcromn commentedon Aug 30, 2026
A(설계대로 E2E 전환)로 진행하기로 하고 착수했습니다. 그런데 구현에 들어가기 전에 설계 문서를 다시 읽어보니 문서대로 만들면 E2E가 성립하지 않습니다. 먼저 정리해두고 나머지 구현 방향을 정해야 할 것 같습니다.
문제
설계 문서의 흐름은 이렇습니다.
즉 N개 조각이 전부 서버의
tbl_key_share에 저장됩니다. 그러면 서버는 언제든 자기 테이블에서 조각을 다 읽어 합칠 수 있고, CEK 를 복원해 모든 캡슐을 열어볼 수 있습니다.Shamir 를 붙였지만 "서버는 캡슐 내용을 절대 볼 수 없다" 는 성질은 전혀 얻어지지 않습니다. CEK 를 그냥 평문으로 한 컬럼에 저장한 것과 보안상 동등합니다. 조각으로 나눠 저장하는 것 자체는 같은 DB 안에서는 아무것도 보호하지 못합니다.
문서 안에서도 이 부분이 서로 어긋납니다.
"일부" 를 갖고 있을 때만 성립하는 문장인데, 저장 구조는 전부를 갖도록 되어 있습니다.
이대로 구현하면 겉보기에는 E2E 인데 실제로는 아닌 코드가 됩니다. 포트폴리오 관점에서는 정직한 at-rest 암호화보다 오히려 불리합니다. 리뷰어가
tbl_key_share스키마만 봐도 바로 드러납니다.성립하려면
서버가 정족수(threshold)를 못 채우게, 최소 한 조각은 서버에 없어야 합니다. 후보는 셋인데 실제로 쓸만한 건 하나뿐입니다.
ST_DWithin때문에 좌표를 반드시 갖고 있으므로 서버도 유도 가능세 번째가 이 서비스의 잠금 모델과 그대로 맞물립니다.
threshold - 1개 조각만 저장KDF(비밀번호 또는 정답)로 유도다만 여기에 걸리는 제약
lockType = NONE인 캡슐은 E2E 가 원리적으로 불가능합니다.잠금이 없는 캡슐의 열람 조건은 "그 좌표에 간다" 뿐이고, 그 조건은 서버가 전부 알고 있습니다. 서버가 스스로 만족시킬 수 있는 조건만으로 열리는 캡슐은, 정의상 서버도 열 수 있습니다. 이건 구현으로 우회할 수 있는 게 아니라 제품 규칙에서 따라 나오는 결론입니다.
그래서 선택이 필요합니다.
PASSWORD/QUESTION캡슐은 서버도 못 열고,NONE캡슐은 지금처럼 서버 봉투 암호화. 캡슐 종류에 따라 보장 수준이 다르다는 걸 문서와 API 에 명시.lockType = NONE을 제거해서 전 캡슐 E2E. 보장은 깔끔해지지만 "지나가다 발견해서 열어보는" 경험이 사라집니다.지금까지 한 것
어느 방식으로 가든 필요한 Shamir GF(256) 구현을 먼저 넣었습니다 (6083e31).
GaloisField256— AES 기약다항식(0x11B) 기반 log/exp 테이블 연산ShamirSecretSharing— 바이트 단위 분할, Lagrange 보간 복원, 2-of-3 / 3-of-5 지원tbl_key_share스키마와 API 계약 변경은 위 A-1/A-2/A-3 결정에 따라 달라져서 아직 손대지 않았습니다.추가로 확인이 필요한 것
기존에 저장된 캡슐 처리 방안입니다. 현재 캡슐은 서버 KEK 로 암호화되어 있어 E2E 로 옮기려면 평문을 거쳐야 하고, 그건 클라이언트가 해야 합니다. 운영 데이터가 아직 없다면 마이그레이션 없이 전환 가능하지만, 있다면 별도 계획이 필요합니다.
chore: #32 :: add key share schema and capsule encryption mode
feat: #32 :: encrypt locked capsules client-side with shamir key shares
test: #32 :: cover the encryption policy and the end-to-end key round…
fix: #32 :: stop sending the lock secret to the server and validate s…
exijn commentedon Aug 31, 2026
연결된 PR #33이 develop에 병합되어 완료된 작업으로 확인되어 종료합니다.
PR: #33