-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
175 additions
and
127 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,164 +1,85 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<html class="bg-gray-50"> | ||
<head> | ||
<title>Tinfoil Verifier</title> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<script src="https://cdn.tailwindcss.com"></script> | ||
</head> | ||
<body> | ||
<body class="min-h-screen"> | ||
<script src="wasm_exec.js"></script> | ||
<script> | ||
function verify() { | ||
let repo = document.getElementById("repo").value; | ||
let digest = document.getElementById("digest").value; | ||
let domain = document.getElementById("domain").value; | ||
let log = document.getElementById("log"); | ||
log.innerText = ""; | ||
<script src="verifier.js"></script> | ||
|
||
function addLog(message) { | ||
let timestamp = new Date().toLocaleString(); | ||
log.innerText += `${timestamp} - ${message}\n`; | ||
} | ||
<div class="container mx-auto p-6"> | ||
<div class="bg-white rounded-lg shadow-sm p-8"> | ||
<h1 class="text-3xl font-bold text-gray-800 mb-4">Tinfoil Verifier</h1> | ||
|
||
addLog(`Verifying ${domain} against EIF digest ${digest}`); | ||
addLog("Loading WASM verifier"); | ||
const go = new Go(); | ||
WebAssembly.instantiateStreaming(fetch("tinfoil-verifier.wasm"), go.importObject).then((result) => { | ||
go.run(result.instance); | ||
addLog("WASM verifier loaded"); | ||
|
||
let bundleURL = "https://api.github.com/repos/" + repo + "/attestations/sha256:" + digest; | ||
addLog("Fetching attestation bundle from " + bundleURL); | ||
let sigstorePromise = fetch(bundleURL) | ||
.catch(error => { | ||
addLog("Failed to fetch attestation bundle from Sigstore: " + error); | ||
addLog("Verification failed"); | ||
}) | ||
.then(response => { | ||
if (response.status !== 200) { | ||
addLog("Failed to fetch attestation bundle from Sigstore: " + response.status); | ||
addLog("Verification failed"); | ||
|
||
} else { | ||
return response.json(); | ||
} | ||
}) | ||
.then(data => { | ||
let bundle = data.attestations[0].bundle; | ||
addLog("Verifying sigstore signature"); | ||
let sigstoreMeasurements = JSON.parse(verifySigstore(digest, JSON.stringify(bundle), repo)); | ||
addLog("Sigstore PCR0: " + sigstoreMeasurements.PCR0); | ||
addLog("Sigstore PCR1: " + sigstoreMeasurements.PCR1); | ||
addLog("Sigstore PCR2: " + sigstoreMeasurements.PCR2); | ||
return sigstoreMeasurements; | ||
}); | ||
|
||
let nitroAttestationURL = `https://${domain}/.well-known/nitro-attestation`; | ||
addLog("Fetching nitro signed attestation from " + nitroAttestationURL); | ||
let nitroPromise = fetch(nitroAttestationURL) | ||
.catch(error => { | ||
addLog("Failed to fetch nitro attestation: " + error); | ||
addLog("Verification failed"); | ||
}) | ||
.then(response => { | ||
if (response.status !== 201) { | ||
addLog("Failed to fetch nitro attestation: " + response.status); | ||
addLog("Verification failed"); | ||
|
||
} else { | ||
return response.json(); | ||
} | ||
}) | ||
.then(nitroAttestation => { | ||
let nitroMeasurements = JSON.parse(verifyNitro(nitroAttestation)); | ||
addLog("Nitro PCR0: " + nitroMeasurements.PCR0); | ||
addLog("Nitro PCR1: " + nitroMeasurements.PCR1); | ||
addLog("Nitro PCR2: " + nitroMeasurements.PCR2); | ||
return nitroMeasurements; | ||
}); | ||
|
||
// Wait for both to finish and print both | ||
Promise.all([sigstorePromise, nitroPromise]).then(([sigstoreMeasurements, nitroMeasurements]) => { | ||
let failed = false; | ||
for (let i = 0; i < 3; i++) { | ||
let sigstorePCR = sigstoreMeasurements["PCR" + i]; | ||
let nitroPCR = nitroMeasurements["PCR" + i]; | ||
|
||
if (sigstorePCR !== nitroPCR) { | ||
addLog(`PCR${i} mismatch`); | ||
failed = true; | ||
} else { | ||
addLog(`PCR${i} match`); | ||
} | ||
} | ||
|
||
if (failed) { | ||
addLog("Verification failed"); | ||
} else { | ||
addLog("Verification successful! ✅"); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
window.addEventListener("load", function () { | ||
verify(); | ||
}); | ||
</script> | ||
</body> | ||
|
||
<div class="container mx-auto p-4"> | ||
<div> | ||
|
||
<h1 class="text-2xl font-bold">Tinfoil Verifier</h1> | ||
|
||
<p class="text-gray-600"> | ||
<p class="text-gray-600 text-md mb-8"> | ||
This page demonstrates how to verify the attestation of a Nitro Enclave using the Tinfoil | ||
verifier. See our <a | ||
class="text-blue-500" | ||
href="https://github.com/tinfoilanalytics/verifier">verifier code on GitHub</a> for more information. | ||
class="text-blue-600 hover:text-blue-800 underline transition-colors" | ||
href="https://github.com/tinfoilanalytics/verifier" | ||
target="_blank" | ||
rel="noopener noreferrer">verifier code on GitHub</a> for more information. | ||
</p> | ||
|
||
<div | ||
class="border border-gray-200 p-4 mt-4" | ||
> | ||
<div class="flex flex-row gap-4 w-full mb-5"> | ||
<div class="w-[50%]"> | ||
<p class="text-gray-600 mb-2">EIF image hash (sha256):</p> | ||
<div class="bg-gray-50 rounded-lg border border-gray-200 p-6"> | ||
<div class="flex gap-6 mb-6"> | ||
<div class="w-[35%]"> | ||
<label for="digest" class="block text-sm font-medium text-gray-700 mb-2">EIF image hash (sha256):</label> | ||
<input | ||
id="digest" | ||
class="border border-gray-200 p-2 w-full" | ||
class="border border-gray-300 rounded-md p-2.5 w-full focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors" | ||
type="text" | ||
value="6d87ba0d92af58c1d740b8aa7d2c3521d8cff96a520502a8b748c3a744ae015f"> | ||
</div> | ||
<div class="w-[35%]"> | ||
<label for="repo" class="block text-sm font-medium text-gray-700 mb-2">Repo:</label> | ||
<input | ||
id="repo" | ||
class="border border-gray-300 rounded-md p-2.5 w-full focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors" | ||
type="text" | ||
value="tinfoilanalytics/nitro-private-inference-image"> | ||
</div> | ||
<div class="w-[20%]"> | ||
<p class="text-gray-600 mb-2">URL:</p> | ||
<label for="domain" class="block text-sm font-medium text-gray-700 mb-2">URL:</label> | ||
<input | ||
id="domain" | ||
class="border border-gray-200 p-2 w-full" | ||
class="border border-gray-300 rounded-md p-2.5 w-full focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors" | ||
type="text" | ||
value="inference.tinfoil.sh"> | ||
</div> | ||
<div class="w-[30%]"> | ||
<p class="text-gray-600 mb-2">Repo:</p> | ||
<input | ||
id="repo" | ||
class="border border-gray-200 p-2 w-full" | ||
type="text" | ||
value="tinfoilanalytics/nitro-private-inference-image"> | ||
<div class="w-[10%] flex items-end"> | ||
<button | ||
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2.5 rounded-md transition-colors duration-200 font-medium w-full" | ||
onclick="verify()"> | ||
Verify | ||
</button> | ||
</div> | ||
</div> | ||
|
||
<div class="flex flex-col gap-4 mb-6"> | ||
<div> | ||
<div class="text-sm font-medium text-gray-600 mb-2">Bundle URL:</div> | ||
<div class="bg-gray-100 p-3 rounded-md"> | ||
<a id="bundleLink" href="#" class="text-blue-600 hover:text-blue-800 break-all text-sm block" target="_blank" rel="noopener noreferrer"></a> | ||
</div> | ||
</div> | ||
<div class="w-[5%] flex items-end"> | ||
<button class="bg-emerald-500 text-white px-4 py-2 w-full" onclick="verify()">Verify</button> | ||
<div> | ||
<div class="text-sm font-medium text-gray-600 mb-2">Attestation URL:</div> | ||
<div class="bg-gray-100 p-3 rounded-md"> | ||
<a id="attestationLink" href="#" class="text-blue-600 hover:text-blue-800 break-all text-sm block" target="_blank" rel="noopener noreferrer"></a> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div | ||
id="log" | ||
class="bg-white p-4 border border-gray-200" | ||
class="bg-white rounded-md p-4 border border-gray-200 min-h-[200px] font-mono text-sm" | ||
></div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
function updateLinks() { | ||
let repo = document.getElementById("repo").value; | ||
let digest = document.getElementById("digest").value; | ||
let domain = document.getElementById("domain").value; | ||
|
||
let bundleURL = "https://api.github.com/repos/" + repo + "/attestations/sha256:" + digest; | ||
let attestationURL = `https://${domain}/.well-known/nitro-attestation`; | ||
|
||
let bundleLink = document.getElementById("bundleLink"); | ||
let attestationLink = document.getElementById("attestationLink"); | ||
|
||
bundleLink.href = bundleURL; | ||
bundleLink.textContent = bundleURL; | ||
|
||
attestationLink.href = attestationURL; | ||
attestationLink.textContent = attestationURL; | ||
} | ||
|
||
function verify() { | ||
let repo = document.getElementById("repo").value; | ||
let digest = document.getElementById("digest").value; | ||
let domain = document.getElementById("domain").value; | ||
let log = document.getElementById("log"); | ||
log.innerText = ""; | ||
|
||
updateLinks(); | ||
|
||
function addLog(message) { | ||
let timestamp = new Date().toLocaleString(); | ||
log.innerText += `${timestamp} - ${message}\n`; | ||
} | ||
|
||
let bundleURL = document.getElementById("bundleLink").href; | ||
let attestationURL = document.getElementById("attestationLink").href; | ||
|
||
addLog(`Verifying ${domain} against EIF digest ${digest}`); | ||
addLog("Loading WASM verifier"); | ||
|
||
const go = new Go(); | ||
WebAssembly.instantiateStreaming(fetch("tinfoil-verifier.wasm"), go.importObject).then((result) => { | ||
go.run(result.instance); | ||
addLog("WASM verifier loaded"); | ||
|
||
addLog("Fetching sigstore attestation bundle from GitHub"); | ||
let sigstorePromise = fetch(bundleURL) | ||
.catch(error => { | ||
addLog("Failed to fetch attestation bundle from Sigstore: " + error); | ||
addLog("Verification failed"); | ||
throw error; | ||
}) | ||
.then(response => { | ||
if (response.status !== 200) { | ||
let error = `Failed to fetch attestation bundle from Sigstore: ${response.status}`; | ||
addLog(error); | ||
throw new Error(error); | ||
} | ||
return response.json(); | ||
}) | ||
.then(data => { | ||
let bundle = data.attestations[0].bundle; | ||
addLog("Verifying sigstore signature"); | ||
let sigstoreMeasurements = JSON.parse(verifySigstore(digest, JSON.stringify(bundle), repo)); | ||
addLog("Sigstore PCR0: " + sigstoreMeasurements.PCR0); | ||
addLog("Sigstore PCR1: " + sigstoreMeasurements.PCR1); | ||
addLog("Sigstore PCR2: " + sigstoreMeasurements.PCR2); | ||
return sigstoreMeasurements; | ||
}); | ||
|
||
addLog("Fetching nitro attestation"); | ||
let nitroPromise = fetch(attestationURL) | ||
.catch(error => { | ||
addLog("Failed to fetch nitro attestation: " + error); | ||
addLog("Verification failed"); | ||
throw error; | ||
}) | ||
.then(response => { | ||
if (response.status !== 201) { | ||
let error = `Failed to fetch nitro attestation: ${response.status}`; | ||
addLog(error); | ||
throw new Error(error); | ||
} | ||
return response.json(); | ||
}) | ||
.then(nitroAttestation => { | ||
let nitroMeasurements = JSON.parse(verifyNitro(nitroAttestation)); | ||
addLog("Nitro PCR0: " + nitroMeasurements.PCR0); | ||
addLog("Nitro PCR1: " + nitroMeasurements.PCR1); | ||
addLog("Nitro PCR2: " + nitroMeasurements.PCR2); | ||
return nitroMeasurements; | ||
}); | ||
|
||
// Wait for both to finish and print both | ||
Promise.all([sigstorePromise, nitroPromise]) | ||
.then(([sigstoreMeasurements, nitroMeasurements]) => { | ||
let failed = false; | ||
for (let i = 0; i < 3; i++) { | ||
let sigstorePCR = sigstoreMeasurements["PCR" + i]; | ||
let nitroPCR = nitroMeasurements["PCR" + i]; | ||
|
||
if (sigstorePCR !== nitroPCR) { | ||
addLog(`PCR${i} mismatch`); | ||
failed = true; | ||
} else { | ||
addLog(`PCR${i} match`); | ||
} | ||
} | ||
|
||
if (failed) { | ||
addLog("Verification failed"); | ||
} else { | ||
addLog("Verification successful! ✅"); | ||
} | ||
}) | ||
.catch(error => { | ||
addLog("Verification failed: " + error); | ||
}); | ||
}); | ||
} | ||
|
||
window.addEventListener("load", function () { | ||
// Update links when any input changes | ||
document.getElementById("repo").addEventListener("input", updateLinks); | ||
document.getElementById("digest").addEventListener("input", updateLinks); | ||
document.getElementById("domain").addEventListener("input", updateLinks); | ||
|
||
verify(); | ||
}); |