A pure C# implementation of Shamir's Secret Sharing (SSS) scheme, designed for .NET 8/9 without external library dependencies beyond the .NET Base Class Library.
$ dotnet add package ShamirSecretSharingA pure C# implementation of Shamir's Secret Sharing (SSS) scheme, designed for .NET 8/9 without external library dependencies beyond the .NET Base Class Library.
Shamir's Secret Sharing allows you to split a secret (e.g., a password, an encryption key, or any piece of data) into multiple unique parts called "shares." The original secret can only be reconstructed if a sufficient number of these shares (a predefined threshold) are brought together. If fewer than the threshold number of shares are available, the secret remains completely hidden.
This is a (t, n) threshold scheme:
n: The total number of shares generated.t: The minimum number of shares required to reconstruct the original secret.System.Security.Cryptography for random number generation).byte[] secrets. Convenience methods for string secrets (using UTF-8 encoding by default) are also provided.ShamirSecretSharingService: The main service class for splitting and reconstructing secrets.
using ShamirSecretSharing;
var sss = new ShamirSecretSharingService(); // Uses default prime 257
// var sssCustomPrime = new ShamirSecretSharingService(prime: 503); // Optional custom prime
Share: A record representing a single share.
// public record Share(int X, int[] YValues);
// X: The unique (non-zero) x-coordinate of this share.
// YValues: An array of y-coordinates, one for each byte of the original secret.
To split a secret string into 5 shares, with any 3 required for reconstruction:
using ShamirSecretSharing;
using System.Text;
using System.Collections.Generic;
var sss = new ShamirSecretSharingService();
string originalSecret = "My Top Secret Data!";
int n = 5; // Total shares
int t = 3; // Threshold
// Split a string (UTF-8 encoding by default)
List<Share> shares = sss.SplitSecret(originalSecret, n, t);
// Or split a byte array
// byte[] secretBytes = Encoding.UTF8.GetBytes(originalSecret);
// List<Share> shares = sss.SplitSecret(secretBytes, n, t);
foreach (var share in shares)
{
Console.WriteLine($"Share {share.X} (raw): {share.ToString()}");
Console.WriteLine($"Share {share.X} (serialized): {share.SerializeToString()}");
// Store/distribute these serializedShare strings securely.
}
To reconstruct the secret using a sufficient number of (serialized) shares:
// Assume you have collected at least 't' serialized shares
List<string> collectedSerializedShares = new List<string>
{
// Example: shares[0].SerializeToString(), shares[2].SerializeToString(), shares[4].SerializeToString()
"1:167,129,32,84,111,114,32,83,101,99,114,101,116,32,68,97,116,97,33", // Placeholder
"3:187,157,188,112,111,114,32,83,101,99,114,101,116,32,68,97,116,97,33", // Placeholder
"5:207,185,20,140,111,114,32,83,101,99,114,101,116,32,68,97,116,97,33" // Placeholder
};
List<Share> sharesForReconstruction = new List<Share>();
foreach (string sShareStr in collectedSerializedShares)
{
sharesForReconstruction.Add(Share.DeserializeFromString(sShareStr));
}
if (sharesForReconstruction.Count >= t)
{
try
{
string reconstructedSecret = sss.ReconstructSecretString(sharesForReconstruction, t);
Console.WriteLine($"Reconstructed Secret: {reconstructedSecret}");
// Or for byte arrays:
// byte[] reconstructedBytes = sss.ReconstructSecret(sharesForReconstruction, t);
// Console.WriteLine($"Reconstructed Secret (bytes): {Encoding.UTF8.GetString(reconstructedBytes)}");
}
catch (ArgumentException ex)
{
Console.WriteLine($"Reconstruction failed: {ex.Message}"); // e.g., not enough distinct shares
}
}
else
{
Console.WriteLine("Not enough shares to reconstruct the secret.");
}
The Share record provides methods for serialization:
share.SerializeToString(): Converts a Share object to a string like "X:Y0,Y1,Y2,...".Share.DeserializeFromString(string s): Converts a serialized string back into a Share object._field.Prime)byte[] secrets, as each byte (0-255) can be a field element.n (the total number of shares).ShamirSecretSharingService constructor if needed, but ensure your secret data and n are compatible.System.Security.Cryptography.RandomNumberGenerator for this purpose.t or more shares, they can reconstruct the secret. SSS protects against the loss/compromise of up to t-1 shares.byte[] secrets using GF(257). Adapting it for secrets composed of larger data types (e.g., int arrays) would require adjusting the prime and potentially the YValues storage in the Share record.n) is limited by Prime - 1. For the default prime 257, n can be at most 256.FiniteField.cs: Implements arithmetic operations in a Galois Field GF(p).Share.cs: Defines the Share record and its serialization/deserialization logic.ShamirSecretSharingService.cs: Contains the core logic for splitting and reconstructing secrets.ShamirSecretSharingTests/ (Separate Project): Contains MSTest unit tests.dotnet builddotnet test