こんにちは!
好奇心過多なコーダー 零壱(ゼロイチ)テクトです。

エニグマ・シミュレータは手に垢がつくほど存在しますけど
PowerShellでの実装は見かけなかったのでやってみました。
アナログの暗号化を知るのにエニグマはとても良い教材だと思いました。
本当の実機を考えると、実は微妙な部分はあるのですけれど
とりあえず、こういう理解であってます?的に動作する形で実装してみました。
1.概要/仕様
・エニグマ1(3ロータの初期型)と同じ暗号化動作
・暗号化→複合化をすぐに見たいので1ソースで実装
・スペース含む記号類は暗号化せずそのまま使用する
2.コード
enigma.ps1
# 暗号化するテキスト(入力は大文字/小文字OK。出力は大文字のみ)
$textToEncrypt = "HELLO, WORLD - abcde"
# ▼ロータ/リフレクタ設定関連
# 使用するロータの配線定義 (エニグマIの汎用ロータ設定)
$rotor1 = [Rotor]::new("EKMFLGDQVZNTOWYHXUSPAIBRCJ")
$rotor2 = [Rotor]::new("AJDKSIRUXBLHWTMCQGZNPYFVOE")
$rotor3 = [Rotor]::new("BDFHJLCPRTXVZNYEIWGAKMUSQO")
# リフレクター定義 (エニグマIのリフレクター設定)
$reflector = "YRUHQSLDPXNGOKMIEBFZCWVJAT"
# エニグママシンを作成
$enigma = [Enigma]::new($rotor1, $rotor2, $rotor3, $reflector)
# 暗号化数値(初期ロータ位置):1-26
$initialPos1 = 11
$initialPos2 = 9
$initialPos3 = 7
# ロータのポジション処理を行うクラス
class Rotor {
[string]$wiring # ロータの配線
[string]$reverseWiring # 逆配線(逆方向の配線)
[int]$position = 0 # 現在のロータの位置
[int]$ringSetting = 0 # リング設定(初期位置)
# コンストラクタ:各文字と対となる配線取得
Rotor([string]$wiring) {
$this.wiring = $wiring
$this.reverseWiring = ""
for ($i = 0; $i -lt 26; $i++) {
$char = [char](65 + $i) # 'A'から'Z'までの文字取得
$index = $wiring.IndexOf($char) # 配線内の文字の位置取得
$this.reverseWiring += [char](65 + $index) # 逆配線を作成
}
}
# 文字を暗号化または復号化するメソッド
[char] Encrypt([char]$char, [bool]$forward) {
$index = ([int][char]$char - 65 + $this.position - $this.ringSetting + 26) % 26
if ($forward) {
$encryptedChar = $this.wiring[$index] # 順方向の配線で文字を変換
} else {
$encryptedChar = $this.reverseWiring[$index] # 逆方向の配線で文字を変換
}
return [char](($encryptedChar - 65 - $this.position + $this.ringSetting + 26) % 26 + 65)
}
# ロータを1回転させるメソッド
[void] Rotate() {
$this.position = ($this.position + 1) % 26
}
# ロータの位置を設定するメソッド
[void] SetPosition([int]$position) {
$this.position = $position % 26
}
}
# エニグマ暗号機のメインクラス
class Enigma {
[Rotor]$rotor1
[Rotor]$rotor2
[Rotor]$rotor3
[string]$reflector
# コンストラクタ:各変数にロータとリフレクタの値で初期化
Enigma([Rotor]$rotor1, [Rotor]$rotor2, [Rotor]$rotor3, [string]$reflector) {
$this.rotor1 = $rotor1
$this.rotor2 = $rotor2
$this.rotor3 = $rotor3
$this.reflector = $reflector
}
# テキストを暗号化または復号化するメソッド
[string] ProcessText([string]$text) {
$processedText = ""
foreach ($char in $text.ToUpper().ToCharArray()) {
if ($char -cmatch '[A-Z]') { # 入力文字がアルファベットかチェック
$this.rotor1.Rotate() # 最初のロータを1回転
if ($this.rotor1.position -eq 0) { # ロータ1が一周した場合
$this.rotor2.Rotate() # ロータ2を1回転
if ($this.rotor2.position -eq 0) { # ロータ2が一周した場合
$this.rotor3.Rotate() # ロータ3を1回転
}
}
# 順方向の暗号化
$char = $this.rotor1.Encrypt($char, $true)
$char = $this.rotor2.Encrypt($char, $true)
$char = $this.rotor3.Encrypt($char, $true)
# リフレクタで変換
$char = $this.reflector[([int][char]$char - 65)]
# 逆方向の復号化
$char = $this.rotor3.Encrypt($char, $false)
$char = $this.rotor2.Encrypt($char, $false)
$char = $this.rotor1.Encrypt($char, $false)
}
$processedText += $char
}
return $processedText
}
# ロータの位置を設定するメソッド
[void] SetRotorPositions([int]$pos1, [int]$pos2, [int]$pos3) {
$this.rotor1.SetPosition($pos1)
$this.rotor2.SetPosition($pos2)
$this.rotor3.SetPosition($pos3)
}
}
# ローターのリセット/設定を行う関数
function Reset-Rotors {
param(
[Enigma]$enigmaMachine, # エニグマ暗号機
[int]$pos1, # ロータ1の初期位置
[int]$pos2, # ロータ2の初期位置
[int]$pos3 # ロータ3の初期位置
)
$enigmaMachine.SetRotorPositions($pos1, $pos2, $pos3)
}
# ▼暗号化/復号化処理開始
# 暗号化プロセスの開始メッセージ出力
Write-Output "Encrypting..."
# ロータ位置を初期化
Reset-Rotors -enigmaMachine $enigma -pos1 $initialPos1 -pos2 $initialPos2 -pos3 $initialPos3
# テキストを暗号化
$encryptedText = $enigma.ProcessText($textToEncrypt)
# 暗号化されたテキストを出力
Write-Output "Encrypted Text: $encryptedText"
# ロータ位置を同じ初期位置にリセット
Reset-Rotors -enigmaMachine $enigma -pos1 $initialPos1 -pos2 $initialPos2 -pos3 $initialPos3
# 複号化プロセスの開始メッセージ出力
Write-Output "Decrypting..."
# 暗号化されたテキストを複号化
$decryptedText = $enigma.ProcessText($encryptedText)
# 複号化されたテキストを出力
Write-Output "Decrypted Text: $decryptedText"
3.備考
何に使えるか分からないですけど、教材代わりにどうぞお使いください。
余談ですけど、最初 Out-GridView を使ったPingツールを作ろうと思ったら
すでに先駆者様がいたので、以前から興味あったエニグマ暗号機を実装しました。
どこかの誰かの何かの足しになれば幸いです。
01000010 01011001 01000101