Skip to main content

Overview

SEA.secret() creates a shared secret between two users using Elliptic Curve Diffie-Hellman (ECDH) key exchange. This enables two parties to communicate securely without exchanging encryption keys over the network.

Syntax

const secret = await SEA.secret(theirPublicKey, myKeyPair)

Parameters

theirPublicKey
string | object
required
The other user’s public encryption key (epub) or their full key pair object
myKeyPair
object
required
Your key pair object containing epub and epriv (encryption keys)
callback
function
Optional callback function called with the derived secret
options
object
Optional configuration object

Returns

A Promise that resolves to a base64-encoded shared secret string that both parties can use for encryption/decryption.

How it works

From ~/workspace/source/sea/secret.js:1:
SEA.secret = SEA.secret || (async (key, pair, cb, opt) => {
  opt = opt || {};
  if(!pair || !pair.epriv || !pair.epub){
    if(!SEA.I){ throw 'No secret mix.' }
    pair = await SEA.I(null, {what: key, how: 'secret', why: opt.why});
  }
  var pub = key.epub || key;
  var epub = pair.epub;
  var epriv = pair.epriv;
  var ecdhSubtle = shim.ossl || shim.subtle;
  var pubKeyData = keysToEcdhJwk(pub);
  var props = Object.assign({ 
    public: await ecdhSubtle.importKey(...pubKeyData, true, []) 
  },{name: 'ECDH', namedCurve: 'P-256'});
  var privKeyData = keysToEcdhJwk(epub, epriv);
  var derived = await ecdhSubtle.importKey(...privKeyData, false, ['deriveBits'])
    .then(async (privKey) => {
      var derivedBits = await ecdhSubtle.deriveBits(props, privKey, 256);
      var rawBits = new Uint8Array(derivedBits);
      var derivedKey = await ecdhSubte.importKey('raw', rawBits,
        { name: 'AES-GCM', length: 256 }, true, [ 'encrypt', 'decrypt' ]);
      return ecdhSubtle.exportKey('jwk', derivedKey).then(({ k }) => k);
    })
  return derived;
});

ECDH key exchange

1

Alice generates her key pair

Alice creates her public/private encryption key pair using SEA.pair()
2

Bob generates his key pair

Bob also creates his own public/private encryption key pair
3

Exchange public keys

Alice and Bob exchange only their public encryption keys (epub)
4

Derive shared secret

Both parties use SEA.secret() to derive the same shared secret:
  • Alice: SEA.secret(bob.epub, alice)
  • Bob: SEA.secret(alice.epub, bob)
5

Encrypt/decrypt messages

Both can now encrypt and decrypt messages using the shared secret

Basic usage

Generate shared secret

// Alice's side
const alice = await SEA.pair()
const bob = await SEA.pair()

// Alice derives shared secret with Bob's public key
const aliceSecret = await SEA.secret(bob.epub, alice)

// Bob derives the same secret with Alice's public key
const bobSecret = await SEA.secret(alice.epub, bob)

// Both secrets are identical!
console.log(aliceSecret === bobSecret) // true

Encrypted messaging

End-to-end encrypted chat

// Alice sends encrypted message to Bob
async function sendMessage(text, recipient) {
  const user = gun.user()
  const myPair = user._.sea
  
  // Get Bob's public key
  const bobData = await gun.get('~' + recipient).once()
  
  // Derive shared secret
  const secret = await SEA.secret(bobData.epub, myPair)
  
  // Encrypt message
  const encrypted = await SEA.encrypt(text, secret)
  
  // Store encrypted message
  gun.get('messages').get(recipient).set({
    from: myPair.pub,
    msg: encrypted,
    time: Date.now()
  })
}

// Bob receives and decrypts message
async function receiveMessages(sender) {
  const user = gun.user()
  const myPair = user._.sea
  
  // Get Alice's public key
  const aliceData = await gun.get('~' + sender).once()
  
  // Derive same shared secret
  const secret = await SEA.secret(aliceData.epub, myPair)
  
  // Listen for messages
  gun.get('messages').get(myPair.pub).map().on(async (data) => {
    if(data.from === sender) {
      // Decrypt message
      const decrypted = await SEA.decrypt(data.msg, secret)
      console.log('Message from Alice:', decrypted)
    }
  })
}

Private data sharing

// Share private data with specific users
async function shareWithUser(data, recipientPub) {
  const user = gun.user()
  if(!user.is) throw 'Not authenticated'
  
  // Get recipient's public key
  const recipient = await gun.get('~' + recipientPub).once()
  
  // Derive shared secret
  const secret = await SEA.secret(recipient.epub, user._.sea)
  
  // Encrypt data
  const encrypted = await SEA.encrypt(data, secret)
  
  // Store in shared space
  gun.get('~' + user.is.pub).get('shared').get(recipientPub).put({
    data: encrypted,
    time: Date.now()
  })
}

async function readSharedData(senderPub) {
  const user = gun.user()
  if(!user.is) throw 'Not authenticated'
  
  // Get sender's public key
  const sender = await gun.get('~' + senderPub).once()
  
  // Derive shared secret
  const secret = await SEA.secret(sender.epub, user._.sea)
  
  // Read and decrypt
  gun.get('~' + senderPub).get('shared').get(user.is.pub).on(async (data) => {
    if(data && data.data) {
      const decrypted = await SEA.decrypt(data.data, secret)
      console.log('Shared data:', decrypted)
    }
  })
}

Security properties

Perfect Forward Secrecy - Each key pair generates a unique shared secret. Compromising one secret doesn’t affect others.
No key exchange required - Public keys can be shared openly. Only the private keys must remain secret.
Store private keys securely - Never share or expose your epriv (encryption private key). The shared secret is only secure if both parties protect their private keys.

Technical details

Cryptographic algorithm

  • Key exchange: ECDH (Elliptic Curve Diffie-Hellman)
  • Curve: P-256 (secp256r1)
  • Derived key: AES-GCM 256-bit
  • Output: Base64-encoded JWK key material

Key format

The derived secret is a base64-encoded string representing the symmetric encryption key:
const secret = await SEA.secret(bob.epub, alice)
console.log(typeof secret) // 'string'
console.log(secret.length) // ~43 characters (base64)

Use cases

Private messaging

End-to-end encrypted chat between two users

File sharing

Share encrypted files with specific recipients

Collaborative editing

Share documents encrypted for specific collaborators

Peer-to-peer sync

Sync private data between user’s devices

Common patterns

Group encryption

// Encrypt for multiple recipients
async function encryptForGroup(data, recipientPubs) {
  const user = gun.user()
  const encrypted = {}
  
  for(const pub of recipientPubs) {
    const recipient = await gun.get('~' + pub).once()
    const secret = await SEA.secret(recipient.epub, user._.sea)
    encrypted[pub] = await SEA.encrypt(data, secret)
  }
  
  return encrypted
}

Key caching

// Cache derived secrets to avoid re-derivation
const secretCache = new Map()

async function getCachedSecret(recipientPub, myPair) {
  if(secretCache.has(recipientPub)) {
    return secretCache.get(recipientPub)
  }
  
  const recipient = await gun.get('~' + recipientPub).once()
  const secret = await SEA.secret(recipient.epub, myPair)
  
  secretCache.set(recipientPub, secret)
  return secret
}

Troubleshooting

Different secrets generated

Ensure both parties are using the correct public keys:
// Alice derives secret
const aliceSecret = await SEA.secret(bob.epub, alice) // Use bob.epub!

// Bob derives secret
const bobSecret = await SEA.secret(alice.epub, bob) // Use alice.epub!
Common mistakes:
  • Using the wrong public key (pub vs epub)
  • Using signing keys instead of encryption keys
  • Key pair not fully generated before deriving secret

Decryption fails

Verify the shared secret matches:
// Both parties should derive the same secret
const secret1 = await SEA.secret(bob.epub, alice)
const secret2 = await SEA.secret(alice.epub, bob)

console.log('Secrets match:', secret1 === secret2)
If secrets don’t match:
  • Check that public keys were exchanged correctly
  • Ensure key pairs are complete (have both epub and epriv)
  • Verify no corruption during key transmission

See also

SEA.pair()

Generate key pairs for ECDH

SEA.encrypt()

Encrypt data with shared secret

SEA.decrypt()

Decrypt data with shared secret

User Authentication

Authenticate users to access key pairs

Build docs developers (and LLMs) love