public void Process(MusigPubNonce aggregatedNonce)
        {
            if (processed_nonce)
            {
                throw new InvalidOperationException($"Nonce already processed");
            }
            var agg_pk = this.SigningPubKey;

            MusigSessionCache session_cache = new MusigSessionCache();
            Span <byte>       fin_nonce     = stackalloc byte[32];

            Span <byte> agg_pk32     = stackalloc byte[32];
            Span <GEJ>  aggnonce_ptj = stackalloc GEJ[2];

            aggnonce_ptj[0] = aggregatedNonce.K1.Q.ToGroupElementJacobian();
            aggnonce_ptj[1] = aggregatedNonce.K2.Q.ToGroupElementJacobian();

            agg_pk.WriteToSpan(agg_pk32);
            /* Add public adaptor to nonce */
            if (adaptor != null)
            {
                aggnonce_ptj[0] = aggnonce_ptj[0].AddVariable(adaptor.Q);
            }

            secp256k1_musig_nonce_process_internal(this.ctx.EcMultContext, out session_cache.FinalNonceParity, fin_nonce, out session_cache.NonceCoeff, aggnonce_ptj, agg_pk32, msg32);

            /* Compute messagehash and store in session cache */
            ECXOnlyPubKey.secp256k1_schnorrsig_challenge(out session_cache.Challenge, fin_nonce, msg32, agg_pk32);

            /* If there is a tweak then set `msghash` times `tweak` to the `s`-part of the sig template.*/
            session_cache.SPart = Scalar.Zero;
            if (is_tweaked)
            {
                Scalar e_tmp = session_cache.Challenge;
                if (!ECPrivKey.secp256k1_eckey_privkey_tweak_mul(ref e_tmp, scalar_tweak))
                {
                    throw new InvalidOperationException("Impossible to sign (secp256k1_eckey_privkey_tweak_mul is false)");
                }
                if (pk_parity)
                {
                    e_tmp = e_tmp.Negate();
                }
                session_cache.SPart = session_cache.SPart.Add(e_tmp);
            }
            fin_nonce.CopyTo(session_cache.FinalNonce);
            SessionCache        = session_cache;
            processed_nonce     = true;
            this.aggregateNonce = aggregatedNonce;
        }
        public bool Verify(ECXOnlyPubKey pubKey, MusigPubNonce pubNonce, MusigPartialSignature partialSignature)
        {
            if (partialSignature == null)
            {
                throw new ArgumentNullException(nameof(partialSignature));
            }
            if (pubNonce == null)
            {
                throw new ArgumentNullException(nameof(pubNonce));
            }
            if (pubKey == null)
            {
                throw new ArgumentNullException(nameof(pubKey));
            }
            if (SessionCache is null)
            {
                throw new InvalidOperationException("You need to run MusigContext.Process first");
            }
            GEJ       pkj;
            Span <GE> nonces = stackalloc GE[2];
            GEJ       rj;
            GEJ       tmp;
            GE        pkp;
            var       b           = SessionCache.NonceCoeff;
            var       pre_session = this;

            /* Compute "effective" nonce rj = nonces[0] + b*nonces[1] */
            /* TODO: use multiexp */

            nonces[0] = pubNonce.K1.Q;
            nonces[1] = pubNonce.K2.Q;

            rj = nonces[1].ToGroupElementJacobian();
            rj = this.ctx.EcMultContext.Mult(rj, b, null);
            rj = rj.AddVariable(nonces[0]);

            pkp = pubKey.Q;

            /* Multiplying the messagehash by the musig coefficient is equivalent
             * to multiplying the signer's public key by the coefficient, except
             * much easier to do. */
            var mu = ECXOnlyPubKey.secp256k1_musig_keyaggcoef(pre_session, pkp.x);
            var e  = SessionCache.Challenge * mu;

            /* If the MuSig-combined point has an odd Y coordinate, the signers will
             * sign for the negation of their individual xonly public key such that the
             * combined signature is valid for the MuSig aggregated xonly key. If the
             * MuSig-combined point was tweaked then `e` is negated if the combined key
             * has an odd Y coordinate XOR the internal key has an odd Y coordinate.*/
            if (pre_session.pk_parity
                != (pre_session.is_tweaked &&
                    pre_session.internal_key_parity))
            {
                e = e.Negate();
            }

            var s = partialSignature.E;

            /* Compute -s*G + e*pkj + rj */
            s   = s.Negate();
            pkj = pkp.ToGroupElementJacobian();
            tmp = ctx.EcMultContext.Mult(pkj, e, s);
            var fin_nonce_parity = SessionCache.FinalNonceParity;

            if (fin_nonce_parity)
            {
                rj = rj.Negate();
            }
            tmp = tmp.AddVariable(rj);
            return(tmp.IsInfinity);
        }
Example #3
0
        public void Process(MusigPubNonce combinedNonce)
        {
            if (processed_nonce)
            {
                throw new InvalidOperationException($"Nonce already processed");
            }
            var combined_pk = this.SigningPubKey;

            MusigSessionCache session_cache     = new MusigSessionCache();
            Span <byte>       noncehash         = stackalloc byte[32];
            Span <byte>       sig_template_data = stackalloc byte[32];

            Span <byte> combined_pk32 = stackalloc byte[32];
            Span <GEJ>  summed_nonces = stackalloc GEJ[2];

            summed_nonces[0] = combinedNonce.K1.Q.ToGroupElementJacobian();
            summed_nonces[1] = combinedNonce.K2.Q.ToGroupElementJacobian();

            combined_pk.WriteToSpan(combined_pk32);
            /* Add public adaptor to nonce */
            if (adaptor != null)
            {
                summed_nonces[0] = summed_nonces[0].AddVariable(adaptor.Q);
            }
            var combined_nonce = secp256k1_musig_process_nonces_internal(
                this.ctx.EcMultContext,
                noncehash,
                summed_nonces,
                combined_pk32,
                msg32);

            session_cache.B = new Scalar(noncehash);

            ECXOnlyPubKey.secp256k1_xonly_ge_serialize(sig_template_data, ref combined_nonce);
            var rx = combined_nonce.x;

            /* Negate nonce if Y coordinate is not square */
            combined_nonce = combined_nonce.NormalizeYVariable();
            /* Store nonce parity in session cache */
            session_cache.CombinedNonceParity = combined_nonce.y.IsOdd;

            /* Compute messagehash and store in session cache */
            ECXOnlyPubKey.secp256k1_musig_compute_messagehash(noncehash, sig_template_data, combined_pk32, msg32);
            session_cache.E = new Scalar(noncehash);

            /* If there is a tweak then set `msghash` times `tweak` to the `s`-part of the sig template.*/
            Scalar s = Scalar.Zero;

            if (is_tweaked)
            {
                Scalar e = session_cache.E;
                if (!ECPrivKey.secp256k1_eckey_privkey_tweak_mul(ref e, scalar_tweak))
                {
                    throw new InvalidOperationException("Impossible to sign (secp256k1_eckey_privkey_tweak_mul is false)");
                }
                if (pk_parity)
                {
                    e = e.Negate();
                }
                s = s.Add(e);
            }
            SessionCache       = session_cache;
            Template           = new SecpSchnorrSignature(rx, s);
            processed_nonce    = true;
            this.combinedNonce = combinedNonce;
        }
 public void ProcessNonces(MusigPubNonce[] nonces)
 {
     Process(MusigPubNonce.Aggregate(nonces));
 }
Example #5
0
 public void ProcessNonces(MusigPubNonce[] nonces)
 {
     Process(MusigPubNonce.Combine(nonces));
 }