|
|
(23 intermediate revisions by the same user not shown) |
Line 1: |
Line 1: |
− | {{Groningen}}
| + | #REDIRECT [[Team:Groningen/Encoding]] |
− | <html>
| + | |
− | <head>
| + | |
− | <meta http-equiv="content-type" content="text/html; charset=utf-8" />
| + | |
− | <title>SpyGEM coding</title>
| + | |
− | <!-- <link rel="stylesheet" type="text/css" href="reset.css" /> -->
| + | |
− | <style>
| + | |
− | body{
| + | |
− | font-family: sans-serif;
| + | |
− | font-size: 13pt;
| + | |
− | line-height: 1.5em;
| + | |
− | padding: 0.5em;
| + | |
− | margin: 0;
| + | |
− | }
| + | |
− | body,textarea,a{
| + | |
− | background-color: #222;
| + | |
− | color: #EEE;
| + | |
− | }
| + | |
− | textarea,code{
| + | |
− | font-family: monospace;
| + | |
− | font-size: 12pt;
| + | |
− | line-height: 1.5em;
| + | |
− | }
| + | |
− | textarea{
| + | |
− | border: 1px solid #444;
| + | |
− | width: 99%;
| + | |
− | }
| + | |
− | h1{
| + | |
− | font-size: 1.2em;
| + | |
− | }
| + | |
− |
| + | |
− | #container{
| + | |
− | overflow: auto;
| + | |
− | width: 100%;
| + | |
− | margin: 0;
| + | |
− | padding: 0;
| + | |
− | }
| + | |
− | #sec-left{
| + | |
− | padding: 5px;
| + | |
− | width: 58.5%;
| + | |
− | float: left;
| + | |
− | }
| + | |
− | #sec-right{
| + | |
− | padding: 5px;
| + | |
− | width: 38.5%;
| + | |
− | float: right;
| + | |
− | }
| + | |
− |
| + | |
− | #err{
| + | |
− | color: #FF3333;
| + | |
− | }
| + | |
− |
| + | |
− | .input{
| + | |
− | border: 1px solid #EEE;
| + | |
− | }
| + | |
− | .strike{
| + | |
− | text-decoration: line-through;
| + | |
− | }
| + | |
− | .vr{
| + | |
− | border-left: 1px solid #EEE;
| + | |
− | width: 0;
| + | |
− | padding: 0;
| + | |
− | margin: 0;
| + | |
− | }
| + | |
− | .clear{
| + | |
− | clear: both;
| + | |
− | }
| + | |
− | </style>
| + | |
− | </head>
| + | |
− | <body>
| + | |
− | <div id="container">
| + | |
− | <h1>SpyGEM coding & crypting</h1>
| + | |
− | <hr class="clearfx" />
| + | |
− | <div id="sec-left">
| + | |
− | <p><div>
| + | |
− | <span>
| + | |
− | <input type="checkbox" id="crypt" />
| + | |
− | <label for="crypt">Encrypt</label>
| + | |
− | </span>
| + | |
− | <span class="vr">​</span>
| + | |
− | <span>
| + | |
− | <input class="dir" type="radio" id="doEnc" name="dir" value="enc" checked />
| + | |
− | <label for="doEnc">Encode</label>
| + | |
− | </span>
| + | |
− | <span>
| + | |
− | <input class="dir" type="radio" id="doDec" name="dir" value="dec" />
| + | |
− | <label for="doDec">Decode</label>
| + | |
− | </span>
| + | |
− | </div></p>
| + | |
− | <p><div>Message</div><div><textarea class="input" rows="5" id="msg">Hello world.</textarea></div></p>
| + | |
− | <p><button id="stage">Do something</button></p>
| + | |
− | <p><div>Key</div><div><textarea class="input" rows="5" id="key"></textarea></div></p>
| + | |
− | <p><div>Formatted</div><textarea rows="5" id="fmt"></textarea></p>
| + | |
− | <p><div>Encoded</div><div><textarea class="input" rows="5" id="enc"></textarea></div></p>
| + | |
− | <p><div>Total length: <span id="len"></span> bases</div></p>
| + | |
− | <p><div id="err"></div></p>
| + | |
− | </div>
| + | |
− | <div id="sec-right">
| + | |
− | <p>This page demonstrates the encoding and decoding of text messages
| + | |
− | into DNA, optionally with AES-256 encryption.</p>
| + | |
− | <p>Enter a message into the Message field to have it encoded, or
| + | |
− | enter an encoded message into the Encoded field to have it decoded.
| + | |
− | The Key field is used for encryption & decryption and the
| + | |
− | Formatted field shows more details about the message block.</p>
| + | |
− | <p>To counter restriction enzymes a sequence of 'A'-bases is
| + | |
− | inserted whereever the sequence for a restriction-enzyme occurs.
| + | |
− | When decoding the message these sequences are removed. The length
| + | |
− | of the substitution sequence is the shortest sequence of
| + | |
− | 'A' that does not occur in the encoded message.</p>
| + | |
− | <p>The encoding requires 4 bases per character.</p>
| + | |
− | <p>The fully encoded message block is formatted as follows:</p>
| + | |
− | <p><ol>
| + | |
− | <li>The letters <code>'Spy'</code>: 3 × 4 bases.</li>
| + | |
− | <li>Padding: 4 'A' bases.</li>
| + | |
− | <li>The length of the restriction-substitutes: 8-bit number (4 bases).</li>
| + | |
− | <li>The length of the encoded message: 16-bit number (8 bases).</li>
| + | |
− | <li>The checksum of the encoded message: 16-bit number (8 bases).</li>
| + | |
− | <li>The message: 4 bases per character.</li>
| + | |
− | <li>The letters <code>'GEM'</code>: 3 × 4 bases.</li>
| + | |
− | </ol></p>
| + | |
− | <p>The total overhead is 48 bases and the maximum message
| + | |
− | length is 65535 characters.</p>
| + | |
− | <p>Encryption has an additional 8 characters (48 bases) overhead,
| + | |
− | regardless of key or message length.</p>
| + | |
− | </div>
| + | |
− | </div>
| + | |
− | <div class="clear"></div>
| + | |
− | <div>
| + | |
− | <p>Credits:</p>
| + | |
− | <ul>
| + | |
− | <li><a href="http://www.movable-type.co.uk/scripts/aes.html">Chris Veness: Javascript implementation of AES</a></li>
| + | |
− | <li><a href="https://gist.github.com/chitchcock/5112270">'chitchcock': Javascript implementation of CRC16-CCITT</a></li>
| + | |
− | </ul>
| + | |
− | </div>
| + | |
− |
| + | |
− | <script src="/Template:Groningen/crc_js?action=raw&ctype=text/javascript"></script>
| + | |
− | <script src="/Template:Groningen/dna_js?action=raw&ctype=text/javascript"></script>
| + | |
− | <script src="/Template:Groningen/eightbit_js?action=raw&ctype=text/javascript"></script>
| + | |
− | <script src="/Template:Groningen/restrict_js?action=raw&ctype=text/javascript"></script>
| + | |
− | <script src="/Template:Groningen/message_js?action=raw&ctype=text/javascript"></script>
| + | |
− | <script src="/Template:Groningen/aes_js?action=raw&ctype=text/javascript"></script>
| + | |
− | <script src="/Template:Groningen/aes-ctr_js?action=raw&ctype=text/javascript"></script>
| + | |
− | <script>
| + | |
− | if(typeof Array.from !== 'function'){
| + | |
− | Array.from = function(src){
| + | |
− | return Array.prototype.slice.call(src);
| + | |
− | };
| + | |
− | }
| + | |
− |
| + | |
− | if(typeof String.prototype.padStart !== 'function'){
| + | |
− | String.prototype.padStart = function(len, chr){
| + | |
− | var ret = this;
| + | |
− | while(ret.length < len){
| + | |
− | ret = chr + ret;
| + | |
− | }
| + | |
− | return ret;
| + | |
− | };
| + | |
− | }
| + | |
− | | + | |
− | var elMsg = document.getElementById('msg'),
| + | |
− | elKey = document.getElementById('key'),
| + | |
− | elFmt = document.getElementById('fmt'),
| + | |
− | elEnc = document.getElementById('enc'),
| + | |
− | elErr = document.getElementById('err'),
| + | |
− | elCrypt = document.getElementById('crypt'),
| + | |
− | elCode8 = document.getElementById('code8'),
| + | |
− | elDoEnc = document.getElementById('doEnc'),
| + | |
− | elDoDec = document.getElementById('doDec'),
| + | |
− | elDirs = [elDoEnc, elDoDec],
| + | |
− | elStage = document.getElementById('stage'),
| + | |
− | elLen = document.getElementById('len');
| + | |
− |
| + | |
− | var subCoding = EightBit, doCrypt = false, lastEdit = elMsg,
| + | |
− | cryptBits = 256, stages = [
| + | |
− | {name: "Plain text", transform: function(prev){
| + | |
− | return elMsg.value;
| + | |
− | }
| + | |
− | },
| + | |
− | {name: "Encrypted", transform: function(prev){
| + | |
− | var key = elKey.value;
| + | |
− | return Aes.Ctr.encrypt(prev, key, cryptBits);
| + | |
− | }, display: function(data){
| + | |
− | return data.utf8Decode();
| + | |
− | }
| + | |
− | },
| + | |
− | {name: "ASCII", display: function(data){
| + | |
− | return data.split('').map(function(c){
| + | |
− | return c.charCodeAt(0).toString(10).padStart(3, '0');
| + | |
− | }).join(' ');
| + | |
− | }
| + | |
− | },
| + | |
− | {name: "Binary", display: function(data){
| + | |
− | return data.split('').map(function(c){
| + | |
− | return c.charCodeAt(0).toString(2).padStart(8, '0');
| + | |
− | }).join(' ');
| + | |
− | }
| + | |
− | },
| + | |
− | {name: "DNA", display: function(data){
| + | |
− | return (EightBit.encodeStr(data).match(/.{1,4}/g) || []).join(' ');
| + | |
− | }
| + | |
− | }
| + | |
− | ], currentStage = null;
| + | |
− |
| + | |
− | function advanceStage(prev){
| + | |
− | var idx = prev.idx + 1,
| + | |
− | cur = stages[idx],
| + | |
− | data = prev.data,
| + | |
− | disp;
| + | |
− |
| + | |
− | console.log(cur);
| + | |
− |
| + | |
− | if(!cur){
| + | |
− | return prev;
| + | |
− | }
| + | |
− |
| + | |
− | if(cur.transform){
| + | |
− | data = cur.transform(data);
| + | |
− | }
| + | |
− |
| + | |
− | disp = data;
| + | |
− | if(cur.display){
| + | |
− | disp = cur.display(data);
| + | |
− | }
| + | |
− |
| + | |
− | return {idx: idx, data: data, display: disp};
| + | |
− | }
| + | |
− |
| + | |
− | function showEncHdr(hdr, data){
| + | |
− | /*elFmt.value = 'Start: Spy = '+hdr.dnaStart+', End: GEM = '+hdr.dnaEnd+
| + | |
− | '\nSubst: '+hdr.subst+' = '+hdr.dnaSubst+
| + | |
− | '\nLength: '+hdr.len+' = '+hdr.dnaLen+
| + | |
− | '\nCRC16: '+hdr.crc+' = '+hdr.dnaCrc+'\nData: '+data;*/
| + | |
− | /*elFmt.value = "Total length: " + hdr.msg.length +
| + | |
− | "\nMessage length: " + hdr.data.length;*/
| + | |
− | }
| + | |
− |
| + | |
− | function showDecHdr(hdr){
| + | |
− | /*elFmt.value = 'Start: '+hdr.dnaStart+' = Spy, End: '+hdr.dnaEnd+' = GEM'+
| + | |
− | '\nSubst: '+hdr.dnaSubst+' = '+hdr.subst+
| + | |
− | '\nLength: '+hdr.dnaLen+' = '+hdr.len+
| + | |
− | '\nCRC16: '+hdr.dnaCrc+' = '+hdr.crc+'\nData: '+hdr.data;*/
| + | |
− | /*elFmt.value = "Total length: " + hdr.msg.length +
| + | |
− | "\nMessage length: " + hdr.data.length;*/
| + | |
− | }
| + | |
− |
| + | |
− | function clearEls(){
| + | |
− | elErr.innerHTML = '';
| + | |
− | //elMsg.value = '';
| + | |
− | elFmt.value = '';
| + | |
− | //elEnc.value = '';
| + | |
− | }
| + | |
− |
| + | |
− | function updateEnc(){
| + | |
− | var msg = elMsg.value, data, key;
| + | |
− | elEnc.value = '';
| + | |
− | clearEls();
| + | |
− | updateDir(elMsg);
| + | |
− |
| + | |
− | if(doCrypt){
| + | |
− | key = elKey.value;
| + | |
− | msg = Aes.Ctr.encrypt(msg, key, cryptBits);
| + | |
− | }
| + | |
− | data = subCoding.encodeStr(msg);
| + | |
− |
| + | |
− | if(data === null){
| + | |
− | elErr.innerHTML = 'Illegal character in input message';
| + | |
− | return;
| + | |
− | }
| + | |
− |
| + | |
− | var hdr, dna;
| + | |
− |
| + | |
− | try{
| + | |
− | hdr = Message.packDNA(data, 1);
| + | |
− | dna = hdr.msg;
| + | |
− | }catch(e){
| + | |
− | elErr.innerHTML = e.message;
| + | |
− | console.error(e);
| + | |
− | return;
| + | |
− | }
| + | |
− |
| + | |
− | showEncHdr(hdr, data);
| + | |
− | elEnc.value = dna;
| + | |
− | elLen.innerHTML = dna.length;
| + | |
− | }
| + | |
− |
| + | |
− | function decode(dna, err){
| + | |
− | if(dna.length < Message.minLength){
| + | |
− | throw err || new Error("Sequence too short to contain a message");
| + | |
− | }
| + | |
− |
| + | |
− | var hdr = null;
| + | |
− | try{
| + | |
− | hdr = Message.unpackDNA(dna, 1);
| + | |
− | }catch(e){
| + | |
− | hdr = null;
| + | |
− | if(!err || (e.level > err.level)){
| + | |
− | err = e;
| + | |
− | }
| + | |
− | }
| + | |
− |
| + | |
− | if(hdr){
| + | |
− | return hdr;
| + | |
− | }
| + | |
− |
| + | |
− | var next = Message.findStart(dna, 1);
| + | |
− |
| + | |
− | if(next === -1){
| + | |
− | throw err || new Error("No message head found");
| + | |
− | }
| + | |
− |
| + | |
− | return decode(dna.substr(next), err);
| + | |
− | }
| + | |
− |
| + | |
− | function updateDec(){
| + | |
− | var dna = elEnc.value, hdr, msg, key;
| + | |
− | elMsg.value = '';
| + | |
− | clearEls();
| + | |
− | updateDir(elEnc);
| + | |
− |
| + | |
− | try{
| + | |
− | hdr = decode(dna, null); //Message.unpackDNA(dna, 1);
| + | |
− | msg = subCoding.decodeStr(hdr.data);
| + | |
− | }catch(e){
| + | |
− | elErr.innerHTML = e.message;
| + | |
− | console.error(e);
| + | |
− | return;
| + | |
− | }
| + | |
− |
| + | |
− | if(msg === null){
| + | |
− | elErr.innerHTML = 'Message data cannot be decoded';
| + | |
− | return;
| + | |
− | }
| + | |
− |
| + | |
− | if(doCrypt){
| + | |
− | key = elKey.value;
| + | |
− | msg = Aes.Ctr.decrypt(msg, key, cryptBits);
| + | |
− | }
| + | |
− |
| + | |
− | showDecHdr(hdr);
| + | |
− | elMsg.value = msg;
| + | |
− | elLen.innerHTML = dna.length;
| + | |
− | }
| + | |
− |
| + | |
− | function updateLast(){
| + | |
− | if(lastEdit === elMsg){
| + | |
− | updateEnc();
| + | |
− | }else{
| + | |
− | updateDec();
| + | |
− | }
| + | |
− | }
| + | |
− |
| + | |
− | function updateCrypt(){
| + | |
− | if(this === elKey) if(this.value.length > 0){
| + | |
− | elCrypt.checked = true;
| + | |
− | }
| + | |
− |
| + | |
− | if(elCrypt.checked){
| + | |
− | doCrypt = true;
| + | |
− | subCoding = EightBit;
| + | |
− | }else{
| + | |
− | doCrypt = false;
| + | |
− | }
| + | |
− |
| + | |
− | updateLast();
| + | |
− | }
| + | |
− |
| + | |
− | function updateDir(last){
| + | |
− | if(last){
| + | |
− | lastEdit = last;
| + | |
− |
| + | |
− | elDoEnc.checked = last === elMsg;
| + | |
− | elDoDec.checked = last === elEnc;
| + | |
− |
| + | |
− | return;
| + | |
− | }
| + | |
− | }
| + | |
− |
| + | |
− | elMsg.onchange = updateEnc;
| + | |
− | elMsg.onkeyup = updateEnc;
| + | |
− |
| + | |
− | elEnc.onchange = updateDec;
| + | |
− | elEnc.onkeyup = updateDec;
| + | |
− |
| + | |
− | elKey.onchange = updateCrypt.bind(elKey);
| + | |
− | elKey.onkeyup = updateCrypt.bind(elKey);
| + | |
− |
| + | |
− | elDirs.forEach(function(el){
| + | |
− | el.onchange = function(){
| + | |
− | if(!this.checked){
| + | |
− | return;
| + | |
− | }
| + | |
− |
| + | |
− | if(this.value === 'enc'){
| + | |
− | lastEdit = elMsg;
| + | |
− | }else{
| + | |
− | lastEdit = elEnc;
| + | |
− | }
| + | |
− |
| + | |
− | updateLast();
| + | |
− | };
| + | |
− | });
| + | |
− |
| + | |
− | elCrypt.onchange = updateCrypt.bind(elCrypt);
| + | |
− |
| + | |
− | elStage.onclick = function(evt){
| + | |
− | if(!currentStage){
| + | |
− | currentStage = {idx: -1, data: null, display: null};
| + | |
− | }
| + | |
− |
| + | |
− | currentStage = advanceStage(currentStage);
| + | |
− | elFmt.value = currentStage.display;
| + | |
− | };
| + | |
− |
| + | |
− | if(window.location.hash.length > 1){
| + | |
− | var h = window.location.hash;
| + | |
− | if(h[1] == 'e'){
| + | |
− | elMsg.value = h.substr(2);
| + | |
− | updateEnc();
| + | |
− | }else{
| + | |
− | elEnc.value = h.substr(2);
| + | |
− | updateDec();
| + | |
− | }
| + | |
− | }else{
| + | |
− | updateEnc();
| + | |
− | }
| + | |
− | </script>
| + | |
− | </body>
| + | |
− | </html>
| + | |
− | {{Groningen/footer}}
| + | |