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
The other user’s public encryption key (epub) or their full key pair object
Your key pair object containing epub and epriv (encryption keys)
Optional callback function called with the derived secret
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
Alice generates her key pair
Alice creates her public/private encryption key pair using SEA.pair()
Bob generates his key pair
Bob also creates his own public/private encryption key pair
Exchange public keys
Alice and Bob exchange only their public encryption keys (epub)
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)
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
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
Why are my derived secrets different?
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
Why can't I decrypt the message?
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