Skip to content

Commit

Permalink
Merge pull request sfackler#53 from vhbit/cert-gen
Browse files Browse the repository at this point in the history
Certificate/PKey generation & PEM export
  • Loading branch information
sfackler committed Sep 30, 2014
2 parents 8b6bc82 + 3f413e9 commit 359043a
Show file tree
Hide file tree
Showing 9 changed files with 782 additions and 313 deletions.
23 changes: 23 additions & 0 deletions src/asn1/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
pub mod ffi {
#![allow(dead_code)]
#![allow(non_camel_case_types)]
use libc::{c_int, c_long, c_void};

pub type ASN1_INTEGER = c_void;
pub type ASN1_TIME = c_void;
pub type ASN1_STRING = c_void;

pub static MBSTRING_FLAG: c_int = 0x1000;
pub static MBSTRING_UTF8: c_int = MBSTRING_FLAG;
pub static MBSTRING_ASC: c_int = MBSTRING_FLAG | 1;
pub static MBSTRING_BMP: c_int = MBSTRING_FLAG | 2;
pub static MBSTRING_UNIV: c_int = MBSTRING_FLAG | 4;

pub static V_ASN1_UTCTIME: c_int = 23;
pub static V_ASN1_GENERALIZEDTIME: c_int = 24;

extern "C" {
pub fn ASN1_STRING_type_new(ty: c_int) -> *mut ASN1_STRING;
pub fn ASN1_INTEGER_set(dest: *mut ASN1_INTEGER, value: c_long) -> c_int;
}
}
103 changes: 103 additions & 0 deletions src/bio/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use libc::{c_void, c_int};
use std::io::{IoResult, IoError, OtherIoError};
use std::io::{Reader, Writer};
use std::ptr;

use ssl::error::{SslError};

pub struct MemBio {
bio: *mut ffi::BIO,
owned: bool
}

impl Drop for MemBio {
fn drop(&mut self) {
if self.owned {
unsafe {
ffi::BIO_free_all(self.bio);
}
}
}
}

impl MemBio {
/// Creates a new owned memory based BIO
pub fn new() -> Result<MemBio, SslError> {
let bio = unsafe { ffi::BIO_new(ffi::BIO_s_mem()) };
try_ssl_null!(bio);

Ok(MemBio {
bio: bio,
owned: true
})
}

/// Returns a "borrow", i.e. it has no ownership
pub fn borrowed(bio: *mut ffi::BIO) -> MemBio {
MemBio {
bio: bio,
owned: false
}
}

/// Consumes current bio and returns wrapped value
/// Note that data ownership is lost and
/// should be handled manually
pub unsafe fn unwrap(mut self) -> *mut ffi::BIO {
self.owned = false;
self.bio
}

/// Temporarily gets wrapped value
pub unsafe fn get_handle(&self) -> *mut ffi::BIO {
self.bio
}
}

impl Reader for MemBio {
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
let ret = unsafe {
ffi::BIO_read(self.bio, buf.as_ptr() as *mut c_void,
buf.len() as c_int)
};

if ret < 0 {
// FIXME: provide details from OpenSSL
Err(IoError{kind: OtherIoError, desc: "mem bio read error", detail: None})
} else {
Ok(ret as uint)
}
}
}

impl Writer for MemBio {
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
let ret = unsafe {
ffi::BIO_write(self.bio, buf.as_ptr() as *const c_void,
buf.len() as c_int)
};
if buf.len() != ret as uint {
// FIXME: provide details from OpenSSL
Err(IoError{kind: OtherIoError, desc: "mem bio write error", detail: None})
} else {
Ok(())
}
}
}

pub mod ffi {
#![allow(non_camel_case_types)]

use libc::{c_int, c_void};

pub type BIO = c_void;
pub type BIO_METHOD = c_void;

extern "C" {
pub fn BIO_s_mem() -> *const BIO_METHOD;
pub fn BIO_new(type_: *const BIO_METHOD) -> *mut BIO;
pub fn BIO_free_all(a: *mut BIO);
pub fn BIO_read(b: *mut BIO, buf: *mut c_void, len: c_int) -> c_int;
pub fn BIO_write(b: *mut BIO, buf: *const c_void, len: c_int) -> c_int;
}
}
29 changes: 28 additions & 1 deletion src/crypto/pkey.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
use libc::{c_char, c_int, c_uint};
use libc::{c_char, c_int, c_uint, c_void};
use libc;
use std::mem;
use std::ptr;
use bio::{mod, MemBio};
use crypto::hash::{HashType, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, RIPEMD160};
use crypto::symm::{EVP_CIPHER};
use ssl::error::{SslError, StreamError};

#[allow(non_camel_case_types)]
pub type EVP_PKEY = *mut libc::c_void;

#[allow(non_camel_case_types)]
pub type RSA = *mut libc::c_void;

pub type PrivateKeyWriteCallback = extern "C" fn(buf: *mut c_char, size: c_int, rwflag: c_int, user_data: *mut c_void) -> c_int;

#[link(name = "crypto")]
extern {
fn EVP_PKEY_new() -> *mut EVP_PKEY;
Expand All @@ -34,6 +39,11 @@ extern {
k: *mut RSA) -> c_int;
fn RSA_verify(t: c_int, m: *const u8, mlen: c_uint, sig: *const u8, siglen: c_uint,
k: *mut RSA) -> c_int;

fn PEM_write_bio_PrivateKey(bio: *mut bio::ffi::BIO, pkey: *mut EVP_PKEY, cipher: *const EVP_CIPHER,
kstr: *mut c_char, klen: c_int,
callback: *mut c_void,
user_data: *mut c_void) -> c_int;
}

enum Parts {
Expand Down Expand Up @@ -163,6 +173,19 @@ impl PKey {
self.parts = Both;
}

/// Stores private key as a PEM
// FIXME: also add password and encryption
pub fn write_pem(&self, writer: &mut Writer/*, password: Option<String>*/) -> Result<(), SslError> {
let mut mem_bio = try!(MemBio::new());
unsafe {
try_ssl!(PEM_write_bio_PrivateKey(mem_bio.get_handle(), self.evp, ptr::null(),
ptr::null_mut(), -1, ptr::null_mut(), ptr::null_mut()));

}
let buf = try!(mem_bio.read_to_end().map_err(StreamError));
writer.write(buf.as_slice()).map_err(StreamError)
}

/**
* Returns the size of the public key modulus.
*/
Expand Down Expand Up @@ -326,6 +349,10 @@ impl PKey {
rv == 1 as c_int
}
}

pub unsafe fn get_handle(&self) -> *mut EVP_PKEY {
return self.evp
}
}

impl Drop for PKey {
Expand Down
9 changes: 7 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ extern crate libc;
extern crate serialize;
extern crate sync;

pub mod ssl;
pub mod crypto;
mod macros;

mod asn1;
pub mod bn;
pub mod bio;
pub mod crypto;
pub mod ssl;
pub mod x509;
55 changes: 55 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#![macro_escape]

macro_rules! try_ssl_stream {
($e:expr) => (
match $e {
Ok(ok) => ok,
Err(err) => return Err(StreamError(err))
}
)
}

/// Shortcut return with SSL error if something went wrong
macro_rules! try_ssl_if {
($e:expr) => (
if $e {
return Err(SslError::get())
}
)
}

/// Shortcut return with SSL error if last error result is 0
/// (default)
macro_rules! try_ssl{
($e:expr) => (try_ssl_if!($e == 0))
}

/// Shortcut return with SSL if got a null result
macro_rules! try_ssl_null{
($e:expr) => (try_ssl_if!($e == ptr::null_mut()))
}


/// Lifts current SSL error code into Result<(), Error>
/// if expression is true
/// Lifting is actually a shortcut of the following form:
///
/// ```ignore
/// let _ = try!(something)
/// Ok(())
/// ```
macro_rules! lift_ssl_if{
($e:expr) => ( {
if $e {
Err(SslError::get())
} else {
Ok(())
}
})
}

/// Lifts current SSL error code into Result<(), Error>
/// if SSL returned 0 (default error indication)
macro_rules! lift_ssl {
($e:expr) => (lift_ssl_if!($e == 0))
}
91 changes: 8 additions & 83 deletions src/ssl/ffi.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
#![allow(non_camel_case_types)]

use libc::{c_int, c_void, c_long, c_ulong, c_char, c_uint};
use crypto::hash::{EVP_MD};
use libc::{c_int, c_void, c_long, c_ulong, c_char};

use bio;
use x509;

pub type SSL_CTX = c_void;
pub type SSL_METHOD = c_void;
pub type COMP_METHOD = c_void;
pub type SSL = c_void;
pub type BIO = c_void;
pub type BIO_METHOD = c_void;
pub type X509_STORE_CTX = c_void;
pub type X509 = c_void;
pub type X509_NAME = c_void;
pub type CRYPTO_EX_DATA = c_void;

pub type CRYPTO_EX_new = extern "C" fn(parent: *mut c_void, ptr: *mut c_void,
Expand Down Expand Up @@ -44,64 +41,6 @@ pub static SSL_CTRL_SET_TLSEXT_HOSTNAME: c_int = 55;

pub static TLSEXT_NAMETYPE_host_name: c_long = 0;

pub static X509_V_OK: c_int = 0;
pub static X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: c_int = 2;
pub static X509_V_ERR_UNABLE_TO_GET_CRL: c_int = 3;
pub static X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: c_int = 4;
pub static X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: c_int = 5;
pub static X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: c_int = 6;
pub static X509_V_ERR_CERT_SIGNATURE_FAILURE: c_int = 7;
pub static X509_V_ERR_CRL_SIGNATURE_FAILURE: c_int = 8;
pub static X509_V_ERR_CERT_NOT_YET_VALID: c_int = 9;
pub static X509_V_ERR_CERT_HAS_EXPIRED: c_int = 10;
pub static X509_V_ERR_CRL_NOT_YET_VALID: c_int = 11;
pub static X509_V_ERR_CRL_HAS_EXPIRED: c_int = 12;
pub static X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: c_int = 13;
pub static X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: c_int = 14;
pub static X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: c_int = 15;
pub static X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: c_int = 16;
pub static X509_V_ERR_OUT_OF_MEM: c_int = 17;
pub static X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: c_int = 18;
pub static X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: c_int = 19;
pub static X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: c_int = 20;
pub static X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: c_int = 21;
pub static X509_V_ERR_CERT_CHAIN_TOO_LONG: c_int = 22;
pub static X509_V_ERR_CERT_REVOKED: c_int = 23;
pub static X509_V_ERR_INVALID_CA: c_int = 24;
pub static X509_V_ERR_PATH_LENGTH_EXCEEDED: c_int = 25;
pub static X509_V_ERR_INVALID_PURPOSE: c_int = 26;
pub static X509_V_ERR_CERT_UNTRUSTED: c_int = 27;
pub static X509_V_ERR_CERT_REJECTED: c_int = 28;
pub static X509_V_ERR_SUBJECT_ISSUER_MISMATCH: c_int = 29;
pub static X509_V_ERR_AKID_SKID_MISMATCH: c_int = 30;
pub static X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: c_int = 31;
pub static X509_V_ERR_KEYUSAGE_NO_CERTSIGN: c_int = 32;
pub static X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: c_int = 33;
pub static X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: c_int = 34;
pub static X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: c_int = 35;
pub static X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: c_int = 36;
pub static X509_V_ERR_INVALID_NON_CA: c_int = 37;
pub static X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED: c_int = 38;
pub static X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: c_int = 39;
pub static X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: c_int = 40;
pub static X509_V_ERR_INVALID_EXTENSION: c_int = 41;
pub static X509_V_ERR_INVALID_POLICY_EXTENSION: c_int = 42;
pub static X509_V_ERR_NO_EXPLICIT_POLICY: c_int = 43;
pub static X509_V_ERR_DIFFERENT_CRL_SCOPE: c_int = 44;
pub static X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE: c_int = 45;
pub static X509_V_ERR_UNNESTED_RESOURCE: c_int = 46;
pub static X509_V_ERR_PERMITTED_VIOLATION: c_int = 47;
pub static X509_V_ERR_EXCLUDED_VIOLATION: c_int = 48;
pub static X509_V_ERR_SUBTREE_MINMAX: c_int = 49;
pub static X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE: c_int = 51;
pub static X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX: c_int = 52;
pub static X509_V_ERR_UNSUPPORTED_NAME_SYNTAX: c_int = 53;
pub static X509_V_ERR_CRL_PATH_VALIDATION_ERROR: c_int = 54;
pub static X509_V_ERR_APPLICATION_VERIFICATION: c_int = 50;

pub static X509_FILETYPE_PEM: c_int = 1;
pub static X509_FILETYPE_ASN1: c_int = 2;
pub static X509_FILETYPE_DEFAULT: c_int = 3;

#[cfg(target_os = "macos", feature = "tlsv1_1")]
#[cfg(target_os = "macos", feature = "tlsv1_2")]
Expand Down Expand Up @@ -139,7 +78,7 @@ extern "C" {
pub fn SSL_CTX_new(method: *const SSL_METHOD) -> *mut SSL_CTX;
pub fn SSL_CTX_free(ctx: *mut SSL_CTX);
pub fn SSL_CTX_set_verify(ctx: *mut SSL_CTX, mode: c_int,
verify_callback: Option<extern fn(c_int, *mut X509_STORE_CTX) -> c_int>);
verify_callback: Option<extern fn(c_int, *mut x509::ffi::X509_STORE_CTX) -> c_int>);
pub fn SSL_CTX_load_verify_locations(ctx: *mut SSL_CTX, CAfile: *const c_char,
CApath: *const c_char) -> c_int;
pub fn SSL_CTX_get_ex_new_index(argl: c_long, argp: *const c_void,
Expand All @@ -154,19 +93,11 @@ extern "C" {
pub fn SSL_CTX_use_certificate_file(ctx: *mut SSL_CTX, cert_file: *const c_char, file_type: c_int) -> c_int;
pub fn SSL_CTX_use_PrivateKey_file(ctx: *mut SSL_CTX, key_file: *const c_char, file_type: c_int) -> c_int;

pub fn X509_STORE_CTX_get_ex_data(ctx: *mut X509_STORE_CTX, idx: c_int)
-> *mut c_void;
pub fn X509_STORE_CTX_get_current_cert(ct: *mut X509_STORE_CTX) -> *mut X509;
pub fn X509_STORE_CTX_get_error(ctx: *mut X509_STORE_CTX) -> c_int;

pub fn X509_get_subject_name(x: *mut X509) -> *mut X509_NAME;
pub fn X509_digest(x: *mut X509, digest: *const EVP_MD, buf: *mut c_char, len: *mut c_uint) -> c_int;

pub fn SSL_new(ctx: *mut SSL_CTX) -> *mut SSL;
pub fn SSL_free(ssl: *mut SSL);
pub fn SSL_set_bio(ssl: *mut SSL, rbio: *mut BIO, wbio: *mut BIO);
pub fn SSL_get_rbio(ssl: *mut SSL) -> *mut BIO;
pub fn SSL_get_wbio(ssl: *mut SSL) -> *mut BIO;
pub fn SSL_set_bio(ssl: *mut SSL, rbio: *mut bio::ffi::BIO, wbio: *mut bio::ffi::BIO);
pub fn SSL_get_rbio(ssl: *mut SSL) -> *mut bio::ffi::BIO;
pub fn SSL_get_wbio(ssl: *mut SSL) -> *mut bio::ffi::BIO;
pub fn SSL_connect(ssl: *mut SSL) -> c_int;
pub fn SSL_ctrl(ssl: *mut SSL, cmd: c_int, larg: c_long,
parg: *mut c_void) -> c_long;
Expand All @@ -177,12 +108,6 @@ extern "C" {
pub fn SSL_get_SSL_CTX(ssl: *mut SSL) -> *mut SSL_CTX;
pub fn SSL_get_current_compression(ssl: *mut SSL) -> *const COMP_METHOD;

pub fn BIO_s_mem() -> *const BIO_METHOD;
pub fn BIO_new(type_: *const BIO_METHOD) -> *mut BIO;
pub fn BIO_free_all(a: *mut BIO);
pub fn BIO_read(b: *mut BIO, buf: *mut c_void, len: c_int) -> c_int;
pub fn BIO_write(b: *mut BIO, buf: *const c_void, len: c_int) -> c_int;

pub fn SSL_COMP_get_name(comp: *const COMP_METHOD) -> *const c_char;
}

Expand Down
Loading

0 comments on commit 359043a

Please sign in to comment.