2019年4月6日 星期六
關於 linux mint 的作業系統(軟硬體)升級
set timeout=3
set default=1
hiddenmenu
menuentry "GPT Linux mint 191 Read Only" {
set root=(hd0,1)
set iso=/boot/linux191.iso
loopback loop $iso
linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$iso locale=zh_TW.UTF-8 nomodeset
initrd (loop)/casper/initrd.lz
}
menuentry "GPT Linux mint 191 iso persistent" {
set root=(hd0,1)
set iso=/boot/linux191.iso
loopback loop $iso
linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$iso locale=zh_TW.UTF-8 persistent nomodeset
initrd (loop)/casper/initrd.lz
}
menuentry "GPT Ubuntu-18.10 iso" {
set root=(hd0,1)
set iso=/boot/ubuntu-18.10-desktop-amd64.iso
linux /boot/kernel418/vmlinuz boot=casper iso-scan/filename=$iso locale=zh_TW.UTF-8
initrd /boot/kernel418/initrd
}
menuentry "GPT Test Linux mint 19 iso" {
set root=(hd0,1)
set iso=/boot/linux191.iso
linux /boot/kernel418/vmlinuz boot=casper iso-scan/filename=$iso locale=zh_TW.UTF-8
initrd /boot/kernel418/initrd
}
Linux Mint 19 開啟終端機執行 inxi 顯示 CPU 資訊:
CPU~Quad core AMD Ryzen 3 2200G with Radeon Vega Graphics (-MCP-) speed/max~1438/3500 MHz Kernel~4.15.0-20-generic x86_64 Up~9 min Mem~1180.1/6980.1MB HDD~1370.3GB(59.9% used) Procs~186 Client~Shell inxi~2.3.56
2019年3月26日 星期二
c++ 善用 smart pointer 在物件建構法中傳回不同類型的物件並統一函式介面的作法
每個物件通常有不同的函式介面, 想要將兩種不同物件的方法統一成相同介面, 可以利用物件建構法將它封裝起來, 用這新類型物件去實例化想要的物件, 達成傳回多型物件(polymorphic class)的目的:
#include <stdio.h>
struct base { // 基礎類型
virtual void work( ){ }//擬統一的函式介面
virtual ~base(){ } // 避免繼承者,當在解構式中想刪除指標物件時,編譯器會報出錯務訊息
}; // 先建構基礎物件抽象類型, 統一函式介面
struct A: base { // 繼承 base 物件, 將物件用物件 A 封裝起來
void work( ) override { printf("A\n"); } // 將方法封裝並實現函式介面
};
struct B: base {// 繼承 base 物件, 將另一物件用物件 B 封裝起來
void work( ) override { printf("B\n"); } // 同樣將方法封裝並實現函式
};
struct C { // 一個全新多型類型(polymorphic class)
base *ptr;// 基礎物件的指標, 可以封裝物件 A 或 B
~C( ) { delete ptr; }
C(int a) { // 根據輸入值實例化不同物件, 奇數傳回 A, 偶數傳回 B, 等同於傳回不同物件
if (a%2) ptr = (base *) new A(); // 實例化物件 A 指標
else ptr = (base *) new B(); // 實例化物件 B 指標
}
void work( ){ ptr->work( ); }// 將多型物件成員用指標連結函式介面,方便使用
};
int main(){
for(int i=0; i < 10; i++) C(i).work( );
}
如果害怕指標的用法(像是造成 memory leak or double free), 可以用 smart pointer 將它封裝起來, 讓系統自動處理, 就不用擔心記憶體釋放的問題. c++ 所提供的 std::shared_ptr < > 就是 smart pointer 其中之一用來封裝類型指標的標準樣板函式(STL), 但有一點要注意的是, 用 smart pointer 封裝類型時, 該類型的建構及解構方法必須放在 public: 區, 讓 STL 可以正常呼叫, 否則會出現一堆錯誤訊息. 又或者很不喜歡用指標箭頭的方法, 還可以用左值(&alias) 換化成正常物件, 將方法從指標箭頭轉成正常物件的使用方式.
#include <stdio.h>
#include <memory> // smart pointer 要使用 memory 函式庫
typedef std::shared_ptr <base> baseptr; // 封裝成 smart pointer, 讓系統自動管理
struct base { // 基礎類型
virtual void work( ){}
virtual ~base(){ } // 避免繼承者若在解構式中想刪除指標物件時會報出錯務訊息
}; // 先建構基礎物件的抽象類型, 統一函式介面
struct A: base { // 繼承 base 類型, 可以將物件用類型 A 封裝起來
void work( ) override { printf("A\n"); } // 將方法封裝並覆蓋虛擬函式
};
struct B: base {// 繼承 base 類型 , 將另一物件用類型 B 封裝起來
void work( ) override { printf("B\n"); } // 同樣將方法封裝並覆蓋虛擬函式
};
struct C { // 一個全新多型類型(polymorphic class)
baseptr ptr;// 宣告 ptr 成為 smart pointer 可以封裝物件 A 或 B 類型的指標
// 無需解構式 ~C( ){ }
C(int a) { // 根據輸入值實例化不同物件, 奇數傳回 A, 偶數傳回 B, 等同傳回不同物件
if (a%2) ptr = baseptr(new A( )); // 實例化物件 A 指標, 同時轉換成 smart pointer
else ptr = baseptr(new B( )); // 實例化物件 B 指標, 同時轉換成 smart pointer
}
};
int main(){ // 呼叫時, 先用物件直取多型成員, 接著就能用指標 -> 呼叫函式介面
for(int i=0; i < 10; i++) C(i).ptr->work( );
}
2019年3月24日 星期日
linux mint 升級 openssl
1. 至官網下載原始碼 https://github.com/openssl/openssl
2. 解壓縮, 並進入目錄
3. 執行 ./config
4. 執行 make
5. 執行 sudo make install
預設程式庫會被安裝至 /usr/local/lib, 導致 openssl 找不到 libssl.so.3 及 libcrypto.so.3 等動態程式庫, 無法執行, 只要將符號連結過去就可以解決問題:
sudo ln -sf /usr/local/lib/libcrypto.so.3 /usr/lib
sudo ln -sf /usr/local/lib/libssl.so.3 /usr/lib
Libressl:
1. 這是另一個源出於 Oenssl 的分支, 程式碼是相容的, 號稱刪除一些不必要的程式碼, 讓它更精減, 目標也要讓程式更安全可靠, 編譯的方式大同小異, 參考文章: http://linuxg.net/how-to-install-libressl-2-1-6-on-linux-systems , 可到官網下載源碼: https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-2.9.0.tar.gz
2. 解壓縮, 並進入目錄
3. 執行 ./configure
4. 執行 make
5. 執行 sudo make install
6. 執行 sudo ldconfig
預設程式庫也是安裝至 /usr/local/lib, 如果導致 openssl 找不到 libssl.so.45 及 libcrypto.so.45 等動態程式庫, 只要將符號連結過去就可以解決問題:
sudo ln -sf /usr/local/lib/libcrypto.so.45.0.1 /usr/lib/libcrypto.so.45
sudo ln -sf /usr/local/lib/libssl.so.47.0.1 /usr/lib/libssl.so.45
編譯程式時要加上選項參數 -L/usr/local/lib -lssl -lcrypto, 例如:
g++ https.c -L/usr/local/lib -lssl -lcrypto -pthread -o https
或是編寫一個簡單的 Makefile
#Makefile to link with ssl, crypto, pthead library
https:
g++ https.c -L/usr/local/lib -lssl -lcrypto -pthread -o$@
clean:
rm -f https
之後只要下一個命令, 不用再打那麼多字
make
Boringssl:
1. 同樣源自於 Openssl 的另一分支 , 由 google 負責維護的 tls/ssl 程式庫, 首先要安裝 cmake 及 golang 兩個必要開發工具:
sudo apt-get install cmake golang
2. 到官網用 git 下載源碼, 複製整個 boringssl 目錄
cd $HOME/Downloads
git clone https://boringssl.googlesource.com/boringssl
3. 進入目錄, 建個子目錄 build, 進入子目錄 build, cmake .. , make
cd $HOME/Downloads/boringss
mkdir build
cd build
cmake ..
make
4. 例如要編譯 c 聯結 Boringssl 程式庫, 可以進到工作目錄, 把編譯好的程式庫複製過來作聯結:
cd $HOME/work/c/https
cp $HOME/Downloads/boringssl/build/ssl/libssl.a .
cp $HOME/Downloads/boringssl/build/crypto/libcrypto.a .
g++ https.c -L. -lssl -lcrypto -pthread
c++ 寫一個簡單的 https server
先安裝好 openssl 開發檔及準備自我認証的憑證
sudo apt-get install libssl–dev
openssl req -x509 -nodes -days 30 -newkey rsa:2048 -keyout mykey.pem -out mycert.pem
編輯 https server 主程式:
// server.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
int main()
{
SSL_library_init();
SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
SSL_CTX_set_ecdh_auto(ctx, 1);
SSL_CTX_use_certificate_file(ctx, "mycert.pem", SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx, "mykey.pem", SSL_FILETYPE_PEM);
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int server = socket(AF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(5000);
addr.sin_addr.s_addr = INADDR_ANY;
int ok = 1;
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &ok, sizeof(ok));
bind(server, (struct sockaddr*)&addr, sizeof(addr));
listen(server, 10);
while(1) {
int client = accept(server, (struct sockaddr*)&addr, &len);
printf("Accept client IP= %s : port=%d\n",inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
SSL * ssl = SSL_new(ctx);
SSL_set_fd(ssl, client);
SSL_accept(ssl);
char buf[1024];
int bytes = SSL_read(ssl, buf, sizeof(buf));
if (bytes > 0) { buf[bytes]=0; printf("Client: %s", buf); }
char msg[] = "<html><head><meta charset='UTF-8'></head><Body>歡迎光臨</Body></html >";
buf[0]=0;
sprintf(buf ,"HTTP/1.0 200 OK\n");
sprintf(buf+strlen(buf),"Server: Welcome\n");
sprintf(buf+strlen(buf),"Content-Type: text/html\n");
sprintf(buf+strlen(buf),"Content-Length: %11ld\n",strlen(msg));
sprintf(buf+strlen(buf),"Connection: close\n\n");
sprintf(buf+strlen(buf),"%s",msg);
SSL_write(ssl, buf, strlen(buf));
SSL_free(ssl);
close(client);
};
close(server);
SSL_CTX_free(ctx);
}
編譯並執行
g++ server.c -lssl -lcrypto -o server && ./server
用瀏覽器瀏覽 https://127.0.0.1:5000 , 如有警告信息, 選取並信任該憑證
c++ 寫一個簡單的 https client
設定完 https server(以下使用 local host: 127.0.0.1 TCP port: 5000). 安裝 SSL 開發檔:
sudo apt-get install libssl–dev
編輯一個簡單的 https client 文字檔:
// client.c
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <openssl/ssl.h>
struct sessionTLS {
int sid;
SSL_CTX *ctx;
SSL *ssl;
};
void freeTLS(sessionTLS &tls) {
if (tls.ssl != NULL) SSL_free(tls.ssl);
if (tls.sid > 0) close(tls.sid);
if (tls.ctx != NULL) SSL_CTX_free(tls.ctx);
}
void bindSSL(sessionTLS &tls) {
tls.ssl = SSL_new(tls.ctx);
if(tls.ssl != NULL ) {
SSL_set_fd(tls.ssl, tls.sid);
SSL_connect(tls.ssl);
printf("\nsession TLS encryption: %s\n", SSL_get_cipher(tls.ssl));
X509 * CA = SSL_get_peer_certificate(tls.ssl);
if ( CA != NULL ) {
char * subject= X509_NAME_oneline(X509_get_subject_name(CA), 0, 0);
char * issuer = X509_NAME_oneline(X509_get_issuer_name(CA) , 0, 0);
printf("CA: %s , %s\n", subject, issuer);
free(subject);
free(issuer);
X509_free(CA);
}
}
}
sessionTLS connectIP(const char *ipaddress, int tcpPort){
SSL_library_init();
sessionTLS tls;// create a new TLS object
tls.ctx = SSL_CTX_new(SSLv23_method());
tls.sid = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in host;
host.sin_family = AF_INET;
host.sin_port = htons(tcpPort);
inet_pton(AF_INET, ipaddress, &host.sin_addr) ;
connect(tls.sid, (sockaddr*)&host, sizeof(host));
bindSSL(tls);// bind tls with SSL
return tls;
}
int writeTLS(sessionTLS &tls, char *msg, int len) { return SSL_write(tls.ssl, msg, len); }
int readTLS(sessionTLS &tls, char *buf, int len) { return SSL_read(tls.ssl, buf, len); }
int main() {
char msg[]="Hello\n";
char buf[1024];
sessionTLS tls = connectIP("127.0.0.1", 5000);
writeTLS(tls, msg, strlen(msg));
int bytes = readTLS(tls, buf, sizeof(buf));
char *ptr = buf;
while (bytes-- >0) printf("%c", *ptr ++);
printf("\n");
freeTLS(tls);
}
用 g++ 編譯並執行:
g++ client.c -lssl -lcrypto && ./a.out
2019年3月23日 星期六
用 Javascript 寫一個任意位元的亂數產生器
function random(maxnum) {
// 將產生後的亂數用時間函數運算過, 傳回整數並把它限制在 {0, ..., 最大值-1} 之間:
let srand = Math.floor(Math.random( )*maxnum) ^ new Date().getMilliseconds();
return srand % maxnum ;
}
function srandBits(bits) {
// 產生任意位元數的亂數
let r = bits % 8; // 以 8 位元為單位(次), 產生一個亂數
let num = [ ]; // 初始化儲存陣列
if (r > 0) {
num.push(random(1 << r)); // 先產生餘數位元的亂數
bits -= r;
}
while (bits > 0) { // 還剩下的位元數, 一次產生一個位元組(8 bits = 1 byte)的亂數
num.push(random(256)); // 產生 8 位元的亂數, 0 <= 亂數 < 256
bits -= 8; // 減掉每次產生的 8 位元
}
return num; // 傳回陣列
}
console.log("random:" + srandBits(32));
2019年3月19日 星期二
ECDSA 數位簽章
橢圓曲線數位簽章演算法:
假設 ECC(x,y) 是橢圓曲線方程式, 座標產生器 G 將大整數映設到 ECC 曲線上, 使用質數 p 取餘數(%), 1< n < p-1, 假設 order number = n, nG = O
1. 模除數改用橢圓曲線的 order number 計算
2. 把私章 d, 1 < d < n -1 映射到橢圓曲線的座標 Q = dG = d*G(1) = G(d) , Q 就是簽章者的公鑰
3. 用 sha256 擷取文件特徵值 z, z < p,並選一亂數 k, 映設到橢圓曲線 (x, y) = kG = G(k) , 1 < k < n -1
4. 只取出 G(k)的 x 座標, r = x, 計算出 k 的模倒數 k-1 = 1/k, (k * k-1) % n = 1
5. 計算 s = (z + rd) / k % n = (z + rd)*k-1 % n, 1 < s < n - 1
6. (z, r, s , Q)就是帶數位簽章的文件
驗證方式:
1. 模除數改用橢圓曲線的 order number 計算,
2. 計算 s 的模倒數 s-1 = 1/s, (s * s-1) % n = 1
3. 計算 u = z*s-1 mod n
4. 計算 v = r*s-1 mod n
5. 用 ECC 加法運算, 計算出座標 K = uG + vQ =G(z*s-1) + G(rd*s-1) = G( (z+rd)*s-1)=G(k)
6. 取出 K 的 x 座標, 應該要等於 r