Remove chrono dependency in favour of time crate
#26
@@ -10,10 +10,10 @@ repository = "https://github.com/acw/simple_asn1"
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { default-features = false, features = ["alloc"], version = "0.4" }
|
|
||||||
num-bigint = { default-features = false, version = "0.4" }
|
num-bigint = { default-features = false, version = "0.4" }
|
||||||
num-traits = { default-features = false, version = "0.2" }
|
num-traits = { default-features = false, version = "0.2" }
|
||||||
thiserror = { default-features = false, version = "1" }
|
thiserror = { default-features = false, version = "1" }
|
||||||
|
time = { default-features = false, version = "0.3", features = ["formatting", "macros", "parsing", "quickcheck"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = "1.0.3"
|
quickcheck = "1.0.3"
|
||||||
|
|||||||
147
src/lib.rs
147
src/lib.rs
@@ -26,7 +26,6 @@
|
|||||||
//!
|
//!
|
||||||
//! Please send any bug reports, patches, and curses to the GitHub repository
|
//! Please send any bug reports, patches, and curses to the GitHub repository
|
||||||
//! at <code>https://github.com/acw/simple_asn1</code>.
|
//! at <code>https://github.com/acw/simple_asn1</code>.
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
|
||||||
pub use num_bigint::{BigInt, BigUint};
|
pub use num_bigint::{BigInt, BigUint};
|
||||||
use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
|
use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -36,6 +35,7 @@ use std::iter::FromIterator;
|
|||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use time::PrimitiveDateTime;
|
||||||
|
|
||||||
/// An ASN.1 block class.
|
/// An ASN.1 block class.
|
||||||
///
|
///
|
||||||
@@ -78,8 +78,8 @@ pub enum ASN1Block {
|
|||||||
PrintableString(usize, String),
|
PrintableString(usize, String),
|
||||||
TeletexString(usize, String),
|
TeletexString(usize, String),
|
||||||
IA5String(usize, String),
|
IA5String(usize, String),
|
||||||
UTCTime(usize, DateTime<Utc>),
|
UTCTime(usize, PrimitiveDateTime),
|
||||||
GeneralizedTime(usize, DateTime<Utc>),
|
GeneralizedTime(usize, PrimitiveDateTime),
|
||||||
UniversalString(usize, String),
|
UniversalString(usize, String),
|
||||||
BMPString(usize, String),
|
BMPString(usize, String),
|
||||||
Sequence(usize, Vec<ASN1Block>),
|
Sequence(usize, Vec<ASN1Block>),
|
||||||
@@ -296,11 +296,9 @@ const PRINTABLE_CHARS: &'static str =
|
|||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+,-./:=? ";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+,-./:=? ";
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
const KNOWN_TAGS: &[u8] =
|
const KNOWN_TAGS: &[u8] = &[
|
||||||
&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0c, 0x10, 0x11, 0x13, 0x14, 0x16, 0x17, 0x18, 0x1c, 0x1e,
|
||||||
0x0c, 0x10, 0x11, 0x13, 0x14, 0x16,
|
];
|
||||||
0x17, 0x18, 0x1c, 0x1e,
|
|
||||||
];
|
|
||||||
|
|
||||||
/// An error that can arise decoding ASN.1 primitive blocks.
|
/// An error that can arise decoding ASN.1 primitive blocks.
|
||||||
#[derive(Clone, Debug, Error, PartialEq)]
|
#[derive(Clone, Debug, Error, PartialEq)]
|
||||||
@@ -506,7 +504,28 @@ fn from_der_(i: &[u8], start_offset: usize) -> Result<Vec<ASN1Block>, ASN1Decode
|
|||||||
}
|
}
|
||||||
|
|
||||||
let v = String::from_iter(body.iter().map(|x| *x as char));
|
let v = String::from_iter(body.iter().map(|x| *x as char));
|
||||||
match Utc.datetime_from_str(&v, "%y%m%d%H%M%SZ") {
|
|
||||||
|
let y = &v[0..2];
|
||||||
|
|
||||||
|
let y_prefix = match y.parse::<u8>() {
|
||||||
|
Err(_) => return Err(ASN1DecodeErr::InvalidDateValue(v)),
|
||||||
|
Ok(y) => {
|
||||||
|
if y >= 50 {
|
||||||
|
"19"
|
||||||
|
} else {
|
||||||
|
"20"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let v = format!("{}{}", y_prefix, v);
|
||||||
|
|
||||||
|
let format = time::format_description::parse(
|
||||||
|
"[year][month][day][hour repr:24][minute][second]Z",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match PrimitiveDateTime::parse(&v, &format) {
|
||||||
Err(_) => return Err(ASN1DecodeErr::InvalidDateValue(v)),
|
Err(_) => return Err(ASN1DecodeErr::InvalidDateValue(v)),
|
||||||
Ok(t) => result.push(ASN1Block::UTCTime(soff, t)),
|
Ok(t) => result.push(ASN1Block::UTCTime(soff, t)),
|
||||||
}
|
}
|
||||||
@@ -533,7 +552,13 @@ fn from_der_(i: &[u8], start_offset: usize) -> Result<Vec<ASN1Block>, ASN1Decode
|
|||||||
let idx = v.len() - 1;
|
let idx = v.len() - 1;
|
||||||
v.insert(idx, '0');
|
v.insert(idx, '0');
|
||||||
}
|
}
|
||||||
match Utc.datetime_from_str(&v, "%Y%m%d%H%M%S.%fZ") {
|
|
||||||
|
let format = time::format_description::parse(
|
||||||
|
"[year][month][day][hour repr:24][minute][second].[subsecond]Z",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match PrimitiveDateTime::parse(&v, &format) {
|
||||||
Err(_) => return Err(ASN1DecodeErr::InvalidDateValue(v)),
|
Err(_) => return Err(ASN1DecodeErr::InvalidDateValue(v)),
|
||||||
Ok(t) => result.push(ASN1Block::GeneralizedTime(soff, t)),
|
Ok(t) => result.push(ASN1Block::GeneralizedTime(soff, t)),
|
||||||
}
|
}
|
||||||
@@ -807,7 +832,12 @@ pub fn to_der(i: &ASN1Block) -> Result<Vec<u8>, ASN1EncodeErr> {
|
|||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
&ASN1Block::UTCTime(_, ref time) => {
|
&ASN1Block::UTCTime(_, ref time) => {
|
||||||
let mut body = time.format("%y%m%d%H%M%SZ").to_string().into_bytes();
|
let format = time::format_description::parse(
|
||||||
|
"[year][month][day][hour repr:24][minute][second]Z",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut body = time.format(&format).unwrap().into_bytes();
|
||||||
|
body.drain(0..2);
|
||||||
let inttag = BigUint::from_u8(0x17).unwrap();
|
let inttag = BigUint::from_u8(0x17).unwrap();
|
||||||
let mut lenbytes = encode_len(body.len());
|
let mut lenbytes = encode_len(body.len());
|
||||||
let mut tagbytes = encode_tag(ASN1Class::Universal, false, &inttag);
|
let mut tagbytes = encode_tag(ASN1Class::Universal, false, &inttag);
|
||||||
@@ -819,7 +849,11 @@ pub fn to_der(i: &ASN1Block) -> Result<Vec<u8>, ASN1EncodeErr> {
|
|||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
&ASN1Block::GeneralizedTime(_, ref time) => {
|
&ASN1Block::GeneralizedTime(_, ref time) => {
|
||||||
let base = time.format("%Y%m%d%H%M%S.%f").to_string();
|
let format = time::format_description::parse(
|
||||||
|
"[year][month][day][hour repr:24][minute][second].[subsecond]",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let base = time.format(&format).unwrap();
|
||||||
let zclear = base.trim_end_matches('0');
|
let zclear = base.trim_end_matches('0');
|
||||||
let dclear = zclear.trim_end_matches('.');
|
let dclear = zclear.trim_end_matches('.');
|
||||||
let mut body = format!("{}Z", dclear).into_bytes();
|
let mut body = format!("{}Z", dclear).into_bytes();
|
||||||
@@ -1061,10 +1095,10 @@ pub fn der_encode<T: ToASN1>(v: &T) -> Result<Vec<u8>, T::Error> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use chrono::offset::LocalResult;
|
|
||||||
use quickcheck::{Arbitrary, Gen};
|
use quickcheck::{Arbitrary, Gen};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use time::{Date, Month, Time};
|
||||||
|
|
||||||
impl Arbitrary for ASN1Class {
|
impl Arbitrary for ASN1Class {
|
||||||
fn arbitrary(g: &mut Gen) -> ASN1Class {
|
fn arbitrary(g: &mut Gen) -> ASN1Class {
|
||||||
@@ -1152,7 +1186,7 @@ mod tests {
|
|||||||
} else {
|
} else {
|
||||||
maxbits - modbits
|
maxbits - modbits
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut bytes = Vec::with_capacity(size);
|
let mut bytes = Vec::with_capacity(size);
|
||||||
while bytes.len() < size {
|
while bytes.len() < size {
|
||||||
bytes.push(u8::arbitrary(g));
|
bytes.push(u8::arbitrary(g));
|
||||||
@@ -1266,42 +1300,38 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn arb_utc(g: &mut Gen, _d: usize) -> ASN1Block {
|
fn arb_utc(g: &mut Gen, _d: usize) -> ASN1Block {
|
||||||
loop {
|
let min = Date::from_calendar_date(1950, Month::January, 01)
|
||||||
let y = (i32::arbitrary(g) % 100).abs() + 1970;
|
.unwrap()
|
||||||
let m = u32::arbitrary(g) % 12 + 1;
|
.to_julian_day();
|
||||||
let d = u32::arbitrary(g) % 31 + 1;
|
let max = Date::from_calendar_date(2049, Month::December, 31)
|
||||||
match Utc.ymd_opt(y, m, d) {
|
.unwrap()
|
||||||
LocalResult::None => {}
|
.to_julian_day();
|
||||||
LocalResult::Single(d) => {
|
let date =
|
||||||
let h = u32::arbitrary(g) % 24;
|
Date::from_julian_day(i32::arbitrary(g).rem_euclid(max - min + 1) + min).unwrap();
|
||||||
let m = u32::arbitrary(g) % 60;
|
|
||||||
let s = u32::arbitrary(g) % 60;
|
let h = u8::arbitrary(g).rem_euclid(24);
|
||||||
let t = d.and_hms(h, m, s);
|
let m = u8::arbitrary(g).rem_euclid(60);
|
||||||
return ASN1Block::UTCTime(0, t);
|
let s = u8::arbitrary(g).rem_euclid(60);
|
||||||
}
|
let time = Time::from_hms(h, m, s).unwrap();
|
||||||
LocalResult::Ambiguous(_, _) => {}
|
|
||||||
}
|
let t = PrimitiveDateTime::new(date, time);
|
||||||
}
|
ASN1Block::UTCTime(0, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arb_time(g: &mut Gen, _d: usize) -> ASN1Block {
|
fn arb_time(g: &mut Gen, _d: usize) -> ASN1Block {
|
||||||
loop {
|
let min = Date::from_calendar_date(0, Month::January, 01)
|
||||||
let y = (i32::arbitrary(g) % 10000).abs();
|
.unwrap()
|
||||||
let m = u32::arbitrary(g) % 12 + 1;
|
.to_julian_day();
|
||||||
let d = u32::arbitrary(g) % 31 + 1;
|
let max = Date::from_calendar_date(9999, Month::December, 31)
|
||||||
match Utc.ymd_opt(y, m, d) {
|
.unwrap()
|
||||||
LocalResult::None => {}
|
.to_julian_day();
|
||||||
LocalResult::Single(d) => {
|
let date =
|
||||||
let h = u32::arbitrary(g) % 24;
|
Date::from_julian_day(i32::arbitrary(g).rem_euclid(max - min + 1) + min).unwrap();
|
||||||
let m = u32::arbitrary(g) % 60;
|
|
||||||
let s = u32::arbitrary(g) % 60;
|
let time = Time::arbitrary(g);
|
||||||
let n = u32::arbitrary(g) % 1000000000;
|
|
||||||
let t = d.and_hms_nano(h, m, s, n);
|
let t = PrimitiveDateTime::new(date, time);
|
||||||
return ASN1Block::GeneralizedTime(0, t);
|
ASN1Block::GeneralizedTime(0, t)
|
||||||
}
|
|
||||||
LocalResult::Ambiguous(_, _) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arb_explicit(g: &mut Gen, d: usize) -> ASN1Block {
|
fn arb_explicit(g: &mut Gen, d: usize) -> ASN1Block {
|
||||||
@@ -1319,16 +1349,16 @@ mod tests {
|
|||||||
fn arb_unknown(g: &mut Gen, _d: usize) -> ASN1Block {
|
fn arb_unknown(g: &mut Gen, _d: usize) -> ASN1Block {
|
||||||
let class = ASN1Class::arbitrary(g);
|
let class = ASN1Class::arbitrary(g);
|
||||||
let tag = loop {
|
let tag = loop {
|
||||||
let potential = RandomUint::arbitrary(g);
|
let potential = RandomUint::arbitrary(g);
|
||||||
match potential.x.to_u8() {
|
match potential.x.to_u8() {
|
||||||
None => break potential,
|
None => break potential,
|
||||||
Some(x) if KNOWN_TAGS.contains(&x) => {},
|
Some(x) if KNOWN_TAGS.contains(&x) => {}
|
||||||
Some(_) => break potential,
|
Some(_) => break potential,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let size = usize::arbitrary(g) % 128;
|
let size = usize::arbitrary(g) % 128;
|
||||||
let mut items = Vec::with_capacity(size);
|
let mut items = Vec::with_capacity(size);
|
||||||
|
|
||||||
while items.len() < size {
|
while items.len() < size {
|
||||||
items.push(u8::arbitrary(g));
|
items.push(u8::arbitrary(g));
|
||||||
}
|
}
|
||||||
@@ -1411,20 +1441,29 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn generalized_time_tests() {
|
fn generalized_time_tests() {
|
||||||
check_spec(
|
check_spec(
|
||||||
&Utc.ymd(1992, 5, 21).and_hms(0, 0, 0),
|
&PrimitiveDateTime::new(
|
||||||
|
Date::from_calendar_date(1992, Month::May, 21).unwrap(),
|
||||||
|
Time::from_hms(0, 0, 0).unwrap(),
|
||||||
|
),
|
||||||
"19920521000000Z".to_string(),
|
"19920521000000Z".to_string(),
|
||||||
);
|
);
|
||||||
check_spec(
|
check_spec(
|
||||||
&Utc.ymd(1992, 6, 22).and_hms(12, 34, 21),
|
&PrimitiveDateTime::new(
|
||||||
|
Date::from_calendar_date(1992, Month::June, 22).unwrap(),
|
||||||
|
Time::from_hms(12, 34, 21).unwrap(),
|
||||||
|
),
|
||||||
"19920622123421Z".to_string(),
|
"19920622123421Z".to_string(),
|
||||||
);
|
);
|
||||||
check_spec(
|
check_spec(
|
||||||
&Utc.ymd(1992, 7, 22).and_hms_milli(13, 21, 00, 300),
|
&PrimitiveDateTime::new(
|
||||||
|
Date::from_calendar_date(1992, Month::July, 22).unwrap(),
|
||||||
|
Time::from_hms_milli(13, 21, 00, 300).unwrap(),
|
||||||
|
),
|
||||||
"19920722132100.3Z".to_string(),
|
"19920722132100.3Z".to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_spec(d: &DateTime<Utc>, s: String) {
|
fn check_spec(d: &PrimitiveDateTime, s: String) {
|
||||||
let b = ASN1Block::GeneralizedTime(0, d.clone());
|
let b = ASN1Block::GeneralizedTime(0, d.clone());
|
||||||
match to_der(&b) {
|
match to_der(&b) {
|
||||||
Err(_) => assert_eq!(format!("Broken: {}", d), s),
|
Err(_) => assert_eq!(format!("Broken: {}", d), s),
|
||||||
|
|||||||
Reference in New Issue
Block a user