From ec65b0c67b452539fded5e06cbb6ce1d165074e0 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 7 Feb 2015 21:28:54 -0800 Subject: Move docs to this repo and auto build --- .gitignore | 1 - .travis.yml | 17 +- .travis/build_docs.sh | 11 + .travis/update_docs.sh | 16 ++ Cargo.toml | 23 -- README.md | 4 +- openssl-sys/Cargo.toml | 1 + openssl-sys/src/lib.rs | 1 + openssl/Cargo.toml | 23 ++ openssl/src/asn1/mod.rs | 49 ++++ openssl/src/bio/mod.rs | 105 +++++++ openssl/src/bn/mod.rs | 604 ++++++++++++++++++++++++++++++++++++++++ openssl/src/crypto/hash.rs | 333 ++++++++++++++++++++++ openssl/src/crypto/hmac.rs | 474 +++++++++++++++++++++++++++++++ openssl/src/crypto/memcmp.rs | 39 +++ openssl/src/crypto/mod.rs | 24 ++ openssl/src/crypto/pkcs5.rs | 119 ++++++++ openssl/src/crypto/pkey.rs | 423 ++++++++++++++++++++++++++++ openssl/src/crypto/rand.rs | 27 ++ openssl/src/crypto/symm.rs | 314 +++++++++++++++++++++ openssl/src/lib.rs | 20 ++ openssl/src/macros.rs | 59 ++++ openssl/src/ssl/error.rs | 122 ++++++++ openssl/src/ssl/mod.rs | 643 +++++++++++++++++++++++++++++++++++++++++++ openssl/src/ssl/tests.rs | 207 ++++++++++++++ openssl/src/x509/mod.rs | 525 +++++++++++++++++++++++++++++++++++ openssl/src/x509/tests.rs | 51 ++++ openssl/test/cert.pem | 21 ++ openssl/test/key.pem | 28 ++ src/asn1/mod.rs | 49 ---- src/bio/mod.rs | 105 ------- src/bn/mod.rs | 604 ---------------------------------------- src/crypto/hash.rs | 333 ---------------------- src/crypto/hmac.rs | 474 ------------------------------- src/crypto/memcmp.rs | 39 --- src/crypto/mod.rs | 24 -- src/crypto/pkcs5.rs | 119 -------- src/crypto/pkey.rs | 423 ---------------------------- src/crypto/rand.rs | 27 -- src/crypto/symm.rs | 314 --------------------- src/lib.rs | 20 -- src/macros.rs | 59 ---- src/ssl/error.rs | 122 -------- src/ssl/mod.rs | 643 ------------------------------------------- src/ssl/tests.rs | 207 -------------- src/x509/mod.rs | 525 ----------------------------------- src/x509/tests.rs | 51 ---- test/cert.pem | 21 -- test/key.pem | 28 -- 49 files changed, 4252 insertions(+), 4219 deletions(-) create mode 100755 .travis/build_docs.sh create mode 100755 .travis/update_docs.sh delete mode 100644 Cargo.toml create mode 100644 openssl/Cargo.toml create mode 100644 openssl/src/asn1/mod.rs create mode 100644 openssl/src/bio/mod.rs create mode 100644 openssl/src/bn/mod.rs create mode 100644 openssl/src/crypto/hash.rs create mode 100644 openssl/src/crypto/hmac.rs create mode 100644 openssl/src/crypto/memcmp.rs create mode 100644 openssl/src/crypto/mod.rs create mode 100644 openssl/src/crypto/pkcs5.rs create mode 100644 openssl/src/crypto/pkey.rs create mode 100644 openssl/src/crypto/rand.rs create mode 100644 openssl/src/crypto/symm.rs create mode 100644 openssl/src/lib.rs create mode 100644 openssl/src/macros.rs create mode 100644 openssl/src/ssl/error.rs create mode 100644 openssl/src/ssl/mod.rs create mode 100644 openssl/src/ssl/tests.rs create mode 100644 openssl/src/x509/mod.rs create mode 100644 openssl/src/x509/tests.rs create mode 100644 openssl/test/cert.pem create mode 100644 openssl/test/key.pem delete mode 100644 src/asn1/mod.rs delete mode 100644 src/bio/mod.rs delete mode 100644 src/bn/mod.rs delete mode 100644 src/crypto/hash.rs delete mode 100644 src/crypto/hmac.rs delete mode 100644 src/crypto/memcmp.rs delete mode 100644 src/crypto/mod.rs delete mode 100644 src/crypto/pkcs5.rs delete mode 100644 src/crypto/pkey.rs delete mode 100644 src/crypto/rand.rs delete mode 100644 src/crypto/symm.rs delete mode 100644 src/lib.rs delete mode 100644 src/macros.rs delete mode 100644 src/ssl/error.rs delete mode 100644 src/ssl/mod.rs delete mode 100644 src/ssl/tests.rs delete mode 100644 src/x509/mod.rs delete mode 100644 src/x509/tests.rs delete mode 100644 test/cert.pem delete mode 100644 test/key.pem diff --git a/.gitignore b/.gitignore index 52d47ac9..2c96eb1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -/doc/ target/ Cargo.lock diff --git a/.travis.yml b/.travis.yml index fe30a5f2..d6321552 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,17 @@ language: rust env: matrix: - - FEATURES="" - - FEATURES="tlsv1_1 tlsv1_2 aes_xts" + - FEATURES="" + - FEATURES="tlsv1_1 tlsv1_2 aes_xts" + global: + secure: J4i75AV4KMrU/UQrLIzzIh35Xix40Ki0uWjm8j05oxlXVl5aPU2zB30AemDne2QXYzkN4kRG/iRnNORE/8D0lF7YipQNSNxgfiBVoOEfj/NSogvI2BftYX9vlLZJUvt+s/nbE3xa/Pyge1IPv7itDYGO7SMe8RTSqitgqyfE2Eg= os: - - osx - - linux +- osx +- linux before_script: - - openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem >/dev/null 2>&1 & +- openssl s_server -accept 15418 -www -cert test/cert.pem -key test/key.pem >/dev/null 2>&1 & script: - - cargo test --features "$FEATURES" +- (cd openssl && cargo test --features "$FEATURES") +- ./.travis/build_docs.sh +after_success: +- test $TRAVIS_PULL_REQUEST == "false" && test $TRAVIS_BRANCH == "master" && ./.travis/update_docs.sh diff --git a/.travis/build_docs.sh b/.travis/build_docs.sh new file mode 100755 index 00000000..4264051a --- /dev/null +++ b/.travis/build_docs.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +mkdir doc + +for crate in $(echo openssl-sys openssl); do + mkdir -p $crate/target + ln -s -t $crate/target ../../doc + (cd $crate && cargo doc --no-deps) +done diff --git a/.travis/update_docs.sh b/.travis/update_docs.sh new file mode 100755 index 00000000..355f75f8 --- /dev/null +++ b/.travis/update_docs.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -o errexit -o nounset + +git clone --branch gh-pages "https://$GH_TOKEN@github.com/${TRAVIS_REPO_SLUG}.git" deploy_docs +cd deploy_docs + +git config user.name "Steven Fackler" +git config user.email "sfackler@gmail.com" + +rm -rf doc +mv ../doc . + +git add -A . +git commit -m "rebuild pages at ${TRAVIS_COMMIT}" +git push diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 75f8ba59..00000000 --- a/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "openssl" -version = "0.3.1" -authors = ["Steven Fackler "] -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 = ["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.3.1" - -[dev-dependencies] -rustc-serialize = "0.2" diff --git a/README.md b/README.md index 58a2285b..cecf2c9d 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ 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](https://sfackler.github.io/doc/openssl). +See the [rustdoc output](https://sfackler.github.io/rust-openssl/doc/openssl). Building -------- -rust-openssl needs to link against the OpenSSL devleopment libraries on your system. It's very easy to get them on Linux. +rust-openssl needs to link against the OpenSSL devleopment libraries on your system. It's very easy to get them on Linux. For some reason, the OpenSSL distribution for Windows is structured differently, so it's a little more involved, but it *is* possible to build rust-openssl successfully on Windows. ###Linux diff --git a/openssl-sys/Cargo.toml b/openssl-sys/Cargo.toml index 5e7d587e..8f7e2bce 100644 --- a/openssl-sys/Cargo.toml +++ b/openssl-sys/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Alex Crichton ", license = "MIT" description = "FFI bindings to OpenSSL" repository = "https://github.com/sfackler/rust-openssl" +documentation = "https://sfackler.github.io/rust-openssl/doc/openssl-sys" links = "openssl" build = "build.rs" diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index fd16792b..70526fd5 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1,6 +1,7 @@ #![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)] #![allow(dead_code)] #![feature(core, io, libc, path, std_misc, env)] +#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/openssl-sys")] extern crate libc; diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml new file mode 100644 index 00000000..f0b7a39b --- /dev/null +++ b/openssl/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "openssl" +version = "0.3.1" +authors = ["Steven Fackler "] +license = "Apache-2.0" +description = "OpenSSL bindings" +repository = "https://github.com/sfackler/rust-openssl" +documentation = "https://sfackler.github.io/rust-openssl/doc/openssl" +readme = "README.md" +keywords = ["crypto", "tls", "ssl"] + +[features] +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.3.1" + +[dev-dependencies] +rustc-serialize = "0.2" diff --git a/openssl/src/asn1/mod.rs b/openssl/src/asn1/mod.rs new file mode 100644 index 00000000..c250096f --- /dev/null +++ b/openssl/src/asn1/mod.rs @@ -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 { + 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: u32) -> Result { + 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) }; + } + } +} diff --git a/openssl/src/bio/mod.rs b/openssl/src/bio/mod.rs new file mode 100644 index 00000000..2f12f906 --- /dev/null +++ b/openssl/src/bio/mod.rs @@ -0,0 +1,105 @@ +use libc::{c_void, c_int}; +use std::old_io::{EndOfFile, IoResult, IoError, OtherIoError}; +use std::old_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 { + 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 { + 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 usize) + } + } +} + +impl Writer for MemBio { + fn write_all(&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 usize { + Err(IoError { + kind: OtherIoError, + desc: "MemBio write error", + detail: Some(format!("{:?}", SslError::get())) + }) + } else { + Ok(()) + } + } +} diff --git a/openssl/src/bn/mod.rs b/openssl/src/bn/mod.rs new file mode 100644 index 00000000..3a5f231e --- /dev/null +++ b/openssl/src/bn/mod.rs @@ -0,0 +1,604 @@ +use libc::{c_int, c_ulong, c_void}; +use std::ffi::{CString, c_str_to_bytes}; +use std::cmp::Ordering; +use std::{fmt, ptr}; + +use ffi; +use ssl::error::SslError; + +pub struct BigNum(*mut ffi::BIGNUM); + +#[derive(Copy)] +#[repr(C)] +pub enum RNGProperty { + MsbMaybeZero = -1, + MsbOne = 0, + TwoMsbOne = 1, +} + +macro_rules! with_ctx( + ($name:ident, $action:block) => ({ + let $name = ffi::BN_CTX_new(); + if ($name).is_null() { + Err(SslError::get()) + } else { + let r = $action; + ffi::BN_CTX_free($name); + r + } + }); +); + +macro_rules! with_bn( + ($name:ident, $action:block) => ({ + let tmp = BigNum::new(); + match tmp { + Ok($name) => { + if $action { + Ok($name) + } else { + Err(SslError::get()) + } + }, + Err(err) => Err(err), + } + }); +); + +macro_rules! with_bn_in_ctx( + ($name:ident, $ctx_name:ident, $action:block) => ({ + let tmp = BigNum::new(); + match tmp { + Ok($name) => { + let $ctx_name = ffi::BN_CTX_new(); + if ($ctx_name).is_null() { + Err(SslError::get()) + } else { + let r = + if $action { + Ok($name) + } else { + Err(SslError::get()) + }; + ffi::BN_CTX_free($ctx_name); + r + } + }, + Err(err) => Err(err), + } + }); +); + +impl BigNum { + pub fn new() -> Result { + unsafe { + ffi::init(); + + let v = try_ssl_null!(ffi::BN_new()); + Ok(BigNum(v)) + } + } + + pub fn new_from(n: u64) -> Result { + BigNum::new().and_then(|v| unsafe { + try_ssl!(ffi::BN_set_word(v.raw(), n as c_ulong)); + Ok(v) + }) + } + + pub fn from_dec_str(s: &str) -> Result { + BigNum::new().and_then(|v| unsafe { + let c_str = CString::from_slice(s.as_bytes()); + try_ssl!(ffi::BN_dec2bn(v.raw_ptr(), c_str.as_ptr())); + Ok(v) + }) + } + + pub fn from_hex_str(s: &str) -> Result { + BigNum::new().and_then(|v| unsafe { + let c_str = CString::from_slice(s.as_bytes()); + try_ssl!(ffi::BN_hex2bn(v.raw_ptr(), c_str.as_ptr())); + Ok(v) + }) + } + + pub fn new_from_slice(n: &[u8]) -> Result { + 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 { + unsafe { + with_bn_in_ctx!(r, ctx, { ffi::BN_sqr(r.raw(), self.raw(), ctx) == 1 }) + } + } + + pub fn checked_nnmod(&self, n: &BigNum) -> Result { + unsafe { + 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 { + unsafe { + 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 { + unsafe { + 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 { + unsafe { + 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 { + unsafe { + 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 { + unsafe { + 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 { + unsafe { + 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 { + unsafe { + with_bn_in_ctx!(r, ctx, { !ffi::BN_mod_inverse(r.raw(), self.raw(), n.raw(), ctx).is_null() }) + } + } + + pub fn add_word(&mut self, w: c_ulong) -> Result<(), SslError> { + unsafe { + if ffi::BN_add_word(self.raw(), w) == 1 { + Ok(()) + } else { + Err(SslError::get()) + } + } + } + + pub fn sub_word(&mut self, w: c_ulong) -> Result<(), SslError> { + unsafe { + if ffi::BN_sub_word(self.raw(), w) == 1 { + Ok(()) + } else { + Err(SslError::get()) + } + } + } + + pub fn mul_word(&mut self, w: c_ulong) -> Result<(), SslError> { + unsafe { + if ffi::BN_mul_word(self.raw(), w) == 1 { + Ok(()) + } else { + Err(SslError::get()) + } + } + } + + pub fn div_word(&mut self, w: c_ulong) -> Result { + unsafe { + let result = ffi::BN_div_word(self.raw(), w); + if result != -1 as c_ulong { + Ok(result) + } else { + Err(SslError::get()) + } + } + } + + pub fn mod_word(&self, w: c_ulong) -> Result { + unsafe { + let result = ffi::BN_mod_word(self.raw(), w); + if result != -1 as c_ulong { + Ok(result) + } else { + Err(SslError::get()) + } + } + } + + pub fn checked_gcd(&self, a: &BigNum) -> Result { + unsafe { + 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 { + unsafe { + with_bn_in_ctx!(r, ctx, { + 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()); + + ffi::BN_generate_prime_ex(r.raw(), bits as c_int, safe as c_int, add_arg, rem_arg, ptr::null()) == 1 + }) + } + } + + pub fn is_prime(&self, checks: i32) -> Result { + unsafe { + with_ctx!(ctx, { + Ok(ffi::BN_is_prime_ex(self.raw(), checks as c_int, ctx, ptr::null()) == 1) + }) + } + } + + pub fn is_prime_fast(&self, checks: i32, do_trial_division: bool) -> Result { + unsafe { + with_ctx!(ctx, { + 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 { + unsafe { + 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 { + unsafe { + 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 { + unsafe { + with_bn_in_ctx!(r, ctx, { ffi::BN_rand_range(r.raw(), self.raw()) == 1 }) + } + } + + pub fn checked_pseudo_rand_in_range(&self) -> Result { + unsafe { + 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 ffi::BN_set_bit(self.raw(), n as c_int) == 1 { + Ok(()) + } else { + Err(SslError::get()) + } + } + } + + pub fn clear_bit(&mut self, n: i32) -> Result<(), SslError> { + unsafe { + if ffi::BN_clear_bit(self.raw(), n as c_int) == 1 { + Ok(()) + } else { + Err(SslError::get()) + } + } + } + + pub fn is_bit_set(&self, n: i32) -> bool { + unsafe { + ffi::BN_is_bit_set(self.raw(), n as c_int) == 1 + } + } + + pub fn mask_bits(&mut self, n: i32) -> Result<(), SslError> { + unsafe { + if ffi::BN_mask_bits(self.raw(), n as c_int) == 1 { + Ok(()) + } else { + Err(SslError::get()) + } + } + } + + pub fn checked_shl1(&self) -> Result { + unsafe { + with_bn!(r, { ffi::BN_lshift1(r.raw(), self.raw()) == 1 }) + } + } + + pub fn checked_shr1(&self) -> Result { + unsafe { + with_bn!(r, { ffi::BN_rshift1(r.raw(), self.raw()) == 1 }) + } + } + + pub fn checked_add(&self, a: &BigNum) -> Result { + unsafe { + with_bn!(r, { ffi::BN_add(r.raw(), self.raw(), a.raw()) == 1 }) + } + } + + pub fn checked_sub(&self, a: &BigNum) -> Result { + unsafe { + with_bn!(r, { ffi::BN_sub(r.raw(), self.raw(), a.raw()) == 1 }) + } + } + + pub fn checked_mul(&self, a: &BigNum) -> Result { + unsafe { + 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 { + unsafe { + 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 { + unsafe { + 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 { + unsafe { + with_bn!(r, { ffi::BN_lshift(r.raw(), self.raw(), *a as c_int) == 1 }) + } + } + + pub fn checked_shr(&self, a: &i32) -> Result { + unsafe { + with_bn!(r, { ffi::BN_rshift(r.raw(), self.raw(), *a as c_int) == 1 }) + } + } + + pub fn negate(&mut self) { + unsafe { + ffi::BN_set_negative(self.raw(), !self.is_negative() as c_int) + } + } + + pub fn abs_cmp(&self, oth: BigNum) -> Ordering { + unsafe { + let res = ffi::BN_ucmp(self.raw(), oth.raw()) as i32; + if res < 0 { + Ordering::Less + } else if res > 0 { + Ordering::Greater + } else { + Ordering::Equal + } + } + } + + pub fn is_negative(&self) -> bool { + unsafe { + (*self.raw()).neg == 1 + } + } + + pub fn num_bits(&self) -> i32 { + unsafe { + ffi::BN_num_bits(self.raw()) as i32 + } + } + + pub fn num_bytes(&self) -> i32 { + (self.num_bits() + 7) / 8 + } + + unsafe fn raw(&self) -> *mut ffi::BIGNUM { + let BigNum(n) = *self; + n + } + + unsafe fn raw_ptr(&self) -> *const *mut ffi::BIGNUM { + let BigNum(ref n) = *self; + n + } + + pub fn to_vec(&self) -> Vec { + let size = self.num_bytes() as usize; + let mut v = Vec::with_capacity(size); + unsafe { + ffi::BN_bn2bin(self.raw(), v.as_mut_ptr()); + v.set_len(size); + } + v + } + + pub fn to_dec_str(&self) -> String { + unsafe { + let buf = ffi::BN_bn2dec(self.raw()); + assert!(!buf.is_null()); + let str = String::from_utf8(c_str_to_bytes(&buf).to_vec()).unwrap(); + ffi::CRYPTO_free(buf as *mut c_void); + str + } + } + + pub fn to_hex_str(&self) -> String { + unsafe { + let buf = ffi::BN_bn2hex(self.raw()); + assert!(!buf.is_null()); + let str = String::from_utf8(c_str_to_bytes(&buf).to_vec()).unwrap(); + ffi::CRYPTO_free(buf as *mut c_void); + str + } + } +} + +impl fmt::Debug for BigNum { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_dec_str()) + } +} + +impl Eq for BigNum { } +impl PartialEq for BigNum { + fn eq(&self, oth: &BigNum) -> bool { + unsafe { + ffi::BN_cmp(self.raw(), oth.raw()) == 0 + } + } +} + +impl Ord for BigNum { + fn cmp(&self, oth: &BigNum) -> Ordering { + self.partial_cmp(oth).unwrap() + } +} + +impl PartialOrd for BigNum { + fn partial_cmp(&self, oth: &BigNum) -> Option { + unsafe { + let v = ffi::BN_cmp(self.raw(), oth.raw()); + let ret = + if v == 0 { + Ordering::Equal + } else if v < 0 { + Ordering::Less + } else { + Ordering::Greater + }; + Some(ret) + } + } +} + +impl Drop for BigNum { + fn drop(&mut self) { + unsafe { + if !self.raw().is_null() { + ffi::BN_clear_free(self.raw()); + } + } + } +} + +pub mod unchecked { + use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub}; + use ffi; + use super::{BigNum}; + + impl<'a> Add<&'a BigNum> for &'a BigNum { + type Output = BigNum; + + fn add(self, oth: &'a BigNum) -> BigNum { + self.checked_add(oth).unwrap() + } + } + + impl<'a> Sub<&'a BigNum> for &'a BigNum { + type Output = BigNum; + + fn sub(self, oth: &'a BigNum) -> BigNum { + self.checked_sub(oth).unwrap() + } + } + + impl<'a> Mul<&'a BigNum> for &'a BigNum { + type Output = BigNum; + + fn mul(self, oth: &'a BigNum) -> BigNum { + self.checked_mul(oth).unwrap() + } + } + + impl<'a> Div<&'a BigNum> for &'a BigNum { + type Output = BigNum; + + fn div(self, oth: &'a BigNum) -> BigNum { + self.checked_div(oth).unwrap() + } + } + + impl<'a> Rem<&'a BigNum> for &'a BigNum { + type Output = BigNum; + + fn rem(self, oth: &'a BigNum) -> BigNum { + self.checked_mod(oth).unwrap() + } + } + + impl<'a> Shl for &'a BigNum { + type Output = BigNum; + + fn shl(self, n: i32) -> BigNum { + self.checked_shl(&n).unwrap() + } + } + + impl<'a> Shr for &'a BigNum { + type Output = BigNum; + + fn shr(self, n: i32) -> BigNum { + self.checked_shr(&n).unwrap() + } + } + + impl Clone for BigNum { + fn clone(&self) -> BigNum { + unsafe { + let r = ffi::BN_dup(self.raw()); + if r.is_null() { + panic!("Unexpected null pointer from BN_dup(..)") + } else { + BigNum(r) + } + } + } + } + + impl Neg for BigNum { + type Output = BigNum; + + fn neg(self) -> BigNum { + let mut n = self.clone(); + n.negate(); + n + } + } +} + +#[cfg(test)] +mod tests { + use bn::BigNum; + + #[test] + fn test_to_from_slice() { + let v0 = BigNum::new_from(10203004_u64).unwrap(); + let vec = v0.to_vec(); + let v1 = BigNum::new_from_slice(vec.as_slice()).unwrap(); + + assert!(v0 == v1); + } + + #[test] + fn test_negation() { + let a = BigNum::new_from(909829283_u64).unwrap(); + + assert!(!a.is_negative()); + assert!((-a).is_negative()); + } + + + #[test] + fn test_prime_numbers() { + let a = BigNum::new_from(19029017_u64).unwrap(); + let p = BigNum::checked_generate_prime(128, true, None, Some(&a)).unwrap(); + + assert!(p.is_prime(100).unwrap()); + assert!(p.is_prime_fast(100, true).unwrap()); + } +} diff --git a/openssl/src/crypto/hash.rs b/openssl/src/crypto/hash.rs new file mode 100644 index 00000000..f81532c9 --- /dev/null +++ b/openssl/src/crypto/hash.rs @@ -0,0 +1,333 @@ +use libc::c_uint; +use std::iter::repeat; +use std::old_io::{IoError, Writer}; + +use ffi; + +/// Message digest (hash) type. +#[derive(Copy)] +pub enum Type { + MD5, + SHA1, + SHA224, + SHA256, + SHA384, + SHA512, + RIPEMD160 +} + +impl Type { + /// Returns the length of the message digest. + #[inline] + pub fn md_len(&self) -> usize { + use self::Type::*; + match *self { + MD5 => 16, + SHA1 => 20, + SHA224 => 28, + SHA256 => 32, + SHA384 => 48, + SHA512 => 64, + RIPEMD160 => 20, + } + } + + /// Internal interface subject to removal. + #[inline] + pub fn evp_md(&self) -> *const ffi::EVP_MD { + unsafe { + use self::Type::*; + match *self { + MD5 => ffi::EVP_md5(), + SHA1 => ffi::EVP_sha1(), + SHA224 => ffi::EVP_sha224(), + SHA256 => ffi::EVP_sha256(), + SHA384 => ffi::EVP_sha384(), + SHA512 => ffi::EVP_sha512(), + RIPEMD160 => ffi::EVP_ripemd160(), + } + } + } +} + +#[derive(PartialEq, Copy)] +enum State { + Reset, + Updated, + Finalized, +} + +use self::State::*; + +/// Provides message digest (hash) computation. +/// +/// # Examples +/// +/// Calculate a hash in one go. +/// +/// ``` +/// use openssl::crypto::hash::{hash, Type}; +/// let data = b"\x42\xF4\x97\xE0"; +/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; +/// let res = hash(Type::MD5, data); +/// assert_eq!(res, spec); +/// ``` +/// +/// Use the `Writer` trait to supply the input in chunks. +/// +/// ``` +/// use std::old_io::Writer; +/// use openssl::crypto::hash::{Hasher, Type}; +/// let data = [b"\x42\xF4", b"\x97\xE0"]; +/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; +/// let mut h = Hasher::new(Type::MD5); +/// h.write_all(data[0]); +/// h.write_all(data[1]); +/// let res = h.finish(); +/// assert_eq!(res, spec); +/// ``` +/// +/// # Warning +/// +/// Don't actually use MD5 and SHA-1 hashes, they're not secure anymore. +/// +/// Don't ever hash passwords, use `crypto::pkcs5` or bcrypt/scrypt instead. +pub struct Hasher { + ctx: *mut ffi::EVP_MD_CTX, + md: *const ffi::EVP_MD, + type_: Type, + state: State, +} + +impl Hasher { + /// Creates a new `Hasher` with the specified hash type. + pub fn new(ty: Type) -> Hasher { + ffi::init(); + + let ctx = unsafe { + let r = ffi::EVP_MD_CTX_create(); + assert!(!r.is_null()); + r + }; + let md = ty.evp_md(); + + let mut h = Hasher { ctx: ctx, md: md, type_: ty, state: Finalized }; + h.init(); + h + } + + #[inline] + fn init(&mut self) { + match self.state { + Reset => return, + Updated => { self.finalize(); }, + Finalized => (), + } + unsafe { + let r = ffi::EVP_DigestInit_ex(self.ctx, self.md, 0 as *const _); + assert_eq!(r, 1); + } + self.state = Reset; + } + + #[inline] + fn update(&mut self, data: &[u8]) { + if self.state == Finalized { + self.init(); + } + unsafe { + let r = ffi::EVP_DigestUpdate(self.ctx, data.as_ptr(), + data.len() as c_uint); + assert_eq!(r, 1); + } + self.state = Updated; + } + + #[inline] + fn finalize(&mut self) -> Vec { + if self.state == Finalized { + self.init(); + } + let md_len = self.type_.md_len(); + let mut res: Vec = repeat(0).take(md_len).collect(); + unsafe { + let mut len = 0; + let r = ffi::EVP_DigestFinal_ex(self.ctx, res.as_mut_ptr(), &mut len); + self.state = Finalized; + assert_eq!(len as usize, md_len); + assert_eq!(r, 1); + } + res + } + + /// Returns the hash of the data written since creation or + /// the last `finish` and resets the hasher. + #[inline] + pub fn finish(&mut self) -> Vec { + self.finalize() + } +} + +impl Writer for Hasher { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.update(buf); + Ok(()) + } +} + +impl Clone for Hasher { + fn clone(&self) -> Hasher { + let ctx = unsafe { + let ctx = ffi::EVP_MD_CTX_create(); + assert!(!ctx.is_null()); + let r = ffi::EVP_MD_CTX_copy_ex(ctx, self.ctx); + assert_eq!(r, 1); + ctx + }; + Hasher { ctx: ctx, md: self.md, type_: self.type_, state: self.state } + } +} + +impl Drop for Hasher { + fn drop(&mut self) { + unsafe { + if self.state != Finalized { + let mut buf: Vec = repeat(0).take(self.type_.md_len()).collect(); + let mut len = 0; + ffi::EVP_DigestFinal_ex(self.ctx, buf.as_mut_ptr(), &mut len); + } + ffi::EVP_MD_CTX_destroy(self.ctx); + } + } +} + +/// Computes the hash of the `data` with the hash `t`. +pub fn hash(t: Type, data: &[u8]) -> Vec { + let mut h = Hasher::new(t); + let _ = h.write_all(data); + h.finish() +} + +#[cfg(test)] +mod tests { + use serialize::hex::{FromHex, ToHex}; + use super::{hash, Hasher, Type}; + use std::old_io::Writer; + + fn hash_test(hashtype: Type, hashtest: &(&str, &str)) { + let res = hash(hashtype, &*hashtest.0.from_hex().unwrap()); + assert_eq!(res.to_hex(), hashtest.1); + } + + fn hash_recycle_test(h: &mut Hasher, hashtest: &(&str, &str)) { + let _ = h.write_all(&*hashtest.0.from_hex().unwrap()); + let res = h.finish(); + assert_eq!(res.to_hex(), hashtest.1); + } + + // Test vectors from http://www.nsrl.nist.gov/testdata/ + #[allow(non_upper_case_globals)] + const md5_tests: [(&'static str, &'static str); 13] = [ + ("", "d41d8cd98f00b204e9800998ecf8427e"), + ("7F", "83acb6e67e50e31db6ed341dd2de1595"), + ("EC9C", "0b07f0d4ca797d8ac58874f887cb0b68"), + ("FEE57A", "e0d583171eb06d56198fc0ef22173907"), + ("42F497E0", "7c430f178aefdf1487fee7144e9641e2"), + ("C53B777F1C", "75ef141d64cb37ec423da2d9d440c925"), + ("89D5B576327B", "ebbaf15eb0ed784c6faa9dc32831bf33"), + ("5D4CCE781EB190", "ce175c4b08172019f05e6b5279889f2c"), + ("81901FE94932D7B9", "cd4d2f62b8cdb3a0cf968a735a239281"), + ("C9FFDEE7788EFB4EC9", "e0841a231ab698db30c6c0f3f246c014"), + ("66AC4B7EBA95E53DC10B", "a3b3cea71910d9af56742aa0bb2fe329"), + ("A510CD18F7A56852EB0319", "577e216843dd11573574d3fb209b97d8"), + ("AAED18DBE8938C19ED734A8D", "6f80fb775f27e0a4ce5c2f42fc72c5f1") + ]; + + #[test] + fn test_md5() { + for test in md5_tests.iter() { + hash_test(Type::MD5, test); + } + } + + #[test] + fn test_md5_recycle() { + let mut h = Hasher::new(Type::MD5); + for test in md5_tests.iter() { + hash_recycle_test(&mut h, test); + } + } + + #[test] + fn test_finish_twice() { + let mut h = Hasher::new(Type::MD5); + let _ = h.write_all(&*md5_tests[6].0.from_hex().unwrap()); + let _ = h.finish(); + let res = h.finish(); + let null = hash(Type::MD5, &[]); + assert_eq!(res, null); + } + + #[test] + fn test_clone() { + let i = 7; + let inp = md5_tests[i].0.from_hex().unwrap(); + assert!(inp.len() > 2); + let p = inp.len() / 2; + let h0 = Hasher::new(Type::MD5); + + println!("Clone a new hasher"); + let mut h1 = h0.clone(); + let _ = h1.write_all(&inp[..p]); + { + println!("Clone an updated hasher"); + let mut h2 = h1.clone(); + let _ = h2.write_all(&inp[p..]); + let res = h2.finish(); + assert_eq!(res.to_hex(), md5_tests[i].1); + } + let _ = h1.write_all(&inp[p..]); + let res = h1.finish(); + assert_eq!(res.to_hex(), md5_tests[i].1); + + println!("Clone a finished hasher"); + let mut h3 = h1.clone(); + let _ = h3.write_all(&*md5_tests[i + 1].0.from_hex().unwrap()); + let res = h3.finish(); + assert_eq!(res.to_hex(), md5_tests[i + 1].1); + } + + #[test] + fn test_sha1() { + let tests = [ + ("616263", "a9993e364706816aba3e25717850c26c9cd0d89d"), + ]; + + for test in tests.iter() { + hash_test(Type::SHA1, test); + } + } + + #[test] + fn test_sha256() { + let tests = [ + ("616263", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") + ]; + + for test in tests.iter() { + hash_test(Type::SHA256, test); + } + } + + #[test] + fn test_ripemd160() { + let tests = [ + ("616263", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc") + ]; + + for test in tests.iter() { + hash_test(Type::RIPEMD160, test); + } + } +} diff --git a/openssl/src/crypto/hmac.rs b/openssl/src/crypto/hmac.rs new file mode 100644 index 00000000..65808e58 --- /dev/null +++ b/openssl/src/crypto/hmac.rs @@ -0,0 +1,474 @@ +/* + * Copyright 2013 Jack Lloyd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use libc::{c_int, c_uint}; +use std::iter::repeat; +use std::old_io::{IoError, Writer}; + +use crypto::hash::Type; +use ffi; + +#[derive(PartialEq, Copy)] +enum State { + Reset, + Updated, + Finalized, +} + +use self::State::*; + +/// Provides HMAC computation. +/// +/// # Examples +/// +/// Calculate a HMAC in one go. +/// +/// ``` +/// use openssl::crypto::hash::Type; +/// use openssl::crypto::hmac::hmac; +/// let key = b"Jefe"; +/// let data = b"what do ya want for nothing?"; +/// let spec = b"\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38"; +/// let res = hmac(Type::MD5, key, data); +/// assert_eq!(spec, res); +/// ``` +/// +/// Use the `Writer` trait to supply the input in chunks. +/// +/// ``` +/// use std::old_io::Writer; +/// use openssl::crypto::hash::Type; +/// use openssl::crypto::hmac::HMAC; +/// let key = b"Jefe"; +/// let data = [b"what do ya ", b"want for nothing?"]; +/// let spec = b"\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38"; +/// let mut h = HMAC::new(Type::MD5, &*key); +/// h.write_all(data[0]); +/// h.write_all(data[1]); +/// let res = h.finish(); +/// assert_eq!(spec, res); +/// ``` +pub struct HMAC { + ctx: ffi::HMAC_CTX, + type_: Type, + state: State, +} + +impl HMAC { + /// Creates a new `HMAC` with the specified hash type using the `key`. + pub fn new(ty: Type, key: &[u8]) -> HMAC { + ffi::init(); + + let ctx = unsafe { + let mut ctx = ::std::mem::uninitialized(); + ffi::HMAC_CTX_init(&mut ctx); + ctx + }; + let md = ty.evp_md(); + + let mut h = HMAC { ctx: ctx, type_: ty, state: Finalized }; + h.init_once(md, key); + h + } + + #[inline] + fn init_once(&mut self, md: *const ffi::EVP_MD, key: &[u8]) { + unsafe { + let r = ffi::HMAC_Init_ex(&mut self.ctx, + key.as_ptr(), key.len() as c_int, + md, 0 as *const _); + assert_eq!(r, 1); + } + self.state = Reset; + } + + #[inline] + fn init(&mut self) { + match self.state { + Reset => return, + Updated => { self.finalize(); }, + Finalized => (), + } + // If the key and/or md is not supplied it's reused from the last time + // avoiding redundant initializations + unsafe { + let r = ffi::HMAC_Init_ex(&mut self.ctx, + 0 as *const _, 0, + 0 as *const _, 0 as *const _); + assert_eq!(r, 1); + } + self.state = Reset; + } + + #[inline] + fn update(&mut self, data: &[u8]) { + if self.state == Finalized { + self.init(); + } + unsafe { + let r = ffi::HMAC_Update(&mut self.ctx, data.as_ptr(), + data.len() as c_uint); + assert_eq!(r, 1); + } + self.state = Updated; + } + + #[inline] + fn finalize(&mut self) -> Vec { + if self.state == Finalized { + self.init(); + } + let md_len = self.type_.md_len(); + let mut res: Vec = repeat(0).take(md_len).collect(); + unsafe { + let mut len = 0; + let r = ffi::HMAC_Final(&mut self.ctx, res.as_mut_ptr(), &mut len); + self.state = Finalized; + assert_eq!(len as usize, md_len); + assert_eq!(r, 1); + } + res + } + + /// Returns the hash of the data written since creation or + /// the last `finish` and resets the hasher. + #[inline] + pub fn finish(&mut self) -> Vec { + self.finalize() + } +} + +impl Writer for HMAC { + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<(), IoError> { + self.update(buf); + Ok(()) + } +} + +impl Clone for HMAC { + fn clone(&self) -> HMAC { + let mut ctx: ffi::HMAC_CTX; + unsafe { + ctx = ::std::mem::uninitialized(); + let r = ffi::HMAC_CTX_copy(&mut ctx, &self.ctx); + assert_eq!(r, 1); + } + HMAC { ctx: ctx, type_: self.type_, state: self.state } + } +} + +impl Drop for HMAC { + fn drop(&mut self) { + unsafe { + if self.state != Finalized { + let mut buf: Vec = repeat(0).take(self.type_.md_len()).collect(); + let mut len = 0; + ffi::HMAC_Final(&mut self.ctx, buf.as_mut_ptr(), &mut len); + } + ffi::HMAC_CTX_cleanup(&mut self.ctx); + } + } +} + +/// Computes the HMAC of the `data` with the hash `t` and `key`. +pub fn hmac(t: Type, key: &[u8], data: &[u8]) -> Vec { + let mut h = HMAC::new(t, key); + let _ = h.write_all(data); + h.finish() +} + +#[cfg(test)] +mod tests { + use std::iter::repeat; + use serialize::hex::FromHex; + use crypto::hash::Type; + use crypto::hash::Type::*; + use super::{hmac, HMAC}; + use std::old_io::Writer; + + fn test_hmac(ty: Type, tests: &[(Vec, Vec, Vec)]) { + for &(ref key, ref data, ref res) in tests.iter() { + assert_eq!(hmac(ty, &**key, &**data), *res); + } + } + + fn test_hmac_recycle(h: &mut HMAC, test: &(Vec, Vec, Vec)) { + let &(_, ref data, ref res) = test; + let _ = h.write_all(&**data); + assert_eq!(h.finish(), *res); + } + + #[test] + fn test_hmac_md5() { + // test vectors from RFC 2202 + let tests: [(Vec, Vec, Vec); 7] = [ + (repeat(0x0b_u8).take(16).collect(), b"Hi There".to_vec(), + "9294727a3638bb1c13f48ef8158bfc9d".from_hex().unwrap()), + (b"Jefe".to_vec(), + b"what do ya want for nothing?".to_vec(), + "750c783e6ab0b503eaa86e310a5db738".from_hex().unwrap()), + (repeat(0xaa_u8).take(16).collect(), repeat(0xdd_u8).take(50).collect(), + "56be34521d144c88dbb8c733f0e8b3f6".from_hex().unwrap()), + ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(), + repeat(0xcd_u8).take(50).collect(), + "697eaf0aca3a3aea3a75164746ffaa79".from_hex().unwrap()), + (repeat(0x0c_u8).take(16).collect(), + b"Test With Truncation".to_vec(), + "56461ef2342edc00f9bab995690efd4c".from_hex().unwrap()), + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), + "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()), + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key \ + and Larger Than One Block-Size Data".to_vec(), + "6f630fad67cda0ee1fb1f562db3aa53e".from_hex().unwrap()) + ]; + + test_hmac(MD5, &tests); + } + + #[test] + fn test_hmac_md5_recycle() { + let tests: [(Vec, Vec, Vec); 2] = [ + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), + "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()), + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key \ + and Larger Than One Block-Size Data".to_vec(), + "6f630fad67cda0ee1fb1f562db3aa53e".from_hex().unwrap()) + ]; + + let mut h = HMAC::new(MD5, &*tests[0].0); + for i in 0..100us { + let test = &tests[i % 2]; + test_hmac_recycle(&mut h, test); + } + } + + #[test] + fn test_finish_twice() { + let test: (Vec, Vec, Vec) = + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), + "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()); + + let mut h = HMAC::new(Type::MD5, &*test.0); + let _ = h.write_all(&*test.1); + let _ = h.finish(); + let res = h.finish(); + let null = hmac(Type::MD5, &*test.0, &[]); + assert_eq!(res, null); + } + + #[test] + fn test_clone() { + let tests: [(Vec, Vec, Vec); 2] = [ + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), + "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()), + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key \ + and Larger Than One Block-Size Data".to_vec(), + "6f630fad67cda0ee1fb1f562db3aa53e".from_hex().unwrap()), + ]; + let p = tests[0].0.len() / 2; + let h0 = HMAC::new(Type::MD5, &*tests[0].0); + + println!("Clone a new hmac"); + let mut h1 = h0.clone(); + let _ = h1.write_all(&tests[0].1[..p]); + { + println!("Clone an updated hmac"); + let mut h2 = h1.clone(); + let _ = h2.write_all(&tests[0].1[p..]); + let res = h2.finish(); + assert_eq!(res, tests[0].2); + } + let _ = h1.write_all(&tests[0].1[p..]); + let res = h1.finish(); + assert_eq!(res, tests[0].2); + + println!("Clone a finished hmac"); + let mut h3 = h1.clone(); + let _ = h3.write_all(&*tests[1].1); + let res = h3.finish(); + assert_eq!(res, tests[1].2); + } + + #[test] + fn test_hmac_sha1() { + // test vectors from RFC 2202 + let tests: [(Vec, Vec, Vec); 7] = [ + (repeat(0x0b_u8).take(20).collect(), b"Hi There".to_vec(), + "b617318655057264e28bc0b6fb378c8ef146be00".from_hex().unwrap()), + (b"Jefe".to_vec(), + b"what do ya want for nothing?".to_vec(), + "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79".from_hex().unwrap()), + (repeat(0xaa_u8).take(20).collect(), repeat(0xdd_u8).take(50).collect(), + "125d7342b9ac11cd91a39af48aa17b4f63f175d3".from_hex().unwrap()), + ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(), + repeat(0xcd_u8).take(50).collect(), + "4c9007f4026250c6bc8414f9bf50c86c2d7235da".from_hex().unwrap()), + (repeat(0x0c_u8).take(20).collect(), + b"Test With Truncation".to_vec(), + "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04".from_hex().unwrap()), + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), + "aa4ae5e15272d00e95705637ce8a3b55ed402112".from_hex().unwrap()), + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key \ + and Larger Than One Block-Size Data".to_vec(), + "e8e99d0f45237d786d6bbaa7965c7808bbff1a91".from_hex().unwrap()) + ]; + + test_hmac(SHA1, &tests); + } + + #[test] + fn test_hmac_sha1_recycle() { + let tests: [(Vec, Vec, Vec); 2] = [ + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), + "aa4ae5e15272d00e95705637ce8a3b55ed402112".from_hex().unwrap()), + (repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key \ + and Larger Than One Block-Size Data".to_vec(), + "e8e99d0f45237d786d6bbaa7965c7808bbff1a91".from_hex().unwrap()) + ]; + + let mut h = HMAC::new(SHA1, &*tests[0].0); + for i in 0..100us { + let test = &tests[i % 2]; + test_hmac_recycle(&mut h, test); + } + } + + + + fn test_sha2(ty: Type, results: &[Vec]) { + // test vectors from RFC 4231 + let tests: [(Vec, Vec); 6] = [ + (repeat(0xb_u8).take(20).collect(), b"Hi There".to_vec()), + (b"Jefe".to_vec(), + b"what do ya want for nothing?".to_vec()), + (repeat(0xaa_u8).take(20).collect(), repeat(0xdd_u8).take(50).collect()), + ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(), + repeat(0xcd_u8).take(50).collect()), + (repeat(0xaa_u8).take(131).collect(), + b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec()), + (repeat(0xaa_u8).take(131).collect(), + b"This is a test using a larger than block-size key and a \ + larger than block-size data. The key needs to be hashed \ + before being used by the HMAC algorithm.".to_vec()) + ]; + + for (&(ref key, ref data), res) in tests.iter().zip(results.iter()) { + assert_eq!(hmac(ty, &**key, &**data), *res); + } + + // recycle test + let mut h = HMAC::new(ty, &*tests[5].0); + for i in 0..100us { + let test = &tests[4 + i % 2]; + let tup = (test.0.clone(), test.1.clone(), results[4 + i % 2].clone()); + test_hmac_recycle(&mut h, &tup); + } + } + + #[test] + fn test_hmac_sha224() { + let results = [ + "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22".from_hex().unwrap(), + "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44".from_hex().unwrap(), + "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea".from_hex().unwrap(), + "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a".from_hex().unwrap(), + "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e".from_hex().unwrap(), + "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1".from_hex().unwrap() + ]; + test_sha2(SHA224, &results); + } + + #[test] + fn test_hmac_sha256() { + let results = [ + "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7".from_hex().unwrap(), + "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843".from_hex().unwrap(), + "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe".from_hex().unwrap(), + "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b".from_hex().unwrap(), + "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54".from_hex().unwrap(), + "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2".from_hex().unwrap() + ]; + test_sha2(SHA256, &results); + } + + #[test] + fn test_hmac_sha384() { + let results = [ + "afd03944d84895626b0825f4ab46907f\ + 15f9dadbe4101ec682aa034c7cebc59c\ + faea9ea9076ede7f4af152e8b2fa9cb6".from_hex().unwrap(), + "af45d2e376484031617f78d2b58a6b1b\ + 9c7ef464f5a01b47e42ec3736322445e\ + 8e2240ca5e69e2c78b3239ecfab21649".from_hex().unwrap(), + "88062608d3e6ad8a0aa2ace014c8a86f\ + 0aa635d947ac9febe83ef4e55966144b\ + 2a5ab39dc13814b94e3ab6e101a34f27".from_hex().unwrap(), + "3e8a69b7783c25851933ab6290af6ca7\ + 7a9981480850009cc5577c6e1f573b4e\ + 6801dd23c4a7d679ccf8a386c674cffb".from_hex().unwrap(), + "4ece084485813e9088d2c63a041bc5b4\ + 4f9ef1012a2b588f3cd11f05033ac4c6\ + 0c2ef6ab4030fe8296248df163f44952".from_hex().unwrap(), + "6617178e941f020d351e2f254e8fd32c\ + 602420feb0b8fb9adccebb82461e99c5\ + a678cc31e799176d3860e6110c46523e".from_hex().unwrap() + ]; + test_sha2(SHA384, &results); + } + + #[test] + fn test_hmac_sha512() { + let results = [ + "87aa7cdea5ef619d4ff0b4241a1d6cb0\ + 2379f4e2ce4ec2787ad0b30545e17cde\ + daa833b7d6b8a702038b274eaea3f4e4\ + be9d914eeb61f1702e696c203a126854".from_hex().unwrap(), + "164b7a7bfcf819e2e395fbe73b56e0a3\ + 87bd64222e831fd610270cd7ea250554\ + 9758bf75c05a994a6d034f65f8f0e6fd\ + caeab1a34d4a6b4b636e070a38bce737".from_hex().unwrap(), + "fa73b0089d56a284efb0f0756c890be9\ + b1b5dbdd8ee81a3655f83e33b2279d39\ + bf3e848279a722c806b485a47e67c807\ + b946a337bee8942674278859e13292fb".from_hex().unwrap(), + "b0ba465637458c6990e5a8c5f61d4af7\ + e576d97ff94b872de76f8050361ee3db\ + a91ca5c11aa25eb4d679275cc5788063\ + a5f19741120c4f2de2adebeb10a298dd".from_hex().unwrap(), + "80b24263c7c1a3ebb71493c1dd7be8b4\ + 9b46d1f41b4aeec1121b013783f8f352\ + 6b56d037e05f2598bd0fd2215d6a1e52\ + 95e64f73f63f0aec8b915a985d786598".from_hex().unwrap(), + "e37b6a775dc87dbaa4dfa9f96e5e3ffd\ + debd71f8867289865df5a32d20cdc944\ + b6022cac3c4982b10d5eeb55c3e4de15\ + 134676fb6de0446065c97440fa8c6a58".from_hex().unwrap() + ]; + test_sha2(SHA512, &results); + } +} diff --git a/openssl/src/crypto/memcmp.rs b/openssl/src/crypto/memcmp.rs new file mode 100644 index 00000000..299effa9 --- /dev/null +++ b/openssl/src/crypto/memcmp.rs @@ -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]); + } +} diff --git a/openssl/src/crypto/mod.rs b/openssl/src/crypto/mod.rs new file mode 100644 index 00000000..e695de33 --- /dev/null +++ b/openssl/src/crypto/mod.rs @@ -0,0 +1,24 @@ +/* + * Copyright 2011 Google Inc. + * 2013 Jack Lloyd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pub mod hash; +pub mod hmac; +pub mod pkcs5; +pub mod pkey; +pub mod rand; +pub mod symm; +pub mod memcmp; diff --git a/openssl/src/crypto/pkcs5.rs b/openssl/src/crypto/pkcs5.rs new file mode 100644 index 00000000..b101c3ed --- /dev/null +++ b/openssl/src/crypto/pkcs5.rs @@ -0,0 +1,119 @@ +use libc::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: usize, keylen: usize) -> Vec { + unsafe { + assert!(iter >= 1); + assert!(keylen >= 1); + + let mut out = Vec::with_capacity(keylen); + + 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 { panic!(); } + + out.set_len(keylen); + + out + } +} + +#[cfg(test)] +mod tests { + // Test vectors from + // http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06 + #[test] + fn test_pbkdf2_hmac_sha1() { + assert_eq!( + super::pbkdf2_hmac_sha1( + "password", + "salt".as_bytes(), + 1, + 20 + ), + vec!( + 0x0c_u8, 0x60_u8, 0xc8_u8, 0x0f_u8, 0x96_u8, 0x1f_u8, 0x0e_u8, + 0x71_u8, 0xf3_u8, 0xa9_u8, 0xb5_u8, 0x24_u8, 0xaf_u8, 0x60_u8, + 0x12_u8, 0x06_u8, 0x2f_u8, 0xe0_u8, 0x37_u8, 0xa6_u8 + ) + ); + + assert_eq!( + super::pbkdf2_hmac_sha1( + "password", + "salt".as_bytes(), + 2, + 20 + ), + vec!( + 0xea_u8, 0x6c_u8, 0x01_u8, 0x4d_u8, 0xc7_u8, 0x2d_u8, 0x6f_u8, + 0x8c_u8, 0xcd_u8, 0x1e_u8, 0xd9_u8, 0x2a_u8, 0xce_u8, 0x1d_u8, + 0x41_u8, 0xf0_u8, 0xd8_u8, 0xde_u8, 0x89_u8, 0x57_u8 + ) + ); + + assert_eq!( + super::pbkdf2_hmac_sha1( + "password", + "salt".as_bytes(), + 4096, + 20 + ), + vec!( + 0x4b_u8, 0x00_u8, 0x79_u8, 0x01_u8, 0xb7_u8, 0x65_u8, 0x48_u8, + 0x9a_u8, 0xbe_u8, 0xad_u8, 0x49_u8, 0xd9_u8, 0x26_u8, 0xf7_u8, + 0x21_u8, 0xd0_u8, 0x65_u8, 0xa4_u8, 0x29_u8, 0xc1_u8 + ) + ); + + assert_eq!( + super::pbkdf2_hmac_sha1( + "password", + "salt".as_bytes(), + 16777216, + 20 + ), + vec!( + 0xee_u8, 0xfe_u8, 0x3d_u8, 0x61_u8, 0xcd_u8, 0x4d_u8, 0xa4_u8, + 0xe4_u8, 0xe9_u8, 0x94_u8, 0x5b_u8, 0x3d_u8, 0x6b_u8, 0xa2_u8, + 0x15_u8, 0x8c_u8, 0x26_u8, 0x34_u8, 0xe9_u8, 0x84_u8 + ) + ); + + assert_eq!( + super::pbkdf2_hmac_sha1( + "passwordPASSWORDpassword", + "saltSALTsaltSALTsaltSALTsaltSALTsalt".as_bytes(), + 4096, + 25 + ), + vec!( + 0x3d_u8, 0x2e_u8, 0xec_u8, 0x4f_u8, 0xe4_u8, 0x1c_u8, 0x84_u8, + 0x9b_u8, 0x80_u8, 0xc8_u8, 0xd8_u8, 0x36_u8, 0x62_u8, 0xc0_u8, + 0xe4_u8, 0x4a_u8, 0x8b_u8, 0x29_u8, 0x1a_u8, 0x96_u8, 0x4c_u8, + 0xf2_u8, 0xf0_u8, 0x70_u8, 0x38_u8 + ) + ); + + assert_eq!( + super::pbkdf2_hmac_sha1( + "pass\x00word", + "sa\x00lt".as_bytes(), + 4096, + 16 + ), + vec!( + 0x56_u8, 0xfa_u8, 0x6a_u8, 0xa7_u8, 0x55_u8, 0x48_u8, 0x09_u8, + 0x9d_u8, 0xcc_u8, 0x37_u8, 0xd7_u8, 0xf0_u8, 0x34_u8, 0x25_u8, + 0xe0_u8, 0xc3_u8 + ) + ); + } +} diff --git a/openssl/src/crypto/pkey.rs b/openssl/src/crypto/pkey.rs new file mode 100644 index 00000000..c20fae4f --- /dev/null +++ b/openssl/src/crypto/pkey.rs @@ -0,0 +1,423 @@ +use libc::{c_int, c_uint, c_ulong}; +use std::iter::repeat; +use std::mem; +use std::ptr; +use bio::{MemBio}; +use crypto::hash; +use crypto::hash::Type as HashType; +use ffi; +use ssl::error::{SslError, StreamError}; + +#[derive(Copy)] +enum Parts { + Neither, + Public, + Both +} + +/// Represents a role an asymmetric key might be appropriate for. +#[derive(Copy)] +pub enum Role { + Encrypt, + Decrypt, + Sign, + Verify +} + +/// Type of encryption padding to use. +#[derive(Copy)] +pub enum EncryptionPadding { + OAEP, + PKCS1v15 +} + +fn openssl_padding_code(padding: EncryptionPadding) -> c_int { + match padding { + EncryptionPadding::OAEP => 4, + EncryptionPadding::PKCS1v15 => 1 + } +} + +fn openssl_hash_nid(hash: HashType) -> c_int { + match hash { + 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 ffi::EVP_PKEY, + parts: Parts, +} + +/// Represents a public key, optionally with a private key attached. +impl PKey { + pub fn new() -> PKey { + unsafe { + ffi::init(); + + PKey { + evp: ffi::EVP_PKEY_new(), + parts: Parts::Neither, + } + } + } + + fn _tostr(&self, f: unsafe extern "C" fn(*mut ffi::RSA, *const *mut u8) -> c_int) -> Vec { + unsafe { + 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 = repeat(0u8).take(len as usize).collect::>(); + + let r = f(rsa, &s.as_mut_ptr()); + + s.truncate(r as usize); + s + } + } + + 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); + ffi::EVP_PKEY_set1_RSA(self.evp, rsa); + } + } + + pub fn gen(&mut self, keysz: usize) { + unsafe { + let rsa = ffi::RSA_generate_key( + keysz as c_int, + 65537 as c_ulong, + ptr::null(), + ptr::null() + ); + + // XXX: 6 == NID_rsaEncryption + ffi::EVP_PKEY_assign( + self.evp, + 6 as c_int, + mem::transmute(rsa)); + + self.parts = Parts::Both; + } + } + + /** + * Returns a serialized form of the public key, suitable for load_pub(). + */ + pub fn save_pub(&self) -> Vec { + 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, ffi::d2i_RSA_PUBKEY); + self.parts = Parts::Public; + } + + /** + * Returns a serialized form of the public and private keys, suitable for + * load_priv(). + */ + pub fn save_priv(&self) -> Vec { + 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, 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*/) -> 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_all(buf.as_slice()).map_err(StreamError) + } + + /** + * Returns the size of the public key modulus. + */ + pub fn size(&self) -> usize { + unsafe { + ffi::RSA_size(ffi::EVP_PKEY_get1_RSA(self.evp)) as usize + } + } + + /** + * Returns whether this pkey object can perform the specified role. + */ + pub fn can(&self, r: Role) -> bool { + match r { + Role::Encrypt => + match self.parts { + Parts::Neither => false, + _ => true, + }, + Role::Verify => + match self.parts { + Parts::Neither => false, + _ => true, + }, + Role::Decrypt => + match self.parts { + Parts::Both => true, + _ => false, + }, + Role::Sign => + match self.parts { + Parts::Both => true, + _ => false, + }, + } + } + + /** + * Returns the maximum amount of data that can be encrypted by an encrypt() + * call. + */ + pub fn max_data(&self) -> usize { + unsafe { + 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 usize - 41 + } + } + + pub fn encrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec { + unsafe { + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); + let len = ffi::RSA_size(rsa); + + assert!(s.len() < self.max_data()); + + let mut r = repeat(0u8).take(len as usize + 1).collect::>(); + + let rv = ffi::RSA_public_encrypt( + s.len() as c_int, + s.as_ptr(), + r.as_mut_ptr(), + rsa, + openssl_padding_code(padding)); + + if rv < 0 as c_int { + vec!() + } else { + r.truncate(rv as usize); + r + } + } + } + + pub fn decrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec { + unsafe { + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); + let len = ffi::RSA_size(rsa); + + assert_eq!(s.len() as c_int, ffi::RSA_size(rsa)); + + let mut r = repeat(0u8).take(len as usize + 1).collect::>(); + + let rv = ffi::RSA_private_decrypt( + s.len() as c_int, + s.as_ptr(), + r.as_mut_ptr(), + rsa, + openssl_padding_code(padding)); + + if rv < 0 as c_int { + vec!() + } else { + r.truncate(rv as usize); + r + } + } + } + + /** + * Encrypts data using OAEP padding, returning the encrypted data. The + * supplied data must not be larger than max_data(). + */ + pub fn encrypt(&self, s: &[u8]) -> Vec { self.encrypt_with_padding(s, EncryptionPadding::OAEP) } + + /** + * Decrypts data, expecting OAEP padding, returning the decrypted data. + */ + pub fn decrypt(&self, s: &[u8]) -> Vec { 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 { 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, HashType::SHA256) } + + pub fn sign_with_hash(&self, s: &[u8], hash: hash::Type) -> Vec { + unsafe { + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); + let len = ffi::RSA_size(rsa); + let mut r = repeat(0u8).take(len as usize + 1).collect::>(); + + let mut len = 0; + let rv = ffi::RSA_sign( + openssl_hash_nid(hash), + s.as_ptr(), + s.len() as c_uint, + r.as_mut_ptr(), + &mut len, + rsa); + + if rv < 0 as c_int { + vec!() + } else { + r.truncate(len as usize); + r + } + } + } + + pub fn verify_with_hash(&self, m: &[u8], s: &[u8], hash: hash::Type) -> bool { + unsafe { + let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); + + let rv = ffi::RSA_verify( + openssl_hash_nid(hash), + m.as_ptr(), + m.len() as c_uint, + s.as_ptr(), + s.len() as c_uint, + rsa + ); + + 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 { + ffi::EVP_PKEY_free(self.evp); + } + } +} + +#[cfg(test)] +mod tests { + use crypto::hash::Type::{MD5, SHA1}; + + #[test] + fn test_gen_pub() { + let mut k0 = super::PKey::new(); + let mut k1 = super::PKey::new(); + k0.gen(512); + 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::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] + fn test_gen_priv() { + let mut k0 = super::PKey::new(); + let mut k1 = super::PKey::new(); + k0.gen(512); + 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::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] + fn test_encrypt() { + let mut k0 = super::PKey::new(); + let mut k1 = super::PKey::new(); + let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8); + k0.gen(512); + k1.load_pub(k0.save_pub().as_slice()); + let emsg = k1.encrypt(msg.as_slice()); + let dmsg = k0.decrypt(emsg.as_slice()); + assert!(msg == dmsg); + } + + #[test] + fn test_encrypt_pkcs() { + let mut k0 = super::PKey::new(); + let mut k1 = super::PKey::new(); + let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8); + k0.gen(512); + k1.load_pub(k0.save_pub().as_slice()); + 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); + } + + #[test] + fn test_sign() { + let mut k0 = super::PKey::new(); + let mut k1 = super::PKey::new(); + let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8); + k0.gen(512); + k1.load_pub(k0.save_pub().as_slice()); + let sig = k0.sign(msg.as_slice()); + let rv = k1.verify(msg.as_slice(), sig.as_slice()); + assert!(rv == true); + } + + #[test] + fn test_sign_hashes() { + let mut k0 = super::PKey::new(); + let mut k1 = super::PKey::new(); + let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8); + k0.gen(512); + k1.load_pub(k0.save_pub().as_slice()); + + let sig = k0.sign_with_hash(msg.as_slice(), MD5); + + assert!(k1.verify_with_hash(msg.as_slice(), sig.as_slice(), MD5)); + assert!(!k1.verify_with_hash(msg.as_slice(), sig.as_slice(), SHA1)); + } +} diff --git a/openssl/src/crypto/rand.rs b/openssl/src/crypto/rand.rs new file mode 100644 index 00000000..dc338a3d --- /dev/null +++ b/openssl/src/crypto/rand.rs @@ -0,0 +1,27 @@ +use libc::c_int; +use ffi; + +pub fn rand_bytes(len: usize) -> Vec { + unsafe { + let mut out = Vec::with_capacity(len); + + 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); + + out + } +} + +#[cfg(test)] +mod tests { + use super::rand_bytes; + + #[test] + fn test_rand_bytes() { + let bytes = rand_bytes(32); + println!("{:?}", bytes); + } +} diff --git a/openssl/src/crypto/symm.rs b/openssl/src/crypto/symm.rs new file mode 100644 index 00000000..922137c0 --- /dev/null +++ b/openssl/src/crypto/symm.rs @@ -0,0 +1,314 @@ +use std::iter::repeat; +use libc::{c_int}; + +use ffi; + +#[derive(Copy)] +pub enum Mode { + Encrypt, + Decrypt, +} + +#[allow(non_camel_case_types)] +#[derive(Copy)] +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) -> (*const ffi::EVP_CIPHER, u32, u32) { + unsafe { + match t { + Type::AES_128_ECB => (ffi::EVP_aes_128_ecb(), 16, 16), + Type::AES_128_CBC => (ffi::EVP_aes_128_cbc(), 16, 16), + #[cfg(feature = "aes_xts")] + Type::AES_128_XTS => (ffi::EVP_aes_128_xts(), 32, 16), + // AES_128_CTR => (EVP_aes_128_ctr(), 16, 0), + //AES_128_GCM => (EVP_aes_128_gcm(), 16, 16), + + Type::AES_256_ECB => (ffi::EVP_aes_256_ecb(), 32, 16), + Type::AES_256_CBC => (ffi::EVP_aes_256_cbc(), 32, 16), + #[cfg(feature = "aes_xts")] + Type::AES_256_XTS => (ffi::EVP_aes_256_xts(), 64, 16), + // AES_256_CTR => (EVP_aes_256_ctr(), 32, 0), + //AES_256_GCM => (EVP_aes_256_gcm(), 32, 16), + + Type::RC4_128 => (ffi::EVP_rc4(), 16, 0), + } + } +} + +/// Represents a symmetric cipher context. +pub struct Crypter { + evp: *const ffi::EVP_CIPHER, + ctx: *mut ffi::EVP_CIPHER_CTX, + keylen: u32, + blocksize: u32, +} + +impl Crypter { + pub fn new(t: Type) -> Crypter { + 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 } + } + + /** + * Enables or disables padding. If padding is disabled, total amount of + * data encrypted must be a multiple of block size. + */ + pub fn pad(&self, padding: bool) { + if self.blocksize > 0 { + unsafe { + let v = if padding { 1 as c_int } else { 0 }; + ffi::EVP_CIPHER_CTX_set_padding(self.ctx, v); + } + } + } + + /** + * Initializes this crypter. + */ + pub fn init(&self, mode: Mode, key: &[u8], iv: Vec) { + unsafe { + let mode = match mode { + Mode::Encrypt => 1 as c_int, + Mode::Decrypt => 0 as c_int, + }; + assert_eq!(key.len(), self.keylen as usize); + + ffi::EVP_CipherInit( + self.ctx, + self.evp, + key.as_ptr(), + iv.as_ptr(), + mode + ); + } + } + + /** + * Update this crypter with more data to encrypt or decrypt. Returns + * encrypted or decrypted bytes. + */ + pub fn update(&self, data: &[u8]) -> Vec { + unsafe { + let sum = data.len() + (self.blocksize as usize); + let mut res = repeat(0u8).take(sum).collect::>(); + let mut reslen = sum as c_int; + + ffi::EVP_CipherUpdate( + self.ctx, + res.as_mut_ptr(), + &mut reslen, + data.as_ptr(), + data.len() as c_int + ); + + res.truncate(reslen as usize); + res + } + } + + /** + * Finish crypting. Returns the remaining partial block of output, if any. + */ + pub fn finalize(&self) -> Vec { + unsafe { + let mut res = repeat(0u8).take(self.blocksize as usize).collect::>(); + let mut reslen = self.blocksize as c_int; + + ffi::EVP_CipherFinal(self.ctx, + res.as_mut_ptr(), + &mut reslen); + + res.truncate(reslen as usize); + res + } + } +} + +impl Drop for Crypter { + fn drop(&mut self) { + unsafe { + ffi::EVP_CIPHER_CTX_free(self.ctx); + } + } +} + +/** + * Encrypts data, using the specified crypter type in encrypt mode with the + * specified key and iv; returns the resulting (encrypted) data. + */ +pub fn encrypt(t: Type, key: &[u8], iv: Vec, data: &[u8]) -> Vec { + let c = Crypter::new(t); + c.init(Mode::Encrypt, key, iv); + let mut r = c.update(data); + let rest = c.finalize(); + r.extend(rest.into_iter()); + r +} + +/** + * Decrypts data, using the specified crypter type in decrypt mode with the + * specified key and iv; returns the resulting (decrypted) data. + */ +pub fn decrypt(t: Type, key: &[u8], iv: Vec, data: &[u8]) -> Vec { + let c = Crypter::new(t); + c.init(Mode::Decrypt, key, iv); + let mut r = c.update(data); + let rest = c.finalize(); + r.extend(rest.into_iter()); + r +} + +#[cfg(test)] +mod tests { + use serialize::hex::FromHex; + + // Test vectors from FIPS-197: + // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + #[test] + fn test_aes_256_ecb() { + let k0 = + vec!(0x00u8, 0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x05u8, 0x06u8, 0x07u8, + 0x08u8, 0x09u8, 0x0au8, 0x0bu8, 0x0cu8, 0x0du8, 0x0eu8, 0x0fu8, + 0x10u8, 0x11u8, 0x12u8, 0x13u8, 0x14u8, 0x15u8, 0x16u8, 0x17u8, + 0x18u8, 0x19u8, 0x1au8, 0x1bu8, 0x1cu8, 0x1du8, 0x1eu8, 0x1fu8); + let p0 = + vec!(0x00u8, 0x11u8, 0x22u8, 0x33u8, 0x44u8, 0x55u8, 0x66u8, 0x77u8, + 0x88u8, 0x99u8, 0xaau8, 0xbbu8, 0xccu8, 0xddu8, 0xeeu8, 0xffu8); + 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::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.finalize().into_iter()); + assert!(r0 == c0); + c.init(super::Mode::Decrypt, k0.as_slice(), vec![]); + c.pad(false); + let mut p1 = c.update(r0.as_slice()); + 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::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.finalize().into_iter()); + + if computed != expected { + println!("Computed: {}", computed.as_slice().to_hex()); + println!("Expected: {}", expected.as_slice().to_hex()); + if computed.len() != expected.len() { + println!("Lengths differ: {} in computed vs {} expected", + computed.len(), expected.len()); + } + panic!("test failure"); + } + } + + #[test] + fn test_rc4() { + + let pt = "0000000000000000000000000000000000000000000000000000000000000000000000000000"; + let ct = "A68686B04D686AA107BD8D4CAB191A3EEC0A6294BC78B60F65C25CB47BD7BB3A48EFC4D26BE4"; + let key = "97CD440324DA5FD1F7955C1C13B6B466"; + let 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] + fn test_aes128_ctr() { + + let pt = ~"6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710"; + let ct = ~"874D6191B620E3261BEF6864990DB6CE9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE"; + let key = ~"2B7E151628AED2A6ABF7158809CF4F3C"; + let iv = ~"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"; + + cipher_test(super::AES_128_CTR, pt, ct, key, iv); + }*/ + + /*#[test] + fn test_aes128_gcm() { + // Test case 3 in GCM spec + let pt = ~"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255"; + let ct = ~"42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f59854d5c2af327cd64a62cf35abd2ba6fab4"; + let key = ~"feffe9928665731c6d6a8f9467308308"; + let iv = ~"cafebabefacedbaddecaf888"; + + cipher_test(super::AES_128_GCM, pt, ct, key, iv); + }*/ +} diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs new file mode 100644 index 00000000..11234c2f --- /dev/null +++ b/openssl/src/lib.rs @@ -0,0 +1,20 @@ +#![feature(unsafe_destructor, core, io, std_misc, libc, hash, path)] +#![crate_name="openssl"] +#![crate_type="rlib"] +#![crate_type="dylib"] +#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/openssl")] + +extern crate libc; +#[cfg(test)] +extern crate "rustc-serialize" as serialize; + +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; diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs new file mode 100644 index 00000000..3e4bb429 --- /dev/null +++ b/openssl/src/macros.rs @@ -0,0 +1,59 @@ +#![macro_use] + +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)) +} diff --git a/openssl/src/ssl/error.rs b/openssl/src/ssl/error.rs new file mode 100644 index 00000000..027554c5 --- /dev/null +++ b/openssl/src/ssl/error.rs @@ -0,0 +1,122 @@ +pub use self::SslError::*; +pub use self::OpensslError::*; + +use libc::c_ulong; +use std::error; +use std::fmt; +use std::ffi::c_str_to_bytes; +use std::old_io::IoError; + +use ffi; + +/// An SSL error +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SslError { + /// The underlying stream reported an error + StreamError(IoError), + /// The SSL session has been closed by the other end + SslSessionClosed, + /// An error in the OpenSSL library + OpenSslErrors(Vec) +} + +impl fmt::Display for SslError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(error::Error::description(self)) + } +} + +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 +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum OpensslError { + /// An unknown error + UnknownError { + /// The library reporting the error + library: String, + /// The function reporting the error + function: String, + /// The reason for the error + reason: String + } +} + +fn get_lib(err: c_ulong) -> String { + unsafe { + let bytes = c_str_to_bytes(&ffi::ERR_lib_error_string(err)).to_vec(); + String::from_utf8(bytes).unwrap() + } +} + +fn get_func(err: c_ulong) -> String { + unsafe { + let bytes = c_str_to_bytes(&ffi::ERR_func_error_string(err)).to_vec(); + String::from_utf8(bytes).unwrap() + } +} + +fn get_reason(err: c_ulong) -> String { + unsafe { + let bytes = c_str_to_bytes(&ffi::ERR_reason_error_string(err)).to_vec(); + String::from_utf8(bytes).unwrap() + } +} + +impl SslError { + /// Creates a new `OpenSslErrors` with the current contents of the error + /// stack. + pub fn get() -> SslError { + let mut errs = vec!(); + loop { + match unsafe { ffi::ERR_get_error() } { + 0 => break, + 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"); +} diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs new file mode 100644 index 00000000..35649e24 --- /dev/null +++ b/openssl/src/ssl/mod.rs @@ -0,0 +1,643 @@ +use libc::{c_int, c_void, c_long}; +use std::ffi::{CString, c_str_to_bytes}; +use std::old_io::{IoResult, IoError, EndOfFile, Stream, Reader, Writer}; +use std::mem; +use std::fmt; +use std::num::FromPrimitive; +use std::ptr; +use std::sync::{Once, ONCE_INIT, Arc}; + +use bio::{MemBio}; +use ffi; +use ssl::error::{SslError, SslSessionClosed, StreamError}; +use x509::{X509StoreContext, X509FileType, X509}; + +pub mod error; +#[cfg(test)] +mod tests; + +static mut VERIFY_IDX: c_int = -1; + +fn init() { + static mut INIT: Once = ONCE_INIT; + + unsafe { + INIT.call_once(|| { + 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; + }); + } +} + +/// Determines the SSL method supported +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum SslMethod { + #[cfg(feature = "sslv2")] + /// 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, + #[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, +} + +impl SslMethod { + unsafe fn to_raw(&self) -> *const ffi::SSL_METHOD { + match *self { + #[cfg(feature = "sslv2")] + SslMethod::Sslv2 => ffi::SSLv2_method(), + SslMethod::Sslv3 => ffi::SSLv3_method(), + SslMethod::Tlsv1 => ffi::TLSv1_method(), + SslMethod::Sslv23 => ffi::SSLv23_method(), + #[cfg(feature = "tlsv1_1")] + SslMethod::Tlsv1_1 => ffi::TLSv1_1_method(), + #[cfg(feature = "tlsv1_2")] + SslMethod::Tlsv1_2 => ffi::TLSv1_2_method() + } + } +} + +/// Determines the type of certificate verification used +#[derive(Copy, Clone, Debug)] +#[repr(i32)] +pub enum SslVerifyMode { + /// Verify that the server's certificate is trusted + SslVerifyPeer = ffi::SSL_VERIFY_PEER, + /// Do not verify the server's certificate + SslVerifyNone = ffi::SSL_VERIFY_NONE +} + +// 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() -> c_int { + static mut VERIFY_DATA_IDX: c_int = -1; + static mut INIT: Once = ONCE_INIT; + + extern fn free_data_box(_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 = unsafe { mem::transmute(ptr) }; + } + + unsafe { + INIT.call_once(|| { + let f: ffi::CRYPTO_EX_free = free_data_box::; + let idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, + None, Some(f)); + assert!(idx >= 0); + VERIFY_DATA_IDX = idx; + }); + VERIFY_DATA_IDX + } +} + +extern fn raw_verify(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 = mem::transmute(verify); + + let ctx = X509StoreContext::new(x509_ctx); + + match verify { + None => preverify_ok, + Some(verify) => verify(preverify_ok != 0, &ctx) as c_int + } + } +} + +extern fn raw_verify_with_data(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> = mem::transmute(verify); + + let data = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::()); + let data: Box = 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; + +/// 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 = fn(preverify_ok: bool, + x509_ctx: &X509StoreContext, + data: &T) -> bool; + +// FIXME: macro may be instead of inlining? +#[inline] +fn wrap_ssl_result(res: c_int) -> Option { + if res == 0 { + Some(SslError::get()) + } else { + None + } +} + +/// An SSL context object +pub struct SslContext { + ctx: ptr::Unique +} + +// TODO: add useful info here +impl fmt::Debug for SslContext { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "SslContext") + } +} + +impl Drop for SslContext { + fn drop(&mut self) { + unsafe { ffi::SSL_CTX_free(self.ctx.0) } + } +} + +impl SslContext { + /// Creates a new SSL context. + pub fn new(method: SslMethod) -> Result { + init(); + + let ctx = unsafe { ffi::SSL_CTX_new(method.to_raw()) }; + if ctx == ptr::null_mut() { + return Err(SslError::get()); + } + + Ok(SslContext { ctx: ptr::Unique(ctx) }) + } + + /// Configures the certificate verification method for new connections. + pub fn set_verify(&mut self, mode: SslVerifyMode, + verify: Option) { + unsafe { + ffi::SSL_CTX_set_ex_data(self.ctx.0, VERIFY_IDX, + mem::transmute(verify)); + let f: extern fn(c_int, *mut ffi::X509_STORE_CTX) -> c_int = + raw_verify; + ffi::SSL_CTX_set_verify(self.ctx.0, mode as c_int, Some(f)); + } + } + + /// 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(&mut self, mode: SslVerifyMode, + verify: VerifyCallbackData, + data: T) { + let data = Box::new(data); + unsafe { + ffi::SSL_CTX_set_ex_data(self.ctx.0, VERIFY_IDX, + mem::transmute(Some(verify))); + ffi::SSL_CTX_set_ex_data(self.ctx.0, get_verify_data_idx::(), + mem::transmute(data)); + let f: extern fn(c_int, *mut ffi::X509_STORE_CTX) -> c_int = + raw_verify_with_data::; + ffi::SSL_CTX_set_verify(self.ctx.0, mode as c_int, Some(f)); + } + } + + /// Sets verification depth + pub fn set_verify_depth(&mut self, depth: u32) { + unsafe { + ffi::SSL_CTX_set_verify_depth(self.ctx.0, depth as c_int); + } + } + + #[allow(non_snake_case)] + /// Specifies the file that contains trusted CA certificates. + pub fn set_CA_file(&mut self, file: &Path) -> Option { + wrap_ssl_result( + unsafe { + let file = CString::from_slice(file.as_vec()); + ffi::SSL_CTX_load_verify_locations(self.ctx.0, file.as_ptr(), ptr::null()) + }) + } + + /// Specifies the file that contains certificate + pub fn set_certificate_file(&mut self, file: &Path, + file_type: X509FileType) -> Option { + wrap_ssl_result( + unsafe { + let file = CString::from_slice(file.as_vec()); + ffi::SSL_CTX_use_certificate_file(self.ctx.0, file.as_ptr(), file_type as c_int) + }) + } + + /// Specifies the file that contains private key + pub fn set_private_key_file(&mut self, file: &Path, + file_type: X509FileType) -> Option { + wrap_ssl_result( + unsafe { + let file = CString::from_slice(file.as_vec()); + ffi::SSL_CTX_use_PrivateKey_file(self.ctx.0, file.as_ptr(), file_type as c_int) + }) + } + + pub fn set_cipher_list(&mut self, cipher_list: &str) -> Option { + wrap_ssl_result( + unsafe { + let cipher_list = CString::from_slice(cipher_list.as_bytes()); + ffi::SSL_CTX_set_cipher_list(self.ctx.0, cipher_list.as_ptr()) + }) + } +} + +#[allow(dead_code)] +struct MemBioRef<'ssl> { + ssl: &'ssl Ssl, + bio: MemBio, +} + +impl<'ssl> MemBioRef<'ssl> { + fn read(&mut self, buf: &mut [u8]) -> Option { + (&mut self.bio as &mut Reader).read(buf).ok() + } + + fn write_all(&mut self, buf: &[u8]) { + let _ = (&mut self.bio as &mut Writer).write_all(buf); + } +} + +pub struct Ssl { + ssl: ptr::Unique +} + +// TODO: put useful information here +impl fmt::Debug for Ssl { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "Ssl") + } +} + +impl Drop for Ssl { + fn drop(&mut self) { + unsafe { ffi::SSL_free(self.ssl.0) } + } +} + +impl Ssl { + pub fn new(ctx: &SslContext) -> Result { + let ssl = unsafe { ffi::SSL_new(ctx.ctx.0) }; + if ssl == ptr::null_mut() { + return Err(SslError::get()); + } + let ssl = Ssl { ssl: ptr::Unique(ssl) }; + + let rbio = try!(MemBio::new()); + let wbio = try!(MemBio::new()); + + unsafe { ffi::SSL_set_bio(ssl.ssl.0, rbio.unwrap(), wbio.unwrap()) } + Ok(ssl) + } + + fn get_rbio<'a>(&'a self) -> MemBioRef<'a> { + unsafe { self.wrap_bio(ffi::SSL_get_rbio(self.ssl.0)) } + } + + fn get_wbio<'a>(&'a self) -> MemBioRef<'a> { + unsafe { self.wrap_bio(ffi::SSL_get_wbio(self.ssl.0)) } + } + + fn wrap_bio<'a>(&'a self, bio: *mut ffi::BIO) -> MemBioRef<'a> { + assert!(bio != ptr::null_mut()); + MemBioRef { + ssl: self, + bio: MemBio::borrowed(bio) + } + } + + fn connect(&self) -> c_int { + unsafe { ffi::SSL_connect(self.ssl.0) } + } + + fn accept(&self) -> c_int { + unsafe { ffi::SSL_accept(self.ssl.0) } + } + + fn read(&self, buf: &mut [u8]) -> c_int { + unsafe { ffi::SSL_read(self.ssl.0, buf.as_ptr() as *mut c_void, + buf.len() as c_int) } + } + + fn write_all(&self, buf: &[u8]) -> c_int { + unsafe { ffi::SSL_write(self.ssl.0, buf.as_ptr() as *const c_void, + buf.len() as c_int) } + } + + fn get_error(&self, ret: c_int) -> LibSslError { + let err = unsafe { ffi::SSL_get_error(self.ssl.0, ret) }; + match FromPrimitive::from_int(err as isize) { + Some(err) => err, + None => unreachable!() + } + } + + /// Set the host name to be used with SNI (Server Name Indication). + pub fn set_hostname(&self, hostname: &str) -> Result<(), SslError> { + let ret = unsafe { + // This is defined as a macro: + // #define SSL_set_tlsext_host_name(s,name) \ + // SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name) + + let hostname = CString::from_slice(hostname.as_bytes()); + ffi::SSL_ctrl(self.ssl.0, ffi::SSL_CTRL_SET_TLSEXT_HOSTNAME, + ffi::TLSEXT_NAMETYPE_host_name, + hostname.as_ptr() as *mut c_void) + }; + + // For this case, 0 indicates failure. + if ret == 0 { + Err(SslError::get()) + } else { + Ok(()) + } + } + + pub fn get_peer_certificate(&self) -> Option { + unsafe { + let ptr = ffi::SSL_get_peer_certificate(self.ssl.0); + if ptr.is_null() { + None + } else { + Some(X509::new(ptr, true)) + } + } + } + +} + +#[derive(FromPrimitive, Debug)] +#[repr(i32)] +enum LibSslError { + ErrorNone = ffi::SSL_ERROR_NONE, + ErrorSsl = ffi::SSL_ERROR_SSL, + ErrorWantRead = ffi::SSL_ERROR_WANT_READ, + ErrorWantWrite = ffi::SSL_ERROR_WANT_WRITE, + ErrorWantX509Lookup = ffi::SSL_ERROR_WANT_X509_LOOKUP, + ErrorSyscall = ffi::SSL_ERROR_SYSCALL, + ErrorZeroReturn = ffi::SSL_ERROR_ZERO_RETURN, + ErrorWantConnect = ffi::SSL_ERROR_WANT_CONNECT, + ErrorWantAccept = ffi::SSL_ERROR_WANT_ACCEPT, +} + +/// A stream wrapper which handles SSL encryption for an underlying stream. +#[derive(Clone)] +pub struct SslStream { + stream: S, + ssl: Arc, + buf: Vec +} + +impl fmt::Debug for SslStream where S: fmt::Debug { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "SslStream {{ stream: {:?}, ssl: {:?} }}", self.stream, self.ssl) + } +} + +impl SslStream { + fn new_base(ssl:Ssl, stream: S) -> SslStream { + SslStream { + stream: stream, + ssl: Arc::new(ssl), + // Maximum TLS record size is 16k + // We're just using this as a buffer, so there's no reason to pay + // to memset it + buf: { + const CAP: usize = 16 * 1024; + let mut v = Vec::with_capacity(CAP); + unsafe { v.set_len(CAP); } + v + } + } + } + + pub fn new_server_from(ssl: Ssl, stream: S) -> Result, 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, 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, SslError> { + 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, 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, mut blk: F) + -> Result where F: FnMut(&Ssl) -> c_int { + loop { + let ret = blk(&*self.ssl); + if ret > 0 { + return Ok(ret); + } + + match self.ssl.get_error(ret) { + 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_all(&self.buf[..len]); + } + LibSslError::ErrorWantWrite => { try_ssl_stream!(self.flush()) } + LibSslError::ErrorZeroReturn => return Err(SslSessionClosed), + LibSslError::ErrorSsl => return Err(SslError::get()), + err => panic!("unexpected error {:?}", err), + } + } + } + + fn write_through(&mut self) -> IoResult<()> { + loop { + match self.ssl.get_wbio().read(self.buf.as_mut_slice()) { + Some(len) => try!(self.stream.write_all(&self.buf[..len])), + None => break + }; + } + Ok(()) + } + + /// Get the compression currently in use. The result will be + /// either None, indicating no compression is in use, or a string + /// with the compression name. + pub fn get_compression(&self) -> Option { + let ptr = unsafe { ffi::SSL_get_current_compression(self.ssl.ssl.0) }; + if ptr == ptr::null() { + return None; + } + + let meth = unsafe { ffi::SSL_COMP_get_name(ptr) }; + let s = unsafe { + String::from_utf8(c_str_to_bytes(&meth).to_vec()).unwrap() + }; + + Some(s) + } +} + +impl Reader for SslStream { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + match self.in_retry_wrapper(|ssl| { ssl.read(buf) }) { + Ok(len) => Ok(len as usize), + Err(SslSessionClosed) => + Err(IoError { + kind: EndOfFile, + desc: "SSL session closed", + detail: None + }), + Err(StreamError(e)) => Err(e), + _ => unreachable!() + } + } +} + +impl Writer for SslStream { + fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { + let mut start = 0; + while start < buf.len() { + let ret = self.in_retry_wrapper(|ssl| { + ssl.write_all(buf.split_at(start).1) + }); + match ret { + Ok(len) => start += len as usize, + _ => unreachable!() + } + try!(self.write_through()); + } + Ok(()) + } + + fn flush(&mut self) -> IoResult<()> { + try!(self.write_through()); + self.stream.flush() + } +} + +/// A utility type to help in cases where the use of SSL is decided at runtime. +#[derive(Debug)] +pub enum MaybeSslStream where S: Stream { + /// A connection using SSL + Ssl(SslStream), + /// A connection not using SSL + Normal(S), +} + +impl Reader for MaybeSslStream where S: Stream { + fn read(&mut self, buf: &mut [u8]) -> IoResult { + match *self { + MaybeSslStream::Ssl(ref mut s) => s.read(buf), + MaybeSslStream::Normal(ref mut s) => s.read(buf), + } + } +} + +impl Writer for MaybeSslStream where S: Stream{ + fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { + match *self { + MaybeSslStream::Ssl(ref mut s) => s.write_all(buf), + MaybeSslStream::Normal(ref mut s) => s.write_all(buf), + } + } + + fn flush(&mut self) -> IoResult<()> { + match *self { + MaybeSslStream::Ssl(ref mut s) => s.flush(), + MaybeSslStream::Normal(ref mut s) => s.flush(), + } + } +} + +impl MaybeSslStream 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, + } + } +} diff --git a/openssl/src/ssl/tests.rs b/openssl/src/ssl/tests.rs new file mode 100644 index 00000000..73f479bf --- /dev/null +++ b/openssl/src/ssl/tests.rs @@ -0,0 +1,207 @@ +use serialize::hex::FromHex; +use std::old_io::net::tcp::TcpStream; +use std::old_io::{Writer}; +use std::thread::Thread; + +use crypto::hash::Type::{SHA256}; +use ssl::SslMethod::Sslv23; +use ssl::{SslContext, SslStream, VerifyCallback}; +use ssl::SslVerifyMode::SslVerifyPeer; +use x509::{X509StoreContext}; + +#[test] +fn test_new_ctx() { + SslContext::new(Sslv23).unwrap(); +} + +#[test] +fn test_new_sslstream() { + 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 mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SslVerifyPeer, None); + match SslStream::new(&ctx, stream) { + 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 mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SslVerifyPeer, None); + match ctx.set_CA_file(&Path::new("test/cert.pem")) { + None => {} + Some(err) => panic!("Unexpected error {:?}", err) + } + match SslStream::new(&ctx, stream) { + Ok(_) => (), + Err(err) => panic!("Expected success, got {:?}", err) + } +} + +#[test] +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 mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); + match SslStream::new(&ctx, stream) { + Ok(_) => (), + Err(err) => panic!("Expected success, got {:?}", err) + } +} + +#[test] +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 mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); + assert!(SslStream::new(&ctx, stream).is_err()); +} + +#[test] +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 mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); + match ctx.set_CA_file(&Path::new("test/cert.pem")) { + None => {} + Some(err) => panic!("Unexpected error {:?}", err) + } + match SslStream::new(&ctx, stream) { + Ok(_) => (), + Err(err) => panic!("Expected success, got {:?}", err) + } +} + +#[test] +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 mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); + match ctx.set_CA_file(&Path::new("test/cert.pem")) { + None => {} + Some(err) => panic!("Unexpected error {:?}", err) + } + assert!(SslStream::new(&ctx, stream).is_err()); +} + +#[test] +fn test_verify_callback_load_certs() { + fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { + assert!(x509_ctx.get_current_cert().is_some()); + true + } + let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); + let mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); + assert!(SslStream::new(&ctx, stream).is_ok()); +} + +#[test] +fn test_verify_trusted_get_error_ok() { + fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { + assert!(x509_ctx.get_error().is_none()); + true + } + let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); + let mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); + match ctx.set_CA_file(&Path::new("test/cert.pem")) { + None => {} + Some(err) => panic!("Unexpected error {:?}", err) + } + assert!(SslStream::new(&ctx, stream).is_ok()); +} + +#[test] +fn test_verify_trusted_get_error_err() { + fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { + assert!(x509_ctx.get_error().is_some()); + false + } + let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); + let mut ctx = SslContext::new(Sslv23).unwrap(); + ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); + assert!(SslStream::new(&ctx, stream).is_err()); +} + +#[test] +fn test_verify_callback_data() { + fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext, node_id: &Vec) -> 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 mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); + stream.write_all("hello".as_bytes()).unwrap(); + stream.flush().unwrap(); + stream.write_all(" there".as_bytes()).unwrap(); + stream.flush().unwrap(); +} + +#[test] +fn test_read() { + let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); + let mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); + stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); + stream.flush().unwrap(); + 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(); + let _t = Thread::spawn(move || { + stream2.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); + stream2.flush().unwrap(); + }); + stream.read_to_end().ok().expect("read error"); +} diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs new file mode 100644 index 00000000..6c09f8b2 --- /dev/null +++ b/openssl/src/x509/mod.rs @@ -0,0 +1,525 @@ +use libc::{c_char, c_int, c_long, c_uint}; +use std::cmp::Ordering; +use std::ffi::CString; +use std::iter::repeat; +use std::mem; +use std::num::SignedInt; +use std::ptr; + +use asn1::{Asn1Time}; +use bio::{MemBio}; +use crypto::hash; +use crypto::hash::Type as HashType; +use crypto::pkey::{PKey}; +use crypto::rand::rand_bytes; +use ffi; +use ssl::error::{SslError, StreamError}; + + +#[cfg(test)] +mod tests; + +#[derive(Copy)] +#[repr(i32)] +pub enum X509FileType { + PEM = ffi::X509_FILETYPE_PEM, + ASN1 = ffi::X509_FILETYPE_ASN1, + Default = ffi::X509_FILETYPE_DEFAULT +} + +#[allow(missing_copy_implementations)] +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 { + let err = unsafe { ffi::X509_STORE_CTX_get_error(self.ctx) }; + X509ValidationError::from_raw(err) + } + + pub fn get_current_cert<'a>(&'a self) -> Option> { + 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; +} + +#[derive(Clone, Copy)] +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" + } + } +} + + +#[derive(Clone, Copy)] +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 { + 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 +/// +/// ``` +/// # #[allow(unstable)] +/// # fn main() { +/// use std::old_io::{File, Open, Write}; +/// # use std::old_io::fs; +/// +/// use openssl::crypto::hash::Type; +/// use openssl::x509::{KeyUsage, X509Generator}; +/// +/// let gen = X509Generator::new() +/// .set_bitlength(2048) +/// .set_valid_period(365*2) +/// .set_CN("SuperMegaCorp Inc.") +/// .set_sign_hash(Type::SHA256) +/// .set_usage(&[KeyUsage::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: u32, + days: u32, + CN: String, + key_usage: Vec, + ext_key_usage: Vec, + 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: u32) -> X509Generator { + self.bits = bits; + self + } + + /// Sets certificate validity period in days since today + pub fn set_valid_period(mut self, days: u32) -> 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: hash::Type) -> 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 value = CString::from_slice(value.as_bytes()); + let ext = ffi::X509V3_EXT_conf_nid(ptr::null_mut(), + mem::transmute(&ctx), + extension, + value.as_ptr() as *mut c_char); + + 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!(unsafe { + let key = CString::from_slice(key.as_bytes()); + let value = CString::from_slice(value.as_bytes()); + ffi::X509_NAME_add_entry_by_txt(name, key.as_ptr(), ffi::MBSTRING_UTF8, + value.as_ptr(), value_len, -1, 0) + }) + } + + fn random_serial() -> c_long { + let len = mem::size_of::(); + 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 as usize); + + 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 = self.hash_type.evp_md(); + 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 R) -> Result, SslError> where R: Reader { + let mut mem_bio = try!(MemBio::new()); + let buf = try!(reader.read_to_end().map_err(StreamError)); + try!(mem_bio.write_all(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: hash::Type) -> Option> { + let evp = hash_type.evp_md(); + let len = hash_type.md_len(); + let v: Vec = repeat(0).take(len as usize).collect(); + 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 usize; + match len.cmp(&act_len) { + Ordering::Greater => None, + Ordering::Equal => Some(v), + Ordering::Less => panic!("Fingerprint buffer was corrupted!") + } + } + } + } + + /// Writes certificate as PEM + pub fn write_pem(&self, writer: &mut W) -> Result<(), SslError> where W: Writer{ + 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_all(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,)+) => ( + #[derive(Copy)] + pub enum X509ValidationError { + $($name,)+ + X509UnknownError(c_int) + } + + impl X509ValidationError { + #[doc(hidden)] + pub fn from_raw(err: c_int) -> Option { + 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(0, 1000) { + assert!(X509Generator::random_serial() > 0, "All serials should be positive"); + } +} diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs new file mode 100644 index 00000000..4f24e70c --- /dev/null +++ b/openssl/src/x509/tests.rs @@ -0,0 +1,51 @@ +use serialize::hex::FromHex; +use std::old_io::{File, Open, Read}; +use std::old_io::util::NullWriter; + +use crypto::hash::Type::{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()); +} diff --git a/openssl/test/cert.pem b/openssl/test/cert.pem new file mode 100644 index 00000000..1523c736 --- /dev/null +++ b/openssl/test/cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAMWJMG/NYWQyMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTQxMTIwMDU0MjIxWhcNMTcxMTE5MDU0MjIxWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2E9uYoLpX4eMGz6+l+1ZdD11Y1PQjNjqSA7/nq0Q6gogPLds99a+Ca7B +2w6mWGMpCQbJTf0tOkdF6Td/gwIBNYtHuCIAiPh2Gbm6oZErVIXWwWuTWC2r7myB +0ePga5ZAE9SqsFqMEuhWikEK1+ae1CCfmbogsQSXyl4+EVN7xAwdi6yUtRL/92nn +ImKdwDBhuqzdBpBODQW/VCn0KG54CvWdwT0iqg7CvLuXQGyxM8K17SAiBtn6N38S +0jeL4D9IrBfi9PCYGpAn3jKr4oBEJVRCNvvni3Q9ikJ+GEF5nvLLVM+I98/o0vCu +Y2o7+1LiwViGONJ1BfRgdFWMjz0BfQIDAQABo1AwTjAdBgNVHQ4EFgQU509vewJT +k6qdvjT8e52gTE4+V9EwHwYDVR0jBBgwFoAU509vewJTk6qdvjT8e52gTE4+V9Ew +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAnLxMvrhgQrdKUbd+RSl3 +i6tEfexDepO5QOMIL/m7w/OAkXddu7Y/IuLSgAztp7cu8tQeeBTqbO9T9XKRd1ZD +hMN3QPZwX1ftREPd1mhe3LQgJ/Q3AbrtlW+0XcANBCTiYi19X193sU2a3Z2nzd5v +vSvrP1W3Yxzvu7Jwvhgl20BQ0fREpPVfZTrJRhg8/jdLA8TmKZA7012LKh59BCEX +dXgXc3KYb+Fn8usj79P1nP1YhYmX0Lp0IuC0i1z+FoP/NxNMe+ebXPjM9KlkvQK1 +Vy9sIxV9MAlXpGsMvBJAte94JRikYkQTPjMT1DGQQYGpHIYb3a8jcWvmZPEEaqhB +Ag== +-----END CERTIFICATE----- diff --git a/openssl/test/key.pem b/openssl/test/key.pem new file mode 100644 index 00000000..a3f80dd9 --- /dev/null +++ b/openssl/test/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDYT25igulfh4wb +Pr6X7Vl0PXVjU9CM2OpIDv+erRDqCiA8t2z31r4JrsHbDqZYYykJBslN/S06R0Xp +N3+DAgE1i0e4IgCI+HYZubqhkStUhdbBa5NYLavubIHR4+BrlkAT1KqwWowS6FaK +QQrX5p7UIJ+ZuiCxBJfKXj4RU3vEDB2LrJS1Ev/3aeciYp3AMGG6rN0GkE4NBb9U +KfQobngK9Z3BPSKqDsK8u5dAbLEzwrXtICIG2fo3fxLSN4vgP0isF+L08JgakCfe +MqvigEQlVEI2++eLdD2KQn4YQXme8stUz4j3z+jS8K5jajv7UuLBWIY40nUF9GB0 +VYyPPQF9AgMBAAECggEBAJiYiH20bqA2xk8eF2SkSxvmk15r7U6/Y59L/WZaHvmM +BSvwFk5MzqmUACviDNWDtpookHCVL4fSae5ZeXnZOzMju4eZbRkzdlU1ogSCnbe1 +50dx9XMaXRUItRh1koczaqbSu0tHxVM9VneX5OdkSR3Kmezf0lourEpV66FbbI9i +1F1Q7u6TzldTuPSkQQgV/FHU9DvRPJ6HgSOyVr6Z9Ec0K6odmUXe3wX7+3PbKPtr +JIVQ0wGcc/sImgAr0uS+YbHNWM4qjFAAPteQ/+Df6usSFOkRoD3+XeZrJQQ98C3q +HHW4afaJM0YCsDwn7/E3KiY5fmXwtAHNRuUbsfReP8ECgYEA9JQICyP1N5bZ4uCc +jjTiHLcQX2dHy4dKatqWkV4F52qf4HCZ/pcvPBKNMzM4uTfkCgR+TMzW+A+escvR +8KmaSKmQHT+nUQfMoU2lpZbWSPTF8lLGx+Mf8JAMur0xcmazDB8uDFnvQg+TQY7y +cF6MMWKW3pp+3qI7wRkclXSLZG0CgYEA4mlzuzuB8e7UJ821N+zD8BBYY4EvpUIj +iparwKbM8vAZ1WZssRd+8oHroHJGbjXX4h7gvpUsVadSgs77W9T0zJ+5kJCpVAnO +nKdJkX1Zo1TaIIrRaJhiaPU4hKlnGnko3uv7SlV9PPUtcyBnXElobREmQv6eCmEf +Z7YP4+JoR1ECgYEA3RyrfO7gNYZyq3Mm9kWHGjDCY43q0W0ZcSr3LqrTKZkyuuTx +w8IImQWok9497O1Dg272hBY4ToFIljLPNQUQD5sER/0RFee4LygUlnSce86W2nHN +dk62xHRmnbiHaIbCXjYeGlqAPLf6CC3kroQ7uDYKcWs5Qatn3DYIqnF3x60CgYA/ +plWatUf6s6GA7xua9TzAKFgw4Qh79PP46hKuvjWvtkAM9hZoUqqlklCjcnzKTui5 +8ORNr7IfAkL38yhG0L9hJyYLth9kOL2U3JKaDBs/B4Oq0lu8g9pml0mkQdtyXc1X +ng+u/gmPMX3td5aXIyvwPXn8K4hScqtZhJ1C+0tFgQKBgQDtlBXw3mY3AMwT+wtx +ZiitDk7tAXj7wioCOziTFKkL01UMt4VIkNvQU7RUTcig6PBc0PxU6TZQpncOk/cP +eqQQP0TJGNzIK71EwAL5aFm9o9VoXsrwjVDZnzMr703MyU15QXO76kmxmh+rK5Qy +uldCJliojIW1UW30MXSXK96YXQ== +-----END PRIVATE KEY----- diff --git a/src/asn1/mod.rs b/src/asn1/mod.rs deleted file mode 100644 index c250096f..00000000 --- a/src/asn1/mod.rs +++ /dev/null @@ -1,49 +0,0 @@ -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 { - 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: u32) -> Result { - 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) }; - } - } -} diff --git a/src/bio/mod.rs b/src/bio/mod.rs deleted file mode 100644 index 2f12f906..00000000 --- a/src/bio/mod.rs +++ /dev/null @@ -1,105 +0,0 @@ -use libc::{c_void, c_int}; -use std::old_io::{EndOfFile, IoResult, IoError, OtherIoError}; -use std::old_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 { - 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 { - 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 usize) - } - } -} - -impl Writer for MemBio { - fn write_all(&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 usize { - Err(IoError { - kind: OtherIoError, - desc: "MemBio write error", - detail: Some(format!("{:?}", SslError::get())) - }) - } else { - Ok(()) - } - } -} diff --git a/src/bn/mod.rs b/src/bn/mod.rs deleted file mode 100644 index 3a5f231e..00000000 --- a/src/bn/mod.rs +++ /dev/null @@ -1,604 +0,0 @@ -use libc::{c_int, c_ulong, c_void}; -use std::ffi::{CString, c_str_to_bytes}; -use std::cmp::Ordering; -use std::{fmt, ptr}; - -use ffi; -use ssl::error::SslError; - -pub struct BigNum(*mut ffi::BIGNUM); - -#[derive(Copy)] -#[repr(C)] -pub enum RNGProperty { - MsbMaybeZero = -1, - MsbOne = 0, - TwoMsbOne = 1, -} - -macro_rules! with_ctx( - ($name:ident, $action:block) => ({ - let $name = ffi::BN_CTX_new(); - if ($name).is_null() { - Err(SslError::get()) - } else { - let r = $action; - ffi::BN_CTX_free($name); - r - } - }); -); - -macro_rules! with_bn( - ($name:ident, $action:block) => ({ - let tmp = BigNum::new(); - match tmp { - Ok($name) => { - if $action { - Ok($name) - } else { - Err(SslError::get()) - } - }, - Err(err) => Err(err), - } - }); -); - -macro_rules! with_bn_in_ctx( - ($name:ident, $ctx_name:ident, $action:block) => ({ - let tmp = BigNum::new(); - match tmp { - Ok($name) => { - let $ctx_name = ffi::BN_CTX_new(); - if ($ctx_name).is_null() { - Err(SslError::get()) - } else { - let r = - if $action { - Ok($name) - } else { - Err(SslError::get()) - }; - ffi::BN_CTX_free($ctx_name); - r - } - }, - Err(err) => Err(err), - } - }); -); - -impl BigNum { - pub fn new() -> Result { - unsafe { - ffi::init(); - - let v = try_ssl_null!(ffi::BN_new()); - Ok(BigNum(v)) - } - } - - pub fn new_from(n: u64) -> Result { - BigNum::new().and_then(|v| unsafe { - try_ssl!(ffi::BN_set_word(v.raw(), n as c_ulong)); - Ok(v) - }) - } - - pub fn from_dec_str(s: &str) -> Result { - BigNum::new().and_then(|v| unsafe { - let c_str = CString::from_slice(s.as_bytes()); - try_ssl!(ffi::BN_dec2bn(v.raw_ptr(), c_str.as_ptr())); - Ok(v) - }) - } - - pub fn from_hex_str(s: &str) -> Result { - BigNum::new().and_then(|v| unsafe { - let c_str = CString::from_slice(s.as_bytes()); - try_ssl!(ffi::BN_hex2bn(v.raw_ptr(), c_str.as_ptr())); - Ok(v) - }) - } - - pub fn new_from_slice(n: &[u8]) -> Result { - 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 { - unsafe { - with_bn_in_ctx!(r, ctx, { ffi::BN_sqr(r.raw(), self.raw(), ctx) == 1 }) - } - } - - pub fn checked_nnmod(&self, n: &BigNum) -> Result { - unsafe { - 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 { - unsafe { - 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 { - unsafe { - 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 { - unsafe { - 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 { - unsafe { - 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 { - unsafe { - 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 { - unsafe { - 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 { - unsafe { - with_bn_in_ctx!(r, ctx, { !ffi::BN_mod_inverse(r.raw(), self.raw(), n.raw(), ctx).is_null() }) - } - } - - pub fn add_word(&mut self, w: c_ulong) -> Result<(), SslError> { - unsafe { - if ffi::BN_add_word(self.raw(), w) == 1 { - Ok(()) - } else { - Err(SslError::get()) - } - } - } - - pub fn sub_word(&mut self, w: c_ulong) -> Result<(), SslError> { - unsafe { - if ffi::BN_sub_word(self.raw(), w) == 1 { - Ok(()) - } else { - Err(SslError::get()) - } - } - } - - pub fn mul_word(&mut self, w: c_ulong) -> Result<(), SslError> { - unsafe { - if ffi::BN_mul_word(self.raw(), w) == 1 { - Ok(()) - } else { - Err(SslError::get()) - } - } - } - - pub fn div_word(&mut self, w: c_ulong) -> Result { - unsafe { - let result = ffi::BN_div_word(self.raw(), w); - if result != -1 as c_ulong { - Ok(result) - } else { - Err(SslError::get()) - } - } - } - - pub fn mod_word(&self, w: c_ulong) -> Result { - unsafe { - let result = ffi::BN_mod_word(self.raw(), w); - if result != -1 as c_ulong { - Ok(result) - } else { - Err(SslError::get()) - } - } - } - - pub fn checked_gcd(&self, a: &BigNum) -> Result { - unsafe { - 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 { - unsafe { - with_bn_in_ctx!(r, ctx, { - 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()); - - ffi::BN_generate_prime_ex(r.raw(), bits as c_int, safe as c_int, add_arg, rem_arg, ptr::null()) == 1 - }) - } - } - - pub fn is_prime(&self, checks: i32) -> Result { - unsafe { - with_ctx!(ctx, { - Ok(ffi::BN_is_prime_ex(self.raw(), checks as c_int, ctx, ptr::null()) == 1) - }) - } - } - - pub fn is_prime_fast(&self, checks: i32, do_trial_division: bool) -> Result { - unsafe { - with_ctx!(ctx, { - 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 { - unsafe { - 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 { - unsafe { - 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 { - unsafe { - with_bn_in_ctx!(r, ctx, { ffi::BN_rand_range(r.raw(), self.raw()) == 1 }) - } - } - - pub fn checked_pseudo_rand_in_range(&self) -> Result { - unsafe { - 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 ffi::BN_set_bit(self.raw(), n as c_int) == 1 { - Ok(()) - } else { - Err(SslError::get()) - } - } - } - - pub fn clear_bit(&mut self, n: i32) -> Result<(), SslError> { - unsafe { - if ffi::BN_clear_bit(self.raw(), n as c_int) == 1 { - Ok(()) - } else { - Err(SslError::get()) - } - } - } - - pub fn is_bit_set(&self, n: i32) -> bool { - unsafe { - ffi::BN_is_bit_set(self.raw(), n as c_int) == 1 - } - } - - pub fn mask_bits(&mut self, n: i32) -> Result<(), SslError> { - unsafe { - if ffi::BN_mask_bits(self.raw(), n as c_int) == 1 { - Ok(()) - } else { - Err(SslError::get()) - } - } - } - - pub fn checked_shl1(&self) -> Result { - unsafe { - with_bn!(r, { ffi::BN_lshift1(r.raw(), self.raw()) == 1 }) - } - } - - pub fn checked_shr1(&self) -> Result { - unsafe { - with_bn!(r, { ffi::BN_rshift1(r.raw(), self.raw()) == 1 }) - } - } - - pub fn checked_add(&self, a: &BigNum) -> Result { - unsafe { - with_bn!(r, { ffi::BN_add(r.raw(), self.raw(), a.raw()) == 1 }) - } - } - - pub fn checked_sub(&self, a: &BigNum) -> Result { - unsafe { - with_bn!(r, { ffi::BN_sub(r.raw(), self.raw(), a.raw()) == 1 }) - } - } - - pub fn checked_mul(&self, a: &BigNum) -> Result { - unsafe { - 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 { - unsafe { - 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 { - unsafe { - 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 { - unsafe { - with_bn!(r, { ffi::BN_lshift(r.raw(), self.raw(), *a as c_int) == 1 }) - } - } - - pub fn checked_shr(&self, a: &i32) -> Result { - unsafe { - with_bn!(r, { ffi::BN_rshift(r.raw(), self.raw(), *a as c_int) == 1 }) - } - } - - pub fn negate(&mut self) { - unsafe { - ffi::BN_set_negative(self.raw(), !self.is_negative() as c_int) - } - } - - pub fn abs_cmp(&self, oth: BigNum) -> Ordering { - unsafe { - let res = ffi::BN_ucmp(self.raw(), oth.raw()) as i32; - if res < 0 { - Ordering::Less - } else if res > 0 { - Ordering::Greater - } else { - Ordering::Equal - } - } - } - - pub fn is_negative(&self) -> bool { - unsafe { - (*self.raw()).neg == 1 - } - } - - pub fn num_bits(&self) -> i32 { - unsafe { - ffi::BN_num_bits(self.raw()) as i32 - } - } - - pub fn num_bytes(&self) -> i32 { - (self.num_bits() + 7) / 8 - } - - unsafe fn raw(&self) -> *mut ffi::BIGNUM { - let BigNum(n) = *self; - n - } - - unsafe fn raw_ptr(&self) -> *const *mut ffi::BIGNUM { - let BigNum(ref n) = *self; - n - } - - pub fn to_vec(&self) -> Vec { - let size = self.num_bytes() as usize; - let mut v = Vec::with_capacity(size); - unsafe { - ffi::BN_bn2bin(self.raw(), v.as_mut_ptr()); - v.set_len(size); - } - v - } - - pub fn to_dec_str(&self) -> String { - unsafe { - let buf = ffi::BN_bn2dec(self.raw()); - assert!(!buf.is_null()); - let str = String::from_utf8(c_str_to_bytes(&buf).to_vec()).unwrap(); - ffi::CRYPTO_free(buf as *mut c_void); - str - } - } - - pub fn to_hex_str(&self) -> String { - unsafe { - let buf = ffi::BN_bn2hex(self.raw()); - assert!(!buf.is_null()); - let str = String::from_utf8(c_str_to_bytes(&buf).to_vec()).unwrap(); - ffi::CRYPTO_free(buf as *mut c_void); - str - } - } -} - -impl fmt::Debug for BigNum { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_dec_str()) - } -} - -impl Eq for BigNum { } -impl PartialEq for BigNum { - fn eq(&self, oth: &BigNum) -> bool { - unsafe { - ffi::BN_cmp(self.raw(), oth.raw()) == 0 - } - } -} - -impl Ord for BigNum { - fn cmp(&self, oth: &BigNum) -> Ordering { - self.partial_cmp(oth).unwrap() - } -} - -impl PartialOrd for BigNum { - fn partial_cmp(&self, oth: &BigNum) -> Option { - unsafe { - let v = ffi::BN_cmp(self.raw(), oth.raw()); - let ret = - if v == 0 { - Ordering::Equal - } else if v < 0 { - Ordering::Less - } else { - Ordering::Greater - }; - Some(ret) - } - } -} - -impl Drop for BigNum { - fn drop(&mut self) { - unsafe { - if !self.raw().is_null() { - ffi::BN_clear_free(self.raw()); - } - } - } -} - -pub mod unchecked { - use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub}; - use ffi; - use super::{BigNum}; - - impl<'a> Add<&'a BigNum> for &'a BigNum { - type Output = BigNum; - - fn add(self, oth: &'a BigNum) -> BigNum { - self.checked_add(oth).unwrap() - } - } - - impl<'a> Sub<&'a BigNum> for &'a BigNum { - type Output = BigNum; - - fn sub(self, oth: &'a BigNum) -> BigNum { - self.checked_sub(oth).unwrap() - } - } - - impl<'a> Mul<&'a BigNum> for &'a BigNum { - type Output = BigNum; - - fn mul(self, oth: &'a BigNum) -> BigNum { - self.checked_mul(oth).unwrap() - } - } - - impl<'a> Div<&'a BigNum> for &'a BigNum { - type Output = BigNum; - - fn div(self, oth: &'a BigNum) -> BigNum { - self.checked_div(oth).unwrap() - } - } - - impl<'a> Rem<&'a BigNum> for &'a BigNum { - type Output = BigNum; - - fn rem(self, oth: &'a BigNum) -> BigNum { - self.checked_mod(oth).unwrap() - } - } - - impl<'a> Shl for &'a BigNum { - type Output = BigNum; - - fn shl(self, n: i32) -> BigNum { - self.checked_shl(&n).unwrap() - } - } - - impl<'a> Shr for &'a BigNum { - type Output = BigNum; - - fn shr(self, n: i32) -> BigNum { - self.checked_shr(&n).unwrap() - } - } - - impl Clone for BigNum { - fn clone(&self) -> BigNum { - unsafe { - let r = ffi::BN_dup(self.raw()); - if r.is_null() { - panic!("Unexpected null pointer from BN_dup(..)") - } else { - BigNum(r) - } - } - } - } - - impl Neg for BigNum { - type Output = BigNum; - - fn neg(self) -> BigNum { - let mut n = self.clone(); - n.negate(); - n - } - } -} - -#[cfg(test)] -mod tests { - use bn::BigNum; - - #[test] - fn test_to_from_slice() { - let v0 = BigNum::new_from(10203004_u64).unwrap(); - let vec = v0.to_vec(); - let v1 = BigNum::new_from_slice(vec.as_slice()).unwrap(); - - assert!(v0 == v1); - } - - #[test] - fn test_negation() { - let a = BigNum::new_from(909829283_u64).unwrap(); - - assert!(!a.is_negative()); - assert!((-a).is_negative()); - } - - - #[test] - fn test_prime_numbers() { - let a = BigNum::new_from(19029017_u64).unwrap(); - let p = BigNum::checked_generate_prime(128, true, None, Some(&a)).unwrap(); - - assert!(p.is_prime(100).unwrap()); - assert!(p.is_prime_fast(100, true).unwrap()); - } -} diff --git a/src/crypto/hash.rs b/src/crypto/hash.rs deleted file mode 100644 index f81532c9..00000000 --- a/src/crypto/hash.rs +++ /dev/null @@ -1,333 +0,0 @@ -use libc::c_uint; -use std::iter::repeat; -use std::old_io::{IoError, Writer}; - -use ffi; - -/// Message digest (hash) type. -#[derive(Copy)] -pub enum Type { - MD5, - SHA1, - SHA224, - SHA256, - SHA384, - SHA512, - RIPEMD160 -} - -impl Type { - /// Returns the length of the message digest. - #[inline] - pub fn md_len(&self) -> usize { - use self::Type::*; - match *self { - MD5 => 16, - SHA1 => 20, - SHA224 => 28, - SHA256 => 32, - SHA384 => 48, - SHA512 => 64, - RIPEMD160 => 20, - } - } - - /// Internal interface subject to removal. - #[inline] - pub fn evp_md(&self) -> *const ffi::EVP_MD { - unsafe { - use self::Type::*; - match *self { - MD5 => ffi::EVP_md5(), - SHA1 => ffi::EVP_sha1(), - SHA224 => ffi::EVP_sha224(), - SHA256 => ffi::EVP_sha256(), - SHA384 => ffi::EVP_sha384(), - SHA512 => ffi::EVP_sha512(), - RIPEMD160 => ffi::EVP_ripemd160(), - } - } - } -} - -#[derive(PartialEq, Copy)] -enum State { - Reset, - Updated, - Finalized, -} - -use self::State::*; - -/// Provides message digest (hash) computation. -/// -/// # Examples -/// -/// Calculate a hash in one go. -/// -/// ``` -/// use openssl::crypto::hash::{hash, Type}; -/// let data = b"\x42\xF4\x97\xE0"; -/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; -/// let res = hash(Type::MD5, data); -/// assert_eq!(res, spec); -/// ``` -/// -/// Use the `Writer` trait to supply the input in chunks. -/// -/// ``` -/// use std::old_io::Writer; -/// use openssl::crypto::hash::{Hasher, Type}; -/// let data = [b"\x42\xF4", b"\x97\xE0"]; -/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; -/// let mut h = Hasher::new(Type::MD5); -/// h.write_all(data[0]); -/// h.write_all(data[1]); -/// let res = h.finish(); -/// assert_eq!(res, spec); -/// ``` -/// -/// # Warning -/// -/// Don't actually use MD5 and SHA-1 hashes, they're not secure anymore. -/// -/// Don't ever hash passwords, use `crypto::pkcs5` or bcrypt/scrypt instead. -pub struct Hasher { - ctx: *mut ffi::EVP_MD_CTX, - md: *const ffi::EVP_MD, - type_: Type, - state: State, -} - -impl Hasher { - /// Creates a new `Hasher` with the specified hash type. - pub fn new(ty: Type) -> Hasher { - ffi::init(); - - let ctx = unsafe { - let r = ffi::EVP_MD_CTX_create(); - assert!(!r.is_null()); - r - }; - let md = ty.evp_md(); - - let mut h = Hasher { ctx: ctx, md: md, type_: ty, state: Finalized }; - h.init(); - h - } - - #[inline] - fn init(&mut self) { - match self.state { - Reset => return, - Updated => { self.finalize(); }, - Finalized => (), - } - unsafe { - let r = ffi::EVP_DigestInit_ex(self.ctx, self.md, 0 as *const _); - assert_eq!(r, 1); - } - self.state = Reset; - } - - #[inline] - fn update(&mut self, data: &[u8]) { - if self.state == Finalized { - self.init(); - } - unsafe { - let r = ffi::EVP_DigestUpdate(self.ctx, data.as_ptr(), - data.len() as c_uint); - assert_eq!(r, 1); - } - self.state = Updated; - } - - #[inline] - fn finalize(&mut self) -> Vec { - if self.state == Finalized { - self.init(); - } - let md_len = self.type_.md_len(); - let mut res: Vec = repeat(0).take(md_len).collect(); - unsafe { - let mut len = 0; - let r = ffi::EVP_DigestFinal_ex(self.ctx, res.as_mut_ptr(), &mut len); - self.state = Finalized; - assert_eq!(len as usize, md_len); - assert_eq!(r, 1); - } - res - } - - /// Returns the hash of the data written since creation or - /// the last `finish` and resets the hasher. - #[inline] - pub fn finish(&mut self) -> Vec { - self.finalize() - } -} - -impl Writer for Hasher { - #[inline] - fn write_all(&mut self, buf: &[u8]) -> Result<(), IoError> { - self.update(buf); - Ok(()) - } -} - -impl Clone for Hasher { - fn clone(&self) -> Hasher { - let ctx = unsafe { - let ctx = ffi::EVP_MD_CTX_create(); - assert!(!ctx.is_null()); - let r = ffi::EVP_MD_CTX_copy_ex(ctx, self.ctx); - assert_eq!(r, 1); - ctx - }; - Hasher { ctx: ctx, md: self.md, type_: self.type_, state: self.state } - } -} - -impl Drop for Hasher { - fn drop(&mut self) { - unsafe { - if self.state != Finalized { - let mut buf: Vec = repeat(0).take(self.type_.md_len()).collect(); - let mut len = 0; - ffi::EVP_DigestFinal_ex(self.ctx, buf.as_mut_ptr(), &mut len); - } - ffi::EVP_MD_CTX_destroy(self.ctx); - } - } -} - -/// Computes the hash of the `data` with the hash `t`. -pub fn hash(t: Type, data: &[u8]) -> Vec { - let mut h = Hasher::new(t); - let _ = h.write_all(data); - h.finish() -} - -#[cfg(test)] -mod tests { - use serialize::hex::{FromHex, ToHex}; - use super::{hash, Hasher, Type}; - use std::old_io::Writer; - - fn hash_test(hashtype: Type, hashtest: &(&str, &str)) { - let res = hash(hashtype, &*hashtest.0.from_hex().unwrap()); - assert_eq!(res.to_hex(), hashtest.1); - } - - fn hash_recycle_test(h: &mut Hasher, hashtest: &(&str, &str)) { - let _ = h.write_all(&*hashtest.0.from_hex().unwrap()); - let res = h.finish(); - assert_eq!(res.to_hex(), hashtest.1); - } - - // Test vectors from http://www.nsrl.nist.gov/testdata/ - #[allow(non_upper_case_globals)] - const md5_tests: [(&'static str, &'static str); 13] = [ - ("", "d41d8cd98f00b204e9800998ecf8427e"), - ("7F", "83acb6e67e50e31db6ed341dd2de1595"), - ("EC9C", "0b07f0d4ca797d8ac58874f887cb0b68"), - ("FEE57A", "e0d583171eb06d56198fc0ef22173907"), - ("42F497E0", "7c430f178aefdf1487fee7144e9641e2"), - ("C53B777F1C", "75ef141d64cb37ec423da2d9d440c925"), - ("89D5B576327B", "ebbaf15eb0ed784c6faa9dc32831bf33"), - ("5D4CCE781EB190", "ce175c4b08172019f05e6b5279889f2c"), - ("81901FE94932D7B9", "cd4d2f62b8cdb3a0cf968a735a239281"), - ("C9FFDEE7788EFB4EC9", "e0841a231ab698db30c6c0f3f246c014"), - ("66AC4B7EBA95E53DC10B", "a3b3cea71910d9af56742aa0bb2fe329"), - ("A510CD18F7A56852EB0319", "577e216843dd11573574d3fb209b97d8"), - ("AAED18DBE8938C19ED734A8D", "6f80fb775f27e0a4ce5c2f42fc72c5f1") - ]; - - #[test] - fn test_md5() { - for test in md5_tests.iter() { - hash_test(Type::MD5, test); - } - } - - #[test] - fn test_md5_recycle() { - let mut h = Hasher::new(Type::MD5); - for test in md5_tests.iter() { - hash_recycle_test(&mut h, test); - } - } - - #[test] - fn test_finish_twice() { - let mut h = Hasher::new(Type::MD5); - let _ = h.write_all(&*md5_tests[6].0.from_hex().unwrap()); - let _ = h.finish(); - let res = h.finish(); - let null = hash(Type::MD5, &[]); - assert_eq!(res, null); - } - - #[test] - fn test_clone() { - let i = 7; - let inp = md5_tests[i].0.from_hex().unwrap(); - assert!(inp.len() > 2); - let p = inp.len() / 2; - let h0 = Hasher::new(Type::MD5); - - println!("Clone a new hasher"); - let mut h1 = h0.clone(); - let _ = h1.write_all(&inp[..p]); - { - println!("Clone an updated hasher"); - let mut h2 = h1.clone(); - let _ = h2.write_all(&inp[p..]); - let res = h2.finish(); - assert_eq!(res.to_hex(), md5_tests[i].1); - } - let _ = h1.write_all(&inp[p..]); - let res = h1.finish(); - assert_eq!(res.to_hex(), md5_tests[i].1); - - println!("Clone a finished hasher"); - let mut h3 = h1.clone(); - let _ = h3.write_all(&*md5_tests[i + 1].0.from_hex().unwrap()); - let res = h3.finish(); - assert_eq!(res.to_hex(), md5_tests[i + 1].1); - } - - #[test] - fn test_sha1() { - let tests = [ - ("616263", "a9993e364706816aba3e25717850c26c9cd0d89d"), - ]; - - for test in tests.iter() { - hash_test(Type::SHA1, test); - } - } - - #[test] - fn test_sha256() { - let tests = [ - ("616263", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") - ]; - - for test in tests.iter() { - hash_test(Type::SHA256, test); - } - } - - #[test] - fn test_ripemd160() { - let tests = [ - ("616263", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc") - ]; - - for test in tests.iter() { - hash_test(Type::RIPEMD160, test); - } - } -} diff --git a/src/crypto/hmac.rs b/src/crypto/hmac.rs deleted file mode 100644 index 65808e58..00000000 --- a/src/crypto/hmac.rs +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright 2013 Jack Lloyd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use libc::{c_int, c_uint}; -use std::iter::repeat; -use std::old_io::{IoError, Writer}; - -use crypto::hash::Type; -use ffi; - -#[derive(PartialEq, Copy)] -enum State { - Reset, - Updated, - Finalized, -} - -use self::State::*; - -/// Provides HMAC computation. -/// -/// # Examples -/// -/// Calculate a HMAC in one go. -/// -/// ``` -/// use openssl::crypto::hash::Type; -/// use openssl::crypto::hmac::hmac; -/// let key = b"Jefe"; -/// let data = b"what do ya want for nothing?"; -/// let spec = b"\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38"; -/// let res = hmac(Type::MD5, key, data); -/// assert_eq!(spec, res); -/// ``` -/// -/// Use the `Writer` trait to supply the input in chunks. -/// -/// ``` -/// use std::old_io::Writer; -/// use openssl::crypto::hash::Type; -/// use openssl::crypto::hmac::HMAC; -/// let key = b"Jefe"; -/// let data = [b"what do ya ", b"want for nothing?"]; -/// let spec = b"\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38"; -/// let mut h = HMAC::new(Type::MD5, &*key); -/// h.write_all(data[0]); -/// h.write_all(data[1]); -/// let res = h.finish(); -/// assert_eq!(spec, res); -/// ``` -pub struct HMAC { - ctx: ffi::HMAC_CTX, - type_: Type, - state: State, -} - -impl HMAC { - /// Creates a new `HMAC` with the specified hash type using the `key`. - pub fn new(ty: Type, key: &[u8]) -> HMAC { - ffi::init(); - - let ctx = unsafe { - let mut ctx = ::std::mem::uninitialized(); - ffi::HMAC_CTX_init(&mut ctx); - ctx - }; - let md = ty.evp_md(); - - let mut h = HMAC { ctx: ctx, type_: ty, state: Finalized }; - h.init_once(md, key); - h - } - - #[inline] - fn init_once(&mut self, md: *const ffi::EVP_MD, key: &[u8]) { - unsafe { - let r = ffi::HMAC_Init_ex(&mut self.ctx, - key.as_ptr(), key.len() as c_int, - md, 0 as *const _); - assert_eq!(r, 1); - } - self.state = Reset; - } - - #[inline] - fn init(&mut self) { - match self.state { - Reset => return, - Updated => { self.finalize(); }, - Finalized => (), - } - // If the key and/or md is not supplied it's reused from the last time - // avoiding redundant initializations - unsafe { - let r = ffi::HMAC_Init_ex(&mut self.ctx, - 0 as *const _, 0, - 0 as *const _, 0 as *const _); - assert_eq!(r, 1); - } - self.state = Reset; - } - - #[inline] - fn update(&mut self, data: &[u8]) { - if self.state == Finalized { - self.init(); - } - unsafe { - let r = ffi::HMAC_Update(&mut self.ctx, data.as_ptr(), - data.len() as c_uint); - assert_eq!(r, 1); - } - self.state = Updated; - } - - #[inline] - fn finalize(&mut self) -> Vec { - if self.state == Finalized { - self.init(); - } - let md_len = self.type_.md_len(); - let mut res: Vec = repeat(0).take(md_len).collect(); - unsafe { - let mut len = 0; - let r = ffi::HMAC_Final(&mut self.ctx, res.as_mut_ptr(), &mut len); - self.state = Finalized; - assert_eq!(len as usize, md_len); - assert_eq!(r, 1); - } - res - } - - /// Returns the hash of the data written since creation or - /// the last `finish` and resets the hasher. - #[inline] - pub fn finish(&mut self) -> Vec { - self.finalize() - } -} - -impl Writer for HMAC { - #[inline] - fn write_all(&mut self, buf: &[u8]) -> Result<(), IoError> { - self.update(buf); - Ok(()) - } -} - -impl Clone for HMAC { - fn clone(&self) -> HMAC { - let mut ctx: ffi::HMAC_CTX; - unsafe { - ctx = ::std::mem::uninitialized(); - let r = ffi::HMAC_CTX_copy(&mut ctx, &self.ctx); - assert_eq!(r, 1); - } - HMAC { ctx: ctx, type_: self.type_, state: self.state } - } -} - -impl Drop for HMAC { - fn drop(&mut self) { - unsafe { - if self.state != Finalized { - let mut buf: Vec = repeat(0).take(self.type_.md_len()).collect(); - let mut len = 0; - ffi::HMAC_Final(&mut self.ctx, buf.as_mut_ptr(), &mut len); - } - ffi::HMAC_CTX_cleanup(&mut self.ctx); - } - } -} - -/// Computes the HMAC of the `data` with the hash `t` and `key`. -pub fn hmac(t: Type, key: &[u8], data: &[u8]) -> Vec { - let mut h = HMAC::new(t, key); - let _ = h.write_all(data); - h.finish() -} - -#[cfg(test)] -mod tests { - use std::iter::repeat; - use serialize::hex::FromHex; - use crypto::hash::Type; - use crypto::hash::Type::*; - use super::{hmac, HMAC}; - use std::old_io::Writer; - - fn test_hmac(ty: Type, tests: &[(Vec, Vec, Vec)]) { - for &(ref key, ref data, ref res) in tests.iter() { - assert_eq!(hmac(ty, &**key, &**data), *res); - } - } - - fn test_hmac_recycle(h: &mut HMAC, test: &(Vec, Vec, Vec)) { - let &(_, ref data, ref res) = test; - let _ = h.write_all(&**data); - assert_eq!(h.finish(), *res); - } - - #[test] - fn test_hmac_md5() { - // test vectors from RFC 2202 - let tests: [(Vec, Vec, Vec); 7] = [ - (repeat(0x0b_u8).take(16).collect(), b"Hi There".to_vec(), - "9294727a3638bb1c13f48ef8158bfc9d".from_hex().unwrap()), - (b"Jefe".to_vec(), - b"what do ya want for nothing?".to_vec(), - "750c783e6ab0b503eaa86e310a5db738".from_hex().unwrap()), - (repeat(0xaa_u8).take(16).collect(), repeat(0xdd_u8).take(50).collect(), - "56be34521d144c88dbb8c733f0e8b3f6".from_hex().unwrap()), - ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(), - repeat(0xcd_u8).take(50).collect(), - "697eaf0aca3a3aea3a75164746ffaa79".from_hex().unwrap()), - (repeat(0x0c_u8).take(16).collect(), - b"Test With Truncation".to_vec(), - "56461ef2342edc00f9bab995690efd4c".from_hex().unwrap()), - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), - "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()), - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key \ - and Larger Than One Block-Size Data".to_vec(), - "6f630fad67cda0ee1fb1f562db3aa53e".from_hex().unwrap()) - ]; - - test_hmac(MD5, &tests); - } - - #[test] - fn test_hmac_md5_recycle() { - let tests: [(Vec, Vec, Vec); 2] = [ - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), - "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()), - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key \ - and Larger Than One Block-Size Data".to_vec(), - "6f630fad67cda0ee1fb1f562db3aa53e".from_hex().unwrap()) - ]; - - let mut h = HMAC::new(MD5, &*tests[0].0); - for i in 0..100us { - let test = &tests[i % 2]; - test_hmac_recycle(&mut h, test); - } - } - - #[test] - fn test_finish_twice() { - let test: (Vec, Vec, Vec) = - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), - "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()); - - let mut h = HMAC::new(Type::MD5, &*test.0); - let _ = h.write_all(&*test.1); - let _ = h.finish(); - let res = h.finish(); - let null = hmac(Type::MD5, &*test.0, &[]); - assert_eq!(res, null); - } - - #[test] - fn test_clone() { - let tests: [(Vec, Vec, Vec); 2] = [ - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), - "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()), - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key \ - and Larger Than One Block-Size Data".to_vec(), - "6f630fad67cda0ee1fb1f562db3aa53e".from_hex().unwrap()), - ]; - let p = tests[0].0.len() / 2; - let h0 = HMAC::new(Type::MD5, &*tests[0].0); - - println!("Clone a new hmac"); - let mut h1 = h0.clone(); - let _ = h1.write_all(&tests[0].1[..p]); - { - println!("Clone an updated hmac"); - let mut h2 = h1.clone(); - let _ = h2.write_all(&tests[0].1[p..]); - let res = h2.finish(); - assert_eq!(res, tests[0].2); - } - let _ = h1.write_all(&tests[0].1[p..]); - let res = h1.finish(); - assert_eq!(res, tests[0].2); - - println!("Clone a finished hmac"); - let mut h3 = h1.clone(); - let _ = h3.write_all(&*tests[1].1); - let res = h3.finish(); - assert_eq!(res, tests[1].2); - } - - #[test] - fn test_hmac_sha1() { - // test vectors from RFC 2202 - let tests: [(Vec, Vec, Vec); 7] = [ - (repeat(0x0b_u8).take(20).collect(), b"Hi There".to_vec(), - "b617318655057264e28bc0b6fb378c8ef146be00".from_hex().unwrap()), - (b"Jefe".to_vec(), - b"what do ya want for nothing?".to_vec(), - "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79".from_hex().unwrap()), - (repeat(0xaa_u8).take(20).collect(), repeat(0xdd_u8).take(50).collect(), - "125d7342b9ac11cd91a39af48aa17b4f63f175d3".from_hex().unwrap()), - ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(), - repeat(0xcd_u8).take(50).collect(), - "4c9007f4026250c6bc8414f9bf50c86c2d7235da".from_hex().unwrap()), - (repeat(0x0c_u8).take(20).collect(), - b"Test With Truncation".to_vec(), - "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04".from_hex().unwrap()), - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), - "aa4ae5e15272d00e95705637ce8a3b55ed402112".from_hex().unwrap()), - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key \ - and Larger Than One Block-Size Data".to_vec(), - "e8e99d0f45237d786d6bbaa7965c7808bbff1a91".from_hex().unwrap()) - ]; - - test_hmac(SHA1, &tests); - } - - #[test] - fn test_hmac_sha1_recycle() { - let tests: [(Vec, Vec, Vec); 2] = [ - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), - "aa4ae5e15272d00e95705637ce8a3b55ed402112".from_hex().unwrap()), - (repeat(0xaa_u8).take(80).collect(), - b"Test Using Larger Than Block-Size Key \ - and Larger Than One Block-Size Data".to_vec(), - "e8e99d0f45237d786d6bbaa7965c7808bbff1a91".from_hex().unwrap()) - ]; - - let mut h = HMAC::new(SHA1, &*tests[0].0); - for i in 0..100us { - let test = &tests[i % 2]; - test_hmac_recycle(&mut h, test); - } - } - - - - fn test_sha2(ty: Type, results: &[Vec]) { - // test vectors from RFC 4231 - let tests: [(Vec, Vec); 6] = [ - (repeat(0xb_u8).take(20).collect(), b"Hi There".to_vec()), - (b"Jefe".to_vec(), - b"what do ya want for nothing?".to_vec()), - (repeat(0xaa_u8).take(20).collect(), repeat(0xdd_u8).take(50).collect()), - ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(), - repeat(0xcd_u8).take(50).collect()), - (repeat(0xaa_u8).take(131).collect(), - b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec()), - (repeat(0xaa_u8).take(131).collect(), - b"This is a test using a larger than block-size key and a \ - larger than block-size data. The key needs to be hashed \ - before being used by the HMAC algorithm.".to_vec()) - ]; - - for (&(ref key, ref data), res) in tests.iter().zip(results.iter()) { - assert_eq!(hmac(ty, &**key, &**data), *res); - } - - // recycle test - let mut h = HMAC::new(ty, &*tests[5].0); - for i in 0..100us { - let test = &tests[4 + i % 2]; - let tup = (test.0.clone(), test.1.clone(), results[4 + i % 2].clone()); - test_hmac_recycle(&mut h, &tup); - } - } - - #[test] - fn test_hmac_sha224() { - let results = [ - "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22".from_hex().unwrap(), - "a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44".from_hex().unwrap(), - "7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea".from_hex().unwrap(), - "6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a".from_hex().unwrap(), - "95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e".from_hex().unwrap(), - "3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1".from_hex().unwrap() - ]; - test_sha2(SHA224, &results); - } - - #[test] - fn test_hmac_sha256() { - let results = [ - "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7".from_hex().unwrap(), - "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843".from_hex().unwrap(), - "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe".from_hex().unwrap(), - "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b".from_hex().unwrap(), - "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54".from_hex().unwrap(), - "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2".from_hex().unwrap() - ]; - test_sha2(SHA256, &results); - } - - #[test] - fn test_hmac_sha384() { - let results = [ - "afd03944d84895626b0825f4ab46907f\ - 15f9dadbe4101ec682aa034c7cebc59c\ - faea9ea9076ede7f4af152e8b2fa9cb6".from_hex().unwrap(), - "af45d2e376484031617f78d2b58a6b1b\ - 9c7ef464f5a01b47e42ec3736322445e\ - 8e2240ca5e69e2c78b3239ecfab21649".from_hex().unwrap(), - "88062608d3e6ad8a0aa2ace014c8a86f\ - 0aa635d947ac9febe83ef4e55966144b\ - 2a5ab39dc13814b94e3ab6e101a34f27".from_hex().unwrap(), - "3e8a69b7783c25851933ab6290af6ca7\ - 7a9981480850009cc5577c6e1f573b4e\ - 6801dd23c4a7d679ccf8a386c674cffb".from_hex().unwrap(), - "4ece084485813e9088d2c63a041bc5b4\ - 4f9ef1012a2b588f3cd11f05033ac4c6\ - 0c2ef6ab4030fe8296248df163f44952".from_hex().unwrap(), - "6617178e941f020d351e2f254e8fd32c\ - 602420feb0b8fb9adccebb82461e99c5\ - a678cc31e799176d3860e6110c46523e".from_hex().unwrap() - ]; - test_sha2(SHA384, &results); - } - - #[test] - fn test_hmac_sha512() { - let results = [ - "87aa7cdea5ef619d4ff0b4241a1d6cb0\ - 2379f4e2ce4ec2787ad0b30545e17cde\ - daa833b7d6b8a702038b274eaea3f4e4\ - be9d914eeb61f1702e696c203a126854".from_hex().unwrap(), - "164b7a7bfcf819e2e395fbe73b56e0a3\ - 87bd64222e831fd610270cd7ea250554\ - 9758bf75c05a994a6d034f65f8f0e6fd\ - caeab1a34d4a6b4b636e070a38bce737".from_hex().unwrap(), - "fa73b0089d56a284efb0f0756c890be9\ - b1b5dbdd8ee81a3655f83e33b2279d39\ - bf3e848279a722c806b485a47e67c807\ - b946a337bee8942674278859e13292fb".from_hex().unwrap(), - "b0ba465637458c6990e5a8c5f61d4af7\ - e576d97ff94b872de76f8050361ee3db\ - a91ca5c11aa25eb4d679275cc5788063\ - a5f19741120c4f2de2adebeb10a298dd".from_hex().unwrap(), - "80b24263c7c1a3ebb71493c1dd7be8b4\ - 9b46d1f41b4aeec1121b013783f8f352\ - 6b56d037e05f2598bd0fd2215d6a1e52\ - 95e64f73f63f0aec8b915a985d786598".from_hex().unwrap(), - "e37b6a775dc87dbaa4dfa9f96e5e3ffd\ - debd71f8867289865df5a32d20cdc944\ - b6022cac3c4982b10d5eeb55c3e4de15\ - 134676fb6de0446065c97440fa8c6a58".from_hex().unwrap() - ]; - test_sha2(SHA512, &results); - } -} diff --git a/src/crypto/memcmp.rs b/src/crypto/memcmp.rs deleted file mode 100644 index 299effa9..00000000 --- a/src/crypto/memcmp.rs +++ /dev/null @@ -1,39 +0,0 @@ -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]); - } -} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs deleted file mode 100644 index e695de33..00000000 --- a/src/crypto/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * 2013 Jack Lloyd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -pub mod hash; -pub mod hmac; -pub mod pkcs5; -pub mod pkey; -pub mod rand; -pub mod symm; -pub mod memcmp; diff --git a/src/crypto/pkcs5.rs b/src/crypto/pkcs5.rs deleted file mode 100644 index b101c3ed..00000000 --- a/src/crypto/pkcs5.rs +++ /dev/null @@ -1,119 +0,0 @@ -use libc::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: usize, keylen: usize) -> Vec { - unsafe { - assert!(iter >= 1); - assert!(keylen >= 1); - - let mut out = Vec::with_capacity(keylen); - - 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 { panic!(); } - - out.set_len(keylen); - - out - } -} - -#[cfg(test)] -mod tests { - // Test vectors from - // http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06 - #[test] - fn test_pbkdf2_hmac_sha1() { - assert_eq!( - super::pbkdf2_hmac_sha1( - "password", - "salt".as_bytes(), - 1, - 20 - ), - vec!( - 0x0c_u8, 0x60_u8, 0xc8_u8, 0x0f_u8, 0x96_u8, 0x1f_u8, 0x0e_u8, - 0x71_u8, 0xf3_u8, 0xa9_u8, 0xb5_u8, 0x24_u8, 0xaf_u8, 0x60_u8, - 0x12_u8, 0x06_u8, 0x2f_u8, 0xe0_u8, 0x37_u8, 0xa6_u8 - ) - ); - - assert_eq!( - super::pbkdf2_hmac_sha1( - "password", - "salt".as_bytes(), - 2, - 20 - ), - vec!( - 0xea_u8, 0x6c_u8, 0x01_u8, 0x4d_u8, 0xc7_u8, 0x2d_u8, 0x6f_u8, - 0x8c_u8, 0xcd_u8, 0x1e_u8, 0xd9_u8, 0x2a_u8, 0xce_u8, 0x1d_u8, - 0x41_u8, 0xf0_u8, 0xd8_u8, 0xde_u8, 0x89_u8, 0x57_u8 - ) - ); - - assert_eq!( - super::pbkdf2_hmac_sha1( - "password", - "salt".as_bytes(), - 4096, - 20 - ), - vec!( - 0x4b_u8, 0x00_u8, 0x79_u8, 0x01_u8, 0xb7_u8, 0x65_u8, 0x48_u8, - 0x9a_u8, 0xbe_u8, 0xad_u8, 0x49_u8, 0xd9_u8, 0x26_u8, 0xf7_u8, - 0x21_u8, 0xd0_u8, 0x65_u8, 0xa4_u8, 0x29_u8, 0xc1_u8 - ) - ); - - assert_eq!( - super::pbkdf2_hmac_sha1( - "password", - "salt".as_bytes(), - 16777216, - 20 - ), - vec!( - 0xee_u8, 0xfe_u8, 0x3d_u8, 0x61_u8, 0xcd_u8, 0x4d_u8, 0xa4_u8, - 0xe4_u8, 0xe9_u8, 0x94_u8, 0x5b_u8, 0x3d_u8, 0x6b_u8, 0xa2_u8, - 0x15_u8, 0x8c_u8, 0x26_u8, 0x34_u8, 0xe9_u8, 0x84_u8 - ) - ); - - assert_eq!( - super::pbkdf2_hmac_sha1( - "passwordPASSWORDpassword", - "saltSALTsaltSALTsaltSALTsaltSALTsalt".as_bytes(), - 4096, - 25 - ), - vec!( - 0x3d_u8, 0x2e_u8, 0xec_u8, 0x4f_u8, 0xe4_u8, 0x1c_u8, 0x84_u8, - 0x9b_u8, 0x80_u8, 0xc8_u8, 0xd8_u8, 0x36_u8, 0x62_u8, 0xc0_u8, - 0xe4_u8, 0x4a_u8, 0x8b_u8, 0x29_u8, 0x1a_u8, 0x96_u8, 0x4c_u8, - 0xf2_u8, 0xf0_u8, 0x70_u8, 0x38_u8 - ) - ); - - assert_eq!( - super::pbkdf2_hmac_sha1( - "pass\x00word", - "sa\x00lt".as_bytes(), - 4096, - 16 - ), - vec!( - 0x56_u8, 0xfa_u8, 0x6a_u8, 0xa7_u8, 0x55_u8, 0x48_u8, 0x09_u8, - 0x9d_u8, 0xcc_u8, 0x37_u8, 0xd7_u8, 0xf0_u8, 0x34_u8, 0x25_u8, - 0xe0_u8, 0xc3_u8 - ) - ); - } -} diff --git a/src/crypto/pkey.rs b/src/crypto/pkey.rs deleted file mode 100644 index c20fae4f..00000000 --- a/src/crypto/pkey.rs +++ /dev/null @@ -1,423 +0,0 @@ -use libc::{c_int, c_uint, c_ulong}; -use std::iter::repeat; -use std::mem; -use std::ptr; -use bio::{MemBio}; -use crypto::hash; -use crypto::hash::Type as HashType; -use ffi; -use ssl::error::{SslError, StreamError}; - -#[derive(Copy)] -enum Parts { - Neither, - Public, - Both -} - -/// Represents a role an asymmetric key might be appropriate for. -#[derive(Copy)] -pub enum Role { - Encrypt, - Decrypt, - Sign, - Verify -} - -/// Type of encryption padding to use. -#[derive(Copy)] -pub enum EncryptionPadding { - OAEP, - PKCS1v15 -} - -fn openssl_padding_code(padding: EncryptionPadding) -> c_int { - match padding { - EncryptionPadding::OAEP => 4, - EncryptionPadding::PKCS1v15 => 1 - } -} - -fn openssl_hash_nid(hash: HashType) -> c_int { - match hash { - 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 ffi::EVP_PKEY, - parts: Parts, -} - -/// Represents a public key, optionally with a private key attached. -impl PKey { - pub fn new() -> PKey { - unsafe { - ffi::init(); - - PKey { - evp: ffi::EVP_PKEY_new(), - parts: Parts::Neither, - } - } - } - - fn _tostr(&self, f: unsafe extern "C" fn(*mut ffi::RSA, *const *mut u8) -> c_int) -> Vec { - unsafe { - 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 = repeat(0u8).take(len as usize).collect::>(); - - let r = f(rsa, &s.as_mut_ptr()); - - s.truncate(r as usize); - s - } - } - - 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); - ffi::EVP_PKEY_set1_RSA(self.evp, rsa); - } - } - - pub fn gen(&mut self, keysz: usize) { - unsafe { - let rsa = ffi::RSA_generate_key( - keysz as c_int, - 65537 as c_ulong, - ptr::null(), - ptr::null() - ); - - // XXX: 6 == NID_rsaEncryption - ffi::EVP_PKEY_assign( - self.evp, - 6 as c_int, - mem::transmute(rsa)); - - self.parts = Parts::Both; - } - } - - /** - * Returns a serialized form of the public key, suitable for load_pub(). - */ - pub fn save_pub(&self) -> Vec { - 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, ffi::d2i_RSA_PUBKEY); - self.parts = Parts::Public; - } - - /** - * Returns a serialized form of the public and private keys, suitable for - * load_priv(). - */ - pub fn save_priv(&self) -> Vec { - 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, 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*/) -> 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_all(buf.as_slice()).map_err(StreamError) - } - - /** - * Returns the size of the public key modulus. - */ - pub fn size(&self) -> usize { - unsafe { - ffi::RSA_size(ffi::EVP_PKEY_get1_RSA(self.evp)) as usize - } - } - - /** - * Returns whether this pkey object can perform the specified role. - */ - pub fn can(&self, r: Role) -> bool { - match r { - Role::Encrypt => - match self.parts { - Parts::Neither => false, - _ => true, - }, - Role::Verify => - match self.parts { - Parts::Neither => false, - _ => true, - }, - Role::Decrypt => - match self.parts { - Parts::Both => true, - _ => false, - }, - Role::Sign => - match self.parts { - Parts::Both => true, - _ => false, - }, - } - } - - /** - * Returns the maximum amount of data that can be encrypted by an encrypt() - * call. - */ - pub fn max_data(&self) -> usize { - unsafe { - 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 usize - 41 - } - } - - pub fn encrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - let len = ffi::RSA_size(rsa); - - assert!(s.len() < self.max_data()); - - let mut r = repeat(0u8).take(len as usize + 1).collect::>(); - - let rv = ffi::RSA_public_encrypt( - s.len() as c_int, - s.as_ptr(), - r.as_mut_ptr(), - rsa, - openssl_padding_code(padding)); - - if rv < 0 as c_int { - vec!() - } else { - r.truncate(rv as usize); - r - } - } - } - - pub fn decrypt_with_padding(&self, s: &[u8], padding: EncryptionPadding) -> Vec { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - let len = ffi::RSA_size(rsa); - - assert_eq!(s.len() as c_int, ffi::RSA_size(rsa)); - - let mut r = repeat(0u8).take(len as usize + 1).collect::>(); - - let rv = ffi::RSA_private_decrypt( - s.len() as c_int, - s.as_ptr(), - r.as_mut_ptr(), - rsa, - openssl_padding_code(padding)); - - if rv < 0 as c_int { - vec!() - } else { - r.truncate(rv as usize); - r - } - } - } - - /** - * Encrypts data using OAEP padding, returning the encrypted data. The - * supplied data must not be larger than max_data(). - */ - pub fn encrypt(&self, s: &[u8]) -> Vec { self.encrypt_with_padding(s, EncryptionPadding::OAEP) } - - /** - * Decrypts data, expecting OAEP padding, returning the decrypted data. - */ - pub fn decrypt(&self, s: &[u8]) -> Vec { 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 { 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, HashType::SHA256) } - - pub fn sign_with_hash(&self, s: &[u8], hash: hash::Type) -> Vec { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - let len = ffi::RSA_size(rsa); - let mut r = repeat(0u8).take(len as usize + 1).collect::>(); - - let mut len = 0; - let rv = ffi::RSA_sign( - openssl_hash_nid(hash), - s.as_ptr(), - s.len() as c_uint, - r.as_mut_ptr(), - &mut len, - rsa); - - if rv < 0 as c_int { - vec!() - } else { - r.truncate(len as usize); - r - } - } - } - - pub fn verify_with_hash(&self, m: &[u8], s: &[u8], hash: hash::Type) -> bool { - unsafe { - let rsa = ffi::EVP_PKEY_get1_RSA(self.evp); - - let rv = ffi::RSA_verify( - openssl_hash_nid(hash), - m.as_ptr(), - m.len() as c_uint, - s.as_ptr(), - s.len() as c_uint, - rsa - ); - - 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 { - ffi::EVP_PKEY_free(self.evp); - } - } -} - -#[cfg(test)] -mod tests { - use crypto::hash::Type::{MD5, SHA1}; - - #[test] - fn test_gen_pub() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - k0.gen(512); - 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::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] - fn test_gen_priv() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - k0.gen(512); - 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::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] - fn test_encrypt() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8); - k0.gen(512); - k1.load_pub(k0.save_pub().as_slice()); - let emsg = k1.encrypt(msg.as_slice()); - let dmsg = k0.decrypt(emsg.as_slice()); - assert!(msg == dmsg); - } - - #[test] - fn test_encrypt_pkcs() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8); - k0.gen(512); - k1.load_pub(k0.save_pub().as_slice()); - 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); - } - - #[test] - fn test_sign() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8); - k0.gen(512); - k1.load_pub(k0.save_pub().as_slice()); - let sig = k0.sign(msg.as_slice()); - let rv = k1.verify(msg.as_slice(), sig.as_slice()); - assert!(rv == true); - } - - #[test] - fn test_sign_hashes() { - let mut k0 = super::PKey::new(); - let mut k1 = super::PKey::new(); - let msg = vec!(0xdeu8, 0xadu8, 0xd0u8, 0x0du8); - k0.gen(512); - k1.load_pub(k0.save_pub().as_slice()); - - let sig = k0.sign_with_hash(msg.as_slice(), MD5); - - assert!(k1.verify_with_hash(msg.as_slice(), sig.as_slice(), MD5)); - assert!(!k1.verify_with_hash(msg.as_slice(), sig.as_slice(), SHA1)); - } -} diff --git a/src/crypto/rand.rs b/src/crypto/rand.rs deleted file mode 100644 index dc338a3d..00000000 --- a/src/crypto/rand.rs +++ /dev/null @@ -1,27 +0,0 @@ -use libc::c_int; -use ffi; - -pub fn rand_bytes(len: usize) -> Vec { - unsafe { - let mut out = Vec::with_capacity(len); - - 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); - - out - } -} - -#[cfg(test)] -mod tests { - use super::rand_bytes; - - #[test] - fn test_rand_bytes() { - let bytes = rand_bytes(32); - println!("{:?}", bytes); - } -} diff --git a/src/crypto/symm.rs b/src/crypto/symm.rs deleted file mode 100644 index 922137c0..00000000 --- a/src/crypto/symm.rs +++ /dev/null @@ -1,314 +0,0 @@ -use std::iter::repeat; -use libc::{c_int}; - -use ffi; - -#[derive(Copy)] -pub enum Mode { - Encrypt, - Decrypt, -} - -#[allow(non_camel_case_types)] -#[derive(Copy)] -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) -> (*const ffi::EVP_CIPHER, u32, u32) { - unsafe { - match t { - Type::AES_128_ECB => (ffi::EVP_aes_128_ecb(), 16, 16), - Type::AES_128_CBC => (ffi::EVP_aes_128_cbc(), 16, 16), - #[cfg(feature = "aes_xts")] - Type::AES_128_XTS => (ffi::EVP_aes_128_xts(), 32, 16), - // AES_128_CTR => (EVP_aes_128_ctr(), 16, 0), - //AES_128_GCM => (EVP_aes_128_gcm(), 16, 16), - - Type::AES_256_ECB => (ffi::EVP_aes_256_ecb(), 32, 16), - Type::AES_256_CBC => (ffi::EVP_aes_256_cbc(), 32, 16), - #[cfg(feature = "aes_xts")] - Type::AES_256_XTS => (ffi::EVP_aes_256_xts(), 64, 16), - // AES_256_CTR => (EVP_aes_256_ctr(), 32, 0), - //AES_256_GCM => (EVP_aes_256_gcm(), 32, 16), - - Type::RC4_128 => (ffi::EVP_rc4(), 16, 0), - } - } -} - -/// Represents a symmetric cipher context. -pub struct Crypter { - evp: *const ffi::EVP_CIPHER, - ctx: *mut ffi::EVP_CIPHER_CTX, - keylen: u32, - blocksize: u32, -} - -impl Crypter { - pub fn new(t: Type) -> Crypter { - 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 } - } - - /** - * Enables or disables padding. If padding is disabled, total amount of - * data encrypted must be a multiple of block size. - */ - pub fn pad(&self, padding: bool) { - if self.blocksize > 0 { - unsafe { - let v = if padding { 1 as c_int } else { 0 }; - ffi::EVP_CIPHER_CTX_set_padding(self.ctx, v); - } - } - } - - /** - * Initializes this crypter. - */ - pub fn init(&self, mode: Mode, key: &[u8], iv: Vec) { - unsafe { - let mode = match mode { - Mode::Encrypt => 1 as c_int, - Mode::Decrypt => 0 as c_int, - }; - assert_eq!(key.len(), self.keylen as usize); - - ffi::EVP_CipherInit( - self.ctx, - self.evp, - key.as_ptr(), - iv.as_ptr(), - mode - ); - } - } - - /** - * Update this crypter with more data to encrypt or decrypt. Returns - * encrypted or decrypted bytes. - */ - pub fn update(&self, data: &[u8]) -> Vec { - unsafe { - let sum = data.len() + (self.blocksize as usize); - let mut res = repeat(0u8).take(sum).collect::>(); - let mut reslen = sum as c_int; - - ffi::EVP_CipherUpdate( - self.ctx, - res.as_mut_ptr(), - &mut reslen, - data.as_ptr(), - data.len() as c_int - ); - - res.truncate(reslen as usize); - res - } - } - - /** - * Finish crypting. Returns the remaining partial block of output, if any. - */ - pub fn finalize(&self) -> Vec { - unsafe { - let mut res = repeat(0u8).take(self.blocksize as usize).collect::>(); - let mut reslen = self.blocksize as c_int; - - ffi::EVP_CipherFinal(self.ctx, - res.as_mut_ptr(), - &mut reslen); - - res.truncate(reslen as usize); - res - } - } -} - -impl Drop for Crypter { - fn drop(&mut self) { - unsafe { - ffi::EVP_CIPHER_CTX_free(self.ctx); - } - } -} - -/** - * Encrypts data, using the specified crypter type in encrypt mode with the - * specified key and iv; returns the resulting (encrypted) data. - */ -pub fn encrypt(t: Type, key: &[u8], iv: Vec, data: &[u8]) -> Vec { - let c = Crypter::new(t); - c.init(Mode::Encrypt, key, iv); - let mut r = c.update(data); - let rest = c.finalize(); - r.extend(rest.into_iter()); - r -} - -/** - * Decrypts data, using the specified crypter type in decrypt mode with the - * specified key and iv; returns the resulting (decrypted) data. - */ -pub fn decrypt(t: Type, key: &[u8], iv: Vec, data: &[u8]) -> Vec { - let c = Crypter::new(t); - c.init(Mode::Decrypt, key, iv); - let mut r = c.update(data); - let rest = c.finalize(); - r.extend(rest.into_iter()); - r -} - -#[cfg(test)] -mod tests { - use serialize::hex::FromHex; - - // Test vectors from FIPS-197: - // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf - #[test] - fn test_aes_256_ecb() { - let k0 = - vec!(0x00u8, 0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x05u8, 0x06u8, 0x07u8, - 0x08u8, 0x09u8, 0x0au8, 0x0bu8, 0x0cu8, 0x0du8, 0x0eu8, 0x0fu8, - 0x10u8, 0x11u8, 0x12u8, 0x13u8, 0x14u8, 0x15u8, 0x16u8, 0x17u8, - 0x18u8, 0x19u8, 0x1au8, 0x1bu8, 0x1cu8, 0x1du8, 0x1eu8, 0x1fu8); - let p0 = - vec!(0x00u8, 0x11u8, 0x22u8, 0x33u8, 0x44u8, 0x55u8, 0x66u8, 0x77u8, - 0x88u8, 0x99u8, 0xaau8, 0xbbu8, 0xccu8, 0xddu8, 0xeeu8, 0xffu8); - 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::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.finalize().into_iter()); - assert!(r0 == c0); - c.init(super::Mode::Decrypt, k0.as_slice(), vec![]); - c.pad(false); - let mut p1 = c.update(r0.as_slice()); - 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::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.finalize().into_iter()); - - if computed != expected { - println!("Computed: {}", computed.as_slice().to_hex()); - println!("Expected: {}", expected.as_slice().to_hex()); - if computed.len() != expected.len() { - println!("Lengths differ: {} in computed vs {} expected", - computed.len(), expected.len()); - } - panic!("test failure"); - } - } - - #[test] - fn test_rc4() { - - let pt = "0000000000000000000000000000000000000000000000000000000000000000000000000000"; - let ct = "A68686B04D686AA107BD8D4CAB191A3EEC0A6294BC78B60F65C25CB47BD7BB3A48EFC4D26BE4"; - let key = "97CD440324DA5FD1F7955C1C13B6B466"; - let 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] - fn test_aes128_ctr() { - - let pt = ~"6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710"; - let ct = ~"874D6191B620E3261BEF6864990DB6CE9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE"; - let key = ~"2B7E151628AED2A6ABF7158809CF4F3C"; - let iv = ~"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"; - - cipher_test(super::AES_128_CTR, pt, ct, key, iv); - }*/ - - /*#[test] - fn test_aes128_gcm() { - // Test case 3 in GCM spec - let pt = ~"d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255"; - let ct = ~"42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f59854d5c2af327cd64a62cf35abd2ba6fab4"; - let key = ~"feffe9928665731c6d6a8f9467308308"; - let iv = ~"cafebabefacedbaddecaf888"; - - cipher_test(super::AES_128_GCM, pt, ct, key, iv); - }*/ -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 58f3d2f0..00000000 --- a/src/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![feature(unsafe_destructor, core, io, std_misc, libc, hash, path)] -#![crate_name="openssl"] -#![crate_type="rlib"] -#![crate_type="dylib"] -#![doc(html_root_url="https://sfackler.github.io/doc/openssl")] - -extern crate libc; -#[cfg(test)] -extern crate "rustc-serialize" as serialize; - -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; diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 3e4bb429..00000000 --- a/src/macros.rs +++ /dev/null @@ -1,59 +0,0 @@ -#![macro_use] - -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)) -} diff --git a/src/ssl/error.rs b/src/ssl/error.rs deleted file mode 100644 index 027554c5..00000000 --- a/src/ssl/error.rs +++ /dev/null @@ -1,122 +0,0 @@ -pub use self::SslError::*; -pub use self::OpensslError::*; - -use libc::c_ulong; -use std::error; -use std::fmt; -use std::ffi::c_str_to_bytes; -use std::old_io::IoError; - -use ffi; - -/// An SSL error -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum SslError { - /// The underlying stream reported an error - StreamError(IoError), - /// The SSL session has been closed by the other end - SslSessionClosed, - /// An error in the OpenSSL library - OpenSslErrors(Vec) -} - -impl fmt::Display for SslError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.write_str(error::Error::description(self)) - } -} - -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 -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum OpensslError { - /// An unknown error - UnknownError { - /// The library reporting the error - library: String, - /// The function reporting the error - function: String, - /// The reason for the error - reason: String - } -} - -fn get_lib(err: c_ulong) -> String { - unsafe { - let bytes = c_str_to_bytes(&ffi::ERR_lib_error_string(err)).to_vec(); - String::from_utf8(bytes).unwrap() - } -} - -fn get_func(err: c_ulong) -> String { - unsafe { - let bytes = c_str_to_bytes(&ffi::ERR_func_error_string(err)).to_vec(); - String::from_utf8(bytes).unwrap() - } -} - -fn get_reason(err: c_ulong) -> String { - unsafe { - let bytes = c_str_to_bytes(&ffi::ERR_reason_error_string(err)).to_vec(); - String::from_utf8(bytes).unwrap() - } -} - -impl SslError { - /// Creates a new `OpenSslErrors` with the current contents of the error - /// stack. - pub fn get() -> SslError { - let mut errs = vec!(); - loop { - match unsafe { ffi::ERR_get_error() } { - 0 => break, - 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"); -} diff --git a/src/ssl/mod.rs b/src/ssl/mod.rs deleted file mode 100644 index 35649e24..00000000 --- a/src/ssl/mod.rs +++ /dev/null @@ -1,643 +0,0 @@ -use libc::{c_int, c_void, c_long}; -use std::ffi::{CString, c_str_to_bytes}; -use std::old_io::{IoResult, IoError, EndOfFile, Stream, Reader, Writer}; -use std::mem; -use std::fmt; -use std::num::FromPrimitive; -use std::ptr; -use std::sync::{Once, ONCE_INIT, Arc}; - -use bio::{MemBio}; -use ffi; -use ssl::error::{SslError, SslSessionClosed, StreamError}; -use x509::{X509StoreContext, X509FileType, X509}; - -pub mod error; -#[cfg(test)] -mod tests; - -static mut VERIFY_IDX: c_int = -1; - -fn init() { - static mut INIT: Once = ONCE_INIT; - - unsafe { - INIT.call_once(|| { - 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; - }); - } -} - -/// Determines the SSL method supported -#[allow(non_camel_case_types)] -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub enum SslMethod { - #[cfg(feature = "sslv2")] - /// 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, - #[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, -} - -impl SslMethod { - unsafe fn to_raw(&self) -> *const ffi::SSL_METHOD { - match *self { - #[cfg(feature = "sslv2")] - SslMethod::Sslv2 => ffi::SSLv2_method(), - SslMethod::Sslv3 => ffi::SSLv3_method(), - SslMethod::Tlsv1 => ffi::TLSv1_method(), - SslMethod::Sslv23 => ffi::SSLv23_method(), - #[cfg(feature = "tlsv1_1")] - SslMethod::Tlsv1_1 => ffi::TLSv1_1_method(), - #[cfg(feature = "tlsv1_2")] - SslMethod::Tlsv1_2 => ffi::TLSv1_2_method() - } - } -} - -/// Determines the type of certificate verification used -#[derive(Copy, Clone, Debug)] -#[repr(i32)] -pub enum SslVerifyMode { - /// Verify that the server's certificate is trusted - SslVerifyPeer = ffi::SSL_VERIFY_PEER, - /// Do not verify the server's certificate - SslVerifyNone = ffi::SSL_VERIFY_NONE -} - -// 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() -> c_int { - static mut VERIFY_DATA_IDX: c_int = -1; - static mut INIT: Once = ONCE_INIT; - - extern fn free_data_box(_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 = unsafe { mem::transmute(ptr) }; - } - - unsafe { - INIT.call_once(|| { - let f: ffi::CRYPTO_EX_free = free_data_box::; - let idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None, - None, Some(f)); - assert!(idx >= 0); - VERIFY_DATA_IDX = idx; - }); - VERIFY_DATA_IDX - } -} - -extern fn raw_verify(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 = mem::transmute(verify); - - let ctx = X509StoreContext::new(x509_ctx); - - match verify { - None => preverify_ok, - Some(verify) => verify(preverify_ok != 0, &ctx) as c_int - } - } -} - -extern fn raw_verify_with_data(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> = mem::transmute(verify); - - let data = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::()); - let data: Box = 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; - -/// 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 = fn(preverify_ok: bool, - x509_ctx: &X509StoreContext, - data: &T) -> bool; - -// FIXME: macro may be instead of inlining? -#[inline] -fn wrap_ssl_result(res: c_int) -> Option { - if res == 0 { - Some(SslError::get()) - } else { - None - } -} - -/// An SSL context object -pub struct SslContext { - ctx: ptr::Unique -} - -// TODO: add useful info here -impl fmt::Debug for SslContext { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "SslContext") - } -} - -impl Drop for SslContext { - fn drop(&mut self) { - unsafe { ffi::SSL_CTX_free(self.ctx.0) } - } -} - -impl SslContext { - /// Creates a new SSL context. - pub fn new(method: SslMethod) -> Result { - init(); - - let ctx = unsafe { ffi::SSL_CTX_new(method.to_raw()) }; - if ctx == ptr::null_mut() { - return Err(SslError::get()); - } - - Ok(SslContext { ctx: ptr::Unique(ctx) }) - } - - /// Configures the certificate verification method for new connections. - pub fn set_verify(&mut self, mode: SslVerifyMode, - verify: Option) { - unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx.0, VERIFY_IDX, - mem::transmute(verify)); - let f: extern fn(c_int, *mut ffi::X509_STORE_CTX) -> c_int = - raw_verify; - ffi::SSL_CTX_set_verify(self.ctx.0, mode as c_int, Some(f)); - } - } - - /// 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(&mut self, mode: SslVerifyMode, - verify: VerifyCallbackData, - data: T) { - let data = Box::new(data); - unsafe { - ffi::SSL_CTX_set_ex_data(self.ctx.0, VERIFY_IDX, - mem::transmute(Some(verify))); - ffi::SSL_CTX_set_ex_data(self.ctx.0, get_verify_data_idx::(), - mem::transmute(data)); - let f: extern fn(c_int, *mut ffi::X509_STORE_CTX) -> c_int = - raw_verify_with_data::; - ffi::SSL_CTX_set_verify(self.ctx.0, mode as c_int, Some(f)); - } - } - - /// Sets verification depth - pub fn set_verify_depth(&mut self, depth: u32) { - unsafe { - ffi::SSL_CTX_set_verify_depth(self.ctx.0, depth as c_int); - } - } - - #[allow(non_snake_case)] - /// Specifies the file that contains trusted CA certificates. - pub fn set_CA_file(&mut self, file: &Path) -> Option { - wrap_ssl_result( - unsafe { - let file = CString::from_slice(file.as_vec()); - ffi::SSL_CTX_load_verify_locations(self.ctx.0, file.as_ptr(), ptr::null()) - }) - } - - /// Specifies the file that contains certificate - pub fn set_certificate_file(&mut self, file: &Path, - file_type: X509FileType) -> Option { - wrap_ssl_result( - unsafe { - let file = CString::from_slice(file.as_vec()); - ffi::SSL_CTX_use_certificate_file(self.ctx.0, file.as_ptr(), file_type as c_int) - }) - } - - /// Specifies the file that contains private key - pub fn set_private_key_file(&mut self, file: &Path, - file_type: X509FileType) -> Option { - wrap_ssl_result( - unsafe { - let file = CString::from_slice(file.as_vec()); - ffi::SSL_CTX_use_PrivateKey_file(self.ctx.0, file.as_ptr(), file_type as c_int) - }) - } - - pub fn set_cipher_list(&mut self, cipher_list: &str) -> Option { - wrap_ssl_result( - unsafe { - let cipher_list = CString::from_slice(cipher_list.as_bytes()); - ffi::SSL_CTX_set_cipher_list(self.ctx.0, cipher_list.as_ptr()) - }) - } -} - -#[allow(dead_code)] -struct MemBioRef<'ssl> { - ssl: &'ssl Ssl, - bio: MemBio, -} - -impl<'ssl> MemBioRef<'ssl> { - fn read(&mut self, buf: &mut [u8]) -> Option { - (&mut self.bio as &mut Reader).read(buf).ok() - } - - fn write_all(&mut self, buf: &[u8]) { - let _ = (&mut self.bio as &mut Writer).write_all(buf); - } -} - -pub struct Ssl { - ssl: ptr::Unique -} - -// TODO: put useful information here -impl fmt::Debug for Ssl { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "Ssl") - } -} - -impl Drop for Ssl { - fn drop(&mut self) { - unsafe { ffi::SSL_free(self.ssl.0) } - } -} - -impl Ssl { - pub fn new(ctx: &SslContext) -> Result { - let ssl = unsafe { ffi::SSL_new(ctx.ctx.0) }; - if ssl == ptr::null_mut() { - return Err(SslError::get()); - } - let ssl = Ssl { ssl: ptr::Unique(ssl) }; - - let rbio = try!(MemBio::new()); - let wbio = try!(MemBio::new()); - - unsafe { ffi::SSL_set_bio(ssl.ssl.0, rbio.unwrap(), wbio.unwrap()) } - Ok(ssl) - } - - fn get_rbio<'a>(&'a self) -> MemBioRef<'a> { - unsafe { self.wrap_bio(ffi::SSL_get_rbio(self.ssl.0)) } - } - - fn get_wbio<'a>(&'a self) -> MemBioRef<'a> { - unsafe { self.wrap_bio(ffi::SSL_get_wbio(self.ssl.0)) } - } - - fn wrap_bio<'a>(&'a self, bio: *mut ffi::BIO) -> MemBioRef<'a> { - assert!(bio != ptr::null_mut()); - MemBioRef { - ssl: self, - bio: MemBio::borrowed(bio) - } - } - - fn connect(&self) -> c_int { - unsafe { ffi::SSL_connect(self.ssl.0) } - } - - fn accept(&self) -> c_int { - unsafe { ffi::SSL_accept(self.ssl.0) } - } - - fn read(&self, buf: &mut [u8]) -> c_int { - unsafe { ffi::SSL_read(self.ssl.0, buf.as_ptr() as *mut c_void, - buf.len() as c_int) } - } - - fn write_all(&self, buf: &[u8]) -> c_int { - unsafe { ffi::SSL_write(self.ssl.0, buf.as_ptr() as *const c_void, - buf.len() as c_int) } - } - - fn get_error(&self, ret: c_int) -> LibSslError { - let err = unsafe { ffi::SSL_get_error(self.ssl.0, ret) }; - match FromPrimitive::from_int(err as isize) { - Some(err) => err, - None => unreachable!() - } - } - - /// Set the host name to be used with SNI (Server Name Indication). - pub fn set_hostname(&self, hostname: &str) -> Result<(), SslError> { - let ret = unsafe { - // This is defined as a macro: - // #define SSL_set_tlsext_host_name(s,name) \ - // SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,(char *)name) - - let hostname = CString::from_slice(hostname.as_bytes()); - ffi::SSL_ctrl(self.ssl.0, ffi::SSL_CTRL_SET_TLSEXT_HOSTNAME, - ffi::TLSEXT_NAMETYPE_host_name, - hostname.as_ptr() as *mut c_void) - }; - - // For this case, 0 indicates failure. - if ret == 0 { - Err(SslError::get()) - } else { - Ok(()) - } - } - - pub fn get_peer_certificate(&self) -> Option { - unsafe { - let ptr = ffi::SSL_get_peer_certificate(self.ssl.0); - if ptr.is_null() { - None - } else { - Some(X509::new(ptr, true)) - } - } - } - -} - -#[derive(FromPrimitive, Debug)] -#[repr(i32)] -enum LibSslError { - ErrorNone = ffi::SSL_ERROR_NONE, - ErrorSsl = ffi::SSL_ERROR_SSL, - ErrorWantRead = ffi::SSL_ERROR_WANT_READ, - ErrorWantWrite = ffi::SSL_ERROR_WANT_WRITE, - ErrorWantX509Lookup = ffi::SSL_ERROR_WANT_X509_LOOKUP, - ErrorSyscall = ffi::SSL_ERROR_SYSCALL, - ErrorZeroReturn = ffi::SSL_ERROR_ZERO_RETURN, - ErrorWantConnect = ffi::SSL_ERROR_WANT_CONNECT, - ErrorWantAccept = ffi::SSL_ERROR_WANT_ACCEPT, -} - -/// A stream wrapper which handles SSL encryption for an underlying stream. -#[derive(Clone)] -pub struct SslStream { - stream: S, - ssl: Arc, - buf: Vec -} - -impl fmt::Debug for SslStream where S: fmt::Debug { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "SslStream {{ stream: {:?}, ssl: {:?} }}", self.stream, self.ssl) - } -} - -impl SslStream { - fn new_base(ssl:Ssl, stream: S) -> SslStream { - SslStream { - stream: stream, - ssl: Arc::new(ssl), - // Maximum TLS record size is 16k - // We're just using this as a buffer, so there's no reason to pay - // to memset it - buf: { - const CAP: usize = 16 * 1024; - let mut v = Vec::with_capacity(CAP); - unsafe { v.set_len(CAP); } - v - } - } - } - - pub fn new_server_from(ssl: Ssl, stream: S) -> Result, 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, 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, SslError> { - 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, 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, mut blk: F) - -> Result where F: FnMut(&Ssl) -> c_int { - loop { - let ret = blk(&*self.ssl); - if ret > 0 { - return Ok(ret); - } - - match self.ssl.get_error(ret) { - 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_all(&self.buf[..len]); - } - LibSslError::ErrorWantWrite => { try_ssl_stream!(self.flush()) } - LibSslError::ErrorZeroReturn => return Err(SslSessionClosed), - LibSslError::ErrorSsl => return Err(SslError::get()), - err => panic!("unexpected error {:?}", err), - } - } - } - - fn write_through(&mut self) -> IoResult<()> { - loop { - match self.ssl.get_wbio().read(self.buf.as_mut_slice()) { - Some(len) => try!(self.stream.write_all(&self.buf[..len])), - None => break - }; - } - Ok(()) - } - - /// Get the compression currently in use. The result will be - /// either None, indicating no compression is in use, or a string - /// with the compression name. - pub fn get_compression(&self) -> Option { - let ptr = unsafe { ffi::SSL_get_current_compression(self.ssl.ssl.0) }; - if ptr == ptr::null() { - return None; - } - - let meth = unsafe { ffi::SSL_COMP_get_name(ptr) }; - let s = unsafe { - String::from_utf8(c_str_to_bytes(&meth).to_vec()).unwrap() - }; - - Some(s) - } -} - -impl Reader for SslStream { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - match self.in_retry_wrapper(|ssl| { ssl.read(buf) }) { - Ok(len) => Ok(len as usize), - Err(SslSessionClosed) => - Err(IoError { - kind: EndOfFile, - desc: "SSL session closed", - detail: None - }), - Err(StreamError(e)) => Err(e), - _ => unreachable!() - } - } -} - -impl Writer for SslStream { - fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { - let mut start = 0; - while start < buf.len() { - let ret = self.in_retry_wrapper(|ssl| { - ssl.write_all(buf.split_at(start).1) - }); - match ret { - Ok(len) => start += len as usize, - _ => unreachable!() - } - try!(self.write_through()); - } - Ok(()) - } - - fn flush(&mut self) -> IoResult<()> { - try!(self.write_through()); - self.stream.flush() - } -} - -/// A utility type to help in cases where the use of SSL is decided at runtime. -#[derive(Debug)] -pub enum MaybeSslStream where S: Stream { - /// A connection using SSL - Ssl(SslStream), - /// A connection not using SSL - Normal(S), -} - -impl Reader for MaybeSslStream where S: Stream { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.read(buf), - MaybeSslStream::Normal(ref mut s) => s.read(buf), - } - } -} - -impl Writer for MaybeSslStream where S: Stream{ - fn write_all(&mut self, buf: &[u8]) -> IoResult<()> { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.write_all(buf), - MaybeSslStream::Normal(ref mut s) => s.write_all(buf), - } - } - - fn flush(&mut self) -> IoResult<()> { - match *self { - MaybeSslStream::Ssl(ref mut s) => s.flush(), - MaybeSslStream::Normal(ref mut s) => s.flush(), - } - } -} - -impl MaybeSslStream 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, - } - } -} diff --git a/src/ssl/tests.rs b/src/ssl/tests.rs deleted file mode 100644 index 73f479bf..00000000 --- a/src/ssl/tests.rs +++ /dev/null @@ -1,207 +0,0 @@ -use serialize::hex::FromHex; -use std::old_io::net::tcp::TcpStream; -use std::old_io::{Writer}; -use std::thread::Thread; - -use crypto::hash::Type::{SHA256}; -use ssl::SslMethod::Sslv23; -use ssl::{SslContext, SslStream, VerifyCallback}; -use ssl::SslVerifyMode::SslVerifyPeer; -use x509::{X509StoreContext}; - -#[test] -fn test_new_ctx() { - SslContext::new(Sslv23).unwrap(); -} - -#[test] -fn test_new_sslstream() { - 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 mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SslVerifyPeer, None); - match SslStream::new(&ctx, stream) { - 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 mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SslVerifyPeer, None); - match ctx.set_CA_file(&Path::new("test/cert.pem")) { - None => {} - Some(err) => panic!("Unexpected error {:?}", err) - } - match SslStream::new(&ctx, stream) { - Ok(_) => (), - Err(err) => panic!("Expected success, got {:?}", err) - } -} - -#[test] -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 mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); - match SslStream::new(&ctx, stream) { - Ok(_) => (), - Err(err) => panic!("Expected success, got {:?}", err) - } -} - -#[test] -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 mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); - assert!(SslStream::new(&ctx, stream).is_err()); -} - -#[test] -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 mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); - match ctx.set_CA_file(&Path::new("test/cert.pem")) { - None => {} - Some(err) => panic!("Unexpected error {:?}", err) - } - match SslStream::new(&ctx, stream) { - Ok(_) => (), - Err(err) => panic!("Expected success, got {:?}", err) - } -} - -#[test] -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 mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); - match ctx.set_CA_file(&Path::new("test/cert.pem")) { - None => {} - Some(err) => panic!("Unexpected error {:?}", err) - } - assert!(SslStream::new(&ctx, stream).is_err()); -} - -#[test] -fn test_verify_callback_load_certs() { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { - assert!(x509_ctx.get_current_cert().is_some()); - true - } - let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); - let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); - assert!(SslStream::new(&ctx, stream).is_ok()); -} - -#[test] -fn test_verify_trusted_get_error_ok() { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { - assert!(x509_ctx.get_error().is_none()); - true - } - let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); - let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); - match ctx.set_CA_file(&Path::new("test/cert.pem")) { - None => {} - Some(err) => panic!("Unexpected error {:?}", err) - } - assert!(SslStream::new(&ctx, stream).is_ok()); -} - -#[test] -fn test_verify_trusted_get_error_err() { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool { - assert!(x509_ctx.get_error().is_some()); - false - } - let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); - let mut ctx = SslContext::new(Sslv23).unwrap(); - ctx.set_verify(SslVerifyPeer, Some(callback as VerifyCallback)); - assert!(SslStream::new(&ctx, stream).is_err()); -} - -#[test] -fn test_verify_callback_data() { - fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext, node_id: &Vec) -> 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 mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); - stream.write_all("hello".as_bytes()).unwrap(); - stream.flush().unwrap(); - stream.write_all(" there".as_bytes()).unwrap(); - stream.flush().unwrap(); -} - -#[test] -fn test_read() { - let stream = TcpStream::connect("127.0.0.1:15418").unwrap(); - let mut stream = SslStream::new(&SslContext::new(Sslv23).unwrap(), stream).unwrap(); - stream.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); - stream.flush().unwrap(); - 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(); - let _t = Thread::spawn(move || { - stream2.write_all("GET /\r\n\r\n".as_bytes()).unwrap(); - stream2.flush().unwrap(); - }); - stream.read_to_end().ok().expect("read error"); -} diff --git a/src/x509/mod.rs b/src/x509/mod.rs deleted file mode 100644 index 6c09f8b2..00000000 --- a/src/x509/mod.rs +++ /dev/null @@ -1,525 +0,0 @@ -use libc::{c_char, c_int, c_long, c_uint}; -use std::cmp::Ordering; -use std::ffi::CString; -use std::iter::repeat; -use std::mem; -use std::num::SignedInt; -use std::ptr; - -use asn1::{Asn1Time}; -use bio::{MemBio}; -use crypto::hash; -use crypto::hash::Type as HashType; -use crypto::pkey::{PKey}; -use crypto::rand::rand_bytes; -use ffi; -use ssl::error::{SslError, StreamError}; - - -#[cfg(test)] -mod tests; - -#[derive(Copy)] -#[repr(i32)] -pub enum X509FileType { - PEM = ffi::X509_FILETYPE_PEM, - ASN1 = ffi::X509_FILETYPE_ASN1, - Default = ffi::X509_FILETYPE_DEFAULT -} - -#[allow(missing_copy_implementations)] -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 { - let err = unsafe { ffi::X509_STORE_CTX_get_error(self.ctx) }; - X509ValidationError::from_raw(err) - } - - pub fn get_current_cert<'a>(&'a self) -> Option> { - 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; -} - -#[derive(Clone, Copy)] -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" - } - } -} - - -#[derive(Clone, Copy)] -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 { - 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 -/// -/// ``` -/// # #[allow(unstable)] -/// # fn main() { -/// use std::old_io::{File, Open, Write}; -/// # use std::old_io::fs; -/// -/// use openssl::crypto::hash::Type; -/// use openssl::x509::{KeyUsage, X509Generator}; -/// -/// let gen = X509Generator::new() -/// .set_bitlength(2048) -/// .set_valid_period(365*2) -/// .set_CN("SuperMegaCorp Inc.") -/// .set_sign_hash(Type::SHA256) -/// .set_usage(&[KeyUsage::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: u32, - days: u32, - CN: String, - key_usage: Vec, - ext_key_usage: Vec, - 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: u32) -> X509Generator { - self.bits = bits; - self - } - - /// Sets certificate validity period in days since today - pub fn set_valid_period(mut self, days: u32) -> 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: hash::Type) -> 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 value = CString::from_slice(value.as_bytes()); - let ext = ffi::X509V3_EXT_conf_nid(ptr::null_mut(), - mem::transmute(&ctx), - extension, - value.as_ptr() as *mut c_char); - - 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!(unsafe { - let key = CString::from_slice(key.as_bytes()); - let value = CString::from_slice(value.as_bytes()); - ffi::X509_NAME_add_entry_by_txt(name, key.as_ptr(), ffi::MBSTRING_UTF8, - value.as_ptr(), value_len, -1, 0) - }) - } - - fn random_serial() -> c_long { - let len = mem::size_of::(); - 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 as usize); - - 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 = self.hash_type.evp_md(); - 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 R) -> Result, SslError> where R: Reader { - let mut mem_bio = try!(MemBio::new()); - let buf = try!(reader.read_to_end().map_err(StreamError)); - try!(mem_bio.write_all(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: hash::Type) -> Option> { - let evp = hash_type.evp_md(); - let len = hash_type.md_len(); - let v: Vec = repeat(0).take(len as usize).collect(); - 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 usize; - match len.cmp(&act_len) { - Ordering::Greater => None, - Ordering::Equal => Some(v), - Ordering::Less => panic!("Fingerprint buffer was corrupted!") - } - } - } - } - - /// Writes certificate as PEM - pub fn write_pem(&self, writer: &mut W) -> Result<(), SslError> where W: Writer{ - 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_all(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,)+) => ( - #[derive(Copy)] - pub enum X509ValidationError { - $($name,)+ - X509UnknownError(c_int) - } - - impl X509ValidationError { - #[doc(hidden)] - pub fn from_raw(err: c_int) -> Option { - 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(0, 1000) { - assert!(X509Generator::random_serial() > 0, "All serials should be positive"); - } -} diff --git a/src/x509/tests.rs b/src/x509/tests.rs deleted file mode 100644 index 4f24e70c..00000000 --- a/src/x509/tests.rs +++ /dev/null @@ -1,51 +0,0 @@ -use serialize::hex::FromHex; -use std::old_io::{File, Open, Read}; -use std::old_io::util::NullWriter; - -use crypto::hash::Type::{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()); -} diff --git a/test/cert.pem b/test/cert.pem deleted file mode 100644 index 1523c736..00000000 --- a/test/cert.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDXTCCAkWgAwIBAgIJAMWJMG/NYWQyMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTQxMTIwMDU0MjIxWhcNMTcxMTE5MDU0MjIxWjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA2E9uYoLpX4eMGz6+l+1ZdD11Y1PQjNjqSA7/nq0Q6gogPLds99a+Ca7B -2w6mWGMpCQbJTf0tOkdF6Td/gwIBNYtHuCIAiPh2Gbm6oZErVIXWwWuTWC2r7myB -0ePga5ZAE9SqsFqMEuhWikEK1+ae1CCfmbogsQSXyl4+EVN7xAwdi6yUtRL/92nn -ImKdwDBhuqzdBpBODQW/VCn0KG54CvWdwT0iqg7CvLuXQGyxM8K17SAiBtn6N38S -0jeL4D9IrBfi9PCYGpAn3jKr4oBEJVRCNvvni3Q9ikJ+GEF5nvLLVM+I98/o0vCu -Y2o7+1LiwViGONJ1BfRgdFWMjz0BfQIDAQABo1AwTjAdBgNVHQ4EFgQU509vewJT -k6qdvjT8e52gTE4+V9EwHwYDVR0jBBgwFoAU509vewJTk6qdvjT8e52gTE4+V9Ew -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAnLxMvrhgQrdKUbd+RSl3 -i6tEfexDepO5QOMIL/m7w/OAkXddu7Y/IuLSgAztp7cu8tQeeBTqbO9T9XKRd1ZD -hMN3QPZwX1ftREPd1mhe3LQgJ/Q3AbrtlW+0XcANBCTiYi19X193sU2a3Z2nzd5v -vSvrP1W3Yxzvu7Jwvhgl20BQ0fREpPVfZTrJRhg8/jdLA8TmKZA7012LKh59BCEX -dXgXc3KYb+Fn8usj79P1nP1YhYmX0Lp0IuC0i1z+FoP/NxNMe+ebXPjM9KlkvQK1 -Vy9sIxV9MAlXpGsMvBJAte94JRikYkQTPjMT1DGQQYGpHIYb3a8jcWvmZPEEaqhB -Ag== ------END CERTIFICATE----- diff --git a/test/key.pem b/test/key.pem deleted file mode 100644 index a3f80dd9..00000000 --- a/test/key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDYT25igulfh4wb -Pr6X7Vl0PXVjU9CM2OpIDv+erRDqCiA8t2z31r4JrsHbDqZYYykJBslN/S06R0Xp -N3+DAgE1i0e4IgCI+HYZubqhkStUhdbBa5NYLavubIHR4+BrlkAT1KqwWowS6FaK -QQrX5p7UIJ+ZuiCxBJfKXj4RU3vEDB2LrJS1Ev/3aeciYp3AMGG6rN0GkE4NBb9U -KfQobngK9Z3BPSKqDsK8u5dAbLEzwrXtICIG2fo3fxLSN4vgP0isF+L08JgakCfe -MqvigEQlVEI2++eLdD2KQn4YQXme8stUz4j3z+jS8K5jajv7UuLBWIY40nUF9GB0 -VYyPPQF9AgMBAAECggEBAJiYiH20bqA2xk8eF2SkSxvmk15r7U6/Y59L/WZaHvmM -BSvwFk5MzqmUACviDNWDtpookHCVL4fSae5ZeXnZOzMju4eZbRkzdlU1ogSCnbe1 -50dx9XMaXRUItRh1koczaqbSu0tHxVM9VneX5OdkSR3Kmezf0lourEpV66FbbI9i -1F1Q7u6TzldTuPSkQQgV/FHU9DvRPJ6HgSOyVr6Z9Ec0K6odmUXe3wX7+3PbKPtr -JIVQ0wGcc/sImgAr0uS+YbHNWM4qjFAAPteQ/+Df6usSFOkRoD3+XeZrJQQ98C3q -HHW4afaJM0YCsDwn7/E3KiY5fmXwtAHNRuUbsfReP8ECgYEA9JQICyP1N5bZ4uCc -jjTiHLcQX2dHy4dKatqWkV4F52qf4HCZ/pcvPBKNMzM4uTfkCgR+TMzW+A+escvR -8KmaSKmQHT+nUQfMoU2lpZbWSPTF8lLGx+Mf8JAMur0xcmazDB8uDFnvQg+TQY7y -cF6MMWKW3pp+3qI7wRkclXSLZG0CgYEA4mlzuzuB8e7UJ821N+zD8BBYY4EvpUIj -iparwKbM8vAZ1WZssRd+8oHroHJGbjXX4h7gvpUsVadSgs77W9T0zJ+5kJCpVAnO -nKdJkX1Zo1TaIIrRaJhiaPU4hKlnGnko3uv7SlV9PPUtcyBnXElobREmQv6eCmEf -Z7YP4+JoR1ECgYEA3RyrfO7gNYZyq3Mm9kWHGjDCY43q0W0ZcSr3LqrTKZkyuuTx -w8IImQWok9497O1Dg272hBY4ToFIljLPNQUQD5sER/0RFee4LygUlnSce86W2nHN -dk62xHRmnbiHaIbCXjYeGlqAPLf6CC3kroQ7uDYKcWs5Qatn3DYIqnF3x60CgYA/ -plWatUf6s6GA7xua9TzAKFgw4Qh79PP46hKuvjWvtkAM9hZoUqqlklCjcnzKTui5 -8ORNr7IfAkL38yhG0L9hJyYLth9kOL2U3JKaDBs/B4Oq0lu8g9pml0mkQdtyXc1X -ng+u/gmPMX3td5aXIyvwPXn8K4hScqtZhJ1C+0tFgQKBgQDtlBXw3mY3AMwT+wtx -ZiitDk7tAXj7wioCOziTFKkL01UMt4VIkNvQU7RUTcig6PBc0PxU6TZQpncOk/cP -eqQQP0TJGNzIK71EwAL5aFm9o9VoXsrwjVDZnzMr703MyU15QXO76kmxmh+rK5Qy -uldCJliojIW1UW30MXSXK96YXQ== ------END PRIVATE KEY----- -- cgit v1.2.3