diff options
Diffstat (limited to 'vendor/openssl/src/x509/tests.rs')
-rw-r--r-- | vendor/openssl/src/x509/tests.rs | 1161 |
1 files changed, 1161 insertions, 0 deletions
diff --git a/vendor/openssl/src/x509/tests.rs b/vendor/openssl/src/x509/tests.rs new file mode 100644 index 0000000..da3ce2f --- /dev/null +++ b/vendor/openssl/src/x509/tests.rs @@ -0,0 +1,1161 @@ +use std::cmp::Ordering; + +use crate::asn1::{Asn1Object, Asn1OctetString, Asn1Time}; +use crate::bn::{BigNum, MsbOption}; +use crate::hash::MessageDigest; +use crate::nid::Nid; +use crate::pkey::{PKey, Private}; +use crate::rsa::Rsa; +#[cfg(not(boringssl))] +use crate::ssl::SslFiletype; +use crate::stack::Stack; +use crate::x509::extension::{ + AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, + SubjectKeyIdentifier, +}; +#[cfg(not(boringssl))] +use crate::x509::store::X509Lookup; +use crate::x509::store::X509StoreBuilder; +#[cfg(any(ossl102, libressl261))] +use crate::x509::verify::{X509VerifyFlags, X509VerifyParam}; +#[cfg(ossl102)] +use crate::x509::X509PurposeId; +#[cfg(any(ossl102, libressl261))] +use crate::x509::X509PurposeRef; +#[cfg(ossl110)] +use crate::x509::{CrlReason, X509Builder}; +use crate::x509::{ + CrlStatus, X509Crl, X509Extension, X509Name, X509Req, X509StoreContext, X509VerifyResult, X509, +}; + +#[cfg(ossl110)] +use foreign_types::ForeignType; +use hex::{self, FromHex}; +#[cfg(any(ossl102, libressl261))] +use libc::time_t; + +use super::{CertificateIssuer, ReasonCode}; + +fn pkey() -> PKey<Private> { + let rsa = Rsa::generate(2048).unwrap(); + PKey::from_rsa(rsa).unwrap() +} + +#[test] +fn test_cert_loading() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let fingerprint = cert.digest(MessageDigest::sha1()).unwrap(); + + let hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; + let hash_vec = Vec::from_hex(hash_str).unwrap(); + + assert_eq!(hash_vec, &*fingerprint); +} + +#[test] +fn test_debug() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let debugged = format!("{:#?}", cert); + #[cfg(boringssl)] + assert!(debugged.contains(r#"serial_number: "8771f7bdee982fa5""#)); + #[cfg(not(boringssl))] + assert!(debugged.contains(r#"serial_number: "8771F7BDEE982FA5""#)); + assert!(debugged.contains(r#"signature_algorithm: sha256WithRSAEncryption"#)); + assert!(debugged.contains(r#"countryName = "AU""#)); + assert!(debugged.contains(r#"stateOrProvinceName = "Some-State""#)); + assert!(debugged.contains(r#"not_before: Aug 14 17:00:03 2016 GMT"#)); + assert!(debugged.contains(r#"not_after: Aug 12 17:00:03 2026 GMT"#)); +} + +#[test] +fn test_cert_issue_validity() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let not_before = cert.not_before().to_string(); + let not_after = cert.not_after().to_string(); + + assert_eq!(not_before, "Aug 14 17:00:03 2016 GMT"); + assert_eq!(not_after, "Aug 12 17:00:03 2026 GMT"); +} + +#[test] +fn test_save_der() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let der = cert.to_der().unwrap(); + assert!(!der.is_empty()); +} + +#[test] +fn test_subject_read_cn() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap(); + assert_eq!(cn.data().as_slice(), b"foobar.com") +} + +#[test] +fn test_nid_values() { + let cert = include_bytes!("../../test/nid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + + let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap(); + assert_eq!(cn.data().as_slice(), b"example.com"); + + let email = subject + .entries_by_nid(Nid::PKCS9_EMAILADDRESS) + .next() + .unwrap(); + assert_eq!(email.data().as_slice(), b"test@example.com"); + + let friendly = subject.entries_by_nid(Nid::FRIENDLYNAME).next().unwrap(); + assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example"); +} + +#[test] +fn test_nameref_iterator() { + let cert = include_bytes!("../../test/nid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + let mut all_entries = subject.entries(); + + let email = all_entries.next().unwrap(); + assert_eq!( + email.object().nid().as_raw(), + Nid::PKCS9_EMAILADDRESS.as_raw() + ); + assert_eq!(email.data().as_slice(), b"test@example.com"); + + let cn = all_entries.next().unwrap(); + assert_eq!(cn.object().nid().as_raw(), Nid::COMMONNAME.as_raw()); + assert_eq!(cn.data().as_slice(), b"example.com"); + + let friendly = all_entries.next().unwrap(); + assert_eq!(friendly.object().nid().as_raw(), Nid::FRIENDLYNAME.as_raw()); + assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example"); + + if all_entries.next().is_some() { + panic!(); + } +} + +#[test] +fn test_nid_uid_value() { + let cert = include_bytes!("../../test/nid_uid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + + let cn = subject.entries_by_nid(Nid::USERID).next().unwrap(); + assert_eq!(cn.data().as_slice(), b"this is the userId"); +} + +#[test] +fn test_subject_alt_name() { + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject_alt_names = cert.subject_alt_names().unwrap(); + assert_eq!(5, subject_alt_names.len()); + assert_eq!(Some("example.com"), subject_alt_names[0].dnsname()); + assert_eq!(subject_alt_names[1].ipaddress(), Some(&[127, 0, 0, 1][..])); + assert_eq!( + subject_alt_names[2].ipaddress(), + Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]) + ); + assert_eq!(Some("test@example.com"), subject_alt_names[3].email()); + assert_eq!(Some("http://www.example.com"), subject_alt_names[4].uri()); +} + +#[test] +#[cfg(ossl110)] +fn test_retrieve_pathlen() { + let cert = include_bytes!("../../test/root-ca.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert_eq!(cert.pathlen(), None); + + let cert = include_bytes!("../../test/intermediate-ca.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert_eq!(cert.pathlen(), Some(0)); + + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert_eq!(cert.pathlen(), None); +} + +#[test] +#[cfg(ossl110)] +fn test_subject_key_id() { + let cert = include_bytes!("../../test/certv3.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject_key_id = cert.subject_key_id().unwrap(); + assert_eq!( + subject_key_id.as_slice(), + &b"\xB6\x73\x2F\x61\xA5\x4B\xA1\xEF\x48\x2C\x15\xB1\x9F\xF3\xDC\x34\x2F\xBC\xAC\x30"[..] + ); +} + +#[test] +#[cfg(ossl110)] +fn test_authority_key_id() { + let cert = include_bytes!("../../test/certv3.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let authority_key_id = cert.authority_key_id().unwrap(); + assert_eq!( + authority_key_id.as_slice(), + &b"\x6C\xD3\xA5\x03\xAB\x0D\x5F\x2C\xC9\x8D\x8A\x9C\x88\xA7\x88\x77\xB8\x37\xFD\x9A"[..] + ); +} + +#[test] +#[cfg(ossl111d)] +fn test_authority_issuer_and_serial() { + let cert = include_bytes!("../../test/authority_key_identifier.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let authority_issuer = cert.authority_issuer().unwrap(); + assert_eq!(1, authority_issuer.len()); + let dn = authority_issuer[0].directory_name().unwrap(); + let mut o = dn.entries_by_nid(Nid::ORGANIZATIONNAME); + let o = o.next().unwrap().data().as_utf8().unwrap(); + assert_eq!(o.as_bytes(), b"PyCA"); + let mut cn = dn.entries_by_nid(Nid::COMMONNAME); + let cn = cn.next().unwrap().data().as_utf8().unwrap(); + assert_eq!(cn.as_bytes(), b"cryptography.io"); + + let authority_serial = cert.authority_serial().unwrap(); + let serial = authority_serial.to_bn().unwrap(); + let expected = BigNum::from_u32(3).unwrap(); + assert_eq!(serial, expected); +} + +#[test] +fn test_subject_alt_name_iter() { + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject_alt_names = cert.subject_alt_names().unwrap(); + let mut subject_alt_names_iter = subject_alt_names.iter(); + assert_eq!( + subject_alt_names_iter.next().unwrap().dnsname(), + Some("example.com") + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().ipaddress(), + Some(&[127, 0, 0, 1][..]) + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().ipaddress(), + Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]) + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().email(), + Some("test@example.com") + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().uri(), + Some("http://www.example.com") + ); + assert!(subject_alt_names_iter.next().is_none()); +} + +#[test] +fn test_aia_ca_issuer() { + // With AIA + let cert = include_bytes!("../../test/aia_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let authority_info = cert.authority_info().unwrap(); + assert_eq!(authority_info.len(), 1); + assert_eq!(authority_info[0].method().to_string(), "CA Issuers"); + assert_eq!( + authority_info[0].location().uri(), + Some("http://www.example.com/cert.pem") + ); + // Without AIA + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert!(cert.authority_info().is_none()); +} + +#[test] +fn x509_builder() { + let pkey = pkey(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com") + .unwrap(); + let name = name.build(); + + let mut builder = X509::builder().unwrap(); + builder.set_version(2).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_issuer_name(&name).unwrap(); + builder + .set_not_before(&Asn1Time::days_from_now(0).unwrap()) + .unwrap(); + builder + .set_not_after(&Asn1Time::days_from_now(365).unwrap()) + .unwrap(); + builder.set_pubkey(&pkey).unwrap(); + + let mut serial = BigNum::new().unwrap(); + serial.rand(128, MsbOption::MAYBE_ZERO, false).unwrap(); + builder + .set_serial_number(&serial.to_asn1_integer().unwrap()) + .unwrap(); + + let basic_constraints = BasicConstraints::new().critical().ca().build().unwrap(); + builder.append_extension(basic_constraints).unwrap(); + let key_usage = KeyUsage::new() + .digital_signature() + .key_encipherment() + .build() + .unwrap(); + builder.append_extension(key_usage).unwrap(); + let ext_key_usage = ExtendedKeyUsage::new() + .client_auth() + .server_auth() + .other("2.999.1") + .build() + .unwrap(); + builder.append_extension(ext_key_usage).unwrap(); + let subject_key_identifier = SubjectKeyIdentifier::new() + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(subject_key_identifier).unwrap(); + let authority_key_identifier = AuthorityKeyIdentifier::new() + .keyid(true) + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(authority_key_identifier).unwrap(); + let subject_alternative_name = SubjectAlternativeName::new() + .dns("example.com") + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(subject_alternative_name).unwrap(); + + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + + let x509 = builder.build(); + + assert!(pkey.public_eq(&x509.public_key().unwrap())); + assert!(x509.verify(&pkey).unwrap()); + + let cn = x509 + .subject_name() + .entries_by_nid(Nid::COMMONNAME) + .next() + .unwrap(); + assert_eq!(cn.data().as_slice(), b"foobar.com"); + assert_eq!(serial, x509.serial_number().to_bn().unwrap()); +} + +#[test] +// This tests `X509Extension::new`, even though its deprecated. +#[allow(deprecated)] +fn x509_extension_new() { + assert!(X509Extension::new(None, None, "crlDistributionPoints", "section").is_err()); + assert!(X509Extension::new(None, None, "proxyCertInfo", "").is_err()); + assert!(X509Extension::new(None, None, "certificatePolicies", "").is_err()); + assert!(X509Extension::new(None, None, "subjectAltName", "dirName:section").is_err()); +} + +#[test] +fn x509_extension_new_from_der() { + let ext = X509Extension::new_from_der( + &Asn1Object::from_str("2.5.29.19").unwrap(), + true, + &Asn1OctetString::new_from_bytes(b"\x30\x03\x01\x01\xff").unwrap(), + ) + .unwrap(); + assert_eq!( + ext.to_der().unwrap(), + b"0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff" + ); +} + +#[test] +fn x509_extension_to_der() { + let builder = X509::builder().unwrap(); + + for (ext, expected) in [ + ( + BasicConstraints::new().critical().ca().build().unwrap(), + b"0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff" as &[u8], + ), + ( + SubjectAlternativeName::new() + .dns("example.com,DNS:example2.com") + .build(&builder.x509v3_context(None, None)) + .unwrap(), + b"0'\x06\x03U\x1d\x11\x04 0\x1e\x82\x1cexample.com,DNS:example2.com", + ), + ( + SubjectAlternativeName::new() + .rid("1.2.3.4") + .uri("https://example.com") + .build(&builder.x509v3_context(None, None)) + .unwrap(), + b"0#\x06\x03U\x1d\x11\x04\x1c0\x1a\x88\x03*\x03\x04\x86\x13https://example.com", + ), + ( + ExtendedKeyUsage::new() + .server_auth() + .other("2.999.1") + .other("clientAuth") + .build() + .unwrap(), + b"0\x22\x06\x03U\x1d%\x04\x1b0\x19\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x03\x887\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x02", + ), + ] { + assert_eq!(&ext.to_der().unwrap(), expected); + } +} + +#[test] +fn eku_invalid_other() { + assert!(ExtendedKeyUsage::new() + .other("1.1.1.1.1,2.2.2.2.2") + .build() + .is_err()); +} + +#[test] +fn x509_req_builder() { + let pkey = pkey(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com") + .unwrap(); + let name = name.build(); + + let mut builder = X509Req::builder().unwrap(); + builder.set_version(0).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_pubkey(&pkey).unwrap(); + + let mut extensions = Stack::new().unwrap(); + let key_usage = KeyUsage::new() + .digital_signature() + .key_encipherment() + .build() + .unwrap(); + extensions.push(key_usage).unwrap(); + let subject_alternative_name = SubjectAlternativeName::new() + .dns("example.com") + .build(&builder.x509v3_context(None)) + .unwrap(); + extensions.push(subject_alternative_name).unwrap(); + builder.add_extensions(&extensions).unwrap(); + + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + + let req = builder.build(); + assert!(req.public_key().unwrap().public_eq(&pkey)); + assert_eq!(req.extensions().unwrap().len(), extensions.len()); + assert!(req.verify(&pkey).unwrap()); +} + +#[test] +fn test_stack_from_pem() { + let certs = include_bytes!("../../test/certs.pem"); + let certs = X509::stack_from_pem(certs).unwrap(); + + assert_eq!(certs.len(), 2); + assert_eq!( + hex::encode(certs[0].digest(MessageDigest::sha1()).unwrap()), + "59172d9313e84459bcff27f967e79e6e9217e584" + ); + assert_eq!( + hex::encode(certs[1].digest(MessageDigest::sha1()).unwrap()), + "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875" + ); +} + +#[test] +fn issued() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + + assert_eq!(ca.issued(&cert), X509VerifyResult::OK); + assert_ne!(cert.issued(&cert), X509VerifyResult::OK); +} + +#[test] +fn signature() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let signature = cert.signature(); + assert_eq!( + hex::encode(signature.as_slice()), + "4af607b889790b43470442cfa551cdb8b6d0b0340d2958f76b9e3ef6ad4992230cead6842587f0ecad5\ + 78e6e11a221521e940187e3d6652de14e84e82f6671f097cc47932e022add3c0cb54a26bf27fa84c107\ + 4971caa6bee2e42d34a5b066c427f2d452038082b8073993399548088429de034fdd589dcfb0dd33be7\ + ebdfdf698a28d628a89568881d658151276bde333600969502c4e62e1d3470a683364dfb241f78d310a\ + 89c119297df093eb36b7fd7540224f488806780305d1e79ffc938fe2275441726522ab36d88348e6c51\ + f13dcc46b5e1cdac23c974fd5ef86aa41e91c9311655090a52333bc79687c748d833595d4c5f987508f\ + e121997410d37c" + ); + let algorithm = cert.signature_algorithm(); + assert_eq!(algorithm.object().nid(), Nid::SHA256WITHRSAENCRYPTION); + assert_eq!(algorithm.object().to_string(), "sha256WithRSAEncryption"); +} + +#[test] +#[allow(clippy::redundant_clone)] +fn clone_x509() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + drop(cert.clone()); +} + +#[test] +fn test_verify_cert() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +fn test_verify_fails() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/alt_name_cert.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(!context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_fails_with_crl_flag_set_and_no_crl() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + store_bldr.set_flags(X509VerifyFlags::CRL_CHECK).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .error_string(), + "unable to get certificate CRL" + ) +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_cert_with_purpose() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + let purpose_idx = X509PurposeRef::get_by_sname("sslserver") + .expect("Getting certificate purpose 'sslserver' failed"); + let x509_purposeref = + X509PurposeRef::from_idx(purpose_idx).expect("Getting certificate purpose failed"); + store_bldr + .set_purpose(x509_purposeref.purpose()) + .expect("Setting certificate purpose failed"); + store_bldr.add_cert(ca).unwrap(); + + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_cert_with_wrong_purpose_fails() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + let purpose_idx = X509PurposeRef::get_by_sname("timestampsign") + .expect("Getting certificate purpose 'timestampsign' failed"); + let x509_purpose = + X509PurposeRef::from_idx(purpose_idx).expect("Getting certificate purpose failed"); + store_bldr + .set_purpose(x509_purpose.purpose()) + .expect("Setting certificate purpose failed"); + store_bldr.add_cert(ca).unwrap(); + + let store = store_bldr.build(); + + let expected_error = ffi::X509_V_ERR_INVALID_PURPOSE; + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .as_raw(), + expected_error + ) +} + +#[cfg(ossl110)] +#[test] +fn x509_ref_version() { + let mut builder = X509Builder::new().unwrap(); + let expected_version = 2; + builder + .set_version(expected_version) + .expect("Failed to set certificate version"); + let cert = builder.build(); + let actual_version = cert.version(); + assert_eq!( + expected_version, actual_version, + "Obtained certificate version is incorrect", + ); +} + +#[cfg(ossl110)] +#[test] +fn x509_ref_version_no_version_set() { + let cert = X509Builder::new().unwrap().build(); + let actual_version = cert.version(); + assert_eq!( + 0, actual_version, + "Default certificate version is incorrect", + ); +} + +#[test] +fn test_load_crl() { + let ca = include_bytes!("../../test/crl-ca.crt"); + let ca = X509::from_pem(ca).unwrap(); + + let crl = include_bytes!("../../test/test.crl"); + let crl = X509Crl::from_der(crl).unwrap(); + assert!(crl.verify(&ca.public_key().unwrap()).unwrap()); + + let cert = include_bytes!("../../test/subca.crt"); + let cert = X509::from_pem(cert).unwrap(); + + let revoked = match crl.get_by_cert(&cert) { + CrlStatus::Revoked(revoked) => revoked, + _ => panic!("cert should be revoked"), + }; + + assert_eq!( + revoked.serial_number().to_bn().unwrap(), + cert.serial_number().to_bn().unwrap(), + "revoked and cert serial numbers should match" + ); +} + +#[test] +fn test_crl_entry_extensions() { + let crl = include_bytes!("../../test/entry_extensions.crl"); + let crl = X509Crl::from_pem(crl).unwrap(); + + let revoked_certs = crl.get_revoked().unwrap(); + let entry = &revoked_certs[0]; + + let (critical, issuer) = entry + .extension::<CertificateIssuer>() + .unwrap() + .expect("Certificate issuer extension should be present"); + assert!(critical, "Certificate issuer extension is critical"); + assert_eq!(issuer.len(), 1, "Certificate issuer should have one entry"); + let issuer = issuer[0] + .directory_name() + .expect("Issuer should be a directory name"); + assert_eq!( + format!("{:?}", issuer), + r#"[countryName = "GB", commonName = "Test CA"]"# + ); + + // reason_code can't be inspected without ossl110 + #[allow(unused_variables)] + let (critical, reason_code) = entry + .extension::<ReasonCode>() + .unwrap() + .expect("Reason code extension should be present"); + assert!(!critical, "Reason code extension is not critical"); + #[cfg(ossl110)] + assert_eq!( + CrlReason::KEY_COMPROMISE, + CrlReason::from_raw(reason_code.get_i64().unwrap() as ffi::c_int) + ); +} + +#[test] +fn test_save_subject_der() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let der = cert.subject_name().to_der().unwrap(); + println!("der: {:?}", der); + assert!(!der.is_empty()); +} + +#[test] +fn test_load_subject_der() { + // The subject from ../../test/cert.pem + const SUBJECT_DER: &[u8] = &[ + 48, 90, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 65, 85, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, + 10, 83, 111, 109, 101, 45, 83, 116, 97, 116, 101, 49, 33, 48, 31, 6, 3, 85, 4, 10, 12, 24, + 73, 110, 116, 101, 114, 110, 101, 116, 32, 87, 105, 100, 103, 105, 116, 115, 32, 80, 116, + 121, 32, 76, 116, 100, 49, 19, 48, 17, 6, 3, 85, 4, 3, 12, 10, 102, 111, 111, 98, 97, 114, + 46, 99, 111, 109, + ]; + X509Name::from_der(SUBJECT_DER).unwrap(); +} + +#[test] +fn test_convert_to_text() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + const SUBSTRINGS: &[&str] = &[ + "Certificate:\n", + "Serial Number:", + "Signature Algorithm:", + "Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd\n", + "Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=foobar.com\n", + "Subject Public Key Info:", + ]; + + let text = String::from_utf8(cert.to_text().unwrap()).unwrap(); + + for substring in SUBSTRINGS { + assert!( + text.contains(substring), + "{:?} not found inside {}", + substring, + text + ); + } +} + +#[test] +fn test_convert_req_to_text() { + let csr = include_bytes!("../../test/csr.pem"); + let csr = X509Req::from_pem(csr).unwrap(); + + const SUBSTRINGS: &[&str] = &[ + "Certificate Request:\n", + "Version:", + "Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=foobar.com\n", + "Subject Public Key Info:", + "Signature Algorithm:", + ]; + + let text = String::from_utf8(csr.to_text().unwrap()).unwrap(); + + for substring in SUBSTRINGS { + assert!( + text.contains(substring), + "{:?} not found inside {}", + substring, + text + ); + } +} + +#[test] +fn test_name_cmp() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject = cert.subject_name(); + let issuer = cert.issuer_name(); + assert_eq!(Ordering::Equal, subject.try_cmp(subject).unwrap()); + assert_eq!(Ordering::Greater, subject.try_cmp(issuer).unwrap()); +} + +#[test] +#[cfg(any(boringssl, ossl110, libressl270))] +fn test_name_to_owned() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let name = cert.subject_name(); + let copied_name = name.to_owned().unwrap(); + assert_eq!(Ordering::Equal, name.try_cmp(&copied_name).unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_param_set_time_fails_verification() { + const TEST_T_2030: time_t = 1893456000; + + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params.set_time(TEST_T_2030); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .error_string(), + "certificate has expired" + ) +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_param_set_time() { + const TEST_T_2020: time_t = 1577836800; + + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params.set_time(TEST_T_2020); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_param_set_depth() { + let cert = include_bytes!("../../test/leaf.pem"); + let cert = X509::from_pem(cert).unwrap(); + let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem"); + let intermediate_ca = X509::from_pem(intermediate_ca).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let mut chain = Stack::new().unwrap(); + chain.push(intermediate_ca).unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + // OpenSSL 1.1.0+ considers the root certificate to not be part of the chain, while 1.0.2 and LibreSSL do + let expected_depth = if cfg!(any(ossl110)) { 1 } else { 2 }; + verify_params.set_depth(expected_depth); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +#[allow(clippy::bool_to_int_with_if)] +fn test_verify_param_set_depth_fails_verification() { + let cert = include_bytes!("../../test/leaf.pem"); + let cert = X509::from_pem(cert).unwrap(); + let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem"); + let intermediate_ca = X509::from_pem(intermediate_ca).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let mut chain = Stack::new().unwrap(); + chain.push(intermediate_ca).unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + // OpenSSL 1.1.0+ considers the root certificate to not be part of the chain, while 1.0.2 and LibreSSL do + let expected_depth = if cfg!(any(ossl110)) { 0 } else { 1 }; + verify_params.set_depth(expected_depth); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + // OpenSSL 1.1.0+ added support for X509_V_ERR_CERT_CHAIN_TOO_LONG, while 1.0.2 simply ignores the intermediate + let expected_error = if cfg!(any(ossl110, libressl261)) { + "certificate chain too long" + } else { + "unable to get local issuer certificate" + }; + + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .error_string(), + expected_error + ) +} + +#[test] +#[cfg(not(boringssl))] +fn test_load_cert_file() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + let lookup = store_bldr.add_lookup(X509Lookup::file()).unwrap(); + lookup + .load_cert_file("test/root-ca.pem", SslFiletype::PEM) + .unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(ossl110)] +fn test_verify_param_auth_level() { + let mut param = X509VerifyParam::new().unwrap(); + let auth_lvl = 2; + let auth_lvl_default = -1; + + assert_eq!(param.auth_level(), auth_lvl_default); + + param.set_auth_level(auth_lvl); + assert_eq!(param.auth_level(), auth_lvl); +} + +#[test] +#[cfg(ossl102)] +fn test_set_purpose() { + let cert = include_bytes!("../../test/leaf.pem"); + let cert = X509::from_pem(cert).unwrap(); + let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem"); + let intermediate_ca = X509::from_pem(intermediate_ca).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let mut chain = Stack::new().unwrap(); + chain.push(intermediate_ca).unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params.set_purpose(X509PurposeId::ANY).unwrap(); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + let mut context = X509StoreContext::new().unwrap(); + + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(ossl102)] +fn test_set_purpose_fails_verification() { + let cert = include_bytes!("../../test/leaf.pem"); + let cert = X509::from_pem(cert).unwrap(); + let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem"); + let intermediate_ca = X509::from_pem(intermediate_ca).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let mut chain = Stack::new().unwrap(); + chain.push(intermediate_ca).unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params + .set_purpose(X509PurposeId::TIMESTAMP_SIGN) + .unwrap(); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + let expected_error = ffi::X509_V_ERR_INVALID_PURPOSE; + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .as_raw(), + expected_error + ) +} + +#[test] +#[cfg(any(ossl101, libressl350))] +fn test_add_name_entry() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let inp_name = cert.subject_name().entries().next().unwrap(); + + let mut names = X509Name::builder().unwrap(); + names.append_entry(inp_name).unwrap(); + let names = names.build(); + + let mut entries = names.entries(); + let outp_name = entries.next().unwrap(); + assert_eq!(outp_name.object().nid(), inp_name.object().nid()); + assert_eq!(outp_name.data().as_slice(), inp_name.data().as_slice()); + assert!(entries.next().is_none()); +} + +#[test] +#[cfg(not(boringssl))] +fn test_load_crl_file_fail() { + let mut store_bldr = X509StoreBuilder::new().unwrap(); + let lookup = store_bldr.add_lookup(X509Lookup::file()).unwrap(); + let res = lookup.load_crl_file("test/root-ca.pem", SslFiletype::PEM); + assert!(res.is_err()); +} + +#[cfg(ossl110)] +fn ipaddress_as_subject_alternative_name_is_formatted_in_debug<T>(expected_ip: T) +where + T: Into<std::net::IpAddr>, +{ + let expected_ip = format!("{:?}", expected_ip.into()); + let mut builder = X509Builder::new().unwrap(); + let san = SubjectAlternativeName::new() + .ip(&expected_ip) + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(san).unwrap(); + let cert = builder.build(); + let actual_ip = cert + .subject_alt_names() + .into_iter() + .flatten() + .map(|n| format!("{:?}", *n)) + .next() + .unwrap(); + assert_eq!(actual_ip, expected_ip); +} + +#[cfg(ossl110)] +#[test] +fn ipv4_as_subject_alternative_name_is_formatted_in_debug() { + ipaddress_as_subject_alternative_name_is_formatted_in_debug([8u8, 8, 8, 128]); +} + +#[cfg(ossl110)] +#[test] +fn ipv6_as_subject_alternative_name_is_formatted_in_debug() { + ipaddress_as_subject_alternative_name_is_formatted_in_debug([ + 8u8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 128, + ]); +} + +#[cfg(ossl110)] +#[test] +fn other_name_as_subject_alternative_name() { + let oid = Asn1Object::from_str("1.3.6.1.5.5.7.8.11").unwrap(); + // this is the hex representation of "test" encoded as a ia5string + let content = [0x16, 0x04, 0x74, 0x65, 0x73, 0x74]; + + let mut builder = X509Builder::new().unwrap(); + let san = SubjectAlternativeName::new() + .other_name2(oid, &content) + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(san).unwrap(); + let cert = builder.build(); + let general_name = cert + .subject_alt_names() + .into_iter() + .flatten() + .next() + .unwrap(); + unsafe { + assert_eq!((*general_name.as_ptr()).type_, 0); + } +} + +#[test] +fn test_dist_point() { + let cert = include_bytes!("../../test/certv3.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let dps = cert.crl_distribution_points().unwrap(); + let dp = dps.get(0).unwrap(); + let dp_nm = dp.distpoint().unwrap(); + let dp_gns = dp_nm.fullname().unwrap(); + let dp_gn = dp_gns.get(0).unwrap(); + assert_eq!(dp_gn.uri().unwrap(), "http://example.com/crl.pem"); + + let dp = dps.get(1).unwrap(); + let dp_nm = dp.distpoint().unwrap(); + let dp_gns = dp_nm.fullname().unwrap(); + let dp_gn = dp_gns.get(0).unwrap(); + assert_eq!(dp_gn.uri().unwrap(), "http://example.com/crl2.pem"); + assert!(dps.get(2).is_none()) +} + +#[test] +fn test_dist_point_null() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert!(cert.crl_distribution_points().is_none()); +} |