From c35301f78b43ff0e299b539e87aee7f9cb57a133 Mon Sep 17 00:00:00 2001 From: amsga <49681949+amsga@users.noreply.github.com> Date: Thu, 8 Oct 2026 21:48:09 +0800 Subject: [PATCH] Added SHA256.cs for SHA-256 implementation for UUIDv8. Added SHA384.cs for SHA-384 implementation for UUIDv8. Added SHA512.cs for SHA-512 implementation for UUIDv8. --- .github/FUNDING.yml | 3 + .github/dependabot.yml | 11 +++ .github/workflows/dotnet.yml | 92 +++++++++++++++++++ .github/workflows/package-release.yml | 56 +++++++++++ CHANGELOG.md | 15 +++ README.md | 68 +++++++++++++- .../SHA256Tests.cs | 52 +++++++++++ .../SHA384Tests.cs | 52 +++++++++++ .../SHA512Tests.cs | 52 +++++++++++ ...Dev.UUID.v8.CryptographicHash.Tests.csproj | 24 +++++ TensionDev.UUID.v8.CryptographicHash.slnx | 1 + .../Class1.cs | 9 -- .../SHA256.cs | 85 +++++++++++++++++ .../SHA384.cs | 87 ++++++++++++++++++ .../SHA512.cs | 87 ++++++++++++++++++ ...ensionDev.UUID.v8.CryptographicHash.csproj | 32 ++++++- 16 files changed, 715 insertions(+), 11 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dotnet.yml create mode 100644 .github/workflows/package-release.yml create mode 100644 CHANGELOG.md create mode 100644 TensionDev.UUID.v8.CryptographicHash.Tests/SHA256Tests.cs create mode 100644 TensionDev.UUID.v8.CryptographicHash.Tests/SHA384Tests.cs create mode 100644 TensionDev.UUID.v8.CryptographicHash.Tests/SHA512Tests.cs create mode 100644 TensionDev.UUID.v8.CryptographicHash.Tests/TensionDev.UUID.v8.CryptographicHash.Tests.csproj delete mode 100644 TensionDev.UUID.v8.CryptographicHash/Class1.cs create mode 100644 TensionDev.UUID.v8.CryptographicHash/SHA256.cs create mode 100644 TensionDev.UUID.v8.CryptographicHash/SHA384.cs create mode 100644 TensionDev.UUID.v8.CryptographicHash/SHA512.cs diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..13f9d90 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +ko_fi: tensiondev diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5072385 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "nuget" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "monthly" diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..e66596c --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,92 @@ +name: .NET + +on: + push: + branches: [ "**" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: windows-latest + + strategy: + matrix: + dotnet: [ '10.0.x' ] + name: .NET ${{ matrix.dotnet }} + + steps: + - name: Set up JDK 25 + uses: actions/setup-java@v6 + with: + java-version: 25 + distribution: 'zulu' # Alternative distribution options are available. + - uses: actions/checkout@v7 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: ${{ matrix.dotnet }} + - name: Cache SonarQube Cloud packages + uses: actions/cache@v5 + with: + path: ~\sonar\cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + - name: Cache SonarQube Cloud scanner + id: cache-sonar-scanner + uses: actions/cache@v5 + with: + path: .\.sonar\scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + - name: Install SonarQube Cloud scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\.sonar\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner + - name: Restore dependencies + run: dotnet restore + - name: SonarCloudPrepare + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: .\.sonar\scanner\dotnet-sonarscanner begin /k:"TensionDev_UUID.v8.CryptographicHash" /o:"tensiondev" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.scanner.scanAll=false /d:sonar.cs.opencover.reportsPaths=**/coverage.opencover.xml + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet run --project TensionDev.UUID.v8.CryptographicHash.Tests/TensionDev.UUID.v8.CryptographicHash.Tests.csproj --no-build -- --coverlet --coverlet-output-format opencover + - name: SonarCloudAnalyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + + test: + + strategy: + matrix: + dotnet: [ '10.0.x' ] + os: [macos-latest, ubuntu-latest, windows-latest] + + runs-on: ${{ matrix.os }} + + name: .NET ${{ matrix.dotnet }} on ${{ matrix.os }} + + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Setup .NET + uses: actions/setup-dotnet@v6 + with: + dotnet-version: ${{ matrix.dotnet }} + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet run --project TensionDev.UUID.v8.CryptographicHash.Tests/TensionDev.UUID.v8.CryptographicHash.Tests.csproj --no-build -- --coverlet --coverlet-output-format opencover diff --git a/.github/workflows/package-release.yml b/.github/workflows/package-release.yml new file mode 100644 index 0000000..0153d82 --- /dev/null +++ b/.github/workflows/package-release.yml @@ -0,0 +1,56 @@ +# This is a basic workflow to help you get started with Actions + +name: Package Release + +# Controls when the workflow will run +on: + # Triggers the workflow on push events but only for the tags + push: + tags: + - v[0-9]+.[0-9]+.[0-9]+ + - v[0-9]+.[0-9]+.[0-9]+-alpha + - v[0-9]+.[0-9]+.[0-9]+-beta + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + permissions: + id-token: write # enable GitHub OIDC token issuance for this job + + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v4 + + - name: Setup .NET 10 + uses: actions/setup-dotnet@v6 + with: + dotnet-version: 10.0.x + source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore + + - name: Build Release + run: dotnet build --configuration Release --no-restore + + - name: Pack + run: dotnet pack --configuration Release + + - name: NuGet login (OIDC ? temp API key) + uses: NuGet/login@v1 + id: login + with: + user: ${{ secrets.NUGET_USER }} # Recommended: use a secret like ${{ secrets.NUGET_USER }} for your nuget.org username (profile name), NOT your email address + + - name: Push NuGet + run: dotnet nuget push "TensionDev.UUID.v8.CryptographicHash/bin/Release/*.nupkg" --source https://api.nuget.org/v3/index.json --api-key ${{steps.login.outputs.NUGET_API_KEY}} --no-symbols --skip-duplicate diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..42a3780 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# TensionDev.UUID.v8.CryptographicHash + +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Added SHA-256 implementation for UUIDv8. +- Added SHA-384 implementation for UUIDv8. +- Added SHA-512 implementation for UUIDv8. + diff --git a/README.md b/README.md index 8b868c6..d516bda 100644 --- a/README.md +++ b/README.md @@ -1 +1,67 @@ -# TensionDev.UUID.v8.CryptographicHash \ No newline at end of file +# TensionDev.UUID.v8.CryptographicHash + +[![.NET](https://github.com/TensionDev/UUID.v8.CryptographicHash/actions/workflows/dotnet.yml/badge.svg)](https://github.com/TensionDev/UUID.v8.CryptographicHash/actions/workflows/dotnet.yml) +[![Package Release](https://github.com/TensionDev/UUID.v8.CryptographicHash/actions/workflows/package-release.yml/badge.svg)](https://github.com/TensionDev/UUID.v8.CryptographicHash/actions/workflows/package-release.yml) +[![CodeQL](https://github.com/TensionDev/UUID.v8.CryptographicHash/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/TensionDev/UUID.v8.CryptographicHash/actions/workflows/github-code-scanning/codeql) + +TensionDev.UUID.v8.CryptographicHash is a .NET library extending [TensionDev.UUID](https://github.com/TensionDev/UUID) with support for name-based UUID Version 8 using Cryptographic Hash Algorithms. +This project references the following documents for implementation. +- [Universally unique identifier - Wikipedia](https://en.wikipedia.org/wiki/Universally_unique_identifier) +- [rfc9562](https://datatracker.ietf.org/doc/html/rfc9562) + +--- + +## Features + +- Support for three SHA-2 hash algorithms: + - SHA-256 + - SHA-384 + - SHA-512 +- Strict RFC compliance for parsing and validation + +--- + +## Installation +```bash +dotnet add package TensionDev.UUID.v8.CryptographicHash +``` + +--- + +## Usage Examples + +### Generate SHA-256 UUID v8 +```csharp +using TensionDev.UUID.v8.CryptographicHash; + +// Generate a UUID using DNS namespace with SHA-256 +Uuid uuid = SHA256.NewUUIDv8(UUIDNamespace.DNS, "www.contoso.com"); +Console.WriteLine(uuid); // Example: 0b3e79a4-6f85-825a-a65d-fc78495c9381 +``` + +### Generate SHA-384 UUID v8 +```csharp +using TensionDev.UUID.v8.CryptographicHash; + +// Generate a UUID using URL namespace with SHA-384 +Uuid uuid = SHA384.NewUUIDv8(UUIDNamespace.URL, "https://www.contoso.com"); +Console.WriteLine(uuid); // Example: 1a261a04-809b-8b96-b370-2cdfaa8dfb9c +``` + +### Generate SHA-512 UUID v8 +```csharp +using TensionDev.UUID.v8.CryptographicHash; + +// Generate a UUID using OID namespace with SHA-512 +Uuid uuid = SHA512.NewUUIDv8(UUIDNamespace.OID, "1.0.3166.1"); +Console.WriteLine(uuid); // c05c49a2-ae23-83e9-913e-34dfe2751dc5 +``` + +### Verify UUID Version 8 +```csharp +using TensionDev.UUID.v8.CryptographicHash; + +Uuid uuid = SHA512.NewUUIDv8(UUIDNamespace.X500, "/c=us/o=Sun/ou=People/cn=Rosanna Lee"); +bool isValid = SHA512.IsUUIDv8(uuid); +Console.WriteLine(isValid); // true +``` diff --git a/TensionDev.UUID.v8.CryptographicHash.Tests/SHA256Tests.cs b/TensionDev.UUID.v8.CryptographicHash.Tests/SHA256Tests.cs new file mode 100644 index 0000000..531c109 --- /dev/null +++ b/TensionDev.UUID.v8.CryptographicHash.Tests/SHA256Tests.cs @@ -0,0 +1,52 @@ +using System; +using Xunit; + +namespace TensionDev.UUID.v8.CryptographicHash.Tests +{ + public class SHA256Tests + { + [Fact] + public void TestNewUUIDv8_DNS() + { + Uuid expectedGuid = new Uuid("44032eb4-74f7-8141-8763-95bcb31a00fe"); + + String name = "www.contoso.com"; + Uuid guid = SHA256.NewUUIDv8(UUIDNamespace.DNS, name); + + Assert.Equal(expectedGuid, guid); + } + + [Fact] + public void TestNewUUIDv8_URL() + { + Uuid expectedGuid = new Uuid("226d46ea-9b85-8da3-8817-276aca4b261b"); + + String name = "https://www.contoso.com"; + Uuid guid = SHA256.NewUUIDv8(UUIDNamespace.URL, name); + + Assert.Equal(expectedGuid, guid); + } + + [Fact] + public void TestNewUUIDv8_OID() + { + Uuid expectedGuid = new Uuid("10356f08-bb14-84f2-a235-eecb59b45093"); + + String name = "1.0.3166.1"; + Uuid guid = SHA256.NewUUIDv8(UUIDNamespace.OID, name); + + Assert.Equal(expectedGuid, guid); + } + + [Fact] + public void TestNewUUIDv8_X500() + { + Uuid expectedGuid = new Uuid("c507cb8a-ae3d-8fb6-955c-e896f927db96"); + + String name = "/c=us/o=Sun/ou=People/cn=Rosanna Lee"; + Uuid guid = SHA256.NewUUIDv8(UUIDNamespace.X500, name); + + Assert.Equal(expectedGuid, guid); + } + } +} diff --git a/TensionDev.UUID.v8.CryptographicHash.Tests/SHA384Tests.cs b/TensionDev.UUID.v8.CryptographicHash.Tests/SHA384Tests.cs new file mode 100644 index 0000000..2eafdce --- /dev/null +++ b/TensionDev.UUID.v8.CryptographicHash.Tests/SHA384Tests.cs @@ -0,0 +1,52 @@ +using System; +using Xunit; + +namespace TensionDev.UUID.v8.CryptographicHash.Tests +{ + public class SHA384Tests + { + [Fact] + public void TestNewUUIDv8_DNS() + { + Uuid expectedGuid = new Uuid("57017b32-f32a-8b1a-8353-90371a43ff48"); + + String name = "www.contoso.com"; + Uuid guid = SHA384.NewUUIDv8(UUIDNamespace.DNS, name); + + Assert.Equal(expectedGuid, guid); + } + + [Fact] + public void TestNewUUIDv8_URL() + { + Uuid expectedGuid = new Uuid("1a261a04-809b-8b96-b370-2cdfaa8dfb9c"); + + String name = "https://www.contoso.com"; + Uuid guid = SHA384.NewUUIDv8(UUIDNamespace.URL, name); + + Assert.Equal(expectedGuid, guid); + } + + [Fact] + public void TestNewUUIDv8_OID() + { + Uuid expectedGuid = new Uuid("0cfd5769-6138-888d-8c4f-0f677985e8b1"); + + String name = "1.0.3166.1"; + Uuid guid = SHA384.NewUUIDv8(UUIDNamespace.OID, name); + + Assert.Equal(expectedGuid, guid); + } + + [Fact] + public void TestNewUUIDv8_X500() + { + Uuid expectedGuid = new Uuid("f72b2828-e8ef-8d98-96e5-a6504cf3d31f"); + + String name = "/c=us/o=Sun/ou=People/cn=Rosanna Lee"; + Uuid guid = SHA384.NewUUIDv8(UUIDNamespace.X500, name); + + Assert.Equal(expectedGuid, guid); + } + } +} diff --git a/TensionDev.UUID.v8.CryptographicHash.Tests/SHA512Tests.cs b/TensionDev.UUID.v8.CryptographicHash.Tests/SHA512Tests.cs new file mode 100644 index 0000000..04560d5 --- /dev/null +++ b/TensionDev.UUID.v8.CryptographicHash.Tests/SHA512Tests.cs @@ -0,0 +1,52 @@ +using System; +using Xunit; + +namespace TensionDev.UUID.v8.CryptographicHash.Tests +{ + public class SHA512Tests + { + [Fact] + public void TestNewUUIDv8_DNS() + { + Uuid expectedGuid = new Uuid("1f58b34f-76c4-8de7-bde4-153328cbb927"); + + String name = "www.contoso.com"; + Uuid guid = SHA512.NewUUIDv8(UUIDNamespace.DNS, name); + + Assert.Equal(expectedGuid, guid); + } + + [Fact] + public void TestNewUUIDv8_URL() + { + Uuid expectedGuid = new Uuid("f1e5150c-0997-8d69-b643-95dc044a901d"); + + String name = "https://www.contoso.com"; + Uuid guid = SHA512.NewUUIDv8(UUIDNamespace.URL, name); + + Assert.Equal(expectedGuid, guid); + } + + [Fact] + public void TestNewUUIDv8_OID() + { + Uuid expectedGuid = new Uuid("c05c49a2-ae23-83e9-913e-34dfe2751dc5"); + + String name = "1.0.3166.1"; + Uuid guid = SHA512.NewUUIDv8(UUIDNamespace.OID, name); + + Assert.Equal(expectedGuid, guid); + } + + [Fact] + public void TestNewUUIDv8_X500() + { + Uuid expectedGuid = new Uuid("3a1bcb1a-c7b9-86de-afc0-a715e399649c"); + + String name = "/c=us/o=Sun/ou=People/cn=Rosanna Lee"; + Uuid guid = SHA512.NewUUIDv8(UUIDNamespace.X500, name); + + Assert.Equal(expectedGuid, guid); + } + } +} diff --git a/TensionDev.UUID.v8.CryptographicHash.Tests/TensionDev.UUID.v8.CryptographicHash.Tests.csproj b/TensionDev.UUID.v8.CryptographicHash.Tests/TensionDev.UUID.v8.CryptographicHash.Tests.csproj new file mode 100644 index 0000000..26bd4cc --- /dev/null +++ b/TensionDev.UUID.v8.CryptographicHash.Tests/TensionDev.UUID.v8.CryptographicHash.Tests.csproj @@ -0,0 +1,24 @@ + + + + net10.0 + + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/TensionDev.UUID.v8.CryptographicHash.slnx b/TensionDev.UUID.v8.CryptographicHash.slnx index 1ed350c..5c1db22 100644 --- a/TensionDev.UUID.v8.CryptographicHash.slnx +++ b/TensionDev.UUID.v8.CryptographicHash.slnx @@ -1,3 +1,4 @@ + diff --git a/TensionDev.UUID.v8.CryptographicHash/Class1.cs b/TensionDev.UUID.v8.CryptographicHash/Class1.cs deleted file mode 100644 index 7530638..0000000 --- a/TensionDev.UUID.v8.CryptographicHash/Class1.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace TensionDev.UUID.v8.CryptographicHash -{ - public class Class1 - { - - } -} diff --git a/TensionDev.UUID.v8.CryptographicHash/SHA256.cs b/TensionDev.UUID.v8.CryptographicHash/SHA256.cs new file mode 100644 index 0000000..269726b --- /dev/null +++ b/TensionDev.UUID.v8.CryptographicHash/SHA256.cs @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright 2021 - 2026 TensionDev +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 TensionDev.UUID.v8.CryptographicHash +{ + /// + /// Class Library to generate Universally Unique Identifier (UUID) / Globally Unique Identifier (GUID) based on Version 8 using SHA-256 namespace name-based. + /// + public static class SHA256 + { + /// + /// Initialises a new GUID/UUID based on Version 8 using SHA-256 namespace name-based + /// + /// A new Uuid object + public static Uuid NewUUIDv8(Uuid nameSpace, String name) + { + Byte[] nsArray = nameSpace.ToByteArray(); + Byte[] nArray = Encoding.UTF8.GetBytes(name); + + Byte[] buffer = new Byte[nsArray.Length + nArray.Length]; + Buffer.BlockCopy(nsArray, 0, buffer, 0, nsArray.Length); + Buffer.BlockCopy(nArray, 0, buffer, nsArray.Length, nArray.Length); + + Byte[] hash; +#if NET6_0_OR_GREATER + hash = System.Security.Cryptography.SHA256.HashData(buffer); +#else + using (System.Security.Cryptography.SHA256 sha256 = System.Security.Cryptography.SHA256.Create()) + { + hash = sha256.ComputeHash(buffer); + } +#endif + + Byte[] hex = new Byte[16]; + + hex[0] = hash[0]; + hex[1] = hash[1]; + hex[2] = hash[2]; + hex[3] = hash[3]; + + hex[4] = hash[4]; + hex[5] = hash[5]; + + hex[6] = (Byte)((hash[6] & 0x0F) + 0x80); + hex[7] = hash[7]; + + hex[8] = (Byte)((hash[8] & 0x3F) + 0x80); + hex[9] = hash[9]; + + hex[10] = hash[10]; + hex[11] = hash[11]; + hex[12] = hash[12]; + hex[13] = hash[13]; + hex[14] = hash[14]; + hex[15] = hash[15]; + + Uuid Id = new Uuid(hex); + + return Id; + } + + /// + /// Returns true if the Uuid specified is Version 8. + /// + /// The Uuid to be tested. + /// Returns true if the Uuid specified is Version 8. + public static bool IsUUIDv8(Uuid uuid) => TensionDev.UUID.UUIDv8.IsUUIDv8(uuid); + } +} diff --git a/TensionDev.UUID.v8.CryptographicHash/SHA384.cs b/TensionDev.UUID.v8.CryptographicHash/SHA384.cs new file mode 100644 index 0000000..cf6b145 --- /dev/null +++ b/TensionDev.UUID.v8.CryptographicHash/SHA384.cs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright 2021 - 2026 TensionDev +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 TensionDev.UUID.v8.CryptographicHash +{ + /// + /// Class Library to generate Universally Unique Identifier (UUID) / Globally Unique Identifier (GUID) based on Version 8 using SHA-384 namespace name-based. + /// + public static class SHA384 + { + /// + /// Initialises a new GUID/UUID based on Version 8 using SHA-384 namespace name-based. + /// + /// The namespace UUID to use for hashing. + /// The name string to hash with the namespace. + /// A new Uuid object + public static Uuid NewUUIDv8(Uuid nameSpace, String name) + { + Byte[] nsArray = nameSpace.ToByteArray(); + Byte[] nArray = Encoding.UTF8.GetBytes(name); + + Byte[] buffer = new Byte[nsArray.Length + nArray.Length]; + Buffer.BlockCopy(nsArray, 0, buffer, 0, nsArray.Length); + Buffer.BlockCopy(nArray, 0, buffer, nsArray.Length, nArray.Length); + + Byte[] hash; +#if NET6_0_OR_GREATER + hash = System.Security.Cryptography.SHA384.HashData(buffer); +#else + using (System.Security.Cryptography.SHA384 sha384 = System.Security.Cryptography.SHA384.Create()) + { + hash = sha384.ComputeHash(buffer); + } +#endif + + Byte[] hex = new Byte[16]; + + hex[0] = hash[0]; + hex[1] = hash[1]; + hex[2] = hash[2]; + hex[3] = hash[3]; + + hex[4] = hash[4]; + hex[5] = hash[5]; + + hex[6] = (Byte)((hash[6] & 0x0F) + 0x80); + hex[7] = hash[7]; + + hex[8] = (Byte)((hash[8] & 0x3F) + 0x80); + hex[9] = hash[9]; + + hex[10] = hash[10]; + hex[11] = hash[11]; + hex[12] = hash[12]; + hex[13] = hash[13]; + hex[14] = hash[14]; + hex[15] = hash[15]; + + Uuid Id = new Uuid(hex); + + return Id; + } + + /// + /// Returns true if the Uuid specified is Version 8. + /// + /// The Uuid to be tested. + /// Returns true if the Uuid specified is Version 8. + public static bool IsUUIDv8(Uuid uuid) => TensionDev.UUID.UUIDv8.IsUUIDv8(uuid); + } +} diff --git a/TensionDev.UUID.v8.CryptographicHash/SHA512.cs b/TensionDev.UUID.v8.CryptographicHash/SHA512.cs new file mode 100644 index 0000000..939ddda --- /dev/null +++ b/TensionDev.UUID.v8.CryptographicHash/SHA512.cs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright 2021 - 2026 TensionDev +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 TensionDev.UUID.v8.CryptographicHash +{ + /// + /// Class Library to generate Universally Unique Identifier (UUID) / Globally Unique Identifier (GUID) based on Version 8 using SHA-512 namespace name-based. + /// + public static class SHA512 + { + /// + /// Initialises a new GUID/UUID based on Version 8 using SHA-512 namespace name-based. + /// + /// The namespace UUID to use for hashing. + /// The name string to hash with the namespace. + /// A new Uuid object + public static Uuid NewUUIDv8(Uuid nameSpace, String name) + { + Byte[] nsArray = nameSpace.ToByteArray(); + Byte[] nArray = Encoding.UTF8.GetBytes(name); + + Byte[] buffer = new Byte[nsArray.Length + nArray.Length]; + Buffer.BlockCopy(nsArray, 0, buffer, 0, nsArray.Length); + Buffer.BlockCopy(nArray, 0, buffer, nsArray.Length, nArray.Length); + + Byte[] hash; +#if NET6_0_OR_GREATER + hash = System.Security.Cryptography.SHA512.HashData(buffer); +#else + using (System.Security.Cryptography.SHA512 sha512 = System.Security.Cryptography.SHA512.Create()) + { + hash = sha512.ComputeHash(buffer); + } +#endif + + Byte[] hex = new Byte[16]; + + hex[0] = hash[0]; + hex[1] = hash[1]; + hex[2] = hash[2]; + hex[3] = hash[3]; + + hex[4] = hash[4]; + hex[5] = hash[5]; + + hex[6] = (Byte)((hash[6] & 0x0F) + 0x80); + hex[7] = hash[7]; + + hex[8] = (Byte)((hash[8] & 0x3F) + 0x80); + hex[9] = hash[9]; + + hex[10] = hash[10]; + hex[11] = hash[11]; + hex[12] = hash[12]; + hex[13] = hash[13]; + hex[14] = hash[14]; + hex[15] = hash[15]; + + Uuid Id = new Uuid(hex); + + return Id; + } + + /// + /// Returns true if the Uuid specified is Version 8. + /// + /// The Uuid to be tested. + /// Returns true if the Uuid specified is Version 8. + public static bool IsUUIDv8(Uuid uuid) => TensionDev.UUID.UUIDv8.IsUUIDv8(uuid); + } +} diff --git a/TensionDev.UUID.v8.CryptographicHash/TensionDev.UUID.v8.CryptographicHash.csproj b/TensionDev.UUID.v8.CryptographicHash/TensionDev.UUID.v8.CryptographicHash.csproj index dbdcea4..792e584 100644 --- a/TensionDev.UUID.v8.CryptographicHash/TensionDev.UUID.v8.CryptographicHash.csproj +++ b/TensionDev.UUID.v8.CryptographicHash/TensionDev.UUID.v8.CryptographicHash.csproj @@ -1,7 +1,37 @@  - netstandard2.0 + netstandard2.0;netcoreapp3.1;net6.0;net8.0;net10.0 + all + True + true + true + 0.1.0-alpha.1 + TensionDev amsga + TensionDev + TensionDev.UUID.v8.CryptographicHash is a .NET library that supports UUID version v8 with strict RFC compliance. + Copyright (c) TensionDev 2021 - 2026 + Apache-2.0 + https://github.com/TensionDev/UUID.v8.CryptographicHash + https://github.com/TensionDev/UUID.v8.CryptographicHash + git + UUID GUID + Initial Release + en-SG + true + snupkg + README.md + + + True + \ + + + + + + +