Files
git.stella-ops.org/src/__Libraries/StellaOps.AuditPack/Services/AuditBundleReader.Read.cs

100 lines
3.5 KiB
C#

namespace StellaOps.AuditPack.Services;
public sealed partial class AuditBundleReader
{
/// <summary>
/// Reads and verifies an audit bundle.
/// </summary>
public async Task<AuditBundleReadResult> ReadAsync(
AuditBundleReadRequest request,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
ArgumentException.ThrowIfNullOrWhiteSpace(request.BundlePath);
if (!File.Exists(request.BundlePath))
{
return AuditBundleReadResult.Failed("Bundle file not found");
}
var tempDir = CreateTempDir("audit-read");
try
{
await ExtractBundleAsync(request.BundlePath, tempDir, cancellationToken).ConfigureAwait(false);
var manifestLoad = await LoadManifestAsync(tempDir, cancellationToken).ConfigureAwait(false);
if (!manifestLoad.Success || manifestLoad.Manifest is null || manifestLoad.ManifestBytes is null)
{
return AuditBundleReadResult.Failed(manifestLoad.Error ?? "Failed to parse manifest");
}
var result = new AuditBundleReadResult
{
Success = true,
Manifest = manifestLoad.Manifest,
BundleDigest = await ComputeFileDigestAsync(request.BundlePath, cancellationToken).ConfigureAwait(false),
ExtractedPath = request.ExtractToPath is not null ? null : tempDir
};
var signature = await ApplySignatureVerificationAsync(
result,
request,
manifestLoad.ManifestBytes,
tempDir,
cancellationToken)
.ConfigureAwait(false);
if (signature.Stop)
{
return signature.Result;
}
result = signature.Result;
var merkle = await ApplyMerkleVerificationAsync(
result,
request,
manifestLoad.Manifest,
tempDir,
cancellationToken)
.ConfigureAwait(false);
if (merkle.Stop)
{
return merkle.Result;
}
result = merkle.Result;
var digests = await ApplyInputDigestVerificationAsync(
result,
request,
manifestLoad.Manifest,
tempDir,
cancellationToken)
.ConfigureAwait(false);
if (digests.Stop)
{
return digests.Result;
}
result = digests.Result;
var extraction = HandleExtraction(result, request, tempDir);
if (!extraction.Success)
{
return extraction.Result with { Success = false, Error = extraction.Error };
}
result = extraction.Result;
tempDir = extraction.TempDir;
if (request.LoadReplayInputs)
{
var extractPath = result.ExtractedPath ?? tempDir;
var inputs = await LoadReplayInputsAsync(extractPath, cancellationToken)
.ConfigureAwait(false);
result = result with { ReplayInputs = inputs };
}
return result;
}
catch (Exception ex)
{
return AuditBundleReadResult.Failed($"Failed to read bundle: {ex.Message}");
}
}
}