Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Usage

This chapter covers how to use the SlayOps JavaScript library to integrate OpenPGP operations into your website.

Live Demo

You can try out all the features described in this chapter using the live demo at https://localpgp.org/demo.html.

Before using the demo:

  1. Make sure you have the LocalPGP extension installed (see Installation)
  2. Allow the website https://localpgp.org in the extension (click the extension icon and click “Allow”)

Using the Demo Page

The demo page at https://localpgp.org/demo.html provides a complete interface to test all OpenPGP operations. Here’s how to use each section:

Step 1: Connect to the Extension

When you first load the demo page, you’ll see a status bar at the top showing the connection status:

  • “Chrome extension not responding” - The website is not yet allowed. Click the LocalPGP extension icon in your browser toolbar and click “Allow” to grant access.
  • “Connected” (with green indicator) - You’re connected and ready to use OpenPGP operations.

Click the Connect button to establish a connection with the extension.

Step 2: Key Management

The Key Management section lets you browse your GPG keyring:

  1. Click All to list all keys in your keyring
  2. Use the search box to find specific keys by email, name, or fingerprint
  3. Click Search to filter keys

The available keys will be displayed with their user IDs and fingerprints. You can click on a key to use its fingerprint in other operations.

Step 3: Encrypt a Message

In the Encrypt section:

  1. Enter the recipient’s fingerprint or email in the “Recipient Key” field
  2. Type your secret message in the “Message to Encrypt” textarea
  3. Optionally check “Sign while encrypting” to also sign the message
  4. Click 🔒 Encrypt

The encrypted PGP message will appear in the “Encrypted Output” area. You can copy this to send securely.

Step 4: Decrypt a Message

In the Decrypt section:

  1. Paste a PGP encrypted message in the textarea
  2. Click 🔓 Decrypt
  3. If you have the corresponding private key, the decrypted message will appear

You can also click ← Paste from Encrypt to quickly copy the output from the Encrypt section.

Note: If the message was encrypted to a key on a hardware device (like Yubikey), you’ll be prompted to enter your PIN.

Step 5: Sign a Message

In the Sign section:

  1. Optionally enter a specific signing key fingerprint (leave empty to use your default key)
  2. Type the message you want to sign
  3. Choose the signature type:
    • Clearsign - The message and signature are combined (readable message)
    • Detached - Only the signature is output (message stays separate)
  4. Click ✍️ Sign

The signed message or signature will appear in the output area.

Step 6: Verify a Signature

In the Verify section:

  1. Choose the verification mode:
    • Clearsigned - For messages that include the signature
    • Detached Signature - For separate message and signature
  2. Paste the clearsigned message OR the original message and signature
  3. Click ✓ Verify

The verification result will show whether the signature is valid and who signed it.

Additional Features

  • Get Default Key - Shows your default GPG signing key
  • Browser Info - Displays detected browser type and connection details

Quick Start

import { SlayOps } from 'slayops';

// Create instance
const pgp = new SlayOps();

// Connect to the extension
await pgp.connect();

// Now you can use OpenPGP operations
const keys = await pgp.getKeys();
console.log('Available keys:', keys);

Or with a CDN:

<script src="https://unpkg.com/slayops/dist/slayops.js"></script>
<script>
  const pgp = new SlayOps.SlayOps();
  
  async function init() {
    await pgp.connect();
    const keys = await pgp.getKeys();
    console.log('Available keys:', keys);
  }
  
  init();
</script>

Configuration

The SlayOps constructor accepts configuration options:

const pgp = new SlayOps({
  timeout: 30000,        // Request timeout in ms (default: 30000)
  autoConnect: false,    // Auto-connect on creation (default: false)
});

For Development

If you’re developing with a local unpacked extension, you can specify the extension ID:

// Option 1: Single dev ID
const pgp = new SlayOps({ chromeExtensionId: 'your-local-dev-id' });

// Option 2: Try multiple IDs (production first, then dev)
const pgp = new SlayOps({ 
  chromeExtensionIds: [
    'ckgehekhpgcaaikpadklkkjgdgoebdnh',  // Production
    'your-local-dev-id'                    // Dev fallback
  ]
});

Connection Management

Connecting to the Extension

const pgp = new SlayOps();

// Connect to extension
await pgp.connect();

// Check if connected
if (pgp.isConnected()) {
  console.log('Connected to LocalPGP');
}

// Get connection status
const status = pgp.getStatus();
// Returns: 'disconnected' | 'connecting' | 'connected' | 'error'

// Check if extension is available
if (pgp.isExtensionAvailable()) {
  console.log('Extension is installed');
}

// Get human-readable status message
console.log(pgp.getStatusMessage());

Events

SlayOps emits events you can listen to:

pgp.on('connected', () => {
  console.log('Connected to extension!');
});

pgp.on('disconnected', () => {
  console.log('Disconnected from extension');
});

pgp.on('error', (err) => {
  console.error('Error:', err);
});

pgp.on('ready', () => {
  console.log('Extension is ready');
});

// Remove a listener
pgp.off('connected', handler);

Key Management

Listing Keys

// Get all keys
const allKeys = await pgp.getKeys();

// Search for keys by email, name, or fingerprint
const searchResults = await pgp.getKeys('alice@example.com');

// Get only secret keys (keys you can sign/decrypt with)
const secretKeys = await pgp.getKeys('', true);

Getting the Default Key

const defaultKey = await pgp.getDefaultKey();
if (defaultKey) {
  console.log('Default key:', defaultKey.fingerprint);
  console.log('User IDs:', defaultKey.userids);
}

Importing Keys

const armoredPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
...
-----END PGP PUBLIC KEY BLOCK-----`;

await pgp.importKey(armoredPublicKey);
console.log('Key imported successfully');

Encryption

Basic Encryption

// Encrypt a message for a recipient
const encrypted = await pgp.encrypt(
  'Hello, this is a secret message!',
  ['recipient@example.com']  // Can be email, name, or fingerprint
);

console.log(encrypted);
// Output: -----BEGIN PGP MESSAGE-----...

Encrypt for Multiple Recipients

const encrypted = await pgp.encrypt(
  'Secret message for the team',
  [
    'alice@example.com',
    'bob@example.com',
    '5286C32E7C71E14C4C82F9AE0B207108925CB162'  // Fingerprint
  ]
);

Encrypt with Signing

const encrypted = await pgp.encrypt(
  'Signed and encrypted message',
  ['recipient@example.com'],
  {
    sign: true,                    // Sign while encrypting
    signingKey: 'FINGERPRINT',     // Optional: specific signing key
    alwaysTrust: true              // Trust recipients without verification
  }
);

Decryption

Basic Decryption

const encryptedMessage = `-----BEGIN PGP MESSAGE-----
...
-----END PGP MESSAGE-----`;

const result = await pgp.decrypt(encryptedMessage);

console.log('Decrypted:', result.data);

// If the message was signed
if (result.signatures && result.signatures.length > 0) {
  console.log('Signed by:', result.signatures[0].fingerprint);
  console.log('Signature valid:', result.signatures[0].valid);
}

Signing

Clearsign (Message + Signature Together)

// Default: clearsign mode
const signed = await pgp.sign('This is my message');

console.log(signed);
// Output:
// -----BEGIN PGP SIGNED MESSAGE-----
// Hash: SHA256
//
// This is my message
// -----BEGIN PGP SIGNATURE-----
// ...
// -----END PGP SIGNATURE-----

Detached Signature

const signature = await pgp.sign('This is my message', {
  mode: 'detached'
});

console.log(signature);
// Output: -----BEGIN PGP SIGNATURE-----...

Sign with Specific Key

const signed = await pgp.sign('My message', {
  signingKey: '5286C32E7C71E14C4C82F9AE0B207108925CB162'
});

Verification

Verify Clearsigned Message

const clearsignedMessage = `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

This is my message
-----BEGIN PGP SIGNATURE-----
...
-----END PGP SIGNATURE-----`;

const result = await pgp.verify(clearsignedMessage);

console.log('Valid:', result.isValid);
console.log('Signatures:', result.signatures);

// Each signature contains:
// - fingerprint: Signer's key fingerprint
// - valid: Whether the signature is valid
// - status: Detailed status information

Verify Detached Signature

const originalMessage = 'This is my message';
const detachedSignature = `-----BEGIN PGP SIGNATURE-----
...
-----END PGP SIGNATURE-----`;

const result = await pgp.verify(originalMessage, detachedSignature);

console.log('Valid:', result.isValid);

Complete Example

Here’s a complete example showing a typical workflow:

<!DOCTYPE html>
<html>
<head>
  <title>SlayOps Demo</title>
  <script src="https://unpkg.com/slayops/dist/slayops.js"></script>
</head>
<body>
  <h1>OpenPGP Demo</h1>
  
  <div id="status">Connecting...</div>
  <div id="keys"></div>
  
  <h2>Encrypt</h2>
  <textarea id="message" placeholder="Enter message"></textarea>
  <input id="recipient" placeholder="Recipient email or fingerprint">
  <button onclick="encryptMessage()">Encrypt</button>
  <pre id="encrypted-output"></pre>
  
  <h2>Sign</h2>
  <textarea id="sign-message" placeholder="Enter message to sign"></textarea>
  <button onclick="signMessage()">Sign</button>
  <pre id="signed-output"></pre>

  <script>
    const pgp = new SlayOps.SlayOps();
    
    async function init() {
      try {
        await pgp.connect();
        document.getElementById('status').textContent = 'Connected!';
        
        // List keys
        const keys = await pgp.getKeys();
        document.getElementById('keys').innerHTML = 
          '<h3>Available Keys:</h3>' +
          keys.map(k => `<p>${k.userids[0]?.uid || 'No UID'}<br><code>${k.fingerprint}</code></p>`).join('');
      } catch (err) {
        document.getElementById('status').textContent = 'Error: ' + err.message;
      }
    }
    
    async function encryptMessage() {
      const message = document.getElementById('message').value;
      const recipient = document.getElementById('recipient').value;
      
      try {
        const encrypted = await pgp.encrypt(message, [recipient]);
        document.getElementById('encrypted-output').textContent = encrypted;
      } catch (err) {
        document.getElementById('encrypted-output').textContent = 'Error: ' + err.message;
      }
    }
    
    async function signMessage() {
      const message = document.getElementById('sign-message').value;
      
      try {
        const signed = await pgp.sign(message);
        document.getElementById('signed-output').textContent = signed;
      } catch (err) {
        document.getElementById('signed-output').textContent = 'Error: ' + err.message;
      }
    }
    
    init();
  </script>
</body>
</html>

Browser Detection

SlayOps automatically handles browser differences between Chrome and Firefox:

// Get detected browser type
const browserType = pgp.getBrowserType();
// Returns: 'chrome' | 'firefox' | 'firefox-injected' | 'chrome-no-api' | 'unknown'

// Manually trigger browser detection
pgp.detectBrowser();

Error Handling

Always wrap SlayOps calls in try-catch blocks:

try {
  await pgp.connect();
  const encrypted = await pgp.encrypt('message', ['recipient@example.com']);
} catch (err) {
  if (err.message.includes('not responding')) {
    console.error('Extension not installed or website not allowed');
  } else if (err.message.includes('No matching keys')) {
    console.error('Recipient key not found in keyring');
  } else {
    console.error('Operation failed:', err.message);
  }
}

Common errors:

  • Extension not responding: The extension is not installed, or the website is not in the allowed list
  • No matching keys: The recipient’s public key is not in the local GPG keyring
  • Decryption failed: You don’t have the private key for this message, or the message is corrupted
  • Timeout: The operation took too long (e.g., waiting for hardware key PIN entry)