---
title: Salt a system hash
summary: Embed a hex-encoded create date into a hash string at random positions and prefix a location map, 42 characters wide only when every post-insertion body-length field renders as exactly two digits, ahead of a variable-length salted body.
status: draft
owner: SAIF Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.HashUtil
reference_type: package
operation_id: OP-HU-03
trigger: Called after constructing a salting SaltShaker
---

# Salt a system hash

This operation (`SaltHash`) inserts each hex-encoded date part from `_dateParts` into the progressively growing hash string at a random position, recording each insertion in a location map prefixed to the result; the map is a fixed 42 characters wide only while each entry's unpadded body-length field renders as two digits ([BR-HU-01](../business-rules.md#br-hu-01-fixed-42-character-location-map-prefix)). In-repo, `SystemHashService.ComputeHash` calls this immediately after constructing a salting `SaltShaker` (OP-HU-01).

| Field | Value |
| --- | --- |
| **Operation ID** | OP-HU-03 |
| **Trigger** | Called after OP-HU-01 (in-repo: `SystemHashService.ComputeHash`) |
| **Inputs** | `systemHash` (in-repo, a 44-char Base64 SHA-256 string) |
| **Outputs** | Salted hash string: a fixed 42-character location map (only while each entry's unpadded body-length field stays two digits) followed by the salted body, whose total string length varies with `systemHash.Length` plus the seven inserted date parts |
| **Primary components** | `SaltShaker.SaltHash(string systemHash)` |
| **Entry point** | `src/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SaltShaker.cs` |

## Steps

1. Create an empty `StringBuilder` to accumulate the location map.
2. For each entry of `_dateParts`, in enumeration order Minute, Year, Day, Millisecond, Second, Hour, Month ([BR-HU-04](../business-rules.md#br-hu-04-constructor-split-between-salting-and-de-salting), [BR-HU-06](../business-rules.md#br-hu-06-insertion-and-removal-order)):
   1. Pick a random insertion index in `[0, currentLength)` of the string as it currently stands, after any prior insertions in this loop ([BR-HU-05](../business-rules.md#br-hu-05-random-insertion-position-per-part)).
   2. Pick a random filler pad character in the range `'A'`–`'Y'` ([BR-HU-07](../business-rules.md#br-hu-07-random-pad-character%2C-ignored-on-decode)).
   3. Insert the hex-encoded date part at the chosen index.
   4. Append the insertion index (zero-padded to 3 digits), the pad character, and the new total body length (an unpadded decimal number, two digits for the observed/assumed input shape) to the map ([BR-HU-01](../business-rules.md#br-hu-01-fixed-42-character-location-map-prefix)).
   - On failure: if the instance was built with the parameterless constructor (OP-HU-02), this step throws an unguarded `NullReferenceException` because `_dateParts` is `null` ([BR-HU-04](../business-rules.md#br-hu-04-constructor-split-between-salting-and-de-salting)).
3. Return the concatenation of the location map and the fully salted `systemHash` body — the map is 42 characters wide only when every body-length field rendered as two digits (bodies of 10–99 characters after each insertion); the total returned string length is otherwise variable, and unsupported body lengths may still be returned without a round-trip guarantee ([BR-HU-01](../business-rules.md#br-hu-01-fixed-42-character-location-map-prefix)).
   - On failure: if `systemHash` is `null`, this operation throws an unguarded `NullReferenceException`; if `systemHash` is empty, `Random.Next(0,0)` returns 0 and insertion proceeds without error.

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start([Called after OP-HU-01]) --> S1[Create empty map StringBuilder]
	S1 --> Loop{For each date part in<br>Minute..Month order}
	Loop -->|next part| S2[Pick random insertion index]
	S2 --> S3["Pick random pad char 'A'-'Y'"]
	S3 --> S4[Insert hex-encoded part at index]
	S4 --> S5["Append index + pad + body length to map"]
	S5 --> Loop
	Loop -->|all parts done| S6["Return map + salted systemHash"]
	S6 --> EndOk([Salted hash string returned])
	S2 -->|_dateParts null| EndErr([NullReferenceException])
```

## Failure and edge-case behavior
- Parameterless-constructed instance (OP-HU-02): `NullReferenceException` iterating `_dateParts`.
- `null` `systemHash`: `NullReferenceException` reading `systemHash.Length`.
- Empty `systemHash`: proceeds without error; `Random.Next(0,0)` returns `0`.
- No validation exists for the post-insertion body length staying within the 2-digit (10–99 character) range assumed by the fixed-width map ([BR-HU-01](../business-rules.md#br-hu-01-fixed-42-character-location-map-prefix)).

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

## Sources
- `src/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SaltShaker.cs`
- `src/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SystemHashService.cs`
