Skip to content

fix: case-insensitive decode for base16/base32/base36 (multibase spec)#341

Merged
rvagg merged 1 commit into
multiformats:masterfrom
spokodev:fix/case-insensitive-decode
Jun 24, 2026
Merged

fix: case-insensitive decode for base16/base32/base36 (multibase spec)#341
rvagg merged 1 commit into
multiformats:masterfrom
spokodev:fix/case-insensitive-decode

Conversation

@spokodev

Copy link
Copy Markdown
Contributor

Problem

The multibase registry marks several codecs as case-insensitive: base16, base16upper, base32, base32upper, base32pad, base32padupper, base32hex, base32hexupper, base32hexpad, base32hexpadupper, base36, base36upper.

The multibase spec test suite (tests/README.md) describes case_insensitivity.csv as:

case_insensitivity.csv | hello world | Differently cased than expected, must decode without errors

On the current code these decoders throw on differently cased input. Every line below encodes hello world (hex 68656c6c6f20776f726c64):

import { base16 } from 'multiformats/bases/base16'
import { base32 } from 'multiformats/bases/base32'
import { base36 } from 'multiformats/bases/base36'

base16.decode('f68656c6c6f20776F726C64')   // throws: Non-base16 character
base32.decode('bnbswy3dpeB3W64TMMQ')        // throws: Non-base32 character
base36.decode('kfUvrsIvVnfRbjWaJo')         // throws: Non-base36 character

All 12 lines of the spec's case_insensitivity.csv fail this way. The other three official fixtures (basic, leading_zero, two_leading_zeros) already pass, so this is purely the case-insensitivity gap. The repo's own test/test-multibase-spec.spec.ts carries the basic fixtures inline but omits case_insensitivity.csv, which hid the gap.

This matters for interop: a multibase string produced by a case-insensitive-tolerant implementation (for example go-multibase output that was upper/lower-folded in transit) does not decode in JS today.

References

  • multibase tests/case_insensitivity.csv fixture ("must decode without errors").
  • multibase README.md registry table marks these codecs case-insensitive.
  • RFC 4648 section 3.4 (base16/base32): decoders accept either case.
  • go-multibase routes base16 through hex.DecodeString (case-insensitive) and base32 through case-insensitive decoders, and multibase_test.go tampers case for case-insensitive codecs and requires decode to succeed.

Fix

Thread an optional caseInsensitive flag from the rfc4648 and baseX factories. When set, the decode lookup table maps both the lower and upper case of each alphabet character to the same index. The flag is enabled only for the spec-designated codecs above.

Scope notes:

  • Only decode becomes case-insensitive. Encode output is unchanged and stays canonical (lowercase or uppercase per codec).
  • Case-sensitive codecs (base58btc, base58flickr, base64, base64url, base64pad, base256emoji, identity, base2, base8, base10) keep the default false and are unaffected. base32z is not in the case-insensitive registry and is left case-sensitive.

Tests

Added a case insensitivity block to test/test-multibase-spec.spec.ts using the exact 12 fixture strings from the spec's case_insensitivity.csv, asserting each decodes to hello world. These tests fail on master and pass with this change. The full node suite is green (478 passing), encode output is byte-identical, and case-sensitive codecs still reject mixed case.

The multibase registry marks base16, base32 (and all 8 variants) and
base36 as case-insensitive, and the spec's case_insensitivity.csv
fixture requires their decoders to accept differently cased input
"without errors". RFC 4648 section 3.4 backs this for base16/base32, and
go-multibase decodes these codecs case-insensitively.

Currently mixed-case input throws "Non-baseXX character". Thread a
caseInsensitive flag from the rfc4648 and baseX factories so the decode
lookup resolves both cases for only the spec-designated codecs. Encode
output stays canonical and case-sensitive codecs are unaffected.

@rvagg rvagg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, legit, and matches go-multibase

@rvagg rvagg merged commit ad87caa into multiformats:master Jun 24, 2026
23 checks passed
github-actions Bot pushed a commit that referenced this pull request Jun 24, 2026
## [14.0.1](v14.0.0...v14.0.1) (2026-06-24)

### Bug Fixes

* case-insensitive decode for base16/base32/base36 (multibase spec) ([#341](#341)) ([ad87caa](ad87caa))
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 14.0.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants