mirror of
https://github.com/Smaug123/ClassicalCiphers.jl
synced 2025-10-05 01:18:48 +00:00
Refined docstrings and added documentation
This commit is contained in:
106
.github/workflows/CI.yml
vendored
Normal file
106
.github/workflows/CI.yml
vendored
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
name: CI
|
||||||
|
# Run on master, tags, or any pull request
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 2 * * *' # Daily at 2 AM UTC (8 PM CST)
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
tags: ["*"]
|
||||||
|
pull_request:
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
version:
|
||||||
|
- "1.0" # LTS
|
||||||
|
- "1.1"
|
||||||
|
- "1.5"
|
||||||
|
- "nightly"
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- macOS-latest
|
||||||
|
- windows-latest
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- x86
|
||||||
|
exclude:
|
||||||
|
# Test 32-bit only on Linux
|
||||||
|
- os: macOS-latest
|
||||||
|
arch: x86
|
||||||
|
- os: windows-latest
|
||||||
|
arch: x86
|
||||||
|
include:
|
||||||
|
# Add a 1.3 job because that's what Invenia actually uses
|
||||||
|
- os: ubuntu-latest
|
||||||
|
version: 1.3
|
||||||
|
arch: x64
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: julia-actions/setup-julia@v1
|
||||||
|
with:
|
||||||
|
version: ${{ matrix.version }}
|
||||||
|
arch: ${{ matrix.arch }}
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
env:
|
||||||
|
cache-name: cache-artifacts
|
||||||
|
with:
|
||||||
|
path: ~/.julia/artifacts
|
||||||
|
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-test-${{ env.cache-name }}-
|
||||||
|
${{ runner.os }}-test-
|
||||||
|
${{ runner.os }}-
|
||||||
|
- uses: julia-actions/julia-buildpkg@latest
|
||||||
|
- run: |
|
||||||
|
git config --global user.name Tester
|
||||||
|
git config --global user.email te@st.er
|
||||||
|
- uses: julia-actions/julia-runtest@latest
|
||||||
|
- uses: julia-actions/julia-processcoverage@v1
|
||||||
|
- uses: codecov/codecov-action@v1
|
||||||
|
with:
|
||||||
|
file: lcov.info
|
||||||
|
|
||||||
|
slack:
|
||||||
|
name: Notify Slack Failure
|
||||||
|
needs: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event == 'schedule'
|
||||||
|
steps:
|
||||||
|
- uses: technote-space/workflow-conclusion-action@v2
|
||||||
|
- uses: voxmedia/github-action-slack-notify-build@v1
|
||||||
|
if: env.WORKFLOW_CONCLUSION == 'failure'
|
||||||
|
with:
|
||||||
|
channel: nightly-dev
|
||||||
|
status: FAILED
|
||||||
|
color: danger
|
||||||
|
env:
|
||||||
|
SLACK_BOT_TOKEN: ${{ secrets.DEV_SLACK_BOT_TOKEN }}
|
||||||
|
|
||||||
|
docs:
|
||||||
|
name: Documentation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: julia-actions/setup-julia@v1
|
||||||
|
with:
|
||||||
|
version: '1'
|
||||||
|
- run: |
|
||||||
|
git config --global user.name name
|
||||||
|
git config --global user.email email
|
||||||
|
git config --global github.user username
|
||||||
|
- run: |
|
||||||
|
julia --project=docs -e '
|
||||||
|
using Pkg;
|
||||||
|
Pkg.develop(PackageSpec(path=pwd()));
|
||||||
|
Pkg.instantiate();'
|
||||||
|
- run: |
|
||||||
|
julia --project=docs -e '
|
||||||
|
using Documenter: doctest
|
||||||
|
using PkgTemplates
|
||||||
|
doctest(PkgTemplates)'
|
||||||
|
- run: julia --project=docs docs/make.jl
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
16
.github/workflows/CompatHelper.yml
vendored
Normal file
16
.github/workflows/CompatHelper.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: CompatHelper
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: 0 0 * * *
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
CompatHelper:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Pkg.add("CompatHelper")
|
||||||
|
run: julia -e 'using Pkg; Pkg.add("CompatHelper")'
|
||||||
|
- name: CompatHelper.main()
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }}
|
||||||
|
run: julia -e 'using CompatHelper; CompatHelper.main()'
|
13
.github/workflows/TagBot.yml
vendored
Normal file
13
.github/workflows/TagBot.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
name: TagBot
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: 0 0 * * *
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
TagBot:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: JuliaRegistries/TagBot@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
ssh: ${{ secrets.DOCUMENTER_KEY }}
|
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,5 +1,27 @@
|
|||||||
|
# Files generated by invoking Julia with --code-coverage
|
||||||
*.jl.cov
|
*.jl.cov
|
||||||
*.jl.*.cov
|
*.jl.*.cov
|
||||||
|
|
||||||
|
# Files generated by invoking Julia with --track-allocation
|
||||||
*.jl.mem
|
*.jl.mem
|
||||||
|
|
||||||
|
# System-specific files and directories generated by the BinaryProvider and BinDeps packages
|
||||||
|
# They contain absolute paths specific to the host computer, and so should not be committed
|
||||||
|
deps/deps.jl
|
||||||
|
deps/build.log
|
||||||
|
deps/downloads/
|
||||||
|
deps/usr/
|
||||||
|
deps/src/
|
||||||
|
|
||||||
|
# Build artifacts for creating documentation generated by the Documenter package
|
||||||
|
docs/build/
|
||||||
|
docs/site/
|
||||||
|
|
||||||
|
# File generated by Pkg, the package manager, based on a corresponding Project.toml
|
||||||
|
# It records a fixed state of all packages used by the project. As such, it should not be
|
||||||
|
# committed for packages, but should be committed for applications that require a static
|
||||||
|
# environment.
|
||||||
Manifest.toml
|
Manifest.toml
|
||||||
.vscode/*
|
|
||||||
|
# Silly macOS stuff
|
||||||
|
.DS_Store
|
||||||
|
21
README.md
21
README.md
@@ -1,10 +1,17 @@
|
|||||||
[](https://travis-ci.org/Smaug123/ClassicalCiphers.jl) [](https://coveralls.io/github/Smaug123/ClassicalCiphers.jl?branch=master) [](https://github.com/invenia/BlueStyle)
|
<h1 align="center">
|
||||||
|
ClassicalCiphers.jl
|
||||||
|
</h1>
|
||||||
|
|
||||||
# ClassicalCiphers
|
<!-- [](https://username.github.io/MyCoolPackage.jl/stable) -->
|
||||||
|
[](https://username.github.io/MyCoolPackage.jl/dev)
|
||||||
|
[](https://github.com/username/MyCoolPackage.jl/actions?query=workflow%3ACI)
|
||||||
|
[](https://coveralls.io/github/Smaug123/ClassicalCiphers.jl?branch=master)
|
||||||
|
[](https://github.com/invenia/BlueStyle)
|
||||||
|
|
||||||
## Main Features
|
## Main Features
|
||||||
|
|
||||||
Provides access to encryption and decryption of strings according to a variety of classical algorithms.
|
Provides access to encryption and decryption of strings according to a variety of classical algorithms. Classical ciphers were created before computers, and thus work on letters rather than bits.
|
||||||
|
|
||||||
The Solitaire cipher is included for completeness, though it is perhaps not strictly classical.
|
The Solitaire cipher is included for completeness, though it is perhaps not strictly classical.
|
||||||
|
|
||||||
## Currently Implemented
|
## Currently Implemented
|
||||||
@@ -20,6 +27,7 @@ The Solitaire cipher is included for completeness, though it is perhaps not stri
|
|||||||
* [Solitaire]
|
* [Solitaire]
|
||||||
* [Rail Fence]
|
* [Rail Fence]
|
||||||
* [Substitution]
|
* [Substitution]
|
||||||
|
* [Atbash]
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
@@ -398,6 +406,12 @@ julia> decrypt_atbash("HLNV GVCG", "abcdefghijklmnopqrstuvwxyz")
|
|||||||
"some text"
|
"some text"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Atbash
|
||||||
|
|
||||||
|
```julia
|
||||||
|
encrypt_atbash("hello this is plaintext", "abcdefghijklmnopqrstuvwxyz") == encrypt_substitution("hello this is plaintext", "abcdefghijklmnopqrstuvwxyz", "zyxwvutsrqponmlkjihgfedcba")
|
||||||
|
```
|
||||||
|
|
||||||
[Caesar]: https://en.wikipedia.org/wiki/Caesar_cipher
|
[Caesar]: https://en.wikipedia.org/wiki/Caesar_cipher
|
||||||
[Affine]: https://en.wikipedia.org/wiki/Affine_cipher
|
[Affine]: https://en.wikipedia.org/wiki/Affine_cipher
|
||||||
[Vigenère]: https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher
|
[Vigenère]: https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher
|
||||||
@@ -409,3 +423,4 @@ julia> decrypt_atbash("HLNV GVCG", "abcdefghijklmnopqrstuvwxyz")
|
|||||||
[Enigma]: https://en.wikipedia.org/wiki/Enigma_machine
|
[Enigma]: https://en.wikipedia.org/wiki/Enigma_machine
|
||||||
[Rail Fence]: https://en.wikipedia.org/wiki/Rail_fence_cipher
|
[Rail Fence]: https://en.wikipedia.org/wiki/Rail_fence_cipher
|
||||||
[Substitution]: https://en.wikipedia.org/wiki/Substitution_cipher
|
[Substitution]: https://en.wikipedia.org/wiki/Substitution_cipher
|
||||||
|
[Atbash]: https://en.wikipedia.org/wiki/Atbash
|
||||||
|
3
docs/Project.toml
Normal file
3
docs/Project.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[deps]
|
||||||
|
ClassicalCiphers = "ecf26e93-935c-5e64-9b21-bc8ac81b4723"
|
||||||
|
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
|
20
docs/make.jl
Normal file
20
docs/make.jl
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
include(joinpath(dirname(@__DIR__), "src", "ClassicalCiphers.jl"))
|
||||||
|
using Documenter, .ClassicalCiphers
|
||||||
|
|
||||||
|
Documenter.makedocs(
|
||||||
|
clean = true,
|
||||||
|
doctest = true,
|
||||||
|
modules = Module[ClassicalCiphers],
|
||||||
|
repo = "",
|
||||||
|
highlightsig = true,
|
||||||
|
sitename = "ClassicalCiphers Documentation",
|
||||||
|
expandfirst = [],
|
||||||
|
pages = [
|
||||||
|
"Index" => "index.md",
|
||||||
|
"Usage" => "usage.md"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
deploydocs(;
|
||||||
|
repo = "github.com/Smaug123/ClassicalCiphers.jl.git",
|
||||||
|
)
|
41
docs/src/index.md
Normal file
41
docs/src/index.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Home
|
||||||
|
|
||||||
|
This package provides access to encryption and decryption of strings according to a variety of classical algorithms. Classical ciphers were created before computers, and thus work on letters rather than bits.
|
||||||
|
|
||||||
|
The Solitaire cipher is included for completeness, though it is perhaps not strictly classical.
|
||||||
|
|
||||||
|
Currently implemented ciphers:
|
||||||
|
- [Caesar](https://en.wikipedia.org/wiki/Caesar_cipher)
|
||||||
|
- [Affine](https://en.wikipedia.org/wiki/Affine_cipher)
|
||||||
|
- [Monoalphabetic substitution](https://en.wikipedia.org/wiki/Substitution_cipher)
|
||||||
|
- [Vigenère](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher)
|
||||||
|
- [Portas](http://practicalcryptography.com/ciphers/porta-cipher/)
|
||||||
|
- [Hill](https://en.wikipedia.org/wiki/Hill_cipher)
|
||||||
|
- [Playfair](https://en.wikipedia.org/wiki/Playfair_cipher)
|
||||||
|
- [Enigma (M3 Army)](https://en.wikipedia.org/wiki/Enigma_machine)
|
||||||
|
- [Solitaire](https://en.wikipedia.org/wiki/Solitaire_(cipher))
|
||||||
|
- [Rail Fence](https://en.wikipedia.org/wiki/Rail_fence_cipher)
|
||||||
|
- [Substitution](https://en.wikipedia.org/wiki/Substitution_cipher)
|
||||||
|
- [Atbash](https://en.wikipedia.org/wiki/Atbash)
|
||||||
|
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
```@contents
|
||||||
|
```
|
||||||
|
|
||||||
|
```@meta
|
||||||
|
CurrentModule = ClassicalCiphers
|
||||||
|
DocTestSetup = quote
|
||||||
|
using ClassicalCiphers
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installing ClassicalCiphers.jl
|
||||||
|
```@repl
|
||||||
|
using Pkg
|
||||||
|
Pkg.add("ClassicalCiphers")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Index
|
||||||
|
```@index
|
||||||
|
```
|
5
docs/src/usage.md
Normal file
5
docs/src/usage.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
## Usage
|
||||||
|
|
||||||
|
```@autodocs
|
||||||
|
Modules = [ClassicalCiphers]
|
||||||
|
```
|
@@ -1,4 +1,8 @@
|
|||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
encrypt_affine(plaintext, mult::Integer, add::Integer; offset::Integer = 0)
|
||||||
|
```
|
||||||
|
|
||||||
Encrypts the given plaintext according to the Affine cipher.
|
Encrypts the given plaintext according to the Affine cipher.
|
||||||
The key is given as a pair of integers: first the multiplier, then
|
The key is given as a pair of integers: first the multiplier, then
|
||||||
the additive constant.
|
the additive constant.
|
||||||
@@ -7,10 +11,19 @@ The multiplier must be coprime to 26. If it is not, an error is thrown.
|
|||||||
|
|
||||||
Converts the input to uppercase, but retains symbols.
|
Converts the input to uppercase, but retains symbols.
|
||||||
|
|
||||||
Optional argument: offset=0, which specifies what number 'a' should be
|
Optional argument: `offset=0`, which specifies what number 'a' should be
|
||||||
considered as.
|
considered as.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_affine("Hello, World!", 3, 4)
|
||||||
|
"ZQLLU, SUDLN!"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_affine(plaintext, mult::T, add::T; offset::T = 0) where {T <: Integer}
|
function encrypt_affine(plaintext, mult::Integer, add::Integer; offset::Integer = 0)
|
||||||
if mult % 2 == 0 || mult % 13 == 0
|
if mult % 2 == 0 || mult % 13 == 0
|
||||||
error("Multiplier must be coprime to 26.")
|
error("Multiplier must be coprime to 26.")
|
||||||
end
|
end
|
||||||
@@ -20,6 +33,10 @@ function encrypt_affine(plaintext, mult::T, add::T; offset::T = 0) where {T <: I
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
decrypt_affine(ciphertext, mult::Integer, add::Integer; offset::Integer=0)
|
||||||
|
```
|
||||||
|
|
||||||
Decrypts the given ciphertext according to the Affine cipher.
|
Decrypts the given ciphertext according to the Affine cipher.
|
||||||
The key is given as a pair of integers: first the multiplier, then
|
The key is given as a pair of integers: first the multiplier, then
|
||||||
the additive constant.
|
the additive constant.
|
||||||
@@ -28,10 +45,19 @@ The multiplier must be coprime to 26. If it is not, an error is thrown.
|
|||||||
|
|
||||||
Converts the input to lowercase, but retains symbols.
|
Converts the input to lowercase, but retains symbols.
|
||||||
|
|
||||||
Optional argument: offset=0, which specifies what number 'a' should be
|
Optional argument: `offset=0`, which specifies what number 'a' should be
|
||||||
considered as.
|
considered as.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_affine("ZQLLU, SUDLN!", 3, 4)
|
||||||
|
"hello, world!"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function decrypt_affine(ciphertext, mult::T, add::T; offset=0) where {T <: Integer}
|
function decrypt_affine(ciphertext, mult::Integer, add::Integer; offset::Integer = 0)
|
||||||
if mult % 2 == 0 || mult % 13 == 0
|
if mult % 2 == 0 || mult % 13 == 0
|
||||||
error("Multiplier must be coprime to 26.")
|
error("Multiplier must be coprime to 26.")
|
||||||
end
|
end
|
||||||
@@ -61,15 +87,28 @@ function max_by(arr::AbstractArray, f::Function)
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
crack_affine(ciphertext; mult::Integer = 0, add::Integer = -1)
|
||||||
|
```
|
||||||
|
|
||||||
Cracks the given ciphertext according to the Affine cipher.
|
Cracks the given ciphertext according to the Affine cipher.
|
||||||
Returns ((multiplier, additive constant), decrypted string).
|
Returns `((multiplier, additive constant), decrypted string)`.
|
||||||
|
|
||||||
Converts the input to lowercase, but retains symbols.
|
Converts the input to lowercase, but retains symbols.
|
||||||
|
|
||||||
Optional arguments: mult=0, which specifies the multiplier if known;
|
Optional arguments: `mult=0`, which specifies the multiplier if known;
|
||||||
add=-1, which specifies the additive constant if known.
|
`add=-1`, which specifies the additive constant if known.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> crack_affine("ZQLLU, SUDLN!")
|
||||||
|
("hello, world!", (3, 4))
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function crack_affine(ciphertext; mult::T = 0, add::T = -1) where {T <: Integer}
|
function crack_affine(ciphertext; mult::Integer = 0, add::Integer = -1)
|
||||||
mults = mult != 0 ? Int[mult] : Int[i for i in filter(x -> (x % 2 != 0 && x % 13 != 0), 1:25)]
|
mults = mult != 0 ? Int[mult] : Int[i for i in filter(x -> (x % 2 != 0 && x % 13 != 0), 1:25)]
|
||||||
adds = add != -1 ? Int[add] : (0:25)
|
adds = add != -1 ? Int[add] : (0:25)
|
||||||
possible_keys = Iterators.product(mults, adds)
|
possible_keys = Iterators.product(mults, adds)
|
||||||
|
@@ -1,14 +1,28 @@
|
|||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
encrypt_caesar(plaintext, key::Integer)
|
||||||
|
encrypt_caesar(plaintext)
|
||||||
|
```
|
||||||
|
|
||||||
Encrypts the given plaintext according to the Caesar cipher.
|
Encrypts the given plaintext according to the Caesar cipher.
|
||||||
The key is given as an integer, being the offset of each character;
|
The key is given as an integer, being the offset of each character;
|
||||||
so encrypt_caesar("abc", 1) == "BCD".
|
so `encrypt_caesar("abc", 1) == "BCD"`.
|
||||||
|
|
||||||
Converts the input to uppercase.
|
Converts the input to uppercase, but retains symbols.
|
||||||
|
|
||||||
Traditionally, the Caesar cipher was used with a shift of 3, so this is the method it will
|
Traditionally, the Caesar cipher was used with a shift of 3, so this is the method it will
|
||||||
fall back to if only given plaintext.
|
fall back to if only given plaintext.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_caesar("Hello, World!", 3)
|
||||||
|
"KHOOR, ZRUOG!"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_caesar(plaintext, key::T) where {T <: Integer}
|
function encrypt_caesar(plaintext, key::Integer)
|
||||||
# plaintext: string; key: integer offset, so k=1 sends "a" to "b"
|
# plaintext: string; key: integer offset, so k=1 sends "a" to "b"
|
||||||
key = ((key - 1) % 26) + 1
|
key = ((key - 1) % 26) + 1
|
||||||
keystr = join(vcat(collect(Char(97 + key):'z'), collect('a':Char(97 + key - 1))))
|
keystr = join(vcat(collect(Char(97 + key):'z'), collect('a':Char(97 + key - 1))))
|
||||||
@@ -17,16 +31,30 @@ end
|
|||||||
encrypt_caesar(plaintext) = encrypt_caesar(plaintext, 3)
|
encrypt_caesar(plaintext) = encrypt_caesar(plaintext, 3)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
decrypt_caesar(ciphertext, key::Integer)
|
||||||
|
decrypt_caesar(ciphertext)
|
||||||
|
```
|
||||||
|
|
||||||
Decrypts the given ciphertext according to the Caesar cipher.
|
Decrypts the given ciphertext according to the Caesar cipher.
|
||||||
The key is given as an integer, being the offset of each character;
|
The key is given as an integer, being the offset of each character;
|
||||||
so decrypt_caesar("abcd", 1) == "zabc".
|
so `decrypt_caesar("abcd", 1) == "zabc"`.
|
||||||
|
|
||||||
Converts the input to lowercase.
|
Converts the input to lowercase, but retains symbols.
|
||||||
|
|
||||||
Traditionally, the Caesar cipher was used with a shift of 3, so this is the method it will
|
Traditionally, the Caesar cipher was used with a shift of 3, so this is the method it will
|
||||||
fall back to if only given plaintext.
|
fall back to if only given plaintext.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_caesar("Khoor, Zruog!", 3)
|
||||||
|
"hello, world!"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function decrypt_caesar(ciphertext, key::T) where {T <: Integer}
|
function decrypt_caesar(ciphertext, key::Integer)
|
||||||
# ciphertext: string; key: integer offset, so k=1 decrypts "B" as "a"
|
# ciphertext: string; key: integer offset, so k=1 decrypts "B" as "a"
|
||||||
key = ((key - 1) % 26) + 1
|
key = ((key - 1) % 26) + 1
|
||||||
return lowercase(encrypt_caesar(ciphertext, 26 - key))
|
return lowercase(encrypt_caesar(ciphertext, 26 - key))
|
||||||
@@ -34,22 +62,36 @@ end
|
|||||||
decrypt_caesar(plaintext) = decrypt_caesar(plaintext, 3)
|
decrypt_caesar(plaintext) = decrypt_caesar(plaintext, 3)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
crack_caesar(ciphertext; cleverness::Integer = 1)
|
||||||
|
```
|
||||||
|
|
||||||
Cracks the given ciphertext according to the Caesar cipher.
|
Cracks the given ciphertext according to the Caesar cipher.
|
||||||
Returns (plaintext, key::Integer), such that encrypt_caesar(plaintext, key)
|
Returns `(plaintext, key::Integer)`, such that `encrypt_caesar(plaintext, key)`
|
||||||
would return ciphertext.
|
would return ciphertext.
|
||||||
|
|
||||||
With cleverness=0, simply does the shift that maximises e's frequency.
|
With `cleverness=0`, simply does the shift that maximises e's frequency.
|
||||||
With cleverness=1, maximises the string's total fitness.
|
With `cleverness=1`, maximises the string's total fitness.
|
||||||
|
|
||||||
Converts the input to lowercase.
|
Converts the input to lowercase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> crack_caesar("Khoor, Zruog!")
|
||||||
|
("hello, world!", 3)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function crack_caesar(ciphertext; cleverness::T = 1) where {T <: Integer}
|
function crack_caesar(ciphertext; cleverness::Integer = 1)
|
||||||
texts = Tuple{String, Int}[(decrypt_caesar(ciphertext,key), key) for key in 0:25]
|
texts = Tuple{String, Int}[(decrypt_caesar(ciphertext,key), key) for key in 0:25]
|
||||||
|
|
||||||
if cleverness == 1
|
if cleverness == 1
|
||||||
texts = sort(texts, by = (x -> string_fitness(first(x))))
|
texts = sort(texts, by = (x -> string_fitness(first(x))))
|
||||||
else
|
else
|
||||||
texts = sort(texts, by = (x -> length(collect(filter(i -> (i == 'e'), first(x))))))
|
texts = sort(texts, by = (x -> length(collect(filter(i -> (i == 'e'), first(x))))))
|
||||||
end
|
end
|
||||||
|
|
||||||
return texts[end]
|
return last(texts)
|
||||||
end
|
end
|
||||||
|
@@ -3,7 +3,7 @@ function letters_only(text::AbstractString)
|
|||||||
return filter(x -> ('A' <= x <= 'Z' || 'a' <= x <= 'z'), text)
|
return filter(x -> ('A' <= x <= 'Z' || 'a' <= x <= 'z'), text)
|
||||||
end
|
end
|
||||||
|
|
||||||
function rotate_right(arr::AbstractVector, n::T) where {T <: Integer}
|
function rotate_right(arr::AbstractVector, n::Integer)
|
||||||
# implementation of the Mathematica function rotate_right - or you could try circshift()?
|
# implementation of the Mathematica function rotate_right - or you could try circshift()?
|
||||||
ans = copy(arr)
|
ans = copy(arr)
|
||||||
for i in 1:length(arr)
|
for i in 1:length(arr)
|
||||||
@@ -13,7 +13,7 @@ function rotate_right(arr::AbstractVector, n::T) where {T <: Integer}
|
|||||||
return ans
|
return ans
|
||||||
end
|
end
|
||||||
|
|
||||||
function rotate_left(arr::AbstractVector, n::T) where {T <: Integer}
|
function rotate_left(arr::AbstractVector, n::Integer)
|
||||||
# implementation of the Mathematica function rotate_left
|
# implementation of the Mathematica function rotate_left
|
||||||
ans = copy(arr)
|
ans = copy(arr)
|
||||||
for i in 1:length(arr)
|
for i in 1:length(arr)
|
||||||
@@ -23,9 +23,9 @@ function rotate_left(arr::AbstractVector, n::T) where {T <: Integer}
|
|||||||
return ans
|
return ans
|
||||||
end
|
end
|
||||||
|
|
||||||
rotate_left_str(st::AbstractString, n::T) where {T <: Integer} =
|
rotate_left_str(st::AbstractString, n::Integer) =
|
||||||
join(rotate_left(collect(st), n))
|
join(rotate_left(collect(st), n))
|
||||||
rotate_right_str(st::AbstractString, n::T) where {T <: Integer} =
|
rotate_right_str(st::AbstractString, n::Integer) =
|
||||||
join(rotate_right(collect(st), n))
|
join(rotate_right(collect(st), n))
|
||||||
|
|
||||||
function split_by(arr::AbstractVector, func::Function)
|
function split_by(arr::AbstractVector, func::Function)
|
||||||
@@ -85,8 +85,12 @@ function string_fitness(input::AbstractString; alreadystripped::Bool = false)
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Finds the frequencies of all characters in the input string, returning a Dict
|
```julia
|
||||||
of 'a' => 4, for instance. Uppercase characters are considered distinct from lowercase.
|
frequencies(input::AbstractString)
|
||||||
|
```
|
||||||
|
|
||||||
|
Finds the frequencies of all characters in the input string, returning a `Dict`
|
||||||
|
of `'a' => 4`, for instance. Uppercase characters are considered distinct from lowercase.
|
||||||
"""
|
"""
|
||||||
function frequencies(input::AbstractString)
|
function frequencies(input::AbstractString)
|
||||||
ans = Dict{Char, Int}()
|
ans = Dict{Char, Int}()
|
||||||
@@ -102,6 +106,10 @@ function frequencies(input::AbstractString)
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
index_of_coincidence(input::AbstractString)
|
||||||
|
```
|
||||||
|
|
||||||
Finds the index of coincidence of the input string. Uppercase characters are considered to be
|
Finds the index of coincidence of the input string. Uppercase characters are considered to be
|
||||||
equal to their lowercase counterparts.
|
equal to their lowercase counterparts.
|
||||||
"""
|
"""
|
||||||
|
@@ -55,30 +55,57 @@ function parse_reflector(reflector::AbstractString)
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
function encrypt_enigma(plaintext,
|
||||||
|
rotors::Array{Integer, 1}, key::AbstractString;
|
||||||
|
reflector_id='B', ring::AbstractString = "AAA",
|
||||||
|
stecker = Tuple{Char, Char}[],
|
||||||
|
skip_stecker_check = false)
|
||||||
|
```
|
||||||
|
|
||||||
Encrypts the given plaintext according to the Enigma (M3, army version).
|
Encrypts the given plaintext according to the Enigma (M3, army version).
|
||||||
|
|
||||||
Arguments are in the order: plaintext, stecker, rotors, ring, key.
|
Arguments are in the order: `plaintext, stecker, rotors, ring, key.`
|
||||||
|
|
||||||
Plaintext is a string; punctuation is stripped out and it is made lowercase.
|
Plaintext is a string; punctuation is stripped out and it is made lowercase.
|
||||||
Rotors is an array - for example, [1,2,3] - being the order of the rotors.
|
Rotors is an array - for example, `[1,2,3]` - being the order of the rotors.
|
||||||
Each entry should be a distinct integer between 1 and 5 inclusive.
|
Each entry should be a distinct integer between 1 and 5 inclusive.
|
||||||
Key is a string of three letters, indicating the starting positions of the rotors.
|
Key is a string of three letters, indicating the starting positions of the rotors.
|
||||||
|
|
||||||
Optional:
|
Optional:
|
||||||
reflector_id='B', which sets whether to use reflector A, B or C.
|
* `reflector_id='B'`, which sets whether to use reflector A, B or C.
|
||||||
Can also be specified as a 26-char string.
|
Can also be specified as a 26-char string.
|
||||||
Stecker is either an array - for example, [('A','B'), ('D', 'E')] specifying
|
* Stecker is either an array - for example,` [('A','B'), ('D', 'E')]` specifying
|
||||||
that A, B are swapped and D, E are swapped - or a string ("ABDE" accomplishing
|
that A, B are swapped and D, E are swapped - or a string ("ABDE" accomplishing
|
||||||
the same thing). No letter may appear more than once.
|
the same thing). No letter may appear more than once.
|
||||||
Ring is a string - for example, "AAA" - being the offset applied to each rotor.
|
* Ring is a string - for example, "AAA" - being the offset applied to each rotor.
|
||||||
"AAA", for example, signifies no offset. The string must be three letters.
|
"AAA", for example, signifies no offset. The string must be three letters.
|
||||||
skip_stecker_check=false, which when `true` skips validation of stecker settings.
|
* `skip_stecker_check=false`, which when `true` skips validation of stecker settings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_enigma("AAA", [1,2,3], "ABC")
|
||||||
|
"CXT"
|
||||||
|
|
||||||
|
julia> encrypt_enigma("AAA", [1,2,3], "ABC", ring="AAA", reflector_id='B', stecker="") # synonymous with above
|
||||||
|
"CXT"
|
||||||
|
|
||||||
|
julia> encrypt_enigma("AAA", [1,2,3], "ABC", ring="AAA", reflector_id="YRUHQSLDPXNGOKMIEBFZCWVJAT", stecker="") # synonymous with above
|
||||||
|
"CXT"
|
||||||
|
|
||||||
|
julia> encrypt_enigma("AAA", [1,2,3], "ABC", ring="AAA", reflector_id='B', stecker=Tuple{Char, Char}[]) # synonymous with above
|
||||||
|
"CXT"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_enigma(plaintext,
|
function encrypt_enigma(plaintext,
|
||||||
rotors::Array{T, 1}, key::AbstractString;
|
rotors::Array{T, 1}, key::AbstractString;
|
||||||
reflector_id='B', ring::AbstractString = "AAA",
|
reflector_id='B', ring::AbstractString = "AAA",
|
||||||
stecker = Tuple{Char, Char}[],
|
stecker = Tuple{Char, Char}[],
|
||||||
skip_stecker_check = false) where {T <: Integer}
|
skip_stecker_check = false) where T <: Integer
|
||||||
|
|
||||||
parsed_stecker = parse_stecker(stecker)
|
parsed_stecker = parse_stecker(stecker)
|
||||||
# validate stecker settings
|
# validate stecker settings
|
||||||
if !skip_stecker_check
|
if !skip_stecker_check
|
||||||
@@ -243,5 +270,8 @@ function encrypt_enigma(plaintext,
|
|||||||
return uppercase(String(take!(ans)))
|
return uppercase(String(take!(ans)))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
See `encrypt_enigma` as this function uses identical arguments.
|
||||||
|
"""
|
||||||
decrypt_enigma(args1...; args2...) =
|
decrypt_enigma(args1...; args2...) =
|
||||||
lowercase(encrypt_enigma(args1...; args2...))
|
lowercase(encrypt_enigma(args1...; args2...))
|
||||||
|
45
src/hill.jl
45
src/hill.jl
@@ -1,8 +1,12 @@
|
|||||||
using LinearAlgebra
|
using LinearAlgebra
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
encrypt_hill(plaintext::AbstractString, key::AbstractArray{Integer, 2})
|
||||||
|
```
|
||||||
|
|
||||||
Encrypts the given plaintext according to the Hill cipher.
|
Encrypts the given plaintext according to the Hill cipher.
|
||||||
The key may be given as a matrix (that is, two-dimensional Array{Int}) or as a string.
|
The key may be given as a matrix (that is, two-dimensional `Array{Int}`) or as a string.
|
||||||
|
|
||||||
If the key is given as a string, the string is converted to uppercase before use, and
|
If the key is given as a string, the string is converted to uppercase before use, and
|
||||||
symbols are removed. It is assumed to be of square integer length, and the matrix entries
|
symbols are removed. It is assumed to be of square integer length, and the matrix entries
|
||||||
@@ -11,6 +15,21 @@ to bottom-left to bottom-right. If the string is not of square integer length, a
|
|||||||
is thrown.
|
is thrown.
|
||||||
|
|
||||||
The matrix must be invertible modulo 26. If it is not, an error is thrown.
|
The matrix must be invertible modulo 26. If it is not, an error is thrown.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_hill("Hello, World!", [1 2; 5 7]) # Encrypt the text "Hello, World!" with a Hill key of matrix `[1 2; 5 7]`
|
||||||
|
"PHHRGUWQRV"
|
||||||
|
|
||||||
|
julia> encrypt_hill("Hello, World!", "bcfh")
|
||||||
|
"PLHCGQWHRY"
|
||||||
|
|
||||||
|
julia> encrypt_hill("Hello", "bcfh") # If the plaintext-length is not a multiple of the dimension of the key matrix, it is padded with X
|
||||||
|
"PLHCIX"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_hill(plaintext::AbstractString, key::AbstractArray{T, 2}) where {T <: Integer}
|
function encrypt_hill(plaintext::AbstractString, key::AbstractArray{T, 2}) where {T <: Integer}
|
||||||
if round(Integer, det(key)) % 26 == 0
|
if round(Integer, det(key)) % 26 == 0
|
||||||
@@ -60,6 +79,10 @@ function minor(mat::AbstractArray{T, 2}, i::K, j::K) where {T <: Integer, K <: I
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
adjugate(mat::AbstractArray{Integer, 2})
|
||||||
|
```
|
||||||
|
|
||||||
Computes the adjugate matrix for given matrix.
|
Computes the adjugate matrix for given matrix.
|
||||||
"""
|
"""
|
||||||
function adjugate(mat::AbstractArray{T, 2}) where {T <: Integer}
|
function adjugate(mat::AbstractArray{T, 2}) where {T <: Integer}
|
||||||
@@ -68,6 +91,26 @@ function adjugate(mat::AbstractArray{T, 2}) where {T <: Integer}
|
|||||||
return Array{Integer, 2}(transpose(ans))
|
return Array{Integer, 2}(transpose(ans))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
```julia
|
||||||
|
decrypt_hill(ciphertext, key::AbstractArray{T, 2}) where {T <: Integer}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_hill("PLHCGQWHRY", [1 2; 5 7]) # Decrypt the text "PLHCGQWHRY" with key of `[1 2; 5 7]`
|
||||||
|
"helloworld"
|
||||||
|
|
||||||
|
julia> decrypt_hill("PLHCGQWHRY", "bcfh")
|
||||||
|
"helloworld"
|
||||||
|
|
||||||
|
julia> decrypt_hill("PLHCIX", "bcfh") # If the plaintext-length is not a multiple of the dimension of the key matrix, it is padded with X
|
||||||
|
"hellox"
|
||||||
|
```
|
||||||
|
"""
|
||||||
function decrypt_hill(ciphertext, key::AbstractArray{T, 2}) where {T <: Integer}
|
function decrypt_hill(ciphertext, key::AbstractArray{T, 2}) where {T <: Integer}
|
||||||
if ndims(key) != 2
|
if ndims(key) != 2
|
||||||
error("Key must be a two-dimensional matrix.")
|
error("Key must be a two-dimensional matrix.")
|
||||||
|
@@ -3,15 +3,31 @@ function keystr_to_dict(keystr::AbstractString)
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
encrypt_monoalphabetic(plaintext, key::Dict{Char, Char})
|
||||||
|
```
|
||||||
|
|
||||||
Encrypts the given plaintext according to the monoalphabetic substitution cipher.
|
Encrypts the given plaintext according to the monoalphabetic substitution cipher.
|
||||||
The key may be given as a Dict of replacements {'a' => 'b', 'c' => 'd'}, etc,
|
The key may be given as a Dict of replacements `Dict('a' => 'b', 'c' => 'd')`, etc,
|
||||||
or as a 26-length string "keystringbcdfhjlmopqruvwxz", which is shorthand for
|
or as a 26-length string "keystringbcdfhjlmopqruvwxz", which is shorthand for
|
||||||
{'a' => 'k', 'e' => 'b', …}
|
`Dict('a' => 'k', 'e' => 'b', ...)`
|
||||||
|
|
||||||
If the key is given as a string, it is assumed that each character occurs only
|
If the key is given as a string, it is assumed that each character occurs only
|
||||||
once, and the string is converted to lowercase.
|
once, and the string is converted to lowercase.
|
||||||
If the key is given as a Dict, the only substitutions made are those in the Dict;
|
If the key is given as a Dict, the only substitutions made are those in the Dict;
|
||||||
in particular, the string is not converted to lowercase automatically.
|
in particular, the string is not converted to lowercase automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_monoalphabetic("Hello, World!", "DEFGHIJKLMNOPQRSTUVWXYZABC")
|
||||||
|
"KHOOR, ZRUOG!"
|
||||||
|
|
||||||
|
julia> encrypt_monoalphabetic("aBcbDd", Dict{Char, Char}('a' => '5', 'B' => '@', 'b' => 'o'))
|
||||||
|
"5@coDd"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_monoalphabetic(plaintext, key::Dict{Char, Char})
|
function encrypt_monoalphabetic(plaintext, key::Dict{Char, Char})
|
||||||
# plaintext: string; key: dictionary of {'a' => 'b'}, etc, for replacing 'a' with 'b'
|
# plaintext: string; key: dictionary of {'a' => 'b'}, etc, for replacing 'a' with 'b'
|
||||||
@@ -19,21 +35,34 @@ function encrypt_monoalphabetic(plaintext, key::Dict{Char, Char})
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
decrypt_monoalphabetic(ciphertext, key::Dict{Char, Char})
|
||||||
|
```
|
||||||
|
|
||||||
Decrypts the given ciphertext according to the monoalphabetic substitution cipher.
|
Decrypts the given ciphertext according to the monoalphabetic substitution cipher.
|
||||||
The key may be given as a Dict of replacements {'a' => 'b', 'c' => 'd'}, etc,
|
The key may be given as a Dict of replacements `Dict('a' => 'b', 'c' => 'd')`, etc,
|
||||||
or as a 26-length string "keystringbcdfhjlmopqruvwxz", which is shorthand for
|
or as a 26-length string "keystringbcdfhjlmopqruvwxz", which is shorthand for
|
||||||
{'a' => 'k', 'e' => 'b', …}
|
`Dict('a' => 'k', 'e' => 'b', ...)`
|
||||||
|
|
||||||
If the key is given as a string, it is assumed that each character occurs only
|
If the key is given as a string, it is assumed that each character occurs only
|
||||||
once, and the string is converted to lowercase.
|
once, and the string is converted to lowercase.
|
||||||
If the key is given as a Dict, the only substitutions made are those in the Dict;
|
If the key is given as a Dict, the only substitutions made are those in the Dict;
|
||||||
in particular, the string is not converted to lowercase automatically.
|
in particular, the string is not converted to lowercase automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_monoalphabetic("Khoor, Zruog!", "DEFGHIJKLMNOPQRSTUVWXYZABC")
|
||||||
|
"hello, world!"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function decrypt_monoalphabetic(ciphertext, key::Dict{Char, Char})
|
function decrypt_monoalphabetic(ciphertext, key::Dict{Char, Char})
|
||||||
# ciphertext: string; key: dictionary of {'a' => 'b'}, etc, where the plaintext 'a' was
|
# ciphertext: string; key: dictionary of {'a' => 'b'}, etc, where the plaintext 'a' was
|
||||||
# replaced by ciphertext 'b'. No character should appear more than once
|
# replaced by ciphertext 'b'. No character should appear more than once
|
||||||
# as a value in {key}.
|
# as a value in {key}.
|
||||||
return encrypt_monoalphabetic(ciphertext, Dict{Char, Char}([reverse(a) for a in key]))
|
return encrypt_monoalphabetic(ciphertext, Dict{Char, Char}(reverse(a) for a in key))
|
||||||
end
|
end
|
||||||
|
|
||||||
function encrypt_monoalphabetic(plaintext, key::AbstractString)
|
function encrypt_monoalphabetic(plaintext, key::AbstractString)
|
||||||
@@ -47,7 +76,7 @@ function decrypt_monoalphabetic(ciphertext, key::AbstractString)
|
|||||||
# working in lowercase; key is assumed only to have each element appearing once
|
# working in lowercase; key is assumed only to have each element appearing once
|
||||||
# and to be in lowercase
|
# and to be in lowercase
|
||||||
# so decrypt_monoalphabetic("cb", "cbade…") is "ab"
|
# so decrypt_monoalphabetic("cb", "cbade…") is "ab"
|
||||||
dict = Dict{Char, Char}(a => Char(96 + findfirst(i -> i == a, lowercase(key))) for a in lowercase(key))
|
dict = Dict{Char, Char}(a => Char(96 + findfirst(==(a), lowercase(key))) for a in lowercase(key))
|
||||||
return encrypt_monoalphabetic(lowercase(ciphertext), dict)
|
return encrypt_monoalphabetic(lowercase(ciphertext), dict)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -56,6 +85,10 @@ end
|
|||||||
# The method we use for cracking is simulated annealing.
|
# The method we use for cracking is simulated annealing.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
swap_two(str)
|
||||||
|
```
|
||||||
|
|
||||||
swap_two(string) swaps two of the characters of the input string, at random.
|
swap_two(string) swaps two of the characters of the input string, at random.
|
||||||
The characters are guaranteed to be at different positions, though "aa" would be
|
The characters are guaranteed to be at different positions, though "aa" would be
|
||||||
'swapped' to "aa".
|
'swapped' to "aa".
|
||||||
@@ -70,34 +103,55 @@ function swap_two(str)
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
crack_monoalphabetic(
|
||||||
|
ciphertext;
|
||||||
|
starting_key::AbstractString = "",
|
||||||
|
min_temp::AbstractFloat = 0.0001,
|
||||||
|
temp_factor::AbstractFloat = 0.97,
|
||||||
|
acceptance_prob::AbstractFloat = ((e,ep,t) -> ep > e ? 1. : exp(-(e-ep)/t)),
|
||||||
|
chatty::Integer = 0,
|
||||||
|
rounds::Integer = 1
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
crack_monoalphabetic cracks the given ciphertext which was encrypted by the monoalphabetic
|
crack_monoalphabetic cracks the given ciphertext which was encrypted by the monoalphabetic
|
||||||
substitution cipher.
|
substitution cipher.
|
||||||
|
|
||||||
Returns (the derived key, decrypted plaintext).
|
Returns `(the derived key, decrypted plaintext)`.
|
||||||
|
|
||||||
Possible arguments include:
|
The various optional arguments to `crack_monoalphabetic` are:
|
||||||
starting_key="", which when specified (for example, as "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
|
* `starting_key=""`, which when specified (for example, as "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
|
||||||
starts the simulation at the given key. The default causes it to start with the most
|
starts the simulation at the given key. The default causes it to start with the most
|
||||||
common characters being decrypted to the most common English characters.
|
common characters being decrypted to the most common English characters.
|
||||||
min_temp=0.0001, which is the temperature at which we stop the simulation.
|
* `min_temp=0.0001`, which is the temperature at which we stop the simulation.
|
||||||
temp_factor=0.97, which is the factor by which the temperature decreases each step.
|
* `temp_factor=0.97`, which is the factor by which the temperature decreases each step.
|
||||||
chatty=0, which can be set to 1 to print whenever the key is updated, or 2 to print
|
* `chatty=0`, which can be set to 1 to print whenever the key is updated, or 2 to print
|
||||||
whenever any new key is considered.
|
whenever any new key is considered.
|
||||||
rounds=1, which sets the number of repetitions we perform. Each round starts with the
|
* `rounds=1`, which sets the number of repetitions we perform. Each round starts with the
|
||||||
best key we've found so far.
|
best key we've found so far.
|
||||||
acceptance_prob=((e, ep, t) -> ep>e ? 1 : exp(-(e-ep)/t)), which is the probability
|
* `acceptance_prob=((e, ep, t) -> ep>e ? 1 : exp(-(e-ep)/t))`, which is the probability
|
||||||
with which we accept new key of fitness ep, given that the current key has fitness e,
|
with which we accept new key of fitness ep, given that the current key has fitness e,
|
||||||
at temperature t.
|
at temperature t.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> crack_monoalphabetic(str, chatty=0, rounds=10)
|
||||||
|
(decrypted_string, key)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function crack_monoalphabetic(
|
function crack_monoalphabetic(
|
||||||
ciphertext;
|
ciphertext;
|
||||||
starting_key::AbstractString = "",
|
starting_key::AbstractString = "",
|
||||||
min_temp::F = 0.0001,
|
min_temp::AbstractFloat = 0.0001,
|
||||||
temp_factor::F = 0.97,
|
temp_factor::AbstractFloat = 0.97,
|
||||||
acceptance_prob::F = ((e,ep,t) -> ep > e ? 1. : exp(-(e-ep)/t)),
|
acceptance_prob::AbstractFloat = ((e,ep,t) -> ep > e ? 1. : exp(-(e-ep)/t)),
|
||||||
chatty::T = 0,
|
chatty::Integer = 0,
|
||||||
rounds::T = 1
|
rounds::Integer = 1
|
||||||
) where {T <: Integer, F <: AbstractFloat}
|
)
|
||||||
|
|
||||||
if isempty(starting_key)
|
if isempty(starting_key)
|
||||||
# most common letters
|
# most common letters
|
||||||
|
@@ -1,11 +1,23 @@
|
|||||||
|
"""
|
||||||
|
```julia
|
||||||
AbstractPair{F, S} = Union{Tuple{F, S}, Pair{F, S}}
|
AbstractPair{F, S} = Union{Tuple{F, S}, Pair{F, S}}
|
||||||
|
```
|
||||||
|
|
||||||
|
A simple wrapper for a `Pair`, or a `Tuple` representing a pair of objects.
|
||||||
|
"""
|
||||||
|
AbstractPair{F, S} = Union{Tuple{F, S}, Pair{F, S}}
|
||||||
|
|
||||||
parse_abstract_pair(P::AbstractPair) =
|
parse_abstract_pair(P::AbstractPair) =
|
||||||
P isa Tuple{Char, Char} ? Dict(reverse(Pair(P...))) : Dict(reverse(P))
|
P isa Tuple{Char, Char} ? Dict(reverse(Pair(P...))) : Dict(reverse(P))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
playfair_key_to_square(key::AbstractString, replacement::AbstractPair{Char, Char})
|
||||||
|
```
|
||||||
|
|
||||||
Converts the given key-string to a Playfair key square.
|
Converts the given key-string to a Playfair key square.
|
||||||
|
|
||||||
Parameter `replacement` is a pair, such as ('I', 'J') or 'I' => 'J', containing
|
Parameter `replacement` is a pair, such as `('I', 'J')` or `'I' => 'J'`, containing
|
||||||
the two letters which are combined. Only the first of these letters will
|
the two letters which are combined. Only the first of these letters will
|
||||||
be present in the keysquare.
|
be present in the keysquare.
|
||||||
"""
|
"""
|
||||||
@@ -32,16 +44,40 @@ function encrypt_playfair(plaintext, key::AbstractString; combined::AbstractPair
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
encrypt_playfair(plaintext, key::Array{Char, 2}; stripped::Bool = false, combined::AbstractPair{Char, Char} = ('I', 'J'))
|
||||||
|
```
|
||||||
|
|
||||||
Encrypts the given plaintext according to the Playfair cipher.
|
Encrypts the given plaintext according to the Playfair cipher.
|
||||||
Throws an error if the second entry in the `combined` tuple is present in the key.
|
Throws an error if the second entry in the `combined` tuple is present in the key.
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
|
|
||||||
stripped=false. When set to true, encrypt_playfair skips
|
`stripped=false`. When set to true, `encrypt_playfair` skips
|
||||||
converting the plaintext to uppercase, removing punctuation, and
|
converting the plaintext to uppercase, removing punctuation, and
|
||||||
combining characters which are to be combined in the key.
|
combining characters which are to be combined in the key.
|
||||||
combined=('I', 'J'), marks the characters which are to be combined in the text.
|
`combined=('I', 'J')`, marks the characters which are to be combined in the text.
|
||||||
Only the first of these two may be present in the output of encrypt_playfair.
|
Only the first of these two may be present in the output of `encrypt_playfair`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_playfair("Hello, World!", "playfair example")
|
||||||
|
"DMYRANVQCRGE"
|
||||||
|
|
||||||
|
julia> arr = ['P' 'L' 'A' 'Y' 'F'; 'I' 'R' 'E' 'X' 'M'; 'B' 'C' 'D' 'G' 'H'; 'K' 'N' 'O' 'Q' 'S'; 'T' 'U' 'V' 'W' 'Z'];
|
||||||
|
|
||||||
|
julia> encrypt_playfair("Hello, World!", arr) # Encrypt the same text using an explicitly specified keysquare
|
||||||
|
"DMYRANVQCRGE"
|
||||||
|
|
||||||
|
julia> encrypt_playfair("IJXYZA", "PLAYFIREXM", combined=('I', 'J')) # Optionally specify the two letters which are to be combined (default 'I','J')
|
||||||
|
"RMRMFWYE"
|
||||||
|
|
||||||
|
julia> encrypt_playfair("IJXYZA", "PLAYFIREXM", combined=('X', 'Z'))
|
||||||
|
"BSGXEY"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_playfair(plaintext, key::Array{Char, 2}; stripped::Bool = false, combined::AbstractPair{Char, Char} = ('I', 'J'))
|
function encrypt_playfair(plaintext, key::Array{Char, 2}; stripped::Bool = false, combined::AbstractPair{Char, Char} = ('I', 'J'))
|
||||||
if !stripped
|
if !stripped
|
||||||
@@ -125,9 +161,22 @@ function decrypt_playfair(ciphertext, key::AbstractString; combined::AbstractPai
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
decrypt_playfair(ciphertext, key::Array{Char, 2}; combined::AbstractPair{Char, Char} = ('I', 'J'))
|
||||||
|
```
|
||||||
|
|
||||||
Decrypts the given ciphertext according to the Playfair cipher.
|
Decrypts the given ciphertext according to the Playfair cipher.
|
||||||
|
|
||||||
Does not attempt to delete X's inserted as padding for double letters.
|
Does not attempt to delete X's inserted as padding for double letters.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_playfair("RMRMFWYE", "playfair example")
|
||||||
|
"ixixyzax"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function decrypt_playfair(ciphertext, key::Array{Char, 2}; combined::AbstractPair{Char, Char} = ('I', 'J'))
|
function decrypt_playfair(ciphertext, key::Array{Char, 2}; combined::AbstractPair{Char, Char} = ('I', 'J'))
|
||||||
# to obtain the decrypting keysquare, reverse every row and every column
|
# to obtain the decrypting keysquare, reverse every row and every column
|
||||||
|
@@ -1,9 +1,22 @@
|
|||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
encrypt_portas(plaintext, key_in::AbstractString)
|
||||||
|
```
|
||||||
|
|
||||||
Encrypts the given plaintext with the Portas cipher.
|
Encrypts the given plaintext with the Portas cipher.
|
||||||
|
|
||||||
The key must be given as a string, whose characters are letters.
|
The key must be given as a string, whose characters are letters.
|
||||||
|
|
||||||
Converts the text to uppercase.
|
Converts the text to uppercase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_portas("Hello, World!", "ab")
|
||||||
|
"URYYB, JBEYQ!"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_portas(plaintext, key_in::AbstractString)
|
function encrypt_portas(plaintext, key_in::AbstractString)
|
||||||
key = uppercase(letters_only(key_in))
|
key = uppercase(letters_only(key_in))
|
||||||
@@ -36,11 +49,24 @@ function encrypt_portas(plaintext, key_in::AbstractString)
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
decrypt_portas(ciphertext, key::AbstractString)
|
||||||
|
```
|
||||||
|
|
||||||
Decrypts the given ciphertext with the Portas cipher.
|
Decrypts the given ciphertext with the Portas cipher.
|
||||||
|
|
||||||
The key must be given as a string, whose characters are letters.
|
The key must be given as a string, whose characters are letters.
|
||||||
|
|
||||||
Converts the text to lowercase.
|
Converts the text to lowercase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_portas("URYYB, JBEYQ!", "ab")
|
||||||
|
"hello, world!"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function decrypt_portas(ciphertext, key::AbstractString)
|
function decrypt_portas(ciphertext, key::AbstractString)
|
||||||
return lowercase(encrypt_portas(ciphertext, key))
|
return lowercase(encrypt_portas(ciphertext, key))
|
||||||
|
@@ -8,13 +8,25 @@ function construct_railfence(input, fence::AbstractArray, n_rails::Integer)
|
|||||||
return fence
|
return fence
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
@doc raw"""
|
||||||
```julia
|
```julia
|
||||||
construct_railfence(input::AbstractString, n_rails::Integer)
|
construct_railfence(input::AbstractString, n_rails::Integer)
|
||||||
construct_railfence(input::AbstractArray{T}, n_rails::Integer) where {T <: Number}
|
construct_railfence(input::AbstractArray{T}, n_rails::Integer) where {T <: Number}
|
||||||
```
|
```
|
||||||
|
|
||||||
See https://en.wikipedia.org/wiki/Rail_fence_cipher.
|
See [`https://en.wikipedia.org/wiki/Rail_fence_cipher`](https://en.wikipedia.org/wiki/Rail_fence_cipher).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> construct_railfence("WE ARE DISCOVERED. FLEE AT ONCE", 3)
|
||||||
|
3×26 Array{Char,2}:
|
||||||
|
'W' '□' '□' '□' 'E' '□' '□' '□' 'C' '□' '□' '□' 'R' … '□' '□' 'F' '□' '□' '□' 'A' '□' '□' '□' 'C' '□'
|
||||||
|
'□' 'E' '□' 'R' '□' 'D' '□' 'S' '□' 'O' '□' 'E' '□' '□' '.' '□' 'L' '□' 'E' '□' 'T' '□' 'N' '□' 'E'
|
||||||
|
'□' '□' 'A' '□' '□' '□' 'I' '□' '□' '□' 'V' '□' '□' 'D' '□' '□' '□' 'E' '□' '□' '□' 'O' '□' '□' '□'
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function construct_railfence(input::AbstractString, n_rails::Integer)
|
function construct_railfence(input::AbstractString, n_rails::Integer)
|
||||||
input = uppercase(replace(input, " " => ""))
|
input = uppercase(replace(input, " " => ""))
|
||||||
@@ -25,23 +37,41 @@ function construct_railfence(input::AbstractArray{T}, n_rails::Integer) where {T
|
|||||||
return construct_railfence(input, zeros(T, n_rails, length(input)), n_rails)
|
return construct_railfence(input, zeros(T, n_rails, length(input)), n_rails)
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
@doc raw"""
|
||||||
```julia
|
```julia
|
||||||
encrypt_railfence(input::AbstractString, n_rails::Integer)
|
encrypt_railfence(input::AbstractString, n_rails::Integer)
|
||||||
```
|
```
|
||||||
|
|
||||||
See https://en.wikipedia.org/wiki/Rail_fence_cipher.
|
See [`https://en.wikipedia.org/wiki/Rail_fence_cipher`](https://en.wikipedia.org/wiki/Rail_fence_cipher).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_railfence("WE ARE DISCOVERED. FLEE AT ONCE", 3) # this reads the above matrix row by row
|
||||||
|
"WECRFACERDSOEE.LETNEAIVDEO"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_railfence(input::AbstractString, n_rails::Integer)
|
function encrypt_railfence(input::AbstractString, n_rails::Integer)
|
||||||
return join(Char[c for rail in eachrow(construct_railfence(input, n_rails)) for c in rail if c != '□'])
|
return join(Char[c for rail in eachrow(construct_railfence(input, n_rails)) for c in rail if c != '□'])
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
@doc raw"""
|
||||||
```julia
|
```julia
|
||||||
encrypt_railfence(input::AbstractString, n_rails::Integer)
|
encrypt_railfence(input::AbstractString, n_rails::Integer)
|
||||||
```
|
```
|
||||||
|
|
||||||
See https://en.wikipedia.org/wiki/Rail_fence_cipher.
|
See [`https://en.wikipedia.org/wiki/Rail_fence_cipher`](https://en.wikipedia.org/wiki/Rail_fence_cipher).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_railfence("WECRFACERDSOEE.LETNEAIVDEO", 3)
|
||||||
|
"wearediscovered.fleeatonce"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function decrypt_railfence(input::AbstractString, n_rails::Integer)
|
function decrypt_railfence(input::AbstractString, n_rails::Integer)
|
||||||
char_positions = Int[n for row in eachrow(construct_railfence(1:length(input), n_rails)) for n in row if n != 0]
|
char_positions = Int[n for row in eachrow(construct_railfence(1:length(input), n_rails)) for n in row if n != 0]
|
||||||
|
@@ -87,10 +87,23 @@ function SolitaireKeyStream(initialDeck::AbstractVector{T}) where {T <: Integer}
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
encrypt_solitaire(string::AbstractString, initialDeck::AbstractVector{T}) where {T <: Integer}
|
||||||
|
```
|
||||||
|
|
||||||
Encrypts the given plaintext according to the Solitaire cipher.
|
Encrypts the given plaintext according to the Solitaire cipher.
|
||||||
The key may be given either as a vector initial deck, where the cards are
|
The key may be given either as a vector initial deck, where the cards are
|
||||||
1 through 54 (the two jokers being 53, 54), or as a string.
|
1 through 54 (the two jokers being 53, 54), or as a string.
|
||||||
Schneier's keying algorithm is used to key the deck if the key is a string.
|
Schneier's keying algorithm is used to key the deck if the key is a string.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_solitaire("Hello, World!", "crypto")
|
||||||
|
"GRNNQISRYA"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_solitaire(string::AbstractString, initialDeck::AbstractVector{T}) where {T <: Integer}
|
function encrypt_solitaire(string::AbstractString, initialDeck::AbstractVector{T}) where {T <: Integer}
|
||||||
inp = uppercase(letters_only(string))
|
inp = uppercase(letters_only(string))
|
||||||
@@ -103,10 +116,23 @@ function encrypt_solitaire(string::AbstractString, initialDeck::AbstractVector{T
|
|||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
decrypt_solitaire(string::AbstractString, initialDeck::AbstractVector{T}) where {T <: Integer}
|
||||||
|
```
|
||||||
|
|
||||||
Decrypts the given ciphertext according to the Solitaire cipher.
|
Decrypts the given ciphertext according to the Solitaire cipher.
|
||||||
The key may be given either as a vector initial deck, where the cards are
|
The key may be given either as a vector initial deck, where the cards are
|
||||||
1 through 54 (the two jokers being 53, 54), or as a string.
|
1 through 54 (the two jokers being 53, 54), or as a string.
|
||||||
Schneier's keying algorithm is used to key the deck if the key is a string.
|
Schneier's keying algorithm is used to key the deck if the key is a string.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_solitaire("EXKYI ZSGEH UNTIQ", collect(1:54)) # as per https://www.schneier.com/code/sol-test.txt
|
||||||
|
"aaaaaaaaaaaaaaa"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function decrypt_solitaire(string::AbstractString, initialDeck::AbstractVector{T}) where {T <: Integer}
|
function decrypt_solitaire(string::AbstractString, initialDeck::AbstractVector{T}) where {T <: Integer}
|
||||||
inp = uppercase(letters_only(string))
|
inp = uppercase(letters_only(string))
|
||||||
|
@@ -26,7 +26,22 @@ encrypt_substitution(plaintext, "zyxwvutsrqponmlkjihgfedcba") # this will create
|
|||||||
|
|
||||||
As per convention, the output will always be uppercase.
|
As per convention, the output will always be uppercase.
|
||||||
|
|
||||||
For more information, see https://en.wikipedia.org/wiki/Substitution_cipher.
|
For more information, see [`https://en.wikipedia.org/wiki/Substitution_cipher`](https://en.wikipedia.org/wiki/Substitution_cipher).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_substitution("Hello, this is plaintext", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm")
|
||||||
|
"ITSSG, ZIOL OL HSQOFZTBZ"
|
||||||
|
|
||||||
|
julia> encrypt_substitution("Hello, this is plaintext", "qwertyuiopasdfghjklzxcvbnm")
|
||||||
|
"ITSSG, ZIOL OL HSQOFZTBZ"
|
||||||
|
|
||||||
|
julia> encrypt_substitution("xyz", Dict('x' => 'd', 'y' => 'e', 'z' => 't'))
|
||||||
|
"DET"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
encrypt_substitution(plaintext, sub_dict::Dict{T, S}) where {T, S} =
|
encrypt_substitution(plaintext, sub_dict::Dict{T, S}) where {T, S} =
|
||||||
uppercase(join(something_crypt_substitution(lowercase(plaintext), sub_dict)))
|
uppercase(join(something_crypt_substitution(lowercase(plaintext), sub_dict)))
|
||||||
@@ -58,7 +73,22 @@ decrypt_substitution(ciphertext, "zyxwvutsrqponmlkjihgfedcba"; reverse_dict = tr
|
|||||||
|
|
||||||
As per convention, the output will always be lowercase.
|
As per convention, the output will always be lowercase.
|
||||||
|
|
||||||
For more information, see https://en.wikipedia.org/wiki/Substitution_cipher.
|
For more information, see [`https://en.wikipedia.org/wiki/Substitution_cipher`](https://en.wikipedia.org/wiki/Substitution_cipher).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_substitution("ITSSG, ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm", reverse_dict = true)
|
||||||
|
"hello, this is plaintext"
|
||||||
|
|
||||||
|
julia> encrypt_atbash("some text", "abcdefghijklmnopqrstuvwxyz")
|
||||||
|
"HLNV GVCG"
|
||||||
|
|
||||||
|
julia> decrypt_atbash("HLNV GVCG", "abcdefghijklmnopqrstuvwxyz")
|
||||||
|
"some text"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function decrypt_substitution(ciphertext, sub_dict::Dict{T, S}; reverse_dict::Bool = true) where {T, S}
|
function decrypt_substitution(ciphertext, sub_dict::Dict{T, S}; reverse_dict::Bool = true) where {T, S}
|
||||||
sub_dict = reverse_dict ? reverse(sub_dict) : sub_dict
|
sub_dict = reverse_dict ? reverse(sub_dict) : sub_dict
|
||||||
|
@@ -1,51 +1,81 @@
|
|||||||
using Statistics
|
using Statistics
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
encrypt_vigenere(plaintext, key::Array)
|
||||||
|
encrypt_vigenere(ciphertext, key::AbstractString)
|
||||||
|
```
|
||||||
|
|
||||||
Encrypts the given string using the Vigenere cipher according to the given vector of offsets.
|
Encrypts the given string using the Vigenere cipher according to the given vector of offsets.
|
||||||
For example, encrypt_vigenere("ab", [0, 1]) returns "AC".
|
For example, `encrypt_vigenere("ab", [0, 1])` returns `"AC"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> encrypt_vigenere("Hello, World!", "ab")
|
||||||
|
"HFLMOXOSLE"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_vigenere(plaintext, key::Array)
|
function encrypt_vigenere(plaintext, key::Array)
|
||||||
# plaintext: string; key: vector of integer offsets, so [0, 1] encrypts "ab" as "ac"
|
# plaintext: string; key: vector of integer offsets, so [0, 1] encrypts "ab" as "ac"
|
||||||
ans = String[encrypt_caesar(chr, key[(i - 1) % length(key) + 1]) for (i, chr) in enumerate(letters_only(plaintext))]
|
ans = String[encrypt_caesar(chr, key[(i - 1) % length(key) + 1]) for (i, chr) in enumerate(letters_only(plaintext))]
|
||||||
return join(ans)
|
return join(ans)
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
|
||||||
Decrypts the given string using the Vigenere cipher according to the given vector of offsets.
|
|
||||||
For example, decrypt_vigenere("ac", [0, 1]) returns "ab".
|
|
||||||
"""
|
|
||||||
function decrypt_vigenere(ciphertext, key::Array)
|
|
||||||
# ciphertext: string; key: vector of integer offsets, so [0, 1] decrypts "ac" as "ab"
|
|
||||||
return lowercase(encrypt_vigenere(ciphertext, map(x -> 26 - x, key)))
|
|
||||||
end
|
|
||||||
|
|
||||||
"""
|
|
||||||
Encrypts the given string using the Vigenere cipher according to the given keystring.
|
|
||||||
For example, encrypt_vigenere("ab", "ab") returns "AC".
|
|
||||||
"""
|
|
||||||
function encrypt_vigenere(ciphertext, key::AbstractString)
|
function encrypt_vigenere(ciphertext, key::AbstractString)
|
||||||
# ciphertext: string; key: string, so "ab" encrypts "ab" as "AC"
|
# ciphertext: string; key: string, so "ab" encrypts "ab" as "AC"
|
||||||
return encrypt_vigenere(ciphertext, Int[Int(i) - 97 for i in lowercase(letters_only(key))])
|
return encrypt_vigenere(ciphertext, Int[Int(i) - 97 for i in lowercase(letters_only(key))])
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Decrypts the given string using the Vigenere cipher according to the given keystring.
|
```julia
|
||||||
For example, decrypt_vigenere("ab", "ac") returns "ab".
|
decrypt_vigenere(ciphertext, key::Array)
|
||||||
|
decrypt_vigenere(plaintext, key::AbstractString)
|
||||||
|
```
|
||||||
|
|
||||||
|
Decrypts the given string using the Vigenere cipher according to the given vector of offsets.
|
||||||
|
For example, `decrypt_vigenere("ac", [0, 1])` returns `"ab"`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> decrypt_vigenere("HFLMOXOSLE", [0, 1]) # Notice that the offset `0` corresponds to the key `a`.
|
||||||
|
"helloworld"
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
function decrypt_vigenere(ciphertext, key::Array)
|
||||||
|
# ciphertext: string; key: vector of integer offsets, so [0, 1] decrypts "ac" as "ab"
|
||||||
|
return lowercase(encrypt_vigenere(ciphertext, map(x -> 26 - x, key)))
|
||||||
|
end
|
||||||
function decrypt_vigenere(plaintext, key::AbstractString)
|
function decrypt_vigenere(plaintext, key::AbstractString)
|
||||||
# plaintext: string; key: string, so "ab" decrypts "ac" as "ab"
|
# plaintext: string; key: string, so "ab" decrypts "ac" as "ab"
|
||||||
return decrypt_vigenere(plaintext, Int[Int(i) - 97 for i in lowercase(letters_only(key))])
|
return decrypt_vigenere(plaintext, Int[Int(i) - 97 for i in lowercase(letters_only(key))])
|
||||||
end
|
end
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
```julia
|
||||||
|
crack_vigenere(plaintext; keylength::Integer = 0)
|
||||||
|
```
|
||||||
|
|
||||||
Cracks the given text encrypted with the Vigenere cipher.
|
Cracks the given text encrypted with the Vigenere cipher.
|
||||||
|
|
||||||
Returns (derived key, decrypted plaintext).
|
Returns `(derived key, decrypted plaintext)`.
|
||||||
|
|
||||||
Optional parameters:
|
Optional parameters:
|
||||||
keylength=0: if the key length is known, specifying it may help the solver.
|
`keylength=0`: if the key length is known, specifying it may help the solver.
|
||||||
If 0, the solver will attempt to derive the key length using the index
|
If 0, the solver will attempt to derive the key length using the index
|
||||||
of coincidence.
|
of coincidence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```julia
|
||||||
|
julia> crack_vigenere(str)
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
function crack_vigenere(plaintext; keylength::Integer = 0)
|
function crack_vigenere(plaintext; keylength::Integer = 0)
|
||||||
stripped_text = letters_only(lowercase(plaintext))
|
stripped_text = letters_only(lowercase(plaintext))
|
||||||
|
Reference in New Issue
Block a user