or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

asymmetric-cryptography.mddigital-signatures.mdhash-functions.mdhmac.mdindex.mdkey-derivation.mdkey-exchange.mdrandom-generation.mdsymmetric-encryption.md
tile.json

key-exchange.mddocs/

Key Exchange

Diffie-Hellman and Elliptic Curve Diffie-Hellman protocols for secure key establishment between parties without prior shared secrets.

Capabilities

Diffie-Hellman Key Exchange

Standard Diffie-Hellman key exchange using discrete logarithms over finite fields.

/**
 * Create a Diffie-Hellman instance with custom prime
 * @param {number|string|Buffer} prime - Prime number as number, string, or Buffer
 * @param {string} [primeEncoding] - Encoding of prime if string ('hex', 'base64', etc.)
 * @returns {DiffieHellman} DiffieHellman instance
 */
function createDiffieHellman(prime, primeEncoding) { }

/**
 * Create a Diffie-Hellman instance from predefined group
 * @param {string} name - Standard group name (e.g., 'modp1', 'modp2', 'modp5')
 * @returns {DiffieHellmanGroup} DiffieHellmanGroup instance
 */
function createDiffieHellmanGroup(name) { }

/**
 * Get a predefined Diffie-Hellman group
 * @param {string} groupName - Standard group name
 * @returns {DiffieHellmanGroup} DiffieHellmanGroup instance
 */
function getDiffieHellman(groupName) { }

/**
 * DiffieHellman object for key exchange operations
 * @class DiffieHellman
 */
/**
 * Generate public and private key pair
 * @param {string} [encoding] - Output encoding for public key
 * @returns {Buffer|string} Public key as Buffer or encoded string
 */
DiffieHellman.prototype.generateKeys = function(encoding) { };

/**
 * Compute shared secret with other party's public key
 * @param {string|Buffer} otherPublicKey - Other party's public key
 * @param {string} [inputEncoding] - Encoding of input key if string
 * @param {string} [outputEncoding] - Encoding for shared secret output
 * @returns {Buffer|string} Shared secret as Buffer or encoded string
 */
DiffieHellman.prototype.computeSecret = function(otherPublicKey, inputEncoding, outputEncoding) { };

/**
 * Get the public key
 * @param {string} [encoding] - Output encoding
 * @returns {Buffer|string} Public key as Buffer or encoded string
 */
DiffieHellman.prototype.getPublicKey = function(encoding) { };

/**
 * Get the private key
 * @param {string} [encoding] - Output encoding
 * @returns {Buffer|string} Private key as Buffer or encoded string
 */
DiffieHellman.prototype.getPrivateKey = function(encoding) { };

/**
 * Set the public key
 * @param {string|Buffer} publicKey - Public key to set
 * @param {string} [encoding] - Encoding of input key if string
 */
DiffieHellman.prototype.setPublicKey = function(publicKey, encoding) { };

/**
 * Set the private key
 * @param {string|Buffer} privateKey - Private key to set
 * @param {string} [encoding] - Encoding of input key if string
 */
DiffieHellman.prototype.setPrivateKey = function(privateKey, encoding) { };

/**
 * Get the prime number
 * @param {string} [encoding] - Output encoding
 * @returns {Buffer|string} Prime as Buffer or encoded string
 */
DiffieHellman.prototype.getPrime = function(encoding) { };

/**
 * Get the generator
 * @param {string} [encoding] - Output encoding
 * @returns {Buffer|string} Generator as Buffer or encoded string
 */
DiffieHellman.prototype.getGenerator = function(encoding) { };

/**
 * Verify the prime number
 * @returns {number} Verification result flags
 */
DiffieHellman.prototype.verifyError = function() { };

/**
 * DiffieHellmanGroup object (inherits all DiffieHellman methods)
 * @class DiffieHellmanGroup
 * @extends DiffieHellman
 */
function DiffieHellmanGroup() { }
DiffieHellmanGroup.prototype = Object.create(DiffieHellman.prototype);

Elliptic Curve Diffie-Hellman (ECDH)

Elliptic curve variant of Diffie-Hellman for more efficient key exchange.

/**
 * Create an ECDH instance for the specified curve
 * @param {string} curveName - Elliptic curve name (e.g., 'secp256k1', 'prime256v1')
 * @returns {ECDH} ECDH instance
 */
function createECDH(curveName) { }

/**
 * ECDH object for elliptic curve key exchange operations
 * @class ECDH
 */
/**
 * Generate public and private key pair
 * @param {string} [encoding] - Output encoding for public key
 * @param {string} [format] - Point format ('compressed', 'uncompressed', 'hybrid')
 * @returns {Buffer|string} Public key as Buffer or encoded string
 */
ECDH.prototype.generateKeys = function(encoding, format) { };

/**
 * Compute shared secret with other party's public key
 * @param {string|Buffer} otherPublicKey - Other party's public key
 * @param {string} [inputEncoding] - Encoding of input key if string
 * @param {string} [outputEncoding] - Encoding for shared secret output
 * @returns {Buffer|string} Shared secret as Buffer or encoded string
 */
ECDH.prototype.computeSecret = function(otherPublicKey, inputEncoding, outputEncoding) { };

/**
 * Get the public key
 * @param {string} [encoding] - Output encoding
 * @param {string} [format] - Point format ('compressed', 'uncompressed', 'hybrid')
 * @returns {Buffer|string} Public key as Buffer or encoded string
 */
ECDH.prototype.getPublicKey = function(encoding, format) { };

/**
 * Get the private key
 * @param {string} [encoding] - Output encoding
 * @returns {Buffer|string} Private key as Buffer or encoded string
 */
ECDH.prototype.getPrivateKey = function(encoding) { };

/**
 * Set the private key
 * @param {string|Buffer} privateKey - Private key to set
 * @param {string} [encoding] - Encoding of input key if string
 */
ECDH.prototype.setPrivateKey = function(privateKey, encoding) { };

/**
 * Set the public key
 * @param {string|Buffer} publicKey - Public key to set
 * @param {string} [encoding] - Encoding of input key if string
 */
ECDH.prototype.setPublicKey = function(publicKey, encoding) { };

Class Constructors

Direct access to key exchange classes.

/**
 * DiffieHellman class constructor
 * @constructor
 */
function DiffieHellman() { }

/**
 * DiffieHellmanGroup class constructor
 * @constructor
 */
function DiffieHellmanGroup() { }

Usage Examples

Basic Diffie-Hellman Key Exchange

Establish shared secret between two parties using standard DH groups.

const crypto = require('crypto-browserify');

// Both parties use the same predefined group
const alice = crypto.createDiffieHellmanGroup('modp14');
const bob = crypto.createDiffieHellmanGroup('modp14');

// Each party generates their key pair
const alicePublicKey = alice.generateKeys('hex');
const bobPublicKey = bob.generateKeys('hex');

console.log('Alice public key:', alicePublicKey);
console.log('Bob public key:', bobPublicKey);

// Each party computes the shared secret using the other's public key
const aliceSharedSecret = alice.computeSecret(bobPublicKey, 'hex', 'hex');
const bobSharedSecret = bob.computeSecret(alicePublicKey, 'hex', 'hex');

console.log('Alice shared secret:', aliceSharedSecret);
console.log('Bob shared secret:', bobSharedSecret);
console.log('Secrets match:', aliceSharedSecret === bobSharedSecret);

Custom Diffie-Hellman Parameters

Create DH instance with custom prime and generator.

const crypto = require('crypto-browserify');

// Generate a prime (in practice, use well-known safe primes)
const prime = Buffer.from('ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca237327ffffffffffffffff', 'hex');

const generator = 2;

// Create DH instance with custom parameters
const alice = crypto.createDiffieHellman(prime, generator);
const bob = crypto.createDiffieHellman(prime, generator);

// Generate keys and compute shared secret
const alicePublicKey = alice.generateKeys();
const bobPublicKey = bob.generateKeys();

const sharedSecret = alice.computeSecret(bobPublicKey);
console.log('Shared secret:', sharedSecret.toString('hex'));

Elliptic Curve Diffie-Hellman (ECDH)

More efficient key exchange using elliptic curves.

const crypto = require('crypto-browserify');

// Create ECDH instances using the same curve
const alice = crypto.createECDH('secp256k1');
const bob = crypto.createECDH('secp256k1');

// Generate key pairs
const alicePublicKey = alice.generateKeys('hex', 'compressed');
const bobPublicKey = bob.generateKeys('hex', 'compressed');

console.log('Alice ECDH public key:', alicePublicKey);
console.log('Bob ECDH public key:', bobPublicKey);

// Compute shared secrets
const aliceSharedSecret = alice.computeSecret(bobPublicKey, 'hex', 'hex');
const bobSharedSecret = bob.computeSecret(alicePublicKey, 'hex', 'hex');

console.log('ECDH shared secret match:', aliceSharedSecret === bobSharedSecret);
console.log('ECDH shared secret:', aliceSharedSecret);

Key Exchange Protocol Implementation

Complete key exchange protocol with key derivation.

const crypto = require('crypto-browserify');

class KeyExchangeProtocol {
  constructor(groupName = 'modp14') {
    this.dh = crypto.createDiffieHellmanGroup(groupName);
    this.publicKey = this.dh.generateKeys();
  }
  
  getPublicKey(encoding = 'base64') {
    return this.dh.getPublicKey(encoding);
  }
  
  computeSharedSecret(otherPublicKey, encoding = 'base64') {
    const sharedSecret = this.dh.computeSecret(otherPublicKey, encoding);
    
    // Derive symmetric keys from shared secret
    const symmetricKey = crypto.pbkdf2Sync(sharedSecret, 'key-derivation-salt', 1000, 32, 'sha256');
    const macKey = crypto.pbkdf2Sync(sharedSecret, 'mac-derivation-salt', 1000, 32, 'sha256');
    
    return {
      sharedSecret: sharedSecret.toString('hex'),
      symmetricKey: symmetricKey.toString('hex'),
      macKey: macKey.toString('hex')
    };
  }
}

// Simulate key exchange between two parties
const alice = new KeyExchangeProtocol();
const bob = new KeyExchangeProtocol();

console.log('=== Key Exchange Protocol ===');

// Exchange public keys
const alicePublicKey = alice.getPublicKey();
const bobPublicKey = bob.getPublicKey();

console.log('Alice sends public key:', alicePublicKey);
console.log('Bob sends public key:', bobPublicKey);

// Compute derived keys
const aliceKeys = alice.computeSharedSecret(bobPublicKey);
const bobKeys = bob.computeSharedSecret(alicePublicKey);

console.log('Alice derived keys:', aliceKeys);
console.log('Bob derived keys:', bobKeys);
console.log('Keys match:', JSON.stringify(aliceKeys) === JSON.stringify(bobKeys));

Authenticated Key Exchange

Key exchange with authentication to prevent man-in-the-middle attacks.

const crypto = require('crypto-browserify');

class AuthenticatedKeyExchange {
  constructor(privateKey, publicKey, groupName = 'modp14') {
    this.privateKey = privateKey; // RSA private key for signing
    this.publicKey = publicKey;   // RSA public key for verification
    this.dh = crypto.createDiffieHellmanGroup(groupName);
    this.dhPublicKey = this.dh.generateKeys();
  }
  
  createSignedPublicKey() {
    const publicKeyHex = this.dh.getPublicKey('hex');
    
    // Sign the DH public key
    const sign = crypto.createSign('RSA-SHA256');
    sign.update(publicKeyHex);
    const signature = sign.sign(this.privateKey, 'base64');
    
    return {
      dhPublicKey: publicKeyHex,
      signature: signature,
      timestamp: Date.now()
    };
  }
  
  verifyAndComputeSecret(signedPublicKey, otherRsaPublicKey) {
    // Verify the signature
    const verify = crypto.createVerify('RSA-SHA256');
    verify.update(signedPublicKey.dhPublicKey);
    const isValid = verify.verify(otherRsaPublicKey, signedPublicKey.signature, 'base64');
    
    if (!isValid) {
      throw new Error('Invalid signature - possible man-in-the-middle attack');
    }
    
    // Check timestamp to prevent replay attacks
    const now = Date.now();
    if (now - signedPublicKey.timestamp > 300000) { // 5 minutes
      throw new Error('Signature too old - possible replay attack');
    }
    
    // Compute shared secret
    const sharedSecret = this.dh.computeSecret(signedPublicKey.dhPublicKey, 'hex');
    
    return {
      sharedSecret: sharedSecret.toString('hex'),
      verified: true,
      timestamp: signedPublicKey.timestamp
    };
  }
}

// Example with RSA keys for authentication
const aliceRsaKeys = {
  private: `-----BEGIN PRIVATE KEY-----\n...Alice's RSA private key...\n-----END PRIVATE KEY-----`,
  public: `-----BEGIN PUBLIC KEY-----\n...Alice's RSA public key...\n-----END PUBLIC KEY-----`
};

const bobRsaKeys = {
  private: `-----BEGIN PRIVATE KEY-----\n...Bob's RSA private key...\n-----END PRIVATE KEY-----`,
  public: `-----BEGIN PUBLIC KEY-----\n...Bob's RSA public key...\n-----END PUBLIC KEY-----`
};

// Note: In practice, you would use real RSA keys
// const alice = new AuthenticatedKeyExchange(aliceRsaKeys.private, aliceRsaKeys.public);
// const bob = new AuthenticatedKeyExchange(bobRsaKeys.private, bobRsaKeys.public);

ECDH with Key Derivation

ECDH with proper key derivation for different purposes.

const crypto = require('crypto-browserify');

function performECDHKeyExchange(curveName = 'prime256v1') {
  // Create ECDH instances for both parties
  const alice = crypto.createECDH(curveName);
  const bob = crypto.createECDH(curveName);
  
  // Generate key pairs
  const aliceKeys = {
    private: alice.generateKeys(),
    public: alice.getPublicKey('hex', 'compressed')
  };
  
  const bobKeys = {
    private: bob.generateKeys(),
    public: bob.getPublicKey('hex', 'compressed')
  };
  
  // Compute shared secret
  const sharedSecret = alice.computeSecret(bob.getPublicKey());
  
  // Derive multiple keys for different purposes
  const keys = {
    encryptionKey: crypto.pbkdf2Sync(sharedSecret, 'encryption', 10000, 32, 'sha256'),
    macKey: crypto.pbkdf2Sync(sharedSecret, 'authentication', 10000, 32, 'sha256'),
    kdfKey: crypto.pbkdf2Sync(sharedSecret, 'key-derivation', 10000, 32, 'sha256')
  };
  
  return {
    alicePublic: aliceKeys.public,
    bobPublic: bobKeys.public,
    sharedSecret: sharedSecret.toString('hex'),
    derivedKeys: {
      encryption: keys.encryptionKey.toString('hex'),
      mac: keys.macKey.toString('hex'),
      kdf: keys.kdfKey.toString('hex')
    }
  };
}

// Usage
const result = performECDHKeyExchange('secp256k1');
console.log('ECDH Key Exchange Result:', result);

Supported Curves and Groups

Diffie-Hellman Groups

Standard predefined groups available:

  • 'modp1' - 768-bit prime (RFC 2409) - Deprecated
  • 'modp2' - 1024-bit prime (RFC 2409) - Deprecated
  • 'modp5' - 1536-bit prime (RFC 3526)
  • 'modp14' - 2048-bit prime (RFC 3526) - Recommended
  • 'modp15' - 3072-bit prime (RFC 3526)
  • 'modp16' - 4096-bit prime (RFC 3526)

Elliptic Curves

Common curves supported:

  • 'prime256v1' - NIST P-256 (recommended)
  • 'secp256k1' - Bitcoin curve
  • 'secp384r1' - NIST P-384
  • 'secp521r1' - NIST P-521

Security Considerations

Group Selection

  • Use at least 2048-bit DH groups (modp14 or higher)
  • Prefer well-known, standardized groups
  • For ECDH, use standardized curves like prime256v1

Key Validation

  • Validate received public keys are in the correct range
  • Check for small subgroup attacks
  • Verify group parameters if using custom groups

Forward Secrecy

  • Generate new key pairs for each session
  • Don't reuse DH private keys
  • Clear sensitive data from memory after use

Authentication

  • Always authenticate key exchange to prevent MITM attacks
  • Use digital signatures or certificates
  • Include timestamps to prevent replay attacks

Error Handling

Key exchange functions may throw errors in the following scenarios:

  • Invalid curve/group: Unsupported curve name or group
  • Invalid public key: Malformed or invalid public key data
  • Point not on curve: ECDH public key not on the specified curve
  • Weak parameters: DH parameters that don't meet security requirements
const crypto = require('crypto-browserify');

try {
  const ecdh = crypto.createECDH('invalid-curve');
} catch (err) {
  console.error('Unsupported curve:', err.message);
}

try {
  const alice = crypto.createECDH('prime256v1');
  alice.generateKeys();
  
  // Invalid public key format
  const secret = alice.computeSecret('invalid-key', 'hex');
} catch (err) {
  console.error('Invalid public key:', err.message);
}

// Safe key exchange with error handling
function safeKeyExchange(curveName, otherPublicKey) {
  try {
    const ecdh = crypto.createECDH(curveName);
    ecdh.generateKeys();
    
    const secret = ecdh.computeSecret(otherPublicKey, 'hex');
    return { success: true, secret: secret.toString('hex') };
  } catch (err) {
    return { success: false, error: err.message };
  }
}