Nix Base32 인코딩¶
Nix는 RFC 4648과 다른 일반적인 base32 변형과도 다른 커스텀 base32 인코딩을 사용합니다.
알파벳¶
표준 (RFC 4648): A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 2 3 4 5 6 7
Nix: 0 1 2 3 4 5 6 7 8 9 a b c d f g h i j k l m n p q r s v w x y z
Nix 알파벳은 32자입니다: 숫자 0-9와 소문자, 단 e, o, t, u가 제거되었습니다.
왜 이 네 글자인가? 다른 문자와 혼동될 수 있기 때문입니다 (e/3, o/0, t/+). 정확한 이유는 역사적인 것입니다.
비트 추출¶
Nix base32가 RFC 4648과 가장 크게 다른 부분입니다. 비트 추출 순서가 반대입니다.
RFC 4648 방식¶
표준 base32는 입력을 왼쪽에서 오른쪽으로 처리하며, 최상위 비트부터 5비트 그룹을 가져갑니다:
Nix 방식¶
Nix는 마지막 5비트 위치에서 처음까지 역순으로 처리하며, 바이트 경계를 넘어 비트를 추출합니다:
for i in range(out_len - 1, -1, -1): # 높은 위치부터
b = i * 5
j = b // 8 # 바이트 인덱스
k = b % 8 # 바이트 내 비트 오프셋
c = (data[j] >> k) # 바이트 j의 비트
if j + 1 < len(data):
c |= data[j + 1] << (8 - k) # 바이트 j+1의 비트
output.append(CHARS[c & 0x1f])
이것의 의미:
- 첫 번째 출력 문자가 가장 높은 5비트 그룹을 인코딩
- 비트는 리틀엔디안 바이트 순서로 입력에서 추출
- 바이트 간 추출: 5비트 그룹이 인접한 두 입력 바이트에 걸칠 수 있음
결과¶
같은 입력 바이트에 대해 알파벳을 바꾸더라도 RFC 4648과 Nix base32는 완전히 다른 출력을 생성합니다. 인코딩이 구조적으로 다르며, 단순한 알파벳 치환이 아닙니다.
출력 길이¶
n 입력 바이트에 대해 출력은 ceil(n * 8 / 5) 문자입니다:
| 입력 바이트 | 출력 문자 | 용도 |
|---|---|---|
| 0 | 0 | — |
| 16 | 26 | MD5 (Nix에서 미사용) |
| 20 | 32 | 스토어 경로 해시 |
| 32 | 52 | SHA-256 해시 |
| 64 | 103 | SHA-512 해시 |
디코딩¶
디코딩은 과정을 역순으로 합니다: 입력 문자열을 역순으로 순회하며, 출력 바이트 배열의 적절한 위치에 5비트 그룹을 배치합니다.
for i, ch in enumerate(reversed(s)):
digit = ALPHABET.index(ch)
b = i * 5
j = b // 8
k = b % 8
result[j] |= (digit << k) & 0xFF
carry = digit >> (8 - k)
if carry and j + 1 < out_len:
result[j + 1] |= carry
비교 표¶
| 속성 | RFC 4648 | Nix |
|---|---|---|
| 알파벳 | A-Z2-7 |
0-9a-z에서 eotu 제거 |
| 패딩 | =로 8자 배수까지 패딩 |
패딩 없음 |
| 비트 순서 | MSB 우선, 왼쪽에서 오른쪽 | LSB 우선, 오른쪽에서 왼쪽 |
| 대소문자 | 대소문자 무관 | 소문자만 |
| 바이트 순서 | 빅엔디안 그룹 | 리틀엔디안 그룹 |
예제¶
"hello"의 SHA-256:
Hex: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
Nix base32: 094qif9n4cq4fdg459qzbhg1c6wywawwaaivx0k0x8xhbyx4vwic
같은 바이트를 RFC 4648 base32로 인코딩하면 완전히 다른 (그리고 패딩이 있는 더 긴) 문자열이 생성됩니다.