Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Compatibility.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,10 @@ jeff@ubuntu:~$ gpg --s2k-digest-algo SHA256 --s2k-cipher-algo AES256 -c hello.tx
then hello.txt.gpg was generated)

You can then use any 2 of the above 3 shares and the generated GPG file in
SecretSplitter to decrypt it.
SecretSplitter to decrypt it.

**************************************************************************
Shares produced by version 0.20 of this program may not function properly
with earlier versions of this program; shares produced by earlier versions
of this program my not function properly with version 0.20 of this program.
**************************************************************************
79 changes: 45 additions & 34 deletions SecretSplitter/Security/Cryptography/Diffuser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ namespace Moserware.Security.Cryptography {
/// </summary>
public abstract class Diffuser {
public virtual BigInteger Scramble(BigInteger input, int rawByteLength) {
return Scramble(input);
return Scramble(input, rawByteLength);
}

protected virtual BigInteger Scramble(BigInteger input) {
return input;
}

public virtual BigInteger Unscramble(BigInteger input, int rawByteLength) {
return Unscramble(input);
return Unscramble(input, rawByteLength);
}

protected virtual BigInteger Unscramble(BigInteger input) {
Expand All @@ -38,42 +38,65 @@ public class NullDiffuser : Diffuser {
// my derived code under the MIT license (instead of GPL) and was generously
// granted permission by him. For more license details, see License.txt included
// with this code.
public class XteaDiffuser : Diffuser {
public class XteaDiffuser : Diffuser {
private const int InnerRounds = 32;
private const int OuterRounds = 40;
private const UInt32 Delta = 0x9E3779B9;
private const UInt32 DecodeInitialSum = unchecked(InnerRounds * Delta);

protected override BigInteger Scramble(BigInteger input) {
int actualByteSize;
byte[] integerBytes = GetBigIntegerBytesWithLeastSignificantWordFirstUsing16BitMsbFirstWords(input,
out actualByteSize);
public override BigInteger Scramble(BigInteger input, int rawByteLength) {

for (int i = 0; i < (OuterRounds * actualByteSize); i += 2) {
EncodeSlice(integerBytes, i, actualByteSize, EncipherBlock);
byte[] integerBytes = GetBigIntegerBytesWithLeastSignificantWordFirstUsing16BitMsbFirstWords(input);
int integerBytesNeededSize = (rawByteLength + 1) / 2 * 2;
int padLen = integerBytesNeededSize - integerBytes.Length;
if (padLen > 0) {
byte[] padded = new byte[integerBytesNeededSize];
Array.Copy(integerBytes, padded, integerBytes.Length);
integerBytes = padded;
}
if (rawByteLength % 2 == 1) {
integerBytes[rawByteLength - 1] = integerBytes[rawByteLength];
}
for (int i = 0; i < (OuterRounds * rawByteLength); i += 2) {
EncodeSlice(integerBytes, i, rawByteLength, EncipherBlock);
}
if (rawByteLength % 2 == 1) {
integerBytes[rawByteLength] = integerBytes[rawByteLength - 1];
integerBytes[rawByteLength - 1] = 0;
}

return GetBigIntegerFromLeastSignificantWordsFirstWith16BitMsbFirstWords(integerBytes, actualByteSize);
return GetBigIntegerFromLeastSignificantWordsFirstWith16BitMsbFirstWords(integerBytes);
}

protected override BigInteger Unscramble(BigInteger input) {
int actualByteSize;
byte[] integerBytes = GetBigIntegerBytesWithLeastSignificantWordFirstUsing16BitMsbFirstWords(input,
out actualByteSize);
public override BigInteger Unscramble(BigInteger input, int rawByteLength) {

for (int i = (OuterRounds * actualByteSize) - 2; i >= 0; i -= 2) {
EncodeSlice(integerBytes, i, actualByteSize, DecipherBlock);
byte[] integerBytes = GetBigIntegerBytesWithLeastSignificantWordFirstUsing16BitMsbFirstWords(input);
int integerBytesNeededSize = (rawByteLength + 1) / 2 * 2;
int padLen = integerBytesNeededSize - integerBytes.Length;
if (padLen > 0)
{
byte[] padded = new byte[integerBytesNeededSize];
Array.Copy(integerBytes, padded, integerBytes.Length);
integerBytes = padded;
}
if (rawByteLength % 2 == 1) {
integerBytes[rawByteLength - 1] = integerBytes[rawByteLength];
}
for (int i = (OuterRounds * rawByteLength) - 2; i >= 0; i -= 2) {
EncodeSlice(integerBytes, i, rawByteLength, DecipherBlock);
}
if (rawByteLength % 2 == 1) {
integerBytes[rawByteLength] = integerBytes[rawByteLength - 1];
integerBytes[rawByteLength - 1] = 0;
}

return GetBigIntegerFromLeastSignificantWordsFirstWith16BitMsbFirstWords(integerBytes, actualByteSize);
return GetBigIntegerFromLeastSignificantWordsFirstWith16BitMsbFirstWords(integerBytes);
}

// The whole point of the diffuser is to diffuse bits, that's why we'll pick least significant words
// with most significant word bits. This alone does some diffusion.
private static byte[] GetBigIntegerBytesWithLeastSignificantWordFirstUsing16BitMsbFirstWords(BigInteger input,
out int actualBytesWithoutPadding) {
private static byte[] GetBigIntegerBytesWithLeastSignificantWordFirstUsing16BitMsbFirstWords(BigInteger input) {
byte[] bigEndianBytes = input.ToUnsignedBigEndianBytes();
actualBytesWithoutPadding = bigEndianBytes.Length;
bool isOddNumberOfBytes = bigEndianBytes.Length % 2 != 0;
if (isOddNumberOfBytes) {
// make sure it's even
Expand All @@ -95,22 +118,10 @@ private static byte[] GetBigIntegerBytesWithLeastSignificantWordFirstUsing16BitM
}
}

bool hasExtraPaddingByte = bigEndianBytes.Length != actualBytesWithoutPadding;

if (hasExtraPaddingByte) {
result[bigEndianBytes.Length - 2] = result[bigEndianBytes.Length - 1];
}

return result;
}

private static BigInteger GetBigIntegerFromLeastSignificantWordsFirstWith16BitMsbFirstWords(byte[] wordBytes,
int actualBytes) {
bool hasBytePadding = actualBytes % 2 == 1;
if (hasBytePadding) {
wordBytes[wordBytes.Length - 1] = wordBytes[wordBytes.Length - 2];
wordBytes[wordBytes.Length - 2] = 0;
}
private static BigInteger GetBigIntegerFromLeastSignificantWordsFirstWith16BitMsbFirstWords(byte[] wordBytes) {

byte[] bigEndianBytes = new byte[wordBytes.Length];

Expand Down Expand Up @@ -183,7 +194,7 @@ public override BigInteger Scramble(BigInteger input, int rawByteLength) {

return _XteaDiffuser.Scramble(input, rawByteLength);
}

public override BigInteger Unscramble(BigInteger input, int rawByteLength) {
if(rawByteLength < _ByteCutoff) {
return input;
Expand Down
26 changes: 20 additions & 6 deletions SecretSplitter/Security/Cryptography/SecretCombiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,31 @@ namespace Moserware.Security.Cryptography {
// REVIEW: Keep this as static for simple API usage?
public static class SecretCombiner {
private static readonly Diffuser DefaultDiffuser = new SsssDiffuser();
public static CombinedSecret Combine(string allShares) {
return Combine(Regex.Matches(allShares, SecretShare.RegexPattern).OfType<Match>().Select(m => SecretShare.Parse(m.Value)), DefaultDiffuser);
}

public static CombinedSecret Combine(IEnumerable<string> allShares) {
return Combine(allShares, DefaultDiffuser);
}

public static CombinedSecret Combine(IEnumerable<string> allShares, Diffuser diffuser) {
return Combine(allShares.Select(share => Regex.Match(share, SecretShare.RegexPattern).Value).Select(SecretShare.Parse), diffuser);
}
private static CombinedSecret Combine(IEnumerable<SecretShare> shares, Diffuser diffuser) {
var allShares = shares.ToArray();

if(allShares.Length == 0) {
throw new SecretSplitterException("You must provide at least one secret share (piece).");
}

int expectedShareLength = allShares[0].ParsedValue.Substring(allShares[0].ParsedValue.LastIndexOf('-') + 1).Length;
if(!allShares.All(s => s.ParsedValue.Substring(s.ParsedValue.LastIndexOf('-') + 1).Length == expectedShareLength)) {
throw new SecretSplitterException("Secret shares (pieces) must be be of the same size.");
}

var expectedShareType = allShares[0].ShareType;
if(!allShares.All(s => s.ShareType == expectedShareType)) {
throw new SecretSplitterException("Secret shares (pieces) must be be of the same type.");
Expand All @@ -43,9 +48,18 @@ private static CombinedSecret Combine(IEnumerable<SecretShare> shares, Diffuser

var secretCoefficient = LagrangeInterpolator.EvaluateAtZero(allShares.Select(s => s.Point));
var scrambledValue = secretCoefficient.PolynomialValue;
var unscrambledValue = diffuser.Unscramble(scrambledValue, scrambledValue.ToByteArray().Length);
var unscrambledValue = diffuser.Unscramble(scrambledValue, expectedShareLength / 2);
var recoveredSecret = unscrambledValue.ToUnsignedBigEndianBytes();


int paddingNeeded = expectedShareLength / 2 - recoveredSecret.Length;
if (paddingNeeded > 0) {
var padBytes = new byte[paddingNeeded];
var newArray = new byte[paddingNeeded + recoveredSecret.Length];
Array.Copy(padBytes, 0, newArray, 0, paddingNeeded);
Array.Copy(recoveredSecret, 0, newArray, paddingNeeded, recoveredSecret.Length);
recoveredSecret = newArray;
}

return new CombinedSecret(allShares[0].ShareType, recoveredSecret);
}
}
Expand Down
2 changes: 1 addition & 1 deletion SecretSplitter/Security/Cryptography/SecretSplitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public SecretSplitterException(string message)

namespace Moserware.Security.Cryptography.Versioning {
public static class VersionInfo {
public const string CurrentVersionString = "0.12";
public const string CurrentVersionString = "0.20";
public static Version CurrentVersion = new Version(CurrentVersionString);
}
}