Skip to content

Commit

Permalink
Update Docs
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-techkid committed Nov 10, 2024
1 parent 1ffe40f commit e54d4a3
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 7 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
# HashingHandler
# HashingHandler

A C# library containing abstract classes for hashing data of various types.

View & Download this project on [NuGet](https://www.nuget.org/packages/HashingHandler/)

View the documentation for this project at [the website](https://simon-techkid.github.io/HashingHandler/).
4 changes: 0 additions & 4 deletions docfx/docs/toc.yml

This file was deleted.

2 changes: 1 addition & 1 deletion docfx/toc.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- name: Docs
href: docs/
href: ../docs/
- name: API
href: api/
153 changes: 153 additions & 0 deletions docs/IHashingAlgorithm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# IHashingAlgorithm and IHashingAlgorithmAsync

`HashingAlgorithmBase` is the primary base class for implementations of `IHashingAlgorithm` and `IHashingAlgorithmAsync`. It provides support for both synchronous and asynchronous hash computation, using `protected` methods `byte[] ComputeHash()` and `Task<byte[]> ComputeHashAsync()`, as well as synchronous and asynchronous checksum computation of those hashes, using `public` methods `string ComputeHash()` and `Task<string> ComputeHashAsync()`.

## Variants

### IHashingAlgorithm

`IHashingAlgorithm<T>` is used for accessing hash algorithm classes. These classes allow the hashing of byte arrays (`byte[]`), producing `string` checksums of the given data.

### IHashingAlgorithmAsync

`IHashingAlgorithmAsync<T>` allows the asynchronous use of `IHashingAlgorithm<T>`. It implements `IHashingAlgorithm<T>`, so objects capable of asynchronous computation are also able to leverage synchronous methods.

## HashingCrypto and HashingNonCrypto

HashingHandler contains base classes for creating hashes using its `HashingCrypto` and `HashingNonCrypto` classes, allowing hash creation using [HashAlgorithm](https://learn.microsoft.com/dotnet/api/system.security.cryptography.hashalgorithm) and [NonCryptographicHashAlgorithm](https://learn.microsoft.com/dotnet/api/system.io.hashing.noncryptographichashalgorithm), respectively.

Both of these classes implement `IHashingAlgorithm<string>` and `IHashingAlgorithmAsync<string>` because they inherit `HashingNonCrypto<string>` and `HashingAlgorithmBase<string>`. This allows you to create common functionality across your program by using the shared methods in different contexts.

### Getting Started

To get started using `HashingCrypto` and `HashingNonCrypto` abstract classes in your program, you must create a class that implements one of these. Below are samples that allow calculation of SHA256 and XXH3 hashes using both `HashAlgorithm` and `NonCryptographicHashAlgorithm` abstract classes.

In your class implementing `HashingCrypto` or `HashingNonCrypto`, you must provide a method, `GetAlgorithm()`, that returns an instance of a hashing class (ie. `SHA256` or `XxHash3`) derived from either `HashAlgorithm` or `NonCryptographicHashAlgorithm`, respectively. This instance will then be used by the base, implemented class to create hashes using those algorithms.

See the sample implementations below of each.

## Samples

After creating a class that inherits `IHashingAlgorithm<T>` (including the below samples), you are able to compute hashes for data of type `T`, using the `IHashingAlgorithm<T>.ComputeHash()` method. This method returns a `string` of the hash of the data.

#### HashingCrypto

```
class SHA256Hasher : HashingCrypto<string>
{
protected override HashAlgorithm GetAlgorithm()
{
return SHA256.Create(); // Returns new SHA256 object
}
}
```

To create a hash using the SHA256 algorithm, construct this `SHA256Hasher` class.

It can be cast to `IHashingAlgorithm`:
```
IHashingAlgorithm<string> sha256 = new SHA256Hasher();
```

It can also be cast to `IHashingAlgorithmAsync` for asynchronous use, because `HashingCrypto` implements it.
```
IHashingAlgorithmAsync<string> sha256Async = new SHA256Hasher();
```

#### HashingNonCrypto

```
class XXH3Hasher(long seed = 0) : HashingNonCrypto<string>
{
private readonly long _seed = seed; // XxHash3 allows a seed, so we will add seed support to our class
protected override NonCryptographicHashAlgorithm GetAlgorithm()
{
return new XxHash3(_seed);
}
}
```

To create a hash using the XXH3 algorithm, construct this `XXH3Hasher` class.

It can be cast to `IHashingAlgorithm`:
```
IHashingAlgorithm<string> xxh3 = new XXH3Hasher();
```

It can also be cast to `IHashingAlgorithmAsync` for asynchronous use, because `HashingNonCrypto` implements it.
```
IHashingAlgorithmAsync<string> xxh3Async = new XXH3Hasher();
```

#### Create Your Own

To create your own hashing algorithm, create a class implementing `IHashingAlgorithm` or `IHashingAlgorithmAsync`. Remember, `IHashingAlgorithmAsync` implements `IHashingAlgorithm`, so you must provide a synchronous implementation for it!

I've chosen to inherit `HashingAlgorithmBase<T>`, because it provides both asynchronous and synchronous support in the base class. The below example provides a synchronous implementation.

By inheriting `HashingAlgorithmBase`, you make this `XORHash` class an `IHashingAlgorithm<T>` and `IHashingAlgorithmAsync<T>`.

The below example is a simple XOR hash algorithm:

```
class XORHash : HashingAlgorithmBase<object>
{
protected override byte[] ComputeHash(byte[] bytes)
{
// Specify the length of the payload to be hashed.
int payloadLength = bytes.Length; // Let's hash the entire payload.
// Specify the length of the returned hash.
int hashLength = 8; // 8 bytes, 16 characters
// Initialize result array to hold the hash of specified length
byte[] result = new byte[hashLength];
// Perform XOR on the bytes, distributing across each position in result
for (int i = 0; i < payloadLength; i++)
{
result[i % hashLength] ^= bytes[i];
}
return result;
}
}
```

To create a hash using this sample algorithm, construct this `XORHash` class.

It can be cast to `IHashingAlgorithm`:
```
IHashingAlgorithm<object> xor = new XORHash();
```

It can also be cast to `IHashingAlgorithmAsync` for asynchronous use, because `HashingAlgorithmBase` implements it.
```
IHashingAlgorithmAsync<object> xor = new XORHash();
```

Because we didn't override `HashingAlgorithmBase.ComputeHashAsync()` in the above sample implementation, any calls to `xor.ComputeHashAsync()` will use the default implementation, a `Task.Run()` of the synchronous implementation, `ComputeHash()`. If you'd like to create a specific asynchronous implementation, override `ComputeHashAsync()`.

### Conclusion

After creating the `IHashingAlgorithm<T>` or `IHashingAlgorithmAsync<T>` implementing object, you can perform hashing using the methods of `IHashingAlgorithm<T>` or `IHashingAlgorithmAsync<T>`, respectively.

```
// Create an IHashingProvider<T>, where T matches the type of your IHashingAlgorithm<T>.
// Assume we have instantiated an IHashingProvider<string> called 'provider'
// See the above samples on how to create an IHashingAlgorithm.
// Assume we have instantiated an IHashingAlgorithm<string> called 'algorithm'
string textPayload = "Hello World!";
return algorithm.ComputeHash(textPayload, provider);
```

We can also do it asynchronously, if our `algorithm` instance is `IHashingAlgorithmAsync<string>`

```
// Simulated asynchronous method below by using GetAwaiter().GetResult()
return algorithm.ComputeHashAsync(textPayload, provider).GetAwaiter().GetResult();
```

Remember, `IHashingAlgorithmAsync<T>` implements `IHashingAlgorithm<T>`, so you can perform synchronous hashing using an asynchronous hashing capable object using the methods of `IHashingAlgorithm<T>` on an `IHashingAlgorithmAsync<T>`.
60 changes: 60 additions & 0 deletions docs/IHashingProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# IHashingProvider and IHashingProviderAsync

## Variants

### IHashingProvider

`IHashingProvider<T>` is used when an object can convert the data to be hashed of type `T` to a byte array (`byte[]`), so that the byte array can serve as a common type in all hash operations.

### IHashingProviderAsync

`IHashingProviderAsync<T>` is offered when an object of type `T` can be converted to a byte array asynchronously. `IHashingProviderAsync<T>` implements `IHashingProvider<T>`, so objects that are capable of handling asynchronous conversions are also able to leverage the synchronous methods of `IHashingProvider<T>`.

## Examples

You can find several examples of data types that you may want to create hashes for.

### Strings

HashingHandler contains a built-in base class to serve as an `IHashingProvider<T>` for `string` types, `StringHashProviderBase<T>`. Implementers of `StringHashProviderBase<T>` must define how to convert the data of type `T` to a `string`. The base class, `StringHashProviderBase` handles conversion of this string (using the method `ConvertToString`) to bytes, using a byte encoding.

Adding the below class to your program, which implements `StringHashProviderBase`, will provide a structure for hashing `string` instances.

```
class StringProvider : StringHashProviderBase<string>
{
protected override string ConvertToString(string data)
{
return data; // Already string, no conversion necessary
}
// Below is not a required field, but overriding it in your class will allow you to use other byte encodings than the default, UTF8.
protected override Encoding HashedDataEncoding => Encoding.UTF16
}
```

Because this `StringProvider` class inherits `StringHashProviderBase`, it also implements `IHashingProvider`.

To create a provider for converting the `string` data type to `byte[]` for hashing later, construct the example `StringProvider` class.

```
IHashingProvider<string> provider = new StringProvider();
```

You now have an `IHashingProvider<string>` that can be passed to an `IHashingAlgorithm<string>` to create a hash.

```
// Assume we have initialized the provider as well as an IHashingAlgorithm<string> above.
// The IHashingProvider<string> instance is called 'provider'
// The IHashingAlgorithm<string> instance is called 'algorithm'
string testPayload = "Hello World!";
string testHash = algorithm.ComputeHash(testPayload, provider);
Console.WriteLine(testHash);
```

We can compute the hash on another thread, if you've got an `IHashingAlgorithmAsync<string>`. Let's call the instance of the algorithm `algorithm`.

```
string testHash = algorithm.ComputeHashAsync(testPayload, provider).GetAwaiter().GetResult();
```
File renamed without changes.
File renamed without changes.
8 changes: 8 additions & 0 deletions docs/toc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- name: Introduction
href: introduction.md
- name: Getting Started
href: getting-started.md
- name: IHashingAlgorithm and IHashingAlgorithmAsync
href: IHashingAlgorithm.md
- name: IHashingProvider and IHashingProviderAsync
href: IHashingProvider.md
2 changes: 1 addition & 1 deletion src/IHashVerifierAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ public interface IHashVerifierAsync<T> : IHashVerifier<T>
/// <param name="expectedHash">A <see cref="string"/> representing the expected hash for the given <paramref name="data"/> of type <typeparamref name="T"/>.</param>
/// <param name="algorithm">The algorithm used to hash the given data of type <typeparamref name="T"/>.</param>
/// <param name="cancellationToken">A cancellation token allowing the canceling of asynchronous jobs.</param>
/// <returns></returns>
/// <returns>True, if the hashes match. Otherwise, false.</returns>
public Task<bool> VerifyHashAsync(T data, string expectedHash, IHashingAlgorithm<T> algorithm, CancellationToken cancellationToken = default);
}

0 comments on commit e54d4a3

Please sign in to comment.