/// <summary> /// Builds the receiver. /// </summary> /// <returns>The receiver.</returns> public TaskResult <bool> Receiver(SecureString secret, ulong input, out CoinDto coin, out byte[] blind, out byte[] salt) { using (var pedersen = new Pedersen()) { salt = Cryptography.RandomBytes(16); coin = MakeSingleCoin(secret, salt.ToHex().ToSecureString(), NewStamp(), -1); blind = DeriveKey(input, coin.Stamp, coin.Version, secret, salt.ToHex().ToSecureString()); try { var blindSum = pedersen.BlindSum(new List <byte[]> { blind }, new List <byte[]> { }); var commitPos = pedersen.Commit(input, blind); var commitSum = pedersen.CommitSum(new List <byte[]> { commitPos }, new List <byte[]> { }); AttachEnvelope(blindSum, commitSum, input, secret, salt.ToHex().ToSecureString(), ref coin); } catch (Exception ex) { logger.LogError($"Message: {ex.Message}\n Stack: {ex.StackTrace}"); return(TaskResult <bool> .CreateFailure(ex)); } } return(TaskResult <bool> .CreateSuccess(true)); }
/// <summary> /// Clears the change, imputs, minted coin, outputs, secret, receiver output, stamp and version cache. /// </summary> public void ClearCache() { mintedCoin = null; stamp = null; version = 0; transactionCoin = null; }
/// <summary> /// Change partial ownership. /// </summary> /// <returns>The partial one.</returns> /// <param name="password">Password.</param> /// <param name="coin">Coin.</param> /// <param name="redemptionKey">Redemption key.</param> public CoinDto SwapPartialOne(SecureString password, CoinDto coin, RedemptionKeyDto redemptionKey) { if (password == null) { throw new ArgumentNullException(nameof(password)); } if (coin == null) { throw new ArgumentNullException(nameof(coin)); } if (redemptionKey == null) { throw new ArgumentNullException(nameof(redemptionKey)); } var v1 = coin.Version + 1; var v2 = coin.Version + 2; var v3 = coin.Version + 3; coin.Keeper = DeriveKey(v2, coin.Stamp, DeriveKey(v3, coin.Stamp, DeriveKey(v3, coin.Stamp, password).ToSecureString()).ToSecureString()); coin.Version = v1; coin.Principle = redemptionKey.Key1; coin.Stamp = coin.Stamp; coin.Envelope = coin.Envelope; coin.Hint = redemptionKey.Key2; return(coin); }
/// <summary> /// Builds the receiver. /// </summary> /// <returns>The receiver.</returns> public (ReceiverOutput, CoinDto) BuildReceiver() { ReceiverOutput receiver = null; CoinDto coin = null; using (var secp256k1 = new Secp256k1()) using (var pedersen = new Pedersen()) { var blind = DeriveKey(Output()); var blindSum = pedersen.BlindSum(new List <byte[]> { blind, blind }, new List <byte[]> { }); var commitPos = Commit((ulong)Output(), blind); var commitNeg = Commit(0, blind); Stamp(GetNewStamp()); Version(-1); coin = BuildCoin(blindSum, commitPos, commitNeg, true); receiver = new ReceiverOutput(Output(), commitPos, blindSum); } return(receiver, coin); }
/// <summary> /// Builds the receiver. /// </summary> /// <returns>The receiver.</returns> public CoinService BuildReceiver() { using (var secp256k1 = new Secp256k1()) using (var pedersen = new Pedersen()) { var naTOutput = NaT(Output()); var blind = DeriveKey(naTOutput); byte[] blindSum = new byte[32]; try { blindSum = pedersen.BlindSum(new List <byte[]> { blind, blind }, new List <byte[]> { }); } catch (Exception ex) { logger.LogError($"Message: {ex.Message}\n Stack: {ex.StackTrace}"); throw ex; } var commitPos = Commit(naTOutput, blind); var commitNeg = Commit(0, blind); Stamp(GetNewStamp()); Version(-1); mintedCoin = BuildCoin(blindSum, commitPos, commitNeg, true); receiverOutput = new ReceiverOutput(Output(), commitPos, blindSum); } return(this); }
/// <summary> /// Derives the coin. /// </summary> /// <returns>The coin.</returns> /// <param name="coin">Coin.</param> public CoinDto DeriveCoin(CoinDto coin) { if (password == null) { throw new ArgumentNullException(nameof(password)); } if (coin == null) { throw new ArgumentNullException(nameof(coin)); } var v0 = +coin.Version; var v1 = +coin.Version + 1; var v2 = +coin.Version + 2; var c = new CoinDto() { Keeper = DeriveKey(v1, coin.Stamp, DeriveKey(v2, coin.Stamp, DeriveKey(v2, coin.Stamp, Password()).ToSecureString()).ToSecureString()), Version = v0, Principle = DeriveKey(v0, coin.Stamp, Password()), Stamp = coin.Stamp, Envelope = coin.Envelope, Hint = DeriveKey(v1, coin.Stamp, DeriveKey(v1, coin.Stamp, Password()).ToSecureString()) }; return(c); }
/// <summary> /// Clears the change, imputs, minted coin, outputs, password, receiver output, stamp and version cache. /// </summary> public void ClearCache() { change = 0; Input(0); mintedCoin = null; Output(0); Password(null); receiverOutput = null; Stamp(string.Empty); Version(0); }
/// <summary> /// Makes the single coin. /// </summary> /// <returns>The single coin.</returns> public void MakeSingleCoin(SecureString secret) { Guard.Argument(secret, nameof(secret)).NotNull(); mintedCoin = DeriveCoin(new CoinDto { Version = version + 1, Stamp = stamp, Envelope = new EnvelopeDto() }, secret); }
/// <summary> /// Change ownership. /// </summary> /// <returns>The swap.</returns> /// <param name="password">Password.</param> /// <param name="coin">Coin.</param> /// <param name="redemptionKey">Redemption key.</param> public (CoinDto, CoinDto) CoinSwap(SecureString password, CoinDto coin, RedemptionKeyDto redemptionKey) { if (password == null) { throw new ArgumentNullException(nameof(password)); } if (coin == null) { throw new ArgumentNullException(nameof(coin)); } if (redemptionKey == null) { throw new ArgumentNullException(nameof(redemptionKey)); } if (!redemptionKey.Stamp.Equals(coin.Stamp)) { throw new Exception("Redemption stamp is not equal to the coins stamp!"); } var v1 = coin.Version + 1; var v2 = coin.Version + 2; var v3 = coin.Version + 3; var v4 = coin.Version + 4; var c1 = new CoinDto() { Keeper = DeriveKey(v2, redemptionKey.Stamp, DeriveKey(v3, redemptionKey.Stamp, DeriveKey(v3, redemptionKey.Stamp, password).ToSecureString()).ToSecureString()), Version = v1, Principle = redemptionKey.Key1, Stamp = redemptionKey.Stamp, Envelope = coin.Envelope, Hint = DeriveKey(v2, redemptionKey.Stamp, redemptionKey.Key2.ToSecureString()) }; c1.Hash = Hash(c1).ToHex(); var c2 = new CoinDto() { Keeper = DeriveKey(v3, redemptionKey.Stamp, DeriveKey(v4, redemptionKey.Stamp, DeriveKey(v4, redemptionKey.Stamp, password).ToSecureString()).ToSecureString()), Version = v2, Principle = redemptionKey.Key2, Stamp = redemptionKey.Stamp, Envelope = coin.Envelope, Hint = DeriveKey(v3, redemptionKey.Stamp, DeriveKey(v3, redemptionKey.Stamp, password).ToSecureString()) }; c2.Hash = Hash(c2).ToHex(); return(c1, c2); }
/// <summary> /// Builds the sender. /// </summary> /// <returns>The sender.</returns> public async Task <TaskResult <CoinDto> > Sender(Session session, PurchaseDto purchase) { CoinDto coin = null; using (var pedersen = new Pedersen()) { try { //TODO: Refactor signature to handle lambda expressions.. var txnsAll = await unitOfWork.GetTransactionRepository().All(session); if (txnsAll.Result?.Any() != true) { throw new Exception("No transactions found!"); } var txns = txnsAll.Result.Where(tx => purchase.Chain.Any(id => id == Guid.Parse(tx.TransactionId))); var received = txns.FirstOrDefault(tx => tx.TransactionType == TransactionType.Receive); coin = MakeSingleCoin(session.MasterKey, received.Salt.ToSecureString(), purchase.Stamp, purchase.Version); var blindNeg = DeriveKey(purchase.Input, received.Stamp, coin.Version, session.MasterKey, received.Salt.ToSecureString()); var commitNeg = pedersen.Commit(purchase.Input, blindNeg); var commitNegs = txns.Where(tx => tx.TransactionType == TransactionType.Send) .Select(c => pedersen.Commit(c.Amount, DeriveKey(c.Amount, c.Stamp, c.Version, session.MasterKey, received.Salt.ToSecureString()))).ToList(); commitNegs.Add(commitNeg); var blindNegSums = txns.Where(tx => tx.TransactionType == TransactionType.Send) .Select(c => DeriveKey(c.Amount, c.Stamp, c.Version, session.MasterKey, received.Salt.ToSecureString())).ToList(); blindNegSums.Add(blindNeg); var blindSum = pedersen.BlindSum(new List <byte[]> { received.Blind.FromHex() }, blindNegSums); var commitSum = pedersen.CommitSum(new List <byte[]> { received.Commitment.FromHex() }, commitNegs); AttachEnvelope(blindSum, commitSum, purchase.Output, session.MasterKey, received.Salt.ToSecureString(), ref coin); } catch (Exception ex) { logger.LogError($"Message: {ex.Message}\n Stack: {ex.StackTrace}"); return(TaskResult <CoinDto> .CreateFailure(ex)); } } return(TaskResult <CoinDto> .CreateSuccess(coin)); }
/// <summary> /// Verifies the coin on ownership. /// </summary> /// <returns>The coin.</returns> /// <param name="terminal">Terminal.</param> /// <param name="current">Current.</param> public int VerifyCoin(CoinDto terminal, CoinDto current) { Guard.Argument(terminal, nameof(terminal)).NotNull(); Guard.Argument(current, nameof(current)).NotNull(); return(terminal.Keeper.Equals(current.Keeper) && terminal.Hint.Equals(current.Hint) ? 1 : terminal.Hint.Equals(current.Hint) ? 2 : terminal.Keeper.Equals(current.Keeper) ? 3 : 4); }
/// <summary> /// Hash the specified coin. /// </summary> /// <returns>The hash.</returns> /// <param name="coin">Coin.</param> public byte[] Hash(CoinDto coin) { Guard.Argument(coin, nameof(coin)).NotNull(); return(Cryptography.GenericHashNoKey( string.Format("{0} {1} {2} {3} {4} {5} {6}", coin.Envelope.Commitment, coin.Envelope.Proof, coin.Envelope.PublicKey, coin.Hint, coin.Keeper, coin.Principle, coin.Stamp))); }
/// <summary> /// Change ownership. /// </summary> /// <returns>The swap.</returns> /// <param name="secret">secret.</param> /// <param name="coin">Coin.</param> /// <param name="redemptionKey">Redemption key.</param> public (CoinDto, CoinDto) CoinSwap(SecureString secret, SecureString salt, CoinDto coin, RedemptionKeyDto redemptionKey) { Guard.Argument(secret, nameof(secret)).NotNull(); Guard.Argument(coin, nameof(coin)).NotNull(); Guard.Argument(redemptionKey, nameof(redemptionKey)).NotNull(); try { coin = coin.FormatCoinFromBase64(); } catch (FormatException) { } if (!redemptionKey.Stamp.Equals(coin.Stamp)) { throw new Exception("Redemption stamp is not equal to the coins stamp!"); } var v1 = coin.Version + 1; var v2 = coin.Version + 2; var v3 = coin.Version + 3; var v4 = coin.Version + 4; var c1 = new CoinDto { Keeper = DeriveKey(v2, redemptionKey.Stamp, DeriveKey(v3, redemptionKey.Stamp, DeriveKey(v3, redemptionKey.Stamp, secret, salt).ToSecureString(), salt).ToSecureString(), salt), Version = v1, Principle = redemptionKey.Key1, Stamp = redemptionKey.Stamp, Envelope = coin.Envelope, Hint = DeriveKey(v2, redemptionKey.Stamp, redemptionKey.Key2.ToSecureString(), salt) }; c1.Hash = Hash(c1).ToHex(); var c2 = new CoinDto { Keeper = DeriveKey(v3, redemptionKey.Stamp, DeriveKey(v4, redemptionKey.Stamp, DeriveKey(v4, redemptionKey.Stamp, secret, salt).ToSecureString(), salt).ToSecureString(), salt), Version = v2, Principle = redemptionKey.Key2, Stamp = redemptionKey.Stamp, Envelope = coin.Envelope, Hint = DeriveKey(v3, redemptionKey.Stamp, DeriveKey(v3, redemptionKey.Stamp, secret, salt).ToSecureString(), salt) }; c2.Hash = Hash(c2).ToHex(); return(c1, c2); }
/// <summary> /// Hash the specified coin. /// </summary> /// <returns>The hash.</returns> /// <param name="coin">Coin.</param> public byte[] Hash(CoinDto coin) { if (coin == null) { throw new ArgumentNullException(nameof(coin)); } return(Cryptography.GenericHashNoKey( string.Format("{0} {1} {2} {3} {4} {5} {6}", coin.Envelope.Commitment, coin.Envelope.Proof, coin.Envelope.PublicKey, coin.Hint, coin.Keeper, coin.Principle, coin.Stamp))); }
/// <summary> /// Change partial ownership. /// </summary> /// <returns>The partial one.</returns> /// <param name="secret">secret.</param> /// <param name="coin">Coin.</param> /// <param name="redemptionKey">Redemption key.</param> public CoinDto SwapPartialOne(SecureString secret, SecureString salt, CoinDto coin, RedemptionKeyDto redemptionKey) { Guard.Argument(secret, nameof(secret)).NotNull(); Guard.Argument(coin, nameof(coin)).NotNull(); Guard.Argument(redemptionKey, nameof(redemptionKey)).NotNull(); var v1 = coin.Version + 1; var v2 = coin.Version + 2; var v3 = coin.Version + 3; coin.Keeper = DeriveKey(v2, coin.Stamp, DeriveKey(v3, coin.Stamp, DeriveKey(v3, coin.Stamp, secret, salt).ToSecureString(), salt).ToSecureString(), salt); coin.Version = v1; coin.Principle = redemptionKey.Key1; coin.Stamp = coin.Stamp; coin.Envelope = coin.Envelope; coin.Hint = redemptionKey.Key2; return(coin); }
/// <summary> /// Verifies the coin on ownership. /// </summary> /// <returns>The coin.</returns> /// <param name="terminal">Terminal.</param> /// <param name="current">Current.</param> public int VerifyCoin(CoinDto terminal, CoinDto current) { if (terminal == null) { throw new ArgumentNullException(nameof(terminal)); } if (current == null) { throw new ArgumentNullException(nameof(current)); } return(terminal.Keeper.Equals(current.Keeper) && terminal.Hint.Equals(current.Hint) ? 1 : terminal.Hint.Equals(current.Hint) ? 2 : terminal.Keeper.Equals(current.Keeper) ? 3 : 4); }
/// <summary> /// Derives the coin. /// </summary> /// <returns>The coin.</returns> /// <param name="coin">Coin.</param> public CoinDto DeriveCoin(CoinDto coin, SecureString secret, SecureString salt) { Guard.Argument(secret, nameof(secret)).NotNull(); Guard.Argument(coin, nameof(coin)).NotNull(); var v0 = +coin.Version; var v1 = +coin.Version + 1; var v2 = +coin.Version + 2; var c = new CoinDto { Keeper = DeriveKey(v1, coin.Stamp, DeriveKey(v2, coin.Stamp, DeriveKey(v2, coin.Stamp, secret, salt).ToSecureString(), salt).ToSecureString(), salt), Version = v0, Principle = DeriveKey(v0, coin.Stamp, secret, salt), Stamp = coin.Stamp, Envelope = coin.Envelope, Hint = DeriveKey(v1, coin.Stamp, DeriveKey(v1, coin.Stamp, secret, salt).ToSecureString(), salt) }; return(c); }
/// <summary> /// Derives the coin. /// </summary> /// <returns>The coin.</returns> /// <param name="coin">Coin.</param> public CoinDto DeriveCoin(CoinDto coin) { Guard.Argument(password, nameof(password)).NotNull(); Guard.Argument(coin, nameof(coin)).NotNull(); var v0 = +coin.Version; var v1 = +coin.Version + 1; var v2 = +coin.Version + 2; var c = new CoinDto() { Keeper = DeriveKey(v1, coin.Stamp, DeriveKey(v2, coin.Stamp, DeriveKey(v2, coin.Stamp, Password()).ToSecureString()).ToSecureString()), Version = v0, Principle = DeriveKey(v0, coin.Stamp, Password()), Stamp = coin.Stamp, Envelope = coin.Envelope, Hint = DeriveKey(v1, coin.Stamp, DeriveKey(v1, coin.Stamp, Password()).ToSecureString()) }; return(c); }
/// <summary> /// Builds the sender. /// </summary> /// <returns>The sender.</returns> public CoinDto BuildSender() { CoinDto coin = null; using (var secp256k1 = new Secp256k1()) using (var pedersen = new Pedersen()) { var blindPos = pedersen.BlindSwitch((ulong)Input(), DeriveKey(Input())); var blindNeg = pedersen.BlindSwitch((ulong)Output(), DeriveKey(Output())); var blindSum = pedersen.BlindSum(new List <byte[]> { blindPos }, new List <byte[]> { blindNeg }); var commitPos = Commit((ulong)Input(), blindPos); var commitNeg = Commit((ulong)Output(), blindNeg); coin = BuildCoin(blindSum, commitPos, commitNeg); } return(coin); }
/// <summary> /// Builds the sender. /// </summary> /// <returns>The sender.</returns> public CoinService BuildSender() { using (var secp256k1 = new Secp256k1()) using (var pedersen = new Pedersen()) { var naTInput = NaT(Input()); var naTOutput = NaT(Output()); byte[] blindPos = new byte[32]; byte[] blindNeg = new byte[32]; byte[] blindSum = new byte[32]; try { blindPos = pedersen.BlindSwitch(naTInput, DeriveKey(naTInput)); blindNeg = pedersen.BlindSwitch(naTOutput, DeriveKey(naTOutput)); blindSum = pedersen.BlindSum(new List <byte[]> { blindPos }, new List <byte[]> { blindNeg }); } catch (Exception ex) { logger.LogError($"Message: {ex.Message}\n Stack: {ex.StackTrace}"); throw ex; } var commitPos = Commit(naTInput, blindPos); var commitNeg = Commit(naTOutput, blindNeg); mintedCoin = BuildCoin(blindSum, commitPos, commitNeg); } return(this); }
/// <summary> /// Attachs the envelope. /// </summary> /// <param name="blindSum">Blind sum.</param> /// <param name="commitSum">Commit sum.</param> /// <param name="balance">Balance.</param> /// <param name="secret">Secret.</param> /// <param name="coin">Coin.</param> private void AttachEnvelope(byte[] blindSum, byte[] commitSum, ulong balance, SecureString secret, SecureString salt, ref CoinDto coin) { var(k1, k2) = Split(blindSum, secret, salt, coin.Stamp, coin.Version); using (var secp256k1 = new Secp256k1()) using (var pedersen = new Pedersen()) using (var bulletProof = new BulletProof()) { coin.Envelope.Commitment = commitSum.ToHex(); coin.Envelope.Proof = k2.ToHex(); coin.Envelope.PublicKey = pedersen.ToPublicKey(pedersen.Commit(0, k1)).ToHex(); coin.Hash = Hash(coin).ToHex(); coin.Envelope.Signature = secp256k1.Sign(coin.Hash.FromHex(), k1).ToHex(); var @struct = bulletProof.ProofSingle(balance, blindSum, Cryptography.RandomBytes(), null, null, null); var success = bulletProof.Verify(commitSum, @struct.proof, null); if (!success) { throw new ArgumentOutOfRangeException(nameof(success), "Bullet proof failed."); } coin.Envelope.RangeProof = @struct.proof.ToHex(); } }
/// <summary> /// Builds the coin. /// </summary> /// <returns>The coin.</returns> /// <param name="blindSum">Blind sum.</param> /// <param name="commitPos">Commit position.</param> /// <param name="commitNeg">Commit neg.</param> private CoinDto BuildCoin(byte[] blindSum, byte[] commitPos, byte[] commitNeg, bool isReceiver = false) { Guard.Argument(blindSum, nameof(blindSum)).NotNull().MaxCount(32); Guard.Argument(commitPos, nameof(commitPos)).NotNull().MaxCount(33); Guard.Argument(commitNeg, nameof(commitNeg)).NotNull().MaxCount(33); CoinDto coin = null; bool isVerified; using (var secp256k1 = new Secp256k1()) using (var pedersen = new Pedersen()) using (var rangeProof = new RangeProof()) { try { var commitSum = pedersen.CommitSum(new List <byte[]> { commitPos }, new List <byte[]> { commitNeg }); var naTInput = NaT(Input()); var naTOutput = NaT(Output()); var naTChange = naTInput - naTOutput; isVerified = isReceiver ? pedersen.VerifyCommitSum(new List <byte[]> { commitPos, commitNeg }, new List <byte[]> { Commit(naTOutput, blindSum) }) : pedersen.VerifyCommitSum(new List <byte[]> { commitPos }, new List <byte[]> { commitNeg, commitSum }); if (!isVerified) { throw new ArgumentOutOfRangeException(nameof(isVerified), "Verify commit sum failed."); } var(k1, k2) = Split(blindSum, isReceiver); coin = MakeSingleCoin(); coin.Envelope.Commitment = isReceiver ? Commit(naTOutput, blindSum).ToHex() : commitSum.ToHex(); coin.Envelope.Proof = k2.ToHex(); coin.Envelope.PublicKey = pedersen.ToPublicKey(Commit(0, k1)).ToHex(); coin.Envelope.Signature = secp256k1.Sign(Hash(coin), k1).ToHex(); coin.Hash = Hash(coin).ToHex(); proofStruct = isReceiver ? rangeProof.Proof(0, naTOutput, blindSum, coin.Envelope.Commitment.FromHex(), coin.Hash.FromHex()) : rangeProof.Proof(0, naTChange, blindSum, coin.Envelope.Commitment.FromHex(), coin.Hash.FromHex()); isVerified = rangeProof.Verify(coin.Envelope.Commitment.FromHex(), proofStruct); if (!isVerified) { throw new ArgumentOutOfRangeException(nameof(isVerified), "Range proof failed."); } } catch (Exception ex) { logger.LogError($"Message: {ex.Message}\n Stack: {ex.StackTrace}"); } } return(coin); }