public void TotMessageTest() { Assert.Equal(TotPurpose.Ping, TotPing.Instance.Purpose); Assert.Equal(TotPurpose.Pong, TotPong.Instance(TotMessageId.Random).Purpose); Assert.Equal(TotPurpose.Success, TotResponse.Success(TotMessageId.Random).Purpose); Assert.Equal(TotPurpose.BadRequest, TotResponse.BadRequest(TotMessageId.Random).Purpose); Assert.Equal(TotPurpose.VersionMismatch, TotResponse.VersionMismatch(TotMessageId.Random).Purpose); Assert.Equal(TotPurpose.UnsuccessfulRequest, TotResponse.UnsuccessfulRequest(TotMessageId.Random).Purpose); var x = new TotRequest("status"); Assert.Equal(97, x.GetLastCellFullnessPercentage()); Assert.Equal(1, x.GetNumberOfCells()); Assert.Equal(497, x.GetNumberOfDummyBytesInLastCell()); var messages = TotMessageBase.SplitByMessages(ByteHelpers.Combine( TotPing.Instance.ToBytes(), TotPong.Instance(TotMessageId.Random).ToBytes(), TotResponse.BadRequest(TotMessageId.Random).ToBytes(), TotResponse.Success(TotMessageId.Random).ToBytes(), TotResponse.UnsuccessfulRequest(TotMessageId.Random).ToBytes(), TotResponse.VersionMismatch(TotMessageId.Random).ToBytes(), new TotRequest("fooPurpose", new TotContent("foo content")).ToBytes())); Assert.Equal(7, messages.Count()); }
public override byte[] ToBytes() => ByteHelpers.Combine( new byte[] { Ver.ToByte(), Cmd.ToByte(), Rsv.ToByte(), Atyp.ToByte() }, DstAddr.ToBytes(), DstPort.ToBytes());
public override byte[] ToBytes() => ByteHelpers.Combine( new byte[] { Ver.ToByte(), NMethods.ToByte() }, Methods.ToBytes());
/// <param name="dstAddr">domain or IPv4</param> public AddrField(string dstAddr) { dstAddr = Guard.NotNullOrEmptyOrWhitespace(nameof(dstAddr), dstAddr, true); var atyp = new AtypField(); atyp.FromDstAddr(dstAddr); Atyp = atyp; byte[] bytes; if (atyp == AtypField.DomainName) { // https://www.ietf.org/rfc/rfc1928.txt // the address field contains a fully-qualified domain name. The first // octet of the address field contains the number of octets of name that // follow, there is no terminating NUL octet. var domainBytes = Encoding.ASCII.GetBytes(dstAddr); // Tor only knows ASCII, UTF8 results in general SOCKS server failure var numberOfOctets = domainBytes.Length; if (numberOfOctets > 255) { throw new FormatException($"{nameof(dstAddr)} can be maximum 255 octets. Actual: {numberOfOctets} octets. Value: {dstAddr}."); } bytes = ByteHelpers.Combine(new byte[] { (byte)numberOfOctets }, domainBytes); } else if (atyp == AtypField.IPv4) { // the address is a version-4 IP address, with a length of 4 octets var parts = dstAddr.Split(".", StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 4 || parts.Any(string.IsNullOrWhiteSpace)) { throw new FormatException($"{nameof(dstAddr)} must be have 4 parts. Actual: {parts.Length} parts. Value: {dstAddr}."); } bytes = new byte[4]; for (int i = 0; i < 4; i++) { if (int.TryParse(parts[i], out int partInt)) { if (partInt < 0 || partInt > 255) { throw new FormatException($"Every part of {nameof(dstAddr)} must be between 0 and 255. The {i}. part is invalid: {partInt}. Value of {nameof(dstAddr)}: {dstAddr}"); } bytes[i] = (byte)partInt; } else { throw new FormatException($"Could not parse the {i}. part of {nameof(dstAddr)} to int. Invalid part: {partInt}. Value of {nameof(dstAddr)}: {dstAddr}."); } } } else { throw new NotSupportedException($"{nameof(atyp)} is not supported. Value: {atyp}."); } Bytes = bytes; }
public byte[] ToBytes(bool startsWithLength) { if (!startsWithLength) { return(Purpose); } return(ByteHelpers.Combine(new byte[] { BitConverter.GetBytes(Length)[0] }, Purpose)); }
private static byte[] CreateHmac(byte[] salt, AesCryptoServiceProvider aes, byte[] encryptedMessage) { using (var hmacsha256 = new HMACSHA256(aes.Key)) { var messagePlusSalt = ByteHelpers.Combine(encryptedMessage, salt); return(hmacsha256.ComputeHash(messagePlusSalt)); } }
public byte[] ToBytes(bool startsWithLength) { if (!startsWithLength) { return(Content); } return(ByteHelpers.Combine(BitConverter.GetBytes(Length), Content)); }
public Strobe128(string procotol) { Guard.NotNullOrEmpty(nameof(procotol), procotol); var initialState = ByteHelpers.Combine( new byte[] { 1, SpongeRate + 2, 1, 0, 1, 12 * 8 }, // F([[1, r/8, 1, 0, 1, 12·8]] Encoding.UTF8.GetBytes("STROBEv1.0.2")); Buffer.BlockCopy(initialState, 0, State, 0, initialState.Length); KeccakF1600(State); AddAssociatedMetaData(Encoding.UTF8.GetBytes(procotol), false); }
public void CombineMergesToArraysTogether() { byte[] test = { 1, 2 }; byte[] test2 = { 3, 4 }; byte[] combined = ByteHelpers.Combine(test, test2); Assert.AreEqual(4, combined.Length); Assert.AreEqual(1, combined[0]); Assert.AreEqual(2, combined[1]); Assert.AreEqual(3, combined[2]); Assert.AreEqual(4, combined[3]); }
public byte[] Encrypt(byte[] dataToEncrypt, string password, byte[] salt, int pbkdfRounds) { if (dataToEncrypt == null) { throw new ArgumentNullException("dataToEncrypt"); } if (dataToEncrypt.Length == 0) { throw new InvalidOperationException("dataToEncrypt"); } if (string.IsNullOrEmpty(password)) { throw new ArgumentNullException("password"); } try { using (var rfc2898 = new Rfc2898DeriveBytes(password, salt, pbkdfRounds)) { using (var aes = new AesCryptoServiceProvider()) { aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.Key = rfc2898.GetBytes(32); aes.IV = rfc2898.GetBytes(16); using (var memoryStream = new MemoryStream()) { var cryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write); cryptoStream.Write(dataToEncrypt, 0, dataToEncrypt.Length); cryptoStream.FlushFinalBlock(); var encryptedMessage = memoryStream.ToArray(); byte[] hmac = CreateHmac(salt, aes, encryptedMessage); byte[] messagePlusHmac = ByteHelpers.Combine(hmac, encryptedMessage); return(messagePlusHmac); } } } } catch { return(null); } }
/// <summary> /// Fiat Shamir heuristic. /// </summary> private static Scalar HashToScalar(IEnumerable <GroupElement> transcript) { var transcriptBytes = transcript.Select(x => x.ToBytes()); // Make sure the length of the data is also committed to: len(data) || data // https://github.com/zkSNACKs/WalletWasabi/pull/4151#discussion_r470334048 var concatenation = transcriptBytes .SelectMany(x => ByteHelpers.Combine(BitConverter.GetBytes(x.Length), x)) .ToArray(); using var sha256 = System.Security.Cryptography.SHA256.Create(); var hash = sha256.ComputeHash(concatenation); var challenge = new Scalar(hash); return(challenge); }
public override byte[] ToBytes() => ByteHelpers.Combine(new byte[] { Ver.ToByte(), Rep.ToByte(), Rsv.ToByte(), Atyp.ToByte() }, BndAddr.ToBytes(), BndPort.ToBytes());
public void CombineThrowsArgumentNullExceptionIfSecondParameterIsNull() { var test = new byte[5]; ByteHelpers.Combine(test, null); }
public void CombineThrowsArgumentNullExceptionIfFirstParameterIsNull() { ByteHelpers.Combine(null, null); }
public async Task <IActionResult> PostInputsAsync([FromBody, Required] InputsRequest request) { // Validate request. if (request.RoundId < 0 || !ModelState.IsValid) { return(BadRequest("Invalid request.")); } if (request.Inputs.Count() > 7) { return(BadRequest("Maximum 7 inputs can be registered.")); } using (await InputsLock.LockAsync()) { CoordinatorRound round = Coordinator.TryGetRound(request.RoundId); if (round is null || round.Phase != RoundPhase.InputRegistration) { return(NotFound("No such running round in InputRegistration. Try another round.")); } // Do more checks. try { uint256[] blindedOutputs = request.BlindedOutputScripts.ToArray(); int blindedOutputCount = blindedOutputs.Length; int maxBlindedOutputCount = round.MixingLevels.Count(); if (blindedOutputCount > maxBlindedOutputCount) { return(BadRequest($"Too many blinded output was provided: {blindedOutputCount}, maximum: {maxBlindedOutputCount}.")); } if (blindedOutputs.Distinct().Count() < blindedOutputs.Length) { return(BadRequest("Duplicate blinded output found.")); } if (round.ContainsAnyBlindedOutputScript(blindedOutputs)) { return(BadRequest("Blinded output has already been registered.")); } if (request.ChangeOutputAddress.Network != Network) { // RegTest and TestNet address formats are sometimes the same. if (Network == Network.Main) { return(BadRequest($"Invalid ChangeOutputAddress Network.")); } } var uniqueInputs = new HashSet <OutPoint>(); foreach (InputProofModel inputProof in request.Inputs) { var outpoint = inputProof.Input.ToOutPoint(); if (uniqueInputs.Contains(outpoint)) { return(BadRequest("Cannot register an input twice.")); } uniqueInputs.Add(outpoint); } var alicesToRemove = new HashSet <Guid>(); var getTxOutResponses = new List <(InputProofModel inputModel, Task <GetTxOutResponse> getTxOutTask)>(); var batch = RpcClient.PrepareBatch(); foreach (InputProofModel inputProof in request.Inputs) { if (round.ContainsInput(inputProof.Input.ToOutPoint(), out List <Alice> tr)) { alicesToRemove.UnionWith(tr.Select(x => x.UniqueId)); // Input is already registered by this alice, remove it later if all the checks are completed fine. } if (Coordinator.AnyRunningRoundContainsInput(inputProof.Input.ToOutPoint(), out List <Alice> tnr)) { if (tr.Union(tnr).Count() > tr.Count) { return(BadRequest("Input is already registered in another round.")); } } OutPoint outpoint = inputProof.Input.ToOutPoint(); var bannedElem = await Coordinator.UtxoReferee.TryGetBannedAsync(outpoint, notedToo : false); if (bannedElem != null) { return(BadRequest($"Input is banned from participation for {(int)bannedElem.BannedRemaining.TotalMinutes} minutes: {inputProof.Input.Index}:{inputProof.Input.TransactionId}.")); } var txOutResponseTask = batch.GetTxOutAsync(inputProof.Input.TransactionId, (int)inputProof.Input.Index, includeMempool: true); getTxOutResponses.Add((inputProof, txOutResponseTask)); } // Perform all RPC request at once var waiting = Task.WhenAll(getTxOutResponses.Select(x => x.getTxOutTask)); await batch.SendBatchAsync(); await waiting; byte[] blindedOutputScriptHashesByte = ByteHelpers.Combine(blindedOutputs.Select(x => x.ToBytes())); uint256 blindedOutputScriptsHash = new uint256(Hashes.SHA256(blindedOutputScriptHashesByte)); var inputs = new HashSet <Coin>(); foreach (var responses in getTxOutResponses) { var(inputProof, getTxOutResponseTask) = responses; var getTxOutResponse = await getTxOutResponseTask; // Check if inputs are unspent. if (getTxOutResponse is null) { return(BadRequest($"Provided input is not unspent: {inputProof.Input.Index}:{inputProof.Input.TransactionId}.")); } // Check if unconfirmed. if (getTxOutResponse.Confirmations <= 0) { // If it spends a CJ then it may be acceptable to register. if (!await Coordinator.ContainsUnconfirmedCoinJoinAsync(inputProof.Input.TransactionId)) { return(BadRequest("Provided input is neither confirmed, nor is from an unconfirmed coinjoin.")); } // Check if mempool would accept a fake transaction created with the registered inputs. // This will catch ascendant/descendant count and size limits for example. var result = await RpcClient.TestMempoolAcceptAsync(new[] { new Coin(inputProof.Input.ToOutPoint(), getTxOutResponse.TxOut) }); if (!result.accept) { return(BadRequest($"Provided input is from an unconfirmed coinjoin, but a limit is reached: {result.rejectReason}")); } } // Check if immature. if (getTxOutResponse.Confirmations <= 100) { if (getTxOutResponse.IsCoinBase) { return(BadRequest("Provided input is immature.")); } } // Check if inputs are native segwit. if (getTxOutResponse.ScriptPubKeyType != "witness_v0_keyhash") { return(BadRequest("Provided input must be witness_v0_keyhash.")); } TxOut txOut = getTxOutResponse.TxOut; var address = (BitcoinWitPubKeyAddress)txOut.ScriptPubKey.GetDestinationAddress(Network); // Check if proofs are valid. if (!address.VerifyMessage(blindedOutputScriptsHash, inputProof.Proof)) { return(BadRequest("Provided proof is invalid.")); } inputs.Add(new Coin(inputProof.Input.ToOutPoint(), txOut)); } var acceptedBlindedOutputScripts = new List <uint256>(); // Calculate expected networkfee to pay after base denomination. int inputCount = inputs.Count; Money networkFeeToPayAfterBaseDenomination = (inputCount * round.FeePerInputs) + (2 * round.FeePerOutputs); // Check if inputs have enough coins. Money inputSum = inputs.Sum(x => x.Amount); Money changeAmount = (inputSum - (round.MixingLevels.GetBaseDenomination() + networkFeeToPayAfterBaseDenomination)); if (changeAmount < Money.Zero) { return(BadRequest($"Not enough inputs are provided. Fee to pay: {networkFeeToPayAfterBaseDenomination.ToString(false, true)} BTC. Round denomination: {round.MixingLevels.GetBaseDenomination().ToString(false, true)} BTC. Only provided: {inputSum.ToString(false, true)} BTC.")); } acceptedBlindedOutputScripts.Add(blindedOutputs.First()); Money networkFeeToPay = networkFeeToPayAfterBaseDenomination; // Make sure we sign the proper number of additional blinded outputs. var moneySoFar = Money.Zero; for (int i = 1; i < blindedOutputCount; i++) { if (!round.MixingLevels.TryGetDenomination(i, out Money denomination)) { break; } Money coordinatorFee = denomination.Percentage(round.CoordinatorFeePercent * round.AnonymitySet); // It should be the number of bobs, but we must make sure they'd have money to pay all. changeAmount -= (denomination + round.FeePerOutputs + coordinatorFee); networkFeeToPay += round.FeePerOutputs; if (changeAmount < Money.Zero) { break; } acceptedBlindedOutputScripts.Add(blindedOutputs[i]); } // Make sure Alice checks work. var alice = new Alice(inputs, networkFeeToPayAfterBaseDenomination, request.ChangeOutputAddress, acceptedBlindedOutputScripts); foreach (Guid aliceToRemove in alicesToRemove) { round.RemoveAlicesBy(aliceToRemove); } round.AddAlice(alice); // All checks are good. Sign. var blindSignatures = new List <uint256>(); for (int i = 0; i < acceptedBlindedOutputScripts.Count; i++) { var blindedOutput = acceptedBlindedOutputScripts[i]; var signer = round.MixingLevels.GetLevel(i).Signer; uint256 blindSignature = signer.Sign(blindedOutput); blindSignatures.Add(blindSignature); } alice.BlindedOutputSignatures = blindSignatures.ToArray(); // Check if phase changed since. if (round.Status != CoordinatorRoundStatus.Running || round.Phase != RoundPhase.InputRegistration) { return(StatusCode(StatusCodes.Status503ServiceUnavailable, "The state of the round changed while handling the request. Try again.")); } // Progress round if needed. if (round.CountAlices() >= round.AnonymitySet) { await round.RemoveAlicesIfAnInputRefusedByMempoolAsync(); if (round.CountAlices() >= round.AnonymitySet) { await round.ExecuteNextPhaseAsync(RoundPhase.ConnectionConfirmation); } } var resp = new InputsResponse { UniqueId = alice.UniqueId, RoundId = round.RoundId }; return(Ok(resp)); } catch (Exception ex) { Logger.LogDebug(ex); return(BadRequest(ex.Message)); } } }
private async Task TryRegisterCoinsAsync(CcjClientRound inputRegistrableRound) { try { // Select the most suitable coins to regiter. List <TxoRef> registrableCoins = State.GetRegistrableCoins( inputRegistrableRound.State.MaximumInputCountPerPeer, inputRegistrableRound.State.Denomination, inputRegistrableRound.State.FeePerInputs, inputRegistrableRound.State.FeePerOutputs).ToList(); // If there are no suitable coins to register return. if (!registrableCoins.Any()) { return; } (HdPubKey change, IEnumerable <HdPubKey> actives)outputAddresses = GetOutputsToRegister(inputRegistrableRound.State.Denomination, inputRegistrableRound.State.SchnorrPubKeys.Count(), registrableCoins); SchnorrPubKey[] schnorrPubKeys = inputRegistrableRound.State.SchnorrPubKeys.ToArray(); List <Requester> requesters = new List <Requester>(); var blindedOutputScriptHashes = new List <uint256>(); var registeredAddresses = new List <BitcoinAddress>(); for (int i = 0; i < schnorrPubKeys.Length; i++) { if (outputAddresses.actives.Count() <= i) { break; } BitcoinAddress address = outputAddresses.actives.Select(x => x.GetP2wpkhAddress(Network)).ElementAt(i); SchnorrPubKey schnorrPubKey = schnorrPubKeys[i]; var outputScriptHash = new uint256(Hashes.SHA256(address.ScriptPubKey.ToBytes())); var requester = new Requester(); uint256 blindedOutputScriptHash = requester.BlindMessage(outputScriptHash, schnorrPubKey); requesters.Add(requester); blindedOutputScriptHashes.Add(blindedOutputScriptHash); registeredAddresses.Add(address); } byte[] blindedOutputScriptHashesByte = ByteHelpers.Combine(blindedOutputScriptHashes.Select(x => x.ToBytes())); uint256 blindedOutputScriptsHash = new uint256(Hashes.SHA256(blindedOutputScriptHashesByte)); var inputProofs = new List <InputProofModel>(); foreach (TxoRef coinReference in registrableCoins) { SmartCoin coin = State.GetSingleOrDefaultFromWaitingList(coinReference); if (coin is null) { throw new NotSupportedException("This is impossible."); } coin.Secret = coin.Secret ?? KeyManager.GetSecrets(SaltSoup(), coin.ScriptPubKey).Single(); var inputProof = new InputProofModel { Input = coin.GetTxoRef(), Proof = coin.Secret.PrivateKey.SignCompact(blindedOutputScriptsHash) }; inputProofs.Add(inputProof); } AliceClient aliceClient = null; try { aliceClient = await AliceClient.CreateNewAsync(inputRegistrableRound.RoundId, registeredAddresses, schnorrPubKeys, requesters, Network, outputAddresses.change.GetP2wpkhAddress(Network), blindedOutputScriptHashes, inputProofs, CcjHostUriAction, TorSocks5EndPoint); } catch (HttpRequestException ex) when(ex.Message.Contains("Input is banned", StringComparison.InvariantCultureIgnoreCase)) { string[] parts = ex.Message.Split(new[] { "Input is banned from participation for ", " minutes: " }, StringSplitOptions.RemoveEmptyEntries); string minutesString = parts[1]; int minuteInt = int.Parse(minutesString); string bannedInputString = parts[2].TrimEnd('.'); string[] bannedInputStringParts = bannedInputString.Split(':', StringSplitOptions.RemoveEmptyEntries); TxoRef coinReference = new TxoRef(new uint256(bannedInputStringParts[1]), uint.Parse(bannedInputStringParts[0])); SmartCoin coin = State.GetSingleOrDefaultFromWaitingList(coinReference); if (coin is null) { throw new NotSupportedException("This is impossible."); } coin.BannedUntilUtc = DateTimeOffset.UtcNow + TimeSpan.FromMinutes(minuteInt); Logger.LogWarning <CcjClient>(ex.Message.Split('\n')[1]); await DequeueCoinsFromMixNoLockAsync(coinReference, "Failed to register the coin with the coordinator."); aliceClient?.Dispose(); return; } catch (HttpRequestException ex) when(ex.Message.Contains("Provided input is not unspent", StringComparison.InvariantCultureIgnoreCase)) { string[] parts = ex.Message.Split(new[] { "Provided input is not unspent: " }, StringSplitOptions.RemoveEmptyEntries); string spentInputString = parts[1].TrimEnd('.'); string[] bannedInputStringParts = spentInputString.Split(':', StringSplitOptions.RemoveEmptyEntries); TxoRef coinReference = new TxoRef(new uint256(bannedInputStringParts[1]), uint.Parse(bannedInputStringParts[0])); SmartCoin coin = State.GetSingleOrDefaultFromWaitingList(coinReference); if (coin is null) { throw new NotSupportedException("This is impossible."); } coin.SpentAccordingToBackend = true; Logger.LogWarning <CcjClient>(ex.Message.Split('\n')[1]); await DequeueCoinsFromMixNoLockAsync(coinReference, "Failed to register the coin with the coordinator. The coin is already spent."); aliceClient?.Dispose(); return; } catch (HttpRequestException ex) when(ex.Message.Contains("No such running round in InputRegistration.", StringComparison.InvariantCultureIgnoreCase)) { Logger.LogInfo <CcjClient>("Client tried to register a round that is not in InputRegistration anymore. Trying again later."); aliceClient?.Dispose(); return; } catch (HttpRequestException ex) when(ex.Message.Contains("too-long-mempool-chain", StringComparison.InvariantCultureIgnoreCase)) { Logger.LogInfo <CcjClient>("Coordinator failed because too much unconfirmed parent transactions. Trying again later."); aliceClient?.Dispose(); return; } var coinsRegistered = new List <SmartCoin>(); foreach (TxoRef coinReference in registrableCoins) { var coin = State.GetSingleOrDefaultFromWaitingList(coinReference); if (coin is null) { throw new NotSupportedException("This is impossible."); } coinsRegistered.Add(coin); State.RemoveCoinFromWaitingList(coin); } var registration = new ClientRoundRegistration(aliceClient, coinsRegistered, outputAddresses.change.GetP2wpkhAddress(Network)); CcjClientRound roundRegistered = State.GetSingleOrDefaultRound(aliceClient.RoundId); if (roundRegistered is null) { // If our SatoshiClient does not yet know about the round, because of delay, then delay the round registration. DelayedRoundRegistration?.Dispose(); DelayedRoundRegistration = registration; } roundRegistered.Registration = registration; } catch (Exception ex) { Logger.LogError <CcjClient>(ex); } }
public override byte[] ToBytes() => ByteHelpers.Combine(new byte[] { Ver.ToByte(), ULen.ToByte() }, UName.ToBytes(), new byte[] { PLen.ToByte() }, Passwd.ToBytes());
public override byte[] ToBytes() => ByteHelpers.Combine(new byte[] { Version.ToByte(), MessageType.ToByte() }, MessageId.ToBytes(), Purpose.ToBytes(startsWithLength: true), Content.ToBytes(startsWithLength: true));