Expose EVP_BytesToKey

This is based on work by pyrho.
Closes #88
This commit is contained in:
Edward Barnard 2015-08-03 18:22:07 +01:00
parent a10604e15d
commit 8067565707
5 changed files with 144 additions and 23 deletions

View File

@ -128,6 +128,8 @@ pub const MBSTRING_UTF8: c_int = MBSTRING_FLAG;
pub const NID_ext_key_usage: c_int = 126;
pub const NID_key_usage: c_int = 83;
pub const PKCS5_SALT_LEN: c_int = 8;
pub const SSL_CTRL_OPTIONS: c_int = 32;
pub const SSL_CTRL_CLEAR_OPTIONS: c_int = 77;
@ -409,6 +411,10 @@ extern "C" {
// fn EVP_aes_256_gcm() -> EVP_CIPHER;
pub fn EVP_rc4() -> *const EVP_CIPHER;
pub fn EVP_BytesToKey(typ: *const EVP_CIPHER, md: *const EVP_MD,
salt: *const u8, data: *const u8, datalen: c_int,
count: c_int, key: *mut u8, iv: *mut u8) -> c_int;
pub fn EVP_CIPHER_CTX_new() -> *mut EVP_CIPHER_CTX;
pub fn EVP_CIPHER_CTX_set_padding(ctx: *mut EVP_CIPHER_CTX, padding: c_int) -> c_int;
pub fn EVP_CIPHER_CTX_free(ctx: *mut EVP_CIPHER_CTX);

View File

@ -22,3 +22,5 @@ pub mod pkey;
pub mod rand;
pub mod symm;
pub mod memcmp;
mod symm_internal;

View File

@ -1,6 +1,69 @@
use libc::c_int;
use std::ptr::null;
use crypto::symm_internal::evpc;
use crypto::hash;
use crypto::symm;
use ffi;
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct KeyIvPair
{
pub key: Vec<u8>,
pub iv: Vec<u8>
}
/// Derives a key and an IV from various parameters.
///
/// If specified `salt` must be 8 bytes in length.
///
/// If the total key and IV length is less than 16 bytes and MD5 is used then
/// the algorithm is compatible with the key derivation algorithm from PKCS#5
/// v1.5 or PBKDF1 from PKCS#5 v2.0.
///
/// New applications should not use this and instead use `pbkdf2_hmac_sha1` or
/// another more modern key derivation algorithm.
pub fn evp_bytes_to_key_pbkdf1_compatible(typ: symm::Type, message_digest_type: hash::Type,
data: &[u8], salt: Option<&[u8]>,
count: u32) -> KeyIvPair {
unsafe {
let salt_ptr = match salt {
Some(salt) => {
assert_eq!(salt.len(), ffi::PKCS5_SALT_LEN as usize);
salt.as_ptr()
},
None => null()
};
ffi::init();
let (evp, keylen, _) = evpc(typ);
let message_digest = message_digest_type.evp_md();
let mut key = vec![0; keylen as usize];
let mut iv = vec![0; keylen as usize];
let ret: c_int = ffi::EVP_BytesToKey(evp,
message_digest,
salt_ptr,
data.as_ptr(),
data.len() as c_int,
count as c_int,
key.as_mut_ptr(),
iv.as_mut_ptr());
assert!(ret == keylen as c_int);
KeyIvPair {
key: key,
iv: iv
}
}
}
/// Derives a key from a password and salt using the PBKDF2-HMAC-SHA1 algorithm.
pub fn pbkdf2_hmac_sha1(pass: &str, salt: &[u8], iter: usize, keylen: usize) -> Vec<u8> {
unsafe {
@ -27,6 +90,9 @@ pub fn pbkdf2_hmac_sha1(pass: &str, salt: &[u8], iter: usize, keylen: usize) ->
#[cfg(test)]
mod tests {
use crypto::hash;
use crypto::symm;
// Test vectors from
// http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06
#[test]
@ -116,4 +182,47 @@ mod tests {
)
);
}
#[test]
fn test_evp_bytes_to_key_pbkdf1_compatible() {
let salt = [
16_u8, 34_u8, 19_u8, 23_u8, 141_u8, 4_u8, 207_u8, 221_u8
];
let data = [
143_u8, 210_u8, 75_u8, 63_u8, 214_u8, 179_u8, 155_u8,
241_u8, 242_u8, 31_u8, 154_u8, 56_u8, 198_u8, 145_u8, 192_u8, 64_u8,
2_u8, 245_u8, 167_u8, 220_u8, 55_u8, 119_u8, 233_u8, 136_u8, 139_u8,
27_u8, 71_u8, 242_u8, 119_u8, 175_u8, 65_u8, 207_u8
];
let expected_key = vec![
249_u8, 115_u8, 114_u8, 97_u8, 32_u8, 213_u8, 165_u8, 146_u8, 58_u8,
87_u8, 234_u8, 3_u8, 43_u8, 250_u8, 97_u8, 114_u8, 26_u8, 98_u8,
245_u8, 246_u8, 238_u8, 177_u8, 229_u8, 161_u8, 183_u8, 224_u8,
174_u8, 3_u8, 6_u8, 244_u8, 236_u8, 255_u8
];
let expected_iv = vec![
4_u8, 223_u8, 153_u8, 219_u8, 28_u8, 142_u8, 234_u8, 68_u8, 227_u8,
69_u8, 98_u8, 107_u8, 208_u8, 14_u8, 236_u8, 60_u8, 0_u8, 0_u8,
0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8, 0_u8,
0_u8, 0_u8, 0_u8
];
assert_eq!(
super::evp_bytes_to_key_pbkdf1_compatible(
symm::Type::AES_256_CBC,
hash::Type::SHA1,
&data,
Some(&salt),
1
),
super::KeyIvPair {
key: expected_key,
iv: expected_iv
}
);
}
}

View File

@ -2,6 +2,7 @@ use std::iter::repeat;
use std::convert::AsRef;
use libc::{c_int};
use crypto::symm_internal::evpc;
use ffi;
#[derive(Copy, Clone)]
@ -34,29 +35,6 @@ pub enum Type {
RC4_128,
}
fn evpc(t: Type) -> (*const ffi::EVP_CIPHER, u32, u32) {
unsafe {
match t {
Type::AES_128_ECB => (ffi::EVP_aes_128_ecb(), 16, 16),
Type::AES_128_CBC => (ffi::EVP_aes_128_cbc(), 16, 16),
#[cfg(feature = "aes_xts")]
Type::AES_128_XTS => (ffi::EVP_aes_128_xts(), 32, 16),
#[cfg(feature = "aes_ctr")]
Type::AES_128_CTR => (ffi::EVP_aes_128_ctr(), 16, 0),
//AES_128_GCM => (EVP_aes_128_gcm(), 16, 16),
Type::AES_256_ECB => (ffi::EVP_aes_256_ecb(), 32, 16),
Type::AES_256_CBC => (ffi::EVP_aes_256_cbc(), 32, 16),
#[cfg(feature = "aes_xts")]
Type::AES_256_XTS => (ffi::EVP_aes_256_xts(), 64, 16),
#[cfg(feature = "aes_ctr")]
Type::AES_256_CTR => (ffi::EVP_aes_256_ctr(), 32, 0),
//AES_256_GCM => (EVP_aes_256_gcm(), 32, 16),
Type::RC4_128 => (ffi::EVP_rc4(), 16, 0),
}
}
}
/// Represents a symmetric cipher context.
pub struct Crypter {

View File

@ -0,0 +1,26 @@
use crypto::symm;
use ffi;
pub fn evpc(t: symm::Type) -> (*const ffi::EVP_CIPHER, u32, u32) {
unsafe {
match t {
symm::Type::AES_128_ECB => (ffi::EVP_aes_128_ecb(), 16, 16),
symm::Type::AES_128_CBC => (ffi::EVP_aes_128_cbc(), 16, 16),
#[cfg(feature = "aes_xts")]
symm::Type::AES_128_XTS => (ffi::EVP_aes_128_xts(), 32, 16),
#[cfg(feature = "aes_ctr")]
symm::Type::AES_128_CTR => (ffi::EVP_aes_128_ctr(), 16, 0),
//AES_128_GCM => (EVP_aes_128_gcm(), 16, 16),
symm::Type::AES_256_ECB => (ffi::EVP_aes_256_ecb(), 32, 16),
symm::Type::AES_256_CBC => (ffi::EVP_aes_256_cbc(), 32, 16),
#[cfg(feature = "aes_xts")]
symm::Type::AES_256_XTS => (ffi::EVP_aes_256_xts(), 64, 16),
#[cfg(feature = "aes_ctr")]
symm::Type::AES_256_CTR => (ffi::EVP_aes_256_ctr(), 32, 0),
//AES_256_GCM => (EVP_aes_256_gcm(), 32, 16),
symm::Type::RC4_128 => (ffi::EVP_rc4(), 16, 0),
}
}
}