diff --git a/src/Cryptography/ShamirsSecretSharing.cs b/src/Cryptography/ShamirsSecretSharing.cs
index e00ff77..86bcc7e 100644
--- a/src/Cryptography/ShamirsSecretSharing.cs
+++ b/src/Cryptography/ShamirsSecretSharing.cs
@@ -5,6 +5,11 @@ namespace SecretSharingDotNet.Cryptography;
///
public abstract class ShamirsSecretSharing
{
+ ///
+ /// The minimum number of shares required to reconstruct the secret
+ ///
+ protected const int MinimumShareLimit = 2;
+
///
/// Saves the known security levels (Mersenne prime exponents)
///
diff --git a/src/Cryptography/ShamirsSecretSharing`3.cs b/src/Cryptography/ShamirsSecretSharing`3.cs
index 2f5531c..0ade8c4 100644
--- a/src/Cryptography/ShamirsSecretSharing`3.cs
+++ b/src/Cryptography/ShamirsSecretSharing`3.cs
@@ -70,7 +70,7 @@ public class ShamirsSecretSharing
@@ -84,7 +84,7 @@ public int SecurityLevel
set
{
- if (value < 13)
+ if (value < SecurityLevels[0])
{
throw new ArgumentOutOfRangeException(nameof(value), value, ErrorMessages.MinimumSecurityLevelExceeded);
}
@@ -128,7 +128,7 @@ public Shares MakeShares(TNumber numberOfMinimumShares, TNumber numberO
int min = ((Calculator)numberOfMinimumShares).ToInt32();
int max = ((Calculator)numberOfShares).ToInt32();
- if (min < 2)
+ if (min < MinimumShareLimit)
{
throw new ArgumentOutOfRangeException(nameof(numberOfMinimumShares), numberOfMinimumShares, ErrorMessages.MinNumberOfSharesLowerThanTwo);
}
@@ -187,7 +187,7 @@ public Shares MakeShares(TNumber numberOfMinimumShares, TNumber numberO
{
int min = ((Calculator)numberOfMinimumShares).ToInt32();
int max = ((Calculator)numberOfShares).ToInt32();
- if (min < 2)
+ if (min < MinimumShareLimit)
{
throw new ArgumentOutOfRangeException(nameof(numberOfMinimumShares), numberOfMinimumShares, ErrorMessages.MinNumberOfSharesLowerThanTwo);
}
@@ -215,20 +215,40 @@ public Shares MakeShares(TNumber numberOfMinimumShares, TNumber numberO
///
/// Minimum number of shared secrets for reconstruction
///
+#if NET6_0_OR_GREATER
+ private unsafe Calculator[] CreatePolynomial(int numberOfMinimumShares)
+#else
private Calculator[] CreatePolynomial(int numberOfMinimumShares)
+#endif
{
var polynomial = new Calculator[numberOfMinimumShares];
polynomial[0] = Calculator.Zero;
byte[] randomNumber = new byte[this.mersennePrime.ByteCount];
- using (var rng = RandomNumberGenerator.Create())
+#if NET6_0_OR_GREATER
+ fixed (byte* pointer = randomNumber)
{
+ var span = new Span(pointer, this.mersennePrime.ByteCount);
+ using var rng = RandomNumberGenerator.Create();
for (int i = 1; i < numberOfMinimumShares; i++)
{
- rng.GetBytes(randomNumber);
- polynomial[i] = (Calculator.Create(randomNumber, typeof(TNumber)) as Calculator)?.Abs() % this.mersennePrime;
+ rng.GetBytes(span);
+ polynomial[i] = (Calculator.Create(randomNumber, typeof(TNumber)) as Calculator)?.Abs() %
+ this.mersennePrime;
}
- }
+ span.Clear();
+ }
+#else
+ using var rng = RandomNumberGenerator.Create();
+ for (int i = 1; i < numberOfMinimumShares; i++)
+ {
+ rng.GetBytes(randomNumber);
+ polynomial[i] = (Calculator.Create(randomNumber, typeof(TNumber)) as Calculator)?.Abs() %
+ this.mersennePrime;
+ }
+
+ Array.Clear(randomNumber, 0, randomNumber.Length);
+#endif
return polynomial;
}
diff --git a/src/Cryptography/SharedSeparator.cs b/src/Cryptography/SharedSeparator.cs
index e86345d..94588ec 100644
--- a/src/Cryptography/SharedSeparator.cs
+++ b/src/Cryptography/SharedSeparator.cs
@@ -41,5 +41,5 @@ internal static class SharedSeparator
///
/// Separator array for method usage to avoid allocation of a new array.
///
- internal static readonly char[] CoordinateSeparatorArray = { CoordinateSeparator };
-}
\ No newline at end of file
+ internal static readonly char[] CoordinateSeparatorArray = [CoordinateSeparator];
+}
diff --git a/src/Cryptography/Shares.cs b/src/Cryptography/Shares.cs
index f774a59..8bf101f 100644
--- a/src/Cryptography/Shares.cs
+++ b/src/Cryptography/Shares.cs
@@ -118,7 +118,7 @@ internal Shares(Secret secret, IList> shares)
public static implicit operator Shares(string s)
{
var points = s
- .Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
+ .Split([Environment.NewLine], StringSplitOptions.RemoveEmptyEntries)
.Select(line => new FinitePoint(line))
.ToArray();
return new Shares(points);
diff --git a/src/Math/Calculator.cs b/src/Math/Calculator.cs
index 840fbeb..9dbf0e3 100644
--- a/src/Math/Calculator.cs
+++ b/src/Math/Calculator.cs
@@ -117,7 +117,7 @@ protected static Dictionary> GetDerivedCtors
var parameterExpression = Expression.Parameter(paramType);
foreach (var childType in ChildTypes)
{
- var ctorInfo = childType.Value.GetConstructor(new[] {paramType});
+ var ctorInfo = childType.Value.GetConstructor([paramType]);
if (ctorInfo == null)
{
continue;
diff --git a/src/Math/ExtendedEuclideanAlgorithm.cs b/src/Math/ExtendedEuclideanAlgorithm.cs
index 75be33b..d7df016 100644
--- a/src/Math/ExtendedEuclideanAlgorithm.cs
+++ b/src/Math/ExtendedEuclideanAlgorithm.cs
@@ -60,7 +60,11 @@ public ExtendedGcdResult Compute(Calculator a, Calculator Compute(Calculator a, Calculator(lastR, coefficients, quotients);
+ return new ExtendedGcdResult(lastR, [lastX, lastY], [x, y]);
}
-}
\ No newline at end of file
+}
diff --git a/src/Math/SecureBigInteger.cs b/src/Math/SecureBigInteger.cs
new file mode 100644
index 0000000..20be6da
--- /dev/null
+++ b/src/Math/SecureBigInteger.cs
@@ -0,0 +1,559 @@
+// ----------------------------------------------------------------------------
+//
+// Copyright (c) 2024 All Rights Reserved
+//
+// Sebastian Walther
+// 04/01/2024 07:34:00 PM
+// ----------------------------------------------------------------------------
+
+#region License
+
+// ----------------------------------------------------------------------------
+// Copyright 2022 Sebastian Walther
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#endregion
+
+#if NET6_0_OR_GREATER
+namespace SecretSharingDotNet.Math;
+
+using System;
+using System.Buffers.Binary;
+using System.Runtime.InteropServices;
+using System.Text;
+
+///
+/// Represents a secure big integer.
+///
+public sealed class SecureBigInteger : IDisposable
+{
+ private readonly long length;
+ private readonly unsafe uint* digitsPtr;
+ private GCHandle digitsHandle;
+ private bool disposed;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The pointer to the digits.
+ /// The handle to the digits.
+ /// The length of the digits.
+ ///
+ /// The handle must be pinned to prevent the garbage collector from moving the object in memory.
+ ///
+ private unsafe SecureBigInteger(uint* digits, GCHandle digitsHandle, long length)
+ {
+ this.digitsPtr = digits;
+ this.length = length;
+ this.digitsHandle = digitsHandle;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The byte representation of the big integer.
+ public unsafe SecureBigInteger(Span bytes)
+ {
+ uint[] digits = new uint[(bytes.Length + 3) / 4];
+ this.digitsHandle = GCHandle.Alloc(digits, GCHandleType.Pinned);
+ this.digitsPtr = (uint*)this.digitsHandle.AddrOfPinnedObject();
+ this.length = digits.LongLength;
+ //// for (int i = 0; i < bytes.Length; i++)
+ for (int i = 0; i < bytes.Length; i += 4)
+ {
+ //// digits[i / 4] |= (uint)bytes[i] << i % 4 * 8;
+ digits[i / 4] = BinaryPrimitives.ReadUInt32LittleEndian(bytes.Slice(i, 4));
+ }
+ }
+
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~SecureBigInteger()
+ {
+ this.Dispose(false);
+ }
+
+ ///
+ /// Gets the number of bytes of the big integer.
+ ///
+ public long ByteCount => this.length * 4;
+
+ ///
+ /// Gets a value indicating whether the big integer is even.
+ ///
+ public unsafe bool IsEven => (this.digitsPtr[0] & 1) == 0;
+
+ ///
+ /// Gets a value indicating whether the big integer is odd.
+ ///
+ public unsafe bool IsOdd => (this.digitsPtr[0] & 1) == 1;
+
+ ///
+ /// Gets a value indicating whether the is zero.
+ ///
+ ///
+ /// The IsZero property returns true if all the digits of the are zero; otherwise, false.
+ ///
+ public unsafe bool IsZero
+ {
+ get
+ {
+ for (long i = 0; i < this.length; ++i)
+ {
+ if (this.digitsPtr[i] != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the is one.
+ ///
+ public unsafe bool IsOne
+ {
+ get
+ {
+ for (long i = 1; i < this.length; ++i)
+ {
+ if (this.digitsPtr[i] != 0)
+ {
+ return false;
+ }
+ }
+
+ return this.digitsPtr[0] == 1;
+ }
+ }
+
+ ///
+ /// Gets a value representing zero value of .
+ ///
+ public static unsafe SecureBigInteger Zero
+ {
+ get
+ {
+ uint[] zeroDigits = [0];
+ var zeroHandle = GCHandle.Alloc(zeroDigits, GCHandleType.Pinned);
+ return new SecureBigInteger((uint*)zeroHandle.AddrOfPinnedObject(), zeroHandle, 1);
+ }
+ }
+
+ ///
+ /// Gets a value representing one value of .
+ ///
+ public static unsafe SecureBigInteger One
+ {
+ get
+ {
+ uint[] oneDigits = [1];
+ var oneHandle = GCHandle.Alloc(oneDigits, GCHandleType.Pinned);
+ return new SecureBigInteger((uint*)oneHandle.AddrOfPinnedObject(), oneHandle, 1);
+ }
+ }
+
+ ///
+ /// Adds two together and returns a new SecureBigInteger containing the sum.
+ ///
+ /// The to be added.
+ /// A new that is the sum of the current and
+ /// the specified .
+ public unsafe SecureBigInteger Add(SecureBigInteger other)
+ {
+ long maxLen = Math.Max(this.length, other.length);
+
+ uint[] result = new uint[maxLen + 1];
+ var resultDigitsHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
+ ulong carry = 0;
+
+ for (long index = 0; index < maxLen || carry != 0; ++index)
+ {
+ ulong sum = carry;
+ if (index < this.length)
+ {
+ sum += this.digitsPtr[index];
+ }
+
+ if (index < other.length)
+ {
+ sum += other.digitsPtr[index];
+ }
+
+ carry = sum >> 32;
+ result[index] = (uint)sum;
+ }
+
+ uint* resultPtr = (uint*)resultDigitsHandle.AddrOfPinnedObject();
+ return new SecureBigInteger(resultPtr, resultDigitsHandle, result.LongLength);
+ }
+
+ ///
+ /// Subtracts the value of another from this instance.
+ ///
+ /// The to subtract.
+ /// A new containing the result of the subtraction.
+ /// Thrown when attempting to subtract a larger number from a smaller one.
+ public unsafe SecureBigInteger Subtract(SecureBigInteger other)
+ {
+ long maxLen = Math.Max(this.length, other.length);
+
+ uint[] result = new uint[maxLen];
+ var resultDigitsHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
+ ulong borrow = 0;
+
+ for (long index = 0; index < maxLen; ++index)
+ {
+ ulong diff;
+ ulong val1 = index < this.length ? this.digitsPtr[index] : 0;
+ ulong val2 = index < other.length ? other.digitsPtr[index] : 0;
+
+ if (val1 < val2 + borrow)
+ {
+ diff = uint.MaxValue - val2 - borrow + val1 + 1;
+ borrow = 1;
+ }
+ else
+ {
+ diff = val1 - val2 - borrow;
+ borrow = 0;
+ }
+
+ result[index] = (uint)diff;
+ }
+
+ if (borrow != 0)
+ {
+ throw new InvalidOperationException("Cannot subtract a larger number from a smaller one.");
+ }
+
+ uint* resultPtr = (uint*)resultDigitsHandle.AddrOfPinnedObject();
+ return new SecureBigInteger(resultPtr, resultDigitsHandle, result.LongLength);
+ }
+
+ ///
+ /// Multiplies the current instance with another instance.
+ ///
+ /// The instance to multiply with.
+ /// A new instance representing the product of the multiplication.
+ public unsafe SecureBigInteger Multiply(SecureBigInteger other)
+ {
+ long maxLen = this.length + other.length;
+
+ uint[] result = new uint[maxLen];
+ var resultDigitsHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
+
+ for (long i = 0; i < this.length; ++i)
+ {
+ ulong carry = 0;
+ for (long j = 0; j < other.length; ++j)
+ {
+ ulong product = (ulong)(this.digitsPtr[i]) * (ulong)(other.digitsPtr[j]) + carry + result[i + j];
+ result[i + j] = (uint)product;
+ carry = product >> 32;
+ }
+
+ result[i + other.length] = (uint)carry;
+ }
+
+ uint* resultPtr = (uint*)resultDigitsHandle.AddrOfPinnedObject();
+ return new SecureBigInteger(resultPtr, resultDigitsHandle, result.LongLength);
+ }
+
+ ///
+ /// Right-shifts the current by the specified number of bits.
+ ///
+ /// The number of bits to right-shift the current SecureBigInteger.
+ /// A new that is the result of right-shifting the current SecureBigInteger by the specified number of bits.
+ /// Thrown when the shift value is less than zero.
+ public unsafe SecureBigInteger RightShift(int shift)
+ {
+ if (shift < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(shift),
+ "The shift value must be greater than or equal to zero.");
+ }
+
+ int shift32 = shift / 32;
+ int shiftMod32 = shift % 32;
+ long newLength = this.length - shift32;
+ if (shiftMod32 != 0)
+ {
+ newLength++;
+ }
+
+ uint[] result = new uint[newLength];
+ var resultDigitsHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
+
+ for (long i = 0; i < newLength; i++)
+ {
+ result[i] = this.digitsPtr[i + shift32] >> shiftMod32;
+ if (i + 1 < newLength && shiftMod32 != 0)
+ {
+ result[i] |= this.digitsPtr[i + shift32 + 1] << (32 - shiftMod32);
+ }
+ }
+
+ uint* resultPtr = (uint*)resultDigitsHandle.AddrOfPinnedObject();
+ return new SecureBigInteger(resultPtr, resultDigitsHandle, newLength);
+ }
+
+ ///
+ /// Shifts the bits of the to the left by the specified amount.
+ ///
+ /// The number of bits to shift.
+ /// A new that represents the result of the left shift operation.
+ /// Thrown when the shift value is less than zero.
+ public unsafe SecureBigInteger LeftShift(int shift)
+ {
+ if (shift < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(shift),
+ "The shift value must be greater than or equal to zero.");
+ }
+
+ if (shift == 0 || this.IsZero)
+ {
+ return this;
+ }
+
+ int digitShift = shift / 32;
+ int bitShift = shift % 32;
+
+ uint[] result = new uint[this.length + digitShift + 1];
+ var resultDigitsHandle = GCHandle.Alloc(result, GCHandleType.Pinned);
+ ulong carry = 0;
+
+ for (int i = 0; i < this.length; ++i)
+ {
+ ulong temp = ((ulong)this.digitsPtr[i] << bitShift) | carry;
+ result[i + digitShift] = (uint)temp;
+ carry = temp >> 32;
+ }
+
+ if (carry != 0)
+ {
+ result[this.length + digitShift] = (uint)carry;
+ }
+
+ uint* resultPtr = (uint*)resultDigitsHandle.AddrOfPinnedObject();
+ return new SecureBigInteger(resultPtr, resultDigitsHandle, result.LongLength);
+ }
+
+ /// Todo: Issue
+ ///
+ /// Divides the current by the specified and returns the quotient.
+ ///
+ /// The to divide by.
+ /// The quotient obtained by dividing the current SecureBigInteger by the specified .
+ /// Thrown when the divisor is zero.
+ public unsafe SecureBigInteger Divide(SecureBigInteger other)
+ {
+ }
+
+ ///
+ /// Computes the hash code for the object.
+ ///
+ /// The computed hash code.
+ public override int GetHashCode()
+ {
+ unsafe
+ {
+ unchecked
+ {
+ int hash = 17;
+ for (long i = 0; i < this.length; i++)
+ {
+ hash = hash * 29 + this.digitsPtr[i].GetHashCode();
+ }
+
+ return hash;
+ }
+ }
+ }
+
+ ///
+ /// Determines whether the current instance of is equal to another object.
+ ///
+ /// The object to compare with the current instance.
+ ///
+ /// Returns if the current instance is equal to the other object; otherwise, .
+ ///
+ public override bool Equals(object obj)
+ {
+ return ReferenceEquals(this, obj) || obj is SecureBigInteger otherBigInt && this.Equals(otherBigInt);
+ }
+
+ ///
+ /// Determines whether the current instance of is equal to another .
+ ///
+ /// The to compare with the current instance.
+ ///
+ /// if the current instance is equal to the other ; otherwise, .
+ ///
+ public bool Equals(SecureBigInteger other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+
+ unsafe
+ {
+ if (this.length != other.length)
+ {
+ return false;
+ }
+
+ for (long i = 0; i < this.length; i++)
+ {
+ if (this.digitsPtr[i] != other.digitsPtr[i])
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public static SecureBigInteger operator +(SecureBigInteger left, SecureBigInteger right) => left.Add(right);
+ public static SecureBigInteger operator -(SecureBigInteger left, SecureBigInteger right) => left.Subtract(right);
+ public static SecureBigInteger operator *(SecureBigInteger left, SecureBigInteger right) => left.Multiply(right);
+ public static SecureBigInteger operator /(SecureBigInteger left, SecureBigInteger right) => left.Divide(right);
+ public static SecureBigInteger operator >>(SecureBigInteger left, int right) => left.RightShift(right);
+ public static SecureBigInteger operator <<(SecureBigInteger left, int right) => left.LeftShift(right);
+ public static bool operator ==(SecureBigInteger left, SecureBigInteger right) => Equals(left, right);
+ public static bool operator !=(SecureBigInteger left, SecureBigInteger right) => !Equals(left, right);
+
+ #region String Conversion
+ ///
+ /// Todo: Implement a more efficient & secure conversion. Only for testing purposes.
+ ///
+ ///
+ public override unsafe string ToString()
+ {
+ var sb = new StringBuilder();
+ for (long i = this.length - 1; i >= 0; i--)
+ {
+ string binary = Convert.ToString(this.digitsPtr[i], 2).PadLeft(32, '0');
+ sb.Append(binary);
+ }
+
+ string binaryString = sb.ToString();
+ string decimalString = BinaryToDecimal(binaryString);
+ return decimalString;
+ }
+
+ private static string BinaryToDecimal(string binaryString)
+ {
+ string total = "0";
+ for (int i = binaryString.Length - 1; i >= 0; i--)
+ {
+ if (binaryString[i] == '1')
+ {
+ total = AddStrings(total, PowerOfTwo(binaryString.Length - 1 - i));
+ }
+ }
+
+ return total;
+ }
+
+ private static string AddStrings(string num1, string num2)
+ {
+ StringBuilder sb = new StringBuilder();
+ int carry = 0;
+ int i = num1.Length - 1;
+ int j = num2.Length - 1;
+ while (i >= 0 || j >= 0 || carry > 0)
+ {
+ int sum = carry;
+ if (i >= 0)
+ {
+ sum += num1[i--] - '0';
+ }
+
+ if (j >= 0)
+ {
+ sum += num2[j--] - '0';
+ }
+
+ sb.Insert(0, sum % 10);
+ carry = sum / 10;
+ }
+
+ return sb.ToString();
+ }
+
+ private static string PowerOfTwo(int power)
+ {
+ string result = "1";
+ for (int i = 0; i < power; i++)
+ {
+ result = AddStrings(result, result);
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ ///
+ /// Releases the resources used by the object.
+ ///
+ public void Dispose()
+ {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Releases the unmanaged resources used by the and optionally releases the managed
+ /// resources.
+ ///
+ /// to release both managed and unmanaged resources;
+ /// to release only unmanaged resources.
+ private unsafe void Dispose(bool disposing)
+ {
+ if (this.disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ // Dispose managed resources.
+ }
+
+ for (long i = 0; i < this.length; i++)
+ {
+ this.digitsPtr[i] = 0;
+ }
+
+ this.digitsHandle.Free();
+ this.digitsHandle = default;
+
+ this.disposed = true;
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/SecretSharingDotNet.csproj b/src/SecretSharingDotNet.csproj
index a196c0b..db83f4c 100644
--- a/src/SecretSharingDotNet.csproj
+++ b/src/SecretSharingDotNet.csproj
@@ -33,6 +33,14 @@
true
+
+ true
+
+
+
+ true
+
+
ResXFileCodeGenerator