public MusigPartialSignature Sign(ECPrivKey privKey, MusigPrivNonce privNonce) { if (privKey == null) { throw new ArgumentNullException(nameof(privKey)); } if (privNonce == null) { throw new ArgumentNullException(nameof(privNonce)); } if (privNonce.IsUsed) { throw new ArgumentNullException(nameof(privNonce), "Nonce already used, a nonce should never be used to sign twice"); } if (SessionCache is null) { throw new InvalidOperationException("You need to run MusigContext.Process first"); } //{ // /* Check in constant time if secnonce has been zeroed. */ // size_t i; // unsigned char secnonce_acc = 0; // for (i = 0; i < sizeof(*secnonce) ; i++) { // secnonce_acc |= secnonce->data[i]; // } // secp256k1_declassify(ctx, &secnonce_acc, sizeof(secnonce_acc)); // ARG_CHECK(secnonce_acc != 0); //} var pre_session = this; var session_cache = SessionCache; Span <Scalar> k = stackalloc Scalar[2]; k[0] = privNonce.K1.sec; k[1] = privNonce.K2.sec; /* Obtain the signer's public key point and determine if the sk is * negated before signing. That happens if if the signer's pubkey has an odd * Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR * (if tweaked) the internal key has an odd Y coordinate. * * This can be seen by looking at the sk key belonging to `combined_pk`. * Let's define * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key * point x_i*G, mu_i is the i-th musig coefficient and |.| is a function * that normalizes a point to an even Y by negating if necessary similar to * secp256k1_extrakeys_ge_even_y. Then we have * P := |P'| + t*G where t is the tweak. * And the combined xonly public key is * |P| = x*G * where x = sum_i(b_i*mu_i*x_i) + b'*t * b' = -1 if P != |P|, 1 otherwise * b_i = -1 if (P_i != |P_i| XOR P' != |P'| XOR P != |P|) and 1 * otherwise. */ var sk = privKey.sec; var pk = privKey.CreatePubKey().Q; pk = pk.NormalizeYVariable(); int l = 0; if (pk.y.IsOdd) { l++; } if (pre_session.pk_parity) { l++; } if (pre_session.internal_key_parity) { l++; } if (l % 2 == 1) { sk = sk.Negate(); } /* Multiply MuSig coefficient */ pk = pk.NormalizeXVariable(); var mu = ECXOnlyPubKey.secp256k1_musig_keyaggcoef(pre_session, pk.x); sk = sk * mu; if (session_cache.FinalNonceParity) { k[0] = k[0].Negate(); k[1] = k[1].Negate(); } var e = session_cache.Challenge * sk; k[1] = session_cache.NonceCoeff * k[1]; k[0] = k[0] + k[1]; e = e + k[0]; Scalar.Clear(ref k[0]); Scalar.Clear(ref k[1]); privNonce.IsUsed = true; return(new MusigPartialSignature(e)); }
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); }