UNPKG

@instun/sm2-multikey

Version:

A JavaScript library for generating and working with SM2Multikey key pairs and digital signatures. Compatible with both Node.js and fibjs runtimes.

668 lines (636 loc) 20.3 kB
/*! * Copyright (c) 2024 Instun, Inc. All rights reserved. */ /** * @fileoverview Error Handling System for SM2 Cryptographic Operations * * This module provides a comprehensive error handling system for the SM2 * cryptographic library. It implements a hierarchy of error classes and * utilities for consistent error creation and formatting. * * Key Features: * - Hierarchical error classes for different types of operations * - Standardized error codes and messages * - Support for error chaining with cause tracking * - Detailed error information with optional parameters * * Error Categories: * - Argument validation errors * - Format and encoding errors * - Key management errors * - Signature operation errors * - Verification process errors * - Import/Export operation errors * - State management errors * * Security Considerations: * - Error messages avoid exposing sensitive information * - Consistent error handling across operations * - Proper error propagation and cause tracking * - Secure error recovery mechanisms * * Performance Considerations: * - Efficient error object creation * - Minimal memory footprint * - Optimized error message formatting * - Lazy parameter evaluation * * Best Practices: * 1. Always use appropriate error types for different scenarios * 2. Include relevant context in error messages * 3. Chain errors to preserve the original cause * 4. Handle errors at appropriate levels * 5. Log errors with sufficient detail for debugging * 6. Avoid exposing sensitive information in errors * * Usage Examples: * ```javascript * // Basic error creation * throw new KeyError('Invalid key format'); * * // Error with additional context * throw new FormatError('Invalid encoding', { * code: ErrorCodes.ERR_FORMAT_ENCODE, * encoding: 'base58' * }); * * // Error chaining * try { * // ... some operation * } catch (error) { * throw createError( * ErrorCodes.ERR_KEY_INVALID, * { keyId: 'abc123' }, * error * ); * } * * // Error with formatted message * const msg = formatErrorMessage( * 'Invalid key type: {type}', * { type: 'RSA' } * ); * * // Complete error handling example * try { * const key = await importKey(data); * } catch (error) { * if (error instanceof FormatError) { * // Handle format errors * console.error('Invalid key format:', error.message); * } else if (error instanceof KeyError) { * // Handle key errors * console.error('Key operation failed:', error.message); * } else { * // Handle other errors * console.error('Unexpected error:', error); * } * } * ``` * * Error Recovery Strategies: * 1. Validation Errors * - Retry with corrected input * - Provide feedback to user * - Log validation failures * * 2. Format Errors * - Attempt format conversion * - Try alternative formats * - Request correct format * * 3. Key Errors * - Attempt key regeneration * - Use backup keys * - Request new keys * * 4. Operation Errors * - Retry with backoff * - Use alternative methods * - Fail gracefully * * Related Standards: * - Error Handling Best Practices (RFC 7807) * - Cryptographic Error Reporting * - Security Considerations (RFC 3552) * * @module errors */ /** * Base error class for all SM2 cryptographic operations * * This class extends the native Error class and adds support for: * - Error codes for programmatic handling * - Error cause tracking for debugging * - Consistent error message formatting * * Best Practices: * 1. Always include a descriptive message * 2. Use appropriate error codes * 3. Chain errors when appropriate * 4. Include relevant context * * @class * @extends Error */ export class SM2Error extends Error { /** * Create a new SM2 error instance * * @param {string} message - Human-readable error description * @param {object} [options] - Additional error options * @param {string} [options.code] - Error code for programmatic handling * @param {Error} [options.cause] - Original error that caused this error * * @example * // Basic error * throw new SM2Error('Operation failed'); * * // Error with code * throw new SM2Error('Invalid key', { * code: 'ERR_KEY_INVALID' * }); * * // Error with cause * try { * // ... operation * } catch (error) { * throw new SM2Error('Key validation failed', { * code: 'ERR_KEY_INVALID', * cause: error * }); * } */ constructor(message, { code, cause } = {}) { super(message, { cause }); this.name = this.constructor.name; this.code = code; } } /** * Error class for key-related operations * * Used when operations involving cryptographic keys fail, such as: * - Key generation * - Key validation * - Key format conversion * - Key pair operations * * @extends SM2Error */ export class KeyError extends SM2Error { /** * Create Key Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause */ constructor(message, options) { super(message, { code: 'ERR_KEY', ...options }); } } /** * Error class for signature operations * * Used when digital signature operations fail, such as: * - Signature creation * - Signature format validation * - Signature encoding/decoding * * @extends SM2Error */ export class SignatureError extends SM2Error { /** * Create Signature Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause */ constructor(message, options) { super(message, { code: 'ERR_SIGNATURE', ...options }); } } /** * Error class for verification operations * * Used when signature verification fails, including: * - Signature validation * - Verification method checks * - Proof purpose validation * - Chain of trust verification * * @extends SM2Error */ export class VerificationError extends SM2Error { /** * Create Verification Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause */ constructor(message, options) { super(message, { code: 'ERR_VERIFICATION', ...options }); } } /** * Error class for format-related operations * * Used when data format operations fail, such as: * - Format validation * - Format conversion * - Encoding/decoding * - Schema validation * * @extends SM2Error */ export class FormatError extends SM2Error { /** * Create Format Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause */ constructor(message, options) { super(message, { code: 'ERR_FORMAT', ...options }); } } /** * Error class for argument validation * * Used when function arguments are invalid, including: * - Missing required arguments * - Invalid argument types * - Invalid argument values * - Invalid argument combinations * * @extends SM2Error */ export class ArgumentError extends SM2Error { /** * Create Argument Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause */ constructor(message, options) { super(message, { code: 'ERR_ARGUMENT', ...options }); } } /** * Error class for general operations * * Used for operational errors, such as: * - Unsupported operations * - Operation timeouts * - Resource constraints * - Internal errors * * @extends SM2Error */ export class OperationError extends SM2Error { /** * Create Operation Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause */ constructor(message, options) { super(message, { code: 'ERR_OPERATION', ...options }); } } /** * Error class for key import operations * * Used when key import operations fail, including: * - Format parsing errors * - Invalid key data * - Unsupported key types * - Import validation failures * * @extends SM2Error */ export class ImportError extends SM2Error { /** * Create Key Import Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause * @param {object} [options.details] - Error details */ constructor(message, options) { super(message, { code: 'ERR_IMPORT', ...options }); this.details = options?.details; } } /** * Error class for key export operations * * Used when key export operations fail, including: * - Format conversion errors * - Export restrictions * - Unsupported formats * - Export validation failures * * @extends SM2Error */ export class ExportError extends SM2Error { /** * Create Key Export Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause * @param {object} [options.details] - Error details */ constructor(message, options) { super(message, { code: 'ERR_EXPORT', ...options }); this.details = options?.details; } } /** * Error class for encoding operations * * Used when data encoding operations fail, such as: * - Base58 encoding * - DER encoding * - JWK encoding * - Format-specific encoding * * @extends SM2Error */ export class EncodingError extends SM2Error { /** * Create Encoding Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause * @param {string} [options.encoding] - Encoding type */ constructor(message, options) { super(message, { code: 'ERR_ENCODING', ...options }); this.encoding = options?.encoding; } } /** * Error class for state-related errors * * Used when operations fail due to invalid state, such as: * - Invalid operation sequence * - Missing prerequisites * - State validation failures * - Inconsistent state * * @extends SM2Error */ export class StateError extends SM2Error { /** * Create State Error * @param {string} message - Error message * @param {object} options - Options * @param {string} [options.code] - Error code * @param {Error} [options.cause] - Error cause * @param {string} [options.state] - Current state */ constructor(message, options) { super(message, { code: 'ERR_STATE', ...options }); this.state = options?.state; } } /** * Comprehensive list of error codes * * These codes are used for programmatic error handling and * consistent error reporting across the library. * * Categories: * - Argument errors: Invalid or missing arguments * - Format errors: Data format and encoding issues * - Key errors: Key management and operations * - Signature errors: Signature creation and validation * - Verification errors: Signature verification process * - Import/Export errors: Key import/export operations * - Encoding errors: Data encoding/decoding operations * - Operation errors: General operational issues * - State errors: Invalid state conditions * * @enum {string} */ export const ErrorCodes = { // Argument errors ERR_ARGUMENT_INVALID: 'ERR_ARGUMENT_INVALID', // Invalid argument ERR_ARGUMENT_MISSING: 'ERR_ARGUMENT_MISSING', // Missing argument // Format errors ERR_FORMAT_INVALID: 'ERR_FORMAT_INVALID', // General format error ERR_FORMAT_INPUT: 'ERR_FORMAT_INPUT', // Input validation error ERR_FORMAT_TYPE: 'ERR_FORMAT_TYPE', // Type error ERR_FORMAT_LENGTH: 'ERR_FORMAT_LENGTH', // Length error ERR_FORMAT_VALUE: 'ERR_FORMAT_VALUE', // Value error ERR_FORMAT_OID: 'ERR_FORMAT_OID', // Object identifier error ERR_FORMAT_ENCODE: 'ERR_FORMAT_ENCODE', // Encoding error ERR_FORMAT_MULTIKEY: 'ERR_FORMAT_MULTIKEY', // Multikey format error ERR_FORMAT_MULTIBASE: 'ERR_FORMAT_MULTIBASE', // Multibase format error // Key errors ERR_KEY_INVALID: 'ERR_KEY_INVALID', // Invalid key ERR_KEY_NOT_FOUND: 'ERR_KEY_NOT_FOUND', // Key not found ERR_KEY_PAIR: 'ERR_KEY_PAIR', // Key pair error ERR_KEY_FORMAT: 'ERR_KEY_FORMAT', // Key format error ERR_KEY_GENERATION: 'ERR_KEY_GENERATION', // Key generation error // Signature errors ERR_SIGNATURE_INVALID: 'ERR_SIGNATURE_INVALID', // Invalid signature // Verification errors ERR_VERIFICATION_FAILED: 'ERR_VERIFICATION_FAILED', // Verification failed // Import/Export errors ERR_IMPORT_FAILED: 'ERR_IMPORT_FAILED', // Import failed ERR_EXPORT_FAILED: 'ERR_EXPORT_FAILED', // Export failed // Operation errors ERR_OPERATION_INVALID: 'ERR_OPERATION_INVALID' // Invalid operation }; /** * Error message templates for error codes * * These templates support parameter substitution using {param} * syntax. Parameters are replaced when formatting error messages. * * Template Rules: * - Use {param} for parameter placeholders * - Keep messages concise but informative * - Avoid exposing sensitive information * - Use consistent terminology * * @enum {string} */ export const ErrorMessages = { [ErrorCodes.ERR_ARGUMENT_INVALID]: 'Invalid argument: {details}', // Invalid argument: {details} [ErrorCodes.ERR_ARGUMENT_MISSING]: 'Missing required argument: {argument}', // Missing argument: {argument} [ErrorCodes.ERR_FORMAT_INVALID]: 'Invalid format: {details}', // Invalid format: {details} [ErrorCodes.ERR_FORMAT_INPUT]: 'Invalid input: {details}', // Invalid input: {details} [ErrorCodes.ERR_FORMAT_TYPE]: 'Invalid type: {details}', // Invalid type: {details} [ErrorCodes.ERR_FORMAT_LENGTH]: 'Invalid length: {details}', // Invalid length: {details} [ErrorCodes.ERR_FORMAT_VALUE]: 'Invalid value: {details}', // Invalid value: {details} [ErrorCodes.ERR_FORMAT_OID]: 'Invalid object identifier: {details}', // Invalid object identifier: {details} [ErrorCodes.ERR_FORMAT_ENCODE]: 'Encoding error: {details}', // Encoding error: {details} [ErrorCodes.ERR_FORMAT_MULTIKEY]: 'Invalid multikey format: {details}', // Invalid multikey format: {details} [ErrorCodes.ERR_FORMAT_MULTIBASE]: 'Invalid multibase format: {details}', // Invalid multibase format: {details} [ErrorCodes.ERR_KEY_INVALID]: 'Invalid key: {details}', // Invalid key: {details} [ErrorCodes.ERR_KEY_NOT_FOUND]: 'Key not found: {id}', // Key not found: {id} [ErrorCodes.ERR_KEY_PAIR]: 'Invalid key pair: {details}', // Invalid key pair: {details} [ErrorCodes.ERR_KEY_FORMAT]: 'Invalid key format: {details}', // Invalid key format: {details} [ErrorCodes.ERR_KEY_GENERATION]: 'Key generation failed: {details}', // Key generation failed: {details} [ErrorCodes.ERR_SIGNATURE_INVALID]: 'Invalid signature: {details}', // Invalid signature: {details} [ErrorCodes.ERR_VERIFICATION_FAILED]: 'Verification failed: {details}', // Verification failed: {details} [ErrorCodes.ERR_IMPORT_FAILED]: 'Import failed: {details}', // Import failed: {details} [ErrorCodes.ERR_EXPORT_FAILED]: 'Export failed: {details}', // Export failed: {details} [ErrorCodes.ERR_OPERATION_INVALID]: 'Invalid operation: {details}' // Invalid operation: {details} }; /** * Format an error message by replacing parameters in a template * * This function replaces placeholders in the format {param} with * corresponding values from the params object. If a parameter is * not found in the params object, the placeholder is replaced * with an empty string. * * Best Practices: * 1. Use descriptive parameter names * 2. Include all relevant context * 3. Keep messages concise but informative * 4. Avoid sensitive information * * @param {string} template - Message template with {param} placeholders * @param {object} [params={}] - Parameters to substitute in template * @returns {string} Formatted error message * * @example * // Basic usage * const msg = formatErrorMessage( * 'Invalid key type: {type}', * { type: 'RSA' } * ); * * // Multiple parameters * const msg = formatErrorMessage( * 'Key {id} not found in format {format}', * { id: 'abc123', format: 'JWK' } * ); * * // With optional parameters * const msg = formatErrorMessage( * 'Operation failed: {details}', * { details: error.message } * ); */ export function formatErrorMessage(template, params = {}) { if (typeof template !== 'string') { return 'Unknown error'; } return template.replace(/\{(\w+)\}/g, (match, key) => { return params[key] !== undefined ? String(params[key]) : ''; }); } /** * Create an appropriate error instance based on error code * * This factory function creates the most appropriate error instance * based on the error code, applying proper inheritance and adding * relevant context information. * * Features: * - Automatic error class selection * - Message formatting with parameters * - Error cause tracking * - Additional context preservation * * Best Practices: * 1. Use specific error codes * 2. Include relevant context * 3. Chain errors appropriately * 4. Handle errors at proper level * * Error Selection Process: * 1. Determine error category from code * 2. Select appropriate error class * 3. Format error message * 4. Add context and cause * * @param {string} code - Error code from ErrorCodes enum * @param {object} [params={}] - Additional error parameters * @param {Error} [cause] - Original error that caused this error * @returns {SM2Error} Appropriate error instance * * @example * // Basic usage * throw createError( * ErrorCodes.ERR_KEY_INVALID, * { keyId: 'abc123' } * ); * * // With error chaining * try { * await verifySignature(data); * } catch (error) { * throw createError( * ErrorCodes.ERR_VERIFICATION_FAILED, * { signatureId: 'sig123' }, * error * ); * } * * // With detailed context * throw createError( * ErrorCodes.ERR_FORMAT_INVALID, * { * format: 'JWK', * field: 'kty', * expected: 'EC', * received: 'RSA' * } * ); */ export function createError(code, params = {}, cause) { const message = formatErrorMessage(ErrorMessages[code] || code, params); const errorOptions = { code, ...params }; if (cause) { errorOptions.cause = cause; } switch (code) { case ErrorCodes.ERR_ARGUMENT_INVALID: case ErrorCodes.ERR_ARGUMENT_MISSING: return new ArgumentError(message, errorOptions); case ErrorCodes.ERR_FORMAT_INVALID: case ErrorCodes.ERR_FORMAT_INPUT: case ErrorCodes.ERR_FORMAT_TYPE: case ErrorCodes.ERR_FORMAT_LENGTH: case ErrorCodes.ERR_FORMAT_VALUE: case ErrorCodes.ERR_FORMAT_OID: case ErrorCodes.ERR_FORMAT_ENCODE: case ErrorCodes.ERR_FORMAT_MULTIKEY: case ErrorCodes.ERR_FORMAT_MULTIBASE: return new FormatError(message, errorOptions); case ErrorCodes.ERR_KEY_INVALID: case ErrorCodes.ERR_KEY_NOT_FOUND: case ErrorCodes.ERR_KEY_PAIR: case ErrorCodes.ERR_KEY_FORMAT: case ErrorCodes.ERR_KEY_GENERATION: return new KeyError(message, errorOptions); case ErrorCodes.ERR_SIGNATURE_INVALID: return new SignatureError(message, errorOptions); case ErrorCodes.ERR_VERIFICATION_FAILED: return new VerificationError(message, errorOptions); case ErrorCodes.ERR_IMPORT_FAILED: return new ImportError(message, errorOptions); case ErrorCodes.ERR_EXPORT_FAILED: return new ExportError(message, errorOptions); case ErrorCodes.ERR_OPERATION_INVALID: return new OperationError(message, errorOptions); default: return new SM2Error(message, errorOptions); } }