Commit e47a3cf7 authored by Valerii Hiora's avatar Valerii Hiora
Browse files

Cert loading from PEM & restructuring

- Added cert loading

- Extracted X509 tests
parent fa951b43
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -142,7 +142,7 @@ impl PKey {
        let mut mem_bio = try!(MemBio::new());
        unsafe {
            try_ssl!(ffi::PEM_write_bio_PrivateKey(mem_bio.get_handle(), self.evp, ptr::null(),
                                                   ptr::null_mut(), -1, ptr::null_mut(), ptr::null_mut()));
                                                   ptr::null_mut(), -1, None, ptr::null_mut()));

        }
        let buf = try!(mem_bio.read_to_end().map_err(StreamError));
+7 −4
Original line number Diff line number Diff line
@@ -76,7 +76,7 @@ pub type CRYPTO_EX_dup = extern "C" fn(to: *mut CRYPTO_EX_DATA,
pub type CRYPTO_EX_free = extern "C" fn(parent: *mut c_void, ptr: *mut c_void,
                                        ad: *mut CRYPTO_EX_DATA, idx: c_int,
                                        argl: c_long, argp: *mut c_void);
pub type PrivateKeyWriteCallback = extern "C" fn(buf: *mut c_char, size: c_int,
pub type PasswordCallback = extern "C" fn(buf: *mut c_char, size: c_int,
                                          rwflag: c_int, user_data: *mut c_void)
                                          -> c_int;

@@ -356,9 +356,12 @@ extern "C" {
    pub fn HMAC_Final(ctx: *mut HMAC_CTX, output: *mut u8, len: *mut c_uint);
    pub fn HMAC_Update(ctx: *mut HMAC_CTX, input: *const u8, len: c_uint);


    pub fn PEM_read_bio_X509(bio: *mut BIO, out: *mut *mut X509, callback: Option<PasswordCallback>,
                             user_data: *mut c_void) -> *mut X509;
    pub fn PEM_write_bio_PrivateKey(bio: *mut BIO, pkey: *mut EVP_PKEY, cipher: *const EVP_CIPHER,
                                    kstr: *mut c_char, klen: c_int,
                                    callback: *mut c_void,
                                    callback: Option<PasswordCallback>,
                                    user_data: *mut c_void) -> c_int;
    pub fn PEM_write_bio_X509(bio: *mut BIO, x509: *mut X509) -> c_int;

+6 −51
Original line number Diff line number Diff line
use std::io::{File, Open, Write, Writer};
use serialize::hex::FromHex;
use std::io::{Writer};
use std::io::net::tcp::TcpStream;
use std::num::FromStrRadix;
use std::str;

use crypto::hash::{SHA256};
use ssl::{Sslv23, SslContext, SslStream, SslVerifyPeer, SslVerifyNone};
use x509::{X509Generator, DigitalSignature, KeyEncipherment, ClientAuth, ServerAuth, X509StoreContext};
use ssl::{Sslv23, SslContext, SslStream, SslVerifyPeer};
use x509::{X509StoreContext};

#[test]
fn test_new_ctx() {
@@ -142,19 +142,6 @@ fn test_verify_trusted_get_error_err() {
    assert!(SslStream::new(&ctx, stream).is_err());
}

fn hash_str_to_vec(s: &str) -> Vec<u8> {
    let mut res = Vec::new();
    assert!(s.len() % 2 == 0, "Hash str should have len = 2 * n");
    for i in range(0, s.len() / 2) {
        let substr = s.slice(i, i + 2);
        let t: Option<u8> = FromStrRadix::from_str_radix(substr, 16);
        assert!(t.is_some(), "Hash str must contain only hex digits, i.e. [0-9a-f]");
        res.push(t.unwrap());
    }

    res
}

#[test]
fn test_verify_callback_data() {
    fn callback(_preverify_ok: bool, x509_ctx: &X509StoreContext, node_id: &Vec<u8>) -> bool {
@@ -175,8 +162,8 @@ fn test_verify_callback_data() {
    // 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 = "6204f6617e1af7495394250655f43600cd483e2dfc2005e92d0fe439d0723c34";
    let node_id = hash_str_to_vec(node_hash_str);
    ctx.set_verify_with_data(SslVerifyNone, callback, node_id);
    let node_id = node_hash_str.from_hex().unwrap();
    ctx.set_verify_with_data(SslVerifyPeer, callback, node_id);
    ctx.set_verify_depth(1);

    match SslStream::new(&ctx, stream) {
@@ -205,35 +192,3 @@ fn test_read() {
    let buf = stream.read_to_end().ok().expect("read error");
    print!("{}", str::from_utf8(buf.as_slice()));
}

#[test]
fn test_cert_gen() {
    let gen = X509Generator::new()
        .set_bitlength(2048)
        .set_valid_period(365*2)
        .set_CN("test_me")
        .set_sign_hash(SHA256)
        .set_usage([DigitalSignature, KeyEncipherment])
        .set_ext_usage([ClientAuth, ServerAuth]);

    let res = gen.generate();
    assert!(res.is_ok());

    let (cert, pkey) = res.unwrap();

    #[cfg(unix)]
    static NULL_PATH: &'static str = "/dev/null";
    #[cfg(windows)]
    static NULL_PATH: &'static str = "nul";

    let cert_path = Path::new(NULL_PATH);
    let mut file = File::open_mode(&cert_path, Open, Write).unwrap();
    assert!(cert.write_pem(&mut file).is_ok());

    let key_path = Path::new(NULL_PATH);
    let mut file = File::open_mode(&key_path, Open, Write).unwrap();
    assert!(pkey.write_pem(&mut file).is_ok());

    // FIXME: check data in result to be correct, needs implementation
    // of X509 getters
}
+37 −0
Original line number Diff line number Diff line
@@ -11,6 +11,9 @@ use ffi;
use ssl::error::{SslError, StreamError};


#[cfg(test)]
mod tests;

#[repr(i32)]
pub enum X509FileType {
    PEM = ffi::X509_FILETYPE_PEM,
@@ -322,6 +325,7 @@ impl X509Generator {
    }
}


#[allow(dead_code)]
/// A public key certificate
pub struct X509<'ctx> {
@@ -331,6 +335,39 @@ pub struct X509<'ctx> {
}

impl<'ctx> X509<'ctx> {
    /// Creates new from handle with desired ownership.
    pub fn new(handle: *mut ffi::X509, owned: bool) -> X509<'ctx> {
        X509 {
            ctx: None,
            handle: handle,
            owned: owned,
        }
    }

    /// Creates a new certificate from context. Doesn't take ownership
    /// of handle.
    pub fn new_in_ctx(handle: *mut ffi::X509, ctx: &'ctx X509StoreContext) -> X509<'ctx> {
        X509 {
            ctx: Some(ctx),
            handle: handle,
            owned: false
        }
    }

    /// Reads certificate from PEM, takes ownership of handle
    pub fn from_pem(reader: &mut Reader) -> Result<X509<'ctx>, SslError> {
        let mut mem_bio = try!(MemBio::new());
        let buf = try!(reader.read_to_end().map_err(StreamError));
        try!(mem_bio.write(buf.as_slice()).map_err(StreamError));

        unsafe {
            let handle = try_ssl_null!(ffi::PEM_read_bio_X509(mem_bio.get_handle(),
                                                              ptr::null_mut(),
                                                              None, ptr::null_mut()));
            Ok(X509::new(handle, true))
        }
    }

    pub fn subject_name<'a>(&'a self) -> X509Name<'a> {
        let name = unsafe { ffi::X509_get_subject_name(self.handle) };
        X509Name { x509: self, name: name }

src/x509/tests.rs

0 → 100644
+49 −0
Original line number Diff line number Diff line
use serialize::hex::FromHex;
use std::io::{File, Open, Read};
use std::io::util::NullWriter;

use crypto::hash::{SHA256};
use x509::{X509, X509Generator, DigitalSignature, KeyEncipherment, ClientAuth, ServerAuth};

#[test]
fn test_cert_gen() {
    let gen = X509Generator::new()
        .set_bitlength(2048)
        .set_valid_period(365*2)
        .set_CN("test_me")
        .set_sign_hash(SHA256)
        .set_usage([DigitalSignature, KeyEncipherment])
        .set_ext_usage([ClientAuth, ServerAuth]);

    let res = gen.generate();
    assert!(res.is_ok());

    let (cert, pkey) = res.unwrap();

    let mut writer = NullWriter;
    assert!(cert.write_pem(&mut writer).is_ok());
    assert!(pkey.write_pem(&mut writer).is_ok());

    // FIXME: check data in result to be correct, needs implementation
    // of X509 getters
}

#[test]
fn test_cert_loading() {
    let cert_path = Path::new("test/cert.pem");
    let mut file = File::open_mode(&cert_path, Open, Read)
        .ok()
        .expect("Failed to open `test/cert.pem`");

    let cert = X509::from_pem(&mut file).ok().expect("Failed to load PEM");
    let fingerprint = cert.fingerprint(SHA256).unwrap();

    // Hash was generated as SHA256 hash of certificate "test/cert.pem"
    // in DER format.
    // Command: openssl x509 -in test/cert.pem  -outform DER | openssl dgst -sha256
    // Please update if "test/cert.pem" will ever change
    let hash_str = "6204f6617e1af7495394250655f43600cd483e2dfc2005e92d0fe439d0723c34";
    let hash_vec = hash_str.from_hex().unwrap();

    assert_eq!(fingerprint.as_slice(), hash_vec.as_slice());
}