HeroSD-JWT 2.0.0

dotnet add package HeroSD-JWT --version 2.0.0
                    
NuGet\Install-Package HeroSD-JWT -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="HeroSD-JWT" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="HeroSD-JWT" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="HeroSD-JWT" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add HeroSD-JWT --version 2.0.0
                    
#r "nuget: HeroSD-JWT, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package HeroSD-JWT@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=HeroSD-JWT&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=HeroSD-JWT&version=2.0.0
                    
Install as a Cake Tool

HeroSD-JWT

NuGet Version NuGet Downloads Build Status License: MIT .NET Native AOT requires validation

A .NET library implementing SD-JWT (Selective Disclosure for JSON Web Tokens) according to the IETF draft-ietf-oauth-selective-disclosure-jwt specification.

Overview

SD-JWT enables privacy-preserving credential sharing by allowing holders to selectively disclose only necessary claims to verifiers, while cryptographically proving the disclosed claims are authentic and unmodified.

Table of Contents

Key Features

  • Create SD-JWTs with selectively disclosable claims
  • Nested object selective disclosure - Full support for nested properties like address.street, address.geo.lat (multi-level nesting)
  • Array element selective disclosure - Syntax like degrees[1] for individual array elements
  • Array & Object Reconstruction API - Automatically reconstruct hierarchical structures from disclosed claims
  • JWT Key Rotation Support - RFC 7515 compliant kid parameter with key resolver pattern for secure key management
  • Key binding (proof of possession) - RFC 7800 compliant with temporal validation
  • Decoy digests - Privacy protection against claim enumeration
  • Holder-controlled claim disclosure
  • Cryptographic verification of signatures and claim integrity
  • Uses .NET BCL APIs plus Microsoft.Bcl.Memory on .NET 8; Ed25519 uses vendored source (see Requirements)
  • Constant-time comparison for security-critical operations
  • Algorithm confusion prevention (rejects "none" algorithm)
  • Multi-targeting .NET 8.0, .NET 9.0, and .NET 10.0

Installation

dotnet add package HeroSD-JWT

Or via NuGet Package Manager:

Install-Package HeroSD-JWT

Upgrade note (1.x → 2.x): In 1.1.7, verification defaults to ExpectedKeyType = Either; starting with 2.0.0, it defaults to Asymmetric. Applications that verify HS* signatures must explicitly configure VerificationKeyType.Symmetric, or previously accepted presentations will fail. RSA/ECDSA verifiers normally use Asymmetric. See the 1.1.7 → 2.0 migration guide for ASP.NET scheme and DI changes. Check the installed NuGet version: repository source may be newer than the latest published package.

Quick Start

The fluent builder provides an easy, discoverable API. Subsequent snippets reuse imports and variables from the preceding example unless marked as alternatives:

using HeroSdJwt.Issuance;
using HeroSdJwt.Cryptography;
using HeroSdJwt.Extensions;

// Generate a signing key
var keyGen = KeyGenerator.Instance;
var key = keyGen.GenerateHmacKey();

// Create SD-JWT with fluent builder
var sdJwt = SdJwtIssuerBuilder.Create()
    .WithClaim("sub", "user-123")
    .WithClaim("name", "Alice")
    .WithClaim("email", "alice@example.com")
    .WithClaim("age", 30)
    .MakeSelective("email", "age")  // Selectively disclosable
    .SignWithHmac(key)
    .Build();

// Create presentation revealing only email
var presentation = sdJwt.ToPresentation("email");

Nested Object Selective Disclosure

Selectively disclose nested properties with full JSONPath-style syntax:

using HeroSdJwt.Cryptography;
using HeroSdJwt.KeyBinding;
using HeroSdJwt.Primitives;
using HeroSdJwt.Verification;

// Create SD-JWT with nested object claims
var sdJwt = SdJwtIssuerBuilder.Create()
    .WithClaim("sub", "user-456")
    .WithClaim("address", new
    {
        street = "123 Main Street",
        city = "Boston",
        state = "MA",
        zip = "02101",
        geo = new { lat = 42.3601, lon = -71.0589 }
    })
    .MakeSelective("address.street", "address.city", "address.geo.lat", "address.geo.lon")
    .SignWithHmac(key)
    .Build();

// Holder creates presentation with only specific nested claims
var presentation = sdJwt.ToPresentation("address.street", "address.geo.lat");

// Verifier receives and verifies
var verifier = new SdJwtVerifier(
    new SdJwtVerificationOptions { ExpectedKeyType = VerificationKeyType.Symmetric },
    new EcPublicKeyConverter(),
    new SignatureValidator(),
    new DigestValidator(),
    new KeyBindingValidator(),
    new ClaimValidator());
var result = verifier.VerifyPresentation(presentation, key);

// Automatically reconstruct the nested object structure
var address = result.GetDisclosedObject("address");
// Returns: { "street": "123 Main Street", "geo": { "lat": 42.3601 } }

Array Element Selective Disclosure

var sdJwt = SdJwtIssuerBuilder.Create()
    .WithClaim("degrees", new[] { "BS", "MS", "PhD" })
    .MakeSelective("degrees[1]", "degrees[2]") // Only MS and PhD are selective
    .SignWithHmac(key)
    .Build();

// Create presentation
var presentation = sdJwt.ToPresentation("degrees[2]"); // Only reveal PhD

// Reconstruct array from disclosed elements
var result = verifier.VerifyPresentation(presentation, key);
var degrees = result.GetDisclosedArray("degrees");
// Returns: [null, null, "PhD"] - sparse array with only disclosed element

Different Signature Algorithms

var keyGen = KeyGenerator.Instance;

// HMAC (simple, symmetric)
var key = keyGen.GenerateHmacKey();
var sdJwt = SdJwtIssuerBuilder.Create()
    .WithClaims(claims)
    .MakeSelective("email")
    .SignWithHmac(key)
    .Build();

// RSA (asymmetric, widely supported)
var (rsaPrivate, rsaPublic) = keyGen.GenerateRsaKeyPair();
var sdJwt = SdJwtIssuerBuilder.Create()
    .WithClaims(claims)
    .MakeSelective("email")
    .SignWithRsa(rsaPrivate)
    .Build();

// ECDSA (asymmetric, compact)
var (ecPrivate, ecPublic) = keyGen.GenerateEcdsaKeyPair();
var sdJwt = SdJwtIssuerBuilder.Create()
    .WithClaims(claims)
    .MakeSelective("email")
    .SignWithEcdsa(ecPrivate)
    .Build();

// Ed25519 (asymmetric, fast, small keys)
var (ed25519Private, ed25519Public) = keyGen.GenerateEd25519KeyPair();
var sdJwt = SdJwtIssuerBuilder.Create()
    .WithClaims(claims)
    .MakeSelective("email")
    .SignWithEd25519(ed25519Private)
    .Build();

JWT Key Rotation Support

HeroSD-JWT supports JWT key rotation using the kid (key ID) parameter per RFC 7515 Section 4.1.4. This enables secure key management practices including regular key rotation, emergency revocation, and multi-key deployments.

Issuing SD-JWTs with Key IDs

Add a key identifier when creating SD-JWTs:

var keyGen = KeyGenerator.Instance;
var key = keyGen.GenerateHmacKey();

// Issue SD-JWT with key ID
var sdJwt = SdJwtIssuerBuilder.Create()
    .WithClaim("sub", "user-123")
    .WithClaim("email", "alice@example.com")
    .MakeSelective("email")
    .WithKeyId("key-2024-10")  // Add key identifier
    .SignWithHmac(key)
    .Build();
Verifying SD-JWTs with Key Resolver

Implement a key resolver to dynamically select verification keys based on the kid parameter:

using HeroSdJwt.Cryptography;
using HeroSdJwt.KeyBinding;
using HeroSdJwt.Primitives;
using HeroSdJwt.Verification;

// Set up key resolver with multiple keys
var keys = new Dictionary<string, byte[]>
{
    ["key-2024-09"] = oldKey,
    ["key-2024-10"] = currentKey,
    ["key-2024-11"] = newKey
};

// Create resolver delegate
KeyResolver resolver = keyId => keys.GetValueOrDefault(keyId);

// Verify presentation using key resolver
var verifier = new SdJwtVerifier(
    new SdJwtVerificationOptions { ExpectedKeyType = VerificationKeyType.Symmetric },
    new EcPublicKeyConverter(),
    new SignatureValidator(),
    new DigestValidator(),
    new KeyBindingValidator(),
    new ClaimValidator());
var result = verifier.TryVerifyPresentation(presentation, resolver);

if (result.IsValid)
{
    // Access disclosed claims
    var email = result.DisclosedClaims["email"].GetString();
}
Key Rotation Workflow

Typical key rotation lifecycle (30-day overlap period):

// Day 1-15: Only key-v1 active
var keysPhase1 = new Dictionary<string, byte[]>
{
    ["key-v1"] = keyV1
};

// Day 15-30: Both keys active (overlap period)
var keysPhase2 = new Dictionary<string, byte[]>
{
    ["key-v1"] = keyV1,  // Old key still valid
    ["key-v2"] = keyV2   // New key added
};
// Start issuing new tokens with key-v2, but both still verify

// Day 30+: Only key-v2 active (old key removed)
var keysPhase3 = new Dictionary<string, byte[]>
{
    ["key-v2"] = keyV2   // Only new key remains
};
// Old tokens with key-v1 now fail verification
Emergency Key Revocation

Immediately revoke a compromised key:

// Before: Both keys active
var keys = new Dictionary<string, byte[]>
{
    ["compromised-key"] = compromisedKey,
    ["emergency-key"] = emergencyKey
};

// After: Immediately remove compromised key
keys.Remove("compromised-key");

// All tokens issued with compromised-key now fail verification immediately
KeyResolver resolver = keyId => keys.GetValueOrDefault(keyId);
Backward Compatibility

Tokens without kid parameter work seamlessly with a fallback key:

// Verify tokens with or without kid
var result = verifier.TryVerifyPresentation(
    presentation,
    keyResolver: resolver,
    fallbackKey: legacyKey  // Used when JWT has no 'kid'
);

Advanced API (Full Control)

For advanced scenarios, use the low-level API:

using HeroSdJwt.Cryptography;
using HeroSdJwt.Issuance;
using HeroSdJwt.Primitives;
using HashAlgorithm = HeroSdJwt.Primitives.HashAlgorithm;

var issuer = new SdJwtIssuer(
    new DisclosureGenerator(),
    new DigestCalculator(),
    new EcPublicKeyConverter(),
    new JwtSigner());
var claims = new Dictionary<string, object>
{
    ["sub"] = "user-123",
    ["email"] = "alice@example.com"
};

var signingKey = KeyGenerator.Instance.GenerateHmacKey();
var sdJwt = issuer.CreateSdJwt(
    claims,
    selectivelyDisclosableClaims: new[] { "email" },
    signingKey,
    HashAlgorithm.Sha256,
    SignatureAlgorithm.HS256);
  • ECDSA signatures (ES256/ES384/ES512) follow the JWS spec and are emitted in JOSE raw R||S format (not ASN.1 DER); verifiers accept the JOSE form.
  • Version compatibility: ExpectedKeyType defaults to Either in 1.1.7 and Asymmetric from 2.0.0 onward. Configure Asymmetric for RSA/ECDSA public keys or Symmetric for HS* secrets; use Either only when both are deliberately permitted. See the migration guide.
  • SdJwtVerifierBuilder exposes .WithExpectedKeyType(...) so you can wire the key-type policy when constructing verifiers via DI.

Array Element Example:

var claims = new Dictionary<string, object>
{
    ["sub"] = "user-456",
    ["degrees"] = new[] { "BS", "MS", "PhD" }
};

// Make only MS and PhD selectively disclosable, keep BS always visible
var sdJwt = issuer.CreateSdJwt(
    claims,
    selectivelyDisclosableClaims: new[] { "degrees[1]", "degrees[2]" },
    signingKey,
    HashAlgorithm.Sha256
);

// JWT payload will contain:
// "degrees": ["BS", {"...": "digest_for_MS"}, {"...": "digest_for_PhD"}]

RS256/ES256 Example:

using System.Security.Cryptography;

// For RSA (RS256)
using var rsa = RSA.Create(2048);
var privateKey = rsa.ExportPkcs8PrivateKey();
var publicKey = rsa.ExportSubjectPublicKeyInfo();

var sdJwt = issuer.CreateSdJwt(
    claims,
    new[] { "email" },
    privateKey,
    HashAlgorithm.Sha256,
    SignatureAlgorithm.RS256);  // Specify algorithm

// For ECDSA (ES256)
using var ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256);
var privateKey = ecdsa.ExportPkcs8PrivateKey();
var publicKey = ecdsa.ExportSubjectPublicKeyInfo();

var sdJwt = issuer.CreateSdJwt(
    claims,
    new[] { "email" },
    privateKey,
    HashAlgorithm.Sha256,
    SignatureAlgorithm.ES256);  // Specify algorithm

2. Holder: Create Presentation

Continue from the first Advanced API HMAC example (the email claim, sdJwt, and signingKey); the array and asymmetric examples above are separate alternatives.

using HeroSdJwt.Presentation;

// Holder receives sdJwt from issuer and creates a presentation
var presenter = new SdJwtPresenter();

var presentation = presenter.CreatePresentation(
    sdJwt,
    selectedClaimNames: new[] { "email" } // Disclose the issuer's email claim
);

// Format for transmission
string presentationString = presenter.FormatPresentation(presentation);
// Format: "eyJhbGc...jwt...~WyI2cU1R...disclosure..."

3. Verifier: Verify Presentation

using HeroSdJwt.Cryptography;
using HeroSdJwt.KeyBinding;
using HeroSdJwt.Primitives;
using HeroSdJwt.Verification;

// Verify presentation
var verifier = new SdJwtVerifier(
    new SdJwtVerificationOptions { ExpectedKeyType = VerificationKeyType.Symmetric },
    new EcPublicKeyConverter(),
    new SignatureValidator(),
    new DigestValidator(),
    new KeyBindingValidator(),
    new ClaimValidator());
var verificationKey = signingKey; // Reuse the actual HS256 signing key from the issuer example

// Option 1: Throws exception on failure (recommended for most cases)
var result = verifier.VerifyPresentation(presentationString, verificationKey);
Console.WriteLine($"Email: {result.DisclosedClaims["email"]}");

// Option 2: Returns result without throwing (Try* pattern)
var tryResult = verifier.TryVerifyPresentation(presentationString, verificationKey);
if (tryResult.IsValid)
{
    Console.WriteLine("Verification succeeded!");
    var email = tryResult.DisclosedClaims["email"];
    Console.WriteLine($"Email: {email}");
}
else
{
    Console.WriteLine("Verification failed!");
    foreach (var error in tryResult.Errors)
    {
        Console.WriteLine($"Error: {error}");
    }
}

Architecture

The library follows the three-party SD-JWT model:

+---------+                  +--------+                  +----------+
| Issuer  |                  | Holder |                  | Verifier |
+----+----+                  +----+---+                  +----+-----+
     |                            |                           |
     |  1. Create SD-JWT          |                           |
     |  with selective disclosures|                           |
     |--------------------------->|                           |
     |                            |                           |
     |                            |  2. Select claims         |
     |                            |  to disclose              |
     |                            |                           |
     |                            |  3. Create presentation   |
     |                            |-------------------------->|
     |                            |                           |
     |                            |                           |  4. Verify
     |                            |                           |  signature
     |                            |                           |  & digests
     |                            |                           |

Security

Key Binding audience policy: The holder-signed KB-JWT aud identifies the verifier, which can differ from (or be absent in) the issuer-signed JWT's aud. For a holder-bound presentation, configure RequireKeyBinding = true, ExpectedKeyBindingAudience = "https://verifier.example", and ExpectedNonce. Use ExpectedAudience or ExpectedAudiences separately if the issuer JWT needs an audience policy. The fluent builder exposes WithExpectedKeyBindingAudience(...). When the new setting is omitted, the previous coupled-audience behavior is preserved for compatibility.

This library implements security best practices:

  • Constant-time comparison: Uses CryptographicOperations.FixedTimeEquals for digest validation to prevent timing attacks
  • Algorithm confusion prevention: Rejects "none" algorithm (both lowercase and uppercase)
  • Cryptographically secure salts: Uses RandomNumberGenerator for 128-bit salts
  • Dependency transparency: .NET 8 uses Microsoft.Bcl.Memory; Ed25519 includes vendored source. Review package and source provenance as part of security maintenance.
  • Strict validation: Treats warnings as errors, validates all inputs

Supported Algorithms

  • Hash algorithms: SHA-256 (default), SHA-384, SHA-512
  • Signature algorithms:
    • HS256 (HMAC-SHA256) - Symmetric signing with HMAC
    • RS256 (RSA-SHA256) - Asymmetric signing with RSA (minimum 2048 bits)
    • ES256 (ECDSA-P256-SHA256) - Asymmetric signing with ECDSA (P-256 curve)

Security Policy

For vulnerability reporting and security best practices, see:

  • SECURITY.md - Vulnerability disclosure policy and security contact
  • Security Guide - Detailed security best practices and guidelines

Requirements

  • Package consumers: supported target frameworks are net8.0, net9.0, and net10.0.
  • Building this repository: install exactly .NET SDK 10.0.402 as pinned by global.json, with SDK roll-forward disabled. Install the corresponding runtimes to run tests for all three target frameworks.
  • Core package dependencies: the .NET 8 target references Microsoft.Bcl.Memory to provide Base64Url APIs; .NET 9/10 use the native BCL APIs.
  • Vendored cryptography: Ed25519 includes source ported from Chaos.NaCl; see third-party notices.

Native AOT and Trimming Compatibility

Native AOT compatibility depends on the exact library version, target platform and claim shapes used. The core uses explicit JSON writing for several internal paths and avoids JWK serialize/deserialize round-trips, but some issuance and nested-claim paths still call JsonSerializer internally and carry IL2026/IL3050 suppressions. Do not interpret a successful regular .NET build, or a JsonElement input, as proof that every path works with Native AOT and trimming.

  • HMAC, RSA and ECDSA use .NET cryptographic APIs; Ed25519 uses vendored source.
  • Basic values and pre-parsed JSON can reduce dependence on reflection at the caller boundary. Custom object serialization may require source-generated JsonSerializerContext and careful preservation of types.
  • Test the actual package version, issuing and verifying your application's real claim shapes in a separately published Native AOT consumer. Check trim/AOT warnings instead of relying on a blanket compatibility claim.

For example, prepare a JsonElement without reflectively serializing an anonymous caller object:

using System.Text.Json;

using var document = JsonDocument.Parse("""{"name":"Alice","age":30}""");
var claims = new Dictionary<string, object>
{
    ["sub"] = "user-123",
    ["profile"] = document.RootElement.Clone()
};

This prepares the input only; the subsequent issuance and verification paths still require application-specific AOT validation.

Development

Building the Project

Source builds require the exact .NET SDK 10.0.402 pinned in global.json, independently of the package's .NET 8/9/10 consumption targets. The project uses the modern .slnx solution format:

# Build the entire solution
dotnet build HeroSD-JWT.slnx

# Build specific configuration
dotnet build HeroSD-JWT.slnx --configuration Release

# Restore dependencies
dotnet restore HeroSD-JWT.slnx

All projects multi-target .NET 8.0, .NET 9.0, and .NET 10.0. The build configuration is centrally managed in Directory.Build.props.

Testing

# Run all tests
dotnet test

# Run with verbose output
dotnet test --verbosity normal

# Run integration tests only (tests are grouped by namespace, not traits)
dotnet test --filter "FullyQualifiedName~HeroSdJwt.Tests.Integration"

# Run unit tests only
dotnet test --filter "FullyQualifiedName~HeroSdJwt.Tests.Unit"

# Run a single test class or method
dotnet test --filter "FullyQualifiedName~SdJwtVerifierTests"

Authentication Policy and Custom Verifiers

AddSdJwt applies the complete VerificationOptions policy separately for each authentication scheme. The default verifier is request-scoped. Custom verifier implementations must implement ISdJwtVerifierFactory, and subclasses of SdJwtVerifier must override CreateVerifier to preserve their custom behavior when applying the scheme policy.

See authentication policy migration for the factory contract, dependency lifetimes, and upgrade guidance.

Distributed Replay Protection & Revocation

For multi-node replay protection, the cache must support atomic create-if-absent with TTL. Ordinary IDistributedCache implementations are rejected when DistributedJtiCache is constructed; a get-then-set sequence cannot prevent concurrent replay.

// ApplicationAtomicCache must implement IAtomicDistributedCache using your backend.
builder.Services.AddSingleton<IDistributedCache, ApplicationAtomicCache>();
builder.Services
    .AddSdJwtServices()
    .AddSdJwtDistributedReplayProtection() // Atomic IJtiCache + enabled JtiValidator
    .AddSdJwtDistributedRevocation();      // Separate IRevocationStore adapter

No atomic Redis/SQL provider is bundled. You can also register an application-provided IJtiCache directly. For distributed JTI revocation, the cache must additionally implement IAtomicRevocationCache so repeating a revocation can never shorten its expiry. Ordinary IDistributedCache remains supported for key/user revocation, but JTI revocation fails closed without that separate atomic capability. See distributed replay and revocation requirements for both contracts, compatibility, and real-backend validation requirements.

Continuous Integration

The project includes comprehensive CI/CD workflows:

  • CI Pipeline (ci.yml) - Runs on every push/PR across all platforms and frameworks
  • Nightly Integration Tests (run-integrations.yml) - Comprehensive test suite running daily at 2 AM UTC
  • Security Scanning (scan-security.yml) - Automated vulnerability and dependency audits
  • Performance Benchmarks (perform-benchmarks.yml) - Continuous performance monitoring

Performance

  • Verification: < 100ms for 50-claim SD-JWTs
  • Processing: < 500ms for 100-claim SD-JWTs
  • Thread-agnostic design: Reuse SdJwtIssuer, SdJwtPresenter, SdJwtVerifier instances (you handle synchronization)

Documentation

Comprehensive documentation is available in the docs/ directory:

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for detailed guidelines.

Quick overview:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Write tests first (TDD)
  4. Ensure all tests pass (dotnet test)
  5. Follow .NET naming conventions
  6. Add XML documentation for public APIs
  7. Commit your changes (git commit -m 'feat: add amazing feature')
  8. Push to the branch (git push origin feature/amazing-feature)
  9. Open a Pull Request

See CONTRIBUTING.md for more details on code style, testing, and the review process.

License

MIT License - see LICENSE file for details

References

Support

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (6)

Showing the top 5 NuGet packages that depend on HeroSD-JWT:

Package Downloads
Sorcha.Cryptography

Multi-algorithm cryptography: ED25519, P-256, RSA-4096, BLS threshold signatures, post-quantum (ML-KEM/ML-DSA), zero-knowledge proofs, SD-JWT, HD wallet support (BIP32/39/44), Merkle trees, and encoding utilities

Sorcha.Blueprint.Engine

Portable blueprint execution engine with validate, calculate, route, and disclose pipeline

Sorcha.Wallet.Core

Wallet management with HD derivation and key operations

Sorcha.Wallet.Portable

Portable wallet library for Sorcha — HD derivation, key types, enums. No server dependencies.

Sorcha.Validator.Core

Enclave-safe validation and consensus logic

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 769 10/28/2026
1.1.7 20,499 11/20/2025
1.1.6 474 11/20/2025
1.1.3 359 11/14/2025
1.1.2 336 11/14/2025
1.1.1 348 11/14/2025
1.1.0 330 11/14/2025
1.0.7 373 11/3/2025
1.0.6 257 11/3/2025
1.0.5 252 10/24/2025