100行代码手把手带你实现Feisitel加密算法

简介

Feistel 加密算法,或者叫做 Feistel 网络,是一种块加密(block cipher)模型,很多常见的加密算法都具有 Feistel 结构,如 DES、blowfish 等。

Feistel 将明文分割成固定大小(block size)的块(如 32bit、64bit),然后对于每个块进行若干轮操作,每轮操作需要用到一个 key,因此总计需要循环轮数个 key。解密时需要用相同的 keys,因此这是一种对称加密算法。

加密步骤

  1. 将明文转换为 8-bit 二进制形式
  2. 根据块大小(block size)分割成若干个数据块,不足整数块时需要加 padding
  3. 生成轮数(rounds)个 key 的 keys 数组
  4. 对于每个数据块进行若干轮操作,具体如下:
  • 在每轮操作中都将数据块分成相等的左右两部分(L1, R1)
  • 将右半部分(R1)与keys[n]进行加密函数f运算,记为f1
  • 然后将f1与左半部分进行异或(XOR)操作的结果作为下一轮输入的左半部分(L2)
  • 将本轮左半部分(L1)作为下一轮输入的右半部分(R2)
  • 下一轮的输入为L2 + R2, 再进行下一轮操作
  • 在最后一轮结束后将左半部分(Ln)和右半部分(Rn)对调(这样可以使用同一个循环进行加密和解密)

解密步骤

  1. 将密文使用相同的 keys 进行与加密过程相同的循环,不过 key 的顺序与加密时相反(加密时使用 key[1], key[2], ..., key[n], 解密时使用 key[n], key[n-1], ..., key[1])
  2. 组合数据块并按照 8bit 进行分割
  3. 根据 ASCII 码转换为明文

Python 实现

首先定义一些变量

plain_text = 'Hello world!'
# 为了便于取整,选择8bit
block_size = 8
rounds = 16
keys = []

生成随机的 keys

def rand_key(n):
  res = ''
  for i in range(n):
    bit = random.randint(0, 1)
    res += str(bit)
  return res

# length 是key的长度,可以根据需要调整
def generate_keys(length, rounds):
  for i in range (rounds):
    keys.append(rand_key(length))
  return res

定义每一轮循环的操作

def exor(a, b):
  n = len(a)
  res = ''
  for i in range(n):
    if a[i] == b[i]: res += '0'
    else: res += '1'

  return res

def round(str, key):
  n = len(str) // 2
  L1 = str[0:n]
  R1 = str[n:]
  # 这里的f函数选择exor操作
  # 可以替换为其他的加密函数
  f1 = exor(R1, key)
  R2 = exor(f1, L1)
  L2 = R1
  return L2 + R2

加密过程

def feistel_encode(s, rounds):
  res = ''
  # 将密文转为8bit二进制表示
  str_ascii = [ord(x) for x in s]
  str_bin = [format(x, '08b') for x in str_ascii]
  str_bin = ''.join(str_bin)
  # 选择block size的一半最为key length是因为将f函数定义为xor操作
  # 保证长度和block size的一半相等,在执行时就不需要补padding
  # 实际的key length会更长,一般不会少于128bit避免被快速爆破
  key_length = block_size // 2
  generate_keys(key_length, rounds)
  res_bin = ''
  for n in range(0, len(str_bin), block_size):
    temp = str_bin[n:n + block_size]
    for i in range(rounds):
      temp = round(temp, keys[i])
    # 最后一轮循环后交换左右部分
    temp = temp[key_length::] + temp[0:key_length]
    res_bin += temp

  # 将密文转为ASCII字符表示
  for i in range(0, len(res_bin), 8):
    bin_data = res_bin[i: i + 8]
    decimal = int(bin_data, 2)
    res += chr(decimal)

  return res

解密过程

def feistel_decode(s):
  res = ''
  str_ascii = [ord(x) for x in s]
  str_bin = [format(x, '08b') for x in str_ascii]
  str_bin = ''.join(str_bin)

  res_bin = ''

  for n in range(0, len(str_bin), block_size):
    temp = str_bin[n:n + block_size]
    for key in reversed(keys):  # 确保使用反向密钥
      temp = round(temp, key)
    key_length = len(keys[0])
    # 最后一轮循环后交换左右部分
    temp = temp[key_length::] + temp[0:key_length]
    res_bin += temp

  # 转为ASCII字符
  for i in range(0, len(res_bin), 8):
    bin_data = res_bin[i: i + 8]
    decimal = int(bin_data, 2)
    res += chr(decimal)

  return res

最后测试一下

def main():
  print('plain_text: ', plain_text)
  # plain_text:  Hello world!
  cipher_text = feistel_encode(plain_text, rounds)
  print('cipher_text: ', cipher_text)
  # cipher_text:  yààÓlKÓàh}
  decode_text = feistel_decode(cipher_text)
  print('decode_text: ', decode_text)
  # decode_text:  Hello world!

if __name__ == "__main__":
  main()

总结

  • 本文中为了方便演示将block size和key size设置较小,实际一般会更长,如果不满整个block则需要补padding(用0填充)
  • 加密和解密使用相同的循环,但是注意不要忘记需要在加/解密的最后一轮循环后交换左、右部分
  • 动手实现时可以将round设为1或2,单步观察每一轮循环的结果
  • 时间复杂度 O(n)
  • 额外空间 O(n)

作者:justMonika
链接:https://juejin.cn/post/7395069382433456147

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会回答你的问题。关于 apriori 算法的原理,它是一种基于频繁项集的挖掘算法,用于发现数据中的频繁项集。频繁项集是指在数据集中经常出现在一起的物品的集合,例如在购物数据中,经常一起购买的商品就是一个频繁项集。 apriori 算法的核心思想是利用频繁项集的性质,从而减少搜索空间。具体来说,apriori 算法采用了一种称为“逐层搜索”的方法,即首先找到所有的单个物品作为频繁项集,然后基于单个物品,逐步扩展物品数量,直到无法扩展为止。在扩展物品的过程中,apriori 算法利用了一个重要的性质:如果一个项集不是频繁的,那么它的所有超集也不是频繁的。这个性质可以帮助我们剪枝,减少搜索空间。 关于 apriori 算法的代码实现,以下是一个简单的 Python 实现,供你参考: ```python def load_dataset(): return [[1,3,4], [2,3,5], [1,2,3,5], [2,5]] def create_c1(dataset): c1 = [] for transaction in dataset: for item in transaction: if not [item] in c1: c1.append([item]) c1.sort() return list(map(frozenset, c1)) def scan_dataset(dataset, candidates, min_support): support_counts = {} for transaction in dataset: for candidate in candidates: if candidate.issubset(transaction): support_counts[candidate] = support_counts.get(candidate, 0) + 1 num_items = float(len(dataset)) frequent_items = [] support_data = {} for candidate in support_counts: support = support_counts[candidate] / num_items if support >= min_support: frequent_items.append(candidate) support_data[candidate] = support return frequent_items, support_data def apriori_gen(frequent_items, k): next_candidates = [] num_items = len(frequent_items) for i in range(num_items): for j in range(i+1, num_items): itemset1 = list(frequent_items[i])[:k-2] itemset2 = list(frequent_items[j])[:k-2] itemset1.sort() itemset2.sort() if itemset1 == itemset2: next_candidates.append(frequent_items[i] | frequent_items[j]) return next_candidates def apriori(dataset, min_support=0.5): C1 = create_c1(dataset) frequent_items, support_data = scan_dataset(dataset, C1, min_support) all_frequent_items = [frequent_items] k = 2 while len(all_frequent_items[k-2]) > 0: next_candidates = apriori_gen(all_frequent_items[k-2], k) next_frequent_items, next_support_data = scan_dataset(dataset, next_candidates, min_support) support_data.update(next_support_data) all_frequent_items.append(next_frequent_items) k += 1 return all_frequent_items, support_data ``` 这段代码实现了 apriori 算法的主要逻辑,包括数据集加载、候选项集生成、频繁项集挖掘等步骤。你可以通过调用 apriori 函数来运算法,并设置最小支持度等参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值