Skip to content

Commit 9e29983

Browse files
authored
feat: support hardware intrinsics (#159)
* feat: ShuffleState and QuarterRound AVX2 and SSE3 and AdvSIMD * chore: copilot feedback * test: improve coverage * chore: remove coverage dependency on build cake * feat: add dotnet 10 and merge coverage from all platforms * fix: wycheproof tests * feat: improvements to code coverage for windows * fix: exclude unused AVX2 256-bit methods * feat: some minor improvements * feat: proper SSE2 and AdvSIMD optimizations for Poly1305 * chore: security improvements * chore: fix azure devops publish
1 parent 0a86aba commit 9e29983

38 files changed

+3332
-450
lines changed

.github/workflows/ci.yml

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ jobs:
7171
uses: actions/upload-artifact@v4
7272
with:
7373
name: ${{matrix.os}}
74-
path: ${{env.BUILD_ARTIFACT_PATH}}
74+
path: |
75+
${{env.BUILD_ARTIFACT_PATH}}
76+
!${{env.BUILD_ARTIFACT_PATH}}/**/In/**/*
7577
7678
coverage:
7779
name: 'Process Coverage'
@@ -80,25 +82,30 @@ jobs:
8082
steps:
8183
- name: 'Checkout'
8284
uses: actions/checkout@v4
83-
- name: 'Download Artifact'
85+
- name: 'Download All Artifacts'
8486
uses: actions/download-artifact@v4
8587
with:
86-
name: 'ubuntu-latest'
88+
path: ./artifacts
89+
pattern: '*-latest'
90+
merge-multiple: false
8791
- name: 'Install .NET SDK'
8892
uses: actions/setup-dotnet@v4
8993
with:
9094
dotnet-version: |
9195
6.0.x
9296
8.0.x
9397
9.0.x
98+
10.0.x
9499
- name: 'Install ReportGenerator'
95100
run: dotnet tool install -g dotnet-reportgenerator-globaltool
96-
- name: 'Generate Coverage Report'
97-
run: reportgenerator -reports:./TestResults/**/coverage.cobertura.xml -targetdir:${{env.BUILD_ARTIFACT_PATH}}/TestResults/Coverage/Reports "-reporttypes:HtmlInline;HTMLChart;Cobertura"
101+
- name: 'List Coverage Files'
102+
run: find ./artifacts -name "coverage.cobertura.xml" -type f
103+
- name: 'Generate Merged Coverage Report'
104+
run: reportgenerator -reports:"./artifacts/**/coverage.cobertura.xml" -targetdir:${{env.BUILD_ARTIFACT_PATH}}/TestResults/Coverage/Reports "-reporttypes:HtmlInline;HTMLChart;Cobertura"
98105
- name: 'Upload Coverage'
99106
uses: codecov/codecov-action@v4
100107
with:
101-
file: Cobertura.xml
108+
file: ${{env.BUILD_ARTIFACT_PATH}}/TestResults/Coverage/Reports/Cobertura.xml
102109
fail_ci_if_error: false
103110
token: ${{ secrets.CODECOV_TOKEN }}
104111
- name: 'Publish Coverage Report'

NaCl.Core.sln

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.27428.2002
3+
# Visual Studio Version 18
4+
VisualStudioVersion = 18.0.11205.157 d18.0
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NaCl.Core", "src\NaCl.Core\NaCl.Core.csproj", "{5B711EBA-6E41-429F-A2AC-719C4441B663}"
77
EndProject
@@ -11,8 +11,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NaCl.Core.Benchmarks", "tes
1111
EndProject
1212
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{28EF1FB3-A057-4C17-A3B2-B9370B234F81}"
1313
ProjectSection(SolutionItems) = preProject
14-
CodeCoverage.runsettings = CodeCoverage.runsettings
1514
build.cake = build.cake
15+
.github\workflows\ci.yml = .github\workflows\ci.yml
16+
CodeCoverage.runsettings = CodeCoverage.runsettings
1617
dotnet-tools.json = dotnet-tools.json
1718
global.json = global.json
1819
LICENSE = LICENSE

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ Daily NuGet builds of the project are also available in the [Azure Artifacts](ht
4848
#### Symmetric Key Encryption
4949

5050
```csharp
51-
// Create the primitive
52-
var aead = new ChaCha20Poly1305(key);
51+
// Create the primitive (implements IDisposable for secure key cleanup)
52+
using var aead = new ChaCha20Poly1305(key);
5353

5454
// Use the primitive to encrypt a plaintext
5555
aead.Encrypt(nonce, plaintext, ciphertext, tag, aad);
@@ -58,6 +58,8 @@ aead.Encrypt(nonce, plaintext, ciphertext, tag, aad);
5858
aead.Decrypt(nonce, ciphertext, tag, plaintext, aad);
5959
```
6060

61+
> **Note:** All cipher classes (`ChaCha20`, `XChaCha20`, `Salsa20`, `XSalsa20`, `ChaCha20Poly1305`, `XChaCha20Poly1305`) implement `IDisposable`. Call `Dispose()` or use `using` statements to securely zero the key from memory when done.
62+
6163
#### MAC (Message Authentication Code)
6264

6365
```csharp
@@ -77,6 +79,19 @@ Poly1305.VerifyMac(key, data, tag);
7779
- Includes the mandatory RFC [test vectors](https://github.com/daviddesmet/NaCl.Core/tree/master/test/NaCl.Core.Tests).
7880
- [Project Wycheproof](https://github.com/google/wycheproof) by members of Google Security Team, for testing against known attacks (when applicable).
7981

82+
## Performance
83+
84+
Refer to the [benchmarks](https://github.com/daviddesmet/NaCl.Core/tree/master/test/NaCl.Core.Benchmarks) for performance numbers.
85+
86+
Run the benchmarks using:
87+
```bash
88+
dotnet run -c Release --framework net9.0
89+
```
90+
91+
```bash
92+
dotnet run -c Release --framework net9.0 --filter "*ChaCha20IntrinsicsBenchmark*"
93+
```
94+
8095
## Learn More
8196

8297
[![License](https://img.shields.io/github/license/daviddesmet/NaCl.Core.svg)](https://github.com/daviddesmet/NaCl.Core/blob/master/LICENSE)

azure-pipelines.yml

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,19 @@ jobs:
5858
# The steps to run to execute the build.
5959
steps:
6060
- task: UseDotNet@2
61-
displayName: 'Use .NET SDK 9.0'
61+
displayName: 'Use .NET SDK 10.0'
6262
inputs:
6363
packageType: 'sdk'
64-
version: '9.0.x'
64+
version: '10.0.x'
65+
includePreviewVersions: true
6566
installationPath: $(Agent.ToolsDirectory)/dotnet
6667
- task: DotNetCoreCLI@2
6768
displayName: 'Build Project in $(BuildConfiguration) mode'
6869
inputs:
6970
command: 'build'
7071
projects: '**/NaCl.Core.csproj'
7172
arguments: '-c $(BuildConfiguration)'
72-
- script: dotnet test -f net9.0 test/NaCl.Core.Tests --logger trx /p:CollectCoverage=true /p:CoverletOutput=$(Build.SourcesDirectory)/TestResults/ /p:CoverletOutputFormat=cobertura /p:Exclude='[NaCl.Core.Tests]*'
73+
- script: dotnet test -f net10.0 test/NaCl.Core.Tests --logger trx /p:CollectCoverage=true /p:CoverletOutput=$(Build.SourcesDirectory)/TestResults/ /p:CoverletOutputFormat=cobertura /p:Exclude='[NaCl.Core.Tests]*'
7374
displayName: 'Unit testing'
7475
enabled: false
7576
- task: DotNetCoreCLI@2
@@ -79,14 +80,14 @@ jobs:
7980
projects: '**/NaCl.Core.Tests.csproj'
8081
arguments: '/p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:Include="[NaCl.*]*" /p:Exclude="[*Tests]*"'
8182
enabled: false
82-
- task: reportgenerator@4
83+
- task: reportgenerator@5
8384
displayName: 'Generate Coverage Report'
8485
inputs:
8586
reports: '$(Build.SourcesDirectory)/**/coverage.cobertura.xml'
8687
targetdir: '$(Build.ArtifactStagingDirectory)/TestResults/'
8788
reporttypes: 'HtmlInline_AzurePipelines_Dark;Cobertura;Badges'
8889
enabled: false
89-
- script: dotnet test -f net9.0 test/NaCl.Core.Tests --logger trx
90+
- script: dotnet test -f net10.0 test/NaCl.Core.Tests --logger trx
9091
displayName: 'Run Unit Tests'
9192
- task: PublishTestResults@2
9293
displayName: 'Publish Test Results'
@@ -108,7 +109,7 @@ jobs:
108109
displayName: 'Publish Library'
109110
inputs:
110111
command: 'publish'
111-
arguments: '-c $(BuildConfiguration) -f net9.0 -o $(Build.ArtifactStagingDirectory) --no-restore'
112+
arguments: '-c $(BuildConfiguration) -f net10.0 -o $(Build.ArtifactStagingDirectory) --no-restore'
112113
projects: src/NaCl.Core/NaCl.Core.csproj
113114
publishWebProjects: false
114115
modifyOutputPath: true
@@ -132,16 +133,17 @@ jobs:
132133
BuildConfiguration: 'release'
133134
steps:
134135
- task: UseDotNet@2
135-
displayName: 'Use .NET SDK 9.0'
136+
displayName: 'Use .NET SDK 10.0'
136137
inputs:
137138
packageType: 'sdk'
138-
version: '9.0.x'
139+
version: '10.0.x'
140+
includePreviewVersions: true
139141
installationPath: $(Agent.ToolsDirectory)/dotnet
140142
- script: dotnet restore
141143
displayName: 'Restore Project'
142144
- script: dotnet build -c $(BuildConfiguration) --no-restore
143145
displayName: 'Build Project in $(BuildConfiguration) mode'
144-
- script: dotnet test -f net9.0 test/NaCl.Core.Tests --logger trx
146+
- script: dotnet test -f net10.0 test/NaCl.Core.Tests --logger trx
145147
displayName: 'Run Unit Tests'
146148
- task: PublishTestResults@2
147149
displayName: 'Publish Test Results'
@@ -164,10 +166,11 @@ jobs:
164166
displayName: 'Authenticate with Azure Artifacts'
165167
condition: ne(variables['Build.Reason'], 'PullRequest')
166168
- task: UseDotNet@2
167-
displayName: 'Use .NET SDK 9.0'
169+
displayName: 'Use .NET SDK 10.0'
168170
inputs:
169171
packageType: 'sdk'
170-
version: '9.0.x'
172+
version: '10.0.x'
173+
includePreviewVersions: true
171174
- task: DotNetCoreCLI@2
172175
displayName: 'Build Project in $(BuildConfiguration) mode'
173176
inputs:
@@ -206,11 +209,13 @@ jobs:
206209
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg'
207210
nuGetFeedType: 'internal'
208211
publishVstsFeed: 'ffbd9fd0-ffca-4040-8576-0fa5065bd6d9/b465c74e-3671-458e-996f-8bbf45f957bc'
212+
allowPackageConflicts: true
213+
continueOnError: true
209214
- task: DotNetCoreCLI@2
210215
displayName: 'Publish Library'
211216
inputs:
212217
command: 'publish'
213-
arguments: '-c $(BuildConfiguration) -f net9.0 -o $(Build.ArtifactStagingDirectory) --no-restore'
218+
arguments: '-c $(BuildConfiguration) -f net10.0 -o $(Build.ArtifactStagingDirectory) --no-restore'
214219
projects: src/NaCl.Core/NaCl.Core.csproj
215220
publishWebProjects: false
216221
enabled: false

build.cake

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,57 @@ Task("Test")
4242
{
4343
Information($"Preparing {project.GetFilename()} for test");
4444

45-
DotNetTest(
46-
project.ToString(),
47-
new DotNetTestSettings()
45+
var settings = new DotNetTestSettings()
46+
{
47+
Blame = true,
48+
Collectors = new string[] { "XPlat Code Coverage" },
49+
Configuration = configuration,
50+
Loggers = new string[]
4851
{
49-
Blame = true,
50-
Collectors = new string[] { "XPlat Code Coverage" },
51-
Configuration = configuration,
52-
Loggers = new string[]
53-
{
54-
$"trx;LogFileName={project.GetFilenameWithoutExtension()}.trx",
55-
$"html;LogFileName={project.GetFilenameWithoutExtension()}.html",
56-
},
57-
NoBuild = true,
58-
NoRestore = true,
59-
ResultsDirectory = $"{artifactsDirectory}/TestResults",
60-
Settings = "CodeCoverage.runsettings"
61-
});
52+
$"trx;LogFileName={project.GetFilenameWithoutExtension()}.trx",
53+
$"html;LogFileName={project.GetFilenameWithoutExtension()}.html",
54+
},
55+
NoBuild = true,
56+
NoRestore = true,
57+
ResultsDirectory = $"{artifactsDirectory}/TestResults",
58+
Settings = "CodeCoverage.runsettings"
59+
};
60+
61+
// Platform-specific intrinsics testing
62+
if (IsRunningOnUnix())
63+
{
64+
// ARM/Mac testing with AdvSIMD
65+
settings.EnvironmentVariables["COMPlus_EnableAdvSimd"] = "1";
66+
settings.ResultsDirectory = $"{artifactsDirectory}/TestResults/AdvSimd";
67+
Information($"Running default {project.GetFilename()} test with ARM AdvSIMD enabled");
68+
DotNetTest(project.ToString(), settings);
69+
70+
settings.EnvironmentVariables["COMPlus_EnableAdvSimd"] = "0";
71+
settings.ResultsDirectory = $"{artifactsDirectory}/TestResults/Scalar";
72+
Information($"Running {project.GetFilename()} test with ARM AdvSIMD disabled (scalar only)");
73+
DotNetTest(project.ToString(), settings);
74+
}
75+
else
76+
{
77+
// x86/x64 testing with AVX2/SSE3
78+
settings.EnvironmentVariables["COMPlus_EnableAVX2"] = "1";
79+
settings.EnvironmentVariables["COMPlus_EnableSSE3"] = "1";
80+
settings.ResultsDirectory = $"{artifactsDirectory}/TestResults/Avx2";
81+
Information($"Running default {project.GetFilename()} test with SSE3 and AVX2 enabled");
82+
DotNetTest(project.ToString(), settings);
83+
84+
settings.EnvironmentVariables["COMPlus_EnableAVX2"] = "0";
85+
settings.EnvironmentVariables["COMPlus_EnableSSE3"] = "1";
86+
settings.ResultsDirectory = $"{artifactsDirectory}/TestResults/Sse3";
87+
Information($"Running {project.GetFilename()} test with SSE3 enabled and AVX2 disabled");
88+
DotNetTest(project.ToString(), settings);
89+
90+
settings.EnvironmentVariables["COMPlus_EnableAVX2"] = "0";
91+
settings.EnvironmentVariables["COMPlus_EnableSSE3"] = "0";
92+
settings.ResultsDirectory = $"{artifactsDirectory}/TestResults/Scalar";
93+
Information($"Running {project.GetFilename()} test with SSE3 and AVX2 disabled");
94+
DotNetTest(project.ToString(), settings);
95+
}
6296
});
6397

6498
Task("CoverageReport")
@@ -72,7 +106,7 @@ Task("CoverageReport")
72106
ArgumentCustomization = args => args.Append("-reporttypes:HtmlInline;HTMLChart;Cobertura")
73107
});
74108
});
75-
109+
76110
Task("Pack")
77111
.Description("Creates the NuGet packages and outputs them to the artifacts directory.")
78112
.Does(() =>

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "9.0.101",
3+
"version": "10.0.100",
44
"rollForward": "latestFeature"
55
}
66
}

0 commit comments

Comments
 (0)