Skip to content
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

GPS scramble recorder #2896

Closed
wants to merge 11 commits into from
1 change: 1 addition & 0 deletions apps/gps-scramble/ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.01: Initial app
23 changes: 23 additions & 0 deletions apps/gps-scramble/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# GPS Scramble

This app wraps the existing [GPS recorder] and "scrambles" the GPS recording so, if lost, a read of the GPS files on the watch won't give away your home, work or other locations.

## Tech

The scrambling works by shifting your GPS records so they start in the middle of the pacific ocean, making it very difficult for someone to attempt to guess where the GPS track originated. To restore your track, the recorder app can unscramble these coordinates when provided with the starting location for your track.

The constraints on this design are:
- Avoid needing to enter a password whenever the watch loads
- Avoid storing true GPS coordinates (to prevent SWD reads)
- Avoid heavy crypto
- Avoid reliance on an external device, e.g. a phone

## Discussion

Initial discussion took place on the [espruino forum](https://forum.espruino.com/conversations/388489/).

## Todo

- [ ] Handle unscrambling a restart of GPS recording at a new location

[GPS recorder]: https://github.com/espruino/BangleApps/blob/0eea248390f5245c8d5bceb3eb2e976fab193a45/apps/recorder/widget.js#L22-L58
1 change: 1 addition & 0 deletions apps/gps-scramble/icon.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added apps/gps-scramble/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions apps/gps-scramble/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"id": "gps-scramble",
"name": "GPS Scrambler",
"shortName": "GPS Scramble",
"version": "0.01",
"description": "Scramble GPS to maintain privacy if your watch is lost",
"icon": "icon.png",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
"type": "recorder",
"storage": [
{ "name": "gps-scramble.recorder.js", "url": "recorder.js" },
{ "name": "gps-scramble.img", "url": "icon.js", "evaluate": true }
]
}
31 changes: 31 additions & 0 deletions apps/gps-scramble/recorder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
(function (recorders) {
var __assign = Object.assign;
var pacific = { lat: -4.21, lon: -146.25 };
var gpsRecorder = recorders.gps;
if (!gpsRecorder)
return;
delete recorders.gps;
recorders.gpsScramble = function () {
var gps = gpsRecorder();
var offset;
return __assign(__assign({}, gps), { name: "GPS (scramble)", fields: gps.fields.concat("ScrambleStart"), getValues: function () {
var values = gps.getValues();
values.push(String(!offset));
if (!values[0].length) {
}
else if (offset) {
values[0] = (Number(values[0]) + offset[0]).toFixed(6);
values[1] = (Number(values[1]) + offset[1]).toFixed(6);
}
else {
offset = [
pacific.lat - Number(values[0]),
pacific.lon - Number(values[1]),
];
values[0] = String(pacific.lat);
values[1] = String(pacific.lon);
}
return values;
} });
};
});
45 changes: 45 additions & 0 deletions apps/gps-scramble/recorder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
((recorders: Recorders) => {
// @ts-ignore helper
const __assign = Object.assign;

const pacific = { lat: -4.21, lon: -146.25 };

// @ts-ignore index signature
const gpsRecorder = recorders.gps;
if (!gpsRecorder) return;

// @ts-ignore index signature
delete recorders.gps;

// @ts-ignore index signature
recorders.gpsScramble = () => {
const gps = gpsRecorder();
let offset: undefined | [number, number];

return {
...gps,
name: "GPS (scramble)",
fields: gps.fields.concat("ScrambleStart"),
getValues: () => {
const values = gps.getValues() as string[];
values.push(String(!offset));

if (!values[0]!.length) {
// no change
} else if (offset) {
values[0] = (Number(values[0]) + offset[0]).toFixed(6);
values[1] = (Number(values[1]) + offset[1]).toFixed(6);
} else {
offset = [
pacific.lat - Number(values[0]),
pacific.lon - Number(values[1]),
];
values[0] = String(pacific.lat);
values[1] = String(pacific.lon);
}

return values;
},
};
};
})
55 changes: 55 additions & 0 deletions apps/recorder/interface.html
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,54 @@

function downloadTrack(filename, callback) {
Util.showModal("Downloading Track...");

const pacific = { lat: -4.21, lon: -146.25 };
const offset = {
lat: Number(document.getElementById("unscramble-lat").value),
lon: Number(document.getElementById("unscramble-lon").value),
};

Util.readStorageFile(filename,data=>{
Util.hideModal();
var lines = data.trim().split("\n");
var headers = lines.shift().split(",");
var track = lines.map(l=>trackLineToObject(headers, l));

if (localStorage.getItem("recorder-unscramble-gps")=="true") {
for (const entry of track) {
if (entry.Latitude.length) {
entry.Latitude = Number(entry.Latitude) - pacific.lat + offset.lat;
entry.Longitude = Number(entry.Longitude) - pacific.lon + offset.lon;
}
}
}

callback(track);
});
}

function scrambleInit() {
document.getElementById("settings-unscramble-gps").addEventListener("change", event => {
var unscramble = event.target.checked;
localStorage.setItem("recorder-unscramble-gps", unscramble);

document.getElementById("unscramble-cfg").classList.toggle("d-hide", !unscramble);
});

if (localStorage.getItem("recorder-unscramble-gps")=="true")
document.getElementById("settings-unscramble-gps").click(); // trigger listener

const bind = (el, storeKey) => {
el.value = localStorage.getItem(storeKey);
el.addEventListener("change", event => {
localStorage.setItem(storeKey, event.target.value);
});
};

bind(document.getElementById("unscramble-lat"), "recorder-unscramble-gps-lat");
bind(document.getElementById("unscramble-lon"), "recorder-unscramble-gps-lon");
}

function getTrackList() {
Util.showModal("Loading Track List...");
domTracks.innerHTML = "";
Expand Down Expand Up @@ -264,13 +303,29 @@ <h2>Settings</h2>
<input type="checkbox" id="settings-allow-no-gps" ${(localStorage.getItem("recorder-allow-no-gps")=="true")?"checked":""}>
<i class="form-icon"></i> Include GPX/KML entries even when there's no GPS info
</label>
<div>
<label class="form-switch">
<input type="checkbox" id="settings-unscramble-gps">
<i class="form-icon"></i> Unscramble/re-originate GPS
</label>

<div id="unscramble-cfg" class="d-hide">
<label class="form-label" for="unscramble-lat">Starting Latitude</label>
<input type="number" id="unscramble-lat" class="form-input" step="any">

<label class="form-label" for="unscramble-lon">Starting Longitude</label>
<input type="number" id="unscramble-lon" class="form-input" step="any">
</div>
</div>
</div>
</div>`;
domTracks.innerHTML = html;
document.getElementById("settings-allow-no-gps").addEventListener("change",event=>{
var allowNoGPS = event.target.checked;
localStorage.setItem("recorder-allow-no-gps", allowNoGPS);
});
scrambleInit();

Util.hideModal();
var buttons = domTracks.querySelectorAll("button");
for (var i=0;i<buttons.length;i++) {
Expand Down
2 changes: 1 addition & 1 deletion bin/sanitycheck.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const APP_KEYS = [
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'supports', 'noOverwrite'];
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
const SUPPORTS_DEVICES = ["BANGLEJS","BANGLEJS2"]; // device IDs allowed for 'supports'
const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","scheduler","notify","locale","settings","waypoints","textinput","module","clkinfo"]; // values allowed for "type" field
const METADATA_TYPES = ["app","clock","widget","bootloader","RAM","launch","scheduler","notify","locale","settings","waypoints","textinput","module","clkinfo","recorder"]; // values allowed for "type" field
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
const GRANDFATHERED_ICONS = ["s7clk", "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"];
Expand Down
10 changes: 10 additions & 0 deletions typescript/types/main.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10255,6 +10255,11 @@ interface StringConstructor {
* @url http://www.espruino.com/Reference#l_String_String
*/
new(...str: any[]): any;

/**
* Converts the given argument to a string
*/
(s: any): string;
}

interface String {
Expand Down Expand Up @@ -10589,6 +10594,11 @@ interface NumberConstructor {
* @url http://www.espruino.com/Reference#l_Number_Number
*/
new(...value: any[]): any;

/**
* Converts the given argument to a number
*/
(value: any): number;
}

interface Number {
Expand Down