Skip to main content
The @agentdoor/core crypto module provides Ed25519 and secp256k1 cryptographic operations for agent authentication, including key generation, signing, verification, and wallet signature support.

Ed25519 Operations

generateKeypair

Generate a new Ed25519 keypair for agent registration.
import { generateKeypair } from "@agentdoor/core";

const keypair = generateKeypair();
console.log(keypair.publicKey);  // Base64-encoded 32-byte public key
console.log(keypair.secretKey);  // Base64-encoded 64-byte secret key
returns
Keypair
An Ed25519 keypair with base64-encoded keys.
Implementation: Uses tweetnacl’s cryptographically secure random byte generation.

signChallenge

Sign a challenge message using an Ed25519 secret key.
import { signChallenge } from "@agentdoor/core";

const message = "agentdoor:register:ag_abc123:1234567890:random_nonce";
const signature = signChallenge(message, secretKeyBase64);
// Returns: Base64-encoded 64-byte signature
message
string
required
The challenge message string to sign.
secretKeyBase64
string
required
Base64-encoded 64-byte Ed25519 secret key.
returns
string
Base64-encoded 64-byte Ed25519 signature.

verifySignature

Verify an Ed25519 signature on a message.
import { verifySignature } from "@agentdoor/core";

const isValid = verifySignature(
  message,
  signatureBase64,
  publicKeyBase64
);

if (isValid) {
  console.log("Signature is valid");
} else {
  console.log("Signature is invalid");
}
message
string
required
The original message string that was signed.
signatureBase64
string
required
Base64-encoded 64-byte Ed25519 signature.
publicKeyBase64
string
required
Base64-encoded 32-byte Ed25519 public key.
returns
boolean
true if the signature is valid, false otherwise.
Error Handling: Returns false for any decoding errors or invalid key/signature lengths. Never throws.

secp256k1 Wallet Operations

verifyWalletSignature

Verify a secp256k1 signature, typically from an x402 wallet. This allows agents to authenticate using their blockchain wallet keys.
import { verifyWalletSignature } from "@agentdoor/core";

const isValid = verifyWalletSignature(
  message,
  signatureHex,
  publicKeyHex
);
message
string
required
The original message string that was signed.
signatureHex
string
required
Hex-encoded secp256k1 signature (64 bytes / 128 hex characters).
publicKeyHex
string
required
Hex-encoded secp256k1 public key (33 bytes compressed or 65 bytes uncompressed).
returns
boolean
true if the signature is valid.
Note: The message is hashed with SHA-256 before verification, following standard secp256k1 practices.

publicKeyToAddress

Derive an Ethereum-style address from a secp256k1 public key.
import { publicKeyToAddress } from "@agentdoor/core";

const address = publicKeyToAddress(publicKeyHex);
console.log(address); // "0x..."
publicKeyHex
string
required
Hex-encoded secp256k1 public key.
returns
string
Hex address string prefixed with "0x".
Note: This is a simplified implementation. For full EIP-55 checksumming, use a dedicated library like ethers.js.

ID & Key Generation

generateAgentId

Generate a unique agent ID.
import { generateAgentId } from "@agentdoor/core";

const agentId = generateAgentId();
console.log(agentId); // "ag_V1StGXR8_Z5jdHi6B"
returns
string
Agent ID in the format ag_{nanoid} (21-character nanoid).

generateApiKey

Generate a unique API key for an agent.
import { generateApiKey } from "@agentdoor/core";

const liveKey = generateApiKey("live");
console.log(liveKey); // "agk_live_..."

const testKey = generateApiKey("test");
console.log(testKey); // "agk_test_..."
mode
'live' | 'test'
Key mode. Defaults to "live".
returns
string
The raw API key string. This should only be returned to the agent once, then hashed for storage.
Security: The raw API key contains 32 characters of random data from nanoid. Store only the hash using hashApiKey().

hashApiKey

Hash an API key using SHA-256 for secure storage.
import { generateApiKey, hashApiKey } from "@agentdoor/core";

const apiKey = generateApiKey("live");
const hash = hashApiKey(apiKey);

// Store 'hash' in database, return 'apiKey' to agent (once)
await store.createAgent({
  // ...
  apiKeyHash: hash
});

return { api_key: apiKey }; // Only returned once
apiKey
string
required
The raw API key string.
returns
string
Hex-encoded SHA-256 hash of the API key.

generateNonce

Generate a cryptographically random nonce for challenges.
import { generateNonce } from "@agentdoor/core";

const nonce = generateNonce(); // Default: 32 characters
const shortNonce = generateNonce(16);
length
number
Nonce length in characters. Defaults to 32.
returns
string
URL-safe random string generated by nanoid.

Hashing Utilities

sha256

Compute SHA-256 hash of a string asynchronously using Web Crypto API.
import { sha256 } from "@agentdoor/core";

const hash = await sha256("hello world");
console.log(hash); // Hex-encoded hash string
input
string
required
String to hash.
returns
Promise<string>
Hex-encoded SHA-256 hash.
Implementation: Uses crypto.subtle.digest() when available. Falls back to synchronous truncated-SHA-512 in environments without Web Crypto.

sha256Sync

Compute SHA-256 hash synchronously.
import { sha256Sync } from "@agentdoor/core";

const hash = sha256Sync("hello world");
input
string
required
String to hash.
returns
string
Hex-encoded hash string.
Note: Uses truncated SHA-512 from tweetnacl as a fallback when true SHA-256 is not available.

Encoding Utilities

bytesToHex

Convert a Uint8Array to a hex string.
import { bytesToHex } from "@agentdoor/core";

const bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
const hex = bytesToHex(bytes);
console.log(hex); // "48656c6c6f"

hexToBytes

Convert a hex string to a Uint8Array.
import { hexToBytes } from "@agentdoor/core";

const bytes = hexToBytes("48656c6c6f");
const bytes2 = hexToBytes("0x48656c6c6f"); // Also accepts 0x prefix

Base64 Encoding

Re-exported from tweetnacl-util for convenience:
import { encodeBase64, decodeBase64, decodeUTF8 } from "@agentdoor/core";

const encoded = encodeBase64(new Uint8Array([1, 2, 3]));
const decoded = decodeBase64(encoded);
const utf8Bytes = decodeUTF8("hello");

Complete Example: Agent Registration Flow

import {
  generateKeypair,
  generateAgentId,
  generateApiKey,
  generateNonce,
  signChallenge,
  verifySignature,
  hashApiKey
} from "@agentdoor/core";

// 1. Agent generates keypair locally
const { publicKey, secretKey } = generateKeypair();

// 2. Server receives registration request
const agentId = generateAgentId();
const nonce = generateNonce();
const message = `agentdoor:register:${agentId}:${Date.now()}:${nonce}`;

// 3. Server sends challenge to agent
return { agent_id: agentId, challenge: { nonce, message } };

// 4. Agent signs the challenge
const signature = signChallenge(message, secretKey);

// 5. Server verifies signature
const isValid = verifySignature(message, signature, publicKey);

if (!isValid) {
  throw new Error("Invalid signature");
}

// 6. Server generates and stores API key
const apiKey = generateApiKey("live");
const apiKeyHash = hashApiKey(apiKey);

await store.createAgent({
  id: agentId,
  publicKey,
  apiKeyHash,
  // ...
});

// 7. Return API key to agent (only once!)
return { agent_id: agentId, api_key: apiKey };

Security Best Practices

  1. Never log secret keys or API keys - Only log agent IDs and public keys
  2. Store only hashed API keys - Use hashApiKey() before storage
  3. Return API keys only once - At registration, then never again
  4. Validate key lengths - All functions validate input lengths automatically
  5. Use Ed25519 for new implementations - Faster and more modern than secp256k1
  6. Keep nonces random - Use generateNonce(), never hardcode or reuse

Build docs developers (and LLMs) love