Bitcoin Forum
January 10, 2026, 06:12:35 PM *
News: Due to a wallet-migration bug, you should not upgrade Bitcoin Core. But if you already did, there's no need to downgrade.
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Is It Possible to Verify Participation In Aggregated Signatures  (Read 198 times)
johndebord (OP)
Newbie
*
Offline Offline

Activity: 8
Merit: 3


View Profile
November 30, 2024, 07:26:19 AM
Merited by vjudeu (1)
 #1

Suppose you have a message, and this message is signed by 100 private/public key pairs. The signatures are then aggregated into a single aggregated signature. Now, assume the only public information available is the aggregated signature, the original message, and one private/public key pair from the 100 key pairs used in creating the aggregated signature.

Is it possible to verify that this specific private/public key pair contributed to the aggregated signature, or is such verification impossible?
vjudeu
Copper Member
Legendary
*
Offline Offline

Activity: 909
Merit: 2342


View Profile
November 30, 2024, 12:43:31 PM
Merited by hugeblack (4), Pmalek (2), nc50lc (1), garlonicon (1)
 #2

Quote
Is it possible to verify that this specific private/public key pair contributed to the aggregated signature, or is such verification impossible?
Yes, it is possible. If you can join signatures, then you can split them as well.

https://en.bitcoin.it/wiki/Schnorr
https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki

For example, let's assume, that you have some message:
Code:
SHA-256("Hello World!")=7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
SHA-256(7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069)=61f417374f4400b47dcae1a8f402d4f4dacf455a0442a06aa455a447b0d4e170
e=61f417374f4400b47dcae1a8f402d4f4dacf455a0442a06aa455a447b0d4e170
And then, you have this signature:
Code:
P=0256B76A89AF551C6C58B1972EE5BE3DAE2A2A39202AD7439962F647780F025E6A
R=025DA08419189F4C48912F64130DC262C522EBE81E10663927FB1341EC41E208E8
s=9308a87057ca70d6b6c597f5043686f2e2fa8487e05e6e46e510528b404da729
R=(s*G)-(e*P)
e*P=03B8A8E160E82E98A0B8C5EE01FF29C3A2E77D46025F650C83A66D88BA403B1553
-e*P=02B8A8E160E82E98A0B8C5EE01FF29C3A2E77D46025F650C83A66D88BA403B1553
s*G=02ED17F278B2F475F11FCB09E6E0E0896DF364A0ED45D38562B54D97AB597207E6
R=02ED17F278B2F475F11FCB09E6E0E0896DF364A0ED45D38562B54D97AB597207E6+02B8A8E160E82E98A0B8C5EE01FF29C3A2E77D46025F650C83A66D88BA403B1553
R=025DA08419189F4C48912F64130DC262C522EBE81E10663927FB1341EC41E208E8
Then, you have some public key, and you want to prove, that it contributed to the joined signature:
Code:
P1=02F985ACFBC922F770416B08DF8349C2921C8D3FB897D33016F248AE0BAC01EAC7
R1=03291B441C0114DBD87A886B78A8638925E87AFFC8984923A73065F2A9A5C28354
s1=4070aca19b7fb7d7c176a4f07847bd95254c6aa0a4130cd5a3450687682564ac
R1=(s1*G)-(e*P1)
e*P1=03D320F76728B621214ECBF76377097543FE1C35CD0F59446B30DB101F6E1E8286
-e*P1=02D320F76728B621214ECBF76377097543FE1C35CD0F59446B30DB101F6E1E8286
s1*G=0351C2C68EC24625EAFF584FA481A0E46B99E9F115AA2606653542A3516A599305
R1=0351C2C68EC24625EAFF584FA481A0E46B99E9F115AA2606653542A3516A599305+02D320F76728B621214ECBF76377097543FE1C35CD0F59446B30DB101F6E1E8286
R1=03291B441C0114DBD87A886B78A8638925E87AFFC8984923A73065F2A9A5C28354
Now, you know, that the key "P1" also signed the message "e" with some "(R1,s1)" signature. And you also know, that there are other signers, if you subtract signatures:
Code:
R2=R-R1
R=025DA08419189F4C48912F64130DC262C522EBE81E10663927FB1341EC41E208E8
R1=03291B441C0114DBD87A886B78A8638925E87AFFC8984923A73065F2A9A5C28354
-R1=02291B441C0114DBD87A886B78A8638925E87AFFC8984923A73065F2A9A5C28354
R2=03BA57C07F6845102EF4BF058BBF9A5E306C9C6E3FA4F23FB95CC40B1F73BBA57B
P2=P-P1
P=0256B76A89AF551C6C58B1972EE5BE3DAE2A2A39202AD7439962F647780F025E6A
P1=02F985ACFBC922F770416B08DF8349C2921C8D3FB897D33016F248AE0BAC01EAC7
-P1=03F985ACFBC922F770416B08DF8349C2921C8D3FB897D33016F248AE0BAC01EAC7
P2=02C566F588AE841DBB44D6269F064F03F1231E389CAA3064F5BE8E95F6395332AD
s2=s-s1
s=9308a87057ca70d6b6c597f5043686f2e2fa8487e05e6e46e510528b404da729
s1=4070aca19b7fb7d7c176a4f07847bd95254c6aa0a4130cd5a3450687682564ac
s2=5297fbcebc4ab8fef54ef3048beec95dbdae19e73c4b617141cb4c03d828427d
And you can verify, that the subtracted part is also valid, if you try to check it in the same way:
Code:
P2=02C566F588AE841DBB44D6269F064F03F1231E389CAA3064F5BE8E95F6395332AD
R2=03BA57C07F6845102EF4BF058BBF9A5E306C9C6E3FA4F23FB95CC40B1F73BBA57B
s2=5297fbcebc4ab8fef54ef3048beec95dbdae19e73c4b617141cb4c03d828427d
R2=(s2*G)-(e*P2)
e*P2=028544ECDFF2E8817BC8700B32C59F084791C11C2F623F0429402AFD0EDE4A70B0
-e*P2=038544ECDFF2E8817BC8700B32C59F084791C11C2F623F0429402AFD0EDE4A70B0
s2*G=03B215DA5324BC05125A6CCED56E12A748F9AF8378370EDDDE69FB22AD7E12EFDB
R2=03BA57C07F6845102EF4BF058BBF9A5E306C9C6E3FA4F23FB95CC40B1F73BBA57B
But of course, you don't know, if "P1" or "P2" belongs to a single person. It can be a part of some larger group, which could be splitted further.

Quote from: satoshi
I've moved on to other things.
garlonicon
Copper Member
Legendary
*
Offline Offline

Activity: 944
Merit: 2309


View Profile
November 30, 2024, 01:10:09 PM
Merited by vjudeu (1)
 #3

Your model is too simple. Let's prove, that Satoshi participated in your message:
Code:
P3=03678AFDB0FE5548271967F1A67130B7105CD6A828E03909A67962E0EA1F61DEB6
R3=02DE3DCE2CDA1208E97C20BB537BB13E349A853463AD4DBA1FA20501979B87D495
s3=0000000000000000000000000000000000000000000000000000000000000001

P4=0249A5B691630895861D099B168950F9E8A38289D90657EFFF6C30B121F4C5E208
R4=03BADE9D75A6D917A5D8BA2A26D1F1AED70E59E50E0C87D366CFAD5BC4D9B8D730
s4=9308a87057ca70d6b6c597f5043686f2e2fa8487e05e6e46e510528b404da728

P=P3+P4
P=0256B76A89AF551C6C58B1972EE5BE3DAE2A2A39202AD7439962F647780F025E6A
R=R3+R4
R=025DA08419189F4C48912F64130DC262C522EBE81E10663927FB1341EC41E208E8
s=s3+s4
s=9308a87057ca70d6b6c597f5043686f2e2fa8487e05e6e46e510528b404da729
Joined parts should not be used directly as a proof, by checking just your public key. You should instead hash all public keys, and put it into R-value, and then prove it, by revealing a commitment. In other case, you can prove, that anyone was a part of your multisig.
NotATether
Legendary
*
Offline Offline

Activity: 2212
Merit: 9237


Trêvoid █ No KYC-AML Crypto Swaps


View Profile WWW
December 01, 2024, 09:48:08 AM
 #4

Joined parts should not be used directly as a proof, by checking just your public key. You should instead hash all public keys, and put it into R-value, and then prove it, by revealing a commitment. In other case, you can prove, that anyone was a part of your multisig.

Well, it's not necessarily proof that you signed one of the signatures in the aggregated Schnorr, but just proof that you can sign it. But maybe someone has your key and then signed it.

To deterministically prove that it was indeed you who signed that particular signature, you'd need to use something like BIP 322 and paste some PGP signed message output inside it along with date, block hash etc.

This "nested signature" allows people with your PGP key or any other alternative form of cryptographic identification to know that in fact, you created the associated transaction.

Of course, it can be placed inside an aggregated Schnorr signature if the BIP is implemented, and all other parties create their own as well.

.
 betpanda.io 
 
ANONYMOUS & INSTANT
.......ONLINE CASINO.......
▄███████████████████████▄
█████████████████████████
█████████████████████████
████████▀▀▀▀▀▀███████████
████▀▀▀█░▀▀░░░░░░▄███████
████░▄▄█▄▄▀█▄░░░█▄░▄█████
████▀██▀░▄█▀░░░█▀░░██████
██████░░▄▀░░░░▐░░░▐█▄████
██████▄▄█░▀▀░░░█▄▄▄██████
█████████████████████████
█████████████████████████
█████████████████████████
▀███████████████████████▀
▄███████████████████████▄
█████████████████████████
██████████▀░░░▀██████████
█████████░░░░░░░█████████
███████░░░░░░░░░███████
████████░░░░░░░░░████████
█████████▄░░░░░▄█████████
███████▀▀▀█▄▄▄█▀▀▀███████
██████░░░░▄░▄░▄░░░░██████
██████░░░░█▀█▀█░░░░██████
██████░░░░░░░░░░░░░██████
█████████████████████████
▀███████████████████████▀
▄███████████████████████▄
█████████████████████████
██████████▀▀▀▀▀▀█████████
███████▀▀░░░░░░░░░███████
██████░░░░░░░░░░░░▀█████
██████░░░░░░░░░░░░░░▀████
██████▄░░░░░░▄▄░░░░░░████
████▀▀▀▀▀░░░█░░█░░░░░████
████░▀░▀░░░░░▀▀░░░░░█████
████░▀░▀▄░░░░░░▄▄▄▄██████
█████░▀░█████████████████
█████████████████████████
▀███████████████████████▀
.
SLOT GAMES
....SPORTS....
LIVE CASINO
▄░░▄█▄░░▄
▀█▀░▄▀▄░▀█▀
▄▄▄▄▄▄▄▄▄▄▄   
█████████████
█░░░░░░░░░░░█
█████████████

▄▀▄██▀▄▄▄▄▄███▄▀▄
▄▀▄█████▄██▄▀▄
▄▀▄▐▐▌▐▐▌▄▀▄
▄▀▄█▀██▀█▄▀▄
▄▀▄█████▀▄████▄▀▄
▀▄▀▄▀█████▀▄▀▄▀
▀▀▀▄█▀█▄▀▄▀▀

Regional Sponsor of the
Argentina National Team
johndebord (OP)
Newbie
*
Offline Offline

Activity: 8
Merit: 3


View Profile
December 05, 2024, 09:38:16 AM
Merited by vjudeu (1)
 #5

I appreciate the responses so far. I realize my initial question wasn’t detailed enough, so I’d like to expand and clarify. I’m working with Schnorr signatures, specifically in the context of multisignatures.

Assume the following setup:

  • A publicly known message (it could be any message, or even empty).
  • The message is signed by 100 private/public key pairs, each using a deterministic nonce value.
  • The signatures are aggregated into a single aggregated signature.
  • The public keys are aggregated into a single aggregated public key.
  • All information is public except for the complete set of private/public key pairs that contributed to the aggregated signature and aggregated public key.

The question is: Given the available public information, is it possible to verify whether a specific private/public key pair contributed to the aggregated signature or whether a private/public key pair contributed to the aggregated public key?

If this verification is possible, how can I achieve it using the secp256k1 library?

From an earlier suggestion, it seems I might need to extract the s value from the 64-byte aggregated signature (which includes both R and s) and modify it appropriately. Additionally, I would need to remove the contribution of the specific public key (noting that the corresponding private key and nonce are known) from the aggregated public key, and then verify against the modified aggregated signature and the modified aggregated public key. Or is there a better method using the secp256k1 library? Any guidance would be greatly appreciated.
vjudeu
Copper Member
Legendary
*
Offline Offline

Activity: 909
Merit: 2342


View Profile
December 05, 2024, 11:37:56 AM
 #6

Quote
The message is signed by 100 private/public key pairs, each using a deterministic nonce value.
Can you derive it? If you can, then it is half of the job done, because if people cannot pick any R-values they want, then you can split the signature, by using derived R-value.

Quote
All information is public except for the complete set of private/public key pairs that contributed to the aggregated signature and aggregated public key.
Aggregated public key has to be public, because you have to verify, that the whole multisig is correct. So, you may not know P1, P2, P3, ..., P100, but you have to know "P=P1+P2+P3+...+P100", because this P-value is used to generate a Taproot address for the whole group.

Quote
If this verification is possible, how can I achieve it using the secp256k1 library?
A signature is just a multiplication and addition between the aggregated public key of all participants (P-value), and aggregated signature nonce (R-value). If you can split the joined signature into "P=AliceP+TailP" and "R=AliceR+TailR", then, there is only one matching s-value, meeting the equation "s=sAlice+sTail".

Quote
it seems I might need to extract the s value from the 64-byte aggregated signature (which includes both R and s) and modify it appropriately
I guess you need both R and s, because "R=AliceR+TailR", and "s=sAlice+sTail". And then, if "AliceR" is deterministic, and you can derive it, then you can avoid the attack, described by Garlo Nicon.

Quote
Additionally, I would need to remove the contribution of the specific public key (noting that the corresponding private key and nonce are known) from the aggregated public key, and then verify against the modified aggregated signature and the modified aggregated public key.
You only have to verify two signatures: the full 100-of-100 multisig, and Alice's signature. The tail signature of 99-of-99 multisig will be correct, if those two will also be, and if joining those two parts will lead you to identical signature, as in 100-of-100 multisig.

Quote
Given the available public information, is it possible to verify whether a specific private/public key pair contributed to the aggregated signature or whether a private/public key pair contributed to the aggregated public key?
It depends on the exact multisig implementation (because there is more than one way to do that, and I don't know, which one you will want to observe). It is technically possible to make proofs, that "Alice took part in this 100-of-100 multisig". But: if your model does not reveal any kind of proofs, and you can only see some Taproot address, spent by key, and you see only things, which are seen by all on-chain observers, then you cannot do that.

Because in general, it works in this way: you have a regular signature, where you don't know, if it is 100-of-100 multisig, or 2-of-2 multisig. And then, someone can give you some data, to reveal, that it is "at least 2-of-2 multisig" (or "exactly 100-of-100 multisig" in some models), and that Alice was there. But: if you don't have this additional data, then you don't know that.

Quote from: satoshi
I've moved on to other things.
NotATether
Legendary
*
Offline Offline

Activity: 2212
Merit: 9237


Trêvoid █ No KYC-AML Crypto Swaps


View Profile WWW
December 05, 2024, 12:20:22 PM
 #7

Quote
Given the available public information, is it possible to verify whether a specific private/public key pair contributed to the aggregated signature or whether a private/public key pair contributed to the aggregated public key?
It depends on the exact multisig implementation (because there is more than one way to do that, and I don't know, which one you will want to observe). It is technically possible to make proofs, that "Alice took part in this 100-of-100 multisig". But: if your model does not reveal any kind of proofs, and you can only see some Taproot address, spent by key, and you see only things, which are seen by all on-chain observers, then you cannot do that.

Because in general, it works in this way: you have a regular signature, where you don't know, if it is 100-of-100 multisig, or 2-of-2 multisig. And then, someone can give you some data, to reveal, that it is "at least 2-of-2 multisig" (or "exactly 100-of-100 multisig" in some models), and that Alice was there. But: if you don't have this additional data, then you don't know that.

It's heavily dependent on the code you use to make such aggregated signatures.

Static programs where you can literally type out all 100 private keys can be modified to make the 100-aggregated signature and a single signature for a given key.

But in a practical setting where the private keys are distributed across multiple users, then it's possible you will not have access to all of the private keys. Sure, maybe the software would make everyone send their public keys to a central server and each user can fetch all the keys from that, but it's also possible to send each user their own public key in a decentralized and interactive manner, and then incrementally construct an aggregated signature like this:

U1 => U2 => U3=> .... U100 => U1

So that no person knows more than three public keys at any time.

In that case, a third party wouldn't be able to verify outside participation by another party.

It really depends on your privacy requirements.

.
 betpanda.io 
 
ANONYMOUS & INSTANT
.......ONLINE CASINO.......
▄███████████████████████▄
█████████████████████████
█████████████████████████
████████▀▀▀▀▀▀███████████
████▀▀▀█░▀▀░░░░░░▄███████
████░▄▄█▄▄▀█▄░░░█▄░▄█████
████▀██▀░▄█▀░░░█▀░░██████
██████░░▄▀░░░░▐░░░▐█▄████
██████▄▄█░▀▀░░░█▄▄▄██████
█████████████████████████
█████████████████████████
█████████████████████████
▀███████████████████████▀
▄███████████████████████▄
█████████████████████████
██████████▀░░░▀██████████
█████████░░░░░░░█████████
███████░░░░░░░░░███████
████████░░░░░░░░░████████
█████████▄░░░░░▄█████████
███████▀▀▀█▄▄▄█▀▀▀███████
██████░░░░▄░▄░▄░░░░██████
██████░░░░█▀█▀█░░░░██████
██████░░░░░░░░░░░░░██████
█████████████████████████
▀███████████████████████▀
▄███████████████████████▄
█████████████████████████
██████████▀▀▀▀▀▀█████████
███████▀▀░░░░░░░░░███████
██████░░░░░░░░░░░░▀█████
██████░░░░░░░░░░░░░░▀████
██████▄░░░░░░▄▄░░░░░░████
████▀▀▀▀▀░░░█░░█░░░░░████
████░▀░▀░░░░░▀▀░░░░░█████
████░▀░▀▄░░░░░░▄▄▄▄██████
█████░▀░█████████████████
█████████████████████████
▀███████████████████████▀
.
SLOT GAMES
....SPORTS....
LIVE CASINO
▄░░▄█▄░░▄
▀█▀░▄▀▄░▀█▀
▄▄▄▄▄▄▄▄▄▄▄   
█████████████
█░░░░░░░░░░░█
█████████████

▄▀▄██▀▄▄▄▄▄███▄▀▄
▄▀▄█████▄██▄▀▄
▄▀▄▐▐▌▐▐▌▄▀▄
▄▀▄█▀██▀█▄▀▄
▄▀▄█████▀▄████▄▀▄
▀▄▀▄▀█████▀▄▀▄▀
▀▀▀▄█▀█▄▀▄▀▀

Regional Sponsor of the
Argentina National Team
johndebord (OP)
Newbie
*
Offline Offline

Activity: 8
Merit: 3


View Profile
December 05, 2024, 09:18:04 PM
Last edit: December 06, 2024, 07:30:55 AM by johndebord
 #8

Thank you for the detailed insights and suggestions.

To make the discussion more concrete, I believe it would be best to present a practical example. This will outline all the data that is publicly available.

For clarity and practical implementation, I will use the data structures provided by the secp256k1 library.

Below is a general idea of how the data was generated. Note that this code is illustrative and not the exact implementation:

Code:
secp256k1_keypair secp256k1Keypair{};
if (secp256k1_keypair_create(
        Secp256k1::context().get(), &secp256k1Keypair, privateKey) == 0)
  throw std::runtime_error{"failed to create key pair"};
}
secp256k1_pubkey secp256k1PublicKey{};
if (secp256k1_keypair_pub(
        Secp256k1::context().get(), &secp256k1PublicKey,
        &secp256k1Keypair) == 0) {
  throw std::runtime_error{"failed to extract public key"};
}
secp256k1_musig_secnonce secp256k1MusigSecretNonce{};
secp256k1_musig_pubnonce secp256k1MusigPublicNonce{};
if (secp256k1_musig_nonce_gen(
        Secp256k1::context().get(), &secp256k1MusigSecretNonce,
        &secp256k1MusigPublicNonce, deterministicValue, nullptr,
        &secp256k1PublicKey, message, nullptr, nullptr) == 0) {
  throw std::runtime_error{"failed to generate nonces"};
}
secp256k1_musig_keyagg_cache secp256k1MusigKeyAggregationCache{};
// ... code to aggregate the public keys ...
secp256k1_musig_aggnonce secp256k1MusigAggregatedNonce{};
// ... code to aggregate the public nonces ...
secp256k1_musig_session secp256k1MusigSession{};
if (secp256k1_musig_nonce_process(
        Secp256k1::context().get(), &secp256k1MusigSession,
        &secp256k1MusigAggregatedNonce, message,
        &secp256k1MusigKeyAggregationCache) == 0) {
  throw std::runtime_error{"failed to process nonce"};
}
if (secp256k1_musig_partial_sign(
        Secp256k1::context().get(), &secp256k1MusigPartialSignature,
        &secp256k1MusigSecretNonce, &secp256k1Keypair,
        &secp256k1MusigKeyAggregationCache,
        &secp256k1MusigSession) == 0) {
  throw std::runtime_error{"failed to partially sign"};
}
const constexpr std::size_t schnorrSignatureSize{64};
CryptoArray<schnorrSignatureSize> aggregatedSignature{};
// ... code to aggregate the partial signatures ...
secp256k1_pubkey secp256k1AggregatedPublicKey;
if (secp256k1_musig_pubkey_get(
        Secp256k1::context().get(), &secp256k1AggregatedPublicKey,
        &secp256k1MusigKeyAggregationCache) == 0) {
  throw std::runtime_error{"failed to get aggregated public key"};
}
secp256k1_xonly_pubkey secp256k1XonlyPublicKeyAggregated{};
if (secp256k1_xonly_pubkey_from_pubkey(
        Secp256k1::context().get(), & secp256k1XonlyPublicKeyAggregated,
        nullptr, &secp256k1AggregatedPublicKey) == 0) {
  throw std::runtime_error{
      "failed to convert public key to xonly public key"};
}
if (secp256k1_schnorrsig_verify(
        Secp256k1::context().get(),
        reinterpret_cast<const std::uint8_t *>(
            aggregatedSignature.data()),
        message, message.size(),
        &secp256k1XonlyPublicKeyAggregated) == 0) {
  throw std::runtime_error{"failed to verify signature"};
}

Here is the data generated for the example:

Code:
message: 45F8481247B5C84C48A9952CC7B9AA9B27EAD6A90B2D250E24F81152FB25DECE

Key Pair 1:
  privateKey: 6942F99EDEE91D2B59780FC865016CC7F452973C3D7A8735AE9F76CAACB57CCD
  secp256k1PublicKey: 02A3D48DC0B2BA69F340F68FB4D8BEEF5BE361FB64DAB71EF74ACCF6ACABF70B01
  secp256k1MusigPublicNonce: F57A3DA0D4A492010CF9777E237D4EA7005212072958248A624E761F70A0027AD78EB2A99784232661BEFD6CA2CF17517E49DAFFD7AA6C5BD69FE93A4FC841CB835123BC3404FB9CBEC7BA112C6D0F6800247DB3382619DA497D11E08869DD926DE9457DEF6E9B9197BD44A5DF599CD0260851F0707C693B8FBE58914C1A219BBE888765
  secp256k1MusigSecretNonce: 220EDCF13B5E1B766BF04082984E34CD17575158C7F7F25DC4A81D1C0A9A8E7E33CAD3BD00A362F6E9AF37DC10EAE6162C6FDF8C0074A050025CD1BB0D2BF14010DB8722010BF7ABACF6CC4AF71EB7DA64FB61E35BEFBED8B48FF640F369BAB2C08DD4A340C4EB116E73B2F1C7B4D2FB188BAB910934212CAF70F1175228FAC79D8F5488
  secp256k1MusigPartialSignature: EBFB1A3284DDE05BA570956916623030D11F266A8260A8C4F074190793F088E12EABB41F

Key Pair 2:
  privateKey: 6942F99EDEE91D2B59780FC865016CC7F452973C3D7A8735AE9F76CAACB57E4A
  secp256k1PublicKey: 027FA2874F02C66B1B7778FD178E8332CF113FDB5536F2005E83DD1F6E77037A89
  secp256k1MusigPublicNonce: F57A3DA05150DF05A15EE6CECB86218C9300090AF710EB2B8C6E51CBF3D0B8EB7235E240249F1B51F53F956FA4609B707AED531CCB37854A8FB9AB0E703CDB34BCABA310846018549CE3086CCB7BB0DC25ABB59D79205D918F6911EBF1B10AFE1516CB7F849E04BA945332881E13A3E5C63AC05120F52FABD248F03D5A5D45ECBEC37538
  secp256k1MusigSecretNonce: 220EDCF12372A9189B273F7EE4E6D63CF919F3735C93AE0D4898F2099F986CA572097D2BB6C5A742EDE67454BD1F20B2071A6772529F7EC8815E817BA33A51BFD9BD2B6A897A03776E1FDD835E00F23655DB3F11CF32838E17FD78771B6BC6024F87A27FF224968B7AE9ECD15FCE3965A05C6E90C78D8527F01744204BE48161B6FEA8C7
  secp256k1MusigPartialSignature: EBFB1A321C984FD7C278DC61F18C80A68DA4B231B08337C39A6F508E27EA184989190027

Key Pair 3:
  privateKey: 6942F99EDEE91D2B59780FC865016CC7F452973C3D7A8735AE9F76CAACB58011
  secp256k1PublicKey: 024CBBD03BC9B079BC360A26A034A298EB4520333F87DA4A3D195F390B50B7BD27
  secp256k1MusigPublicNonce: F57A3DA0D30E12A1367AE49B87EAA99FA678DEAD817221DBAC9BB0AFA70D9A08AB36B147D3B03A46C163E0401898FA6D209B6F139680A4E95D07A9A4E501106725C1671B8B916792FBE092441290E2300FC5C093DD40118D29DFF8F25A9C7413B4D86D26E75DAD92CD3A94455B9DDD94673F283B7E0438652FA5FCC91B03C3B44DA39130
  secp256k1MusigSecretNonce: 220EDCF1E48E62BE3329485818C71B5D129F0294E3C7E899AA584B6940FD9737CFDD2EE3483F714997C706C3F984E35CB405F869E3D477EA479E42B300C0EC82B145F0D027BDB7500B395F193D4ADA873F332045EB98A234A0260A36BC79B0C93BD0BB4C940202A0A14E78D617DD2CD39E62C10A2D43EBC66E5962179C7C85D062401979
  secp256k1MusigPartialSignature: EBFB1A32894F0D7D27ACE83FD53B0E14B3F65F963BB62F1F1F29B89B25E5DF3B96174160

Aggregated Data:
  secp256k1XonlyPublicKeyAggregated: 6BF1779F97025AFD66240B3CDDB97DED15B6D44902CBDC98FE8BA5E18F4F37A894B88F128CDDFB02C265154E17E30D09D82A5528F123046235124D34E2F8262C
  aggregatedSignature: BE99342E3F9A4F4EDC5815C90270B32D29DB7B7F65F39DE33FCFA82E9C19EA6B2AC53DB08F965A0ADD29BEEC12BA3833B3EB32C0FAC481F521EE21D97DA5B465

So, given the above data, what specific operations need to be performed to confirm that the public key: 024CBBD03BC9B079BC360A26A034A298EB4520333F87DA4A3D195F390B50B7BD27 contributed to the aggregated signature or the aggregated public key? Or that the secp256k1MusigPartialSignature EBFB1A3284DDE05BA570956916623030D11F266A8260A8C4F074190793F088E12EABB41F contributed to the aggregated signature?
NotATether
Legendary
*
Offline Offline

Activity: 2212
Merit: 9237


Trêvoid █ No KYC-AML Crypto Swaps


View Profile WWW
December 06, 2024, 07:03:54 AM
 #9

OP, what is the value of deterministicValue that you used for this line?

Code:
if (secp256k1_musig_nonce_gen(
        Secp256k1::context().get(), &secp256k1MusigSecretNonce,
        &secp256k1MusigPublicNonce, deterministicValue, nullptr,
        &secp256k1PublicKey, message, nullptr, nullptr) == 0) {
  throw std::runtime_error{"failed to generate nonces"};
}

.
 betpanda.io 
 
ANONYMOUS & INSTANT
.......ONLINE CASINO.......
▄███████████████████████▄
█████████████████████████
█████████████████████████
████████▀▀▀▀▀▀███████████
████▀▀▀█░▀▀░░░░░░▄███████
████░▄▄█▄▄▀█▄░░░█▄░▄█████
████▀██▀░▄█▀░░░█▀░░██████
██████░░▄▀░░░░▐░░░▐█▄████
██████▄▄█░▀▀░░░█▄▄▄██████
█████████████████████████
█████████████████████████
█████████████████████████
▀███████████████████████▀
▄███████████████████████▄
█████████████████████████
██████████▀░░░▀██████████
█████████░░░░░░░█████████
███████░░░░░░░░░███████
████████░░░░░░░░░████████
█████████▄░░░░░▄█████████
███████▀▀▀█▄▄▄█▀▀▀███████
██████░░░░▄░▄░▄░░░░██████
██████░░░░█▀█▀█░░░░██████
██████░░░░░░░░░░░░░██████
█████████████████████████
▀███████████████████████▀
▄███████████████████████▄
█████████████████████████
██████████▀▀▀▀▀▀█████████
███████▀▀░░░░░░░░░███████
██████░░░░░░░░░░░░▀█████
██████░░░░░░░░░░░░░░▀████
██████▄░░░░░░▄▄░░░░░░████
████▀▀▀▀▀░░░█░░█░░░░░████
████░▀░▀░░░░░▀▀░░░░░█████
████░▀░▀▄░░░░░░▄▄▄▄██████
█████░▀░█████████████████
█████████████████████████
▀███████████████████████▀
.
SLOT GAMES
....SPORTS....
LIVE CASINO
▄░░▄█▄░░▄
▀█▀░▄▀▄░▀█▀
▄▄▄▄▄▄▄▄▄▄▄   
█████████████
█░░░░░░░░░░░█
█████████████

▄▀▄██▀▄▄▄▄▄███▄▀▄
▄▀▄█████▄██▄▀▄
▄▀▄▐▐▌▐▐▌▄▀▄
▄▀▄█▀██▀█▄▀▄
▄▀▄█████▀▄████▄▀▄
▀▄▀▄▀█████▀▄▀▄▀
▀▀▀▄█▀█▄▀▄▀▀

Regional Sponsor of the
Argentina National Team
johndebord (OP)
Newbie
*
Offline Offline

Activity: 8
Merit: 3


View Profile
December 06, 2024, 07:27:29 AM
Merited by vjudeu (1)
 #10

OP, what is the value of deterministicValue that you used for this line?

Code:
if (secp256k1_musig_nonce_gen(
        Secp256k1::context().get(), &secp256k1MusigSecretNonce,
        &secp256k1MusigPublicNonce, deterministicValue, nullptr,
        &secp256k1PublicKey, message, nullptr, nullptr) == 0) {
  throw std::runtime_error{"failed to generate nonces"};
}

Code:
if (secp256k1_musig_nonce_gen(
        Secp256k1::context().get(), &secp256k1MusigSecretNonce,
        &secp256k1MusigPublicNonce, masterPrivateKey, nullptr,
        &secp256k1PublicKey, messageHash, nullptr, nullptr) == 0) {
  throw std::runtime_error{"failed to generate nonces"};
}

In this case, the masterPrivateKey used is:

Code:
6942F99EDEE91D2B59780FC865016CC7F452973C3D7A8735AE9F76CAACB57C1A

It is important to note that everything is known except the full set of private/public key pairs that contributed to the aggregated signature or public key.
vjudeu
Copper Member
Legendary
*
Offline Offline

Activity: 909
Merit: 2342


View Profile
December 06, 2024, 01:19:13 PM
 #11

Let's see how keys are aggregated:
Code:
P3=024CBBD03BC9B079BC360A26A034A298EB4520333F87DA4A3D195F390B50B7BD27
P2=027FA2874F02C66B1B7778FD178E8332CF113FDB5536F2005E83DD1F6E77037A89
P1=02A3D48DC0B2BA69F340F68FB4D8BEEF5BE361FB64DAB71EF74ACCF6ACABF70B01
SHA-256("KeyAgg list")=481c971c3c0b46d7f0b275ae598d4e2c7ed7319c594a5c6ec79ea0d4990294f0
SHA-256(481c971c3c0b46d7f0b275ae598d4e2c7ed7319c594a5c6ec79ea0d4990294f0481c971c3c0b46d7f0b275ae598d4e2c7ed7319c594a5c6ec79ea0d4990294f0)=634f77a422b6a39257a76f2c13ae017702bacd4c49b33dad1139cdd56060d360
SHA-256(481c971c3c0b46d7f0b275ae598d4e2c7ed7319c594a5c6ec79ea0d4990294f0481c971c3c0b46d7f0b275ae598d4e2c7ed7319c594a5c6ec79ea0d4990294f0024CBBD03BC9B079BC360A26A034A298EB4520333F87DA4A3D195F390B50B7BD27027FA2874F02C66B1B7778FD178E8332CF113FDB5536F2005E83DD1F6E77037A8902A3D48DC0B2BA69F340F68FB4D8BEEF5BE361FB64DAB71EF74ACCF6ACABF70B01)=9309c24c2bc0162de694d5f3a80e35cd17e7db4afd1994b86a1470dc4ea5bf57
Then, the aggregated hash is 9309c24c2bc0162de694d5f3a80e35cd17e7db4afd1994b86a1470dc4ea5bf57, if those three keys are sorted. Because SHA-256 is executed in 512-bit chunks, it is aligned in this way:
Code:
6a09e667 bb67ae85 3c6ef372 a54ff53a 510e527f 9b05688c 1f83d9ab 5be0cd19

481c971c 3c0b46d7 f0b275ae 598d4e2c
7ed7319c 594a5c6e c79ea0d4 990294f0
481c971c 3c0b46d7 f0b275ae 598d4e2c
7ed7319c 594a5c6e c79ea0d4 990294f0

b399d5e0 c8fff302 6badac71 07c5b7f1 9701e2ef 2a72ecf8 201a4c7b ab148a38

024CBBD0 3BC9B079 BC360A26 A034A298
EB452033 3F87DA4A 3D195F39 0B50B7BD
27027FA2 874F02C6 6B1B7778 FD178E83
32CF113F DB5536F2 005E83DD 1F6E7703

e4408850 f10bfbab 723cc288 07ec49e0 491d6a67 50fff49c 2b6358ec 0ac4bc1a

7A8902A3 D48DC0B2 BA69F340 F68FB4D8
BEEF5BE3 61FB64DA B71EF74A CCF6ACAB
F70B0180 00000000 00000000 00000000
00000000 00000000 00000000 00000518

9309c24c 2bc0162d e694d5f3 a80e35cd 17e7db4a fd1994b8 6a1470dc 4ea5bf57
So, I guess by providing some in-between hashes, it could be possible to prove, that the aggregated hash contained a specific public key. For example, for the last key:
Code:
e4408850 f10bfbab 723cc288 07ec49e0 491d6a67 50fff49c 2b6358ec 0ac4bc1a

7A8902A3 D48DC0B2 BA69F340 F68FB4D8
BEEF5BE3 61FB64DA B71EF74A CCF6ACAB
F70B0180 00000000 00000000 00000000
00000000 00000000 00000000 00000518

9309c24c 2bc0162d e694d5f3 a80e35cd 17e7db4a fd1994b8 6a1470dc 4ea5bf57
Of course, public keys are not-so-well-aligned, because they take 33 bytes, instead of 32, but still, picking any other keys will change the initialization vector from e4408850f10bfbab723cc28807ec49e0491d6a6750fff49c2b6358ec0ac4bc1a to something else. And picking any other data, would very likely not hash into 9309c24c2bc0162de694d5f3a80e35cd17e7db4afd1994b86a1470dc4ea5bf57. But: I still have to dig deeper into the code, to produce more detailed logs.

I assume at least some of my assumptions about SHA-256 are correct, because I found the same constants in the code:
https://github.com/bitcoin-core/secp256k1/blob/master/src/modules/musig/keyagg_impl.h#L64
Code:
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
 * SHA256 to SHA256("KeyAgg list")||SHA256("KeyAgg list"). */
static void secp256k1_musig_keyagglist_sha256(secp256k1_sha256 *sha) {
    secp256k1_sha256_initialize(sha);

    sha->s[0] = 0xb399d5e0ul;
    sha->s[1] = 0xc8fff302ul;
    sha->s[2] = 0x6badac71ul;
    sha->s[3] = 0x07c5b7f1ul;
    sha->s[4] = 0x9701e2eful;
    sha->s[5] = 0x2a72ecf8ul;
    sha->s[6] = 0x201a4c7bul;
    sha->s[7] = 0xab148a38ul;
    sha->bytes = 64;
}

Quote from: satoshi
I've moved on to other things.
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!