diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
new file mode 100644
index 0000000..f6f6de0
--- /dev/null
+++ b/.github/workflows/dotnet.yml
@@ -0,0 +1,28 @@
+name: .NET
+
+on:
+ push:
+ branches:
+ - 'main'
+ - 'release'
+ pull_request:
+ branches:
+ - '**'
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: 5.0.x
+ - name: Restore dependencies
+ run: dotnet restore
+ - name: Build
+ run: dotnet build --no-restore
+ - name: Test
+ run: dotnet test --no-build --verbosity normal
diff --git a/.gitignore b/.gitignore
index dfcfd56..c660ba9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,12 @@
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+*.swp
+*.*~
+project.lock.json
+.DS_Store
+*.pyc
+
# User-specific files
*.rsuser
*.suo
@@ -348,3 +354,16 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
+
+# Visual Studio Code
+.vscode
+
+# Jetbrains Rider
+bin/
+obj/
+/packages/
+riderModule.iml
+/_ReSharper.Caches/
+
+#nuget
+nuget.config
\ No newline at end of file
diff --git a/.idea/.idea.onixlabs-dotnet/.idea/.gitignore b/.idea/.idea.onixlabs-dotnet/.idea/.gitignore
new file mode 100644
index 0000000..ea8755a
--- /dev/null
+++ b/.idea/.idea.onixlabs-dotnet/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/projectSettingsUpdater.xml
+/.idea.onixlabs-dotnet.iml
+/modules.xml
+/contentModel.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/.idea.onixlabs-dotnet/.idea/encodings.xml b/.idea/.idea.onixlabs-dotnet/.idea/encodings.xml
new file mode 100644
index 0000000..df87cf9
--- /dev/null
+++ b/.idea/.idea.onixlabs-dotnet/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.onixlabs-dotnet/.idea/indexLayout.xml b/.idea/.idea.onixlabs-dotnet/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.onixlabs-dotnet/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.onixlabs-dotnet/.idea/vcs.xml b/.idea/.idea.onixlabs-dotnet/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/.idea.onixlabs-dotnet/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OnixLabs.Core.UnitTests/Base16Tests.cs b/OnixLabs.Core.UnitTests/Base16Tests.cs
new file mode 100644
index 0000000..b10101e
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/Base16Tests.cs
@@ -0,0 +1,54 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class Base16Tests
+ {
+ [Theory(DisplayName = "Base16_FromString should produce the expected Base-16 value.")]
+ [InlineData("31323334353637383930", "1234567890")]
+ [InlineData("4142434445464748494a4b4c4d4e4f505152535455565758595a", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("6162636465666768696a6b6c6d6e6f707172737475767778797a", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base16FromStringShouldProduceTheExpectedBase16Value(string expected, string value)
+ {
+ // Arrange
+ Base16 candidate = Base16.FromString(value);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base16_Parse should produce the expected plain text value.")]
+ [InlineData("1234567890", "31323334353637383930")]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "4142434445464748494a4b4c4d4e4f505152535455565758595a")]
+ [InlineData("abcdefghijklmnopqrstuvwxyz", "6162636465666768696a6b6c6d6e6f707172737475767778797a")]
+ public void Base16ParseShouldProduceTheExpectedPlainTextValue(string expected, string value)
+ {
+ // Arrange
+ Base16 candidate = Base16.Parse(value);
+
+ // Act
+ string actual = candidate.ToPlainTextString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/Base32Base32HexAlphabetTests.cs b/OnixLabs.Core.UnitTests/Base32Base32HexAlphabetTests.cs
new file mode 100644
index 0000000..92d44dc
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/Base32Base32HexAlphabetTests.cs
@@ -0,0 +1,70 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class Base32Base32HexAlphabetTests
+ {
+ [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")]
+ [InlineData("64P36D1L6ORJGE9G", "1234567890")]
+ [InlineData("85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8======", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8======", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base32FromStringWithPaddingShouldProduceTheExpectedBase32Value(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.FromString(value, Base32Alphabet.Base32Hex, true);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")]
+ [InlineData("64P36D1L6ORJGE9G", "1234567890")]
+ [InlineData("85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base32FromStringWithoutPaddingShouldProduceTheExpectedBase32Value(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.FromString(value, Base32Alphabet.Base32Hex, false);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base32_Parse should produce the expected plain text value.")]
+ [InlineData("1234567890", "64P36D1L6ORJGE9G")]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "85146H258P3KGIAA9D64QJIFA18L4KQKALB5EM2PB8")]
+ [InlineData("abcdefghijklmnopqrstuvwxyz", "C5H66P35CPJMGQBADDM6QRJFE1ON4SRKELR7EU3PF8")]
+ public void Base32ParseShouldProduceTheExpectedPlainTextValue(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.Parse(value, Base32Alphabet.Base32Hex);
+
+ // Act
+ string actual = candidate.ToPlainTextString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/Base32CrockfordAlphabetTests.cs b/OnixLabs.Core.UnitTests/Base32CrockfordAlphabetTests.cs
new file mode 100644
index 0000000..c65fa01
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/Base32CrockfordAlphabetTests.cs
@@ -0,0 +1,70 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class Base32CrockfordAlphabetTests
+ {
+ [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")]
+ [InlineData("64S36D1N6RVKGE9G", "1234567890")]
+ [InlineData("85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8======", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8======", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base32FromStringWithPaddingShouldProduceTheExpectedBase32Value(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.FromString(value, Base32Alphabet.Crockford, true);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")]
+ [InlineData("64S36D1N6RVKGE9G", "1234567890")]
+ [InlineData("85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base32FromStringWithoutPaddingShouldProduceTheExpectedBase32Value(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.FromString(value, Base32Alphabet.Crockford, false);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base32_Parse should produce the expected plain text value.")]
+ [InlineData("1234567890", "64S36D1N6RVKGE9G")]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "85146H258S3MGJAA9D64TKJFA18N4MTMANB5EP2SB8")]
+ [InlineData("abcdefghijklmnopqrstuvwxyz", "C5H66S35CSKPGTBADDP6TVKFE1RQ4WVMENV7EY3SF8")]
+ public void Base32ParseShouldProduceTheExpectedPlainTextValue(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.Parse(value, Base32Alphabet.Crockford);
+
+ // Act
+ string actual = candidate.ToPlainTextString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/Base32DefaultAlphabetTests.cs b/OnixLabs.Core.UnitTests/Base32DefaultAlphabetTests.cs
new file mode 100644
index 0000000..f34c412
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/Base32DefaultAlphabetTests.cs
@@ -0,0 +1,70 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class Base32DefaultAlphabetTests
+ {
+ [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")]
+ [InlineData("GEZDGNBVGY3TQOJQ", "1234567890")]
+ [InlineData("IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI======", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI======", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base32FromStringWithPaddingShouldProduceTheExpectedBase32Value(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.FromString(value, Base32Alphabet.Default, true);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")]
+ [InlineData("GEZDGNBVGY3TQOJQ", "1234567890")]
+ [InlineData("IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base32FromStringWithoutPaddingShouldProduceTheExpectedBase32Value(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.FromString(value, Base32Alphabet.Default, false);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base32_Parse should produce the expected plain text value.")]
+ [InlineData("1234567890", "GEZDGNBVGY3TQOJQ")]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLI")]
+ [InlineData("abcdefghijklmnopqrstuvwxyz", "MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43UOV3HO6DZPI")]
+ public void Base32ParseShouldProduceTheExpectedPlainTextValue(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.Parse(value, Base32Alphabet.Default);
+
+ // Act
+ string actual = candidate.ToPlainTextString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/Base32ZBase32AlphabetTests.cs b/OnixLabs.Core.UnitTests/Base32ZBase32AlphabetTests.cs
new file mode 100644
index 0000000..d729170
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/Base32ZBase32AlphabetTests.cs
@@ -0,0 +1,70 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class Base32ZBase32AlphabetTests
+ {
+ [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")]
+ [InlineData("gr3dgpbiga5uoqjo", "1234567890")]
+ [InlineData("efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me======", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe======", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base32FromStringWithPaddingShouldProduceTheExpectedBase32Value(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.FromString(value, Base32Alphabet.ZBase32, true);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base32_FromString without padding should produce the expected Base-32 value.")]
+ [InlineData("gr3dgpbiga5uoqjo", "1234567890")]
+ [InlineData("efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base32FromStringWithoutPaddingShouldProduceTheExpectedBase32Value(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.FromString(value, Base32Alphabet.ZBase32, false);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base32_Parse should produce the expected plain text value.")]
+ [InlineData("1234567890", "gr3dgpbiga5uoqjo")]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "efbrgtnfe3dwo1kkjpgr4u1xkbeirw4wkimfqsn3me")]
+ [InlineData("abcdefghijklmnopqrstuvwxyz", "cftgg3dfc3uso4mkppsg45uxqbazrh5wqi58q6d3xe")]
+ public void Base32ParseShouldProduceTheExpectedPlainTextValue(string expected, string value)
+ {
+ // Arrange
+ Base32 candidate = Base32.Parse(value, Base32Alphabet.ZBase32);
+
+ // Act
+ string actual = candidate.ToPlainTextString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/Base58DefaultAlphabetTests.cs b/OnixLabs.Core.UnitTests/Base58DefaultAlphabetTests.cs
new file mode 100644
index 0000000..eb4e039
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/Base58DefaultAlphabetTests.cs
@@ -0,0 +1,54 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class Base58DefaultAlphabetTests
+ {
+ [Theory(DisplayName = "Base58_FromString should produce the expected Base-58 value.")]
+ [InlineData("3mJr7AoUCHxNqd", "1234567890")]
+ [InlineData("2zuFXTJSTRK6ESktqhM2QDBkCnH1U46CnxaD", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base58FromStringShouldProduceTheExpectedBase58Value(string expected, string value)
+ {
+ // Arrange
+ Base58 candidate = Base58.FromString(value, Base58Alphabet.Default);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base58_Parse should produce the expected plain text value.")]
+ [InlineData("1234567890", "3mJr7AoUCHxNqd")]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "2zuFXTJSTRK6ESktqhM2QDBkCnH1U46CnxaD")]
+ [InlineData("abcdefghijklmnopqrstuvwxyz", "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f")]
+ public void Base58ParseShouldProduceTheExpectedPlainTextValue(string expected, string value)
+ {
+ // Arrange
+ Base58 candidate = Base58.Parse(value, Base58Alphabet.Default);
+
+ // Act
+ string actual = candidate.ToPlainTextString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/Base58FlickrAlphabetTests.cs b/OnixLabs.Core.UnitTests/Base58FlickrAlphabetTests.cs
new file mode 100644
index 0000000..de52d7c
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/Base58FlickrAlphabetTests.cs
@@ -0,0 +1,54 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class Base58FlickrAlphabetTests
+ {
+ [Theory(DisplayName = "Base58_FromString should produce the expected Base-58 value.")]
+ [InlineData("3LiR7aNtchXnQC", "1234567890")]
+ [InlineData("2ZUfwsirsqj6erKTQGm2pdbKcMh1t46cMXzd", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("3YXt3U1HFx8vKFTJj92EAipcC4byHHs1V25E", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base58FromStringShouldProduceTheExpectedBase58Value(string expected, string value)
+ {
+ // Arrange
+ Base58 candidate = Base58.FromString(value, Base58Alphabet.Flickr);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base58_Parse should produce the expected plain text value.")]
+ [InlineData("1234567890", "3LiR7aNtchXnQC")]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "2ZUfwsirsqj6erKTQGm2pdbKcMh1t46cMXzd")]
+ [InlineData("abcdefghijklmnopqrstuvwxyz", "3YXt3U1HFx8vKFTJj92EAipcC4byHHs1V25E")]
+ public void Base58ParseShouldProduceTheExpectedPlainTextValue(string expected, string value)
+ {
+ // Arrange
+ Base58 candidate = Base58.Parse(value, Base58Alphabet.Flickr);
+
+ // Act
+ string actual = candidate.ToPlainTextString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/Base58RippleAlphabetTests.cs b/OnixLabs.Core.UnitTests/Base58RippleAlphabetTests.cs
new file mode 100644
index 0000000..ce63c97
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/Base58RippleAlphabetTests.cs
@@ -0,0 +1,54 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class Base58RippleAlphabetTests
+ {
+ [Theory(DisplayName = "Base58_FromString should produce the expected Base-58 value.")]
+ [InlineData("smJifwo7UHx4qd", "1234567890")]
+ [InlineData("pzuEXTJSTRKaNSktq6MpQDBkU8Hr7haU8x2D", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("syx7sur5gY3WkgtjK9pCbJQUdhBZ55TrvpnC", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base58FromStringShouldProduceTheExpectedBase58Value(string expected, string value)
+ {
+ // Arrange
+ Base58 candidate = Base58.FromString(value, Base58Alphabet.Ripple);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base58_Parse should produce the expected plain text value.")]
+ [InlineData("1234567890", "smJifwo7UHx4qd")]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "pzuEXTJSTRKaNSktq6MpQDBkU8Hr7haU8x2D")]
+ [InlineData("abcdefghijklmnopqrstuvwxyz", "syx7sur5gY3WkgtjK9pCbJQUdhBZ55TrvpnC")]
+ public void Base58ParseShouldProduceTheExpectedPlainTextValue(string expected, string value)
+ {
+ // Arrange
+ Base58 candidate = Base58.Parse(value, Base58Alphabet.Ripple);
+
+ // Act
+ string actual = candidate.ToPlainTextString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/Base64Tests.cs b/OnixLabs.Core.UnitTests/Base64Tests.cs
new file mode 100644
index 0000000..acf6372
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/Base64Tests.cs
@@ -0,0 +1,54 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class Base64Tests
+ {
+ [Theory(DisplayName = "Base64_FromString should produce the expected Base-64 value.")]
+ [InlineData("MTIzNDU2Nzg5MA==", "1234567890")]
+ [InlineData("QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", "abcdefghijklmnopqrstuvwxyz")]
+ public void Base64FromStringShouldProduceTheExpectedBase64Value(string expected, string value)
+ {
+ // Arrange
+ Base64 candidate = Base64.FromString(value);
+
+ // Act
+ string actual = candidate.ToString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory(DisplayName = "Base64_Parse should produce the expected plain text value.")]
+ [InlineData("1234567890", "MTIzNDU2Nzg5MA==")]
+ [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=")]
+ [InlineData("abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=")]
+ public void Base64ParseShouldProduceTheExpectedPlainTextValue(string expected, string value)
+ {
+ // Arrange
+ Base64 candidate = Base64.Parse(value);
+
+ // Act
+ string actual = candidate.ToPlainTextString();
+
+ // Assert
+ Assert.Equal(expected, actual);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/EnumerationTests.cs b/OnixLabs.Core.UnitTests/EnumerationTests.cs
new file mode 100644
index 0000000..21ddc73
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/EnumerationTests.cs
@@ -0,0 +1,77 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Generic;
+using OnixLabs.Core.UnitTests.MockData;
+using Xunit;
+
+namespace OnixLabs.Core.UnitTests
+{
+ public sealed class EnumerationTests
+ {
+ [Fact(DisplayName = "Enumerations should be equal")]
+ public void EnumerationsShouldBeEqual()
+ {
+ // Arrange
+ Color a = Color.Red;
+ Color b = Color.Red;
+
+ // Assert
+ Assert.Equal(a, b);
+ }
+
+ [Fact(DisplayName = "Enumerations should not be equal")]
+ public void EnumerationsShouldNotBeEqual()
+ {
+ // Arrange
+ Color a = Color.Red;
+ Color b = Color.Blue;
+
+ // Assert
+ Assert.NotEqual(a, b);
+ }
+
+ [Fact(DisplayName = "Enumeration should return all enumeration instances")]
+ public void EnumerationsShouldReturnAllEnumerationInstances()
+ {
+ // Arrange
+ IEnumerable colors = Color.GetAll();
+
+ // Assert
+ Assert.Contains(colors, item => item == Color.Red);
+ Assert.Contains(colors, item => item == Color.Green);
+ Assert.Contains(colors, item => item == Color.Blue);
+ }
+
+ [Fact(DisplayName = "Enumeration_FromName should return the expected enumeration entry")]
+ public void EnumerationFromNameShouldReturnTheExpectedEnumerationEntry()
+ {
+ // Arrange
+ Color color = Color.FromName("Green");
+
+ // Assert
+ Assert.Equal(Color.Green, color);
+ }
+
+ [Fact(DisplayName = "Enumeration_FromValue should return the expected enumeration entry")]
+ public void EnumerationFromValueShouldReturnTheExpectedEnumerationEntry()
+ {
+ // Arrange
+ Color color = Color.FromValue(2);
+
+ // Assert
+ Assert.Equal(Color.Green, color);
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/MockData/Color.cs b/OnixLabs.Core.UnitTests/MockData/Color.cs
new file mode 100644
index 0000000..014b9da
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/MockData/Color.cs
@@ -0,0 +1,27 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Core.UnitTests.MockData
+{
+ public sealed class Color : Enumeration
+ {
+ public static readonly Color Red = new(1, nameof(Red));
+ public static readonly Color Green = new(2, nameof(Green));
+ public static readonly Color Blue = new(3, nameof(Blue));
+
+ private Color(int value, string name) : base(value, name)
+ {
+ }
+ }
+}
diff --git a/OnixLabs.Core.UnitTests/OnixLabs.Core.UnitTests.csproj b/OnixLabs.Core.UnitTests/OnixLabs.Core.UnitTests.csproj
new file mode 100644
index 0000000..67e2add
--- /dev/null
+++ b/OnixLabs.Core.UnitTests/OnixLabs.Core.UnitTests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net5.0
+
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/OnixLabs.Core/ArrayExtensions.cs b/OnixLabs.Core/ArrayExtensions.cs
new file mode 100644
index 0000000..bce2240
--- /dev/null
+++ b/OnixLabs.Core/ArrayExtensions.cs
@@ -0,0 +1,71 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.ComponentModel;
+
+namespace OnixLabs.Core
+{
+ ///
+ /// Provides extension methods for arrays.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static class ArrayExtensions
+ {
+ ///
+ /// Copies this array.
+ ///
+ /// The array to copy.
+ /// The underlying type of the array.
+ /// Returns an exact copy of this array.
+ public static T[] Copy(this T[] array)
+ {
+ return Copy(array, 0, array.Length);
+ }
+
+ ///
+ /// Copies this array.
+ ///
+ /// The array to copy.
+ /// The index of the array to begin copying from.
+ /// The number of elements of the array to copy.
+ /// The underlying type of the array.
+ /// Returns an exact copy of this array.
+ public static T[] Copy(this T[] array, int index, int count)
+ {
+ T[] result = new T[count];
+ Array.Copy(array, index, result, 0, count);
+
+ return result;
+ }
+
+ ///
+ /// Concatenates this array with another array.
+ ///
+ /// The source array to concatenate with the other array.
+ /// The other array to concatenate with the source array.
+ /// The underlying type of the array.
+ /// Returns this array concatenated with the other array.
+ public static T[] ConcatenateWith(this T[] array, T[] other)
+ {
+ int length = array.Length + other.Length;
+ T[] result = new T[length];
+
+ Array.Copy(array, 0, result, 0, array.Length);
+ Array.Copy(other, 0, result, array.Length, other.Length);
+
+ return result;
+ }
+ }
+}
diff --git a/OnixLabs.Core/Enumeration.Comparable.cs b/OnixLabs.Core/Enumeration.Comparable.cs
new file mode 100644
index 0000000..b3c7ba0
--- /dev/null
+++ b/OnixLabs.Core/Enumeration.Comparable.cs
@@ -0,0 +1,48 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace OnixLabs.Core
+{
+ ///
+ /// Represents the base class for implementing enumeration classes.
+ ///
+ public abstract partial class Enumeration : IComparable, IComparable
+ {
+ ///
+ /// Compares the current instance with another object of the same type and returns an integer that indicates
+ /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the
+ /// other object.
+ ///
+ /// An object to compare with this instance.
+ /// Returns a value that indicates the relative order of the objects being compared.
+ public virtual int CompareTo(object? obj)
+ {
+ return CompareTo(obj as T);
+ }
+
+ ///
+ /// Compares the current instance with another object of the same type and returns an integer that indicates
+ /// whether the current instance precedes, follows, or occurs in the same position in the sort order as the
+ /// other object.
+ ///
+ /// An object to compare with this instance.
+ /// Returns a value that indicates the relative order of the objects being compared.
+ public virtual int CompareTo(T? other)
+ {
+ return Value.CompareTo(other?.Value);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Enumeration.Equatable.cs b/OnixLabs.Core/Enumeration.Equatable.cs
new file mode 100644
index 0000000..c6b3103
--- /dev/null
+++ b/OnixLabs.Core/Enumeration.Equatable.cs
@@ -0,0 +1,79 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace OnixLabs.Core
+{
+ ///
+ /// Represents the base class for implementing enumeration classes.
+ ///
+ public abstract partial class Enumeration : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(Enumeration a, Enumeration b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(Enumeration a, Enumeration b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Indicates whether the current object is equal to another object of the same type.
+ ///
+ /// An object to compare with this object.
+ /// Returns true if the current object is equal to the other parameter; otherwise, false.
+ public virtual bool Equals(T? other)
+ {
+ return ReferenceEquals(this, other)
+ || other is not null
+ && other.GetType() == GetType()
+ && other.Value == Value
+ && other.Name == Name;
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return Equals(obj as T);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(GetType(), Name, Value);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Enumeration.From.cs b/OnixLabs.Core/Enumeration.From.cs
new file mode 100644
index 0000000..5897da8
--- /dev/null
+++ b/OnixLabs.Core/Enumeration.From.cs
@@ -0,0 +1,81 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+using OnixLabs.Core.Linq;
+
+namespace OnixLabs.Core
+{
+ ///
+ /// Represents the base class for implementing enumeration classes.
+ ///
+ public abstract partial class Enumeration
+ {
+ ///
+ /// Gets an enumeration entry for the specified name.
+ ///
+ /// The name of the enumeration entry.
+ /// Returns an enumeration entry for the specified name.
+ /// If a single enumeration entry for the specified name does not exist.
+ public static T FromName(string name)
+ {
+ IImmutableList results = GetAll()
+ .Where(entry => entry.Name == name)
+ .ToImmutableArray();
+
+ if (results.IsEmpty())
+ {
+ string type = typeof(T).Name;
+ throw new ArgumentException($"Enumeration entry for name '{name}' not found in {type}.");
+ }
+
+ if (!results.IsSingle())
+ {
+ string type = typeof(T).Name;
+ throw new ArgumentException($"Multiple enumeration entries for name '{name}' found in {type}.");
+ }
+
+ return results.Single();
+ }
+
+ ///
+ /// Gets an enumeration entry for the specified value.
+ ///
+ /// The value of the enumeration entry.
+ /// Returns an enumeration entry for the specified value.
+ /// If a single enumeration entry for the specified value does not exist.
+ public static T FromValue(int value)
+ {
+ IImmutableList results = GetAll()
+ .Where(entry => entry.Value == value)
+ .ToImmutableArray();
+
+ if (results.IsEmpty())
+ {
+ string type = typeof(T).Name;
+ throw new ArgumentException($"Enumeration entry for value '{value}' not found in {type}.");
+ }
+
+ if (!results.IsSingle())
+ {
+ string type = typeof(T).Name;
+ throw new ArgumentException($"Multiple enumeration entries for value '{value}' found in {type}.");
+ }
+
+ return results.Single();
+ }
+ }
+}
diff --git a/OnixLabs.Core/Enumeration.Get.cs b/OnixLabs.Core/Enumeration.Get.cs
new file mode 100644
index 0000000..a9a9e3b
--- /dev/null
+++ b/OnixLabs.Core/Enumeration.Get.cs
@@ -0,0 +1,73 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Immutable;
+using System.Linq;
+using System.Reflection;
+using OnixLabs.Core.Linq;
+
+namespace OnixLabs.Core
+{
+ ///
+ /// Represents the base class for implementing enumeration classes.
+ ///
+ public abstract partial class Enumeration
+ {
+ ///
+ /// Gets all of the enumeration entries for the current type.
+ ///
+ /// Returns all of the enumeration entries for the current type.
+ public static IImmutableList GetAll()
+ {
+ return typeof(T)
+ .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
+ .Select(field => field.GetValue(null))
+ .WhereInstanceOf()
+ .ToImmutableList();
+ }
+
+ ///
+ /// Gets all of the enumeration entries for the current type.
+ ///
+ /// Returns all of the enumeration entries for the current type.
+ public static IImmutableList<(int Value, string Name)> GetEntries()
+ {
+ return GetAll()
+ .Select(entry => (entry.Value, entry.Name))
+ .ToImmutableList();
+ }
+
+ ///
+ /// Gets all of the enumeration names for the current type.
+ ///
+ /// Returns all of the enumeration names for the current type.
+ public static IImmutableList GetNames()
+ {
+ return GetAll()
+ .Select(entry => entry.Name)
+ .ToImmutableList();
+ }
+
+ ///
+ /// Gets all of the enumeration values for the current type.
+ ///
+ /// Returns all of the enumeration values for the current type.
+ public static IImmutableList GetValues()
+ {
+ return GetAll()
+ .Select(entry => entry.Value)
+ .ToImmutableList();
+ }
+ }
+}
diff --git a/OnixLabs.Core/Enumeration.To.cs b/OnixLabs.Core/Enumeration.To.cs
new file mode 100644
index 0000000..284a0ef
--- /dev/null
+++ b/OnixLabs.Core/Enumeration.To.cs
@@ -0,0 +1,31 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Core
+{
+ ///
+ /// Represents the base class for implementing enumeration classes.
+ ///
+ public abstract partial class Enumeration
+ {
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+}
diff --git a/OnixLabs.Core/Enumeration.cs b/OnixLabs.Core/Enumeration.cs
new file mode 100644
index 0000000..3af4439
--- /dev/null
+++ b/OnixLabs.Core/Enumeration.cs
@@ -0,0 +1,44 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Core
+{
+ ///
+ /// Represents the base class for implementing enumeration classes.
+ ///
+ /// The underlying enumeration type.
+ public abstract partial class Enumeration where T : Enumeration
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The value of the enumeration entry.
+ /// The name of the enumeration entry.
+ protected Enumeration(int value, string name)
+ {
+ Value = value;
+ Name = name;
+ }
+
+ ///
+ /// Gets the name of the enumeration entry.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets the value of the enumeration entry.
+ ///
+ public int Value { get; }
+ }
+}
diff --git a/OnixLabs.Core/Linq/IEnumerableExtensions.cs b/OnixLabs.Core/Linq/IEnumerableExtensions.cs
new file mode 100644
index 0000000..f55b82b
--- /dev/null
+++ b/OnixLabs.Core/Linq/IEnumerableExtensions.cs
@@ -0,0 +1,173 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// ReSharper disable InconsistentNaming
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.ComponentModel;
+using System.Linq;
+
+namespace OnixLabs.Core.Linq
+{
+ ///
+ /// Provides LINQ-like extension methods for .
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static class IEnumerableExtensions
+ {
+ ///
+ /// Determines whether all elements of this are equal by a specified property.
+ ///
+ /// The on which to perform the operation.
+ /// The selector function which will be used to select each property from each element.
+ /// The underlying type of the .
+ /// The underlying type of each selected element.
+ /// Returns true if all selected element properties are equal; otherwise false.
+ public static bool AllEqualBy(
+ this IEnumerable enumerable,
+ Func selector)
+ {
+ IImmutableList elements = enumerable.ToImmutableList();
+
+ if (elements.IsEmpty())
+ {
+ return false;
+ }
+
+ if (elements.IsSingle())
+ {
+ return true;
+ }
+
+ TProperty first = selector(elements[0]);
+ return elements.All(element => Equals(first, selector(element)));
+ }
+
+ ///
+ /// Determines whether any elements of this are equal by a specified property.
+ ///
+ /// The on which to perform the operation.
+ /// The selector function which will be used to select each property from each element.
+ /// The underlying type of the .
+ /// The underlying type of each selected element.
+ /// Returns true if any selected element properties are equal; otherwise false.
+ public static bool AnyEqualBy(
+ this IEnumerable enumerable,
+ Func selector)
+ {
+ IImmutableList elements = enumerable.ToImmutableList();
+
+ if (elements.IsEmpty())
+ {
+ return false;
+ }
+
+ if (elements.IsSingle())
+ {
+ return true;
+ }
+
+ TProperty first = selector(elements[0]);
+ return elements.Any(element => Equals(first, selector(element)));
+ }
+
+ ///
+ /// Performs the specified for each element of this .
+ ///
+ /// The over which to iterate.
+ /// The to perform for each element.
+ /// The underlying type of the .
+ public static void ForEach(this IEnumerable enumerable, Action action)
+ {
+ foreach (T element in enumerable)
+ {
+ action(element);
+ }
+ }
+
+ ///
+ /// Determines whether this is empty.
+ ///
+ /// The on which to perform the operation.
+ /// he underlying type of the .
+ /// Returns true if the is empty; otherwise, false.
+ public static bool IsEmpty(this IEnumerable enumerable)
+ {
+ return !enumerable.Any();
+ }
+
+ ///
+ /// Determines whether this is not empty.
+ ///
+ /// The on which to perform the operation.
+ /// The underlying type of the .
+ /// Returns true if the is not empty; otherwise, false.
+ public static bool IsNotEmpty(this IEnumerable enumerable)
+ {
+ return enumerable.Any();
+ }
+
+ ///
+ /// Determines whether this contains a single element.
+ ///
+ /// The on which to perform the operation.
+ /// The underlying type of the .
+ /// Returns true if the contains a single element; otherwise, false.
+ public static bool IsSingle(this IEnumerable enumerable)
+ {
+ return enumerable.LongCount() == 1;
+ }
+
+ ///
+ /// Determines whether this contains an even number of elements.
+ ///
+ /// The on which to perform the operation.
+ /// The underlying type of the .
+ /// Returns true if the contains an even number of elements; otherwise, false.
+ public static bool IsEvenCount(this IEnumerable enumerable)
+ {
+ return enumerable.LongCount() % 2 == 0;
+ }
+
+ ///
+ /// Determines whether this contains an odd number of elements.
+ ///
+ /// The on which to perform the operation.
+ /// The underlying type of the .
+ /// Returns true if the contains an odd number of elements; otherwise, false.
+ public static bool IsOddCount(this IEnumerable enumerable)
+ {
+ return enumerable.LongCount() % 2 != 0;
+ }
+
+ ///
+ /// Filters this to only elements of the specified type.
+ ///
+ /// The on which to perform the operation.
+ /// The underlying type of the .
+ /// Returns a new containing only elements of the specified type.
+ public static IEnumerable WhereInstanceOf(this IEnumerable enumerable)
+ {
+ foreach (object? element in enumerable)
+ {
+ if (element is T elementOfType)
+ {
+ yield return elementOfType;
+ }
+ }
+ }
+ }
+}
diff --git a/OnixLabs.Core/OnixLabs.Core.csproj b/OnixLabs.Core/OnixLabs.Core.csproj
new file mode 100644
index 0000000..2d77c06
--- /dev/null
+++ b/OnixLabs.Core/OnixLabs.Core.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net5.0
+ 9
+ enable
+ true
+ OnixLabs.Core
+ ONIXLabs
+ ONIXLabs Core API for .NET
+ 1.0.0
+ en
+
+
+
+ true
+ bin\Debug\net5.0\OnixLabs.Core.xml
+
+
+
diff --git a/OnixLabs.Core/Text/Base16.Equatable.cs b/OnixLabs.Core/Text/Base16.Equatable.cs
new file mode 100644
index 0000000..022e921
--- /dev/null
+++ b/OnixLabs.Core/Text/Base16.Equatable.cs
@@ -0,0 +1,76 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-16 (hexadecimal) value.
+ ///
+ public readonly partial struct Base16 : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(Base16 a, Base16 b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(Base16 a, Base16 b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public bool Equals(Base16 other)
+ {
+ return other.value.SequenceEqual(value);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return obj is Base16 other && Equals(other);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(value);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base16.From.cs b/OnixLabs.Core/Text/Base16.From.cs
new file mode 100644
index 0000000..637589d
--- /dev/null
+++ b/OnixLabs.Core/Text/Base16.From.cs
@@ -0,0 +1,102 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-16 (hexadecimal) value.
+ ///
+ public readonly partial struct Base16
+ {
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base16 FromByteArray(byte[] value)
+ {
+ return new Base16(value);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base16 FromCharArray(char[] value)
+ {
+ return FromCharArray(value, Encoding.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base16 FromCharArray(char[] value, Encoding encoding)
+ {
+ byte[] bytes = encoding.GetBytes(value);
+ return FromByteArray(bytes);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base16 FromSpan(ReadOnlySpan value)
+ {
+ return FromSpan(value, Encoding.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base16 FromSpan(ReadOnlySpan value, Encoding encoding)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base16 FromString(string value)
+ {
+ return FromString(value, Encoding.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base16 FromString(string value, Encoding encoding)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base16.Parse.cs b/OnixLabs.Core/Text/Base16.Parse.cs
new file mode 100644
index 0000000..1ea6132
--- /dev/null
+++ b/OnixLabs.Core/Text/Base16.Parse.cs
@@ -0,0 +1,57 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-16 (hexadecimal) value.
+ ///
+ public readonly partial struct Base16
+ {
+ ///
+ /// Parses a Base-16 (hexadecimal) value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// Returns a new instance.
+ public static Base16 Parse(string value)
+ {
+ ReadOnlySpan characters = value.AsSpan();
+ return Parse(characters);
+ }
+
+ ///
+ /// Parses a Base-16 (hexadecimal) value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// Returns a new instance.
+ public static Base16 Parse(char[] value)
+ {
+ ReadOnlySpan characters = value.AsSpan();
+ return Parse(characters);
+ }
+
+ ///
+ /// Parses a Base-16 (hexadecimal) value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// Returns a new instance.
+ public static Base16 Parse(ReadOnlySpan value)
+ {
+ byte[] bytes = Convert.FromHexString(value);
+ return FromByteArray(bytes);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base16.To.cs b/OnixLabs.Core/Text/Base16.To.cs
new file mode 100644
index 0000000..73cb131
--- /dev/null
+++ b/OnixLabs.Core/Text/Base16.To.cs
@@ -0,0 +1,62 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-16 (hexadecimal) value.
+ ///
+ public readonly partial struct Base16
+ {
+ ///
+ /// Returns a array that represents the current object.
+ ///
+ /// Returns a array that represents the current object.
+ public byte[] ToByteArray()
+ {
+ return value.Copy();
+ }
+
+ ///
+ /// Returns a that represents the current object in plain text.
+ ///
+ /// Returns a that represents the current object in plain text.
+ public string ToPlainTextString()
+ {
+ return ToPlainTextString(Encoding.Default);
+ }
+
+ ///
+ /// Returns a that represents the current object in plain text.
+ ///
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a that represents the current object in plain text.
+ public string ToPlainTextString(Encoding encoding)
+ {
+ return encoding.GetString(value);
+ }
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Convert.ToHexString(value).ToLower();
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base16.cs b/OnixLabs.Core/Text/Base16.cs
new file mode 100644
index 0000000..7a66bff
--- /dev/null
+++ b/OnixLabs.Core/Text/Base16.cs
@@ -0,0 +1,36 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-16 (hexadecimal) value.
+ ///
+ public readonly partial struct Base16
+ {
+ ///
+ /// The underlying value.
+ ///
+ private readonly byte[] value;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The underlying value.
+ private Base16(byte[] value)
+ {
+ this.value = value;
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base32.Codec.cs b/OnixLabs.Core/Text/Base32.Codec.cs
new file mode 100644
index 0000000..d2c21e0
--- /dev/null
+++ b/OnixLabs.Core/Text/Base32.Codec.cs
@@ -0,0 +1,160 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-32 value.
+ ///
+ public readonly partial struct Base32
+ {
+ ///
+ /// The Base-32 input data size.
+ ///
+ private const int InputSize = 8;
+
+ ///
+ /// The Base-32 output data size.
+ ///
+ private const int OutputSize = 5;
+
+ ///
+ /// Encode a byte array into a Base-32 string.
+ ///
+ /// The value to encode.
+ /// The Base-32 alphabet to use for encoding.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a Base-32 encoded string.
+ private static string Encode(byte[] value, string alphabet, bool padding)
+ {
+ if (value.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ StringBuilder builder = new(value.Length * InputSize / OutputSize);
+
+ int inputPosition = 0;
+ int inputSubPosition = 0;
+ byte outputPosition = 0;
+ int outputSubPosition = 0;
+
+ while (inputPosition < value.Length)
+ {
+ int availableBits = Math.Min(InputSize - inputSubPosition, OutputSize - outputSubPosition);
+
+ outputPosition <<= availableBits;
+ outputPosition |= (byte) (value[inputPosition] >> (InputSize - (inputSubPosition + availableBits)));
+ inputSubPosition += availableBits;
+
+ if (inputSubPosition >= InputSize)
+ {
+ inputPosition++;
+ inputSubPosition = 0;
+ }
+
+ outputSubPosition += availableBits;
+
+ if (outputSubPosition < OutputSize) continue;
+
+ outputPosition &= 0x1F;
+ builder.Append(alphabet[outputPosition]);
+ outputSubPosition = 0;
+ }
+
+ if (outputSubPosition <= 0) return builder.ToString();
+
+ outputPosition <<= (OutputSize - outputSubPosition);
+ outputPosition &= 0x1F;
+ builder.Append(alphabet[outputPosition]);
+
+ while (padding && builder.Length % InputSize != 0)
+ {
+ builder.Append('=');
+ }
+
+ return builder.ToString();
+ }
+
+ ///
+ /// Decodes a Base-32 into a byte array.
+ ///
+ /// The value to decode.
+ /// The Base-32 alphabet to use for decoding.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a byte array.
+ /// If the Base-32 string format is invalid.
+ private static byte[] Decode(ReadOnlySpan value, string alphabet, bool padding)
+ {
+ if (value == string.Empty)
+ {
+ return Array.Empty();
+ }
+
+ if (padding && value.Length % InputSize != 0)
+ {
+ throw new FormatException("Base-23 string is invalid. Insufficient padding has been applied.");
+ }
+
+ ReadOnlySpan valueWithoutPadding = padding ? value.TrimEnd('=') : value;
+
+ byte[] outputBytes = new byte[valueWithoutPadding.Length * OutputSize / InputSize];
+
+ if (outputBytes.Length == 0)
+ {
+ throw new FormatException("Base-23 string is invalid. Not enough data to construct byte array.");
+ }
+
+ int inputPosition = 0;
+ int inputSubPosition = 0;
+ int outputPosition = 0;
+ int outputSubPosition = 0;
+
+ while (outputPosition < outputBytes.Length)
+ {
+ char character = valueWithoutPadding[inputPosition];
+ int index = alphabet.IndexOf(character);
+
+ if (index < 0)
+ {
+ throw new FormatException($"Invalid Base58 character '{character}' at position {inputPosition}");
+ }
+
+ int availableBits = Math.Min(OutputSize - inputSubPosition, InputSize - outputSubPosition);
+
+ outputBytes[outputPosition] <<= availableBits;
+ outputBytes[outputPosition] |= (byte) (index >> (OutputSize - (inputSubPosition + availableBits)));
+ outputSubPosition += availableBits;
+
+ if (outputSubPosition >= InputSize)
+ {
+ outputPosition++;
+ outputSubPosition = 0;
+ }
+
+ inputSubPosition += availableBits;
+
+ if (inputSubPosition < OutputSize) continue;
+
+ inputPosition++;
+ inputSubPosition = 0;
+ }
+
+ return outputBytes;
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base32.Equatable.cs b/OnixLabs.Core/Text/Base32.Equatable.cs
new file mode 100644
index 0000000..340427e
--- /dev/null
+++ b/OnixLabs.Core/Text/Base32.Equatable.cs
@@ -0,0 +1,78 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-32 value.
+ ///
+ public readonly partial struct Base32 : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(Base32 a, Base32 b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(Base32 a, Base32 b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public bool Equals(Base32 other)
+ {
+ return other.value.SequenceEqual(value)
+ && other.alphabet == alphabet
+ && other.padding == padding;
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return obj is Base32 other && Equals(other);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(value);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base32.From.cs b/OnixLabs.Core/Text/Base32.From.cs
new file mode 100644
index 0000000..b9a1125
--- /dev/null
+++ b/OnixLabs.Core/Text/Base32.From.cs
@@ -0,0 +1,353 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-32 value.
+ ///
+ public readonly partial struct Base32
+ {
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base32 FromByteArray(byte[] value)
+ {
+ return FromByteArray(value, Base32Alphabet.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromByteArray(byte[] value, Base32Alphabet alphabet)
+ {
+ return FromByteArray(value, alphabet, true);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromByteArray(byte[] value, bool padding)
+ {
+ return FromByteArray(value, Base32Alphabet.Default, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromByteArray(byte[] value, Base32Alphabet alphabet, bool padding)
+ {
+ return new Base32(value, alphabet, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base32 FromCharArray(char[] value)
+ {
+ return FromCharArray(value, Encoding.Default, Base32Alphabet.Default, true);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base32 FromCharArray(char[] value, Encoding encoding)
+ {
+ return FromCharArray(value, encoding, Base32Alphabet.Default, true);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromCharArray(char[] value, bool padding)
+ {
+ return FromCharArray(value, Encoding.Default, Base32Alphabet.Default, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromCharArray(char[] value, Encoding encoding, bool padding)
+ {
+ return FromCharArray(value, encoding, Base32Alphabet.Default, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromCharArray(char[] value, Base32Alphabet alphabet)
+ {
+ return FromCharArray(value, Encoding.Default, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromCharArray(char[] value, Encoding encoding, Base32Alphabet alphabet)
+ {
+ byte[] bytes = encoding.GetBytes(value);
+ return FromByteArray(bytes, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromCharArray(char[] value, Base32Alphabet alphabet, bool padding)
+ {
+ return FromCharArray(value, Encoding.Default, alphabet, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromCharArray(char[] value, Encoding encoding, Base32Alphabet alphabet, bool padding)
+ {
+ byte[] bytes = encoding.GetBytes(value);
+ return FromByteArray(bytes, alphabet, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base32 FromSpan(ReadOnlySpan value)
+ {
+ return FromSpan(value, Encoding.Default, Base32Alphabet.Default, true);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base32 FromSpan(ReadOnlySpan value, Encoding encoding)
+ {
+ return FromSpan(value, encoding, Base32Alphabet.Default, true);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromSpan(ReadOnlySpan value, bool padding)
+ {
+ return FromSpan(value, Encoding.Default, Base32Alphabet.Default, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromSpan(ReadOnlySpan value, Encoding encoding, bool padding)
+ {
+ return FromSpan(value, encoding, Base32Alphabet.Default, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromSpan(ReadOnlySpan value, Base32Alphabet alphabet)
+ {
+ return FromSpan(value, Encoding.Default, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromSpan(ReadOnlySpan value, Encoding encoding, Base32Alphabet alphabet)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromSpan(ReadOnlySpan value, Base32Alphabet alphabet, bool padding)
+ {
+ return FromSpan(value, Encoding.Default, alphabet, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromSpan(ReadOnlySpan value, Encoding encoding, Base32Alphabet alphabet,
+ bool padding)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding, alphabet, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base32 FromString(string value)
+ {
+ return FromString(value, Encoding.Default, Base32Alphabet.Default, true);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base32 FromString(string value, Encoding encoding)
+ {
+ return FromString(value, encoding, Base32Alphabet.Default, true);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromString(string value, bool padding)
+ {
+ return FromString(value, Encoding.Default, Base32Alphabet.Default, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromString(string value, Encoding encoding, bool padding)
+ {
+ return FromString(value, encoding, Base32Alphabet.Default, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromString(string value, Base32Alphabet alphabet)
+ {
+ return FromString(value, Encoding.Default, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromString(string value, Encoding encoding, Base32Alphabet alphabet)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromString(string value, Base32Alphabet alphabet, bool padding)
+ {
+ return FromString(value, Encoding.Default, alphabet, padding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base32 FromString(string value, Encoding encoding, Base32Alphabet alphabet, bool padding)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding, alphabet, padding);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base32.Parse.cs b/OnixLabs.Core/Text/Base32.Parse.cs
new file mode 100644
index 0000000..c037e57
--- /dev/null
+++ b/OnixLabs.Core/Text/Base32.Parse.cs
@@ -0,0 +1,91 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-32 value.
+ ///
+ public readonly partial struct Base32
+ {
+ ///
+ /// Parses a Base-32 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// A new instance.
+ public static Base32 Parse(string value)
+ {
+ return Parse(value, Base32Alphabet.Default);
+ }
+
+ ///
+ /// Parses a Base-32 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// A new instance.
+ public static Base32 Parse(string value, Base32Alphabet alphabet)
+ {
+ ReadOnlySpan characters = value.AsSpan();
+ return Parse(characters, alphabet);
+ }
+
+ ///
+ /// Parses a Base-32 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// A new instance.
+ public static Base32 Parse(char[] value)
+ {
+ return Parse(value, Base32Alphabet.Default);
+ }
+
+ ///
+ /// Parses a Base-32 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// A new instance.
+ public static Base32 Parse(char[] value, Base32Alphabet alphabet)
+ {
+ ReadOnlySpan characters = value.AsSpan();
+ return Parse(characters, alphabet);
+ }
+
+ ///
+ /// Parses a Base-32 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// A new instance.
+ public static Base32 Parse(ReadOnlySpan value)
+ {
+ return Parse(value, Base32Alphabet.Default);
+ }
+
+ ///
+ /// Parses a Base-32 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// A new instance.
+ public static Base32 Parse(ReadOnlySpan value, Base32Alphabet alphabet)
+ {
+ bool padding = value.Contains('=');
+ byte[] bytes = Decode(value, alphabet.Alphabet, padding);
+ return FromByteArray(bytes, padding);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base32.To.cs b/OnixLabs.Core/Text/Base32.To.cs
new file mode 100644
index 0000000..c449694
--- /dev/null
+++ b/OnixLabs.Core/Text/Base32.To.cs
@@ -0,0 +1,61 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-32 value.
+ ///
+ public readonly partial struct Base32
+ {
+ ///
+ /// Returns a array that represents the current object.
+ ///
+ /// Returns a array that represents the current object.
+ public byte[] ToByteArray()
+ {
+ return value.Copy();
+ }
+
+ ///
+ /// Returns a that represents the current object in plain text.
+ ///
+ /// Returns a that represents the current object in plain text.
+ public string ToPlainTextString()
+ {
+ return ToPlainTextString(Encoding.Default);
+ }
+
+ ///
+ /// Returns a that represents the current object in plain text.
+ ///
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a that represents the current object in plain text.
+ public string ToPlainTextString(Encoding encoding)
+ {
+ return encoding.GetString(value);
+ }
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Encode(value, alphabet.Alphabet, padding);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base32.cs b/OnixLabs.Core/Text/Base32.cs
new file mode 100644
index 0000000..0a18f2c
--- /dev/null
+++ b/OnixLabs.Core/Text/Base32.cs
@@ -0,0 +1,50 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-32 value.
+ ///
+ public readonly partial struct Base32
+ {
+ ///
+ /// The underlying value.
+ ///
+ private readonly byte[] value;
+
+ ///
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ ///
+ private readonly Base32Alphabet alphabet;
+
+ ///
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ ///
+ private readonly bool padding;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ /// Determines whether padding should be applied for Base-32 encoding and decoding operations.
+ private Base32(byte[] value, Base32Alphabet alphabet, bool padding)
+ {
+ this.value = value;
+ this.alphabet = alphabet;
+ this.padding = padding;
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base32Alphabet.cs b/OnixLabs.Core/Text/Base32Alphabet.cs
new file mode 100644
index 0000000..6fd845d
--- /dev/null
+++ b/OnixLabs.Core/Text/Base32Alphabet.cs
@@ -0,0 +1,68 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Specifies the supported Base-32 alphabets.
+ ///
+ public sealed class Base32Alphabet : Enumeration
+ {
+ ///
+ /// The default (RFC-4648) Base-32 alphabet.
+ ///
+ public static readonly Base32Alphabet Default =
+ new(0, nameof(Default), "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567");
+
+ ///
+ /// The Z-Base-32 alphabet.
+ ///
+ public static readonly Base32Alphabet ZBase32 =
+ new(1, nameof(ZBase32), "ybndrfg8ejkmcpqxot1uwisza345h769");
+
+ ///
+ /// The Geohash Base-32 alphabet.
+ ///
+ public static readonly Base32Alphabet GeoHash =
+ new(2, nameof(GeoHash), "0123456789bcdefghjkmnpqrstuvwxyz");
+
+ ///
+ /// The Crockford Base-32 alphabet.
+ ///
+ public static readonly Base32Alphabet Crockford =
+ new(3, nameof(Crockford), "0123456789ABCDEFGHJKMNPQRSTVWXYZ");
+
+ ///
+ /// The Base-32 Hex alphabet.
+ ///
+ public static readonly Base32Alphabet Base32Hex =
+ new(4, nameof(Base32Hex), "0123456789ABCDEFGHIJKLMNOPQRSTUV");
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The value of the enumeration entry.
+ /// The name of the enumeration entry.
+ /// The alphabet that will be used for Base-32 encoding and decoding operations.
+ private Base32Alphabet(int value, string name, string alphabet) : base(value, name)
+ {
+ Alphabet = alphabet;
+ }
+
+ ///
+ /// Gets the alphabet that will be used for Base-32 encoding and decoding operations.
+ ///
+ public string Alphabet { get; }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base58.Checksum.cs b/OnixLabs.Core/Text/Base58.Checksum.cs
new file mode 100644
index 0000000..62d5ca0
--- /dev/null
+++ b/OnixLabs.Core/Text/Base58.Checksum.cs
@@ -0,0 +1,97 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+using System.Security.Cryptography;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-58 value.
+ ///
+ public readonly partial struct Base58
+ {
+ ///
+ /// The size of a Base-58 checksum.
+ ///
+ private const int ChecksumSize = 4;
+
+ ///
+ /// Computes a Base-58 checksum.
+ ///
+ /// The value for which to compute a Base-58 checksum.
+ /// Returns the computed Base-58 checksum.
+ private static byte[] ComputeChecksum(byte[] value)
+ {
+ using HashAlgorithm algorithm = SHA256.Create();
+
+ byte[] hashedValue = algorithm.ComputeHash(value);
+ byte[] checksum = algorithm.ComputeHash(hashedValue);
+ byte[] result = new byte[ChecksumSize];
+
+ Buffer.BlockCopy(checksum, 0, result, 0, result.Length);
+
+ return result;
+ }
+
+ ///
+ /// Adds a checksum to the specified byte array.
+ ///
+ /// The value for which to add a checksum.
+ /// Returns the original value with a checksum.
+ private static byte[] AddChecksum(byte[] value)
+ {
+ byte[] checksum = ComputeChecksum(value);
+ return value.ConcatenateWith(checksum);
+ }
+
+ ///
+ /// Removes a checksum from the specified byte array.
+ ///
+ /// The value for which to remove a checksum.
+ /// Returns the original value without a checksum.
+ private static byte[] RemoveChecksum(byte[] value)
+ {
+ return value.Copy(0, value.Length - ChecksumSize);
+ }
+
+ ///
+ /// Gets a checksum from the specified byte array.
+ ///
+ /// The value from which to obtain a checksum.
+ /// Returns a checksum from the specified byte array.
+ private static byte[] GetChecksum(byte[] value)
+ {
+ return value.Copy(value.Length - ChecksumSize - 1, ChecksumSize);
+ }
+
+ ///
+ /// Verifies a Base-58 checksum.
+ ///
+ /// The value for which to verify its checksum.
+ /// If the Base-58 checksum is invalid.
+ private static void VerifyChecksum(byte[] value)
+ {
+ byte[] valueWithoutChecksum = RemoveChecksum(value);
+ byte[] originalChecksum = GetChecksum(value);
+ byte[] computedChecksum = ComputeChecksum(valueWithoutChecksum);
+
+ if (!originalChecksum.SequenceEqual(computedChecksum))
+ {
+ throw new FormatException("Base-58 checksum is invalid.");
+ }
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base58.Codec.cs b/OnixLabs.Core/Text/Base58.Codec.cs
new file mode 100644
index 0000000..00a6f2d
--- /dev/null
+++ b/OnixLabs.Core/Text/Base58.Codec.cs
@@ -0,0 +1,96 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-58 value.
+ ///
+ public readonly partial struct Base58
+ {
+ ///
+ /// Encode a byte array into a Base-58 string.
+ ///
+ /// The value to encode.
+ /// The Base-58 alphabet to use for encoding.
+ /// Returns a Base-58 encoded string.
+ private static string Encode(byte[] value, string alphabet)
+ {
+ BigInteger data = value.Aggregate(BigInteger.Zero, (a, b) => a * 256 + b);
+ StringBuilder result = new();
+
+ while (data > 0)
+ {
+ BigInteger remainder = data % 58;
+ data /= 58;
+ result.Insert(0, alphabet[(int) remainder]);
+ }
+
+ for (int index = 0; index < value.Length && value[index] == 0; index++)
+ {
+ result.Insert(0, '1');
+ }
+
+ return result.ToString();
+ }
+
+ ///
+ /// Decodes a Base-58 into a byte array.
+ ///
+ /// The value to decode.
+ /// The Base-58 alphabet to use for decoding.
+ /// Returns a byte array.
+ /// If the Base-58 string format is invalid.
+ private static byte[] Decode(ReadOnlySpan value, string alphabet)
+ {
+ BigInteger data = BigInteger.Zero;
+
+ for (int index = 0; index < value.Length; index++)
+ {
+ char character = value[index];
+ int characterIndex = alphabet.IndexOf(character);
+
+ if (characterIndex < 0)
+ {
+ throw new FormatException($"Invalid Base58 character '{character}' at position {index}");
+ }
+
+ data = data * 58 + characterIndex;
+ }
+
+ int leadingZeroCount = value
+ .ToArray()
+ .TakeWhile(character => character == '1')
+ .Count();
+
+ IEnumerable leadingZeros = Enumerable
+ .Repeat(byte.MinValue, leadingZeroCount);
+
+ IEnumerable bytesWithoutLeadingZeros = data
+ .ToByteArray()
+ .Reverse()
+ .SkipWhile(byteValue => byteValue == 0);
+
+ return leadingZeros
+ .Concat(bytesWithoutLeadingZeros)
+ .ToArray();
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base58.Equatable.cs b/OnixLabs.Core/Text/Base58.Equatable.cs
new file mode 100644
index 0000000..9e56587
--- /dev/null
+++ b/OnixLabs.Core/Text/Base58.Equatable.cs
@@ -0,0 +1,77 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-58 value.
+ ///
+ public readonly partial struct Base58 : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(Base58 a, Base58 b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(Base58 a, Base58 b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public bool Equals(Base58 other)
+ {
+ return other.value.SequenceEqual(value)
+ && other.alphabet == alphabet;
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return obj is Base58 other && Equals(other);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(value);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base58.From.cs b/OnixLabs.Core/Text/Base58.From.cs
new file mode 100644
index 0000000..8061527
--- /dev/null
+++ b/OnixLabs.Core/Text/Base58.From.cs
@@ -0,0 +1,182 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-58 value.
+ ///
+ public readonly partial struct Base58
+ {
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base58 FromByteArray(byte[] value)
+ {
+ return new Base58(value, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base58 FromByteArray(byte[] value, Base58Alphabet alphabet)
+ {
+ return new Base58(value, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base58 FromCharArray(char[] value)
+ {
+ return FromCharArray(value, Encoding.Default, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base58 FromCharArray(char[] value, Encoding encoding)
+ {
+ return FromCharArray(value, encoding, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base58 FromCharArray(char[] value, Base58Alphabet alphabet)
+ {
+ return FromCharArray(value, Encoding.Default, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base58 FromCharArray(char[] value, Encoding encoding, Base58Alphabet alphabet)
+ {
+ byte[] bytes = encoding.GetBytes(value);
+ return FromByteArray(bytes, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base58 FromSpan(ReadOnlySpan value)
+ {
+ return FromSpan(value, Encoding.Default, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base58 FromSpan(ReadOnlySpan value, Encoding encoding)
+ {
+ return FromSpan(value, encoding, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base58 FromSpan(ReadOnlySpan value, Base58Alphabet alphabet)
+ {
+ return FromSpan(value, Encoding.Default, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base58 FromSpan(ReadOnlySpan value, Encoding encoding, Base58Alphabet alphabet)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base58 FromString(string value)
+ {
+ return FromString(value, Encoding.Default, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base58 FromString(string value, Encoding encoding)
+ {
+ return FromString(value, encoding, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base58 FromString(string value, Base58Alphabet alphabet)
+ {
+ return FromString(value, Encoding.Default, alphabet);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// Returns a new instance.
+ public static Base58 FromString(string value, Encoding encoding, Base58Alphabet alphabet)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding, alphabet);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base58.Parse.cs b/OnixLabs.Core/Text/Base58.Parse.cs
new file mode 100644
index 0000000..b096322
--- /dev/null
+++ b/OnixLabs.Core/Text/Base58.Parse.cs
@@ -0,0 +1,160 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-58 value.
+ ///
+ public readonly partial struct Base58
+ {
+ ///
+ /// Parses a Base-58 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// A new instance.
+ public static Base58 Parse(string value)
+ {
+ return Parse(value, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Parses a Base-58 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// A new instance.
+ public static Base58 Parse(string value, Base58Alphabet alphabet)
+ {
+ ReadOnlySpan characters = value.AsSpan();
+ return Parse(characters, alphabet);
+ }
+
+ ///
+ /// Parses a Base-58 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// A new instance.
+ public static Base58 Parse(char[] value)
+ {
+ return Parse(value, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Parses a Base-58 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// A new instance.
+ public static Base58 Parse(char[] value, Base58Alphabet alphabet)
+ {
+ ReadOnlySpan characters = value.AsSpan();
+ return Parse(characters, alphabet);
+ }
+
+ ///
+ /// Parses a Base-58 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// A new instance.
+ public static Base58 Parse(ReadOnlySpan value)
+ {
+ return Parse(value, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Parses a Base-58 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// A new instance.
+ public static Base58 Parse(ReadOnlySpan value, Base58Alphabet alphabet)
+ {
+ byte[] bytes = Decode(value, alphabet.Alphabet);
+ return FromByteArray(bytes, alphabet);
+ }
+
+ ///
+ /// Parses a Base-58 value with a checksum into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to ParseWithChecksum.
+ /// A new instance.
+ public static Base58 ParseWithChecksum(string value)
+ {
+ return ParseWithChecksum(value, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Parses a Base-58 value with a checksum into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to ParseWithChecksum.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// A new instance.
+ public static Base58 ParseWithChecksum(string value, Base58Alphabet alphabet)
+ {
+ ReadOnlySpan characters = value.AsSpan();
+ return ParseWithChecksum(characters, alphabet);
+ }
+
+ ///
+ /// Parses a Base-58 value with a checksum into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to ParseWithChecksum.
+ /// A new instance.
+ public static Base58 ParseWithChecksum(char[] value)
+ {
+ return ParseWithChecksum(value, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Parses a Base-58 value with a checksum into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to ParseWithChecksum.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// A new instance.
+ public static Base58 ParseWithChecksum(char[] value, Base58Alphabet alphabet)
+ {
+ ReadOnlySpan characters = value.AsSpan();
+ return ParseWithChecksum(characters, alphabet);
+ }
+
+ ///
+ /// Parses a Base-58 value with a checksum into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to ParseWithChecksum.
+ /// A new instance.
+ public static Base58 ParseWithChecksum(ReadOnlySpan value)
+ {
+ return ParseWithChecksum(value, Base58Alphabet.Default);
+ }
+
+ ///
+ /// Parses a Base-58 value with a checksum into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to ParseWithChecksum.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ /// A new instance.
+ public static Base58 ParseWithChecksum(ReadOnlySpan value, Base58Alphabet alphabet)
+ {
+ byte[] bytes = Decode(value, alphabet.Alphabet);
+ byte[] bytesWithoutChecksum = RemoveChecksum(bytes);
+
+ VerifyChecksum(bytes);
+
+ return FromByteArray(bytesWithoutChecksum, alphabet);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base58.To.cs b/OnixLabs.Core/Text/Base58.To.cs
new file mode 100644
index 0000000..97d27c8
--- /dev/null
+++ b/OnixLabs.Core/Text/Base58.To.cs
@@ -0,0 +1,72 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-58 value.
+ ///
+ public readonly partial struct Base58
+ {
+ ///
+ /// Returns a array that represents the current object.
+ ///
+ /// Returns a array that represents the current object.
+ public byte[] ToByteArray()
+ {
+ return value.Copy();
+ }
+
+ ///
+ /// Returns a that represents the current object, with a checksum.
+ ///
+ /// A that represents the current object, with a checksum.
+ public string ToStringWithChecksum()
+ {
+ byte[] valueWithChecksum = AddChecksum(value);
+ return Encode(valueWithChecksum, alphabet.Alphabet);
+ }
+
+ ///
+ /// Returns a that represents the current object in plain text.
+ ///
+ /// Returns a that represents the current object in plain text.
+ public string ToPlainTextString()
+ {
+ return ToPlainTextString(Encoding.Default);
+ }
+
+ ///
+ /// Returns a that represents the current object in plain text.
+ ///
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a that represents the current object in plain text.
+ public string ToPlainTextString(Encoding encoding)
+ {
+ return encoding.GetString(value);
+ }
+
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Encode(value, alphabet.Alphabet);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base58.cs b/OnixLabs.Core/Text/Base58.cs
new file mode 100644
index 0000000..42028e4
--- /dev/null
+++ b/OnixLabs.Core/Text/Base58.cs
@@ -0,0 +1,43 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-58 value.
+ ///
+ public readonly partial struct Base58
+ {
+ ///
+ /// The underlying value.
+ ///
+ private readonly byte[] value;
+
+ ///
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ ///
+ private readonly Base58Alphabet alphabet;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The underlying value.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ private Base58(byte[] value, Base58Alphabet alphabet)
+ {
+ this.value = value;
+ this.alphabet = alphabet;
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base58Alphabet.cs b/OnixLabs.Core/Text/Base58Alphabet.cs
new file mode 100644
index 0000000..6f8c7a0
--- /dev/null
+++ b/OnixLabs.Core/Text/Base58Alphabet.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Specifies the supported Base-58 alphabets.
+ ///
+ public sealed class Base58Alphabet : Enumeration
+ {
+ ///
+ /// The default Base-58 alphabet, which is the same as Bitcoin's Base-58 alphabet.
+ ///
+ public static readonly Base58Alphabet Default =
+ new(0, nameof(Default), "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
+
+ ///
+ /// The Ripple Base-58 alphabet.
+ ///
+ public static readonly Base58Alphabet Ripple =
+ new(0, nameof(Ripple), "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz");
+
+ ///
+ /// The Flickr Base-58 alphabet.
+ ///
+ public static readonly Base58Alphabet Flickr =
+ new(0, nameof(Flickr), "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ");
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The value of the enumeration entry.
+ /// The name of the enumeration entry.
+ /// The alphabet that will be used for Base-58 encoding and decoding operations.
+ private Base58Alphabet(int value, string name, string alphabet) : base(value, name)
+ {
+ Alphabet = alphabet;
+ }
+
+ ///
+ /// Gets the alphabet that will be used for Base-58 encoding and decoding operations.
+ ///
+ public string Alphabet { get; }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base64.Equatable.cs b/OnixLabs.Core/Text/Base64.Equatable.cs
new file mode 100644
index 0000000..d9cdcf4
--- /dev/null
+++ b/OnixLabs.Core/Text/Base64.Equatable.cs
@@ -0,0 +1,76 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-64 value.
+ ///
+ public readonly partial struct Base64 : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(Base64 a, Base64 b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(Base64 a, Base64 b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public bool Equals(Base64 other)
+ {
+ return other.value.SequenceEqual(value);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return obj is Base64 other && Equals(other);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(value);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base64.From.cs b/OnixLabs.Core/Text/Base64.From.cs
new file mode 100644
index 0000000..131bc9e
--- /dev/null
+++ b/OnixLabs.Core/Text/Base64.From.cs
@@ -0,0 +1,102 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-64 value.
+ ///
+ public readonly partial struct Base64
+ {
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base64 FromByteArray(byte[] value)
+ {
+ return new Base64(value);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base64 FromCharArray(char[] value)
+ {
+ return FromCharArray(value, Encoding.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified array.
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base64 FromCharArray(char[] value, Encoding encoding)
+ {
+ byte[] bytes = encoding.GetBytes(value);
+ return FromByteArray(bytes);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base64 FromSpan(ReadOnlySpan value)
+ {
+ return FromSpan(value, Encoding.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base64 FromSpan(ReadOnlySpan value, Encoding encoding)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// Returns a new instance.
+ public static Base64 FromString(string value)
+ {
+ return FromString(value, Encoding.Default);
+ }
+
+ ///
+ /// Creates a instance from the specified .
+ ///
+ /// The underlying value.
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a new instance.
+ public static Base64 FromString(string value, Encoding encoding)
+ {
+ char[] characters = value.ToArray();
+ return FromCharArray(characters, encoding);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base64.Parse.cs b/OnixLabs.Core/Text/Base64.Parse.cs
new file mode 100644
index 0000000..55bf8ff
--- /dev/null
+++ b/OnixLabs.Core/Text/Base64.Parse.cs
@@ -0,0 +1,57 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-64 value.
+ ///
+ public readonly partial struct Base64
+ {
+ ///
+ /// Parses a Base-64 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// Returns a new instance.
+ public static Base64 Parse(string value)
+ {
+ char[] characters = value.ToCharArray();
+ return Parse(characters);
+ }
+
+ ///
+ /// Parses a Base-64 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// Returns a new instance.
+ public static Base64 Parse(char[] value)
+ {
+ byte[] bytes = Convert.FromBase64CharArray(value, 0, value.Length);
+ return FromByteArray(bytes);
+ }
+
+ ///
+ /// Parses a Base-64 value into a instance.
+ ///
+ /// The Base-16 (hexadecimal) value to parse.
+ /// Returns a new instance.
+ public static Base64 Parse(ReadOnlySpan value)
+ {
+ char[] characters = value.ToArray();
+ return Parse(characters);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base64.To.cs b/OnixLabs.Core/Text/Base64.To.cs
new file mode 100644
index 0000000..831a46e
--- /dev/null
+++ b/OnixLabs.Core/Text/Base64.To.cs
@@ -0,0 +1,62 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Text;
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-64 value.
+ ///
+ public readonly partial struct Base64
+ {
+ ///
+ /// Returns a array that represents the current object.
+ ///
+ /// Returns a array that represents the current object.
+ public byte[] ToByteArray()
+ {
+ return value.Copy();
+ }
+
+ ///
+ /// Returns a that represents the current object in plain text.
+ ///
+ /// Returns a that represents the current object in plain text.
+ public string ToPlainTextString()
+ {
+ return ToPlainTextString(Encoding.Default);
+ }
+
+ ///
+ /// Returns a that represents the current object in plain text.
+ ///
+ /// The encoding to use to obtain the underlying value.
+ /// Returns a that represents the current object in plain text.
+ public string ToPlainTextString(Encoding encoding)
+ {
+ return encoding.GetString(value);
+ }
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Convert.ToBase64String(value);
+ }
+ }
+}
diff --git a/OnixLabs.Core/Text/Base64.cs b/OnixLabs.Core/Text/Base64.cs
new file mode 100644
index 0000000..051d05d
--- /dev/null
+++ b/OnixLabs.Core/Text/Base64.cs
@@ -0,0 +1,36 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Core.Text
+{
+ ///
+ /// Represents a Base-64 value.
+ ///
+ public readonly partial struct Base64
+ {
+ ///
+ /// The underlying value.
+ ///
+ private readonly byte[] value;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The underlying value.
+ private Base64(byte[] value)
+ {
+ this.value = value;
+ }
+ }
+}
diff --git a/OnixLabs.Playground/OnixLabs.Playground.csproj b/OnixLabs.Playground/OnixLabs.Playground.csproj
new file mode 100644
index 0000000..af40665
--- /dev/null
+++ b/OnixLabs.Playground/OnixLabs.Playground.csproj
@@ -0,0 +1,13 @@
+
+
+
+ Exe
+ net5.0
+ enable
+
+
+
+
+
+
+
diff --git a/OnixLabs.Playground/Program.cs b/OnixLabs.Playground/Program.cs
new file mode 100644
index 0000000..f16bb30
--- /dev/null
+++ b/OnixLabs.Playground/Program.cs
@@ -0,0 +1,23 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Playground
+{
+ internal static class Program
+ {
+ private static void Main(string[] args)
+ {
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/EcdsaEncryptedPkcs8Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/EcdsaEncryptedPkcs8Tests.cs
new file mode 100644
index 0000000..2c89a96
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/EcdsaEncryptedPkcs8Tests.cs
@@ -0,0 +1,111 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class EcdsaKeyEncryptedPkcs8Tests : KeyTestBase
+ {
+ [Fact(DisplayName = "Two identical ECDSA PKCS #8 private keys should be considered equal")]
+ public void TwoIdenticalPrivateKeysShouldBeConsideredEqual()
+ {
+ // Arrange
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ const string password = "This is a secret!";
+ PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64);
+
+ // Act
+ byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters);
+ PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type);
+
+ // Assert
+ Assert.Equal(privateKey1, privateKey2);
+ }
+
+ [Fact(DisplayName = "Two identical ECDSA PKCS #8 keys should be able to sign and verify the same data")]
+ public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameData()
+ {
+ // Arrange
+ IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ const string password = "This is a secret!";
+ PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64);
+ byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters);
+ PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ DigitalSignature signature1 = privateKey1.SignData(data);
+ DigitalSignature signature2 = privateKey2.SignData(data);
+
+ signatures.Add((signature1, data));
+ signatures.Add((signature2, data));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, byte[] data) in signatures)
+ {
+ Assert.True(signature.IsDataValid(data, publicKey1));
+ Assert.True(signature.IsDataValid(data, publicKey2));
+ }
+ }
+
+ [Fact(DisplayName = "Two identical ECDSA PKCS #8 keys should be able to sign and verify the same hash")]
+ public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameHash()
+ {
+ // Arrange
+ IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ const string password = "This is a secret!";
+ PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64);
+ byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters);
+ PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ Hash hashedData = Hash.ComputeSha2Hash256(data);
+ DigitalSignature signature1 = privateKey1.SignHash(hashedData);
+ DigitalSignature signature2 = privateKey2.SignHash(hashedData);
+
+ signatures.Add((signature1, hashedData));
+ signatures.Add((signature2, hashedData));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, Hash hashedData) in signatures)
+ {
+ Assert.True(signature.IsHashValid(hashedData, publicKey1));
+ Assert.True(signature.IsHashValid(hashedData, publicKey2));
+ }
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyPkcs8Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyPkcs8Tests.cs
new file mode 100644
index 0000000..b0b6bc2
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyPkcs8Tests.cs
@@ -0,0 +1,101 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class EcdsaKeyPkcs8Tests : KeyTestBase
+ {
+ [Fact(DisplayName = "Two identical ECDSA PKCS #8 private keys should be considered equal")]
+ public void TwoIdenticalPrivateKeysShouldBeConsideredEqual()
+ {
+ // Arrange
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PrivateKey privateKey1 = pair.PrivateKey;
+
+ // Act
+ byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key();
+ PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, type);
+
+ // Assert
+ Assert.Equal(privateKey1, privateKey2);
+ }
+
+ [Fact(DisplayName = "Two identical ECDSA PKCS #8 keys should be able to sign and verify the same data")]
+ public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameData()
+ {
+ // Arrange
+ IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(privateKey1.ExportPkcs8Key(), type);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ DigitalSignature signature1 = privateKey1.SignData(data);
+ DigitalSignature signature2 = privateKey2.SignData(data);
+
+ signatures.Add((signature1, data));
+ signatures.Add((signature2, data));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, byte[] data) in signatures)
+ {
+ Assert.True(signature.IsDataValid(data, publicKey1));
+ Assert.True(signature.IsDataValid(data, publicKey2));
+ }
+ }
+
+ [Fact(DisplayName = "Two identical ECDSA PKCS #8 keys should be able to sign and verify the same hash")]
+ public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameHash()
+ {
+ // Arrange
+ IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ PrivateKey privateKey2 = EcdsaPrivateKey.ImportPkcs8Key(privateKey1.ExportPkcs8Key(), type);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ Hash hashedData = Hash.ComputeSha2Hash256(data);
+ DigitalSignature signature1 = privateKey1.SignHash(hashedData);
+ DigitalSignature signature2 = privateKey2.SignHash(hashedData);
+
+ signatures.Add((signature1, hashedData));
+ signatures.Add((signature2, hashedData));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, Hash hashedData) in signatures)
+ {
+ Assert.True(signature.IsHashValid(hashedData, publicKey1));
+ Assert.True(signature.IsHashValid(hashedData, publicKey2));
+ }
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyTests.cs b/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyTests.cs
new file mode 100644
index 0000000..c653aa3
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyTests.cs
@@ -0,0 +1,118 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Generic;
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class EcdsaKeyTests : KeyTestBase
+ {
+ [Fact(DisplayName = "Two identical ECDSA private keys should be considered equal")]
+ public void TwoIdenticalPrivateKeysShouldBeConsideredEqual()
+ {
+ // Arrange
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PrivateKey privateKey1 = pair.PrivateKey;
+
+ // Act
+ Base58 base58PrivateKey = privateKey1.ToBase58();
+ PrivateKey privateKey2 = EcdsaPrivateKey.FromBase58(base58PrivateKey, type);
+
+ // Assert
+ Assert.Equal(privateKey1, privateKey2);
+ }
+
+ [Fact(DisplayName = "Two identical ECDSA public keys should be considered equal")]
+ public void TwoIdenticalPublicKeysShouldBeConsideredEqual()
+ {
+ // Arrange
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PublicKey publicKey1 = pair.PublicKey;
+
+ // Act
+ Base58 base58PublicKey = publicKey1.ToBase58();
+ PublicKey publicKey2 = EcdsaPublicKey.FromBase58(base58PublicKey, type);
+
+ // Assert
+ Assert.Equal(publicKey1, publicKey2);
+ }
+
+ [Fact(DisplayName = "Two identical ECDSA keys should be able to sign and verify the same data")]
+ public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameData()
+ {
+ // Arrange
+ IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ PrivateKey privateKey2 = EcdsaPrivateKey.FromBase64(privateKey1.ToBase64(), type);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ DigitalSignature signature1 = privateKey1.SignData(data);
+ DigitalSignature signature2 = privateKey2.SignData(data);
+
+ signatures.Add((signature1, data));
+ signatures.Add((signature2, data));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, byte[] data) in signatures)
+ {
+ Assert.True(signature.IsDataValid(data, publicKey1));
+ Assert.True(signature.IsDataValid(data, publicKey2));
+ }
+ }
+
+ [Fact(DisplayName = "Two identical ECDSA keys should be able to sign and verify the same hash")]
+ public void TwoIdenticalEcdsaKeysShouldBeAbleToSignAndVerifyTheSameHash()
+ {
+ // Arrange
+ IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ KeyPair pair = KeyPair.CreateEcdsaKeyPair(type);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ PrivateKey privateKey2 = EcdsaPrivateKey.FromBase64(privateKey1.ToBase64(), type);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ Hash hashedData = Hash.ComputeSha2Hash256(data);
+ DigitalSignature signature1 = privateKey1.SignHash(hashedData);
+ DigitalSignature signature2 = privateKey2.SignHash(hashedData);
+
+ signatures.Add((signature1, hashedData));
+ signatures.Add((signature2, hashedData));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, Hash hashedData) in signatures)
+ {
+ Assert.True(signature.IsHashValid(hashedData, publicKey1));
+ Assert.True(signature.IsHashValid(hashedData, publicKey2));
+ }
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/HashTests.cs b/OnixLabs.Security.Cryptography.UnitTests/HashTests.cs
new file mode 100644
index 0000000..26f430e
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/HashTests.cs
@@ -0,0 +1,43 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class HashTests
+ {
+ [Fact(DisplayName = "Identical hashes should be considered equal")]
+ public void IdenticalHashesShouldBeConsideredEqual()
+ {
+ // Arrange
+ Hash a = Hash.ComputeSha2Hash256("abc");
+ Hash b = Hash.ComputeSha2Hash256("abc");
+
+ // Assert
+ Assert.Equal(a, b);
+ }
+
+ [Fact(DisplayName = "Different hashes should not be considered equal")]
+ public void DifferentHashesShouldNotBeConsideredEqual()
+ {
+ // Arrange
+ Hash a = Hash.ComputeSha2Hash256("abc");
+ Hash b = Hash.ComputeSha2Hash256("xyz");
+
+ // Assert
+ Assert.NotEqual(a, b);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/KeyTestBase.cs b/OnixLabs.Security.Cryptography.UnitTests/KeyTestBase.cs
new file mode 100644
index 0000000..3617250
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/KeyTestBase.cs
@@ -0,0 +1,31 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public abstract class KeyTestBase
+ {
+ protected static byte[] GenerateRandomData(int length = 1024)
+ {
+ byte[] result = new byte[length];
+ Random random = new(Guid.NewGuid().GetHashCode());
+
+ random.NextBytes(result);
+
+ return result;
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeTests.cs b/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeTests.cs
new file mode 100644
index 0000000..4711e28
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/MerkleTreeTests.cs
@@ -0,0 +1,62 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class MerkleTreeTests
+ {
+ [Fact(DisplayName = "Identical Merkle trees should be considered equal")]
+ public void IdenticalMerkleTreesShouldBeConsideredEqual()
+ {
+ // Arrange
+ List hashes = Enumerable
+ .Range(1, 937)
+ .Select(i => Hash.ComputeSha2Hash256(i.ToString()))
+ .ToList();
+
+ // Act
+ MerkleTree a = MerkleTree.Build(hashes);
+ MerkleTree b = MerkleTree.Build(hashes);
+
+ // Assert
+ Assert.Equal(a, b);
+ }
+
+ [Fact(DisplayName = "Different Merkle trees should not be considered equal")]
+ public void DifferentMerkleTreesShouldNotBeConsideredEqual()
+ {
+ // Arrange
+ List hashesForMerkleTreeA = Enumerable
+ .Range(1, 937)
+ .Select(i => Hash.ComputeSha2Hash256($"A{i}"))
+ .ToList();
+
+ List hashesForMerkleTreeB = Enumerable
+ .Range(1, 677)
+ .Select(i => Hash.ComputeSha2Hash256($"B{i}"))
+ .ToList();
+
+ // Act
+ MerkleTree a = MerkleTree.Build(hashesForMerkleTreeA);
+ MerkleTree b = MerkleTree.Build(hashesForMerkleTreeB);
+
+ // Assert
+ Assert.NotEqual(a, b);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/OnixLabs.Security.Cryptography.UnitTests.csproj b/OnixLabs.Security.Cryptography.UnitTests/OnixLabs.Security.Cryptography.UnitTests.csproj
new file mode 100644
index 0000000..8ac109e
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/OnixLabs.Security.Cryptography.UnitTests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net5.0
+
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/OnixLabs.Security.Cryptography.UnitTests/RsaKeyEncryptedPkcs8Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyEncryptedPkcs8Tests.cs
new file mode 100644
index 0000000..80ac269
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyEncryptedPkcs8Tests.cs
@@ -0,0 +1,113 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class RsaKeyEncryptedPkcs8Tests : KeyTestBase
+ {
+ [Fact(DisplayName = "Two identical RSA PKCS #8 private keys should be considered equal")]
+ public void TwoIdenticalPrivateKeysShouldBeConsideredEqual()
+ {
+ // Arrange
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ const string password = "This is a secret!";
+ PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64);
+
+ // Act
+ byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters);
+ PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type, padding);
+
+ // Assert
+ Assert.Equal(privateKey1, privateKey2);
+ }
+
+ [Fact(DisplayName = "Two identical RSA PKCS #8 keys should be able to sign and verify the same data")]
+ public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameData()
+ {
+ // Arrange
+ IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ const string password = "This is a secret!";
+ PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64);
+ byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters);
+ PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type, padding);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ DigitalSignature signature1 = privateKey1.SignData(data);
+ DigitalSignature signature2 = privateKey2.SignData(data);
+
+ signatures.Add((signature1, data));
+ signatures.Add((signature2, data));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, byte[] data) in signatures)
+ {
+ Assert.True(signature.IsDataValid(data, publicKey1));
+ Assert.True(signature.IsDataValid(data, publicKey2));
+ }
+ }
+
+ [Fact(DisplayName = "Two identical RSA PKCS #8 keys should be able to sign and verify the same hash")]
+ public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameHash()
+ {
+ // Arrange
+ IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ const string password = "This is a secret!";
+ PbeParameters parameters = new(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 64);
+ byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key(password, parameters);
+ PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, password, type, padding);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ Hash hashedData = Hash.ComputeSha2Hash256(data);
+ DigitalSignature signature1 = privateKey1.SignHash(hashedData);
+ DigitalSignature signature2 = privateKey2.SignHash(hashedData);
+
+ signatures.Add((signature1, hashedData));
+ signatures.Add((signature2, hashedData));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, Hash hashedData) in signatures)
+ {
+ Assert.True(signature.IsHashValid(hashedData, publicKey1));
+ Assert.True(signature.IsHashValid(hashedData, publicKey2));
+ }
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/RsaKeyPkcs8Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyPkcs8Tests.cs
new file mode 100644
index 0000000..79a4e9c
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyPkcs8Tests.cs
@@ -0,0 +1,105 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class RsaKeyPkcs8Tests : KeyTestBase
+ {
+ [Fact(DisplayName = "Two identical RSA PKCS #8 private keys should be considered equal")]
+ public void TwoIdenticalPrivateKeysShouldBeConsideredEqual()
+ {
+ // Arrange
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PrivateKey privateKey1 = pair.PrivateKey;
+
+ // Act
+ byte[] pkcs8PrivateKey = privateKey1.ExportPkcs8Key();
+ PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(pkcs8PrivateKey, type, padding);
+
+ // Assert
+ Assert.Equal(privateKey1, privateKey2);
+ }
+
+ [Fact(DisplayName = "Two identical RSA PKCS #8 keys should be able to sign and verify the same data")]
+ public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameData()
+ {
+ // Arrange
+ IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(privateKey1.ExportPkcs8Key(), type, padding);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ DigitalSignature signature1 = privateKey1.SignData(data);
+ DigitalSignature signature2 = privateKey2.SignData(data);
+
+ signatures.Add((signature1, data));
+ signatures.Add((signature2, data));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, byte[] data) in signatures)
+ {
+ Assert.True(signature.IsDataValid(data, publicKey1));
+ Assert.True(signature.IsDataValid(data, publicKey2));
+ }
+ }
+
+ [Fact(DisplayName = "Two identical RSA PKCS #8 keys should be able to sign and verify the same hash")]
+ public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameHash()
+ {
+ // Arrange
+ IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ PrivateKey privateKey2 = RsaPrivateKey.ImportPkcs8Key(privateKey1.ExportPkcs8Key(), type, padding);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ Hash hashedData = Hash.ComputeSha2Hash256(data);
+ DigitalSignature signature1 = privateKey1.SignHash(hashedData);
+ DigitalSignature signature2 = privateKey2.SignHash(hashedData);
+
+ signatures.Add((signature1, hashedData));
+ signatures.Add((signature2, hashedData));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, Hash hashedData) in signatures)
+ {
+ Assert.True(signature.IsHashValid(hashedData, publicKey1));
+ Assert.True(signature.IsHashValid(hashedData, publicKey2));
+ }
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/RsaKeyTests.cs b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyTests.cs
new file mode 100644
index 0000000..ba76154
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/RsaKeyTests.cs
@@ -0,0 +1,123 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using OnixLabs.Core.Text;
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class RsaKeyTests : KeyTestBase
+ {
+ [Fact(DisplayName = "Two identical RSA private keys should be considered equal")]
+ public void TwoIdenticalPrivateKeysShouldBeConsideredEqual()
+ {
+ // Arrange
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PrivateKey privateKey1 = pair.PrivateKey;
+
+ // Act
+ Base58 base58PrivateKey = privateKey1.ToBase58();
+ PrivateKey privateKey2 = RsaPrivateKey.FromBase58(base58PrivateKey, type, padding);
+
+ // Assert
+ Assert.Equal(privateKey1, privateKey2);
+ }
+
+ [Fact(DisplayName = "Two identical RSA public keys should be considered equal")]
+ public void TwoIdenticalPublicKeysShouldBeConsideredEqual()
+ {
+ // Arrange
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PublicKey publicKey1 = pair.PublicKey;
+
+ // Act
+ Base58 base58PublicKey = publicKey1.ToBase58();
+ PublicKey publicKey2 = RsaPublicKey.FromBase58(base58PublicKey, type, padding);
+
+ // Assert
+ Assert.Equal(publicKey1, publicKey2);
+ }
+
+ [Fact(DisplayName = "Two identical RSA keys should be able to sign and verify the same data")]
+ public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameData()
+ {
+ // Arrange
+ IList<(DigitalSignature, byte[])> signatures = new List<(DigitalSignature, byte[])>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ PrivateKey privateKey2 = RsaPrivateKey.FromBase64(privateKey1.ToBase64(), type, padding);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ DigitalSignature signature1 = privateKey1.SignData(data);
+ DigitalSignature signature2 = privateKey2.SignData(data);
+
+ signatures.Add((signature1, data));
+ signatures.Add((signature2, data));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, byte[] data) in signatures)
+ {
+ Assert.True(signature.IsDataValid(data, publicKey1));
+ Assert.True(signature.IsDataValid(data, publicKey2));
+ }
+ }
+
+ [Fact(DisplayName = "Two identical RSA keys should be able to sign and verify the same hash")]
+ public void TwoIdenticalRsaKeysShouldBeAbleToSignAndVerifyTheSameHash()
+ {
+ // Arrange
+ IList<(DigitalSignature, Hash)> signatures = new List<(DigitalSignature, Hash)>();
+ HashAlgorithmType type = HashAlgorithmType.Sha2Hash256;
+ RSASignaturePadding padding = RSASignaturePadding.Pss;
+ KeyPair pair = KeyPair.CreateRsaKeyPair(type, padding);
+ PrivateKey privateKey1 = pair.PrivateKey;
+ PrivateKey privateKey2 = RsaPrivateKey.FromBase64(privateKey1.ToBase64(), type, padding);
+ PublicKey publicKey1 = pair.PublicKey;
+ PublicKey publicKey2 = privateKey1.GetPublicKey();
+
+ // Act
+ for (int index = 0; index < 5; index++)
+ {
+ byte[] data = GenerateRandomData();
+ Hash hashedData = Hash.ComputeSha2Hash256(data);
+ DigitalSignature signature1 = privateKey1.SignHash(hashedData);
+ DigitalSignature signature2 = privateKey2.SignHash(hashedData);
+
+ signatures.Add((signature1, hashedData));
+ signatures.Add((signature2, hashedData));
+ }
+
+ // Assert
+ foreach ((DigitalSignature signature, Hash hashedData) in signatures)
+ {
+ Assert.True(signature.IsHashValid(hashedData, publicKey1));
+ Assert.True(signature.IsHashValid(hashedData, publicKey2));
+ }
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash224Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash224Tests.cs
new file mode 100644
index 0000000..1025b47
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash224Tests.cs
@@ -0,0 +1,40 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class Sha3Hash224Tests : Sha3HashTestBase
+ {
+ protected override Sha3 HashAlgorithm => Sha3.CreateSha3Hash224();
+
+ [Theory(DisplayName = "Sha3Managed224 should produce the expected hash for the specified string literal")]
+ [InlineData("6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", "")]
+ [InlineData("9877af03f5e1919851d0ef4ce6b23f1e85a40b446d93713f4c6e6dcd", "1234567890")]
+ [InlineData("beae76edd99d4ad4d398d51c5ea1d8b7b3fa6d49d687b0cb1ec2ec41", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("5cdeca81e123f87cad96b9cba999f16f6d41549608d4e0f4681b8239", "abcdefghijklmnopqrstuvwxyz")]
+ public override void TestSha3WithLiteralString(string expected, string literal)
+ {
+ base.TestSha3WithLiteralString(expected, literal);
+ }
+
+ [Theory(DisplayName = "Sha3Managed224 should produce the expected hash for the specified string template")]
+ [InlineData("d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c", "a", 1_000_000)]
+ public override void TestSha3WithGeneratedString(string expected, string template, int iterations)
+ {
+ base.TestSha3WithGeneratedString(expected, template, iterations);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash256Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash256Tests.cs
new file mode 100644
index 0000000..84b983c
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash256Tests.cs
@@ -0,0 +1,40 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class Sha3Hash256Tests : Sha3HashTestBase
+ {
+ protected override Sha3 HashAlgorithm => Sha3.CreateSha3Hash256();
+
+ [Theory(DisplayName = "Sha3Managed256 should produce the expected hash for the specified string literal")]
+ [InlineData("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", "")]
+ [InlineData("01da8843e976913aa5c15a62d45f1c9267391dcbd0a76ad411919043f374a163", "1234567890")]
+ [InlineData("738eeb2d4adf0d452456695011bb252bd4701a0ae78fdd3fc945a963bceb1702", "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData("7cab2dc765e21b241dbc1c255ce620b29f527c6d5e7f5f843e56288f0d707521", "abcdefghijklmnopqrstuvwxyz")]
+ public override void TestSha3WithLiteralString(string expected, string literal)
+ {
+ base.TestSha3WithLiteralString(expected, literal);
+ }
+
+ [Theory(DisplayName = "Sha3Managed256 should produce a valid hash result for the given string template")]
+ [InlineData("5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1", "a", 1_000_000)]
+ public override void TestSha3WithGeneratedString(string expected, string template, int iterations)
+ {
+ base.TestSha3WithGeneratedString(expected, template, iterations);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash384Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash384Tests.cs
new file mode 100644
index 0000000..a5d9ca3
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash384Tests.cs
@@ -0,0 +1,53 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class Sha3Managed384Tests : Sha3HashTestBase
+ {
+ protected override Sha3 HashAlgorithm => Sha3.CreateSha3Hash384();
+
+ [Theory(DisplayName = "Sha3Managed384 should produce the expected hash for the specified string literal")]
+ [InlineData(
+ "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004",
+ ""
+ )]
+ [InlineData(
+ "6fdddab7d670f202629531c1a51b32ca30696d0af4dd5b0fbb5f82c0aba5e505110455f37d7ef73950c2bb0495a38f56",
+ "1234567890"
+ )]
+ [InlineData(
+ "284da0df47fc9e75a4ef1248f69ca0d12a5d44508942e63b03b8c227510c2e1b43400009fcd36c0acc941679e5024a04",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ )]
+ [InlineData(
+ "fed399d2217aaf4c717ad0c5102c15589e1c990cc2b9a5029056a7f7485888d6ab65db2370077a5cadb53fc9280d278f",
+ "abcdefghijklmnopqrstuvwxyz"
+ )]
+ public override void TestSha3WithLiteralString(string expected, string literal)
+ {
+ base.TestSha3WithLiteralString(expected, literal);
+ }
+
+ [Theory(DisplayName = "Sha3Managed384 should produce a valid hash result for the given string template")]
+ [InlineData("eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340",
+ "a", 1_000_000)]
+ public override void TestSha3WithGeneratedString(string expected, string template, int iterations)
+ {
+ base.TestSha3WithGeneratedString(expected, template, iterations);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash512Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash512Tests.cs
new file mode 100644
index 0000000..e5c7ba3
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/Sha3Hash512Tests.cs
@@ -0,0 +1,50 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class Sha3Managed512Tests : Sha3HashTestBase
+ {
+ protected override Sha3 HashAlgorithm => Sha3.CreateSha3Hash512();
+
+ [Theory(DisplayName = "Sha3Managed512 should produce the expected hash for the specified message")]
+ [InlineData(
+ "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26",
+ "")]
+ [InlineData(
+ "36dde7d288a2166a651d51ec6ded9e70e72cf6b366293d6f513c75393c57d6f33b949879b9d5e7f7c21cd8c02ede75e74fc54ea15bd043b4df008533fc68ae69",
+ "1234567890")]
+ [InlineData(
+ "69958b041bc72e9922e02cd4250953ee69d5f6e69f97d8def72b34effc0aea2bf5cfe03bd4ada0e271060593395656c1bf9eb68d1fc4cf146f90601152222df7",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
+ [InlineData(
+ "af328d17fa28753a3c9f5cb72e376b90440b96f0289e5703b729324a975ab384eda565fc92aaded143669900d761861687acdc0a5ffa358bd0571aaad80aca68",
+ "abcdefghijklmnopqrstuvwxyz")]
+ public override void TestSha3WithLiteralString(string expected, string literal)
+ {
+ base.TestSha3WithLiteralString(expected, literal);
+ }
+
+ [Theory(DisplayName = "Sha3Managed512 should produce the expected hash for the specified string template")]
+ [InlineData(
+ "3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87",
+ "a", 1_000_000)]
+ public override void TestSha3WithGeneratedString(string expected, string template, int iterations)
+ {
+ base.TestSha3WithGeneratedString(expected, template, iterations);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3HashTestBase.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3HashTestBase.cs
new file mode 100644
index 0000000..83fe8f3
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/Sha3HashTestBase.cs
@@ -0,0 +1,45 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public abstract class Sha3HashTestBase
+ {
+ protected abstract Sha3 HashAlgorithm { get; }
+
+ public virtual void TestSha3WithLiteralString(string expected, string literal)
+ {
+ string actual = ComputeHash(HashAlgorithm, literal);
+ Assert.Equal(expected, actual);
+ }
+
+ public virtual void TestSha3WithGeneratedString(string expected, string template, int iterations)
+ {
+ string actual = ComputeHash(HashAlgorithm, string.Concat(Enumerable.Repeat(template, iterations)));
+ Assert.Equal(expected, actual);
+ }
+
+ private static string ComputeHash(HashAlgorithm algorithm, string plainText)
+ {
+ byte[] plainTextBytes = Encoding.Default.GetBytes(plainText);
+ byte[] hashedBytes = algorithm.ComputeHash(plainTextBytes);
+ return Hash.FromByteArray(hashedBytes).ToString();
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake128Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake128Tests.cs
new file mode 100644
index 0000000..b8c253f
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake128Tests.cs
@@ -0,0 +1,55 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class Sha3ManagedShake128Tests : Sha3ShakeTestBase
+ {
+ [Theory(DisplayName = "Sha3ManagedShake128 should produce the expected hash for the specified string literal")]
+ [InlineData("7f9c2ba4", "", 4)]
+ [InlineData("7f9c2ba4e88f827d", "", 8)]
+ [InlineData("7f9c2ba4e88f827d616045507605853e", "", 16)]
+ [InlineData("7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26", "", 32)]
+ [InlineData("1cd2c71a", "1234567890", 4)]
+ [InlineData("1cd2c71a52e3f2a6", "1234567890", 8)]
+ [InlineData("1cd2c71a52e3f2a620173e915f17648d", "1234567890", 16)]
+ [InlineData("1cd2c71a52e3f2a620173e915f17648dcc43443ef78754302c6b44cf47daf527", "1234567890", 32)]
+ [InlineData("5b3b6a58", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4)]
+ [InlineData("5b3b6a587417f8fa", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 8)]
+ [InlineData("5b3b6a587417f8fa192aba21c16b7b7a", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 16)]
+ [InlineData("5b3b6a587417f8fa192aba21c16b7b7a6375aac5f04e950f", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 24)]
+ [InlineData("961c919c", "abcdefghijklmnopqrstuvwxyz", 4)]
+ [InlineData("961c919c0854576e", "abcdefghijklmnopqrstuvwxyz", 8)]
+ [InlineData("961c919c0854576e561320e81514bf37", "abcdefghijklmnopqrstuvwxyz", 16)]
+ [InlineData("961c919c0854576e561320e81514bf3724197d0715e16a36", "abcdefghijklmnopqrstuvwxyz", 24)]
+ public override void TestSha3WithLiteralString(string expected, string literal, int length)
+ {
+ HashAlgorithm = Sha3.CreateSha3Shake128(length);
+ base.TestSha3WithLiteralString(expected, literal, length);
+ }
+
+ [Theory(DisplayName = "Sha3ManagedShake128 should produce the expected hash for the specified string template")]
+ [InlineData("9d222c79", "a", 1_000_000, 4)]
+ [InlineData("9d222c79c4ff9d09", "a", 1_000_000, 8)]
+ [InlineData("9d222c79c4ff9d092cf6ca86143aa411", "a", 1_000_000, 16)]
+ [InlineData("9d222c79c4ff9d092cf6ca86143aa411e369973808ef97093255826c5572ef58", "a", 1_000_000, 32)]
+ public override void TestSha3WithGeneratedString(string expected, string template, int iterations, int length)
+ {
+ HashAlgorithm = Sha3.CreateSha3Shake128(length);
+ base.TestSha3WithGeneratedString(expected, template, iterations, length);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake256Tests.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake256Tests.cs
new file mode 100644
index 0000000..bf8679b
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/Sha3Shake256Tests.cs
@@ -0,0 +1,55 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public sealed class Sha3ManagedShake256Tests : Sha3ShakeTestBase
+ {
+ [Theory(DisplayName = "Sha3ManagedShake256 should produce the expected hash for the specified string literal")]
+ [InlineData("46b9dd2b", "", 4)]
+ [InlineData("46b9dd2b0ba88d13", "", 8)]
+ [InlineData("46b9dd2b0ba88d13233b3feb743eeb24", "", 16)]
+ [InlineData("46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f", "", 32)]
+ [InlineData("cd65a4e5", "1234567890", 4)]
+ [InlineData("cd65a4e553405b50", "1234567890", 8)]
+ [InlineData("cd65a4e553405b50c2f37001ea81905f", "1234567890", 16)]
+ [InlineData("cd65a4e553405b50c2f37001ea81905f36d650cc775fdad898b2e343644cb3db", "1234567890", 32)]
+ [InlineData("fa8775b6", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4)]
+ [InlineData("fa8775b64bf3aaf1", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 8)]
+ [InlineData("fa8775b64bf3aaf10d7f473c460f4d23", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 16)]
+ [InlineData("fa8775b64bf3aaf10d7f473c460f4d2361f56ff34ae7267a", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 24)]
+ [InlineData("b7b78b04", "abcdefghijklmnopqrstuvwxyz", 4)]
+ [InlineData("b7b78b04a3dd30a2", "abcdefghijklmnopqrstuvwxyz", 8)]
+ [InlineData("b7b78b04a3dd30a265c8886c33fda947", "abcdefghijklmnopqrstuvwxyz", 16)]
+ [InlineData("b7b78b04a3dd30a265c8886c33fda94799853de5d3d10541", "abcdefghijklmnopqrstuvwxyz", 24)]
+ public override void TestSha3WithLiteralString(string expected, string literal, int length)
+ {
+ HashAlgorithm = Sha3.CreateSha3Shake256(length);
+ base.TestSha3WithLiteralString(expected, literal, length);
+ }
+
+ [Theory(DisplayName = "Sha3ManagedShake256 should produce the expected hash for the specified string template")]
+ [InlineData("3578a7a4", "a", 1_000_000, 4)]
+ [InlineData("3578a7a4ca913756", "a", 1_000_000, 8)]
+ [InlineData("3578a7a4ca9137569cdf76ed617d31bb", "a", 1_000_000, 16)]
+ [InlineData("3578a7a4ca9137569cdf76ed617d31bb994fca9c1bbf8b184013de8234dfd13a", "a", 1_000_000, 32)]
+ public override void TestSha3WithGeneratedString(string expected, string template, int iterations, int length)
+ {
+ HashAlgorithm = Sha3.CreateSha3Shake256(length);
+ base.TestSha3WithGeneratedString(expected, template, iterations, length);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography.UnitTests/Sha3ShakeTestBase.cs b/OnixLabs.Security.Cryptography.UnitTests/Sha3ShakeTestBase.cs
new file mode 100644
index 0000000..8e8678e
--- /dev/null
+++ b/OnixLabs.Security.Cryptography.UnitTests/Sha3ShakeTestBase.cs
@@ -0,0 +1,45 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using Xunit;
+
+namespace OnixLabs.Security.Cryptography.UnitTests
+{
+ public abstract class Sha3ShakeTestBase
+ {
+ protected Sha3 HashAlgorithm { get; set; }
+
+ public virtual void TestSha3WithLiteralString(string expected, string literal, int length)
+ {
+ string actual = ComputeHash(HashAlgorithm, literal);
+ Assert.Equal(expected, actual);
+ }
+
+ public virtual void TestSha3WithGeneratedString(string expected, string template, int iterations, int length)
+ {
+ string actual = ComputeHash(HashAlgorithm, string.Concat(Enumerable.Repeat(template, iterations)));
+ Assert.Equal(expected, actual);
+ }
+
+ private static string ComputeHash(HashAlgorithm algorithm, string plainText)
+ {
+ byte[] plainTextBytes = Encoding.Default.GetBytes(plainText);
+ byte[] hashedBytes = algorithm.ComputeHash(plainTextBytes);
+ return Hash.FromByteArray(hashedBytes).ToString();
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.Equatable.cs b/OnixLabs.Security.Cryptography/DigitalSignature.Equatable.cs
new file mode 100644
index 0000000..5750a94
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/DigitalSignature.Equatable.cs
@@ -0,0 +1,76 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a digital signature.
+ ///
+ public readonly partial struct DigitalSignature : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(DigitalSignature a, DigitalSignature b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(DigitalSignature a, DigitalSignature b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public bool Equals(DigitalSignature other)
+ {
+ return other.value.SequenceEqual(value);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return obj is DigitalSignature other && Equals(other);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(value);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.From.cs b/OnixLabs.Security.Cryptography/DigitalSignature.From.cs
new file mode 100644
index 0000000..0b4c3ee
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/DigitalSignature.From.cs
@@ -0,0 +1,78 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a digital signature.
+ ///
+ public readonly partial struct DigitalSignature
+ {
+ ///
+ /// Creates a instance from the specified digitally signed data.
+ ///
+ /// The digitally signed data.
+ /// Returns a instance from the specified digitally signed data.
+ public static DigitalSignature FromByteArray(byte[] value)
+ {
+ return new DigitalSignature(value);
+ }
+
+ ///
+ /// Creates a from the specified value.
+ ///
+ /// The Base-16 from which to construct a signature value.
+ /// Returns an from the specified Base-16 value.
+ public static DigitalSignature FromBase16(Base16 value)
+ {
+ byte[] bytes = value.ToByteArray();
+ return FromByteArray(bytes);
+ }
+
+ ///
+ /// Creates a from the specified value.
+ ///
+ /// The Base-32 from which to construct a signature value.
+ /// Returns an from the specified Base-32 value.
+ public static DigitalSignature FromBase32(Base32 value)
+ {
+ byte[] bytes = value.ToByteArray();
+ return FromByteArray(bytes);
+ }
+
+ ///
+ /// Creates a from the specified value.
+ ///
+ /// The Base-58 from which to construct a signature value.
+ /// Returns an from the specified Base-58 value.
+ public static DigitalSignature FromBase58(Base58 value)
+ {
+ byte[] bytes = value.ToByteArray();
+ return FromByteArray(bytes);
+ }
+
+ ///
+ /// Creates a from the specified value.
+ ///
+ /// The Base-64 from which to construct a signature value.
+ /// Returns an from the specified Base-64 value.
+ public static DigitalSignature FromBase64(Base64 value)
+ {
+ byte[] bytes = value.ToByteArray();
+ return FromByteArray(bytes);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.To.cs b/OnixLabs.Security.Cryptography/DigitalSignature.To.cs
new file mode 100644
index 0000000..47e7fe1
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/DigitalSignature.To.cs
@@ -0,0 +1,100 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using OnixLabs.Core;
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a digital signature.
+ ///
+ public readonly partial struct DigitalSignature
+ {
+ ///
+ /// Returns a byte array containing the underlying signed data.
+ ///
+ /// A byte array containing the underlying signed data.
+ public byte[] ToByteArray()
+ {
+ return value.Copy();
+ }
+
+ ///
+ /// Returns a value that represents the underlying signature data.
+ ///
+ /// Returns a value that represents the underlying signature data.
+ public Base16 ToBase16()
+ {
+ return Base16.FromByteArray(value);
+ }
+
+ ///
+ /// Returns a value that represents the underlying signature data.
+ ///
+ /// Returns a value that represents the underlying signature data.
+ public Base32 ToBase32()
+ {
+ return ToBase32(Base32Alphabet.Default);
+ }
+
+ ///
+ /// Returns a value that represents the underlying signature data.
+ ///
+ /// The Base-32 alphabet to use to encode the signature data.
+ /// Returns a value that represents the underlying signature data.
+ public Base32 ToBase32(Base32Alphabet alphabet)
+ {
+ return Base32.FromByteArray(value, alphabet);
+ }
+
+ ///
+ /// Returns a value that represents the underlying signature data.
+ ///
+ /// Returns a value that represents the underlying signature data.
+ public Base58 ToBase58()
+ {
+ return ToBase58(Base58Alphabet.Default);
+ }
+
+ ///
+ /// Returns a value that represents the underlying signature data.
+ ///
+ /// The Base-58 alphabet to use to encode the signature data.
+ /// Returns a value that represents the underlying signature data.
+ public Base58 ToBase58(Base58Alphabet alphabet)
+ {
+ return Base58.FromByteArray(value, alphabet);
+ }
+
+ ///
+ /// Returns a value that represents the underlying signature data.
+ ///
+ /// Returns a value that represents the underlying signature data.
+ public Base64 ToBase64()
+ {
+ return Base64.FromByteArray(value);
+ }
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Convert.ToHexString(value).ToLower();
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.Validation.cs b/OnixLabs.Security.Cryptography/DigitalSignature.Validation.cs
new file mode 100644
index 0000000..0867175
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/DigitalSignature.Validation.cs
@@ -0,0 +1,98 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a digital signature.
+ ///
+ public readonly partial struct DigitalSignature
+ {
+ ///
+ /// Determines whether this is valid, given the specified unsigned data and public key.
+ ///
+ /// The unsigned data to validate.
+ /// The public key to validate.
+ /// Returns true if this is valid; otherwise, false.
+ public bool IsDataValid(byte[] unsignedData, PublicKey key)
+ {
+ return key.IsDataValid(this, unsignedData);
+ }
+
+ ///
+ /// Determines whether this is valid, given the specified unsigned hash and public key.
+ ///
+ /// The unsigned data to validate.
+ /// The public key to validate.
+ /// Returns true if this is valid; otherwise, false.
+ public bool IsHashValid(byte[] unsignedHash, PublicKey key)
+ {
+ return key.IsHashValid(this, unsignedHash);
+ }
+
+ ///
+ /// Determines whether this is valid, given the specified unsigned hash and public key.
+ ///
+ /// The unsigned data to validate.
+ /// The public key to validate.
+ /// Returns true if this is valid; otherwise, false.
+ public bool IsHashValid(Hash unsignedHash, PublicKey key)
+ {
+ byte[] unsignedHashBytes = unsignedHash.ToByteArray();
+ return IsHashValid(unsignedHashBytes, key);
+ }
+
+ ///
+ /// Verifies this .
+ ///
+ /// The unsigned data to verify.
+ /// The public key to verify.
+ /// If this was not signed by the specified key.
+ public void VerifyData(byte[] unsignedData, PublicKey key)
+ {
+ if (!IsDataValid(unsignedData, key))
+ {
+ throw new CryptographicException("The specified digital signature was not signed with this key.");
+ }
+ }
+
+ ///
+ /// Verifies this .
+ ///
+ /// The unsigned hash to verify.
+ /// The public key to verify.
+ /// If this was not signed by the specified key.
+ public void VerifyHash(byte[] unsignedHash, PublicKey key)
+ {
+ if (!IsHashValid(unsignedHash, key))
+ {
+ throw new CryptographicException("The specified digital signature was not signed with this key.");
+ }
+ }
+
+ ///
+ /// Verifies this .
+ ///
+ /// The unsigned hash to verify.
+ /// The public key to verify.
+ /// If this was not signed by the specified key.
+ public void VerifyHash(Hash unsignedHash, PublicKey key)
+ {
+ byte[] unsignedHashBytes = unsignedHash.ToByteArray();
+ VerifyHash(unsignedHashBytes, key);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/DigitalSignature.cs b/OnixLabs.Security.Cryptography/DigitalSignature.cs
new file mode 100644
index 0000000..159adea
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/DigitalSignature.cs
@@ -0,0 +1,36 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a digital signature.
+ ///
+ public readonly partial struct DigitalSignature
+ {
+ ///
+ /// The underlying digitally signed data.
+ ///
+ private readonly byte[] value;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The digitally signed data.
+ private DigitalSignature(byte[] value)
+ {
+ this.value = value;
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Export.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Export.cs
new file mode 100644
index 0000000..cac0d61
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Export.cs
@@ -0,0 +1,50 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ public sealed partial class EcdsaPrivateKey
+ {
+ ///
+ /// Exports the key in PKCS #8 format.
+ ///
+ /// Returns the key in PKCS #8 format.
+ public override byte[] ExportPkcs8Key()
+ {
+ using ECDsa privateKey = ECDsa.Create();
+
+ privateKey.ImportECPrivateKey(PrivateKeyData, out int _);
+
+ return privateKey.ExportPkcs8PrivateKey();
+ }
+
+ ///
+ /// Exports the key in encrypted PKCS #8 format.
+ ///
+ /// The password to use for encryption.
+ /// The parameters required for password based encryption.
+ /// Returns the key in encrypted PKCS #8 format.
+ public override byte[] ExportPkcs8Key(ReadOnlySpan password, PbeParameters parameters)
+ {
+ using ECDsa privateKey = ECDsa.Create();
+
+ privateKey.ImportECPrivateKey(PrivateKeyData, out int _);
+
+ return privateKey.ExportEncryptedPkcs8PrivateKey(password, parameters);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.From.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.From.cs
new file mode 100644
index 0000000..99666a5
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.From.cs
@@ -0,0 +1,80 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ public sealed partial class EcdsaPrivateKey
+ {
+ ///
+ /// Creates an from the specified key data and hash algorithm type.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPrivateKey FromByteArray(byte[] key, HashAlgorithmType type)
+ {
+ return new EcdsaPrivateKey(key, type);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPrivateKey FromBase16(Base16 key, HashAlgorithmType type)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPrivateKey FromBase32(Base32 key, HashAlgorithmType type)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPrivateKey FromBase58(Base58 key, HashAlgorithmType type)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPrivateKey FromBase64(Base64 key, HashAlgorithmType type)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Get.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Get.cs
new file mode 100644
index 0000000..3a71dda
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Get.cs
@@ -0,0 +1,38 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an ECDSA private key.
+ ///
+ public sealed partial class EcdsaPrivateKey
+ {
+ ///
+ /// Gets the public key component from this private key.
+ ///
+ /// Returns the public key component from this private key.
+ public override PublicKey GetPublicKey()
+ {
+ using ECDsa privateKey = ECDsa.Create();
+
+ privateKey.ImportECPrivateKey(PrivateKeyData, out int _);
+ byte[] publicKey = privateKey.ExportSubjectPublicKeyInfo();
+
+ return new EcdsaPublicKey(publicKey, AlgorithmType);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Import.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Import.cs
new file mode 100644
index 0000000..526316a
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Import.cs
@@ -0,0 +1,135 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ public sealed partial class EcdsaPrivateKey
+ {
+ ///
+ /// Imports a PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static EcdsaPrivateKey ImportPkcs8Key(ReadOnlySpan data, HashAlgorithmType type)
+ {
+ ECDsa privateKey = ECDsa.Create();
+
+ privateKey.ImportPkcs8PrivateKey(data, out int _);
+ byte[] bytes = privateKey.ExportECPrivateKey();
+
+ return FromByteArray(bytes, type);
+ }
+
+ ///
+ /// Imports a PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static EcdsaPrivateKey ImportPkcs8Key(byte[] data, HashAlgorithmType type)
+ {
+ ReadOnlySpan bytes = data.AsSpan();
+ return ImportPkcs8Key(bytes, type);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static EcdsaPrivateKey ImportPkcs8Key(
+ ReadOnlySpan data,
+ ReadOnlySpan password,
+ HashAlgorithmType type)
+ {
+ ECDsa privateKey = ECDsa.Create();
+
+ privateKey.ImportEncryptedPkcs8PrivateKey(password, data, out int _);
+ byte[] bytes = privateKey.ExportECPrivateKey();
+
+ return FromByteArray(bytes, type);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static EcdsaPrivateKey ImportPkcs8Key(ReadOnlySpan data, char[] password, HashAlgorithmType type)
+ {
+ ReadOnlySpan characters = password.AsSpan();
+ return ImportPkcs8Key(data, characters, type);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static EcdsaPrivateKey ImportPkcs8Key(ReadOnlySpan data, string password, HashAlgorithmType type)
+ {
+ ReadOnlySpan characters = password.AsSpan();
+ return ImportPkcs8Key(data, characters, type);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static EcdsaPrivateKey ImportPkcs8Key(byte[] data, ReadOnlySpan password, HashAlgorithmType type)
+ {
+ ReadOnlySpan bytes = data.AsSpan();
+ return ImportPkcs8Key(bytes, password, type);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static EcdsaPrivateKey ImportPkcs8Key(byte[] data, char[] password, HashAlgorithmType type)
+ {
+ ReadOnlySpan bytes = data.AsSpan();
+ return ImportPkcs8Key(bytes, password, type);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static EcdsaPrivateKey ImportPkcs8Key(byte[] data, string password, HashAlgorithmType type)
+ {
+ ReadOnlySpan bytes = data.AsSpan();
+ return ImportPkcs8Key(bytes, password, type);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Sign.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Sign.cs
new file mode 100644
index 0000000..afb1323
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.Sign.cs
@@ -0,0 +1,52 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ public sealed partial class EcdsaPrivateKey
+ {
+ ///
+ /// Computes a from the specified unsigned data.
+ ///
+ /// The unsigned data from which to compute a .
+ /// Returns a from the specified unsigned data.
+ public override DigitalSignature SignData(byte[] unsignedData)
+ {
+ using ECDsa privateKey = ECDsa.Create();
+
+ privateKey.ImportECPrivateKey(PrivateKeyData, out int _);
+ HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName();
+ byte[] signedData = privateKey.SignData(unsignedData, name);
+
+ return DigitalSignature.FromByteArray(signedData);
+ }
+
+ ///
+ /// Computes a from the specified unsigned hash.
+ ///
+ /// The unsigned hash from which to compute a .
+ /// Returns a from the specified unsigned hash.
+ public override DigitalSignature SignHash(byte[] unsignedHash)
+ {
+ using ECDsa privateKey = ECDsa.Create();
+
+ privateKey.ImportECPrivateKey(PrivateKeyData, out int _);
+ byte[] signedData = privateKey.SignHash(unsignedHash);
+
+ return DigitalSignature.FromByteArray(signedData);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/EcdsaPrivateKey.cs b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.cs
new file mode 100644
index 0000000..dea5669
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/EcdsaPrivateKey.cs
@@ -0,0 +1,31 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an ECDSA private key.
+ ///
+ public sealed partial class EcdsaPrivateKey : PrivateKey
+ {
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The private key data.
+ /// The hash algorithm type for computing signature data.
+ internal EcdsaPrivateKey(byte[] data, HashAlgorithmType type) : base(data, type)
+ {
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/EcdsaPublicKey.From.cs b/OnixLabs.Security.Cryptography/EcdsaPublicKey.From.cs
new file mode 100644
index 0000000..4d75b02
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/EcdsaPublicKey.From.cs
@@ -0,0 +1,83 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an ECDSA public key.
+ ///
+ public sealed partial class EcdsaPublicKey
+ {
+ ///
+ /// Creates an from the specified key data and hash algorithm type.
+ ///
+ /// The key data from which to construct a public key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPublicKey FromByteArray(byte[] key, HashAlgorithmType type)
+ {
+ return new EcdsaPublicKey(key, type);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPublicKey FromBase16(Base16 key, HashAlgorithmType type)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPublicKey FromBase32(Base32 key, HashAlgorithmType type)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPublicKey FromBase58(Base58 key, HashAlgorithmType type)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static EcdsaPublicKey FromBase64(Base64 key, HashAlgorithmType type)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/EcdsaPublicKey.Verify.cs b/OnixLabs.Security.Cryptography/EcdsaPublicKey.Verify.cs
new file mode 100644
index 0000000..ff61bd6
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/EcdsaPublicKey.Verify.cs
@@ -0,0 +1,57 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an ECDSA public key.
+ ///
+ public sealed partial class EcdsaPublicKey
+ {
+ ///
+ /// Determines whether the specified was signed by the private component of this public key.
+ ///
+ /// The to validate.
+ /// The unsigned data to validate.
+ /// Returns true if the specified was signed by the private component of this public key; otherwise, false.
+ public override bool IsDataValid(DigitalSignature signature, byte[] unsignedData)
+ {
+ using ECDsa publicKey = ECDsa.Create();
+
+ publicKey.ImportSubjectPublicKeyInfo(PublicKeyData, out int _);
+ byte[] signatureData = signature.ToByteArray();
+ HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName();
+
+ return publicKey.VerifyData(unsignedData, signatureData, name);
+ }
+
+ ///
+ /// Determines whether the specified was signed by the private component of this public key.
+ ///
+ /// The to validate.
+ /// The unsigned hash to validate.
+ /// Returns true if the specified was signed by the private component of this public key; otherwise, false.
+ public override bool IsHashValid(DigitalSignature signature, byte[] unsignedHash)
+ {
+ using ECDsa publicKey = ECDsa.Create();
+
+ publicKey.ImportSubjectPublicKeyInfo(PublicKeyData, out int _);
+ byte[] signatureData = signature.ToByteArray();
+
+ return publicKey.VerifyHash(unsignedHash, signatureData);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/EcdsaPublicKey.cs b/OnixLabs.Security.Cryptography/EcdsaPublicKey.cs
new file mode 100644
index 0000000..c37cec7
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/EcdsaPublicKey.cs
@@ -0,0 +1,31 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an ECDSA public key.
+ ///
+ public sealed partial class EcdsaPublicKey : PublicKey
+ {
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The public key data.
+ /// The hash algorithm type for computing signature data.
+ internal EcdsaPublicKey(byte[] data, HashAlgorithmType type) : base(data, type)
+ {
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.AllOneHash.cs b/OnixLabs.Security.Cryptography/Hash.AllOneHash.cs
new file mode 100644
index 0000000..8dc5d59
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.AllOneHash.cs
@@ -0,0 +1,63 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Creates an all-one hash of the specified length. This will create a hash of an unknown type.
+ ///
+ /// The length of the hash in bytes.
+ /// Returns an all-one of the specified length.
+ public static Hash CreateAllOneHash(int length)
+ {
+ return CreateAllOneHash(HashAlgorithmType.Unknown, length);
+ }
+
+ ///
+ /// Creates an all-one hash of the specified hash algorithm type.
+ ///
+ /// The type of hash to create.
+ /// Returns an all-one of the specified hash algorithm type.
+ public static Hash CreateAllOneHash(HashAlgorithmType type)
+ {
+ return CreateAllOneHash(type, type.Length);
+ }
+
+ ///
+ /// Creates an all-one hash of the specified hash algorithm type and length.
+ ///
+ /// The type of hash to create.
+ /// The length of the hash in bytes.
+ /// Returns an all-one of the specified hash algorithm type and length.
+ /// If the length of the hash is unexpected.
+ public static Hash CreateAllOneHash(HashAlgorithmType type, int length)
+ {
+ if (type.Length != HashAlgorithmType.UnknownLength && type.Length != length)
+ {
+ throw new ArgumentException("Unexpected hash algorithm output length.");
+ }
+
+ byte[] bytes = Enumerable.Repeat(byte.MaxValue, length).ToArray();
+ return FromByteArray(bytes, type);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.AllZeroHash.cs b/OnixLabs.Security.Cryptography/Hash.AllZeroHash.cs
new file mode 100644
index 0000000..a9af9be
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.AllZeroHash.cs
@@ -0,0 +1,63 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Creates an all-zero hash of the specified length. This will create a hash of an unknown type.
+ ///
+ /// The length of the hash in bytes.
+ /// Returns an all-zero of the specified length.
+ public static Hash CreateAllZeroHash(int length)
+ {
+ return CreateAllZeroHash(HashAlgorithmType.Unknown, length);
+ }
+
+ ///
+ /// Creates an all-zero hash of the specified hash algorithm type.
+ ///
+ /// The type of hash to create.
+ /// Returns an all-zero of the specified hash algorithm type.
+ public static Hash CreateAllZeroHash(HashAlgorithmType type)
+ {
+ return CreateAllZeroHash(type, type.Length);
+ }
+
+ ///
+ /// Creates an all-zero hash of the specified hash algorithm type and length.
+ ///
+ /// The type of hash to create.
+ /// The length of the hash in bytes.
+ /// Returns an all-zero of the specified hash algorithm type and length.
+ /// If the length of the hash is unexpected.
+ public static Hash CreateAllZeroHash(HashAlgorithmType type, int length)
+ {
+ if (type.Length != HashAlgorithmType.UnknownLength && type.Length != length)
+ {
+ throw new ArgumentException("Unexpected hash algorithm output length.");
+ }
+
+ byte[] bytes = Enumerable.Repeat(byte.MinValue, length).ToArray();
+ return FromByteArray(bytes, type);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Md5Hash.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Md5Hash.cs
new file mode 100644
index 0000000..72d55b9
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Md5Hash.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes an MD5 hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeMd5Hash(string value)
+ {
+ return ComputeMd5Hash(value, Encoding.Default);
+ }
+
+ ///
+ /// Computes an MD5 hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a of the input value.
+ public static Hash ComputeMd5Hash(string value, Encoding encoding)
+ {
+ return ComputeHash(value, HashAlgorithmType.Md5Hash, encoding);
+ }
+
+ ///
+ /// Computes an MD5 hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeMd5Hash(byte[] value)
+ {
+ return ComputeHash(value, HashAlgorithmType.Md5Hash);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha1Hash.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha1Hash.cs
new file mode 100644
index 0000000..f643078
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha1Hash.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-1 hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha1Hash(string value)
+ {
+ return ComputeSha1Hash(value, Encoding.Default);
+ }
+
+ ///
+ /// Computes a SHA-1 hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a of the input value.
+ public static Hash ComputeSha1Hash(string value, Encoding encoding)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha1Hash, encoding);
+ }
+
+ ///
+ /// Computes a SHA-1 hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha1Hash(byte[] value)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha1Hash);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash256.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash256.cs
new file mode 100644
index 0000000..008dc3f
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash256.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-2 256-bit hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha2Hash256(string value)
+ {
+ return ComputeSha2Hash256(value, Encoding.Default);
+ }
+
+ ///
+ /// Computes a SHA-2 256-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a of the input value.
+ public static Hash ComputeSha2Hash256(string value, Encoding encoding)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha2Hash256, encoding);
+ }
+
+ ///
+ /// Computes a SHA-2 256-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha2Hash256(byte[] value)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha2Hash256);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash384.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash384.cs
new file mode 100644
index 0000000..a827878
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash384.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-2 384-bit hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha2Hash384(string value)
+ {
+ return ComputeSha2Hash384(value, Encoding.Default);
+ }
+
+ ///
+ /// Computes a SHA-2 384-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a of the input value.
+ public static Hash ComputeSha2Hash384(string value, Encoding encoding)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha2Hash384, encoding);
+ }
+
+ ///
+ /// Computes a SHA-2 384-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha2Hash384(byte[] value)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha2Hash384);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash512.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash512.cs
new file mode 100644
index 0000000..a709e08
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash512.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-2 512-bit hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha2Hash512(string value)
+ {
+ return ComputeSha2Hash512(value, Encoding.Default);
+ }
+
+ ///
+ /// Computes a SHA-2 512-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a of the input value.
+ public static Hash ComputeSha2Hash512(string value, Encoding encoding)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha2Hash512, encoding);
+ }
+
+ ///
+ /// Computes a SHA-2 512-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha2Hash512(byte[] value)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha2Hash512);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash224.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash224.cs
new file mode 100644
index 0000000..10c9205
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash224.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-3 224-bit hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash224(string value)
+ {
+ return ComputeSha3Hash224(value, Encoding.Default);
+ }
+
+ ///
+ /// Computes a SHA-3 224-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash224(string value, Encoding encoding)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Hash224, encoding);
+ }
+
+ ///
+ /// Computes a SHA-3 224-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash224(byte[] value)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Hash224);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash256.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash256.cs
new file mode 100644
index 0000000..eab6ae2
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash256.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-3 256-bit hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash256(string value)
+ {
+ return ComputeSha3Hash256(value, Encoding.Default);
+ }
+
+ ///
+ /// Computes a SHA-3 256-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash256(string value, Encoding encoding)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Hash256, encoding);
+ }
+
+ ///
+ /// Computes a SHA-3 256-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash256(byte[] value)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Hash256);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash384.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash384.cs
new file mode 100644
index 0000000..1faf641
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash384.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-3 384-bit hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash384(string value)
+ {
+ return ComputeSha3Hash384(value, Encoding.Default);
+ }
+
+ ///
+ /// Computes a SHA-3 384-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash384(string value, Encoding encoding)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Hash384, encoding);
+ }
+
+ ///
+ /// Computes a SHA-3 384-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash384(byte[] value)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Hash384);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash512.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash512.cs
new file mode 100644
index 0000000..88ea926
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash512.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-3 512-bit hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash512(string value)
+ {
+ return ComputeSha3Hash512(value, Encoding.Default);
+ }
+
+ ///
+ /// Computes a SHA-3 512-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash512(string value, Encoding encoding)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Hash512, encoding);
+ }
+
+ ///
+ /// Computes a SHA-3 512-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Hash512(byte[] value)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Hash512);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake128.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake128.cs
new file mode 100644
index 0000000..3b86b2d
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake128.cs
@@ -0,0 +1,59 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-3 Shake 128-bit hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// The length of the hash in bytes.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Shake128(string value, int length)
+ {
+ return ComputeSha3Shake128(value, Encoding.Default, length);
+ }
+
+ ///
+ /// Computes a SHA-3 Shake 128-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// The length of the hash in bytes.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Shake128(string value, Encoding encoding, int length)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Shake128, encoding, length);
+ }
+
+ ///
+ /// Computes a SHA-3 Shake 128-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The length of the hash in bytes.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Shake128(byte[] value, int length)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Shake128, length);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake256.cs b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake256.cs
new file mode 100644
index 0000000..4a49cb1
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake256.cs
@@ -0,0 +1,59 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a SHA-3 Shake 256-bit hash from the specified value.
+ /// This will use the default encoding to convert the input string into a byte array.
+ ///
+ /// The input value to hash.
+ /// The length of the hash in bytes.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Shake256(string value, int length)
+ {
+ return ComputeSha3Shake256(value, Encoding.Default, length);
+ }
+
+ ///
+ /// Computes a SHA-3 Shake 256-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// The length of the hash in bytes.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Shake256(string value, Encoding encoding, int length)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Shake256, encoding, length);
+ }
+
+ ///
+ /// Computes a SHA-3 Shake 256-bit hash from the specified value.
+ ///
+ /// The input value to hash.
+ /// The length of the hash in bytes.
+ /// Returns a of the input value.
+ public static Hash ComputeSha3Shake256(byte[] value, int length)
+ {
+ return ComputeHash(value, HashAlgorithmType.Sha3Shake256, length);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Compute.cs b/OnixLabs.Security.Cryptography/Hash.Compute.cs
new file mode 100644
index 0000000..5884b76
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Compute.cs
@@ -0,0 +1,100 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes the hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// Returns a representing a hash of the specified value.
+ public static Hash ComputeHash(string value, HashAlgorithmType type)
+ {
+ return ComputeHash(value, type, Encoding.Default);
+ }
+
+ ///
+ /// Computes the hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a representing a hash of the specified value.
+ public static Hash ComputeHash(string value, HashAlgorithmType type, Encoding encoding)
+ {
+ return ComputeHash(encoding.GetBytes(value), type);
+ }
+
+ ///
+ /// Computes the hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// The output length of the computed hash in bytes.
+ /// Returns a representing a hash of the specified value.
+ public static Hash ComputeHash(string value, HashAlgorithmType type, int length)
+ {
+ return ComputeHash(value, type, Encoding.Default, length);
+ }
+
+ ///
+ /// Computes the hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// The output length of the computed hash in bytes.
+ /// Returns a representing a hash of the specified value.
+ public static Hash ComputeHash(string value, HashAlgorithmType type, Encoding encoding, int length)
+ {
+ return ComputeHash(encoding.GetBytes(value), type, length);
+ }
+
+ ///
+ /// Computes the hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// Returns a representing a hash of the specified value.
+ public static Hash ComputeHash(byte[] value, HashAlgorithmType type)
+ {
+ using HashAlgorithm algorithm = type.GetHashAlgorithm();
+ byte[] hashedValue = algorithm.ComputeHash(value);
+ return FromByteArray(hashedValue, type);
+ }
+
+ ///
+ /// Computes the hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// The output length of the computed hash in bytes.
+ /// Returns a representing a hash of the specified value.
+ public static Hash ComputeHash(byte[] value, HashAlgorithmType type, int length)
+ {
+ using HashAlgorithm algorithm = type.GetHashAlgorithm(length);
+ byte[] hashedValue = algorithm.ComputeHash(value);
+ return FromByteArray(hashedValue, type);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.ComputeTwice.cs b/OnixLabs.Security.Cryptography/Hash.ComputeTwice.cs
new file mode 100644
index 0000000..aeb09f5
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.ComputeTwice.cs
@@ -0,0 +1,102 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+using System.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Computes a twice-hashed hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// Returns a representing a twice-hashed hash of the specified value.
+ public static Hash ComputeHashTwice(string value, HashAlgorithmType type)
+ {
+ return ComputeHashTwice(value, type, Encoding.Default);
+ }
+
+ ///
+ /// Computes a twice-hashed hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// Returns a representing a twice-hashed hash of the specified value.
+ public static Hash ComputeHashTwice(string value, HashAlgorithmType type, Encoding encoding)
+ {
+ return ComputeHashTwice(encoding.GetBytes(value), type);
+ }
+
+ ///
+ /// Computes a twice-hashed hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// The output length of the computed hash in bytes.
+ /// Returns a representing a twice-hashed hash of the specified value.
+ public static Hash ComputeHashTwice(string value, HashAlgorithmType type, int length)
+ {
+ return ComputeHashTwice(value, type, Encoding.Default, length);
+ }
+
+ ///
+ /// Computes a twice-hashed hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// The encoding which will be used to convert the input string into a byte array.
+ /// The output length of the computed hash in bytes.
+ /// Returns a representing a twice-hashed hash of the specified value.
+ public static Hash ComputeHashTwice(string value, HashAlgorithmType type, Encoding encoding, int length)
+ {
+ return ComputeHashTwice(encoding.GetBytes(value), type, length);
+ }
+
+ ///
+ /// Computes a twice-hashed hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// Returns a representing a twice-hashed hash of the specified value.
+ public static Hash ComputeHashTwice(byte[] value, HashAlgorithmType type)
+ {
+ using HashAlgorithm algorithm = type.GetHashAlgorithm();
+ byte[] firstRoundValue = algorithm.ComputeHash(value);
+ byte[] secondRoundValue = algorithm.ComputeHash(firstRoundValue);
+ return FromByteArray(secondRoundValue, type);
+ }
+
+ ///
+ /// Computes a twice-hashed hash of the specified value using the specified .
+ ///
+ /// The value for which to compute a hash.
+ /// The hash algorithm type of the computed hash.
+ /// The output length of the computed hash in bytes.
+ /// Returns a representing a twice-hashed hash of the specified value.
+ public static Hash ComputeHashTwice(byte[] value, HashAlgorithmType type, int length)
+ {
+ using HashAlgorithm algorithm = type.GetHashAlgorithm(length);
+ byte[] firstRoundValue = algorithm.ComputeHash(value);
+ byte[] secondRoundValue = algorithm.ComputeHash(firstRoundValue);
+ return FromByteArray(secondRoundValue, type);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Concantenate.cs b/OnixLabs.Security.Cryptography/Hash.Concantenate.cs
new file mode 100644
index 0000000..5ea4d5b
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Concantenate.cs
@@ -0,0 +1,83 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ public readonly partial struct Hash
+ {
+ ///
+ /// Concatenates two hashes.
+ ///
+ /// The first to concatenate.
+ /// The second to concatenate.
+ /// Returns the concatenation of the two instances.
+ public static Hash Concatenate(Hash a, Hash b)
+ {
+ if (a.AlgorithmType != b.AlgorithmType)
+ {
+ throw new ArgumentException("Cannot concatenate hashes of different algorithm types.");
+ }
+
+ using HashAlgorithm algorithm = a.AlgorithmType.GetHashAlgorithm();
+ byte[] concatenatedValue = a.ToByteArray().Concat(b.ToByteArray()).ToArray();
+ byte[] hashedValue = algorithm.ComputeHash(concatenatedValue);
+ return FromByteArray(hashedValue, a.AlgorithmType);
+ }
+
+ ///
+ /// Concatenates two hashes.
+ ///
+ /// The first to concatenate.
+ /// The second to concatenate.
+ /// The length of the hash in bytes.
+ /// Returns the concatenation of the two instances.
+ public static Hash Concatenate(Hash a, Hash b, int length)
+ {
+ if (a.AlgorithmType != b.AlgorithmType)
+ {
+ throw new ArgumentException("Cannot concatenate hashes of different algorithm types.");
+ }
+
+ using HashAlgorithm algorithm = a.AlgorithmType.GetHashAlgorithm(length);
+ byte[] concatenatedValue = a.ToByteArray().Concat(b.ToByteArray()).ToArray();
+ byte[] hashedValue = algorithm.ComputeHash(concatenatedValue);
+ return FromByteArray(hashedValue, a.AlgorithmType);
+ }
+
+ ///
+ /// Concatenates this hash with another hash.
+ ///
+ /// The other hash to concatenate with this hash.
+ /// Returns the concatenation of the two instances.
+ public Hash Concatenate(Hash other)
+ {
+ return Concatenate(this, other);
+ }
+
+ ///
+ /// Concatenates this hash with another hash.
+ ///
+ /// The other hash to concatenate with this hash.
+ /// The length of the hash in bytes.
+ /// Returns the concatenation of the two instances.
+ public Hash Concatenate(Hash other, int length)
+ {
+ return Concatenate(this, other, length);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Equatable.cs b/OnixLabs.Security.Cryptography/Hash.Equatable.cs
new file mode 100644
index 0000000..f7f25c1
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Equatable.cs
@@ -0,0 +1,76 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(Hash a, Hash b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(Hash a, Hash b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public bool Equals(Hash other)
+ {
+ return other.value.SequenceEqual(value) && other.AlgorithmType == AlgorithmType;
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return obj is Hash hash && Equals(hash);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(value, AlgorithmType);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.From.cs b/OnixLabs.Security.Cryptography/Hash.From.cs
new file mode 100644
index 0000000..c6c6828
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.From.cs
@@ -0,0 +1,90 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Creates a instance from a array.
+ /// This will create a hash of an unknown type.
+ ///
+ /// The array to represent as a hash.
+ /// A new instance.
+ public static Hash FromByteArray(byte[] value)
+ {
+ return FromByteArray(value, HashAlgorithmType.Unknown);
+ }
+
+ ///
+ /// Creates a instance from a array.
+ ///
+ /// The array to represent as a hash.
+ /// The hash algorithm type of the hash.
+ /// A new instance.
+ public static Hash FromByteArray(byte[] value, HashAlgorithmType type)
+ {
+ return new Hash(value, type);
+ }
+
+ ///
+ /// Creates a from the specified value.
+ ///
+ /// The Base-16 from which to construct a hash value.
+ /// Returns an from the specified Base-16 value.
+ public static Hash FromBase16(Base16 value)
+ {
+ byte[] bytes = value.ToByteArray();
+ return FromByteArray(bytes);
+ }
+
+ ///
+ /// Creates a from the specified value.
+ ///
+ /// The Base-32 from which to construct a hash value.
+ /// Returns an from the specified Base-32 value.
+ public static Hash FromBase32(Base32 value)
+ {
+ byte[] bytes = value.ToByteArray();
+ return FromByteArray(bytes);
+ }
+
+ ///
+ /// Creates a from the specified value.
+ ///
+ /// The Base-58 from which to construct a hash value.
+ /// Returns an from the specified Base-58 value.
+ public static Hash FromBase58(Base58 value)
+ {
+ byte[] bytes = value.ToByteArray();
+ return FromByteArray(bytes);
+ }
+
+ ///
+ /// Creates a from the specified value.
+ ///
+ /// The Base-64 from which to construct a hash value.
+ /// Returns an from the specified Base-64 value.
+ public static Hash FromBase64(Base64 value)
+ {
+ byte[] bytes = value.ToByteArray();
+ return FromByteArray(bytes);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.Parse.cs b/OnixLabs.Security.Cryptography/Hash.Parse.cs
new file mode 100644
index 0000000..9b3d8d0
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.Parse.cs
@@ -0,0 +1,47 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Converts a hexadecimal representation of a hash into a instance.
+ /// This will create a hash of an unknown type.
+ ///
+ /// A that contains a hash to convert.
+ /// A new instance.
+ public static Hash Parse(string value)
+ {
+ return Parse(value, HashAlgorithmType.Unknown);
+ }
+
+ ///
+ /// Converts a hexadecimal representation of a hash into a instance.
+ ///
+ /// A that contains a hash to convert.
+ /// The hash algorithm type of the hash.
+ /// A new instance.
+ public static Hash Parse(string value, HashAlgorithmType type)
+ {
+ byte[] bytes = Convert.FromHexString(value);
+ return new Hash(bytes, type);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.To.cs b/OnixLabs.Security.Cryptography/Hash.To.cs
new file mode 100644
index 0000000..49c80d2
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.To.cs
@@ -0,0 +1,100 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using OnixLabs.Core;
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// Returns a array containing the underlying hash data.
+ ///
+ /// A array containing the underlying hash data.
+ public byte[] ToByteArray()
+ {
+ return value.Copy();
+ }
+
+ ///
+ /// Returns a value that represents the underlying hash data.
+ ///
+ /// Returns a value that represents the underlying hash data.
+ public Base16 ToBase16()
+ {
+ return Base16.FromByteArray(value);
+ }
+
+ ///
+ /// Returns a value that represents the underlying hash data.
+ ///
+ /// Returns a value that represents the underlying hash data.
+ public Base32 ToBase32()
+ {
+ return ToBase32(Base32Alphabet.Default);
+ }
+
+ ///
+ /// Returns a value that represents the underlying hash data.
+ ///
+ /// The Base-32 alphabet to use to encode the hash data.
+ /// Returns a value that represents the underlying hash data.
+ public Base32 ToBase32(Base32Alphabet alphabet)
+ {
+ return Base32.FromByteArray(value, alphabet);
+ }
+
+ ///
+ /// Returns a value that represents the underlying hash data.
+ ///
+ /// Returns a value that represents the underlying hash data.
+ public Base58 ToBase58()
+ {
+ return ToBase58(Base58Alphabet.Default);
+ }
+
+ ///
+ /// Returns a value that represents the underlying hash data.
+ ///
+ /// The Base-58 alphabet to use to encode the hash data.
+ /// Returns a value that represents the underlying hash data.
+ public Base58 ToBase58(Base58Alphabet alphabet)
+ {
+ return Base58.FromByteArray(value, alphabet);
+ }
+
+ ///
+ /// Returns a value that represents the underlying hash data.
+ ///
+ /// Returns a value that represents the underlying hash data.
+ public Base64 ToBase64()
+ {
+ return Base64.FromByteArray(value);
+ }
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Convert.ToHexString(value).ToLower();
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Hash.cs b/OnixLabs.Security.Cryptography/Hash.cs
new file mode 100644
index 0000000..99ca022
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Hash.cs
@@ -0,0 +1,45 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic hash.
+ ///
+ public readonly partial struct Hash
+ {
+ ///
+ /// The underlying hexadecimal value of the hash.
+ ///
+ private readonly byte[] value;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The underlying hexadecimal value of the hash.
+ /// The hash algorithm type of the hash.
+ private Hash(byte[] value, HashAlgorithmType type)
+ {
+ type.VerifyHashLength(value);
+
+ this.value = value;
+ AlgorithmType = type;
+ }
+
+ ///
+ /// Gets the hash algorithm type of the hash.
+ ///
+ public HashAlgorithmType AlgorithmType { get; }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/HashAlgorithmType.cs b/OnixLabs.Security.Cryptography/HashAlgorithmType.cs
new file mode 100644
index 0000000..1ed691f
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/HashAlgorithmType.cs
@@ -0,0 +1,199 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Security.Cryptography;
+using OnixLabs.Core;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Specifies values that define known hash algorithm types.
+ ///
+ public sealed class HashAlgorithmType : Enumeration
+ {
+ ///
+ /// A constant that defines an unknown hash algorithm length.
+ ///
+ public const int UnknownLength = -1;
+
+ ///
+ /// An unknown hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Unknown = new(0, nameof(Unknown), UnknownLength);
+
+ ///
+ /// The MD5 hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Md5Hash = new(1, nameof(Md5Hash), 16);
+
+ ///
+ /// The SHA-1 hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha1Hash = new(3, nameof(Sha1Hash), 20);
+
+ ///
+ /// The SHA-2 256-bit hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha2Hash256 = new(4, nameof(Sha2Hash256), 32);
+
+ ///
+ /// The SHA-2 384-bit hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha2Hash384 = new(5, nameof(Sha2Hash384), 48);
+
+ ///
+ /// The SHA-2 512-bit hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha2Hash512 = new(6, nameof(Sha2Hash512), 64);
+
+ ///
+ /// The SHA-3 224-bit hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha3Hash224 = new(7, nameof(Sha3Hash224), 28);
+
+ ///
+ /// The SHA-3 256-bit hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha3Hash256 = new(8, nameof(Sha3Hash256), 32);
+
+ ///
+ /// The SHA-3 384-bit hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha3Hash384 = new(9, nameof(Sha3Hash384), 48);
+
+ ///
+ /// The SHA-3 512-bit hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha3Hash512 = new(10, nameof(Sha3Hash512), 64);
+
+ ///
+ /// The SHA-3 Shake 128-bit hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha3Shake128 = new(11, nameof(Sha3Shake128), UnknownLength);
+
+ ///
+ /// The SHA-3 Shake 256-bit hash algorithm.
+ ///
+ public static readonly HashAlgorithmType Sha3Shake256 = new(12, nameof(Sha3Shake256), UnknownLength);
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The value of the hash algorithm type.
+ /// The name of the hash algorithm type.
+ /// The length in bytes of the hash algorithm type.
+ private HashAlgorithmType(int value, string name, int length) : base(value, name)
+ {
+ Length = length;
+ }
+
+ ///
+ /// Gets the length of an algorithm's hash in bytes.
+ /// -1 Means that the algorithm's hash is of variable length, or is unknown.
+ ///
+ public int Length { get; }
+
+ ///
+ /// Gets the hash algorithm for this instance.
+ ///
+ /// Specifies the expected output hash length.
+ /// Returns a for this .
+ /// If the hash algorithm does not expect an output length.
+ /// If the hash algorithm is unknown.
+ public HashAlgorithm GetHashAlgorithm(int length = UnknownLength)
+ {
+ if (length != UnknownLength && Length > UnknownLength)
+ {
+ throw new ArgumentException($"Output length not expected for the specified hash algorithm: {Name}");
+ }
+
+ return Name switch
+ {
+ nameof(Md5Hash) => MD5.Create(),
+ nameof(Sha1Hash) => SHA1.Create(),
+ nameof(Sha2Hash256) => SHA256.Create(),
+ nameof(Sha2Hash384) => SHA384.Create(),
+ nameof(Sha2Hash512) => SHA512.Create(),
+ nameof(Sha3Hash224) => Sha3.CreateSha3Hash224(),
+ nameof(Sha3Hash256) => Sha3.CreateSha3Hash256(),
+ nameof(Sha3Hash384) => Sha3.CreateSha3Hash384(),
+ nameof(Sha3Hash512) => Sha3.CreateSha3Hash512(),
+ nameof(Sha3Shake128) => Sha3.CreateSha3Shake128(length),
+ nameof(Sha3Shake256) => Sha3.CreateSha3Shake256(length),
+ _ => throw new ArgumentException($"Unknown hash algorithm: {Name}.")
+ };
+ }
+
+ ///
+ /// Gets the equivalent for this .
+ ///
+ /// Returns the equivalent for this .
+ /// If there is no corresponding equivalent for this .
+ public HashAlgorithmName GetHashAlgorithmName()
+ {
+ return Name switch
+ {
+ nameof(Md5Hash) => HashAlgorithmName.MD5,
+ nameof(Sha1Hash) => HashAlgorithmName.SHA1,
+ nameof(Sha2Hash256) => HashAlgorithmName.SHA256,
+ nameof(Sha2Hash384) => HashAlgorithmName.SHA384,
+ nameof(Sha2Hash512) => HashAlgorithmName.SHA512,
+ _ => throw new ArgumentException($"No corresponding {nameof(HashAlgorithmName)} for {Name}.")
+ };
+ }
+
+ ///
+ /// Determines whether the length of a byte array is valid.
+ ///
+ /// The byte array to length check.
+ /// Returns true if the length of the byte array is valid; otherwise, false.
+ public bool IsValidHashLength(byte[] value)
+ {
+ return Length == -1 || Length == value.Length;
+ }
+
+ ///
+ /// Verifies that the length of a byte array is valid.
+ ///
+ /// The byte array to length check.
+ /// If the length of the byte array is invalid.
+ public void VerifyHashLength(byte[] value)
+ {
+ if (!IsValidHashLength(value))
+ {
+ throw new ArgumentException("The length of the hash is invalid.", nameof(value));
+ }
+ }
+
+ ///
+ /// Checks for equality between this and another .
+ /// This will return false if either this, or the other is .
+ ///
+ /// The object to check for equality.
+ ///
+ /// Returns true if this is equal to the other; otherwise, false.
+ /// If either is , this always returns false.
+ ///
+ public override bool Equals(HashAlgorithmType? other)
+ {
+ if (ReferenceEquals(this, Unknown) || ReferenceEquals(other, Unknown))
+ {
+ return false;
+ }
+
+ return base.Equals(other);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/KeyPair.cs b/OnixLabs.Security.Cryptography/KeyPair.cs
new file mode 100644
index 0000000..f1b4ca4
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/KeyPair.cs
@@ -0,0 +1,209 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a cryptographic public/private key pair.
+ ///
+ public sealed class KeyPair : IEquatable
+ {
+ ///
+ /// Prevents new instances of the class from being created.
+ ///
+ /// The private key component of this key pair.
+ /// The public key component of this key pair.
+ private KeyPair(PrivateKey privateKey, PublicKey publicKey)
+ {
+ PrivateKey = privateKey;
+ PublicKey = publicKey;
+ }
+
+ ///
+ /// Gets the private key component of this key pair.
+ ///
+ public PrivateKey PrivateKey { get; }
+
+ ///
+ /// Gets the public key component of this key pair.
+ ///
+ public PublicKey PublicKey { get; }
+
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(KeyPair a, KeyPair b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(KeyPair a, KeyPair b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Creates an ECDSA public/private key pair.
+ ///
+ /// The for computing signature data.
+ /// Returns an ECDSA public/private key pair.
+ public static KeyPair CreateEcdsaKeyPair(HashAlgorithmType type)
+ {
+ using ECDsa asymmetricAlgorithm = ECDsa.Create();
+
+ PrivateKey privateKey = new EcdsaPrivateKey(asymmetricAlgorithm.ExportECPrivateKey(), type);
+ PublicKey publicKey = new EcdsaPublicKey(asymmetricAlgorithm.ExportSubjectPublicKeyInfo(), type);
+
+ return new KeyPair(privateKey, publicKey);
+ }
+
+ ///
+ /// Creates an ECDSA public/private key pair.
+ ///
+ /// The for computing signature data.
+ /// The curve to use for key generation.
+ /// Returns an ECDSA public/private key pair.
+ public static KeyPair CreateEcdsaKeyPair(HashAlgorithmType type, ECCurve curve)
+ {
+ using ECDsa asymmetricAlgorithm = ECDsa.Create(curve);
+
+ PrivateKey privateKey = new EcdsaPrivateKey(asymmetricAlgorithm.ExportECPrivateKey(), type);
+ PublicKey publicKey = new EcdsaPublicKey(asymmetricAlgorithm.ExportSubjectPublicKeyInfo(), type);
+
+ return new KeyPair(privateKey, publicKey);
+ }
+
+ ///
+ /// Creates an ECDSA public/private key pair.
+ ///
+ /// The for computing signature data.
+ /// The parameters representing the key to use.
+ /// Returns an ECDSA public/private key pair.
+ public static KeyPair CreateEcdsaKeyPair(HashAlgorithmType type, ECParameters parameters)
+ {
+ using ECDsa asymmetricAlgorithm = ECDsa.Create(parameters);
+
+ PrivateKey privateKey = new EcdsaPrivateKey(asymmetricAlgorithm.ExportECPrivateKey(), type);
+ PublicKey publicKey = new EcdsaPublicKey(asymmetricAlgorithm.ExportSubjectPublicKeyInfo(), type);
+
+ return new KeyPair(privateKey, publicKey);
+ }
+
+ ///
+ /// Creates an RSA public/private key pair.
+ ///
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an ECDSA public/private key pair.
+ public static KeyPair CreateRsaKeyPair(HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ using RSA asymmetricAlgorithm = RSA.Create();
+
+ PrivateKey privateKey = new RsaPrivateKey(asymmetricAlgorithm.ExportRSAPrivateKey(), type, padding);
+ PublicKey publicKey = new RsaPublicKey(asymmetricAlgorithm.ExportRSAPublicKey(), type, padding);
+
+ return new KeyPair(privateKey, publicKey);
+ }
+
+ ///
+ /// Creates an RSA public/private key pair.
+ ///
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// The key size, in bits.
+ /// Returns an ECDSA public/private key pair.
+ public static KeyPair CreateRsaKeyPair(HashAlgorithmType type, RSASignaturePadding padding, int keySizeInBits)
+ {
+ using RSA asymmetricAlgorithm = RSA.Create(keySizeInBits);
+
+ PrivateKey privateKey = new RsaPrivateKey(asymmetricAlgorithm.ExportRSAPrivateKey(), type, padding);
+ PublicKey publicKey = new RsaPublicKey(asymmetricAlgorithm.ExportRSAPublicKey(), type, padding);
+
+ return new KeyPair(privateKey, publicKey);
+ }
+
+ ///
+ /// Creates an RSA public/private key pair.
+ ///
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// The parameters for the RSA algorithm.
+ /// Returns an ECDSA public/private key pair.
+ public static KeyPair CreateRsaKeyPair(
+ HashAlgorithmType type,
+ RSASignaturePadding padding,
+ RSAParameters parameters)
+ {
+ using RSA asymmetricAlgorithm = RSA.Create(parameters);
+
+ PrivateKey privateKey = new RsaPrivateKey(asymmetricAlgorithm.ExportRSAPrivateKey(), type, padding);
+ PublicKey publicKey = new RsaPublicKey(asymmetricAlgorithm.ExportRSAPublicKey(), type, padding);
+
+ return new KeyPair(privateKey, publicKey);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public bool Equals(KeyPair? other)
+ {
+ return ReferenceEquals(this, other)
+ || other is not null
+ && other.PrivateKey == PrivateKey
+ && other.PublicKey == PublicKey;
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return Equals(obj as KeyPair);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(PrivateKey, PublicKey);
+ }
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return $"{PrivateKey}:{PublicKey}";
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/MerkleTree.Equatable.cs b/OnixLabs.Security.Cryptography/MerkleTree.Equatable.cs
new file mode 100644
index 0000000..9ac7144
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/MerkleTree.Equatable.cs
@@ -0,0 +1,77 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a Merkle tree.
+ ///
+ public abstract partial class MerkleTree : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(MerkleTree a, MerkleTree b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(MerkleTree a, MerkleTree b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public virtual bool Equals(MerkleTree? other)
+ {
+ return ReferenceEquals(this, other)
+ || other is not null
+ && other.Hash == Hash;
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return Equals(obj as MerkleTree);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(GetType(), Hash);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/MerkleTree.cs b/OnixLabs.Security.Cryptography/MerkleTree.cs
new file mode 100644
index 0000000..df646e8
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/MerkleTree.cs
@@ -0,0 +1,146 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using OnixLabs.Core.Linq;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a Merkle tree.
+ ///
+ public abstract partial class MerkleTree
+ {
+ ///
+ /// Prevents an instance of from being created.
+ ///
+ internal MerkleTree()
+ {
+ }
+
+ ///
+ /// Gets the hash of this node.
+ ///
+ public abstract Hash Hash { get; }
+
+ ///
+ /// Builds a Merkle tree from the specified leaf node hashes.
+ ///
+ /// The leaf nodes from which to build a Merkle tree.
+ /// Returns a new instance from the specified leaf nodes.
+ public static MerkleTree Build(IEnumerable nodes)
+ {
+ IReadOnlyList leafNodes = nodes
+ .Select(MerkleTreeLeafNode.CreateHashNode)
+ .ToList();
+
+ return Build(leafNodes);
+ }
+
+ ///
+ /// Builds a Merkle tree from the specified Merkle tree nodes.
+ ///
+ /// The Merkle tree nodes from which to build a Merkle tree.
+ /// Returns a new instance from the specified nodes.
+ private static MerkleTree Build(IReadOnlyList nodes)
+ {
+ CheckIfMerkleTreesAreEmpty(nodes);
+ CheckNodesHaveEqualHashAlgorithms(nodes);
+
+ return MergeMerkleTreeNodes(nodes);
+ }
+
+ ///
+ /// Checks whether an is empty.
+ ///
+ /// The to check.
+ /// if the is empty.
+ private static void CheckIfMerkleTreesAreEmpty(IEnumerable merkleTrees)
+ {
+ if (merkleTrees.IsEmpty())
+ {
+ throw new ArgumentException("Cannot construct a merkle tree from an empty list.");
+ }
+ }
+
+ ///
+ /// Checks whether all elements of an have the same hash algorithm type.
+ ///
+ /// The to check.
+ /// if the elements of the do not have the same hash algorithm type.
+ private static void CheckNodesHaveEqualHashAlgorithms(IReadOnlyList merkleTrees)
+ {
+ if (!merkleTrees.AllEqualBy(merkleTree => merkleTree.Hash.AlgorithmType))
+ {
+ throw new ArgumentException("Cannot construct a merkle tree with different hash types.");
+ }
+ }
+
+ ///
+ /// Ensures that an has an even number of elements.
+ /// If the contains an odd number of elements, then an all-zero hash
+ /// of the same will be inserted at the end of the list.
+ ///
+ /// The to ensure has an even number of elements.
+ /// Returns a new containing an even number of elements.
+ private static IReadOnlyList EnsureEvenNumberOfNodes(IReadOnlyList merkleTrees)
+ {
+ if (!merkleTrees.IsOddCount()) return merkleTrees;
+
+ HashAlgorithmType hashAlgorithmType = merkleTrees[0].Hash.AlgorithmType;
+ return merkleTrees.Append(MerkleTreeLeafNode.CreateEmptyNode(hashAlgorithmType)).ToList();
+ }
+
+ ///
+ /// Merges an of Merkle tree nodes.
+ ///
+ /// The of Merkle tree nodes to merge.
+ /// Returns a merged of Merkle tree nodes.
+ private static MerkleTree MergeMerkleTreeNodes(IReadOnlyList merkleTrees)
+ {
+ while (true)
+ {
+ if (merkleTrees.IsSingle())
+ {
+ return merkleTrees[0];
+ }
+
+ merkleTrees = EnsureEvenNumberOfNodes(merkleTrees);
+
+ List mutableMerkleTrees = new();
+
+ for (int index = 0; index < merkleTrees.Count; index += 2)
+ {
+ MerkleTree leftMerkleTree = merkleTrees[index];
+ MerkleTree rightMerkleTree = merkleTrees[index + 1];
+ MerkleTree merkleTreeNode = new MerkleTreeBranchNode(leftMerkleTree, rightMerkleTree);
+ mutableMerkleTrees.Add(merkleTreeNode);
+ }
+
+ merkleTrees = mutableMerkleTrees;
+ }
+ }
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Hash.ToString();
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/MerkleTreeBranchNode.cs b/OnixLabs.Security.Cryptography/MerkleTreeBranchNode.cs
new file mode 100644
index 0000000..5049b21
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/MerkleTreeBranchNode.cs
@@ -0,0 +1,48 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a Merkle tree branch node.
+ ///
+ internal sealed class MerkleTreeBranchNode : MerkleTree
+ {
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The left-hand node.
+ /// The right-hand node.
+ public MerkleTreeBranchNode(MerkleTree left, MerkleTree right)
+ {
+ Left = left;
+ Right = right;
+ }
+
+ ///
+ /// Gets the left-hand node.
+ ///
+ public MerkleTree Left { get; }
+
+ ///
+ /// Gets the right-hand node.
+ ///
+ public MerkleTree Right { get; }
+
+ ///
+ /// Gets the hash of this node.
+ ///
+ public override Hash Hash => Left.Hash.Concatenate(Right.Hash);
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/MerkleTreeLeafNode.cs b/OnixLabs.Security.Cryptography/MerkleTreeLeafNode.cs
new file mode 100644
index 0000000..cbf8ec9
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/MerkleTreeLeafNode.cs
@@ -0,0 +1,55 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents a Merkle tree leaf node.
+ ///
+ internal sealed class MerkleTreeLeafNode : MerkleTree
+ {
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public MerkleTreeLeafNode(Hash hash)
+ {
+ Hash = hash;
+ }
+
+ ///
+ /// Gets the hash of this node.
+ ///
+ public override Hash Hash { get; }
+
+ ///
+ /// Creates a from from the specified hash.
+ ///
+ /// The from which to create a node.
+ /// Returns an node.
+ public static MerkleTree CreateHashNode(Hash hash)
+ {
+ return new MerkleTreeLeafNode(hash);
+ }
+
+ ///
+ /// Creates an empty node represented by an all-zero hash.
+ ///
+ /// The hash algorithm type of the node to create.
+ /// Returns an empty node represented by an all-zero hash.
+ public static MerkleTree CreateEmptyNode(HashAlgorithmType type)
+ {
+ return new MerkleTreeLeafNode(Hash.CreateAllZeroHash(type));
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/OnixLabs.Security.Cryptography.csproj b/OnixLabs.Security.Cryptography/OnixLabs.Security.Cryptography.csproj
new file mode 100644
index 0000000..a46cdc3
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/OnixLabs.Security.Cryptography.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net5.0
+ OnixLabs.Security.Cryptography
+ ONIXLabs
+ ONIXLabs Cryptography API for .NET
+ 1.0.0
+ en
+ enable
+
+
+
+
+
+
+
diff --git a/OnixLabs.Security.Cryptography/PrivateKey.Equatable.cs b/OnixLabs.Security.Cryptography/PrivateKey.Equatable.cs
new file mode 100644
index 0000000..e3444ce
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PrivateKey.Equatable.cs
@@ -0,0 +1,80 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for private key implementations.
+ ///
+ public abstract partial class PrivateKey : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(PrivateKey a, PrivateKey b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(PrivateKey a, PrivateKey b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public virtual bool Equals(PrivateKey? other)
+ {
+ return ReferenceEquals(this, other)
+ || other is not null
+ && other.GetType() == GetType()
+ && other.PrivateKeyData.SequenceEqual(PrivateKeyData)
+ && other.AlgorithmType == AlgorithmType;
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return Equals(obj as PrivateKey);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(GetType(), PrivateKeyData, AlgorithmType);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/PrivateKey.Export.cs b/OnixLabs.Security.Cryptography/PrivateKey.Export.cs
new file mode 100644
index 0000000..d536250
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PrivateKey.Export.cs
@@ -0,0 +1,63 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for private key implementations.
+ ///
+ public abstract partial class PrivateKey
+ {
+ ///
+ /// Exports the key in PKCS #8 format.
+ ///
+ /// Returns the key in PKCS #8 format.
+ public abstract byte[] ExportPkcs8Key();
+
+ ///
+ /// Exports the key in encrypted PKCS #8 format.
+ ///
+ /// The password to use for encryption.
+ /// The parameters required for password based encryption.
+ /// Returns the key in encrypted PKCS #8 format.
+ public abstract byte[] ExportPkcs8Key(ReadOnlySpan password, PbeParameters parameters);
+
+ ///
+ /// Exports the key in encrypted PKCS #8 format.
+ ///
+ /// The password to use for encryption.
+ /// The parameters required for password based encryption.
+ /// Returns the key in encrypted PKCS #8 format.
+ public byte[] ExportPkcs8Key(char[] password, PbeParameters parameters)
+ {
+ ReadOnlySpan characters = password.AsSpan();
+ return ExportPkcs8Key(characters, parameters);
+ }
+
+ ///
+ /// Exports the key in encrypted PKCS #8 format.
+ ///
+ /// The password to use for encryption.
+ /// The parameters required for password based encryption.
+ /// Returns the key in encrypted PKCS #8 format.
+ public byte[] ExportPkcs8Key(string password, PbeParameters parameters)
+ {
+ char[] characters = password.ToCharArray();
+ return ExportPkcs8Key(characters, parameters);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/PrivateKey.Get.cs b/OnixLabs.Security.Cryptography/PrivateKey.Get.cs
new file mode 100644
index 0000000..3e340e0
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PrivateKey.Get.cs
@@ -0,0 +1,28 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for private key implementations.
+ ///
+ public abstract partial class PrivateKey
+ {
+ ///
+ /// Gets the public key component from this private key.
+ ///
+ /// Returns the public key component from this private key.
+ public abstract PublicKey GetPublicKey();
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/PrivateKey.Sign.cs b/OnixLabs.Security.Cryptography/PrivateKey.Sign.cs
new file mode 100644
index 0000000..7ad304d
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PrivateKey.Sign.cs
@@ -0,0 +1,47 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for private key implementations.
+ ///
+ public abstract partial class PrivateKey
+ {
+ ///
+ /// Computes a from the specified unsigned data.
+ ///
+ /// The unsigned data from which to compute a .
+ /// Returns a from the specified unsigned data.
+ public abstract DigitalSignature SignData(byte[] unsignedData);
+
+ ///
+ /// Computes a from the specified unsigned hash.
+ ///
+ /// The unsigned hash from which to compute a .
+ /// Returns a from the specified unsigned hash.
+ public abstract DigitalSignature SignHash(byte[] unsignedHash);
+
+ ///
+ /// Computes a from the specified unsigned hash.
+ ///
+ /// The unsigned hash from which to compute a .
+ /// Returns a from the specified unsigned hash.
+ public DigitalSignature SignHash(Hash unsignedHash)
+ {
+ byte[] unsignedHashBytes = unsignedHash.ToByteArray();
+ return SignHash(unsignedHashBytes);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/PrivateKey.To.cs b/OnixLabs.Security.Cryptography/PrivateKey.To.cs
new file mode 100644
index 0000000..86e6174
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PrivateKey.To.cs
@@ -0,0 +1,100 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using OnixLabs.Core;
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for private key implementations.
+ ///
+ public abstract partial class PrivateKey
+ {
+ ///
+ /// Returns a array that represents the underlying private key data.
+ ///
+ /// Returns a array that represents the underlying private key data.
+ public byte[] ToByteArray()
+ {
+ return PrivateKeyData.Copy();
+ }
+
+ ///
+ /// Returns a value that represents the underlying private key data.
+ ///
+ /// Returns a value that represents the underlying private key data.
+ public Base16 ToBase16()
+ {
+ return Base16.FromByteArray(PrivateKeyData);
+ }
+
+ ///
+ /// Returns a value that represents the underlying private key data.
+ ///
+ /// Returns a value that represents the underlying private key data.
+ public Base32 ToBase32()
+ {
+ return ToBase32(Base32Alphabet.Default);
+ }
+
+ ///
+ /// Returns a value that represents the underlying private key data.
+ ///
+ /// The Base-32 alphabet to use to encode the private key data.
+ /// Returns a value that represents the underlying private key data.
+ public Base32 ToBase32(Base32Alphabet alphabet)
+ {
+ return Base32.FromByteArray(PrivateKeyData, alphabet);
+ }
+
+ ///
+ /// Returns a value that represents the underlying private key data.
+ ///
+ /// Returns a value that represents the underlying private key data.
+ public Base58 ToBase58()
+ {
+ return ToBase58(Base58Alphabet.Default);
+ }
+
+ ///
+ /// Returns a value that represents the underlying private key data.
+ ///
+ /// The Base-58 alphabet to use to encode the private key data.
+ /// Returns a value that represents the underlying private key data.
+ public Base58 ToBase58(Base58Alphabet alphabet)
+ {
+ return Base58.FromByteArray(PrivateKeyData, alphabet);
+ }
+
+ ///
+ /// Returns a value that represents the underlying private key data.
+ ///
+ /// Returns a value that represents the underlying private key data.
+ public Base64 ToBase64()
+ {
+ return Base64.FromByteArray(PrivateKeyData);
+ }
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Convert.ToHexString(PrivateKeyData).ToLower();
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/PrivateKey.cs b/OnixLabs.Security.Cryptography/PrivateKey.cs
new file mode 100644
index 0000000..59570fb
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PrivateKey.cs
@@ -0,0 +1,45 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for private key implementations.
+ ///
+ public abstract partial class PrivateKey
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The private key data.
+ /// The hash algorithm type for computing signature data.
+ protected PrivateKey(byte[] data, HashAlgorithmType type)
+ {
+ type.GetHashAlgorithmName();
+
+ PrivateKeyData = data;
+ AlgorithmType = type;
+ }
+
+ ///
+ /// Gets the underlying private key data.
+ ///
+ protected byte[] PrivateKeyData { get; }
+
+ ///
+ /// Gets the hash algorithm type for computing signature data.
+ ///
+ public HashAlgorithmType AlgorithmType { get; }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/PublicKey.Equatable.cs b/OnixLabs.Security.Cryptography/PublicKey.Equatable.cs
new file mode 100644
index 0000000..d94baab
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PublicKey.Equatable.cs
@@ -0,0 +1,80 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Linq;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for public key implementations.
+ ///
+ public abstract partial class PublicKey : IEquatable
+ {
+ ///
+ /// Performs an equality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are equal; otherwise, false.
+ public static bool operator ==(PublicKey a, PublicKey b)
+ {
+ return Equals(a, b);
+ }
+
+ ///
+ /// Performs an inequality check between two object instances.
+ ///
+ /// Instance a.
+ /// Instance b.
+ /// True if the instances are not equal; otherwise, false.
+ public static bool operator !=(PublicKey a, PublicKey b)
+ {
+ return !Equals(a, b);
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public virtual bool Equals(PublicKey? other)
+ {
+ return ReferenceEquals(this, other)
+ || other is not null
+ && other.GetType() == GetType()
+ && other.PublicKeyData.SequenceEqual(PublicKeyData)
+ && other.AlgorithmType == AlgorithmType;
+ }
+
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(object? obj)
+ {
+ return Equals(obj as PublicKey);
+ }
+
+ ///
+ /// Serves as a hash code function for this instance.
+ ///
+ /// A hash code for this instance.
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(GetType(), PublicKeyData, AlgorithmType);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/PublicKey.To.cs b/OnixLabs.Security.Cryptography/PublicKey.To.cs
new file mode 100644
index 0000000..a8cad71
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PublicKey.To.cs
@@ -0,0 +1,100 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using OnixLabs.Core;
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for public key implementations.
+ ///
+ public abstract partial class PublicKey
+ {
+ ///
+ /// Returns a array that represents the underlying public key data.
+ ///
+ /// Returns a array that represents the underlying public key data.
+ public byte[] ToByteArray()
+ {
+ return PublicKeyData.Copy();
+ }
+
+ ///
+ /// Returns a value that represents the underlying public key data.
+ ///
+ /// Returns a value that represents the underlying public key data.
+ public Base16 ToBase16()
+ {
+ return Base16.FromByteArray(PublicKeyData);
+ }
+
+ ///
+ /// Returns a value that represents the underlying public key data.
+ ///
+ /// Returns a value that represents the underlying public key data.
+ public Base32 ToBase32()
+ {
+ return ToBase32(Base32Alphabet.Default);
+ }
+
+ ///
+ /// Returns a value that represents the underlying public key data.
+ ///
+ /// The Base-32 alphabet to use to encode the public key data.
+ /// Returns a value that represents the underlying public key data.
+ public Base32 ToBase32(Base32Alphabet alphabet)
+ {
+ return Base32.FromByteArray(PublicKeyData, alphabet);
+ }
+
+ ///
+ /// Returns a value that represents the underlying public key data.
+ ///
+ /// Returns a value that represents the underlying public key data.
+ public Base58 ToBase58()
+ {
+ return ToBase58(Base58Alphabet.Default);
+ }
+
+ ///
+ /// Returns a value that represents the underlying public key data.
+ ///
+ /// The Base-58 alphabet to use to encode the public key data.
+ /// Returns a value that represents the underlying public key data.
+ public Base58 ToBase58(Base58Alphabet alphabet)
+ {
+ return Base58.FromByteArray(PublicKeyData, alphabet);
+ }
+
+ ///
+ /// Returns a value that represents the underlying public key data.
+ ///
+ /// Returns a value that represents the underlying public key data.
+ public Base64 ToBase64()
+ {
+ return Base64.FromByteArray(PublicKeyData);
+ }
+
+ ///
+ /// Returns a that represents the current object.
+ ///
+ /// A that represents the current object.
+ public override string ToString()
+ {
+ return Convert.ToHexString(PublicKeyData).ToLower();
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/PublicKey.Verify.cs b/OnixLabs.Security.Cryptography/PublicKey.Verify.cs
new file mode 100644
index 0000000..ba61bbd
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PublicKey.Verify.cs
@@ -0,0 +1,92 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for public key implementations.
+ ///
+ public abstract partial class PublicKey
+ {
+ ///
+ /// Determines whether the specified was signed by the private component of this public key.
+ ///
+ /// The to validate.
+ /// The unsigned data to validate.
+ /// Returns true if the specified was signed by the private component of this public key; otherwise, false.
+ public abstract bool IsDataValid(DigitalSignature signature, byte[] unsignedData);
+
+ ///
+ /// Determines whether the specified was signed by the private component of this public key.
+ ///
+ /// The to validate.
+ /// The unsigned hash to validate.
+ /// Returns true if the specified was signed by the private component of this public key; otherwise, false.
+ public abstract bool IsHashValid(DigitalSignature signature, byte[] unsignedHash);
+
+ ///
+ /// Determines whether the specified was signed by the private component of this public key.
+ ///
+ /// The to validate.
+ /// The unsigned hash to validate.
+ /// Returns true if the specified was signed by the private component of this public key; otherwise, false.
+ public bool IsHashValid(DigitalSignature signature, Hash unsignedHash)
+ {
+ byte[] unsignedHashBytes = unsignedHash.ToByteArray();
+ return IsHashValid(signature, unsignedHashBytes);
+ }
+
+ ///
+ /// Verifies whether the specified was signed by the private component of this public key.
+ ///
+ /// The to verify.
+ /// he unsigned data to verify.
+ /// If the specified was not signed by the private component of this public key.
+ public void VerifyData(DigitalSignature signature, byte[] unsignedData)
+ {
+ if (!IsDataValid(signature, unsignedData))
+ {
+ throw new CryptographicException("The specified digital signature was not signed with this key.");
+ }
+ }
+
+ ///
+ /// Verifies whether the specified was signed by the private component of this public key.
+ ///
+ /// The to verify.
+ /// he unsigned hash to verify.
+ /// If the specified was not signed by the private component of this public key.
+ public void VerifyHash(DigitalSignature signature, byte[] unsignedHash)
+ {
+ if (!IsHashValid(signature, unsignedHash))
+ {
+ throw new CryptographicException("The specified digital signature was not signed with this key.");
+ }
+ }
+
+ ///
+ /// Verifies whether the specified was signed by the private component of this public key.
+ ///
+ /// The to verify.
+ /// he unsigned hash to verify.
+ /// If the specified was not signed by the private component of this public key.
+ public void VerifyHash(DigitalSignature signature, Hash unsignedHash)
+ {
+ byte[] unsignedHashBytes = unsignedHash.ToByteArray();
+ VerifyHash(signature, unsignedHashBytes);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/PublicKey.cs b/OnixLabs.Security.Cryptography/PublicKey.cs
new file mode 100644
index 0000000..6e120bb
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/PublicKey.cs
@@ -0,0 +1,45 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents the base class for public key implementations.
+ ///
+ public abstract partial class PublicKey
+ {
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The public key data.
+ /// The hash algorithm type for computing signature data.
+ protected PublicKey(byte[] data, HashAlgorithmType type)
+ {
+ type.GetHashAlgorithmName();
+
+ PublicKeyData = data;
+ AlgorithmType = type;
+ }
+
+ ///
+ /// Gets the underlying public key data.
+ ///
+ protected byte[] PublicKeyData { get; }
+
+ ///
+ /// Gets the hash algorithm type for computing signature data.
+ ///
+ public HashAlgorithmType AlgorithmType { get; }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Equatable.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Equatable.cs
new file mode 100644
index 0000000..77e9d84
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Equatable.cs
@@ -0,0 +1,34 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA private key.
+ ///
+ public sealed partial class RsaPrivateKey
+ {
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(PrivateKey? other)
+ {
+ return base.Equals(other)
+ && other is RsaPrivateKey rsaOther
+ && rsaOther.Padding == Padding;
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Export.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Export.cs
new file mode 100644
index 0000000..2f6be7f
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Export.cs
@@ -0,0 +1,53 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA private key.
+ ///
+ public sealed partial class RsaPrivateKey
+ {
+ ///
+ /// Exports the key in PKCS #8 format.
+ ///
+ /// Returns the key in PKCS #8 format.
+ public override byte[] ExportPkcs8Key()
+ {
+ using RSA privateKey = RSA.Create();
+
+ privateKey.ImportRSAPrivateKey(PrivateKeyData, out int _);
+
+ return privateKey.ExportPkcs8PrivateKey();
+ }
+
+ ///
+ /// Exports the key in encrypted PKCS #8 format.
+ ///
+ /// The password to use for encryption.
+ /// The parameters required for password based encryption.
+ /// Returns the key in encrypted PKCS #8 format.
+ public override byte[] ExportPkcs8Key(ReadOnlySpan password, PbeParameters parameters)
+ {
+ using RSA privateKey = RSA.Create();
+
+ privateKey.ImportRSAPrivateKey(PrivateKeyData, out int _);
+
+ return privateKey.ExportEncryptedPkcs8PrivateKey(password, parameters);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.From.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.From.cs
new file mode 100644
index 0000000..2316ace
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.From.cs
@@ -0,0 +1,89 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA private key.
+ ///
+ public sealed partial class RsaPrivateKey
+ {
+ ///
+ /// Creates an from the specified key data and hash algorithm type.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPrivateKey FromByteArray(byte[] key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ return new RsaPrivateKey(key, type, padding);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPrivateKey FromBase16(Base16 key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type, padding);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPrivateKey FromBase32(Base32 key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type, padding);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPrivateKey FromBase58(Base58 key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type, padding);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPrivateKey FromBase64(Base64 key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type, padding);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Get.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Get.cs
new file mode 100644
index 0000000..4c7ebf0
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Get.cs
@@ -0,0 +1,38 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA private key.
+ ///
+ public sealed partial class RsaPrivateKey
+ {
+ ///
+ /// Gets the public key component from this private key.
+ ///
+ /// Returns the public key component from this private key.
+ public override PublicKey GetPublicKey()
+ {
+ using RSA privateKey = RSA.Create();
+
+ privateKey.ImportRSAPrivateKey(PrivateKeyData, out int _);
+ byte[] publicKey = privateKey.ExportRSAPublicKey();
+
+ return new RsaPublicKey(publicKey, AlgorithmType, Padding);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Import.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Import.cs
new file mode 100644
index 0000000..1639440
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Import.cs
@@ -0,0 +1,170 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA private key.
+ ///
+ public sealed partial class RsaPrivateKey
+ {
+ ///
+ /// Imports a PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static RsaPrivateKey ImportPkcs8Key(
+ ReadOnlySpan data,
+ HashAlgorithmType type,
+ RSASignaturePadding padding)
+ {
+ RSA privateKey = RSA.Create();
+
+ privateKey.ImportPkcs8PrivateKey(data, out int _);
+ byte[] bytes = privateKey.ExportRSAPrivateKey();
+
+ return FromByteArray(bytes, type, padding);
+ }
+
+ ///
+ /// Imports a PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static RsaPrivateKey ImportPkcs8Key(byte[] data, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ ReadOnlySpan bytes = data.AsSpan();
+ return ImportPkcs8Key(bytes, type, padding);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static RsaPrivateKey ImportPkcs8Key(
+ ReadOnlySpan data,
+ ReadOnlySpan password,
+ HashAlgorithmType type,
+ RSASignaturePadding padding)
+ {
+ RSA privateKey = RSA.Create();
+
+ privateKey.ImportEncryptedPkcs8PrivateKey(password, data, out int _);
+ byte[] bytes = privateKey.ExportRSAPrivateKey();
+
+ return FromByteArray(bytes, type, padding);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static RsaPrivateKey ImportPkcs8Key(
+ ReadOnlySpan data,
+ char[] password,
+ HashAlgorithmType type,
+ RSASignaturePadding padding)
+ {
+ ReadOnlySpan characters = password.AsSpan();
+ return ImportPkcs8Key(data, characters, type, padding);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static RsaPrivateKey ImportPkcs8Key(
+ ReadOnlySpan data,
+ string password,
+ HashAlgorithmType type,
+ RSASignaturePadding padding)
+ {
+ ReadOnlySpan characters = password.AsSpan();
+ return ImportPkcs8Key(data, characters, type, padding);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static RsaPrivateKey ImportPkcs8Key(
+ byte[] data,
+ ReadOnlySpan password,
+ HashAlgorithmType type,
+ RSASignaturePadding padding)
+ {
+ ReadOnlySpan bytes = data.AsSpan();
+ return ImportPkcs8Key(bytes, password, type, padding);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static RsaPrivateKey ImportPkcs8Key(
+ byte[] data,
+ char[] password,
+ HashAlgorithmType type,
+ RSASignaturePadding padding)
+ {
+ ReadOnlySpan bytes = data.AsSpan();
+ return ImportPkcs8Key(bytes, password, type, padding);
+ }
+
+ ///
+ /// Imports an encrypted PKCS #8 formatted key.
+ ///
+ /// The key data to import.
+ /// The password to decrypt the key data.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified PKCS #8 key data.
+ public static RsaPrivateKey ImportPkcs8Key(
+ byte[] data,
+ string password,
+ HashAlgorithmType type,
+ RSASignaturePadding padding)
+ {
+ ReadOnlySpan bytes = data.AsSpan();
+ return ImportPkcs8Key(bytes, password, type, padding);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.Sign.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.Sign.cs
new file mode 100644
index 0000000..df5414e
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.Sign.cs
@@ -0,0 +1,56 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA private key.
+ ///
+ public sealed partial class RsaPrivateKey
+ {
+ ///
+ /// Computes a from the specified unsigned data.
+ ///
+ /// The unsigned data from which to compute a .
+ /// Returns a from the specified unsigned data.
+ public override DigitalSignature SignData(byte[] unsignedData)
+ {
+ using RSA privateKey = RSA.Create();
+
+ privateKey.ImportRSAPrivateKey(PrivateKeyData, out int _);
+ HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName();
+ byte[] signedData = privateKey.SignData(unsignedData, name, Padding);
+
+ return DigitalSignature.FromByteArray(signedData);
+ }
+
+ ///
+ /// Computes a from the specified unsigned hash.
+ ///
+ /// The unsigned hash from which to compute a .
+ /// Returns a from the specified unsigned hash.
+ public override DigitalSignature SignHash(byte[] unsignedHash)
+ {
+ using RSA privateKey = RSA.Create();
+
+ privateKey.ImportRSAPrivateKey(PrivateKeyData, out int _);
+ HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName();
+ byte[] signedData = privateKey.SignHash(unsignedHash, name, Padding);
+
+ return DigitalSignature.FromByteArray(signedData);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPrivateKey.cs b/OnixLabs.Security.Cryptography/RsaPrivateKey.cs
new file mode 100644
index 0000000..22f05f7
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPrivateKey.cs
@@ -0,0 +1,40 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA private key.
+ ///
+ public sealed partial class RsaPrivateKey : PrivateKey
+ {
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The private key data.
+ /// The hash algorithm type for computing signature data.
+ /// The for computing signature data.
+ internal RsaPrivateKey(byte[] data, HashAlgorithmType type, RSASignaturePadding padding) : base(data, type)
+ {
+ Padding = padding;
+ }
+
+ ///
+ /// Gets the for computing signature data.
+ ///
+ public RSASignaturePadding Padding { get; }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPublicKey.Equatable.cs b/OnixLabs.Security.Cryptography/RsaPublicKey.Equatable.cs
new file mode 100644
index 0000000..f174ab0
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPublicKey.Equatable.cs
@@ -0,0 +1,34 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA public key.
+ ///
+ public sealed partial class RsaPublicKey
+ {
+ ///
+ /// Checks for equality between this instance and another object.
+ ///
+ /// The object to check for equality.
+ /// true if the object is equal to this instance; otherwise, false.
+ public override bool Equals(PublicKey? other)
+ {
+ return base.Equals(other)
+ && other is RsaPublicKey rsaOther
+ && rsaOther.Padding == Padding;
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPublicKey.From.cs b/OnixLabs.Security.Cryptography/RsaPublicKey.From.cs
new file mode 100644
index 0000000..bc09176
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPublicKey.From.cs
@@ -0,0 +1,89 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+using OnixLabs.Core.Text;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA public key.
+ ///
+ public sealed partial class RsaPublicKey
+ {
+ ///
+ /// Creates an from the specified key data and hash algorithm type.
+ ///
+ /// The key data from which to construct a public key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPublicKey FromByteArray(byte[] key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ return new RsaPublicKey(key, type, padding);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPublicKey FromBase16(Base16 key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type, padding);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPublicKey FromBase32(Base32 key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type, padding);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPublicKey FromBase58(Base58 key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type, padding);
+ }
+
+ ///
+ /// Creates an from the specified value.
+ ///
+ /// The key data from which to construct a private key.
+ /// The for computing signature data.
+ /// The for computing signature data.
+ /// Returns an from the specified key data and hash algorithm type.
+ public static RsaPublicKey FromBase64(Base64 key, HashAlgorithmType type, RSASignaturePadding padding)
+ {
+ byte[] bytes = key.ToByteArray();
+ return FromByteArray(bytes, type, padding);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPublicKey.Verify.cs b/OnixLabs.Security.Cryptography/RsaPublicKey.Verify.cs
new file mode 100644
index 0000000..05ad35a
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPublicKey.Verify.cs
@@ -0,0 +1,58 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA public key.
+ ///
+ public sealed partial class RsaPublicKey
+ {
+ ///
+ /// Determines whether the specified was signed by the private component of this public key.
+ ///
+ /// The to validate.
+ /// The unsigned data to validate.
+ /// Returns true if the specified was signed by the private component of this public key; otherwise, false.
+ public override bool IsDataValid(DigitalSignature signature, byte[] unsignedData)
+ {
+ using RSA publicKey = RSA.Create();
+
+ publicKey.ImportRSAPublicKey(PublicKeyData, out int _);
+ byte[] signatureData = signature.ToByteArray();
+ HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName();
+
+ return publicKey.VerifyData(unsignedData, signatureData, name, Padding);
+ }
+
+ ///
+ /// Determines whether the specified was signed by the private component of this public key.
+ ///
+ /// The to validate.
+ /// The unsigned hash to validate.
+ /// Returns true if the specified was signed by the private component of this public key; otherwise, false.
+ public override bool IsHashValid(DigitalSignature signature, byte[] unsignedHash)
+ {
+ using RSA publicKey = RSA.Create();
+
+ publicKey.ImportRSAPublicKey(PublicKeyData, out int _);
+ byte[] signatureData = signature.ToByteArray();
+ HashAlgorithmName name = AlgorithmType.GetHashAlgorithmName();
+
+ return publicKey.VerifyHash(unsignedHash, signatureData, name, Padding);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/RsaPublicKey.cs b/OnixLabs.Security.Cryptography/RsaPublicKey.cs
new file mode 100644
index 0000000..1b5089e
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/RsaPublicKey.cs
@@ -0,0 +1,40 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Represents an RSA public key.
+ ///
+ public sealed partial class RsaPublicKey : PublicKey
+ {
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The public key data.
+ /// The hash algorithm type for computing signature data.
+ /// The for computing signature data.
+ internal RsaPublicKey(byte[] data, HashAlgorithmType type, RSASignaturePadding padding) : base(data, type)
+ {
+ Padding = padding;
+ }
+
+ ///
+ /// Gets the for computing signature data.
+ ///
+ public RSASignaturePadding Padding { get; }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Sha3.Create.cs b/OnixLabs.Security.Cryptography/Sha3.Create.cs
new file mode 100644
index 0000000..cf76bf0
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Sha3.Create.cs
@@ -0,0 +1,78 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Computes the FIPS 202 SHA-3 hash for the input data.
+ ///
+ public abstract partial class Sha3
+ {
+ ///
+ /// Creates an instance of the algorithm.
+ ///
+ /// An instance of the algorithm.
+ public static Sha3 CreateSha3Hash224()
+ {
+ return new Sha3Hash224();
+ }
+
+ ///
+ /// Creates an instance of the algorithm.
+ ///
+ /// An instance of the algorithm.
+ public static Sha3 CreateSha3Hash256()
+ {
+ return new Sha3Hash256();
+ }
+
+ ///
+ /// Creates an instance of the algorithm.
+ ///
+ /// An instance of the algorithm.
+ public static Sha3 CreateSha3Hash384()
+ {
+ return new Sha3Hash384();
+ }
+
+ ///
+ /// Creates an instance of the algorithm.
+ ///
+ /// An instance of the algorithm.
+ public static Sha3 CreateSha3Hash512()
+ {
+ return new Sha3Hash512();
+ }
+
+ ///
+ /// Creates an instance of the algorithm.
+ ///
+ /// The output length of the hash in bytes.
+ /// An instance of the algorithm.
+ public static Sha3 CreateSha3Shake128(int length)
+ {
+ return new Sha3Shake128(length);
+ }
+
+ ///
+ /// Creates an instance of the algorithm.
+ ///
+ /// The output length of the hash in bytes.
+ /// An instance of the algorithm.
+ public static Sha3 CreateSha3Shake256(int length)
+ {
+ return new Sha3Shake256(length);
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Sha3.Permute.cs b/OnixLabs.Security.Cryptography/Sha3.Permute.cs
new file mode 100644
index 0000000..a71b90f
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Sha3.Permute.cs
@@ -0,0 +1,158 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System.Runtime.CompilerServices;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Computes the FIPS 202 SHA-3 hash for the input data.
+ ///
+ public abstract partial class Sha3
+ {
+ ///
+ /// Performs the FIPS 202 SHA-3 permutation.
+ ///
+ /// The state upon which to perform the permutation.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Permute(ulong[] state)
+ {
+ const int hashRounds = 24;
+
+ ulong c0, c1, c2, c3, c4, d0, d1, d2, d3, d4;
+
+ ulong[] roundConstants =
+ {
+ 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000,
+ 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
+ 0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
+ 0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003,
+ 0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
+ 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
+ };
+
+ for (int round = 0; round < hashRounds; round++)
+ {
+ Theta();
+ RhoPi();
+ Chi();
+ Iota(round);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ void Theta()
+ {
+ c0 = state[0] ^ state[5] ^ state[10] ^ state[15] ^ state[20];
+ c1 = state[1] ^ state[6] ^ state[11] ^ state[16] ^ state[21];
+ c2 = state[2] ^ state[7] ^ state[12] ^ state[17] ^ state[22];
+ c3 = state[3] ^ state[8] ^ state[13] ^ state[18] ^ state[23];
+ c4 = state[4] ^ state[9] ^ state[14] ^ state[19] ^ state[24];
+
+ d0 = RotateLeft(c1, 1) ^ c4;
+ d1 = RotateLeft(c2, 1) ^ c0;
+ d2 = RotateLeft(c3, 1) ^ c1;
+ d3 = RotateLeft(c4, 1) ^ c2;
+ d4 = RotateLeft(c0, 1) ^ c3;
+
+ state[00] ^= d0;
+ state[05] ^= d0;
+ state[10] ^= d0;
+ state[15] ^= d0;
+ state[20] ^= d0;
+ state[01] ^= d1;
+ state[06] ^= d1;
+ state[11] ^= d1;
+ state[16] ^= d1;
+ state[21] ^= d1;
+ state[02] ^= d2;
+ state[07] ^= d2;
+ state[12] ^= d2;
+ state[17] ^= d2;
+ state[22] ^= d2;
+ state[03] ^= d3;
+ state[08] ^= d3;
+ state[13] ^= d3;
+ state[18] ^= d3;
+ state[23] ^= d3;
+ state[04] ^= d4;
+ state[09] ^= d4;
+ state[14] ^= d4;
+ state[19] ^= d4;
+ state[24] ^= d4;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ void RhoPi()
+ {
+ ulong final = RotateLeft(state[1], 1);
+
+ state[01] = RotateLeft(state[06], 44);
+ state[06] = RotateLeft(state[09], 20);
+ state[09] = RotateLeft(state[22], 61);
+ state[22] = RotateLeft(state[14], 39);
+ state[14] = RotateLeft(state[20], 18);
+ state[20] = RotateLeft(state[02], 62);
+ state[02] = RotateLeft(state[12], 43);
+ state[12] = RotateLeft(state[13], 25);
+ state[13] = RotateLeft(state[19], 08);
+ state[19] = RotateLeft(state[23], 56);
+ state[23] = RotateLeft(state[15], 41);
+ state[15] = RotateLeft(state[04], 27);
+ state[04] = RotateLeft(state[24], 14);
+ state[24] = RotateLeft(state[21], 02);
+ state[21] = RotateLeft(state[08], 55);
+ state[08] = RotateLeft(state[16], 45);
+ state[16] = RotateLeft(state[05], 36);
+ state[05] = RotateLeft(state[03], 28);
+ state[03] = RotateLeft(state[18], 21);
+ state[18] = RotateLeft(state[17], 15);
+ state[17] = RotateLeft(state[11], 10);
+ state[11] = RotateLeft(state[07], 06);
+ state[07] = RotateLeft(state[10], 03);
+ state[10] = final;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ void Chi()
+ {
+ for (int i = 0; i < 25; i += 5)
+ {
+ c0 = state[0 + i] ^ ((~state[1 + i]) & state[2 + i]);
+ c1 = state[1 + i] ^ ((~state[2 + i]) & state[3 + i]);
+ c2 = state[2 + i] ^ ((~state[3 + i]) & state[4 + i]);
+ c3 = state[3 + i] ^ ((~state[4 + i]) & state[0 + i]);
+ c4 = state[4 + i] ^ ((~state[0 + i]) & state[1 + i]);
+
+ state[0 + i] = c0;
+ state[1 + i] = c1;
+ state[2 + i] = c2;
+ state[3 + i] = c3;
+ state[4 + i] = c4;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ void Iota(int round)
+ {
+ state[0] ^= roundConstants[round];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ ulong RotateLeft(ulong x, byte y)
+ {
+ return (x << y) | (x >> (64 - y));
+ }
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Sha3.cs b/OnixLabs.Security.Cryptography/Sha3.cs
new file mode 100644
index 0000000..67abda7
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Sha3.cs
@@ -0,0 +1,165 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Security.Cryptography;
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Computes the FIPS 202 SHA-3 hash for the input data.
+ ///
+ public abstract partial class Sha3 : HashAlgorithm
+ {
+ ///
+ /// The delimiter used for hash implementations.
+ ///
+ protected const int HashDelimiter = 0x06;
+
+ ///
+ /// The delimiter used for Shake implementations.
+ ///
+ protected const int ShakeDelimiter = 0x1f;
+
+ ///
+ /// The rate in bytes of the sponge state.
+ ///
+ private readonly int rateBytes;
+
+ ///
+ /// The state delimiter.
+ ///
+ private readonly int delimiter;
+
+ ///
+ /// The length of the hash in bits.
+ ///
+ private readonly int bitLength;
+
+ ///
+ /// The state block size.
+ ///
+ private int blockSize;
+
+ ///
+ /// The state input pointer.
+ ///
+ private int inputPointer;
+
+ ///
+ /// The state output pointer.
+ ///
+ private int outputPointer;
+
+ ///
+ /// The permutable sponge state.
+ ///
+ private ulong[] state = Array.Empty();
+
+ ///
+ /// The hash result.
+ ///
+ private byte[] result = Array.Empty();
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The rate in bytes of the sponge state.
+ /// The state delimiter.
+ /// The output length of the hash in bits.
+ protected Sha3(int rateBytes, int delimiter, int bitLength)
+ {
+ this.rateBytes = rateBytes;
+ this.delimiter = delimiter;
+ this.bitLength = bitLength;
+ }
+
+ ///
+ /// Initializes an implementation of the class.
+ ///
+ public override void Initialize()
+ {
+ blockSize = default;
+ inputPointer = default;
+ outputPointer = default;
+ state = new ulong[25];
+ result = new byte[bitLength / 8];
+ }
+
+ ///
+ /// Routes data written to the object into the hash algorithm for computing the hash.
+ ///
+ /// The input to compute the hash code for.
+ /// The offset into the byte array from which to begin using data.
+ /// The number of bytes in the byte array to use as data.
+ protected override void HashCore(byte[] array, int ibStart, int cbSize)
+ {
+ Initialize();
+
+ while (cbSize > 0)
+ {
+ blockSize = Math.Min(cbSize, rateBytes);
+
+ for (int index = ibStart; index < blockSize; index++)
+ {
+ byte x = Convert.ToByte(Buffer.GetByte(state, index) ^ array[index + inputPointer]);
+ Buffer.SetByte(state, index, x);
+ }
+
+ inputPointer += blockSize;
+ cbSize -= blockSize;
+
+ if (blockSize != rateBytes) continue;
+ Permute(state);
+ blockSize = 0;
+ }
+ }
+
+ ///
+ /// Finalizes the hash computation after the last data is processed by the cryptographic stream object.
+ ///
+ /// The computed hash code.
+ protected override byte[] HashFinal()
+ {
+ byte pad = Convert.ToByte(Buffer.GetByte(state, blockSize) ^ delimiter);
+ Buffer.SetByte(state, blockSize, pad);
+
+ if (((delimiter & 0x80) != 0) && blockSize == (rateBytes - 1))
+ {
+ Permute(state);
+ }
+
+ pad = Convert.ToByte(Buffer.GetByte(state, rateBytes - 1) ^ 0x80);
+ Buffer.SetByte(state, rateBytes - 1, pad);
+ Permute(state);
+
+ int outputBytesLeft = bitLength / 8;
+
+ while (outputBytesLeft > 0)
+ {
+ blockSize = Math.Min(outputBytesLeft, rateBytes);
+ Buffer.BlockCopy(state, 0, result, outputPointer, blockSize);
+ outputPointer += blockSize;
+ outputBytesLeft -= blockSize;
+
+ if (outputBytesLeft > 0)
+ {
+ Permute(state);
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Sha3Hash224.cs b/OnixLabs.Security.Cryptography/Sha3Hash224.cs
new file mode 100644
index 0000000..1080236
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Sha3Hash224.cs
@@ -0,0 +1,39 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Computes the FIPS 202 SHA-3 224-bit hash for the input data.
+ ///
+ public sealed class Sha3Hash224 : Sha3
+ {
+ ///
+ /// The rate in bytes of the sponge state.
+ ///
+ private const int RateBytes = 144;
+
+ ///
+ /// The length of the hash in bits.
+ ///
+ private const int BitLength = 224;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public Sha3Hash224() : base(RateBytes, HashDelimiter, BitLength)
+ {
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Sha3Hash256.cs b/OnixLabs.Security.Cryptography/Sha3Hash256.cs
new file mode 100644
index 0000000..222c2a9
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Sha3Hash256.cs
@@ -0,0 +1,39 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Computes the FIPS 202 SHA-3 256-bit hash for the input data.
+ ///
+ public sealed class Sha3Hash256 : Sha3
+ {
+ ///
+ /// The rate in bytes of the sponge state.
+ ///
+ private const int RateBytes = 136;
+
+ ///
+ /// The length of the hash in bits.
+ ///
+ private const int BitLength = 256;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public Sha3Hash256() : base(RateBytes, HashDelimiter, BitLength)
+ {
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Sha3Hash384.cs b/OnixLabs.Security.Cryptography/Sha3Hash384.cs
new file mode 100644
index 0000000..d60a022
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Sha3Hash384.cs
@@ -0,0 +1,39 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Computes the FIPS 202 SHA-3 384-bit hash for the input data.
+ ///
+ public sealed class Sha3Hash384 : Sha3
+ {
+ ///
+ /// The rate in bytes of the sponge state.
+ ///
+ private const int RateBytes = 104;
+
+ ///
+ /// The length of the hash in bits.
+ ///
+ private const int BitLength = 384;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public Sha3Hash384() : base(RateBytes, HashDelimiter, BitLength)
+ {
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Sha3Hash512.cs b/OnixLabs.Security.Cryptography/Sha3Hash512.cs
new file mode 100644
index 0000000..c7d2971
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Sha3Hash512.cs
@@ -0,0 +1,39 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Computes the FIPS 202 SHA-3 512-bit hash for the input data.
+ ///
+ public sealed class Sha3Hash512 : Sha3
+ {
+ ///
+ /// The rate in bytes of the sponge state.
+ ///
+ private const int RateBytes = 72;
+
+ ///
+ /// The length of the hash in bits.
+ ///
+ private const int BitLength = 512;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ public Sha3Hash512() : base(RateBytes, HashDelimiter, BitLength)
+ {
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Sha3Shake128.cs b/OnixLabs.Security.Cryptography/Sha3Shake128.cs
new file mode 100644
index 0000000..44865f2
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Sha3Shake128.cs
@@ -0,0 +1,40 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Computes the FIPS 202 SHA-3 Shake 128-bit hash for the input data.
+ ///
+ public sealed class Sha3Shake128 : Sha3
+ {
+ ///
+ /// The rate in bytes of the sponge state.
+ ///
+ private const int RateBytes = 168;
+
+ ///
+ /// The length multiplier of the hash in bits.
+ ///
+ private const int BitLengthMultiplier = 8;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The hash output length in bytes.
+ public Sha3Shake128(int length) : base(RateBytes, ShakeDelimiter, length * BitLengthMultiplier)
+ {
+ }
+ }
+}
diff --git a/OnixLabs.Security.Cryptography/Sha3Shake256.cs b/OnixLabs.Security.Cryptography/Sha3Shake256.cs
new file mode 100644
index 0000000..858ecd5
--- /dev/null
+++ b/OnixLabs.Security.Cryptography/Sha3Shake256.cs
@@ -0,0 +1,40 @@
+// Copyright 2020-2021 ONIXLabs
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace OnixLabs.Security.Cryptography
+{
+ ///
+ /// Computes the FIPS 202 SHA-3 Shake 256-bit hash for the input data.
+ ///
+ public sealed class Sha3Shake256 : Sha3
+ {
+ ///
+ /// The rate in bytes of the sponge state.
+ ///
+ private const int RateBytes = 136;
+
+ ///
+ /// The length multiplier of the hash in bits.
+ ///
+ private const int BitLengthMultiplier = 8;
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The hash output length in bytes.
+ public Sha3Shake256(int length) : base(RateBytes, ShakeDelimiter, length * BitLengthMultiplier)
+ {
+ }
+ }
+}
diff --git a/README.md b/README.md
index 020f029..465b274 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,6 @@
-# onixlabs-dotnet
-ONIXLabs Core APIs for .NET
+![ONIX Labs](https://raw.githubusercontent.com/onix-labs/onix-labs.github.io/master/content/logo/master_full_md.png)
+
+# ONIXLabs Core APIs for .NET
+
+This repository contains the ONIXLabs Core APIs for .NET
+
diff --git a/onixlabs-dotnet.sln b/onixlabs-dotnet.sln
new file mode 100644
index 0000000..c2af2ab
--- /dev/null
+++ b/onixlabs-dotnet.sln
@@ -0,0 +1,53 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Framework", "Framework", "{6E4FE7B0-5E44-4EB4-B64F-228645C4F79B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Playground", "Playground", "{039F52FD-BF3C-4E6A-B0AB-1F34E48ADBFF}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{C1D7FFDA-A234-4A01-9371-19C68DD74C91}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Core", "OnixLabs.Core\OnixLabs.Core.csproj", "{1EDC1164-0205-433D-A356-00DAD4686264}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Playground", "OnixLabs.Playground\OnixLabs.Playground.csproj", "{23F9EB42-D9D4-4C43-9C99-3D2A1F8866B7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Core.UnitTests", "OnixLabs.Core.UnitTests\OnixLabs.Core.UnitTests.csproj", "{893547DB-4EAA-4D1F-886F-0D6114F8601E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Security.Cryptography", "OnixLabs.Security.Cryptography\OnixLabs.Security.Cryptography.csproj", "{78FDB1BA-6545-41DE-99B8-F007C6A6B093}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OnixLabs.Security.Cryptography.UnitTests", "OnixLabs.Security.Cryptography.UnitTests\OnixLabs.Security.Cryptography.UnitTests.csproj", "{C3DE665B-5B02-41DE-8BB0-C53A1326E162}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {1EDC1164-0205-433D-A356-00DAD4686264} = {6E4FE7B0-5E44-4EB4-B64F-228645C4F79B}
+ {23F9EB42-D9D4-4C43-9C99-3D2A1F8866B7} = {039F52FD-BF3C-4E6A-B0AB-1F34E48ADBFF}
+ {893547DB-4EAA-4D1F-886F-0D6114F8601E} = {C1D7FFDA-A234-4A01-9371-19C68DD74C91}
+ {78FDB1BA-6545-41DE-99B8-F007C6A6B093} = {6E4FE7B0-5E44-4EB4-B64F-228645C4F79B}
+ {C3DE665B-5B02-41DE-8BB0-C53A1326E162} = {C1D7FFDA-A234-4A01-9371-19C68DD74C91}
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1EDC1164-0205-433D-A356-00DAD4686264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1EDC1164-0205-433D-A356-00DAD4686264}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1EDC1164-0205-433D-A356-00DAD4686264}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1EDC1164-0205-433D-A356-00DAD4686264}.Release|Any CPU.Build.0 = Release|Any CPU
+ {23F9EB42-D9D4-4C43-9C99-3D2A1F8866B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {23F9EB42-D9D4-4C43-9C99-3D2A1F8866B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {23F9EB42-D9D4-4C43-9C99-3D2A1F8866B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {23F9EB42-D9D4-4C43-9C99-3D2A1F8866B7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {893547DB-4EAA-4D1F-886F-0D6114F8601E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {893547DB-4EAA-4D1F-886F-0D6114F8601E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {893547DB-4EAA-4D1F-886F-0D6114F8601E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {893547DB-4EAA-4D1F-886F-0D6114F8601E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {78FDB1BA-6545-41DE-99B8-F007C6A6B093}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {78FDB1BA-6545-41DE-99B8-F007C6A6B093}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {78FDB1BA-6545-41DE-99B8-F007C6A6B093}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {78FDB1BA-6545-41DE-99B8-F007C6A6B093}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C3DE665B-5B02-41DE-8BB0-C53A1326E162}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C3DE665B-5B02-41DE-8BB0-C53A1326E162}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C3DE665B-5B02-41DE-8BB0-C53A1326E162}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C3DE665B-5B02-41DE-8BB0-C53A1326E162}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal