2009年12月25日 星期五

基本密碼學

在網路通訊上
密碼學可以提供以下幾點安全防護

1. Keeping secrets (Confidentiality)
在將原始文件加密的情況下
可避免通訊內容被惡意的第三方竊聽 (eavesdropping)

2. Providing identity (Authentication)
文件可以加密達到保護內容不被竊聽的效果
可是如果惡意第三方不是以竊聽的方式
而是以假冒(Forgery and masquerade)的方式攻擊通訊呢
此時就需要身份認證的功能
以確定彼此通訊的雙方是想要通訊的對象

3. Verifying information (Message integrity)
假設惡意第三方有辦法更改通訊內容
造成通訊雙方的誤解
此時就需要驗證通訊內容的完整性
一般我們都是使用雜湊函數
來計算文件的message digest

--------
密碼學的加解密又分為兩個主軸
Secret Key Cryptography 和 Public Key Cryptography

1. Secret Key Cryptography (Symmetric)

此種加解密方式因為通訊雙方使用同一把key
所以又稱為對稱式加密
而這把共用的key因為不能讓通訊雙方以外的人知道
所以又稱為private key

2. Public Key Cryptography (Asymmetric)

此種加解密方式有兩把key
一把是public key,會公諸於大眾
一把是private key,只有自己能知道
由於傳送方加密的key與收受方解密的key不是同一把
因此此種加密方式又稱為非對稱式加密

以下舉一個例子來解釋public key cryptography

通訊雙方為Alice和Bob (密碼學界用到爛的梗 XD)
Alice有兩把key,分別為A.publickey以及A.privatekey
Bob也有兩把key,分別為B.publickey以及B.privatekey

因為public key是公諸於眾的
所以Alice知道B.publickey,而Bob也知道A.publickey
因此當Alice要跟Bob通訊的時候
就利用B.publickey加密她要傳輸的資料傳給Bob
而因為由B.publickey加密的內容只有B.privatekey能解
所以通訊內容也就只有擁有B.privatekey的Bob才可以看
同樣地
Bob想要回應Alice的通訊內容也可以經由A.publickey加密
這個加密的回應也只有Alice可以解密


了解了secret和public key cryptography的內容後
接下來我們來對其做個比較
並了解為何有這兩種加解密方式的需求

首先secret key cryptography (以下簡稱secret)
在加解密的效率上遠勝於public key cryptography (以下簡稱public)
但是secret有個嚴重的缺點是key的管理
使用secret的雙方需要在通訊前協議好要使用的key
但問題是這個協議的過程是沒有加密的
所以協議的內容也會被惡意的第三方竊聽走
因此之後的加解密也形同虛設
衍生出了『雞生蛋,蛋生雞』的問題

而public正好可以解決這個問題
因為加解密用的是不同的key
而加密使用的又是可以公諸於眾的public key
所以在key的管理上很安全
不過public的缺點也正好是private的優點
public加解密的效率遠遜於secret

看完以上的比較
我們就可以知道為什麼public與secret有著並存的理由
而實際的應用上
這兩著通常也是合作的
以著名的SSL為例
通訊的雙方一開始協議key的過程是使用public的方法
而key協議好之後,就開始使用secret的方法
等於是各取其優點來達成安全通訊通道的建立


[Reference]
SSL and TLS Essentials

2009年12月18日 星期五

C 的不定參數 variable argument

最近重看了一遍 K&R
發現一個陌生的議題 - variable argument

主要有以下幾個macro要搞懂

typedef char * va_list;

#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))

#define va_start(ap,v) (ap = (va_list)&v + _INTSIZEOF(v))

#define va_arg(ap,t) (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))

#define va_end(ap) (ap = (va_list)0)

va_start主要是算出不定參數的起始位置並設給 ap
之後 va_arg再利用 ap的值一一取得這些不定參數
最後 va_end將 ap歸零

#define va_start(ap,v) (ap = (va_list)&v + _INTSIZEOF(v))
這裡的 v 是函數固定參數的最後一個
以最後一個固定參數的位置再加上這個參數的大小
就可以得到下一個參數的位置
而下一個參數就是不定參數群的第一個

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
這裡的 t 是不定參數的型態
每次取完一個不定參數後
就將 ap 的位置設往下一個不定參數的起始位置

#define va_end(ap) ( ap = (va_list)0 )
這個就是簡單的歸零動作而已
在char*的型態中,0就是NULL


[Reference]
http://ehome.hifly.to/showthread.php?threadid=329

神奇的macro _INTSIZEOF( )

#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int)-1) & ~(sizeof(int)-1))

這個macro可以算出變數n的大小
而且是以 int作為 alignment
簡單說就是
變數 n所佔用的 size會被 round up到 sizeof(int) 的整數倍

例如說 /* 假設 sizeof(int) == 4 */
char n[11];
則 _INTSIZEOF(n) 就是 12
原本使用 sizeof(n) 會等於 11

short s;
則 _INTSIZEOF(s) 就是 4
原本使用 sizeof(s) 會等於 2

原理主要是利用最小的兩個bits
加上3(decimal) = 11(binary) 強迫進位
以達到alignment的效果

當sizeof(n)最小兩個bits有以下四種情況:
(此處以8個bits簡化說明)

xxxxxx00 + 00000011 = xxxxxx11 (sizeof(n) + sizeof(int)-1)
xxxxxx11 & 11111100 = xxxxxx00 & ~(sizeof(int)-1)

xxxxxx01 + 00000011 = xxxxxx00 (有進位到第3個bits)
xxxxxx00 & 11111100 = xxxxxx00

xxxxxx10 + 00000011 = xxxxxx01 (有進位到第3個bits)
xxxxxx01 & 11111100 = xxxxxx00

xxxxxx11 + 00000011 = xxxxxx10 (有進位到第3個bits)
xxxxxx10 & 11111100 = xxxxxx00

由以上式子可以看出來
最小2個bits是0的話,沒有影響
因為本來就是4的倍數
有1,2,3的成分都會被無條件round up到4
所以會造成
5 ( 4 + 1 ) 會被 round up 到 8
6 ( 4 + 2 ) 會被 round up 到 8
7 ( 4 + 3 ) 會被 round up 到 8
之後的數字可以此類推

想到這個macro的,真是個高手!

[Reference]
macro是抄來的,說明是原創哩

2009年12月16日 星期三

ioctl 函數

ioctls are typically employed to allow userspace code to communicate with hardware devices or kernel components.

ioctl是應用程式用來和驅動程式溝通的function
讓應用程式可以對某個裝置下命令
在unix/linux環境中, 裝置的存取和檔案是一樣的
一般會先用open()來取得對裝置的一個控制權
其中,open會傳回一個handle值

ioctl(handle, command, …)

前面handle跟command是必須的,後面的參數則視情況而定

其中handle就是open函式傳回來的值
command就是一個command code
command code每個值所代表的命令會因裝置而不同
完全由驅動程式來解釋command code的意義
若是有的command code還需要傳進其他參數時
就會使用第3、第4個參數了

因此使用ioctl之前,
要先看是要對那個裝置下命令
再去查那個裝置的驅動程式
支援哪些command code讓應用程式使用

而當應用程式呼叫 ioctl時
相對應的驅動程式會有一個callback function被呼叫到
這個callback function專門用來處理 ioctl傳入的command code
你呼叫ioctl時可以傳哪些command code
以及要傳哪些參數都和此callback function有關


例如
int nread;
ioctl( 0, FIONREAD, &nread);
/* 0 是表示標準輸入 standard in */
/* FIONREAD 就是 standard in 的 command code */
此函數呼叫能夠得到緩衝區裡面有多少bytes要被讀取
值就放在nread變數中
接下來就可以呼叫
nread = read( 0, buffer, nread );
來讀取緩衝區內的值


[Reference]
http://planet.nccucs.org/2008/10/19/775/
http://bbs.chinaunix.net/viewthread.php?tid=1302131

Linux 網路程式設計 -- 筆記

先引用一張Richard Stevens大師的圖
來了解一下client-server架構下的程式流程
此圖是connection-oriented的流程
以網路程式設計來說,就是使用TCP



[Server端]

a. 使用socket函數,建立socket
此步要提供socket函數三個重要的參數
Domain, Type, Protocol
Domain 決定socket底下傳輸的媒介是什麼
一般最常使用的是AF_UNIXAF_INET
Type 決定傳輸的性質是什麼
一般最常使用的是SOCK_STREAMSOCK_DGRAM
Protocol 則是決定要在(domain, type)的組合底下
要使用哪一個protocol,一般給0表示使用預設值
socket函數成功執行的話會回傳該 socket的 file descriptor

b. 使用bind函數,將建立好的socket命名(naming)
用命名這個直翻的中文名詞有點詞不達意的感覺
說穿了,建立好的socket和賦好值的address
會被bind函數bind起來 (bind有結合的意思)
int bind( int sockfd, struct sockaddr* address, size_t len);
*這裡的address在書上寫可以設定允許連進來server的客戶*
這句話似乎有點問題
因為將address->sin_addr設定為INADDR_ANY
其實是表示server上面任何一個interface都接受連線
並非表示所有的客戶都能連線
意義不同,需注意!

c. 使用listen函數,設定儲存pending requests的queue的大小
例如 listen(sockfd, 5);
注意此處設定的數字5是儲存pending requests的數目
而不是server總共可以服務多少人
也不是表示同時間可以服務多少人

d. 使用accept函數,準備接受client連線的要求
int accpet(int sockfd, struct sockaddr* client_addr, size_t len);
accept函數成功執行的話,會回傳一個socket file descriptor
以後就是利用這個socket fd跟client溝通
另外client address的資料也會存到client_addr中
這邊就是看server要不要使用這個資訊了
accept會等待連線請求而block

e. 使用read, write函數,與client進行資料的交換

f. 使用close函數,結束掉socket 的使用


[Client端]

a. 使用socket函數,建立socket
原理同server建立socket
只是這邊是建立client端自己的socket

b. 使用connect函數,連線到提供服務的 server
這個函數功能很簡單
就是對server提出連線的要求
該server的accept函數會處理我們connect函數所發出的request

c. 使用read, write函數,與 server進行資料的交換

d. 使用close函數,結束掉socket 的使用


[Reference]
Unix Network Programming Vol.1 3rd edition - Prentice Hall
Begining Linux Programming 4th edition - Wrox

2009年12月14日 星期一

Linux程式設計 - 重點整理

[Reference]
Advanced Linux Programming
http://www.advancedlinuxprogramming.com/

2009年12月10日 星期四

extern "C" 的用法

C語言本身不支援 extern "C" 這個功能
如果使用的話,會出現compile error

作者王飛用一句話概括extern "C"這個宣告(declaration)的目的:
***實現C++與C及其他語言的混合programming***

[Reference]
http://blog.csdn.net/wfwd/archive/2006/05/30/763734.aspx
http://ytshen.wikidot.com/extern-c-usage

C 的關鍵字 static 用法

static

依使用對象的不同
static可以分為以下兩種功能

1. 針對全域變數使用 (Global variable)
--讓變數的可視範圍只侷限在該檔案內
--其他檔案看不到此變數的存在

2. 針對區域變數使用 (Local variable)
--區域變數預設就是動態變數
--將變數由動態(dynamic)變數轉為靜態(static)變數
--靜態變數的壽命與動態變數不同
--靜態變數會一直存在,直到程式結束

3. 針對全域函數使用 (Global function)
--讓函數的可視範圍只侷限在該檔案內
--其他檔案看不到此函數的存在
--讓其他檔案的extern宣告失敗

[Reference]
K&R C Language 2/e

C 的關鍵字 extern 用法

extern

一般是用在外部變數(或稱為全域變數)上的
*宣告在函數內部的變數為內部變數,又稱為自動變數

主要功能是可以讓不同檔案間可以共用同一個變數
*變數宣告可以多次,宣告其存在
*變數定義只可以一次,讓程式為其配置空間

file1.cpp
---------
int a; /* 變數a的定義兼宣告 */

file2.cpp
---------
extern int a;
/* 變數a的外部宣告 */
/* 表示a在別的檔案被定義了 */

2009年12月9日 星期三

const char* p 與 char* const p 的差別

const char* p
是一個變數指標(variable pointer) p 指向 字元常數(const char)
也就是說,p可以改值,導致指向對象可以不同
但指向的對象永遠會是定值

const char* p;
const char ch1 = 'a';
const char ch2 = 'b';
p = &ch1;
p = &ch2;

---------------------------------------------------------------
char* const p
是一個常數指標(const pointer) p 指向 字元變數 (variable char)
也就是說,p不可以改值,會永遠指向同一個對象
但指向的對象可以改變內容

char ch='a';
char* const p = &ch;
/* 注意這邊初始化要在定義的時候就做了,因為p是常數,定義完之後無法改值 */
ch='b';

---------------------------------------------------------------
const char* const p
這就是以上兩項合併的結果
是一個常數指標(const pointer) p 指向 字元常數 (const char)
兩者可是都不能更動的

const char ch='a';
const char* const p = &ch;


[Reference]
http://twpug.net/docs/ccfaq/node164.html

2009年12月1日 星期二

一些make的語法筆記

=內部變數=
$?: 代表已被更新的dependencies的值
$@: 代表targets的值
$<: 代表第一個dependencies的值
$*: 代表targets所指定的檔案,不包含副檔名
$^: 代表所有dependencies的值

使用了$$,讓'$'能傳到shell中
第一個$是為了讓第二個$可以escape掉特殊意義所使用的!

Make命本身可帶有四種參數:
標誌、巨集定義、描述檔案名和目標檔案名。

其標準形式為:
make [flags] [macro definitions] [targets]
Unix系統上flags選項及其含義為:
-f file 
指定file文件為描述文件
如果file參數為"-",那麼描述文件為stdin
如果沒有"-f"參數
預設值為當前目錄下名為makefile或者名為Makefile的文件


-i   忽略指令執行返回的出錯資訊
-s   沉默模式,在執行之前不輸出相應的指令行資訊
-r   禁止使用built-in規則 (隱含規則)
-n   非執行模式,輸出所有執行指令,但並不執行
-t   更新目標文件 (touch)
-q  根據目標文件是否已經更新返回零或非零的狀態資訊
-p   輸出所有巨集定義和目標文件描述
-d   Debug模式,輸出有關文件和檢測時間的詳細資料

Linux下make標誌位的常用選項與Unix系統中稍有不同,
下面我們只列出了不同部分:
-C dir   在讀取 makefile 之前改變到指定的目錄dir
-I dir   當包含其他 makefile文件時,利用該選項指定搜尋目錄
-h     help,顯示所有的make選項
-w     在處理 makefile 之前和之後,都顯示工作目錄(default選取)


[Reference]
忘記出處了...= =...原作者,對不起 Orz

進階的 C struct 用法

這幾天在trace linux code的時候發現一個有趣的struct initialization的方法,
假設struct是定義為

struct sched_param {
/* ... */
int sched_priority;
/* ... */
};


那麼一般來說,初始化的方式是先宣告一個struct sched_param變數,
然後再用dot運算子去存取內部成員並賦予值,如下

struct sched_param sp1;
sp1.sched_param = 1;


那看到一個進階的方式是以集合的概念去處理struct的initialization

struct sched_param sp2 = {
.sched_priority = 1
};


在上述的語法中,(.) dot運算子可以抓到struct內的成員
若是struct內有多個成員的話,只需要用(,) 逗點分開即可
給值順序可以不用按照定義內的先後順序

PS.
這是GNU牛頭牌gcc的延伸C語法,非標準C語法
在它牌 c compiler 上要注意能否使用

[Reference]
http://linuxprograms.wordpress.com/2008/03/07/c-structure-initialization-advanced/