2018/08/27

微型加密演算法 Tiny Encryption Algorithm (TEA)

Tiny Encryption Algorithm (TEA) 微型加密演算法是劍橋大學電腦實驗室的 David Wheeler 與 Roger Needham 在 1994年於 Fast Software Encryption workshop 發表的加密演算法。因為該演算法速度快,很容易實作,也很容易設計電路,因此常常被使用。

這個演算法在內地的很有名氣,主要因為騰訊 QQ 大量使用了這個演算法,進行資料加密的工作。

tea.c

原本找到的文件,提供了一個 C 語言的版本。因為 TEA 是針對 byte array 進行加解密,實作時要注意,是使用哪一種 CPU,如果是 Intel CPU,都是 Little Endian 的 byte order。

#include <stdio.h>
#include <stdint.h>

void encipher(unsigned long *const v,unsigned long *const w,
   const unsigned long *const k)
{
   register unsigned long y=v[0], z=v[1], sum=0;
   register unsigned long delta= 0x9E3779B9;
   register unsigned long a=k[0], b=k[1], c=k[2], d=k[3];
   register unsigned long n=32;

   while(n-->0)
      {
      sum += delta;
      y += (z << 4)+a ^ z+sum ^ (z >> 5)+b;
      z += (y << 4)+c ^ y+sum ^ (y >> 5)+d;
      }

   w[0]=y; w[1]=z;
}

void decipher(unsigned long *const v,unsigned long *const w,
   const unsigned long *const k)
{
   register unsigned long y=v[0], z=v[1];
   register unsigned long delta=0x9E3779B9;
   register unsigned long sum= delta * 32;
   register unsigned long a=k[0],b=k[1], c=k[2], d=k[3];
   register unsigned long n=32;

   /* sum = delta<<5, in general sum = delta * n */

   while(n-->0)
      {
      z -= (y << 4)+c ^ y+sum ^ (y >> 5)+d;
      y -= (z << 4)+a ^ z+sum ^ (z >> 5)+b;
      sum -= delta;
      }

   w[0]=y; w[1]=z;
}


#define htonl(A) ((((unsigned long)(A) & 0xff000000) >> 24) | \
    (((unsigned long)(A) & 0x00ff0000) >> 8) | \
    (((unsigned long)(A) & 0x0000ff00) << 8) | \
    (((unsigned long)(A) & 0x000000ff) << 24))

#define ntohl htonl
#define ENCRYPTED_VALUE_POSITION_IN_CONN_REQ 16

void printlong(unsigned long l) {
    unsigned long nl = ntohl(l);
    printf("%lX ", nl);
}

int main() {
    unsigned long u_auth_key[4] = {0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00};
    char random_num[8] = {0x72, 0x13, 0x8F, 0x90, 0x57, 0x68, 0x29, 0xaa };

    unsigned long encrypted_num[2] = {0};
    unsigned long * p_rand_num = (unsigned long *) random_num;
    char xnl_conn_req[24] = {0};
    // 由 big endiness 轉為 little
    *p_rand_num = ntohl(*p_rand_num);
    *( p_rand_num + 1) = ntohl(*(p_rand_num + 1));

    encipher(p_rand_num, encrypted_num, u_auth_key);
    // 由 little 轉為 big
    encrypted_num[0] = htonl(encrypted_num[0]);
    encrypted_num[1] = htonl(encrypted_num[1]);

    // printf("%lx %lx\n", encrypted_num[0], encrypted_num[1]);
    printlong(encrypted_num[0]);
    printlong(encrypted_num[1]);

}

但是編譯後發現,加密後的結果是錯誤的。原因在於 unsigned long 這個資料型別,因為該資料型別在 64位元 CPU 的機器上,資料長度超過 32 bits。解決方式是在編譯時,要加上 -m32 這個參數。

$ gcc -m32 tea.c
$ ./a.out
C5E65710 A9CA5E6F

tea2.c

TEA Wiki 也提供了一個 C 語言的加解密版本,可以注意到他使用了 uint32_t 這個資料型別,uint32_t 在 UNIX 系統上,是固定為 32-bit unsigned interger,不管 CPU 是 32bits 或是 64bits 都一樣,所以使用這個資料型別會比 long 來得好。

tea2.c

#include <stdio.h>
#include <stdint.h>

void encrypt(uint32_t* v, uint32_t key[4], uint32_t delta) {
  uint32_t v0=v[0], v1=v[1], sum=0, i;             // set up
  // uint32_t delta=0x12345678;                    // a key schedule constant
  for (i=0; i < 32; i++) {
    sum += delta;
    v0 += ((v1<<4) + key[0]) ^ (v1 + sum) ^ ((v1>>5) + key[1]);
    v1 += ((v0<<4) + key[2]) ^ (v0 + sum) ^ ((v0>>5) + key[3]);
  }
  v[0]=v0; v[1]=v1;
}

void decrypt (uint32_t* v, uint32_t key[4], uint32_t delta) {
    uint32_t v0=v[0], v1=v[1], i;  // set up
    // uint32_t sum=0x468ACF00;
    // uint32_t delta=0x12345678;  // a key schedule constant
    uint32_t sum = delta * 32;  // delta << 5

    // printf("%X %X %X\n", v0, v1, sum);
    for (i=0; i<32; i++) {
      v1 -= ((v0<<4) + key[2]) ^ (v0 + sum) ^ ((v0>>5) + key[3]);
      v0 -= ((v1<<4) + key[0]) ^ (v1 + sum) ^ ((v1>>5) + key[1]);
      sum -= delta;
      // printf("%X %X %X\n", v0, v1, sum);
    }
    v[0]=v0; v[1]=v1;
}

int main()
{
    uint32_t v[] = {0x72138F90, 0x576829AA};

    uint32_t key[4]={0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00};
    uint32_t delta=0x9E3779B9;

    printf("Original Values: ");
    printf("%X %X\n", v[0], v[1]);

    encrypt(v, key, delta);

    printf("Encrypted      : ");
    printf("%X %X\n", v[0], v[1]);

    decrypt(v, key, delta);
    printf("Decrypted      : ");
    printf("%X %X\n", v[0], v[1]);
    return 0;
}

執行結果如下

$ gcc tea2.c
$ ./a.out
Original Values: 72138F90 576829AA
Encrypted      : C5E65710 A9CA5E6F
Decrypted      : 72138F90 576829AA

tea.py

另外如果是 python,可以參考 C 語言的寫法,直接用 ctypes 進行開發。

tea.py

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import sys
from ctypes import *

def encipher(v, k):
    y = c_uint32(v[0])
    z = c_uint32(v[1])
    sum = c_uint32(0)
    delta = c_uint32(0x9E3779B9).value
    n = 32
    w = [0,0]

    while(n>0):
        sum.value += delta
        y.value += ( z.value << 4 ) + k[0] ^ z.value + sum.value ^ ( z.value >> 5 ) + k[1]
        z.value += ( y.value << 4 ) + k[2] ^ y.value + sum.value ^ ( y.value >> 5 ) + k[3]
        n -= 1

    w[0] = y.value
    w[1] = z.value
    return w

def decipher(v, k):
    y = c_uint32(v[0])
    z = c_uint32(v[1])
    delta = c_uint32(0x9E3779B9).value
    sum = c_uint32(delta * 32)
    n = 32
    w = [0,0]

    while(n>0):
        z.value -= ( y.value << 4 ) + k[2] ^ y.value + sum.value ^ ( y.value >> 5 ) + k[3]
        y.value -= ( z.value << 4 ) + k[0] ^ z.value + sum.value ^ ( z.value >> 5 ) + k[1]
        sum.value -= delta
        n -= 1

    w[0] = y.value
    w[1] = z.value
    return w

if __name__ == "__main__":
    key = [0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00]
    v = [0x72138F90, 0x576829AA]

    enc = encipher(v,key)
    print "Original Values: " + ' '.join([format(i, 'x').upper() for i in v])

    print "Encrypted:       " + ' '.join([format(i, 'x').upper() for i in enc])

    dec = decipher(enc,key)
    print "Decrypted:       " + ' '.join([format(i, 'x').upper() for i in dec])

執行結果

$ python tea.py
Original Values: 72138F90 576829AA
Encrypted:       C5E65710 A9CA5E6F
Decrypted:       72138F90 576829AA

TEA.java

這是 Java 語言的版本

public class TEA {

    public static int[] encrypt(int[] block, int[] key) {
        int i = block[0];
        int j = block[1];
        int sum = 0;
        int delta = 0x9E3779B9;

        for (int k = 0; k < 32; ++k) {
            sum += delta;
            i += (j << 4 & 0xfffffff0) + key[0] ^ j + sum ^ (j >> 5 & 0x7ffffff) + key[1];
            j += (i << 4 & 0xfffffff0) + key[2] ^ i + sum ^ (i >> 5 & 0x7ffffff) + key[3];
        }

        block[0] = i;
        block[1] = j;

        return block;
    }

    public static int[] decrypt(int[] block, int[] key) {
        int i = block[0];
        int j = block[1];
        int delta = 0x9E3779B9;
        int sum = delta*32;   // sum=468ACF00
        // System.out.println("sum="+byteToUnsignedHex(sum));

        // System.out.println("i, j, sum="+byteToUnsignedHex(i)+", "+byteToUnsignedHex(j)+", "+byteToUnsignedHex(sum));
        for (int k = 0; k < 32; ++k) {
            j -= (i << 4 & 0xfffffff0) + key[2] ^ i + sum ^ (i >> 5 & 0x7ffffff) + key[3];
            i -= (j << 4 & 0xfffffff0) + key[0] ^ j + sum ^ (j >> 5 & 0x7ffffff) + key[1];
            sum -= delta;

            // System.out.println("i, j, sum="+byteToUnsignedHex(i)+", "+byteToUnsignedHex(j)+", "+byteToUnsignedHex(sum));
        }

        block[0] = i;
        block[1] = j;

        return block;
    }


    public static String byteToUnsignedHex(int i) {
        String hex = Integer.toHexString(i).toUpperCase();
        while(hex.length() < 8){
            hex = "0" + hex;
        }
        return hex;
    }

    public static String intArrToHex(int[] arr) {
        StringBuilder builder = new StringBuilder(arr.length * 8 + arr.length-1);
        for (int b : arr) {
            builder.append(byteToUnsignedHex(b));
            builder.append(" ");
        }
        return builder.toString();
    }

    public static void main(String[] args){
        int[] key = new int[]{0x11223344, 0x55667788, 0x99AABBCC, 0xDDEEFF00};
        int[] data = new int[]{0x72138F90, 0x576829AA};
        System.out.println("Original Values: "+intArrToHex(data));

        int[] enc = TEA.encrypt(data, key);
        System.out.println("Encrypted      : "+intArrToHex(enc));

        int[] dec = TEA.decrypt(enc, key);
        System.out.println("Decrypted      : "+intArrToHex(dec));
    }
}

執行結果

$ javac TEA.java
$ java TEA
Original Values: 72138F90 576829AA
Encrypted      : C5E65710 A9CA5E6F
Decrypted      : 72138F90 576829AA

References

Tiny Encryption Algorithm

Java/J2ME implementation of the Tiny Encryption Algorithm

TEA、XTEA、XXTEA加密解密算法

介紹XXTEA加密算法

XXTEA encryption arithmetic library

TEA和QQTEA

TEA加密算法java版

一個長整數各自表述 (in 64-bit system)

Tiny-Encryption-Algorithm

TEA算法在QQ中的應用

2018/08/20

Lua Support in Wireshark

wireshark 是網路封包解析工具,但如果是新的或是自己開發的 protocol,就必須要自行開發封包解析的 plugin,讓 wireshark 也能看得懂新的協定的封包。

wireshark 是以 C 開發的,所以可以用 C 語言開發 plugin,但還有另一個比較快速的方式,就是使用 lua script 開發,Lua 在 wireshark 可支援撰寫 dissectors, post-dissectors and taps。

dissector 就是封包解析的 plugin,雖然可以用 lua 快速開發 dissector,但是執行速度還是比 C 語言開發的 plugin 慢。不過如果要快速完成 dissector 開發,lua 還是一個好的選擇,畢竟不需要花時間 compile, debug C dissector。

post-dissectors 是在每一個 dissector 執行後,可以運作的 plugin,可用來增加 dissection tree,建立自訂的 filtering mechanism。

taps 是針對被 dissected 的封包,用來收集資訊的 plugin。

installation

由 wireshark 官網下載安裝套件安裝後,在 About Wireshark 選單中,可查閱 wireshark 的資訊。如果在中間有看到 "with Lua 5.2.4" 的字串,就表示這個 wireshark 有支援 lua。

另外,要查閱 Folders 頁籤,要注意 Personal Lua Plugins, Global Lua Plugins, Global Configuration 的路徑

Global Configuration: /Applications/Wireshark.app/Contents/Resources/share/wireshark

Personal Lua Plugins: /Users/charley/.local/lib/wireshark/plugins

Global Lua Plugins: /Applications/Wireshark.app/Contents/PlugIns/wireshark

安裝 plugin

有三種方式

  1. 放在 Personal Lua Plugins 目錄中
  2. 修改 init.lua,以 dofile() 載入 lua script
  3. 在啟動 wireshark 的 command line 中,加上 "-X lua_script:pcap_file.lua" 參數

如果已經做好了 plugin,可以直接放在 Personal Lua Plugins 目錄中,複製進去後,就可以在 About Wireshark -> Plugins 頁籤,看到那些 lua script。

第二種方式,先找到 init.lua 的存放位置,在這台機器上是放在 Global Configuration 目錄中。

編輯 init.lua,可看到最後面是 dofile(DATA_DIR.."console.lua")

# 備份 init.lua
cp /Applications/Wireshark.app/Contents/Resources/share/wireshark/init.lua /Applications/Wireshark.app/Contents/Resources/share/wireshark/init.lua.bak

vi /Applications/Wireshark.app/Contents/Resources/share/wireshark/init.lua

.....
dofile(DATA_DIR.."console.lua")
--dofile(DATA_DIR.."dtd_gen.lua")

要注意 init.lua 的最前面 disable_lua 必須要是 false

disable_lua = false

if disable_lua then
    return
end

在 init.lua 最後面加上

LUA_SCRIPT_PATH="/Users/charley/.wireshark/lua/"
dofile(LUA_SCRIPT_PATH.."hello.lua")

編輯 hello.lua vi /Users/charley/.wireshark/lua/hello.lua

-- hello.lua
print("hello world!")

以 command line wireshark 測試

$ tshark -x lua_script:hello.lua
hello world!
Capturing on 'FireWire'

References

使用 wireshark 的 lua script

Lua編寫Wireshark插件實戰

Lua

Chapter 10. Lua Support in Wireshark

2018/08/13

如何編譯 RPi kernel

以下紀錄如何編譯 Raspberry Pi Kernel

查詢 RPi 上安裝的 kernel 版本號碼

$ uname -a
Linux raspberrypi 4.14.34-v7+ #1110 SMP Mon Apr 16 15:18:51 BST 2018 armv7l GNU/Linux

找一台 x86 linux 電腦,以 cross-compile 方式重新編譯 RPi kernel,雖然可以直接在 RPi 編譯,但速度會很慢,因此還是選擇用 cross-compile 方式編譯。

RPi kernel 存放在 git repository 裡面

cd
mkdir raspberry
cd raspberry

確認有安裝 git, 並更新一些套件(需要安裝nss 相關和更新curl)

yum update git
yum update nss nss-util nspr
yum update curl

下載 kernel source

git clone https://github.com/raspberrypi/linux.git

下載 cross-compilers tools

git clone https://github.com/raspberrypi/tools

使用 tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian,設定環境變數 CCPREFIX

export CCPREFIX=/root/raspberry/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-

${CCPREFIX}gcc -v

在 GitHub 上面的 kernel 有許多不同的 branch,請選擇你想要使用的 branch 來使用。

cd linux
git branch -a

目前版本為

* rpi-4.14.y
  remotes/origin/HEAD -> origin/rpi-4.14.y

由 RPi 取得目前的 kernel 設定,因為 RPi 上沒有 config.gz,用以下指令產生 /proc/config.gz

sudo modprobe configs

將 /proc/config.gz 複製到要編譯 kernel 的機器上的 /root/raspberry/linux 目錄中

cd /root/raspberry/linux
scp pi@192.168.1.15:/proc/config.gz . 
gunzip -c config.gz > .config

用以下指令產生新的 kernel configuration,如果遇到新的功能,就會問你要不要支援

ARCH=arm CROSS_COMPILE=${CCPREFIX} make oldconfig

reset .config 裡面的 debug symbols,Enable the ‘DEBUGINFO’ option and don’t enable the DEBUGINFO_REDUCED option.

grep -v DEBUG_INFO < .config > newconfig 
mv newconfig .config 
ARCH=arm CROSS_COMPILE=${CCPREFIX} make oldconfig

以 menuconfig 微調設定,調整 kernel modules support。

make -j4 ARCH=arm CROSS_COMPILE=${CCPREFIX} menuconfig

編譯 kernel,如果是 multi-core cpu,可加上 -j<amount of cores> 加速編譯

ARCH=arm CROSS_COMPILE=${CCPREFIX} make

如果編譯過程中,有發生 compile error,打開錯誤的 source code,在發生錯誤的 function 前面加上

#pragma GCC optimize("-g0")

編譯完成後,必須檢查 symbols,用以下指令,查看 initutsns.name.release 的值

${CCPREFIX}gdb vmlinux 
print init_uts_ns.name.release

確認 kernel 有包含 symbols 後,將 RPi 需要的 modules 放在 modules 目錄

ARCH=arm CROSS_COMPILE=${CCPREFIX} INSTALL_MOD_PATH=../modules make modules_install

建立 uncompressed kernel image,並放到 RPi 的 tmp 目錄中

cd <raspberry pi downloads>/tools/mkimage 
./imagetool-uncompressed.py ../../linux/arch/arm/boot/zImage
scp kernel.img pi@raspberrypi:/tmp

將 modules 目錄,壓縮後,傳送到 RPi 的 tmp 目錄中

cd <raspberry pi downloads>/tools/modules 
tar czf modules.tgz * 
scp modules.tgz pi@raspberrypi:/tmp

最後 ssh 到 RPi,安裝新的 kernel 及 modules

cd / 
sudo mv /tmp/kernel.img /boot/ 
sudo tar xzf /tmp/modules.tgz 
rm /tmp/modules.tgz

reboot RPi

sudo shutdown -r now

以 uname 檢查是否使用了新版的 kernel

uname -r

如果要 debug kernel ,可參考 Preparing Raspberry PI for JTAG Debugging 的說明

References

https://sysprogs.com/VisualKernel/tutorials/raspberry/buildkernel/

https://blog.gtwang.org/iot/raspberry-pi-compile-linux-kernel/

https://www.raspberrypi.com.tw/528/compiling-kernel-for-raspberry-pi/

https://coldnew.github.io/f5873b3f/