From 326321697469bb2df26af78cddf3eb031b02a19f Mon Sep 17 00:00:00 2001 From: ITScutaru Date: Tue, 1 Jul 2025 12:50:36 +0300 Subject: [PATCH] enforce double signature --- lib/commands/block.js | 179 ++++++++++++++++++++++++++++-------------- 1 file changed, 119 insertions(+), 60 deletions(-) diff --git a/lib/commands/block.js b/lib/commands/block.js index e05e10d48..1afb7dab9 100644 --- a/lib/commands/block.js +++ b/lib/commands/block.js @@ -77,47 +77,74 @@ Block.validateLoginBlock = function (Env, publicKey, signature, block, pqPublicK if (sigType === 1 && u8_signature.length > 65) { // Hybrid signature - check the classical part first const classicalSig = u8_signature.subarray(1, 1 + 64); // Ed25519 signature is 64 bytes - verified = Nacl.sign.detached.verify(hash, classicalSig, u8_public_key); + const classicalVerified = Nacl.sign.detached.verify(hash, classicalSig, u8_public_key); - // Check for PQ signature as well, but don't require it to pass - if (verified && ml_kem && ml_dsa) { - try { - // Get PQ signature length from the 4 bytes after classical signature - const pqSigLenView = new DataView(u8_signature.buffer, u8_signature.byteOffset + 65); - const pqSigLen = pqSigLenView.getUint32(0, false); // big-endian + if (!classicalVerified) { + Env.Log.error('BLOCK_CLASSICAL_VERIFICATION_FAILED', { + blockId: publicKey + }); + return void cb('E_COULD_NOT_VERIFY_CLASSICAL'); + } - // Extract PQ signature - const pqSig = u8_signature.subarray(1 + 64 + 4, 1 + 64 + 4 + pqSigLen); + // Now check PQ signature - this is required for hybrid signatures + let pqVerified = false; - // If PQ public key is provided, verify the PQ signature - let publicKeyToUse = pqPublicKey; - if (!publicKeyToUse && Env.blockInfo && Env.blockInfo[publicKey] && Env.blockInfo[publicKey].pqPublicKey) { - publicKeyToUse = Env.blockInfo[publicKey].pqPublicKey; - } + if (!ml_kem || !ml_dsa) { + Env.Log.error('BLOCK_PQ_VERIFICATION_ERROR', { + error: 'PQ libraries not available', + blockId: publicKey + }); + return void cb('E_PQ_LIBRARIES_MISSING'); + } - if (publicKeyToUse) { - const pqPublicKeyDecoded = Util.decodeBase64(publicKeyToUse); + try { + // Get PQ signature length from the 4 bytes after classical signature + const pqSigLenView = new DataView(u8_signature.buffer, u8_signature.byteOffset + 65); + const pqSigLen = pqSigLenView.getUint32(0, false); // big-endian - const pqVerified = ml_dsa.ml_dsa44.internal.verify( - pqPublicKeyDecoded, - hash, - pqSig - ); + // Extract PQ signature + const pqSig = u8_signature.subarray(1 + 64 + 4, 1 + 64 + 4 + pqSigLen); - console.log("PQ verification result:", pqVerified); + // If PQ public key is provided, verify the PQ signature + let publicKeyToUse = pqPublicKey; + if (!publicKeyToUse && Env.blockInfo && Env.blockInfo[publicKey] && Env.blockInfo[publicKey].pqPublicKey) { + publicKeyToUse = Env.blockInfo[publicKey].pqPublicKey; + } - Env.Log.info('BLOCK_PQ_VERIFICATION_RESULT', { - blockId: publicKey, - result: pqVerified - }); - } - } catch (err) { + if (!publicKeyToUse) { Env.Log.error('BLOCK_PQ_VERIFICATION_ERROR', { - error: err.message, + error: 'PQ public key not available', blockId: publicKey }); + return void cb('E_MISSING_PQ_PUBLIC_KEY'); } + + const pqPublicKeyDecoded = Util.decodeBase64(publicKeyToUse); + + pqVerified = ml_dsa.ml_dsa44.internal.verify( + pqPublicKeyDecoded, + hash, + pqSig + ); + + Env.Log.info('BLOCK_PQ_VERIFICATION_RESULT', { + blockId: publicKey, + result: pqVerified + }); + + if (!pqVerified) { + return void cb('E_COULD_NOT_VERIFY_PQ'); + } + } catch (err) { + Env.Log.error('BLOCK_PQ_VERIFICATION_ERROR', { + error: err.message, + blockId: publicKey + }); + return void cb('E_PQ_VERIFICATION_ERROR'); } + + // Both classical and PQ signatures verified + verified = classicalVerified && pqVerified; } else { // Classical signature or unrecognized format - use normal verification // For type 0 we need to skip the first byte @@ -151,49 +178,81 @@ Block.validateAncestorProof = function (Env, proof, _cb) { if (sigType === 1 && u8_sig.length > 65) { // Hybrid signature - check the classical part first const classicalSig = u8_sig.subarray(1, 1 + 64); // Ed25519 signature is 64 bytes - valid = Nacl.sign.detached.verify(u8_pub, classicalSig, u8_pub); + const classicalVerified = Nacl.sign.detached.verify(u8_pub, classicalSig, u8_pub); - // Check for PQ signature as well, but don't require it to pass - if (valid && ml_kem && ml_dsa) { - try { - // Get PQ signature length from the 4 bytes after classical signature - const pqSigLenView = new DataView(u8_sig.buffer, u8_sig.byteOffset + 65); - const pqSigLen = pqSigLenView.getUint32(0, false); // big-endian + if (!classicalVerified) { + Env.Log.error('ANCESTOR_CLASSICAL_VERIFICATION_FAILED', { + blockId: pub + }); + w.abort(); + return void cb('E_INVALID_ANCESTOR_PROOF_CLASSICAL'); + } - // Extract PQ signature - const pqSig = u8_sig.subarray(1 + 64 + 4, 1 + 64 + 4 + pqSigLen); + // Now check PQ signature - this is required for hybrid signatures + let pqVerified = false; - // If PQ public key is provided, verify the PQ signature - let pqPublicKey; - if (Env.blockInfo && Env.blockInfo[pub] && Env.blockInfo[pub].pqPublicKey) { - pqPublicKey = Env.blockInfo[pub].pqPublicKey; - } else if (ancestorPqPublicKey) { - pqPublicKey = ancestorPqPublicKey; - } + if (!ml_kem || !ml_dsa) { + Env.Log.error('ANCESTOR_PQ_VERIFICATION_ERROR', { + error: 'PQ libraries not available', + blockId: pub + }); + w.abort(); + return void cb('E_PQ_LIBRARIES_MISSING'); + } - if (pqPublicKey) { - const pqPublicKeyDecoded = Util.decodeBase64(pqPublicKey); + try { + // Get PQ signature length from the 4 bytes after classical signature + const pqSigLenView = new DataView(u8_sig.buffer, u8_sig.byteOffset + 65); + const pqSigLen = pqSigLenView.getUint32(0, false); // big-endian - const pqVerified = ml_dsa.ml_dsa44.internal.verify( - pqPublicKeyDecoded, - u8_pub, - pqSig - ); + // Extract PQ signature + const pqSig = u8_sig.subarray(1 + 64 + 4, 1 + 64 + 4 + pqSigLen); - console.log("PQ verification result 2:", pqVerified); + // If PQ public key is provided, verify the PQ signature + let pqPublicKey; + if (Env.blockInfo && Env.blockInfo[pub] && Env.blockInfo[pub].pqPublicKey) { + pqPublicKey = Env.blockInfo[pub].pqPublicKey; + } else if (ancestorPqPublicKey) { + pqPublicKey = ancestorPqPublicKey; + } - Env.Log.info('ANCESTOR_PQ_VERIFICATION_RESULT', { - blockId: pub, - result: pqVerified - }); - } - } catch (err) { + if (!pqPublicKey) { Env.Log.error('ANCESTOR_PQ_VERIFICATION_ERROR', { - error: err.message, + error: 'PQ public key not available', blockId: pub }); + w.abort(); + return void cb('E_MISSING_PQ_PUBLIC_KEY'); } + + const pqPublicKeyDecoded = Util.decodeBase64(pqPublicKey); + + pqVerified = ml_dsa.ml_dsa44.internal.verify( + pqPublicKeyDecoded, + u8_pub, + pqSig + ); + + Env.Log.info('ANCESTOR_PQ_VERIFICATION_RESULT', { + blockId: pub, + result: pqVerified + }); + + if (!pqVerified) { + w.abort(); + return void cb('E_INVALID_ANCESTOR_PROOF_PQ'); + } + } catch (err) { + Env.Log.error('ANCESTOR_PQ_VERIFICATION_ERROR', { + error: err.message, + blockId: pub + }); + w.abort(); + return void cb('E_PQ_VERIFICATION_ERROR'); } + + // Both classical and PQ signatures verified + valid = classicalVerified && pqVerified; } else { // Classical signature or unrecognized format - use normal verification // For type 0 we need to skip the first byte