|
|
Line 6: |
Line 6: |
| <!-- <link rel="stylesheet" type="text/css" href="reset.css" /> --> | | <!-- <link rel="stylesheet" type="text/css" href="reset.css" /> --> |
| <style> | | <style> |
− | body{ | + | #container{ |
− | font-family: sans-serif; | + | display: flex; |
− | font-size: 13pt; | + | flex-flow: row wrap; |
− | line-height: 1.5em; | + | align-items: center; |
− | padding: 0.5em; | + | justify-content: space-between; |
− | margin: 0;
| + | |
| } | | } |
− | body,textarea,a{ | + | #container > div{ |
− | background-color: #222; | + | position: relative; |
− | color: #EEE; | + | margin-right: 5px; |
| } | | } |
− | textarea,code{ | + | #container > div:last-child{ |
− | font-family: monospace;
| + | margin-right: 0px; |
− | font-size: 12pt;
| + | |
− | line-height: 1.5em;
| + | |
− | }
| + | |
− | textarea{
| + | |
− | border: 1px solid #444;
| + | |
− | width: 99%;
| + | |
− | }
| + | |
− | h1{
| + | |
− | font-size: 1.2em; | + | |
| } | | } |
| | | |
− | #container{ | + | .cnt-desc{ |
− | overflow: auto; | + | flex: 1; |
− | width: 100%;
| + | |
− | margin: 0;
| + | |
− | padding: 0;
| + | |
| } | | } |
− | #sec-left{ | + | #cnt-message{ |
− | padding: 5px; | + | flex: 4; |
− | width: 58.5%;
| + | |
− | float: left;
| + | |
| } | | } |
− | #sec-right{ | + | #cnt-key{ |
− | padding: 5px; | + | flex: 2; |
− | width: 38.5%;
| + | |
− | float: right;
| + | |
| } | | } |
| | | |
− | #err{ | + | textarea{ |
− | color: #FF3333;
| + | width: 100%; |
− | }
| + | height: 6em; |
− |
| + | |
− | .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> | | </style> |
| </head> | | </head> |
| <body> | | <body> |
| + | <h1>Encryption</h1> |
| + | |
| + | <hr /> |
| + | |
| <div id="container"> | | <div id="container"> |
− | <h1>SpyGEM coding & crypting</h1>
| + | <div id="cnt-desc-message" class="cnt-desc"> |
− | <hr class="clearfx" />
| + | Enter the message you want to secure here |
− | <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> |
− | <div id="sec-right"> | + | <div id="cnt-message"> |
− | <p>This page demonstrates the encoding and decoding of text messages | + | <h2>Message</h2> |
− | into DNA, optionally with AES-256 encryption.</p>
| + | |
− | <p>Enter a message into the Message field to have it encoded, or | + | <textarea id="msg">Hello world</textarea> |
− | enter an encoded message into the Encoded field to have it decoded. | + | </div> |
− | The Key field is used for encryption & decryption and the
| + | <div id="cnt-key"> |
− | Formatted field shows more details about the message block.</p>
| + | <h2>Key</h2> |
− | <p>To counter restriction enzymes a sequence of 'A'-bases is
| + | |
− | inserted whereever the sequence for a restriction-enzyme occurs.
| + | <textarea id="key">secret</textarea> |
− | When decoding the message these sequences are removed. The length
| + | </div> |
− | of the substitution sequence is the shortest sequence of
| + | <div id="cnt-desc-key" class="cnt-desc"> |
− | 'A' that does not occur in the encoded message.</p>
| + | Enter your secret key/password here |
− | <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> | | </div> |
− | <div class="clear"></div> | + | |
| + | <p> |
| + | <button id="stage">Transform</button> |
| + | </p> |
| + | |
| + | <div id="cnt-transform"> |
| + | <h2 id="cur">Plain text</h2> |
| + | |
| + | <textarea id="enc"></textarea> |
| + | </div> |
| + | |
| <div> | | <div> |
| <p>Credits:</p> | | <p>Credits:</p> |
Line 160: |
Line 104: |
| var elMsg = document.getElementById('msg'), | | var elMsg = document.getElementById('msg'), |
| elKey = document.getElementById('key'), | | elKey = document.getElementById('key'), |
− | elFmt = document.getElementById('fmt'),
| |
| elEnc = document.getElementById('enc'), | | elEnc = document.getElementById('enc'), |
| elErr = document.getElementById('err'), | | elErr = document.getElementById('err'), |
− | elCrypt = document.getElementById('crypt'), | + | elCur = document.getElementById('cur'), |
− | elCode8 = document.getElementById('code8'),
| + | elStage = document.getElementById('stage'); |
− | 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, | | var subCoding = EightBit, doCrypt = false, lastEdit = elMsg, |
| cryptBits = 256, stages = [ | | cryptBits = 256, stages = [ |
− | {name: "Plain text", transform: function(prev){ | + | {name: "Plain text", transform: function(prev){ // null |
| return elMsg.value; | | return elMsg.value; |
| } | | } |
| }, | | }, |
− | {name: "Encrypted", transform: function(prev){ | + | {name: "Encrypted", transform: function(prev){ // plain msg |
| var key = elKey.value; | | var key = elKey.value; |
| return Aes.Ctr.encrypt(prev, key, cryptBits); | | return Aes.Ctr.encrypt(prev, key, cryptBits); |
Line 184: |
Line 122: |
| } | | } |
| }, | | }, |
− | {name: "ASCII", display: function(data){ | + | {name: "ASCII", display: function(data){ // crypt msg |
| return data.split('').map(function(c){ | | return data.split('').map(function(c){ |
| return c.charCodeAt(0).toString(10).padStart(3, '0'); | | return c.charCodeAt(0).toString(10).padStart(3, '0'); |
Line 190: |
Line 128: |
| } | | } |
| }, | | }, |
− | {name: "Binary", display: function(data){ | + | {name: "Binary", display: function(data){ // crypt msg |
| return data.split('').map(function(c){ | | return data.split('').map(function(c){ |
| return c.charCodeAt(0).toString(2).padStart(8, '0'); | | return c.charCodeAt(0).toString(2).padStart(8, '0'); |
Line 196: |
Line 134: |
| } | | } |
| }, | | }, |
− | {name: "DNA", display: function(data){ | + | {name: "DNA", transform: function(prev){ // crypt msg |
− | return (EightBit.encodeStr(data).match(/.{1,4}/g) || []).join(' '); | + | return EightBit.encodeStr(prev); |
| + | }, display: function(data){ |
| + | return (data.match(/.{1,4}/g) || []).join(' '); |
| + | } |
| + | }, |
| + | {name: "Message", transform: function(prev){ // dna msg |
| + | return Message.packDNA(prev); |
| } | | } |
| } | | } |
Line 225: |
Line 169: |
| return {idx: idx, data: data, display: disp}; | | 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){ | | elStage.onclick = function(evt){ |
| currentStage = advanceStage(currentStage); | | currentStage = advanceStage(currentStage); |
− | elFmt.value = currentStage.display; | + | elEnc.value = currentStage.display; |
| + | elCur.innerHTML = stages[currentStage.idx].name; |
| currentStage.idx = (currentStage.idx + 1) % stages.length; | | currentStage.idx = (currentStage.idx + 1) % stages.length; |
| }; | | }; |
− |
| |
− | 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> | | </script> |
| </body> | | </body> |
| </html> | | </html> |
| {{Groningen/footer}} | | {{Groningen/footer}} |