This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Provenance.Attestation;
|
||||
using Xunit;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Provenance.Attestation;
|
||||
using Xunit;
|
||||
|
||||
@@ -11,16 +13,40 @@ public class PromotionAttestationBuilderTests
|
||||
public void Produces_canonical_json_for_predicate()
|
||||
{
|
||||
var predicate = new PromotionPredicate(
|
||||
ImageDigest: sha256:img,
|
||||
SbomDigest: sha256:sbom,
|
||||
VexDigest: sha256:vex,
|
||||
PromotionId: prom-1,
|
||||
RekorEntry: uuid,
|
||||
Metadata: new Dictionary<string, string>{{env,prod}});
|
||||
ImageDigest: "sha256:img",
|
||||
SbomDigest: "sha256:sbom",
|
||||
VexDigest: "sha256:vex",
|
||||
PromotionId: "prom-1",
|
||||
RekorEntry: "uuid",
|
||||
// Intentionally shuffled input order; canonical JSON must be sorted.
|
||||
Metadata: new Dictionary<string, string> { { "env", "prod" }, { "region", "us-east" } });
|
||||
|
||||
var bytes = PromotionAttestationBuilder.CreateCanonicalJson(predicate);
|
||||
var json = Encoding.UTF8.GetString(bytes);
|
||||
|
||||
json.Should().Be("ImageDigest":"sha256:img");
|
||||
json.Should().Be("{\"ImageDigest\":\"sha256:img\",\"Metadata\":{\"env\":\"prod\",\"region\":\"us-east\"},\"PromotionId\":\"prom-1\",\"RekorEntry\":\"uuid\",\"SbomDigest\":\"sha256:sbom\",\"VexDigest\":\"sha256:vex\"}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuildAsync_adds_predicate_claim_and_signs_payload()
|
||||
{
|
||||
var predicate = new PromotionPredicate(
|
||||
ImageDigest: "sha256:img",
|
||||
SbomDigest: "sha256:sbom",
|
||||
VexDigest: "sha256:vex",
|
||||
PromotionId: "prom-1");
|
||||
|
||||
var key = new InMemoryKeyProvider("kid-1", Encoding.UTF8.GetBytes("secret"));
|
||||
var signer = new HmacSigner(key);
|
||||
|
||||
var attestation = await PromotionAttestationBuilder.BuildAsync(
|
||||
predicate,
|
||||
signer,
|
||||
claims: new Dictionary<string, string> { { "traceId", "abc123" } });
|
||||
|
||||
attestation.Payload.Should().BeEquivalentTo(PromotionAttestationBuilder.CreateCanonicalJson(predicate));
|
||||
attestation.Signature.KeyId.Should().Be("kid-1");
|
||||
attestation.Signature.Claims.Should().ContainKey("predicateType").WhoseValue.Should().Be(PromotionAttestationBuilder.PredicateType);
|
||||
attestation.Signature.Claims.Should().ContainKey("traceId").WhoseValue.Should().Be("abc123");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,6 @@ public class SampleStatementDigestTests
|
||||
var statements = LoadSamples().Select(pair => pair.Statement).ToArray();
|
||||
BuildStatementDigest.ComputeMerkleRootHex(statements)
|
||||
.Should()
|
||||
.Be("e3a89fe0d08e2b16a6c7f1feb1d82d9e7ef9e8b74363bf60da64f36078d80eea");
|
||||
.Be("958465d432c9c8497f9ea5c1476cc7f2bea2a87d3ca37d8293586bf73922dd73");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Remove="xunit" />
|
||||
<PackageReference Remove="xunit.runner.visualstudio" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../StellaOps.Provenance.Attestation/StellaOps.Provenance.Attestation.csproj" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Text;
|
||||
using FluentAssertions;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Provenance.Attestation;
|
||||
using Xunit;
|
||||
|
||||
@@ -7,36 +8,38 @@ namespace StellaOps.Provenance.Attestation.Tests;
|
||||
|
||||
public class VerificationTests
|
||||
{
|
||||
private const string Payload = "{\"hello\":\"world\"}";
|
||||
private const string ContentType = "application/json";
|
||||
|
||||
[Fact]
|
||||
public async Task Verifier_accepts_valid_signature()
|
||||
{
|
||||
var key = new InMemoryKeyProvider(test-key, Encoding.UTF8.GetBytes(secret));
|
||||
var key = new InMemoryKeyProvider("test-key", Encoding.UTF8.GetBytes("secret"));
|
||||
var signer = new HmacSigner(key);
|
||||
var verifier = new HmacVerifier(key);
|
||||
|
||||
var request = new SignRequest(Encoding.UTF8.GetBytes(payload), application/json);
|
||||
var request = new SignRequest(Encoding.UTF8.GetBytes(Payload), ContentType);
|
||||
var signature = await signer.SignAsync(request);
|
||||
|
||||
var result = await verifier.VerifyAsync(request, signature);
|
||||
result.IsValid.Should().BeTrue();
|
||||
result.Reason.Should().Be(ok);
|
||||
result.Reason.Should().Be("verified");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Verifier_rejects_tampered_payload()
|
||||
{
|
||||
var key = new InMemoryKeyProvider(test-key, Encoding.UTF8.GetBytes(secret));
|
||||
var key = new InMemoryKeyProvider("test-key", Encoding.UTF8.GetBytes("secret"));
|
||||
var signer = new HmacSigner(key);
|
||||
var verifier = new HmacVerifier(key);
|
||||
|
||||
var request = new SignRequest(Encoding.UTF8.GetBytes(payload), application/json);
|
||||
var request = new SignRequest(Encoding.UTF8.GetBytes(Payload), ContentType);
|
||||
var signature = await signer.SignAsync(request);
|
||||
|
||||
var tampered = new SignRequest(Encoding.UTF8.GetBytes(payload-tampered), application/json);
|
||||
var tampered = new SignRequest(Encoding.UTF8.GetBytes(Payload + "-tampered"), ContentType);
|
||||
var result = await verifier.VerifyAsync(tampered, signature);
|
||||
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Reason.Should().Contain(mismatch);
|
||||
result.Reason.Should().Be("signature or time invalid");
|
||||
}
|
||||
}
|
||||
EOF}
|
||||
|
||||
Reference in New Issue
Block a user