---
title: SAIF.NetCore.HashUtil architecture
summary: Architectural responsibilities, boundaries, and interaction patterns.
status: draft
owner: SAIF Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.HashUtil
reference_type: package
---

# SAIF.NetCore.HashUtil architecture

SAIF.NetCore.HashUtil is a leaf utility library in the Framework.NCore implementation layer that provides reversible, non-cryptographic obfuscation of a create timestamp inside an existing hash string (`SaltShaker`) plus the `SystemHashExpiredException` type used by callers to signal hash expiry; it takes no package dependency on `SAIF.NetCore.ServiceContracts` or `SAIF.NetCore.Common`, and it is consumed, via a NuGet package reference, only by `SAIF.NetCore.SystemHash`, which is one of the three documented implementation-to-implementation exceptions in `Architecture.md`.

## Responsibilities
- Salt a caller-supplied hash string by hex-encoding a create date into seven positionally-random insertion points and returning a location map prefixed to the salted body; the map is a fixed 42 characters only while each entry's unpadded body-length field stays two digits, a condition `SaltHash` does not validate (`SaltShaker.SaltHash`, see [BR-HU-01](business-rules.md#br-hu-01-fixed-42-character-location-map-prefix)).
- De-salt a previously salted hash string by walking the location map in reverse insertion order, removing and decoding each date part, and recovering the original create date (`SaltShaker.BlandHash`).
- Define `SystemHashExpiredException`, the exception type a consumer throws when it decides a recovered create date has expired.
- Provide these primitives as a stateful, dependency-free building block — each `SaltShaker` instance retains its constructor-supplied `_dateParts` and a per-instance `Random` (`_random`) across calls — with no DI registration, no hosting, and no configuration surface. The observed in-repo consumer constructs a fresh `SaltShaker` per salt/de-salt operation rather than reusing or sharing an instance.*

## Component boundaries
- `SaltShaker`: owns the salting/de-salting algorithm, the per-instance `Random` used for insertion positions and pad characters, and the internal hex encode/decode helpers. It never throws `SystemHashExpiredException` and never compares dates itself*.
- `SystemHashExpiredException`: a plain `System.Exception` subtype that only carries constructor parameters (`nowDate`, `hashDate`); it has no serialization constructor and no parameterless/message constructors.
- Boundary with `SAIF.NetCore.SystemHash`: SystemHash produces the SHA-256/Base64 hash, supplies `DateTime.Now` as the create date, invokes `SaltShaker` to salt/de-salt, and owns the lifetime/expiry decision and the `throw` of `SystemHashExpiredException`; HashUtil owns only the obfuscation mechanics.

## Interaction model
- HashUtil exposes no service interfaces and performs no dependency injection; the sole in-repo consumer (`SystemHashService`) instantiates `SaltShaker` directly with `new` — once with a `DateTime` (to salt) and once parameterless (to de-salt).
- There is no inbound or outbound network call anywhere in this library; all behavior is in-process BCL computation (`System.Collections.Generic`, `System.Linq`, `System.Text`, `System.Random`, `System.Convert`).
- The only architectural relationship is the inbound NuGet package reference from `SAIF.NetCore.SystemHash`, which reaches applications transitively through `SAIF.NetCore.ServiceExtensions → SAIF.NetCore.SystemHash → SAIF.NetCore.HashUtil`.

## Architecture diagram
The following diagram shows HashUtil's position relative to its sole consumer and the rest of the Framework.NCore dependency graph.

```mermaid
flowchart TB
    HASH["SAIF.NetCore.HashUtil<br>(SaltShaker, SystemHashExpiredException)"]
    SYSHASH["SAIF.NetCore.SystemHash<br>(SystemHashService)"]
    SE["SAIF.NetCore.ServiceExtensions"]
    APP["Consuming application"]

    SYSHASH -.->|NuGet ref| HASH
    SE -.->|NuGet ref| SYSHASH
    APP -.->|NuGet ref| SE
```

## Sequence diagram
The following diagram shows the primary salt/de-salt round trip as invoked by the sole in-repo consumer, `SystemHashService`.

```mermaid
sequenceDiagram
	participant Caller
	participant SystemHashService
	participant SaltShaker

	Caller->>SystemHashService: Get(systemType)
	SystemHashService->>SystemHashService: Compute SHA-256/Base64 hash
	SystemHashService->>SaltShaker: new SaltShaker(DateTime.Now)
	SystemHashService->>SaltShaker: SaltHash(systemHash)
	SaltShaker-->>SystemHashService: salted hash string
	SystemHashService-->>Caller: salted hash string

	Caller->>SystemHashService: RemoveSystemHashSaltAndValidateLifetime(saltedHash)
	SystemHashService->>SaltShaker: new SaltShaker()
	SystemHashService->>SaltShaker: BlandHash(saltedHash, out createDate)
	SaltShaker-->>SystemHashService: original hash + createDate
	SystemHashService->>SystemHashService: Compare createDate against lifetime
	SystemHashService-->>Caller: value, or throw SystemHashExpiredException
```

## Design constraints
- No cryptographic guarantee: salting is positional obfuscation with a non-cryptographic `System.Random`, not encryption, and the location map discloses every insertion index*.
- Correct round-tripping depends on `Dictionary<string,string>` (`_dateParts`) and `Dictionary<string,int>` (`_datePartLength`) enumeration order being stable, which is not a documented contract of `Dictionary<TKey,TValue>`*.
- The fixed 42-character map width (`MAP_LENGTH`) holds only when the salted body length after each insertion stays within two digits and indices stay below 1000; this is satisfied for the only observed input shape (44-char Base64 SHA-256) but is not enforced*.
- A `SaltShaker` built with the parameterless constructor has a null `_dateParts` and is usable only for `BlandHash`; nothing enforces this split at compile time*.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [API reference](api/index.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.

## Sources
- `src/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SaltShaker.cs`
- `src/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SystemHashExpiredException.cs`
- `src/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil.csproj`
- `src/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SystemHashService.cs`
- `Architecture.md`
