본문으로 건너뛰기

저수준 ADNL

Abstract Datagram Network Layer (ADNL)는 네트워크 피어들이 서로 통신하는 것을 돕는 TON의 핵심 프로토콜입니다.

피어 신원

각 피어는 최소 하나의 신원을 가져야 하며, 여러 개를 사용할 수 있지만 필수는 아닙니다. 각 신원은 피어 간 Diffie-Hellman을 수행하는 데 사용되는 키쌍입니다. 추상 네트워크 주소는 공개 키에서 다음과 같이 도출됩니다: address = SHA-256(type_id || public_key). type_id는 리틀 엔디안 uint32로 직렬화되어야 합니다.

공개 키 암호 시스템 목록

type_id암호시스템
0x4813b4c6ed255191

1. x25519를 수행하려면 키쌍이 x25519 형식으로 생성되어야 합니다. 하지만 공개 키는 ed25519 형식으로 네트워크를 통해 전송되므로 공개 키를 x25519에서 ed25519로 변환해야 합니다. 이러한 변환의 예시는 Kotlin용 여기에서 찾을 수 있습니다.

클라이언트-서버 프로토콜 (TCP 기반 ADNL)

클라이언트는 TCP를 사용하여 서버에 연결하고 ADNL 핸드셰이크 패킷을 보냅니다. 이 패킷에는 서버 추상 주소, 클라이언트 공개 키 및 클라이언트가 결정한 암호화된 AES-CTR 세션 매개변수가 포함됩니다.

핸드셰이크

먼저, 클라이언트는 서버 키의 type_id를 고려하여 자신의 개인 키와 서버 공개 키를 사용하여 키 합의 프로토콜(예: x25519)을 수행해야 합니다. 그 결과로 클라이언트는 이후 단계에서 세션 키 암호화에 사용되는 secret을 얻게 됩니다.

그런 다음 클라이언트는 AES-CTR 세션 매개변수, 16바이트 논스 및 32바이트 키를 TX(클라이언트->서버)와 RX(서버->클라이언트) 방향 모두에 대해 생성하고 다음과 같이 160바이트 버퍼로 직렬화해야 합니다:

매개변수크기
rx_key32 바이트
tx_key32 바이트
rx_nonce16 바이트
tx_nonce16 바이트
padding64 바이트

패딩의 목적은 알려지지 않았으며 서버 구현에서 사용되지 않습니다. 160바이트 버퍼 전체를 무작위 바이트로 채우는 것이 권장됩니다. 그렇지 않으면 공격자가 손상된 AES-CTR 세션 매개변수를 사용하여 활성 MitM 공격을 수행할 수 있습니다.

다음 단계는 위의 키 합의 프로토콜을 통해 얻은 secret을 사용하여 세션 매개변수를 암호화하는 것입니다. 이를 위해 AES-256은 128비트 빅 엔디안 카운터를 사용하는 CTR 모드에서 다음과 같이 계산된 (key, nonce) 쌍으로 초기화되어야 합니다(aes_params는 위에서 구축된 160바이트 버퍼):

hash = SHA-256(aes_params)
key = secret[0..16] || hash[16..32]
nonce = hash[0..4] || secret[20..32]

aes_params의 암호화(E(aes_params)로 표시)가 끝나면 더 이상 필요하지 않으므로 AES는 제거되어야 합니다.

이제 모든 정보를 256바이트 핸드셰이크 패킷으로 직렬화하여 서버에 보낼 준비가 되었습니다:

매개변수크기참고
receiver_address32 바이트해당 섹션에 설명된 서버 피어 신원
sender_public32 바이트클라이언트 공개 키
SHA-256(aes_params)32 바이트세션 매개변수의 무결성 증명
E(aes_params)160 바이트암호화된 세션 매개변수

서버는 클라이언트와 동일한 방식으로 키 합의 프로토콜에서 도출된 secret을 사용하여 세션 매개변수를 복호화해야 합니다. 그런 다음 서버는 프로토콜의 보안 속성을 확인하기 위해 다음 검사를 수행해야 합니다:

  1. 서버는 receiver_address에 해당하는 개인 키를 가지고 있어야 합니다. 그렇지 않으면 키 합의 프로토콜을 수행할 방법이 없습니다.
  2. SHA-256(aes_params) == SHA-256(D(E(aes_params))), 그렇지 않으면 키 합의 프로토콜이 실패했고 양쪽의 secret이 같지 않습니다.

이러한 검사 중 하나라도 실패하면 서버는 클라이언트에 응답하지 않고 즉시 연결을 끊습니다. 모든 검사가 통과되면 서버는 지정된 receiver_address에 대한 개인 키를 소유하고 있음을 증명하기 위해 클라이언트에 빈 데이터그램(데이터그램 섹션 참조)을 발행해야 합니다.

데이터그램

클라이언트와 서버 모두 TX와 RX 방향 모두에 대해 각각 두 개의 AES-CTR 인스턴스를 초기화해야 합니다. 128비트 빅 엔디안 카운터를 사용하는 CTR 모드에서 AES-256을 사용해야 합니다. 각 AES 인스턴스는 핸드셰이크의 aes_params에서 가져올 수 있는 해당 (key, nonce) 쌍을 사용하여 초기화됩니다.

데이터그램을 보내기 위해 피어(클라이언트 또는 서버)는 다음 구조를 구축하고 암호화하여 다른 피어에게 보내야 합니다:

매개변수크기참고
length4 바이트 (LE)length 필드를 제외한 전체 데이터그램의 길이
nonce32 바이트무작위 값
bufferlength - 64 바이트다른 쪽으로 보낼 실제 데이터
hash32 바이트무결성을 보장하기 위한 `SHA-256(nonce \

전체 구조는 해당 AES 인스턴스(클라이언트 -> 서버는 TX, 서버 -> 클라이언트는 RX)를 사용하여 암호화되어야 합니다.

수신 피어는 처음 4바이트를 가져와서 length 필드로 복호화하고 정확히 length 바이트를 읽어 전체 데이터그램을 얻어야 합니다. 수신 피어는 더 일찍 buffer를 복호화하고 처리하기 시작할 수 있지만, 의도적으로 또는 우연히 손상될 수 있다는 점을 고려해야 합니다. buffer의 무결성을 보장하기 위해 데이터그램 hash를 확인해야 합니다. 실패할 경우 새로운 데이터그램을 발행할 수 없으며 연결을 끊어야 합니다.

세션의 첫 번째 데이터그램은 핸드셰이크 패킷이 서버에 의해 성공적으로 수락된 후 항상 서버에서 클라이언트로 전송되며 실제 버퍼는 비어 있습니다. 클라이언트는 이를 복호화해야 하며 실패할 경우 서버와 연결을 끊어야 합니다. 이는 서버가 프로토콜을 제대로 따르지 않았고 서버와 클라이언트 측의 실제 세션 키가 다르다는 것을 의미하기 때문입니다.

통신 세부사항

통신 세부사항을 더 자세히 알아보려면 ADNL TCP - Liteserver 문서에서 몇 가지 예시를 확인할 수 있습니다.

보안 고려사항

핸드셰이크 패딩

초기 TON 팀이 이 필드를 핸드셰이크에 포함하기로 한 이유는 알려지지 않았습니다. aes_params의 무결성은 SHA-256 해시로 보호되고 기밀성은 secret 매개변수에서 도출된 키로 보호됩니다. 아마도 어느 시점에서 AES-CTR에서 마이그레이션하려는 의도였을 것입니다. 이를 위해 사양은 피어가 업데이트된 기본 요소를 사용할 준비가 되었음을 알리는 특별한 매직 값을 aes_params에 포함하도록 확장될 수 있습니다. 이러한 핸드셰이크에 대한 응답은 다른 피어가 실제로 어떤 방식을 사용하고 있는지 확인하기 위해 새로운 방식과 이전 방식으로 두 번 복호화될 수 있습니다.

세션 매개변수 암호화 키 도출 프로세스

암호화 키가 secret 매개변수에서만 도출되는 경우, secret이 정적이므로 키도 정적일 것입니다. 각 세션마다 새로운 암호화 키를 도출하기 위해 개발자들은 aes_params가 무작위이면 무작위인 SHA-256(aes_params)도 사용합니다. 하지만 서로 다른 하위 배열을 연결하는 실제 키 도출 알고리즘은 유해한 것으로 간주됩니다.

데이터그램 논스

데이터그램에 nonce 필드가 있는 이유가 명확하지 않습니다. AES의 세션 바운드 키와 CTR 모드의 암호화 때문에 이 필드가 없어도 두 암호문은 다르기 때문입니다. 하지만 논스가 없거나 예측 가능한 경우 다음과 같은 공격이 가능합니다. CTR 암호화 모드는 AES와 같은 블록 암호를 스트림 암호로 전환하여 비트 플리핑 공격이 가능하게 합니다. 공격자가 암호화된 데이터그램에 속하는 평문을 알고 있다면 순수한 키스트림을 얻어 자신의 평문과 XOR 연산을 수행하고 피어가 보낸 메시지를 효율적으로 대체할 수 있습니다. 버퍼 무결성은 SHA-256 해시로 보호되지만 전체 평문을 알고 있다는 것은 해시도 알고 있다는 것을 의미하므로 공격자는 이것도 대체할 수 있습니다. 논스 필드는 이러한 공격을 방지하기 위해 존재하므로 공격자는 논스를 알지 못하면 SHA-256을 대체할 수 없습니다.

P2P 프로토콜 (UDP 기반 ADNL)

자세한 설명은 ADNL UDP - Internode 문서에서 찾을 수 있습니다.

참조

커뮤니티에 기여해 주신 hacker-volodya님께 감사드립니다!
GitHub에 있는 원본 문서 링크입니다.