# Stick Cipher

#### When a letter is more than a letter.

A stick cipher is a Homophonic substitution cipher that counts the sticks and curves in any letter making it hard to distinguish if the cipher is actually related to the complete symbol (the letter) or its sub-components (the stems, bowls, crossbars and spines of a letter).

You could use a stick cipher with any symbol or drawing as long as a number of lines per symbol are convened by the encoder and the decoder.

## Tool, Rules and Method

Of course counting the lines of a letter is just one way to do it, the main idea is to associate a number with a symbol that then composes the ciphertext, the fact that we are counting sticks just makes the key easy to remember.

### Rules

The number of "sticks" in a character or drawing may vary, so its important that the following rules are applied:

1. The character set to be used for encoding has been previously established and every character in the set has a numeric value. This is called the stick value.
2. The character set to be used for decoding is known and each letter of the decoding set has a numeric value.
3. At least one space value has been established.

Note that the encoding set and the decoding set do not have to be in the same order or even contain the same characters. Usually the encoding and decoding set is a a 26 letter alphabet. And the stick count of each letter can be easily guessed by using a sans-serif font.

To make your cipher harder to crack add lots of space characters and use an inverted frequency alphabet as your decoding set, that way the encoding will have nice long letter sequences making frequency analysis harder.

### Method

To encode:

1. Choose a letter at random from your encoding alphabet and get its stick value.
2. Then choose another letter at random and add its value to your previous letter.
3. Repeat step 1 and 2 until you reach the exact value of the position of the desired decoding alphabet letter.
4. To signal the end of a letter use a space character. To end a word use two space characters.

To decode:

1. Count the stick value of each character in the cipher until you hit a null value (see step 4 above).
2. Add the number you counted and cash it in for a the letter of the decoding set of that corresponding number.
3. Repeat the last two steps until you have decoded the message.

### Example:

Here is a simple example of a stick cipher.

• #### Cleartext:

Since were counting sticks no serif fonts are used (thats what sans-serif means) and letters must all be capitalized.

``ENTRE MAS LEJOS ME VAYA MAS ME ACUERDO YO DE TI``
• #### Numbered alphabetically:

Using a 26 letter alphabet, replace the original letter for its alphabet number equivalent.

``E=5, N=14, T=20, R=18, E=5, M=13, A=1, S=19, L=12, E=5, J=10, O=15, S=19, M=13, E=5, V=22, A=1, Y=25, A=1, M=13, A=1, S=19, M=13, E=5, A=1, C=3, U=21, E=5, R=18, D=4, O=15, Y=25, O=15, D=4, E=5, T=20, I=9``
• Here are the 4 different groups for an A to Z alphabet in Helvetica ordered by the number of sticks per letter.

• 1 Stick

``CIJOSU``
• 2 Sticks

``DLPQTVX``
• 3 Sticks

``ABFGHKNRYZ``
• 4 Sticks

``EMW``

Please note that the "I" in Arial and Helvetica is different from "I" in Currier New. And that the "J" in Helvetica and the "J" in Currier New are also different.

Using numbers from 0 to 9 as spaces and word breaks you can create many different ciphers that all decode to the same clear text.

• #### Posible Ciphertext:

Here is our cipher text:

``UM4RLNTHS4KIQLTJEWS3MWALMS4XR90ECQSPJD8C3VZZCTTWL66RZMV2RV8VULVF3JWTGWC5MRWBJE86LTQUTKO0EC23XWZMJJHNJ5O1RRLJPLKAPSTU6C35MXMZ7J0JPEKNAH94AWNST8MO34J2SX6DJGQKUVXWI3TB8ELKRMX1ZO8EPJENS06MWRLRWCM8DBDQYXU05XCU3OZO35ELJFEWCO1EWU9``
``` Of course any number of cipher texts are possible (it really depends on the character sets you use). ```
• ``` ```
``` ```
``` ```
``` pachanka /* Tools and polyfils */ function \$(id){ return document.getElementById(id); } // trim() if (!String.prototype.trim) { String.prototype.trim = function () { return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+\$/g, ''); }; } function add_event(element, evnt, funct){ if(element.attachEvent){ return element.attachEvent('on'+evnt, funct); }else{ return element.addEventListener(evnt, funct, false); } } /* FROM github https://github.com/lautis/unicode-substring */ function charAt(string, index) { var first = string.charCodeAt(index); var second; if (first >= 0xD800 && first <= 0xDBFF && string.length > index + 1) { second = string.charCodeAt(index + 1); if (second >= 0xDC00 && second <= 0xDFFF) { return string.substring(index, index + 2); } } return string[index]; } function slice(string, start, end) { var accumulator = ""; var character; var stringIndex = 0; var unicodeIndex = 0; var length = string.length; while (stringIndex < length) { character = charAt(string, stringIndex); if (unicodeIndex >= start && unicodeIndex < end) { accumulator += character; } stringIndex += character.length; unicodeIndex += 1; } return accumulator; } function unicode_substring(string, start, end) { var realStart = (start === undefined) ? 0 : Number(start); var realEnd = (end === undefined) ? string.length : Number(end); if (realEnd == realStart) { return ""; } else if (realEnd > realStart) { return slice(string, realStart, realEnd); } else { return slice(string, realEnd, realStart); } } /* End from github */ function load_set_preset(set_preset){ var set_presets = { latin : 'A=3, B=3, C=1, D=2, E=4, F=3, G=3, H=3, I=1, J=1, K=3, L=2, M=4, N=3, O=1, P=2, Q=2, R=3, S=1, T=2, U=1, V=2, W=4, X=2, Y=3, Z=3', iching : '䷀=1, ䷁=2, ䷂=3, ䷃=4, ䷄=5, ䷅=6, ䷆=7, ䷇=8, ䷈=9, ䷉=10, ䷊=11, ䷋=12, ䷌=13, ䷍=14, ䷎=15, ䷏=16, ䷐=17, ䷑=18, ䷒=19, ䷓=20, ䷔=21, ䷕=22, ䷖=23, ䷗=24, ䷘=25, ䷙=26, ䷚=27, ䷛=28, ䷜=29, ䷝=30, ䷞=31, ䷟=32, ䷠=33, ䷡=34, ䷢=35, ䷣=36, ䷤=37, ䷥=38, ䷦=39, ䷧=40, ䷨=41, ䷩=42, ䷪=43, ䷫=44, ䷬=45, ䷭=46, ䷮=47, ䷯=48, ䷰=49, ䷱=50, ䷲=51, ䷳=52, ䷴=53, ䷵=54, ䷶=55, ䷷=56, ䷸=57, ䷹=58, ䷺=59, ䷻=60, ䷼=61, ䷽=62, ䷾=63, ䷿=64', astrology : '♈=2, ♉=3, ♊=4, ♋=2, ♌=1, ♍=4, ♎=2, ♏=3, ♐=4, ♑=2, ♒=2, ♓=3, ☿=4, ♀=3, ♁=3, ♂=3, ♃=3, ♄=3, ⛢=5, ♆=6, ♇=3, ☉=2, 🌕=1', russian : 'А=3, Б=3, В=3, Г=2, Д=4, Е=4, Ж=3, З=2, И=3, Й=4, К=3, Л=3, М=4, Н=3, О=1, П=4, Р=2, С=1, Т=2, У=2, Ф=2, Х=2, Ц=3, Ч=2, Ш=4, Щ=4, Ъ=2, Ы=3, Ь=2, Э=2, Ю=3, Я=3', } return set_presets[set_preset]; } function load_abc_preset(abc_preset){ var abc_presets = { atoz : 'A=1, B=2, C=3, D=4, E=5, F=6, G=7, H=8, I=9, J=10, K=11, L=12, M=13, N=14, O=15, P=16, Q=17, R=18, S=19, T=20, U=21, V=22, W=23, X=24, Y=25, Z=26', ascii : '!=1, "=2, #=3, \$=4, %=5, &=6, \'=7, (=8, )=9, *=10, +=11, ,=12, -=13, .=14, /=15, 0=16, 1=17, 2=18, 3=19, 4=20, 5=21, 6=22, 7=23, 8=24, 9=25, :=26, ;=27, <=28, ==29, >=30, ?=31, @=32, A=33, B=34, C=35, D=36, E=37, F=38, G=39, H=40, I=41, J=42, K=43, L=44, M=45, N=46, O=47, P=48, Q=49, R=50, S=51, T=52, U=53, V=54, W=55, X=56, Y=57, Z=58, [=59, \\=60, ]=61, ^=62, _=63, `=64, a=65, b=66, c=67, d=68, e=69, f=70, g=71, h=72, i=73, j=74, k=75, l=76, m=77, n=78, o=79, p=80, q=81, r=82, s=83, t=84, u=85, v=86, w=87, x=88, y=89, z=90, {=91, |=92, }=93, ~=94', freq : 'E=1, T=2, A=3, O=4, I=5, N=6, S=7, R=8, H=9, D=10, L=11, U=12, C=13, M=14, F=15, Y=16, W=17, G=18, P=19, B=20, V=21, K=22, X=23, Q=24, J=25, Z=26', invf : 'Z=1, J=2, Q=3, X=4, K=5, V=6, B=7, P=8, G=9, W=10, Y=11, F=12, M=13, C=14, U=15, L=16, D=17, H=18, R=19, S=20, N=21, I=22, O=23, A=24, T=25, E=26' } return abc_presets[abc_preset]; } function rand_int(min, max) { if(typeof window.crypto !== 'undefined' && typeof window.crypto.getRandomValues === 'function'){ // Create byte array and fill with 1 random number var byteArray = new Uint8Array(1); window.crypto.getRandomValues(byteArray); var range = max - min + 1; var max_range = 256; if (byteArray[0] >= Math.floor(max_range / range) * range){ return rand_int(min, max); } return min + (byteArray[0] % range); }else{ return Math.floor(Math.random() * (max - min + 1)) + min; } } function rand_val(arr){ if(arr){ var len = arr.length - 1; return arr[rand_int(0,len)]; } } function object_invert(obj) { var new_obj = {}; for (var prop in obj) { if(obj.hasOwnProperty(prop)) { new_obj[obj[prop]] = prop; } } return new_obj; } /* Stick cipher */ function parse_set(str){ var set_map = false; if(str){ set_map = {} var set_arr = str.split(', '); set_arr = set_arr.map(function(s) { return s.trim() }); var re = new RegExp('/\d*/','g'); for (var i = 0; i < set_arr.length; i++) { var int = set_arr[i].match(/\d+\$/); var char = unicode_substring(set_arr[i], 0, 1); if(char.length = 1 && int[0]){ // Convert char to unicode number set_map[char] = parseInt(int[0], 10); } } } return set_map; } function parse_nulls(str,set_obj){ var out_arr = false; if(str || str == ' '){ out_arr = []; for (var i = 0; i < str.length; i++) { var null_char = unicode_substring(str, i, i+1); if(null_char !== ''){ out_arr.push((null_char != ' ') ? null_char.trim() : null_char); } } } return out_arr; } function validate_fields(opts){ var error = []; if(!opts.set){ error.push('The Character must be filled in.'); } if(!opts.alphabet){ error.push('The Alphabet must be filled in.'); } if(!opts.nulls){ error.push('The Nulls must be filled in.'); } if(error.length){ alert(error.join("\n")); return false; } return true; } function get_stick_cipher_options(){ var set_preset = \$('set-preset'); var set_obj = parse_set(\$('character-set').value); var nulls_arr = parse_nulls(\$('nulls').value, set_obj); return { set : set_obj, alphabet : parse_set(\$('alphabet').value), set_preset : set_preset.options[set_preset.selectedIndex].value, //strength : parseInt(\$('strength').value, 10), nulls : nulls_arr, to_upper: \$('to-upper').checked }; } function encode_stick_cipher(input, opts){ // Modify input if(opts.to_upper){ input = input.toUpperCase(); } // Declare vars var abc = opts.alphabet; var set = opts.set; var nulls = opts.nulls; var clear_arr = input.split(''); var cipher_arr = []; // Get set ranges and make array // with the values as var set_range = { min : 1, max : 1 }; var ordered_set = []; for (var key in set) { if (set.hasOwnProperty(key)){ if(set[key] < set_range.min){ set_range.min = set[key]; } if(set[key] > set_range.max){ set_range.max = set[key]; } if(!ordered_set[set[key]]){ ordered_set[set[key]] = [] } // Convert back to char ordered_set[set[key]].push(key); } } // Loop through text to create the ciphertext for (var i = 0; i < clear_arr.length; i++) { var pos = abc[clear_arr[i]]; // convert to Unicode number var sum = 0; var cipher_letter = []; while(sum < pos){ // Add random var add = rand_int(set_range.min, set_range.max); if((sum + add) > pos){ // If the new add exceeds the letter we want // we settle for a random value from the // remaining difference add = rand_int(set_range.min, (pos - sum)); } // Add a random cipher letter var rand_letter = rand_val(ordered_set[add]); //cipher_letter.push(String.fromCharCode(rand_val(ordered_set[add]))); cipher_letter.push(rand_letter); // Add for the next cipher letter sum = sum + add; } // Null separator //cipher_letter.push(String.fromCharCode(rand_val(opts.nulls))); cipher_letter.push(rand_val(opts.nulls)); // Add to the array of letters cipher_arr.push(cipher_letter.join('')); } var ciphertext = cipher_arr.join(''); /* if(opts.strength > 1){ opts.strength--; ciphertext = encode_stick_cipher(ciphertext, opts); } */ return ciphertext.trim(); } function decode_stick_cipher(ciphertext, opts){ var cleartext = ''; var cipher_arr = []; // Do Unicode split. for (var i = 0; i < ciphertext.length; i++) { cipher_arr.push(unicode_substring(ciphertext,i,i+1)); } var nulls = ''; for (var i = 0; i < opts.nulls.length; i++) { nulls += opts.nulls[i]; } var inverted_abc = object_invert(opts.alphabet); var sum = 0; var new_letter; for (var i = 0; i < cipher_arr.length; i++) { var in_nulls = nulls.indexOf(cipher_arr[i]); if(in_nulls == -1 && opts.set.hasOwnProperty(cipher_arr[i])){ sum = sum + opts.set[cipher_arr[i]]; new_letter = false; }else if(in_nulls >= 0){ if(new_letter){ cleartext += ' '; }else{ //cleartext += String.fromCharCode(inverted_abc[sum]); cleartext += inverted_abc[sum]; new_letter = true; } sum = 0; } } /* if(opts.strength > 1){ opts.strength--; cleartext = decode_stick_cipher(cleartext, opts); } */ return cleartext; } function init(){ var alphabet = \$('alphabet'); var charset = \$('character-set'); var set_preset = \$('set-preset'); var abc_preset = \$('abc-preset'); var input = \$('input'); var output = \$('output'); // Fill in sets if empty if(charset.value == ''){ charset.value = load_set_preset(set_preset.options[set_preset.selectedIndex].value); } if(alphabet.value == ''){ alphabet.value = load_abc_preset(abc_preset.options[abc_preset.selectedIndex].value); } // Presets add_event(set_preset, 'change', function(){ charset.value = load_set_preset(set_preset.options[set_preset.selectedIndex].value); }); add_event(abc_preset, 'change', function(){ alphabet.value = load_abc_preset(abc_preset.options[abc_preset.selectedIndex].value); }); // Encode while typing add_event(input, 'keyup', function(){ var opts = get_stick_cipher_options(); if(validate_fields(opts)){ output.value = encode_stick_cipher(input.value, opts); } }); add_event(output, 'keyup', function(){ var opts = get_stick_cipher_options(); if(validate_fields(opts)){ input.value = decode_stick_cipher(output.value, opts); } }); // Button listeners add_event(\$('encode'), 'click', function(){ var opts = get_stick_cipher_options(); if(validate_fields(opts)){ output.value = encode_stick_cipher(input.value, opts); } }); add_event(\$('decode'), 'click', function(){ var opts = get_stick_cipher_options(); if(validate_fields(opts)){ input.value = decode_stick_cipher(output.value, opts); } }); } window.onload = init(); ```
``` ```
``` ```
``` Menu Homepage Crypto Tools Alberti wheel cipher DRYAD Generator Morse code Obfuscation tools PGP Word List Shift cipher Stick cipher HTML Tools CSS Colour Inverter Google Maps API Search CSS optimizer HTML tester tool iframe Markdown tools Text tools Web Safe Fonts Toys Tools ;) BSOD Bunny Continue? User Countdown Facepalm Ferris, the rustacean. Motivation Random Text Generators Zen Search Get in touch [email protected] Building 3 - Business Bay - Bay Square - Dubai © Pachanka.org. All rights reserved. ```
``` ```