Compare commits

...
2 Commits
8 changed files with 73 additions and 45 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "nostr_quantum_preparation",
"version": "0.0.35",
"version": "0.0.37",
"description": "A migration strategy for bringing post-quantum security to Nostr without breaking the social graph, without requiring consensus on a single post-quantum algorithm, and without forcing existing users to abandon their identities.",
"main": "index.js",
"scripts": {
-3
View File
@@ -549,9 +549,6 @@
<div id="pqEventIdDisplay" style="margin: 5px 0; font-size: 11px; color: var(--primary-color); word-break: break-all;"></div>
<div class="pq-event-preview" id="pqSuccessEventPreview"></div>
<div class="pq-info-text" id="pqEventSizeWarning" style="display: none; font-size: 13px; margin-top: 8px;"></div>
<div class="pq-button-row" style="margin-top: 10px;">
<button class="pq-button pq-button-secondary" id="pqCopyProofCarrierBtn">Copy Proof Carrier Event</button>
</div>
<div style="margin-top: 15px;">
<div class="pq-info-text" style="margin-bottom: 5px;"><strong>Relays to publish to:</strong></div>
+43 -12
View File
@@ -16,6 +16,7 @@
bytesToBase64,
base64ToBytes,
bytesToHex,
hexToNpub,
PQ_KEY_INFO,
NIP_QR_KIND,
buildUpgradedEvent,
@@ -1050,16 +1051,6 @@
if (e.key === 'Enter') { e.preventDefault(); document.getElementById('pqRelayAddBtn').click(); }
});
// Copy Proof Carrier Event button
document.getElementById('pqCopyProofCarrierBtn').addEventListener('click', () => {
if (!pqEvent) return;
navigator.clipboard.writeText(JSON.stringify(pqEvent, null, 2)).then(() => {
const btn = document.getElementById('pqCopyProofCarrierBtn');
btn.textContent = 'Copied!';
setTimeout(() => { btn.textContent = 'Copy Proof Carrier Event'; }, 2000);
});
});
// Sign out button
const pqSignOutBtn = document.getElementById('pqSignOutBtn');
pqSignOutBtn.addEventListener('click', async () => {
@@ -1318,7 +1309,7 @@
const saved = loadPendingOts();
if (!saved || !saved.confirmedPublished) {
setStatus(document.getElementById('pqOtsStatus'), 'success', 'Bitcoin attestation verified. Publishing new kind 9999 with confirmed proof...');
await publishUpgradedEvent(pendingOtsBytes);
await publishUpgradedEvent(pendingOtsBytes, att ? att.height : null, att ? att.time : null);
}
} else if (isOtsConfirmed(pendingOtsBytes)) {
// Structural check found a Bitcoin attestation tag but full
@@ -1350,7 +1341,7 @@
otsPollInterval = setInterval(poll, 60000);
}
async function publishUpgradedEvent(upgradedOtsBytes) {
async function publishUpgradedEvent(upgradedOtsBytes, bitcoinHeight = null, bitcoinTime = null) {
const otsStatus = document.getElementById('pqOtsStatus');
try {
otsLog('Building upgraded proof carrier with confirmed OTS proof...');
@@ -1385,6 +1376,46 @@
kind1Event
});
// Publish a kind 1 reply announcing the confirmed attestation.
// This is a regular Nostr text note (reply to the original kind 1
// announcement) so it shows up in feeds/thread views. It only needs
// the secp256k1 signature — no PQ signatures.
if (kind1Event && kind1Event.id) {
try {
otsLog('Publishing kind 1 reply announcing confirmed attestation...');
const npub = hexToNpub(currentPubkey);
const dateStr = bitcoinTime
? new Date(bitcoinTime * 1000).toISOString().substring(0, 19)
: 'unknown';
const blockStr = bitcoinHeight ? `Bitcoin block ${bitcoinHeight} (mined ${dateStr} UTC)` : 'a Bitcoin block';
const replyContent = `✅ The OpenTimestamps proof for my post-quantum key attestation has been confirmed in ${blockStr}. The link between my current Nostr identity and my post-quantum keys is now anchored to the Bitcoin blockchain and cannot be backdated.
Verify it here: https://laantungir.net/quantum-prep/verify.html?npub=${npub}
This is a reply to my original attestation announcement. The confirmed proof is carried in a new kind 9999 event referencing the original.`;
const replyTemplate = {
kind: 1,
content: replyContent,
tags: [
['e', kind1Event.id, '', 'root'],
['e', validatedEvent.id, '', 'mention']
],
pubkey: currentPubkey,
created_at: Math.floor(Date.now() / 1000)
};
const replySigned = await window.nostr.signEvent(replyTemplate);
const replyValidated = validateSignerOutput(replySigned, replyTemplate, currentPubkey);
const replyResults = await publishToRelays(replyValidated, relayUrls);
const replyOk = replyResults.filter(r => r.success).length;
const replyFail = replyResults.filter(r => !r.success).length;
otsLog(`Kind 1 reply published. Successful: ${replyOk}; failed: ${replyFail}.`);
} catch (replyErr) {
// The upgraded proof carrier is already published; a failed reply
// is non-fatal — log it but don't fail the whole operation.
otsLog(`WARNING: kind 1 reply failed: ${replyErr.message}`);
}
}
setStatus(otsStatus, 'success', `Upgraded proof carrier published to ${successCount} relays. Timestamping complete.`);
setStepDone(6);
setStepActive(7);
+2 -1
View File
@@ -638,7 +638,8 @@ Post-quantum public keys in tags:
Each post-quantum key has cryptographically signed this attestation. This event is pending timestamp on the Bitcoin blockchain via OpenTimestamps.
Verify this attestation: https://laantungir.net/quantum-prep/verify.html
Verify this attestation: https://laantungir.net/quantum-prep/verify.html?npub=${npub}
Created at: https://laantungir.net/quantum-prep/`;
const statementBytes = new TextEncoder().encode(content);
+22 -21
View File
@@ -49,7 +49,6 @@
const resultsWrap = document.getElementById('pqResultsWrap');
const resultList = document.getElementById('pqResultList');
const eventJsonWrap = document.getElementById('pqEventJsonWrap');
const downloadEventBtn = document.getElementById('pqDownloadEventBtn');
const eventJsonEl = document.getElementById('pqEventJson');
const otsBadge = document.getElementById('pqOtsBadge');
const otsUpgradeWrap = document.getElementById('pqOtsUpgradeWrap');
@@ -496,22 +495,6 @@
});
}
/* ================================================================
DOWNLOAD EVENT JSON
================================================================ */
downloadEventBtn.addEventListener('click', () => {
if (!currentEvent) return;
const blob = new Blob([JSON.stringify(currentEvent, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = (currentEvent.id ? currentEvent.id.substring(0, 16) : 'event') + '.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
/* ================================================================
OTS UPGRADE (from verify page)
================================================================ */
@@ -750,12 +733,30 @@
});
/* ================================================================
AUTO-DETECT SIGNED-IN USER
On page load, check for a NIP-07 signer / nostr-login-lite. If the
user is already signed in, pre-fill the pubkey field and auto-query
relays for their kind 9999 event.
URL ?npub= PARAMETER + AUTO-DETECT SIGNED-IN USER
On page load, first check for a ?npub= query parameter (used by the
"Verify this attestation" link in the kind 1 event). If present,
pre-fill the pubkey field and auto-query relays for that pubkey's
kind 9999 event. Otherwise, fall back to detecting a NIP-07 signer /
nostr-login-lite session and auto-query for the signed-in user.
================================================================ */
async function initAutoDetect() {
// 1. Check for ?npub= (or ?pubkey=) URL parameter first
const params = new URLSearchParams(window.location.search);
const npubParam = params.get('npub') || params.get('pubkey');
if (npubParam) {
const pubkeyHex = normalizePubkey(npubParam);
if (pubkeyHex) {
console.log('[verify] URL parameter pubkey:', pubkeyHex);
const pubkeyInput = document.getElementById('pqPubkeyInput');
const npub = hexToNpub(pubkeyHex);
pubkeyInput.value = npub || pubkeyHex;
queryBtn.click();
return;
}
}
// 2. Fall back to signed-in user detection
if (window.NOSTR_LOGIN_LITE) {
try {
await initNostrLoginLite();
+3 -3
View File
@@ -1,5 +1,5 @@
{
"VERSION": "v0.0.35",
"VERSION_NUMBER": "0.0.35",
"BUILD_DATE": "2026-07-27T17:03:35.968Z"
"VERSION": "v0.0.37",
"VERSION_NUMBER": "0.0.37",
"BUILD_DATE": "2026-07-27T21:08:31.939Z"
}
+2 -1
View File
@@ -9929,7 +9929,8 @@ Post-quantum public keys in tags:
Each post-quantum key has cryptographically signed this attestation. This event is pending timestamp on the Bitcoin blockchain via OpenTimestamps.
Verify this attestation: https://laantungir.net/quantum-prep/verify.html
Verify this attestation: https://laantungir.net/quantum-prep/verify.html?npub=${npub}
Created at: https://laantungir.net/quantum-prep/`;
const statementBytes = new TextEncoder().encode(content);
const mlDsa44Sig = signWithMLDSA44(statementBytes, pqKeys.mlDsa44.secretKey);
-3
View File
@@ -326,9 +326,6 @@
<div id="pqEventJsonWrap" style="display: none; margin-top: 15px;">
<div class="pq-info-text" style="margin-bottom: 5px;"><strong>Full event JSON:</strong></div>
<div class="pq-event-preview" id="pqEventJson"></div>
<div class="pq-button-row" style="margin-top: 10px;">
<button class="pq-button pq-button-secondary" id="pqDownloadEventBtn">Download Event JSON</button>
</div>
</div>
<!-- OTS upgrade section -->