From 0aba4762f8a2b1e220acbfc0fc2befcd7cc44ed4 Mon Sep 17 00:00:00 2001 From: Arnaud Brousseau Date: Thu, 9 May 2024 11:24:28 -0500 Subject: [PATCH 1/4] Fix export signature verification --- export/index.template.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/export/index.template.html b/export/index.template.html index c2b1af5..d24c2a0 100644 --- a/export/index.template.html +++ b/export/index.template.html @@ -742,7 +742,14 @@

Message log

if (!TKHQ.verifyEnclaveSignature) { throw new Error("method not loaded"); } - verified = await TKHQ.verifyEnclaveSignature(bundleObj.enclaveQuorumPublic, bundleObj.dataSignature, bundleObj.data.encappedPublic); + + // Temporary solution to get the bytes signed by the enclave + var signedData = /"data":({[^}]+)/.exec(bundle); + if (signedData === null || signedData.length !== 2) { + throw new Error(`unable to find signed data in bundle: ${bundle}`); + } + + verified = await TKHQ.verifyEnclaveSignature(bundleObj.enclaveQuorumPublic, bundleObj.dataSignature, signedData + "}"); if (!verified) { throw new Error(`failed to verify enclave signature: ${bundle}`); } From a42ce6fc09fc6c649c3f5e0d6e54b806aabde039 Mon Sep 17 00:00:00 2001 From: Arnaud Brousseau Date: Thu, 9 May 2024 12:25:10 -0500 Subject: [PATCH 2/4] Fix regex usage --- export/index.template.html | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/export/index.template.html b/export/index.template.html index d24c2a0..61d7a51 100644 --- a/export/index.template.html +++ b/export/index.template.html @@ -749,7 +749,12 @@

Message log

throw new Error(`unable to find signed data in bundle: ${bundle}`); } - verified = await TKHQ.verifyEnclaveSignature(bundleObj.enclaveQuorumPublic, bundleObj.dataSignature, signedData + "}"); + // We add the closing brace back: it was our marker for the end of the group in the regex above, hence not captured. + const signedDataBytes = new TextEncoder().encode(signedData[1] + "}"); + // Encode these bytes to hex since verifyEnclaveSignature expects hex-encoded bytes + const signedDataHexString = TKHQ.uint8arrayToHexString(signedDataBytes); + + verified = await TKHQ.verifyEnclaveSignature(bundleObj.enclaveQuorumPublic, bundleObj.dataSignature, signedDataHexString); if (!verified) { throw new Error(`failed to verify enclave signature: ${bundle}`); } From 7de782f23bc14d748e92b4c1f3530e654d206b99 Mon Sep 17 00:00:00 2001 From: Arnaud Brousseau Date: Thu, 9 May 2024 12:28:15 -0500 Subject: [PATCH 3/4] Patch import/index and import/standalone --- import/index.template.html | 11 ++++++++++- import/standalone.template.html | 12 +++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/import/index.template.html b/import/index.template.html index 5d5f5c7..ea42c25 100644 --- a/import/index.template.html +++ b/import/index.template.html @@ -546,7 +546,16 @@ if (!TKHQ.verifyEnclaveSignature) { throw new Error("method not loaded"); } - verified = await TKHQ.verifyEnclaveSignature(bundleObj.enclaveQuorumPublic, bundleObj.dataSignature, bundleObj.data); + + // Temporary solution to get the bytes signed by the enclave + var signedData = /"data":({[^}]+)/.exec(bundle); + if (signedData === null || signedData.length !== 2) { + throw new Error(`unable to find signed data in bundle: ${bundle}`); + } + const signedDataBytes = new TextEncoder().encode(signedData[1] + "}"); + const signedDataHexString = TKHQ.uint8arrayToHexString(signedDataBytes); + + verified = await TKHQ.verifyEnclaveSignature(bundleObj.enclaveQuorumPublic, bundleObj.dataSignature, signedDataHexString); if (!verified) { throw new Error(`failed to verify enclave signature: ${bundle}`); } diff --git a/import/standalone.template.html b/import/standalone.template.html index 203ef76..78d1ed1 100644 --- a/import/standalone.template.html +++ b/import/standalone.template.html @@ -543,7 +543,17 @@

Message log

if (!TKHQ.verifyEnclaveSignature) { throw new Error("method not loaded"); } - verified = await TKHQ.verifyEnclaveSignature(bundleObj.enclaveQuorumPublic, bundleObj.dataSignature, bundleObj.data); + + // Temporary solution to get the bytes signed by the enclave + var signedData = /"data":({[^}]+)/.exec(bundle); + if (signedData === null || signedData.length !== 2) { + throw new Error(`unable to find signed data in bundle: ${bundle}`); + } + const signedDataBytes = new TextEncoder().encode(signedData[1] + "}"); + const signedDataHexString = TKHQ.uint8arrayToHexString(signedDataBytes); + + verified = await TKHQ.verifyEnclaveSignature(bundleObj.enclaveQuorumPublic, bundleObj.dataSignature, signedDataHexString); + if (!verified) { throw new Error(`failed to verify enclave signature: ${bundle}`); } From db658ca5e2aa614688197cf3edde9bec83156bae Mon Sep 17 00:00:00 2001 From: Arnaud Brousseau Date: Thu, 9 May 2024 12:39:58 -0500 Subject: [PATCH 4/4] Rename publicKey param and document params for verifyEnclaveSignature --- export/index.template.html | 9 ++++++--- import/index.template.html | 9 ++++++--- import/standalone.template.html | 9 ++++++--- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/export/index.template.html b/export/index.template.html index 61d7a51..4b36709 100644 --- a/export/index.template.html +++ b/export/index.template.html @@ -328,8 +328,11 @@

Message log

/** * Function to verify enclave signature on import bundle received from the server. + * @param {string} enclaveQuorumPublic uncompressed public key for the quorum key which produced the signature + * @param {string} publicSignature signature bytes encoded as a hexadecimal string + * @param {string} signedData signed bytes encoded as a hexadecimal string. This could be public key bytes directly, or JSON-encoded bytes */ - async function verifyEnclaveSignature(enclaveQuorumPublic, publicSignature, publicKey) { + async function verifyEnclaveSignature(enclaveQuorumPublic, publicSignature, signedData) { /** Turnkey Signer enclave's public keys */ const TURNKEY_SIGNERS_ENCLAVES = { "prod": "04cf288fe433cc4e1aa0ce1632feac4ea26bf2f5a09dcfe5a42c398e06898710330f0572882f4dbdf0f5304b8fc8703acd69adca9a4bbf7f5d00d20a5e364b2569", @@ -355,8 +358,8 @@

Message log

// The ECDSA signature is ASN.1 DER encoded but WebCrypto uses raw format const publicSignatureBuf = fromDerSignature(publicSignature); - const publicKeyBuf = uint8arrayFromHexString(publicKey); - return await crypto.subtle.verify({ name: "ECDSA", namedCurve: "P-256", hash: {name: "SHA-256" }}, quorumKey, publicSignatureBuf, publicKeyBuf); + const signedDataBuf = uint8arrayFromHexString(signedData); + return await crypto.subtle.verify({ name: "ECDSA", namedCurve: "P-256", hash: {name: "SHA-256" }}, quorumKey, publicSignatureBuf, signedDataBuf); } /** diff --git a/import/index.template.html b/import/index.template.html index ea42c25..9de340f 100644 --- a/import/index.template.html +++ b/import/index.template.html @@ -285,8 +285,11 @@ /** * Function to verify enclave signature on import bundle received from the server. + * @param {string} enclaveQuorumPublic uncompressed public key for the quorum key which produced the signature + * @param {string} publicSignature signature bytes encoded as a hexadecimal string + * @param {string} signedData signed bytes encoded as a hexadecimal string. This could be public key bytes directly, or JSON-encoded bytes */ - async function verifyEnclaveSignature(enclaveQuorumPublic, publicSignature, publicKey) { + async function verifyEnclaveSignature(enclaveQuorumPublic, publicSignature, signedData) { /** Turnkey Signer enclave's public keys */ const TURNKEY_SIGNERS_ENCLAVES = { "prod": "04cf288fe433cc4e1aa0ce1632feac4ea26bf2f5a09dcfe5a42c398e06898710330f0572882f4dbdf0f5304b8fc8703acd69adca9a4bbf7f5d00d20a5e364b2569", @@ -312,8 +315,8 @@ // The ECDSA signature is ASN.1 DER encoded but WebCrypto uses raw format const publicSignatureBuf = fromDerSignature(publicSignature); - const publicKeyBuf = uint8arrayFromHexString(publicKey); - return await crypto.subtle.verify({ name: "ECDSA", namedCurve: "P-256", hash: {name: "SHA-256" }}, quorumKey, publicSignatureBuf, publicKeyBuf); + const signedDataBuf = uint8arrayFromHexString(signedData); + return await crypto.subtle.verify({ name: "ECDSA", namedCurve: "P-256", hash: {name: "SHA-256" }}, quorumKey, publicSignatureBuf, signedDataBuf); } /** diff --git a/import/standalone.template.html b/import/standalone.template.html index 78d1ed1..819ad48 100644 --- a/import/standalone.template.html +++ b/import/standalone.template.html @@ -337,8 +337,11 @@

Message log

/** * Function to verify enclave signature on import bundle received from the server. + * @param {string} enclaveQuorumPublic uncompressed public key for the quorum key which produced the signature + * @param {string} publicSignature signature bytes encoded as a hexadecimal string + * @param {string} signedData signed bytes encoded as a hexadecimal string. This could be public key bytes directly, or JSON-encoded bytes */ - async function verifyEnclaveSignature(enclaveQuorumPublic, publicSignature, publicKey) { + async function verifyEnclaveSignature(enclaveQuorumPublic, publicSignature, signedData) { /** Turnkey Signer enclave's public keys */ const TURNKEY_SIGNERS_ENCLAVES = { "prod": "04cf288fe433cc4e1aa0ce1632feac4ea26bf2f5a09dcfe5a42c398e06898710330f0572882f4dbdf0f5304b8fc8703acd69adca9a4bbf7f5d00d20a5e364b2569", @@ -364,8 +367,8 @@

Message log

// The ECDSA signature is ASN.1 DER encoded but WebCrypto uses raw format const publicSignatureBuf = fromDerSignature(publicSignature); - const publicKeyBuf = uint8arrayFromHexString(publicKey); - return await crypto.subtle.verify({ name: "ECDSA", namedCurve: "P-256", hash: {name: "SHA-256" }}, quorumKey, publicSignatureBuf, publicKeyBuf); + const signedDataBuf = uint8arrayFromHexString(signedData); + return await crypto.subtle.verify({ name: "ECDSA", namedCurve: "P-256", hash: {name: "SHA-256" }}, quorumKey, publicSignatureBuf, signedDataBuf); } /**