↓
76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rot8000", 3 | "version": "0.0.1", 4 | "description": "http://rot8000.com/info", 5 | "main": "rot8000.js", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/rottytooth/rot8000.git" 12 | }, 13 | "keywords": [ 14 | "rot8000", 15 | "cipher" 16 | ], 17 | "author": "https://github.com/rottytooth", 18 | "license": "GPL-3.0", 19 | "bugs": { 20 | "url": "https://github.com/rottytooth/rot8000/issues" 21 | }, 22 | "homepage": "https://github.com/rottytooth/rot8000#readme", 23 | "devDependencies": { 24 | "jest": "^23.6.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # rot8000 2 | 3 | **rot8000** solves the burning question of the day: how to de-spoilerize text containing non-Western characters, in a universal way. Rot13, which rotates any text 13 characters through the alphabet, was great for Usenet days, so long as you only use the English alphabet. This is rot13 for the Unicode generation. 4 | 5 | Rot8000 only works in the Basic Multilingual Plane, which covers languages in use today, including the commonly used CJK characters. If you want to write spoilers in Linear B, you might need another system. It rotates by 0x8000 or half the BMP (actually a bit less than 8000, as it leaves alone whitespace and surrogates). For the most part, it leaves emoji alone (as most live outside of BMP). 6 | 7 | The C# version was created when rot13 still used a postback; when it went to JS, rot8000 did as well. Both the C# and JavaScript versions are here; if you just want to start rot8000ing stuff, the rot8000.min.js file is all you need. 8 | 9 | Rot8000 was created for the pl41nt3xt pavillion of text-based works, curated by A Bill Miller, for The Wrong (online) digital art biennale. 10 | 11 | Discussions: 12 | * [Cory Doctorow: "ROT 8000 and what 'security' means"](https://pluralistic.net/2021/10/15/fargo-north-decoder/#on-trusting-trust) (10/2021) 13 | * [Hacker News](https://news.ycombinator.com/item?id=28615855) (9/2021) 14 | * [BoingBoing](https://boingboing.net/2021/09/22/the-rot8000-cipher-for-when-rot13-just-isnt-enough-letters.html) (9/2021) 15 | * [Hacker News](https://news.ycombinator.com/item?id=18495518) (11/2018) 16 | 17 | Tests in rot8000.test.js ensure reversability of BMP codepoints. **Jest** is needed to run tests. The JS version relies on the C# component to define whitespace, surrogates, etc, which appear in the valid-code-point-transitions.json. 18 | 19 | See it in action: [rot8000.com](https://rot8000.com ) 20 | -------------------------------------------------------------------------------- /rot8000.js: -------------------------------------------------------------------------------- 1 | 2 | function rot8000() 3 | { 4 | // these come from the valid-code-point-transitions.json file generated from the c# proj 5 | // this is done bc: 1) don't trust JS's understanging of surrogate pairs and 2) consistency with original rot8000 6 | const valid_code_points = 7 | JSON.parse('{"33":true,"127":false,"161":true,"5760":false,"5761":true,"8192":false,"8203":true,"8232":false,"8234":true,"8239":false,"8240":true,"8287":false,"8288":true,"12288":false,"12289":true,"55296":false,"57344":true}'); 8 | 9 | const BMP_SIZE = 0x10000; 10 | 11 | this.rotlist = {}; // the mapping of char to rotated char 12 | 13 | 14 | hiddenblocks = []; 15 | var startblock = 0; 16 | 17 | for (var key in valid_code_points) { 18 | if (valid_code_points.hasOwnProperty(key)) { 19 | if (valid_code_points[key] == true) 20 | hiddenblocks.push({ start: startblock, end: parseInt(key) - 1 }); 21 | else 22 | startblock = parseInt(key); 23 | } 24 | } 25 | 26 | var validintlist = []; // list of all valid chars 27 | 28 | var currvalid = false; 29 | 30 | for (var i = 0; i < BMP_SIZE; i++) { 31 | if (valid_code_points[i] !== undefined) { 32 | currvalid = valid_code_points[i] 33 | } 34 | if (currvalid) validintlist.push(i); 35 | } 36 | var rotatenum = Object.keys(validintlist).length / 2; 37 | 38 | // go through every valid char and find its match 39 | for (var i = 0; i < validintlist.length; i++) { 40 | this.rotlist[String.fromCharCode(validintlist[i])] = 41 | String.fromCharCode(validintlist[(i + rotatenum) % (rotatenum * 2)]); 42 | } 43 | 44 | this.rotate = function(convstring) 45 | { 46 | var outstring = ""; 47 | 48 | for (var count = 0; count < convstring.length; count++) 49 | { 50 | // if it is not in the mappings list, just add it directly (no rotation) 51 | if (this.rotlist[convstring[count]] === undefined) 52 | { 53 | outstring += convstring[count]; 54 | continue; 55 | } 56 | 57 | // otherwise, rotate it and add it to the string 58 | outstring += this.rotlist[convstring[count]]; 59 | } 60 | 61 | return outstring; 62 | } 63 | } 64 | module.exports = {rot8000}; 65 | -------------------------------------------------------------------------------- /rot8000.min.js: -------------------------------------------------------------------------------- 1 | function rot8000(){var r=JSON.parse('{"33":true,"127":false,"161":true,"5760":false,"5761":true,"8192":false,"8203":true,"8232":false,"8234":true,"8239":false,"8240":true,"8287":false,"8288":true,"12288":false,"12289":true,"55296":false,"57344":true}');this.rotlist={},hiddenblocks=[];var t=0;for(var e in r)r.hasOwnProperty(e)&&(1==r[e]?hiddenblocks.push({start:t,end:parseInt(e)-1}):t=parseInt(e));for(var s=[],a=!1,o=0;o<65536;o++)void 0!==r[o]&&(a=r[o]),a&&s.push(o);var i=Object.keys(s).length/2;for(o=0;o