콘텐츠로 이동

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비트 그룹을 가져갑니다:

입력 바이트:  [b0] [b1] [b2] ...
비트:        76543210 76543210 76543210 ...
그룹:        |4444433333|22222|11111|00000|...

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로 인코딩하면 완전히 다른 (그리고 패딩이 있는 더 긴) 문자열이 생성됩니다.