지식 베이스
종단 간 암호화
브라우저에서 생성되는 OpenPGP 키입니다. 다른 OpenEmail 주소로 보내는 메일은 탭을 떠나기 전에 봉인할 수 있고, 나에게 온 봉인된 메일은 내 컴퓨터에서 복호화되어 읽기 창에서 열립니다. 키는 저희 것이 아니므로 넘겨줄 수도 없습니다.
세부 사항
- 양쪽 절반 모두 끝에서 끝까지 연결되어 있습니다. 키를 공개한 수신자에게 메일을 작성하면 요청이 브라우저를 떠나기 전에 본문이 봉인됩니다. 서버는 읽을 수 없는 아머를 넘겨받아
pgp-mime으로 표시하고, 실제multipart/encrypted메시지를 만듭니다. 읽기는 같은 과정을 반대로 진행합니다. 암호문을 가져와 탭 안에서 복호화한 뒤 평범한 메일처럼 렌더링합니다. - 알고리즘은 다른 모두가 이미 쓰고 있는 것입니다. openpgp.js를 통한 OpenPGP이고, 전송 구간에서는 PGP/MIME입니다. Curve25519 기반 v4 키로, Proton이 발급하고 gpg가 기본 최신 알고리즘으로 생성하는 것과 같은 형태입니다. 따라서 여기서 읽는 메일은 원칙적으로 Thunderbird, gpg, Proton이 만드는 메일과 같으며, 나중에 풀어야 할 자체 형식은 여기에 없습니다.
- 다만 실제로는 OpenEmail 간에만 가능합니다. 이 코드베이스 어디에도 Web Key Directory도, 키서버 조회도, Autocrypt 헤더 파싱도 없습니다. 수신자의 키를 찾는 곳은 오직 저희 자체 디렉터리뿐이며, 여기에는 이곳에서 호스팅하는 도메인의 주소를 쓰는 사람들이 이 앱에서 공개한 키가 들어 있습니다. 자신의 키를 남에게 건넬 방법도 없습니다. 앱은 공개 키를 그 디렉터리에 게시할 뿐 복사나 내보내기를 제공하지 않으므로, Thunderbird를 쓰는 상대방은 그것을 얻을 수 있는 지원된 방법이 없습니다. 더 넓은 PGP 세계와의 상호 운용성은 형식의 속성일 뿐, 아직 제품이 대신 해 주는 일은 아닙니다.
- 읽기는 그렇게 제한되지 않습니다. 복호화에는 디렉터리가 필요 없기 때문입니다. 이 브라우저에 있는 키로 봉인되어 이 메일함에 도착한 PGP/MIME 또는 인라인 PGP 메시지는 누가 보냈든 어떤 클라이언트를 썼든 열립니다. 이미 교체한 키로 봉인된 메시지도 포함됩니다. 폐기한 키도 키링에 남아 현재 키와 함께 시도되므로, 키를 교체해도 이미 받은 메일을 잃지 않습니다.
- 초록색은 열렸다는 뜻이며, 다른 어떤 방법으로도 그 상태에 도달할 수 없습니다. Details → Security 행은 이 탭에서 복호화가 실제로 평문을 반환한 뒤에만 초록색이 되며, 메시지의 어떤 필드로도, 암호화된 봉투가 도착했다는 사실만으로도 초록색이 되지 않습니다. 이때는 “Encrypted end-to-end. Opened with your key in this browser”라고 표시됩니다. 그에 못 미치는 모든 경우는 공용 문구가 아니라 각자의 문장을 가집니다. 확인 중, 키 잠김, 이 브라우저에 없는 키로 봉인됨, 열 수 없음, 여기에 키가 전혀 없음, 그리고 이 브라우저가 확인을 허용하지 않음입니다. “확인할 수 없었다”와 “키가 없다”는 서로 다른 진술이며, 이 행은 어느 쪽을 받았는지 읽게 만듭니다.
- S/MIME는 여전히 열 수 없습니다. X.509 인증서 아래의 CMS이고 openpgp.js는 그것을 다룰 수 없으며, 설령 다룰 수 있더라도 키를 보관할 인증서 저장소가 제품에 없습니다. 그래서 S/MIME 메시지는 OpenEmail이 열 수 없다고 밝히며, 아무 효과도 없을 잠금 해제를 제안하지 않습니다.
- 개인 키는 브라우저에서 생성되어 그곳을 떠나지 않습니다. 암호화된 형태로도, 백업 안에도, 지원 도구 안에도 없습니다. 서버로 전송되지 않으므로 여기에는 넘겨주거나 소환장에 응하거나 유출될 것이 없습니다. 개인 키는 openemail-keyring이라는 자체 IndexedDB 데이터베이스에 암호 구문으로 잠긴 채 저장되며, 이는 “캐시를 지워 보세요”라는 안내와 디버그 콘솔의 초기화에서 의도적으로 벗어나 있습니다. 두 동작 모두 쿼리 캐시를 지우는데, 키가 그 옆에 저장되어 있다면 일상적인 지원 안내가 그 계정이 받은 모든 암호화된 메시지를 영구히 파괴하게 되기 때문입니다. 계정을 삭제하면 키도 삭제됩니다. 그때는 메일도 함께 사라지기 때문입니다. 잠금을 해제하면 비활성 15분, 최대 8시간 동안 메모리에 유지되며, 그 이후 다음 봉인된 메시지를 읽으려면 암호 구문을 다시 입력해야 합니다.
- 에스크로도 복구도 없으며, 이는 미구현이 아니라 영구적인 설계입니다. 암호 구문이 유일한 통로입니다. 잊어버리면 누군가 봉인해 보낸 모든 메시지는 저희를 포함해 아무도 읽을 수 없는 암호문으로 서버에 남습니다. 메일은 사라지고, 아무리 요청해도 되돌릴 수 없습니다. 등록 화면은 첫 키가 만들어지기 전에 직접 체크해야 하는 체크박스와 함께 그 사실을 밝히며, 백업 파일은 필수이고 그것을 내려받기 전까지 Done 버튼은 비활성 상태로 남습니다. 그 파일은 이 브라우저가 쓰는 추가적인 저장 보호 없이 의도적으로 기록되므로, 오래된 gpg 빌드에서도 가져올 수 있습니다. 다른 곳에서 열 수 없는 백업은 백업이 아니기 때문입니다. 또한 키는 한 기기의 한 브라우저에만 존재하므로, 휴대폰이나 두 번째 노트북에는 그 파일을 가져오기 전까지 아무것도 없습니다.
- 디렉터리는 공개 키만 보관하며, 키는 메일함이 아니라 사람에게 속합니다. 주소에 묶인 키라면 그 주소를 공유하는 모든 사람에게 복사되어야 하는데, 그것은 이름만 다른 에스크로이기 때문입니다. 조회에는 로그인된 세션과 발송 권한이 필요하며 공개 엔드포인트는 결코 아닙니다. 누구나 쓸 수 있는 주소 탐색은 이곳에서 어떤 주소가 살아 있는 메일함인지 알려 주는 오라클이 되기 때문입니다. 조회는 “그 도메인은 호스팅하지 않는다”와 “호스팅하지만 아무도 키를 공개하지 않았다”에 똑같이 응답합니다. 둘을 구분해 주면 오라클을 없애는 것이 아니라 로그인 뒤로 옮길 뿐이기 때문입니다. 그리고 키는 누군가 폐기를 기억해 내는 시점이 아니라, 소유자가 그 주소에 대한 접근 권한을 잃는 순간부터 더 이상 제공되지 않습니다.
- 모든 바이트는 글을 쓰는 순간 브라우저에서 봉인되며, 지연 발송이 아예 가능한 이유가 바로 그것입니다. 예약된 메시지나 실행 취소 대기 중인 메시지는 암호문으로 저장되고, 키를 갖지 않아 내용을 전혀 읽을 수 없는 큐가 나중에 발송합니다. 디렉터리 조회에 실패(FAILED)한 수신자는 키가 없는 것으로 조용히 처리되지 않고 발송을 막습니다. 그리고 봉인된 메시지는 메시지를 통째로 전달하는 경로로만 나갑니다. HTML 본문을 받는 발송 경로라면 아머를 보이는 텍스트로 올리고 성공했다고 보고할 것이므로, 그런 경로로 가게 될 발송은 한 바이트라도 나간 뒤가 아니라 그 전에 거부됩니다.
- 봉인의 비용은 추측이 아니라 측정한 값입니다. 아머는 원본 바이트의 약 1.86배이므로, 발송 경로가 허용하는 5 MB에는 첨부 파일 약 2.7 MB가 들어갑니다. 작성기는 그 한도를 넘는 데이터를 전송이 어차피 거부할 것이므로, 몇 초를 들여 암호화하기 전에 미리 거부합니다. 암호화된 메일은 열람과 클릭을 전혀 보고하지 않습니다. 수신자별 추적은 사람마다 본문을 다르게 만드는 방식으로 동작하는데 봉인된 블록 하나는 달라질 수 없고, 다시 쓴 링크는 저희가 읽을 수 있는 링크여서 이 주장과 정반대이기 때문입니다. 암호화된 발송은 멱등 키로 중복 제거될 수도 없습니다. 매번 새로운 세션 키가 쓰이므로 시도할 때마다 암호문의 바이트가 달라지고, 재시도가 원본과 같은 지문을 남기지 않습니다.
- 예약 발송은 발송 시점이 아니라 작성 시점에 봉인합니다. 작성기는 메시지가 실제로 나가는 날이 아니라 글을 쓰는 날에 수신자가 보유한 키로 암호문을 만듭니다. 이 제품이 템플릿과 번역에 이미 적용하고 있는 규칙과 같아서, 예약된 메시지는 이후에 바뀐 것이 아니라 승인한 내용을 담고 갑니다. 차이는 오래된 템플릿은 그저 낡았을 뿐이지만 오래된 키는 읽을 수 없다는 점입니다. 그래서 작성기는 단서를 다는 대신 날짜를 문장으로 밝힙니다. “Sealed now, sent on …. Everyone on it will need the key they have today.” 나머지 절반을 지키는 거부 로직은 배포되었지만 아직 한 번도 발동하지 않았습니다. 그런 메시지가 아직 존재할 수 없기 때문입니다. 만약 존재한다면, 이후의 편집은 조용히 허용되는 대신 거부됩니다. 본문을 수정하면 암호문 위에 평문을 쓰게 되고 수신자를 바꾸면 봉인 대상이 달라지는데, 둘 중 어느 쪽이든 모든 화면은 여전히 암호화되었다고 말하는 동안 평문으로 배달되기 때문입니다.
- 작성기가 아예 제공하지 않고, 전송 단계에서 실패하는 대신 미리 알려 주는 것이 두 가지 있습니다. 템플릿은 게시된 버전을 바탕으로 서버에서 렌더링되므로 이 브라우저에는 봉인할 것이 없습니다. 회신은 아래에 대화를 인용하는데 그 인용은 본문이 봉인된 뒤에 덧붙으므로, 스레드 전체의 읽을 수 있는 사본이 봉인 바깥에 남게 됩니다. 그래서 인용된 이전 대화가 암호문 안으로 들어오기 전까지 회신과 전달은 암호화할 수 없습니다.
- 이 가운데 어느 것도 제목 줄, 수신자, 보낸 시각을 숨기지 않습니다. 제목은 봉인된 메시지에서도 다른 메시지와 똑같이 평문으로 전달되며, 읽기 창은 메시지 자체에 그렇게 밝힙니다. “The subject and the addresses travelled in the clear; this text did not.” PGP는 본문을 보호하며, 어떤 구현도 나머지는 보호하지 않습니다. 초안도 봉인되지 않습니다. 작성하는 동안 자동 저장이 계속 메일함에 평문을 쓰기 때문이고, 조용히 저장하지 않으면 작업이 사라지기 때문이며, 자물쇠는 그 사실을 문장으로 밝힙니다.
- 봉인된 채로 도착한 메일을 인식하는 일이 먼저였고 지금도 유효합니다. PGP/MIME, 인라인 PGP 아머, S/MIME는 예전에 본문이 비어 있고 의미 없는 첨부 파일 두 개가 달린 형태로 렌더링되었습니다. 파서가 일반 텍스트와 HTML만 읽을 수 있는 것으로 취급하고 나머지는 파일 목록으로 떨어뜨렸기 때문입니다. 이제 그 파트들은 식별되고, 프로토콜 구성 요소는 첨부 목록에서 제외되며, 암호문은 온전히 저장되어 리더가 새로 무언가를 가져오지 않고 그것을 복호화합니다. 그리고 여기서 아무도 열 수 없는 메시지는 그 사실을 분명히 밝힙니다.
- 서명된 메시지는 별개로 취급합니다. 실제로 별개이기 때문입니다. 서명된 메시지의 본문은 읽을 수 있으므로 검색, 규칙을 비롯한 모든 기능이 그대로 동작하고, 잠금 뒤에 가려지지 않으며 복호화를 거치지도 않습니다. 해당 행은 “Signed by the sender. Signature not checked”라고 흐리게 표시하며, 이곳에서 실제로 서명을 검증할 수 있게 될 때까지 계속 흐린 상태로 남습니다. 복호화에 성공한 메시지를 포함해 아직 아무것도 서명을 검증하지 않습니다. 메시지가 봉인되어 있음을 아는 것과 그것을 연 것은 다르며, 그것을 연 것과 누가 봉인했는지 아는 것도 다릅니다.
- 암호화된 메시지에서는 본문을 읽었을 모든 기능으로부터 본문을 차단합니다. 검색 미리보기 문구, 피싱 점수 산정의 본문 단계, AI 작성 검사, 규칙의 본문 조건, 캘린더 초대 가져오기, 스레드 요약이 그 대상입니다. 피싱 검사의 인증 부분은 계속 실행됩니다. DMARC, DKIM, SPF는 암호문이 가리지 못하는 헤더에서 읽기 때문입니다. 복호화해도 이 중 달라지는 것은 없습니다. 평문은 그것을 연 탭 안에만 존재하므로, 봉인된 메시지는 읽은 뒤에도 검색되지 않고 AI 기능에서 제외된 채로 남으며 번역도 제공되지 않습니다. 이는 누락이 아니라 이 주장의 비용입니다.