Files
git.stella-ops.org/src/Cryptography/StellaOps.Cryptography/KeyEscrow/IKeyEscrowService.cs

208 lines
6.3 KiB
C#

// Copyright © StellaOps. All rights reserved.
// SPDX-License-Identifier: AGPL-3.0-or-later
// Sprint: SPRINT_20260112_018_CRYPTO_key_escrow_shamir
// Tasks: ESCROW-003, ESCROW-004
namespace StellaOps.Cryptography.KeyEscrow;
/// <summary>
/// Service for key escrow operations using Shamir's Secret Sharing.
/// </summary>
public interface IKeyEscrowService
{
/// <summary>
/// Escrow a key by splitting it into shares and distributing to agents.
/// </summary>
/// <param name="keyId">Identifier for the key being escrowed.</param>
/// <param name="keyMaterial">The key material to escrow.</param>
/// <param name="options">Escrow configuration options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Result containing share IDs and metadata.</returns>
Task<KeyEscrowResult> EscrowKeyAsync(
string keyId,
byte[] keyMaterial,
KeyEscrowOptions options,
CancellationToken cancellationToken = default);
/// <summary>
/// Recover a key from escrow using collected shares.
/// </summary>
/// <param name="request">Recovery request with authorization details.</param>
/// <param name="shares">Decrypted shares from custodians.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Result containing recovered key material.</returns>
Task<KeyRecoveryResult> RecoverKeyAsync(
KeyRecoveryRequest request,
IReadOnlyList<KeyShare> shares,
CancellationToken cancellationToken = default);
/// <summary>
/// Get escrow status for a key.
/// </summary>
/// <param name="keyId">Key identifier.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Escrow status or null if not escrowed.</returns>
Task<KeyEscrowStatus?> GetEscrowStatusAsync(
string keyId,
CancellationToken cancellationToken = default);
/// <summary>
/// List all escrowed keys.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>List of escrowed key summaries.</returns>
Task<IReadOnlyList<KeyEscrowSummary>> ListEscrowedKeysAsync(
CancellationToken cancellationToken = default);
/// <summary>
/// Revoke escrow for a key (delete all shares).
/// </summary>
/// <param name="keyId">Key identifier.</param>
/// <param name="reason">Reason for revocation.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>True if revocation succeeded.</returns>
Task<bool> RevokeEscrowAsync(
string keyId,
string reason,
CancellationToken cancellationToken = default);
/// <summary>
/// Re-escrow a key with new shares (after recovery or rotation).
/// Invalidates previous shares.
/// </summary>
/// <param name="keyId">Key identifier.</param>
/// <param name="keyMaterial">Key material to re-escrow.</param>
/// <param name="options">New escrow options (or null to use previous).</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Result containing new share IDs.</returns>
Task<KeyEscrowResult> ReEscrowKeyAsync(
string keyId,
byte[] keyMaterial,
KeyEscrowOptions? options = null,
CancellationToken cancellationToken = default);
}
/// <summary>
/// Options for key escrow operations.
/// </summary>
public sealed record KeyEscrowOptions
{
/// <summary>
/// Minimum shares required for recovery (M in M-of-N).
/// </summary>
public required int Threshold { get; init; }
/// <summary>
/// Total shares to create (N in M-of-N).
/// </summary>
public required int TotalShares { get; init; }
/// <summary>
/// Days until shares expire.
/// </summary>
public int ExpirationDays { get; init; } = 365;
/// <summary>
/// IDs of agents to distribute shares to.
/// Must have at least TotalShares agents.
/// </summary>
public IReadOnlyList<string>? AgentIds { get; init; }
/// <summary>
/// Whether to require dual-control ceremony for recovery.
/// </summary>
public bool RequireDualControl { get; init; } = true;
/// <summary>
/// Metadata to attach to the escrow record.
/// </summary>
public IReadOnlyDictionary<string, string>? Metadata { get; init; }
}
/// <summary>
/// Status of a key's escrow.
/// </summary>
public sealed record KeyEscrowStatus
{
/// <summary>
/// Key identifier.
/// </summary>
public required string KeyId { get; init; }
/// <summary>
/// Whether the key is currently escrowed.
/// </summary>
public required bool IsEscrowed { get; init; }
/// <summary>
/// Threshold for recovery.
/// </summary>
public int Threshold { get; init; }
/// <summary>
/// Total shares created.
/// </summary>
public int TotalShares { get; init; }
/// <summary>
/// Number of shares still valid (not expired or revoked).
/// </summary>
public int ValidShares { get; init; }
/// <summary>
/// When the escrow was created.
/// </summary>
public DateTimeOffset? CreatedAt { get; init; }
/// <summary>
/// When shares expire.
/// </summary>
public DateTimeOffset? ExpiresAt { get; init; }
/// <summary>
/// Whether recovery is currently possible.
/// </summary>
public bool CanRecover => ValidShares >= Threshold;
/// <summary>
/// Custodians holding shares.
/// </summary>
public IReadOnlyList<string>? CustodianIds { get; init; }
}
/// <summary>
/// Summary of an escrowed key.
/// </summary>
public sealed record KeyEscrowSummary
{
/// <summary>
/// Key identifier.
/// </summary>
public required string KeyId { get; init; }
/// <summary>
/// Threshold for recovery.
/// </summary>
public required int Threshold { get; init; }
/// <summary>
/// Total shares.
/// </summary>
public required int TotalShares { get; init; }
/// <summary>
/// When escrowed.
/// </summary>
public required DateTimeOffset CreatedAt { get; init; }
/// <summary>
/// When shares expire.
/// </summary>
public required DateTimeOffset ExpiresAt { get; init; }
/// <summary>
/// Escrow metadata.
/// </summary>
public IReadOnlyDictionary<string, string>? Metadata { get; init; }
}