diff options
| author | Mirek Kratochvil <exa.exa@gmail.com> | 2026-10-29 21:59:11 +0100 |
|---|---|---|
| committer | Mirek Kratochvil <exa.exa@gmail.com> | 2026-10-29 21:59:11 +0100 |
| commit | a57b23e308bb5dd0846ad977dbf931aeef8ed42f (patch) | |
| tree | 27ce30db55bc94d87d6c83f0973407a7b8ec30f1 /README.md | |
| parent | 74b7edfefcc51a7e199c2573dab4668a04642356 (diff) | |
| download | ls47-a57b23e308bb5dd0846ad977dbf931aeef8ed42f.tar.gz ls47-a57b23e308bb5dd0846ad977dbf931aeef8ed42f.tar.bz2 | |
a bit of a clean-up
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 398 |
1 files changed, 195 insertions, 203 deletions
@@ -1,8 +1,6 @@ # LS47 hand-computable cipher -**This repository is a mirror, please point all activity/issues to https://git.e-x-a.org/exa/ls47** - This is a slight improvement of the ElsieFour cipher as described by Alan Kaminsky [1]. We use 7x7 characters instead of original (barely fitting) 6x6, to be able to encrypt some structured information. We also describe a simple @@ -17,19 +15,29 @@ We suggest printing the model using more than one filament color to make the letters easily recognizable. Thanks go to Martin Ptasek for providing the picture. -If you trust your computer, there are several LS47 implementations around: +If you trust your computer, there's an implementation in `ls47.py`. + +### Version history + +Historically, there were several other versions, e.g.: -- A very simple python implementation in available here in `ls47.py`. -- A much better version usuable as an actual binary (also supporting several +- A nice Python version usuable as an actual binary (also supporting MANY versions of padding and the original ElsieFour cipher) was supplied by - Bernhard Esslinger from the CrypTool project, available in `lc4.py`. + Bernhard Esslinger from the CrypTool project. - Javascript version (npm-compatible) of the cipher implementations was created - by Ulysse McConnell, available at https://github.com/umcconnell/lc4 + by Ulysse McConnell, now available at https://github.com/umcconnell/lc4 +- Trevor Terris supplied a nicely arranged playing-cards version together with + useful hints on how to encrypt with these. -### Character board +These versions can be found in git history of the repository. -We have added some real punctuation, basic stuff for writing expressions, -punctuation and quotes. The letters of the board now look like this: +In 2026, the board was slightly reorganized and we started using the version +with arrows instead of the numbers. + +## Character board + +Compared to LC4, we have added some real punctuation, basic stuff for writing +expressions, and some quotes. The letters of the board now look like this: ``` _ A B C D E F @@ -41,20 +49,21 @@ U V W X Y Z . 7 8 9 + - * / ``` -The cipher works with position changes. Zoomed in; it's very practical to also -have the shift information written on the tiles as arrows: +The cipher works by changing positions of a marker over a set of tiles (with +arrows) and permuting the tiles. For the position changes, the tiles have +arrows on them (0-3 horizontally and vertically, in either direction): ``` .-----. .-----. .-----. .-----. .-----. -| | | >| | >| | >| |< | -| _ | | A | | B >| | C >| |< D | ... -| | | | | | | >| |< | +| | | | |< | |< | | >| +| _ | |< A | | B | |< C | | D >| ... +| | | | |< | |< | | >| '-----' '-----' '-----' '-----' '-----' .-----. .-----. -| | | >| -| G | | H >| ... -|v | |v | +| ^ | | ^ | +| G | |< H | ... +| | | | '-----' '-----' . . . . @@ -72,229 +81,212 @@ more concentrated: ### Encryption -1. The symmetric key is the initial state of the board. Arrange your tiles to 7x7 square according to the key. +Initialization: + +1. The symmetric key is the initial state of the board. Arrange your tiles to + 7x7 square, according to the key. 2. Put the marker on the tile in the upper left corner. -3. Find the next letter you want to encrypt on the board (call it `P`). -4. Look at the marker; let's call the tile under the marker `M`. -5. The marked tile `M` has arrows drawn on it. Put a finger on tile `P` and move it to next tiles according to arrows on `M` -- e.g., if it has 2 arrows to the right and one to up, you jump 2 tiles to the right and one up. In case you hit the corner of the board, do a "wraparound" and continue on the other side. After the jumps, your finger rests on the ciphertext tile; mark it `C`. This letter is now encrypted and you can "transmit" it. -6. Shift the row that contains the plaintext letter one position to the right. The tile on the right will get out of the board; so do a "wraparound" again and put it to the empty spot on the left. (Let's call this "rotation"). If the marker is present on any of the affected tiles, DO NOT CARRY IT with the tiles (it should stay on the same coordinates). -7. In a similar fashion, rotate the column that now (after the first rotation) contains the ciphertext tile `C` one position down, again doing the wraparound and preserving the marker position. -8. Update the position of the marker: Look at the arrow directions on the ciphertext tile `C` and move the marker according to the directions (just like previously with finding the ciphertext). -9. Repeat from 3 as many times as needed to encrypt the whole plaintext. -#### Encryption example with ascii images! +Encryption loop (repeat for all letters of the plaintext): + +1. You need to encrypt one letter of the plaintext. First, find the position of + the tile with that letter on the board. This is the *plaintext tile*. +2. Second, look at the tile under the marker (*marker tile*); we are interested + in the arrows that are on the marker tile. Start at the *plaintext tile*, + and make steps from it in the exact direction and count as there are arrows + on the *marker tile*. In case you run out of board in any direction, wrap + around to the other side of the board. The steps will end on a *ciphertext + tile*. +3. Look at the letter on the *ciphertext tile*. This is the next letter of the + ciphertext; write it down (or send it via telegraph, or such). +4. Do the rotations: + - First, shift the row where the *plaintext tile* lies by one position to the + right. The tile on the right will get shifted out out of the board, so wrap + it around again to the now-empty spot on the left of the board. (We can + call this *rotation*.). If the marker is sitting on any of the affected + tiles, DO NOT MOVE IT with the tiles (or move it back after you do the + rotation) -- it should stay on the exactly same coordinates. + - Second, similarly, rotate the *column* that now (after the first rotation) + contains the *ciphertext tile* one position down, again wrapping the last + tile, and keeping the marker position intact. +5. Look at the arrows on the *ciphertext tile* `C` and move the marker + according to the arrow directions (just like previously when you were + following the arrows from the plaintext to ciphertext). +6. Repeat from 3 as many times as needed to encrypt the whole plaintext. + +### Decryption + +Decryption procedure is basically the same, except that instead of knowing the +plaintext tile and finding the ciphertext tile, you you *know the ciphertext +tile and need to trace back to the plaintext tile*. + +This is done simply by reversing the step 2 of the encryption loop: Instead of +taking the arrows on the marker tile "forward" from plaintext to ciphertext, +you start at ciphertext, and *take the arrows "backwards"* to find the +plaintext. + +Note that the rotations and marker position change is still done "forwards", +just like in encryption. +### Encryption example! + +Let's use this as the starting key: +``` + E F _ A B C D + L M G H I J K + ( ) / : ? ! ' + S T N O P Q R + Z . U V W X Y + 5 6 0 1 2 3 4 + + * 7 8 9 , - ``` -1,2. Symmetric key with 3,4. We want to encrypt 'Y'. - marker put on 'E': Look at the marked tile first: - [E]F _ A B C D /-----\ - L M G H I J K |< | - ( ) / : ? ! ' |< E | - S T N O P Q R | | - Z . U V W X Y \-----/ - 5 6 0 1 2 3 4 - + * 7 8 9 , - +The marker starts in the upper right, thus sitting on `E`. We label the marker +with brackets, a marked tile is shown as `[E]`: -5. Ciphertext is 'W' 6. Rotate the plaintext 1 position - (='Y' moved to <<) right, keep marker coordinates. +``` +[E]F _ A B C D + L M G H I J K + ( ) / : ? ! ' + S T N O P Q R + Z . U V W X Y + 5 6 0 1 2 3 4 + + * 7 8 9 , - +``` - [E]F _ A B C D [E]F _ A B C D - Output 'U'! L M G H I J K L M G H I J K - ( ) / : ? ! ' ( ) / : ? ! ' - S T N O P Q R S T N O P Q R - Z . U V W X Y >> Y Z . U V W X - 5 6 0 1 2 3 4 5 6 0 1 2 3 4 - + * 7 8 9 , - + * 7 8 9 , - +Initialization is done, we start the encryption loop. +**We want to encrypt `Q`.** Look at the arrows at the marked tile: -7. Rotate the ciphertext 1 Now look at the ciphertext tile: - position down. +``` +.-----. +| >| +| E | +| >| +'-----' +``` - [E]F _ A B , D /-----\ - L M G H I C K | >| - ( ) / : ? J ' | W >| - S T N O P ! R |vvv | - Y Z . U V Q X \-----/ - 5 6 0 1 2 W 4 - + * 7 8 9 3 - +This means that the ciphertext tile is 2 positions to the right from the +plaintext tile. We find `Q` on the board, move 2 positions to the right +(wrapping around to the left side), and finish at `S`. **`S` is the next letter +of the ciphertext.** -8. Update the marker position 9. GOTO 3. - by ciphertext arrows (vvv>>) +Now, let's do the rotation: First we rotate plaintext tile to the right: +``` +[E]F _ A B C D + L M G H I J K + ( ) / : ? ! ' + R S T N O P Q <--- this row moved, and R wrapped around + Z . U V W X Y + 5 6 0 1 2 3 4 + + * 7 8 9 , - +``` - E F _ A B , D - L M G H I C K - ( ) / : ? J ' - S T[N]O P ! R - Y Z . U V Q X - 5 6 0 1 2 W 4 - + * 7 8 9 3 - +After that, we rotate the ciphertext tile downwards: +``` +[E]* _ A B C D + L F G H I J K + ( M / : ? ! ' + R ) T N O P Q + Z S U V W X Y + 5 . 0 1 2 3 4 + + 6 7 8 9 , - + + ^ + '------- this column moved, and the * got wrapped to the top +``` +Now we look at the ciphertext: +``` +.-----. +| ^ ^>| +| S | +| >| +'-----' ``` -### Decryption +It says that we move the marker 2 positions up and 2 to the right, so we end up +with a new marker position: +``` + E * _ A B C D + L F G H I J K + ( M / : ? ! ' + R ) T N O P Q + Z S U V W X Y + 5 .[0]1 2 3 4 + + 6 7 8 9 , - +``` -Decryption procedure is basically the same, except that in step 5 you know `C` -and `M`, and need to produce `P` by going back to it from `C`. Simply take the -arrows on the marked tile and use them in reverse direction starting from `C`. +That's it for encrypting the `Q`; now we can continue the encryption loop with +another letter. ### Key generation Grab a bag full of tiles and randomly draw them one by one. Key is the 49-item permutation of them. -## Modifications +## Extensions and modifications ### Key expansion from a password Remembering 49-position random permutation that includes weird characters is -not very handy. You can instead derive the keys from an arbitrary string of -sufficient length. - -"Sufficient" means "provides enough entropy". Full keys store around 208 bits -of entropy. To reach that, your password should have: - -- at least around 61 decimal digits if made only from random decimal digits -- at least around 44 letters if made only from completely random letters -- at least around 40 alphanumeric characters if made randomly only from them - -To have the "standard" 128 bits of entropy, the numbers reduce to roughly 39, -28 and 25, respectively. - -Note that you can save the expanded tile board for later if you don't want to -expand the passwords before each encryption/decryption. - -The actual expansion can be as simple as this: - -1. Put the tiles on the board in a fixed "initial" position, - e.g. as on the character board picture above, sorted by the arrow offsets. -2. Find the first letter of the password. Rotate it first horizontally, then - vertically, according to the number of arrows on the tile. -3. Find the tile below right (`v>` in arrows) of the new position of the - password letter (possibly wrapping around the board), and rotate it - horizontally and vertically according to what its arrows say -- just as with - the password letter before. -4. Repeat as needed from step 2 for the whole password. -5. Final tile positions are the expanded key. +not very handy. There are many methods to do key expansion, so we show a simple +(although not super secure) one here: + +1. Think a password as a string of letters from the board. +2. Start with a board in the "canonical order". +3. In series, take all letters of your password from the board and place them + to the new board, starting at bottom right, and contining by rows going to + the left, and wrapping upwards to the next line. If some letter is already + moved out of the board, ignore it. +4. If the password did not include the space tile (`_`), add it as if it was + the last letter of the password. +5. Finally, take the rest of the board in the order (line-by-line from the + bottom right) and continue adding it to the password lines (again adding + line-by-line, as with the password). This will squash any blanks left over + after removing the password tiles. + +The new board is the expanded key. + +For any notion of real security, it is quite adviseable to **not** rely on the +mixing provided by this procedure (it **is** insufficient). Instead, run a few +encryption rounds of random padding characters (i.e., a *nonce*) before +starting the encryption of the actual plaintext (see below). ### Undistinguishable ciphertexts To get a different ciphertext even if the same plaintext is encrypted -repeatedly, prepend it with a nonce. A nonce is a completely random sequence of -letters of a pre-negotiated length (e.g. N tiles drawn randomly from a bag, -adviseable value of N is at least 10). +repeatedly, prepend the plaintext with a *nonce*. A nonce is a completely +random sequence of letters, usually of pre-negotiated length or with an easily +distinguishable separator. + +You can e.g. agree to always draw exactly 10 random tiles from a bag as the +nonce, or to use nonces of random length while keeping the space tile as a +separator that is guaranteed to not occur in the nonce. -You may also want to add a random number of spaces to the end of the ciphertext --- it prevents the enemy from seeing the difference between ciphertexts of 'yes -please' and 'no', which would otherwise encrypt to gibberish that is easily -distinguishable by length, like `qwc3w_cs'(` and `+v`. +Randomization of ciphertext length is quite beneficial for short messages; even +if not using nonces, it is quite beneficial to add a random number of spaces to +the end of the ciphertext: It prevents the adversary from seeing the difference +between plaintexts such as 'yes please' and 'no', the ciphertexts of whese +would otherwise be easily distinguishable by length, like `qwc3w_cs'(` and +`+v`. ### Authenticated encryption Because ciphertext may be altered in the transfer or during the error-prone -human processing, it is advised to append a simple "signature" to the end of -the message; which may look as simple as `__YourHonorableNameHere`. If the -signature doesn't match expectations (which happens with overwhelming -probability if there was any error in the process), either try again to see if -you didn't make a mistake, or discard the message and ask the sender to -re-transmit. +human processing, it is advised to append a simple human-readable "signature" +to the end of the message. That may look as simple as `__YourNameHere`. In case +there's any error or manipulation in the ciphertext, the signature will quite +likely come out completely garbled with overwhelming probability, serving +essentially as an authenticated checksum. -This works because the cipher output is message-dependent: Having a wrong bit -somewhere in the middle causes avalanche effect and erases any meaning from the -text after several characters. - -### Alternative board with playing cards ♥♦♠♣ - -If you find the above tiles complicated to obtain or create, you can very -easily use playing cards to do the same, similarly as with the [Solitaire -cipher by Schneier](https://www.schneier.com/academic/solitaire/). (Moreover, -use of playing cards could be more innocuous and easily explainable to a -suddenly appearing adversary. Playing cards is sure less suspicious than -playing some peculiarly numbered tiles! :] ) - -To simplify things a bit, we shall use the following layout: - -``` -A B C D E F G -H I J K L M N -O P Q R S T U -V W X Y Z _ . -, - + * / : ? -! ' ( ) 1 2 3 -4 5 6 7 8 9 0 -``` - -This maps nicely to the playing card suits: - -| Card value | ♦ | ♣ | ♥ | ♠ | -|------------|---|---|---|---| -| A | A | N | _ | 1 | -| 2 | B | O | . | 2 | -| 3 | C | P | , | 3 | -| 4 | D | Q | - | 4 | -| 5 | E | R | + | 5 | -| 6 | F | S | * | 6 | -| 7 | G | T | / | 7 | -| 8 | H | U | : | 8 | -| 9 | I | V | ? | 9 | -| 10 | J | W | ! | 0 | -| J | K | X | ' | | -| Q | L | Y | ( | | -| K | M | Z | ) | | - -The last 3 cards are not used. (Actually, you are free to use the suits in -whatever order you like and discard the last 3 of the suit you like the least.) - -Getting the X and Y offsets from the cards is a slightly more challenging than -with the tiles that have the offsets marked, but still doable. Calculate -`13*suit + card value - 1`, then compute the standard quotient and remainder -for division by 7. I settled with having the second card set laid out next to -the "active" board, serving as a reference. - -The result may look like this: - - - -In this example, the marker is on J♣ (at the top left), and we want to encrypt -the plaintext character `E`. The image includes the character, index, and -offsets for the relevant cards. - -1. From the card mapping, `E` maps to 5♦ (letters `A` through `M` map to -Tiles), which is on the second row. -2. The marker card, J♣, has: - - an index of 24 (Clubs are suit 1, and the Jack is the 11th card in the - suit): `i = 1 * 13 + 11 = 24` - - an x-offset of 3: `x = i % 7 = 24 % 7 = 3` - - a y-offset of 3: `y = i / 7 = 24 / 7 = 3` -3. Using the marker offsets, the ciphertext card is three rows down and three - columns to the right of 5♦, which is 6♠. -4. From the card mapping, 6♠ maps to `6` (Spades map directly to digits), and - has: - - an index of 45 (Spades are suit 3, and 6 is the 6th card in the suit): `i - = 3 * 13 + 6 = 45` - - an x-offset of 3: `x = i % 7 = 45 % 7 = 3` - - a y-offset of 6: `y = i / 7 = 45 / 7 = 6` -5. We then complete the cipher as normal: - - Output `6` as the ciphertext - - Rotate the row containing the plaintext card, 5♦ - - Rotate the column containing the ciphertext card, 6♠ - - Move the marker according to the offsets of the ciphertext card, to the - right 3 and down 6 - -For the original LC4, you need just 3 suits, modifying the board to "align" to -the suits e.g. as follows: - -``` -A B C D E F -G H I J K L -M N O P Q R -S T U V W X -Y Z _ 2 3 4 -5 6 7 8 9 # -``` +This works because the cipher output is message-dependent: Taking a different +input (or making a different step) anywhere in the middle of the encryption +causes an avalanche effect, and effectively erases any meaning from the text +after several iterations. ## References -[1] *Kaminsky, Alan. "ElsieFour: A Low-Tech Authenticated Encryption Algorithm For Human-to-Human Communication." IACR Cryptology ePrint Archive 2017 (2017): 339.* +[1] *Kaminsky, Alan. "ElsieFour: A Low-Tech Authenticated Encryption Algorithm For Human-to-Human Communication." IACR Cryptology ePrint Archive 2017 (2017): 339.* |
