SEA.verify()
Verify that a digital signature is valid and was created by the owner of a specific public key. Used to confirm data authenticity and integrity.
Syntax
const verified = await SEA.verify(signed, publicKey)
SEA.verify(signed, publicKey, callback)
SEA.verify(signed, publicKey, callback, options)
Parameters
- signed (string|object): Signed data from
SEA.sign()
- publicKey (string|object): Public key to verify against, or key pair with
pub property
- callback (function, optional): Called with verified data or undefined
- options (object, optional):
encode: Signature encoding (default: ‘base64’)
fallback: Enable fallback verification (default: enabled)
Returns
A Promise that resolves to:
- The original data if signature is valid
- undefined if signature is invalid
Basic Usage
Verify a Signature
const pair = await SEA.pair();
// Sign data
const signed = await SEA.sign('Hello World', pair);
// Verify with public key
const verified = await SEA.verify(signed, pair.pub);
if (verified) {
console.log('Valid! Message:', verified); // 'Hello World'
} else {
console.log('Invalid signature');
}
Verify with Key Pair
const verified = await SEA.verify(signed, pair);
// Uses pair.pub for verification
With Callback
SEA.verify(signed, publicKey, (verified) => {
if (verified) {
console.log('Verified data:', verified);
} else {
console.log('Verification failed');
}
});
How It Works
Reference: ~/workspace/source/sea/verify.js:9-41
- Parse Signed Data: Extracts message (m) and signature (s)
- Import Public Key: Converts public key to Web Crypto format
- Hash Message: Computes SHA-256 hash of the message
- Verify Signature: Uses ECDSA to verify signature against hash
- Return: Original message if valid, undefined if invalid
Verification Process
// 1. Extract message and signature
const { m: message, s: signature } = parseSignedData(signed);
// 2. Hash the message
const hash = await SHA256(message);
// 3. Verify signature
const isValid = await crypto.subtle.verify(
{ name: 'ECDSA', hash: { name: 'SHA-256' } },
publicKey,
signature,
hash
);
// 4. Return message if valid
return isValid ? message : undefined;
Signature Verification
Valid Signature
const alice = await SEA.pair();
const signed = await SEA.sign('Authentic message', alice);
const verified = await SEA.verify(signed, alice.pub);
console.log(verified); // 'Authentic message'
Invalid Signature (Wrong Key)
const alice = await SEA.pair();
const bob = await SEA.pair();
const signed = await SEA.sign('Message', alice);
const verified = await SEA.verify(signed, bob.pub); // Wrong key!
console.log(verified); // undefined (failed)
Tampered Data
const signed = await SEA.sign('Original', pair);
// Tamper with the signed data
const tampered = JSON.parse(signed.slice(3)); // Remove 'SEA' prefix
tampered.m = 'Modified'; // Change message
const resigned = 'SEA' + JSON.stringify(tampered);
const verified = await SEA.verify(resigned, pair.pub);
console.log(verified); // undefined (tampered data detected)
Skip Verification
Pass false as the public key to skip verification and extract the message:
const signed = await SEA.sign('data', pair);
const message = await SEA.verify(signed, false);
console.log(message); // 'data' (unverified!)
Using SEA.verify(data, false) bypasses security. Only use this when you explicitly want to extract the message without verification, such as for debugging or when verification happens elsewhere.
Use Cases
Verify User Messages
gun.get('messages').map().on(async (signed, id) => {
const senderPub = signed.from; // Sender's public key
const verified = await SEA.verify(signed.data, senderPub);
if (verified) {
displayMessage({
from: senderPub,
message: verified,
verified: true
});
} else {
console.warn('Invalid signature for message:', id);
}
});
Verify Identity
const verifyIdentity = async (claim, publicKey) => {
const verified = await SEA.verify(claim, publicKey);
if (!verified) {
throw new Error('Identity verification failed');
}
return {
pub: publicKey,
identity: verified,
verified_at: Date.now()
};
};
const identity = await verifyIdentity(signedClaim, alice.pub);
Verify Transactions
const verifyTransaction = async (signedTx) => {
const tx = await SEA.verify(signedTx, signedTx.m.from);
if (!tx) {
throw new Error('Invalid transaction signature');
}
if (tx.amount <= 0) {
throw new Error('Invalid amount');
}
return tx;
};
const validTx = await verifyTransaction(incomingTx);
processTransaction(validTx);
Verify File Integrity
const verifyFile = async (file, signature, authorPub) => {
const fileHash = await SEA.work(file, null, null, { name: 'SHA-256' });
const verified = await SEA.verify(signature, authorPub);
if (verified === fileHash) {
console.log('File integrity verified');
return true;
} else {
console.warn('File has been modified or signature is invalid');
return false;
}
};
Fallback Verification
SEA includes fallback verification for backward compatibility with older signature formats.
Reference: ~/workspace/source/sea/verify.js:55-79
Automatic Fallback
// Modern signature fails, tries legacy format automatically
const verified = await SEA.verify(legacySignedData, publicKey);
// Disable fallback (strict mode)
SEA.opt.fallback = 0;
const verified = await SEA.verify(signed, pub);
// Only accepts current signature format
Security Considerations
Trust the Public Key
Verification only confirms the signature matches the public key. You must trust that the public key belongs to the claimed entity:
const verified = await SEA.verify(signed, publicKey);
if (verified) {
// Signature is valid for this public key
// But: Do you trust this public key?
const isTrusted = await checkPublicKeyTrust(publicKey);
if (isTrusted) {
processVerifiedData(verified);
}
}
Message Integrity
Verification guarantees:
- Authenticity: Message was signed by holder of private key
- Integrity: Message has not been modified
- Non-repudiation: Signer cannot deny creating the signature
Verification does NOT guarantee:
- The public key belongs to who you think it does
- The message content is truthful
- The signer had authority to make the claim
Memory Leak Prevention
SEA caches imported public keys to prevent memory leaks:
Reference: ~/workspace/source/sea/verify.js:46-52
// Keys are cached and reused
const key1 = await importKey(publicKey);
const key2 = await importKey(publicKey); // Returns cached key
Error Handling
Invalid Signature
const verified = await SEA.verify(signed, publicKey);
if (!verified) {
// Could be:
// - Wrong public key
// - Tampered data
// - Corrupted signature
// - Invalid format
console.error('Verification failed');
}
With Callback
SEA.verify(signed, publicKey, (verified) => {
if (verified === undefined) {
console.error('Verification failed');
return;
}
console.log('Success:', verified);
});
Try-Catch
try {
const verified = await SEA.verify(signed, publicKey);
if (!verified) {
throw new Error('Invalid signature');
}
process(verified);
} catch (err) {
console.error('Verification error:', err);
}
- Speed: ~1-3ms per verification (varies by device)
- Caching: Public keys are cached for reuse
- Async: Always asynchronous
const start = Date.now();
const verified = await SEA.verify(signed, publicKey);
console.log('Verified in', Date.now() - start, 'ms');
Batch Verification
const signedMessages = [...];
const verified = await Promise.all(
signedMessages.map(signed =>
SEA.verify(signed, publicKey)
)
);
const valid = verified.filter(v => v !== undefined);
console.log(`${valid.length} of ${signedMessages.length} verified`);
Common Patterns
Verify Before Processing
const processSignedData = async (signed, expectedPub) => {
const verified = await SEA.verify(signed, expectedPub);
if (!verified) {
throw new Error('Signature verification failed');
}
return processData(verified);
};
Multi-signature Verification
const verifyMultiSig = async (multiSig, requiredSigners) => {
const verified = await Promise.all(
requiredSigners.map(async (pub) => {
const sig = multiSig.signatures[pub];
return await SEA.verify(sig, pub);
})
);
return verified.every(v => v !== undefined);
};
const isValid = await verifyMultiSig(multiSig, [alice.pub, bob.pub]);
Verify Chain of Signatures
const verifyChain = async (signedChain, signers) => {
let data = signedChain;
// Verify in reverse order (last signer first)
for (const pub of signers.reverse()) {
data = await SEA.verify(data, pub);
if (!data) return false;
}
return data; // Original data if all valid
};
const original = await verifyChain(chain, [alice.pub, bob.pub, charlie.pub]);
Trust List Verification
const trustedKeys = new Set([
alice.pub,
bob.pub,
charlie.pub
]);
const verifyTrusted = async (signed) => {
// Try each trusted key
for (const pub of trustedKeys) {
const verified = await SEA.verify(signed, pub);
if (verified) {
return { verified, signer: pub };
}
}
return null; // No trusted signer found
};
const result = await verifyTrusted(incomingMessage);
if (result) {
console.log('Signed by trusted key:', result.signer);
}
Integration with GUN
Auto-verification
GUN automatically verifies signatures when reading user data:
gun.get('~' + publicKey).on((data) => {
// GUN automatically verifies signatures
// Only data with valid signatures is returned
});
Manual Verification
gun.get('announcements').map().on(async (signed) => {
const verified = await SEA.verify(signed, adminPublicKey);
if (verified) {
displayAnnouncement(verified);
} else {
console.warn('Unverified announcement ignored');
}
});