Skip to main content

SEA.decrypt()

Decrypt data that was encrypted with SEA.encrypt(). Requires the same key that was used for encryption.

Syntax

const decrypted = await SEA.decrypt(encrypted, key)
SEA.decrypt(encrypted, key, callback)
SEA.decrypt(encrypted, key, callback, options)

Parameters

  • encrypted (string|object): Encrypted data from SEA.encrypt()
  • key (string|object): Decryption key or key pair with epriv property
  • callback (function, optional): Called with decrypted result
  • options (object, optional):
    • encode: Encoding of encrypted data (default: ‘base64’)
    • name: Decryption algorithm (default: ‘AES-GCM’)

Returns

A Promise that resolves to the decrypted data in its original format.

Basic Usage

Decrypt with Password

const encrypted = await SEA.encrypt('secret', 'password');
const decrypted = await SEA.decrypt(encrypted, 'password');

console.log(decrypted);  // 'secret'

Decrypt with Key Pair

const pair = await SEA.pair();
const encrypted = await SEA.encrypt('data', pair);
const decrypted = await SEA.decrypt(encrypted, pair);

console.log(decrypted);  // 'data'

With Callback

SEA.decrypt(encrypted, 'password', (decrypted) => {
  if (decrypted) {
    console.log('Decrypted:', decrypted);
  } else {
    console.error('Decryption failed');
  }
});

How It Works

Reference: ~/workspace/source/sea/decrypt.js:8-40
  1. Parse Encrypted Data: Extracts ct, iv, and s from encrypted string/object
  2. Decode: Converts base64 strings to binary buffers
  3. Derive Key: Reconstructs AES key from input key + salt
  4. Decrypt: Uses AES-GCM to decrypt ciphertext
  5. Parse Result: Converts decrypted data back to original format

Decryption Process

// 1. Parse encrypted data
const { ct, iv, s } = parseEncrypted(encrypted);

// 2. Derive same AES key used for encryption
const aesKey = await deriveKey(key, s);

// 3. Decrypt
const decrypted = await crypto.subtle.decrypt(
  { 
    name: 'AES-GCM', 
    iv: iv,
    tagLength: 128 
  },
  aesKey,
  ct
);

// 4. Parse result (JSON if object, string otherwise)
return parse(decrypted);

Input Formats

String Format (Default)

const encrypted = 'SEA{"ct":"...","iv":"...","s":"..."}';
const decrypted = await SEA.decrypt(encrypted, key);

Object Format

const encrypted = {
  ct: 'base64_ciphertext',
  iv: 'base64_iv',
  s: 'base64_salt'
};
const decrypted = await SEA.decrypt(encrypted, key);

Decryption Key Types

Using a Password

const decrypted = await SEA.decrypt(encrypted, 'same-password-used-to-encrypt');

Using Key Pair

const pair = await SEA.pair();
// Encrypted with this pair
const encrypted = await SEA.encrypt(data, pair);
// Decrypt with same pair
const decrypted = await SEA.decrypt(encrypted, pair);

Using epriv Directly

const decrypted = await SEA.decrypt(encrypted, pair.epriv);

Using Shared Secret

const alice = await SEA.pair();
const bob = await SEA.pair();

// Alice encrypts
const secret = await SEA.secret(bob.epub, alice);
const encrypted = await SEA.encrypt('message', secret);

// Bob decrypts
const bobSecret = await SEA.secret(alice.epub, bob);
const decrypted = await SEA.decrypt(encrypted, bobSecret);
console.log(decrypted);  // 'message'

Data Type Preservation

Decryption restores the original data type:

Strings

const encrypted = await SEA.encrypt('Hello', key);
const decrypted = await SEA.decrypt(encrypted, key);
console.log(typeof decrypted);  // 'string'

Objects

const encrypted = await SEA.encrypt({ name: 'Alice' }, key);
const decrypted = await SEA.decrypt(encrypted, key);
console.log(decrypted.name);  // 'Alice'

Arrays

const encrypted = await SEA.encrypt([1, 2, 3], key);
const decrypted = await SEA.decrypt(encrypted, key);
console.log(Array.isArray(decrypted));  // true

Numbers

const encrypted = await SEA.encrypt(42, key);
const decrypted = await SEA.decrypt(encrypted, key);
console.log(typeof decrypted);  // 'number'

Error Handling

Wrong Key

try {
  const decrypted = await SEA.decrypt(encrypted, 'wrong-key');
} catch (err) {
  console.error('Could not decrypt');  // Wrong key fails silently or throws
}

With Callbacks

SEA.decrypt(encrypted, key, (result) => {
  if (!result) {
    console.error('Decryption failed - wrong key or corrupted data');
    return;
  }
  console.log('Success:', result);
});

Corrupted Data

try {
  const decrypted = await SEA.decrypt('invalid-data', key);
} catch (err) {
  console.error('Malformed encrypted data');
}
Decryption failures often return undefined rather than throwing errors. Always check the result before using it.

Options

Custom Encoding

If data was encrypted with custom encoding:
const encrypted = await SEA.encrypt(data, key, null, { encode: 'hex' });
const decrypted = await SEA.decrypt(encrypted, key, null, { encode: 'hex' });

Fallback Encoding

Reference: ~/workspace/source/sea/decrypt.js:24-28 SEA automatically falls back to ‘utf8’ encoding for legacy data:
// Automatic fallback if base64 decryption fails
const decrypted = await SEA.decrypt(legacyEncrypted, key);
// Tries base64 first, then utf8

Use Cases

Decrypt Private User Data

gun.user().get('private').once(async (encrypted) => {
  if (!encrypted) return;
  
  const decrypted = await SEA.decrypt(encrypted, user._.sea);
  console.log('Private data:', decrypted);
});

End-to-End Encrypted Messaging

// Bob receives message from Alice
gun.get('messages').get(msgId).once(async (data) => {
  // Derive shared secret
  const secret = await SEA.secret(alice.epub, bob);
  
  // Decrypt message
  const message = await SEA.decrypt(data.encrypted, secret);
  console.log('Message from Alice:', message);
});

Decrypt Backup

const backupFile = await loadFile('backup.enc');
const password = prompt('Enter backup password:');

try {
  const data = await SEA.decrypt(backupFile, password);
  console.log('Backup restored:', data);
} catch (err) {
  alert('Wrong password or corrupted backup');
}

Conditional Decryption

gun.get('data').on(async (data) => {
  // Check if data is encrypted (starts with "SEA")
  if (typeof data === 'string' && data.startsWith('SEA')) {
    data = await SEA.decrypt(data, user._.sea);
  }
  displayData(data);
});

Security Considerations

Authentication Tag

AES-GCM includes authentication:
  • Verifies data integrity
  • Detects tampering
  • Decryption fails if ciphertext modified
Reference: ~/workspace/source/sea/decrypt.js:22
{
  name: 'AES-GCM',
  iv: iv,
  tagLength: 128  // 128-bit authentication tag
}

Key Security

Keep decryption keys secure:
  • Never log keys to console
  • Don’t send keys over unencrypted channels
  • Store keys encrypted when possible
  • Use the same key that was used for encryption

Timing Attacks

Web Crypto API implementations use constant-time operations to prevent timing attacks.

Performance

  • Speed: Very fast (hardware-accelerated)
  • Async: Always asynchronous
  • Memory: Efficient (streaming decryption where supported)
const start = Date.now();
const decrypted = await SEA.decrypt(encrypted, key);
console.log('Decrypted in', Date.now() - start, 'ms');

Common Patterns

Decrypt Helper Function

const decryptIfNeeded = async (data, key) => {
  if (typeof data === 'string' && data.startsWith('SEA')) {
    return await SEA.decrypt(data, key);
  }
  return data;  // Not encrypted
};

const result = await decryptIfNeeded(maybeEncrypted, key);

Batch Decryption

const encryptedItems = ['SEA{...}', 'SEA{...}', 'SEA{...}'];
const decrypted = await Promise.all(
  encryptedItems.map(item => SEA.decrypt(item, key))
);

Safe Decryption

const safeDecrypt = async (encrypted, key, fallback = null) => {
  try {
    const result = await SEA.decrypt(encrypted, key);
    return result || fallback;
  } catch (err) {
    console.error('Decryption error:', err);
    return fallback;
  }
};

const data = await safeDecrypt(encrypted, key, { default: true });

Integration with GUN

Auto-decrypt User Data

gun.user().get('secrets').on(async (encrypted) => {
  if (!user.is) return;  // Not authenticated
  
  const decrypted = await SEA.decrypt(encrypted, user._.sea);
  updateUI(decrypted);
});

Middleware Pattern

gun.get('private-data').on(async (data) => {
  // Automatically decrypt all incoming data
  if (data && typeof data === 'string' && data.startsWith('SEA')) {
    data = await SEA.decrypt(data, user._.sea);
  }
  return data;
});

Troubleshooting

Decryption Returns Undefined

Causes:
  • Wrong decryption key
  • Corrupted encrypted data
  • Data was not encrypted
  • Different encoding used
Solution:
const result = await SEA.decrypt(data, key);
if (!result) {
  // Try fallback or show error
  console.error('Decryption failed');
}

“Could not decrypt” Error

Cause: Encoding mismatch or corrupted data Solution:
try {
  // Try with fallback encoding
  return await SEA.decrypt(data, key, null, { encode: 'utf8' });
} catch (err) {
  console.error('All decryption attempts failed');
}

Build docs developers (and LLMs) love