Merge remote-tracking branch 'remotes/kballard/master'

Conflicts:
	crypto.rs
	hash.rs
	pkcs5.rs
	pkey.rs
	rand.rs
	symm.rs
This commit is contained in:
Erick Tryzelaar 2013-08-15 08:20:47 -07:00
commit 08374ec054
10 changed files with 490 additions and 129 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
*.sw[po] *.sw[po]
libcrypto*.dylib libcrypto*.dylib
libcrypto*.so
*.dSYM/ *.dSYM/
crypto crypto

9
Makefile Normal file
View File

@ -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

View File

@ -1,21 +1,13 @@
This package provides Rust bindings for the functionality exposed by OpenSSL's 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) * Hash functions (hash.rs)
* MD5 * SHA-512, SHA-384, SHA-256, SHA-224
* SHA-1 * SHA-1
* SHA-2 (224, 256, 384, 512) * MD5
* Symmetric crypto (symm.rs) * Symmetric crypto (symm.rs)
* AES in ECB or CBC mode, all key lengths * AES-128 and AES-256 (ECB, CBC, CTR or GCM mode)
* Keypair generation (pkey.rs) * RC4-128
* RSA, all key lengths * RSA (pkey.rs)
* Asymmetric encryption (pkey.rs) * Encryption with PKCS #1 OAEP padding or PKCS #1 v1.5 padding
* RSA with PKCS#1 OAEP padding * Signatures with PKCS #1 v1.5 padding and any supported hash
* 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.

View File

@ -1,5 +1,6 @@
/* /*
* Copyright 2011 Google Inc. * Copyright 2011 Google Inc.
* 2013 Jack Lloyd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,12 +16,14 @@
*/ */
#[link(name = "crypto", #[link(name = "crypto",
vers = "0.2", vers = "0.3",
uuid = "38297409-b4c2-4499-8131-a99a7e44dad3")]; uuid = "38297409-b4c2-4499-8131-a99a7e44dad3")];
#[crate_type = "lib"]; #[crate_type = "lib"];
pub mod hash; pub mod hash;
pub mod pkey; pub mod hex;
pub mod symm; pub mod hmac;
pub mod pkcs5; pub mod pkcs5;
pub mod pkey;
pub mod rand; pub mod rand;
pub mod symm;

76
hash.rs
View File

@ -40,7 +40,7 @@ mod libcrypto {
} }
} }
fn evpmd(t: HashType) -> (EVP_MD, uint) { pub fn evpmd(t: HashType) -> (EVP_MD, uint) {
unsafe { unsafe {
match t { match t {
MD5 => (libcrypto::EVP_md5(), 16u), MD5 => (libcrypto::EVP_md5(), 16u),
@ -115,35 +115,73 @@ pub fn hash(t: HashType, data: &[u8]) -> ~[u8] {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; 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 vectors from http://www.nsrl.nist.gov/testdata/
#[test] #[test]
fn test_md5() { fn test_md5() {
let s0 = ~[0x61u8, 0x62u8, 0x63u8]; let tests = [
let d0 = HashTest(~"", ~"D41D8CD98F00B204E9800998ECF8427E"),
~[0x90u8, 0x01u8, 0x50u8, 0x98u8, 0x3cu8, 0xd2u8, 0x4fu8, 0xb0u8, HashTest(~"7F", ~"83ACB6E67E50E31DB6ED341DD2DE1595"),
0xd6u8, 0x96u8, 0x3fu8, 0x7du8, 0x28u8, 0xe1u8, 0x7fu8, 0x72u8]; HashTest(~"EC9C", ~"0B07F0D4CA797D8AC58874F887CB0B68"),
assert!(hash(MD5, s0) == d0); 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] #[test]
fn test_sha1() { fn test_sha1() {
let s0 = ~[0x61u8, 0x62u8, 0x63u8]; let tests = [
let d0 = HashTest(~"616263", ~"A9993E364706816ABA3E25717850C26C9CD0D89D"),
~[0xa9u8, 0x99u8, 0x3eu8, 0x36u8, 0x47u8, 0x06u8, 0x81u8, 0x6au8, ];
0xbau8, 0x3eu8, 0x25u8, 0x71u8, 0x78u8, 0x50u8, 0xc2u8, 0x6cu8,
0x9cu8, 0xd0u8, 0xd8u8, 0x9du8]; for test in tests.iter() {
assert!(hash(SHA1, s0) == d0); hash_test(SHA1, test);
}
} }
#[test] #[test]
fn test_sha256() { fn test_sha256() {
let s0 = ~[0x61u8, 0x62u8, 0x63u8]; let tests = [
let d0 = HashTest(~"616263", ~"BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD")
~[0xbau8, 0x78u8, 0x16u8, 0xbfu8, 0x8fu8, 0x01u8, 0xcfu8, 0xeau8, ];
0x41u8, 0x41u8, 0x40u8, 0xdeu8, 0x5du8, 0xaeu8, 0x22u8, 0x23u8,
0xb0u8, 0x03u8, 0x61u8, 0xa3u8, 0x96u8, 0x17u8, 0x7au8, 0x9cu8, for test in tests.iter() {
0xb4u8, 0x10u8, 0xffu8, 0x61u8, 0xf2u8, 0x00u8, 0x15u8, 0xadu8]; hash_test(SHA256, test);
assert!(hash(SHA256, s0) == d0); }
} }
} }

87
hex.rs Normal file
View File

@ -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");
}
}

96
hmac.rs Normal file
View File

@ -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()))
}

116
pkey.rs
View File

@ -3,11 +3,11 @@ use std::libc::{c_int, c_uint};
use std::libc; use std::libc;
use std::ptr; use std::ptr;
use std::vec; use std::vec;
use hash::{HashType, MD5, SHA1, SHA224, SHA256, SHA384, SHA512};
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub type EVP_PKEY = *libc::c_void; pub type EVP_PKEY = *libc::c_void;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub type RSA = *libc::c_void; pub type RSA = *libc::c_void;
@ -16,7 +16,7 @@ mod libcrypto {
use std::libc::{c_char, c_int, c_uint}; use std::libc::{c_char, c_int, c_uint};
#[link_args = "-lcrypto"] #[link_args = "-lcrypto"]
extern "C" { extern {
fn EVP_PKEY_new() -> *EVP_PKEY; fn EVP_PKEY_new() -> *EVP_PKEY;
fn EVP_PKEY_free(k: *EVP_PKEY); fn EVP_PKEY_free(k: *EVP_PKEY);
fn EVP_PKEY_assign(pkey: *EVP_PKEY, typ: c_int, key: *c_char) -> c_int; fn EVP_PKEY_assign(pkey: *EVP_PKEY, typ: c_int, key: *c_char) -> c_int;
@ -55,6 +55,30 @@ pub enum Role {
Verify 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 { pub struct PKey {
priv evp: *EVP_PKEY, priv evp: *EVP_PKEY,
priv parts: Parts, priv parts: Parts,
@ -195,32 +219,26 @@ impl PKey {
} }
} }
/** pub fn encrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> ~[u8] {
* 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 { unsafe {
let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp);
let len = libcrypto::RSA_size(rsa); let len = libcrypto::RSA_size(rsa);
// 41 comes from RSA_public_encrypt(3) for OAEP assert!(s.len() < self.max_data());
assert!(s.len() < libcrypto::RSA_size(rsa) as uint - 41u);
let mut r = vec::from_elem(len as uint + 1u, 0u8); let mut r = vec::from_elem(len as uint + 1u, 0u8);
let rv = do r.as_mut_buf |pr, _len| { let rv = do r.as_mut_buf |pr, _len| {
do s.as_imm_buf |ps, s_len| { do s.as_imm_buf |ps, s_len| {
// XXX: 4 == RSA_PKCS1_OAEP_PADDING
libcrypto::RSA_public_encrypt( libcrypto::RSA_public_encrypt(
s_len as c_uint, s_len as c_uint,
ps, ps,
pr, pr,
rsa, 4 as c_int rsa,
openssl_padding_code(padding)
) )
} }
}; };
if rv < 0 as c_int { if rv < 0 as c_int {
~[] ~[]
} else { } else {
@ -230,27 +248,23 @@ impl PKey {
} }
} }
/** pub fn decrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> ~[u8] {
* Decrypts data, expecting OAEP padding, returning the decrypted data.
*/
pub fn decrypt(&self, s: &[u8]) -> ~[u8] {
unsafe { unsafe {
let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp);
let len = libcrypto::RSA_size(rsa); let len = libcrypto::RSA_size(rsa);
assert!(s.len() as c_uint == 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 mut r = vec::from_elem(len as uint + 1u, 0u8);
let rv = do r.as_mut_buf |pr, _len| { let rv = do r.as_mut_buf |pr, _len| {
do s.as_imm_buf |ps, s_len| { do s.as_imm_buf |ps, s_len| {
// XXX: 4 == RSA_PKCS1_OAEP_PADDING
libcrypto::RSA_private_decrypt( libcrypto::RSA_private_decrypt(
s_len as c_uint, s_len as c_uint,
ps, ps,
pr, pr,
rsa, rsa,
4 as c_int openssl_padding_code(padding)
) )
} }
}; };
@ -264,23 +278,39 @@ impl PKey {
} }
} }
/**
* 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] { self.encrypt_with_padding(s, OAEP) }
/**
* Decrypts data, expecting OAEP padding, returning the decrypted data.
*/
pub fn decrypt(&self, s: &[u8]) -> ~[u8] { self.decrypt_with_padding(s, OAEP) }
/** /**
* Signs data, using OpenSSL's default scheme and sha256. Unlike encrypt(), * Signs data, using OpenSSL's default scheme and sha256. Unlike encrypt(),
* can process an arbitrary amount of data; returns the signature. * 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 { unsafe {
let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); 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 mut r = vec::from_elem(len as uint + 1u, 0u8);
let rv = do r.as_mut_buf |pr, _len| { let rv = do r.as_mut_buf |pr, _len| {
do s.as_imm_buf |ps, s_len| { do s.as_imm_buf |ps, s_len| {
let mut len = len;
// XXX: 672 == NID_sha256
libcrypto::RSA_sign( libcrypto::RSA_sign(
672 as c_int, openssl_hash_nid(hash),
ps, ps,
s_len as c_uint, s_len as c_uint,
pr, pr,
@ -298,19 +328,14 @@ impl PKey {
} }
} }
/** pub fn verify_with_hash(&self, m: &[u8], s: &[u8], hash: HashType) -> bool {
* 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 {
unsafe { unsafe {
let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp); let rsa = libcrypto::EVP_PKEY_get1_RSA(self.evp);
do m.as_imm_buf |pm, m_len| { do m.as_imm_buf |pm, m_len| {
do s.as_imm_buf |ps, s_len| { do s.as_imm_buf |ps, s_len| {
// XXX: 672 == NID_sha256
let rv = libcrypto::RSA_verify( let rv = libcrypto::RSA_verify(
672 as c_int, openssl_hash_nid(hash),
pm, pm,
m_len as c_uint, m_len as c_uint,
ps, ps,
@ -336,6 +361,7 @@ impl Drop for PKey {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use hash::{MD5, SHA1};
#[test] #[test]
fn test_gen_pub() { fn test_gen_pub() {
@ -385,6 +411,18 @@ mod tests {
assert!(msg == dmsg); 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] #[test]
fn test_sign() { fn test_sign() {
let mut k0 = PKey::new(); let mut k0 = PKey::new();
@ -396,4 +434,18 @@ mod tests {
let rv = k1.verify(msg, sig); let rv = k1.verify(msg, sig);
assert!(rv == true); 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));
}
} }

View File

@ -29,6 +29,7 @@ mod tests {
#[test] #[test]
fn test_rand_bytes() { fn test_rand_bytes() {
let _bytes = rand_bytes(5u); let bytes = rand_bytes(32u);
println(fmt!("%?", bytes));
} }
} }

92
symm.rs
View File

@ -8,7 +8,7 @@ pub type EVP_CIPHER_CTX = *libc::c_void;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub type EVP_CIPHER = *libc::c_void; pub type EVP_CIPHER = *libc::c_void;
pub mod libcrypto { mod libcrypto {
use super::*; use super::*;
use std::libc::{c_int, c_uint}; 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_ecb() -> EVP_CIPHER;
fn EVP_aes_128_cbc() -> EVP_CIPHER; fn EVP_aes_128_cbc() -> EVP_CIPHER;
fn EVP_aes_192_ecb() -> EVP_CIPHER; // fn EVP_aes_128_ctr() -> EVP_CIPHER;
fn EVP_aes_192_cbc() -> EVP_CIPHER; // fn EVP_aes_128_gcm() -> EVP_CIPHER;
fn EVP_aes_256_ecb() -> EVP_CIPHER; fn EVP_aes_256_ecb() -> EVP_CIPHER;
fn EVP_aes_256_cbc() -> 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, fn EVP_CipherInit(ctx: EVP_CIPHER_CTX, evp: EVP_CIPHER,
key: *u8, iv: *u8, mode: c_int); key: *u8, iv: *u8, mode: c_int);
@ -40,15 +45,33 @@ pub enum Mode {
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
pub enum Type { pub enum Type {
AES_128_ECB,
AES_128_CBC,
// AES_128_CTR,
//AES_128_GCM,
AES_256_ECB, AES_256_ECB,
AES_256_CBC, AES_256_CBC,
// AES_256_CTR,
//AES_256_GCM,
RC4_128,
} }
fn evpc(t: Type) -> (EVP_CIPHER, uint, uint) { fn evpc(t: Type) -> (EVP_CIPHER, uint, uint) {
unsafe { unsafe {
match t { 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_ECB => (libcrypto::EVP_aes_256_ecb(), 32u, 16u),
AES_256_CBC => (libcrypto::EVP_aes_256_cbc(), 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. * data encrypted must be a multiple of block size.
*/ */
pub fn pad(&self, padding: bool) { pub fn pad(&self, padding: bool) {
let v = if padding { 1 } else { 0} as c_int; if self.blocksize > 0 {
unsafe { libcrypto::EVP_CIPHER_CTX_set_padding(self.ctx, v) }; 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 { mod tests {
use super::*; use super::*;
use hex::FromHex;
// Test vectors from FIPS-197: // Test vectors from FIPS-197:
// http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
#[test] #[test]
@ -211,4 +240,57 @@ mod tests {
let p1 = c.update(r0) + c.final(); let p1 = c.update(r0) + c.final();
assert!(p1 == p0); 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);
}*/
} }