#![deny(missing_docs)]
use ffi;
use foreign_types::{ForeignType, ForeignTypeRef};
use libc::{c_char, c_int, c_long, time_t};
#[cfg(ossl102)]
use std::cmp::Ordering;
use std::ffi::CString;
use std::fmt;
use std::ptr;
use std::slice;
use std::str;
use bio::MemBio;
use bn::{BigNum, BigNumRef};
use error::ErrorStack;
use nid::Nid;
use string::OpensslString;
use {cvt, cvt_p};
foreign_type_and_impl_send_sync! {
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 = match MemBio::new() {
Err(_) => return f.write_str("error"),
Ok(m) => m,
};
let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print(
mem_bio.as_ptr(),
self.as_ptr(),
));
match print_result {
Err(_) => f.write_str("error"),
Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg(ossl102)]
pub struct TimeDiff {
pub days: c_int,
pub secs: c_int,
}
foreign_type_and_impl_send_sync! {
type CType = ffi::ASN1_TIME;
fn drop = ffi::ASN1_TIME_free;
pub struct Asn1Time;
pub struct Asn1TimeRef;
}
impl Asn1TimeRef {
#[cfg(ossl102)]
pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> {
let mut days = 0;
let mut secs = 0;
let other = compare.as_ptr();
let err = unsafe { ffi::ASN1_TIME_diff(&mut days, &mut secs, self.as_ptr(), other) };
match err {
0 => Err(ErrorStack::get()),
_ => Ok(TimeDiff { days, secs }),
}
}
#[cfg(ossl102)]
pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> {
let d = self.diff(other)?;
if d.days > 0 || d.secs > 0 {
return Ok(Ordering::Less);
}
if d.days < 0 || d.secs < 0 {
return Ok(Ordering::Greater);
}
Ok(Ordering::Equal)
}
}
#[cfg(ossl102)]
impl PartialEq for Asn1TimeRef {
fn eq(&self, other: &Asn1TimeRef) -> bool {
self.diff(other)
.map(|t| t.days == 0 && t.secs == 0)
.unwrap_or(false)
}
}
#[cfg(ossl102)]
impl PartialEq<Asn1Time> for Asn1TimeRef {
fn eq(&self, other: &Asn1Time) -> bool {
self.diff(other)
.map(|t| t.days == 0 && t.secs == 0)
.unwrap_or(false)
}
}
#[cfg(ossl102)]
impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef {
fn eq(&self, other: &Asn1Time) -> bool {
self.diff(other)
.map(|t| t.days == 0 && t.secs == 0)
.unwrap_or(false)
}
}
#[cfg(ossl102)]
impl PartialOrd for Asn1TimeRef {
fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
self.compare(other).ok()
}
}
#[cfg(ossl102)]
impl PartialOrd<Asn1Time> for Asn1TimeRef {
fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
self.compare(other).ok()
}
}
#[cfg(ossl102)]
impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef {
fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
self.compare(other).ok()
}
}
impl fmt::Display for Asn1TimeRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unsafe {
let mem_bio = match MemBio::new() {
Err(_) => return f.write_str("error"),
Ok(m) => m,
};
let print_result = cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr()));
match print_result {
Err(_) => f.write_str("error"),
Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
}
}
}
}
impl fmt::Debug for Asn1TimeRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.to_string())
}
}
impl Asn1Time {
fn new() -> Result<Asn1Time, ErrorStack> {
ffi::init();
unsafe {
let handle = cvt_p(ffi::ASN1_TIME_new())?;
Ok(Asn1Time::from_ptr(handle))
}
}
fn from_period(period: c_long) -> Result<Asn1Time, ErrorStack> {
ffi::init();
unsafe {
let handle = cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period))?;
Ok(Asn1Time::from_ptr(handle))
}
}
pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> {
Asn1Time::from_period(days as c_long * 60 * 60 * 24)
}
pub fn from_unix(time: time_t) -> Result<Asn1Time, ErrorStack> {
ffi::init();
unsafe {
let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?;
Ok(Asn1Time::from_ptr(handle))
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Result<Asn1Time, ErrorStack> {
unsafe {
let s = CString::new(s).unwrap();
let time = Asn1Time::new()?;
cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?;
Ok(time)
}
}
#[cfg(ossl111)]
pub fn from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack> {
unsafe {
let s = CString::new(s).unwrap();
let time = Asn1Time::new()?;
cvt(ffi::ASN1_TIME_set_string_X509(time.as_ptr(), s.as_ptr()))?;
Ok(time)
}
}
}
#[cfg(ossl102)]
impl PartialEq for Asn1Time {
fn eq(&self, other: &Asn1Time) -> bool {
self.diff(other)
.map(|t| t.days == 0 && t.secs == 0)
.unwrap_or(false)
}
}
#[cfg(ossl102)]
impl PartialEq<Asn1TimeRef> for Asn1Time {
fn eq(&self, other: &Asn1TimeRef) -> bool {
self.diff(other)
.map(|t| t.days == 0 && t.secs == 0)
.unwrap_or(false)
}
}
#[cfg(ossl102)]
impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
fn eq(&self, other: &&'a Asn1TimeRef) -> bool {
self.diff(other)
.map(|t| t.days == 0 && t.secs == 0)
.unwrap_or(false)
}
}
#[cfg(ossl102)]
impl PartialOrd for Asn1Time {
fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
self.compare(other).ok()
}
}
#[cfg(ossl102)]
impl PartialOrd<Asn1TimeRef> for Asn1Time {
fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
self.compare(other).ok()
}
}
#[cfg(ossl102)]
impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
self.compare(other).ok()
}
}
foreign_type_and_impl_send_sync! {
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<OpensslString, ErrorStack> {
unsafe {
let mut ptr = ptr::null_mut();
let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr());
if len < 0 {
return Err(ErrorStack::get());
}
Ok(OpensslString::from_ptr(ptr as *mut c_char))
}
}
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) }
}
pub fn len(&self) -> usize {
unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl fmt::Debug for Asn1StringRef {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.as_utf8() {
Ok(openssl_string) => openssl_string.fmt(fmt),
Err(_) => fmt.write_str("error"),
}
}
}
foreign_type_and_impl_send_sync! {
type CType = ffi::ASN1_INTEGER;
fn drop = ffi::ASN1_INTEGER_free;
pub struct Asn1Integer;
pub struct Asn1IntegerRef;
}
impl Asn1Integer {
pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> {
bn.to_asn1_integer()
}
}
impl Asn1IntegerRef {
#[allow(missing_docs)]
#[deprecated(since = "0.10.6", note = "use to_bn instead")]
pub fn get(&self) -> i64 {
unsafe { ::ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 }
}
pub fn to_bn(&self) -> Result<BigNum, ErrorStack> {
unsafe {
cvt_p(::ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut()))
.map(|p| BigNum::from_ptr(p))
}
}
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_and_impl_send_sync! {
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_get0_data(self.as_ptr() as *mut _), self.len()) }
}
pub fn len(&self) -> usize {
unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
foreign_type_and_impl_send_sync! {
type CType = ffi::ASN1_OBJECT;
fn drop = ffi::ASN1_OBJECT_free;
pub struct Asn1Object;
pub struct Asn1ObjectRef;
}
impl Asn1ObjectRef {
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,
);
match str::from_utf8(&buf[..len as usize]) {
Err(_) => fmt.write_str("error"),
Ok(s) => fmt.write_str(s),
}
}
}
}
impl fmt::Debug for Asn1ObjectRef {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(self.to_string().as_str())
}
}
cfg_if! {
if #[cfg(any(ossl110, libressl273))] {
use ffi::ASN1_STRING_get0_data;
} else {
#[allow(bad_style)]
unsafe fn ASN1_STRING_get0_data(s: *mut ffi::ASN1_STRING) -> *const ::libc::c_uchar {
ffi::ASN1_STRING_data(s)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use bn::BigNum;
#[test]
fn bn_cvt() {
fn roundtrip(bn: BigNum) {
let large = Asn1Integer::from_bn(&bn).unwrap();
assert_eq!(large.to_bn().unwrap(), bn);
}
roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap());
roundtrip(BigNum::from_u32(1234).unwrap());
roundtrip(-BigNum::from_u32(1234).unwrap());
}
#[test]
fn time_from_str() {
Asn1Time::from_str("99991231235959Z").unwrap();
#[cfg(ossl111)]
Asn1Time::from_str_x509("99991231235959Z").unwrap();
}
#[test]
fn time_from_unix() {
let t = Asn1Time::from_unix(0).unwrap();
assert_eq!("Jan 1 00:00:00 1970 GMT", t.to_string());
}
#[test]
#[cfg(ossl102)]
fn time_eq() {
let a = Asn1Time::from_str("99991231235959Z").unwrap();
let b = Asn1Time::from_str("99991231235959Z").unwrap();
let c = Asn1Time::from_str("99991231235958Z").unwrap();
let a_ref = a.as_ref();
let b_ref = b.as_ref();
let c_ref = c.as_ref();
assert!(a == b);
assert!(a != c);
assert!(a == b_ref);
assert!(a != c_ref);
assert!(b_ref == a);
assert!(c_ref != a);
assert!(a_ref == b_ref);
assert!(a_ref != c_ref);
}
#[test]
#[cfg(ossl102)]
fn time_ord() {
let a = Asn1Time::from_str("99991231235959Z").unwrap();
let b = Asn1Time::from_str("99991231235959Z").unwrap();
let c = Asn1Time::from_str("99991231235958Z").unwrap();
let a_ref = a.as_ref();
let b_ref = b.as_ref();
let c_ref = c.as_ref();
assert!(a >= b);
assert!(a > c);
assert!(b <= a);
assert!(c < a);
assert!(a_ref >= b);
assert!(a_ref > c);
assert!(b_ref <= a);
assert!(c_ref < a);
assert!(a >= b_ref);
assert!(a > c_ref);
assert!(b <= a_ref);
assert!(c < a_ref);
assert!(a_ref >= b_ref);
assert!(a_ref > c_ref);
assert!(b_ref <= a_ref);
assert!(c_ref < a_ref);
}
}