순환 중복 검사
1. 개요
순환 중복 검사(CRC)는 데이터 전송 또는 저장 과정에서 발생하는 오류를 감지하기 위한 기술로, 1961년 W. 웨슬리 피터슨에 의해 처음 제안되었다. CRC는 생성 다항식을 사용하여 입력 데이터의 나머지를 계산하며, 이 나머지를 검사 값으로 사용한다. CRC는 구현이 간단하고 버스트 오류 감지에 효과적이지만, 데이터의 의도적인 변경으로부터 보호하는 데는 적합하지 않다. 다양한 CRC 표준이 존재하며, 응용 분야에 따라 적합한 다항식을 선택해야 한다. CRC는 하드웨어 및 소프트웨어로 구현될 수 있으며, 통신 시스템, 저장 장치 등 다양한 분야에서 활용된다.
이미지 준비중입니다.
| 종류 | 오류 검출 코드 |
|---|---|
| 개발 | W. 웨슬리 피터슨 |
| 첫 발표 | 1961년 |
| 약칭 | CRC |
|---|---|
| 로마자 표기 | Sunhwan Jungbok Geomsa |
| 사용 목적 | 데이터 전송 중 오류 검출 |
| 원리 | 다항식 나눗셈의 나머지 계산 송신 측: 데이터를 다항식으로 간주, 미리 정해진 생성 다항식으로 나눈 나머지를 덧붙여 전송 수신 측: 받은 데이터를 동일한 생성 다항식으로 나누어 나머지가 0인지 확인 |
| 장점 | 구현 용이 비교적 짧은 계산 시간 강력한 오류 검출 능력 |
| 단점 | 오류 정정 능력은 없음 |
| 데이터 저장 장치 | 하드 디스크 드라이브 SSD (Solid State Drive) |
|---|---|
| 통신 프로토콜 | 이더넷 USB ZIP |
| 파일 형식 | PNG GZIP |
| CRC-4 | ITU-T 권고 O.150 |
|---|---|
| CRC-5 | ITU-T 권고 G.704 |
| CRC-6 | ITU-T 권고 G.704 |
| CRC-7 | SD 카드 ITU-T 권고 G.707 |
| CRC-8 | I²C 버스 CAN (Controller Area Network) 1-Wire |
| CRC-10 | ATM (Asynchronous Transfer Mode) |
| CRC-11 | DARC (Data Radio Channel) |
| CRC-12 | UMTS (Universal Mobile Telecommunications System) |
| CRC-14 | AUTODIN-II |
| CRC-15 | MIL-STD-1553 |
| CRC-16 | X.25 Modbus Bluetooth ZIP |
| CRC-24 | FlexRay |
| CRC-32 | 이더넷 ZIP PNG MPEG |
-
체크섬 알고리즘 -
MD5
MD5는 로널드 리베스트 교수가 개발한 128비트 해시 값 생성 암호화 해시 함수이나, 보안 취약점으로 인해 현재는 보안이 중요한 분야에서는 사용이 중단되었다. -
체크섬 알고리즘 -
범용 상품 부호
범용 상품 부호(UPC)는 소매점에서 상품을 식별하기 위해 상품 포장에 인쇄되는 널리 사용되는 바코드의 일종으로, 12자리 숫자로 구성된 UPC-A를 포함한 다양한 변형이 존재한다. -
유한체 -
이산 로그
-
유한체 -
타원곡선 암호
타원 곡선 암호는 타원 곡선 이론에 기반한 공개 키 암호 방식으로, 작은 키 크기로 높은 보안성을 제공하며 타원 곡선 이산 로그 문제의 어려움을 이용해 ECDH, ECDSA 등 다양한 암호 프로토콜에 활용되어 널리 사용된다. -
디지털 전자공학 -
트랜지스터-트랜지스터 논리
트랜지스터-트랜지스터 논리(TTL)는 1961년 제임스 L. 부이에 의해 발명된 바이폴라 접합 트랜지스터 기반의 디지털 회로 기술로, 텍사스 인스트루먼츠의 7400 시리즈를 통해 널리 사용되었으며, 저렴한 비용으로 디지털 기술 발전에 기여했다. -
디지털 전자공학 -
플립플롭
플립플롭은 1비트 이상의 정보를 저장하는 디지털 논리 회로로, 에클스-조던 트리거 회로에서 기원하여 SR, D, T, JK 등 다양한 유형으로 구현되며, 컴퓨터 기억 장치의 기본 구성 요소로 사용되지만 타이밍 요소에 민감하게 설계해야 한다.
2. 역사
W. 웨슬리 피터슨은 1961년에 오류 감지를 위해 고정 길이 검사 값을 추가하여 메시지를 인코딩하는 체계적 순환 부호 사용을 처음으로 제안했다. 순환 부호는 구현이 간단하고, 버스트 오류를 감지하는 데 특히 적합하다. 이는 자기 및 광학 저장 장치를 포함한 많은 통신 채널에서 버스트 오류가 흔한 전송 오류이기 때문에 중요하다.
가장 간단한 오류 감지 시스템인 패리티 비트는 실제로 1비트 CRC이다. 즉, 생성 다항식(두 항)을 사용하며, CRC-1이라는 이름을 갖는다.
3. CRC의 원리
가환환의 나눗셈(Modulo-2 덧셈, 즉 XOR)에 기반한 CRC는, 법 2 정수에서 정의된 다항식 환을 사용한다. 이는 한 비트의 계수를 갖는 다항식 집합으로, 사칙연산은 계수들을 가장 아래 비트만 취하도록 정의된다. 예를 들어:
:
여기서 2는 이진수로 10이므로, 가장 아랫자리 수인 0을 취하고 그 이상은 버린다. 곱셈의 예는 다음과 같다:
:
나눗셈도 정의할 수 있다. 예를 들어, x3 + x2 + x를 x + 1 로 나누면:
:
나눗셈의 몫은 x2 + 1 이고 나머지는 -1 이고, -1은 홀수이기 때문에 1이 된다.
모든 데이터 비트 스트림은 이러한 다항식의 계수로 생각할 수 있다. 101은 에 해당한다. CRC 값은, 정해진 다항식으로 데이터 스트링의 다항식을 나누어 그 나머지를 나타내는 비트 스트링이다. 이 나머지를 구하는 빠른 알고리즘이 알려져 있다. CRC는 체크섬으로 불리기도 하지만, 엄밀히는 옳지 않은 이름이다.
CRC 알고리즘은 의사코드로 다음과 같이 표현할 수 있다:
function crc(bit array bitString[1..len], int polynomial) {
shiftRegister := initial value // 보통 00000000 또는 11111111
for i from 1 to len {
if (shiftRegister의 최상위 비트) xor bitString[i] = 1
shiftRegister := (shiftRegister left shift 1) xor polynomial
else
shiftRegister := shiftRegister left shift 1
}
return shiftRegister
}
실제로는 여러 최상위 비트를 처리하는 표를 만들어 속도를 높이며, 8비트 단위 처리가 많이 사용된다.
위 구현은 두 가지로 고칠 수 있으며, CRC 계산에는 네 가지 방법이 존재한다:
# shiftRegister를 비트 단위로 뒤집고, 최하위 비트를 테스트한 뒤 오른쪽으로 1비트 쉬프트한다. polynomial 값을 비트 단위로 뒤집고, 결과도 뒤집는다.
# shiftRegister와 bitString에서 polynomial에 설정된 비트들을 xor한 1비트 결과를 shiftRegister에 더한다. 하드웨어 구현에서 종종 사용되며, 선형 되먹임 시프트 레지스터 설명에 자주 사용된다.
특정 CRC는 사용되는 다항식으로 정의한다. n비트 CRC는 꼴의 n차 다항식이 필요하다. 이 다항식은 (n+1)비트 문자열로 나타낼 수 있지만, 차수가 가장 높은 xn 항의 계수는 항상 1이므로 n비트 문자열로 나타낸다. 예를 들어 CRC-16 표준 다항식 은 0x8005나 0xa001로, 이더넷, FDDI, PKZIP, WinZip, PNG 등에서 널리 사용되는 CRC-32는 0x04C11DB7 또는 0xEDB88320으로 쓸 수 있다.
다항식 표현은 일반적으로 다음과 같다.
다항식 예:
:
이 다항식은 3가지 방법으로 표현할 수 있다:
* 0x3 = 0b0011 : (MSB-우선 코드)
* 0xC = 0b1100 : (LSB-우선 코드)
* 0x9 = 0b1001 : (Koopman 표시)
표로 나타내면:
| 다항식(representations) | ||
|---|---|---|
| 정상(normal) | 역방향(reversed) | 역방향의 역수(reversed reciprocal) |
| 0x3 | 0xC | 0x9 |
n-bit 이진 CRC 계산을 위해 다항식(polynomial) (n+1)-비트 패턴의 제수(divisor)를 만든다.
| 다항식 | 제수 | 비트수 | CRC |
|---|---|---|---|
| 4비트 | 3비트 |
다항식의 각 자릿수 별로 표현하면:
:
3비트 CRC를 계산하는 예시는 다음과 같다.
메시지 데이터는:
11010011101100
나누면 :
1
---------------------
1011 ) 11010011101100 000 <--- 오른쪽으로 3비트 후부터
1011 <--- 제수(divisor) 4비트 <= x³+x+1
------------------
01100011101100 000 <--- 결과
각 비트별로 XOR 연산을 한다. 일반적인 나눗셈과 다르다. 첫 번째 비트가 0이 되도록 몫의 비트를 정한다.
:1101 XOR 1011 => 0 110
전체 계산:
11110001111 100 <--- 몫
-------------------
11010011101100 000 <--- 오른쪽으로 3비트 후부터
1011 <--- 제수
01100011101100 000 <--- 결과
1011 <--- 제수 ...
00111011101100 000
1011
00010111101100 000
1011
00000001101100 000
0000
00000001101100 000
0000
00000001101100 000
0000
00000001101100 000
1011
00000000110100 000
1011
00000000011000 000
1011
00000000001110 000
1011
00000000000101 000
101 1
00000000000000 100
00 00
00000000000000 100
0 000
-----------------
00000000000000 100 <--- 나머지 3비트
최대 입력 비트수 만큼 나누면 모두 0이 된다.
검증은 입력 메시지에 CRC 결과를 붙여 나누면 나머지가 0이 되는 것으로 확인 가능하다.:
11010011101100 100 <--- 입력과 CRC 체크값을 붙이고
1011 <--- 나누고
01100011101100 100 <--- 결과
1011 <--- 나누고 ...
00111011101100 100
......
00000000001110 100
1011
00000000000101 100
101 1
------------------
0 <--- 나머지
4. CRC의 종류 및 응용
CRC는 다양한 표준과 응용 분야에서 사용되며, 각각 다른 특징과 다항식 표현 방법을 가진다. CRC는 기본적으로 다항식 나눗셈에 기반하며, 데이터 비트 스트림을 특정 다항식으로 나누어 나머지를 검사 값으로 사용한다.
CRC 다항식은 16진수로 표현될 수 있으며, 비트 순서에 따라 표현이 달라진다. 예를 들어, CRC-16 표준 다항식 은 0x8005 또는 0xA001로 표현될 수 있다. 널리 사용되는 CRC-32는 0x04C11DB7 또는 0xEDB88320으로 표현된다.
다항식의 표현은 일반적으로 다음과 같이 3가지 방법으로 표현할 수 있다.
* 정상(normal): MSB 우선 코드 (예: 0x3)
* 역방향(reversed): LSB 우선 코드 (예: 0xC)
* 역방향의 역수(reversed reciprocal): Koopman 표시 (예: 0x9)
| 다항식 표현 | ||
|---|---|---|
| 정상(normal) | 역방향(reversed) | 역방향의 역수(reversed reciprocal) |
| 0x3 | 0xC | 0x9 |
CRC의 종류는 매우 다양하며, 사용되는 다항식에 따라 그 특성이 달라진다. 다음은 몇 가지 주요 CRC 종류와 그 사용 예시이다.
| 이름 | 사용처 | 다항식 (정상) | 다항식 (역방향) | 다항식 (역방향의 역수) |
|---|---|---|---|---|
| CRC-1 | 주로 하드웨어, 패리티 비트 | 0x1 | 0x1 | 0x1 |
| CRC-4-ITU | G.704 | 0x3 | 0xC | 0x9 |
| CRC-5-EPC | Gen 2 RFID | 0x09 | 0x12 | 0x14 |
| CRC-5-ITU | G.704 | 0x15 | 0x15 | 0x1A |
| CRC-5-USB | USB 토큰 패킷 | 0x05 | 0x14 | 0x12 |
| CRC-6-ITU | G.704 | 0x03 | 0x30 | 0x21 |
| CRC-7 | 통신 체계, G.707, G.832, MMC, SD 카드 | 0x09 | 0x48 | 0x44 |
| CRC-8-CCITT | I.432.1; ATM HEC, ISDN HEC | 0x07 | 0xE0 | 0x83 |
| CRC-8-Dallas/Maxim | 1-Wire 버스 | 0x31 | 0x8C | 0x98 |
| CRC-16-IBM | Bisync, Modbus, USB, ANSI X3.28, SIA DC-07 등 | 0x8005 | 0xA001 | 0xC002 |
| CRC-16-CCITT | X.25, V.41, HDLC FCS, XMODEM, 블루투스, SD 카드 등 | 0x1021 | 0x8408 | 0x8810 |
| CRC-32 | HDLC, 이더넷, SATA, MPEG-2, PKZIP, Gzip, Bzip2, PNG 등 | 0x04C11DB7 | 0xEDB88320 | 0x82608EDB |
| CRC-32C (Castagnoli) | iSCSI, SCTP, G.hn 페이로드, SSE4.2, Btrfs, ext4 | 0x1EDC6F41 | 0x82F63B78 | 0x8F6E37A0 |
이 외에도 다양한 CRC 종류가 존재하며, 각 CRC는 특정 응용 분야에 맞게 최적화되어 있다.
5. CRC의 수학적 분석
CRC는 순환 오류 정정 부호의 이론에 기반한다. CRC의 수학적 분석은 나눗셈과 유사한 과정에 대한 분석을 통해 좋은 오류 감지 속성을 보장하는 제수를 선택하는 방법을 제공한다. 이 분석에서 비트열의 숫자는 일부 변수 x의 다항식의 계수로 간주되며, 이 계수는 GF(2) (정수 모듈로 2, 즉 0 또는 1)의 요소이다. 이러한 이진 다항식 집합은 수학적 환을 이룬다.
생성 다항식의 선택은 CRC 알고리즘을 구현하는 데 가장 중요한 부분이다. 이 다항식은 오류 감지 능력을 극대화하는 동시에 전체 충돌 확률을 최소화하도록 선택해야 한다. 다항식의 가장 중요한 속성은 길이(다항식의 모든 항 중 가장 큰 차수(지수) +1)로, 계산된 검사 값의 길이에 직접적인 영향을 미친다.
가장 일반적으로 사용되는 다항식 길이는 다음과 같다.
* 9비트 (CRC-8)
* 17비트 (CRC-16)
* 33비트 (CRC-32)
* 65비트 (CRC-64)
CRC는 검사 값이 n비트일 때 n비트 CRC라고 한다. 주어진 n에 대해, 각각 다른 다항식을 가진 여러 CRC가 가능하다. 이러한 다항식은 최고 차수가 n이므로 n+1 개의 항을 갖는다 (다항식의 길이는 n+1 이다). 나머지는 길이가 n이다. CRC는 CRC-n-XXX 형태의 이름을 갖는다.
CRC 다항식의 설계는 보호할 블록의 최대 총 길이(데이터 + CRC 비트), 원하는 오류 보호 기능, CRC를 구현하기 위한 리소스 유형, 원하는 성능에 따라 달라진다. 일반적인 오해는 "최상의" CRC 다항식이 기약 다항식이거나 기약 다항식에 인수를 곱한 형태에서 파생된다는 것이다. 이는 코드에 홀수 비트에 영향을 미치는 모든 오류를 감지하는 기능을 추가한다. 실제로는 위에 설명된 모든 요인이 다항식 선택에 영향을 미쳐 약분 가능한 다항식을 초래할 수 있다. 그러나 약분 가능한 다항식을 선택하면 몫 환이 영인자를 가지므로 특정 비율의 오류가 누락될 수 있다.
원시 다항식을 CRC 코드의 생성기로 선택하는 것의 장점은 결과 코드가 최대 총 블록 길이를 갖는다는 것이다. 즉, 해당 블록 길이 내의 모든 1비트 오류가 서로 다른 나머지(증후군이라고도 함)를 가지며, 나머지가 블록의 선형 함수이므로 코드는 해당 블록 길이 내의 모든 2비트 오류를 감지할 수 있다.
6. CRC의 구현
CRC는 가환환의 나눗셈(Modulo-2 덧셈, 즉 XOR)에 기반한다. 이는 한 비트의 계수를 갖는 다항식 집합 간의 연산으로, 가장 아래 비트만 결과로 취한다.
* 덧셈 예시:
(2는 이진수 10이므로, 최하위 비트 0만 남김)
* 곱셈 예시:
나눗셈도 정의 가능하다. 예를 들어, x3 + x2 + x를 x + 1로 나누면 몫은 x2 + 1이고 나머지는 1이다.
데이터 비트 스트림은 이러한 다항식의 계수로 표현될 수 있다. 예를 들어, 101은 에 해당한다. CRC 값은 데이터 스트링 다항식을 특정 다항식으로 나눈 나머지를 나타내는 특정 길이의 비트 스트링이다.
### 하드웨어 구현
CRC는 통신 시스템 등에서 널리 사용되므로 하드웨어로 구현하는 경우가 많다.
* 직렬 논리 회로:
* 예시: 다항식에 대한 CRC 직렬 논리 회로는 다음과 같이 표현할 수 있다.
--
* 직렬 비트 데이터가 입력될 때마다 논리 회로를 통해 CRC 값이 계산된다.
* D-FF(D 플립플롭)을 사용한 논리 회로는 다음과 같다.
* 입력은 한 클럭 동안 하나의 비트를 받고, 출력은 O2 O1 O0의 3비트이다. 직렬 입력에 대해 매 클럭마다 CRC 값이 출력된다.
* 병렬 논리 회로:
* 디지털 통신에서는 직렬 비트 데이터 형태가 주로 사용되지만, 병렬로 여러 비트를 한 번에 계산해야 할 경우도 있다. 이 경우 직렬 회로를 층층이 쌓아 병렬 회로를 구성할 수 있다.
* 예시: 4비트(b3:b2:b1:b0)를 동시에 계산하는 회로는 다음과 같다.
* 입력 b[3:0]에 대해 계산된 CRC 출력은 C[2:0]이다. (b0가 가장 먼저 입력되고, b1, b2, b3 순서로 입력)
* 고속 클럭 회로에서는 XOR 연산의 전파 지연 시간을 고려하여 설계해야 한다.
* 병렬 처리 예시 (4비트):
* 메시지 입력: 1101 0011 1011 0000
* 입력 순서: 1101 = b0 b1 b2 b3 = b[0:3]
| 클럭 | 입력 (b[0:3]) | 이전 CRC | XOR 결과 | CRC 결과 (C[2:0]) |
|---|---|---|---|---|
| 1 | 1101 | 000 | 1101 000 | 001 |
| 2 | 0011 | 001 | 0001 000 | 011 |
| 3 | 1011 | 011 | 1011 000 | 001 |
| 4 | 0000 | 001 | 0000 000 | 110 |
* 최종 CRC 결과: C[2:0] = 110 (4클럭 소요)
### 소프트웨어 구현
CRC는 바이트(Octet) 단위로 구현하는 것이 일반적이다. 통신 시스템에서 옥텟 단위가 기본이기 때문이다. 비트 단위 계산은 속도 문제로 인해 CRC 테이블 기법을 많이 사용한다.
소프트웨어 접근법 (CRC 테이블 기법):
1. CRC 테이블 생성: 모든 옥텟(Octet) 입력에 대해 미리 CRC 값을 계산하여 테이블(변수)로 만든다.
2. CRC 계산: 데이터가 들어오면 옥텟 단위로 테이블을 참조하여 CRC 값을 결정한다.
[[C (프로그래밍 언어)|C]] 코드 예시 (3비트 CRC):
* CRC 테이블 생성 코드:
```c
static unsigned char crctable[256];
void make_crc_table( void ) {
int cnt, bcnt;
unsigned short poly, c;
// 다항식 정의 (x^3 + x + 1):
static const char p[] = {0,1};
// 다항식으로부터 XOR 패턴 생성
poly = 0;
for ( cnt = 0; cnt < sizeof(p)/sizeof(p[0]); cnt++ ) {
poly |= 1 << p[cnt];
}
poly <<= 5;
for ( cnt = 0; cnt < 256; cnt++ ) {
c = cnt;
for ( bcnt = 0; bcnt < 8; bcnt++ ) {
c = ( c & 0x80 ) ? poly ^ ( c << 1 ) : ( c << 1 );
}
crctable[cnt] = (unsigned char) (c & 0xE0); // 상위 3비트만 사용
}
}
```
* CRC 테이블 (crc3table.c):
```c
const unsigned char crctable[256] = {
0x00, 0x60, 0xC0, 0xA0, 0xE0, 0x80, 0x20, 0x40,
0xA0, 0xC0, 0x60, 0x00, 0x40, 0x20, 0x80, 0xE0,
0x20, 0x40, 0xE0, 0x80, 0xC0, 0xA0, 0x00, 0x60,
0x80, 0xE0, 0x40, 0x20, 0x60, 0x00, 0xA0, 0xC0,
0x40, 0x20, 0x80, 0xE0, 0xA0, 0xC0, 0x60, 0x00,
0xE0, 0x80, 0x20, 0x40, 0x00, 0x60, 0xC0, 0xA0,
0x60, 0x00, 0xA0, 0xC0, 0x80, 0xE0, 0x40, 0x20,
0xC0, 0xA0, 0x00, 0x60, 0x20, 0x40, 0xE0, 0x80,
0x80, 0xE0, 0x40, 0x20, 0x60, 0x00, 0xA0, 0xC0,
0x20, 0x40, 0xE0, 0x80, 0xC0, 0xA0, 0x00, 0x60,
0xA0, 0xC0, 0x60, 0x00, 0x40, 0x20, 0x80, 0xE0,
0x00, 0x60, 0xC0, 0xA0, 0xE0, 0x80, 0x20, 0x40,
0xC0, 0xA0, 0x00, 0x60, 0x20, 0x40, 0xE0, 0x80,
0x60, 0x00, 0xA0, 0xC0, 0x80, 0xE0, 0x40, 0x20,
0xE0, 0x80, 0x20, 0x40, 0x00, 0x60, 0xC0, 0xA0,
0x40, 0x20, 0x80, 0xE0, 0xA0, 0xC0, 0x60, 0x00,
0x60, 0x00, 0xA0, 0xC0, 0x80, 0xE0, 0x40, 0x20,
0xC0, 0xA0, 0x00, 0x60, 0x20, 0x40, 0xE0, 0x80,
0x40, 0x20, 0x80, 0xE0, 0xA0, 0xC0, 0x60, 0x00,
0xE0, 0x80, 0x20, 0x40, 0x00, 0x60, 0xC0, 0xA0,
0x20, 0x40, 0xE0, 0x80, 0xC0, 0xA0, 0x00, 0x60,
0x80, 0xE0, 0x40, 0x20, 0x60, 0x00, 0xA0, 0xC0,
0x00, 0x60, 0xC0, 0xA0, 0xE0, 0x80, 0x20, 0x40,
0xA0, 0xC0, 0x60, 0x00, 0x40, 0x20, 0x80, 0xE0,
0xE0, 0x80, 0x20, 0x40, 0x00, 0x60, 0xC0, 0xA0,
0x40, 0x20, 0x80, 0xE0, 0xA0, 0xC0, 0x60, 0x00,
0xC0, 0xA0, 0x00, 0x60, 0x20, 0x40, 0xE0, 0x80,
0x60, 0x00, 0xA0, 0xC0, 0x80, 0xE0, 0x40, 0x20,
0xA0, 0xC0, 0x60, 0x00, 0x40, 0x20, 0x80, 0xE0,
0x00, 0x60, 0xC0, 0xA0, 0xE0, 0x80, 0x20, 0x40,
0x80, 0xE0, 0x40, 0x20, 0x60, 0x00, 0xA0, 0xC0,
0x20, 0x40, 0xE0, 0x80, 0xC0, 0xA0, 0x00, 0x60
};
```
* CRC 계산 함수 (crc3.c):
```c
#include "crc3.h"
#include "crc3table.c" // CRC 테이블 코드 포함
unsigned char crc3_CalcChecksum(const unsigned char icrc, const void *data, int length )
{
unsigned char crc;
const unsigned char *buf = (const unsigned char *) data;
crc = icrc;
while (length--) {
crc = crctable[crc ^ *buf++]; // 테이블 참조하여 XOR 연산
}
return crc;
}
```
* main 함수 예시 (testcrc3main.c):
```c
#include
#include "crc3.h"
int main(int argc, char* argv[])
{
int cnt;
unsigned char crc;
unsigned char data[8] = { 0xD3, 0xB0 };
#define SZ_MSGDATA 2
crc = crc3_CalcChecksum(0x00, (void *)data, SZ_MSGDATA); // 초기 CRC 값은 0x00
printf("Input Data : ");
for (cnt = 0;cnt < SZ_MSGDATA;cnt++)
printf("0x%02X ", data[cnt] );
printf(" : CRC = 0x%02X\n", crc);
int sz = SZ_MSGDATA;
data[sz++] = 0xCB;
data[sz++] = 0x0D;
crc = crc3_CalcChecksum(crc, &data[SZ_MSGDATA], sz-SZ_MSGDATA);
printf("Input Data : ");
for (cnt = 0;cnt < sz;cnt++)
printf("0x%02X ", data[cnt] );
printf(" : CRC = 0x%02X\n", crc);
crc = crc3_CalcChecksum(0x00, (void *)data, sz); // 전체 데이터에 대해 다시 계산
printf("다시 계산 : ");
for (cnt = 0;cnt < sz;cnt++)
printf("0x%02X ", data[cnt] );
printf(" : CRC = 0x%02X\n", crc);
return 0;
}
```
* 실행 결과:
```
Input Data : 0xD3 0xB0 : CRC = 0xC0
Input Data : 0xD3 0xB0 0xCB 0x0D : CRC = 0x20
다시 계산 : 0xD3 0xB0 0xCB 0x0D : CRC = 0x20
```
이 예시에서는 2바이트 데이터(0xD3, 0xB0)에 대한 3비트 CRC 값이 0xC0으로 계산되었다.
7. CRC의 한계와 보안
CRC는 통신 채널에서 발생하는 일반적인 오류 유형으로부터 데이터를 보호하고, 전송된 메시지의 무결성을 빠르고 합리적으로 보장하도록 설계되었다. 그러나 의도적인 데이터 변경을 막는 데는 적합하지 않다.
첫째, 인증이 없기 때문에 공격자는 메시지를 편집하고 CRC를 다시 계산하여 변경 사항이 감지되지 않도록 할 수 있다. CRC와 암호화 해시 함수는 데이터와 함께 저장될 때 그 자체로는 데이터의 의도적인 수정으로부터 보호하지 못한다. 이러한 공격을 막으려면 메시지 인증 코드 또는 디지털 서명(일반적으로 암호화 해시 함수를 기반으로 함)과 같은 암호화 인증 메커니즘을 사용해야 한다.
둘째, 암호화 해시 함수와 달리 CRC는 쉽게 되돌릴 수 있는 함수이므로 디지털 서명에 사용하기에 적합하지 않다.
셋째, CRC는 선형 함수와 유사한 관계(또는 좀 더 정확하게는 아핀 함수)를 만족한다.
:
여기서 는 와 의 길이에 따라 달라진다. 이는 , 및 의 길이가 같은 경우 다음과 같이 나타낼 수도 있다.
:
결과적으로 CRC가 XOR을 결합 연산으로 사용하는 스트림 암호 또는 OFB나 CFB와 같이 이를 효과적으로 스트림 암호로 바꾸는 블록 암호의 운용 모드로 암호화된 경우에도, 암호화 키를 알지 못해도 메시지와 관련 CRC를 모두 조작할 수 있다. 이는 WEP 프로토콜의 잘 알려진 설계 결함 중 하나였다.
CRC 값은 메시지와 1:1 대응이 불가능하다는 점에서(메시지보다 항상 정보량이 적으므로) 암호학적 해시 함수에 의한 해시 값과 같지만, 해시 값은 보통 100비트 이상 크기를 가지며, 내용이 다른데 같은 해시 값을 가지는 메시지를 위조하는 것은 쉽지 않다. 반면 CRC 값은 일반적으로 작고, 오류 정정 기법으로 같은 CRC 값이 되는 메시지를 쉽게 생성할 수 있으며, 원래 메시지를 약간만 변경해도 CRC 값을 같게 할 수 있다.
통신 시스템 전체의 보안을 고려할 때, 전송 도중에 도청하여 메시지를 위조품으로 바꾸려면, 동시에 CRC도 바꿔야 한다. 따라서 CRC는 제3자에 의한 의도적인 변조 등을 막는 직접적인 수단이 되지 않는다. 더욱이 CRC는 분배 법칙·결합 법칙이 성립하므로, 메시지 인증 코드의 HMAC와 같은 "비밀의 문자열"을 전치하거나 후치해도 변조에 대한 내성은 전혀 올라가지 않는다.