Hỏi các bác về mã hóa

yeuhanoi
05-07-06, 10:21
Em nghe nói nhiều đến việc mã hóa, mã mật thông tin, rồi thì các thuật toán mã hóa DES, 3-DES, MD5 gì gì đó. Em muốn nhìn rõ hơn sự mã hóa đó bằng những ví dụ đơn giản. Ví dụ viết một chương trình mã hóa đơn giản một vài file *.doc, *.bmp vớ vẩn gì đó. Rồi lại viết một chương trình giải mã các file đã được mã hóa đó. Thuật toán có thể là các thuật toán nổi tiếng trên, hoặc một thuật toán bất kì đơn giản nào đó cũng được.

Nhờ các bác chỉ giúp em hướng giải quyết.

(À, viết bằng C/C++ trên Linux là tốt nhất)
wasabi
05-07-06, 11:05
MD5 - Cái này bác bị nhầm từ khái niệm.

Thực ra nói cái này là mã hóa làm cho bác nhầm từ đó. Bác cứ tưởng là cứ "mã" thì phải giải. Nhưng thực ra là không phải. Chính xác ra ta phải gọi thuật này là thuật "hash".

Hash có thể hình dung đơn giản (như bác yêu cầu) như thế này. Bác có một tập hợp A gồm toàn xúc xích. Bây giờ bác "nhặt" bất kỳ 1 cái xúc xích của tập A - đưa cho em. Em từ tập A em cho vào một cái máy gọi là máy md5() - nó ra được A' - một con bò. Giả sử cái máy này cứ thấy cái xúc xích đen và lệch bên phải 20.5 độ thì sẽ nhả ra một con bò loang lổ (còn nếu lệch 21 độ thì sẽ ra một con bò khác hẳn màu nâu). Tức là cứ cái xúc xích như thế nhét vào máy md5() là ra con bò như thế. Biết được cái xúc xích thì sẽ biết được con bò bằng cách nhét vào máy md5(). Nhưng biết được con bò thì chưa chắc biết được cái xúc xích :D. Lúc đó mã ấy gọi là mã 1 chiều - hay là hash.

Thế thì md5 để làm gì? Giả sử thế này. Bác có cái xúc xích của bác. Bi giờ bác gọi cửa em em ở trong nhà. Làm cách nào em nhận ra bác? Em sẽ bắt bác đứng ngoài cửa và nhét cái xúc xích của bác vào cái máy md5 của em và kiểm tra con bò tuồn ra từ cái máy đó (tất nhiên cái đầu tuồn bò của em đặt trong nhà) - xem cái con bò của bác có màu lông loang lổ không (điều mà em biết chắc chắn do đã ghi lại từ khi bác đăng ký). Nếu đúng là con bò loang lổ thì em cho vào, còn không thì biến!

Khi bác đăng ký, nếu em chụp hình cái xúc xích mà thằng khác ăn trộm được cái ảnh hình xúc xích của bác thì khi đó bác có thể bị lộ ra là xúc xích của bác màu nâu, cong, lệch :D. Còn nếu em chỉ ghi lại hình con bò loang lổ thì việc ăn trộm chả nghĩa lý gì. Nó không thể suy ra được cái xúc xích từ hình con bò.

Đại khái md5 là thế.... :D

Còn DES thì em không biết - nhưng nếu mà muốn mã giải được thì nó phải là Base64 hay GPG/PGP - bác có thể tham khảo mã nguồn của Gnu PG chạy trên POSIX bằng cách google từ GPG.
yeuhanoi
05-07-06, 13:09
Bác minh họa bằng bò và xúc xích vui vãi. Cảm ơn bác nhiều lắm.

Tình hình là hồi xưa em cũng cài PGP tích hợp với Outlook Express rồi. Nhưng hiện giờ em muốn tìm hiểu cụ thể cách thức mã hóa thế nào?

Bác có một chương trình mã hóa đơn giản nào viết trên Linux ko thì cho em xin để bắt đầu tìm hiểu.

Mục đích cuối cùng của em là chọc được vào source của mấy chương trình kiểu PGP hoặc IPSec..., hiểu được cách thức nó mã hóa thông tin, có thể tùy biến các kiểu mã hóa của mình vào. Nếu bác đã từng làm việc tương tự rồi thì nhờ bác giúp em.
Phuongdong
05-07-06, 14:55
http://www.itl.nist.gov/fipspubs/fip46-2.htm
http://sourceforge.net/search/?type_of_search=soft&words=Encryption
yeuhanoi
05-07-06, 15:55
Cảm ơn các bác rất nhiều :-)
duongdfd
05-07-06, 16:42
MD5 - Cái này bác bị nhầm từ khái niệm.

Thực ra nói cái này là mã hóa làm cho bác nhầm từ đó. Bác cứ tưởng là cứ "mã" thì phải giải. Nhưng thực ra là không phải. Chính xác ra ta phải gọi thuật này là thuật "hash".

Hash có thể hình dung đơn giản (như bác yêu cầu) như thế này. Bác có một tập hợp A gồm toàn xúc xích. Bây giờ bác "nhặt" bất kỳ 1 cái xúc xích của tập A - đưa cho em. Em từ tập A em cho vào một cái máy gọi là máy md5() - nó ra được A' - một con bò. Giả sử cái máy này cứ thấy cái xúc xích đen và lệch bên phải 20.5 độ thì sẽ nhả ra một con bò loang lổ (còn nếu lệch 21 độ thì sẽ ra một con bò khác hẳn màu nâu). Tức là cứ cái xúc xích như thế nhét vào máy md5() là ra con bò như thế. Biết được cái xúc xích thì sẽ biết được con bò bằng cách nhét vào máy md5(). Nhưng biết được con bò thì chưa chắc biết được cái xúc xích :D. Lúc đó mã ấy gọi là mã 1 chiều - hay là hash.

Thế thì md5 để làm gì? Giả sử thế này. Bác có cái xúc xích của bác. Bi giờ bác gọi cửa em em ở trong nhà. Làm cách nào em nhận ra bác? Em sẽ bắt bác đứng ngoài cửa và nhét cái xúc xích của bác vào cái máy md5 của em và kiểm tra con bò tuồn ra từ cái máy đó (tất nhiên cái đầu tuồn bò của em đặt trong nhà) - xem cái con bò của bác có màu lông loang lổ không (điều mà em biết chắc chắn do đã ghi lại từ khi bác đăng ký). Nếu đúng là con bò loang lổ thì em cho vào, còn không thì biến!

Khi bác đăng ký, nếu em chụp hình cái xúc xích mà thằng khác ăn trộm được cái ảnh hình xúc xích của bác thì khi đó bác có thể bị lộ ra là xúc xích của bác màu nâu, cong, lệch :D. Còn nếu em chỉ ghi lại hình con bò loang lổ thì việc ăn trộm chả nghĩa lý gì. Nó không thể suy ra được cái xúc xích từ hình con bò.

Đại khái md5 là thế.... :D

Còn DES thì em không biết - nhưng nếu mà muốn mã giải được thì nó phải là Base64 hay GPG/PGP - bác có thể tham khảo mã nguồn của Gnu PG chạy trên POSIX bằng cách google từ GPG.

Bi giải thích hay đấy. Cách giải thích khóa công, khóa tư như này hay và dễ hiểu hơn mấy con bò trong trường dạy nhiều. Nói thế này lại nhớ ngày xưa. Mịe, mấy thằng mang tiếng thạc sĩ nọ kia mà vừa dạy vừa nhìn sách đọc theo. Kiểu như Transaction Recovery là cái transaction nó recovery. Very much ý mà. Mở lớp truyền mồm đi Bi, tớ trợ giảng cho.
wasabi
05-07-06, 17:14
Khóa công và khóa tư thì lại là chuyện khác.

Thằng PGP lại có cách tiếp cận bài toán khác, hoàn toàn khác với hash. Hash là hoàn toàn không giải được:
Xúc xích ----md5----> Bò

Còn cái PGP thì lại là giải được
Xúc xích ----PGP-Public-key---> Xúc xích' ----PGP-Private-key---> Xúc xích

Điều kỳ diệu ở đây là, khác với cách mọi người nghĩ là "Chìa nào khóa nấy" thì mã hóa PGP dùng chìa khóa một đằng chìa giải một nẻo.

Đây là một bài minh họa rất súc tích về PGP/GPG em chôm từ mạng về:


Để đảm bảo độ bí mật, người ta đã áp dụng nguyên lý số nguyên tố. Như chúng ta biết, số nguyên tố rất đặc biệt vì chúng là một số nguyên chỉ chia hết cho 1 và chính nó. Ta dễ dàng thực hiện phép nhân giữa các số nguyên tố với nhau. Ví dụ, ai cũng có thể nhân được 319489 x 242483 = 774707470337. Nhưng quá trình ngược lại lại rất phức tạp. Ví dụ để kiểm tra xem số 267281174273 có phải là số nguyên tố hay không, ta phải mất rất nhiều thời gian với hàng loạt phép tính mới có thể phát hiện được số này là kết quả của phép nhân giữa 274177 với 974849. Mà đây mới chỉlà những số có ít chữ số. Các bạn hình dung nếu kết quả ban đầu là một số có 20, 30 hay 50 chữ số thì khối lượng các phép toán sẽ khổng lồ đến mức nào!

Ngược lại với các phương pháp hai chiều hay còn gọi là đối xứng, mô hình số nguyên tố cho phép dễ dàng mã hóa thông tin nhưng dường như là không thực hiện được quá trình ngược lại. Ví dụ, chúng ta có thể chọn hai số nguyên tó p và q bất kỳ sau đó nhân chúng với nhau để thu được kết quả N. N chính là mật mã và ai cũng có thể biết được mật mã này và sử dụng nó để khóa một thông tin ai đó gửi cho bạn nhưng không ai biết được kết quả N là phép nhân hai số p và q (hai yếu ốt không thể thiếu để giải mã và chỉ có bạn biết) nên không thể đọc được thông tin mã hóa của bạn. Phương pháp này vừa dễ thực hiện mà độ bảo mật lại rất cao.

Dựa trên nguyên tắc này, các nhà lập trình và quản lý mạng máy tính đã nghĩ ra một hệ thống mã hóa đáp ứng được hai yêu cầu cơ bản là dễ sử dụng và độ bảo mật cao của các thông tin trên mạng Internet mang tên RSA (*) (RSA là tên viết tắt của các thành viên sáng lập: Rivest, Shamir và Adleman). Năm 1991, Phil Zimmermann cũng đã nghĩ ra một phiên bản khác hiệu quả hơn đặt tên là PGP (Pretty Good Privacy). Tất cả mọi người đều có thể truy cập vào PGP thông qua Internet để khóa thông tin của mình.
wasabi
05-07-06, 17:26
Với những người ngoài ngành:

Nhân tiện đây nói luôn về việc "giải mã" mật mã file RAR hay ZIP. Nhiều bác kêu ca là chương trình giải mã chậm quá. Các thằng RAR hay ZIP nó đều mã dựa trên những thứ như thế này hết. Nên không còn cách nào khác là hùng hục mà đi thử từng cái một

Các bác có file RAR hay ZIP hay là có cái mật khẩu đã bị mã hóa MD5 đều giống như việc bác có được 1 con bò trong tay. Để tìm được cái xúc xích nào đã làm ra con bò đó, không cách nào khác là phải thử từng cái xúc xích lại cho lại vào md5 xem nó ra cái con bò nào. Việc làm này, có khi là không bao giờ thành công vì rằng lượng xúc xích thì nhiều, mà thời gian sức lực lại có hạn.

Người ta đã nghĩ cách để hạn chế số lần thử sai bằng cách dùng dictionary, tức là chỉ kiểm tra những thằng giai làng: Chỉ kiểm tra những từ trong từ điển và sự kết hợp có thể giữa chúng, vd, BLuESky chẳng hạn thế. Nhưng hiệu quả cũng không nhiều.

Hiểu được md5 cũng có ích khi các bác bị mất mật khẩu. Các bác có thể suy ra là không thằng webmaster nào nó lại đi lưu cái mật khẩu trơn chưa mã hóa (hình cái xúc xích) của các bác vì thế chính nó (thường) là cũng không biết mật khẩu của bác là gì - mà nó dùng cách mã hóa một chiều (hash) rồi so khớp như em trình bày ở trên. Vì thế để lấy lại mật khẩu đã mất là điều không tưởng. Chỉ có thể reset nó mà thôi.

Nhân tiện đây cũng nói cách làm của cái bẹn gì mà vnpissport.com bên TTVNOl là rất chi đần độn khi mà nó lưu lại cái câu trả lời cho câu hỏi bí mật là text trơn (không tin các bác cứ thử truy cập vào cái trang cá nhân của mình xem- các bác sẽ xem lại được chính câu trả lời cho câu hỏi bí mật mà mình đã khai) - khi đó chỉ cần một tay "chẳng may" đọc được cái cơ sở dữ liệu thông tin thành viên của VnPiss (chứ không cần là can thiệp) của vnpiss kia thôi là hắn có thể reset được pass tài khoản của bất cứ ai. - Như thế cũng đồng nghĩa nói cái bẹn ở vnpiss tuy rằng nổi tiếng thật nhiều members thật nhưng chả hiểu mẹ gì về mã hóa cả - Vì cái lợi thế của việc mã hóa 1 chiều là "biết nhưng vẫn lực bất tòng tâm".
wasabi
05-07-06, 17:49
Mã hóa mình cũng cần hiểu một khái niệm nữa là "salt". "salt" chính là cách để làm cho md5 an toàn hơn. Cho ví dụ, ngẫu nhiên mà hai người đăng ký với em có 2 cái xúc xích giống hệt nhau. Kết quả là hai xúc xích sau khi qua cái máy md5 của em nó sẽ ra 2 con bò giống hệt nhau.

Nếu một kẻ đánh cắp được cái gallery bò của em mà nhìn thấy con bò của ông A giống con bò của ông B sẽ assume được rằng là nếu ta tìm ra được xúc xích của ông A thì ta cũng tìm luôn được xúc xích của ông B. Điều này là rất nguy hiểm vì việc ông A chả may hoặc cố í khoe hàng lại bị ảnh hưởng tới ông B là người không liên quan tý nào cả.

Muốn thế thì em sẽ order thêm cho cái máy md5 của em là "Đề nghị cái máy bẻ lệch xúc xích của ông A đi 5 độ, với ông B là 10 độ". Như vậy dù 2 cái mật khẩu giống nhau, nhưng kết quả thì lại khác nhau. Và điều này hạn chế được nhược điểm em đã nói ở trên của hash password.
chairuou
06-07-06, 14:09
Coi bộ thằng Bím thuộc lòng cái cuốn Applied Cryptography :D
yeuhanoi
06-07-06, 22:00
Các bác cho em hỏi tiếp, về an toàn bảo mật nội dung trên mạng. Những từ em hay nghe nói đến là IPSec, SSL, SSH... Em thì chỉ hiểu đại khái là chẳng hạn với loại cổ điển như Telnet thì thông tin trên đường truyền nó là dạng trơn, nều bị bắt trộm nghe lén (cái nì họ gọi là sniff phải ko các bác?) thì dễ bị đọc được nên ko an toàn, còn với SSH chẳng hạn thì nó đã được mã hóa nên ổn hơn... Còn SSL thì dùng với giao thức http, IPSec dùng trong thiết lập vpn..v..v... Các bác có thể giải thích giúp em cơ chế của mấy thằng đó nó bảo mật packet trên đường truyền ra sao mà giúp an toàn hơn được? Các bác có code minh họa cơ chế mã hóa mã mật các packet đó thì cho em xin càng tốt để rộng đường tham khảo.

Cảm ơn các bác
wasabi
06-07-06, 22:53
Cái này em e rằng là bác phải xem mã nguồn của chương trình ssh client/server rồi...

But this article is f*cking good! http://computer.howstuffworks.com/encryption.htm

Chính ra đọc cái www.howstuffworks.com là một cái cực kỳ khoái cảm các bác ạ! :icecream: :icecream:

Xem mới biết là kiến thức của mình nhỏ hơn cả 1 hạt cát.. :)
nghuy
06-07-06, 22:59
Em thấy có cuốn Network top down approach hơi bị dễ hiểu cho novice
N3tXpert
07-07-06, 17:24
Thế các bạn có biết RainbowCrack Project không?
hathongon
11-07-06, 18:41
RainbowCrack là cái này (http://www.antsight.com/zsl/rainbowcrack/rcracktutorial.htm) hả? Trời ơi, vậy là pass sau khi qua md5 cũng tiêu tùng rồi. Đa số người ta đặt pass là chữ với số, giờ phải thêm dấu than quá!!!
Phuongdong
16-07-06, 14:16
Tình cờ lượm đựoc cái này mang về cho chú yếu hẳn hoi
http://pajhome.org.uk/crypt/md5/index.html
http://pajhome.org.uk/crypt/md5/md5.js



/*
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/

/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */

/*
* These are the functions you'll usually want to call
* They take string arguments and return either hex or base-64 encoded strings
*/
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }

/*
* Perform a simple self-test to see if the VM is working
*/
function md5_vm_test()
{
return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
}

/*
* Calculate the MD5 of an array of little-endian words, and a bit length
*/
function core_md5(x, len)
{
/* append padding */
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;

var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;

for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;

a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);

a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return Array(a, b, c, d);

}

/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn(q, a, b, x, s, t)
{
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
* Calculate the HMAC-MD5, of a key and some data
*/
function core_hmac_md5(key, data)
{
var bkey = str2binl(key);
if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);

var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}

var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
return core_md5(opad.concat(hash), 512 + 128);
}

/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}

/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}

/*
* Convert a string to an array of little-endian words
* If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
*/
function str2binl(str)
{
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz)
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
return bin;
}

/*
* Convert an array of little-endian words to a string
*/
function binl2str(bin)
{
var str = "";
var mask = (1 << chrsz) - 1;
for(var i = 0; i < bin.length * 32; i += chrsz)
str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
return str;
}

/*
* Convert an array of little-endian words to a hex string.
*/
function binl2hex(binarray)
{
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++)
{
str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
}
return str;
}

/*
* Convert an array of little-endian words to a base-64 string
*/
function binl2b64(binarray)
{
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var str = "";
for(var i = 0; i < binarray.length * 4; i += 3)
{
var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
| (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
| ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
for(var * = 0; * < 4; *++)
{
if(i * 8 + * * 6 > binarray.length * 32) str += b64pad;
else str += tab.charAt((triplet >> 6*(3-*)) & 0x3F);
}
}
return str;
}