Quá trình mã hóa thực hiện nhƣ sau:
- Với i = 1 (mã hóa chuỗi S1 = "C"): low_code[1] = low_range[C] = 0.8 hi_code[1] = hi_range[C] = 1.0 - Với i = 2 ((mã hóa chuỗi S2 = "CA")
low_code[2] = low_code[1] + low_range[A]×(hi_code[1] - low_code[1]) = 0.8 + 0×(1.0 - 0.8) = 0.8
hi_code[2] = low_code[1]+hi_range[A] × (hi_code[1] - low_code[1]) = 0.8 + 0.4×(1.0-0.8) = 0.88
- Với i = 3 (mã hóa chuỗi S3 = "CAB")
low_code[3] = low_code[2] + low_range[B]×(hi_code[2] - low_code[2]) = 0.8 + 0.4×(0.88-0.8) = 0.832
hi_code[3] = low_code[2]+hi_range[B] × (hi_code[2] - low_code[2]) = 0.8 + 0.8×(0.88-0.8) = 0.864
- Với i = 4 (mã hóa chuỗi S4 = "CABA")
low_code[4] = low_code[3] + low_range[A]×(hi_code[3] - low_code[3]) = 0.832 + 0×(0.864-0.832) = 0.832
hi_code[4] = low_code[3]+hi_range[A] × (hi_code[3] - low_code[3]) = 0.832 + 0.4(0.864 – 0.832) = 0.848
- Với i = 5 (mã hóa chuỗi S5 = "CABAB")
low_code[5] = low_code[4] + low_range[B]×(hi_code[4] - low_code[4]) = 0.832 + 0.4(0.848-0.832) = 0.83712
hi_code[5] = low_code[4] + hi_range[B] × (hi_code[4] - low_code[4]) = 0.832 + 0.8(0.848-0.832) = 0.84224
Khi đó giá trị code của văn bản đƣợc xác định nhƣ sau:
Quá trình giải mã thực hiện nhƣ sau: - Với i = 1 code[1] = code = 0.83968 z[1] = g(code[1]) = g(0.83968) = 'C' - Với i = 2 code[2] = = code[2] = (0.83968 – 0.8) / (1 – 0.8) = 0.1984 z[2] = g(code[2]) = g(0.1984) = 'A' - Với i = 3 code[3] = (0.1984 – 0.0) / (0.4 – 0.0) = 0.496 z[3] = g(code[3]) = g(0.496) = 'B' - Với i = 4 code[4] = (0.496 – 0.4) / (0.8 – 0.4) = 0.24 z[4] = g(code[4]) = g(0.24) = 'A' - Với i = 5 code[5] = (0.24 – 0) / (0.4 – 0.0) = 0.6 z[5] = g(code[5]) = g(0.6) = 'B'
Nhƣ vậy sau khi giải mã ta khôi phục đƣợc bản rõ ban đầu.
3.3. Thuật toán mã hóa số học cải tiến
Trong các công thức (3.4) và (3.5) của thuật toán mã hóa và công thức (3.11) của thuật toán giải mã phải thực hiện các phép nhân, chia trên các số lớn làm tốc độ tính toán chậm, hạn chế khả năng ứng dụng của thuật toán này. Để giảm khối lƣợng tính toán, chúng tôi đề xuất giải pháp thay các phép nhân, chia bằng các phép dịch bit. Điều này có thể thực hiện đƣợc bằng cách chọn các miền phân bố một cách hợp lý.
3.3.1. Thuật toán mã hóa cải tiến
Chọn D và xác định bảng phân bố (bảng 3.2) nhƣ sau:
- D = 2s (3.16)
- low_range(ch[i]) = 2h[i] (i=1,...,m) (3.17)
Trong đó s, h[i], k[i] cần thỏa mãn điều kiện: - 2h[i]+ 2k[i] ≤ 2h[i+1] (i=1,2,...,m-1) - 2h[m]+ 2k[m] ≤ 2s
Với cách chọn trên thì:
hi_range(ch[i]) = 2h[i] + 2k[i] (i=1,...,m) (3.19)
Nhận xét: Với mỗi ký tự kt[i] của bản rõ (1 ≤ i ≤ n), luôn tồn tại chỉ số duy nhất f(i) trong khoảng từ 1 đến m sao cho:
kt[i] = ch[f(i)] (3.20)
Sử dụng hàm f(i), từ (3.17)-(3.19) có thể xác định miền phân bố của ký tự kt[i] bất kỳ của bản rõ nhƣ sau:
low_range(kt[i]) = 2h[f(i)] (i=1,...,n) (3.21)
hi_range(kt[i]) = 2h[f(i)] + 2k[f(i)] (i=1,...,n) (3.22)
Với cách chọn miền phân bố nhƣ các công thức (3.16), (3.17), (3.19) và dựa vào (3.20), (3.21) thì các phép nhân, chia trong công thức (3.4)-(3.5) đƣợc thay bằng các phép dịch chuyển bit nhƣ sau:
low_code[i] = low_code[i-1]+(low_range[kt[i]]×(hi_code[i-1]-low_code[i-1]))/D = low_code[i-1] + 2h[f(i)] × (hi_code[i-1]-low_code[i-1])×2-s
= low_code[i-1] + (hi_code[i-1]-low_code[i-1])×2-(s-h[f(i)]) Vậy:
low_code[i] = low_code[i-1] + (hi_code[i-1]-low_code[i-1]) >> (s-h[f(i)]) Tƣơng tự ta có:
hi_code[i] = low_code[i-1]+ (hi_range[kt[i]]×(hi_code[i-1]-low_code[i-1]))/D = low_code[i-1]+(2h[f(i)] + 2k[f(i)])×(hi_code[i-1]-low_code[i-1])*2-s = low_code[i-1]+(2-(s-h[f(i)]) + 2-(s-k[f(i)]))×(hi_code[i-1]-low_code[i-1]) = low_code[i-1]+ (hi_code[i-1]-low_code[i-1])×2-(s-h[f(i)])
+ (hi_code[i-1]-low_code[i-1])×2-(s-k[f(i)]) Vậy:
hi_code[i] = low_code[i] + (hi_code[i-1]-low_code[i-1])>>(s-h[f(i)]) + (hi_code[i-1]-low_code[i-1])>>(s-k[f(i)])
low_code[1] = low_range[kt[1]] hi_code[1] = hi_range[kt[1]] for(i=2; i≤n; i++)
{
low_code[i]=low_code[i-1]+(hi_code[i-1]-low_code[i-1])>>(s-h[f(i)]) hi_code[i]=low_code[i]+(hi_code[i-1]-low_code[i-1])>>(s-h[f(i)]) +(hi_code[i-1]-low_code[i-1])>>(s-k[f(i)]) }
3.3.2. Thuật toán giải mã cải tiến
Bằng cách lập luận tƣơng tự, từ (3.20), (3.21) và để ý thêm rằng z[i] = kt[i], i (công thức 3.12) thì các phép nhân, chia trong công thức (2.11) đƣợc thay bằng các phép dịch chuyển bit nhƣ sau:
code[i] =
code[i] = (code[i-1] - 2h[f(i-1)])×2s / (2h[f(i-1)] + 2k[f(i-1)]-2h[f(i-1)]) code[i] = (code[i-1]×2s - 2s+h[f(i-1)]) × 2-k[f(i-1)]
= code[i-1] ×2s-k[f(i-1)]- 2s+h[f(i-1)]- k[f(i-1)] Vậy:
code[i] = (code[i-1]<<(s-k[f(i-1)]))-(2<<(s+h[f(i-1)]-k[f(i-1)])) Vậy ta có thuật toán giải mã đƣợc cải tiến nhƣ sau:
code[1] = code
z[1] = g(code[1]) //xác định đƣợc ký tự đầu tiên của bản rõ for(i=2; i ≤ n; i++)
{
code[i]=(code[i-1]<<(s-k[f(i-1)])) - (2<<(s+h[f(i-1)]-k[f(i-1)])) (3.25) z[i] = g(code[i])// xác định các ký tự tiếp theo
}
Nhận xét: các công thức (3.23), (3.24) và (3.25) trong thuật toán mã hoá và giải mã cải tiến chỉ chứa các phép cộng, trừ và dịch bit nên chẳng những có tốc độ thực hiện nhanh mà còn cho kết quả chính xác.
(3.23) (3.24)
3.4. So sánh độ phức tạp
Để so sánh độ phức tạp của thuật toán mã hóa số học truyền thống so với thuật toán cải tiến ta đánh giá số phép toán mà mỗi thuật toán cần thực hiện. Giả sử cả các thuật toán đƣợc thực hiện trên các số nguyên lớn có độ dài size byte (8×size bit).
3.4.1. Thuật toán mã hóa số học truyền thống
3.4.1.1. Thuật toán mã hóa
Theo (3.4) và (3.5) trong mỗi bƣớc lặp của thuật toán mã hóa cần thực hiện 2 phép nhân và 2 phép chia trên các số nguyên có độ dài 8×size bit (chƣa kể các phép cộng và trừ) vì vậy số phép toán cần thực hiện là:
4× (8×size)2 = 256×size 2 phép nhân bit Vậy tổng số phép toán cần thực hiện là:
256× size 2×n phép nhân bit (3.26)
3.4.1.2. Thuật toán giải mã
Bằng cách phân tích tƣơng tự ta suy ra tổng số phép toán cần thực hiện:
128× size 2×n phép nhân bit (3.27)
3.4.2. Thuật toán mã hóa số học cải tiến
3.4.2.1. Thuật toán mã hóa
Theo (3.23) và (3.24) trong mỗi bƣớc lặp của thuật toán mã hóa cần thực hiện 3 phép dịch bit trên các số nguyên có độ dài size byte. Mỗi phép dịch bit yêu cầu tối đa 8× size phép gán bit. Vì vậy số phép toán cần thực hiện của n phép lặp là:
3×8× size ×n = 24×size×n phép gán bit (3.28)
3.4.2.2. Thuật toán giải mã
Bằng cách phân tích tƣơng tự ta suy ra tổng số phép toán cần thực hiện là:
16× size×n phép gán bit (3.29)
3.4.3. So sánh độ phức tạp của 2 phƣơng pháp
Trong phƣơng pháp mã hóa số học truyền thống chọn phép toán nhân bit làm phép toán cơ sở, trong khi đó phƣơng pháp mã hóa số học cải tiến chọn phép gán bit làm phép toán cơ sở. Thực tế phép gán bit thực hiện nhanh hơn phép nhân bit. Trong trƣờng hợp
này để tiện so sánh ta giả sử 2 phép toán này là tƣơng đƣơng về độ phức tạp. Khi đó từ các công thức (3.26) và (3.28) thì tốc độ tính toán giữa 2 phƣơng pháp có thể đƣợc so sánh qua phép tính sau:
Nhƣ vậy tốc độ của thuật toán mã hóa cải tiến nhanh gấp 10.7×size lần so với thuật toán mã hóa truyền thống, tốc độ của thuật toán giải mã cải tiến nhanh gấp 8 lần so với thuật toán giải mã truyền thống. Nếu sử dụng số nguyên lớn với độ dài 25 byte (size=25 - nhƣ trong phần cài đặt chƣơng trình) thì thuật toán mã hóa cải tiến ít nhất nhanh gấp 267 lần so với thuật toán mã hóa truyền thống (thuật toán giải mã cải tiến nhanh gấp 200 lần so với thuật toán giải mã truyền thống). Nhƣng qua thử nghiệm chƣơng trình thì thấy tốc độ của thuật toán cải tiến còn nhanh hơn nhiều (xem chƣơng 4)
3.6. Một thuật toán cải tiến khác
3.6.1. Xác định miền phân bố
Có thể nhận đƣợc một phƣơng án cải tiến đơn giản bằng cách chia đều miền phân bố cho các ký tự. Khi đó miền phân bố đƣợc xác định nhƣ sau:
D = 2s (3.30)
low_range(ch[i]) = (i-1)×2h (i=1,...,m) (3.31)
hi_range(ch[i]) = i×2h (i=1,...,m) (3.32)
Trong đó h và s cần thỏa mã điều kiện:
m×2h ≤ 2s và n×h (n-1)×s (3.33)
Với kt[i] = ch[f(i)] ta có thể xác định miền phân bố của ký tự kt[i] bất kỳ của bản rõ nhƣ sau:
low_range(kt[i]) = (f(i)-1)×2h (i=1,...,n) (3.34)
3.6.2. Thuật toán mã hóa cải tiến
Với cách chọn miền phân bố theo (3.30), (3.34), (3.35) thì các phép nhân, chia trong công thức (3.4), (3.5) đƣợc thay bằng các phép dịch bit nhƣ sau:
low_code[i] = low_code[i-1]+(low_range[kt[i]]×(hi_code[i-1]-low_code[i-1]))/D = low_code[i] + (f(i)-1)×2h×(hi_code[i-1]-low_code[i-1]))×2-s
= low_code[i] + (f(i)-1)×(hi_code[i-1]-low_code[i-1]))×2-(s-h) Vậy:
low_code[i]=low_code[i-1]+(f(i)-1))×(hi_code[i-1]-low_code[i-1])>>(s-h) Tƣơng tự ta có:
hi_code[i] = low_code[i-1]+ (hi_range[kt[i]]×(hi_code[i-1]-low_code[i-1]))/D = low_code[i-1]+ f(i) ×2h ×(hi_code[i-1]-low_code[i-1]))×2-s
= low_code[i-1]+ f(i)×(hi_code[i-1]-low_code[i-1]))×2-(s-h) Vậy:
hi_code[i]=low_code[i-1] + f(i)×(hi_code[i-1]- low_code[i-1]) >> (s-h) Nếu đặt [i] = hi_code[i] - low_code[i] thì từ (3.1), (3.2), (3.31), (3.32) suy ra:
[1] = 2h (3.38)
Ngoài ra theo (3.34), (3.35) ta có:
[i] = [i-1] ×2-(s-h) (i=2,...,n) (3.39)
Do đó từ (3.1), (3.2) và bằng cách kết hợp (3.34), (3.35) với (3.37) ta nhận đƣợc thuật toán mã hóa cải tiến nhƣ sau:
low_code[1] = low_range[kt[1]] hi_code[1] = hi_range[kt[1]] [1] = hi_code[1] - low_code[1] u[1] = h
for(i=2; i≤ n; ++i) {
[i] = [i-1] >> (s-h) u[i] = u[i-1] - (s-h)
low_code[i] = low_code[i-1] + (f(i)-1) << u[i] hi_code[i] = low_code[i] + [i]
(3.37) (3.36)
}
Nhận xét: Điều kiện (3.33) chẳng những đảm bảo miền phân bố của các ký tự thuộc [0,D] mà còn cho phép các phép dịch chuyển trong thuật toán luôn thực hiện được (vì u[i]0).
3.6.3. Thuật toán giải mã cải tiến
Với cách chọn miền phân bố theo (3.30)-(3.32) và để ý thêm rằng kt[i] = ch[f(i)] thì thuật toán giải mã tại mục 3.2.2.3 đƣợc cải tiến nhƣ sau:
code[1] = code
z[1] = g(code[1]) //xác định đƣợc ký tự đầu tiên của bản rõ for(i=2; i ≤ n; i++)
{
code[i] = (code[i-1]<<(s-h)) – (f(i)-1)<<s
z[i] = g(code[i]) // xác định các ký tự tiếp theo }
Nhận xét: Tại mỗi bước trong thuật toán mã hóa 3.6.2 chỉ chứa 2 phép dịch bit vì vậy tốc độ của thuật toán này nhanh hơn tốc độ của thuật toán mã hóa cải tiến tại mục 3.3.1
3.7. Nghiên cứu tính bảo mật của phƣơng pháp mã hóa số học học
Theo nhƣ các tác giả Ranjan Bose [16], Hyungjin Kim [11] mặc dù mã hóa số học là một phƣơng pháp mã hóa có hiệu quả cao (optimal code) nhƣng tính bảo mật của phƣơng pháp là tƣơng đối thấp. Phƣơng pháp này dễ bị tấn công bằng cách lựa chọn bản rõ (chosen plaintext attack) [11]. Trong đó kẻ tấn công chủ động đƣa ra các bản rõ có lựa chọn và theo dõi kết quả đầu ra để tìm ra khóa.
Ví dụ, trong mô hình nhị phân với hai ký tự A và B (trong mô hình này xác suất xuất hiện của A là p, của B là q=1-p, và miền phân bố của 2 ký tự này là không thay đổi trong suốt quá trình mã hóa cũng nhƣ giải mã), với mô hình này dễ dàng lựa chọn chuỗi ký tự đầu vào làm bản rõ, sau đó quan sát kết quả bản mã ở đầu ra, kẻ tấn công có thể xác định đƣợc xác suất xuất hiện của các ký tự cũng nhƣ miền phân bố của các ký tự, đó chính là chìa khóa của mô hình.
Trƣớc đây vấn đề bảo mật của phƣơng pháp mã hóa số học ít đƣợc chú ý. Tuy nhiên hiện nay đã xuất hiện nhiều nghiên cứu nhƣ các tài liệu [10] [11] [15] [16] nhằm nâng cao khả năng bảo mật của phƣơng pháp. Trƣớc tiên phải nói đến mô hình mã hóa số học ngẫu nhiên RAC (randomized arithmetic coding) [15] dựa trên sự hoán đổi ngẫu nhiên miền phân bố của 2 ký tự A và B trong mô hình nhị phân ở trên. Việc hoán đổi này dựa vào một dãy nhị phân sinh ngẫu nhiên đƣợc gọi là khóa.
Ví dụ:
+ Miền phân bố đƣợc chia làm 2 khoảng: của ký tự A là [0,p) của B là [p,1) + Khóa: 01001
+ Bản rõ: BAABB
Dựa vào khóa ta có miền phân bố của các ký tự nhƣ sau:
B A A B B
[0,p) [p,1) [0,p) [0,p) [p,1)