-
Notifications
You must be signed in to change notification settings - Fork 208
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
2,903 additions
and
2,273 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,3 +64,5 @@ build-aux/test-driver | |
src/stamp-h1 | ||
libsecp256k1.pc | ||
contrib/gh-pr-create.sh | ||
|
||
example_musig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
/*********************************************************************** | ||
* Copyright (c) 2018 Jonas Nick * | ||
* Distributed under the MIT software license, see the accompanying * | ||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.* | ||
**********************************************************************/ | ||
|
||
/** | ||
* This file demonstrates how to use the MuSig module to create a multisignature. | ||
* Additionally, see the documentation in include/secp256k1_musig.h. | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <assert.h> | ||
#include <secp256k1.h> | ||
#include <secp256k1_schnorrsig.h> | ||
#include <secp256k1_musig.h> | ||
|
||
struct signer_secrets { | ||
secp256k1_keypair keypair; | ||
secp256k1_musig_secnonce secnonce; | ||
}; | ||
|
||
struct signer { | ||
secp256k1_xonly_pubkey pubkey; | ||
secp256k1_musig_pubnonce pubnonce; | ||
secp256k1_musig_partial_sig partial_sig; | ||
}; | ||
|
||
/* Number of public keys involved in creating the aggregate signature */ | ||
#define N_SIGNERS 3 | ||
/* Create a key pair, store it in signer_secrets->keypair and signer->pubkey */ | ||
int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) { | ||
unsigned char seckey[32]; | ||
FILE *frand = fopen("/dev/urandom", "r"); | ||
if (frand == NULL) { | ||
return 0; | ||
} | ||
do { | ||
if(!fread(seckey, sizeof(seckey), 1, frand)) { | ||
fclose(frand); | ||
return 0; | ||
} | ||
/* The probability that this not a valid secret key is approximately 2^-128 */ | ||
} while (!secp256k1_ec_seckey_verify(ctx, seckey)); | ||
fclose(frand); | ||
if (!secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) { | ||
return 0; | ||
} | ||
if (!secp256k1_keypair_xonly_pub(ctx, &signer->pubkey, NULL, &signer_secrets->keypair)) { | ||
return 0; | ||
} | ||
return 1; | ||
} | ||
|
||
/* Sign a message hash with the given key pairs and store the result in sig */ | ||
int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, unsigned char *sig64) { | ||
int i; | ||
const secp256k1_xonly_pubkey *pubkeys[N_SIGNERS]; | ||
const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS]; | ||
const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS]; | ||
/* The same for all signers */ | ||
secp256k1_musig_keyagg_cache cache; | ||
secp256k1_musig_session session; | ||
|
||
for (i = 0; i < N_SIGNERS; i++) { | ||
FILE *frand; | ||
unsigned char seckey[32]; | ||
unsigned char session_id[32]; | ||
/* Create random session ID. It is absolutely necessary that the session ID | ||
* is unique for every call of secp256k1_musig_nonce_gen. Otherwise | ||
* it's trivial for an attacker to extract the secret key! */ | ||
frand = fopen("/dev/urandom", "r"); | ||
if(frand == NULL) { | ||
return 0; | ||
} | ||
if (!fread(session_id, 32, 1, frand)) { | ||
fclose(frand); | ||
return 0; | ||
} | ||
fclose(frand); | ||
if (!secp256k1_keypair_sec(ctx, seckey, &signer_secrets[i].keypair)) { | ||
return 0; | ||
} | ||
/* Initialize session and create secret nonce for signing and public | ||
* nonce to send to the other signers. */ | ||
if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, msg32, NULL, NULL)) { | ||
return 0; | ||
} | ||
pubkeys[i] = &signer[i].pubkey; | ||
pubnonces[i] = &signer[i].pubnonce; | ||
} | ||
/* Communication round 1: A production system would exchange public nonces | ||
* here before moving on. */ | ||
for (i = 0; i < N_SIGNERS; i++) { | ||
secp256k1_musig_aggnonce agg_pubnonce; | ||
|
||
/* Create aggregate pubkey, aggregate nonce and initialize signer data */ | ||
if (!secp256k1_musig_pubkey_agg(ctx, NULL, NULL, &cache, pubkeys, N_SIGNERS)) { | ||
return 0; | ||
} | ||
if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) { | ||
return 0; | ||
} | ||
if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, &cache, NULL)) { | ||
return 0; | ||
} | ||
/* partial_sign will clear the secnonce by setting it to 0. That's because | ||
* you must _never_ reuse the secnonce (or use the same session_id to | ||
* create a secnonce). If you do, you effectively reuse the nonce and | ||
* leak the secret key. */ | ||
if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, &cache, &session)) { | ||
return 0; | ||
} | ||
partial_sigs[i] = &signer[i].partial_sig; | ||
} | ||
/* Communication round 2: A production system would exchange | ||
* partial signatures here before moving on. */ | ||
for (i = 0; i < N_SIGNERS; i++) { | ||
/* To check whether signing was successful, it suffices to either verify | ||
* the aggregate signature with the aggregate public key using | ||
* secp256k1_schnorrsig_verify, or verify all partial signatures of all | ||
* signers individually. Verifying the aggregate signature is cheaper but | ||
* verifying the individual partial signatures has the advantage that it | ||
* can be used to determine which of the partial signatures are invalid | ||
* (if any), i.e., which of the partial signatures cause the aggregate | ||
* signature to be invalid and thus the protocol run to fail. It's also | ||
* fine to first verify the aggregate sig, and only verify the individual | ||
* sigs if it does not work. | ||
*/ | ||
if (!secp256k1_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, &cache, &session)) { | ||
return 0; | ||
} | ||
} | ||
return secp256k1_musig_partial_sig_agg(ctx, sig64, &session, partial_sigs, N_SIGNERS); | ||
} | ||
|
||
int main(void) { | ||
secp256k1_context* ctx; | ||
int i; | ||
struct signer_secrets signer_secrets[N_SIGNERS]; | ||
struct signer signers[N_SIGNERS]; | ||
const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS]; | ||
secp256k1_xonly_pubkey agg_pk; | ||
unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; | ||
unsigned char sig[64]; | ||
|
||
/* Create a context for signing and verification */ | ||
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); | ||
printf("Creating key pairs......"); | ||
for (i = 0; i < N_SIGNERS; i++) { | ||
if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) { | ||
printf("FAILED\n"); | ||
return 1; | ||
} | ||
pubkeys_ptr[i] = &signers[i].pubkey; | ||
} | ||
printf("ok\n"); | ||
printf("Combining public keys..."); | ||
if (!secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, NULL, pubkeys_ptr, N_SIGNERS)) { | ||
printf("FAILED\n"); | ||
return 1; | ||
} | ||
printf("ok\n"); | ||
printf("Signing message........."); | ||
if (!sign(ctx, signer_secrets, signers, msg, sig)) { | ||
printf("FAILED\n"); | ||
return 1; | ||
} | ||
printf("ok\n"); | ||
printf("Verifying signature....."); | ||
if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &agg_pk)) { | ||
printf("FAILED\n"); | ||
return 1; | ||
} | ||
printf("ok\n"); | ||
secp256k1_context_destroy(ctx); | ||
return 0; | ||
} |
Oops, something went wrong.