---
title: Compute a salted system hash
summary: Issue a time-stamped, randomized identity credential for a named system.
status: draft
owner: SAIF Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.SystemHash
reference_type: package
operation_id: OP-SH-01
trigger: A caller requests a credential for a given SystemType/name pair, directly or via the Get operation.
---

# Compute a salted system hash

Issues a salted, time-stamped identity credential that a caller attaches to a secure-setting or crypto-configuration request. The embedded timestamp is what a later lifetime check validates.

This credential represents a **claimed** identity for the named system — it is not proof of possession or one-time-use, since the hash is derivable from public inputs (see [Security and authorization](../security-authorization.md)). The later lifetime check in [De-salt and validate credential lifetime](validate-credential-lifetime.md) verifies only that the credential is fresh; comparing the recovered hash against an authorized identity is the external validator's own responsibility.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-SH-01 |
| **Trigger** | Direct call, or internal delegation from [Get the current application's system hash](get-current-application-hash.md). |
| **Inputs** | `type` (`SystemType`), `systemName` (string) |
| **Outputs** | Salted hash string (~120 characters) |
| **Primary components** | `SystemHashService.ComputeHash` |
| **Entry point** | `src/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SystemHashService.cs` |

## Steps

1. Compute the deterministic identity hash for `type` and `systemName` ([BR-SH-01](../business-rules.md#br-sh-01-identity-hash-encoding), [BR-SH-02](../business-rules.md#br-sh-02-identity-string-uses-type-ordinal)):
   - Build the identity string `"{systemName}-{(int)type}"`, using the `SystemType` enum's integer ordinal, not its name.
   - Encode the identity string as UTF-16LE bytes (`Encoding.Unicode.GetBytes`).
   - Compute the SHA-256 digest of those bytes.
   - Base64-encode the digest (44 characters) and return it.

   See [Compute the identity hash](compute-identity-hash.md) for the full step-by-step detail of this delegation.

   > **Origin**: Step 1 is implemented in [Compute the identity hash](compute-identity-hash.md) and executed on behalf of this operation.
2. Construct a salting `SaltShaker` pre-populated with the hex-encoded parts of the current host-local time (`DateTime.Now`, millisecond precision) ([BR-SH-03](../business-rules.md#br-sh-03-credential-embeds-creation-time)).
   - Sub-steps embedded from `SAIF.NetCore.HashUtil`:
     - Chain to the parameterless constructor, which creates an independent `System.Random` instance used later by `SaltHash` for insertion positions and pad characters — this `Random` instance is separate from, and unrelated to, the embedded create-date timestamp.
     - Create the `_dateParts` dictionary and, for each date part, format the create date with the current culture and hex-encode it, inserting entries in this order: Minute (`"mm"`), Year (`"yyyy"`), Day (`"dd"`), Millisecond (`"fff"`), Second (`"ss"`), Hour (`"HH"`, 24-hour), Month (`"MM"`).

     > **Origin**: The sub-steps above are implemented in [Construct a salting SaltShaker](../../saif-netcore-hashutil/operations/op-hu-01-construct-salting-saltshaker.md) (`SAIF.NetCore.HashUtil`) and executed on behalf of this operation.
3. Salt the identity hash by inserting the hex-encoded date parts at pseudo-random positions within the hash string ([BR-SH-04](../business-rules.md#br-sh-04-credential-randomized-per-call)).
   - Sub-steps embedded from `SAIF.NetCore.HashUtil`:
     - For each date part, in order Minute, Year, Day, Millisecond, Second, Hour, Month: pick a random insertion index in `[0, currentLength)` using the `SaltShaker`'s own `Random` instance, pick a random pad character in `'A'`–`'Y'`, insert the hex-encoded part at the chosen index, and append the zero-padded index, the pad character, and the new body length to a 42-character location map.
     - Return the concatenation of the 42-character location map and the fully salted hash body.

     > **Origin**: The sub-steps above are implemented in [Salt a system hash](../../saif-netcore-hashutil/operations/op-hu-03-salt-a-system-hash.md) (`SAIF.NetCore.HashUtil`) and executed on behalf of this operation.
4. Return the salted string as the credential.
   - Because the insertion positions and pad characters are chosen independently at random on every call, and the embedded timestamp also typically differs between calls, two calls with identical `type`/`systemName` arguments normally return different strings — but this is not a guaranteed-unique/collision-free property ([BR-SH-04](../business-rules.md#br-sh-04-credential-randomized-per-call)).

## Process flow

The following diagram shows the credential-issuance path — computing the identity hash and salting it with the current timestamp — and stops at returning the credential, before any lifetime check or external identity comparison occurs.

```mermaid
flowchart TD
	Start([Caller requests a credential]) --> Identity[Compute identity hash]
	Identity --> SaltConstruct[Construct salting SaltShaker with DateTime.Now]
	SaltConstruct --> SaltApply[Interleave date parts into hash]
	SaltApply --> Return([Return salted credential])
```

## Failure and edge-case behavior
- No input validation is performed on `systemName`; a null or empty value is concatenated into the identity string as-is and hashed without error.*

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.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.SystemHash/SAIF.NetCore.SystemHash/SAIF.NetCore.SystemHash/SystemHashService.cs`
- `src/SAIF.NetCore.HashUtil/SAIF.NetCore.HashUtil/SaltShaker.cs`
