import * as Crypto from 'expo-crypto'; /** * Cryptographic utilities for BLE communication * Provides proper MD5 hashing to match Android implementation */ export class CryptoUtils { /** * Calculate MD5 hash of data * @param data - ArrayBuffer or string to hash * @returns Promise - MD5 hash as hexadecimal string */ public static async calculateMD5(data: ArrayBuffer | string): Promise { let dataString: string; if (data instanceof ArrayBuffer) { // Convert ArrayBuffer to string const uint8Array = new Uint8Array(data); dataString = Array.from(uint8Array) .map(byte => String.fromCharCode(byte)) .join(''); } else { dataString = data; } const hash = await Crypto.digestStringAsync( Crypto.CryptoDigestAlgorithm.MD5, dataString ); return hash.toUpperCase(); } /** * Calculate MD5 hash of a file * @param file - File object to hash * @returns Promise - MD5 hash as hexadecimal string */ public static async calculateFileMD5(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = async (event) => { try { const arrayBuffer = event.target?.result as ArrayBuffer; const hash = await this.calculateMD5(arrayBuffer); resolve(hash); } catch (error) { reject(error); } }; reader.onerror = () => { reject(new Error('Failed to read file for MD5 calculation')); }; reader.readAsArrayBuffer(file); }); } /** * Get partial MD5 hash (substring) to match Android implementation * Android uses substring(8, 24) for file transfers * @param data - Data to hash * @returns Promise - Partial MD5 hash */ public static async calculatePartialMD5(data: ArrayBuffer | string): Promise { const fullHash = await this.calculateMD5(data); return fullHash.substring(8, 24); // Match Android implementation } /** * Get partial file MD5 hash * @param file - File to hash * @returns Promise - Partial MD5 hash */ public static async calculatePartialFileMD5(file: File): Promise { const fullHash = await this.calculateFileMD5(file); return fullHash.substring(8, 24); // Match Android implementation } }