ECDSA & Signers
Signers
Ox exports utilities for the following ECDSA signers:
Secp256k1: Utilities for the secp256k1 elliptic curve – the primary curve used on the Ethereum protocol.P256: Utilities for NIST P256 cryptography. Commonly used in Ethereum Account Abstraction.WebAuthnP256: P256 utilities using the Web Authentication API. Commonly used in Ethereum Account Abstraction.WebCryptoP256: P256 utilities using the Web Crypto API. Commonly used in Ethereum Account Abstraction.
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: abigintrepresenting thercomponent of the signature.s: abigintrepresenting thescomponent of the signature.yParity(or "recovery bit"): an optionalnumberrepresenting 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: Uint8Arrayserialized_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: anumberrepresenting the prefix of the public key (4for uncompressed,2or3for compressed).x: abigintrepresenting thexcoordinate of the public key.y: (if uncompressed) abigintrepresenting theycoordinate 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: Uint8Arrayserialized_bytes = PublicKey.toBytes(publicKey)
Related Modules
| Module | Description |
|---|---|
| P256 | Utility functions for NIST P256 ECDSA cryptography. |
| PublicKey | Utility functions for working with ECDSA public keys. |
| Secp256k1 | Utility functions for secp256k1 ECDSA cryptography. |
| Signature | Utility functions for working with ECDSA signatures. |
| WebAuthnP256 | Utility functions for NIST P256 ECDSA cryptography using the Web Authentication API |
| WebCryptoP256 | Utility functions for NIST P256 ECDSA cryptography using the Web Crypto API |

