208 lines
6.3 KiB
C#
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; }
|
|
}
|