Example #1
0
        private string CreateSecretMessageShares()
        {
            if (!TryVerifyThresholdValues())
            {
                return(null);
            }

            if (String.IsNullOrWhiteSpace(txtSecretMessage.Text))
            {
                ShowError("Enter something for the secret message", "Empty secret");
                return(null);
            }

            var sb = new StringBuilder();

            sb.AppendFormat("Your secret message has been split into the following {0} pieces:", nudShares.Value);
            sb.AppendLine();
            sb.AppendLine();
            int maxShareWidth = 0;

            foreach (var currentSplit in SecretSplitter.SplitMessage(txtSecretMessage.Text, (int)nudThreshold.Value, (int)nudShares.Value))
            {
                sb.AppendLine(currentSplit);
                maxShareWidth = Math.Max(maxShareWidth, currentSplit.Length);
                sb.AppendLine();
            }

            SetWidthFromShareLength(maxShareWidth);

            sb.AppendFormat("To reconstruct your secret, you'll need to provide exactly {0} of the above pieces. Please remember to keep the pieces safe and give them only to people you trust.", nudThreshold.Value);
            sb.AppendLine();

            tabs.SelectedTab = tabRecover;
            return(sb.ToString());
        }
Example #2
0
        public void TestSplits()
        {
            const string helloWorld = "Hello World!";

            // Try it a bunch of times since it's random each time
            for (int i = 0; i < 10; i++)
            {
                var splits    = SecretSplitter.SplitMessage(helloWorld, 2, 3);
                var recovered = SecretCombiner.Combine(splits.Take(2)).RecoveredTextString;
                Assert.AreEqual(helloWorld, recovered);
            }
        }
        private static void SplitSimpleMessage()
        {
            // In this example, we take a very simple message (a string) and split it directly.
            // You can do this for messages below ~1250 characters. However, keep in mind
            // that the size of the message is directly proportional to the size of *each* share/split.

            const string secretMessage = "Hello World!";

            // The threshold is exactly how many shares need to be combined to reconstruct the secret.
            // You must have exactly this many: no more, no less
            const int threshold = 3;

            // The total shares is the number of shares to generate. You can generate as many as you'd like.
            // Any combination of the threshold (3 in this example) can be used to reconstruct the secret;
            const int totalShares = 5;
            var       shares      = SecretSplitter.SplitMessage(secretMessage, threshold, totalShares);

            // Note that every time you run this example you'll get different shares. That's because there is
            // a randomized component to the share generation process.

            Debug.Assert(shares.Length == 5);

            // Notice that *any* combination of three shares works to reconstruct the secret message
            Debug.Assert(SecretCombiner.Combine(new[] { shares[0], shares[1], shares[2] }).RecoveredTextString == secretMessage);
            Debug.Assert(SecretCombiner.Combine(new[] { shares[0], shares[1], shares[3] }).RecoveredTextString == secretMessage);
            Debug.Assert(SecretCombiner.Combine(new[] { shares[0], shares[1], shares[4] }).RecoveredTextString == secretMessage);
            Debug.Assert(SecretCombiner.Combine(new[] { shares[0], shares[2], shares[3] }).RecoveredTextString == secretMessage);
            Debug.Assert(SecretCombiner.Combine(new[] { shares[0], shares[2], shares[4] }).RecoveredTextString == secretMessage);
            Debug.Assert(SecretCombiner.Combine(new[] { shares[0], shares[3], shares[4] }).RecoveredTextString == secretMessage);
            Debug.Assert(SecretCombiner.Combine(new[] { shares[1], shares[2], shares[3] }).RecoveredTextString == secretMessage);
            Debug.Assert(SecretCombiner.Combine(new[] { shares[1], shares[2], shares[4] }).RecoveredTextString == secretMessage);
            Debug.Assert(SecretCombiner.Combine(new[] { shares[1], shares[3], shares[4] }).RecoveredTextString == secretMessage);
            Debug.Assert(SecretCombiner.Combine(new[] { shares[2], shares[3], shares[4] }).RecoveredTextString == secretMessage);

            // However, having less shares doesn't work
            Debug.Assert(SecretCombiner.Combine(shares.Take(threshold - 1)).RecoveredTextString != secretMessage);

            // Having more shares doesn't work either
            Debug.Assert(SecretCombiner.Combine(shares.Take(threshold + 1)).RecoveredTextString != secretMessage);
        }
        public async Task GenerateSplitedSecret(SecureString hsmSecret, int electionId)
        {
            using (var initializedHsm = await hsmFactory.GetHsm(hsmSecret))
            {
                var keys = keyManager.GenerateNewSerializedKeyPair();

                var handle             = initializedHsm.GenerateMasterKey();
                var encryptedSecretKey = initializedHsm.Encrypt(handle, keys.Secretkey);

                var totalNumberOfShares = (int)ShareHolders.Sum(s => s.NumberOfShares);
                var splitedSecret       = SecretSplitter.SplitMessage(KeySerializer.ByteArrayToString(encryptedSecretKey), Treshold, totalNumberOfShares);
                int index = 0;

                foreach (var shareHolder in ShareHolders)
                {
                    var shares = splitedSecret.Skip(index).Take((int)shareHolder.NumberOfShares);
                    index += (int)shareHolder.NumberOfShares;
                    var bytes = SerializeShares(shares.ToArray());
                    await shareHolder.SaveShares(bytes);
                }

                await centralDbRepository.SaveCryptoDetailsAsync(electionId, handle, keys.PublicKey);
            }
        }
Example #5
0
        private void ProduceWalletParts()
        {
            if (PrivateKey == null || Signature == null)
            {
                return;
            }

            var _parts = new List <WalletPart>(Signature.PartsTotal);

            // Generate Parts from the Private Key
            var shares = SecretSplitter
                         .SplitMessage(PrivateKey, Signature.PartsThreshold, Signature.PartsTotal);

            foreach (var share in shares)
            {
                _parts.Add(new WalletPart()
                {
                    Signature = Signature,
                    Data      = share
                });
            }

            Parts = _parts;
        }
        private static void SplitFile()
        {
            // If your message is above 1K, you should use the hybrid approach of splitting the master secret
            // (which is the encryption key) and then use that to encrypt the file using OpenPGP and AES.

            // Here's an example of how to do this hybrid approach:

            // First, get a master secret of whatever size you want. 128 bits is plenty big and is a nice
            // balance of share sizes and security. However, to be fun, let's be super paranoid and go with
            // 256 bits (at the cost of bigger shares!)
            var masterPassword      = HexadecimalPasswordGenerator.GeneratePasswordOfBitSize(bitSize: 256);
            var masterPasswordBytes = SecretEncoder.ParseHexString(masterPassword);

            // As mentioned above, the threshold is the total number of shares that need to come together
            // to unlock the secret.
            const int threshold = 3;

            // Now we create a class to help us encrypt everything else:
            var splitSecret = SecretSplitter.SplitFile(masterPasswordBytes, threshold);

            // We can generate as many shares as we'd like knowing that 3 of them need to come together
            // to reconstruct the secret.
            const int totalShares = 5;
            var       shares      = splitSecret.GetShares(totalShares);

            // The textual representation is what you'd typically distribute:
            var sharesText = shares.Select(s => s.ToString()).ToList();

            // Remember that the shares are just mechanisms to distribute the master password. You still
            // need to distribute the encrypted file

            // Normally, you'd probably use the simpler method of
            // splitSecret.EncryptFile(inputPath, outputPath);

            // But this sample will use the more generic stream based version to keep everything in memory

            // Let's get a spot to store the encrypted output
            Stream encryptedStream;

            // First, let's make up a sample message of the numbers 1-1000
            // As mentioned before, normally you'd just use a simple file name with EncryptFile or an
            // existing stream:

            // The OpenPGP format stores the name of the file, so we provide that here. It can be whatever
            // you want
            const string fileNameInsideEncryptedContainer = "numbers.txt";

            using (var inputStream = new MemoryStream())
                using (var streamWriter = new StreamWriter(inputStream)) {
                    // Generate the numbers 1..1000 each on a single line
                    for (int i = 1; i <= 1000; i++)
                    {
                        streamWriter.WriteLine(i);
                    }

                    // we're done writing
                    streamWriter.Flush();

                    // Note that the size is bigger than 1250 bytes (the limit of splitting the message itself):
                    Debug.Assert(inputStream.Length > 1250);

                    // Now we can use the input. Reset its position to the start of the stream:
                    inputStream.Position = 0;

                    // Finally, encrypt the file. Save it with the filename metadata:
                    encryptedStream = splitSecret.Encrypt(inputStream, fileNameInsideEncryptedContainer);
                }

            // We can save the contents of outputStream wherever we'd like. It's encrypted.

            // Let's go ahead and decrypt it.

            // First, take the threshold number of shares to recover the master secret:
            var combinedSecret = SecretCombiner.Combine(sharesText.Take(threshold));

            // This metadata is present inside the encrypted file
            string   decryptedFileName;
            DateTime decryptedFileDateTime;

            using (var decryptedStream = combinedSecret.Decrypt(encryptedStream, out decryptedFileName, out decryptedFileDateTime))
                using (var decryptedStreamReader = new StreamReader(decryptedStream)) {
                    // For fun, verify the decrypted file is what we expect:

                    Debug.Assert(decryptedFileName == fileNameInsideEncryptedContainer);
                    for (int expectedNumber = 1; expectedNumber <= 1000; expectedNumber++)
                    {
                        var currentLineText   = decryptedStreamReader.ReadLine();
                        var currentLineNumber = Int32.Parse(currentLineText);
                        Debug.Assert(currentLineNumber == expectedNumber);
                    }

                    // Nothing left:
                    Debug.Assert(decryptedStreamReader.EndOfStream);
                }
        }
Example #7
0
        private void CreateSecretFileShares()
        {
            if (!TryVerifyThresholdValues())
            {
                return;
            }

            if (!File.Exists(txtSecretFilePath.Text))
            {
                ShowError("Secret file does not exist", "File Not Found");
                return;
            }

            if (String.IsNullOrEmpty(txtMasterKey.Text))
            {
                ShowError("A valid key is required to encrypt a file.", "Invalid Key Length");
                return;
            }

            byte[] keyBytes;
            if (!SecretEncoder.TryParseHexString(txtMasterKey.Text, out keyBytes))
            {
                ShowError("The key must contain all hexadecimal characters.", "Invalid Key Character");
                return;
            }

            SplitSecret splitSecret = SecretSplitter.SplitFile(keyBytes, (int)nudThreshold.Value);

            saveFileDialog.Filter           = DialogFilter;
            saveFileDialog.Title            = "Save Encrypted File";
            saveFileDialog.InitialDirectory = Path.GetDirectoryName(txtSecretFilePath.Text);
            saveFileDialog.FileName         = Path.GetFileName(Path.ChangeExtension(txtSecretFilePath.Text, ".splitsecret"));
            saveFileDialog.AddExtension     = true;

            ShowInfo("You'll now have to specify where to save the encrypted file.", "Specify Encrypted File Path");
            if (saveFileDialog.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            splitSecret.EncryptFile(txtSecretFilePath.Text, saveFileDialog.FileName);

            var sb = new StringBuilder();

            sb.AppendLine("Here are your secret pieces that form the decryption key for the encrypted file located at:");
            sb.AppendLine(saveFileDialog.FileName);
            sb.AppendLine();
            int shareWidth = saveFileDialog.FileName.Length;

            foreach (var currentShare in splitSecret.GetShares((int)nudShares.Value))
            {
                string currentShareText = currentShare.ToString();
                sb.AppendLine(currentShareText);
                shareWidth = Math.Max(shareWidth, currentShareText.Length);
                sb.AppendLine();
            }

            SetWidthFromShareLength(shareWidth);

            sb.AppendLine("When distributing the secret pieces, remember to also make sure that you distribute the encrypted file. It's safe to email the encrypted file, but you should securely distribute the secret pieces (i.e. in person).");
            sb.AppendLine();
            sb.AppendLine("Make sure that each person knows that exactly " + ((int)nudThreshold.Value) + " pieces are required to reconstruct the file.");

            txtShares.Text   = sb.ToString();
            tabs.SelectedTab = tabRecover;
        }
Example #8
0
        private static void Split()
        {
            int degree = Arguments.HasSpecifiedSecurityLevel
                             ? Arguments.SecurityLevel
                             : IrreduciblePolynomial.MaxDegree;

            if (!Arguments.QuietMode)
            {
                Console.Write("Generating shares using a ({0},{1}) scheme with ", Arguments.Threshold, Arguments.Shares);

                if (Arguments.HasSpecifiedSecurityLevel)
                {
                    Console.Write("a {0} bit", Arguments.SecurityLevel);
                }
                else
                {
                    Console.Write("dynamic");
                }

                Console.WriteLine(" security level.");

                Console.Error.Write("Enter the secret, ");

                if (Arguments.HexMode)
                {
                    Console.Error.Write("at most {0} hex digits: ", degree / 4);
                }
                else
                {
                    Console.Error.Write("at most {0} ASCII characters: ", degree / 8);
                }
            }

            var secret = String.Empty;

            try {
                secret = ReadLineHidden();
            }
            catch {
                Fatal("I/O error while reading secret");
            }

            int securitySize = Arguments.SecurityLevel;

            if (!Arguments.HasSpecifiedSecurityLevel)
            {
                securitySize = Arguments.HexMode
                                   ? 4 * (((secret.Length + 1) & ~1))
                                   : 8 * secret.Length;

                if (!IrreduciblePolynomial.IsValidDegree(securitySize))
                {
                    Fatal("security level invalid (secret too long?)");
                }

                if (!Arguments.QuietMode)
                {
                    Console.Error.WriteLine("Using a {0} bit security level.", securitySize);
                }
            }

            Diffuser diffuser = new NullDiffuser();

            if (Arguments.EnableDiffusion)
            {
                if (securitySize >= 64)
                {
                    diffuser = new XteaDiffuser();
                }
                else
                {
                    Warning("security level too small for the diffusion layer");
                }
            }

            var secretBytes = MakeSecretBytes(secret, Arguments.HexMode, degree);

            foreach (var share in SecretSplitter.Split(SecretShareType.Message, secretBytes, Arguments.Threshold, diffuser).GetShares(Arguments.Shares))
            {
                Console.WriteLine(share.ToString(SecretShareFormattingOptions.None));
            }
        }