
Wallet application boundaries, participation in ECDSA transaction signing and private key generation: analysis of documentation, source code, and project history. Field: Applied cryptography / Bitcoin. Method: source code audit + standards analysis.
Annotation
Libgcrypt is a general-purpose GNU cryptographic library, but is not a component of the Bitcoin protocol. It supports SHA-256, RIPEMD-160, HMAC, PBKDF2, CSPRNG, MPI/ECC, ECDSA, and the named curve secp256k1, so a single wallet can build a significant portion of the cryptographic circuit on it. [1] However, Bitcoin Core does not use Libgcrypt for key generation, signing, or consensus signature verification: these operations are performed by a specialized libsecp256k1. [11] [12]
Direct widespread use is confirmed in the experimental C project wall_e_t . Historical limited use was found in Bitcoin Toolkit 2.0.x , where Libgcrypt supported SHA-256/RIPEMD-160 but not ECDSA; the dependency was removed from the code in 2023. The claim of widespread adoption of Libgcrypt in popular production wallets is not confirmed.
Short answer
In Bitcoin, it can be used by the implementation, but the protocol does not require it.
ECDSA technically Yes: Libgcrypt can do secp256k1/ECDSA and gcry_pk_sign.
In Bitcoin Core No: Core uses libsecp256k1.
Key generation Yes in some wallets; no as a required part of Bitcoin .
- Subject and methodology
- Libgcrypt Capabilities
- Bitcoin’s Cryptographic Layers
- What projects are used?
- Bitcoin Core
- ECDSA signing
- Private key generation
- Taproot and Schnorr
- Security audit
- Recommendations
- Conclusions
- Sources
Subject and methodology
The study distinguishes four layers: (1) protocol and consensus rules; (2) node implementation; (3) wallet implementation; (4) indirect system dependencies. Installing libgcrypt on Linux, using GnuPG to test the release, or being present guile-gcrypt in a reproducible build environment does not mean that this library processes Bitcoin private keys.
For each open source project, build files, includes <gcrypt.h>, calls gcry_*, crypto call graph, and Git history were checked. The current snapshot was separated from older releases. A negative result is interpreted with caution: it confirms the absence of a direct dependency in the checked open source snapshot, but does not rule out closed source applications, forks, or older versions.
Proof criterion. The project name in the README is not sufficient. Reliable proof requires direct linking and API calls in the code that actually performs key generation, hashing, signing, or wallet encryption.
Libgcrypt Capabilities
The official manual calls Libgcrypt a GNU library of cryptographic building blocks with an extensible API. [1] The public-key module supports ECDSA/ECDH/EdDSA, and the list of curves includes secp256k1; the flag rfc6979 is for the deterministic parameter k in DSA/ECDSA. [2]
Keys and signatures are passed as S-expressions. gcry_pk_sign returns the ECDSA components r and s, and gcry_pk_verify verifies them. [2] Additionally, hashing, HMAC, PBKDF2, CSPRNG, multi-precision integers, symmetric encryption, and secure memory are available. [3]
Functional sufficiency does not equal protocol compatibility. Libgcrypt does not build Bitcoin sighash, serialize transactions, execute Bitcoin Script, or implement BIP-66/BIP-340 as a complete Bitcoin stack. These responsibilities lie with the application.
Bitcoin Layers
| Layer | Operation | Libgcrypt Capability | Limitation |
|---|---|---|---|
| Entropy | Random bytes | Yes | Correct quality level and error checking are required. |
| Private key | Scalar 1 ≤ d < n | Yes | Unbiased display/rejection sampling is required. |
| Public key | Q = dG on secp256k1 | Yes | Bitcoin-compatible coding is required |
| Addresses | SHA-256, RIPEMD-160, HMAC | Yes | Base58Check/Bech32 implements the application |
| HD wallet | BIP-39/BIP-32 primitives | Yes | The application implements standards and derivation path |
| Legacy/SegWit v0 | ECDSA sign/verify | Yes | Need sighash, DER, low-S, witness/script |
| Taproot | BIP-340 Schnorr | Not confirmed by a ready API | Generic ECDSA/EdDSA does not replace BIP-340 |
| Storage | KDF + AEAD/AES | Yes | This is a wallet policy, not a consensus. |
| Consensus | Script, sighash rules, soft forks | There is no ready stack | A Bitcoin implementation is needed |
Nodes don’t know which library created the signature. They accept or reject the byte transaction according to Bitcoin consensus and policy. Therefore, the same valid ECDSA signature can be calculated by Libgcrypt, OpenSSL, a hardware wallet, or libsecp256k1.
Projects and wallets
wall_e_t: direct example
In the currently examined commit of the Rubberazer/wall_e_te63602b project , the Makefile links to , and the code includes . This is an experimental GNU/Linux C wallet, not a widely used audited production wallet.-lgcrypt<gcrypt.h>
gcry_random_bytes(..., GCRY_STRONG_RANDOM)— entropy mnemonics;gcry_md_hash_buffer(SHA256)— BIP-39 checksum;gcry_kdf_derive(PBKDF2, SHA512, 2048)— mnemonic-to-seed;- HMAC-SHA512 и MPI modulo
n— master/child key derivation; gcry_mpi_ec_new,gcry_pubkey_get_sexp— public key from private;gcry_pk_sign,gcry_pk_verify— ECDSA on secp256k1;- AES-256-GCM-SIV, PBKDF2, secure allocations — protection of wallet records.
Therefore, in wall_e_t, Libgcrypt is directly linked to both private key origins and ECDSA. However, sign_ECDSA it is called from crypto-test; no call to it was detected in the production path for constructing and signing Bitcoin input. The presence of an ECDSA function, but an incomplete Bitcoin transaction signer, is proven.
Bitcoin Toolkit: A Historical Example
The Git history of bartobri/bitcoin-toolkit shows that releases 2.0.1–2.0.2 linked -lgcrypt and called gcry_md_* for SHA-256/RIPEMD-160. This handled hashing, Base58Check, and addresses, but not ECDSA or private key generation.
37f84b7 The February 14, 2023, commit removed gcry_* and -lgcrypt. The README for versions 3.x also mentioned the library—an example of why documentation needs to be verified with build graph. The current version, 4.0, uses libsecp256k1, and gets randomness via getentropy(2) or /dev/urandom.
Summary matrix
| Project | Slice | Libgcrypt | ECDSA through it | Keygen through it | Qualification |
|---|---|---|---|---|---|
| wall_e_t | e63602b | Yes | There is a function | Yes, via entropy/HD pipeline | Experimental wallet |
| Bitcoin Toolkit | 2.0.x | Historical | No | No | SHA-256/RIPEMD-160 only |
| Bitcoin Toolkit | 4.0 | No | libsecp256k1 | OS CSPRNG | CLI toolkit, не stored wallet |
| Bitcoin Core | current | No | libsecp256k1 | Core RNG + validation | Reference node/wallet |
| Electrum | current | Not confirmed | libsecp256k1/electrum-ecc | No Libgcrypt dependencies | A popular SPV wallet |
| Wasabi / btcwallet / Bitcoin Wallet Android | tested open cuts | Not confirmed | Other stacks | Other stacks | There is no reason to include it in the list |
Bitcoin Core
The modern Bitcoin Core includes headers secp256k1.h, calls secp256k1_ecdsa_sign, secp256k1_ecdsa_verifyand secp256k1_keypair_create the Schnorr API. The new secret key receives random bytes through a custom RNG pipeline and verifies it as a scalar secp256k1. The API libsecp256k1 considers the secret valid when 0 < d < n and strict ECDSA verification accepts lower-S. [12]
Historically, Bitcoin Core 0.10 switched signing from OpenSSL to libsecp256k1 constant-time and deterministic signatures; version 0.12 switched ECDSA verification to it, reducing the attack surface and speeding up verification. [10] [11]
Why is libgcrypt listed in the Core tree? The table guile-gcrypt → libgcrypt-dev in the Guix documentation refers to reproducible build tools. It’s not a runtime crypto wallet backend or a consensus dependency.
Electrum also explicitly lists libsecp256k1 ECC as a dependency and a package cryptography for symmetric ciphers; it does not acknowledge Libgcrypt in the signing path. [13]
ECDSA signing
Mathematical scheme
For a private scalar d, a message hash z, a base point G, its order n , and a one-time secret k:
Q = dG R = kG r = x(R) mod n s = k⁻¹(z + r·d) mod n signature = (r, s)
The secp256k1 curve is standardized by SEC 2 as a 256-bit Koblitz curve over a prime field; parameters include p, a=0, b=7, G, n and the cofactor 1. [4]
Bitcoin-specific context
In Bitcoin, ECDSA signs not a “transaction as a file,” but a special digest for a specific input. Its preimage and covered fields depend on the script type, witness version, and SIGHASH flags. The signature is then placed in scriptSig the witness and verified against the public key and the conditions of the spent output. [7]
Transaction + prevout→Sighash preimage→32-byte digest→ECDSA(digest,d)→low-S + strict DER→scriptSig / witness
The role of Libgcrypt
Libgcrypt can perform the central mathematical step: deriving (r,s) via gcry_pk_sign for a secp256k1 key. The documented RFC 6979 mode is specified by a flag rfc6979 and a hash algorithm. [2] RFC 6979 makes the nonce a deterministic function of the private key and message hash, maintaining compatibility with the standard ECDSA verifier. [5]
But the library does not exempt the calling code from Bitcoin rules. BIP-66 requires strict DER for ECDSA signatures in the corresponding opcodes. [8] Lower-S eliminates substitutability (r,s) for (r,n−s); specialized libsecp256k1 has explicit normalization and lower-S verification. [9] [12]
What the adapter must do
- Build exact legacy, SegWit v0 or other applicable sighash.
- Pass the digest itself, and don’t re-hash/mis-hash it.
- Provide RFC 6979 or other secure nonce mechanism.
- Normalize
s = min(s, n−s). - Serialize
r,sto minimal positive strict DER. - Add Bitcoin SIGHASH byte where required.
- Generate a correct
scriptSig/witness. - For P2TR use BIP-340, not ECDSA.
Key generation
A Bitcoin private key is an integer:
d ∈ {1, 2, …, n − 1} Q = dG
NIST describes the ECDSA private key as being d in the range [1,n−1], derived from the DRBG, and the public key as being Q=[d]G. [6] Therefore, Libgcrypt may be directly related to the keygen if the application uses its CSPRNG and ECC arithmetic. However, neither the WIF format, nor the address, nor the descriptor are the private key itself.
Two typical paths
Independent key: The CSPRNG produces sufficient entropy; the application receives an unbiased scalar and rejects 0 or a value ≥ n. HD wallet: The entropy is converted to a mnemonic and seed, then BIP-32 deterministically creates a key tree.
CSPRNG entropy→BIP-39 mnemonic→PBKDF2-HMAC-SHA512 seed→BIP-32 master key→child d
BIP-39 encodes 128–256 bits of entropy by appending the first ENT/32 bits of the SHA-256 checksum and breaking the result into 11-bit word indices. [15] BIP-32 defines a tree of normal and hardened keys from a single seed. [14] Libgcrypt is capable of handling the primitive layer—RNG, SHA-256, PBKDF2-HMAC-SHA512, HMAC-SHA512, and ECC—but BIP compliance is ensured by the wallet code.
Don’t confuse d, k, and seed
| Object | Purpose | Lifespan | Relationship with RFC 6979 |
|---|---|---|---|
d | Bitcoin private key | Long-term | Enter RFC 6979 |
k | ECDSA secret nonce | One signature | It is precisely this that is defined by RFC 6979. |
| seed | HD Wallet Root Secret | Long-term | Not being created RFC 6979 |
| chain code | BIP-32 derivation parameter | На extended key | Not an ECDSA nonce |
RFC 6979 explicitly states that the key-pair generation does not change. [5] Therefore, the presence of the flag in Libgcrypt rfc6979 applies to signing, not to the creation of a new wallet private key.
Taproot and Schnorr
After Taproot, it cannot be claimed that every Bitcoin signature is ECDSA. BIP-340 defines 64-byte Schnorr signatures over the same secp256k1. [16] P2TR uses x-only public keys, tagged hashes, and separate signature message rules.
Libgcrypt documentation confirms ECDSA and EdDSA, but EdDSA is not BIP-340. Generic ECC arithmetic theoretically allows for a native implementation of BIP-340, but this is not equivalent to a ready-made audited BIP-340 API. Therefore, gcry_pk_sign Taproot spend does not sign in ECDSA mode.
Security audit
wall_e_t: risks found
A static analysis of the function sign_ECDSA shows why the presence of a cryptographic backend does not necessarily mean the presence of a secure Bitcoin signer.
| Observation | Risk | Rating |
|---|---|---|
It is used (flags raw), not explicit rfc6979 | Nonce policy depends on the backend/RNG and is not fixed by the interface. | High |
| No lower-S normalization | Bitcoin policy/compatibility, malleability | High |
The homemade DER takes sign-padding into account only for r | Non-canonical/incorrect signatures for some values r,s | Critical |
Condition r[0] > 0x80, not a check of whether the high bit is set | Boundary error at 0x80 | High |
| One-shot SHA-256 of a random buffer | Non-universal Bitcoin sighash | Critical |
| No SIGHASH byte | Incomplete Bitcoin signature serialization | Critical |
| The function was found only in a test call. | Production transaction signing not confirmed | Average |
These are the results of a static analysis of a specific slice, not a claim of compromised funds. Before deployment with real assets, patches, official test vectors, differential testing against Bitcoin Core, and an independent audit are required.
The wall_e_tinternal Libgcrypt function _gcry_ecc_ecdsa_sign()is applied indirectly when sign_ECDSA().
The code itself wall_e_tdoesn’t call it _gcry_ecc_ecdsa_sign()directly—it’s an internal Libgcrypt symbol, not intended for external use. Instead, wall_e_tit generates a private ECC key on the curve secp256k1as an S-expression and calls the public API:
cgcry_pk_sign(&s_sign, s_data, s_key);
For such an ECC key, Libgcrypt directs execution along the internal chain:
text:wall_e_t::sign_ECDSA()
→ gcry_pk_sign()
→ внутренний ECC/ECDSA dispatcher Libgcrypt
→ _gcry_ecc_ecdsa_sign() [cipher/ecc-ecdsa.c]
→ _gcry_mpi_ec_mul_point()
Therefore, the precise professional wording is: wall_e_t it doesn’t have a direct source-level call _gcry_ecc_ecdsa_sign , but its ECDSA signing via [libgcrypt] gcry_pk_sign()results in this function being executed within the dynamically linked Libgcrypt. The repository explicitly lists Libgcrypt as a fundamental dependency and uses it for cryptographic operations. [gnupg+2]
What does the function do?
_gcry_ecc_ecdsa_sign()— the internal implementation of ECDSA signature calculation. For the Bitcoin secp256k1 curve, it is involved in the calculation of:
Q=dG,R=kG,r=x(R)modn,
s=k−1(z+rd)modn.
Here:
- — private scalar;
- — the corresponding public key;
- — one-time ECDSA nonce;
- — hash of the message being signed;
- — base point order secp256k1;
- — the final ECDSA signature.
Libgcrypt is documented to support ECDSA secp256k1and RFC 6979 deterministic nonce mode . gnupg
Important limitation of wall_e_t
The presence of a challenge _gcry_ecc_ecdsa_sign() does not prove that it wall_e_talready correctly signs real Bitcoin transactions in production mode.
Its function sign_ECDSA()has been found to have significant limitations:
| Bitcoin signature component | Condition inwall_e_t::sign_ECDSA() | Risk |
|---|---|---|
| ECDSA on secp256k1 | Yes, throughgcry_pk_sign() | The basic cryptographic step works |
| Bitcoin sighash | Not confirmed | The function simply applies a single SHA-256 to the input buffer. |
| RFC 6979 | Not explicitly specified | Used (flags raw), not documented(flags rfc6979) |
| low-S normalization | Not found | Possible incompatibility with Bitcoin policy/SegWit rules |
| BIP-66 strict DER | Not fully implemented | DER encoding is manual and incomplete |
Padding fors | Not processed similarlyr | Possible invalid ASN.1 INTEGER |
| SIGHASH-byte | Not added | Incomplete serialization of Bitcoin ECDSA signature |
| Integration into transaction path | Not confirmed | README marks transaction/transfer as not ready yet |
BIP-66 requires strict DER representation of ECDSA signatures, and BIP-146 requires lower-S for relevant scenarios. The repository itself warns that the project is in early stages and is not intended for serious use; transaction and transfer operations are marked as not yet ready.
Other Bitcoin wallets
Confirmed answer
For any wallet that uses Libgcrypt for ECDSA signatures on secp256k1 gcry_pk_sign(), _gcry_ecc_ecdsa_sign()this will be called indirectly. This is an internal feature of Libgcrypt, not a unique feature wall_e_t.
But among the widely known Bitcoin wallets, there are few publicly confirmed examples:
| Project | Uses _gcry_ecc_ecdsa_sign()via Libgcrypt? | Justification |
|---|---|---|
wall_e_t | Yes, indirectly | sign_ECDSA()calls gcry_pk_sign()with(curve "secp256k1") |
| Bitcoin Core | No | Uses libsecp256k1ECDSA for signing and verification |
| Electrum | Not confirmed; no dependencies | For ECC specified libsecp256k1/electrum-ecc |
| Bitcoin Toolkit 2.0.x | No for ECDSA | Libgcrypt has been used historically for SHA-256 and RIPEMD-160 |
| Bitcoin Toolkit 4.0 | No | Useslibsecp256k1 |
| Wasabi, btcwallet, Bitcoin Wallet Android | Not confirmed | There is no reliable evidence that they use Libgcrypt ECDSA backend. |
Bitcoin Core historically replaced OpenSSL with [unclear] libsecp256k1: first for signing, then for validation. The goal was constant-time operations, a smaller attack surface, and faster signature verification. Electrum also specifies [unclear] libsecp256k1for elliptic operations in its dependencies, rather than Libgcrypt.cryptobook.nakov+4.
Safety_gcry_ecc_ecdsa_sign
This internal function is also important from a cryptanalytic perspective. It has been addressed, in particular:
- CVE-2018-0495 / ROHNP : Cache side-channel in ECDSA signing on older Libgcrypt, potentially allowing private key recovery in a local or cross-VM scenario; fix related to blinding in
_gcry_ecc_ecdsa_sign().security-tracker.debian+1 - CVE-2019-13627 : Changes to nonce and scalar multiplication handling to reduce timing leaks. openembedded
Therefore, the outdated Libgcrypt cannot be used for research or application builds wall_e_t. However, even the current library does not fix the Bitcoin framework’s bugs: sighash, DER, low-S, nonce policy, serialization, and Script/witness must be implemented separately and verified with test vectors.
Practical test
For your own debug build, you can confirm the dynamic path:
bash:# Проверить наличие runtime-зависимости
ldd ./wall_e_t | grep -i gcrypt
# Найти публичную функцию
nm -D /usr/lib/*/libgcrypt.so* 2>/dev/null | grep ' gcry_pk_sign$'
# При наличии debug-symbols Libgcrypt
gdb --args ./test_crypto
B GDB:
textset breakpoint pending on
break _gcry_ecc_ecdsa_sign
run
bt
If an internal symbol is hidden or stripped in a release package, the breakpoint may not be set. This does not invalidate the call; reliable tracing requires a debug build of Libgcrypt or the debug symbols package installed.
Systemic threats
- Weak entropy: bias or seed duplication compromises the entire key tree.
- Nonce failure: retry, bias, or leakage
kallows recoveryd. - Side channels: secure memory does not guarantee constant-time scalar arithmetic.
- Fault injection: For sensitive devices, hedged deterministic signatures with additional randomness are useful. [17]
- Encoding errors: DER, MPI, endianness and leading zeros often create incompatibilities.
- Protocol confusion: generic
sign(data)cannot be substituted for Bitcoin sighash layer.
Recommendations
For a new wallet
- Use OS CSPRNG or hardware TRNG to seed entropy.
- Use a mature BIP-32/BIP-39/descriptor implementation with official vectors.
- Use
libsecp256k1for key validation, public keys, ECDSA and BIP-340. - Separate the crypto primitive layer and the Bitcoin transaction/sighash/script layer.
- Store keys using AEAD and a modern password-based KDF; if possible, isolate them in a hardware wallet/HSM.
- Добавить fuzzing, sanitizers, differential tests, regtest integration и reproducible builds.
If Libgcrypt is required
- Explicitly set RFC 6979 or documented hedged nonce mode.
- Normalize lower-S after signature.
- Do not write your own DER encoder without comprehensive BIP-66 tests.
- Verify each generated ECDSA signature independently
libsecp256k1in tests. - Check zero/out-of-range private scalars and all API return codes.
- Do not announce P2TR support without a definitive BIP-340 implementation and official test vectors.
- Conduct an audit of S-expression/MPI conversions, secure-memory lifetime, and error paths.
Conclusions
- Libgcrypt is not part of the Bitcoin protocol or consensus. Neither transactions nor nodes contain the library’s identifier.
- Bitcoin Core does not use it for keygen/ECDSA. The main backend is
libsecp256k1. - There is a technical connection to ECDSA. Libgcrypt supports secp256k1, ECDSA, and RFC6979-oriented mode; a wallet can sign using it if it correctly implements the Bitcoin bindings.
- The connection to private key generation is conditional. It’s direct if the wallet uses Libgcrypt CSPRNG/KDF/ECC, like wall_e_t; other wallets may not.
- Confirmed examples are rare. wall_e_t is a current experimental example; Bitcoin Toolkit is a historical hash-only example.
- Generic ECDSA is not sufficient. Sighash, strict DER, low-S, SIGHASH byte, and a valid script/witness are required; Taproot requires BIP-340 Schnorr.
The final formula: “The Bitcoin protocol uses Libgcrypt” is incorrect. “An individual Bitcoin wallet can use Libgcrypt for entropy, HD derivation, and ECDSA” is correct. “Any Libgcrypt signature is automatically a valid Bitcoin signature” is incorrect.
Sources
- GNU Privacy Guard. The Libgcrypt Reference Manual.
- GNU Privacy Guard. Cryptographic Functions: ECDSA, rfc6979, gcry_pk_sign.
- GNU Privacy Guard. Key Derivation: gcry_kdf_derive, PBKDF2.
- SECG. SEC 2, Recommended Elliptic Curve Domain Parameters, secp256k1.
- T. Pornin. RFC 6979: Deterministic Usage of DSA and ECDSA, 2013.
- NIST. FIPS 186-5 Digital Signature Standard, 2023.
- Bitcoin Developer Documentation. Transactions.
- P. Wuille. BIP 66: Strict DER signatures.
- Bitcoin Core libsecp256k1. ECDSA lower-S normalization documentation.
- Bitcoin Core 0.10 release notes. Improved signing security / libsecp256k1.
- Bitcoin Core. Bitcoin Core 0.12.0: libsecp256k1 verification.
- Bitcoin Core. libsecp256k1 API: secret-key validity and ECDSA verification.
- Electrum. Repository and dependency documentation.
- P. Wuille. BIP 32: Hierarchical Deterministic Wallets.
- M. Palatinus et al. BIP 39: Mnemonic code for generating deterministic keys.
- P. Wuille, J. Nick, T. Ruffing. BIP 340: Schnorr Signatures for secp256k1.
- IETF CFRG draft. Deterministic signatures with additional randomness.
- Rubberazer. wall_e_t source repository, audited commit
e63602bb88a9fce65e5af522255ad0f5b8749063. - Brian Barto. Bitcoin Toolkit source repository; commit removing Libgcrypt.
- Bitcoin Core. Source repository, audited commit
f3fec67c3eeb27d5be1bc9d57ca737b74fcd762d.
