ls47/README.md
2025-08-29 09:43:48 +02:00

301 lines
12 KiB
Markdown

# 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
key-expansion algorithm, because remembering passwords is popular. Similar
security considerations as with ElsieFour hold.
There's also a 3D-printable SCAD model of the whole thing. Yay!
![Tiles printed out](tiles.jpg)
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:
- A very simple python implementation in available here in `ls47.py`.
- A much better version usuable as an actual binary (also supporting several
versions of padding and the original ElsieFour cipher) was supplied by
Bernhard Esslinger from the CrypTool project, available in `lc4.py`.
- Javascript version (npm-compatible) of the cipher implementations was created
by Ulysse McConnell, available at https://github.com/umcconnell/lc4
### Character board
We have added some real punctuation, basic stuff for writing expressions,
punctuation and quotes. The letters of the board now look like this:
```
_ 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 .
, : ? ! ' ( )
0 1 2 3 4 5 6
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:
```
.-----. .-----. .-----. .-----. .-----.
| | | >| | >| | >| |< |
| _ | | A | | B >| | C >| |< D | ...
| | | | | | | >| |< |
'-----' '-----' '-----' '-----' '-----'
.-----. .-----.
| | | >|
| G | | H >| ...
|v | |v |
'-----' '-----'
. .
. .
. .
```
To run (hand-run) the encryption/decryption, you will also need some kind of a
marker (e.g. a small shiny stone, bolt nut or similar kind of well-shaped
trash).
## How-To
You may as well see the paper [1], there are also pictures. This is somewhat
more concentrated:
### Encryption
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!
```
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 , -
5. Ciphertext is 'W' 6. Rotate the plaintext 1 position
(='Y' moved to <<) right, keep marker coordinates.
[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 , -
7. Rotate the ciphertext 1 Now look at the ciphertext tile:
position down.
[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 -
8. Update the marker position 9. GOTO 3.
by ciphertext arrows (vvv>>)
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 -
```
### Decryption
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`.
### Key generation
Grab a bag full of tiles and randomly draw them one by one. Key is the 49-item
permutation of them.
## 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.
### 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).
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`.
### 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.
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:
![Using cards instead of tiles](card-tiles.jpg)
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 #
```
## References
[1] *Kaminsky, Alan. "ElsieFour: A Low-Tech Authenticated Encryption Algorithm For Human-to-Human Communication." IACR Cryptology ePrint Archive 2017 (2017): 339.*