diff options
Diffstat (limited to 'src/lib/cert/cvc')
-rw-r--r-- | src/lib/cert/cvc/asn1_eac_str.cpp | 127 | ||||
-rw-r--r-- | src/lib/cert/cvc/asn1_eac_tm.cpp | 293 | ||||
-rw-r--r-- | src/lib/cert/cvc/cvc_ado.cpp | 127 | ||||
-rw-r--r-- | src/lib/cert/cvc/cvc_ado.h | 98 | ||||
-rw-r--r-- | src/lib/cert/cvc/cvc_cert.cpp | 135 | ||||
-rw-r--r-- | src/lib/cert/cvc/cvc_cert.h | 116 | ||||
-rw-r--r-- | src/lib/cert/cvc/cvc_gen_cert.h | 180 | ||||
-rw-r--r-- | src/lib/cert/cvc/cvc_req.cpp | 53 | ||||
-rw-r--r-- | src/lib/cert/cvc/cvc_req.h | 59 | ||||
-rw-r--r-- | src/lib/cert/cvc/cvc_self.cpp | 340 | ||||
-rw-r--r-- | src/lib/cert/cvc/cvc_self.h | 170 | ||||
-rw-r--r-- | src/lib/cert/cvc/eac_asn_obj.h | 238 | ||||
-rw-r--r-- | src/lib/cert/cvc/eac_obj.h | 55 | ||||
-rw-r--r-- | src/lib/cert/cvc/ecdsa_sig.cpp | 59 | ||||
-rw-r--r-- | src/lib/cert/cvc/ecdsa_sig.h | 61 | ||||
-rw-r--r-- | src/lib/cert/cvc/info.txt | 36 | ||||
-rw-r--r-- | src/lib/cert/cvc/signed_obj.cpp | 96 | ||||
-rw-r--r-- | src/lib/cert/cvc/signed_obj.h | 96 |
18 files changed, 2339 insertions, 0 deletions
diff --git a/src/lib/cert/cvc/asn1_eac_str.cpp b/src/lib/cert/cvc/asn1_eac_str.cpp new file mode 100644 index 000000000..2084a9c03 --- /dev/null +++ b/src/lib/cert/cvc/asn1_eac_str.cpp @@ -0,0 +1,127 @@ +/* +* Simple ASN.1 String Types +* (C) 2007 FlexSecure GmbH +* 2008-2011 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/eac_asn_obj.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/charset.h> +#include <botan/parsing.h> +#include <sstream> +#include <ios> + +namespace Botan { + +/* +* Create an ASN1_EAC_String +*/ +ASN1_EAC_String::ASN1_EAC_String(const std::string& str, ASN1_Tag t) : tag(t) + { + iso_8859_str = Charset::transcode(str, LOCAL_CHARSET, LATIN1_CHARSET); + + if(!sanity_check()) + throw Invalid_Argument("ASN1_EAC_String contains illegal characters"); + } + +/* +* Return this string in ISO 8859-1 encoding +*/ +std::string ASN1_EAC_String::iso_8859() const + { + return iso_8859_str; + } + +/* +* Return this string in local encoding +*/ +std::string ASN1_EAC_String::value() const + { + return Charset::transcode(iso_8859_str, LATIN1_CHARSET, LOCAL_CHARSET); + } + +/* +* Return the type of this string object +*/ +ASN1_Tag ASN1_EAC_String::tagging() const + { + return tag; + } + +/* +* DER encode an ASN1_EAC_String +*/ +void ASN1_EAC_String::encode_into(DER_Encoder& encoder) const + { + std::string value = iso_8859(); + encoder.add_object(tagging(), APPLICATION, value); + } + +/* +* Decode a BER encoded ASN1_EAC_String +*/ +void ASN1_EAC_String::decode_from(BER_Decoder& source) + { + BER_Object obj = source.get_next_object(); + + if(obj.type_tag != this->tag) + { + std::stringstream ss; + + ss << "ASN1_EAC_String tag mismatch, tag was " + << std::hex << obj.type_tag + << " expected " + << std::hex << this->tag; + + throw Decoding_Error(ss.str()); + } + + Character_Set charset_is; + charset_is = LATIN1_CHARSET; + + try + { + *this = ASN1_EAC_String( + Charset::transcode(ASN1::to_string(obj), charset_is, LOCAL_CHARSET), + obj.type_tag); + } + catch(Invalid_Argument& inv_arg) + { + throw Decoding_Error(std::string("ASN1_EAC_String decoding failed: ") + + inv_arg.what()); + } + } + +// checks for compliance to the alphabet defined in TR-03110 v1.10, 2007-08-20 +// p. 43 +bool ASN1_EAC_String::sanity_check() const + { + const byte* rep = reinterpret_cast<const byte*>(iso_8859_str.data()); + const size_t rep_len = iso_8859_str.size(); + + for(size_t i = 0; i != rep_len; ++i) + { + if((rep[i] < 0x20) || ((rep[i] >= 0x7F) && (rep[i] < 0xA0))) + return false; + } + + return true; + } + +bool operator==(const ASN1_EAC_String& lhs, const ASN1_EAC_String& rhs) + { + return (lhs.iso_8859() == rhs.iso_8859()); + } + +ASN1_Car::ASN1_Car(std::string const& str) + : ASN1_EAC_String(str, ASN1_Tag(2)) + {} + +ASN1_Chr::ASN1_Chr(std::string const& str) + : ASN1_EAC_String(str, ASN1_Tag(32)) + {} + +} diff --git a/src/lib/cert/cvc/asn1_eac_tm.cpp b/src/lib/cert/cvc/asn1_eac_tm.cpp new file mode 100644 index 000000000..e40f555b3 --- /dev/null +++ b/src/lib/cert/cvc/asn1_eac_tm.cpp @@ -0,0 +1,293 @@ +/* +* EAC Time Types +* (C) 2007 FlexSecure GmbH +* 2008-2009 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/eac_asn_obj.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> +#include <botan/charset.h> +#include <botan/parsing.h> +#include <botan/internal/rounding.h> +#include <botan/calendar.h> + +namespace Botan { + +namespace { + +std::vector<byte> enc_two_digit(u32bit in) + { + std::vector<byte> result; + in %= 100; + if(in < 10) + result.push_back(0x00); + else + { + u32bit y_first_pos = round_down<u32bit>(in, 10) / 10; + result.push_back(static_cast<byte>(y_first_pos)); + } + + u32bit y_sec_pos = in % 10; + result.push_back(static_cast<byte>(y_sec_pos)); + return result; + } + +u32bit dec_two_digit(byte b1, byte b2) + { + u32bit upper = b1; + u32bit lower = b2; + + if(upper > 9 || lower > 9) + throw Invalid_Argument("CVC dec_two_digit value too large"); + + return upper*10 + lower; + } + +} + +/* +* Create an EAC_Time +*/ +EAC_Time::EAC_Time(const std::chrono::system_clock::time_point& time, + ASN1_Tag t) : tag(t) + { + calendar_point cal = calendar_value(time); + + year = cal.year; + month = cal.month; + day = cal.day; + } + +/* +* Create an EAC_Time +*/ +EAC_Time::EAC_Time(const std::string& t_spec, ASN1_Tag t) : tag(t) + { + set_to(t_spec); + } + +/* +* Create an EAC_Time +*/ +EAC_Time::EAC_Time(u32bit y, u32bit m, u32bit d, ASN1_Tag t) : + year(y), month(m), day(d), tag(t) + { + } + +/* +* Set the time with a human readable string +*/ +void EAC_Time::set_to(const std::string& time_str) + { + if(time_str == "") + { + year = month = day = 0; + return; + } + + std::vector<std::string> params; + std::string current; + + for(u32bit j = 0; j != time_str.size(); ++j) + { + if(Charset::is_digit(time_str[j])) + current += time_str[j]; + else + { + if(current != "") + params.push_back(current); + current.clear(); + } + } + if(current != "") + params.push_back(current); + + if(params.size() != 3) + throw Invalid_Argument("Invalid time specification " + time_str); + + year = to_u32bit(params[0]); + month = to_u32bit(params[1]); + day = to_u32bit(params[2]); + + if(!passes_sanity_check()) + throw Invalid_Argument("Invalid time specification " + time_str); + } + + +/* +* DER encode a EAC_Time +*/ +void EAC_Time::encode_into(DER_Encoder& der) const + { + der.add_object(tag, APPLICATION, + encoded_eac_time()); + } + +/* +* Return a string representation of the time +*/ +std::string EAC_Time::as_string() const + { + if(time_is_set() == false) + throw Invalid_State("EAC_Time::as_string: No time set"); + + return std::to_string(year * 10000 + month * 100 + day); + } + +/* +* Return if the time has been set somehow +*/ +bool EAC_Time::time_is_set() const + { + return (year != 0); + } + +/* +* Return a human readable string representation +*/ +std::string EAC_Time::readable_string() const + { + if(time_is_set() == false) + throw Invalid_State("EAC_Time::readable_string: No time set"); + + std::string output(11, 0); + + std::sprintf(&output[0], "%04d/%02d/%02d", year, month, day); + + return output; + } + +/* +* Do a general sanity check on the time +*/ +bool EAC_Time::passes_sanity_check() const + { + if(year < 2000 || year > 2099) + return false; + if(month == 0 || month > 12) + return false; + if(day == 0 || day > 31) + return false; + + return true; + } + +/* +* modification functions +*/ +void EAC_Time::add_years(u32bit years) + { + year += years; + } + +void EAC_Time::add_months(u32bit months) + { + year += months/12; + month += months % 12; + if(month > 12) + { + year += 1; + month -= 12; + } + } + +/* +* Compare this time against another +*/ +s32bit EAC_Time::cmp(const EAC_Time& other) const + { + if(time_is_set() == false) + throw Invalid_State("EAC_Time::cmp: No time set"); + + const s32bit EARLIER = -1, LATER = 1, SAME_TIME = 0; + + if(year < other.year) return EARLIER; + if(year > other.year) return LATER; + if(month < other.month) return EARLIER; + if(month > other.month) return LATER; + if(day < other.day) return EARLIER; + if(day > other.day) return LATER; + + return SAME_TIME; + } + +/* +* Compare two EAC_Times for in various ways +*/ +bool operator==(const EAC_Time& t1, const EAC_Time& t2) + { + return (t1.cmp(t2) == 0); + } + +bool operator!=(const EAC_Time& t1, const EAC_Time& t2) + { + return (t1.cmp(t2) != 0); + } + +bool operator<=(const EAC_Time& t1, const EAC_Time& t2) + { + return (t1.cmp(t2) <= 0); + } + +bool operator>=(const EAC_Time& t1, const EAC_Time& t2) + { + return (t1.cmp(t2) >= 0); + } + +bool operator>(const EAC_Time& t1, const EAC_Time& t2) + { + return (t1.cmp(t2) > 0); + } + +bool operator<(const EAC_Time& t1, const EAC_Time& t2) + { + return (t1.cmp(t2) < 0); + } + +/* +* Decode a BER encoded EAC_Time +*/ +void EAC_Time::decode_from(BER_Decoder& source) + { + BER_Object obj = source.get_next_object(); + + if(obj.type_tag != this->tag) + throw BER_Decoding_Error("Tag mismatch when decoding"); + + if(obj.value.size() != 6) + { + throw Decoding_Error("EAC_Time decoding failed"); + } + + try + { + u32bit tmp_year = dec_two_digit(obj.value[0], obj.value[1]); + u32bit tmp_mon = dec_two_digit(obj.value[2], obj.value[3]); + u32bit tmp_day = dec_two_digit(obj.value[4], obj.value[5]); + year = tmp_year + 2000; + month = tmp_mon; + day = tmp_day; + } + catch (Invalid_Argument) + { + throw Decoding_Error("EAC_Time decoding failed"); + } + + } + +/* +* make the value an octet string for encoding +*/ +std::vector<byte> EAC_Time::encoded_eac_time() const + { + std::vector<byte> result; + result += enc_two_digit(year); + result += enc_two_digit(month); + result += enc_two_digit(day); + return result; + } + +} diff --git a/src/lib/cert/cvc/cvc_ado.cpp b/src/lib/cert/cvc/cvc_ado.cpp new file mode 100644 index 000000000..54bc9facd --- /dev/null +++ b/src/lib/cert/cvc/cvc_ado.cpp @@ -0,0 +1,127 @@ +/* +* CVC Certificate Constructor +* (C) 2007 FlexSecure GmbH +* 2008 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cvc_ado.h> +#include <fstream> + +namespace Botan { + +EAC1_1_ADO::EAC1_1_ADO(DataSource& in) + { + init(in); + do_decode(); + } + +EAC1_1_ADO::EAC1_1_ADO(const std::string& in) + { + DataSource_Stream stream(in, true); + init(stream); + do_decode(); + } + +void EAC1_1_ADO::force_decode() + { + std::vector<byte> inner_cert; + BER_Decoder(tbs_bits) + .start_cons(ASN1_Tag(33)) + .raw_bytes(inner_cert) + .end_cons() + .decode(m_car) + .verify_end(); + + std::vector<byte> req_bits = DER_Encoder() + .start_cons(ASN1_Tag(33), APPLICATION) + .raw_bytes(inner_cert) + .end_cons() + .get_contents_unlocked(); + + DataSource_Memory req_source(req_bits); + m_req = EAC1_1_Req(req_source); + sig_algo = m_req.sig_algo; + } + +std::vector<byte> EAC1_1_ADO::make_signed(PK_Signer& signer, + const std::vector<byte>& tbs_bits, + RandomNumberGenerator& rng) + { + const std::vector<byte> concat_sig = signer.sign_message(tbs_bits, rng); + + return DER_Encoder() + .start_cons(ASN1_Tag(7), APPLICATION) + .raw_bytes(tbs_bits) + .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons() + .get_contents_unlocked(); + } + +ASN1_Car EAC1_1_ADO::get_car() const + { + return m_car; + } + +void EAC1_1_ADO::decode_info(DataSource& source, + std::vector<byte> & res_tbs_bits, + ECDSA_Signature & res_sig) + { + std::vector<byte> concat_sig; + std::vector<byte> cert_inner_bits; + ASN1_Car car; + + BER_Decoder(source) + .start_cons(ASN1_Tag(7)) + .start_cons(ASN1_Tag(33)) + .raw_bytes(cert_inner_bits) + .end_cons() + .decode(car) + .decode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons(); + + std::vector<byte> enc_cert = DER_Encoder() + .start_cons(ASN1_Tag(33), APPLICATION) + .raw_bytes(cert_inner_bits) + .end_cons() + .get_contents_unlocked(); + + res_tbs_bits = enc_cert; + res_tbs_bits += DER_Encoder().encode(car).get_contents(); + res_sig = decode_concatenation(concat_sig); + } + +void EAC1_1_ADO::encode(Pipe& out, X509_Encoding encoding) const + { + if(encoding == PEM) + throw Invalid_Argument("EAC1_1_ADO::encode() cannot PEM encode an EAC object"); + + auto concat_sig = EAC1_1_obj<EAC1_1_ADO>::m_sig.get_concatenation(); + + out.write(DER_Encoder() + .start_cons(ASN1_Tag(7), APPLICATION) + .raw_bytes(tbs_bits) + .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons() + .get_contents()); + } + +std::vector<byte> EAC1_1_ADO::tbs_data() const + { + return tbs_bits; + } + +bool EAC1_1_ADO::operator==(EAC1_1_ADO const& rhs) const + { + return (this->get_concat_sig() == rhs.get_concat_sig() + && this->tbs_data() == rhs.tbs_data() + && this->get_car() == rhs.get_car()); + } + +EAC1_1_Req EAC1_1_ADO::get_request() const + { + return m_req; + } + +} diff --git a/src/lib/cert/cvc/cvc_ado.h b/src/lib/cert/cvc/cvc_ado.h new file mode 100644 index 000000000..6f5b1d527 --- /dev/null +++ b/src/lib/cert/cvc/cvc_ado.h @@ -0,0 +1,98 @@ +/* +* EAC1_1 CVC ADO +* (C) 2008 Falko Strenzke +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_EAC_CVC_ADO_H__ +#define BOTAN_EAC_CVC_ADO_H__ + +#include <botan/eac_obj.h> +#include <botan/eac_asn_obj.h> +#include <botan/cvc_req.h> +#include <string> + +namespace Botan { + +/** +* This class represents a TR03110 (EAC) v1.1 CVC ADO request +*/ + + // CRTP continuation from EAC1_1_obj +class BOTAN_DLL EAC1_1_ADO : public EAC1_1_obj<EAC1_1_ADO> + { + public: + friend class EAC1_1_obj<EAC1_1_ADO>; + + /** + * Construct a CVC ADO request from a DER encoded CVC ADO request file. + * @param str the path to the DER encoded file + */ + EAC1_1_ADO(const std::string& str); + + /** + * Construct a CVC ADO request from a data source + * @param source the data source + */ + EAC1_1_ADO(DataSource& source); + + /** + * Create a signed CVC ADO request from to be signed (TBS) data + * @param signer the signer used to sign the CVC ADO request + * @param tbs_bits the TBS data to sign + * @param rng a random number generator + */ + static std::vector<byte> make_signed( + PK_Signer& signer, + const std::vector<byte>& tbs_bits, + RandomNumberGenerator& rng); + + /** + * Get the CAR of this CVC ADO request + * @result the CAR of this CVC ADO request + */ + ASN1_Car get_car() const; + + /** + * Get the CVC request contained in this object. + * @result the CVC request inside this CVC ADO request + */ + EAC1_1_Req get_request() const; + + /** + * Encode this object into a pipe. Only DER is supported. + * @param out the pipe to encode this object into + * @param encoding the encoding type to use, must be DER + */ + void encode(Pipe& out, X509_Encoding encoding) const; + + bool operator==(EAC1_1_ADO const& rhs) const; + + /** + * Get the TBS data of this CVC ADO request. + * @result the TBS data + */ + std::vector<byte> tbs_data() const; + + virtual ~EAC1_1_ADO() {} + private: + ASN1_Car m_car; + EAC1_1_Req m_req; + + void force_decode(); + static void decode_info(DataSource& source, + std::vector<byte> & res_tbs_bits, + ECDSA_Signature & res_sig); + }; + +inline bool operator!=(EAC1_1_ADO const& lhs, EAC1_1_ADO const& rhs) + { + return (!(lhs == rhs)); + } + +} + +#endif + + diff --git a/src/lib/cert/cvc/cvc_cert.cpp b/src/lib/cert/cvc/cvc_cert.cpp new file mode 100644 index 000000000..3ab78b7d4 --- /dev/null +++ b/src/lib/cert/cvc/cvc_cert.cpp @@ -0,0 +1,135 @@ +/* + (C) 2007 FlexSecure GmbH + 2008-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cvc_cert.h> +#include <botan/oids.h> + +namespace Botan { + +ASN1_Car EAC1_1_CVC::get_car() const + { + return m_car; + } + +ASN1_Ced EAC1_1_CVC::get_ced() const + { + return m_ced; + } +ASN1_Cex EAC1_1_CVC::get_cex() const + { + return m_cex; + } +u32bit EAC1_1_CVC::get_chat_value() const + { + return m_chat_val; + } + +/* +* Decode the TBSCertificate data +*/ +void EAC1_1_CVC::force_decode() + { + std::vector<byte> enc_pk; + std::vector<byte> enc_chat_val; + size_t cpi; + BER_Decoder tbs_cert(tbs_bits); + tbs_cert.decode(cpi, ASN1_Tag(41), APPLICATION) + .decode(m_car) + .start_cons(ASN1_Tag(73)) + .raw_bytes(enc_pk) + .end_cons() + .decode(m_chr) + .start_cons(ASN1_Tag(76)) + .decode(m_chat_oid) + .decode(enc_chat_val, OCTET_STRING, ASN1_Tag(19), APPLICATION) + .end_cons() + .decode(m_ced) + .decode(m_cex) + .verify_end(); + + if(enc_chat_val.size() != 1) + throw Decoding_Error("CertificateHolderAuthorizationValue was not of length 1"); + + if(cpi != 0) + throw Decoding_Error("EAC1_1 certificate's cpi was not 0"); + + m_pk = decode_eac1_1_key(enc_pk, sig_algo); + + m_chat_val = enc_chat_val[0]; + + self_signed = (m_car.iso_8859() == m_chr.iso_8859()); + } + +/* +* CVC Certificate Constructor +*/ +EAC1_1_CVC::EAC1_1_CVC(DataSource& in) + { + init(in); + self_signed = false; + do_decode(); + } + +EAC1_1_CVC::EAC1_1_CVC(const std::string& in) + { + DataSource_Stream stream(in, true); + init(stream); + self_signed = false; + do_decode(); + } + +bool EAC1_1_CVC::operator==(EAC1_1_CVC const& rhs) const + { + return (tbs_data() == rhs.tbs_data() + && get_concat_sig() == rhs.get_concat_sig()); + } + +ECDSA_PublicKey* decode_eac1_1_key(const std::vector<byte>&, + AlgorithmIdentifier&) + { + throw Internal_Error("decode_eac1_1_key: Unimplemented"); + return 0; + } + +EAC1_1_CVC make_cvc_cert(PK_Signer& signer, + const std::vector<byte>& public_key, + ASN1_Car const& car, + ASN1_Chr const& chr, + byte holder_auth_templ, + ASN1_Ced ced, + ASN1_Cex cex, + RandomNumberGenerator& rng) + { + OID chat_oid(OIDS::lookup("CertificateHolderAuthorizationTemplate")); + std::vector<byte> enc_chat_val; + enc_chat_val.push_back(holder_auth_templ); + + std::vector<byte> enc_cpi; + enc_cpi.push_back(0x00); + std::vector<byte> tbs = DER_Encoder() + .encode(enc_cpi, OCTET_STRING, ASN1_Tag(41), APPLICATION) // cpi + .encode(car) + .raw_bytes(public_key) + .encode(chr) + .start_cons(ASN1_Tag(76), APPLICATION) + .encode(chat_oid) + .encode(enc_chat_val, OCTET_STRING, ASN1_Tag(19), APPLICATION) + .end_cons() + .encode(ced) + .encode(cex) + .get_contents_unlocked(); + + std::vector<byte> signed_cert = + EAC1_1_CVC::make_signed(signer, + EAC1_1_CVC::build_cert_body(tbs), + rng); + + DataSource_Memory source(signed_cert); + return EAC1_1_CVC(source); + } + +} diff --git a/src/lib/cert/cvc/cvc_cert.h b/src/lib/cert/cvc/cvc_cert.h new file mode 100644 index 000000000..7c084379f --- /dev/null +++ b/src/lib/cert/cvc/cvc_cert.h @@ -0,0 +1,116 @@ +/* +* EAC1_1 CVC +* (C) 2008 Falko Strenzke +* 2008 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_CVC_EAC_H__ +#define BOTAN_CVC_EAC_H__ + +#include <botan/cvc_gen_cert.h> +#include <botan/ecdsa.h> +#include <string> + +namespace Botan { + +/** +* This class represents TR03110 (EAC) v1.1 CV Certificates +*/ +class BOTAN_DLL EAC1_1_CVC : public EAC1_1_gen_CVC<EAC1_1_CVC>//Signed_Object + { + public: + friend class EAC1_1_obj<EAC1_1_CVC>; + + /** + * Get the CAR of the certificate. + * @result the CAR of the certificate + */ + ASN1_Car get_car() const; + + /** + * Get the CED of this certificate. + * @result the CED this certificate + */ + ASN1_Ced get_ced() const; + + /** + * Get the CEX of this certificate. + * @result the CEX this certificate + */ + ASN1_Cex get_cex() const; + + /** + * Get the CHAT value. + * @result the CHAT value + */ + u32bit get_chat_value() const; + + bool operator==(const EAC1_1_CVC&) const; + + /** + * Construct a CVC from a data source + * @param source the data source + */ + EAC1_1_CVC(DataSource& source); + + /** + * Construct a CVC from a file + * @param str the path to the certificate file + */ + EAC1_1_CVC(const std::string& str); + + virtual ~EAC1_1_CVC() {} + private: + void force_decode(); + EAC1_1_CVC() {} + + ASN1_Car m_car; + ASN1_Ced m_ced; + ASN1_Cex m_cex; + byte m_chat_val; + OID m_chat_oid; + }; + +/* +* Comparison +*/ +inline bool operator!=(EAC1_1_CVC const& lhs, EAC1_1_CVC const& rhs) + { + return !(lhs == rhs); + } + +/** +* Create an arbitrary EAC 1.1 CVC. +* The desired key encoding must be set within the key (if applicable). +* @param signer the signer used to sign the certificate +* @param public_key the DER encoded public key to appear in +* the certificate +* @param car the CAR of the certificate +* @param chr the CHR of the certificate +* @param holder_auth_templ the holder authorization value byte to +* appear in the CHAT of the certificate +* @param ced the CED to appear in the certificate +* @param cex the CEX to appear in the certificate +* @param rng a random number generator +*/ +EAC1_1_CVC BOTAN_DLL make_cvc_cert(PK_Signer& signer, + const std::vector<byte>& public_key, + ASN1_Car const& car, + ASN1_Chr const& chr, + byte holder_auth_templ, + ASN1_Ced ced, + ASN1_Cex cex, + RandomNumberGenerator& rng); + +/** +* Decode an EAC encoding ECDSA key +*/ +BOTAN_DLL ECDSA_PublicKey* decode_eac1_1_key(const std::vector<byte>& enc_key, + AlgorithmIdentifier& sig_algo); + +} + +#endif + diff --git a/src/lib/cert/cvc/cvc_gen_cert.h b/src/lib/cert/cvc/cvc_gen_cert.h new file mode 100644 index 000000000..7fa4eba29 --- /dev/null +++ b/src/lib/cert/cvc/cvc_gen_cert.h @@ -0,0 +1,180 @@ +/* +* EAC1_1 general CVC +* (C) 2008 Falko Strenzke +* 2008-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_EAC_CVC_GEN_CERT_H__ +#define BOTAN_EAC_CVC_GEN_CERT_H__ + +#include <botan/eac_obj.h> +#include <botan/eac_asn_obj.h> +#include <botan/ecdsa.h> +#include <botan/pubkey.h> + +namespace Botan { + +/** +* This class represents TR03110 (EAC) v1.1 generalized CV Certificates +*/ +template<typename Derived> +class EAC1_1_gen_CVC : public EAC1_1_obj<Derived> // CRTP continuation from EAC1_1_obj + { + friend class EAC1_1_obj<EAC1_1_gen_CVC>; + + public: + + /** + * Get this certificates public key. + * @result this certificates public key + */ + Public_Key* subject_public_key() const; + + /** + * Find out whether this object is self signed. + * @result true if this object is self signed + */ + bool is_self_signed() const; + + /** + * Get the CHR of the certificate. + * @result the CHR of the certificate + */ + ASN1_Chr get_chr() const; + + /** + * Put the DER encoded version of this object into a pipe. PEM + * is not supported. + * @param out the pipe to push the DER encoded version into + * @param encoding the encoding to use. Must be DER. + */ + void encode(Pipe& out, X509_Encoding encoding) const; + + /** + * Get the to-be-signed (TBS) data of this object. + * @result the TBS data of this object + */ + std::vector<byte> tbs_data() const; + + /** + * Build the DER encoded certifcate body of an object + * @param tbs the data to be signed + * @result the correctly encoded body of the object + */ + static std::vector<byte> build_cert_body(const std::vector<byte>& tbs); + + /** + * Create a signed generalized CVC object. + * @param signer the signer used to sign this object + * @param tbs_bits the body the generalized CVC object to be signed + * @param rng a random number generator + * @result the DER encoded signed generalized CVC object + */ + static std::vector<byte> make_signed( + PK_Signer& signer, + const std::vector<byte>& tbs_bits, + RandomNumberGenerator& rng); + + EAC1_1_gen_CVC() { m_pk = 0; } + + virtual ~EAC1_1_gen_CVC<Derived>() + { delete m_pk; } + + protected: + ECDSA_PublicKey* m_pk; + ASN1_Chr m_chr; + bool self_signed; + + static void decode_info(DataSource& source, + std::vector<byte> & res_tbs_bits, + ECDSA_Signature & res_sig); + + }; + +template<typename Derived> ASN1_Chr EAC1_1_gen_CVC<Derived>::get_chr() const + { + return m_chr; + } + +template<typename Derived> bool EAC1_1_gen_CVC<Derived>::is_self_signed() const + { + return self_signed; + } + +template<typename Derived> +std::vector<byte> EAC1_1_gen_CVC<Derived>::make_signed( + PK_Signer& signer, + const std::vector<byte>& tbs_bits, + RandomNumberGenerator& rng) // static + { + const auto concat_sig = signer.sign_message(tbs_bits, rng); + + return DER_Encoder() + .start_cons(ASN1_Tag(33), APPLICATION) + .raw_bytes(tbs_bits) + .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons() + .get_contents_unlocked(); + } + +template<typename Derived> +Public_Key* EAC1_1_gen_CVC<Derived>::subject_public_key() const + { + return new ECDSA_PublicKey(*m_pk); + } + +template<typename Derived> std::vector<byte> EAC1_1_gen_CVC<Derived>::build_cert_body(const std::vector<byte>& tbs) + { + return DER_Encoder() + .start_cons(ASN1_Tag(78), APPLICATION) + .raw_bytes(tbs) + .end_cons().get_contents_unlocked(); + } + +template<typename Derived> std::vector<byte> EAC1_1_gen_CVC<Derived>::tbs_data() const + { + return build_cert_body(EAC1_1_obj<Derived>::tbs_bits); + } + +template<typename Derived> void EAC1_1_gen_CVC<Derived>::encode(Pipe& out, X509_Encoding encoding) const + { + std::vector<byte> concat_sig(EAC1_1_obj<Derived>::m_sig.get_concatenation()); + std::vector<byte> der = DER_Encoder() + .start_cons(ASN1_Tag(33), APPLICATION) + .start_cons(ASN1_Tag(78), APPLICATION) + .raw_bytes(EAC1_1_obj<Derived>::tbs_bits) + .end_cons() + .encode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons() + .get_contents_unlocked(); + + if (encoding == PEM) + throw Invalid_Argument("EAC1_1_gen_CVC::encode() cannot PEM encode an EAC object"); + else + out.write(der); + } + +template<typename Derived> +void EAC1_1_gen_CVC<Derived>::decode_info( + DataSource& source, + std::vector<byte> & res_tbs_bits, + ECDSA_Signature & res_sig) + { + std::vector<byte> concat_sig; + BER_Decoder(source) + .start_cons(ASN1_Tag(33)) + .start_cons(ASN1_Tag(78)) + .raw_bytes(res_tbs_bits) + .end_cons() + .decode(concat_sig, OCTET_STRING, ASN1_Tag(55), APPLICATION) + .end_cons(); + res_sig = decode_concatenation(concat_sig); + } + +} + +#endif + + diff --git a/src/lib/cert/cvc/cvc_req.cpp b/src/lib/cert/cvc/cvc_req.cpp new file mode 100644 index 000000000..6c013f755 --- /dev/null +++ b/src/lib/cert/cvc/cvc_req.cpp @@ -0,0 +1,53 @@ +/* + (C) 2007 FlexSecure GmbH + 2008-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cvc_req.h> +#include <botan/cvc_cert.h> +#include <botan/ber_dec.h> + +namespace Botan { + +bool EAC1_1_Req::operator==(EAC1_1_Req const& rhs) const + { + return (this->tbs_data() == rhs.tbs_data() && + this->get_concat_sig() == rhs.get_concat_sig()); + } + +void EAC1_1_Req::force_decode() + { + std::vector<byte> enc_pk; + BER_Decoder tbs_cert(tbs_bits); + size_t cpi; + tbs_cert.decode(cpi, ASN1_Tag(41), APPLICATION) + .start_cons(ASN1_Tag(73)) + .raw_bytes(enc_pk) + .end_cons() + .decode(m_chr) + .verify_end(); + + if(cpi != 0) + throw Decoding_Error("EAC1_1 requests cpi was not 0"); + + m_pk = decode_eac1_1_key(enc_pk, sig_algo); + } + +EAC1_1_Req::EAC1_1_Req(DataSource& in) + { + init(in); + self_signed = true; + do_decode(); + } + +EAC1_1_Req::EAC1_1_Req(const std::string& in) + { + DataSource_Stream stream(in, true); + init(stream); + self_signed = true; + do_decode(); + } + +} diff --git a/src/lib/cert/cvc/cvc_req.h b/src/lib/cert/cvc/cvc_req.h new file mode 100644 index 000000000..ac4e22453 --- /dev/null +++ b/src/lib/cert/cvc/cvc_req.h @@ -0,0 +1,59 @@ +/* +* EAC1_1 CVC Request +* (C) 2008 Falko Strenzke +* 2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_EAC_CVC_REQ_H__ +#define BOTAN_EAC_CVC_REQ_H__ + +#include <botan/cvc_gen_cert.h> + +namespace Botan { + +/** +* This class represents TR03110 v1.1 EAC CV Certificate Requests. +*/ +class BOTAN_DLL EAC1_1_Req : public EAC1_1_gen_CVC<EAC1_1_Req> + { + public: + friend class EAC1_1_ADO; + friend class EAC1_1_obj<EAC1_1_Req>; + + /** + * Compare for equality with other + * @param other compare for equality with this object + */ + bool operator==(const EAC1_1_Req& other) const; + + /** + * Construct a CVC request from a data source. + * @param source the data source + */ + EAC1_1_Req(DataSource& source); + + /** + * Construct a CVC request from a DER encoded CVC request file. + * @param str the path to the DER encoded file + */ + EAC1_1_Req(const std::string& str); + + virtual ~EAC1_1_Req(){} + private: + void force_decode(); + EAC1_1_Req() {} + }; + +/* +* Comparison Operator +*/ +inline bool operator!=(EAC1_1_Req const& lhs, EAC1_1_Req const& rhs) + { + return !(lhs == rhs); + } + +} + +#endif diff --git a/src/lib/cert/cvc/cvc_self.cpp b/src/lib/cert/cvc/cvc_self.cpp new file mode 100644 index 000000000..46e4960e1 --- /dev/null +++ b/src/lib/cert/cvc/cvc_self.cpp @@ -0,0 +1,340 @@ +/* + (C) 2007 FlexSecure GmbH + 2008-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/cvc_self.h> +#include <botan/ecc_key.h> +#include <botan/point_gfp.h> +#include <botan/oids.h> +#include <sstream> +#include <memory> + +namespace Botan { + +namespace { + +/* +* cvc CHAT values +*/ +enum CHAT_values{ + CVCA = 0xC0, + DVCA_domestic = 0x80, + DVCA_foreign = 0x40, + IS = 0x00, + + IRIS = 0x02, + FINGERPRINT = 0x01 +}; + +void encode_eac_bigint(DER_Encoder& der, const BigInt& x, ASN1_Tag tag) + { + der.encode(BigInt::encode_1363(x, x.bytes()), OCTET_STRING, tag); + } + +std::vector<byte> eac_1_1_encoding(const EC_PublicKey* key, + const OID& sig_algo) + { + if(key->domain_format() == EC_DOMPAR_ENC_OID) + throw Encoding_Error("CVC encoder: cannot encode parameters by OID"); + + const EC_Group& domain = key->domain(); + + // This is why we can't have nice things + + DER_Encoder enc; + enc.start_cons(ASN1_Tag(73), APPLICATION) + .encode(sig_algo); + + if(key->domain_format() == EC_DOMPAR_ENC_EXPLICIT) + { + encode_eac_bigint(enc, domain.get_curve().get_p(), ASN1_Tag(1)); + encode_eac_bigint(enc, domain.get_curve().get_a(), ASN1_Tag(2)); + encode_eac_bigint(enc, domain.get_curve().get_b(), ASN1_Tag(3)); + + enc.encode(EC2OSP(domain.get_base_point(), PointGFp::UNCOMPRESSED), + OCTET_STRING, ASN1_Tag(4)); + + encode_eac_bigint(enc, domain.get_order(), ASN1_Tag(4)); + } + + enc.encode(EC2OSP(key->public_point(), PointGFp::UNCOMPRESSED), + OCTET_STRING, ASN1_Tag(6)); + + if(key->domain_format() == EC_DOMPAR_ENC_EXPLICIT) + encode_eac_bigint(enc, domain.get_cofactor(), ASN1_Tag(7)); + + enc.end_cons(); + + return enc.get_contents_unlocked(); + } + +std::string padding_and_hash_from_oid(OID const& oid) + { + std::string padding_and_hash = OIDS::lookup(oid); // use the hash + + if(padding_and_hash.substr(0,6) != "ECDSA/") + throw Invalid_State("CVC: Can only use ECDSA, not " + padding_and_hash); + + padding_and_hash.erase(0, padding_and_hash.find("/") + 1); + return padding_and_hash; + } + +} + +namespace CVC_EAC { + +EAC1_1_CVC create_self_signed_cert(Private_Key const& key, + EAC1_1_CVC_Options const& opt, + RandomNumberGenerator& rng) + { + // NOTE: we ignore the value of opt.chr + + const ECDSA_PrivateKey* priv_key = dynamic_cast<const ECDSA_PrivateKey*>(&key); + + if(priv_key == 0) + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + + ASN1_Chr chr(opt.car.value()); + + AlgorithmIdentifier sig_algo; + std::string padding_and_hash("EMSA1_BSI(" + opt.hash_alg + ")"); + sig_algo.oid = OIDS::lookup(priv_key->algo_name() + "/" + padding_and_hash); + sig_algo = AlgorithmIdentifier(sig_algo.oid, AlgorithmIdentifier::USE_NULL_PARAM); + + PK_Signer signer(*priv_key, padding_and_hash); + + std::vector<byte> enc_public_key = eac_1_1_encoding(priv_key, sig_algo.oid); + + return make_cvc_cert(signer, + enc_public_key, + opt.car, chr, + opt.holder_auth_templ, + opt.ced, opt.cex, rng); + } + +EAC1_1_Req create_cvc_req(Private_Key const& key, + ASN1_Chr const& chr, + std::string const& hash_alg, + RandomNumberGenerator& rng) + { + + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + AlgorithmIdentifier sig_algo; + std::string padding_and_hash("EMSA1_BSI(" + hash_alg + ")"); + sig_algo.oid = OIDS::lookup(priv_key->algo_name() + "/" + padding_and_hash); + sig_algo = AlgorithmIdentifier(sig_algo.oid, AlgorithmIdentifier::USE_NULL_PARAM); + + PK_Signer signer(*priv_key, padding_and_hash); + + std::vector<byte> enc_public_key = eac_1_1_encoding(priv_key, sig_algo.oid); + + std::vector<byte> enc_cpi; + enc_cpi.push_back(0x00); + std::vector<byte> tbs = DER_Encoder() + .encode(enc_cpi, OCTET_STRING, ASN1_Tag(41), APPLICATION) + .raw_bytes(enc_public_key) + .encode(chr) + .get_contents_unlocked(); + + std::vector<byte> signed_cert = + EAC1_1_gen_CVC<EAC1_1_Req>::make_signed(signer, + EAC1_1_gen_CVC<EAC1_1_Req>::build_cert_body(tbs), + rng); + + DataSource_Memory source(signed_cert); + return EAC1_1_Req(source); + } + +EAC1_1_ADO create_ado_req(Private_Key const& key, + EAC1_1_Req const& req, + ASN1_Car const& car, + RandomNumberGenerator& rng) + { + + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + + std::string padding_and_hash = padding_and_hash_from_oid(req.signature_algorithm().oid); + PK_Signer signer(*priv_key, padding_and_hash); + std::vector<byte> tbs_bits = req.BER_encode(); + tbs_bits += DER_Encoder().encode(car).get_contents(); + + std::vector<byte> signed_cert = + EAC1_1_ADO::make_signed(signer, tbs_bits, rng); + + DataSource_Memory source(signed_cert); + return EAC1_1_ADO(source); + } + +} // namespace CVC_EAC +namespace DE_EAC +{ + +EAC1_1_CVC create_cvca(Private_Key const& key, + std::string const& hash, + ASN1_Car const& car, bool iris, bool fingerpr, + u32bit cvca_validity_months, + RandomNumberGenerator& rng) + { + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + EAC1_1_CVC_Options opts; + opts.car = car; + + opts.ced = ASN1_Ced(std::chrono::system_clock::now()); + opts.cex = ASN1_Cex(opts.ced); + opts.cex.add_months(cvca_validity_months); + opts.holder_auth_templ = (CVCA | (iris * IRIS) | (fingerpr * FINGERPRINT)); + opts.hash_alg = hash; + return CVC_EAC::create_self_signed_cert(*priv_key, opts, rng); + } + + + +EAC1_1_CVC link_cvca(EAC1_1_CVC const& signer, + Private_Key const& key, + EAC1_1_CVC const& signee, + RandomNumberGenerator& rng) + { + const ECDSA_PrivateKey* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + + if (priv_key == 0) + throw Invalid_Argument("link_cvca(): unsupported key type"); + + ASN1_Ced ced(std::chrono::system_clock::now()); + ASN1_Cex cex(signee.get_cex()); + if (*static_cast<EAC_Time*>(&ced) > *static_cast<EAC_Time*>(&cex)) + { + std::string detail("link_cvca(): validity periods of provided certificates don't overlap: currend time = ced = "); + detail += ced.as_string(); + detail += ", signee.cex = "; + detail += cex.as_string(); + throw Invalid_Argument(detail); + } + if (signer.signature_algorithm() != signee.signature_algorithm()) + { + throw Invalid_Argument("link_cvca(): signature algorithms of signer and signee don't match"); + } + AlgorithmIdentifier sig_algo = signer.signature_algorithm(); + std::string padding_and_hash = padding_and_hash_from_oid(sig_algo.oid); + PK_Signer pk_signer(*priv_key, padding_and_hash); + std::unique_ptr<Public_Key> pk(signee.subject_public_key()); + ECDSA_PublicKey* subj_pk = dynamic_cast<ECDSA_PublicKey*>(pk.get()); + subj_pk->set_parameter_encoding(EC_DOMPAR_ENC_EXPLICIT); + + std::vector<byte> enc_public_key = eac_1_1_encoding(priv_key, sig_algo.oid); + + return make_cvc_cert(pk_signer, enc_public_key, + signer.get_car(), + signee.get_chr(), + signer.get_chat_value(), + ced, cex, + rng); + } + +EAC1_1_CVC sign_request(EAC1_1_CVC const& signer_cert, + Private_Key const& key, + EAC1_1_Req const& signee, + u32bit seqnr, + u32bit seqnr_len, + bool domestic, + u32bit dvca_validity_months, + u32bit ca_is_validity_months, + RandomNumberGenerator& rng) + { + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&key); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + std::string chr_str = signee.get_chr().value(); + + std::string seqnr_string = std::to_string(seqnr); + + while(seqnr_string.size() < seqnr_len) + seqnr_string = '0' + seqnr_string; + + chr_str += seqnr_string; + ASN1_Chr chr(chr_str); + std::string padding_and_hash = padding_and_hash_from_oid(signee.signature_algorithm().oid); + PK_Signer pk_signer(*priv_key, padding_and_hash); + std::unique_ptr<Public_Key> pk(signee.subject_public_key()); + ECDSA_PublicKey* subj_pk = dynamic_cast<ECDSA_PublicKey*>(pk.get()); + std::unique_ptr<Public_Key> signer_pk(signer_cert.subject_public_key()); + + // for the case that the domain parameters are not set... + // (we use those from the signer because they must fit) + //subj_pk->set_domain_parameters(priv_key->domain_parameters()); + + subj_pk->set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); + + AlgorithmIdentifier sig_algo(signer_cert.signature_algorithm()); + + ASN1_Ced ced(std::chrono::system_clock::now()); + + u32bit chat_val; + u32bit chat_low = signer_cert.get_chat_value() & 0x3; // take the chat rights from signer + ASN1_Cex cex(ced); + if ((signer_cert.get_chat_value() & CVCA) == CVCA) + { + // we sign a dvca + cex.add_months(dvca_validity_months); + if (domestic) + chat_val = DVCA_domestic | chat_low; + else + chat_val = DVCA_foreign | chat_low; + } + else if ((signer_cert.get_chat_value() & DVCA_domestic) == DVCA_domestic || + (signer_cert.get_chat_value() & DVCA_foreign) == DVCA_foreign) + { + cex.add_months(ca_is_validity_months); + chat_val = IS | chat_low; + } + else + { + throw Invalid_Argument("sign_request(): encountered illegal value for CHAT"); + // (IS cannot sign certificates) + } + + std::vector<byte> enc_public_key = eac_1_1_encoding(priv_key, sig_algo.oid); + + return make_cvc_cert(pk_signer, enc_public_key, + ASN1_Car(signer_cert.get_chr().iso_8859()), + chr, + chat_val, + ced, + cex, + rng); + } + +EAC1_1_Req create_cvc_req(Private_Key const& prkey, + ASN1_Chr const& chr, + std::string const& hash_alg, + RandomNumberGenerator& rng) + { + ECDSA_PrivateKey const* priv_key = dynamic_cast<ECDSA_PrivateKey const*>(&prkey); + if (priv_key == 0) + { + throw Invalid_Argument("CVC_EAC::create_self_signed_cert(): unsupported key type"); + } + ECDSA_PrivateKey key(*priv_key); + key.set_parameter_encoding(EC_DOMPAR_ENC_IMPLICITCA); + return CVC_EAC::create_cvc_req(key, chr, hash_alg, rng); + } + +} // namespace DE_EAC + +} diff --git a/src/lib/cert/cvc/cvc_self.h b/src/lib/cert/cvc/cvc_self.h new file mode 100644 index 000000000..1e6bbe49a --- /dev/null +++ b/src/lib/cert/cvc/cvc_self.h @@ -0,0 +1,170 @@ +/* +* CVC Self-Signed Certificate +* (C) 2007 FlexSecure GmbH +* 2008 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_CVC_EAC_SELF_H__ +#define BOTAN_CVC_EAC_SELF_H__ + +#include <botan/pkcs8.h> +#include <botan/cvc_cert.h> +#include <botan/ecdsa.h> +#include <botan/asn1_obj.h> +#include <botan/cvc_req.h> +#include <botan/cvc_ado.h> + +namespace Botan { + +/** +* This class represents a set of options used for the creation of CVC certificates +*/ +class BOTAN_DLL EAC1_1_CVC_Options + { + public: + + ASN1_Car car; + ASN1_Chr chr; + byte holder_auth_templ; + ASN1_Ced ced; + ASN1_Cex cex; + std::string hash_alg; + }; + +/** +* This namespace represents general EAC 1.1 convenience functions. +*/ +namespace CVC_EAC { + +/** +* Create a selfsigned CVCA +* @param rng the rng to use +* @param key the ECDSA private key to be used to sign the certificate +* @param opts used to set several parameters. Necessary are: +* car, holder_auth_templ, hash_alg, ced, cex and hash_alg +* @result the self signed certificate +*/ + +EAC1_1_CVC BOTAN_DLL create_self_signed_cert(Private_Key const& key, + EAC1_1_CVC_Options const& opts, + RandomNumberGenerator& rng); +/** +* Create a CVC request. The key encoding will be according to the provided private key. +* @param priv_key the private key associated with the requesting entity +* @param chr the chr to appear in the certificate (to be provided without +* sequence number) +* @param hash_alg the string defining the hash algorithm to be used for the creation +* of the signature +* @param rng the rng to use +* @result the new request +*/ +EAC1_1_Req BOTAN_DLL create_cvc_req(Private_Key const& priv_key, + ASN1_Chr const& chr, + std::string const& hash_alg, + RandomNumberGenerator& rng); + +/** +* Create an ADO from a request object. +* @param priv_key the private key used to sign the ADO +* @param req the request forming the body of the ADO +* @param car the CAR forming the body of the ADO, i.e. the +* CHR of the entity associated with the provided private key +* @param rng the rng to use +*/ +EAC1_1_ADO BOTAN_DLL create_ado_req(Private_Key const& priv_key, + EAC1_1_Req const& req, + ASN1_Car const& car, + RandomNumberGenerator& rng); +} +/** +* This namespace represents EAC 1.1 CVC convenience functions +* following the specific german requirements. +*/ + +namespace DE_EAC { + +/** +* Create a CVCA certificate. +* @param priv_key the private key associated with the CVCA certificate +* to be created +* @param hash the string identifying the hash algorithm to be used +* for signing the certificate to be created +* @param car the CAR of the certificate to be created +* @param iris indicates whether the entity associated with the certificate +* shall be entitled to read the biometrical iris image +* @param fingerpr indicates whether the entity associated with the certificate +* shall be entitled to read the biometrical fingerprint image +* @param cvca_validity_months length of time in months this will be valid +* @param rng a random number generator +* @result the CVCA certificate created +*/ +EAC1_1_CVC BOTAN_DLL create_cvca(Private_Key const& priv_key, + std::string const& hash, + ASN1_Car const& car, + bool iris, + bool fingerpr, + u32bit cvca_validity_months, + RandomNumberGenerator& rng); + +/** +* Create a link certificate between two CVCA certificates. The key +* encoding will be implicitCA. +* @param signer the cvca certificate associated with the signing +* entity +* @param priv_key the private key associated with the signer +* @param to_be_signed the certificate which whose CAR/CHR will be +* the holder of the link certificate +* @param rng a random number generator +*/ +EAC1_1_CVC BOTAN_DLL link_cvca(EAC1_1_CVC const& signer, + Private_Key const& priv_key, + EAC1_1_CVC const& to_be_signed, + RandomNumberGenerator& rng); + +/** +* Create a CVC request. The key encoding will be implicitCA. +* @param priv_key the private key associated with the requesting entity +* @param chr the chr to appear in the certificate (to be provided without +* sequence number) +* @param hash_alg the string defining the hash algorithm to be used for the creation +* of the signature +* @param rng a random number generator +* @result the new request +*/ +EAC1_1_Req BOTAN_DLL create_cvc_req(Private_Key const& priv_key, + ASN1_Chr const& chr, + std::string const& hash_alg, + RandomNumberGenerator& rng); + +/** +* Sign a CVC request. +* @param signer_cert the certificate of the signing entity +* @param priv_key the private key of the signing entity +* @param req the request to be signed +* @param seqnr the sequence number of the certificate to be created +* @param seqnr_len the number of digits the sequence number will be +* encoded in +* @param domestic indicates whether to sign a domestic or a foreign +* certificate: set to true for domestic +* @param dvca_validity_months validity period in months +* @param ca_is_validity_months validity period in months +* @param rng a random number generator +* @result the new certificate +* +**/ +EAC1_1_CVC BOTAN_DLL sign_request(EAC1_1_CVC const& signer_cert, + Private_Key const& priv_key, + EAC1_1_Req const& req, + u32bit seqnr, + u32bit seqnr_len, + bool domestic, + u32bit dvca_validity_months, + u32bit ca_is_validity_months, + RandomNumberGenerator& rng); +} + +} + +#endif diff --git a/src/lib/cert/cvc/eac_asn_obj.h b/src/lib/cert/cvc/eac_asn_obj.h new file mode 100644 index 000000000..ab8536e45 --- /dev/null +++ b/src/lib/cert/cvc/eac_asn_obj.h @@ -0,0 +1,238 @@ +/* +* EAC ASN.1 Objects +* (C) 2007-2008 FlexSecure GmbH +* 2008-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_EAC_ASN1_OBJ_H__ +#define BOTAN_EAC_ASN1_OBJ_H__ + +#include <botan/asn1_obj.h> + +namespace Botan { + +/** +* This class represents CVC EAC Time objects. +* It only models year, month and day. Only limited sanity checks of +* the inputted date value are performed. +*/ +class BOTAN_DLL EAC_Time : public ASN1_Object + { + public: + void encode_into(class DER_Encoder&) const; + void decode_from(class BER_Decoder&); + + /** + * Get a this objects value as a string. + * @return date string + */ + std::string as_string() const; + + /** + * Get a this objects value as a readable formatted string. + * @return date string + */ + std::string readable_string() const; + + /** + * Find out whether this object's values have been set. + * @return true if this object's internal values are set + */ + bool time_is_set() const; + + /** + * Compare this to another EAC_Time object. + * @return -1 if this object's date is earlier than + * other, +1 in the opposite case, and 0 if both dates are + * equal. + */ + s32bit cmp(const EAC_Time& other) const; + + /** + * Set this' value by a string value. + * @param str a string in the format "yyyy mm dd", + * e.g. "2007 08 01" + */ + void set_to(const std::string& str); + + /** + * Add the specified number of years to this. + * @param years the number of years to add + */ + void add_years(u32bit years); + + /** + * Add the specified number of months to this. + * @param months the number of months to add + */ + void add_months(u32bit months); + + /** + * Get the year value of this objects. + * @return year value + */ + u32bit get_year() const { return year; } + + /** + * Get the month value of this objects. + * @return month value + */ + u32bit get_month() const { return month; } + + /** + * Get the day value of this objects. + * @return day value + */ + u32bit get_day() const { return day; } + + EAC_Time(const std::chrono::system_clock::time_point& time, + ASN1_Tag tag = ASN1_Tag(0)); + + EAC_Time(const std::string& yyyy_mm_dd, + ASN1_Tag tag = ASN1_Tag(0)); + + EAC_Time(u32bit year, u32bit month, u32bit day, + ASN1_Tag tag = ASN1_Tag(0)); + + virtual ~EAC_Time() {} + private: + std::vector<byte> encoded_eac_time() const; + bool passes_sanity_check() const; + u32bit year, month, day; + ASN1_Tag tag; + }; + +/** +* This class represents CVC CEDs. Only limited sanity checks of +* the inputted date value are performed. +*/ +class BOTAN_DLL ASN1_Ced : public EAC_Time + { + public: + /** + * Construct a CED from a string value. + * @param str a string in the format "yyyy mm dd", + * e.g. "2007 08 01" + */ + ASN1_Ced(const std::string& str = "") : + EAC_Time(str, ASN1_Tag(37)) {} + + /** + * Construct a CED from a time point + */ + ASN1_Ced(const std::chrono::system_clock::time_point& time) : + EAC_Time(time, ASN1_Tag(37)) {} + + /** + * Copy constructor (for general EAC_Time objects). + * @param other the object to copy from + */ + ASN1_Ced(const EAC_Time& other) : + EAC_Time(other.get_year(), other.get_month(), other.get_day(), + ASN1_Tag(37)) + {} + }; + +/** +* This class represents CVC CEXs. Only limited sanity checks of +* the inputted date value are performed. +*/ +class BOTAN_DLL ASN1_Cex : public EAC_Time + { + public: + /** + * Construct a CEX from a string value. + * @param str a string in the format "yyyy mm dd", + * e.g. "2007 08 01" + */ + ASN1_Cex(const std::string& str = "") : + EAC_Time(str, ASN1_Tag(36)) {} + + ASN1_Cex(const std::chrono::system_clock::time_point& time) : + EAC_Time(time, ASN1_Tag(36)) {} + + ASN1_Cex(const EAC_Time& other) : + EAC_Time(other.get_year(), other.get_month(), other.get_day(), + ASN1_Tag(36)) + {} + }; + +/** +* Base class for car/chr of cv certificates. +*/ +class BOTAN_DLL ASN1_EAC_String: public ASN1_Object + { + public: + void encode_into(class DER_Encoder&) const; + void decode_from(class BER_Decoder&); + + /** + * Get this objects string value. + * @return string value + */ + std::string value() const; + + /** + * Get this objects string value. + * @return string value in iso8859 encoding + */ + std::string iso_8859() const; + + ASN1_Tag tagging() const; + ASN1_EAC_String(const std::string& str, ASN1_Tag the_tag); + + virtual ~ASN1_EAC_String() {} + protected: + bool sanity_check() const; + private: + std::string iso_8859_str; + ASN1_Tag tag; + }; + +/** +* This class represents CARs of CVCs. (String tagged with 2) +*/ +class BOTAN_DLL ASN1_Car : public ASN1_EAC_String + { + public: + /** + * Create a CAR with the specified content. + * @param str the CAR value + */ + ASN1_Car(std::string const& str = ""); + }; + +/** +* This class represents CHRs of CVCs (tag 32) +*/ +class BOTAN_DLL ASN1_Chr : public ASN1_EAC_String + { + public: + /** + * Create a CHR with the specified content. + * @param str the CHR value + */ + ASN1_Chr(std::string const& str = ""); + }; + +/* +* Comparison Operations +*/ +bool BOTAN_DLL operator==(const EAC_Time&, const EAC_Time&); +bool BOTAN_DLL operator!=(const EAC_Time&, const EAC_Time&); +bool BOTAN_DLL operator<=(const EAC_Time&, const EAC_Time&); +bool BOTAN_DLL operator>=(const EAC_Time&, const EAC_Time&); +bool BOTAN_DLL operator>(const EAC_Time&, const EAC_Time&); +bool BOTAN_DLL operator<(const EAC_Time&, const EAC_Time&); + +bool BOTAN_DLL operator==(const ASN1_EAC_String&, const ASN1_EAC_String&); +inline bool operator!=(const ASN1_EAC_String& lhs, const ASN1_EAC_String& rhs) + { + return !(lhs == rhs); + } + +} + +#endif diff --git a/src/lib/cert/cvc/eac_obj.h b/src/lib/cert/cvc/eac_obj.h new file mode 100644 index 000000000..42274446c --- /dev/null +++ b/src/lib/cert/cvc/eac_obj.h @@ -0,0 +1,55 @@ +/* +* EAC1_1 objects +* (C) 2008 Falko Strenzke +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_EAC_OBJ_H__ +#define BOTAN_EAC_OBJ_H__ + +#include <botan/signed_obj.h> +#include <botan/ecdsa_sig.h> + +namespace Botan { + +/** +* TR03110 v1.1 EAC CV Certificate +*/ +template<typename Derived> // CRTP is used enable the call sequence: +class EAC1_1_obj : public EAC_Signed_Object + { + public: + /** + * Return the signature as a concatenation of the encoded parts. + * @result the concatenated signature + */ + std::vector<byte> get_concat_sig() const + { return m_sig.get_concatenation(); } + + bool check_signature(class Public_Key& key) const + { + return EAC_Signed_Object::check_signature(key, m_sig.DER_encode()); + } + + protected: + ECDSA_Signature m_sig; + + void init(DataSource& in) + { + try + { + Derived::decode_info(in, tbs_bits, m_sig); + } + catch(Decoding_Error) + { + throw Decoding_Error(PEM_label_pref + " decoding failed"); + } + } + + virtual ~EAC1_1_obj<Derived>(){} + }; + +} + +#endif diff --git a/src/lib/cert/cvc/ecdsa_sig.cpp b/src/lib/cert/cvc/ecdsa_sig.cpp new file mode 100644 index 000000000..690244d50 --- /dev/null +++ b/src/lib/cert/cvc/ecdsa_sig.cpp @@ -0,0 +1,59 @@ +/* +* ECDSA Signature +* (C) 2007 Falko Strenzke, FlexSecure GmbH +* (C) 2008-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/ecdsa_sig.h> + +namespace Botan { + +ECDSA_Signature::ECDSA_Signature(const std::vector<byte>& ber) + { + BER_Decoder(ber) + .start_cons(SEQUENCE) + .decode(m_r) + .decode(m_s) + .end_cons() + .verify_end(); + } + +std::vector<byte> ECDSA_Signature::DER_encode() const + { + return DER_Encoder() + .start_cons(SEQUENCE) + .encode(get_r()) + .encode(get_s()) + .end_cons() + .get_contents_unlocked(); + } + +std::vector<byte> ECDSA_Signature::get_concatenation() const + { + // use the larger + const size_t enc_len = m_r > m_s ? m_r.bytes() : m_s.bytes(); + + const auto sv_r = BigInt::encode_1363(m_r, enc_len); + const auto sv_s = BigInt::encode_1363(m_s, enc_len); + + secure_vector<byte> result(sv_r); + result += sv_s; + return unlock(result); + } + +ECDSA_Signature decode_concatenation(const std::vector<byte>& concat) + { + if(concat.size() % 2 != 0) + throw Invalid_Argument("Erroneous length of signature"); + + const size_t rs_len = concat.size() / 2; + + BigInt r = BigInt::decode(&concat[0], rs_len); + BigInt s = BigInt::decode(&concat[rs_len], rs_len); + + return ECDSA_Signature(r, s); + } + +} diff --git a/src/lib/cert/cvc/ecdsa_sig.h b/src/lib/cert/cvc/ecdsa_sig.h new file mode 100644 index 000000000..1c3b506cb --- /dev/null +++ b/src/lib/cert/cvc/ecdsa_sig.h @@ -0,0 +1,61 @@ +/* +* ECDSA Signature +* (C) 2007 Falko Strenzke, FlexSecure GmbH +* (C) 2008-2010 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_ECDSA_SIGNATURE_H__ +#define BOTAN_ECDSA_SIGNATURE_H__ + +#include <botan/bigint.h> +#include <botan/der_enc.h> +#include <botan/ber_dec.h> + +namespace Botan { + +/** +* Class representing an ECDSA signature +*/ +class BOTAN_DLL ECDSA_Signature + { + public: + friend class ECDSA_Signature_Decoder; + + ECDSA_Signature() {} + ECDSA_Signature(const BigInt& r, const BigInt& s) : + m_r(r), m_s(s) {} + + ECDSA_Signature(const std::vector<byte>& ber); + + const BigInt& get_r() const { return m_r; } + const BigInt& get_s() const { return m_s; } + + /** + * return the r||s + */ + std::vector<byte> get_concatenation() const; + + std::vector<byte> DER_encode() const; + + bool operator==(const ECDSA_Signature& other) const + { + return (get_r() == other.get_r() && get_s() == other.get_s()); + } + + private: + BigInt m_r; + BigInt m_s; + }; + +inline bool operator!=(const ECDSA_Signature& lhs, const ECDSA_Signature& rhs) + { + return !(lhs == rhs); + } + +ECDSA_Signature decode_concatenation(const std::vector<byte>& concatenation); + +} + +#endif diff --git a/src/lib/cert/cvc/info.txt b/src/lib/cert/cvc/info.txt new file mode 100644 index 000000000..1d8e54dc4 --- /dev/null +++ b/src/lib/cert/cvc/info.txt @@ -0,0 +1,36 @@ +define CARD_VERIFIABLE_CERTIFICATES 20131128 +load_on request + +<header:public> +cvc_ado.h +cvc_cert.h +cvc_gen_cert.h +cvc_req.h +cvc_self.h +eac_asn_obj.h +eac_obj.h +ecdsa_sig.h +signed_obj.h +</header:public> + +<source> +asn1_eac_str.cpp +asn1_eac_tm.cpp +ecdsa_sig.cpp +cvc_ado.cpp +cvc_cert.cpp +cvc_req.cpp +cvc_self.cpp +signed_obj.cpp +</source> + +<requires> +asn1 +bigint +ecdsa +filters +libstate +oid_lookup +pem +pubkey +</requires> diff --git a/src/lib/cert/cvc/signed_obj.cpp b/src/lib/cert/cvc/signed_obj.cpp new file mode 100644 index 000000000..20226fbe7 --- /dev/null +++ b/src/lib/cert/cvc/signed_obj.cpp @@ -0,0 +1,96 @@ +/* +* EAC SIGNED Object +* (C) 1999-2010 Jack Lloyd +* 2007 FlexSecure GmbH +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/signed_obj.h> +#include <botan/pubkey.h> +#include <botan/oids.h> +#include <memory> + +namespace Botan { + +/* +* Return a BER encoded X.509 object +*/ +std::vector<byte> EAC_Signed_Object::BER_encode() const + { + Pipe ber; + ber.start_msg(); + encode(ber, RAW_BER); + ber.end_msg(); + return unlock(ber.read_all()); + } + +/* +* Return a PEM encoded X.509 object +*/ +std::string EAC_Signed_Object::PEM_encode() const + { + Pipe pem; + pem.start_msg(); + encode(pem, PEM); + pem.end_msg(); + return pem.read_all_as_string(); + } + +/* +* Return the algorithm used to sign this object +*/ +AlgorithmIdentifier EAC_Signed_Object::signature_algorithm() const + { + return sig_algo; + } + +bool EAC_Signed_Object::check_signature(Public_Key& pub_key, + const std::vector<byte>& sig) const + { + try + { + std::vector<std::string> sig_info = + split_on(OIDS::lookup(sig_algo.oid), '/'); + + if(sig_info.size() != 2 || sig_info[0] != pub_key.algo_name()) + { + return false; + } + + std::string padding = sig_info[1]; + Signature_Format format = + (pub_key.message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363; + + std::vector<byte> to_sign = tbs_data(); + + PK_Verifier verifier(pub_key, padding, format); + return verifier.verify_message(to_sign, sig); + } + catch(...) + { + return false; + } + } + +/* +* Try to decode the actual information +*/ +void EAC_Signed_Object::do_decode() + { + try { + force_decode(); + } + catch(Decoding_Error& e) + { + const std::string what = e.what(); + throw Decoding_Error(PEM_label_pref + " decoding failed (" + what + ")"); + } + catch(Invalid_Argument& e) + { + const std::string what = e.what(); + throw Decoding_Error(PEM_label_pref + " decoding failed (" + what + ")"); + } + } + +} diff --git a/src/lib/cert/cvc/signed_obj.h b/src/lib/cert/cvc/signed_obj.h new file mode 100644 index 000000000..ce2bd4dd7 --- /dev/null +++ b/src/lib/cert/cvc/signed_obj.h @@ -0,0 +1,96 @@ +/* +* EAC SIGNED Object +* (C) 2007 FlexSecure GmbH +* 2008 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#ifndef BOTAN_EAC_SIGNED_OBJECT_H__ +#define BOTAN_EAC_SIGNED_OBJECT_H__ + +#include <botan/asn1_obj.h> +#include <botan/key_constraint.h> +#include <botan/x509_key.h> +#include <botan/pipe.h> +#include <vector> + +namespace Botan { + +/** +* This class represents abstract signed EAC object +*/ +class BOTAN_DLL EAC_Signed_Object + { + public: + /** + * Get the TBS (to-be-signed) data in this object. + * @return DER encoded TBS data of this object + */ + virtual std::vector<byte> tbs_data() const = 0; + + /** + * Get the signature of this object as a concatenation, i.e. if the + * signature consists of multiple parts (like in the case of ECDSA) + * these will be concatenated. + * @return signature as a concatenation of its parts + */ + + /* + NOTE: this is here only because abstract signature objects have + not yet been introduced + */ + virtual std::vector<byte> get_concat_sig() const = 0; + + /** + * Get the signature algorithm identifier used to sign this object. + * @result the signature algorithm identifier + */ + AlgorithmIdentifier signature_algorithm() const; + + /** + * Check the signature of this object. + * @param key the public key associated with this signed object + * @param sig the signature we are checking + * @return true if the signature was created by the private key + * associated with this public key + */ + bool check_signature(class Public_Key& key, + const std::vector<byte>& sig) const; + + /** + * Write this object DER encoded into a specified pipe. + * @param pipe the pipe to write the encoded object to + * @param encoding the encoding type to use + */ + virtual void encode(Pipe& pipe, + X509_Encoding encoding = PEM) const = 0; + + /** + * BER encode this object. + * @return result containing the BER representation of this object. + */ + std::vector<byte> BER_encode() const; + + /** + * PEM encode this object. + * @return result containing the PEM representation of this object. + */ + std::string PEM_encode() const; + + virtual ~EAC_Signed_Object() {} + protected: + void do_decode(); + EAC_Signed_Object() {} + + AlgorithmIdentifier sig_algo; + std::vector<byte> tbs_bits; + std::string PEM_label_pref; + std::vector<std::string> PEM_labels_allowed; + private: + virtual void force_decode() = 0; + }; + +} + +#endif |