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
SaltHashdoes not validate (SaltShaker.SaltHash, see BR-HU-01). - 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
SaltShakerinstance retains its constructor-supplied_datePartsand a per-instanceRandom(_random) across calls — with no DI registration, no hosting, and no configuration surface. The observed in-repo consumer constructs a freshSaltShakerper salt/de-salt operation rather than reusing or sharing an instance.*
Component boundaries¶
SaltShaker: owns the salting/de-salting algorithm, the per-instanceRandomused for insertion positions and pad characters, and the internal hex encode/decode helpers. It never throwsSystemHashExpiredExceptionand never compares dates itself*.SystemHashExpiredException: a plainSystem.Exceptionsubtype 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, suppliesDateTime.Nowas the create date, invokesSaltShakerto salt/de-salt, and owns the lifetime/expiry decision and thethrowofSystemHashExpiredException; HashUtil owns only the obfuscation mechanics.
Interaction model¶
- HashUtil exposes no service interfaces and performs no dependency injection; the sole in-repo consumer (
SystemHashService) instantiatesSaltShakerdirectly withnew— once with aDateTime(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 throughSAIF.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.
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.
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) andDictionary<string,int>(_datePartLength) enumeration order being stable, which is not a documented contract ofDictionary<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
SaltShakerbuilt with the parameterless constructor has a null_datePartsand is usable only forBlandHash; nothing enforces this split at compile time*.
Related documentation¶
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.cssrc/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SystemHashExpiredException.cssrc/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil.csprojsrc/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SystemHashService.csArchitecture.md