Measured, not asserted

Does obfuscation break evidence chain of custody?

A chain of custody is a sequence whose links are joined by an identity comparison: this handover's giver must be the last handover's taker. Two names that cannot be read compare equal to each other, which is the direction that makes a broken chain look continuous. This test measured exactly that, and it only became visible after a second control that was firing first was taken out of the pattern.

Four exhibits, four different ways to lose one

The fixture holds four exhibits and eleven handovers, each exhibit failing on at most one axis. One is clean and admissible. One has a break: the second handover is given up by somebody who never received the item. One is continuous and authorised but the seal was not intact when the laboratory took delivery. One is continuous, authorised and sealed, and fifteen hours pass between two handovers with nobody recorded as holding it.

Four controls sit over that: every holder must be on the authorised list, each handover must start where the last one ended, the seal must be intact at every handover, and no unexplained interval may exceed twelve hours. Above them, the firm's own rule refuses a chain with fewer links than the seizing officer's paperwork declares.

Protection on its own moved nothing

Five areas, five presets, twenty-five comparisons, all identical to the unprotected runs. Nothing below is caused by protecting the code.

A control that fires first hides the result behind it

Renaming the field naming who handed the exhibit over broke every link, because each handover's giver became undefined while the previous taker was still a real name. All four exhibits were refused with two or three breaks listed each. Loud, and safe.

Renaming both sides of that comparison -- giver and taker together -- should have produced the opposite, because two undefined values compare equal. What it actually produced was four refusals again, this time for a different reason: undefined is not an authorised role. The authorisation control was reading the same names, found them absent from the list, and refused first.

That is the whole reason to re-run a group with a colliding control removed. With the authorisation requirement taken out of the pattern, the broken-chain exhibit came back ADMISSIBLE, printing links=3[undefined>undefined@120 undefined>undefined@500 undefined>undefined@800] declared=3 [] => ADMISSIBLE, checked by link-check(3 of 3 declared link(s) recorded).

No reasons at all. The break is not merely unreported -- it does not exist, because the adjacency test that defines a break compared two unreadable values and found them identical. A reviewer testing the obvious broad pattern sees a system that fails closed and concludes the area is safe.

The quiet controls are the ones with no identity in them

Renaming the flag requiring an unbroken chain produced an admissible exhibit with the break STILL VISIBLE in the printed links -- the record shows one custodian handing on and a different one taking over, and no reason is listed. The evidence of the fault is on the page for anybody who reads the handover list rather than the verdict.

The gap control failed open in both of its directions. Renaming the maximum-gap option reverted it to the library's default of no limit, and the exhibit with fifteen unaccounted hours came back admissible with an empty reason list and every custodian, seal and link correct. Renaming the timestamp field on the handovers produced the same verdict by a different route, printing every time as @undefined.

The seal behaved the same way: with the flag renamed, the exhibit whose seal was recorded as not intact was ADMISSIBLE, and the record still carries the broken seal. It was written down. It was simply not consulted.

A guard your own bundle writes and reads is a rehearsal

The most instructive arm in this area looks inert and is not. The firm's rule compares the number of links recorded against the number the exhibit declares, and both of those values live in the protected bundle: the declaration is written on the exhibit record and read by the rule.

Renaming that field left the rule working perfectly. It kept reporting link-check(3 of 3 declared link(s) recorded), because both sides of the comparison moved together. Meanwhile the engine's own printout of the same field read declared=undefined, because the engine is not protected and its read did not move.

Both halves of that are worth keeping. A provenance assertion that never crosses a boundary is robust against renaming -- and worthless as provenance, because it is comparing a value against itself. The same field read by anything on the other side of the boundary was lost. If the engine had decided on that value instead of printing it, the loss would have been silent.

What failed loudly

Renaming the exhibit identifier refused everything with unidentified-exhibit(refused before review). That is the same grouping-key collapse that committed a trust-account overdraw silently in another area of this pass, and here it was loud -- because in this file the key is the SUBJECT of the question and the engine refuses an unidentified subject, whereas a client code on an accounting posting is just an attribute and no ledger refuses a posting for carrying an unfamiliar one.

Renaming the seal flag reported every seal as broken and refused all four exhibits. Renaming the authorised-custodian list refused everything. Renaming the seal identifier was cosmetic, changing seal S-4403 to seal undefined in a reason string and no verdict.

If you protect an evidence or case-management application

Make an unreadable custodian a refusal rather than a comparison. Any adjacency test over identities needs an explicit check that both identities are present before it compares them, because the failure mode of an identity comparison is that two absences match.

Verify against something written on the far side. The link count in this test was declared and read inside the same bundle, so it survived a rename and proved nothing. A count taken from the seizing officer's record, a signed manifest or the receiving laboratory's system is the version that is load-bearing.

And print the values a guard consumed, not only its conclusion -- while knowing that is not sufficient on its own. The chain here printed undefined>undefined in plain sight and still returned an admissible verdict, which is why the refusal has to be in the code rather than in the reader. Our ISO 27001 and SOC 2 guide covers what evidence of that kind an assessor expects to see.

Frequently asked questions

Did obfuscation change any admissibility result on its own?

No. Twenty-five base comparisons across five areas and five presets were identical to the unprotected runs. Every failure here required member renaming aimed at a property name the installed custody engine also reads.

How does a broken chain end up looking continuous?

The adjacency test asks whether this handover's giver is the last handover's taker. With both fields renamed, both sides read undefined, and undefined equals undefined. The exhibit with a genuine break came back admissible with no reason listed at all.

Why did that only show up in one arm?

The authorisation control was reading the same custodian names and refusing first, so renaming both fields together looked like a safe, loud failure. Re-running the group with the authorisation requirement removed from the pattern is what exposed the quiet result behind it.

What happened to the gap control?

It failed open from both directions. Renaming the maximum-gap option reverted it to no limit and an exhibit with fifteen unaccounted hours was admissible with an empty reason list. Renaming the timestamp field produced the same verdict and printed every handover time as undefined.

Did the firm's own link-count rule help?

It kept working and proved nothing. The declared link count is written on the exhibit record and read by the rule, both inside the protected bundle, so renaming moved both sides together and the check still reported three of three. The engine's own printout of the same field read undefined.

Which parts failed loudly?

The exhibit identifier, the seal-intact flag and the authorised-custodian list all refused every exhibit. Renaming the field naming who handed the item over broke every link and refused everything. Those are the failures found before a hearing.

What is the cheapest fix?

Check that both identities are present before comparing them, and take the expected link count from a record written outside your bundle -- the seizing officer's paperwork or the receiving laboratory's system. Both are worth doing regardless of obfuscation, because a dropped field in an import produces the same continuous-looking chain.

Related reading