/* * CVC EAC1.1 tests * * (C) 2008 Falko Strenzke (strenzke@flexsecure.de) * 2008 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include "tests.h" #if defined(BOTAN_HAS_CARD_VERIFIABLE_CERTIFICATES) #if defined(BOTAN_HAS_ECDSA) && defined(BOTAN_HAS_RSA) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CVC_TEST_DATA_DIR TEST_DATA_DIR "/ecc" using namespace Botan; #define CHECK_MESSAGE(expr, print) try { if(!(expr)) std::cout << print << std::endl; } catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << std::endl; } #define CHECK(expr) try { if(!(expr)) std::cout << #expr << std::endl; } catch(std::exception& e) { std::cout << __FUNCTION__ << ": " << e.what() << std::endl; } namespace { // helper functions void helper_write_file(EAC_Signed_Object const& to_write, std::string const& file_path) { std::vector sv = to_write.BER_encode(); std::ofstream cert_file(file_path, std::ios::binary); cert_file.write((char*)sv.data(), sv.size()); cert_file.close(); } bool helper_files_equal(std::string const& file_path1, std::string const& file_path2) { std::ifstream cert_1_in(file_path1); std::ifstream cert_2_in(file_path2); std::vector sv1; std::vector sv2; if (!cert_1_in || !cert_2_in) { return false; } while (!cert_1_in.eof()) { char now; cert_1_in.read(&now, 1); sv1.push_back(now); } while (!cert_2_in.eof()) { char now; cert_2_in.read(&now, 1); sv2.push_back(now); } if (sv1.size() == 0) { return false; } return sv1 == sv2; } void test_enc_gen_selfsigned(RandomNumberGenerator& rng) { EAC1_1_CVC_Options opts; //opts.cpi = 0; opts.chr = ASN1_Chr("my_opt_chr"); // not used opts.car = ASN1_Car("my_opt_car"); opts.cex = ASN1_Cex("2010 08 13"); opts.ced = ASN1_Ced("2010 07 27"); opts.holder_auth_templ = 0xC1; opts.hash_alg = "SHA-256"; // creating a non sense selfsigned cert w/o dom pars EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.11")); ECDSA_PrivateKey key(rng, dom_pars); key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); EAC1_1_CVC cert = CVC_EAC::create_self_signed_cert(key, opts, rng); std::vector der(cert.BER_encode()); std::ofstream cert_file; cert_file.open(CVC_TEST_DATA_DIR "/my_cv_cert.ber", std::ios::binary); //cert_file << der; // this is bad !!! cert_file.write((char*)der.data(), der.size()); cert_file.close(); EAC1_1_CVC cert_in(CVC_TEST_DATA_DIR "/my_cv_cert.ber"); CHECK(cert == cert_in); // encoding it again while it has no dp std::vector der2(cert_in.BER_encode()); std::ofstream cert_file2(CVC_TEST_DATA_DIR "/my_cv_cert2.ber", std::ios::binary); cert_file2.write((char*)der2.data(), der2.size()); cert_file2.close(); // read both and compare them std::ifstream cert_1_in(CVC_TEST_DATA_DIR "/my_cv_cert.ber"); std::ifstream cert_2_in(CVC_TEST_DATA_DIR "/my_cv_cert2.ber"); std::vector sv1; std::vector sv2; if (!cert_1_in || !cert_2_in) { CHECK_MESSAGE(false, "could not read certificate files"); } while (!cert_1_in.eof()) { char now; cert_1_in.read(&now, 1); sv1.push_back(now); } while (!cert_2_in.eof()) { char now; cert_2_in.read(&now, 1); sv2.push_back(now); } CHECK(sv1.size() > 10); CHECK_MESSAGE(sv1 == sv2, "reencoded file of cert without domain parameters is different from original"); //cout << "reading cert again" << std::endl; CHECK(cert_in.get_car().value() == "my_opt_car"); CHECK(cert_in.get_chr().value() == "my_opt_car"); CHECK(cert_in.get_ced().as_string() == "20100727"); CHECK(cert_in.get_ced().readable_string() == "2010/07/27 "); bool ill_date_exc = false; try { ASN1_Ced("1999 01 01"); } catch (...) { ill_date_exc = true; } CHECK(ill_date_exc); bool ill_date_exc2 = false; try { ASN1_Ced("2100 01 01"); } catch (...) { ill_date_exc2 = true; } CHECK(ill_date_exc2); //cout << "readable = '" << cert_in.get_ced().readable_string() << "'" << std::endl; std::unique_ptr p_pk(cert_in.subject_public_key()); ECDSA_PublicKey* p_ecdsa_pk = dynamic_cast(p_pk.get()); // let´s see if encoding is truely implicitca, because this is what the key should have // been set to when decoding (see above)(because it has no domain params): CHECK(p_ecdsa_pk->domain_format() == EC_DOMPAR_ENC_IMPLICITCA); bool exc = false; try { std::cout << "order = " << p_ecdsa_pk->domain().get_order() << std::endl; } catch (Invalid_State) { exc = true; } CHECK(exc); // set them and try again //cert_in.set_domain_parameters(dom_pars); std::unique_ptr p_pk2(cert_in.subject_public_key()); ECDSA_PublicKey* p_ecdsa_pk2 = dynamic_cast(p_pk2.get()); //p_ecdsa_pk2->set_domain_parameters(dom_pars); CHECK(p_ecdsa_pk2->domain().get_order() == dom_pars.get_order()); bool ver_ec = cert_in.check_signature(*p_pk2); CHECK_MESSAGE(ver_ec, "could not positively verify correct selfsigned cvc certificate"); } void test_enc_gen_req(RandomNumberGenerator& rng) { EAC1_1_CVC_Options opts; //opts.cpi = 0; opts.chr = ASN1_Chr("my_opt_chr"); opts.hash_alg = "SHA-160"; // creating a non sense selfsigned cert w/o dom pars EC_Group dom_pars(OID("1.3.132.0.8")); ECDSA_PrivateKey key(rng, dom_pars); key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); EAC1_1_Req req = CVC_EAC::create_cvc_req(key, opts.chr, opts.hash_alg, rng); std::vector der(req.BER_encode()); std::ofstream req_file(CVC_TEST_DATA_DIR "/my_cv_req.ber", std::ios::binary); req_file.write((char*)der.data(), der.size()); req_file.close(); // read and check signature... EAC1_1_Req req_in(CVC_TEST_DATA_DIR "/my_cv_req.ber"); //req_in.set_domain_parameters(dom_pars); std::unique_ptr p_pk(req_in.subject_public_key()); ECDSA_PublicKey* p_ecdsa_pk = dynamic_cast(p_pk.get()); //p_ecdsa_pk->set_domain_parameters(dom_pars); CHECK(p_ecdsa_pk->domain().get_order() == dom_pars.get_order()); bool ver_ec = req_in.check_signature(*p_pk); CHECK_MESSAGE(ver_ec, "could not positively verify correct selfsigned (created by myself) cvc request"); } void test_cvc_req_ext(RandomNumberGenerator&) { EAC1_1_Req req_in(CVC_TEST_DATA_DIR "/DE1_flen_chars_cvcRequest_ECDSA.der"); EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" //req_in.set_domain_parameters(dom_pars); std::unique_ptr p_pk(req_in.subject_public_key()); ECDSA_PublicKey* p_ecdsa_pk = dynamic_cast(p_pk.get()); //p_ecdsa_pk->set_domain_parameters(dom_pars); CHECK(p_ecdsa_pk->domain().get_order() == dom_pars.get_order()); bool ver_ec = req_in.check_signature(*p_pk); CHECK_MESSAGE(ver_ec, "could not positively verify correct selfsigned (external testdata) cvc request"); } void test_cvc_ado_ext(RandomNumberGenerator&) { EAC1_1_ADO req_in(CVC_TEST_DATA_DIR "/ado.cvcreq"); EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" //cout << "car = " << req_in.get_car().value() << std::endl; //req_in.set_domain_parameters(dom_pars); } void test_cvc_ado_creation(RandomNumberGenerator& rng) { EAC1_1_CVC_Options opts; //opts.cpi = 0; opts.chr = ASN1_Chr("my_opt_chr"); opts.hash_alg = "SHA-256"; // creating a non sense selfsigned cert w/o dom pars EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.11")); //cout << "mod = " << hex << dom_pars.get_curve().get_p() << std::endl; ECDSA_PrivateKey req_key(rng, dom_pars); req_key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); //EAC1_1_Req req = CVC_EAC::create_cvc_req(req_key, opts); EAC1_1_Req req = CVC_EAC::create_cvc_req(req_key, opts.chr, opts.hash_alg, rng); std::vector der(req.BER_encode()); std::ofstream req_file(CVC_TEST_DATA_DIR "/my_cv_req.ber", std::ios::binary); req_file.write((char*)der.data(), der.size()); req_file.close(); // create an ado with that req ECDSA_PrivateKey ado_key(rng, dom_pars); EAC1_1_CVC_Options ado_opts; ado_opts.car = ASN1_Car("my_ado_car"); ado_opts.hash_alg = "SHA-256"; // must be equal to req´s hash alg, because ado takes his sig_algo from it´s request //EAC1_1_ADO ado = CVC_EAC::create_ado_req(ado_key, req, ado_opts); EAC1_1_ADO ado = CVC_EAC::create_ado_req(ado_key, req, ado_opts.car, rng); CHECK_MESSAGE(ado.check_signature(ado_key), "failure of ado verification after creation"); std::ofstream ado_file(CVC_TEST_DATA_DIR "/ado", std::ios::binary); std::vector ado_der(ado.BER_encode()); ado_file.write((char*)ado_der.data(), ado_der.size()); ado_file.close(); // read it again and check the signature EAC1_1_ADO ado2(CVC_TEST_DATA_DIR "/ado"); CHECK(ado == ado2); //ECDSA_PublicKey* p_ado_pk = dynamic_cast(&ado_key); //bool ver = ado2.check_signature(*p_ado_pk); bool ver = ado2.check_signature(ado_key); CHECK_MESSAGE(ver, "failure of ado verification after reloading"); } void test_cvc_ado_comparison(RandomNumberGenerator& rng) { EAC1_1_CVC_Options opts; //opts.cpi = 0; opts.chr = ASN1_Chr("my_opt_chr"); opts.hash_alg = "SHA-224"; // creating a non sense selfsigned cert w/o dom pars EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.11")); ECDSA_PrivateKey req_key(rng, dom_pars); req_key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); //EAC1_1_Req req = CVC_EAC::create_cvc_req(req_key, opts); EAC1_1_Req req = CVC_EAC::create_cvc_req(req_key, opts.chr, opts.hash_alg, rng); // create an ado with that req ECDSA_PrivateKey ado_key(rng, dom_pars); EAC1_1_CVC_Options ado_opts; ado_opts.car = ASN1_Car("my_ado_car1"); ado_opts.hash_alg = "SHA-224"; // must be equal to req's hash alg, because ado takes his sig_algo from it's request //EAC1_1_ADO ado = CVC_EAC::create_ado_req(ado_key, req, ado_opts); EAC1_1_ADO ado = CVC_EAC::create_ado_req(ado_key, req, ado_opts.car, rng); CHECK_MESSAGE(ado.check_signature(ado_key), "failure of ado verification after creation"); // make a second one for comparison EAC1_1_CVC_Options opts2; //opts2.cpi = 0; opts2.chr = ASN1_Chr("my_opt_chr"); opts2.hash_alg = "SHA-160"; // this is the only difference ECDSA_PrivateKey req_key2(rng, dom_pars); req_key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); //EAC1_1_Req req2 = CVC_EAC::create_cvc_req(req_key2, opts2, rng); EAC1_1_Req req2 = CVC_EAC::create_cvc_req(req_key2, opts2.chr, opts2.hash_alg, rng); ECDSA_PrivateKey ado_key2(rng, dom_pars); EAC1_1_CVC_Options ado_opts2; ado_opts2.car = ASN1_Car("my_ado_car1"); ado_opts2.hash_alg = "SHA-160"; // must be equal to req's hash alg, because ado takes his sig_algo from it's request EAC1_1_ADO ado2 = CVC_EAC::create_ado_req(ado_key2, req2, ado_opts2.car, rng); CHECK_MESSAGE(ado2.check_signature(ado_key2), "failure of ado verification after creation"); CHECK_MESSAGE(ado != ado2, "ado's found to be equal where they are not"); // std::ofstream ado_file(CVC_TEST_DATA_DIR "/ado"); // std::vector ado_der(ado.BER_encode()); // ado_file.write((char*)ado_der.data(), ado_der.size()); // ado_file.close(); // read it again and check the signature // EAC1_1_ADO ado2(CVC_TEST_DATA_DIR "/ado"); // ECDSA_PublicKey* p_ado_pk = dynamic_cast(&ado_key); // //bool ver = ado2.check_signature(*p_ado_pk); // bool ver = ado2.check_signature(ado_key); // CHECK_MESSAGE(ver, "failure of ado verification after reloading"); } void test_eac_time(RandomNumberGenerator&) { EAC_Time time(std::chrono::system_clock::now()); // std::cout << "time as std::string = " << time.as_string() << std::endl; EAC_Time sooner("", ASN1_Tag(99)); //X509_Time sooner("", ASN1_Tag(99)); sooner.set_to("2007 12 12"); // std::cout << "sooner as std::string = " << sooner.as_string() << std::endl; EAC_Time later("2007 12 13"); //X509_Time later("2007 12 13"); // std::cout << "later as std::string = " << later.as_string() << std::endl; CHECK(sooner <= later); CHECK(sooner == sooner); ASN1_Cex my_cex("2007 08 01"); my_cex.add_months(12); CHECK(my_cex.get_year() == 2008); CHECK_MESSAGE(my_cex.get_month() == 8, "shoult be 8, was " << my_cex.get_month()); my_cex.add_months(4); CHECK(my_cex.get_year() == 2008); CHECK(my_cex.get_month() == 12); my_cex.add_months(4); CHECK(my_cex.get_year() == 2009); CHECK(my_cex.get_month() == 4); my_cex.add_months(41); CHECK(my_cex.get_year() == 2012); CHECK(my_cex.get_month() == 9); } void test_ver_cvca(RandomNumberGenerator&) { EAC1_1_CVC req_in(CVC_TEST_DATA_DIR "/cvca01.cv.crt"); bool exc = false; std::unique_ptr p_pk2(req_in.subject_public_key()); ECDSA_PublicKey* p_ecdsa_pk2 = dynamic_cast(p_pk2.get()); bool ver_ec = req_in.check_signature(*p_pk2); CHECK_MESSAGE(ver_ec, "could not positively verify correct selfsigned cvca certificate"); try { p_ecdsa_pk2->domain().get_order(); } catch (Invalid_State) { exc = true; } CHECK(!exc); } void test_copy_and_assignment(RandomNumberGenerator&) { EAC1_1_CVC cert_in(CVC_TEST_DATA_DIR "/cvca01.cv.crt"); EAC1_1_CVC cert_cp(cert_in); EAC1_1_CVC cert_ass = cert_in; CHECK(cert_in == cert_cp); CHECK(cert_in == cert_ass); EAC1_1_ADO ado_in(CVC_TEST_DATA_DIR "/ado.cvcreq"); //EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" EAC1_1_ADO ado_cp(ado_in); EAC1_1_ADO ado_ass = ado_in; CHECK(ado_in == ado_cp); CHECK(ado_in == ado_ass); EAC1_1_Req req_in(CVC_TEST_DATA_DIR "/DE1_flen_chars_cvcRequest_ECDSA.der"); //EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" EAC1_1_Req req_cp(req_in); EAC1_1_Req req_ass = req_in; CHECK(req_in == req_cp); CHECK(req_in == req_ass); } void test_eac_str_illegal_values(RandomNumberGenerator&) { bool exc = false; try { EAC1_1_CVC(CVC_TEST_DATA_DIR "/cvca_illegal_chars.cv.crt"); } catch (Decoding_Error) { exc = true; } CHECK(exc); bool exc2 = false; try { EAC1_1_CVC(CVC_TEST_DATA_DIR "/cvca_illegal_chars2.cv.crt"); } catch (Decoding_Error) { exc2 = true; } CHECK(exc2); } void test_tmp_eac_str_enc(RandomNumberGenerator&) { bool exc = false; try { ASN1_Car("abc!+-µ\n"); } catch (Invalid_Argument) { exc = true; } CHECK(exc); // std::string val = car.iso_8859(); // std::cout << "car 8859 = " << val << std::endl; // std::cout << hex <<(unsigned char)val[1] << std::endl; } void test_cvc_chain(RandomNumberGenerator& rng) { EC_Group dom_pars(OID("1.3.36.3.3.2.8.1.1.5")); // "german curve" ECDSA_PrivateKey cvca_privk(rng, dom_pars); std::string hash("SHA-224"); ASN1_Car car("DECVCA00001"); EAC1_1_CVC cvca_cert = DE_EAC::create_cvca(cvca_privk, hash, car, true, true, 12, rng); std::ofstream cvca_file(CVC_TEST_DATA_DIR "/cvc_chain_cvca.cer", std::ios::binary); std::vector cvca_sv = cvca_cert.BER_encode(); cvca_file.write((char*)cvca_sv.data(), cvca_sv.size()); cvca_file.close(); ECDSA_PrivateKey cvca_privk2(rng, dom_pars); ASN1_Car car2("DECVCA00002"); EAC1_1_CVC cvca_cert2 = DE_EAC::create_cvca(cvca_privk2, hash, car2, true, true, 12, rng); EAC1_1_CVC link12 = DE_EAC::link_cvca(cvca_cert, cvca_privk, cvca_cert2, rng); std::vector link12_sv = link12.BER_encode(); std::ofstream link12_file(CVC_TEST_DATA_DIR "/cvc_chain_link12.cer", std::ios::binary); link12_file.write((char*)link12_sv.data(), link12_sv.size()); link12_file.close(); // verify the link CHECK(link12.check_signature(cvca_privk)); EAC1_1_CVC link12_reloaded(CVC_TEST_DATA_DIR "/cvc_chain_link12.cer"); EAC1_1_CVC cvca1_reloaded(CVC_TEST_DATA_DIR "/cvc_chain_cvca.cer"); std::unique_ptr cvca1_rel_pk(cvca1_reloaded.subject_public_key()); CHECK(link12_reloaded.check_signature(*cvca1_rel_pk)); // create first round dvca-req ECDSA_PrivateKey dvca_priv_key(rng, dom_pars); EAC1_1_Req dvca_req = DE_EAC::create_cvc_req(dvca_priv_key, ASN1_Chr("DEDVCAEPASS"), hash, rng); std::ofstream dvca_file(CVC_TEST_DATA_DIR "/cvc_chain_dvca_req.cer", std::ios::binary); std::vector dvca_sv = dvca_req.BER_encode(); dvca_file.write((char*)dvca_sv.data(), dvca_sv.size()); dvca_file.close(); // sign the dvca_request EAC1_1_CVC dvca_cert1 = DE_EAC::sign_request(cvca_cert, cvca_privk, dvca_req, 1, 5, true, 3, 1, rng); CHECK(dvca_cert1.get_car().iso_8859() == "DECVCA00001"); CHECK(dvca_cert1.get_chr().iso_8859() == "DEDVCAEPASS00001"); helper_write_file(dvca_cert1, CVC_TEST_DATA_DIR "/cvc_chain_dvca_cert1.cer"); // make a second round dvca ado request ECDSA_PrivateKey dvca_priv_key2(rng, dom_pars); EAC1_1_Req dvca_req2 = DE_EAC::create_cvc_req(dvca_priv_key2, ASN1_Chr("DEDVCAEPASS"), hash, rng); std::ofstream dvca_file2(CVC_TEST_DATA_DIR "/cvc_chain_dvca_req2.cer", std::ios::binary); std::vector dvca_sv2 = dvca_req2.BER_encode(); dvca_file2.write((char*)dvca_sv2.data(), dvca_sv2.size()); dvca_file2.close(); EAC1_1_ADO dvca_ado2 = CVC_EAC::create_ado_req(dvca_priv_key, dvca_req2, ASN1_Car(dvca_cert1.get_chr().iso_8859()), rng); helper_write_file(dvca_ado2, CVC_TEST_DATA_DIR "/cvc_chain_dvca_ado2.cer"); // verify the ado and sign the request too std::unique_ptr ap_pk(dvca_cert1.subject_public_key()); ECDSA_PublicKey* cert_pk = dynamic_cast(ap_pk.get()); //cert_pk->set_domain_parameters(dom_pars); //std::cout << "dvca_cert.public_point.size() = " << ec::EC2OSP(cert_pk->get_public_point(), ec::PointGFp::COMPRESSED).size() << std::endl; EAC1_1_CVC dvca_cert1_reread(CVC_TEST_DATA_DIR "/cvc_chain_cvca.cer"); CHECK(dvca_ado2.check_signature(*cert_pk)); CHECK(dvca_ado2.check_signature(dvca_priv_key)); // must also work EAC1_1_Req dvca_req2b = dvca_ado2.get_request(); helper_write_file(dvca_req2b, CVC_TEST_DATA_DIR "/cvc_chain_dvca_req2b.cer"); CHECK(helper_files_equal(CVC_TEST_DATA_DIR "/cvc_chain_dvca_req2b.cer", CVC_TEST_DATA_DIR "/cvc_chain_dvca_req2.cer")); EAC1_1_CVC dvca_cert2 = DE_EAC::sign_request(cvca_cert, cvca_privk, dvca_req2b, 2, 5, true, 3, 1, rng); CHECK(dvca_cert2.get_car().iso_8859() == "DECVCA00001"); CHECK_MESSAGE(dvca_cert2.get_chr().iso_8859() == "DEDVCAEPASS00002", "chr = " << dvca_cert2.get_chr().iso_8859()); // make a first round IS request ECDSA_PrivateKey is_priv_key(rng, dom_pars); EAC1_1_Req is_req = DE_EAC::create_cvc_req(is_priv_key, ASN1_Chr("DEIS"), hash, rng); helper_write_file(is_req, CVC_TEST_DATA_DIR "/cvc_chain_is_req.cer"); // sign the IS request //dvca_cert1.set_domain_parameters(dom_pars); EAC1_1_CVC is_cert1 = DE_EAC::sign_request(dvca_cert1, dvca_priv_key, is_req, 1, 5, true, 3, 1, rng); CHECK_MESSAGE(is_cert1.get_car().iso_8859() == "DEDVCAEPASS00001", "car = " << is_cert1.get_car().iso_8859()); CHECK(is_cert1.get_chr().iso_8859() == "DEIS00001"); helper_write_file(is_cert1, CVC_TEST_DATA_DIR "/cvc_chain_is_cert.cer"); // verify the signature of the certificate CHECK(is_cert1.check_signature(dvca_priv_key)); } } size_t test_cvc() { auto& rng = test_rng(); test_enc_gen_selfsigned(rng); test_enc_gen_req(rng); test_cvc_req_ext(rng); test_cvc_ado_ext(rng); test_cvc_ado_creation(rng); test_cvc_ado_comparison(rng); test_eac_time(rng); test_ver_cvca(rng); test_copy_and_assignment(rng); test_eac_str_illegal_values(rng); test_tmp_eac_str_enc(rng); test_cvc_chain(rng); return 0; } #else UNTESTED_WARNING(cvc); #endif // BOTAN_HAS_ECDSA && BOTAN_HAS_RSA #else SKIP_TEST(cvc); #endif // BOTAN_HAS_CARD_VERIFIABLE_CERTIFICATES