mirror of
https://github.com/kkapsner/CanvasBlocker
synced 2024-12-22 12:50:36 +01:00
webGL: added image hash tests
This commit is contained in:
parent
a79c4ec8c5
commit
da5a9f4509
@ -5,6 +5,11 @@
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<link href="testIcon.svg" type="image/png" rel="icon">
|
||||
<link href="testIcon.svg" type="image/png" rel="shortcut icon">
|
||||
<style>
|
||||
th {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>webGL test</h1>
|
||||
@ -12,12 +17,16 @@
|
||||
<ul>
|
||||
<li>both webGL versions should be supported (unless you disabled it in the browser settings) - reload if one is showing "not supported"</li>
|
||||
<li>upon page reload the hashes change (depending on CanvasBlocker settings - e.g. not in the stealth preset)</li>
|
||||
<li>all the image hashes should be the same (offscreen workers are not yet protected by CanvasBlocker)</li>
|
||||
<li>upon page reload some of the parameters (depending on CanvasBlocker settings - e.g. not in the stealth preset)</li>
|
||||
<li>the "vendor" und "renderer" (also unmasked) parameters reflect the values you chose for them</li>
|
||||
</ul>
|
||||
<h3>Support</h3>
|
||||
<div id="output"></div>
|
||||
<h3>Hashes</h3>
|
||||
<div id="hashes"></div>
|
||||
<h3>Parameters</h3>
|
||||
<div id="parameters"></div>
|
||||
<script src="testAPI.js"></script>
|
||||
<script src="webGL-Test.js"></script>
|
||||
</body></html>
|
@ -1,3 +1,4 @@
|
||||
/* globals testAPI */
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
@ -45,13 +46,61 @@
|
||||
return parameters;
|
||||
}
|
||||
|
||||
["webgl", "webgl2"].forEach(async function(context, index){
|
||||
const output = document.createElement("div");
|
||||
document.getElementById("output").appendChild(output);
|
||||
try {
|
||||
function createHashRow(hashTable, rowName){
|
||||
const row = document.createElement("tr");
|
||||
hashTable.appendChild(row);
|
||||
|
||||
const nameCell = document.createElement("th");
|
||||
nameCell.textContent = rowName;
|
||||
row.appendChild(nameCell);
|
||||
|
||||
const valueCell = document.createElement("td");
|
||||
valueCell.innerHTML = "<i>computing</i>";
|
||||
row.appendChild(valueCell);
|
||||
|
||||
return valueCell;
|
||||
}
|
||||
|
||||
function fillWebGlContext(context){
|
||||
// taken from https://github.com/Valve/fingerprintjs2/blob/master/fingerprint2.js
|
||||
const vertexShaderTemplate = "attribute vec2 attrVertex;varying vec2 varyingTexCoordinate;" +
|
||||
"uniform vec2 uniformOffset;void main(){varyingTexCoordinate=attrVertex+uniformOffset;" +
|
||||
"gl_Position=vec4(attrVertex,0,1);}";
|
||||
const fragmentShaderTemplate = "precision mediump float;varying vec2 varyingTexCoordinate;" +
|
||||
"void main() {gl_FragColor=vec4(varyingTexCoordinate,0,1);}";
|
||||
const vertexPosBuffer = context.createBuffer();
|
||||
context.bindBuffer(context.ARRAY_BUFFER, vertexPosBuffer);
|
||||
const vertices = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.732134444, 0]);
|
||||
context.bufferData(context.ARRAY_BUFFER, vertices, context.STATIC_DRAW);
|
||||
vertexPosBuffer.itemSize = 3;
|
||||
vertexPosBuffer.numItems = 3;
|
||||
const program = context.createProgram();
|
||||
const vertexShader = context.createShader(context.VERTEX_SHADER);
|
||||
context.shaderSource(vertexShader, vertexShaderTemplate);
|
||||
context.compileShader(vertexShader);
|
||||
const fragmentShader = context.createShader(context.FRAGMENT_SHADER);
|
||||
context.shaderSource(fragmentShader, fragmentShaderTemplate);
|
||||
context.compileShader(fragmentShader);
|
||||
context.attachShader(program, vertexShader);
|
||||
context.attachShader(program, fragmentShader);
|
||||
context.linkProgram(program);
|
||||
context.useProgram(program);
|
||||
program.vertexPosAttrib = context.getAttribLocation(program, "attrVertex");
|
||||
program.offsetUniform = context.getUniformLocation(program, "uniformOffset");
|
||||
context.enableVertexAttribArray(program.vertexPosArray);
|
||||
context.vertexAttribPointer(program.vertexPosAttrib, vertexPosBuffer.itemSize, context.FLOAT, !1, 0, 0);
|
||||
context.uniform2f(program.offsetUniform, 1, 1);
|
||||
context.drawArrays(context.TRIANGLE_STRIP, 0, vertexPosBuffer.numItems);
|
||||
}
|
||||
|
||||
async function testSupport(context, index){
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = 11;
|
||||
canvas.height = 13;
|
||||
|
||||
const output = document.createElement("div");
|
||||
document.getElementById("output").appendChild(output);
|
||||
try {
|
||||
const gl = canvas.getContext(context) || canvas.getContext("experimental-" + context);
|
||||
|
||||
// paint it completely black
|
||||
@ -67,6 +116,26 @@
|
||||
max = Math.max(max, values[pixels[i]]);
|
||||
}
|
||||
|
||||
output.textContent = context + ": " +
|
||||
(max !== 3 * values[255]? "": "not ") + "supported ";
|
||||
output.title = JSON.stringify(values);
|
||||
}
|
||||
catch (error){
|
||||
output.textContent = context + ": ERROR";
|
||||
output.title = error;
|
||||
}
|
||||
}
|
||||
|
||||
async function testParameters(context, hashTable){
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = 11;
|
||||
canvas.height = 13;
|
||||
|
||||
const hashCell = createHashRow(hashTable, "parameter");
|
||||
|
||||
try {
|
||||
const gl = canvas.getContext(context) || canvas.getContext("experimental-" + context);
|
||||
|
||||
const parameters = getParameters(gl);
|
||||
if (context === "webgl2"){
|
||||
const parameterOutput = document.createElement("table");
|
||||
@ -77,27 +146,82 @@
|
||||
parameterOutput.appendChild(parameterRow);
|
||||
});
|
||||
}
|
||||
const hashBytes = await crypto.subtle.digest("SHA-256", new TextEncoder("utf-8")
|
||||
.encode(parameters.map(function(parameter){
|
||||
|
||||
hashCell.textContent = await testAPI.hash(parameters.map(function(parameter){
|
||||
return parameter.name + ": " + parameter.value;
|
||||
}).join(",")));
|
||||
|
||||
const chunks = [];
|
||||
(new Uint32Array(hashBytes)).forEach(function(num){
|
||||
chunks.push(num.toString(16));
|
||||
});
|
||||
const hash = chunks.map(function(chunk){
|
||||
return "0".repeat(8 - chunk.length) + chunk;
|
||||
}).join("");
|
||||
|
||||
output.textContent = context + ": " +
|
||||
(max !== 3 * values[255]? "": "not ") + "supported " +
|
||||
"(parameter hash: " + hash + ")";
|
||||
output.title = JSON.stringify(values);
|
||||
}).join(","));
|
||||
}
|
||||
catch (error){
|
||||
output.textContent = context + ": ERROR";
|
||||
output.title = error;
|
||||
hashCell.textContent = "ERROR";
|
||||
hashCell.title = error;
|
||||
}
|
||||
}
|
||||
|
||||
async function createImageHash(context, hashTable){
|
||||
return Promise.all([function(){
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = 300;
|
||||
canvas.height = 150;
|
||||
return canvas;
|
||||
}, function(){
|
||||
if (window.OffscreenCanvas){
|
||||
return new OffscreenCanvas(300, 150);
|
||||
}
|
||||
return null;
|
||||
}].map(async function(getter){
|
||||
const canvas = getter();
|
||||
if (canvas){
|
||||
const hashCell = createHashRow(hashTable, "image" + (canvas.toDataURL? "": " (offscreen)"));
|
||||
try {
|
||||
const gl = canvas.getContext(context) || canvas.getContext("experimental-" + context);
|
||||
fillWebGlContext(gl);
|
||||
if (canvas.convertToBlob || canvas.toBlob){
|
||||
const blob = await (canvas.convertToBlob? canvas.convertToBlob(): (canvas.toBlob.length?
|
||||
new Promise(function(resolve){canvas.toBlob(resolve);}):
|
||||
canvas.toBlob()
|
||||
));
|
||||
hashCell.textContent = await testAPI.hash(await testAPI.readBlob(blob));
|
||||
}
|
||||
}
|
||||
catch (error){
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
}
|
||||
|
||||
async function createWorkerImageHash(context, hashTable){
|
||||
if (window.Worker && window.OffscreenCanvas){
|
||||
const hashCell = createHashRow(hashTable, "image (offscreen worker)");
|
||||
const url = new URL("./testAPI.js", location);
|
||||
hashCell.textContent = await testAPI.runInWorker(async function getHash(contextType){
|
||||
const canvas = new OffscreenCanvas(300, 150);
|
||||
const context = canvas.getContext(contextType);
|
||||
fillWebGlContext(context);
|
||||
return await testAPI.hash(
|
||||
await testAPI.readBlob(
|
||||
canvas.convertToBlob?
|
||||
await canvas.convertToBlob():
|
||||
await canvas.toBlob()
|
||||
)
|
||||
);
|
||||
}, [context], [url, fillWebGlContext]);
|
||||
}
|
||||
}
|
||||
|
||||
["webgl", "webgl2"].forEach(async function(context, index){
|
||||
testSupport(context, index);
|
||||
|
||||
const hashOutput = document.createElement("div");
|
||||
document.getElementById("hashes").appendChild(hashOutput);
|
||||
|
||||
hashOutput.textContent = context + ": ";
|
||||
const table = document.createElement("table");
|
||||
hashOutput.appendChild(table);
|
||||
|
||||
testParameters(context, table);
|
||||
createImageHash(context, table);
|
||||
createWorkerImageHash(context, table);
|
||||
});
|
||||
}());
|
Loading…
x
Reference in New Issue
Block a user