Skip to content

Commit

Permalink
Secret`1.cs: Introduction of ReadOnlySpans and Spans
Browse files Browse the repository at this point in the history
... as alterantive to byte array parameters and byte array return values.

Resolves: No entry
  • Loading branch information
shinji-san committed Sep 17, 2023
1 parent ca3d5b8 commit 8889e49
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 5 deletions.
67 changes: 62 additions & 5 deletions src/Cryptography/Secret`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ public sealed class Secret<TNumber> : Secret, IEquatable<Secret<TNumber>>, IComp
/// </summary>
/// <param name="secretSource">A secret as array of type <see cref="byte"/></param>
/// <exception cref="T:System.ArgumentNullException"><paramref name="secretSource"/> is <see langword="null"/></exception>
#if NET6_0_OR_GREATER
public Secret(ReadOnlySpan<byte> secretSource)
#else
public Secret(byte[] secretSource)
#endif
{
if (secretSource == null)
{
Expand All @@ -76,7 +80,11 @@ public Secret(byte[] secretSource)

byte maxMarkByte = secretSource.Length == 1 ? MinMarkByte : MaxMarkByte;
byte markByte = (byte)(new Random(buffer[0]).Next(0x01, maxMarkByte) % maxMarkByte);
#if NET6_0_OR_GREATER
byte[] bytes = secretSource.ToArray();
#else
byte[] bytes = (byte[])secretSource.Clone();
#endif
this.secretNumber = bytes.Concat(new[] {markByte}).ToArray();
}

Expand All @@ -93,7 +101,11 @@ public Secret(Calculator secretSource) : this(secretSource.ByteRepresentation.To
/// <param name="encoded">Secret encoded as base-64</param>
/// <remarks>For normal text use the implicit cast from <see cref="string"/> to <see cref="Secret{TNumber}"/></remarks>
/// <exception cref="T:System.ArgumentNullException"><paramref name="encoded"/> is <see langword="null"/>, empty or consists exclusively of white-space characters</exception>
#if NET6_0_OR_GREATER
public Secret(ReadOnlySpan<char> encoded) : this(FromBase64CharSpan(encoded)){ }
#else
public Secret(string encoded) : this(Convert.FromBase64String(encoded)) { }
#endif

/// <summary>
/// Gets the <see cref="Secret{TNumber}"/> byte size.
Expand Down Expand Up @@ -133,25 +145,46 @@ public static implicit operator Calculator<TNumber>(Secret<TNumber> secret)
}

/// <summary>
/// Casts the <see cref="Calculator{TNumber}"/> instance to an <see cref="Secret{TNumber}"/> instance
/// Casts the <see cref="Calculator{TNumber}"/> instance to an <see cref="Secret{TNumber}"/> instance.
/// </summary>
public static implicit operator Secret<TNumber>(Calculator<TNumber> calculator) => new Secret<TNumber>(calculator.ByteRepresentation.ToArray());

/// <summary>
/// Casts the <see cref="byte"/> array instance to an <see cref="Secret{TNumber}"/> instance
/// Casts the <see cref="byte"/> array instance to an <see cref="Secret{TNumber}"/> instance.
/// </summary>
public static implicit operator Secret<TNumber>(byte[] array) => new Secret<TNumber>(array);

#if NET6_0_OR_GREATER
/// <summary>
/// Casts the <see cref="Secret{TNumber}"/> instance to an <see cref="byte"/> array instance
/// Casts the <see cref="ReadOnlySpan{Byte}"/> to an <see cref="Secret{TNumber}"/> instance.
/// </summary>
public static implicit operator Secret<TNumber>(ReadOnlySpan<byte> buffer) => new Secret<TNumber>(buffer);
#endif

/// <summary>
/// Casts the <see cref="Secret{TNumber}"/> instance to an <see cref="byte"/> array instance.
/// </summary>
public static implicit operator byte[](Secret<TNumber> secret) => secret.ToByteArray();

#if NET6_0_OR_GREATER
/// <summary>
/// Casts the <see cref="Secret{TNumber}"/> instance to a <see cref="ReadOnlySpan{Byte}"/>.
/// </summary>
public static implicit operator ReadOnlySpan<byte>(Secret<TNumber> secret) => secret.AsReadOnlySpan();
#endif

/// <summary>
/// Casts the <see cref="string"/> instance to an <see cref="Secret{TNumber}"/> instance
/// </summary>
public static implicit operator Secret<TNumber>(string secret) => new Secret<TNumber>(Encoding.Unicode.GetBytes(secret));

#if NET6_0_OR_GREATER
/// <summary>

Check warning on line 182 in src/Cryptography/Secret`1.cs

View workflow job for this annotation

GitHub Actions / Dotnet 6.0.414

XML comment is not placed on a valid language element

Check warning on line 182 in src/Cryptography/Secret`1.cs

View workflow job for this annotation

GitHub Actions / Dotnet 6.0.414

XML comment is not placed on a valid language element

Check warning on line 182 in src/Cryptography/Secret`1.cs

View workflow job for this annotation

GitHub Actions / build

XML comment is not placed on a valid language element

Check warning on line 182 in src/Cryptography/Secret`1.cs

View workflow job for this annotation

GitHub Actions / build

XML comment is not placed on a valid language element

Check warning on line 182 in src/Cryptography/Secret`1.cs

View workflow job for this annotation

GitHub Actions / build

XML comment is not placed on a valid language element

Check warning on line 182 in src/Cryptography/Secret`1.cs

View workflow job for this annotation

GitHub Actions / build

XML comment is not placed on a valid language element

Check warning on line 182 in src/Cryptography/Secret`1.cs

View workflow job for this annotation

GitHub Actions / Dotnet 7.0.401

XML comment is not placed on a valid language element

Check warning on line 182 in src/Cryptography/Secret`1.cs

View workflow job for this annotation

GitHub Actions / Dotnet 7.0.401

XML comment is not placed on a valid language element
/// Casts the <see cref="ReadOnlySpan{Char}"/> instance to an <see cref="Secret{TNumber}"/> instance
/// </summary>
// public static implicit operator Secret<TNumber>(ReadOnlySpan<char> secret) => new Secret<TNumber>(Encoding.Unicode.GetBytes(secret));
#endif

/// <summary>
/// Equality operator
/// </summary>
Expand Down Expand Up @@ -257,14 +290,25 @@ public override string ToString()
}

/// <summary>
/// Converts the secret to a byte array
/// Converts the secret to a byte array.
/// </summary>
/// <returns>Array of type <see cref="byte"/></returns>
public byte[] ToByteArray()
{
return this.secretNumber.Subset(0, this.secretNumber.Length - MarkByteCount).Clone() as byte[];
return this.secretNumber.Subset(0, this.secretNumber.Length - MarkByteCount);
}

#if NET6_0_OR_GREATER
/// <summary>
/// Converts the secret to a <see cref="ReadOnlySpan{Byte}"/>.
/// </summary>
/// <returns></returns>
public ReadOnlySpan<byte> AsReadOnlySpan()
{
return this.secretNumber.AsSpan(0, this.secretNumber.Length - MarkByteCount);
}
#endif

/// <summary>
/// Converts the value of <see cref="Secret{TNumber}"/> structure to its equivalent <see cref="string"/> representation
/// that is encoded with base-64 digits.
Expand All @@ -274,5 +318,18 @@ public string ToBase64()
{
return Convert.ToBase64String(this.secretNumber, 0, this.secretNumber.Length - MarkByteCount);
}

#if NET6_0_OR_GREATER
private static ReadOnlySpan<byte> FromBase64CharSpan(ReadOnlySpan<char> encoded)
{
if (encoded.IsEmpty || encoded.IsWhiteSpace())
{
throw new ArgumentException(ErrorMessages.EmptyCollection, nameof(encoded));
}

var bytes = new Span<byte>(new byte[((encoded.Length * 3) + 3) / 4]);
return Convert.TryFromBase64Chars(encoded, bytes, out int bytesWritten) ? bytes[..bytesWritten] : Span<byte>.Empty;
}
#endif
}
}
23 changes: 23 additions & 0 deletions tests/SecretTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace SecretSharingDotNet.Test
{
using Cryptography;
using Math;
using System;
using System.Linq;
using System.Numerics;
using Xunit;
Expand Down Expand Up @@ -184,6 +185,17 @@ public void TestSecretToString()
Secret<BigInteger> secret = secretText;
Assert.Equal(secretText, secret.ToString());
}

[Theory]
[InlineData("UG9seWZvbiB6d2l0c2NoZXJuZCBhw59lbiBNw6R4Y2hlbnMgVsO2Z2VsIFLDvGJlbiwgSm9naHVydCB1bmQgUXVhcms=")]
[InlineData("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu")]
[InlineData("bGlnaHQgd29yaw==")]
[InlineData("bGlnaHQgd29yay4=")]
public void TestSecretToBase64String(string base64Secret)
{
Secret<BigInteger> secret = new Secret<BigInteger>(base64Secret);
Assert.Equal(base64Secret, secret.ToBase64());
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
[Theory]
Expand Down Expand Up @@ -218,5 +230,16 @@ public void TestSecretSourceConversion(object secretSource)
Secret<BigInteger> secret2 = new Secret<BigInteger>(base64);
Assert.Equal(base64, secret2.ToBase64());
}

#if NET6_0_OR_GREATER
[Fact]
public void TestSecretAsReadOnlySpan()
{
byte[] bytes = {0x1, 0x2, 0x3, 0x4};
var secret = new Secret<BigInteger>(bytes);
ReadOnlySpan<byte> readOnlySpan = secret;
Assert.Equal(bytes, readOnlySpan.ToArray());
}
#endif
}
}

0 comments on commit 8889e49

Please sign in to comment.