안전한 프로그래밍의 교훈 1: IV를 재사용하지 마십시오

May 10 2023
나는 최근 삼성 취약점에 대한 기사를 [여기]에 썼고, 한 댓글은 "오래된 버그입니다. IV(초기화 벡터)의 재사용은 매우 기본적인 문제인 것 같습니다."라고 말했습니다. 겉으로 보기에는 주석이 충분히 자세히 설명되어 있지 않을 수 있으므로 "버그"에 대해 설명하고 바라건대 그것이 충격적으로 나쁜 코딩이라는 것을 보여주려고 합니다. 보호 측면에서 거의 태만하고 심지어 볼 수도 있습니다. 의도적인 백도어로.
Unsplash에 있는 Med Badr Chemmaoui의 사진

나는 최근의 삼성 취약점 [ 여기 ] 에 대한 기사를 썼고 한 댓글은 "오래된 버그입니다. IV(초기화 벡터)의 재사용은 매우 기본적인 문제인 것 같습니다."라고 말했습니다. 겉으로 보기에는 주석이 충분히 자세히 설명되어 있지 않을 수 있으므로 "버그"에 대해 설명하고 바라건대 그것이 충격적으로 나쁜 코딩 이라는 것을 보여주려고 합니다 . 보호 측면에서 거의 태만하고 심지어 볼 수도 있습니다. 의도적인 백도어 로 .

그리고 "매우 기본적인 문제"의 경우 "매우 나쁜 코딩"이어야 하며 이 "버그"는 신뢰할 수 있는 환경 내에서 절대로 보여서는 안 됩니다. 초보자 취약성과 함께 암호화 작동 방식에 대한 지식이 거의 완전히 부족함을 보여줍니다. 논문은 여기 [1]:

사실, WEP가 다시 시작되는 것과 같습니다. WEP Wifi 방법에는 작은 IV(초기화 벡터)가 있고, 출시되었을 때 암호화 스트림을 XOR하고 일반 텍스트를 발견하는 것이 가능했습니다. 잠들어 있는 프로그램은 하루도 안 되어 모든 시스코 액세스 포인트를 크래킹할 수 있습니다. 운 좋게도 우리는 이제 WPA-2를 사용하며 IV를 재사용하지 않습니다.

이와 같은 코드가 사용자의 장치에 접근하면 걱정해야 한다는 것을 보여주고 싶습니다. 사실 휴대폰에 백도어가 있다면 바로 이 백도어일 것이다.

"버그"에 대해 읽으려면 다음을 시도하십시오.

Samsung Galaxy 장치의 암호화 버그: 신뢰할 수 있는 실행 환경(TEE) 파괴

나쁜 "버그"

이제 이 "버그"가 얼마나 나쁜지 설명하겠습니다. 사이버 보안에 관심이 있다면 AES GCM이 스트림 암호임을 알아야 합니다. 이를 통해 비밀 키 값과 솔트 값(IV — 초기화 벡터)을 가져와 유사 무한 키스트림을 생성합니다. 그런 다음 일반 텍스트를 키 스트림과 XOR하여 암호문을 생성합니다.

고정된 솔트 값은 항상 동일한 일반 텍스트에 대해 동일한 키 스트림을 생성하고 암호화 스트림을 XOR하여 키 스트림을 표시하고 결국 일반 텍스트를 표시할 수 있으므로 소금 값은 항상 임의적이어야 합니다. 키 래핑의 경우 평문이 암호화 키이므로 TEE에서 사용하는 암호화 키가 노출됩니다.

IV를 재사용하면 Eve는 암호화 스트림을 함께 XOR하고 키 스트림(K)을 공개할 수 있습니다. 거기에서 그녀는 모든 암호 스트림을 해독할 수 있지만 단순히 암호 스트림을 K와 XOR-ing합니다.

코딩

AES GCM(Galois Counter Mode)은 AES용 스트림 암호 모드입니다. CTR 모드를 기반으로 하지만 스트림 암호로 변환됩니다. 이는 암호화/암호 해독 프로세스에서 대기 시간이 짧고 처리 속도가 빠릅니다. 이와 함께 인증을 위해 AEAD 모드를 통합합니다. 그러나 GCM은 스트림 암호 모드이므로 재사용 IV 공격에 노출되어 있습니다. 이것으로, 암호의 IV(초기화 벡터)는 두 개의 암호 메시지에 대해 동일합니다. 그런 다음 두 암호 스트림을 함께 XOR하여 암호 스트림 키( K )를 표시할 수 있습니다. 그런 다음 K 로 모든 암호 스트림을 XOR-ing하여 평문을 밝힐 수 있습니다 .

이를 위해 몇 가지 코드를 사용해 봅시다. 이 경우 Golang을 사용하여 방법의 기본 원칙을 보여 드리겠습니다. 이 경우 "0123456789ABCDEF"(16바이트 — 128비트 키)의 정적 키(TEE 내에서 변경되지 않으므로)와 "0123456789AB " (12바이트 — 96비트)의 정적 nonce를 사용합니다. ]:

package main
import (
 "crypto/aes"
 "crypto/cipher"
 "fmt"
 "os"
)
func xor(a, b []byte, length int) []byte {
 c := make([]byte, len(a))
 for i := 0; i < length; i++ {
  c[i] = a[i] ^ b[i]
 }
 return (c)
}
func main() {
 nonce := []byte("0123456789AB")
 key := []byte("0123456789ABCDEF")
 block, err := aes.NewCipher(key)
 if err != nil {
  panic(err.Error())
 }
 msg1 := "hello"
 msg2 := "Hello"
 argCount := len(os.Args[1:])
 if argCount > 0 {
  msg1 = (os.Args[1])
 }
 if argCount > 1 {
  msg2 = (os.Args[2])
 }
 plaintext1 := []byte(msg1)
 plaintext2 := []byte(msg2)
 aesgcm, err := cipher.NewGCM(block)
 if err != nil {
  panic(err.Error())
 }
 ciphertext1 := aesgcm.Seal(nil, nonce, plaintext1, nil)
 ciphertext2 := aesgcm.Seal(nil, nonce, plaintext2, nil)
 xor_length := len(ciphertext1)
 if len(ciphertext1) > len(ciphertext2) {
  xor_length = len(ciphertext2)
 }
 ciphertext_res := xor(ciphertext1, ciphertext2, xor_length)
 fmt.Printf("Message 1:\t%s\n", msg1)
 fmt.Printf("Message 2:\t%s\n", msg2)
 fmt.Printf("Cipher 1:\t%x\n", ciphertext1)
 fmt.Printf("Cipher 2:\t%x\n", ciphertext2)
 fmt.Printf("Key:\t\t%x\n", key)
 fmt.Printf("Nonce:\t\t%x\n", nonce)
 fmt.Printf("XOR:\t\t%x\n", ciphertext_res)
 plain1, _ := aesgcm.Open(nil, nonce, ciphertext1, nil)
 plain2, _ := aesgcm.Open(nil, nonce, ciphertext2, nil)
 fmt.Printf("Decrypted:\t%s\n", plain1)
 fmt.Printf("Decrypted:\t%s\n", plain2)
}

Message 1:	hello
Message 2:	Hello
Cipher 1:	7fcbe7378c2b87a5dfb2803d4fcaca8d5cde86dbfa
Cipher 2:	5fcbe7378cf8c68b82a2b8d705354e8d6c0502cef2
Key:		30313233343536373839414243444546
Nonce:		303132333435363738394142
XOR:		2000000000d3412e5d1038ea4aff840030db841508
Decrypted:	hello
Decrypted:	Hello

Message 1: hello
Message 2: Cello
Cipher 1: 7fcbe7378c2b87a5dfb2803d4fcaca8d5cde86dbfa
Cipher 2: 54cbe7378c5638db82df34a46172abed62b887aa48
Key:  30313233343536373839414243444546
Nonce:  303132333435363738394142
XOR:  2b000000007dbf7e5d6db4992eb861603e660171b2
Decrypted: hello
Decrypted: Cello

결론

이것은 매우 나쁜 코딩 이며 새로운 졸업생에게 이러한 수준의 구현을 기대하지 않습니다. TEE 내에서 코드를 생성하는 개발 팀이 재사용 IV 공격을 이해하지 못하는 경우 더 이상 신뢰할 수 있는 코드를 만지기 전에 안전한 코딩 교육 과정을 거쳐야 합니다. 이것이 의도적인 백도어라면 완전히 다른 이야기입니다. 그냥 버그였으면 좋겠지만 TEE 생성에 대한 지식을 개선해야 합니다. TEE도 클라우드 기반 시스템 내에서 실행되기 때문입니다.

참조

[1] Shakevsky, A., Ronen, E., & Wool, A. (2022). 신뢰는 어둠 속에서 죽습니다: 삼성의 TrustZone Keymaster 디자인에 빛을 비추다. 암호화 ePrint 아카이브 .