UltrafastSecp256k1/docs/FAQ.md
vano be528aef66 audit: add AUDIT_COVERAGE.md + ASCII cleanup + CT fixes
- Add comprehensive AUDIT_COVERAGE.md documenting all 46 audit modules
  across 8 sections with ~1M+ total assertions
- Pure ASCII cleanup: remove all Unicode from source/cmake/script files
  (box-drawing, arrows, Greek, emoji, BOM, Georgian in comments)
- CT fix: RISC-V is_zero_mask (seqz+neg inline asm)
- CT fix: ct_compare general path (snez)
- All 188 files updated for ASCII-only compliance (Section 17 rule)
- Verified: 46/46 audit PASS on X64, ARM64, RISC-V (QEMU + Mars HW)
- Verified: 24/24 CTest PASS on X64
2026-02-25 19:14:21 +04:00

10 KiB

FAQ & Common Pitfalls

UltrafastSecp256k1 -- Frequently Asked Questions


General

Q: What is UltrafastSecp256k1?

A high-performance secp256k1 elliptic curve cryptography library supporting ECDSA (RFC 6979), Schnorr (BIP-340), MuSig2, FROST threshold signatures, BIP-32 HD derivation, and 27-coin address generation. Targets x86-64, ARM64, RISC-V, WASM, CUDA, OpenCL, Metal, and embedded platforms.

Q: How does it compare to bitcoin-core/libsecp256k1?

UltrafastSecp256k1 focuses on breadth (multi-protocol, multi-platform, GPU backends) while libsecp256k1 focuses on depth (formally reviewed, battle-tested ECDSA/Schnorr for Bitcoin Core). Our differential test suite (test_cross_libsecp256k1) verifies matching results for 7,860+ operations.

Q: Is it production-ready?

The library has not yet undergone an independent external security audit. Core arithmetic is verified with 641K+ audit checks, but cryptographic libraries should be audited before production use with real funds. See AUDIT_SCOPE.md.

Q: What license is it under?

MIT License. See LICENSE in the repository root.


Building

Q: What compilers are supported?

Compiler Minimum Version Recommended
GCC 10+ 13+
Clang 14+ 18+
MSVC 19.29+ (VS 2019) 19.38+ (VS 2022)
Apple Clang 14+ 15+
Emscripten 3.1+ 3.1.50+

The CMake toolchain auto-detects Clang 19+ and prefers it when available.

Q: How do I build from source?

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failure

See BUILDING.md for full details.

Q: I get "ninja not found" on Windows

Install Ninja via pip install ninja or download from https://ninja-build.org/. Ensure it's on your PATH.

Q: Build fails with CUDA errors

  • Set CMAKE_CUDA_ARCHITECTURES to your GPU's compute capability (e.g., -DCMAKE_CUDA_ARCHITECTURES=86)
  • Do not add global -flto flags -- CUDA device-link breaks with host LTO
  • CUDA >= 11.0 required

Q: How do I build without GPU support?

GPU support is auto-detected. To explicitly disable:

cmake -S . -B build -DSECP256K1_BUILD_CUDA=OFF -DSECP256K1_BUILD_OPENCL=OFF

API Usage

Q: FAST vs CT -- which should I use?

Operation Namespace When to Use
Public key generation (from known pubkey) fast:: When input is public
Signature verification fast:: Always public-data operation
Signing (ECDSA/Schnorr) ct:: Always for signing with secret keys
Scalar multiplication with secret ct:: Always when scalar is sensitive
Batch operations fast:: When all inputs are public

Rule: Use ct:: whenever any input is a secret key or secret nonce. Use fast:: only when all inputs are public.

Q: What happens if I use FAST for signing?

Variable-time code may leak information about the private key through timing side channels. On shared hardware (cloud VMs, multitenant systems), an attacker may recover the key after observing enough signing operations.

Compile with SECP256K1_REQUIRE_CT=1 to get warnings when using FAST for signing.

Q: How do I use the C API?

#include <ufsecp/ufsecp.h>

ufsecp_context* ctx = ufsecp_context_create();

uint8_t privkey[32] = { /* your key */ };
uint8_t pubkey[33];
ufsecp_pubkey_create(ctx, privkey, pubkey);

uint8_t msg[32] = { /* message hash */ };
uint8_t sig[64];
ufsecp_ecdsa_sign(ctx, msg, privkey, sig);

int valid = ufsecp_ecdsa_verify(ctx, msg, sig, pubkey);

ufsecp_context_destroy(ctx);

See USER_GUIDE.md for complete examples.

Q: Is ufsecp_context thread-safe?

No. Each thread must create its own context. See THREAD_SAFETY.md.


Common Pitfalls

Pitfall 1: Sharing ufsecp_context across threads

// [FAIL] WRONG -- context is not thread-safe
ufsecp_context* ctx = ufsecp_context_create();
// Thread A: ufsecp_ecdsa_sign(ctx, ...);
// Thread B: ufsecp_ecdsa_verify(ctx, ...);  // DATA RACE

// [OK] CORRECT -- one context per thread
void worker(void) {
    ufsecp_context* ctx = ufsecp_context_create();
    ufsecp_ecdsa_sign(ctx, ...);
    ufsecp_context_destroy(ctx);
}

Pitfall 2: Using from_bytes for binary database I/O

// [FAIL] WRONG -- from_bytes is big-endian (for hex/test vectors)
auto fe = FieldElement::from_bytes(db_record);

// [OK] CORRECT -- from_limbs is little-endian (native x86/64)
auto fe = FieldElement::from_limbs(reinterpret_cast<const uint64_t*>(db_record));

from_limbs is the primary function for internal I/O. from_bytes is only for standard crypto vectors and hex strings.

Pitfall 3: Forgetting low-S normalization

ECDSA signatures must have s <= n/2 (BIP-62 / BIP-66). UltrafastSecp256k1 enforces this automatically in ecdsa_sign(), but if you construct signatures manually, you must check and normalize.

Pitfall 4: Using GPU for secret key operations

// [FAIL] WRONG -- GPU is variable-time, leaks timing information
cuda_scalar_mul(secret_key, G);

// [OK] CORRECT -- use CT layer on CPU for secret operations
auto pubkey = ct::scalar_mul(secret_key, G);

GPU backends are for public-data operations only (verification, public key batch generation, search).

Pitfall 5: Not zeroing secret keys after use

The library does not manage key lifetimes. After use, explicitly zero secret material:

// [OK] Zero sensitive buffers
std::memset(privkey, 0, 32);
std::memset(&signing_share, 0, sizeof(signing_share));

Consider using SecureZeroMemory (Windows) or explicit_bzero (Linux) to prevent compiler optimization from removing the zeroing.

Pitfall 6: Confusing x-only and compressed pubkeys

Format Size Functions
Compressed 33 bytes (02/03 ∥ x) ufsecp_pubkey_create, ufsecp_ecdsa_verify
X-only 32 bytes (x only) ufsecp_schnorr_verify, ufsecp_pubkey_xonly

Schnorr (BIP-340) uses x-only (32 bytes). ECDSA uses compressed (33 bytes). Don't mix them.

Pitfall 7: FROST nonce reuse

// [FAIL] WRONG -- reusing nonce for different messages
auto [nonce, commit] = frost_sign_nonce_gen(my_id, seed);
auto sig1 = frost_sign(key_pkg, nonce, msg1, commits);
auto sig2 = frost_sign(key_pkg, nonce, msg2, commits);  // KEY LEAK!

// [OK] CORRECT -- fresh nonce for each signing session
auto [nonce1, commit1] = frost_sign_nonce_gen(my_id, seed1);
auto sig1 = frost_sign(key_pkg, nonce1, msg1, commits1);
auto [nonce2, commit2] = frost_sign_nonce_gen(my_id, seed2);
auto sig2 = frost_sign(key_pkg, nonce2, msg2, commits2);

Pitfall 8: Assuming BIP-32 paths are always valid

// [FAIL] WRONG -- no error checking
auto keys = bip32_derive_path(master, user_input);

// [OK] CORRECT -- validate path first
auto parsed = bip32_parse_path(user_input);
if (!parsed.has_value()) {
    // Handle invalid path
    return error;
}
auto keys = bip32_derive_path(master, *parsed);

Pitfall 9: Static linking without UFSECP_API define (Windows)

On Windows, static linking against ufsecp_static requires defining UFSECP_API= to prevent __declspec(dllimport):

target_compile_definitions(my_app PRIVATE UFSECP_API=)
target_link_libraries(my_app PRIVATE ufsecp_static fastsecp256k1)

Pitfall 10: MuSig2 with attacker-controlled public keys

Always use the MuSig2 key aggregation function which includes key-prefixed hashing (KeyAgg coefficient). Never manually sum public keys -- this enables rogue-key attacks.

// [FAIL] WRONG -- naive key aggregation
auto agg_pk = pk1 + pk2;  // Rogue-key attack!

// [OK] CORRECT -- use MuSig2 key aggregation
auto agg = musig2_key_agg({pk1, pk2});

Performance

Q: What's the throughput for ECDSA verification?

Platform-dependent. Typical on modern x86-64 (single-core):

  • ECDSA verify: ~15,000-25,000 ops/sec
  • Schnorr verify: ~20,000-30,000 ops/sec
  • Key generation: ~30,000-50,000 ops/sec

See docs/BENCHMARKS.md for detailed numbers.

Q: How fast is the GPU batch mode?

CUDA on RTX 3090: millions of point operations per second. Performance depends on batch size, memory bandwidth, and compute capability. Set threads_per_batch in config.json for optimal throughput.

Q: How do I run benchmarks?

cmake -S . -B build -DSECP256K1_BUILD_BENCH=ON
cmake --build build -j --target bench_secp256k1
./build/bench_secp256k1

Protocols

Q: Is the MuSig2 implementation compatible with BIP-327?

The protocol structure matches BIP-327, but key format differs: we use x-only 32-byte pubkeys throughout, while BIP-327 specifies 33-byte compressed for hash inputs. This means signatures are not bitwise-identical to other BIP-327 implementations when producing the aggregate. Final BIP-340 verification remains compatible.

Q: Can I use FROST for Bitcoin multisig?

FROST produces standard BIP-340 Schnorr signatures, so the final signature is indistinguishable from a single-signer signature. However, FROST is experimental and should not be used for production Bitcoin transactions without additional review.

Q: What threshold configurations does FROST support?

Any t-of-n where 2 <= t <= n. Tested with:

  • 2-of-3
  • 3-of-5
  • Arbitrary t and n via API

Troubleshooting

Q: Tests fail with "stack overflow"

Some tests use deep recursion or large stack allocations. On Windows, link with /STACK:8388608 (8MB). CMake does this automatically for test targets.

Q: "Unresolved external symbol" when linking ufsecp

Ensure you link both libraries and define UFSECP_API=:

target_compile_definitions(my_app PRIVATE UFSECP_API=)
target_link_libraries(my_app PRIVATE ufsecp_static fastsecp256k1)

Q: dudect test fails intermittently

dudect is statistical. A single borderline pass/fail is normal. The CI uses conservative thresholds (t=25 for smoke, t=4.5 for nightly). If it fails consistently, there may be a real timing leak -- investigate with the full nightly run.

Q: How do I report a bug?


Last updated: 2026-02-24