From f5ddbb0c6d022f8197cb6798564b52507d358e32 Mon Sep 17 00:00:00 2001 From: Matthew Layton Date: Sun, 12 Sep 2021 14:27:49 +0100 Subject: [PATCH] Working commit - Version 1.0.0 --- .github/workflows/dotnet.yml | 28 ++ .gitignore | 19 + .idea/.idea.onixlabs-dotnet/.idea/.gitignore | 13 + .../.idea.onixlabs-dotnet/.idea/encodings.xml | 4 + .../.idea/indexLayout.xml | 8 + .idea/.idea.onixlabs-dotnet/.idea/vcs.xml | 6 + OnixLabs.Core.UnitTests/Base16Tests.cs | 54 +++ .../Base32Base32HexAlphabetTests.cs | 70 ++++ .../Base32CrockfordAlphabetTests.cs | 70 ++++ .../Base32DefaultAlphabetTests.cs | 70 ++++ .../Base32ZBase32AlphabetTests.cs | 70 ++++ .../Base58DefaultAlphabetTests.cs | 54 +++ .../Base58FlickrAlphabetTests.cs | 54 +++ .../Base58RippleAlphabetTests.cs | 54 +++ OnixLabs.Core.UnitTests/Base64Tests.cs | 54 +++ OnixLabs.Core.UnitTests/EnumerationTests.cs | 77 ++++ OnixLabs.Core.UnitTests/MockData/Color.cs | 27 ++ .../OnixLabs.Core.UnitTests.csproj | 26 ++ OnixLabs.Core/ArrayExtensions.cs | 71 ++++ OnixLabs.Core/Enumeration.Comparable.cs | 48 +++ OnixLabs.Core/Enumeration.Equatable.cs | 79 ++++ OnixLabs.Core/Enumeration.From.cs | 81 ++++ OnixLabs.Core/Enumeration.Get.cs | 73 ++++ OnixLabs.Core/Enumeration.To.cs | 31 ++ OnixLabs.Core/Enumeration.cs | 44 +++ OnixLabs.Core/Linq/IEnumerableExtensions.cs | 173 +++++++++ OnixLabs.Core/OnixLabs.Core.csproj | 20 + OnixLabs.Core/Text/Base16.Equatable.cs | 76 ++++ OnixLabs.Core/Text/Base16.From.cs | 102 +++++ OnixLabs.Core/Text/Base16.Parse.cs | 57 +++ OnixLabs.Core/Text/Base16.To.cs | 62 +++ OnixLabs.Core/Text/Base16.cs | 36 ++ OnixLabs.Core/Text/Base32.Codec.cs | 160 ++++++++ OnixLabs.Core/Text/Base32.Equatable.cs | 78 ++++ OnixLabs.Core/Text/Base32.From.cs | 353 ++++++++++++++++++ OnixLabs.Core/Text/Base32.Parse.cs | 91 +++++ OnixLabs.Core/Text/Base32.To.cs | 61 +++ OnixLabs.Core/Text/Base32.cs | 50 +++ OnixLabs.Core/Text/Base32Alphabet.cs | 68 ++++ OnixLabs.Core/Text/Base58.Checksum.cs | 97 +++++ OnixLabs.Core/Text/Base58.Codec.cs | 96 +++++ OnixLabs.Core/Text/Base58.Equatable.cs | 77 ++++ OnixLabs.Core/Text/Base58.From.cs | 182 +++++++++ OnixLabs.Core/Text/Base58.Parse.cs | 160 ++++++++ OnixLabs.Core/Text/Base58.To.cs | 72 ++++ OnixLabs.Core/Text/Base58.cs | 43 +++ OnixLabs.Core/Text/Base58Alphabet.cs | 56 +++ OnixLabs.Core/Text/Base64.Equatable.cs | 76 ++++ OnixLabs.Core/Text/Base64.From.cs | 102 +++++ OnixLabs.Core/Text/Base64.Parse.cs | 57 +++ OnixLabs.Core/Text/Base64.To.cs | 62 +++ OnixLabs.Core/Text/Base64.cs | 36 ++ .../OnixLabs.Playground.csproj | 13 + OnixLabs.Playground/Program.cs | 23 ++ .../EcdsaEncryptedPkcs8Tests.cs | 111 ++++++ .../EcdsaKeyPkcs8Tests.cs | 101 +++++ .../EcdsaKeyTests.cs | 118 ++++++ .../HashTests.cs | 43 +++ .../KeyTestBase.cs | 31 ++ .../MerkleTreeTests.cs | 62 +++ ...abs.Security.Cryptography.UnitTests.csproj | 26 ++ .../RsaKeyEncryptedPkcs8Tests.cs | 113 ++++++ .../RsaKeyPkcs8Tests.cs | 105 ++++++ .../RsaKeyTests.cs | 123 ++++++ .../Sha3Hash224Tests.cs | 40 ++ .../Sha3Hash256Tests.cs | 40 ++ .../Sha3Hash384Tests.cs | 53 +++ .../Sha3Hash512Tests.cs | 50 +++ .../Sha3HashTestBase.cs | 45 +++ .../Sha3Shake128Tests.cs | 55 +++ .../Sha3Shake256Tests.cs | 55 +++ .../Sha3ShakeTestBase.cs | 45 +++ .../DigitalSignature.Equatable.cs | 76 ++++ .../DigitalSignature.From.cs | 78 ++++ .../DigitalSignature.To.cs | 100 +++++ .../DigitalSignature.Validation.cs | 98 +++++ .../DigitalSignature.cs | 36 ++ .../EcdsaPrivateKey.Export.cs | 50 +++ .../EcdsaPrivateKey.From.cs | 80 ++++ .../EcdsaPrivateKey.Get.cs | 38 ++ .../EcdsaPrivateKey.Import.cs | 135 +++++++ .../EcdsaPrivateKey.Sign.cs | 52 +++ .../EcdsaPrivateKey.cs | 31 ++ .../EcdsaPublicKey.From.cs | 83 ++++ .../EcdsaPublicKey.Verify.cs | 57 +++ .../EcdsaPublicKey.cs | 31 ++ .../Hash.AllOneHash.cs | 63 ++++ .../Hash.AllZeroHash.cs | 63 ++++ .../Hash.Compute.Md5Hash.cs | 56 +++ .../Hash.Compute.Sha1Hash.cs | 56 +++ .../Hash.Compute.Sha2Hash256.cs | 56 +++ .../Hash.Compute.Sha2Hash384.cs | 56 +++ .../Hash.Compute.Sha2Hash512.cs | 56 +++ .../Hash.Compute.Sha3Hash224.cs | 56 +++ .../Hash.Compute.Sha3Hash256.cs | 56 +++ .../Hash.Compute.Sha3Hash384.cs | 56 +++ .../Hash.Compute.Sha3Hash512.cs | 56 +++ .../Hash.Compute.Sha3Shake128.cs | 59 +++ .../Hash.Compute.Sha3Shake256.cs | 59 +++ .../Hash.Compute.cs | 100 +++++ .../Hash.ComputeTwice.cs | 102 +++++ .../Hash.Concantenate.cs | 83 ++++ .../Hash.Equatable.cs | 76 ++++ OnixLabs.Security.Cryptography/Hash.From.cs | 90 +++++ OnixLabs.Security.Cryptography/Hash.Parse.cs | 47 +++ OnixLabs.Security.Cryptography/Hash.To.cs | 100 +++++ OnixLabs.Security.Cryptography/Hash.cs | 45 +++ .../HashAlgorithmType.cs | 199 ++++++++++ OnixLabs.Security.Cryptography/KeyPair.cs | 209 +++++++++++ .../MerkleTree.Equatable.cs | 77 ++++ OnixLabs.Security.Cryptography/MerkleTree.cs | 146 ++++++++ .../MerkleTreeBranchNode.cs | 48 +++ .../MerkleTreeLeafNode.cs | 55 +++ .../OnixLabs.Security.Cryptography.csproj | 17 + .../PrivateKey.Equatable.cs | 80 ++++ .../PrivateKey.Export.cs | 63 ++++ .../PrivateKey.Get.cs | 28 ++ .../PrivateKey.Sign.cs | 47 +++ .../PrivateKey.To.cs | 100 +++++ OnixLabs.Security.Cryptography/PrivateKey.cs | 45 +++ .../PublicKey.Equatable.cs | 80 ++++ .../PublicKey.To.cs | 100 +++++ .../PublicKey.Verify.cs | 92 +++++ OnixLabs.Security.Cryptography/PublicKey.cs | 45 +++ .../RsaPrivateKey.Equatable.cs | 34 ++ .../RsaPrivateKey.Export.cs | 53 +++ .../RsaPrivateKey.From.cs | 89 +++++ .../RsaPrivateKey.Get.cs | 38 ++ .../RsaPrivateKey.Import.cs | 170 +++++++++ .../RsaPrivateKey.Sign.cs | 56 +++ .../RsaPrivateKey.cs | 40 ++ .../RsaPublicKey.Equatable.cs | 34 ++ .../RsaPublicKey.From.cs | 89 +++++ .../RsaPublicKey.Verify.cs | 58 +++ .../RsaPublicKey.cs | 40 ++ OnixLabs.Security.Cryptography/Sha3.Create.cs | 78 ++++ .../Sha3.Permute.cs | 158 ++++++++ OnixLabs.Security.Cryptography/Sha3.cs | 165 ++++++++ OnixLabs.Security.Cryptography/Sha3Hash224.cs | 39 ++ OnixLabs.Security.Cryptography/Sha3Hash256.cs | 39 ++ OnixLabs.Security.Cryptography/Sha3Hash384.cs | 39 ++ OnixLabs.Security.Cryptography/Sha3Hash512.cs | 39 ++ .../Sha3Shake128.cs | 40 ++ .../Sha3Shake256.cs | 40 ++ README.md | 8 +- onixlabs-dotnet.sln | 53 +++ 146 files changed, 10104 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/dotnet.yml create mode 100644 .idea/.idea.onixlabs-dotnet/.idea/.gitignore create mode 100644 .idea/.idea.onixlabs-dotnet/.idea/encodings.xml create mode 100644 .idea/.idea.onixlabs-dotnet/.idea/indexLayout.xml create mode 100644 .idea/.idea.onixlabs-dotnet/.idea/vcs.xml create mode 100644 OnixLabs.Core.UnitTests/Base16Tests.cs create mode 100644 OnixLabs.Core.UnitTests/Base32Base32HexAlphabetTests.cs create mode 100644 OnixLabs.Core.UnitTests/Base32CrockfordAlphabetTests.cs create mode 100644 OnixLabs.Core.UnitTests/Base32DefaultAlphabetTests.cs create mode 100644 OnixLabs.Core.UnitTests/Base32ZBase32AlphabetTests.cs create mode 100644 OnixLabs.Core.UnitTests/Base58DefaultAlphabetTests.cs create mode 100644 OnixLabs.Core.UnitTests/Base58FlickrAlphabetTests.cs create mode 100644 OnixLabs.Core.UnitTests/Base58RippleAlphabetTests.cs create mode 100644 OnixLabs.Core.UnitTests/Base64Tests.cs create mode 100644 OnixLabs.Core.UnitTests/EnumerationTests.cs create mode 100644 OnixLabs.Core.UnitTests/MockData/Color.cs create mode 100644 OnixLabs.Core.UnitTests/OnixLabs.Core.UnitTests.csproj create mode 100644 OnixLabs.Core/ArrayExtensions.cs create mode 100644 OnixLabs.Core/Enumeration.Comparable.cs create mode 100644 OnixLabs.Core/Enumeration.Equatable.cs create mode 100644 OnixLabs.Core/Enumeration.From.cs create mode 100644 OnixLabs.Core/Enumeration.Get.cs create mode 100644 OnixLabs.Core/Enumeration.To.cs create mode 100644 OnixLabs.Core/Enumeration.cs create mode 100644 OnixLabs.Core/Linq/IEnumerableExtensions.cs create mode 100644 OnixLabs.Core/OnixLabs.Core.csproj create mode 100644 OnixLabs.Core/Text/Base16.Equatable.cs create mode 100644 OnixLabs.Core/Text/Base16.From.cs create mode 100644 OnixLabs.Core/Text/Base16.Parse.cs create mode 100644 OnixLabs.Core/Text/Base16.To.cs create mode 100644 OnixLabs.Core/Text/Base16.cs create mode 100644 OnixLabs.Core/Text/Base32.Codec.cs create mode 100644 OnixLabs.Core/Text/Base32.Equatable.cs create mode 100644 OnixLabs.Core/Text/Base32.From.cs create mode 100644 OnixLabs.Core/Text/Base32.Parse.cs create mode 100644 OnixLabs.Core/Text/Base32.To.cs create mode 100644 OnixLabs.Core/Text/Base32.cs create mode 100644 OnixLabs.Core/Text/Base32Alphabet.cs create mode 100644 OnixLabs.Core/Text/Base58.Checksum.cs create mode 100644 OnixLabs.Core/Text/Base58.Codec.cs create mode 100644 OnixLabs.Core/Text/Base58.Equatable.cs create mode 100644 OnixLabs.Core/Text/Base58.From.cs create mode 100644 OnixLabs.Core/Text/Base58.Parse.cs create mode 100644 OnixLabs.Core/Text/Base58.To.cs create mode 100644 OnixLabs.Core/Text/Base58.cs create mode 100644 OnixLabs.Core/Text/Base58Alphabet.cs create mode 100644 OnixLabs.Core/Text/Base64.Equatable.cs create mode 100644 OnixLabs.Core/Text/Base64.From.cs create mode 100644 OnixLabs.Core/Text/Base64.Parse.cs create mode 100644 OnixLabs.Core/Text/Base64.To.cs create mode 100644 OnixLabs.Core/Text/Base64.cs create mode 100644 OnixLabs.Playground/OnixLabs.Playground.csproj create mode 100644 OnixLabs.Playground/Program.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/EcdsaEncryptedPkcs8Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyPkcs8Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/EcdsaKeyTests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/HashTests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/KeyTestBase.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/MerkleTreeTests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/OnixLabs.Security.Cryptography.UnitTests.csproj create mode 100644 OnixLabs.Security.Cryptography.UnitTests/RsaKeyEncryptedPkcs8Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/RsaKeyPkcs8Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/RsaKeyTests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/Sha3Hash224Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/Sha3Hash256Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/Sha3Hash384Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/Sha3Hash512Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/Sha3HashTestBase.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/Sha3Shake128Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/Sha3Shake256Tests.cs create mode 100644 OnixLabs.Security.Cryptography.UnitTests/Sha3ShakeTestBase.cs create mode 100644 OnixLabs.Security.Cryptography/DigitalSignature.Equatable.cs create mode 100644 OnixLabs.Security.Cryptography/DigitalSignature.From.cs create mode 100644 OnixLabs.Security.Cryptography/DigitalSignature.To.cs create mode 100644 OnixLabs.Security.Cryptography/DigitalSignature.Validation.cs create mode 100644 OnixLabs.Security.Cryptography/DigitalSignature.cs create mode 100644 OnixLabs.Security.Cryptography/EcdsaPrivateKey.Export.cs create mode 100644 OnixLabs.Security.Cryptography/EcdsaPrivateKey.From.cs create mode 100644 OnixLabs.Security.Cryptography/EcdsaPrivateKey.Get.cs create mode 100644 OnixLabs.Security.Cryptography/EcdsaPrivateKey.Import.cs create mode 100644 OnixLabs.Security.Cryptography/EcdsaPrivateKey.Sign.cs create mode 100644 OnixLabs.Security.Cryptography/EcdsaPrivateKey.cs create mode 100644 OnixLabs.Security.Cryptography/EcdsaPublicKey.From.cs create mode 100644 OnixLabs.Security.Cryptography/EcdsaPublicKey.Verify.cs create mode 100644 OnixLabs.Security.Cryptography/EcdsaPublicKey.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.AllOneHash.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.AllZeroHash.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Md5Hash.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha1Hash.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash256.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash384.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha2Hash512.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash224.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash256.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash384.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha3Hash512.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake128.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.Sha3Shake256.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Compute.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.ComputeTwice.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Concantenate.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Equatable.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.From.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.Parse.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.To.cs create mode 100644 OnixLabs.Security.Cryptography/Hash.cs create mode 100644 OnixLabs.Security.Cryptography/HashAlgorithmType.cs create mode 100644 OnixLabs.Security.Cryptography/KeyPair.cs create mode 100644 OnixLabs.Security.Cryptography/MerkleTree.Equatable.cs create mode 100644 OnixLabs.Security.Cryptography/MerkleTree.cs create mode 100644 OnixLabs.Security.Cryptography/MerkleTreeBranchNode.cs create mode 100644 OnixLabs.Security.Cryptography/MerkleTreeLeafNode.cs create mode 100644 OnixLabs.Security.Cryptography/OnixLabs.Security.Cryptography.csproj create mode 100644 OnixLabs.Security.Cryptography/PrivateKey.Equatable.cs create mode 100644 OnixLabs.Security.Cryptography/PrivateKey.Export.cs create mode 100644 OnixLabs.Security.Cryptography/PrivateKey.Get.cs create mode 100644 OnixLabs.Security.Cryptography/PrivateKey.Sign.cs create mode 100644 OnixLabs.Security.Cryptography/PrivateKey.To.cs create mode 100644 OnixLabs.Security.Cryptography/PrivateKey.cs create mode 100644 OnixLabs.Security.Cryptography/PublicKey.Equatable.cs create mode 100644 OnixLabs.Security.Cryptography/PublicKey.To.cs create mode 100644 OnixLabs.Security.Cryptography/PublicKey.Verify.cs create mode 100644 OnixLabs.Security.Cryptography/PublicKey.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPrivateKey.Equatable.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPrivateKey.Export.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPrivateKey.From.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPrivateKey.Get.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPrivateKey.Import.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPrivateKey.Sign.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPrivateKey.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPublicKey.Equatable.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPublicKey.From.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPublicKey.Verify.cs create mode 100644 OnixLabs.Security.Cryptography/RsaPublicKey.cs create mode 100644 OnixLabs.Security.Cryptography/Sha3.Create.cs create mode 100644 OnixLabs.Security.Cryptography/Sha3.Permute.cs create mode 100644 OnixLabs.Security.Cryptography/Sha3.cs create mode 100644 OnixLabs.Security.Cryptography/Sha3Hash224.cs create mode 100644 OnixLabs.Security.Cryptography/Sha3Hash256.cs create mode 100644 OnixLabs.Security.Cryptography/Sha3Hash384.cs create mode 100644 OnixLabs.Security.Cryptography/Sha3Hash512.cs create mode 100644 OnixLabs.Security.Cryptography/Sha3Shake128.cs create mode 100644 OnixLabs.Security.Cryptography/Sha3Shake256.cs create mode 100644 onixlabs-dotnet.sln 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