JavaScript Restrictor
Browser extension that improves privacy and security
wrappingS-SENSOR-MAGNET.js File Reference

Wrappers for the Magnetometer Sensor. More...

Functions

function SineCfg ()
 
function configureSines (cntMin, cntMax, center, flucMin, fluctMax, periodMin, periodMax)
 
function initFieldGenerator ()
 
function generateBaseField ()
 
function generateAxisBase ()
 
function updateReadings (sensorObject)
 

Variables

var previousReading
 
var emulateStationaryDevice = (typeof args === 'undefined') ? true : args[0]
 
var debugMode = false
 
const TWOPI = 2 * Math.PI
 
var orig_getters
 
var origGetY = Object.getOwnPropertyDescriptor(Magnetometer.prototype, "y").get
 
var origGetZ = Object.getOwnPropertyDescriptor(Magnetometer.prototype, "z").get
 
var origGetTimestamp = Object.getOwnPropertyDescriptor(Sensor.prototype, "timestamp").get
 
var generators
 
var helping_functions
 
var hc = init_data + orig_getters + helping_functions + generators
 
var wrappers
 

Detailed Description

Wrappers for the Magnetometer Sensor.

See also
https://www.w3.org/TR/magnetometer/
Author
Copyright (C) 2021 Radek Hranicky
License:
SPDX-License-Identifier: GPL-3.0-or-later

MOTIVATION

Magnetometer is a platform sensor available under the Generic Sensor API. Magnetometer measures strength and direction of the magnetic field at device's location. The interface offers sensor readings using three properties: x, y, and z. Each returns a number that describes the magnetic field aroud the particular axis. The numbers have a double precision and can be positive or negative, depending on the orientation of the field. The total strength of the magnetic field (M) can be calculated as M = sqrt(x^2 + z^2 + y^2). The unit is in microtesla (µT).

The Earth's magnetic field ranges between approximately 25 and 65 µT. Concrete values depend on location, altitude, weather, interference made by other electric. devices, etc. While we consider it is unlikely that someone determines the precise location of the device from the Mangetometer values, its data can be used for fingerprinting. For instance, it can be determined wheter the device is moving or not. In case of a stationary device, we can make a fingerprint from the device's orientation. Another fingerprintable value is the average total strength of the field, which should remain stable if the device is at the same position and in the same environment.

WRAPPING

To protect the device, we are wrapping the x, y, z getters of the Magnetometer.prototype object. Instead of using the original data, we use artificially generated values that look like actual sensor readings.

At every moment, our wrapper stores information about the previous reading. Each rewrapped getter first checks the timestamp value of the sensor object. If there is no difference from the previous reading's timestamp, the wrapper returns the last measured value. Otherwise, it provides a new fake reading.

We designed our fake field generator to fulfill the following properties:

  • The randomness of the generator should be high enough to prevent attackers from deducing the sensor values.
  • Multiple scripts from the same website that access readings with the same timestamp must get the same results. And thus:
  • The readings are deterministic - e.g., for a given website and time, we must be able to say what values to return.

For every "random" draw, we use the Mulberry32 sen_prng that is seeded with a value generated from the domainHash which ensures deterministic behavior for the given website. First, we choose the desired total strength M of the magnetic field at our simulated location. This is a pseudo-random number from 25 to 60 uT, like on the Earth. In the current implementation, we simulate a stationary device with a pseudo-randomly drawn orientation. Therefore, we choose the orientation of the device by generating a number from -1 to 1 for each axis. Those values we call baseX, baseY, and baseZ. By modifying the above-shown formula, we calculate the multiplier that needs to be applied to the base values to get the desired field. The calculation is done as follows:

  • mult = (M * sqrt(baseX^2 + baseY^2 + baseZ^2) / (baseX^2 + baseY^2 + baseZ^2)) Now, we know that for axis x, the value should fluctuate around baseX * mult, etc.

How much the field changes over time is specified by the fluctuation factor (0;1] that can also be configured. For instance, 0.2 means that the magnetic field on the axis may change from the base value by 20% in both positive and negative way.

The fluctuation is simulated by using a series of sine functions for each axis. Each sine has a unique amplitude, phase shift, and period. The number of sines per axis is chosen pseudorandomly based on the wrapper settings. For initial experiments, we used around 20 to 30 sines for each axis. The optimal configuration is in question. More sines give less predictable results, but also increase the computing complexity that could have a negative impact on the browser's performance.

For the given timestamp t, we make the sum of all sine values at the point x=t. The result is then shifted over the y-axis by adding base[X|Y|Z] * multiplier to the sum. The initial configuration of the fake field generator was chosen intuitively to resemble the results of the real measurements. Currently, the generator uses at least one sine with the period around 100 us (with 10% tolerance), which seems to be the minimum sampling rate obtainable using the API on mobile devices. Then, at least one sine around 1 s, around 10 s, 1 minute, and 1 hour. When more than 5 sines are used, the cycle repeats using modulo 5 and creates a new sine with the period around 100 us, but this time the tolerance is 20%. The same follows for seconds, tens of seconds, minutes, hours. The tolerance grows every 5 sines. For 11+ sines, the tolerance is 30% up to the maximum (currently 50%). The amplitude of each sine is chosen pseudo- randomly based on the fluctuation factor described above. The phase shift of each sine is also pseudo-random number from [0;2PI).

Based on the results, this heuristic returns belivable values that look like actual sensor readings. Nevertheless, the generator uses a series of constants, whose optimal values should be a subject of future research and improvements. Perphaps, a correlation analysis with real mesurements could help in the future.

POSSIBLE IMPROVEMENTS The initial toss-up of the device orientation can be utilized in other sensors, e.g., the Absolute Orientation Sensor frequently uses an underlying magnetometer device. The orientation should be unified across the individual wrappers.

Function Documentation

◆ configureSines()

function configureSines (   cntMin,
  cntMax,
  center,
  flucMin,
  fluctMax,
  periodMin,
  periodMax 
)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ generateAxisBase()

function generateAxisBase ( )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ generateBaseField()

function generateBaseField ( )
Here is the call graph for this function:
Here is the caller graph for this function:

◆ initFieldGenerator()

function initFieldGenerator ( )
Here is the call graph for this function:

◆ SineCfg()

function SineCfg ( )
Here is the caller graph for this function:

◆ updateReadings()

function updateReadings (   sensorObject)

Variable Documentation

◆ debugMode

var debugMode = false

◆ emulateStationaryDevice

var emulateStationaryDevice = (typeof args === 'undefined') ? true : args[0]

◆ generators

var generators
Initial value:
= `
var fieldGenerator = fieldGenerator || initFieldGenerator()
function initFieldGenerator()
Definition: wrappingS-SENSOR-MAGNET.js:230

◆ hc

var hc = init_data + orig_getters + helping_functions + generators

◆ helping_functions

var helping_functions
Initial value:
var sensorapi_prng_functions
Definition: wrappingL-SENSOR.js:30
function generateAxisBase()
Definition: wrappingS-SENSOR-MAGNET.js:341
function generateBaseField()
Definition: wrappingS-SENSOR-MAGNET.js:332
function updateReadings(sensorObject)
Definition: wrappingS-SENSOR-MAGNET.js:354
function configureSines(cntMin, cntMax, center, flucMin, fluctMax, periodMin, periodMax)
Definition: wrappingS-SENSOR-MAGNET.js:167
function SineCfg()
Definition: wrappingS-SENSOR-MAGNET.js:149

◆ orig_getters

var orig_getters
Initial value:
= `
var origGetX = Object.getOwnPropertyDescriptor(Magnetometer.prototype, "x").get

◆ origGetTimestamp

var origGetTimestamp = Object.getOwnPropertyDescriptor(Sensor.prototype, "timestamp").get

◆ origGetY

var origGetY = Object.getOwnPropertyDescriptor(Magnetometer.prototype, "y").get

◆ origGetZ

var origGetZ = Object.getOwnPropertyDescriptor(Magnetometer.prototype, "z").get

◆ previousReading

var previousReading
Initial value:
= previousReading || {orig_x: null, orig_y: null, orig_z: null, timestamp: null,
fake_x: null, fake_y: null, fake_z: null}
var previousReading
Definition: wrappingS-SENSOR-MAGNET.js:128

◆ TWOPI

const TWOPI = 2 * Math.PI

◆ wrappers

var wrappers