Skip to content
This repository was archived by the owner on Oct 12, 2026. It is now read-only.

feat: add CNH, RENAVAM and boleto - #9

Merged
jeferson0306 merged 1 commit into
masterfrom
feat/cnh-renavam-boleto
Sep 4, 2026
Merged

jeferson0306 merged 1 commit into
masterfrom
feat/cnh-renavam-boleto

Conversation

@jeferson0306

Copy link
Copy Markdown
Collaborator

The three left out of #6 because I could not settle which published algorithm was authoritative. brazilian-utils — already the reference for PIS and the voter ID — covers all three, so the same method applies: its published vectors first, then a differential run against a flat transcription of its algorithm.

What each one turned out to be

CNH's two check digits are coupled. When the first overflows past nine it is written as zero and the second is computed two lower to compensate. Treating them independently — which is what several published implementations do, and what I would have written from memory — accepts numbers the Detran would reject. This is exactly the variance that kept it out of #6.

RENAVAM runs its weights from the right, cycling two through nine, rather than the left-to-right descending series every other document here uses. Nine digits is the old format and eleven the current one; the shorter is the longer with leading zeros, so both are accepted and normalised to eleven.

The boleto is two checks, not one. The linha digitável is the barcode's 44 digits shuffled into three typed groups, each carrying its own module-10 digit — that exists so someone copying from paper is told which group they mistyped. Only once those pass is the barcode reassembled (the pieces sit in a different order in each) and its own module-11 digit checked, which lives inside the barcode at position 4 rather than at the end.

The 48-digit slip is reported as unchecked, not invalid

Arrecadação slips — utilities, taxes — are a different structure and are not implemented. They return a message saying the number was not checked. Calling it invalid would be a lie about a slip that may well be good, and the caller would have no way to tell the difference.

Two deliberate deviations from the reference

Both in the direction this service already goes:

  • Stray characters are rejected, not stripped. The reference accepts 639884962abc as a valid RENAVAM because it removes the letters first. This service has rejected that shape since fix: reject stray characters instead of stripping them #3 and is not making an exception.
  • A run of one repeated digit is not a registration. 00000000000 satisfies RENAVAM's arithmetic and the reference accepts it; every other document here excludes repeated-digit values by name, and so does this one.

Both are asserted in the tests, so the divergence is recorded rather than accidental.

Verification

  • The reference's own published vectors for all three.
  • A differential run of 5,000 generated numbers each for CNH and RENAVAM, against transcriptions written without the helpers the code under test uses.
  • 500 constructed-valid CNHs, because random digits are almost never valid and a validator returning false unconditionally would otherwise sail through a differential.
  • For the boleto, a stronger test than a differential: changing any one of the 47 digits must invalidate it. A check that skipped a position would show up as a survivor. All 47 pass.

go test ./... -race green; utils coverage 71% → 76%.

Wiring was three lines

Because #8 moved every validator into one table both endpoints read. A test asserts all three are reachable through POST /validate/batch with no further wiring — that refactor earning its keep.

The three left out of the catalogue because I could not settle which published
algorithm was authoritative. brazilian-utils — already the reference for PIS and
the voter ID — covers all three, so the same method applies: its vectors, then a
differential run against a flat transcription of its algorithm.

CNH's two check digits are coupled. When the first overflows past nine it is
written as zero and the second is computed two lower to compensate. Treating
them independently, which is what several published implementations do, accepts
numbers the Detran would reject.

RENAVAM runs its weights from the right, cycling two through nine, rather than
the left-to-right descending series the rest of these documents use. Nine digits
is the old format and eleven the current one; the shorter is the longer with
leading zeros, so both are accepted and normalised to eleven.

The boleto is two checks, not one. The linha digitável is the barcode's 44
digits shuffled into three typed groups, each carrying its own module-10 digit
so that someone copying from paper is told which group they mistyped. Only after
those pass is the barcode reassembled — the pieces are in a different order in
each — and its own module-11 digit checked, which sits inside the barcode rather
than at the end.

A 48-digit arrecadação slip is a different structure and is not implemented. It
is reported as not checked rather than as invalid: calling it invalid would be
a lie about a number that may well be good.

One deliberate deviation from the reference in each of the first two: stray
characters are rejected rather than stripped, and a run of one repeated digit is
not a registration. Both match what the rest of this service already does.

Wiring was three lines, because #8 moved the validators into one table that both
endpoints read. A test asserts all three are reachable through the batch without
any further wiring — that table earning its keep.
@jeferson0306
jeferson0306 merged commit f167878 into master Sep 4, 2026
1 check passed
@jeferson0306
jeferson0306 deleted the feat/cnh-renavam-boleto branch September 4, 2026 09:01
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant