keeLoq加密算法

轉載自:http://blog.csdn.net/kangweijian

KeeLoq算法介紹

1 KeeLoq運算規則

KeeLoq算法的核心思想就是用8byte密鑰加密4byte明文,從而得到4byte密文或者用8byte密鑰解密4byte密文,還原出原4byte明文。KeeLoq算法演算過程需要定義一個數據寄存器,用於存放4byte明文y31~0或者4byte密文y31~0,和一個密鑰寄存器,用於存放8byte密鑰k63~0。

KeeLoq數據加密過程模型圖如圖1所示,首先定義一個非線性表,這個非線性表有5bits輸入碼,1bit輸出碼。它在數據寄存器中間隔均勻地取5bits: y31、y26、y20、y9、y1,通過NLF(Nonlinear Logic Function)運算產生一個輸出碼。輸出碼再與數據寄存器中的y16與y0以及密鑰寄存器中的k0進行異或運算後生成1bit加密數據碼。每生成1bit加密數據碼,密鑰寄存器和數據寄存器分別進行移位,密鑰寄存器作循環移位,加密數據碼作爲數據寄存器移位的輸入,重複上述步驟528次後,得到4byte的輸出密文。

NLF(x4,x3,x2,x1,x0)=x4x3x2^x4x3x1^x4x2x0^x4x1x0^x4x2^x4x0^x3x2^x3x0^x2x1^x1x0^x1^x0

圖1 KeeLoq加密模型圖

KeeLoq數據解密模型圖如圖2所示,其過程的運算方法與數據加密過程的運算方法基本一致,只是其中運算數據數據位發生變化。非線性表的5bits輸入碼改成從數據寄存器中間隔均勻地取固定5bits:y30、y25、y19、y8、y0。產生1bit輸出碼後輸出碼再與數據寄存器中的y31與y15以及密鑰寄存器中的k15進行異或運算後生成1bit解密數據碼。每輸出1bit解密數據碼後,密鑰寄存器和數據寄存器分別進行移位,密鑰寄存器作循環移位,解密數據碼作爲數據寄存器移位的輸入,重複上述步驟528次後,還原出4byte的明文。

圖2 KeeLoq解密模型圖

KeeLoq實現機制

採用KeeLoq方法實現數據編碼和解碼,其通信過程需嚴格按照下述過程進行。首先,要求編碼端和解碼端都需要有非易失性存儲空間以存儲8byte密鑰(用於編解碼,可編程且不被髮送不可泄露)、3byte序列號(用於區分不同的編碼端)、2byte同步計數值(用於產生編碼滾動效果,每完成一次數據傳送後,其值自加1後更新)、1byte識別碼(序列號的低1byte)和4byte種子碼(安全模式下用來生成密鑰)。

當用戶有按鍵操作時,KeeLoq編碼端將1byte功能鍵、1byte識別碼、2byte同步計數值組合成4byte明文,按照圖1的NLF運算規則加密成4byte密文,再加上4byte固定碼(3byte序列號、1byte功能鍵),組合成一組8byte的編碼數據發送。由於每次發送過程,同步計數值自加1,使得每次發送的4byte密文都是惟一的、不規則的、且不重複,故稱之爲滾動碼,可以有效的防止密碼捕捉和密碼拷貝。由於8byte的編碼組合達到2^64=1.84*10^19,因而可以有效的防止密碼掃描。

解碼端接收到8byte密文數據後,首先匹配編碼端和解碼段的3byte序列號一致後,按照圖2的KeeLoq解碼運算規則還原出4byte明文。再校驗明文中的識別碼以及功能鍵正確後,最後判斷同步計數值是否合理增加。確認成功後根據功能鍵定義,控制相應執行機構動作。

KeeLoq算法不足與改進

KeeLoq算法的安全性與不足

KeeLoq算法的NLF運算規則,使得一個很小的輸入變化量,也會造成很大的輸出變化量,產生的編碼滾動效果。密碼分析者就無法通過輸入微小的變化來觀察分析輸出的變化,從而破解出密鑰,使得KeeLoq算法具有安全性高特點。

雖然KeeLoq算法發佈於20世紀80年代,但直到2007年,Bogdanov才首次對KeeLoq算法進行***,他使用猜測-決定和滑動技術來完成***,***的時間複雜度爲252,空間複雜度爲16GB。在2008年,Courtois等人提出了4種滑動-代數***方法,其主要思想是利用KeeLoq算法連續64圈圈函數形成的置換和圈結構與隨機置換圈結構的差異,先***密鑰的前16bits,再***剩餘的48bits。摺合計算複雜性至少約爲O(2^43)次加密。2010年,遊建雄等人提出3 種不同採用面向字節的差分故障***方法,其中***效率最好的方法,恢復1 bit 密鑰信息平均只需要0. 707617 個錯誤,恢復8byte的密鑰只需要46個錯誤。

雖然KeeLoq算法發佈後至今已經取得很多有效的***,大大降低了計算時間複雜度,但是也增加了計算空間複雜度,並且需要一定數量的已知前提。導致在實際密碼破解過程中難度係數高,故其安全性足以保證,在實際應用當中有着廣泛應用。

KeeLoq算法的改進

KeeLoq加密算法是4byte的分組密碼,密鑰長度較短,僅爲8byte。算法發佈至今已有相關文獻報導取得有效***,針對這一現狀及上述算法不足之處,爲了進一步提高 KeeLoq算法安全性,本文分別對編解碼過程及密鑰管理進行改進。

編解碼過程的改進

首先,編解碼過程借鑑了三重數據加密算法(3DES,Triple Data Encryption Algorithm),提出三重KeeLoq算法。即採用三個不同密鑰分別對明文、第一次獲得密文及第二次獲得密文進行KeeLoq加密後,生成最終密文。解密過程則是,先用第三個密鑰對最終密文解密得到第二次密文,用第二個密鑰對第二次密文解密得到第一次密文,最後用第一個密鑰對第一次密文解密得到原始明文。該過程是KeeLoq算法的一種更安全的變形,通過增加KeeLoq的密鑰長度來提高針對KeeLoq***的時間複雜度和計算複雜度,以進一步提高其安全性。

KeeLoq密鑰管理機制的改進

以Microchip的KeeLoq密鑰管理機制爲例。Microchip定義了一套學習模式來管理密鑰。學習模式目的是爲了編碼端與解碼端進行配對。在學習過程中,首先按下學習按鍵之後,解碼端解碼密文後,校驗功能鍵和識別碼正確後,就將該編碼端的同步計數值和序列號存到自身的非易失性存儲空間,這樣就表示學習成功。學習成功之後,編碼端才能與解碼端正常通信。

Microchip總共提出了三種學習模式(密鑰管理機制)。第一種:簡易學習模式。簡易學習模式無法修改密鑰,一旦密鑰泄露,後果無法挽回。

第二種:標準學習模式。標準學習模式在學習過程中可同時按下全部或多個功能按鈕,則該模式將原始密鑰和編碼端的序列號通過某種算法生成新的密鑰,解碼端接收到密文後,通過原始密鑰解密不正確之後,就會嘗試用原始密鑰+序列號生成新的密鑰來解密密文,一旦解密成功,解碼端就會在自身的非易失性存儲空間內將密鑰更改掉。很明顯,標準學習模式的安全性就比簡易學習模式高了許多,原始密鑰和序列號同時泄漏的難度明顯比單單僅原始密鑰泄漏的難度高了多。

第三種:安全學習模式。安全學習模式比標準學習模式多了一個種子碼,學習過程與標準模式差不多,差別僅在於標準模式生成新密鑰的方式是:原始密鑰+序列號。而安全學習模式是:原始密鑰+種子碼+序列號通過某種算法生成新的密鑰。

三種學習模式,其安全性一層比一層高。簡易學習模式的安全性完全依賴於密鑰不丟失,標準學習模式的安全性依賴於密鑰和序列號不同時丟失,安全學習模式的安全性依賴於密鑰、種子碼和序列號不同時丟失。當然了生成新密鑰的算法也不能丟失。

說到這裏,我們再說說密鑰管理機制的改進,其實有很多種。比如我們可以有一個手動輸入的種子碼讓其放於固定碼中發送出去,這樣就如我們QQ或者Google帳號被盜竊後,直接手動修改密碼那般愜意。或者如果無法做到手動修改,我們可以做一個隨機生成器,隨機生成一個種子碼,這樣完全不必擔心種子碼在哪個環節會泄漏,因爲我們自己都不知道種子碼會是多少。總的來說,密鑰管理機制的改進取決於KeeLoq算法的應用環境中,視具體情況而定。當然Microchip已經做的非常好了。但是凡事沒有盡善盡美,哪怕我們可以做出一點點的進步。

附KeeLoq算法源碼

源碼一:KeeLoq.c (32位處理器)

[cpp] view plain copy
include<iostream>
include<stdio.h>
using namespace std;
unsigned long long int SERX=0xefcdab2143658709;
unsigned long long int key=0xefcdab2143658709;
unsigned int HOPE;
unsigned int c=0;
int NLF[2][2][2][2][2];
int getBit(unsigned long long int source,int n) {
unsigned long long int temp0=((long long int) 1<<n);
unsigned long long int temp1=source&temp0;
if ( temp1 != 0) {
return 1;
}
return 0;
}
void RrcHOPE(){
if(c!=0){
HOPE=(HOPE>>1)|0x80000000;
}else{
HOPE=(HOPE>>1)&0x7fffffff;
}
}
void RlcHOPE(){
if(c!=0){
HOPE=(HOPE<<1)|1;
}else{
HOPE=(HOPE<<1)&0xFFFFFFFE;
}
}

void CRYPT() {
//key=SERX;
for (int i = 0; i < 528; i++) {
int nlf=NLF[getBit(HOPE, 31)][getBit(HOPE, 26)][getBit(HOPE, 20)][getBit(HOPE, 9)][getBit(HOPE, 1)];
int y16=getBit(HOPE, 16);
int y0=getBit(HOPE, 0);
int k=getBit(key, i%64);
int result=nlf^y16^y0^k;
if (result!=0) {
c=1;
}else {
c=0;
}

RrcHOPE();
}
}

void DECRYPT() {
key=SERX;
for (int i = 528; i >0; i--) {
int nlf=NLF[getBit(HOPE, 30)][getBit(HOPE, 25)][getBit(HOPE, 19)][getBit(HOPE, 8)][getBit(HOPE, 0)];
int y15=getBit(HOPE, 15);
int y31=getBit(HOPE, 31);
int k=getBit(key, (i-1)%64);
int result=nlf^y15^y31^k;
if (result!=0) {
c=1;
}else {
c=0;
}
//printf("step %d : %x %x %x %x %x %x\n",i,HOPE,nlf,y15,y31,k,result);
RlcHOPE();
}
}

int main(){
NLF[0][0][0][0][0]=0;
NLF[0][0][0][0][1]=1;
NLF[0][0][0][1][0]=1;
NLF[0][0][0][1][1]=1;
NLF[0][0][1][0][0]=0;
NLF[0][0][1][0][1]=1;
NLF[0][0][1][1][0]=0;
NLF[0][0][1][1][1]=0;

NLF[0][1][0][0][0]=0;
NLF[0][1][0][0][1]=0;
NLF[0][1][0][1][0]=1;
NLF[0][1][0][1][1]=0;
NLF[0][1][1][0][0]=1;
NLF[0][1][1][0][1]=1;
NLF[0][1][1][1][0]=1;
NLF[0][1][1][1][1]=0;

NLF[1][0][0][0][0]=0;
NLF[1][0][0][0][1]=0;
NLF[1][0][0][1][0]=1;
NLF[1][0][0][1][1]=1;
NLF[1][0][1][0][0]=1;
NLF[1][0][1][0][1]=0;
NLF[1][0][1][1][0]=1;
NLF[1][0][1][1][1]=0;

NLF[1][1][0][0][0]=0;
NLF[1][1][0][0][1]=1;
NLF[1][1][0][1][0]=0;
NLF[1][1][0][1][1]=1;
NLF[1][1][1][0][0]=1;
NLF[1][1][1][0][1]=1;
NLF[1][1][1][1][0]=0;
NLF[1][1][1][1][1]=0;

while(1){
scanf("%x",&HOPE);
CRYPT();
printf("%x\n",HOPE);
DECRYPT();
printf("%x\n",HOPE);

}
return 0;
}

源碼二:KeeLoq.h+KeeLoq.c(8位微機)
[cpp] view plain copy
unsigned char SERX[]={0XEF,0XCD,0XAB,0X21,0X43,0X65,0X87,0X09};
unsigned char key[]={0X09,0X87,0X65,0X43,0X21,0XAB,0XCD,0XEF};
unsigned char NLF[2][2][2][2][2];

unsigned char getBit(unsigned char source[],int n);
unsigned char RRC(unsigned char source[],char c,char n);
unsigned char
RLC(unsigned char source[],char c,char n);
unsigned char CRYPT(unsigned char source);
unsigned char DECRYPT(unsigned char source);
void init();

[cpp] view plain copy
include<stdio.h>
include"KeeLoq.h"

/*****

@param source
@param n
@return source的第n個位數

***/
unsigned char getBit(unsigned char source[],int n)
{
unsigned char temp0=(unsigned char)1<<(n%8);
unsigned char temp1=source[n/8]&temp0;
if(temp1!=0)
{
return 1;
}
return 0;
}

/***

@param source[]
@param c 進位標誌位
@param n 數組長度
@return source數組帶進位右移

****/
unsigned char * RRC(unsigned char source[],char c,char n)
{
int i=0;
unsigned char temp;
for(i=n-1;i>=0;i--)
{
temp=source[i];
if(c!=0){
source[i]=(source[i]>>1)|0x80;
}else{
source[i]=(source[i]>>1)&0x7f;
}

if(temp&0x01!=0){
c=1;
}else{
c=0;
}
}
return source;
}

/*****

@param source[]
@param c 進位標誌位
@param n 數組長度
@return source數組帶進位左移

**/
unsigned char * RLC(unsigned char source[],char c,char n)
{
int i=0;
unsigned char temp;
for(i=0;i<n;i++)
{
temp=source[i];
if(c!=0){
source[i]=(source[i]<<1)|0x01;
}else{
source[i]=(source[i]<<1)&0xfe;
}

if((temp&0x80)!=0){
c=1;
}else{
c=0;
}
}
return source;
}

/*****

@param source
@return source明文經過KeeLoq加密後的密文

**/
unsigned char CRYPT(unsigned char source)
{
int i=0;
unsigned char c=0;
unsigned char nlf,y16,y0,k,result;
init();
for (i = 0; i < 528; i++)
{
nlf=NLF[getBit(source, 31)][getBit(source, 26)][getBit(source, 20)][getBit(source, 9)][getBit(source, 1)];
y16=getBit(source, 16);
y0=getBit(source, 0);
k=getBit(key, i%64);
result=nlf^y16^y0^k;
if (result!=0) {
c=1;
}else {
c=0;
}
source=RRC(source,c,4);
}
return source;
}

/*****

@param source
@return source密文經過KeeLoq解密後的明文

**/
unsigned char DECRYPT(unsigned char source)
{
int i=0;
unsigned char c=0;
unsigned char nlf,y15,y31,k,result;
init();
for (i = 528; i >0; i--)
{
nlf=NLF[getBit(source, 30)][getBit(source, 25)][getBit(source, 19)][getBit(source, 8)][getBit(source, 0)];
y15=getBit(source, 15);
y31=getBit(source, 31);
k=getBit(key, (i-1)%64);
result=nlf^y15^y31^k;
if (result!=0) {
c=1;
}else {
c=0;
}
source=RLC(source,c,4);
}
return source;
}

/*****

初始化非線性邏輯函數的值

**/
void init()
{
NLF[0][0][0][0][0]=0;
NLF[0][0][0][0][1]=1;
NLF[0][0][0][1][0]=1;
NLF[0][0][0][1][1]=1;
NLF[0][0][1][0][0]=0;
NLF[0][0][1][0][1]=1;
NLF[0][0][1][1][0]=0;
NLF[0][0][1][1][1]=0;

NLF[0][1][0][0][0]=0;
NLF[0][1][0][0][1]=0;
NLF[0][1][0][1][0]=1;
NLF[0][1][0][1][1]=0;
NLF[0][1][1][0][0]=1;
NLF[0][1][1][0][1]=1;
NLF[0][1][1][1][0]=1;
NLF[0][1][1][1][1]=0;

NLF[1][0][0][0][0]=0;
NLF[1][0][0][0][1]=0;
NLF[1][0][0][1][0]=1;
NLF[1][0][0][1][1]=1;
NLF[1][0][1][0][0]=1;
NLF[1][0][1][0][1]=0;
NLF[1][0][1][1][0]=1;
NLF[1][0][1][1][1]=0;

NLF[1][1][0][0][0]=0;
NLF[1][1][0][0][1]=1;
NLF[1][1][0][1][0]=0;
NLF[1][1][0][1][1]=1;
NLF[1][1][1][0][0]=1;
NLF[1][1][1][0][1]=1;
NLF[1][1][1][1][0]=0;
NLF[1][1][1][1][1]=0;
}

int main()
{
unsigned char source[4];
unsigned char *p;
scanf("%c %c %c %c",&source[3],&source[2],&source[1],&source[0]);

p=source;

printf("%x %x %x %x\n",p[3],p[2],p[1],p[0]);

p=CRYPT(p);

printf("%x %x %x %x\n",p[3],p[2],p[1],p[0]);

p=DECRYPT(p);

printf("%x %x %x %x",p[3],p[2],p[1],p[0]);

return 0;
}

源碼三:KeeLoqCrypt.java
[java] view plain copy
import java.util.Scanner;

public class KeeLoqCrypt {

/**
@param args
/
private static long SERX=0xefcdab2143658709L;
private static long key=0Xefcdab2143658709L;
private static int NLF[][][][][]=new int[2][2][2][2][2];

/*****
構造函數
/
KeeLoqCrypt(){
NLF[0][0][0][0][0]=0;
NLF[0][0][0][0][1]=1;
NLF[0][0][0][1][0]=1;
NLF[0][0][0][1][1]=1;
NLF[0][0][1][0][0]=0;
NLF[0][0][1][0][1]=1;
NLF[0][0][1][1][0]=0;
NLF[0][0][1][1][1]=0;

NLF[0][1][0][0][0]=0;
NLF[0][1][0][0][1]=0;
NLF[0][1][0][1][0]=1;
NLF[0][1][0][1][1]=0;
NLF[0][1][1][0][0]=1;
NLF[0][1][1][0][1]=1;
NLF[0][1][1][1][0]=1;
NLF[0][1][1][1][1]=0;

NLF[1][0][0][0][0]=0;
NLF[1][0][0][0][1]=0;
NLF[1][0][0][1][0]=1;
NLF[1][0][0][1][1]=1;
NLF[1][0][1][0][0]=1;
NLF[1][0][1][0][1]=0;
NLF[1][0][1][1][0]=1;
NLF[1][0][1][1][1]=0;

NLF[1][1][0][0][0]=0;
NLF[1][1][0][0][1]=1;
NLF[1][1][0][1][0]=0;
NLF[1][1][0][1][1]=1;
NLF[1][1][1][0][0]=1;
NLF[1][1][1][0][1]=1;
NLF[1][1][1][1][0]=0;
NLF[1][1][1][1][1]=0;
}

/*****

@param source
@param n
@return source的第n個位數
/
private static int getBit(long source,int n) {
long temp0=((long) 1<<n);
long temp1=source&temp0;
if ( temp1 != 0) {
return 1;
}
return 0;
}

/****

@param soucre
@param c
@return 帶進位右移
/
private static int RRC(int soucre,int c){
if(c!=0){
soucre=(soucre>>1)|0x80000000;
}else{
soucre=(soucre>>1)&0x7fffffff;
}
return soucre;
}

/****

@param source
@param c
@return 帶進位左移
/
private static int RLC(int source, int c){
if(c!=0){
source=(source<<1)|1;
}else{
source=(source<<1)&0xFFFFFFFE;
}
return source;
}

/****

@param source
@param key
@return source經過key密鑰進行KeeLoq加密後的數據
/
private static int CRYPT(int source , long key) {
long mKey=key;
int c;
for (int i = 0; i < 528; i++) {
int nlf=NLF[getBit(source, 31)][getBit(source, 26)][getBit(source, 20)][getBit(source, 9)][getBit(source, 1)];
int y16=getBit(source, 16);
int y0=getBit(source, 0);
int k=getBit(mKey, i%64);
int result=nlf^y16^y0^k;
if (result!=0) {
c=1;
}else {
c=0;
}
source=RRC(source,c);
}
return source;
}

/*****

@param source
@param key
@return source經過key密鑰進行KeeLoq解密後的數據
/
private static int DECRYPT(int source , long key) {
long mkey=key;
int c;
for (int i = 528; i >0; i--) {
int nlf=NLF[getBit(source, 30)][getBit(source, 25)][getBit(source, 19)][getBit(source, 8)][getBit(source, 0)];
int y15=getBit(source, 15);
int y31=getBit(source, 31);
int k=getBit(mkey, (i-1)%64);
int result=nlf^y15^y31^k;
if (result!=0) {
c=1;
}else {
c=0;
}
source=RLC(source,c);
}
return source;
}

public static void main(String[] args) {
// TODO Auto-generated method stub
NLF[0][0][0][0][0]=0;
NLF[0][0][0][0][1]=1;
NLF[0][0][0][1][0]=1;
NLF[0][0][0][1][1]=1;
NLF[0][0][1][0][0]=0;
NLF[0][0][1][0][1]=1;
NLF[0][0][1][1][0]=0;
NLF[0][0][1][1][1]=0;

NLF[0][1][0][0][0]=0;
NLF[0][1][0][0][1]=0;
NLF[0][1][0][1][0]=1;
NLF[0][1][0][1][1]=0;
NLF[0][1][1][0][0]=1;
NLF[0][1][1][0][1]=1;
NLF[0][1][1][1][0]=1;
NLF[0][1][1][1][1]=0;

NLF[1][0][0][0][0]=0;
NLF[1][0][0][0][1]=0;
NLF[1][0][0][1][0]=1;
NLF[1][0][0][1][1]=1;
NLF[1][0][1][0][0]=1;
NLF[1][0][1][0][1]=0;
NLF[1][0][1][1][0]=1;
NLF[1][0][1][1][1]=0;

NLF[1][1][0][0][0]=0;
NLF[1][1][0][0][1]=1;
NLF[1][1][0][1][0]=0;
NLF[1][1][0][1][1]=1;
NLF[1][1][1][0][0]=1;
NLF[1][1][1][0][1]=1;
NLF[1][1][1][1][0]=0;
NLF[1][1][1][1][1]=0;

Scanner mScanner=new Scanner(System.in);
int HOPE=mScanner.nextInt(16);
System.out.printf("%x\n",CRYPT(HOPE,key));
System.out.printf("%x\n",DECRYPT(CRYPT(HOPE,key),key));
System.out.printf("%x\n",getBit(HOPE,1));
}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章