npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ray-js/aes-utils

v1.0.1

Published

AES-CTR + HMAC 加解密工具包 - 符合安全合规标准的认证加密方案

Readme

English | 简体中文

@ray-js/aes-utils

latest download

AES Encryption/Decryption Utilities

⚠️ Security Compliance Update

This package has been updated to meet the latest security standards:

  • Encryption Mode: Upgraded from AES-CBC to AES-CTR + HMAC-SHA256 (Authenticated Encryption)
  • Hash Algorithm: Replaced MD5 with SHA-256
  • Compliance: Meets EU RED (Radio Equipment Directive) and NIST security requirements

Why AES-CTR + HMAC?

  • Authenticated Encryption: Provides both confidentiality (AES-CTR) and data integrity (HMAC-SHA256)
  • No Padding Oracle Attacks: CTR is a stream cipher mode, immune to padding vulnerabilities
  • Proven Security: CTR + HMAC is a widely recognized AEAD construction
  • Standards Compliant: NIST recommended, widely used in security protocols

Note: While AES-GCM is the ideal choice, the crypto-js library does not support GCM mode. AES-CTR + HMAC provides equivalent security as an authenticated encryption scheme.

Breaking Changes

Important: This version is not backward compatible with data encrypted by older versions (AES-CBC). Please ensure:

  1. All encryption/decryption operations are updated simultaneously
  2. Old data is re-encrypted using the new AES-CTR + HMAC mode
  3. Both client and server use the same version

Installation

$ npm install @ray-js/aes-utils
// or
$ yarn add @ray-js/aes-utils

Usage

import { decryptImage } from '@ray-js/aes-utils';
const base64Image = decryptImage(url, key);

Dependencies

BaseKit >= 2.4.3

// BaseKit 2.3.2
ty.downloadFile(options);
const manager = ty.getFileSystemManager();

// BaseKit 2.4.3
const { data } = manager.readFileSync({
  filePath: tempFilePath,
  encoding: 'base64',
});

Migration Guide

This guide helps you migrate from @ray-js/aes-utils v0.0.9 (using AES-CBC) to v1.0.0 (using AES-CTR + HMAC-SHA256).

Why Migrate?

According to Tuya Security Compliance Notice (2025-12-25) and EU RED certification requirements:

  • AES-CBC no longer meets security standards as of 2026-01-01
  • Lacks data integrity protection, vulnerable to padding oracle attacks
  • TLS 1.3 has removed AES-CBC support

Key Changes

| Item | v0.0.9 (Old) | v1.0.0 (New) | | --------------- | ------------------ | ---------------------------- | | Encryption Mode | AES-CBC | AES-CTR + HMAC-SHA256 | | IV Length | 128-bit (16 bytes) | 96-bit (12 bytes) | | Authentication | ❌ None | ✅ HMAC-SHA256 (256-bit tag) | | Hash Algorithm | MD5 | SHA-256 | | Data Format | iv + ciphertext | iv + ciphertext + hmac | | Backward Compat | N/A | ❌ Not Compatible |


⚠️ Breaking Changes

1. Data Format Incompatibility

Old Format (v0.0.9):

[16-byte IV][ciphertext]

New Format (v1.0.0):

[12-byte IV][ciphertext][32-byte HMAC tag]

2. Decryption Behavior Change

  • v0.0.9: Direct decryption, no integrity verification
  • v1.0.0: HMAC verification first, then decrypt (authenticated encryption)

3. API Remains Compatible

✅ Good news: All public API signatures remain unchanged

// These function signatures haven't changed
encrypt(data, key);
decrypt(data, key);
encryptBase64Data(data, key, keyType);
decryptBase64Data(data, key, keyType);
decryptImage(url, key);

🔧 Migration Steps

Step 1: Assess Impact Scope

# 1. Check all projects using this library
grep -r "@ray-js/aes-utils" .

# 2. Identify all encrypted data storage locations
# - Database
# - Local storage
# - File system
# - API transfer data

Step 2: Define Migration Strategy

Choose one of the following strategies:

Strategy A: One-time Migration (Recommended for small projects)

// 1. Decrypt all data using old version
import { decrypt as oldDecrypt } from '@ray-js/[email protected]';

// 2. Upgrade to new version
// npm install @ray-js/[email protected]

// 3. Re-encrypt using new version
import { encrypt as newEncrypt } from '@ray-js/[email protected]';

// 4. Replace all data
const oldData = oldDecrypt(encryptedData, key);
const newEncryptedData = newEncrypt(oldData, key);

Strategy B: Dual Version Compatibility (Recommended for large projects)

// Install both versions using aliases
// package.json:
{
  "dependencies": {
    "@ray-js/aes-utils": "1.0.0",
    "@ray-js/aes-utils-legacy": "npm:@ray-js/[email protected]"
  }
}
// Implement compatibility layer
import { decrypt as newDecrypt } from '@ray-js/aes-utils';
import { decrypt as oldDecrypt } from '@ray-js/aes-utils-legacy';

function compatibleDecrypt(data: string, key: string): string {
  try {
    // Try new format (with HMAC)
    return newDecrypt(data, key);
  } catch (error) {
    // Fallback to old format
    console.warn('Decrypting with old format, recommend re-encrypting this data');
    return oldDecrypt(data, key);
  }
}

Step 3: Data Migration Script

// migrate-data.ts
import { decrypt as oldDecrypt } from '@ray-js/aes-utils-legacy';
import { encrypt as newEncrypt } from '@ray-js/aes-utils';

interface EncryptedRecord {
  id: string;
  encryptedData: string;
}

async function migrateDatabase(key: string) {
  const records = await db.getAllEncryptedRecords();
  let successCount = 0;
  let failCount = 0;

  for (const record of records) {
    try {
      // 1. Decrypt using old version
      const plaintext = oldDecrypt(record.encryptedData, key);

      // 2. Encrypt using new version
      const newEncrypted = newEncrypt(plaintext, key);

      // 3. Update database
      await db.update(record.id, { encryptedData: newEncrypted });

      successCount++;
      console.log(`✅ Migrated: ${record.id}`);
    } catch (error) {
      failCount++;
      console.error(`❌ Migration failed: ${record.id}`, error);
    }
  }

  console.log(`\nMigration complete: ${successCount} successful, ${failCount} failed`);
}

// Execute migration
migrateDatabase('your-encryption-key')
  .then(() => console.log('Data migration complete'))
  .catch(console.error);

Step 4: Test and Verify

// test-migration.ts
import { encrypt, decrypt } from '@ray-js/aes-utils';

const testKey = 'OQMpT3obElFmXzBMBGgoPw==';
const testData = 'Hello, World! 你好世界!';

// 1. Basic encryption/decryption test
console.log('Test 1: Basic functionality');
const encrypted = encrypt(testData, testKey);
const decrypted = decrypt(encrypted, testKey);
console.assert(decrypted === testData, '❌ Decryption failed');
console.log('✅ Basic functionality works');

// 2. HMAC tampering detection test
console.log('\nTest 2: HMAC tampering detection');
const tamperedData = encrypted.slice(0, -10) + '0000000000';
try {
  decrypt(tamperedData, testKey);
  console.error('❌ Should have detected tampering');
} catch (error) {
  console.log('✅ Successfully detected data tampering');
}

// 3. Old data incompatibility test
console.log('\nTest 3: Old data compatibility');
const oldFormatData = 'AQAAAESzsJZ93dvv...'; // Data encrypted with v0.0.9
try {
  decrypt(oldFormatData, testKey);
  console.error('❌ Should not successfully decrypt old format data');
} catch (error) {
  console.log('✅ Correctly rejected old format data');
}

// 4. Base64 functionality test
console.log('\nTest 4: Base64 encoding');
import { encryptBase64Data, decryptBase64Data } from '@ray-js/aes-utils';
const base64Encrypted = encryptBase64Data(testData, testKey, 'base64');
const base64Decrypted = decryptBase64Data(base64Encrypted, testKey, 'base64');
console.assert(base64Decrypted === testData, '❌ Base64 decryption failed');
console.log('✅ Base64 functionality works');

console.log('\n🎉 All tests passed!');

Step 5: Staged Deployment

# 1. Test in development environment
npm install @ray-js/[email protected]
npm test

# 2. Verify in staging environment
# - Run data migration script
# - Verify all functionality
# - Perform load testing

# 3. Deploy to production
# - Choose off-peak hours
# - Prepare rollback plan
# - Monitor error logs

# 4. Post-migration cleanup
# - Remove old version dependency
# - Delete compatibility layer code
# - Update documentation

📝 Code Examples

Example 1: Simple Encryption/Decryption

import { encrypt, decrypt } from '@ray-js/aes-utils';

const key = 'OQMpT3obElFmXzBMBGgoPw==';
const message = 'Sensitive data';

// Encrypt
const encrypted = encrypt(message, key);
console.log('Encrypted:', encrypted);

// Decrypt
const decrypted = decrypt(encrypted, key);
console.log('Decrypted:', decrypted); // 'Sensitive data'

Example 2: Image Encryption/Decryption

import { decryptImage } from '@ray-js/aes-utils';

// Decrypt image URL
const imageUrl = 'https://example.com/encrypted-image';
const key = 'your-encryption-key';

const base64Image = await decryptImage(imageUrl, key);
console.log('Decrypted image Base64:', base64Image);

Example 3: Batch Data Migration

import { decrypt as oldDecrypt } from '@ray-js/aes-utils-legacy';
import { encrypt as newEncrypt } from '@ray-js/aes-utils';

async function migrateBatch(records: Array<{ id: string; data: string }>, key: string) {
  const results = await Promise.allSettled(
    records.map(async record => {
      const plaintext = oldDecrypt(record.data, key);
      const newEncrypted = newEncrypt(plaintext, key);
      await saveToDatabase(record.id, newEncrypted);
      return record.id;
    })
  );

  const successful = results.filter(r => r.status === 'fulfilled').length;
  const failed = results.filter(r => r.status === 'rejected').length;

  return { successful, failed };
}

🔄 Rollback Plan

If you encounter issues after migration, you can rollback to v0.0.9:

# 1. Rollback npm package
npm install @ray-js/[email protected]

# 2. Restore data backup
# Restore database from pre-migration backup

# 3. Restart application
npm run build
npm run start

Important Notes:

  • ⚠️ Data encrypted with v1.0.0 cannot be decrypted by v0.0.9
  • Must restore data from pre-migration backup
  • Recommend making complete data backup before migration

❓ FAQ

Q1: Why can't it be backward compatible?

A: The data formats are completely different:

  • Old version has no HMAC tag, new version must verify HMAC
  • IV length changed (16 bytes → 12 bytes)
  • Different encryption modes (CBC → CTR)

Q2: What happens if only some systems are updated?

A: It will cause encryption/decryption failures:

  • New version encrypts → Old version cannot decrypt (missing HMAC handling)
  • Old version encrypts → New version cannot decrypt (HMAC verification fails)

All systems using this library must be updated synchronously!

Q3: Can I only update the client?

A: No. Coordinated updates are required:

Scenario 1 - Old server, new client:
Client encrypts with new format → Server cannot decrypt ❌

Scenario 2 - New server, old client:
Server encrypts with new format → Client cannot decrypt ❌

Correct approach:
Update both server and client simultaneously ✅

Q4: How to handle data in transit?

A: Recommend using "dual-write" strategy:

// Support both formats during migration period
function sendData(data: string, key: string) {
  return {
    legacy: oldEncrypt(data, key), // Compatible with old clients
    modern: newEncrypt(data, key), // For new clients
  };
}

function receiveData(payload: any, key: string) {
  if (payload.modern) {
    return newDecrypt(payload.modern, key);
  } else {
    return oldDecrypt(payload.legacy, key);
  }
}

Q5: How long does migration take?

A: Depends on data volume:

  • Small projects (< 1GB data): 1-2 hours
  • Medium projects (1-10GB data): 4-8 hours
  • Large projects (> 10GB data): Batch migration required, may take days

Q6: Will performance be affected?

A: AES-CTR + HMAC performs slightly better than AES-CBC:

  • CTR mode can be parallelized
  • No padding required
  • HMAC computation overhead is minimal (< 1ms)

Benchmark tests show ~5-10% performance improvement.

Q7: How to verify migration success?

A: Checklist:

✅ All encrypted data can be decrypted normally
✅ HMAC tampering detection works correctly
✅ Application functions completely normally
✅ No decryption error logs
✅ Performance metrics normal
✅ Can delete old version dependency

Q8: What to do about "HMAC verification failed" errors?

A: Possible causes:

  1. Attempting to decrypt old format data → Use compatibility layer
  2. Data tampered during transmission → Check network/storage
  3. Using wrong key → Verify key configuration
  4. Data corruption → Restore from backup

📞 Support & Feedback

If you encounter problems during migration:

  1. Check CHANGELOG.md
  2. Submit an Issue to GitHub
  3. Contact Tuya Technical Support

📚 Further Reading


Good luck with your migration! 🎉

Feel free to reach out if you have any questions.