Skip to content

Commit a10e91b

Browse files
committed
Merge branch 'main' into release-0.x
2 parents 84961e0 + 701e8f6 commit a10e91b

File tree

62 files changed

+1259
-760
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1259
-760
lines changed

CLAUDE.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with the Asherah encryption SDK.
4+
5+
## Project Overview
6+
Asherah is an application-layer encryption SDK providing envelope encryption with hierarchical key management, secure memory handling, and defense in depth against compromise. It supports multiple languages with pluggable KMS and metastore backends.
7+
8+
## Build Commands
9+
- C#: `cd csharp/<Project> && ./scripts/build.sh` or `dotnet build`
10+
- Go: `cd go/<module> && ./scripts/build.sh` or `go build ./...`
11+
- Java: `cd java/<module> && ./scripts/build.sh` or `mvn compile`
12+
- Server: `cd server/<language> && ./scripts/build.sh`
13+
14+
## Test Commands
15+
- Full test suites: `./scripts/test.sh` (in respective language directories)
16+
- Integration tests: `./scripts/integration_test.sh` (where available)
17+
- C# single test: `dotnet test --filter "FullyQualifiedName=Namespace.ClassName.MethodName"`
18+
- Go single test: `go test -run TestName ./path/to/package`
19+
- Java single test: `mvn -Dtest=TestClassName#methodName test`
20+
- Cross-language tests: `cd tests/cross-language && ./scripts/encrypt_all.sh && ./scripts/decrypt_all.sh`
21+
22+
## Lint Commands
23+
- C#: `dotnet format` for formatting, built-in analyzers
24+
- Go: `./scripts/lint.sh` (installs and runs golangci-lint v1.59.0)
25+
- Java: Checkstyle via Maven (configured in pom.xml)
26+
27+
## Environment Setup
28+
Common test environment variables:
29+
```bash
30+
export AWS_ACCESS_KEY_ID=dummykey
31+
export AWS_SECRET_ACCESS_KEY=dummysecret
32+
export AWS_DEFAULT_REGION=us-west-2
33+
export MYSQL_HOSTNAME=localhost
34+
export DYNAMODB_HOSTNAME=localhost
35+
```
36+
37+
## Key Components
38+
- **AppEncryption**: Core encryption libraries (C#, Go, Java)
39+
- **SecureMemory**: Protected memory allocation (C#, Go, Java)
40+
- **Server**: gRPC service layer (Go, Java)
41+
- **Samples**: Reference implementations and AWS deployment examples
42+
43+
## Testing Infrastructure
44+
- Uses TestContainers for integration testing (DynamoDB, MySQL)
45+
- Docker Compose files for local development (`samples/` directory)
46+
- Cross-language compatibility tests verify encrypt/decrypt operations work across all SDKs
47+
48+
## Style Guidelines
49+
- C#: Microsoft guidelines, 120 char line limit, prefer method overloading over optional args
50+
- Go: Standard idioms, proper error handling with error propagation
51+
- Java: Google's style guide with Checkstyle enforcement
52+
53+
## Security Patterns
54+
- Envelope encryption with hierarchical key model (System Key → Intermediate Key → Data Encryption Key)
55+
- Protected memory handling for sensitive data (off-heap, secure wiping)
56+
- Session-based encryption with partition isolation
57+
- KMS integration for root key management (AWS KMS, Static keys for testing)
58+
- Pluggable metastore backends (DynamoDB, RDBMS, In-Memory)

csharp/AppEncryption/AppEncryption.IntegrationTests/AppEncryption.IntegrationTests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,18 @@
99
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
1010
</PropertyGroup>
1111
<ItemGroup Label="Package References">
12-
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.1.6" />
12+
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.2.1" />
1313
<PackageReference Include="coverlet.msbuild" Version="6.0.4">
1414
<PrivateAssets>all</PrivateAssets>
1515
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1616
</PackageReference>
1717
<PackageReference Include="MySql.Data" Version="9.4.0" />
1818
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
19-
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.7" />
19+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.8" />
2020
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
2121
<PackageReference Include="Moq" Version="4.20.72" />
2222
<PackageReference Include="xunit" Version="2.9.3" />
23-
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
23+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
2424
<PrivateAssets>all</PrivateAssets>
2525
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2626
</PackageReference>

csharp/AppEncryption/AppEncryption.Tests/AppEncryption.Tests.csproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@
99
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
1010
</PropertyGroup>
1111
<ItemGroup Label="Package References">
12-
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.1.6" />
12+
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.2.1" />
1313
<PackageReference Include="JunitXml.TestLogger" Version="6.1.0" />
1414
<PackageReference Include="coverlet.msbuild" Version="6.0.4">
1515
<PrivateAssets>all</PrivateAssets>
1616
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1717
</PackageReference>
18-
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.7" />
18+
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.8" />
1919
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
2020
<PackageReference Include="Moq" Version="4.20.72" />
2121
<PackageReference Include="MySql.Data" Version="9.4.0" />
2222
<PackageReference Include="System.Net.Http" Version="4.3.4" />
2323
<PackageReference Include="System.Net.Security" Version="4.3.2" />
2424
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
25-
<PackageReference Include="Testcontainers.DynamoDb" Version="4.6.0" />
26-
<PackageReference Include="Testcontainers.MySql" Version="4.6.0" />
25+
<PackageReference Include="Testcontainers.DynamoDb" Version="4.7.0" />
26+
<PackageReference Include="Testcontainers.MySql" Version="4.7.0" />
2727
<PackageReference Include="xunit" Version="2.9.3" />
28-
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
28+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
2929
<PrivateAssets>all</PrivateAssets>
3030
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3131
</PackageReference>

csharp/AppEncryption/AppEncryption.Tests/AppEncryption/Persistence/DynamoDbMetastoreImplTest.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,23 @@ public void TestStoreWithSuffixSuccess()
266266
Assert.True(actualValue);
267267
}
268268

269+
[Fact]
270+
public void TestStoreWithClientProvidedExternally()
271+
{
272+
var client = new AmazonDynamoDBClient(new AmazonDynamoDBConfig
273+
{
274+
ServiceURL = serviceUrl,
275+
AuthenticationRegion = Region,
276+
});
277+
278+
var dbMetastoreImpl = NewBuilder(Region)
279+
.WithDynamoDbClient(client)
280+
.Build();
281+
bool actualValue = dbMetastoreImpl.Store(TestKey, DateTimeOffset.Now, JObject.FromObject(keyRecord));
282+
283+
Assert.True(actualValue);
284+
}
285+
269286
[Fact]
270287
public void TestStoreWithDbErrorShouldThrowException()
271288
{

csharp/AppEncryption/AppEncryption/AppEncryption.csproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@
2424
<!-- End of Properties related to NuGet packaging: -->
2525
</PropertyGroup>
2626
<ItemGroup Label="Package References">
27-
<PackageReference Include="AWSSDK.DynamoDBv2" Version="4.0.3.1" />
28-
<PackageReference Include="AWSSDK.KeyManagementService" Version="4.0.3.9" />
27+
<PackageReference Include="AWSSDK.DynamoDBv2" Version="4.0.6.1" />
28+
<PackageReference Include="AWSSDK.KeyManagementService" Version="4.0.4.1" />
2929
<PackageReference Include="LanguageExt.Core" Version="4.4.9" />
30-
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.7" />
31-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
30+
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.8" />
31+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.8" />
3232
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
3333
<PackageReference Include="App.Metrics" Version="4.3.0" />
34-
<PackageReference Include="System.Text.Encodings.Web" Version="9.0.7" />
35-
<PackageReference Include="System.Text.Json" Version="9.0.7" />
34+
<PackageReference Include="System.Text.Encodings.Web" Version="9.0.8" />
35+
<PackageReference Include="System.Text.Json" Version="9.0.8" />
3636
</ItemGroup>
3737
<ItemGroup Label="Project References">
3838
<ProjectReference Include="../Crypto/Crypto.csproj" />

csharp/AppEncryption/AppEncryption/Persistence/DynamoDbMetastoreImpl.cs

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public class DynamoDbMetastoreImpl : IMetastore<JObject>
4343
private readonly string preferredRegion;
4444
private readonly Table table;
4545

46+
4647
internal DynamoDbMetastoreImpl(Builder builder)
4748
{
4849
DbClient = builder.DbClient;
@@ -51,6 +52,7 @@ internal DynamoDbMetastoreImpl(Builder builder)
5152
hasKeySuffix = builder.HasKeySuffix;
5253
this._logger = builder.Logger;
5354

55+
5456
// Note this results in a network call. For now, cleaner than refactoring w/ thread-safe lazy loading
5557
table = builder.LoadTable(DbClient, TableName);
5658
}
@@ -83,6 +85,8 @@ public interface IBuildStep
8385

8486
/// <summary>
8587
/// Adds Endpoint config to the AWS DynamoDb client.
88+
/// Note: This method will be ignored if <see cref="WithRegion"/> has already been called.
89+
/// The first method called between <see cref="WithRegion"/> and <see cref="WithEndPointConfiguration"/> takes precedence.
8690
/// </summary>
8791
///
8892
/// <param name="endPoint">the service endpoint either with or without the protocol.</param>
@@ -93,6 +97,8 @@ public interface IBuildStep
9397
/// <summary>
9498
/// Specifies the region for the AWS DynamoDb client. If this is not specified, then the region from
9599
/// <see cref="DynamoDbMetastoreImpl.NewBuilder"/> is used.
100+
/// Note: This method will be ignored if <see cref="WithEndPointConfiguration"/> has already been called.
101+
/// The first method called between <see cref="WithRegion"/> and <see cref="WithEndPointConfiguration"/> takes precedence.
96102
/// </summary>
97103
///
98104
/// <param name="region">The region for the DynamoDb client.</param>
@@ -107,6 +113,17 @@ public interface IBuildStep
107113
/// <returns>The current <see cref="IBuildStep"/> instance.</returns>
108114
IBuildStep WithLogger(ILogger logger);
109115

116+
/// <summary>
117+
/// Provides a custom DynamoDB client. When this is used, the credentials, endpoint, and region
118+
/// configurations will be ignored.
119+
/// Note: This method completely bypasses all other client configuration methods.
120+
/// This is the recommended approach, especially when using dependency injection frameworks.
121+
/// </summary>
122+
///
123+
/// <param name="client">The custom DynamoDB client to use.</param>
124+
/// <returns>The current <see cref="IBuildStep"/> instance.</returns>
125+
IBuildStep WithDynamoDbClient(IAmazonDynamoDB client);
126+
110127
/// <summary>
111128
/// Builds the finalized <see cref="DynamoDbMetastoreImpl"/> with the parameters specified in the builder.
112129
/// </summary>
@@ -274,26 +291,31 @@ public string GetKeySuffix()
274291
return DefaultKeySuffix;
275292
}
276293

294+
295+
277296
/// <summary>
278297
/// Builder class to create an instance of the <see cref="DynamoDbMetastoreImpl"/> class.
279298
/// </summary>
280-
public class Builder : IBuildStep, IDisposable
299+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Builder intentionally should not manage client lifecycle")]
300+
public class Builder : IBuildStep
281301
{
282302
private readonly string preferredRegion;
283-
private AmazonDynamoDBClient dbClient;
303+
private IAmazonDynamoDB dbClient;
284304
private bool hasKeySuffix;
285305
private string tableName = DefaultTableName;
286306
private AWSCredentials credentials;
287307
private ILogger _logger;
288308

309+
289310
// Internal properties for access
290311
internal string PreferredRegion => preferredRegion;
291-
internal AmazonDynamoDBClient DbClient => dbClient;
312+
internal IAmazonDynamoDB DbClient => dbClient;
292313
internal bool HasKeySuffix => hasKeySuffix;
293314
internal string TableName => tableName;
294315
internal AWSCredentials Credentials => credentials;
295316
internal ILogger Logger => _logger;
296317

318+
297319
private const string DefaultTableName = "EncryptionKey";
298320
private readonly AmazonDynamoDBConfig dbConfig = new AmazonDynamoDBConfig();
299321
private bool hasEndPoint;
@@ -366,6 +388,21 @@ public IBuildStep WithLogger(ILogger logger)
366388
return this;
367389
}
368390

391+
/// <summary>
392+
/// Provides a custom DynamoDB client. When this is used, the credentials, endpoint, and region
393+
/// configurations will be ignored.
394+
/// Note: This method completely bypasses all other client configuration methods.
395+
/// This is the recommended approach, especially when using dependency injection frameworks.
396+
/// </summary>
397+
///
398+
/// <param name="client">The custom DynamoDB client to use.</param>
399+
/// <returns>The current <see cref="IBuildStep"/> instance.</returns>
400+
public IBuildStep WithDynamoDbClient(IAmazonDynamoDB client)
401+
{
402+
this.dbClient = client;
403+
return this;
404+
}
405+
369406
/// <summary>
370407
/// Builds the finalized <see cref="DynamoDbMetastoreImpl"/> object with the parameters specified in the
371408
/// <see cref="Builder"/>.
@@ -374,12 +411,15 @@ public IBuildStep WithLogger(ILogger logger)
374411
/// <returns>The fully instantiated <see cref="DynamoDbMetastoreImpl"/> object.</returns>
375412
public DynamoDbMetastoreImpl Build()
376413
{
377-
if (!hasEndPoint && !hasRegion)
414+
if (dbClient == null)
378415
{
379-
dbConfig.RegionEndpoint = RegionEndpoint.GetBySystemName(preferredRegion);
380-
}
416+
if (!hasEndPoint && !hasRegion)
417+
{
418+
dbConfig.RegionEndpoint = RegionEndpoint.GetBySystemName(preferredRegion);
419+
}
381420

382-
dbClient = new AmazonDynamoDBClient(credentials, dbConfig);
421+
dbClient = new AmazonDynamoDBClient(credentials, dbConfig);
422+
}
383423

384424
return new DynamoDbMetastoreImpl(this);
385425
}
@@ -392,26 +432,7 @@ internal virtual Table LoadTable(IAmazonDynamoDB client, string tableName)
392432
.Build();
393433
}
394434

395-
/// <summary>
396-
/// Disposes of the managed resources.
397-
/// </summary>
398-
public void Dispose()
399-
{
400-
Dispose(true);
401-
GC.SuppressFinalize(this);
402-
}
403435

404-
/// <summary>
405-
/// Disposes of the managed resources.
406-
/// </summary>
407-
/// <param name="disposing">True if called from Dispose, false if called from finalizer.</param>
408-
protected virtual void Dispose(bool disposing)
409-
{
410-
if (disposing)
411-
{
412-
dbClient?.Dispose();
413-
}
414-
}
415436
}
416437
}
417438
}

csharp/AppEncryption/Crypto/Crypto.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<ItemGroup Label="Package References">
2424
<PackageReference Include="BouncyCastle.NetCore" Version="2.2.1" />
2525
<PackageReference Include="GoDaddy.Asherah.SecureMemory" Version="0.4.0" />
26-
<PackageReference Include="System.Text.Encodings.Web" Version="9.0.7" />
27-
<PackageReference Include="System.Text.Json" Version="9.0.7" />
26+
<PackageReference Include="System.Text.Encodings.Web" Version="9.0.8" />
27+
<PackageReference Include="System.Text.Json" Version="9.0.8" />
2828
</ItemGroup>
2929
</Project>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>0.7.0</Version>
3+
<Version>0.8.0</Version>
44
</PropertyGroup>
55
</Project>

csharp/AppEncryption/README.md

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,10 @@ build the metastore by calling the `Build` method.
9595
- **WithKeySuffix**: Specifies whether key suffix should be enabled for DynamoDB. **This is required to enable Global
9696
Tables**.
9797
- **WithTableName**: Specifies the name of the DynamoDb table.
98-
- **WithRegion**: Specifies the region for the AWS DynamoDb client.
99-
- **WithEndPointConfiguration**: Adds an EndPoint configuration to the AWS DynamoDb client.
98+
- **WithRegion**: Specifies the region for the AWS DynamoDb client. *Will be ignored if WithEndPointConfiguration was called first*.
99+
- **WithEndPointConfiguration**: Adds an EndPoint configuration to the AWS DynamoDb client. *Will be ignored if WithRegion was called first*.
100100
- **WithCredentials**: Specifies custom credentials for the AWS DynamoDb client.
101+
- **WithDynamoDbClient**: **Recommended** - Provides a custom DynamoDB client. This method is preferred over letting the builder create the client, especially when using dependency injection frameworks. It gives you full control over client configuration and lifecycle management. *Completely bypasses all other client configuration methods*.
101102

102103
Below is an example of a DynamoDB metastore that uses a Global Table named `TestTable`
103104

@@ -112,6 +113,49 @@ IMetastore<JObject> dynamoDbMetastore = DynamoDbMetastoreImpl.NewBuilder("us-wes
112113
.Build();
113114
```
114115

116+
**Recommended: Using WithDynamoDbClient with Dependency Injection and AWSSDK.Extensions.NETCore.Setup**
117+
118+
```c#
119+
// In your DI container setup (e.g., Startup.cs, Program.cs)
120+
services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
121+
services.AddAWSService<IAmazonDynamoDB>();
122+
123+
// In your service or controller
124+
public class AsherahHelper(IAmazonDynamoDB dynamoDbClient, AWSOptions awsOptions)
125+
{
126+
public SessionFactory BuildSessionFactory()
127+
{
128+
var preferredRegion = awsOptions.Region.SystemName;
129+
var metastore = DynamoDbMetastoreImpl.NewBuilder(preferredRegion)
130+
.WithDynamoDbClient(dynamoDbClient) // Use the injected client
131+
.WithKeySuffix()
132+
.WithTableName("TestTable")
133+
.Build();
134+
135+
// continue building factory here
136+
}
137+
}
138+
```
139+
140+
**Alternative: Manual Client Configuration**
141+
142+
```c#
143+
// Create a custom DynamoDB client with specific configuration
144+
var config = new AmazonDynamoDBConfig
145+
{
146+
RegionEndpoint = Amazon.RegionEndpoint.USWest2,
147+
Timeout = TimeSpan.FromSeconds(30)
148+
};
149+
var customClient = new AmazonDynamoDBClient(credentials, config);
150+
151+
// Use the custom client in the metastore
152+
IMetastore<JObject> dynamoDbMetastore = DynamoDbMetastoreImpl.NewBuilder("us-west-2")
153+
.WithDynamoDbClient(customClient)
154+
.WithKeySuffix()
155+
.WithTableName("TestTable")
156+
.Build();
157+
```
158+
115159
#### In-memory Metastore (FOR TESTING ONLY)
116160

117161
```c#

0 commit comments

Comments
 (0)