AES-GCM-HKDF 스트리밍 AEAD

이 문서에서는 type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey로 proto 형식으로 인코딩된 AES-GCM-HKDF 스트리밍 키로 표현되는 수학 함수를 공식적으로 정의합니다.

이 암호화는 대략적으로 HRRV15를 기반으로 합니다1. 보안 분석은 HS202을 참조합니다.

키 및 매개변수

키는 다음 부분으로 설명됩니다 (이 문서의 모든 크기는 바이트 단위임).

  • \(\mathrm{KeyValue}\): 바이트 문자열입니다.
  • \(\mathrm{CiphertextSegmentSize} \in \{1, 2, \ldots, 2^{31}-1\}\).
  • \(\mathrm{DerivedKeySize} \in \{16, 32\}\).
  • \(\mathrm{HkdfHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256}, \mathrm{SHA512}\}\).

유효한 키는 다음 속성을 추가로 충족합니다.

  • \(\mathrm{len}(\mathrm{KeyValue}) \geq \mathrm{DerivedKeySize}\).
  • \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} + 24\) (이는 \(\mathrm{len}(\mathrm{Header}) + 16\) 아래 설명과 동일함).

이러한 속성을 하나도 충족하지 않는 키는 키가 파싱되거나 해당하는 프리미티브가 생성될 때 Tink에서 거부됩니다.

암호화 함수

연결된 데이터\(\mathrm{AssociatedData}\)로 \(\mathrm{Msg}\) 메시지를 암호화하기 위해 Google에서는 헤더를 만들고, 메시지를 세그먼트로 분할하고, 각 세그먼트를 암호화하고, 암호화된 세그먼트를 연결합니다.

헤더 만들기

길이가 \(\mathrm{Salt}\) 인 균일한 임의의 문자열과\(\mathrm{DerivedKeySize}\) 길이가 7인 균일한 임의의 문자열을 \(\mathrm{NoncePrefix}\)선택합니다.

그런 다음 헤더의 길이가 단일 바이트로 인코딩되는 \(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\)를 설정합니다. \(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\)를 참고하세요.

그런 다음 \(\mathrm{HkdfHashType}\)와 입력 \(\mathrm{ikm} := \mathrm{KeyValue}\), \(\mathrm{salt} := \mathrm{Salt}\), \(\mathrm{info} := \mathrm{AssociatedData}\)에 의해 제공된 해시 함수와 함께 HKDF3을 사용합니다(출력 길이 \(\mathrm{DerivedKeySize}\)와 함께). 그 결과를 \(\mathrm{DerivedKey}\)라고 합니다.

메일 분할

그런 다음 \(\mathrm{Msg}\) 메시지는 \(\mathrm{Msg} = M_0 \| M_1 \| \cdots \| M_{n-1}\)부분으로 나뉩니다.

길이는 다음 조건을 만족하도록 선택됩니다.

  • \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{16}\}\).
  • \(n>1\)인 경우 \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1}) \in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{16}\}\).
  • \(n>1\)인 경우 \(\mathrm{len}(M_{0}), \ldots, \mathrm{len}(M_{n-2})\) 는 위에 따라 제약 조건에 따라 최대 길이를 가져야 합니다.

\(n\) 최대 \(2^{32}\)가능합니다. 그렇지 않으면 암호화가 실패합니다.

블록 암호화

세그먼트 \(M_i\)를 암호화하기 위해 \(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b\)를 계산합니다. 여기서 \(\mathrm{i}\) 는 big-endian 인코딩에서 4바이트이고 $i < n-1$ 인 경우 바이트 $b$ 는 0x00이고, 그렇지 않으면 0x01입니다.

그런 다음 \(M_i\) AES-GCM4를 사용하여 암호화합니다. 여기서 키는\(\mathrm{DerivedKey}\), 초기화 벡터는 \(\mathrm{IV}_i\), 연결된 데이터는 빈 문자열입니다. \(C_i\) 이 암호화의 결과입니다(즉, 연결된 AES-GCM 참조의 섹션 5.2.1.2에서 \(C\) 및 \(T\) 의 연결).

암호화된 세그먼트 연결

마지막으로 모든 세그먼트는 최종 암호문인 \(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\)로 연결됩니다.

복호화

복호화는 암호화를 반전시킵니다. 헤더를 사용하여 \mathrm{NoncePrefix}$$를 가져오고 암호문의 각 세그먼트를 개별적으로 복호화합니다.

API는 무작위 액세스 또는 파일의 끝을 검사하지 않고 파일의 시작 부분에 대한 액세스를 허용할 수 있으며 또한 일반적으로 허용합니다. 이는 이전의 모든 암호문 블록과 나머지 암호문 블록을 복호화하지 않고 \(C_i\)에서 \(M_i\) 를 복호화할 수 있기 때문에 의도된 것입니다.

그러나 API는 사용자가 파일 끝 오류와 복호화 오류를 혼동하지 않도록 주의해야 합니다. 두 경우 모두 API는 오류를 반환해야 할 수 있으며 차이를 무시하면 공격자가 파일을 효과적으로 자를 수 있습니다.

키 직렬화 및 파싱

키를 'Tink Proto' 형식으로 직렬화하려면 먼저 aes_gcm_hkdf_streaming.proto에 지정된 proto에 명확한 방식으로 매개변수를 매핑합니다. version 필드는 0으로 설정해야 합니다. 그런 다음 일반 proto 직렬화를 사용하여 직렬화하고 결과 문자열을 KeyData proto의 필드 값에 삽입합니다. type_url 필드를 type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey로 설정합니다. 그런 다음 key_material_typeSYMMETRIC로 설정하고 키 세트에 삽입합니다. 일반적으로 output_prefix_typeRAW로 설정합니다. 예외적으로 output_prefix_type에 설정된 다른 값으로 키가 파싱된 경우 Tink가 RAW 또는 이전 값을 쓸 수 있습니다.

키를 파싱하기 위해 proto를 파싱할 때 일반적으로 사용하는 방식으로 위 프로세스를 반대로 진행합니다. key_material_type 필드는 무시됩니다. output_prefix_type 값을 무시하거나 output_prefix_typeRAW와 다른 키를 거부할 수 있습니다. version가 0과 다른 키는 거부해야 합니다.

알려진 문제

위 암호화 함수의 구현은 포크 안전하지 않을 것으로 예상됩니다. 포크 안전을 참고하세요.

참조


  1. Hoang, Reyhanitabar, Rogaway, Vizar, 2015년 온라인 인증 암호화와 논스 재사용(nonce-reuse) 오용 방지가 포함됩니다 CRYPTO 2015. https://eprint.iacr.org/2015/189 

  2. 호앙, 셴, 2020년. Google Tink 라이브러리의 스트리밍 암호화 보안. https://eprint.iacr.org/2020/1019 

  3. RFC 5869입니다. HMAC 기반 추출 및 확장 키 파생 함수 (HKDF). https://www.rfc-editor.org/rfc/rfc5869 

  4. NIST SP 800-38D. 암호화 모드 차단 관련 권장사항: Galois/Counter Mode (GCM) 및 GMAC. https://csrc.nist.gov/pubs/sp/800/38/d/final