Skip to content

ECDSA & Signers

Signers

Ox exports utilities for the following ECDSA signers:

Private Keys

We can generate private keys using one of the Signer modules with their respective generation function:

import { P256, Secp256k1, WebCryptoP256 } from 'ox'
 
// Generate a private key on the secp256k1 curve.
const 
const privateKey: `0x${string}`
privateKey
= Secp256k1.randomPrivateKey()
// Generate a private key on the P256 curve. const
const privateKey_2: `0x${string}`
privateKey_2
= P256.randomPrivateKey()
// Generate a private key on the P256 curve using the Web Crypto API. const
const keypair: { privateKey: CryptoKey; publicKey: PublicKey; }
keypair
= await WebCryptoP256.createKeyPair()

Public Keys

We can extract Public Keys from Private Keys using one of the respective Signer functions:

import { Secp256k1, P256 } from 'ox'
 
{
  const privateKey = Secp256k1.randomPrivateKey()
  const 
const publicKey: { prefix: number; x: bigint; y: bigint; }
publicKey
= Secp256k1.getPublicKey({ privateKey })
} { const privateKey = P256.randomPrivateKey() const publicKey = P256.getPublicKey({ privateKey }) }

We can also extract an Ethereum Address from a Public Key using Address.fromPublicKey:

import { Address, Secp256k1 } from 'ox'
 
const privateKey = Secp256k1.randomPrivateKey()
const publicKey = Secp256k1.getPublicKey({ privateKey })
const 
const address: `0x${string}`
address
= Address.fromPublicKey(publicKey)

Signing

Payloads are signed using the respective Signer's sign function:

import { Secp256k1, P256, WebCryptoP256, WebAuthnP256 } from 'ox'
 
const payload = '0xdeadbeef'
 
// secp256k1
{
  const privateKey = Secp256k1.randomPrivateKey()
  const 
const signature: { r: bigint; s: bigint; yParity: number; }
signature
= Secp256k1.sign({ payload, privateKey })
} // P256 { const privateKey = P256.randomPrivateKey() const signature = P256.sign({ payload, privateKey }) } // WebCrypto-P256 { const { privateKey } = await WebCryptoP256.createKeyPair() const signature = WebCryptoP256.sign({ payload, privateKey }) }

Verification

Signatures can be verified against the signing payload and respective public key using the respective Signer's verify function:

import { Secp256k1, P256, WebCryptoP256 } from 'ox'
 
const payload = '0xdeadbeef'
 
// secp256k1
{
  const privateKey = Secp256k1.randomPrivateKey()
  const publicKey = Secp256k1.getPublicKey({ privateKey })
  const signature = Secp256k1.sign({ payload, privateKey })
  const verified = Secp256k1.verify({ payload, publicKey, signature })
}
 
// P256
{
  const privateKey = P256.randomPrivateKey()
  const publicKey = P256.getPublicKey({ privateKey })
  const signature = P256.sign({ payload, privateKey })
  const verified = P256.verify({ payload, publicKey, signature })
}
 
// WebCrypto-P256
{
  const { privateKey, publicKey } = await WebCryptoP256.createKeyPair()
  const signature = await WebCryptoP256.sign({ payload, privateKey })
  const verified = await WebCryptoP256.verify({ payload, publicKey, signature })
}

Recovery

Public Keys can be recovered from a signature and payload using the Signer's respective function:

import { Secp256k1, P256, WebCryptoP256 } from 'ox'
 
const payload = '0xdeadbeef'
 
// secp256k1
{
  const privateKey = Secp256k1.randomPrivateKey()
  const signature = Secp256k1.sign({ payload, privateKey })
  const publicKey = Secp256k1.recoverPublicKey({ payload, signature })
}
 
// P256
{
  const privateKey = P256.randomPrivateKey()
  const signature = P256.sign({ payload, privateKey })
  const publicKey = P256.recoverPublicKey({ payload, signature })
}

Signatures

Signatures in Ox are represented via the Signature.Signature type – an object containing the standard ECDSA signature components of:

  • r: a bigint representing the r component of the signature.
  • s: a bigint representing the s component of the signature.
  • yParity (or "recovery bit"): an optional number representing the recovery bit of the signature – typically utilized for recovery operations.

Examples:

import { Signature } from 'ox'
 
// Signature with a recovery bit (yParity)
const signature = Signature.from({
  r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
  s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
  yParity: 0,
})
 
// Signature without a recovery bit (yParity)
const signature_2 = Signature.from({
  r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
  s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
})

Serializing

You may need to serialize a Signature into Hex or Bytes format for specific use cases. You can do this using the Signature.toHex or Signature.toBytes functions:

import { Signature } from 'ox'
 
const signature = Signature.from({
  r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
  s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
  yParity: 0,
})
const 
const serialized: `0x${string}`
serialized
= Signature.toHex(signature)
const
const serialized_bytes: Uint8Array
serialized_bytes
= Signature.toBytes(signature)

Public Keys

Public Keys in Ox can come in two forms: a compressed and uncompressed format. Generally, you will interact with uncompressed Public Keys, but it is important to be aware of the format.

Public Keys are represented via the PublicKey.PublicKey type – an object containing the standard ECDSA public key components of:

  • prefix: a number representing the prefix of the public key (4 for uncompressed, 2 or 3 for compressed).
  • x: a bigint representing the x coordinate of the public key.
  • y: (if uncompressed) a bigint representing the y coordinate of the public key.

An example Public Key:

import { PublicKey } from 'ox'
 
// Uncompressed
const publicKey = PublicKey.from({
  prefix: 4,
  x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
  y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
})
 
// Compressed
const publicKey_2 = PublicKey.from({
  prefix: 2,
  x: 59295962801117472859457908919941473389380284132224861839820747729565200149877n,
})

Serializing

You may need to serialize a Public Key into Hex or Bytes format for specific use cases. You can do this using the PublicKey.toHex or PublicKey.toBytes functions:

import { PublicKey } from 'ox'
 
const publicKey = PublicKey.from({
  x: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
  y: 24099691209996290925259367678540227198235484593389470330605641003500238088869n,
})
const 
const serialized: `0x${string}`
serialized
= PublicKey.toHex(publicKey)
const
const serialized_bytes: Uint8Array
serialized_bytes
= PublicKey.toBytes(publicKey)

Related Modules

ModuleDescription
P256Utility functions for NIST P256 ECDSA cryptography.
PublicKeyUtility functions for working with ECDSA public keys.
Secp256k1Utility functions for secp256k1 ECDSA cryptography.
SignatureUtility functions for working with ECDSA signatures.
WebAuthnP256Utility functions for NIST P256 ECDSA cryptography using the Web Authentication API
WebCryptoP256Utility functions for NIST P256 ECDSA cryptography using the Web Crypto API