작동 방식

Crocodile은 어떻게 대화를 여러분의 것으로 지키는가

아키텍처와 프로토콜을 쉬운 말로 둘러봅니다. 각 장은 기본 개념으로 시작하며, 기술적인 내용은 ‘기술적인 세부 사항’을 펼쳐 보세요.

장 1

네 종류의 참여자

Crocodile은 역할을 나누어, 여러분의 말은 대화 상대만 볼 수 있고 낯선 사람이 운영하는 부분은 절대 볼 수 없게 합니다.

내 앱

키를 보관하고, 보내는 모든 것을 암호화하고, 받는 것을 복호화합니다. 메시지 기록은 여기에만 있고 다른 어디에도 없습니다.

아는 것: 주고받는 모든 것. 원래 여러분의 것이니까요.

호스트

음성 방이나 스페이스마다 한 멤버의 앱이 선택되어 암호화된 트래픽을 나머지에게 전달합니다. 다른 사람과 똑같은 멤버일 뿐입니다.

아는 것: 멤버의 IP 주소와 누가 말하는지. 무슨 말을 하는지는 절대 모릅니다.

조정 서버

자원봉사자가 운영하는 전화 교환대입니다. 프로필과 스페이스를 저장하고, 누가 온라인인지 알려 주고, 호스트를 고릅니다. 하나의 네트워크로 연결되어 있습니다.

아는 것: 프로필, 멤버십, 누가 온라인인지. 무슨 말을 하는지는 절대 모릅니다.

디렉터리

새 앱이 어디서 시작할지 알 수 있게 해 주는 조정 서버의 전화번호부입니다. 사람에 대해서는 아무것도 모릅니다.

아는 것: 서버 목록만.

장 2

계정은 비밀번호가 아니라 키입니다

Crocodile을 설치하면 앱이 컴퓨터에서 32바이트의 무작위 비밀 값을 만듭니다. 여기서 두 개의 키가 나옵니다. 하나는 내가 게시하는 것에 서명하는 키, 다른 하나는 암호화된 메시지를 받는 키입니다. 사용자 ID는 서명 키의 지문입니다.

ID가 키에서 계산되므로 서버가 다른 키를 끼워 넣어 나를 사칭할 수 없습니다. 훔쳐 갈 비밀번호도, 고객 지원을 속여 받아 낼 재설정도 없습니다. 비밀 값은 복구 키로 한 번만 표시되며, 시스템 키체인으로 암호화되어 기기에 보관됩니다.

기술적인 세부 사항
  • 32바이트 시드에서 Ed25519 서명 키와 X25519 암호화 키를 도출합니다. 사용자 ID는 base32(SHA-256(서명 공개 키))의 앞 26자입니다.
  • 각 기기는 서명된 기기 레코드를 게시하며, 매주 교체되는 사전 키 번들(X25519 + ML-KEM-768)을 함께 둡니다. 오래된 사전 키 비밀 값은 유예 기간이 지나면 삭제됩니다.
  • 보안 번호를 사용하면 다른 안전한 메신저처럼 두 사람이 다른 경로로 키를 비교할 수 있습니다.
  • 로컬 데이터(신원, 사전 키, 기록)는 운영체제 키체인이 보호하는 키로 암호화되어 저장됩니다.

장 3

가장 빠른 서버 찾기

앱은 디렉터리에서 조정 서버 목록을 내려받아 각 서버의 응답 속도를 측정하고 가장 빠른 서버에 연결합니다. 두 번째 서버는 대기하고 있다가, 서버가 다운되면 앱이 즉시 전환합니다.

가장 먼저 서버가 자신의 신원을 증명하고, 앱과 서버가 그 연결에 쓸 키를 합의합니다. 그 이후에는 둘이 주고받는 메타데이터조차 HTTPS 위에서 한 번 더 암호화되므로, 집에 있는 서버에서도 안전하게 운영할 수 있습니다.

기술적인 세부 사항
  • 디렉터리는 각 서버를 해당 서버가 직접 서명한 형태로, 주소를 가린 채 나열합니다. 앱은 항목을 사용하기 전에 서명을 검증합니다.
  • 서버가 서명한 hello 메시지에는 새로운 하이브리드 키 교환 제안(X25519 + ML-KEM-768)이 담겨 있습니다. 앱은 디렉터리의 키로 이를 검증하고, 응답하고, 전체 기록에 서명합니다. 이후 모든 프레임은 AES-256-GCM으로 암호화되고 패딩됩니다.
  • 서버는 두 포트에서 STUN에도 응답하므로, 앱은 Google이나 Cloudflare에 묻지 않고도 자신의 공인 주소와 NAT 유형을 알 수 있습니다.

장 4

나에 대해 거짓말할 수 없는 서버

내 프로필, 내가 만든 스페이스, 그 초대, 멤버 목록은 모두 작성자가 서명한 작은 레코드입니다. 서버는 이를 저장하고 공유하며, 모든 서버와 모든 앱이 서명을 검증합니다. 서버는 레코드 전달을 거부할 수는 있어도, 수정하거나 지어낼 수는 없습니다.

조정 서버들은 서로를 찾아 네트워크를 이룹니다. 각 레코드는 해시로 선택된 여러 서버에 보관되고, 이 서버들이 변경 사항을 서로 복사하므로 어느 서버가 다운되어도 네트워크는 계속 작동합니다.

기술적인 세부 사항
  • 레코드 종류: 프로필, 봉인된 친구 목록, 스페이스, 초대, 멤버, 기기, 노트. 각각 작성 권한이 있는 작성자가 있으며, 검증(서명, 권한, ‘마지막 쓰기 우선’ 버전)은 서버와 클라이언트에서 동일합니다.
  • 친구 목록은 소유자의 기기만 읽을 수 있게 암호화되고, 친구 요청은 보낸 사람을 밝히지 않는 봉인된 노트로 전달되므로 서버는 누가 누구와 친구인지 알지 못합니다.
  • 각 레코드는 해당 샤드의 일관 해시 링에서 앞쪽 몇 대의 서버가 담당합니다. 담당 서버는 쓰기를 복제하고, 시퀀스 커서로 빠진 부분을 따라잡으며, 서버가 참여하고 떠날 때 레코드를 넘겨줍니다.

장 5

친구가 호스트하고, 백업이 대기합니다

사람들이 음성 방에 들어오거나 스페이스를 열면, 조정 서버는 모두의 연결 상태(네트워크가 얼마나 열려 있는지, 업로드 속도, 컴퓨터, 배터리 사용 여부)를 보고 가장 적합한 멤버를 호스트로 고릅니다. 그다음으로 적합한 멤버가 백업입니다.

모두가 호스트의 앱에 연결하고, 호스트는 암호화된 오디오를 전달합니다. 부담을 줄이기 위해 각 청취자에게는 가장 큰 소리로 말하는 몇 명만 보냅니다. 오디오는 전달되는 동안 절대 평문으로 드러나지 않습니다. 호스트는 봉인된 패킷을 나를 뿐입니다.

호스트가 나가거나 연결이 끊기면 백업이 넘겨받고 모두가 백업에 다시 연결합니다. 대개는 아무도 눈치채지 못합니다.

기술적인 세부 사항
  • 호스트는 앱 안에서 작은 SFU(selective forwarding unit)를 실행합니다. 각 멤버는 여기에 WebRTC 연결 하나를 열며, 업스트림 오디오 트랙 1개, 다운스트림 화자 슬롯 5개, 데이터 채널 1개를 사용합니다.
  • 화자 슬롯은 RFC 6464 오디오 레벨 헤더(“last-N”)에 따라 배정되고, 시퀀스 번호를 다시 써서 각 슬롯이 끊김 없는 하나의 스트림으로 유지됩니다.
  • 오퍼와 응답은 세션, 에포크, 양측 ID, DTLS 지문에 대해 신원 키로 서명되므로 조정 서버가 중간에 끼어들 수 없습니다.
  • 호스트가 바뀔 때마다 에포크 번호가 올라가고 오래된 오퍼는 무시됩니다. 호스트에 연결할 수 없는 멤버는 이를 보고하고, 절반이 동의하면 백업이 넘겨받습니다.

장 6

겹겹이 쌓인 암호화

모든 것은 기기를 떠나기 전에 암호화되고, 키는 대화에 참여한 사람만 가지고 있습니다. 메시지가 전달되는 동안 무슨 일이 일어나는지 살펴보세요.

메시지 하나, 네 겹의 보호

앱은 메시지에 서명해 다른 사람이 그것이 나에게서 왔음을 증명할 수 있게 하고, 길이로 아무것도 드러나지 않도록 패딩하고, 대화 멤버만 가진 키로 암호화한 뒤, 그 자체로도 암호화된 연결로 보냅니다. 중간에 누가 있든 보이는 것은 표준 크기의 뒤죽박죽 바이트뿐입니다.

양자 컴퓨터에 대비

키는 검증된 기존 방식인 X25519와 새로운 양자 내성 표준 ML-KEM-768을 결합한 하이브리드 방식으로 교환됩니다. 공격자는 둘 다 깨야 합니다. 누군가 오늘 암호화된 트래픽을 기록해 두었다가 양자 컴퓨터로 해독할 날을 기다릴 수 있기 때문에 이 점이 중요합니다.

계속 앞으로 나아가는 키

각 멤버의 키는 래칫처럼 돌아갑니다. 메시지를 보낼 때마다 앞으로만 나아가는 체인의 다음 키로 바뀌고 이전 키는 삭제됩니다. 오늘 키를 훔친 사람도 어제의 메시지는 읽을 수 없습니다. 30분마다 그리고 누군가 나갈 때마다 모두가 새로운 무작위 키로 새 체인을 시작하므로, 훔친 키도 금세 쓸모없어집니다.

음성, 프레임 단위로

목소리는 1초에 약 50개의 작은 프레임으로 나뉘어, 마이크 바로 다음, 네트워크에 닿기 전에 암호화됩니다. 암호화할 수 없는 프레임은 버려지고 절대 평문으로 전송되지 않습니다. 음성은 고정 비트레이트를 사용하므로 패킷 크기로 무슨 말을 하는지 알 수 없습니다.

기술적인 세부 사항
  • 쌍별 비밀 값(발신자 키 배포, 기록 동기화, 기기 연결, 서버 채널)은 HKDF-SHA256으로 X25519와 ML-KEM-768(FIPS 203)을 결합합니다.
  • 발신자 키에는 두 개의 HKDF 체인이 있습니다. 텍스트 체인은 메시지마다, 음성 체인은 30초마다 진행됩니다. 키는 30분마다 그리고 누군가 나갈 때 교체되며, 앱이 서명된 레코드로 멤버십을 직접 확인한 기기에만 전송됩니다.
  • 음성: Encoded Transform 워커가 각 Opus 프레임을 AES-256-GCM과 인증된 SFrame 방식 접미사(키 ID, 세대, 카운터)로 암호화합니다.
  • 텍스트: 작성자가 서명(Ed25519)하고, Padmé로 패딩하고, 텍스트 체인으로 암호화합니다.
  • 기기로 보내는 봉인 상자는 그 기기의 현재 사전 키를 사용하고 발신자가 서명하므로, 오프라인 기기로 보낸 메시지에도 전방 비밀성이 보장됩니다.

장 7

직접 연결할 수 없을 때

사무실이나 대학의 엄격한 방화벽처럼 일부 네트워크는 컴퓨터 간 직접 연결을 막습니다. 이럴 때 Crocodile이 알려 주며, 조정 서버가 트래픽을 중계하도록 허용할 수 있습니다.

릴레이는 이미 종단 간 암호화된 패킷을 전달할 뿐입니다. 크기와 타이밍은 보지만 내용은 보지 못합니다. 허용은 한 번에 1시간 동안 유지되며, 여전히 필요할 때만 갱신됩니다.

기술적인 세부 사항
  • 릴레이는 조정 서버의 UDP 포트에서 단기 자격 증명으로 동작하는 표준 TURN(RFC 5766)입니다. 서버는 사용할 수 있는 인원을 제한합니다.
  • 연결할 때 앱은 두 STUN 포트에 대해 네트워크를 테스트하고, 열림, 제한됨(대칭형 NAT), 차단됨으로 분류한 뒤 필요하면 릴레이를 제안합니다.
  • 각 연결은 네 단계(오퍼, 응답, 네트워크 경로, 암호화된 채널)를 기록하므로, 무언가 실패하면 앱이 어디서 실패했는지 알려 줄 수 있습니다.

장 8

결국 도착하는 메시지

어떤 서버도 기록을 보관하지 않으므로, Crocodile 앱끼리 메시지를 주고받습니다. 오프라인인 사람에게 메시지를 보내면 이렇게 됩니다.

  1. 내 쪽에서 대기

    메시지가 앱의 보낼 편지함에 들어가고 작은 시계가 표시됩니다.

  2. 친구가 온라인으로

    두 사람이 모두 온라인이 되는 순간, 앱이 친구의 기기용으로 봉인한 메시지를 전달합니다.

  3. 확인됨

    친구의 앱이 수신을 확인하면 시계가 사라집니다. 스페이스에서는 메시지를 놓친 멤버가 호스트와 백업에게서 따라잡습니다.

  4. 또는 메일박스

    원하면 서버가 다이렉트 메시지를 최대 7일 동안, 기기별로 봉인해(서버는 읽을 수 없음) 보관하고, 전달되면 삭제합니다.

장 9

컴퓨터 추가하기

새 컴퓨터에 짧은 코드가 표시됩니다. 이미 로그인된 컴퓨터에 그 코드를 입력하고, 두 화면에 같은 6자리 보안 코드가 표시되는지 확인하세요. 일치하면 계정이 암호화되어 전송되고 새 기기를 쓸 수 있습니다.

보안 코드는 두 기기의 키를 모두 포함하므로, 누군가 중간에서 하나를 바꿔치기하려 하면 코드가 달라져 알아챌 수 있습니다.

용어집

보안 관련 글에서 만나게 될 용어를 한두 문장으로 설명합니다.

종단 간 암호화
키는 보내는 사람과 받는 사람만 가지고 있습니다. 서버를 포함해 중간에서 메시지를 나르는 모든 것은 뒤죽박죽인 데이터만 봅니다.
P2P(피어 투 피어)
앱이 중앙 서버 대신 서로 직접 연결합니다.
양자 내성 암호
오늘날 인터넷 대부분이 쓰는 방식을 깰 수 있는 미래의 양자 컴퓨터에 견디도록 설계된 암호화입니다. ML-KEM은 NIST가 2024년에 발표한 표준입니다.
전방 비밀성
오늘 키를 도난당해도 예전 메시지는 열 수 없습니다. 그때 쓴 키는 이미 삭제되었기 때문입니다.
침해 후 보안
키를 도난당한 뒤에도 새로운 무작위 키가 도둑을 금방 다시 밀어냅니다. Crocodile에서는 30분 안에, 또는 누군가 나가는 즉시 그렇게 됩니다.
디지털 서명
개인 키로 만드는 수학적인 도장입니다. 누구나 공개 키로 검증할 수 있고, 개인 키 없이는 아무도 위조할 수 없습니다.
NAT, STUN, TURN
가정용 공유기는 컴퓨터를 하나의 공인 주소 뒤에 숨깁니다(NAT). STUN은 앱이 직접 연결할 수 있도록 자신의 공인 주소를 알아내게 돕고, TURN은 직접 연결이 안 될 때 트래픽을 중계합니다.
SFU
선택적 전달 장치(selective forwarding unit)로, 각 화자의 오디오를 섞거나 복호화하지 않고 각 청취자에게 전달합니다. Crocodile에서는 호스트의 앱 안에서 실행됩니다.
메타데이터
대화의 내용이 아닌 대화에 관한 데이터: 누가 누구와, 언제, 얼마나 자주 이야기했는지. Crocodile은 내용을 완전히 암호화하고 메타데이터를 줄이지만, 서버는 여전히 일부를 봅니다.

아키텍처 문서 전체 읽기(영어)