Refactor code structure for improved readability and maintainability; removed redundant code blocks and optimized function calls.
This commit is contained in:
50
src/Attestor/StellaOps.Attestation.Tests/DsseHelperTests.cs
Normal file
50
src/Attestor/StellaOps.Attestation.Tests/DsseHelperTests.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Attestation;
|
||||
using StellaOps.Attestor.Envelope;
|
||||
using Xunit;
|
||||
|
||||
public class DsseHelperTests
|
||||
{
|
||||
private sealed class FakeSigner : IAuthoritySigner
|
||||
{
|
||||
public Task<string> GetKeyIdAsync(System.Threading.CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult("fake-key");
|
||||
|
||||
public Task<byte[]> SignAsync(ReadOnlyMemory<byte> paePayload, System.Threading.CancellationToken cancellationToken = default)
|
||||
=> Task.FromResult(Convert.FromHexString("deadbeef"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WrapAsync_ProducesDsseEnvelope()
|
||||
{
|
||||
var stmt = new InTotoStatement(
|
||||
Type: "https://in-toto.io/Statement/v1",
|
||||
Subject: new[] { new Subject("demo", new System.Collections.Generic.Dictionary<string, string> { { "sha256", "abcd" } }) },
|
||||
PredicateType: "demo/predicate",
|
||||
Predicate: new { hello = "world" });
|
||||
|
||||
var envelope = await DsseHelper.WrapAsync(stmt, new FakeSigner());
|
||||
|
||||
envelope.PayloadType.Should().Be("https://in-toto.io/Statement/v1");
|
||||
var roundtrip = JsonSerializer.Deserialize<InTotoStatement>(envelope.Payload.Span);
|
||||
roundtrip!.PredicateType.Should().Be("demo/predicate");
|
||||
envelope.Signatures.Should().ContainSingle();
|
||||
envelope.Signatures[0].KeyId.Should().Be("fake-key");
|
||||
envelope.Signatures[0].Signature.Should().Be(Convert.ToBase64String(Convert.FromHexString("deadbeef")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PreAuthenticationEncoding_FollowsDsseSpec()
|
||||
{
|
||||
var payloadType = "example/type";
|
||||
var payload = Encoding.UTF8.GetBytes("{}");
|
||||
|
||||
var pae = DsseHelper.PreAuthenticationEncoding(payloadType, payload);
|
||||
pae.Should().ContainSubsequence(Encoding.UTF8.GetBytes(payloadType));
|
||||
pae.Should().ContainSubsequence(payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../StellaOps.Attestation/StellaOps.Attestation.csproj" />
|
||||
<PackageReference Include="xunit" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
55
src/Attestor/StellaOps.Attestation/DsseHelper.cs
Normal file
55
src/Attestor/StellaOps.Attestation/DsseHelper.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Attestor.Envelope;
|
||||
|
||||
namespace StellaOps.Attestation;
|
||||
|
||||
public static class DsseHelper
|
||||
{
|
||||
public static byte[] PreAuthenticationEncoding(string payloadType, ReadOnlySpan<byte> payload)
|
||||
{
|
||||
static byte[] Cat(params byte[][] parts)
|
||||
{
|
||||
var len = 0;
|
||||
foreach (var part in parts)
|
||||
{
|
||||
len += part.Length;
|
||||
}
|
||||
|
||||
var buf = new byte[len];
|
||||
var offset = 0;
|
||||
foreach (var part in parts)
|
||||
{
|
||||
Buffer.BlockCopy(part, 0, buf, offset, part.Length);
|
||||
offset += part.Length;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
var header = Encoding.UTF8.GetBytes("DSSEv1");
|
||||
var pt = Encoding.UTF8.GetBytes(payloadType);
|
||||
var lenPt = Encoding.UTF8.GetBytes(pt.Length.ToString(CultureInfo.InvariantCulture));
|
||||
var lenPayload = Encoding.UTF8.GetBytes(payload.Length.ToString(CultureInfo.InvariantCulture));
|
||||
var space = Encoding.UTF8.GetBytes(" ");
|
||||
|
||||
return Cat(header, space, lenPt, space, pt, space, lenPayload, space, payload.ToArray());
|
||||
}
|
||||
|
||||
public static async Task<DsseEnvelope> WrapAsync(InTotoStatement statement, IAuthoritySigner signer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(statement);
|
||||
ArgumentNullException.ThrowIfNull(signer);
|
||||
|
||||
var payloadBytes = JsonSerializer.SerializeToUtf8Bytes(statement, statement.GetType());
|
||||
var pae = PreAuthenticationEncoding(statement.Type ?? string.Empty, payloadBytes);
|
||||
var signatureBytes = await signer.SignAsync(pae, cancellationToken).ConfigureAwait(false);
|
||||
var keyId = await signer.GetKeyIdAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var dsseSignature = DsseSignature.FromBytes(signatureBytes, keyId);
|
||||
return new DsseEnvelope(statement.Type, payloadBytes, new[] { dsseSignature });
|
||||
}
|
||||
}
|
||||
11
src/Attestor/StellaOps.Attestation/IAuthoritySigner.cs
Normal file
11
src/Attestor/StellaOps.Attestation/IAuthoritySigner.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StellaOps.Attestation;
|
||||
|
||||
public interface IAuthoritySigner
|
||||
{
|
||||
Task<string> GetKeyIdAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
Task<byte[]> SignAsync(ReadOnlyMemory<byte> paePayload, CancellationToken cancellationToken = default);
|
||||
}
|
||||
14
src/Attestor/StellaOps.Attestation/Models.cs
Normal file
14
src/Attestor/StellaOps.Attestation/Models.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Attestation;
|
||||
|
||||
public sealed record Subject(
|
||||
[property: JsonPropertyName("name")] string Name,
|
||||
[property: JsonPropertyName("digest")] IReadOnlyDictionary<string, string> Digest);
|
||||
|
||||
public sealed record InTotoStatement(
|
||||
[property: JsonPropertyName("_type")] string Type,
|
||||
[property: JsonPropertyName("subject")] IReadOnlyList<Subject> Subject,
|
||||
[property: JsonPropertyName("predicateType")] string PredicateType,
|
||||
[property: JsonPropertyName("predicate")] object Predicate);
|
||||
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../StellaOps.Attestor.Envelope/StellaOps.Attestor.Envelope.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user