-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
audioLies + audio data #67
Comments
for (let i=0; i < getTest.length; i++) {
let x = getTest[i]
sum2 += x
sum3 += Math.abs(x)
if (i > 4499 && i < 5000) {sum += Math.abs(x)}
}
console.log("sum: "+ sum +"\nsum2: "+ sum2 +"\nsum3: "+ sum3) my machine
|
note to self: currently leveraging
the current test needs a massive upgrade
polyfill
|
@abrahamjuliot add here as you get results blink: also see https://fingerprintjs.com/blog/audio-fingerprinting/
firefox
FYI: current min/max if (isFF) {
min = 35.738329
max = 35.78334
} |
reminder to self: split up the output better, so RFP protected keys are split from the rest: when I rebuild the audio section |
Here's a back door to get audio entropy using DynamicsCompressorNode.reduction. I tested in FF and Chrome on Windows, Android, and ChromeOS. It's also unprotected in Brave. By default, it's context.oncomplete = function(event) {
const compressorGainReduction = pxi_compressor.reduction
//... I have not examined this yet, but the result might mirror the entropy in the sum fingerprint. So, if the reduction is a known value, we can infer the sum fingerprint. FF on Windows:
Some observations in Brave... Here's a quick function to detect Brave's standard farbling (based on the FPJS fudge factor detection): const getNoiseFactor = () => {
const buffer = new AudioBuffer({
length: 1,
sampleRate: 44100
})
buffer.getChannelData(0)[0] = 1
return buffer.getChannelData(0)[0]
}
const noiseFactor = getNoiseFactor()
console.log(`Audio Noise Detected: ${noiseFactor != 1 ? noiseFactor : 'none'}`) Brave strict mode has a max unique rate, so it too can be detected const bins = buffer.getChannelData(0)
const numOfUniqueValues = new Set([...bins]).size // Brave strict returns the length of the buffer |
nice. I need to finish my revamped domrect first
And then it's onto audio. I already had ideas on how other ways to detect lies, but the more in the arsenal, the better. I'm just not sure how far down this rabbit hole I want to go as I believe entropy is SFA
Will check out your code bits when I get back to this |
@sgmenda - I'm not too far away from rebuilding the audio section - maybe a week I plan on building a stand-alone test first which
There will be a run button, as this allows us to run audio tests behind a user gesture
So something similar to the webgl test you and @tomrittervg set up , but one where we can eliminate the noise. The standalone test would be minus the submission which can be added on tom's site. And then you drill down into the data by OS, by RFP, and even by version (which shouldn't matter but can weed out old releases), and even see the correlation between the math entropy. FWIW, it seems as if If you're interested in chipping in here and collaborating when and if you have time, rather than us all doing our own thing ... I can add you as an admin - just confirm with me via email PS: the webgl parameters is also not far away: see
|
that's the other way round: math is a subset of audio |
OK, am onto it in theory .. getting a stable known result across all platforms, that audio randomizes are triggered into adding noise to should be totally doable with a non-sinusoidal periodic waveform .. e.g. a square wave, perhaps a sawtooth wave, or a flat line ... that said the triangle wave is also non-sinusoidal (but we don't really have any data sets on if this wave contains any entropy within a platform - but note that CB's tests include it, but for testing randomizing, not entropy) flat wave .. I wonder how hard it would be to generate one at a specific "height" and the sum is shitloads of decimal places, you know like 4/3 = 1.333333333333333333333333333333333... class discuss homework for @abrahamjuliot : https://en.wikipedia.org/wiki/Rectangular_function |
alrighty .. now we're cooking
Firefox audio FPs taken from appendix C and figure 10 @abrahamjuliot .. you can add that paper to your collection and repo, to save me typing this shit out twice, TIA - edit, you can also add https://www.cse.chalmers.se/~andrei/secweb21.pdf which is interesting |
😀 homework for abraham: calculate Pi to 62.8tn decimal places for max entropy Hmmm ... Pi is used in fourier transformations AFAIK, so if Pi has entropy in android ... something does in function cbrt(x) {
let y = Math.pow(Math.abs(x), 1 / 3)
return x < 0 ? -y : y
} |
I now have the code they used (not sure if I can share it yet). At quick glance, it looks like it's has combined the audio tests behind user gestures e.g. OscillatorNode etc .. and I think it has also included the audioContext keys I also have all the data, including the sums, such as @sgmenda - have you built the web package yet: I'm kinda lost on how to: I just want the js |
Sorry for the late response.
I haven't yet had a chance to. I will try to take a look over the weekend and update here if I figure it out. |
cool ... https://en.wikipedia.org/wiki/User:LucasVB/Gallery#Waves really cool ... https://bl.ocks.org/jinroh/7524988 .. with code |
see arkenfox/user.js#1701 (comment) - basically mozilla have standardized the math library in audio - read https://bugzilla.mozilla.org/show_bug.cgi?id=1358149#c56 in FF118+ but in my ToDo #169 under audio I have
now a lot of that can be ignored, now we know audio is RFP protected, so I don't need to do sawtooth, or other tests, but I may do a standalone more comprehensive test: I have the code from those researchers too. but I was interested in some of creepy's code: for example I do not have these
is there any point in adding more? also what to make of #67 (comment) @karlt FYI |
Here's a simplified concept that gets the gain, freq, time. From what I recall, all the default Node values produce no entropy connected with the audio rendering or system configuration, but there might be something there I missed. function getRenderedAudioData() {
// samples get filled around 200, so we could speed this up within
// 200-5000 bins
const context = new OfflineAudioContext(1, 5000, 44100)
const analyser = context.createAnalyser()
const dynamicsCompressor = context.createDynamicsCompressor()
const oscillator = context.createOscillator()
const biquadFilter = context.createBiquadFilter() // optional if we want the values
oscillator.type = 'triangle'
oscillator.frequency.value = 10000
dynamicsCompressor.threshold.value = -50
dynamicsCompressor.knee.value = 40
dynamicsCompressor.attack.value = 0
analyser.connect(context.destination);
dynamicsCompressor.connect(analyser);
dynamicsCompressor.connect(context.destination);
oscillator.connect(dynamicsCompressor);
oscillator.start(0);
context.startRendering()
return new Promise((resolve) => {
context.oncomplete = (event) => {
const channelData = event.renderedBuffer.getChannelData(0)
const freqData = new Float32Array(analyser.frequencyBinCount)
const timeData = new Float32Array(analyser.fftSize)
analyser.getFloatFrequencyData(freqData)
analyser.getFloatTimeDomainData(timeData)
dynamicsCompressor.disconnect()
oscillator.disconnect()
// reduction gets rendered with the audio
// value is not implemented older WebKit, so we fallback to reduction
const { reduction } = dynamicsCompressor
const gainReduction = Number(reduction.value || reduction) || 0;
// Optionally collect or hash the values below
/*
analyser.channelCount
analyser.channelCountMode
analyser.channelInterpretation
analyser.context.sampleRate
analyser.fftSize
analyser.frequencyBinCount
analyser.maxDecibels
analyser.minDecibels
analyser.numberOfInputs
analyser.numberOfOutputs
analyser.smoothingTimeConstant
analyser.context.listener.forwardX.maxValue
biquadFilter.gain.maxValue
biquadFilter.frequency.defaultValue
biquadFilter.frequency.maxValue
dynamicsCompressor.attack.defaultValue
dynamicsCompressor.knee.defaultValue
dynamicsCompressor.knee.maxValue
dynamicsCompressor.ratio.defaultValue
dynamicsCompressor.ratio.maxValue
dynamicsCompressor.release.defaultValue
dynamicsCompressor.release.maxValue
dynamicsCompressor.threshold.defaultValue
dynamicsCompressor.threshold.minValue
oscillator.detune.maxValue
oscillator.detune.minValue
oscillator.frequency.defaultValue
oscillator.frequency.maxValue
oscillator.frequency.minValue
*/
// Simplify to one sample per field
return resolve([
channelData.slice(-1)[0],
gainReduction,
freqData.slice(-1)[0],
timeData.slice(-1)[0],
]);
};
});
}
getRenderedAudioData().then(data => console.log(data)) |
The trap concept code is somewhat difficult to parse… I forgot what I was thinking. I added some comments to better explain it. // Returns a random integer between min and max
const getRandFromRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
// Copies data from an audio buffer to a float array and modifies it with randomness
// Returns an array of unique and non-zero values from the modified data
const getCopyFrom = (rand, buffer, copy) => {
const { length } = buffer // get the length of the buffer
const max = 20; // number of bins to modify
const start = getRandFromRange(275, length - (max + 1)); // audio usually begins after 275
const mid = start + max / 2; // the middle bin
const end = start + max; // the last bin
// Set data to randomness at the start, middle and end bins
buffer.getChannelData(0)[start] = rand
buffer.getChannelData(0)[mid] = rand
buffer.getChannelData(0)[end] = rand
buffer.copyFromChannel(copy, 0) // copy the data from the buffer to the array
// Modify the data with randomness if it is zero at the start, middle and end bins
const attack = [
buffer.getChannelData(0)[start] === 0 ? Math.random() : 0,
buffer.getChannelData(0)[mid] === 0 ? Math.random() : 0,
buffer.getChannelData(0)[end] === 0 ? Math.random() : 0,
]
// Read back unique data and filter non-zero values
return [...new Set([...buffer.getChannelData(0), ...copy, ...attack])].filter((x) => x !== 0)
}
// Copies data from a float array to an audio buffer and modifies it with randomness
// Returns an array of values from the modified data that are different from the frequency value
const getCopyTo = (rand, buffer, copy) => {
buffer.copyToChannel(copy.map(() => rand), 0) // copy the data from the array to the buffer with randomness
const frequency = buffer.getChannelData(0)[0] // get the frequency value from the first bin
const dataAttacked = [...buffer.getChannelData(0)] // get the modified data as an array
.map((x) => x !== frequency || !x ? Math.random() : x) // modify the data with randomness if it is different from the frequency or zero
return dataAttacked.filter((x) => x !== frequency) // filter out the frequency values
}
// Generates a noise factor based on two audio buffers and two float arrays
// Returns a number that represents the noise factor or zero if there is an error
const getNoiseFactor = () => {
const length = 2000 // length of the audio buffers and arrays
const rand = Math.random() // use to modify with noise
try {
const result = [...new Set([ // get unique values from both functions
...getCopyFrom(
rand,
new AudioBuffer({ length, sampleRate: 44100 }), // create an empty audio buffer with length and sample rate
new Float32Array(length), // create an empty float array with length
),
...getCopyTo(
rand,
new AudioBuffer({ length, sampleRate: 44100 }), // create another empty audio buffer with length and sample rate
new Float32Array(length), // create another empty float array with length
),
])]
return Number(
result.length !== 1 && // check if there is more than one value in the result array
result.reduce((acc, n) => acc += +n, 0) // sum up all the values in the result array
)
} catch (error) {
console.error(error)
return 0
}
}
const noiseFactor = getNoiseFactor()
console.log(noiseFactor) |
It can be useful for sanity checks, but it seems there's not much to classify if the values are genuine. |
Thanks 👍 this is now on my todo, to at least explore if they're 1. stable and 2. worth it and 3. where to add, under auto or click by "worth it" for example, I see duplication? equivalency?
some of the others look suspiciously common or related to context keys remembering that TZP is gecko only, this is a nice to have to finish the section off for RFP FF118+ (don't care about older releases, except ESR but I'm hoping it might get backported upstream for TB, we haven't asked yet). But at this stage, I'm inclined to trust the moz engineers have hooked up all the math and we're not going to get any extra entropy. Twould be nice to close an issue for once :~p |
so this is very similar to the existing onlineAudioContext test that gets the sum and channel hashes, so rather than run a separate test I can probably just piggy back on that one? anyway, using your standalone test (on file:// scheme) I do not get the same results as creepy context.oncomplete = (event) => {
// snip
let oData = {
"freq": freqData.slice(-1)[0],
"gain": gainReduction,
"time": timeData.slice(-1)[0],
"wtf_is_this": channelData.slice(-1)[0],
}
console.log(performance.now() - t0, oData)
return resolve()
};
// what I get
{
'freq': -190.46719360351562
'gain': -31.502185821533203
'time': -0.06294857710599899
'wtf_is_this': -0.21359434723854065
}
// what creepy reports (no RFP or anything)
{
'freq': 167700.7530517578
'gain': -31.502185821533203
'time': 148.475412953645
'trap': 0.7879753706889067
} so |
gain seems to be directly related to
nope, because we need the analyser |
This should align the results, but I might be missing something... function slice(arr, start, end) {
const collection = []
for (let i = start; i < end; i++) {
collection.push(arr[i])
}
return collection
}
function toSum(arr) {
return !arr ? 0 : [...arr].reduce((acc, curr) => (acc += Math.abs(curr)), 0)
}
let oData = {
"freq": toSum(freqData)
"gain": gainReduction,
"time": toSum(timeData),
"wtf_is_this": toSum(slice([...channelData], 4500, bufferLen /* 5000 */)), // fpjs sum
} |
@kkapsner : just FYI on the CB inconsistencies: not that I think it matters, as anyone wanting to NOT protect one of get vs copy is a bit silly and not likely in the slightest, but it does make testing it a bit weird, or otherwise I'm missing something
just some notes
have leveraged
isBraveMode
locally, but have found prototype lies to be unreliable. This can be said for any of prototype lies, while it may detect the extension is willing to use them, it doesn't mean it doesI would rather go down the road of double executions, mathematical proofs and equivalency
prototype lie results: trying to work out which lies MUST be present for a lie on the automatic audio test
The text was updated successfully, but these errors were encountered: