Merge remote-tracking branch 'upstream/master'

Conflicts:
	src/bn/mod.rs
This commit is contained in:
Chris Cole 2014-11-29 19:47:09 -05:00
commit 5f76f1cb62
30 changed files with 2290 additions and 1027 deletions

4
.gitignore vendored
View File

@ -1,3 +1,3 @@
/doc/
/target/
/Cargo.lock
target/
Cargo.lock

View File

@ -2,16 +2,20 @@ language: rust
env:
global:
- secure: qLvBJoJOJcPPZ+e31175O6sMUGBHgHe/kBuI0FCPeifYmpFyeRAkEvGddEkf8t3rojV+wE14CNYzzGsT/W/+JY7xW0C1FQKW3r+8SZ1Cave/8ahee0aCQVXGf0XY8c52uG6MrLGiUlNZbOsyFSdFUc/Io+kYZas4DxrinRSOIEA=
matrix:
- FEATURES=""
- FEATURES="tlsv1_1 tlsv1_2 aes_xts"
os:
- osx
- linux
before_script:
- openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem >/dev/null &
- openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem >/dev/null 2>&1 &
script:
- cargo build
- cargo test
- rustdoc src/lib.rs
- cargo build --features "sslv2"
- cargo build --features "tlsv1_1 tlsv1_2"
after_script:
- curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh
- cargo build --features "$FEATURES"
- cargo test --features "$FEATURES"
- cargo doc --features "sslv2 tlsv1_1 tlsv1_2 aes_xts"
after_success: |
[ $TRAVIS_BRANCH = master ] &&
[ $TRAVIS_PULL_REQUEST = false ] &&
cd target/doc &&
(curl http://www.rust-ci.org/artifacts/put?t=$RUSTCI_TOKEN | sh)

View File

@ -1,15 +1,20 @@
[package]
name = "openssl"
version = "0.0.0"
authors = ["Steven Fackler <sfackler@gmail.com"]
[lib]
name = "openssl"
path = "src/lib.rs"
version = "0.2.1"
authors = ["Steven Fackler <sfackler@gmail.com>"]
license = "Apache-2.0"
description = "OpenSSL bindings"
repository = "https://github.com/sfackler/rust-openssl"
documentation = "https://sfackler.github.io/doc/openssl"
readme = "README.md"
keywords = ["crypto", "tls", "ssl"]
[features]
tlsv1_2 = []
tlsv1_1 = []
sslv2 = []
tlsv1_2 = ["openssl-sys/tlsv1_2"]
tlsv1_1 = ["openssl-sys/tlsv1_1"]
sslv2 = ["openssl-sys/sslv2"]
aes_xts = ["openssl-sys/aes_xts"]
[dependencies.openssl-sys]
path = "openssl-sys"
version = "0.2.1"

View File

@ -1,7 +1,7 @@
rust-openssl [![Build Status](https://travis-ci.org/sfackler/rust-openssl.svg?branch=master)](https://travis-ci.org/sfackler/rust-openssl)
============
See the [rustdoc output](http://www.rust-ci.org/sfackler/rust-openssl/doc/openssl/).
See the [rustdoc output](https://sfackler.github.io/doc/openssl).
Building
--------

27
THIRD_PARTY Normal file
View File

@ -0,0 +1,27 @@
openssl-sys
Copyright (c) 2014 Alex Crichton
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

20
openssl-sys/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "openssl-sys"
version = "0.2.1"
authors = ["Alex Crichton <alex@alexcrichton.com>",
"Steven Fackler <sfackler@gmail.com>"]
license = "MIT"
description = "FFI bindings to OpenSSL"
repository = "https://github.com/sfackler/rust-openssl"
links = "openssl"
build = "src/build.rs"
[features]
tlsv1_2 = []
tlsv1_1 = []
sslv2 = []
aes_xts = []
[build-dependencies]
pkg-config = "0.1"

25
openssl-sys/LICENSE-MIT Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) 2014 Alex Crichton
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

22
openssl-sys/src/build.rs Normal file
View File

@ -0,0 +1,22 @@
extern crate "pkg-config" as pkg_config;
use std::os;
fn main() {
if pkg_config::find_library("openssl").is_err() {
let mut flags = " -l crypto -l ssl".to_string();
let target = os::getenv("TARGET").unwrap();
let win_pos = target.find_str("windows")
.or(target.find_str("win32"))
.or(target.find_str("win64"));
// It's fun, but it looks like win32 and win64 both
// have all the libs with 32 sufix
if win_pos.is_some() {
flags.push_str(" -l gdi32 -l wsock32");
}
println!("cargo:rustc-flags={}", flags);
}
}

480
openssl-sys/src/lib.rs Normal file
View File

@ -0,0 +1,480 @@
#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
#![allow(dead_code)]
extern crate libc;
extern crate rustrt;
use libc::{c_void, c_int, c_char, c_ulong, c_long, c_uint, c_uchar, size_t};
use std::mem;
use std::ptr;
use rustrt::mutex::NativeMutex;
use std::sync::{Once, ONCE_INIT};
pub type ASN1_INTEGER = c_void;
pub type ASN1_STRING = c_void;
pub type ASN1_TIME = c_void;
pub type BIO = c_void;
pub type BIO_METHOD = c_void;
pub type BN_CTX = c_void;
pub type COMP_METHOD = c_void;
pub type CRYPTO_EX_DATA = c_void;
pub type ENGINE = c_void;
pub type EVP_CIPHER = c_void;
pub type EVP_CIPHER_CTX = c_void;
pub type EVP_MD = c_void;
pub type EVP_PKEY = c_void;
pub type EVP_PKEY_CTX = c_void;
pub type RSA = c_void;
pub type SSL = c_void;
pub type SSL_CTX = c_void;
pub type SSL_METHOD = c_void;
pub type X509 = c_void;
pub type X509_CRL = c_void;
pub type X509_EXTENSION = c_void;
pub type X509_NAME = c_void;
pub type X509_NAME_ENTRY = c_void;
pub type X509_REQ = c_void;
pub type X509_STORE_CTX = c_void;
#[repr(C)]
pub struct EVP_MD_CTX {
digest: *mut EVP_MD,
engine: *mut c_void,
flags: c_ulong,
md_data: *mut c_void,
pctx: *mut EVP_PKEY_CTX,
update: *mut c_void
}
#[repr(C)]
pub struct HMAC_CTX {
md: *mut EVP_MD,
md_ctx: EVP_MD_CTX,
i_ctx: EVP_MD_CTX,
o_ctx: EVP_MD_CTX,
key_length: c_uint,
key: [c_uchar, ..128]
}
#[repr(C)]
pub struct X509V3_CTX {
flags: c_int,
issuer_cert: *mut c_void,
subject_cert: *mut c_void,
subject_req: *mut c_void,
crl: *mut c_void,
db_meth: *mut c_void,
db: *mut c_void,
// I like the last comment line, it is copied from OpenSSL sources:
// Maybe more here
}
#[repr(C)]
pub struct BIGNUM {
pub d: *mut c_void,
pub top: c_int,
pub dmax: c_int,
pub neg: c_int,
pub flags: c_int,
}
pub type CRYPTO_EX_new = extern "C" fn(parent: *mut c_void, ptr: *mut c_void,
ad: *const CRYPTO_EX_DATA, idx: c_int,
argl: c_long, argp: *const c_void) -> c_int;
pub type CRYPTO_EX_dup = extern "C" fn(to: *mut CRYPTO_EX_DATA,
from: *mut CRYPTO_EX_DATA, from_d: *mut c_void,
idx: c_int, argl: c_long, argp: *mut c_void)
-> c_int;
pub type CRYPTO_EX_free = extern "C" fn(parent: *mut c_void, ptr: *mut c_void,
ad: *mut CRYPTO_EX_DATA, idx: c_int,
argl: c_long, argp: *mut c_void);
pub type PasswordCallback = extern "C" fn(buf: *mut c_char, size: c_int,
rwflag: c_int, user_data: *mut c_void)
-> c_int;
pub const BIO_CTRL_EOF: c_int = 2;
pub const CRYPTO_LOCK: c_int = 1;
pub const MBSTRING_ASC: c_int = MBSTRING_FLAG | 1;
pub const MBSTRING_BMP: c_int = MBSTRING_FLAG | 2;
pub const MBSTRING_FLAG: c_int = 0x1000;
pub const MBSTRING_UNIV: c_int = MBSTRING_FLAG | 4;
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 SSL_CTRL_SET_TLSEXT_HOSTNAME: c_int = 55;
pub const SSL_ERROR_NONE: c_int = 0;
pub const SSL_ERROR_SSL: c_int = 1;
pub const SSL_ERROR_SYSCALL: c_int = 5;
pub const SSL_ERROR_WANT_ACCEPT: c_int = 8;
pub const SSL_ERROR_WANT_CONNECT: c_int = 7;
pub const SSL_ERROR_WANT_READ: c_int = 2;
pub const SSL_ERROR_WANT_WRITE: c_int = 3;
pub const SSL_ERROR_WANT_X509_LOOKUP: c_int = 4;
pub const SSL_ERROR_ZERO_RETURN: c_int = 6;
pub const SSL_VERIFY_NONE: c_int = 0;
pub const SSL_VERIFY_PEER: c_int = 1;
pub const TLSEXT_NAMETYPE_host_name: c_long = 0;
pub const V_ASN1_GENERALIZEDTIME: c_int = 24;
pub const V_ASN1_UTCTIME: c_int = 23;
pub const X509_FILETYPE_ASN1: c_int = 2;
pub const X509_FILETYPE_DEFAULT: c_int = 3;
pub const X509_FILETYPE_PEM: c_int = 1;
pub const X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: c_int = 31;
pub const X509_V_ERR_AKID_SKID_MISMATCH: c_int = 30;
pub const X509_V_ERR_APPLICATION_VERIFICATION: c_int = 50;
pub const X509_V_ERR_CERT_CHAIN_TOO_LONG: c_int = 22;
pub const X509_V_ERR_CERT_HAS_EXPIRED: c_int = 10;
pub const X509_V_ERR_CERT_NOT_YET_VALID: c_int = 9;
pub const X509_V_ERR_CERT_REJECTED: c_int = 28;
pub const X509_V_ERR_CERT_REVOKED: c_int = 23;
pub const X509_V_ERR_CERT_SIGNATURE_FAILURE: c_int = 7;
pub const X509_V_ERR_CERT_UNTRUSTED: c_int = 27;
pub const X509_V_ERR_CRL_HAS_EXPIRED: c_int = 12;
pub const X509_V_ERR_CRL_NOT_YET_VALID: c_int = 11;
pub const X509_V_ERR_CRL_PATH_VALIDATION_ERROR: c_int = 54;
pub const X509_V_ERR_CRL_SIGNATURE_FAILURE: c_int = 8;
pub const X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: c_int = 18;
pub const X509_V_ERR_DIFFERENT_CRL_SCOPE: c_int = 44;
pub const X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: c_int = 14;
pub const X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: c_int = 13;
pub const X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: c_int = 15;
pub const X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: c_int = 16;
pub const X509_V_ERR_EXCLUDED_VIOLATION: c_int = 48;
pub const X509_V_ERR_INVALID_CA: c_int = 24;
pub const X509_V_ERR_INVALID_EXTENSION: c_int = 41;
pub const X509_V_ERR_INVALID_NON_CA: c_int = 37;
pub const X509_V_ERR_INVALID_POLICY_EXTENSION: c_int = 42;
pub const X509_V_ERR_INVALID_PURPOSE: c_int = 26;
pub const X509_V_ERR_KEYUSAGE_NO_CERTSIGN: c_int = 32;
pub const X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: c_int = 35;
pub const X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: c_int = 39;
pub const X509_V_ERR_NO_EXPLICIT_POLICY: c_int = 43;
pub const X509_V_ERR_OUT_OF_MEM: c_int = 17;
pub const X509_V_ERR_PATH_LENGTH_EXCEEDED: c_int = 25;
pub const X509_V_ERR_PERMITTED_VIOLATION: c_int = 47;
pub const X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: c_int = 40;
pub const X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: c_int = 38;
pub const X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: c_int = 19;
pub const X509_V_ERR_SUBJECT_ISSUER_MISMATCH: c_int = 29;
pub const X509_V_ERR_SUBTREE_MINMAX: c_int = 49;
pub const X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: c_int = 6;
pub const X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: c_int = 4;
pub const X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: c_int = 5;
pub const X509_V_ERR_UNABLE_TO_GET_CRL: c_int = 3;
pub const X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: c_int = 33;
pub const X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: c_int = 2;
pub const X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: c_int = 20;
pub const X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: c_int = 21;
pub const X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: c_int = 36;
pub const X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: c_int = 34;
pub const X509_V_ERR_UNNESTED_RESOURCE: c_int = 46;
pub const X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: c_int = 52;
pub const X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: c_int = 51;
pub const X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: c_int = 45;
pub const X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: c_int = 53;
pub const X509_V_OK: c_int = 0;
static mut MUTEXES: *mut Vec<NativeMutex> = 0 as *mut Vec<NativeMutex>;
extern fn locking_function(mode: c_int, n: c_int, _file: *const c_char,
_line: c_int) {
unsafe {
let mutex = &(*MUTEXES)[n as uint];
if mode & CRYPTO_LOCK != 0 {
mutex.lock_noguard();
} else {
mutex.unlock_noguard();
}
}
}
pub fn init() {
static mut INIT: Once = ONCE_INIT;
unsafe {
INIT.doit(|| {
SSL_library_init();
SSL_load_error_strings();
let num_locks = CRYPTO_num_locks();
let mutexes = box Vec::from_fn(num_locks as uint, |_| NativeMutex::new());
MUTEXES = mem::transmute(mutexes);
CRYPTO_set_locking_callback(locking_function);
})
}
}
// Functions converted from macros
pub unsafe fn BIO_eof(b: *mut BIO) -> bool {
BIO_ctrl(b, BIO_CTRL_EOF, 0, ptr::null_mut()) == 1
}
// True functions
extern "C" {
pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int;
pub fn ASN1_STRING_type_new(ty: c_int) -> *mut ASN1_STRING;
pub fn ASN1_TIME_free(tm: *mut ASN1_TIME);
pub fn BIO_ctrl(b: *mut BIO, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long;
pub fn BIO_free_all(b: *mut BIO);
pub fn BIO_new(type_: *const BIO_METHOD) -> *mut BIO;
pub fn BIO_read(b: *mut BIO, buf: *mut c_void, len: c_int) -> c_int;
pub fn BIO_write(b: *mut BIO, buf: *const c_void, len: c_int) -> c_int;
pub fn BIO_s_mem() -> *const BIO_METHOD;
pub fn BN_new() -> *mut BIGNUM;
pub fn BN_dup(n: *mut BIGNUM) -> *mut BIGNUM;
pub fn BN_clear_free(bn: *mut BIGNUM);
pub fn BN_CTX_new() -> *mut BN_CTX;
pub fn BN_CTX_free(ctx: *mut BN_CTX);
pub fn BN_num_bits(bn: *mut BIGNUM) -> c_int;
pub fn BN_set_negative(bn: *mut BIGNUM, n: c_int);
pub fn BN_set_word(bn: *mut BIGNUM, n: c_ulong) -> c_int;
/* Arithmetic operations on BIGNUMs */
pub fn BN_add(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM) -> c_int;
pub fn BN_div(dv: *mut BIGNUM, rem: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_exp(r: *mut BIGNUM, a: *mut BIGNUM, p: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_gcd(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_mod_add(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_mod_exp(r: *mut BIGNUM, a: *mut BIGNUM, p: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_mod_inverse(r: *mut BIGNUM, a: *mut BIGNUM, n: *mut BIGNUM, ctx: *mut BN_CTX) -> *const BIGNUM;
pub fn BN_mod_mul(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_mod_sqr(r: *mut BIGNUM, a: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_mod_sub(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_mul(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_nnmod(rem: *mut BIGNUM, a: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_sqr(r: *mut BIGNUM, a: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
pub fn BN_sub(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM) -> c_int;
/* Bit operations on BIGNUMs */
pub fn BN_clear_bit(a: *mut BIGNUM, n: c_int) -> c_int;
pub fn BN_is_bit_set(a: *mut BIGNUM, n: c_int) -> c_int;
pub fn BN_lshift(r: *mut BIGNUM, a: *mut BIGNUM, n: c_int) -> c_int;
pub fn BN_lshift1(r: *mut BIGNUM, a: *mut BIGNUM) -> c_int;
pub fn BN_mask_bits(a: *mut BIGNUM, n: c_int) -> c_int;
pub fn BN_rshift(r: *mut BIGNUM, a: *mut BIGNUM, n: c_int) -> c_int;
pub fn BN_set_bit(a: *mut BIGNUM, n: c_int) -> c_int;
pub fn BN_rshift1(r: *mut BIGNUM, a: *mut BIGNUM) -> c_int;
/* Comparisons on BIGNUMs */
pub fn BN_cmp(a: *mut BIGNUM, b: *mut BIGNUM) -> c_int;
pub fn BN_ucmp(a: *mut BIGNUM, b: *mut BIGNUM) -> c_int;
/* Prime handling */
pub fn BN_generate_prime_ex(r: *mut BIGNUM, bits: c_int, safe: c_int, add: *mut BIGNUM, rem: *mut BIGNUM, cb: *const c_void) -> c_int;
pub fn BN_is_prime_ex(p: *mut BIGNUM, checks: c_int, ctx: *mut BN_CTX, cb: *const c_void) -> c_int;
pub fn BN_is_prime_fasttest_ex(p: *mut BIGNUM, checks: c_int, ctx: *mut BN_CTX, do_trial_division: c_int, cb: *const c_void) -> c_int;
/* Random number handling */
pub fn BN_rand(r: *mut BIGNUM, bits: c_int, top: c_int, bottom: c_int) -> c_int;
pub fn BN_pseudo_rand(r: *mut BIGNUM, bits: c_int, top: c_int, bottom: c_int) -> c_int;
pub fn BN_rand_range(r: *mut BIGNUM, range: *mut BIGNUM) -> c_int;
pub fn BN_pseudo_rand_range(r: *mut BIGNUM, range: *mut BIGNUM) -> c_int;
/* Conversion from/to binary representation */
pub fn BN_bin2bn(s: *const u8, size: c_int, ret: *mut BIGNUM) -> *mut BIGNUM;
pub fn BN_bn2bin(a: *mut BIGNUM, to: *mut u8) -> c_int;
/* Conversion from/to string representation */
pub fn BN_bn2dec(a: *mut BIGNUM) -> *const c_char;
pub fn CRYPTO_num_locks() -> c_int;
pub fn CRYPTO_set_locking_callback(func: extern "C" fn(mode: c_int,
n: c_int,
file: *const c_char,
line: c_int));
pub fn CRYPTO_free(buf: *mut c_void);
pub fn CRYPTO_memcmp(a: *const c_void, b: *const c_void,
len: size_t) -> c_int;
pub fn ERR_get_error() -> c_ulong;
pub fn ERR_lib_error_string(err: c_ulong) -> *const c_char;
pub fn ERR_func_error_string(err: c_ulong) -> *const c_char;
pub fn ERR_reason_error_string(err: c_ulong) -> *const c_char;
pub fn ERR_load_crypto_strings();
pub fn EVP_md5() -> *const EVP_MD;
pub fn EVP_ripemd160() -> *const EVP_MD;
pub fn EVP_sha1() -> *const EVP_MD;
pub fn EVP_sha224() -> *const EVP_MD;
pub fn EVP_sha256() -> *const EVP_MD;
pub fn EVP_sha384() -> *const EVP_MD;
pub fn EVP_sha512() -> *const EVP_MD;
pub fn EVP_aes_128_cbc() -> *const EVP_CIPHER;
pub fn EVP_aes_128_ecb() -> *const EVP_CIPHER;
#[cfg(feature = "aes_xts")]
pub fn EVP_aes_128_xts() -> *const EVP_CIPHER;
// fn EVP_aes_128_ctr() -> EVP_CIPHER;
// fn EVP_aes_128_gcm() -> EVP_CIPHER;
pub fn EVP_aes_256_cbc() -> *const EVP_CIPHER;
pub fn EVP_aes_256_ecb() -> *const EVP_CIPHER;
#[cfg(feature = "aes_xts")]
pub fn EVP_aes_256_xts() -> *const EVP_CIPHER;
// fn EVP_aes_256_ctr() -> EVP_CIPHER;
// fn EVP_aes_256_gcm() -> EVP_CIPHER;
pub fn EVP_rc4() -> *const EVP_CIPHER;
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);
pub fn EVP_CIPHER_CTX_free(ctx: *mut EVP_CIPHER_CTX);
pub fn EVP_CipherInit(ctx: *mut EVP_CIPHER_CTX, evp: *const EVP_CIPHER,
key: *const u8, iv: *const u8, mode: c_int);
pub fn EVP_CipherUpdate(ctx: *mut EVP_CIPHER_CTX, outbuf: *mut u8,
outlen: &mut c_uint, inbuf: *const u8, inlen: c_int);
pub fn EVP_CipherFinal(ctx: *mut EVP_CIPHER_CTX, res: *mut u8, len: &mut c_int);
pub fn EVP_DigestInit(ctx: *mut EVP_MD_CTX, typ: *const EVP_MD);
pub fn EVP_DigestInit_ex(ctx: *mut EVP_MD_CTX, typ: *const EVP_MD, imple: *const ENGINE);
pub fn EVP_DigestUpdate(ctx: *mut EVP_MD_CTX, data: *const u8, n: c_uint);
pub fn EVP_DigestFinal(ctx: *mut EVP_MD_CTX, res: *mut u8, n: *mut u32);
pub fn EVP_DigestFinal_ex(ctx: *mut EVP_MD_CTX, res: *mut u8, n: *mut u32);
pub fn EVP_MD_CTX_create() -> *mut EVP_MD_CTX;
pub fn EVP_MD_CTX_destroy(ctx: *mut EVP_MD_CTX);
pub fn EVP_PKEY_new() -> *mut EVP_PKEY;
pub fn EVP_PKEY_free(k: *mut EVP_PKEY);
pub fn EVP_PKEY_assign(pkey: *mut EVP_PKEY, typ: c_int, key: *const c_char) -> c_int;
pub fn EVP_PKEY_get1_RSA(k: *mut EVP_PKEY) -> *mut RSA;
pub fn EVP_PKEY_set1_RSA(k: *mut EVP_PKEY, r: *mut RSA) -> c_int;
pub fn HMAC_CTX_init(ctx: *mut HMAC_CTX);
pub fn HMAC_Init_ex(ctx: *mut HMAC_CTX, key: *const u8, keylen: c_int, md: *const EVP_MD, imple: *const ENGINE);
pub fn HMAC_Final(ctx: *mut HMAC_CTX, output: *mut u8, len: *mut c_uint);
pub fn HMAC_Update(ctx: *mut HMAC_CTX, input: *const u8, len: c_uint);
pub fn PEM_read_bio_X509(bio: *mut BIO, out: *mut *mut X509, callback: Option<PasswordCallback>,
user_data: *mut c_void) -> *mut X509;
pub fn PEM_write_bio_PrivateKey(bio: *mut BIO, pkey: *mut EVP_PKEY, cipher: *const EVP_CIPHER,
kstr: *mut c_char, klen: c_int,
callback: Option<PasswordCallback>,
user_data: *mut c_void) -> c_int;
pub fn PEM_write_bio_X509(bio: *mut BIO, x509: *mut X509) -> c_int;
pub fn PKCS5_PBKDF2_HMAC_SHA1(pass: *const u8, passlen: c_int,
salt: *const u8, saltlen: c_int,
iter: c_int, keylen: c_int,
out: *mut u8) -> c_int;
pub fn RAND_bytes(buf: *mut u8, num: c_int) -> c_int;
pub fn RSA_generate_key(modsz: c_uint, e: c_uint, cb: *const u8, cbarg: *const u8) -> *mut RSA;
pub fn RSA_private_decrypt(flen: c_uint, from: *const u8, to: *mut u8, k: *mut RSA,
pad: c_int) -> c_int;
pub fn RSA_public_encrypt(flen: c_uint, from: *const u8, to: *mut u8, k: *mut RSA,
pad: c_int) -> c_int;
pub fn RSA_sign(t: c_int, m: *const u8, mlen: c_uint, sig: *mut u8, siglen: *mut c_uint,
k: *mut RSA) -> c_int;
pub fn RSA_size(k: *mut RSA) -> c_uint;
pub fn RSA_verify(t: c_int, m: *const u8, mlen: c_uint, sig: *const u8, siglen: c_uint,
k: *mut RSA) -> c_int;
pub fn SSL_library_init() -> c_int;
pub fn SSL_load_error_strings();
#[cfg(feature = "sslv2")]
pub fn SSLv2_method() -> *const SSL_METHOD;
pub fn SSLv3_method() -> *const SSL_METHOD;
pub fn TLSv1_method() -> *const SSL_METHOD;
#[cfg(feature = "tlsv1_1")]
pub fn TLSv1_1_method() -> *const SSL_METHOD;
#[cfg(feature = "tlsv1_2")]
pub fn TLSv1_2_method() -> *const SSL_METHOD;
pub fn SSLv23_method() -> *const SSL_METHOD;
pub fn SSL_new(ctx: *mut SSL_CTX) -> *mut SSL;
pub fn SSL_free(ssl: *mut SSL);
pub fn SSL_set_bio(ssl: *mut SSL, rbio: *mut BIO, wbio: *mut BIO);
pub fn SSL_get_rbio(ssl: *mut SSL) -> *mut BIO;
pub fn SSL_get_wbio(ssl: *mut SSL) -> *mut BIO;
pub fn SSL_accept(ssl: *mut SSL) -> c_int;
pub fn SSL_connect(ssl: *mut SSL) -> c_int;
pub fn SSL_ctrl(ssl: *mut SSL, cmd: c_int, larg: c_long,
parg: *mut c_void) -> c_long;
pub fn SSL_get_error(ssl: *mut SSL, ret: c_int) -> c_int;
pub fn SSL_read(ssl: *mut SSL, buf: *mut c_void, num: c_int) -> c_int;
pub fn SSL_write(ssl: *mut SSL, buf: *const c_void, num: c_int) -> c_int;
pub fn SSL_get_ex_data_X509_STORE_CTX_idx() -> c_int;
pub fn SSL_get_SSL_CTX(ssl: *mut SSL) -> *mut SSL_CTX;
pub fn SSL_get_current_compression(ssl: *mut SSL) -> *const COMP_METHOD;
pub fn SSL_get_peer_certificate(ssl: *mut SSL) -> *mut X509;
pub fn SSL_COMP_get_name(comp: *const COMP_METHOD) -> *const c_char;
pub fn SSL_CTX_new(method: *const SSL_METHOD) -> *mut SSL_CTX;
pub fn SSL_CTX_free(ctx: *mut SSL_CTX);
pub fn SSL_CTX_set_verify(ctx: *mut SSL_CTX, mode: c_int,
verify_callback: Option<extern fn(c_int, *mut X509_STORE_CTX) -> c_int>);
pub fn SSL_CTX_set_verify_depth(ctx: *mut SSL_CTX, depth: c_int);
pub fn SSL_CTX_load_verify_locations(ctx: *mut SSL_CTX, CAfile: *const c_char,
CApath: *const c_char) -> c_int;
pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *const c_void,
new_func: Option<CRYPTO_EX_new>,
dup_func: Option<CRYPTO_EX_dup>,
free_func: Option<CRYPTO_EX_free>)
-> c_int;
pub fn SSL_CTX_set_ex_data(ctx: *mut SSL_CTX, idx: c_int, data: *mut c_void)
-> c_int;
pub fn SSL_CTX_get_ex_data(ctx: *mut SSL_CTX, idx: c_int) -> *mut c_void;
pub fn SSL_CTX_use_certificate_file(ctx: *mut SSL_CTX, cert_file: *const c_char, file_type: c_int) -> c_int;
pub fn SSL_CTX_use_PrivateKey_file(ctx: *mut SSL_CTX, key_file: *const c_char, file_type: c_int) -> c_int;
pub fn SSL_CTX_set_cipher_list(ssl: *mut SSL_CTX, s: *const c_char) -> c_int;
pub fn X509_add_ext(x: *mut X509, ext: *mut X509_EXTENSION, loc: c_int) -> c_int;
pub fn X509_digest(x: *mut X509, digest: *const EVP_MD, buf: *mut c_char, len: *mut c_uint) -> c_int;
pub fn X509_free(x: *mut X509);
pub fn X509_get_serialNumber(x: *mut X509) -> *mut ASN1_INTEGER;
pub fn X509_get_subject_name(x: *mut X509) -> *mut X509_NAME;
pub fn X509_gmtime_adj(time: *mut ASN1_TIME, adj: c_long) -> *mut ASN1_TIME;
pub fn X509_new() -> *mut X509;
pub fn X509_set_issuer_name(x: *mut X509, name: *mut X509_NAME) -> c_int;
pub fn X509_set_notAfter(x: *mut X509, tm: *const ASN1_TIME) -> c_int;
pub fn X509_set_notBefore(x: *mut X509, tm: *const ASN1_TIME) -> c_int;
pub fn X509_set_version(x: *mut X509, version: c_ulong) -> c_int;
pub fn X509_set_pubkey(x: *mut X509, pkey: *mut EVP_PKEY) -> c_int;
pub fn X509_sign(x: *mut X509, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> c_int;
pub fn X509_EXTENSION_free(ext: *mut X509_EXTENSION);
pub fn X509_NAME_add_entry_by_txt(x: *mut X509, field: *const c_char, ty: c_int, bytes: *const c_char, len: c_int, loc: c_int, set: c_int) -> c_int;
pub fn X509_NAME_get_index_by_NID(n: *mut X509_NAME, nid: c_int, last_pos: c_int) ->c_int;
pub fn X509_NAME_get_entry(n: *mut X509_NAME, loc: c_int) -> *mut X509_NAME_ENTRY;
pub fn X509_NAME_ENTRY_get_data(ne: *mut X509_NAME_ENTRY) -> *mut ASN1_STRING;
pub fn ASN1_STRING_to_UTF8(out: *mut *mut c_char, s: *mut ASN1_STRING) -> c_int;
pub fn X509_STORE_CTX_get_current_cert(ct: *mut X509_STORE_CTX) -> *mut X509;
pub fn X509_STORE_CTX_get_error(ctx: *mut X509_STORE_CTX) -> c_int;
pub fn X509_STORE_CTX_get_ex_data(ctx: *mut X509_STORE_CTX, idx: c_int) -> *mut c_void;
pub fn X509V3_EXT_conf_nid(conf: *mut c_void, ctx: *mut X509V3_CTX, ext_nid: c_int, value: *mut c_char) -> *mut X509_EXTENSION;
pub fn X509V3_set_ctx(ctx: *mut X509V3_CTX, issuer: *mut X509, subject: *mut X509, req: *mut X509_REQ, crl: *mut X509_CRL, flags: c_int);
pub fn i2d_RSA_PUBKEY(k: *mut RSA, buf: *const *mut u8) -> c_int;
pub fn d2i_RSA_PUBKEY(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA;
pub fn i2d_RSAPrivateKey(k: *mut RSA, buf: *const *mut u8) -> c_int;
pub fn d2i_RSAPrivateKey(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA;
}
pub mod probe;

72
openssl-sys/src/probe.rs Normal file
View File

@ -0,0 +1,72 @@
use std::os;
use std::io::fs::PathExtensions;
pub struct ProbeResult {
pub cert_file: Option<Path>,
pub cert_dir: Option<Path>,
}
/// Probe the system for the directory in which CA certificates should likely be
/// found.
///
/// This will only search known system locations.
pub fn find_certs_dirs() -> Vec<Path> {
// see http://gagravarr.org/writing/openssl-certs/others.shtml
[
"/var/ssl",
"/usr/share/ssl",
"/usr/local/ssl",
"/usr/local/openssl",
"/usr/local/share",
"/usr/lib/ssl",
"/usr/ssl",
"/etc/openssl",
"/etc/pki/tls",
"/etc/ssl",
].iter().map(|s| Path::new(*s)).filter(|p| {
p.exists()
}).collect()
}
pub fn init_ssl_cert_env_vars() {
let ProbeResult { cert_file, cert_dir } = probe();
match cert_file {
Some(path) => put("SSL_CERT_FILE", path),
None => {}
}
match cert_dir {
Some(path) => put("SSL_CERT_DIR", path),
None => {}
}
fn put(var: &str, path: Path) {
// Don't stomp over what anyone else has set
match os::getenv(var) {
Some(..) => {}
None => os::setenv(var, path),
}
}
}
pub fn probe() -> ProbeResult {
let mut result = ProbeResult {
cert_file: os::getenv("SSL_CERT_FILE").map(Path::new),
cert_dir: os::getenv("SSL_CERT_DIR").map(Path::new),
};
for certs_dir in find_certs_dirs().iter() {
// cert.pem looks to be an openssl 1.0.1 thing, while
// certs/ca-certificates.crt appears to be a 0.9.8 thing
try(&mut result.cert_file, certs_dir.join("cert.pem"));
try(&mut result.cert_file, certs_dir.join("certs/ca-certificates.crt"));
try(&mut result.cert_file, certs_dir.join("certs/ca-root-nss.crt"));
try(&mut result.cert_dir, certs_dir.join("certs"));
}
result
}
fn try(dst: &mut Option<Path>, val: Path) {
if dst.is_none() && val.exists() {
*dst = Some(val);
}
}

49
src/asn1/mod.rs Normal file
View File

@ -0,0 +1,49 @@
use libc::{c_long};
use std::ptr;
use ffi;
use ssl::error::{SslError};
pub struct Asn1Time {
handle: *mut ffi::ASN1_TIME,
owned: bool
}
impl Asn1Time {
/// Wraps existing ASN1_TIME and takes ownership
pub fn new(handle: *mut ffi::ASN1_TIME) -> Asn1Time {
Asn1Time {
handle: handle,
owned: true
}
}
fn new_with_period(period: u64) -> Result<Asn1Time, SslError> {
ffi::init();
let handle = unsafe {
try_ssl_null!(ffi::X509_gmtime_adj(ptr::null_mut(),
period as c_long))
};
Ok(Asn1Time::new(handle))
}
/// Creates a new time on specified interval in days from now
pub fn days_from_now(days: uint) -> Result<Asn1Time, SslError> {
Asn1Time::new_with_period(days as u64 * 60 * 60 * 24)
}
/// Returns raw handle
pub unsafe fn get_handle(&self) -> *mut ffi::ASN1_TIME {
return self.handle
}
}
impl Drop for Asn1Time {
fn drop(&mut self) {
if self.owned {
unsafe { ffi::ASN1_TIME_free(self.handle) };
}
}
}

105
src/bio/mod.rs Normal file
View File

@ -0,0 +1,105 @@
use libc::{c_void, c_int};
use std::io::{EndOfFile, IoResult, IoError, OtherIoError};
use std::io::{Reader, Writer};
use std::ptr;
use ffi;
use ssl::error::{SslError};
pub struct MemBio {
bio: *mut ffi::BIO,
owned: bool
}
impl Drop for MemBio {
fn drop(&mut self) {
if self.owned {
unsafe {
ffi::BIO_free_all(self.bio);
}
}
}
}
impl MemBio {
/// Creates a new owned memory based BIO
pub fn new() -> Result<MemBio, SslError> {
ffi::init();
let bio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) };
try_ssl_null!(bio);
Ok(MemBio {
bio: bio,
owned: true
})
}
/// Returns a "borrow", i.e. it has no ownership
pub fn borrowed(bio: *mut ffi::BIO) -> MemBio {
MemBio {
bio: bio,
owned: false
}
}
/// Consumes current bio and returns wrapped value
/// Note that data ownership is lost and
/// should be managed manually
pub unsafe fn unwrap(mut self) -> *mut ffi::BIO {
self.owned = false;
self.bio
}
/// Temporarily gets wrapped value
pub unsafe fn get_handle(&self) -> *mut ffi::BIO {
self.bio
}
}
impl Reader for MemBio {
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
let ret = unsafe {
ffi::BIO_read(self.bio, buf.as_ptr() as *mut c_void,
buf.len() as c_int)
};
if ret <= 0 {
let is_eof = unsafe { ffi::BIO_eof(self.bio) };
let err = if is_eof {
IoError {
kind: EndOfFile,
desc: "MemBio EOF",
detail: None
}
} else {
IoError {
kind: OtherIoError,
desc: "MemBio read error",
detail: Some(format!("{}", SslError::get()))
}
};
Err(err)
} else {
Ok(ret as uint)
}
}
}
impl Writer for MemBio {
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
let ret = unsafe {
ffi::BIO_write(self.bio, buf.as_ptr() as *const c_void,
buf.len() as c_int)
};
if buf.len() != ret as uint {
Err(IoError {
kind: OtherIoError,
desc: "MemBio write error",
detail: Some(format!("{}", SslError::get()))
})
} else {
Ok(())
}
}
}

View File

@ -1,89 +1,11 @@
use libc::{c_void, c_int, c_ulong, c_char};
use libc::{c_int, c_ulong, c_void};
use std::{fmt, ptr};
use std::c_str::CString;
use std::num::{One, Zero};
use ffi;
use ssl::error::SslError;
#[allow(dead_code)]
#[repr(C)]
struct BIGNUM {
d: *mut c_void,
top: c_int,
dmax: c_int,
neg: c_int,
flags: c_int,
}
#[allow(non_camel_case_types)]
type BN_CTX = *mut c_void;
#[link(name = "crypto")]
extern {
fn BN_new() -> *mut BIGNUM;
fn BN_clear_free(bn: *mut BIGNUM);
fn BN_CTX_new() -> *mut BN_CTX;
fn BN_CTX_free(ctx: *mut BN_CTX);
fn BN_set_word(bn: *mut BIGNUM, n: c_ulong) -> c_int;
fn BN_set_negative(bn: *mut BIGNUM, n: c_int);
fn BN_num_bits(bn: *mut BIGNUM) -> c_int;
/* Arithmetic operations on BIGNUMs */
fn BN_add(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM) -> c_int;
fn BN_sub(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM) -> c_int;
fn BN_mul(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_sqr(r: *mut BIGNUM, a: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_div(dv: *mut BIGNUM, rem: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_nnmod(rem: *mut BIGNUM, a: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_mod_add(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_mod_sub(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_mod_mul(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_mod_sqr(r: *mut BIGNUM, a: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_exp(r: *mut BIGNUM, a: *mut BIGNUM, p: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_mod_exp(r: *mut BIGNUM, a: *mut BIGNUM, p: *mut BIGNUM, m: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
fn BN_mod_inverse(r: *mut BIGNUM, a: *mut BIGNUM, n: *mut BIGNUM, ctx: *mut BN_CTX) -> *const BIGNUM;
fn BN_mod_word(r: *mut BIGNUM, w: c_ulong) -> c_ulong;
fn BN_gcd(r: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int;
/* Bit operations on BIGNUMs */
fn BN_set_bit(a: *mut BIGNUM, n: c_int) -> c_int;
fn BN_clear_bit(a: *mut BIGNUM, n: c_int) -> c_int;
fn BN_is_bit_set(a: *mut BIGNUM, n: c_int) -> c_int;
fn BN_mask_bits(a: *mut BIGNUM, n: c_int) -> c_int;
fn BN_lshift(r: *mut BIGNUM, a: *mut BIGNUM, n: c_int) -> c_int;
fn BN_lshift1(r: *mut BIGNUM, a: *mut BIGNUM) -> c_int;
fn BN_rshift(r: *mut BIGNUM, a: *mut BIGNUM, n: c_int) -> c_int;
fn BN_rshift1(r: *mut BIGNUM, a: *mut BIGNUM) -> c_int;
/* Comparisons on BIGNUMs */
fn BN_cmp(a: *mut BIGNUM, b: *mut BIGNUM) -> c_int;
fn BN_ucmp(a: *mut BIGNUM, b: *mut BIGNUM) -> c_int;
fn BN_is_zero(a: *mut BIGNUM) -> c_int;
/* Prime handling */
fn BN_generate_prime_ex(r: *mut BIGNUM, bits: c_int, safe: c_int, add: *mut BIGNUM, rem: *mut BIGNUM, cb: *const c_void) -> c_int;
fn BN_is_prime_ex(p: *mut BIGNUM, checks: c_int, ctx: *mut BN_CTX, cb: *const c_void) -> c_int;
fn BN_is_prime_fasttest_ex(p: *mut BIGNUM, checks: c_int, ctx: *mut BN_CTX, do_trial_division: c_int, cb: *const c_void) -> c_int;
/* Random number handling */
fn BN_rand(r: *mut BIGNUM, bits: c_int, top: c_int, bottom: c_int) -> c_int;
fn BN_pseudo_rand(r: *mut BIGNUM, bits: c_int, top: c_int, bottom: c_int) -> c_int;
fn BN_rand_range(r: *mut BIGNUM, range: *mut BIGNUM) -> c_int;
fn BN_pseudo_rand_range(r: *mut BIGNUM, range: *mut BIGNUM) -> c_int;
/* Conversion from/to binary representation */
fn BN_bn2bin(a: *mut BIGNUM, to: *mut u8) -> c_int;
fn BN_bin2bn(s: *const u8, size: c_int, ret: *mut BIGNUM) -> *mut BIGNUM;
/* Conversion from/to string representation */
fn BN_bn2dec(a: *mut BIGNUM) -> *const c_char;
fn CRYPTO_free(buf: *const c_char);
}
pub struct BigNum(*mut BIGNUM);
pub struct BigNum(*mut ffi::BIGNUM);
#[repr(C)]
pub enum RNGProperty {
@ -94,12 +16,12 @@ pub enum RNGProperty {
macro_rules! with_ctx(
($name:ident, $action:block) => ({
let $name = BN_CTX_new();
let $name = ffi::BN_CTX_new();
if ($name).is_null() {
Err(SslError::get())
} else {
let r = $action;
BN_CTX_free($name);
ffi::BN_CTX_free($name);
r
}
});
@ -126,7 +48,7 @@ macro_rules! with_bn_in_ctx(
let tmp = BigNum::new();
match tmp {
Ok($name) => {
let $ctx_name = BN_CTX_new();
let $ctx_name = ffi::BN_CTX_new();
if ($ctx_name).is_null() {
Err(SslError::get())
} else {
@ -136,7 +58,7 @@ macro_rules! with_bn_in_ctx(
} else {
Err(SslError::get())
};
BN_CTX_free($ctx_name);
ffi::BN_CTX_free($ctx_name);
r
}
},
@ -148,110 +70,102 @@ macro_rules! with_bn_in_ctx(
impl BigNum {
pub fn new() -> Result<BigNum, SslError> {
unsafe {
let v = BN_new();
if v.is_null() {
Err(SslError::get())
} else {
Ok(BigNum(v))
}
ffi::init();
let v = try_ssl_null!(ffi::BN_new());
Ok(BigNum(v))
}
}
pub fn new_from(n: u64) -> Result<BigNum, SslError> {
unsafe {
let bn = BN_new();
if bn.is_null() || BN_set_word(bn, n as c_ulong) == 0 {
Err(SslError::get())
} else {
Ok(BigNum(bn))
}
}
BigNum::new().and_then(|v| unsafe {
try_ssl!(ffi::BN_set_word(v.raw(), n as c_ulong));
Ok(v)
})
}
pub fn new_from_slice(n: &[u8]) -> Result<BigNum, SslError> {
unsafe {
let bn = BN_new();
if bn.is_null() || BN_bin2bn(n.as_ptr(), n.len() as c_int, bn).is_null() {
Err(SslError::get())
} else {
Ok(BigNum(bn))
}
}
BigNum::new().and_then(|v| unsafe {
try_ssl_null!(ffi::BN_bin2bn(n.as_ptr(), n.len() as c_int, v.raw()));
Ok(v)
})
}
pub fn checked_sqr(&self) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_sqr(r.raw(), self.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_sqr(r.raw(), self.raw(), ctx) == 1 })
}
}
pub fn checked_nnmod(&self, n: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_nnmod(r.raw(), self.raw(), n.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_nnmod(r.raw(), self.raw(), n.raw(), ctx) == 1 })
}
}
pub fn checked_mod_add(&self, a: &BigNum, n: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_mod_add(r.raw(), self.raw(), a.raw(), n.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_mod_add(r.raw(), self.raw(), a.raw(), n.raw(), ctx) == 1 })
}
}
pub fn checked_mod_sub(&self, a: &BigNum, n: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_mod_sub(r.raw(), self.raw(), a.raw(), n.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_mod_sub(r.raw(), self.raw(), a.raw(), n.raw(), ctx) == 1 })
}
}
pub fn checked_mod_mul(&self, a: &BigNum, n: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_mod_mul(r.raw(), self.raw(), a.raw(), n.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_mod_mul(r.raw(), self.raw(), a.raw(), n.raw(), ctx) == 1 })
}
}
pub fn checked_mod_sqr(&self, n: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_mod_sqr(r.raw(), self.raw(), n.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_mod_sqr(r.raw(), self.raw(), n.raw(), ctx) == 1 })
}
}
pub fn checked_exp(&self, p: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_exp(r.raw(), self.raw(), p.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_exp(r.raw(), self.raw(), p.raw(), ctx) == 1 })
}
}
pub fn checked_mod_exp(&self, p: &BigNum, n: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_mod_exp(r.raw(), self.raw(), p.raw(), n.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_mod_exp(r.raw(), self.raw(), p.raw(), n.raw(), ctx) == 1 })
}
}
pub fn checked_mod_inv(&self, n: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { !BN_mod_inverse(r.raw(), self.raw(), n.raw(), ctx).is_null() })
with_bn_in_ctx!(r, ctx, { !ffi::BN_mod_inverse(r.raw(), self.raw(), n.raw(), ctx).is_null() })
}
}
/*XXX
pub fn mod_word(&self, w: c_ulong) -> c_ulong {
unsafe {
return BN_mod_word(self.raw(), w);
BN_mod_word(self.raw(), w)
}
}
*/
pub fn checked_gcd(&self, a: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_gcd(r.raw(), self.raw(), a.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_gcd(r.raw(), self.raw(), a.raw(), ctx) == 1 })
}
}
pub fn checked_generate_prime(bits: i32, safe: bool, add: Option<&BigNum>, rem: Option<&BigNum>) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, {
let add_arg = add.map(|a| a.raw()).unwrap_or(ptr::mut_null());
let rem_arg = rem.map(|r| r.raw()).unwrap_or(ptr::mut_null());
let add_arg = add.map(|a| a.raw()).unwrap_or(ptr::null_mut());
let rem_arg = rem.map(|r| r.raw()).unwrap_or(ptr::null_mut());
BN_generate_prime_ex(r.raw(), bits as c_int, safe as c_int, add_arg, rem_arg, ptr::null()) == 1
ffi::BN_generate_prime_ex(r.raw(), bits as c_int, safe as c_int, add_arg, rem_arg, ptr::null()) == 1
})
}
}
@ -259,7 +173,7 @@ impl BigNum {
pub fn is_prime(&self, checks: i32) -> Result<bool, SslError> {
unsafe {
with_ctx!(ctx, {
Ok(BN_is_prime_ex(self.raw(), checks as c_int, ctx, ptr::null()) == 1)
Ok(ffi::BN_is_prime_ex(self.raw(), checks as c_int, ctx, ptr::null()) == 1)
})
}
}
@ -267,38 +181,38 @@ impl BigNum {
pub fn is_prime_fast(&self, checks: i32, do_trial_division: bool) -> Result<bool, SslError> {
unsafe {
with_ctx!(ctx, {
Ok(BN_is_prime_fasttest_ex(self.raw(), checks as c_int, ctx, do_trial_division as c_int, ptr::null()) == 1)
Ok(ffi::BN_is_prime_fasttest_ex(self.raw(), checks as c_int, ctx, do_trial_division as c_int, ptr::null()) == 1)
})
}
}
pub fn checked_new_random(bits: i32, prop: RNGProperty, odd: bool) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_rand(r.raw(), bits as c_int, prop as c_int, odd as c_int) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_rand(r.raw(), bits as c_int, prop as c_int, odd as c_int) == 1 })
}
}
pub fn checked_new_pseudo_random(bits: i32, prop: RNGProperty, odd: bool) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_pseudo_rand(r.raw(), bits as c_int, prop as c_int, odd as c_int) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_pseudo_rand(r.raw(), bits as c_int, prop as c_int, odd as c_int) == 1 })
}
}
pub fn checked_rand_in_range(&self) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_rand_range(r.raw(), self.raw()) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_rand_range(r.raw(), self.raw()) == 1 })
}
}
pub fn checked_pseudo_rand_in_range(&self) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_pseudo_rand_range(r.raw(), self.raw()) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_pseudo_rand_range(r.raw(), self.raw()) == 1 })
}
}
pub fn set_bit(&mut self, n: i32) -> Result<(), SslError> {
unsafe {
if BN_set_bit(self.raw(), n as c_int) == 1 {
if ffi::BN_set_bit(self.raw(), n as c_int) == 1 {
Ok(())
} else {
Err(SslError::get())
@ -308,7 +222,7 @@ impl BigNum {
pub fn clear_bit(&mut self, n: i32) -> Result<(), SslError> {
unsafe {
if BN_clear_bit(self.raw(), n as c_int) == 1 {
if ffi::BN_clear_bit(self.raw(), n as c_int) == 1 {
Ok(())
} else {
Err(SslError::get())
@ -318,13 +232,13 @@ impl BigNum {
pub fn is_bit_set(&self, n: i32) -> bool {
unsafe {
BN_is_bit_set(self.raw(), n as c_int) == 1
ffi::BN_is_bit_set(self.raw(), n as c_int) == 1
}
}
pub fn mask_bits(&mut self, n: i32) -> Result<(), SslError> {
unsafe {
if BN_mask_bits(self.raw(), n as c_int) == 1 {
if ffi::BN_mask_bits(self.raw(), n as c_int) == 1 {
Ok(())
} else {
Err(SslError::get())
@ -334,67 +248,67 @@ impl BigNum {
pub fn checked_shl1(&self) -> Result<BigNum, SslError> {
unsafe {
with_bn!(r, { BN_lshift1(r.raw(), self.raw()) == 1 })
with_bn!(r, { ffi::BN_lshift1(r.raw(), self.raw()) == 1 })
}
}
pub fn checked_shr1(&self) -> Result<BigNum, SslError> {
unsafe {
with_bn!(r, { BN_rshift1(r.raw(), self.raw()) == 1 })
with_bn!(r, { ffi::BN_rshift1(r.raw(), self.raw()) == 1 })
}
}
pub fn checked_add(&self, a: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn!(r, { BN_add(r.raw(), self.raw(), a.raw()) == 1 })
with_bn!(r, { ffi::BN_add(r.raw(), self.raw(), a.raw()) == 1 })
}
}
pub fn checked_sub(&self, a: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn!(r, { BN_sub(r.raw(), self.raw(), a.raw()) == 1 })
with_bn!(r, { ffi::BN_sub(r.raw(), self.raw(), a.raw()) == 1 })
}
}
pub fn checked_mul(&self, a: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_mul(r.raw(), self.raw(), a.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_mul(r.raw(), self.raw(), a.raw(), ctx) == 1 })
}
}
pub fn checked_div(&self, a: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_div(r.raw(), ptr::mut_null(), self.raw(), a.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_div(r.raw(), ptr::null_mut(), self.raw(), a.raw(), ctx) == 1 })
}
}
pub fn checked_mod(&self, a: &BigNum) -> Result<BigNum, SslError> {
unsafe {
with_bn_in_ctx!(r, ctx, { BN_div(ptr::mut_null(), r.raw(), self.raw(), a.raw(), ctx) == 1 })
with_bn_in_ctx!(r, ctx, { ffi::BN_div(ptr::null_mut(), r.raw(), self.raw(), a.raw(), ctx) == 1 })
}
}
pub fn checked_shl(&self, a: &i32) -> Result<BigNum, SslError> {
unsafe {
with_bn!(r, { BN_lshift(r.raw(), self.raw(), *a as c_int) == 1 })
with_bn!(r, { ffi::BN_lshift(r.raw(), self.raw(), *a as c_int) == 1 })
}
}
pub fn checked_shr(&self, a: &i32) -> Result<BigNum, SslError> {
unsafe {
with_bn!(r, { BN_rshift(r.raw(), self.raw(), *a as c_int) == 1 })
with_bn!(r, { ffi::BN_rshift(r.raw(), self.raw(), *a as c_int) == 1 })
}
}
pub fn negate(&mut self) {
unsafe {
BN_set_negative(self.raw(), !self.is_negative() as c_int)
ffi::BN_set_negative(self.raw(), !self.is_negative() as c_int)
}
}
pub fn abs_cmp(&self, oth: BigNum) -> Ordering {
unsafe {
let res = BN_ucmp(self.raw(), oth.raw()) as i32;
let res = ffi::BN_ucmp(self.raw(), oth.raw()) as i32;
if res < 0 {
Less
} else if res > 0 {
@ -413,7 +327,7 @@ impl BigNum {
pub fn num_bits(&self) -> i32 {
unsafe {
BN_num_bits(self.raw()) as i32
ffi::BN_num_bits(self.raw()) as i32
}
}
@ -421,7 +335,7 @@ impl BigNum {
(self.num_bits() + 7) / 8
}
unsafe fn raw(&self) -> *mut BIGNUM {
unsafe fn raw(&self) -> *mut ffi::BIGNUM {
let BigNum(n) = *self;
n
}
@ -430,7 +344,7 @@ impl BigNum {
let size = self.num_bytes() as uint;
let mut v = Vec::with_capacity(size);
unsafe {
BN_bn2bin(self.raw(), v.as_mut_ptr());
ffi::BN_bn2bin(self.raw(), v.as_mut_ptr());
v.set_len(size);
}
v
@ -438,11 +352,11 @@ impl BigNum {
pub fn to_dec_str(&self) -> String {
unsafe {
let buf = BN_bn2dec(self.raw());
let buf = ffi::BN_bn2dec(self.raw());
assert!(!buf.is_null());
let c_str = CString::new(buf, false);
let str = c_str.as_str().unwrap().to_string();
CRYPTO_free(buf);
ffi::CRYPTO_free(buf as *mut c_void);
str
}
}
@ -454,28 +368,11 @@ impl fmt::Show for BigNum {
}
}
impl One for BigNum {
fn one() -> BigNum {
BigNum::new_from(1).unwrap()
}
}
impl Zero for BigNum {
fn zero() -> BigNum {
BigNum::new_from(0).unwrap()
}
fn is_zero(&self) -> bool {
unsafe {
BN_is_zero(self.raw()) == 1
}
}
}
impl Eq for BigNum { }
impl PartialEq for BigNum {
fn eq(&self, oth: &BigNum) -> bool {
unsafe {
BN_cmp(self.raw(), oth.raw()) == 0
ffi::BN_cmp(self.raw(), oth.raw()) == 0
}
}
}
@ -489,7 +386,7 @@ impl Ord for BigNum {
impl PartialOrd for BigNum {
fn partial_cmp(&self, oth: &BigNum) -> Option<Ordering> {
unsafe {
let v = BN_cmp(self.raw(), oth.raw());
let v = ffi::BN_cmp(self.raw(), oth.raw());
let ret =
if v == 0 {
Equal
@ -507,18 +404,15 @@ impl Drop for BigNum {
fn drop(&mut self) {
unsafe {
if !self.raw().is_null() {
BN_clear_free(self.raw());
ffi::BN_clear_free(self.raw());
}
}
}
}
pub mod unchecked {
use super::{BIGNUM, BigNum};
extern {
fn BN_dup(n: *mut BIGNUM) -> *mut BIGNUM;
}
use ffi;
use super::{BigNum};
impl Add<BigNum, BigNum> for BigNum {
fn add(&self, oth: &BigNum) -> BigNum {
@ -565,9 +459,9 @@ pub mod unchecked {
impl Clone for BigNum {
fn clone(&self) -> BigNum {
unsafe {
let r = BN_dup(self.raw());
let r = ffi::BN_dup(self.raw());
if r.is_null() {
fail!("Unexpected null pointer from BN_dup(..)")
panic!("Unexpected null pointer from BN_dup(..)")
} else {
BigNum(r)
}

View File

@ -1,6 +1,8 @@
use libc;
use libc::c_uint;
use std::ptr;
use std::io;
use ffi;
pub enum HashType {
MD5,
@ -12,80 +14,75 @@ pub enum HashType {
RIPEMD160
}
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct EVP_MD_CTX {
digest: *mut EVP_MD,
engine: *mut libc::c_void,
flags: libc::c_ulong,
md_data: *mut libc::c_void,
pctx: *mut EVP_PKEY_CTX,
update: *mut libc::c_void
}
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct EVP_MD;
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct EVP_PKEY_CTX;
#[link(name = "crypto")]
extern {
fn EVP_MD_CTX_create() -> *mut EVP_MD_CTX;
fn EVP_MD_CTX_destroy(ctx: *mut EVP_MD_CTX);
fn EVP_md5() -> *const EVP_MD;
fn EVP_sha1() -> *const EVP_MD;
fn EVP_sha224() -> *const EVP_MD;
fn EVP_sha256() -> *const EVP_MD;
fn EVP_sha384() -> *const EVP_MD;
fn EVP_sha512() -> *const EVP_MD;
fn EVP_ripemd160() -> *const EVP_MD;
fn EVP_DigestInit(ctx: *mut EVP_MD_CTX, typ: *const EVP_MD);
fn EVP_DigestUpdate(ctx: *mut EVP_MD_CTX, data: *const u8, n: c_uint);
fn EVP_DigestFinal(ctx: *mut EVP_MD_CTX, res: *mut u8, n: *mut u32);
}
pub fn evpmd(t: HashType) -> (*const EVP_MD, uint) {
pub fn evpmd(t: HashType) -> (*const ffi::EVP_MD, uint) {
unsafe {
match t {
MD5 => (EVP_md5(), 16u),
SHA1 => (EVP_sha1(), 20u),
SHA224 => (EVP_sha224(), 28u),
SHA256 => (EVP_sha256(), 32u),
SHA384 => (EVP_sha384(), 48u),
SHA512 => (EVP_sha512(), 64u),
RIPEMD160 => (EVP_ripemd160(), 20u),
HashType::MD5 => (ffi::EVP_md5(), 16u),
HashType::SHA1 => (ffi::EVP_sha1(), 20u),
HashType::SHA224 => (ffi::EVP_sha224(), 28u),
HashType::SHA256 => (ffi::EVP_sha256(), 32u),
HashType::SHA384 => (ffi::EVP_sha384(), 48u),
HashType::SHA512 => (ffi::EVP_sha512(), 64u),
HashType::RIPEMD160 => (ffi::EVP_ripemd160(), 20u),
}
}
}
pub struct HasherContext {
ptr: *mut ffi::EVP_MD_CTX
}
impl HasherContext {
pub fn new() -> HasherContext {
ffi::init();
unsafe {
HasherContext { ptr: ffi::EVP_MD_CTX_create() }
}
}
}
impl Drop for HasherContext {
fn drop(&mut self) {
unsafe {
ffi::EVP_MD_CTX_destroy(self.ptr);
}
}
}
#[allow(dead_code)]
pub struct Hasher {
evp: *const EVP_MD,
ctx: *mut EVP_MD_CTX,
evp: *const ffi::EVP_MD,
ctx: HasherContext,
len: uint,
}
impl io::Writer for Hasher {
fn write(&mut self, buf: &[u8]) -> io::IoResult<()> {
self.update(buf);
Ok(())
}
}
impl Hasher {
pub fn new(ht: HashType) -> Hasher {
let ctx = unsafe { EVP_MD_CTX_create() };
let ctx = HasherContext::new();
Hasher::with_context(ctx, ht)
}
pub fn with_context(ctx: HasherContext, ht: HashType) -> Hasher {
let (evp, mdlen) = evpmd(ht);
unsafe {
EVP_DigestInit(ctx, evp);
ffi::EVP_DigestInit_ex(ctx.ptr, evp, 0 as *const _);
}
Hasher { evp: evp, ctx: ctx, len: mdlen }
}
/// Update this hasher with more input bytes
pub fn update(&self, data: &[u8]) {
pub fn update(&mut self, data: &[u8]) {
unsafe {
EVP_DigestUpdate(self.ctx, data.as_ptr(), data.len() as c_uint)
ffi::EVP_DigestUpdate(self.ctx.ptr, data.as_ptr(), data.len() as c_uint)
}
}
@ -93,20 +90,21 @@ impl Hasher {
* Return the digest of all bytes added to this hasher since its last
* initialization
*/
pub fn final(&self) -> Vec<u8> {
unsafe {
let mut res = Vec::from_elem(self.len, 0u8);
EVP_DigestFinal(self.ctx, res.as_mut_ptr(), ptr::null_mut());
res
}
pub fn finalize(self) -> Vec<u8> {
let (res, _) = self.finalize_reuse();
res
}
}
impl Drop for Hasher {
fn drop(&mut self) {
/**
* Return the digest of all bytes added to this hasher since its last
* initialization and its context for reuse
*/
pub fn finalize_reuse(self) -> (Vec<u8>, HasherContext) {
let mut res = Vec::from_elem(self.len, 0u8);
unsafe {
EVP_MD_CTX_destroy(self.ctx);
}
ffi::EVP_DigestFinal_ex(self.ctx.ptr, res.as_mut_ptr(), ptr::null_mut())
};
(res, self.ctx)
}
}
@ -115,9 +113,9 @@ impl Drop for Hasher {
* value
*/
pub fn hash(t: HashType, data: &[u8]) -> Vec<u8> {
let h = Hasher::new(t);
let mut h = Hasher::new(t);
h.update(data);
h.final()
h.finalize()
}
#[cfg(test)]
@ -135,9 +133,7 @@ mod tests {
expected_output: output.to_string() }
}
fn hash_test(hashtype: super::HashType, hashtest: &HashTest) {
let calced_raw = super::hash(hashtype, hashtest.input.as_slice());
fn compare(calced_raw: Vec<u8>, hashtest: &HashTest) {
let calced = calced_raw.as_slice().to_hex().into_string();
if calced != hashtest.expected_output {
@ -147,6 +143,28 @@ mod tests {
assert!(calced == hashtest.expected_output);
}
fn hash_test(hashtype: super::HashType, hashtest: &HashTest) {
let calced_raw = super::hash(hashtype, hashtest.input.as_slice());
compare(calced_raw, hashtest);
}
fn hash_reuse_test(ctx: super::HasherContext, hashtype: super::HashType,
hashtest: &HashTest) -> super::HasherContext {
let mut h = super::Hasher::with_context(ctx, hashtype);
h.update(hashtest.input.as_slice());
let (calced_raw, ctx) = h.finalize_reuse();
compare(calced_raw, hashtest);
ctx
}
pub fn hash_writer(t: super::HashType, data: &[u8]) -> Vec<u8> {
let mut h = super::Hasher::new(t);
h.write(data).unwrap();
h.finalize()
}
// Test vectors from http://www.nsrl.nist.gov/testdata/
#[test]
fn test_md5() {
@ -165,8 +183,10 @@ mod tests {
HashTest("A510CD18F7A56852EB0319", "577e216843dd11573574d3fb209b97d8"),
HashTest("AAED18DBE8938C19ED734A8D", "6f80fb775f27e0a4ce5c2f42fc72c5f1")];
let mut ctx = super::HasherContext::new();
for test in tests.iter() {
hash_test(super::MD5, test);
ctx = hash_reuse_test(ctx, super::HashType::MD5, test);
}
}
@ -177,7 +197,7 @@ mod tests {
];
for test in tests.iter() {
hash_test(super::SHA1, test);
hash_test(super::HashType::SHA1, test);
}
}
@ -188,7 +208,7 @@ mod tests {
];
for test in tests.iter() {
hash_test(super::SHA256, test);
hash_test(super::HashType::SHA256, test);
}
}
@ -199,7 +219,14 @@ mod tests {
];
for test in tests.iter() {
hash_test(super::RIPEMD160, test);
hash_test(super::HashType::RIPEMD160, test);
}
}
#[test]
fn test_writer() {
let tv = "rust-openssl".as_bytes();
let ht = super::HashType::RIPEMD160;
assert!(hash_writer(ht, tv) == super::hash(ht, tv));
}
}

View File

@ -14,50 +14,30 @@
* limitations under the License.
*/
use libc::{c_uchar, c_int, c_uint};
use libc::{c_int, c_uint};
use crypto::hash;
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct HMAC_CTX {
md: *mut hash::EVP_MD,
md_ctx: hash::EVP_MD_CTX,
i_ctx: hash::EVP_MD_CTX,
o_ctx: hash::EVP_MD_CTX,
key_length: c_uint,
key: [c_uchar, ..128]
}
#[link(name = "crypto")]
extern {
fn HMAC_CTX_init(ctx: *mut HMAC_CTX);
fn HMAC_Init_ex(ctx: *mut HMAC_CTX, key: *const u8, keylen: c_int, md: *const hash::EVP_MD, imple: *const ENGINE);
fn HMAC_Update(ctx: *mut HMAC_CTX, input: *const u8, len: c_uint);
fn HMAC_Final(ctx: *mut HMAC_CTX, output: *mut u8, len: *mut c_uint);
}
#[allow(non_camel_case_types)]
#[repr(C)]
struct ENGINE;
use ffi;
pub struct HMAC {
ctx: HMAC_CTX,
ctx: ffi::HMAC_CTX,
len: uint,
}
#[allow(non_snake_case)]
pub fn HMAC(ht: hash::HashType, key: &[u8]) -> HMAC {
unsafe {
ffi::init();
let (evp, mdlen) = hash::evpmd(ht);
let mut ctx : HMAC_CTX = ::std::mem::uninitialized();
let mut ctx : ffi::HMAC_CTX = ::std::mem::uninitialized();
HMAC_CTX_init(&mut ctx);
HMAC_Init_ex(&mut ctx,
key.as_ptr(),
key.len() as c_int,
evp, 0 as *const _);
ffi::HMAC_CTX_init(&mut ctx);
ffi::HMAC_Init_ex(&mut ctx,
key.as_ptr(),
key.len() as c_int,
evp, 0 as *const _);
HMAC { ctx: ctx, len: mdlen }
}
@ -66,15 +46,15 @@ pub fn HMAC(ht: hash::HashType, key: &[u8]) -> HMAC {
impl HMAC {
pub fn update(&mut self, data: &[u8]) {
unsafe {
HMAC_Update(&mut self.ctx, data.as_ptr(), data.len() as c_uint)
ffi::HMAC_Update(&mut self.ctx, data.as_ptr(), data.len() as c_uint)
}
}
pub fn final(&mut self) -> Vec<u8> {
pub fn finalize(&mut self) -> Vec<u8> {
unsafe {
let mut res = Vec::from_elem(self.len, 0u8);
let mut outlen = 0;
HMAC_Final(&mut self.ctx, res.as_mut_ptr(), &mut outlen);
ffi::HMAC_Final(&mut self.ctx, res.as_mut_ptr(), &mut outlen);
assert!(self.len == outlen as uint)
res
}
@ -84,7 +64,7 @@ impl HMAC {
#[cfg(test)]
mod tests {
use serialize::hex::FromHex;
use crypto::hash::{HashType, MD5, SHA1, SHA224, SHA256, SHA384, SHA512};
use crypto::hash::HashType::{mod, MD5, SHA1, SHA224, SHA256, SHA384, SHA512};
use super::HMAC;
#[test]
@ -116,7 +96,7 @@ mod tests {
for &(ref key, ref data, ref res) in tests.iter() {
let mut hmac = HMAC(MD5, key.as_slice());
hmac.update(data.as_slice());
assert_eq!(hmac.final(), *res);
assert_eq!(hmac.finalize(), *res);
}
}
@ -149,7 +129,7 @@ mod tests {
for &(ref key, ref data, ref res) in tests.iter() {
let mut hmac = HMAC(SHA1, key.as_slice());
hmac.update(data.as_slice());
assert_eq!(hmac.final(), *res);
assert_eq!(hmac.finalize(), *res);
}
}
@ -173,7 +153,7 @@ mod tests {
for (&(ref key, ref data), res) in tests.iter().zip(results.iter()) {
let mut hmac = HMAC(ty, key.as_slice());
hmac.update(data.as_slice());
assert_eq!(hmac.final(), *res);
assert_eq!(hmac.finalize(), *res);
}
}
@ -187,7 +167,7 @@ mod tests {
"95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e".from_hex().unwrap(),
"3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1".from_hex().unwrap()
];
test_sha2(SHA224, results);
test_sha2(SHA224, &results);
}
#[test]
@ -200,7 +180,7 @@ mod tests {
"60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54".from_hex().unwrap(),
"9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2".from_hex().unwrap()
];
test_sha2(SHA256, results);
test_sha2(SHA256, &results);
}
#[test]
@ -225,7 +205,7 @@ mod tests {
602420feb0b8fb9adccebb82461e99c5\
a678cc31e799176d3860e6110c46523e".from_hex().unwrap()
];
test_sha2(SHA384, results);
test_sha2(SHA384, &results);
}
#[test]
@ -256,6 +236,6 @@ mod tests {
b6022cac3c4982b10d5eeb55c3e4de15\
134676fb6de0446065c97440fa8c6a58".from_hex().unwrap()
];
test_sha2(SHA512, results);
test_sha2(SHA512, &results);
}
}

39
src/crypto/memcmp.rs Normal file
View File

@ -0,0 +1,39 @@
use libc::size_t;
use ffi;
/// Returns `true` iff `a` and `b` contain the same bytes.
///
/// This operation takes an amount of time dependent on the length of the two
/// arrays given, but is independent of the contents of a and b.
///
/// # Failure
///
/// This function will panic the current task if `a` and `b` do not have the same
/// length.
pub fn eq(a: &[u8], b: &[u8]) -> bool {
assert!(a.len() == b.len());
let ret = unsafe {
ffi::CRYPTO_memcmp(a.as_ptr() as *const _,
b.as_ptr() as *const _,
a.len() as size_t)
};
ret == 0
}
#[cfg(test)]
mod tests {
use super::eq;
#[test]
fn test_eq() {
assert!(eq(&[], &[]));
assert!(eq(&[1], &[1]));
assert!(!eq(&[1, 2, 3], &[1, 2, 4]));
}
#[test]
#[should_fail]
fn test_diff_lens() {
eq(&[], &[1]);
}
}

View File

@ -21,3 +21,4 @@ pub mod pkcs5;
pub mod pkey;
pub mod rand;
pub mod symm;
pub mod memcmp;

View File

@ -1,12 +1,5 @@
use libc::c_int;
#[link(name = "crypto")]
extern {
fn PKCS5_PBKDF2_HMAC_SHA1(pass: *const u8, passlen: c_int,
salt: *const u8, saltlen: c_int,
iter: c_int, keylen: c_int,
out: *mut u8) -> c_int;
}
use ffi;
/// Derives a key from a password and salt using the PBKDF2-HMAC-SHA1 algorithm.
pub fn pbkdf2_hmac_sha1(pass: &str, salt: &[u8], iter: uint, keylen: uint) -> Vec<u8> {
@ -16,13 +9,15 @@ pub fn pbkdf2_hmac_sha1(pass: &str, salt: &[u8], iter: uint, keylen: uint) -> Ve
let mut out = Vec::with_capacity(keylen);
let r = PKCS5_PBKDF2_HMAC_SHA1(
ffi::init();
let r = ffi::PKCS5_PBKDF2_HMAC_SHA1(
pass.as_ptr(), pass.len() as c_int,
salt.as_ptr(), salt.len() as c_int,
iter as c_int, keylen as c_int,
out.as_mut_ptr());
if r != 1 { fail!(); }
if r != 1 { panic!(); }
out.set_len(keylen);

View File

@ -1,40 +1,11 @@
use libc::{c_char, c_int, c_uint};
use libc;
use libc::{c_int, c_uint};
use std::mem;
use std::ptr;
use crypto::hash::{HashType, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, RIPEMD160};
use bio::{MemBio};
use crypto::hash::HashType;
use ffi;
use ssl::error::{SslError, StreamError};
#[allow(non_camel_case_types)]
pub type EVP_PKEY = *mut libc::c_void;
#[allow(non_camel_case_types)]
pub type RSA = *mut libc::c_void;
#[link(name = "crypto")]
extern {
fn EVP_PKEY_new() -> *mut EVP_PKEY;
fn EVP_PKEY_free(k: *mut EVP_PKEY);
fn EVP_PKEY_assign(pkey: *mut EVP_PKEY, typ: c_int, key: *const c_char) -> c_int;
fn EVP_PKEY_get1_RSA(k: *mut EVP_PKEY) -> *mut RSA;
fn EVP_PKEY_set1_RSA(k: *mut EVP_PKEY, r: *mut RSA) -> c_int;
fn i2d_RSA_PUBKEY(k: *mut RSA, buf: *const *mut u8) -> c_int;
fn d2i_RSA_PUBKEY(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA;
fn i2d_RSAPrivateKey(k: *mut RSA, buf: *const *mut u8) -> c_int;
fn d2i_RSAPrivateKey(k: *const *mut RSA, buf: *const *const u8, len: c_uint) -> *mut RSA;
fn RSA_generate_key(modsz: c_uint, e: c_uint, cb: *const u8, cbarg: *const u8) -> *mut RSA;
fn RSA_size(k: *mut RSA) -> c_uint;
fn RSA_public_encrypt(flen: c_uint, from: *const u8, to: *mut u8, k: *mut RSA,
pad: c_int) -> c_int;
fn RSA_private_decrypt(flen: c_uint, from: *const u8, to: *mut u8, k: *mut RSA,
pad: c_int) -> c_int;
fn RSA_sign(t: c_int, m: *const u8, mlen: c_uint, sig: *mut u8, siglen: *mut c_uint,
k: *mut RSA) -> c_int;
fn RSA_verify(t: c_int, m: *const u8, mlen: c_uint, sig: *const u8, siglen: c_uint,
k: *mut RSA) -> c_int;
}
enum Parts {
Neither,
@ -58,25 +29,25 @@ pub enum EncryptionPadding {
fn openssl_padding_code(padding: EncryptionPadding) -> c_int {
match padding {
OAEP => 4,
PKCS1v15 => 1
EncryptionPadding::OAEP => 4,
EncryptionPadding::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
RIPEMD160 => 117, // NID_ripemd160
HashType::MD5 => 4, // NID_md5,
HashType::SHA1 => 64, // NID_sha1
HashType::SHA224 => 675, // NID_sha224
HashType::SHA256 => 672, // NID_sha256
HashType::SHA384 => 673, // NID_sha384
HashType::SHA512 => 674, // NID_sha512
HashType::RIPEMD160 => 117, // NID_ripemd160
}
}
pub struct PKey {
evp: *mut EVP_PKEY,
evp: *mut ffi::EVP_PKEY,
parts: Parts,
}
@ -84,16 +55,18 @@ pub struct PKey {
impl PKey {
pub fn new() -> PKey {
unsafe {
ffi::init();
PKey {
evp: EVP_PKEY_new(),
parts: Neither,
evp: ffi::EVP_PKEY_new(),
parts: Parts::Neither,
}
}
}
fn _tostr(&self, f: unsafe extern "C" fn(*mut RSA, *const *mut u8) -> c_int) -> Vec<u8> {
fn _tostr(&self, f: unsafe extern "C" fn(*mut ffi::RSA, *const *mut u8) -> c_int) -> Vec<u8> {
unsafe {
let rsa = EVP_PKEY_get1_RSA(self.evp);
let rsa = ffi::EVP_PKEY_get1_RSA(self.evp);
let len = f(rsa, ptr::null());
if len < 0 as c_int { return vec!(); }
let mut s = Vec::from_elem(len as uint, 0u8);
@ -105,17 +78,17 @@ impl PKey {
}
}
fn _fromstr(&mut self, s: &[u8], f: unsafe extern "C" fn(*const *mut RSA, *const *const u8, c_uint) -> *mut RSA) {
fn _fromstr(&mut self, s: &[u8], f: unsafe extern "C" fn(*const *mut ffi::RSA, *const *const u8, c_uint) -> *mut ffi::RSA) {
unsafe {
let rsa = ptr::null_mut();
f(&rsa, &s.as_ptr(), s.len() as c_uint);
EVP_PKEY_set1_RSA(self.evp, rsa);
ffi::EVP_PKEY_set1_RSA(self.evp, rsa);
}
}
pub fn gen(&mut self, keysz: uint) {
unsafe {
let rsa = RSA_generate_key(
let rsa = ffi::RSA_generate_key(
keysz as c_uint,
65537u as c_uint,
ptr::null(),
@ -123,12 +96,12 @@ impl PKey {
);
// XXX: 6 == NID_rsaEncryption
EVP_PKEY_assign(
ffi::EVP_PKEY_assign(
self.evp,
6 as c_int,
mem::transmute(rsa));
self.parts = Both;
self.parts = Parts::Both;
}
}
@ -136,15 +109,15 @@ impl PKey {
* Returns a serialized form of the public key, suitable for load_pub().
*/
pub fn save_pub(&self) -> Vec<u8> {
self._tostr(i2d_RSA_PUBKEY)
self._tostr(ffi::i2d_RSA_PUBKEY)
}
/**
* Loads a serialized form of the public key, as produced by save_pub().
*/
pub fn load_pub(&mut self, s: &[u8]) {
self._fromstr(s, d2i_RSA_PUBKEY);
self.parts = Public;
self._fromstr(s, ffi::d2i_RSA_PUBKEY);
self.parts = Parts::Public;
}
/**
@ -152,15 +125,28 @@ impl PKey {
* load_priv().
*/
pub fn save_priv(&self) -> Vec<u8> {
self._tostr(i2d_RSAPrivateKey)
self._tostr(ffi::i2d_RSAPrivateKey)
}
/**
* Loads a serialized form of the public and private keys, as produced by
* save_priv().
*/
pub fn load_priv(&mut self, s: &[u8]) {
self._fromstr(s, d2i_RSAPrivateKey);
self.parts = Both;
self._fromstr(s, ffi::d2i_RSAPrivateKey);
self.parts = Parts::Both;
}
/// Stores private key as a PEM
// FIXME: also add password and encryption
pub fn write_pem(&self, writer: &mut Writer/*, password: Option<String>*/) -> Result<(), SslError> {
let mut mem_bio = try!(MemBio::new());
unsafe {
try_ssl!(ffi::PEM_write_bio_PrivateKey(mem_bio.get_handle(), self.evp, ptr::null(),
ptr::null_mut(), -1, None, ptr::null_mut()));
}
let buf = try!(mem_bio.read_to_end().map_err(StreamError));
writer.write(buf.as_slice()).map_err(StreamError)
}
/**
@ -168,7 +154,7 @@ impl PKey {
*/
pub fn size(&self) -> uint {
unsafe {
RSA_size(EVP_PKEY_get1_RSA(self.evp)) as uint
ffi::RSA_size(ffi::EVP_PKEY_get1_RSA(self.evp)) as uint
}
}
@ -177,24 +163,24 @@ impl PKey {
*/
pub fn can(&self, r: Role) -> bool {
match r {
Encrypt =>
Role::Encrypt =>
match self.parts {
Neither => false,
Parts::Neither => false,
_ => true,
},
Verify =>
Role::Verify =>
match self.parts {
Neither => false,
Parts::Neither => false,
_ => true,
},
Decrypt =>
Role::Decrypt =>
match self.parts {
Both => true,
Parts::Both => true,
_ => false,
},
Sign =>
Role::Sign =>
match self.parts {
Both => true,
Parts::Both => true,
_ => false,
},
}
@ -206,8 +192,8 @@ impl PKey {
*/
pub fn max_data(&self) -> uint {
unsafe {
let rsa = EVP_PKEY_get1_RSA(self.evp);
let len = RSA_size(rsa);
let rsa = ffi::EVP_PKEY_get1_RSA(self.evp);
let len = ffi::RSA_size(rsa);
// 41 comes from RSA_public_encrypt(3) for OAEP
len as uint - 41u
@ -216,14 +202,14 @@ impl PKey {
pub fn encrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> {
unsafe {
let rsa = EVP_PKEY_get1_RSA(self.evp);
let len = RSA_size(rsa);
let rsa = ffi::EVP_PKEY_get1_RSA(self.evp);
let len = ffi::RSA_size(rsa);
assert!(s.len() < self.max_data());
let mut r = Vec::from_elem(len as uint + 1u, 0u8);
let rv = RSA_public_encrypt(
let rv = ffi::RSA_public_encrypt(
s.len() as c_uint,
s.as_ptr(),
r.as_mut_ptr(),
@ -241,14 +227,14 @@ impl PKey {
pub fn decrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec<u8> {
unsafe {
let rsa = EVP_PKEY_get1_RSA(self.evp);
let len = RSA_size(rsa);
let rsa = ffi::EVP_PKEY_get1_RSA(self.evp);
let len = ffi::RSA_size(rsa);
assert_eq!(s.len() as c_uint, RSA_size(rsa));
assert_eq!(s.len() as c_uint, ffi::RSA_size(rsa));
let mut r = Vec::from_elem(len as uint + 1u, 0u8);
let rv = RSA_private_decrypt(
let rv = ffi::RSA_private_decrypt(
s.len() as c_uint,
s.as_ptr(),
r.as_mut_ptr(),
@ -268,32 +254,32 @@ 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]) -> Vec<u8> { self.encrypt_with_padding(s, OAEP) }
pub fn encrypt(&self, s: &[u8]) -> Vec<u8> { self.encrypt_with_padding(s, EncryptionPadding::OAEP) }
/**
* Decrypts data, expecting OAEP padding, returning the decrypted data.
*/
pub fn decrypt(&self, s: &[u8]) -> Vec<u8> { self.decrypt_with_padding(s, OAEP) }
pub fn decrypt(&self, s: &[u8]) -> Vec<u8> { self.decrypt_with_padding(s, EncryptionPadding::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]) -> Vec<u8> { self.sign_with_hash(s, SHA256) }
pub fn sign(&self, s: &[u8]) -> Vec<u8> { self.sign_with_hash(s, HashType::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 verify(&self, m: &[u8], s: &[u8]) -> bool { self.verify_with_hash(m, s, HashType::SHA256) }
pub fn sign_with_hash(&self, s: &[u8], hash: HashType) -> Vec<u8> {
unsafe {
let rsa = EVP_PKEY_get1_RSA(self.evp);
let mut len = RSA_size(rsa);
let rsa = ffi::EVP_PKEY_get1_RSA(self.evp);
let mut len = ffi::RSA_size(rsa);
let mut r = Vec::from_elem(len as uint + 1u, 0u8);
let rv = RSA_sign(
let rv = ffi::RSA_sign(
openssl_hash_nid(hash),
s.as_ptr(),
s.len() as c_uint,
@ -312,9 +298,9 @@ impl PKey {
pub fn verify_with_hash(&self, m: &[u8], s: &[u8], hash: HashType) -> bool {
unsafe {
let rsa = EVP_PKEY_get1_RSA(self.evp);
let rsa = ffi::EVP_PKEY_get1_RSA(self.evp);
let rv = RSA_verify(
let rv = ffi::RSA_verify(
openssl_hash_nid(hash),
m.as_ptr(),
m.len() as c_uint,
@ -326,19 +312,23 @@ impl PKey {
rv == 1 as c_int
}
}
pub unsafe fn get_handle(&self) -> *mut ffi::EVP_PKEY {
return self.evp
}
}
impl Drop for PKey {
fn drop(&mut self) {
unsafe {
EVP_PKEY_free(self.evp);
ffi::EVP_PKEY_free(self.evp);
}
}
}
#[cfg(test)]
mod tests {
use crypto::hash::{MD5, SHA1};
use crypto::hash::HashType::{MD5, SHA1};
#[test]
fn test_gen_pub() {
@ -348,14 +338,14 @@ mod tests {
k1.load_pub(k0.save_pub().as_slice());
assert_eq!(k0.save_pub(), k1.save_pub());
assert_eq!(k0.size(), k1.size());
assert!(k0.can(super::Encrypt));
assert!(k0.can(super::Decrypt));
assert!(k0.can(super::Verify));
assert!(k0.can(super::Sign));
assert!(k1.can(super::Encrypt));
assert!(!k1.can(super::Decrypt));
assert!(k1.can(super::Verify));
assert!(!k1.can(super::Sign));
assert!(k0.can(super::Role::Encrypt));
assert!(k0.can(super::Role::Decrypt));
assert!(k0.can(super::Role::Verify));
assert!(k0.can(super::Role::Sign));
assert!(k1.can(super::Role::Encrypt));
assert!(!k1.can(super::Role::Decrypt));
assert!(k1.can(super::Role::Verify));
assert!(!k1.can(super::Role::Sign));
}
#[test]
@ -366,14 +356,14 @@ mod tests {
k1.load_priv(k0.save_priv().as_slice());
assert_eq!(k0.save_priv(), k1.save_priv());
assert_eq!(k0.size(), k1.size());
assert!(k0.can(super::Encrypt));
assert!(k0.can(super::Decrypt));
assert!(k0.can(super::Verify));
assert!(k0.can(super::Sign));
assert!(k1.can(super::Encrypt));
assert!(k1.can(super::Decrypt));
assert!(k1.can(super::Verify));
assert!(k1.can(super::Sign));
assert!(k0.can(super::Role::Encrypt));
assert!(k0.can(super::Role::Decrypt));
assert!(k0.can(super::Role::Verify));
assert!(k0.can(super::Role::Sign));
assert!(k1.can(super::Role::Encrypt));
assert!(k1.can(super::Role::Decrypt));
assert!(k1.can(super::Role::Verify));
assert!(k1.can(super::Role::Sign));
}
#[test]
@ -395,8 +385,8 @@ mod tests {
let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8);
k0.gen(512u);
k1.load_pub(k0.save_pub().as_slice());
let emsg = k1.encrypt_with_padding(msg.as_slice(), super::PKCS1v15);
let dmsg = k0.decrypt_with_padding(emsg.as_slice(), super::PKCS1v15);
let emsg = k1.encrypt_with_padding(msg.as_slice(), super::EncryptionPadding::PKCS1v15);
let dmsg = k0.decrypt_with_padding(emsg.as_slice(), super::EncryptionPadding::PKCS1v15);
assert!(msg == dmsg);
}

View File

@ -1,16 +1,13 @@
use libc::c_int;
#[link(name = "crypto")]
extern {
fn RAND_bytes(buf: *mut u8, num: c_int) -> c_int;
}
use ffi;
pub fn rand_bytes(len: uint) -> Vec<u8> {
unsafe {
let mut out = Vec::with_capacity(len);
let r = RAND_bytes(out.as_mut_ptr(), len as c_int);
if r != 1 as c_int { fail!() }
ffi::init();
let r = ffi::RAND_bytes(out.as_mut_ptr(), len as c_int);
if r != 1 as c_int { panic!() }
out.set_len(len);

View File

@ -1,36 +1,6 @@
use libc::{c_int, c_uint};
use libc;
use libc::{c_int};
#[allow(non_camel_case_types)]
pub type EVP_CIPHER_CTX = *mut libc::c_void;
#[allow(non_camel_case_types)]
pub type EVP_CIPHER = *mut libc::c_void;
#[link(name = "crypto")]
extern {
fn EVP_CIPHER_CTX_new() -> EVP_CIPHER_CTX;
fn EVP_CIPHER_CTX_set_padding(ctx: EVP_CIPHER_CTX, padding: c_int);
fn EVP_CIPHER_CTX_free(ctx: EVP_CIPHER_CTX);
fn EVP_aes_128_ecb() -> EVP_CIPHER;
fn EVP_aes_128_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: *const u8, iv: *const u8, mode: c_int);
fn EVP_CipherUpdate(ctx: EVP_CIPHER_CTX, outbuf: *mut u8,
outlen: &mut c_uint, inbuf: *const u8, inlen: c_int);
fn EVP_CipherFinal(ctx: EVP_CIPHER_CTX, res: *mut u8, len: &mut c_int);
}
use ffi;
pub enum Mode {
Encrypt,
@ -41,46 +11,58 @@ pub enum Mode {
pub enum Type {
AES_128_ECB,
AES_128_CBC,
/// Requires the `aes_xts` feature
#[cfg(feature = "aes_xts")]
AES_128_XTS,
// AES_128_CTR,
//AES_128_GCM,
AES_256_ECB,
AES_256_CBC,
/// Requires the `aes_xts` feature
#[cfg(feature = "aes_xts")]
AES_256_XTS,
// AES_256_CTR,
//AES_256_GCM,
RC4_128,
}
fn evpc(t: Type) -> (EVP_CIPHER, uint, uint) {
fn evpc(t: Type) -> (*const ffi::EVP_CIPHER, uint, uint) {
unsafe {
match t {
AES_128_ECB => (EVP_aes_128_ecb(), 16u, 16u),
AES_128_CBC => (EVP_aes_128_cbc(), 16u, 16u),
Type::AES_128_ECB => (ffi::EVP_aes_128_ecb(), 16u, 16u),
Type::AES_128_CBC => (ffi::EVP_aes_128_cbc(), 16u, 16u),
#[cfg(feature = "aes_xts")]
Type::AES_128_XTS => (ffi::EVP_aes_128_xts(), 32u, 16u),
// AES_128_CTR => (EVP_aes_128_ctr(), 16u, 0u),
//AES_128_GCM => (EVP_aes_128_gcm(), 16u, 16u),
AES_256_ECB => (EVP_aes_256_ecb(), 32u, 16u),
AES_256_CBC => (EVP_aes_256_cbc(), 32u, 16u),
Type::AES_256_ECB => (ffi::EVP_aes_256_ecb(), 32u, 16u),
Type::AES_256_CBC => (ffi::EVP_aes_256_cbc(), 32u, 16u),
#[cfg(feature = "aes_xts")]
Type::AES_256_XTS => (ffi::EVP_aes_256_xts(), 64u, 16u),
// AES_256_CTR => (EVP_aes_256_ctr(), 32u, 0u),
//AES_256_GCM => (EVP_aes_256_gcm(), 32u, 16u),
RC4_128 => (EVP_rc4(), 16u, 0u),
Type::RC4_128 => (ffi::EVP_rc4(), 16u, 0u),
}
}
}
/// Represents a symmetric cipher context.
pub struct Crypter {
evp: EVP_CIPHER,
ctx: EVP_CIPHER_CTX,
evp: *const ffi::EVP_CIPHER,
ctx: *mut ffi::EVP_CIPHER_CTX,
keylen: uint,
blocksize: uint
}
impl Crypter {
pub fn new(t: Type) -> Crypter {
let ctx = unsafe { EVP_CIPHER_CTX_new() };
ffi::init();
let ctx = unsafe { ffi::EVP_CIPHER_CTX_new() };
let (evp, keylen, blocksz) = evpc(t);
Crypter { evp: evp, ctx: ctx, keylen: keylen, blocksize: blocksz }
}
@ -93,7 +75,7 @@ impl Crypter {
if self.blocksize > 0 {
unsafe {
let v = if padding { 1 as c_int } else { 0 };
EVP_CIPHER_CTX_set_padding(self.ctx, v);
ffi::EVP_CIPHER_CTX_set_padding(self.ctx, v);
}
}
}
@ -104,12 +86,12 @@ impl Crypter {
pub fn init(&self, mode: Mode, key: &[u8], iv: Vec<u8>) {
unsafe {
let mode = match mode {
Encrypt => 1 as c_int,
Decrypt => 0 as c_int,
Mode::Encrypt => 1 as c_int,
Mode::Decrypt => 0 as c_int,
};
assert_eq!(key.len(), self.keylen);
EVP_CipherInit(
ffi::EVP_CipherInit(
self.ctx,
self.evp,
key.as_ptr(),
@ -128,7 +110,7 @@ impl Crypter {
let mut res = Vec::from_elem(data.len() + self.blocksize, 0u8);
let mut reslen = (data.len() + self.blocksize) as u32;
EVP_CipherUpdate(
ffi::EVP_CipherUpdate(
self.ctx,
res.as_mut_ptr(),
&mut reslen,
@ -144,12 +126,12 @@ impl Crypter {
/**
* Finish crypting. Returns the remaining partial block of output, if any.
*/
pub fn final(&self) -> Vec<u8> {
pub fn finalize(&self) -> Vec<u8> {
unsafe {
let mut res = Vec::from_elem(self.blocksize, 0u8);
let mut reslen = self.blocksize as c_int;
EVP_CipherFinal(self.ctx,
ffi::EVP_CipherFinal(self.ctx,
res.as_mut_ptr(),
&mut reslen);
@ -162,7 +144,7 @@ impl Crypter {
impl Drop for Crypter {
fn drop(&mut self) {
unsafe {
EVP_CIPHER_CTX_free(self.ctx);
ffi::EVP_CIPHER_CTX_free(self.ctx);
}
}
}
@ -173,9 +155,9 @@ impl Drop for Crypter {
*/
pub fn encrypt(t: Type, key: &[u8], iv: Vec<u8>, data: &[u8]) -> Vec<u8> {
let c = Crypter::new(t);
c.init(Encrypt, key, iv);
c.init(Mode::Encrypt, key, iv);
let mut r = c.update(data);
let rest = c.final();
let rest = c.finalize();
r.extend(rest.into_iter());
r
}
@ -186,9 +168,9 @@ pub fn encrypt(t: Type, key: &[u8], iv: Vec<u8>, data: &[u8]) -> Vec<u8> {
*/
pub fn decrypt(t: Type, key: &[u8], iv: Vec<u8>, data: &[u8]) -> Vec<u8> {
let c = Crypter::new(t);
c.init(Decrypt, key, iv);
c.init(Mode::Decrypt, key, iv);
let mut r = c.update(data);
let rest = c.final();
let rest = c.finalize();
r.extend(rest.into_iter());
r
}
@ -212,28 +194,62 @@ mod tests {
let c0 =
vec!(0x8eu8, 0xa2u8, 0xb7u8, 0xcau8, 0x51u8, 0x67u8, 0x45u8, 0xbfu8,
0xeau8, 0xfcu8, 0x49u8, 0x90u8, 0x4bu8, 0x49u8, 0x60u8, 0x89u8);
let c = super::Crypter::new(super::AES_256_ECB);
c.init(super::Encrypt, k0.as_slice(), vec![]);
let c = super::Crypter::new(super::Type::AES_256_ECB);
c.init(super::Mode::Encrypt, k0.as_slice(), vec![]);
c.pad(false);
let mut r0 = c.update(p0.as_slice());
r0.extend(c.final().into_iter());
r0.extend(c.finalize().into_iter());
assert!(r0 == c0);
c.init(super::Decrypt, k0.as_slice(), vec![]);
c.init(super::Mode::Decrypt, k0.as_slice(), vec![]);
c.pad(false);
let mut p1 = c.update(r0.as_slice());
p1.extend(c.final().into_iter());
p1.extend(c.finalize().into_iter());
assert!(p1 == p0);
}
#[test]
fn test_aes_256_cbc_decrypt() {
let cr = super::Crypter::new(super::Type::AES_256_CBC);
let 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
];
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 ciphered_data = [
0x4a_u8, 0x2e_u8, 0xe5_u8, 0x6_u8, 0xbf_u8, 0xcf_u8, 0xf2_u8, 0xd7_u8,
0xea_u8, 0x2d_u8, 0xb1_u8, 0x85_u8, 0x6c_u8, 0x93_u8, 0x65_u8, 0x6f_u8
];
cr.init(super::Mode::Decrypt, &data, iv);
cr.pad(false);
let unciphered_data_1 = cr.update(&ciphered_data);
let unciphered_data_2 = cr.finalize();
let expected_unciphered_data = b"I love turtles.\x01";
assert!(unciphered_data_2.len() == 0);
assert_eq!(
unciphered_data_1.as_slice(),
expected_unciphered_data
);
}
fn cipher_test(ciphertype: super::Type, pt: &str, ct: &str, key: &str, iv: &str) {
use serialize::hex::ToHex;
let cipher = super::Crypter::new(ciphertype);
cipher.init(super::Encrypt, key.from_hex().unwrap().as_slice(), iv.from_hex().unwrap());
cipher.init(super::Mode::Encrypt, key.from_hex().unwrap().as_slice(), iv.from_hex().unwrap());
let expected = ct.from_hex().unwrap().as_slice().to_vec();
let mut computed = cipher.update(pt.from_hex().unwrap().as_slice());
computed.extend(cipher.final().into_iter());
computed.extend(cipher.finalize().into_iter());
if computed != expected {
println!("Computed: {}", computed.as_slice().to_hex());
@ -242,7 +258,7 @@ mod tests {
println!("Lengths differ: {} in computed vs {} expected",
computed.len(), expected.len());
}
fail!("test failure");
panic!("test failure");
}
}
@ -254,7 +270,20 @@ mod tests {
let key = "97CD440324DA5FD1F7955C1C13B6B466";
let iv = "";
cipher_test(super::RC4_128, pt, ct, key, iv);
cipher_test(super::Type::RC4_128, pt, ct, key, iv);
}
#[test]
#[cfg(feature = "aes_xts")]
fn test_aes256_xts() {
// Test case 174 from
// http://csrc.nist.gov/groups/STM/cavp/documents/aes/XTSTestVectors.zip
let pt = "77f4ef63d734ebd028508da66c22cdebdd52ecd6ee2ab0a50bc8ad0cfd692ca5fcd4e6dedc45df7f6503f462611dc542";
let ct = "ce7d905a7776ac72f240d22aafed5e4eb7566cdc7211220e970da634ce015f131a5ecb8d400bc9e84f0b81d8725dbbc7";
let key = "b6bfef891f83b5ff073f2231267be51eb084b791fa19a154399c0684c8b2dfcb37de77d28bbda3b4180026ad640b74243b3133e7b9fae629403f6733423dae28";
let iv = "db200efb7eaaa737dbdf40babb68953f";
cipher_test(super::Type::AES_256_XTS, pt, ct, key, iv);
}
/*#[test]

View File

@ -1,14 +1,20 @@
#![feature(struct_variant, macro_rules)]
#![feature(macro_rules, unsafe_destructor, globs)]
#![crate_name="openssl"]
#![crate_type="rlib"]
#![crate_type="dylib"]
#![doc(html_root_url="http://www.rust-ci.org/sfackler/rust-openssl/doc")]
#![doc(html_root_url="https://sfackler.github.io/doc/openssl")]
extern crate libc;
#[cfg(test)]
extern crate serialize;
extern crate sync;
pub mod ssl;
pub mod crypto;
extern crate "openssl-sys" as ffi;
mod macros;
pub mod asn1;
pub mod bn;
pub mod bio;
pub mod crypto;
pub mod ssl;
pub mod x509;

59
src/macros.rs Normal file
View File

@ -0,0 +1,59 @@
#![macro_escape]
macro_rules! try_ssl_stream {
($e:expr) => (
match $e {
Ok(ok) => ok,
Err(err) => return Err(StreamError(err))
}
)
}
/// Shortcut return with SSL error if something went wrong
macro_rules! try_ssl_if {
($e:expr) => (
if $e {
return Err(SslError::get())
}
)
}
/// Shortcut return with SSL error if last error result is 0
/// (default)
macro_rules! try_ssl{
($e:expr) => (try_ssl_if!($e == 0))
}
/// Shortcut return with SSL if got a null result
macro_rules! try_ssl_null{
($e:expr) => ({
let t = $e;
try_ssl_if!(t == ptr::null_mut());
t
})
}
/// Lifts current SSL error code into Result<(), Error>
/// if expression is true
/// Lifting is actually a shortcut of the following form:
///
/// ```ignore
/// let _ = try!(something)
/// Ok(())
/// ```
macro_rules! lift_ssl_if{
($e:expr) => ( {
if $e {
Err(SslError::get())
} else {
Ok(())
}
})
}
/// Lifts current SSL error code into Result<(), Error>
/// if SSL returned 0 (default error indication)
macro_rules! lift_ssl {
($e:expr) => (lift_ssl_if!($e == 0))
}

View File

@ -1,12 +1,17 @@
use libc::c_ulong;
use std::io::IoError;
pub use self::SslError::*;
pub use self::OpensslError::*;
use ssl::ffi;
use libc::c_ulong;
use std::error;
use std::io::IoError;
use std::c_str::CString;
use ffi;
/// An SSL error
#[deriving(Show, Clone, PartialEq, Eq)]
pub enum SslError {
/// The underlying stream has reported an error
/// The underlying stream reported an error
StreamError(IoError),
/// The SSL session has been closed by the other end
SslSessionClosed,
@ -14,30 +19,47 @@ pub enum SslError {
OpenSslErrors(Vec<OpensslError>)
}
impl error::Error for SslError {
fn description(&self) -> &str {
match *self {
StreamError(_) => "The underlying stream reported an error",
SslSessionClosed => "The SSL session has been closed by the other end",
OpenSslErrors(_) => "An error in the OpenSSL library",
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
StreamError(ref err) => Some(err as &error::Error),
_ => None
}
}
}
/// An error from the OpenSSL library
#[deriving(Show, Clone, PartialEq, Eq)]
pub enum OpensslError {
/// An unknown error
UnknownError {
/// The library reporting the error
library: u8,
library: String,
/// The function reporting the error
function: u16,
function: String,
/// The reason for the error
reason: u16
reason: String
}
}
fn get_lib(err: c_ulong) -> u8 {
((err >> 24) & 0xff) as u8
fn get_lib(err: c_ulong) -> String {
unsafe { CString::new(ffi::ERR_lib_error_string(err), false) }.to_string()
}
fn get_func(err: c_ulong) -> u16 {
((err >> 12) & 0xfff) as u16
fn get_func(err: c_ulong) -> String {
unsafe { CString::new(ffi::ERR_func_error_string(err), false).to_string() }
}
fn get_reason(err: c_ulong) -> u16 {
(err & 0xfff) as u16
fn get_reason(err: c_ulong) -> String {
unsafe { CString::new(ffi::ERR_reason_error_string(err), false).to_string() }
}
impl SslError {
@ -48,13 +70,37 @@ impl SslError {
loop {
match unsafe { ffi::ERR_get_error() } {
0 => break,
err => errs.push(UnknownError {
library: get_lib(err),
function: get_func(err),
reason: get_reason(err)
})
err => errs.push(SslError::from_error_code(err))
}
}
OpenSslErrors(errs)
}
/// Creates an `SslError` from the raw numeric error code.
pub fn from_error(err: c_ulong) -> SslError {
OpenSslErrors(vec![SslError::from_error_code(err)])
}
fn from_error_code(err: c_ulong) -> OpensslError {
ffi::init();
UnknownError {
library: get_lib(err),
function: get_func(err),
reason: get_reason(err)
}
}
}
#[test]
fn test_uknown_error_should_have_correct_messages() {
let errs = match SslError::from_error(336032784) {
OpenSslErrors(errs) => errs,
_ => panic!("This should always be an `OpenSslErrors` variant.")
};
let UnknownError { ref library, ref function, ref reason } = errs[0];
assert_eq!(library.as_slice(),"SSL routines");
assert_eq!(function.as_slice(), "SSL23_GET_SERVER_HELLO");
assert_eq!(reason.as_slice(), "sslv3 alert handshake failure");
}

View File

@ -1,192 +0,0 @@
#![allow(non_camel_case_types)]
use libc::{c_int, c_void, c_long, c_ulong, c_char, c_uint};
use crypto::hash::{EVP_MD};
pub type SSL_CTX = c_void;
pub type SSL_METHOD = c_void;
pub type COMP_METHOD = c_void;
pub type SSL = c_void;
pub type BIO = c_void;
pub type BIO_METHOD = c_void;
pub type X509_STORE_CTX = c_void;
pub type X509 = c_void;
pub type X509_NAME = c_void;
pub type CRYPTO_EX_DATA = c_void;
pub type CRYPTO_EX_new = extern "C" fn(parent: *mut c_void, ptr: *mut c_void,
ad: *const CRYPTO_EX_DATA, idx: c_int,
argl: c_long, argp: *const c_void) -> c_int;
pub type CRYPTO_EX_dup = extern "C" fn(to: *mut CRYPTO_EX_DATA,
from: *mut CRYPTO_EX_DATA, from_d: *mut c_void,
idx: c_int, argl: c_long, argp: *mut c_void)
-> c_int;
pub type CRYPTO_EX_free = extern "C" fn(parent: *mut c_void, ptr: *mut c_void,
ad: *mut CRYPTO_EX_DATA, idx: c_int,
argl: c_long, argp: *mut c_void);
pub static CRYPTO_LOCK: c_int = 1;
pub static SSL_ERROR_NONE: c_int = 0;
pub static SSL_ERROR_SSL: c_int = 1;
pub static SSL_ERROR_WANT_READ: c_int = 2;
pub static SSL_ERROR_WANT_WRITE: c_int = 3;
pub static SSL_ERROR_WANT_X509_LOOKUP: c_int = 4;
pub static SSL_ERROR_SYSCALL: c_int = 5;
pub static SSL_ERROR_ZERO_RETURN: c_int = 6;
pub static SSL_ERROR_WANT_CONNECT: c_int = 7;
pub static SSL_ERROR_WANT_ACCEPT: c_int = 8;
pub static SSL_VERIFY_NONE: c_int = 0;
pub static SSL_VERIFY_PEER: c_int = 1;
pub static SSL_CTRL_SET_TLSEXT_HOSTNAME: c_int = 55;
pub static TLSEXT_NAMETYPE_host_name: c_long = 0;
pub static X509_V_OK: c_int = 0;
pub static X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: c_int = 2;
pub static X509_V_ERR_UNABLE_TO_GET_CRL: c_int = 3;
pub static X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: c_int = 4;
pub static X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: c_int = 5;
pub static X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: c_int = 6;
pub static X509_V_ERR_CERT_SIGNATURE_FAILURE: c_int = 7;
pub static X509_V_ERR_CRL_SIGNATURE_FAILURE: c_int = 8;
pub static X509_V_ERR_CERT_NOT_YET_VALID: c_int = 9;
pub static X509_V_ERR_CERT_HAS_EXPIRED: c_int = 10;
pub static X509_V_ERR_CRL_NOT_YET_VALID: c_int = 11;
pub static X509_V_ERR_CRL_HAS_EXPIRED: c_int = 12;
pub static X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: c_int = 13;
pub static X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: c_int = 14;
pub static X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: c_int = 15;
pub static X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: c_int = 16;
pub static X509_V_ERR_OUT_OF_MEM: c_int = 17;
pub static X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: c_int = 18;
pub static X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: c_int = 19;
pub static X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: c_int = 20;
pub static X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: c_int = 21;
pub static X509_V_ERR_CERT_CHAIN_TOO_LONG: c_int = 22;
pub static X509_V_ERR_CERT_REVOKED: c_int = 23;
pub static X509_V_ERR_INVALID_CA: c_int = 24;
pub static X509_V_ERR_PATH_LENGTH_EXCEEDED: c_int = 25;
pub static X509_V_ERR_INVALID_PURPOSE: c_int = 26;
pub static X509_V_ERR_CERT_UNTRUSTED: c_int = 27;
pub static X509_V_ERR_CERT_REJECTED: c_int = 28;
pub static X509_V_ERR_SUBJECT_ISSUER_MISMATCH: c_int = 29;
pub static X509_V_ERR_AKID_SKID_MISMATCH: c_int = 30;
pub static X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: c_int = 31;
pub static X509_V_ERR_KEYUSAGE_NO_CERTSIGN: c_int = 32;
pub static X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: c_int = 33;
pub static X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: c_int = 34;
pub static X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: c_int = 35;
pub static X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: c_int = 36;
pub static X509_V_ERR_INVALID_NON_CA: c_int = 37;
pub static X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: c_int = 38;
pub static X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: c_int = 39;
pub static X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: c_int = 40;
pub static X509_V_ERR_INVALID_EXTENSION: c_int = 41;
pub static X509_V_ERR_INVALID_POLICY_EXTENSION: c_int = 42;
pub static X509_V_ERR_NO_EXPLICIT_POLICY: c_int = 43;
pub static X509_V_ERR_DIFFERENT_CRL_SCOPE: c_int = 44;
pub static X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: c_int = 45;
pub static X509_V_ERR_UNNESTED_RESOURCE: c_int = 46;
pub static X509_V_ERR_PERMITTED_VIOLATION: c_int = 47;
pub static X509_V_ERR_EXCLUDED_VIOLATION: c_int = 48;
pub static X509_V_ERR_SUBTREE_MINMAX: c_int = 49;
pub static X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: c_int = 51;
pub static X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: c_int = 52;
pub static X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: c_int = 53;
pub static X509_V_ERR_CRL_PATH_VALIDATION_ERROR: c_int = 54;
pub static X509_V_ERR_APPLICATION_VERIFICATION: c_int = 50;
pub static X509_FILETYPE_PEM: c_int = 1;
pub static X509_FILETYPE_ASN1: c_int = 2;
pub static X509_FILETYPE_DEFAULT: c_int = 3;
#[cfg(target_os = "macos", feature = "tlsv1_1")]
#[cfg(target_os = "macos", feature = "tlsv1_2")]
#[link(name="ssl.1.0.0")]
#[link(name="crypto.1.0.0")]
extern {}
#[cfg(not(target_os = "macos"))]
#[cfg(target_os = "macos", not(feature = "tlsv1_1"), not(feature = "tlsv1_2"))]
#[link(name="ssl")]
#[link(name="crypto")]
extern {}
extern "C" {
pub fn CRYPTO_num_locks() -> c_int;
pub fn CRYPTO_set_locking_callback(func: extern "C" fn(mode: c_int,
n: c_int,
file: *const c_char,
line: c_int));
pub fn ERR_get_error() -> c_ulong;
pub fn SSL_library_init() -> c_int;
#[cfg(feature = "sslv2")]
pub fn SSLv2_method() -> *const SSL_METHOD;
pub fn SSLv3_method() -> *const SSL_METHOD;
pub fn TLSv1_method() -> *const SSL_METHOD;
#[cfg(feature = "tlsv1_1")]
pub fn TLSv1_1_method() -> *const SSL_METHOD;
#[cfg(feature = "tlsv1_2")]
pub fn TLSv1_2_method() -> *const SSL_METHOD;
pub fn SSLv23_method() -> *const SSL_METHOD;
pub fn SSL_CTX_new(method: *const SSL_METHOD) -> *mut SSL_CTX;
pub fn SSL_CTX_free(ctx: *mut SSL_CTX);
pub fn SSL_CTX_set_verify(ctx: *mut SSL_CTX, mode: c_int,
verify_callback: Option<extern fn(c_int, *mut X509_STORE_CTX) -> c_int>);
pub fn SSL_CTX_load_verify_locations(ctx: *mut SSL_CTX, CAfile: *const c_char,
CApath: *const c_char) -> c_int;
pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *const c_void,
new_func: Option<CRYPTO_EX_new>,
dup_func: Option<CRYPTO_EX_dup>,
free_func: Option<CRYPTO_EX_free>)
-> c_int;
pub fn SSL_CTX_set_ex_data(ctx: *mut SSL_CTX, idx: c_int, data: *mut c_void)
-> c_int;
pub fn SSL_CTX_get_ex_data(ctx: *mut SSL_CTX, idx: c_int) -> *mut c_void;
pub fn SSL_CTX_use_certificate_file(ctx: *mut SSL_CTX, cert_file: *const c_char, file_type: c_int) -> c_int;
pub fn SSL_CTX_use_PrivateKey_file(ctx: *mut SSL_CTX, key_file: *const c_char, file_type: c_int) -> c_int;
pub fn X509_STORE_CTX_get_ex_data(ctx: *mut X509_STORE_CTX, idx: c_int)
-> *mut c_void;
pub fn X509_STORE_CTX_get_current_cert(ct: *mut X509_STORE_CTX) -> *mut X509;
pub fn X509_STORE_CTX_get_error(ctx: *mut X509_STORE_CTX) -> c_int;
pub fn X509_get_subject_name(x: *mut X509) -> *mut X509_NAME;
pub fn X509_digest(x: *mut X509, digest: *const EVP_MD, buf: *mut c_char, len: *mut c_uint) -> c_int;
pub fn SSL_new(ctx: *mut SSL_CTX) -> *mut SSL;
pub fn SSL_free(ssl: *mut SSL);
pub fn SSL_set_bio(ssl: *mut SSL, rbio: *mut BIO, wbio: *mut BIO);
pub fn SSL_get_rbio(ssl: *mut SSL) -> *mut BIO;
pub fn SSL_get_wbio(ssl: *mut SSL) -> *mut BIO;
pub fn SSL_connect(ssl: *mut SSL) -> c_int;
pub fn SSL_ctrl(ssl: *mut SSL, cmd: c_int, larg: c_long,
parg: *mut c_void) -> c_long;
pub fn SSL_get_error(ssl: *mut SSL, ret: c_int) -> c_int;
pub fn SSL_read(ssl: *mut SSL, buf: *mut c_void, num: c_int) -> c_int;
pub fn SSL_write(ssl: *mut SSL, buf: *const c_void, num: c_int) -> c_int;
pub fn SSL_get_ex_data_X509_STORE_CTX_idx() -> c_int;
pub fn SSL_get_SSL_CTX(ssl: *mut SSL) -> *mut SSL_CTX;
pub fn SSL_get_current_compression(ssl: *mut SSL) -> *const COMP_METHOD;
pub fn BIO_s_mem() -> *const BIO_METHOD;
pub fn BIO_new(type_: *const BIO_METHOD) -> *mut BIO;
pub fn BIO_free_all(a: *mut BIO);
pub fn BIO_read(b: *mut BIO, buf: *mut c_void, len: c_int) -> c_int;
pub fn BIO_write(b: *mut BIO, buf: *const c_void, len: c_int) -> c_int;
pub fn SSL_COMP_get_name(comp: *const COMP_METHOD) -> *const c_char;
}
#[cfg(target_os = "win32")]
#[link(name="gdi32")]
#[link(name="wsock32")]
extern { }

View File

@ -1,47 +1,31 @@
use libc::{c_int, c_uint, c_void, c_char};
use libc::{c_int, c_void, c_long};
use std::io::{IoResult, IoError, EndOfFile, Stream, Reader, Writer};
use std::mem;
use std::ptr;
use std::rt::mutex::NativeMutex;
use std::string;
use sync::one::{Once, ONCE_INIT};
use std::sync::{Once, ONCE_INIT, Arc};
use crypto::hash::{HashType, evpmd};
use bio::{MemBio};
use ffi;
use ssl::error::{SslError, SslSessionClosed, StreamError};
use x509::{X509StoreContext, X509FileType, X509};
pub mod error;
mod ffi;
#[cfg(test)]
mod tests;
static mut VERIFY_IDX: c_int = -1;
static mut MUTEXES: *mut Vec<NativeMutex> = 0 as *mut Vec<NativeMutex>;
macro_rules! try_ssl(
($e:expr) => (
match $e {
Ok(ok) => ok,
Err(err) => return Err(StreamError(err))
}
)
)
fn init() {
static mut INIT: Once = ONCE_INIT;
unsafe {
INIT.doit(|| {
ffi::SSL_library_init();
ffi::init();
let verify_idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None,
None, None);
assert!(verify_idx >= 0);
VERIFY_IDX = verify_idx;
let num_locks = ffi::CRYPTO_num_locks();
let mutexes = box Vec::from_fn(num_locks as uint, |_| NativeMutex::new());
MUTEXES = mem::transmute(mutexes);
ffi::CRYPTO_set_locking_callback(locking_function);
});
}
}
@ -51,17 +35,19 @@ fn init() {
#[allow(non_camel_case_types)]
pub enum SslMethod {
#[cfg(feature = "sslv2")]
/// Only support the SSLv2 protocol
/// Only support the SSLv2 protocol, requires `feature="sslv2"`
Sslv2,
/// Support the SSLv2, SSLv3 and TLSv1 protocols
Sslv23,
/// Only support the SSLv3 protocol
Sslv3,
/// Only support the TLSv1 protocol
Tlsv1,
/// Support the SSLv2, SSLv3 and TLSv1 protocols
Sslv23,
#[cfg(feature = "tlsv1_1")]
/// Support TLSv1.1 protocol, requires `feature="tlsv1_1"`
Tlsv1_1,
#[cfg(feature = "tlsv1_2")]
/// Support TLSv1.2 protocol, requires `feature="tlsv1_2"`
Tlsv1_2,
}
@ -69,14 +55,14 @@ impl SslMethod {
unsafe fn to_raw(&self) -> *const ffi::SSL_METHOD {
match *self {
#[cfg(feature = "sslv2")]
Sslv2 => ffi::SSLv2_method(),
Sslv3 => ffi::SSLv3_method(),
Tlsv1 => ffi::TLSv1_method(),
Sslv23 => ffi::SSLv23_method(),
SslMethod::Sslv2 => ffi::SSLv2_method(),
SslMethod::Sslv3 => ffi::SSLv3_method(),
SslMethod::Tlsv1 => ffi::TLSv1_method(),
SslMethod::Sslv23 => ffi::SSLv23_method(),
#[cfg(feature = "tlsv1_1")]
Tlsv1_1 => ffi::TLSv1_1_method(),
SslMethod::Tlsv1_1 => ffi::TLSv1_1_method(),
#[cfg(feature = "tlsv1_2")]
Tlsv1_2 => ffi::TLSv1_2_method()
SslMethod::Tlsv1_2 => ffi::TLSv1_2_method()
}
}
}
@ -90,16 +76,27 @@ pub enum SslVerifyMode {
SslVerifyNone = ffi::SSL_VERIFY_NONE
}
extern fn locking_function(mode: c_int, n: c_int, _file: *const c_char,
_line: c_int) {
unsafe {
let mutex = (*MUTEXES).get_mut(n as uint);
// Creates a static index for user data of type T
// Registers a destructor for the data which will be called
// when context is freed
fn get_verify_data_idx<T>() -> c_int {
static mut VERIFY_DATA_IDX: c_int = -1;
static mut INIT: Once = ONCE_INIT;
if mode & ffi::CRYPTO_LOCK != 0 {
mutex.lock_noguard();
} else {
mutex.unlock_noguard();
}
extern fn free_data_box<T>(_parent: *mut c_void, ptr: *mut c_void,
_ad: *mut ffi::CRYPTO_EX_DATA, _idx: c_int,
_argl: c_long, _argp: *mut c_void) {
let _: Box<T> = unsafe { mem::transmute(ptr) };
}
unsafe {
INIT.doit(|| {
let idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None,
None, Some(free_data_box::<T>));
assert!(idx >= 0);
VERIFY_DATA_IDX = idx;
});
VERIFY_DATA_IDX
}
}
@ -112,7 +109,7 @@ extern fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX)
let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, VERIFY_IDX);
let verify: Option<VerifyCallback> = mem::transmute(verify);
let ctx = X509StoreContext { ctx: x509_ctx };
let ctx = X509StoreContext::new(x509_ctx);
match verify {
None => preverify_ok,
@ -121,16 +118,44 @@ extern fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX)
}
}
extern fn raw_verify_with_data<T>(preverify_ok: c_int,
x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int {
unsafe {
let idx = ffi::SSL_get_ex_data_X509_STORE_CTX_idx();
let ssl = ffi::X509_STORE_CTX_get_ex_data(x509_ctx, idx);
let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, VERIFY_IDX);
let verify: Option<VerifyCallbackData<T>> = mem::transmute(verify);
let data = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::<T>());
let data: Box<T> = mem::transmute(data);
let ctx = X509StoreContext::new(x509_ctx);
let res = match verify {
None => preverify_ok,
Some(verify) => verify(preverify_ok != 0, &ctx, &*data) as c_int
};
// Since data might be required on the next verification
// it is time to forget about it and avoid dropping
// data will be freed once OpenSSL considers it is time
// to free all context data
mem::forget(data);
res
}
}
/// The signature of functions that can be used to manually verify certificates
pub type VerifyCallback = fn(preverify_ok: bool,
x509_ctx: &X509StoreContext) -> bool;
#[repr(i32)]
pub enum X509FileType {
PEM = ffi::X509_FILETYPE_PEM,
ASN1 = ffi::X509_FILETYPE_ASN1,
Default = ffi::X509_FILETYPE_DEFAULT
}
/// The signature of functions that can be used to manually verify certificates
/// when user-data should be carried for all verification process
pub type VerifyCallbackData<T> = fn(preverify_ok: bool,
x509_ctx: &X509StoreContext,
data: &T) -> bool;
// FIXME: macro may be instead of inlining?
#[inline]
@ -176,9 +201,33 @@ impl SslContext {
}
}
/// Configures the certificate verification method for new connections also
/// carrying supplied data.
// Note: no option because there is no point to set data without providing
// a function handling it
pub fn set_verify_with_data<T>(&mut self, mode: SslVerifyMode,
verify: VerifyCallbackData<T>,
data: T) {
let data = box data;
unsafe {
ffi::SSL_CTX_set_ex_data(self.ctx, VERIFY_IDX,
mem::transmute(Some(verify)));
ffi::SSL_CTX_set_ex_data(self.ctx, get_verify_data_idx::<T>(),
mem::transmute(data));
ffi::SSL_CTX_set_verify(self.ctx, mode as c_int, Some(raw_verify_with_data::<T>));
}
}
/// Sets verification depth
pub fn set_verify_depth(&mut self, depth: uint) {
unsafe {
ffi::SSL_CTX_set_verify_depth(self.ctx, depth as c_int);
}
}
#[allow(non_snake_case)]
/// Specifies the file that contains trusted CA certificates.
pub fn set_CA_file(&mut self, file: &str) -> Option<SslError> {
pub fn set_CA_file(&mut self, file: &Path) -> Option<SslError> {
wrap_ssl_result(file.with_c_str(|file| {
unsafe {
ffi::SSL_CTX_load_verify_locations(self.ctx, file, ptr::null())
@ -186,8 +235,8 @@ impl SslContext {
}))
}
/// Specifies the file that is client certificate
pub fn set_certificate_file(&mut self, file: &str,
/// Specifies the file that contains certificate
pub fn set_certificate_file(&mut self, file: &Path,
file_type: X509FileType) -> Option<SslError> {
wrap_ssl_result(file.with_c_str(|file| {
unsafe {
@ -196,8 +245,8 @@ impl SslContext {
}))
}
/// Specifies the file that is client private key
pub fn set_private_key_file(&mut self, file: &str,
/// Specifies the file that contains private key
pub fn set_private_key_file(&mut self, file: &Path,
file_type: X509FileType) -> Option<SslError> {
wrap_ssl_result(file.with_c_str(|file| {
unsafe {
@ -205,148 +254,32 @@ impl SslContext {
}
}))
}
}
pub struct X509StoreContext {
ctx: *mut ffi::X509_STORE_CTX
}
impl X509StoreContext {
pub fn get_error(&self) -> Option<X509ValidationError> {
let err = unsafe { ffi::X509_STORE_CTX_get_error(self.ctx) };
X509ValidationError::from_raw(err)
}
pub fn get_current_cert<'a>(&'a self) -> Option<X509<'a>> {
let ptr = unsafe { ffi::X509_STORE_CTX_get_current_cert(self.ctx) };
if ptr.is_null() {
None
} else {
Some(X509 { ctx: self, x509: ptr })
}
pub fn set_cipher_list(&mut self, cipher_list: &str) -> Option<SslError> {
wrap_ssl_result(cipher_list.with_c_str(|cipher_list| {
unsafe {
ffi::SSL_CTX_set_cipher_list(self.ctx, cipher_list)
}
}))
}
}
#[allow(dead_code)]
/// A public key certificate
pub struct X509<'ctx> {
ctx: &'ctx X509StoreContext,
x509: *mut ffi::X509
struct MemBioRef<'ssl> {
ssl: &'ssl Ssl,
bio: MemBio,
}
impl<'ctx> X509<'ctx> {
pub fn subject_name<'a>(&'a self) -> X509Name<'a> {
let name = unsafe { ffi::X509_get_subject_name(self.x509) };
X509Name { x509: self, name: name }
impl<'ssl> MemBioRef<'ssl> {
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
(&mut self.bio as &mut Reader).read(buf).ok()
}
/// Returns certificate fingerprint calculated using provided hash
pub fn fingerprint(&self, hash_type: HashType) -> Option<Vec<u8>> {
let (evp, len) = evpmd(hash_type);
let v: Vec<u8> = Vec::from_elem(len, 0);
let act_len: c_uint = 0;
let res = unsafe {
ffi::X509_digest(self.x509, evp, mem::transmute(v.as_ptr()),
mem::transmute(&act_len))
};
match res {
0 => None,
_ => {
let act_len = act_len as uint;
match len.cmp(&act_len) {
Greater => None,
Equal => Some(v),
Less => fail!("Fingerprint buffer was corrupted!")
}
}
}
fn write(&mut self, buf: &[u8]) {
let _ = (&mut self.bio as &mut Writer).write(buf);
}
}
#[allow(dead_code)]
pub struct X509Name<'x> {
x509: &'x X509<'x>,
name: *mut ffi::X509_NAME
}
macro_rules! make_validation_error(
($ok_val:ident, $($name:ident = $val:ident,)+) => (
pub enum X509ValidationError {
$($name,)+
X509UnknownError(c_int)
}
impl X509ValidationError {
#[doc(hidden)]
pub fn from_raw(err: c_int) -> Option<X509ValidationError> {
match err {
self::ffi::$ok_val => None,
$(self::ffi::$val => Some($name),)+
err => Some(X509UnknownError(err))
}
}
}
)
)
make_validation_error!(X509_V_OK,
X509UnableToGetIssuerCert = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
X509UnableToGetCrl = X509_V_ERR_UNABLE_TO_GET_CRL,
X509UnableToDecryptCertSignature = X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
X509UnableToDecryptCrlSignature = X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
X509UnableToDecodeIssuerPublicKey = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
X509CertSignatureFailure = X509_V_ERR_CERT_SIGNATURE_FAILURE,
X509CrlSignatureFailure = X509_V_ERR_CRL_SIGNATURE_FAILURE,
X509CertNotYetValid = X509_V_ERR_CERT_NOT_YET_VALID,
X509CertHasExpired = X509_V_ERR_CERT_HAS_EXPIRED,
X509CrlNotYetValid = X509_V_ERR_CRL_NOT_YET_VALID,
X509CrlHasExpired = X509_V_ERR_CRL_HAS_EXPIRED,
X509ErrorInCertNotBeforeField = X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
X509ErrorInCertNotAfterField = X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
X509ErrorInCrlLastUpdateField = X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
X509ErrorInCrlNextUpdateField = X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
X509OutOfMem = X509_V_ERR_OUT_OF_MEM,
X509DepthZeroSelfSignedCert = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
X509SelfSignedCertInChain = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
X509UnableToGetIssuerCertLocally = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
X509UnableToVerifyLeafSignature = X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
X509CertChainTooLong = X509_V_ERR_CERT_CHAIN_TOO_LONG,
X509CertRevoked = X509_V_ERR_CERT_REVOKED,
X509InvalidCA = X509_V_ERR_INVALID_CA,
X509PathLengthExceeded = X509_V_ERR_PATH_LENGTH_EXCEEDED,
X509InvalidPurpose = X509_V_ERR_INVALID_PURPOSE,
X509CertUntrusted = X509_V_ERR_CERT_UNTRUSTED,
X509CertRejected = X509_V_ERR_CERT_REJECTED,
X509SubjectIssuerMismatch = X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
X509AkidSkidMismatch = X509_V_ERR_AKID_SKID_MISMATCH,
X509AkidIssuerSerialMismatch = X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
X509KeyusageNoCertsign = X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
X509UnableToGetCrlIssuer = X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER,
X509UnhandledCriticalExtension = X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION,
X509KeyusageNoCrlSign = X509_V_ERR_KEYUSAGE_NO_CRL_SIGN,
X509UnhandledCriticalCrlExtension = X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION,
X509InvalidNonCA = X509_V_ERR_INVALID_NON_CA,
X509ProxyPathLengthExceeded = X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED,
X509KeyusageNoDigitalSignature = X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE,
X509ProxyCertificatesNotAllowed = X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED,
X509InvalidExtension = X509_V_ERR_INVALID_EXTENSION,
X509InavlidPolicyExtension = X509_V_ERR_INVALID_POLICY_EXTENSION,
X509NoExplicitPolicy = X509_V_ERR_NO_EXPLICIT_POLICY,
X509DifferentCrlScope = X509_V_ERR_DIFFERENT_CRL_SCOPE,
X509UnsupportedExtensionFeature = X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE,
X509UnnestedResource = X509_V_ERR_UNNESTED_RESOURCE,
X509PermittedVolation = X509_V_ERR_PERMITTED_VIOLATION,
X509ExcludedViolation = X509_V_ERR_EXCLUDED_VIOLATION,
X509SubtreeMinmax = X509_V_ERR_SUBTREE_MINMAX,
X509UnsupportedConstraintType = X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE,
X509UnsupportedConstraintSyntax = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX,
X509UnsupportedNameSyntax = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX,
X509CrlPathValidationError= X509_V_ERR_CRL_PATH_VALIDATION_ERROR,
X509ApplicationVerification = X509_V_ERR_APPLICATION_VERIFICATION,
)
pub struct Ssl {
ssl: *mut ffi::SSL
}
@ -365,18 +298,10 @@ impl Ssl {
}
let ssl = Ssl { ssl: ssl };
let rbio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) };
if rbio == ptr::null_mut() {
return Err(SslError::get());
}
let rbio = try!(MemBio::new());
let wbio = try!(MemBio::new());
let wbio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) };
if wbio == ptr::null_mut() {
unsafe { ffi::BIO_free_all(rbio) }
return Err(SslError::get());
}
unsafe { ffi::SSL_set_bio(ssl.ssl, rbio, wbio) }
unsafe { ffi::SSL_set_bio(ssl.ssl, rbio.unwrap(), wbio.unwrap()) }
Ok(ssl)
}
@ -389,13 +314,10 @@ impl Ssl {
}
fn wrap_bio<'a>(&'a self, bio: *mut ffi::BIO) -> MemBioRef<'a> {
assert!(bio != ptr::mut_null());
assert!(bio != ptr::null_mut());
MemBioRef {
ssl: self,
bio: MemBio {
bio: bio,
owned: false
}
bio: MemBio::borrowed(bio)
}
}
@ -403,6 +325,10 @@ impl Ssl {
unsafe { ffi::SSL_connect(self.ssl) }
}
fn accept(&self) -> c_int {
unsafe { ffi::SSL_accept(self.ssl) }
}
fn read(&self, buf: &mut [u8]) -> c_int {
unsafe { ffi::SSL_read(self.ssl, buf.as_ptr() as *mut c_void,
buf.len() as c_int) }
@ -443,6 +369,17 @@ impl Ssl {
}
}
pub fn get_peer_certificate(&self) -> Option<X509> {
unsafe {
let ptr = ffi::SSL_get_peer_certificate(self.ssl);
if ptr.is_null() {
None
} else {
Some(X509::new(ptr, true))
}
}
}
}
#[deriving(FromPrimitive)]
@ -459,110 +396,90 @@ enum LibSslError {
ErrorWantAccept = ffi::SSL_ERROR_WANT_ACCEPT,
}
#[allow(dead_code)]
struct MemBioRef<'ssl> {
ssl: &'ssl Ssl,
bio: MemBio,
}
impl<'ssl> MemBioRef<'ssl> {
fn read(&self, buf: &mut [u8]) -> Option<uint> {
self.bio.read(buf)
}
fn write(&self, buf: &[u8]) {
self.bio.write(buf)
}
}
struct MemBio {
bio: *mut ffi::BIO,
owned: bool
}
impl Drop for MemBio {
fn drop(&mut self) {
if self.owned {
unsafe {
ffi::BIO_free_all(self.bio);
}
}
}
}
impl MemBio {
fn read(&self, buf: &mut [u8]) -> Option<uint> {
let ret = unsafe {
ffi::BIO_read(self.bio, buf.as_ptr() as *mut c_void,
buf.len() as c_int)
};
if ret < 0 {
None
} else {
Some(ret as uint)
}
}
fn write(&self, buf: &[u8]) {
let ret = unsafe {
ffi::BIO_write(self.bio, buf.as_ptr() as *const c_void,
buf.len() as c_int)
};
assert_eq!(buf.len(), ret as uint);
}
}
/// A stream wrapper which handles SSL encryption for an underlying stream.
#[deriving(Clone)]
pub struct SslStream<S> {
stream: S,
ssl: Ssl,
ssl: Arc<Ssl>,
buf: Vec<u8>
}
impl<S: Stream> SslStream<S> {
/// Attempts to create a new SSL stream from a given `Ssl` instance.
pub fn new_from(ssl: Ssl, stream: S) -> Result<SslStream<S>, SslError> {
let mut ssl = SslStream {
fn new_base(ssl:Ssl, stream: S) -> SslStream<S> {
SslStream {
stream: stream,
ssl: ssl,
ssl: Arc::new(ssl),
// Maximum TLS record size is 16k
buf: Vec::from_elem(16 * 1024, 0u8)
};
match ssl.in_retry_wrapper(|ssl| { ssl.connect() }) {
Ok(_) => Ok(ssl),
Err(err) => Err(err)
}
}
pub fn new_server_from(ssl: Ssl, stream: S) -> Result<SslStream<S>, SslError> {
let mut ssl = SslStream::new_base(ssl, stream);
ssl.in_retry_wrapper(|ssl| { ssl.accept() }).and(Ok(ssl))
}
/// Attempts to create a new SSL stream from a given `Ssl` instance.
pub fn new_from(ssl: Ssl, stream: S) -> Result<SslStream<S>, SslError> {
let mut ssl = SslStream::new_base(ssl, stream);
ssl.in_retry_wrapper(|ssl| { ssl.connect() }).and(Ok(ssl))
}
/// Creates a new SSL stream
pub fn new(ctx: &SslContext, stream: S) -> Result<SslStream<S>, SslError> {
let ssl = match Ssl::new(ctx) {
Ok(ssl) => ssl,
Err(err) => return Err(err)
};
let ssl = try!(Ssl::new(ctx));
SslStream::new_from(ssl, stream)
}
/// Creates a new SSL server stream
pub fn new_server(ctx: &SslContext, stream: S) -> Result<SslStream<S>, SslError> {
let ssl = try!(Ssl::new(ctx));
SslStream::new_server_from(ssl, stream)
}
/// Returns a mutable reference to the underlying stream.
///
/// ## Warning
///
/// `read`ing or `write`ing directly to the underlying stream will most
/// likely desynchronize the SSL session.
#[deprecated="use get_mut instead"]
pub fn get_inner(&mut self) -> &mut S {
self.get_mut()
}
/// Returns a reference to the underlying stream.
pub fn get_ref(&self) -> &S {
&self.stream
}
/// Returns a mutable reference to the underlying stream.
///
/// ## Warning
///
/// It is inadvisable to read from or write to the underlying stream as it
/// will most likely desynchronize the SSL session.
pub fn get_mut(&mut self) -> &mut S {
&mut self.stream
}
fn in_retry_wrapper(&mut self, blk: |&Ssl| -> c_int)
-> Result<c_int, SslError> {
loop {
let ret = blk(&self.ssl);
let ret = blk(&*self.ssl);
if ret > 0 {
return Ok(ret);
}
match self.ssl.get_error(ret) {
ErrorWantRead => {
try_ssl!(self.flush());
let len = try_ssl!(self.stream.read(self.buf.as_mut_slice()));
LibSslError::ErrorWantRead => {
try_ssl_stream!(self.flush());
let len = try_ssl_stream!(self.stream.read(self.buf.as_mut_slice()));
self.ssl.get_rbio().write(self.buf.slice_to(len));
}
ErrorWantWrite => { try_ssl!(self.flush()) }
ErrorZeroReturn => return Err(SslSessionClosed),
ErrorSsl => return Err(SslError::get()),
LibSslError::ErrorWantWrite => { try_ssl_stream!(self.flush()) }
LibSslError::ErrorZeroReturn => return Err(SslSessionClosed),
LibSslError::ErrorSsl => return Err(SslError::get()),
_ => unreachable!()
}
}
@ -588,7 +505,7 @@ impl<S: Stream> SslStream<S> {
}
let meth = unsafe { ffi::SSL_COMP_get_name(ptr) };
let s = unsafe { string::raw::from_buf(meth as *const u8) };
let s = unsafe { String::from_raw_buf(meth as *const u8) };
Some(s)
}
@ -615,7 +532,7 @@ impl<S: Stream> Writer for SslStream<S> {
let mut start = 0;
while start < buf.len() {
let ret = self.in_retry_wrapper(|ssl| {
ssl.write(buf.slice_from(start))
ssl.write(buf.split_at(start).val1())
});
match ret {
Ok(len) => start += len as uint,
@ -631,3 +548,58 @@ impl<S: Stream> Writer for SslStream<S> {
self.stream.flush()
}
}
/// A utility type to help in cases where the use of SSL is decided at runtime.
pub enum MaybeSslStream<S> where S: Stream {
/// A connection using SSL
Ssl(SslStream<S>),
/// A connection not using SSL
Normal(S),
}
impl<S> Reader for MaybeSslStream<S> where S: Stream {
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
match *self {
MaybeSslStream::Ssl(ref mut s) => s.read(buf),
MaybeSslStream::Normal(ref mut s) => s.read(buf),
}
}
}
impl<S> Writer for MaybeSslStream<S> where S: Stream{
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
match *self {
MaybeSslStream::Ssl(ref mut s) => s.write(buf),
MaybeSslStream::Normal(ref mut s) => s.write(buf),
}
}
fn flush(&mut self) -> IoResult<()> {
match *self {
MaybeSslStream::Ssl(ref mut s) => s.flush(),
MaybeSslStream::Normal(ref mut s) => s.flush(),
}
}
}
impl<S> MaybeSslStream<S> where S: Stream {
/// Returns a reference to the underlying stream.
pub fn get_ref(&self) -> &S {
match *self {
MaybeSslStream::Ssl(ref s) => s.get_ref(),
MaybeSslStream::Normal(ref s) => s,
}
}
/// Returns a mutable reference to the underlying stream.
///
/// ## Warning
///
/// It is inadvisable to read from or write to the underlying stream.
pub fn get_mut(&mut self) -> &mut S {
match *self {
MaybeSslStream::Ssl(ref mut s) => s.get_mut(),
MaybeSslStream::Normal(ref mut s) => s,
}
}
}

View File

@ -1,8 +1,12 @@
use std::io::Writer;
use serialize::hex::FromHex;
use std::io::{Writer};
use std::io::net::tcp::TcpStream;
use std::str;
use ssl::{Sslv23, SslContext, SslStream, SslVerifyPeer, X509StoreContext};
use crypto::hash::HashType::{SHA256};
use ssl::SslMethod::Sslv23;
use ssl::{SslContext, SslStream};
use ssl::SslVerifyMode::SslVerifyPeer;
use x509::{X509StoreContext};
#[test]
fn test_new_ctx() {
@ -11,33 +15,33 @@ fn test_new_ctx() {
#[test]
fn test_new_sslstream() {
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
SslStream::new(&SslContext::new(Sslv23).unwrap(), stream).unwrap();
}
#[test]
fn test_verify_untrusted() {
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
ctx.set_verify(SslVerifyPeer, None);
match SslStream::new(&ctx, stream) {
Ok(_) => fail!("expected failure"),
Ok(_) => panic!("expected failure"),
Err(err) => println!("error {}", err)
}
}
#[test]
fn test_verify_trusted() {
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
ctx.set_verify(SslVerifyPeer, None);
match ctx.set_CA_file("test/cert.pem") {
match ctx.set_CA_file(&Path::new("test/cert.pem")) {
None => {}
Some(err) => fail!("Unexpected error {}", err)
Some(err) => panic!("Unexpected error {}", err)
}
match SslStream::new(&ctx, stream) {
Ok(_) => (),
Err(err) => fail!("Expected success, got {}", err)
Err(err) => panic!("Expected success, got {}", err)
}
}
@ -46,12 +50,12 @@ fn test_verify_untrusted_callback_override_ok() {
fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool {
true
}
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
ctx.set_verify(SslVerifyPeer, Some(callback));
match SslStream::new(&ctx, stream) {
Ok(_) => (),
Err(err) => fail!("Expected success, got {}", err)
Err(err) => panic!("Expected success, got {}", err)
}
}
@ -60,7 +64,7 @@ fn test_verify_untrusted_callback_override_bad() {
fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool {
false
}
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
ctx.set_verify(SslVerifyPeer, Some(callback));
assert!(SslStream::new(&ctx, stream).is_err());
@ -71,16 +75,16 @@ fn test_verify_trusted_callback_override_ok() {
fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool {
true
}
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
ctx.set_verify(SslVerifyPeer, Some(callback));
match ctx.set_CA_file("test/cert.pem") {
match ctx.set_CA_file(&Path::new("test/cert.pem")) {
None => {}
Some(err) => fail!("Unexpected error {}", err)
Some(err) => panic!("Unexpected error {}", err)
}
match SslStream::new(&ctx, stream) {
Ok(_) => (),
Err(err) => fail!("Expected success, got {}", err)
Err(err) => panic!("Expected success, got {}", err)
}
}
@ -89,12 +93,12 @@ fn test_verify_trusted_callback_override_bad() {
fn callback(_preverify_ok: bool, _x509_ctx: &X509StoreContext) -> bool {
false
}
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
ctx.set_verify(SslVerifyPeer, Some(callback));
match ctx.set_CA_file("test/cert.pem") {
match ctx.set_CA_file(&Path::new("test/cert.pem")) {
None => {}
Some(err) => fail!("Unexpected error {}", err)
Some(err) => panic!("Unexpected error {}", err)
}
assert!(SslStream::new(&ctx, stream).is_err());
}
@ -105,7 +109,7 @@ fn test_verify_callback_load_certs() {
assert!(x509_ctx.get_current_cert().is_some());
true
}
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
ctx.set_verify(SslVerifyPeer, Some(callback));
assert!(SslStream::new(&ctx, stream).is_ok());
@ -117,12 +121,12 @@ fn test_verify_trusted_get_error_ok() {
assert!(x509_ctx.get_error().is_none());
true
}
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
ctx.set_verify(SslVerifyPeer, Some(callback));
match ctx.set_CA_file("test/cert.pem") {
match ctx.set_CA_file(&Path::new("test/cert.pem")) {
None => {}
Some(err) => fail!("Unexpected error {}", err)
Some(err) => panic!("Unexpected error {}", err)
}
assert!(SslStream::new(&ctx, stream).is_ok());
}
@ -133,15 +137,46 @@ fn test_verify_trusted_get_error_err() {
assert!(x509_ctx.get_error().is_some());
false
}
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
ctx.set_verify(SslVerifyPeer, Some(callback));
assert!(SslStream::new(&ctx, stream).is_err());
}
#[test]
fn test_verify_callback_data() {
fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext, node_id: &Vec<u8>) -> bool {
let cert = x509_ctx.get_current_cert();
match cert {
None => false,
Some(cert) => {
let fingerprint = cert.fingerprint(SHA256).unwrap();
fingerprint.as_slice() == node_id.as_slice()
}
}
}
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut ctx = SslContext::new(Sslv23).unwrap();
// Node id was generated as SHA256 hash of certificate "test/cert.pem"
// in DER format.
// Command: openssl x509 -in test/cert.pem -outform DER | openssl dgst -sha256
// Please update if "test/cert.pem" will ever change
let node_hash_str = "46e3f1a6d17a41ce70d0c66ef51cee2ab4ba67cac8940e23f10c1f944b49fb5c";
let node_id = node_hash_str.from_hex().unwrap();
ctx.set_verify_with_data(SslVerifyPeer, callback, node_id);
ctx.set_verify_depth(1);
match SslStream::new(&ctx, stream) {
Ok(_) => (),
Err(err) => panic!("Expected success, got {}", err)
}
}
#[test]
fn test_write() {
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), stream).unwrap();
stream.write("hello".as_bytes()).unwrap();
stream.flush().unwrap();
@ -151,10 +186,21 @@ fn test_write() {
#[test]
fn test_read() {
let stream = TcpStream::connect("127.0.0.1", 15418).unwrap();
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), stream).unwrap();
stream.write("GET /\r\n\r\n".as_bytes()).unwrap();
stream.flush().unwrap();
let buf = stream.read_to_end().ok().expect("read error");
print!("{}", str::from_utf8(buf.as_slice()));
stream.read_to_end().ok().expect("read error");
}
#[test]
fn test_clone() {
let stream = TcpStream::connect("127.0.0.1:15418").unwrap();
let mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), stream).unwrap();
let mut stream2 = stream.clone();
spawn(proc() {
stream2.write("GET /\r\n\r\n".as_bytes()).unwrap();
stream2.flush().unwrap();
});
stream.read_to_end().ok().expect("read error");
}

514
src/x509/mod.rs Normal file
View File

@ -0,0 +1,514 @@
use libc::{c_int, c_long, c_uint};
use std::mem;
use std::num::SignedInt;
use std::ptr;
use asn1::{Asn1Time};
use bio::{MemBio};
use crypto::hash::{HashType, evpmd};
use crypto::pkey::{PKey};
use crypto::rand::rand_bytes;
use ffi;
use ssl::error::{SslError, StreamError};
#[cfg(test)]
mod tests;
#[repr(i32)]
pub enum X509FileType {
PEM = ffi::X509_FILETYPE_PEM,
ASN1 = ffi::X509_FILETYPE_ASN1,
Default = ffi::X509_FILETYPE_DEFAULT
}
pub struct X509StoreContext {
ctx: *mut ffi::X509_STORE_CTX
}
impl X509StoreContext {
pub fn new(ctx: *mut ffi::X509_STORE_CTX) -> X509StoreContext {
X509StoreContext {
ctx: ctx
}
}
pub fn get_error(&self) -> Option<X509ValidationError> {
let err = unsafe { ffi::X509_STORE_CTX_get_error(self.ctx) };
X509ValidationError::from_raw(err)
}
pub fn get_current_cert<'a>(&'a self) -> Option<X509<'a>> {
let ptr = unsafe { ffi::X509_STORE_CTX_get_current_cert(self.ctx) };
if ptr.is_null() {
None
} else {
Some(X509 { ctx: Some(self), handle: ptr, owned: false })
}
}
}
#[doc(hidden)]
trait AsStr<'a> {
fn as_str(&self) -> &'a str;
}
#[deriving(Clone)]
pub enum KeyUsage {
DigitalSignature,
NonRepudiation,
KeyEncipherment,
DataEncipherment,
KeyAgreement,
KeyCertSign,
CRLSign,
EncipherOnly,
DecipherOnly
}
impl AsStr<'static> for KeyUsage {
fn as_str(&self) -> &'static str {
match self {
&KeyUsage::DigitalSignature => "digitalSignature",
&KeyUsage::NonRepudiation => "nonRepudiation",
&KeyUsage::KeyEncipherment => "keyEncipherment",
&KeyUsage::DataEncipherment => "dataEncipherment",
&KeyUsage::KeyAgreement => "keyAgreement",
&KeyUsage::KeyCertSign => "keyCertSign",
&KeyUsage::CRLSign => "cRLSign",
&KeyUsage::EncipherOnly => "encipherOnly",
&KeyUsage::DecipherOnly => "decipherOnly"
}
}
}
#[deriving(Clone)]
pub enum ExtKeyUsage {
ServerAuth,
ClientAuth,
CodeSigning,
EmailProtection,
TimeStamping,
MsCodeInd,
MsCodeCom,
MsCtlSign,
MsSgc,
MsEfs,
NsSgc
}
impl AsStr<'static> for ExtKeyUsage {
fn as_str(&self) -> &'static str {
match self {
&ExtKeyUsage::ServerAuth => "serverAuth",
&ExtKeyUsage::ClientAuth => "clientAuth",
&ExtKeyUsage::CodeSigning => "codeSigning",
&ExtKeyUsage::EmailProtection => "emailProtection",
&ExtKeyUsage::TimeStamping => "timeStamping",
&ExtKeyUsage::MsCodeInd => "msCodeInd",
&ExtKeyUsage::MsCodeCom => "msCodeCom",
&ExtKeyUsage::MsCtlSign => "msCTLSign",
&ExtKeyUsage::MsSgc => "msSGC",
&ExtKeyUsage::MsEfs => "msEFS",
&ExtKeyUsage::NsSgc =>"nsSGC"
}
}
}
// FIXME: a dirty hack as there is no way to
// implement ToString for Vec as both are defined
// in another crate
#[doc(hidden)]
trait ToStr {
fn to_str(&self) -> String;
}
impl<'a, T: AsStr<'a>> ToStr for Vec<T> {
fn to_str(&self) -> String {
self.iter().enumerate().fold(String::new(), |mut acc, (idx, v)| {
if idx > 0 { acc.push(',') };
acc.push_str(v.as_str());
acc
})
}
}
#[allow(non_snake_case)]
/// Generator of private key/certificate pairs
///
/// # Example
///
/// ```
/// use std::io::{File, Open, Write};
/// # use std::io::fs;
///
/// use openssl::crypto::hash::SHA256;
/// use openssl::x509::{DigitalSignature, X509Generator};
///
/// let gen = X509Generator::new()
/// .set_bitlength(2048)
/// .set_valid_period(365*2)
/// .set_CN("SuperMegaCorp Inc.")
/// .set_sign_hash(SHA256)
/// .set_usage(&[DigitalSignature]);
///
/// let (cert, pkey) = gen.generate().unwrap();
///
/// let cert_path = Path::new("doc_cert.pem");
/// let mut file = File::open_mode(&cert_path, Open, Write).unwrap();
/// assert!(cert.write_pem(&mut file).is_ok());
/// # let _ = fs::unlink(&cert_path);
///
/// let pkey_path = Path::new("doc_key.pem");
/// let mut file = File::open_mode(&pkey_path, Open, Write).unwrap();
/// assert!(pkey.write_pem(&mut file).is_ok());
/// # let _ = fs::unlink(&pkey_path);
/// ```
pub struct X509Generator {
bits: uint,
days: uint,
CN: String,
key_usage: Vec<KeyUsage>,
ext_key_usage: Vec<ExtKeyUsage>,
hash_type: HashType,
}
impl X509Generator {
/// Creates a new generator with the following defaults:
///
/// bit length: 1024
///
/// validity period: 365 days
///
/// CN: "rust-openssl"
///
/// hash: SHA1
pub fn new() -> X509Generator {
X509Generator {
bits: 1024,
days: 365,
CN: "rust-openssl".to_string(),
key_usage: Vec::new(),
ext_key_usage: Vec::new(),
hash_type: HashType::SHA1
}
}
/// Sets desired bit length
pub fn set_bitlength(mut self, bits: uint) -> X509Generator {
self.bits = bits;
self
}
/// Sets certificate validity period in days since today
pub fn set_valid_period(mut self, days: uint) -> X509Generator {
self.days = days;
self
}
#[allow(non_snake_case)]
/// Sets Common Name of certificate
pub fn set_CN(mut self, CN: &str) -> X509Generator {
self.CN = CN.to_string();
self
}
/// Sets what for certificate could be used
pub fn set_usage(mut self, purposes: &[KeyUsage]) -> X509Generator {
self.key_usage = purposes.to_vec();
self
}
/// Sets allowed extended usage of certificate
pub fn set_ext_usage(mut self, purposes: &[ExtKeyUsage]) -> X509Generator {
self.ext_key_usage = purposes.to_vec();
self
}
pub fn set_sign_hash(mut self, hash_type: HashType) -> X509Generator {
self.hash_type = hash_type;
self
}
fn add_extension(x509: *mut ffi::X509, extension: c_int, value: &str) -> Result<(), SslError> {
unsafe {
let mut ctx: ffi::X509V3_CTX = mem::zeroed();
ffi::X509V3_set_ctx(&mut ctx, x509, x509,
ptr::null_mut(), ptr::null_mut(), 0);
let ext = value.with_c_str(|value|
ffi::X509V3_EXT_conf_nid(ptr::null_mut(),
mem::transmute(&ctx),
extension,
mem::transmute(value)));
let mut success = false;
if ext != ptr::null_mut() {
success = ffi::X509_add_ext(x509, ext, -1) != 0;
ffi::X509_EXTENSION_free(ext);
}
lift_ssl_if!(!success)
}
}
fn add_name(name: *mut ffi::X509_NAME, key: &str, value: &str) -> Result<(), SslError> {
let value_len = value.len() as c_int;
lift_ssl!(key.with_c_str(|key| {
value.with_c_str(|value| unsafe {
ffi::X509_NAME_add_entry_by_txt(name, key, ffi::MBSTRING_UTF8,
value, value_len, -1, 0)
})
}))
}
fn random_serial() -> c_long {
let len = mem::size_of::<c_long>();
let bytes = rand_bytes(len);
let mut res = 0;
for b in bytes.iter() {
res = res << 8;
res |= (*b as c_long) & 0xff;
}
// While OpenSSL is actually OK to have negative serials
// other libraries (for example, Go crypto) can drop
// such certificates as invalid
res.abs()
}
/// Generates a private key and a signed certificate and returns them
pub fn generate<'a>(&self) -> Result<(X509<'a>, PKey), SslError> {
ffi::init();
let mut p_key = PKey::new();
p_key.gen(self.bits);
unsafe {
let x509 = ffi::X509_new();
try_ssl_null!(x509);
let x509 = X509 { handle: x509, ctx: None, owned: true};
try_ssl!(ffi::X509_set_version(x509.handle, 2));
try_ssl!(ffi::ASN1_INTEGER_set(ffi::X509_get_serialNumber(x509.handle), X509Generator::random_serial()));
let not_before = try!(Asn1Time::days_from_now(0));
let not_after = try!(Asn1Time::days_from_now(self.days));
try_ssl!(ffi::X509_set_notBefore(x509.handle, mem::transmute(not_before.get_handle())));
// If prev line succeded - ownership should go to cert
mem::forget(not_before);
try_ssl!(ffi::X509_set_notAfter(x509.handle, mem::transmute(not_after.get_handle())));
// If prev line succeded - ownership should go to cert
mem::forget(not_after);
try_ssl!(ffi::X509_set_pubkey(x509.handle, p_key.get_handle()));
let name = ffi::X509_get_subject_name(x509.handle);
try_ssl_null!(name);
try!(X509Generator::add_name(name, "CN", self.CN.as_slice()));
ffi::X509_set_issuer_name(x509.handle, name);
if self.key_usage.len() > 0 {
try!(X509Generator::add_extension(x509.handle, ffi::NID_key_usage,
self.key_usage.to_str().as_slice()));
}
if self.ext_key_usage.len() > 0 {
try!(X509Generator::add_extension(x509.handle, ffi::NID_ext_key_usage,
self.ext_key_usage.to_str().as_slice()));
}
let (hash_fn, _) = evpmd(self.hash_type);
try_ssl!(ffi::X509_sign(x509.handle, p_key.get_handle(), hash_fn));
Ok((x509, p_key))
}
}
}
#[allow(dead_code)]
/// A public key certificate
pub struct X509<'ctx> {
ctx: Option<&'ctx X509StoreContext>,
handle: *mut ffi::X509,
owned: bool
}
impl<'ctx> X509<'ctx> {
/// Creates new from handle with desired ownership.
pub fn new(handle: *mut ffi::X509, owned: bool) -> X509<'ctx> {
X509 {
ctx: None,
handle: handle,
owned: owned,
}
}
/// Creates a new certificate from context. Doesn't take ownership
/// of handle.
pub fn new_in_ctx(handle: *mut ffi::X509, ctx: &'ctx X509StoreContext) -> X509<'ctx> {
X509 {
ctx: Some(ctx),
handle: handle,
owned: false
}
}
/// Reads certificate from PEM, takes ownership of handle
pub fn from_pem(reader: &mut Reader) -> Result<X509<'ctx>, SslError> {
let mut mem_bio = try!(MemBio::new());
let buf = try!(reader.read_to_end().map_err(StreamError));
try!(mem_bio.write(buf.as_slice()).map_err(StreamError));
unsafe {
let handle = try_ssl_null!(ffi::PEM_read_bio_X509(mem_bio.get_handle(),
ptr::null_mut(),
None, ptr::null_mut()));
Ok(X509::new(handle, true))
}
}
pub fn subject_name<'a>(&'a self) -> X509Name<'a> {
let name = unsafe { ffi::X509_get_subject_name(self.handle) };
X509Name { x509: self, name: name }
}
/// Returns certificate fingerprint calculated using provided hash
pub fn fingerprint(&self, hash_type: HashType) -> Option<Vec<u8>> {
let (evp, len) = evpmd(hash_type);
let v: Vec<u8> = Vec::from_elem(len, 0);
let act_len: c_uint = 0;
let res = unsafe {
ffi::X509_digest(self.handle, evp, mem::transmute(v.as_ptr()),
mem::transmute(&act_len))
};
match res {
0 => None,
_ => {
let act_len = act_len as uint;
match len.cmp(&act_len) {
Greater => None,
Equal => Some(v),
Less => panic!("Fingerprint buffer was corrupted!")
}
}
}
}
/// Writes certificate as PEM
pub fn write_pem(&self, writer: &mut Writer) -> Result<(), SslError> {
let mut mem_bio = try!(MemBio::new());
unsafe {
try_ssl!(ffi::PEM_write_bio_X509(mem_bio.get_handle(),
self.handle));
}
let buf = try!(mem_bio.read_to_end().map_err(StreamError));
writer.write(buf.as_slice()).map_err(StreamError)
}
}
#[unsafe_destructor]
impl<'ctx> Drop for X509<'ctx> {
fn drop(&mut self) {
if self.owned {
unsafe { ffi::X509_free(self.handle) };
}
}
}
#[allow(dead_code)]
pub struct X509Name<'x> {
x509: &'x X509<'x>,
name: *mut ffi::X509_NAME
}
macro_rules! make_validation_error(
($ok_val:ident, $($name:ident = $val:ident,)+) => (
pub enum X509ValidationError {
$($name,)+
X509UnknownError(c_int)
}
impl X509ValidationError {
#[doc(hidden)]
pub fn from_raw(err: c_int) -> Option<X509ValidationError> {
match err {
ffi::$ok_val => None,
$(ffi::$val => Some(X509ValidationError::$name),)+
err => Some(X509ValidationError::X509UnknownError(err))
}
}
}
)
)
make_validation_error!(X509_V_OK,
X509UnableToGetIssuerCert = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
X509UnableToGetCrl = X509_V_ERR_UNABLE_TO_GET_CRL,
X509UnableToDecryptCertSignature = X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
X509UnableToDecryptCrlSignature = X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
X509UnableToDecodeIssuerPublicKey = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
X509CertSignatureFailure = X509_V_ERR_CERT_SIGNATURE_FAILURE,
X509CrlSignatureFailure = X509_V_ERR_CRL_SIGNATURE_FAILURE,
X509CertNotYetValid = X509_V_ERR_CERT_NOT_YET_VALID,
X509CertHasExpired = X509_V_ERR_CERT_HAS_EXPIRED,
X509CrlNotYetValid = X509_V_ERR_CRL_NOT_YET_VALID,
X509CrlHasExpired = X509_V_ERR_CRL_HAS_EXPIRED,
X509ErrorInCertNotBeforeField = X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
X509ErrorInCertNotAfterField = X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
X509ErrorInCrlLastUpdateField = X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
X509ErrorInCrlNextUpdateField = X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
X509OutOfMem = X509_V_ERR_OUT_OF_MEM,
X509DepthZeroSelfSignedCert = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
X509SelfSignedCertInChain = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
X509UnableToGetIssuerCertLocally = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
X509UnableToVerifyLeafSignature = X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
X509CertChainTooLong = X509_V_ERR_CERT_CHAIN_TOO_LONG,
X509CertRevoked = X509_V_ERR_CERT_REVOKED,
X509InvalidCA = X509_V_ERR_INVALID_CA,
X509PathLengthExceeded = X509_V_ERR_PATH_LENGTH_EXCEEDED,
X509InvalidPurpose = X509_V_ERR_INVALID_PURPOSE,
X509CertUntrusted = X509_V_ERR_CERT_UNTRUSTED,
X509CertRejected = X509_V_ERR_CERT_REJECTED,
X509SubjectIssuerMismatch = X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
X509AkidSkidMismatch = X509_V_ERR_AKID_SKID_MISMATCH,
X509AkidIssuerSerialMismatch = X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
X509KeyusageNoCertsign = X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
X509UnableToGetCrlIssuer = X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER,
X509UnhandledCriticalExtension = X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION,
X509KeyusageNoCrlSign = X509_V_ERR_KEYUSAGE_NO_CRL_SIGN,
X509UnhandledCriticalCrlExtension = X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION,
X509InvalidNonCA = X509_V_ERR_INVALID_NON_CA,
X509ProxyPathLengthExceeded = X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED,
X509KeyusageNoDigitalSignature = X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE,
X509ProxyCertificatesNotAllowed = X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED,
X509InvalidExtension = X509_V_ERR_INVALID_EXTENSION,
X509InavlidPolicyExtension = X509_V_ERR_INVALID_POLICY_EXTENSION,
X509NoExplicitPolicy = X509_V_ERR_NO_EXPLICIT_POLICY,
X509DifferentCrlScope = X509_V_ERR_DIFFERENT_CRL_SCOPE,
X509UnsupportedExtensionFeature = X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE,
X509UnnestedResource = X509_V_ERR_UNNESTED_RESOURCE,
X509PermittedVolation = X509_V_ERR_PERMITTED_VIOLATION,
X509ExcludedViolation = X509_V_ERR_EXCLUDED_VIOLATION,
X509SubtreeMinmax = X509_V_ERR_SUBTREE_MINMAX,
X509UnsupportedConstraintType = X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE,
X509UnsupportedConstraintSyntax = X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX,
X509UnsupportedNameSyntax = X509_V_ERR_UNSUPPORTED_NAME_SYNTAX,
X509CrlPathValidationError= X509_V_ERR_CRL_PATH_VALIDATION_ERROR,
X509ApplicationVerification = X509_V_ERR_APPLICATION_VERIFICATION,
)
#[test]
fn test_negative_serial() {
// I guess that's enough to get a random negative number
for _ in range(0u, 1000) {
assert!(X509Generator::random_serial() > 0, "All serials should be positive");
}
}

51
src/x509/tests.rs Normal file
View File

@ -0,0 +1,51 @@
use serialize::hex::FromHex;
use std::io::{File, Open, Read};
use std::io::util::NullWriter;
use crypto::hash::HashType::{SHA256};
use x509::{X509, X509Generator};
use x509::KeyUsage::{DigitalSignature, KeyEncipherment};
use x509::ExtKeyUsage::{ClientAuth, ServerAuth};
#[test]
fn test_cert_gen() {
let gen = X509Generator::new()
.set_bitlength(2048)
.set_valid_period(365*2)
.set_CN("test_me")
.set_sign_hash(SHA256)
.set_usage(&[DigitalSignature, KeyEncipherment])
.set_ext_usage(&[ClientAuth, ServerAuth]);
let res = gen.generate();
assert!(res.is_ok());
let (cert, pkey) = res.unwrap();
let mut writer = NullWriter;
assert!(cert.write_pem(&mut writer).is_ok());
assert!(pkey.write_pem(&mut writer).is_ok());
// FIXME: check data in result to be correct, needs implementation
// of X509 getters
}
#[test]
fn test_cert_loading() {
let cert_path = Path::new("test/cert.pem");
let mut file = File::open_mode(&cert_path, Open, Read)
.ok()
.expect("Failed to open `test/cert.pem`");
let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM");
let fingerprint = cert.fingerprint(SHA256).unwrap();
// Hash was generated as SHA256 hash of certificate "test/cert.pem"
// in DER format.
// Command: openssl x509 -in test/cert.pem -outform DER | openssl dgst -sha256
// Please update if "test/cert.pem" will ever change
let hash_str = "46e3f1a6d17a41ce70d0c66ef51cee2ab4ba67cac8940e23f10c1f944b49fb5c";
let hash_vec = hash_str.from_hex().unwrap();
assert_eq!(fingerprint.as_slice(), hash_vec.as_slice());
}

View File

@ -1,7 +1,7 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAJUaA2QC829wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
MIIDXTCCAkWgAwIBAgIJAMWJMG/NYWQyMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTMxMDIyMDUzODU5WhcNMTQxMDIyMDUzODU5WjBF
aWRnaXRzIFB0eSBMdGQwHhcNMTQxMTIwMDU0MjIxWhcNMTcxMTE5MDU0MjIxWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA2E9uYoLpX4eMGz6+l+1ZdD11Y1PQjNjqSA7/nq0Q6gogPLds99a+Ca7B
@ -11,11 +11,11 @@ ImKdwDBhuqzdBpBODQW/VCn0KG54CvWdwT0iqg7CvLuXQGyxM8K17SAiBtn6N38S
0jeL4D9IrBfi9PCYGpAn3jKr4oBEJVRCNvvni3Q9ikJ+GEF5nvLLVM+I98/o0vCu
Y2o7+1LiwViGONJ1BfRgdFWMjz0BfQIDAQABo1AwTjAdBgNVHQ4EFgQU509vewJT
k6qdvjT8e52gTE4+V9EwHwYDVR0jBBgwFoAU509vewJTk6qdvjT8e52gTE4+V9Ew
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAb0Sz/ixFc+pShXur1peh
J0n7sPxX29WyTN8IZ+Sl/Ks0vG6bmtO1kAudVigStdQKeBPco+c2/+BF1sL79pXf
Ao1hOWCkXVu6E/Nxl5rZlGNjkxwurKLNV5mZ6nXYkseYZH9A2ighGYQHj4YqspN2
2+/K1DLNBZCsZF6XozhUiDysUGN/xVh6TE0gsPvL1A27Xe4mQ8vd8Mz7av4dek//
V9F+cXnVBTAqLPpEbAX+0+k1QZtOVnDGovshlRFZ8d17f0pJZ66n9k6TPriQNWV/
RXjIOBLbXtPFL5w6MetkEBE+DsRckLT/50cf+f/y8CHv9v9iobu4LpTjx/8l0GuQ
hg==
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAnLxMvrhgQrdKUbd+RSl3
i6tEfexDepO5QOMIL/m7w/OAkXddu7Y/IuLSgAztp7cu8tQeeBTqbO9T9XKRd1ZD
hMN3QPZwX1ftREPd1mhe3LQgJ/Q3AbrtlW+0XcANBCTiYi19X193sU2a3Z2nzd5v
vSvrP1W3Yxzvu7Jwvhgl20BQ0fREpPVfZTrJRhg8/jdLA8TmKZA7012LKh59BCEX
dXgXc3KYb+Fn8usj79P1nP1YhYmX0Lp0IuC0i1z+FoP/NxNMe+ebXPjM9KlkvQK1
Vy9sIxV9MAlXpGsMvBJAte94JRikYkQTPjMT1DGQQYGpHIYb3a8jcWvmZPEEaqhB
Ag==
-----END CERTIFICATE-----