Skip to content

De-salt a system hash and recover the embedded date

This operation (BlandHash) reads the 42-character location map off the front of a salted hash, walks it in reverse insertion order to remove and hex-decode each embedded date part, and reconstructs both the original (unsalted) hash and the create date that was embedded at salting time. In-repo, SystemHashService.RemoveSystemHashSaltAndValidateLifetime calls this immediately after constructing a de-salting SaltShaker (OP-HU-02).

Field Value
Operation ID OP-HU-04
Trigger Called after OP-HU-02 (in-repo: SystemHashService.RemoveSystemHashSaltAndValidateLifetime)
Inputs saltyHash produced by OP-HU-03
Outputs Return value: original (unsalted) hash; out expireDate: the create date embedded at salting time
Primary components SaltShaker.BlandHash(string saltyHash, out DateTime expireDate)
Entry point src/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SaltShaker.cs

Steps

  1. Take the first 42 characters of saltyHash as the location map (BR-HU-01).
  2. On failure: a saltyHash shorter than 42 characters throws an ArgumentOutOfRangeException.
  3. Split the map into 6-character chunks and reverse their order, producing entries in Month → Minute order (BR-HU-01, BR-HU-06).
  4. Remove every occurrence of the map string from saltyHash (BR-HU-08).
  5. Enumerate the date-part lengths in reverse order (Month, Hour, Second, Millisecond, Day, Year, Minute), with a parallel index into the reversed map entries (BR-HU-06). For each part:
  6. Parse the insertion index from the first 3 characters of the map entry; the pad character and body-length digits are ignored.
  7. Read the appropriate number of characters (4, 6, or 8, depending on the part) at that index and hex-decode them (BR-HU-02).
    • On failure: malformed hexadecimal digits in the salted body produce a FormatException; an out-of-range insertion index produces an ArgumentOutOfRangeException.
  8. Remove those characters from the working string, undoing the insertion in LIFO order.
  9. Map the dictionary key back to the matching local variable (minute, year, day, millisecond, second, hour, month).
  10. Build the recovered DateTime from the decoded parts (BR-HU-03).
  11. On failure: an unparseable date string under the current culture produces a FormatException.
  12. Return the remaining string as the original (unsalted) hash.

Process flow

The following diagram visualizes this operation's control flow.

flowchart TD
    Start([Called after OP-HU-02]) --> S1[Take first 42 chars as map]
    S1 --> S2["Split map into 6-char chunks,<br>reverse to Month..Minute order"]
    S2 --> S3[Remove map string from saltyHash]
    S3 --> Loop{For each part in<br>Month..Minute order}
    Loop -->|next part| S4[Parse insertion index from map entry]
    S4 --> S5["Read + hex-decode date-part chars"]
    S5 --> S6[Remove decoded chars from working string]
    S6 --> Loop
    Loop -->|all parts done| S7[Build recovered DateTime]
    S7 --> S8[Return remaining string as original hash]
    S8 --> EndOk(["Original hash + create date returned"])
    S1 -->|saltyHash < 42 chars| EndErr([ArgumentOutOfRangeException])

Failure and edge-case behavior

  • saltyHash shorter than 42 characters: ArgumentOutOfRangeException at step 1.
  • Malformed decimal digits in the location map (step 4.1 index parse): FormatException.
  • Malformed hexadecimal digits in the salted body (step 4.2 date-part decode): FormatException.
  • Out-of-range insertion index used as a substring/removal bound: ArgumentOutOfRangeException at steps 4.2–4.3 (step 4.1 only converts the fixed three-character decimal field and does not itself use the parsed value as an index).
  • Unparseable date string under the current culture: FormatException at step 5.
  • null input: NullReferenceException.
  • This operation performs no lifetime check of its own and never throws SystemHashExpiredException; that decision belongs entirely to the calling consumer (see Signal an expired system hash).

Sources

  • src/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SaltShaker.cs
  • src/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SystemHashService.cs