Skip to main content

Try it Live

Run Signature examples in the interactive playground

Utilities

Helper functions for signature operations.

Component Extraction

getAlgorithm

Get signature algorithm.

Signature

function getAlgorithm(signature: BrandedSignature): SignatureAlgorithm

Parameters

  • signature - BrandedSignature

Returns

SignatureAlgorithm ("secp256k1" | "p256" | "ed25519")

Example

const sig = Signature.fromSecp256k1(r, s, 27);
console.log(Signature.getAlgorithm(sig)); // "secp256k1"

const p256Sig = Signature.fromP256(r, s);
console.log(Signature.getAlgorithm(p256Sig)); // "p256"

const ed25519Sig = Signature.fromEd25519(bytes);
console.log(Signature.getAlgorithm(ed25519Sig)); // "ed25519"

getR

Extract r component from ECDSA signature.

Signature

function getR(signature: BrandedSignature): Uint8Array

Parameters

  • signature - ECDSA BrandedSignature

Returns

Uint8Array (32 bytes) - r component

Throws

  • Error if signature is Ed25519 (no r component)

Example

const sig = Signature.fromSecp256k1(r, s, 27);

const rComponent = Signature.getR(sig);
console.log(rComponent.length); // 32
console.log(rComponent.every((b, i) => b === r[i])); // true

// Ed25519 throws
const ed25519Sig = Signature.fromEd25519(bytes);
try {
  Signature.getR(ed25519Sig); // Throws
} catch (err) {
  console.error('Ed25519 has no r component');
}

getS

Extract s component from ECDSA signature.

Signature

function getS(signature: BrandedSignature): Uint8Array

Parameters

  • signature - ECDSA BrandedSignature

Returns

Uint8Array (32 bytes) - s component

Throws

  • Error if signature is Ed25519 (no s component)

Example

const sig = Signature.fromSecp256k1(r, s, 27);

const sComponent = Signature.getS(sig);
console.log(sComponent.length); // 32
console.log(sComponent.every((b, i) => b === s[i])); // true

// Check canonicality of s
const SECP256K1_N_DIV_2 = new Uint8Array([
  0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  0x5d, 0x57, 0x6e, 0x73, 0x57, 0xa4, 0x50, 0x1d,
  0xdf, 0xe9, 0x2f, 0x46, 0x68, 0x1b, 0x20, 0xa0,
]);

function isLowS(sig: BrandedSignature): boolean {
  const s = Signature.getS(sig);
  for (let i = 0; i < 32; i++) {
    if (s[i]! < SECP256K1_N_DIV_2[i]!) return true;
    if (s[i]! > SECP256K1_N_DIV_2[i]!) return false;
  }
  return true;
}

getV

Get recovery ID from secp256k1 signature.

Signature

function getV(signature: BrandedSignature): number | undefined

Parameters

  • signature - BrandedSignature

Returns

  • number (27 or 28) - Recovery ID if present
  • undefined - If not secp256k1 or v not set

Example

// With recovery ID
const sig1 = Signature.fromSecp256k1(r, s, 27);
console.log(Signature.getV(sig1)); // 27

// Without recovery ID
const sig2 = Signature.fromSecp256k1(r, s);
console.log(Signature.getV(sig2)); // undefined

// P-256 has no v
const sig3 = Signature.fromP256(r, s);
console.log(Signature.getV(sig3)); // undefined

// Ed25519 has no v
const sig4 = Signature.fromEd25519(bytes);
console.log(Signature.getV(sig4)); // undefined

Use Cases

// Ethereum address recovery
function recoverAddress(
  message: Uint8Array,
  sig: BrandedSignature
): Address {
  const v = Signature.getV(sig);
  if (v === undefined) {
    throw new Error('Recovery ID required');
  }

  const r = Signature.getR(sig);
  const s = Signature.getS(sig);

  // Use crypto module for actual recovery
  return recoverPublicKey(message, r, s, v);
}

// EIP-155 transaction encoding
function encodeTransaction(tx: Transaction, sig: BrandedSignature): Uint8Array {
  const v = Signature.getV(sig);
  const eip155V = tx.chainId * 2 + 35 + (v! - 27);

  return RLP.encode([
    tx.nonce,
    tx.gasPrice,
    tx.gasLimit,
    tx.to,
    tx.value,
    tx.data,
    eip155V,
    Signature.getR(sig),
    Signature.getS(sig),
  ]);
}

Comparison

equals

Compare signatures for equality.

Signature

function equals(
  a: BrandedSignature,
  b: BrandedSignature
): boolean

Parameters

  • a - First signature
  • b - Second signature

Returns

true if signatures are equal (algorithm, bytes, and v match)

Example

const sig1 = Signature.fromSecp256k1(r, s, 27);
const sig2 = Signature.fromSecp256k1(r, s, 27);
const sig3 = Signature.fromSecp256k1(r, s, 28);

console.log(Signature.equals(sig1, sig2)); // true
console.log(Signature.equals(sig1, sig3)); // false (different v)

// Different algorithms
const p256Sig = Signature.fromP256(r, s);
console.log(Signature.equals(sig1, p256Sig)); // false

Implementation

Checks:
  1. Algorithm match
  2. Byte-by-byte comparison
  3. Recovery ID match (if present)
// Constant-time byte comparison
function equals(a: BrandedSignature, b: BrandedSignature): boolean {
  if (a.algorithm !== b.algorithm) return false;
  if (a.length !== b.length) return false;
  if (a.v !== b.v) return false;

  let result = 0;
  for (let i = 0; i < a.length; i++) {
    result |= a[i]! ^ b[i]!;
  }

  return result === 0;
}

is

Type guard for BrandedSignature.

Signature

function is(value: unknown): value is BrandedSignature

Parameters

  • value - Value to check

Returns

true if value is BrandedSignature

Example

const sig = Signature.fromSecp256k1(r, s, 27);
console.log(Signature.is(sig)); // true

const bytes = Bytes64();
console.log(Signature.is(bytes)); // false

const obj = { algorithm: 'secp256k1' };
console.log(Signature.is(obj)); // false

// Type narrowing
function process(value: unknown) {
  if (Signature.is(value)) {
    // value is BrandedSignature
    console.log(value.algorithm);
  }
}

Checks

  1. Is object
  2. Is Uint8Array instance
  3. Has [brand] === "Signature"
  4. Has algorithm property

Verification (Placeholder)

verify

Verify signature against message and public key.

Signature

function verify(
  signature: BrandedSignature,
  message: Uint8Array,
  publicKey: Uint8Array
): boolean

Parameters

  • signature - Signature to verify
  • message - Message that was signed
  • publicKey - Public key to verify against

Returns

true if signature is valid

Throws

  • InvalidAlgorithmError - Always (placeholder implementation)

Example

const sig = Signature.fromSecp256k1(r, s, 27);

try {
  const valid = Signature.verify(sig, message, publicKey);
} catch (err) {
  console.error(err.message);
  // "Signature verification for secp256k1 requires integration with crypto primitives"
}

Note

This is a placeholder. Actual verification requires crypto module integration:
// Use crypto module instead
import { secp256k1 } from '@voltaire/crypto';

function verifySecp256k1(
  sig: BrandedSignature,
  message: Uint8Array,
  publicKey: Uint8Array
): boolean {
  const r = Signature.getR(sig);
  const s = Signature.getS(sig);

  return secp256k1.verify(message, r, s, publicKey);
}

Helper Functions

Component Comparison

// Compare r components
function compareR(a: BrandedSignature, b: BrandedSignature): boolean {
  const rA = Signature.getR(a);
  const rB = Signature.getR(b);
  return rA.every((byte, i) => byte === rB[i]);
}

// Compare s components
function compareS(a: BrandedSignature, b: BrandedSignature): boolean {
  const sA = Signature.getS(a);
  const sB = Signature.getS(b);
  return sA.every((byte, i) => byte === sB[i]);
}

Algorithm Detection

function isECDSA(sig: BrandedSignature): boolean {
  return sig.algorithm === 'secp256k1' || sig.algorithm === 'p256';
}

function isEdDSA(sig: BrandedSignature): boolean {
  return sig.algorithm === 'ed25519';
}

function hasRecoveryId(sig: BrandedSignature): boolean {
  return sig.v !== undefined;
}

Component Extraction Helpers

// Extract both r and s
function getComponents(sig: BrandedSignature): {
  r: Uint8Array;
  s: Uint8Array;
} {
  return {
    r: Signature.getR(sig),
    s: Signature.getS(sig),
  };
}

// Extract all metadata
function getMetadata(sig: BrandedSignature): {
  algorithm: SignatureAlgorithm;
  v?: number;
  length: number;
} {
  return {
    algorithm: Signature.getAlgorithm(sig),
    v: Signature.getV(sig),
    length: sig.length,
  };
}

Display Helpers

function formatSignature(sig: BrandedSignature): string {
  const r = Signature.getR(sig);
  const s = Signature.getS(sig);
  const v = Signature.getV(sig);

  return [
    `Algorithm: ${sig.algorithm}`,
    `r: 0x${Hex(r)}`,
    `s: 0x${Hex(s)}`,
    v !== undefined ? `v: ${v}` : null,
  ].filter(Boolean).join('\n');
}

console.log(formatSignature(sig));
// Algorithm: secp256k1
// r: 0x1234...
// s: 0x5678...
// v: 27

Validation Helpers

// Check if signature has expected algorithm
function assertAlgorithm(
  sig: BrandedSignature,
  expected: SignatureAlgorithm
): void {
  const actual = Signature.getAlgorithm(sig);
  if (actual !== expected) {
    throw new Error(`Expected ${expected}, got ${actual}`);
  }
}

// Check if signature has recovery ID
function assertRecoveryId(sig: BrandedSignature): void {
  if (Signature.getV(sig) === undefined) {
    throw new Error('Recovery ID required');
  }
}

// Usage
assertAlgorithm(sig, 'secp256k1');
assertRecoveryId(sig);

Performance Considerations

Component Access

// Efficient: direct property access
const algorithm = sig.algorithm; // O(1)
const v = sig.v; // O(1)

// Efficient: slice creates view (no copy)
const r = sig.slice(0, 32); // O(1) view creation
const s = sig.slice(32, 64); // O(1) view creation

// Less efficient: helper functions have call overhead
const r2 = Signature.getR(sig); // O(1) + function call

Equality Comparison

// Constant-time comparison (security)
Signature.equals(sig1, sig2); // O(n), constant-time

// Fast but not constant-time
function fastEquals(a: BrandedSignature, b: BrandedSignature): boolean {
  if (a.algorithm !== b.algorithm) return false;
  if (a.v !== b.v) return false;
  return a.every((byte, i) => byte === b[i]); // Early exit
}

Type Checking

// Fast runtime check
Signature.is(value); // O(1)

// Use for validation at API boundaries
function acceptSignature(value: unknown): BrandedSignature {
  if (!Signature.is(value)) {
    throw new TypeError('Expected BrandedSignature');
  }
  return value;
}

Common Patterns

Decompose Signature

function decomposeSignature(sig: BrandedSignature) {
  return {
    algorithm: Signature.getAlgorithm(sig),
    r: Signature.getR(sig),
    s: Signature.getS(sig),
    v: Signature.getV(sig),
  };
}

const { algorithm, r, s, v } = decomposeSignature(sig);

Clone Signature

function cloneSignature(sig: BrandedSignature): BrandedSignature {
  const algorithm = Signature.getAlgorithm(sig);

  switch (algorithm) {
    case 'secp256k1':
      return Signature.fromSecp256k1(
        Signature.getR(sig),
        Signature.getS(sig),
        Signature.getV(sig)
      );
    case 'p256':
      return Signature.fromP256(
        Signature.getR(sig),
        Signature.getS(sig)
      );
    case 'ed25519':
      return Signature.fromEd25519(sig.slice());
  }
}

Signature Info

function getSignatureInfo(sig: BrandedSignature): string {
  const algorithm = Signature.getAlgorithm(sig);
  const v = Signature.getV(sig);

  return `${algorithm}${v !== undefined ? ` (v=${v})` : ''}`;
}

console.log(getSignatureInfo(sig)); // "secp256k1 (v=27)"

See Also