Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | 20x 20x 4x 4x 1x 1x 3x 3x 3x 3x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 100x 3x 2x 1x 1x 4x 1x 3x 1x 2x 1x | import { CloseCallData } from '../../models/close-call-data'; import { CallData } from '../../models/infrasctructure/call-data'; import { CallPayload } from '../../models/infrasctructure/call-payload'; import { CallRequest } from '../../models/infrasctructure/call-request'; import { Base64 } from '../../utils/base64'; import { Cryptography } from '../../utils/cryptography'; import { Logger } from '../../utils/logger'; import { newError } from '../../utils/new-error'; import { Utf8 } from '../../utils/utf8'; import { SessionService } from '../session-service'; import { TimeService } from '../time-service'; /** * Generic interface for handling different types of call requests. * Provides methods for parsing, validating, and processing call requests. * * @template TypeData - The specific type of call data this handler processes */ export interface CallHandler<TypeData extends CallData> { /** * Parses a raw call payload into a typed call request. * * @param payloadData - The raw call payload to parse * @returns A properly typed call request object */ parse(payloadData: CallPayload): CallRequest<TypeData>; /** * Validates a call request to ensure it is legitimate and intended for this recipient. * Performs timestamp validation, recipient validation, and signature verification. * * @param request - The call request to validate * @returns Boolean indicating if the request is valid */ validate(request: CallRequest<TypeData>): boolean; /** * Processes a validated call request and performs the necessary actions. * This method must be implemented by specific call handlers. * * @param request - The validated call request to handle * @returns Promise resolving to a boolean indicating if handling was successful */ handle(request: CallRequest<TypeData>): Promise<boolean>; } /** * Factory function that creates a base implementation of the CallHandler interface. * Provides common functionality for parsing and validating call requests. * * @template TypeData - The specific type of call data this handler processes * @param logger - Logger instance for error and debugging information * @param timeService - Service for managing time synchronization * @param sessionService - Service for accessing session information * @param base64 - Utility for Base64 encoding/decoding * @param utf8 - Utility for UTF-8 encoding/decoding * @param cryptography - Cryptography service for signature verification * @returns A partial implementation of the CallHandler interface */ export function getCallHandler<TypeData extends CloseCallData>( logger: Logger, timeService: TimeService, sessionService: SessionService, base64: Base64, utf8: Utf8, cryptography: Cryptography, ): CallHandler<TypeData> { function validateTimestamp(request: CallRequest<TypeData>): boolean { const delta = request.b.b - timeService.serverTime; if (Math.abs(delta) > 5 * 1000) { logger.debug( `[${request.a}-call-handler] Request timestamp is more than 5 seconds stale (delta ${delta}ms).`, ); return false; } logger.debug(`[${request.a}-call-handler] Request timestamp is valid (delta ${delta}ms).`); return true; } function validatePublicKey(request: CallRequest<TypeData>): boolean { const isIntendedForMe = sessionService.signingPublicKeyBase64 === request.b.c; if (!isIntendedForMe) { logger.debug(`[${request.a}-call-handler] Message is not intended for this user.`); return false; } logger.debug(`[${request.a}-call-handler] Message is intended for this user.`); return true; } function validateSignature(request: CallRequest<TypeData>): boolean { const message = JSON.stringify(request.b); const messageBytes = utf8.decode(message); const signature = base64.decode(request.c); const singingPublicKeyBase64 = request.b.a; const signingPublicKey = base64.decode(singingPublicKeyBase64); const isSignatureVerified = cryptography.verifySignature(messageBytes, signature, signingPublicKey); if (!isSignatureVerified) { logger.debug(`[${request.a}-call-handler] Signature is not valid.`); return false; } logger.debug(`[${request.a}-call-handler] Signature is valid.`); return true; } return { parse(payloadData: CallPayload): CallRequest<TypeData> { const data = JSON.parse(payloadData.b) as TypeData; if (!data) { throw newError( logger, `[${payloadData.a}-call-handler] Unable to parse call data for call '${payloadData.a}'. Data: ${payloadData.b}`, ); } return { a: payloadData.a, b: data, c: payloadData.c, }; }, validate(request: CallRequest<TypeData>): boolean { if (!validateTimestamp(request)) { return false; } if (!validatePublicKey(request)) { return false; } return validateSignature(request); }, handle(request: CallRequest<TypeData>): Promise<boolean> { throw newError(logger, `[${request.a}-call-handler] Base handle method is not implemented.`); }, }; } |