diff --git a/config.json b/config.json index 3ff8e47..65c5a94 100644 --- a/config.json +++ b/config.json @@ -330,6 +330,14 @@ "prerequisites": [], "difficulty": 3 }, + { + "slug": "simple-cipher", + "name": "Simple Cipher", + "uuid": "7e0d78ab-d655-4763-93ea-bac581dadcbb", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "resistor-color", "name": "Resistor Color", diff --git a/exercises/practice/simple-cipher/.docs/instructions.md b/exercises/practice/simple-cipher/.docs/instructions.md new file mode 100644 index 0000000..475af61 --- /dev/null +++ b/exercises/practice/simple-cipher/.docs/instructions.md @@ -0,0 +1,66 @@ +# Instructions + +Implement a simple shift cipher like Caesar and a more secure substitution cipher. + +## Step 1 + +"If he had anything confidential to say, he wrote it in cipher, that is, by so changing the order of the letters of the alphabet, that not a word could be made out. +If anyone wishes to decipher these, and get at their meaning, he must substitute the fourth letter of the alphabet, namely D, for A, and so with the others." +—Suetonius, Life of Julius Caesar + +Ciphers are very straight-forward algorithms that allow us to render text less readable while still allowing easy deciphering. +They are vulnerable to many forms of cryptanalysis, but Caesar was lucky that his enemies were not cryptanalysts. + +The Caesar Cipher was used for some messages from Julius Caesar that were sent afield. +Now Caesar knew that the cipher wasn't very good, but he had one ally in that respect: almost nobody could read well. +So even being a couple letters off was sufficient so that people couldn't recognize the few words that they did know. + +Your task is to create a simple shift cipher like the Caesar Cipher. +This image is a great example of the Caesar Cipher: + +![Caesar Cipher][img-caesar-cipher] + +For example: + +Giving "iamapandabear" as input to the encode function returns the cipher "ldpdsdqgdehdu". +Obscure enough to keep our message secret in transit. + +When "ldpdsdqgdehdu" is put into the decode function it would return the original "iamapandabear" letting your friend read your original message. + +## Step 2 + +Shift ciphers quickly cease to be useful when the opposition commander figures them out. +So instead, let's try using a substitution cipher. +Try amending the code to allow us to specify a key and use that for the shift distance. + +Here's an example: + +Given the key "aaaaaaaaaaaaaaaaaa", encoding the string "iamapandabear" +would return the original "iamapandabear". + +Given the key "ddddddddddddddddd", encoding our string "iamapandabear" +would return the obscured "ldpdsdqgdehdu" + +In the example above, we've set a = 0 for the key value. +So when the plaintext is added to the key, we end up with the same message coming out. +So "aaaa" is not an ideal key. +But if we set the key to "dddd", we would get the same thing as the Caesar Cipher. + +## Step 3 + +The weakest link in any cipher is the human being. +Let's make your substitution cipher a little more fault tolerant by providing a source of randomness and ensuring that the key contains only lowercase letters. + +If someone doesn't submit a key at all, generate a truly random key of at least 100 lowercase characters in length. + +## Extensions + +Shift ciphers work by making the text slightly odd, but are vulnerable to frequency analysis. +Substitution ciphers help that, but are still very vulnerable when the key is short or if spaces are preserved. +Later on you'll see one solution to this problem in the exercise "crypto-square". + +If you want to go farther in this field, the questions begin to be about how we can exchange keys in a secure way. +Take a look at [Diffie-Hellman on Wikipedia][dh] for one of the first implementations of this scheme. + +[img-caesar-cipher]: https://upload.wikimedia.org/wikipedia/commons/thumb/4/4a/Caesar_cipher_left_shift_of_3.svg/320px-Caesar_cipher_left_shift_of_3.svg.png +[dh]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange diff --git a/exercises/practice/simple-cipher/.meta/config.json b/exercises/practice/simple-cipher/.meta/config.json new file mode 100644 index 0000000..98f1304 --- /dev/null +++ b/exercises/practice/simple-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "erikschierboom" + ], + "files": { + "solution": [ + "simple-cipher.ua" + ], + "test": [ + "tests.ua" + ], + "example": [ + ".meta/example.ua" + ] + }, + "blurb": "Implement a simple shift cipher like Caesar and a more secure substitution cipher.", + "source": "Substitution Cipher at Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Substitution_cipher" +} diff --git a/exercises/practice/simple-cipher/.meta/example.ua b/exercises/practice/simple-cipher/.meta/example.ua new file mode 100644 index 0000000..c7ee96e --- /dev/null +++ b/exercises/practice/simple-cipher/.meta/example.ua @@ -0,0 +1,4 @@ +Translate! ← ⍜∩(-@a)(◿26 ^0)↯△,↙↧◡∩⧻ +Encode ← Translate!+ +Decode ← Translate!- +RandomKey ← +@a ⌊×26 [⍥⚂100] diff --git a/exercises/practice/simple-cipher/.meta/tests.toml b/exercises/practice/simple-cipher/.meta/tests.toml new file mode 100644 index 0000000..77e6571 --- /dev/null +++ b/exercises/practice/simple-cipher/.meta/tests.toml @@ -0,0 +1,46 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[b8bdfbe1-bea3-41bb-a999-b41403f2b15d] +description = "Random key cipher -> Can encode" + +[3dff7f36-75db-46b4-ab70-644b3f38b81c] +description = "Random key cipher -> Can decode" + +[8143c684-6df6-46ba-bd1f-dea8fcb5d265] +description = "Random key cipher -> Is reversible. I.e., if you apply decode in a encoded result, you must see the same plaintext encode parameter as a result of the decode method" + +[defc0050-e87d-4840-85e4-51a1ab9dd6aa] +description = "Random key cipher -> Key is made only of lowercase letters" + +[565e5158-5b3b-41dd-b99d-33b9f413c39f] +description = "Substitution cipher -> Can encode" + +[d44e4f6a-b8af-4e90-9d08-fd407e31e67b] +description = "Substitution cipher -> Can decode" + +[70a16473-7339-43df-902d-93408c69e9d1] +description = "Substitution cipher -> Is reversible. I.e., if you apply decode in a encoded result, you must see the same plaintext encode parameter as a result of the decode method" + +[69a1458b-92a6-433a-a02d-7beac3ea91f9] +description = "Substitution cipher -> Can double shift encode" + +[21d207c1-98de-40aa-994f-86197ae230fb] +description = "Substitution cipher -> Can wrap on encode" + +[a3d7a4d7-24a9-4de6-bdc4-a6614ced0cb3] +description = "Substitution cipher -> Can wrap on decode" + +[e31c9b8c-8eb6-45c9-a4b5-8344a36b9641] +description = "Substitution cipher -> Can encode messages longer than the key" + +[93cfaae0-17da-4627-9a04-d6d1e1be52e3] +description = "Substitution cipher -> Can decode messages longer than the key" diff --git a/exercises/practice/simple-cipher/simple-cipher.ua b/exercises/practice/simple-cipher/simple-cipher.ua new file mode 100644 index 0000000..f6e5987 --- /dev/null +++ b/exercises/practice/simple-cipher/simple-cipher.ua @@ -0,0 +1,11 @@ +# Encode plaintext using the Caesar Cipher +# Ciphertext ? Key Plaintext +Encode ← |2 ⊙(⍤ "Please implement Encode") + +# Decode plaintext using the Caesar Cipher +# Plaintext ? Key Ciphertext +Decode ← |2 ⊙(⍤ "Please implement Decode") + +# Generate a random key for the Caesar Cipher +# Key +RandomKey ← ⍤"Please implement RandomKey" 0 0 diff --git a/exercises/practice/simple-cipher/tests.ua b/exercises/practice/simple-cipher/tests.ua new file mode 100644 index 0000000..3574bdc --- /dev/null +++ b/exercises/practice/simple-cipher/tests.ua @@ -0,0 +1,41 @@ +~ "simple-cipher.ua" ~ Decode Encode RandomKey + +# Substitution cipher - Can encode +⍤⤙≍ "abcdefghij" Encode "abcdefghij" "aaaaaaaaaa" + +# Substitution cipher - Can decode +⍤⤙≍ "aaaaaaaaaa" Decode "abcdefghij" "abcdefghij" + +# Substitution cipher - Is reversible. I.e., if you apply decode in a encoded result, you must see the same plaintext encode parameter as a result of the decode method +Key ← "abcdefghij" +⍤⤙≍ "abcdefghij" Decode Key Encode "abcdefghij" Key + +# Substitution cipher - Can double shift encode +⍤⤙≍ "qayaeaagaciai" Encode "iamapandabear" "iamapandabear" + +# Substitution cipher - Can wrap on encode +⍤⤙≍ "zabcdefghi" Encode "abcdefghij" "zzzzzzzzzz" + +# Substitution cipher - Can wrap on decode +⍤⤙≍ "zzzzzzzzzz" Decode "abcdefghij" "zabcdefghi" + +# Substitution cipher - Can encode messages longer than the key +⍤⤙≍ "iboaqcnecbfcr" Encode "abc" "iamapandabear" + +# Substitution cipher - Can decode messages longer than the key +⍤⤙≍ "iamapandabear" Decode "abc" "iboaqcnecbfcr" + +# Random key cipher - Can encode +Key ← RandomKey() +⍤⤙≍ ↙ 10 Key Encode Key "aaaaaaaaaa" + +# Random key cipher - Can decode +Key ← RandomKey() +⍤⤙≍ "aaaaaaaaaa" Decode ⟜(↙10) Key + +# Random key cipher - Is reversible. I.e., if you apply decode in a encoded result, you must see the same plaintext encode parameter as a result of the decode method +Key ← RandomKey() +⍤⤙≍ "abcdefghij" Decode Key Encode Key "abcdefghij" + +# Random key cipher - Key is made only of lowercase letters +⍤⤙≍ 1 ±⧻♭ regex "^[a-z]{100,}$" RandomKey()