Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
2b8f937
ruby spec updates
seebees Nov 14, 2025
d0f16e0
Start on instruction failures
seebees Nov 15, 2025
496a8db
Instruction files that pass and fail
seebees Nov 17, 2025
54853fa
starting to add raw
seebees Nov 17, 2025
f0aa6f7
adding ruby to raw support
seebees Nov 17, 2025
7c4ddb8
update names
seebees Nov 17, 2025
39c7799
Merge branch 'fireegg-test-servers' into seebees/instruction-files
seebees Nov 17, 2025
7c0ef74
Merge branch 'fireegg-test-servers' into seebees/instruction-files
seebees Nov 17, 2025
aeb359c
speed up the build
seebees Nov 17, 2025
270f2d5
speed up wait
seebees Nov 17, 2025
271fcba
update client building
seebees Nov 17, 2025
231e3ad
fix: Go in instruction files tests (#104)
lucasmcdonald3 Nov 17, 2025
0a4ef95
better failures?
seebees Nov 17, 2025
4d73663
bump php sdk to fixed instruction file (#106)
josecorella Nov 18, 2025
63e0ed1
faster parallel tests (#107)
seebees Nov 19, 2025
91933b7
enable cpp
seebees Nov 19, 2025
490ffb2
fix c++?
seebees Nov 19, 2025
d1366a9
Make the instruction file tests parallel
seebees Nov 20, 2025
c7f19ec
fix cpp
seebees Nov 20, 2025
ab1ba3a
go concurency
seebees Nov 20, 2025
99f05fe
cpp is hard
seebees Nov 20, 2025
35e8772
try this
seebees Nov 20, 2025
954b559
next cut
seebees Nov 20, 2025
ff1a84a
Enable address sanitizer during tests (#105)
ajewellamz Nov 20, 2025
0858f67
try not deleting the body
seebees Nov 20, 2025
5bfc9af
simplify?
seebees Nov 20, 2025
8d9ff30
try less work
seebees Nov 20, 2025
03d3151
try a different direction
seebees Nov 20, 2025
b4416dc
cpp build update
seebees Nov 20, 2025
30d797e
Making C test servers concurent
seebees Nov 21, 2025
d093671
update the test
seebees Nov 22, 2025
2e315c7
fix c build issue
seebees Nov 23, 2025
b3c4997
net v2 transition
rishav-karanjit Nov 25, 2025
cb6e80f
m
rishav-karanjit Nov 25, 2025
305be03
m
rishav-karanjit Nov 25, 2025
03db2be
m
rishav-karanjit Nov 25, 2025
666bb31
m
rishav-karanjit Nov 25, 2025
c1c0445
m
rishav-karanjit Nov 25, 2025
9d317ec
m
rishav-karanjit Nov 25, 2025
9ada40b
merge from seebees/instruction-files
rishav-karanjit Nov 25, 2025
60f5fc9
Merge branch 'seebees/instruction-files' of https://github.com/aws/am…
rishav-karanjit Nov 25, 2025
d4502d5
revert Ryan's branch merge
rishav-karanjit Nov 25, 2025
22dc07c
revert Ryan's branch merge
rishav-karanjit Nov 25, 2025
6e103b8
change V3 to V2 in logs and in class names
rishav-karanjit Nov 25, 2025
4ca7fcd
Merge branch 'fireegg-test-servers' into netV2TransitionServer
ShubhamChaturvedi7 Dec 6, 2025
1f405f8
Merge branch 'fireegg-test-servers' into netV2TransitionServer
ShubhamChaturvedi7 Dec 11, 2025
f2051d8
Merge branch 'fireegg-test-servers' into netV2TransitionServer
ShubhamChaturvedi7 Dec 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@
[submodule "test-server/cpp-v2-server/aws-sdk-cpp"]
path = test-server/cpp-v2-server/aws-sdk-cpp
url = git@github.com:awslabs/aws-sdk-cpp-staging.git
[submodule "test-server/net-v2-transition-server/s3ec-v2-transition-branch"]
path = test-server/net-v2-transition-server/s3ec-v2-transition-branch
url = https://github.com/aws/private-amazon-s3-encryption-client-dotnet-staging.git
branch = s3ec-v2-transition-integ
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ public void kmsV1LegacyFailsWhenLegacyDisabled(TestUtils.LanguageServerTarget la
.build());
fail("Expected Exception");
} catch (S3EncryptionClientError e) {
if (language.getLanguageName().equals(NET_V3_CURRENT) || language.getLanguageName().equals(NET_V2_CURRENT) || language.getLanguageName().equals(NET_V3_TRANSITION) || language.getLanguageName().equals(NET_V4)
if (language.getLanguageName().equals(NET_V3_CURRENT) || language.getLanguageName().equals(NET_V2_CURRENT) || language.getLanguageName().equals(NET_V2_TRANSITION) || language.getLanguageName().equals(NET_V3_TRANSITION) || language.getLanguageName().equals(NET_V4)
|| language.getLanguageName().equals(CPP_V2_CURRENT) || language.getLanguageName().equals(CPP_V2_TRANSITION) || language.getLanguageName().equals(CPP_V3)) {
assertTrue(e.getMessage().contains(
"The requested object is encrypted with V1 encryption schemas that have been disabled by client configuration"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ public class TestUtils {

// Sets of unsupported features by language
public static final Set<String> ENCRYPTION_CONTEXT_ON_DECRYPT_UNSUPPORTED =
Set.of(GO_V3_CURRENT, PHP_V2_CURRENT, PHP_V2_TRANSITION, PHP_V3, NET_V2_CURRENT, NET_V3_CURRENT, NET_V3_TRANSITION, NET_V4);
Set.of(GO_V3_CURRENT, PHP_V2_CURRENT, PHP_V2_TRANSITION, PHP_V3, NET_V2_CURRENT, NET_V3_CURRENT, NET_V2_TRANSITION, NET_V3_TRANSITION, NET_V4);

public static final Set<String> ENCRYPTION_CONTEXT_ON_ENCRYPT_UNSUPPORTED =
Set.of(NET_V2_CURRENT, NET_V3_CURRENT, NET_V3_TRANSITION, NET_V4);
Set.of(NET_V2_CURRENT, NET_V3_CURRENT, NET_V2_TRANSITION, NET_V3_TRANSITION, NET_V4);

public static final Set<String> RE_ENCRYPT_SUPPORTED =
Set.of(JAVA_V3_CURRENT, JAVA_V3_TRANSITION, JAVA_V4);
Expand All @@ -114,7 +114,7 @@ public class TestUtils {
// Cpp only supports Raw AES
public static final Set<String> RAW_AES_SUPPORTED =
Set.of(JAVA_V3_CURRENT, JAVA_V3_TRANSITION, JAVA_V4
, NET_V2_CURRENT, NET_V3_CURRENT, NET_V3_TRANSITION, NET_V4
, NET_V2_CURRENT, NET_V2_TRANSITION, NET_V3_CURRENT, NET_V3_TRANSITION, NET_V4
, RUBY_V2_TRANSITION, RUBY_V3
, CPP_V2_CURRENT, CPP_V2_TRANSITION, CPP_V3
);
Expand All @@ -134,7 +134,7 @@ public class TestUtils {
// .NET only supports decrypting instruction files using AES and RSA.
// Python MUST support decrypting KMS instruction files, but does not yet.
public static final Set<String> KMS_INSTRUCTION_FILE_UNSUPPORTED =
Set.of(NET_V2_CURRENT, NET_V2_TRANSITION, NET_V3_CURRENT, NET_V3_TRANSITION, NET_V4);
Set.of(NET_V2_CURRENT, NET_V3_CURRENT, NET_V2_TRANSITION, NET_V3_TRANSITION, NET_V4);

// Go does not write with instruction files
public static final Set<String> INSTRUCTION_FILE_PUT_UNSUPPORTED =
Expand Down Expand Up @@ -174,7 +174,7 @@ public class TestUtils {
Set.of(
JAVA_V3_TRANSITION,
GO_V3_TRANSITION,
// NET_V2_TRANSITION,
NET_V2_TRANSITION,
NET_V3_TRANSITION,
CPP_V2_TRANSITION,
PHP_V2_TRANSITION,
Expand Down Expand Up @@ -212,7 +212,7 @@ public class TestUtils {
servers.put(PHP_V3, new LanguageServerTarget(PHP_V3, "8093"));
servers.put(JAVA_V3_TRANSITION, new LanguageServerTarget(JAVA_V3_TRANSITION, "8094"));
servers.put(GO_V3_TRANSITION, new LanguageServerTarget(GO_V3_TRANSITION, "8095"));
// servers.put(NET_V2_TRANSITION, new LanguageServerTarget(NET_V2_TRANSITION, "8096"));
servers.put(NET_V2_TRANSITION, new LanguageServerTarget(NET_V2_TRANSITION, "8096"));
servers.put(RUBY_V2_TRANSITION, new LanguageServerTarget(RUBY_V2_TRANSITION, "8098"));
servers.put(PHP_V2_TRANSITION, new LanguageServerTarget(PHP_V2_TRANSITION, "8099"));
servers.put(JAVA_V4, new LanguageServerTarget(JAVA_V4, "8088"));
Expand Down
3 changes: 3 additions & 0 deletions test-server/net-v2-transition-server/.duvet/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
reports/
requirements/
specification/
27 changes: 27 additions & 0 deletions test-server/net-v2-transition-server/.duvet/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'$schema' = "https://awslabs.github.io/duvet/config/v0.4.0.json"

[[source]]
pattern = "**/*.cs"

# Include required specifications here
[[specification]]
source = "../specification/s3-encryption/client.md"
[[specification]]
source = "../specification/s3-encryption/decryption.md"
[[specification]]
source = "../specification/s3-encryption/encryption.md"
[[specification]]
source = "../specification/s3-encryption/key-commitment.md"
[[specification]]
source = "../specification/s3-encryption/key-derivation.md"
[[specification]]
source = "../specification/s3-encryption/data-format/content-metadata.md"
[[specification]]
source = "../specification/s3-encryption/data-format/metadata-strategy.md"

[report.html]
enabled = true

# Enable snapshots to prevent requirement coverage regressions
[report.snapshot]
enabled = false
44 changes: 44 additions & 0 deletions test-server/net-v2-transition-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/

# Visual Studio 2015/2017 cache/options directory
.vs/

# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates

# NuGet Packages
*.nupkg
*.snupkg
packages/

# JetBrains Rider
.idea/
*.sln.iml

# VS Code
.vscode/

# macOS
.DS_Store

# Temporary files
*.tmp
*.temp
128 changes: 128 additions & 0 deletions test-server/net-v2-transition-server/Controllers/ClientController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using System.Net;
using System.Security.Cryptography;
using System.Text.Json;
using Amazon.Extensions.S3.Encryption;
using Amazon.Extensions.S3.Encryption.Primitives;
using Microsoft.AspNetCore.Mvc;
using NetV2TransitionServer.Models;
using NetV2TransitionServer.Services;

namespace NetV2TransitionServer.Controllers;

[ApiController]
[Route("[controller]")]
public class ClientController(IClientCacheService clientCacheService, ILogger<ClientController> logger) : ControllerBase
{
[HttpPost]
public IActionResult CreateClient([FromBody] ClientRequest request)
{
// Return 501 for not implemented features by the server
if (request.Config.EnableDelayedAuthenticationMode)
return StatusCode(501, new GenericServerError { Message = "EnableDelayedAuthenticationMode not supported" });
if (request.Config.SetBufferSize.HasValue)
return StatusCode(501, new GenericServerError { Message = "SetBufferSize not supported" });

try
{
EncryptionMaterialsV2 encryptionMaterial;
if (request.Config.KeyMaterial.KmsKeyId != null)
{
// The POST request does not contain encryption context.
// However, encryption context is a required field when using KMS.
// So, we are passing empty dictionary.
var encryptionContext = new Dictionary<string, string>();
var kmsKeyId = request.Config.KeyMaterial.KmsKeyId;
encryptionMaterial = new EncryptionMaterialsV2(kmsKeyId, KmsType.KmsContext, encryptionContext);
logger.LogInformation(
"[NET-V2-Transitional] Created EncryptionMaterialsV2: KMS={KmsKeyId}",
kmsKeyId);
}
else if (request.Config.KeyMaterial.AesKey != null)
{
var aesKeyBytes = request.Config.KeyMaterial.AesKey;
var aes = Aes.Create();
aes.Key = aesKeyBytes;
encryptionMaterial = new EncryptionMaterialsV2(aes, SymmetricAlgorithmType.AesGcm);
logger.LogInformation(
"[NET-V2-Transitional] Created EncryptionMaterialsV2: AES");
}
else if (request.Config.KeyMaterial.RsaKey != null)
{
var rsaKeyBytes = request.Config.KeyMaterial.RsaKey;
var rsaKey = RSA.Create();
rsaKey.ImportPkcs8PrivateKey(new ReadOnlySpan<byte>(rsaKeyBytes), out _);
encryptionMaterial = new EncryptionMaterialsV2(rsaKey, AsymmetricAlgorithmType.RsaOaepSha1);
logger.LogInformation(
"Created EncryptionMaterialsV2: RSA");
} else
{
return StatusCode(501, new GenericServerError { Message = "Unknown or missing key material!" });
}

var enableLegacyUnauthenticatedModes = request.Config.EnableLegacyUnauthenticatedModes;
var enableLegacyWrappingAlgorithms = request.Config.EnableLegacyWrappingAlgorithms;
var commitmentPolicy = MapCommitmentPolicy(request.Config.CommitmentPolicy);

// SecurityProfile V2AndLegacy can decrypt from legacy S3EC but V2 cannot
var enableLegacyMode = enableLegacyUnauthenticatedModes || enableLegacyWrappingAlgorithms;
var securityProfile = enableLegacyMode ? SecurityProfile.V2AndLegacy : SecurityProfile.V2;
logger.LogInformation("[NET-V2-Transitional] Created securityProfile= {securityProfile}", securityProfile.ToString());

var encryptionAlgorithm = MapEncryptionAlgorithm(request.Config.EncryptionAlgorithm);
// var encryptionAlgorithm = commitmentPolicy == Amazon.Extensions.S3.Encryption.CommitmentPolicy.ForbidEncryptAllowDecrypt ? ContentEncryptionAlgorithm.AesGcm : ContentEncryptionAlgorithm.AesGcmWithCommitment;
logger.LogInformation("[NET-V2-Transitional] Created commitmentPolicy= {commitmentPolicy}", commitmentPolicy);
logger.LogInformation("[NET-V2-Transitional] Created encryptionAlgorithm= {encryptionAlgorithm}", encryptionAlgorithm);

var configuration = new AmazonS3CryptoConfigurationV2(securityProfile, commitmentPolicy, encryptionAlgorithm);
if (request.Config.InstructionFileConfig?.EnableInstructionFilePutObject == true)
{
configuration.StorageMode = CryptoStorageMode.InstructionFile;
logger.LogInformation("[NET-V2-Transitional] Created StorageMode= InstructionFile");
}
// Create S3 encryption client
var encryptionClient = new AmazonS3EncryptionClientV2(configuration, encryptionMaterial);
// Add to cache and return client ID
var clientId = clientCacheService.AddClient(encryptionClient);
var response = new ClientResponse { ClientId = clientId };

logger.LogInformation("[NET-V2-Transitional] Created S3EC client with ID: {clientId}", clientId);

return new ContentResult
{
Content = JsonSerializer.Serialize(response),
ContentType = "application/json",
StatusCode = 200
};
}
catch (Exception ex)
{
logger.LogError(ex, "[NET-V2-Transitional] Failed to create S3EC client");
return StatusCode(500, new S3EncryptionClientError
{
Message = $"Failed to create client: {ex.Message}"
});
}
}

private static Amazon.Extensions.S3.Encryption.CommitmentPolicy MapCommitmentPolicy(Models.CommitmentPolicy? policy)
{
return policy switch
{
Models.CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT => Amazon.Extensions.S3.Encryption.CommitmentPolicy.RequireEncryptRequireDecrypt,
Models.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT => Amazon.Extensions.S3.Encryption.CommitmentPolicy.RequireEncryptAllowDecrypt,
Models.CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT => Amazon.Extensions.S3.Encryption.CommitmentPolicy.ForbidEncryptAllowDecrypt,
_ => Amazon.Extensions.S3.Encryption.CommitmentPolicy.ForbidEncryptAllowDecrypt
};
}

// This is redundant but useful when tests starts sending EncryptionAlgorithm
private static ContentEncryptionAlgorithm MapEncryptionAlgorithm(Models.EncryptionAlgorithm? algorithm)
{
return algorithm switch
{
Models.EncryptionAlgorithm.ALG_AES_256_GCM_IV12_TAG16_NO_KDF => ContentEncryptionAlgorithm.AesGcm,
Models.EncryptionAlgorithm.ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY => ContentEncryptionAlgorithm.AesGcmWithCommitment,
_ => ContentEncryptionAlgorithm.AesGcm
};
}
}
105 changes: 105 additions & 0 deletions test-server/net-v2-transition-server/Controllers/ObjectController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System.Text.Json;
using Amazon.S3.Model;
using Microsoft.AspNetCore.Mvc;
using NetV2TransitionServer.Models;
using NetV2TransitionServer.Services;

namespace NetV2TransitionServer.Controllers;

[ApiController]
[Route("[controller]")]
public class ObjectController(IClientCacheService clientCacheService, ILogger<ObjectController> logger) : ControllerBase
{
[HttpPut("{bucket}/{key}")]
public async Task<IActionResult> PutObject(string bucket, string key)
{
logger.LogInformation("Starting PutObject");
var clientId = Request.Headers["clientId"].FirstOrDefault();
if (string.IsNullOrEmpty(clientId))
return BadRequest(new GenericServerError { Message = "[NET-V2-Transitional] ClientID header is required" });

var client = clientCacheService.GetClient(clientId);
if (client == null)
return NotFound(new GenericServerError { Message = $"[NET-V2-Transitional] No client found for ClientID: {clientId}" });

try
{
// Read raw body data
using var memoryStream = new MemoryStream();
// Request is the HTTP request this method is currently handling
await Request.Body.CopyToAsync(memoryStream);
var bodyBytes = memoryStream.ToArray();

// Create put request
var putRequest = new PutObjectRequest
{
BucketName = bucket,
Key = key,
InputStream = new MemoryStream(bodyBytes)
};

await client.PutObjectAsync(putRequest);

var response = new { bucket, key };

logger.LogInformation(
"[NET-V2-Transitional] Put object succeeded for bucket={bucket}, key={key} and clientId = {clientId}",
bucket, key, clientId);
return new ContentResult
{
Content = JsonSerializer.Serialize(response),
ContentType = "application/json",
StatusCode = 200
};
}
catch (Exception ex)
{
logger.LogError(ex, "[NET-V2-Transitional] Failed to put object from S3 for bucket={bucket}, key={key}", bucket, key);
return StatusCode(500, new S3EncryptionClientError { Message = $"[NET-V2-Transitional] Failed to put object: {ex.Message}" });
}
}

[HttpGet("{bucket}/{key}")]
public async Task<IActionResult> GetObject(string bucket, string key)
{
logger.LogInformation("Starting GetObject");
var clientId = Request.Headers["clientId"].FirstOrDefault();
if (string.IsNullOrEmpty(clientId))
return BadRequest(new GenericServerError { Message = "[NET-V2-Transitional] ClientID header is required" });

var client = clientCacheService.GetClient(clientId);
if (client == null)
return NotFound(new GenericServerError { Message = $"[NET-V2-Transitional] No client found for ClientID: {clientId}" });

try
{
var getRequest = new GetObjectRequest
{
BucketName = bucket,
Key = key
};
var response = await client.GetObjectAsync(getRequest);
logger.LogInformation("[NET-V2-Transitional] Got object from S3 for bucket={bucket}, key={key}", bucket, key);
// Read response body
using var memoryStream = new MemoryStream();
await response.ResponseStream.CopyToAsync(memoryStream);
var bodyBytes = memoryStream.ToArray();

// Convert metadata to content-metadata header format
var metadataList = response.Metadata.Keys
.Select(metaDataKey => $"{metaDataKey}={response.Metadata[metaDataKey]}")
.ToList();
var metadataStr = string.Join(",", metadataList);

// Set response headers
Response.Headers["Content-Metadata"] = metadataStr;

return File(bodyBytes, "application/octet-stream");
}
catch (Exception ex)
{
logger.LogError(ex, "[NET-V2-Transitional] Failed to get object from S3 for bucket={bucket}, key={key}", bucket, key);
return StatusCode(500, new S3EncryptionClientError { Message = ex.Message });
}
}
}
Loading
Loading