diff --git a/.gitignore b/.gitignore index cd49243d..ecfc5572 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.sw[po] libcrypto*.dylib +libcrypto*.so *.dSYM/ crypto diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..9719e55c --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ + +crypto: $(wildcard *.rs) + rustc crypto.rs + rustc --test crypto.rs + +clean: + rm -f crypto libcrypto-*.so + rm -f libcrypto-*.dylib + rm -rf *.dSYM diff --git a/README.md b/README.md index 774949f7..16740d5b 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,13 @@ This package provides Rust bindings for the functionality exposed by OpenSSL's -libcrypto. Currently provided: +libcrypto. OpenSSL 1.0.1 or higher is required. Currently provided: -* Hashes (hash.rs) - * MD5 +* Hash functions (hash.rs) + * SHA-512, SHA-384, SHA-256, SHA-224 * SHA-1 - * SHA-2 (224, 256, 384, 512) + * MD5 * Symmetric crypto (symm.rs) - * AES in ECB or CBC mode, all key lengths -* Keypair generation (pkey.rs) - * RSA, all key lengths -* Asymmetric encryption (pkey.rs) - * RSA with PKCS#1 OAEP padding -* Digital signatures (pkey.rs) - * RSA with whatever your system openssl does (PKCS#1 on my system) and sha256 - -Each module provides two interfaces: a low-level API which wraps the OpenSSL -interfaces as directly as possible and a high-level API which presents the -OpenSSL API as a Rust object and tries to make sensible default choices about -parameters most users won't care about. You probably want to use the high-level -API. For documentation on these, see the individual source files. + * AES-128 and AES-256 (ECB, CBC, CTR or GCM mode) + * RC4-128 +* RSA (pkey.rs) + * Encryption with PKCS #1 OAEP padding or PKCS #1 v1.5 padding + * Signatures with PKCS #1 v1.5 padding and any supported hash diff --git a/crypto.rs b/crypto.rs index e562bdca..1686a715 100644 --- a/crypto.rs +++ b/crypto.rs @@ -1,5 +1,6 @@ /* * Copyright 2011 Google Inc. + * 2013 Jack Lloyd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +16,14 @@ */ #[link(name = "crypto", - vers = "0.2", + vers = "0.3", uuid = "38297409-b4c2-4499-8131-a99a7e44dad3")]; #[crate_type = "lib"]; pub mod hash; -pub mod pkey; -pub mod symm; +pub mod hex; +pub mod hmac; pub mod pkcs5; +pub mod pkey; pub mod rand; +pub mod symm; diff --git a/hash.rs b/hash.rs index 8cae213d..2a43cbd2 100644 --- a/hash.rs +++ b/hash.rs @@ -40,7 +40,7 @@ mod libcrypto { } } -fn evpmd(t: HashType) -> (EVP_MD, uint) { +pub fn evpmd(t: HashType) -> (EVP_MD, uint) { unsafe { match t { MD5 => (libcrypto::EVP_md5(), 16u), @@ -115,35 +115,73 @@ pub fn hash(t: HashType, data: &[u8]) -> ~[u8] { #[cfg(test)] mod tests { use super::*; + use hex::FromHex; + use hex::ToHex; + + struct HashTest { + input: ~[u8], + expected_output: ~str + } + + fn HashTest(input: ~str, output: ~str) -> HashTest { + HashTest { input: input.from_hex(), + expected_output: output } + } + + fn hash_test(hashtype: HashType, hashtest: &HashTest) { + let calced_raw = hash(hashtype, hashtest.input); + + let calced = calced_raw.to_hex(); + + if calced != hashtest.expected_output { + println(fmt!("Test failed - %s != %s", calced, hashtest.expected_output)); + } + + assert!(calced == hashtest.expected_output); + } // Test vectors from http://www.nsrl.nist.gov/testdata/ #[test] fn test_md5() { - let s0 = ~[0x61u8, 0x62u8, 0x63u8]; - let d0 = - ~[0x90u8, 0x01u8, 0x50u8, 0x98u8, 0x3cu8, 0xd2u8, 0x4fu8, 0xb0u8, - 0xd6u8, 0x96u8, 0x3fu8, 0x7du8, 0x28u8, 0xe1u8, 0x7fu8, 0x72u8]; - assert!(hash(MD5, s0) == d0); + let tests = [ + HashTest(~"", ~"D41D8CD98F00B204E9800998ECF8427E"), + HashTest(~"7F", ~"83ACB6E67E50E31DB6ED341DD2DE1595"), + HashTest(~"EC9C", ~"0B07F0D4CA797D8AC58874F887CB0B68"), + HashTest(~"FEE57A", ~"E0D583171EB06D56198FC0EF22173907"), + HashTest(~"42F497E0", ~"7C430F178AEFDF1487FEE7144E9641E2"), + HashTest(~"C53B777F1C", ~"75EF141D64CB37EC423DA2D9D440C925"), + HashTest(~"89D5B576327B", ~"EBBAF15EB0ED784C6FAA9DC32831BF33"), + HashTest(~"5D4CCE781EB190", ~"CE175C4B08172019F05E6B5279889F2C"), + HashTest(~"81901FE94932D7B9", ~"CD4D2F62B8CDB3A0CF968A735A239281"), + HashTest(~"C9FFDEE7788EFB4EC9", ~"E0841A231AB698DB30C6C0F3F246C014"), + HashTest(~"66AC4B7EBA95E53DC10B", ~"A3B3CEA71910D9AF56742AA0BB2FE329"), + HashTest(~"A510CD18F7A56852EB0319", ~"577E216843DD11573574D3FB209B97D8"), + HashTest(~"AAED18DBE8938C19ED734A8D", ~"6F80FB775F27E0A4CE5C2F42FC72C5F1")]; + + for test in tests.iter() { + hash_test(MD5, test); + } } #[test] fn test_sha1() { - let s0 = ~[0x61u8, 0x62u8, 0x63u8]; - let d0 = - ~[0xa9u8, 0x99u8, 0x3eu8, 0x36u8, 0x47u8, 0x06u8, 0x81u8, 0x6au8, - 0xbau8, 0x3eu8, 0x25u8, 0x71u8, 0x78u8, 0x50u8, 0xc2u8, 0x6cu8, - 0x9cu8, 0xd0u8, 0xd8u8, 0x9du8]; - assert!(hash(SHA1, s0) == d0); + let tests = [ + HashTest(~"616263", ~"A9993E364706816ABA3E25717850C26C9CD0D89D"), + ]; + + for test in tests.iter() { + hash_test(SHA1, test); + } } #[test] fn test_sha256() { - let s0 = ~[0x61u8, 0x62u8, 0x63u8]; - let d0 = - ~[0xbau8, 0x78u8, 0x16u8, 0xbfu8, 0x8fu8, 0x01u8, 0xcfu8, 0xeau8, - 0x41u8, 0x41u8, 0x40u8, 0xdeu8, 0x5du8, 0xaeu8, 0x22u8, 0x23u8, - 0xb0u8, 0x03u8, 0x61u8, 0xa3u8, 0x96u8, 0x17u8, 0x7au8, 0x9cu8, - 0xb4u8, 0x10u8, 0xffu8, 0x61u8, 0xf2u8, 0x00u8, 0x15u8, 0xadu8]; - assert!(hash(SHA256, s0) == d0); + let tests = [ + HashTest(~"616263", ~"BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD") + ]; + + for test in tests.iter() { + hash_test(SHA256, test); + } } } diff --git a/hex.rs b/hex.rs new file mode 100644 index 00000000..b479ea18 --- /dev/null +++ b/hex.rs @@ -0,0 +1,87 @@ +/* + * Copyright 2013 Jack Lloyd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::vec; + +pub trait ToHex { + fn to_hex(&self) -> ~str; +} + +impl<'self> ToHex for &'self [u8] { + fn to_hex(&self) -> ~str { + + let chars = "0123456789ABCDEF".iter().collect::<~[char]>(); + + let mut s = ~""; + + for i in range(0u, self.len()) { + + let x = self[i]; + + let xhi = (x >> 4) & 0x0F; + let xlo = (x ) & 0x0F; + + s.push_char(chars[xhi]); + s.push_char(chars[xlo]); + } + + s + } +} + +pub trait FromHex { + fn from_hex(&self) -> ~[u8]; +} + +impl<'self> FromHex for &'self str { + fn from_hex(&self) -> ~[u8] { + let mut vec = vec::with_capacity(self.len() / 2); + + for (i,c) in self.iter().enumerate() { + let nibble = + if c >= '0' && c <= '9' { (c as u8) - 0x30 } + else if c >= 'a' && c <= 'f' { (c as u8) - (0x61 - 10) } + else if c >= 'A' && c <= 'F' { (c as u8) - (0x41 - 10) } + else { fail!(~"bad hex character"); }; + + if i % 2 == 0 { + vec.push(nibble << 4); + } + else { + vec[i/2] |= nibble; + } + } + + vec + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test() { + + assert!([05u8, 0xffu8, 0x00u8, 0x59u8].to_hex() == ~"05FF0059"); + + assert!("00FFA9D1F5".from_hex() == ~[0, 0xff, 0xa9, 0xd1, 0xf5]); + + assert!("00FFA9D1F5".from_hex().to_hex() == ~"00FFA9D1F5"); + } + + +} diff --git a/hmac.rs b/hmac.rs new file mode 100644 index 00000000..1e71ed1b --- /dev/null +++ b/hmac.rs @@ -0,0 +1,96 @@ +/* + * Copyright 2013 Jack Lloyd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use hash::*; +use std::{libc,ptr,vec}; + +#[allow(non_camel_case_types)] +pub struct HMAC_CTX { + md: EVP_MD, + md_ctx: EVP_MD_CTX, + i_ctx: EVP_MD_CTX, + o_ctx: EVP_MD_CTX, + key_length: libc::c_uint, + key: [libc::c_uchar, ..128] +} + +#[link_args = "-lcrypto"] +#[abi = "cdecl"] +extern { + fn HMAC_CTX_init(ctx: *mut HMAC_CTX, key: *u8, keylen: libc::c_int, md: EVP_MD); + + fn HMAC_Update(ctx: *mut HMAC_CTX, input: *u8, len: libc::c_uint); + + fn HMAC_Final(ctx: *mut HMAC_CTX, output: *mut u8, len: *mut libc::c_uint); +} + +pub struct HMAC { + priv ctx: HMAC_CTX, + priv len: uint, +} + +pub fn HMAC(ht: HashType, key: ~[u8]) -> HMAC { + unsafe { + + let (evp, mdlen) = evpmd(ht); + + let mut ctx : HMAC_CTX = HMAC_CTX { + md: ptr::null(), + md_ctx: ptr::null(), + i_ctx: ptr::null(), + o_ctx: ptr::null(), + key_length: 0, + key: [0u8, .. 128] + }; + + HMAC_CTX_init(&mut ctx, + vec::raw::to_ptr(key), + key.len() as libc::c_int, + evp); + + HMAC { ctx: ctx, len: mdlen } + } +} + +impl HMAC { + pub fn update(&mut self, data: &[u8]) { + unsafe { + do data.as_imm_buf |pdata, len| { + HMAC_Update(&mut self.ctx, pdata, len as libc::c_uint) + } + } + } + + pub fn final(&mut self) -> ~[u8] { + unsafe { + let mut res = vec::from_elem(self.len, 0u8); + let mut outlen: libc::c_uint = 0; + do res.as_mut_buf |pres, _len| { + HMAC_Final(&mut self.ctx, pres, &mut outlen); + assert!(self.len == outlen as uint) + } + res + } + } +} + +fn main() { + let mut h = HMAC(SHA512, ~[00u8]); + + h.update([00u8]); + + println(fmt!("%?", h.final())) +} diff --git a/pkey.rs b/pkey.rs index 75f5008e..9b97721b 100644 --- a/pkey.rs +++ b/pkey.rs @@ -3,11 +3,11 @@ use std::libc::{c_int, c_uint}; use std::libc; use std::ptr; use std::vec; +use hash::{HashType, MD5, SHA1, SHA224, SHA256, SHA384, SHA512}; #[allow(non_camel_case_types)] pub type EVP_PKEY = *libc::c_void; - #[allow(non_camel_case_types)] pub type RSA = *libc::c_void; @@ -16,7 +16,7 @@ mod libcrypto { use std::libc::{c_char, c_int, c_uint}; #[link_args = "-lcrypto"] - extern "C" { + extern { fn EVP_PKEY_new() -> *EVP_PKEY; fn EVP_PKEY_free(k: *EVP_PKEY); fn EVP_PKEY_assign(pkey: *EVP_PKEY, typ: c_int, key: *c_char) -> c_int; @@ -55,6 +55,30 @@ pub enum Role { Verify } +#[doc = "Type of encryption padding to use."] +pub enum EncryptionPadding { + OAEP, + PKCS1v15 +} + +fn openssl_padding_code(padding: EncryptionPadding) -> c_int { + match padding { + OAEP => 4, + PKCS1v15 => 1 + } +} + +fn openssl_hash_nid(hash: HashType) -> c_int { + match hash { + MD5 => 4, // NID_md5, + SHA1 => 64, // NID_sha1 + SHA224 => 675, // NID_sha224 + SHA256 => 672, // NID_sha256 + SHA384 => 673, // NID_sha384 + SHA512 => 674, // NID_sha512 + } +} + pub struct PKey { priv evp: *EVP_PKEY, priv parts: Parts, @@ -195,99 +219,105 @@ impl PKey { } } + pub fn encrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> ~[u8] { + unsafe { + let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); + let len = libcrypto::RSA_size(rsa); + + assert!(s.len() < self.max_data()); + + let mut r = vec::from_elem(len as uint + 1u, 0u8); + + let rv = do r.as_mut_buf |pr, _len| { + do s.as_imm_buf |ps, s_len| { + libcrypto::RSA_public_encrypt( + s_len as c_uint, + ps, + pr, + rsa, + openssl_padding_code(padding) + ) + } + }; + if rv < 0 as c_int { + ~[] + } else { + r.truncate(rv as uint); + r + } + } + } + + pub fn decrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> ~[u8] { + unsafe { + let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); + let len = libcrypto::RSA_size(rsa); + + assert_eq!(s.len() as c_uint, libcrypto::RSA_size(rsa)); + + let mut r = vec::from_elem(len as uint + 1u, 0u8); + + let rv = do r.as_mut_buf |pr, _len| { + do s.as_imm_buf |ps, s_len| { + libcrypto::RSA_private_decrypt( + s_len as c_uint, + ps, + pr, + rsa, + openssl_padding_code(padding) + ) + } + }; + + if rv < 0 as c_int { + ~[] + } else { + r.truncate(rv as uint); + r + } + } + } + /** * Encrypts data using OAEP padding, returning the encrypted data. The * supplied data must not be larger than max_data(). */ - pub fn encrypt(&self, s: &[u8]) -> ~[u8] { - unsafe { - let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); - let len = libcrypto::RSA_size(rsa); - - // 41 comes from RSA_public_encrypt(3) for OAEP - assert!(s.len() < libcrypto::RSA_size(rsa) as uint - 41u); - - let mut r = vec::from_elem(len as uint + 1u, 0u8); - - let rv = do r.as_mut_buf |pr, _len| { - do s.as_imm_buf |ps, s_len| { - // XXX: 4 == RSA_PKCS1_OAEP_PADDING - libcrypto::RSA_public_encrypt( - s_len as c_uint, - ps, - pr, - rsa, 4 as c_int - ) - } - }; - - if rv < 0 as c_int { - ~[] - } else { - r.truncate(rv as uint); - r - } - } - } + pub fn encrypt(&self, s: &[u8]) -> ~[u8] { self.encrypt_with_padding(s, OAEP) } /** * Decrypts data, expecting OAEP padding, returning the decrypted data. */ - pub fn decrypt(&self, s: &[u8]) -> ~[u8] { - unsafe { - let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); - let len = libcrypto::RSA_size(rsa); - - assert!(s.len() as c_uint == libcrypto::RSA_size(rsa)); - - let mut r = vec::from_elem(len as uint + 1u, 0u8); - - let rv = do r.as_mut_buf |pr, _len| { - do s.as_imm_buf |ps, s_len| { - // XXX: 4 == RSA_PKCS1_OAEP_PADDING - libcrypto::RSA_private_decrypt( - s_len as c_uint, - ps, - pr, - rsa, - 4 as c_int - ) - } - }; - - if rv < 0 as c_int { - ~[] - } else { - r.truncate(rv as uint); - r - } - } - } + pub fn decrypt(&self, s: &[u8]) -> ~[u8] { self.decrypt_with_padding(s, OAEP) } /** * Signs data, using OpenSSL's default scheme and sha256. Unlike encrypt(), * can process an arbitrary amount of data; returns the signature. */ - pub fn sign(&self, s: &[u8]) -> ~[u8] { + pub fn sign(&self, s: &[u8]) -> ~[u8] { self.sign_with_hash(s, SHA256) } + + /** + * Verifies a signature s (using OpenSSL's default scheme and sha256) on a + * message m. Returns true if the signature is valid, and false otherwise. + */ + pub fn verify(&self, m: &[u8], s: &[u8]) -> bool { self.verify_with_hash(m, s, SHA256) } + + pub fn sign_with_hash(&self, s: &[u8], hash: HashType) -> ~[u8] { unsafe { let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); - let len = libcrypto::RSA_size(rsa); + let mut len = libcrypto::RSA_size(rsa); let mut r = vec::from_elem(len as uint + 1u, 0u8); let rv = do r.as_mut_buf |pr, _len| { - do s.as_imm_buf |ps, s_len| { - let mut len = len; - - // XXX: 672 == NID_sha256 - libcrypto::RSA_sign( - 672 as c_int, - ps, - s_len as c_uint, - pr, - &mut len, - rsa) - } - }; + do s.as_imm_buf |ps, s_len| { + libcrypto::RSA_sign( + openssl_hash_nid(hash), + ps, + s_len as c_uint, + pr, + &mut len, + rsa) + } + }; if rv < 0 as c_int { ~[] @@ -298,19 +328,14 @@ impl PKey { } } - /** - * Verifies a signature s (using OpenSSL's default scheme and sha256) on a - * message m. Returns true if the signature is valid, and false otherwise. - */ - pub fn verify(&self, m: &[u8], s: &[u8]) -> bool { + pub fn verify_with_hash(&self, m: &[u8], s: &[u8], hash: HashType) -> bool { unsafe { let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); do m.as_imm_buf |pm, m_len| { do s.as_imm_buf |ps, s_len| { - // XXX: 672 == NID_sha256 let rv = libcrypto::RSA_verify( - 672 as c_int, + openssl_hash_nid(hash), pm, m_len as c_uint, ps, @@ -336,6 +361,7 @@ impl Drop for PKey { #[cfg(test)] mod tests { use super::*; + use hash::{MD5, SHA1}; #[test] fn test_gen_pub() { @@ -385,6 +411,18 @@ mod tests { assert!(msg == dmsg); } + #[test] + fn test_encrypt_pkcs() { + let mut k0 = PKey::new(); + let mut k1 = PKey::new(); + let msg = ~[0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; + k0.gen(512u); + k1.load_pub(k0.save_pub()); + let emsg = k1.encrypt_with_padding(msg, PKCS1v15); + let dmsg = k0.decrypt_with_padding(emsg, PKCS1v15); + assert!(msg == dmsg); + } + #[test] fn test_sign() { let mut k0 = PKey::new(); @@ -396,4 +434,18 @@ mod tests { let rv = k1.verify(msg, sig); assert!(rv == true); } + + #[test] + fn test_sign_hashes() { + let mut k0 = PKey::new(); + let mut k1 = PKey::new(); + let msg = ~[0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; + k0.gen(512u); + k1.load_pub(k0.save_pub()); + + let sig = k0.sign_with_hash(msg, MD5); + + assert!(k1.verify_with_hash(msg, sig, MD5)); + assert!(!k1.verify_with_hash(msg, sig, SHA1)); + } } diff --git a/rand.rs b/rand.rs index b05706a0..eedac4ab 100644 --- a/rand.rs +++ b/rand.rs @@ -29,6 +29,7 @@ mod tests { #[test] fn test_rand_bytes() { - let _bytes = rand_bytes(5u); + let bytes = rand_bytes(32u); + println(fmt!("%?", bytes)); } } diff --git a/symm.rs b/symm.rs index c4ed97e9..bc28624a 100644 --- a/symm.rs +++ b/symm.rs @@ -8,7 +8,7 @@ pub type EVP_CIPHER_CTX = *libc::c_void; #[allow(non_camel_case_types)] pub type EVP_CIPHER = *libc::c_void; -pub mod libcrypto { +mod libcrypto { use super::*; use std::libc::{c_int, c_uint}; @@ -20,10 +20,15 @@ pub mod libcrypto { fn EVP_aes_128_ecb() -> EVP_CIPHER; fn EVP_aes_128_cbc() -> EVP_CIPHER; - fn EVP_aes_192_ecb() -> EVP_CIPHER; - fn EVP_aes_192_cbc() -> EVP_CIPHER; + // fn EVP_aes_128_ctr() -> EVP_CIPHER; + // fn EVP_aes_128_gcm() -> EVP_CIPHER; + fn EVP_aes_256_ecb() -> EVP_CIPHER; fn EVP_aes_256_cbc() -> EVP_CIPHER; + // fn EVP_aes_256_ctr() -> EVP_CIPHER; + // fn EVP_aes_256_gcm() -> EVP_CIPHER; + + fn EVP_rc4() -> EVP_CIPHER; fn EVP_CipherInit(ctx: EVP_CIPHER_CTX, evp: EVP_CIPHER, key: *u8, iv: *u8, mode: c_int); @@ -40,15 +45,33 @@ pub enum Mode { #[allow(non_camel_case_types)] pub enum Type { + AES_128_ECB, + AES_128_CBC, + // AES_128_CTR, + //AES_128_GCM, + AES_256_ECB, AES_256_CBC, + // AES_256_CTR, + //AES_256_GCM, + + RC4_128, } fn evpc(t: Type) -> (EVP_CIPHER, uint, uint) { unsafe { match t { + AES_128_ECB => (libcrypto::EVP_aes_128_ecb(), 16u, 16u), + AES_128_CBC => (libcrypto::EVP_aes_128_cbc(), 16u, 16u), + // AES_128_CTR => (libcrypto::EVP_aes_128_ctr(), 16u, 0u), + //AES_128_GCM => (libcrypto::EVP_aes_128_gcm(), 16u, 16u), + AES_256_ECB => (libcrypto::EVP_aes_256_ecb(), 32u, 16u), AES_256_CBC => (libcrypto::EVP_aes_256_cbc(), 32u, 16u), + // AES_256_CTR => (libcrypto::EVP_aes_256_ctr(), 32u, 0u), + //AES_256_GCM => (libcrypto::EVP_aes_256_gcm(), 32u, 16u), + + RC4_128 => (libcrypto::EVP_rc4(), 16u, 0u), } } } @@ -73,8 +96,12 @@ impl Crypter { * data encrypted must be a multiple of block size. */ pub fn pad(&self, padding: bool) { - let v = if padding { 1 } else { 0} as c_int; - unsafe { libcrypto::EVP_CIPHER_CTX_set_padding(self.ctx, v) }; + if self.blocksize > 0 { + unsafe { + let v = if padding { 1 } else { 0 } as c_int; + libcrypto::EVP_CIPHER_CTX_set_padding(self.ctx, v); + } + } } /** @@ -186,6 +213,8 @@ pub fn decrypt(t: Type, key: &[u8], iv: ~[u8], data: &[u8]) -> ~[u8] { mod tests { use super::*; + use hex::FromHex; + // Test vectors from FIPS-197: // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf #[test] @@ -211,4 +240,57 @@ mod tests { let p1 = c.update(r0) + c.final(); assert!(p1 == p0); } + + fn cipher_test(ciphertype: Type, pt: ~str, ct: ~str, key: ~str, iv: ~str) { + use hex::ToHex; + + let cipher = Crypter::new(ciphertype); + cipher.init(Encrypt, key.from_hex(), iv.from_hex()); + + let expected = ct.from_hex(); + let computed = cipher.update(pt.from_hex()) + cipher.final(); + + if computed != expected { + println(fmt!("Computed: %s", computed.to_hex())); + println(fmt!("Expected: %s", expected.to_hex())); + if computed.len() != expected.len() { + println(fmt!("Lengths differ: %u in computed vs %u expected", + computed.len(), expected.len())); + } + fail!(~"test failure"); + } + } + + #[test] + fn test_rc4() { + + let pt = ~"0000000000000000000000000000000000000000000000000000000000000000000000000000"; + let ct = ~"A68686B04D686AA107BD8D4CAB191A3EEC0A6294BC78B60F65C25CB47BD7BB3A48EFC4D26BE4"; + let key = ~"97CD440324DA5FD1F7955C1C13B6B466"; + let iv = ~""; + + cipher_test(RC4_128, pt, ct, key, iv); + } + + /*#[test] + fn test_aes128_ctr() { + + let pt = ~"6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710"; + let ct = ~"874D6191B620E3261BEF6864990DB6CE9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE"; + let key = ~"2B7E151628AED2A6ABF7158809CF4F3C"; + let iv = ~"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"; + + cipher_test(AES_128_CTR, pt, ct, key, iv); + }*/ + + /*#[test] + fn test_aes128_gcm() { + // Test case 3 in GCM spec + let pt = ~"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255"; + let ct = ~"42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f59854d5c2af327cd64a62cf35abd2ba6fab4"; + let key = ~"feffe9928665731c6d6a8f9467308308"; + let iv = ~"cafebabefacedbaddecaf888"; + + cipher_test(AES_128_GCM, pt, ct, key, iv); + }*/ }