Cipherion
Sdk IntegrationsJavaScript SDK

Core Concepts

Install the Cipherion JavaScript SDK

Encryption

Cipherion provides two types of encryption methods: simple string encryption and deep encryption for complex data structures.

Simple Encryption

Use encrypt() to encrypt simple string values:

import { CipherionClient } from '@cipherion/client';

const client = new CipherionClient();

async function encryptString() {
  const plaintext = "Hello, World!";
  const encrypted = await client.encrypt(plaintext);
  
  console.log(encrypted);
  // Output: "encrypted_token,mapped_data,iv"
}

Use Cases

  • Encrypting passwords
  • Encrypting API tokens
  • Encrypting single field values
  • Encrypting configuration secrets

Example: Password Encryption

async function encryptPassword(password: string) {
  try {
    const encrypted = await client.encrypt(password);
    return {
      success: true,
      encryptedPassword: encrypted
    };
  } catch (error) {
    console.error('Password encryption failed:', error);
    throw error;
  }
}

// Usage
const result = await encryptPassword("MySecurePass123!");

Deep Encryption

Use deepEncrypt() to encrypt complex nested objects and arrays:

async function encryptObject() {
  const userData = {
    name: "John Doe",
    email: "john@example.com",
    address: {
      street: "123 Main St",
      city: "New York",
      zipCode: "10001"
    },
    phones: ["+1234567890", "+0987654321"]
  };
  
  const result = await client.deepEncrypt(userData);
  
  console.log('Encrypted:', result.encrypted);
  console.log('Metadata:', result.meta);
}

Response Structure

interface DeepEncryptResponse {
  encrypted: any;  // The encrypted data structure
  meta: {
    encryptionMetadata: {
      excluded_fields: string[];
      excluded_patterns: string[];
      operation: "deep_encrypt";
    };
    totalFields: number;      // Total fields in the object
    billableFields: number;   // Fields that were encrypted
    totalPrice: number;       // Cost of the operation
  };
}

Example with Metadata

const user = {
  _id: "507f1f77bcf86cd799439011",
  username: "johndoe",
  email: "john@example.com",
  ssn: "123-45-6789",
  createdAt: "2024-01-15T10:30:00Z"
};

const result = await client.deepEncrypt(user, {
  exclude_patterns: ["_id", "*At"]
});

console.log('Total fields:', result.meta.totalFields);
// Output: 5

console.log('Billable fields:', result.meta.billableFields);
// Output: 3 (username, email, ssn)

console.log('Cost:', result.meta.totalPrice);
// Output: 0.003

console.log('Encrypted data:', result.encrypted);
// {
//   _id: "507f1f77bcf86cd799439011",  // Not encrypted
//   username: "encrypted_...",
//   email: "encrypted_...",
//   ssn: "encrypted_...",
//   createdAt: "2024-01-15T10:30:00Z"  // Not encrypted
// }

Nested Objects

Deep encryption handles nested structures automatically:

const company = {
  name: "Acme Corp",
  employees: [
    {
      id: 1,
      name: "Alice",
      salary: 100000,
      address: {
        street: "456 Oak Ave",
        city: "Boston"
      }
    },
    {
      id: 2,
      name: "Bob",
      salary: 95000,
      address: {
        street: "789 Pine Rd",
        city: "Seattle"
      }
    }
  ]
};

const result = await client.deepEncrypt(company, {
  exclude_fields: ["employees[*].id"]  // Keep IDs unencrypted
});

console.log(result.encrypted);
// All fields encrypted except employee IDs

Arrays

Arrays are encrypted element by element:

const data = {
  users: ["Alice", "Bob", "Charlie"],
  scores: [95, 87, 92]
};

const result = await client.deepEncrypt(data);

console.log(result.encrypted);
// {
//   users: ["encrypted_Alice", "encrypted_Bob", "encrypted_Charlie"],
//   scores: ["encrypted_95", "encrypted_87", "encrypted_92"]
// }

Selective Array Encryption

Encrypt specific array elements:

const data = {
  users: ["Alice", "Bob", "Charlie", "Diana"]
};

const result = await client.deepEncrypt(data, {
  exclude_fields: ["users[1]", "users[3]"]  // Don't encrypt Bob and Diana
});

console.log(result.encrypted);
// {
//   users: [
//     "encrypted_Alice",
//     "Bob",              // Not encrypted
//     "encrypted_Charlie",
//     "Diana"             // Not encrypted
//   ]
// }

Performance Considerations

Field Count

The number of fields affects:

  • Processing time
  • API cost (billable fields)
  • Response size
// Example: Large object
const largeObject = {
  field1: "value1",
  field2: "value2",
  // ... 1000 more fields
};

const result = await client.deepEncrypt(largeObject);

console.log(`Encrypted ${result.meta.billableFields} fields`);
console.log(`Cost: $${result.meta.totalPrice.toFixed(4)}`);

Optimization Tips

  1. Exclude unnecessary fields
   const result = await client.deepEncrypt(data, {
     exclude_patterns: ["_id", "__v", "*_at", "metadata"]
   });
  1. Use batch operations for multiple items
   // Instead of loop
   for (const item of items) {
     await client.deepEncrypt(item);  // ❌ Slow
   }
   
   // Use migration
   await client.migrateEncrypt(items);  // ✅ Fast
  1. Cache encrypted data when possible
   const cache = new Map();
   
   async function getCachedEncryption(key: string, data: any) {
     if (cache.has(key)) {
       return cache.get(key);
     }
     
     const encrypted = await client.deepEncrypt(data);
     cache.set(key, encrypted);
     return encrypted;
   }

Data Types

Cipherion handles various data types:

const mixed = {
  string: "Hello",
  number: 42,
  boolean: true,
  null: null,
  undefined: undefined,
  date: new Date(),
  array: [1, 2, 3],
  object: { nested: "value" }
};

const result = await client.deepEncrypt(mixed);

// All types are preserved after encryption/decryption

null and undefined values are preserved and not encrypted.

Encryption Best Practices

1. Always Use HTTPS

const client = new CipherionClient({
  baseUrl: 'https://api.cipherion.in',  // ✅ HTTPS
  // NOT http://api.cipherion.in         // ❌ Insecure
});

2. Validate Data Before Encryption

function validateUserData(data: any) {
  if (!data.email || !data.name) {
    throw new Error('Missing required fields');
  }
  return true;
}

async function encryptUser(user: any) {
  validateUserData(user);
  return await client.deepEncrypt(user);
}

3. Handle Errors Gracefully

import { CipherionError } from '@cipherion/client';

async function safeEncryption(data: any) {
  try {
    return await client.deepEncrypt(data);
  } catch (error) {
    if (error instanceof CipherionError) {
      if (error.isRetryable()) {
        // Retry logic
        await delay(2000);
        return await client.deepEncrypt(data);
      }
      console.error('Encryption failed:', error.getUserMessage());
    }
    throw error;
  }
}

4. Don't Encrypt Everything

// ❌ Bad: Encrypting IDs and metadata
const result = await client.deepEncrypt(data);

// ✅ Good: Exclude non-sensitive fields
const result = await client.deepEncrypt(data, {
  exclude_fields: ["id", "userId"],
  exclude_patterns: ["_id", "*_at", "version"]
});

Decryption

Decrypt data that was encrypted using Cipherion's encryption methods.

Simple Decryption

Use decrypt() to decrypt strings encrypted with encrypt():

import { CipherionClient } from '@cipherion/client';

const client = new CipherionClient();

async function decryptString() {
  const encrypted = "encrypted_token,mapped_data,iv";
  const decrypted = await client.decrypt(encrypted);
  
  console.log(decrypted);
  // Output: "Hello, World!"
}

Example: Password Verification

async function verifyPassword(
  inputPassword: string,
  encryptedPassword: string
): Promise<boolean> {
  try {
    const decrypted = await client.decrypt(encryptedPassword);
    return inputPassword === decrypted;
  } catch (error) {
    console.error('Password verification failed:', error);
    return false;
  }
}

// Usage
const isValid = await verifyPassword(
  "UserInputPass123",
  "encrypted_stored_password"
);

Deep Decryption

Use deepDecrypt() to decrypt complex objects:

async function decryptObject() {
  const encrypted = {
    name: "encrypted_token...",
    email: "encrypted_token...",
    address: {
      street: "encrypted_token...",
      city: "encrypted_token..."
    }
  };
  
  const result = await client.deepDecrypt(encrypted);
  
  console.log('Decrypted:', result.data);
}

Response Structure

interface DeepDecryptResponse {
  data: any;  // The decrypted data structure
  meta: {
    decryptionMetadata: {
      excluded_fields: string[];
      excluded_patterns: string[];
      failed_fields: string[];  // Fields that failed to decrypt
      operation: "deep_decrypt";
    };
  };
}

Important: Use Same Exclusions

Always use the same exclude_fields and exclude_patterns during decryption that were used during encryption.

// Encryption
const encrypted = await client.deepEncrypt(data, {
  exclude_fields: ["id"],
  exclude_patterns: ["_id", "*_at"]
});

// Decryption - MUST use same options
const decrypted = await client.deepDecrypt(encrypted.encrypted, {
  exclude_fields: ["id"],
  exclude_patterns: ["_id", "*_at"]
});

Why This Matters

// ❌ Wrong: Different exclusions
const encrypted = await client.deepEncrypt(data, {
  exclude_patterns: ["_id"]
});

const decrypted = await client.deepDecrypt(encrypted.encrypted, {
  exclude_patterns: ["*_id"]  // Different pattern!
});
// Result: May attempt to decrypt unencrypted fields

// ✅ Correct: Same exclusions
const encrypted = await client.deepEncrypt(data, {
  exclude_patterns: ["_id"]
});

const decrypted = await client.deepDecrypt(encrypted.encrypted, {
  exclude_patterns: ["_id"]  // Same pattern
});

Graceful Failure Handling

Handle corrupted or invalid encrypted data gracefully:

const encrypted = {
  _id: "123",
  name: "encrypted_valid_token",
  email: "corrupted_data",      // Corrupted!
  phone: "encrypted_valid_token"
};

const result = await client.deepDecrypt(encrypted, {
  exclude_patterns: ["_id"],
  fail_gracefully: true  // Don't throw on failure
});

console.log('Decrypted data:', result.data);
// {
//   _id: "123",
//   name: "John Doe",
//   email: "corrupted_data",  // Kept as-is
//   phone: "+1234567890"
// }

console.log('Failed fields:', result.meta.decryptionMetadata.failed_fields);
// ["email"]

When to Use Graceful Failure

  1. Production environments - Don't break the application
  2. Data migrations - Some records might be corrupted
  3. Partial data recovery - Get what you can
  4. Logging and monitoring - Track decryption issues

Without Graceful Failure

try {
  const result = await client.deepDecrypt(encrypted, {
    fail_gracefully: false  // Throw on first failure (default)
  });
} catch (error) {
  console.error('Decryption failed:', error);
  // Entire operation fails on first corrupted field
}

Handling Failed Fields

async function decryptWithRecovery(encrypted: any) {
  const result = await client.deepDecrypt(encrypted, {
    exclude_patterns: ["_id", "*_at"],
    fail_gracefully: true
  });
  
  // Check for failures
  const failedFields = result.meta.decryptionMetadata.failed_fields;
  
  if (failedFields.length > 0) {
    console.warn(`Failed to decrypt ${failedFields.length} fields:`, failedFields);
    
    // Log for monitoring
    await logDecryptionFailure({
      recordId: result.data._id,
      failedFields: failedFields,
      timestamp: new Date()
    });
    
    // Optionally alert admins
    if (failedFields.includes('ssn') || failedFields.includes('payment')) {
      await alertAdmin('Critical field decryption failed', result.data._id);
    }
  }
  
  return result.data;
}

Nested Objects Decryption

const encrypted = {
  user: {
    profile: {
      name: "encrypted_...",
      email: "encrypted_..."
    },
    settings: {
      theme: "encrypted_...",
      notifications: "encrypted_..."
    }
  }
};

const result = await client.deepDecrypt(encrypted);

console.log(result.data);
// Fully decrypted nested structure

Array Decryption

const encrypted = {
  users: [
    "encrypted_Alice",
    "encrypted_Bob",
    "encrypted_Charlie"
  ],
  scores: [
    "encrypted_95",
    "encrypted_87",
    "encrypted_92"
  ]
};

const result = await client.deepDecrypt(encrypted);

console.log(result.data);
// {
//   users: ["Alice", "Bob", "Charlie"],
//   scores: [95, 87, 92]
// }

Selective Array Decryption

// If you excluded specific array elements during encryption
const encrypted = {
  users: [
    "encrypted_Alice",
    "Bob",              // Was excluded during encryption
    "encrypted_Charlie",
    "Diana"             // Was excluded during encryption
  ]
};

const result = await client.deepDecrypt(encrypted, {
  exclude_fields: ["users[1]", "users[3]"]  // Same exclusions
});

console.log(result.data.users);
// ["Alice", "Bob", "Charlie", "Diana"]

Storing Decryption Options

Store encryption options with your data for consistent decryption:

// During encryption
const encryptionOptions = {
  exclude_fields: ["id", "userId"],
  exclude_patterns: ["_id", "*_at"]
};

const encrypted = await client.deepEncrypt(data, encryptionOptions);

// Store both
await database.save({
  data: encrypted.encrypted,
  _encryptionOptions: encryptionOptions,  // Store options
  _encrypted: true
});

// During decryption
const record = await database.findById(id);

const decrypted = await client.deepDecrypt(
  record.data,
  record._encryptionOptions  // Use stored options
);

Performance Optimization

1. Cache Decrypted Data

import LRU from 'lru-cache';

const decryptionCache = new LRU({
  max: 500,
  ttl: 1000 * 60 * 5  // 5 minutes
});

async function cachedDecrypt(encrypted: any, cacheKey: string) {
  const cached = decryptionCache.get(cacheKey);
  if (cached) return cached;
  
  const result = await client.deepDecrypt(encrypted, {
    exclude_patterns: ["_id"],
    fail_gracefully: true
  });
  
  decryptionCache.set(cacheKey, result.data);
  return result.data;
}

2. Parallel Decryption

async function decryptMultiple(encryptedArray: any[]) {
  const results = await Promise.all(
    encryptedArray.map(item =>
      client.deepDecrypt(item, {
        exclude_patterns: ["_id"],
        fail_gracefully: true
      })
    )
  );
  
  return results.map(r => r.data);
}

3. Lazy Decryption

Only decrypt fields when needed:

class LazyDecryptedUser {
  private _decrypted: any = null;
  
  constructor(private encrypted: any) {}
  
  async getName() {
    if (!this._decrypted) {
      await this.decrypt();
    }
    return this._decrypted.name;
  }
  
  async getEmail() {
    if (!this._decrypted) {
      await this.decrypt();
    }
    return this._decrypted.email;
  }
  
  private async decrypt() {
    const result = await client.deepDecrypt(this.encrypted);
    this._decrypted = result.data;
  }
}

Error Handling

import { CipherionError } from '@cipherion/client';

async function safeDecryption(encrypted: any) {
  try {
    return await client.deepDecrypt(encrypted, {
      fail_gracefully: false
    });
  } catch (error) {
    if (error instanceof CipherionError) {
      switch (error.statusCode) {
        case 400:
          console.error('Invalid encrypted data format');
          break;
        case 401:
          console.error('Invalid credentials');
          break;
        case 500:
          console.error('Server error during decryption');
          if (error.isRetryable()) {
            // Retry logic
            await delay(2000);
            return await client.deepDecrypt(encrypted, {
              fail_gracefully: false
            });
          }
          break;
        default:
          console.error('Decryption error:', error.getUserMessage());
      }
    }
    throw error;
  }
}

Common Pitfalls

1. Mismatched Exclusions

// ❌ Wrong
const encrypted = await client.deepEncrypt(data, {
  exclude_patterns: ["_id", "*_at"]
});

const decrypted = await client.deepDecrypt(encrypted.encrypted, {
  exclude_patterns: ["_id"]  // Missing "*_at"
});

// ✅ Correct
const options = {
  exclude_patterns: ["_id", "*_at"]
};

const encrypted = await client.deepEncrypt(data, options);
const decrypted = await client.deepDecrypt(encrypted.encrypted, options);

2. Not Handling Failures

// ❌ Wrong - assumes all fields decrypt successfully
const result = await client.deepDecrypt(encrypted);
const email = result.data.email;  // Might be corrupted!

// ✅ Correct - check for failures
const result = await client.deepDecrypt(encrypted, {
  fail_gracefully: true
});

if (result.meta.decryptionMetadata.failed_fields.includes('email')) {
  console.warn('Email field is corrupted');
  // Handle appropriately
}

3. Decrypting Unencrypted Data

// ❌ Wrong - no check
const decrypted = await client.deepDecrypt(data);

// ✅ Correct - verify encryption flag
if (data._encrypted === true) {
  const decrypted = await client.deepDecrypt(data);
} else {
  // Data is already in plaintext
  return data;
}

Best Practices

  1. Store encryption options with data
   await database.save({
     data: encrypted.encrypted,
     _encryptionOptions: options
   });
  1. Use graceful failure in production
   const result = await client.deepDecrypt(encrypted, {
     fail_gracefully: process.env.NODE_ENV === 'production'
   });
  1. Monitor failed decryptions
   if (result.meta.decryptionMetadata.failed_fields.length > 0) {
     await metrics.increment('decryption_failures', {
       fields: result.meta.decryptionMetadata.failed_fields
     });
   }
  1. Clear decrypted data after use
   let decrypted = await client.deepDecrypt(encrypted);
   // Use the data
   processData(decrypted.data);
   // Clear from memory
   decrypted = null;

Field Exclusions

Field exclusions allow you to selectively encrypt data while keeping certain fields in plaintext. This is crucial for maintaining searchability, reducing costs, and preserving data structure.

Why Exclude Fields?

1. Maintain Searchability

Database queries require unencrypted fields:

// Keep user ID unencrypted for queries
await client.deepEncrypt(user, {
  exclude_fields: ["userId"]
});

// Now you can query: db.users.find({ userId: "123" })

2. Reduce Costs

Only encrypt sensitive data:

const result = await client.deepEncrypt(data, {
  exclude_patterns: ["_id", "__v", "*_at"]
});

console.log(`Billable fields: ${result.meta.billableFields}`);
// Lower billable fields = lower costs

3. Preserve Metadata

Keep system fields unencrypted:

await client.deepEncrypt(document, {
  exclude_patterns: ["createdAt", "updatedAt", "version"]
});

4. Improve Performance

Fewer encrypted fields = faster operations:

// Exclude non-sensitive fields
await client.deepEncrypt(data, {
  exclude_patterns: ["_id", "metadata.*", "stats.*"]
});

Exclusion Methods

1. Exact Path Matching (exclude_fields)

Specify exact paths to exclude:

const data = {
  id: "user_123",
  profile: {
    name: "John Doe",
    email: "john@example.com"
  },
  settings: {
    theme: "dark",
    language: "en"
  }
};

await client.deepEncrypt(data, {
  exclude_fields: [
    "id",                    // Top-level field
    "profile.email",         // Nested field
    "settings.theme"         // Another nested field
  ]
});

2. Pattern Matching (exclude_patterns)

Use patterns for flexible matching:

await client.deepEncrypt(data, {
  exclude_patterns: [
    "_id",          // Matches any field named "_id"
    "__v",          // Matches any field named "__v"
    "*_at",         // Matches fields ending with "_at"
    "timestamp*",   // Matches fields starting with "timestamp"
    "meta_*"        // Matches fields starting with "meta_"
  ]
});

Common Patterns

MongoDB Documents

const mongoOptions = {
  exclude_patterns: ["_id", "__v", "*At"]
};

await client.deepEncrypt(document, mongoOptions);

User Profiles

const userOptions = {
  exclude_fields: ["id", "username", "role"],
  exclude_patterns: ["*_at", "lastLogin"]
};

E-commerce Orders

const orderOptions = {
  exclude_fields: ["orderId", "customerId", "items[*].productId"],
  exclude_patterns: ["_id", "*_at", "status", "total"]
};