docs consolidation

This commit is contained in:
StellaOps Bot
2025-12-24 21:45:46 +02:00
parent 4231305fec
commit 43e2af88f6
76 changed files with 2887 additions and 796 deletions

View File

@@ -0,0 +1,342 @@
# CanonJson API Reference
**Namespace**: `StellaOps.Canonical.Json`
**Assembly**: `StellaOps.Canonical.Json`
**Version**: 1.0.0
---
## Overview
The `CanonJson` class provides RFC 8785-compliant JSON canonicalization and cryptographic hashing utilities for content-addressed identifiers. It ensures deterministic, reproducible JSON serialization across all environments.
---
## CanonVersion Class
Static class containing canonicalization version constants and utilities.
### Constants
| Constant | Type | Value | Description |
|----------|------|-------|-------------|
| `V1` | `string` | `"stella:canon:v1"` | Version 1: RFC 8785 JSON canonicalization |
| `VersionFieldName` | `string` | `"_canonVersion"` | Field name for version marker (underscore ensures first position) |
| `Current` | `string` | `V1` | Current default version for new hashes |
### Methods
#### IsVersioned
```csharp
public static bool IsVersioned(ReadOnlySpan<byte> canonicalJson)
```
Detects if canonical JSON includes a version marker.
**Parameters:**
- `canonicalJson`: UTF-8 encoded canonical JSON bytes
**Returns:** `true` if the JSON starts with `{"_canonVersion":`, `false` otherwise
**Example:**
```csharp
var json = """{"_canonVersion":"stella:canon:v1","foo":"bar"}"""u8;
bool versioned = CanonVersion.IsVersioned(json); // true
var legacy = """{"foo":"bar"}"""u8;
bool legacyVersioned = CanonVersion.IsVersioned(legacy); // false
```
---
#### ExtractVersion
```csharp
public static string? ExtractVersion(ReadOnlySpan<byte> canonicalJson)
```
Extracts the version string from versioned canonical JSON.
**Parameters:**
- `canonicalJson`: UTF-8 encoded canonical JSON bytes
**Returns:** The version string (e.g., `"stella:canon:v1"`) or `null` if not versioned
**Example:**
```csharp
var json = """{"_canonVersion":"stella:canon:v1","foo":"bar"}"""u8;
string? version = CanonVersion.ExtractVersion(json); // "stella:canon:v1"
```
---
## CanonJson Class
Static class providing JSON canonicalization and hashing methods.
### Canonicalization Methods
#### Canonicalize<T>
```csharp
public static byte[] Canonicalize<T>(T obj)
```
Canonicalizes an object to RFC 8785 JSON without version marker (legacy format).
**Parameters:**
- `obj`: The object to canonicalize
**Returns:** UTF-8 encoded canonical JSON bytes
**Example:**
```csharp
var obj = new { z = 3, a = 1 };
byte[] canonical = CanonJson.Canonicalize(obj);
// Result: {"a":1,"z":3}
```
---
#### CanonicalizeVersioned<T>
```csharp
public static byte[] CanonicalizeVersioned<T>(T obj, string version = CanonVersion.Current)
```
Canonicalizes an object with a version marker for content-addressed hashing.
**Parameters:**
- `obj`: The object to canonicalize
- `version`: Canonicalization version (default: `CanonVersion.Current`)
**Returns:** UTF-8 encoded canonical JSON bytes with version marker
**Exceptions:**
- `ArgumentNullException`: When `version` is null
- `ArgumentException`: When `version` is empty
**Example:**
```csharp
var obj = new { z = 3, a = 1 };
byte[] canonical = CanonJson.CanonicalizeVersioned(obj);
// Result: {"_canonVersion":"stella:canon:v1","a":1,"z":3}
// With explicit version
byte[] v2 = CanonJson.CanonicalizeVersioned(obj, "stella:canon:v2");
// Result: {"_canonVersion":"stella:canon:v2","a":1,"z":3}
```
---
### Hashing Methods
#### Hash<T>
```csharp
public static string Hash<T>(T obj)
```
Computes SHA-256 hash of canonical JSON (legacy format, no version marker).
**Parameters:**
- `obj`: The object to hash
**Returns:** Lowercase hex-encoded SHA-256 hash (64 characters)
**Example:**
```csharp
var obj = new { foo = "bar" };
string hash = CanonJson.Hash(obj);
// Result: "7a38bf81f383f69433ad6e900d35b3e2385593f76a7b7ab5d4355b8ba41ee24b"
```
---
#### HashVersioned<T>
```csharp
public static string HashVersioned<T>(T obj, string version = CanonVersion.Current)
```
Computes SHA-256 hash of versioned canonical JSON.
**Parameters:**
- `obj`: The object to hash
- `version`: Canonicalization version (default: `CanonVersion.Current`)
**Returns:** Lowercase hex-encoded SHA-256 hash (64 characters)
**Example:**
```csharp
var obj = new { foo = "bar" };
string hash = CanonJson.HashVersioned(obj);
// Different from legacy hash due to version marker
```
---
#### HashPrefixed<T>
```csharp
public static string HashPrefixed<T>(T obj)
```
Computes SHA-256 hash with `sha256:` prefix (legacy format).
**Parameters:**
- `obj`: The object to hash
**Returns:** Hash in format `sha256:<64-hex-chars>`
**Example:**
```csharp
var obj = new { foo = "bar" };
string hash = CanonJson.HashPrefixed(obj);
// Result: "sha256:7a38bf81f383f69433ad6e900d35b3e2385593f76a7b7ab5d4355b8ba41ee24b"
```
---
#### HashVersionedPrefixed<T>
```csharp
public static string HashVersionedPrefixed<T>(T obj, string version = CanonVersion.Current)
```
Computes SHA-256 hash with `sha256:` prefix (versioned format).
**Parameters:**
- `obj`: The object to hash
- `version`: Canonicalization version (default: `CanonVersion.Current`)
**Returns:** Hash in format `sha256:<64-hex-chars>`
**Example:**
```csharp
var obj = new { foo = "bar" };
string hash = CanonJson.HashVersionedPrefixed(obj);
// Result: "sha256:..." (different from HashPrefixed due to version marker)
```
---
## IJsonCanonicalizer Interface
Interface for JSON canonicalization implementations.
### Methods
#### Canonicalize
```csharp
byte[] Canonicalize(ReadOnlySpan<byte> json)
```
Canonicalizes UTF-8 JSON bytes per RFC 8785.
**Parameters:**
- `json`: Raw UTF-8 JSON bytes to canonicalize
**Returns:** Canonical UTF-8 JSON bytes
---
#### CanonicalizeWithVersion
```csharp
byte[] CanonicalizeWithVersion(ReadOnlySpan<byte> json, string version)
```
Canonicalizes UTF-8 JSON bytes with version marker prepended.
**Parameters:**
- `json`: Raw UTF-8 JSON bytes to canonicalize
- `version`: Version string to embed
**Returns:** Canonical UTF-8 JSON bytes with `_canonVersion` field
---
## Usage Examples
### Computing Content-Addressed IDs
```csharp
using StellaOps.Canonical.Json;
// Evidence predicate hashing
var evidence = new EvidencePredicate
{
Source = "scanner/trivy",
SbomEntryId = "sha256:91f2ab3c:pkg:npm/lodash@4.17.21",
VulnerabilityId = "CVE-2021-23337"
};
// Compute versioned hash (recommended)
string evidenceId = CanonJson.HashVersionedPrefixed(evidence);
// Result: "sha256:..."
```
### Verifying Attestations
```csharp
public bool VerifyAttestation(byte[] payload, string expectedHash)
{
// Detect format and verify accordingly
if (CanonVersion.IsVersioned(payload))
{
var version = CanonVersion.ExtractVersion(payload);
// Re-canonicalize with same version and compare
var computed = CanonJson.HashVersioned(payload, version!);
return computed == expectedHash;
}
// Legacy format
var legacyHash = CanonJson.Hash(payload);
return legacyHash == expectedHash;
}
```
### Migration from Legacy to Versioned
```csharp
// Old code (legacy)
var hash = CanonJson.Hash(predicate);
// New code (versioned) - just add "Versioned"
var hash = CanonJson.HashVersioned(predicate);
```
---
## Algorithm Details
### RFC 8785 Compliance
| Requirement | Implementation |
|-------------|----------------|
| Key ordering | Ordinal string comparison (case-sensitive, ASCII) |
| Number format | IEEE 754, shortest representation |
| String escaping | Minimal (only `"`, `\`, control characters) |
| Whitespace | None (compact output) |
| Encoding | UTF-8 without BOM |
### Version Marker Position
The `_canonVersion` field is **always first** in the output due to:
1. Underscore (`_`) sorts before all letters in ASCII
2. After injecting version, remaining keys are sorted normally
```json
{"_canonVersion":"stella:canon:v1","aaa":1,"bbb":2,"zzz":3}
```
---
## Related Documentation
- [Proof Chain Specification](../modules/attestor/proof-chain-specification.md)
- [Canonicalization Migration Guide](../operations/canon-version-migration.md)
- [RFC 8785 - JSON Canonicalization Scheme](https://datatracker.ietf.org/doc/html/rfc8785)