最近要做密碼學課程設計,每做一個密碼就當複習一下,把算法原理啥的都過一遍吧
簡介
這個是用於GSM系統的序列密碼算法,A5的特點是效率高,適合硬件上高效實現。
加密過程
- A5 / 1基於三個線性反饋移位寄存器LFSR的組合,三個寄存器的長度分別爲 19 22 23位,然後產生一個114位的祕鑰流。因此首先輸入8個字節的祕鑰去填充三個寄存器的總長也就是64位。
- 每次獲取各寄存器的最後一位相互異或一下再與明文進行異或。從這一點還是可以看出關鍵點在於祕鑰,異或了兩遍原信息一致,所以加密解密都是一樣的算法了。
- 然後根據寄存器的某一位決定是否需要對某個儲存器進行移位,存在的數據多的決定
引用維基百科的一個圖,這個圖很好的揭示了加密的流程
總結要注意的加密的幾個點:
產生祕鑰流
- 根據8位祕鑰產生的64位二進制填充寄存器
- 輸出祕鑰流,每一位都遵循
Z1[18]^Z2[21]^Z3[22]
這樣的流程,我們需要注意的是這裏講序列密碼的時候用+
表示的都是異或 - 然後就經過114輪迭代,就可以產生114位祕鑰啦
寄存器反饋
遵循“服從多數”的原則,從每個寄存器裏面取出一箇中間位(上圖標黃的位置)進行運算,取出的3箇中間位裏面至少有兩個1,則中間位爲1的就進行移位,爲0的就不移位,反之至少兩個爲0,則爲0的進行一次位移,爲1的就不移位了,保證了每次至少有了兩個LFSR被驅動移位了。
生成的補充值
補充值由某些抽頭位進行異或運算的結果決定,運算結果爲1則補充爲1,否則補充0,3個LFSR裏的抽頭位如下圖,但是補充的前提是又寄存器反饋決定的
python實現:
# -*- coding: utf-8 -*-
# Author:0verWatch
X = ''
Y = ''
Z = ''
import base64
import re
def str2bin(str_mess):
res = ""
for i in str_mess:
tmp = bin(ord(i))[2:].zfill(8)
res += tmp
return res
def bin2str(bin_mess):
res = ""
tmp = re.findall(r'.{8}',bin_mess)
for i in tmp:
res += chr(int(i,2))
return res
def LFSRinit(): #用64bit密鑰初始3個移位寄存器,分別是19,22,23位
global X
global Y
global Z
key = input("請輸入8位祕鑰\n")
while len(key) != 8: #限定只能是8位,然後生成64位的二進制流
key = input("請輸入8位祕鑰\n")
key_bin_str = ""
for i in key:
tmp = bin(ord(i))[2:].zfill(8)
key_bin_str += tmp
X = key_bin_str[0:19]
Y = key_bin_str[19:41]
Z = key_bin_str[41:]
# print("X"+X)
# print("Y"+Y)
# print("Z"+Z)
def xor(bin_str,bin_key): #輸入的字符串的二進制流
res = ""
for i in range(len(bin_str)):
if bin_str[i] == bin_key[i]:
res += '0'
else:
res += '1'
return res
def create_key():
global X
global Y
global Z
LFSRinit()
res = ""
for i in range(114): # A5 / 1用於爲每個突發產生114比特的密鑰流序列
# a = X[-1]
# b = Y[-1]
# c = Z[-1]
g = int(X[-1]) ^ int(Y[-1]) ^ int(Z[-1])
res += str(g) #用最後一位異或產生祕鑰流
x = str(int(X[13]) ^ int(X[16]) ^ int(X[17]) ^ int(X[18]) ^ 1) #候選位的值
y = str(int(Y[20]) ^ int(Y[21]) ^ 1)
z = str(int(Z[7]) ^ int(Z[20]) ^ int(Z[21]) ^ int(Z[22]) ^ 1)
#選擇的鐘控位
c_x = int(X[8])
c_y = int(Y[10])
c_z = int(Z[10])
if (c_x + c_y + c_z) >= 2:#多數的佔優
choice = '1'
else:
choice = '0'
if str(c_x) == choice:
X = x + X[:-1] #隱式位移,這裏是不包含最後一位的
if str(c_y) == choice:
Y = y + Y[:-1]
if str(c_z) == choice:
Z = z + Z[:-1]
# print("X"+X)
# print("Y"+Y)
# print("Z"+Z)
# print(res)
return res
def a5_encode(mess):
bin_mess = str2bin(mess)
bin_key = create_key()
bin_cipher = ""
#print(len(bin_mess))
if len(bin_mess) % 114 == 0:
for i in range(0, len(bin_mess), 114):
bin_cipher += xor(bin_mess, bin_key)
elif len(bin_mess) > 114:
j = 0
for i in range(len(bin_mess)):
bin_cipher += str(int(bin_mess[i]) ^ int(bin_key[i]))
j += 1
if j == 114:
j = 0
else:
for i in range(len(bin_mess)):
bin_cipher += str(int(bin_mess[i]) ^ int(bin_key[i]))
print("二進制密文" + bin_cipher)
print("十六進制密文"+hex(int(bin_cipher,2)))
str_cipher = bin2str(bin_cipher)
print(base64.b64encode(str_cipher.encode('utf-8')))
def a5_decode(bin_mess):
bin_key = create_key()
bin_cipher = ""
# print(len(bin_mess))
if len(bin_mess) % 114 == 0:
for i in range(0, len(bin_mess), 114):
bin_cipher += xor(bin_mess, bin_key)
elif len(bin_mess) > 114:
j = 0
for i in range(len(bin_mess)):
bin_cipher += str(int(bin_mess[i]) ^ int(bin_key[i]))
j += 1
if j == 114:
j = 0
else:
for i in range(len(bin_mess)):
bin_cipher += str(int(bin_mess[i]) ^ int(bin_key[i]))
str_cipher = bin2str(bin_cipher)
print("解密後的結果:"+str_cipher)
def get_info():
choice = input("1.加密\n2.解密\n")
if choice == '1':
message = input("輸入你的信息\n")
a5_encode(message)
elif choice == '2':
bin_message = input("輸入你的信息\n")
a5_decode(bin_message)
else:
print("請重新輸入")
if __name__ == '__main__':
while True:
get_info()
安全性
其實這個算法的關鍵點還是落在了祕鑰的長度,體現在其寄存器的長度上,太短了,所以現在已知明文攻擊法對這個算法的攻擊是先確定其中兩個寄存器的初始值再計算出另外一個,所以要想改進就是採用更長的線性反饋寄存器了。。。。。