Commit c0e02e7e authored by Steven Fackler's avatar Steven Fackler
Browse files

Use X509Builder in X509Generator

parent 18c5d1f7
Loading
Loading
Loading
Loading
+42 −119
Original line number Diff line number Diff line
use libc::{c_char, c_int, c_long, c_ulong};
use libc::{c_int, c_long};
use std::borrow::Borrow;
use std::cmp;
use std::collections::HashMap;
@@ -14,14 +14,14 @@ use std::str;

use {cvt, cvt_p};
use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef, Asn1IntegerRef};
use bn::{BigNum, MSB_MAYBE_ZERO};
use bio::{MemBio, MemBioSlice};
use conf::ConfRef;
use hash::MessageDigest;
use pkey::{PKey, PKeyRef};
use rand::rand_bytes;
use error::ErrorStack;
use ffi;
use nid::Nid;
use nid::{self, Nid};
use types::{OpenSslType, OpenSslTypeRef};
use stack::{Stack, StackRef, Stackable};

@@ -210,123 +210,55 @@ impl X509Generator {
        self
    }

    fn add_extension_internal(x509: *mut ffi::X509,
                              exttype: &extension::ExtensionType,
                              value: &str)
                              -> Result<(), ErrorStack> {
        unsafe {
            let mut ctx: ffi::X509V3_CTX = mem::zeroed();
            ffi::X509V3_set_ctx(&mut ctx, x509, x509, ptr::null_mut(), ptr::null_mut(), 0);
            let value = CString::new(value.as_bytes()).unwrap();
            let ext = match exttype.get_nid() {
                Some(nid) => {
                    try!(cvt_p(ffi::X509V3_EXT_nconf_nid(ptr::null_mut(),
                                                         &mut ctx,
                                                         nid.as_raw(),
                                                         value.as_ptr() as *mut c_char)))
                }
                None => {
                    let name = CString::new(exttype.get_name().unwrap().as_bytes()).unwrap();
                    try!(cvt_p(ffi::X509V3_EXT_nconf(ptr::null_mut(),
                                                     &mut ctx,
                                                     name.as_ptr() as *mut c_char,
                                                     value.as_ptr() as *mut c_char)))
                }
            };
            if ffi::X509_add_ext(x509, ext, -1) != 1 {
                ffi::X509_EXTENSION_free(ext);
                Err(ErrorStack::get())
            } else {
                Ok(())
            }
        }
    }

    fn add_name_internal(name: *mut ffi::X509_NAME,
                         key: &str,
                         value: &str)
                         -> Result<(), ErrorStack> {
        let value_len = value.len() as c_int;
        unsafe {
            let key = CString::new(key.as_bytes()).unwrap();
            let value = CString::new(value.as_bytes()).unwrap();
            cvt(ffi::X509_NAME_add_entry_by_txt(name,
                                                key.as_ptr() as *const _,
                                                ffi::MBSTRING_UTF8,
                                                value.as_ptr() as *const _,
                                                value_len,
                                                -1,
                                                0))
                .map(|_| ())
        }
    }

    fn random_serial() -> Result<c_long, ErrorStack> {
        let len = mem::size_of::<c_long>();
        let mut bytes = vec![0; len];
        try!(rand_bytes(&mut bytes));
        let mut res = 0;
        for b in bytes.iter() {
            res = res << 8;
            res |= (*b as c_long) & 0xff;
        }

        // While OpenSSL is actually OK to have negative serials
        // other libraries (for example, Go crypto) can drop
        // such certificates as invalid, so we clear the high bit
        Ok(((res as c_ulong) >> 1) as c_long)
    }

    /// Sets the certificate public-key, then self-sign and return it
    pub fn sign(&self, p_key: &PKeyRef) -> Result<X509, ErrorStack> {
        ffi::init();

        unsafe {
            let x509 = X509::from_ptr(try!(cvt_p(ffi::X509_new())));
        let mut builder = try!(X509::builder());
        try!(builder.set_version(2));

            try!(cvt(ffi::X509_set_version(x509.as_ptr(), 2)));
            try!(cvt(ffi::ASN1_INTEGER_set(ffi::X509_get_serialNumber(x509.as_ptr()),
                                           try!(X509Generator::random_serial()))));
        let mut serial = try!(BigNum::new());
        try!(serial.rand(128, MSB_MAYBE_ZERO, false));
        let serial = try!(serial.to_asn1_integer());
        try!(builder.set_serial_number(&serial));

        let not_before = try!(Asn1Time::days_from_now(0));
        try!(builder.set_not_before(&not_before));
        let not_after = try!(Asn1Time::days_from_now(self.days));
        try!(builder.set_not_after(&not_after));

            try!(cvt(X509_set_notBefore(x509.as_ptr(), not_before.as_ptr() as *const _)));
            // If prev line succeded - ownership should go to cert
            mem::forget(not_before);

            try!(cvt(X509_set_notAfter(x509.as_ptr(), not_after.as_ptr() as *const _)));
            // If prev line succeded - ownership should go to cert
            mem::forget(not_after);

            try!(cvt(ffi::X509_set_pubkey(x509.as_ptr(), p_key.as_ptr())));
        try!(builder.set_pubkey(p_key));

            let name = try!(cvt_p(ffi::X509_get_subject_name(x509.as_ptr())));

            let default = [("CN", "rust-openssl")];
            let default_iter = &mut default.iter().map(|&(k, v)| (k, v));
            let arg_iter = &mut self.names.iter().map(|&(ref k, ref v)| (&k[..], &v[..]));
            let iter: &mut Iterator<Item = (&str, &str)> = if self.names.len() == 0 {
                default_iter
        let mut name = try!(X509Name::builder());
        if self.names.is_empty() {
            try!(name.append_entry_by_nid(nid::COMMONNAME, "rust-openssl"));
        } else {
                arg_iter
            };

            for (key, val) in iter {
                try!(X509Generator::add_name_internal(name, &key, &val));
            for &(ref key, ref value) in &self.names {
                try!(name.append_entry_by_text(key, value));
            }
            try!(cvt(ffi::X509_set_issuer_name(x509.as_ptr(), name)));
        }
        let name = name.build();

        try!(builder.set_subject_name(&name));
        try!(builder.set_issuer_name(&name));

        for (exttype, ext) in self.extensions.iter() {
                try!(X509Generator::add_extension_internal(x509.as_ptr(),
                                                           &exttype,
                                                           &ext.to_string()));
            let extension = match exttype.get_nid() {
                Some(nid) => {
                    let ctx = builder.x509v3_context(None, None);
                    try!(X509Extension::new_nid(None, Some(&ctx), nid, &ext.to_string()))
                }

            let hash_fn = self.hash_type.as_ptr();
            try!(cvt(ffi::X509_sign(x509.as_ptr(), p_key.as_ptr(), hash_fn)));
            Ok(x509)
                None => {
                    let ctx = builder.x509v3_context(None, None);
                    try!(X509Extension::new(None,
                                            Some(&ctx),
                                            &exttype.get_name().unwrap(),
                                            &ext.to_string()))
                }
            };
            try!(builder.append_extension(extension));
        }

        try!(builder.sign(p_key, self.hash_type));
        Ok(builder.build())
    }

    /// Obtain a certificate signing request (CSR)
@@ -1020,15 +952,6 @@ impl Stackable for GeneralName {
    type StackType = ffi::stack_st_GENERAL_NAME;
}

#[test]
fn test_negative_serial() {
    // I guess that's enough to get a random negative number
    for _ in 0..1000 {
        assert!(X509Generator::random_serial().unwrap() > 0,
                "All serials should be positive");
    }
}

#[cfg(ossl110)]
mod compat {
    pub use ffi::X509_getm_notAfter as X509_get_notAfter;