문제 상황
펌웨어 업데이트나 블루투스 통신을 통해 데이터를 안전하게 전송하기 위해 CRC32를 사용하여 데이터의 무결성을 검사하려고 합니다. CRC32은 어떤 것이며, 안드로이드 앱에서 이를 어떻게 사용할 수 있는지 정리해 보겠습니다.
왜 CRC32를 사용해야하는가?
CRC32는 데이터의 무결성을 검증하기 위한 체크섬 기법 중 하나입니다. 데이터가 전송 도중 변경되었는지 여부를 간단하게 확인할 수 있어, 데이터 손상을 감지하고 재전송 요구 없이 효율적으로 오류를 처리할 수 있습니다.
- 체크섬(Checksum)은 주어진 데이터의 일부를 특정 알고리즘을 통해 계산하여 생성됩니다. 이 값은 데이터 블록이나 파일의 모든 비트를 고려하여 생성되며, 일반적으로 고유한 데이터 시그니처 역할을 합니다. 데이터가 손상되었는지 여부를 빠르게 감지하거나, 데이터의 무결성을 확인하는 데 사용됩니다.
CRC32는 무엇인가(작동 원리)
- 다항식 선택: CRC32는 다항식(Polynomial)을 사용하여 체크섬을 계산합니다. 이 다항식은 CRC 알고리즘의 핵심 요소로, 여러 다항식 중 하나가 선택됩니다. 예를 들어, CRC32의 다항식은 다음과 같습니다:이 다항식은 CRC32의 체크섬 계산 과정에서 각 비트에 대한 계수로 사용됩니다.
- 초기값 설정: CRC32는 초기값으로 일반적으로 모든 비트가 1인 값이 사용됩니다. 이 초기값은 CRC 알고리즘의 시작점을 결정짓습니다.
- 데이터 처리: CRC32는 데이터 블록의 각 비트를 순차적으로 처리하여 체크섬 값을 생성합니다. 데이터 블록은 보통 바이트(Byte) 단위로 처리되며, 각 바이트의 모든 비트를 고려하여 체크섬 값을 업데이트합니다.
- 비트 연산: CRC32는 XOR(Xclusive OR) 연산과 시프트(Shift) 연산을 통해 체크섬 값을 계산합니다. 각 비트 처리 단계에서 다항식을 사용하여 데이터의 각 비트에 대한 계수를 반영하고, 이전 체크섬 값에 XOR 연산을 수행하여 새로운 체크섬 값을 생성합니다.
- 종료 처리: 데이터 블록의 모든 비트가 처리된 후에는 최종적으로 계산된 CRC32 값이 생성됩니다. 이 값은 데이터 블록의 내용을 나타내는 체크섬으로서, 데이터의 무결성을 나타냅니다.
CRC32를 안드로이드 앱에서 사용하는 방법
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import java.io.File
import java.io.FileInputStream
import java.util.zip.CRC32
import kotlin.experimental.and
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Surface {
CRC32FileVerifier()
}
}
}
}
}
@Composable
fun CRC32FileVerifier() {
var filePath by remember { mutableStateOf("") }
var crc32Result by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
OutlinedTextField(
value = filePath,
onValueChange = { filePath = it },
label = { Text("File Path") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = {
if (filePath.isNotBlank()) {
crc32Result = calculateFileCRC32(filePath)
} else {
crc32Result = ""
}
},
enabled = filePath.isNotBlank(),
) {
Text("Verify File Integrity")
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "CRC32: $crc32Result",
style = MaterialTheme.typography.body1
)
}
}
fun calculateFileCRC32(filePath: String): String {
val crc32 = CRC32()
val file = File(filePath)
val buffer = ByteArray(8192)
FileInputStream(file).use { fis ->
var bytesRead = fis.read(buffer)
while (bytesRead != -1) {
crc32.update(buffer, 0, bytesRead)
bytesRead = fis.read(buffer)
}
}
return crc32.value.toString(16).toUpperCase()
}
블루투스 통신 과정에서 받은 값과 제가 CRC32를 사용해서 얻은 값이 같다면 데이터가 무결하다는 증명이 됩니다.
예를 들어, "Hello"라는 문자열에 대한 CRC32 값은 다음과 같이 계산됩니다:
- 문자열 "Hello"의 각 바이트를 이진 데이터로 변환합니다.
- 각 바이트의 모든 비트를 CRC32 알고리즘에 따라 처리하여 최종적인 CRC32 값이 생성됩니다
참고 링크
https://developer.android.com/reference/kotlin/java/util/zip/CRC32?hl=en
https://stackoverflow.com/questions/20993557/wrong-crc32-hash-in-android
'질문 정리' 카테고리의 다른 글
소프트웨어 개발 구현 단계에서 검증해야하는 보안 점검 내용 (0) | 2024.07.08 |
---|---|
Secure SDLC이란 무엇일까? (0) | 2024.07.03 |
안드로이드 PDF 파일 저장하기 구현 (1) | 2024.06.28 |
컴포즈 블루투스 권한 없을 때 처리 (0) | 2024.06.27 |
Compose Snackbar 알림 관리를 위한 Utils (0) | 2024.06.20 |