Exemplo n.º 1
0
        public KdfResult DeriveKey(IAns942Parameters param)
        {
            if (!(param is DerAns942Parameters derParams))
            {
                return(new KdfResult("Unable to parse der parameters"));
            }

            if (derParams.KeyLen <= 0 || derParams.KeyLen > 65536)
            {
                return(new KdfResult($"KeyLen must be between [1, 65536]. Value given was: {derParams.KeyLen}"));
            }

            var d       = (int)System.Math.Ceiling(derParams.KeyLen / (decimal)_sha.HashFunction.OutputLen);
            var h       = new BitString(0);
            var counter = BitString.To32BitString(0);

            for (var i = 1; i <= d; i++)
            {
                // Increment Counter
                counter = counter.BitStringAddition(BitString.One());

                // Prepare ANS.1/DER encoded OtherInfo
                var derEncodedOtherInfo = DerEncode(derParams, counter);
                var str = derEncodedOtherInfo.ToHex();
                // H[i] = Hash(ZZ || otherInfo)
                h = h.ConcatenateBits(_sha.HashMessage(derParams.Zz.ConcatenateBits(derEncodedOtherInfo)).Digest);
            }

            return(new KdfResult(h.GetMostSignificantBits(derParams.KeyLen)));
        }
Exemplo n.º 2
0
        private BitString HashGen(BitString v, int bitsToReturn)
        {
            // 1
            var m = bitsToReturn / HashAttributes.OutputLength + (bitsToReturn % HashAttributes.OutputLength != 0 ? 1 : 0);

            // 2
            var data = v.GetDeepCopy();

            // 3
            var W = new BitString(0);

            // 4
            for (var i = 0; i < m; i++)
            {
                // 4.1
                var w = _sha.HashMessage(data).Digest;

                // 4.2
                W = W.ConcatenateBits(w);

                // 4.3
                data = data.BitStringAddition(BitString.One()).GetLeastSignificantBits(HashAttributes.SeedLength);
            }

            // 5
            var returnedBits = W.MSBSubstring(0, bitsToReturn);

            // 6
            return(returnedBits);
        }
Exemplo n.º 3
0
        // Public for use in HashConditioningComponent
        public DrbgResult Hash_Df(BitString data, int bitsToReturn)
        {
            // 0
            if (bitsToReturn > 255 * HashAttributes.OutputLength)
            {
                throw new ArgumentException("Requesting too many bits to return");
            }

            // 1
            var temp = new BitString(0);

            // 2
            var len = bitsToReturn.CeilingDivide(HashAttributes.OutputLength);

            // 3
            var counter = new BitString("01");

            // 4
            for (var i = 0; i < len; i++)
            {
                // 4.1
                var dataToHash = counter.ConcatenateBits(BitString.To32BitString(bitsToReturn)).ConcatenateBits(data);
                temp = temp.ConcatenateBits(_sha.HashMessage(dataToHash).Digest);

                // 4.2
                counter = counter.BitStringAddition(BitString.One());
            }

            // 5
            var requestedBits = temp.MSBSubstring(0, bitsToReturn);

            // 6
            return(new DrbgResult(requestedBits));
        }
Exemplo n.º 4
0
        public KdfResult DeriveKey(IAns942Parameters param)
        {
            if (!(param is ConcatAns942Parameters concatParams))
            {
                return(new KdfResult("Unable to parse concat parameters"));
            }

            if (concatParams.KeyLen <= 0 || concatParams.KeyLen > 65536)
            {
                return(new KdfResult($"KeyLen must be between [1, 65536]. Value given was: {concatParams.KeyLen}"));
            }

            var d       = (int)System.Math.Ceiling(concatParams.KeyLen / (decimal)_sha.HashFunction.OutputLen);
            var counter = BitString.To32BitString(1);
            var h       = new BitString(0);

            for (var i = 1; i <= d; i++)
            {
                // H[i] = Hash(ZZ || counter || otherInfo)
                var hashInput = concatParams.Zz.ConcatenateBits(counter).ConcatenateBits(concatParams.OtherInfo);
                h = h.ConcatenateBits(_sha.HashMessage(hashInput).Digest);

                counter = counter.BitStringAddition(BitString.One());
            }

            return(new KdfResult(h.GetMostSignificantBits(concatParams.KeyLen)));
        }
Exemplo n.º 5
0
        public BitString GetNextIV()
        {
            var currentIV = _iv.GetLeastSignificantBits(_blockSize).GetDeepCopy();

            _iv = _iv.BitStringAddition(BitString.One());

            return(currentIV);
        }
Exemplo n.º 6
0
        /// <summary>
        /// pad10*1
        /// </summary>
        /// <param name="message">Message to which to add padding</param>
        /// <param name="x">Positive integer, rate of sponge function</param>
        /// <returns></returns>
        public static BitString PadMessage(BitString message, int x)
        {
            var m     = message.BitLength;
            var j     = ((-1 * m - 2) % x + x) % x;
            var zeros = new BitString(j);           // Works just fine if j == 0

            // 1 || 0^j || 1
            var padding = BitString.ConcatenateBits(BitString.One(), BitString.ConcatenateBits(zeros, BitString.One()));

            return(BitString.ConcatenateBits(message, padding));
        }
Exemplo n.º 7
0
        private BitString ConcatenatePieceOntoOtherInfo(string workingPiece)
        {
            BitString oi = new BitString(0);

            if (workingPiece.Equals("uPartyInfo", StringComparison.OrdinalIgnoreCase))
            {
                if (_thisPartyKeyAgreementRole == KeyAgreementRole.InitiatorPartyU)
                {
                    oi = oi.ConcatenateBits(BitString.GetAtLeastZeroLengthBitString(_thisPartyOtherInfo.PartyId));
                    oi = oi.ConcatenateBits(BitString.GetAtLeastZeroLengthBitString(_thisPartyOtherInfo.DkmNonce));
                }
                else
                {
                    oi = oi.ConcatenateBits(BitString.GetAtLeastZeroLengthBitString(_otherPartyOtherInfo.PartyId));
                    oi = oi.ConcatenateBits(BitString.GetAtLeastZeroLengthBitString(_otherPartyOtherInfo.DkmNonce));
                }

                return(oi);
            }

            if (workingPiece.Equals("vPartyInfo", StringComparison.OrdinalIgnoreCase))
            {
                if (_thisPartyKeyAgreementRole == KeyAgreementRole.ResponderPartyV)
                {
                    oi = oi.ConcatenateBits(BitString.GetAtLeastZeroLengthBitString(_thisPartyOtherInfo.PartyId));
                    oi = oi.ConcatenateBits(BitString.GetAtLeastZeroLengthBitString(_thisPartyOtherInfo.DkmNonce));
                }
                else
                {
                    oi = oi.ConcatenateBits(BitString.GetAtLeastZeroLengthBitString(_otherPartyOtherInfo.PartyId));
                    oi = oi.ConcatenateBits(BitString.GetAtLeastZeroLengthBitString(_otherPartyOtherInfo.DkmNonce));
                }

                return(oi);
            }

            if (workingPiece.Equals("counter", StringComparison.OrdinalIgnoreCase))
            {
                oi = oi.ConcatenateBits(new BitString(32).BitStringAddition(BitString.One()));

                return(oi);
            }

            if (workingPiece.StartsWith("literal[", StringComparison.OrdinalIgnoreCase))
            {
                // remove the "literal[]" to get just the hex value
                workingPiece = workingPiece.Replace("literal[", "").Replace("]", "");
                oi           = oi.ConcatenateBits(new BitString(workingPiece));

                return(oi);
            }

            throw new ArgumentException(nameof(workingPiece));
        }
Exemplo n.º 8
0
        public void ShouldWrapTheCounterWhenAtMaxValue()
        {
            var initialValue = BitString.Ones(128);
            var subject      = new AdditiveCounter(_aesEngine, initialValue);

            var firstResult  = subject.GetNextIV();
            var secondResult = subject.GetNextIV();
            var thirdResult  = subject.GetNextIV();

            Assert.AreEqual(initialValue, firstResult);
            Assert.AreEqual(BitString.Zeroes(128), secondResult);
            Assert.AreEqual(BitString.ConcatenateBits(BitString.Zeroes(127), BitString.One()), thirdResult);
        }
Exemplo n.º 9
0
        private HashResult Md5Final()
        {
            var originalLength = _fullMessage.Sum(block => block.BitLength);
            var lastBlock      = _fullMessage[_fullMessage.Length - 1];

            // If the last block is full, we need to process and put a new one on the end
            if (lastBlock.BitLength == SizeOfChunk)
            {
                var result = Md5Compress(lastBlock);
                if (!result.Success)
                {
                    return(result);
                }

                _fullMessage = _fullMessage.Append(new BitString(0)).ToArray();
                lastBlock    = _fullMessage[_fullMessage.Length - 1];
            }

            lastBlock = lastBlock.ConcatenateBits(BitString.One());

            if (lastBlock.BitLength > SizeOfChunk)
            {
                return(new HashResult("Invalid block size, must be 512-bits"));
            }

            // If the last block has more than 448 bits, append 0s to fill it and process the full block
            if (lastBlock.BitLength > 448)
            {
                lastBlock = lastBlock.ConcatenateBits(BitString.Zeroes(SizeOfChunk - lastBlock.BitLength));
                var result = Md5Compress(lastBlock);
                if (!result.Success)
                {
                    return(result);
                }

                _fullMessage = _fullMessage.Append(new BitString(0)).ToArray();
                lastBlock    = _fullMessage[_fullMessage.Length - 1];
            }

            // Append zeroes to the last block until it has 448 bits and append 64 bits of original length
            lastBlock = lastBlock.ConcatenateBits(BitString.Zeroes(448 - lastBlock.BitLength));

            var lengthBytes = BitString.To64BitString(originalLength).ToBytes(true);

            lastBlock = lastBlock.ConcatenateBits(new BitString(lengthBytes));

            // Process the final block
            return(Md5Compress(lastBlock));
        }
Exemplo n.º 10
0
        /// <summary>
        /// External Keccak function. This is the method to call.
        /// </summary>
        /// <param name="message">Message to hash</param>
        /// <param name="digestSize">Size of the digest to return</param>
        /// <param name="capacity">Capacity of the function</param>
        /// <param name="outputType">XOF for SHAKE, CONSTANT for SHA3, cXOF for cSHAKE</param>
        /// <param name="cSHAKEPrePad">True if cSHAKE had customization parameters other than empty string</param>
        /// <returns>Message digest as BitString</returns>
        public static BitString Keccak(BitString message, int digestSize, int capacity, bool XOF, bool cSHAKEPrePad = false)
        {
            message = ConvertEndianness(message);

            if (!cSHAKEPrePad && XOF)
            {
                message = BitString.ConcatenateBits(message, BitString.Ones(4));
            }
            else if (!XOF)
            {
                message = BitString.ConcatenateBits(message, BitString.Zero());
                message = BitString.ConcatenateBits(message, BitString.One());
            }

            return(Sponge(message, digestSize, capacity));
        }
Exemplo n.º 11
0
        /// <inheritdoc />
        protected override void GenerateDomainParameters()
        {
            var paramDetails = ParameterSetDetails.GetDetailsForFfcParameterSet(SchemeParameters.KasAlgoAttributes.ParameterSet);

            SetDomainParameters(
                Dsa.GenerateDomainParameters(
                    new FfcDomainParametersGenerateRequest(
                        paramDetails.qLength,
                        paramDetails.pLength,
                        paramDetails.qLength,
                        Dsa.Sha.HashFunction.OutputLen,
                        BitString.One(),
                        PrimeGenMode.Provable,
                        GeneratorGenMode.Canonical
                        )
                    ).PqgDomainParameters);
        }
Exemplo n.º 12
0
        public BitString Expand(BitString pseudoRandomKey, BitString otherInfo, int keyLengthBytes)
        {
            // keyLength comes in as bytes
            var keyLengthBits = keyLengthBytes * 8;
            var n             = keyLengthBits.CeilingDivide(_hmac.OutputLength);
            var t             = new BitString(0);
            var counter       = new BitString("00");
            var result        = new BitString(0);

            for (short i = 1; i <= n; i++)
            {
                counter = counter.BitStringAddition(BitString.One());
                t       = _hmac.Generate(pseudoRandomKey, t.ConcatenateBits(otherInfo).ConcatenateBits(counter)).Mac;
                result  = result.ConcatenateBits(t);
            }

            return(result.GetMostSignificantBits(keyLengthBits));
        }
Exemplo n.º 13
0
        public static BitString Encode(EdPoint point, int b)
        {
            var encoding = new BitString(point.Y, b);

            var xBit = new BitString(point.X, b).GetLeastSignificantBits(1);

            var bytes = new byte[b / 8];

            bytes[0] = 1 << 7;
            if (xBit.Equals(BitString.One()))
            {
                encoding = encoding.OR(new BitString(bytes));
            }
            else
            {
                encoding = encoding.AND(new BitString(bytes).NOT());
            }

            return(BitString.ReverseByteOrder(encoding));      // switch to little endian
        }
Exemplo n.º 14
0
        public BitString GetNextIV()
        {
            if (_iv.BitLength < _blockSize)
            {
                _iv = BitString.Zeroes(_blockSize - _iv.BitLength).ConcatenateBits(_iv);
            }

            var currentIV = _iv.GetLeastSignificantBits(_blockSize).GetDeepCopy();

            // Avoid throwing an exception by subtracting from 000...0
            if (currentIV.Equals(BitString.Zeroes(_blockSize)))
            {
                _iv = BitString.Ones(_blockSize);
            }
            else
            {
                _iv = _iv.BitStringSubtraction(BitString.One());
            }

            return(currentIV);
        }
Exemplo n.º 15
0
        private void Update(BitString seedMaterial)
        {
            BitString v   = V.GetDeepCopy();
            BitString key = Key.GetDeepCopy();

            // 1. temp = Null
            BitString temp = new BitString(0);

            // 2. While (len(temp)<seedlen) do:
            while (temp.BitLength < CounterAttributes.SeedLength)
            {
                v = v
                    .BitStringAddition(BitString.One())
                    .ConcatenateBits(new BitString(CounterAttributes.OutputLength - v.BitLength)); // Add zeroes to bitstring to make it the length of the OutputLength

                BitString outputBlock = BlockEncrypt(key, v);

                temp = temp.ConcatenateBits(outputBlock);
            }

            // 3. temp = Leftmost seedlen bits of temp
            temp = temp.GetMostSignificantBits(CounterAttributes.SeedLength);

            // 4. temp = temp xor provided_data
            Debug.Assert(temp.BitLength == seedMaterial.BitLength);
            temp = temp.XOR(seedMaterial);

            // 5. Key = Leftmost keylen bits of temp
            key = temp.GetMostSignificantBits(CounterAttributes.KeyLength);

            // 6. V = Rightmost outlen bits of temp
            v = temp.GetLeastSignificantBits(CounterAttributes.OutputLength);

            // 7. Return new values of Key and V
            Key = key.GetDeepCopy();
            V   = v.GetDeepCopy();
        }
Exemplo n.º 16
0
        private void GetDataFromPiece(Dictionary <string, BitString> fixedInfoParts, string workingPiece, FixedInfoParameter fixedInfoParameter)
        {
            if (workingPiece.Equals("uPartyInfo", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, BitString.GetAtLeastZeroLengthBitString(fixedInfoParameter.FixedInfoPartyU.PartyId)
                                   .ConcatenateBits(
                                       BitString.GetAtLeastZeroLengthBitString(fixedInfoParameter.FixedInfoPartyU.EphemeralData)));
                return;
            }

            if (workingPiece.Equals("uPartyId", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, BitString.GetAtLeastZeroLengthBitString(fixedInfoParameter.FixedInfoPartyU.PartyId));
                return;
            }

            if (workingPiece.Equals("uEphemeralData", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, BitString.GetAtLeastZeroLengthBitString(fixedInfoParameter.FixedInfoPartyU.EphemeralData));
                return;
            }

            if (workingPiece.Equals("vPartyInfo", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, BitString.GetAtLeastZeroLengthBitString(fixedInfoParameter.FixedInfoPartyV.PartyId)
                                   .ConcatenateBits(
                                       BitString.GetAtLeastZeroLengthBitString(fixedInfoParameter.FixedInfoPartyV.EphemeralData)));
                return;
            }

            if (workingPiece.Equals("vPartyId", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, BitString.GetAtLeastZeroLengthBitString(fixedInfoParameter.FixedInfoPartyV.PartyId));
                return;
            }

            if (workingPiece.Equals("vEphemeralData", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, BitString.GetAtLeastZeroLengthBitString(fixedInfoParameter.FixedInfoPartyV.EphemeralData));
                return;
            }

            if (workingPiece.Equals("L", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, BitString.To32BitString(fixedInfoParameter.L));
                return;
            }

            if (workingPiece.Equals("salt", StringComparison.OrdinalIgnoreCase))
            {
                if (fixedInfoParameter.Salt?.BitLength > 0)
                {
                    // We only want to add the salt to the fixed info when it isn't the default
                    if (fixedInfoParameter.Salt.ToPositiveBigInteger() == 0)
                    {
                        return;
                    }
                    fixedInfoParts.Add(workingPiece, fixedInfoParameter.Salt);
                }
                return;
            }

            if (workingPiece.Equals("iv", StringComparison.OrdinalIgnoreCase))
            {
                if (fixedInfoParameter.Iv?.BitLength > 0)
                {
                    fixedInfoParts.Add(workingPiece, fixedInfoParameter.Iv);
                }
                return;
            }

            if (workingPiece.Equals("counter", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, new BitString(32).BitStringAddition(BitString.One()));
                return;
            }

            if (workingPiece.Equals("algorithmId", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, fixedInfoParameter.AlgorithmId);
                return;
            }

            if (workingPiece.Equals("label", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, fixedInfoParameter.Label);
                return;
            }

            if (workingPiece.Equals("context", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, fixedInfoParameter.Context);
                return;
            }

            if (workingPiece.Equals("t", StringComparison.OrdinalIgnoreCase))
            {
                fixedInfoParts.Add(workingPiece, fixedInfoParameter.T);
                return;
            }

            if (workingPiece.StartsWith("literal[", StringComparison.OrdinalIgnoreCase))
            {
                // remove the "literal[]" to get just the hex value
                workingPiece = workingPiece.Replace("literal[", "").Replace("]", "");
                fixedInfoParts.Add(workingPiece, new BitString(workingPiece));
                return;
            }

            if (workingPiece.StartsWith("entropyBits[", StringComparison.OrdinalIgnoreCase))
            {
                workingPiece = workingPiece.Replace("entropyBits[", "").Replace("]", "");
                int.TryParse(workingPiece, out var result);
                fixedInfoParts.Add("entropyBits", fixedInfoParameter.EntropyBits);
                return;
            }

            throw new ArgumentException($"{nameof(workingPiece)}: {workingPiece}");
        }
Exemplo n.º 17
0
        public virtual KdfResult DeriveKey(BitString z, int keyDataLength, BitString fixedInfo, BitString salt)
        {
            KeyDataLength = keyDataLength;

            // 1. reps =  keydatalen / hashlen.
            var reps = KeyDataLength.CeilingDivide(OutputLength);

            // 2. If reps > (23^2 −1), then return an error indicator without performing the remaining actions.
            if (reps > ((BigInteger)1 << 32) - 1)
            {
                throw new ArgumentException($"{nameof(reps)} exceeds (2^32)-1");
            }

            if (!UseCounter & reps != 1)
            {
                throw new ArgumentException("When utilizing NoCounter, reps should always be 1.");
            }

            // 3. Initialize a 32-bit, big-endian bit string counter as 1 (i.e. 0x00000001).
            var counter = new BitString(32).BitStringAddition(BitString.One());

            if (!UseCounter)
            {
                counter = new BitString(0);
            }

            var messageToH = counter.ConcatenateBits(z).ConcatenateBits(fixedInfo);

            // 4. If counter || Z || FixedInfo is more than max_H_inputlen bits long,
            // then return an error indicator without performing the remaining actions
            if (MaxInputLength != -1 && messageToH.BitLength > MaxInputLength)
            {
                throw new ArgumentException($"{nameof(messageToH)} exceeds length of {MaxInputLength}");
            }

            BitString k = new BitString(0);

            // 5. For i = 1 to reps by 1, do the following:
            for (int i = 0; i < reps - 1; i++)
            {
                // 5.1 Compute K(i) = H(counter || Z || OtherInfo).
                k = k.ConcatenateBits(H(messageToH, salt));

                // 5.2 Increment counter(modulo 23^2), treating it as an unsigned 32 - bit integer.
                counter = counter.BitStringAddition(BitString.One());

                // Update the message to H with the current counter
                messageToH = counter.ConcatenateBits(z).ConcatenateBits(fixedInfo);
            }

            // 6. Let K_Last be set to K(reps) if (keydatalen / hashlen) is an integer; otherwise, let K_Last
            // be set to the(keydatalen mod hashlen) leftmost bits of K(reps)
            if (KeyDataLength % OutputLength == 0)
            {
                k = k.ConcatenateBits(H(messageToH, salt));
            }
            else
            {
                k = k.ConcatenateBits(
                    H(messageToH, salt).GetMostSignificantBits(KeyDataLength % OutputLength)
                    );
            }

            // 7. Set DerivedKeyingMaterial = K(1) || K(2) || … || K(reps-1) || K_Last.
            return(new KdfResult(k.GetMostSignificantBits(keyDataLength)));
        }
Exemplo n.º 18
0
        private DrbgResult GenerateAlgorithmNoDf(int requestedNumberOfBits, BitString additionalInput)
        {
            additionalInput = additionalInput.GetDeepCopy();

            // 1. If reseed_counter > reseed_interval, then return an indication that
            // a reseed is required
            if (ReseedCounter > Attributes.MaxNumberOfRequestsBetweenReseeds)
            {
                return(new DrbgResult(DrbgStatus.ReseedRequired));
            }

            // 2. If (additional_input != Null), then
            if (additionalInput.BitLength != 0)
            {
                // 2.1 temp = len(additional_input)
                int tempLen = additionalInput.BitLength;
                // 2.2 If (temp < seedlen), then
                //     additional_input = additional_input || 0^(seedlen - temp)
                if (tempLen < CounterAttributes.SeedLength)
                {
                    additionalInput = additionalInput.ConcatenateBits(new BitString(CounterAttributes.SeedLength - tempLen));
                }

                // 2.3 (Key, V) = Update(additional_input, Key, V)
                Update(additionalInput);
            }
            else
            {
                // 2 (cont) Else additional_input = 0^seedlen
                additionalInput = new BitString(CounterAttributes.SeedLength);
            }

            // 3. temp = Null
            BitString temp = new BitString(0);

            // 4. While (len(temp) < requested_number_of_bits) do:
            while (temp.BitLength < requestedNumberOfBits)
            {
                // 4.1 V = (V + 1) mod 2^outlen
                V = V
                    .BitStringAddition(BitString.One())
                    .GetLeastSignificantBits(CounterAttributes.OutputLength);

                // 4.2 output_block = Block_Encrypt(Key, V)
                BitString outputBlock = BlockEncrypt(Key, V);

                // 4.3 temp = temp || output_block
                temp = temp.ConcatenateBits(outputBlock);
            }

            // 5. returned_bits = Leftmost requested_number_of_bits of temp
            var returnedBits = temp.GetMostSignificantBits(requestedNumberOfBits);

            // 6. (Key, V) = Update(additional_input, Key, V)
            // Comment: Update for backtracking resistance
            Update(additionalInput);

            // 7. reseed_counter = reseed_counter + 1
            ++ReseedCounter;

            // 8. Return SUCCESS and returned bits; also return Key, V and
            // reseed_counter as the new_working_state
            // NOTE: returned_bits is a function parameter passed by non-const
            // value.  m_Key, m_V, and m_reseed_counter hold the new working state
            return(new DrbgResult(returnedBits));
        }
        public BitString RandomizeMessage(BitString message, int randomizationSecurityStrength)
        {
            var       rv      = _entropyProvider.GetEntropy(randomizationSecurityStrength);
            BitString padding = BitString.One();

            // from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-106.pdf

            /*
             * 1. If(| Ms | ≥ (| rv | -1))
             * {
             *  1.1 padding = 1.
             * }
             * Else
             * {
             *  1.2 padding = 1 || 0(| rv | - | Ms | -1).
             * }
             */
            if (message.BitLength < rv.BitLength - 1)
            {
                padding = padding.ConcatenateBits(BitString.Zeroes(rv.BitLength - message.BitLength - 1));
            }

            // 2. m = Ms || padding.
            var m = message.ConcatenateBits(padding);

            // 3. n is a positive integer, and n = | rv |.
            var n = rv.BitLength;

            // 4. If(n > 1024) then stop and output an error indicator(see Section 3.3).
            if (n < 80 || n > 1024)
            {
                throw new ArgumentOutOfRangeException(nameof(n));
            }

            // 5. counter = ⎣| m | / n⎦.
            var counter = (int)System.Math.Floor((double)m.BitLength / n);

            // 6. remainder = (| m | mod n).
            var remainder = m.BitLength % n;

            /*
             * 7. Concatenate counter copies of the rv to the remainder left - most bits of the rv to get Rv,
             *  such that | Rv | = | m |.
             */
            var Rv = new BitString(0);

            for (var i = 0; i < counter; i++)
            {
                Rv = Rv.ConcatenateBits(rv);
            }
            Rv = Rv.ConcatenateBits(rv.GetLeastSignificantBits(remainder));

            // Sanity check
            if (Rv.BitLength != m.BitLength)
            {
                throw new ArgumentOutOfRangeException(nameof(Rv));
            }

            /*
             * 8. Convert n to a 16 - bit binary string rv_length_indicator using the
             *  rv_length_indicator_generation function specified in the Appendix.
             *  rv_length_indicator = rv_length_indicator_generation(n).
             */
            // this cast should be safe as we're ensuring n <= 1024
            var nBitString = BitString.To16BitString((short)n);

            //9. M = rv || (m ⊕ Rv) || rv_length_indicator(Figure 1).
            return(rv
                   .ConcatenateBits(m.XOR(Rv))
                   .ConcatenateBits(nBitString));
        }