diff --git a/include/secp256k1_rangeproof.h b/include/secp256k1_rangeproof.h index 746710610..c3b29bbc5 100644 --- a/include/secp256k1_rangeproof.h +++ b/include/secp256k1_rangeproof.h @@ -284,6 +284,99 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( size_t plen ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); +/** Returns the serialized size of a ring signature with the given number of keys + * Args: ctx: pointer to a context object (cannot be NULL) + * In: n_keys: number of keys + */ +SECP256K1_API size_t secp256k1_ring_signature_length( + const secp256k1_context* ctx, + size_t n_keys +) SECP256K1_ARG_NONNULL(1); + +/** Returns the minimum scratch space size to sign or verify a ring signature with the given number of keys + * Args: ctx: pointer to a context object (cannot be NULL) + * In: n_keys: number of keys + */ +SECP256K1_API size_t secp256k1_ring_signature_scratch_space_size( + const secp256k1_context* ctx, + size_t n_keys +) SECP256K1_ARG_NONNULL(1); + +/** Produces a linear-sized ring signature with a given set of keys, returning a "proof" which can be used to reveal the signer + * Returns 1: Proof successfully created. + * 0: if sig_len was less than the `secp256k1_ring_signature_length` for this number of keys + * Args: ctx: pointer to a context object, initialized for both signing and verification (cannot be NULL) + * scratch: scratch space with enough space available (to learn a minimum size + * use `secp256k1_ring_signature_scratch_space_size`) (cannot be NULL) + * Out: sig: pointer to array to receive the signature (cannot be NULL) + * author_proof: if non-NULL, pointer to a 32-byte byte array to receive a proof of authorship + * In: pubkeys: pointer to array of public keys (cannot be NULL) + * n_pubkeys: length of the above array + * sec_key: secret key to sign with (cannot be NULL) + * sec_idx: index of the signer in the list of pubkeys + * nonce: if non-NULL, 32-byte secret nonce used to randomize the proof; to make a proof it is + * impossible to revoke anonymity on, leave `author_proof` NULL and throw this value away + * message: 32-byte message hash to sign (cannot be NULL) + * In/out: sig_len: pointer to the size of `sig`, overwritten with the actual proof size (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ring_sign( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + unsigned char* sig, + size_t* sig_len, + unsigned char* author_proof, + const secp256k1_pubkey* pubkeys, + size_t n_pubkeys, + const unsigned char *sec_key, + size_t sec_idx, + const unsigned char *nonce, + const unsigned char *message +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11); + +/** Verify a ring signature + * Returns 1: Signature was valid + * 0: Signature was not valid + * Args: ctx: pointer to a context object, initialized for verification (cannot be NULL) + * scratch: scratch space with enough space available (to learn a minimum size + * use `secp256k1_ring_signature_scratch_space_size`) (cannot be NULL) + * In: sig: pointer to the signature to verify (cannot be NULL) + * sig_len: length of the signature in bytes. + * pubkeys: pointer to array of public keys (cannot be NULL) + * n_pubkeys: length of the above array + * message: 32-byte message hash that was signed (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ring_verify( + const secp256k1_context* ctx, + secp256k1_scratch_space* scratch, + const unsigned char* sig, + size_t sig_len, + const secp256k1_pubkey* pubkeys, + size_t n_pubkeys, + const unsigned char *message +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7); + +/** De-anonymizes a ring signature, given a 32-byte author proof + * Returns 1: Signature was successfully deanonymized + * 0: Author proof was a lie + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: author_idx: populated with the index of the author of the ring signature on success; + * if the function fails, this value is unspecified (cannot be NULL) + * In: sig: pointer to the signature to verify (cannot be NULL) + * sig_len: length of the signature in bytes. + * author_proof: an author proof as output from `secp256k1_ring_sign` (cannot be NULL) + * pubkeys: pointer to array of public keys (cannot be NULL) + * n_pubkeys: length of the above array + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ring_deanonymize( + const secp256k1_context* ctx, + size_t *author_idx, + const unsigned char* sig, + size_t sig_len, + const unsigned char* author_proof, + const secp256k1_pubkey* pubkeys, + size_t n_pubkeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + # ifdef __cplusplus } # endif diff --git a/src/modules/rangeproof/main_impl.h b/src/modules/rangeproof/main_impl.h index c7f921fcc..4e2280da6 100644 --- a/src/modules/rangeproof/main_impl.h +++ b/src/modules/rangeproof/main_impl.h @@ -297,4 +297,229 @@ int secp256k1_rangeproof_sign(const secp256k1_context* ctx, unsigned char *proof proof, plen, min_value, &commitp, blind, nonce, exp, min_bits, value, message, msg_len, extra_commit, extra_commit_len, &genp); } +size_t secp256k1_ring_signature_length(const secp256k1_context* ctx, size_t n_keys) { + (void) ctx; + if (SIZE_MAX / 32 < n_keys + 1) + return 0; + return 32 * (n_keys + 1); +} + +size_t secp256k1_ring_signature_scratch_space_size(const secp256k1_context* ctx, size_t n_keys) { + (void) ctx; + if (SIZE_MAX / (sizeof(secp256k1_gej) + sizeof(secp256k1_scalar)) < n_keys) { + return 0; + } else { + size_t gej_len = ROUND_TO_ALIGN(sizeof(secp256k1_gej) * n_keys); + size_t sca_len = ROUND_TO_ALIGN(sizeof(secp256k1_scalar) * n_keys); + if (gej_len + sca_len < gej_len) { + return 0; + } + return gej_len + sca_len; + } +} + +int secp256k1_ring_sign(const secp256k1_context* ctx, secp256k1_scratch_space* scratch, unsigned char* sig, size_t* sig_len, unsigned char* author_proof, const secp256k1_pubkey* pubkeys, size_t n_pubkeys, const unsigned char *sec_key, size_t sec_idx, const unsigned char *nonce, const unsigned char *message) { + size_t scratch_checkpoint; + secp256k1_rfc6979_hmac_sha256 rng; + int overflow; + secp256k1_scalar x; + secp256k1_scalar k; + secp256k1_scalar *s; + secp256k1_gej *pubs; + secp256k1_sha256 sha2eng; + unsigned char buf[32]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(scratch != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(sig_len != NULL); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(sec_key != NULL); + ARG_CHECK(message != NULL); + + /* Check for overflow, or 0 pubkeys (which is technically ok but probably + * more confusing than meaningful) */ + if (secp256k1_ring_signature_length(ctx, n_pubkeys) == 0 || secp256k1_ring_signature_scratch_space_size(ctx, n_pubkeys) == 0) { + return 0; + } + if (*sig_len >= secp256k1_ring_signature_length(ctx, n_pubkeys)) { + *sig_len = secp256k1_ring_signature_length(ctx, n_pubkeys); + } else { + return 0; + } + + secp256k1_scalar_set_b32(&x, sec_key, &overflow); + if (overflow || secp256k1_scalar_is_zero(&x)) { + return 0; + } + + /* Directly derive `k` from the nonce */ + secp256k1_sha256_initialize(&sha2eng); + secp256k1_sha256_write(&sha2eng, (unsigned char*)"ringsig/k", 9); + secp256k1_sha256_finalize(&sha2eng, buf); + + secp256k1_sha256_initialize(&sha2eng); + secp256k1_sha256_write(&sha2eng, buf, 32); + secp256k1_sha256_write(&sha2eng, buf, 32); + secp256k1_sha256_write(&sha2eng, sec_key, 32); + secp256k1_sha256_write(&sha2eng, message, 32); + if (nonce != NULL) { + secp256k1_sha256_write(&sha2eng, nonce, 32); + } + secp256k1_sha256_finalize(&sha2eng, buf); + + secp256k1_scalar_set_b32(&k, buf, &overflow); + if (overflow || secp256k1_scalar_is_zero(&k)) { + return 0; + } + + /* Use a different tagged hash to get a seed for the `s` values */ + secp256k1_sha256_initialize(&sha2eng); + secp256k1_sha256_write(&sha2eng, (unsigned char*)"ringsig/s", 9); + secp256k1_sha256_finalize(&sha2eng, buf); + + secp256k1_sha256_initialize(&sha2eng); + secp256k1_sha256_write(&sha2eng, buf, 32); + secp256k1_sha256_write(&sha2eng, buf, 32); + secp256k1_sha256_write(&sha2eng, sec_key, 32); + secp256k1_sha256_write(&sha2eng, message, 32); + if (nonce != NULL) { + secp256k1_sha256_write(&sha2eng, nonce, 32); + } + secp256k1_sha256_finalize(&sha2eng, buf); + + if (author_proof != NULL) { + memcpy(author_proof, buf, 32); + } + + /* Generate s values from the author proof (and unpack pubkeys while we're at it) */ + scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + s = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, n_pubkeys * sizeof(*s)); + pubs = (secp256k1_gej*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, n_pubkeys * sizeof(*pubs)); + if (s == NULL || pubs == NULL) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, buf, 32); + for (i = 0; i < n_pubkeys; i++) { + secp256k1_ge pubp; + CHECK (secp256k1_pubkey_load(ctx, &pubp, &pubkeys[i])); + secp256k1_gej_set_ge(&pubs[i], &pubp); + + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, buf, 32); + secp256k1_scalar_set_b32(&s[i], buf, &overflow); + } while (overflow || secp256k1_scalar_is_zero(&s[i])); + } + + /* Produce the proof -- will update the `s` array and output `e0` in `buf` */ + if (!secp256k1_borromean_sign(&ctx->ecmult_ctx, &ctx->ecmult_gen_ctx, buf, s, pubs, &k, &x, &n_pubkeys, &sec_idx, 1, message, 32)) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + + /* Serialize it */ + memcpy(sig, buf, 32); + for (i = 0; i < n_pubkeys; i++) { + secp256k1_scalar_get_b32(&sig[32 * (1 + i)], &s[i]); + } + + return 1; +} + +int secp256k1_ring_verify(const secp256k1_context* ctx, secp256k1_scratch_space* scratch, const unsigned char* sig, size_t sig_len, const secp256k1_pubkey* pubkeys, size_t n_pubkeys, const unsigned char *message) { + size_t scratch_checkpoint; + secp256k1_scalar *s; + secp256k1_gej *pubs; + size_t i; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(scratch != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(message != NULL); + + /* Check for overflow, or 0 pubkeys (which is technically ok but probably + * more confusing than meaningful) */ + if (secp256k1_ring_signature_length(ctx, n_pubkeys) == 0 || secp256k1_ring_signature_scratch_space_size(ctx, n_pubkeys) == 0) { + return 0; + } + if (sig_len != secp256k1_ring_signature_length(ctx, n_pubkeys)) { + return 0; + } + + scratch_checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch); + s = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, n_pubkeys * sizeof(*s)); + pubs = (secp256k1_gej*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, n_pubkeys * sizeof(*pubs)); + if (s == NULL || pubs == NULL) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + + for (i = 0; i < n_pubkeys; i++) { + secp256k1_ge pubp; + CHECK (secp256k1_pubkey_load(ctx, &pubp, &pubkeys[i])); + secp256k1_gej_set_ge(&pubs[i], &pubp); + + secp256k1_scalar_set_b32(&s[i], &sig[32 * (1 + i)], &overflow); + if (overflow || secp256k1_scalar_is_zero(&s[i])) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + } + + if (!secp256k1_borromean_verify(&ctx->ecmult_ctx, NULL, sig, s, pubs, &n_pubkeys, 1, message, 32)) { + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 0; + } + secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, scratch_checkpoint); + return 1; +} + +int secp256k1_ring_deanonymize(const secp256k1_context* ctx, size_t *author_idx, const unsigned char* sig, size_t sig_len, const unsigned char* author_proof, const secp256k1_pubkey* pubkeys, size_t n_pubkeys) { + secp256k1_rfc6979_hmac_sha256 rng; + int found = 0; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(author_idx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(author_proof != NULL); + ARG_CHECK(pubkeys != NULL); + + if (sig_len == 0 || sig_len != secp256k1_ring_signature_length(ctx, n_pubkeys)) { + return 0; + } + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, author_proof, 32); + for (i = 0; i < n_pubkeys; i++) { + unsigned char buf[32]; + secp256k1_scalar ref; + int overflow; + + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, buf, 32); + secp256k1_scalar_set_b32(&ref, buf, &overflow); + } while (overflow || secp256k1_scalar_is_zero(&ref)); + + /* FIXME this should not be var-time since author_proof may be secret */ + if (secp256k1_memcmp_var(&sig[32 * (i + 1)], buf, 32) != 0) { + if (found == 1) { + return 0; + } + found = 1; + *author_idx = i; + } + } + + return 1; +} + #endif diff --git a/src/modules/rangeproof/tests_impl.h b/src/modules/rangeproof/tests_impl.h index 7b8a610ce..bf9f89cf1 100644 --- a/src/modules/rangeproof/tests_impl.h +++ b/src/modules/rangeproof/tests_impl.h @@ -229,6 +229,129 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c } } +static void test_ring_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const int32_t *ecount) { + unsigned char sig[128 + 32]; + size_t sig_len = sizeof(sig); + secp256k1_pubkey pubkeys[4]; + const unsigned char message[32] = "a tout le monde; a tout mes amis"; + const unsigned char nonce[32] = "..je vous aime..je dois partir.."; + unsigned char sec_key[32]; + unsigned char author_proof[32]; + secp256k1_scratch *scratch; + size_t author_idx = 2; + + secp256k1_testrand256(sec_key); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkeys[0], sec_key)); + secp256k1_testrand256(sec_key); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkeys[1], sec_key)); + secp256k1_testrand256(sec_key); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkeys[3], sec_key)); + secp256k1_testrand256(sec_key); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkeys[2], sec_key)); + + CHECK(*ecount == 0); + + CHECK(secp256k1_ring_signature_length(none, 0) == 32); + CHECK(secp256k1_ring_signature_length(none, 1) == 64); + CHECK(secp256k1_ring_signature_length(none, 1000) == 32032); + CHECK(*ecount == 0); + + CHECK(secp256k1_ring_signature_scratch_space_size(none, 1) > 0); + CHECK(secp256k1_ring_signature_scratch_space_size(none, 100000) > 0); + CHECK(*ecount == 0); + + scratch = secp256k1_scratch_space_create(ctx, secp256k1_ring_signature_scratch_space_size(none, 4)); + + CHECK(secp256k1_ring_sign(both, NULL, sig, &sig_len, author_proof, pubkeys, 4, sec_key, author_idx, nonce, message) == 0); + CHECK(*ecount == 1); + CHECK(secp256k1_ring_sign(both, scratch, NULL, &sig_len, author_proof, pubkeys, 4, sec_key, author_idx, nonce, message) == 0); + CHECK(*ecount == 2); + CHECK(secp256k1_ring_sign(both, scratch, sig, NULL, author_proof, pubkeys, 4, sec_key, author_idx, nonce, message) == 0); + CHECK(*ecount == 3); + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, NULL, pubkeys, 4, sec_key, author_idx, nonce, message) == 1); + CHECK(*ecount == 3); /* author_proof can be NULL */ + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, author_proof, NULL, 4, sec_key, author_idx, nonce, message) == 0); + CHECK(*ecount == 4); + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, author_proof, pubkeys, 4, NULL, author_idx, nonce, message) == 0); + CHECK(*ecount == 5); + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, author_proof, pubkeys, 4, sec_key, author_idx, NULL, message) == 1); + CHECK(*ecount == 5); /* nonce can be NULL */ + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, author_proof, pubkeys, 4, sec_key, author_idx, nonce, NULL) == 0); + CHECK(*ecount == 6); + CHECK(secp256k1_ring_sign(none, scratch, sig, &sig_len, author_proof, pubkeys, 4, sec_key, author_idx, nonce, message) == 0); + CHECK(*ecount == 7); + CHECK(secp256k1_ring_sign(sign, scratch, sig, &sig_len, author_proof, pubkeys, 4, sec_key, author_idx, nonce, message) == 0); + CHECK(*ecount == 8); + CHECK(secp256k1_ring_sign(vrfy, scratch, sig, &sig_len, author_proof, pubkeys, 4, sec_key, author_idx, nonce, message) == 0); + CHECK(*ecount == 9); + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, author_proof, pubkeys, SIZE_MAX, sec_key, author_idx, nonce, message) == 0); + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, author_proof, pubkeys, SIZE_MAX / 32, sec_key, author_idx, nonce, message) == 0); + CHECK(*ecount == 9); + + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, author_proof, pubkeys, 0, sec_key, author_idx, nonce, message) == 0); + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, author_proof, pubkeys, 3, sec_key, author_idx, nonce, message) == 1); + CHECK(sig_len == secp256k1_ring_signature_length(none, 3)); + sig_len = secp256k1_ring_signature_length(none, 4); + CHECK(secp256k1_ring_sign(both, scratch, sig, &sig_len, author_proof, pubkeys, 4, sec_key, author_idx, nonce, message) == 1); + CHECK(sig_len == secp256k1_ring_signature_length(none, 4)); + CHECK(*ecount == 9); + + + CHECK(secp256k1_ring_verify(both, NULL, sig, sig_len, pubkeys, 4, message) == 0); + CHECK(*ecount == 10); + CHECK(secp256k1_ring_verify(both, scratch, NULL, sig_len, pubkeys, 4, message) == 0); + CHECK(*ecount == 11); + CHECK(secp256k1_ring_verify(both, scratch, sig, sig_len - 1, pubkeys, 4, message) == 0); + CHECK(*ecount == 11); + CHECK(secp256k1_ring_verify(both, scratch, sig, sig_len, NULL, 4, message) == 0); + CHECK(*ecount == 12); + CHECK(secp256k1_ring_verify(both, scratch, sig, sig_len, pubkeys, 4, NULL) == 0); + CHECK(*ecount == 13); + CHECK(secp256k1_ring_verify(none, scratch, sig, sig_len, pubkeys, 4, message) == 0); + CHECK(*ecount == 14); + CHECK(secp256k1_ring_verify(sign, scratch, sig, sig_len, pubkeys, 4, message) == 0); + CHECK(*ecount == 15); + CHECK(secp256k1_ring_verify(vrfy, scratch, sig, sig_len, pubkeys, 5, message) == 0); + CHECK(secp256k1_ring_verify(vrfy, scratch, sig, sig_len, pubkeys, SIZE_MAX, message) == 0); + CHECK(secp256k1_ring_verify(vrfy, scratch, sig, sig_len, pubkeys, SIZE_MAX / 32, message) == 0); + CHECK(*ecount == 15); + + CHECK(secp256k1_ring_verify(vrfy, scratch, sig, sig_len, pubkeys, 3, message) == 0); + CHECK(secp256k1_ring_verify(vrfy, scratch, sig, sig_len, pubkeys, 0, message) == 0); + CHECK(secp256k1_ring_verify(vrfy, scratch, sig, sig_len, pubkeys, 4, sec_key) == 0); + + CHECK(secp256k1_ring_verify(vrfy, scratch, sig, sig_len, pubkeys, 4, message) == 1); + CHECK(*ecount == 15); + + CHECK(secp256k1_ring_deanonymize(none, NULL, sig, sig_len, author_proof, pubkeys, 4) == 0); + CHECK(*ecount == 16); + CHECK(secp256k1_ring_deanonymize(none, &author_idx, NULL, sig_len, author_proof, pubkeys, 4) == 0); + CHECK(*ecount == 17); + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len - 1, author_proof, pubkeys, 4) == 0); + CHECK(*ecount == 17); + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len, NULL, pubkeys, 4) == 0); + CHECK(*ecount == 18); + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len, author_proof, NULL, 4) == 0); + CHECK(*ecount == 19); + + author_idx = 0; + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len, author_proof, pubkeys, 4) == 1); + CHECK(author_idx == 2); + CHECK(*ecount == 19); + + author_idx = 0; + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len, author_proof, pubkeys, 3) == 0); + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len, author_proof, pubkeys, 0) == 0); + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len, sec_key, pubkeys, 4) == 0); + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len, sec_key, pubkeys, 5) == 0); + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len, sec_key, pubkeys, SIZE_MAX / 32) == 0); + CHECK(secp256k1_ring_deanonymize(none, &author_idx, sig, sig_len, sec_key, pubkeys, SIZE_MAX) == 0); + CHECK(*ecount == 19); + CHECK(author_idx != 2); /* result is not specified, but it shouldn't be correct! */ + + secp256k1_scratch_space_destroy(ctx, scratch); +} + static void test_api(void) { secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); @@ -251,6 +374,8 @@ static void test_api(void) { test_pedersen_api(none, sign, vrfy, &ecount); ecount = 0; test_rangeproof_api(none, sign, vrfy, both, &ecount); + ecount = 0; + test_ring_api(none, sign, vrfy, both, &ecount); } secp256k1_context_destroy(none);