본문으로 건너뛰기

낮은 수준의 ADNL

추상 데이터그램 네트워크 계층(ADNL)은 네트워크 피어가 서로 통신할 수 있도록 도와주는 TON의 핵심 프로토콜입니다.

동료 신원

각 피어에는 하나 이상의 ID가 있어야 하며, 여러 개를 사용할 수도 있지만 반드시 사용할 필요는 없습니다. 각 ID는 키 쌍으로, 피어 간에 Diffie-Hellman을 수행하는 데 사용됩니다. 추상 네트워크 주소는 공개 키에서 '주소 = SHA-256(type_id || public_key)`와 같은 방식으로 파생됩니다. type_id는 리틀엔디안 uint32로 직렬화해야 합니다.

공개 키 암호화 시스템 목록

type_id암호화 시스템
0x4813b4c6ed255191

_1. x25519를 수행하려면 키쌍을 x25519 형식으로 생성해야 합니다. 그러나 공개 키는 네트워크를 통해 ed25519 형식으로 전송되므로 공개 키를 x25519에서 ed25519로 변환해야 하며, 이러한 변환 예는 여기여기에서 확인할 수 있습니다.

클라이언트-서버 프로토콜(TCP를 통한 ADNL)

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

핸드셰이크

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

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

매개변수크기
rx_key32바이트
tx_key32바이트
rx_nonce16바이트
tx_nonce16바이트
패딩64바이트

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

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

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

E(aes_params)로 표시되는 aes_params`의 암호화 이후에는 더 이상 필요하지 않으므로 AES를 제거해야 합니다.

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

매개변수크기참고
수신자_주소32바이트해당 섹션에 설명된 서버 피어 ID
보낸 사람_공개32바이트클라이언트 공개 키
SHA-256(aes_params)32바이트세션 매개변수의 무결성 증명
E(aes_params)160바이트암호화된 세션 매개변수

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

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

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

데이터그램

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

데이터그램을 전송하려면 피어(클라이언트 또는 서버)가 다음 구조를 만들어 암호화한 후 다른 피어에게 전송해야 합니다:

매개변수크기참고
길이4바이트(LE)길이` 필드를 제외한 전체 데이터그램의 길이
nonce32바이트임의의 값
버퍼길이 - 64` 바이트상대방에게 전송할 실제 데이터
해시32바이트무결성 보장을 위한 `SHA-256(논스 \

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

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

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

커뮤니케이션 세부 정보

통신에 대해 자세히 알아보고 싶으시다면 ADNL TCP - 라이트서버 문서에서 몇 가지 예시를 확인하실 수 있습니다.

보안 고려 사항

핸드셰이크 패딩

초기 TON 팀이 이 필드를 핸드셰이크에 포함하기로 결정한 이유는 알려지지 않았습니다. aes_params무결성은 SHA-256 해시로 보호되고 기밀성은secret매개변수에서 파생된 키로 보호됩니다. 아마도 언젠가는 AES-CTR에서 마이그레이션할 예정이었을 것입니다. 이를 위해aes_params`에 특별한 마법 값을 포함하도록 사양을 확장하여 피어가 업데이트된 프리미티브를 사용할 준비가 되었다는 신호를 보낼 수 있습니다. 이러한 핸드셰이크에 대한 응답은 상대방이 실제로 어떤 체계를 사용하고 있는지 명확히 하기 위해 새 체계와 이전 체계를 사용하여 두 번 복호화할 수 있습니다.

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

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

데이터그램 논스

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

P2P 프로토콜(UDP를 통한 ADNL)

자세한 설명은 ADNL UDP - 인터노드 문서에서 확인할 수 있습니다.

참조

커뮤니티에 기여해 주신 hacker-volodya에게 감사드립니다!\ 여기에 원본 문서 링크가 GitHub에 있습니다.