C# P/Invoke bindings for UltrafastSecp256k1 — high-performance secp256k1 ECC (ufsecp C ABI v1)
$ dotnet add package UfsecpC# P/Invoke bindings for UltrafastSecp256k1 -- high-performance secp256k1 elliptic curve cryptography.
Bundles native runtimes for Windows x64, Linux x64, Linux ARM64, and macOS ARM64. The native library is auto-copied to your build output -- no manual setup required.
dotnet add package Ufsecp
using Ultrafast.Ufsecp;
using System.Security.Cryptography;
using var ctx = new Ufsecp();
// Generate key pair
byte[] privkey = RandomNumberGenerator.GetBytes(32);
byte[] pubkey = ctx.PubkeyCreate(privkey);
Console.WriteLine($"Version: {Ufsecp.VersionString}");
Console.WriteLine($"Pubkey: {Convert.ToHexString(pubkey)}");
byte[] msgHash = Ufsecp.Sha256("hello world"u8.ToArray());
// Sign (RFC 6979 deterministic nonce, low-S normalized)
byte[] sig = ctx.EcdsaSign(msgHash, privkey);
// Verify
bool valid = ctx.EcdsaVerify(msgHash, sig, pubkey);
// DER encode/decode
byte[] der = ctx.EcdsaSigToDer(sig);
byte[] compact = ctx.EcdsaSigFromDer(der);
var (recSig, recId) = ctx.EcdsaSignRecoverable(msgHash, privkey);
byte[] recovered = ctx.EcdsaRecover(msgHash, recSig, recId);
byte[] xOnlyPub = ctx.PubkeyXonly(privkey);
byte[] auxRand = RandomNumberGenerator.GetBytes(32);
byte[] schnorrSig = ctx.SchnorrSign(msgHash, privkey, auxRand);
bool ok = ctx.SchnorrVerify(msgHash, schnorrSig, xOnlyPub);
byte[] otherPriv = RandomNumberGenerator.GetBytes(32);
byte[] otherPub = ctx.PubkeyCreate(otherPriv);
byte[] shared = ctx.Ecdh(privkey, otherPub); // SHA-256 of compressed point
byte[] xonly = ctx.EcdhXonly(privkey, otherPub); // SHA-256 of x-coordinate
byte[] raw = ctx.EcdhRaw(privkey, otherPub); // raw 32-byte x-coordinate
string p2pkh = ctx.AddrP2PKH(pubkey); // 1...
string p2wpkh = ctx.AddrP2WPKH(pubkey); // bc1q...
string p2tr = ctx.AddrP2TR(xOnlyPub); // bc1p...
string test = ctx.AddrP2WPKH(pubkey, Network.Testnet); // tb1q...
byte[] seed = RandomNumberGenerator.GetBytes(64);
byte[] master = ctx.Bip32Master(seed);
byte[] child = ctx.Bip32DerivePath(master, "m/44'/0'/0'/0/0");
byte[] childPriv = ctx.Bip32Privkey(child);
byte[] childPub = ctx.Bip32Pubkey(child);string wif = ctx.WifEncode(privkey, compressed: true, Network.Mainnet);
var decoded = ctx.WifDecode(wif);
// decoded.Privkey, decoded.Compressed, decoded.Networkvar (outputKeyX, parity) = ctx.TaprootOutputKey(xOnlyPub);
byte[] tweakedPriv = ctx.TaprootTweakSeckey(privkey);
bool tapValid = ctx.TaprootVerify(outputKeyX, parity, xOnlyPub);byte[] sha = Ufsecp.Sha256(data); // SHA-256 (SHA-NI accelerated)
byte[] h160 = Ufsecp.Hash160(data); // RIPEMD160(SHA256(data))
byte[] tagged = Ufsecp.TaggedHash("BIP0340/aux", data); // BIP-340 tagged hashKeys, ECDSA (sign/verify/recover/DER), Schnorr BIP-340, ECDH (compressed/xonly/raw), SHA-256, HASH160, Tagged Hash, BIP-32 HD, Taproot (BIP-341), Bitcoin Addresses (P2PKH/P2WPKH/P2TR), WIF, Key Tweaking.
The C ABI layer uses the fast (variable-time) implementation for maximum throughput. A constant-time (CT) layer with identical mathematical operations is available via the C++ headers for applications requiring timing-attack resistance.
| Platform | Runtime |
|---|---|
| Windows x64 | ufsecp.dll |
| Linux x64 | libufsecp.so |
| Linux ARM64 | libufsecp.so |
| macOS ARM64 | libufsecp.dylib |
MIT