diff options
Diffstat (limited to 'data/extensions/jsr@javascriptrestrictor/wrappingS-SENSOR-ORIENT.js')
-rw-r--r-- | data/extensions/jsr@javascriptrestrictor/wrappingS-SENSOR-ORIENT.js | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/data/extensions/jsr@javascriptrestrictor/wrappingS-SENSOR-ORIENT.js b/data/extensions/jsr@javascriptrestrictor/wrappingS-SENSOR-ORIENT.js new file mode 100644 index 0000000..9a87004 --- /dev/null +++ b/data/extensions/jsr@javascriptrestrictor/wrappingS-SENSOR-ORIENT.js @@ -0,0 +1,276 @@ +/** \file + * \brief Wrappers for the AbsoluteOrientationSensor and RelativeOrientationSensor + * + * \see https://www.w3.org/TR/orientation-sensor + * \see https://www.w3.org/TR/orientation-sensor/#absoluteorientationsensor-model + * \see https://www.w3.org/TR/orientation-sensor/#relativeorientationsensor-model + * + * \author Copyright (C) 2021 Radek Hranicky + * + * \license SPDX-License-Identifier: GPL-3.0-or-later + */ + // + // This program is free software: you can redistribute it and/or modify + // it under the terms of the GNU General Public License as published by + // the Free Software Foundation, either version 3 of the License, or + // (at your option) any later version. + // + // This program is distributed in the hope that it will be useful, + // but WITHOUT ANY WARRANTY; without even the implied warranty of + // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + // GNU General Public License for more details. + // + // You should have received a copy of the GNU General Public License + // along with this program. If not, see <https://www.gnu.org/licenses/>. + // + + /** \file + * \ingroup wrappers + * + * MOTIVATION + * Device orientation sensors can be easily used for fingerprinting. As it highly + * unlikely that two devices visiting the same site will be oriented exactly + * the same, the orientation itself can serve as a fingerprint. + * + * + * WRAPPING + * AbsoluteOrientationSensor returns a quaterion decribing the physical + * orientation of the device in relation to the Earth's reference coordinate + * system. The faked orientation of the device is saved inside the "orient" + * global variable that is accessible to all wrappers. The value is chosen + * pseudorandomly from the domain hash. The wrappper supports possible change + * of orientation. With each reading, it loads the "orient"'s contents, + * converts the rotation matrix to a quaternion that is returned by the wrapped + * getter. + * + * RelativeOrientationSensor also describes the orientation, but without + * regard to the Earth's reference coordinate system. We suppose the coordinate + * system is chosen at the beginning of the sensor instance creation. + * As we observed, no matter how the device is oriented, there is always a slight + * difference from the AbsoluteOrientationSensor's in at least one axis. + * When the device moves, both sensors' readings change. But their difference + * should be always constant. And thus, we pseudorandomly generate a deviation + * from the Earth's reference coordinate system. And for each reading, we + * take the values from the fake AbsoluteOrientationSensor and modify them + * by the constant deviation. + * + * POSSIBLE IMPROVEMENTS + * Study the supported coordinate systems of the RelativeOrientationSensor + * and modify the wrapper behavior if needed. + */ + + /* + * Create private namespace + */ +(function() { + /* + * \brief Initialization of data for storing sensor readings + */ + var init_data = ` + var currentReading = currentReading || {quaternion: null, fake_quaternion: null, fake_quaternion_rel: null, timestamp: null}; + var previousReading = previousReading || {quaternion: null, fake_quaternion: null, fake_quaternion_rel: null, timestamp: null}; + var debugMode = false; + + const TWOPI = 2 * Math.PI; + `; + + /* + * \brief Property getters of the original sensor object + */ + var orig_getters = ` + var origGetQuaternion = Object.getOwnPropertyDescriptor(OrientationSensor.prototype, "quaternion").get; + var origGetTimestamp = Object.getOwnPropertyDescriptor(Sensor.prototype, "timestamp").get; + `; + + /* + * \brief Convert a given 3D rotation matrix to quaternion + * + * \param Rotation matrix + */ + function matrixToQuaternion(rot) { + var q = {x: null, y: null, z: null, w: null}; + var m; + if (rot[2][2] < 0) { + if (rot[0][0] > rot[1][1]) { + m = 1 + rot[0][0] -rot[1][1] -rot[2][2]; + q.x = m; + q.y = rot[0][1]+rot[1][0]; + q.z = rot[2][0]+rot[0][2]; + q.w = rot[1][2]-rot[2][1]; + } else { + m = 1 -rot[0][0] + rot[1][1] -rot[2][2]; + q.x = rot[0][1]+rot[1][0]; + q.y = m; + q.z = rot[1][2]+rot[2][1]; + q.w = rot[2][0]-rot[0][2]; + } + } else { + if (rot[0][0] < -rot[1][1]) { + m = 1 -rot[0][0] -rot[1][1] + rot[2][2]; + q.x = rot[2][0]+rot[0][2]; + q.y = rot[1][2]+rot[2][1]; + q.z = m; + q.w = rot[0][1]-rot[1][0]; + } else { + m = 1 + rot[0][0] + rot[1][1] + rot[2][2]; + q.x = rot[1][2]-rot[2][1]; + q.y = rot[2][0]-rot[0][2]; + q.z = rot[0][1]-rot[1][0]; + q.w = m; + } + } + var multiplier = 0.5 / Math.sqrt(m); + + q.x *= multiplier; + q.y *= multiplier; + q.z *= multiplier; + q.w *= multiplier; + + return q; + } + + /* + * \brief The fake quaternion generator class + * Note: Requires "orient" global var to be set. + */ + class QuaternionGenerator { + constructor() { + this.DEVIATION_MIN = 0; + this.DEVIATION_MAX = (Math.PI / 2) / 90 * 10; // 10° + + this.quaternion = null; + this.quaternion_rel = null; + + this.yawDeviation = this.generateDeviation(); + this.pitchDeviation = this.generateDeviation(); + this.rollDeviation = this.generateDeviation(); + } + + /* + * \brief Generates the rotation deviation + */ + generateDeviation() { + var devi = sen_prng() * (this.DEVIATION_MAX - this.DEVIATION_MIN) + this.DEVIATION_MIN; + devi *= Math.round(sen_prng()) ? 1 : -1; + return devi; + } + + /* + * \brief Updates the fake quaternions + * + * \param Current timestamp from the sensor object + */ + update(t) { + // Calculate quaternion for absolute orientation + var rotMat = orient.rotMat; // Get the device rotation matrix + + var q = matrixToQuaternion(rotMat); + this.quaternion = [ + fixedNumber(q.x, 3), + fixedNumber(q.y, 3), + fixedNumber(q.z, 3), + fixedNumber(q.w, 3) + ]; + + // Calculate quaternion for relative orientation + var relYaw = (orient.yaw + this.yawDeviation) % TWOPI; + var relPitch = (orient.pitch + this.pitchDeviation) % TWOPI; + var relRoll = (orient.roll + this.rollDeviation) % TWOPI; + + var relMat = calculateRotationMatrix(relYaw, relPitch, relRoll); + + var qr = matrixToQuaternion(relMat); + this.quaternion_rel = [ + fixedNumber(qr.x, 3), + fixedNumber(qr.y, 3), + fixedNumber(qr.z, 3), + fixedNumber(qr.w, 3) + ]; + } + } + + /* + * \brief Updates the stored (both real and fake) sensor readings + * according to the data from the sensor object. + * + * \param The sensor object + */ + function updateReadings(sensorObject) { + // We need the original reading's timestamp to see if it differs + // from the previous sample. If so, we need to update the faked quaternion + let previousTimestamp = previousReading.timestamp; + let currentTimestamp = origGetTimestamp.call(sensorObject); + if (debugMode) { + // [!] Debug mode: overriding timestamp + // This allows test suites to set a custom timestamp externally + // by modifying the property of the Magnetometer object directly. + currentTimestamp = sensorObject.timestamp; + } + if (currentTimestamp === previousTimestamp) { + // No new reading, nothing to update + return; + } + + // Rotate the readings: previous <- current + previousReading = JSON.parse(JSON.stringify(currentReading)); + // Update current reading + // NOTE: Original values are also stored for possible future use + // in improvements of the magnetic field generator + currentReading.orig_quaterion = origGetQuaternion.call(sensorObject); + currentReading.timestamp = currentTimestamp; + quaternionGenerator.update(currentTimestamp); + currentReading.fake_quaternion = quaternionGenerator.quaternion; + currentReading.fake_quaternion_rel = quaternionGenerator.quaternion_rel; + + if (debugMode) { + } + } + + /* + * \brief Initializes the related generators + */ + var generators = ` + // Initialize the quaternion generator, if not initialized before + var quaternionGenerator = quaternionGenerator || new QuaternionGenerator(); + `; + + var helping_functions = sensorapi_prng_functions + device_orientation_functions + + matrixToQuaternion + QuaternionGenerator + updateReadings; + var hc = init_data + orig_getters + helping_functions + generators; + + var wrappers = [ + { + parent_object: "OrientationSensor.prototype", + parent_object_property: "quaternion", + wrapped_objects: [], + helping_code: hc, + post_wrapping_code: [ + { + code_type: "object_properties", + parent_object: "OrientationSensor.prototype", + parent_object_property: "quaternion", + wrapped_objects: [], + /** \brief replaces OrientationSensor.quaternion getter to return a faked value + */ + wrapped_properties: [ + { + property_name: "get", + property_value: ` + function() { + updateReadings(this); + if (this.__proto__.constructor.name === 'AbsoluteOrientationSensor') { + // AbsoluteOrientationSensor + return currentReading.fake_quaternion; + } else { + // RelativeOrientationSensor + return currentReading.fake_quaternion_rel; + } + }`, + }, + ], + } + ], + } + ] + add_wrappers(wrappers); +})() |