Примеры Криптографии Web TypeScript

Примеры криптографии Web TypeScript включая вычисление хэша, симметричное шифрование и кодирование Base64

💻 Вычисление Хэша typescript

🟢 simple ⭐⭐⭐

Вычислять хэши MD5, SHA-1, SHA-256, SHA-384, SHA-512 используя Web Crypto API

⏱️ 20 min 🏷️ typescript, web, cryptography, hash
Prerequisites: Basic TypeScript, Web Crypto API
// Web TypeScript Hash Calculation Examples
// Using Web Crypto API for secure hash computation

// 1. SHA Hash Calculator
class SHAHashCalculator {
  // Calculate SHA-256 hash
  async sha256(message: string): Promise<string> {
    const encoder = new TextEncoder();
    const data = encoder.encode(message);
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    return this.bufferToHex(hashBuffer);
  }

  // Calculate SHA-384 hash
  async sha384(message: string): Promise<string> {
    const encoder = new TextEncoder();
    const data = encoder.encode(message);
    const hashBuffer = await crypto.subtle.digest('SHA-384', data);
    return this.bufferToHex(hashBuffer);
  }

  // Calculate SHA-512 hash
  async sha512(message: string): Promise<string> {
    const encoder = new TextEncoder();
    const data = encoder.encode(message);
    const hashBuffer = await crypto.subtle.digest('SHA-512', data);
    return this.bufferToHex(hashBuffer);
  }

  // Calculate SHA-1 hash (for legacy compatibility)
  async sha1(message: string): Promise<string> {
    const encoder = new TextEncoder();
    const data = encoder.encode(message);
    const hashBuffer = await crypto.subtle.digest('SHA-1', data);
    return this.bufferToHex(hashBuffer);
  }

  // Convert ArrayBuffer to hex string
  private bufferToHex(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    return Array.from(bytes)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }
}

// 2. File Hash Calculator
class FileHashCalculator {
  private shaCalculator = new SHAHashCalculator();

  // Calculate hash of file
  async hashFile(file: File, algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512' = 'SHA-256'): Promise<string> {
    const arrayBuffer = await file.arrayBuffer();
    const hashBuffer = await crypto.subtle.digest(algorithm, arrayBuffer);
    return this.bufferToHex(hashBuffer);
  }

  // Calculate hash with progress
  async hashFileWithProgress(
    file: File,
    algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512' = 'SHA-256',
    onProgress: (progress: number) => void
  ): Promise<string> {
    const chunkSize = 1024 * 1024; // 1MB chunks
    let offset = 0;

    // For progress tracking, we'll read in chunks
    while (offset < file.size) {
      const end = Math.min(offset + chunkSize, file.size);
      offset = end;
      const progress = Math.floor((offset / file.size) * 100);
      onProgress(progress);
    }

    // Actually hash the file
    return this.hashFile(file, algorithm);
  }

  // Calculate multiple hashes
  async hashMultipleAlgorithms(file: File): Promise<Record<string, string>> {
    const arrayBuffer = await file.arrayBuffer();

    const algorithms = ['SHA-256', 'SHA-384', 'SHA-512'] as const;
    const hashes: Record<string, string> = {};

    for (const algo of algorithms) {
      const hashBuffer = await crypto.subtle.digest(algo, arrayBuffer);
      hashes[algo] = this.bufferToHex(hashBuffer);
    }

    return hashes;
  }

  private bufferToHex(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    return Array.from(bytes)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }
}

// 3. HMAC Calculator
class HMACCalculator {
  // Calculate HMAC-SHA256
  async hmacSha256(message: string, key: string): Promise<string> {
    const encoder = new TextEncoder();
    const keyData = encoder.encode(key);
    const messageData = encoder.encode(message);

    const cryptoKey = await crypto.subtle.importKey(
      'raw',
      keyData,
      { name: 'HMAC', hash: 'SHA-256' },
      false,
      ['sign']
    );

    const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
    return this.bufferToHex(signature);
  }

  // Calculate HMAC-SHA512
  async hmacSha512(message: string, key: string): Promise<string> {
    const encoder = new TextEncoder();
    const keyData = encoder.encode(key);
    const messageData = encoder.encode(message);

    const cryptoKey = await crypto.subtle.importKey(
      'raw',
      keyData,
      { name: 'HMAC', hash: 'SHA-512' },
      false,
      ['sign']
    );

    const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData);
    return this.bufferToHex(signature);
  }

  // Verify HMAC
  async verifyHMAC(
    message: string,
    key: string,
    signature: string,
    algorithm: 'SHA-256' | 'SHA-512' = 'SHA-256'
  ): Promise<boolean> {
    const encoder = new TextEncoder();
    const keyData = encoder.encode(key);
    const messageData = encoder.encode(message);

    const cryptoKey = await crypto.subtle.importKey(
      'raw',
      keyData,
      { name: 'HMAC', hash: algorithm },
      false,
      ['verify']
    );

    const signatureBytes = this.hexToBuffer(signature);

    const isValid = await crypto.subtle.verify(
      'HMAC',
      cryptoKey,
      signatureBytes,
      messageData
    );

    return isValid;
  }

  private bufferToHex(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    return Array.from(bytes)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }

  private hexToBuffer(hex: string): Uint8Array {
    const bytes = new Uint8Array(hex.length / 2);
    for (let i = 0; i < bytes.length; i++) {
      bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
    }
    return bytes;
  }
}

// 4. Hash Comparison Utility
class HashComparison {
  // Constant-time comparison to prevent timing attacks
  static constantTimeCompare(hash1: string, hash2: string): boolean {
    if (hash1.length !== hash2.length) {
      return false;
    }

    let result = 0;
    for (let i = 0; i < hash1.length; i++) {
      result |= hash1.charCodeAt(i) ^ hash2.charCodeAt(i);
    }

    return result === 0;
  }

  // Compare hashes (case-insensitive)
  static compare(hash1: string, hash2: string): boolean {
    return hash1.toLowerCase() === hash2.toLowerCase();
  }

  // Find matching hash in list
  static findMatch(targetHash: string, hashList: string[]): string | null {
    const normalized = targetHash.toLowerCase();

    for (const hash of hashList) {
      if (hash.toLowerCase() === normalized) {
        return hash;
      }
    }

    return null;
  }
}

// 5. Password Hasher (PBKDF2)
class PasswordHasher {
  // Hash password with PBKDF2
  async hashPassword(
    password: string,
    salt?: string,
    iterations: number = 100000
  ): Promise<{ hash: string; salt: string }> {
    // Generate salt if not provided
    const saltBytes = salt
      ? this.hexToBuffer(salt)
      : crypto.getRandomValues(new Uint8Array(16));

    const encoder = new TextEncoder();
    const passwordData = encoder.encode(password);

    const importedKey = await crypto.subtle.importKey(
      'raw',
      passwordData,
      { name: 'PBKDF2' },
      false,
      ['deriveBits']
    );

    const hashBuffer = await crypto.subtle.deriveBits(
      {
        name: 'PBKDF2',
        salt: saltBytes,
        iterations: iterations,
        hash: 'SHA-256'
      },
      importedKey,
      256
    );

    return {
      hash: this.bufferToHex(hashBuffer),
      salt: this.bufferToHex(saltBytes)
    };
  }

  // Verify password
  async verifyPassword(
    password: string,
    hash: string,
    salt: string,
    iterations: number = 100000
  ): Promise<boolean> {
    const { hash: computedHash } = await this.hashPassword(password, salt, iterations);
    return HashComparison.constantTimeCompare(hash, computedHash);
  }

  // Hash password with custom parameters
  async hashPasswordAdvanced(
    password: string,
    options: {
      saltLength?: number;
      iterations?: number;
      hashLength?: number;
    } = {}
  ): Promise<{ hash: string; salt: string; iterations: number }> {
    const {
      saltLength = 16,
      iterations = 100000,
      hashLength = 32
    } = options;

    const saltBytes = crypto.getRandomValues(new Uint8Array(saltLength));
    const encoder = new TextEncoder();
    const passwordData = encoder.encode(password);

    const importedKey = await crypto.subtle.importKey(
      'raw',
      passwordData,
      { name: 'PBKDF2' },
      false,
      ['deriveBits']
    );

    const hashBuffer = await crypto.subtle.deriveBits(
      {
        name: 'PBKDF2',
        salt: saltBytes,
        iterations: iterations,
        hash: 'SHA-256'
      },
      importedKey,
      hashLength * 8
    );

    return {
      hash: this.bufferToHex(hashBuffer),
      salt: this.bufferToHex(saltBytes),
      iterations
    };
  }

  private bufferToHex(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    return Array.from(bytes)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }

  private hexToBuffer(hex: string): Uint8Array {
    const bytes = new Uint8Array(hex.length / 2);
    for (let i = 0; i < bytes.length; i++) {
      bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
    }
    return bytes;
  }
}

// 6. Checksum Calculator
class ChecksumCalculator {
  // Simple checksum (sum of bytes)
  calculateSimpleChecksum(data: string): number {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(data);

    let sum = 0;
    for (const byte of bytes) {
      sum += byte;
    }

    return sum & 0xFF; // Keep only lowest byte
  }

  // XOR checksum
  calculateXORChecksum(data: string): number {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(data);

    let checksum = 0;
    for (const byte of bytes) {
      checksum ^= byte;
    }

    return checksum;
  }

  // CRC-32 checksum
  calculateCRC32(data: string): number {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(data);

    let crc = 0xFFFFFFFF;

    for (const byte of bytes) {
      crc ^= byte;
      for (let i = 0; i < 8; i++) {
        if (crc & 1) {
          crc = (crc >>> 1) ^ 0xEDB88320;
        } else {
          crc = crc >>> 1;
        }
      }
    }

    return (crc ^ 0xFFFFFFFF) >>> 0;
  }

  // Adler-32 checksum
  calculateAdler32(data: string): number {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(data);

    let a = 1;
    let b = 0;

    for (const byte of bytes) {
      a = (a + byte) % 65521;
      b = (b + a) % 65521;
    }

    return (b << 16) | a;
  }
}

// 7. Hash Utility Functions
class HashUtils {
  // Generate random hash
  static async generateRandomHash(algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512' = 'SHA-256'): Promise<string> {
    const randomBytes = crypto.getRandomValues(new Uint8Array(32));
    const hashBuffer = await crypto.subtle.digest(algorithm, randomBytes);
    return HashUtils.bufferToHex(hashBuffer);
  }

  // Generate hash from multiple inputs
  static async combineHashes(hashes: string[]): Promise<string> {
    const combined = hashes.join('');
    const encoder = new TextEncoder();
    const data = encoder.encode(combined);
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    return HashUtils.bufferToHex(hashBuffer);
  }

  // Hash object (JSON)
  static async hashObject(obj: any): Promise<string> {
    const json = JSON.stringify(obj);
    const encoder = new TextEncoder();
    const data = encoder.encode(json);
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    return HashUtils.bufferToHex(hashBuffer);
  }

  private static bufferToHex(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    return Array.from(bytes)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }
}

// Usage Examples
async function demonstrateHashCalculation() {
  console.log('=== Web TypeScript Hash Calculation Examples ===\n');

  const shaCalc = new SHAHashCalculator();
  const hmacCalc = new HMACCalculator();
  const passwordHasher = new PasswordHasher();
  const checksumCalc = new ChecksumCalculator();

  // 1. SHA hashes
  console.log('--- 1. SHA Hashes ---');
  const message = 'Hello, World!';

  const sha256 = await shaCalc.sha256(message);
  const sha384 = await shaCalc.sha384(message);
  const sha512 = await shaCalc.sha512(message);

  console.log(`SHA-256: ${sha256}`);
  console.log(`SHA-384: ${sha384}`);
  console.log(`SHA-512: ${sha512}`);

  // 2. HMAC
  console.log('\n--- 2. HMAC ---');
  const key = 'secret-key';
  const hmac = await hmacCalc.hmacSha256(message, key);
  console.log(`HMAC-SHA256: ${hmac}`);

  const verified = await hmacCalc.verifyHMAC(message, key, hmac);
  console.log(`Verified: ${verified}`);

  // 3. Password hashing
  console.log('\n--- 3. Password Hashing ---');
  const password = 'MySecurePassword123!';
  const { hash: pwdHash, salt } = await passwordHasher.hashPassword(password);
  console.log(`Password hash: ${pwdHash}`);
  console.log(`Salt: ${salt}`);

  const isValid = await passwordHasher.verifyPassword(password, pwdHash, salt);
  console.log(`Password valid: ${isValid}`);

  // 4. Checksums
  console.log('\n--- 4. Checksums ---');
  const data = 'The quick brown fox jumps over the lazy dog';

  console.log(`Simple checksum: ${checksumCalc.calculateSimpleChecksum(data)}`);
  console.log(`XOR checksum: ${checksumCalc.calculateXORChecksum(data)}`);
  console.log(`CRC-32: ${checksumCalc.calculateCRC32(data)}`);
  console.log(`Adler-32: ${checksumCalc.calculateAdler32(data)}`);

  // 5. Hash utilities
  console.log('\n--- 5. Hash Utilities ---');
  const randomHash = await HashUtils.generateRandomHash();
  console.log(`Random hash: ${randomHash}`);

  const objHash = await HashUtils.hashObject({ name: 'Alice', age: 30 });
  console.log(`Object hash: ${objHash}`);

  console.log('\n=== All Hash Calculation Examples Completed ===');
}

// Export functions
export { SHAHashCalculator, FileHashCalculator, HMACCalculator, HashComparison, PasswordHasher, ChecksumCalculator, HashUtils };
export { demonstrateHashCalculation };

💻 Кодирование Base64 typescript

🟢 simple ⭐⭐

Кодировать/декодировать данные в/из формата Base64 с поддержкой Unicode и бинарных данных

⏱️ 15 min 🏷️ typescript, web, cryptography, base64
Prerequisites: Basic TypeScript, Base64 encoding
// Web TypeScript Base64 Encoding Examples
// Encoding and decoding data to/from Base64 format

// 1. Basic Base64 Encoder/Decoder
class Base64Coder {
  // Encode string to Base64
  encode(input: string): string {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(input);
    return this.bufferToBase64(bytes);
  }

  // Decode Base64 to string
  decode(input: string): string {
    const bytes = this.base64ToBuffer(input);
    const decoder = new TextDecoder();
    return decoder.decode(bytes);
  }

  // Encode Uint8Array to Base64
  encodeBytes(bytes: Uint8Array): string {
    return this.bufferToBase64(bytes);
  }

  // Decode Base64 to Uint8Array
  decodeBytes(input: string): Uint8Array {
    return this.base64ToBuffer(input);
  }

  // Convert ArrayBuffer/Base64 to Base64 string
  private bufferToBase64(buffer: ArrayBuffer | Uint8Array): string {
    const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
    let binary = '';
    for (const byte of bytes) {
      binary += String.fromCharCode(byte);
    }
    return btoa(binary);
  }

  // Convert Base64 string to Uint8Array
  private base64ToBuffer(base64: string): Uint8Array {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
  }
}

// 2. URL-Safe Base64 Encoder/Decoder
class URLSafeBase64Coder {
  // Encode to URL-safe Base64
  encode(input: string): string {
    const base64 = btoa(input);
    return this.makeURLSafe(base64);
  }

  // Decode from URL-safe Base64
  decode(input: string): string {
    const base64 = this.makeStandard(input);
    return atob(base64);
  }

  // Encode bytes to URL-safe Base64
  encodeBytes(bytes: Uint8Array): string {
    const base64 = this.bufferToBase64(bytes);
    return this.makeURLSafe(base64);
  }

  // Decode URL-safe Base64 to bytes
  decodeBytes(input: string): Uint8Array {
    const base64 = this.makeStandard(input);
    return this.base64ToBuffer(base64);
  }

  // Convert to URL-safe format
  private makeURLSafe(base64: string): string {
    return base64
      .replace(/\+/g, '-')
      .replace(///g, '_')
      .replace(/=/g, '');
  }

  // Convert to standard format
  private makeStandard(urlSafe: string): string {
    let base64 = urlSafe
      .replace(/-/g, '+')
      .replace(/_/g, '/');

    // Add padding
    while (base64.length % 4) {
      base64 += '=';
    }

    return base64;
  }

  private bufferToBase64(buffer: ArrayBuffer | Uint8Array): string {
    const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
    let binary = '';
    for (const byte of bytes) {
      binary += String.fromCharCode(byte);
    }
    return btoa(binary);
  }

  private base64ToBuffer(base64: string): Uint8Array {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
  }
}

// 3. Unicode Base64 Encoder/Decoder
class UnicodeBase64Coder {
  // Encode Unicode string to Base64
  encode(input: string): string {
    // Escape Unicode characters
    const escaped = encodeURIComponent(input);
    const binary = unescape(escaped);
    return btoa(binary);
  }

  // Decode Base64 to Unicode string
  decode(input: string): string {
    const binary = atob(input);
    const escaped = escape(binary);
    return decodeURIComponent(escaped);
  }

  // Encode using UTF-8
  encodeUTF8(input: string): string {
    const encoder = new TextEncoder();
    const bytes = encoder.encode(input);
    return this.bufferToBase64(bytes);
  }

  // Decode using UTF-8
  decodeUTF8(input: string): string {
    const bytes = this.base64ToBuffer(input);
    const decoder = new TextDecoder();
    return decoder.decode(bytes);
  }

  private bufferToBase64(buffer: ArrayBuffer | Uint8Array): string {
    const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
    let binary = '';
    for (const byte of bytes) {
      binary += String.fromCharCode(byte);
    }
    return btoa(binary);
  }

  private base64ToBuffer(base64: string): Uint8Array {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
  }
}

// 4. File Base64 Encoder/Decoder
class FileBase64Coder {
  // Convert file to Base64
  async fileToBase64(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = () => {
        const result = reader.result as string;
        // Remove data URL prefix (e.g., "data:image/png;base64,")
        const base64 = result.split(',')[1];
        resolve(base64);
      };

      reader.onerror = () => reject(reader.error);
      reader.readAsDataURL(file);
    });
  }

  // Convert file to Data URL
  async fileToDataURL(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = () => {
        resolve(reader.result as string);
      };

      reader.onerror = () => reject(reader.error);
      reader.readAsDataURL(file);
    });
  }

  // Convert Base64 to Blob
  base64ToBlob(base64: string, mimeType: string = 'application/octet-stream'): Blob {
    const bytes = this.base64ToBuffer(base64);
    return new Blob([bytes], { type: mimeType });
  }

  // Convert Base64 to File
  base64ToFile(base64: string, fileName: string, mimeType: string = 'application/octet-stream'): File {
    const blob = this.base64ToBlob(base64, mimeType);
    return new File([blob], fileName, { type: mimeType });
  }

  // Convert file to Base64 with progress
  async fileToBase64WithProgress(
    file: File,
    onProgress: (progress: number) => void
  ): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = () => {
        const result = reader.result as string;
        const base64 = result.split(',')[1];
        resolve(base64);
      };

      reader.onerror = () => reject(reader.error);

      if (reader.onprogress) {
        reader.onprogress = (event) => {
          if (event.lengthComputable) {
            const progress = Math.floor((event.loaded / event.total) * 100);
            onProgress(progress);
          }
        };
      }

      reader.readAsDataURL(file);
    });
  }

  private base64ToBuffer(base64: string): Uint8Array {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
  }
}

// 5. Image Base64 Encoder/Decoder
class ImageBase64Coder {
  private fileCoder = new FileBase64Coder();

  // Convert image file to Base64
  async imageToBase64(file: File): Promise<{
    base64: string;
    dataURL: string;
    mimeType: string;
  }> {
    const dataURL = await this.fileCoder.fileToDataURL(file);
    const base64 = dataURL.split(',')[1];
    const mimeType = file.type;

    return { base64, dataURL, mimeType };
  }

  // Convert Base64 to image element
  base64ToImage(base64: string, mimeType: string = 'image/png'): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const img = new Image();
      const dataURL = `data:${mimeType};base64,${base64}`;

      img.onload = () => resolve(img);
      img.onerror = () => reject(new Error('Failed to load image'));
      img.src = dataURL;
    });
  }

  // Resize image and convert to Base64
  async resizeAndEncode(
    file: File,
    maxWidth: number,
    maxHeight: number
  ): Promise<string> {
    const img = await this.fileCoder.fileToDataURL(file);

    return new Promise((resolve, reject) => {
      const image = new Image();
      image.onload = () => {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');

        if (!ctx) {
          reject(new Error('Failed to get canvas context'));
          return;
        }

        // Calculate new dimensions
        let width = image.width;
        let height = image.height;

        if (width > maxWidth) {
          height = (maxWidth / width) * height;
          width = maxWidth;
        }

        if (height > maxHeight) {
          width = (maxHeight / height) * width;
          height = maxHeight;
        }

        canvas.width = width;
        canvas.height = height;

        ctx.drawImage(image, 0, 0, width, height);

        const dataURL = canvas.toDataURL(file.type);
        const base64 = dataURL.split(',')[1];
        resolve(base64);
      };

      image.onerror = () => reject(new Error('Failed to load image'));
      image.src = img;
    });
  }

  // Get image dimensions from Base64
  async getImageDimensions(base64: string): Promise<{
    width: number;
    height: number;
  }> {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => {
        resolve({ width: img.width, height: img.height });
      };
      img.onerror = () => reject(new Error('Failed to load image'));
      img.src = `data:image/png;base64,${base64}`;
    });
  }
}

// 6. Base64 Utility Functions
class Base64Utils {
  // Check if string is valid Base64
  isValidBase64(str: string): boolean {
    try {
      return btoa(atob(str)) === str;
    } catch (e) {
      return false;
    }
  }

  // Get Base64 string length in bytes
  getByteLength(base64: string): number {
    const padding = (base64.match(/=/g) || []).length;
    return (base64.length * 3) / 4 - padding;
  }

  // Truncate Base64 string
  truncate(base64: string, maxLength: number): string {
    if (base64.length <= maxLength) {
      return base64;
    }
    return base64.substring(0, maxLength) + '...';
  }

  // Split Base64 into chunks
  chunk(base64: string, chunkSize: number): string[] {
    const chunks: string[] = [];
    for (let i = 0; i < base64.length; i += chunkSize) {
      chunks.push(base64.substring(i, Math.min(i + chunkSize, base64.length)));
    }
    return chunks;
  }

  // Concatenate Base64 chunks
  concat(chunks: string[]): string {
    return chunks.join('');
  }

  // Calculate Base64 checksum
  checksum(base64: string): number {
    const bytes = this.base64ToBuffer(base64);
    let sum = 0;
    for (const byte of bytes) {
      sum += byte;
    }
    return sum & 0xFF;
  }

  private base64ToBuffer(base64: string): Uint8Array {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
  }
}

// 7. Base64 Stream Encoder/Decoder
class Base64StreamCoder {
  // Encode stream to Base64
  async encodeStream(stream: ReadableStream): Promise<string> {
    const reader = stream.getReader();
    const chunks: Uint8Array[] = [];

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      chunks.push(value);
    }

    // Combine all chunks
    const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
    const combined = new Uint8Array(totalLength);

    let offset = 0;
    for (const chunk of chunks) {
      combined.set(chunk, offset);
      offset += chunk.length;
    }

    return this.bufferToBase64(combined);
  }

  // Decode Base64 to stream
  decodeStream(base64: string): ReadableStream {
    const bytes = this.base64ToBuffer(base64);

    return new ReadableStream({
      start(controller) {
        controller.enqueue(bytes);
        controller.close();
      }
    });
  }

  private bufferToBase64(buffer: ArrayBuffer | Uint8Array): string {
    const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
    let binary = '';
    for (const byte of bytes) {
      binary += String.fromCharCode(byte);
    }
    return btoa(binary);
  }

  private base64ToBuffer(base64: string): Uint8Array {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
  }
}

// Usage Examples
async function demonstrateBase64Encoding() {
  console.log('=== Web TypeScript Base64 Encoding Examples ===\n');

  const coder = new Base64Coder();
  const urlSafeCoder = new URLSafeBase64Coder();
  const unicodeCoder = new UnicodeBase64Coder();
  const fileCoder = new FileBase64Coder();
  const imageCoder = new ImageBase64Coder();
  const utils = new Base64Utils();

  // 1. Basic encoding/decoding
  console.log('--- 1. Basic Encoding/Decoding ---');
  const text = 'Hello, World!';
  const encoded = coder.encode(text);
  const decoded = coder.decode(encoded);

  console.log(`Original: ${text}`);
  console.log(`Encoded: ${encoded}`);
  console.log(`Decoded: ${decoded}`);

  // 2. URL-safe encoding
  console.log('\n--- 2. URL-Safe Encoding ---');
  const urlSafeEncoded = urlSafeCoder.encode(text);
  console.log(`URL-safe encoded: ${urlSafeEncoded}`);
  console.log(`URL-safe decoded: ${urlSafeCoder.decode(urlSafeEncoded)}`);

  // 3. Unicode encoding
  console.log('\n--- 3. Unicode Encoding ---');
  const unicodeText = 'Hello, 世界! 🌍';
  const unicodeEncoded = unicodeCoder.encodeUTF8(unicodeText);
  const unicodeDecoded = unicodeCoder.decodeUTF8(unicodeEncoded);

  console.log(`Unicode original: ${unicodeText}`);
  console.log(`Unicode encoded: ${unicodeEncoded}`);
  console.log(`Unicode decoded: ${unicodeDecoded}`);

  // 4. Bytes encoding
  console.log('\n--- 4. Bytes Encoding ---');
  const bytes = new Uint8Array([72, 101, 108, 108, 111]);
  const bytesEncoded = coder.encodeBytes(bytes);
  console.log(`Bytes encoded: ${bytesEncoded}`);

  // 5. Validation
  console.log('\n--- 5. Validation ---');
  console.log(`Is valid Base64: ${utils.isValidBase64(encoded)}`);
  console.log(`Is invalid Base64: ${utils.isValidBase64('invalid!@#')}`);

  // 6. Utility functions
  console.log('\n--- 6. Utility Functions ---');
  console.log(`Byte length: ${utils.getByteLength(encoded)}`);
  console.log(`Truncated: ${utils.truncate(encoded, 10)}`);
  console.log(`Checksum: ${utils.checksum(encoded)}`);

  // 7. Chunks
  console.log('\n--- 7. Chunks ---');
  const longText = 'A'.repeat(100);
  const longEncoded = coder.encode(longText);
  const chunks = utils.chunk(longEncoded, 20);
  console.log(`Number of chunks: ${chunks.length}`);

  const concatenated = utils.concat(chunks);
  console.log(`Concatenated equals original: ${concatenated === longEncoded}`);

  console.log('\n=== All Base64 Encoding Examples Completed ===');
}

// Export functions
export { Base64Coder, URLSafeBase64Coder, UnicodeBase64Coder, FileBase64Coder, ImageBase64Coder, Base64Utils, Base64StreamCoder };
export { demonstrateBase64Encoding };

💻 Симметричное Шифрование typescript

🟡 intermediate ⭐⭐⭐⭐

Шифровать и расшифровывать данные используя алгоритм AES-GCM с Web Crypto API

⏱️ 30 min 🏷️ typescript, web, cryptography, encryption
Prerequisites: Intermediate TypeScript, Web Crypto API, Cryptography basics
// Web TypeScript Symmetric Encryption Examples
// Using AES-GCM algorithm with Web Crypto API

// 1. AES-GCM Encryptor
class AESGCMEncryptor {
  // Generate random key
  async generateKey(): Promise<CryptoKey> {
    return await crypto.subtle.generateKey(
      {
        name: 'AES-GCM',
        length: 256
      },
      true,
      ['encrypt', 'decrypt']
    );
  }

  // Encrypt data
  async encrypt(
    key: CryptoKey,
    data: string,
    associatedData?: string
  ): Promise<{ ciphertext: string; iv: string; tag?: string }> {
    const encoder = new TextEncoder();
    const encodedData = encoder.encode(data);

    // Generate random IV (Initialization Vector)
    const iv = crypto.getRandomValues(new Uint8Array(12));

    let encryptOptions: any = {
      name: 'AES-GCM',
      iv: iv
    };

    // Add associated data if provided (for authenticated encryption)
    if (associatedData) {
      encryptOptions.additionalData = encoder.encode(associatedData);
    }

    const encrypted = await crypto.subtle.encrypt(
      encryptOptions,
      key,
      encodedData
    );

    return {
      ciphertext: this.bufferToBase64(encrypted),
      iv: this.bufferToHex(iv)
    };
  }

  // Decrypt data
  async decrypt(
    key: CryptoKey,
    ciphertext: string,
    iv: string,
    associatedData?: string
  ): Promise<string> {
    const encryptedData = this.base64ToBuffer(ciphertext);
    const ivBytes = this.hexToBuffer(iv);

    let decryptOptions: any = {
      name: 'AES-GCM',
      iv: ivBytes
    };

    if (associatedData) {
      const encoder = new TextEncoder();
      decryptOptions.additionalData = encoder.encode(associatedData);
    }

    const decrypted = await crypto.subtle.decrypt(
      decryptOptions,
      key,
      encryptedData
    );

    const decoder = new TextDecoder();
    return decoder.decode(decrypted);
  }

  // Export key to base64
  async exportKey(key: CryptoKey): Promise<string> {
    const exported = await crypto.subtle.exportKey('raw', key);
    return this.bufferToBase64(exported);
  }

  // Import key from base64
  async importKey(keyData: string): Promise<CryptoKey> {
    const keyBytes = this.base64ToBuffer(keyData);

    return await crypto.subtle.importKey(
      'raw',
      keyBytes,
      {
        name: 'AES-GCM',
        length: 256
      },
      true,
      ['encrypt', 'decrypt']
    );
  }

  // Generate key from password
  async deriveKey(
    password: string,
    salt?: string,
    iterations: number = 100000
  ): Promise<{ key: CryptoKey; salt: string }> {
    // Generate salt if not provided
    const saltBytes = salt
      ? this.hexToBuffer(salt)
      : crypto.getRandomValues(new Uint8Array(16));

    const encoder = new TextEncoder();
    const passwordData = encoder.encode(password);

    const importedKey = await crypto.subtle.importKey(
      'raw',
      passwordData,
      { name: 'PBKDF2' },
      false,
      ['deriveKey']
    );

    const key = await crypto.subtle.deriveKey(
      {
        name: 'PBKDF2',
        salt: saltBytes,
        iterations: iterations,
        hash: 'SHA-256'
      },
      importedKey,
      {
        name: 'AES-GCM',
        length: 256
      },
      true,
      ['encrypt', 'decrypt']
    );

    return {
      key,
      salt: this.bufferToHex(saltBytes)
    };
  }

  private bufferToBase64(buffer: ArrayBuffer): string {
    const bytes = new Uint8Array(buffer);
    let binary = '';
    for (const byte of bytes) {
      binary += String.fromCharCode(byte);
    }
    return btoa(binary);
  }

  private base64ToBuffer(base64: string): Uint8Array {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
  }

  private bufferToHex(buffer: ArrayBuffer | Uint8Array): string {
    const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
    return Array.from(bytes)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }

  private hexToBuffer(hex: string): Uint8Array {
    const bytes = new Uint8Array(hex.length / 2);
    for (let i = 0; i < bytes.length; i++) {
      bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
    }
    return bytes;
  }
}

// 2. Encrypted Message Formatter
class EncryptedMessageFormatter {
  private encryptor: AESGCMEncryptor;

  constructor() {
    this.encryptor = new AESGCMEncryptor();
  }

  // Format encrypted message
  formatMessage(
    ciphertext: string,
    iv: string,
    salt?: string
  ): string {
    const message: any = {
      ct: ciphertext,
      iv: iv
    };

    if (salt) {
      message.salt = salt;
    }

    return JSON.stringify(message);
  }

  // Parse encrypted message
  parseMessage(message: string): {
    ciphertext: string;
    iv: string;
    salt?: string;
  } {
    const data = JSON.parse(message);
    return {
      ciphertext: data.ct,
      iv: data.iv,
      salt: data.salt
    };
  }
}

// 3. File Encryptor
class FileEncryptor {
  private encryptor = new AESGCMEncryptor();

  // Encrypt file
  async encryptFile(
    key: CryptoKey,
    file: File,
    chunkSize: number = 1024 * 1024 // 1MB
  ): Promise<{ encryptedData: Blob; iv: string }> {
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const encryptedChunks: BlobPart[] = [];

    let offset = 0;
    while (offset < file.size) {
      const end = Math.min(offset + chunkSize, file.size);
      const chunk = file.slice(offset, end);
      const arrayBuffer = await chunk.arrayBuffer();

      const encrypted = await crypto.subtle.encrypt(
        { name: 'AES-GCM', iv: iv },
        key,
        arrayBuffer
      );

      encryptedChunks.push(new Uint8Array(encrypted));
      offset = end;
    }

    const encryptedBlob = new Blob(encryptedChunks, { type: 'application/octet-stream' });

    return {
      encryptedData: encryptedBlob,
      iv: this.bufferToHex(iv)
    };
  }

  // Decrypt file
  async decryptFile(
    key: CryptoKey,
    encryptedFile: File,
    iv: string,
    chunkSize: number = 1024 * 1024
  ): Promise<Blob> {
    const ivBytes = this.hexToBuffer(iv);
    const decryptedChunks: BlobPart[] = [];

    let offset = 0;
    while (offset < encryptedFile.size) {
      const end = Math.min(offset + chunkSize, encryptedFile.size);
      const chunk = encryptedFile.slice(offset, end);
      const arrayBuffer = await chunk.arrayBuffer();

      const decrypted = await crypto.subtle.decrypt(
        { name: 'AES-GCM', iv: ivBytes },
        key,
        arrayBuffer
      );

      decryptedChunks.push(new Uint8Array(decrypted));
      offset = end;
    }

    return new Blob(decryptedChunks, { type: 'application/octet-stream' });
  }

  // Encrypt file with progress
  async encryptFileWithProgress(
    key: CryptoKey,
    file: File,
    onProgress: (progress: number) => void
  ): Promise<{ encryptedData: Blob; iv: string }> {
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const chunkSize = 1024 * 1024;
    const encryptedChunks: BlobPart[] = [];

    let offset = 0;
    while (offset < file.size) {
      const end = Math.min(offset + chunkSize, file.size);
      const chunk = file.slice(offset, end);
      const arrayBuffer = await chunk.arrayBuffer();

      const encrypted = await crypto.subtle.encrypt(
        { name: 'AES-GCM', iv: iv },
        key,
        arrayBuffer
      );

      encryptedChunks.push(new Uint8Array(encrypted));
      offset = end;

      const progress = Math.floor((offset / file.size) * 100);
      onProgress(progress);
    }

    const encryptedBlob = new Blob(encryptedChunks, { type: 'application/octet-stream' });

    return {
      encryptedData: encryptedBlob,
      iv: this.bufferToHex(iv)
    };
  }

  private bufferToHex(buffer: ArrayBuffer | Uint8Array): string {
    const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
    return Array.from(bytes)
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');
  }

  private hexToBuffer(hex: string): Uint8Array {
    const bytes = new Uint8Array(hex.length / 2);
    for (let i = 0; i < bytes.length; i++) {
      bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
    }
    return bytes;
  }
}

// 4. Key Management
class KeyManager {
  // Store key in IndexedDB
  async storeKey(keyId: string, key: CryptoKey, dbName: string = 'encryptionKeys'): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(dbName, 1);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        const db = request.result;
        const transaction = db.transaction(['keys'], 'readwrite');
        const store = transaction.objectStore('keys');

        const encryptor = new AESGCMEncryptor();

        encryptor.exportKey(key).then(exportedKey => {
          store.put({ id: keyId, keyData: exportedKey, created: Date.now() });

          transaction.oncomplete = () => resolve();
          transaction.onerror = () => reject(transaction.error);
        });
      };

      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;
        if (!db.objectStoreNames.contains('keys')) {
          db.createObjectStore('keys', { keyPath: 'id' });
        }
      };
    });
  }

  // Retrieve key from IndexedDB
  async retrieveKey(keyId: string, dbName: string = 'encryptionKeys'): Promise<CryptoKey | null> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(dbName, 1);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        const db = request.result;
        const transaction = db.transaction(['keys'], 'readonly');
        const store = transaction.objectStore('keys');
        const getRequest = store.get(keyId);

        getRequest.onsuccess = async () => {
          const data = getRequest.result;
          if (data) {
            const encryptor = new AESGCMEncryptor();
            const key = await encryptor.importKey(data.keyData);
            resolve(key);
          } else {
            resolve(null);
          }
        };

        getRequest.onerror = () => reject(getRequest.error);
      };

      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;
        if (!db.objectStoreNames.contains('keys')) {
          db.createObjectStore('keys', { keyPath: 'id' });
        }
      };
    });
  }

  // Delete key from storage
  async deleteKey(keyId: string, dbName: string = 'encryptionKeys'): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(dbName, 1);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        const db = request.result;
        const transaction = db.transaction(['keys'], 'readwrite');
        const store = transaction.objectStore('keys');

        store.delete(keyId);

        transaction.oncomplete = () => resolve();
        transaction.onerror = () => reject(transaction.error);
      };
    });
  }

  // List all stored key IDs
  async listKeys(dbName: string = 'encryptionKeys'): Promise<string[]> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(dbName, 1);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        const db = request.result;
        const transaction = db.transaction(['keys'], 'readonly');
        const store = transaction.objectStore('keys');
        const getAllRequest = store.getAllKeys();

        getAllRequest.onsuccess = () => {
          resolve(getAllRequest.result as string[]);
        };

        getAllRequest.onerror = () => reject(getAllRequest.error);
      };
    });
  }
}

// 5. Secure Messaging
class SecureMessaging {
  private encryptor = new AESGCMEncryptor();
  private formatter = new EncryptedMessageFormatter();

  // Send secure message
  async sendMessage(
    recipientKey: CryptoKey,
    message: string,
    associatedData?: string
  ): Promise<string> {
    const encrypted = await this.encryptor.encrypt(
      recipientKey,
      message,
      associatedData
    );

    return this.formatter.formatMessage(
      encrypted.ciphertext,
      encrypted.iv
    );
  }

  // Receive secure message
  async receiveMessage(
    privateKey: CryptoKey,
    encryptedMessage: string,
    associatedData?: string
  ): Promise<string> {
    const parsed = this.formatter.parseMessage(encryptedMessage);

    return await this.encryptor.decrypt(
      privateKey,
      parsed.ciphertext,
      parsed.iv,
      associatedData
    );
  }

  // Create secure conversation
  async createConversation(): Promise<{
    key: CryptoKey;
    keyData: string;
  }> {
    const key = await this.encryptor.generateKey();
    const keyData = await this.encryptor.exportKey(key);

    return { key, keyData };
  }
}

// Usage Examples
async function demonstrateSymmetricEncryption() {
  console.log('=== Web TypeScript Symmetric Encryption Examples ===\n');

  const encryptor = new AESGCMEncryptor();
  const fileEncryptor = new FileEncryptor();
  const keyManager = new KeyManager();
  const messaging = new SecureMessaging();

  // 1. Generate key
  console.log('--- 1. Generate Key ---');
  const key = await encryptor.generateKey();
  const keyData = await encryptor.exportKey(key);
  console.log(`Key: ${keyData.substring(0, 32)}...`);

  // 2. Encrypt/Decrypt message
  console.log('\n--- 2. Encrypt/Decrypt Message ---');
  const message = 'This is a secret message!';
  const encrypted = await encryptor.encrypt(key, message);
  console.log(`Encrypted: ${encrypted.ciphertext.substring(0, 32)}...`);

  const decrypted = await encryptor.decrypt(key, encrypted.ciphertext, encrypted.iv);
  console.log(`Decrypted: ${decrypted}`);

  // 3. Encrypt with associated data
  console.log('\n--- 3. Encrypt with Associated Data ---');
  const aad = 'user123';
  const encryptedAAD = await encryptor.encrypt(key, message, aad);
  const decryptedAAD = await encryptor.decrypt(
    key,
    encryptedAAD.ciphertext,
    encryptedAAD.iv,
    aad
  );
  console.log(`With AAD: ${decryptedAAD}`);

  // 4. Derive key from password
  console.log('\n--- 4. Derive Key from Password ---');
  const password = 'MySecurePassword';
  const { key: derivedKey, salt } = await encryptor.deriveKey(password);
  const derivedEncrypted = await encryptor.encrypt(derivedKey, message);
  console.log(`Derived key encrypted: ${derivedEncrypted.ciphertext.substring(0, 32)}...`);

  // 5. Key management
  console.log('\n--- 5. Key Management ---');
  await keyManager.storeKey('test-key', key);
  const retrievedKey = await keyManager.retrieveKey('test-key');
  console.log(`Key retrieved: ${retrievedKey ? 'Yes' : 'No'}`);

  const keyIds = await keyManager.listKeys();
  console.log(`Stored keys: ${keyIds.join(', ')}`);

  // 6. Secure messaging
  console.log('\n--- 6. Secure Messaging ---');
  const secureMsg = await messaging.sendMessage(key, 'Hello, secure world!');
  console.log(`Secure message: ${secureMsg.substring(0, 50)}...`);

  const receivedMsg = await messaging.receiveMessage(key, secureMsg);
  console.log(`Received: ${receivedMsg}`);

  console.log('\n=== All Symmetric Encryption Examples Completed ===');
}

// Export functions
export { AESGCMEncryptor, EncryptedMessageFormatter, FileEncryptor, KeyManager, SecureMessaging };
export { demonstrateSymmetricEncryption };