Checksum
| Align bytes:7
| Type | Pad each character:0=no
| | | | LSB Len:6 MSB Len:0
| | | | | [------------------------ Plaintext -----------------------] |
| | | | | Len:4 Len:4 Len:3 Len:1 Len:4 Len:1 Align |
+--------------+ | | | | | Ucase | Lcase | Lcase | Lcase | Lcase | Lcase Padding |
+---------+ | | | | | | | | | | | | | | | | | | |
+----+ | | | | | | | | P | | h | | o | | e | | b | | e | |
|___ |___ |___ |___ |___ |__ | |___ |__ | |___ |__ | |___ |__ | |__ |__ | | |__ | |__ |__ | | |______ ___|
1101 1111 0100 1001 1100 111 0 0110 100 1 0110 100 0 0000 011 0 111 001 0 0 100 0 1000 001 0 0 1011101 0000
kkkk kkkk kkkk kkkk tttt aaa p oooo bbb c dddd bbb c dddd bbb c ddd bbb c d bbb c dddd bbb c d eeeeeee xxxx
Where:
k ... FCS-16 checksum (4 bytes)
t ... Encryption type (always '1100')
a ... # pad bytes (the number of binary digits must be a multiple of 4; this is the number of padding bits to ensure this)
p ... Pad each character (0 = no, 1 = yes) *** This example does not pad the individual characters ***
o ... Plaintext length (LSB)
b ... Length of encoded character
c ... Case (0 = lowercase, 1 = UPPERCASE)
d ... Binary-encoded Morse code character (optionally padded to 7 chars)
e ... Word boundary pad chars
x ... Plaintext length (MSB)
d h b
- -- ----
0 00 0000
1 01 0001
2 02 0010
3 03 0011
4 04 0100
5 05 0101
6 06 0110 - the length of "Phoebe"
7 07 0111
8 08 1000
9 09 1001
10 0A 1010
11 0B 1011
12 0C 1100
13 0D 1101
14 0E 1110
15 0F 1111
<script type="text/javascript" src="morse_code.js"></script>
That will make the X_MORSE object available.
var job = {
'mode':'e',
'text':Enter your plaintext here' + '\t' + 'tabs are OK' + '\n' + 'and so are newlines',
'pad:'y'
}
var result = X_MORSE.code(job); // Encode our plaintext
alert(result.hex + '\t\n\n' + result.bin); // Show the results of encoding
To unencode, we can use the same object, but we have to move things around a bit:
job.mode = 'd'; // Decoding now
job.text = result.hex; // We unencode the hex-coded string
var result = X_MORSE.code(job); // Decode our encoded string
alert(result.hex + '\t\n\n'); // Show the decoded plaintext
And there you have it: encoding - and decoding - plaintext using Morse Code.
/*
######################################
Encryption method based on morse code
Public methods
------------------------------------
code Encoder/Decoder
######################################
*/
var X_MORSE = (function() {
var CHR_VALID = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,?-=:;()"$\'^_@!*+~#&/ \\`%[]{}|<>' + '\n\t\r',
crc = '',
//
MORSE = [
/*
The first line is the Morse code for the ASCII character, where '0' = 'DIT' and '1' = 'DAH'.
The second line is the ASCII character (for reference only).
The third line is a composite number (currently unused): the length of the Morse code followed
by the decimal equivalent of the binary value of the Morse code.
*/
'01','1000','1010','100','0','0010','110','0000','00','0111','101','0100','11',
// A B C D E F G H I J K L M
// 21 48 410 34 10 42 36 40 20 47 35 44 23
'10','111','0110','1101','010','000','1','001','0001','011','1001','1011','1100',
// N O P Q R S T U V W X Y Z
// 22 37 46 413 32 30 11 31 41 33 49 411 412
'11111','01111','00111','00011','00001','00000','10000','11000','11100','11110',
// 0 1 2 3 4 5 6 7 8 9
// 531 515 57 53 51 50 516 524 528 530
'010101','110011','001100','100001','10001','111000','101010','10110','101101','010010','0001001',
// . , ? - = : ; ( ) " $
// 621 635 612 633 517 656 642 522 645 618 79
'011110','010100','001101','011010','1110','101011','01010','01000','000101','0100011','10010',
// ' ^ _ @ ! * + ~ # & /
// 630 620 613 626 414 643 510 58 63 735 518
'000000','0100000','00010','0101010','1000101','110101','1001001','0110110','1110110','1110111',
// (sp) \ ` % [ ] { } | <
// 60 732 52 742 769 653 773 754 7118 7119
'0001000','1100111','1100110','1100100'
// > \n \t \r
// 78 7103 7102 7100
],
//
exknob;
function get_random_binary_string(strlen) {
var retval = '';
while (retval.length < strlen) {
retval += (String(Math.floor(Math.random() * 10)) < 5) ? "0" : "1";
}
return retval;
}
/*
---------------------------------------
Min bits per char: 5 (not padded)
Min bits per char: 11 (padded)
Format of encrypted string (spaces for clarity):
**********************************
No nonsense, characters not padded
**********************************
Checksum Nonsense:0
| | Align bytes:2
| Type | | Pad each character:0
| | | | | LSB Len:6 MSB Len:0
| | | | | | [------------------------ Plaintext -----------------------] |
| | | | | | Len:4 Len:4 Len:3 Len:1 Len:4 Len:1 Align |
+--------------+ | | | | | | Ucase | Lcase | Lcase | Lcase | Lcase | Lcase Padding |
+---------+ | | | | | | | | | | | | | | | | | | | |
+----+ | | | | | | | | | P | | h | | o | | e | | b | | e | |
|___ |___ |___ |___ |__ | _|_ | |___ |__ | |___ |__ | |___ |__ | |__ |__ | | |__ | |__ |__ | | |______ ___|
1101 1111 0100 1001 110 0 111 0 0110 100 1 0110 100 0 0000 011 0 111 001 0 0 100 0 1000 001 0 0 1011101 0000
kkkk kkkk kkkk kkkk ttt n aaa p oooo bbb c dddd bbb c dddd bbb c ddd bbb c d bbb c dddd bbb c d ee xxxx
Where:
k ... FCS-16 checksum (4 bytes)
t ... Encryption type (always '110')
n ... Nonsense present (0 = no, 1 = yes)
a ... # pad bytes (to align word boundary)
p ... Pad each character (0 = no, 1 = yes)
o ... Plaintext length (LSB)
b ... Length of encoded character
c ... Case (0 = UPPER, 1 = lower)
d ... Binary-encoded Morse code character (optionally padded to 7 chars)
e ... Word boundary pad chars
x ... Plaintext length (MSB)
Combining all of the binary digits yields:
1101 1111 0100 1001 1100 1110 0110 1001 0110 1000 0000 0110 1110 0100 1000 1000 0010 0101 1101 0000
Or, to be more concise:
DF 49 CE 69 68 06 E4 88 25 D0
*/
function asc_to_morse(pfx, ptxt, bin_len, padded) {
var retval = [],
s = pfx + ptxt,
S = s.toUpperCase(),
m = '',
t = '',
u = '',
charIndex = 0,
//
exknob;
retval.push('1100'); // Nonsense always disabled
// Replaced after encoding is complete
retval.push('0000');
// Plaintext length (LSB)
retval.push(bin_len.substr(4));
for (var i = 0; i < S.length; i++) {
charIndex = CHR_VALID.indexOf(S.substr(i, 1));
// UPPER(1) or lower(0) case
u = (s.substr(i, 1) === S.substr(i, 1)) ? '1' : '0';
m = String(MORSE[charIndex]); // Binary Morse code equivalent
t = '000' + m.length.toString(2);
t = t.substr(t.length - 3) + u + m; // LEN + CASE + CHAR
if (padded === '1') {
if (t.length < 11) {
t += get_random_binary_string(11 - t.length);
}
}
retval.push(t);
}
// Pad 'n' bytes for the word boundary
charIndex = 8 - ((retval.join('') + '1111').length % 8);
if (charIndex !== 8) {
retval.push(get_random_binary_string(charIndex));
}
t = '000' + charIndex.toString(2) + padded;
// # of word align bits + padding indicator
retval[1] = t.substr(t.length - 4);
// Plaintext length (MSB)
retval.push(bin_len.substr(0, 4));
return retval.join('');
}
// ------------------------------------
function binary_to_hex(s) {
var retval = [];
var h = 0;
for (var i = 0; i < s.length; i += 4) {
h = parseInt(s.substr(i, 4), 2);
retval.push(h.toString(16));
}
return retval.join('');
}
// ------------------------------------
function hex_to_binary(h) {
var b = '';
var n = 0;
var retval = [];
for (var i = 0; i < h.length; i++) {
n = parseInt('0' + h.substr(i, 1), 16);
b = '0000' + n.toString(2);
retval.push(b.substr(b.length - 4));
}
return retval.join('');
}
// ------------------------------------
function morse_to_asc(b) {
var i = 0,
j = 0,
retval = [],
x = 0,
charIndex = 0,
pad_it = b.substr(7, 1),
pbyt = binary_to_hex('0' + b.substr(4, 3)), // Pad binary digits
plen = parseInt(pbyt, 16), // 10);
hlen = binary_to_hex(b.substr(b.length - 4) + b.substr(8, 4)), // Length, in hex
tlen = (parseInt(hlen.substr(0,1),16)*16) + parseInt(hlen.substr(1,1),16),
t = '',
o = 0,
jwok = 0,
//
exknob;
// Move to the encoded text
b = b.substr(12, b.length - (16 + plen));
while (b !== '') {
i = parseInt(b.substr(0, 3), 2);
t = b.substr(4, i);
// Morse code to ASCII
j = -1;
x = 0;
while (x < MORSE.length && j < 0) {
if (t === MORSE[x]) {
j = x;
}
x += 1;
}
if (j < 0) {
return '*** t = ' + t + ' - Invalid crypt text ***';
}
else {
t = CHR_VALID.substr(j, 1);
if (b.substr(3, 1) === '0') { t = t.toLowerCase(); }
retval.push(t);
if (pad_it === '1') {
b = b.substr(11);
}
else {
b = b.substr(i + 4);
}
}
}
return retval.join('');
}
// --------------------------------
function morse(how) {
//
// how.mode : "d" = decode, "e" = encode
// how.text : plaintext
// how.pad : "y" = pad each Morse char
// how.wok : "y" = add "nonsense" text to output
var retval = '',
p = 0,
bin='',
ipfx = '',
crc = '',
b = '',
x = '',
tlen = 0,
clen = CHR_VALID.length,
pad_it = '',
//
exknob;
switch (how.mode) {
case 'd':
crc = how.text.substr(0,4);
how.text = how.text.substr(4);
// If you did not calculate the CRC, skip this test
if (X_CRC.Fcs16(how.text) !== crc) {
return ['Invalid format - cannot decode'];
}
bin = hex_to_binary(how.text);
retval = morse_to_asc(bin);
break;
case 'e':
pad_it = (how.pad === 'y') ? '1' : '0';
// Create IV
tlen = how.text.length;
p = tlen;
var ilen = hex_to_binary(p.toString(16));
while (ilen.length < 8) { ilen = '0' + ilen; }
bin = asc_to_morse('', how.text, ilen, pad_it);
x = binary_to_hex(bin).toUpperCase();
// You may omit this step, but we still need
// a 16-bit hexadecimal value to take the place of the CRC value
// (e.g. FACE)
retval = X_CRC.Fcs16(x) + x; // crc is first 2 bytes
bin = hex_to_binary(retval);
break;
}
how.hex = retval; // Hexadecimal value
how.bin = bin; // Binary value
return how;
}
// ------------------------------------
// Exposed methods
return {
'bin_to_hex' :function(arg) { return binary_to_hex(arg); },
'hex_to_bin' :function(arg) { return hex_to_binary(arg); },
'code' :function(how) { return morse(how); },
//
'empty' :null /* dummy object closer */
};
// ------------------------------------
})();
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (1)
Author
Commented:http://www.over-engineering.com/test_morse_code.htm
should anyone be interested...