【Python随笔】Enigma密码机的原理及python代码实现

最近笔者接触到了Cypher这款游戏,玩法很简单,就是通过文字、图片等各种表达手段组成的谜面,猜一段英文,算是初步接触了密码学的一些知识。游戏中提到了很多类型的密码,其中Enigma密码机就是单独一种,在电影《模仿游戏》中,夏洛克.福尔摩斯费尽心力破解的德军密码,也就出自此密码机之手。在游戏里,有一道题目便是,需要根据Enigma密码机的初始设置,破译一段密文,得到明文。没办法,为了解出来这道题,只能发挥程序员的职业本性,写一段程序来跑一下了。

今天,笔者就分享一下自己用python实现的Enigma密码机,虽然代码非常粗糙,有很多优化空间,但整体逻辑比较清晰,尤其是解Cypher游戏里的题目,已经够用了。

简单来讲,一个Engima密码机是分层的,包含以下几个部分:

第一层是Keyboard键盘,是输入输出的部分,输入26个字母,输出的字母可能是26个字母之一。从打字人员的视角,按下键盘上某个字母之后,经过加密,对应密文的字母就会亮起来。

第二层是Plugboard接线板,接线板本质上会把一些字母的输入输出做交换,比如A和B两个字母接起来了,那么输入A输出就是B,反之输入B输出就是A,是一个很简单的置换密码。

第三层叫做Scrambler扰码器,在Enigma构成里也叫做Rotor转子,是整个Enigma密码机最核心的加密部分。虽然扰码器的输入是26个字母,输出也是26个字母的另一种排列,但其复杂度在于,每打一个字母,转子会转一下。比方说转子是ABCD对应DCBA,现在你输一个A,转子出来一个D,但下一次你输A就不是D了,而是C,这是因为转子转了一下,变成BCDA对应CBAD,你输A其实一直对应转子第一个位置,下一次就变成B的位置了,所以就会出来C。所以说,有了这个Scrambler,加密会变得更加复杂,而且转子起始位置不同的话,出来的结果也会不一样。

除此之外,转子还可以叠加,比如叠3个转子,第1个转子转满一圈,第2个转子转1次,第2个转子转满1圈,第3个转子转1次。这样叠下来,复杂度就更高了。如果不了解扰码器的字母映射,不了解转子起始位置,基本破解不出来。

最后一层是Reflector反射器,是Enigma构成里最鬼才的设计。有了这个东西,Enigma就成了自对称加密,也就是说,你输入明文,可能出来一组密文,但你复位到初始设置之后,输入密文,就会把最开始的明文输出出来。Reflector本身也是和Plugboard一样的接线板设计,但在整个加密过程中只经过一次,而不像其他部分经过两次。整个加密步骤是这样的:

  • Keyboard输入1个字母
  • 第1个转子转一下,可能带动后面的转子转
  • 经过Plugboard,看是从当前位置出口输出,还是从某个接线位置的出口输出
  • 逐层经过Scrambler转子1~3,从对应位置输入,从映射到的字母的当前位置输出
  • 经过Reflector,从Scrambler输出的位置输入,从对应字母的当前位置输出
  • 逐个反向经过Scrambler转子3~1,从上一个输出的位置输入,从映射到的字母的当前位置输出
  • 反向经过Plugboard,再看是从当前位置返回给Keyboard,还是某个接线的位置返回
  • 反向经过Keyboard,输出对应位置的字母

所以整个逻辑捋下来,Enigma密码机重点并非字母之间的映射,而是不同层次之间接线扣子索引位置的映射。而且,当你输入时,其实输入到输出的全加密过程的连线已经连好了,所以反过来也是一样,你输入密文,也会跟着这条连线,最终走到明文对应的字母。

捋完了逻辑,就可以写代码了,可以用简单的面向对象方式,把每个组件定义清楚。首先是PlugBoard,实现比较简单的字母位置映射:

def to_chararray(o):
    if isinstance(o, list):
        return o
    return [c for c in str(o)]
    

def alphabetical_letters():
    return to_chararray('abcdefghijklmnopqrstuvwxyz')


class Plugboard(object):
    def __init__(self):  # should reflect
        self.inputs = alphabetical_letters()
        self.outputs = alphabetical_letters()
        self.mapping = {}

    def plug_one(self, c1, c2):
        idx1, idx2 = self.inputs.index(c1), self.outputs.index(c2)
        self.mapping[idx1] = idx2
        self.mapping[idx2] = idx1

    def plug_many(self, pairs):
        for c1, c2 in pairs:
            self.plug_one(c1, c2)

    def proxy(self, idx):  # inputs/outputs are the same, so forward & backward no differs
        if idx in self.mapping:
            return self.mapping[idx]
        return idx

然后是Scrambler(Rotor),需要实现正反向输入输出,以及重制到某个位置(字母)的功能:

def rotate_array(arr: list, direction: int):
    if direction > 0:  # right
        final = arr[-1]
        for i in range(len(arr) - 1, 0, -1):
            arr[i] = arr[i - 1]
        arr[0] = final
    elif direction < 0:  # left
        final = arr[0]
        for i in range(len(arr) - 1):
            arr[i] = arr[i + 1]
        arr[-1] = final


class Scrambler(object):
    def __init__(self, inputs, outputs):
        self.inputs = to_chararray(inputs)
        self.outputs = to_chararray(outputs)
        self.direction = -1 # rotate left
        self.rotate_cnt = 0

    def rotate(self, cnt=1):
        for _ in range(cnt):
            rotate_array(self.inputs, self.direction)
            rotate_array(self.outputs, self.direction)
            self.rotate_cnt += 1

    def reset_to_idx(self, idx):
        for _ in range(idx):
            self.rotate() # 整个都要转,不是一边转
        self.rotate_cnt = 0

    def reset_to_char(self, c):
        while self.inputs[0] != c:
            self.rotate() # 看input的字母来转
        self.rotate_cnt = 0

    def forward(self, idx):
        c = self.inputs[idx]
        return self.outputs.index(c)

    def backward(self, idx):
        c = self.outputs[idx]
        return self.inputs.index(c)

然后是Reflector,找到output对应位置的字母,反射到input中接线字母的位置:

class Reflector(object):
    def __init__(self, inputs, outputs):
        self.inputs = to_chararray(inputs)
        self.outputs = to_chararray(outputs)

    def reflect(self, idx):
        c = self.outputs[idx]
        return self.inputs.index(c)

最后组装成整个Enigma机:

class Enigma(object):
    def __init__(self, plugboard=None, scramblers=None, reflector=None):
        self.keyboard = alphabetical_letters()
        self.plugboard: Plugboard = plugboard
        self.scramblers: List[Scrambler] = scramblers
        self.reflector: Reflector = reflector

    def _rotate_scramblers(self):
        for i in range(len(self.scramblers)):
            if i == 0 or (self.scramblers[i-1].rotate_cnt > 0 and
                          self.scramblers[i-1].rotate_cnt % len(self.keyboard) == 0):
                self.scramblers[i].rotate()

    def encode_string(self, s):
        chars = []
        for c in s:
            # input with idx
            idx = self.keyboard.index(c)

            # plugboard forward
            if self.plugboard:
                idx = self.plugboard.proxy(idx)

            # scramblers forward
            if self.scramblers:
                self._rotate_scramblers()
                for scrambler in self.scramblers:
                    idx = scrambler.forward(idx)

            # reflect and backward
            if self.reflector:
                idx = self.reflector.reflect(idx)

                if self.scramblers:
                    scramblers_cnt = len(self.scramblers)
                    for i in range(scramblers_cnt):
                        scrambler = self.scramblers[scramblers_cnt-1-i]
                        idx = scrambler.backward(idx)

                if self.plugboard:
                    idx = self.plugboard.proxy(idx)

            # output char of final idx
            chars.append(self.keyboard[idx])

        return ''.join(chars)

有了这些代码之后,攻克Cypher游戏中的关卡也就变得非常容易了。

def debug():
    input_string = 'gyhrvflrxy'

    plugboard = Plugboard()
    plugboard.plug_many([
        ('a', 'b'),
        ('s', 'z'),
        ('u', 'y'),
        ('g', 'h'),
        ('l', 'q'),
        ('e', 'n')
    ])

    # scramblers
    scrambler_1 = Scrambler(
        inputs=alphabetical_letters(),
        outputs='uwygadfpvzbeckmthxslrinqoj'
    )
    scrambler_1.reset_to_char('e')

    scrambler_2 = Scrambler(
        inputs=alphabetical_letters(),
        outputs='ajpczwrlfbdkotyuqgenhxmivs'
    )
    scrambler_2.reset_to_char('a')

    scrambler_3 = Scrambler(
        inputs=alphabetical_letters(),
        outputs='tagbpcsdqeufvnzhyixjwlrkom'
    )
    scrambler_3.reset_to_char('b')

    # reflector
    reflector = Reflector(
        inputs=alphabetical_letters(),
        outputs='yruhqsldpxngokmiebfzcwvjat'
    )

    enigma = Enigma(
        plugboard=plugboard,
        scramblers=[
            scrambler_2,
            scrambler_1,
            scrambler_3,
        ],
        reflector=reflector
    )
    encoded = enigma.encode_string(input_string)
    print(encoded)  # blitzkrieg


if __name__ == '__main__':
    debug()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/940570.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

金融信息系统多活技术-应用策略

目录 概述 ​编辑 多活应用场景 流水型系统 账户型系统 流水型系统应用策略 业务模型说明 系统并行策略 接入和路由策略 系列阅读 概述 本文件提出了金融信息系统多活技术的应用指南&#xff0c;金融机构可根据自身业务需要&#xff0c;结合本文件进行 多活信息系统的…

大数据之Hbase环境安装

Hbase软件版本下载地址&#xff1a; http://mirror.bit.edu.cn/apache/hbase/ 1. 集群环境 Master 172.16.11.97 Slave1 172.16.11.98 Slave2 172.16.11.99 2. 下载软件包 #Master wget http://archive.apache.org/dist/hbase/0.98.24/hbase-0.98.24-hadoop1-bin.tar.gz…

人工智能ACA(四)--机器学习基础

零、参考资料 一篇文章完全搞懂正则化&#xff08;Regularization&#xff09;-CSDN博客 一、 机器学习概述 0. 机器学习的层次结构 学习范式&#xff08;最高层&#xff09; 怎么学 监督学习 无监督学习 半监督学习 强化学习 学习任务&#xff08;中间层&#xff0…

HTML语法规范

HTML语法规则 HTML 标签是由尖括号包围的关键词&#xff0c;标签通常是成对出现的&#xff0c;例如 <html> 和 </html>&#xff0c;称为双标签 。标签对中的第一个标签是开始标签&#xff0c;第二个标签是结束标签单标签比较少&#xff0c;例如<br />&#x…

四川托普信息技术职业学院教案1

四川托普信息技术职业学院教案 【计科系】 周次 第 1周&#xff0c;第1次课 备 注 章节名称 第1章 XML语言简介 引言 1.1 HTML与标记语言 1.2 XML的来源 1.3 XML的制定目标 1.4 XML概述 1.5 有了HTML了&#xff0c;为什么还要发展XML 1.5.1 HTML的缺点 1.5.2 XML的特点 1.6 X…

Win10将WindowsTerminal设置默认终端并添加到右键(无法使用微软商店)

由于公司内网限制&#xff0c;无法通过微软商店安装 Windows Terminal&#xff0c;本指南提供手动安装和配置新版 Windows Terminal 的步骤&#xff0c;并添加右键菜单快捷方式。 1. 下载新版终端安装包: 访问 Windows Terminal 的 GitHub 发布页面&#xff1a;https://githu…

OpenAI发布新一代推理模型O3和O3 Mini:能力与性能的双重突破

2024年12月21日&#xff0c;OpenAI通过一场特别活动正式宣布了其新一代推理模型O3及其轻量化版本O3 Mini。这标志着AI推理能力和效率的又一次飞跃。本文将围绕发布会中的关键内容&#xff0c;详细介绍O3和O3 Mini的核心能力、性能表现、以及面向公众安全测试的相关计划。 1. 背…

VScode插件之get、set函数自动生成

文章目录 VScode插件之get、set函数自动生成插件名称现有功能功能快捷键使用总结与部分插件的get、set生成对比部分实现效果展示部分实现思路 VScode插件之get、set函数自动生成 初次尝试插件的编写开发&#xff0c;这篇博客也是对自己成果的一个记录&#xff0c;如有不足请指…

【Lua热更新】上篇

Lua 热更新 - 上篇 下篇链接&#xff1a;【Lua热更新】下篇 文章目录 Lua 热更新 - 上篇一、AssetBundle1.理论2. AB包资源加载 二、Lua 语法1. 简单数据类型2.字符串操作3.运算符4.条件分支语句5.循环语句6.函数7. table数组8.迭代器遍历9.复杂数据类型 - 表9.1字典9.2类9.3…

完全二叉树的权值(蓝桥杯2019年试题G)

给定一棵包含N个节点的完全二叉树&#xff0c;树上的每个节点都有一个权值&#xff0c;按从上到小、从左到右的顺序依次是A1、A2……An,&#xff08;1&#xff0c;2&#xff0c;n为下标。&#xff09;如下图所示。 现在&#xff0c;小明要把相同深度的节点的权值加到一起&#…

时间管理系统|Java|SSM|JSP|

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、JSP、jquery,html 5⃣️数据库可…

前端yarn工具打包时网络连接问题排查与解决

最近线上前端打包时提示 “There appears to be trouble with your network connection”&#xff0c;以此文档记录下排查过程。 前端打包方式 docker启动临时容器打包&#xff0c;命令如下 docker run --rm -w /app -v pwd:/app alpine-node-common:v16.20-pro sh -c "…

harmony UI组件学习(1)

Image 图片组件 string格式&#xff0c;通常用来加载网络图片&#xff0c;需要申请网络访问权限:ohos.permission.INTERNET Image(https://xxx.png) PixelMap格式&#xff0c;可以加载像素图&#xff0c;常用在图片编辑中 Image(pixelMapobject) Resource格式&#xff0c;加…

mac 安装graalvm

Download GraalVM 上面链接选择jdk的版本 以及系统的环境下载graalvm的tar包 解压tar包 tar -xzf graalvm-jdk-<version>_macos-<architecture>.tar.gz 移入java的文件夹目录 sudo mv graalvm-jdk-<version> /Library/Java/JavaVirtualMachines 设置环境变…

14-zookeeper环境搭建

0、环境 java&#xff1a;1.8zookeeper&#xff1a;3.5.6 1、下载 zookeeper下载点击这里。 2、安装 下载完成后解压&#xff0c;放到你想放的目录里。先看一下zookeeper的目录结构&#xff0c;如下图&#xff1a; 进入conf目录&#xff0c;复制zoo_sample.cfg&#xff0…

如何使用Python处理视频合成

使用 Python 处理视频合成可借助 MoviePy 库&#xff0c;以下是具体步骤&#xff1a; 安装 MoviePy 通过 pip 命令安装&#xff0c;即 pip install moviepy&#xff0c;需确保已安装 ffmpeg&#xff0c;并正确设置环境变量&#xff0c;因为 MoviePy 依赖它来处理视频. 基本合…

存储过程 与 存储函数的区别及用法 及 触发器 !!!

引言&#xff1a; 存储函数和存储过程&#xff0c;作为数据库中的预编译代码块&#xff0c;能够封装复杂的业务逻辑和数据处理流程&#xff0c;使得数据库操作更加简洁、易读和可维护。而触发器&#xff0c;则像是一个智能的守卫&#xff0c;能够在特定事件发生时自动执行预设的…

用nginx部署两个前端(超简单,三步!)

1.首先在nginx的html目录下创两个文件夹分别用于放两个前端打包好的静态资源&#xff0c;并且把静态资源各自放好&#xff1a; 2. 在nginx的配置文件里&#xff0c;写好两个server。如图&#xff0c;写好两个前端要用的端口以及刚才那两文件夹的路径&#xff1a; worker_proces…

level2逐笔委托查询接口

沪深逐笔委托队列查询 前置步骤 分配数据库服务器 查询模板 以下是沪深委托队列查询的请求模板&#xff1a; http://<数据库服务器>/sql?modeorder_book&code<股票代码>&offset<offset>&token<token>查询参数说明 参数名类型说明mo…

flask-admin的modelview 实现list列表视图中某个列字段值翻译

背景&#xff1a; flask-admin 开发中modelview视图是非常强大的&#xff0c;但文档写的很难受&#xff0c;只能通过源码慢慢摸索学习&#xff0c;一点点记录 材料&#xff1a; 可用的flask-admin 环境 制作&#xff1a; 样例代码&#xff1a; 1、modelview 视图代码 col…