packages feed

secp256k1-haskell 2.0.0 → 2.0.1

raw patch · 10 files changed

+624/−1 lines, 10 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -4,6 +4,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [2.0.1] - 2026-11-07++### Changed++- Documentation: the README explains the purpose and design of the bindings,+  with installation steps for every platform, a tutorial, a usage guide, the+  differences from Bitcoin Core and security notes. Every module, including+  the foreign imports, has Haddock, and the main module and key functions say+  why they work the way they do.+- CI builds, tests and documents the package with Stack, against the+  libsecp256k1 of Ubuntu and with `stack --nix`.+- Build with Stackage nightly-2026-11-07.+ ## [2.0.0] - 2026-11-03  This release follows a review of the bindings, including the changes of
+ README.md view
@@ -0,0 +1,453 @@+# secp256k1-haskell++[![Hackage](https://img.shields.io/hackage/v/secp256k1-haskell.svg)](https://hackage.haskell.org/package/secp256k1-haskell)+[![CI](https://github.com/jprupp/secp256k1-haskell/actions/workflows/ci.yml/badge.svg)](https://github.com/jprupp/secp256k1-haskell/actions/workflows/ci.yml)+[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jprupp/secp256k1-haskell/blob/master/LICENSE)++Haskell bindings to [libsecp256k1](https://github.com/bitcoin-core/secp256k1),+the library that Bitcoin Core uses for elliptic curve cryptography on the+secp256k1 curve. It covers ECDSA signatures, BIP-340 Schnorr signatures,+BIP-341 Taproot tweaks, key tweaking for BIP-32 derivation, and ECDSA public+key recovery for Bitcoin signed messages.++- [API documentation](https://hackage.haskell.org/package/secp256k1-haskell)+- [Changelog](https://github.com/jprupp/secp256k1-haskell/blob/master/CHANGELOG.md)+- [Report a bug](https://github.com/jprupp/secp256k1-haskell/issues)++## Contents++- [Why secp256k1-haskell](#why-secp256k1-haskell)+- [Design principles](#design-principles)+- [Features](#features)+- [Installation](#installation)+- [Quick start](#quick-start)+- [Tutorial: sign, verify and serialize](#tutorial-sign-verify-and-serialize)+- [Concepts](#concepts)+- [Usage guide](#usage-guide)+- [Differences from Bitcoin Core](#differences-from-bitcoin-core)+- [Module overview](#module-overview)+- [Security notes](#security-notes)+- [Development](#development)+- [Related projects](#related-projects)+- [License](#license)++## Why secp256k1-haskell++Elliptic curve cryptography is easy to write and very hard to get right. A+signing routine whose running time depends on the secret key leaks it through+timing. A nonce that repeats once reveals the key outright. An edge case in+point arithmetic accepts a forged signature. These are not theoretical: each has+cost real users real money.++libsecp256k1 is the answer the Bitcoin community converged on. It is+constant-time wherever secrets are involved, blinds secret key operations+against side channels, is tested exhaustively, and has been reviewed for over a+decade. It is what Bitcoin Core runs.++This package lets a Haskell program use that library instead of reimplementing+it, and wraps it in types that make the usual mistakes hard to make. It is the+cryptographic foundation of [haskoin-core](https://github.com/jprupp/haskoin-core)+and, through it, of the rest of the Haskoin libraries.++## Design principles++- **Valid by construction.** Every key, signature, hash and tweak goes through+  a smart constructor or an import function that checks it. A `PubKey` is+  always a point on the curve; a `Sig` always has both components in range.+  Invalid input is refused where it enters the program, not deep inside a+  computation.+- **Never abort the process.** By default libsecp256k1 calls `abort()` on an+  illegal argument, which kills the whole Haskell program and cannot be caught.+  Every context this package creates installs a callback that makes the C+  function fail instead, so a bad argument becomes `Nothing`, `False` or an+  exception.+- **Pure where the mathematics is pure.** Signing with deterministic nonces,+  verifying, deriving and tweaking keys are pure functions. Only operations+  that need fresh randomness run in `IO`.+- **Standard encodings at the edges.** `Show`, `Read` and `IsString` use SEC1,+  compact or plain hex, never libsecp256k1's internal memory layout, which+  differs between platforms and library versions.+- **Explicit about Bitcoin Core.** Where Bitcoin Core goes beyond the+  standards, such as grinding signatures to a low R value or accepting high-S+  signatures from old blocks, this package offers the behaviour as a separate,+  documented function instead of silently picking one.+- **Thin.** Hashing, address encoding, BIP-32 paths and transaction formats+  belong in higher-level libraries such as haskoin-core.++## Features++| Area                 | What you get                                                                                   |+| -------------------- | ---------------------------------------------------------------------------------------------- |+| ECDSA                | Deterministic RFC 6979 signing, Bitcoin Core's low-R grinding, verification, lower-S normalization |+| Encodings            | SEC1 public keys (33 or 65 bytes), DER and compact signatures, hex `Show`/`Read`/`IsString`    |+| Schnorr (BIP-340)    | Signing (deterministic, with given or fresh auxiliary randomness), verification, x-only keys    |+| Taproot (BIP-341)    | Tweaking an x-only public key and its secret key                                               |+| Key arithmetic       | Add or multiply secret and public keys by a tweak, negate, combine public keys                 |+| Recovery             | Recoverable ECDSA signatures and public key recovery, as Bitcoin signed messages use           |+| Safety               | Validated values, no aborts, length checks before every C call, contexts shared across threads |+| Testing              | QuickCheck `Arbitrary` instances for secret keys, hashes and tweaks                            |++### What is not included++- No hashing. Pass a 32-byte hash you computed with a hash library, such as+  `cryptohash-sha256` or haskoin-core.+- No ECDH, MuSig2 or adaptor signatures, although libsecp256k1 or its+  forks offer some of them.+- No secure erasure of secret keys from memory. See [Security notes](#security-notes).++## Installation++### System library++The package needs [libsecp256k1](https://github.com/bitcoin-core/secp256k1)+**0.2.0 or later** and `pkg-config` to find it. Public key recovery also needs+the library's recovery module, which the packages below include.++```sh+# Debian and Ubuntu (24.04 ships 0.2.0)+sudo apt-get install libsecp256k1-dev pkg-config++# Fedora+sudo dnf install libsecp256k1-devel pkgconf++# macOS with Homebrew+brew install secp256k1 pkg-config++# Nix+nix-shell -p secp256k1 pkg-config+```++Check what you have:++```sh+pkg-config --modversion libsecp256k1   # must print 0.2.0 or later+```++Older distributions, such as Debian 11, ship an earlier version. Build the+library from source then:++```sh+git clone https://github.com/bitcoin-core/secp256k1.git+cd secp256k1+./autogen.sh+./configure --enable-module-recovery+make && sudo make install+```++If your library was built without the recovery module, build this package with+the `recovery` flag off: `stack build --flag secp256k1-haskell:-recovery`, or+`cabal build -f -recovery`. Everything except `Crypto.Secp256k1.Recovery` still+works.++### Step by step with Stack++1. Install [Stack](https://docs.haskellstack.org/) and the library above.+2. Create a project:++   ```sh+   stack new sig-demo simple+   cd sig-demo+   ```++3. Add `secp256k1-haskell` and `bytestring` to `build-depends` in+   `sig-demo.cabal`.+4. If your resolver does not include the package, add it to `stack.yaml`:++   ```yaml+   extra-deps:+     - secp256k1-haskell-2.0.1+     - base16-2.0+   ```++5. Put the [quick start](#quick-start) program in `src/Main.hs` and run it with+   `stack run`. It prints `True` three times.++### With Cabal++```cabal+build-depends:+    base+  , bytestring+  , secp256k1-haskell+```++Run `cabal update` first, then `cabal build`.++## Quick start++```haskell+{-# LANGUAGE OverloadedStrings #-}++import Crypto.Secp256k1++main :: IO ()+main = do+  ctx <- createContext++  -- Hex literals go through IsString, which validates them.+  let key = "c7e5a2b4f4c5d7e8a9b0c1d2e3f405162738495a6b7c8d9e0f1a2b3c4d5e6f70" :: SecKey+      hash = "4b688df40bcedbe641ddb16ff0a1842d9c67ea1c3bf63f3e0471baa664531d1a" :: Msg+      pub = derivePubKey ctx key++  -- ECDSA+  let s = signMsg ctx key hash+  print (verifySig ctx pub s hash)++  -- BIP-340 Schnorr, with fresh auxiliary randomness+  schnorr <- signSchnorrIO ctx key hash+  print (verifySchnorr ctx pub schnorr hash)++  -- Round trip through the standard encodings+  let Just pub' = importPubKey ctx (exportPubKey ctx True pub)+  print (pub == pub')+```++## Tutorial: sign, verify and serialize++This walks through what a wallet or a protocol implementation typically does,+and why each step looks the way it does.++### 1. Create one context and share it++```haskell+ctx <- createContext+```++A context holds precomputed tables and random blinding values. Creating one is+relatively expensive, and one context can be used by every thread for the life+of the program. `createContext` randomizes it once, which is what libsecp256k1+recommends.++### 2. Get keys from bytes++```haskell+case secKey keyBytes of+  Nothing -> fail "not a valid secret key"+  Just key -> ...+```++`secKey` refuses 0 and numbers not below the curve order, so a `SecKey` is+always usable. In practice, keys come from a wallet: from a BIP-39 mnemonic and+BIP-32 derivation in haskoin-core, or from 32 bytes of the operating system's+secure randomness (`getEntropy 32` from the `entropy` package).++### 3. Hash, then sign++The library signs 32-byte hashes, not messages. Which hash to use is part of+the protocol: Bitcoin transactions use double SHA-256 of a specific+serialization, BIP-340 uses tagged hashes. Build the `Msg` from your hash:++```haskell+let Just m = msg hashBytes+    s = signMsgLowR ctx key m -- or signMsg for plain RFC 6979+```++Signing is deterministic: the same key and hash give the same signature, so+there is no random number generator to get wrong.++### 4. Serialize for the wire++Never send the `get` bytes of a `PubKey` or `Sig`: they are libsecp256k1's+internal layout. Use the standard encodings:++```haskell+let pubBytes = exportPubKey ctx True pub -- 33-byte compressed SEC1+    derBytes = exportSig ctx s -- DER, as in Bitcoin transactions+    compactBytes = exportCompactSig ctx s -- 64 bytes r || s+```++### 5. Parse and verify what you receive++```haskell+verified = fromMaybe False $ do+  pub <- importPubKey ctx pubBytes+  s <- importSig ctx derBytes+  m <- msg hashBytes+  return (verifySig ctx pub (normalizeSig ctx s) m)+```++Every import returns `Maybe`, so malformed input is handled in one place.+`normalizeSig` is there because `verifySig` only accepts lower-S signatures;+apply it when the signature may come from software that does not produce them,+such as old Bitcoin transactions. Leave it out when your protocol requires+lower-S, as Bitcoin's relay rules do.++### 6. Move to Schnorr where the protocol allows++```haskell+sig <- signSchnorrIO ctx key m+let ok = verifySchnorr ctx pub sig m+    xonly = exportXOnlyPubKey ctx pub -- 32 bytes, as BIP-340 publishes keys+```++Schnorr signatures are always 64 bytes, are not malleable, and are what Taproot+uses. `signSchnorrIO` mixes fresh randomness into the nonce, as BIP-340+recommends, to protect against fault attacks.++## Concepts++### Values++| Type         | What it holds                                            | Built with                              |+| ------------ | -------------------------------------------------------- | --------------------------------------- |+| `SecKey`     | 32-byte number from 1 to n − 1                           | `secKey`, `IsString`                    |+| `PubKey`     | A point on the curve                                     | `importPubKey`, `derivePubKey`          |+| `Msg`        | 32-byte hash                                             | `msg`                                   |+| `Sig`        | ECDSA signature with both components from 1 to n − 1    | `signMsg`, `importSig`, `importCompactSig` |+| `CompactSig` | 64 bytes, r ‖ s                                          | `exportCompactSig`, `compactSig`        |+| `SchnorrSig` | 64-byte BIP-340 signature                                | `signSchnorr`, `schnorrSig`             |+| `Tweak`      | 32-byte number from 0 to n − 1                           | `tweak`                                 |+| `RecSig`     | ECDSA signature with its recovery id                     | `signRecMsg`, `importCompactRecSig`     |++Here n is the order of the curve. The raw constructors are hidden, so these+are the only ways to build values. Each type has a `get` field with its bytes.++### Contexts++Every operation takes a `Ctx`. It is created once, shared freely between+threads, and destroyed by the garbage collector. The only operation that+modifies a context is `randomizeContext`, which needs exclusive access; most+programs never call it.++### Errors++Parsing and constructing return `Maybe`. Arithmetic that can produce an+invalid result, such as a tweak that cancels a key, returns `Maybe`. Pure+operations on valid values, such as signing, cannot fail and return their+result directly. Programming errors, such as a value of the wrong length built+with an internal constructor, raise an exception.++## Usage guide++### Lower-S and low-R++```haskell+isLowS ctx s -- the form verifySig accepts+isLowR ctx s -- r below 2^255, so DER is at most 70 bytes+normalizeSig ctx s -- the lower-S form of any signature+```++### Key tweaking++```haskell+let Just t = tweak tweakBytes+    Just childKey = tweakAddSecKey ctx parentKey t+    Just childPub = tweakAddPubKey ctx parentPub t+-- derivePubKey ctx childKey == childPub+```++This is the operation behind BIP-32 derivation; the tweak itself comes from an+HMAC of the chain code, computed by haskoin-core. `tweakMulSecKey`,+`tweakMulPubKey`, `tweakNegate` and `combinePubKeys` cover the other group+operations.++### Taproot++```haskell+let Just t = tweak taprootTweakBytes -- tagged hash of key and script tree+    Just outputKey = tweakAddXOnlyPubKey ctx internalKey t+    Just outputSecKey = tweakAddXOnlySecKey ctx internalSecKey t+```++Computing the tweak, a BIP-341 tagged hash, is left to the caller.++### Bitcoin signed messages++```haskell+import Crypto.Secp256k1.Recovery++let r = signRecMsg ctx key m+    (compact, recId) = exportCompactRecSig ctx r+-- the 65-byte message signature is a header byte (27 + recId, + 4 if the key+-- is compressed) followed by the 64 compact bytes+recover ctx r m -- Just the signer's PubKey+```++### Testing with QuickCheck++`SecKey`, `Msg` and `Tweak` have `Arbitrary` instances, so properties can+range over them directly. With a context `ctx` created once for the test suite:++```haskell+prop_roundTrip :: SecKey -> Msg -> Bool+prop_roundTrip key m =+  verifySig ctx (derivePubKey ctx key) (signMsg ctx key m) m+```++## Differences from Bitcoin Core++| Topic                      | This package                                     | Bitcoin Core                          |+| -------------------------- | ------------------------------------------------ | ------------------------------------- |+| ECDSA nonce                | `signMsg`: plain RFC 6979                        | Grinds for low R                      |+| Low-R signatures           | `signMsgLowR`: same grinding, same signatures    | Default since 0.17                    |+| High-S signatures          | `verifySig` refuses them; use `normalizeSig`     | Normalizes before verifying           |+| Verify after ECDSA signing | No                                               | Yes, as a precaution                  |+| Taproot tweak when signing | Separate step: `tweakAddXOnlySecKey`             | Built into Schnorr signing            |+| Message signatures         | `signRecMsg`, no grinding                        | Same, so signatures are identical     |++## Module overview++| Module                                  | Contents                                                  |+| --------------------------------------- | --------------------------------------------------------- |+| `Crypto.Secp256k1`                      | The public API: contexts, keys, ECDSA, Schnorr, tweaks    |+| `Crypto.Secp256k1.Recovery`             | Recoverable signatures and public key recovery            |+| `Crypto.Secp256k1.Internal.*`           | Raw constructors and foreign imports; no stability promise |++## Security notes++- **Secret keys stay in memory.** A `SecKey` is an ordinary `ByteString`. The+  garbage collector may copy it, and nothing clears it when it is no longer+  used. If your threat model includes an attacker reading process memory,+  keep keys in a hardware device instead.+- **`Show` prints secret keys.** Showing a record that holds a `SecKey`, in a+  log line or an error message, reveals it.+- **Verification only proves what was signed.** It is your protocol's job to+  hash the right data; signing a hash an attacker chose is as good as signing+  whatever they want.+- **Use fresh randomness for Schnorr when you can.** `signSchnorrIO` protects+  against fault attacks that deterministic signing does not.+- **Keep libsecp256k1 up to date.** Security fixes land in the C library;+  this package only binds it.++## Development++```sh+git clone https://github.com/jprupp/secp256k1-haskell.git+cd secp256k1-haskell++stack build                        # build against the system library+stack --nix build                  # or take GHC and libsecp256k1 from nixpkgs+stack test                         # run the tests+stack haddock --no-haddock-deps    # build the API documentation+```++`package.yaml` is the source of the package description. Change it rather than+`secp256k1-haskell.cabal`, which [hpack](https://github.com/sol/hpack)+generates.++The tests are in `test/` and use [hspec](https://hspec.github.io/) and+QuickCheck, with published test vectors from RFC 6979, Bitcoin Core and+BIP-340. CI runs them with the system library and+with `stack --nix`.++### Contributing++Pull requests are welcome.++- Add tests for the behaviour you change, with published test vectors where+  they exist.+- Keep the documentation in step with the code, as `CLAUDE.md` describes.+- Describe the change in `CHANGELOG.md`, which follows+  [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).++## Related projects++- [haskoin-core](https://github.com/jprupp/haskoin-core) builds Bitcoin keys,+  addresses, transactions and PSBTs on this package.+- [libsecp256k1](https://github.com/bitcoin-core/secp256k1) is the C library+  underneath.++## License++secp256k1-haskell is released under the+[MIT License](https://github.com/jprupp/secp256k1-haskell/blob/master/LICENSE).
secp256k1-haskell.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           secp256k1-haskell-version:        2.0.0+version:        2.0.1 synopsis:       Bindings for secp256k1 description:    Sign and verify signatures using the secp256k1 library. category:       Crypto@@ -18,6 +18,7 @@ build-type:     Simple extra-source-files:     CHANGELOG.md+    README.md  source-repository head   type: git
src/Crypto/Secp256k1.hs view
@@ -3,6 +3,41 @@ -- | ECDSA and BIP-340 Schnorr signatures on the secp256k1 curve, through -- <https://github.com/bitcoin-core/secp256k1 libsecp256k1>. --+-- == Purpose+--+-- secp256k1 is the elliptic curve of Bitcoin, and libsecp256k1 is the C+-- library Bitcoin Core uses for it: constant-time where secrets are involved,+-- hardened against side channels, exhaustively tested and reviewed. Getting+-- elliptic curve cryptography right is hard enough that a Haskell program+-- should not reimplement it. This package gives Haskell the same library, with+-- types that make the common mistakes hard to make.+--+-- == Design+--+-- * __Valid by construction.__ Every value goes through a smart constructor or+--   an import function that checks it. Code that holds a 'PubKey' or a 'Sig'+--   never has to wonder whether it is a point on the curve or a well-formed+--   signature, and invalid input is refused at the boundary where it arrives.+--+-- * __Never abort.__ The C library calls @abort()@ on an illegal argument by+--   default, which kills the whole Haskell process and cannot be caught. Every+--   context this package creates replaces that callback, so a bad argument+--   becomes 'Nothing', 'False' or an exception instead.+--+-- * __Pure where the mathematics is pure.__ Signing, verifying and tweaking are+--   deterministic functions of their inputs, so they are pure functions here.+--   Only the operations that need fresh randomness, such as 'signSchnorrIO'+--   and 'randomizeContext', run in 'IO'.+--+-- * __Standard encodings at the edges.__ Values print and parse in the encodings+--   other software understands (SEC1, DER, compact, BIP-340), never in the+--   library's internal memory layout, which differs between platforms.+--+-- * __Bitcoin Core compatibility is explicit.__ Where Bitcoin Core does+--   something beyond the standards, such as low-R grinding or accepting high-S+--   signatures from old blocks, this package offers it as a separate function+--   and documents the difference, rather than silently picking one behaviour.+-- -- == Values -- -- Every value is built through a smart constructor or an import function
src/Crypto/Secp256k1/Internal/Base.hs view
@@ -274,6 +274,7 @@       0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41     ] +-- | Thirty-two zero bytes. zero32 :: ByteString zero32 = BS.replicate 32 0 @@ -502,6 +503,12 @@ -- | Verify a signature over a message hash. 'True' means the signature is -- valid for the key and the hash and is in lower-S form. A valid signature -- in high-S form gives 'False'. Apply 'normalizeSig' first to accept it.+--+-- The lower-S rule exists because ECDSA signatures are malleable: if (r, s)+-- is valid, so is (r, n − s). Anyone could change a transaction's signature,+-- and therefore its id, without the key. Accepting only one of the two forms+-- removes that freedom, which is why libsecp256k1 and Bitcoin's relay rules+-- insist on it. verifySig :: Ctx -> PubKey -> Sig -> Msg -> Bool verifySig (Ctx fctx) (PubKey pub_key) (Sig sig_bs) (Msg m) =   unsafePerformIO $ withForeignPtr fctx $ \ctx ->@@ -512,6 +519,12 @@  -- | Sign a message hash. The nonce comes from RFC 6979, so the same key and -- hash always give the same signature. The signature is in lower-S form.+--+-- Deterministic nonces are the reason this is a pure function, and they are a+-- safety feature, not a convenience: an ECDSA nonce that repeats, or that is+-- even slightly predictable, reveals the secret key. Deriving it from the key+-- and the hash, as RFC 6979 does, removes the need for a good random number+-- generator at signing time. signMsg :: Ctx -> SecKey -> Msg -> Sig signMsg ctx key m = signMsgWith ctx key m Nothing @@ -542,6 +555,10 @@ -- nonce data (a 32-bit little-endian counter padded to 32 bytes), so the -- signatures are the same as Bitcoin Core's. On average it takes two -- attempts. The result is still deterministic.+--+-- Use it where the size of a signature matters, as in Bitcoin transactions,+-- where every byte costs a fee, or where signatures must match Bitcoin Core's+-- byte for byte. Use 'signMsg' where the RFC 6979 test vectors must hold. signMsgLowR :: Ctx -> SecKey -> Msg -> Sig signMsgLowR ctx key m = go 0 (signMsg ctx key m)   where@@ -556,6 +573,9 @@         <> BS.replicate 28 0  -- | Compute the public key of a secret key.+--+-- It is a pure function and cannot fail, because a 'SecKey' is valid by+-- construction. derivePubKey :: Ctx -> SecKey -> PubKey derivePubKey (Ctx fctx) (SecKey sec_key) =   unsafePerformIO $ withForeignPtr fctx $ \ctx ->@@ -567,6 +587,11 @@ -- | Add a tweak to a secret key: @key + tweak mod n@. 'Nothing' when the -- result would be zero, which happens when the tweak is the negation of the -- key.+--+-- Tweaks are the building block of key derivation: BIP-32 child keys are a+-- parent key plus a tweak, and Taproot outputs are an internal key plus a+-- tweak. 'tweakAddPubKey' does the same on the public side, which is what lets+-- a watch-only wallet derive addresses without any secret. tweakAddSecKey :: Ctx -> SecKey -> Tweak -> Maybe SecKey tweakAddSecKey (Ctx fctx) (SecKey sec_key) (Tweak t) =   unsafePerformIO $ withForeignPtr fctx $ \ctx ->
src/Crypto/Secp256k1/Internal/BaseOps.hs view
@@ -1,3 +1,12 @@+-- | Foreign imports of the core, @extrakeys@ and @schnorrsig@ modules of libsecp256k1.+--+-- Each function is a direct binding to the C function of the same name in+-- @secp256k1.h@, @secp256k1_extrakeys.h@ or @secp256k1_schnorrsig.h@. The+-- bindings are @safe@ calls: signing and verifying take tens of microseconds,+-- and a @safe@ call lets other Haskell threads run meanwhile. Never call these+-- directly with unchecked inputs; "Crypto.Secp256k1.Internal.Base" validates+-- lengths and contents first. This module is internal and has no API+-- stability guarantee. module Crypto.Secp256k1.Internal.BaseOps where  import Crypto.Secp256k1.Internal.ForeignTypes@@ -24,6 +33,7 @@     CUInt (..),   ) +-- | Binding to @secp256k1_ec_pubkey_parse@. foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_parse"   ecPubKeyParse ::     Ptr LCtx ->@@ -34,6 +44,7 @@     CSize ->     IO Ret +-- | Binding to @secp256k1_ec_pubkey_serialize@. foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_serialize"   ecPubKeySerialize ::     Ptr LCtx ->@@ -45,6 +56,7 @@     SerFlags ->     IO Ret +-- | Binding to @secp256k1_ecdsa_signature_parse_compact@. foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_parse_compact"   ecdsaSignatureParseCompact ::     Ptr LCtx ->@@ -52,6 +64,7 @@     Ptr Compact64 ->     IO Ret +-- | Binding to @secp256k1_ecdsa_signature_parse_der@. foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_parse_der"   ecdsaSignatureParseDer ::     Ptr LCtx ->@@ -62,6 +75,7 @@     CSize ->     IO Ret +-- | Binding to @secp256k1_ecdsa_signature_serialize_der@. foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_serialize_der"   ecdsaSignatureSerializeDer ::     Ptr LCtx ->@@ -72,6 +86,7 @@     Ptr Sig64 ->     IO Ret +-- | Binding to @secp256k1_ecdsa_signature_serialize_compact@. foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_serialize_compact"   ecdsaSignatureSerializeCompact ::     Ptr LCtx ->@@ -79,6 +94,7 @@     Ptr Sig64 ->     IO Ret +-- | Binding to @secp256k1_ecdsa_verify@. foreign import ccall safe "secp256k1.h secp256k1_ecdsa_verify"   ecdsaVerify ::     Ptr LCtx ->@@ -87,6 +103,7 @@     Ptr PubKey64 ->     IO Ret +-- | Binding to @secp256k1_ecdsa_signature_normalize@. foreign import ccall safe "secp256k1.h secp256k1_ecdsa_signature_normalize"   ecdsaSignatureNormalize ::     Ptr LCtx ->@@ -96,6 +113,7 @@     Ptr Sig64 ->     IO Ret +-- | Binding to @secp256k1_ecdsa_sign@. foreign import ccall safe "secp256k1.h secp256k1_ecdsa_sign"   ecdsaSign ::     Ptr LCtx ->@@ -107,12 +125,14 @@     Ptr a ->     IO Ret +-- | Binding to @secp256k1_ec_seckey_verify@. foreign import ccall safe "secp256k1.h secp256k1_ec_seckey_verify"   ecSecKeyVerify ::     Ptr LCtx ->     Ptr SecKey32 ->     IO Ret +-- | Binding to @secp256k1_ec_pubkey_create@. foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_create"   ecPubKeyCreate ::     Ptr LCtx ->@@ -120,6 +140,7 @@     Ptr SecKey32 ->     IO Ret +-- | Binding to @secp256k1_ec_seckey_tweak_add@. foreign import ccall safe "secp256k1.h secp256k1_ec_seckey_tweak_add"   ecSecKeyTweakAdd ::     Ptr LCtx ->@@ -127,12 +148,14 @@     Ptr Tweak32 ->     IO Ret +-- | Binding to @secp256k1_ec_seckey_negate@. foreign import ccall safe "secp256k1.h secp256k1_ec_seckey_negate"   ecTweakNegate ::     Ptr LCtx ->     Ptr Tweak32 ->     IO Ret +-- | Binding to @secp256k1_ec_pubkey_tweak_add@. foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_tweak_add"   ecPubKeyTweakAdd ::     Ptr LCtx ->@@ -140,6 +163,7 @@     Ptr Tweak32 ->     IO Ret +-- | Binding to @secp256k1_ec_seckey_tweak_mul@. foreign import ccall safe "secp256k1.h secp256k1_ec_seckey_tweak_mul"   ecSecKeyTweakMul ::     Ptr LCtx ->@@ -147,6 +171,7 @@     Ptr Tweak32 ->     IO Ret +-- | Binding to @secp256k1_ec_pubkey_tweak_mul@. foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_tweak_mul"   ecPubKeyTweakMul ::     Ptr LCtx ->@@ -154,6 +179,7 @@     Ptr Tweak32 ->     IO Ret +-- | Binding to @secp256k1_ec_pubkey_combine@. foreign import ccall safe "secp256k1.h secp256k1_ec_pubkey_combine"   ecPubKeyCombine ::     Ptr LCtx ->@@ -168,6 +194,7 @@ -- The following functions belong to the @extrakeys@ and @schnorrsig@ modules -- of libsecp256k1, enabled by default since version 0.2.0. +-- | Binding to @secp256k1_xonly_pubkey_from_pubkey@. foreign import ccall safe "secp256k1_extrakeys.h secp256k1_xonly_pubkey_from_pubkey"   xOnlyPubKeyFromPubKey ::     Ptr LCtx ->@@ -177,6 +204,7 @@     Ptr PubKey64 ->     IO Ret +-- | Binding to @secp256k1_xonly_pubkey_serialize@. foreign import ccall safe "secp256k1_extrakeys.h secp256k1_xonly_pubkey_serialize"   xOnlyPubKeySerialize ::     Ptr LCtx ->@@ -185,6 +213,7 @@     Ptr XOnlyPubKey64 ->     IO Ret +-- | Binding to @secp256k1_xonly_pubkey_tweak_add@. foreign import ccall safe "secp256k1_extrakeys.h secp256k1_xonly_pubkey_tweak_add"   xOnlyPubKeyTweakAdd ::     Ptr LCtx ->@@ -194,6 +223,7 @@     Ptr Tweak32 ->     IO Ret +-- | Binding to @secp256k1_keypair_create@. foreign import ccall safe "secp256k1_extrakeys.h secp256k1_keypair_create"   keyPairCreate ::     Ptr LCtx ->@@ -201,6 +231,7 @@     Ptr SecKey32 ->     IO Ret +-- | Binding to @secp256k1_keypair_sec@. foreign import ccall safe "secp256k1_extrakeys.h secp256k1_keypair_sec"   keyPairSec ::     Ptr LCtx ->@@ -208,6 +239,7 @@     Ptr KeyPair96 ->     IO Ret +-- | Binding to @secp256k1_keypair_xonly_pub@. foreign import ccall safe "secp256k1_extrakeys.h secp256k1_keypair_xonly_pub"   keyPairXOnlyPub ::     Ptr LCtx ->@@ -217,6 +249,7 @@     Ptr KeyPair96 ->     IO Ret +-- | Binding to @secp256k1_keypair_xonly_tweak_add@. foreign import ccall safe "secp256k1_extrakeys.h secp256k1_keypair_xonly_tweak_add"   keyPairXOnlyTweakAdd ::     Ptr LCtx ->@@ -224,6 +257,7 @@     Ptr Tweak32 ->     IO Ret +-- | Binding to @secp256k1_schnorrsig_sign32@. foreign import ccall safe "secp256k1_schnorrsig.h secp256k1_schnorrsig_sign32"   schnorrSigSign32 ::     Ptr LCtx ->@@ -234,6 +268,7 @@     Ptr AuxRand32 ->     IO Ret +-- | Binding to @secp256k1_schnorrsig_verify@. foreign import ccall safe "secp256k1_schnorrsig.h secp256k1_schnorrsig_verify"   schnorrSigVerify ::     Ptr LCtx ->
src/Crypto/Secp256k1/Internal/Context.hs view
@@ -108,25 +108,31 @@ signVerify :: CtxFlags signVerify = 0x0301 +-- | Binding to @secp256k1_context_create@. foreign import ccall safe "secp256k1.h secp256k1_context_create"   contextCreate ::     CtxFlags ->     IO (Ptr LCtx) +-- | Binding to @secp256k1_context_clone@. foreign import ccall safe "secp256k1.h secp256k1_context_clone"   contextClone ::     Ptr LCtx ->     IO (Ptr LCtx) +-- | Binding to @secp256k1_context_destroy@. foreign import ccall safe "secp256k1.h secp256k1_context_destroy"   contextDestroy :: Ptr LCtx -> IO () +-- | Pointer to @secp256k1_context_destroy@, for use as a finalizer or callback. foreign import ccall safe "secp256k1.h &secp256k1_context_destroy"   contextDestroyFunPtr :: FunPtr (Ptr LCtx -> IO ()) +-- | Pointer to @secp256k1_haskell_illegal_callback@, for use as a finalizer or callback. foreign import ccall safe "&secp256k1_haskell_illegal_callback"   illegalCallbackFunPtr :: FunPtr (CString -> Ptr a -> IO ()) +-- | Binding to @secp256k1_context_set_illegal_callback@. foreign import ccall safe "secp256k1.h secp256k1_context_set_illegal_callback"   setIllegalCallback ::     Ptr LCtx ->@@ -136,6 +142,7 @@     Ptr a ->     IO () +-- | Binding to @secp256k1_context_set_error_callback@. foreign import ccall safe "secp256k1.h secp256k1_context_set_error_callback"   setErrorCallback ::     Ptr LCtx ->@@ -145,6 +152,7 @@     Ptr a ->     IO () +-- | Binding to @secp256k1_context_randomize@. foreign import ccall safe "secp256k1.h secp256k1_context_randomize"   contextRandomize ::     Ptr LCtx ->
src/Crypto/Secp256k1/Internal/ForeignTypes.hs view
@@ -1,40 +1,66 @@+-- | Types and constants shared by the foreign imports.+--+-- Each empty data type stands for a C structure of libsecp256k1 and is only+-- ever used behind a 'Ptr', so that the foreign imports cannot mix up, say, a+-- pointer to a public key and a pointer to a signature. The number in the name+-- is the size of the structure in bytes, which the Haskell side must allocate.+-- This module is internal and has no API stability guarantee. module Crypto.Secp256k1.Internal.ForeignTypes where  import Foreign (Ptr) import Foreign.C (CInt (..), CUChar, CUInt (..)) +-- | Context (@secp256k1_context@). data LCtx +-- | Public key (@secp256k1_pubkey@), 64 bytes. data PubKey64 +-- | Message hash, 32 bytes. data Msg32 +-- | ECDSA signature (@secp256k1_ecdsa_signature@), 64 bytes. data Sig64 +-- | Randomization seed for a context, 32 bytes. data Seed32 +-- | Compact ECDSA signature, r and s, 64 bytes. data Compact64 +-- | Secret key, 32 bytes. data SecKey32 +-- | Tweak, 32 bytes. data Tweak32 +-- | Recoverable ECDSA signature (@secp256k1_ecdsa_recoverable_signature@), 65 bytes. data RecSig65 +-- | X-only public key (@secp256k1_xonly_pubkey@), 64 bytes. data XOnlyPubKey64 +-- | Key pair (@secp256k1_keypair@), 96 bytes. data KeyPair96 +-- | BIP-340 signature, 64 bytes. data SchnorrSig64 +-- | Auxiliary randomness for BIP-340 signing, 32 bytes. data AuxRand32 +-- | Flags passed to @secp256k1_context_create@. type CtxFlags = CUInt +-- | Flags passed to @secp256k1_ec_pubkey_serialize@. type SerFlags = CUInt +-- | Return value of a C function: 1 for success, 0 for failure. type Ret = CInt +-- | Nonce generation function, as @secp256k1_nonce_function@. The bindings pass+-- 'Foreign.nullFunPtr' for the default RFC 6979 function, or a pointer to+-- Bitcoin Core's grinding data through the last argument. type NonceFun a =   Ptr CUChar ->   Ptr CUChar ->@@ -44,12 +70,17 @@   CInt ->   IO CInt +-- | @SECP256K1_EC_COMPRESSED@: serialize a public key in 33 bytes. compressed :: SerFlags compressed = 0x0102 +-- | @SECP256K1_EC_UNCOMPRESSED@: serialize a public key in 65 bytes. uncompressed :: SerFlags uncompressed = 0x0002 +-- | Interpret a return value. Anything but 0 or 1 means the C library and these+-- bindings disagree about a function's contract, so it raises an error rather+-- than guess. isSuccess :: Ret -> Bool isSuccess 0 = False isSuccess 1 = True
src/Crypto/Secp256k1/Internal/RecoveryOps.hs view
@@ -18,6 +18,7 @@ import Foreign (FunPtr, Ptr) import Foreign.C (CInt (..)) +-- | Binding to @secp256k1_ecdsa_recoverable_signature_parse_compact@. foreign import ccall safe "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_parse_compact"   ecdsaRecoverableSignatureParseCompact ::     Ptr LCtx ->@@ -27,6 +28,7 @@     CInt ->     IO Ret +-- | Binding to @secp256k1_ecdsa_recoverable_signature_serialize_compact@. foreign import ccall safe "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_serialize_compact"   ecdsaRecoverableSignatureSerializeCompact ::     Ptr LCtx ->@@ -36,6 +38,7 @@     Ptr RecSig65 ->     IO Ret +-- | Binding to @secp256k1_ecdsa_recoverable_signature_convert@. foreign import ccall safe "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_convert"   ecdsaRecoverableSignatureConvert ::     Ptr LCtx ->@@ -45,6 +48,7 @@     Ptr RecSig65 ->     IO Ret +-- | Binding to @secp256k1_ecdsa_sign_recoverable@. foreign import ccall safe "secp256k1_recovery.h secp256k1_ecdsa_sign_recoverable"   ecdsaSignRecoverable ::     Ptr LCtx ->@@ -56,6 +60,7 @@     Ptr a ->     IO Ret +-- | Binding to @secp256k1_ecdsa_recover@. foreign import ccall safe "secp256k1_recovery.h secp256k1_ecdsa_recover"   ecdsaRecover ::     Ptr LCtx ->
src/Crypto/Secp256k1/Internal/Util.hs view
@@ -1,5 +1,13 @@ {-# LANGUAGE FlexibleContexts #-} +-- | Helpers to move bytes between Haskell and the C library safely.+--+-- Most mistakes in C bindings are about memory: passing a buffer of the wrong+-- size, letting the C side write into a 'ByteString' that other code shares,+-- or leaking a buffer when an exception is thrown. Every binding goes through+-- these functions, which check lengths before calling C, give the C library+-- fresh buffers to write into, and free them on every path. This module is+-- internal and has no API stability guarantee. module Crypto.Secp256k1.Internal.Util where  import Control.Exception (onException)@@ -14,20 +22,26 @@ import Foreign.C (CSize (..)) import System.Entropy (getEntropy) +-- | Decode hex, or 'Nothing' if the string is not valid hex. decodeHex :: (ConvertibleStrings a ByteString) => a -> Maybe ByteString decodeHex str =   case parseBase16 $ cs str of     Right b16 -> Just $ decodeBase16 b16     Left _ -> Nothing +-- | Show bytes as a quoted hex string. showsHex :: ByteString -> ShowS showsHex = shows . extractBase16 . encodeBase16 +-- | Pass the bytes of a 'ByteString' to C without copying them. Only for+-- arguments that the C library reads and never writes: writing would change a+-- 'ByteString' that may be shared. unsafeUseByteString :: ByteString -> ((Ptr a, CSize) -> IO b) -> IO b unsafeUseByteString bs f =   BU.unsafeUseAsCStringLen bs $ \(b, l) ->     f (castPtr b, fromIntegral l) +-- | Pass a copy of the bytes of a 'ByteString' to C. useByteString :: ByteString -> ((Ptr a, CSize) -> IO b) -> IO b useByteString bs f =   BS.useAsCStringLen bs $ \(b, l) ->@@ -82,14 +96,17 @@       copyBytes dst (castPtr src) len       f dst +-- | Take ownership of a @malloc@'ed buffer as a 'ByteString', without copying. unsafePackByteString :: (Ptr a, CSize) -> IO ByteString unsafePackByteString (b, l) =   BU.unsafePackMallocCStringLen (castPtr b, fromIntegral l) +-- | Copy a C buffer into a new 'ByteString'. packByteString :: (Ptr a, CSize) -> IO ByteString packByteString (b, l) =   BS.packCStringLen (castPtr b, fromIntegral l) +-- | Pass 32 bytes from the operating system's secure random source to C. withRandomSeed :: (Ptr Seed32 -> IO a) -> IO a withRandomSeed go = do   bs <- getEntropy 32