BSV.PrivateKey (bsv_sdk v1.1.0)

Copy Markdown View Source

Bitcoin private key operations: generation, WIF encoding, signing.

OpenSSL Requirement

Signing requires OpenSSL 3.0+ for RFC 6979 deterministic nonce generation. Earlier OpenSSL versions may use random k-values, which is unsafe if the same message is signed across different OpenSSL configurations.

Security Notice — Key Material Lifetime

Private key bytes (raw field) persist in BEAM process memory until garbage collected. Erlang/Elixir binaries are immutable and the GC is non-deterministic, so key material cannot be reliably zeroed from pure Elixir.

Mitigations for production use:

  • Run signing operations in dedicated short-lived processes (GenServer / Task) so that key memory is reclaimed when the process exits.
  • Consider NIF-backed secure memory (e.g. libsodium sodium_mlock/sodium_munlock) for long-lived key storage.
  • Avoid logging or inspecting PrivateKey structs.
  • Be aware that BEAM crash dumps may contain key material.

Summary

Functions

Derive a child private key using BRC-42 key derivation.

Compute ECDH shared secret with a public key.

Create a private key from 32 raw bytes.

Decode a WIF string to a private key.

Decode a WIF string, raising on error.

Generate a random private key.

Sign a 32-byte message hash with this key. Returns DER-encoded signature with low-S.

Derive the public key from this private key.

Encode private key to WIF format.

Types

t()

@type t() :: %BSV.PrivateKey{raw: <<_::256>>}

Functions

derive_child(key, pub_key, invoice_number)

@spec derive_child(t(), BSV.PublicKey.t(), String.t()) ::
  {:ok, t()} | {:error, String.t()}

Derive a child private key using BRC-42 key derivation.

Computes ECDH shared secret with the counterparty's public key, then HMAC-SHA256(shared_secret_compressed, invoice_number) to derive a scalar offset, which is added to this key's scalar mod N.

derive_shared_secret(private_key, pub_key)

@spec derive_shared_secret(t(), BSV.PublicKey.t()) ::
  {:ok, BSV.PublicKey.t()} | {:error, String.t()}

Compute ECDH shared secret with a public key.

Uses OpenSSL-backed :crypto.compute_key/4 for security (includes curve validation) and performance. Returns the shared point as a compressed public key.

from_bytes(arg1)

@spec from_bytes(binary()) :: {:ok, t()} | {:error, String.t()}

Create a private key from 32 raw bytes.

from_wif(wif)

@spec from_wif(String.t()) :: {:ok, t()} | {:error, String.t()}

Decode a WIF string to a private key.

from_wif!(wif)

@spec from_wif!(String.t()) :: t()

Decode a WIF string, raising on error.

generate()

@spec generate() :: t()

Generate a random private key.

sign(private_key, arg)

@spec sign(t(), binary()) :: {:ok, binary()}

Sign a 32-byte message hash with this key. Returns DER-encoded signature with low-S.

to_public_key(key)

@spec to_public_key(t()) :: BSV.PublicKey.t()

Derive the public key from this private key.

to_wif(private_key, opts \\ [])

@spec to_wif(
  t(),
  keyword()
) :: String.t()

Encode private key to WIF format.