From 8ca11d98e601dda66cdfb12d526b20fa3beece8e Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Wed, 2 Dec 2009 21:53:04 +0000 Subject: Add Blowfish ECB, CBC and OFB modes My previous patch added CFB mode. This patch adds all remaining Blowfish modes. According to the man page http://www.fifi.org/cgi-bin/man2html/usr/share/man/man3/blowfish.3ssl.gz these are available in all versions of OpenSSL. [ Squashed in elimination of signed/unsigned compiler warnings. /bg ] --- lib/crypto/c_src/crypto_drv.c | 78 +++++++++++++++++++++++++++++++++++++++++++ lib/crypto/doc/src/crypto.xml | 64 +++++++++++++++++++++++++++++++++++ lib/crypto/src/crypto.erl | 25 +++++++++++++- 3 files changed, 166 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/crypto/c_src/crypto_drv.c b/lib/crypto/c_src/crypto_drv.c index 241c4ec733..5b6d750dde 100644 --- a/lib/crypto/c_src/crypto_drv.c +++ b/lib/crypto/c_src/crypto_drv.c @@ -233,6 +233,11 @@ static ErlDrvEntry crypto_driver_entry = { #define DRV_BF_CFB64_ENCRYPT 59 #define DRV_BF_CFB64_DECRYPT 60 +#define DRV_BF_ECB_ENCRYPT 61 +#define DRV_BF_ECB_DECRYPT 62 +#define DRV_BF_OFB64_ENCRYPT 63 +#define DRV_BF_CBC_ENCRYPT 64 +#define DRV_BF_CBC_DECRYPT 65 /* #define DRV_CBC_IDEA_ENCRYPT 34 */ /* #define DRV_CBC_IDEA_DECRYPT 35 */ @@ -533,6 +538,79 @@ static int crypto_control(ErlDrvData drv_data, unsigned int command, char *buf, (command == DRV_CBC_DES_ENCRYPT)); return dlen; + case DRV_BF_ECB_ENCRYPT: + case DRV_BF_ECB_DECRYPT: + { + /* buf = klen[4] key data */ + int bf_direction; + const unsigned char *ukey; + const unsigned char *bf_dbuf; /* blowfish input data */ + BF_KEY bf_key; /* blowfish key 8 */ + + klen = get_int32(buf); + ukey = (unsigned char *) buf + 4; + bf_dbuf = ukey + klen; + dlen = len - 4 - klen; + if (dlen < 0) return -1; + BF_set_key(&bf_key, klen, ukey); + bin = return_binary(rbuf,rlen,dlen); + if (bin==NULL) return -1; + bf_direction = command == DRV_BF_ECB_ENCRYPT ? BF_ENCRYPT : BF_DECRYPT; + BF_ecb_encrypt(bf_dbuf, bin, &bf_key, bf_direction); + return dlen; + } + + case DRV_BF_CBC_ENCRYPT: + case DRV_BF_CBC_DECRYPT: + { + /* buf = klen[4] key ivec[8] data */ + unsigned char *ukey; + unsigned char* ivec; + unsigned char bf_tkey[8]; /* blowfish ivec */ + int bf_direction; + const unsigned char *bf_dbuf; /* blowfish input data */ + BF_KEY bf_key; /* blowfish key 8 */ + + klen = get_int32(buf); + ukey = (unsigned char *)buf + 4; + ivec = ukey + klen; + bf_dbuf = ivec + 8; + dlen = len - 4 - klen - 8; + if (dlen < 0) return -1; + BF_set_key(&bf_key, klen, ukey); + memcpy(bf_tkey, ivec, 8); + bin = return_binary(rbuf,rlen,dlen); + if (bin==NULL) return -1; + bf_direction = command == DRV_BF_CBC_ENCRYPT ? BF_ENCRYPT : BF_DECRYPT; + BF_cbc_encrypt(bf_dbuf, bin, dlen, &bf_key, bf_tkey, bf_direction); + return dlen; + } + + case DRV_BF_OFB64_ENCRYPT: + { + /* buf = klen[4] key ivec[8] data */ + unsigned char *ukey; + unsigned char* ivec; + unsigned char bf_tkey[8]; /* blowfish ivec */ + int bf_n; /* blowfish ivec pos */ + const unsigned char *bf_dbuf; /* blowfish input data */ + BF_KEY bf_key; /* blowfish key 8 */ + + klen = get_int32(buf); + ukey = (unsigned char *)buf + 4; + ivec = ukey + klen; + bf_dbuf = ivec + 8; + dlen = len - 4 - klen - 8; + if (dlen < 0) return -1; + BF_set_key(&bf_key, klen, ukey); + memcpy(bf_tkey, ivec, 8); + bin = return_binary(rbuf,rlen,dlen); + if (bin==NULL) return -1; + bf_n = 0; + BF_ofb64_encrypt(bf_dbuf, bin, dlen, &bf_key, bf_tkey, &bf_n); + return dlen; + } + case DRV_BF_CFB64_ENCRYPT: case DRV_BF_CFB64_DECRYPT: { diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 42ba523c8c..cfc6996332 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -337,6 +337,53 @@ Mpint() = >]]> Key3, and IVec must be 64 bits (8 bytes).

+ + + blowfish_ecb_encrypt(Key, Text) -> Cipher + Encrypt the first 64 bits of Text using Blowfish in ECB mode + + Key = Text = iolist() | binary() + IVec = Cipher = binary() + + +

Encrypts the first 64 bits of Text using Blowfish in ECB mode. Key is the Blowfish key. The length of Text must be at least 64 bits (8 bytes).

+
+ blowfish_ecb_decrypt(Key, Text) -> Cipher + Decrypt the first 64 bits of Text using Blowfish in ECB mode + + Key = Text = iolist() | binary() + IVec = Cipher = binary() + + +

Decrypts the first 64 bits of Text using Blowfish in ECB mode. Key is the Blowfish key. The length of Text must be at least 64 bits (8 bytes).

+
+
+ + + blowfish_cbc_encrypt(Key, Text) -> Cipher + Encrypt Text using Blowfish in CBC mode + + Key = Text = iolist() | binary() + IVec = Cipher = binary() + + +

Encrypts Text using Blowfish in CBC mode. Key is the Blowfish key, and IVec is an + arbitrary initializing vector. The length of IVec + must be 64 bits (8 bytes). The length of Text must be a multiple of 64 bits (8 bytes).

+
+ blowfish_cbc_decrypt(Key, Text) -> Cipher + Decrypt Text using Blowfish in CBC mode + + Key = Text = iolist() | binary() + IVec = Cipher = binary() + + +

Decrypts Text using Blowfish in CBC mode. Key is the Blowfish key, and IVec is an + arbitrary initializing vector. The length of IVec + must be 64 bits (8 bytes). The length of Text must be a multiple 64 bits (8 bytes).

+
+
+ blowfish_cfb64_encrypt(Key, IVec, Text) -> Cipher Encrypt Textusing Blowfish in CFB mode with 64 @@ -367,6 +414,23 @@ Mpint() = >]]> must be 64 bits (8 bytes).

+ + + blowfish_ofb64_encrypt(Key, IVec, Text) -> Cipher + Encrypt Textusing Blowfish in OFB mode with 64 + bit feedback + + Key = Text = iolist() | binary() + IVec = Cipher = binary() + + +

Encrypts Text using Blowfish in OFB mode with 64 bit + feedback. Key is the Blowfish key, and IVec is an + arbitrary initializing vector. The length of IVec + must be 64 bits (8 bytes).

+
+
+ aes_cfb_128_encrypt(Key, IVec, Text) -> Cipher aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 5189677dd0..fa33bad2e0 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -30,7 +30,10 @@ -export([md5_mac/2, md5_mac_96/2, sha_mac/2, sha_mac_96/2]). -export([des_cbc_encrypt/3, des_cbc_decrypt/3, des_cbc_ivec/1]). -export([des3_cbc_encrypt/5, des3_cbc_decrypt/5]). --export([blowfish_cfb64_encrypt/3,blowfish_cfb64_decrypt/3]). +-export([blowfish_ecb_encrypt/2, blowfish_ecb_decrypt/2]). +-export([blowfish_cbc_encrypt/3, blowfish_cbc_decrypt/3]). +-export([blowfish_cfb64_encrypt/3, blowfish_cfb64_decrypt/3]). +-export([blowfish_ofb64_encrypt/3]). -export([des_ede3_cbc_encrypt/5, des_ede3_cbc_decrypt/5]). -export([aes_cfb_128_encrypt/3, aes_cfb_128_decrypt/3]). -export([exor/2]). @@ -115,6 +118,11 @@ -define(BF_CFB64_ENCRYPT, 59). -define(BF_CFB64_DECRYPT, 60). +-define(BF_ECB_ENCRYPT, 61). +-define(BF_ECB_DECRYPT, 62). +-define(BF_OFB64_ENCRYPT, 63). +-define(BF_CBC_ENCRYPT, 64). +-define(BF_CBC_DECRYPT, 65). %% -define(IDEA_CBC_ENCRYPT, 34). %% -define(IDEA_CBC_DECRYPT, 35). @@ -303,12 +311,27 @@ des_ede3_cbc_decrypt(Key1, Key2, Key3, IVec, Data) -> %% %% Blowfish %% +blowfish_ecb_encrypt(Key, Data) when byte_size(Data) >= 8 -> + control_bin(?BF_ECB_ENCRYPT, Key, list_to_binary([Data])). + +blowfish_ecb_decrypt(Key, Data) when byte_size(Data) >= 8 -> + control_bin(?BF_ECB_DECRYPT, Key, list_to_binary([Data])). + +blowfish_cbc_encrypt(Key, IVec, Data) when byte_size(Data) rem 8 =:= 0 -> + control_bin(?BF_CBC_ENCRYPT, Key, list_to_binary([IVec, Data])). + +blowfish_cbc_decrypt(Key, IVec, Data) when byte_size(Data) rem 8 =:= 0 -> + control_bin(?BF_CBC_DECRYPT, Key, list_to_binary([IVec, Data])). + blowfish_cfb64_encrypt(Key, IVec, Data) when byte_size(IVec) =:= 8 -> control_bin(?BF_CFB64_ENCRYPT, Key, list_to_binary([IVec, Data])). blowfish_cfb64_decrypt(Key, IVec, Data) when byte_size(IVec) =:= 8 -> control_bin(?BF_CFB64_DECRYPT, Key, list_to_binary([IVec, Data])). +blowfish_ofb64_encrypt(Key, IVec, Data) when byte_size(IVec) =:= 8 -> + control_bin(?BF_OFB64_ENCRYPT, Key, list_to_binary([IVec, Data])). + %% %% AES in cipher feedback mode (CFB) %% -- cgit v1.2.3 From b113de760b4e4c04ba573ed54c7298a86e6bfe8a Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Thu, 3 Dec 2009 11:09:00 +0000 Subject: Add Blowfish tests --- lib/crypto/test/Makefile | 4 +- lib/crypto/test/blowfish_SUITE.erl | 210 +++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 lib/crypto/test/blowfish_SUITE.erl (limited to 'lib') diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile index bf5c42877e..e728875027 100644 --- a/lib/crypto/test/Makefile +++ b/lib/crypto/test/Makefile @@ -5,7 +5,9 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # Target Specs # ---------------------------------------------------- -MODULES= crypto_SUITE +MODULES = \ + blowfish_SUITE \ + crypto_SUITE ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/crypto/test/blowfish_SUITE.erl b/lib/crypto/test/blowfish_SUITE.erl new file mode 100644 index 0000000000..0a30ac33f3 --- /dev/null +++ b/lib/crypto/test/blowfish_SUITE.erl @@ -0,0 +1,210 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(blowfish_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +-define(TIMEOUT, 120000). % 2 min + +-define(KEY, to_bin("0123456789ABCDEFF0E1D2C3B4A59687")). +-define(IVEC, to_bin("FEDCBA9876543210")). +%% "7654321 Now is the time for " (includes trailing '\0') +-define(DATA, to_bin("37363534333231204E6F77206973207468652074696D6520666F722000")). +-define(DATA_PADDED, to_bin("37363534333231204E6F77206973207468652074696D6520666F722000000000")). + +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initialization before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + crypto:start(), + Config. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + crypto:stop(). + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initialization before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initialization before each test case +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, Config) -> + Dog = ?config(watchdog, Config), + case Dog of + undefined -> + ok; + _ -> + test_server:timetrap_cancel(Dog) + end. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all(doc) -> + ["Test Blowfish functionality"]; + +all(suite) -> + [ecb, + cbc, + cfb64, + ofb64 + ]. + +%% Test cases start here. +%%-------------------------------------------------------------------- + +ecb_test(KeyBytes, ClearBytes, CipherBytes) -> + {Key, Clear, Cipher} = + {to_bin(KeyBytes), to_bin(ClearBytes), to_bin(CipherBytes)}, + crypto:blowfish_ecb_encrypt(Key, Clear) =:= Cipher. + +ecb(doc) -> + "Test that ECB mode is OK"; +ecb(suite) -> + []; +ecb(Config) when is_list(Config) -> + true = ecb_test("0000000000000000", "0000000000000000", "4EF997456198DD78"), + true = ecb_test("FFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFF", "51866FD5B85ECB8A"), + true = ecb_test("3000000000000000", "1000000000000001", "7D856F9A613063F2"), + true = ecb_test("1111111111111111", "1111111111111111", "2466DD878B963C9D"), + true = ecb_test("0123456789ABCDEF", "1111111111111111", "61F9C3802281B096"), + true = ecb_test("1111111111111111", "0123456789ABCDEF", "7D0CC630AFDA1EC7"), + true = ecb_test("0000000000000000", "0000000000000000", "4EF997456198DD78"), + true = ecb_test("FEDCBA9876543210", "0123456789ABCDEF", "0ACEAB0FC6A0A28D"), + true = ecb_test("7CA110454A1A6E57", "01A1D6D039776742", "59C68245EB05282B"), + true = ecb_test("0131D9619DC1376E", "5CD54CA83DEF57DA", "B1B8CC0B250F09A0"), + true = ecb_test("07A1133E4A0B2686", "0248D43806F67172", "1730E5778BEA1DA4"), + true = ecb_test("3849674C2602319E", "51454B582DDF440A", "A25E7856CF2651EB"), + true = ecb_test("04B915BA43FEB5B6", "42FD443059577FA2", "353882B109CE8F1A"), + true = ecb_test("0113B970FD34F2CE", "059B5E0851CF143A", "48F4D0884C379918"), + true = ecb_test("0170F175468FB5E6", "0756D8E0774761D2", "432193B78951FC98"), + true = ecb_test("43297FAD38E373FE", "762514B829BF486A", "13F04154D69D1AE5"), + true = ecb_test("07A7137045DA2A16", "3BDD119049372802", "2EEDDA93FFD39C79"), + true = ecb_test("04689104C2FD3B2F", "26955F6835AF609A", "D887E0393C2DA6E3"), + true = ecb_test("37D06BB516CB7546", "164D5E404F275232", "5F99D04F5B163969"), + true = ecb_test("1F08260D1AC2465E", "6B056E18759F5CCA", "4A057A3B24D3977B"), + true = ecb_test("584023641ABA6176", "004BD6EF09176062", "452031C1E4FADA8E"), + true = ecb_test("025816164629B007", "480D39006EE762F2", "7555AE39F59B87BD"), + true = ecb_test("49793EBC79B3258F", "437540C8698F3CFA", "53C55F9CB49FC019"), + true = ecb_test("4FB05E1515AB73A7", "072D43A077075292", "7A8E7BFA937E89A3"), + true = ecb_test("49E95D6D4CA229BF", "02FE55778117F12A", "CF9C5D7A4986ADB5"), + true = ecb_test("018310DC409B26D6", "1D9D5C5018F728C2", "D1ABB290658BC778"), + true = ecb_test("1C587F1C13924FEF", "305532286D6F295A", "55CB3774D13EF201"), + true = ecb_test("0101010101010101", "0123456789ABCDEF", "FA34EC4847B268B2"), + true = ecb_test("1F1F1F1F0E0E0E0E", "0123456789ABCDEF", "A790795108EA3CAE"), + true = ecb_test("E0FEE0FEF1FEF1FE", "0123456789ABCDEF", "C39E072D9FAC631D"), + true = ecb_test("0000000000000000", "FFFFFFFFFFFFFFFF", "014933E0CDAFF6E4"), + true = ecb_test("FFFFFFFFFFFFFFFF", "0000000000000000", "F21E9A77B71C49BC"), + true = ecb_test("0123456789ABCDEF", "0000000000000000", "245946885754369A"), + true = ecb_test("FEDCBA9876543210", "FFFFFFFFFFFFFFFF", "6B5C5A9C5D9E0A5A"), + ok. + +cbc(doc) -> + "Test that CBC mode is OK"; +cbc(suite) -> + []; +cbc(Config) when is_list(Config) -> + true = crypto:blowfish_cbc_encrypt(?KEY, ?IVEC, ?DATA_PADDED) =:= + to_bin("6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC"), + ok. + +cfb64(doc) -> + "Test that CFB64 mode is OK"; +cfb64(suite) -> + []; +cfb64(Config) when is_list(Config) -> + true = crypto:blowfish_cfb64_encrypt(?KEY, ?IVEC, ?DATA) =:= + to_bin("E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3"), + ok. + +ofb64(doc) -> + "Test that OFB64 mode is OK"; +ofb64(suite) -> + []; +ofb64(Config) when is_list(Config) -> + true = crypto:blowfish_ofb64_encrypt(?KEY, ?IVEC, ?DATA) =:= + to_bin("E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA"), + ok. + +%% Helper functions + +%% Convert a hexadecimal string to a binary. +-spec(to_bin(L::string()) -> binary()). +to_bin(L) -> + to_bin(L, []). + +%% @spec dehex(char()) -> integer() +%% @doc Convert a hex digit to its integer value. +-spec(dehex(char()) -> integer()). +dehex(C) when C >= $0, C =< $9 -> + C - $0; +dehex(C) when C >= $a, C =< $f -> + C - $a + 10; +dehex(C) when C >= $A, C =< $F -> + C - $A + 10. + +-spec(to_bin(L::string(), list()) -> binary()). +to_bin([], Acc) -> + iolist_to_binary(lists:reverse(Acc)); +to_bin([C1, C2 | Rest], Acc) -> + to_bin(Rest, [(dehex(C1) bsl 4) bor dehex(C2) | Acc]). -- cgit v1.2.3