diff --git a/.travis.yml b/.travis.yml index 55ba716dd0f973676e75144d9c5297eddfd17f8c..795c937062dbea54fbd8e5419ec556788e766823 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,17 @@ env: - BUILD_OPENSSL_VERSION=1.0.1u matrix: include: + # osx 32/64 + - os: osx + env: TARGET=x86_64-apple-darwin + - os: osx + env: TARGET=i686-apple-darwin + install: brew uninstall openssl && brew install openssl --universal --without-test + # ARM-bit version compat - env: > TARGET=arm-unknown-linux-gnueabihf - BUILD_OPENSSL_VERSION=1.0.2h + BUILD_OPENSSL_VERSION=1.0.2k CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf RUST_TEST_THREADS=1 @@ -25,7 +32,7 @@ matrix: - binfmt-support - env: > TARGET=arm-unknown-linux-gnueabihf - BUILD_OPENSSL_VERSION=1.1.0b + BUILD_OPENSSL_VERSION=1.1.0d CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf RUST_TEST_THREADS=1 @@ -38,15 +45,15 @@ matrix: - binfmt-support # Minimum version supported - - rust: 1.9.0 + - rust: 1.10.0 # beta/nightly channels - rust: beta - rust: nightly # 64-bit version compat - - env: BUILD_OPENSSL_VERSION=1.0.2h - - env: BUILD_OPENSSL_VERSION=1.1.0b + - env: BUILD_OPENSSL_VERSION=1.0.2k + - env: BUILD_OPENSSL_VERSION=1.1.0d # 32-bit version compat - env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.0.1u @@ -54,24 +61,19 @@ matrix: apt: packages: - gcc-multilib - - env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.0.2h + - env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.0.2k addons: apt: packages: - gcc-multilib - - env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.1.0b + - env: TARGET=i686-unknown-linux-gnu BUILD_OPENSSL_VERSION=1.1.0d addons: apt: packages: - gcc-multilib - # osx 32/64 - - os: osx - env: TARGET=x86_64-apple-darwin - - os: osx - env: TARGET=i686-apple-darwin - install: brew uninstall openssl && brew install openssl --universal - + # LibreSSL + - env: BUILD_LIBRESSL_VERSION=2.5.0 before_install: - ./openssl/test/build.sh @@ -83,4 +85,5 @@ script: cache: cargo: true directories: + - $HOME/libressl - $HOME/openssl diff --git a/README.md b/README.md index 3f17a571af161ac5d59172d1e58350bdf4513fc1..3bc3960b3d24068e8aae54ddff3a15df1dd47f29 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,18 @@ [![Build Status](https://travis-ci.org/sfackler/rust-openssl.svg?branch=master)](https://travis-ci.org/sfackler/rust-openssl) -[Documentation](https://sfackler.github.io/rust-openssl/doc/v0.9.0/openssl). +[Documentation](https://docs.rs/openssl). ## Warning -This README does not correspond to rust-openssl 0.7.x. See +This README does not correspond to rust-openssl 0.7.x or 0.8.x. See [here](https://github.com/sfackler/rust-openssl/blob/b8fb29db5c246175a096260eacca38180cd77dd0/README.md) for that README. ## Building -rust-openssl depends on the OpenSSL runtime libraries version 1.0.1 or above. -Currently the libraries need to be present in the build environment before this +rust-openssl depends on OpenSSL version 1.0.1 or above, or LibreSSL. Both the +libraries and headers need to be present in the build environment before this crate is compiled, and some instructions of how to do this are in the sections below. @@ -37,9 +37,9 @@ compiling to a separate target, you'll typically need to compile OpenSSL from source. That can normally be done with: ``` -curl -O https://www.openssl.org/source/openssl-1.1.0b.tar.gz -tar xf openssl-1.1.0b.tar.gz -cd openssl-1.1.0b +curl -O https://www.openssl.org/source/openssl-1.1.0c.tar.gz +tar xf openssl-1.1.0c.tar.gz +cd openssl-1.1.0c export CC=... ./Configure --prefix=... linux-x86_64 -fPIC make -j$(nproc) @@ -49,20 +49,29 @@ make install ### OSX Although OpenSSL 0.9.8 is preinstalled on OSX this library is being phased out -of OSX and this crate also does not support this version of OpenSSL. To use this +of OSX and this crate also does not support that version of OpenSSL. To use this crate on OSX you'll need to install OpenSSL via some alternate means, typically -homebrew: +Homebrew: ```bash brew install openssl ``` +> Occasionally an update of XCode or MacOS will cause the linker to fail after compilation, to rectify this you may want to try and run: + +```bash +xcode-select --install +``` + +If Homebrew is installed to the default location of `/usr/local`, OpenSSL will be +automatically detected. + ### Windows MSVC On MSVC it's unfortunately not always a trivial process acquiring OpenSSL. Perhaps the easiest way to do this right now is to download [precompiled binaries] and install them on your system. Currently it's recommended to -install the 1.1.0b light installation if you're choosing this route. +install the 1.1.0 (non-light) installation if you're choosing this route. [precompiled binaries]: http://slproweb.com/products/Win32OpenSSL.html @@ -73,6 +82,18 @@ installation via an environment variable: set OPENSSL_DIR=C:\OpenSSL-Win64 ``` +Note that this OpenSSL distribution does not ship with any root certificates. +So to make requests to servers on the internet, you have to install them +manually. Download the [cacert.pem file from here], copy it somewhere safe +(`C:\OpenSSL-Win64\certs` is a good place) and point the `SSL_CERT_FILE` +environment variable there: + +``` +set SSL_CERT_FILE=C:\OpenSSL-Win64\certs\cacert.pem +``` + +[cacert.pem file from here]: https://curl.haxx.se/docs/caextract.html + After that, you're just a `cargo build` away! ### Windows GNU (MinGW) @@ -102,6 +123,12 @@ The build script can be configured via environment variables: * `OPENSSL_DIR` - If specified, a directory that will be used to find OpenSSL installation. It's expected that under this directory the `include` folder has header files and a `lib` folder has the runtime libraries. +* `OPENSSL_LIB_DIR` - If specified, a directory that will be used to find + OpenSSL libraries. Overrides the `lib` folder implied by `OPENSSL_DIR` + (if specified). +* `OPENSSL_INCLUDE_DIR` - If specified, a directory that will be used to find + OpenSSL header files. Overrides the `include` folder implied by `OPENSSL_DIR` + (if specified). * `OPENSSL_STATIC` - If specified, OpenSSL libraries will be statically rather than dynamically linked. diff --git a/appveyor.yml b/appveyor.yml index 3e23044e537e7ccd12a0350b746a17a3959c8c0a..d396bd622ec00d9ff45818ff63d77bafbad927a4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,20 +5,20 @@ environment: - TARGET: i686-pc-windows-gnu BITS: 32 MSYS2: 1 - OPENSSL_VERSION: 1_1_0b + OPENSSL_VERSION: 1_1_0d - TARGET: x86_64-pc-windows-msvc BITS: 64 - OPENSSL_VERSION: 1_1_0b + OPENSSL_VERSION: 1_1_0d OPENSSL_DIR: C:\OpenSSL # 1.0.2, 64/32 bit - TARGET: x86_64-pc-windows-gnu BITS: 64 MSYS2: 1 - OPENSSL_VERSION: 1_0_2j + OPENSSL_VERSION: 1_0_2k - TARGET: i686-pc-windows-msvc BITS: 32 - OPENSSL_VERSION: 1_0_2j + OPENSSL_VERSION: 1_0_2k OPENSSL_DIR: C:\OpenSSL install: # install OpenSSL diff --git a/openssl-sys/Cargo.toml b/openssl-sys/Cargo.toml index 11c20a1c21d025e797ec8bcf1d1a72d4b1fd6a98..76bd9172c4276d78355ed30fc908cb749243d8b7 100644 --- a/openssl-sys/Cargo.toml +++ b/openssl-sys/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "openssl-sys" -version = "0.9.0" +version = "0.9.6" authors = ["Alex Crichton ", "Steven Fackler "] license = "MIT" description = "FFI bindings to OpenSSL" repository = "https://github.com/sfackler/rust-openssl" -documentation = "https://sfackler.github.io/rust-openssl/doc/v0.9.0/openssl_sys" +documentation = "https://docs.rs/openssl-sys/0.9.6/openssl_sys" +categories = ["cryptography", "external-ffi-bindings"] links = "openssl" build = "build.rs" @@ -14,8 +15,13 @@ build = "build.rs" libc = "0.2" [build-dependencies] -pkg-config = "0.3" +pkg-config = "0.3.9" +gcc = "0.3.42" [target.'cfg(windows)'.dependencies] user32-sys = "0.2" gdi32-sys = "0.2" + +# We don't actually use metadeps for annoying reasons but this is still here for tooling +[package.metadata.pkg-config] +openssl = "1.0.1" diff --git a/openssl-sys/build.rs b/openssl-sys/build.rs index b6c8c2de4ca1956b928428a5380446659bb4c134..fc0f4680ca598021f1e920d57bfe9137a0ac8c07 100644 --- a/openssl-sys/build.rs +++ b/openssl-sys/build.rs @@ -1,27 +1,60 @@ extern crate pkg_config; +extern crate gcc; use std::collections::HashSet; use std::env; use std::ffi::OsString; use std::fs::File; -use std::io::Read; +use std::io::{BufWriter, Write}; use std::path::{Path, PathBuf}; +use std::panic::{self, AssertUnwindSafe}; use std::process::Command; +// The set of `OPENSSL_NO_`s that we care about. +const DEFINES: &'static [&'static str] = &[ + "OPENSSL_NO_BUF_FREELISTS", + "OPENSSL_NO_COMP", + "OPENSSL_NO_EC", + "OPENSSL_NO_ENGINE", + "OPENSSL_NO_KRB5", + "OPENSSL_NO_NEXTPROTONEG", + "OPENSSL_NO_PSK", + "OPENSSL_NO_RFC3779", + "OPENSSL_NO_SHA", + "OPENSSL_NO_SRP", + "OPENSSL_NO_SSL3_METHOD", + "OPENSSL_NO_TLSEXT", +]; + +enum Version { + Openssl110, + Openssl102, + Openssl101, + Libressl, +} + fn main() { let target = env::var("TARGET").unwrap(); - let openssl_dir = env::var_os("OPENSSL_DIR").unwrap_or_else(|| { - find_openssl_dir(&target) - }); + let lib_dir = env::var_os("OPENSSL_LIB_DIR").map(PathBuf::from); + let include_dir = env::var_os("OPENSSL_INCLUDE_DIR").map(PathBuf::from); + + let (lib_dir, include_dir) = if lib_dir.is_none() || include_dir.is_none() { + let openssl_dir = env::var_os("OPENSSL_DIR").unwrap_or_else(|| { + find_openssl_dir(&target) + }); + let openssl_dir = Path::new(&openssl_dir); + let lib_dir = lib_dir.unwrap_or_else(|| openssl_dir.join("lib")); + let include_dir = include_dir.unwrap_or_else(|| openssl_dir.join("include")); + (lib_dir, include_dir) + } else { + (lib_dir.unwrap(), include_dir.unwrap()) + }; - let lib_dir = Path::new(&openssl_dir).join("lib"); - let include_dir = Path::new(&openssl_dir).join("include"); if !Path::new(&lib_dir).exists() { panic!("OpenSSL library directory does not exist: {}", lib_dir.to_string_lossy()); } - if !Path::new(&include_dir).exists() { panic!("OpenSSL include directory does not exist: {}", include_dir.to_string_lossy()); @@ -30,17 +63,14 @@ fn main() { println!("cargo:rustc-link-search=native={}", lib_dir.to_string_lossy()); println!("cargo:include={}", include_dir.to_string_lossy()); - let version = validate_headers(&[include_dir.clone().into()], - &[lib_dir.clone().into()]); + let version = validate_headers(&[include_dir.clone().into()]); - let libs = if (version.contains("0x10001") || - version.contains("0x10002")) && - target.contains("windows") { - ["ssleay32", "libeay32"] - } else if target.contains("windows") { - ["libssl", "libcrypto"] - } else { - ["ssl", "crypto"] + let libs = match version { + Version::Openssl101 | Version::Openssl102 if target.contains("windows") => { + ["ssleay32", "libeay32"] + } + Version::Openssl110 if target.contains("windows") => ["libssl", "libcrypto"], + _ => ["ssl", "crypto"], }; let kind = determine_mode(Path::new(&lib_dir), &libs); @@ -160,29 +190,12 @@ fn try_pkg_config() { return } - // We're going to be looking at header files, so show us all the system - // cflags dirs for showing us lots of `-I`. - env::set_var("PKG_CONFIG_ALLOW_SYSTEM_CFLAGS", "1"); - - let lib = match pkg_config::find_library("openssl") { - Ok(lib) => lib, - Err(_) => return, - }; + let lib = pkg_config::Config::new() + .print_system_libs(false) + .find("openssl") + .unwrap(); - if lib.include_paths.len() == 0 { - panic!(" - -Used pkg-config to discover the OpenSSL installation, but pkg-config did not -return any include paths for the installation. This crate needs to take a peek -at the header files so it cannot proceed unless they're found. - -You can try fixing this by setting the `OPENSSL_DIR` environment variable -pointing to your OpenSSL installation. - -"); - } - - validate_headers(&lib.include_paths, &lib.link_paths); + validate_headers(&lib.include_paths); for include in lib.include_paths.iter() { println!("cargo:include={}", include.display()); @@ -193,65 +206,11 @@ pointing to your OpenSSL installation. /// Validates the header files found in `include_dir` and then returns the /// version string of OpenSSL. -fn validate_headers(include_dirs: &[PathBuf], - libdirs: &[PathBuf]) -> String { +fn validate_headers(include_dirs: &[PathBuf]) -> Version { // This `*-sys` crate only works with OpenSSL 1.0.1, 1.0.2, and 1.1.0. To // correctly expose the right API from this crate, take a look at // `opensslv.h` to see what version OpenSSL claims to be. - let mut version_header = String::new(); - let mut include = include_dirs.iter() - .map(|p| p.join("openssl/opensslv.h")) - .filter(|p| p.exists()); - let mut f = match include.next() { - Some(f) => File::open(f).unwrap(), - None => { - panic!("failed to open header file at `openssl/opensslv.h` to learn - about OpenSSL's version number, looked inside:\n\n{:#?}\n\n", - include_dirs); - } - }; - f.read_to_string(&mut version_header).unwrap(); - - // Do a bit of string parsing to find `#define OPENSSL_VERSION_NUMBER ...` - let version_line = version_header.lines().find(|l| { - l.contains("define ") && l.contains("OPENSSL_VERSION_NUMBER") - }).and_then(|line| { - let start = match line.find("0x") { - Some(start) => start, - None => return None, - }; - Some(line[start..].trim()) - }); - let version_text = match version_line { - Some(text) => text, - None => { - panic!("header file at `{}` did not include `OPENSSL_VERSION_NUMBER` \ - that this crate recognized, failed to learn about the \ - OpenSSL version number"); - } - }; - if version_text.contains("0x10001") { - println!("cargo:rustc-cfg=ossl101"); - println!("cargo:version=101"); - } else if version_text.contains("0x10002") { - println!("cargo:rustc-cfg=ossl102"); - println!("cargo:version=102"); - } else if version_text.contains("0x10100") { - println!("cargo:rustc-cfg=ossl110"); - println!("cargo:version=110"); - } else { - panic!(" - -This crate is only compatible with OpenSSL 1.0.1, 1.0.2, and 1.1.0, but a -different version of OpenSSL was found: - - {} - -The build is now aborting due to this version mismatch. - -", version_text); - } - + // // OpenSSL has a number of build-time configuration options which affect // various structs and such. Since OpenSSL 1.1.0 this isn't really a problem // as the library is much more FFI-friendly, but 1.0.{1,2} suffer this problem. @@ -260,56 +219,105 @@ The build is now aborting due to this version mismatch. // file of OpenSSL, `opensslconf.h`, and then dump out everything it defines // as our own #[cfg] directives. That way the `ossl10x.rs` bindings can // account for compile differences and such. - let mut conf_header = String::new(); - let mut include = include_dirs.iter() - .map(|p| p.join("openssl/opensslconf.h")) - .filter(|p| p.exists()); - let mut f = match include.next() { - Some(f) => File::open(f).unwrap(), - None => { - // It's been seen that on linux the include dir printed out by - // `pkg-config` doesn't actually have opensslconf.h. Instead - // it's in an architecture-specific include directory. - // - // Try to detect that case to see if it exists. - let mut libdirs = libdirs.iter().map(|p| { - p.iter() - .map(|p| if p == "lib" {"include".as_ref()} else {p}) - .collect::() - }).map(|p| { - p.join("openssl/opensslconf.h") - }).filter(|p| p.exists()); - match libdirs.next() { - Some(f) => File::open(f).unwrap(), - None => { - panic!("failed to open header file at - `openssl/opensslconf.h` to learn about \ - OpenSSL's version number, looked \ - inside:\n\n{:#?}\n\n", - include_dirs); - } - } + let mut path = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + path.push("expando.c"); + let mut file = BufWriter::new(File::create(&path).unwrap()); + + write!(file, "\ +#include +#include + +#ifdef LIBRESSL_VERSION_NUMBER +RUST_LIBRESSL +#elif OPENSSL_VERSION_NUMBER >= 0x10200000 +RUST_OPENSSL_NEW +#elif OPENSSL_VERSION_NUMBER >= 0x10100000 +RUST_OPENSSL_110 +#elif OPENSSL_VERSION_NUMBER >= 0x10002000 +RUST_OPENSSL_102 +#elif OPENSSL_VERSION_NUMBER >= 0x10001000 +RUST_OPENSSL_101 +#else +RUST_OPENSSL_OLD +#endif +").unwrap(); + + for define in DEFINES { + write!(file, "\ +#ifdef {define} +RUST_{define} +#endif +", define = define).unwrap(); + } + + file.flush().unwrap(); + drop(file); + + let mut gcc = gcc::Config::new(); + for include_dir in include_dirs { + gcc.include(include_dir); + } + // https://github.com/alexcrichton/gcc-rs/issues/133 + let expanded = match panic::catch_unwind(AssertUnwindSafe(|| gcc.file(&path).expand())) { + Ok(expanded) => expanded, + Err(_) => { + panic!(" +Failed to find OpenSSL development headers. + +You can try fixing this setting the `OPENSSL_DIR` environment variable +pointing to your OpenSSL installation or installing OpenSSL headers package +specific to your distribution: + + # On Ubuntu + sudo apt-get install libssl-dev + # On Arch Linux + sudo pacman -S openssl + # On Fedora + sudo dnf install openssl-devel + +See rust-openssl README for more information: + + https://github.com/sfackler/rust-openssl#linux +"); } }; - f.read_to_string(&mut conf_header).unwrap(); - - // Look for `#define OPENSSL_FOO`, print out everything as our own - // #[cfg] flag. - let mut vars = vec![]; - for line in conf_header.lines() { - let i = match line.find("define ") { - Some(i) => i, - None => continue, - }; - let var = line[i + "define ".len()..].trim(); - if var.starts_with("OPENSSL") && !var.contains(" ") { - println!("cargo:rustc-cfg=osslconf=\"{}\"", var); - vars.push(var); + let expanded = String::from_utf8(expanded).unwrap(); + + let mut enabled = vec![]; + for &define in DEFINES { + if expanded.contains(&format!("RUST_{}", define)) { + println!("cargo:rustc-cfg=osslconf=\"{}\"", define); + enabled.push(define); } } - println!("cargo:conf={}", vars.join(",")); + println!("cargo:conf={}", enabled.join(",")); + + if expanded.contains("RUST_LIBRESSL") { + println!("cargo:rustc-cfg=libressl"); + println!("cargo:libressl=true"); + println!("cargo:version=101"); + Version::Libressl + } else if expanded.contains("RUST_OPENSSL_110") { + println!("cargo:rustc-cfg=ossl110"); + println!("cargo:version=110"); + Version::Openssl110 + } else if expanded.contains("RUST_OPENSSL_102") { + println!("cargo:rustc-cfg=ossl102"); + println!("cargo:version=102"); + Version::Openssl102 + } else if expanded.contains("RUST_OPENSSL_101") { + println!("cargo:rustc-cfg=ossl101"); + println!("cargo:version=101"); + Version::Openssl101 + } else { + panic!(" - return version_text.to_string() +This crate is only compatible with OpenSSL 1.0.1, 1.0.2, and 1.1.0, or LibreSSL, +but a different version of OpenSSL was found. The build is now aborting due to +this version mismatch. + +"); + } } /// Given a libdir for OpenSSL (where artifacts are located) as well as the name diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 5852e7d0755f355d48984962c94873b8ecbdc0f4..3d90461b0f998896571d8ca913ae1bea9f0e4d08 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -1,11 +1,12 @@ #![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)] #![allow(dead_code, overflowing_literals)] -#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.9.0")] +#![doc(html_root_url="https://docs.rs/openssl-sys/0.9.6")] extern crate libc; use libc::{c_void, c_int, c_char, c_ulong, c_long, c_uint, c_uchar, size_t, FILE}; use std::ptr; +use std::mem; #[cfg(any(ossl101, ossl102))] mod ossl10x; @@ -17,46 +18,72 @@ mod ossl110; #[cfg(ossl110)] pub use ossl110::*; +#[cfg(libressl)] +mod libressl; +#[cfg(libressl)] +pub use libressl::*; + pub enum ASN1_INTEGER {} +pub enum ASN1_GENERALIZEDTIME {} pub enum ASN1_STRING {} +pub enum ASN1_BIT_STRING {} pub enum ASN1_TIME {} pub enum ASN1_TYPE {} +pub enum ASN1_OBJECT {} pub enum BN_CTX {} pub enum BN_GENCB {} pub enum CONF {} pub enum CONF_METHOD {} pub enum COMP_METHOD {} pub enum EC_KEY {} +pub enum EC_GROUP {} +pub enum EC_METHOD {} +pub enum EC_POINT {} pub enum ENGINE {} pub enum EVP_CIPHER_CTX {} pub enum EVP_MD {} pub enum EVP_PKEY_CTX {} -pub enum SSL {} +pub enum OCSP_BASICRESP {} +pub enum OCSP_CERTID {} +pub enum OCSP_RESPONSE {} +pub enum OCSP_REQUEST {} +pub enum OCSP_ONEREQ {} pub enum SSL_CIPHER {} pub enum SSL_METHOD {} pub enum X509_CRL {} pub enum X509_EXTENSION {} pub enum X509_NAME {} pub enum X509_NAME_ENTRY {} -pub enum X509_REQ {} +pub enum X509_STORE {} pub enum X509_STORE_CTX {} pub enum bio_st {} -pub enum PKCS12 {} pub enum DH_METHOD {} - -pub type bio_info_cb = Option; - pub enum RSA_METHOD {} pub enum BN_MONT_CTX {} pub enum BN_BLINDING {} pub enum DSA_METHOD {} pub enum EVP_PKEY_ASN1_METHOD {} +pub type bio_info_cb = Option; +pub type GEN_SESSION_CB = Option c_int>; +pub type tls_session_ticket_ext_cb_fn = Option c_int>; +pub type tls_session_secret_cb_fn = Option c_int>; + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum point_conversion_form_t { + POINT_CONVERSION_COMPRESSED = 2, + POINT_CONVERSION_UNCOMPRESSED = 4, + POINT_CONVERSION_HYBRID = 6, +} + +#[repr(C)] +pub struct AES_KEY { + // There is some business with AES_LONG which is there to ensure the values here are 32 bits + rd_key: [u32; 4 * (AES_MAXNR as usize + 1)], + rounds: c_int, +} + #[repr(C)] pub struct GENERAL_NAME { pub type_: c_int, @@ -95,6 +122,12 @@ pub type PasswordCallback = unsafe extern fn(buf: *mut c_char, size: c_int, rwflag: c_int, user_data: *mut c_void) -> c_int; +pub const AES_ENCRYPT: c_int = 1; +pub const AES_DECRYPT: c_int = 0; + +pub const AES_MAXNR: c_int = 14; +pub const AES_BLOCK_SIZE: c_int = 16; + pub const BIO_TYPE_NONE: c_int = 0; pub const BIO_CTRL_EOF: c_int = 2; @@ -110,10 +143,25 @@ pub const BIO_FLAGS_SHOULD_RETRY: c_int = 0x08; pub const CRYPTO_LOCK: c_int = 1; +pub const ERR_LIB_PEM: c_int = 9; +pub const PEM_R_NO_START_LINE: c_int = 108; + pub const EVP_MAX_MD_SIZE: c_uint = 64; pub const EVP_PKEY_RSA: c_int = NID_rsaEncryption; pub const EVP_PKEY_HMAC: c_int = NID_hmac; pub const EVP_PKEY_DSA: c_int = NID_dsa; +pub const EVP_PKEY_DH: c_int = NID_dhKeyAgreement; +pub const EVP_PKEY_EC: c_int = NID_X9_62_id_ecPublicKey; + +pub const EVP_PKEY_ALG_CTRL: c_int = 0x1000; + +pub const EVP_PKEY_CTRL_RSA_PADDING: c_int = EVP_PKEY_ALG_CTRL + 1; + +pub const EVP_PKEY_CTRL_GET_RSA_PADDING: c_int = EVP_PKEY_ALG_CTRL + 6; + +pub const EVP_CTRL_GCM_SET_IVLEN: c_int = 0x9; +pub const EVP_CTRL_GCM_GET_TAG: c_int = 0x10; +pub const EVP_CTRL_GCM_SET_TAG: c_int = 0x11; pub const MBSTRING_ASC: c_int = MBSTRING_FLAG | 1; pub const MBSTRING_BMP: c_int = MBSTRING_FLAG | 2; @@ -1034,7 +1082,44 @@ pub const NID_aes_128_cbc_hmac_sha1: c_int = 916; pub const NID_aes_192_cbc_hmac_sha1: c_int = 917; pub const NID_aes_256_cbc_hmac_sha1: c_int = 918; +pub const OCSP_NOCERTS: c_ulong = 0x1; +pub const OCSP_NOINTERN: c_ulong = 0x2; +pub const OCSP_NOSIGS: c_ulong = 0x4; +pub const OCSP_NOCHAIN: c_ulong = 0x8; +pub const OCSP_NOVERIFY: c_ulong = 0x10; +pub const OCSP_NOEXPLICIT: c_ulong = 0x20; +pub const OCSP_NOCASIGN: c_ulong = 0x40; +pub const OCSP_NODELEGATED: c_ulong = 0x80; +pub const OCSP_NOCHECKS: c_ulong = 0x100; +pub const OCSP_TRUSTOTHER: c_ulong = 0x200; +pub const OCSP_RESPID_KEY: c_ulong = 0x400; +pub const OCSP_NOTIME: c_ulong = 0x800; + +pub const V_OCSP_CERTSTATUS_GOOD: c_int = 0; +pub const V_OCSP_CERTSTATUS_REVOKED: c_int = 1; +pub const V_OCSP_CERTSTATUS_UNKNOWN: c_int = 2; + +pub const OCSP_REVOKED_STATUS_NOSTATUS: c_int = -1; +pub const OCSP_REVOKED_STATUS_UNSPECIFIED: c_int = 0; +pub const OCSP_REVOKED_STATUS_KEYCOMPROMISE: c_int = 1; +pub const OCSP_REVOKED_STATUS_CACOMPROMISE: c_int = 2; +pub const OCSP_REVOKED_STATUS_AFFILIATIONCHANGED: c_int = 3; +pub const OCSP_REVOKED_STATUS_SUPERSEDED: c_int = 4; +pub const OCSP_REVOKED_STATUS_CESSATIONOFOPERATION: c_int = 5; +pub const OCSP_REVOKED_STATUS_CERTIFICATEHOLD: c_int = 6; +pub const OCSP_REVOKED_STATUS_REMOVEFROMCRL: c_int = 8; + +pub const OCSP_RESPONSE_STATUS_SUCCESSFUL: c_int = 0; +pub const OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: c_int = 1; +pub const OCSP_RESPONSE_STATUS_INTERNALERROR: c_int = 2; +pub const OCSP_RESPONSE_STATUS_TRYLATER: c_int = 3; +pub const OCSP_RESPONSE_STATUS_SIGREQUIRED: c_int = 5; +pub const OCSP_RESPONSE_STATUS_UNAUTHORIZED: c_int = 6; + +pub const OPENSSL_EC_NAMED_CURVE: c_int = 1; + pub const PKCS5_SALT_LEN: c_int = 8; +pub const PKCS12_DEFAULT_ITER: c_int = 2048; pub const RSA_F4: c_long = 0x10001; @@ -1052,14 +1137,23 @@ pub const SSL_CTRL_SET_READ_AHEAD: c_int = 41; pub const SSL_CTRL_SET_TLSEXT_SERVERNAME_CB: c_int = 53; pub const SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG: c_int = 54; pub const SSL_CTRL_SET_TLSEXT_HOSTNAME: c_int = 55; +pub const SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB: c_int = 63; +pub const SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG: c_int = 64; +pub const SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE: c_int = 65; +pub const SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP: c_int = 70; +pub const SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP: c_int = 71; +pub const SSL_CTRL_GET_EXTRA_CHAIN_CERTS: c_int = 82; pub const SSL_MODE_ENABLE_PARTIAL_WRITE: c_long = 0x1; pub const SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER: c_long = 0x2; pub const SSL_MODE_AUTO_RETRY: c_long = 0x4; pub const SSL_MODE_NO_AUTO_CHAIN: c_long = 0x8; pub const SSL_MODE_RELEASE_BUFFERS: c_long = 0x10; +#[cfg(not(libressl))] pub const SSL_MODE_SEND_CLIENTHELLO_TIME: c_long = 0x20; +#[cfg(not(libressl))] pub const SSL_MODE_SEND_SERVERHELLO_TIME: c_long = 0x40; +#[cfg(not(libressl))] pub const SSL_MODE_SEND_FALLBACK_SCSV: c_long = 0x80; pub const SSL_ERROR_NONE: c_int = 0; @@ -1078,31 +1172,38 @@ pub const SSL_VERIFY_FAIL_IF_NO_PEER_CERT: c_int = 2; #[cfg(not(ossl101))] pub const SSL_OP_TLSEXT_PADDING: c_ulong = 0x00000010; pub const SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: c_ulong = 0x00000800; +#[cfg(not(libressl))] pub const SSL_OP_ALL: c_ulong = 0x80000BFF; pub const SSL_OP_NO_QUERY_MTU: c_ulong = 0x00001000; pub const SSL_OP_COOKIE_EXCHANGE: c_ulong = 0x00002000; pub const SSL_OP_NO_TICKET: c_ulong = 0x00004000; +#[cfg(not(libressl))] pub const SSL_OP_CISCO_ANYCONNECT: c_ulong = 0x00008000; pub const SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: c_ulong = 0x00010000; +#[cfg(not(libressl))] pub const SSL_OP_NO_COMPRESSION: c_ulong = 0x00020000; +#[cfg(not(libressl))] pub const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: c_ulong = 0x00040000; pub const SSL_OP_CIPHER_SERVER_PREFERENCE: c_ulong = 0x00400000; pub const SSL_OP_TLS_ROLLBACK_BUG: c_ulong = 0x00800000; +#[cfg(not(libressl))] pub const SSL_OP_NO_SSLv3: c_ulong = 0x02000000; pub const SSL_OP_NO_TLSv1: c_ulong = 0x04000000; pub const SSL_OP_NO_TLSv1_2: c_ulong = 0x08000000; pub const SSL_OP_NO_TLSv1_1: c_ulong = 0x10000000; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const SSL_OP_NO_DTLSv1: c_ulong = 0x04000000; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const SSL_OP_NO_DTLSv1_2: c_ulong = 0x08000000; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const SSL_OP_NO_SSL_MASK: c_ulong = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; pub const TLSEXT_NAMETYPE_host_name: c_int = 0; +pub const TLSEXT_STATUSTYPE_ocsp: c_int = 1; + pub const SSL_TLSEXT_ERR_OK: c_int = 0; pub const SSL_TLSEXT_ERR_ALERT_WARNING: c_int = 1; pub const SSL_TLSEXT_ERR_ALERT_FATAL: c_int = 2; @@ -1173,15 +1274,15 @@ pub const X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: c_int = 45; pub const X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: c_int = 53; pub const X509_V_OK: c_int = 0; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT: c_uint = 0x1; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_NO_WILDCARDS: c_uint = 0x2; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS: c_uint = 0x4; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS: c_uint = 0x8; -#[cfg(not(ossl101))] +#[cfg(not(any(ossl101, libressl)))] pub const X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS: c_uint = 0x10; pub const GEN_OTHERNAME: c_int = 0; @@ -1211,6 +1312,15 @@ pub unsafe fn BIO_set_retry_write(b: *mut BIO) { BIO_set_flags(b, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY) } +// EVP_PKEY_CTX_ctrl macros +pub unsafe fn EVP_PKEY_CTX_set_rsa_padding(ctx: *mut EVP_PKEY_CTX, pad: c_int) -> c_int { + EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_RSA_PADDING, pad, ptr::null_mut()) +} + +pub unsafe fn EVP_PKEY_CTX_get_rsa_padding(ctx: *mut EVP_PKEY_CTX, ppad: *mut c_int) -> c_int { + EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, -1, EVP_PKEY_CTRL_GET_RSA_PADDING, 0, ppad as *mut c_void) +} + pub unsafe fn SSL_CTX_set_mode(ctx: *mut SSL_CTX, op: c_long) -> c_long { SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, op, ptr::null_mut()) } @@ -1227,12 +1337,20 @@ pub unsafe fn SSL_CTX_set_tmp_ecdh(ctx: *mut SSL_CTX, key: *mut EC_KEY) -> c_lon SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_ECDH, 0, key as *mut c_void) } +pub unsafe fn SSL_set_tmp_dh(ssl: *mut SSL, dh: *mut DH) -> c_long { + SSL_ctrl(ssl, SSL_CTRL_SET_TMP_DH, 0, dh as *mut c_void) +} + +pub unsafe fn SSL_set_tmp_ecdh(ssl: *mut SSL, key: *mut EC_KEY) -> c_long { + SSL_ctrl(ssl, SSL_CTRL_SET_TMP_ECDH, 0, key as *mut c_void) +} + pub unsafe fn SSL_CTX_add_extra_chain_cert(ctx: *mut SSL_CTX, x509: *mut X509) -> c_long { SSL_CTX_ctrl(ctx, SSL_CTRL_EXTRA_CHAIN_CERT, 0, x509 as *mut c_void) } pub unsafe fn SSL_CTX_set_tlsext_servername_callback(ctx: *mut SSL_CTX, - cb: Option) + cb: Option) -> c_long { SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_SERVERNAME_CB, cb) } @@ -1243,6 +1361,32 @@ pub unsafe fn SSL_set_tlsext_host_name(s: *mut SSL, name: *mut c_char) -> c_long name as *mut c_void) } +pub unsafe fn SSL_set_tlsext_status_type(s: *mut SSL, type_: c_int) -> c_long { + SSL_ctrl(s, SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE, type_ as c_long, ptr::null_mut()) +} + +pub unsafe fn SSL_CTX_set_tlsext_status_cb(ctx: *mut SSL_CTX, + cb: Option c_int>) + -> c_long { + SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB, mem::transmute(cb)) +} + +pub unsafe fn SSL_CTX_set_tlsext_status_arg(ctx: *mut SSL_CTX, arg: *mut c_void) -> c_long { + SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG, 0, arg) +} + +pub unsafe fn SSL_CTX_get_extra_chain_certs(ctx: *mut SSL_CTX, chain: *mut *mut stack_st_X509) -> c_long { + SSL_CTX_ctrl(ctx, SSL_CTRL_GET_EXTRA_CHAIN_CERTS, 0, chain as *mut c_void) +} + +pub unsafe fn SSL_get_tlsext_status_ocsp_resp(ssl: *mut SSL, resp: *mut *mut c_uchar) -> c_long { + SSL_ctrl(ssl, SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP, 0, resp as *mut c_void) +} + +pub unsafe fn SSL_set_tlsext_status_ocsp_resp(ssl: *mut SSL, resp: *mut c_uchar, len: c_long) -> c_long { + SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP, len, resp as *mut c_void) +} + pub fn ERR_GET_LIB(l: c_ulong) -> c_int { ((l >> 24) & 0x0FF) as c_int } @@ -1256,11 +1400,19 @@ pub fn ERR_GET_REASON(l: c_ulong) -> c_int { } extern { + pub fn AES_set_encrypt_key(userKey: *const c_uchar, bits: c_int, key: *mut AES_KEY) -> c_int; + pub fn AES_set_decrypt_key(userKey: *const c_uchar, bits: c_int, key: *mut AES_KEY) -> c_int; + pub fn AES_ige_encrypt(in_: *const c_uchar, out: *mut c_uchar, length: size_t, key: *const AES_KEY, ivec: *mut c_uchar, enc: c_int); + + pub fn ASN1_INTEGER_get(dest: *const ASN1_INTEGER) -> c_long; pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int; + pub fn ASN1_GENERALIZEDTIME_free(tm: *mut ASN1_GENERALIZEDTIME); + pub fn ASN1_GENERALIZEDTIME_print(b: *mut BIO, tm: *const ASN1_GENERALIZEDTIME) -> c_int; pub fn ASN1_STRING_type_new(ty: c_int) -> *mut ASN1_STRING; pub fn ASN1_TIME_free(tm: *mut ASN1_TIME); pub fn ASN1_TIME_print(b: *mut BIO, tm: *const ASN1_TIME) -> c_int; - pub fn ASN1_INTEGER_free(x: *mut ASN1_INTEGER); + pub fn ASN1_BIT_STRING_free(x: *mut ASN1_BIT_STRING); + pub fn ASN1_OBJECT_free(x: *mut ASN1_OBJECT); pub fn BIO_ctrl(b: *mut BIO, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long; pub fn BIO_free_all(b: *mut BIO); @@ -1268,9 +1420,9 @@ extern { pub fn BIO_new_socket(sock: c_int, close_flag: c_int) -> *mut BIO; pub fn BIO_read(b: *mut BIO, buf: *mut c_void, len: c_int) -> c_int; pub fn BIO_write(b: *mut BIO, buf: *const c_void, len: c_int) -> c_int; - #[cfg(ossl101)] + #[cfg(any(ossl101, libressl))] pub fn BIO_new_mem_buf(buf: *mut c_void, len: c_int) -> *mut BIO; - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn BIO_new_mem_buf(buf: *const c_void, len: c_int) -> *mut BIO; pub fn BIO_set_flags(b: *mut BIO, flags: c_int); pub fn BIO_clear_flags(b: *mut BIO, flags: c_int); @@ -1339,20 +1491,54 @@ extern { pub fn DH_new() -> *mut DH; pub fn DH_free(dh: *mut DH); - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn DH_get_1024_160() -> *mut DH; - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn DH_get_2048_224() -> *mut DH; - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn DH_get_2048_256() -> *mut DH; + pub fn EC_KEY_new() -> *mut EC_KEY; pub fn EC_KEY_new_by_curve_name(nid: c_int) -> *mut EC_KEY; + pub fn EC_KEY_set_group(key: *mut EC_KEY, group: *const EC_GROUP) -> c_int; + pub fn EC_KEY_get0_group(key: *const EC_KEY) -> *const EC_GROUP; + pub fn EC_KEY_set_public_key(key: *mut EC_KEY, key: *const EC_POINT) -> c_int; + pub fn EC_KEY_get0_public_key(key: *const EC_KEY) -> *const EC_POINT; + pub fn EC_KEY_set_private_key(key: *mut EC_KEY, key: *const BIGNUM) -> c_int; + pub fn EC_KEY_get0_private_key(key: *const EC_KEY) -> *const BIGNUM; + pub fn EC_KEY_generate_key(key: *mut EC_KEY) -> c_int; + pub fn EC_KEY_check_key(key: *const EC_KEY) -> c_int; pub fn EC_KEY_free(key: *mut EC_KEY); + pub fn EC_GF2m_simple_method() -> *const EC_METHOD; + + pub fn EC_GROUP_new(meth: *const EC_METHOD) -> *mut EC_GROUP; + pub fn EC_GROUP_new_curve_GFp(p: *const BIGNUM, a: *const BIGNUM, b: *const BIGNUM, ctx: *mut BN_CTX) -> *mut EC_GROUP; + pub fn EC_GROUP_new_curve_GF2m(p: *const BIGNUM, a: *const BIGNUM, b: *const BIGNUM, ctx: *mut BN_CTX) -> *mut EC_GROUP; + pub fn EC_GROUP_new_by_curve_name(nid: c_int) -> *mut EC_GROUP; + pub fn EC_GROUP_get_curve_GFp(group: *const EC_GROUP, p: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int; + pub fn EC_GROUP_get_curve_GF2m(group: *const EC_GROUP, p: *mut BIGNUM, a: *mut BIGNUM, b: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int; + pub fn EC_GROUP_get_degree(group: *const EC_GROUP) -> c_int; + pub fn EC_GROUP_get_order(group: *const EC_GROUP, order: *mut BIGNUM, ctx: *mut BN_CTX) -> c_int; + pub fn EC_GROUP_set_asn1_flag(key: *mut EC_GROUP, flag: c_int); + + pub fn EC_GROUP_free(group: *mut EC_GROUP); + + pub fn EC_POINT_new(group: *const EC_GROUP) -> *mut EC_POINT; + pub fn EC_POINT_add(group: *const EC_GROUP, r: *mut EC_POINT, a: *const EC_POINT, b: *const EC_POINT, ctx: *mut BN_CTX) -> c_int; + pub fn EC_POINT_mul(group: *const EC_GROUP, r: *mut EC_POINT, n: *const BIGNUM, q: *const EC_POINT, m: *const BIGNUM, ctx: *mut BN_CTX) -> c_int; + pub fn EC_POINT_invert(group: *const EC_GROUP, r: *mut EC_POINT, ctx: *mut BN_CTX) -> c_int; + pub fn EC_POINT_point2oct(group: *const EC_GROUP, p: *const EC_POINT, form: point_conversion_form_t, buf: *mut c_uchar, len: size_t, ctx: *mut BN_CTX) -> size_t; + pub fn EC_POINT_oct2point(group: *const EC_GROUP, p: *mut EC_POINT, buf: *const c_uchar, len: size_t, ctx: *mut BN_CTX) -> c_int; + pub fn EC_POINT_cmp(group: *const EC_GROUP, a: *const EC_POINT, b: *const EC_POINT, ctx: *mut BN_CTX) -> c_int; + pub fn EC_POINT_free(point: *mut EC_POINT); + + pub fn ERR_peek_last_error() -> c_ulong; pub fn ERR_get_error() -> c_ulong; pub fn ERR_lib_error_string(err: c_ulong) -> *const c_char; pub fn ERR_func_error_string(err: c_ulong) -> *const c_char; pub fn ERR_reason_error_string(err: c_ulong) -> *const c_char; + pub fn ERR_clear_error(); pub fn EVP_md5() -> *const EVP_MD; pub fn EVP_ripemd160() -> *const EVP_MD; @@ -1378,6 +1564,10 @@ extern { pub fn EVP_aes_256_cfb1() -> *const EVP_CIPHER; pub fn EVP_aes_256_cfb128() -> *const EVP_CIPHER; pub fn EVP_aes_256_cfb8() -> *const EVP_CIPHER; + pub fn EVP_bf_cbc() -> *const EVP_CIPHER; + pub fn EVP_bf_ecb() -> *const EVP_CIPHER; + pub fn EVP_bf_cfb64() -> *const EVP_CIPHER; + pub fn EVP_bf_ofb() -> *const EVP_CIPHER; pub fn EVP_rc4() -> *const EVP_CIPHER; pub fn EVP_des_cbc() -> *const EVP_CIPHER; @@ -1390,6 +1580,7 @@ extern { pub fn EVP_CIPHER_CTX_new() -> *mut EVP_CIPHER_CTX; pub fn EVP_CIPHER_CTX_set_padding(ctx: *mut EVP_CIPHER_CTX, padding: c_int) -> c_int; pub fn EVP_CIPHER_CTX_set_key_length(ctx: *mut EVP_CIPHER_CTX, keylen: c_int) -> c_int; + pub fn EVP_CIPHER_CTX_ctrl(ctx: *mut EVP_CIPHER_CTX, type_: c_int, arg: c_int, ptr: *mut c_void) -> c_int; pub fn EVP_CIPHER_CTX_free(ctx: *mut EVP_CIPHER_CTX); pub fn EVP_CipherInit(ctx: *mut EVP_CIPHER_CTX, evp: *const EVP_CIPHER, @@ -1423,11 +1614,11 @@ extern { type_: *const EVP_MD, e: *mut ENGINE, pkey: *mut EVP_PKEY) -> c_int; - #[cfg(ossl101)] + #[cfg(any(ossl101, libressl))] pub fn EVP_DigestVerifyFinal(ctx: *mut EVP_MD_CTX, sigret: *mut c_uchar, siglen: size_t) -> c_int; - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn EVP_DigestVerifyFinal(ctx: *mut EVP_MD_CTX, sigret: *const c_uchar, siglen: size_t) -> c_int; @@ -1440,14 +1631,47 @@ extern { pub fn EVP_PKEY_copy_parameters(to: *mut EVP_PKEY, from: *const EVP_PKEY) -> c_int; pub fn EVP_PKEY_get1_RSA(k: *mut EVP_PKEY) -> *mut RSA; pub fn EVP_PKEY_set1_RSA(k: *mut EVP_PKEY, r: *mut RSA) -> c_int; + pub fn EVP_PKEY_get1_DSA(k: *mut EVP_PKEY) -> *mut DSA; + pub fn EVP_PKEY_get1_DH(k: *mut EVP_PKEY) -> *mut DH; + pub fn EVP_PKEY_get1_EC_KEY(k: *mut EVP_PKEY) -> *mut EC_KEY; pub fn EVP_PKEY_cmp(a: *const EVP_PKEY, b: *const EVP_PKEY) -> c_int; pub fn EVP_PKEY_new_mac_key(type_: c_int, e: *mut ENGINE, key: *const c_uchar, keylen: c_int) -> *mut EVP_PKEY; + + pub fn EVP_PKEY_CTX_ctrl(ctx: *mut EVP_PKEY_CTX, keytype: c_int, optype: c_int, cmd: c_int, p1: c_int, p2: *mut c_void) -> c_int; + pub fn HMAC_CTX_copy(dst: *mut HMAC_CTX, src: *mut HMAC_CTX) -> c_int; + pub fn OBJ_obj2nid(o: *const ASN1_OBJECT) -> c_int; + pub fn OBJ_obj2txt(buf: *mut c_char, buf_len: c_int, a: *const ASN1_OBJECT, no_name: c_int) -> c_int; + + pub fn OCSP_BASICRESP_new() -> *mut OCSP_BASICRESP; + pub fn OCSP_BASICRESP_free(r: *mut OCSP_BASICRESP); + pub fn OCSP_basic_verify(bs: *mut OCSP_BASICRESP, certs: *mut stack_st_X509, st: *mut X509_STORE, flags: c_ulong) -> c_int; + pub fn OCSP_resp_find_status(bs: *mut OCSP_BASICRESP, id: *mut OCSP_CERTID, status: *mut c_int, reason: *mut c_int, revtime: *mut *mut ASN1_GENERALIZEDTIME, thisupd: *mut *mut ASN1_GENERALIZEDTIME, nextupd: *mut *mut ASN1_GENERALIZEDTIME) -> c_int; + pub fn OCSP_check_validity(thisupd: *mut ASN1_GENERALIZEDTIME, nextupd: *mut ASN1_GENERALIZEDTIME, sec: c_long, maxsec: c_long) -> c_int; + + pub fn OCSP_CERTID_free(id: *mut OCSP_CERTID); + + pub fn OCSP_RESPONSE_new() -> *mut OCSP_RESPONSE; + pub fn OCSP_RESPONSE_free(r: *mut OCSP_RESPONSE); + pub fn i2d_OCSP_RESPONSE(a: *mut OCSP_RESPONSE, pp: *mut *mut c_uchar) -> c_int; + pub fn d2i_OCSP_RESPONSE(a: *mut *mut OCSP_RESPONSE, pp: *mut *const c_uchar, length: c_long) -> *mut OCSP_RESPONSE; + pub fn OCSP_response_create(status: c_int, bs: *mut OCSP_BASICRESP) -> *mut OCSP_RESPONSE; + pub fn OCSP_response_status(resp: *mut OCSP_RESPONSE) -> c_int; + pub fn OCSP_response_get1_basic(resp: *mut OCSP_RESPONSE) -> *mut OCSP_BASICRESP; + + pub fn OCSP_REQUEST_new() -> *mut OCSP_REQUEST; + pub fn OCSP_REQUEST_free(r: *mut OCSP_REQUEST); + pub fn i2d_OCSP_REQUEST(a: *mut OCSP_REQUEST, pp: *mut *mut c_uchar) -> c_int; + pub fn d2i_OCSP_REQUEST(a: *mut *mut OCSP_REQUEST, pp: *mut *const c_uchar, length: c_long) -> *mut OCSP_REQUEST; + pub fn OCSP_request_add0_id(r: *mut OCSP_REQUEST, id: *mut OCSP_CERTID) -> *mut OCSP_ONEREQ; + + pub fn OCSP_ONEREQ_free(r: *mut OCSP_ONEREQ); + pub fn PEM_read_bio_DHparams(bio: *mut BIO, out: *mut *mut DH, callback: Option, user_data: *mut c_void) -> *mut DH; pub fn PEM_read_bio_X509(bio: *mut BIO, out: *mut *mut X509, callback: Option, @@ -1467,6 +1691,10 @@ extern { kstr: *mut c_uchar, klen: c_int, callback: Option, user_data: *mut c_void) -> c_int; + pub fn PEM_write_bio_PKCS8PrivateKey(bio: *mut BIO, pkey: *mut EVP_PKEY, cipher: *const EVP_CIPHER, + kstr: *mut c_char, klen: c_int, + callback: Option, + user_data: *mut c_void) -> c_int; pub fn PEM_write_bio_PUBKEY(bp: *mut BIO, x: *mut EVP_PKEY) -> c_int; pub fn PEM_write_bio_RSAPrivateKey(bp: *mut BIO, rsa: *mut RSA, cipher: *const EVP_CIPHER, kstr: *mut c_uchar, klen: c_int, @@ -1484,11 +1712,19 @@ extern { user_data: *mut c_void) -> c_int; pub fn PEM_write_bio_DSA_PUBKEY(bp: *mut BIO, dsa: *mut DSA) -> c_int; - - pub fn PEM_write_bio_X509(bio: *mut BIO, x509: *mut X509) -> c_int; pub fn PEM_write_bio_X509_REQ(bio: *mut BIO, x509: *mut X509_REQ) -> c_int; + pub fn PEM_write_bio_ECPrivateKey(bio: *mut BIO, + key: *mut EC_KEY, + cipher: *const EVP_CIPHER, + kstr: *mut c_uchar, + klen: c_int, + callback: Option, + user_data: *mut c_void) + -> c_int; + pub fn PEM_read_bio_ECPrivateKey(bio: *mut BIO, key: *mut *mut EC_KEY, callback: Option, user_data: *mut c_void) -> *mut EC_KEY; + pub fn PKCS5_PBKDF2_HMAC_SHA1(pass: *const c_char, passlen: c_int, salt: *const u8, saltlen: c_int, iter: c_int, keylen: c_int, @@ -1547,8 +1783,10 @@ extern { pub fn SSL_get_ex_data_X509_STORE_CTX_idx() -> c_int; pub fn SSL_get_SSL_CTX(ssl: *const SSL) -> *mut SSL_CTX; pub fn SSL_set_SSL_CTX(ssl: *mut SSL, ctx: *mut SSL_CTX) -> *mut SSL_CTX; - #[cfg(not(osslconf = "OPENSSL_NO_COMP"))] + #[cfg(not(any(osslconf = "OPENSSL_NO_COMP", libressl)))] pub fn SSL_get_current_compression(ssl: *mut SSL) -> *const COMP_METHOD; + #[cfg(libressl)] + pub fn SSL_get_current_compression(ssl: *mut SSL) -> *const libc::c_void; pub fn SSL_get_peer_certificate(ssl: *const SSL) -> *mut X509; pub fn SSL_get_ssl_method(ssl: *mut SSL) -> *const SSL_METHOD; pub fn SSL_get_version(ssl: *const SSL) -> *const c_char; @@ -1561,19 +1799,26 @@ extern { pub fn SSL_get_ex_data(ssl: *const SSL, idx: c_int) -> *mut c_void; pub fn SSL_get_servername(ssl: *const SSL, name_type: c_int) -> *const c_char; pub fn SSL_get_current_cipher(ssl: *const SSL) -> *const SSL_CIPHER; - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn SSL_get0_param(ssl: *mut SSL) -> *mut X509_VERIFY_PARAM; pub fn SSL_get_verify_result(ssl: *const SSL) -> c_long; pub fn SSL_shutdown(ssl: *mut SSL) -> c_int; pub fn SSL_get_certificate(ssl: *const SSL) -> *mut X509; - #[cfg(ossl101)] + #[cfg(any(ossl101, libressl))] pub fn SSL_get_privatekey(ssl: *mut SSL) -> *mut EVP_PKEY; - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn SSL_get_privatekey(ssl: *const SSL) -> *mut EVP_PKEY; pub fn SSL_load_client_CA_file(file: *const c_char) -> *mut stack_st_X509_NAME; + pub fn SSL_set_tmp_dh_callback(ctx: *mut SSL, + dh: unsafe extern fn(ssl: *mut SSL, + is_export: c_int, + keylength: c_int) + -> *mut DH); - #[cfg(not(osslconf = "OPENSSL_NO_COMP"))] + #[cfg(not(any(osslconf = "OPENSSL_NO_COMP", libressl)))] pub fn SSL_COMP_get_name(comp: *const COMP_METHOD) -> *const c_char; + #[cfg(libressl)] + pub fn SSL_COMP_get_name(comp: *const libc::c_void) -> *const c_char; pub fn SSL_CIPHER_get_name(cipher: *const SSL_CIPHER) -> *const c_char; pub fn SSL_CIPHER_get_bits(cipher: *const SSL_CIPHER, alg_bits: *mut c_int) -> c_int; @@ -1582,7 +1827,7 @@ extern { pub fn SSL_CTX_new(method: *const SSL_METHOD) -> *mut SSL_CTX; pub fn SSL_CTX_free(ctx: *mut SSL_CTX); pub fn SSL_CTX_ctrl(ctx: *mut SSL_CTX, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long; - pub fn SSL_CTX_callback_ctrl(ctx: *mut SSL_CTX, cmd: c_int, fp: Option) -> c_long; + pub fn SSL_CTX_callback_ctrl(ctx: *mut SSL_CTX, cmd: c_int, fp: Option) -> c_long; pub fn SSL_CTX_set_verify(ctx: *mut SSL_CTX, mode: c_int, verify_callback: Option c_int>); pub fn SSL_CTX_set_verify_depth(ctx: *mut SSL_CTX, depth: c_int); @@ -1602,10 +1847,16 @@ extern { pub fn SSL_CTX_use_PrivateKey(ctx: *mut SSL_CTX, key: *mut EVP_PKEY) -> c_int; pub fn SSL_CTX_check_private_key(ctx: *const SSL_CTX) -> c_int; pub fn SSL_CTX_set_client_CA_list(ctx: *mut SSL_CTX, list: *mut stack_st_X509_NAME); - - #[cfg(not(ossl101))] + pub fn SSL_CTX_get_cert_store(ctx: *const SSL_CTX) -> *mut X509_STORE; + pub fn SSL_CTX_set_tmp_dh_callback(ctx: *mut SSL_CTX, + dh: unsafe extern fn(ssl: *mut SSL, + is_export: c_int, + keylength: c_int) + -> *mut DH); + + #[cfg(not(any(ossl101, libressl)))] pub fn SSL_CTX_get0_certificate(ctx: *const SSL_CTX) -> *mut X509; - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn SSL_CTX_get0_privatekey(ctx: *const SSL_CTX) -> *mut EVP_PKEY; pub fn SSL_CTX_set_cipher_list(ssl: *mut SSL_CTX, s: *const c_char) -> c_int; @@ -1628,6 +1879,12 @@ extern { inbuf: *const c_uchar, inlen: c_uint, client: *const c_uchar, client_len: c_uint) -> c_int; pub fn SSL_get0_next_proto_negotiated(s: *const SSL, data: *mut *const c_uchar, len: *mut c_uint); + pub fn SSL_get_session(s: *const SSL) -> *mut SSL_SESSION; + #[cfg(not(any(ossl101, libressl)))] + pub fn SSL_is_server(s: *mut SSL) -> c_int; + + pub fn SSL_SESSION_free(s: *mut SSL_SESSION); + pub fn SSL_SESSION_get_id(s: *const SSL_SESSION, len: *mut c_uint) -> *const c_uchar; #[cfg(not(ossl101))] pub fn SSL_CTX_set_alpn_protos(s: *mut SSL_CTX, data: *const c_uchar, len: c_uint) -> c_int; @@ -1663,6 +1920,10 @@ extern { pub fn X509_get_pubkey(x: *mut X509) -> *mut EVP_PKEY; pub fn X509_to_X509_REQ(x: *mut X509, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> *mut X509_REQ; pub fn X509_verify_cert_error_string(n: c_long) -> *const c_char; + pub fn X509_get1_ocsp(x: *mut X509) -> *mut stack_st_OPENSSL_STRING; + pub fn X509_check_issued(issuer: *mut X509, subject: *mut X509) -> c_int; + + pub fn X509_ALGOR_free(x: *mut X509_ALGOR); pub fn X509_EXTENSION_free(ext: *mut X509_EXTENSION); @@ -1676,6 +1937,13 @@ extern { pub fn ASN1_STRING_free(x: *mut ASN1_STRING); pub fn ASN1_STRING_length(x: *const ASN1_STRING) -> c_int; + pub fn ASN1_INTEGER_free(x: *mut ASN1_INTEGER); + + pub fn X509_STORE_new() -> *mut X509_STORE; + pub fn X509_STORE_free(store: *mut X509_STORE); + pub fn X509_STORE_add_cert(store: *mut X509_STORE, x: *mut X509) -> c_int; + pub fn X509_STORE_set_default_paths(store: *mut X509_STORE) -> c_int; + pub fn X509_STORE_CTX_free(ctx: *mut X509_STORE_CTX); pub fn X509_STORE_CTX_get_current_cert(ctx: *mut X509_STORE_CTX) -> *mut X509; pub fn X509_STORE_CTX_get_error(ctx: *mut X509_STORE_CTX) -> c_int; @@ -1694,24 +1962,43 @@ extern { #[cfg(not(ossl101))] pub fn X509_VERIFY_PARAM_free(param: *mut X509_VERIFY_PARAM); - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn X509_VERIFY_PARAM_set_hostflags(param: *mut X509_VERIFY_PARAM, flags: c_uint); - #[cfg(not(ossl101))] + #[cfg(not(any(ossl101, libressl)))] pub fn X509_VERIFY_PARAM_set1_host(param: *mut X509_VERIFY_PARAM, name: *const c_char, namelen: size_t) -> c_int; + pub fn d2i_DHparams(k: *mut *mut DH, pp: *mut *const c_uchar, length: c_long) -> *mut DH; + pub fn i2d_DHparams(dh: *const DH, pp: *mut *mut c_uchar) -> c_int; + + pub fn d2i_DSAPublicKey(a: *mut *mut DSA, pp: *mut *const c_uchar, length: c_long) -> *mut DSA; + pub fn i2d_DSAPublicKey(a: *const DSA, pp: *mut *mut c_uchar) -> c_int; + pub fn d2i_DSAPrivateKey(a: *mut *mut DSA, pp: *mut *const c_uchar, length: c_long) -> *mut DSA; + pub fn i2d_DSAPrivateKey(a: *const DSA, pp: *mut *mut c_uchar) -> c_int; + + pub fn d2i_ECPrivateKey(k: *mut *mut EC_KEY, pp: *mut *const c_uchar, length: c_long) -> *mut EC_KEY; + pub fn i2d_ECPrivateKey(ec_key: *mut EC_KEY, pp: *mut *mut c_uchar) -> c_int; + pub fn d2i_X509(a: *mut *mut X509, pp: *mut *const c_uchar, length: c_long) -> *mut X509; + pub fn d2i_X509_REQ(a: *mut *mut X509_REQ, pp: *mut *const c_uchar, length: c_long) -> *mut X509_REQ; pub fn i2d_X509_bio(b: *mut BIO, x: *mut X509) -> c_int; + pub fn i2d_X509(x: *mut X509, buf: *mut *mut u8) -> c_int; pub fn i2d_X509_REQ_bio(b: *mut BIO, x: *mut X509_REQ) -> c_int; + pub fn i2d_X509_REQ(x: *mut X509_REQ, buf: *mut *mut u8) -> c_int; pub fn i2d_PUBKEY_bio(b: *mut BIO, x: *mut EVP_PKEY) -> c_int; + pub fn i2d_PrivateKey_bio(b: *mut BIO, x: *mut EVP_PKEY) -> c_int; + pub fn i2d_PUBKEY(k: *mut EVP_PKEY, buf: *mut *mut u8) -> c_int; + pub fn i2d_PrivateKey(k: *mut EVP_PKEY, buf: *mut *mut u8) -> c_int; pub fn i2d_RSA_PUBKEY(k: *mut RSA, buf: *mut *mut u8) -> c_int; pub fn d2i_RSA_PUBKEY(k: *mut *mut RSA, buf: *mut *const u8, len: c_long) -> *mut RSA; pub fn i2d_RSAPrivateKey(k: *const RSA, buf: *mut *mut u8) -> c_int; pub fn d2i_RSAPrivateKey(k: *mut *mut RSA, buf: *mut *const u8, len: c_long) -> *mut RSA; + pub fn i2d_PKCS12_bio(b: *mut BIO, a: *mut PKCS12) -> c_int; + pub fn i2d_PKCS12(a: *mut PKCS12, buf: *mut *mut u8) -> c_int; pub fn d2i_PKCS12(a: *mut *mut PKCS12, pp: *mut *const u8, length: c_long) -> *mut PKCS12; pub fn PKCS12_parse(p12: *mut PKCS12, pass: *const c_char, diff --git a/openssl-sys/src/libressl.rs b/openssl-sys/src/libressl.rs new file mode 100644 index 0000000000000000000000000000000000000000..197d2c226ceda0a149c4b2b3dea18ee381ab38b7 --- /dev/null +++ b/openssl-sys/src/libressl.rs @@ -0,0 +1,734 @@ +use std::sync::{Mutex, MutexGuard}; +use std::sync::{Once, ONCE_INIT}; +use std::mem; + +use libc::{c_int, c_char, c_void, c_long, c_uchar, size_t, c_uint, c_ulong}; +use libc::time_t; + +#[repr(C)] +pub struct stack_st_ASN1_OBJECT { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_X509 { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_X509_NAME { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_X509_ATTRIBUTE { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_X509_EXTENSION { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_GENERAL_NAME { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_void { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_SSL_CIPHER { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_OPENSSL_STRING { + pub stack: _STACK, +} + +#[repr(C)] +pub struct _STACK { + pub num: c_int, + pub data: *mut *mut c_char, + pub sorted: c_int, + pub num_alloc: c_int, + pub comp: Option c_int>, +} + +#[repr(C)] +pub struct BIO_METHOD { + pub type_: c_int, + pub name: *const c_char, + pub bwrite: Option c_int>, + pub bread: Option c_int>, + pub bputs: Option c_int>, + pub bgets: Option c_int>, + pub ctrl: Option c_long>, + pub create: Option c_int>, + pub destroy: Option c_int>, + pub callback_ctrl: Option c_long>, +} + +#[repr(C)] +pub struct RSA { + pub pad: c_int, + pub version: c_long, + pub meth: *const ::RSA_METHOD, + + pub engine: *mut ::ENGINE, + pub n: *mut ::BIGNUM, + pub e: *mut ::BIGNUM, + pub d: *mut ::BIGNUM, + pub p: *mut ::BIGNUM, + pub q: *mut ::BIGNUM, + pub dmp1: *mut ::BIGNUM, + pub dmq1: *mut ::BIGNUM, + pub iqmp: *mut ::BIGNUM, + + pub ex_data: ::CRYPTO_EX_DATA, + pub references: c_int, + pub flags: c_int, + + pub _method_mod_n: *mut ::BN_MONT_CTX, + pub _method_mod_p: *mut ::BN_MONT_CTX, + pub _method_mod_q: *mut ::BN_MONT_CTX, + + pub blinding: *mut ::BN_BLINDING, + pub mt_blinding: *mut ::BN_BLINDING, +} + +#[repr(C)] +pub struct DSA { + pub pad: c_int, + pub version: c_long, + pub write_params: c_int, + + pub p: *mut ::BIGNUM, + pub q: *mut ::BIGNUM, + pub g: *mut ::BIGNUM, + pub pub_key: *mut ::BIGNUM, + pub priv_key: *mut ::BIGNUM, + pub kinv: *mut ::BIGNUM, + pub r: *mut ::BIGNUM, + + pub flags: c_int, + pub method_mont_p: *mut ::BN_MONT_CTX, + pub references: c_int, + pub ex_data: ::CRYPTO_EX_DATA, + pub meth: *const ::DSA_METHOD, + pub engine: *mut ::ENGINE, +} + +#[repr(C)] +pub struct EVP_PKEY { + pub type_: c_int, + pub save_type: c_int, + pub references: c_int, + pub ameth: *const ::EVP_PKEY_ASN1_METHOD, + pub engine: *mut ::ENGINE, + pub pkey: *mut c_void, + pub save_parameters: c_int, + pub attributes: *mut stack_st_X509_ATTRIBUTE, +} + +#[repr(C)] +pub struct BIO { + pub method: *mut ::BIO_METHOD, + pub callback: Option c_long>, + pub cb_arg: *mut c_char, + pub init: c_int, + pub shutdown: c_int, + pub flags: c_int, + pub retry_reason: c_int, + pub num: c_int, + pub ptr: *mut c_void, + pub next_bio: *mut ::BIO, + pub prev_bio: *mut ::BIO, + pub references: c_int, + pub num_read: c_ulong, + pub num_write: c_ulong, + pub ex_data: ::CRYPTO_EX_DATA, +} + +#[repr(C)] +pub struct CRYPTO_EX_DATA { + pub sk: *mut ::stack_st_void, +} + +#[repr(C)] +pub struct EVP_MD_CTX { + digest: *mut ::EVP_MD, + engine: *mut ::ENGINE, + flags: c_ulong, + md_data: *mut c_void, + pctx: *mut ::EVP_PKEY_CTX, + update: *mut c_void +} + +#[repr(C)] +pub struct EVP_CIPHER { + pub nid: c_int, + pub block_size: c_int, + pub key_len: c_int, + pub iv_len: c_int, + pub flags: c_ulong, + pub init: Option c_int>, + pub do_cipher: Option c_int>, + pub cleanup: Option c_int>, + pub ctx_size: c_int, + pub set_asn1_parameters: Option c_int>, + pub get_asn1_parameters: Option c_int>, + pub ctrl: Option c_int>, + pub app_data: *mut c_void, +} + +#[repr(C)] +pub struct HMAC_CTX { + md: *mut ::EVP_MD, + md_ctx: ::EVP_MD_CTX, + i_ctx: ::EVP_MD_CTX, + o_ctx: ::EVP_MD_CTX, + key_length: c_uint, + key: [c_uchar; 128] +} + +#[repr(C)] +pub struct BIGNUM { + pub d: *mut ::BN_ULONG, + pub top: c_int, + pub dmax: c_int, + pub neg: c_int, + pub flags: c_int, +} + +#[repr(C)] +pub struct DH { + pub pad: c_int, + pub version: c_int, + pub p: *mut ::BIGNUM, + pub g: *mut ::BIGNUM, + pub length: c_long, + pub pub_key: *mut ::BIGNUM, + pub priv_key: *mut ::BIGNUM, + pub flags: c_int, + pub method_mont_p: *mut ::BN_MONT_CTX, + pub q: *mut ::BIGNUM, + pub j: *mut ::BIGNUM, + pub seed: *mut c_uchar, + pub seedlen: c_int, + pub counter: *mut ::BIGNUM, + pub references: c_int, + pub ex_data: ::CRYPTO_EX_DATA, + pub meth: *const ::DH_METHOD, + pub engine: *mut ::ENGINE, +} + +#[repr(C)] +pub struct X509 { + pub cert_info: *mut X509_CINF, + pub sig_alg: *mut ::X509_ALGOR, + pub signature: *mut ::ASN1_BIT_STRING, + pub valid: c_int, + pub references: c_int, + pub name: *mut c_char, + pub ex_data: ::CRYPTO_EX_DATA, + pub ex_pathlen: c_long, + pub ex_pcpathlen: c_long, + pub ex_flags: c_ulong, + pub ex_kusage: c_ulong, + pub ex_xkusage: c_ulong, + pub ex_nscert: c_ulong, + skid: *mut c_void, + akid: *mut c_void, + policy_cache: *mut c_void, + crldp: *mut c_void, + altname: *mut c_void, + nc: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_SHA"))] + sha1_hash: [c_uchar; 20], + aux: *mut c_void, +} + +#[repr(C)] +pub struct X509_CINF { + version: *mut c_void, + serialNumber: *mut c_void, + signature: *mut c_void, + issuer: *mut c_void, + pub validity: *mut X509_VAL, + subject: *mut c_void, + key: *mut c_void, + issuerUID: *mut c_void, + subjectUID: *mut c_void, + pub extensions: *mut stack_st_X509_EXTENSION, + enc: ASN1_ENCODING, +} + +#[repr(C)] +pub struct X509_ALGOR { + pub algorithm: *mut ::ASN1_OBJECT, + parameter: *mut c_void, +} + +#[repr(C)] +pub struct ASN1_ENCODING { + pub enc: *mut c_uchar, + pub len: c_long, + pub modified: c_int, +} + +#[repr(C)] +pub struct X509_VAL { + pub notBefore: *mut ::ASN1_TIME, + pub notAfter: *mut ::ASN1_TIME, +} + +#[repr(C)] +pub struct X509_REQ_INFO { + pub enc: ASN1_ENCODING, + pub version: *mut ::ASN1_INTEGER, + pub subject: *mut ::X509_NAME, + pubkey: *mut c_void, + pub attributes: *mut stack_st_X509_ATTRIBUTE +} + +#[repr(C)] +pub struct X509_REQ { + pub req_info: *mut X509_REQ_INFO, + sig_alg: *mut c_void, + signature: *mut c_void, + references: c_int +} + +#[repr(C)] +pub struct SSL { + version: c_int, + type_: c_int, + method: *const ::SSL_METHOD, + rbio: *mut c_void, + wbio: *mut c_void, + bbio: *mut c_void, + rwstate: c_int, + in_handshake: c_int, + handshake_func: Option c_int>, + pub server: c_int, + new_session: c_int, + quiet_shutdown: c_int, + shutdown: c_int, + state: c_int, + rstate: c_int, + init_buf: *mut c_void, + init_msg: *mut c_void, + init_num: c_int, + init_off: c_int, + packet: *mut c_uchar, + packet_length: c_uint, + s3: *mut c_void, + d1: *mut c_void, + read_ahead: c_int, + msg_callback: Option, + msg_callback_arg: *mut c_void, + hit: c_int, + param: *mut c_void, + cipher_list: *mut stack_st_SSL_CIPHER, + cipher_list_by_id: *mut stack_st_SSL_CIPHER, + mac_flags: c_int, + aead_read_ctx: *mut c_void, + enc_read_ctx: *mut ::EVP_CIPHER_CTX, + read_hash: *mut ::EVP_MD_CTX, + aead_write_ctx: *mut c_void, + enc_write_ctx: *mut ::EVP_CIPHER_CTX, + write_hash: *mut ::EVP_MD_CTX, + cert: *mut c_void, + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], + session: *mut ::SSL_SESSION, + generate_session_id: ::GEN_SESSION_CB, + verify_mode: c_int, + verify_callback: Option c_int>, + info_callback: Option, + error: c_int, + error_code: c_int, + ctx: *mut ::SSL_CTX, + debug: c_int, + verify_result: c_long, + ex_data: ::CRYPTO_EX_DATA, + client_CA: *mut stack_st_X509_NAME, + references: c_int, + options: c_ulong, + mode: c_ulong, + max_cert_list: c_long, + first_packet: c_int, + client_version: c_int, + max_send_fragment: c_uint, + tlsext_debug_cb: Option, + tlsext_debug_arg: *mut c_void, + tlsext_hostname: *mut c_char, + servername_done: c_int, + tlsext_status_type: c_int, + tlsext_status_expected: c_int, + tlsext_ocsp_ids: *mut c_void, + tlsext_ocsp_exts: *mut c_void, + tlsext_ocsp_resp: *mut c_uchar, + tlsext_ocsp_resplen: c_int, + tlsext_ticket_expected: c_int, + tlsext_ecpointformatlist_length: size_t, + tlsext_ecpointformatlist: *mut c_uchar, + tlsext_ellipticcurvelist_length: size_t, + tlsext_ellipticcurvelist: *mut c_uchar, + tlsext_session_ticket: *mut c_void, + tlsext_session_ticket_ext_cb: ::tls_session_ticket_ext_cb_fn, + tls_session_ticket_ext_cb_arg: *mut c_void, + tls_session_secret_cb: ::tls_session_secret_cb_fn, + tls_session_secret_cb_arg: *mut c_void, + initial_ctx: *mut ::SSL_CTX, + next_proto_negotiated: *mut c_uchar, + next_proto_negotiated_len: c_uchar, + srtp_profiles: *mut c_void, + srtp_profile: *mut c_void, + tlsext_heartbeat: c_uint, + tlsext_hb_pending: c_uint, + tlsext_hb_seq: c_uint, + alpn_client_proto_list: *mut c_uchar, + alpn_client_proto_list_len: c_uint, + renegotiate: c_int, +} + +#[repr(C)] +pub struct SSL_CTX { + method: *mut c_void, + cipher_list: *mut c_void, + cipher_list_by_id: *mut c_void, + cert_store: *mut c_void, + sessions: *mut c_void, + session_cache_size: c_ulong, + session_cache_head: *mut c_void, + session_cache_tail: *mut c_void, + session_cache_mode: c_int, + session_timeout: c_long, + new_session_cb: *mut c_void, + remove_session_cb: *mut c_void, + get_session_cb: *mut c_void, + stats: [c_int; 11], + pub references: c_int, + app_verify_callback: *mut c_void, + app_verify_arg: *mut c_void, + default_passwd_callback: *mut c_void, + default_passwd_callback_userdata: *mut c_void, + client_cert_cb: *mut c_void, + app_gen_cookie_cb: *mut c_void, + app_verify_cookie_cb: *mut c_void, + ex_dat: ::CRYPTO_EX_DATA, + rsa_md5: *mut c_void, + md5: *mut c_void, + sha1: *mut c_void, + extra_certs: *mut c_void, + comp_methods: *mut c_void, + info_callback: *mut c_void, + client_CA: *mut c_void, + options: c_ulong, + mode: c_ulong, + max_cert_list: c_long, + cert: *mut c_void, + read_ahead: c_int, + msg_callback: *mut c_void, + msg_callback_arg: *mut c_void, + verify_mode: c_int, + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; 32], + default_verify_callback: *mut c_void, + generate_session_id: *mut c_void, + param: *mut c_void, + quiet_shutdown: c_int, + max_send_fragment: c_uint, + + #[cfg(not(osslconf = "OPENSSL_NO_ENGINE"))] + client_cert_engine: *mut c_void, + + tlsext_servername_callback: *mut c_void, + tlsect_servername_arg: *mut c_void, + tlsext_tick_key_name: [c_uchar; 16], + tlsext_tick_hmac_key: [c_uchar; 16], + tlsext_tick_aes_key: [c_uchar; 16], + tlsext_ticket_key_cb: *mut c_void, + tlsext_status_cb: *mut c_void, + tlsext_status_arg: *mut c_void, + tlsext_opaque_prf_input_callback: *mut c_void, + tlsext_opaque_prf_input_callback_arg: *mut c_void, + + next_protos_advertised_cb: *mut c_void, + next_protos_advertised_cb_arg: *mut c_void, + next_proto_select_cb: *mut c_void, + next_proto_select_cb_arg: *mut c_void, + + srtp_profiles: *mut c_void, +} + +#[repr(C)] +pub struct SSL_SESSION { + ssl_version: c_int, + pub master_key_length: c_int, + pub master_key: [c_uchar; 48], + session_id_length: c_uint, + session_id: [c_uchar; SSL_MAX_SSL_SESSION_ID_LENGTH as usize], + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; SSL_MAX_SID_CTX_LENGTH as usize], + not_resumable: c_int, + sess_cert: *mut c_void, + peer: *mut X509, + verify_result: c_long, + timeout: c_long, + time: time_t, + references: c_int, + cipher: *const c_void, + cipher_id: c_ulong, + ciphers: *mut c_void, + ex_data: ::CRYPTO_EX_DATA, + prev: *mut c_void, + next: *mut c_void, + tlsext_hostname: *mut c_char, + tlsext_ecpointformatlist_length: size_t, + tlsext_ecpointformatlist: *mut u8, + tlsext_ellipticcurvelist_length: size_t, + tlsext_ellipticcurvelist: *mut u16, + tlsext_tick: *mut c_uchar, + tlsext_ticklen: size_t, + tlsext_tick_lifetime_hint: c_long, +} + +#[repr(C)] +pub struct X509_VERIFY_PARAM { + pub name: *mut c_char, + pub check_time: time_t, + pub inh_flags: c_ulong, + pub flags: c_ulong, + pub purpose: c_int, + pub trust: c_int, + pub depth: c_int, + pub policies: *mut stack_st_ASN1_OBJECT, + //pub id: *mut X509_VERIFY_PARAM_ID, +} + +pub enum X509_VERIFY_PARAM_ID {} +pub enum PKCS12 {} + +pub const SSL_CTRL_OPTIONS: c_int = 32; +pub const SSL_CTRL_CLEAR_OPTIONS: c_int = 77; +pub const SSL_CTRL_SET_ECDH_AUTO: c_int = 94; + +pub const SSL_OP_ALL: c_ulong = 0x80000014; +pub const SSL_OP_CISCO_ANYCONNECT: c_ulong = 0x0; +pub const SSL_OP_NO_COMPRESSION: c_ulong = 0x0; +pub const SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: c_ulong = 0x0; +pub const SSL_OP_NO_SSLv3: c_ulong = 0x0; +pub const SSL_OP_MICROSOFT_SESS_ID_BUG: c_ulong = 0x0; +pub const SSL_OP_NETSCAPE_CHALLENGE_BUG: c_ulong = 0x0; +pub const SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: c_ulong = 0x0; +pub const SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: c_ulong = 0x0; +pub const SSL_OP_SSLEAY_080_CLIENT_DH_BUG: c_ulong = 0x0; +pub const SSL_OP_TLS_D5_BUG: c_ulong = 0x0; +pub const SSL_OP_TLS_BLOCK_PADDING_BUG: c_ulong = 0x0; +pub const SSL_OP_SINGLE_ECDH_USE: c_ulong = 0x00080000; +pub const SSL_OP_SINGLE_DH_USE: c_ulong = 0x00100000; +pub const SSL_OP_NO_SSLv2: c_ulong = 0x0; + +pub const SSL_MAX_SSL_SESSION_ID_LENGTH: c_int = 32; +pub const SSL_MAX_SID_CTX_LENGTH: c_int = 32; +pub const SSL_MAX_MASTER_KEY_LENGTH: c_int = 48; + +pub const SSLEAY_VERSION : c_int = 0; +pub const SSLEAY_CFLAGS : c_int = 2; +pub const SSLEAY_BUILT_ON : c_int = 3; +pub const SSLEAY_PLATFORM : c_int = 4; +pub const SSLEAY_DIR : c_int = 5; + +pub const CRYPTO_LOCK_X509: c_int = 3; +pub const CRYPTO_LOCK_SSL_CTX: c_int = 12; + +static mut MUTEXES: *mut Vec> = 0 as *mut Vec>; +static mut GUARDS: *mut Vec>> = 0 as *mut Vec>>; + +unsafe extern fn locking_function(mode: c_int, n: c_int, _file: *const c_char, + _line: c_int) { + let mutex = &(*MUTEXES)[n as usize]; + + if mode & ::CRYPTO_LOCK != 0 { + (*GUARDS)[n as usize] = Some(mutex.lock().unwrap()); + } else { + &(*GUARDS)[n as usize].take(); + } +} + +pub fn init() { + static INIT: Once = ONCE_INIT; + + INIT.call_once(|| { + unsafe { + SSL_library_init(); + SSL_load_error_strings(); + OPENSSL_add_all_algorithms_noconf(); + + let num_locks = ::CRYPTO_num_locks(); + let mut mutexes = Box::new(Vec::new()); + for _ in 0..num_locks { + mutexes.push(Mutex::new(())); + } + MUTEXES = mem::transmute(mutexes); + let guards: Box>>> = + Box::new((0..num_locks).map(|_| None).collect()); + GUARDS = mem::transmute(guards); + + CRYPTO_set_locking_callback(locking_function); + set_id_callback(); + } + }) +} + +#[cfg(unix)] +fn set_id_callback() { + unsafe extern fn thread_id() -> c_ulong { + ::libc::pthread_self() as c_ulong + } + + unsafe { + CRYPTO_set_id_callback(thread_id); + } +} + +#[cfg(not(unix))] +fn set_id_callback() {} + +// macros + +pub unsafe fn SSL_CTX_set_ecdh_auto(ctx: *mut SSL_CTX, onoff: c_int) -> c_int { + ::SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff as c_long, ::std::ptr::null_mut()) as c_int +} + +pub unsafe fn SSL_set_ecdh_auto(ssl: *mut ::SSL, onoff: c_int) -> c_int { + ::SSL_ctrl(ssl, SSL_CTRL_SET_ECDH_AUTO, onoff as c_long, ::std::ptr::null_mut()) as c_int +} + +extern { + pub fn BIO_new(type_: *mut BIO_METHOD) -> *mut BIO; + pub fn BIO_s_file() -> *mut BIO_METHOD; + pub fn BIO_s_mem() -> *mut BIO_METHOD; + + pub fn get_rfc2409_prime_768(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc2409_prime_1024(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_1536(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_2048(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_3072(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_4096(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_6144(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_8192(bn: *mut BIGNUM) -> *mut BIGNUM; + + pub fn CRYPTO_malloc(num: c_int, file: *const c_char, line: c_int) -> *mut c_void; + pub fn CRYPTO_free(buf: *mut c_void); + pub fn CRYPTO_num_locks() -> c_int; + pub fn CRYPTO_set_locking_callback(func: unsafe extern "C" fn(mode: c_int, + n: c_int, + file: *const c_char, + line: c_int)); + pub fn CRYPTO_set_id_callback(func: unsafe extern "C" fn() -> c_ulong); + + pub fn ERR_load_crypto_strings(); + + pub fn RSA_generate_key(modsz: c_int, + e: c_ulong, + cb: Option, + cbarg: *mut c_void) -> *mut RSA; + + pub fn OCSP_cert_to_id(dgst: *const ::EVP_MD, subject: *mut ::X509, issuer: *mut ::X509) -> *mut ::OCSP_CERTID; + + pub fn PKCS12_create(pass: *mut c_char, + friendly_name: *mut c_char, + pkey: *mut EVP_PKEY, + cert: *mut X509, + ca: *mut stack_st_X509, + nid_key: c_int, + nid_cert: c_int, + iter: c_int, + mac_iter: c_int, + keytype: c_int) -> *mut PKCS12; + + pub fn SSL_library_init() -> c_int; + pub fn SSL_load_error_strings(); + pub fn OPENSSL_add_all_algorithms_noconf(); + pub fn HMAC_CTX_init(ctx: *mut ::HMAC_CTX); + pub fn HMAC_CTX_cleanup(ctx: *mut ::HMAC_CTX); + pub fn TLSv1_method() -> *const ::SSL_METHOD; + pub fn SSLv23_method() -> *const ::SSL_METHOD; + pub fn TLSv1_1_method() -> *const ::SSL_METHOD; + pub fn TLSv1_2_method() -> *const ::SSL_METHOD; + pub fn DTLSv1_method() -> *const ::SSL_METHOD; + pub fn SSL_get_ex_new_index(argl: c_long, argp: *mut c_void, + new_func: Option<::CRYPTO_EX_new>, + dup_func: Option<::CRYPTO_EX_dup>, + free_func: Option<::CRYPTO_EX_free>) + -> c_int; + pub fn SSL_set_tmp_ecdh_callback(ssl: *mut ::SSL, + ecdh: unsafe extern fn(ssl: *mut ::SSL, + is_export: c_int, + keylength: c_int) + -> *mut ::EC_KEY); + pub fn SSL_CIPHER_get_version(cipher: *const ::SSL_CIPHER) -> *mut c_char; + pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *mut c_void, + new_func: Option<::CRYPTO_EX_new>, + dup_func: Option<::CRYPTO_EX_dup>, + free_func: Option<::CRYPTO_EX_free>) + -> c_int; + pub fn SSL_CTX_set_tmp_ecdh_callback(ctx: *mut ::SSL_CTX, + ecdh: unsafe extern fn(ssl: *mut ::SSL, + is_export: c_int, + keylength: c_int) + -> *mut ::EC_KEY); + pub fn X509_get_subject_name(x: *mut ::X509) -> *mut ::X509_NAME; + pub fn X509_set_notAfter(x: *mut ::X509, tm: *const ::ASN1_TIME) -> c_int; + pub fn X509_set_notBefore(x: *mut ::X509, tm: *const ::ASN1_TIME) -> c_int; + pub fn X509_get_ext_d2i(x: *mut ::X509, nid: c_int, crit: *mut c_int, idx: *mut c_int) -> *mut c_void; + pub fn X509_NAME_get_entry(n: *mut ::X509_NAME, loc: c_int) -> *mut ::X509_NAME_ENTRY; + pub fn X509_NAME_ENTRY_get_data(ne: *mut ::X509_NAME_ENTRY) -> *mut ::ASN1_STRING; + pub fn X509_STORE_CTX_get_chain(ctx: *mut ::X509_STORE_CTX) -> *mut stack_st_X509; + pub fn X509V3_EXT_nconf_nid(conf: *mut ::CONF, ctx: *mut ::X509V3_CTX, ext_nid: c_int, value: *mut c_char) -> *mut ::X509_EXTENSION; + pub fn X509V3_EXT_nconf(conf: *mut ::CONF, ctx: *mut ::X509V3_CTX, name: *mut c_char, value: *mut c_char) -> *mut ::X509_EXTENSION; + pub fn ASN1_STRING_to_UTF8(out: *mut *mut c_uchar, s: *mut ::ASN1_STRING) -> c_int; + pub fn ASN1_STRING_data(x: *mut ::ASN1_STRING) -> *mut c_uchar; + pub fn CRYPTO_add_lock(pointer: *mut c_int, + amount: c_int, + type_: c_int, + file: *const c_char, + line: c_int) -> c_int; + pub fn EVP_MD_CTX_create() -> *mut EVP_MD_CTX; + pub fn EVP_MD_CTX_destroy(ctx: *mut EVP_MD_CTX); + pub fn EVP_PKEY_bits(key: *mut EVP_PKEY) -> c_int; + + pub fn sk_num(st: *const _STACK) -> c_int; + pub fn sk_value(st: *const _STACK, n: c_int) -> *mut c_void; + pub fn sk_free(st: *mut _STACK); + pub fn sk_pop_free(st: *mut _STACK, free: Option); + pub fn sk_pop(st: *mut _STACK) -> *mut c_void; + + pub fn SSLeay() -> c_ulong; + pub fn SSLeay_version(key: c_int) -> *const c_char; +} diff --git a/openssl-sys/src/ossl10x.rs b/openssl-sys/src/ossl10x.rs index f552be01a58fb768edf39817c8069b8c7e356bb6..128689e58d614424798054905cd344c9932ebe8f 100644 --- a/openssl-sys/src/ossl10x.rs +++ b/openssl-sys/src/ossl10x.rs @@ -41,6 +41,16 @@ pub struct stack_st_void { pub stack: _STACK, } +#[repr(C)] +pub struct stack_st_SSL_CIPHER { + pub stack: _STACK, +} + +#[repr(C)] +pub struct stack_st_OPENSSL_STRING { + pub stack: _STACK, +} + #[repr(C)] pub struct _STACK { pub num: c_int, @@ -240,8 +250,8 @@ pub struct DH { #[repr(C)] pub struct X509 { pub cert_info: *mut X509_CINF, - sig_alg: *mut c_void, - signature: *mut c_void, + pub sig_alg: *mut ::X509_ALGOR, + pub signature: *mut ::ASN1_BIT_STRING, pub valid: c_int, pub references: c_int, pub name: *mut c_char, @@ -282,6 +292,12 @@ pub struct X509_CINF { enc: ASN1_ENCODING, } +#[repr(C)] +pub struct X509_ALGOR { + pub algorithm: *mut ::ASN1_OBJECT, + parameter: *mut c_void, +} + #[repr(C)] pub struct ASN1_ENCODING { pub enc: *mut c_uchar, @@ -295,6 +311,160 @@ pub struct X509_VAL { pub notAfter: *mut ::ASN1_TIME, } +#[repr(C)] +pub struct X509_REQ_INFO { + pub enc: ASN1_ENCODING, + pub version: *mut ::ASN1_INTEGER, + pub subject: *mut ::X509_NAME, + pubkey: *mut c_void, + pub attributes: *mut stack_st_X509_ATTRIBUTE +} + +#[repr(C)] +pub struct X509_REQ { + pub req_info: *mut X509_REQ_INFO, + sig_alg: *mut c_void, + signature: *mut c_void, + references: c_int +} + +#[repr(C)] +pub struct SSL { + version: c_int, + type_: c_int, + method: *const ::SSL_METHOD, + rbio: *mut c_void, + wbio: *mut c_void, + bbio: *mut c_void, + rwstate: c_int, + in_handshake: c_int, + handshake_func: Option c_int>, + pub server: c_int, + new_session: c_int, + quiet_session: c_int, + shutdown: c_int, + state: c_int, + rstate: c_int, + init_buf: *mut c_void, + init_msg: *mut c_void, + init_num: c_int, + init_off: c_int, + packet: *mut c_uchar, + packet_length: c_uint, + s2: *mut c_void, + s3: *mut c_void, + d1: *mut c_void, + read_ahead: c_int, + msg_callback: Option, + msg_callback_arg: *mut c_void, + hit: c_int, + param: *mut c_void, + cipher_list: *mut stack_st_SSL_CIPHER, + cipher_list_by_id: *mut stack_st_SSL_CIPHER, + mac_flags: c_int, + enc_read_ctx: *mut ::EVP_CIPHER_CTX, + read_hash: *mut ::EVP_MD_CTX, + expand: *mut c_void, + enc_write_ctx: *mut ::EVP_CIPHER_CTX, + write_hash: *mut ::EVP_MD_CTX, + compress: *mut c_void, + cert: *mut c_void, + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; ::SSL_MAX_SID_CTX_LENGTH as usize], + session: *mut ::SSL_SESSION, + generate_session_id: ::GEN_SESSION_CB, + verify_mode: c_int, + verify_callback: Option c_int>, + info_callback: Option, + error: c_int, + error_code: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_KRB5"))] + kssl_ctx: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + psk_client_callback: Option c_uint>, + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + psk_server_callback: Option c_uint>, + ctx: *mut ::SSL_CTX, + debug: c_int, + verify_result: c_long, + ex_data: ::CRYPTO_EX_DATA, + client_CA: *mut stack_st_X509_NAME, + references: c_int, + options: c_ulong, + mode: c_ulong, + max_cert_list: c_long, + first_packet: c_int, + client_version: c_int, + max_send_fragment: c_uint, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_debug_cb: Option, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_debug_arg: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_hostname: *mut c_char, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + servername_done: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_status_type: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_status_expected: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ocsp_ids: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ocsp_exts: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ocsp_resp: *mut c_uchar, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ocsp_resplen: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ticket_expected: c_int, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ecpointformatlist_length: size_t, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ecpointformatlist: *mut c_uchar, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ellipticcurvelist_length: size_t, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ellipticcurvelist: *mut c_uchar, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_opaque_prf_input: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_opaque_prf_input_len: size_t, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_session_ticket: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_session_ticket_ext_cb: ::tls_session_ticket_ext_cb_fn, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tls_session_ticket_ext_cb_arg: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tls_session_secret_cb: ::tls_session_secret_cb_fn, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tls_session_secret_cb_arg: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + initial_ctx: *mut ::SSL_CTX, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_NEXTPROTONEG")))] + next_proto_negotiated: *mut c_uchar, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_NEXTPROTONEG")))] + next_proto_negotiated_len: c_uchar, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + srtp_profiles: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + srtp_profile: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_heartbeat: c_uint, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_hb_pending: c_uint, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_hb_seq: c_uint, + renegotiate: c_int, + #[cfg(not(osslconf = "OPENSSL_NO_SRP"))] + srp_ctx: ::SRP_CTX, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), ossl102))] + alpn_client_proto_list: *mut c_uchar, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), ossl102))] + alpn_client_proto_list_len: c_uint, +} + #[repr(C)] pub struct SSL_CTX { method: *mut c_void, @@ -417,6 +587,59 @@ pub struct SSL_CTX { tlsext_ellipticcurvelist: *mut c_uchar, } +#[repr(C)] +pub struct SSL_SESSION { + ssl_version: c_int, + key_arg_length: c_uint, + key_arg: [c_uchar; SSL_MAX_KEY_ARG_LENGTH as usize], + pub master_key_length: c_int, + pub master_key: [c_uchar; 48], + session_id_length: c_uint, + session_id: [c_uchar; SSL_MAX_SSL_SESSION_ID_LENGTH as usize], + sid_ctx_length: c_uint, + sid_ctx: [c_uchar; SSL_MAX_SID_CTX_LENGTH as usize], + #[cfg(not(osslconf = "OPENSSL_NO_KRB5"))] + krb5_client_princ_len: c_uint, + #[cfg(not(osslconf = "OPENSSL_NO_KRB5"))] + krb5_client_princ: [c_uchar; SSL_MAX_KRB5_PRINCIPAL_LENGTH as usize], + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + psk_identity_hint: *mut c_char, + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + psk_identity: *mut c_char, + not_resumable: c_int, + sess_cert: *mut c_void, + peer: *mut X509, + verify_result: c_long, + references: c_int, + timeout: c_long, + time: c_long, + compress_meth: c_uint, + cipher: *const c_void, + cipher_id: c_ulong, + ciphers: *mut c_void, + ex_data: ::CRYPTO_EX_DATA, + prev: *mut c_void, + next: *mut c_void, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_hostname: *mut c_char, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ecpointformatlist_length: size_t, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ecpointformatlist: *mut c_uchar, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ellipticcurvelist_length: size_t, + #[cfg(all(not(osslconf = "OPENSSL_NO_TLSEXT"), not(osslconf = "OPENSSL_NO_EC")))] + tlsext_ellipticcurvelist: *mut c_uchar, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_tick: *mut c_uchar, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_ticklen: size_t, + #[cfg(not(osslconf = "OPENSSL_NO_TLSEXT"))] + tlsext_tick_lifetime_hint: c_long, + #[cfg(not(osslconf = "OPENSSL_NO_SRP"))] + srp_username: *mut c_char, +} + #[repr(C)] pub struct SRP_CTX { SRP_cb_arg: *mut c_void, @@ -453,6 +676,7 @@ pub struct X509_VERIFY_PARAM { #[cfg(not(ossl101))] pub enum X509_VERIFY_PARAM_ID {} +pub enum PKCS12 {} pub const SSL_CTRL_OPTIONS: c_int = 32; pub const SSL_CTRL_CLEAR_OPTIONS: c_int = 77; @@ -470,6 +694,12 @@ pub const SSL_OP_SINGLE_ECDH_USE: c_ulong = 0x00080000; pub const SSL_OP_SINGLE_DH_USE: c_ulong = 0x00100000; pub const SSL_OP_NO_SSLv2: c_ulong = 0x01000000; +pub const SSL_MAX_SSL_SESSION_ID_LENGTH: c_int = 32; +pub const SSL_MAX_SID_CTX_LENGTH: c_int = 32; +pub const SSL_MAX_KEY_ARG_LENGTH: c_int = 8; +pub const SSL_MAX_MASTER_KEY_LENGTH: c_int = 48; +pub const SSL_MAX_KRB5_PRINCIPAL_LENGTH: c_int = 256; + pub const SSLEAY_VERSION : c_int = 0; pub const SSLEAY_CFLAGS : c_int = 2; pub const SSLEAY_BUILT_ON : c_int = 3; @@ -533,15 +763,32 @@ fn set_id_callback() { fn set_id_callback() {} // macros + #[cfg(ossl102)] pub unsafe fn SSL_CTX_set_ecdh_auto(ctx: *mut SSL_CTX, onoff: c_int) -> c_int { ::SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff as c_long, ::std::ptr::null_mut()) as c_int } +#[cfg(ossl102)] +pub unsafe fn SSL_set_ecdh_auto(ssl: *mut ::SSL, onoff: c_int) -> c_int { + ::SSL_ctrl(ssl, SSL_CTRL_SET_ECDH_AUTO, onoff as c_long, ::std::ptr::null_mut()) as c_int +} + extern { pub fn BIO_new(type_: *mut BIO_METHOD) -> *mut BIO; pub fn BIO_s_file() -> *mut BIO_METHOD; pub fn BIO_s_mem() -> *mut BIO_METHOD; + + pub fn get_rfc2409_prime_768(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc2409_prime_1024(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_1536(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_2048(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_3072(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_4096(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_6144(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn get_rfc3526_prime_8192(bn: *mut BIGNUM) -> *mut BIGNUM; + + pub fn CRYPTO_malloc(num: c_int, file: *const c_char, line: c_int) -> *mut c_void; pub fn CRYPTO_free(buf: *mut c_void); pub fn CRYPTO_num_locks() -> c_int; pub fn CRYPTO_set_locking_callback(func: unsafe extern "C" fn(mode: c_int, @@ -557,6 +804,19 @@ extern { cb: Option, cbarg: *mut c_void) -> *mut RSA; + pub fn OCSP_cert_to_id(dgst: *const ::EVP_MD, subject: *mut ::X509, issuer: *mut ::X509) -> *mut ::OCSP_CERTID; + + pub fn PKCS12_create(pass: *mut c_char, + friendly_name: *mut c_char, + pkey: *mut EVP_PKEY, + cert: *mut X509, + ca: *mut stack_st_X509, + nid_key: c_int, + nid_cert: c_int, + iter: c_int, + mac_iter: c_int, + keytype: c_int) -> *mut PKCS12; + pub fn SSL_library_init() -> c_int; pub fn SSL_load_error_strings(); pub fn OPENSSL_add_all_algorithms_noconf(); @@ -576,17 +836,33 @@ extern { dup_func: Option<::CRYPTO_EX_dup>, free_func: Option<::CRYPTO_EX_free>) -> c_int; + pub fn SSL_set_tmp_ecdh_callback(ssl: *mut ::SSL, + ecdh: unsafe extern fn(ssl: *mut ::SSL, + is_export: c_int, + keylength: c_int) + -> *mut ::EC_KEY); pub fn SSL_CIPHER_get_version(cipher: *const ::SSL_CIPHER) -> *mut c_char; pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *mut c_void, new_func: Option<::CRYPTO_EX_new>, dup_func: Option<::CRYPTO_EX_dup>, free_func: Option<::CRYPTO_EX_free>) -> c_int; + pub fn SSL_CTX_set_tmp_ecdh_callback(ctx: *mut ::SSL_CTX, + ecdh: unsafe extern fn(ssl: *mut ::SSL, + is_export: c_int, + keylength: c_int) + -> *mut ::EC_KEY); pub fn X509_get_subject_name(x: *mut ::X509) -> *mut ::X509_NAME; pub fn X509_set_notAfter(x: *mut ::X509, tm: *const ::ASN1_TIME) -> c_int; pub fn X509_set_notBefore(x: *mut ::X509, tm: *const ::ASN1_TIME) -> c_int; pub fn X509_get_ext_d2i(x: *mut ::X509, nid: c_int, crit: *mut c_int, idx: *mut c_int) -> *mut c_void; pub fn X509_NAME_add_entry_by_NID(x: *mut ::X509_NAME, field: c_int, ty: c_int, bytes: *mut c_uchar, len: c_int, loc: c_int, set: c_int) -> c_int; + #[cfg(not(ossl101))] + pub fn X509_get0_signature(psig: *mut *mut ::ASN1_BIT_STRING, palg: *mut *mut ::X509_ALGOR, x: *const ::X509); + #[cfg(not(ossl101))] + pub fn X509_get_signature_nid(x: *const X509) -> c_int; + #[cfg(not(ossl101))] + pub fn X509_ALGOR_get0(paobj: *mut *mut ::ASN1_OBJECT, pptype: *mut c_int, ppval: *mut *mut c_void, alg: *mut ::X509_ALGOR); pub fn X509_NAME_get_entry(n: *mut ::X509_NAME, loc: c_int) -> *mut ::X509_NAME_ENTRY; pub fn X509_NAME_ENTRY_get_data(ne: *mut ::X509_NAME_ENTRY) -> *mut ::ASN1_STRING; pub fn X509_STORE_CTX_get_chain(ctx: *mut ::X509_STORE_CTX) -> *mut stack_st_X509; @@ -601,6 +877,7 @@ extern { line: c_int) -> c_int; pub fn EVP_MD_CTX_create() -> *mut EVP_MD_CTX; pub fn EVP_MD_CTX_destroy(ctx: *mut EVP_MD_CTX); + pub fn EVP_PKEY_bits(key: *mut EVP_PKEY) -> c_int; pub fn sk_new_null() -> *mut _STACK; pub fn sk_num(st: *const _STACK) -> c_int; diff --git a/openssl-sys/src/ossl110.rs b/openssl-sys/src/ossl110.rs index eca4c84e54a9bd3835b232dcf28c79e3d49ac36f..24ac47d272ecc5402b70e54722175ef5c77b5709 100644 --- a/openssl-sys/src/ossl110.rs +++ b/openssl-sys/src/ossl110.rs @@ -1,4 +1,4 @@ -use libc::{c_int, c_void, c_char, c_uchar, c_ulong, c_long, c_uint}; +use libc::{c_int, c_void, c_char, c_uchar, c_ulong, c_long, c_uint, size_t}; pub enum BIGNUM {} pub enum BIO {} @@ -11,8 +11,11 @@ pub enum EVP_MD_CTX {} pub enum EVP_PKEY {} pub enum HMAC_CTX {} pub enum OPENSSL_STACK {} +pub enum PKCS12 {} pub enum RSA {} +pub enum SSL {} pub enum SSL_CTX {} +pub enum SSL_SESSION {} pub enum stack_st_ASN1_OBJECT {} pub enum stack_st_GENERAL_NAME {} pub enum stack_st_OPENSSL_STRING {} @@ -21,8 +24,11 @@ pub enum stack_st_X509 {} pub enum stack_st_X509_NAME {} pub enum stack_st_X509_ATTRIBUTE {} pub enum stack_st_X509_EXTENSION {} +pub enum stack_st_SSL_CIPHER {} pub enum X509 {} +pub enum X509_ALGOR {} pub enum X509_VERIFY_PARAM {} +pub enum X509_REQ {} pub const SSL_OP_MICROSOFT_SESS_ID_BUG: c_ulong = 0x00000000; pub const SSL_OP_NETSCAPE_CHALLENGE_BUG: c_ulong = 0x00000000; @@ -52,9 +58,27 @@ extern { pub fn BIO_new(type_: *const BIO_METHOD) -> *mut BIO; pub fn BIO_s_file() -> *const BIO_METHOD; pub fn BIO_s_mem() -> *const BIO_METHOD; + + pub fn BN_get_rfc2409_prime_768(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn BN_get_rfc2409_prime_1024(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn BN_get_rfc3526_prime_1536(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn BN_get_rfc3526_prime_2048(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn BN_get_rfc3526_prime_3072(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn BN_get_rfc3526_prime_4096(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn BN_get_rfc3526_prime_6144(bn: *mut BIGNUM) -> *mut BIGNUM; + pub fn BN_get_rfc3526_prime_8192(bn: *mut BIGNUM) -> *mut BIGNUM; + + pub fn CRYPTO_malloc(num: size_t, file: *const c_char, line: c_int) -> *mut c_void; pub fn CRYPTO_free(buf: *mut c_void, file: *const c_char, line: c_int); + + pub fn EVP_chacha20() -> *const ::EVP_CIPHER; + pub fn EVP_chacha20_poly1305() -> *const ::EVP_CIPHER; + pub fn HMAC_CTX_new() -> *mut HMAC_CTX; pub fn HMAC_CTX_free(ctx: *mut HMAC_CTX); + + pub fn OCSP_cert_to_id(dgst: *const ::EVP_MD, subject: *const ::X509, issuer: *const ::X509) -> *mut ::OCSP_CERTID; + pub fn TLS_method() -> *const ::SSL_METHOD; pub fn DTLS_method() -> *const ::SSL_METHOD; pub fn SSL_CIPHER_get_version(cipher: *const ::SSL_CIPHER) -> *const c_char; @@ -63,6 +87,8 @@ extern { pub fn X509_set1_notBefore(x: *mut ::X509, tm: *const ::ASN1_TIME) -> c_int; pub fn X509_get_ext_d2i(x: *const ::X509, nid: c_int, crit: *mut c_int, idx: *mut c_int) -> *mut c_void; pub fn X509_NAME_add_entry_by_NID(x: *mut ::X509_NAME, field: c_int, ty: c_int, bytes: *const c_uchar, len: c_int, loc: c_int, set: c_int) -> c_int; + pub fn X509_get_signature_nid(x: *const X509) -> c_int; + pub fn X509_ALGOR_get0(paobj: *mut *const ::ASN1_OBJECT, pptype: *mut c_int, ppval: *mut *const c_void, alg: *const ::X509_ALGOR); pub fn X509_NAME_get_entry(n: *const ::X509_NAME, loc: c_int) -> *mut ::X509_NAME_ENTRY; pub fn X509_NAME_ENTRY_get_data(ne: *const ::X509_NAME_ENTRY) -> *mut ::ASN1_STRING; pub fn X509V3_EXT_nconf_nid(conf: *mut ::CONF, ctx: *mut ::X509V3_CTX, ext_nid: c_int, value: *const c_char) -> *mut ::X509_EXTENSION; @@ -106,6 +132,7 @@ extern { pub fn SSL_CTX_clear_options(ctx: *mut ::SSL_CTX, op: c_ulong) -> c_ulong; pub fn X509_getm_notAfter(x: *const ::X509) -> *mut ::ASN1_TIME; pub fn X509_getm_notBefore(x: *const ::X509) -> *mut ::ASN1_TIME; + pub fn X509_get0_signature(psig: *mut *const ::ASN1_BIT_STRING, palg: *mut *const ::X509_ALGOR, x: *const ::X509); pub fn DH_set0_pqg(dh: *mut ::DH, p: *mut ::BIGNUM, q: *mut ::BIGNUM, @@ -144,10 +171,15 @@ extern { -> c_int; pub fn X509_up_ref(x: *mut X509) -> c_int; pub fn SSL_CTX_up_ref(x: *mut SSL_CTX) -> c_int; + pub fn SSL_SESSION_get_master_key(session: *const SSL_SESSION, + out: *mut c_uchar, + outlen: size_t) + -> size_t; pub fn X509_get0_extensions(req: *const ::X509) -> *const stack_st_X509_EXTENSION; pub fn X509_STORE_CTX_get0_chain(ctx: *mut ::X509_STORE_CTX) -> *mut stack_st_X509; pub fn EVP_MD_CTX_new() -> *mut EVP_MD_CTX; pub fn EVP_MD_CTX_free(ctx: *mut EVP_MD_CTX); + pub fn EVP_PKEY_bits(key: *const EVP_PKEY) -> c_int; pub fn OpenSSL_version_num() -> c_ulong; pub fn OpenSSL_version(key: c_int) -> *const c_char; @@ -156,4 +188,17 @@ extern { pub fn OPENSSL_sk_pop_free(st: *mut ::OPENSSL_STACK, free: Option); pub fn OPENSSL_sk_push(st: *mut ::OPENSSL_STACK, data: *const c_void) -> c_int; pub fn OPENSSL_sk_pop(st: *mut ::OPENSSL_STACK) -> *mut c_void; + + pub fn PKCS12_create(pass: *const c_char, + friendly_name: *const c_char, + pkey: *mut EVP_PKEY, + cert: *mut X509, + ca: *mut stack_st_X509, + nid_key: c_int, + nid_cert: c_int, + iter: c_int, + mac_iter: c_int, + keytype: c_int) -> *mut PKCS12; + pub fn X509_REQ_get_version(req: *const X509_REQ) -> c_long; + pub fn X509_REQ_get_subject_name(req: *const X509_REQ) -> *mut ::X509_NAME; } diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml index 1417bea7de060dd006fd311bd46a9c137736ef8f..7855897f681fdaa112cafb0bc7f4359005e28594 100644 --- a/openssl/Cargo.toml +++ b/openssl/Cargo.toml @@ -1,13 +1,14 @@ [package] name = "openssl" -version = "0.9.0" +version = "0.9.6" 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/v0.9.0/openssl" +documentation = "https://docs.rs/openssl/0.9.6/openssl" readme = "../README.md" keywords = ["crypto", "tls", "ssl", "dtls"] +categories = ["cryptography", "api-bindings"] build = "build.rs" exclude = ["test/*"] @@ -18,12 +19,13 @@ v110 = [] [dependencies] bitflags = "0.7" +foreign-types = { git = "https://github.com/sfackler/foreign-types" } lazy_static = "0.2" libc = "0.2" -openssl-sys = { version = "0.9", path = "../openssl-sys" } +openssl-sys = { version = "0.9.6", path = "../openssl-sys" } [dev-dependencies] -rustc-serialize = "0.3" tempdir = "0.3" winapi = "0.2" ws2_32-sys = "0.2" +hex = "0.2" \ No newline at end of file diff --git a/openssl/build.rs b/openssl/build.rs index 7a0c60dcc5529b7a7f0aa1332f71407498637374..954e9b0c622cecb58faabb7f94fe22371567634b 100644 --- a/openssl/build.rs +++ b/openssl/build.rs @@ -16,6 +16,10 @@ fn main() { _ => panic!("Unable to detect OpenSSL version"), } + if let Ok(_) = env::var("DEP_OPENSSL_LIBRESSL") { + println!("cargo:rustc-cfg=libressl"); + } + if let Ok(vars) = env::var("DEP_OPENSSL_CONF") { for var in vars.split(",") { println!("cargo:rustc-cfg=osslconf=\"{}\"", var); diff --git a/openssl/src/aes.rs b/openssl/src/aes.rs new file mode 100644 index 0000000000000000000000000000000000000000..d226c515b7f884d341674e5809f7ced061b33868 --- /dev/null +++ b/openssl/src/aes.rs @@ -0,0 +1,115 @@ +//! Low level AES functionality +//! +//! The `symm` module should be used in preference to this module in most cases. +use ffi; +use std::mem; +use libc::c_int; + +use symm::Mode; + +#[derive(Debug)] +pub struct KeyError(()); + +pub struct AesKey(ffi::AES_KEY); + +impl AesKey { + /// Prepares a key for encryption. + /// + /// # Failure + /// + /// Returns an error if the key is not 128, 192, or 256 bits. + pub fn new_encrypt(key: &[u8]) -> Result { + unsafe { + assert!(key.len() <= c_int::max_value() as usize / 8); + + let mut aes_key = mem::uninitialized(); + let r = ffi::AES_set_encrypt_key(key.as_ptr() as *const _, + key.len() as c_int * 8, + &mut aes_key); + if r == 0 { + Ok(AesKey(aes_key)) + } else { + Err(KeyError(())) + } + } + } + + /// Prepares a key for decryption. + /// + /// # Failure + /// + /// Returns an error if the key is not 128, 192, or 256 bits. + pub fn new_decrypt(key: &[u8]) -> Result { + unsafe { + assert!(key.len() <= c_int::max_value() as usize / 8); + + let mut aes_key = mem::uninitialized(); + let r = ffi::AES_set_decrypt_key(key.as_ptr() as *const _, + key.len() as c_int * 8, + &mut aes_key); + + if r == 0 { + Ok(AesKey(aes_key)) + } else { + Err(KeyError(())) + } + } + } +} + +/// Performs AES IGE encryption or decryption +/// +/// # Panics +/// +/// Panics if `in_` is not the same length as `out`, if that length is not a multiple of 16, or if +/// `iv` is not at least 32 bytes. +pub fn aes_ige(in_: &[u8], out: &mut [u8], key: &AesKey, iv: &mut [u8], mode: Mode) { + unsafe { + assert!(in_.len() == out.len()); + assert!(in_.len() % ffi::AES_BLOCK_SIZE as usize == 0); + assert!(iv.len() >= ffi::AES_BLOCK_SIZE as usize * 2); + + let mode = match mode { + Mode::Encrypt => ffi::AES_ENCRYPT, + Mode::Decrypt => ffi::AES_DECRYPT, + }; + ffi::AES_ige_encrypt(in_.as_ptr() as *const _, + out.as_mut_ptr() as *mut _, + in_.len(), + &key.0, + iv.as_mut_ptr() as *mut _, + mode); + } +} + +#[cfg(test)] +mod test { + use hex::FromHex; + + use symm::Mode; + use super::*; + + // From https://www.mgp25.com/AESIGE/ + #[test] + fn ige_vector_1() { + let raw_key = "000102030405060708090A0B0C0D0E0F"; + let raw_iv = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"; + let raw_pt = "0000000000000000000000000000000000000000000000000000000000000000"; + let raw_ct = "1A8519A6557BE652E9DA8E43DA4EF4453CF456B4CA488AA383C79C98B34797CB"; + + let key = AesKey::new_encrypt(&Vec::from_hex(raw_key).unwrap()).unwrap(); + let mut iv = Vec::from_hex(raw_iv).unwrap(); + let pt = Vec::from_hex(raw_pt).unwrap(); + let ct = Vec::from_hex(raw_ct).unwrap(); + + let mut ct_actual = vec![0; ct.len()]; + aes_ige(&pt, &mut ct_actual, &key, &mut iv, Mode::Encrypt); + assert_eq!(ct_actual, ct); + + let key = AesKey::new_decrypt(&Vec::from_hex(raw_key).unwrap()).unwrap(); + let mut iv = Vec::from_hex(raw_iv).unwrap(); + let mut pt_actual = vec![0; pt.len()]; + aes_ige(&ct, &mut pt_actual, &key, &mut iv, Mode::Decrypt); + assert_eq!(pt_actual, pt); + } +} diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs index 998f9878a9c1d3631a8c4b1fc0b6bc90f90f745d..a74f22e809c3695252a6d00f965c536361d920f9 100644 --- a/openssl/src/asn1.rs +++ b/openssl/src/asn1.rs @@ -1,5 +1,6 @@ use ffi; -use libc::c_long; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_long, c_char, c_int}; use std::fmt; use std::ptr; use std::slice; @@ -7,11 +8,35 @@ use std::str; use {cvt, cvt_p}; use bio::MemBio; -use crypto::CryptoString; use error::ErrorStack; -use types::{OpenSslType, OpenSslTypeRef}; +use nid::Nid; +use string::OpensslString; -type_!(Asn1Time, Asn1TimeRef, ffi::ASN1_TIME, ffi::ASN1_TIME_free); +foreign_type! { + type CType = ffi::ASN1_GENERALIZEDTIME; + fn drop = ffi::ASN1_GENERALIZEDTIME_free; + + pub struct Asn1GeneralizedTime; + pub struct Asn1GeneralizedTimeRef; +} + +impl fmt::Display for Asn1GeneralizedTimeRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { + let mem_bio = try!(MemBio::new()); + try!(cvt(ffi::ASN1_GENERALIZEDTIME_print(mem_bio.as_ptr(), self.as_ptr()))); + write!(f, "{}", str::from_utf8_unchecked(mem_bio.get_buf())) + } + } +} + +foreign_type! { + type CType = ffi::ASN1_TIME; + fn drop = ffi::ASN1_TIME_free; + + pub struct Asn1Time; + pub struct Asn1TimeRef; +} impl fmt::Display for Asn1TimeRef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -39,10 +64,16 @@ impl Asn1Time { } } -type_!(Asn1String, Asn1StringRef, ffi::ASN1_STRING, ffi::ASN1_STRING_free); +foreign_type! { + type CType = ffi::ASN1_STRING; + fn drop = ffi::ASN1_STRING_free; + + pub struct Asn1String; + pub struct Asn1StringRef; +} impl Asn1StringRef { - pub fn as_utf8(&self) -> Result { + pub fn as_utf8(&self) -> Result { unsafe { let mut ptr = ptr::null_mut(); let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr()); @@ -50,7 +81,7 @@ impl Asn1StringRef { return Err(ErrorStack::get()); } - Ok(CryptoString::from_raw_parts(ptr, len as usize)) + Ok(OpensslString::from_ptr(ptr as *mut c_char)) } } @@ -63,7 +94,77 @@ impl Asn1StringRef { } } -type_!(Asn1Integer, Asn1IntegerRef, ffi::ASN1_INTEGER, ffi::ASN1_INTEGER_free); +foreign_type! { + type CType = ffi::ASN1_INTEGER; + fn drop = ffi::ASN1_INTEGER_free; + + pub struct Asn1Integer; + pub struct Asn1IntegerRef; +} + +impl Asn1IntegerRef { + pub fn get(&self) -> i64 { + unsafe { + ::ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 + } + } + + pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> + { + unsafe { + cvt(::ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) + } + } +} + +foreign_type! { + type CType = ffi::ASN1_BIT_STRING; + fn drop = ffi::ASN1_BIT_STRING_free; + + pub struct Asn1BitString; + pub struct Asn1BitStringRef; +} + +impl Asn1BitStringRef { + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(ASN1_STRING_data(self.as_ptr() as *mut _), self.len()) } + } + + pub fn len(&self) -> usize { + unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *mut _) as usize } + } +} + +foreign_type! { + type CType = ffi::ASN1_OBJECT; + fn drop = ffi::ASN1_OBJECT_free; + + pub struct Asn1Object; + pub struct Asn1ObjectRef; +} + +impl Asn1ObjectRef { + /// Returns the NID associated with this OID. + pub fn nid(&self) -> Nid { + unsafe { + Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) + } + } +} + +impl fmt::Display for Asn1ObjectRef { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + unsafe { + let mut buf = [0; 80]; + let len = ffi::OBJ_obj2txt(buf.as_mut_ptr() as *mut _, + buf.len() as c_int, + self.as_ptr(), + 0); + let s = try!(str::from_utf8(&buf[..len as usize]).map_err(|_| fmt::Error)); + fmt.write_str(s) + } + } +} #[cfg(any(ossl101, ossl102))] use ffi::ASN1_STRING_data; diff --git a/openssl/src/bn.rs b/openssl/src/bn.rs index ec0b1aba35dae828227ed895a3fc11adc1cd1811..9557b5bb4a92f8f3216737cc596df0d745738703 100644 --- a/openssl/src/bn.rs +++ b/openssl/src/bn.rs @@ -1,4 +1,5 @@ use ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; use libc::c_int; use std::cmp::Ordering; use std::ffi::CString; @@ -7,9 +8,23 @@ use std::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub, Deref}; use {cvt, cvt_p, cvt_n}; use asn1::Asn1Integer; -use crypto::CryptoString; use error::ErrorStack; -use types::{OpenSslType, OpenSslTypeRef}; +use string::OpensslString; + +#[cfg(ossl10x)] +use ffi::{get_rfc2409_prime_768 as BN_get_rfc2409_prime_768, + get_rfc2409_prime_1024 as BN_get_rfc2409_prime_1024, + get_rfc3526_prime_1536 as BN_get_rfc3526_prime_1536, + get_rfc3526_prime_2048 as BN_get_rfc3526_prime_2048, + get_rfc3526_prime_3072 as BN_get_rfc3526_prime_3072, + get_rfc3526_prime_4096 as BN_get_rfc3526_prime_4096, + get_rfc3526_prime_6144 as BN_get_rfc3526_prime_6144, + get_rfc3526_prime_8192 as BN_get_rfc3526_prime_8192}; + +#[cfg(ossl110)] +use ffi::{BN_get_rfc2409_prime_768, BN_get_rfc2409_prime_1024, BN_get_rfc3526_prime_1536, + BN_get_rfc3526_prime_2048, BN_get_rfc3526_prime_3072, BN_get_rfc3526_prime_4096, + BN_get_rfc3526_prime_6144, BN_get_rfc3526_prime_8192}; /// Options for the most significant bits of a randomly generated `BigNum`. pub struct MsbOption(c_int); @@ -26,7 +41,13 @@ pub const MSB_ONE: MsbOption = MsbOption(0); /// of bits in the original numbers. pub const TWO_MSB_ONE: MsbOption = MsbOption(1); -type_!(BigNumContext, BigNumContextRef, ffi::BN_CTX, ffi::BN_CTX_free); +foreign_type! { + type CType = ffi::BN_CTX; + fn drop = ffi::BN_CTX_free; + + pub struct BigNumContext; + pub struct BigNumContextRef; +} impl BigNumContext { /// Returns a new `BigNumContext`. @@ -470,12 +491,12 @@ impl BigNumRef { /// # use openssl::bn::BigNum; /// let s = -BigNum::from_u32(12345).unwrap(); /// - /// assert_eq!(&*s.to_dec_str().unwrap(), "-12345"); + /// assert_eq!(&**s.to_dec_str().unwrap(), "-12345"); /// ``` - pub fn to_dec_str(&self) -> Result { + pub fn to_dec_str(&self) -> Result { unsafe { let buf = try!(cvt_p(ffi::BN_bn2dec(self.as_ptr()))); - Ok(CryptoString::from_null_terminated(buf)) + Ok(OpensslString::from_ptr(buf)) } } @@ -485,12 +506,12 @@ impl BigNumRef { /// # use openssl::bn::BigNum; /// let s = -BigNum::from_u32(0x99ff).unwrap(); /// - /// assert_eq!(&*s.to_hex_str().unwrap(), "-99FF"); + /// assert_eq!(&**s.to_hex_str().unwrap(), "-99FF"); /// ``` - pub fn to_hex_str(&self) -> Result { + pub fn to_hex_str(&self) -> Result { unsafe { let buf = try!(cvt_p(ffi::BN_bn2hex(self.as_ptr()))); - Ok(CryptoString::from_null_terminated(buf)) + Ok(OpensslString::from_ptr(buf)) } } @@ -503,7 +524,13 @@ impl BigNumRef { } } -type_!(BigNum, BigNumRef, ffi::BIGNUM, ffi::BN_free); +foreign_type! { + type CType = ffi::BIGNUM; + fn drop = ffi::BN_free; + + pub struct BigNum; + pub struct BigNumRef; +} impl BigNum { /// Creates a new `BigNum` with the value 0. @@ -525,6 +552,7 @@ impl BigNum { /// Creates a `BigNum` from a decimal string. pub fn from_dec_str(s: &str) -> Result { unsafe { + ffi::init(); let c_str = CString::new(s.as_bytes()).unwrap(); let mut bn = ptr::null_mut(); try!(cvt(ffi::BN_dec2bn(&mut bn, c_str.as_ptr() as *const _))); @@ -535,6 +563,7 @@ impl BigNum { /// Creates a `BigNum` from a hexadecimal string. pub fn from_hex_str(s: &str) -> Result { unsafe { + ffi::init(); let c_str = CString::new(s.as_bytes()).unwrap(); let mut bn = ptr::null_mut(); try!(cvt(ffi::BN_hex2bn(&mut bn, c_str.as_ptr() as *const _))); @@ -542,6 +571,62 @@ impl BigNum { } } + pub fn get_rfc2409_prime_768() -> Result { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc2409_prime_768(ptr::null_mut())).map(BigNum) + } + } + + pub fn get_rfc2409_prime_1024() -> Result { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc2409_prime_1024(ptr::null_mut())).map(BigNum) + } + } + + pub fn get_rfc3526_prime_1536() -> Result { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_1536(ptr::null_mut())).map(BigNum) + } + } + + pub fn get_rfc3526_prime_2048() -> Result { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_2048(ptr::null_mut())).map(BigNum) + } + } + + pub fn get_rfc3526_prime_3072() -> Result { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_3072(ptr::null_mut())).map(BigNum) + } + } + + pub fn get_rfc3526_prime_4096() -> Result { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_4096(ptr::null_mut())).map(BigNum) + } + } + + pub fn get_rfc3526_prime_6144() -> Result { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_6144(ptr::null_mut())).map(BigNum) + } + } + + pub fn get_rfc3526_prime_8192() -> Result { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_8192(ptr::null_mut())).map(BigNum) + } + } + /// Creates a new `BigNum` from an unsigned, big-endian encoded number of arbitrary length. /// /// ``` @@ -552,6 +637,7 @@ impl BigNum { /// ``` pub fn from_slice(n: &[u8]) -> Result { unsafe { + ffi::init(); assert!(n.len() <= c_int::max_value() as usize); cvt_p(ffi::BN_bin2bn(n.as_ptr(), n.len() as c_int, ptr::null_mut())) .map(|p| BigNum::from_ptr(p)) diff --git a/openssl/src/conf.rs b/openssl/src/conf.rs index 5483955de88c3773bb4af710cdc1d31ba9c1a47a..a648bb150c8237389d7976f58dc6a4090b886ad0 100644 --- a/openssl/src/conf.rs +++ b/openssl/src/conf.rs @@ -22,7 +22,13 @@ impl ConfMethod { } } -type_!(Conf, ConfRef, ffi::CONF, ffi::NCONF_free); +foreign_type! { + type CType = ffi::CONF; + fn drop = ffi::NCONF_free; + + pub struct Conf; + pub struct ConfRef; +} impl Conf { pub fn new(method: ConfMethod) -> Result { diff --git a/openssl/src/crypto.rs b/openssl/src/crypto.rs index ce83cbaec2a30bac2773dc013ada81f747ffea39..9853d99c14573f3c2292e477ea7c851a0db4d6b8 100644 --- a/openssl/src/crypto.rs +++ b/openssl/src/crypto.rs @@ -1,59 +1,4 @@ -use libc::{c_char, c_int, c_void}; -use std::fmt; -use std::ffi::CStr; -use std::slice; -use std::ops::Deref; -use std::str; +use string::OpensslString; -pub struct CryptoString(&'static str); - -impl Drop for CryptoString { - fn drop(&mut self) { - unsafe { - CRYPTO_free(self.0.as_ptr() as *mut c_void, - concat!(file!(), "\0").as_ptr() as *const c_char, - line!() as c_int); - } - } -} - -impl Deref for CryptoString { - type Target = str; - - fn deref(&self) -> &str { - self.0 - } -} - -impl CryptoString { - pub unsafe fn from_raw_parts(buf: *mut u8, len: usize) -> CryptoString { - let slice = slice::from_raw_parts(buf, len); - CryptoString(str::from_utf8_unchecked(slice)) - } - - pub unsafe fn from_null_terminated(buf: *mut c_char) -> CryptoString { - let slice = CStr::from_ptr(buf).to_bytes(); - CryptoString(str::from_utf8_unchecked(slice)) - } -} - -impl fmt::Display for CryptoString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self.0, f) - } -} - -impl fmt::Debug for CryptoString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self.0, f) - } -} - -#[cfg(not(ossl110))] -#[allow(non_snake_case)] -unsafe fn CRYPTO_free(buf: *mut c_void, _: *const c_char, _: c_int) { - ::ffi::CRYPTO_free(buf); -} - -#[cfg(ossl110)] -use ffi::CRYPTO_free; +#[deprecated(note = "renamed to OpensslString", since = "0.9.7")] +pub type CryptoString = OpensslString; diff --git a/openssl/src/dh.rs b/openssl/src/dh.rs index 5a07e50f8df4d3a29db5f7ecae3be63bfa83d2f2..a7454150ee2ac1f3b845f7b66ec2858eea527d78 100644 --- a/openssl/src/dh.rs +++ b/openssl/src/dh.rs @@ -1,32 +1,30 @@ -use ffi; use error::ErrorStack; -use bio::MemBioSlice; -use std::ptr; +use ffi; +use foreign_types::ForeignTypeRef; use std::mem; +use std::ptr; -use {cvt, cvt_p}; -use bio::MemBio; +use {cvt, cvt_p, init}; use bn::BigNum; -use types::OpenSslTypeRef; -type_!(Dh, DhRef, ffi::DH, ffi::DH_free); +foreign_type! { + type CType = ffi::DH; + fn drop = ffi::DH_free; -impl DhRef { - /// Encodes the parameters to PEM. - pub fn to_pem(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); + pub struct Dh; - unsafe { - try!(cvt(ffi::PEM_write_bio_DHparams(mem_bio.as_ptr(), self.as_ptr()))); - } + pub struct DhRef; +} - Ok(mem_bio.get_buf().to_owned()) - } +impl DhRef { + to_pem!(ffi::PEM_write_bio_DHparams); + to_der!(ffi::i2d_DHparams); } impl Dh { pub fn from_params(p: BigNum, g: BigNum, q: BigNum) -> Result { unsafe { + init(); let dh = Dh(try!(cvt_p(ffi::DH_new()))); try!(cvt(compat::DH_set0_pqg(dh.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))); mem::forget((p, g, q)); @@ -34,16 +32,8 @@ impl Dh { } } - pub fn from_pem(buf: &[u8]) -> Result { - let mem_bio = try!(MemBioSlice::new(buf)); - unsafe { - cvt_p(ffi::PEM_read_bio_DHparams(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut())) - .map(Dh) - } - } + from_pem!(Dh, ffi::PEM_read_bio_DHparams); + from_der!(Dh, ffi::d2i_DHparams); /// Requires the `v102` or `v110` features and OpenSSL 1.0.2 or OpenSSL 1.1.0. #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] @@ -139,7 +129,15 @@ mod tests { fn test_dh_from_pem() { let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); let params = include_bytes!("../test/dhparams.pem"); - let dh = Dh::from_pem(params).ok().expect("Failed to load PEM"); + let dh = Dh::from_pem(params).unwrap(); ctx.set_tmp_dh(&dh).unwrap(); } + + #[test] + fn test_dh_from_der() { + let params = include_bytes!("../test/dhparams.pem"); + let dh = Dh::from_pem(params).unwrap(); + let der = dh.to_der().unwrap(); + Dh::from_der(&der).unwrap(); + } } diff --git a/openssl/src/dsa.rs b/openssl/src/dsa.rs index 9dd5669c5cd8de3f5dca94f4d308c04433d58861..60a1afdeb78bbf26b689138db64313ca92b61f8c 100644 --- a/openssl/src/dsa.rs +++ b/openssl/src/dsa.rs @@ -1,41 +1,31 @@ -use error::ErrorStack; use ffi; +use foreign_types::ForeignTypeRef; use libc::{c_int, c_char, c_void}; use std::fmt; use std::ptr; -use bio::{MemBio, MemBioSlice}; -use bn::BigNumRef; use {cvt, cvt_p}; -use types::OpenSslTypeRef; -use util::{CallbackState, invoke_passwd_cb}; +use bio::MemBioSlice; +use bn::BigNumRef; +use error::ErrorStack; +use util::{CallbackState, invoke_passwd_cb_old}; -type_!(Dsa, DsaRef, ffi::DSA, ffi::DSA_free); +foreign_type! { + type CType = ffi::DSA; + fn drop = ffi::DSA_free; -impl DsaRef { - /// Writes an DSA private key as unencrypted PEM formatted data - pub fn private_key_to_pem(&self) -> Result, ErrorStack> { - assert!(self.has_private_key()); - let mem_bio = try!(MemBio::new()); + pub struct Dsa; + pub struct DsaRef; +} - unsafe { - try!(cvt(ffi::PEM_write_bio_DSAPrivateKey(mem_bio.as_ptr(), self.as_ptr(), - ptr::null(), ptr::null_mut(), 0, - None, ptr::null_mut()))) - }; +impl DsaRef { + private_key_to_pem!(ffi::PEM_write_bio_DSAPrivateKey); + public_key_to_pem!(ffi::PEM_write_bio_DSA_PUBKEY); - Ok(mem_bio.get_buf().to_owned()) - } - - /// Writes an DSA public key as PEM formatted data - pub fn public_key_to_pem(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); - unsafe { - try!(cvt(ffi::PEM_write_bio_DSA_PUBKEY(mem_bio.as_ptr(), self.as_ptr()))); - } - Ok(mem_bio.get_buf().to_owned()) - } + private_key_to_der!(ffi::i2d_DSAPrivateKey); + public_key_to_der!(ffi::i2d_DSAPublicKey); + // FIXME should return u32 pub fn size(&self) -> Option { if self.q().is_some() { unsafe { Some(ffi::DSA_size(self.as_ptr()) as u32) } @@ -103,25 +93,12 @@ impl Dsa { } } - /// Reads a DSA private key from PEM formatted data. - pub fn private_key_from_pem(buf: &[u8]) -> Result { - ffi::init(); - let mem_bio = try!(MemBioSlice::new(buf)); + private_key_from_pem!(Dsa, ffi::PEM_read_bio_DSAPrivateKey); + private_key_from_der!(Dsa, ffi::d2i_DSAPrivateKey); + public_key_from_pem!(Dsa, ffi::PEM_read_bio_DSA_PUBKEY); + public_key_from_der!(Dsa, ffi::d2i_DSAPublicKey); - unsafe { - let dsa = try!(cvt_p(ffi::PEM_read_bio_DSAPrivateKey(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(Dsa(dsa)) - } - } - - /// Read a private key from PEM supplying a password callback to be invoked if the private key - /// is encrypted. - /// - /// The callback will be passed the password buffer and should return the number of characters - /// placed into the buffer. + #[deprecated(since = "0.9.2", note = "use private_key_from_pem_callback")] pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result where F: FnOnce(&mut [c_char]) -> usize { @@ -133,25 +110,11 @@ impl Dsa { let cb_ptr = &mut cb as *mut _ as *mut c_void; let dsa = try!(cvt_p(ffi::PEM_read_bio_DSAPrivateKey(mem_bio.as_ptr(), ptr::null_mut(), - Some(invoke_passwd_cb::), + Some(invoke_passwd_cb_old::), cb_ptr))); Ok(Dsa(dsa)) } } - - /// Reads an DSA public key from PEM formatted data. - pub fn public_key_from_pem(buf: &[u8]) -> Result { - ffi::init(); - - let mem_bio = try!(MemBioSlice::new(buf)); - unsafe { - let dsa = try!(cvt_p(ffi::PEM_read_bio_DSA_PUBKEY(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(Dsa(dsa)) - } - } } impl fmt::Debug for Dsa { @@ -193,7 +156,7 @@ mod compat { #[cfg(test)] mod test { - use libc::c_char; + use symm::Cipher; use super::*; @@ -204,17 +167,26 @@ mod test { #[test] pub fn test_password() { + let key = include_bytes!("../test/dsa-encrypted.pem"); + Dsa::private_key_from_pem_passphrase(key, b"mypass").unwrap(); + } + + #[test] + fn test_to_password() { + let key = Dsa::generate(2048).unwrap(); + let pem = key.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar").unwrap(); + Dsa::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(Dsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + + #[test] + pub fn test_password_callback() { let mut password_queried = false; let key = include_bytes!("../test/dsa-encrypted.pem"); - Dsa::private_key_from_pem_cb(key, |password| { + Dsa::private_key_from_pem_callback(key, |password| { password_queried = true; - password[0] = b'm' as c_char; - password[1] = b'y' as c_char; - password[2] = b'p' as c_char; - password[3] = b'a' as c_char; - password[4] = b's' as c_char; - password[5] = b's' as c_char; - 6 + password[..6].copy_from_slice(b"mypass"); + Ok(6) }) .unwrap(); diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs new file mode 100644 index 0000000000000000000000000000000000000000..378150212041b484de28848e7d846cfff65b4743 --- /dev/null +++ b/openssl/src/ec.rs @@ -0,0 +1,484 @@ +use ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use std::ptr; +use std::mem; +use libc::c_int; + +use {cvt, cvt_n, cvt_p, init}; +use bn::{BigNumRef, BigNumContextRef}; +use error::ErrorStack; +use nid::Nid; + +pub const POINT_CONVERSION_COMPRESSED: PointConversionForm = + PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_COMPRESSED); + +pub const POINT_CONVERSION_UNCOMPRESSED: PointConversionForm = + PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED); + +pub const POINT_CONVERSION_HYBRID: PointConversionForm = + PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_HYBRID); + +// OPENSSL_EC_EXPLICIT_CURVE, but that was only added in 1.1. +// Man page documents that 0 can be used in older versions. +pub const EXPLICIT_CURVE: Asn1Flag = Asn1Flag(0); +pub const NAMED_CURVE: Asn1Flag = Asn1Flag(ffi::OPENSSL_EC_NAMED_CURVE); + +#[derive(Copy, Clone)] +pub struct PointConversionForm(ffi::point_conversion_form_t); + +#[derive(Copy, Clone)] +pub struct Asn1Flag(c_int); + +foreign_type! { + type CType = ffi::EC_GROUP; + fn drop = ffi::EC_GROUP_free; + + pub struct EcGroup; + pub struct EcGroupRef; +} + +impl EcGroup { + /// Returns the group of a standard named curve. + pub fn from_curve_name(nid: Nid) -> Result { + unsafe { + init(); + cvt_p(ffi::EC_GROUP_new_by_curve_name(nid.as_raw())).map(EcGroup) + } + } +} + +impl EcGroupRef { + /// Places the components of a curve over a prime field in the provided `BigNum`s. + pub fn components_gfp(&self, + p: &mut BigNumRef, + a: &mut BigNumRef, + b: &mut BigNumRef, + ctx: &mut BigNumContextRef) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_curve_GFp(self.as_ptr(), + p.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr())) + .map(|_| ()) + } + } + + /// Places the components of a curve over a binary field in the provided `BigNum`s. + pub fn components_gf2m(&self, + p: &mut BigNumRef, + a: &mut BigNumRef, + b: &mut BigNumRef, + ctx: &mut BigNumContextRef) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_curve_GF2m(self.as_ptr(), + p.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr())) + .map(|_| ()) + } + } + + /// Returns the degree of the curve. + pub fn degree(&self) -> u32 { + unsafe { ffi::EC_GROUP_get_degree(self.as_ptr()) as u32 } + } + + /// Places the order of the curve in the provided `BigNum`. + pub fn order(&self, + order: &mut BigNumRef, + ctx: &mut BigNumContextRef) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_order(self.as_ptr(), order.as_ptr(), ctx.as_ptr())).map(|_| ()) + } + } + + /// Sets the flag determining if the group corresponds to a named curve or must be explicitly + /// parameterized. + /// + /// This defaults to `EXPLICIT_CURVE` in OpenSSL 1.0.1 and 1.0.2, but `NAMED_CURVE` in OpenSSL + /// 1.1.0. + pub fn set_asn1_flag(&mut self, flag: Asn1Flag) { + unsafe { + ffi::EC_GROUP_set_asn1_flag(self.as_ptr(), flag.0); + } + } +} + +foreign_type! { + type CType = ffi::EC_POINT; + fn drop = ffi::EC_POINT_free; + + pub struct EcPoint; + pub struct EcPointRef; +} + +impl EcPointRef { + /// Computes `a + b`, storing the result in `self`. + pub fn add(&mut self, + group: &EcGroupRef, + a: &EcPointRef, + b: &EcPointRef, + ctx: &mut BigNumContextRef) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_add(group.as_ptr(), + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr())) + .map(|_| ()) + } + } + + /// Computes `q * m`, storing the result in `self`. + pub fn mul(&mut self, + group: &EcGroupRef, + q: &EcPointRef, + m: &BigNumRef, + ctx: &BigNumContextRef) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_mul(group.as_ptr(), + self.as_ptr(), + ptr::null(), + q.as_ptr(), + m.as_ptr(), + ctx.as_ptr())) + .map(|_| ()) + } + } + + /// Computes `generator * n`, storing the result ing `self`. + pub fn mul_generator(&mut self, + group: &EcGroupRef, + n: &BigNumRef, + ctx: &BigNumContextRef) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_mul(group.as_ptr(), + self.as_ptr(), + n.as_ptr(), + ptr::null(), + ptr::null(), + ctx.as_ptr())) + .map(|_| ()) + } + } + + /// Computes `generator * n + q * m`, storing the result in `self`. + pub fn mul_full(&mut self, + group: &EcGroupRef, + n: &BigNumRef, + q: &EcPointRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_mul(group.as_ptr(), + self.as_ptr(), + n.as_ptr(), + q.as_ptr(), + m.as_ptr(), + ctx.as_ptr())) + .map(|_| ()) + } + } + + /// Inverts `self`. + pub fn invert(&mut self, group: &EcGroupRef, ctx: &BigNumContextRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_invert(group.as_ptr(), self.as_ptr(), ctx.as_ptr())).map(|_| ()) + } + } + + /// Serializes the point to a binary representation. + pub fn to_bytes(&self, + group: &EcGroupRef, + form: PointConversionForm, + ctx: &mut BigNumContextRef) + -> Result, ErrorStack> { + unsafe { + let len = ffi::EC_POINT_point2oct(group.as_ptr(), + self.as_ptr(), + form.0, + ptr::null_mut(), + 0, + ctx.as_ptr()); + if len == 0 { + return Err(ErrorStack::get()); + } + let mut buf = vec![0; len]; + let len = ffi::EC_POINT_point2oct(group.as_ptr(), + self.as_ptr(), + form.0, + buf.as_mut_ptr(), + len, + ctx.as_ptr()); + if len == 0 { + Err(ErrorStack::get()) + } else { + Ok(buf) + } + } + } + + /// Determines if this point is equal to another. + pub fn eq(&self, + group: &EcGroupRef, + other: &EcPointRef, + ctx: &mut BigNumContextRef) + -> Result { + unsafe { + let res = try!(cvt_n(ffi::EC_POINT_cmp(group.as_ptr(), + self.as_ptr(), + other.as_ptr(), + ctx.as_ptr()))); + Ok(res == 0) + } + } +} + +impl EcPoint { + /// Creates a new point on the specified curve. + pub fn new(group: &EcGroupRef) -> Result { + unsafe { cvt_p(ffi::EC_POINT_new(group.as_ptr())).map(EcPoint) } + } + + pub fn from_bytes(group: &EcGroupRef, + buf: &[u8], + ctx: &mut BigNumContextRef) + -> Result { + let point = try!(EcPoint::new(group)); + unsafe { + try!(cvt(ffi::EC_POINT_oct2point(group.as_ptr(), + point.as_ptr(), + buf.as_ptr(), + buf.len(), + ctx.as_ptr()))); + } + Ok(point) + } +} + +foreign_type! { + type CType = ffi::EC_KEY; + fn drop = ffi::EC_KEY_free; + + pub struct EcKey; + pub struct EcKeyRef; +} + +impl EcKeyRef { + private_key_to_pem!(ffi::PEM_write_bio_ECPrivateKey); + private_key_to_der!(ffi::i2d_ECPrivateKey); + + pub fn group(&self) -> Option<&EcGroupRef> { + unsafe { + let ptr = ffi::EC_KEY_get0_group(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(EcGroupRef::from_ptr(ptr as *mut _)) + } + } + } + + pub fn public_key(&self) -> Option<&EcPointRef> { + unsafe { + let ptr = ffi::EC_KEY_get0_public_key(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(EcPointRef::from_ptr(ptr as *mut _)) + } + } + } + + pub fn private_key(&self) -> Option<&BigNumRef> { + unsafe { + let ptr = ffi::EC_KEY_get0_private_key(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(BigNumRef::from_ptr(ptr as *mut _)) + } + } + } + + /// Checks the key for validity. + pub fn check_key(&self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())).map(|_| ()) } + } +} + +impl EcKey { + /// Constructs an `EcKey` corresponding to a known curve. + /// + /// It will not have an associated public or private key. This kind of key is primarily useful + /// to be provided to the `set_tmp_ecdh` methods on `Ssl` and `SslContextBuilder`. + pub fn from_curve_name(nid: Nid) -> Result { + unsafe { + init(); + cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(EcKey) + } + } + + /// Constructs an `EcKey` from the specified group with the associated `EcPoint`, public_key. + /// + /// This will only have the associated public_key. + /// + /// # Example + /// + /// ```no_run + /// use openssl::bn::BigNumContext; + /// use openssl::ec::*; + /// use openssl::nid; + /// use openssl::pkey::PKey; + /// + /// // get bytes from somewhere, i.e. this will not produce a valid key + /// let public_key: Vec = vec![]; + /// + /// // create an EcKey from the binary form of a EcPoint + /// let group = EcGroup::from_curve_name(nid::SECP256K1).unwrap(); + /// let mut ctx = BigNumContext::new().unwrap(); + /// let point = EcPoint::from_bytes(&group, &public_key, &mut ctx).unwrap(); + /// let key = EcKey::from_public_key(&group, &point); + /// ``` + pub fn from_public_key(group: &EcGroupRef, public_key: &EcPointRef) -> Result { + let mut builder = try!(EcKeyBuilder::new()); + try!(builder.set_group(group)); + try!(builder.set_public_key(public_key)); + Ok(builder.build()) + } + + /// Generates a new public/private key pair on the specified curve. + pub fn generate(group: &EcGroupRef) -> Result { + let mut builder = try!(EcKeyBuilder::new()); + try!(builder.set_group(group)); + try!(builder.generate_key()); + Ok(builder.build()) + } + + #[deprecated(since = "0.9.2", note = "use from_curve_name")] + pub fn new_by_curve_name(nid: Nid) -> Result { + EcKey::from_curve_name(nid) + } + + private_key_from_pem!(EcKey, ffi::PEM_read_bio_ECPrivateKey); + private_key_from_der!(EcKey, ffi::d2i_ECPrivateKey); +} + + +foreign_type! { + type CType = ffi::EC_KEY; + fn drop = ffi::EC_KEY_free; + + pub struct EcKeyBuilder; + pub struct EcKeyBuilderRef; +} + +impl EcKeyBuilder { + pub fn new() -> Result { + unsafe { + init(); + cvt_p(ffi::EC_KEY_new()).map(EcKeyBuilder) + } + } + + pub fn build(self) -> EcKey { + unsafe { + let key = EcKey::from_ptr(self.as_ptr()); + mem::forget(self); + key + } + } +} + +impl EcKeyBuilderRef { + pub fn set_group(&mut self, group: &EcGroupRef) -> Result<&mut EcKeyBuilderRef, ErrorStack> { + unsafe { + cvt(ffi::EC_KEY_set_group(self.as_ptr(), group.as_ptr())).map(|_| self) + } + } + + pub fn set_public_key(&mut self, + public_key: &EcPointRef) + -> Result<&mut EcKeyBuilderRef, ErrorStack> { + unsafe { + cvt(ffi::EC_KEY_set_public_key(self.as_ptr(), public_key.as_ptr())).map(|_| self) + } + } + + pub fn generate_key(&mut self) -> Result<&mut EcKeyBuilderRef, ErrorStack> { + unsafe { + cvt(ffi::EC_KEY_generate_key(self.as_ptr())).map(|_| self) + } + } +} + +#[cfg(test)] +mod test { + use bn::BigNumContext; + use nid; + use super::*; + + #[test] + fn key_new_by_curve_name() { + EcKey::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + } + + #[test] + fn generate() { + let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + key.public_key().unwrap(); + key.private_key().unwrap(); + } + + #[test] + fn point_new() { + let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + EcPoint::new(&group).unwrap(); + } + + #[test] + fn point_bytes() { + let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let point = key.public_key().unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let bytes = point.to_bytes(&group, POINT_CONVERSION_COMPRESSED, &mut ctx).unwrap(); + let point2 = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap(); + assert!(point.eq(&group, &point2, &mut ctx).unwrap()); + } + + #[test] + fn mul_generator() { + let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let mut public_key = EcPoint::new(&group).unwrap(); + public_key.mul_generator(&group, key.private_key().unwrap(), &mut ctx).unwrap(); + assert!(public_key.eq(&group, key.public_key().unwrap(), &mut ctx).unwrap()); + } + + #[test] + fn key_from_public_key() { + let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let bytes = key.public_key().unwrap().to_bytes(&group, POINT_CONVERSION_COMPRESSED, &mut ctx).unwrap(); + + drop(key); + let public_key = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap(); + let ec_key = EcKey::from_public_key(&group, &public_key).unwrap(); + assert!(ec_key.check_key().is_ok()); + assert!(ec_key.public_key().is_some()); + assert!(ec_key.private_key().is_none()); + } +} diff --git a/openssl/src/ec_key.rs b/openssl/src/ec_key.rs index 41501c14992a65875465c9d9d767600a9f14f7bc..cb7c49960e02d27ab738f930d2c988b34db3a33f 100644 --- a/openssl/src/ec_key.rs +++ b/openssl/src/ec_key.rs @@ -1,24 +1,3 @@ -use ffi; +#![deprecated(since = "0.9.2", note = "renamed to `ec`")] -use cvt_p; -use error::ErrorStack; -use nid::Nid; - -type_!(EcKey, EcKeyRef, ffi::EC_KEY, ffi::EC_KEY_free); - -impl EcKey { - pub fn new_by_curve_name(nid: Nid) -> Result { - unsafe { cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(EcKey) } - } -} - -#[cfg(test)] -mod test { - use nid; - use super::*; - - #[test] - fn new_by_curve_name() { - EcKey::new_by_curve_name(nid::X9_62_PRIME256V1).unwrap(); - } -} +pub use ec::{EcKey, EcKeyRef}; diff --git a/openssl/src/error.rs b/openssl/src/error.rs index 4dd219af64406217f2f381deda5799124cfc69c0..26b964085f225c3d272a25b47c9bff93b336874b 100644 --- a/openssl/src/error.rs +++ b/openssl/src/error.rs @@ -32,11 +32,11 @@ impl fmt::Display for ErrorStack { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let mut first = true; for err in &self.0 { - if first { + if !first { try!(fmt.write_str(", ")); - first = false; } try!(write!(fmt, "{}", err)); + first = false; } Ok(()) } diff --git a/openssl/src/hash.rs b/openssl/src/hash.rs index 6a13371dd7b0fa739d80ea52b0ddb94b86e21c7e..47d6dc7a070e6ea2bb3195e467b8fb334e8a8d75 100644 --- a/openssl/src/hash.rs +++ b/openssl/src/hash.rs @@ -60,7 +60,7 @@ use self::State::*; /// /// # Examples /// -/// Calculate a hash in one go. +/// Calculate a hash in one go: /// /// ``` /// use openssl::hash::{hash, MessageDigest}; @@ -71,7 +71,7 @@ use self::State::*; /// assert_eq!(res, spec); /// ``` /// -/// Use the `Write` trait to supply the input in chunks. +/// Supply the input in chunks: /// /// ``` /// use openssl::hash::{Hasher, MessageDigest}; @@ -208,17 +208,17 @@ pub fn hash(t: MessageDigest, data: &[u8]) -> Result, ErrorStack> { #[cfg(test)] mod tests { - use serialize::hex::{FromHex, ToHex}; + use hex::{FromHex, ToHex}; use super::{hash, Hasher, MessageDigest}; use std::io::prelude::*; fn hash_test(hashtype: MessageDigest, hashtest: &(&str, &str)) { - let res = hash(hashtype, &*hashtest.0.from_hex().unwrap()).unwrap(); + let res = hash(hashtype, &Vec::from_hex(hashtest.0).unwrap()).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()).unwrap(); + let _ = h.write_all(&Vec::from_hex(hashtest.0).unwrap()).unwrap(); let res = h.finish().unwrap(); assert_eq!(res.to_hex(), hashtest.1); } @@ -258,7 +258,7 @@ mod tests { #[test] fn test_finish_twice() { let mut h = Hasher::new(MessageDigest::md5()).unwrap(); - h.write_all(&*md5_tests[6].0.from_hex().unwrap()).unwrap(); + h.write_all(&Vec::from_hex(md5_tests[6].0).unwrap()).unwrap(); h.finish().unwrap(); let res = h.finish().unwrap(); let null = hash(MessageDigest::md5(), &[]).unwrap(); @@ -268,7 +268,7 @@ mod tests { #[test] fn test_clone() { let i = 7; - let inp = md5_tests[i].0.from_hex().unwrap(); + let inp = Vec::from_hex(md5_tests[i].0).unwrap(); assert!(inp.len() > 2); let p = inp.len() / 2; let h0 = Hasher::new(MessageDigest::md5()).unwrap(); @@ -289,7 +289,7 @@ mod tests { println!("Clone a finished hasher"); let mut h3 = h1.clone(); - h3.write_all(&*md5_tests[i + 1].0.from_hex().unwrap()).unwrap(); + h3.write_all(&Vec::from_hex(md5_tests[i + 1].0).unwrap()).unwrap(); let res = h3.finish().unwrap(); assert_eq!(res.to_hex(), md5_tests[i + 1].1); } diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 5750b410421a4eca4fd05e12167d7f833969025d..86de35d0cc630905d77dbc84f293b6dd17c8f9fe 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -1,15 +1,16 @@ -#![doc(html_root_url="https://sfackler.github.io/rust-openssl/doc/v0.9.0")] +#![doc(html_root_url="https://docs.rs/openssl/0.9.6")] #[macro_use] extern crate bitflags; +#[macro_use] +extern crate foreign_types; extern crate libc; #[macro_use] extern crate lazy_static; extern crate openssl_sys as ffi; #[cfg(test)] -extern crate rustc_serialize as serialize; - +extern crate hex; #[cfg(test)] extern crate tempdir; @@ -20,76 +21,38 @@ use libc::c_int; use error::ErrorStack; -macro_rules! type_ { - ($n:ident, $r:ident, $c:path, $d:path) => { - pub struct $n(*mut $c); - - impl ::types::OpenSslType for $n { - type CType = $c; - type Ref = $r; - - unsafe fn from_ptr(ptr: *mut $c) -> $n { - $n(ptr) - } - - fn as_ptr(&self) -> *mut $c { - self.0 - } - } - - impl Drop for $n { - fn drop(&mut self) { - unsafe { $d(self.0) } - } - } - - impl ::std::ops::Deref for $n { - type Target = $r; - - fn deref(&self) -> &$r { - unsafe { ::types::OpenSslTypeRef::from_ptr(self.0) } - } - } - - impl ::std::ops::DerefMut for $n { - fn deref_mut(&mut self) -> &mut $r { - unsafe { ::types::OpenSslTypeRef::from_ptr_mut(self.0) } - } - } - - pub struct $r(::util::Opaque); - - impl ::types::OpenSslTypeRef for $r { - type CType = $c; - } - } -} +#[macro_use] +mod macros; mod bio; mod util; +pub mod aes; pub mod asn1; pub mod bn; pub mod conf; pub mod crypto; pub mod dh; pub mod dsa; +pub mod ec; pub mod ec_key; pub mod error; pub mod hash; pub mod memcmp; pub mod nid; +pub mod ocsp; pub mod pkcs12; pub mod pkcs5; pub mod pkey; pub mod rand; -pub mod types; pub mod rsa; pub mod sign; pub mod ssl; +pub mod stack; +pub mod string; pub mod symm; +pub mod types; pub mod version; pub mod x509; -pub mod stack; #[cfg(any(ossl102, ossl110))] mod verify; diff --git a/openssl/src/macros.rs b/openssl/src/macros.rs new file mode 100644 index 0000000000000000000000000000000000000000..b2fe0c18a9e4715ae906b13fb934f5279c7de485 --- /dev/null +++ b/openssl/src/macros.rs @@ -0,0 +1,216 @@ + +macro_rules! private_key_from_pem { + ($t:ident, $f:path) => { + from_pem_inner!(/// Deserializes a PEM-formatted private key. + private_key_from_pem, $t, $f); + + /// Deserializes a PEM-formatted private key, using the supplied password if the key is + /// encrypted. + /// + /// # Panics + /// + /// Panics if `passphrase` contains an embedded null. + pub fn private_key_from_pem_passphrase(pem: &[u8], + passphrase: &[u8]) + -> Result<$t, ::error::ErrorStack> { + unsafe { + ffi::init(); + let bio = try!(::bio::MemBioSlice::new(pem)); + let passphrase = ::std::ffi::CString::new(passphrase).unwrap(); + cvt_p($f(bio.as_ptr(), + ptr::null_mut(), + None, + passphrase.as_ptr() as *const _ as *mut _)) + .map($t) + } + } + + /// Deserializes a PEM-formatted private key, using a callback to retrieve a password if the + /// key is encrypted. + /// + /// The callback should copy the password into the provided buffer and return the number of + /// bytes written. + pub fn private_key_from_pem_callback(pem: &[u8], + callback: F) + -> Result<$t, ::error::ErrorStack> + where F: FnOnce(&mut [u8]) -> Result + { + unsafe { + ffi::init(); + let mut cb = ::util::CallbackState::new(callback); + let bio = try!(::bio::MemBioSlice::new(pem)); + cvt_p($f(bio.as_ptr(), + ptr::null_mut(), + Some(::util::invoke_passwd_cb::), + &mut cb as *mut _ as *mut _)) + .map($t) + } + } + } +} + +macro_rules! private_key_to_pem { + ($f:path) => { + /// Serializes the private key to PEM. + pub fn private_key_to_pem(&self) -> Result, ::error::ErrorStack> { + unsafe { + let bio = try!(::bio::MemBio::new()); + try!(cvt($f(bio.as_ptr(), + self.as_ptr(), + ptr::null(), + ptr::null_mut(), + -1, + None, + ptr::null_mut()))); + Ok(bio.get_buf().to_owned()) + } + } + + /// Serializes the private key to PEM, encrypting it with the specified symmetric cipher and + /// passphrase. + pub fn private_key_to_pem_passphrase(&self, + cipher: ::symm::Cipher, + passphrase: &[u8]) + -> Result, ::error::ErrorStack> { + unsafe { + let bio = try!(::bio::MemBio::new()); + assert!(passphrase.len() <= ::libc::c_int::max_value() as usize); + try!(cvt($f(bio.as_ptr(), + self.as_ptr(), + cipher.as_ptr(), + passphrase.as_ptr() as *const _ as *mut _, + passphrase.len() as ::libc::c_int, + None, + ptr::null_mut()))); + Ok(bio.get_buf().to_owned()) + } + } + } +} + +macro_rules! to_pem_inner { + (#[$m:meta] $n:ident, $f:path) => { + #[$m] + pub fn $n(&self) -> Result, ::error::ErrorStack> { + unsafe { + let bio = try!(::bio::MemBio::new()); + try!(cvt($f(bio.as_ptr(), self.as_ptr()))); + Ok(bio.get_buf().to_owned()) + } + } + } +} + +macro_rules! public_key_to_pem { + ($f:path) => { + to_pem_inner!(/// Serializes a public key to PEM. + public_key_to_pem, $f); + } +} + +macro_rules! to_pem { + ($f:path) => { + to_pem_inner!(/// Serializes this value to PEM. + to_pem, $f); + } +} + +macro_rules! to_der_inner { + (#[$m:meta] $n:ident, $f:path) => { + #[$m] + pub fn $n(&self) -> Result, ::error::ErrorStack> { + unsafe { + let len = try!(::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self), + ptr::null_mut()))); + let mut buf = vec![0; len as usize]; + try!(::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self), + &mut buf.as_mut_ptr()))); + Ok(buf) + } + } + }; +} + +macro_rules! to_der { + ($f:path) => { + to_der_inner!(/// Serializes this value to DER. + to_der, $f); + } +} + +macro_rules! private_key_to_der { + ($f:path) => { + to_der_inner!(/// Serializes the private key to DER. + private_key_to_der, $f); + } +} + +macro_rules! public_key_to_der { + ($f:path) => { + to_der_inner!(/// Serializes the public key to DER. + public_key_to_der, $f); + } +} + +macro_rules! from_der_inner { + (#[$m:meta] $n:ident, $t:ident, $f:path) => { + #[$m] + pub fn $n(der: &[u8]) -> Result<$t, ::error::ErrorStack> { + unsafe { + ::ffi::init(); + let len = ::std::cmp::min(der.len(), ::libc::c_long::max_value() as usize) as ::libc::c_long; + ::cvt_p($f(::std::ptr::null_mut(), &mut der.as_ptr(), len)) + .map($t) + } + } + } +} + +macro_rules! from_der { + ($t:ident, $f:path) => { + from_der_inner!(/// Deserializes a value from DER-formatted data. + from_der, $t, $f); + } +} + +macro_rules! private_key_from_der { + ($t:ident, $f:path) => { + from_der_inner!(/// Deserializes a private key from DER-formatted data. + private_key_from_der, $t, $f); + } +} + +macro_rules! public_key_from_der { + ($t:ident, $f:path) => { + from_der_inner!(/// Deserializes a public key from DER-formatted data. + public_key_from_der, $t, $f); + } +} + +macro_rules! from_pem_inner { + (#[$m:meta] $n:ident, $t:ident, $f:path) => { + #[$m] + pub fn $n(pem: &[u8]) -> Result<$t, ::error::ErrorStack> { + unsafe { + ::init(); + let bio = try!(::bio::MemBioSlice::new(pem)); + cvt_p($f(bio.as_ptr(), ::std::ptr::null_mut(), None, ::std::ptr::null_mut())) + .map($t) + } + } + } +} + +macro_rules! public_key_from_pem { + ($t:ident, $f:path) => { + from_pem_inner!(/// Deserializes a public key from PEM-formatted data. + public_key_from_pem, $t, $f); + } +} + +macro_rules! from_pem { + ($t:ident, $f:path) => { + from_pem_inner!(/// Deserializes a value from PEM-formatted data. + from_pem, $t, $f); + } +} diff --git a/openssl/src/ocsp.rs b/openssl/src/ocsp.rs new file mode 100644 index 0000000000000000000000000000000000000000..67d518384970e878e85ac48c37c73023493e143f --- /dev/null +++ b/openssl/src/ocsp.rs @@ -0,0 +1,304 @@ +use ffi; +use foreign_types::ForeignTypeRef; +use libc::{c_int, c_long, c_ulong}; +use std::ptr; +use std::mem; + +use {cvt, cvt_p}; +use asn1::Asn1GeneralizedTimeRef; +use error::ErrorStack; +use hash::MessageDigest; +use stack::StackRef; +use x509::store::X509StoreRef; +use x509::{X509, X509Ref}; + +bitflags! { + pub flags Flag: c_ulong { + const FLAG_NO_CERTS = ffi::OCSP_NOCERTS, + const FLAG_NO_INTERN = ffi::OCSP_NOINTERN, + const FLAG_NO_CHAIN = ffi::OCSP_NOCHAIN, + const FLAG_NO_VERIFY = ffi::OCSP_NOVERIFY, + const FLAG_NO_EXPLICIT = ffi::OCSP_NOEXPLICIT, + const FLAG_NO_CA_SIGN = ffi::OCSP_NOCASIGN, + const FLAG_NO_DELEGATED = ffi::OCSP_NODELEGATED, + const FLAG_NO_CHECKS = ffi::OCSP_NOCHECKS, + const FLAG_TRUST_OTHER = ffi::OCSP_TRUSTOTHER, + const FLAG_RESPID_KEY = ffi::OCSP_RESPID_KEY, + const FLAG_NO_TIME = ffi::OCSP_NOTIME, + } +} + +pub const RESPONSE_STATUS_SUCCESSFUL: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SUCCESSFUL); +pub const RESPONSE_STATUS_MALFORMED_REQUEST: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_MALFORMEDREQUEST); +pub const RESPONSE_STATUS_INTERNAL_ERROR: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_INTERNALERROR); +pub const RESPONSE_STATUS_TRY_LATER: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_TRYLATER); +pub const RESPONSE_STATUS_SIG_REQUIRED: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SIGREQUIRED); +pub const RESPONSE_STATUS_UNAUTHORIZED: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_UNAUTHORIZED); + +pub const CERT_STATUS_GOOD: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_GOOD); +pub const CERT_STATUS_REVOKED: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_REVOKED); +pub const CERT_STATUS_UNKNOWN: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_UNKNOWN); + +pub const REVOKED_STATUS_NO_STATUS: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_NOSTATUS); +pub const REVOKED_STATUS_UNSPECIFIED: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_UNSPECIFIED); +pub const REVOKED_STATUS_KEY_COMPROMISE: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_KEYCOMPROMISE); +pub const REVOKED_STATUS_CA_COMPROMISE: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CACOMPROMISE); +pub const REVOKED_STATUS_AFFILIATION_CHANGED: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_AFFILIATIONCHANGED); +pub const REVOKED_STATUS_SUPERSEDED: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_SUPERSEDED); +pub const REVOKED_STATUS_CESSATION_OF_OPERATION: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CESSATIONOFOPERATION); +pub const REVOKED_STATUS_CERTIFICATE_HOLD: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CERTIFICATEHOLD); +pub const REVOKED_STATUS_REMOVE_FROM_CRL: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_REMOVEFROMCRL); + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspResponseStatus(c_int); + +impl OcspResponseStatus { + pub fn from_raw(raw: c_int) -> OcspResponseStatus { + OcspResponseStatus(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspCertStatus(c_int); + +impl OcspCertStatus { + pub fn from_raw(raw: c_int) -> OcspCertStatus { + OcspCertStatus(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspRevokedStatus(c_int); + +impl OcspRevokedStatus { + pub fn from_raw(raw: c_int) -> OcspRevokedStatus { + OcspRevokedStatus(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +pub struct Status<'a> { + /// The overall status of the response. + pub status: OcspCertStatus, + /// If `status` is `CERT_STATUS_REVOKED`, the reason for the revocation. + pub reason: OcspRevokedStatus, + /// If `status` is `CERT_STATUS_REVOKED`, the time at which the certificate was revoked. + pub revocation_time: Option<&'a Asn1GeneralizedTimeRef>, + /// The time that this revocation check was performed. + pub this_update: &'a Asn1GeneralizedTimeRef, + /// The time at which this revocation check expires. + pub next_update: &'a Asn1GeneralizedTimeRef, +} + +impl<'a> Status<'a> { + /// Checks validity of the `this_update` and `next_update` fields. + /// + /// The `nsec` parameter specifies an amount of slack time that will be used when comparing + /// those times with the current time to account for delays and clock skew. + /// + /// The `maxsec` parameter limits the maximum age of the `this_update` parameter to prohibit + /// very old responses. + pub fn check_validity(&self, nsec: u32, maxsec: Option) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OCSP_check_validity(self.this_update.as_ptr(), + self.next_update.as_ptr(), + nsec as c_long, + maxsec.map(|n| n as c_long).unwrap_or(-1))) + .map(|_| ()) + } + } +} + +foreign_type! { + type CType = ffi::OCSP_BASICRESP; + fn drop = ffi::OCSP_BASICRESP_free; + + pub struct OcspBasicResponse; + pub struct OcspBasicResponseRef; +} + +impl OcspBasicResponseRef { + /// Verifies the validity of the response. + /// + /// The `certs` parameter contains a set of certificates that will be searched when locating the + /// OCSP response signing certificate. Some responders do not include this in the response. + pub fn verify(&self, + certs: &StackRef, + store: &X509StoreRef, + flags: Flag) + -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OCSP_basic_verify(self.as_ptr(), certs.as_ptr(), store.as_ptr(), flags.bits())) + .map(|_| ()) + } + } + + /// Looks up the status for the specified certificate ID. + pub fn find_status<'a>(&'a self, id: &OcspCertIdRef) -> Option> { + unsafe { + let mut status = ffi::V_OCSP_CERTSTATUS_UNKNOWN; + let mut reason = ffi::OCSP_REVOKED_STATUS_NOSTATUS; + let mut revocation_time = ptr::null_mut(); + let mut this_update = ptr::null_mut(); + let mut next_update = ptr::null_mut(); + + let r = ffi::OCSP_resp_find_status(self.as_ptr(), + id.as_ptr(), + &mut status, + &mut reason, + &mut revocation_time, + &mut this_update, + &mut next_update); + if r == 1 { + let revocation_time = if revocation_time.is_null() { + None + } else { + Some(Asn1GeneralizedTimeRef::from_ptr(revocation_time)) + }; + Some(Status { + status: OcspCertStatus(status), + reason: OcspRevokedStatus(status), + revocation_time: revocation_time, + this_update: Asn1GeneralizedTimeRef::from_ptr(this_update), + next_update: Asn1GeneralizedTimeRef::from_ptr(next_update), + }) + } else { + None + } + } + } +} + +foreign_type! { + type CType = ffi::OCSP_CERTID; + fn drop = ffi::OCSP_CERTID_free; + + pub struct OcspCertId; + pub struct OcspCertIdRef; +} + +impl OcspCertId { + /// Constructs a certificate ID for certificate `subject`. + pub fn from_cert(digest: MessageDigest, + subject: &X509Ref, + issuer: &X509Ref) + -> Result { + unsafe { + cvt_p(ffi::OCSP_cert_to_id(digest.as_ptr(), subject.as_ptr(), issuer.as_ptr())) + .map(OcspCertId) + } + } +} + +foreign_type! { + type CType = ffi::OCSP_RESPONSE; + fn drop = ffi::OCSP_RESPONSE_free; + + pub struct OcspResponse; + pub struct OcspResponseRef; +} + +impl OcspResponse { + /// Creates an OCSP response from the status and optional body. + /// + /// A body should only be provided if `status` is `RESPONSE_STATUS_SUCCESSFUL`. + pub fn create(status: OcspResponseStatus, + body: Option<&OcspBasicResponseRef>) + -> Result { + unsafe { + ffi::init(); + + cvt_p(ffi::OCSP_response_create(status.as_raw(), + body.map(|r| r.as_ptr()).unwrap_or(ptr::null_mut()))) + .map(OcspResponse) + } + } + + from_der!(OcspResponse, ffi::d2i_OCSP_RESPONSE); +} + +impl OcspResponseRef { + to_der!(ffi::i2d_OCSP_RESPONSE); + + /// Returns the status of the response. + pub fn status(&self) -> OcspResponseStatus { + unsafe { + OcspResponseStatus(ffi::OCSP_response_status(self.as_ptr())) + } + } + + /// Returns the basic response. + /// + /// This will only succeed if `status()` returns `RESPONSE_STATUS_SUCCESSFUL`. + pub fn basic(&self) -> Result { + unsafe { + cvt_p(ffi::OCSP_response_get1_basic(self.as_ptr())).map(OcspBasicResponse) + } + } +} + +foreign_type! { + type CType = ffi::OCSP_REQUEST; + fn drop = ffi::OCSP_REQUEST_free; + + pub struct OcspRequest; + pub struct OcspRequestRef; +} + +impl OcspRequest { + pub fn new() -> Result { + unsafe { + ffi::init(); + + cvt_p(ffi::OCSP_REQUEST_new()).map(OcspRequest) + } + } + + from_der!(OcspRequest, ffi::d2i_OCSP_REQUEST); +} + +impl OcspRequestRef { + to_der!(ffi::i2d_OCSP_REQUEST); + + pub fn add_id(&mut self, id: OcspCertId) -> Result<&mut OcspOneReqRef, ErrorStack> { + unsafe { + let ptr = try!(cvt_p(ffi::OCSP_request_add0_id(self.as_ptr(), id.as_ptr()))); + mem::forget(id); + Ok(OcspOneReqRef::from_ptr_mut(ptr)) + } + } +} + +foreign_type! { + type CType = ffi::OCSP_ONEREQ; + fn drop = ffi::OCSP_ONEREQ_free; + + pub struct OcspOneReq; + pub struct OcspOneReqRef; +} diff --git a/openssl/src/pkcs12.rs b/openssl/src/pkcs12.rs index 1ef0bf3f56ffe180a7d46c479cc571fb7efc722b..9f014af61852d5bd04e5eec603c479172cdda409 100644 --- a/openssl/src/pkcs12.rs +++ b/openssl/src/pkcs12.rs @@ -1,35 +1,31 @@ //! PKCS #12 archives. use ffi; -use libc::{c_long, c_uchar}; -use std::cmp; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; use std::ptr; use std::ffi::CString; use {cvt, cvt_p}; -use pkey::PKey; +use pkey::{PKey, PKeyRef}; use error::ErrorStack; use x509::X509; -use types::{OpenSslType, OpenSslTypeRef}; use stack::Stack; +use nid; -type_!(Pkcs12, Pkcs12Ref, ffi::PKCS12, ffi::PKCS12_free); +foreign_type! { + type CType = ffi::PKCS12; + fn drop = ffi::PKCS12_free; -impl Pkcs12 { - /// Deserializes a `Pkcs12` structure from DER-encoded data. - pub fn from_der(der: &[u8]) -> Result { - unsafe { - ffi::init(); - let mut ptr = der.as_ptr() as *const c_uchar; - let length = cmp::min(der.len(), c_long::max_value() as usize) as c_long; - let p12 = try!(cvt_p(ffi::d2i_PKCS12(ptr::null_mut(), &mut ptr, length))); - Ok(Pkcs12(p12)) - } - } + pub struct Pkcs12; + pub struct Pkcs12Ref; } impl Pkcs12Ref { + to_der!(ffi::i2d_PKCS12); + /// Extracts the contents of the `Pkcs12`. + // FIXME should take an &[u8] pub fn parse(&self, pass: &str) -> Result { unsafe { let pass = CString::new(pass).unwrap(); @@ -57,16 +53,132 @@ impl Pkcs12Ref { } } +impl Pkcs12 { + from_der!(Pkcs12, ffi::d2i_PKCS12); + + /// Creates a new builder for a protected pkcs12 certificate. + /// + /// This uses the defaults from the OpenSSL library: + /// + /// * `nid_key` - `nid::PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC` + /// * `nid_cert` - `nid::PBE_WITHSHA1AND40BITRC2_CBC` + /// * `iter` - `2048` + /// * `mac_iter` - `2048` + pub fn builder() -> Pkcs12Builder { + ffi::init(); + + Pkcs12Builder { + nid_key: nid::UNDEF, //nid::PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC, + nid_cert: nid::UNDEF, //nid::PBE_WITHSHA1AND40BITRC2_CBC, + iter: ffi::PKCS12_DEFAULT_ITER, + mac_iter: ffi::PKCS12_DEFAULT_ITER, + ca: None, + } + } +} + pub struct ParsedPkcs12 { pub pkey: PKey, pub cert: X509, pub chain: Stack, } +pub struct Pkcs12Builder { + nid_key: nid::Nid, + nid_cert: nid::Nid, + iter: c_int, + mac_iter: c_int, + ca: Option>, +} + +impl Pkcs12Builder { + /// The encryption algorithm that should be used for the key + pub fn key_algorithm(&mut self, nid: nid::Nid) -> &mut Self { + self.nid_key = nid; + self + } + + /// The encryption algorithm that should be used for the cert + pub fn cert_algorithm(&mut self, nid: nid::Nid) -> &mut Self { + self.nid_cert = nid; + self + } + + /// Key iteration count, default is 2048 as of this writing + pub fn key_iter(&mut self, iter: u32) -> &mut Self { + self.iter = iter as c_int; + self + } + + /// MAC iteration count, default is the same as key_iter. + /// + /// Old implementations don't understand MAC iterations greater than 1, (pre 1.0.1?), if such + /// compatibility is required this should be set to 1. + pub fn mac_iter(&mut self, mac_iter: u32) -> &mut Self { + self.mac_iter = mac_iter as c_int; + self + } + + /// An additional set of certificates to include in the archive beyond the one provided to + /// `build`. + pub fn ca(&mut self, ca: Stack) -> &mut Self { + self.ca = Some(ca); + self + } + + /// Builds the PKCS #12 object + /// + /// # Arguments + /// + /// * `password` - the password used to encrypt the key and certificate + /// * `friendly_name` - user defined name for the certificate + /// * `pkey` - key to store + /// * `cert` - certificate to store + pub fn build(self, + password: &str, + friendly_name: &str, + pkey: &PKeyRef, + cert: &X509) -> Result { + unsafe { + let pass = CString::new(password).unwrap(); + let friendly_name = CString::new(friendly_name).unwrap(); + let pkey = pkey.as_ptr(); + let cert = cert.as_ptr(); + let ca = self.ca.as_ref().map(|ca| ca.as_ptr()).unwrap_or(ptr::null_mut()); + let nid_key = self.nid_key.as_raw(); + let nid_cert = self.nid_cert.as_raw(); + + // According to the OpenSSL docs, keytype is a non-standard extension for MSIE, + // It's values are KEY_SIG or KEY_EX, see the OpenSSL docs for more information: + // https://www.openssl.org/docs/man1.0.2/crypto/PKCS12_create.html + let keytype = 0; + + cvt_p(ffi::PKCS12_create(pass.as_ptr() as *const _ as *mut _, + friendly_name.as_ptr() as *const _ as *mut _, + pkey, + cert, + ca, + nid_key, + nid_cert, + self.iter, + self.mac_iter, + keytype)) + .map(Pkcs12) + } + } +} + #[cfg(test)] mod test { use hash::MessageDigest; - use serialize::hex::ToHex; + use hex::ToHex; + + use asn1::Asn1Time; + use rsa::Rsa; + use pkey::PKey; + use nid; + use x509::{X509, X509Name}; + use x509::extension::KeyUsage; use super::*; @@ -83,4 +195,38 @@ mod test { assert_eq!(parsed.chain[0].fingerprint(MessageDigest::sha1()).unwrap().to_hex(), "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875"); } + + #[test] + fn create() { + let subject_name = "ns.example.com"; + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(nid::COMMONNAME, subject_name).unwrap(); + let name = name.build(); + + let key_usage = KeyUsage::new().digital_signature().build().unwrap();; + + let mut builder = X509::builder().unwrap(); + builder.set_version(2).unwrap(); + builder.set_not_before(&Asn1Time::days_from_now(0).unwrap()).unwrap(); + builder.set_not_after(&Asn1Time::days_from_now(365).unwrap()).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_issuer_name(&name).unwrap(); + builder.append_extension(key_usage).unwrap(); + builder.set_pubkey(&pkey).unwrap(); + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + let cert = builder.build(); + + let pkcs12_builder = Pkcs12::builder(); + let pkcs12 = pkcs12_builder.build("mypass", subject_name, &pkey, &cert).unwrap(); + let der = pkcs12.to_der().unwrap(); + + let pkcs12 = Pkcs12::from_der(&der).unwrap(); + let parsed = pkcs12.parse("mypass").unwrap(); + + assert_eq!(parsed.cert.fingerprint(MessageDigest::sha1()).unwrap(), cert.fingerprint(MessageDigest::sha1()).unwrap()); + assert!(parsed.pkey.public_eq(&pkey)); + } } diff --git a/openssl/src/pkcs5.rs b/openssl/src/pkcs5.rs index 8d6dcce845713573448d2a87eecd5c6082cac7b5..9e1c2d263d749ed1e14e6aa684d9838ff5df5ba8 100644 --- a/openssl/src/pkcs5.rs +++ b/openssl/src/pkcs5.rs @@ -22,7 +22,7 @@ pub struct KeyIvPair { /// v1.5 or PBKDF1 from PKCS#5 v2.0. /// /// New applications should not use this and instead use -/// `pkcs5_pbkdf2_hmac_sha1` or another more modern key derivation algorithm. +/// `pbkdf2_hmac` or another more modern key derivation algorithm. pub fn bytes_to_key(cipher: Cipher, digest: MessageDigest, data: &[u8], diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index a1ebd695d6f819cfcf08f14c4fb89663abd6de30..5608dd51fccb0f40d9281ca7427be123fc1aeada 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -2,62 +2,73 @@ use libc::{c_void, c_char, c_int}; use std::ptr; use std::mem; use ffi; +use foreign_types::{Opaque, ForeignType, ForeignTypeRef}; use {cvt, cvt_p}; -use bio::{MemBio, MemBioSlice}; +use bio::MemBioSlice; +use dh::Dh; use dsa::Dsa; -use rsa::Rsa; +use ec::EcKey; +use rsa::{Rsa, Padding}; use error::ErrorStack; -use util::{CallbackState, invoke_passwd_cb}; -use types::{OpenSslType, OpenSslTypeRef}; +use util::{CallbackState, invoke_passwd_cb_old}; -type_!(PKey, PKeyRef, ffi::EVP_PKEY, ffi::EVP_PKEY_free); +foreign_type! { + type CType = ffi::EVP_PKEY; + fn drop = ffi::EVP_PKEY_free; + + pub struct PKey; + pub struct PKeyRef; +} impl PKeyRef { - /// Get a reference to the interal RSA key for direct access to the key components + /// Returns a copy of the internal RSA key. pub fn rsa(&self) -> Result { unsafe { let rsa = try!(cvt_p(ffi::EVP_PKEY_get1_RSA(self.as_ptr()))); - // this is safe as the ffi increments a reference counter to the internal key Ok(Rsa::from_ptr(rsa)) } } - /// Stores private key as a PEM - // FIXME: also add password and encryption - pub fn private_key_to_pem(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); + /// Returns a copy of the internal DSA key. + pub fn dsa(&self) -> Result { unsafe { - try!(cvt(ffi::PEM_write_bio_PrivateKey(mem_bio.as_ptr(), - self.as_ptr(), - ptr::null(), - ptr::null_mut(), - -1, - None, - ptr::null_mut()))); - + let dsa = try!(cvt_p(ffi::EVP_PKEY_get1_DSA(self.as_ptr()))); + Ok(Dsa::from_ptr(dsa)) } - Ok(mem_bio.get_buf().to_owned()) } - /// Encode public key in PEM format - pub fn public_key_to_pem(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); + /// Returns a copy of the internal DH key. + pub fn dh(&self) -> Result { unsafe { - try!(cvt(ffi::PEM_write_bio_PUBKEY(mem_bio.as_ptr(), self.as_ptr()))); + let dh = try!(cvt_p(ffi::EVP_PKEY_get1_DH(self.as_ptr()))); + Ok(Dh::from_ptr(dh)) } - Ok(mem_bio.get_buf().to_owned()) } - /// Encode public key in DER format - pub fn public_key_to_der(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); + /// Returns a copy of the internal elliptic curve key. + pub fn ec_key(&self) -> Result { unsafe { - try!(cvt(ffi::i2d_PUBKEY_bio(mem_bio.as_ptr(), self.as_ptr()))); + let ec_key = try!(cvt_p(ffi::EVP_PKEY_get1_EC_KEY(self.as_ptr()))); + Ok(EcKey::from_ptr(ec_key)) } - Ok(mem_bio.get_buf().to_owned()) } + public_key_to_pem!(ffi::PEM_write_bio_PUBKEY); + private_key_to_pem!(ffi::PEM_write_bio_PKCS8PrivateKey); + + private_key_to_der!(ffi::i2d_PrivateKey); + public_key_to_der!(ffi::i2d_PUBKEY); + + /// Returns the size of the key. + /// + /// This corresponds to the bit length of the modulus of an RSA key, and the bit length of the + /// group order for an elliptic curve key, for example. + pub fn bits(&self) -> u32 { + unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) as u32 } + } + + /// Compares the public component of this key with another. pub fn public_eq(&self, other: &PKeyRef) -> bool { unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 } } @@ -67,7 +78,7 @@ unsafe impl Send for PKey {} unsafe impl Sync for PKey {} impl PKey { - /// Create a new `PKey` containing an RSA key. + /// Creates a new `PKey` containing an RSA key. pub fn from_rsa(rsa: Rsa) -> Result { unsafe { let evp = try!(cvt_p(ffi::EVP_PKEY_new())); @@ -78,7 +89,7 @@ impl PKey { } } - /// Create a new `PKey` containing a DSA key. + /// Creates a new `PKey` containing a DSA key. pub fn from_dsa(dsa: Dsa) -> Result { unsafe { let evp = try!(cvt_p(ffi::EVP_PKEY_new())); @@ -89,7 +100,32 @@ impl PKey { } } - /// Create a new `PKey` containing an HMAC key. + /// Creates a new `PKey` containing a Diffie-Hellman key. + pub fn from_dh(dh: Dh) -> Result { + unsafe { + let evp = try!(cvt_p(ffi::EVP_PKEY_new())); + let pkey = PKey(evp); + try!(cvt(ffi::EVP_PKEY_assign(pkey.0, ffi::EVP_PKEY_DH, dh.as_ptr() as *mut _))); + mem::forget(dh); + Ok(pkey) + } + } + + /// Creates a new `PKey` containing an elliptic curve key. + pub fn from_ec_key(ec_key: EcKey) -> Result { + unsafe { + let evp = try!(cvt_p(ffi::EVP_PKEY_new())); + let pkey = PKey(evp); + try!(cvt(ffi::EVP_PKEY_assign(pkey.0, ffi::EVP_PKEY_EC, ec_key.as_ptr() as *mut _))); + mem::forget(ec_key); + Ok(pkey) + } + } + + /// Creates a new `PKey` containing an HMAC key. + /// + /// # Note + /// To compute HMAC values, use the `sign` module. pub fn hmac(key: &[u8]) -> Result { unsafe { assert!(key.len() <= c_int::max_value() as usize); @@ -101,24 +137,10 @@ impl PKey { } } - /// Reads private key from PEM, takes ownership of handle - pub fn private_key_from_pem(buf: &[u8]) -> Result { - ffi::init(); - let mem_bio = try!(MemBioSlice::new(buf)); - unsafe { - let evp = try!(cvt_p(ffi::PEM_read_bio_PrivateKey(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(PKey::from_ptr(evp)) - } - } + private_key_from_pem!(PKey, ffi::PEM_read_bio_PrivateKey); + public_key_from_pem!(PKey, ffi::PEM_read_bio_PUBKEY); - /// Read a private key from PEM, supplying a password callback to be invoked if the private key - /// is encrypted. - /// - /// The callback will be passed the password buffer and should return the number of characters - /// placed into the buffer. + #[deprecated(since = "0.9.2", note = "use private_key_from_pem_callback")] pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result where F: FnOnce(&mut [c_char]) -> usize { @@ -128,44 +150,72 @@ impl PKey { unsafe { let evp = try!(cvt_p(ffi::PEM_read_bio_PrivateKey(mem_bio.as_ptr(), ptr::null_mut(), - Some(invoke_passwd_cb::), + Some(invoke_passwd_cb_old::), &mut cb as *mut _ as *mut c_void))); Ok(PKey::from_ptr(evp)) } } +} - /// Reads public key from PEM, takes ownership of handle - pub fn public_key_from_pem(buf: &[u8]) -> Result { - ffi::init(); - let mem_bio = try!(MemBioSlice::new(buf)); +pub struct PKeyCtxRef(Opaque); + +impl PKeyCtxRef { + pub fn set_rsa_padding(&mut self, pad: Padding) -> Result<(), ErrorStack> { unsafe { - let evp = try!(cvt_p(ffi::PEM_read_bio_PUBKEY(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(PKey::from_ptr(evp)) + try!(cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(self.as_ptr(), pad.as_raw()))); } + Ok(()) } + + pub fn rsa_padding(&self) -> Result { + let mut pad: c_int = 0; + unsafe { + try!(cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.as_ptr(), &mut pad))); + }; + Ok(Padding::from_raw(pad)) + } +} + +impl ForeignTypeRef for PKeyCtxRef { + type CType = ffi::EVP_PKEY_CTX; } #[cfg(test)] mod tests { + use symm::Cipher; + use dh::Dh; + use dsa::Dsa; + use ec::EcKey; + use rsa::Rsa; + use nid; + + use super::*; + + #[test] + fn test_to_password() { + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + let pem = pkey.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar").unwrap(); + PKey::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(PKey::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + #[test] fn test_private_key_from_pem() { let key = include_bytes!("../test/key.pem"); - super::PKey::private_key_from_pem(key).unwrap(); + PKey::private_key_from_pem(key).unwrap(); } #[test] fn test_public_key_from_pem() { let key = include_bytes!("../test/key.pem.pub"); - super::PKey::public_key_from_pem(key).unwrap(); + PKey::public_key_from_pem(key).unwrap(); } #[test] fn test_pem() { let key = include_bytes!("../test/key.pem"); - let key = super::PKey::private_key_from_pem(key).unwrap(); + let key = PKey::private_key_from_pem(key).unwrap(); let priv_key = key.private_key_to_pem().unwrap(); let pub_key = key.public_key_to_pem().unwrap(); @@ -175,4 +225,37 @@ mod tests { assert!(priv_key.windows(11).any(|s| s == b"PRIVATE KEY")); assert!(pub_key.windows(10).any(|s| s == b"PUBLIC KEY")); } + + #[test] + fn test_rsa_accessor() { + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + pkey.rsa().unwrap(); + assert!(pkey.dsa().is_err()); + } + + #[test] + fn test_dsa_accessor() { + let dsa = Dsa::generate(2048).unwrap(); + let pkey = PKey::from_dsa(dsa).unwrap(); + pkey.dsa().unwrap(); + assert!(pkey.rsa().is_err()); + } + + #[test] + fn test_dh_accessor() { + let dh = include_bytes!("../test/dhparams.pem"); + let dh = Dh::from_pem(dh).unwrap(); + let pkey = PKey::from_dh(dh).unwrap(); + pkey.dh().unwrap(); + assert!(pkey.rsa().is_err()); + } + + #[test] + fn test_ec_key_accessor() { + let ec_key = EcKey::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + let pkey = PKey::from_ec_key(ec_key).unwrap(); + pkey.ec_key().unwrap(); + assert!(pkey.rsa().is_err()); + } } diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs index bd1d16d3c77605f87f8df937c9baaf4e335d25a1..792f70709f7344992bd09a663960d472f2959862 100644 --- a/openssl/src/rsa.rs +++ b/openssl/src/rsa.rs @@ -3,52 +3,48 @@ use std::fmt; use std::ptr; use std::mem; use libc::{c_int, c_void, c_char}; +use foreign_types::ForeignTypeRef; use {cvt, cvt_p, cvt_n}; use bn::{BigNum, BigNumRef}; -use bio::{MemBio, MemBioSlice}; +use bio::MemBioSlice; use error::ErrorStack; -use util::{CallbackState, invoke_passwd_cb}; -use types::OpenSslTypeRef; +use util::{CallbackState, invoke_passwd_cb_old}; /// Type of encryption padding to use. -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Padding(c_int); +impl Padding { + pub fn from_raw(value: c_int) -> Padding { + Padding(value) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } +} + pub const NO_PADDING: Padding = Padding(ffi::RSA_NO_PADDING); pub const PKCS1_PADDING: Padding = Padding(ffi::RSA_PKCS1_PADDING); pub const PKCS1_OAEP_PADDING: Padding = Padding(ffi::RSA_PKCS1_OAEP_PADDING); -type_!(Rsa, RsaRef, ffi::RSA, ffi::RSA_free); +foreign_type! { + type CType = ffi::RSA; + fn drop = ffi::RSA_free; -impl RsaRef { - /// Writes an RSA private key as unencrypted PEM formatted data - pub fn private_key_to_pem(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); - - unsafe { - try!(cvt(ffi::PEM_write_bio_RSAPrivateKey(mem_bio.as_ptr(), - self.as_ptr(), - ptr::null(), - ptr::null_mut(), - 0, - None, - ptr::null_mut()))); - } - Ok(mem_bio.get_buf().to_owned()) - } + pub struct Rsa; + pub struct RsaRef; +} - /// Writes an RSA public key as PEM formatted data - pub fn public_key_to_pem(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); +impl RsaRef { + private_key_to_pem!(ffi::PEM_write_bio_RSAPrivateKey); + public_key_to_pem!(ffi::PEM_write_bio_RSA_PUBKEY); - unsafe { - try!(cvt(ffi::PEM_write_bio_RSA_PUBKEY(mem_bio.as_ptr(), self.as_ptr()))); - } - - Ok(mem_bio.get_buf().to_owned()) - } + private_key_to_der!(ffi::i2d_RSAPrivateKey); + public_key_to_der!(ffi::i2d_RSA_PUBKEY); + // FIXME should return u32 pub fn size(&self) -> usize { unsafe { assert!(self.n().is_some()); @@ -250,6 +246,7 @@ impl Rsa { /// /// The public exponent will be 65537. pub fn generate(bits: u32) -> Result { + ffi::init(); unsafe { let rsa = Rsa(try!(cvt_p(ffi::RSA_new()))); let e = try!(BigNum::from_u32(ffi::RSA_F4 as u32)); @@ -258,22 +255,16 @@ impl Rsa { } } - /// Reads an RSA private key from PEM formatted data. - pub fn private_key_from_pem(buf: &[u8]) -> Result { - let mem_bio = try!(MemBioSlice::new(buf)); - unsafe { - let rsa = try!(cvt_p(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(Rsa(rsa)) - } - } + private_key_from_pem!(Rsa, ffi::PEM_read_bio_RSAPrivateKey); + private_key_from_der!(Rsa, ffi::d2i_RSAPrivateKey); + public_key_from_pem!(Rsa, ffi::PEM_read_bio_RSA_PUBKEY); + public_key_from_der!(Rsa, ffi::d2i_RSA_PUBKEY); - /// Reads an RSA private key from PEM formatted data and supplies a password callback. + #[deprecated(since = "0.9.2", note = "use private_key_from_pem_callback")] pub fn private_key_from_pem_cb(buf: &[u8], pass_cb: F) -> Result where F: FnOnce(&mut [c_char]) -> usize { + ffi::init(); let mut cb = CallbackState::new(pass_cb); let mem_bio = try!(MemBioSlice::new(buf)); @@ -281,23 +272,11 @@ impl Rsa { let cb_ptr = &mut cb as *mut _ as *mut c_void; let rsa = try!(cvt_p(ffi::PEM_read_bio_RSAPrivateKey(mem_bio.as_ptr(), ptr::null_mut(), - Some(invoke_passwd_cb::), + Some(invoke_passwd_cb_old::), cb_ptr))); Ok(Rsa(rsa)) } } - - /// Reads an RSA public key from PEM formatted data. - pub fn public_key_from_pem(buf: &[u8]) -> Result { - let mem_bio = try!(MemBioSlice::new(buf)); - unsafe { - let rsa = try!(cvt_p(ffi::PEM_read_bio_RSA_PUBKEY(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(Rsa(rsa)) - } - } } impl fmt::Debug for Rsa { @@ -380,26 +359,26 @@ mod compat { } } - #[cfg(test)] mod test { - use libc::c_char; + use symm::Cipher; use super::*; #[test] - pub fn test_password() { + fn test_from_password() { + let key = include_bytes!("../test/rsa-encrypted.pem"); + Rsa::private_key_from_pem_passphrase(key, b"mypass").unwrap(); + } + + #[test] + fn test_from_password_callback() { let mut password_queried = false; let key = include_bytes!("../test/rsa-encrypted.pem"); - Rsa::private_key_from_pem_cb(key, |password| { + Rsa::private_key_from_pem_callback(key, |password| { password_queried = true; - password[0] = b'm' as c_char; - password[1] = b'y' as c_char; - password[2] = b'p' as c_char; - password[3] = b'a' as c_char; - password[4] = b's' as c_char; - password[5] = b's' as c_char; - 6 + password[..6].copy_from_slice(b"mypass"); + Ok(6) }) .unwrap(); @@ -407,7 +386,15 @@ mod test { } #[test] - pub fn test_public_encrypt_private_decrypt_with_padding() { + fn test_to_password() { + let key = Rsa::generate(2048).unwrap(); + let pem = key.private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar").unwrap(); + Rsa::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(Rsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + + #[test] + fn test_public_encrypt_private_decrypt_with_padding() { let key = include_bytes!("../test/rsa.pem.pub"); let public_key = Rsa::public_key_from_pem(key).unwrap(); diff --git a/openssl/src/sign.rs b/openssl/src/sign.rs index 4ca551b6aa288c5c30862935b7438460bf4c5011..279f294d1e17a1768402a24f854524221d62589f 100644 --- a/openssl/src/sign.rs +++ b/openssl/src/sign.rs @@ -35,12 +35,13 @@ //! assert!(verifier.finish(&signature).unwrap()); //! ``` //! -//! Compute an HMAC (note that `Verifier` cannot be used with HMACs): +//! Compute an HMAC: //! //! ```rust -//! use openssl::sign::Signer; -//! use openssl::pkey::PKey; //! use openssl::hash::MessageDigest; +//! use openssl::memcmp; +//! use openssl::pkey::PKey; +//! use openssl::sign::Signer; //! //! // Create a PKey //! let key = PKey::hmac(b"my secret").unwrap(); @@ -53,29 +54,40 @@ //! signer.update(data).unwrap(); //! signer.update(data2).unwrap(); //! let hmac = signer.finish().unwrap(); +//! +//! // `Verifier` cannot be used with HMACs; use the `memcmp::eq` function instead +//! // +//! // Do not simply check for equality with `==`! +//! # let target = hmac.clone(); +//! assert!(memcmp::eq(&hmac, &target)); //! ``` use ffi; +use foreign_types::ForeignTypeRef; use std::io::{self, Write}; use std::marker::PhantomData; use std::ptr; use {cvt, cvt_p}; use hash::MessageDigest; -use pkey::PKeyRef; +use pkey::{PKeyRef, PKeyCtxRef}; use error::ErrorStack; -use types::OpenSslTypeRef; #[cfg(ossl110)] use ffi::{EVP_MD_CTX_new, EVP_MD_CTX_free}; #[cfg(any(ossl101, ossl102))] use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free}; -pub struct Signer<'a>(*mut ffi::EVP_MD_CTX, PhantomData<&'a PKeyRef>); +pub struct Signer<'a> { + md_ctx: *mut ffi::EVP_MD_CTX, + pkey_ctx: *mut ffi::EVP_PKEY_CTX, + pkey_pd: PhantomData<&'a PKeyRef>, +} impl<'a> Drop for Signer<'a> { fn drop(&mut self) { + // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. unsafe { - EVP_MD_CTX_free(self.0); + EVP_MD_CTX_free(self.md_ctx); } } } @@ -86,8 +98,9 @@ impl<'a> Signer<'a> { ffi::init(); let ctx = try!(cvt_p(EVP_MD_CTX_new())); + let mut pctx: *mut ffi::EVP_PKEY_CTX = ptr::null_mut(); let r = ffi::EVP_DigestSignInit(ctx, - ptr::null_mut(), + &mut pctx, type_.as_ptr(), ptr::null_mut(), pkey.as_ptr()); @@ -95,22 +108,37 @@ impl<'a> Signer<'a> { EVP_MD_CTX_free(ctx); return Err(ErrorStack::get()); } - Ok(Signer(ctx, PhantomData)) + + assert!(!pctx.is_null()); + + Ok(Signer { + md_ctx: ctx, + pkey_ctx: pctx, + pkey_pd: PhantomData, + }) } } + pub fn pkey_ctx(&self) -> &PKeyCtxRef { + unsafe { PKeyCtxRef::from_ptr(self.pkey_ctx) } + } + + pub fn pkey_ctx_mut(&mut self) -> &mut PKeyCtxRef { + unsafe { PKeyCtxRef::from_ptr_mut(self.pkey_ctx) } + } + pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> { unsafe { - cvt(ffi::EVP_DigestUpdate(self.0, buf.as_ptr() as *const _, buf.len())).map(|_| ()) + cvt(ffi::EVP_DigestUpdate(self.md_ctx, buf.as_ptr() as *const _, buf.len())).map(|_| ()) } } pub fn finish(&self) -> Result, ErrorStack> { unsafe { let mut len = 0; - try!(cvt(ffi::EVP_DigestSignFinal(self.0, ptr::null_mut(), &mut len))); + try!(cvt(ffi::EVP_DigestSignFinal(self.md_ctx, ptr::null_mut(), &mut len))); let mut buf = vec![0; len]; - try!(cvt(ffi::EVP_DigestSignFinal(self.0, buf.as_mut_ptr() as *mut _, &mut len))); + try!(cvt(ffi::EVP_DigestSignFinal(self.md_ctx, buf.as_mut_ptr() as *mut _, &mut len))); // The advertised length is not always equal to the real length for things like DSA buf.truncate(len); Ok(buf) @@ -129,12 +157,17 @@ impl<'a> Write for Signer<'a> { } } -pub struct Verifier<'a>(*mut ffi::EVP_MD_CTX, PhantomData<&'a PKeyRef>); +pub struct Verifier<'a> { + md_ctx: *mut ffi::EVP_MD_CTX, + pkey_ctx: *mut ffi::EVP_PKEY_CTX, + pkey_pd: PhantomData<&'a PKeyRef>, +} impl<'a> Drop for Verifier<'a> { fn drop(&mut self) { + // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. unsafe { - EVP_MD_CTX_free(self.0); + EVP_MD_CTX_free(self.md_ctx); } } } @@ -145,8 +178,9 @@ impl<'a> Verifier<'a> { ffi::init(); let ctx = try!(cvt_p(EVP_MD_CTX_new())); + let mut pctx: *mut ffi::EVP_PKEY_CTX = ptr::null_mut(); let r = ffi::EVP_DigestVerifyInit(ctx, - ptr::null_mut(), + &mut pctx, type_.as_ptr(), ptr::null_mut(), pkey.as_ptr()); @@ -155,19 +189,33 @@ impl<'a> Verifier<'a> { return Err(ErrorStack::get()); } - Ok(Verifier(ctx, PhantomData)) + assert!(!pctx.is_null()); + + Ok(Verifier { + md_ctx: ctx, + pkey_ctx: pctx, + pkey_pd: PhantomData, + }) } } + pub fn pkey_ctx(&self) -> &PKeyCtxRef { + unsafe { PKeyCtxRef::from_ptr(self.pkey_ctx) } + } + + pub fn pkey_ctx_mut(&mut self) -> &mut PKeyCtxRef { + unsafe { PKeyCtxRef::from_ptr_mut(self.pkey_ctx) } + } + pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> { unsafe { - cvt(ffi::EVP_DigestUpdate(self.0, buf.as_ptr() as *const _, buf.len())).map(|_| ()) + cvt(ffi::EVP_DigestUpdate(self.md_ctx, buf.as_ptr() as *const _, buf.len())).map(|_| ()) } } pub fn finish(&self, signature: &[u8]) -> Result { unsafe { - let r = EVP_DigestVerifyFinal(self.0, signature.as_ptr() as *const _, signature.len()); + let r = EVP_DigestVerifyFinal(self.md_ctx, signature.as_ptr() as *const _, signature.len()); match r { 1 => Ok(true), 0 => { @@ -205,12 +253,14 @@ unsafe fn EVP_DigestVerifyFinal(ctx: *mut ffi::EVP_MD_CTX, #[cfg(test)] mod test { - use serialize::hex::FromHex; + use hex::FromHex; use std::iter; use hash::MessageDigest; use sign::{Signer, Verifier}; - use rsa::Rsa; + use ec::{EcGroup, EcKey}; + use nid; + use rsa::{Rsa, PKCS1_PADDING}; use dsa::Dsa; use pkey::PKey; @@ -245,6 +295,8 @@ mod test { let pkey = PKey::from_rsa(private_key).unwrap(); let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); + assert_eq!(signer.pkey_ctx_mut().rsa_padding().unwrap(), PKCS1_PADDING); + signer.pkey_ctx_mut().set_rsa_padding(PKCS1_PADDING).unwrap(); signer.update(INPUT).unwrap(); let result = signer.finish().unwrap(); @@ -258,6 +310,7 @@ mod test { let pkey = PKey::from_rsa(private_key).unwrap(); let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey).unwrap(); + assert_eq!(verifier.pkey_ctx_mut().rsa_padding().unwrap(), PKCS1_PADDING); verifier.update(INPUT).unwrap(); assert!(verifier.finish(SIGNATURE).unwrap()); } @@ -339,27 +392,27 @@ mod test { let tests: [(Vec, Vec, Vec); 7] = [(iter::repeat(0x0b_u8).take(16).collect(), b"Hi There".to_vec(), - "9294727a3638bb1c13f48ef8158bfc9d".from_hex().unwrap()), + Vec::from_hex("9294727a3638bb1c13f48ef8158bfc9d").unwrap()), (b"Jefe".to_vec(), b"what do ya want for nothing?".to_vec(), - "750c783e6ab0b503eaa86e310a5db738".from_hex().unwrap()), + Vec::from_hex("750c783e6ab0b503eaa86e310a5db738").unwrap()), (iter::repeat(0xaa_u8).take(16).collect(), iter::repeat(0xdd_u8).take(50).collect(), - "56be34521d144c88dbb8c733f0e8b3f6".from_hex().unwrap()), - ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(), + Vec::from_hex("56be34521d144c88dbb8c733f0e8b3f6").unwrap()), + (Vec::from_hex("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), iter::repeat(0xcd_u8).take(50).collect(), - "697eaf0aca3a3aea3a75164746ffaa79".from_hex().unwrap()), + Vec::from_hex("697eaf0aca3a3aea3a75164746ffaa79").unwrap()), (iter::repeat(0x0c_u8).take(16).collect(), b"Test With Truncation".to_vec(), - "56461ef2342edc00f9bab995690efd4c".from_hex().unwrap()), + Vec::from_hex("56461ef2342edc00f9bab995690efd4c").unwrap()), (iter::repeat(0xaa_u8).take(80).collect(), b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), - "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd".from_hex().unwrap()), + Vec::from_hex("6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd").unwrap()), (iter::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())]; + Vec::from_hex("6f630fad67cda0ee1fb1f562db3aa53e").unwrap())]; test_hmac(MessageDigest::md5(), &tests); } @@ -370,28 +423,43 @@ mod test { let tests: [(Vec, Vec, Vec); 7] = [(iter::repeat(0x0b_u8).take(20).collect(), b"Hi There".to_vec(), - "b617318655057264e28bc0b6fb378c8ef146be00".from_hex().unwrap()), + Vec::from_hex("b617318655057264e28bc0b6fb378c8ef146be00").unwrap()), (b"Jefe".to_vec(), b"what do ya want for nothing?".to_vec(), - "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79".from_hex().unwrap()), + Vec::from_hex("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79").unwrap()), (iter::repeat(0xaa_u8).take(20).collect(), iter::repeat(0xdd_u8).take(50).collect(), - "125d7342b9ac11cd91a39af48aa17b4f63f175d3".from_hex().unwrap()), - ("0102030405060708090a0b0c0d0e0f10111213141516171819".from_hex().unwrap(), + Vec::from_hex("125d7342b9ac11cd91a39af48aa17b4f63f175d3").unwrap()), + (Vec::from_hex("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), iter::repeat(0xcd_u8).take(50).collect(), - "4c9007f4026250c6bc8414f9bf50c86c2d7235da".from_hex().unwrap()), + Vec::from_hex("4c9007f4026250c6bc8414f9bf50c86c2d7235da").unwrap()), (iter::repeat(0x0c_u8).take(20).collect(), b"Test With Truncation".to_vec(), - "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04".from_hex().unwrap()), + Vec::from_hex("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04").unwrap()), (iter::repeat(0xaa_u8).take(80).collect(), b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), - "aa4ae5e15272d00e95705637ce8a3b55ed402112".from_hex().unwrap()), + Vec::from_hex("aa4ae5e15272d00e95705637ce8a3b55ed402112").unwrap()), (iter::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())]; + Vec::from_hex("e8e99d0f45237d786d6bbaa7965c7808bbff1a91").unwrap())]; test_hmac(MessageDigest::sha1(), &tests); } + + #[test] + fn ec() { + let group = EcGroup::from_curve_name(nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let key = PKey::from_ec_key(key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &key).unwrap(); + signer.update(b"hello world").unwrap(); + let signature = signer.finish().unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &key).unwrap(); + verifier.update(b"hello world").unwrap(); + assert!(verifier.finish(&signature).unwrap()); + } } diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs index 486b4dbad65796d1fee0d34d049025805849faf8..4dc7cbd42f7c007778205cfc57b92464d97affc1 100644 --- a/openssl/src/ssl/bio.rs +++ b/openssl/src/ssl/bio.rs @@ -5,6 +5,7 @@ use std::any::Any; use std::io; use std::io::prelude::*; use std::mem; +use std::panic::{AssertUnwindSafe, catch_unwind}; use std::ptr; use std::slice; @@ -67,22 +68,16 @@ pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S { } unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState { - mem::transmute(compat::BIO_get_data(bio)) + &mut *(compat::BIO_get_data(bio) as *mut _) } -fn catch_unwind(f: F) -> Result> - where F: FnOnce() -> T -{ - ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(f)) -} - -unsafe extern "C" fn bwrite(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int { +unsafe extern fn bwrite(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int { BIO_clear_retry_flags(bio); let state = state::(bio); let buf = slice::from_raw_parts(buf as *const _, len as usize); - match catch_unwind(|| state.stream.write(buf)) { + match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) { Ok(Ok(len)) => len as c_int, Ok(Err(err)) => { if retriable_error(&err) { @@ -98,13 +93,13 @@ unsafe extern "C" fn bwrite(bio: *mut BIO, buf: *const c_char, len: c_ } } -unsafe extern "C" fn bread(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int { +unsafe extern fn bread(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int { BIO_clear_retry_flags(bio); let state = state::(bio); let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize); - match catch_unwind(|| state.stream.read(buf)) { + match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) { Ok(Ok(len)) => len as c_int, Ok(Err(err)) => { if retriable_error(&err) { @@ -128,11 +123,11 @@ fn retriable_error(err: &io::Error) -> bool { } } -unsafe extern "C" fn bputs(bio: *mut BIO, s: *const c_char) -> c_int { +unsafe extern fn bputs(bio: *mut BIO, s: *const c_char) -> c_int { bwrite::(bio, s, strlen(s) as c_int) } -unsafe extern "C" fn ctrl(bio: *mut BIO, +unsafe extern fn ctrl(bio: *mut BIO, cmd: c_int, _num: c_long, _ptr: *mut c_void) @@ -140,7 +135,7 @@ unsafe extern "C" fn ctrl(bio: *mut BIO, if cmd == BIO_CTRL_FLUSH { let state = state::(bio); - match catch_unwind(|| state.stream.flush()) { + match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) { Ok(Ok(())) => 1, Ok(Err(err)) => { state.error = Some(err); @@ -156,7 +151,7 @@ unsafe extern "C" fn ctrl(bio: *mut BIO, } } -unsafe extern "C" fn create(bio: *mut BIO) -> c_int { +unsafe extern fn create(bio: *mut BIO) -> c_int { compat::BIO_set_init(bio, 0); compat::BIO_set_num(bio, 0); compat::BIO_set_data(bio, ptr::null_mut()); @@ -164,7 +159,7 @@ unsafe extern "C" fn create(bio: *mut BIO) -> c_int { 1 } -unsafe extern "C" fn destroy(bio: *mut BIO) -> c_int { +unsafe extern fn destroy(bio: *mut BIO) -> c_int { if bio.is_null() { return 0; } diff --git a/openssl/src/ssl/connector.rs b/openssl/src/ssl/connector.rs index 55177767d115abe0b654bb1d270a510b5da4006b..73d7b675051f7409636d0dab325754d7dc1008d2 100644 --- a/openssl/src/ssl/connector.rs +++ b/openssl/src/ssl/connector.rs @@ -7,32 +7,26 @@ use ssl::{self, SslMethod, SslContextBuilder, SslContext, Ssl, SSL_VERIFY_PEER, use pkey::PKeyRef; use x509::X509Ref; -// Serialized form of DH_get_2048_256 -#[cfg(any(ossl101, all(test, any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))))] -const DHPARAM_PEM: &'static str = r#" +// ffdhe2048 from https://wiki.mozilla.org/Security/Server_Side_TLS#ffdhe2048 +const DHPARAM_PEM: &'static str = " -----BEGIN DH PARAMETERS----- -MIICCQKCAQEAh6jmHbS2Zjz/u9GcZRlZmYzu9ghmDdDyXSzu1ENeOwDgDfjx1hlX -1Pr330VhsqowFsPZETQJb6o79Cltgw6afCCeDGSXUXq9WoqdMGvPZ+2R+eZyW0dY -wCLgse9Cdb97bFv8EdRfkIi5QfVOseWbuLw5oL8SMH9cT9twxYGyP3a2Osrhyqa3 -kC1SUmc1SIoO8TxtmlG/pKs62DR3llJNjvahZ7WkGCXZZ+FE5RQFZCUcysuD5rSG -9rPKP3lxUGAmwLhX9omWKFbe1AEKvQvmIcOjlgpU5xDDdfJjddcBQQOktUMwwZiv -EmEW0iduEXFfaTh3+tfvCcrbCUrpHhoVlwKCAQA/syybcxNNCy53UGZg7b1ITKex -jyHvIFQH9Hk6GguhJRDbwVB3vkY//0/tSqwLtVW+OmwbDGtHsbw3c79+jG9ikBIo -+MKMuxilWuMTQQAKZQGW+THHelfy3fRj5ensFEt3feYqqrioYorDdtKC1u04ZOZ5 -gkKOvIMdFDSPby+Rk7UEWvJ2cWTh38lnwfs/LlWkvRv/6DucgNBSuYXRguoK2yo7 -cxPT/hTISEseBSWIubfSu9LfAWGZ7NBuFVfNCRWzNTu7ZODsN3/QKDcN+StSx4kU -KM3GfrYYS1I9HbJGwy9jB4SQ8A741kfRSNR5VFFeIyfP75jFgmZLTA9sxBZZ +MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== -----END DH PARAMETERS----- -"#; +"; fn ctx(method: SslMethod) -> Result { let mut ctx = try!(SslContextBuilder::new(method)); let mut opts = ssl::SSL_OP_ALL; - opts |= ssl::SSL_OP_NO_TICKET; - opts |= ssl::SSL_OP_NO_COMPRESSION; opts &= !ssl::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; opts &= !ssl::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + opts |= ssl::SSL_OP_NO_TICKET; + opts |= ssl::SSL_OP_NO_COMPRESSION; opts |= ssl::SSL_OP_NO_SSLV2; opts |= ssl::SSL_OP_NO_SSLV3; opts |= ssl::SSL_OP_SINGLE_DH_USE; @@ -61,6 +55,7 @@ impl SslConnectorBuilder { try!(ctx.set_cipher_list("ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:\ DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:\ RSA+AES:RSA+HIGH:!aNULL:!eNULL:!MD5:!3DES")); + ctx.set_verify(SSL_VERIFY_PEER); Ok(SslConnectorBuilder(ctx)) } @@ -88,6 +83,7 @@ impl SslConnectorBuilder { /// /// OpenSSL's built in hostname verification is used when linking against OpenSSL 1.0.2 or 1.1.0, /// and a custom implementation is used when linking against OpenSSL 1.0.1. +#[derive(Clone)] pub struct SslConnector(SslContext); impl SslConnector { @@ -103,6 +99,22 @@ impl SslConnector { ssl.connect(stream) } + + /// Initiates a client-side TLS session on a stream without performing hostname verification. + /// + /// The verification configuration of the connector's `SslContext` is not overridden. + /// + /// # Warning + /// + /// You should think very carefully before you use this method. If hostname verification is not + /// used, *any* valid certificate for *any* site will be trusted for use from any other. This + /// introduces a significant vulnerability to man-in-the-middle attacks. + pub fn danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication( + &self, stream: S) -> Result, HandshakeError> + where S: Read + Write + { + try!(Ssl::new(&self.0)).connect(stream) + } } /// A builder for `SslAcceptor`s. @@ -124,8 +136,32 @@ impl SslAcceptorBuilder { where I: IntoIterator, I::Item: AsRef { + let builder = try!(SslAcceptorBuilder::mozilla_intermediate_raw(method)); + builder.finish_setup(private_key, certificate, chain) + } + + /// Creates a new builder configured to connect to modern clients. + /// + /// This corresponds to the modern configuration of Mozilla's server side TLS recommendations. + /// See its [documentation][docs] for more details on specifics. + /// + /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS + pub fn mozilla_modern(method: SslMethod, + private_key: &PKeyRef, + certificate: &X509Ref, + chain: I) + -> Result + where I: IntoIterator, + I::Item: AsRef + { + let builder = try!(SslAcceptorBuilder::mozilla_modern_raw(method)); + builder.finish_setup(private_key, certificate, chain) + } + + /// Like `mozilla_intermediate`, but does not load the certificate chain and private key. + pub fn mozilla_intermediate_raw(method: SslMethod) -> Result { let mut ctx = try!(ctx(method)); - let dh = try!(get_dh()); + let dh = try!(Dh::from_pem(DHPARAM_PEM.as_bytes())); try!(ctx.set_tmp_dh(&dh)); try!(setup_curves(&mut ctx)); try!(ctx.set_cipher_list("ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ @@ -142,23 +178,11 @@ impl SslAcceptorBuilder { EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:\ AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:\ DES-CBC3-SHA:!DSS")); - SslAcceptorBuilder::finish_setup(ctx, private_key, certificate, chain) + Ok(SslAcceptorBuilder(ctx)) } - /// Creates a new builder configured to connect to modern clients. - /// - /// This corresponds to the modern configuration of Mozilla's server side TLS recommendations. - /// See its [documentation][docs] for more details on specifics. - /// - /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS - pub fn mozilla_modern(method: SslMethod, - private_key: &PKeyRef, - certificate: &X509Ref, - chain: I) - -> Result - where I: IntoIterator, - I::Item: AsRef - { + /// Like `mozilla_modern`, but does not load the certificate chain and private key. + pub fn mozilla_modern_raw(method: SslMethod) -> Result { let mut ctx = try!(ctx(method)); try!(setup_curves(&mut ctx)); try!(ctx.set_cipher_list("ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ @@ -166,10 +190,10 @@ impl SslAcceptorBuilder { ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\ ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\ ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256")); - SslAcceptorBuilder::finish_setup(ctx, private_key, certificate, chain) + Ok(SslAcceptorBuilder(ctx)) } - fn finish_setup(mut ctx: SslContextBuilder, + fn finish_setup(mut self, private_key: &PKeyRef, certificate: &X509Ref, chain: I) @@ -177,13 +201,13 @@ impl SslAcceptorBuilder { where I: IntoIterator, I::Item: AsRef { - try!(ctx.set_private_key(private_key)); - try!(ctx.set_certificate(certificate)); - try!(ctx.check_private_key()); + try!(self.0.set_private_key(private_key)); + try!(self.0.set_certificate(certificate)); + try!(self.0.check_private_key()); for cert in chain { - try!(ctx.add_extra_chain_cert(cert.as_ref().to_owned())); + try!(self.0.add_extra_chain_cert(cert.as_ref().to_owned())); } - Ok(SslAcceptorBuilder(ctx)) + Ok(self) } /// Returns a shared reference to the inner `SslContextBuilder`. @@ -202,28 +226,12 @@ impl SslAcceptorBuilder { } } -#[cfg(ossl101)] -fn get_dh() -> Result { - Dh::from_pem(DHPARAM_PEM.as_bytes()) -} - -#[cfg(not(ossl101))] -fn get_dh() -> Result { - use ffi; - - use cvt_p; - use types::OpenSslType; - - // manually call into ffi to avoid forcing the features - unsafe { cvt_p(ffi::DH_get_2048_256()).map(|p| Dh::from_ptr(p)) } -} - #[cfg(ossl101)] fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> { - use ec_key::EcKey; + use ec::EcKey; use nid; - let curve = try!(EcKey::new_by_curve_name(nid::X9_62_PRIME256V1)); + let curve = try!(EcKey::from_curve_name(nid::X9_62_PRIME256V1)); ctx.set_tmp_ecdh(&curve) } @@ -241,6 +249,7 @@ fn setup_curves(_: &mut SslContextBuilder) -> Result<(), ErrorStack> { /// /// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL /// structures, configuring cipher suites, session options, and more. +#[derive(Clone)] pub struct SslAcceptor(SslContext); impl SslAcceptor { @@ -255,7 +264,9 @@ impl SslAcceptor { #[cfg(any(ossl102, ossl110))] fn setup_verify(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> { - ssl.set_verify(SSL_VERIFY_PEER); + // pass a noop closure in here to ensure that we consistently override any callback on the + // context + ssl.set_verify_callback(SSL_VERIFY_PEER, |p, _| p); let param = ssl._param_mut(); param.set_hostflags(::verify::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); param.set_host(domain) @@ -380,7 +391,7 @@ mod verify { // the same thing we do here. // // The Public Suffix (https://www.publicsuffix.org/) list could - // potentically be used here, but it's both huge and updated frequently + // potentially be used here, but it's both huge and updated frequently // enough that management would be a PITA. if dot_idxs.next().is_none() { return None; @@ -437,15 +448,3 @@ mod verify { } } } - -#[cfg(test)] -mod test { - #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] - #[test] - fn check_dhparam() { - use dh::Dh; - - let expected = String::from_utf8(Dh::get_2048_256().unwrap().to_pem().unwrap()).unwrap(); - assert_eq!(expected.trim(), super::DHPARAM_PEM.trim()); - } -} diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 7060433f54970f0597fe294216ed95b091d36327..5a65aa77caea4fd543973c57b4f7074319666a5e 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -71,6 +71,7 @@ //! } //! ``` use ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_int, c_void, c_long, c_ulong}; use libc::{c_uchar, c_uint}; use std::any::Any; @@ -84,6 +85,7 @@ use std::io::prelude::*; use std::marker::PhantomData; use std::mem; use std::ops::{Deref, DerefMut}; +use std::panic::resume_unwind; use std::path::Path; use std::ptr; use std::slice; @@ -91,16 +93,18 @@ use std::str; use std::sync::Mutex; use {init, cvt, cvt_p}; -use dh::DhRef; -use ec_key::EcKeyRef; +use dh::{Dh, DhRef}; +use ec::EcKeyRef; +#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))] +use ec::EcKey; use x509::{X509StoreContextRef, X509FileType, X509, X509Ref, X509VerifyError, X509Name}; +use x509::store::{X509StoreBuilderRef, X509StoreRef}; #[cfg(any(ossl102, ossl110))] use verify::X509VerifyParamRef; use pkey::PKeyRef; use error::ErrorStack; -use types::{OpenSslType, OpenSslTypeRef}; use util::Opaque; -use stack::Stack; +use stack::{Stack, StackRef}; mod error; mod connector; @@ -162,8 +166,11 @@ bitflags! { const SSL_MODE_AUTO_RETRY = ffi::SSL_MODE_AUTO_RETRY, const SSL_MODE_NO_AUTO_CHAIN = ffi::SSL_MODE_NO_AUTO_CHAIN, const SSL_MODE_RELEASE_BUFFERS = ffi::SSL_MODE_RELEASE_BUFFERS, + #[cfg(not(libressl))] const SSL_MODE_SEND_CLIENTHELLO_TIME = ffi::SSL_MODE_SEND_CLIENTHELLO_TIME, + #[cfg(not(libressl))] const SSL_MODE_SEND_SERVERHELLO_TIME = ffi::SSL_MODE_SEND_SERVERHELLO_TIME, + #[cfg(not(libressl))] const SSL_MODE_SEND_FALLBACK_SCSV = ffi::SSL_MODE_SEND_FALLBACK_SCSV, } } @@ -210,6 +217,22 @@ bitflags! { } } +#[derive(Copy, Clone)] +pub struct StatusType(c_int); + +impl StatusType { + pub fn from_raw(raw: c_int) -> StatusType { + StatusType(raw) + } + + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// An OSCP status. +pub const STATUS_TYPE_OCSP: StatusType = StatusType(ffi::TLSEXT_STATUSTYPE_ocsp); + lazy_static! { static ref INDEXES: Mutex> = Mutex::new(HashMap::new()); static ref SSL_INDEXES: Mutex> = Mutex::new(HashMap::new()); @@ -218,11 +241,11 @@ lazy_static! { // 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 { +fn get_callback_idx() -> c_int { *INDEXES.lock().unwrap().entry(TypeId::of::()).or_insert_with(|| get_new_idx::()) } -fn get_ssl_verify_data_idx() -> c_int { +fn get_ssl_callback_idx() -> c_int { *SSL_INDEXES.lock().unwrap().entry(TypeId::of::()).or_insert_with(|| get_new_ssl_idx::()) } @@ -271,7 +294,7 @@ extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_ 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 as *const _); - let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::()); + let verify = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_callback_idx::()); let verify: &F = &*(verify as *mut F); let ctx = X509StoreContextRef::from_ptr(x509_ctx); @@ -286,7 +309,7 @@ extern "C" fn ssl_raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_ST 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 verify = ffi::SSL_get_ex_data(ssl as *const _, get_ssl_verify_data_idx::()); + let verify = ffi::SSL_get_ex_data(ssl as *const _, get_ssl_callback_idx::()); let verify: &F = &*(verify as *mut F); let ctx = X509StoreContextRef::from_ptr(x509_ctx); @@ -300,7 +323,7 @@ extern "C" fn raw_sni(ssl: *mut ffi::SSL, al: *mut c_int, _arg: *mut c_void) { unsafe { let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); - let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_verify_data_idx::()); + let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_callback_idx::()); let callback: &F = &*(callback as *mut F); let ssl = SslRef::from_ptr_mut(ssl); @@ -372,6 +395,133 @@ extern "C" fn raw_alpn_select_cb(ssl: *mut ffi::SSL, unsafe { select_proto_using(ssl, out as *mut _, outlen, inbuf, inlen, *ALPN_PROTOS_IDX) } } +unsafe extern fn raw_tmp_dh(ssl: *mut ffi::SSL, + is_export: c_int, + keylength: c_int) + -> *mut ffi::DH + where F: Fn(&mut SslRef, bool, u32) -> Result + Any + 'static + Sync + Send +{ + let ctx = ffi::SSL_get_SSL_CTX(ssl); + let callback = ffi::SSL_CTX_get_ex_data(ctx, get_callback_idx::()); + let callback = &*(callback as *mut F); + + let ssl = SslRef::from_ptr_mut(ssl); + match callback(ssl, is_export != 0, keylength as u32) { + Ok(dh) => { + let ptr = dh.as_ptr(); + mem::forget(dh); + ptr + } + Err(_) => { + // FIXME reset error stack + ptr::null_mut() + } + } +} + +#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))] +unsafe extern fn raw_tmp_ecdh(ssl: *mut ffi::SSL, + is_export: c_int, + keylength: c_int) + -> *mut ffi::EC_KEY + where F: Fn(&mut SslRef, bool, u32) -> Result + Any + 'static + Sync + Send +{ + let ctx = ffi::SSL_get_SSL_CTX(ssl); + let callback = ffi::SSL_CTX_get_ex_data(ctx, get_callback_idx::()); + let callback = &*(callback as *mut F); + + let ssl = SslRef::from_ptr_mut(ssl); + match callback(ssl, is_export != 0, keylength as u32) { + Ok(ec_key) => { + let ptr = ec_key.as_ptr(); + mem::forget(ec_key); + ptr + } + Err(_) => { + // FIXME reset error stack + ptr::null_mut() + } + } +} + +unsafe extern fn raw_tmp_dh_ssl(ssl: *mut ffi::SSL, + is_export: c_int, + keylength: c_int) + -> *mut ffi::DH + where F: Fn(&mut SslRef, bool, u32) -> Result + Any + 'static + Sync + Send +{ + let callback = ffi::SSL_get_ex_data(ssl, get_ssl_callback_idx::()); + let callback = &*(callback as *mut F); + + let ssl = SslRef::from_ptr_mut(ssl); + match callback(ssl, is_export != 0, keylength as u32) { + Ok(dh) => { + let ptr = dh.as_ptr(); + mem::forget(dh); + ptr + } + Err(_) => { + // FIXME reset error stack + ptr::null_mut() + } + } +} + +#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))] +unsafe extern fn raw_tmp_ecdh_ssl(ssl: *mut ffi::SSL, + is_export: c_int, + keylength: c_int) + -> *mut ffi::EC_KEY + where F: Fn(&mut SslRef, bool, u32) -> Result + Any + 'static + Sync + Send +{ + let callback = ffi::SSL_get_ex_data(ssl, get_ssl_callback_idx::()); + let callback = &*(callback as *mut F); + + let ssl = SslRef::from_ptr_mut(ssl); + match callback(ssl, is_export != 0, keylength as u32) { + Ok(ec_key) => { + let ptr = ec_key.as_ptr(); + mem::forget(ec_key); + ptr + } + Err(_) => { + // FIXME reset error stack + ptr::null_mut() + } + } +} + +unsafe extern fn raw_tlsext_status(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int + where F: Fn(&mut SslRef) -> Result + Any + 'static + Sync + Send +{ + let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl as *const _); + let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_callback_idx::()); + let callback = &*(callback as *mut F); + + let ssl = SslRef::from_ptr_mut(ssl); + let ret = callback(ssl); + + if ssl.is_server() { + match ret { + Ok(true) => ffi::SSL_TLSEXT_ERR_OK, + Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK, + Err(_) => { + // FIXME reset error stack + ffi::SSL_TLSEXT_ERR_ALERT_FATAL + } + } + } else { + match ret { + Ok(true) => 1, + Ok(false) => 0, + Err(_) => { + // FIXME reset error stack + -1 + } + } + } +} + /// The function is given as the callback to `SSL_CTX_set_next_protos_advertised_cb`. /// /// It causes the parameter `out` to point at a `*const c_uchar` instance that @@ -471,7 +621,7 @@ impl SslContextBuilder { unsafe { let verify = Box::new(verify); ffi::SSL_CTX_set_ex_data(self.as_ptr(), - get_verify_data_idx::(), + get_callback_idx::(), mem::transmute(verify)); ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::)); } @@ -487,7 +637,7 @@ impl SslContextBuilder { unsafe { let callback = Box::new(callback); ffi::SSL_CTX_set_ex_data(self.as_ptr(), - get_verify_data_idx::(), + get_callback_idx::(), mem::transmute(callback)); let f: extern "C" fn(_, _, _) -> _ = raw_sni::; let f: extern "C" fn() = mem::transmute(f); @@ -519,10 +669,38 @@ impl SslContextBuilder { unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } } + pub fn set_tmp_dh_callback(&mut self, callback: F) + where F: Fn(&mut SslRef, bool, u32) -> Result + Any + 'static + Sync + Send + { + unsafe { + let callback = Box::new(callback); + ffi::SSL_CTX_set_ex_data(self.as_ptr(), + get_callback_idx::(), + Box::into_raw(callback) as *mut c_void); + let f: unsafe extern fn (_, _, _) -> _ = raw_tmp_dh::; + ffi::SSL_CTX_set_tmp_dh_callback(self.as_ptr(), f); + } + } + pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } } + /// Requires the `v101` feature and OpenSSL 1.0.1, or the `v102` feature and OpenSSL 1.0.2. + #[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))] + pub fn set_tmp_ecdh_callback(&mut self, callback: F) + where F: Fn(&mut SslRef, bool, u32) -> Result + Any + 'static + Sync + Send + { + unsafe { + let callback = Box::new(callback); + ffi::SSL_CTX_set_ex_data(self.as_ptr(), + get_callback_idx::(), + Box::into_raw(callback) as *mut c_void); + let f: unsafe extern fn(_, _, _) -> _ = raw_tmp_ecdh::; + ffi::SSL_CTX_set_tmp_ecdh_callback(self.as_ptr(), f); + } + } + /// Use the default locations of trusted certificates for verification. /// /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` @@ -571,7 +749,7 @@ impl SslContextBuilder { } } - /// Specifies the file that contains certificate + /// Loads a certificate from a file. pub fn set_certificate_file>(&mut self, file: P, file_type: X509FileType) @@ -585,7 +763,11 @@ impl SslContextBuilder { } } - /// Specifies the file that contains certificate chain + /// Loads a certificate chain from a file. + /// + /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf + /// certificate, and the remainder forming the chain of certificates up to and including the + /// trusted root certificate. pub fn set_certificate_chain_file>(&mut self, file: P) -> Result<(), ErrorStack> { @@ -596,13 +778,15 @@ impl SslContextBuilder { } } - /// Specifies the certificate + /// Sets the certificate. pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr())).map(|_| ()) } } - /// Adds a certificate to the certificate chain presented together with the - /// certificate specified using set_certificate() + /// Appends a certificate to the certificate chain. + /// + /// This chain should contain all certificates necessary to go from the certificate specified by + /// `set_certificate` to a trusted root. pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { unsafe { try!(cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)); @@ -611,7 +795,7 @@ impl SslContextBuilder { } } - /// Specifies the file that contains private key + /// Loads the private key from a file. pub fn set_private_key_file>(&mut self, file: P, file_type: X509FileType) @@ -625,11 +809,14 @@ impl SslContextBuilder { } } - /// Specifies the private key + /// Sets the private key. pub fn set_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr())).map(|_| ()) } } + /// Sets the cipher configuration. + /// + /// See `man 1 ciphers` for details on the format. pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { let cipher_list = CString::new(cipher_list).unwrap(); unsafe { @@ -638,17 +825,15 @@ impl SslContextBuilder { } } - /// If `onoff` is set to `true`, enable ECDHE for key exchange with - /// compatible clients, and automatically select an appropriate elliptic - /// curve. + /// Enables ECDHE key exchange with an automatically chosen curve list. /// /// Requires the `v102` feature and OpenSSL 1.0.2. - #[cfg(all(feature = "v102", ossl102))] + #[cfg(all(feature = "v102", any(ossl102, libressl)))] pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { self._set_ecdh_auto(onoff) } - #[cfg(ossl102)] + #[cfg(any(ossl102,libressl))] fn _set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { unsafe { cvt(ffi::SSL_CTX_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) } } @@ -739,6 +924,41 @@ impl SslContextBuilder { unsafe { cvt(ffi::SSL_CTX_check_private_key(self.as_ptr())).map(|_| ()) } } + /// Returns a shared reference to the context's certificate store. + pub fn cert_store(&self) -> &X509StoreBuilderRef { + unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } + } + + /// Returns a mutable reference to the context's certificate store. + pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef { + unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } + } + + /// Sets the callback dealing with OCSP stapling. + /// + /// On the client side, this callback is responsible for validating the OCSP status response + /// returned by the server. The status may be retrieved with the `SslRef::ocsp_status` method. + /// A response of `Ok(true)` indicates that the OCSP status is valid, and a response of + /// `Ok(false)` indicates that the OCSP status is invalid and the handshake should be + /// terminated. + /// + /// On the server side, this callback is resopnsible for setting the OCSP status response to be + /// returned to clients. The status may be set with the `SslRef::set_ocsp_status` method. A + /// response of `Ok(true)` indicates that the OCSP status should be returned to the client, and + /// `Ok(false)` indicates that the status should not be returned to the client. + pub fn set_status_callback(&mut self, callback: F) -> Result<(), ErrorStack> + where F: Fn(&mut SslRef) -> Result + Any + 'static + Sync + Send + { + unsafe { + let callback = Box::new(callback); + ffi::SSL_CTX_set_ex_data(self.as_ptr(), + get_callback_idx::(), + Box::into_raw(callback) as *mut c_void); + let f: unsafe extern fn (_, _) -> _ = raw_tlsext_status::; + cvt(ffi::SSL_CTX_set_tlsext_status_cb(self.as_ptr(), Some(f)) as c_int).map(|_| ()) + } + } + pub fn build(self) -> SslContext { let ctx = SslContext(self.0); mem::forget(self); @@ -746,7 +966,13 @@ impl SslContextBuilder { } } -type_!(SslContext, SslContextRef, ffi::SSL_CTX, ffi::SSL_CTX_free); +foreign_type! { + type CType = ffi::SSL_CTX; + fn drop = ffi::SSL_CTX_free; + + pub struct SslContext; + pub struct SslContextRef; +} unsafe impl Send for SslContext {} unsafe impl Sync for SslContext {} @@ -803,6 +1029,22 @@ impl SslContextRef { } } } + + /// Returns the certificate store used for verification. + pub fn cert_store(&self) -> &X509StoreRef { + unsafe { + X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) + } + } + + pub fn extra_chain_certs(&self) -> &StackRef { + unsafe { + let mut chain = ptr::null_mut(); + ffi::SSL_CTX_get_extra_chain_certs(self.as_ptr(), &mut chain); + assert!(!chain.is_null()); + StackRef::from_ptr(chain) + } + } } pub struct CipherBits { @@ -815,14 +1057,16 @@ pub struct CipherBits { pub struct SslCipher(*mut ffi::SSL_CIPHER); -impl OpenSslType for SslCipher { +impl ForeignType for SslCipher { type CType = ffi::SSL_CIPHER; type Ref = SslCipherRef; + #[inline] unsafe fn from_ptr(ptr: *mut ffi::SSL_CIPHER) -> SslCipher { SslCipher(ptr) } + #[inline] fn as_ptr(&self) -> *mut ffi::SSL_CIPHER { self.0 } @@ -844,7 +1088,7 @@ impl DerefMut for SslCipher { pub struct SslCipherRef(Opaque); -impl OpenSslTypeRef for SslCipherRef { +impl ForeignTypeRef for SslCipherRef { type CType = ffi::SSL_CIPHER; } @@ -892,7 +1136,44 @@ impl SslCipherRef { } } -type_!(Ssl, SslRef, ffi::SSL, ffi::SSL_free); +foreign_type! { + type CType = ffi::SSL_SESSION; + fn drop = ffi::SSL_SESSION_free; + + pub struct SslSession; + pub struct SslSessionRef; +} + +impl SslSessionRef { + /// Returns the SSL session ID. + pub fn id(&self) -> &[u8] { + unsafe { + let mut len = 0; + let p = ffi::SSL_SESSION_get_id(self.as_ptr(), &mut len); + slice::from_raw_parts(p as *const u8, len as usize) + } + } + + /// Returns the length of the master key. + pub fn master_key_len(&self) -> usize { + unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) } + } + + /// Copies the master key into the provided buffer. + /// + /// Returns the number of bytes written. + pub fn master_key(&self, buf: &mut [u8]) -> usize { + unsafe { compat::SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } + } +} + +foreign_type! { + type CType = ffi::SSL; + fn drop = ffi::SSL_free; + + pub struct Ssl; + pub struct SslRef; +} impl fmt::Debug for SslRef { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { @@ -944,12 +1225,58 @@ impl SslRef { unsafe { let verify = Box::new(verify); ffi::SSL_set_ex_data(self.as_ptr(), - get_ssl_verify_data_idx::(), + get_ssl_callback_idx::(), mem::transmute(verify)); ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, Some(ssl_raw_verify::)); } } + pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } + } + + pub fn set_tmp_dh_callback(&mut self, callback: F) + where F: Fn(&mut SslRef, bool, u32) -> Result + Any + 'static + Sync + Send + { + unsafe { + let callback = Box::new(callback); + ffi::SSL_set_ex_data(self.as_ptr(), + get_ssl_callback_idx::(), + Box::into_raw(callback) as *mut c_void); + let f: unsafe extern fn (_, _, _) -> _ = raw_tmp_dh_ssl::; + ffi::SSL_set_tmp_dh_callback(self.as_ptr(), f); + } + } + + pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } + } + + /// Requires the `v101` feature and OpenSSL 1.0.1, or the `v102` feature and OpenSSL 1.0.2. + #[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))] + pub fn set_tmp_ecdh_callback(&mut self, callback: F) + where F: Fn(&mut SslRef, bool, u32) -> Result + Any + 'static + Sync + Send + { + unsafe { + let callback = Box::new(callback); + ffi::SSL_set_ex_data(self.as_ptr(), + get_ssl_callback_idx::(), + Box::into_raw(callback) as *mut c_void); + let f: unsafe extern fn(_, _, _) -> _ = raw_tmp_ecdh_ssl::; + ffi::SSL_set_tmp_ecdh_callback(self.as_ptr(), f); + } + } + + /// If `onoff` is set to `true`, enable ECDHE for key exchange with + /// compatible clients, and automatically select an appropriate elliptic + /// curve. + /// + /// Requires the `v102` feature and OpenSSL 1.0.2. + #[cfg(all(feature = "v102", ossl102))] + pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) } + } + pub fn current_cipher(&self) -> Option<&SslCipherRef> { unsafe { let ptr = ffi::SSL_get_current_cipher(self.as_ptr()); @@ -1151,6 +1478,61 @@ impl SslRef { pub fn verify_result(&self) -> Option { unsafe { X509VerifyError::from_raw(ffi::SSL_get_verify_result(self.as_ptr())) } } + + /// Returns the SSL session. + pub fn session(&self) -> Option<&SslSessionRef> { + unsafe { + let p = ffi::SSL_get_session(self.as_ptr()); + if p.is_null() { + None + } else { + Some(SslSessionRef::from_ptr(p)) + } + } + } + + /// Sets the status response a client wishes the server to reply with. + pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ()) + } + } + + /// Returns the server's OCSP response, if present. + pub fn ocsp_status(&self) -> Option<&[u8]> { + unsafe { + let mut p = ptr::null_mut(); + let len = ffi::SSL_get_tlsext_status_ocsp_resp(self.as_ptr(), &mut p); + + if len < 0 { + None + } else { + Some(slice::from_raw_parts(p as *const u8, len as usize)) + } + } + } + + /// Sets the OCSP response to be returned to the client. + pub fn set_ocsp_status(&mut self, response: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(response.len() <= c_int::max_value() as usize); + let p = try!(cvt_p(ffi::CRYPTO_malloc(response.len() as _, + concat!(file!(), "\0").as_ptr() as *const _, + line!() as c_int))); + ptr::copy_nonoverlapping(response.as_ptr(), p as *mut u8, response.len()); + cvt(ffi::SSL_set_tlsext_status_ocsp_resp(self.as_ptr(), + p as *mut c_uchar, + response.len() as c_long) as c_int) + .map(|_| ()) + } + } + + /// Determines if this `Ssl` is configured for server-side or client-side use. + pub fn is_server(&self) -> bool { + unsafe { + compat::SSL_is_server(self.as_ptr()) != 0 + } + } } unsafe impl Sync for Ssl {} @@ -1326,11 +1708,24 @@ impl SslStream { /// This is particularly useful with a nonblocking socket, where the error /// value will identify if OpenSSL is waiting on read or write readiness. pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result { + // The intepretation of the return code here is a little odd with a + // zero-length write. OpenSSL will likely correctly report back to us + // that it read zero bytes, but zero is also the sentinel for "error". + // To avoid that confusion short-circuit that logic and return quickly + // if `buf` has a length of zero. + if buf.len() == 0 { + return Ok(0) + } + let ret = self.ssl.read(buf); - if ret >= 0 { + if ret > 0 { Ok(ret as usize) } else { - Err(self.make_error(ret)) + match self.make_error(ret) { + // Don't treat unexpected EOFs as errors when reading + Error::Stream(ref e) if e.kind() == io::ErrorKind::ConnectionAborted => Ok(0), + e => Err(e), + } } } @@ -1339,8 +1734,13 @@ impl SslStream { /// This is particularly useful with a nonblocking socket, where the error /// value will identify if OpenSSL is waiting on read or write readiness. pub fn ssl_write(&mut self, buf: &[u8]) -> Result { + // See above for why we short-circuit on zero-length buffers + if buf.len() == 0 { + return Ok(0) + } + let ret = self.ssl.write(buf); - if ret >= 0 { + if ret > 0 { Ok(ret as usize) } else { Err(self.make_error(ret)) @@ -1377,19 +1777,38 @@ impl SslStream { ffi::SSL_ERROR_SYSCALL => { let errs = ErrorStack::get(); if errs.errors().is_empty() { - if ret == 0 { - Error::Stream(io::Error::new(io::ErrorKind::ConnectionAborted, - "unexpected EOF observed")) - } else { - Error::Stream(self.get_bio_error()) + match self.get_bio_error() { + Some(err) => Error::Stream(err), + None => { + Error::Stream(io::Error::new(io::ErrorKind::ConnectionAborted, + "unexpected EOF observed")) + } } } else { Error::Ssl(errs) } } ffi::SSL_ERROR_ZERO_RETURN => Error::ZeroReturn, - ffi::SSL_ERROR_WANT_WRITE => Error::WantWrite(self.get_bio_error()), - ffi::SSL_ERROR_WANT_READ => Error::WantRead(self.get_bio_error()), + ffi::SSL_ERROR_WANT_WRITE => { + let err = match self.get_bio_error() { + Some(err) => err, + None => { + io::Error::new(io::ErrorKind::Other, + "BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO") + } + }; + Error::WantWrite(err) + }, + ffi::SSL_ERROR_WANT_READ => { + let err = match self.get_bio_error() { + Some(err) => err, + None => { + io::Error::new(io::ErrorKind::Other, + "BUG: got an SSL_ERROR_WANT_WRITE with no error in the BIO") + } + }; + Error::WantRead(err) + }, err => { Error::Stream(io::Error::new(io::ErrorKind::InvalidData, format!("unexpected error {}", err))) @@ -1399,19 +1818,12 @@ impl SslStream { fn check_panic(&mut self) { if let Some(err) = unsafe { bio::take_panic::(self.ssl.get_raw_rbio()) } { - ::std::panic::resume_unwind(err) + resume_unwind(err) } } - fn get_bio_error(&mut self) -> io::Error { - let error = unsafe { bio::take_error::(self.ssl.get_raw_rbio()) }; - match error { - Some(error) => error, - None => { - io::Error::new(io::ErrorKind::Other, - "BUG: got an ErrorSyscall without an error in the BIO?") - } - } + fn get_bio_error(&mut self) -> Option { + unsafe { bio::take_error::(self.ssl.get_raw_rbio()) } } /// Returns a reference to the underlying stream. @@ -1488,8 +1900,8 @@ mod compat { use ffi; use libc::c_int; - pub use ffi::{SSL_CTX_get_options, SSL_CTX_set_options}; - pub use ffi::{SSL_CTX_clear_options, SSL_CTX_up_ref}; + pub use ffi::{SSL_CTX_get_options, SSL_CTX_set_options, SSL_CTX_clear_options, SSL_CTX_up_ref, + SSL_SESSION_get_master_key, SSL_is_server}; pub unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { ffi::CRYPTO_get_ex_new_index(ffi::CRYPTO_EX_INDEX_SSL_CTX, @@ -1524,7 +1936,7 @@ mod compat { use std::ptr; use ffi; - use libc::{self, c_long, c_ulong, c_int}; + use libc::{self, c_long, c_ulong, c_int, size_t, c_uchar}; pub unsafe fn SSL_CTX_get_options(ctx: *const ffi::SSL_CTX) -> c_ulong { ffi::SSL_CTX_ctrl(ctx as *mut _, ffi::SSL_CTRL_OPTIONS, 0, ptr::null_mut()) as c_ulong @@ -1561,6 +1973,19 @@ mod compat { 0 } + pub unsafe fn SSL_SESSION_get_master_key(session: *const ffi::SSL_SESSION, + out: *mut c_uchar, + mut outlen: size_t) -> size_t { + if outlen == 0 { + return (*session).master_key_length as size_t; + } + if outlen > (*session).master_key_length as size_t { + outlen = (*session).master_key_length as size_t; + } + ptr::copy_nonoverlapping((*session).master_key.as_ptr(), out, outlen); + outlen + } + pub fn tls_method() -> *const ffi::SSL_METHOD { unsafe { ffi::SSLv23_method() } } @@ -1568,4 +1993,8 @@ mod compat { pub fn dtls_method() -> *const ffi::SSL_METHOD { unsafe { ffi::DTLSv1_method() } } + + pub unsafe fn SSL_is_server(s: *mut ffi::SSL) -> c_int { + (*s).server + } } diff --git a/openssl/src/ssl/tests/mod.rs b/openssl/src/ssl/tests/mod.rs index 146d0806db8d4986ebe564acaae25afbec61a7ac..9c00e3eda493512683d896b0b94ce1cfc2df928b 100644 --- a/openssl/src/ssl/tests/mod.rs +++ b/openssl/src/ssl/tests/mod.rs @@ -9,17 +9,18 @@ use std::mem; use std::net::{TcpStream, TcpListener, SocketAddr}; use std::path::Path; use std::process::{Command, Child, Stdio, ChildStdin}; +use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; use std::thread; use std::time::Duration; - use tempdir::TempDir; +use dh::Dh; use hash::MessageDigest; +use ocsp::{OcspResponse, RESPONSE_STATUS_UNAUTHORIZED}; use ssl; -use ssl::SSL_VERIFY_PEER; -use ssl::{SslMethod, HandshakeError}; -use ssl::{SslContext, SslStream, Ssl, ShutdownResult, SslConnectorBuilder, SslAcceptorBuilder, - Error}; +use ssl::{SslMethod, HandshakeError, SslContext, SslStream, Ssl, ShutdownResult, + SslConnectorBuilder, SslAcceptorBuilder, Error, SSL_VERIFY_PEER, SSL_VERIFY_NONE, + STATUS_TYPE_OCSP}; use x509::{X509StoreContext, X509, X509Name, X509_FILETYPE_PEM}; #[cfg(any(all(feature = "v102", ossl102), all(feature = "v110", ossl110)))] use x509::verify::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; @@ -29,6 +30,7 @@ use std::net::UdpSocket; mod select; +static ROOT_CERT: &'static [u8] = include_bytes!("../../../test/root-ca.pem"); static CERT: &'static [u8] = include_bytes!("../../../test/cert.pem"); static KEY: &'static [u8] = include_bytes!("../../../test/key.pem"); @@ -98,6 +100,7 @@ impl Server { Server::new_tcp(&["-www"]) } + #[allow(dead_code)] fn new_alpn() -> (Server, TcpStream) { Server::new_tcp(&["-www", "-nextprotoneg", @@ -170,8 +173,8 @@ macro_rules! run_test( use ssl::SSL_VERIFY_PEER; use hash::MessageDigest; use x509::X509StoreContext; - use serialize::hex::FromHex; - use types::OpenSslTypeRef; + use hex::FromHex; + use foreign_types::ForeignTypeRef; use super::Server; #[test] @@ -181,7 +184,7 @@ macro_rules! run_test( } #[test] - #[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467) + #[cfg_attr(any(libressl, windows, target_arch = "arm"), ignore)] // FIXME(#467) fn dtlsv1() { let (_s, stream) = Server::new_dtlsv1(Some("hello")); $blk(SslMethod::dtls(), stream); @@ -302,7 +305,7 @@ run_test!(verify_callback_data, |method, stream| { // 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 = "59172d9313e84459bcff27f967e79e6e9217e584"; - let node_id = node_hash_str.from_hex().unwrap(); + let node_id = Vec::from_hex(node_hash_str).unwrap(); ctx.set_verify_callback(SSL_VERIFY_PEER, move |_preverify_ok, x509_ctx| { let cert = x509_ctx.current_cert(); match cert { @@ -330,7 +333,7 @@ run_test!(ssl_verify_callback, |method, stream| { let mut ssl = Ssl::new(&ctx.build()).unwrap(); let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; - let node_id = node_hash_str.from_hex().unwrap(); + let node_id = Vec::from_hex(node_hash_str).unwrap(); ssl.set_verify_callback(SSL_VERIFY_PEER, move |_, x509| { CHECKED.store(1, Ordering::SeqCst); match x509.current_cert() { @@ -421,18 +424,28 @@ fn test_write() { stream.flush().unwrap(); } +#[test] +fn zero_length_buffers() { + let (_s, stream) = Server::new(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let mut stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); + + assert_eq!(stream.write(b"").unwrap(), 0); + assert_eq!(stream.read(&mut []).unwrap(), 0); +} + run_test!(get_peer_certificate, |method, stream| { let ctx = SslContext::builder(method).unwrap(); let stream = Ssl::new(&ctx.build()).unwrap().connect(stream).unwrap(); let cert = stream.ssl().peer_certificate().unwrap(); let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); let node_hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; - let node_id = node_hash_str.from_hex().unwrap(); + let node_id = Vec::from_hex(node_hash_str).unwrap(); assert_eq!(node_id, fingerprint) }); #[test] -#[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467) +#[cfg_attr(any(libressl, windows, target_arch = "arm"), ignore)] // FIXME(#467) fn test_write_dtlsv1() { let (_s, stream) = Server::new_dtlsv1(iter::repeat("y\n")); let ctx = SslContext::builder(SslMethod::dtls()).unwrap(); @@ -771,7 +784,7 @@ fn test_alpn_server_select_none() { } #[test] -#[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467) +#[cfg_attr(any(libressl, windows, target_arch = "arm"), ignore)] // FIXME(#467) fn test_read_dtlsv1() { let (_s, stream) = Server::new_dtlsv1(Some("hello")); @@ -849,7 +862,7 @@ fn test_write_nonblocking() { } #[test] -#[cfg_attr(any(windows, target_arch = "arm"), ignore)] // FIXME(#467) +#[cfg_attr(any(libressl, windows, target_arch = "arm"), ignore)] // FIXME(#467) fn test_read_nonblocking() { let (_s, stream) = Server::new(); stream.set_nonblocking(true).unwrap(); @@ -1088,6 +1101,36 @@ fn connector_invalid_hostname() { assert!(connector.connect("foobar.com", s).is_err()); } +#[test] +fn connector_invalid_no_hostname_verification() { + let connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build(); + + let s = TcpStream::connect("google.com:443").unwrap(); + connector.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(s) + .unwrap(); +} + +#[test] +fn connector_no_hostname_still_verifies() { + let (_s, tcp) = Server::new(); + + let connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build(); + + assert!(connector.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(tcp) + .is_err()); +} + +#[test] +fn connector_no_hostname_can_disable_verify() { + let (_s, tcp) = Server::new(); + + let mut connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap(); + connector.builder_mut().set_verify(SSL_VERIFY_NONE); + let connector = connector.build(); + + connector.danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(tcp).unwrap(); +} + #[test] fn connector_client_server_mozilla_intermediate() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); @@ -1193,6 +1236,210 @@ fn client_ca_list() { ctx.set_client_ca_list(names); } +#[test] +fn cert_store() { + let (_s, tcp) = Server::new(); + + let cert = X509::from_pem(ROOT_CERT).unwrap(); + + let mut ctx = SslConnectorBuilder::new(SslMethod::tls()).unwrap(); + ctx.builder_mut().cert_store_mut().add_cert(cert).unwrap(); + let ctx = ctx.build(); + + ctx.connect("foobar.com", tcp).unwrap(); +} + +#[test] +fn tmp_dh_callback() { + static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + thread::spawn(move ||{ + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_tmp_dh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + let dh = include_bytes!("../../../test/dhparams.pem"); + Dh::from_pem(dh) + }); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_cipher_list("EDH").unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))] +fn tmp_ecdh_callback() { + use ec::EcKey; + use nid; + + static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + thread::spawn(move ||{ + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_tmp_ecdh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + EcKey::new_by_curve_name(nid::X9_62_PRIME256V1) + }); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_cipher_list("ECDH").unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn tmp_dh_callback_ssl() { + static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + thread::spawn(move ||{ + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_tmp_dh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + let dh = include_bytes!("../../../test/dhparams.pem"); + Dh::from_pem(dh) + }); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_cipher_list("EDH").unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))] +fn tmp_ecdh_callback_ssl() { + use ec::EcKey; + use nid; + + static CALLED_BACK: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + thread::spawn(move ||{ + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_tmp_ecdh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + EcKey::new_by_curve_name(nid::X9_62_PRIME256V1) + }); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_cipher_list("ECDH").unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn idle_session() { + let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); + let ssl = Ssl::new(&ctx).unwrap(); + assert!(ssl.session().is_none()); +} + +#[test] +fn active_session() { + let connector = SslConnectorBuilder::new(SslMethod::tls()).unwrap().build(); + + let s = TcpStream::connect("google.com:443").unwrap(); + let socket = connector.connect("google.com", s).unwrap(); + let session = socket.ssl().session().unwrap(); + let len = session.master_key_len(); + let mut buf = vec![0; len - 1]; + let copied = session.master_key(&mut buf); + assert_eq!(copied, buf.len()); + let mut buf = vec![0; len + 1]; + let copied = session.master_key(&mut buf); + assert_eq!(copied, len); +} + +#[test] +fn status_callbacks() { + static CALLED_BACK_SERVER: AtomicBool = ATOMIC_BOOL_INIT; + static CALLED_BACK_CLIENT: AtomicBool = ATOMIC_BOOL_INIT; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(&Path::new("test/cert.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_private_key_file(&Path::new("test/key.pem"), X509_FILETYPE_PEM).unwrap(); + ctx.set_status_callback(|ssl| { + CALLED_BACK_SERVER.store(true, Ordering::SeqCst); + let response = OcspResponse::create(RESPONSE_STATUS_UNAUTHORIZED, None).unwrap(); + let response = response.to_der().unwrap(); + ssl.set_ocsp_status(&response).unwrap(); + Ok(true) + }).unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.accept(stream).unwrap(); + }); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_status_callback(|ssl| { + CALLED_BACK_CLIENT.store(true, Ordering::SeqCst); + let response = OcspResponse::from_der(ssl.ocsp_status().unwrap()).unwrap(); + assert_eq!(response.status(), RESPONSE_STATUS_UNAUTHORIZED); + Ok(true) + }).unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_status_type(STATUS_TYPE_OCSP).unwrap(); + ssl.connect(stream).unwrap(); + + assert!(CALLED_BACK_SERVER.load(Ordering::SeqCst)); + assert!(CALLED_BACK_CLIENT.load(Ordering::SeqCst)); + + guard.join().unwrap(); +} + fn _check_kinds() { fn is_send() {} fn is_sync() {} diff --git a/openssl/src/stack.rs b/openssl/src/stack.rs index 584ead8c53ed8aa37a28f779e711bdebd158386d..268afde71bc52a12a3299a57296bca4938e92b19 100644 --- a/openssl/src/stack.rs +++ b/openssl/src/stack.rs @@ -1,15 +1,16 @@ -use std::ops::{Deref, DerefMut, Index, IndexMut}; -use std::iter; +use foreign_types::{ForeignTypeRef, ForeignType}; +use libc::c_int; use std::borrow::Borrow; use std::convert::AsRef; +use std::iter; use std::marker::PhantomData; -use libc::c_int; use std::mem; use ffi; use {cvt, cvt_p}; use error::ErrorStack; -use types::{OpenSslType, OpenSslTypeRef}; +use std::ops::{Deref, DerefMut, Index, IndexMut}; + use util::Opaque; #[cfg(ossl10x)] @@ -22,9 +23,8 @@ use ffi::{OPENSSL_sk_pop, OPENSSL_sk_free, OPENSSL_sk_num, OPENSSL_sk_value, OPE /// Trait implemented by types which can be placed in a stack. /// -/// Like `OpenSslType`, it should not be implemented for any type outside -/// of this crate. -pub trait Stackable: OpenSslType { +/// It should not be implemented for any type outside of this crate. +pub trait Stackable: ForeignType { /// The C stack type for this element. /// /// Generally called `stack_st_{ELEMENT_TYPE}`, normally hidden by the @@ -80,14 +80,16 @@ impl Borrow> for Stack { } } -impl OpenSslType for Stack { +impl ForeignType for Stack { type CType = T::StackType; type Ref = StackRef; + #[inline] unsafe fn from_ptr(ptr: *mut T::StackType) -> Stack { Stack(ptr) } + #[inline] fn as_ptr(&self) -> *mut T::StackType { self.0 } @@ -152,7 +154,7 @@ impl ExactSizeIterator for IntoIter {} pub struct StackRef(Opaque, PhantomData); -impl OpenSslTypeRef for StackRef { +impl ForeignTypeRef for StackRef { type CType = T::StackType; } diff --git a/openssl/src/string.rs b/openssl/src/string.rs new file mode 100644 index 0000000000000000000000000000000000000000..4a1d3479593d0b48f97d3a01b0058ede14e39517 --- /dev/null +++ b/openssl/src/string.rs @@ -0,0 +1,80 @@ +use ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_char, c_void}; +use std::fmt; +use std::ffi::CStr; +use std::ops::Deref; +use std::str; + +use stack::Stackable; + +foreign_type! { + type CType = c_char; + fn drop = free; + + pub struct OpensslString; + pub struct OpensslStringRef; +} + +impl OpensslString { + #[deprecated(note = "use from_ptr", since = "0.9.7")] + pub unsafe fn from_raw_parts(buf: *mut u8, _: usize) -> OpensslString { + OpensslString::from_ptr(buf as *mut c_char) + } + + #[deprecated(note = "use from_ptr", since = "0.9.7")] + pub unsafe fn from_null_terminated(buf: *mut c_char) -> OpensslString { + OpensslString::from_ptr(buf) + } +} + +impl fmt::Display for OpensslString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Debug for OpensslString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl Stackable for OpensslString { + type StackType = ffi::stack_st_OPENSSL_STRING; +} + +impl Deref for OpensslStringRef { + type Target = str; + + fn deref(&self) -> &str { + unsafe { + let slice = CStr::from_ptr(self.as_ptr()).to_bytes(); + str::from_utf8_unchecked(slice) + } + } +} + +impl fmt::Display for OpensslStringRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Debug for OpensslStringRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[cfg(not(ossl110))] +unsafe fn free(buf: *mut c_char) { + ::ffi::CRYPTO_free(buf as *mut c_void); +} + +#[cfg(ossl110)] +unsafe fn free(buf: *mut c_char) { + ::ffi::CRYPTO_free(buf as *mut c_void, + concat!(file!(), "\0").as_ptr() as *const c_char, + line!() as ::libc::c_int); +} diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs index d4b15f28124edddf613fa67e421d7d3b7c691c60..5cf1ce0b54be497e6211a954115e48faba7e61cf 100644 --- a/openssl/src/symm.rs +++ b/openssl/src/symm.rs @@ -80,6 +80,22 @@ impl Cipher { unsafe { Cipher(ffi::EVP_aes_256_gcm()) } } + pub fn bf_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_cbc()) } + } + + pub fn bf_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_ecb()) } + } + + pub fn bf_cfb64() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_cfb64()) } + } + + pub fn bf_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_ofb()) } + } + pub fn des_cbc() -> Cipher { unsafe { Cipher(ffi::EVP_des_cbc()) } } @@ -92,6 +108,18 @@ impl Cipher { unsafe { Cipher(ffi::EVP_rc4()) } } + /// Requires the `v110` feature and OpenSSL 1.1.0. + #[cfg(all(ossl110, feature = "v110"))] + pub fn chacha20() -> Cipher { + unsafe { Cipher(ffi::EVP_chacha20()) } + } + + /// Requires the `v110` feature and OpenSSL 1.1.0. + #[cfg(all(ossl110, feature = "v110"))] + pub fn chacha20_poly1305() -> Cipher { + unsafe { Cipher(ffi::EVP_chacha20_poly1305()) } + } + pub unsafe fn from_ptr(ptr: *const ffi::EVP_CIPHER) -> Cipher { Cipher(ptr) } @@ -135,8 +163,7 @@ impl Crypter { /// /// # Panics /// - /// Panics if an IV is required by the cipher but not provided, or if the - /// IV's length does not match the expected length (see `Cipher::iv_len`). + /// Panics if an IV is required by the cipher but not provided. pub fn new(t: Cipher, mode: Mode, key: &[u8], @@ -169,7 +196,13 @@ impl Crypter { let key = key.as_ptr() as *mut _; let iv = match (iv, t.iv_len()) { (Some(iv), Some(len)) => { - assert!(iv.len() == len); + if iv.len() != len { + assert!(iv.len() <= c_int::max_value() as usize); + try!(cvt(ffi::EVP_CIPHER_CTX_ctrl(crypter.ctx, + ffi::EVP_CTRL_GCM_SET_IVLEN, + iv.len() as c_int, + ptr::null_mut()))); + } iv.as_ptr() as *mut _ } (Some(_), None) | (None, None) => ptr::null_mut(), @@ -196,6 +229,39 @@ impl Crypter { } } + /// Sets the tag used to authenticate ciphertext in AEAD ciphers such as AES GCM. + /// + /// When decrypting cipher text using an AEAD cipher, this must be called before `finalize`. + pub fn set_tag(&mut self, tag: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(tag.len() <= c_int::max_value() as usize); + // NB: this constant is actually more general than just GCM. + cvt(ffi::EVP_CIPHER_CTX_ctrl(self.ctx, + ffi::EVP_CTRL_GCM_SET_TAG, + tag.len() as c_int, + tag.as_ptr() as *mut _)) + .map(|_| ()) + } + } + + /// Feeds Additional Authenticated Data (AAD) through the cipher. + /// + /// This can only be used with AEAD ciphers such as AES GCM. Data fed in is not encrypted, but + /// is factored into the authentication tag. It must be called before the first call to + /// `update`. + pub fn aad_update(&mut self, input: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(input.len() <= c_int::max_value() as usize); + let mut len = 0; + cvt(ffi::EVP_CipherUpdate(self.ctx, + ptr::null_mut(), + &mut len, + input.as_ptr(), + input.len() as c_int)) + .map(|_| ()) + } + } + /// Feeds data from `input` through the cipher, writing encrypted/decrypted /// bytes into `output`. /// @@ -244,6 +310,25 @@ impl Crypter { Ok(outl as usize) } } + + /// Retrieves the authentication tag used to authenticate ciphertext in AEAD ciphers such + /// as AES GCM. + /// + /// When encrypting data with an AEAD cipher, this must be called after `finalize`. + /// + /// The size of the buffer indicates the required size of the tag. While some ciphers support a + /// range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16 + /// bytes, for example. + pub fn get_tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(tag.len() <= c_int::max_value() as usize); + cvt(ffi::EVP_CIPHER_CTX_ctrl(self.ctx, + ffi::EVP_CTRL_GCM_GET_TAG, + tag.len() as c_int, + tag.as_mut_ptr() as *mut _)) + .map(|_| ()) + } + } } impl Drop for Crypter { @@ -292,6 +377,52 @@ fn cipher(t: Cipher, Ok(out) } +/// Like `encrypt`, but for AEAD ciphers such as AES GCM. +/// +/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag +/// will be copied into the `tag` field. +/// +/// The size of the `tag` buffer indicates the required size of the tag. While some ciphers support +/// a range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16 bytes, +/// for example. +pub fn encrypt_aead(t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + aad: &[u8], + data: &[u8], + tag: &mut [u8]) + -> Result, ErrorStack> { + let mut c = try!(Crypter::new(t, Mode::Encrypt, key, iv)); + let mut out = vec![0; data.len() + t.block_size()]; + try!(c.aad_update(aad)); + let count = try!(c.update(data, &mut out)); + let rest = try!(c.finalize(&mut out[count..])); + try!(c.get_tag(tag)); + out.truncate(count + rest); + Ok(out) +} + +/// Like `decrypt`, but for AEAD ciphers such as AES GCM. +/// +/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag +/// should be provided in the `tag` field. +pub fn decrypt_aead(t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + aad: &[u8], + data: &[u8], + tag: &[u8]) + -> Result, ErrorStack> { + let mut c = try!(Crypter::new(t, Mode::Decrypt, key, iv)); + let mut out = vec![0; data.len() + t.block_size()]; + try!(c.aad_update(aad)); + let count = try!(c.update(data, &mut out)); + try!(c.set_tag(tag)); + let rest = try!(c.finalize(&mut out[count..])); + out.truncate(count + rest); + Ok(out) +} + #[cfg(ossl110)] use ffi::{EVP_CIPHER_iv_length, EVP_CIPHER_block_size, EVP_CIPHER_key_length}; @@ -318,7 +449,8 @@ use self::compat::*; #[cfg(test)] mod tests { - use serialize::hex::{FromHex, ToHex}; + use hex::{FromHex, ToHex}; + use super::*; // Test vectors from FIPS-197: // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf @@ -385,12 +517,10 @@ mod tests { } fn cipher_test(ciphertype: super::Cipher, pt: &str, ct: &str, key: &str, iv: &str) { - use serialize::hex::ToHex; - - let pt = pt.from_hex().unwrap(); - let ct = ct.from_hex().unwrap(); - let key = key.from_hex().unwrap(); - let iv = iv.from_hex().unwrap(); + let pt = Vec::from_hex(pt).unwrap(); + let ct = Vec::from_hex(ct).unwrap(); + let key = Vec::from_hex(key).unwrap(); + let iv = Vec::from_hex(iv).unwrap(); let computed = super::decrypt(ciphertype, &key, Some(&iv), &ct).unwrap(); let expected = pt; @@ -407,6 +537,35 @@ mod tests { } } + fn cipher_test_nopad(ciphertype: super::Cipher, pt: &str, ct: &str, key: &str, iv: &str) { + let pt = Vec::from_hex(pt).unwrap(); + let ct = Vec::from_hex(ct).unwrap(); + let key = Vec::from_hex(key).unwrap(); + let iv = Vec::from_hex(iv).unwrap(); + + let computed = { + let mut c = Crypter::new(ciphertype, Mode::Decrypt, &key, Some(&iv)).unwrap(); + c.pad(false); + let mut out = vec![0; ct.len() + ciphertype.block_size()]; + let count = c.update(&ct, &mut out).unwrap(); + let rest = c.finalize(&mut out[count..]).unwrap(); + out.truncate(count + rest); + out + }; + let expected = pt; + + if computed != expected { + println!("Computed: {}", computed.to_hex()); + println!("Expected: {}", expected.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() { @@ -513,6 +672,51 @@ mod tests { cipher_test(super::Cipher::aes_256_cfb8(), pt, ct, key, iv); } + #[test] + fn test_bf_cbc() { + // https://www.schneier.com/code/vectors.txt + + let pt = "37363534333231204E6F77206973207468652074696D6520666F722000000000"; + let ct = "6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC"; + let key = "0123456789ABCDEFF0E1D2C3B4A59687"; + let iv = "FEDCBA9876543210"; + + cipher_test_nopad(super::Cipher::bf_cbc(), pt, ct, key, iv); + } + + #[test] + fn test_bf_ecb() { + + let pt = "5CD54CA83DEF57DA"; + let ct = "B1B8CC0B250F09A0"; + let key = "0131D9619DC1376E"; + let iv = "0000000000000000"; + + cipher_test_nopad(super::Cipher::bf_ecb(), pt, ct, key, iv); + } + + #[test] + fn test_bf_cfb64() { + + let pt = "37363534333231204E6F77206973207468652074696D6520666F722000"; + let ct = "E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3"; + let key = "0123456789ABCDEFF0E1D2C3B4A59687"; + let iv = "FEDCBA9876543210"; + + cipher_test_nopad(super::Cipher::bf_cfb64(), pt, ct, key, iv); + } + + #[test] + fn test_bf_ofb() { + + let pt = "37363534333231204E6F77206973207468652074696D6520666F722000"; + let ct = "E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA"; + let key = "0123456789ABCDEFF0E1D2C3B4A59687"; + let iv = "FEDCBA9876543210"; + + cipher_test_nopad(super::Cipher::bf_ofb(), pt, ct, key, iv); + } + #[test] fn test_des_cbc() { @@ -534,4 +738,93 @@ mod tests { cipher_test(super::Cipher::des_ecb(), pt, ct, key, iv); } + + #[test] + fn test_aes128_gcm() { + let key = "0e00c76561d2bd9b40c3c15427e2b08f"; + let iv = + "492cadaccd3ca3fbc9cf9f06eb3325c4e159850b0dbe98199b89b7af528806610b6f63998e1eae80c348e7\ + 4cbb921d8326631631fc6a5d304f39166daf7ea15fa1977f101819adb510b50fe9932e12c5a85aa3fd1e73\ + d8d760af218be829903a77c63359d75edd91b4f6ed5465a72662f5055999e059e7654a8edc921aa0d496"; + let pt = + "fef03c2d7fb15bf0d2df18007d99f967c878ad59359034f7bb2c19af120685d78e32f6b8b83b032019956c\ + a9c0195721476b85"; + let aad = + "d8f1163d8c840292a2b2dacf4ac7c36aff8733f18fabb4fa5594544125e03d1e6e5d6d0fd61656c8d8f327\ + c92839ae5539bb469c9257f109ebff85aad7bd220fdaa95c022dbd0c7bb2d878ad504122c943045d3c5eba\ + 8f1f56c0"; + let ct = + "4f6cf471be7cbd2575cd5a1747aea8fe9dea83e51936beac3e68f66206922060c697ffa7af80ad6bb68f2c\ + f4fc97416ee52abe"; + let tag = "e20b6655"; + + // this tag is smaller than you'd normally want, but I pulled this test from the part of + // the NIST test vectors that cover 4 byte tags. + let mut actual_tag = [0; 4]; + let out = encrypt_aead(Cipher::aes_128_gcm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(pt).unwrap(), + &mut actual_tag) + .unwrap(); + assert_eq!(ct, out.to_hex()); + assert_eq!(tag, actual_tag.to_hex()); + + let out = decrypt_aead(Cipher::aes_128_gcm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap()).unwrap(); + assert_eq!(pt, out.to_hex()); + } + + #[test] + #[cfg(all(ossl110, feature = "v110"))] + fn test_chacha20() { + let key = "0000000000000000000000000000000000000000000000000000000000000000"; + let iv = "00000000000000000000000000000000"; + let pt = "000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 00000000000000000000000000000000000000000000000"; + let ct = "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7\ + 724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"; + + cipher_test(Cipher::chacha20(), pt, ct, key, iv); + } + + #[test] + #[cfg(all(ossl110, feature = "v110"))] + fn test_chacha20_poly1305() { + let key = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"; + let iv = "070000004041424344454647"; + let aad = "50515253c0c1c2c3c4c5c6c7"; + let pt = "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393\ + a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f722074\ + 6865206675747572652c2073756e73637265656e20776f756c642062652069742e"; + let ct = "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca967128\ + 2fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fa\ + b324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116"; + let tag = "1ae10b594f09e26a7e902ecbd0600691"; + + let mut actual_tag = [0; 16]; + let out = encrypt_aead(Cipher::chacha20_poly1305(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(pt).unwrap(), + &mut actual_tag) + .unwrap(); + assert_eq!(ct, out.to_hex()); + assert_eq!(tag, actual_tag.to_hex()); + + let out = decrypt_aead(Cipher::chacha20_poly1305(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap()) + .unwrap(); + assert_eq!(pt, out.to_hex()); + } } diff --git a/openssl/src/types.rs b/openssl/src/types.rs index a444529d38429caa12d87e977e21cca01a0747bc..25ffe501bfb7870eb5bcffd18698ed4df6497ef1 100644 --- a/openssl/src/types.rs +++ b/openssl/src/types.rs @@ -1,43 +1,5 @@ -//! Items used by other types. +#[deprecated(note = "use foreign_types instead", since = "0.9.7")] +pub use foreign_types::ForeignType as OpenSslType; -/// A type implemented by wrappers over OpenSSL types. -/// -/// This should not be implemented by anything outside of this crate; new methods may be added at -/// any time. -pub trait OpenSslType: Sized { - /// The raw C type. - type CType; - - /// The type representing a reference to this type. - type Ref: OpenSslTypeRef; - - /// Constructs an instance of this type from its raw type. - unsafe fn from_ptr(ptr: *mut Self::CType) -> Self; - - /// Returns a raw pointer to the wrapped value. - fn as_ptr(&self) -> *mut Self::CType; -} - -/// A trait implemented by types which reference borrowed OpenSSL types. -/// -/// This should not be implemented by anything outside of this crate; new methods may be added at -/// any time. -pub trait OpenSslTypeRef: Sized { - /// The raw C type. - type CType; - - /// Constructs a shared instance of this type from its raw type. - unsafe fn from_ptr<'a>(ptr: *mut Self::CType) -> &'a Self { - &*(ptr as *mut _) - } - - /// Constructs a mutable reference of this type from its raw type. - unsafe fn from_ptr_mut<'a>(ptr: *mut Self::CType) -> &'a mut Self { - &mut *(ptr as *mut _) - } - - /// Returns a raw pointer to the wrapped value. - fn as_ptr(&self) -> *mut Self::CType { - self as *const _ as *mut _ - } -} +#[deprecated(note = "use foreign_types instead", since = "0.9.7")] +pub use foreign_types::ForeignTypeRef as OpenSslTypeRef; diff --git a/openssl/src/util.rs b/openssl/src/util.rs index 302bd31678580485edf42b367b65dbc604346bdc..f4883976fcc0387f09c836ee180de96f9d09950a 100644 --- a/openssl/src/util.rs +++ b/openssl/src/util.rs @@ -4,6 +4,8 @@ use std::cell::UnsafeCell; use std::panic::{self, AssertUnwindSafe}; use std::slice; +use error::ErrorStack; + /// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI /// frames are on the stack). /// @@ -33,10 +35,7 @@ impl Drop for CallbackState { } } -/// Password callback function, passed to private key loading functions. -/// -/// `cb_state` is expected to be a pointer to a `CallbackState`. -pub unsafe extern "C" fn invoke_passwd_cb(buf: *mut c_char, +pub unsafe extern fn invoke_passwd_cb_old(buf: *mut c_char, size: c_int, _rwflag: c_int, cb_state: *mut c_void) @@ -46,9 +45,7 @@ pub unsafe extern "C" fn invoke_passwd_cb(buf: *mut c_char, let callback = &mut *(cb_state as *mut CallbackState); let result = panic::catch_unwind(AssertUnwindSafe(|| { - // build a `i8` slice to pass to the user callback let pass_slice = slice::from_raw_parts_mut(buf, size as usize); - callback.cb.take().unwrap()(pass_slice) })); @@ -61,6 +58,36 @@ pub unsafe extern "C" fn invoke_passwd_cb(buf: *mut c_char, } } +/// Password callback function, passed to private key loading functions. +/// +/// `cb_state` is expected to be a pointer to a `CallbackState`. +pub unsafe extern fn invoke_passwd_cb(buf: *mut c_char, + size: c_int, + _rwflag: c_int, + cb_state: *mut c_void) + -> c_int + where F: FnOnce(&mut [u8]) -> Result +{ + let callback = &mut *(cb_state as *mut CallbackState); + + let result = panic::catch_unwind(AssertUnwindSafe(|| { + let pass_slice = slice::from_raw_parts_mut(buf as *mut u8, size as usize); + callback.cb.take().unwrap()(pass_slice) + })); + + match result { + Ok(Ok(len)) => len as c_int, + Ok(Err(_)) => { + // FIXME restore error stack + 0 + } + Err(err) => { + callback.panic = Some(err); + 0 + } + } +} + /// This is intended to be used as the inner type for `FooRef` types converted from raw C pointers. /// It has an `UnsafeCell` internally to inform the compiler about aliasability and doesn't /// implement `Copy`, so it can't be dereferenced. diff --git a/openssl/src/verify.rs b/openssl/src/verify.rs index 2f070fe5f2e59c805b77e29986d33ae1c79f10a2..f554127af209c4125cfe477845a64ca1e7c54f36 100644 --- a/openssl/src/verify.rs +++ b/openssl/src/verify.rs @@ -1,9 +1,9 @@ use libc::c_uint; use ffi; +use foreign_types::ForeignTypeRef; use cvt; use error::ErrorStack; -use types::OpenSslTypeRef; bitflags! { pub flags X509CheckFlags: c_uint { @@ -19,7 +19,13 @@ bitflags! { } } -type_!(X509VerifyParam, X509VerifyParamRef, ffi::X509_VERIFY_PARAM, ffi::X509_VERIFY_PARAM_free); +foreign_type! { + type CType = ffi::X509_VERIFY_PARAM; + fn drop = ffi::X509_VERIFY_PARAM_free; + + pub struct X509VerifyParam; + pub struct X509VerifyParamRef; +} impl X509VerifyParamRef { pub fn set_hostflags(&mut self, hostflags: X509CheckFlags) { diff --git a/openssl/src/version.rs b/openssl/src/version.rs index d7db39a750d0bdd417e18516104e4a9870e020a2..bf47695b81aa952d07ba0b4f82eda2a652d1ee7b 100644 --- a/openssl/src/version.rs +++ b/openssl/src/version.rs @@ -20,11 +20,8 @@ use ffi::{SSLEAY_VERSION as OPENSSL_VERSION, SSLEAY_CFLAGS as OPENSSL_CFLAGS, SSLeay_version as OpenSSL_version}; #[cfg(ossl110)] -use ffi::{OPENSSL_VERSION, OPENSSL_CFLAGS}; -#[cfg(ossl110)] -use ffi::{OPENSSL_BUILT_ON, OPENSSL_PLATFORM, OPENSSL_DIR}; -#[cfg(ossl110)] -use ffi::{OpenSSL_version_num, OpenSSL_version}; +use ffi::{OPENSSL_VERSION, OPENSSL_CFLAGS, OPENSSL_BUILT_ON, OPENSSL_PLATFORM, OPENSSL_DIR, + OpenSSL_version_num, OpenSSL_version}; /// OPENSSL_VERSION_NUMBER is a numeric release version identifier: /// @@ -92,8 +89,13 @@ fn test_versions() { println!("Platform: '{}'", platform()); println!("Dir: '{}'", dir()); + #[cfg(not(libressl))] + fn expected_name() -> &'static str { "OpenSSL" } + #[cfg(libressl)] + fn expected_name() -> &'static str { "LibreSSL" } + assert!(number() > 0); - assert!(version().starts_with("OpenSSL")); + assert!(version().starts_with(expected_name())); assert!(c_flags().starts_with("compiler:")); assert!(built_on().starts_with("built on:")); assert!(dir().starts_with("OPENSSLDIR:")); diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index e3a54d7d7d5b07dde6e2ca07bde3fdae2d26eb7c..6b65fbd2acab4dd484c415148bf4700d4baf2196 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -1,7 +1,8 @@ #![allow(deprecated)] use libc::{c_int, c_long}; +use ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; use std::borrow::Borrow; -use std::cmp; use std::collections::HashMap; use std::error::Error; use std::ffi::{CStr, CString}; @@ -14,17 +15,16 @@ use std::slice; use std::str; use {cvt, cvt_p}; -use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef, Asn1IntegerRef}; +use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef, Asn1BitStringRef, Asn1IntegerRef, Asn1ObjectRef}; +use bio::MemBioSlice; use bn::{BigNum, MSB_MAYBE_ZERO}; -use bio::{MemBio, MemBioSlice}; use conf::ConfRef; -use hash::MessageDigest; -use pkey::{PKey, PKeyRef}; use error::ErrorStack; -use ffi; +use hash::MessageDigest; use nid::{self, Nid}; -use types::{OpenSslType, OpenSslTypeRef}; +use pkey::{PKey, PKeyRef}; use stack::{Stack, StackRef, Stackable}; +use string::OpensslString; #[cfg(ossl10x)] use ffi::{X509_set_notBefore, X509_set_notAfter, ASN1_STRING_data, X509_STORE_CTX_get_chain}; @@ -39,6 +39,7 @@ pub mod verify; use x509::extension::{ExtensionType, Extension}; pub mod extension; +pub mod store; #[cfg(test)] mod tests; @@ -55,7 +56,13 @@ pub const X509_FILETYPE_PEM: X509FileType = X509FileType(ffi::X509_FILETYPE_PEM) pub const X509_FILETYPE_ASN1: X509FileType = X509FileType(ffi::X509_FILETYPE_ASN1); pub const X509_FILETYPE_DEFAULT: X509FileType = X509FileType(ffi::X509_FILETYPE_DEFAULT); -type_!(X509StoreContext, X509StoreContextRef, ffi::X509_STORE_CTX, ffi::X509_STORE_CTX_free); +foreign_type! { + type CType = ffi::X509_STORE_CTX; + fn drop = ffi::X509_STORE_CTX_free; + + pub struct X509StoreContext; + pub struct X509StoreContextRef; +} impl X509StoreContextRef { pub fn error(&self) -> Option { @@ -376,7 +383,13 @@ impl X509Builder { } } -type_!(X509, X509Ref, ffi::X509, ffi::X509_free); +foreign_type! { + type CType = ffi::X509; + fn drop = ffi::X509_free; + + pub struct X509; + pub struct X509Ref; +} impl X509Ref { pub fn subject_name(&self) -> &X509NameRef { @@ -420,8 +433,8 @@ impl X509Ref { } } - /// Returns certificate Not After validity period. - pub fn not_after<'a>(&'a self) -> &'a Asn1TimeRef { + /// Returns the certificate's Not After validity period. + pub fn not_after(&self) -> &Asn1TimeRef { unsafe { let date = compat::X509_get_notAfter(self.as_ptr()); assert!(!date.is_null()); @@ -429,8 +442,8 @@ impl X509Ref { } } - /// Returns certificate Not Before validity period. - pub fn not_before<'a>(&'a self) -> &'a Asn1TimeRef { + /// Returns the certificate's Not Before validity period. + pub fn not_before(&self) -> &Asn1TimeRef { unsafe { let date = compat::X509_get_notBefore(self.as_ptr()); assert!(!date.is_null()); @@ -438,23 +451,47 @@ impl X509Ref { } } - /// Writes certificate as PEM - pub fn to_pem(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); + /// Returns the certificate's signature + pub fn signature(&self) -> &Asn1BitStringRef { + unsafe { + let mut signature = ptr::null(); + compat::X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr()); + assert!(!signature.is_null()); + Asn1BitStringRef::from_ptr(signature as *mut _) + } + } + + /// Returns the certificate's signature algorithm. + pub fn signature_algorithm(&self) -> &X509AlgorithmRef { + unsafe { + let mut algor = ptr::null(); + compat::X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr()); + assert!(!algor.is_null()); + X509AlgorithmRef::from_ptr(algor as *mut _) + } + } + + /// Returns the list of OCSP responder URLs specified in the certificate's Authority Information + /// Access field. + pub fn ocsp_responders(&self) -> Result, ErrorStack> { unsafe { - try!(cvt(ffi::PEM_write_bio_X509(mem_bio.as_ptr(), self.as_ptr()))); + cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) } - Ok(mem_bio.get_buf().to_owned()) } - /// Returns a DER serialized form of the certificate - pub fn to_der(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); + /// Checks that this certificate issued `subject`. + pub fn issued(&self, subject: &X509Ref) -> Result<(), X509VerifyError> { unsafe { - ffi::i2d_X509_bio(mem_bio.as_ptr(), self.as_ptr()); + let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr()); + match X509VerifyError::from_raw(r as c_long) { + Some(e) => Err(e), + None => Ok(()), + } } - Ok(mem_bio.get_buf().to_owned()) } + + to_pem!(ffi::PEM_write_bio_X509); + to_der!(ffi::i2d_X509); } impl ToOwned for X509Ref { @@ -474,25 +511,36 @@ impl X509 { X509Builder::new() } - /// Reads a certificate from DER. - pub fn from_der(buf: &[u8]) -> Result { - unsafe { - let mut ptr = buf.as_ptr(); - let len = cmp::min(buf.len(), c_long::max_value() as usize) as c_long; - let x509 = try!(cvt_p(ffi::d2i_X509(ptr::null_mut(), &mut ptr, len))); - Ok(X509::from_ptr(x509)) - } - } + from_pem!(X509, ffi::PEM_read_bio_X509); + from_der!(X509, ffi::d2i_X509); - /// Reads a certificate from PEM. - pub fn from_pem(buf: &[u8]) -> Result { - let mem_bio = try!(MemBioSlice::new(buf)); + /// Deserializes a list of PEM-formatted certificates. + pub fn stack_from_pem(pem: &[u8]) -> Result, ErrorStack> { unsafe { - let handle = try!(cvt_p(ffi::PEM_read_bio_X509(mem_bio.as_ptr(), - ptr::null_mut(), - None, - ptr::null_mut()))); - Ok(X509::from_ptr(handle)) + ffi::init(); + let bio = try!(MemBioSlice::new(pem)); + + let mut certs = vec![]; + loop { + let r = ffi::PEM_read_bio_X509(bio.as_ptr(), + ptr::null_mut(), + None, + ptr::null_mut()); + if r.is_null() { + let err = ffi::ERR_peek_last_error(); + if ffi::ERR_GET_LIB(err) == ffi::ERR_LIB_PEM + && ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE { + ffi::ERR_clear_error(); + break; + } + + return Err(ErrorStack::get()); + } else { + certs.push(X509(r)); + } + } + + Ok(certs) } } } @@ -534,7 +582,13 @@ impl<'a> X509v3Context<'a> { } } -type_!(X509Extension, X509ExtensionRef, ffi::X509_EXTENSION, ffi::X509_EXTENSION_free); +foreign_type! { + type CType = ffi::X509_EXTENSION; + fn drop = ffi::X509_EXTENSION_free; + + pub struct X509Extension; + pub struct X509ExtensionRef; +} impl Stackable for X509Extension { type StackType = ffi::stack_st_X509_EXTENSION; @@ -635,7 +689,13 @@ impl X509NameBuilder { } } -type_!(X509Name, X509NameRef, ffi::X509_NAME, ffi::X509_NAME_free); +foreign_type! { + type CType = ffi::X509_NAME; + fn drop = ffi::X509_NAME_free; + + pub struct X509Name; + pub struct X509NameRef; +} impl X509Name { /// Returns a new builder. @@ -694,7 +754,13 @@ impl<'a> Iterator for X509NameEntries<'a> { } } -type_!(X509NameEntry, X509NameEntryRef, ffi::X509_NAME_ENTRY, ffi::X509_NAME_ENTRY_free); +foreign_type! { + type CType = ffi::X509_NAME_ENTRY; + fn drop = ffi::X509_NAME_ENTRY_free; + + pub struct X509NameEntry; + pub struct X509NameEntryRef; +} impl X509NameEntryRef { pub fn data(&self) -> &Asn1StringRef { @@ -769,7 +835,13 @@ impl X509ReqBuilder { } } -type_!(X509Req, X509ReqRef, ffi::X509_REQ, ffi::X509_REQ_free); +foreign_type! { + type CType = ffi::X509_REQ; + fn drop = ffi::X509_REQ_free; + + pub struct X509Req; + pub struct X509ReqRef; +} impl X509Req { pub fn builder() -> Result { @@ -787,25 +859,40 @@ impl X509Req { Ok(X509Req::from_ptr(handle)) } } + + from_der!(X509Req, ffi::d2i_X509_REQ); } impl X509ReqRef { - /// Writes CSR as PEM - pub fn to_pem(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); - if unsafe { ffi::PEM_write_bio_X509_REQ(mem_bio.as_ptr(), self.as_ptr()) } != 1 { - return Err(ErrorStack::get()); + to_pem!(ffi::PEM_write_bio_X509_REQ); + to_der!(ffi::i2d_X509_REQ); + + pub fn version(&self) -> i32 + { + unsafe { + compat::X509_REQ_get_version(self.as_ptr()) as i32 + } + } + + pub fn set_version(&mut self, value: i32) -> Result<(), ErrorStack> + { + unsafe { + cvt(ffi::X509_REQ_set_version(self.as_ptr(), value as c_long)).map(|_| ()) } - Ok(mem_bio.get_buf().to_owned()) } - /// Returns a DER serialized form of the CSR - pub fn to_der(&self) -> Result, ErrorStack> { - let mem_bio = try!(MemBio::new()); + pub fn subject_name(&self) -> &X509NameRef { unsafe { - ffi::i2d_X509_REQ_bio(mem_bio.as_ptr(), self.as_ptr()); + let name = compat::X509_REQ_get_subject_name(self.as_ptr()); + assert!(!name.is_null()); + X509NameRef::from_ptr(name) + } + } + + pub fn set_subject_name(&mut self, value: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REQ_set_subject_name(self.as_ptr(), value.as_ptr())).map(|_| ()) } - Ok(mem_bio.get_buf().to_owned()) } } @@ -932,7 +1019,13 @@ impl X509VerifyError { } } -type_!(GeneralName, GeneralNameRef, ffi::GENERAL_NAME, ffi::GENERAL_NAME_free); +foreign_type! { + type CType = ffi::GENERAL_NAME; + fn drop = ffi::GENERAL_NAME_free; + + pub struct GeneralName; + pub struct GeneralNameRef; +} impl GeneralNameRef { /// Returns the contents of this `GeneralName` if it is a `dNSName`. @@ -972,18 +1065,42 @@ impl Stackable for GeneralName { type StackType = ffi::stack_st_GENERAL_NAME; } +foreign_type! { + type CType = ffi::X509_ALGOR; + fn drop = ffi::X509_ALGOR_free; + + pub struct X509Algorithm; + pub struct X509AlgorithmRef; +} + +impl X509AlgorithmRef { + /// Returns the ASN.1 OID of this algorithm. + pub fn object(&self) -> &Asn1ObjectRef { + unsafe { + let mut oid = ptr::null(); + compat::X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr()); + assert!(!oid.is_null()); + Asn1ObjectRef::from_ptr(oid as *mut _) + } + } +} + #[cfg(ossl110)] mod compat { pub use ffi::X509_getm_notAfter as X509_get_notAfter; pub use ffi::X509_getm_notBefore as X509_get_notBefore; pub use ffi::X509_up_ref; pub use ffi::X509_get0_extensions; + pub use ffi::X509_REQ_get_version; + pub use ffi::X509_REQ_get_subject_name; + pub use ffi::X509_get0_signature; + pub use ffi::X509_ALGOR_get0; } #[cfg(ossl10x)] #[allow(bad_style)] mod compat { - use libc::c_int; + use libc::{c_int, c_void}; use ffi; pub unsafe fn X509_get_notAfter(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME { @@ -1011,4 +1128,36 @@ mod compat { (*info).extensions } } + + pub unsafe fn X509_REQ_get_version(x: *mut ffi::X509_REQ) -> ::libc::c_long + { + ::ffi::ASN1_INTEGER_get((*(*x).req_info).version) + } + + pub unsafe fn X509_REQ_get_subject_name(x: *mut ffi::X509_REQ) -> *mut ::ffi::X509_NAME + { + (*(*x).req_info).subject + } + + pub unsafe fn X509_get0_signature(psig: *mut *const ffi::ASN1_BIT_STRING, + palg: *mut *const ffi::X509_ALGOR, + x: *const ffi::X509) { + if !psig.is_null() { + *psig = (*x).signature; + } + if !palg.is_null() { + *palg = (*x).sig_alg; + } + } + + pub unsafe fn X509_ALGOR_get0(paobj: *mut *const ffi::ASN1_OBJECT, + pptype: *mut c_int, + pval: *mut *mut c_void, + alg: *const ffi::X509_ALGOR) { + if !paobj.is_null() { + *paobj = (*alg).algorithm; + } + assert!(pptype.is_null()); + assert!(pval.is_null()); + } } diff --git a/openssl/src/x509/store.rs b/openssl/src/x509/store.rs new file mode 100644 index 0000000000000000000000000000000000000000..8b7a084bed4e0ae97b6c686f1327ec181a250950 --- /dev/null +++ b/openssl/src/x509/store.rs @@ -0,0 +1,65 @@ +use ffi; +use foreign_types::ForeignTypeRef; +use std::mem; + +use {cvt, cvt_p}; +use error::ErrorStack; +use x509::X509; + +foreign_type! { + type CType = ffi::X509_STORE; + fn drop = ffi::X509_STORE_free; + + pub struct X509StoreBuilder; + pub struct X509StoreBuilderRef; +} + +impl X509StoreBuilder { + /// Returns a builder for a certificate store. + /// + /// The store is initially empty. + pub fn new() -> Result { + unsafe { + ffi::init(); + + cvt_p(ffi::X509_STORE_new()).map(X509StoreBuilder) + } + } + + /// Constructs the `X509Store`. + pub fn build(self) -> X509Store { + let store = X509Store(self.0); + mem::forget(self); + store + } +} + +impl X509StoreBuilderRef { + /// Adds a certificate to the certificate store. + pub fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { + unsafe { + let ptr = cert.as_ptr(); + mem::forget(cert); // the cert will be freed inside of X509_STORE_add_cert on error + cvt(ffi::X509_STORE_add_cert(self.as_ptr(), ptr)).map(|_| ()) + } + } + + /// Load certificates from their default locations. + /// + /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` + /// environment variables if present, or defaults specified at OpenSSL + /// build time otherwise. + pub fn set_default_paths(&mut self) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ()) + } + } +} + +foreign_type! { + type CType = ffi::X509_STORE; + fn drop = ffi::X509_STORE_free; + + pub struct X509Store; + pub struct X509StoreRef; +} diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 514b8edccaaba04a603ea14554a4477f18ab0f22..2632eade6c22023d96236aa90cb7dcc6eefa77f2 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -1,14 +1,17 @@ -use serialize::hex::FromHex; +use hex::{FromHex, ToHex}; use asn1::Asn1Time; use bn::{BigNum, MSB_MAYBE_ZERO}; +use ec::{NAMED_CURVE, EcGroup, EcKey}; use hash::MessageDigest; +use nid::X9_62_PRIME256V1; use pkey::PKey; use rsa::Rsa; use stack::Stack; use x509::{X509, X509Generator, X509Name, X509Req}; use x509::extension::{Extension, BasicConstraints, KeyUsage, ExtendedKeyUsage, SubjectKeyIdentifier, AuthorityKeyIdentifier, SubjectAlternativeName}; +use ssl::{SslMethod, SslContextBuilder}; use x509::extension::AltNameOption as SAN; use x509::extension::KeyUsageOption::{DigitalSignature, KeyEncipherment}; use x509::extension::ExtKeyUsageOption::{self, ClientAuth, ServerAuth}; @@ -76,7 +79,12 @@ fn test_req_gen() { let pkey = pkey(); let req = get_generator().request(&pkey).unwrap(); - req.to_pem().unwrap(); + let reqpem = req.to_pem().unwrap(); + + let req = X509Req::from_pem(&reqpem).ok().expect("Failed to load PEM"); + let cn = (*req).subject_name().entries_by_nid(nid::COMMONNAME).next().unwrap(); + assert_eq!(0, (*req).version()); + assert_eq!(cn.data().as_slice(), b"test_me"); // FIXME: check data in result to be correct, needs implementation // of X509_REQ getters @@ -89,7 +97,7 @@ fn test_cert_loading() { let fingerprint = cert.fingerprint(MessageDigest::sha1()).unwrap(); let hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; - let hash_vec = hash_str.from_hex().unwrap(); + let hash_vec = Vec::from_hex(hash_str).unwrap(); assert_eq!(fingerprint, hash_vec); } @@ -136,7 +144,7 @@ fn test_nid_values() { assert_eq!(email.data().as_slice(), b"test@example.com"); let friendly = subject.entries_by_nid(nid::FRIENDLYNAME).next().unwrap(); - assert_eq!(&*friendly.data().as_utf8().unwrap(), "Example"); + assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example"); } #[test] @@ -260,3 +268,64 @@ fn x509_req_builder() { builder.sign(&pkey, MessageDigest::sha256()).unwrap(); } + +#[test] +fn test_stack_from_pem() { + let certs = include_bytes!("../../test/certs.pem"); + let certs = X509::stack_from_pem(certs).unwrap(); + + assert_eq!(certs.len(), 2); + assert_eq!(certs[0].fingerprint(MessageDigest::sha1()).unwrap().to_hex(), + "59172d9313e84459bcff27f967e79e6e9217e584"); + assert_eq!(certs[1].fingerprint(MessageDigest::sha1()).unwrap().to_hex(), + "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875"); +} + +#[test] +fn issued() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + + ca.issued(&cert).unwrap(); + cert.issued(&cert).err().unwrap(); +} + +#[test] +fn ecdsa_cert() { + let mut group = EcGroup::from_curve_name(X9_62_PRIME256V1).unwrap(); + group.set_asn1_flag(NAMED_CURVE); + let key = EcKey::generate(&group).unwrap(); + let key = PKey::from_ec_key(key).unwrap(); + + let cert = X509Generator::new() + .set_valid_period(365) + .add_name("CN".to_owned(), "TestServer".to_owned()) + .set_sign_hash(MessageDigest::sha256()) + .sign(&key) + .unwrap(); + + let mut ctx = SslContextBuilder::new(SslMethod::tls()).unwrap(); + ctx.set_certificate(&cert).unwrap(); + ctx.set_private_key(&key).unwrap(); + ctx.check_private_key().unwrap(); +} + +#[test] +fn signature() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let signature = cert.signature(); + assert_eq!(signature.as_slice().to_hex(), + "4af607b889790b43470442cfa551cdb8b6d0b0340d2958f76b9e3ef6ad4992230cead6842587f0ecad5\ + 78e6e11a221521e940187e3d6652de14e84e82f6671f097cc47932e022add3c0cb54a26bf27fa84c107\ + 4971caa6bee2e42d34a5b066c427f2d452038082b8073993399548088429de034fdd589dcfb0dd33be7\ + ebdfdf698a28d628a89568881d658151276bde333600969502c4e62e1d3470a683364dfb241f78d310a\ + 89c119297df093eb36b7fd7540224f488806780305d1e79ffc938fe2275441726522ab36d88348e6c51\ + f13dcc46b5e1cdac23c974fd5ef86aa41e91c9311655090a52333bc79687c748d833595d4c5f987508f\ + e121997410d37c"); + let algorithm = cert.signature_algorithm(); + assert_eq!(algorithm.object().nid(), nid::SHA256WITHRSAENCRYPTION); + assert_eq!(algorithm.object().to_string(), "sha256WithRSAEncryption"); +} diff --git a/openssl/test/build.sh b/openssl/test/build.sh index 106a38d3418347fc97d49c8f4b77703e62d62955..cd740895d3f52a148711400ee9c5e4f5f59d2415 100755 --- a/openssl/test/build.sh +++ b/openssl/test/build.sh @@ -3,21 +3,29 @@ set -ex MAX_REDIRECTS=5 -OPENSSL=openssl-$BUILD_OPENSSL_VERSION.tar.gz -OUT=/tmp/$OPENSSL + +if [ -n "${BUILD_LIBRESSL_VERSION}" ]; then + NAME=libressl + URL1="http://ftp3.usa.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${BUILD_LIBRESSL_VERSION}.tar.gz" + URL2="http://ftp.eu.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${BUILD_LIBRESSL_VERSION}.tar.gz" + OUT="/tmp/libressl-${BUILD_OPENSSL_VERSION}.tar.gz" +elif [ -n "${BUILD_OPENSSL_VERSION}" ]; then + NAME=openssl + URL1="https://openssl.org/source/openssl-${BUILD_OPENSSL_VERSION}.tar.gz" + URL2="http://mirrors.ibiblio.org/openssl/source/openssl-${BUILD_OPENSSL_VERSION}.tar.gz" + OUT="/tmp/openssl-${BUILD_OPENSSL_VERSION}.tar.gz" +else + exit 0 +fi me=$0 -myname=`basename $me` +myname=`basename ${me}` -cmp --silent $me $HOME/openssl/$myname && exit 0 || echo "cache is busted" +cmp --silent ${me} ${HOME}/${NAME}/${myname} && exit 0 || echo "cache is busted" -rm -rf $HOME/openssl +rm -rf "${HOME}/${NAME}" -if [ "$TRAVIS_OS_NAME" == "osx" ]; then - exit 0 -fi - -if [ "$BUILD_OPENSSL_VERSION" == "" ]; then +if [ "${TRAVIS_OS_NAME}" == "osx" ]; then exit 0 fi @@ -32,17 +40,21 @@ else OS_COMPILER=linux-x86_64 fi -mkdir -p /tmp/openssl -cp $me /tmp/openssl/$myname -cd /tmp/openssl +mkdir -p /tmp/build +cp ${me} /tmp/build/${myname} +cd /tmp/build -curl -o $OUT -L --max-redirs $MAX_REDIRECTS https://openssl.org/source/$OPENSSL \ - || curl -o $OUT -L --max-redirs ${MAX_REDIRECTS} http://mirrors.ibiblio.org/openssl/source/$OPENSSL +curl -o ${OUT} -L --max-redirs ${MAX_REDIRECTS} ${URL1} \ + || curl -o ${OUT} -L --max-redirs ${MAX_REDIRECTS} ${URL2} -tar --strip-components=1 -xzf $OUT +tar --strip-components=1 -xzf ${OUT} -./Configure --prefix=$HOME/openssl $OS_COMPILER -fPIC $OS_FLAGS +if [ -n "${BUILD_LIBRESSL_VERSION}" ]; then + ./configure --prefix=${HOME}/libressl +else + ./Configure --prefix=${HOME}/openssl ${OS_COMPILER} -fPIC ${OS_FLAGS} +fi make -j$(nproc) make install -cp $myname $HOME/openssl/$myname +cp ${myname} ${HOME}/${NAME}/${myname} diff --git a/openssl/test/certs.pem b/openssl/test/certs.pem new file mode 100644 index 0000000000000000000000000000000000000000..9d3516620da5d83a5dabb3b5962651aa15f8bc32 --- /dev/null +++ b/openssl/test/certs.pem @@ -0,0 +1,40 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgMCCQCHcfe97pgvpTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTE2MDgxNDE3MDAwM1oXDTI2MDgxMjE3MDAwM1owWjELMAkG +A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKZm9vYmFyLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKj0JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub +3mw2/Ja5BD/yN96/7zMSumXF8uS3SkmpyiJkbyD01TSRTqjlP7/VCBlyUIChlpLQ +mrGaijZiT/VCyPXqmcwFzXS5IOTpX1olJfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6 +TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS1ieT2asFsAyOZqWhk2fakwwBDFWDhOGI +ubfO+5aq9cBJbNRlzsgB3UZs3gC0O6GzbnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4Y +euZkkbHTFBMz288PUc3m3ZTcpN+E7+ZOUBRZXKD20K07NugqCzUCAwEAATANBgkq +hkiG9w0BAQsFAAOCAQEASvYHuIl5C0NHBELPpVHNuLbQsDQNKVj3a54+9q1JkiMM +6taEJYfw7K1Xjm4RoiFSHpQBh+PWZS3hToToL2Zx8JfMR5MuAirdPAy1Sia/J/qE +wQdJccqmvuLkLTSlsGbEJ/LUUgOAgrgHOZM5lUgIhCneA0/dWJ3PsN0zvn69/faY +oo1iiolWiIHWWBUSdr3jM2AJaVAsTmLh00cKaDNk37JB940xConBGSl98JPrNrf9 +dUAiT0iIBngDBdHnn/yTj+InVEFyZSKrNtiDSObFHxPcxGteHNrCPJdP1e+GqkHp +HJMRZVCQpSMzvHlofHSNgzWV1MX5h1CP4SGZdBDTfA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G +ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV +eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr +7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 +aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc +klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN +XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn +BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv +Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 +AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy +OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 +mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 +GA== +-----END CERTIFICATE----- diff --git a/openssl/test/run.sh b/openssl/test/run.sh index 4d3397a60a8028028268ee367595d933033e7411..b07b8e4b56f3b8c1cbe8f5b0526b10f64d5ae54c 100755 --- a/openssl/test/run.sh +++ b/openssl/test/run.sh @@ -12,15 +12,21 @@ esac echo Using features: $FEATURES -if [ -d "$HOME/openssl/lib" ]; then - export OPENSSL_DIR=$HOME/openssl - export PATH=$HOME/openssl/bin:$PATH +if [ -n "${BUILD_LIBRESSL_VERSION}" -a -d "$HOME/libressl/lib" ]; then + echo "Testing build libressl-${BUILD_LIBRESSL_VERSION}" + export OPENSSL_DIR=${HOME}/libressl + export PATH="${HOME}/libressl/bin:${PATH}" + +elif [ -n "${BUILD_OPENSSL_VERSION}" -a -d "$HOME/openssl/lib" ]; then + echo "Testing build openssl-${BUILD_LIBRESSL_VERSION}" + export OPENSSL_DIR="${HOME}/openssl" + export PATH="${HOME}/openssl/bin:${PATH}" fi if [ "$TARGET" == "arm-unknown-linux-gnueabihf" ]; then FLAGS="--no-run" fi -cargo run --manifest-path systest/Cargo.toml --target $TARGET +cargo run --manifest-path systest/Cargo.toml --target $TARGET -v exec cargo test --manifest-path openssl/Cargo.toml --target $TARGET \ - --features "$FEATURES" $FLAGS + --features "$FEATURES" -v $FLAGS diff --git a/systest/build.rs b/systest/build.rs index 81907a57a851330c447e841135d6b07ff9de22c3..548d6080578f3314fe3cbce8d36f740e026fd2c1 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -22,7 +22,11 @@ fn main() { } } - cfg.cfg(&format!("ossl{}", env::var("DEP_OPENSSL_VERSION").unwrap()), None); + if let Ok(_) = env::var("DEP_OPENSSL_LIBRESSL") { + cfg.cfg("libressl", None); + } else if let Ok(version) = env::var("DEP_OPENSSL_VERSION") { + cfg.cfg(&format!("ossl{}", version), None); + } if let Ok(vars) = env::var("DEP_OPENSSL_CONF") { for var in vars.split(",") { cfg.cfg("osslconf", Some(var)); @@ -42,7 +46,9 @@ fn main() { .header("openssl/err.h") .header("openssl/rand.h") .header("openssl/pkcs12.h") - .header("openssl/bn.h"); + .header("openssl/bn.h") + .header("openssl/aes.h") + .header("openssl/ocsp.h"); cfg.type_name(|s, is_struct| { // Add some `*` on some callback parameters to get function pointer to // typecheck in C, especially on MSVC. @@ -52,7 +58,8 @@ fn main() { format!("bio_info_cb*") } else if s == "_STACK" { format!("struct stack_st") - } else if is_struct && s.chars().next().unwrap().is_lowercase() { + // This logic should really be cleaned up + } else if is_struct && s != "point_conversion_form_t" && s.chars().next().unwrap().is_lowercase() { format!("struct {}", s) } else { format!("{}", s) @@ -85,6 +92,8 @@ fn main() { }); cfg.skip_signededness(|s| { s.ends_with("_cb") || + s.ends_with("_CB") || + s.ends_with("_cb_fn") || s.starts_with("CRYPTO_") || s == "PasswordCallback" });