nostr-nsec-seedphrase
Version:
A comprehensive TypeScript library for Nostr key management with BIP39 seed phrases, supporting both ESM and CommonJS. Implements NIP-01, NIP-06, NIP-19, and NIP-26 with key generation, event signing, bech32 encoding/decoding, and secure cryptographic ope
117 lines (116 loc) • 4.22 kB
JavaScript
/**
* @module nips/nip-26
* @description NIP-26 Delegated Event Signing implementation
* @see https://github.com/nostr-protocol/nips/blob/master/26.md
*/
import { schnorr } from '@noble/curves/secp256k1';
import { sha256 } from '@noble/hashes/sha256';
import { bytesToHex } from '@noble/hashes/utils';
import { logger } from '../utils/logger.js';
/**
* Creates a delegation token string
* @param conditions - Delegation conditions
* @returns Delegation token string
*/
function createDelegationString(delegator, delegatee, conditions) {
const parts = ['nostr', 'delegation', delegator, delegatee];
if (conditions.kinds) {
parts.push(`kinds=${conditions.kinds.join(',')}`);
}
if (conditions.since) {
parts.push(`created_at>${conditions.since}`);
}
if (conditions.until) {
parts.push(`created_at<${conditions.until}`);
}
return parts.join(':');
}
/**
* Creates a delegation token
* @param delegatee - Public key of the delegatee
* @param conditions - Delegation conditions
* @param delegatorPrivateKey - Private key of the delegator
* @returns Delegation token
*/
export async function createDelegation(delegatee, conditions, delegatorPrivateKey) {
try {
// Get delegator's public key
const delegator = bytesToHex(schnorr.getPublicKey(delegatorPrivateKey));
// Create delegation string
const tokenString = createDelegationString(delegator, delegatee, conditions);
// Sign the token
const messageHash = sha256(new TextEncoder().encode(tokenString));
const signature = await schnorr.sign(messageHash, delegatorPrivateKey);
logger.log('Created delegation token');
return {
delegator,
delegatee,
conditions,
signature: bytesToHex(signature),
};
}
catch (error) {
logger.error('Failed to create delegation token:', error?.toString());
throw error;
}
}
/**
* Verifies a delegation token
* @param token - The delegation token to verify
* @param now - Current Unix timestamp in seconds (optional, defaults to current time)
* @returns Result of the validation
*/
async function verifyDelegation(token, now) {
try {
const tokenObject = JSON.parse(token);
// Check conditions
if (now) {
if (tokenObject.conditions.since && now < tokenObject.conditions.since) {
return {
isValid: false,
error: 'Event timestamp before delegation validity period',
};
}
if (tokenObject.conditions.until && now > tokenObject.conditions.until) {
return {
isValid: false,
error: 'Event timestamp after delegation validity period',
};
}
}
// Verify signature
const tokenString = createDelegationString(tokenObject.delegator, tokenObject.delegatee, tokenObject.conditions);
const messageHash = sha256(new TextEncoder().encode(tokenString));
const isValid = await schnorr.verify(tokenObject.signature, messageHash, tokenObject.delegator);
return {
isValid,
error: isValid ? undefined : 'Invalid delegation signature',
};
}
catch (error) {
logger.error('Failed to verify delegation:', error?.toString());
return {
isValid: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}
/**
* Checks if a delegation token is currently valid
* @param token - The delegation token to check
* @param now - Current Unix timestamp in seconds (optional, defaults to current time)
* @returns True if the delegation is valid
*/
export async function isValidDelegation(token, now) {
const result = await verifyDelegation(token, now);
return result.isValid;
}
/**
* Gets the expiration time of a delegation token
* @param token - Delegation token
* @returns Expiration timestamp or undefined if no expiration
*/
export function getDelegationExpiry(token) {
const tokenObject = JSON.parse(token);
return tokenObject.conditions.until;
}