개발 이야기3분 읽기

이름은 같은데 값이 다른 것들

SHA3-256과 Keccak-256은 다른 값을 냅니다. 이름만으로는 특정되지 않는 여덟 가지.

문서에 "SHA-3을 쓰라"고 적혀 있어서 SHA3-256으로 계산했는데 상대가 거부합니다.

SHA3-256과 Keccak-256은 다르다

SHA3-256("abc")   = 3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532
Keccak-256("abc") = 4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45

완전히 다른 값입니다.

Keccak이 SHA-3 공모전에서 우승한 뒤, NIST가 표준으로 확정하면서 패딩 바이트를 바꿨습니다. 원래 Keccak은 0x01을 붙였고 SHA-3은 0x06을 붙입니다. 그 한 바이트 차이가 전체 출력을 바꿉니다.

이더리움은 표준 확정 전에 원래 Keccak을 채택했고 지금도 그것을 씁니다. 그래서 이더리움 주소나 서명을 SHA3-256으로 계산하면 전부 틀립니다.

라이브러리 이름도 헷갈립니다. 일부 라이브러리의 sha3 함수가 실제로는 Keccak이고, 반대인 경우도 있습니다. 알려진 값("abc")으로 확인하는 것이 확실합니다.

CRC-16은 일곱 가지가 넘는다

이름                  다항식   초기값   반전   "123456789" 결과
CRC-16/ARC            0x8005  0x0000  예     0xBB3D
CRC-16/MODBUS         0x8005  0xFFFF  예     0x4B37
CRC-16/CCITT-FALSE    0x1021  0xFFFF  아니오  0x29B1
CRC-16/XMODEM         0x1021  0x0000  아니오  0x31C3
CRC-16/KERMIT         0x1021  0x0000  예     0x2189

같은 다항식 0x1021을 쓰는 셋이 다 다릅니다. 초기값과 반전 여부가 값을 바꿉니다.

"CCITT"라고만 적혀 있으면 CCITT-FALSE와 KERMIT 중 어느 것인지 알 수 없습니다.

base64는 네 가지다

문자셋   62·63번 문자   패딩
표준     +  /          = 있음
표준     +  /          = 없음
URL-safe -  _          = 있음
URL-safe -  _          = 없음

JWT는 URL-safe에 패딩 없음입니다. HTTP 기본 인증은 표준에 패딩 있음입니다. 둘을 섞으면 디코딩이 실패하거나, 더 나쁘게는 조용히 다른 바이트가 나옵니다.

+/가 보이면 표준, -_가 보이면 URL-safe입니다. 둘 다 없으면 구분할 수 없으므로 문맥으로 정해야 합니다.

시간대 약어

CST   미국 중부  -06:00
CST   중국       +08:00
CST   쿠바       -05:00
IST   인도       +05:30
IST   아일랜드   +01:00
IST   이스라엘   +02:00

약어는 유일하지 않습니다. Asia/Seoul 같은 IANA 지역 이름을 써야 특정됩니다.

단위

1 갤런    미국 3.785L / 영국 4.546L          20% 차이
1 온스    무게 28.35g / 액량(미) 29.57mL     아예 다른 종류
1 톤      미터톤 1000kg / 숏톤 907kg / 롱톤 1016kg
1 kB      SI 1000바이트 / 관행 1024바이트

무게 온스와 액량 온스는 하나가 질량이고 하나가 부피라, 이름만 같고 차원이 다릅니다.

MD5는 두 가지 문맥이다

MD5           128비트 해시 함수
md5crypt      $1$ 로 시작하는 비밀번호 해시, MD5 를 1000번 돌린다

"MD5로 비밀번호를 저장한다"는 말이 둘 중 어느 쪽인지에 따라 안전성 평가가 달라집니다. 전자는 솔트도 반복도 없고, 후자는 둘 다 있습니다. 둘 다 지금 고를 것은 아니지만 같은 것은 아닙니다.

무엇으로 구분하나

규격이 정한 검사값을 씁니다. CRC 규격은 "123456789"의 값을 함께 적어 둡니다. 해시는 "abc"나 빈 문자열의 값이 널리 공개돼 있습니다. 새 구현을 붙일 때 이것부터 넣어 보면 어느 변종인지 바로 나옵니다.

이름 대신 파라미터를 받습니다. 문서에 "CRC-16"이 아니라 다항식·초기값·반전·최종 XOR이 적혀 있어야 합니다. 시간대는 약어가 아니라 IANA 이름이어야 합니다.

확인 순서

  1. 해시가 안 맞으면 알려진 검사값("abc")으로 어느 변종인지 확인합니다
  2. 이더리움 관련 코드에서 sha3 함수가 실제로 Keccak인지 봅니다
  3. base64 디코딩이 실패하면 문자셋과 패딩을 봅니다
  4. 시간대를 약어로 주고받고 있다면 IANA 이름으로 바꿉니다
  5. 외부 장비·API 문서에서 이름만 적혀 있으면 파라미터를 요청합니다

tools.onuel.dev에서 변종을 나란히 확인할 수 있습니다. SHA3-256Keccak-256abc를 넣으면 위의 두 값이 그대로 나오고, CRC 계산기는 다항식과 초기값을 직접 지정해 변종을 재현합니다. base64 문자셋 차이는 Base64 인코딩에서, 시간대는 시간대 변환기에서 지역 이름으로 다루고, 단위 계통은 단위 변환기에 함께 있습니다.

  • #해시
  • #규격
  • #호환성